Parallelization
Version 0.1.15
adds support for parallelization through multi-processing. Currently, the only available backend for parallelization is MPI.jl.
Available functions
Parallelization is only available for certain functions. To check if a function is parallelizable, you can use parallelizable
function:
using CounterfactualExplanations.Evaluation: evaluate, benchmark
println(parallelizable(generate_counterfactual))
println(parallelizable(evaluate))
println(parallelizable(predict_label))
true
true
false
In the following, we will generate multiple counterfactuals and evaluate them in parallel:
chosen = rand(findall(predict_label(M, counterfactual_data) .== factual), 1000)
xs = select_factual(counterfactual_data, chosen)
Multi-threading
We first instantiate an ThreadParallelizer
object:
parallelizer = ThreadsParallelizer()
ThreadsParallelizer()
To generate counterfactuals in parallel, we use the parallelize
function:
ces = @with_parallelizer parallelizer begin
generate_counterfactual(
xs,
target,
counterfactual_data,
M,
generator
)
end
Generating counterfactuals ... 0%| | ETA: 0:01:29 (89.14 ms/it)Generating counterfactuals ... 100%|โโโโโโโ| Time: 0:00:01 ( 1.59 ms/it)
1000-element Vector{AbstractCounterfactualExplanation}:
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 6 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 6 steps.
โฎ
CounterfactualExplanation
Convergence: โ
after 9 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 6 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
To evaluate counterfactuals in parallel, we again use the parallelize
function:
@with_parallelizer parallelizer evaluate(ces)
Evaluating counterfactuals ... 0%| | ETA: 0:07:03 ( 0.42 s/it)Evaluating counterfactuals ... 100%|โโโโโโโ| Time: 0:00:00 ( 0.86 ms/it)
1000-element Vector{Any}:
Vector[[1.0], Float32[3.2939816], [0.0]]
Vector[[1.0], Float32[3.019046], [0.0]]
Vector[[1.0], Float32[3.701171], [0.0]]
Vector[[1.0], Float32[2.5611918], [0.0]]
Vector[[1.0], Float32[2.9027307], [0.0]]
Vector[[1.0], Float32[3.7893882], [0.0]]
Vector[[1.0], Float32[3.5026522], [0.0]]
Vector[[1.0], Float32[3.6317568], [0.0]]
Vector[[1.0], Float32[3.084984], [0.0]]
Vector[[1.0], Float32[3.2268934], [0.0]]
Vector[[1.0], Float32[2.834947], [0.0]]
Vector[[1.0], Float32[3.656587], [0.0]]
Vector[[1.0], Float32[2.5985842], [0.0]]
โฎ
Vector[[1.0], Float32[4.067538], [0.0]]
Vector[[1.0], Float32[3.02231], [0.0]]
Vector[[1.0], Float32[2.748292], [0.0]]
Vector[[1.0], Float32[2.9483426], [0.0]]
Vector[[1.0], Float32[3.066149], [0.0]]
Vector[[1.0], Float32[3.6018147], [0.0]]
Vector[[1.0], Float32[3.0138078], [0.0]]
Vector[[1.0], Float32[3.5724509], [0.0]]
Vector[[1.0], Float32[3.117551], [0.0]]
Vector[[1.0], Float32[2.9670508], [0.0]]
Vector[[1.0], Float32[3.4107168], [0.0]]
Vector[[1.0], Float32[3.0252533], [0.0]]
Benchmarks can also be run with parallelization by specifying parallelizer
argument:
# Models:
bmk = benchmark(counterfactual_data; parallelizer = parallelizer)
MPI
To use MPI, you need to have MPI installed on your machine. Running the following code straight from a running Julia session will work if you have MPI installed on your machine, but it will be run on a single process. To execute the code on multiple processes, you need to run it from the command line with mpirun
or mpiexec
. For example, to run a script on 4 processes, you can run the following command from the command line:
mpiexecjl --project -n 4 julia -e 'include("docs/src/srcipts/mpi.jl")'
For more information, see MPI.jl.
We first instantiate an MPIParallelizer
object:
import MPI
MPI.Init()
parallelizer = MPIParallelizer(MPI.COMM_WORLD; threaded=true)
Precompiling MPIExt
โ TaijaParallel โ MPIExt
1 dependency successfully precompiled in 3 seconds. 255 already precompiled.
[ Info: Precompiling MPIExt [48137b38-b316-530b-be8a-261f41e68c23]
โ Warning: Module TaijaParallel with build ID ffffffff-ffff-ffff-0001-2d458926c256 is missing from the cache.
โ This may mean TaijaParallel [bf1c2c22-5e42-4e78-8b6b-92e6c673eeb0] does not support precompilation but is imported by a module that does.
โ @ Base loading.jl:1948
[ Info: Skipping precompilation since __precompile__(false). Importing MPIExt [48137b38-b316-530b-be8a-261f41e68c23].
[ Info: Using `MPI.jl` for multi-processing.
Running on 1 processes.
MPIExt.MPIParallelizer(MPI.Comm(1140850688), 0, 1, nothing, true)
To generate counterfactuals in parallel, we use the parallelize
function:
ces = @with_parallelizer parallelizer begin
generate_counterfactual(
xs,
target,
counterfactual_data,
M,
generator
)
end
Generating counterfactuals ... 9%|โ | ETA: 0:00:01 ( 1.15 ms/it)Generating counterfactuals ... 19%|โโ | ETA: 0:00:01 ( 1.07 ms/it)Generating counterfactuals ... 29%|โโ | ETA: 0:00:01 ( 1.10 ms/it)Generating counterfactuals ... 39%|โโโ | ETA: 0:00:01 ( 1.08 ms/it)Generating counterfactuals ... 49%|โโโโ | ETA: 0:00:01 ( 1.08 ms/it)Generating counterfactuals ... 59%|โโโโโ | ETA: 0:00:00 ( 1.08 ms/it)Generating counterfactuals ... 69%|โโโโโ | ETA: 0:00:00 ( 1.08 ms/it)Generating counterfactuals ... 79%|โโโโโโ | ETA: 0:00:00 ( 1.07 ms/it)Generating counterfactuals ... 89%|โโโโโโโ| ETA: 0:00:00 ( 1.07 ms/it)Generating counterfactuals ... 99%|โโโโโโโ| ETA: 0:00:00 ( 1.06 ms/it)Generating counterfactuals ... 100%|โโโโโโโ| Time: 0:00:01 ( 1.06 ms/it)
1000-element Vector{AbstractCounterfactualExplanation}:
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 6 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 6 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 6 steps.
โฎ
CounterfactualExplanation
Convergence: โ
after 9 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 6 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 8 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
CounterfactualExplanation
Convergence: โ
after 7 steps.
To evaluate counterfactuals in parallel, we again use the parallelize
function:
@with_parallelizer parallelizer evaluate(ces)
1000-element Vector{Any}:
Vector[[1.0], Float32[3.0941274], [0.0]]
Vector[[1.0], Float32[3.0894346], [0.0]]
Vector[[1.0], Float32[3.5737448], [0.0]]
Vector[[1.0], Float32[2.6201036], [0.0]]
Vector[[1.0], Float32[2.8519764], [0.0]]
Vector[[1.0], Float32[3.7762523], [0.0]]
Vector[[1.0], Float32[3.4162796], [0.0]]
Vector[[1.0], Float32[3.6095932], [0.0]]
Vector[[1.0], Float32[3.1347957], [0.0]]
Vector[[1.0], Float32[3.0313473], [0.0]]
Vector[[1.0], Float32[2.7612567], [0.0]]
Vector[[1.0], Float32[3.6191392], [0.0]]
Vector[[1.0], Float32[2.610616], [0.0]]
โฎ
Vector[[1.0], Float32[4.0844703], [0.0]]
Vector[[1.0], Float32[3.0119], [0.0]]
Vector[[1.0], Float32[2.4461186], [0.0]]
Vector[[1.0], Float32[3.071967], [0.0]]
Vector[[1.0], Float32[3.132917], [0.0]]
Vector[[1.0], Float32[3.5403214], [0.0]]
Vector[[1.0], Float32[3.0588162], [0.0]]
Vector[[1.0], Float32[3.5600657], [0.0]]
Vector[[1.0], Float32[3.2205954], [0.0]]
Vector[[1.0], Float32[2.896302], [0.0]]
Vector[[1.0], Float32[3.2603998], [0.0]]
Vector[[1.0], Float32[3.1369917], [0.0]]
Note that parallelizable processes can be supplied as input to the macro either as a block or directly as an expression.
Benchmarks can also be run with parallelization by specifying parallelizer
argument:
# Models:
bmk = benchmark(counterfactual_data; parallelizer = parallelizer)
The following code snippet shows a complete example script that uses MPI for running a benchmark in parallel:
using CounterfactualExplanations
using CounterfactualExplanations.Evaluation: benchmark
using CounterfactualExplanations.Models
import MPI
MPI.Init()
data = TaijaData.load_linearly_separable()
counterfactual_data = DataPreprocessing.CounterfactualData(data...)
M = fit_model(counterfactual_data, :Linear)
factual = 1
target = 2
chosen = rand(findall(predict_label(M, counterfactual_data) .== factual), 100)
xs = select_factual(counterfactual_data, chosen)
generator = GenericGenerator()
parallelizer = MPIParallelizer(MPI.COMM_WORLD)
bmk = benchmark(counterfactual_data; parallelizer=parallelizer)
MPI.Finalize()
The file can be executed from the command line as follows:
mpiexecjl --project -n 4 julia -e 'include("docs/src/srcipts/mpi.jl")'