Skip to content

Commit

Permalink
✨ Add 7 workflow examples for MLJFlux
Browse files Browse the repository at this point in the history
  • Loading branch information
EssamWisam committed Jun 3, 2024
1 parent 01ad08e commit d5fe2c7
Show file tree
Hide file tree
Showing 35 changed files with 15,048 additions and 67 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
.DS_Store
sandbox/
docs/build
/examples/mnist/mnist_machine*
/examples/mnist/mnist_machine*
Manifest.toml
11 changes: 7 additions & 4 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,13 @@ makedocs(
"Image Classification"=>"interface/Image Classification.md",
],
"Workflow Examples" => Any[
"Incremental Training"=>"workflow examples/Incremental Training.md",
#"Validation and Hyperparameter Tuning"=>"workflow examples/Hyperparameter Tuning.md",
#"Early Stopping"=>"workflow examples/Early Stopping.md",
#"Model Composition"=>"workflow examples/Composition.md",
"Incremental Training"=>"workflow examples/Incremental Training/incremental.md",
"Hyperparameter Tuning"=>"workflow examples/Hyperparameter Tuning/tuning.md",
"Neural Architecture Search"=>"workflow examples/Basic Neural Architecture Search/tuning.md",
"Model Composition"=>"workflow examples/Composition/composition.md",
"Model Comparison"=>"workflow examples/Comparison/comparison.md",
"Early Stopping"=>"workflow examples/Early Stopping/iteration.md",
"Live Training"=>"workflow examples/Live Training/live-training.md",
],
# "Tutorials"=>Any[
# "MNIST Digits Classification"=>"full tutorials/MNIST.md",
Expand Down
4 changes: 3 additions & 1 deletion docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,6 @@ chain.

- **Comparing** your model with a non-deep learning models

A comparable project, [FastAI](https://github.com/FluxML/FastAI.jl)/[FluxTraining](https://github.com/FluxML/FluxTraining.jl), also provides a high-level interface for interacting with Flux models and supports a set of features that may overlap with (but not include all of) those supported by MLJFlux.
A comparable project, [FastAI](https://github.com/FluxML/FastAI.jl)/[FluxTraining](https://github.com/FluxML/FluxTraining.jl), also provides a high-level interface for interacting with Flux models and supports a set of features that may overlap with (but not include all of) those supported by MLJFlux.

Many of the features mentioned above are showcased in the workflow examples that you can access from the sidebar.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
MLJ = "add582a8-e3ab-11e8-2d5e-e98b27df1bc7"
MLJFlux = "094fc8d1-fd35-5302-93ea-dabda2abf845"
RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b"

Large diffs are not rendered by default.

126 changes: 126 additions & 0 deletions docs/src/workflow examples/Basic Neural Architecture Search/tuning.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# # Neural Architecture Search with MLJFlux

# Neural Architecture Search is (NAS) is an instance of hyperparameter tuning concerned with tuning model hyperparameters
# defining the architecture itself. Although it's typically performed with sophisticated search algorithms for efficiency,
# in this example we will be using a simple random search.

using Pkg #src
Pkg.activate(@__DIR__); #src
Pkg.instantiate(); #src

# **Julia version** is assumed to be 1.10.*

# ### Basic Imports

using MLJ # Has MLJFlux models
using Flux # For more flexibility
using RDatasets: RDatasets # Dataset source
using DataFrames # To view tuning results in a table

# ### Loading and Splitting the Data

iris = RDatasets.dataset("datasets", "iris");
y, X = unpack(iris, ==(:Species), colname -> true, rng = 123);
X = Float32.(X); # To be compatible with type of network network parameters
first(X, 5)



# ### Instantiating the model

# Now let's construct our model. This follows a similar setup the one followed in the [Quick Start](../../index.md).
NeuralNetworkClassifier = @load NeuralNetworkClassifier pkg = "MLJFlux"
clf = NeuralNetworkClassifier(
builder = MLJFlux.MLP(; hidden = (1, 1, 1), σ = Flux.relu),
optimiser = Flux.ADAM(0.01),
batch_size = 8,
epochs = 10,
rng = 42,
)


# ### Generating Network Architectures
# We know that the MLP builder takes a tuple of the form $(z_1, z_2, ..., z_k)$ to define a network with $k$ hidden layers and
# where the ith layer has $z_i$ neurons. We will proceed by defining a function that can generate all possible networks with a
# specific number of hidden layers, a minimum and maximum number of neurons per layer and increments to consider for the number of neurons.

function generate_networks(;
min_neurons::Int,
max_neurons::Int,
neuron_step::Int,
num_layers::Int,
)
## Define the range of neurons
neuron_range = min_neurons:neuron_step:max_neurons

## Empty list to store the network configurations
networks = Vector{Tuple{Vararg{Int, num_layers}}}()

## Recursive helper function to generate all combinations of tuples
function generate_tuple(current_layers, remaining_layers)
if remaining_layers > 0
for n in neuron_range
## current_layers =[] then current_layers=[(min_neurons)],
## [(min_neurons+neuron_step)], [(min_neurons+2*neuron_step)],...
## for each of these we call generate_layers again which appends
## the n combinations for each one of them
generate_tuple(vcat(current_layers, [n]), remaining_layers - 1)
end
else
## in the base case, no more layers to "recurse on"
## and we just append the current_layers as a tuple
push!(networks, tuple(current_layers...))
end
end

## Generate networks for the given number of layers
generate_tuple([], num_layers)

return networks
end


# Now let's generate an array of all possible neural networks with three hidden layers and number of neurons per layer ∈ [1,64] with a step of 4
networks_space =
generate_networks(min_neurons = 1, max_neurons = 64, neuron_step = 4, num_layers = 3)

networks_space[1:5]

# ### Wrapping the Model for Tuning


# Let's use this array to define the range of hyperparameters and pass it along with the model to the `TunedModel` constructor.
r1 = range(clf, :(builder.hidden), values = networks_space)

tuned_clf = TunedModel(
model = clf,
tuning = RandomSearch(),
resampling = CV(nfolds = 4, rng = 42),
range = [r1],
measure = cross_entropy,
n = 100, # searching over 100 random samples are enough
);

# ### Performing the Search

# Similar to the last workflow example, all we need now is to fit our model and the search will take place automatically:
mach = machine(tuned_clf, X, y);
fit!(mach, verbosity = 0);
fitted_params(mach).best_model

# ### Analyzing the Search Results

# Let's analyze the search results by converting the history array to a dataframe and viewing it:
history = report(mach).history
history_df = DataFrame(
mlp = [x[:model].builder for x in history],
measurement = [x[:measurement][1] for x in history],
)
first(sort!(history_df, [order(:measurement)]), 10)




using Literate #src
Literate.markdown(@__FILE__, @__DIR__, execute = false) #src
Literate.notebook(@__FILE__, @__DIR__, execute = true) #src
141 changes: 141 additions & 0 deletions docs/src/workflow examples/Basic Neural Architecture Search/tuning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
```@meta
EditURL = "tuning.jl"
```

# Neural Architecture Search with MLJFlux

Neural Architecture Search is (NAS) is an instance of hyperparameter tuning concerned with tuning model hyperparameters
defining the architecture itself. Although it's typically performed with sophisticated search algorithms for efficiency,
in this example we will be using a simple random search.

**Julia version** is assumed to be 1.10.*

### Basic Imports

````@example tuning
using MLJ # Has MLJFlux models
using Flux # For more flexibility
using RDatasets: RDatasets # Dataset source
using DataFrames # To view tuning results in a table
````

### Loading and Splitting the Data

````@example tuning
iris = RDatasets.dataset("datasets", "iris");
y, X = unpack(iris, ==(:Species), colname -> true, rng = 123);
X = Float32.(X); # To be compatible with type of network network parameters
first(X, 5)
````

### Instantiating the model

Now let's construct our model. This follows a similar setup the one followed in the [Quick Start](../../index.md).

````@example tuning
NeuralNetworkClassifier = @load NeuralNetworkClassifier pkg = "MLJFlux"
clf = NeuralNetworkClassifier(
builder = MLJFlux.MLP(; hidden = (1, 1, 1), σ = Flux.relu),
optimiser = Flux.ADAM(0.01),
batch_size = 8,
epochs = 10,
rng = 42,
)
````

### Generating Network Architectures
We know that the MLP builder takes a tuple of the form $(z_1, z_2, ..., z_k)$ to define a network with $k$ hidden layers and
where the ith layer has $z_i$ neurons. We will proceed by defining a function that can generate all possible networks with a
specific number of hidden layers, a minimum and maximum number of neurons per layer and increments to consider for the number of neurons.

````@example tuning
function generate_networks(;
min_neurons::Int,
max_neurons::Int,
neuron_step::Int,
num_layers::Int,
)
# Define the range of neurons
neuron_range = min_neurons:neuron_step:max_neurons
# Empty list to store the network configurations
networks = Vector{Tuple{Vararg{Int, num_layers}}}()
# Recursive helper function to generate all combinations of tuples
function generate_tuple(current_layers, remaining_layers)
if remaining_layers > 0
for n in neuron_range
# current_layers =[] then current_layers=[(min_neurons)],
# [(min_neurons+neuron_step)], [(min_neurons+2*neuron_step)],...
# for each of these we call generate_layers again which appends
# the n combinations for each one of them
generate_tuple(vcat(current_layers, [n]), remaining_layers - 1)
end
else
# in the base case, no more layers to "recurse on"
# and we just append the current_layers as a tuple
push!(networks, tuple(current_layers...))
end
end
# Generate networks for the given number of layers
generate_tuple([], num_layers)
return networks
end
````

Now let's generate an array of all possible neural networks with three hidden layers and number of neurons per layer ∈ [1,64] with a step of 4

````@example tuning
networks_space =
generate_networks(min_neurons = 1, max_neurons = 64, neuron_step = 4, num_layers = 3)
networks_space[1:5]
````

### Wrapping the Model for Tuning

Let's use this array to define the range of hyperparameters and pass it along with the model to the `TunedModel` constructor.

````@example tuning
r1 = range(clf, :(builder.hidden), values = networks_space)
tuned_clf = TunedModel(
model = clf,
tuning = RandomSearch(),
resampling = CV(nfolds = 4, rng = 42),
range = [r1],
measure = cross_entropy,
n = 100, # searching over 100 random samples are enough
);
nothing #hide
````

### Performing the Search

Similar to the last workflow example, all we need now is to fit our model and the search will take place automatically:

````@example tuning
mach = machine(tuned_clf, X, y);
fit!(mach, verbosity = 0);
fitted_params(mach).best_model
````

### Analyzing the Search Results

Let's analyze the search results by converting the history array to a dataframe and viewing it:

````@example tuning
history = report(mach).history
history_df = DataFrame(
mlp = [x[:model].builder for x in history],
measurement = [x[:measurement][1] for x in history],
)
first(sort!(history_df, [order(:measurement)]), 10)
````

---

*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).*

13 changes: 13 additions & 0 deletions docs/src/workflow examples/Comparison/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
DecisionTree = "7806a523-6efd-50cb-b5f6-3fa6f1930dbb"
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
MLJ = "add582a8-e3ab-11e8-2d5e-e98b27df1bc7"
MLJDecisionTreeInterface = "c6f25543-311c-4c74-83dc-3ea6d1015661"
MLJFlux = "094fc8d1-fd35-5302-93ea-dabda2abf845"
MLJMultivariateStatsInterface = "1b6a4a23-ba22-4f51-9698-8599985d3728"
MLJXGBoostInterface = "54119dfa-1dab-4055-a167-80440f4f7a91"
MultivariateStats = "6f286f6a-111f-5878-ab1e-185364afe411"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
RDatasets = "ce6b1742-4840-55fa-b093-852dadbb1d8b"
Loading

0 comments on commit d5fe2c7

Please sign in to comment.