@@ -19,54 +19,48 @@ pak::pak("davidrsch/kerasnip")
1919
2020## Example
2121
22- ### Example: Building a Sequential MLP from Layer Blocks
22+ ### Example 1 : Building a Sequential MLP
2323
24- This example shows the core ` kerasnip ` workflow for building a model from modular "layer blocks". We will:
25- 1 . Define reusable blocks of Keras layers.
26- 2 . Create a model specification from these blocks.
27- 3 . Fit the model with a fixed architecture.
24+ This example shows the core workflow for building a simple, linear stack of layers using ` create_keras_sequential_spec() ` .
2825
2926``` r
3027library(kerasnip )
3128library(tidymodels )
3229library(keras3 )
3330
3431# 1. Define Keras layer blocks
35- # Each block is a function that takes a Keras model object and adds layers.
36- # The first block in the sequence is responsible for initializing the model.
37- mlp_input_block <- function (model , input_shape ) {
32+ # The first block initializes the model.
33+ input_block <- function (model , input_shape ) {
3834 keras_model_sequential(input_shape = input_shape )
3935}
40-
41- mlp_dense_block <- function (model , units = 32 ) {
42- model | >
43- layer_dense(units = units , activation = " relu" )
36+ # Subsequent blocks add layers.
37+ dense_block <- function (model , units = 32 ) {
38+ model | > layer_dense(units = units , activation = " relu" )
4439}
45-
46- mlp_output_block <- function (model ) {
40+ # The final block creates the output layer.
41+ output_block <- function (model ) {
4742 model | >
4843 layer_dense(units = 1 )
4944}
5045
5146# 2. Create a spec from the layer blocks
5247# This creates a new model function, `basic_mlp()`, in your environment.
53- create_keras_spec (
48+ create_keras_sequential_spec (
5449 model_name = " basic_mlp" ,
5550 layer_blocks = list (
56- input = mlp_input_block ,
57- dense = mlp_dense_block ,
58- output = mlp_output_block
51+ input = input_block ,
52+ dense = dense_block ,
53+ output = output_block
5954 ),
6055 mode = " regression"
6156)
6257
63- # 3. Use the generated spec to define and fit a model
64- # We can set the number of dense layers (`num_dense`) and their parameters
65- # (`dense_units`).
58+ # 3. Use the generated spec to define a model.
59+ # We can set the number of dense layers (`num_dense`) and their parameters (`dense_units`).
6660spec <- basic_mlp(
6761 num_dense = 2 ,
6862 dense_units = 64 ,
69- epochs = 50 ,
63+ fit_epochs = 10 ,
7064 learn_rate = 0.01
7165) | >
7266 set_engine(" keras" )
@@ -75,27 +69,70 @@ spec <- basic_mlp(
7569rec <- recipe(mpg ~ . , data = mtcars ) | >
7670 step_normalize(all_numeric_predictors())
7771
78- wf <- workflow() | >
79- add_recipe(rec ) | >
80- add_model(spec )
72+ wf <- workflow(rec , spec )
8173
8274set.seed(123 )
8375fit_obj <- fit(wf , data = mtcars )
8476
8577# 5. Make predictions
86- predictions <- predict(fit_obj , new_data = mtcars [1 : 5 , ])
87- print(predictions )
78+ predict(fit_obj , new_data = mtcars [1 : 5 , ])
8879# > # A tibble: 5 × 1
8980# > .pred
9081# > <dbl>
91- # > 1 22.6
92- # > 2 20.9
93- # > 3 26.1
94- # > 4 19.7
95- # > 5 17.8
82+ # > 1 21.3
83+ # > 2 21.3
84+ # > 3 22.8
85+ # > 4 21.4
86+ # > 5 18.7
9687```
9788
98- ### Example: Tuning a Sequential MLP Architecture
89+ ### Example 2: Building a Functional "Fork-Join" Model
90+
91+ For complex, non-linear architectures, use ` create_keras_functional_spec() ` . This example builds a model where the input is forked into two paths, which are then concatenated.
92+
93+ ``` r
94+ library(kerasnip )
95+ library(tidymodels )
96+ library(keras3 )
97+
98+ # 1. Define blocks. For the functional API, blocks are nodes in a graph.
99+ input_block <- function (input_shape ) layer_input(shape = input_shape )
100+ path_block <- function (tensor , units = 16 ) tensor | > layer_dense(units = units )
101+ concat_block <- function (input_a , input_b ) layer_concatenate(list (input_a , input_b ))
102+ output_block <- function (tensor ) layer_dense(tensor , units = 1 )
103+
104+ # 2. Create the spec. The graph is defined by block names and their arguments.
105+ create_keras_functional_spec(
106+ model_name = " forked_mlp" ,
107+ layer_blocks = list (
108+ main_input = input_block ,
109+ path_a = inp_spec(path_block , " main_input" ),
110+ path_b = inp_spec(path_block , " main_input" ),
111+ concatenated = inp_spec(concat_block , c(path_a = " input_a" , path_b = " input_b" )),
112+ # The output block must be named 'output'.
113+ output = inp_spec(output_block , " concatenated" )
114+ ),
115+ mode = " regression"
116+ )
117+
118+ # 3. Use the new spec. Arguments are prefixed with their block name.
119+ spec <- forked_mlp(path_a_units = 16 , path_b_units = 8 , fit_epochs = 10 ) | >
120+ set_engine(" keras" )
121+
122+ # Fit and predict as usual
123+ set.seed(123 )
124+ fit(spec , mpg ~ . , data = mtcars ) | >
125+ predict(new_data = mtcars [1 : 5 , ])
126+ # > # A tibble: 5 × 1
127+ # > .pred
128+ # > <dbl>
129+ # > 1 19.4
130+ # > 2 19.5
131+ # > 3 21.9
132+ # > 4 18.6
133+ # > 5 17.9
134+ ```
135+ ### Example 3: Tuning a Sequential MLP Architecture
99136
100137This example demonstrates how to tune the number of dense layers and the rate of a final dropout layer, showcasing how to tune both architecture and block hyperparameters simultaneously.
101138
@@ -105,30 +142,27 @@ library(tidymodels)
105142library(keras3 )
106143
107144# 1. Define Keras layer blocks for a tunable MLP
108- mlp_input_block <- function (model , input_shape ) {
145+ input_block <- function (model , input_shape ) {
109146 keras_model_sequential(input_shape = input_shape )
110147}
111-
112- tunable_dense_block <- function (model , units = 32 ) {
148+ dense_block <- function (model , units = 32 ) {
113149 model | > layer_dense(units = units , activation = " relu" )
114150}
115-
116- tunable_dropout_block <- function (model , rate = 0.2 ) {
151+ dropout_block <- function (model , rate = 0.2 ) {
117152 model | > layer_dropout(rate = rate )
118153}
119-
120- mlp_output_block <- function (model ) {
154+ output_block <- function (model ) {
121155 model | > layer_dense(units = 1 )
122156}
123157
124158# 2. Create a spec from the layer blocks
125- create_keras_spec (
159+ create_keras_sequential_spec (
126160 model_name = " tunable_mlp" ,
127161 layer_blocks = list (
128- input = mlp_input_block ,
129- dense = tunable_dense_block ,
130- dropout = tunable_dropout_block ,
131- output = mlp_output_block
162+ input = input_block ,
163+ dense = dense_block ,
164+ dropout = dropout_block ,
165+ output = output_block
132166 ),
133167 mode = " regression"
134168)
@@ -139,17 +173,15 @@ tune_spec <- tunable_mlp(
139173 dense_units = tune(),
140174 num_dropout = 1 ,
141175 dropout_rate = tune(),
142- epochs = 20
176+ fit_epochs = 10
143177) | >
144178 set_engine(" keras" )
145179
146- # 4. Set up a tuning workflow
180+ # 4. Set up and run a tuning workflow
147181rec <- recipe(mpg ~ . , data = mtcars ) | >
148182 step_normalize(all_numeric_predictors())
149183
150- wf_tune <- workflow() | >
151- add_recipe(rec ) | >
152- add_model(tune_spec )
184+ wf_tune <- workflow(rec , tune_spec )
153185
154186# Define the tuning grid.
155187params <- extract_parameter_set_dials(wf_tune ) | >
@@ -167,7 +199,8 @@ folds <- vfold_cv(mtcars, v = 3)
167199tune_res <- tune_grid(
168200 wf_tune ,
169201 resamples = folds ,
170- grid = grid
202+ grid = grid ,
203+ control = control_grid(verbose = FALSE )
171204)
172205
173206# 6. Show the best architecture
@@ -180,4 +213,4 @@ show_best(tune_res, metric = "rmse")
180213# > 3 3 64 0.1 rmse standard 3.15 Preprocessor1_Model04
181214# > 4 1 8 0.1 rmse standard 3.20 Preprocessor1_Model01
182215# > 5 3 8 0.1 rmse standard 3.22 Preprocessor1_Model03
183- ```
216+ ```
0 commit comments