From 8c9bb98ffbf32213d19759c8fe1c4eff6783aec6 Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 07:51:21 -0400 Subject: [PATCH 01/14] export `named_list()` --- NAMESPACE | 1 + R/r-utils.R | 25 +++++++++++++++++++++---- man/named_list.Rd | 30 ++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 man/named_list.Rd diff --git a/NAMESPACE b/NAMESPACE index 4c4b200b2..e7e47eaf4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -494,6 +494,7 @@ export(metric_sum) export(metric_top_k_categorical_accuracy) export(metric_true_negatives) export(metric_true_positives) +export(named_list) export(new_callback_class) export(new_layer_class) export(new_learning_rate_schedule_class) diff --git a/R/r-utils.R b/R/r-utils.R index 0c1d3ab84..fbeb56e48 100644 --- a/R/r-utils.R +++ b/R/r-utils.R @@ -40,15 +40,32 @@ drop_nulls <- function(x, i = NULL) { x[!drop] } +#' Create a named list from arguments +#' +#' Constructs a list from the provided arguments where all elements are named. +#' This wraps [rlang::dots_list()] but changes two defaults: +#' - `.named` is set to `TRUE` +#' - `.homonyms` is set to `"error"` +#' +#' Other parameters retain their defaults from [rlang::dots_list()]: +#' - `.ignore_empty = "trailing"` +#' - `.preserve_empty = FALSE` +#' - `.check_assign = FALSE` +#' +#' @inheritParams dots_list +#' +#' @return A named list. +#' +#' @seealso [rlang::dots_list()] +#' +#' @export #' @importFrom rlang dots_list -# identical to rlang::list2(), except .named = TRUE named_list <- function(...) dots_list(..., - .named = TRUE, - # not the default + .named = TRUE, # not default .ignore_empty = "trailing", .preserve_empty = FALSE, - .homonyms = "error", + .homonyms = "error", # not default .check_assign = FALSE) `append1<-` <- function(x, value) { diff --git a/man/named_list.Rd b/man/named_list.Rd new file mode 100644 index 000000000..ed3957f7f --- /dev/null +++ b/man/named_list.Rd @@ -0,0 +1,30 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/r-utils.R +\name{named_list} +\alias{named_list} +\title{Create a named list from arguments} +\usage{ +named_list(...) +} +\value{ +A named list. +} +\description{ +Constructs a list from the provided arguments where all elements are named. +This wraps \code{\link[rlang:list2]{rlang::dots_list()}} but changes two defaults: +\itemize{ +\item \code{.named} is set to \code{TRUE} +\item \code{.homonyms} is set to \code{"error"} +} +} +\details{ +Other parameters retain their defaults from \code{\link[rlang:list2]{rlang::dots_list()}}: \cr +\itemize{ +\item \code{.ignore_empty = "trailing"} +\item \code{.preserve_empty = FALSE} +\item \code{.check_assign = FALSE} +} +} +\seealso{ +\code{\link[rlang:list2]{rlang::dots_list()}} +} From 9a91d0a3bd3c0377af259e9c0fed626960c2d57f Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 07:51:48 -0400 Subject: [PATCH 02/14] fix switching backends twice --- R/install.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/install.R b/R/install.R index c8383c73b..8afe49e53 100644 --- a/R/install.R +++ b/R/install.R @@ -388,7 +388,7 @@ uv_unset_override_never_tensorflow <- function() { if (is.na(override)) return() cpu_override <- pkg_file("never-tensorflow-override.txt") if (override == cpu_override) { - Sys.unsetenv(override) + Sys.unsetenv("UV_OVERRIDE") } else { new <- gsub(cpu_override, "", override, fixed = TRUE) new <- gsub(" +", " ", new) From da7fae6a6c8a7e334570fb52086d0a208bb0dd2d Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 07:52:13 -0400 Subject: [PATCH 03/14] fix `register_keras_serializable()` for bare functions --- R/model-persistence.R | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/R/model-persistence.R b/R/model-persistence.R index fc4a7a25a..936c47f39 100644 --- a/R/model-persistence.R +++ b/R/model-persistence.R @@ -585,8 +585,8 @@ function (object, filepath, call_endpoint = "serve", call_training_endpoint = NU #' @param object #' A keras object. #' -#' @returns `object` is returned invisibly, for convenient piping. This is -#' primarily called for side effects. +#' @returns The registered `object` (and converted) is returned. This returned object is what you +#' should must use when building and serializing the model. #' @export #' @family saving and loading functions #' @family serialization utilities @@ -605,7 +605,7 @@ function (object, name = NULL, package = NULL) c("", "base", "R_GlobalEnv"), "Custom") keras$saving$register_keras_serializable(package, name)(py_object) - invisible(object) + py_object } From 0e428100fe4ca1ec1c876fc9251d4a64202bf309 Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 07:53:28 -0400 Subject: [PATCH 04/14] add `t`, `aperm` `all.equal` methods for backend tensors --- R/package.R | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/R/package.R b/R/package.R index 45bdaa00a..fe532498b 100644 --- a/R/package.R +++ b/R/package.R @@ -186,7 +186,12 @@ keras <- NULL keras <- import("keras") convert_to_tensor <- import("keras.ops", convert = FALSE)$convert_to_tensor with(keras$device("cpu:0"), { - backend_tensor_class <- class(convert_to_tensor(array(1L)))[1L] + all_backend_tensor_s3_classes <- class(convert_to_tensor(array(1L))) + backend_tensor_class <- all_backend_tensor_s3_classes[1L] + if ("jax.Array" %in% all_backend_tensor_s3_classes) + backend_tensor_class <- "jax.Array" + # message("setting methods on backend_tensor_class: ", backend_tensor_class, + # "\nother options: ", paste0(all_backend_tensor_s3_classes, collapse = " ")) }) symbolic_tensor_class <- nameOfClass__python.builtin.type(keras$KerasTensor) @@ -207,6 +212,9 @@ keras <- NULL registerS3method("as.array", backend_tensor_class, op_convert_to_array, baseenv()) registerS3method("^", backend_tensor_class, `^__keras.backend.tensor`, baseenv()) registerS3method("%*%", backend_tensor_class, op_matmul, baseenv()) + registerS3method("t", backend_tensor_class, op_transpose, baseenv()) + registerS3method("aperm", backend_tensor_class, op_transpose, baseenv()) + registerS3method("all.equal", backend_tensor_class, all.equal.numpy.ndarray, baseenv()) if(keras$config$backend() == "jax") { for(py_type in import("jax")$Array$`__subclasses__`()) { @@ -271,6 +279,13 @@ keras <- NULL } +## should this live in reticulate?? probably... +#' @export +all.equal.numpy.ndarray <- function(target, current, ...) { + # or use numpy.allequal? + all.equal(as.array(target), as.array(current), ...) +} + at.keras_backend_tensor <- function(object, name) { out <- rlang::env_clone(object) From 204670972d352328ced34d416a2f0d428151ecbb Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 07:55:19 -0400 Subject: [PATCH 05/14] tweak `super()` def in subclasses --- R/py-classes.R | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/R/py-classes.R b/R/py-classes.R index 27680f036..85e2fc51d 100644 --- a/R/py-classes.R +++ b/R/py-classes.R @@ -119,12 +119,16 @@ function(classname, type = `__class__`, object_or_type = base::get("self", envir = base::parent.frame())) { - convert <- base::get("convert", envir = base::as.environment(object_or_type)) - py_builtins <- reticulate::import_builtins(convert) - reticulate::py_call(py_builtins$super, type, object_or_type) - } + convert <- base::get("convert", object_or_type) + py_super <- reticulate::py_eval( + "__import__('builtins').super", + convert = convert + ) + py_super(type, object_or_type) + } class(super) <- "python_builtin_super_getter" - })) + }) +) py_class From f786ae73969568c8239238537852b34c73c9f12b Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 07:55:56 -0400 Subject: [PATCH 06/14] support `super()$initialize()` --- R/py-classes.R | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/R/py-classes.R b/R/py-classes.R index 85e2fc51d..185120d66 100644 --- a/R/py-classes.R +++ b/R/py-classes.R @@ -141,15 +141,23 @@ function(classname, #' @export `$.python_builtin_super_getter` <- function(x, name) { super <- do.call(x, list(), envir = parent.frame()) # call super() + `[[.python.builtin.super`(super, name) +} + +#' @export +`[[.python_builtin_super_getter` <- `$.python_builtin_super_getter` + +#' @export +`$.python.builtin.super` <- function(x, name) { name <- switch(name, initialize = "__init__", finalize = "__del__", name) - out <- py_get_attr(super, name) + out <- py_get_attr(x, name) convert <- get0("convert", as.environment(out), inherits = FALSE, ifnotfound = TRUE) if (convert) py_to_r(out) else out } #' @export -`[[.python_builtin_super_getter` <- `$.python_builtin_super_getter` +`[[.python.builtin.super` <- `$.python.builtin.super` # No .DollarNames.python_builtin_super_getter because the python.builtin.super # object doesn't have populated attributes itself, only a dynamic `__getattr__` From b37ab632417f3e495405ee118fe6dfe9e0d8c0db Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 07:56:07 -0400 Subject: [PATCH 07/14] reexport `py_help()` --- R/reexports.R | 3 +++ 1 file changed, 3 insertions(+) diff --git a/R/reexports.R b/R/reexports.R index f77dc1255..c04b2c115 100644 --- a/R/reexports.R +++ b/R/reexports.R @@ -77,6 +77,9 @@ reticulate::py_to_r #' @export reticulate::r_to_py +#' @export +reticulate::py_help + #' @importFrom tensorflow tensorboard #' @export tensorflow::tensorboard From 8cb4a06814d42056f57fbb6e49364d39f11c945b Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 07:56:32 -0400 Subject: [PATCH 08/14] make `plot(model)` `dip` globally configurable --- R/utils.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/utils.R b/R/utils.R index e1aaef25d..9fcb0f526 100644 --- a/R/utils.R +++ b/R/utils.R @@ -369,6 +369,8 @@ to_categorical <- function (x, num_classes = NULL) { if (inherits(x, "factor")) { + # if (length(DIM(x)) == 1) + # return(diag(nrow = num_classes %||% length(levels(x)))[as.integer(x), ]) x <- array(as.integer(x) - 1L, dim = dim(x) %||% length(x)) if (is.null(num_classes)) num_classes <- length(levels(x)) @@ -631,7 +633,7 @@ function(x, ..., rankdir = "TB", expand_nested = FALSE, - dpi = 200, + dpi = getOption("keras.plot.model.dpi", 200L), layer_range = NULL, show_layer_activations = FALSE, show_trainable = NA, From 861a806f7f40dbae82686d0ac48e8c7b6887a37d Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 07:57:31 -0400 Subject: [PATCH 09/14] rebuild NAMESPACE --- NAMESPACE | 5 +++++ man/reexports.Rd | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index e7e47eaf4..7d066382d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,16 +1,19 @@ # Generated by roxygen2: do not edit by hand S3method("!=",keras_shape) +S3method("$",python.builtin.super) S3method("$",python_builtin_super_getter) S3method("$<-",keras.src.callbacks.callback.Callback) S3method("+",keras.src.backend.common.keras_tensor.KerasTensor) S3method("==",keras.src.backend.common.keras_tensor.KerasTensor) S3method("==",keras_shape) S3method("[",keras_shape) +S3method("[[",python.builtin.super) S3method("[[",python_builtin_super_getter) S3method(Arg,keras.src.backend.Tensor) S3method(Arg,keras.src.backend.common.keras_tensor.KerasTensor) S3method(Summary,keras_shape) +S3method(all,equal.numpy.ndarray) S3method(as.array,jax.Array) S3method(as.array,jaxlib._jax.ArrayImpl) S3method(as.array,jaxlib.xla_extension.ArrayImpl) @@ -807,6 +810,7 @@ export(optimizer_sgd) export(pad_sequences) export(pop_layer) export(predict_on_batch) +export(py_help) export(py_require) export(py_to_r) export(quantize_weights) @@ -888,6 +892,7 @@ importFrom(reticulate,py_func) importFrom(reticulate,py_get_attr) importFrom(reticulate,py_get_item) importFrom(reticulate,py_has_attr) +importFrom(reticulate,py_help) importFrom(reticulate,py_install) importFrom(reticulate,py_is_null_xptr) importFrom(reticulate,py_iterator) diff --git a/man/reexports.Rd b/man/reexports.Rd index 8fa6d08bc..b26d7b602 100644 --- a/man/reexports.Rd +++ b/man/reexports.Rd @@ -17,6 +17,7 @@ \alias{import} \alias{py_to_r} \alias{r_to_py} +\alias{py_help} \alias{tensorboard} \alias{export_savedmodel} \alias{as_tensor} @@ -45,7 +46,7 @@ below to see their documentation. \item{magrittr}{\code{\link[magrittr:compound]{\%<>\%}}} - \item{reticulate}{\code{\link[reticulate:with-as-operator]{\%as\%}}, \code{\link[reticulate]{array_reshape}}, \code{\link[reticulate:iterate]{as_iterator}}, \code{\link[reticulate]{import}}, \code{\link[reticulate:iterate]{iter_next}}, \code{\link[reticulate]{iterate}}, \code{\link[reticulate]{np_array}}, \code{\link[reticulate]{py_require}}, \code{\link[reticulate:r-py-conversion]{py_to_r}}, \code{\link[reticulate:r-py-conversion]{r_to_py}}, \code{\link[reticulate]{tuple}}, \code{\link[reticulate]{use_python}}, \code{\link[reticulate:use_python]{use_virtualenv}}} + \item{reticulate}{\code{\link[reticulate:with-as-operator]{\%as\%}}, \code{\link[reticulate]{array_reshape}}, \code{\link[reticulate:iterate]{as_iterator}}, \code{\link[reticulate]{import}}, \code{\link[reticulate:iterate]{iter_next}}, \code{\link[reticulate]{iterate}}, \code{\link[reticulate]{np_array}}, \code{\link[reticulate]{py_help}}, \code{\link[reticulate]{py_require}}, \code{\link[reticulate:r-py-conversion]{py_to_r}}, \code{\link[reticulate:r-py-conversion]{r_to_py}}, \code{\link[reticulate]{tuple}}, \code{\link[reticulate]{use_python}}, \code{\link[reticulate:use_python]{use_virtualenv}}} \item{tensorflow}{\code{\link[tensorflow]{all_dims}}, \code{\link[tensorflow]{as_tensor}}, \code{\link[tensorflow]{evaluate}}, \code{\link[tensorflow]{export_savedmodel}}, \code{\link[tensorflow]{tensorboard}}} From 1ce74aafa52cdc92fe171f077909ddb1fae0a54e Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 08:22:26 -0400 Subject: [PATCH 10/14] add new NEWS entries --- NEWS.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/NEWS.md b/NEWS.md index ec7e290a7..885b3ebaf 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,32 @@ # keras3 (development version) +- `plot(model)` DPI is now globally configurable via the + `keras.plot.model.dpi` option. + +- Reexported reticulate functions: `py_help()`, `py_to_r()`, `r_to_py()`, + `py_require()`, and `import()`. + +- Support `super()$initialize()` in subclassed Keras classes; improved + `super()` behavior in subclasses. + +- Added base-array compatibility methods for backend tensors: `t()`, + `aperm()`, and `all.equal()`. + +- Fixed `register_keras_serializable()` when passed bare functions. + +- Fixed an issue when switching backends twice in a row. + +- Exported `named_list()` utility. + +- Built-in datasets now accept `convert = FALSE` to return NumPy arrays + instead of R arrays. + +- Updated `plot(history, theme_bw = TRUE)` for `ggplot2` 3.4.0 + compatibility. + +- Added `pillar::type_sum()` for JAX variables and `JaxVariable`; + extended `str()` coverage to the new JAX variable class. + - Added S3 methods for JAX array: `str`, `as.array`, `as.double`, `as.integer`, `as.numeric`. - Added `str` S3 method for Keras Variables. From 32af916c3b477121870bc4a01a4dfbc906cd129f Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 08:22:41 -0400 Subject: [PATCH 11/14] reorder NEWS entries --- NEWS.md | 91 +++++++++++++++++++++++++++++---------------------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/NEWS.md b/NEWS.md index 885b3ebaf..dfcaffe0d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,22 +1,39 @@ # keras3 (development version) -- `plot(model)` DPI is now globally configurable via the - `keras.plot.model.dpi` option. +- Added training loop configuration helpers: + `config_max_epochs()`, `config_set_max_epochs()`, `config_max_steps_per_epoch()`, + and `config_set_max_steps_per_epoch()`. The caps can also be set via the + `KERAS_MAX_EPOCHS` and `KERAS_MAX_STEPS_PER_EPOCH` environment variables. + Added `config_is_nnx_enabled()` to check whether JAX NNX features are enabled. -- Reexported reticulate functions: `py_help()`, `py_to_r()`, `r_to_py()`, - `py_require()`, and `import()`. +- LoRA-enabled layers (`layer_dense()`, `layer_embedding()`, `layer_einsum_dense()`) + gain a `lora_alpha` argument to scale the adaptation delta independently of the + chosen rank. -- Support `super()$initialize()` in subclassed Keras classes; improved - `super()` behavior in subclasses. +- `Layer$add_weight()` gains an `overwrite_with_gradient` option and + layers now provide a `symbolic_call()` method. -- Added base-array compatibility methods for backend tensors: `t()`, - `aperm()`, and `all.equal()`. +- `layer_layer_normalization()` removes the `rms_scaling` argument. -- Fixed `register_keras_serializable()` when passed bare functions. +- Transposed convolution utilities now follow the latest Keras API: + `op_conv_transpose()` defaults `strides = 1` and the `layer_conv_*_transpose()` + layers expose `output_padding` for precise shape control. -- Fixed an issue when switching backends twice in a row. +- Added the Muon optimizer via `optimizer_muon()`. -- Exported `named_list()` utility. +- Added `loss_categorical_generalized_cross_entropy()` for training with noisy + labels. + +- Added elastic deformation utilities for images: `layer_random_elastic_transform()` + and the lower-level `op_image_elastic_transform()`. + +- `save_model_weights()` adds a `max_shard_size` argument to split large weight + files into manageable shards. + +- `layer_reshape()` can now accept `-1` as a sentinel for an automatically calculated axis size. + +- `layer_torch_module_wrapper()` gains an `output_shape` argument to help Keras + infer shapes when wrapping PyTorch modules. - Built-in datasets now accept `convert = FALSE` to return NumPy arrays instead of R arrays. @@ -24,51 +41,37 @@ - Updated `plot(history, theme_bw = TRUE)` for `ggplot2` 3.4.0 compatibility. -- Added `pillar::type_sum()` for JAX variables and `JaxVariable`; - extended `str()` coverage to the new JAX variable class. +- `plot(model)` DPI is now globally configurable via + `options(keras.plot.model.dpi = )`, (defaults to `200`). -- Added S3 methods for JAX array: `str`, `as.array`, `as.double`, `as.integer`, `as.numeric`. +- Added base-array compatibility methods for backend tensors: `t()`, + `aperm()`, and `all.equal()`. -- Added `str` S3 method for Keras Variables. +- Reexported reticulate functions: `py_help()`, `py_to_r()`, `r_to_py()`, + `py_require()`, and `import()`. -- `layer_reshape()` can now accept `-1` as a sentinel for an automatically calculated axis size. +- Support `super()$initialize()` in subclassed Keras classes; improved + `super()` behavior in subclasses. -- Updated dependencies declared by `use_backend("jax", gpu=TRUE)` - for compatability with `keras-hub`. +- Added `pillar::type_sum()` for JAX variables and `JaxVariable`; + extended `str()` coverage to the new JAX variable class. -- Added training loop configuration helpers: - `config_max_epochs()`, `config_set_max_epochs()`, `config_max_steps_per_epoch()`, - and `config_set_max_steps_per_epoch()`. The caps can also be set via the - `KERAS_MAX_EPOCHS` and `KERAS_MAX_STEPS_PER_EPOCH` environment variables. - Added `config_is_nnx_enabled()` to check whether JAX NNX features are enabled. +- Added S3 methods for JAX array: + `str()`, `as.array()`, `as.double()`, `as.integer()`, `as.numeric()`. -- LoRA-enabled layers (`layer_dense()`, `layer_embedding()`, `layer_einsum_dense()`) - gain a `lora_alpha` argument to scale the adaptation delta independently of the - chosen rank. +- Added `str()` S3 method for Keras Variables. - `keras_variable()` now accepts a `synchronization` argument for distributed strategies. -- `Layer$add_weight()` gains an `overwrite_with_gradient` option and - layers now provide a `symbolic_call()` method. - -- Transposed convolution utilities now follow the latest Keras API: - `op_conv_transpose()` defaults `strides = 1` and the `layer_conv_*_transpose()` - layers expose `output_padding` for precise shape control. - -- `layer_torch_module_wrapper()` gains an `output_shape` argument to help Keras - infer shapes when wrapping PyTorch modules. - -- `save_model_weights()` adds a `max_shard_size` argument to split large weight - files into manageable shards. +- Updated dependencies declared by `use_backend("jax", gpu=TRUE)` + for compatability with `keras-hub`. -- Added elastic deformation utilities for images: `layer_random_elastic_transform()` - and the lower-level `op_image_elastic_transform()`. +- Fixed `register_keras_serializable()` when passed bare functions. -- Added `loss_categorical_generalized_cross_entropy()` for training with noisy - labels. +- Fixed an issue when switching backends twice in a row. -- Added the Muon optimizer via `optimizer_muon()`. +- Exported `named_list()` utility. - Added complex-valued helpers: S3 `Arg()` methods for tensors, `op_angle()`, and conversions `op_view_as_real()` / `op_view_as_complex()`. @@ -81,8 +84,6 @@ plus matching `activation_sparse_sigmoid()`, and an `attn_logits_soft_cap` argument for `op_dot_product_attention()`. -- `layer_layer_normalization()` removes the `rms_scaling` argument. - # keras3 1.4.0 - New `op_subset()` and `x@r[...]` methods enable tensor subsetting From 62b1f5f95bab5005e89eb91d1a2718b810ecb43f Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 08:31:03 -0400 Subject: [PATCH 12/14] rebuild docs --- man/deserialize_keras_object.Rd | 12 +++++++++++- man/layer_tfsm.Rd | 4 ++-- man/op_irfft.Rd | 2 +- man/plot.keras.src.models.model.Model.Rd | 2 +- man/register_keras_serializable.Rd | 12 +++++++++--- 5 files changed, 24 insertions(+), 8 deletions(-) diff --git a/man/deserialize_keras_object.Rd b/man/deserialize_keras_object.Rd index 8c4fddfcb..0335259d3 100644 --- a/man/deserialize_keras_object.Rd +++ b/man/deserialize_keras_object.Rd @@ -95,8 +95,18 @@ loss_modified_mse <- Loss( # register the custom object register_keras_serializable(loss_modified_mse) +}\if{html}{\out{}} + +\if{html}{\out{
}}\preformatted{## .ModifiedMeanSquaredError'> +## signature: ( +## reduction='sum_over_batch_size', +## name='mean_squared_error', +## dtype=None +## ) + +}\if{html}{\out{
}} -# confirm object is registered +\if{html}{\out{
}}\preformatted{# confirm object is registered get_custom_objects() }\if{html}{\out{
}} diff --git a/man/layer_tfsm.Rd b/man/layer_tfsm.Rd index e11c0dadb..4b482cf44 100644 --- a/man/layer_tfsm.Rd +++ b/man/layer_tfsm.Rd @@ -59,8 +59,8 @@ model |> export_savedmodel("path/to/artifact") ## Output Type: ## TensorSpec(shape=(None, 10), dtype=tf.float32, name=None) ## Captures: -## 131730679462800: TensorSpec(shape=(), dtype=tf.resource, name=None) -## 131730679456848: TensorSpec(shape=(), dtype=tf.resource, name=None) +## 130615262532560: TensorSpec(shape=(), dtype=tf.resource, name=None) +## 130615262538704: TensorSpec(shape=(), dtype=tf.resource, name=None) }\if{html}{\out{}} diff --git a/man/op_irfft.Rd b/man/op_irfft.Rd index ba41b0243..44ea2dbc3 100644 --- a/man/op_irfft.Rd +++ b/man/op_irfft.Rd @@ -48,7 +48,7 @@ op_irfft(c(real, imag)) \if{html}{\out{
}}\preformatted{all.equal(op_irfft(op_rfft(real, 5), 5), real) }\if{html}{\out{
}} -\if{html}{\out{
}}\preformatted{#> [1] TRUE +\if{html}{\out{
}}\preformatted{#> [1] "Mean relative difference: 5.960465e-08" }\if{html}{\out{
}} } diff --git a/man/plot.keras.src.models.model.Model.Rd b/man/plot.keras.src.models.model.Model.Rd index 830ec7893..67f0f18f2 100644 --- a/man/plot.keras.src.models.model.Model.Rd +++ b/man/plot.keras.src.models.model.Model.Rd @@ -12,7 +12,7 @@ ..., rankdir = "TB", expand_nested = FALSE, - dpi = 200, + dpi = getOption("keras.plot.model.dpi", 200L), layer_range = NULL, show_layer_activations = FALSE, show_trainable = NA, diff --git a/man/register_keras_serializable.Rd b/man/register_keras_serializable.Rd index fcbc45e0a..d66dca278 100644 --- a/man/register_keras_serializable.Rd +++ b/man/register_keras_serializable.Rd @@ -16,8 +16,8 @@ register_keras_serializable(object, name = NULL, package = NULL) Defaults to the current package name, or \code{"Custom"} outside of a package.} } \value{ -\code{object} is returned invisibly, for convenient piping. This is -primarily called for side effects. +The registered \code{object} (and converted) is returned. This returned object is what you +should must use when building and serializing the model. } \description{ This function registers a custom class or function with the Keras custom @@ -36,8 +36,14 @@ defaults to the object name if not passed. # the `name` argument is not provided, `'MyDense'` is used as the `name`. layer_my_dense <- Layer("MyDense") register_keras_serializable(layer_my_dense, package = "my_package") +}\if{html}{\out{
}} + +\if{html}{\out{
}}\preformatted{## .MyDense'> +## signature: (*args, **kwargs) + +}\if{html}{\out{
}} -MyDense <- environment(layer_my_dense)$`__class__` # the python class obj +\if{html}{\out{
}}\preformatted{MyDense <- environment(layer_my_dense)$`__class__` # the python class obj stopifnot(exprs = \{ get_registered_object('my_package>MyDense') == MyDense get_registered_name(MyDense) == 'my_package>MyDense' From 1cf7aed54431352ccbcb1563a787dba4e850320d Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 09:18:08 -0400 Subject: [PATCH 13/14] fix `R CMD check` --- R/r-utils.R | 2 ++ man/layer_tfsm.Rd | 4 ++-- man/named_list.Rd | 4 ++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/R/r-utils.R b/R/r-utils.R index fbeb56e48..993c5be36 100644 --- a/R/r-utils.R +++ b/R/r-utils.R @@ -52,6 +52,8 @@ drop_nulls <- function(x, i = NULL) { #' - `.preserve_empty = FALSE` #' - `.check_assign = FALSE` #' +#' @inheritParams rlang::dots_list +#' #' @inheritParams dots_list #' #' @return A named list. diff --git a/man/layer_tfsm.Rd b/man/layer_tfsm.Rd index 4b482cf44..0eee57166 100644 --- a/man/layer_tfsm.Rd +++ b/man/layer_tfsm.Rd @@ -59,8 +59,8 @@ model |> export_savedmodel("path/to/artifact") ## Output Type: ## TensorSpec(shape=(None, 10), dtype=tf.float32, name=None) ## Captures: -## 130615262532560: TensorSpec(shape=(), dtype=tf.resource, name=None) -## 130615262538704: TensorSpec(shape=(), dtype=tf.resource, name=None) +## 129280799091280: TensorSpec(shape=(), dtype=tf.resource, name=None) +## 129280799082064: TensorSpec(shape=(), dtype=tf.resource, name=None) }\if{html}{\out{
}} diff --git a/man/named_list.Rd b/man/named_list.Rd index ed3957f7f..541d1cfb8 100644 --- a/man/named_list.Rd +++ b/man/named_list.Rd @@ -6,6 +6,10 @@ \usage{ named_list(...) } +\arguments{ +\item{...}{Arguments to collect in a list. These dots are +\link[rlang:dyn-dots]{dynamic}.} +} \value{ A named list. } From 52d30e956597928dc365faf86a673174e70009d4 Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Tue, 14 Oct 2025 09:18:15 -0400 Subject: [PATCH 14/14] reorder NEWS --- NEWS.md | 88 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/NEWS.md b/NEWS.md index dfcaffe0d..a390cd1aa 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,41 +1,68 @@ # keras3 (development version) -- Added training loop configuration helpers: - `config_max_epochs()`, `config_set_max_epochs()`, `config_max_steps_per_epoch()`, - and `config_set_max_steps_per_epoch()`. The caps can also be set via the - `KERAS_MAX_EPOCHS` and `KERAS_MAX_STEPS_PER_EPOCH` environment variables. - Added `config_is_nnx_enabled()` to check whether JAX NNX features are enabled. +- Expanded numeric operations with `op_layer_normalization()`, `op_cbrt()`, + `op_corrcoef()`, `op_deg2rad()`, `op_heaviside()`, the new `op_sparse_sigmoid()` + plus matching `activation_sparse_sigmoid()`, and an `attn_logits_soft_cap` + argument for `op_dot_product_attention()`. + +- Added signal window operations: `op_bartlett()`, `op_blackman()`, + `op_hamming()`, `op_hanning()`, and `op_kaiser()`. + +- Added `loss_categorical_generalized_cross_entropy()` for training with noisy + labels. - LoRA-enabled layers (`layer_dense()`, `layer_embedding()`, `layer_einsum_dense()`) gain a `lora_alpha` argument to scale the adaptation delta independently of the chosen rank. -- `Layer$add_weight()` gains an `overwrite_with_gradient` option and - layers now provide a `symbolic_call()` method. +- Added complex-valued helpers: S3 `Arg()` methods for tensors, `op_angle()`, + and conversions `op_view_as_real()` / `op_view_as_complex()`. -- `layer_layer_normalization()` removes the `rms_scaling` argument. +- Added the Muon optimizer via `optimizer_muon()`. + +- Added elastic deformation utilities for images: `layer_random_elastic_transform()` + and the lower-level `op_image_elastic_transform()`. - Transposed convolution utilities now follow the latest Keras API: `op_conv_transpose()` defaults `strides = 1` and the `layer_conv_*_transpose()` layers expose `output_padding` for precise shape control. -- Added the Muon optimizer via `optimizer_muon()`. - -- Added `loss_categorical_generalized_cross_entropy()` for training with noisy - labels. - -- Added elastic deformation utilities for images: `layer_random_elastic_transform()` - and the lower-level `op_image_elastic_transform()`. +- `register_keras_serializable()` now returns a registered Python callable, + making it easier to use with bare R functions. - `save_model_weights()` adds a `max_shard_size` argument to split large weight files into manageable shards. +- `keras_variable()` now accepts a `synchronization` argument for distributed + strategies. + +- `layer_layer_normalization()` removes the `rms_scaling` argument. + - `layer_reshape()` can now accept `-1` as a sentinel for an automatically calculated axis size. - `layer_torch_module_wrapper()` gains an `output_shape` argument to help Keras infer shapes when wrapping PyTorch modules. -- Built-in datasets now accept `convert = FALSE` to return NumPy arrays +- `Layer$add_weight()` gains an `overwrite_with_gradient` option and + layers now provide a `symbolic_call()` method. + +- Added `str()` S3 method for Keras Variables. + +- Added S3 methods for JAX array: + `str()`, `as.array()`, `as.double()`, `as.integer()`, `as.numeric()`. + +- Added base-array compatibility methods for backend tensors: `t()`, + `aperm()`, and `all.equal()`. + +- Added `pillar::type_sum()` for JAX variables and `JaxVariable`; + extended `str()` coverage to the new JAX variable class. + +- `config_max_epochs()`, `config_set_max_epochs()`, `config_max_steps_per_epoch()`, + and `config_set_max_steps_per_epoch()`. The caps can also be set via the + `KERAS_MAX_EPOCHS` and `KERAS_MAX_STEPS_PER_EPOCH` environment variables. + Added `config_is_nnx_enabled()` to check whether JAX NNX features are enabled. + +- Built-in dataset loaders now accept `convert = FALSE` to return NumPy arrays instead of R arrays. - Updated `plot(history, theme_bw = TRUE)` for `ggplot2` 3.4.0 @@ -44,45 +71,18 @@ - `plot(model)` DPI is now globally configurable via `options(keras.plot.model.dpi = )`, (defaults to `200`). -- Added base-array compatibility methods for backend tensors: `t()`, - `aperm()`, and `all.equal()`. - - Reexported reticulate functions: `py_help()`, `py_to_r()`, `r_to_py()`, `py_require()`, and `import()`. - Support `super()$initialize()` in subclassed Keras classes; improved `super()` behavior in subclasses. -- Added `pillar::type_sum()` for JAX variables and `JaxVariable`; - extended `str()` coverage to the new JAX variable class. - -- Added S3 methods for JAX array: - `str()`, `as.array()`, `as.double()`, `as.integer()`, `as.numeric()`. - -- Added `str()` S3 method for Keras Variables. - -- `keras_variable()` now accepts a `synchronization` argument for distributed - strategies. - - Updated dependencies declared by `use_backend("jax", gpu=TRUE)` for compatability with `keras-hub`. -- Fixed `register_keras_serializable()` when passed bare functions. - -- Fixed an issue when switching backends twice in a row. - - Exported `named_list()` utility. -- Added complex-valued helpers: S3 `Arg()` methods for tensors, `op_angle()`, - and conversions `op_view_as_real()` / `op_view_as_complex()`. - -- Added signal window operations: `op_bartlett()`, `op_blackman()`, - `op_hamming()`, `op_hanning()`, and `op_kaiser()`. - -- Expanded numeric operations with `op_layer_normalization()`, `op_cbrt()`, - `op_corrcoef()`, `op_deg2rad()`, `op_heaviside()`, the new `op_sparse_sigmoid()` - plus matching `activation_sparse_sigmoid()`, and an `attn_logits_soft_cap` - argument for `op_dot_product_attention()`. +- Fixed an issue when switching backends twice in a row. # keras3 1.4.0