From 5fe4aadf9250ebfbba1f193ac9206956be9f61d4 Mon Sep 17 00:00:00 2001 From: sahusiddharth Date: Mon, 24 Mar 2025 21:01:03 +0530 Subject: [PATCH 1/2] benchmarking post initial --- posts/benchmarking/async_executor.py | 138 ++ posts/benchmarking/benchmarking.png | Bin 0 -> 98501 bytes .../benchmarking/benchmarking_notebook.ipynb | 1512 +++++++++++++++++ 3 files changed, 1650 insertions(+) create mode 100644 posts/benchmarking/async_executor.py create mode 100644 posts/benchmarking/benchmarking.png create mode 100644 posts/benchmarking/benchmarking_notebook.ipynb diff --git a/posts/benchmarking/async_executor.py b/posts/benchmarking/async_executor.py new file mode 100644 index 0000000..717e375 --- /dev/null +++ b/posts/benchmarking/async_executor.py @@ -0,0 +1,138 @@ +# custom_async_executor.py +from __future__ import annotations +import asyncio +import time +import logging +from typing import Callable, Any, List, Tuple +from dataclasses import dataclass, field +import nest_asyncio +from tqdm import tqdm + +# Apply nest_asyncio to allow nested event loops (e.g., in Jupyter) +nest_asyncio.apply() + +logger = logging.getLogger(__name__) + + +def is_event_loop_running() -> bool: + try: + loop = asyncio.get_running_loop() + except RuntimeError: + return False + else: + return loop.is_running() + + +class RateLimiter: + """ + An asynchronous rate limiter that enforces a minimum interval between calls. + For example, with max_calls_per_minute=1250, it ensures that calls are spaced by ~0.048 seconds. + """ + + def __init__(self, max_calls_per_minute: int): + self.interval = 60.0 / max_calls_per_minute + self.last_call = 0.0 + self.lock = asyncio.Lock() + + async def acquire(self): + async with self.lock: + now = time.monotonic() + elapsed = now - self.last_call + wait_time = self.interval - elapsed + if wait_time > 0: + await asyncio.sleep(wait_time) + self.last_call = time.monotonic() + + +@dataclass +class AsyncExecutor: + """ + An asynchronous executor similar in usage to the one in the evaluate function. + + Attributes: + desc: Description for the progress bar. + show_progress: Whether to display a progress bar. + raise_exceptions: Whether to propagate exceptions. + max_calls_per_minute: API rate limit to enforce. + """ + + desc: str = "Evaluating" + show_progress: bool = True + raise_exceptions: bool = False + max_calls_per_minute: int = 5 + jobs: List[Tuple[Callable[..., Any], tuple, dict, int]] = field( + default_factory=list, repr=False + ) + job_counter: int = 0 + rate_limiter: RateLimiter = field(init=False) + print("hi") + + def __post_init__(self): + self.rate_limiter = RateLimiter(self.max_calls_per_minute) + + def wrap_callable_with_index( + self, func: Callable[..., Any], index: int + ) -> Callable[..., Any]: + """ + Wraps an asynchronous callable so that it enforces the rate limit, + and if an error occurs, it waits for an increasing delay (fallback) + before retrying the function call indefinitely. + """ + async def wrapped(*args, **kwargs) -> Tuple[int, Any]: + retry_delay = 10 # initial delay in seconds + while True: + try: + # Enforce the API rate limit before executing the function + await self.rate_limiter.acquire() + result = await func(*args, **kwargs) + return index, result + except Exception as e: + if self.raise_exceptions: + raise e + else: + logger.error( + "Error in job %d: %s. Retrying in %d seconds...", + index, e, retry_delay + ) + # Wait asynchronously before retrying + await asyncio.sleep(retry_delay) + retry_delay += 5 # Increase delay for subsequent retries + return wrapped + + def submit(self, func: Callable[..., Any], *args, **kwargs): + """ + Submit an asynchronous job to the executor. + """ + wrapped_func = self.wrap_callable_with_index(func, self.job_counter) + self.jobs.append((wrapped_func, args, kwargs, self.job_counter)) + self.job_counter += 1 + + async def _run_jobs(self) -> List[Any]: + tasks = [] + # Create asyncio tasks for each job + for wrapped_func, args, kwargs, index in self.jobs: + tasks.append(asyncio.create_task(wrapped_func(*args, **kwargs))) + + results = [None] * len(tasks) + if self.show_progress: + pbar = tqdm(total=len(tasks), desc=self.desc) + for completed in asyncio.as_completed(tasks): + index, result = await completed + results[index] = result + pbar.update(1) + pbar.close() + else: + for completed in asyncio.as_completed(tasks): + index, result = await completed + results[index] = result + return results + + def results(self) -> List[Any]: + """ + Execute all submitted asynchronous jobs and return their results + in the order they were submitted. + + Thanks to nest_asyncio, this method can be used inside a Jupyter Notebook. + """ + # If an event loop is already running, nest_asyncio allows asyncio.run() to work. + return asyncio.run(self._run_jobs()) diff --git a/posts/benchmarking/benchmarking.png b/posts/benchmarking/benchmarking.png new file mode 100644 index 0000000000000000000000000000000000000000..cfa8282aa189184d2866812489604fdb1c769968 GIT binary patch literal 98501 zcmdqJgWo%cWZxUT2QWM{rRM1y8*ZKDkbt1hN?8n!#u z11^u3(s+or!P>pYfoND$wUYy%8oo4gW6`h5Q$YGDbc8Z{o`3Mw@3`2jQhbP!>rJw_ zTj32aa|@ulEPZ^oRgyfvd|S0Mw9}|~lCA$8@e{36(Dl`VS(bMU4(9^}c>kQOdxU%O zNLpp~dymk|ccMi^+}9+V0jq- zlw!)~Vg8fGF!@taR!d$*1@)d_^J1{GE3pYC#4?AZkhCl6^nLB%WNHH@0>FD2oe~;73 z$L>EpIl2F9TBr%~{3+q#9L2xR3i+sw=?(zu8rc8*D!mRW;IJo<1 z3Udk=eSnhr!2|MvQif**46-TA4?)}viZU=Q<&vsfIxHz10XyY1j6|9C4?`soz!A4(6jmf6rn50SSs^` z{;T4@r7Vcilk@qmW<96=S0Wn!7wzAz{=2fAsVXA_zaR^Jvy=Y+^o*Vi`HK0!O~k1H zV3EBW7Bgyi|7qjzG4mIB|KGvK1ow|4;Co-}{(DY;Bw>-Kg#X`}qCbcP(EFH-lFEX}u!#fk*CN~|qGg1Uo4{+g zoZk#=>)ThpwRPqh*l$|Ssrq_8e|ph&|FwK1L(hu5W8cv(5C6xFj3}FV81kI|AA2ms3MsGp^0*S! zv?punS2ud+)+=kjG5@91;*3{i#&#)9>*Q^h5%l-xO>Lf)Mz+H`CIM%s+l7gQuezqS zG_nIvGMDkimp_n1(YPr;%_#BwuE%NwKk9k^O@7gD|4LEbe^pG$z+;aN)$flkW1)YP zMnML6^)bU<$T;Bao9s^kSEHcYvnlA^QKIaO;_q(y`^!GT1&8{0kBuwDj&xF)8rh)+`eQ0l~Ji+(%Uo{bri8E;+xRPTKX+daG)w@+p`r z+{6itDl5^r!d^Pxe4H_UN`Kjo^JPBL{HL$Y{jcK8Dj(nO)+n8wNNOi#4ap;zT&(bV zoIo*&x@gBoJ3+wY@qu?+bD?9XY5qvZ>Hd((o!jpphk;u^QTpAqTk_pH8%s0t($n|; z;W>yKjDZpS22bsrTgY4dMro+)~n<6gbWPqx6sFG!aAqbTW=#xYLJcW$TcYsb0pWh4~KtjFGLapv0~`=>%BDD#1`kr>F0KBx93mkZ&q-o zMB1)KPWzb>i~hKh`tcyAPpgr?P`@?xKf}7EDO#(~y>$?VELkK<5w8Q>jt5v-3t}Kc zob_=U2R69@UiWk)nmc9@cc_(6qjqeVK-)*p2tQiy^Y^=Z2K`ml`uo)wrkL4Yp_Eyt z*J=cL@zqu?l&fjc%?@RXD$tu%qN&QJrJW0fJIAIU-^XNIY^R0=#tiKw+vA)&u*iq! zpP!=_boc}=2V6XAcmy!KoGy|6sX}@93Cg>fLvG~pj5YDf&33u<`e1ker=(*zKDKpM z*bBKlQSxq*5ha1EpFBb72@(gNPRHeme4i_C5lZYOH7@zt-;ZW(T&^d2$J1DBI5qEY zj$y3nCVIHFA%2Yrj7k1Njq>8?2Bnta23b;?d4_}dYY447f+*;+%h>rw;;`jpQiU~} z)MQG5+-QR;dk?mXX&w6p=R+Yu5^ukiv8_mV<`)%`L#hm==oiT@N9uB=awrQ(F5a8n zmMVSi-aPiXLP~r_35YOq)rh{$dlg0NrRx~W)mW<6_k7)7;&>xfYx zXL1A1_3oWg;fGD}q*oUdK?Ol2Ep^gq?$FB)BFVADpquql7K!Kd7L{rYw}uVQ@89Z_ z1>EkMRHyrr*yyY;|+`FR9WmhWb|UfrkB+sSvI z(iWVWxt?mBcK|iteV0?3X+8f~{Vk69Qmi$s+Ro{wIX#nrZ2{=2mpmtS+_Tu;ATW1z z*?v1U`qJ3s{LEW{)!t+7tE z5A#uMQL>Vg=)kr+cS`%YWmtca_h58iddtso^6E}@<=%7Ah}oy~vB;9ox+T=sM9&=S zfzUav_u_+Nz**+`#I znkc;9y<#eVT=6bxt6@sr-Cuuj-a69}-jmqR`<;EG-m=V6z0R+*jE2KcaoA9M^udog zn_MY|AHf(8OmshfJ{gq=_!;+*PREJ+(+Y-(+B)4Ie@gC#f6KAsJ}hbL&4b!Aj>(;k zE7s234894YHf%*!j6NFoxNEOGv5xTQe#3s07c_ ziUloSH_>bwCHL7G;>ith&p}t9IN& zX4ki7EfPKVsWt<{zYsbS=!;lh?>A}WUN3GL3c!pT1Z`vFTy|LLFZwQrKgESx$0<7+ z_Hh!N@VMZfe5zw}dd96Z+y-h;mlP26wT0Va&O>*E<<{-u;8y`|^tR74B6<{0NXDA? zDES+ihDqWie_ywY!M#8`Qa|5l-3VL+4m?V`6o|P#$9r_oTe#tzVOM-qK5++sF4x#6 z$td`u$c9ey4X;A%MeSU((J=jT=2x7FQ9n_lpJGbX9(yJLzfk=Aj6_g!5|+3x3({lI zr_G|#1$A(&O?|IgbyT>~x}CReSSY!daYAejo-uTfm-xuvE>rAc*lkI8 zCPLefP3@2*#=m)z7GMqf*}6Roo^Pi=pB5eG*CptqBjpo)^YKI4=ux1`U#G&Z5VUnb zvBIT~Of)Y^DP|5|lP|=`i5Xe{M-)udBkm5^*v;MlrXa7Se=Kg9`ql_-^!QVl5;=( zNCZaIEnIlqp3O8H@$Uya)VvA>Nnu^TYG5mt_P(}i8(*QPdVlkJ{B%kq_kA&D{6lW_ z$z*|GP2q9|<%-Tr@@w*Pnu^e)E=3?m%nyEALCo23ToU+0yh*vDG!PIRjwykx`MgQx zdu6uuNSw5vf{G8YPZ%Bx75CL?rSM@;uVqZ-DO}pxe&gz4`TjK_$+ej{Gd8u(Ou?w1 zm7s)%4J9pYAW$RO3|?p26XLlQm>e!nV>D0#VA*Bs$n)U{A1h$&b& z7fYlU9dQ4Ex)bCDa7}9W=(ET_N=DbdY0S*=9U0QRO`vKyL2DLs0T5wnho-xR>Fi6K0##M7( z=F^6}S&<0Um^Sv`*QOc{<{vlZBa?rQ6zO;wrSP-@AF(1JT*zgCp3z9NfFz^(_+`DX*X7p~8 zFaEsLJpI-3R8qp1ve#h2u_#)1U}Mv41`eHYQt$hB361<|b3ICW zS4^KwEtaY%R4URx=Uz)p1Uh`Kl&>Y^ zbBB}B1|XGssyoS6TQo$tk}jZ)(VGh6JnQ#zxz?#Jhf|Ib%Tp`W;5&v(GhRaVEYXRn zsh8>BWJW63CAn^?THh<;^P4>Uts3qcDvfAOfYTIK_mxB+jr50?SjEMz)BmGW_CUNr~xEvMOlrIi#5UJXkiY4hpewMhE-WA?D4%-Sed zV*1Ai&B>a^E2@FV6v@NAc1)7SCV_A>)dF>aLi-mDCU_17gf$92qtiB)BW-<97rxFN zlM%zQsg**-%y#TyAMTNE#zWtw_DH5crGC`maed}fA_zWWtIDbrEH{vR69cpk0spZzbjiI>x>~VU{9$<}o%VEERsW zYoU)RBrJ5o$8|PF+I3@OzK)eW2p!>OaYc+oWP7zbGzgs+o@-Skfgi=YSEL{?^D^tr z1|v-$$d*2Lr#bt^UeKdkvOY5|Zd(4DEan`vkF}tugHZ^dm$9Ut{!!66;kEl>aX46; zOlP3856eBYU@ZCNi!J(#Sp6U3ZNr8PDRs{&twU@xE9zsP&Ve2DC@r*cNFL}2cAckv z6VsVE?i?g-oT->JEqkekTO9-gvZ{UfOKlQbh9v=^{Ca|kPld%uR#w-sV-*kz0jck={P{^`?%bj%k zkaPUXs%BNC@2tb9hxW2muxv*ajv%rGSOiHp`FO)P6uFt`*Fo#fwj2eVnGV-dyex$T z<}?HZWb@<|PbqBJ$pv_N#d=}C;k|4PVh#+i6@iWqes1;24l318s`|_Omtu03E4~=9 zAivP^vK$Q|i`znfXN!U9&ddHisyF&%k^F>6(94#vR?=*#%P{yesMUG|;K(>e0%d9l z*dSV!8W_v=$&L1kZcN^af5$JF-!jueAJ?p0=-oWwKeHzl-S{)nH%sCu>%?l;{hnc5 zh(COv{jrI-^K|vq09!U=w&0F-Y<~*!eMhed%YnirgzoYIpDu3r2-=N*w*c z;`R5Ah{xk0a)OkC3_V55D5wfYmFn1W!sajc!U}G`A*kn=DzJdR;Nfh#GdC4F%V`o-KTLH~-alDmXov88IO%aTy0oILLydi}H*U~--M51z?1RRp zAy{y^(Li8PM`QX}t(?5nWiErQ+a+E{0u>hp$1~Wcu;^MjFtH^j7syfO8Z6wo(Zf!> zuP%jOkX6T2uCFY*Nj9Y^O!%dOs`fQS`MU~kIfqZ)KsrlY0z3{?(Nc3fd_G(*g7c0f z2+&l@42#6A^VZGeQY#`dL7P=)(>NBce#w@;HN`fdp6-tn6-Gy^~OAn>3z~cgybeg>NO9 zXf0G2j|=N8FF?*Bbcf0vItaw&)SU>m%+JRqkvoWTTY$*Dp&yO4&17VNO;h)ccfu?s zftWC00S3C<*+@OGE^6x4vru?}I1dSs5D;M#7RG@v{X#ZcDak`)10I$Fo}=|jTGW$x zr#3Q-UmY{mfr@bJB~;~rnF)Ddjwh;$7$<9<0px#ag$jN6UDEcUxKZmM$zN!4$T@hM zit_c_K$h>IJP^yF8TZ3Y^G%RwG(K;-6+f;1LuK%MX7qPCUs$PZS9ds-Rx>}pj6}3y zpJm4Hnrj81S`3JQg=IXjprZ}>tibptZCu9e$xCfHgOGu2EG=b359G3w#Nk?TmDq^C?>f-s(DKCa2cOIm&PbmhY}lMZauC#Bjne zQ&P!QtH&SnrbjW$vLS8Ej^nQ=Nvv1~MDzKNZ203X=r?_2%R1p)!i9Lx-&B_@Z6W15 z-{7x>oV-^UJOUw9$0>ohsWI4iIPxqv{V0rVhpLW2D@%SiJQ9!Z;HW{2qWF3V-lT+vAh!qZ_9RtL8$>2}um__!pxB?x|9LTIi!t20dLOTR@JF8mXJFf9ewj_wt z>k(679eSzv=eI;3Qk4veM!S2g+AzVNcU8)o^md#%|3?UoY{57MXqjH1Df&W#a&Irr z8r#OVAB6sZL0R?Slqg8C%8Dc>ys@LNPtY_iD<4%HZAYg-Q zGHbyNh}0Z~fqI!^cE}DX-R?DqARUm<2O+=-orWOWEZ(v8E8;6)JTdHD49wOK`I508 zka;3?ce%bKox^d6e)qJt3FD`HX*&bGEN(6rfEe4gQxLSwcR*747WN0%<)s1Eth`C1 z;5oh?|5?53?g^M3!hwFD)vfm%-DIbXMN0Jy$};}B2QZ?xB0v@*Xce#fH8fQVU>+>Y zEKbL=D0@ZIyy22Irql*gM!@} zZ2_(AIt2|Amp)StdgvVe7#e}zK@_ufnxcIn_9wv|=QD;&ErCC$O(ya7`e zrYxb+`}}0~rv{RJQW5Xo`&4EhCSi)Zh@;^B>=_lbB8=1)^OY#Sp8_?efigJ-EA`qf z&`|Q4J`zl3St1}p=7;a9^bu|78`6>6Pl^E>CGhA&6xewvEpx@s@Z2^s+ARvHp7-&A z9IYMX8;l61^)^vIjE6Wp@j8rC{xo!0!I<{1$X{0s+GZPKfAVD$$Eh>t1G8hv6ynW= z>190P{Gp?n5d(}Djs{FLdJSEw32a63;7xvAAyf1T4rVvD3jzIOVZX-0pLElst4ew0 z8TpT!qTU66d*ePPHN#qIYb^IvlLwkj9wZMIq^!)~;aycMoKUw}RAKB65a^&U6Vx~3 zAcpKPs)}!VC1puo^*4oGGn*TyZ}8>8fkd>FAq3+uT%8icHeEu~SUnKL$5DDTo5>Zr z=E9U6#IHT`!E){Z-;o(T)snxV@l`oAt-W#GCIg%RU6{0EQ=Hd{XZcdyd2c71ey~>& zIN=TL1lj_-#Bt4dlr{jbl2i4GB6f)4SYrhi=oM!K!oAKELP-rgm84wbTM@zNV89oq zeO=@Y-WL3vCpio#M{p3kKtdl2$^$!frd5T83U~iPC2-lgUc9vUdxdwAQ{i`SBeuQ-A*|!Mzkfg^YxUq&a6STnSb6-&%_j|9BhGnEwRE z+w(f4N3zwTU^ymYD8I4N&ufM z{(C#D4Mk(=u*IdaTh^Ok@gbD8LwY}wYApm2!Z~@G_v-MuZ=Cq(d!2jssiZDGWT=qz5t-cuw^>(M%+jYY1rC6$c6ubU zFDOJX3@P-p#~vQWHZtV9#(J*=*=W6ARxTQ$KZ@VH^BKdX&}KQCfBsHqOA!a7FcLqW zQ1siAKgZ2Kapi%JthxdmD=ZiS*nseO z$|j6s=YI=M>hZ$LsTpmvzK|D=#$B)n3F1QdX{6hdI(XxG?8)zPYty;T7##3AbZ`L= z1~Xq=_bj$O7B!bmVwG>elhYS0td6ov>TV}E;W!_*@8J*eW^3$2!z<|Q4FD6oMN?bk z0Q>pcbb3E_w{0F_mE-PH}R#O)m!vKs;!}L|44k2N60?`p|lL=A2F;s+vx9< ziP%{}IWwnEazfDoyDMQtUKlz*=iYrA5(|8K;MLyxQ9gEJric`0a&d&rXY1Kg?~E zTPKU(ieGVcZszGEM?x0}JEsVqn6f%FhgRTczWE*({B5|U(mK=N+LuW{Xi9f_II7M% zRhcKzP4#+X#__%_1B>g}zJZAE`HIqkBMLJ;AJe`QYNns!^vPBXuUm5u37)6=W5q*36}iU4#-o;*$p`>qW&V!gDc+PMSxKyK!=OeyLrr zm-x&uW-C~n*&k(bJ*f;5ar=2#lFWEND$(OFD|T}v4Mhd3?pmL*?f+~^fAPINzf2W{ zp#+g1(P>5|^N%@68|r;-*&=gbZw{K;^ZB8jkTI(kEaEoE zUh;_yeFSW+u_va){F1qWa+(@-mB}AK*?QMwXlNs@nKr!|Ch?|5{CtEWMn}aM%i&$R zc+3i!{sXAqOuIz{S`Pc5N&(CBwbDv;a%Kwu#Y+ z^y(H+GLVI7t3oDHYFVG}HHG_9xANFV@+MoB<4H$tgh}OhtQyI$hpdznR5|D*BApm* zduGeJmCKw9Lnvtuv8*$I#i_Q*au&)4e7G=q6(r;E&obaV%RxL4-cr4q8~L3G9q0UD z?IJ=k&PHuvZV0qkCb{6ZZ@K_#FjZbrZ7^JV6gXu&SE@7#Me$Y6#_FfD|7O+5q=9P` zCM+{BmiIDa1X7z2%I5D*=k2SlLV^*C=A*rq2cdY}$afZ-;k+LrYLX(i(;On>2Q(eN zKgnP~SET|2F}okc5ESz@;dK-NLu_Sik+dN|NCRcaMvam&0<9M8>)>~Q@;hgW*;!1s zm>VCG1;6C-y2q9}E-JmDFDif5%)kNEHE+e{Yj40s@3Ni*D&?@=JyWe=PaWXhG&b|o z#nRd#ssmjpP-kM@plc1U%|1=^w=|Io=MQ}WawHG=WIp=oeBS<=&L3ed_nbipWHd!{ z<@HeVX*3o~#A-c2RsuygXrZ|Y;Pn*PR}Y<5(!?K~GCr1-{BGd<6`gvPq~{@qKKvzv zVN!`t8($c+Gf23)VPl!2x@FB~jy$=gHt&xkv9^vyEdIPLT$76Sj>MpnT1a_yOBxG; z6)POKiSVw85D@lMN=nw=#p|P3FN*txT3vs0jq+wdrV3PCMCH>JuLNApnH+AYK`h^F zWe_y=^Qw86EU|w=N+vcWPJl`R5^lI4o$m&iRiR?(5XGbj%nquZdrYk=UpMQ_CJ3sS zW#YTqbQcX@-$Ji7PZ?p(c<42+5H27saAb9i%Px!Axsi_R0z8ugA>fe~fnO`}88r&8 zWn1nyhH~~l;z_@LY)N@jfvl{M`N9Vo=G~E zDD?m0S}9B$OY0c$nK>9OJhUBb}^85xgjLNvMNZk{?)K{M6v^IO@Xdm!Ld9pDjt z#t$N&6?p}{2Aq!N$8a60k3-RVnH@GVGF^@n9?&l-Dh$roG>3@_%+M}Gg zwLW)DxQ^N599(zMax!XuyeDzC%JYVt$>XDKiJjhUKt9c#EzZ~=8P>5L)tw?X^KbyV z4N@@0S}i$CggA zEVjN*xT4ah5LCG|cBmf_rnBOjtaVe*Kyfpg*-5F$!hiE6%E><`ZBT~Q;q5;w>N6x5 z(TP^85Lk{C?Ms%X*(WaF5*9lTlpIbz{&k#NzN8^dbP=3x#^+|B*F4Uj_kN&>J|1YT zZTmJuMT#b!(M*vnBb%)c7;LY&cBRunIs%-R>5N#wa=^rx~?q|1Q> zN?mq_P#)qw+ecz{5|wKYEK~dX9>A{G0}}kX-{~FBkxl(2{o>cE973-3Z!!mWDm$;Y6a34of;8GaU<@_R^l&6^1H)`!N(s>#^621}XSz zUr`$wwTN#sXYABA&+_MVNTCAw?YQ>M`9dJ&uv2qqhx8z6!fO+nT^7Vf!B!1olh0#? zM@GlA9S?F`KOn0;jv*mRJyZw*O5L7L5im`A6#f9`W0ws3$-N-oYwQ}RG5)eDoogay z{(f-!sIX(!(g9A z3h0rI5B_G2iU@g}y(+y3A<}jTr{7l)W!h1z{z#w|&L7eawu&xQ6z%fT8g87Ck5Hs` zdD3%(2}vPV|0)Vr@+ed;`3nvC4{>yx8N=w2{A{kGsFFLzsWK~UMHVq#II#VwL!C;( zC>+Q&dqd0~&6^w!-G7%?N9Y!5#Jegavgji3Os%vRf}5!F=|y-y-(LKq2vh&V-suMS1UVvQ9{IY0$Qa@p` z5@MUoVy^xv-;4!Q<4q;_Q!sRGk%XR}zEEh!aLQt6YYJRQ=O)|YldzX8lWt3`W8nCa zlYR6iiq3Bvt)NSRr4#}S9w32?){mh*^O!Z|6n?ZWNn(8-!O%GfR-#GvT5uFuMvJ#k zk}N2F#=1YKbOj;C^VeA6>A55Fm6eY?XP$3&nnAQAmuxcuP~bin>ZaFP?<*~iPZBV% zNy@Ud$4l5@(e?nyel=%<5t>BqWgU4bTg8wJ`_KVM99cOQ-8gv@P)rlP1^c=utY*o0 zH)|&)OvikT4-WP5Qlj%MDk%&+;6v89WE{A|e6^Q}CZDtV3$N6N2WLm%sZl%Bgajo9 zCAppZMhk;K#(2oG`L=z!=ZA`TRg9+Qv*!48Uf`l{wFoW*bLZ;h{%hZ-!q9cE*@m6g zsSmll`|+#s+`{+bhj%WHv3UrB&!Y+z1Q)dV)pz|}iU1QsIC=N=Y_BUFbt~s+tDV-X zMl(d;VPY?MCR_BpJC*0UdAlH%@dJHKkcRk)MA;lnXh@#Z&-{c*_}AztNo=kZox?ic za!Civd`@K0^oTT3_mD_s#1S{v$=}PRJupjUG z;rP<5R)~GERJq=^3e==!Yu344*sMG~a%BB3HByS5D#nhvWl_0niEv`noVIwb?B`+X zmK+rmUaJkpwGAu>-dwonr$jAN2TyL>>lYB5+~}bQIh8zGO3L}I=D+3WaCXZppt?L( zP+>f^VWoyczeG7%%Y!+VGPc(RHiyX9U+1e8rk|e1G2>lsk*|C7z64@7vy!w2y`6u zLr_#!!!Oqd&9@v>lsbTMww3a(*V3CWY@f<8@`O;#I*I`t$73aF&XpxsE4Cv-jGYT*6;;nVUv zPu^C4!I`e`I4Pppk>4mHC%DAOhpZw+JGnpypw+4-@?%=a;}Rcilq9&PCR2mOEcoIw z9inJBMIf@cB2iUnW`B0eV2kNbm-hqRLNLUv&W41v8Z(};nmpCT-% z)Ay%{>|7LO_wA$?6ZnK+Vf)(_N!-VmMxJjC#$Px-H60VB$t~a17Kea<9`X1+fVwiK zpi8^)^tGrC0M%ru3YE2kDmNc%Yyy8j88fz7U7QN3IR`Aza-CqHF+fAJblfFJMb5J1LZsvrI z8^SrMSAPMb6|uH*g&Nn~W}=AQ>xG$g=1S!IGkjiU(e0D; z6&%s^bemX54;{JV`)_Z4OqR?>lI2)PGCAjq^n+bhue64u){dA>2k(b(+2`_>c(H? z%bJ5Mwd2rpE|gIrg!WmPU~LDuQWRsH80Ue9ynbbxYj{s%K5!jmVHF5>)-jHY;nb;B z?qRgjWgUN6KV#IK!qfbNzn|PaN{7!;$7VPx^0j|$4r4;ku6ElAO>WSg@3ILmS#s?g zM8Nr6CK=d04RL)WMW&Ext%Cv4l*zNVjCPp@KujkS=cuh;@~K!6k1>NQpCv^h_A+@U zf(=XyaPtdjA=LfIR(`6Z2B@YV*ELz+Vc?`Cj<1BLK<8lQ**c(G^RH!G;WnS~zSmr4- zD+VW0Z1^-mIwhTfV2{Lb>?2zA(b+eBF}EC}`>yF{W6oT><> zYz@xz#jTIr9^ZZU@iXvQ){mHA@)0_K_d({pi}C&b9cK zI|e4qjNz8%0jADw8(xieQ}n5hl1H@T?_qWWsO!RGNcW?_dt2%z(1jWcl5{F74cnyX0?GzfYLfD5(PzZX56jjFTh|vd5PV zhZZ*7Xs3jLw8GO@_Roy(qSNUwUciO_O!rMpF2!s(8C!1g+lw&xCFX>2G%!rYwj7aV zy3|WQ8-_d60aAltLdJxFcw^b#Q8t~HVbF$D5I2y-E_wTuGU-Ds@9L#_^1JI*Km3ZL z8>zxO+z@GnU=;$qXPk=+wwlhLf4-}rr{8ZHDN2bEkxo`!)K!h?DbcOvG00nMO@HHz zq7%2~$t}y>3!`pHHr6txeZIW}znxhoIM+6M3LGBYnCl_U;^fDR@_Ni}!cARZLnESD zB5I+wF5JJF$%gu$8gb4~nD8kYFptRUAH%7Sb0@;NKCOgaX<FOR$lD&Lt?z-xFb$Ww;aA|wiYJS!Im&Tz9wsjmATD`2$kLpib6X~$HC0;t*O2( zikY3SHbs~=IXoBPnj$ChH`goV5AKge<({N>>AlgW$NY?=+NQTy&T4zHCf-qSdFyV{ zQr|N3i5E_j^R z=xgkEbgQ&IoEP=AT^Jjf6bhR}OVG<6V2NyEYHVZ`!I^8M!6;s^SXNTWi#j6_Hv}@ny4PHFW zj0yEp0cVS%T&(7OI z$`mghJ}w8Xhu=IRg|=O6w>h}&F8{*WehOUH@S8&VKw)hpx0tKBv2xJP-1IBIFKeEy z^&@g`I&@6|!`HUIjYp}|Kcq*tS$EjN7o+bI?_zcxKAU#<-B9hzQ$RuUuSi_)4bSMU z5WY1R0q|nDON{h%f9^WyXKf|=zUeM7+%+uljl09wXWDUwzI2ld7;l}E+Z%C!7fQYk z6Ya@-KLi~D(H#h%-TJCtZOPpL>UmqKZ#@hF~S8^_(#> z?OTd{q0qFS?13w^TTNiQ_5Nlv%NA?qrRrKWtj?C`^OFNe2;L#CTwqNyFvJUF`rg1C zI8IsH7IzhXnHWd!TBsg*s!%Y&A;UkHDM8n|o44LOlOYWd z<&5%JxM**Tu1qYh^alQw>|( zh=6ZDev5B6LG;_HZLufiz*^1+61nIaTgv@{R`cc2`W11C5T@>A8?xD-L&D`~8beF+ z%90j2W^b_+ck)|Mkt0ImD#uf)l@vSk8~B_*7#AY+<|#FUb=s`)Ye|#QbRFIWdvJ=Z z{sKy^SRK)BulpDitRsJ2v88#l=A_zXzShHm+l(n4Bk3}Gp2YiO+0^$U6~I+k=ptzV zjiEz;S#t0$CnwDRl2#>LMgpih@xoJ3k=QgcVvejB|GVC&0rXc$Ke^wUKW3gzRi0Q` zTmE?Uv~3!%7;$k#{_3jEzpERKL{TotVxCx*VJl z{W6|_3b84Fq-otl(b@31u_K=0yaFe9F-1Lf47jJFa#6rBF;5O05a&LPgMH+{MpCaf z52Qobn{6hD>BiOe)D3h=A%`u9*gJFk+}Hf11}t8)`(H*=QX)B9p8Cd|_`;88u4pbW zHe09>t49m+aTQm$N9o>xVaI3OwC=-vtzWUWc2&TAvx=eq?e`D0e)|re^3Z>7s;HP9 z18YeQA0wajUVQJ@6K>-#SZ{ShOhU})`h2^|iUpvo(og?dvpIE^pMMe4P!QNSK03=m zg~otE9IX1Mua0bxi8!i7Y$SMY`2 z*UM^zXShaLx~}~64lZ8QWfJeIFjs`LU_3nuIx*5-L059E6}kI}0x(8j0D$1fTx^P+ z^q66&t-QAml$SMZ4;c{0d$AI+LKMCNePa2{r6Y*U@W`y-M?-QRHtI#qjFOT2p|lbA zLU_LXJnQ8z zk?XIb(O-;Lf4mYmlnf^>*f#3xLnUPKOS7FE&WhZgXUNin(Tf zHwze(aU{)=`I$W?DH6P4q*jfTJZ4DUN3yO*&D>Uj%aZtoN9`yPXfh0a|Jws$KNWqr zjk(=Y+?ILc{TO0eM%8O#tj7nP$8ul!!mtS5@~LwjzQEs+o~mJZwrCK~CDNPqTxkTM zsM%4o>R`axjep$huq5hRT*GwqKGnrWRjU_$cEiX|W83Eb$E~cYV^V3_-O)Lxanbhb zhda~P5s^+2kw@iff__aJkJzWrd`b1 zc76d?G0N*78=i%(=$+erqDzj15Y=^Xj-|Ks+d*7b_{gM|?Q=BuguT}>3@2`Ia z86P<(v%8cn{CKe___L^S;Kvq{EkI%o%Ibq$OJ)oDN1oLC`;>Hl$;3R<&-I%Fn!B|e@T`~Hwa0tf{}2-#oT{fAyF=I{ z5UU9>^Lw_g23pSUoXEJmB#Zx_DHQBu4fr%qY@iv z^-ArZxk0A@#@podcy05FcSwMZfw_eA;4CVPqAPAgIkq!`Mv%2^!z?y0%}%&Uru~x^ z-ar=4j1V?SE9D=9%M{cCg%8E9iW_?Zt@0B7aVsvl-Ud8ux;Q&hgWs=1K zGNMoGfECxL^rhfVZ{rH#o&**f;`O!-Nc0#yQTbsIqka9~z_jZ>*{|_Q^%vAmzWE8U6kL>~d~XbQ zcIK;1&Rir0f5w&fZSE6ZE2cmC4Xg{gCtLx( z_^{=d^RV(T4gp8>W6}K)=XMfV(@{J!M<8g+BUO3CGv#?WRa(%dLSS@F54|%^n56or z>1=_E^t(JjT`{lh8cFiYS#8h%t>l0hj!mb6>NOmytd!;4NBbk{`m|yS4IIFkAxx0i z_8WM$TNfICx}sEA(w(1t897i8J{U6@R`EVjCq64bSuOg2@q8(znwww|`->;&3NVJr zpxD^M?@1jV2?6C2e^A_2$LUu%YE3Y1Y8@M`UOm(bM@ItxO!T?8F z4iB8bzijUyFzPLc+MEK+%<0158B2G-mHr+Mp=N^fj@)K=PL6YRPXaK*xu!ne zuT?R8wE~b`E&=V1JK=_S(3v8rx!ZgTs)PpV;133Z1p7fCr#Fid-~e2aopAROm*kg-VFE)7-j^8yzP!|%m4R=0>K-4fR?5F$Hp4w!r(qwzStQaJ3Sm4{3LVS zr@B_}6K+6YmLtqN^#GE59>FR5+0nxv&RvDeOX_C;)8%ZnBQ-@p?vpUvM?KmJ&TL)Z z$PS`^St%q?8)4}RL!E`e6|Ry%9pv~`fuM`+j_mDR99r#zwHZbs)B+`y7bHHJz`yMyI-I88n!&S?$?YN zYkqviL1cQj>M6;1$Tt8dolvKPX zNeeXQm5s(85{v`;W$N4+YM?x3eQ@Fmf=rs(+VZPl+SCGPzr!MCI@ zKF^^;S^G4TZt~#RWPkc&q#t z2;c9ispJNLm(FU;2%G?&y!Z@4WH}OD=CxnsB?Q&xxql&02FzSiy)TZ_VI(Ll0~$Fk z@{%VN^)5|+NYUlqsv@mo3J^mWl{K#Dzs|2J(5GAV#T&M2Ox+^^<(GiJJwq@6%Or;h zx3!YtcTr-iF~h-bftO!9C=J$6XX=5m&B|&~+2f-g4H6H>Xt(yjhKSED{SQV#wdJ;) z?S*m5`1+0LFi4t0=T7>Yxe%)#n%cH?_mJlAh~oaeuI6owwOajAp))3yG$$jmlnFmx zUYxo=T?ac};6AWh!c!i^gf2ARIGC77f~IlggGTV%|y(G@q1G8v!~ zz=#9(L1w<2x#0Kd@Z$i>Pc;+PMJ^xjMg4oADj%Pmo@%Vb*#FCh6i*GYRk)2>A;jlO z{GY+s#Yr+14jVU%8Kh1+y40R=Tf2YhSEi;GT;JI!jEZKDg(87aZV%X~4CCqYlr{b{ zs%jpS2FY$+%zdgBbD^w1P*k+P!TGAT)!EVCkaPQ^HFW*)Vwygoxuvv`JtHK7)P0NW z`UXsrF2}QD?S%8+x|)!J3bx|-*Q?R1kYXfh2&xN*$|{U!(|50fO1csz#t|bIZ8Eu= zVsiWF?P1Hj|I_L0fG>VwaNueh6KwojI5y0o7@2_7y$>9G?!?)@3qECArW`-h5fWzk zcQqs&_9jUvU0|kz^T`_Ew=thAG7B4rkPRIuvl`HB7v1ptZuiaKpGBVAwJH3s;yR(m zmX2Fn>ncjSN=xA!IF@!`1lp`D-sgJyEjbF5WgwM#K8Yhq9UA+3$1lKYZMcnCd>x&?;TQj3 zy^5B=21|?sKe_ks3DPBJYbNa!feDiuaPz4h0$<_Rv~v2<+vJ;jIFz#-DKnk22Y=Br$7OU#2{8713|=is;t`rHSGp zX{vE3+Ys;_%G4^<(EaxsVK@mSw-Zd4fft<|#zSjn3rSy8P|+l_@SzZWwfpHM;oSo{ zVVtR8zCK~^WUb8o@;sr;w;qd1C zJ5q#sKtZV|6L`{}orot1J#+!{czJlF%1 z5S?Z;4r#d8(dkZ7)1%q@#*DtT-N1rNV79*TD$==;`+9>~Xa!>hcXYx5$m?mrON`st zVut%>ZRx(`wY`hOCh>Fdhl=?5ib^^(=747ANXBjdhxRZ?zNucosLfTs`&Ql{P;Hr> zfFIpNvo%?c6g|v;{x1&=oI=Ch`cY1N_Am6_Q-H|F zZ4PU03L;W#+D+feRaX&xa1NZ|4%~@9?o1yx3MxPNp@GMXHn&VMOQiB4T3K$=>1(~i zIsfr1ry_9WdM6v_|Exk=EuQN9JXVo2H$tB%kT?BJuX_WKFV z?a;c&^1lgYkR?C}pYe=s##6ecBZ;ZaWwY)UA~8$GecQJQI4RnF`AUzBPP52xHJ4zl z5(T}H zTkmBVJVykb?{bYI6fC3Ra-iEu-LgkfNxltzwOj|g%?1H3CDrXYEkR;?*)fGr_Q4Gd z;NV;~5so66@6w4uj2Ggl&A~mqW{zxmE8n8PUJ?(C;+lvIu4#P4M2lHE2)ATjlTpl# z97zLyz@&Wudt`VeZRX4z_$_|)#)zIgr8G{sn7uGC@&qXJF-^d%>%atN*cgvUg~t*} zVlx!ZAU>@!NFVA-MDMt%Vzlm%qfk$#~l?`Ea zu}l-&iHXX^xJ>Fjz4xpY}cQ~P-W#?cUCj`snyh`hrbk$Y40pgy#^JU`?rNjwx4 z-%Mg2k_Vq&Ah~bG55hqqLxxCoTQ!IJbMRmrbWndju?O#zZ)c4=`RBiJ?RgQ1*^=!N zO`vSa3*%}Wi*syJQt$$vfaY;%53m23Klqk;ZoFvZf}Im1y=gtagI3}#CQ^^qXT6Vn zp}C3@ff%)H;g%?;WN9)w;D|{>)GRdz9%T!ifE}L6tehH;`G%GJ`9UZ10aWVa)mkNq zMSJfEd_QD*O+1LJIuYG+jndO82Ht)vAQD{J`N`~=AN+Ds8MVBaqinjZ$`yup)N1>& zhdE?96cf6>pnZr;McK?3RV!PP%B?+5Ev`9jfJ%wRr|G(C_vUM$JnSt^sqMjf#Gz!{ zuVq`gb^^zQ;$;X9dvUqnq2ykZ`HmLY=6|eZ46V%<`R3;?!RP!&+?+Zs^%zQY@4H82 z{pJGj`5UNqQ5jH4wKCWMUqR32ns(EPW?cu{468&l1;GnU&)MHMWL?v|yp?*N_2dwf zznR={>)l1x%l$_^&;E9eDw*g`0tY>7b93Ey>3P+AFY*Ry2VDwy!DC!7_-63ORW8;p z`kRP<1pkfGYNha7t!KN+k zY3)-P%7nsv9o|Ie7j`_>C-%0Q0uwe?ro+|kiEBu(H?k_26MD>+))0`)@lg|Na77Fls=8-(-g)^s7`9>fQt?^L`A^>52w&*2ux zI-BhAEXbpf3Rf;YZFsa7`VfopFK}YCFb(EOWG&@@f=MM1QXiw?s?jtaZ^j7-ZlML@ z&Y&iLF+js5?d8*7sQg3V5$R0y;#t_A_euJzsjN8u<+Jj4P^&?2ZGm)W^My0OS>D`{ z_DhtA{485l3^1ivTH<#iu@sLcCDCvT^aqPZ3>%WggClCiJo>rk{53faMBcdLo>tE4 z)Lm>!)zL1~fO=pmml>Dq=IZ>fbR@(+p=Y$;sOX~4h;Ix?uwnKA-%>-_BSGC_D=n{! zlh2NSeiaAHGx(B0T_mQ8g8>m%2i^8ULIzsr1EBtvy6_V@OW)S{h;ILGU?eFuI=@YI zpO6|QhmU;t#*d!ljH%bx@-3(u9;*gt1g}^O?)cXm8S7HuB?kC%Px|NnO*3>P%ejUv zMc@ot{A1eRpvudBXdp2w*v63eCf9!e0U|(p=DQ7ajW|FoJ^!|Ft!9FK{2nCFgjQZ& z76iX@o)U7oJ=1NKO*=raD4m?4O5Vma81|g_l`G22I?-VCssu5E&V~igTw@f?x( zY`qg)-i}dbW%A+f?SZ%)Z)U$jTeP??-+spqc;Cs7(1ks^PoCS3$z`_{T`k5xgev<; z1$zZ^+vVuAOh7^oiEsjr7nj5@UB$nPSMjfBU$5}9+4jnbF*2Iv%RE*}ycx%zLy_Uo ziWg$(c*Gu)@dY_B2md|kpU7G)bVL+QK`I6%Y_;cZ;_juFmWwU`0du@*H?5rmHgv6^ zwJX?tj<)^geTt^%y-dG6Mz&J+Re<9up>Js%LQZ1b;AiV=vH15Z`%WG1tX?06jTc@4 zA?{aBX$wl0<*ct4@Ocgv77NJSN@r-e?T z{dpTIhUCDKc*FQsWbq{;8mcq`r2L3cvVg6dqsRT*r*Mfj6|bE*)WD6#cWn4A3^x(w zIJvldb*%mlyHMuWmP&{h$)D61E@X}%D#_dkj5}${PYS<4aaGHbIk9|7appc{#G9&s z8CKgi+Njg`e-%*a4b|qxgBV$Qs$cg+NZB7eGyTCW968i3>K03W?=AJpEOJLo$_6UO zqSzH!Am><}&?9N-&FK9%SPS}*4B4d+M4xW|cJ!*z7=U7H6^*vM&c&Y_{Yl9D^71->JjK^M;>Sjf6rzNl$uf=rbM)**1Jd{BT zDfM%-SIg?Yt48_x^Gj@w-+qG`(Ql%|VK+4H^wI{eC{fP;`i+GqPzX!(T*V8Pg6CqOyuigUMU6i|^g6 zPnbUK#QOei6crklkk8B8{y-~WSuQL!TWc#+SZ)8^$A8Wbh4AJaB4$AhY%A+j^f4LP zBqQzwv}k7M4+jCXWPucjg?#80m7$KwmM~tY3xUghw&IV$80AiYl&K&I&&SQZ5$C+f z8tvIlKo9|=V63vO=%COU5jb-jgVN*RxccTxJU$}uRgDQ$*23F}ydi)-X^rRnW|T*1 zYgjs>v=fHk$M;#h@8s#zNCEFnr%c7)3_pq=J*UJm!SgMR&DN*m5IM&wx4@<5-uy&Q zmGc;BJ!D1ns$<9J+D$o6R7H)9wo(MaT$%?^6 z&!buz6Rx6gW+mDkQ`X)VPO70>r(k+P)iI|9h0PbodW{jKkycI_xicpx%uL4Buos(o ziwfTq!>s^;qK!jD(;R3Vx0vmt(aY}L1d^?tgAIDiBM|)?Lcf&uWANK6iqznVACWhT(@)}M|239wG`@?0`3+MSYCnWEb}gGz zd%*rpyeW1x%RyE2VQj8ar0%N!8IXs-?>%(=BL}n!90N<+=@NL!$HofipFzu->#ryv zD}W=&pCp5lQ8VzPqUk(B;K+H;jq1@e+EKj zk5`!02_XOwjJ&dvvtUHTMm{X_W(mF*fSh(5{_+%I=Jb%Uom(dcEDsza9Ds#JcO=%& z_C@$VoS;3U!^1u662BOH@aTx?R%fP%bcsu2_?$)G?DDC@+_x8r%YTA7P5|2^tY$n2 zMG&<}*WVA)dL(Jqj(x~jC0hI?>0U+qnC_8a5%DZj2$KAWYf z?*}f+DGaI_At}$2YdUJ@9D#9gFVcqBs@dzZ1($)=PzIUK8^_LgW#gd%YPIX?0<^t) zBq!-li>(rgH;7|ap#u{0#dC?+mRZ3y0a|axND&%5gLQHY^8qJ0(?HWR@jw@14J_8h zX3ag~zrLb~7^{KH(WY|pufaH#K9ck$L}4o5zr}708EXG=6-@-^&$XIfPrq#l8zY&;~T^o@gEeZ`hf+L&YKS%1& zEqbia?D#XBHMe=cD=?0t7=l-Q7-KH&*k=_SnhmN2+}VdX6)_=4F_htr}e0Dmbm z41U#GF5)EhVU7AV*Fj4Yv1j48d#f>iHK$FW-zj)M=RY4^P<%O0pZM&h{1z8N?d--) z;pMF2%MKf2MpW9jhkpnglI~}hwO>^h7m^a@FPY9bcTH09Z^d%w*s) zZuU4==5wt{qp-_8T=zaFBviVObGkx@0kr@+FU7Uu%E&aXTQ!F(A2J_f{C!V3%C!lM zLSnRfVAQE&?uDPFT&q5;2Oq$w1nu53=*V|bH5A5AwY3s(J$vA8^#zWe4dFi4H;H(l zFuXe7jcBe@g02H_@%#C!R*N7C39_fq+2AN9-8(rABt~`^J{rReHBGLM4%wJ!did-> z0(USl(-&`IqlU22&thbfG~eC(w632C40=e5m>6M|kcMekhJW4y8`7Rt^IqA1i-`n_ z`DJ?m$|O>DT?1N4w!Z(DMY2v+MK>Ka$}YR4C*>nQhbw+0ISS>$YyV{dGU|Slhy0Ns z%q=-#A49NlNd_ic&UHs<1%+sL188$LI}AL(vI^-^m~6%=#)%wL6_a!QIFGcVD>{#^ zhTB+{`Q2XNi>2st)!M}S=G$2`E$RVKul*37!(9O4@?tDRBZ;UPszi^82oa8;OQ=@K z8Al(18h1}Fbfn)^>GFt3&g~j@hUgY_iz1WBWa#@u8naSzDFWY1WppZam zx=7670B;?nqA{Exu}?XGupT0ORsZ&d;b0@g(YbL*8Y^*Z%WZW==ZCh?)(61n=Xl!I zqwCQYaPi&^e#I!2?t#yw{S%PNvCCiYlpuWeeJFBu+ms4UF)%*oBW*2U(!*`l93Ils z{Hf`D!?_y~^jX=1GBx+!8r%VM`o!nfTyd?0UCLw+l0o7pU)yh0rqS4aMkx9TJ&umS zrGL}R;$8d<0AN2KqmWMVX63}F)>aN;T(VgG9jo?s^&&Tw(&|Bvq$AW~ zJ`L%z36V!t33kgjxcjh5jv<6GeL$KT25+Ok9)3`<6SWxUB6XWBlT|;NCFWwi^1BPs z#NDK4u>+;@ZUopFl)8Pe6P64%2ow*L>JncCt%h>j#iR~ryqiwD9dFK|wc-y6BM3d8 z!#?o#8y~?ie5RVMY$U+EE0UZ*5_8KH>eCtQ{6j;2;b2({cZV@f9DpN0%nrw5Wb&g) zq_7xKO|TQFT#eofGr)RHpst3w=`$?(PRRPkXT zYhwnEX^|?aXh&0EdClH{+k4h;@AZciJD8MIM}Wi}*_RvJlHe}I6ev$ms>a*}lVi>} z<6$G3x%W-`uIn`SIgJ`b-|Kh*TkYpEm|eN){z3BX&O94+*Y10JCa|q|0;GB~9)uy| z!?Z$9f?=|mw_i!8l+Ygct5NI&udAD)d#aaz_?qGXXV(5e%JSiOSMci$fnAaQ7Z2sR zsOX?ois;XET6h{oE3&wLK%;cK4z!CzyOkEaq0CtsnDa@fsElFaN3;H(;x|hsMx=`C zmj0$SU*@qsG?ASb=q7?d=dSg@t;zjamIem0*nIm8Lm}DF4dseH;Kwo9{}ZkR2zMP_ zvjojwh%-=xvNG>)L7aWIxhqUH=_Lu!5+l%Q9t>x8H{9ihq=~=FDu}tevp15v|K3cT zYP+S#@tc}181{0j;V=*5UEoT7cunWa=9e`O#;@qOfes}ZJUn{c>%C^JA$j4JuVK9M zTZraeDL6Yo?9;?wPb2uHkKBGQ-JZmOIEFBp`YAs%VbZRehHgoy<h1 zNRg^Az;f1gYxZ@`$2S!sHCRT@mAv#Elgo4)hlq%*p}tyCZJwf_#zY}jPB7{7ih(Ttw7tLqOD zjMKWa6(5<0zbmNKUHjM*nwu8g#&9~;^_Gl>Xa1bM4!4_@Mmx|3^b%G826)BC%8xwK zkj&-YxW?3nMIv|WFpP~12*htl{sJIb4lA8I4Euy3m>rU_)KM42r3O+LVJpde3U8<%8+{WEe^T;2%n>_y3<_2Jkej~s>SErgz&2bqC6O?*j@Lsw zde&r~JYF;-{#pHu3}Mv&F+ZRH88iI(fG1z)Uhz0wgO6pswEEpJ`DFX(7Tado52iQC z#JxV|;|fhCr=3FVNm@mVb^v;{t8yE%`-W8b}RrNJNB`{?HOEMHL(Vtt?y?4XWO z?3kEt|4b-JCaf?2vH1BOM~T=~X&*@HyZq@Pb+!Ya91cf$8JU=~r8Ohb1RVJsuz_>a zcd9}xsdy@Kw@|0lYOr_x^U?z!P27~mEnhwS&S28@hHw1NON5u>5sqVf5V5Vg+Ccb_ z$}Lj}gL0q2VBlOO*`FsoQH_V3IFpbz@blOXg2Z454bg3!?7F}2!*+8K3JAmm&-?cJh^jNm zJSaYnBco{CRPy#MpSCKE&0YT}TKDP-uG@Y+^nUiVi+Rx*Fhgg6td>AhE@>{J>o-w%Aj$tiiyw*6=f8@W>{m4f< zax-V~PL{!N6v_(KCbfrTbn2w%f-p(Op?m1mmY`2|3RFCMFS}KmZw&!FuV4khlDpj7 zZ!Ma*T3bj| zEVj(-{`Rz$vT+uDuXJTh97<@mmCcC%u0L1FH2(+!^m$CTFJJB$MQCs<7FPMcgkVq8HWhVP>Vo;hLqts5E#ErtvXAz*(<6tVl2exWs(T`6MOemK&SzIHP z%!Nd~Z{kbU0$w?D;!2Yk{lX=aAM*k%-1oc+WNx#l#13N_7+Yx^ZY-)i;Gsc<@Ld{d zD4}MUc&J_VLf*#@7@pJjQZ~z?2j)+aKJI|hHo>}sV$UP{UljF%3_vB(;GmZpOx^d; zsG^}3m-lYJuzXqyZjV3dUZNg`0etR4 z+!6_~pRhbYN)O zgP0j^D}t88CE#tf&uR}5j)wZ`BeN)#dr${%D@1B~CwRUYccpy7Q{M7FJm32gQR=?Z z(c<8Y$p4A&dx=LL~L^qKUEU*V;1aX*@Z^(RU96Wa(aYGDG60nj!6{f@18VzROoK!;yqp6Th;5&r>I+a%df+;?8bt}T)Z731?D(!^{9R20 zEStM*2#edA;T`prjL4G8*G)IEkh$B8Iwz4v4I$OwqSd`BpK2Adv*4h@^nRgtwCFq1 zke_j&(iF-)P9S%R9nm>zJwF*P6TB=5>|Bg{%X0Vs6h0jsF_|B$Tg-nQ`{iU)R#eA@>|n7P zcTmSPA4+9LneMNIQpfUMW*SrPj50Yr^B?$2d9&y^#FPrb6orC~^6t zQjhl-i$VyRf+Wr|t?kZchm`$j?<8h5B#Y zEfrL3i|Cjy9#q!4ALe{?&L8(Ny(7%_{tNAxDouoC|3uCjGD{EIF2ZR%Qo@quR>bJT z=WV(8r=cYEb^c0z-9s_L>b}8PaSAM1MfV?%5d-%zfr3Ogt!YppaLmB6DhPM$r=rgL z>3jY({+vbrB$`3K*hD0{cXAb|G$(TSF`}JN_Mc{3M)jYLUxr1_<_c|84e!YF@(Di< zyit|^fG%3n04O%$FbM0k{K;?L$~6+p&_WO>0057~1ikw}Fe^I@+QzxleL`zj;>=|8 zRI4G*y7k!U)9J$l2lmrCD;~A*gbKK%8R>VI7}m7%8oi$m3p8O9Wp5UJS5$i>QVgDr zqwD}7AA0Y+TSR*g_Z5`f_cx`Tie8j=wFf4Ow7jqF{^{ zpL>Z6GCjh#d7pEUyy1*|QUpfvA)>;8~8$|cyzWRY|%(RqIE z<_YKtMGO^*In?h)Oy}b%C-xT0omYt@v%EvkDZ-ggYFW#|laua`Y=5KhCWzs@6r&tg zC0^NilF01cFu3%FOGxH?FLN)bzR7Hi|DMXIt}I;Kr8+}T2rY4m0N+jiJQ4jh-;^%k!u}ltwW5Mgz(IP0 zMlWupHZ8m7#Ll^^9$d`#$j<*sX8Gm8l}K)tdcTo-{JvrwSCoq^cPe}^-e7OC5AZ)v zWS)kmstqMrneDE;v8Y`d$O>H4$c65GVb5*}(~6I=DkY!so^F^EQAvG-{b!1U>2Jq% z-;ey4UDK}31`jU1&q_)}tNHoW;%`W6A@ToyGTWMy?G0X!i7CDl(;yEW&klc*o&F>~ zkH0FZ@pAWU-brL;xOQf%VP=bN>DQ^(;pZtnvE5gjhi|{H)l+&-RE#$<9d4!W-V2qe z2j(M$wlj^Q(?ZvCJYpE7ad;was33w;4G8nEKfX=7e#|fTLPopTc&g4Mj zgOEzkimd9U7lIX^mi9$Fp-+*v)h_xkzrLzOPlzgPZE;Zxy&nBLS#w_ZeelEGt%J1P z=^5UIWkKo?r%wfwp%Pg=13+tJkJPw4-D=AI@8KwS_Ag0)&~J=&c;!rln_I`^ zAE=K%&J*nmp;(Qi)*XYs1-h(DC53CHc;4;-wp zuf7D}%~znT>&c+pdzf0{}Bp z>z$(uK#SG-iC8q59`e+g(Nchw$d`wVS#=Af{*8>F2ENeeY!ba|?=Y{jzPG|_fTD|kU z&Lsi73aT7F`{_r=09a6R<~j4E;L^8Q#VCi6=>Vx!nFi8i4En%iTOlcKnF>2A%xMTl zvR7LUG9Ckcm}yV!-Ok*b1ZaHndvYbZ)!{OQE*le1-pLRRCtf7ohB!N>+@`Hgn1>sB zXC8lXxB}h~uIZRmNmOly;26x7uL{LA71$uvV-6~52B=xUC zwe^pAAnS|OZ*WUdui`Q7hGAyE!Y`Lc3e+Qx^(xK#=Yfigcd?rcO;}>kG(93IWg74Y zumW#GnuBVreiUjRXk^UTPS#9yKm@+yyYV_emlgAxpU0}+?6!`GXARid>3C!raY@Uk zp{ok+1>6!`91?ut+DT<`TbTd?0m?E)UW;$EQqgC@(FS!7Ui^&597vd~KujZ`RH#*A z9jcru_8jcTyTp&SeGo{Ddb1s62ksyq+~~K5i^IfuKlv=a^+}@@k~nRvB;ptRqn;e^ zEHFtuZE$;KswYV1Th15_&FnA#CaU(QbBkY<6whTFEQvp}&KV$YkkK8(HbA_-uxvl-_#xi|o-rsP2GSM=gu_51)+ zRVDg^i=m8Ch`m@SpE>yA)en5KH+Z*Sab9gdqtERm^=_Wxmgh8>bLu>V&iU)m+PYoe zxRvKzm3@m7k~1Yz%P{HwQu12i{2_lqsKpy!JE~=oXbO4Ld?Mbi9ga?4DLb}dbeAky=z)uGAx<3)cS{h@5)xTj^h zpL>mlIwyyL$XSeYqZiPPOqd~*zg)VN=E@`TQs`(9HDvCas0@4q+$AH8pdG*3y7669 zO`}&3gYTQI!N1dC>Jib?g6a`(HZSJRMHfm-+$CisdQ_v!A|vm!!-5a7DeaGovD8?o zppf9X&dH;v=wLexYlD|>C?8oO>xQHBBY0b@$;-3lwI$#hqhs$X_U0+4g&$bWcgOJB zPVnp99TjX}+G@HKU^s!puh|mb=9a+Bf62`TJWFt9StA*6fARAYLgvFE4X&H0|0cbL zF$>Cyvm${tH(cvvaSR}N#wjU%xCpvSY`wgV5HFO1J`IbgKTR-6r_x+{)m;!HM{x?i zQ#}75N2S&I_15%rx4mV=`N@79;162#ln+4?k{}7D!sZWA$TA6JAGn@Cc>QE&dDhRz zXL{Suf!vue#ix0B@SU<9q6m-CkER7SFqTEstI~LJ@~RYrS&dly@S`=NkxEP5ZzRK^0@5P#7LOhCSS1cuF+S7}pO68ca+P{WYz zE<7e-%gKq)a4akdEP0fqhUJf9_FC=8EPRiFCq-x)?7LFG8@+^2_z^x#?~JFCbFE(xvq~3UN+QMrqHp z@W*1urr^VwUj=Dd(DcRE#QQHnvglnOuT&ns^LoMGivB_g6bsy`Iba+#Euhb_i;nUu ztV*dHHk;A6#i^Cq>33q1N04Z_8%ptu<@uk!5ieJY*m)g;I492%#2HGttK1Uo5rBr@-xpL|-7>6&a_`vL%+T1od zM*ge(DtPc8sDe9@ur?@t(AMRJR)X>fQxVNCdjiaHlqlVKbpMpP_pWX zVia6Xun8V;81dVZ83j=uPqxbh9Zw1Tgrg&pohq zf*k2?TaUPIy7~Lw0I0alCmj1ZM_L$YKFoUK*@lXB_+D3jDFNFvR3ZbK@4~9`2I=%s z&QhlEnkFVDOOQZ;43D=Q1&ynD6FjikpQelwQbfz8VrbqCqzx823ttYT(f-36=`bas zR_k~Bf8$;*^!D^7u)ha?%T$hlcoH1gQ&vx}4r%rC$$T&Iw3EKZafk7ix{@b?s)jwu zy}ppDHzH`04JXtGd?|OJz8G?`3SBT1Ou}SrM;@Wt7D!|U(p}!Se6@FX#GB}=#L3yU z_!s_4wR$XIve0qZ0P8Uxo)%9nQ@CI|-f(S{_{CURKK)l!h@a^uEs^naOIa!A+g%S- zrMXL`jAXJ+^SPI;o@1Ww0ge%YK8Zm2-x*hM6n40k-XoKsqpT@OXjD3|w0-zja+~TS za_^zsz^_`Th{b%%!JGq)Qu`g^{f$ZO&AKfyDiH3izP`{(D)QSz-@v|TbaePGBdM32 z172wbBFmIj;4O|ezO3P=V*Q4RhN8)X>ZuZP`>#= z?@BW0DGhgl#1nQzYGx_KK7W4q0d(vcPQglvA>xZ>n_7912~#|Iam`PK8#r_#F8uuh z)+62MnQvuLDSQ^GjL{j!9WTX)Rz-x+a$iChS-5ynexqO}guAJ9&m$3@)y}BM>FoER z08v9qXo;Wp^?Y8=sB1#=C2UrC8Dnw#wzIPQ$C(mQEP11_cz)X)TrYXM6cC(-Ufg6S z^@~dP_|bT_KFju6U-@3GjwDy{gYtpbWOll%>wfZBc1kNlcg>nu*}r#l2b^cmYv8&- zPLvnTDxp3W8JkV!_dL0@p4j!J%bTbQ6&V+sSul2sX z;34)rSRar1SWk!yB&1^8nEXW9H}hHL<>ei?e{42XL#F~8_@3n|`e$-R>qJw^uxFM! z1~nL@IkB$qc6a_|r|Ru2Ds|GjDtvmNeVO70K$0&+CF)j%V1x)*4&{b8ul?*t9lezG zGGT>0lHiWw(D|4<9=Xs23XNZ6dY|mlm4Ms44TH;=5p#-l$gR7Krk3B{3Vj6CPv=l> zVhEnqWdSQB)Uj#`M~YoKzkZnd>$i^feV0h?rmq-B6y zduYh7iRu?7R*%1eIO4;o3NAgh%BKNe9@3ZZQJvtDw7I4l_j$}==n44OXfepZ5Uu-q z1xclT-;l8LGJHQh-wr$vxgR6Y$P#zbMsAOT`jHfRYhim0fnE6ERhSvY80Dw5lYbF(g2cx`qfCG5S7mP! z7Z>UcX6o7nfh3GS9hD*$y#uDAd6)3qr@p*e!|DGGD2Oo2?6b`<%gACGD4#I-RZR{q z)Ewqx9atX>F`YRT_;EAr&)A5pi2c`vL(iXeqA$s<)GGt_tX@l(nq2D=z5D&K+k?@H zFum7@_bmvjCo8LIKU#R$zqWmT4tAEW1ET{N)x7KzL}Y_o!l(Q4#NhI!K?I-?IGcGWR~KcIhw9u0Nt!#}&MeJ*L0^kOxO- zpGB$#p2ShAghhX__##@I+f9(M$&N!6j74pOLjlKNFG{HHRtIoaR*E4$%5QC~!sM5b z5nR6;1b>^j+Qvab9x=$}orHBHW^RCtB5#~^wJx@?=q5HxPUfPk!pwuT4Vk~kO%npSSC|F8k+x8jTEKOwe0Ud)7b>+nP#rpofQYM_72$Hs zibn9*jN$3gu4t}|TYa;5!1POf&d+XYYHy996{Da|bd62z{wVzGiP)k}hLHG4{I`!~ zj(T8)&yEL3`}TqmuEN_|Y>?&L%r>L_M^hQ3v1sJbnh&Q>4{g(vBho7cV$+_vUg5rM zz6qalKFsd641ANrR=&xKWunz8Smhx|sdStVKb^~Y=bq>S;Z^`qBh+CNuvRhw zHG0xeCS`*|-q(5g=H1=QpR2SS^Ppj3@`F)jA;4$wTkBtN9HW73!=sPcxjrw*0%?|` ze95}_BEqQzUAK*?K|hvkq-q?>le?KC?UK{+X2I)TV5>%R#snvbCU7ojKkozJLr+=+ z+qXM7ojH@gNXmL8Z1Gk~)%yGKt1uI@Cs(}UA4UhmPSn#^QzDePM{C0DD3m% zVD1;;)OuoE1l#26$+5J8IlCiu&D%l>iGb4w2MNAU z6dfqkPIimZ^dua=$+xP2JZh9~Am8O;RB0|({P4MS5$}IupafG$Z7PZ_>oCDu<2lRb znym@^_%d+@LB-az<&{JeRVcr$nL3Edp-I8-lG32Gongxj2SBR4E`N-Znx)TzSMEUA z56s(Paxw7QYgr#~z2;usQL_^O|0SY;6OQ@rtN8goN~excf{DVOk#l!O8LcPDe-Db5 zDzd`CV<@JlIncxI#+Vc9Lk{~*a$*nOh`PRCX1L2n;T=6OtT4?EQHlJF2t}grC7*|~ z3_X!I0{oOp&4Xi}3d?t!hzsu7Au6nPk(-;u9Gbyt{`IKJCDU7hU<#I(oe`(uRO?ngd14% z$9??-8biiiswGFrte7e-8M&J&Z!(NK!lEw$S2{4iz6HKi9#ntc!P@Dn7^OdOD?lh}a zK(3OyS$3m9^%nKgwOnc9P2uq@al1&Zp5WdnO0@xDcrmEECs}GPv~~^x=doGH%LkjI zG9@ULpIXo>r|gmr@i9J|m42UNc$t<)0JI`8Fwgb! ziK1WqI7LVuxdody##9A-J~GJO0x50HPmbg>{?x^4?CxJo%m+cK<%6DsL51mM$^ArD z`Ch3@ofh*wq1>X2vB3DH4#honJvyNOs^va_D7g^|8-AcH7%aoCv1pkRe{|`S1RRjN zi!Dtk?9bUoce_`h$yTJm%;gawW&QLJ2#Iqaz_QA~piswF%d(Q>tn9jJ30XL*AUO z45X0j{hUcszHwt0x>;f>6ri-U81SXcFHiw^#%`a?cl2yK2?$47fZ2rVP7#JeuG`t} zV(Nts{hRFzN=S|Ss>V$^moeZp;^+@W!q#rb@&F-e66!k6w`aUFm|$%SP$<&??ahQq z+L;gNxs&9GR2~63uKjX*h=$XuEA^x-jI?pA*CvY9K`(8HZkw@_;Zv4=BCHbENHhK; zQmY+CM48ccaXOf+)T=&!$Yihy4(4T3iOvGdQ~1!gvG8A8(?CJCmZqBfHCyAC@?YRG z5geRaAQfC`(Hn`UhW(uSzM6LGFWfVP;=s@od2eaE_L9w)&l6*_>YIq9?@n;Nx?N`2 zolaH}{;UP~L2QWK8DBuV?o$ta5-JGE?-szS!K3k;Qv3CxO~JbGib?ov z>wGr69xesrlFM{$J#n~64~b9!?GPS({`ssU(100*GiVO0(_7esg}tmC>S{(X@;hQ) zYRyn$p_|2I9~1IVY{`bUz+a^Ie}N3aIo$9AALU)x^gx;1n_nRQ^lLzUh;6K7?N`sc zqya36XtcGI3{{nSar++(y$m`2hUO@*y5X3TBuW~08b)!Y|M&=fZ|teDN|!apbKcJX zCShUq{S{TzRed4V9n@Um^|IObui4Z{Kf43+B@=lcKsY>WdK}i5ep3hIOUk`Q=gG=| zwv;bWk*@=@0ZC#n?tuF$>eU;1tZof>LU*^&F|Yw$W+aIa%VWmJE_bk-?PMP_eHV*-=@f z9Bhug7K1HU0z2S5$r+gK$y``N_{RwAoM$JnPk~jp#?9VaQ-5$+7P&k%Ai&k3fxK%U zt>*Xgyy8dwurN4VW!0PY0^UbWRn9nJ+&6osjJOx8w*Y1>LbXF!B}Ho~kH5iV65OJR zNu8q+DUU%xY$6cn9KKc#nyD&ld8}L;o>?X1dP1sxSfk`uH%odAdeTu4Ho3&H9I+{& z=9EZfc(^f?y}@7~<2VICB{?c&tlYSoV?)VVB~&~Z5e_X3CENynPZ=OPf)bbknn2^J zg6Yzc8Bl47q-1x65ko8QP8LTl-+yXNfsJD@0yPPFJy#3&KAWmFte>1KZWm`FleKCy zhrMjR?6MiYL2{t5G3u_n35K3bDt5QQL}-$yJKDYAHmAa)v%~FSUr&QSH~&_AYA~*J zvc1Tm0T5>GoL|jul1~5<%)RT`Zfu6K+57O*wm)aec9>xdhx6yaF2?%mEuX&!sGky_ z?#J3f2PNSWYk$nq&04I!oszm4oI#37XlR_KT9XG~(BW2iVdQ=5jEE)%CQc|hfs4<4 z-I>t0@m4T{y!rlM33L%$67zeiFEpQWv^KAac3zKC8r#9`%W%k}QQF9|BTH#duee#F zr^*g$g@W-PXlrL{qNT_Hib4WTUTS>$ays&Bqz7vAp>4E~6=a?a6TN@%7$0zP3)lyH zYndg~??+AQ$qyfI=fD4aiq3=Ksko+r|D0}lxf;PW$2_zEUKs^R`fu*SVs4l+Xou=I zdDF|4q@EGh`jt-8l@3A*W#Sc_vIN~Ymigs?{f%7T{0K~L{~V@=K=*1oV1GZQ8UbsMa*p$Pkd4*jwR zzoDf7SrX1euF~mI4a=qbIM=DGBk7>`Yuf;6D7-D!JkMEj z{`z1h^Dtu69V$#?%5lFli1c%q8_jP-#ps6By{YnSRBUqTEz%tE)XIJN8}HfiEe+F{ zhcv{7+9EUfV36hVVh;ipkzZa^8j)4P;O$(EfcmsBuDKv(48K>-26p(`*}GNhrYTTt z&(%o^JaV0yk+5SX=l{&JOUq(V{gvkde=!uB=Hv6jFiyrP18pPFP84PMZN@&h_Uf^r z>vGG9hkUdLSv4ZqXRy~yM_1k_?xgL{@6nbz7CDb1-#3vl_>^eZ))szFdiB82OW(mx z65GY_x9kr;aB}feiXZJ^*TrwJ-TXax$6E^*LS}Raqde) z(nr{x&DwF$ZPfgM=P6kBws!>vckx?ELqCD#TDiyKCY z)n;v0lT%GN2%XgCGn8$aK_j!C)h%ki}WVJ z5X`5H5LFX%WQ_cQ_nd+&Jc@~jh!W7iyAe~0flgGaelru)>(LPevWQnvFhw|O1I9K! zfD!Qx(`G#F(_oua)8)t_lTvJQAE zcUFu#sX}lsVG!F>{$$pjOf`$ui$p`>GUzucI`^ELuoKIFBBr&_n!Beb2OX> zGGl_#u3FkqAr@D?1nuZZ$3uOlpnT|Dhmt-d=Xp4$py_)MEi7wKc-5Na#w#6poS0)T zz}8X?FZAb(0^Lt?Ih;f);rUl!mbdbxc$PPo5|Q3-(b1JlgmM!HgFJJ)sKh@s8$?2A zc-y}0TU)|T76#^ZL@$Z2ZDQ8Ts{Hm_!9>E-9lO`cUcLPD7v(O8;T_RCO$_PKT2gSQ z!A{6Jj^LtcV-z1N%%=1DyrZhA?k3V(X3(4%QuM2I+8jX065vPJjr{+*5vq|sH~Ye( z8@v=`-6_;{LaZx+*s_9b!z{}~MRsPRAo5Mwbnh%B6;<3PO2|0?f za%-Z5UZMnIQ~JfXE0Y*|`Cbfj%i}kc$Y=&rnrJF$xVUX3;a=y+WKM<3H~7#IQXEc^ zrzO4{CYEA{vlSHclp#?36GE9Irb*8e82_7+_v{K+uapgT#7BoT zhAWjd^qp)@&8djI%SJ36`GKOY1=3gGFOcpI#XE6m9Ccq><-U;|E^gz}E;EW?4o7A< zYQi@-_X^4$5}u5zrOSGaNFgc=F@)t}!@Iq* zA`x^n!{ijtxneN6)JapJ>TTq?vC`o(8OiW|nqs1?Y89?Ay?4vUyogE~7qaYT6ie3Q zl&knxO{CQ}CZ%dx63!%AX%BZ+7NW?w1|h13HQk%XkoMu$@Cm6pfh{pfGev1<0H zHn~~%5KQ}Lfv%_{HwJu(SDLOS zFx?s08`2H`(V0Y_jJsiQA*c`Pb#Yb4Tb^-^v}8OID5gqDHITI$9tGT{$W4NiXQG_T zH|T4x2QaVU)$D^xHv2|lG9r}k!S|W*<1y>Ey;)WK%3q7p;(b{u1=#kFkAJ{Jx$^mI zR%=!xm#wvl_V#stIxb}yux-PkgCwn*aJZC>YX_84f*|H|Lh(mgeM092h9C9R601gR z6ERkZ9M0OCL*yR4=AUNUV*Eib_<7c)8Tbbd=bGJR%zCyfG@sI;@id0aCG%0!SpkuUen4TJ{wD=5%tDa$agTbeD1mA znS@;*54B0$q!m4kXT3@GugUpY1usD|^ic7oTJ+u?BtG+AOCyI2oDT0Rjnp9w-}xL` z)BTQF(lzwN+QVWjd13KzTqB}KcTE0JZRbpJiF&rR{so9!*>Pyqp*0Emr`<#GGFitQ zJpy|fNp+vZ9^X7@6tfU!b>cw=PceJiK&d|=N#lYbUy;zSS`9yHNKr2npTWgo6SmY{ zpXFmC`4FKIkd29dZx0`Y00YLh9rxqPrT~%Idg#Emg2wH{c%Ix&?s5w0>g`GKCo2u_~hQ{N3ALGExUWq{U4om2vJ?lJrC+@8~8bT2t1wa6XjbQiY<6od`I|8@={mT{4Vz$d`mh_bP(x#kH~rhMpTo$ z#=Zzk!>hEJ+$$*7WMA}InrrX*JMK2#?Sd*84XN^zNyfj)8npHZ@jK7b znDDBZIO>UKTRhgarLS2tIf}d1-~7DJiP2oqP4OxeGNIi3*}ReFL?T2G{>$<8Ag$1d-(pk8qBs4y%Dnz2%CX7 z!u)^4aeRz;Gb6qE^IuOPG!gU^@~u#;&;IrPG=dkLV5AScxvpLHvrbI=o?SKel?rmj z*f#QgH8&=io^B%2fhD$dg2ccE0QTf?6PWAcEp-F zQw4r6KU>ARk)3pgCw?h3)I_K--bbnm6gwU7pg$w!;UeMb0iz90X8N`#q zX{ZaNOoKjj6pc`!<~r9BFWM8%y|ebX1s=$5m;HlZx==7c$o_I~K0+1EIY3LIR8bD= z4iC1oM1r`|*CJrVO1A;a34IVp&a$lM)(&Q&``VIH`~BjXJ_J@=eG59kwpt1U!A42J zl41LJ1s}nRa;z~@i0ik8S%gNkrYklR?8dv7wjgi%XRU#g{)w5u?b39?4)5to3-XhW zs3<=7#F)JC8!OFFe6=mih+<({VL-^w5m zWTEg0L`Rd9E0(JnG}tB95`SZVtBfoJ>ekADsPLRt^(G+?yoWw-Z4+=mr8)Cg7P~xu zQKvq*EHv4ZO@6sm`v)QzW^o$mmhHYivrK7t^(8Ev0bjROb9tzfVLLL{S@wd{K_!7F z6bboCGc-7cM#IEt->6R9rCW+;-j3$Jf9Nl?l&q-a>Oyq31D&nCG+T2FhlgC0XG9%j zl{d{VXVH!nL{$HhqxfpA2aT>$eV_nA0!uc_Xg#7PSzvK^?Lxkva#URR63U3{i$<9` z9+4AA^xbFjc#%sAUj`NF+XB9w{JI**CH5M0TR+Cb#cXD(Y$N~`6?D|rft_|%*-(&r z+P?b4M=;Ql<9BiNcn+Pvp(O%&yQ3@88goyvtRG$)@V)#oocr5mTFXNrMvlU~$Z0*c zk+~X<_Rf#)Q!>StmYtG5KdhU+m6pX%;Rii=Kx~RiEw2f_>KR7YMbam>*LjP~Q*3DF zRt-Hq-Ln(oKhLGa;)gzz={Tcf#QgvDda7UGM5jUO{IJchSKW(PU z$FpK#HSI+PH&H7|_Ky`{yt6aCLa)x_p-{7TuRvW$*a1K5p6f~@waIXJ6?lgCO#8mF zWxAFSg&5PFRHti=eyKA8L0OXs^3}KIgJ+{p#6~5U&)RV7 zb9n6HotX?Wxq_lqZI*3U7zD%aPk#%kP#e;Ct=-x0cxFZdj2>PomM?+5If!=i>$p;{ zGYY)iKP=vqGrH&I$a5g?42OFa#-d{pRTRInGFL`F)A}2hFV9oGM%TyE3Exu+-0quX zvyY-2A8HBhC28Y2xM=J3X2I~k zb2u(Gd?{h+w~RTo$|weWD71oNeE2isJPDxg#%PUbYbe1-3UR$}s^x`sDi1D)D=3Z+ zbV~*bm%+Nj@B-D##c$Ez$KhQ#9ha5?oLtUF^j~Hq_ANP6me8&whUUDCm)R9HfZ-3lXd!Z(p%PB z&ft7pbF$!KdB1o$)@uK?NN~WG6rvqztD^O9CZB8rOFkmJ3*Pt-*(yr?cf*ozNE#Nf z$;B0FwdjA%0HHCb^Mi<}s+#JJwT7iY$)(e09LWOEA8j|c(ry)0{N{tis3XhzZwQ?b zQIWo`&wxP*6aBPD9yMI$$eEA^*?UNiNpM5T__K#L$C6QG7+N zZSs*WIG-fJ?c-P3g+W(X<}wiJ8&c;y0`!XJT6@VkOim%9*g`;#NGyW0U<=f>cOX*v z6)Md*qI%nB{iK3BHTM0ZMM(h6UI1x)wlq>Vf_#Nei}j1@m!M@H z`r4u|u^o)?#OF}D8{nX+BgJ8GnvR^OHnx#GC15%u;kMYKNpxuAn)Xyfue%CAMecJn z=Nd3;rh-Vr%L|aUzjK{~p>s%EmZpfg1~*aMpm+G;mob$^uw)?gL4g;TOmd++RprqL&A4(LNvNKqdmf8UMYKbMzKj)U?WpG( zfxm2z1RN;Fm-lkDRPa3IlyFBq*%&%TSYh5YG1EYuLjD;;rB&FV2&M@sBcH|7;%*77 z5z56lw*im^r|Qtll^(8*^<~jAWIO{y2#fxwwQv7off#jkT|`Weq)EjcTEN{STZyyxR6* z1L(DX1>~;6w-XO>_bFc`hlJdwS1Nz5^N0BWc-2>$gsw~&!`p*21EOtJw886C&52$P z1Ge$~aQ4F1*|E9WV1Cn5f=)Z2K~4)VA5Uw7{HBJn zo~BtD*Z$_Njtqt=$bY?^-DqC}w^qT0Lz1X=kW~0U|8w|bKUiMM`CKDGq7P-Rx7-F;Gp{Qnq;# z_xEA`?OXi4=nXNbRIqBt{Yg_48huG9e3|1b{BPqMJXc(Wpvei2?*y#%;S+x-tf~qW z#MeH^oyXzcntx0ndt`7@^u}W?!kW2aj%0IaIvF>;m5X8m3~**5h8jJ}%@^J_$H;nr zJ$t84tj{DG{qP@kCz2CgWBd07p@*2zqE)&qBe(jpUyiHB780*%a*nBEt|kfMnq{~K zbSzp0Sir;N*yKFyRZ5+n&~PN*&M5#kX50~D_l#`N!Bq=W@p{yakt?xZ8UDYI&w|TO zR7AP;>HM(da$3nN-KX@g_$`XfEuKXE_pR{dOup^l;fpD7d_#-xq|w*#Y&Or#?t|;- z)IGz@3T2un4S#>z7a9DE_=F5cPePjGV%bDd*~+PVv0d%>c@&RGwh)M)l5?EBzjPa- z9@D&7h1`Ai_bWgd@Q3XfmgCYs7u=orNOQ?DdPnN(JyiQ->1-5U&U1HC-k6U(CeSf9 z8;9vaLr)y%Nbb9^-Qc%>m;K+{AO`Oj7``EfoYoHqWaO)XbDT1J)uvpnds-fG+&5Xe z)+qLzM)SsSlc2wk=iiUiSdjUPI?ECqsUfbaG%kzS$3iBmY2nA&3$LCTNI}7I#ogr& zHT1Q;Fkg!Yw_?$Ou$S59fv~T>>c|+_F@f_k1F8*io?f1eo)U)hMAO<{J2;0ZjIk9m z@m&hLDEa%04B#(smL#_94+s_pVzfexm~TT%tiAcR1rJt_bG&~BsE zSq9X^K#kGcB6{q_?$P3lQX}T|tL5iX78%B~gr5Rt?f#QF>QU&>Yicn!>Mc&b9Mq){ z{E|ViKF`m44{pN^^fY*LUTGkesqBXL7mB`>*3fxlrm1x@y)y45*P*Ejm8fyhcj z=+yz0SZ~7J0<-;zz@|Zdeb46w^D$^-TN~#opC;qCli!w~TxQ$Cj4w1=?qHWL65hD< zn2Aia$s7$*&wbvVp3i6?TR-2qRGn^TZfK?_uW57oV9AeUKBLELp+UJnH6m_(P4fCI z0jaY2J0Ee1qhAh7yD}Z_kF7bDFQ);{KlL#BY9b5c5dPLn4w(I82R;d>PdA`7WFhBl zrGbH-%~aQRrnl|Zm3CZ{#Jfv>d!$LtajsN)w+N*# zY{aA=OcwP_7}n4~0?Vh2p*G`RSm_xa(dV-qn6c~vI+UI|RAQT44N_@&{%$&&u*JoF z$ZTZyXm#3As4dQT{xj415p&!Wo#%bhAWk~c-h|+!Gw*9=hi=1D!&|$<8P!WWZ8CmC zzqeg-JzHvj$nR7-HoUXjJM3%a4y#WjN8253dk4DckC^e8n$-_umt4&^UCfbpjs!N% zJ}maher?@ZbXxOfKUnm9*Y+DLn#+*OEZLT+*&6mSj6lO0%*7=hem6!f-I5AH{`xw3 z^>Xw$p7x~sN3s{hKHG*MHX9`2kwP1M=q(P-h1QMJ8++WUSA;RXN09!hql^Qm!RE$- zN~XZt-O0ySRaF}>8jJH0Ej@jEx} z&>PC=l_t6y2e!4t@9Gpb*}~P`{Kk7zER1CT%-RabJ@_&fy_97n8NTra5Rp=VYp&#< z-dZ(nTzkl%u}<>8->J}$;H5Z(3}YzF_p;%5t$s(A{-diaJm?)44M3evE%<54*|zv* zKsS+{gE8b|_V8{=e&7GSmB?K9eQNeL-{;w?&%+yscUqVP8#3PKpib{r{yvR3WBTpbb+{fzio-5ypQaH7*gKBdYAX1J6GG zNEO%;-Kd~Wde7SG1s8N<4*X5Do)s{gsFgSG-LJv?fSq(8%Zr5r#KxbY{}*KeRN#ZQ zAiAkOx-!h~QE}|DEbw?wU4PHa(qlms#9Ma9gPrnJ`mQ;w*g7VK_4U07A*zlu5vmr{ z*XaNIY5?*`Lt3^Qc=S8i?uuzdMMiz`QQHe&Lq9w()Frg5C158g_5iomvy!k%*ML|A zQp!lOQ#YRSA2vEJ*yAD4oJC4Vf@U#Sa;ov~=enEaZOQVs_8pIOtc1%h-=c2%X%S}Z zHzF_$7tnq^Ct;XhoA=+r92v|=<3#ccpZ|Cl-ypmxPhKX6TSeL!lXVV0ak-@UV~i6F zVKCvGLcHS*ZsfbW1V>RXIsXodaZ$4~7|-ooJZ$2!`pRp%m`w4^H(W4V`>p`9w!+S% z)KfZVE;%Q*l8$DBAo9;&#Q{cFXvdoT_t|KH{n)Vb?D5g(pHa$TfBi68^MQXSgF;8) zGPm-*$Mz}28x5%i)|T|FkN^8qGbZ67*2gFc5H--~-(qo>AKOtdsf!#N8C>Fn{$ctG z`|rHgZw!qVyDCP|(0EC>F)7N$Y?wbRsHa9-@&RB7k<_>TZ-s-)pKDQgPJ~kwfC>Ko z^~0Rj8^@53Vh&^cK2sz2ro@Gf2eSeKfX z#tC>Ik53LZ`=*04{!*y_Wfv>xf^YZ({Vh^`RZ3F`&BGv+4Bt1wH4V*9>YlxPB6_>y1wVMalr!iFXHJeS_DQ?<@us0f%Oy zP@NQT0ft33fBpvn{QV*t?W|NL!+9QdY2}I>gSckKWXmsBZ9}WMN{(fqI!b0vka86@ ztJeiPT(01h0w@__vw9k(o>*Gy5p_-sSrZ^U)ws+{^XnIe?~+B5GxLzri=+emGys@X z{Pb4-)lyDVLk;C@fEn>Gch<1t7l!PO%bdMBx96Wu z0Pk`=+wo}%C=g6R<(#wIFYPjCKY)&WX;q*~uyh5GBWm^@zuoI)PT+avIQnz~h?2g3 zYHmmuGMQ*d{I5&l<8SGF=TX|(c5ZK%zZw^FajE_fL$<;eaQC{?3K}sqF}WL zVx|;3XCok+Bf5 zV%==p=w6)IjGRddIN-{4Lp6iTmoYzab=?7&*2_$WlBqbH+XI51gk3M$O`@Ak;5lk1 zu_>;Bpe;W460G0Qg7pSF-0hA=7yx3@WFJtXy6NuX)~AOktc>E=I1E%c`5?n&Gq`)% z`7~WolrqKsS6SS{`RU^v2Hnd47Nq~a2oD5~dKyH;az8lckT#vb#L_borZUr6qz=1O z>1$KDNfj4tHs2oylX{?XlEBBP{k=})fm1To&>T`lI-OS;3E@PCBL%TBGnNgm7Twoj zl1*4847>n^iOkAlYt78*ULX5<`z1;mZ#b0BTIG=- ztDxSuE*e1tQ;}#`W6bq2U0UxsH3CGF5DT@8bLSmk5DU#J1e13w4fQ~<;DDKYWPg1y zfsl|YT#L1{-e{`Q!ifT*HF7On_86#(`++q%x7XIxWhjwBSY%)17Y#|Z`E*pxC$M9K z3y6c>j^ph`4&=;?izs_=t6Ki_vif%>kS4IkC8>F(Xh`1y^>ET<<^9RxMtX<(%0Q5S z&<_Ekx9{KWWh@#58;+XKs7N|(Jy&^tbg+6apups!GM-sR54WfQV~$ighWvHt)9s^r zLC!4w-Gd^n9x&S&|2zkTaWE_Qx9xlc_-(tz+aQE6{wX9NrRxXvn*-Ho066nJUm*HB zxQ!#v6+4ZHpK<=L3;=wOgwiwUBWz|2VS z82;*Sm4-v-uFw`z-yyq}QR`Ycq4))S$obQcS$m#oR|TJ4R+VU z>XWif%OMC=Bp)oN0Q>oBxU-3WlF;l5LkK0-4^-s6Y9j%O2U*O=*(~VtfAw7d187T} zUt2^&16fwL!N_YeuXo!gkK-ER>t>Irs87T4e0t1As2B!Js8)Y<+32=}|BP)AKGO~P zKI76R_h`#^{$s%3G)NK(>W$F+j1@02<@TQxZ<@iWmhW;bn*=EqNJo|})6ayls}|V# z>H^{)^_zzkMJ(eTVsRdER+^uU>9-djki~yGEtpXPuyr8`&u8!iL7?oSDHL#8w;mNC zpI*zVyyTjyVrF?j+n~`q^`0p z&TI1~i6Y4{8{CQGKv;)3UnUM5Z~8K*qbs{{70=cLdvjW(YF6W%Zm{;*Gjt+Dn^}uX zHM@>kGNBA{{xvr<7Jxm?fQ5h?<6P{E>)(1*ue7M89KKUE3tZ*H%DE|i0v#Ao8Depj z;pd86ZD$x6n}Te8s40MuP}PKBZjxly=vbpmUA|>s(evoge;bk8|*%*WPUZ}Z&}{|77pwCfj;pxL@49W+CAvvbs+Q(V|dPzBvSy2cG# zoe<}P(BU^o_ffI#sy_W~TuZU|sGjn#B;^MOy2i_cG}ZLduq_x}+UAhSQ4^1--Gke& zbOKHIplI%wI?L^Qy5zkk(Z3df*nD^f$a1!rtp%od=!H$Wa8l~=z-Pey-DAPM??mGw zq3)gWK*}`2n;_Jhz@k5ZF=^BfzL|F0o)1fpjAwlDj=^-2ad%I5F=<`Hb*aWtg5pZU zGH0+MkupNiQj)(`PTN-K3T`M#0V!(!veedwQ$X<)oB~;cS$@PXATs3)^nvHJ^y9>j z5kHx^T-ZS($4^^`UejX%jPVw*=3yVGqy|8o5Ck*7+NPsgAP9H0?fA_6%mIc0iEg+r zU|seC*q7vBq}e%CigCiKG{N!KWB2LzPRyz`_V1~(FFR#u$zETx^bmOpwo`x~Ln$}`CR{SLy?GsYIc2aZK2oCqE&)n4q}I+Od1&d zlYc1NeUlc^xU9%kya6;+x)?wm4MQ(?3Us{4wNby-_pg(K2VX8o@@1Yw>T0APLzfG` zz-d5({s%xsSEJ#g=I@C$McQ>l-+jQw*|K(m#`hYSM90za{>eA%jc4SVV(YMJp1`y6 zWj4K9oMuD4q6h!mi0+^LVDSw}%q@)Mwu> zT)upNLyezW2$tAYIP52rmnEn?cL#zmKqtVg0c&E(R{0Z@l=F;O2iLGR5b16gqo~1y z@vmeQTT`ZaudDA_mO-$>v>1I&cxv#)omJ(KG=+=L84R3Bso?bG72X)nDaI zbY*dx3bRbdZxM}mkCG*@^l4`ZWlxCH*1iD^M4S5b1SJuI-|kpN5R70v5FtERyY2H9 z1il`wky^uyUn`5_gCfyP##v?|{vb}UI%EdJ9#*@I`n8#SSOhcjCW^H5PXj=P^SxO7 zeV2Vay;ufJUGJ?l*p(^zX1X=ttbZJwFeX(A|Ks}|&g2|;uWSnvlo6rCOyLo$t{mNR zr`B}PVu}F*h06!?a9x$Sv>?DX#rf(nIed@C2MI~_xXu9vgewjO= zXltbe0u?C~j4;r4afxIav4DDsg(94KqX`qM!$v{1Pl=xJvT=# zZ#WATA#^8@66Bw>uWSF07XY)g)S1v*VFYFi9Z(C;dV#2>Fz8gQ1_lSk3TLn!Ax%oV zt2!EEgmlM33)#vZ!1bh$wmswmt`j>WV)3^2=l1+`vdj=9hKtnqfV2{?9$V}Tt;flF z_9iLjxJ5_QDpz~xkIq{BqmXNS&9f+%)N#7w41{Vwf?A3oLcvNzc7?o)4(+Rt!H8QkoQ&BC_8GsqCt}r zPJq8PAC}ET>e-Nw2u51h;Jp4AteE<5N=bz4;1|^HT~=MiXS7xMPFL6%u#FY6eDsxj z)dVgDU$jzZ{8mM=Hk^_(JR1I`le-Y(I563@u6)_0FS+l%ux&>~W_6Sue6Q{6W}IPImZor=rfbD|POZ0381U2uq}xIGhBc z`Imt7pAWG3oh$d)fgQj;khw6|5XIPzBwx`T0uIIWOhxjKysEj`!ILbr(;)~b%Oa5$z{0&3iW{X$U!%&xmw@8O^I#{CyhEA3HcZ5`E06L3 z(&f1iogPia%oPNkKWQJi-abnObl7YwSx8&cD8m#d-Jsf-4C0k5nDl+75-VjD`Hb62 znDgVX44=zbWf*G`h|N$^{KW}dp1;!nShN(>8RkT{;@TlZ%7LyGRb_vJzIwjDu0N8~ zhhQlHUpR81YNiSJ{2IwL=j()@wa&jtD0^PC& zwdhB2^rH12$=#d4{^a-T*gx3gMjH`|F6GH4)x-R*uBAsf@;2dfJ%y7JG zY2>gD_lEpWs?PQbuuE#NLI}Ps>p)$V{VpKudl|{hXZX+I5iCOBEL(Q!SU0hbl7XCE z_jl2UQJ}a};+eU`;F$n~KK6DK4#c(1E>eE5BH}ml9LK+QKFzaWw!YBnWC(989@xx| z3JfPx6&wdk4 zDm%8UaW2%Vb_6vc(!C^50*UBZW&_{?bZ{-&E&QUQIR4k;%Py;m)SRHaRA*(y8FL0Aw6A zUQq!l)a2ZXm7uCZqVw^j;K}K@%^X>;Z9)lARisXFG>JQ|HmJC}wDtqSS(XL2t;H$D zLvK`J9SY)3X4^w9MW{=J5E~!A$>RCUxZ#tbX>xFxIbbA}!&`a0UOWgR#vSIfd>zy1 z!ckgqg5K&WWF@EDVxl{TjjxR*n@IGCN7FD-=ys14q4^j{^?x|uCmdMkP38?{LNmck zgJ)4Z!O7G^1K+bcMl{ObtOhd_R&0$N2aI5MbJ2O8 z<3dG<)p@g61!oriIUG zZ|=9gm=FHKvEv>p%+v@1qOg?&ls{hM$Db_@ZY%9b1BhqUl@OTCn{OpNlvA42%1n#p zd&L&&2-p>fA!m^q-xnk8z7kjwJAO3Yg@91)xCCxLyFL+tNwpGZ)+3cfq&`*=R+5ly z2KZo{zhc-T9f%!$`{6mf{czGCs_gweYP!#GS`ccTm^O?S{e~hx_j>ayqdfAsF@}DZ zO!i4O9>5tj@s-4IT07K(MUTaRXac*aWEDF2(BW|-K?O;15S+TqD^y68OMJWQe25Ax zEM3*$Bnm+@J8r>`@lkxxIoZ>w<^1*Me<)b5WF43@pC^^0!};P4DHEtSe9f))`3sst z`wPiya@ZhOl6hl-VL*p-d3E*ezI=PoD}k@OG*BN|!j%LQ1D=uu4LH(jO;A}VCGpHKV~$owBXKK| z11<{53fgntOi+%y3AGKD?$$W9T~P##Bx*t?Wp=tQI7*5Jeo2RGA;8RqhzUN_i8R1E z^~ZHVatOo{kKp)28~v2so$Qm+f(qKGr==a1&07!C`k*6+(yB5lRW+)q*8w+R7k-jc28SHNUQWYoQI%Rk1z}e<62S zjK57B|d9qlVgT*%~?ze3;iUmiuzc~U*vkhRJ8WOMP@y4Gdm_E{VYo?6j2jmR_QnV&^`a+i_^+*6Ch)uXgk_$!t^ zA(1FKxw(w2+=sgFJy={a-F(|l#_1d7q5OT-@P2m>$!&v7-z!|)eJHQiO%};3z^dX& zK0~o^<-0S?8AWyM2XCn;IX!5n`5nI9Zg%{6^0EZQ;r>4)I~AzX z6o+_Rs-SLG*zhqHo5;`rGg$X);r`4Lb*AV}t&b0Ij*SCT<-dCEhGX0!2ZvJYOU1$HU_EVYv+V|p$`Sqo3 zIfG*R@&0P{GMT~s%VtG)K6&hq_6FVgwBdpXDyH3P7?_qc0{;vLzLA@)uk85g)Om1o zczJQMz?Mb3ui}avwREyOE3bn!J!S=D4c~SdIS5i**|p}tGuf0WO+5kJ5{ClaoHqs{ zUv6EuJhpAMBYa~w5+uiEUyG3%aW=v%W;g1xonW8-+@8`*bTb*IY5d;wb-&}7^*vu4 zy&y+j*$x~2R>_Ie+Tn`lTF0BCjDUL?F)R(tyvqA&LdLBs4$A(G&k^_Y@|Kb&f6vEsEhBIC`q*D}WF*!AtjdWMl>j)Z#Lcb*sCq9R)? zp|>mYwpH5xp>B5Es7I<{D3SeTtC!vd`@I|At<#imZlh9Q@I%Y@H#iXF4rXb8%$+oQ zY|f0*4z7HVwtv}Z=MWnI{I-Pqp*EwkcO44TCS)u)d?+N?!}@uDYIec$91nqQf2xs z_~}`0kB*bjrgc2u7zlscG0rz$E=P?@jtj z$-2cSRkt$aObcB8?EF}1wEbewZ&Vy^{zx~##YV&=OtukNw%Zq>eXAqaZV82EZP)$}&&uwDb9gySqBCY+%w&}(3@$hYiT zoyJFRY<8)Uz#ZL9Q<+L3QLAcTCcJI(sz~9!rKAe8*B|*e>!Bqj?>lejx_JMG?0^N6 zW|}#jl1n%ym#%92DWcR+JgxHHPLp-c!;SoBKk~4B$uu^WjmQNE;svFhKfNVEjUP($ zfL+2QFuPg%u+q#j=YxPCk<2S0^3rXQ63RRzFZ(T>!%7oS_B4l)DSpQ$NObH)^{zx`(00tuQ521m@frrC_FG-Op6Mtq%9A2=E5d@~XhR=%h=<@vLhwBTca zxm(*uK~CL)SddsECeT~*$~z#EF6ELNQ4Zx>E(zzilKxmN|uv=}Te)p33zm&Pca?O07um zq}?*+s6v>lG0OTyY7EZBZ}Zzn zC3U|kuA?-GNy5?NianB3BBD1m!{lfS9sj*5An;d-%cIv$Rfy9b~Jl z8je>S>8hOBmQ$;_o3hhAj!iTbsLt*H(Iuo>98ZZAowgghzeOB#FPL~mK?Wx3uw9Lo zTk}4Kw~EhzkS`7t46Js7K*awasRR|k*RjMEbV}9T2GDTg4SeYMkM9>3=rOObx=;RC z`2?6q4KJ(CWG~zNFU|jtIwi*eltfVCcvDnN5{-1GEna5=v;y_{d#of*zc_=*wE>BYI_5m1|NmKXgmN>F z8jPfAfF!>u*3ROX&?)Vuur_i(-^qMlFRcmMT<-xsC>`j?0Y>}@Qs{CFk^!$Xa#lX3_(kI8l> z#fz+4T46lmk@P=~(hr4j=XVk|ptG7GMG?6b#SeeST4Y)ab!V2u*cA79G|Ps9p~@UU zx)78`9Ogz-K`c|R4+C~Ys4&LFAgX*9YRms|D)&y4$6uv+KaH`ZTMZSM5ROz z6QBc~KeOKgX1^{D8^E)`7X^=0_Fua#U;RG{2_!WcfdqR+s5u5Q>K}7#NDJI?;~Q$R z6FAW^gqvQh>yeN)a#s1@+5`SXVJKB?i~p+-LO}zPI-L11Rwyj()6akWf4M0?rGF*I zsN@#-|6RTVzCSbw9|a?T$Z7sJY&>Gid1tG_{oQoJM#OJ?5D);}fBTdqi!cDjQM=8L zkr4e)q-G9K_b(_BI2X|QRQ?ZD^65bEg)1>)`&HgyLK*CUyx;fNap?Gz^nq#Bdfi*$ zc5DvMb!O@2{`Bex=$j>ed>zxO(+zJwBz?Rcn^UM5va&ZjAY(4IS6wt|-o48>!oPV) zGv}{m|F;7{MfEl^7mM#=V~)*k68!ry!qWkGNWf0`4#Utp!iP@%4k@)SwxDm}F(cl^ z(%UE>cN`x4jD)gPt%2d>_JOF@ZQPmtVQt!IZ{m;$?zT*`s*+m0PA41xv7%$gQ>W{Z zp8fo$Est3ER9MC1KTePju@(ZMDcdv_&@_ZqC`@wSl>`%Xkbb7QAFD-L|15gtek!7#M;r5tyx4-;v@lN`*ti`gtj=5$pCFB;$L&=d;QI63};q$}PbDos6dsXpU znVOLx?Tj%4ZcXtt1vm>pBPOu_kHFm|Rh=-7^jz>&icoPzhdKH?dAinY2n66uELG+L zfFib6C@?b@u7p3JOqMZ`V4?vNaGr_*0aGnm$0=IlQyGxbF?8atwk3DC?)TQ4EGjTO zZnZdLraiBT-@UOyAAEQWjXB_dQr7rzh+1ES77sPX62N?9V62q)ICEfLntDtB@OMWLv zuYVO=$l5D05|Av~ESTh5rmo3E7?#ctp@jh$uYoafDB?T>K)V?p+9A*As(>05veM|`VoPaeNBK>EROkS zgVn7_*RR^3$Kx?*8(xzQ*SZ_*n)Ycm?_X)=O?yLxi|b5im2n2{KC}_4Rw$#Rahg+{ zeAcI1QW9vgmJ0#)?dU18*SviQxdF8Aa^A0(HfHWu?f#HkWK*|$!jGSF4HnBqm^pV~mM9iij^ zE;i)!X99G-ffco3A2)bYT1ZBr>pjWp-7OM6!0r>b%1V>8`#uqR-)3Zr&U9N65XD-{ z95jb9ecQ9iMK@TeXm@#@kW*+gw(`T>kZIiRV%{C4g3dYguwinW#U*mRvAA8NKiLP} zKgF0jmBn&yXHl_ct?)?*onGfeeUG8fexBJ({TY)htXlt>*#!1MzdLS&E*=iyWL0k7d`sKdEA`=%^kF$j!KDU zJgmn%-IeUW|DA$tj`EQkWF#pPzpS_C>kn6`(}J9rV%89RWy%_;EWuY%z4CNR-?-nS zv!C4O9qFX|q86;Spd-W8%J8ffPjDM&9aGGiNMKrDPAq1TkSuv89hoF!3FmS=b$tmW zu^+EsI$YBiKDhZT7biP*us`Q6^u!|Clm2$ds&uiXjnsPWgDKO=3*2xp-|u5iuyeWw zaKWC)!r-@usvYQkUDV@&tpl7cH282J9U6o+&U0W25W|*^iFHJP8%Cw@zba2+5WnW( z4+@j5*C@WwaP~;UiClxjSg(pB%uTEDB^dzLmU9oog;bsAVI>)fgp#|}4V=BjcRvY!&V;op~ zIg;_dv%w`{fF@b38L{E~B$?yNjkR~Rp9*0@=fhs?A=Jvq`W?Yu?B;zyuMu$0YMpeJ z1cf#N?9lD8^({U0-6o(?vrgpX;rXZS)J{v#WVddfEO0wC1&8L-9oI zCLN7NooOoZUW@q2dw=8ss-aQclG=3zt%g1Vu+5kxyt3;&)&0< z{)nFb>yI;pJXm|v2rS0CtzU)tf_nLoLJ%~QQX#P@t5u;F@_C6cE#*~?=IYw!O0=kd zMGQZSt7IT9qwKFO<_{aEkH$Ov$_u;p9I|EJGLVT@4=O_dhdVelc$oy8R>@dn98?{_6K(5i;FE79H(N z$~d^hPx&w1Rq4!P^v!WdwEtq%tMPhsd}wbvFA>dd5REhIIqZu6ZrR1YRIcOpCsK5a zTc#MEZDK+2{C<{h{P2CXqEIGj^Uz0hIq5X|_eoL>S}G5r7kpNGOoNRQg@9$L!>2{} zScbuOB8Nv}+_bg1g*QGR(I6<|*)8Vm!YqbR=Kx=ZT84~ih-P^+gf%OyhNfkil@CB@ zg*g9uJ}E?5{czoXjgi!v%_|Z4BezVF?|H3@`$AldY#O7uP0rHQYv~*sWaW6kZ9P?X zat<>M>W*^`8(yumepM`6C$U2>i)Rca<;SOI7kn_>7Rids1b&dy$^=vU8e$S7b8Cyno^kvyPq=Y)GTIdJMD-7GF&a zS^(-gd=67uD-N88U5r}|cv!8MpjwX~irX7L{}${Dh{);~H|V{yeaRhj930&gCms5_ zbBaf_6RqV;J%5sC{I*HxG*65x`nHUd^-(9C)}HN~&9+Jb7nw*VOlgjFhNT)&Wl|j( z4Dg9dhNWUR4jeoz1VYaI0V&ER{}Da{4aeVpdWb30eF#`Ww6r#w^JG?S?x$TX@Qh$! z%s0n1sDC-4+)oPa`cOwN+x!X5dYx#8k~&VaJ07o`VYo1PS-2j+%h9DU4#VKF-5Y7~!oS^AFFVQDZav99aVxd|b5#!gW0kl-0{c~6Uz*WA%IVZ7U5mPi~z2l>CnYvjCFM=QD5PQ?ESI(Du zS5Ai#P6el^JmH^VSILkK-JNjfQ)_ilwQ2u zaisaKdmno&O8YrpGkBT-tK`}H4WJEB8@zGOOa#q%LB?r+;zsf&{?%tD+} ziBN0!k324Y2Fs-#?GIO4y4m0QT^&~-aV-z2Z^fe&(G*zT{JNY%1qx~UGSx+s^{a#s zWDS+1<2XKPuAtq8m+b3Cfd$&H{iDziLq1-s5RUK5n|bmAzk-TOwaWD8zWcWigXoU2 z_a5g`g=Mxq*y8q^By_uqcGM5akn;_yA}WOsKP@Svv|~%y2&8kR2(nrifUFqh40yQN`=2*dVHUWW@jK3OJ>=wGG=R!^AGnM6@j)K_iw|V0JvI|L~ z%S^`B$2?C7I4cd|9dmz!GWQy8KUgCQ7u^nv>?Y@i3a@6`hemB8meNb5s?L&<`0>AK zJ8=^_prcFAUpQ#uuW@0uYiJ)2F+)P7U8F!|ojudwTp$oMO89dorr8(`X+!_loCt_y z(EcImJg*x`opiK=Mnz$Qal{_Wv*fGy)agpz@JNx4Yj?&hJ<_jEqlB&cPeW4tMyI zxC`>X*+HVM4w#0wLw-Px$iFMaQMEHC9!AH^BBTFKK>G%@cxk!zW85j&KD?E`FAO$d zbd`!Qw>RA%n_HVDh5dMe$K-Sx%32g6PBI@bxe04mT%!n_Nr%FyLrr`+~36L_z`~be6b$S12bhax5u- z&v$fFUOaT7+pKUbxwJ4#QqXK%%74mQ3DYJPAVvziAiU1Bu{U5$6TdPf9i=|@ura>c zc(3Zt82dcVyvBhhqq)4aQ<*|brYMn2<@-A`rpDV^vo?G;MF-CCXXpwEFK*)o*sJbg zzROg*I~+-vgkA62*e}R=x{rQ>yt1i|ZMTzY38ksUip^adGDdKgf|3LK`gnJ)#r=y* z#B<7(`m1`;=++pk(C;U4DpdzMz%o(R&Oru{OU-4{Z zdL0^nYq_w;jhzq#U^Yq)3rrp8s*8J!hQzOgH85@r(~K-=6tMav?>65}QrCAJ#rlly z(y;gNr!1;bUkuvAn(MhwDBF&kr%B`?t*LQfu_62Rd2VChkgK`^|DNf+9ln`9l#r3M zRL}ZJ$FVxe-vO@COm1+iGsVrPL)G+R`rS_UnPbJ_jj?Sm8;2rVn$I7D?ddaa6=2=F zvj!pr!#?`y^cZ5JgAYx#pC5)I=|8SUjfs!_oOi5dVG)r##}vI#7tMDtNJZ{4UbDn9 zr@bEVw1<(Oerml(jCy^h_LEaqXUOIp#?8wcb8}7H_$qroGHnT7!;tlvn4kg;I9igg zl0VwR=7OCG@Tng?muG!_*}Mcrt0@~PbA^{L2J%wu3spL^SICF8^ClE}f`w_4Lqj#@memVmEMqn0UnqqWIHPQp+PeZX7fTYr{Ax! z=>oZt5U#n5Rs!rS9^{wD3>PkS%R*CfLiM$~(2mZ-Zo!T!G(qO$PlLO~#eS6FF-5yi z3a;nB5+54)op>E9Z6GrB%Br{S9Y!Coi0wQU$!`L-niORJxm)i{fUq&tp)8XEh8$_M zDXNr#Xu)WN68`Ilmif z8RLZm@bYU-5ly)rn_>D8tI|W9$ z=`cfy7v7Z*$1Y#i+NXaS;D+exEGO8(QwS6^C?j%NzT9t`VNvjm11%w1AA6eByzt)w z0Omyw1*jQG@S}Fh+I~unnQ*{?P9hP-X>;x^E#{Uj`3y6M70q~Pcqu$@PblH9$Oz_i zYl~;(d+ew55cAmA3szZD9KQA7h4S!d_`cs65KrpwE-LG5g$TSoZrdk)tgSC2d}C_v zaJpmM1(x02Yn!2sMKWb=UMO5I=U#4zhv&J)CSpq5V)|7r>Yz0qK6bzAcmlxLskCU3 zl+iEhRd-9neNK6(N7>%E&gNzIpEEB4us{#dzn7NEYk{9ZgG9Ucud|(*7dGSe)TGjX zn9!ER)ye5Z-qWDEc$iQ;W5e^K3E5|OIb0MIn-nsY6Q)jG!N!&96AuA<-z%}px;<62 zLgaF!T@8bxtLUTCyGltO zd9p;KgwBhhlxXC)b4U^8*+<0og+jF*74LP3N@8N;{>kAAsvV*_N?3L67xa1J=1THK zYv`mXGJ?h=`t)N(Mfo*H7Krywjr<_?^fX)Gu={dv?G(#m<3glGYU?3Rz1hZn*y)+p z^bjcPy)|39*n8O14p!7{e;YYerW`2EnUWHul8w8=udU8*b{(}9Z9r#_PTTd5UmpMCr422=~-bC0&*la8yLnAE%eS9mrVKM+~1GZ z4$|RV!vdx=WSqB1VYpj9_m_8yFP?*#i+~&{TL_M_UEkR| zxGOOQM(Q8CeF_%bB=_q`PC}AOW^0Rimt;@;O5T@xa|V2a7UGvV4943x5zq{`1d2J5 zSpl;i=$i%M*?d9#d#*c0K`X^qQ$1Mg`S;jZpsxj@UIqMk-bBT+Tuz@}vjv)zwT2Fx z#}`zz2#NnQgj+II2nC&EZ)z`Wl?IolY)fLZ9>2z4Ry6T6^^@&lV;!~Y_O?Yoy0CEG z<(wgx?>e6PndS;2yYxyeO)}?z6cab<4Wy`?WuZM``$q|x@xDAJ@lu(kXrHaKaGq;R zFwH>kA`Y%)ZT!bVPyW@Dmou<|I45Er66nPbSQQ+rU1iVl{qqpbT+G(z%i^~#okP1! ztqyBW>J7I_hfKFWxy=`%g`az5bW?!eE3?OPK|1m=2(H zk=6xkwTELBo^E?^T(6rtT%r@YdGzZx6P#eqd72HJXfDpBd1rTPq&zd3cz8&4pF%`n zO>jfq1CDF+R`i8d^mqjyqFPYcH6p&vNOnvkjLH9jhT6?hDE9rDPRzOvVB>x?xtB|1>_W7Kqv?IQ^53lIv@*epQVDR+OFeY5V z&Xp#-(Jb$o4?GAcjXYM~M?8IU`$CO& z5H%|#n-t=C`f?+!(6jg0r{Z7@@RM9%^Rh86e^hxF8+uCkCb571C~PHH)%o=;?x@!k zc#_ydogO<*l7Bi1EbVW0GIQRW9C!}L8pf7`iRftZ>ElB7`bO#%LQ!M;!N9(#KQXyqPSiai%jr)!OFC{JdW=`D2gPPgn9%R^R$CuBXe_Be=FcS$gj z`JF@``sadAEbb5`Ueu0Z-?|s^$!^MFrC^OvP*_cvG`T&H)I(sMZNXa(qWRFOrroe9 z_#;Mp_wry7VH|n;mucRQ^bZM_t~U{zCkqW&#?fX`?IjmO)93lKA$LEVY!s9MUt12} z+8&hC8mTi`QO?cXaae6KrA9XvsgC9_tO}n90#CM_{CC8@RyyY% zi-R>b&pR2XsLjoz7z-P_4gxGPme04$)>$EYbS^Q02br~kg$NJ?&9yEK@1fH{gm1r) zZ)v+`?r5Um1s|w8QoynG6Ty~>VE}%8>MC0A1A6{t1;{(uGTWc!yso%<5;*}*BIyS- z60Mn`UyA@yXw^^eG37D6S9&d)ulJ+|OH|uD#rs_UteKrH)Uk>>*&$-|mMQqBu+iXB zYfxG=`+TcT3AS5h{K9!}!pXI|yT#}+@qR>JAM#nb%^1kt6`rPkyuQTK(R zO)W~(yVT-L30r{0Xhx9K;Dnlg6D%;#ZqZ;SF5=oie@gg$$1ZvLD(WI!KOdC@A zNBx6jx94x1G74pM$tw92xIFanTm~I53tNb5RJx#jQk3n47atfdjLY}I-LPrN)b1+dzH;lFY?3Gr6-nP)>3pB3dvmko1EtvB;MOec2(EzZ69yXmKj?NrN7U~ zc|7V_S~XWi+iXX!K%fE<`1~+jx5GN$oF3l^&;JV*T7LPlgdiJs$6{}|0oAwP^TsNeybjIUqu!1p ze-a1zwmaPKtW+7Civ4d^0qbNd0St(#?wUpmgM81CUCm<&%gzQcJuaJ8+$v=JkelHy zF!Y;wihdF5Q2}v#a9iODu0aD4>f!o5qh-m#zA-mncPXD66E;c)_^yW$S|E8O55dMV z03J*)n`_noV*%!7a5R+|V4Gvv6_0B2D?c95ivsjfkg_0L+|-n+Ew21O&`!8R3bOXQ z{EU>!>Ds7mGdnNtmner30_c9I#ac{dqepau^Wba4Z~B1jPwAs2rOehOW$dHrUi`vD z96y_3r`{}^%3cF}8Km7fgklKTyk9Brff z`{#IRkqqBJY|_Z;WOS9J)(d}j1Rhmrx#_;V!n50E}izBe-?CzZ)IyHs7&iHvA#7&t z3$hXJyOoQG{fo906HyU@0%vVbkEco`k#qp(L{Ahc@eQsn z;%Eij3$$hKS0{d2`r8nWme?4jRd0#(7tf0SY6T3UpNiL7$;j^}r5U6a^PHv!606M! zzCTxOSHc1+R3Ky7Kgcwa_H;ltWMIrZY?x>D;dlG`#E^wQ_6;Z_FBpO=zSPzzrL>bG zD#y9}G}o4ag@;5&w0-mjLJV)kr))^{D_|mW+0Uo)-)350ydELz7E(niOmG8zb)drn zq*RLuLA3r2fy#ey*UY{RylFp;!0T&)eXrgExT3eDp1*Fs2&%G0CL7>#Ojz0H^)Ez* zmnRyTtQ1$C=OR0dBfi_7&}B52H8G%DkGepDAIoB{hn+;)hrjx+4R6x~Yup#9@=1bt zs}yFTJz})`RvkuLGWm(Zgl6Ymn1{QNQ)=D5L7#(2sa&9dhs^M$e6CjvfQow!s2|Hd zv8_+t?WVE83n{`LQC)QAWFRn*V>bkbm^Y4BXa*6sbuRy8@vhn%e84w4zrW&J&g&9< zK#wn^_x6ce)HMF_8w?5>1a#;(&_;ZgsW(tN>7AxToPN+W9^VgsLKmIV$wmqPF#tqw zawvX@57e1mvEh&4dpE;piskN0ix#GG^Z1aG!3mFKNHyOG(SPeyZO{6WHm>Xult#f_ zoXTR-YMg@r5Y*AlndF?P!OI*|WNYn5b3;|wjGnGi#ewny%0U|fxgGEt{#1icMa27c zZ6~RQ!v6iW&^~X&ZCeopkSI})z)8Ci8H2mtPto+`i6Rt;mMRQJ;|j=M`wpI#ryZ^C z)bXU!>4hMm-vQI#Gv`(|YdJ;N#Y7V=I)4Cd*Q0d^wo1U_X6mWkgjkYIBELRHgpmYP z6GIs~ZYv#bdLKGn5NegriC}zZWXZl?FO6 z?B}fE19tEQ9pY?NF=gHkS0Cs|%nh+eZ5e>Lpc0RyZ;w^ZmB5Rg-~coC>Q!S~cjpX{ zS2{SFhsd2r`N~OQUIdb>neESvuMB&6YU?^`M8Uz9$sr;!WWJ544|@tQDYLyh5VlgF z_I4{aj1V8^p`9En`A}&oRX3l8!4Rd#!KkQ!nPx%hjrG5@XDTIH0t-`YjO)oY?*(f+ ztuHUlUKrdhb$QWCexqb$>af^Na-fQ-pU!kh9AFW$omk$fBJgn^du~O>S_OvH$F=&$ zpcm1hf&*nj1H^^;AI+TfUm3XO(cZ*kZB5l--nbS84)f(Fvy5WYVx%317PpYKExX+! z&$&4?UPVAW@9)HH2Msj(V%us8k`OKMK(E)MURRB$62dTPNVB(DfKCCT(gu*&>>p5E zWESCg+qM?(3s3N<%a8r(uaersBt2-!b&4(qqs4zi+Y_S)9GgB|0j})ks`vc`&8&mk z+~x&3wfMl3m)oPL^lOx>K~_T@)3_z5VqD ze~IM}H(8$0u0_3-cvm1ADo9OT->F3n?VS&DZfUm zUMjnxnh+e{ug4mtHARkRxw-(>n#;mL(@Rt9_kZy&P2OG`vqw781ALrwnoXABW|wE0 zf9Gz{FfrV8pX2EBFhbsgv@yfc`|wXe0pM1gFKM7_&eq)RCCyR-^p!9GPk3zFs8=D7 zGXUen?(7vQ1%kZgt5)nFVTW*DEmwoJHCBe9KvjIMen7A+t(5Jux&MabCSU1IB_zSd zayp=KQg_(h=B^`Yo0_}wockzvv6#msp|>ejZEXlixefi%ey!#XNdSNnmSjC~7$lxe zmpIyG;C2ZPMeV177Jr~4nA&gH-#;$jf9>btiThln0=K!{(5ZB=mXdNAtV{W2={&lm6 z4<0A8gxxjtmggN6p30iXx)$rY>(GP}4Z?c7xz%!A)rdj=g}+92;1{(G*U+k~d2~=| z^}4ljd)6%hu~@nnaC042@RxRI^oXXKL&mw^unkVqcMu*OE;9FUKRj(|`p+>HvI7nP zTjLz-ps*uz`wuAKN*AKAgtnGC2siGYr_0Ur2*aG_9i=;6Wz<4KFJ&w2g=}LjR!4FW z`#;I!u**R;#4*tDAm6m>iQA~T32O#Q>zlkJ244_YeM0x~o7X#0__ zDQ2k;rgGWpOW^?j>%$a>t*){-)Lsm&4y_#XB;?O0){dWM-%OXh9-fE8r`howW3~QK zIPwDvxXT7gFc`~cqvRb00L3HNLI7Jn?h5q}y*5f!mFt7rhiV=sCLk~zBsNQv(tw1^ z<`O@#17vtN-;eg~HPC_|twOhT&K3AJYHe@tX=~z(hDeO|c(*V5at!&?>pn2N^Hd8o zl5Woes>sF7tt5iYvj&T(17n@R)j-Vr#W+oBWSc&yw~zwrbglbOSE$CKXmq#Nj!5DK zv{-|Y24wO&6+g-^lz}$ag8<#&+&hvRG)c=P*Q!>lZ39jKWb+S*XZRlN4fzi{>cZ<9 zRKz4FMEs(#&)26A=+Obtj;j%c5Btd=nX~c#EJh=7*pUE9EZ}z8H3L{yo+c3V1g4w2 zpH(-r`&Ho|>Ja%GT-}{ZsHgiJ2KRYvk(sqX!_y?Yw=t~x6NUDrnw>;CZZ{%aYrK(o zvsMm3RQ8O{rFu3M2oV*p*af#I15sk5Qz}HEgT&C8LytdaY6Ky`oXVs#KX%TQ@yMoi z38QaOAZeixOOWlZ6rkc6|JGSr5PyJA4MoA0>tAtl&o;<-pJT=_S@jYM>rG@$LaQ~u zaMaK|*@NZp%^^sJ8MCim6dgz{7Ke9}36#I(i}U+7dI$gVNtTQO9HrBgtjp#agI5Qp`z@4_Dy4HIzj=++T~3f@)ygFVa%4 zVuTqGM`jcJc_8it_Uxp07NXi%u8`U4_e48UyMPYKu=+kz6A2CyDP4M)^CU$7P7p`tND>?ofq{a2TH@TdqyRO&lEFSsAA6Rc7 z9zsIQ;8xLgqXX=W0Q{3#>Z;Onn26i@w4Gm*ljnLIOk6|be?vxCOQ0MDCVgQ^9Kv-kv8uQNE}LgU?=G$GgL zj52@e$o6&$4(?6?hKGDH&tBz@+AltwA-O`RoeTqRXois-X}}z=n(w>KWvTAfQmgd)uc?lO5si?9s9dmyIQKvxE;LgR-6p}^j%6F8&%Ue7@(o` zZKk%8BDCt#POK!x>`L^y?7z$3lxHFWlgRc2c;}T3hsEPdh|rVv>;*@#_{1H9t9m5w zi9}PG!?|nM2nY%?I2=i}x0s=_ka_4Hd-PTt0~AslS6FIlYR}QIlV?Gj(Cq%m4+z%` zlDm%Jixu+$n^B39qnA$~;q^v#$LuLc6uajhdwppo{>V3IvV5>qM6OX_ZUv+)092W$IC0F~OYu zU7Wcse(NI&JB}Bx9udqeW3Q8UI2m9E2$cG4hCzNa@p>I%8d))IMB^-!?nSuTTNxs* zcPo-V;dO>Wk6{_fm49pz6f$nPH=wpL*92n`f0fJOkvWi%d{WZJQ)!#l)~Jsswf>Iw zjK+t$eDy_RJj(%wuZ!;R7EQx;V0zZOCcpn{g!jQ@tTpl3k+!t^=6;q|(QhUF{#PmH zTmljJN{EjdfH0dct&D12d}IRW{uGY^3nAmL6;413p{Wc71wpm9W1>$!PQGNa*I89> z0Rrc{je`~Q!pVrLATHi5-9q(*$oJ#@mqZw5n6d#55Wk}=fB9zcqIiWmx70Pwl|Lxp|$$r|7bPN%vJyKMG~m_bmW7~TW??P&$)h_u(;pe zom@}@bQTAH9w0sSHMiHEY-Z~rD#zU+vXJf?PGKc2^JC&mW#i`sB6#$0``Ac&%D}K# zL~fM{XG#zCj*Radl&zcLFO`*!ABd#JS#TSKCH<7e^6fw@bKU7XbW{eA-(FdXV8NVl7qxsh9^;1u(|7+Mi zFf^Nki^*g8@3(o8DB;e^ziOF(r8BK;Kxi1n?70)%fwg>dP?`@?>o+XY1E!BJ^IF^v z$ADD&C3Bog;y5v{Y_q8;Pd&V*U)wf9q0Sn{sF^AvZi@d;lXx*9bM?G4+dC<6a_PQ86%K4?6qSH!-t?MMq$Vdc#0FD zpGcO1?Mn(*E+UgOcy}FNE4FAhp7_zww`F1qDkKq$r~agYCvuvJ#R!-xw7!ytyRW84 zK!wIy{iW+zYz?8*mW@qX2F;|CpP`#+%<n%fB>-X4cN^{Fctz)=FA=KU&tXjJN?< zi;V|x&PMyh7zWeI^klv;XxH1}?imxX4k&}ST;9i=&DK`G^(^#dV@!^!#NgbS7@VM`s%*vr_O9mnG&wpmt|Y1U2Pfd z?AImfm*|c2e7eFNMqxfqysXdy`7pIsZpvmixS$`b7e4a(e8`|5X{!A>LGBYL+I$TI(W zd035ZDKWuKvGp78wjJMAd%1}aMnp#wCLxS`>1L&=FF0JCnP&ZTIlmZOnH#hoQUG8D z1mI{sK!EdW0~FU`22aUJNm;o8d@cVB{ZZ9t{z=0ZWd455W%5j~Mt5VVo2!vQiuaNw z*{u8OX7lAZ+{gIkxOc64seaLjHOOhn0EmKC@yg?&BqQl@FX;~}%E?gzZ*=L1Y0SnF z$`^4{17p66_Duxzb6lLT{E2=%MdUMT{e_{NP#dvU?k5KWir1xr0*3+x1|OEXTD8t0 zgazJBVJ@5wF}>^d{A?-lm9QD<^xffXguj68Ou*yr@y(bEXKj?${88tkc9b%b4YiB4 zn;Ac-96Qvn|9PHA86>N9$5=#vT3mZ^^ zR=vwO;}!niK6VIF+Je?RQLASKcekS{dv@IProv$`wd9#!)7qr7h#`w~AOilmKTMsS z)FL0(#hf3UM{pL7Dc`h)Spq#t08C{c-#%I`FARyeU!*7mf4SbAQB|I`{4QBT z{mlUoKyw!^SRLdz6?Xs*P0_D{m`mR7bds*z#0`)=7T!7Z$S^Cpd>hp#jsStWHYuAz zJe3+N+o6ur*xcjQV*aj?DCf4SUAXX=ZshGwjpSMG0vv+#HUPVh~ zAVqyV{-R&`*x(9VtcUkAfgRWA^|LPV#CSKsG9}{{>8#Mufg|Ufh2B2@o*YM2QhI*& zsl{|q$x<4^Zz7)bYG%Er=8(UMVjeRin*1MV6n%wD~f*gg%eZsR4jL++KEGXynC3 zwG53Llt)KBe*&WfAsxCkT8z;wi$Bw&skQ+$)W7No20(QLW&eAhN?3f;PU7yJBW%^O ze_?ZWHUDNuycr6IrrpcjS7xek|IBw1%Sy*L@KN;e0olV#Ay!zb&;>YNGgHwQ&#T(_M? z@MZ|n3|AGkZ`2D7GXpqQ(b|V#_QGiS`eV>=SAN1FiQ7<=>!8wpf1tV;k#^n#p?n|1~8uxQ1_m+zk`q*b&AN1 z)LX>f9Too8+Xnu=quay@b191=#PRzhVCmKR|1tNLOLc8s*C-GO5?mG#2<{pZ++Bma z6WpEP7Tn$4-GjSZaCd^cyMB}Gy`Q)0T*0YYKcHC5IizbFt@lwy7(^2Ma6@&BveC+A z-nr)@Pz&)@tMw2UiU7=iC6?#O$vapnJy2q(=YrNq*84`#{n5>p(cRdlymCDp{<5`3 z8EdC~ML;zK)UpO$A0w04%sokW^0PivyR0kX(%A93xT5cm9+%&?{$A^)A_3&iLq{3H z`S37Q%!Dl$od-7t0P4wb-Zo;A8ja-U)=}G-47&S=2%kPL&hSomxyoGMG!DtJ#Ll|f z=i6=De^CoB7Xa~^GfZl1G(@!uBH#9QqRqFe*fH$boyg2fs=f?tBRZ>{SPRm}zEV(VkB*c|{)RH|6t3tEEp z#*qPu%t^L;|K^O@uy;sh zt2EPtcIopr^?zeLRc~Q|%6Y=>ak^ud7BZ!n8rk#d*}m6cJ*?k$Q(>af@3wIxFzkj| zHYvvg?SQ^_gRp#&CHI~S*w<^n$KTjS6%v zQ`sYJfRF@y9zg_AH`ulLneaiw{?x)yR2=)TAl~HbfP{{XfDo=y-eTe$7G-;HX0>!& z06rvu#7zg5BcNT#ANHe_3%%*k?0cw6=4=hw&6>lER?p5933Rn-&eB#b)XTabh~;t_ z_@$eaoOWSg87%?&-2Fn2@Yzq?Po{IMl^xX!?jeWUGM0pvvpYq3Lk~IA{<0Q7NSg(} zw>BDaYt?;yKT|j+C0QKB`vry~=RoPxsH&?!0&n7-DD@1T*A4N?#PvgZp2)+T$gl|J z<~`+79F{I$-?pg!Prg*_Hqha7Yev_Mi{-w|5*1+&{J}PC7H(-8Cm2gD95V-Wc?pI^ zg`$D2wz(1uSO78F`Ngy*7H_%DjyOTp{N>bfq35d`iIIWNE$Q=1lSijvxwHs-!SScb z8(fSXZllAN5h%mTT4^KWF^HI34>1%7AV`a40)jLHcWC=?D_lvb587olMJvT|2FUL~ z$}T>%^A??xvQ@BWKFlGB9}=cy&u7?`YXV;LYIG zu?kLoq^t56j?aMZw%&5f_~zxg$pL!^RT+DRjVZ(l}qBAJVg#Nmc!uNum)a!RFw&`}>qxw3456MC-Nr*=> z=-K6IA@GE@UA`aM6jY+IRkY8`_M4&$V>(|Y4%afGGZruj)mGBu{A^HAp0yGdLPsGd z2G9hWQIQ$+l~YOQl3HnLdy`C59~~2}?|Q%Qj9Rc_@Au}y=NcFhM?-ce&Ep=8xp&ZE z=@16s4Gd?Y`h}M7Df1nFH_@O+W~na=uX1D-;x)k6AdQRS!?|q*t_?LdFPW^hi~Uf5 zs>(!Uxautly?OqaEPv-{#7L?tnVrfncHo_svF}r8S$6Q^U>?yHS~A=xS__?E&xTWCopZFmNCpX%za*q$T$fk zhn}VX<;)nwMY?QUZB8<|^E6*}O-Z#%#-8Bgmy%_~f|(RtG5QpeTOI;1yEa681M z5b0}Sbm!epQYKwDBBl2Cd|e|2P^Y=J_G)%Q8Q}g29Ss%x3YUQ$E`Ia`3@W8D+LWW%<427*=k`I|lA3DxL0Y;9{#y$t zsM%%ueY2fOo{0)rP!{@`RuX`H&K!2CAAH2SG7$t(Y1429w^MRD(p)#+I^haS2N$HW zr_8iGI3o5^-?H>0keG)O*RHG;;CZN_dY`Wa=uH;ZEn44mqea*3xgrKNz>$!_+h&_l zKTphDI2djwWK~q;<8h9s3X%k@(iP(dZ)Ku0x;Eh_lS-M%mlWe?)92|PC2+uD2$;(} zxW#TSOR0GRKx`> z@v0w6J2I3fYX-Ap_(6H-+XoJbzGw;k+>3!C`z|Wi;>L13$XT)N6tu)CTY=Er$#z(1 zc$Q))YjQHp$w+=DbQO!%X=CZY;hG!K&j+>UQzDQBX9yEVcBT0+HqFwG} zGwWCXy&nR|ocku8L(4vg2x($RvS;H^;=2T2uwd$Koodi{G;sC&9e}Pq9$!)d_Vl zn7*WtIb44W-|a~WEt~Q_uBZ+=S++hyG7{JI^Z;B2v;1L4H&RipFBQ;O<4 z-$%ZgB8jLeqTN+Vh~dhhpe6W}2J8f}KrY^{pn~(g0GznYhWow)&TTpBRB7X>woB6% z7CcM&j>p264-f_umKSa$&&LVFM*H%OLYw zfla(2AzaE=BavQRzEKAj|hw{WqnOhOgSo<8^1Wu6MuTT z5k~q2R|3x?9ysjRyzQ=i?e;Y7=uC|bp?1|2y|3wl@n-X3yu03{HZ=U5=2voGa}@4A zi7Cj?8xyxO*8bk0A1wL<$SSCMLa>z^zpEN77=?*$f+fapehh&Gtvt5g&w2a8wucTw zN7Vj)Oy++K%%*IB2+7BP;r5={0@Wv6U>>u9B7(kx(SP~mr*JL8fhu$=3v&#*rpj{P z1pDTi<#Rwx$t2OZ;@%dfS{^^E9sJy_((SX}uRFJ( zZGd;*Ihct4)bIqkjm;i`wWzM>O|;Kcj_I{TS$f<3+%?@4*4+4-R(o(LXw zlVa;5KoaF}NT9mZVDIWeSx$p51##X)8feEmQ$nXJO0HB=Vnf0vwKqghhpRan7aX56 zXfN4fN%^)q;35QoK%kub3POYyP~i>&9ciJZ~H z24LV{k2RzFR=a9fd+XlV}{h;}KnfUzEh*wLS zWdHP~`@-lY))N&JpNyCx7qaN)>U0+9AV5LTP$|6H&|=jSJXf+ZR!}yKW9QG^QI@II zOkCb=NhUIaz|%E+-a%!q-g&RVyOFe8nWT7Is=+BHrnY^i|MFM<4#l_JKaPX)5E;0- zay`GQq{>DcJ_rek)EAyZJ-FV9_On2O;Y>#b624g;XL8Tjo-?5Ck?W9w$mmb9B=1oFX6 zo`#W7@}&*@ON%^;(`y2kX&Z3tB0IQ-)=WgV(^S4-g&q4CM>Gbt-XZN1TN@-`<5{~$ z!HnNfXiK;W-2vF9X;V1BurOaWY6Xd@pQTdq#X`H^+>ih~%G9 z@^^H;as1D4NTvWHr`O7o?d^D^klxT`+WFI`us9f(mu4<1C-2Uck(XSjdBd!@vk|-F z<2APHyQ8`0%ZqpC>BL;%$m}c#J-76Z{qh->*#nL?rl+W=2f>IiC&$D%d8?PGaJ2p zri%aXFVQi7$3(dP@o(j%`xb?%n#yi={_pD}j34S_D)|nqaC6$-frHU4q=D~8a6rh+9fs!pV^_Tc0`;pJ&KcX(LX^bW;=(PpziT>LqvA11{ z#@RhUT5I8+>Z$HUVN8aZ<_@uQwJ=dnJe;101`__> zu1ZqvN6EBl>ir}TU-|he)B&;(nG%5 z))TI-tf;FS2n91Rx7Q^ZR=GP4)P%_ptVbNq*b$x$*b@nIl=sCzzY6Fg$TswN>n2El zyF@yXu#coC(};E~`b7L4t*`@DO<$T`&95nbBb0{blXe4Xo{TtTON+`~q)EWh=Q)bO zVyjnGrcQZu zjs+}DchM%{OGQ!6o#~|INgB1eQCs?De$-58`=QC0gOe7}*q!=U6WL8Ls-2pQ zU-eT7(OBoqJ2iBdnyqY27~l7@V2}@Mil}U=3v7354dDk4+=QR3UfjU`#0pSwJbpA) zxa^Mc!TFPp=OFqN`EAf4W6744ZzLx;!f3Uzi;Vf{m`R+IsYo7s=3NV|Qd<2>X_Hb) z-%MY%iYnc|5iPQ{?Hl)d?>*=mOe<01Cs(|Q#H&ycVckd7>ik4B=VS^hQ_Z?yGP_N5 z5o8dPbmNH!WNiMRgL_IXGit;&l035M`v(73WA0`oxWwq`mnwVoLK2Lf=@cCI8BXj{u%es_A_>9wHD z-Q<|cku?_j zH`>zCt6vn_H<5xnt;0+q0z9yP0t^JuqmkLvkcH7g45IYf%5Pn+MjdU46LginwyoTM zN&?*h-(QU+vM&S;{^OD2x5dv2oKvJ~hxUm5Sh&YS!|$G|wZE(iD{B+OX#t>?4I)*) zR!s=MM{}@aWUP!AIIY|%-sW&*q=I^jPKk_HA6qXqr`l-N9Pk9RRG}vM6l^Ok4%Yeo zkmnw3?!|^2_0RHMU!#7q1vZ5LpKlii5cHB2S#qt2D&!4=L^Q%#x-k_Q3^FIU!?^~e zGQ12lhTMrrX>}OfXA6qr6$a>pshYDR<>vEiyJMOk34DCW$viEA<*u2<87wZ#$O@S! zUURd$y-L9_B4l{d;(bk*0ZS9b9&r%Y-=tff^%~e`3fyU9r;>mM0Z8{+E|9ZZ!e}%n zf$qte@aeM4ZMkW0y4jKR<|KBScXjRPsnpU#XqDrMfu^s3zucQ+K&P%3`IF)YZNvHn zC4~G+uF)qnr{97dU1TEYoYPdrNY>J^A{M z^sI*7+wZ#*kXIJ+0>`ge?}g&~h(TCCX(ETUN-DFMLV4`#}4$ zsW6mq>h!BppWWA$>X=2_e{x%ye87ptQF>r&0D*=wpmhO0s1!cogSrbtQ@eRH=-6~3 zLjI3ONm5w1*FU0#@X+rFa9&Ts2hBQR)o|v@LxD1jKCkmv0y08`xAO>|_G!`zM106Q zXH`;P4{gjKd-0PYyJeou8#njCgh|2bQ@`T7SfV&F3TzEeHfv& zD4r@gy*4BhDdB0wMslylXfM+t?7_Ue9WJ+bV~^@Gi%4w%kC?>EKGoh27kTir*>3r= zGps2(^gDYNPld5cdDWSXf(xR>QTX36#(sJOE&H{EnFV;!h?Cbp%lFb1d?TL2m@O*e zooWjKa@%BAEj>c}%Z3@1_JHU@R0y823u8wC2To7ruZ+ctRba6|F-a`-fcot!UfDau z(z8QqO@_^FI@XVIp^U*_jMQ#YMSG0e@q5l|g7lMyfZ`g9p2j=ICr2kc#cNw}P(B^| zH$7J!orETs5<)R0N$epi%s^@j=6-bu@6L>0KlhLuWsm525>s(wSh=-- zKoFqPRjH66DKL<4##J~!?^;7cn3byeG${7ZTah8?zGbF%lg3qbPtjJ zrwH7CN16g)v|ITst0)Prz}2>AZ4`FB$zISqi%X9hV$e>Nw{Cgu(WKT}M;JYtVg-D* zXM-^o;y*s)xM+WE#82kBDyV*+CqE9ghPpha-_4j;^lu2`EeH7K!fBadUKbdZYCKi; ziUR2(Vo34ij(X2e@{iWV$~WMry*AU6g+GL1$#;#1s)8eSaQm8(u)CZ&4%`g1{>c*n zZ=39OgiU4AHQ5e4nNHG6b1R>YGk4bMQSv)ttI{elW2TuVQ~$n1IQx~xiiz!8Y0;Fe zLD}Db4|r3?EX#mz!hm@)BDN-DMo3kU+e8LPa%l_uH9Axw35|oCKp_FUJ2PF}&d5R} zfNuROirNqxWV>y*T1r}7kVhDIS-u67D@&s5-nT48kuxvp+v}%xTm{I(p-XEj5wxyj zDU!jao0aUz+CJ^|bC{y0DFSGG8E|Caf@u^NbSeM7ws zE9lSHJfFmEl)l6=c%N-1jOW>Sol~;?MQUKYH&xIJ=3)xP`zM?iIBe*}xxup<`?0L#49*W|4Ez^J!$dK1m{?cpXtVVe8*&QeUqn z2K&19e8vk)5{G!8@4L zEgP4VXX8nP5o#GLe(lQ^U(83lIfT3D8}wsNboKm8n(y$Ftud&^OBC&1j*|uhzuw%m zHDDcapWd5Hy_NgXRb|3Ll%<^)kVfffh>TU?$;vLqlcv|FJMXJzOrt;up{UetayAZt zr*CtOKl^7w%n0~AO*fXB%|%LC8nWT+3LuF})IO z;|_H`JY!CjC|Y~h4#57m+U4Ie*R4d9waVU=m9Zk7xGe4!$$!McHNeZOQo`(fxZNF!t?D#uYA$y=kR|WHUfGGw`01=VRpcn`6^Kx{@(pSWt#)@kzAUmokHY4r=gJw1jv{zowLG*Q$Oic&~*52ARE__!fC`Xy%nmbt@o#wZ7ab+ zV(Gu;p!Ek-Yaclu>Pxaptdvw2lo!gvg+RjulIGalTx^`F7^Do80yE`Dns+?c-f=CF zE|fR{QX19Sjzy;Q5IDrY?M|mS$F&4vRd~!0ak8n@zIx?dt4#$=|6>|Lc>lB$9y0}B z$sa(LqEOFH(_Z@xcma?gj6xZ|Qo7yXox{GY+k?JY$oCrH76B(ht9e(xTR0U#F1B2@ zHf=Xw`^cy>*#I`6gsbMHtwC2!t*&^jS>7V#Sk#k{j8IV%-^NTJl!Ub zkrwIKpL#d!K;+(^n)q8BaD4S|S@N-EBaI61i#m+kor3z6yA3HFnbCThHqv?;w0fdL zLW$f&ilFev>{0D1(}vUZQ!tQBz*uE%2SK0;=E`z8Gu{biK0s7p!(Q-TM{vA?shvN0 zL1HdhhvS?Kh)6<$`Ce6}QJK^M6d8Ru&S8zm+(fjf?V7L77{sPg?&`$?c@*+wERv_xj!08`|7T!95Y8pC_y!9b z$K^ti@m?+*UL4x9386V$v(Go_QBm~hHQd-+>H3mflF3AAojM{RI=N`H-V|A8{h`Sn z6DWLrCE_NP4{C(AvVf%jaU8QD18I(dURUQxf%OIZ%@6>D`OkMYvAq40d2e>HLlO2U zof(-+0SxK~gM>g$z^h=h>~QDzs@lnva-8662%nX0c1G9^fO*?cJBQUG8O8#tA)4J{2s5r;6n za}^3a1aAJ0+O%40YaL)G)g%jTuqNFG=8ahSRV7ZmW&U8RLtWlkmsZ z1=JPME30xOWIR*(@rNf5av4rFlfQu3?%GR^BfR=yRm!sBfp4A)skSspzJAtojD*br zzioNm(7PFGSH0Z`AC!;wTwi zYi1Q3%r4)JBZ$mpWeW~xEZHmy6XC>p*|6tPy~=P-HQEqeKV@2WWjz=u*p#+!&Pry| z6xaN3G;|_Bgy6F>nN8G;;jq#fPdtSJK_~6i+uO<|V1oojnOMV(iqulC1x0daaL-Wt z1H?bKSLQ}plC`;84sEH@8WUYH$;tNRmM7yD`qVZ0KXjKD28_smtKba}C)9z4D0T3^ zEe`o!BCLBfZSBVKicGOfrK)F35y}5$K3E}{l$~zmGcGKH)tIf*ZP1O={iz=$KGEN6ujH_8O z>U;Wzvx=pQC_-`rk(o>l#Th?p6}ZRx^K_I2Q>))!pW+5zY+v>do2Zl!wPCH)DeP>V z__WmTLDtEw2cK>3#FR8~;M*PUK1pZq-E&nULz8xX@P|2LP znS-iEcga36HK)SQAM>auQ@y{bj#N<`ii)Wws1(}lPd4bl+-y{i$D|A-l`8dqC$Mw*Cd@0A@vcI zwHfN&`o_s_!J{iGquCY>WmREotkz7l+FU@XHhWGll`#31Ed5o-DP-i~$f;$jq(b18 zv&eU5w?c}o!4;RUBjhjQWDwNO$(kN&JBkk69TiT8`Yg?RcZ_$@Fs4hN!M@KtS8ZE! zK1j}oUM8fi6&r$*Q~wQs2qPsjo!YseGTx@q;SDR;SD@Ehj>Xh%iZK!b1D)>SUDQiz z0F;19dtbT{aSiLBz+KU=vYRQ~@ZSY+T|5}tOh2cMu)1?Y6$I&xP^~)8ZT(NWN>G3S z1EAqw(ZagC?Bs-tE31T03zz7BofY{Lb&9l}r;6h2j_wed>Vd%_MY>(mM?^~6ty4Qe zclJU`JKnkdtEB1nD=>gatS|K9<>T`j#fn#x{<0a$VlzOCq-_r6X-rVmJD!Ci-*GQQ z!oA5%aal>AOToXQT0lk+Pgv0A{>2$sz;CdXi3<)#sCfJ8NK?vbe2vRI_dU@L1|wM# zSgUztJEtvPgA!63Gg)Du8zd_aHj04$N);Bn(^W=(wuPUzQs%BtzKS`yt}?iN$;M6& zKwlQI+ud3TY)K5x$+(4pwsbm<$Vsev2sQOo??-+*4T?Xw3*!m@BdUOkcY+b@xQi>h zi;iIf+r!JcHG}*tapIYlyJrEsr%9yw@}b z7!#7dkF&*rr1Xyb7;6{K&q*At=xIMY@vY=7@DAjW@R7`P%ai{xK!BXA%f0F5^l@x2 z;NZzBZ_U4~-W`9oG{p!F5dBt(bwOi=42}~{rUUd*m7OTyOn?5K0m1Y#CqUxwLRk_{ z<_l_@Eb0#`0p% zu>y|If5u`&M;h?b%D6P=hOWXUu zu$!gGiVed|u}Z%&LAZCEm>uIN_f|;P%PAF=Fw%qg!wwa?Uc_1}_g5_SM=m-rJ=0{A z$m$qvQYh*;)0%(oC|Ds~5C06O)kscBtv6!Rmu*!Rg}23CZevFjAup*b!5#R+b)!xrF z=%Y%l#nc83U$|p3I7pJnP1byV&ASm7ed4?KQjxJXK1lFn1H-R?bFvb&gM7f&ST5cb z_I0(1>)c?+W zt3JJX*+dm6GnIufXL4R|Zk@Ly0v|b05=L7@71Lh?1LMpP6Xa7Mo+=YlWQ4itb&vV6 zJ~~=EPSb-l#e&r(=qaU?lh5>wjsPTLr98G`L0QuA>3x~AokO~IG|UKAFDTw)ublZe zxK+&`Pl+GwCm2W-oNz&M8d_MBFl7SNMVmBm$sL!@9=z^X9$9JZMo&A(Ce{nh@pdZ* z3ai~Rn=@?v3gsEcz{%)in#a>E&BLk;0>S{T<-#b|Z(}8l&R>pu1+_+sIkVSP-n9H0 zx`XcFP$&r?{kY`YjJgArQ=E|Awo?mXFeqKtZy>6FNIifxTYl?E;@B`;EljxH1-VN0 zVSv4Qvc<&_?Ww@CwI$AWi7y|UGp9xmY>EjMGs!<>rWdA7b zqpoqfT4c~sz;QoX)$Nr*i>-(NV>T|g2ICwvH=g8U5Xd5;GebS6bK@v!=_6G_b7?8= zlN6mJO}Qc05{S5c#YcH?vBz)aLdCb0_mB%CA%C=tShin@W;NvMIXI?)cO&1~!V{O( z6>5nxQ?S|J`|e&$fB<@?V{0Tp5aUA|SuH}>=vKAcKjbe!2BIN3Z3`Q=hV*5D7^DHp zdn`UJe;rbBydrYI5mGtlbG%ZPK3LAY4IaH^Tg~eVkw825@^L~f#8&!EAc4AZkUtY1i&WtN5c-sd~ zkQP@_X^>AAX0q%^c1(UT`AtMY{n(wzMq!EMH?v4{37{Z%oToT0F+^*$OYY!Ru(9NY z#q_XQV4>u5oOO{HN=_|sj0{ta7_-9Vafse)bv^5O`_N~D-L&UA^^ud<3c`P-ya60Q zhAgdP41)fNu-2C$Sxx_Z;5k+fVdZSBN1YvQB}#fD{sF}qZxo@>M}nGvogX=22dK${ zoIXcO5&u`Y`gqxApPO)-i5ojrH4ygsZ?!+H(SPzcVbMWtxIjY*89ufKq}Aa%`Y*7K zwG~-3fw-;KH}RJ$(%#JBaxcZr%`<&l(8=34;r(|3rLk?lw9AZQI^5#>-7qZfbej;R zZXOTwu}V46rK6DOV;m-NaMn(WU41iH1al})pUsR#A5sKmabB~=FK7q+rskXOryh)b zl`cDJe^s1}tyGB)xA?!%mJk)ny+HOfLB|Kb?IkJr!q=_%dQA}RKM7keVF%KI)hK)E zjjhdN0AFe?we|853gbZ3PKWPfvKjy(i zccZ5w<3%QlG@s$>sx|?ioH-DPixDY7j0$G9wpR)%yLr&Ij+r2Ib;gt2)nf+>cg9!p z0RBsa#uMgx;Ld(oDr;jwGy21r(WCE46Xe92E1fR4kVt4=tK)UaGo4M_Mm+aCfnJn$~81EKmC1pCV5#vD?TNufW(%p{&VEIg7ybyjraUZ_MY;X>khrYQHiwBRRrf?l$`JoI zj~WM1rARHgfBJa?79kmIH*o{n0fav$zaCmdJ*KnM9wiQMgLM@ZyBjRQ<1%g~S3?An z(+RCN!td(~lt?^RpvEomZT`S4viTmB7L8JkFoAYDoR*u$7Ev&7eN?)xgj1d#(IPah z0JSRpr;l~C_NW8X#!_pndHs7~`?4VH!)%2|914@uE=-O>-?jt66P(l6rfVMy^am~+ zpn2#Akg!!$;O1603lnWa`)I@Q6c4@}I9IW}77Ze5HP9#5;r=inrDTl9MDvjHV4dZc z2Z&!IKl1baa}qPjBpn6e)}bceu-0iR`Kkh`i{2r^?k#(43UugPd}4Cg>aX31-6y#7 zZC*TjImrHQ21+Mi-|rMqc=sj>W-Uzg{M+Xb_JZdJ_ zLtEJ?t^O+1CrFb0Vnk6np?`zy4rug789zPG<`3ydiqIii<`In~f(=0KULswG{`{pl zE*Pfa@cBeX_7Vx!w-tTnvtKExh6;=gPfh>FMOz4MI{a66e$`YD&n&LKHIToyD6gbz z6T)KD`XAfS=#z4?1QxnAu2~38%Fq3T)xx#7^`Mye4F8?2$P-=%OgvAg6R zI1!Pf>5g@me0GM*zMm1qINYoB@UA=uL1Tl*;bmV!_EKj(*&D%6vSIulrO0%qz25Il z5Me!1_G2XMQm732#C9ao3b3>`qKy4!g{*sTlE>}WVpP>l8o!Ld=6xREnJ%+NPQzZl z^RzEO8(Xq;Xb1nZin)<^G>amPBZ72YxZ2BkNBFK#icg@y8nTak&jT{XHT8$2X5>GV z1VkV`wdakd`o|~{Ku&(Go=7ZZ2XA|S`0iABUpNZ9QGa{K>A+F1)-AKzSpXp}_m_EU zj;N1@O00FZR-M1KR>7y(y95bTRneEV)uav~#ahoNn{I$TDT9pBi=n)nz z97*?g_L|gQIv=^7UP&4=?ekU5>hEXcqud9Nt>&_3pVq}xpPKQV;JMVDYy1jp*~#gd z5(8p)A7&I!oxs77!2l@3D@DVm@DB?C7zFT<65-vj?IA+Fa`}t^Q(SbDE&fLz{Kfou zZ>>w>JhA$UkrW&F)KRhZ-kLU5YXgK(DitRkj7R*4UF`Pn76xLO3w2Myc1o9-w| zB2r2|xN{Z96_?daETX%y2R+o5@@>YaW@9>!mH0{5W?r_m#~(r6Ox1l*@bgO#EZu3L zJnMVMFw{#Ic-tP05YyW$Q)-}{bmWK8dmK>dC!V3Jh4WLwS-|7L9`v1Q9k1YfCkKod zFA8j|YlsAjr_V4S7$h$kBphMowmz!Sbd#vMG-;71mvdH#?i%F#_s;Tn+lTf)zc%?* zG8yq0xqaA`bUk!#w#AL0m%X?BwJ(EPrib#2kR+T=dqfwr94z^IE#LH&OnYnyJuO=7 zw6bCRCZ<@Si1WFHI9Y4QJ+q1VA~W>pi+%FSAO3lWUlA)^;dbh;@w5`J$4csrDW44M z8>-$NP*eZD)c%wQl`=Zv5#G#(7E{iA8MdttYjF2~-JLuUy-uN4_tigIg~ojMxy-OE zER!fWPY3b3U5&S;t^nEtfZ_o$JA z3d3U9<%JBbqxAyYoasckTs zgmJ+z55e=Iq|%U~NF$74&|Y;`g$dUKg7>w9M6!uv-KwanCy6(=$|tGsG^{;gk`hh? zNo$-97K7hd-uh3BL|;Y+@|H@+_(LX2^3YmnZ?WP}jkuE<7G~Q2d0D3Msa(-VUv+(r+nKPcZW%5M%~r`t1t4QcdC87qQ!M>Qj{4EAB=>;2$(Uy=ct zA(hYVy@SS&uE5d;4QL)XAsB=2<6l846)H>wMk%hV3oD+w>wND>IY>q(#Btj# z@u|{FUhqtp7Q%%=SFNDYE`Lt#abC?Lj~4Hw}3EbWlO+U{}Kl zD?mUHteZoY8mvr|idUj8tbHOm|19?{1|O@fm-_Djytt4*w?8Ixg@U<5zL_3WG6D$U zQuYmyV~NtIAo^%l;v#8CP_R3)lhugo1%7kVDvKqqDSqpZQZ4a1PITmPG^eAG?&+Oc zoj6U|S9cq3&l=j~H=xVP&F?!Ao)X8r5$5F?^i`u|x_C zq1nzS9azbEWbmp*&YOLYgUNXTgOHk=RWhQ$9z`j*QbA_Mb23fUI`Vj>qfjN+hodAL zXb0FrLtCM9qzIur*$NiN@zJx`4Q07dZI8Bn&XxN<#AtWlqdbx`Eh)KWYSp>~d5=wq z)2G7uQ%9bYU|CR>EBk{S!raCh=11?WG9v0ZG zCdg6@dtdPpSj<>F^2ojQh7(6q_9#3F@is_QAcJX%2LN+VbEYo zPJH>^!E{TEer1|ffJ(=uNbg# z>A*HsJ(lzW+!gY12B#(o@4-5Q-QnN~L#>fId9+8vAmkf!o{^zEqq+qd-3QXD4c}ga z@wugJV-b~V6GbRIiKPvX`WtJ`ypO$O8=n{s@Lt{02yOZS=XJH)sS+$ckhxWoB#w3L zd5frarhy!GqAm>;{kmHtKvISMKy;42_65g1=v#yE@U3PxE6Bb+M`NSlN*8KOSXKyV(ue^62|UiJ?|9 z3-PG2FKbv+!tT$uJXW0Xyh~E#QTuBme3wRx`Dck$8}N?_se$hDAksIY)<%ysv3geC?Xj`WeF@P7wEV0{QFK14gJAuZ%KxGJQk4V2SU z2vBlCOa#phOttE^DScFpL`Xs7Zw>{2CQAM>w#VlXYN?T7&MA^%x6`;@-yCf?qSC+= zyt*r-fdo=kWqFZ3VI|`Cw=a^!=UEIT@lvPu#BKhzX2B*B=3YELjW2DgJ=N&^yr-?T z?cRP#WWkxri%pXXcK}mup$R5|4hG zNya;nk)WfMW3%Uui#Kl<-C*8R1xh*2)(qJ=h2R^NsGMLgmpWOm8MQxNT@(tI;W)`; zYAepP(Rdtt-y-3@tJxHUa6eW|;+|05-l?WYswIkjx_3>2Hap{XI)Q6_T7iXQYBHWZ zFXzvBxS#skdO%?)(MW%|@Wti+0KtP-FLBAgwq){#rMdVrpZ=W2h2-V9jn0G7Gq=;? zSm7J{wTBrr)2(B{NDO;PF68a#VgV}Nz-it{>*;dVOWW^}3=?&Ht3ZVyL^7`BSdF0- zg?{|fkX*yI^68u+XoUMSu(Tklyf`$D?VfuKX2Y^iD9g65xG)k5ND!qa&XbEBuKTE7 z28H(bISzm-;SgsAA(`cl)b)-3V$F{H8dQm_`fiVQ+J=idWik|^>sN9C2v3oJuQnig z{9#a20e-ZrJ=>O!dW@P11sBQFHc~%}_2^RI>U~IZhl}Oa>UbaewGa>cmqRIsR(kr? zYktu-`x0|xVIp6(>Dx?ND~Ftzj4L_m1y{+nGDta%A*Kt%`l|h5=@=51$mqWKyqB*x z6oibwL^Q+2%tW!DR;sxe#6Js-ol4uKn--OqjfI$@@H|v=5cvT&SmvcKB3UZSr?G!@ zVA_rDrcn`uSy|~+h9wjeq2V{?b=6+Hk#dYP);;MPNIj%|uGBYzM{sjwDVzT!c~ZW; z&-$V9k$X8qk4tnbkq{*ub&N!*R+#=p66v%0enf094P)Lwmuvqo%U_0WkEwp{$Y1NB zSKX6wzF?$}pMwa=-GtUxW`;saJJr8jtaRT7!eqT1n@%M1A;dvljATenqJ0Rb*ils7 z8hWPY7d^>&K3x;@Y?)PM7(FGOPQ<9Hw}H1E`8|VjFp1}_MS_I%-a19>sh+^Hz>4Ri zsOTL!g!{pP^aOB6(%$gM?mc1ecT(T2t%Ad`Vlx`- znzm{~nx|af8bwg=ISem6+iI$=Adp4$k`T-$tQfLpjlGrD4oc6ZqaESY$dJ&8tAJp7ZiS(xSQWz{u4yWm^ zye1&NdQP!9Jde5R&BWX$_%mip<6HHe8-bgivo+QK32J9lUFaqFbz#OrM;g;buazf( zn`6flLRdxgiQQ=62f3w)UK>|FjbsKlUZL+osCKtPb2^QVVMT<3#jTk^CCz@n-bqaZ zcw5t5BC0z%_kqxGA0!ap8z>${*QrP~TZ*Q+5kKO)6sF*FO*G>je~`>|U0W*wU1&d5 zKsg|k@{!MPOkBjZ?-SSI?XA=tdDp8TKSIvrR_>)dQnof;gf>BAhFg}={G44+OEuKF z*Vp!Z2tQ1-)du^db30&j%VbzTC_!>t9}z&vEaBU*EaKs&k*C`!F+fq5Egt2n9^B9w zgp&_Y1wJWcwCO|<>CUDIt+bs{GbRWH{}vGVHtvR#iqgCW6UE0u+QjjIVnoiryAa-$61z z&ZCi5bRcYZQebFE3BnK?DvoqAWnxfqzhR>L zS$mhOWga%)$$*zIe{IQWG>evXwLhR#EZv`7LG}OIJIk-Ony7EL6k1xOIFwQ-?ocFX zaS!emq-gO%30fQq!3kE}-QA(IxCM82cWYAM3HSZnYrP-dKjF!m4_R4R=ggTivuF0T ze>3~QPX}_HCYO!X8lAx1%Z_!b4ek#MmjgW?j^C1Q4@_tY10PFH|D;wA^|hI^F6;8%O0Q>kh-}?{=jhvhbuzbcWPt<7Q7C11GVqqddo3%c zFsSAz@sQ%G^DfHLk5&uz$Pf;+E}HExm!>f^s>J!>=X05Q@IwP&F($9(Y}ul!MS{^z()aDG19xt;GnGG_D9&!0!R6`!O zcq-&p*k6$4z98Z>pPKl+JlcZKgcwx{_VS$8UOzc#!}yoC%M$>fi5^6G7~+vf&u0|J z#ef4gf{^LZe!?ezwm6OOb=Ao&CJO7}r^o&fl{YAB5{lTu{AzR4t%86o#4%^0S5ex1 z)%V`qa~G?kfq8kRVh7QW4GQ?>M#y-&a`ga(ZiYGHhm{sjb5CL%_iDX;Sn_J*r$p^t zuZy`ZC$)Qbk9n4zIRzr7tM%h_$Vn?(GIY4S%~h`kGM~&}ddS3oTnD>w5b*x?O)lpj zvP(lcxQgbkb*e^WYHnRZ_H<#Ny_z(a%XFhwd z_-!V12iq7q67yaDX5&zN z=UWidRFNuVrDF8HlIGQBP=0Pfg6OTZw|#BQvqokO28md2>^xeZv>h~0g^^z%CYvF# zdnzItZ0SWGnk0YTf-|BRsstR})xAN)Z~f81)>VQSqE-F)<#*HJ9z0v!%)O|rmdpb- zH{6+X$_@FiwS$K*QYQ=^NrsqcTfS5H_PGoe95B9FhNNlGUd#d0i-J2`bjhg%K}7} zP6j7D0IP8?taxzNtCoA#ztm8os?5XAgby$#w!>+2o%2%TnB2Rz1p$Yb(-{a28xL#4 z5WU$7w>*i+t1i9P6hzZF^2CjNm=0=3*A0{a7ni+;P^Q#_s+l%jA=EIug#DEp?!lG} zwO+ATlK}5UF7;vhbYCx^x9+%P%&zdHHq9C4>1pLw(G27}`ed{2=+a5%sVvc)h%fOY zMMQ3rvLDz*Se-GlUvv_=Sm4T2mcAiOpIyR+u~sQ>#a$Soon<;bLz}cI8Is&{zE{y{ ziJ(>4He$P8@VX>>^w&d?3ar4c7ZDU>(KR2K3>EWknC&n};>?xa*3iU_@)WYlg&Drm z*O*sYESBs?kF0Ww;x&(>lEbBA!OAmsp^Pn9t^V@LS|!5Joa3B}o@>@)owF=$G-lBq z*Rou8erJa`E%TMF9qCvNw$OM#{Z3JX*@hFCQ68*oyBCI@N8=(>yp`^GLR}?mbqt)p zTHpTtdh3Fqe;E+4p_*Jx^7i*MMO#=#Bdk9gl)h~vH7-13kwt@H?iB-H0pUuQT)#1i zr|Bs~qvs&$N3ne0GCM`lqRVRB9aL6HW$#^`5lbXbJHxEncQ-m8zoC9uJX0MRGO`FL z7lDdWlwucrP{MqlGB9l>Hq-14NWPFXj!emHJ8d7j&`&B0$afT&-ekSp6ef}{u(+DU z$HG?3SHfDnSX77GoFiD|_{djv*l@R^oLiGLRmMk^cRZEWJS2)E9XQrTq|6)n~?qI?h+l{>+xfO&IF)G2GJKT& zUCDc$x&5I`Bjg&pA;Oo4cM~1M zToIi=`yeBa*SF{iboi@0Q<8t4R~PtSoL?`8>|;DTXl=+o);Nqx)+xR@BrePDyQ$?z zxXbgi932#kD!+zD7EEswmSi>T=0C^^EpW7u}6xT#xi|8Sg0+)U(Er-Z8DIH+Zoyc$DS{b}PFVzJH8!AqeCbYg! z%%o6R{f=Y1oHA0q94rsJgs(n#-u+8BU~!>(lVEr`ce>qF3d|FJ?~Htsf69+1ug(cA$hd!mL;xp2mKyOV)-&(C z_B5&{4!l7@dc7wGHKx+9VKi{q>Jc*eGZ@qo2K#m zi=x`A&nQz!T7vbhc)U;Wu23z=$@wEsK)cz2ictGD=-cz*WfHl;n;x|rx~U@MtX!L9 z8Vs8A*MOC9zQa_`C!Jza?e(?(0|+?zx0OXRC13vJ)W~ieBSBW74S!TJ48YZ51qOZ_ zz3qHTBS&UiXJ+vrA7MBv&PCxM7UgoKx)j%#d83t)hZW3LM3SX-KegLW?(HmN`a0ft z?IL1_!Jk!fnZ;j#yHwgiWby}SAiipfGW&7pBa4zGa$rN~-b$b$#}oNe$U%%#35QTJ zA`Q?Te4pdj&*4~Yhc5Uq9eMD8GwQ?Y)7X{+ zq83(uFS6PqL8D@iU7n}K5zOnCTx51^MeJYZLIpjKP-!+kz0p0;ib&OJNHPK=v0@8O z{vuKWStrMBJnF~k0u4nEZYLLH2YHXj3|;OXN!SQbaMUURMczB8NbJA+ytIj%e>A(` zC~i%cw$+835XUmoPQQgb2%R>O*h7&>1Rhmcc_hwUvxc!=)>YCd5vTZlOYG;sU1F%d z6y(l+E?tL|gFaAgf^*ML>3gHF)kl7zFgAm#5ijjga4DOG-4bdN+^Z zc89(^&^*!Ud$4FL*tbh2n~xkx>3dVu!_kqmZQ#q0Oo;Ai$LL*%Bn#4^T|BS{Ocw?Q z|Cm^uM4iMeyUd+&?Ivm1x*HrG*r9{2LWZ* z_9}^{6~gIyq4b%>jRAd#w&NSJZG})ZH7&wE8g=a_UM`L+0D14z6+zPqrx>tEb; z3v=inbkV>pqK6;;oq>+AWr+?|j@mW7QR*O4M+^vJ+7%#-Q zkuJBS18piCK&Un7gS9rg$lQim&TO5l)BLS16j(@B(os8`Q8x7*urFtxC`u9Kzrz%J zGG$$!!0z|V%fRY$RC6uMEy_`(Y!=%rec9{{g!O*(T(FbKGfMa$Gm3?WBS!O3%ktF= zWnsaXeJn2ZReWyaK5HLTj$MaR7kiK`psbS>_B4}(8|mr{jh0<#`<@$Fv%ev zv~&5wLkoUF``z+2tjy;MIGLZGavuk`J#PQd=Em6jN-oBIl(^~`QaCk`J-j1(yk-Gj z+6#p&WHg=ic`^74cBsYioFm$<(*XOpGeBGo;VCBTscmREE3g@ZA~?_{VBx=X5TCMDO6N{z=c z_a8s$$qM%fP+Gjybz9A~DhNu6(0F&kWXswl!AheZ9tN|H$6^zx{_?h`^SDLHPzxMq z3a;{$eqFp_rPJ4aX5!9}Ap*V+6);NLsH<-sizv4+D>9t{5>8IszfQN6i*h+r^=Htw ze8nH#^@R1VnCU4UW9i*&U13?rC)MLld&vh53+%X{_1SLvUH5vvyss8@4^Cg2b)t~I zff7_lBGk4z#fW3(JBYnJc;%!~c0rjAiqZQ#VvPs>ob-p`8vS3jVr|xMP1y~L{SMoa z){3veg;FaHQThMHO89p3gAUxuVapSVdux%x?r?Hg=hX#|LFVXe$z(9vJ|M?)t6B&s7)&7RxM7ron)-GzBM@+Zaoj4ct0GMqGzAg_( z$iOEKw>mzXt-xs<1Jop2AEGv*BhwZ0BNdDFC#984(Y{sIRzS1w5oyF$)CEPbzM*`- zj}@lyDfPO6mGHQI&97nauJX=kV|`AtJl}shM!tIkoG{I`#-JSoo=9m&G8A5l7`~Ao zAMpNLAqhOyy?i(~(Vy=`S&cacGq5nle}@UndRZQ3(QITJ5Jf!O{6RbsmhK?`51A*^ zCLptx?Jq@JwNakU&~wvxNvX?Fk+CSn8cuvqmG(z%MXsw?=9vvYH_oFZ=WM71ChL_y`j}ToH0}7bv4a}AFa9ordmSq%Vfn- zam8I-PiY}9$8HSZlP#23dBW?>KJk<&0?Er%)-|UzboXR^J<5xy&HP3K-Nt}G;y8fZ9$cZG@2tjvuvhVjkJH)J=Z0{D3** z**;b>GG5{(@}bYz>(Kr7* zHR1BcQa|!IpSYhppeAhaF2mHyMCfA58NE(JW-tyhd~CZ*^hZOQQDXyO-I`_89bAgj65b+yYsQK)5v2p5x&7l@O%TB4V1na)% zXl`%{lYR1(Nlz$OvGT(5p?HqRbk<5wT|ox_o?qOrV+^pWF#EA4roUs!*g&BHmALf1 zCU-OXEA|PsSvNBfbFZt$&GBrI%%q`iyiVplM;n_(BI~s>^pn@}oLkF$$snDSt z5vONad<1^}4+rZizq^e8a}RD&v8z|#2tUY{+gjnZTs&SuqpQee{MQV{XL0UcO%Cis+}5%34%N!5ZD zIwhY`W)C;@W7=(hakGVk!N`agMh)(l{-wuQ8X-isK>f>cz=u@bs{-g8QU>Ta{GijV zwacQUwsLjYis4q4W;&)FKZ?T)Yv+=yU0bDXF34eT>pFJP+v`5I6P#_6GjZ;Da04E_ zy+DRp2D)LjC-kra6(m-bTLg6**bgWd)LMU@LdLq?Pxd6OA0>*mFaP@OG|^*JI7l~) zG_eVctya?!700tT_mgvCseTR~n5+2>k&DqLx5;^yzNzxn zaD_Amq~D4N^?8-^)Mv5e%1iDDkeh;m)~wrRwsc)FLrldKVrllh^>L!0@G$7DQ`E@z zEUO}p_0{H$p^DHsZ~c}E8FB-?{89j?$b>h?N6Bb}4(Nva09NvvJAb9OF6Zpo!2~d8 zlt5cycuN&ODCll@A5kDgFzOQwyH@_$x-@s+8;tv}%F6dAD}IRLh=vwf)n-VEg1TWJ za8Zm8gQO~Cacogu=pC`xd!j(3y0IW%*!|glUBOuO2Nnl~x#{nw&b2!DUNWA5i(K8m zL0$A$Hu_ zWqH(2VlCpBf|pbMgM9qj;%{{O2`787PNCNyR|2nJ(_C|{mpQm)Is|E%6?5@NXZG#I z%Y}i?uaf2@n&!5+&HlbW)>rfPXixLQ!mzB8v=fYxmEdd71L((K2hysI9&~cQ4`J12 z(dcU4fqm7R_4;H>1zYe<0BF1o{uTTAMVQ(I37Z3NK!~sxM+E7Yq0#YG2GTARHRkOe zY6w~S-F{*7%oh3VfbIeLc4H$wd@_gk#39bqoMWbkcs==#J_&d`kW9}Bqs(sN#(y7o zFb4gmqv*zY8Jv}}5w_&x%6H89uvf_bLC!!fp zQVva)^-LBt_~cUqZ||;}Jui#lT?YDK2`^CiIXO61b=c?h{_Cl~S8+lmt*Ja)y^Zv7 z0}%CMTx`TJG9`(Fd5H-{E*V>f`?HG$*-sNsslFoj@BmF>DuuX{{?TX3E33N9RZq6M zXwLtfE7Y5+kf!#y9XqkLx{ZIcrYJ_op;K(Mm%aUxXB z9e*}>mJ{soHo#_ClSt!5Qv`jZ1KZXB6?a%BDj&x+X~ELWk)OC&-5+ewC&pxB}JRj*QN_7M=Qh( zDFdStY2S9`U3{E2hKyW^qfwgcWu!tChBQnp?^NsWbC)wIlJ?#ny4>EMSnhWFVQE-Y z4SuAcToQ-IKPo(T=a!%0ZpiD1_%i$&dCWK6YNdZ6R*|z~IZf zRtjXEeRaFwCm+|f?hJtK2{Qls%mn+2)R_V<%LWoCcs-&gpf+7ULmu>&8=ko- zMoRl%zuK`_Ho#16!QT)u`AzV=E9D3Ka&mVP%IS`r!)u+2d}gnEXB*P#xnOV0lHE^A z=;uDEvSKEU^&bLJ3+A_`6NnT&N0K9e%)KNkk&8)dHSPt z$!<(I+#D8CE{achg%{}kILQbYZ^-^pjy~EeUqbXg^~*hGSRwtxI-YhSvF*|y$Yn0& z)6MR6Ikob^=Vj1nvBhuygnta(!h)nhhyJ7*IokXfx;8F#6T~ky`(XPQZ!wDBET-#} z^i-lreyy2!(0b6luV8=JfK{+HDZyEODTQ|RHP&e6WKx0qCp;L>1s$oK%>A_DcxdGO zC$k)6dXk6ZWlKXFVNDk%TBy2WQsq9e-F?Mg-}#uUzSNz%7R0peM|WXbNpa3hS*+z> z=^_kQT>l(%V~A8q*KN6v-^gzdrtGK;;1RWA@GF8DF!<#(S?wJHX4fxKV17h`xoned zyY5y-@+)Xr?aES3?6n@XH+~d{+UvU2@9e*osk04EU9M1n-dZNvAG8lJMIh9LTwoJG zv5hx9>m@e}sgH>g3M;z!2LvH6K?9HKITaTB9`H3@U>{ zu04E?UK6DiPD}hFmD+Q8w&e10xyH(7VMc=OGPwlTyv#>dx)d`?`>7X#SEjAALP+k@ zk|B=yFR%Sc^m4P|6fmQ#OF&GcGIWQ%VfnQ-jxaQ0?cH-`Rjju>Mm5 zGd;bF^_CYvp)`A$_p5^OzZ)c!T*V5O+?FIC^Tyf{D2;rPQeCLYHLPld41N-<_n3F; zM>@9G@0+{Wz^^L>_c)c*BiG%K`d_D3{_c?fUFkd$^nCQb?HX$S2)9r6sXS9w&OMBe z>Y#3J)9C*-ivK`NVLNKzn_)FkSvKY|4qQ7Q275TA0G9!|M&O*YnlJ+ e1poixar)py-LR(9m96^(`H_=Sk}MZD_WvJaRpmth literal 0 HcmV?d00001 diff --git a/posts/benchmarking/benchmarking_notebook.ipynb b/posts/benchmarking/benchmarking_notebook.ipynb new file mode 100644 index 0000000..dc9042c --- /dev/null +++ b/posts/benchmarking/benchmarking_notebook.ipynb @@ -0,0 +1,1512 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Dataset Link: https://huggingface.co/datasets/allenai/qasper\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/nexus/miniconda3/envs/fixci/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n" + ] + } + ], + "source": [ + "from datasets import load_dataset\n", + "import pandas as pd\n", + "import numpy as np\n", + "from tqdm.auto import tqdm\n", + "\n", + "dataset = load_dataset(\"allenai/qasper\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![](benchmarking.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def convert_full_text_to_markdown(full_text_dict):\n", + " \"\"\"\n", + " Converts a full_text dictionary into a markdown-formatted string.\n", + "\n", + " Expected keys:\n", + " - \"section_name\": list of section titles.\n", + " - \"paragraphs\": list of lists of paragraphs corresponding to each section.\n", + "\n", + " Each section becomes a markdown header (##) followed by its paragraphs.\n", + " \"\"\"\n", + " sections = full_text_dict.get(\"section_name\", [])\n", + " paragraphs = full_text_dict.get(\"paragraphs\", [])\n", + "\n", + " markdown_lines = []\n", + " for section, paragraph in zip(sections, paragraphs):\n", + " markdown_lines.append(f\"## {section}\")\n", + " markdown_lines.append(\"\") # Blank line\n", + " markdown_lines.append(\"\\n\".join(map(str, paragraph)))\n", + " markdown_lines.append(\"\") # End of section\n", + " markdown_lines.append(\"\") # Extra blank line for separation\n", + " return \"\\n\".join(markdown_lines)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def combine_responses(row):\n", + " \"\"\"\n", + " Combines 'extractive_spans', 'yes_no', and 'free_form_answer'\n", + " into one single string. Skips components that are missing.\n", + " \"\"\"\n", + " responses = []\n", + " if pd.notna(row.get(\"extractive_spans\")):\n", + " if isinstance(row[\"extractive_spans\"], list):\n", + " responses.append(\" \".join(map(str, row[\"extractive_spans\"])))\n", + " else:\n", + " responses.append(str(row[\"extractive_spans\"]))\n", + " if pd.notna(row.get(\"yes_no\")):\n", + " responses.append(str(row[\"yes_no\"]))\n", + " if pd.notna(row.get(\"free_form_answer\")):\n", + " responses.append(str(row[\"free_form_answer\"]))\n", + " return \"\\n\".join(responses) if responses else np.nan" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def preprocess_hf_dataset(hf_ds):\n", + " \"\"\"\n", + " Processes a HuggingFace dataset split into a cleaned Pandas DataFrame.\n", + "\n", + " Steps:\n", + " 1. For each sample, convert 'full_text' to a markdown string.\n", + " 2. For every QA pair in the sample, extract the question and first answer.\n", + " 3. Build lists for answers, questions, and full_text (duplicated per question).\n", + " 4. Create a DataFrame from the collected data.\n", + " 5. Clean columns by replacing empty lists/strings with NaN and joining lists.\n", + " 6. Combine the answer components into a single 'golden response'.\n", + "\n", + " The function uses nested tqdm progress bars for real-time feedback.\n", + "\n", + " Returns:\n", + " pd.DataFrame: The preprocessed DataFrame.\n", + " \"\"\"\n", + " answers_list = [] # Stores the first answer for each question\n", + " questions_list = [] # Stores each question text\n", + " full_text_list = [] # Stores the formatted full text per QA pair\n", + "\n", + " # Outer loop: iterate over samples with progress bar\n", + " for sample in tqdm(hf_ds, desc=\"Processing samples\", unit=\"sample\"):\n", + " # Convert full text once per sample\n", + " formatted_text = convert_full_text_to_markdown(sample[\"full_text\"])\n", + " # Create a list of QA pairs\n", + " qa_pairs = list(zip(sample[\"qas\"][\"question\"], sample[\"qas\"][\"answers\"]))\n", + "\n", + " # Inner loop: iterate over each QA pair with its own progress bar\n", + " for question, answer_set in tqdm(\n", + " qa_pairs, desc=\"Processing QAs\", total=len(qa_pairs), leave=False, unit=\"qa\"\n", + " ):\n", + " answers_list.append(answer_set[\"answer\"][0])\n", + " questions_list.append(question)\n", + " full_text_list.append(formatted_text)\n", + "\n", + " # Create DataFrame from the collected data\n", + " df = pd.DataFrame(answers_list)\n", + " df[\"question\"] = questions_list\n", + " df[\"full_text\"] = full_text_list\n", + "\n", + " # Data Cleaning: Replace empty lists/strings with NaN and join lists if needed\n", + " df[\"extractive_spans\"] = df[\"extractive_spans\"].apply(\n", + " lambda x: np.nan if isinstance(x, list) and len(x) == 0 else x\n", + " )\n", + " df[\"free_form_answer\"] = df[\"free_form_answer\"].apply(\n", + " lambda x: np.nan if isinstance(x, str) and x.strip() == \"\" else x\n", + " )\n", + " df[\"yes_no\"] = df[\"yes_no\"].apply(lambda x: np.nan if x is None else x)\n", + " df[\"extractive_spans\"] = df[\"extractive_spans\"].apply(\n", + " lambda x: \"\\n\".join(x) if isinstance(x, list) else x\n", + " )\n", + "\n", + " # Combine the answer components into a single 'golden response'\n", + " df[\"golden response\"] = df.apply(lambda row: combine_responses(row), axis=1)\n", + "\n", + " return df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "train_ds = dataset[\"train\"]\n", + "validation_ds = dataset[\"validation\"]\n", + "test_ds = dataset[\"test\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Processing samples: 100%|██████████| 888/888 [00:02<00:00, 420.05sample/s]\n", + "Processing samples: 100%|██████████| 281/281 [00:00<00:00, 405.59sample/s]\n", + "Processing samples: 100%|██████████| 416/416 [00:01<00:00, 288.50sample/s]\n" + ] + } + ], + "source": [ + "train_df = preprocess_hf_dataset(train_ds)\n", + "validation_df = preprocess_hf_dataset(validation_ds)\n", + "test_df = preprocess_hf_dataset(test_ds)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from llama_index.llms.google_genai import GoogleGenAI\n", + "from dotenv import load_dotenv\n", + "\n", + "load_dotenv()\n", + "\n", + "gemini_2 = GoogleGenAI(\n", + " model=\"gemini-2.0-flash\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'which multilingual approaches do they compare with?'" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "idx = 0\n", + "query = validation_df.iloc[idx][\"question\"]\n", + "context = validation_df.iloc[idx][\"full_text\"]\n", + "query" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'The paper compares its approach with multilingual NMT (MNMT) from BIBREF19. Another comparison is made against a pivoting method that uses MNMT (pivotingm), which uses MNMT to translate source to pivot and then to target.\\n'" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "context_str = context\n", + "query_str = query\n", + "\n", + "\n", + "qa_prompt = (\n", + " f\"Context information is below.\\n\"\n", + " \"---------------------\\n\"\n", + " \"{context_str}\\n\"\n", + " \"---------------------\\n\"\n", + " \"Given the context information and not prior knowledge, \"\n", + " \"answer the query.\\n\"\n", + " \"If you cannot answer the query, just say that it cannot be answered.\\n\"\n", + " \"Query: {query_str}\\n\"\n", + " \"Answer: \"\n", + ")\n", + "\n", + "formatted_prompt = qa_prompt.format(context_str=context_str, query_str=query_str)\n", + "response = gemini_2.complete(formatted_prompt)\n", + "response.text" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'BIBREF19\\nBIBREF20'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation_df.iloc[idx][\"golden response\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Benchmarking Gemini 2.0 Flash" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from async_executor import AsyncExecutor\n", + "\n", + "gemini_2 = GoogleGenAI(\n", + " model=\"gemini-2.0-flash\",\n", + ")\n", + "\n", + "\n", + "async def query_llm(query_str: str, context_str: str):\n", + " formatted_prompt = qa_prompt.format(context_str=context_str, query_str=query_str)\n", + " response = await gemini_2.acomplete(formatted_prompt)\n", + " return response\n", + "\n", + "\n", + "# Create an instance of the asynchronous executor\n", + "executor = AsyncExecutor(\n", + " desc=\"LLM Processing\",\n", + " show_progress=True,\n", + " raise_exceptions=False,\n", + " max_calls_per_minute=1250,\n", + ")\n", + "\n", + "df = validation_df" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "LLM Processing: 100%|██████████| 1005/1005 [00:52<00:00, 19.13it/s]\n" + ] + } + ], + "source": [ + "for idx in range(df.shape[0]):\n", + " query = df.iloc[idx][\"question\"]\n", + " context = df.iloc[idx][\"full_text\"]\n", + " executor.submit(query_llm, query, context)\n", + "\n", + "# Execute the jobs and get the results in order\n", + "validation_responses = executor.results()" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_inputresponsereference
0which multilingual approaches do they compare ...They compare their approaches with Multilingua...BIBREF19\\nBIBREF20
1what are the pivot-based baselines?The pivot-based method is used as a baseline. ...pivoting\\npivoting$_{\\rm m}$
2which datasets did they experiment with?They experimented with two public datasets: Eu...Europarl\\nMultiUN
3what language pairs are explored?The language pairs explored in this paper are:...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...
4what ner models were evaluated?Stanford NER, spaCy 2.0, and a recurrent model...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...
............
1000What approaches do they use towards text analy...Based on the provided text, the approaches use...Domain experts and fellow researchers can prov...
1001What dataset do they use for analysis?The context information mentions using data fr...
1002Do they demonstrate why interdisciplinary insi...Yes, the text explicitly states that interdisc...False
1003What background do they have?The authors are scholars from very different d...
1004What kind of issues (that are not on the foref...The article aims to shed light on thorny issue...identifying the questions we wish to explore\\n...
\n", + "

1005 rows × 3 columns

\n", + "
" + ], + "text/plain": [ + " user_input \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "... ... \n", + "1000 What approaches do they use towards text analy... \n", + "1001 What dataset do they use for analysis? \n", + "1002 Do they demonstrate why interdisciplinary insi... \n", + "1003 What background do they have? \n", + "1004 What kind of issues (that are not on the foref... \n", + "\n", + " response \\\n", + "0 They compare their approaches with Multilingua... \n", + "1 The pivot-based method is used as a baseline. ... \n", + "2 They experimented with two public datasets: Eu... \n", + "3 The language pairs explored in this paper are:... \n", + "4 Stanford NER, spaCy 2.0, and a recurrent model... \n", + "... ... \n", + "1000 Based on the provided text, the approaches use... \n", + "1001 The context information mentions using data fr... \n", + "1002 Yes, the text explicitly states that interdisc... \n", + "1003 The authors are scholars from very different d... \n", + "1004 The article aims to shed light on thorny issue... \n", + "\n", + " reference \n", + "0 BIBREF19\\nBIBREF20 \n", + "1 pivoting\\npivoting$_{\\rm m}$ \n", + "2 Europarl\\nMultiUN \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... \n", + "... ... \n", + "1000 Domain experts and fellow researchers can prov... \n", + "1001 \n", + "1002 False \n", + "1003 \n", + "1004 identifying the questions we wish to explore\\n... \n", + "\n", + "[1005 rows x 3 columns]" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ragas.dataset_schema import EvaluationDataset\n", + "\n", + "dataset_list = []\n", + "\n", + "for i in range(df.shape[0]):\n", + " sample = {\n", + " \"user_input\": (\n", + " \"\" if pd.isna(df.iloc[i].get(\"question\")) else df.iloc[i].get(\"question\")\n", + " ),\n", + " \"reference\": (\n", + " \"\"\n", + " if pd.isna(df.iloc[i].get(\"golden response\"))\n", + " else df.iloc[i].get(\"golden response\")\n", + " ),\n", + " \"response\": (\n", + " \"\"\n", + " if pd.isna(validation_responses[i].text)\n", + " else validation_responses[i].text\n", + " ),\n", + " }\n", + " dataset_list.append(sample)\n", + "\n", + "dataset = EvaluationDataset.from_list(dataset_list)\n", + "dataset.to_pandas()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ragas.metrics import (\n", + " AnswerAccuracy,\n", + " AnswerCorrectness,\n", + " FactualCorrectness,\n", + " AspectCritic,\n", + ")\n", + "import getpass\n", + "import os\n", + "\n", + "from ragas.llms import LangchainLLMWrapper\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "if \"OPENAI_API_KEY\" not in os.environ:\n", + " os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"Enter your OpenAI API key: \")\n", + "\n", + "evaluator_llm = LangchainLLMWrapper(ChatOpenAI(model=\"gpt-4o-mini\"))\n", + "\n", + "aspect_critic = AspectCritic(\n", + " name=\"unanswerable\",\n", + " definition=\"Return 1 if the query cannot be answered by the provided context, otherwise return 0.\",\n", + " llm=evaluator_llm,\n", + ")\n", + "\n", + "metrics = [\n", + " AnswerAccuracy(llm=evaluator_llm),\n", + " AnswerCorrectness(llm=evaluator_llm, weights=[1, 0]),\n", + " aspect_critic,\n", + " FactualCorrectness(llm=evaluator_llm),\n", + "]" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'They compare their approaches with Multilingual NMT (MNMT) from BIBREF19 and BIBREF22.\\n'" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "validation_responses[0].text" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Evaluating: 100%|██████████| 4020/4020 [25:38<00:00, 2.61it/s] \n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_inputresponsereferencenv_accuracyanswer_correctnessunanswerablefactual_correctness(mode=f1)
0which multilingual approaches do they compare ...They compare their approaches with Multilingua...BIBREF19\\nBIBREF200.250.500.67
1what are the pivot-based baselines?The pivot-based method is used as a baseline. ...pivoting\\npivoting$_{\\rm m}$0.500.800.00
2which datasets did they experiment with?They experimented with two public datasets: Eu...Europarl\\nMultiUN1.000.800.40
3what language pairs are explored?The language pairs explored in this paper are:...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...0.001.000.00
4what ner models were evaluated?Stanford NER, spaCy 2.0, and a recurrent model...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...0.500.800.00
........................
1000What approaches do they use towards text analy...Based on the provided text, the approaches use...Domain experts and fellow researchers can prov...0.000.000.00
1001What dataset do they use for analysis?The context information mentions using data fr...0.500.000.00
1002Do they demonstrate why interdisciplinary insi...Yes, the text explicitly states that interdisc...False0.000.000.00
1003What background do they have?The authors are scholars from very different d...0.500.010.00
1004What kind of issues (that are not on the foref...The article aims to shed light on thorny issue...identifying the questions we wish to explore\\n...0.250.000.00
\n", + "

1005 rows × 7 columns

\n", + "
" + ], + "text/plain": [ + " user_input \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "... ... \n", + "1000 What approaches do they use towards text analy... \n", + "1001 What dataset do they use for analysis? \n", + "1002 Do they demonstrate why interdisciplinary insi... \n", + "1003 What background do they have? \n", + "1004 What kind of issues (that are not on the foref... \n", + "\n", + " response \\\n", + "0 They compare their approaches with Multilingua... \n", + "1 The pivot-based method is used as a baseline. ... \n", + "2 They experimented with two public datasets: Eu... \n", + "3 The language pairs explored in this paper are:... \n", + "4 Stanford NER, spaCy 2.0, and a recurrent model... \n", + "... ... \n", + "1000 Based on the provided text, the approaches use... \n", + "1001 The context information mentions using data fr... \n", + "1002 Yes, the text explicitly states that interdisc... \n", + "1003 The authors are scholars from very different d... \n", + "1004 The article aims to shed light on thorny issue... \n", + "\n", + " reference nv_accuracy \\\n", + "0 BIBREF19\\nBIBREF20 0.25 \n", + "1 pivoting\\npivoting$_{\\rm m}$ 0.50 \n", + "2 Europarl\\nMultiUN 1.00 \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... 0.00 \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... 0.50 \n", + "... ... ... \n", + "1000 Domain experts and fellow researchers can prov... 0.00 \n", + "1001 0.50 \n", + "1002 False 0.00 \n", + "1003 0.50 \n", + "1004 identifying the questions we wish to explore\\n... 0.25 \n", + "\n", + " answer_correctness unanswerable factual_correctness(mode=f1) \n", + "0 0.5 0 0.67 \n", + "1 0.8 0 0.00 \n", + "2 0.8 0 0.40 \n", + "3 1.0 0 0.00 \n", + "4 0.8 0 0.00 \n", + "... ... ... ... \n", + "1000 0.0 0 0.00 \n", + "1001 0.0 0 0.00 \n", + "1002 0.0 0 0.00 \n", + "1003 0.0 1 0.00 \n", + "1004 0.0 0 0.00 \n", + "\n", + "[1005 rows x 7 columns]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ragas import evaluate\n", + "\n", + "gemini_2_score = evaluate(dataset=dataset, metrics=metrics)\n", + "gemini_2_score.to_pandas()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gemini_2_score" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluation results uploaded! View at https://app.ragas.io/dashboard/alignment/evaluation/908c34a5-3996-4703-8eae-a7daf210c6d7\n" + ] + }, + { + "data": { + "text/plain": [ + "'https://app.ragas.io/dashboard/alignment/evaluation/908c34a5-3996-4703-8eae-a7daf210c6d7'" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gemini_2_score.upload()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "preds = gemini_2_score[\"unanswerable\"]\n", + "actuals = validation_df[\"unanswerable\"].astype(int)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Accuracy: 0.844776119402985\n", + "Precision: 0.31736526946107785\n", + "Recall: 0.5578947368421052\n", + "F1 Score: 0.40458015267175573\n", + "\n", + "Classification Report:\n", + " precision recall f1-score support\n", + "\n", + " 0 0.95 0.87 0.91 910\n", + " 1 0.32 0.56 0.40 95\n", + "\n", + " accuracy 0.84 1005\n", + " macro avg 0.63 0.72 0.66 1005\n", + "weighted avg 0.89 0.84 0.86 1005\n", + "\n" + ] + } + ], + "source": [ + "from sklearn.metrics import (\n", + " classification_report,\n", + " accuracy_score,\n", + " precision_score,\n", + " recall_score,\n", + " f1_score,\n", + ")\n", + "\n", + "# Calculate and print basic metrics\n", + "print(\"Accuracy:\", accuracy_score(actuals, preds))\n", + "print(\"Precision:\", precision_score(actuals, preds))\n", + "print(\"Recall:\", recall_score(actuals, preds))\n", + "print(\"F1 Score:\", f1_score(actuals, preds))\n", + "\n", + "# Generate and print the classification report\n", + "print(\"\\nClassification Report:\")\n", + "print(classification_report(actuals, preds))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Benchmarking Gemini 1.5 Flash" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "gemini_1_5 = GoogleGenAI(\n", + " model=\"gemini-1.5-flash\",\n", + ")\n", + "\n", + "\n", + "async def query_llm(query_str: str, context_str: str):\n", + " formatted_prompt = qa_prompt.format(context_str=context_str, query_str=query_str)\n", + " response = await gemini_1_5.acomplete(formatted_prompt)\n", + " return response\n", + "\n", + "\n", + "# Create an instance of the asynchronous executor\n", + "executor = AsyncExecutor(\n", + " desc=\"Querying LLM\",\n", + " show_progress=True,\n", + " raise_exceptions=False,\n", + " max_calls_per_minute=1250,\n", + ")\n", + "\n", + "df = validation_df" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "for idx in range(df.shape[0]):\n", + " query = df.iloc[idx][\"question\"]\n", + " context = df.iloc[idx][\"full_text\"]\n", + " executor.submit(query_llm, query, context)\n", + "\n", + "# Execute the jobs and get the results in order\n", + "validation_responses = executor.results()" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_inputresponsereference
0which multilingual approaches do they compare ...The paper compares its approach with multiling...BIBREF19\\nBIBREF20
1what are the pivot-based baselines?The provided text mentions two types of pivot-...pivoting\\npivoting$_{\\rm m}$
2which datasets did they experiment with?The experiments were conducted on two public d...Europarl\\nMultiUN
3what language pairs are explored?The paper explores the following language pair...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...
4what ner models were evaluated?Stanford NER, spaCy 2.0, and a recurrent model...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...
............
1000What approaches do they use towards text analy...The authors utilize several approaches to text...Domain experts and fellow researchers can prov...
1001What dataset do they use for analysis?The primary dataset used for analysis in the p...
1002Do they demonstrate why interdisciplinary insi...Yes, the authors demonstrate the importance of...False
1003What background do they have?The authors have diverse disciplinary backgrou...
1004What kind of issues (that are not on the foref...The authors tackle thorny issues related to th...identifying the questions we wish to explore\\n...
\n", + "

1005 rows × 3 columns

\n", + "
" + ], + "text/plain": [ + " user_input \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "... ... \n", + "1000 What approaches do they use towards text analy... \n", + "1001 What dataset do they use for analysis? \n", + "1002 Do they demonstrate why interdisciplinary insi... \n", + "1003 What background do they have? \n", + "1004 What kind of issues (that are not on the foref... \n", + "\n", + " response \\\n", + "0 The paper compares its approach with multiling... \n", + "1 The provided text mentions two types of pivot-... \n", + "2 The experiments were conducted on two public d... \n", + "3 The paper explores the following language pair... \n", + "4 Stanford NER, spaCy 2.0, and a recurrent model... \n", + "... ... \n", + "1000 The authors utilize several approaches to text... \n", + "1001 The primary dataset used for analysis in the p... \n", + "1002 Yes, the authors demonstrate the importance of... \n", + "1003 The authors have diverse disciplinary backgrou... \n", + "1004 The authors tackle thorny issues related to th... \n", + "\n", + " reference \n", + "0 BIBREF19\\nBIBREF20 \n", + "1 pivoting\\npivoting$_{\\rm m}$ \n", + "2 Europarl\\nMultiUN \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... \n", + "... ... \n", + "1000 Domain experts and fellow researchers can prov... \n", + "1001 \n", + "1002 False \n", + "1003 \n", + "1004 identifying the questions we wish to explore\\n... \n", + "\n", + "[1005 rows x 3 columns]" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ragas.dataset_schema import EvaluationDataset\n", + "\n", + "dataset_list = []\n", + "\n", + "for i in range(df.shape[0]):\n", + " sample = {\n", + " \"user_input\": (\n", + " \"\" if pd.isna(df.iloc[i].get(\"question\")) else df.iloc[i].get(\"question\")\n", + " ),\n", + " \"reference\": (\n", + " \"\"\n", + " if pd.isna(df.iloc[i].get(\"golden response\"))\n", + " else df.iloc[i].get(\"golden response\")\n", + " ),\n", + " \"response\": (\n", + " \"\"\n", + " if pd.isna(validation_responses[i].text)\n", + " else validation_responses[i].text\n", + " ),\n", + " }\n", + " dataset_list.append(sample)\n", + "\n", + "dataset = EvaluationDataset.from_list(dataset_list)\n", + "dataset.to_pandas()" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Evaluating: 100%|██████████| 4020/4020 [27:40<00:00, 2.42it/s] \n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_inputresponsereferencenv_accuracyanswer_correctnessunanswerablefactual_correctness(mode=f1)
0which multilingual approaches do they compare ...The paper compares its approach with multiling...BIBREF19\\nBIBREF200.250.00000000.0
1what are the pivot-based baselines?The provided text mentions two types of pivot-...pivoting\\npivoting$_{\\rm m}$0.250.50000000.0
2which datasets did they experiment with?The experiments were conducted on two public d...Europarl\\nMultiUN1.001.00000000.0
3what language pairs are explored?The paper explores the following language pair...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...0.000.25000000.0
4what ner models were evaluated?Stanford NER, spaCy 2.0, and a recurrent model...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...0.500.57142900.0
........................
1000What approaches do they use towards text analy...The authors utilize several approaches to text...Domain experts and fellow researchers can prov...0.000.00000000.0
1001What dataset do they use for analysis?The primary dataset used for analysis in the p...1.000.00000000.0
1002Do they demonstrate why interdisciplinary insi...Yes, the authors demonstrate the importance of...False0.000.00000000.0
1003What background do they have?The authors have diverse disciplinary backgrou...0.750.00000000.0
1004What kind of issues (that are not on the foref...The authors tackle thorny issues related to th...identifying the questions we wish to explore\\n...0.000.00000000.0
\n", + "

1005 rows × 7 columns

\n", + "
" + ], + "text/plain": [ + " user_input \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "... ... \n", + "1000 What approaches do they use towards text analy... \n", + "1001 What dataset do they use for analysis? \n", + "1002 Do they demonstrate why interdisciplinary insi... \n", + "1003 What background do they have? \n", + "1004 What kind of issues (that are not on the foref... \n", + "\n", + " response \\\n", + "0 The paper compares its approach with multiling... \n", + "1 The provided text mentions two types of pivot-... \n", + "2 The experiments were conducted on two public d... \n", + "3 The paper explores the following language pair... \n", + "4 Stanford NER, spaCy 2.0, and a recurrent model... \n", + "... ... \n", + "1000 The authors utilize several approaches to text... \n", + "1001 The primary dataset used for analysis in the p... \n", + "1002 Yes, the authors demonstrate the importance of... \n", + "1003 The authors have diverse disciplinary backgrou... \n", + "1004 The authors tackle thorny issues related to th... \n", + "\n", + " reference nv_accuracy \\\n", + "0 BIBREF19\\nBIBREF20 0.25 \n", + "1 pivoting\\npivoting$_{\\rm m}$ 0.25 \n", + "2 Europarl\\nMultiUN 1.00 \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... 0.00 \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... 0.50 \n", + "... ... ... \n", + "1000 Domain experts and fellow researchers can prov... 0.00 \n", + "1001 1.00 \n", + "1002 False 0.00 \n", + "1003 0.75 \n", + "1004 identifying the questions we wish to explore\\n... 0.00 \n", + "\n", + " answer_correctness unanswerable factual_correctness(mode=f1) \n", + "0 0.000000 0 0.0 \n", + "1 0.500000 0 0.0 \n", + "2 1.000000 0 0.0 \n", + "3 0.250000 0 0.0 \n", + "4 0.571429 0 0.0 \n", + "... ... ... ... \n", + "1000 0.000000 0 0.0 \n", + "1001 0.000000 0 0.0 \n", + "1002 0.000000 0 0.0 \n", + "1003 0.000000 0 0.0 \n", + "1004 0.000000 0 0.0 \n", + "\n", + "[1005 rows x 7 columns]" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ragas import evaluate\n", + "\n", + "gemini_1_5_score = evaluate(dataset=dataset, metrics=metrics)\n", + "gemini_1_5_score.to_pandas()" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'nv_accuracy': 0.4724, 'answer_correctness': 0.3366, 'unanswerable': 0.1841, 'factual_correctness(mode=f1)': 0.2269}" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gemini_1_5_score" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "preds = gemini_1_5_score[\"unanswerable\"]\n", + "actuals = validation_df[\"unanswerable\"].astype(int)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Accuracy: 0.83681592039801\n", + "Precision: 0.31351351351351353\n", + "Recall: 0.6105263157894737\n", + "F1 Score: 0.4142857142857143\n", + "\n", + "Classification Report:\n", + " precision recall f1-score support\n", + "\n", + " 0 0.95 0.86 0.91 910\n", + " 1 0.31 0.61 0.41 95\n", + "\n", + " accuracy 0.84 1005\n", + " macro avg 0.63 0.74 0.66 1005\n", + "weighted avg 0.89 0.84 0.86 1005\n", + "\n" + ] + } + ], + "source": [ + "from sklearn.metrics import (\n", + " classification_report,\n", + " accuracy_score,\n", + " precision_score,\n", + " recall_score,\n", + " f1_score,\n", + ")\n", + "\n", + "# Calculate and print basic metrics\n", + "print(\"Accuracy:\", accuracy_score(actuals, preds))\n", + "print(\"Precision:\", precision_score(actuals, preds))\n", + "print(\"Recall:\", recall_score(actuals, preds))\n", + "print(\"F1 Score:\", f1_score(actuals, preds))\n", + "\n", + "# Generate and print the classification report\n", + "print(\"\\nClassification Report:\")\n", + "print(classification_report(actuals, preds))" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluation results uploaded! View at https://app.ragas.io/dashboard/alignment/evaluation/2a3849ff-b142-4440-9c13-42f5fda332c9\n" + ] + }, + { + "data": { + "text/plain": [ + "'https://app.ragas.io/dashboard/alignment/evaluation/2a3849ff-b142-4440-9c13-42f5fda332c9'" + ] + }, + "execution_count": 71, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gemini_1_5_score.upload()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Comparing the Results" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "fixci", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 030bce1f571681158f17866e0f4db11c05305333 Mon Sep 17 00:00:00 2001 From: sahusiddharth Date: Mon, 31 Mar 2025 16:17:54 +0530 Subject: [PATCH 2/2] tutorial_prep_and_content --- posts/benchmarking/_final_benchmarking.md | 1007 +++++++++++ posts/benchmarking/async_executor.py | 3 +- .../benchmarking/benchmarking_notebook.ipynb | 195 +- posts/benchmarking/final_benchmarking.ipynb | 1582 +++++++++++++++++ posts/benchmarking/qasper_data_collection.png | Bin 0 -> 98501 bytes 5 files changed, 2751 insertions(+), 36 deletions(-) create mode 100644 posts/benchmarking/_final_benchmarking.md create mode 100644 posts/benchmarking/final_benchmarking.ipynb create mode 100644 posts/benchmarking/qasper_data_collection.png diff --git a/posts/benchmarking/_final_benchmarking.md b/posts/benchmarking/_final_benchmarking.md new file mode 100644 index 0000000..1e6bd59 --- /dev/null +++ b/posts/benchmarking/_final_benchmarking.md @@ -0,0 +1,1007 @@ +# Benchmarking Gemini Models on using Ragas + +In this tutorial, we'll benchmark Gemini models on the AllenAI QASPER dataset using Ragas metrics for the Academic Question Answering task. + +### About the Dataset + +QASPER (Question Answering over Scientific Papers) is a dataset consisting of 5,049 questions based on 1,585 NLP research papers. Annotators created these questions from titles and abstracts, with answers extracted from the full paper texts. It is designed to challenge document-level reasoning and support research in academic question answering. + +Data Collection Process: +1. Paper Selection: NLP domain papers from arXiv (LaTeX format) were selected from the S2ORC corpus. +2. Question Writing: Annotators wrote realistic, information-seeking questions based only on paper titles and abstracts. +3. Answer Annotation: Different annotators reviewed the entire paper to identify answers, selecting minimal relevant evidence (texts, tables, figures). + +![Data collection Process of QASPER Dataset](qasper_data_collection.png) + + +Link to the [Dataset](https://huggingface.co/datasets/allenai/qasper) and further details about QASPER can be found [here](https://huggingface.co/datasets/allenai/qasper). + + +## Loading Dataset + +For demonstration purposes, we'll use a subset of 10 examples from the validation split: + + +```python +from datasets import load_dataset +import pandas as pd +import numpy as np +from tqdm.auto import tqdm + +dataset = load_dataset("allenai/qasper", split="validation[:10]") +dataset +``` +Output +``` +Dataset({ + features: ['id', 'title', 'abstract', 'full_text', 'qas', 'figures_and_tables'], + num_rows: 10 +}) +``` + + +## Processing Dataset + +Since our goal is to benchmark the model’s performance on academic question-answering tasks, we need the responses generated by LLMs based on the full text of each research paper. We extract the full text from the dataset’s "full_text" column and format it into markdown, clearly organizing sections and paragraphs for readability and context. + +To create question-answer pairs for evaluation, we use the dataset’s "qas" column, which provides questions and corresponding answers in three formats: extractive spans, yes/no responses, or free-form answers. We then combine these formats into a single "golden answer" column, which serves as the ground truth for assessing model performance. + + +```python +def convert_full_text_to_markdown(full_text_dict): + """ + Converts a full_text dictionary into a markdown-formatted string. + + Expected keys: + - "section_name": list of section titles. + - "paragraphs": list of lists of paragraphs corresponding to each section. + + Each section becomes a markdown header (##) followed by its paragraphs. + """ + sections = full_text_dict.get("section_name", []) + paragraphs = full_text_dict.get("paragraphs", []) + + markdown_lines = [] + for section, paragraph in zip(sections, paragraphs): + markdown_lines.append(f"## {section}") + markdown_lines.append("") # Blank line + markdown_lines.append("\n".join(map(str, paragraph))) + markdown_lines.append("") # End of section + markdown_lines.append("") # Extra blank line for separation + return "\n".join(markdown_lines) +``` + + +```python +def combine_responses(row): + """ + Combines 'extractive_spans', 'yes_no', and 'free_form_answer' + into one single string. Skips components that are missing. + """ + responses = [] + if pd.notna(row.get("extractive_spans")): + if isinstance(row["extractive_spans"], list): + responses.append(" ".join(map(str, row["extractive_spans"]))) + else: + responses.append(str(row["extractive_spans"])) + if pd.notna(row.get("yes_no")): + responses.append(str(row["yes_no"])) + if pd.notna(row.get("free_form_answer")): + responses.append(str(row["free_form_answer"])) + return "\n".join(responses) if responses else np.nan +``` + + +```python +def preprocess_hf_dataset(hf_ds): + """ + Processes a HuggingFace dataset split into a cleaned Pandas DataFrame. + + Steps: + 1. For each sample, convert 'full_text' to a markdown string. + 2. For every QA pair in the sample, extract the question and first answer. + 3. Build lists for answers, questions, and full_text (duplicated per question). + 4. Create a DataFrame from the collected data. + 5. Clean columns by replacing empty lists/strings with NaN and joining lists. + 6. Combine the answer components into a single 'golden response'. + + The function uses nested tqdm progress bars for real-time feedback. + + Returns: + pd.DataFrame: The preprocessed DataFrame. + """ + answers_list = [] # Stores the first answer for each question + questions_list = [] # Stores each question text + full_text_list = [] # Stores the formatted full text per QA pair + + # Outer loop: iterate over samples with progress bar + for sample in tqdm(hf_ds, desc="Processing samples", unit="sample"): + # Convert full text once per sample + formatted_text = convert_full_text_to_markdown(sample["full_text"]) + # Create a list of QA pairs + qa_pairs = list(zip(sample["qas"]["question"], sample["qas"]["answers"])) + + # Inner loop: iterate over each QA pair with its own progress bar + for question, answer_set in tqdm( + qa_pairs, desc="Processing QAs", total=len(qa_pairs), leave=False, unit="qa" + ): + answers_list.append(answer_set["answer"][0]) + questions_list.append(question) + full_text_list.append(formatted_text) + + # Create DataFrame from the collected data + df = pd.DataFrame(answers_list) + df["question"] = questions_list + df["full_text"] = full_text_list + + # Data Cleaning: Replace empty lists/strings with NaN and join lists if needed + df["extractive_spans"] = df["extractive_spans"].apply( + lambda x: np.nan if isinstance(x, list) and len(x) == 0 else x + ) + df["free_form_answer"] = df["free_form_answer"].apply( + lambda x: np.nan if isinstance(x, str) and x.strip() == "" else x + ) + df["yes_no"] = df["yes_no"].apply(lambda x: np.nan if x is None else x) + df["extractive_spans"] = df["extractive_spans"].apply( + lambda x: "\n".join(x) if isinstance(x, list) else x + ) + + # Combine the answer components into a single 'golden response' + df["golden response"] = df.apply(lambda row: combine_responses(row), axis=1) + + return df +``` + + +```python +processed_dataset = preprocess_hf_dataset(dataset) +processed_dataset.head() +``` +``` +Processing samples: 100%|██████████| 10/10 [00:00<00:00, 208.37sample/s] +``` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
unanswerableextractive_spansyes_nofree_form_answerevidencehighlighted_evidencequestionfull_textgolden response
0FalseBIBREF19\nBIBREF20NaNNaN[Table TABREF19 and TABREF26 report zero-shot ...[We compare our approaches with related approa...which multilingual approaches do they compare ...## Introduction\n\nAlthough Neural Machine Tra...BIBREF19\nBIBREF20
1Falsepivoting\npivoting$_{\rm m}$NaNNaN[Table TABREF19 and TABREF26 report zero-shot ...[We compare our approaches with related approa...what are the pivot-based baselines?## Introduction\n\nAlthough Neural Machine Tra...pivoting\npivoting$_{\rm m}$
2FalseEuroparl\nMultiUNNaNNaN[We evaluate our cross-lingual pre-training ba...[We evaluate our cross-lingual pre-training ba...which datasets did they experiment with?## Introduction\n\nAlthough Neural Machine Tra...Europarl\nMultiUN
3FalseNaNNaNDe-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...[For MultiUN corpus, we use four languages: En...[For MultiUN corpus, we use four languages: En...what language pairs are explored?## Introduction\n\nAlthough Neural Machine Tra...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...
4FalseStanford NER\nspaCy 2.0 \nrecurrent model with...NaNNaN[In this section we describe a number of exper...[In this section we describe a number of exper...what ner models were evaluated?## Introduction\n\nNamed entity recognition is...Stanford NER\nspaCy 2.0 \nrecurrent model with...
+
+ + + +## Generating Responses from Gemini Models + +To generate responses using the Gemini model, we’ll first need to instantiate the Google GenAI client. We will define a prompt template that will be used when generating responses. + + +```python +import os +from google import genai +from dotenv import load_dotenv + +load_dotenv() + +client = genai.Client(api_key=os.getenv("GOOGLE_API_KEY")) + +qa_prompt = ( + f"Context information is below.\n" + "---------------------\n" + "{context_str}\n" + "---------------------\n" + "Given the context information and not prior knowledge, " + "answer the query.\n" + "If you cannot find answer to the query, just say that it cannot be answered.\n" + "Query: {query_str}\n" + "Answer: " +) +``` + +### Gemini 2.0 Falsh + + +```python +from async_executor import AsyncExecutor + +async def query_gemini_2(query_str: str, context_str: str): + formatted_prompt = qa_prompt.format(context_str=context_str, query_str=query_str) + response = await client.aio.models.generate_content( + model="gemini-2.0-flash", contents=formatted_prompt + ) + return response.text + +# Create an instance of the asynchronous executor +executor = AsyncExecutor( + desc="LLM Processing", + show_progress=True, + raise_exceptions=False, +) + +for idx in range(processed_dataset.shape[0]): + query = processed_dataset.iloc[idx]["question"] + context = processed_dataset.iloc[idx]["full_text"] + executor.submit(query_gemini_2, query, context) + +processed_dataset["gemini_2_flash_responses"] = executor.results() +``` +``` +LLM Processing: 100%|██████████| 30/30 [00:04<00:00, 7.20it/s] +``` + +### Gemini 1.5 Falsh + + +```python +from async_executor import AsyncExecutor + +async def query_gemini_1_5(query_str: str, context_str: str): + formatted_prompt = qa_prompt.format(context_str=context_str, query_str=query_str) + response = await client.aio.models.generate_content( + model="gemini-1.5-flash", contents=formatted_prompt + ) + return response.text + +# Create a new instance of the asynchronous executor +executor = AsyncExecutor( + desc="LLM Processing", + show_progress=True, + raise_exceptions=False, +) + +for idx in range(processed_dataset.shape[0]): + query = processed_dataset.iloc[idx]["question"] + context = processed_dataset.iloc[idx]["full_text"] + executor.submit(query_gemini_1_5, query, context) + +processed_dataset["gemini_1_5_flash_responses"] = executor.results() +``` +``` +LLM Processing: 100%|██████████| 30/30 [00:05<00:00, 5.94it/s] +``` + + +```python +processed_dataset.head() +``` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
unanswerableextractive_spansyes_nofree_form_answerevidencehighlighted_evidencequestionfull_textgolden responsegemini_2_flash_responsesgemini_1_5_flash_responses
0FalseBIBREF19\nBIBREF20NaNNaN[Table TABREF19 and TABREF26 report zero-shot ...[We compare our approaches with related approa...which multilingual approaches do they compare ...## Introduction\n\nAlthough Neural Machine Tra...BIBREF19\nBIBREF20The text mentions comparison with Multilingual...The paper compares its approach with multiling...
1Falsepivoting\npivoting$_{\rm m}$NaNNaN[Table TABREF19 and TABREF26 report zero-shot ...[We compare our approaches with related approa...what are the pivot-based baselines?## Introduction\n\nAlthough Neural Machine Tra...pivoting\npivoting$_{\rm m}$The pivot-based baselines are pivoting and piv...The provided text mentions two types of pivot-...
2FalseEuroparl\nMultiUNNaNNaN[We evaluate our cross-lingual pre-training ba...[We evaluate our cross-lingual pre-training ba...which datasets did they experiment with?## Introduction\n\nAlthough Neural Machine Tra...Europarl\nMultiUNThey experimented with the Europarl and MultiU...The experiments used two public datasets: Euro...
3FalseNaNNaNDe-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...[For MultiUN corpus, we use four languages: En...[For MultiUN corpus, we use four languages: En...what language pairs are explored?## Introduction\n\nAlthough Neural Machine Tra...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...The language pairs explored in this paper are:...The paper explores the following language pair...
4FalseStanford NER\nspaCy 2.0 \nrecurrent model with...NaNNaN[In this section we describe a number of exper...[In this section we describe a number of exper...what ner models were evaluated?## Introduction\n\nNamed entity recognition is...Stanford NER\nspaCy 2.0 \nrecurrent model with...Based on the provided text, the following NER ...Stanford NER, spaCy 2.0, and a recurrent model...
+
+ + + +## Defining Metrics For Evaluation + +We are benchmarking a question-answering task and we want to ensure that each question is answered properly and accurately. To achieve this, we use the following metrics from Ragas you find the complete list of metrics [here]() + +- [Answer Accuracy](): Measures how closely a response matches the reference answer. +- [Answer Correctness](): Assesses the alignment between the generated answer and the reference answer. +- [Factual Correctness]():Checks if all statements in a response are supported by the reference answer. + +For each question, we know whether it can be answered from the provided context, and we want to verify if the model correctly identifies when it cannot. For this purpose, we define a custom binary metric using [AspectCritique](). + + +```python +from ragas.metrics import AnswerAccuracy, AnswerCorrectness, FactualCorrectness, AspectCritic +import getpass +import os + +from ragas.llms import LangchainLLMWrapper +from langchain_openai import ChatOpenAI + +if "OPENAI_API_KEY" not in os.environ: + os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter your OpenAI API key: ") + +evaluator_llm = LangchainLLMWrapper(ChatOpenAI(model="gpt-4o-mini")) + +aspect_critic = AspectCritic( + name="unanswerable", + definition="Return 1 if the query cannot be answered by the provided context, otherwise return 0.", + llm=evaluator_llm, +) + +metrics = [ + AnswerAccuracy(llm=evaluator_llm), + AnswerCorrectness(llm=evaluator_llm, weights=[1, 0]), + aspect_critic, + FactualCorrectness(llm=evaluator_llm), +] +``` + +## Benchmarking on Ragas Metrics + +We format the processed data into a Ragas-compatible EvaluationDataset, then apply the metrics to evaluate model performance, more information on it can be found [here](). We’ll construct the EvaluationDataset using the questions and the golden answer responses generated by the Gemini models from our processed dataset. + +### Gemini 2.0 Falsh + +We'll create EvaluationDataset for the Gemini 2.0 Flash. + + +```python +from ragas.dataset_schema import EvaluationDataset + +dataset_list = [] + +for i in range(processed_dataset.shape[0]): + sample = { + "user_input": ( + "" if pd.isna(processed_dataset.iloc[i].get("question")) else processed_dataset.iloc[i].get("question") + ), + "reference": ( + "" + if pd.isna(processed_dataset.iloc[i].get("golden response")) + else processed_dataset.iloc[i].get("golden response") + ), + "response": ( + "" + if pd.isna(processed_dataset["gemini_2_flash_responses"].iloc[i]) + else processed_dataset["gemini_2_flash_responses"].iloc[i] + ), + } + dataset_list.append(sample) + +gemini_2_dataset = EvaluationDataset.from_list(dataset_list) +gemini_2_dataset.to_pandas().head() +``` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
user_inputresponsereference
0which multilingual approaches do they compare ...The text mentions comparison with Multilingual...BIBREF19\nBIBREF20
1what are the pivot-based baselines?The pivot-based baselines are pivoting and piv...pivoting\npivoting$_{\rm m}$
2which datasets did they experiment with?They experimented with the Europarl and MultiU...Europarl\nMultiUN
3what language pairs are explored?The language pairs explored in this paper are:...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...
4what ner models were evaluated?Based on the provided text, the following NER ...Stanford NER\nspaCy 2.0 \nrecurrent model with...
+
+ + + +Now, let’s evaluate the responses of Gemini 2.0 Falsh. + + +```python +from ragas import evaluate + +gemini_2_flash_score = evaluate(dataset=gemini_2_dataset, metrics=metrics) +gemini_2_flash_score.to_pandas().head() +``` +``` +Evaluating: 100%|██████████| 120/120 [00:49<00:00, 2.44it/s] +``` +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
user_inputresponsereferencenv_accuracyanswer_correctnessunanswerablefactual_correctness(mode=f1)
0which multilingual approaches do they compare ...The text mentions comparison with Multilingual...BIBREF19\nBIBREF200.250.40000000.5
1what are the pivot-based baselines?The pivot-based baselines are pivoting and piv...pivoting\npivoting$_{\rm m}$0.250.00000000.0
2which datasets did they experiment with?They experimented with the Europarl and MultiU...Europarl\nMultiUN1.001.00000000.0
3what language pairs are explored?The language pairs explored in this paper are:...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...0.250.54545500.0
4what ner models were evaluated?Based on the provided text, the following NER ...Stanford NER\nspaCy 2.0 \nrecurrent model with...0.500.60000000.0
+
+ + + +A completely optional step, if you want to upload the evaluation results to your Ragas app, you can run the command below.You can learn more about Ragas app here. + + +```python +gemini_2_flash_score.upload() +``` + +### Gemini 1.5 Flash + +Next, we’ll follow similar steps for Gemini 1.5 Flash as well. + +We’ll generate the evaluation dataset for the Gemini 1.5 Flash responses and then perform the same evaluation on it's responses. + + +```python +from ragas.dataset_schema import EvaluationDataset + +dataset_list = [] + +for i in range(processed_dataset.shape[0]): + sample = { + "user_input": ( + "" if pd.isna(processed_dataset.iloc[i].get("question")) else processed_dataset.iloc[i].get("question") + ), + "reference": ( + "" + if pd.isna(processed_dataset.iloc[i].get("golden response")) + else processed_dataset.iloc[i].get("golden response") + ), + "response": ( + "" + if pd.isna(processed_dataset["gemini_1_5_flash_responses"].iloc[i]) + else processed_dataset["gemini_1_5_flash_responses"].iloc[i] + ), + } + dataset_list.append(sample) + +gemini_1_5_dataset = EvaluationDataset.from_list(dataset_list) +gemini_1_5_dataset.to_pandas().head() +``` + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
user_inputresponsereference
0which multilingual approaches do they compare ...The paper compares its approach with multiling...BIBREF19\nBIBREF20
1what are the pivot-based baselines?The provided text mentions two types of pivot-...pivoting\npivoting$_{\rm m}$
2which datasets did they experiment with?The experiments used two public datasets: Euro...Europarl\nMultiUN
3what language pairs are explored?The paper explores the following language pair...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...
4what ner models were evaluated?Stanford NER, spaCy 2.0, and a recurrent model...Stanford NER\nspaCy 2.0 \nrecurrent model with...
+
+ + + + +```python +from ragas import evaluate + +gemini_1_5_flash_score = evaluate(dataset=gemini_1_5_dataset, metrics=metrics) +gemini_1_5_flash_score.to_pandas().head() +``` +``` +Evaluating: 100%|██████████| 120/120 [01:02<00:00, 1.93it/s] +``` + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
user_inputresponsereferencenv_accuracyanswer_correctnessunanswerablefactual_correctness(mode=f1)
0which multilingual approaches do they compare ...The paper compares its approach with multiling...BIBREF19\nBIBREF200.250.40000000.00
1what are the pivot-based baselines?The provided text mentions two types of pivot-...pivoting\npivoting$_{\rm m}$0.250.18181800.18
2which datasets did they experiment with?The experiments used two public datasets: Euro...Europarl\nMultiUN1.000.80000000.00
3what language pairs are explored?The paper explores the following language pair...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...0.000.53333300.00
4what ner models were evaluated?Stanford NER, spaCy 2.0, and a recurrent model...Stanford NER\nspaCy 2.0 \nrecurrent model with...0.500.57142900.00
+
+ + + +## Comparing the Results + +Now that we have completed our evaluations, let’s compare how both models performed on acadmic question answering. + + +```python +def print__results(result): + result = result._repr_dict + print("Response Accuracy:", result.get("nv_accuracy")) + print("Answer Correctness:", result.get("answer_correctness")) + print("Factual Correctness:", result.get("factual_correctness(mode=f1)")) + +print__results(gemini_1_5_flash_score) +``` +Output +``` +Response Accuracy: 0.5416666666666666 +Answer Correctness: 0.47723550201811066 +Factual Correctness: 0.2533333333333333 +``` + + +```python +print__results(gemini_2_flash_score) +``` +Output +``` +Response Accuracy: 0.5666666666666667 +Answer Correctness: 0.48055486996663466 +Factual Correctness: 0.23633333333333334 +``` + +Gemini 2.0 Flash performs slightly better overall. + +Let’s see how well the models performed on classifying if a given question can be answered with the provided text. + +For this, we’ll use the result from the “unanswerable” metric and compare it with the original ground truth from the “unanswerable” column in our pre-processed dataset. + + +```python +from sklearn.metrics import classification_report, accuracy_score, precision_score, recall_score, f1_score + + +def print_metrics(actuals, preds, model_name="Model", zero_division_value=0): + """ + Prints common classification metrics for a given set of actual and predicted values. + + Parameters: + actuals (array-like): Ground truth labels. + preds (array-like): Predicted labels. + model_name (str): Name of the model for display purposes. + zero_division_value (int or str): Sets the value to return when there is a zero division. + Options: 0, 1, or "warn" (default is 0 here). + """ + print(f"Metrics for {model_name}:") + print("Accuracy:", accuracy_score(actuals, preds)) + print( + "Precision:", precision_score(actuals, preds, zero_division=zero_division_value) + ) + print("Recall:", recall_score(actuals, preds, zero_division=zero_division_value)) + print("F1 Score:", f1_score(actuals, preds, zero_division=zero_division_value)) + print("\nClassification Report:") + print(classification_report(actuals, preds, zero_division=zero_division_value)) + +gemini_1_5_flash_prediction = gemini_1_5_flash_score["unanswerable"] +gemini_2_flash_prediction = gemini_2_flash_score["unanswerable"] +groundtruth = processed_dataset["unanswerable"].astype(int) + +print_metrics(groundtruth, gemini_2_flash_prediction, model_name="Gemini 2 Flash") +``` + +Output +``` +Metrics for Gemini 2 Flash: +Accuracy: 0.9333333333333333 +Precision: 0.5 +Recall: 1.0 +F1 Score: 0.6666666666666666 + +Classification Report: + precision recall f1-score support + + 0 1.00 0.93 0.96 28 + 1 0.50 1.00 0.67 2 + + accuracy 0.93 30 + macro avg 0.75 0.96 0.81 30 +weighted avg 0.97 0.93 0.94 30 +``` + +```python +print_metrics(groundtruth, gemini_1_5_flash_prediction, model_name="Gemini 1.5 Flash") +``` +Output +``` +Metrics for Gemini 1.5 Flash: +Accuracy: 0.9 +Precision: 0.3333333333333333 +Recall: 0.5 +F1 Score: 0.4 + +Classification Report: + precision recall f1-score support + + 0 0.96 0.93 0.95 28 + 1 0.33 0.50 0.40 2 + + accuracy 0.90 30 + macro avg 0.65 0.71 0.67 30 +weighted avg 0.92 0.90 0.91 30 +``` + + +Gemini 2.0 Flash also outperforms Gemini 1.5 Flash in identifying unanswerable questions. + +## What's Next + +You can benchmark your model on any dataset using Ragas metrics as long as the dataset is formatted according to Ragas EvaluationDatase. Try benchmarking your models on a variety of established benchmarking datasets. +- [PubMedQA](https://huggingface.co/datasets/qiaojin/PubMedQA) +- [MultiHopRAG](https://huggingface.co/datasets/yixuantt/MultiHopRAG) +- [ms_marco](https://huggingface.co/datasets/microsoft/ms_marco) + +And many more. diff --git a/posts/benchmarking/async_executor.py b/posts/benchmarking/async_executor.py index 717e375..3f540fb 100644 --- a/posts/benchmarking/async_executor.py +++ b/posts/benchmarking/async_executor.py @@ -59,13 +59,12 @@ class AsyncExecutor: desc: str = "Evaluating" show_progress: bool = True raise_exceptions: bool = False - max_calls_per_minute: int = 5 + max_calls_per_minute: int = 1250 jobs: List[Tuple[Callable[..., Any], tuple, dict, int]] = field( default_factory=list, repr=False ) job_counter: int = 0 rate_limiter: RateLimiter = field(init=False) - print("hi") def __post_init__(self): self.rate_limiter = RateLimiter(self.max_calls_per_minute) diff --git a/posts/benchmarking/benchmarking_notebook.ipynb b/posts/benchmarking/benchmarking_notebook.ipynb index dc9042c..0329bbd 100644 --- a/posts/benchmarking/benchmarking_notebook.ipynb +++ b/posts/benchmarking/benchmarking_notebook.ipynb @@ -4,59 +4,96 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Dataset Link: https://huggingface.co/datasets/allenai/qasper\n" + "Dataset Link: https://huggingface.co/datasets/allenai/qasper\n", + "\n", + "While leaderboards and reports offer insights into overall model performance, they don't reveal how a model handles your specific needs. The Gen AI evaluation service helps you define your own evaluation criteria, ensuring a clear understanding of how well generative AI models and applications align with your unique use case." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "metadata": {}, "outputs": [ { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "/Users/nexus/miniconda3/envs/fixci/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n" + "Note: you may need to restart the kernel to use updated packages.\n" ] } ], "source": [ - "from datasets import load_dataset\n", - "import pandas as pd\n", - "import numpy as np\n", - "from tqdm.auto import tqdm\n", - "\n", - "dataset = load_dataset(\"allenai/qasper\")" + "%pip install google-genai -q" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "![](benchmarking.png)" + "# Benchmarking Gemini Models on using Ragas" ] }, { "cell_type": "markdown", "metadata": {}, - "source": [] + "source": [ + "In this tutorial, we will see how we can benchmark the Gemini models on the AllenAI's QASPER dataset using the RAGAS metrics on Question Answering Task. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Data collection Process of QASPER Dataset](qasper_data_collection.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## \n", + "\n", + "For the sake of demonstration, we will use only a subset of the whole dataset. You can perform benchmarking using the complete dataset." + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 25, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "text/plain": [ + "Dataset({\n", + " features: ['id', 'title', 'abstract', 'full_text', 'qas', 'figures_and_tables'],\n", + " num_rows: 100\n", + "})" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datasets import load_dataset\n", + "import pandas as pd\n", + "import numpy as np\n", + "from tqdm.auto import tqdm\n", + "\n", + "dataset = load_dataset(\"allenai/qasper\")" + ] }, { "cell_type": "markdown", "metadata": {}, - "source": [] + "source": [ + "![](benchmarking.png)" + ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -85,7 +122,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -109,7 +146,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -185,16 +222,16 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Processing samples: 100%|██████████| 888/888 [00:02<00:00, 420.05sample/s]\n", - "Processing samples: 100%|██████████| 281/281 [00:00<00:00, 405.59sample/s]\n", - "Processing samples: 100%|██████████| 416/416 [00:01<00:00, 288.50sample/s]\n" + "Processing samples: 100%|██████████| 888/888 [00:04<00:00, 211.20sample/s]\n", + "Processing samples: 100%|██████████| 281/281 [00:01<00:00, 199.77sample/s]\n", + "Processing samples: 100%|██████████| 416/416 [00:02<00:00, 198.84sample/s]\n" ] } ], @@ -222,7 +259,36 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AI learns patterns from data to make predictions or decisions.\n", + "\n" + ] + } + ], + "source": [ + "import os\n", + "from google import genai\n", + "from dotenv import load_dotenv\n", + "\n", + "load_dotenv()\n", + "\n", + "client = genai.Client(api_key=os.getenv(\"GOOGLE_API_KEY\"))\n", + "\n", + "response = client.models.generate_content(\n", + " model=\"gemini-2.0-flash\", contents=\"Explain how AI works in a few words\"\n", + ")\n", + "print(response.text)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -231,7 +297,7 @@ "'which multilingual approaches do they compare with?'" ] }, - "execution_count": 57, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -281,6 +347,49 @@ "response.text" ] }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "They compare their approaches with Multilingual NMT (MNMT) described in BIBREF19 and BIBREF22.\n", + "\n" + ] + } + ], + "source": [ + "from google import genai\n", + "\n", + "client = genai.Client()\n", + "\n", + "context_str = context\n", + "query_str = query\n", + "\n", + "qa_prompt = (\n", + " f\"Context information is below.\\n\"\n", + " \"---------------------\\n\"\n", + " \"{context_str}\\n\"\n", + " \"---------------------\\n\"\n", + " \"Given the context information and not prior knowledge, \"\n", + " \"answer the query.\\n\"\n", + " \"If you cannot answer the query, just say that it cannot be answered.\\n\"\n", + " \"Query: {query_str}\\n\"\n", + " \"Answer: \"\n", + ")\n", + "\n", + "formatted_prompt = qa_prompt.format(context_str=context_str, query_str=query_str)\n", + "\n", + "\n", + "response = await client.aio.models.generate_content(\n", + " model='gemini-2.0-flash', contents=formatted_prompt\n", + ")\n", + "print(response.text)" + ] + }, { "cell_type": "code", "execution_count": 11, @@ -301,13 +410,6 @@ "validation_df.iloc[idx][\"golden response\"]" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Benchmarking Gemini 2.0 Flash" - ] - }, { "cell_type": "code", "execution_count": null, @@ -320,7 +422,6 @@ " model=\"gemini-2.0-flash\",\n", ")\n", "\n", - "\n", "async def query_llm(query_str: str, context_str: str):\n", " formatted_prompt = qa_prompt.format(context_str=context_str, query_str=query_str)\n", " response = await gemini_2.acomplete(formatted_prompt)\n", @@ -831,6 +932,13 @@ "gemini_2_score" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A completely optional step, if you want to upload the evaluation results to your Ragas app, you can run the command below.You can learn more about Ragas app here." + ] + }, { "cell_type": "code", "execution_count": 28, @@ -1446,6 +1554,11 @@ "print(classification_report(actuals, preds))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [] + }, { "cell_type": "code", "execution_count": 71, @@ -1486,6 +1599,20 @@ "metadata": {}, "outputs": [], "source": [] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next Steps" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you follow the steps like above you can Benchmark any model using Ragas Metrics you will you need to figure out How to convert the benchmark dataset to ragas EvaluationDataset then select the metrics of you choice and using then use the evaluate function." + ] } ], "metadata": { diff --git a/posts/benchmarking/final_benchmarking.ipynb b/posts/benchmarking/final_benchmarking.ipynb new file mode 100644 index 0000000..014dd4c --- /dev/null +++ b/posts/benchmarking/final_benchmarking.ipynb @@ -0,0 +1,1582 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Benchmarking Gemini Models on using Ragas" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this tutorial, we'll benchmark Gemini models on the AllenAI QASPER dataset using Ragas metrics for the Academic Question Answering task." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### About the Dataset\n", + "\n", + "QASPER (Question Answering over Scientific Papers) is a dataset consisting of 5,049 questions based on 1,585 NLP research papers. Annotators created these questions from titles and abstracts, with answers extracted from the full paper texts. It is designed to challenge document-level reasoning and support research in academic question answering.\n", + "\n", + "Data Collection Process:\n", + "1. Paper Selection: NLP domain papers from arXiv (LaTeX format) were selected from the S2ORC corpus.\n", + "2. Question Writing: Annotators wrote realistic, information-seeking questions based only on paper titles and abstracts.\n", + "3. Answer Annotation: Different annotators reviewed the entire paper to identify answers, selecting minimal relevant evidence (texts, tables, figures).\n", + "\n", + "![Data collection Process of QASPER Dataset](qasper_data_collection.png)\n", + "\n", + "\n", + "Link to the [Dataset](https://huggingface.co/datasets/allenai/qasper) and further details about QASPER can be found [here](https://huggingface.co/datasets/allenai/qasper). \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Loading Dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For demonstration purposes, we'll use a subset of 10 examples from the validation split:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Dataset({\n", + " features: ['id', 'title', 'abstract', 'full_text', 'qas', 'figures_and_tables'],\n", + " num_rows: 10\n", + "})" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from datasets import load_dataset\n", + "import pandas as pd\n", + "import numpy as np\n", + "from tqdm.auto import tqdm\n", + "\n", + "dataset = load_dataset(\"allenai/qasper\", split=\"validation[:10]\")\n", + "dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Processing Dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since our goal is to benchmark the model’s performance on academic question-answering tasks, we need the responses generated by LLMs based on the full text of each research paper. We extract the full text from the dataset’s \"full_text\" column and format it into markdown, clearly organizing sections and paragraphs for readability and context.\n", + "\n", + "To create question-answer pairs for evaluation, we use the dataset’s \"qas\" column, which provides questions and corresponding answers in three formats: extractive spans, yes/no responses, or free-form answers. We then combine these formats into a single \"golden answer\" column, which serves as the ground truth for assessing model performance." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def convert_full_text_to_markdown(full_text_dict):\n", + " \"\"\"\n", + " Converts a full_text dictionary into a markdown-formatted string.\n", + "\n", + " Expected keys:\n", + " - \"section_name\": list of section titles.\n", + " - \"paragraphs\": list of lists of paragraphs corresponding to each section.\n", + "\n", + " Each section becomes a markdown header (##) followed by its paragraphs.\n", + " \"\"\"\n", + " sections = full_text_dict.get(\"section_name\", [])\n", + " paragraphs = full_text_dict.get(\"paragraphs\", [])\n", + "\n", + " markdown_lines = []\n", + " for section, paragraph in zip(sections, paragraphs):\n", + " markdown_lines.append(f\"## {section}\")\n", + " markdown_lines.append(\"\") # Blank line\n", + " markdown_lines.append(\"\\n\".join(map(str, paragraph)))\n", + " markdown_lines.append(\"\") # End of section\n", + " markdown_lines.append(\"\") # Extra blank line for separation\n", + " return \"\\n\".join(markdown_lines)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def combine_responses(row):\n", + " \"\"\"\n", + " Combines 'extractive_spans', 'yes_no', and 'free_form_answer'\n", + " into one single string. Skips components that are missing.\n", + " \"\"\"\n", + " responses = []\n", + " if pd.notna(row.get(\"extractive_spans\")):\n", + " if isinstance(row[\"extractive_spans\"], list):\n", + " responses.append(\" \".join(map(str, row[\"extractive_spans\"])))\n", + " else:\n", + " responses.append(str(row[\"extractive_spans\"]))\n", + " if pd.notna(row.get(\"yes_no\")):\n", + " responses.append(str(row[\"yes_no\"]))\n", + " if pd.notna(row.get(\"free_form_answer\")):\n", + " responses.append(str(row[\"free_form_answer\"]))\n", + " return \"\\n\".join(responses) if responses else np.nan" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def preprocess_hf_dataset(hf_ds):\n", + " \"\"\"\n", + " Processes a HuggingFace dataset split into a cleaned Pandas DataFrame.\n", + "\n", + " Steps:\n", + " 1. For each sample, convert 'full_text' to a markdown string.\n", + " 2. For every QA pair in the sample, extract the question and first answer.\n", + " 3. Build lists for answers, questions, and full_text (duplicated per question).\n", + " 4. Create a DataFrame from the collected data.\n", + " 5. Clean columns by replacing empty lists/strings with NaN and joining lists.\n", + " 6. Combine the answer components into a single 'golden response'.\n", + "\n", + " The function uses nested tqdm progress bars for real-time feedback.\n", + "\n", + " Returns:\n", + " pd.DataFrame: The preprocessed DataFrame.\n", + " \"\"\"\n", + " answers_list = [] # Stores the first answer for each question\n", + " questions_list = [] # Stores each question text\n", + " full_text_list = [] # Stores the formatted full text per QA pair\n", + "\n", + " # Outer loop: iterate over samples with progress bar\n", + " for sample in tqdm(hf_ds, desc=\"Processing samples\", unit=\"sample\"):\n", + " # Convert full text once per sample\n", + " formatted_text = convert_full_text_to_markdown(sample[\"full_text\"])\n", + " # Create a list of QA pairs\n", + " qa_pairs = list(zip(sample[\"qas\"][\"question\"], sample[\"qas\"][\"answers\"]))\n", + "\n", + " # Inner loop: iterate over each QA pair with its own progress bar\n", + " for question, answer_set in tqdm(\n", + " qa_pairs, desc=\"Processing QAs\", total=len(qa_pairs), leave=False, unit=\"qa\"\n", + " ):\n", + " answers_list.append(answer_set[\"answer\"][0])\n", + " questions_list.append(question)\n", + " full_text_list.append(formatted_text)\n", + "\n", + " # Create DataFrame from the collected data\n", + " df = pd.DataFrame(answers_list)\n", + " df[\"question\"] = questions_list\n", + " df[\"full_text\"] = full_text_list\n", + "\n", + " # Data Cleaning: Replace empty lists/strings with NaN and join lists if needed\n", + " df[\"extractive_spans\"] = df[\"extractive_spans\"].apply(\n", + " lambda x: np.nan if isinstance(x, list) and len(x) == 0 else x\n", + " )\n", + " df[\"free_form_answer\"] = df[\"free_form_answer\"].apply(\n", + " lambda x: np.nan if isinstance(x, str) and x.strip() == \"\" else x\n", + " )\n", + " df[\"yes_no\"] = df[\"yes_no\"].apply(lambda x: np.nan if x is None else x)\n", + " df[\"extractive_spans\"] = df[\"extractive_spans\"].apply(\n", + " lambda x: \"\\n\".join(x) if isinstance(x, list) else x\n", + " )\n", + "\n", + " # Combine the answer components into a single 'golden response'\n", + " df[\"golden response\"] = df.apply(lambda row: combine_responses(row), axis=1)\n", + "\n", + " return df" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Processing samples: 100%|██████████| 10/10 [00:00<00:00, 208.37sample/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
unanswerableextractive_spansyes_nofree_form_answerevidencehighlighted_evidencequestionfull_textgolden response
0FalseBIBREF19\\nBIBREF20NaNNaN[Table TABREF19 and TABREF26 report zero-shot ...[We compare our approaches with related approa...which multilingual approaches do they compare ...## Introduction\\n\\nAlthough Neural Machine Tra...BIBREF19\\nBIBREF20
1Falsepivoting\\npivoting$_{\\rm m}$NaNNaN[Table TABREF19 and TABREF26 report zero-shot ...[We compare our approaches with related approa...what are the pivot-based baselines?## Introduction\\n\\nAlthough Neural Machine Tra...pivoting\\npivoting$_{\\rm m}$
2FalseEuroparl\\nMultiUNNaNNaN[We evaluate our cross-lingual pre-training ba...[We evaluate our cross-lingual pre-training ba...which datasets did they experiment with?## Introduction\\n\\nAlthough Neural Machine Tra...Europarl\\nMultiUN
3FalseNaNNaNDe-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...[For MultiUN corpus, we use four languages: En...[For MultiUN corpus, we use four languages: En...what language pairs are explored?## Introduction\\n\\nAlthough Neural Machine Tra...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...
4FalseStanford NER\\nspaCy 2.0 \\nrecurrent model with...NaNNaN[In this section we describe a number of exper...[In this section we describe a number of exper...what ner models were evaluated?## Introduction\\n\\nNamed entity recognition is...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...
\n", + "
" + ], + "text/plain": [ + " unanswerable extractive_spans yes_no \\\n", + "0 False BIBREF19\\nBIBREF20 NaN \n", + "1 False pivoting\\npivoting$_{\\rm m}$ NaN \n", + "2 False Europarl\\nMultiUN NaN \n", + "3 False NaN NaN \n", + "4 False Stanford NER\\nspaCy 2.0 \\nrecurrent model with... NaN \n", + "\n", + " free_form_answer \\\n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... \n", + "4 NaN \n", + "\n", + " evidence \\\n", + "0 [Table TABREF19 and TABREF26 report zero-shot ... \n", + "1 [Table TABREF19 and TABREF26 report zero-shot ... \n", + "2 [We evaluate our cross-lingual pre-training ba... \n", + "3 [For MultiUN corpus, we use four languages: En... \n", + "4 [In this section we describe a number of exper... \n", + "\n", + " highlighted_evidence \\\n", + "0 [We compare our approaches with related approa... \n", + "1 [We compare our approaches with related approa... \n", + "2 [We evaluate our cross-lingual pre-training ba... \n", + "3 [For MultiUN corpus, we use four languages: En... \n", + "4 [In this section we describe a number of exper... \n", + "\n", + " question \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "\n", + " full_text \\\n", + "0 ## Introduction\\n\\nAlthough Neural Machine Tra... \n", + "1 ## Introduction\\n\\nAlthough Neural Machine Tra... \n", + "2 ## Introduction\\n\\nAlthough Neural Machine Tra... \n", + "3 ## Introduction\\n\\nAlthough Neural Machine Tra... \n", + "4 ## Introduction\\n\\nNamed entity recognition is... \n", + "\n", + " golden response \n", + "0 BIBREF19\\nBIBREF20 \n", + "1 pivoting\\npivoting$_{\\rm m}$ \n", + "2 Europarl\\nMultiUN \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... " + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "processed_dataset = preprocess_hf_dataset(dataset)\n", + "processed_dataset.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Generating Responses from Gemini Models" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To generate responses using the Gemini model, we’ll first need to instantiate the Google GenAI client. We will define a prompt template that will be used when generating responses." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "from google import genai\n", + "from dotenv import load_dotenv\n", + "\n", + "load_dotenv()\n", + "\n", + "client = genai.Client(api_key=os.getenv(\"GOOGLE_API_KEY\"))\n", + "\n", + "qa_prompt = (\n", + " f\"Context information is below.\\n\"\n", + " \"---------------------\\n\"\n", + " \"{context_str}\\n\"\n", + " \"---------------------\\n\"\n", + " \"Given the context information and not prior knowledge, \"\n", + " \"answer the query.\\n\"\n", + " \"If you cannot find answer to the query, just say that it cannot be answered.\\n\"\n", + " \"Query: {query_str}\\n\"\n", + " \"Answer: \"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Gemini 2.0 Falsh" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "LLM Processing: 100%|██████████| 30/30 [00:04<00:00, 7.20it/s]\n" + ] + } + ], + "source": [ + "from async_executor import AsyncExecutor\n", + "\n", + "async def query_gemini_2(query_str: str, context_str: str):\n", + " formatted_prompt = qa_prompt.format(context_str=context_str, query_str=query_str)\n", + " response = await client.aio.models.generate_content(\n", + " model=\"gemini-2.0-flash\", contents=formatted_prompt\n", + " )\n", + " return response.text\n", + "\n", + "# Create an instance of the asynchronous executor\n", + "executor = AsyncExecutor(\n", + " desc=\"LLM Processing\",\n", + " show_progress=True,\n", + " raise_exceptions=False,\n", + ")\n", + "\n", + "for idx in range(processed_dataset.shape[0]):\n", + " query = processed_dataset.iloc[idx][\"question\"]\n", + " context = processed_dataset.iloc[idx][\"full_text\"]\n", + " executor.submit(query_gemini_2, query, context)\n", + "\n", + "processed_dataset[\"gemini_2_flash_responses\"] = executor.results()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Gemini 1.5 Falsh" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "LLM Processing: 100%|██████████| 30/30 [00:05<00:00, 5.94it/s]\n" + ] + } + ], + "source": [ + "from async_executor import AsyncExecutor\n", + "\n", + "async def query_gemini_1_5(query_str: str, context_str: str):\n", + " formatted_prompt = qa_prompt.format(context_str=context_str, query_str=query_str)\n", + " response = await client.aio.models.generate_content(\n", + " model=\"gemini-1.5-flash\", contents=formatted_prompt\n", + " )\n", + " return response.text\n", + "\n", + "# Create a new instance of the asynchronous executor\n", + "executor = AsyncExecutor(\n", + " desc=\"LLM Processing\",\n", + " show_progress=True,\n", + " raise_exceptions=False,\n", + ")\n", + "\n", + "for idx in range(processed_dataset.shape[0]):\n", + " query = processed_dataset.iloc[idx][\"question\"]\n", + " context = processed_dataset.iloc[idx][\"full_text\"]\n", + " executor.submit(query_gemini_1_5, query, context)\n", + "\n", + "processed_dataset[\"gemini_1_5_flash_responses\"] = executor.results()" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
unanswerableextractive_spansyes_nofree_form_answerevidencehighlighted_evidencequestionfull_textgolden responsegemini_2_flash_responsesgemini_1_5_flash_responses
0FalseBIBREF19\\nBIBREF20NaNNaN[Table TABREF19 and TABREF26 report zero-shot ...[We compare our approaches with related approa...which multilingual approaches do they compare ...## Introduction\\n\\nAlthough Neural Machine Tra...BIBREF19\\nBIBREF20The text mentions comparison with Multilingual...The paper compares its approach with multiling...
1Falsepivoting\\npivoting$_{\\rm m}$NaNNaN[Table TABREF19 and TABREF26 report zero-shot ...[We compare our approaches with related approa...what are the pivot-based baselines?## Introduction\\n\\nAlthough Neural Machine Tra...pivoting\\npivoting$_{\\rm m}$The pivot-based baselines are pivoting and piv...The provided text mentions two types of pivot-...
2FalseEuroparl\\nMultiUNNaNNaN[We evaluate our cross-lingual pre-training ba...[We evaluate our cross-lingual pre-training ba...which datasets did they experiment with?## Introduction\\n\\nAlthough Neural Machine Tra...Europarl\\nMultiUNThey experimented with the Europarl and MultiU...The experiments used two public datasets: Euro...
3FalseNaNNaNDe-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...[For MultiUN corpus, we use four languages: En...[For MultiUN corpus, we use four languages: En...what language pairs are explored?## Introduction\\n\\nAlthough Neural Machine Tra...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...The language pairs explored in this paper are:...The paper explores the following language pair...
4FalseStanford NER\\nspaCy 2.0 \\nrecurrent model with...NaNNaN[In this section we describe a number of exper...[In this section we describe a number of exper...what ner models were evaluated?## Introduction\\n\\nNamed entity recognition is...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...Based on the provided text, the following NER ...Stanford NER, spaCy 2.0, and a recurrent model...
\n", + "
" + ], + "text/plain": [ + " unanswerable extractive_spans yes_no \\\n", + "0 False BIBREF19\\nBIBREF20 NaN \n", + "1 False pivoting\\npivoting$_{\\rm m}$ NaN \n", + "2 False Europarl\\nMultiUN NaN \n", + "3 False NaN NaN \n", + "4 False Stanford NER\\nspaCy 2.0 \\nrecurrent model with... NaN \n", + "\n", + " free_form_answer \\\n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... \n", + "4 NaN \n", + "\n", + " evidence \\\n", + "0 [Table TABREF19 and TABREF26 report zero-shot ... \n", + "1 [Table TABREF19 and TABREF26 report zero-shot ... \n", + "2 [We evaluate our cross-lingual pre-training ba... \n", + "3 [For MultiUN corpus, we use four languages: En... \n", + "4 [In this section we describe a number of exper... \n", + "\n", + " highlighted_evidence \\\n", + "0 [We compare our approaches with related approa... \n", + "1 [We compare our approaches with related approa... \n", + "2 [We evaluate our cross-lingual pre-training ba... \n", + "3 [For MultiUN corpus, we use four languages: En... \n", + "4 [In this section we describe a number of exper... \n", + "\n", + " question \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "\n", + " full_text \\\n", + "0 ## Introduction\\n\\nAlthough Neural Machine Tra... \n", + "1 ## Introduction\\n\\nAlthough Neural Machine Tra... \n", + "2 ## Introduction\\n\\nAlthough Neural Machine Tra... \n", + "3 ## Introduction\\n\\nAlthough Neural Machine Tra... \n", + "4 ## Introduction\\n\\nNamed entity recognition is... \n", + "\n", + " golden response \\\n", + "0 BIBREF19\\nBIBREF20 \n", + "1 pivoting\\npivoting$_{\\rm m}$ \n", + "2 Europarl\\nMultiUN \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... \n", + "\n", + " gemini_2_flash_responses \\\n", + "0 The text mentions comparison with Multilingual... \n", + "1 The pivot-based baselines are pivoting and piv... \n", + "2 They experimented with the Europarl and MultiU... \n", + "3 The language pairs explored in this paper are:... \n", + "4 Based on the provided text, the following NER ... \n", + "\n", + " gemini_1_5_flash_responses \n", + "0 The paper compares its approach with multiling... \n", + "1 The provided text mentions two types of pivot-... \n", + "2 The experiments used two public datasets: Euro... \n", + "3 The paper explores the following language pair... \n", + "4 Stanford NER, spaCy 2.0, and a recurrent model... " + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "processed_dataset.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining Metrics For Evaluation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We are benchmarking a question-answering task and we want to ensure that each question is answered properly and accurately. To achieve this, we use the following metrics from Ragas you find the complete list of metrics [here]()\n", + "\n", + "- [Answer Accuracy](): Measures how closely a response matches the reference answer.\n", + "- [Answer Correctness](): Assesses the alignment between the generated answer and the reference answer.\n", + "- [Factual Correctness]():Checks if all statements in a response are supported by the reference answer.\n", + "\n", + "For each question, we know whether it can be answered from the provided context, and we want to verify if the model correctly identifies when it cannot. For this purpose, we define a custom binary metric using [AspectCritique]()." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from ragas.metrics import AnswerAccuracy, AnswerCorrectness, FactualCorrectness, AspectCritic\n", + "import getpass\n", + "import os\n", + "\n", + "from ragas.llms import LangchainLLMWrapper\n", + "from langchain_openai import ChatOpenAI\n", + "\n", + "if \"OPENAI_API_KEY\" not in os.environ:\n", + " os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"Enter your OpenAI API key: \")\n", + "\n", + "evaluator_llm = LangchainLLMWrapper(ChatOpenAI(model=\"gpt-4o-mini\"))\n", + "\n", + "aspect_critic = AspectCritic(\n", + " name=\"unanswerable\",\n", + " definition=\"Return 1 if the query cannot be answered by the provided context, otherwise return 0.\",\n", + " llm=evaluator_llm,\n", + ")\n", + "\n", + "metrics = [\n", + " AnswerAccuracy(llm=evaluator_llm),\n", + " AnswerCorrectness(llm=evaluator_llm, weights=[1, 0]),\n", + " aspect_critic,\n", + " FactualCorrectness(llm=evaluator_llm),\n", + "]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Benchmarking on Ragas Metrics" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We format the processed data into a Ragas-compatible EvaluationDataset, then apply the metrics to evaluate model performance, more information on it can be found [here](). We’ll construct the EvaluationDataset using the questions and the golden answer responses generated by the Gemini models from our processed dataset." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Gemini 2.0 Falsh" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We'll create EvaluationDataset for the Gemini 2.0 Flash." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_inputresponsereference
0which multilingual approaches do they compare ...The text mentions comparison with Multilingual...BIBREF19\\nBIBREF20
1what are the pivot-based baselines?The pivot-based baselines are pivoting and piv...pivoting\\npivoting$_{\\rm m}$
2which datasets did they experiment with?They experimented with the Europarl and MultiU...Europarl\\nMultiUN
3what language pairs are explored?The language pairs explored in this paper are:...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...
4what ner models were evaluated?Based on the provided text, the following NER ...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...
\n", + "
" + ], + "text/plain": [ + " user_input \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "\n", + " response \\\n", + "0 The text mentions comparison with Multilingual... \n", + "1 The pivot-based baselines are pivoting and piv... \n", + "2 They experimented with the Europarl and MultiU... \n", + "3 The language pairs explored in this paper are:... \n", + "4 Based on the provided text, the following NER ... \n", + "\n", + " reference \n", + "0 BIBREF19\\nBIBREF20 \n", + "1 pivoting\\npivoting$_{\\rm m}$ \n", + "2 Europarl\\nMultiUN \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... " + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ragas.dataset_schema import EvaluationDataset\n", + "\n", + "dataset_list = []\n", + "\n", + "for i in range(processed_dataset.shape[0]):\n", + " sample = {\n", + " \"user_input\": (\n", + " \"\" if pd.isna(processed_dataset.iloc[i].get(\"question\")) else processed_dataset.iloc[i].get(\"question\")\n", + " ),\n", + " \"reference\": (\n", + " \"\"\n", + " if pd.isna(processed_dataset.iloc[i].get(\"golden response\"))\n", + " else processed_dataset.iloc[i].get(\"golden response\")\n", + " ),\n", + " \"response\": (\n", + " \"\"\n", + " if pd.isna(processed_dataset[\"gemini_2_flash_responses\"].iloc[i])\n", + " else processed_dataset[\"gemini_2_flash_responses\"].iloc[i]\n", + " ),\n", + " }\n", + " dataset_list.append(sample)\n", + "\n", + "gemini_2_dataset = EvaluationDataset.from_list(dataset_list)\n", + "gemini_2_dataset.to_pandas().head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now, let’s evaluate the responses of Gemini 2.0 Falsh." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Evaluating: 100%|██████████| 120/120 [00:49<00:00, 2.44it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_inputresponsereferencenv_accuracyanswer_correctnessunanswerablefactual_correctness(mode=f1)
0which multilingual approaches do they compare ...The text mentions comparison with Multilingual...BIBREF19\\nBIBREF200.250.40000000.5
1what are the pivot-based baselines?The pivot-based baselines are pivoting and piv...pivoting\\npivoting$_{\\rm m}$0.250.00000000.0
2which datasets did they experiment with?They experimented with the Europarl and MultiU...Europarl\\nMultiUN1.001.00000000.0
3what language pairs are explored?The language pairs explored in this paper are:...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...0.250.54545500.0
4what ner models were evaluated?Based on the provided text, the following NER ...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...0.500.60000000.0
\n", + "
" + ], + "text/plain": [ + " user_input \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "\n", + " response \\\n", + "0 The text mentions comparison with Multilingual... \n", + "1 The pivot-based baselines are pivoting and piv... \n", + "2 They experimented with the Europarl and MultiU... \n", + "3 The language pairs explored in this paper are:... \n", + "4 Based on the provided text, the following NER ... \n", + "\n", + " reference nv_accuracy \\\n", + "0 BIBREF19\\nBIBREF20 0.25 \n", + "1 pivoting\\npivoting$_{\\rm m}$ 0.25 \n", + "2 Europarl\\nMultiUN 1.00 \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... 0.25 \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... 0.50 \n", + "\n", + " answer_correctness unanswerable factual_correctness(mode=f1) \n", + "0 0.400000 0 0.5 \n", + "1 0.000000 0 0.0 \n", + "2 1.000000 0 0.0 \n", + "3 0.545455 0 0.0 \n", + "4 0.600000 0 0.0 " + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ragas import evaluate\n", + "\n", + "gemini_2_flash_score = evaluate(dataset=gemini_2_dataset, metrics=metrics)\n", + "gemini_2_flash_score.to_pandas().head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A completely optional step, if you want to upload the evaluation results to your Ragas app, you can run the command below.You can learn more about Ragas app here." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Evaluation results uploaded! View at https://app.ragas.io/dashboard/alignment/evaluation/0e83b23d-ceb6-49cf-b8b2-eec951b43417\n" + ] + }, + { + "data": { + "text/plain": [ + "'https://app.ragas.io/dashboard/alignment/evaluation/0e83b23d-ceb6-49cf-b8b2-eec951b43417'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gemini_2_flash_score.upload()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Gemini 1.5 Flash" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we’ll follow similar steps for Gemini 1.5 Flash as well.\n", + "\n", + "We’ll generate the evaluation dataset for the Gemini 1.5 Flash responses and then perform the same evaluation on it's responses." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_inputresponsereference
0which multilingual approaches do they compare ...The paper compares its approach with multiling...BIBREF19\\nBIBREF20
1what are the pivot-based baselines?The provided text mentions two types of pivot-...pivoting\\npivoting$_{\\rm m}$
2which datasets did they experiment with?The experiments used two public datasets: Euro...Europarl\\nMultiUN
3what language pairs are explored?The paper explores the following language pair...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...
4what ner models were evaluated?Stanford NER, spaCy 2.0, and a recurrent model...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...
\n", + "
" + ], + "text/plain": [ + " user_input \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "\n", + " response \\\n", + "0 The paper compares its approach with multiling... \n", + "1 The provided text mentions two types of pivot-... \n", + "2 The experiments used two public datasets: Euro... \n", + "3 The paper explores the following language pair... \n", + "4 Stanford NER, spaCy 2.0, and a recurrent model... \n", + "\n", + " reference \n", + "0 BIBREF19\\nBIBREF20 \n", + "1 pivoting\\npivoting$_{\\rm m}$ \n", + "2 Europarl\\nMultiUN \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... " + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ragas.dataset_schema import EvaluationDataset\n", + "\n", + "dataset_list = []\n", + "\n", + "for i in range(processed_dataset.shape[0]):\n", + " sample = {\n", + " \"user_input\": (\n", + " \"\" if pd.isna(processed_dataset.iloc[i].get(\"question\")) else processed_dataset.iloc[i].get(\"question\")\n", + " ),\n", + " \"reference\": (\n", + " \"\"\n", + " if pd.isna(processed_dataset.iloc[i].get(\"golden response\"))\n", + " else processed_dataset.iloc[i].get(\"golden response\")\n", + " ),\n", + " \"response\": (\n", + " \"\"\n", + " if pd.isna(processed_dataset[\"gemini_1_5_flash_responses\"].iloc[i])\n", + " else processed_dataset[\"gemini_1_5_flash_responses\"].iloc[i]\n", + " ),\n", + " }\n", + " dataset_list.append(sample)\n", + "\n", + "gemini_1_5_dataset = EvaluationDataset.from_list(dataset_list)\n", + "gemini_1_5_dataset.to_pandas().head()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Evaluating: 100%|██████████| 120/120 [01:02<00:00, 1.93it/s]\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
user_inputresponsereferencenv_accuracyanswer_correctnessunanswerablefactual_correctness(mode=f1)
0which multilingual approaches do they compare ...The paper compares its approach with multiling...BIBREF19\\nBIBREF200.250.40000000.00
1what are the pivot-based baselines?The provided text mentions two types of pivot-...pivoting\\npivoting$_{\\rm m}$0.250.18181800.18
2which datasets did they experiment with?The experiments used two public datasets: Euro...Europarl\\nMultiUN1.000.80000000.00
3what language pairs are explored?The paper explores the following language pair...De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E...0.000.53333300.00
4what ner models were evaluated?Stanford NER, spaCy 2.0, and a recurrent model...Stanford NER\\nspaCy 2.0 \\nrecurrent model with...0.500.57142900.00
\n", + "
" + ], + "text/plain": [ + " user_input \\\n", + "0 which multilingual approaches do they compare ... \n", + "1 what are the pivot-based baselines? \n", + "2 which datasets did they experiment with? \n", + "3 what language pairs are explored? \n", + "4 what ner models were evaluated? \n", + "\n", + " response \\\n", + "0 The paper compares its approach with multiling... \n", + "1 The provided text mentions two types of pivot-... \n", + "2 The experiments used two public datasets: Euro... \n", + "3 The paper explores the following language pair... \n", + "4 Stanford NER, spaCy 2.0, and a recurrent model... \n", + "\n", + " reference nv_accuracy \\\n", + "0 BIBREF19\\nBIBREF20 0.25 \n", + "1 pivoting\\npivoting$_{\\rm m}$ 0.25 \n", + "2 Europarl\\nMultiUN 1.00 \n", + "3 De-En, En-Fr, Fr-En, En-Es, Ro-En, En-De, Ar-E... 0.00 \n", + "4 Stanford NER\\nspaCy 2.0 \\nrecurrent model with... 0.50 \n", + "\n", + " answer_correctness unanswerable factual_correctness(mode=f1) \n", + "0 0.400000 0 0.00 \n", + "1 0.181818 0 0.18 \n", + "2 0.800000 0 0.00 \n", + "3 0.533333 0 0.00 \n", + "4 0.571429 0 0.00 " + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from ragas import evaluate\n", + "\n", + "gemini_1_5_flash_score = evaluate(dataset=gemini_1_5_dataset, metrics=metrics)\n", + "gemini_1_5_flash_score.to_pandas().head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Comparing the Results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now that we have completed our evaluations, let’s compare how both models performed on acadmic question answering." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "def print__results(result):\n", + " result = result._repr_dict\n", + " print(\"Response Accuracy:\", result.get(\"nv_accuracy\"))\n", + " print(\"Answer Correctness:\", result.get(\"answer_correctness\"))\n", + " print(\"Factual Correctness:\", result.get(\"factual_correctness(mode=f1)\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Response Accuracy: 0.5416666666666666\n", + "Answer Correctness: 0.47723550201811066\n", + "Factual Correctness: 0.2533333333333333\n" + ] + } + ], + "source": [ + "print__results(gemini_1_5_flash_score)" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Response Accuracy: 0.5666666666666667\n", + "Answer Correctness: 0.48055486996663466\n", + "Factual Correctness: 0.23633333333333334\n" + ] + } + ], + "source": [ + "print__results(gemini_2_flash_score)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gemini 2.0 Flash performs slightly better overall." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let’s now see how well the models performed on classifying if a given question can be answered with the provided text. \n", + "\n", + "For this, we’ll use the result from the “unanswerable” metric and compare it with the original ground truth from the “unanswerable” column in our pre-processed dataset." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from sklearn.metrics import classification_report, accuracy_score, precision_score, recall_score, f1_score\n", + "\n", + "\n", + "def print_metrics(actuals, preds, model_name=\"Model\", zero_division_value=0):\n", + " \"\"\"\n", + " Prints common classification metrics for a given set of actual and predicted values.\n", + "\n", + " Parameters:\n", + " actuals (array-like): Ground truth labels.\n", + " preds (array-like): Predicted labels.\n", + " model_name (str): Name of the model for display purposes.\n", + " zero_division_value (int or str): Sets the value to return when there is a zero division.\n", + " Options: 0, 1, or \"warn\" (default is 0 here).\n", + " \"\"\"\n", + " print(f\"Metrics for {model_name}:\")\n", + " print(\"Accuracy:\", accuracy_score(actuals, preds))\n", + " print(\n", + " \"Precision:\", precision_score(actuals, preds, zero_division=zero_division_value)\n", + " )\n", + " print(\"Recall:\", recall_score(actuals, preds, zero_division=zero_division_value))\n", + " print(\"F1 Score:\", f1_score(actuals, preds, zero_division=zero_division_value))\n", + " print(\"\\nClassification Report:\")\n", + " print(classification_report(actuals, preds, zero_division=zero_division_value))\n", + " \n", + "gemini_1_5_flash_prediction = gemini_1_5_flash_score[\"unanswerable\"]\n", + "gemini_2_flash_prediction = gemini_2_flash_score[\"unanswerable\"]\n", + "groundtruth = processed_dataset[\"unanswerable\"].astype(int)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Metrics for Gemini 2 Flash:\n", + "Accuracy: 0.9333333333333333\n", + "Precision: 0.5\n", + "Recall: 1.0\n", + "F1 Score: 0.6666666666666666\n", + "\n", + "Classification Report:\n", + " precision recall f1-score support\n", + "\n", + " 0 1.00 0.93 0.96 28\n", + " 1 0.50 1.00 0.67 2\n", + "\n", + " accuracy 0.93 30\n", + " macro avg 0.75 0.96 0.81 30\n", + "weighted avg 0.97 0.93 0.94 30\n", + "\n" + ] + } + ], + "source": [ + "print_metrics(groundtruth, gemini_2_flash_prediction, model_name=\"Gemini 2 Flash\")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Metrics for Gemini 1.5 Flash:\n", + "Accuracy: 0.9\n", + "Precision: 0.3333333333333333\n", + "Recall: 0.5\n", + "F1 Score: 0.4\n", + "\n", + "Classification Report:\n", + " precision recall f1-score support\n", + "\n", + " 0 0.96 0.93 0.95 28\n", + " 1 0.33 0.50 0.40 2\n", + "\n", + " accuracy 0.90 30\n", + " macro avg 0.65 0.71 0.67 30\n", + "weighted avg 0.92 0.90 0.91 30\n", + "\n" + ] + } + ], + "source": [ + "print_metrics(groundtruth, gemini_1_5_flash_prediction, model_name=\"Gemini 1.5 Flash\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Gemini 2.0 Flash also outperforms Gemini 1.5 Flash in identifying unanswerable questions." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## What's Next\n", + "\n", + "You can benchmark your model on any dataset using Ragas metrics as long as the dataset is formatted according to Ragas EvaluationDatase. You can try benchmarking your models on a variety of established benchmarking datasets.\n", + "- [PubMedQA](https://huggingface.co/datasets/qiaojin/PubMedQA)\n", + "- [MultiHopRAG](https://huggingface.co/datasets/yixuantt/MultiHopRAG)\n", + "- [ms_marco](https://huggingface.co/datasets/microsoft/ms_marco)\n", + "\n", + "And many more." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "fixci", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/posts/benchmarking/qasper_data_collection.png b/posts/benchmarking/qasper_data_collection.png new file mode 100644 index 0000000000000000000000000000000000000000..cfa8282aa189184d2866812489604fdb1c769968 GIT binary patch literal 98501 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 literal 0 HcmV?d00001