From a4ef7395fea15e20e9a1905d858699aed5c74017 Mon Sep 17 00:00:00 2001 From: Pat Alt <55311242+pat-alt@users.noreply.github.com> Date: Wed, 19 Apr 2023 17:15:27 +0200 Subject: [PATCH] batchnorm sorted --- Manifest.toml | 2 +- docs/Manifest.toml | 7 ++++ docs/Project.toml | 1 + notebooks/Manifest.toml | 2 +- notebooks/mnist.qmd | 85 ++++++++++++++++++++++++++-------------- src/model.jl | 1 + src/penalties.jl | 32 +++++++-------- www/cce_mnist.png | Bin 7586 -> 15731 bytes 8 files changed, 82 insertions(+), 48 deletions(-) create mode 100644 docs/Manifest.toml create mode 100644 docs/Project.toml diff --git a/Manifest.toml b/Manifest.toml index d16a0962..4531e744 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -304,7 +304,7 @@ uuid = "d38c429a-6771-53c6-b99e-75d170b6e991" version = "0.6.2" [[deps.CounterfactualExplanations]] -deps = ["CSV", "CUDA", "CategoricalArrays", "DataFrames", "Flux", "LaplaceRedux", "LazyArtifacts", "LinearAlgebra", "MLDatasets", "MLJBase", "MLJModels", "MLUtils", "MultivariateStats", "NearestNeighborModels", "Parameters", "Plots", "ProgressMeter", "Random", "Serialization", "SliceMap", "SnoopPrecompile", "Statistics", "StatsBase", "Tables", "UMAP"] +deps = ["CSV", "CUDA", "CategoricalArrays", "ChainRulesCore", "DataFrames", "Flux", "LaplaceRedux", "LazyArtifacts", "LinearAlgebra", "MLDatasets", "MLJBase", "MLJModels", "MLUtils", "MultivariateStats", "NearestNeighborModels", "Parameters", "Plots", "ProgressMeter", "Random", "Serialization", "SliceMap", "SnoopPrecompile", "Statistics", "StatsBase", "Tables", "UMAP"] path = "../CounterfactualExplanations.jl" uuid = "2f13d31b-18db-44c1-bc43-ebaf2cff0be0" version = "0.1.9" diff --git a/docs/Manifest.toml b/docs/Manifest.toml new file mode 100644 index 00000000..bc62edc1 --- /dev/null +++ b/docs/Manifest.toml @@ -0,0 +1,7 @@ +# This file is machine-generated - editing it directly is not advised + +julia_version = "1.8.5" +manifest_format = "2.0" +project_hash = "da39a3ee5e6b4b0d3255bfef95601890afd80709" + +[deps] diff --git a/docs/Project.toml b/docs/Project.toml new file mode 100644 index 00000000..81648c0b --- /dev/null +++ b/docs/Project.toml @@ -0,0 +1 @@ +[deps] diff --git a/notebooks/Manifest.toml b/notebooks/Manifest.toml index 6dd7ba4c..a1c318aa 100644 --- a/notebooks/Manifest.toml +++ b/notebooks/Manifest.toml @@ -383,7 +383,7 @@ uuid = "150eb455-5306-5404-9cee-2592286d6298" version = "0.6.2" [[deps.CounterfactualExplanations]] -deps = ["CSV", "CUDA", "CategoricalArrays", "DataFrames", "Flux", "LaplaceRedux", "LazyArtifacts", "LinearAlgebra", "MLDatasets", "MLJBase", "MLJModels", "MLUtils", "MultivariateStats", "NearestNeighborModels", "Parameters", "Plots", "ProgressMeter", "Random", "Serialization", "SliceMap", "SnoopPrecompile", "Statistics", "StatsBase", "Tables", "UMAP"] +deps = ["CSV", "CUDA", "CategoricalArrays", "ChainRulesCore", "DataFrames", "Flux", "LaplaceRedux", "LazyArtifacts", "LinearAlgebra", "MLDatasets", "MLJBase", "MLJModels", "MLUtils", "MultivariateStats", "NearestNeighborModels", "Parameters", "Plots", "ProgressMeter", "Random", "Serialization", "SliceMap", "SnoopPrecompile", "Statistics", "StatsBase", "Tables", "UMAP"] path = "../../CounterfactualExplanations.jl" uuid = "2f13d31b-18db-44c1-bc43-ebaf2cff0be0" version = "0.1.9" diff --git a/notebooks/mnist.qmd b/notebooks/mnist.qmd index 0716c8bc..07a6fec5 100644 --- a/notebooks/mnist.qmd +++ b/notebooks/mnist.qmd @@ -22,12 +22,14 @@ First, let's create a couple of image classifier architectures: ```{julia} # Model parameters: epochs = 100 -batch_size = minimum([Int(round(n_obs/10)), 100]) -n_hidden = 32 +batch_size = minimum([Int(round(n_obs/10)), 128]) +n_hidden = 200 activation = Flux.relu builder = MLJFlux.@builder Flux.Chain( Dense(n_in, n_hidden), BatchNorm(n_hidden, activation), + Dense(n_hidden, n_hidden), + BatchNorm(n_hidden, activation), Dense(n_hidden, n_out), ) # builder = MLJFlux.Short(n_hidden=n_hidden, dropout=0.2, σ=activation) @@ -51,7 +53,11 @@ mlp = NeuralNetworkClassifier( # Joint Energy Model: ð’Ÿx = Uniform(0,1) ð’Ÿy = Categorical(ones(output_dim) ./ output_dim) -sampler = ConditionalSampler(ð’Ÿx, ð’Ÿy, input_size=(input_dim,), batch_size=batch_size) +sampler = ConditionalSampler( + ð’Ÿx, ð’Ÿy, + input_size=(input_dim,), + batch_size=10 +) jem = JointEnergyClassifier( sampler; builder=builder, @@ -73,31 +79,33 @@ mlp_ens = EnsembleModel(model=mlp, n=5) ```{julia} cov = .90 -conf_model = conformal_model(jem; method=:simple_inductive, coverage=cov) +conf_model = conformal_model(mlp; method=:adaptive_inductive, coverage=cov) mach = machine(conf_model, X, labels) fit!(mach) M = CCE.ConformalModel(mach.model, mach.fitresult) ``` ```{julia} -jem = mach.model.model.jem -n_iter = 100 -_w = 1500 -plts = [] -neach = 10 -for i in 1:10 - x = jem.sampler(jem.chain, jem.sampling_rule; niter=n_iter, n_samples=neach, y=i) - plts_i = [] - for j in 1:size(x, 2) - xj = x[:,j] - xj = reshape(xj, (n_digits, n_digits)) - plts_i = [plts_i..., Plots.heatmap(rotl90(xj), axis=nothing, cb=false)] +if mach.model.model isa JointEnergyModels.JointEnergyClassifier + jem = mach.model.model.jem + n_iter = 100 + _w = 1500 + plts = [] + neach = 10 + for i in 1:10 + x = jem.sampler(jem.chain, jem.sampling_rule; niter=n_iter, n_samples=neach, y=i) + plts_i = [] + for j in 1:size(x, 2) + xj = x[:,j] + xj = reshape(xj, (n_digits, n_digits)) + plts_i = [plts_i..., Plots.heatmap(rotl90(xj), axis=nothing, cb=false)] + end + plt = Plots.plot(plts_i..., size=(_w,0.10*_w), layout=(1,10)) + plts = [plts..., plt] end - plt = Plots.plot(plts_i..., size=(_w,0.10*_w), layout=(1,10)) - plts = [plts..., plt] + plt = Plots.plot(plts..., size=(_w,_w), layout=(10,1)) + display(plt) end -plt = Plots.plot(plts..., size=(_w,_w), layout=(10,1)) -display(plt) ``` ```{julia} @@ -110,12 +118,12 @@ println("F1 score (test): $(round(f1,digits=3))") Random.seed!(1234) # Set up search: -factual_label = 9 +factual_label = 4 x = reshape(counterfactual_data.X[:,rand(findall(predict_label(M, counterfactual_data).==factual_label))],input_dim,1) -target = 4 +target = 9 factual = predict_label(M, counterfactual_data, x)[1] γ = 0.5 -T = 1 +T = 100 # Generate counterfactual using generic generator: generator = GenericGenerator() @@ -125,11 +133,14 @@ ce_wachter = generate_counterfactual( initialization=:identity, ) +# CCE: +λ=[0.0,1.0] +temp=0.5 + # Generate counterfactual using CCE generator: generator = CCEGenerator( - λ=[0.0,1.0], - temp=0.5, - # opt=CounterfactualExplanations.Generators.JSMADescent(η=1.0), + λ=λ, + temp=temp, ) ce_conformal = generate_counterfactual( x, target, counterfactual_data, M, generator; @@ -138,6 +149,19 @@ ce_conformal = generate_counterfactual( converge_when=:generator_conditions, ) +# Generate counterfactual using CCE generator: +generator = CCEGenerator( + λ=λ, + temp=temp, + opt=CounterfactualExplanations.Generators.JSMADescent(η=1.0), +) +ce_conformal_jsma = generate_counterfactual( + x, target, counterfactual_data, M, generator; + decision_threshold=γ, max_iter=T, + initialization=:identity, + converge_when=:generator_conditions, +) + # Plot: p1 = Plots.plot( convert2image(MNIST, reshape(x,28,28)), @@ -147,12 +171,13 @@ p1 = Plots.plot( ) plts = [p1] -ces = zip([ce_wachter,ce_conformal]) +ces = zip([ce_wachter,ce_conformal,ce_conformal_jsma]) +_names = ["Wachter", "CCE", "CCE-JSMA"] counterfactuals = reduce((x,y)->cat(x,y,dims=3),map(ce -> CounterfactualExplanations.counterfactual(ce[1]), ces)) phat = reduce((x,y) -> cat(x,y,dims=3), map(ce -> target_probs(ce[1]), ces)) -for x in zip(eachslice(counterfactuals; dims=3), eachslice(phat; dims=3)) - ce, _phat = (x[1],x[2]) - _title = "p(y=$(target)|x′)=$(round(_phat[1]; digits=3))" +for x in zip(eachslice(counterfactuals; dims=3), _names, eachslice(phat; dims=3)) + ce, _name, _phat = (x[1],x[2],x[3]) + _title = "$_name (pÌ‚=$(round(_phat[1]; digits=3)))" plt = Plots.plot( convert2image(MNIST, reshape(ce,28,28)), axis=nothing, diff --git a/src/model.jl b/src/model.jl index c724867b..cc7cde3c 100644 --- a/src/model.jl +++ b/src/model.jl @@ -52,6 +52,7 @@ function ConformalModel(model, fitresult=nothing; likelihood::Union{Nothing,Symb end # Construct model: + testmode!(fitresult[1]) M = ConformalModel(model, fitresult, likelihood) return M end diff --git a/src/penalties.jl b/src/penalties.jl index 09d2c2a2..30ae653c 100644 --- a/src/penalties.jl +++ b/src/penalties.jl @@ -3,21 +3,21 @@ using LinearAlgebra: norm using Statistics: mean """ - set_size_penalty(counterfactual_explanation::AbstractCounterfactualExplanation) + set_size_penalty(ce::AbstractCounterfactualExplanation) Penalty for smooth conformal set size. """ function set_size_penalty( - counterfactual_explanation::AbstractCounterfactualExplanation; + ce::AbstractCounterfactualExplanation; κ::Real=0.0, temp::Real=0.05, agg=mean ) - conf_model = counterfactual_explanation.M.model - fitresult = counterfactual_explanation.M.fitresult - X = CounterfactualExplanations.decode_state(counterfactual_explanation) - loss = map(eachslice(X, dims=3)) do x - x = Matrix(x) - if target_probs(counterfactual_explanation, x)[1] >= 0.5 + conf_model = ce.M.model + fitresult = ce.M.fitresult + X = CounterfactualExplanations.decode_state(ce) + loss = map(eachslice(X, dims=ndims(X))) do x + x = ndims(x) == 1 ? x[:,:] : x + if target_probs(ce, x)[1] >= 0.5 l = ConformalPrediction.smooth_size_loss( conf_model, fitresult, x'; κ=κ, @@ -35,19 +35,19 @@ function set_size_penalty( end function distance_from_energy( - counterfactual_explanation::AbstractCounterfactualExplanation; + ce::AbstractCounterfactualExplanation; n::Int=10000, from_buffer=true, agg=mean, kwargs... ) conditional_samples = [] ignore_derivatives() do - _dict = counterfactual_explanation.params + _dict = ce.params if !(:energy_sampler ∈ collect(keys(_dict))) - _dict[:energy_sampler] = CCE.EnergySampler(counterfactual_explanation; kwargs...) + _dict[:energy_sampler] = CCE.EnergySampler(ce; kwargs...) end sampler = _dict[:energy_sampler] push!(conditional_samples, rand(sampler, n; from_buffer=from_buffer)) end - x′ = CounterfactualExplanations.counterfactual(counterfactual_explanation) + x′ = CounterfactualExplanations.counterfactual(ce) loss = map(eachslice(x′, dims=3)) do x x = Matrix(x) Δ = map(eachcol(conditional_samples[1])) do xsample @@ -62,13 +62,13 @@ function distance_from_energy( end function distance_from_targets( - counterfactual_explanation::AbstractCounterfactualExplanation; + ce::AbstractCounterfactualExplanation; n::Int=10000, agg=mean ) - target_idx = counterfactual_explanation.data.output_encoder.labels .== counterfactual_explanation.target - target_samples = counterfactual_explanation.data.X[:,target_idx] |> + target_idx = ce.data.output_encoder.labels .== ce.target + target_samples = ce.data.X[:,target_idx] |> X -> X[:,rand(1:end,n)] - x′ = CounterfactualExplanations.counterfactual(counterfactual_explanation) + x′ = CounterfactualExplanations.counterfactual(ce) loss = map(eachslice(x′, dims=3)) do x x = Matrix(x) Δ = map(eachcol(target_samples)) do xsample diff --git a/www/cce_mnist.png b/www/cce_mnist.png index ea956a80689729139b58966c11514539991a792a..ff97f8657cd5af3cd5f753e84cb38286614bf6ca 100644 GIT binary patch literal 15731 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 JxAG64{tpR|ka_?B literal 7586 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 -- GitLab