From 814530740722af293d3a991f0d5eb0aa58fc5be6 Mon Sep 17 00:00:00 2001 From: Antonello Lobianco Date: Fri, 24 May 2024 16:22:28 +0200 Subject: [PATCH] Revert "changed readme" This reverts commit be03ca6cf0a401d229ed94844c56654cc57a8d24. --- README.md | 6 +- announce_autoencoder.txt | 21 - cancellable/# Scratchpad.jl | 108 - cancellable/Untitled-2.jl | 97 - cancellable/Untitled-3.jl | 79 - cancellable/announce.txt | 85 - cancellable/using Statistics.jl | 29 - docs/Manifest_old.toml | 2327 ----------------- docs/src/Benchmarks_temp.jl | 82 - .../breast+cancer+wisconsin+diagnostic.zip | Bin 51284 -> 0 bytes .../betaml_tutorial_classification_cars.md | 389 --- .../betaml_tutorial_cluster_iris.md | 301 --- ...etaml_tutorial_dimensionality_reduction.jl | 275 -- .../Feature importance/Feature_importance.jl | 2 +- .../Feature importance/Feature_importance.md | 230 -- .../betaml_tutorial_multibranch_nn.md | 169 -- ...betaml_tutorial_regression_sharingBikes.md | 565 ---- loss_by_var.png | Bin 25500 -> 0 bytes plot1.png | Bin 7719 -> 0 bytes plot2.png | Bin 7090 -> 0 bytes plot3.png | Bin 7877 -> 0 bytes plot4.png | Bin 7595 -> 0 bytes plot_2.png | 50 - sobol_by_var.png | Bin 17679 -> 0 bytes sobol_ny_var.png | Bin 17766 -> 0 bytes test.jld2 | Bin 616141 -> 0 bytes test/temp_test.jl | 154 -- 27 files changed, 4 insertions(+), 4965 deletions(-) delete mode 100644 announce_autoencoder.txt delete mode 100644 cancellable/# Scratchpad.jl delete mode 100644 cancellable/Untitled-2.jl delete mode 100644 cancellable/Untitled-3.jl delete mode 100644 cancellable/announce.txt delete mode 100644 cancellable/using Statistics.jl delete mode 100644 docs/Manifest_old.toml delete mode 100644 docs/src/Benchmarks_temp.jl delete mode 100644 docs/src/assets/data/breast+cancer+wisconsin+diagnostic.zip delete mode 100644 docs/src/tutorials/Classification - cars/betaml_tutorial_classification_cars.md delete mode 100644 docs/src/tutorials/Clustering - Iris/betaml_tutorial_cluster_iris.md delete mode 100644 docs/src/tutorials/Dimensionality reduction/betaml_tutorial_dimensionality_reduction.jl delete mode 100644 docs/src/tutorials/Feature importance/Feature_importance.md delete mode 100644 docs/src/tutorials/Multi-branch neural network/betaml_tutorial_multibranch_nn.md delete mode 100644 docs/src/tutorials/Regression - bike sharing/betaml_tutorial_regression_sharingBikes.md delete mode 100644 loss_by_var.png delete mode 100644 plot1.png delete mode 100644 plot2.png delete mode 100644 plot3.png delete mode 100644 plot4.png delete mode 100644 plot_2.png delete mode 100644 sobol_by_var.png delete mode 100644 sobol_ny_var.png delete mode 100644 test.jld2 delete mode 100644 test/temp_test.jl diff --git a/README.md b/README.md index 02522166..36a7b2e1 100644 --- a/README.md +++ b/README.md @@ -167,7 +167,7 @@ Neural Networks | [Flux.jl](https://fluxml.ai/), [Knet](https://github.com/deni Decision Trees | [DecisionTree.jl](https://github.com/bensadeghi/DecisionTree.jl) Clustering | [Clustering.jl](https://github.com/JuliaStats/Clustering.jl), [GaussianMixtures.jl](https://github.com/davidavdav/GaussianMixtures.jl) Missing imputation | [Impute.jl](https://github.com/invenia/Impute.jl), [Mice.jl](https://github.com/tom-metherell/Mice.jl) -Variable importance | [ShapML.jl](https://github.com/nredell/ShapML.jl) + ## TODO @@ -175,7 +175,7 @@ Variable importance | [ShapML.jl](https://github.com/nredell/ShapML.jl) ### Short term - Implement autotuning of `GaussianMixtureClusterer` using `BIC` or `AIC` -- Add Silhouette method to check cluster validity +- Add Silhouette method to check cluster validity - Implement PAM and/or variants for kmedoids ### Mid/Long term @@ -183,7 +183,7 @@ Variable importance | [ShapML.jl](https://github.com/nredell/ShapML.jl) - Add RNN support and improve convolutional layers speed - Reinforcement learning (Markov decision processes) - Standardize data sampling in training -- Add GPU +- Convert to GPU ## Contribute diff --git a/announce_autoencoder.txt b/announce_autoencoder.txt deleted file mode 100644 index b96a94e7..00000000 --- a/announce_autoencoder.txt +++ /dev/null @@ -1,21 +0,0 @@ - -[Ann] Easiest AutoEncoder on Earth: `m=AutoEncoder(); fit!(m,x); latent_x = predict(m,x); xest=inverse_predict(m,x_latent)` - -[ANN] A easy to use AutoEncoder to reduce the dimensionality of the data - -(I hope I'm not breaking the self-promotion rule ;-) ) -Hello, I am pleased to announce one of the easiest to use [AutoEncoder](https://sylvaticus.github.io/BetaML.jl/dev/Utils.html#BetaML.Utils.AutoEncoder) models in the world. - -No need to implement a neural network yourself, just use -- `mod = AutoEncoder([optional stuff])` to create the model -- `fit!(mod,x)` to fit the model to some tabular data (dims in cols) -- x_latent = predict(mod)` or `x_latent = predict(mod,otherx)` to get the data encoded in latent space (usually with many less dimensions than the original) -- x_decoded = inverse_predict(mod,x_latent)` to get the decoded values. - -The user can still specify the number of dimensions in the latent space, the number of neurons in the inner layers, or the full specification of encoding/decoding layers and NN training options, but this remains completely optional as some heuristics are applied. The `autotune' method also allows to further simplify these choices. - -`AutoEncoder` is part of the v0.10.4 of [BetaML Machine Learning Toolkit](https://github.com/sylvaticus/BetaML.jl), an open source set of machine learning models and utilities, and a wrapper should soon be available as part of the MLJ library. -Although developed in the Julia language, it can be easily accessed from R, Python or any other language with a Julia binding, as specified [here](https://sylvaticus.github.io/BetaML.jl/stable/tutorials/Betaml_tutorial_getting_started.html#using_betaml_from_other_languages). - - - diff --git a/cancellable/# Scratchpad.jl b/cancellable/# Scratchpad.jl deleted file mode 100644 index 36712612..00000000 --- a/cancellable/# Scratchpad.jl +++ /dev/null @@ -1,108 +0,0 @@ -# Scratchpad - -x = [0.12 0.31 0.29 3.21 0.21; - 0.44 1.21 1.18 13.54 0.85 - 0.22 0.61 0.58 6.43 0.42; - 0.35 0.93 0.91 10.04 0.71; - 0.51 1.47 1.46 16.12 0.99; - 0.35 0.93 0.91 10.04 0.71; - 0.51 1.47 1.46 16.12 0.99; - 0.22 0.61 0.58 6.43 0.42; - 0.12 0.31 0.29 3.21 0.21; - 0.44 1.21 1.18 13.54 0.85]; -m = AutoEncoder(encoded_size=2,layers_size=15,epochs=400,autotune=false,rng=copy(TESTRNG)) -x_reduced = fit!(m,x) -x̂ = inverse_predict(m,x_reduced) -x̂sum = sum(x̂) - -x = vcat(rand(copy(TESTRNG),0:0.001:0.6,30,5), rand(copy(TESTRNG),0.4:0.001:1,30,5)) -m = AutoEncoder(rng=copy(TESTRNG), verbosity=NONE) -x_reduced = fit!(m,x) -x̂ = inverse_predict(m,x_reduced) -x̂sum = sum(x̂) - -l2loss_by_cv2(AutoEncoder(rng=copy(TESTRNG), verbosity=NONE),(x,),rng=copy(TESTRNG)) - -m = AutoEncoder(rng=copy(TESTRNG), verbosity=NONE) -sampler = KFold(nsplits=5,nrepeats=1,rng=copy(TESTRNG)) -(μ,σ) = cross_validation([x],sampler) do trainData,valData,rng - (xtrain,) = trainData; (xval,) = valData - fit!(m,xtrain) - x̂val_red = predict(m,xval) - x̂val = inverse_predict(m,x̂val_red) - ϵ = norm(xval .- x̂val)/size(xval,1) - println(ϵ) # different - reset!(m) - return ismissing(ϵ) ? Inf : ϵ -end - -function l2loss_by_cv2(m,data;nsplits=5,nrepeats=1,rng=Random.GLOBAL_RNG) - x= data[1] - sampler = KFold(nsplits=nsplits,nrepeats=nrepeats,rng=rng) - (μ,σ) = cross_validation([x],sampler) do trainData,valData,rng - (xtrain,) = trainData; (xval,) = valData - fit!(m,xtrain) - x̂val = inverse_predict(m,xval) - ϵ = norm(xval .- x̂val)/size(xval,1) - reset!(m) - return ismissing(ϵ) ? Inf : ϵ - end - return μ -end - - - -using Random, Pipe, HTTP, CSV, DataFrames, Plots, BetaML -import Distributions: Normal, quantile -Random.seed!(123) - -# We download the Boston house prices dataset from interet and split it into x and y -dataURL = "https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data" -data = @pipe HTTP.get(dataURL).body |> CSV.File(_, delim=' ', header=false, ignorerepeated=true) |> DataFrame - -data = CSV.File(joinpath("docs","src","tutorials","Feature importance", "data","housing.data"), delim=' ', header=false, ignorerepeated=true) |> DataFrame - -var_names = [ - "CRIM", # per capita crime rate by town - "ZN", # proportion of residential land zoned for lots over 25,000 sq.ft. - "INDUS", # proportion of non-retail business acres per town - "CHAS", # Charles River dummy variable (= 1 if tract bounds river; 0 otherwise) - "NOX", # nitric oxides concentration (parts per 10 million) - "RM", # average number of rooms per dwelling - "AGE", # proportion of owner-occupied units built prior to 1940 - "DIS", # weighted distances to five Boston employment centres - "RAD", # index of accessibility to radial highways - "TAX", # full-value property-tax rate per $10,000 - "PTRATIO", # pupil-teacher ratio by town - "B", # 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town - "LSTAT", # % lower status of the population -] -y_name = "MEDV" # Median value of owner-occupied homes in $1000's - -# Our features are a set of 13 explanatory variables, while the label that we want to estimate is the average housing prices: -x = Matrix(data[:,1:13]) -y = data[:,14] - -# We use a Random Forest model as regressor and we compute the variable importance for this model : -fr = FeatureRanker(model=RandomForestEstimator(),nsplits=3,nrepeats=2,recursive=false, ignore_dims_keyword="ignore_dims") -rank = fit!(fr,x,y) - -loss_by_col = info(fr)["loss_by_col"] -sobol_by_col = info(fr)["sobol_by_col"] -loss_by_col_sd = info(fr)["loss_by_col_sd"] -sobol_by_col_sd = info(fr)["sobol_by_col_sd"] -loss_fullmodel = info(fr)["loss_all_cols"] -loss_fullmodel_sd = info(fr)["loss_all_cols_sd"] -ntrials_per_metric = info(fr)["ntrials_per_metric"] - -# Finally we can plot the variable importance, first using the loss metric ("mda") and then the sobol one: -bar(var_names[sortperm(loss_by_col)], loss_by_col[sortperm(loss_by_col)],label="Loss by var", permute=(:x,:y), yerror=quantile(Normal(1,0),0.975) .* (loss_by_col_sd[sortperm(loss_by_col)]./sqrt(ntrials_per_metric)), yrange=[0,0.5]) -vline!([loss_fullmodel], label="Loss with all vars",linewidth=2) -vline!([loss_fullmodel-quantile(Normal(1,0),0.975) * loss_fullmodel_sd/sqrt(ntrials_per_metric), - loss_fullmodel+quantile(Normal(1,0),0.975) * loss_fullmodel_sd/sqrt(ntrials_per_metric), -], label=nothing,linecolor=:black,linestyle=:dot,linewidth=1) -savefig("loss_by_var.png") -#- -bar(var_names[sortperm(sobol_by_col)],sobol_by_col[sortperm(sobol_by_col)],label="Sobol index by col", permute=(:x,:y), yerror=quantile(Normal(1,0),0.975) .* (sobol_by_col_sd[sortperm(sobol_by_col)]./sqrt(ntrials_per_metric)), yrange=[0,0.4]) -savefig("sobol_ny_var.png") -# As we can see, the two analyses agree on the most important variables, showing that the size of the house (number of rooms), the percentage of low-income population in the neighbourhood and, to a lesser extent, the distance to employment centres are the most important explanatory variables of house price in the Boston area. \ No newline at end of file diff --git a/cancellable/Untitled-2.jl b/cancellable/Untitled-2.jl deleted file mode 100644 index 06884223..00000000 --- a/cancellable/Untitled-2.jl +++ /dev/null @@ -1,97 +0,0 @@ - -using Random, LinearAlgebra, Plots -Random.seed!(123) -# Syntetic data generation -# x1: high importance, x2: little importance, x3: mixed effects with x1, x4: highly correlated with x1 but no effects on Y, x5 and x6: no effects on Y -TEMPRNG = copy(Random.GLOBAL_RNG) -N = 2000 -D = 6 -nAttempts = 30 -xa = rand(TEMPRNG,0:0.0001:10,N,3) -xb = (xa[:,1] .* 0.5 .* rand(TEMPRNG,0.7:0.001:1.3)) .+ 10 -xc = rand(TEMPRNG,0:0.0001:10,N,D-4) -x = hcat(xa,xb,xc) -y = [10*r[1]-r[2]+0.1*r[3]*r[1] for r in eachrow(x) ] -((xtrain,xtest),(ytrain,ytest)) = BetaML.partition([x,y],[0.8,0.2],rng=TEMPRNG) - - -# full cols model: -m = RandomForestEstimator(n_trees=100,rng=TEMPRNG) -m = DecisionTreeEstimator(rng=TEMPRNG) -m = NeuralNetworkEstimator(verbosity=NONE,rng=TEMPRNG) -fit!(m,xtrain,ytrain) -ŷtrain = predict(m,xtrain) -loss = norm(ytrain-ŷtrain)/length(ytrain) # this is good - -ŷtest = predict(m,xtest) -loss = norm(ytest-ŷtest)/length(ytest) # this is good - -loss_by_cols = zeros(D) -sobol_by_cols = zeros(D) -loss_by_cols2 = zeros(D) -sobol_by_cols2 = zeros(D) -diffest_bycols = zeros(D) -loss_by_cols_test = zeros(D) -sobol_by_cols_test = zeros(D) -loss_by_cols2_test = zeros(D) -sobol_by_cols2_test = zeros(D) -diffest_bycols_test = zeros(D) -for a in 1:nAttempts - println("Running attempt $a...") - for d in 1:D - println("- doing modelling without dimension $d ....") - xd_train = hcat(xtrain[:,1:d-1],shuffle(TEMPRNG,xtrain[:,d]),xtrain[:,d+1:end]) - xd_test = hcat(xtest[:,1:d-1],shuffle(TEMPRNG,xtest[:,d]),xtest[:,d+1:end]) - #md = RandomForestEstimator(n_trees=100,rng=TEMPRNG) - #md = DecisionTreeEstimator(rng=TEMPRNG) - md = NeuralNetworkEstimator(verbosity=NONE,rng=TEMPRNG) - fit!(md,xd_train,ytrain) - ŷdtrain = predict(md,xd_train) - #ŷdtrain2 = predict(m,xtrain,ignore_dims=d) - ŷdtest = predict(md,xd_test) - #ŷdtest2 = predict(m,xtest,ignore_dims=d) - if a == 1 - loss_by_cols[d] = norm(ytrain-ŷdtrain)/length(ytrain) - sobol_by_cols[d] = sobol_index(ŷtrain,ŷdtrain) - #loss_by_cols2[d] = norm(ytrain-ŷdtrain2)/length(ytrain) - #sobol_by_cols2[d] = sobol_index(ŷtrain,ŷdtrain2) - #diffest_bycols[d] = norm(ŷdtrain-ŷdtrain2)/length(ytrain) - loss_by_cols_test[d] = norm(ytest-ŷdtest)/length(ytest) - sobol_by_cols_test[d] = sobol_index(ŷtest,ŷdtest) - #loss_by_cols2_test[d] = norm(ytest-ŷdtest2)/length(ytest) - #sobol_by_cols2_test[d] = sobol_index(ŷtest,ŷdtest2) - #diffest_bycols_test[d] = norm(ŷdtest-ŷdtest2)/length(ytest) - else - loss_by_cols[d] = online_mean(norm(ytrain-ŷdtrain)/length(ytrain); mean=loss_by_cols[d],n=a-1) - sobol_by_cols[d] = online_mean(sobol_index(ŷtrain,ŷdtrain) ; mean=sobol_by_cols[d],n=a-1) - #loss_by_cols2[d] = online_mean(norm(ytrain-ŷdtrain2)/length(ytrain); mean=loss_by_cols2[d],n=a-1) - #sobol_by_cols2[d] = online_mean(sobol_index(ŷtrain,ŷdtrain2) ; mean=sobol_by_cols2[d],n=a-1) - #diffest_bycols[d] = online_mean(norm(ŷdtrain-ŷdtrain2)/length(ytrain); mean=diffest_bycols[d],n=a-1) - loss_by_cols_test[d] = online_mean(norm(ytest-ŷdtest)/length(ytest); mean=loss_by_cols_test[d],n=a-1) - sobol_by_cols_test[d] = online_mean(sobol_index(ŷtest,ŷdtest) ; mean=sobol_by_cols_test[d],n=a-1) - #loss_by_cols2_test[d] = online_mean(norm(ytest-ŷdtest2)/length(ytest); mean=loss_by_cols2_test[d],n=a-1) - #sobol_by_cols2_test[d] = online_mean(sobol_index(ŷtest,ŷdtest2) ; mean=sobol_by_cols2_test[d],n=a-1) - #diffest_bycols_test[d] = online_mean(norm(ŷdtest-ŷdtest2)/length(ytest); mean=diffest_bycols_test[d],n=a-1) - end - end -end -# Expected order: ~ [{5,6,4},{3,2},1] good -# ~ [{5,6},{4,3,2,1}] still good but don't see corelation -bar(string.(sortperm(loss_by_cols)),loss_by_cols[sortperm(loss_by_cols)],label="loss_by_cols train") -bar(string.(sortperm(sobol_by_cols)),sobol_by_cols[sortperm(sobol_by_cols)],label="sobol_by_cols train") -bar(string.(sortperm(loss_by_cols2)),loss_by_cols2[sortperm(loss_by_cols2)],label="loss_by_cols2 train") -bar(string.(sortperm(sobol_by_cols2)),sobol_by_cols2[sortperm(sobol_by_cols2)],label="sobol_by_cols2 train") -bar(string.(sortperm(loss_by_cols_test)),loss_by_cols_test[sortperm(loss_by_cols_test)],label="loss_by_cols test") -bar(string.(sortperm(sobol_by_cols_test)),sobol_by_cols_test[sortperm(sobol_by_cols_test)],label="sobol_by_cols test") -bar(string.(sortperm(loss_by_cols2_test)),loss_by_cols2_test[sortperm(loss_by_cols2_test)],label="loss_by_cols2 test") -bar(string.(sortperm(sobol_by_cols2_test)),sobol_by_cols2_test[sortperm(sobol_by_cols2_test)],label="sobol_by_cols2 test") - - - - -d = 5 -xd_train = hcat(xtrain[:,1:d-1],shuffle(xtrain[:,d]),xtrain[:,d+1:end]) -md = RandomForestEstimator(n_trees=50) -fit!(md,xd_train,ytrain) -ŷdtrain = predict(md,xd_train) -loss_d = norm(ytrain-ŷdtrain)/length(ytrain) \ No newline at end of file diff --git a/cancellable/Untitled-3.jl b/cancellable/Untitled-3.jl deleted file mode 100644 index be4e4a68..00000000 --- a/cancellable/Untitled-3.jl +++ /dev/null @@ -1,79 +0,0 @@ - -# Syntetic data generation -# x1: high importance, x2: little importance, x3: mixed effects with x1, x4: highly correlated with x1 but no effects on Y, x5 and x6: no effects on Y -using Random -TESTRNG = Random.GLOBAL_RNG -N = 2000 -D = 6 -nAttempts = 10 -xa = rand(copy(TESTRNG),0:0.0001:10,N,3) -xb = (xa[:,1] .* 0.5 .* rand(0.8:0.001:1.2)) .+ 10 -xc = rand(copy(TESTRNG),0:0.0001:10,N,D-4) -x = hcat(xa,xb,xc) -y = [10*r[1]-r[2]+0.05*r[3]*r[1] for r in eachrow(x) ] -((xtrain,xtest),(ytrain,ytest)) = BetaML.partition([x,y],[0.8,0.2],rng=copy(TESTRNG)) - - -# full cols model: -m = RandomForestEstimator(n_trees=50) -fit!(m,xtrain,ytrain) -ŷtrain = predict(m,xtrain) -loss = norm(ytrain-ŷtrain)/length(ytrain) # this is good - -ŷtest = predict(m,xtest) -loss = norm(ytest-ŷtest)/length(ytest) # this is good - -loss_by_cols = zeros(D) -sobol_by_cols = zeros(D) -loss_by_cols2 = zeros(D) -sobol_by_cols2 = zeros(D) -diffest_bycols = zeros(D) -for a in 1:nAttempts - println("Running attempt $a...") - for d in 1:D - println("- doing modelling without dimension $d ....") - xd_train = hcat(xtrain[:,1:d-1],shuffle(xtrain[:,d]),xtrain[:,d+1:end]) - xd_test = hcat(xtest[:,1:d-1],shuffle(xtest[:,d]),xtest[:,d+1:end]) - md = RandomForestEstimator(n_trees=50) - fit!(md,xd_train,ytrain) - ŷdtrain = predict(md,xd_train) - ŷdtrain2 = predict(m,xtrain,ignore_dims=d) - ŷdtest = predict(md,xd_test) - ŷdtest2 = predict(m,xtest,ignore_dims=d) - if a == 1 - #= - loss_by_cols[d] = norm(ytest-ŷdtest)/length(ytest) - sobol_by_cols[d] = sobol_index(ŷtest,ŷdtest) - loss_by_cols2[d] = norm(ytest-ŷdtest2)/length(ytest) - sobol_by_cols2[d] = sobol_index(ŷtest,ŷdtest2) - diffest_bycols[d] = norm(ŷdtest-ŷdtest2)/length(ytest) - =# - loss_by_cols[d] = norm(ytrain-ŷdtrain)/length(ytrain) - sobol_by_cols[d] = sobol_index(ŷtrain,ŷdtrain) - loss_by_cols2[d] = norm(ytrain-ŷdtrain2)/length(ytrain) - sobol_by_cols2[d] = sobol_index(ŷtrain,ŷdtrain2) - diffest_bycols[d] = norm(ŷdtrain-ŷdtrain2)/length(ytrain) - else - #= - loss_by_cols[d] = online_mean(norm(ytest-ŷdtest)/length(ytest); mean=loss_by_cols[d],n=a-1) - sobol_by_cols[d] = online_mean(sobol_index(ŷtest,ŷdtest) ; mean=sobol_by_cols[d],n=a-1) - loss_by_cols2[d] = online_mean(norm(ytest-ŷdtest2)/length(ytest); mean=loss_by_cols2[d],n=a-1) - sobol_by_cols2[d] = online_mean(sobol_index(ŷtest,ŷdtest2) ; mean=sobol_by_cols2[d],n=a-1) - diffest_bycols[d] = online_mean(norm(ŷdtest-ŷdtest2)/length(ytest); mean=diffest_bycols[d],n=a-1) - =# - loss_by_cols[d] = online_mean(norm(ytrain-ŷdtrain)/length(ytrain); mean=loss_by_cols[d],n=a-1) - sobol_by_cols[d] = online_mean(sobol_index(ŷtrain,ŷdtrain) ; mean=sobol_by_cols[d],n=a-1) - loss_by_cols2[d] = online_mean(norm(ytrain-ŷdtrain2)/length(ytrain); mean=loss_by_cols2[d],n=a-1) - sobol_by_cols2[d] = online_mean(sobol_index(ŷtrain,ŷdtrain2) ; mean=sobol_by_cols2[d],n=a-1) - diffest_bycols[d] = online_mean(norm(ŷdtrain-ŷdtrain2)/length(ytrain); mean=diffest_bycols[d],n=a-1) - end - end -end -# Expected order: ~ [5,6,4,3,2,1] - -d = 5 -xd_train = hcat(xtrain[:,1:d-1],shuffle(xtrain[:,d]),xtrain[:,d+1:end]) -md = RandomForestEstimator(n_trees=50) -fit!(md,xd_train,ytrain) -ŷdtrain = predict(md,xd_train) -loss_d = norm(ytrain-ŷdtrain)/length(ytrain) \ No newline at end of file diff --git a/cancellable/announce.txt b/cancellable/announce.txt deleted file mode 100644 index ddff3bef..00000000 --- a/cancellable/announce.txt +++ /dev/null @@ -1,85 +0,0 @@ -@JuliaRegistrator register - -Release notes: - -- Added `FeatureRanker`, a flexible feature ranking estimator using multiple feature importance metrics -- new functions `kl_divergence` and `sobol_index` -- added option to RF/DT models to ignore specific variables in prediction, by following _both_ the splits on nodes occurring on that dimensions, as the keyword `ignore_dims` to the `predict` function -- added option `sampling_share` to `RandomForestEstimator` model -- DOC: added Benchmarks (but then temporarily removed due to the issue of SystemBenchmark not installable, see [this issue](https://github.com/IanButterworth/SystemBenchmark.jl/issues/64) ) -- DOC: added `FeatureRanker` tutorial -- bugfix on l2loss_by_cv for unsupervised models - - -[Announce]: FeatureRanking: learn which variables contribute the most to the estimation of black box models - -I am pleased to announce the availability of [`FeatureRanker`](https://sylvaticus.github.io/BetaML.jl/dev/Utils.html#BetaML.Utils.FeatureRanker), a simple yet flexible feature ranking estimator where different metrics can be used to estimate the importance of individual variables. - -Key features: -- Choose between loss-based or variance-based (Sobol indices) metrics -- Choose between _permute and relearn_ or _permute only_ strategies, or exploit the ability of some models (typically tree-based) to "ignore" variables at prediction time. -- Choose whether to generate the rank in a single stage (a single loop) or recursively, where at each stage the less important variable is "removed". -- Choose the number of splits and possibly the number of iterations of the splits in the cross-validation used internally to produce the rank -- Works with any estimator model (not just from the BetaML suit) that can be wrapped in a BetaML-like API (`m=ModelName(hyperparameters...); fit_function(m,x,y); predict_function(m,x)` where `fit_function` and `predict_function` can be specified in the `FeatureRanker` options). - -In the following example, we estimate the importance of different variables in predicting house prices using the Boston dataset: - -```julia -# Loading packages... -using Random, Pipe, HTTP, CSV, DataFrames, Plots, BetaML -import Distributions: Normal, quantile -Random.seed!(123) - -# We download the Boston house prices dataset from internet and split it into x and y -dataURL = "https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data" -data = @pipe HTTP.get(dataURL).body |> CSV.File(_, delim=' ', header=false, ignorerepeated=true) |> DataFrame - -var_names = [ - "CRIM", # per capita crime rate by town - "ZN", # proportion of residential land zoned for lots over 25,000 sq.ft. - "INDUS", # proportion of non-retail business acres per town - "CHAS", # Charles River dummy variable (= 1 if tract bounds river; 0 otherwise) - "NOX", # nitric oxides concentration (parts per 10 million) - "RM", # average number of rooms per dwelling - "AGE", # proportion of owner-occupied units built prior to 1940 - "DIS", # weighted distances to five Boston employment centres - "RAD", # index of accessibility to radial highways - "TAX", # full-value property-tax rate per $10,000 - "PTRATIO", # pupil-teacher ratio by town - "B", # 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town - "LSTAT", # % lower status of the population -] -y_name = "MEDV" # Median value of owner-occupied homes in $1000's - -# Our features are a set of 13 explanatory variables, while the label that we want to estimate is the average housing prices: -x = Matrix(data[:,1:13]) -y = data[:,14] - -# We use a Random Forest model as regressor and we compute the variable importance for this model : -fr = FeatureRanker(model=RandomForestEstimator(),nsplits=3,nrepeats=2,recursive=false, ignore_dims_keyword="ignore_dims") -rank = fit!(fr,x,y) - -loss_by_col = info(fr)["loss_by_col"] -sobol_by_col = info(fr)["sobol_by_col"] -loss_by_col_sd = info(fr)["loss_by_col_sd"] -sobol_by_col_sd = info(fr)["sobol_by_col_sd"] -loss_fullmodel = info(fr)["loss_all_cols"] -loss_fullmodel_sd = info(fr)["loss_all_cols_sd"] -ntrials_per_metric = info(fr)["ntrials_per_metric"] - -# Finally we can plot the variable importance, first using the loss metric ("mda") and then the sobol one: -bar(var_names[sortperm(loss_by_col)], loss_by_col[sortperm(loss_by_col)],label="Loss by var", permute=(:x,:y), yerror=quantile(Normal(1,0),0.975) .* (loss_by_col_sd[sortperm(loss_by_col)]./sqrt(ntrials_per_metric)), yrange=[0,0.5]) -vline!([loss_fullmodel], label="Loss with all vars",linewidth=2) -vline!([loss_fullmodel-quantile(Normal(1,0),0.975) * loss_fullmodel_sd/sqrt(ntrials_per_metric), - loss_fullmodel+quantile(Normal(1,0),0.975) * loss_fullmodel_sd/sqrt(ntrials_per_metric), -], label=nothing,linecolor=:black,linestyle=:dot,linewidth=1) - -bar(var_names[sortperm(sobol_by_col)],sobol_by_col[sortperm(sobol_by_col)],label="Sobol index by col", permute=(:x,:y), yerror=quantile(Normal(1,0),0.975) .* (sobol_by_col_sd[sortperm(sobol_by_col)]./sqrt(ntrials_per_metric)), yrange=[0,0.4]) -``` - -As we can see, the two analyses agree on the most important variables, showing that the size of the house (number of rooms), the percentage of low-income population in the neighbourhood and, to a lesser extent, the distance to employment centres are the most important variables for the estimation of house price in the Boston area. - -`FeatureRanker` is shipped with the [Beta Machine Learning Toolkit (BetaML.jl)](https://github.com/sylvaticus/BetaML.jl) v0.12. A tutorial is available [here](https://sylvaticus.github.io/BetaML.jl/dev/tutorials/Feature%20importance/Feature_importance.html). - - - diff --git a/cancellable/using Statistics.jl b/cancellable/using Statistics.jl deleted file mode 100644 index 711cfa77..00000000 --- a/cancellable/using Statistics.jl +++ /dev/null @@ -1,29 +0,0 @@ -using Statistics - -scalars = [1.0,1.1,1.2] -vectors = [[1.0,10.0],[1.1,11],[1.2,12]] -vofv = [[[1.0,10.0],[100,1000]], [[1.1,11],[111,1111]],[[1.2,12],[122,1222]]] - -mean(scalars) -std(scalars) -mean(vectors) -std(vectors) -mean(vofv) -std.(vofv) - - -mean([[1.1,3.1],[1.3,3.3]]) - -using DataFrames, Plots - -df = DataFrame(group=["A", "B", "C"], total=[7.7, 4.6, 5.1], std_error = [0.04, 0.05, 0.06]) - -bar(df.group, df.total, c=:blues, lw=0, widen=false) -plot!(1/2:(ncol(df)-1/2), df.total, lw=0, yerror=20*df.std_error, ms=10) - -group=["A", "B", "C"] -total=[7.7, 4.6, 5.1] -std_error = [0.04, 0.05, 0.06] - -bar(group, total, c=:blues, lw=0, widen=false) -plot!(1/2:(3-1/2), total, lw=0, yerror=20*std_error, ms=10) diff --git a/docs/Manifest_old.toml b/docs/Manifest_old.toml deleted file mode 100644 index 7f45716d..00000000 --- a/docs/Manifest_old.toml +++ /dev/null @@ -1,2327 +0,0 @@ -# This file is machine-generated - editing it directly is not advised - -julia_version = "1.10.0" -manifest_format = "2.0" -project_hash = "4babdefef2fbb529ff83e45595ee20526dac2b29" - -[[deps.ANSIColoredPrinters]] -git-tree-sha1 = "574baf8110975760d391c710b6341da1afa48d8c" -uuid = "a4c015fc-c6ff-483c-b24f-f7ea428134e9" -version = "0.0.1" - -[[deps.AbstractFFTs]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "d92ad398961a3ed262d8bf04a1a2b8340f915fef" -uuid = "621f4979-c628-5d54-868e-fcf4e3e8185c" -version = "1.5.0" -weakdeps = ["ChainRulesCore", "Test"] - - [deps.AbstractFFTs.extensions] - AbstractFFTsChainRulesCoreExt = "ChainRulesCore" - AbstractFFTsTestExt = "Test" - -[[deps.AbstractTrees]] -git-tree-sha1 = "2d9c9a55f9c93e8887ad391fbae72f8ef55e1177" -uuid = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" -version = "0.4.5" - -[[deps.Adapt]] -deps = ["LinearAlgebra", "Requires"] -git-tree-sha1 = "cde29ddf7e5726c9fb511f340244ea3481267608" -uuid = "79e6a3ab-5dfb-504d-930d-738a2a938a0e" -version = "3.7.2" -weakdeps = ["StaticArrays"] - - [deps.Adapt.extensions] - AdaptStaticArraysExt = "StaticArrays" - -[[deps.ArgCheck]] -git-tree-sha1 = "a3a402a35a2f7e0b87828ccabbd5ebfbebe356b4" -uuid = "dce04be8-c92d-5529-be00-80e4d2c0e197" -version = "2.3.0" - -[[deps.ArgTools]] -uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f" -version = "1.1.1" - -[[deps.Arpack]] -deps = ["Arpack_jll", "Libdl", "LinearAlgebra", "Logging"] -git-tree-sha1 = "9b9b347613394885fd1c8c7729bfc60528faa436" -uuid = "7d9fca2a-8960-54d3-9f78-7d1dccf2cb97" -version = "0.5.4" - -[[deps.Arpack_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "OpenBLAS_jll", "Pkg"] -git-tree-sha1 = "5ba6c757e8feccf03a1554dfaf3e26b3cfc7fd5e" -uuid = "68821587-b530-5797-8361-c406ea357684" -version = "3.5.1+1" - -[[deps.ArrayInterface]] -deps = ["Adapt", "LinearAlgebra", "Requires", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "c5aeb516a84459e0318a02507d2261edad97eb75" -uuid = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9" -version = "7.7.1" - - [deps.ArrayInterface.extensions] - ArrayInterfaceBandedMatricesExt = "BandedMatrices" - ArrayInterfaceBlockBandedMatricesExt = "BlockBandedMatrices" - ArrayInterfaceCUDAExt = "CUDA" - ArrayInterfaceGPUArraysCoreExt = "GPUArraysCore" - ArrayInterfaceStaticArraysCoreExt = "StaticArraysCore" - ArrayInterfaceTrackerExt = "Tracker" - - [deps.ArrayInterface.weakdeps] - BandedMatrices = "aae01518-5342-5314-be14-df237901396f" - BlockBandedMatrices = "ffab5731-97b5-5995-9138-79e8c1846df0" - CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba" - GPUArraysCore = "46192b85-c4d5-4398-a991-12ede77f4527" - StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" - Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" - -[[deps.Artifacts]] -uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33" - -[[deps.Atomix]] -deps = ["UnsafeAtomics"] -git-tree-sha1 = "c06a868224ecba914baa6942988e2f2aade419be" -uuid = "a9b6321e-bd34-4604-b9c9-b65b8de01458" -version = "0.1.0" - -[[deps.BFloat16s]] -deps = ["LinearAlgebra", "Printf", "Random", "Test"] -git-tree-sha1 = "a598ecb0d717092b5539dbbe890c98bac842b072" -uuid = "ab4f0b2a-ad5b-11e8-123f-65d77653426b" -version = "0.2.0" - -[[deps.BSON]] -git-tree-sha1 = "4c3e506685c527ac6a54ccc0c8c76fd6f91b42fb" -uuid = "fbb218c0-5317-5bc6-957e-2ee96dd4b1f0" -version = "0.3.9" - -[[deps.BangBang]] -deps = ["Compat", "ConstructionBase", "InitialValues", "LinearAlgebra", "Requires", "Setfield", "Tables"] -git-tree-sha1 = "7aa7ad1682f3d5754e3491bb59b8103cae28e3a3" -uuid = "198e06fe-97b7-11e9-32a5-e1d131e6ad66" -version = "0.3.40" - - [deps.BangBang.extensions] - BangBangChainRulesCoreExt = "ChainRulesCore" - BangBangDataFramesExt = "DataFrames" - BangBangStaticArraysExt = "StaticArrays" - BangBangStructArraysExt = "StructArrays" - BangBangTypedTablesExt = "TypedTables" - - [deps.BangBang.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - StructArrays = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" - TypedTables = "9d95f2ec-7b3d-5a63-8d20-e2491e220bb9" - -[[deps.Base64]] -uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f" - -[[deps.Baselet]] -git-tree-sha1 = "aebf55e6d7795e02ca500a689d326ac979aaf89e" -uuid = "9718e550-a3fa-408a-8086-8db961cd8217" -version = "0.1.1" - -[[deps.BenchmarkTools]] -deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"] -git-tree-sha1 = "f1dff6729bc61f4d49e140da1af55dcd1ac97b2f" -uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" -version = "1.5.0" - -[[deps.BetaML]] -deps = ["AbstractTrees", "CategoricalArrays", "Combinatorics", "DelimitedFiles", "Distributions", "DocStringExtensions", "ForceImport", "JLD2", "LinearAlgebra", "LoopVectorization", "MLJModelInterface", "PDMats", "PrecompileTools", "Printf", "ProgressMeter", "Random", "Reexport", "StableRNGs", "StaticArrays", "Statistics", "StatsBase", "Test", "Zygote"] -path = "/home/lobianco/.julia/dev/BetaML" -uuid = "024491cd-cc6b-443e-8034-08ea7eb7db2b" -version = "0.11.4" - -[[deps.BinDeps]] -deps = ["Libdl", "Pkg", "SHA", "URIParser", "Unicode"] -git-tree-sha1 = "1289b57e8cf019aede076edab0587eb9644175bd" -uuid = "9e28174c-4ba2-5203-b857-d8d62c4213ee" -version = "1.0.2" - -[[deps.BinaryProvider]] -deps = ["Libdl", "Logging", "SHA"] -git-tree-sha1 = "ecdec412a9abc8db54c0efc5548c64dfce072058" -uuid = "b99e7846-7c00-51b0-8f62-c81ae34c0232" -version = "0.5.10" - -[[deps.BitTwiddlingConvenienceFunctions]] -deps = ["Static"] -git-tree-sha1 = "0c5f81f47bbbcf4aea7b2959135713459170798b" -uuid = "62783981-4cbd-42fc-bca8-16325de8dc4b" -version = "0.1.5" - -[[deps.BufferedStreams]] -git-tree-sha1 = "4ae47f9a4b1dc19897d3743ff13685925c5202ec" -uuid = "e1450e63-4bb3-523b-b2a4-4ffa8c0fd77d" -version = "1.2.1" - -[[deps.Bzip2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "9e2a6b69137e6969bab0152632dcb3bc108c8bdd" -uuid = "6e34b625-4abd-537c-b88f-471c36dfa7a0" -version = "1.0.8+1" - -[[deps.CEnum]] -git-tree-sha1 = "eb4cb44a499229b3b8426dcfb5dd85333951ff90" -uuid = "fa961155-64e5-5f13-b03f-caf6b980ea82" -version = "0.4.2" - -[[deps.CPUSummary]] -deps = ["CpuId", "IfElse", "PrecompileTools", "Static"] -git-tree-sha1 = "601f7e7b3d36f18790e2caf83a882d88e9b71ff1" -uuid = "2a0fbf3d-bb9c-48f3-b0a9-814d99fd7ab9" -version = "0.2.4" - -[[deps.CSV]] -deps = ["Dates", "Mmap", "Parsers", "PooledArrays", "SentinelArrays", "Tables", "Unicode"] -git-tree-sha1 = "b83aa3f513be680454437a0eee21001607e5d983" -uuid = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" -version = "0.8.5" - -[[deps.CUDA]] -deps = ["AbstractFFTs", "Adapt", "BFloat16s", "CEnum", "CompilerSupportLibraries_jll", "ExprTools", "GPUArrays", "GPUCompiler", "LLVM", "LazyArtifacts", "Libdl", "LinearAlgebra", "Logging", "Printf", "Random", "Random123", "RandomNumbers", "Reexport", "Requires", "SparseArrays", "SpecialFunctions", "TimerOutputs"] -git-tree-sha1 = "6717cb9a3425ebb7b31ca4f832823615d175f64a" -uuid = "052768ef-5323-5732-b1bb-66c8b64840ba" -version = "3.13.1" - -[[deps.Cairo_jll]] -deps = ["Artifacts", "Bzip2_jll", "CompilerSupportLibraries_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "JLLWrappers", "LZO_jll", "Libdl", "Pixman_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "a4c43f59baa34011e303e76f5c8c91bf58415aaf" -uuid = "83423d85-b0ee-5818-9007-b63ccbeb887a" -version = "1.18.0+1" - -[[deps.Calculus]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "f641eb0a4f00c343bbc32346e1217b86f3ce9dad" -uuid = "49dc2e85-a5d0-5ad3-a950-438e2897f1b9" -version = "0.5.1" - -[[deps.CategoricalArrays]] -deps = ["DataAPI", "Future", "Missings", "Printf", "Requires", "Statistics", "Unicode"] -git-tree-sha1 = "1568b28f91293458345dabba6a5ea3f183250a61" -uuid = "324d7699-5711-5eae-9e2f-1d82baa6b597" -version = "0.10.8" -weakdeps = ["JSON", "RecipesBase", "SentinelArrays", "StructTypes"] - - [deps.CategoricalArrays.extensions] - CategoricalArraysJSONExt = "JSON" - CategoricalArraysRecipesBaseExt = "RecipesBase" - CategoricalArraysSentinelArraysExt = "SentinelArrays" - CategoricalArraysStructTypesExt = "StructTypes" - -[[deps.ChainRules]] -deps = ["Adapt", "ChainRulesCore", "Compat", "Distributed", "GPUArraysCore", "IrrationalConstants", "LinearAlgebra", "Random", "RealDot", "SparseArrays", "SparseInverseSubset", "Statistics", "StructArrays", "SuiteSparse"] -git-tree-sha1 = "4e42872be98fa3343c4f8458cbda8c5c6a6fa97c" -uuid = "082447d4-558c-5d27-93f4-14fc19e9eca2" -version = "1.63.0" - -[[deps.ChainRulesCore]] -deps = ["Compat", "LinearAlgebra"] -git-tree-sha1 = "575cd02e080939a33b6df6c5853d14924c08e35b" -uuid = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -version = "1.23.0" -weakdeps = ["SparseArrays"] - - [deps.ChainRulesCore.extensions] - ChainRulesCoreSparseArraysExt = "SparseArrays" - -[[deps.CloseOpenIntervals]] -deps = ["Static", "StaticArrayInterface"] -git-tree-sha1 = "70232f82ffaab9dc52585e0dd043b5e0c6b714f1" -uuid = "fb6a15b2-703c-40df-9091-08a04967cfa9" -version = "0.1.12" - -[[deps.Clustering]] -deps = ["Distances", "LinearAlgebra", "NearestNeighbors", "Printf", "Random", "SparseArrays", "Statistics", "StatsBase"] -git-tree-sha1 = "9ebb045901e9bbf58767a9f34ff89831ed711aae" -uuid = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5" -version = "0.15.7" - -[[deps.CodecZlib]] -deps = ["TranscodingStreams", "Zlib_jll"] -git-tree-sha1 = "59939d8a997469ee05c4b4944560a820f9ba0d73" -uuid = "944b1d66-785c-5afd-91f1-9de20f533193" -version = "0.7.4" - -[[deps.ColorSchemes]] -deps = ["ColorTypes", "ColorVectorSpace", "Colors", "FixedPointNumbers", "PrecompileTools", "Random"] -git-tree-sha1 = "67c1f244b991cad9b0aa4b7540fb758c2488b129" -uuid = "35d6a980-a343-548e-a6ea-1d62b119f2f4" -version = "3.24.0" - -[[deps.ColorTypes]] -deps = ["FixedPointNumbers", "Random"] -git-tree-sha1 = "eb7f0f8307f71fac7c606984ea5fb2817275d6e4" -uuid = "3da002f7-5984-5a60-b8a6-cbb66c0b333f" -version = "0.11.4" - -[[deps.ColorVectorSpace]] -deps = ["ColorTypes", "FixedPointNumbers", "LinearAlgebra", "SpecialFunctions", "Statistics", "TensorCore"] -git-tree-sha1 = "600cc5508d66b78aae350f7accdb58763ac18589" -uuid = "c3611d14-8923-5661-9e6a-0046d554d3a4" -version = "0.9.10" - -[[deps.Colors]] -deps = ["ColorTypes", "FixedPointNumbers", "Reexport"] -git-tree-sha1 = "fc08e5930ee9a4e03f84bfb5211cb54e7769758a" -uuid = "5ae59095-9a9b-59fe-a467-6f913c188581" -version = "0.12.10" - -[[deps.Combinatorics]] -git-tree-sha1 = "08c8b6831dc00bfea825826be0bc8336fc369860" -uuid = "861a8166-3701-5b0c-9a16-15d98fcdc6aa" -version = "1.0.2" - -[[deps.CommonSubexpressions]] -deps = ["MacroTools", "Test"] -git-tree-sha1 = "7b8a93dba8af7e3b42fecabf646260105ac373f7" -uuid = "bbf7d656-a473-5ed7-a52c-81e309532950" -version = "0.3.0" - -[[deps.Compat]] -deps = ["TOML", "UUIDs"] -git-tree-sha1 = "c955881e3c981181362ae4088b35995446298b80" -uuid = "34da2185-b29b-5c13-b0c7-acf172513d20" -version = "4.14.0" -weakdeps = ["Dates", "LinearAlgebra"] - - [deps.Compat.extensions] - CompatLinearAlgebraExt = "LinearAlgebra" - -[[deps.CompilerSupportLibraries_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "e66e0078-7015-5450-92f7-15fbd957f2ae" -version = "1.0.5+1" - -[[deps.CompositionsBase]] -git-tree-sha1 = "802bb88cd69dfd1509f6670416bd4434015693ad" -uuid = "a33af91c-f02d-484b-be07-31d278c5ca2b" -version = "0.1.2" - - [deps.CompositionsBase.extensions] - CompositionsBaseInverseFunctionsExt = "InverseFunctions" - - [deps.CompositionsBase.weakdeps] - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.ComputationalResources]] -git-tree-sha1 = "52cb3ec90e8a8bea0e62e275ba577ad0f74821f7" -uuid = "ed09eef8-17a6-5b46-8889-db040fac31e3" -version = "0.3.2" - -[[deps.Conda]] -deps = ["Downloads", "JSON", "VersionParsing"] -git-tree-sha1 = "51cab8e982c5b598eea9c8ceaced4b58d9dd37c9" -uuid = "8f4d0f93-b110-5947-807f-2305c1781a2d" -version = "1.10.0" - -[[deps.ConstructionBase]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "c53fc348ca4d40d7b371e71fd52251839080cbc9" -uuid = "187b0558-2788-49d3-abe0-74a17ed4e7c9" -version = "1.5.4" - - [deps.ConstructionBase.extensions] - ConstructionBaseIntervalSetsExt = "IntervalSets" - ConstructionBaseStaticArraysExt = "StaticArrays" - - [deps.ConstructionBase.weakdeps] - IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" - StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" - -[[deps.ContextVariablesX]] -deps = ["Compat", "Logging", "UUIDs"] -git-tree-sha1 = "25cc3803f1030ab855e383129dcd3dc294e322cc" -uuid = "6add18c4-b38d-439d-96f6-d6bc489c04c5" -version = "0.1.3" - -[[deps.Contour]] -git-tree-sha1 = "d05d9e7b7aedff4e5b51a029dced05cfb6125781" -uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" -version = "0.6.2" - -[[deps.CpuId]] -deps = ["Markdown"] -git-tree-sha1 = "fcbb72b032692610bfbdb15018ac16a36cf2e406" -uuid = "adafc99b-e345-5852-983c-f28acb93d879" -version = "0.3.1" - -[[deps.Crayons]] -git-tree-sha1 = "249fe38abf76d48563e2f4556bebd215aa317e15" -uuid = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" -version = "4.1.1" - -[[deps.DataAPI]] -git-tree-sha1 = "abe83f3a2f1b857aac70ef8b269080af17764bbe" -uuid = "9a962f9c-6df0-11e9-0e5d-c546b8b5ee8a" -version = "1.16.0" - -[[deps.DataDeps]] -deps = ["BinaryProvider", "HTTP", "Libdl", "Reexport", "SHA", "p7zip_jll"] -git-tree-sha1 = "e299d8267135ef2f9c941a764006697082c1e7e8" -uuid = "124859b0-ceae-595e-8997-d05f6a7a8dfe" -version = "0.7.8" - -[[deps.DataFrames]] -deps = ["Compat", "DataAPI", "Future", "InvertedIndices", "IteratorInterfaceExtensions", "LinearAlgebra", "Markdown", "Missings", "PooledArrays", "PrettyTables", "Printf", "REPL", "Reexport", "SortingAlgorithms", "Statistics", "TableTraits", "Tables", "Unicode"] -git-tree-sha1 = "db2a9cb664fcea7836da4b414c3278d71dd602d2" -uuid = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -version = "1.3.6" - -[[deps.DataStructures]] -deps = ["Compat", "InteractiveUtils", "OrderedCollections"] -git-tree-sha1 = "0f4b5d62a88d8f59003e43c25a8a90de9eb76317" -uuid = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8" -version = "0.18.18" - -[[deps.DataValueInterfaces]] -git-tree-sha1 = "bfc1187b79289637fa0ef6d4436ebdfe6905cbd6" -uuid = "e2d170a0-9d28-54be-80f0-106bbe20a464" -version = "1.0.0" - -[[deps.Dates]] -deps = ["Printf"] -uuid = "ade2ca70-3891-5945-98fb-dc099432e06a" - -[[deps.DecisionTree]] -deps = ["AbstractTrees", "DelimitedFiles", "LinearAlgebra", "Random", "ScikitLearnBase", "Statistics"] -git-tree-sha1 = "526ca14aaaf2d5a0e242f3a8a7966eb9065d7d78" -uuid = "7806a523-6efd-50cb-b5f6-3fa6f1930dbb" -version = "0.12.4" - -[[deps.DefineSingletons]] -git-tree-sha1 = "0fba8b706d0178b4dc7fd44a96a92382c9065c2c" -uuid = "244e2a9f-e319-4986-a169-4d1fe445cd52" -version = "0.1.2" - -[[deps.DelimitedFiles]] -deps = ["Mmap"] -git-tree-sha1 = "9e2f36d3c96a820c678f2f1f1782582fcf685bae" -uuid = "8bb1440f-4735-579b-a4ab-409b98df4dab" -version = "1.9.1" - -[[deps.DiffResults]] -deps = ["StaticArraysCore"] -git-tree-sha1 = "782dd5f4561f5d267313f23853baaaa4c52ea621" -uuid = "163ba53b-c6d8-5494-b064-1a9d43ac40c5" -version = "1.1.0" - -[[deps.DiffRules]] -deps = ["IrrationalConstants", "LogExpFunctions", "NaNMath", "Random", "SpecialFunctions"] -git-tree-sha1 = "23163d55f885173722d1e4cf0f6110cdbaf7e272" -uuid = "b552c78f-8df3-52c6-915a-8e097449b14b" -version = "1.15.1" - -[[deps.Distances]] -deps = ["LinearAlgebra", "Statistics", "StatsAPI"] -git-tree-sha1 = "66c4c81f259586e8f002eacebc177e1fb06363b0" -uuid = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" -version = "0.10.11" -weakdeps = ["ChainRulesCore", "SparseArrays"] - - [deps.Distances.extensions] - DistancesChainRulesCoreExt = "ChainRulesCore" - DistancesSparseArraysExt = "SparseArrays" - -[[deps.Distributed]] -deps = ["Random", "Serialization", "Sockets"] -uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" - -[[deps.Distributions]] -deps = ["FillArrays", "LinearAlgebra", "PDMats", "Printf", "QuadGK", "Random", "SpecialFunctions", "Statistics", "StatsAPI", "StatsBase", "StatsFuns"] -git-tree-sha1 = "7c302d7a5fec5214eb8a5a4c466dcf7a51fcf169" -uuid = "31c24e10-a181-5473-b8eb-7969acd0382f" -version = "0.25.107" - - [deps.Distributions.extensions] - DistributionsChainRulesCoreExt = "ChainRulesCore" - DistributionsDensityInterfaceExt = "DensityInterface" - DistributionsTestExt = "Test" - - [deps.Distributions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - DensityInterface = "b429d917-457f-4dbc-8f4c-0cc954292b1d" - Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.DocStringExtensions]] -deps = ["LibGit2"] -git-tree-sha1 = "2fb1e02f2b635d0845df5d7c167fec4dd739b00d" -uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.9.3" - -[[deps.Documenter]] -deps = ["ANSIColoredPrinters", "AbstractTrees", "Base64", "CodecZlib", "Dates", "DocStringExtensions", "Downloads", "Git", "IOCapture", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "MarkdownAST", "Pkg", "PrecompileTools", "REPL", "RegistryInstances", "SHA", "TOML", "Test", "Unicode"] -git-tree-sha1 = "4a40af50e8b24333b9ec6892546d9ca5724228eb" -uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "1.3.0" - -[[deps.Downloads]] -deps = ["ArgTools", "FileWatching", "LibCURL", "NetworkOptions"] -uuid = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -version = "1.6.0" - -[[deps.DualNumbers]] -deps = ["Calculus", "NaNMath", "SpecialFunctions"] -git-tree-sha1 = "5837a837389fccf076445fce071c8ddaea35a566" -uuid = "fa6b7ba4-c1ee-5f82-b5fc-ecf0adba8f74" -version = "0.6.8" - -[[deps.EarlyStopping]] -deps = ["Dates", "Statistics"] -git-tree-sha1 = "ea0b56527cefce87419d4b7559811bd96974a6c8" -uuid = "792122b4-ca99-40de-a6bc-6742525f08b6" -version = "0.1.9" - -[[deps.EpollShim_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8e9441ee83492030ace98f9789a654a6d0b1f643" -uuid = "2702e6a9-849d-5ed8-8c21-79e8b8f9ee43" -version = "0.0.20230411+0" - -[[deps.Expat_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "4558ab818dcceaab612d1bb8c19cee87eda2b83c" -uuid = "2e619515-83b5-522b-bb60-26c02a35a201" -version = "2.5.0+0" - -[[deps.ExprTools]] -git-tree-sha1 = "27415f162e6028e81c72b82ef756bf321213b6ec" -uuid = "e2ba6199-217a-4e67-a87a-7c52f15ade04" -version = "0.1.10" - -[[deps.FFMPEG]] -deps = ["FFMPEG_jll"] -git-tree-sha1 = "b57e3acbe22f8484b4b5ff66a7499717fe1a9cc8" -uuid = "c87230d0-a227-11e9-1b43-d7ebe4e7570a" -version = "0.4.1" - -[[deps.FFMPEG_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "JLLWrappers", "LAME_jll", "Libdl", "Ogg_jll", "OpenSSL_jll", "Opus_jll", "PCRE2_jll", "Zlib_jll", "libaom_jll", "libass_jll", "libfdk_aac_jll", "libvorbis_jll", "x264_jll", "x265_jll"] -git-tree-sha1 = "466d45dc38e15794ec7d5d63ec03d776a9aff36e" -uuid = "b22a6f82-2f65-5046-a5b2-351ab43fb4e5" -version = "4.4.4+1" - -[[deps.FLoops]] -deps = ["BangBang", "Compat", "FLoopsBase", "InitialValues", "JuliaVariables", "MLStyle", "Serialization", "Setfield", "Transducers"] -git-tree-sha1 = "ffb97765602e3cbe59a0589d237bf07f245a8576" -uuid = "cc61a311-1640-44b5-9fba-1b764f453329" -version = "0.2.1" - -[[deps.FLoopsBase]] -deps = ["ContextVariablesX"] -git-tree-sha1 = "656f7a6859be8673bf1f35da5670246b923964f7" -uuid = "b9860ae5-e623-471e-878b-f6a53c775ea6" -version = "0.1.1" - -[[deps.FileIO]] -deps = ["Pkg", "Requires", "UUIDs"] -git-tree-sha1 = "82d8afa92ecf4b52d78d869f038ebfb881267322" -uuid = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" -version = "1.16.3" - -[[deps.FilePathsBase]] -deps = ["Compat", "Dates", "Mmap", "Printf", "Test", "UUIDs"] -git-tree-sha1 = "9f00e42f8d99fdde64d40c8ea5d14269a2e2c1aa" -uuid = "48062228-2e41-5def-b9a4-89aafe57970f" -version = "0.9.21" - -[[deps.FileWatching]] -uuid = "7b1f6079-737a-58dc-b8bc-7a2ca5c1b5ee" - -[[deps.FillArrays]] -deps = ["LinearAlgebra", "Random"] -git-tree-sha1 = "5b93957f6dcd33fc343044af3d48c215be2562f1" -uuid = "1a297f60-69ca-5386-bcde-b61e274b549b" -version = "1.9.3" -weakdeps = ["PDMats", "SparseArrays", "Statistics"] - - [deps.FillArrays.extensions] - FillArraysPDMatsExt = "PDMats" - FillArraysSparseArraysExt = "SparseArrays" - FillArraysStatisticsExt = "Statistics" - -[[deps.FixedPointNumbers]] -deps = ["Statistics"] -git-tree-sha1 = "335bfdceacc84c5cdf16aadc768aa5ddfc5383cc" -uuid = "53c48c17-4a7d-5ca2-90c5-79b7896eea93" -version = "0.8.4" - -[[deps.Flux]] -deps = ["Adapt", "CUDA", "ChainRulesCore", "Functors", "LinearAlgebra", "MLUtils", "MacroTools", "NNlib", "NNlibCUDA", "OneHotArrays", "Optimisers", "ProgressLogging", "Random", "Reexport", "SparseArrays", "SpecialFunctions", "Statistics", "StatsBase", "Zygote"] -git-tree-sha1 = "4ff3a1d7b0dd38f2fc38e813bc801f817639c1f2" -uuid = "587475ba-b771-5e3f-ad9e-33799f191a9c" -version = "0.13.13" - -[[deps.Fontconfig_jll]] -deps = ["Artifacts", "Bzip2_jll", "Expat_jll", "FreeType2_jll", "JLLWrappers", "Libdl", "Libuuid_jll", "Pkg", "Zlib_jll"] -git-tree-sha1 = "21efd19106a55620a188615da6d3d06cd7f6ee03" -uuid = "a3f928ae-7b40-5064-980b-68af3947d34b" -version = "2.13.93+0" - -[[deps.ForceImport]] -deps = ["Test"] -git-tree-sha1 = "7ac07d5194360af910146abd33af89bb69541194" -uuid = "9dda63f9-cce7-5873-89fa-eccbb2fffcde" -version = "0.0.3" - -[[deps.Format]] -git-tree-sha1 = "f3cf88025f6d03c194d73f5d13fee9004a108329" -uuid = "1fa38f19-a742-5d3f-a2b9-30dd87b9d5f8" -version = "1.3.6" - -[[deps.Formatting]] -deps = ["Logging", "Printf"] -git-tree-sha1 = "fb409abab2caf118986fc597ba84b50cbaf00b87" -uuid = "59287772-0a20-5a39-b81b-1366585eb4c0" -version = "0.4.3" - -[[deps.ForwardDiff]] -deps = ["CommonSubexpressions", "DiffResults", "DiffRules", "LinearAlgebra", "LogExpFunctions", "NaNMath", "Preferences", "Printf", "Random", "SpecialFunctions"] -git-tree-sha1 = "cf0fe81336da9fb90944683b8c41984b08793dad" -uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "0.10.36" -weakdeps = ["StaticArrays"] - - [deps.ForwardDiff.extensions] - ForwardDiffStaticArraysExt = "StaticArrays" - -[[deps.FreeType2_jll]] -deps = ["Artifacts", "Bzip2_jll", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "d8db6a5a2fe1381c1ea4ef2cab7c69c2de7f9ea0" -uuid = "d7e528f0-a631-5988-bf34-fe36492bcfd7" -version = "2.13.1+0" - -[[deps.FriBidi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "aa31987c2ba8704e23c6c8ba8a4f769d5d7e4f91" -uuid = "559328eb-81f9-559d-9380-de523a88c83c" -version = "1.0.10+0" - -[[deps.Functors]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "8ae30e786837ce0a24f5e2186938bf3251ab94b2" -uuid = "d9f16b24-f501-4c13-a1f2-28368ffc5196" -version = "0.4.8" - -[[deps.Future]] -deps = ["Random"] -uuid = "9fa8497b-333b-5362-9e8d-4d0656e87820" - -[[deps.GLFW_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libglvnd_jll", "Xorg_libXcursor_jll", "Xorg_libXi_jll", "Xorg_libXinerama_jll", "Xorg_libXrandr_jll"] -git-tree-sha1 = "ff38ba61beff76b8f4acad8ab0c97ef73bb670cb" -uuid = "0656b61e-2033-5cc2-a64a-77c0f6c09b89" -version = "3.3.9+0" - -[[deps.GPUArrays]] -deps = ["Adapt", "GPUArraysCore", "LLVM", "LinearAlgebra", "Printf", "Random", "Reexport", "Serialization", "Statistics"] -git-tree-sha1 = "2e57b4a4f9cc15e85a24d603256fe08e527f48d1" -uuid = "0c68f7d7-f131-5f86-a1c3-88cf8149b2d7" -version = "8.8.1" - -[[deps.GPUArraysCore]] -deps = ["Adapt"] -git-tree-sha1 = "2d6ca471a6c7b536127afccfa7564b5b39227fe0" -uuid = "46192b85-c4d5-4398-a991-12ede77f4527" -version = "0.1.5" - -[[deps.GPUCompiler]] -deps = ["ExprTools", "InteractiveUtils", "LLVM", "Libdl", "Logging", "TimerOutputs", "UUIDs"] -git-tree-sha1 = "19d693666a304e8c371798f4900f7435558c7cde" -uuid = "61eb1bfa-7361-4325-ad38-22787b887f55" -version = "0.17.3" - -[[deps.GR]] -deps = ["Artifacts", "Base64", "DelimitedFiles", "Downloads", "GR_jll", "HTTP", "JSON", "Libdl", "LinearAlgebra", "Pkg", "Preferences", "Printf", "Random", "Serialization", "Sockets", "TOML", "Tar", "Test", "UUIDs", "p7zip_jll"] -git-tree-sha1 = "3437ade7073682993e092ca570ad68a2aba26983" -uuid = "28b8d3ca-fb5f-59d9-8090-bfdbd6d07a71" -version = "0.73.3" - -[[deps.GR_jll]] -deps = ["Artifacts", "Bzip2_jll", "Cairo_jll", "FFMPEG_jll", "Fontconfig_jll", "FreeType2_jll", "GLFW_jll", "JLLWrappers", "JpegTurbo_jll", "Libdl", "Libtiff_jll", "Pixman_jll", "Qt6Base_jll", "Zlib_jll", "libpng_jll"] -git-tree-sha1 = "a96d5c713e6aa28c242b0d25c1347e258d6541ab" -uuid = "d2c73de3-f751-5644-a686-071e5b155ba9" -version = "0.73.3+0" - -[[deps.GZip]] -deps = ["Libdl"] -git-tree-sha1 = "8d838ee3772e00c75d6cc06bb08891379868c18d" -uuid = "92fee26a-97fe-5a0c-ad85-20a5f3185b63" -version = "0.5.2" - -[[deps.GaussianMixtures]] -deps = ["Arpack", "Clustering", "Compat", "DelimitedFiles", "Distributed", "Distributions", "FileIO", "JLD2", "LinearAlgebra", "Logging", "PDMats", "Printf", "Random", "ScikitLearnBase", "SpecialFunctions", "Statistics", "StatsBase"] -git-tree-sha1 = "289e5126240812b9fb0ebada26c1e5ad305f904f" -uuid = "cc18c42c-b769-54ff-9e2a-b28141a64aae" -version = "0.3.9" - -[[deps.Gettext_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Libiconv_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "9b02998aba7bf074d14de89f9d37ca24a1a0b046" -uuid = "78b55507-aeef-58d4-861c-77aaff3498b1" -version = "0.21.0+0" - -[[deps.Git]] -deps = ["Git_jll"] -git-tree-sha1 = "04eff47b1354d702c3a85e8ab23d539bb7d5957e" -uuid = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" -version = "1.3.1" - -[[deps.Git_jll]] -deps = ["Artifacts", "Expat_jll", "JLLWrappers", "LibCURL_jll", "Libdl", "Libiconv_jll", "OpenSSL_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "12945451c5d0e2d0dca0724c3a8d6448b46bbdf9" -uuid = "f8c6e375-362e-5223-8a59-34ff63f689eb" -version = "2.44.0+1" - -[[deps.Glib_jll]] -deps = ["Artifacts", "Gettext_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Libiconv_jll", "Libmount_jll", "PCRE2_jll", "Zlib_jll"] -git-tree-sha1 = "359a1ba2e320790ddbe4ee8b4d54a305c0ea2aff" -uuid = "7746bdde-850d-59dc-9ae8-88ece973131d" -version = "2.80.0+0" - -[[deps.Graphite2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "344bf40dcab1073aca04aa0df4fb092f920e4011" -uuid = "3b182d85-2403-5c21-9c21-1e1f0cc25472" -version = "1.3.14+0" - -[[deps.Grisu]] -git-tree-sha1 = "53bb909d1151e57e2484c3d1b53e19552b887fb2" -uuid = "42e2da0e-8278-4e71-bc24-59509adca0fe" -version = "1.0.2" - -[[deps.HDF5]] -deps = ["Compat", "HDF5_jll", "Libdl", "MPIPreferences", "Mmap", "Preferences", "Printf", "Random", "Requires", "UUIDs"] -git-tree-sha1 = "26407bd1c60129062cec9da63dc7d08251544d53" -uuid = "f67ccb44-e63f-5c2f-98bd-6dc0ccc4ba2f" -version = "0.17.1" - - [deps.HDF5.extensions] - MPIExt = "MPI" - - [deps.HDF5.weakdeps] - MPI = "da04e1cc-30fd-572f-bb4f-1f8673147195" - -[[deps.HDF5_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "LazyArtifacts", "LibCURL_jll", "Libdl", "MPICH_jll", "MPIPreferences", "MPItrampoline_jll", "MicrosoftMPI_jll", "OpenMPI_jll", "OpenSSL_jll", "TOML", "Zlib_jll", "libaec_jll"] -git-tree-sha1 = "38c8874692d48d5440d5752d6c74b0c6b0b60739" -uuid = "0234f1f7-429e-5d53-9886-15a909be8d59" -version = "1.14.2+1" - -[[deps.HTTP]] -deps = ["Base64", "Dates", "IniFile", "Logging", "MbedTLS", "NetworkOptions", "Sockets", "URIs"] -git-tree-sha1 = "0fa77022fe4b511826b39c894c90daf5fce3334a" -uuid = "cd3eb016-35fb-5094-929b-558a96fad6f3" -version = "0.9.17" - -[[deps.HarfBuzz_jll]] -deps = ["Artifacts", "Cairo_jll", "Fontconfig_jll", "FreeType2_jll", "Glib_jll", "Graphite2_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg"] -git-tree-sha1 = "129acf094d168394e80ee1dc4bc06ec835e510a3" -uuid = "2e76f6c2-a576-52d4-95c1-20adfe4de566" -version = "2.8.1+1" - -[[deps.HostCPUFeatures]] -deps = ["BitTwiddlingConvenienceFunctions", "IfElse", "Libdl", "Static"] -git-tree-sha1 = "eb8fed28f4994600e29beef49744639d985a04b2" -uuid = "3e5b6fbb-0976-4d2c-9146-d79de83f2fb0" -version = "0.1.16" - -[[deps.Hwloc_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "ca0f6bf568b4bfc807e7537f081c81e35ceca114" -uuid = "e33a78d0-f292-5ffc-b300-72abe9b543c8" -version = "2.10.0+0" - -[[deps.HypergeometricFunctions]] -deps = ["DualNumbers", "LinearAlgebra", "OpenLibm_jll", "SpecialFunctions"] -git-tree-sha1 = "f218fe3736ddf977e0e772bc9a586b2383da2685" -uuid = "34004b35-14d8-5ef3-9330-4cdb6864b03a" -version = "0.3.23" - -[[deps.IOCapture]] -deps = ["Logging", "Random"] -git-tree-sha1 = "8b72179abc660bfab5e28472e019392b97d0985c" -uuid = "b5f81e59-6552-4d32-b1f0-c071b021bf89" -version = "0.2.4" - -[[deps.IRTools]] -deps = ["InteractiveUtils", "MacroTools", "Test"] -git-tree-sha1 = "5d8c5713f38f7bc029e26627b687710ba406d0dd" -uuid = "7869d1d1-7146-5819-86e3-90919afe41df" -version = "0.4.12" - -[[deps.IfElse]] -git-tree-sha1 = "debdd00ffef04665ccbb3e150747a77560e8fad1" -uuid = "615f187c-cbe4-4ef1-ba3b-2fcf58d6d173" -version = "0.1.1" - -[[deps.IniFile]] -git-tree-sha1 = "f550e6e32074c939295eb5ea6de31849ac2c9625" -uuid = "83e8ac13-25f8-5344-8a64-a9f2b223428f" -version = "0.5.1" - -[[deps.InitialValues]] -git-tree-sha1 = "4da0f88e9a39111c2fa3add390ab15f3a44f3ca3" -uuid = "22cec73e-a1b8-11e9-2c92-598750a2cf9c" -version = "0.3.1" - -[[deps.InteractiveUtils]] -deps = ["Markdown"] -uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240" - -[[deps.InternedStrings]] -deps = ["Random", "Test"] -git-tree-sha1 = "eb05b5625bc5d821b8075a77e4c421933e20c76b" -uuid = "7d512f48-7fb1-5a58-b986-67e6dc259f01" -version = "0.7.0" - -[[deps.InvertedIndices]] -git-tree-sha1 = "0dc7b50b8d436461be01300fd8cd45aa0274b038" -uuid = "41ab1584-1d38-5bbf-9106-f11c6c58b48f" -version = "1.3.0" - -[[deps.IrrationalConstants]] -git-tree-sha1 = "630b497eafcc20001bba38a4651b327dcfc491d2" -uuid = "92d709cd-6900-40b7-9082-c6be49f344b6" -version = "0.2.2" - -[[deps.IterationControl]] -deps = ["EarlyStopping", "InteractiveUtils"] -git-tree-sha1 = "f61d5d4d0e433b3fab03ca5a1bfa2d7dcbb8094c" -uuid = "b3c1a2ee-3fec-4384-bf48-272ea71de57c" -version = "0.4.0" - -[[deps.IteratorInterfaceExtensions]] -git-tree-sha1 = "a3f24677c21f5bbe9d2a714f95dcd58337fb2856" -uuid = "82899510-4779-5014-852e-03e436cf321d" -version = "1.0.0" - -[[deps.JLD2]] -deps = ["FileIO", "MacroTools", "Mmap", "OrderedCollections", "Pkg", "PrecompileTools", "Printf", "Reexport", "Requires", "TranscodingStreams", "UUIDs"] -git-tree-sha1 = "5ea6acdd53a51d897672edb694e3cc2912f3f8a7" -uuid = "033835bb-8acc-5ee8-8aae-3f567f8a3819" -version = "0.4.46" - -[[deps.JLFzf]] -deps = ["Pipe", "REPL", "Random", "fzf_jll"] -git-tree-sha1 = "a53ebe394b71470c7f97c2e7e170d51df21b17af" -uuid = "1019f520-868f-41f5-a6de-eb00f4b6a39c" -version = "0.1.7" - -[[deps.JLLWrappers]] -deps = ["Artifacts", "Preferences"] -git-tree-sha1 = "7e5d6779a1e09a36db2a7b6cff50942a0a7d0fca" -uuid = "692b3bcd-3c85-4b1f-b108-f13ce0eb3210" -version = "1.5.0" - -[[deps.JLSO]] -deps = ["BSON", "CodecZlib", "FilePathsBase", "Memento", "Pkg", "Serialization"] -git-tree-sha1 = "7e3821e362ede76f83a39635d177c63595296776" -uuid = "9da8a3cd-07a3-59c0-a743-3fdc52c30d11" -version = "2.7.0" - -[[deps.JSON]] -deps = ["Dates", "Mmap", "Parsers", "Unicode"] -git-tree-sha1 = "31e996f0a15c7b280ba9f76636b3ff9e2ae58c9a" -uuid = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" -version = "0.21.4" - -[[deps.JSON3]] -deps = ["Dates", "Mmap", "Parsers", "PrecompileTools", "StructTypes", "UUIDs"] -git-tree-sha1 = "eb3edce0ed4fa32f75a0a11217433c31d56bd48b" -uuid = "0f8b85d8-7281-11e9-16c2-39a750bddbf1" -version = "1.14.0" - - [deps.JSON3.extensions] - JSON3ArrowExt = ["ArrowTypes"] - - [deps.JSON3.weakdeps] - ArrowTypes = "31f734f8-188a-4ce0-8406-c8a06bd891cd" - -[[deps.JpegTurbo_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "3336abae9a713d2210bb57ab484b1e065edd7d23" -uuid = "aacddb02-875f-59d6-b918-886e6ef4fbf8" -version = "3.0.2+0" - -[[deps.JuliaVariables]] -deps = ["MLStyle", "NameResolution"] -git-tree-sha1 = "49fb3cb53362ddadb4415e9b73926d6b40709e70" -uuid = "b14d175d-62b4-44ba-8fb7-3064adc8c3ec" -version = "0.2.4" - -[[deps.KernelAbstractions]] -deps = ["Adapt", "Atomix", "InteractiveUtils", "LinearAlgebra", "MacroTools", "PrecompileTools", "Requires", "SparseArrays", "StaticArrays", "UUIDs", "UnsafeAtomics", "UnsafeAtomicsLLVM"] -git-tree-sha1 = "ed7167240f40e62d97c1f5f7735dea6de3cc5c49" -uuid = "63c18a36-062a-441e-b654-da1e3ab1ce7c" -version = "0.9.18" - - [deps.KernelAbstractions.extensions] - EnzymeExt = "EnzymeCore" - - [deps.KernelAbstractions.weakdeps] - EnzymeCore = "f151be2c-9106-41f4-ab19-57ee4f262869" - -[[deps.LAME_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f6250b16881adf048549549fba48b1161acdac8c" -uuid = "c1c5ebd0-6772-5130-a774-d5fcae4a789d" -version = "3.100.1+0" - -[[deps.LERC_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "bf36f528eec6634efc60d7ec062008f171071434" -uuid = "88015f11-f218-50d7-93a8-a6af411a945d" -version = "3.0.0+1" - -[[deps.LLVM]] -deps = ["CEnum", "LLVMExtra_jll", "Libdl", "Printf", "Unicode"] -git-tree-sha1 = "f044a2796a9e18e0531b9b3072b0019a61f264bc" -uuid = "929cbde3-209d-540e-8aea-75f648917ca0" -version = "4.17.1" - -[[deps.LLVMExtra_jll]] -deps = ["Artifacts", "JLLWrappers", "LazyArtifacts", "Libdl", "TOML"] -git-tree-sha1 = "070e4b5b65827f82c16ae0916376cb47377aa1b5" -uuid = "dad2f222-ce93-54a1-a47d-0025e8a3acab" -version = "0.0.18+0" - -[[deps.LLVMOpenMP_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "d986ce2d884d49126836ea94ed5bfb0f12679713" -uuid = "1d63c593-3942-5779-bab2-d838dc0a180e" -version = "15.0.7+0" - -[[deps.LZO_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "e5b909bcf985c5e2605737d2ce278ed791b89be6" -uuid = "dd4b983a-f0e5-5f8d-a1b7-129d4a5fb1ac" -version = "2.10.1+0" - -[[deps.LaTeXStrings]] -git-tree-sha1 = "50901ebc375ed41dbf8058da26f9de442febbbec" -uuid = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" -version = "1.3.1" - -[[deps.Latexify]] -deps = ["Format", "InteractiveUtils", "LaTeXStrings", "MacroTools", "Markdown", "OrderedCollections", "Requires"] -git-tree-sha1 = "cad560042a7cc108f5a4c24ea1431a9221f22c1b" -uuid = "23fbe1c1-3f47-55db-b15f-69d7ec21a316" -version = "0.16.2" - - [deps.Latexify.extensions] - DataFramesExt = "DataFrames" - SymEngineExt = "SymEngine" - - [deps.Latexify.weakdeps] - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - SymEngine = "123dc426-2d89-5057-bbad-38513e3affd8" - -[[deps.LatinHypercubeSampling]] -deps = ["Random", "StableRNGs", "StatsBase", "Test"] -git-tree-sha1 = "825289d43c753c7f1bf9bed334c253e9913997f8" -uuid = "a5e1c1ea-c99a-51d3-a14d-a9a37257b02d" -version = "1.9.0" - -[[deps.LayoutPointers]] -deps = ["ArrayInterface", "LinearAlgebra", "ManualMemory", "SIMDTypes", "Static", "StaticArrayInterface"] -git-tree-sha1 = "62edfee3211981241b57ff1cedf4d74d79519277" -uuid = "10f19ff3-798f-405d-979b-55457f8fc047" -version = "0.1.15" - -[[deps.LazilyInitializedFields]] -git-tree-sha1 = "8f7f3cabab0fd1800699663533b6d5cb3fc0e612" -uuid = "0e77f7df-68c5-4e49-93ce-4cd80f5598bf" -version = "1.2.2" - -[[deps.LazyArtifacts]] -deps = ["Artifacts", "Pkg"] -uuid = "4af54fe1-eca0-43a8-85a7-787d91b784e3" - -[[deps.LearnBase]] -git-tree-sha1 = "a0d90569edd490b82fdc4dc078ea54a5a800d30a" -uuid = "7f8f8fb0-2700-5f03-b4bd-41f8cfc144b6" -version = "0.4.1" - -[[deps.LibCURL]] -deps = ["LibCURL_jll", "MozillaCACerts_jll"] -uuid = "b27032c2-a3e7-50c8-80cd-2d36dbcbfd21" -version = "0.6.4" - -[[deps.LibCURL_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll", "Zlib_jll", "nghttp2_jll"] -uuid = "deac9b47-8bc7-5906-a0fe-35ac56dc84c0" -version = "8.4.0+0" - -[[deps.LibGit2]] -deps = ["Base64", "LibGit2_jll", "NetworkOptions", "Printf", "SHA"] -uuid = "76f85450-5226-5b5a-8eaa-529ad045b433" - -[[deps.LibGit2_jll]] -deps = ["Artifacts", "LibSSH2_jll", "Libdl", "MbedTLS_jll"] -uuid = "e37daf67-58a4-590a-8e99-b0245dd2ffc5" -version = "1.6.4+0" - -[[deps.LibSSH2_jll]] -deps = ["Artifacts", "Libdl", "MbedTLS_jll"] -uuid = "29816b5a-b9ab-546f-933c-edad1886dfa8" -version = "1.11.0+1" - -[[deps.Libdl]] -uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb" - -[[deps.Libffi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "0b4a5d71f3e5200a7dff793393e09dfc2d874290" -uuid = "e9f186c6-92d2-5b65-8a66-fee21dc1b490" -version = "3.2.2+1" - -[[deps.Libgcrypt_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgpg_error_jll", "Pkg"] -git-tree-sha1 = "64613c82a59c120435c067c2b809fc61cf5166ae" -uuid = "d4300ac3-e22c-5743-9152-c294e39db1e4" -version = "1.8.7+0" - -[[deps.Libglvnd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll", "Xorg_libXext_jll"] -git-tree-sha1 = "6f73d1dd803986947b2c750138528a999a6c7733" -uuid = "7e76a0d4-f3c7-5321-8279-8d96eeed0f29" -version = "1.6.0+0" - -[[deps.Libgpg_error_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "c333716e46366857753e273ce6a69ee0945a6db9" -uuid = "7add5ba3-2f88-524e-9cd5-f83b8a55f7b8" -version = "1.42.0+0" - -[[deps.Libiconv_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "f9557a255370125b405568f9767d6d195822a175" -uuid = "94ce4f54-9a6c-5748-9c1c-f9c7231a4531" -version = "1.17.0+0" - -[[deps.Libmount_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "dae976433497a2f841baadea93d27e68f1a12a97" -uuid = "4b2f31a3-9ecc-558c-b454-b3730dcb73e9" -version = "2.39.3+0" - -[[deps.Libtiff_jll]] -deps = ["Artifacts", "JLLWrappers", "JpegTurbo_jll", "LERC_jll", "Libdl", "XZ_jll", "Zlib_jll", "Zstd_jll"] -git-tree-sha1 = "2da088d113af58221c52828a80378e16be7d037a" -uuid = "89763e89-9b03-5906-acba-b20f662cd828" -version = "4.5.1+1" - -[[deps.Libuuid_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "0a04a1318df1bf510beb2562cf90fb0c386f58c4" -uuid = "38a345b3-de98-5d2b-a5d3-14cd9215e700" -version = "2.39.3+1" - -[[deps.LinearAlgebra]] -deps = ["Libdl", "OpenBLAS_jll", "libblastrampoline_jll"] -uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" - -[[deps.Literate]] -deps = ["Base64", "IOCapture", "JSON", "REPL"] -git-tree-sha1 = "bad26f1ccd99c553886ec0725e99a509589dcd11" -uuid = "98b081ad-f1c9-55d3-8b20-4c87d4299306" -version = "2.16.1" - -[[deps.LogExpFunctions]] -deps = ["DocStringExtensions", "IrrationalConstants", "LinearAlgebra"] -git-tree-sha1 = "18144f3e9cbe9b15b070288eef858f71b291ce37" -uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" -version = "0.3.27" - - [deps.LogExpFunctions.extensions] - LogExpFunctionsChainRulesCoreExt = "ChainRulesCore" - LogExpFunctionsChangesOfVariablesExt = "ChangesOfVariables" - LogExpFunctionsInverseFunctionsExt = "InverseFunctions" - - [deps.LogExpFunctions.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - ChangesOfVariables = "9e997f8a-9a97-42d5-a9f1-ce6bfc15e2c0" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.Logging]] -uuid = "56ddb016-857b-54e1-b83d-db4d58db5568" - -[[deps.LoopVectorization]] -deps = ["ArrayInterface", "CPUSummary", "CloseOpenIntervals", "DocStringExtensions", "HostCPUFeatures", "IfElse", "LayoutPointers", "LinearAlgebra", "OffsetArrays", "PolyesterWeave", "PrecompileTools", "SIMDTypes", "SLEEFPirates", "Static", "StaticArrayInterface", "ThreadingUtilities", "UnPack", "VectorizationBase"] -git-tree-sha1 = "0f5648fbae0d015e3abe5867bca2b362f67a5894" -uuid = "bdcacae8-1622-11e9-2a5c-532679323890" -version = "0.12.166" -weakdeps = ["ChainRulesCore", "ForwardDiff", "SpecialFunctions"] - - [deps.LoopVectorization.extensions] - ForwardDiffExt = ["ChainRulesCore", "ForwardDiff"] - SpecialFunctionsExt = "SpecialFunctions" - -[[deps.LossFunctions]] -deps = ["InteractiveUtils", "LearnBase", "Markdown", "RecipesBase", "StatsBase"] -git-tree-sha1 = "0f057f6ea90a84e73a8ef6eebb4dc7b5c330020f" -uuid = "30fc2ffe-d236-52d8-8643-a9d8f7c094a7" -version = "0.7.2" - -[[deps.MAT]] -deps = ["BufferedStreams", "CodecZlib", "HDF5", "SparseArrays"] -git-tree-sha1 = "ed1cf0a322d78cee07718bed5fd945e2218c35a1" -uuid = "23992714-dd62-5051-b70f-ba57cb901cac" -version = "0.10.6" - -[[deps.MLDatasets]] -deps = ["BinDeps", "ColorTypes", "DataDeps", "DelimitedFiles", "FixedPointNumbers", "GZip", "JSON3", "MAT", "Pickle", "Requires", "SparseArrays"] -git-tree-sha1 = "f1ff456828cfceb8fd64f2f212dea67b1414be96" -uuid = "eb30cadb-4394-5ae3-aed4-317e484a6458" -version = "0.5.15" - -[[deps.MLJ]] -deps = ["CategoricalArrays", "ComputationalResources", "Distributed", "Distributions", "LinearAlgebra", "MLJBase", "MLJEnsembles", "MLJIteration", "MLJModels", "MLJOpenML", "MLJSerialization", "MLJTuning", "Pkg", "ProgressMeter", "Random", "ScientificTypes", "Statistics", "StatsBase", "Tables"] -git-tree-sha1 = "7cbd651e39fd3f3aa37e8a4d8beaccfa8d13b1cd" -uuid = "add582a8-e3ab-11e8-2d5e-e98b27df1bc7" -version = "0.16.7" - -[[deps.MLJBase]] -deps = ["CategoricalArrays", "ComputationalResources", "Dates", "DelimitedFiles", "Distributed", "Distributions", "InteractiveUtils", "InvertedIndices", "LinearAlgebra", "LossFunctions", "MLJModelInterface", "Missings", "OrderedCollections", "Parameters", "PrettyTables", "ProgressMeter", "Random", "ScientificTypes", "StatisticalTraits", "Statistics", "StatsBase", "Tables"] -git-tree-sha1 = "4a0b5d1212786f5de364f481ef2a84458d3dfe4b" -uuid = "a7f614a8-145f-11e9-1d2a-a57a1082229d" -version = "0.18.26" - -[[deps.MLJEnsembles]] -deps = ["CategoricalArrays", "ComputationalResources", "Distributed", "Distributions", "MLJBase", "MLJModelInterface", "ProgressMeter", "Random", "ScientificTypes", "StatsBase"] -git-tree-sha1 = "f8ca949d52432b81f621d9da641cf59829ad2c8c" -uuid = "50ed68f4-41fd-4504-931a-ed422449fee0" -version = "0.1.2" - -[[deps.MLJIteration]] -deps = ["IterationControl", "MLJBase", "Random"] -git-tree-sha1 = "1c94830f8927b10a5653d6e1868c20faccf57be5" -uuid = "614be32b-d00c-4edb-bd02-1eb411ab5e55" -version = "0.3.3" - -[[deps.MLJModelInterface]] -deps = ["Random", "ScientificTypesBase", "StatisticalTraits"] -git-tree-sha1 = "0174e9d180b0cae1f8fe7976350ad52f0e70e0d8" -uuid = "e80e1ace-859a-464e-9ed9-23947d8ae3ea" -version = "1.3.3" - -[[deps.MLJModels]] -deps = ["CategoricalArrays", "Dates", "Distances", "Distributions", "InteractiveUtils", "LinearAlgebra", "MLJBase", "MLJModelInterface", "OrderedCollections", "Parameters", "Pkg", "REPL", "Random", "Requires", "ScientificTypes", "Statistics", "StatsBase", "Tables"] -git-tree-sha1 = "271c431ef783079db3371ffe770140bb83cf2f16" -uuid = "d491faf4-2d78-11e9-2867-c94bc002c0b7" -version = "0.14.14" - -[[deps.MLJOpenML]] -deps = ["CSV", "HTTP", "JSON", "Markdown", "ScientificTypes"] -git-tree-sha1 = "a0d6e25ec042ab84505733a62a2b2894fbcf260c" -uuid = "cbea4545-8c96-4583-ad3a-44078d60d369" -version = "1.1.0" - -[[deps.MLJSerialization]] -deps = ["IterationControl", "JLSO", "MLJBase", "MLJModelInterface"] -git-tree-sha1 = "cc5877ad02ef02e273d2622f0d259d628fa61cd0" -uuid = "17bed46d-0ab5-4cd4-b792-a5c4b8547c6d" -version = "1.1.3" - -[[deps.MLJTuning]] -deps = ["ComputationalResources", "Distributed", "Distributions", "LatinHypercubeSampling", "MLJBase", "ProgressMeter", "Random", "RecipesBase"] -git-tree-sha1 = "a443cc088158b949876d7038a1aa37cfc8c5509b" -uuid = "03970b2e-30c4-11ea-3135-d1576263f10f" -version = "0.6.16" - -[[deps.MLStyle]] -git-tree-sha1 = "bc38dff0548128765760c79eb7388a4b37fae2c8" -uuid = "d8e11817-5142-5d16-987a-aa16d5891078" -version = "0.4.17" - -[[deps.MLUtils]] -deps = ["ChainRulesCore", "Compat", "DataAPI", "DelimitedFiles", "FLoops", "NNlib", "Random", "ShowCases", "SimpleTraits", "Statistics", "StatsBase", "Tables", "Transducers"] -git-tree-sha1 = "b45738c2e3d0d402dffa32b2c1654759a2ac35a4" -uuid = "f1d291b0-491e-4a28-83b9-f70985020b54" -version = "0.4.4" - -[[deps.MPICH_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "656036b9ed6f942d35e536e249600bc31d0f9df8" -uuid = "7cb0a576-ebde-5e09-9194-50597f1243b4" -version = "4.2.0+0" - -[[deps.MPIPreferences]] -deps = ["Libdl", "Preferences"] -git-tree-sha1 = "8f6af051b9e8ec597fa09d8885ed79fd582f33c9" -uuid = "3da0fdf6-3ccc-4f1b-acd9-58baa6c99267" -version = "0.1.10" - -[[deps.MPItrampoline_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "TOML"] -git-tree-sha1 = "77c3bd69fdb024d75af38713e883d0f249ce19c2" -uuid = "f1f71cc9-e9ae-5b93-9b94-4fe0e1ad3748" -version = "5.3.2+0" - -[[deps.MacroTools]] -deps = ["Markdown", "Random"] -git-tree-sha1 = "2fa9ee3e63fd3a4f7a9a4f4744a52f4856de82df" -uuid = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09" -version = "0.5.13" - -[[deps.ManualMemory]] -git-tree-sha1 = "bcaef4fc7a0cfe2cba636d84cda54b5e4e4ca3cd" -uuid = "d125e4d3-2237-4719-b19c-fa641b8a4667" -version = "0.1.8" - -[[deps.Markdown]] -deps = ["Base64"] -uuid = "d6f4376e-aef5-505a-96c1-9c027394607a" - -[[deps.MarkdownAST]] -deps = ["AbstractTrees", "Markdown"] -git-tree-sha1 = "465a70f0fc7d443a00dcdc3267a497397b8a3899" -uuid = "d0879d2d-cac2-40c8-9cee-1863dc0c7391" -version = "0.1.2" - -[[deps.MbedTLS]] -deps = ["Dates", "MbedTLS_jll", "MozillaCACerts_jll", "NetworkOptions", "Random", "Sockets"] -git-tree-sha1 = "c067a280ddc25f196b5e7df3877c6b226d390aaf" -uuid = "739be429-bea8-5141-9913-cc70e7f3736d" -version = "1.1.9" - -[[deps.MbedTLS_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "c8ffd9c3-330d-5841-b78e-0817d7145fa1" -version = "2.28.2+1" - -[[deps.Measures]] -git-tree-sha1 = "c13304c81eec1ed3af7fc20e75fb6b26092a1102" -uuid = "442fdcdd-2543-5da2-b0f3-8c86c306513e" -version = "0.3.2" - -[[deps.Memento]] -deps = ["Dates", "Distributed", "Requires", "Serialization", "Sockets", "Test", "UUIDs"] -git-tree-sha1 = "bb2e8f4d9f400f6e90d57b34860f6abdc51398e5" -uuid = "f28f55f0-a522-5efc-85c2-fe41dfb9b2d9" -version = "1.4.1" - -[[deps.MicroCollections]] -deps = ["BangBang", "InitialValues", "Setfield"] -git-tree-sha1 = "629afd7d10dbc6935ec59b32daeb33bc4460a42e" -uuid = "128add7d-3638-4c79-886c-908ea0c25c34" -version = "0.1.4" - -[[deps.MicrosoftMPI_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "f12a29c4400ba812841c6ace3f4efbb6dbb3ba01" -uuid = "9237b28f-5490-5468-be7b-bb81f5f5e6cf" -version = "10.1.4+2" - -[[deps.Missings]] -deps = ["DataAPI"] -git-tree-sha1 = "f66bdc5de519e8f8ae43bdc598782d35a25b1272" -uuid = "e1d29d7a-bbdc-5cf2-9ac0-f12de2c33e28" -version = "1.1.0" - -[[deps.Mmap]] -uuid = "a63ad114-7e13-5084-954f-fe012c677804" - -[[deps.Mocking]] -deps = ["Compat", "ExprTools"] -git-tree-sha1 = "4cc0c5a83933648b615c36c2b956d94fda70641e" -uuid = "78c3b35d-d492-501b-9361-3d52fe80e533" -version = "0.7.7" - -[[deps.MozillaCACerts_jll]] -uuid = "14a3606d-f60d-562e-9121-12d972cd8159" -version = "2023.1.10" - -[[deps.NNlib]] -deps = ["Adapt", "Atomix", "ChainRulesCore", "GPUArraysCore", "KernelAbstractions", "LinearAlgebra", "Pkg", "Random", "Requires", "Statistics"] -git-tree-sha1 = "72240e3f5ca031937bd536182cb2c031da5f46dd" -uuid = "872c559c-99b0-510c-b3b7-b6c96a88d5cd" -version = "0.8.21" - - [deps.NNlib.extensions] - NNlibAMDGPUExt = "AMDGPU" - - [deps.NNlib.weakdeps] - AMDGPU = "21141c5a-9bdb-4563-92ae-f87d6854732e" - -[[deps.NNlibCUDA]] -deps = ["Adapt", "CUDA", "LinearAlgebra", "NNlib", "Random", "Statistics"] -git-tree-sha1 = "b05a082b08a3af0e5c576883bc6dfb6513e7e478" -uuid = "a00861dc-f156-4864-bf3c-e6376f28a68d" -version = "0.2.6" - -[[deps.NaNMath]] -deps = ["OpenLibm_jll"] -git-tree-sha1 = "0877504529a3e5c3343c6f8b4c0381e57e4387e4" -uuid = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" -version = "1.0.2" - -[[deps.NameResolution]] -deps = ["PrettyPrint"] -git-tree-sha1 = "1a0fa0e9613f46c9b8c11eee38ebb4f590013c5e" -uuid = "71a1bf82-56d0-4bbc-8a3c-48b961074391" -version = "0.1.5" - -[[deps.NearestNeighbors]] -deps = ["Distances", "StaticArrays"] -git-tree-sha1 = "ded64ff6d4fdd1cb68dfcbb818c69e144a5b2e4c" -uuid = "b8a86587-4115-5ab1-83bc-aa920d37bbce" -version = "0.4.16" - -[[deps.NetworkOptions]] -uuid = "ca575930-c2e3-43a9-ace4-1e988b2c1908" -version = "1.2.0" - -[[deps.OffsetArrays]] -git-tree-sha1 = "6a731f2b5c03157418a20c12195eb4b74c8f8621" -uuid = "6fe1bfb0-de20-5000-8ca7-80f57d26f881" -version = "1.13.0" -weakdeps = ["Adapt"] - - [deps.OffsetArrays.extensions] - OffsetArraysAdaptExt = "Adapt" - -[[deps.Ogg_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "887579a3eb005446d514ab7aeac5d1d027658b8f" -uuid = "e7412a2a-1a6e-54c0-be00-318e2571c051" -version = "1.3.5+1" - -[[deps.OneHotArrays]] -deps = ["Adapt", "ChainRulesCore", "Compat", "GPUArraysCore", "LinearAlgebra", "NNlib"] -git-tree-sha1 = "963a3f28a2e65bb87a68033ea4a616002406037d" -uuid = "0b1bfda6-eb8a-41d2-88d8-f5af5cad476f" -version = "0.2.5" - -[[deps.OpenBLAS_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Libdl"] -uuid = "4536629a-c528-5b80-bd46-f80d51c5b363" -version = "0.3.23+2" - -[[deps.OpenLibm_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+2" - -[[deps.OpenMPI_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Hwloc_jll", "JLLWrappers", "LazyArtifacts", "Libdl", "MPIPreferences", "PMIx_jll", "TOML", "Zlib_jll", "libevent_jll", "prrte_jll"] -git-tree-sha1 = "f46caf663e069027a06942d00dced37f1eb3d8ad" -uuid = "fe0851c0-eecd-5654-98d4-656369965a5c" -version = "5.0.2+0" - -[[deps.OpenSSL_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "60e3045590bd104a16fefb12836c00c0ef8c7f8c" -uuid = "458c3c95-2e84-50aa-8efc-19380b2a3a95" -version = "3.0.13+0" - -[[deps.OpenSpecFun_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "13652491f6856acfd2db29360e1bbcd4565d04f1" -uuid = "efe28fd5-8261-553b-a9e1-b2916fc3738e" -version = "0.5.5+0" - -[[deps.Optimisers]] -deps = ["ChainRulesCore", "Functors", "LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "c1fc26bab5df929a5172f296f25d7d08688fd25b" -uuid = "3bd65402-5787-11e9-1adc-39752487f4e2" -version = "0.2.20" - -[[deps.Opus_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "51a08fb14ec28da2ec7a927c4337e4332c2a4720" -uuid = "91d4177d-7536-5919-b921-800302f37372" -version = "1.3.2+0" - -[[deps.OrderedCollections]] -git-tree-sha1 = "dfdf5519f235516220579f949664f1bf44e741c5" -uuid = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" -version = "1.6.3" - -[[deps.PCRE2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "efcefdf7-47ab-520b-bdef-62a2eaa19f15" -version = "10.42.0+1" - -[[deps.PDMats]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "949347156c25054de2db3b166c52ac4728cbad65" -uuid = "90014a1f-27ba-587c-ab20-58faa44d9150" -version = "0.11.31" - -[[deps.PMIx_jll]] -deps = ["Artifacts", "Hwloc_jll", "JLLWrappers", "Libdl", "Zlib_jll", "libevent_jll"] -git-tree-sha1 = "8b3b19351fa24791f94d7ae85faf845ca1362541" -uuid = "32165bc3-0280-59bc-8c0b-c33b6203efab" -version = "4.2.7+0" - -[[deps.Parameters]] -deps = ["OrderedCollections", "UnPack"] -git-tree-sha1 = "34c0e9ad262e5f7fc75b10a9952ca7692cfc5fbe" -uuid = "d96e819e-fc66-5662-9728-84c9c7592b0a" -version = "0.12.3" - -[[deps.Parsers]] -deps = ["Dates"] -git-tree-sha1 = "bfd7d8c7fd87f04543810d9cbd3995972236ba1b" -uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "1.1.2" - -[[deps.Pickle]] -deps = ["DataStructures", "InternedStrings", "Serialization", "SparseArrays", "Strided", "StringEncodings", "ZipFile"] -git-tree-sha1 = "e6a34eb1dc0c498f0774bbfbbbeff2de101f4235" -uuid = "fbb45041-c46e-462f-888f-7c521cafbc2c" -version = "0.3.2" - -[[deps.Pipe]] -git-tree-sha1 = "6842804e7867b115ca9de748a0cf6b364523c16d" -uuid = "b98c9c47-44ae-5843-9183-064241ee97a0" -version = "1.3.0" - -[[deps.Pixman_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] -git-tree-sha1 = "64779bc4c9784fee475689a1752ef4d5747c5e87" -uuid = "30392449-352a-5448-841d-b1acce4e97dc" -version = "0.42.2+0" - -[[deps.Pkg]] -deps = ["Artifacts", "Dates", "Downloads", "FileWatching", "LibGit2", "Libdl", "Logging", "Markdown", "Printf", "REPL", "Random", "SHA", "Serialization", "TOML", "Tar", "UUIDs", "p7zip_jll"] -uuid = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" -version = "1.10.0" - -[[deps.PlotThemes]] -deps = ["PlotUtils", "Statistics"] -git-tree-sha1 = "1f03a2d339f42dca4a4da149c7e15e9b896ad899" -uuid = "ccf2f8ad-2431-5c83-bf29-c5338b663b6a" -version = "3.1.0" - -[[deps.PlotUtils]] -deps = ["ColorSchemes", "Colors", "Dates", "PrecompileTools", "Printf", "Random", "Reexport", "Statistics"] -git-tree-sha1 = "7b1a9df27f072ac4c9c7cbe5efb198489258d1f5" -uuid = "995b91a9-d308-5afd-9ec6-746e21dbc043" -version = "1.4.1" - -[[deps.Plots]] -deps = ["Base64", "Contour", "Dates", "Downloads", "FFMPEG", "FixedPointNumbers", "GR", "JLFzf", "JSON", "LaTeXStrings", "Latexify", "LinearAlgebra", "Measures", "NaNMath", "Pkg", "PlotThemes", "PlotUtils", "PrecompileTools", "Printf", "REPL", "Random", "RecipesBase", "RecipesPipeline", "Reexport", "RelocatableFolders", "Requires", "Scratch", "Showoff", "SparseArrays", "Statistics", "StatsBase", "UUIDs", "UnicodeFun", "UnitfulLatexify", "Unzip"] -git-tree-sha1 = "3c403c6590dd93b36752634115e20137e79ab4df" -uuid = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" -version = "1.40.2" - - [deps.Plots.extensions] - FileIOExt = "FileIO" - GeometryBasicsExt = "GeometryBasics" - IJuliaExt = "IJulia" - ImageInTerminalExt = "ImageInTerminal" - UnitfulExt = "Unitful" - - [deps.Plots.weakdeps] - FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" - GeometryBasics = "5c1252a2-5f33-56bf-86c9-59e7332b4326" - IJulia = "7073ff75-c697-5162-941a-fcdaad2a7d2a" - ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" - Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" - -[[deps.PolyesterWeave]] -deps = ["BitTwiddlingConvenienceFunctions", "CPUSummary", "IfElse", "Static", "ThreadingUtilities"] -git-tree-sha1 = "240d7170f5ffdb285f9427b92333c3463bf65bf6" -uuid = "1d0040c9-8b98-4ee7-8388-3f51789ca0ad" -version = "0.2.1" - -[[deps.PooledArrays]] -deps = ["DataAPI", "Future"] -git-tree-sha1 = "36d8b4b899628fb92c2749eb488d884a926614d3" -uuid = "2dfb63ee-cc39-5dd5-95bd-886bf059d720" -version = "1.4.3" - -[[deps.PrecompileTools]] -deps = ["Preferences"] -git-tree-sha1 = "5aa36f7049a63a1528fe8f7c3f2113413ffd4e1f" -uuid = "aea7be01-6a6a-4083-8856-8a6e6704d82a" -version = "1.2.1" - -[[deps.Preferences]] -deps = ["TOML"] -git-tree-sha1 = "9306f6085165d270f7e3db02af26a400d580f5c6" -uuid = "21216c6a-2e73-6563-6e65-726566657250" -version = "1.4.3" - -[[deps.PrettyPrint]] -git-tree-sha1 = "632eb4abab3449ab30c5e1afaa874f0b98b586e4" -uuid = "8162dcfd-2161-5ef2-ae6c-7681170c5f98" -version = "0.2.0" - -[[deps.PrettyTables]] -deps = ["Crayons", "Formatting", "Markdown", "Reexport", "Tables"] -git-tree-sha1 = "dfb54c4e414caa595a1f2ed759b160f5a3ddcba5" -uuid = "08abe8d2-0d0c-5749-adfa-8a2ac140af0d" -version = "1.3.1" - -[[deps.Printf]] -deps = ["Unicode"] -uuid = "de0858da-6303-5e67-8744-51eddeeeb8d7" - -[[deps.Profile]] -deps = ["Printf"] -uuid = "9abbd945-dff8-562f-b5e8-e1ebf5ef1b79" - -[[deps.ProgressLogging]] -deps = ["Logging", "SHA", "UUIDs"] -git-tree-sha1 = "80d919dee55b9c50e8d9e2da5eeafff3fe58b539" -uuid = "33c8b6b6-d38a-422a-b730-caa89a2f386c" -version = "0.1.4" - -[[deps.ProgressMeter]] -deps = ["Distributed", "Printf"] -git-tree-sha1 = "763a8ceb07833dd51bb9e3bbca372de32c0605ad" -uuid = "92933f4c-e287-5a05-a399-4b506db050ca" -version = "1.10.0" - -[[deps.PyCall]] -deps = ["Conda", "Dates", "Libdl", "LinearAlgebra", "MacroTools", "Serialization", "VersionParsing"] -git-tree-sha1 = "9816a3826b0ebf49ab4926e2b18842ad8b5c8f04" -uuid = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" -version = "1.96.4" - -[[deps.Qt6Base_jll]] -deps = ["Artifacts", "CompilerSupportLibraries_jll", "Fontconfig_jll", "Glib_jll", "JLLWrappers", "Libdl", "Libglvnd_jll", "OpenSSL_jll", "Vulkan_Loader_jll", "Xorg_libSM_jll", "Xorg_libXext_jll", "Xorg_libXrender_jll", "Xorg_libxcb_jll", "Xorg_xcb_util_cursor_jll", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_keysyms_jll", "Xorg_xcb_util_renderutil_jll", "Xorg_xcb_util_wm_jll", "Zlib_jll", "libinput_jll", "xkbcommon_jll"] -git-tree-sha1 = "37b7bb7aabf9a085e0044307e1717436117f2b3b" -uuid = "c0090381-4147-56d7-9ebc-da0b1113ec56" -version = "6.5.3+1" - -[[deps.QuadGK]] -deps = ["DataStructures", "LinearAlgebra"] -git-tree-sha1 = "9b23c31e76e333e6fb4c1595ae6afa74966a729e" -uuid = "1fd47b50-473d-5c70-9696-f719f8f3bcdc" -version = "2.9.4" - -[[deps.RCall]] -deps = ["CategoricalArrays", "Conda", "DataFrames", "DataStructures", "Dates", "Libdl", "Missings", "Preferences", "REPL", "Random", "Requires", "StatsModels", "WinReg"] -git-tree-sha1 = "846b2aab2d312fda5e7b099fc217c661e8fae27e" -uuid = "6f49c342-dc21-5d91-9882-a32aef131414" -version = "0.14.1" - -[[deps.RData]] -deps = ["CategoricalArrays", "CodecZlib", "DataFrames", "Dates", "FileIO", "Requires", "TimeZones", "Unicode"] -git-tree-sha1 = "19e47a495dfb7240eb44dc6971d660f7e4244a72" -uuid = "df47a6cb-8c03-5eed-afd8-b6050d6c41da" -version = "0.8.3" - -[[deps.RDatasets]] -deps = ["CSV", "CodecZlib", "DataFrames", "FileIO", "Printf", "RData", "Reexport"] -git-tree-sha1 = "2720e6f6afb3e562ccb70a6b62f8f308ff810333" -uuid = "ce6b1742-4840-55fa-b093-852dadbb1d8b" -version = "0.7.7" - -[[deps.REPL]] -deps = ["InteractiveUtils", "Markdown", "Sockets", "Unicode"] -uuid = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" - -[[deps.Random]] -deps = ["SHA"] -uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" - -[[deps.Random123]] -deps = ["Random", "RandomNumbers"] -git-tree-sha1 = "4743b43e5a9c4a2ede372de7061eed81795b12e7" -uuid = "74087812-796a-5b5d-8853-05524746bad3" -version = "1.7.0" - -[[deps.RandomNumbers]] -deps = ["Random", "Requires"] -git-tree-sha1 = "043da614cc7e95c703498a491e2c21f58a2b8111" -uuid = "e6cf234a-135c-5ec9-84dd-332b85af5143" -version = "1.5.3" - -[[deps.RealDot]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "9f0a1b71baaf7650f4fa8a1d168c7fb6ee41f0c9" -uuid = "c1ae055f-0cd5-4b69-90a6-9a35b1a98df9" -version = "0.1.0" - -[[deps.RecipesBase]] -deps = ["PrecompileTools"] -git-tree-sha1 = "5c3d09cc4f31f5fc6af001c250bf1278733100ff" -uuid = "3cdcf5f2-1ef4-517c-9805-6587b60abb01" -version = "1.3.4" - -[[deps.RecipesPipeline]] -deps = ["Dates", "NaNMath", "PlotUtils", "PrecompileTools", "RecipesBase"] -git-tree-sha1 = "45cf9fd0ca5839d06ef333c8201714e888486342" -uuid = "01d81517-befc-4cb6-b9ec-a95719d0359c" -version = "0.6.12" - -[[deps.Reexport]] -git-tree-sha1 = "45e428421666073eab6f2da5c9d310d99bb12f9b" -uuid = "189a3867-3050-52da-a836-e630ba90ab69" -version = "1.2.2" - -[[deps.RegistryInstances]] -deps = ["LazilyInitializedFields", "Pkg", "TOML", "Tar"] -git-tree-sha1 = "ffd19052caf598b8653b99404058fce14828be51" -uuid = "2792f1a3-b283-48e8-9a74-f99dce5104f3" -version = "0.1.0" - -[[deps.RelocatableFolders]] -deps = ["SHA", "Scratch"] -git-tree-sha1 = "ffdaf70d81cf6ff22c2b6e733c900c3321cab864" -uuid = "05181044-ff0b-4ac5-8273-598c1e38db00" -version = "1.0.1" - -[[deps.Requires]] -deps = ["UUIDs"] -git-tree-sha1 = "838a3a4188e2ded87a4f9f184b4b0d78a1e91cb7" -uuid = "ae029012-a4dd-5104-9daa-d747884805df" -version = "1.3.0" - -[[deps.Rmath]] -deps = ["Random", "Rmath_jll"] -git-tree-sha1 = "f65dcb5fa46aee0cf9ed6274ccbd597adc49aa7b" -uuid = "79098fc4-a85e-5d69-aa6a-4863f24498fa" -version = "0.7.1" - -[[deps.Rmath_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "6ed52fdd3382cf21947b15e8870ac0ddbff736da" -uuid = "f50d1b31-88e8-58de-be2c-1cc44531875f" -version = "0.4.0+0" - -[[deps.SHA]] -uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce" -version = "0.7.0" - -[[deps.SIMDTypes]] -git-tree-sha1 = "330289636fb8107c5f32088d2741e9fd7a061a5c" -uuid = "94e857df-77ce-4151-89e5-788b33177be4" -version = "0.1.0" - -[[deps.SLEEFPirates]] -deps = ["IfElse", "Static", "VectorizationBase"] -git-tree-sha1 = "3aac6d68c5e57449f5b9b865c9ba50ac2970c4cf" -uuid = "476501e8-09a2-5ece-8869-fb82de89a1fa" -version = "0.6.42" - -[[deps.ScientificTypes]] -deps = ["CategoricalArrays", "ColorTypes", "Dates", "Distributions", "PrettyTables", "Reexport", "ScientificTypesBase", "StatisticalTraits", "Tables"] -git-tree-sha1 = "7a3efcacd212801a8cf2f961e8238ffb2109b30d" -uuid = "321657f4-b219-11e9-178b-2701a2544e81" -version = "2.3.3" - -[[deps.ScientificTypesBase]] -git-tree-sha1 = "185e373beaf6b381c1e7151ce2c2a722351d6637" -uuid = "30f210dd-8aff-4c5f-94ba-8e64358c1161" -version = "2.3.0" - -[[deps.ScikitLearnBase]] -deps = ["LinearAlgebra", "Random", "Statistics"] -git-tree-sha1 = "7877e55c1523a4b336b433da39c8e8c08d2f221f" -uuid = "6e75b9c4-186b-50bd-896f-2d2496a4843e" -version = "0.5.0" - -[[deps.Scratch]] -deps = ["Dates"] -git-tree-sha1 = "3bac05bc7e74a75fd9cba4295cde4045d9fe2386" -uuid = "6c6a2e73-6563-6170-7368-637461726353" -version = "1.2.1" - -[[deps.SentinelArrays]] -deps = ["Dates", "Random"] -git-tree-sha1 = "0e7508ff27ba32f26cd459474ca2ede1bc10991f" -uuid = "91c51154-3ec4-41a3-a24f-3f23e20d615c" -version = "1.4.1" - -[[deps.Serialization]] -uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b" - -[[deps.Setfield]] -deps = ["ConstructionBase", "Future", "MacroTools", "StaticArraysCore"] -git-tree-sha1 = "e2cc6d8c88613c05e1defb55170bf5ff211fbeac" -uuid = "efcf1570-3423-57d1-acb7-fd33fddbac46" -version = "1.1.1" - -[[deps.ShiftedArrays]] -git-tree-sha1 = "503688b59397b3307443af35cd953a13e8005c16" -uuid = "1277b4bf-5013-50f5-be3d-901d8477a67a" -version = "2.0.0" - -[[deps.ShowCases]] -git-tree-sha1 = "7f534ad62ab2bd48591bdeac81994ea8c445e4a5" -uuid = "605ecd9f-84a6-4c9e-81e2-4798472b76a3" -version = "0.1.0" - -[[deps.Showoff]] -deps = ["Dates", "Grisu"] -git-tree-sha1 = "91eddf657aca81df9ae6ceb20b959ae5653ad1de" -uuid = "992d4aef-0814-514b-bc4d-f2e9a6c4116f" -version = "1.0.3" - -[[deps.SimpleTraits]] -deps = ["InteractiveUtils", "MacroTools"] -git-tree-sha1 = "5d7e3f4e11935503d3ecaf7186eac40602e7d231" -uuid = "699a6c99-e7fa-54fc-8d76-47d257e15c1d" -version = "0.9.4" - -[[deps.Sockets]] -uuid = "6462fe0b-24de-5631-8697-dd941f90decc" - -[[deps.SortingAlgorithms]] -deps = ["DataStructures"] -git-tree-sha1 = "66e0a8e672a0bdfca2c3f5937efb8538b9ddc085" -uuid = "a2af1166-a08f-5f64-846c-94a0d3cef48c" -version = "1.2.1" - -[[deps.SparseArrays]] -deps = ["Libdl", "LinearAlgebra", "Random", "Serialization", "SuiteSparse_jll"] -uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" -version = "1.10.0" - -[[deps.SparseInverseSubset]] -deps = ["LinearAlgebra", "SparseArrays", "SuiteSparse"] -git-tree-sha1 = "52962839426b75b3021296f7df242e40ecfc0852" -uuid = "dc90abb0-5640-4711-901d-7e5b23a2fada" -version = "0.1.2" - -[[deps.SpecialFunctions]] -deps = ["IrrationalConstants", "LogExpFunctions", "OpenLibm_jll", "OpenSpecFun_jll"] -git-tree-sha1 = "e2cfc4012a19088254b3950b85c3c1d8882d864d" -uuid = "276daf66-3868-5448-9aa4-cd146d93841b" -version = "2.3.1" -weakdeps = ["ChainRulesCore"] - - [deps.SpecialFunctions.extensions] - SpecialFunctionsChainRulesCoreExt = "ChainRulesCore" - -[[deps.SplittablesBase]] -deps = ["Setfield", "Test"] -git-tree-sha1 = "e08a62abc517eb79667d0a29dc08a3b589516bb5" -uuid = "171d559e-b47b-412a-8079-5efa626c420e" -version = "0.1.15" - -[[deps.StableRNGs]] -deps = ["Random", "Test"] -git-tree-sha1 = "ddc1a7b85e760b5285b50b882fa91e40c603be47" -uuid = "860ef19b-820b-49d6-a774-d7a799459cd3" -version = "1.0.1" - -[[deps.Static]] -deps = ["IfElse"] -git-tree-sha1 = "d2fdac9ff3906e27f7a618d47b676941baa6c80c" -uuid = "aedffcd0-7271-4cad-89d0-dc628f76c6d3" -version = "0.8.10" - -[[deps.StaticArrayInterface]] -deps = ["ArrayInterface", "Compat", "IfElse", "LinearAlgebra", "PrecompileTools", "Requires", "SparseArrays", "Static", "SuiteSparse"] -git-tree-sha1 = "5d66818a39bb04bf328e92bc933ec5b4ee88e436" -uuid = "0d7ed370-da01-4f52-bd93-41d350b8b718" -version = "1.5.0" -weakdeps = ["OffsetArrays", "StaticArrays"] - - [deps.StaticArrayInterface.extensions] - StaticArrayInterfaceOffsetArraysExt = "OffsetArrays" - StaticArrayInterfaceStaticArraysExt = "StaticArrays" - -[[deps.StaticArrays]] -deps = ["LinearAlgebra", "PrecompileTools", "Random", "StaticArraysCore"] -git-tree-sha1 = "bf074c045d3d5ffd956fa0a461da38a44685d6b2" -uuid = "90137ffa-7385-5640-81b9-e52037218182" -version = "1.9.3" -weakdeps = ["ChainRulesCore", "Statistics"] - - [deps.StaticArrays.extensions] - StaticArraysChainRulesCoreExt = "ChainRulesCore" - StaticArraysStatisticsExt = "Statistics" - -[[deps.StaticArraysCore]] -git-tree-sha1 = "36b3d696ce6366023a0ea192b4cd442268995a0d" -uuid = "1e83bf80-4336-4d27-bf5d-d5a4f845583c" -version = "1.4.2" - -[[deps.StatisticalTraits]] -deps = ["ScientificTypesBase"] -git-tree-sha1 = "730732cae4d3135e2f2182bd47f8d8b795ea4439" -uuid = "64bff920-2084-43da-a3e6-9bb72801c0c9" -version = "2.1.0" - -[[deps.Statistics]] -deps = ["LinearAlgebra", "SparseArrays"] -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" -version = "1.10.0" - -[[deps.StatsAPI]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1ff449ad350c9c4cbc756624d6f8a8c3ef56d3ed" -uuid = "82ae8749-77ed-4fe6-ae5f-f523153014b0" -version = "1.7.0" - -[[deps.StatsBase]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "LogExpFunctions", "Missings", "Printf", "Random", "SortingAlgorithms", "SparseArrays", "Statistics", "StatsAPI"] -git-tree-sha1 = "d1bf48bfcc554a3761a133fe3a9bb01488e06916" -uuid = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" -version = "0.33.21" - -[[deps.StatsFuns]] -deps = ["HypergeometricFunctions", "IrrationalConstants", "LogExpFunctions", "Reexport", "Rmath", "SpecialFunctions"] -git-tree-sha1 = "cef0472124fab0695b58ca35a77c6fb942fdab8a" -uuid = "4c63d2b9-4356-54db-8cca-17b64c39e42c" -version = "1.3.1" - - [deps.StatsFuns.extensions] - StatsFunsChainRulesCoreExt = "ChainRulesCore" - StatsFunsInverseFunctionsExt = "InverseFunctions" - - [deps.StatsFuns.weakdeps] - ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.StatsModels]] -deps = ["DataAPI", "DataStructures", "LinearAlgebra", "Printf", "REPL", "ShiftedArrays", "SparseArrays", "StatsAPI", "StatsBase", "StatsFuns", "Tables"] -git-tree-sha1 = "5cf6c4583533ee38639f73b880f35fc85f2941e0" -uuid = "3eaba693-59b7-5ba5-a881-562e759f1c8d" -version = "0.7.3" - -[[deps.Strided]] -deps = ["LinearAlgebra", "TupleTools"] -git-tree-sha1 = "a7a664c91104329c88222aa20264e1a05b6ad138" -uuid = "5e0ebb24-38b0-5f93-81fe-25c709ecae67" -version = "1.2.3" - -[[deps.StringEncodings]] -deps = ["Libiconv_jll"] -git-tree-sha1 = "b765e46ba27ecf6b44faf70df40c57aa3a547dcb" -uuid = "69024149-9ee7-55f6-a4c4-859efe599b68" -version = "0.3.7" - -[[deps.StructArrays]] -deps = ["ConstructionBase", "DataAPI", "Tables"] -git-tree-sha1 = "f4dc295e983502292c4c3f951dbb4e985e35b3be" -uuid = "09ab397b-f2b6-538f-b94a-2f83cf4a842a" -version = "0.6.18" -weakdeps = ["Adapt", "GPUArraysCore", "SparseArrays", "StaticArrays"] - - [deps.StructArrays.extensions] - StructArraysAdaptExt = "Adapt" - StructArraysGPUArraysCoreExt = "GPUArraysCore" - StructArraysSparseArraysExt = "SparseArrays" - StructArraysStaticArraysExt = "StaticArrays" - -[[deps.StructTypes]] -deps = ["Dates", "UUIDs"] -git-tree-sha1 = "ca4bccb03acf9faaf4137a9abc1881ed1841aa70" -uuid = "856f2bd8-1eba-4b0a-8007-ebc267875bd4" -version = "1.10.0" - -[[deps.SuiteSparse]] -deps = ["Libdl", "LinearAlgebra", "Serialization", "SparseArrays"] -uuid = "4607b0f0-06f3-5cda-b6b1-a6196a1729e9" - -[[deps.SuiteSparse_jll]] -deps = ["Artifacts", "Libdl", "libblastrampoline_jll"] -uuid = "bea87d4a-7f5b-5778-9afe-8cc45184846c" -version = "7.2.1+1" - -[[deps.TOML]] -deps = ["Dates"] -uuid = "fa267f1f-6049-4f14-aa54-33bafae1ed76" -version = "1.0.3" - -[[deps.TableTraits]] -deps = ["IteratorInterfaceExtensions"] -git-tree-sha1 = "c06b2f539df1c6efa794486abfb6ed2022561a39" -uuid = "3783bdb8-4a98-5b6b-af9a-565f29a5fe9c" -version = "1.0.1" - -[[deps.Tables]] -deps = ["DataAPI", "DataValueInterfaces", "IteratorInterfaceExtensions", "LinearAlgebra", "OrderedCollections", "TableTraits"] -git-tree-sha1 = "cb76cf677714c095e535e3501ac7954732aeea2d" -uuid = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" -version = "1.11.1" - -[[deps.Tar]] -deps = ["ArgTools", "SHA"] -uuid = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" -version = "1.10.0" - -[[deps.TensorCore]] -deps = ["LinearAlgebra"] -git-tree-sha1 = "1feb45f88d133a655e001435632f019a9a1bcdb6" -uuid = "62fd8b95-f654-4bbd-a8a5-9c27f68ccd50" -version = "0.1.1" - -[[deps.Test]] -deps = ["InteractiveUtils", "Logging", "Random", "Serialization"] -uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[[deps.ThreadingUtilities]] -deps = ["ManualMemory"] -git-tree-sha1 = "eda08f7e9818eb53661b3deb74e3159460dfbc27" -uuid = "8290d209-cae3-49c0-8002-c8c24d57dab5" -version = "0.5.2" - -[[deps.TimeZones]] -deps = ["Dates", "Future", "LazyArtifacts", "Mocking", "Pkg", "Printf", "RecipesBase", "Serialization", "Unicode"] -git-tree-sha1 = "a5688ffdbd849a98503c6650effe79fe89a41252" -uuid = "f269a46b-ccf7-5d73-abea-4c690281aa53" -version = "1.5.9" - -[[deps.TimerOutputs]] -deps = ["ExprTools", "Printf"] -git-tree-sha1 = "f548a9e9c490030e545f72074a41edfd0e5bcdd7" -uuid = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" -version = "0.5.23" - -[[deps.TranscodingStreams]] -git-tree-sha1 = "3caa21522e7efac1ba21834a03734c57b4611c7e" -uuid = "3bb67fe8-82b1-5028-8e26-92a6c54297fa" -version = "0.10.4" -weakdeps = ["Random", "Test"] - - [deps.TranscodingStreams.extensions] - TestExt = ["Test", "Random"] - -[[deps.Transducers]] -deps = ["Adapt", "ArgCheck", "BangBang", "Baselet", "CompositionsBase", "ConstructionBase", "DefineSingletons", "Distributed", "InitialValues", "Logging", "Markdown", "MicroCollections", "Requires", "Setfield", "SplittablesBase", "Tables"] -git-tree-sha1 = "3064e780dbb8a9296ebb3af8f440f787bb5332af" -uuid = "28d57a85-8fef-5791-bfe6-a80928e7c999" -version = "0.4.80" - - [deps.Transducers.extensions] - TransducersBlockArraysExt = "BlockArrays" - TransducersDataFramesExt = "DataFrames" - TransducersLazyArraysExt = "LazyArrays" - TransducersOnlineStatsBaseExt = "OnlineStatsBase" - TransducersReferenceablesExt = "Referenceables" - - [deps.Transducers.weakdeps] - BlockArrays = "8e7c35d0-a365-5155-bbbb-fb81a777f24e" - DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" - LazyArrays = "5078a376-72f3-5289-bfd5-ec5146d43c02" - OnlineStatsBase = "925886fa-5bf2-5e8e-b522-a9147a512338" - Referenceables = "42d2dcc6-99eb-4e98-b66c-637b7d73030e" - -[[deps.TupleTools]] -git-tree-sha1 = "41d61b1c545b06279871ef1a4b5fcb2cac2191cd" -uuid = "9d95972d-f1c8-5527-a6e0-b4b365fa01f6" -version = "1.5.0" - -[[deps.URIParser]] -deps = ["Unicode"] -git-tree-sha1 = "53a9f49546b8d2dd2e688d216421d050c9a31d0d" -uuid = "30578b45-9adc-5946-b283-645ec420af67" -version = "0.4.1" - -[[deps.URIs]] -git-tree-sha1 = "67db6cc7b3821e19ebe75791a9dd19c9b1188f2b" -uuid = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4" -version = "1.5.1" - -[[deps.UUIDs]] -deps = ["Random", "SHA"] -uuid = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" - -[[deps.UnPack]] -git-tree-sha1 = "387c1f73762231e86e0c9c5443ce3b4a0a9a0c2b" -uuid = "3a884ed6-31ef-47d7-9d2a-63182c4928ed" -version = "1.0.2" - -[[deps.Unicode]] -uuid = "4ec0a83e-493e-50e2-b9ac-8f72acf5a8f5" - -[[deps.UnicodeFun]] -deps = ["REPL"] -git-tree-sha1 = "53915e50200959667e78a92a418594b428dffddf" -uuid = "1cfade01-22cf-5700-b092-accc4b62d6e1" -version = "0.4.1" - -[[deps.Unitful]] -deps = ["Dates", "LinearAlgebra", "Random"] -git-tree-sha1 = "3c793be6df9dd77a0cf49d80984ef9ff996948fa" -uuid = "1986cc42-f94f-5a68-af5c-568840ba703d" -version = "1.19.0" - - [deps.Unitful.extensions] - ConstructionBaseUnitfulExt = "ConstructionBase" - InverseFunctionsUnitfulExt = "InverseFunctions" - - [deps.Unitful.weakdeps] - ConstructionBase = "187b0558-2788-49d3-abe0-74a17ed4e7c9" - InverseFunctions = "3587e190-3f89-42d0-90ee-14403ec27112" - -[[deps.UnitfulLatexify]] -deps = ["LaTeXStrings", "Latexify", "Unitful"] -git-tree-sha1 = "e2d817cc500e960fdbafcf988ac8436ba3208bfd" -uuid = "45397f5d-5981-4c77-b2b3-fc36d6e9b728" -version = "1.6.3" - -[[deps.UnsafeAtomics]] -git-tree-sha1 = "6331ac3440856ea1988316b46045303bef658278" -uuid = "013be700-e6cd-48c3-b4a1-df204f14c38f" -version = "0.2.1" - -[[deps.UnsafeAtomicsLLVM]] -deps = ["LLVM", "UnsafeAtomics"] -git-tree-sha1 = "ead6292c02aab389cb29fe64cc9375765ab1e219" -uuid = "d80eeb9a-aca5-4d75-85e5-170c8b632249" -version = "0.1.1" - -[[deps.Unzip]] -git-tree-sha1 = "ca0969166a028236229f63514992fc073799bb78" -uuid = "41fe7b60-77ed-43a1-b4f0-825fd5a5650d" -version = "0.2.0" - -[[deps.VectorizationBase]] -deps = ["ArrayInterface", "CPUSummary", "HostCPUFeatures", "IfElse", "LayoutPointers", "Libdl", "LinearAlgebra", "SIMDTypes", "Static", "StaticArrayInterface"] -git-tree-sha1 = "7209df901e6ed7489fe9b7aa3e46fb788e15db85" -uuid = "3d5dd08c-fd9d-11e8-17fa-ed2836048c2f" -version = "0.21.65" - -[[deps.VersionParsing]] -git-tree-sha1 = "58d6e80b4ee071f5efd07fda82cb9fbe17200868" -uuid = "81def892-9a0e-5fdd-b105-ffc91e053289" -version = "1.3.0" - -[[deps.Vulkan_Loader_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Wayland_jll", "Xorg_libX11_jll", "Xorg_libXrandr_jll", "xkbcommon_jll"] -git-tree-sha1 = "2f0486047a07670caad3a81a075d2e518acc5c59" -uuid = "a44049a8-05dd-5a78-86c9-5fde0876e88c" -version = "1.3.243+0" - -[[deps.Wayland_jll]] -deps = ["Artifacts", "EpollShim_jll", "Expat_jll", "JLLWrappers", "Libdl", "Libffi_jll", "Pkg", "XML2_jll"] -git-tree-sha1 = "7558e29847e99bc3f04d6569e82d0f5c54460703" -uuid = "a2964d1f-97da-50d4-b82a-358c7fce9d89" -version = "1.21.0+1" - -[[deps.Wayland_protocols_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "93f43ab61b16ddfb2fd3bb13b3ce241cafb0e6c9" -uuid = "2381bf8a-dfd0-557d-9999-79630e7b1b91" -version = "1.31.0+0" - -[[deps.WinReg]] -git-tree-sha1 = "cd910906b099402bcc50b3eafa9634244e5ec83b" -uuid = "1b915085-20d7-51cf-bf83-8f477d6f5128" -version = "1.0.0" - -[[deps.XML2_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libiconv_jll", "Zlib_jll"] -git-tree-sha1 = "07e470dabc5a6a4254ffebc29a1b3fc01464e105" -uuid = "02c8fc9c-b97f-50b9-bbe4-9be30ff0a78a" -version = "2.12.5+0" - -[[deps.XSLT_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Libgcrypt_jll", "Libgpg_error_jll", "Libiconv_jll", "Pkg", "XML2_jll", "Zlib_jll"] -git-tree-sha1 = "91844873c4085240b95e795f692c4cec4d805f8a" -uuid = "aed1982a-8fda-507f-9586-7b0439959a61" -version = "1.1.34+0" - -[[deps.XZ_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "31c421e5516a6248dfb22c194519e37effbf1f30" -uuid = "ffd25f8a-64ca-5728-b0f7-c24cf3aae800" -version = "5.6.1+0" - -[[deps.Xorg_libICE_jll]] -deps = ["Libdl", "Pkg"] -git-tree-sha1 = "e5becd4411063bdcac16be8b66fc2f9f6f1e8fe5" -uuid = "f67eecfb-183a-506d-b269-f58e52b52d7c" -version = "1.0.10+1" - -[[deps.Xorg_libSM_jll]] -deps = ["Libdl", "Pkg", "Xorg_libICE_jll"] -git-tree-sha1 = "4a9d9e4c180e1e8119b5ffc224a7b59d3a7f7e18" -uuid = "c834827a-8449-5923-a945-d239c165b7dd" -version = "1.2.3+0" - -[[deps.Xorg_libX11_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxcb_jll", "Xorg_xtrans_jll"] -git-tree-sha1 = "afead5aba5aa507ad5a3bf01f58f82c8d1403495" -uuid = "4f6342f7-b3d2-589e-9d20-edeb45f2b2bc" -version = "1.8.6+0" - -[[deps.Xorg_libXau_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "6035850dcc70518ca32f012e46015b9beeda49d8" -uuid = "0c0b7dd1-d40b-584c-a123-a41640f87eec" -version = "1.0.11+0" - -[[deps.Xorg_libXcursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXfixes_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "12e0eb3bc634fa2080c1c37fccf56f7c22989afd" -uuid = "935fb764-8cf2-53bf-bb30-45bb1f8bf724" -version = "1.2.0+4" - -[[deps.Xorg_libXdmcp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "34d526d318358a859d7de23da945578e8e8727b7" -uuid = "a3789734-cfe1-5b06-b2d0-1dd0d9d62d05" -version = "1.1.4+0" - -[[deps.Xorg_libXext_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "b7c0aa8c376b31e4852b360222848637f481f8c3" -uuid = "1082639a-0dae-5f34-9b06-72781eeb8cb3" -version = "1.3.4+4" - -[[deps.Xorg_libXfixes_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "0e0dc7431e7a0587559f9294aeec269471c991a4" -uuid = "d091e8ba-531a-589c-9de9-94069b037ed8" -version = "5.0.3+4" - -[[deps.Xorg_libXi_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXfixes_jll"] -git-tree-sha1 = "89b52bc2160aadc84d707093930ef0bffa641246" -uuid = "a51aa0fd-4e3c-5386-b890-e753decda492" -version = "1.7.10+4" - -[[deps.Xorg_libXinerama_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll"] -git-tree-sha1 = "26be8b1c342929259317d8b9f7b53bf2bb73b123" -uuid = "d1454406-59df-5ea1-beac-c340f2130bc3" -version = "1.1.4+4" - -[[deps.Xorg_libXrandr_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libXext_jll", "Xorg_libXrender_jll"] -git-tree-sha1 = "34cea83cb726fb58f325887bf0612c6b3fb17631" -uuid = "ec84b674-ba8e-5d96-8ba1-2a689ba10484" -version = "1.5.2+4" - -[[deps.Xorg_libXrender_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libX11_jll"] -git-tree-sha1 = "19560f30fd49f4d4efbe7002a1037f8c43d43b96" -uuid = "ea2f1a96-1ddc-540d-b46f-429655e07cfa" -version = "0.9.10+4" - -[[deps.Xorg_libpthread_stubs_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "8fdda4c692503d44d04a0603d9ac0982054635f9" -uuid = "14d82f49-176c-5ed1-bb49-ad3f5cbd8c74" -version = "0.1.1+0" - -[[deps.Xorg_libxcb_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "XSLT_jll", "Xorg_libXau_jll", "Xorg_libXdmcp_jll", "Xorg_libpthread_stubs_jll"] -git-tree-sha1 = "b4bfde5d5b652e22b9c790ad00af08b6d042b97d" -uuid = "c7cfdc94-dc32-55de-ac96-5a1b8d977c5b" -version = "1.15.0+0" - -[[deps.Xorg_libxkbfile_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libX11_jll"] -git-tree-sha1 = "730eeca102434283c50ccf7d1ecdadf521a765a4" -uuid = "cc61e674-0454-545c-8b26-ed2c68acab7a" -version = "1.1.2+0" - -[[deps.Xorg_xcb_util_cursor_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xcb_util_image_jll", "Xorg_xcb_util_jll", "Xorg_xcb_util_renderutil_jll"] -git-tree-sha1 = "04341cb870f29dcd5e39055f895c39d016e18ccd" -uuid = "e920d4aa-a673-5f3a-b3d7-f755a4d47c43" -version = "0.1.4+0" - -[[deps.Xorg_xcb_util_image_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "0fab0a40349ba1cba2c1da699243396ff8e94b97" -uuid = "12413925-8142-5f55-bb0e-6d7ca50bb09b" -version = "0.4.0+1" - -[[deps.Xorg_xcb_util_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_libxcb_jll"] -git-tree-sha1 = "e7fd7b2881fa2eaa72717420894d3938177862d1" -uuid = "2def613f-5ad1-5310-b15b-b15d46f528f5" -version = "0.4.0+1" - -[[deps.Xorg_xcb_util_keysyms_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "d1151e2c45a544f32441a567d1690e701ec89b00" -uuid = "975044d2-76e6-5fbe-bf08-97ce7c6574c7" -version = "0.4.0+1" - -[[deps.Xorg_xcb_util_renderutil_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "dfd7a8f38d4613b6a575253b3174dd991ca6183e" -uuid = "0d47668e-0667-5a69-a72c-f761630bfb7e" -version = "0.3.9+1" - -[[deps.Xorg_xcb_util_wm_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Xorg_xcb_util_jll"] -git-tree-sha1 = "e78d10aab01a4a154142c5006ed44fd9e8e31b67" -uuid = "c22f9ab0-d5fe-5066-847c-f4bb1cd4e361" -version = "0.4.1+1" - -[[deps.Xorg_xkbcomp_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_libxkbfile_jll"] -git-tree-sha1 = "330f955bc41bb8f5270a369c473fc4a5a4e4d3cb" -uuid = "35661453-b289-5fab-8a00-3d9160c6a3a4" -version = "1.4.6+0" - -[[deps.Xorg_xkeyboard_config_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Xorg_xkbcomp_jll"] -git-tree-sha1 = "691634e5453ad362044e2ad653e79f3ee3bb98c3" -uuid = "33bec58e-1273-512f-9401-5d533626f822" -version = "2.39.0+0" - -[[deps.Xorg_xtrans_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "e92a1a012a10506618f10b7047e478403a046c77" -uuid = "c5fb5394-a638-5e4d-96e5-b29de1b5cf10" -version = "1.5.0+0" - -[[deps.ZipFile]] -deps = ["Libdl", "Printf", "Zlib_jll"] -git-tree-sha1 = "f492b7fe1698e623024e873244f10d89c95c340a" -uuid = "a5390f91-8eb1-5f08-bee0-b1d1ffed6cea" -version = "0.10.1" - -[[deps.Zlib_jll]] -deps = ["Libdl"] -uuid = "83775a58-1f1d-513f-b197-d71354ab007a" -version = "1.2.13+1" - -[[deps.Zstd_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "49ce682769cd5de6c72dcf1b94ed7790cd08974c" -uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" -version = "1.5.5+0" - -[[deps.Zygote]] -deps = ["AbstractFFTs", "ChainRules", "ChainRulesCore", "DiffRules", "Distributed", "FillArrays", "ForwardDiff", "GPUArrays", "GPUArraysCore", "IRTools", "InteractiveUtils", "LinearAlgebra", "LogExpFunctions", "MacroTools", "NaNMath", "PrecompileTools", "Random", "Requires", "SparseArrays", "SpecialFunctions", "Statistics", "ZygoteRules"] -git-tree-sha1 = "4ddb4470e47b0094c93055a3bcae799165cc68f1" -uuid = "e88e6eb3-aa80-5325-afca-941959d7151f" -version = "0.6.69" - - [deps.Zygote.extensions] - ZygoteColorsExt = "Colors" - ZygoteDistancesExt = "Distances" - ZygoteTrackerExt = "Tracker" - - [deps.Zygote.weakdeps] - Colors = "5ae59095-9a9b-59fe-a467-6f913c188581" - Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7" - Tracker = "9f7883ad-71c0-57eb-9f7f-b5c9e6d3789c" - -[[deps.ZygoteRules]] -deps = ["ChainRulesCore", "MacroTools"] -git-tree-sha1 = "27798139afc0a2afa7b1824c206d5e87ea587a00" -uuid = "700de1a5-db45-46bc-99cf-38207098b444" -version = "0.2.5" - -[[deps.eudev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "gperf_jll"] -git-tree-sha1 = "431b678a28ebb559d224c0b6b6d01afce87c51ba" -uuid = "35ca27e7-8b34-5b7f-bca9-bdc33f59eb06" -version = "3.2.9+0" - -[[deps.fzf_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "a68c9655fbe6dfcab3d972808f1aafec151ce3f8" -uuid = "214eeab7-80f7-51ab-84ad-2988db7cef09" -version = "0.43.0+0" - -[[deps.gperf_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3516a5630f741c9eecb3720b1ec9d8edc3ecc033" -uuid = "1a1c6b14-54f6-533d-8383-74cd7377aa70" -version = "3.1.1+0" - -[[deps.libaec_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl"] -git-tree-sha1 = "46bf7be2917b59b761247be3f317ddf75e50e997" -uuid = "477f73a3-ac25-53e9-8cc3-50b2fa2566f0" -version = "1.1.2+0" - -[[deps.libaom_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "3a2ea60308f0996d26f1e5354e10c24e9ef905d4" -uuid = "a4ae2306-e953-59d6-aa16-d00cac43593b" -version = "3.4.0+0" - -[[deps.libass_jll]] -deps = ["Artifacts", "Bzip2_jll", "FreeType2_jll", "FriBidi_jll", "HarfBuzz_jll", "JLLWrappers", "Libdl", "Pkg", "Zlib_jll"] -git-tree-sha1 = "5982a94fcba20f02f42ace44b9894ee2b140fe47" -uuid = "0ac62f75-1d6f-5e53-bd7c-93b484bb37c0" -version = "0.15.1+0" - -[[deps.libblastrampoline_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850b90-86db-534c-a0d3-1478176c7d93" -version = "5.8.0+1" - -[[deps.libevdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "141fe65dc3efabb0b1d5ba74e91f6ad26f84cc22" -uuid = "2db6ffa8-e38f-5e21-84af-90c45d0032cc" -version = "1.11.0+0" - -[[deps.libevent_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "OpenSSL_jll"] -git-tree-sha1 = "f04ec6d9a186115fb38f858f05c0c4e1b7fc9dcb" -uuid = "1080aeaf-3a6a-583e-a51c-c537b09f60ec" -version = "2.1.13+1" - -[[deps.libfdk_aac_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "daacc84a041563f965be61859a36e17c4e4fcd55" -uuid = "f638f0a6-7fb0-5443-88ba-1cc74229b280" -version = "2.0.2+0" - -[[deps.libinput_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "eudev_jll", "libevdev_jll", "mtdev_jll"] -git-tree-sha1 = "ad50e5b90f222cfe78aa3d5183a20a12de1322ce" -uuid = "36db933b-70db-51c0-b978-0f229ee0e533" -version = "1.18.0+0" - -[[deps.libpng_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Zlib_jll"] -git-tree-sha1 = "d7015d2e18a5fd9a4f47de711837e980519781a4" -uuid = "b53b4c65-9356-5827-b1ea-8c7a1a84506f" -version = "1.6.43+1" - -[[deps.libvorbis_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Ogg_jll", "Pkg"] -git-tree-sha1 = "b910cb81ef3fe6e78bf6acee440bda86fd6ae00c" -uuid = "f27f6e37-5d2b-51aa-960f-b287f2bc3b7a" -version = "1.3.7+1" - -[[deps.mtdev_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "814e154bdb7be91d78b6802843f76b6ece642f11" -uuid = "009596ad-96f7-51b1-9f1b-5ce2d5e8a71e" -version = "1.1.6+0" - -[[deps.nghttp2_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "8e850ede-7688-5339-a07c-302acd2aaf8d" -version = "1.52.0+1" - -[[deps.p7zip_jll]] -deps = ["Artifacts", "Libdl"] -uuid = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" -version = "17.4.0+2" - -[[deps.prrte_jll]] -deps = ["Artifacts", "Hwloc_jll", "JLLWrappers", "Libdl", "PMIx_jll", "libevent_jll"] -git-tree-sha1 = "5adb2d7a18a30280feb66cad6f1a1dfdca2dc7b0" -uuid = "eb928a42-fffd-568d-ab9c-3f5d54fc65b9" -version = "3.0.2+0" - -[[deps.x264_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "4fea590b89e6ec504593146bf8b988b2c00922b2" -uuid = "1270edf5-f2f9-52d2-97e9-ab00b5d0237a" -version = "2021.5.5+0" - -[[deps.x265_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg"] -git-tree-sha1 = "ee567a171cce03570d77ad3a43e90218e38937a9" -uuid = "dfaa095f-4041-5dcd-9319-2fabd8486b76" -version = "3.5.0+0" - -[[deps.xkbcommon_jll]] -deps = ["Artifacts", "JLLWrappers", "Libdl", "Pkg", "Wayland_jll", "Wayland_protocols_jll", "Xorg_libxcb_jll", "Xorg_xkeyboard_config_jll"] -git-tree-sha1 = "9c304562909ab2bab0262639bd4f444d7bc2be37" -uuid = "d8fb68d0-12a3-5cfd-a85a-d49703b185fd" -version = "1.4.1+1" diff --git a/docs/src/Benchmarks_temp.jl b/docs/src/Benchmarks_temp.jl deleted file mode 100644 index ff9cd48a..00000000 --- a/docs/src/Benchmarks_temp.jl +++ /dev/null @@ -1,82 +0,0 @@ -# temp file -using Pkg -Pkg.activate(joinpath(@__DIR__,"..")) -ENV["PYTHON"] = "" -using Test, Statistics, Random, DelimitedFiles, Logging -using DataStructures, DataFrames, BenchmarkTools, StableRNGs, SystemBenchmark -import DecisionTree, Flux -import Clustering, GaussianMixtures -using BetaML -using Conda -using PyCall -pyimport_conda("sklearn", "sklearn", "conda-forge") - -TESTRNG = StableRNG(123) - - - - -println("*** Benchmarking regression task..") - -bm_regression = DataFrame(name= String[],time=Float64[],memory=Int64[],allocs=Int64[],mre_train=Float64[],std_train=Float64[],mre_test=Float64[],std_test=Float64[]) -n = 500 -seeds = rand(copy(TESTRNG),n) -x = vcat([[s*2 (s-3)^2 s/2 0.2-s] for s in seeds]...) -y = [r[1]*2-r[2]+r[3]^2 for r in eachrow(x)] - - -Random.seed!(123) -dt_models = OrderedDict("DT (DecisionTrees.jl)"=>DecisionTree.DecisionTreeRegressor(rng=copy(TESTRNG)), - "RF (DecisionTrees.jl)"=>DecisionTree.RandomForestRegressor(n_trees=30, rng=copy(TESTRNG)), -); - -# DT: -# set of regression parameters and respective default values -# pruning_purity: purity threshold used for post-pruning (default: 1.0, no pruning) -# max_depth: maximum depth of the decision tree (default: -1, no maximum) -# min_samples_leaf: the minimum number of samples each leaf needs to have (default: 5) -# min_samples_split: the minimum number of samples in needed for a split (default: 2) -# min_purity_increase: minimum purity needed for a split (default: 0.0) -# n_subfeatures: number of features to select at random (default: 0, keep all) -# keyword rng: the random number generator or seed to use (default Random.GLOBAL_RNG) - - -# RF: -# set of regression build_forest() parameters and respective default values -# n_subfeatures: number of features to consider at random per split (default: -1, sqrt(# features)) -# n_trees: number of trees to train (default: 10) -# partial_sampling: fraction of samples to train each tree on (default: 0.7) -# max_depth: maximum depth of the decision trees (default: no maximum) -# min_samples_leaf: the minimum number of samples each leaf needs to have (default: 5) -# min_samples_split: the minimum number of samples in needed for a split (default: 2) -# min_purity_increase: minimum purity needed for a split (default: 0.0) -# keyword rng: the random number generator or seed to use (default Random.GLOBAL_RNG) -# multi-threaded forests must be seeded with an `Int` - -for (mname,m) in dt_models - #mname = "DT" - #m = NeuralNetworkEstimator(rng=copy(TESTRNG),verbosity=NONE) - # speed measure - bres = @benchmark DecisionTree.fit!(m2,$x,$y) setup=(m2 = deepcopy($m)) - m_time = median(bres.times) - m_memory = bres.memory - m_allocs = bres.allocs - sampler = KFold(nsplits=10,rng=copy(TESTRNG)); - cv_out = cross_validation([x,y],sampler,return_statistics=false) do trainData,valData,rng - (xtrain,ytrain) = trainData; (xval,yval) = valData - m2 = deepcopy(m) - DecisionTree.fit!(m2,xtrain,ytrain) - ŷtrain = DecisionTree.predict(m2,xtrain) - ŷval = DecisionTree.predict(m2,xval) - rme_train = relative_mean_error(ytrain,ŷtrain) - rme_val = relative_mean_error(yval,ŷval) - return (rme_train, rme_val) - end - - mre_train = mean([r[1] for r in cv_out]) - std_train = std([r[1] for r in cv_out]) - mre_test = mean([r[2] for r in cv_out]) - std_test = std([r[2] for r in cv_out]) - push!(bm_regression,[mname, m_time, m_memory, m_allocs, mre_train, std_train, mre_test, std_test]) - @test mre_test <= 0.05 -end \ No newline at end of file diff --git a/docs/src/assets/data/breast+cancer+wisconsin+diagnostic.zip b/docs/src/assets/data/breast+cancer+wisconsin+diagnostic.zip deleted file mode 100644 index 17d98def90a57194aa47426c6b5a3688a53a5607..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51284 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 diff --git a/docs/src/tutorials/Classification - cars/betaml_tutorial_classification_cars.md b/docs/src/tutorials/Classification - cars/betaml_tutorial_classification_cars.md deleted file mode 100644 index e14627c5..00000000 --- a/docs/src/tutorials/Classification - cars/betaml_tutorial_classification_cars.md +++ /dev/null @@ -1,389 +0,0 @@ -```@meta -EditURL = "betaml_tutorial_classification_cars.jl" -``` - -# [A classification task when labels are known - determining the country of origin of cars given the cars characteristics](@id classification_tutorial) - -In this exercise we are provided with several technical characteristics (mpg, horsepower,weight, model year...) for several car's models, together with the country of origin of such models, and we would like to create a machine learning model such that the country of origin can be accurately predicted given the technical characteristics. -As the information to predict is a multi-class one, this is a _[classification](https://en.wikipedia.org/wiki/Statistical_classification) task. -It is a challenging exercise due to the simultaneous presence of three factors: (1) presence of missing data; (2) unbalanced data - 254 out of 406 cars are US made; (3) small dataset. - -Data origin: -- dataset description: [https://archive.ics.uci.edu/ml/datasets/auto+mpg](https://archive.ics.uci.edu/ml/datasets/auto+mpg) -- data source we use here: [https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data](https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data-original) - -Field description: - -1. mpg: _continuous_ -2. cylinders: _multi-valued discrete_ -3. displacement: _continuous_ -4. horsepower: _continuous_ -5. weight: _continuous_ -6. acceleration: _continuous_ -7. model year: _multi-valued discrete_ -8. origin: _multi-valued discrete_ -9. car name: _string (unique for each instance)_ - -The car name is not used in this tutorial, so that the country is inferred only from technical data. As this field includes also the car maker, and there are several car's models from the same car maker, a more sophisticated machine learnign model could exploit this information e.g. using a bag of word encoding. - -## Library loading and initialisation - -Activating the local environment specific to BetaML documentation - -```text -using Pkg -Pkg.activate(joinpath(@__DIR__,"..","..","..")) -``` - -We load a buch of packages that we'll use during this tutorial.. - -```text -using Random, HTTP, Plots, CSV, DataFrames, BenchmarkTools, StableRNGs, BetaML -import DecisionTree, Flux -import Pipe: @pipe -``` - -Machine Learning workflows include stochastic components in several steps: in the data sampling, in the model initialisation and often in the models's own algorithms (and sometimes also in the prediciton step). -BetaML provides a random nuber generator (RNG) in order to simplify reproducibility ( [`FIXEDRNG`](@ref BetaML.Utils.FIXEDRNG). This is nothing else than an istance of `StableRNG(123)` defined in the [`BetaML.Utils`](@ref utils_module) sub-module, but you can choose of course your own "fixed" RNG). See the [Dealing with stochasticity](@ref stochasticity_reproducibility) section in the [Getting started](@ref getting_started) tutorial for details. - -Here we are explicit and we use our own fixed RNG: - -```text -seed = 123 # The table at the end of this tutorial has been obtained with seeds 123, 1000 and 10000 -AFIXEDRNG = StableRNG(seed) -``` - -## Data loading and preparation - -To load the data from the internet our workflow is -(1) Retrieve the data --> (2) Clean it --> (3) Load it --> (4) Output it as a DataFrame. - -For step (1) we use `HTTP.get()`, for step (2) we use `replace!`, for steps (3) and (4) we uses the `CSV` package, and we use the "pip" `|>` operator to chain these operations, so that no file is ever saved on disk: - -```text -urlDataOriginal = "https://archive.ics.uci.edu/ml/machine-learning-databases/auto-mpg/auto-mpg.data-original" -data = @pipe HTTP.get(urlDataOriginal).body |> - replace!(_, UInt8('\t') => UInt8(' ')) |> # the original dataset has mixed field delimiters ! - CSV.File(_, delim=' ', missingstring="NA", ignorerepeated=true, header=false) |> - DataFrame; -nothing #hide -``` - -This results in a table where the rows are the observations (the various cars' models) and the column the fields. All BetaML models expect this layout. - -As the dataset is ordered, we randomly shuffle the data. - -```text -idx = randperm(copy(AFIXEDRNG),size(data,1)) -data[idx, :] -describe(data) -``` - -Columns 1 to 7 contain characteristics of the car, while column 8 encodes the country or origin ("1" -> US, "2" -> EU, "3" -> Japan). That's the variable we want to be able to predict. - -Columns 9 contains the car name, but we are not going to use this information in this tutorial. -Note also that some fields have missing data. - -Our first step is hence to divide the dataset in features (the x) and the labels (the y) we want to predict. The `x` is then a Julia standard `Matrix` of 406 rows by 7 columns and the `y` is a vector of the 406 observations: - -```text -x = Matrix{Union{Missing,Float64}}(data[:,1:7]); -y = Vector{Int64}(data[:,8]); -x = fit!(Scaler(),x) -``` - -Some algorithms that we will use today don't accept missing data, so we need to _impute_ them. BetaML provides several imputation models in the [`Imputation`](@ref) module. Note that many of these imputation models can be used for Collaborative Filtering / Recomendation Systems. Models as [`GaussianMixtureImputer`](@ref) have the advantage over traditional algorithms as k-nearest neighbors (KNN) that GMM can "detect" the hidden structure of the observed data, where some observation can be similar to a certain pool of other observvations for a certain characteristic, but similar to an other pool of observations for other characteristics. -Here we use [`RandomForestImputer`](@ref). While the model allows for reproducible multiple imputations (with the parameter `multiple_imputation=an_integer`) and multiple passages trough the various columns (fields) containing missing data (with the option `recursive_passages=an_integer`), we use here just a single imputation and a single passage. -As all `BetaML` models, `RandomForestImputer` follows the patters `m=ModelConstruction(pars); fit!(m,x,[y]); est = predict(m,x)` where `est` can be an estimation of some labels or be some characteristics of x itself (the imputed version, as in this case, a reprojected version as in [`PCAEncoder`](@ref)), depending if the model is supervised or not. See the [`API user documentation`](@ref api_usage)` for more details. -For imputers, the output of `predict` is the matrix with the imputed values replacing the missing ones, and we write here the model in a single line using a convenience feature that when the default `cache` parameter is used in the model constructor the `fit!` function returns itself the prediciton over the trained data: - -```text -x = fit!(RandomForestImputer(rng=copy(AFIXEDRNG)),x) # Same as `m = RandomForestImputer(rng=copy(AFIXEDRNG)); fit!(m,x); x= predict(m,x)` -``` - -Further, some models don't work with categorical data as well, so we need to represent our `y` as a matrix with a separate column for each possible categorical value (the so called "one-hot" representation). -For example, within a three classes field, the individual value `2` (or `"Europe"` for what it matters) would be represented as the vector `[0 1 0]`, while `3` (or `"Japan"`) would become the vector `[0 0 1]`. -To encode as one-hot we use the [`OneHotEncoder`](@ref) in [`BetaML.Utils`](@ref utils_module), using the same shortcut as for the imputer we used earlier: - -```text -y_oh = fit!(OneHotEncoder(),y) -``` - -In supervised machine learning it is good practice to partition the available data in a _training_, _validation_, and _test_ subsets, where the first one is used to train the ML algorithm, the second one to train any eventual "hyper-parameters" of the algorithm and the _test_ subset is finally used to evaluate the quality of the algorithm. -Here, for brevity, we use only the _train_ and the _test_ subsets, implicitly assuming we already know the best hyper-parameters. Please refer to the [regression tutorial](@ref regression_tutorial) for examples of the auto-tune feature of BetaML models to "automatically" train the hyper-parameters (hint: in most cases just add the parameter `autotune=true` in the model constructor), or the [clustering tutorial](@ref clustering_tutorial) for an example of using the [`cross_validation`](@ref) function to do it manually. - -We use then the [`partition`](@ref) function in [BetaML.Utils](@ref utils_module), where we can specify the different data to partition (each matrix or vector to partition must have the same number of observations) and the shares of observation that we want in each subset. Here we keep 80% of observations for training (`xtrain`, and `ytrain`) and we use 20% of them for testing (`xtest`, and `ytest`): - -```text -((xtrain,xtest),(ytrain,ytest),(ytrain_oh,ytest_oh)) = partition([x,y,y_oh],[0.8,1-0.8],rng=copy(AFIXEDRNG)); -nothing #hide -``` - -We finally set up a dataframe to store the accuracies of the various models we'll use. - -```text -results = DataFrame(model=String[],train_acc=Float64[],test_acc=Float64[]) -``` - -## Random Forests - -We are now ready to use our first model, the [`RandomForestEstimator`](@ref). Random Forests build a "forest" of decision trees models and then average their predictions in order to make an overall prediction, wheter a regression or a classification. - -While here the missing data has been imputed and the dataset is comprised of only numerical values, one attractive feature of BetaML `RandomForestEstimator` is that they can work directly with missing and categorical data without any prior processing required. - -However as the labels are encoded using integers, we need also to specify the parameter `force_classification=true`, otherwise the model would undergo a _regression_ job instead. - -```text -rfm = RandomForestEstimator(force_classification=true, rng=copy(AFIXEDRNG)) -``` - -Opposite to the `RandomForestImputer` and `OneHotEncoder` models used earielr, to train a `RandomForestEstimator` model we need to provide it with both the training feature matrix and the associated "true" training labels. We use the same shortcut to get the training predictions directly from the `fit!` function. In this case the predictions correspond to the labels: - -```text -ŷtrain = fit!(rfm,xtrain,ytrain) -``` - -You can notice that for each record the result is reported in terms of a dictionary with the possible categories and their associated probabilities. - -!!! warning - Only categories with non-zero probabilities are reported for each record, and being a dictionary, the order of the categories is not undefined - -For example `ŷtrain[1]` is a `Dict(2 => 0.0333333, 3 => 0.933333, 1 => 0.0333333)`, indicating an overhelming probability that that car model originates from Japan. -To retrieve the predictions with the highest probabilities use `mode(ŷ)`: - -```text -ŷtrain_top = mode(ŷtrain,rng=copy(AFIXEDRNG)) -``` - -Why `mode` takes (optionally) a RNG ? I let the answer for you :-) - -To obtain the predicted labels for the test set we simply run the `predict` function over the features of the test set: - -```text -ŷtest = predict(rfm,xtest) -``` - -Finally we can measure the _accuracy_ of our predictions with the [`accuracy`](@ref) function. We don't need to explicitly use `mode`, as `accuracy` does it itself when it is passed with predictions expressed as a dictionary: - -```text -trainAccuracy,testAccuracy = accuracy.([ytrain,ytest],[ŷtrain,ŷtest],rng=copy(AFIXEDRNG)) -``` - -We are now ready to store our first model accuracies in the `results` dataframe: - -```text -push!(results,["RF",trainAccuracy,testAccuracy]); -nothing #hide -``` - -The predictions are quite good, for the training set the algoritm predicted almost all cars' origins correctly, while for the testing set (i.e. those records that has **not** been used to train the algorithm), the correct prediction level is still quite high, at around 80% (depends on the random seed) - -While accuracy can sometimes suffice, we may often want to better understand which categories our model has trouble to predict correctly. -We can investigate the output of a multi-class classifier more in-deep with a [`ConfusionMatrix`](@ref) where the true values (`y`) are given in rows and the predicted ones (`ŷ`) in columns, together to some per-class metrics like the _precision_ (true class _i_ over predicted in class _i_), the _recall_ (predicted class _i_ over the true class _i_) and others. - -We fist build the [`ConfusionMatrix`](@ref) model, we train it with `ŷ` and `y` and then we print it (we do it here for the test subset): - -```text -cfm = ConfusionMatrix(categories_names=Dict(1=>"US",2=>"EU",3=>"Japan"),rng=copy(AFIXEDRNG)) -fit!(cfm,ytest,ŷtest) # the output is by default the confusion matrix in relative terms -print(cfm) -``` - -From the report we can see that Japanese cars have more trouble in being correctly classified, and in particular many Japanease cars are classified as US ones. This is likely a result of the class imbalance of the data set, and could be solved by balancing the dataset with various sampling tecniques before training the model. - -If you prefer a more graphical approach, we can also plot the confusion matrix. In order to do so, we pick up information from the `info(cfm)` function. Indeed most BetaML models can be queried with `info(model)` to retrieve additional information, in terms of a dictionary, that is not necessary to the prediciton, but could still be relevant. Other functions that you can use with BetaML models are `parameters(m)` and `hyperparamaeters(m)`. - -```text -res = info(cfm) -heatmap(string.(res["categories"]),string.(res["categories"]),res["normalised_scores"],seriescolor=cgrad([:white,:blue]),xlabel="Predicted",ylabel="Actual", title="Confusion Matrix (normalised scores)") -``` - -### Comparision with DecisionTree.jl - -We now compare BetaML [`RandomForestEstimator`] with the random forest estimator of the package [`DecisionTrees.jl`](https://github.com/JuliaAI/DecisionTree.jl)` random forests are similar in usage: we first "build" (train) the forest and we then make predictions out of the trained model. - -```text -# We train the model... -model = DecisionTree.build_forest(ytrain, xtrain,rng=seed) -# ..and we generate predictions and measure their error -(ŷtrain,ŷtest) = DecisionTree.apply_forest.([model],[xtrain,xtest]); -(trainAccuracy,testAccuracy) = accuracy.([ytrain,ytest],[ŷtrain,ŷtest]) -push!(results,["RF (DecisionTrees.jl)",trainAccuracy,testAccuracy]); -nothing #hide -``` - -While the accuracy on the training set is exactly the same as for `BetaML` random forets, `DecisionTree.jl` random forests are slighly less accurate in the testing sample. -Where however `DecisionTrees.jl` excell is in the efficiency: they are extremelly fast and memory thrifty, even if we should consider also the resources needed to impute the missing values, as they don't work with missing data. - -Also, one of the reasons DecisionTrees are such efficient is that internally the data is sorted to avoid repeated comparision, but in this way they work only with features that are sortable, while BetaML random forests accept virtually any kind of input without the needs to process it. - -### Neural network - -Neural networks (NN) can be very powerfull, but have two "inconvenients" compared with random forests: first, are a bit "picky". We need to do a bit of work to provide data in specific format. Note that this is _not_ feature engineering. One of the advantages on neural network is that for the most this is not needed for neural networks. However we still need to "clean" the data. One issue is that NN don't like missing data. So we need to provide them with the feature matrix "clean" of missing data. Secondly, they work only with numerical data. So we need to use the one-hot encoding we saw earlier. -Further, they work best if the features are scaled such that each feature has mean zero and standard deviation 1. This is why we scaled the data back at the beginning of this tutorial. - -We firt measure the dimensions of our data in input (i.e. the column of the feature matrix) and the dimensions of our output, i.e. the number of categories or columns in out one-hot encoded y. - -```text -D = size(xtrain,2) -classes = unique(y) -nCl = length(classes) -``` - -The second "inconvenient" of NN is that, while not requiring feature engineering, they still need a bit of practice on the way the structure of the network is built . It's not as simple as `fit!(Model(),x,y)` (altougth BetaML provides a "default" neural network structure that can be used, it isn't often adapted to the specific task). We need instead to specify how we want our layers, _chain_ the layers together and then decide a _loss_ overall function. Only when we done these steps, we have the model ready for training. -Here we define 2 [`DenseLayer`](@ref) where, for each of them, we specify the number of neurons in input (the first layer being equal to the dimensions of the data), the output layer (for a classification task, the last layer output size beying equal to the number of classes) and an _activation function_ for each layer (default the `identity` function). - -```text -ls = 50 # number of neurons in the inned layer -l1 = DenseLayer(D,ls,f=relu,rng=copy(AFIXEDRNG)) -l2 = DenseLayer(ls,nCl,f=relu,rng=copy(AFIXEDRNG)) -``` - -For a classification task, the last layer is a [`VectorFunctionLayer`](@ref) that has no learnable parameters but whose activation function is applied to the ensemble of the neurons, rather than individually on each neuron. In particular, for classification we pass the [`softmax`](@ref) function whose output has the same size as the input (i.e. the number of classes to predict), but we can use the `VectorFunctionLayer` with any function, including the [`pool1d`](@ref) function to create a "pooling" layer (using maximum, mean or whatever other sub-function we pass to `pool1d`) - -```text -l3 = VectorFunctionLayer(nCl,f=softmax) ## Add a (parameterless) layer whose activation function (softmax in this case) is defined to all its nodes at once -``` - -Finally we _chain_ the layers and assign a loss function and the number of epochs we want to train the model to the constructor of [`NeuralNetworkEstimator`](@ref): - -```text -nn = NeuralNetworkEstimator(layers=[l1,l2,l3],loss=crossentropy,rng=copy(AFIXEDRNG),epochs=500) -``` - -Aside the layer structure and size and the number of epochs, other hyper-parameters you may want to try are the `batch_size` and the optimisation algoritm to employ (`opt_alg`). - -Now we can train our network: - -```text -ŷtrain = fit!(nn, xtrain, ytrain_oh) -``` - -Predictions are in form of a _n_records_ by _n_classes_ matrix of the probabilities of each record being in that class. To retrieve the classes with the highest probabilities we can use again the `mode` function: - -```text -ŷtrain_top = mode(ŷtrain) -``` - -Once trained, we can predict the test labels. As the trained was based on the scaled feature matrix, so must be for the predictions - -```text -ŷtest = predict(nn,xtest) -``` - -And finally we can measure the accuracies and store the accuracies in the `result` dataframe: - -```text -trainAccuracy, testAccuracy = accuracy.([ytrain,ytest],[ŷtrain,ŷtest],rng=copy(AFIXEDRNG)) -push!(results,["NN",trainAccuracy,testAccuracy]); -nothing #hide -``` - -```text -cfm = ConfusionMatrix(categories_names=Dict(1=>"US",2=>"EU",3=>"Japan"),rng=copy(AFIXEDRNG)) -fit!(cfm,ytest,ŷtest) -print(cfm) -res = info(cfm) -heatmap(string.(res["categories"]),string.(res["categories"]),res["normalised_scores"],seriescolor=cgrad([:white,:blue]),xlabel="Predicted",ylabel="Actual", title="Confusion Matrix (normalised scores)") -``` - -While accuracies are a bit lower, the distribution of misclassification is similar, with many Jamanease cars misclassified as US ones (here we have also some EU cars misclassified as Japanease ones). - -### Comparisons with Flux - -As we did for Random Forests, we compare BetaML neural networks with the leading package for deep learning in Julia, [`Flux.jl`](https://github.com/FluxML/Flux.jl). - -In Flux the input must be in the form (fields, observations), so we transpose our original matrices - -```text -xtrainT, ytrain_ohT = transpose.([xtrain, ytrain_oh]) -xtestT, ytest_ohT = transpose.([xtest, ytest_oh]) -``` - -We define the Flux neural network model in a similar way than BetaML and load it with data, we train it, predict and measure the accuracies on the training and the test sets: - -We fix the random seed for Flux, altough you may still get different results depending on the number of threads used.. this is a problem we solve in BetaML with [`generate_parallel_rngs`](@ref). - -```text -Random.seed!(seed) - -l1 = Flux.Dense(D,ls,Flux.relu) -l2 = Flux.Dense(ls,nCl,Flux.relu) -Flux_nn = Flux.Chain(l1,l2) -fluxloss(x, y) = Flux.logitcrossentropy(Flux_nn(x), y) -ps = Flux.params(Flux_nn) -nndata = Flux.Data.DataLoader((xtrainT, ytrain_ohT),shuffle=true) -begin for i in 1:500 Flux.train!(fluxloss, ps, nndata, Flux.ADAM()) end end -ŷtrain = Flux.onecold(Flux_nn(xtrainT),1:3) -ŷtest = Flux.onecold(Flux_nn(xtestT),1:3) -trainAccuracy, testAccuracy = accuracy.([ytrain,ytest],[ŷtrain,ŷtest]) -``` - -```text -push!(results,["NN (Flux.jl)",trainAccuracy,testAccuracy]); -nothing #hide -``` - -While the train accuracy is little bit higher that BetaML, the test accuracy remains comparable - -## Perceptron-like classifiers. - -We finaly test 3 "perceptron-like" classifiers, the "classical" Perceptron ([`PerceptronClassifier`](@ref)), one of the first ML algorithms (a linear classifier), a "kernellised" version of it ([`KernelPerceptronClassifier`](@ref), default to using the radial kernel) and "PegasosClassifier" ([`PegasosClassifier`](@ref)) another linear algorithm that starts considering a gradient-based optimisation, altought without the regularisation term as in the Support Vector Machines (SVM). - -As for the previous classifiers we construct the model object, we train and predict and we compute the train and test accuracies: - -```text -pm = PerceptronClassifier(rng=copy(AFIXEDRNG)) -ŷtrain = fit!(pm, xtrain, ytrain) -ŷtest = predict(pm, xtest) -(trainAccuracy,testAccuracy) = accuracy.([ytrain,ytest],[ŷtrain,ŷtest]) -push!(results,["Perceptron",trainAccuracy,testAccuracy]); - -kpm = KernelPerceptronClassifier(rng=copy(AFIXEDRNG)) -ŷtrain = fit!(kpm, xtrain, ytrain) -ŷtest = predict(kpm, xtest) -(trainAccuracy,testAccuracy) = accuracy.([ytrain,ytest],[ŷtrain,ŷtest]) -push!(results,["KernelPerceptronClassifier",trainAccuracy,testAccuracy]); - - -pegm = PegasosClassifier(rng=copy(AFIXEDRNG)) -ŷtrain = fit!(pegm, xtrain, ytrain) -ŷtest = predict(pm, xtest) -(trainAccuracy,testAccuracy) = accuracy.([ytrain,ytest],[ŷtrain,ŷtest]) -push!(results,["Pegasaus",trainAccuracy,testAccuracy]); -nothing #hide -``` - -## Summary - -This is the summary of the results we had trying to predict the country of origin of the cars, based on their technical characteristics: - -```text -println(results) -``` - -If you clone BetaML repository - -Model accuracies on my machine with seedd 123, 1000 and 10000 respectivelly - -| model | train 1 | test 1 | train 2 | test 2 | train 3 | test 3 | -| --------------------- | --------- | -------- | -------- | -------- | -------- | -------- | -| RF | 0.996923 | 0.765432 | 1.000000 | 0.802469 | 1.000000 | 0.888889 | -| RF (DecisionTrees.jl) | 0.975385 | 0.765432 | 0.984615 | 0.777778 | 0.975385 | 0.864198 | -| NN | 0.886154 | 0.728395 | 0.916923 | 0.827160 | 0.895385 | 0.876543 | -│ NN (Flux.jl) | 0.793846 | 0.654321 | 0.938462 | 0.790123 | 0.935385 | 0.851852 | -│ Perceptron | 0.778462 | 0.703704 | 0.720000 | 0.753086 | 0.670769 | 0.654321 | -│ KernelPerceptronClassifier | 0.987692 | 0.703704 | 0.978462 | 0.777778 | 0.944615 | 0.827160 | -│ Pegasaus | 0.732308 | 0.703704 | 0.633846 | 0.753086 | 0.575385 | 0.654321 | - -We warn that this table just provides a rought idea of the various algorithms performances. Indeed there is a large amount of stochasticity both in the sampling of the data used for training/testing and in the initial settings of the parameters of the algorithm. For a statistically significant comparision we would have to repeat the analysis with multiple sampling (e.g. by cross-validation, see the [clustering tutorial](@ref clustering_tutorial) for an example) and initial random parameters. - -Neverthless the table above shows that, when we compare BetaML with the algorithm-specific leading packages, we found similar results in terms of accuracy, but often the leading packages are better optimised and run more efficiently (but sometimes at the cost of being less verstatile). -Also, for this dataset, Random Forests seems to remain marginally more accurate than Neural Network, altought of course this depends on the hyper-parameters and, with a single run of the models, we don't know if this difference is significant. - -[View this file on Github](betaml_tutorial_classification_cars.jl). - ---- - -*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* - diff --git a/docs/src/tutorials/Clustering - Iris/betaml_tutorial_cluster_iris.md b/docs/src/tutorials/Clustering - Iris/betaml_tutorial_cluster_iris.md deleted file mode 100644 index 99f0a930..00000000 --- a/docs/src/tutorials/Clustering - Iris/betaml_tutorial_cluster_iris.md +++ /dev/null @@ -1,301 +0,0 @@ -```@meta -EditURL = "betaml_tutorial_cluster_iris.jl" -``` - -# [A clustering task: the prediction of plant species from floreal measures (the iris dataset)](@id clustering_tutorial) -The task is to estimate the species of a plant given some floreal measurements. It use the classical "Iris" dataset. -Note that in this example we are using clustering approaches, so we try to understand the "structure" of our data, without relying to actually knowing the true labels ("classes" or "factors"). However we have chosen a dataset for which the true labels are actually known, so we can compare the accuracy of the algorithms we use, but these labels will not be used during the algorithms training. - -Data origin: -- dataset description: [https://en.wikipedia.org/wiki/Iris_flower_data_set](https://en.wikipedia.org/wiki/Iris_flower_data_set) -- data source we use here: [https://github.com/JuliaStats/RDatasets.jl](https://github.com/JuliaStats/RDatasets.jl) - -## Library and data loading - -Activating the local environment specific to BetaML documentation - -```text -using Pkg -Pkg.activate(joinpath(@__DIR__,"..","..","..")) -``` - -We load the Beta Machine Learning Toolkit as well as some other packages that we use in this tutorial - -```text -using BetaML -using Random, Statistics, Logging, BenchmarkTools, StableRNGs, RDatasets, Plots, DataFrames -``` - -We are also going to compare our results with two other leading packages in Julia for clustering analysis, [`Clustering.jl`](https://github.com/JuliaStats/Clustering.jl) that provides (inter alia) kmeans and kmedoids algorithms and [`GaussianMixtures.jl`](https://github.com/davidavdav/GaussianMixtures.jl) that provides, as the name says, Gaussian Mixture Models. So we import them (we "import" them, rather than "use", not to bound their full names into namespace as some would collide with BetaML). - -```text -import Clustering, GaussianMixtures -``` - -Here we are explicit and we use our own fixed RNG: - -```text -seed = 123 # The table at the end of this tutorial has been obtained with seeds 123, 1000 and 10000 -AFIXEDRNG = StableRNG(seed) -``` - -We do a few tweeks for the Clustering and GaussianMixtures packages. Note that in BetaML we can also control both the random seed and the verbosity in the algorithm call, not only globally - -```text -Random.seed!(seed) -#logger = Logging.SimpleLogger(stdout, Logging.Error); global_logger(logger); ## For suppressing GaussianMixtures output -``` - -Differently from the [regression tutorial](@ref regression_tutorial), we load the data here from [`RDatasets`](https://github.com/JuliaStats/RDatasets.jl](https://github.com/JuliaStats/RDatasets.jl), a package providing standard datasets. - -```text -iris = dataset("datasets", "iris") -describe(iris) -``` - -The iris dataset provides floreal measures in columns 1 to 4 and the assigned species name in column 5. There are no missing values - -## Data preparation -The first step is to prepare the data for the analysis. We collect the first 4 columns as our _feature_ `x` matrix and the last one as our `y` label vector. -As we are using clustering algorithms, we are not actually using the labels to train the algorithms, we'll behave like we do not know them, we'll just let the algorithm "learn" from the structure of the data itself. We'll however use it to judge the accuracy that the various algorithms reach. - -```text -x = Matrix{Float64}(iris[:,1:4]); -yLabels = unique(iris[:,5]) -``` - -As the labels are expressed as strings, the first thing we do is encode them as integers for our analysis using the [`OrdinalEncoder`](@ref) model (data isn't really needed to be actually ordered): - -```text -y = fit!(OrdinalEncoder(categories=yLabels),iris[:,5]) -``` - -The dataset from RDatasets is ordered by species, so we need to shuffle it to avoid biases. -Shuffling happens by default in cross_validation, but we are keeping here a copy of the shuffled version for later. -Note that the version of [`consistent_shuffle`](@ref) that is included in BetaML accepts several n-dimensional arrays and shuffle them (by default on rows, by we can specify the dimension) keeping the association between the various arrays in the shuffled output. - -```text -(xs,ys) = consistent_shuffle([x,y], rng=copy(AFIXEDRNG)); -nothing #hide -``` - -## Main analysis - -We will try 3 BetaML models ([`KMeansClusterer`](@ref), [`KMedoidsClusterer`](@ref) and [`GaussianMixtureClusterer`](@ref)) and we compare them with `kmeans` from Clusterings.jl and `GMM` from GaussianMixtures.jl - -`KMeansClusterer` and `KMedoidsClusterer` works by first initialising the centers of the k-clusters (step a ). These centers, also known as the "representatives", must be selected within the data for kmedoids, while for kmeans they are the geometrical centers. - -Then ( step b ) the algorithms iterates toward each point to assign the point to the cluster of the closest representative (according with a user defined distance metric, default to Euclidean), and ( step c ) moves each representative at the center of its newly acquired cluster (where "center" depends again from the metric). - -Steps _b_ and _c_ are reiterated until the algorithm converge, i.e. the tentative k representative points (and their relative clusters) don't move any more. The result (output of the algorithm) is that each point is assigned to one of the clusters (classes). - -The algorithm in `GaussianMixtureClusterer` is similar in that it employs an iterative approach (the Expectation_Minimisation algorithm, "em") but here we make the hipothesis that the data points are the observed outcomes of some _mixture_ probabilistic models where we have first a k-categorical variables whose outcomes are the (unobservble) parameters of a probabilistic distribution from which the data is finally drawn. Because the parameters of each of the k-possible distributions is unobservable this is also called a model with latent variables. - -Most `gmm` models use the Gaussain distribution as the family of the mixture components, so we can tought the `gmm` acronym to indicate _Gaussian Mixture Model_. In BetaML we have currently implemented only Gaussain components, but any distribution could be used by just subclassing `AbstractMixture` and implementing a couple of methids (you are invited to contribute or just ask for a distribution family you are interested), so I prefer to think "gmm" as an acronym for _Generative Mixture Model_. - -The algorithm tries to find the mixture that maximises the likelihood that the data has been generated indeed from such mixture, where the "E" step refers to computing the probability that each point belongs to each of the k-composants (somehow similar to the step _b_ in the kmeans/kmedoids algorithms), and the "M" step estimates, giving the association probabilities in step "E", the parameters of the mixture and of the individual components (similar to step _c_). - -The result here is that each point has a categorical distribution (PMF) representing the probabilities that it belongs to any of the k-components (our classes or clusters). This is interesting, as `gmm` can be used for many other things that clustering. It forms the backbone of the [`GaussianMixtureImputer`](@ref) model to impute missing values (on some or all dimensions) based to how close the record seems to its pears. For the same reasons, `GaussianMixtureImputer` can also be used to predict user's behaviours (or users' appreciation) according to the behaviour/ranking made by pears ("collaborative filtering"). - -While the result of `GaussianMixtureClusterer` is a vector of PMFs (one for each record), error measures and reports with the true values (if known) can be directly applied, as in BetaML they internally call `mode()` to retrieve the class with the highest probability for each record. - - -As we are here, we also try different versions of the BetaML models, even if the default "versions" should be fine. For `KMeansClusterer` and `KMedoidsClusterer` we will try different initialisation strategies ("gird", the default one, "random" and "shuffle"), while for the `GaussianMixtureClusterer` model we'll choose different distributions of the Gaussain family (`SphericalGaussian` - where the variance is a scalar, `DiagonalGaussian` - with a vector variance, and `FullGaussian`, where the covariance is a matrix). - -As the result would depend on stochasticity both in the data selected and in the random initialisation, we use a cross-validation approach to run our models several times (with different data) and then we average their results. -Cross-Validation in BetaML is very flexible and it is done using the [`cross_validation`](@ref) function. It is used by default for hyperparameters autotuning of the BetaML supervised models. -`cross_validation` works by calling the function `f`, defined by the user, passing to it the tuple `trainData`, `valData` and `rng` and collecting the result of the function f. The specific method for which `trainData`, and `valData` are selected at each iteration depends on the specific `sampler`. - -We start by selectign a k-fold sampler that split our data in 5 different parts, it uses 4 for training and 1 part (not used here) for validation. We run the simulations twice and, to be sure to have replicable results, we fix the random seed (at the whole crossValidaiton level, not on each iteration). - -```text -sampler = KFold(nsplits=5,nrepeats=3,shuffle=true, rng=copy(AFIXEDRNG)) -``` - -We can now run the cross-validation with our models. Note that instead of defining the function `f` and then calling `cross_validation[f(trainData,testData,rng),[x,y],...)` we use the Julia `do` block syntax and we write directly the content of the `f` function in the `do` block. -Also, by default cross_validation already returns the mean and the standard deviation of the output of the user-provided `f` function (or the `do` block). However this requires that the `f` function returns a single scalar. Here we are returning a vector of the accuracies of the different models (so we can run the cross-validation only once), and hence we indicate with `return_statistics=false` to cross_validation not to attempt to generate statistics but rather report the whole output. -We'll compute the statistics ex-post. - -Inside the `do` block we do 4 things: -- we recover from `trainData` (a tuple, as we passed a tuple to `cross_validation` too) the `xtrain` features and `ytrain` labels; -- we run the various clustering algorithms -- we use the real labels to compute the model accuracy. Note that the clustering algorithm know nothing about the specific label name or even their order. This is why [`accuracy`](@ref) has the parameter `ignorelabels` to compute the accuracy oven any possible permutation of the classes found. -- we return the various models' accuracies - -```text -cOut = cross_validation([x,y],sampler,return_statistics=false) do trainData,testData,rng - # For unsupervised learning we use only the train data. - # Also, we use the associated labels only to measure the performances - (xtrain,ytrain) = trainData; - # We run the clustering algorithm and then and we compute the accuracy using the real labels: - estcl = fit!(KMeansClusterer(n_classes=3,initialisation_strategy="grid",rng=rng),xtrain) - kMeansGAccuracy = accuracy(ytrain,estcl,ignorelabels=true) - estcl = fit!(KMeansClusterer(n_classes=3,initialisation_strategy="random",rng=rng),xtrain) - kMeansRAccuracy = accuracy(ytrain,estcl,ignorelabels=true) - estcl = fit!(KMeansClusterer(n_classes=3,initialisation_strategy="shuffle",rng=rng),xtrain) - kMeansSAccuracy = accuracy(ytrain,estcl,ignorelabels=true) - estcl = fit!(KMedoidsClusterer(n_classes=3,initialisation_strategy="grid",rng=rng),xtrain) - kMedoidsGAccuracy = accuracy(ytrain,estcl,ignorelabels=true) - estcl = fit!(KMedoidsClusterer(n_classes=3,initialisation_strategy="random",rng=rng),xtrain) - kMedoidsRAccuracy = accuracy(ytrain,estcl,ignorelabels=true) - estcl = fit!(KMedoidsClusterer(n_classes=3,initialisation_strategy="shuffle",rng=rng),xtrain) - kMedoidsSAccuracy = accuracy(ytrain,estcl,ignorelabels=true) - estcl = fit!(GaussianMixtureClusterer(n_classes=3,mixtures=SphericalGaussian,rng=rng,verbosity=NONE),xtrain) - gmmSpherAccuracy = accuracy(ytrain,estcl,ignorelabels=true, rng=rng) - estcl = fit!(GaussianMixtureClusterer(n_classes=3,mixtures=DiagonalGaussian,rng=rng,verbosity=NONE),xtrain) - gmmDiagAccuracy = accuracy(ytrain,estcl,ignorelabels=true, rng=rng) - estcl = fit!(GaussianMixtureClusterer(n_classes=3,mixtures=FullGaussian,rng=rng,verbosity=NONE),xtrain) - gmmFullAccuracy = accuracy(ytrain,estcl,ignorelabels=true, rng=rng) - # For comparision with Clustering.jl - clusteringOut = Clustering.kmeans(xtrain', 3) - kMeans2Accuracy = accuracy(ytrain,clusteringOut.assignments,ignorelabels=true) - # For comparision with GaussianMistures.jl - sometimes GaussianMistures.jl em! fails with a PosDefException - dGMM = GaussianMixtures.GMM(3, xtrain; method=:kmeans, kind=:diag) - GaussianMixtures.em!(dGMM, xtrain) - gmmDiag2Accuracy = accuracy(ytrain,GaussianMixtures.gmmposterior(dGMM, xtrain)[1],ignorelabels=true) - fGMM = GaussianMixtures.GMM(3, xtrain; method=:kmeans, kind=:full) - GaussianMixtures.em!(fGMM, xtrain) - gmmFull2Accuracy = accuracy(ytrain,GaussianMixtures.gmmposterior(fGMM, xtrain)[1],ignorelabels=true) - # Returning the accuracies - return kMeansGAccuracy,kMeansRAccuracy,kMeansSAccuracy,kMedoidsGAccuracy,kMedoidsRAccuracy,kMedoidsSAccuracy,gmmSpherAccuracy,gmmDiagAccuracy,gmmFullAccuracy,kMeans2Accuracy,gmmDiag2Accuracy,gmmFull2Accuracy - end - -# We transform the output in matrix for easier analysis -accuracies = fill(0.0,(length(cOut),length(cOut[1]))) -[accuracies[r,c] = cOut[r][c] for r in 1:length(cOut),c in 1:length(cOut[1])] -μs = mean(accuracies,dims=1) -σs = std(accuracies,dims=1) - - -modelLabels=["kMeansG","kMeansR","kMeansS","kMedoidsG","kMedoidsR","kMedoidsS","gmmSpher","gmmDiag","gmmFull","kMeans (Clustering.jl)","gmmDiag (GaussianMixtures.jl)","gmmFull (GaussianMixtures.jl)"] - -report = DataFrame(mName = modelLabels, avgAccuracy = dropdims(round.(μs',digits=3),dims=2), stdAccuracy = dropdims(round.(σs',digits=3),dims=2)) -``` - -Accuracies (mean and its standard dev.) running this scripts with different random seeds (`123`, `1000` and `10000`): - -| model | μ 1 | σ² 1 | μ 2 | σ² 2 | μ 3 | σ² 3 | -| ------------------------------| ----- | ----- | ----- | ----- | ----- | ----- | -│ kMeansG | 0.891 | 0.017 | 0.892 | 0.012 | 0.893 | 0.017 | -│ kMeansR | 0.866 | 0.083 | 0.831 | 0.127 | 0.836 | 0.114 | -│ kMeansS | 0.764 | 0.174 | 0.822 | 0.145 | 0.779 | 0.170 | -│ kMedoidsG | 0.894 | 0.015 | 0.896 | 0.012 | 0.894 | 0.017 | -│ kMedoidsR | 0.804 | 0.144 | 0.841 | 0.123 | 0.825 | 0.134 | -│ kMedoidsS | 0.893 | 0.018 | 0.834 | 0.130 | 0.877 | 0.085 | -│ gmmSpher | 0.893 | 0.016 | 0.891 | 0.016 | 0.895 | 0.017 | -│ gmmDiag | 0.917 | 0.022 | 0.912 | 0.016 | 0.916 | 0.014 | -│ gmmFull | 0.970 | 0.035 | 0.982 | 0.013 | 0.981 | 0.009 | -│ kMeans (Clustering.jl) | 0.856 | 0.112 | 0.873 | 0.083 | 0.873 | 0.089 | -│ gmmDiag (GaussianMixtures.jl) | 0.865 | 0.127 | 0.872 | 0.090 | 0.833 | 0.152 | -│ gmmFull (GaussianMixtures.jl) | 0.907 | 0.133 | 0.914 | 0.160 | 0.917 | 0.141 | - -We can see that running the script multiple times with different random seed confirm the estimated standard deviations collected with the cross_validation, with the BetaML GMM-based models and grid based ones being the most stable ones. - -### BetaML model accuracies - -From the output We see that the gmm models perform for this dataset generally better than kmeans or kmedoids algorithms, and they further have very low variances. -In detail, it is the (default) `grid` initialisation that leads to the better results for `kmeans` and `kmedoids`, while for the `gmm` models it is the `FullGaussian` to perform better. - -### Comparisions with `Clustering.jl` and `GaussianMixtures.jl` -For this specific case, both `Clustering.jl` and `GaussianMixtures.jl` report substantially worst accuracies, and with very high variances. But we maintain the ranking that Full Gaussian gmm > Diagonal Gaussian > Kmeans accuracy. -I suspect the reason that BetaML gmm works so well is in relation to the usage of kmeans algorithm for initialisation of the mixtures, itself initialized with a "grid" arpproach. -The grid initialisation "guarantee" indeed that the initial means of the mixture components are well spread across the multidimensional space defined by the data, and it helps avoiding the EM algoritm to converge to a bad local optimus. - -## Working without the labels - -Up to now we used the real labels to compare the model accuracies. But in real clustering examples we don't have the true classes, or we wouln't need to do clustering in the first instance, so we don't know the number of classes to use. -There are several methods to judge clusters algorithms goodness. For likelyhood based algorithms as `GaussianMixtureClusterer` we can use a information criteria that trade the goodness of the lickelyhood with the number of parameters used to do the fit. -BetaML provides by default in the gmm clustering outputs both the _Bayesian information criterion_ ([`BIC`](@ref bic)) and the _Akaike information criterion_ ([`AIC`](@ref aic)), where for both a lower value is better. - -We can then run the model with different number of classes and see which one leads to the lower BIC or AIC. -We run hence `cross_validation` again with the `FullGaussian` gmm model. -Note that we use the BIC/AIC criteria here for establishing the "best" number of classes but we could have used it also to select the kind of Gaussain distribution to use. This is one example of hyper-parameter tuning that we developed more in detail using autotuning in the [regression tutorial](@ref regression_tutorial). - -Let's try up to 4 possible classes: - -```text -K = 4 -sampler = KFold(nsplits=5,nrepeats=2,shuffle=true, rng=copy(AFIXEDRNG)) -cOut = cross_validation([x,y],sampler,return_statistics=false) do trainData,testData,rng - (xtrain,ytrain) = trainData; - BICS = [] - AICS = [] - for k in 1:K - m = GaussianMixtureClusterer(n_classes=k,mixtures=FullGaussian,rng=rng,verbosity=NONE) - fit!(m,xtrain) - push!(BICS,info(m)["BIC"]) - push!(AICS,info(m)["AIC"]) - end - return (BICS,AICS) -end - -# Transforming the output in matrices for easier analysis -Nit = length(cOut) - -BICS = fill(0.0,(Nit,K)) -AICS = fill(0.0,(Nit,K)) -[BICS[r,c] = cOut[r][1][c] for r in 1:Nit,c in 1:K] -[AICS[r,c] = cOut[r][2][c] for r in 1:Nit,c in 1:K] - -μsBICS = mean(BICS,dims=1) -``` - -```text -σsBICS = std(BICS,dims=1) -``` - -```text -μsAICS = mean(AICS,dims=1) -``` - -```text -σsAICS = std(AICS,dims=1) -``` - -```text -plot(1:K,[μsBICS' μsAICS'], labels=["BIC" "AIC"], title="Information criteria by number of classes", xlabel="number of classes", ylabel="lower is better") -``` - -We see that following the "lowest AIC" rule we would indeed choose three classes, while following the "lowest BIC" criteria we would have choosen only two classes. This means that there is two classes that, concerning the floreal measures used in the database, are very similar, and our models are unsure about them. Perhaps the biologists will end up one day with the conclusion that it is indeed only one specie :-). - -We could study this issue more in detail by analysing the [`ConfusionMatrix`](@ref), but the one used in BetaML does not account for the ignorelabels option (yet). - -### Analysing the silhouette of the cluster - -A further metric to analyse cluster output is the so-called [Sinhouette method](https://en.wikipedia.org/wiki/Silhouette_(clustering)) - -Silhouette is a distance-based metric and require as first argument a matrix of pairwise distances. This can be computed with the [`pairwise`](@ref) function, that default to using `l2_distance` (i.e. Euclidean). Many other distance functions are available in the [`Clustering`](@ref) sub-module or one can use the efficiently implemented distances from the [`Distances`](https://github.com/JuliaStats/Distances.jl) package, as in this example. - -We'll use here the [`silhouette`](@ref) function over a simple loop: - -```text -x,y = consistent_shuffle([x,y],dims=1) -import Distances -pd = pairwise(x,distance=Distances.euclidean) # we compute the pairwise distances -nclasses = 2:6 -models = [KMeansClusterer, KMedoidsClusterer, GaussianMixtureClusterer] -println("Silhouette score by model type and class number:") -for ncl in nclasses, mtype in models - m = mtype(n_classes=ncl, verbosity=NONE) - ŷ = fit!(m,x) - if mtype == GaussianMixtureClusterer - ŷ = mode(ŷ) - end - s = mean(silhouette(pd,ŷ)) - println("$mtype \t ($ncl classes): $s") -end -``` - -Highest levels are better. We see again that 2 classes have better scores ! - -## Conclusions - -We have shown in this tutorial how we can easily run clustering algorithms in BetaML with just one line of code `fit!(ChoosenClusterer(),x)`, but also how can we use cross-validation in order to help the model or parameter selection, with or whithout knowing the real classes. -We retrieve here what we observed with supervised models. Globally the accuracy of BetaML models are comparable to those of leading specialised packages (in this case they are even better), but there is a significant gap in computational efficiency that restricts the pratical usage of BetaML to datasets that fits in the pc memory. However we trade this relative inefficiency with very flexible model definition and utility functions (for example `GaussianMixtureClusterer` works with missing data, allowing it to be used as the backbone of the [`GaussianMixtureImputer`](@ref) missing imputation function, or for collaborative reccomendation systems). - -[View this file on Github](betaml_tutorial_cluster_iris.jl). - ---- - -*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* - diff --git a/docs/src/tutorials/Dimensionality reduction/betaml_tutorial_dimensionality_reduction.jl b/docs/src/tutorials/Dimensionality reduction/betaml_tutorial_dimensionality_reduction.jl deleted file mode 100644 index bfea7d61..00000000 --- a/docs/src/tutorials/Dimensionality reduction/betaml_tutorial_dimensionality_reduction.jl +++ /dev/null @@ -1,275 +0,0 @@ -# [A dimensionality reduction task: imagee encoding (the MNIST dataset)](@id dimensionality_reduction_tutorial) -# For several reasons, including reducing the risk to incur into the [curse of the dimensionality](), we need to reduce the dimensionality of our data. - -# Currently BetaML provides two methods for dimensionality reduction, using Principal Component Analysis ([`PCAEncoder`](@ref)), that linearlyreproject the data toward the axis of greeatest variance, or using an AutoEncoder ([`AutoEncoder`](@ref)) that try to learn, unsupervised, the characteristics of the data using neural network. - -# We will apply them to - -# -# Data origin: -# - dataset description: [https://en.wikipedia.org/wiki/Iris_flower_data_set](https://en.wikipedia.org/wiki/Iris_flower_data_set) -# - data source we use here: [https://github.com/JuliaStats/RDatasets.jl](https://github.com/JuliaStats/RDatasets.jl) - - -# ## Library and data loading -using Dates #src -println(now(), " ", "*** Start image recognition tutorial..." ) #src - -# Activating the local environment specific to BetaML documentation -using Pkg -Pkg.activate(joinpath(@__DIR__,"..","..","..")) -using Random -using BetaML -using MLDatasets # For loading the training data - -Random.seed!(123); -TESTRNG = FIXEDRNG # This could change... - -x,y = MLDatasets.MNIST()[:] -x = permutedims(x,(3,2,1)) -x = convert(Array{Float64,3},x) -x = reshape(x,size(x,1),size(x,2)*size(x,3)) -ohm = OneHotEncoder() -y_oh = fit!(ohm,y) -(N,D) = size(x) - -x2 = collect(x[1:10,:]) - -e_layers = [ - ReshaperLayer((D,1),(28,28,1)), # 784x1 => 28x28x1 - ConvLayer((28,28,1),(5,5),4,stride=2,f=relu,rng=copy(TESTRNG)), # 28x28x1 => 14x14x4 - ConvLayer((14,14,4),(3,3),8,stride=2,f=relu,rng=copy(TESTRNG)), # 14x14x4 => 7x7x8 - ConvLayer((7,7,8),(3,3),8,stride=2,f=relu,rng=copy(TESTRNG)), # 7x7x8 => 4x4x8 - ReshaperLayer((4,4,8),(128,1)), # 4x4x8 => 128x1 - DenseLayer(128,2,f=relu,rng=copy(TESTRNG)) # 128x1 => 2x1 -] - -d_layers = [ - DenseLayer(2,16,f=relu,rng=copy(TESTRNG)) - DenseLayer(16,784,f=relu,rng=copy(TESTRNG)) -] - - -ae_mod = AutoEncoder(encoded_size=2, e_layers=e_layers, d_layers=d_layers, epochs=4, cache=false) -x_ae = fit!(ae_mod,x2) - -predict(ae_mod,x2) - -xtemp = copy(x2) -xi = x[1,:] -for el in ae_mod.par.fullnn.par.nnstruct.layers[1:ae_mod.par.n_el] - xi = forward(el,xi) - println(typeof(xi)) - println(size(xi)) - - # xtemp = vcat([forward(el,r) for r in eachrow(xtemp)]'...) -end -return xtemp|> makematrix - - - - -DenseLayer(2,4*4*8,f=relu,rng=copy(TESTRNG)) # 2x1 => 128x1 - -ReshaperLayer((4*4*8,1),(4,4,8)) # 128x1 => 4x4x8 -a = ConvLayer((4,4,8),(3,3),8,stride=1,padding=3,f=relu,rng=copy(TESTRNG)) # 4x4x8 => 8x8x8 -a = ConvLayer((8,8,8),(3,3),8,stride=1,padding=3,f=relu,rng=copy(TESTRNG)) # 4x4x8 => 8x8x8 - -ReshaperLayer((D,1),(28,28,1)) - - -l1 = ReshaperLayer((D,1),(28,28,1)) -## 28x28x1 => 14x14x8 -l2 = ConvLayer(size(l1)[2],(5,5),8,stride=2,f=relu,rng=copy(TESTRNG)) -## 14x14x8 => 7x7x16 -l3 = ConvLayer(size(l2)[2],(3,3),16,stride=2,f=relu,rng=copy(TESTRNG)) -## 7x7x16 => 4x4x32 -l4 = ConvLayer(size(l3)[2],(3,3),32,stride=2,f=relu,rng=copy(TESTRNG)) -## 4x4x32 => 2x2x32 -l5 = ConvLayer(size(l4)[2],(3,3),32,stride=2,f=relu,rng=copy(TESTRNG)) -## 2x2x32 => 1x1x32 (global per layer mean) -l6 = PoolingLayer(size(l5)[2],(2,2),stride=(2,2),f=mean) -## 1x1x32 => 32x1 -l7 = ReshaperLayer(size(l6)[2]) -## 32x1 => 10x1 -l8 = DenseLayer(size(l7)[2][1],10,f=identity, rng=copy(TESTRNG)) - - - -pca_mod = PCAEncoder() -#x_pca = fit!(pca_mod,x[1:20000,200:end]) -e_layers = [DenseLayer(784,30)] -d_layers = [DenseLayer(30,784)] -ae_mod = AutoEncoder(encoded_size=2) -x_ae = fit!(ae_mod,x[1:200,:]) - -e_layers = - - - -x_train, y_train = MLDatasets.MNIST(split=:train)[:] -x_train = permutedims(x_train,(3,2,1)) -x_train = convert(Array{Float64,3},x_train) -x_train = reshape(x_train,size(x_train,1),size(x_train,2)*size(x_train,3)) -ohm = OneHotEncoder() -y_train_oh = fit!(ohm,y_train) - -x_test, y_test = MLDatasets.MNIST(split=:test)[:] -x_test = permutedims(x_test,(3,2,1)) -x_test = convert(Array{Float64,3},x_test) -x_test = reshape(x_test,size(x_test,1),size(x_test,2)*size(x_test,3)) -y_test_oh = predict(ohm,y_test) -(N,D) = size(x_train) - - -using DelimitedFiles -using Statistics -using BenchmarkTools -using Plots -using Flux -using Flux: Data.DataLoader -using Flux: onehotbatch, onecold, crossentropy -using MLDatasets # For loading the training data -#using Images, FileIO, ImageTransformations # For loading the actual images - -TESTRNG = FIXEDRNG # This could change... - -x_train, y_train = MLDatasets.MNIST(split=:train)[:] -x_train = permutedims(x_train,(3,2,1)) -x_train = convert(Array{Float64,3},x_train) -x_train = reshape(x_train,size(x_train,1),size(x_train,2)*size(x_train,3)) -ohm = OneHotEncoder() -y_train_oh = fit!(ohm,y_train) - -x_test, y_test = MLDatasets.MNIST(split=:test)[:] -x_test = permutedims(x_test,(3,2,1)) -x_test = convert(Array{Float64,3},x_test) -x_test = reshape(x_test,size(x_test,1),size(x_test,2)*size(x_test,3)) -y_test_oh = predict(ohm,y_test) -(N,D) = size(x_train) - -# Building the model: - -## 784x1 => 28x28x1 -l1 = ReshaperLayer((D,1),(28,28,1)) -## 28x28x1 => 14x14x8 -l2 = ConvLayer(size(l1)[2],(5,5),8,stride=2,f=relu,rng=copy(TESTRNG)) -## 14x14x8 => 7x7x16 -l3 = ConvLayer(size(l2)[2],(3,3),16,stride=2,f=relu,rng=copy(TESTRNG)) -## 7x7x16 => 4x4x32 -l4 = ConvLayer(size(l3)[2],(3,3),32,stride=2,f=relu,rng=copy(TESTRNG)) -## 4x4x32 => 2x2x32 -l5 = ConvLayer(size(l4)[2],(3,3),32,stride=2,f=relu,rng=copy(TESTRNG)) -## 2x2x32 => 1x1x32 (global per layer mean) -l6 = PoolingLayer(size(l5)[2],(2,2),stride=(2,2),f=mean) -## 1x1x32 => 32x1 -l7 = ReshaperLayer(size(l6)[2]) -## 32x1 => 10x1 -l8 = DenseLayer(size(l7)[2][1],10,f=identity, rng=copy(TESTRNG)) -## 10x1 => 10x1 -l9 = VectorFunctionLayer(size(l8)[2][1],f=BetaML.softmax) -layers = [l1,l2,l3,l4,l5,l6,l7,l8,l9] -m = NeuralNetworkEstimator(layers=layers,loss=squared_cost,verbosity=HIGH,batch_size=128,epochs=4) - -# We train the model only on a subset of the training data, otherwise it is too long for the automated building of this page. -# Training the whole MINST set takes approximatly 16 minutes on a mid-level laptop (on CPU), leading to a test accuracy of 0.969 -(x_debug,x_other),(y_debug_oh,y_other_oh) = partition([x_train,y_train_oh],[0.01,0.99],rng=copy(TESTRNG)) - -#preprocess!.(layers) -# 0.131836 seconds (477.02 k allocations: 53.470 MiB, 72.73% compilation time) -#@code_warntype preprocess!(l5) - -ŷ = fit!(m,x_debug,y_debug_oh) -#@btime fit!(m,x_debug,y_debug_oh) -# 1%: 15.909 s (1940246 allocations: 1.39 GiB) -# 17.509 s (1039126 allocations: 1.37 GiB) -# 15.766 s (1039111 allocations: 1.37 GiB) -# 14.669 s (3129139 allocations: 1.64 GiB) (w threads) -# 18.119 s (1039121 allocations: 1.37 GiB) -# 14.966 s (1039123 allocations: 1.37 GiB) (whout threads) -# 19.357 s (1039123 allocations: 1.37 GiB) - -#println(now(), " ", "*** prefit..." ) #src -#ŷ = fit!(m,x_train,y_train_oh) -#println(now(), " ", "*** postfit..." ) #src - -#y_true = inverse_predict(ohm,convert(Matrix{Bool},y_train_oh)) -y_true = inverse_predict(ohm,convert(Matrix{Bool},y_debug_oh)) -ŷ_nonoh = inverse_predict(ohm,ŷ) -accuracy(y_true,ŷ_nonoh) -hcat(y_true,ŷ_nonoh) - -ŷtest = predict(m,x_test) -ytest_true = inverse_predict(ohm,convert(Matrix{Bool},y_test_oh)) -ŷtest_nonoh = inverse_predict(ohm,ŷtest) -accuracy(ytest_true,ŷtest_nonoh) -hcat(ytest_true,ŷtest_nonoh) - -cm = ConfusionMatrix() -fit!(cm,ytest_true,ŷtest_nonoh) -print(cm) - -res = info(cm) - -heatmap(string.(res["categories"]),string.(res["categories"]),res["normalised_scores"],seriescolor=cgrad([:white,:blue]),xlabel="Predicted",ylabel="Actual", title="Confusion Matrix (normalised scores)") - -# ----------------------------------------------------------- -# ## Flux implementation -# This is the equivalent workflow in Flux. -# Fitting on the whole training dataset lead to a test accuracy of 0.9658, so likely not statistically different than BetaML, but with still a much faster comutation time, as it takes only 2 minutes instead of 16... - - -x_train, y_train = MLDatasets.MNIST(split=:train)[:] -x_train = permutedims(x_train,(2,1,3)); # For correct img axis -#x_train = convert(Array{Float32,3},x_train); -x_train = reshape(x_train,(28,28,1,60000)); -y_train = Flux.onehotbatch(y_train, 0:9) -train_data = Flux.Data.DataLoader((x_train, y_train), batchsize=128) -#x_test, y_test = MLDatasets.MNIST.testdata(dir = "data/MNIST") -x_test, y_test = MLDatasets.MNIST(split=:test)[:] -x_test = permutedims(x_test,(2,1,3)); # For correct img axis -#x_test = convert(Array{Float32,3},x_test); -x_test = reshape(x_test,(28,28,1,10000)); -y_test = Flux.onehotbatch(y_test, 0:9) - -model = Chain( - ## 28x28 => 14x14 - Conv((5, 5), 1=>8, pad=2, stride=2, Flux.relu), - ## 14x14 => 7x7 - Conv((3, 3), 8=>16, pad=1, stride=2, Flux.relu), - ## 7x7 => 4x4 - Conv((3, 3), 16=>32, pad=1, stride=2, Flux.relu), - ## 4x4 => 2x2 - Conv((3, 3), 32=>32, pad=1, stride=2, Flux.relu), - ## Average pooling on each width x height feature map - GlobalMeanPool(), - Flux.flatten, - Dense(32, 10), - Flux.softmax -) - - - -myaccuracy(y,ŷ) = (mean(Flux.onecold(ŷ) .== Flux.onecold(y))) -myloss(x, y) = Flux.crossentropy(model(x), y) - -opt = Flux.ADAM() -ps = Flux.params(model) -number_epochs = 4 - -[(println(e); Flux.train!(myloss, ps, train_data, opt)) for e in 1:number_epochs] - -ŷtrain = model(x_train) -ŷtest = model(x_test) -myaccuracy(y_train,ŷtrain) -myaccuracy(y_test,ŷtest) - -plot(Gray.(x_train[:,:,1,2])) - -cm = ConfusionMatrix() -fit!(cm,Flux.onecold(y_test) .-1, Flux.onecold(ŷtest) .-1 ) -println(cm) - -res = info(cm) -heatmap(string.(res["categories"]),string.(res["categories"]),res["normalised_scores"],seriescolor=cgrad([:white,:blue]),xlabel="Predicted",ylabel="Actual", title="Confusion Matrix (normalised scores)") - diff --git a/docs/src/tutorials/Feature importance/Feature_importance.jl b/docs/src/tutorials/Feature importance/Feature_importance.jl index 64d96e9c..9036369c 100644 --- a/docs/src/tutorials/Feature importance/Feature_importance.jl +++ b/docs/src/tutorials/Feature importance/Feature_importance.jl @@ -162,4 +162,4 @@ vline!([loss_fullmodel-quantile(Normal(1,0),0.975) * loss_fullmodel_sd/sqrt(ntri #- bar(var_names[sortperm(sobol_by_col)],sobol_by_col[sortperm(sobol_by_col)],label="Sobol index by col", permute=(:x,:y), yerror=quantile(Normal(1,0),0.975) .* (sobol_by_col_sd[sortperm(sobol_by_col)]./sqrt(ntrials_per_metric)), yrange=[0,0.5]) -# As we can see, the two analyses agree on the most important variables, showing that the size of the house (number of rooms), the percentage of low-income population in the neighbourhood and, to a lesser extent, the distance to employment centres are the most important variables for the estimation of house price in the Boston area. \ No newline at end of file +# As we can see, the two analyses agree on the most important variables, showing that the size of the house (number of rooms), the percentage of low-income population in the neighbourhood and, to a lesser extent, the distance to employment centres are the most important explanatory variables of house price in the Boston area. \ No newline at end of file diff --git a/docs/src/tutorials/Feature importance/Feature_importance.md b/docs/src/tutorials/Feature importance/Feature_importance.md deleted file mode 100644 index 8d5ef564..00000000 --- a/docs/src/tutorials/Feature importance/Feature_importance.md +++ /dev/null @@ -1,230 +0,0 @@ -```@meta -EditURL = "Feature_importance.jl" -``` - -# [Understanding variable importance in black-box machine learning models](@id variable_importance_tutorial) - -Often we want to understand the contribution of different variables (x columns) to the prediction accuracy of a black-box machine learning model. -To this end, BetaML 0.12 introduces [`FeatureRanker`](@ref), a flexible variable ranking estimator that employs multiple variable importance metrics. -`FeatureRanker` helps to determine the importance of features in predictions from any black-box machine learning model (not necessarily the BetaML suit), internally using cross-validation to assess the quality of the predictions (`metric="mda"`), or the contribution of the variable to the variance of the predictions (`metric="sobol"`), with or without a given variable. - -By default, it ranks variables (columns) in a single pass without retraining on each one. However, it is possible to specify the model to use multiple passes (where on each pass the less important variable is permuted). This helps to assess importance in the presence of highly correlated variables. -While the default strategy is to simply (temporarily) permute the "test" variable and predict the modified data set, it is possible to refit the model to be evaluated on each variable ("permute and relearn"), of course at a much higher computational cost. -However, if the ML model to be evaluated supports ignoring variables during prediction (as BetaML tree models do), it is possible to specify the keyword argument for such an option in the target model prediction function and avoid refitting. - -In this tutorial we will use `FeatureRanker` first with some synthetic data, and then with the Boston dataset to determine the most important variables in determining house prices. -We will compare the results with Shapley values using the [`ShapML`](https://github.com/nredell/ShapML.jl) package. - -Let's start by activating the local environment specific to the BetaML documentation and loading the necessary packages: - -```text -using Pkg -Pkg.activate(joinpath(@__DIR__,"..","..","..")) -using Statistics, Random, Pipe, StableRNGs, HTTP, CSV, DataFrames, Plots, BetaML -import Distributions: Normal, Uniform, quantile -import ShapML -Random.seed!(123) -``` - -## Example with synthetic data - -In this example, we generate a dataset of 5 random variables, where `x1` is the most important in determining `y`, `x2` is somewhat less important, `x3` has interaction effects with `x1`, while `x4` and `x5` do not contribute at all to the calculation of `y`. -We also add `x6` as a highly correlated variable to `x1`, but note that `x4` also does not contribute to `y`: - -```text -N = 2000 -xa = rand(Uniform(0.0,10.0),N,5) -xb = xa[:,1] .* rand.(Normal(1,0.5)) -x = hcat(xa,xb) -y = [10*r[1]-r[2]+0.1*r[3]*r[1] for r in eachrow(x) ]; -nothing #hide -``` - -Aside of `y`, that is numerical, we create also a categorical version to test classification and a further one-hot version to test neural networks models that, for classification tasks, work using one-hot encoded variables: - -```text -ysort = sort(y) -ycat = [(i < ysort[Int(round(N/3))]) ? "c" : ( (i < ysort[Int(round(2*N/3))]) ? "a" : "b") for i in y] -yoh = fit!(OneHotEncoder(),ycat); -nothing #hide -``` - -We first try a Random Forest regressor. The BetaML `RandomForestEstimator` model supports a `predict` function with the option to ignore specific dimensions. This allow us to "test" the various variables without retraining the model: - -```text -fr = FeatureRanker(model=RandomForestEstimator(),nsplits=5,nrepeats=1,recursive=false,metric="mda",ignore_dims_keyword="ignore_dims") -rank = fit!(fr,x,y) # As for the other BetaML models, `fit!` by default returns the predictions, in this case the ranking, avoiding a `predict` call -``` - -As expected, the ranking shows `x1` as the most important variable. Let's look in detail at the metrics that we can obtain by querying the model with `info(fr)`: - -```text -loss_by_col = info(fr)["loss_by_col"] -sobol_by_col = info(fr)["sobol_by_col"] -loss_by_col_sd = info(fr)["loss_by_col_sd"] -sobol_by_col_sd = info(fr)["sobol_by_col_sd"] -loss_fullmodel = info(fr)["loss_all_cols"] -loss_fullmodel_sd = info(fr)["loss_all_cols_sd"] -ntrials_per_metric = info(fr)["ntrials_per_metric"] -``` - -Since we choosed `mda` as the reported metric, we must have that the reported rank is equal to the sortperm of `loss_by_col`: - -```text -sortperm(loss_by_col) == rank -``` - -We can plot the loss per (omitted) column... - -```text -bar(string.(rank),loss_by_col[rank],label="Loss by col", yerror=quantile(Normal(1,0),0.975) .* (loss_by_col_sd[rank]./sqrt(ntrials_per_metric))) -``` - -..and the sobol values: - -```text -bar(string.(sortperm(sobol_by_col)),sobol_by_col[sortperm(sobol_by_col)],label="Sobol index by col", yerror=quantile(Normal(1,0),0.975) .* (sobol_by_col_sd[sortperm(sobol_by_col)]./sqrt(ntrials_per_metric))) -``` - -As we can see from the graphs, the model did a good job of identifying the first variable as the most important one, ignoring the others and even giving a very low importance to the correlated one. - -### Comparision with the Shapley values - -For Shapley values we need first to have a trained model - -```text -m = RandomForestEstimator() -fit!(m,x,y); -nothing #hide -``` - -We need then to wrap the predict function, accounting with the fact that BetaML models works with standard arrays, while `ShapML` assume data in DataFrame format: - -```text -function predict_function(model, data) - data_pred = DataFrame(y_pred = BetaML.predict(model, Matrix(data))) - return data_pred -end -``` - -We set up other data related to the simulation.. - -```text -explain = DataFrame(x[1:300, :],:auto) -reference = DataFrame(x,:auto) - -sample_size = 60 ; # Number of Monte Carlo samples. -nothing #hide -``` - -...and finally compute the stochastic Shapley values per individual record: - -```text -data_shap = ShapML.shap(explain = explain, - reference = reference, - model = m, - predict_function = predict_function, - sample_size = sample_size, - seed = 1 - ); -nothing #hide -``` - -We aggregate the Shape values by feature - -```text -shap_aggregated =combine(groupby(data_shap,[:feature_name])) do subdf - (mean_effect = mean(abs.(subdf.shap_effect)), std = std(abs.(subdf.shap_effect)), n = size(subdf,1) ) -end -shap_values = shap_aggregated.mean_effect - -bar(string.(sortperm(shap_values)),shap_values[sortperm(shap_values)],label="Shapley values by col", yerror=quantile(Normal(1,0),0.975) .* (shap_aggregated.std[sortperm(shap_values)]./ sqrt.(shap_aggregated.n))) -``` - -Note that the output using the Sobol index and the Shapley values are very similar. This shoudn't come as a surprice, as the two metrics are related. - -### Classifications - -For classification tasks, the usage of `FeatureRanker` doesn't change: - -```text -fr = FeatureRanker(model=RandomForestEstimator(),nsplits=3,nrepeats=2,recursive=true,metric="mda",ignore_dims_keyword="ignore_dims") -rank = fit!(fr,x,ycat) -``` - -```text -fr = FeatureRanker(model=NeuralNetworkEstimator(verbosity=NONE),nsplits=3,nrepeats=1,recursive=false,metric="sobol",refit=false) -rank = fit!(fr,x,yoh) -``` - -## Determinant of house prices in the Boston alrea - -We start this example by first loading the data from a CSV file and splitting the data in features and labels: - -```text -data = CSV.File(joinpath(@__DIR__,"data","housing.data"), delim=' ', header=false, ignorerepeated=true) |> DataFrame - -var_names = [ - "CRIM", # per capita crime rate by town - "ZN", # proportion of residential land zoned for lots over 25,000 sq.ft. - "INDUS", # proportion of non-retail business acres per town - "CHAS", # Charles River dummy variable (= 1 if tract bounds river; 0 otherwise) - "NOX", # nitric oxides concentration (parts per 10 million) - "RM", # average number of rooms per dwelling - "AGE", # proportion of owner-occupied units built prior to 1940 - "DIS", # weighted distances to five Boston employment centres - "RAD", # index of accessibility to radial highways - "TAX", # full-value property-tax rate per $10,000 - "PTRATIO", # pupil-teacher ratio by town - "B", # 1000(Bk - 0.63)^2 where Bk is the proportion of blacks by town - "LSTAT", # % lower status of the population -] -y_name = "MEDV" ;# Median value of owner-occupied homes in $1000's -nothing #hide -``` - -Our features are a set of 13 explanatory variables, while the label that we want to estimate is the average housing prices: - -```text -x = Matrix(data[:,1:13]) -y = data[:,14]; -nothing #hide -``` - -We use a Random Forest model as regressor and we compute the variable importance for this model as we did for the synthetic data: - -```text -fr = FeatureRanker(model=RandomForestEstimator(),nsplits=3,nrepeats=2,recursive=false) -rank = fit!(fr,x,y) - -loss_by_col = info(fr)["loss_by_col"] -sobol_by_col = info(fr)["sobol_by_col"] -loss_by_col_sd = info(fr)["loss_by_col_sd"] -sobol_by_col_sd = info(fr)["sobol_by_col_sd"] -loss_fullmodel = info(fr)["loss_all_cols"] -loss_fullmodel_sd = info(fr)["loss_all_cols_sd"] -ntrials_per_metric = info(fr)["ntrials_per_metric"] -``` - -Finally we can plot the variable importance: - -```text -bar(var_names[sortperm(loss_by_col)], loss_by_col[sortperm(loss_by_col)],label="Loss by var", permute=(:x,:y), yerror=quantile(Normal(1,0),0.975) .* (loss_by_col_sd[sortperm(loss_by_col)]./sqrt(ntrials_per_metric)), yrange=[0,0.5]) -vline!([loss_fullmodel], label="Loss with all vars",linewidth=2) -vline!([loss_fullmodel-quantile(Normal(1,0),0.975) * loss_fullmodel_sd/sqrt(ntrials_per_metric), - loss_fullmodel+quantile(Normal(1,0),0.975) * loss_fullmodel_sd/sqrt(ntrials_per_metric), -], label=nothing,linecolor=:black,linestyle=:dot,linewidth=1) -``` - -```text -bar(var_names[sortperm(sobol_by_col)],sobol_by_col[sortperm(sobol_by_col)],label="Sobol index by col", permute=(:x,:y), yerror=quantile(Normal(1,0),0.975) .* (sobol_by_col_sd[sortperm(sobol_by_col)]./sqrt(ntrials_per_metric)), yrange=[0,0.4]) -``` - -As we can see, the two analyses agree on the most important variables, showing that the size of the house (number of rooms), the percentage of low-income population in the neighbourhood and, to a lesser extent, the distance to employment centres are the most important explanatory variables of house price in the Boston area. - -[View this file on Github](Feature_importance.jl). - ---- - -*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* - diff --git a/docs/src/tutorials/Multi-branch neural network/betaml_tutorial_multibranch_nn.md b/docs/src/tutorials/Multi-branch neural network/betaml_tutorial_multibranch_nn.md deleted file mode 100644 index 6488587e..00000000 --- a/docs/src/tutorials/Multi-branch neural network/betaml_tutorial_multibranch_nn.md +++ /dev/null @@ -1,169 +0,0 @@ -```@meta -EditURL = "betaml_tutorial_multibranch_nn.jl" -``` - -# [A deep neural network with multi-branch architecture](@id multibranch_nn_tutorial) - -Often we can "divide" our feature sets into different groups, where for each group we have many, many variables whose importance in prediction we don't know, but for which using a fully dense layer would be too computationally expensive. -For example, we want to predict the growth of forest trees based on soil characteristics, climate characteristics and a bunch of other data (species, age, density...). - -A soil (or climate) database may have hundreds of variables, how can we reduce them to a few that encode all the "soil" information? -Sure, we could do a PCA or a clustering analysis, but a better way is to let our model itself find a way to _encode_ the soil information into a vector in a way that is optimal for our prediction goal, i.e. we target the encoding task at our prediction goal. - -So we run a multi-branch neural network where one branch is given by the soil variables - it starts from all the hundreds of variables and ends in a few neuron outputs, another branch in a similar way is for the climate variables, we merge them in a branch to take into account the soil-weather interrelation (for example, it is well known that the water retention capacity of a sandy soil is quite different from that of a clay soil) and finally we merge this branch with the other variable branch to arrive at a single predicted output. -In this example we focus on building, training and predicting a multi-branch neural network. See the other examples for cross-validation, hyperparameter tuning, scaling, overfitting, encoding, etc. - -Data origin: -- while we hope to apply this example soon on actual real world data, for now we work on synthetic random data just to assess the validity of the network configuration. - -## Library and data generation - -Activating the local environment specific to the tutorials - -```text -using Pkg -Pkg.activate(joinpath(@__DIR__,"..","..","..")) -``` - -We first load all the packages we are going to use - -```text -using StableRNGs, BetaML, Plots -``` - -Here we are explicit and we use our own fixed RNG: - -```text -seed = 123 -AFIXEDRNG = StableRNG(seed) -``` - -Here we generate the random data.. - -```text -N = 100 # records -soilD = 20 # dimensions of the soil database -climateD = 30 # dimensions of the climate database -othervarD = 10 # dimensions of the other variables database - -soilX = rand(StableRNG(seed),N,soilD) -climateX = rand(StableRNG(seed+10),N,climateD) -othervarX = rand(StableRNG(seed+20),N,othervarD) -X = hcat(soilX,climateX,othervarX) -Y = rand(StableRNG(seed+30),N) -``` - -## Model definition - -![Neural Network model](imgs/multibranch_nn.png) - -In the figure above, each circle represents a multi-neuron layer, with the number of neurons (output dimensions) written inside. Dotted circles are `RreplicatorLayer`s, which simply "pass through" the information to the next layer. -Red layers represent the layers responsible for the final step in encoding the information for a given branch. Subsequent layers will use this encoded information (i.e. decode it) to finally provide the prediction for the branch. -We create a first branch for the soil variables, a second for the climate variables and finally a third for the other variables. We merge the soil and climate branches in layer 4 and the resulting branch and the other variables branch in layer 6. Finally, the single neuron layer 8 provides the prediction. - -The weights along the whole chain can be learned using the traditional backpropagation algorithm. - -The whole model can be implemented with the following code: - -- layer 1: - -```text -l1_soil = DenseLayer(20,30,f=relu,rng=copy(AFIXEDRNG)) -l1_climate = ReplicatorLayer(30) -l1_oth = ReplicatorLayer(10) -l1 = GroupedLayer([l1_soil,l1_climate,l1_oth]) -``` - -- layer 2: - -```text -l2_soil = DenseLayer(30,30,f=relu,rng=copy(AFIXEDRNG)) -l2_climate = DenseLayer(30,40,f=relu,rng=copy(AFIXEDRNG)) -l2_oth = ReplicatorLayer(10) -l2 = GroupedLayer([l2_soil,l2_climate,l2_oth]) -``` - -- layer 3: - -```text -l3_soil = DenseLayer(30,4,f=relu,rng=copy(AFIXEDRNG)) # encoding of soil properties -l3_climate = DenseLayer(40,4,f=relu,rng=copy(AFIXEDRNG)) # encoding of climate properties -l3_oth = DenseLayer(10,15,f=relu,rng=copy(AFIXEDRNG)) -l3 = GroupedLayer([l3_soil,l3_climate,l3_oth]) -``` - -- layer 4: - -```text -l4_soilclim = DenseLayer(8,15,f=relu,rng=copy(AFIXEDRNG)) -l4_oth = DenseLayer(15,15,f=relu,rng=copy(AFIXEDRNG)) -l4 = GroupedLayer([l4_soilclim,l4_oth]) -``` - -- layer 5: - -```text -l5_soilclim = DenseLayer(15,6,f=relu,rng=copy(AFIXEDRNG)) # encoding of soil and climate properties together -l5_oth = DenseLayer(15,6,f=relu,rng=copy(AFIXEDRNG)) # encoding of other vars -l5 = GroupedLayer([l5_soilclim,l5_oth]) -``` - -- layer 6: - -```text -l6 = DenseLayer(12,15,f=relu,rng=copy(AFIXEDRNG)) -``` - -- layer 7: - -```text -l7 = DenseLayer(15,15,f=relu,rng=copy(AFIXEDRNG)) -``` - -- layer 8: - -```text -l8 = DenseLayer(15,1,f=relu,rng=copy(AFIXEDRNG)) -``` - -Finally we put the layers together and we create our `NeuralNetworkEstimator` model: - -```text -layers = [l1,l2,l3,l4,l5,l6,l7,l8] -m = NeuralNetworkEstimator(layers=layers,opt_alg=ADAM(),epochs=100,rng=copy(AFIXEDRNG)) -``` - -## Fitting the model -We are now ready to fit the model to the data. By default BetaML models return directly the predictions of the trained data as the output of the fitting call, so there is no need to separate call `predict(m,X)`. - -```text -Ŷ = fit!(m,X,Y) -``` - -## Model quality assessment -We can compute the relative mean error between the "true" Y and the Y estimated by the model. - -```text -rme = relative_mean_error(Y,Ŷ) -``` - -Of course we know there is no actual relation here between the X and The Y, as both are randomly generated, the result above just tell us that the network has been able to find a path between the X and Y that has been used for training, but we hope that in the real application this learned path represent a true, general relation beteen the inputs and the outputs. - -Finally we can also plot Y again Ŷ and visualize how the average loss reduced along the training: - -```text -scatter(Y,Ŷ,xlabel="vol observed",ylabel="vol estimated",label=nothing,title="Est vs. obs volumes") -``` - -```text -loss_per_epoch = info(m)["loss_per_epoch"] - -plot(loss_per_epoch, xlabel="epoch", ylabel="loss per epoch", label=nothing, title="Loss per epoch") -``` - -[View this file on Github](betaml_tutorial_multibranch_nn.jl). - ---- - -*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* - diff --git a/docs/src/tutorials/Regression - bike sharing/betaml_tutorial_regression_sharingBikes.md b/docs/src/tutorials/Regression - bike sharing/betaml_tutorial_regression_sharingBikes.md deleted file mode 100644 index 7747f9b0..00000000 --- a/docs/src/tutorials/Regression - bike sharing/betaml_tutorial_regression_sharingBikes.md +++ /dev/null @@ -1,565 +0,0 @@ -```@meta -EditURL = "betaml_tutorial_regression_sharingBikes.jl" -``` - -# [A regression task: the prediction of bike sharing demand](@id regression_tutorial) -The task is to estimate the influence of several variables (like the weather, the season, the day of the week..) on the demand of shared bicycles, so that the authority in charge of the service can organise the service in the best way. - -Data origin: -- original full dataset (by hour, not used here): [https://archive.ics.uci.edu/ml/datasets/Bike+Sharing+Dataset](https://archive.ics.uci.edu/ml/datasets/Bike+Sharing+Dataset) -- simplified dataset (by day, with some simple scaling): [https://www.hds.utc.fr/~tdenoeux/dokuwiki/en/aec](https://www.hds.utc.fr/~tdenoeux/dokuwiki/en/aec) -- description: [https://www.hds.utc.fr/~tdenoeux/dokuwiki/_media/en/exam_2019_ace_.pdf](https://www.hds.utc.fr/~tdenoeux/dokuwiki/_media/en/exam_2019_ace_.pdf) -- data: [https://www.hds.utc.fr/~tdenoeux/dokuwiki/_media/en/bike_sharing_day.csv.zip](https://www.hds.utc.fr/~tdenoeux/dokuwiki/_media/en/bike_sharing_day.csv.zip) - -Note that even if we are estimating a time serie, we are not using here a recurrent neural network as we assume the temporal dependence to be negligible (i.e. $Y_t = f(X_t)$ alone). - -## Library and data loading - -Activating the local environment specific to - -```text -using Pkg -Pkg.activate(joinpath(@__DIR__,"..","..","..")) -``` - -We first load all the packages we are going to use - -```text -using LinearAlgebra, Random, Statistics, StableRNGs, DataFrames, CSV, Plots, Pipe, BenchmarkTools, BetaML -import Distributions: Uniform, DiscreteUniform -import DecisionTree, Flux ## For comparisions -``` - -Here we are explicit and we use our own fixed RNG: - -```text -seed = 123 # The table at the end of this tutorial has been obtained with seeds 123, 1000 and 10000 -AFIXEDRNG = StableRNG(seed) -``` - -Here we load the data from a csv provided by the BataML package - -```text -basedir = joinpath(dirname(pathof(BetaML)),"..","docs","src","tutorials","Regression - bike sharing") -data = CSV.File(joinpath(basedir,"data","bike_sharing_day.csv"),delim=',') |> DataFrame -describe(data) -``` - -The variable we want to learn to predict is `cnt`, the total demand of bikes for a given day. Even if it is indeed an integer, we treat it as a continuous variable, so each single prediction will be a scalar $Y \in \mathbb{R}$. - -```text -plot(data.cnt, title="Daily bike sharing rents (2Y)", label=nothing) -``` - -## Decision Trees - -We start our regression task with Decision Trees. - -Decision trees training consist in choosing the set of questions (in a hierarcical way, so to form indeed a "decision tree") that "best" split the dataset given for training, in the sense that the split generate the sub-samples (always 2 subsamples in the BetaML implementation) that are, for the characteristic we want to predict, the most homogeneous possible. Decision trees are one of the few ML algorithms that has an intuitive interpretation and can be used for both regression or classification tasks. - -### Data preparation - -The first step is to prepare the data for the analysis. This indeed depends already on the model we want to employ, as some models "accept" almost everything as input, no matter if the data is numerical or categorical, if it has missing values or not... while other models are instead much more exigents, and require more work to "clean up" our dataset. - -The tutorial starts using Decision Tree and Random Forest models that definitly belong to the first group, so the only thing we have to do is to select the variables in input (the "feature matrix", that we will indicate with "X") and the variable representing our output (the information we want to learn to predict, we call it "y"): - -```text -x = Matrix{Float64}(data[:,[:instant,:season,:yr,:mnth,:holiday,:weekday,:workingday,:weathersit,:temp,:atemp,:hum,:windspeed]]) -y = data[:,16]; -nothing #hide -``` - -We finally set up a dataframe to store the relative mean errors of the various models we'll use. - -```text -results = DataFrame(model=String[],train_rme=Float64[],test_rme=Float64[]) -``` - -### Model selection - -We can now split the dataset between the data that we will use for training the algorithm and selecting the hyperparameters (`xtrain`/`ytrain`) and those for testing the quality of the algoritm with the optimal hyperparameters (`xtest`/`ytest`). We use the `partition` function specifying the share we want to use for these two different subsets, here 80%, and 20% respectively. As our data represents indeed a time serie, we want our model to be able to predict _future_ demand of bike sharing from _past_, observed rented bikes, so we do not shuffle the datasets as it would be the default. - -```text -((xtrain,xtest),(ytrain,ytest)) = partition([x,y],[0.75,1-0.75],shuffle=false) -(ntrain, ntest) = size.([ytrain,ytest],1) -``` - -Then we define the model we want to use, [`DecisionTreeEstimator`](@ref) in this case, and we create an instance of the model: - -```text -m = DecisionTreeEstimator(autotune=true, rng=copy(AFIXEDRNG)) -``` - -Passing a fixed Random Number Generator (RNG) to the `rng` parameter guarantees that everytime we use the model with the same data (from the model creation downward to value prediciton) we obtain the same results. In particular BetaML provide `FIXEDRNG`, an istance of `StableRNG` that guarantees reproducibility even across different Julia versions. See the section ["Dealing with stochasticity"](@ref stochasticity_reproducibility) for details. -Note the `autotune` parameter. BetaML has perhaps what is the easiest method for automatically tuning the model hyperparameters (thus becoming in this way _learned_ parameters). Indeed, in most cases it is enought to pass the attribute `autotune=true` on the model constructor and hyperparameters search will be automatically performed on the first `fit!` call. -If needed we can customise hyperparameter tuning, chosing the tuning method on the parameter `tunemethod`. The single-line above is equivalent to: - -```text -tuning_method = SuccessiveHalvingSearch( - hpranges = Dict("max_depth" =>[5,10,nothing], "min_gain"=>[0.0, 0.1, 0.5], "min_records"=>[2,3,5],"max_features"=>[nothing,5,10,30]), - loss = l2loss_by_cv, - res_shares = [0.05, 0.2, 0.3], - multithreads = true - ) -m_dt = DecisionTreeEstimator(autotune=true, rng=copy(AFIXEDRNG), tunemethod=tuning_method) -``` - -Note that the defaults change according to the specific model, for example `RandomForestEstimator`](@ref) autotuning default to not being multithreaded, as the individual model is already multithreaded. - -!!! tip - Refer to the versions of this tutorial for BetaML <= 0.6 for a good exercise on how to perform model selection using the [`cross_validation`](@ref) function, or even by custom grid search. - -We can now fit the model, that is learn the model parameters that lead to the best predictions from the data. By default (unless we use `cache=false` in the model constructor) the model stores also the training predictions, so we can just use `fit!()` instead of `fit!()` followed by `predict(model,xtrain)` - -```text -ŷtrain = fit!(m_dt,xtrain,ytrain) -``` - -The above code produces a fitted `DecisionTreeEstimator` object that can be used to make predictions given some new features, i.e. given a new X matrix of (number of observations x dimensions), predict the corresponding Y vector of scalars in R. - -```text -ŷtest = predict(m_dt, xtest) -``` - -We now compute the mean relative error for the training and the test set. The [`relative_mean_error`](@ref) is a very flexible error function. Without additional parameter, it computes, as the name says, the _relative mean error_, between an estimated and a true vector. -However it can also compute the _mean relative error_, also known as the "mean absolute percentage error" ([MAPE](https://en.wikipedia.org/wiki/Mean_absolute_percentage_error)), or use a p-norm higher than 1. -The _mean relative error_ enfatises the relativeness of the error, i.e. all observations and dimensions weigth the same, wether large or small. Conversly, in the _relative mean error_ the same relative error on larger observations (or dimensions) weights more. -In this tutorial we use the later, as our data has clearly some outlier days with very small rents, and we care more of avoiding our customers finding empty bike racks than having unrented bikes on the rack. Targeting a low mean average error would push all our predicitons down to try accomodate the low-level predicitons (to avoid a large relative error), and that's not what we want. - -We can then compute the relative mean error for the decision tree - -```text -rme_train = relative_mean_error(ytrain,ŷtrain) # 0.1367 -rme_test = relative_mean_error(ytest,ŷtest) # 0.1547 -``` - -And we save the real mean accuracies in the `results` dataframe: - -```text -push!(results,["DT",rme_train,rme_test]); -nothing #hide -``` - -We can plot the true labels vs the estimated one for the three subsets... - -```text -scatter(ytrain,ŷtrain,xlabel="daily rides",ylabel="est. daily rides",label=nothing,title="Est vs. obs in training period (DT)") -``` - -```text -scatter(ytest,ŷtest,xlabel="daily rides",ylabel="est. daily rides",label=nothing,title="Est vs. obs in testing period (DT)") -``` - -Or we can visualise the true vs estimated bike shared on a temporal base. -First on the full period (2 years) ... - -```text -ŷtrainfull = vcat(ŷtrain,fill(missing,ntest)) -ŷtestfull = vcat(fill(missing,ntrain), ŷtest) -plot(data[:,:dteday],[data[:,:cnt] ŷtrainfull ŷtestfull], label=["obs" "train" "test"], legend=:topleft, ylabel="daily rides", title="Daily bike sharing demand observed/estimated across the\n whole 2-years period (DT)") -``` - -..and then focusing on the testing period - -```text -stc = ntrain -endc = size(x,1) -plot(data[stc:endc,:dteday],[data[stc:endc,:cnt] ŷtestfull[stc:endc]], label=["obs" "test"], legend=:bottomleft, ylabel="Daily rides", title="Focus on the testing period (DT)") -``` - -The predictions aren't so bad in this case, however decision trees are highly instable, and the output could have depended just from the specific initial random seed. - -## Random Forests -Rather than trying to solve this problem using a single Decision Tree model, let's not try to use a _Random Forest_ model. Random forests average the results of many different decision trees and provide a more "stable" result. -Being made of many decision trees, random forests are hovever more computationally expensive to train. - -```text -m_rf = RandomForestEstimator(autotune=true, oob=true, rng=copy(AFIXEDRNG)) -ŷtrain = fit!(m_rf,xtrain,ytrain); -ŷtest = predict(m_rf,xtest); -rme_train = relative_mean_error(ytrain,ŷtrain) # 0.056 -rme_test = relative_mean_error(ytest,ŷtest) # 0.161 -push!(results,["RF",rme_train,rme_test]); -nothing #hide -``` - -While slower than individual decision trees, random forests remain relativly fast. We should also consider that they are by default efficiently parallelised, so their speed increases with the number of available cores (in building this documentation page, GitHub CI servers allow for a single core, so all the bechmark you see in this tutorial are run with a single core available). - -Random forests support the so-called "out-of-bag" error, an estimation of the error that we would have when the model is applied on a testing sample. -However in this case the oob reported is much smaller than the testing error we will actually find. This is due to the fact that the division between training/validation and testing in this exercise is not random, but has a temporal basis. It seems that in this example the data in validation/testing follows a different pattern/variance than those in training (in probabilistic terms, the daily observations are not i.i.d.). - -```text -info(m_rf) -oob_error, rme_test = info(m_rf)["oob_errors"],relative_mean_error(ytest,ŷtest) -``` - -In this case we found an error very similar to the one employing a single decision tree. Let's print the observed data vs the estimated one using the random forest and then along the temporal axis: - -```text -scatter(ytrain,ŷtrain,xlabel="daily rides",ylabel="est. daily rides",label=nothing,title="Est vs. obs in training period (RF)") -``` - -```text -scatter(ytest,ŷtest,xlabel="daily rides",ylabel="est. daily rides",label=nothing,title="Est vs. obs in testing period (RF)") -``` - -Full period plot (2 years): - -```text -ŷtrainfull = vcat(ŷtrain,fill(missing,ntest)) -ŷtestfull = vcat(fill(missing,ntrain), ŷtest) -plot(data[:,:dteday],[data[:,:cnt] ŷtrainfull ŷtestfull], label=["obs" "train" "test"], legend=:topleft, ylabel="daily rides", title="Daily bike sharing demand observed/estimated across the\n whole 2-years period (RF)") -``` - -Focus on the testing period: - -```text -stc = 620 -endc = size(x,1) -plot(data[stc:endc,:dteday],[data[stc:endc,:cnt] ŷtrainfull[stc:endc] ŷtestfull[stc:endc]], label=["obs" "val" "test"], legend=:bottomleft, ylabel="Daily rides", title="Focus on the testing period (RF)") -``` - -### Comparison with DecisionTree.jl random forest - -We now compare our results with those obtained employing the same model in the [DecisionTree package](https://github.com/bensadeghi/DecisionTree.jl), using the hyperparameters of the obtimal BetaML Random forest model: - -```text -best_rf_hp = hyperparameters(m_rf) -``` - -Hyperparameters of the DecisionTree.jl random forest model - -```text -n_subfeatures=isnothing(best_rf_hp.max_features) ? -1 : best_rf_hp.max_features; n_trees=best_rf_hp.n_trees; partial_sampling=0.7; max_depth=isnothing(best_rf_hp.max_depth) ? typemax(Int64) : best_rf_hp.max_depth; -min_samples_leaf=best_rf_hp.min_records; min_samples_split=best_rf_hp.min_records; min_purity_increase=best_rf_hp.min_gain; -nothing #hide -``` - -We train the model.. - -```text -model = DecisionTree.build_forest(ytrain, convert(Matrix,xtrain), - n_subfeatures, - n_trees, - partial_sampling, - max_depth, - min_samples_leaf, - min_samples_split, - min_purity_increase; - rng = seed) -``` - -And we generate predictions and measure their error - -```text -(ŷtrain,ŷtest) = DecisionTree.apply_forest.([model],[xtrain,xtest]); - - -(rme_train, rme_test) = relative_mean_error.([ytrain,ytest],[ŷtrain,ŷtest]) # 0.022 and 0.304 -push!(results,["RF (DecisionTree.jl)",rme_train,rme_test]); -nothing #hide -``` - -While the train error is very small, the error on the test set remains relativly high. The very low error level on the training set is a sign that it overspecialised on the training set, and we should have better ran a dedicated hyper-parameter tuning function for the DecisionTree.jl model (we did try using the default `DecisionTrees.jl` parameters, but we obtained roughtly the same results). - -Finally we plot the DecisionTree.jl predictions alongside the observed value: - -```text -ŷtrainfull = vcat(ŷtrain,fill(missing,ntest)) -ŷtestfull = vcat(fill(missing,ntrain), ŷtest) -plot(data[:,:dteday],[data[:,:cnt] ŷtrainfull ŷtestfull], label=["obs" "train" "test"], legend=:topleft, ylabel="daily rides", title="Daily bike sharing demand observed/estimated across the\n whole 2-years period (DT.jl RF)") -``` - -Again, focusing on the testing data: - -```text -stc = ntrain -endc = size(x,1) -plot(data[stc:endc,:dteday],[data[stc:endc,:cnt] ŷtestfull[stc:endc]], label=["obs" "test"], legend=:bottomleft, ylabel="Daily rides", title="Focus on the testing period (DT.jl RF)") -``` - -### Conclusions of Decision Trees / Random Forests methods -The error obtained employing DecisionTree.jl is significantly larger than those obtained using a BetaML random forest model, altought to be fair with DecisionTrees.jl we didn't tuned its hyper-parameters. Also, the DecisionTree.jl random forest model is much faster. -This is partially due by the fact that, internally, DecisionTree.jl models optimise the algorithm by sorting the observations. BetaML trees/forests don't employ this optimisation and hence they can work with true categorical data for which ordering is not defined. An other explanation of this difference in speed is that BetaML Random Forest models accept `missing` values within the feature matrix. -To sum up, BetaML random forests are ideal algorithms when we want to obtain good predictions in the most simpler way, even without manually tuning the hyper-parameters, and without spending time in cleaning ("munging") the feature matrix, as they accept almost "any kind" of data as it is. - -## Neural Networks - -BetaML provides only _deep forward neural networks_, artificial neural network units where the individual "nodes" are arranged in _layers_, from the _input layer_, where each unit holds the input coordinate, through various _hidden layer_ transformations, until the actual _output_ of the model: - -![Neural Networks](imgs/nn_scheme.png) - -In this layerwise computation, each unit in a particular layer takes input from _all_ the preceding layer units and it has its own parameters that are adjusted to perform the overall computation. The _training_ of the network consists in retrieving the coefficients that minimise a _loss_ function between the output of the model and the known data. -In particular, a _deep_ (feedforward) neural network refers to a neural network that contains not only the input and output layers, but also (a variable number of) hidden layers in between. - -Neural networks accept only numerical inputs. We hence need to convert all categorical data in numerical units. A common approach is to use the so-called "one-hot-encoding" where the catagorical values are converted into indicator variables (0/1), one for each possible value. This can be done in BetaML using the [`OneHotEncoder`](@ref) function: - -```text -seasonDummies = fit!(OneHotEncoder(),data.season) -weatherDummies = fit!(OneHotEncoder(),data.weathersit) -wdayDummies = fit!(OneHotEncoder(),data.weekday .+ 1) - - -# We compose the feature matrix with the new dimensions obtained from the onehotencoder functions -x = hcat(Matrix{Float64}(data[:,[:instant,:yr,:mnth,:holiday,:workingday,:temp,:atemp,:hum,:windspeed]]), - seasonDummies, - weatherDummies, - wdayDummies) -y = data[:,16]; -nothing #hide -``` - -As we did for decision trees/ random forests, we split the data in training, validation and testing sets - -```text -((xtrain,xtest),(ytrain,ytest)) = partition([x,y],[0.75,1-0.75],shuffle=false) -(ntrain, ntest) = size.([ytrain,ytest],1) -``` - -An other common operation with neural networks is to scale the feature vectors (X) and the labels (Y). The BetaML [`Scaler`](@ref) model, by default, scales the data such that each dimension has mean 0 and variance 1. - -Note that we can provide the `Scaler`` model with different scale factors or specify the columns that shoudn't be scaled (e.g. those resulting from the one-hot encoding). Finally we can reverse the scaling (this is useful to retrieve the unscaled features from a model trained with scaled ones). - -```text -cols_nottoscale = [2;4;5;10:23] -xsm = Scaler(skip=cols_nottoscale) -xtrain_scaled = fit!(xsm,xtrain) -xtest_scaled = predict(xsm,xtest) -ytrain_scaled = ytrain ./ 1000 # We just divide Y by 1000, as using full scaling of Y we may get negative demand. -ytest_scaled = ytest ./ 1000 -D = size(xtrain,2) -``` - -We can now build our feed-forward neaural network. We create three layers, the first layers will always have a input size equal to the dimensions of our data (the number of columns), and the output layer, for a simple regression where the predictions are scalars, it will always be one. We will tune the size of the middle layer size. - -There are already several kind of layers available (and you can build your own kind by defining a new `struct` and implementing a few functions. See the [`Nn`](@ref nn_module) module documentation for details). Here we use only _dense_ layers, those found in typycal feed-fordward neural networks. - -For each layer, on top of its size (in "neurons") we can specify an _activation function_. Here we use the [`relu`](@ref) for the terminal layer (this will guarantee that our predictions are always positive) and `identity` for the hidden layer. Again, consult the `Nn` module documentation for other activation layers already defined, or use any function of your choice. - -Initial weight parameters can also be specified if needed. By default [`DenseLayer`](@ref) use the so-called _Xavier initialisation_. - -Let's hence build our candidate neural network structures, choosing between 5 and 10 nodes in the hidden layers: - -```text -candidate_structures = [ - [DenseLayer(D,k,f=relu,df=drelu,rng=copy(AFIXEDRNG)), # Activation function is ReLU, it's derivative is drelu - DenseLayer(k,k,f=identity,df=identity,rng=copy(AFIXEDRNG)), # This is the hidden layer we vant to test various sizes - DenseLayer(k,1,f=relu,df=didentity,rng=copy(AFIXEDRNG))] for k in 5:2:10] -``` - -Note that specify the derivatives of the activation functions (and of the loss function that we'll see in a moment) it totally optional, as without them BetaML will use [`Zygote.jl`](https://github.com/FluxML/Zygote.jl for automatic differentiation. - -We do also set a few other parameters as "turnable": the number of "epochs" to train the model (the number of iterations trough the whole dataset), the sample size at each batch and the optimisation algorithm to use. -Several optimisation algorithms are indeed available, and each accepts different parameters, like the _learning rate_ for the Stochastic Gradient Descent algorithm ([`SGD`](@ref), used by default) or the exponential decay rates for the moments estimates for the [`ADAM`](@ref) algorithm (that we use here, with the default parameters). - -The hyperparameter ranges will then look as follow: - -```text -hpranges = Dict("layers" => candidate_structures, - "epochs" => rand(copy(AFIXEDRNG),DiscreteUniform(50,100),3), # 3 values sampled at random between 50 and 100 - "batch_size" => [4,8,16], - "opt_alg" => [SGD(λ=2),SGD(λ=1),SGD(λ=3),ADAM(λ=0.5),ADAM(λ=1),ADAM(λ=0.25)]) -``` - -Finally we can build "neural network" [`NeuralNetworkEstimator`](@ref) model where we "chain" the layers together and we assign a final loss function (again, you can provide your own loss function, if those available in BetaML don't suit your needs): - -```text -nnm = NeuralNetworkEstimator(loss=squared_cost, descr="Bike sharing regression model", tunemethod=SuccessiveHalvingSearch(hpranges = hpranges), autotune=true,rng=copy(AFIXEDRNG)) # Build the NN model and use the squared cost (aka MSE) as error function by default -``` - -We can now fit and autotune the model: - -```text -ŷtrain_scaled = fit!(nnm,xtrain_scaled,ytrain_scaled) -``` - -The model training is one order of magnitude slower than random forests, altought the memory requirement is approximatly the same. - -To obtain the neural network predictions we apply the function `predict` to the feature matrix X for which we want to generate previsions, and then we rescale y. -Normally we would apply here the `inverse_predict` function, but as we simple divided by 1000, we multiply ŷ by the same amount: - -```text -ŷtrain = ŷtrain_scaled .* 1000 -ŷtest = predict(nnm,xtest_scaled) .* 1000 -``` - -```text -(rme_train, rme_test) = relative_mean_error.([ŷtrain,ŷtest],[ytrain,ytest]) -push!(results,["NN",rme_train,rme_test]); -nothing #hide -``` - -The error is much lower. Let's plot our predictions: - -Again, we can start by plotting the estimated vs the observed value: - -```text -scatter(ytrain,ŷtrain,xlabel="daily rides",ylabel="est. daily rides",label=nothing,title="Est vs. obs in training period (NN)") -``` - -```text -scatter(ytest,ŷtest,xlabel="daily rides",ylabel="est. daily rides",label=nothing,title="Est vs. obs in testing period (NN)") -``` - -We now plot across the time dimension, first plotting the whole period (2 years): - -```text -ŷtrainfull = vcat(ŷtrain,fill(missing,ntest)) -ŷtestfull = vcat(fill(missing,ntrain), ŷtest) -plot(data[:,:dteday],[data[:,:cnt] ŷtrainfull ŷtestfull], label=["obs" "train" "test"], legend=:topleft, ylabel="daily rides", title="Daily bike sharing demand observed/estimated across the\n whole 2-years period (NN)") -``` - -...and then focusing on the testing data - -```text -stc = 620 -endc = size(x,1) -plot(data[stc:endc,:dteday],[data[stc:endc,:cnt] ŷtestfull[stc:endc]], label=["obs" "val" "test"], legend=:bottomleft, ylabel="Daily rides", title="Focus on the testing period (NN)") -``` - -### Comparison with Flux.jl - -We now apply the same Neural Network model using the [Flux](https://fluxml.ai/) framework, a dedicated neural network library, reusing the optimal parameters that we did learn from tuning `NeuralNetworkEstimator`: - -```text -hp_opt = hyperparameters(nnm) -opt_size = size(hp_opt.layers[1])[2][1] -opt_batch_size = hp_opt.batch_size -opt_epochs = hp_opt.epochs -``` - -We fix the default random number generator so that the Flux example gives a reproducible output - -```text -Random.seed!(seed) -``` - -We define the Flux neural network model and load it with data... - -```text -l1 = Flux.Dense(D,opt_size,Flux.relu) -l2 = Flux.Dense(opt_size,opt_size,identity) -l3 = Flux.Dense(opt_size,1,Flux.relu) -Flux_nn = Flux.Chain(l1,l2,l3) -fluxloss(x, y) = Flux.mse(Flux_nn(x), y) -ps = Flux.params(Flux_nn) -nndata = Flux.Data.DataLoader((xtrain_scaled', ytrain_scaled'), batchsize=opt_batch_size,shuffle=true) -``` - -We do the training of the Flux model... - -```text -[Flux.train!(fluxloss, ps, nndata, Flux.ADAM(0.001, (0.9, 0.8))) for i in 1:opt_epochs] -``` - -We obtain the predicitons... - -```text -ŷtrainf = @pipe Flux_nn(xtrain_scaled')' .* 1000; -ŷtestf = @pipe Flux_nn(xtest_scaled')' .* 1000; -nothing #hide -``` - -..and we compute the mean relative errors.. - -```text -(rme_train, rme_test) = relative_mean_error.([ŷtrainf,ŷtestf],[ytrain,ytest]) -push!(results,["NN (Flux.jl)",rme_train,rme_test]); -nothing #hide -``` - -.. finding an error not significantly different than the one obtained from BetaML.Nn. - -Plots: - -```text -scatter(ytrain,ŷtrainf,xlabel="daily rides",ylabel="est. daily rides",label=nothing,title="Est vs. obs in training period (Flux.NN)") -``` - -```text -scatter(ytest,ŷtestf,xlabel="daily rides",ylabel="est. daily rides",label=nothing,title="Est vs. obs in testing period (Flux.NN)") -``` - -```text -ŷtrainfullf = vcat(ŷtrainf,fill(missing,ntest)) -ŷtestfullf = vcat(fill(missing,ntrain), ŷtestf) -plot(data[:,:dteday],[data[:,:cnt] ŷtrainfullf ŷtestfullf], label=["obs" "train" "test"], legend=:topleft, ylabel="daily rides", title="Daily bike sharing demand observed/estimated across the\n whole 2-years period (Flux.NN)") -``` - -```text -stc = 620 -endc = size(x,1) -plot(data[stc:endc,:dteday],[data[stc:endc,:cnt] ŷtestfullf[stc:endc]], label=["obs" "val" "test"], legend=:bottomleft, ylabel="Daily rides", title="Focus on the testing period (Flux.NN)") -``` - -### Conclusions of Neural Network models - -If we strive for the most accurate predictions, deep neural networks are usually the best choice. However they are computationally expensive, so with limited resourses we may get better results by fine tuning and running many repetitions of "simpler" decision trees or even random forest models than a large naural network with insufficient hyper-parameter tuning. -Also, we shoudl consider that decision trees/random forests are much simpler to work with. - -That said, specialised neural network libraries, like Flux, allow to use GPU and specialised hardware letting neural networks to scale with very large datasets. - -Still, for small and medium datasets, BetaML provides simpler yet customisable solutions that are accurate and fast. - -## GMM-based regressors - -BetaML 0.8 introduces new regression algorithms based on Gaussian Mixture Model. -Specifically, there are two variants available, `GaussianMixtureRegressor2` and `GaussianMixtureRegressor`, and this example uses `GaussianMixtureRegressor` -As for neural networks, they work on numerical data only, so we reuse the datasets we prepared for the neural networks. - -As usual we first define the model. - -```text -m = GaussianMixtureRegressor(rng=copy(AFIXEDRNG),verbosity=NONE) -``` - -!!! info - We disabled autotune here, as this code is run by GitHub continuous_integration servers on each code update, and GitHub servers seem to have some strange problem with it, taking almost 4 hours instead of a few seconds on my machine. - -We then fit the model to the training data.. - -```text -ŷtrainGMM_unscaled = fit!(m,xtrain_scaled,ytrain_scaled) -``` - -And we predict... - -```text -ŷtrainGMM = ŷtrainGMM_unscaled .* 1000; -ŷtestGMM = predict(m,xtest_scaled) .* 1000; - -(rme_train, rme_test) = relative_mean_error.([ŷtrainGMM,ŷtestGMM],[ytrain,ytest]) -push!(results,["GMM",rme_train,rme_test]); -nothing #hide -``` - -## Summary - -This is the summary of the results (train and test relative mean error) we had trying to predict the daily bike sharing demand, given weather and calendar information: - -```text -println(results) -``` - -You may ask how stable are these results? How much do they depend from the specific RNG seed ? We re-evaluated a couple of times the whole script but changing random seeds (to `1000` and `10000`): - -| Model | Train rme1 | Test rme1 | Train rme2 | Test rme2 | Train rme3 | Test rme3 | -|:-------------------- |:----------:|:---------:|:----------:|:---------:|:----------:|:---------:| -| DT | 0.1366960 | 0.154720 | 0.0233044 | 0.249329 | 0.0621571 | 0.161657 | -| RF | 0.0421267 | 0.180186 | 0.0535776 | 0.136920 | 0.0386144 | 0.141606 | -| RF (DecisionTree.jl) | 0.0230439 | 0.235823 | 0.0801040 | 0.243822 | 0.0168764 | 0.219011 | -| NN | 0.1604000 | 0.169952 | 0.1091330 | 0.121496 | 0.1481440 | 0.150458 | -| NN (Flux.jl) | 0.0931161 | 0.166228 | 0.0920796 | 0.167047 | 0.0907810 | 0.122469 | -| GaussianMixtureRegressor* | 0.1432800 | 0.293891 | 0.1380340 | 0.295470 | 0.1477570 | 0.284567 | - -* GMM is a deterministic model, the variations are due to the different random sampling in choosing the best hyperparameters - -Neural networks can be more precise than random forests models, but are more computationally expensive (and tricky to set up). When we compare BetaML with the algorithm-specific leading packages, we found similar results in terms of accuracy, but often the leading packages are better optimised and run more efficiently (but sometimes at the cost of being less versatile). -GMM_based regressors are very computationally cheap and a good compromise if accuracy can be traded off for performances. - -[View this file on Github](betaml_tutorial_regression_sharingBikes.jl). - ---- - -*This page was generated using [Literate.jl](https://github.com/fredrikekre/Literate.jl).* - diff --git a/loss_by_var.png b/loss_by_var.png deleted file mode 100644 index de891ea0b4b12fb56f20496c54923d967be4fe14..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25500 zcmag`1yq%7_CF3EKxrkUL6DFxK@_AE5Rj7Y6p&IIEJo8=e`mObznKk1=Ip@Bw`?~i2)II@<3R2gwDXhRDgIXT zfy|sNRhiMXDYFF@@$vBu@fMubYNMZC`+tsmO`c<6SqMMy&U_n*N_VS-x2x&J-}CcW zPoJhHZwK;?uD`payIs8R=Z5~o)e1{Bm9V2A5V35j#W&zGxEYDW;D4={eGmx4Uy7J0 zh^8;8_^1ePEpgf_2t=u$7$ySo{F*O50?|c*O$)!s_s=hov?(-w*8d_Px=mt6$pgZC+(7`8YB>z}ujmQHHOx zDxj6t=PyAg%p`ticf?u@W#3|=cVc4VR^Omy`D4?I9r;{*5wG*pu{^wH9Bpzz0&i`` zb5>JE<;sBLu6(YKXS4!I9izuAk}H%KC!sk#FYLFnJ^c_DPhXwXmZzP@aOxc&r;_<8 zF%vDSVFpOwD$Y=#4^F)LkYnz*H_wgz2|m@hy+eL2Z56?K=ZrGV9ZFdz&LIW-l-OGW zHGW=cImBWJW~tJG!7ps@x8Hx6=H^Fyw=Ko>Ca>~}_MB`sI<;X`R=|G0jZ)7L<-KXRbF<6nlzhsbjLO@%{4@S!ooi`Iinssx__%h(3k!>* z9*^I4c6L3ZqUG>TnCiz?BX{m=F_iF96?bJdB&=;B4a&f6oH zHm4e=8az(qKiwk^{kt--)bs{zG+9MinOfLwes0bUy>1C#yU#GbptSfH)Gv^Uy!R$MWkESZk9LKH>t);O{ zUZ=Uy+;X@73QW~H_jGp3W+1sbu2h?i&KpC8VJ!a9C`hk@s|w@g4qrMr{=@#Dwq zHy_~Md@$J8S8UoDO?*WuZ-{+k2Aw%?&en1uwB=9*@1xY?6Gj9?S3prEE!Rs;a7*nwr~R@mjc#iF`+uF=Mz4M4orWzI}@-D%x07RAe!bN!+-wxcIBu zVQFO`i%Y+*YW7w|MFj~72|vRXX8BtTC^5s2e5wpl+_L{X;&AKrcO>p1^lW^li1%G) zm?H6xv1E(iwfrw%SYunb<&2CLG;UiB^N9%x*1{9xFlf+ajEJI2<~93}m9-Rm$2d5O z4AD3t@W@PLj{AHPE0W~9kvoEOs6b3w0`A&+v~15moCN8~)j1ZArIvYA`0#Pes@U`55svUI`cGzIgEhml(N05!2B+4utmArInNq zD?U)QVN4TZ(|VIX6cs~ESWf)pK^VF$OJSx^{`*6@^+D!5b=%rZIt0zoeo77X@dm{U zYJxCrHDA4)i}ws3Px@nqE@u9TVqpHeKwI})8_j6bC%1^m1o4uL z$R%&#e}tNrFU_gKLVVzuxEHtf{5FFZQ&2P2X6NyS%m~*2Rf67Qy83fbBQiyi@+P+{PAkiG;#EfyB%0Do0 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 diff --git a/plot2.png b/plot2.png deleted file mode 100644 index d3bc4ed40cec48190f818135c7cfa660e68c50f3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7090 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 diff --git a/plot3.png b/plot3.png deleted file mode 100644 index ae99526dd90f5654db91ea459a7870d00c9d5baa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7877 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 Q0*qWx(NNA(x_0Zo0LCk1c>n+a diff --git a/plot4.png b/plot4.png deleted file mode 100644 index c840da1e0e3a565d8aa217844f11bae642a95f19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7595 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sobol_by_var.png b/sobol_by_var.png deleted file mode 100644 index 4b2c0f57099ecc244b09051e8b8f6293750d0bcf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17679 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 diff --git a/test/temp_test.jl b/test/temp_test.jl deleted file mode 100644 index e21531e1..00000000 --- a/test/temp_test.jl +++ /dev/null @@ -1,154 +0,0 @@ -using Test -using DelimitedFiles, LinearAlgebra, Statistics #, MLDatasets - -#using StableRNGs -#rng = StableRNG(123) -using BetaML - -import BetaML.Nn: buildNetwork, forward, loss, backward, train!, get_nparams, _get_n_layers_weights -import BetaML.Nn: ConvLayer, ReshaperLayer, _zComp! -TESTRNG = FIXEDRNG # This could change... -#TESTRNG = StableRNG(123) - - - -x = reshape(1:100*3*3*2,100,3*3*2) ./ 100 -#x = rand(100,18) -y = [norm(r[1:9])+2*norm(r[10:18],2) for r in eachrow(x) ] -(N,D) = size(x) -l1 = ReshaperLayer((D,1),(3,3,2)) -l2 = ConvLayer((3,3),(2,2),2,3,rng=copy(TESTRNG),f=identity) -l3 = ConvLayer(size(l2)[2],(2,2),8,rng=copy(TESTRNG), f=identity) -l4 = ReshaperLayer(size(l3)[2]) -l5 = DenseLayer(size(l4)[2][1],1,f=relu, rng=copy(TESTRNG)) -layers = [l1,l2,l3,l4,l5] -mynn = buildNetwork(layers,squared_cost,name="Regression with a convolutional layer") -preprocess!(mynn) -predict(mynn,x[1,:]') - -l1out = forward(l1,x[1,:]) -l2out = forward(l2,l1out) -@btime forward(l2,l1out) - -@btime forward($l2,$l1out) - -_, output_size = size(l2) -z = zeros(output_size) -@btime _zComp!($z,$l1out,$l2.weight,$l2.bias,$l2.y_ids,$l2.x_ids,$l2.w_ids,$l2.usebias) -@btime _zComp!($z,$l2,$l1out) - -@btime zeros(size($l2)[2]) - - -train!(mynn,x,y,epochs=60,verbosity=NONE,rng=copy(TESTRNG)) -ŷ = predict(mynn,x) -rmeTrain = relative_mean_error(y,ŷ,normrec=false) -@test rmeTrain < 0.01 - -using BenchmarkTools -@btime train!($mynn,$x,$y,epochs=60,verbosity=NONE,rng=copy(TESTRNG)) - -# original (already int64 and no return value): 2.988 s (117156427 allocations: 3.13 GiB) -# vith vector instead of array: 1.111 s (44415127 allocations: 772.20 MiB) -# with _dedxComp!: 777.724 ms (22815127 allocations: 442.61 MiB) -# with _dedwComp!: 410.060 ms (1215127 allocations: 113.02 MiB) -# with all inbounds: 256.673 ms (1215127 allocations: 113.02 MiB) -y_id = [3,2,1,2] -x_id = [1,2,2,1] -w_id = [2,3,2,1] - -x = [1.5,2.5] -w = [2.0,3.0,4.0] - - -function foo!(y,x,w,y_id,x_id,w_id) - for i in 1:length(y_id) - y[y_id[i]] += x[x_id[i]] * w[w_id[i]] - end - return y -end - -foo(x,w,y_id,x_id,w_id) - - -# --------------------------------------------------- -y_id = [(3,1),(2,2),(2,2),(2,1)] -x_id = [(1,2),(2,1),(1,1),(2,2)] -w_id = [(2,2),(3,2),(2,1),(1,1)] - -x = [1.5 2.5; 2.0 1.0] -w = [2.0 3.0 4.0; 1.0 1.5 2.5; 0.5 1.0 0.5] - -y = zeros(3,2) - -function foo!(y,x,w,y_id,x_id,w_id) - for i in 1:length(y_id) - y[y_id[i][1],y_id[i][2]] += x[x_id[i][1],x_id[i][2]] * w[w_id[i][1],w_id[i][2]] - end - return y -end - - -foo!(y,x,w,y_id,x_id,w_id) -@btime foo!($zeros(3,2),$x,$w,$y_id,$x_id,$w_id) - - - -# ------------------------------------------------------------------------------ - -using StaticArrays, BenchmarkTools - -mutable struct ConvLayerTest4{Int32} - x_ids::Vector{SVector{3,Int32}} - y_ids::Vector{SVector{3,Int32}} - w_ids::Vector{SVector{4,Int32}} - w::Array{Float64,4} - somethingelse -end - -# Data generation for the MWE... -x = rand(64,64,3) -y = zeros(32,32,5) -w = rand(4,4,3,5) -N = 3000 -x_ids = [SVector{3,Int64}([rand(1:id) for id in size(x)]...) for n in 1:N] -y_ids = [SVector{3,Int64}([rand(1:id) for id in size(y)]...) for n in 1:N] -w_ids = [SVector{4,Int64}([rand(1:id) for id in size(w)]...) for n in 1:N] -layer = ConvLayerTest4(x_ids, y_ids, w_ids, w, "foo") - -function compute!(y,l,x) - for i in 1:length(l.y_ids) - y[l.y_ids[i][1],l.y_ids[i][2],l.y_ids[i][3]] += - x[l.x_ids[i][1],l.x_ids[i][2],l.x_ids[i][3]] * - l.w[l.w_ids[i][1],l.w_ids[i][2],l.w_ids[i][3],l.w_ids[i][4]] - end - return nothing -end - -function compute!(y,x,w,y_ids,x_ids,w_ids) - for i in 1:length(y_ids) - y[y_ids[i][1],y_ids[i][2],y_ids[i][3]] += - x[x_ids[i][1],x_ids[i][2],x_ids[i][3]] * - w[w_ids[i][1],w_ids[i][2],w_ids[i][3],w_ids[i][4]] - end - return nothing -end - -# The computation that I care... -@btime compute!($y,$layer,$x) -@btime compute!($y,$x,$w,$y_ids,$x_ids,$w_ids) - -a = compute!(y,layer,x) -y = zeros(32,32,5) -b = compute!(y,x,w,y_ids,x_ids,w_ids) -a == b - - - -# ------------------------------------------------------------------------------ - -foo(x,::Val{true}) = println("t $x") - -foo(x,::Val{false}) = println("f $x") - -foo(10,Val(true)) \ No newline at end of file