From 30814a7ee860b978d6ec3be14da499a961f8d824 Mon Sep 17 00:00:00 2001 From: adrhill Date: Tue, 3 Dec 2024 17:22:23 +0100 Subject: [PATCH 1/3] Constant memory input augmentations --- src/input_augmentation.jl | 194 +++++++++++--------------------------- 1 file changed, 55 insertions(+), 139 deletions(-) diff --git a/src/input_augmentation.jl b/src/input_augmentation.jl index 02a6675..026b441 100644 --- a/src/input_augmentation.jl +++ b/src/input_augmentation.jl @@ -8,80 +8,6 @@ struct AugmentationSelector{I} <: AbstractOutputSelector end (s::AugmentationSelector)(out) = s.indices -""" - augment_batch_dim(input, n) - -Repeat each sample in input batch n-times along batch dimension. -This turns arrays of size `(..., B)` into arrays of size `(..., B*n)`. - -## Example -```julia-repl -julia> A = [1 2; 3 4] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> augment_batch_dim(A, 3) -2×6 Matrix{Int64}: - 1 1 1 2 2 2 - 3 3 3 4 4 4 -``` -""" -function augment_batch_dim(input::AbstractArray{T,N}, n) where {T,N} - return repeat(input; inner=(ntuple(Returns(1), N - 1)..., n)) -end - -""" - reduce_augmentation(augmented_input, n) - -Reduce augmented input batch by averaging the explanation for each augmented sample. -""" -function reduce_augmentation(input::AbstractArray{T,N}, n) where {T<:AbstractFloat,N} - # Allocate output array - in_size = size(input) - in_size[end] % n != 0 && - throw(ArgumentError("Can't reduce augmented batch size of $(in_size[end]) by $n")) - out_size = (in_size[1:(end - 1)]..., div(in_size[end], n)) - out = similar(input, eltype(input), out_size) - - axs = axes(input, N) - colons = ntuple(Returns(:), N - 1) - for (i, ax) in enumerate(first(axs):n:last(axs)) - view(out, colons..., i) .= - dropdims(sum(view(input, colons..., ax:(ax + n - 1)); dims=N); dims=N) / n - end - return out -end - -""" - augment_indices(indices, n) - -Strip batch indices and return indices for batch augmented by n samples. - -## Example -```julia-repl -julia> inds = [CartesianIndex(5,1), CartesianIndex(3,2)] -2-element Vector{CartesianIndex{2}}: - CartesianIndex(5, 1) - CartesianIndex(3, 2) - -julia> augment_indices(inds, 3) -6-element Vector{CartesianIndex{2}}: - CartesianIndex(5, 1) - CartesianIndex(5, 2) - CartesianIndex(5, 3) - CartesianIndex(3, 4) - CartesianIndex(3, 5) - CartesianIndex(3, 6) -``` -""" -function augment_indices(inds::Vector{CartesianIndex{N}}, n) where {N} - indices_wo_batch = [i.I[1:(end - 1)] for i in inds] - return map(enumerate(repeat(indices_wo_batch; inner=n))) do (i, idx) - CartesianIndex{N}(idx..., i) - end -end - """ NoiseAugmentation(analyzer, n) NoiseAugmentation(analyzer, n, std::Real) @@ -104,38 +30,53 @@ struct NoiseAugmentation{A<:AbstractXAIMethod,D<:Sampleable,R<:AbstractRNG} <: n::Int distribution::D rng::R -end -function NoiseAugmentation(analyzer, n, distribution::Sampleable, rng=GLOBAL_RNG) - return NoiseAugmentation(analyzer, n, distribution::Sampleable, rng) + + function NoiseAugmentation( + analyzer::A, n::Int, distribution::D, rng::R + ) where {A<:AbstractXAIMethod,D<:Sampleable,R<:AbstractRNG} + n < 2 && + throw(ArgumentError("Number of noise samples `n` needs to be larger than one.")) + return new{A,D,R}(analyzer, n, distribution, rng) + end end function NoiseAugmentation(analyzer, n, std::T=1.0f0, rng=GLOBAL_RNG) where {T<:Real} return NoiseAugmentation(analyzer, n, Normal(zero(T), std^2), rng) end +function NoiseAugmentation(analyzer, n, distribution::Sampleable, rng=GLOBAL_RNG) + return NoiseAugmentation(analyzer, n, distribution, rng) +end function call_analyzer(input, aug::NoiseAugmentation, ns::AbstractOutputSelector; kwargs...) # Regular forward pass of model output = aug.analyzer.model(input) output_indices = ns(output) - - # Call regular analyzer on augmented batch - augmented_input = add_noise(augment_batch_dim(input, aug.n), aug.distribution, aug.rng) - augmented_indices = augment_indices(output_indices, aug.n) - augmented_expl = aug.analyzer(augmented_input, AugmentationSelector(augmented_indices)) + output_selector = AugmentationSelector(output_indices) + + # First augmentation + input_aug = similar(input) + input_aug = sample_noise!(input_aug, input, aug) + expl_aug = aug.analyzer(input_aug, output_selector) + sum_val = expl_aug.val + + # Further augmentations + for _ in 2:(aug.n) + input_aug = sample_noise!(input_aug, input, aug) + expl_aug = aug.analyzer(input_aug, output_selector) + sum_val += expl_aug.val + end # Average explanation + val = sum_val / aug.n + return Explanation( - reduce_augmentation(augmented_expl.val, aug.n), - input, - output, - output_indices, - augmented_expl.analyzer, - augmented_expl.heatmap, - nothing, + val, input, output, output_indices, expl_aug.analyzer, expl_aug.heatmap, nothing ) end -function add_noise(A::AbstractArray{T}, distr::Distribution, rng::AbstractRNG) where {T} - return A + T.(rand(rng, distr, size(A))) +function sample_noise!( + out::A, input::A, aug::NoiseAugmentation +) where {T,A<:AbstractArray{T}} + out .= input .+ rand(aug.rng, aug.distribution, size(input)) end """ @@ -149,6 +90,13 @@ difference between the input and the reference input. struct InterpolationAugmentation{A<:AbstractXAIMethod} <: AbstractXAIMethod analyzer::A n::Int + + function InterpolationAugmentation(analyzer::A, n::Int) where {A<:AbstractXAIMethod} + n < 2 && throw( + ArgumentError("Number of interpolation steps `n` needs to be larger than one."), + ) + return new{A}(analyzer, n) + end end function call_analyzer( @@ -160,57 +108,25 @@ function call_analyzer( # Regular forward pass of model output = aug.analyzer.model(input) output_indices = ns(output) - - # Call regular analyzer on augmented batch - augmented_input = interpolate_batch(input, input_ref, aug.n) - augmented_indices = augment_indices(output_indices, aug.n) - augmented_expl = aug.analyzer(augmented_input, AugmentationSelector(augmented_indices)) + output_selector = AugmentationSelector(output_indices) + + # First augmentations + input_aug = input_ref + expl_aug = aug.analyzer(input_aug, output_selector) + sum_val = expl_aug.val + + # Further augmentations + input_delta = (input - input_ref) / (aug.n - 1) + for _ in 1:(aug.n) + input_aug += input_delta + expl_aug = aug.analyzer(input_aug, output_selector) + sum_val += expl_aug.val + end # Average gradients and compute explanation - expl = (input - input_ref) .* reduce_augmentation(augmented_expl.val, aug.n) + val = (input - input_ref) .* sum_val / aug.n return Explanation( - expl, - input, - output, - output_indices, - augmented_expl.analyzer, - augmented_expl.heatmap, - nothing, + val, input, output, output_indices, expl_aug.analyzer, expl_aug.heatmap, nothing ) end - -""" - interpolate_batch(x, x0, nsamples) - -Augment batch along batch dimension using linear interpolation between input `x` and a reference input `x0`. - -## Example -```julia-repl -julia> x = Float16.(reshape(1:4, 2, 2)) -2×2 Matrix{Float16}: - 1.0 3.0 - 2.0 4.0 - -julia> x0 = zero(x) -2×2 Matrix{Float16}: - 0.0 0.0 - 0.0 0.0 - -julia> interpolate_batch(x, x0, 5) -2×10 Matrix{Float16}: - 0.0 0.25 0.5 0.75 1.0 0.0 0.75 1.5 2.25 3.0 - 0.0 0.5 1.0 1.5 2.0 0.0 1.0 2.0 3.0 4.0 -``` -""" -function interpolate_batch( - x::AbstractArray{T,N}, x0::AbstractArray{T,N}, nsamples -) where {T,N} - in_size = size(x) - outs = similar(x, (in_size[1:(end - 1)]..., in_size[end] * nsamples)) - colons = ntuple(Returns(:), N - 1) - for (i, t) in enumerate(range(zero(T), oneunit(T); length=nsamples)) - outs[colons..., i:nsamples:end] .= x0 + t * (x - x0) - end - return outs -end From 166296180690f0b573c819cd713ca448b7e8ab16 Mon Sep 17 00:00:00 2001 From: adrhill Date: Tue, 3 Dec 2024 17:22:39 +0100 Subject: [PATCH 2/3] Remove (now) unused tests --- test/runtests.jl | 4 --- test/test_input_augmentation.jl | 52 --------------------------------- 2 files changed, 56 deletions(-) delete mode 100644 test/test_input_augmentation.jl diff --git a/test/runtests.jl b/test/runtests.jl index 8bd5819..18c842c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -21,10 +21,6 @@ using JET end end - @testset "Input augmentation" begin - @info "Testing input augmentation..." - include("test_input_augmentation.jl") - end @testset "CNN" begin @info "Testing analyzers on CNN..." include("test_cnn.jl") diff --git a/test/test_input_augmentation.jl b/test/test_input_augmentation.jl deleted file mode 100644 index 78bdaa4..0000000 --- a/test/test_input_augmentation.jl +++ /dev/null @@ -1,52 +0,0 @@ -using ExplainableAI: augment_batch_dim, augment_indices, reduce_augmentation -using ExplainableAI: interpolate_batch -using Test - -# augment_batch_dim -A = [1 2; 3 4] -B = @inferred augment_batch_dim(A, 3) -@test B == [ - 1 1 1 2 2 2 - 3 3 3 4 4 4 -] -B = @inferred augment_batch_dim(A, 4) -@test B == [ - 1 1 1 1 2 2 2 2 - 3 3 3 3 4 4 4 4 -] -A = reshape(1:8, 2, 2, 2) -B = @inferred augment_batch_dim(A, 3) -@test B[:, :, 1] == A[:, :, 1] -@test B[:, :, 2] == A[:, :, 1] -@test B[:, :, 3] == A[:, :, 1] -@test B[:, :, 4] == A[:, :, 2] -@test B[:, :, 5] == A[:, :, 2] -@test B[:, :, 6] == A[:, :, 2] - -# augment_batch_dim -inds = [CartesianIndex(5, 1), CartesianIndex(3, 2)] -augmented_inds = @inferred augment_indices(inds, 3) -@test augmented_inds == [ - CartesianIndex(5, 1) - CartesianIndex(5, 2) - CartesianIndex(5, 3) - CartesianIndex(3, 4) - CartesianIndex(3, 5) - CartesianIndex(3, 6) -] - -# reduce_augmentation -A = Float32.(reshape(1:10, 1, 1, 10)) -R = @inferred reduce_augmentation(A, 5) -@test R == reshape([sum(1:5), sum(6:10)] / 5, 1, 1, :) -A = Float64.(reshape(1:10, 1, 1, 1, 1, 10)) -R = @inferred reduce_augmentation(A, 2) -@test R == reshape([3, 7, 11, 15, 19] / 2, 1, 1, 1, 1, :) - -x = Float16.(reshape(1:4, 2, 2)) -x0 = zero(x) -A = @inferred interpolate_batch(x, x0, 5) -@test A ≈ [ - 0.0 0.25 0.5 0.75 1.0 0.0 0.75 1.5 2.25 3.0 - 0.0 0.5 1.0 1.5 2.0 0.0 1.0 2.0 3.0 4.0 -] From 4c35746a6f86bd230f76137823e8d139a6157681 Mon Sep 17 00:00:00 2001 From: adrhill Date: Tue, 3 Dec 2024 17:22:45 +0100 Subject: [PATCH 3/3] Update test references --- .../cnn/IntegratedGradients_max.jld2 | Bin 13127 -> 13127 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/references/cnn/IntegratedGradients_max.jld2 b/test/references/cnn/IntegratedGradients_max.jld2 index f78ec5a215f92d40f08c929570b70a0397974d51..4e3b68de3bf85c335bcb6df0790e02cca2235a9b 100644 GIT binary patch literal 13127 zcmeIZYgCWV_dlFYI!lz4M5!o}QtxZ_lv5;8C_)HP2%!|BK8Zq+ghWa@kWeZ0zGhD+ z^^tQZl2V95P6;{ZZ~eaC-@4bj*Zu5%bgy;KgX=XjYtNds_jS$ey=ShM^JFL6Q9jFp zd{+!{30}K;*^tT0HZ2?Cv_5d%vP~n0g!l#qtzI86#LCjz(n@B?@c)R6u^cnT(t3#9 z*ik;KHw~FFWrWQCU;e*pfxXU?ozzwIRAiNuWMpLjqs5>9mZ>)XY3E!02hXMNtDfmR z*DBAM+RObHhy34AuD?vLF+&VxMg31JS9vO&dznr?JzOSCU-S>0)>WmW=DUJe_8D4> z4ubjh6hWiUJu=C3JlxDqW>97aBxw3!-Mm)iF>$Z~C$Ayv)pvrre zxUsJKE70(^kjA8K2$UnJg;*z^XTzhl?D|H)9s)OucTbmCrby~=Mt3_TpnBdu=Q&au=w>Z=(u$Z9_+gV5syERFP~J&tToN-wGBmur@P>? z!&z*Ej8g{GCngJY<>EvD4@`FNjnpJ3#Xv170+7 zA-yniFInz&mfa{nDOf~qgDq>v+bs!@;d(Phu{XD)NQoq!Mc+yh`W+fgTdpdKE z&{}mKZJh#6bNi5n&?zir(gAP`{6j*{$a5R-a{9yiFm@GE82%-kU(Jbyr2U&<*n+s_;X);YkNqj&6&yzT#lpZqW<(7dBgN3$YbxY$@J>rVf1iz840^?2*1DDa}Dcg zto%t)DN>+&G}?uJHFDf5QUga<&furNOatY*-mpSChN@ZSlLt;l{O+J({PfvJv~OGl zY(I0IAH8#h1-U44y%Y05Nvn}9nyUoQo}GY@>sO>+BMy*@y2Ejhp97d(QsznhX7b`L zO3!Y{V@`qjc1LOqX+fkW&HU|z-oH<<6FRzd{o#{1W!O_TY+53-xvs?87x^)j%qNgA zARoe;axt^{1eN{KmtW{91(!J!M9YI_yq`e~uEb#w>aN6lK1IN*Bz0yv%z(!DYtyE< zUy%0s9E+K1h{x@%c~HzbkeT)gp9imHmTL^?Bj>*Kf%#Wr+9<>3xSS?4wAH!Gk_A|} zcq<&csDjd*BT$k23F;;;mRz0)q+0u!uy}0;R7maV_7%Iu7b7n5imJ_Y%@IwSdP0j_ z`0`b(d>YENW_C-X@226Q^3_7{=_C^EzXyC;M&g&VGezdSo1R*$$PHF5_{h_Un1BRIc|-CXtWF?uA;kqdS7X(OknP#W>(U<9pHf7p7pX%N#cAivol_^udV5 z4iMM(B-{7)U!u7qo+lcOqkDgt!U_?P<&4G94z@vopds!^Eg%w zPykiiSFCl|bpCuzn-KVDDQ(XOpykWOlNQMs>R`if*hJt4m0CzXx<%}mxsi3Osl(pq zv~X73uj20Uqo{xRJLd15N?lmC&^_^@w5tCSl$-hgK9G~wF=}FjYZ6E zMdr(RB-y# z&JO>hNvw=o(ZGKZbOzY*kQdIF?7ju6&l}Tj*-~;y<}}L*`wWAt%)r*-2i&-~N7PLZ zC-<7H$nJnRTMPLt*0*vXo$_!bPG@&W$IS$)JkXd|w0C0N_EHeS%X#p@6jrb%ns#Cs zE)Q$QxT@u7cr_CmG+t1@8TPzxLNdmfmWanD#7NKAPk|6_#C_IygXv)(e7Mxx?z;E9Lu=mSJqBnag z+ntt-4{asfweSI0=WfBLp(U7Zoyb*eS4lgQqQwF~WpsDVz`h|7#9tv8=wd_F z*Ig%@Y_+J0Od8Evv>vXl>ca~<=Zgj36R5=+7aFk8nl`+xf{r^|czjG8eQBG^UYR^Z z+i{U(uJvwVMwK#ZHXTK^apTeSpdx=0da}swK`VY$)utUWmi+23MJ~I?l&(2%PItPc z5YLUJG{acY-mbBdhE=q}t^^rD{YGE3`1P1(IGx3*Au{%>BhtC*#ZVaDw@pw=-ULO` z2r8@FL&uplbLBCqLjH(GxcZPo-=EjP`|v|>KIJT$X)D8t`wYH3D`TNAj=~7ZIg;Kl z7^1%(JIDLdCWP^h2E% z)mXSp;#%R0ZCU-r;`I~YTIm7d!n$W{)+_`Q3mZNU<9*GED6J}nntoF6Vt$v(yw76+2^pU*&1>n3QG zy~p(KpTr(_Gd}XBDLhE^0PmGU_>D#hHI2&W50>8$qPI?^QMN6-wfqittsX-w7HRUd zx4Y=Q8bwxA_Zs%;mQklpeLP*Q&xh{S1beRnTz_jn7;Y?-=6&c-RR(3TSc8QmM_hnz z|GdUY-+Px#J+o8T`Njrk>G z@g-RtFf)gCd1UbJ(~kV0+gLE2KSz3RzXku@y#gLJ%2KV~+IVaBIgA(_kByBDxcuxT z5?xpcigRMYv9k@={v3k2(S0d;f58282&>Fe;^n83gwqGx#QGT{Sz(w7y;o?)Z>%=t zrN$%qw|5a-x84Thbe4#tN9%!K(rI3BY7I3hv%&6 zU%WZ$H|w|K1c@4T6wmHVVqN9M;NWG#)x67ie$pgfx^^)?vUUjS#AGw_=?FCC&0{}T zcL?r_&ayl2POyp(<5}X}3b?JrrH78TGOq+6KBm`6b#ntG*4=@c#wfA$-V8P;$%!iM z{3t0)JdaA_TczjRlc939DoxY-3VIpFwDPH;{Uv^m{M&JoHUCK@^X=rQ zM$uQ6W%@w0x894zGUMovcVpqu!%VhFD~|ZBOUA~_JD8XmM0U+N3ubqW&?F|BF4p=3 zdzVGR=O>cOI0?Re98S851~B#YW*8Wd zCMLC=r|AKvC11XFfnn`Uyxx3`^q-`Op~ZWsU&Uc6ckB~<9p?;JOC6b0+8=~A3E$bH zNtX9{nSJ8-p>SRm^y?nU)d~re3&fixIRIfr@A!#-88PI zX-O@{JOG2_XV`(q7);g0qN2le%2*Zt>25H8W%-r(xGy9wOJ>oyi%ytu&6#hF%%#xPP5O;bbrdD4TUZwWa@O_C6Dgx5SUg2B1QD7-ufRe#@sxg!_!GDF})zx^m_oX9_^ zzlE?bck!Fu5Q1tWxwGeOSW~rI7vmevUST?pkFI(KWrflirWhLD$4M_z zxaB&7-rRSbz0DsfPQI**?a%+_6QVi3XibDYS7N~9L%C$^HD#Fn^o9VZ>_P46A!)E? zGFX=v5UMql3vTOJ;J8DicD@PNl(h(9u7j~5BUY&VngOYoazSfC4*0E=!hDBhsjQn9 z9NMrM*Y%x_&c|0$rAr3n;%zzpd*K2uTt_@pU%@6H6G>qCeJFc&7O%>WMep2L=%v7M zTp$&vRA<1;?KW^Pc$!c@VhU<5JI<4|d@yM}XLR{|P=PX%x zp3wo+U>X#}b+d%SGkAiGHGl7F$fCA?g+nW2Fk;~=qIjzj?duM~mPzwLV{Q}1UMy?bCbit7M~w;&3BmcwV&VRi zbZ*Bi_&wqjbJzY1ohd#*T)DR&RVZem`-aV#cFh=95kBsj#>IzrrH35p<1uGiLRDh^G5g zsl(er*t@fmgy%Jq=Qq4*W`G%S44a3y!%u*7{aiYB`guBC@`ssp{lwWZM=&8VlMUH% zhNy<#fv{dCxYMs6-EMapGcFwir}#=RA{sPg^;^4~7h~~=?>YQOZ6tX6jbINfcZxd8 z9^&9jX1RbhD(R13)6f}XpZGEer8ZUZdlSTUIjIO`yA#3p?>eIW1xV)9cgSC}xexFj7jzO5a z|1uF2nxt+@1yFTMfcFM&LP5JH;BDTLfgy#lJs%UPo3Lp!g#(WL7_8gm$@&fwd#u z`TI}}>iKt#G}J#qUqc5n<>*h?=Kh5Z{r7|<@->1n-b)XvEzSX$e{D+Q7{iR*a48=X-R|enWVtK2={H-!F*n*)1`WI zVDewz+MeyrQe$$V>1La)Wl|pbDRV$DnK=txSLtxgBY~jPyaa+v+)4Y7S;TI#h(GEE za9_JuxWH5t-5;1^>!Gcz{=z`PB0v{uOYC1=Hj}`kA@JqNXEImgu`s7*Bwb*uMq>_$ zxTjhJ2Pcjuxslgx^LL#lgI}ztALd2C##OtBzl|n8r{yp8J5s=8?&Oo6i*u;&_>bgr zkt6e0I?rUoTS<>|j9|L=xkRz*HbyH)ORX|{p{8U5k1?Bux~mwjGHxX*TZ=G!;~U}^ zFqDotssdhx&m{?`tf71DNH%twxzOmX3Nm>a(nEvy5Dm!%)`LZyZF1m;?wlmP!VPqc z@+FPNm)P8z65efIk0!PM3h!e(nL&~*q;+XRYKn-FGd~GGD#{QPQfzH)BgNr!4#H^N zMDk5QC|sb;^Xtdrs*-lLD_og+ri^9^ALeu0J?o2`<_om!R~m8r=z)X2=aADTCo#3> z4*Tu$2qMqf6&nlnaK+Z`Rigu!4_z!vg;y5#VZx3fodvU*GvbgPfJ-C;)iT6y9fP+lu1(7(tMb=p7(@B4TE_`_EnNTe*=quUX2?e4x)#yL;7L@EWJAq%r98c zoYGFx@q983j=3vIU+@D}%$LH~t_qfYLJhS?zC`^!!{B=_MJ&`iLEeD>n2hC~iua+b%#+={x*Tteev1`miNelf9-vjd1T~HB zz^v*B(b_?YkNh;0cWk}Qio1v7jo0$_lYNdup*W2+{)}S(ZXPQBESmzq^*lh_-7N+z z@P+ixFF-M;MCuTq0EfQC620r^c-lN2NNU+hmwBb*@u{OE8u11!xNisPmsra@_Kw87 zzh`=fNAIK8_p3=;ACIM{^e>W#xJ_vEuN<14GUrDwEu<@(``TZs9cwQ}ei6o%ZRFN9 zHEfI1Z3xpI0d;=Y(Ng7+uj4iFYK)Z^P+J zLluhM@+TucysbYzyQBanCtD?NqR!yd9|SbK z*U+$cUnCy78_2kzC}F$ZC^B4kFh-1A$2as3k;H1QgL`jI!m0>=y3=MSp6JowwthcY zy54a?`=J({VDN%x9Ph!V;s%m)zYKhDucWn#>frWxK0m$Nhpst%NZ6NEDNc+u1;>P8 zJl5O;jb8N^TmPvOj(A>Trm&R%u-pOP6nnFz{vP1I*-~5(l!LFPFQ#9z8i?PIw^;8p zi3RUpN$Zd2;i2e>Xb~s@i{AN?!E<9_3Xa8`U6*M0idH&of+5see8#9j%fWQrV|GJv z7xeU<1}Z8edFO!zg2uc{P-N&Pa=j1aeVK~zbY>!|#1CUjZX|%yteYex>kK)!@TcIW z;STN%N5Rc!hhQG7j=~iWy6X2#su~h6y2)g*M}BJTMCy4qN$MeZUM>QkfOPcL?85D{ zoNz{px)7?81nqM_vB5LjKp5iBTZj&y@W&QouPg>)93^&tw#3K3^)YJkSIjNw4da6E zGm{PK*x9iOuYVrPOQok+eBLv5H7Eh>22RJ?jFpn6G=zpU0zM(`jQ2#VwN0gjdT|K?Og#yMFZ)W-jw-O2H}+* zx_GhXFwB%WB@{lqCoPC?WG?y&==)F;UdHFqnt8Fb?0O?;hIHDt+}7rI-<{(!M;;SL zUl~-E=+T;(2I=yFY8V%+OFvufA|0w?B(l%rI%*%#z}7>Gt`WQ43}bv=P9;J z@gi(=$iWLY&J%K4jVT9TMm8^5I(VK2R<9q#JiW$Ig-km#*R7r;@MEy6;1p4vIF%1? zRpuEft}s4JnFQ>Jq=)>}@xaMU9`*&~4X-{3y0iAO zxuJDr#7JHA*`>tCpC}d!ZVsY4rA~N9|2PS(?#&l@=D>}xGAMm!&q;Lwj&<3C`dUu7 zwc)5x(XxVv%vi>aEFZDx!@W@O(V{K4R%6MKY#}dLkuK9T1?3UbF?5dwUbr|HPs_ES zbyO$HrTrkry(e?e_D5o0%S+5C^SEf@vk$VUo6uvw3?kkfpdF5BlBl(vq^7#J_+taW zI`7RaFnWe?{K){AW%C-{ri!$@L=Id>e-KNQ93XaL2s~Ic7BgPnLmI0B?==+Zq+`11 zbh45S)RGfcjF?1k`dkvX-KA)@^#a*?e805n`75?CGDUDYFc21b?Iv@lo4~=g(YWf= zTk&RM4EnMPGH2RA-kzt$-OLtZ>EjhVrcRHYT8EIbb2x5IIf$jz{rT78eNv~1>1>q# zK^BPzz{qz7C3ul++X)O9W!{&5o+%v9!ggSsSjLtBJNnPVlP%0pwx2g8n$<3k;Mw`9*xfu8>J0zD*^ zHlXCwJKGWkwin*@xfD#2e4gJ%(CSogg!4Ao$9CM(P&Lt}h(NHSCXbe&7rGkLXV~+$Ox{ z?GEfWdlj{OdKAt|O1PVYIyF0x3&OM4xLEBe4FC9pjhYe9p1<&ApGM}v2J;daR}zcH zsy2367RG3)vQkyI#V;u_V@tT?Zs-psr{2DJ#pV=F3 zt{%o!)bHYtXZJ|5OBxQI(~X8riL|&q4z@2FBVB!2ncUrW8os$~M&HXmv?|Sk8qED` z_qR55ZQsMNtXzRyblb<`ysY46KWlK1vyoi$^oG(`4<+@>by&hEOX)Dj(Z%ZiRbuH^ zDTa?a01HRT^0Z@~WVO5tJ)=-otUwx}an~Iz#(&u8;}_ZK3<>vH9FKY%2SD}a)$nHC zTX;Ot6tn#2Gi7x<@b{5nPgiHy6~s(k4(<@n4+3yE>(d9hO%mk0|X>`BwK^W)RgmPL>aInuP(zCgSJbjXY z?)O?SJ>OmYF+4!fSDi;%x<7%N$rNZE^cw0rPLQK-HJS0VSChmdU472s~G$@Uzj=TUlKq0rl6hh z8}ow>@xUPyasHGsLZ_E5i>z%G_07kzSFQ=5uJ{d7hTjLJF@5-+Ap&o@_5ion#Y6Fj zJEC#%Kr|lJ#Uhyudu@4^DTRL*^sZ>~)|C!4-0dszyA9IMSH?p7#h+4_&fDm`>Kifr zT8tYL_TtrMdr;UEPGxgWq5kAQ*f6CW--VokjAxNF(Lb7`Z{NyCIvR7oKj--+y*Stw z)Ss4ikE2y~Ms#pND0oNz-KSNwarweW7_{gPv6xUMywhDri^r~n-@m?y7JqH|XzJg) zrj`ZJGioWWbnYc});@~rHDllR}gbm%F2yg?r$jtyp2%1s#jU5BBSHchswqPJ)3iTuGDmf4WR)ThLA zkAD4O!><8s*{d_?x@-{!r?x|k-$ihG+zC%k%J7zWCE@$C;eWSwC9y4Z9e*y@OyveH zrzI4iBzOW2@mh-1A{zpl&aw?#-{8TMQK;dznJhgOBDPL3plvqtRAc!N5>b8!^mn;3 zw=b*V+R)Y5^|OS;jxa>`gX@J)2glGO_KDmzRslS}uB3A$Qob@gm!8+N5|(Hml+hZqV>$I2+PyCFY-+1!;>K(Y8_DzUpC?^w!pc#g9}E(AXEBrM>42pyBEVNepwQ ziyh~pN##$WrEr80y`xHa_Bo9NI@}jtjZ7itN>QZBv$x=q-OT*sm z1jP?$B_|#gV{7~;K~uqqwrF&c>dOk?_fvuP+Iog14&6&ui^EvIyT{qnZZ~$pU4$BW zKW;iR3ggZh(w?3GxLPTP)1Ipee3LJ{h@H$EUOW|NduBt=KwB~4NWI|w-T=2-4PoV{ z&x6GlCFlrBBZ{}pQE&MRp?uj%n0@LX1R0%$?w0}#w2BamedOuxd;P&`{|z?l$qiU_ z+Y{qPUn6T0#(@7dTiQ5=!Q17A&|={&#@!l(I($a|`jUip#}fBKCcGN10y|0tTKn%z2v~TE?P#vROO=;l+QfJaw4T7MuEw%P<(F7n z6C)@YroztDEl{x97?jpFV?}fghN~<^tGB=KeSR{QyY417d4nK5s)PA@g^^XhQQ%@F zLnoLpQq_N_FmHYaJfD6*loJlJ?t*`LQ2qd}(v{8{bJQiZ5v#DP;UDlk+a_@-?jq|J zwhC6;9+R7g-;NXp#5=zo-gO4o{EGY`iB>vthvBW-*kf3v5&yw znGgM@)q)eP_c702Rd{-cHL5gKz*hxpFfEVai87B_&aP5$8ZQVFUy1N`))=NeH0D~j2<`V?7fpFL6*K#BMc3uTNiBkxUU@@?zg$4$ zySli4oxnzPrV8HbZrI=_WwQeguxnk1@l(}3GSk|cg|2xC|Nh)dj1u4Ax1pY7deS{8 z8)YK;#P{OXmkZdW7l}gF!I#kH{}~(C4`g536LF)VBP%^Mhn|_DL22S%Y!BPU?XrAC zyWBi-{+k5$Kb>Qecm?5li$C28uhcjVrpgsxUBKw1^Gt&pM(gWemaPb zJ28-Y?VL)UJ9P+q_Wxwd@f!;j=JTD;YnjfVV4R$Bid;UY$m17DnWXY8nr@C1wKjV2 zxIGg|<>ej{-(v|I)?AiEOc(wnztEKf-mS?Si-3 zP`Y*90p@;o9~7G|=cVT1?AW>}`mAV&_b(Q<+)*0D!;{#>tX8EB=ojK6EeTITxj1{+jHx4iE?1hu6 zZ!ljKM{+h;221*;qW`S>7}nvBTi9GEc3UD;c@+tEDLc8XLLGAr&!&MVzA|#zkm>v1 z!Qk<;xURQIJ1px-!TVh_uyZV8V*v3{mq(jRy5d*c8!*pb79a1qEM1(P3&GM-OemZW zRxY({Rk0;;h*5*Zz3j!o6;@PdlPpQM*h)0Gb*xiC%wa$}bxXW5-mbc;S^k4YsHy-4R7_eaHb~T{DsgjeSFM zjy00HHP$S<>z`;( z^}d}X7H4;eEAL!D-F~6)fMaC7@fbJ{InArLWW#sX_i@2pO3GrtByj z9{;a2-E55$z_<~!>cuVue#l{dc9EoGa}4TOwC6X5g=1?m+XgYOTmpj>*NL_C)W&Ihhw_V6uK@pdOw zeY;H9nLCzduDZ!4$-6=0^OH>L;tNSZ=S@MS_C07ix1-JQ2BG~AvY_z+^xK?WBxysH zSl!>#KJfETGJ91v9q?x`gr7^MJ{EFZc3=v$@efSqa5~l5a0f<@y^M+##=N>}fe>%} zPHbxU8>gH~#cP?fz;}BQY{)n$%rw}Gv!4GT$y-ig`td>3YhD@u`DPcLJo_kZcv{b< zxMvG@SC>NC`AInBVINwlVFR6syP==KX1tPD$7^ny(#%LxZfaYGF&dM}2=yb8C(ikmWY6y~#<#r_n)~UBetvo-ia+1+hKLXhg#?iYeL*~%B-wr^ z0Y(`1qdJA3uw_IVdvkCa(dxeq29DIA>$h0hwRdacR8fWM-m3wF#=rj*_h^A@ffsbS zAB2di8Pw<2XjvN3G_cvA{1)u!ca{;9vu3L?VDhXv%-#v!qIi`^Jy4c zl{p_O{&cbQUci+?TNq@os literal 13127 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 U{tuVn|5fe(ftwI;QqB4Q0*suCUjP6A