Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -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)
Expand Down Expand Up @@ -494,6 +497,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)
Expand Down Expand Up @@ -806,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)
Expand Down Expand Up @@ -887,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)
Expand Down
92 changes: 60 additions & 32 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,60 +1,88 @@
# keras3 (development version)

- Added S3 methods for JAX array: `str`, `as.array`, `as.double`, `as.integer`, `as.numeric`.

- Added `str` S3 method for Keras Variables.

- `layer_reshape()` can now accept `-1` as a sentinel for an automatically calculated axis size.
- 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()`.

- Updated dependencies declared by `use_backend("jax", gpu=TRUE)`
for compatability with `keras-hub`.
- Added signal window operations: `op_bartlett()`, `op_blackman()`,
`op_hamming()`, `op_hanning()`, and `op_kaiser()`.

- 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 `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.

- `keras_variable()` now accepts a `synchronization` argument for distributed
strategies.
- Added complex-valued helpers: S3 `Arg()` methods for tensors, `op_angle()`,
and conversions `op_view_as_real()` / `op_view_as_complex()`.

- `Layer$add_weight()` gains an `overwrite_with_gradient` option and
layers now provide a `symbolic_call()` method.
- 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.

- `layer_torch_module_wrapper()` gains an `output_shape` argument to help Keras
infer shapes when wrapping PyTorch modules.
- `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.

- Added elastic deformation utilities for images: `layer_random_elastic_transform()`
and the lower-level `op_image_elastic_transform()`.
- `keras_variable()` now accepts a `synchronization` argument for distributed
strategies.

- Added `loss_categorical_generalized_cross_entropy()` for training with noisy
labels.
- `layer_layer_normalization()` removes the `rms_scaling` argument.

- Added the Muon optimizer via `optimizer_muon()`.
- `layer_reshape()` can now accept `-1` as a sentinel for an automatically calculated axis size.

- Added complex-valued helpers: S3 `Arg()` methods for tensors, `op_angle()`,
and conversions `op_view_as_real()` / `op_view_as_complex()`.
- `layer_torch_module_wrapper()` gains an `output_shape` argument to help Keras
infer shapes when wrapping PyTorch modules.

- Added signal window operations: `op_bartlett()`, `op_blackman()`,
`op_hamming()`, `op_hanning()`, and `op_kaiser()`.
- `Layer$add_weight()` gains an `overwrite_with_gradient` option and
layers now provide a `symbolic_call()` method.

- 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 `str()` S3 method for Keras Variables.

- `layer_layer_normalization()` removes the `rms_scaling` argument.
- 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
compatibility.

- `plot(model)` DPI is now globally configurable via
`options(keras.plot.model.dpi = )`, (defaults to `200`).

- 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.

- Updated dependencies declared by `use_backend("jax", gpu=TRUE)`
for compatability with `keras-hub`.

- Exported `named_list()` utility.

- Fixed an issue when switching backends twice in a row.

# keras3 1.4.0

Expand Down
2 changes: 1 addition & 1 deletion R/install.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions R/model-persistence.R
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}


Expand Down
17 changes: 16 additions & 1 deletion R/package.R
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand All @@ -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__`()) {
Expand Down Expand Up @@ -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)
Expand Down
26 changes: 19 additions & 7 deletions R/py-classes.R
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -137,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__`
Expand Down
27 changes: 23 additions & 4 deletions R/r-utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,34 @@ 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 rlang::dots_list
#'
#' @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) {
Expand Down
3 changes: 3 additions & 0 deletions R/reexports.R
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ reticulate::py_to_r
#' @export
reticulate::r_to_py

#' @export
reticulate::py_help

#' @importFrom tensorflow tensorboard
#' @export
tensorflow::tensorboard
Expand Down
4 changes: 3 additions & 1 deletion R/utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 11 additions & 1 deletion man/deserialize_keras_object.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions man/layer_tfsm.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading