From 139d21e068585fc2eb4b79808c3477347eb3dafe Mon Sep 17 00:00:00 2001 From: Richard Liaw Date: Mon, 6 Jul 2020 15:35:10 -0700 Subject: [PATCH] [tune] Docs for tune-sklearn (#9129) Co-authored-by: krfricke --- ci/jenkins_tests/run_tune_tests.sh | 3 + doc/requirements-doc.txt | 1 + doc/source/conf.py | 10 +- doc/source/images/tune-sklearn.png | Bin 0 -> 82750 bytes doc/source/joblib.rst | 16 +- doc/source/tune/_tutorials/overview.rst | 11 +- doc/source/tune/_tutorials/tune-sklearn.py | 161 +++++++++++++++++++++ doc/source/tune/api_docs/execution.rst | 4 +- doc/source/tune/api_docs/overview.rst | 1 + doc/source/tune/api_docs/sklearn.rst | 14 ++ docker/tune_test/requirements.txt | 1 + python/ray/tune/examples/README.rst | 1 + python/ray/tune/sklearn.py | 14 ++ 13 files changed, 223 insertions(+), 14 deletions(-) create mode 100644 doc/source/images/tune-sklearn.png create mode 100644 doc/source/tune/_tutorials/tune-sklearn.py create mode 100644 doc/source/tune/api_docs/sklearn.rst create mode 100644 python/ray/tune/sklearn.py diff --git a/ci/jenkins_tests/run_tune_tests.sh b/ci/jenkins_tests/run_tune_tests.sh index 8783a10eb..3d2863c68 100755 --- a/ci/jenkins_tests/run_tune_tests.sh +++ b/ci/jenkins_tests/run_tune_tests.sh @@ -94,6 +94,9 @@ $SUPPRESS_OUTPUT docker run --rm --shm-size=${SHM_SIZE} --memory=${MEMORY_SIZE} python /ray/python/ray/tune/examples/hyperopt_example.py \ --smoke-test +$SUPPRESS_OUTPUT docker run --rm --shm-size=${SHM_SIZE} --memory=${MEMORY_SIZE} --memory-swap=-1 $DOCKER_SHA \ + python /ray/doc/source/tune/_tutorials/tune-sklearn.py + # if [[ ! -z "$SIGOPT_KEY" ]]; then # $SUPPRESS_OUTPUT docker run --rm --shm-size=${SHM_SIZE} --memory=${MEMORY_SIZE} --memory-swap=-1 -e SIGOPT_KEY $DOCKER_SHA \ # python /ray/python/ray/tune/examples/sigopt_example.py \ diff --git a/doc/requirements-doc.txt b/doc/requirements-doc.txt index 34f188af4..dcddb9606 100644 --- a/doc/requirements-doc.txt +++ b/doc/requirements-doc.txt @@ -24,3 +24,4 @@ sphinx_rtd_theme tabulate uvicorn werkzeug +git+https://github.com/ray-project/tune-sklearn.git#egg=tune-sklearn diff --git a/doc/source/conf.py b/doc/source/conf.py index 367b5be67..273515fd8 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -25,8 +25,8 @@ import mock MOCK_MODULES = [ "blist", "gym", "gym.spaces", "psutil", "ray._raylet", "ray.core.generated", "ray.core.generated.gcs_pb2", - "ray.core.generated.ray.protocol.Task", "scipy", "scipy.signal", - "scipy.stats", "setproctitle", "tensorflow_probability", "tensorflow", + "ray.core.generated.ray.protocol.Task", "scipy.signal", "scipy.stats", + "setproctitle", "tensorflow_probability", "tensorflow", "tensorflow.contrib", "tensorflow.contrib.all_reduce", "tree", "tensorflow.contrib.all_reduce.python", "tensorflow.contrib.layers", "tensorflow.contrib.rnn", "tensorflow.contrib.slim", "tensorflow.core", @@ -35,6 +35,9 @@ MOCK_MODULES = [ "torch.nn.parallel", "torch.utils.data", "torch.utils.data.distributed", "zoopt" ] +import scipy.stats +import scipy.linalg + for mod_name in MOCK_MODULES: sys.modules[mod_name] = mock.Mock() # ray.rllib.models.action_dist.py and @@ -80,7 +83,8 @@ versionwarning_messages = { versionwarning_body_selector = "div.document" sphinx_gallery_conf = { - "examples_dirs": ["../examples", "tune/_tutorials"], # path to example scripts + "examples_dirs": ["../examples", + "tune/_tutorials"], # path to example scripts # path where to save generated examples "gallery_dirs": ["auto_examples", "tune/tutorials"], "ignore_pattern": "../examples/doc_code/", diff --git a/doc/source/images/tune-sklearn.png b/doc/source/images/tune-sklearn.png new file mode 100644 index 0000000000000000000000000000000000000000..e427818b427efadb9e076850c47d2efe8e7ef1ac GIT binary patch literal 82750 zcmeFZhdzu=Nz2BerKk&QVe%)?3*E#1o9_#*?uPaPbL+KO=JqaEj-YFGjd2KvAct0K< zEQ#m@c&84QlZ}T*cHK@+PE$or4x#Dhe8h8kP=AEfHrq;dlsF3L6sY`}ab(@KLm+R%1~2 z&-4*G6CRQ-?4F}6};24Pi^+$Yrcwy=-a;MD}qo}X1H0ik6B#iVj*$n@+Dty{Jy(}WtwI}T3T%pfjhWJrZxd6WO}0+&|%+P3gP zx7(g`N4F07whQVpdxBDHq1z+Ry5y^Dst@fbyw!wnTQZ3}*e;gOuMRnv6CNLRx2gY=2f+IWW6PdA3I?w ztZ?hT7msu;o`&)%83tbNP__Q%Fq@w%ZOwL*??|s*9>1^I1XCRpdUrOOfYovI`shkx z?xhPW=nGZ^lrDYtCls^u{j*n+@xq%jD8f*dE~cTi25^$+2UD5)>mCF~^JB7`p{6V6 zCht)Yu8lkA_{DkjmEi2e<7;w#GH!jp7UwO50f+y@&v!*4hVA`+9 z)CqKr48y66cV)ig4&S{I!`*)#v_0^8Pw@VV?8%9*Rx0Itg@$=exAy2RE$M!KGC zpL-ZGjvswdXiEJ%SZ;tEL!Rv+t{v zeYOsjy);)$RgEiMfbq%>V~AxneJzGHJa_5IO$-Wf>(sqrhn z zN_$XP@N^|bnN=t(N}jQXUMEbvIk~x|*|Yg(^NVJ$NjXdYFnJ-iH?c6~a#raAl&-3= z-0(FC*;@5^)_S&u=;C&UpDu3nMyh=52C=W7o_@;zG$Z;i$L?AGa7MNS=Il?IJUI@z zm709o5}X0L1TR^xpi-2(o`25M*E*knslYp*-_ygx!Q-B1>zuO3%$)Ar;#ct(?&h-> zyP{`gr+RzsREMdC^@p{Fd56^`o-HF!=jMGO{iXKHb7N}5f8%v6+2};AHrlat=f>gahV@JZ9w(6XjurDj6zL~EI~3mvQ#)_SK^MKO=^OZ%5lo4DJ$PL+N6 zUHNT;Jw9=+uU0*#?5E^fFRDk*HT@J^Pf|W@<3 zsiEqf>R!pF!oJR(LZ^hguH1AQw(|Q%Ch9JD-bSR$p?*lEV)?nE+>t>sNlc$Xlaz|DKw#LGyq2v*Fh&s`j4xc^S* zj;o<>5!Y8@{U=p#JhQ8M{a$#cdS*O+aXt2Wc^6-5UA3-BwPV7P>WKab!&UCOa<7WT z`K7>$yupg4mOq6{H%1y=T|AUsx5u7$OccBbW9cq$FCSZtIy1`IZue5BxKGa}OL#>5 zoA0xAHroD{j|z+0OKxANJvY7LYf9W585PgXTveAS@)|rPnsUCaxTkn*>A+=X<<~^Z zhtx+hjJHIY9h=uE^OWwnZMk*3|5yz4&z@)(WQkC!j@%ph&>}akwd6YRbeV2h{9DD) zdJD%ltt_ntrO^#bpU0cKV}%0?FJ3=+{k`s8-N&}impu@GG5$q#QB!0SBf-`CMZw$G zs;-^B?-=~>oyq+Smo#zlw2Nu8Ov_Ab(fdr!HG$uUzQ1^GXe)1PWNVUi%TRl8xXgb- zWMXJyb3%&Jj8d2-N|B2B1#_a(*tpRO{H=sp_qhzging@SECn|C;>UgzOH{m1oaiE= zE(&|K3|$Or}M@6Ev#-~W8DL|l4C zjw+FTOv6o8o4tyCfi3USH?~F<;_C&nmJ%3{HPt+KrdvZOHBNM{$Ev<#uft!Mh>nwPL|A&|_Dvx`@xD9!I z*vV*0&U%HxOlY7H1@lB_y^+lci+>(@GmM5{!T8Qwl@EI73k40mqhjw7z zTk~^pk{`CWYo+Eqn}4 za4rXl9=e|Zcu8wH*@AMBiivDP*WGFWH>b<1=!`05_*XNlNuOB_GORDSi4j%0s z4cbdvUEh?_@;BakwI#Ax@x!1Zqr9<*(k|`GU>~c~ysd_#lZ?O&@@_=X#{=_6hp#p> zH{Z?6wT2`x6z}()ID)3oXtM8#hPOy+d5`7E2CBJN;+2?1Q`y z0t`aD+n)b@zQr%8?BnwH`S?VIQn(VMgc?I1qk(j}pY~S&)ZDw0l@hfe_3any$%{tj z-g6lzg1$<6`!)P79dG2@i2OZwk16QO;nrkgF8}KW`puVBq9t2n3wz%JoIA%-8b<#_ z?JsZqjoZ4hn;U=#$lv|7S3a%NpLcbCXMbO)A+1!J#G>rEkrCcrJvE*m7ha^=%AUYA z_ii21tT(AIDYtJJy3^`Lh~k}E$&n~=e*!BzFB0M#eO_chOG@xuW_o(?b?$JU8>dgv zo*MHZa{l|*+!zmOk3at6ZDXVJ&7i}!E`dQ4ybI4>zA=CKfav!LnMW^simA`cqDz08 z>njA!MXoASgZv`g+EC@r_3LUF$@0BNB4K7}}>xw)BjjTgosAt5mu3v*@pk0EumuwuLKhOuj7cU5Xfu11UxCs z|Mx0H;G@Ss{^xcuIwDQYk9ZNe|8q}G%_-6UwH*q10^DDyDU0)ek`72a_5YLhZ^r$9 zP-+&mXdlzb=j3680aT}}3%eVNGj6{lk5Sf~EH7(*aVy4Fxn8NiZnxwQ+gMe1*zKEJ zVO|>YRbd5xoJz$^*(P0_13MD7etItjj5KHuXjJ_O%nRiVd zvI~JSpwk35$e%O)!?5Tmq~wKYLz@@PdtLMO*6Ve<7k(_eTLxw7>3Z@!<2yCdYBXe! zHhpT6tCZH}pig+j@R4Z@4XdeEi z=JE0F`mCq@j}u{b4~43WVNtPMu6mmWrGb-0-w)~=cgMWf2b@hz*6}f>i~dB&k#FtP zO7|}_`Nf|}-F5Fg+iNlAAdPOVpBu(5v68fWbJ}dc-{{~*R7H3-}l?X3twg2de1@u zAszZXKo#yt;TQj)fT0SqIF8QFm@{TGg7xjS`melkhHdQJ*2;WzM7D(ye!IU*!tYKs z3nS|b@285T(QP1gFqm0Etf?tH5=qH_%qiqM!pRtm$@z+v-G?tjt2N&*Bq~dOH(C)p z*K+|ze#1AbX@*YBjCzorHNA~}zEea5v(Ey!QN?G|SmNV-P#Q4fa-O1_0o6`u`F`#q zr>QtY`Mj~YRwuAa0asDM;N5LNA#Z?nQMc~R1+{x|0^?+XWv{>)HiX5J~;VK&Cx z4D(A0u3OIYcGaD@H2L*T)h%Me%TlIuETs4$#^N3id_pv?zpbQJ@I5f5iA=i>fc&f> z>b^1h9Ub|xtTM%5U#hF9%=XJ4=m%wm+phbbFm}yFYxz^UBn;%gANE)!|8dooI`zZ! z(li-43uJ3&frS_g_S2m?-URU*wrZ<9+OU)Sz#xF*d8y3V;>^}ZsATwsbfH!9RCYF& zE!ysAPFdG3^$MjTzz$Eq4*iTcB^%FBS(6+A;9)9BBR-4SFUFNszvI(S}x+#Eh?bvu2ycn` zyreQ3<<+RdJ7sZEQmU#-j9gK%s&_A`c|jI~4+l55pd!Lz1a$pOe>Bi)J*59#cS^@T z+EGzUpfwKEJv4kAbe_?1%Yl_Oe?>UFSv;e+r#M{^<}$mrJ~hkrWXBkb6*M<*jCskFJ>8%o1% zOVw-#l$gtH?_BHwh~sw_Xh8w@*EDKjdS@j#tlWTSQ%;{I}qH7@@Z-10_;4y{veo0Tk_e zh~Wk&cgm%RZ>+-&(%+WF81dmkG%lgQ60}7o;H%_AI?uZ1GwIH_!C*v#W&oy_f?#;Z zP_aWBs&qeWHj7X?Na6im+I;+}9mY6Hz*__SCDp1|Dwk9`+6nsIzna!p=ix)v+;j(M z;U3jLTwG(Purp;xv+-Y|ITspIlOych;}MCBf|YazYKyGObJDpdiG3ft;6LR7MAozm zLYX3&HmlGhKAzP@F#IWm4X3`0{*xsy!pr?@kPHNQZ-{LpXyUGKD_T+#gyiwO{w#JL zh`bDPU!*tq3XYNY;n~p3rEyJJvuxWf3-)OIAf}KzJaCF0qb4owc#fj@$1;i+(%qi< z=Us(P-vg(Ak_oXoc7A7XAZX&pJH@FCw~LI+-EXZ) zy+!na;44WRaE~+ZaV8}$#zaq&G{cIi4EMAr!z&GKZo3BG0z|ggp(-kkdCgp2)lzTR zrk5vpJDvawgaIZBf8e2HIl@E>3bgII!Q|oMTvr5{yxfVfZ~?$~P1xtP6Py`tkK&@z zJ6>i!l(o44+;%pgTTX-Y+A-a&R79ow>U-;qi9O#K3z{ZdNuq4Q>Ty`fFq5|6rYQiR zy7Ak>2na^uKw^qXgqFvVaU=)DlS}DOW)3%8!yHWQn~B1R@!{b-<8%UMTz?LtfYorh zv64N3P)i>u+^u@LQ%7(i@D?dauTj)ra5IL+@#gJqA7X$cV5y7pTuF%PPQQDzk(t3y zi*t})u00m)@xv zVnP53ngC{xWo?8UDN2$$f?*>pXhL|>d?m6uc!@nC-Gvg_WD6^)y`}x;$CiBE#W?7i zWiJ7*3P1Ds9`mZ6sCkb!jQT1;MEX;o!5?aOF^HT@ls|%OyTC==*T@zX`%+yan-^jh z31C)k68n8jxCC)*<4lj!>_*V;gWCFawlJmpyPoPdAUhz3eSSq>Ft`Qdr1NbeSK5X$ zh$QIJZ)TtTI@XX%Ld%P(+3S66tTjKW!<6LgGL+*e}@JPIA;E!EpBRsk{ZKn zU@7&TmK)>uqIjU1w9=Zs5&eqrGNUtH|X8e`s|&W%Vs*hApq z^f~NWkKH5*55`6gG*X~GmpBqcsTt4B%WBER3@>dzwltX-nNU=~^r2tEM_ zzLUgec}%bnMX0ApM9|m^V3EuQ+1BHVAUNPiLwPOOOdrTl{9Xwl5#?Zk_LG1hmGWuH z@s%whd$2D@x3QIJ77up__lAp8M?S`b*^vNkE^s2)FAo7&V2qK1r{&Q{ z61L){IPDo8bZ`bpm@zD5-)QSY7~nKg5N;a3pS?Do5o*&^V@cMX1ih&bR=={GVg);4 zz%X+gwyLao^YNb|W0!H)u#+PZj1-g*%nbo|q%A#X1#Buev@hO~KMhi;v+6*x-{sR0 z9VvE?3P1vsae2l1)Je9lL)kic9>LJ}%$r8ND|NeSbWH~cA{faqTXF(dl+!dpaje%o z=o+}erKo^py&NaZ5$yWEdr$DI|lFtThW@c_9r_`|rtR;Q$ zjW;gp^Z{GUDK(GZspOFAu#m(>O6yX8P!i8$?rG1aC*5#5Y)?D3IQmoqTydJ!EW-3Xs!JdId5A&@x%1rq5DM;98Gx!BI zJ?$$~+NUQtdR&|9Up22HP6LH}M zHflrJPy6xP$WQJ-R{PT&oO$q=?l`je#YOQ%&0?;wSA5GUY{#UIVG0wpBZpyZFUVs% z2?Cql2YP8P2%2A(W9^F*Ttl_8h$~% zQDy=$5FvTUvAGw)EwxtpeT<98cTmkN?}}>2G3JO~B~kzyqaN=Or{B z*?BaGnhp0DWmuoBGtz1qz=VfM~2|6z)Rm265P;5ap$p@+a-` z;oc?r$@1dAJ3#FLbn@UEbXgQ6Z*sfpZwTsJ$OFL0r3Tr?Z~==8ZY@VH>Q7PSAJGHE zmznq$vfy%zXXriaqW95gI;je+)G z2PjTBl|t1E=W*35I-h8+F@qbF9jehu7YLNscuv_D@A({zP~$L`0!*Pe|c1^$BpL@grQJj@HG49_lIwn z&@MfL)S_J;m?yh2MtLED_!BiV_pVOtPi7p}C`&p$N$Y@6uB-r6=$F}_&pnVfy(vPC za0Ck#!9o%HGziBb>Jq3!tBYg)d~=F#&*kSyWeLM*jt8|1C>ogRau+ZAp`5`Nk*9%c z@k8Ab#qnhD2BK_3Z!XC_(bxxJ(fG6D9CX|pVA31~;=7`gD{&*H_cth!h>L){IgT$U zAe8}(;Q{W<+sVwwVkkA?^V6T0_q0iUPN+xEvy@VXjHK2wDS-gy4>--?@C+V~mHwpi zB7E_GzB$L(j!5{+MN2`qK*}~R8?f;(mQ#xs6isO&Iih4YC#mb@ctbwPCd(lkvu)@4Wfr2|C}9W zy6J|A0@`IW9eP!?1()9PeExYB5E~3as;i%b#BpA$NgoZ$0ru40Cahn-)Oa#7V(sLY zMy6%PZ`GmgsoJZ}(h!fpveZL7Wss6U-}p$vjv?C@V%gMU|f4K+YnBJ}sO ztE@GP*FR1x%3}1b<;)7%IsPjZfozi=wERShK<(wAB%t{+1stbazt|`oZ}K{p9_VLv zna>Td?PIO5OfS=F zsHpq86JLd0qGvgm1z;=-9L7y@H5ex~R0(V~3Gy%7vr;9sj~9lofJ75Ajk2eJ{{tcB zoMX>*yd|2+f#gDfW7E;*KtGEWRJnusV-VUkgSbkqkEzN)rS9*}di zl7gB75|lK~_OEGt!o$!H{1iW0xsSvyXj(C>y7c2Wij4@2DI%Z1N>bk0Pj{QhbC&@8 zM%4qr9lG1lL4#G4k3{gqMqZ$mtgFgRLgF!45ZZyi{gA7+dCMRa1nxtCUTJXHqVPAY z5}{C6;FGR%dn?j&X;5gXxs;pIf4b)DV^&V$c!QT z3Fpi#9veE?b)?#z54L>tZV-EU4^T+p6q}@nq6|-&AK<)Kkdj=jV#W3#P*w0O$A#>r z=^x6K(|7N(DT-j$B~X9}U2TcDk=90mQSGN372tv~FjjlUs3SSx!vXB6jTDNiNDexo zY;hG0^fKZ1{Z`c_LD5jLo1>x@pq60>04&8iH&z~NUuC&Si`9t9RQEh(9{VnM2UhCZ zFzYinqQmEYywcTR1yFYt)L2G_9zebVVzvbw?V~O+_BU^vR?_i-Z78n9dYQ0tw%{ft z@jizeARHHX0VFQ`nO+cE*|Bg%`wW)SFxJGkoaB-2&W1u}Lp{W*WYw8Nj?Zg;g}KUR z|6NV6;6WQIy{N3;xEpqvR}||G!6iWVBK?}ba+lV(uzYHa-24C^i$wFR9$=IKWk<;T z*qlAwuKY85-wbjbdv8`kuH0vrteymw{!iD`b$q2ErX=)d=bSEr? z;EqP{Al5Dh*~dN9mR3b0>TV@E{@n)qCr|(!3A1A*#)|qAFNNXWedCUGKK-@JTbHS@ z;3|qqA^$!$X_;8_a*w+Sn5GX@JU-EO&?d*dPY0u;)enKbPA;xi{!Hy9IN z(VYa?BMI2?N!UOh+k!$_!zXVwR@VDHeP9sekesHB2?FuZy0DV@Yuf&kjLj2#Dg%)q zjIy8tmt;@oc#qB8nz&$sL4|-ei7*G_JMnSt1lVgI)^ z1olQe0D;UeIr5uwgNkNCXM`12*Ob*jq&z(3MCJWM(C!L^t>-{wp5i+Ds2!(_amZ}D zhn4j{#my3_yz#UhSaky-XPr|`fBK3)Wghp_K3ib$CqQg|r;L~h)G;7)E8?IqYg8Vc z*|{4QB2kpYAdUs0=osWqe?`u{Cpy9sA+4C;3d9&Gr?ar3b(0(HxgMj+-@?XtA6DBp z-<7e68`=SWfv>bbedCg=-s$!kkI&%S1Q5nHu~I;RGB5#sY9hWMrD5~N>#Q{8_TfJt zVj)N$Hpb+yvKeDws$f_sI*R{xM45-n9co3iQ4oA%+BnT&(WqxbjWtF4B9(&fbiRnt)fxhH^ z8_hkd6M&LMC{~_rHNt+5C{xJ1y*&Cd>CZU^+&j_1sJh?h3ZxFaU!GJVuXo`BNgfEz zjh|=J;nEjU5*On1G}hBaf*eq9gv1I;_I*Xx5b!YzdxvzX}SA4 z+hLOU>hjE0pw^%ekn)cGfeUWCS`;WifIPJ^l7W2{tC|+7_)04A{TW_)VsEK)BQr87 z%OU_;)_uB+V|->X(&CHzWNJX-JZFiVv&T_IW0W{<$@P`4y434BIVj-`hBUKXo&wj1 zQlur~+b^wb^sIZCqoppqYf6bx7}OiW3(HgmS2s&1c}hjSndP6L9siBbGfNLDQ`L%Tw_Ew?C(`3owvyP9XlIU8n*Bgn=T4zb}-D zsZI=HS+yO6f1zT=swWMlOKpxu@%G%ydFPl1$^`g#K^Lgv>%G`h*w1T8hpZTHg+d*Gs+Xdj?~G8G=jHhy;zxxNDEF>o zVAeZn9_!^#&x}Ief-J8!Sqmq&$0#oaF;r_D<3rk z!jkoXtc`4xAUfy~tJ$rb%Ub{i^%rhGtFq=axY z4i3`^vmEc{Y|6+a8fNl#vwXq$Q=n|W*fa%~OgU3MUYU)4ISnJS0*c71CZ>S}o~BQ* z)yx~l>2K7tcnd$JohZi;GM(o(?C3f&RTf6Pe*&)&waKp`0j?W5k9NfNOC zbcNS~R+cW{g!Y^Z8RlyEq{I)t1+o|OoAPEk?O5UP72loIM`@9JKse~uGHWOU{uvON z39tU7l)&269Ep6u`RvUCHKQ-WHwA`qSmS{oqBDP^PAx4y;4uT|DwBl@xLiV-ek@ii zEK-5a#_3mO*<`!!R6o0Sl!5n=0;mZGP}8>I8;u9M1LaSxMl5%*IxR$D8?>)a%;zs0 zEPOobatB1J1Iqh!hZL^0T@-wR%>UJI%`8L(z3b~;T|V?2?GVKoM_D9tAW|*Is{dL- zM~FOy00e{;pU%4CJkWEb�_jz=0|aQ+vZ0mX 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

(uDuc`}3{h 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

q}KpQrvhK&Rrwb3NDJ>{kO4 zmIDy&4SNzDeZ(BNfx5MXOheM!5O<`Y{t1gX59_*WNL>hPm*?#64({iXFW>frp-qKp z*w0zsEx>&EpE;RG!U^P*T-a_0)L`5GV3vbWW{&}OG^ev5J5J}l5!B7YbU%t~C=tbY zK@DoIfhAw-VhF0#5|P74T4|i7)^8J3gNt3H(i^t&6uiknaH8@g`3OHsA6J2EW0ym%)-S3ff z_9cXv&@s$mj`Y6{R_X6DH~Gz9-E6eo+q6i1>}h_eRr;MLw%JL(-c3{f!jeHz-7`|f z_n9rT$==Gw3P&jWD4-5y_0y;fH2gkzVWRkn<^ZP;kEqW$%3Q#a<~ZA@mDA#Pp$-Y3Txc>NVdzPq4l*c`3R_XS5LmC`Sz z)Gv2F7HZ;x)mgWk2Hee}mkIfpI`Np52RitO6)5aC-rMg3v7^1B9bLC5&Bx62!=%%0 zcjy5Ip6HB@;x$(AX*cMTEl%9eXD;5RHcj8-&8v3*WR{2!4uqZEKaux;6}d6VXL5LB zop)yPW@J7ggN+-#5cqj7J+dv(%Z8E88Hk(hUDb7yZ6x1kpw$o3p~`AASwR5}DeBj( zFcKmcvo-G47CYku4Eu-VU>XMnB_+10sVOoFicM?15GIiNCmt(O<*{ap1Al%5ob4Z! zjm0I1=fykkh5oDJea#SbNuf*w;yxAr0a}m<{mr(RYJ-KN6)Xu16eS|!ujL_fO=Z#0?1w(ug zz09g(K^Kxe2KmF#&f{{Av!9;gO2$JeJ(vNDy1Gi4jy~)QI8yUe=OJ)k1KN|OG+Ih# zZ6oyDB6`b@2cxyuCnegY!_D4q^!>UZRG|nck-mjR0n1gA&P=tXvPNV{Lc*Cb9Ec(y zm3(9@{iIb3foWE!ZE(J;l2HxHr)<6=udpMKD|`Q~Ecn4o%@KM0_rOBLL!SpCmv6$7 zeJ03IgMy#bFKr!%3+``vDwbm>Ta_DLV*gQsrp6((!#H#!Tg1=({U9r+O&_; z)55}@m)<-+W^GLNdr)Z`hs(AZTNh6;dhb(M3K^XtQMeX;;Vf8bdo;weO3P2TsV70# zmDs)f^OMz8<-Js1wRDT-7iDvm!*<@FiM`Wq^fF;H@2fz$k$Ly*@WK3FVER?CvtTdwY@scF0?wZOcu5j- z+IGhdw^;E*Y*F=HPnMX-ugeti7I9-Zj9)*NTf zUuD=H3$8EtPzSs@*YBk?bMrnFd>CZEL**{P2!X;9>fmG#c|=rqNkx0_B3|Pi69J*z zZalVpsu8Se%g)>nukva0o;BstnCYFiOx?6_o9!og1Y_U*qLwcPe(xX6r1<-llew+@fYOShS^l+&z1yZ&X7Nb5CF zm%vl#>*MX+DIgoUvsxhRxt~E$|I_*>Rp3n!yD|Kyr(wblP%_X3{Na6AZ=e-(Kpy8+ zX4i@~?RRXx(C@A@#nROJSioY7E%m#P^qYp5lY#cRlCd~!mNDuIS%)0{ap734r0r8E z>kB^mfgcWCBF(3Wjwv=EyU*MTOIXo=`x06aPb7WXY)f#&HLh^2;3r__Ux_26ed&n9 zF5z{zWNDmTjA!GWQar&!Zdx)T@HRJts@9J^&HQFq_|PW=PU$BarG@p=Kw>xKzUGgY z+|NLvnhT05YX~2xiR=7+rxwOwepAX4SPbm1dO)+pQ6wSa1rd>zt-_zHI{vn}a;{sT zSECEiHp){=rZVsu)pFdoMkBYh3`T&VUg~sSZ~$?Puuz`b zkG~>SagtsAZKK7EDXh~MYqX_ZHUvGCJ3tA`T^*(%txUUc<-;iKsIKf~6xYbZg{)M~ zocl`hAuI^;C)D!|!U=L!U_Hqgh2JUwH7Mzr_5AVd9wP_%aCW|A(&EOs60oU2K-+;t z^Q-Y=z^Tgx=2FdRg0U~ai(0z5JXT28`8Fw|a=R{0$@}&>gL;jF2>M zKD(Roy`I;KYGp*!{-gzQJ>9|0Twh*B3JeS^P)uM5qdyY60R-<607JWcb{!b?s}<-A z*|s%xcjx_O<8j}*7Q}OO z#S6x|v$^@_h?_T+wSm~cw;dR7wkAi0(^ABR_GZ;Yf+uyF4-S>~gDL1rSnfz^HzTfk^ulTzhb)8n9iZPq)YG#Nm27z~7ia*+GPdaTq3GaCA!C-_V6H z+sixy?(3+>G7%rm`AxebnuZcJI)(qXS-w-w0e^nBsmYbTeU15lc>2n)Dxa@wMUVzT zy1Tm+knZkI>6SWxG=hM1cXxM5DhSfuNF&`K9q+*J|GZ!P;>CTRGiPSc-fOS5w(d0H z7&B36dqgg?qVc(Y8ulkm)7WCd(CqYJBS+C|@%Iy(2!$zQ`6eiz(84!E%}hOoqAepR zB1OHH$ow~C7V;-d#yjORX zNuS82ND0lf1XD-I<8IukJIQn7R8xzXLW-Il)&ptfSjK+xpk}G|0Arh78?pVrrK-hW z+B`0M+OtnEt)`=BRj59A(JjLU1Q>xhZ2@FmJ*sZse?8sCtio7J%RY=RVqNuX?Xggw zry#t?@@t0iI{q9_Q1Xe#ZGc8}DY5c4Zx?^Y&2LSlVO=Qd@8aSl*I6S`egHXD?EHXa zamV60`K-fq((pzrF5g&d8MAgUGr5=$?Y;0UxPo{F-J)*h#Wwa46yXLLI!hcwDr`wF4UNmd+D=~cNW&q>hna7LAY+oC#t(nv)1<%=8Q;F?<&Wj zD)lAEohs$<%FX83N?)rB#@|^orkYUao}DikL3k3>;T-cX1=p36e`$y-PQD$uh2|ON z3GtEV_k7HO9%;9Vg6&Gd&+Xb$LrYgzJKg4Rq_2wP`rzlp>UM z4QaMao}eNAcDXQ&k5heq7er-!nr5Zlhc|`s_D7BBz$hSlCne^&F@%b+(E;15_I9^B z&Eeid5RtNkzal87;o_h0zjP^T8xe^%pUDg`I<~r{g)W1`P=98o1`B0LA98FPu)BuQ z?|umLw8#7TXz$?w*>0b@IsXiefHGUKiLSo4bO$kw8~$ti&ofs(k0z$fz$Rh*u2V}n zD|;Jdu1ZvVh6XCM26$jm`B&SCuxY$uVBr) z^LCQZ++eFFrABpJN|5+90W!$>p)VadKCTVen9_wT_EX${Jn5DNgy?J&r=C6U8ORiA zHSmzi^4moiZ%j#`5}g}*xrl6V{ZiME1=^CMm5PRA$;J5>5s^gtvU&5k(XFTimE`_~ znhQ?X9gfdZ){r4nK9e6Q5mV79|R$gMwsarHZg3i6b5fGKpWT)HI4}T-Y zD>q4Xt29YdtKR-rzH5n&8<{pFHi!FP6gs8!3gD)@GuWP_0ATL-AJEs&DBKJHmqiNR zi{7ZixegznCldP5fp)paMsG9@r&Zk4+2@>QkkJ;P8%SU0O)C7)eKc4uOL%w^N0(P& zAD>PaUUL@Zll}=T0bLen@dxT=4{wTQ&wiR)iJ2FeO|Bes4i{+ia*rA*wuS#3yT0~8 z@~VHs1uBx>hb$i^jH`AWm697!U6VWVJh~&L%3sO!u@oqQT+Y8S$a+vlneUKe59fjV zFRJx@%w$_!b5aQHb(QEWp#tr>8L{Jhn$k&q3e^W+qCqh3FwPY6;3_ zrct07x&nd-TN3&}U??^-nL)c}b;aK2RX`)}jg?4!T>tl{klVUeWQagoJ?t}W2)nC} zv+~*Wka|-8e{(ca-j0rEU)XdhwtCy#Q}3m8vCe+^U$vpE?d5DEXCkwuiKY~Hq3ZL; z`vd&9s9o8J@Y239ORO}>4grOaeLq{U21o6-|I$K){#BV^S@kN}PU`REPOue72Tr`T zmI*xtp+!_A=vI9}gDvRYG;#&pI}Pem2n9#<@b}#!K6Tz(^l{tawq$nm6aux)uqo@o zpZ>68SwgRxzJFf@#&7P)Xj{#I#K}p6srD(Gshm=#Wb3@%)Mrw1ZClBUjAgx+_x*bu zAS?IwvJ2K6MwVRPE->|L%_?@=)>=K~GlDtLbFYQ-GZ$JvbxgJavxK+{*~cxs zp0akDd|Rd*tm*r?a8X8L+M(MK8oG>*)uy{KOEtv2uKt&QA8U0AU|(Tp^mKZ;mT`S+ zPPD^S$K6q(s@0Y-dK<*RAA0R!N695yf2-G}JB@19nVG~O&k5KX$@JfCYxBOJ zSC0=FHmI{e13Ht!B6;h$dti5Wb=Y!LmRxc}uuCEnj&FiywKNJ?&nhM#ot^6#%yz6` zc_&qW`LM50zOMLCx`t4e@?4k6LTU3%b`DocY=8W|8;El@xz^x5P#m<)qEextu&m{7 zs?C?@=<0PjS<#^qLc%?9m-$uZ`v@0it)ctYn({nVtWv{tiLwW?D(cbNMza;OaqPp} zcbVU4E=Onbk`Gn{A zH~P2lQKAy_2Y^~qe}KB_5a8+HDpUyw?AJN{Le>?6#h9|fU)Q7skf@!U9~R4#0V_S> z_`L~?saQCJ+PMZD-dBGU4P=eyvM;c3c_#ed2@G48ynY(LvY8p-|#7K9vkiiO5 zX=bd>aM>0~yU0L}Wp`hy_!C=y0;A3-0F(m0_Fkvzs`h z!~@Vcu7*BTapJYr(y_dW$;B`iV@Iusoay{+C5++eur*y}=kBxrYY$mg{!>(q+CHD_ z)AU6$Iq2`YJnsGztE{~bN1Ivr2{u*P&R@SZ<`l^Yuin-T6JsQUZj;hTGMxSYya2rK z9-(_GTf(I9DF6W>{Koc4i<)ZHbYa}qd;L%JQb)@q#C|uTcC489 zq&8<;Q|>-E#ohRF{nrr5;wUDaDxCR9f8w_~mjmrO+XXB%G{>p2yW^~)UVhAl8J*3M z%t%l(Z`+Ee?J1nr?^3s;;I zDbE)KC_RSI5bSQ^qGJ^Y@JvBGwFKUY*U@C%@Q7>sl9h;8Hv1;)RN{B;JAPL&?i!U= zp1;{QrJ#%a6An-%UUZ|*1=_S&v4B|R*U3$*2vijvHnFt#_?ti94V^CJb*1%bh=q7i zrnjt214lIgj@PU6Uk3)1Jr!TP{>CFqNkQ+v1p8EnuXrpYx;aI3k zAE2TiPQ79X5W`+FGn}`C`yrmov`E3>ww+MJ`tl)g`fs1_DFMubZSbO|00Ilq$2@wg z3GHXGDu>^&L9ltyZi+)Rg_z%UTG=mS`RTDk-uT3OFLZmax)?EbkEA@Yo%U1RQMEPG z$KB0RE>a5}TEqeuhKjzCMkGMKfe}Yx&;q?I?(M~vOAj>BnpvzwLN@2CJSt=$u#ixU z@{W#hsLr;oalZWEw24%rQ5gZ~W!-G?gjS!TL0Ud7BXW&Mnxnb76{0vG$=3JZWZGxJq0g>xar#m|%Fn+6_-YopNss9SSD2^Wa^>lxE^50brmzA%}%qittl8Al#inrQ% zM96PwB`+v^LgCWwnl$vJw?$c7(3^drPVqGZpZU$uiOKxunBH9P!@pog{P2sD!o?*{NIMu%N z*Z0;|TZ$g{=R6tZ_{RdTuA+^gv91??`oa`qP88c51e_FI(m z{6tYPv23t6lXzV#^r>d+m>@IYTQ|P7F?y|DtPF#ZPnEhcl!~3mv~!8)b9YruJH*b4 z4+t9<@m(mK$Ua|sKDB%y=W)p$voCFZ9wSR41T}S9Al8BR9i1R|W9Xey<(sOka2jos zGix}Du^&-`d7gB|)zsz^QE zkn=4dBOQLInmwD|9OLng=)LX;u zZd;0;oSq5;JK>6g4$o_J04(&BYMgSS06v!hm0U_5VkU6WelpNBgH)LY%`cFH3_p;0 ziat_*Wv{;HF1M1DN^?l_6$vU6>95Nl&;c66sW*X!wcvT09o)oC_gB4` zV~p}-bGdg+u~Lkp?}d`Jb@5LtTgvk1Q89WbP%2XOwv}vp+l|;xyWMsKxUc(NlGH1 z_=W$fRT@RE+`HWsfvnBW~bU4J9ECyY;&@&+FO#+L_YQp72mY%lH+c+CF=B` znmdYEFr#*zq-8z)Zf>(WG9CvDy*rJN7|$|A&*7xUI>EPvx!7F%6kxR?PmV;B$@r`xCU5snu!T zFiB8aSxg@vF2Vc$Xb*Zc6<I)3w8mRVCk{uuJK6BX2zHvRARlk&H_kXLoU0T`R2}FW#ak3xE0hn#P*bLcaHd#e* z7d_(peEMrcme*^(2KD*m+s|d2t%mA;TlLq-?D|KYEnQ?p_#D_p&@J>okk2O$loTIC zKWx2e)mp~dNU_t*?Iwbi$U4rl1<=GbTU?B@m9VYiO`+1p@I;~o-#?ZLftm*&nEA$z z4Lwv?uKv($xij3qnhXuGIhFu9gIbL~<>6vnic4CF{{v%AJ?+<$dNHg>SFo(b0ja8k z|M@2)f2>JWd~gw>;@mJf3?K7m8GTOWJ=#;fu_2$ek2xk^KNoi()kv7aOJ}rHosWLdnV^+2c2ql58Zp>`fMz=j5XV zFloWr#6)1z^da!fe7IVVY_cnM99mvp2IRb;^88M4<^2LJpB;J$zY_^Nn2&eU^avJ4 zn9nO?jDD3D$2=c5TvmMYNxx0>N2iOC+`V*RNFeK|ruTejVysYsSZ(Nu3Jv-_VZm?<4^%}~d0+cA zDs;bLF=}hRm-)V`>Sl0+l6!I>E@MtE#;ZJ3SE^CjV=n&%6qgrRsJxTtp2%65SnWBr>02q5A(k@Sz-!C%%zhKB zacXbSY1T%tvM(XI+L*%8478)dYi4Mym$U!1?LKT$itrckSn~M@L@aLEK589bNYd?A zw8f;nO20DWGj9t&1_|;TLtyxb*<*mm3dLf=Ez zo5{OFilwWe9VK)*nLX@!!=Z=XK6ckjxzSWFN-JI^@E$ZY(cvgSebh3JDR63@?2X8r z$5b+vyE*=R*F2Ead|lE#To*ZD-cwdVX*4Gmv8V5o77Wp=L|ildbkio}WR9j}7@I(LA3{3S1pBYiQC3Zrk$38TcZXU6$OAQU(=@q<(rjh}VZ) z_1bLuH1K&5edOr^(!YDxp#)e}g$S}QBo(8 zx>hSSR|7R!s-LxfQ9@i)@!FBGnfrDWogk2IFjQi+M2)^QO3A@hI-6WV3}nb^JVx@4 zj*e_Qz)BjQNxu~+ff2e}%lFm+u$)A8bIy?itSH~R4%c8fGVVj1oJ2G=ljvmR(s`yf z&g=7|e}I7}tX7jWYWwUp$}UAap6<6|jP-rGGICogD}Mo!Gn(=vi3&!EC6_I;SI5d@ zbhkYiq1kwcgDCUGOKIl8`@dMJdp?v^0c4FKCUW0EKX4Z>ABzb0YxDrK=AU8~{uf#k z&Bm4;j#<40sHUls_c=V$;^+WLy_=KqE}TzLL1k5Z&yo=!O7o@BBtlhlyz)aU-^13g zqU$dHv^L}0RBJILSwVPi;ldDA;}8T8A&Ot+yG7`D!2eXAd_0vtG;%EH{&yzWaL{qC z+C2XyuB2@zwVx(>u^Ba4{n3LMkV7kbt*gbeov8}B#9I5wB3tE3=?&U}}xn6n4h6r76 zB3LF^3XZ{j4>{3h&iuX@e^o50wKNP`{5#*Rik)aGIXtaF>Cp%%7=$bUgcewEI{xvb z*$}-2(k?m9aX{qp)oHZzdjD&8Yok}E)s0oV!R9&>Wsbn=$yNsN9v*8I_Fy;S{+);2 zzl@u9`PE1?HCzftt#~#vHXj}xMLtnoJw!EBbOO>X;U|oPv^zaHzI*Inu!k*@nZwmm zy>LSVk%A!N$G%JVD*2M&HYs2;u5|ku{L`GTm?5A+*keAAeH`+UcGa*UR)SHz(xcBt zNA0j{`54lylrxMqr`M?IKDeV#H(b|{bZhnQ9*Jcqk)5&v2wfn;=OHI=Md!U6V^rA= zUcmOLvHWz8xAF6M6W;ZA3pP$*jEL)OU4(XQU-VR?z8#WH8a75fR$`Qs=Y*8cgmrYz z!8W8gz*C*F^h%2!Vash=%?Nn_LgA+o?q@%s1p`dJg@1m5VbsW8N0M&IlTQHhNfDs) z5F*4Zc9{iILYtD0n9B(tK8Q+8tCcDOy8gwtHbA!~TiMcdnCcO({DF2VmeTvFb)Okt;3{6$;fwfi>Qhrm zi=iI|D(@D`D!`L<+m2H{=;$s>iBbR#>ox`^PB*(c{dGdW7^i6d;kqg8l8qR{RIW*ML^R`&cP}_IG3W$ja!rA zytl!W79-+uE-JU$_vYuFq~KnAf;3X=x3UMfWMgbXhMt*>zl407ugkaf)jNEaa&u+F z;g?!bBD;9nO&I-P*N`1gNEgb}E3QAd`**bZd^VAdmyNIgB=GqgJU)|g{WhH>Bs=bn zBUYHD9-Q7+|2|!KgmBSlp#=<|w|0j&(&4#KJA|dC?D>8-d8z`<@np#lSi_PxIWrC8 zY*kA7NR~0oKbQS6JXJN*(v>0eaZCOTH@(+&VjQGd+bT{J3LT8rN&E zU$*t$B|4k6{Jn1;T0oYv}3yq_%9iKdO=bl%`?xK2P!Ly8WrGEOnQE_tECcySCiI83?11(!R{drt|S_?skwO#XUV z?$y?Yh)kgrcScSckY7RXhG{D0VNzj5oAgmZiFtACwl~Y?kq}`(isPgULoHv;-Kw;Z zQEeFx?w#-*Qd%9Bqswb0G{KLKbPIIR`RB+FVdhSHaJULghvmkNXD(oiwe)M1Cs*te z%*cvG*BrX~PCQy6D6c+%PJT&q1zJ2d@FD{aFaedH?(|%Mw827}FW%Ir_trxKJ-@ z1e3?+`@rl5X_S236_YgNA>R{+{}Pr~`FP?3aU(QlQDNs{T!PGUo8R-r^~#__cx#O3kVW;l64gL!oauz8q>hla|$O27x&g4<_Jmp z!SIq+bjg^4WX$=`X4#;#K$7qspIAa76pOuwOwe13zN-SctS!on`!>)6 z)>@)7T6BYp4a!(1Eab@NJ7;kF{Fl%hW%FJStTgq?$i)mig2VT%7_EHem6zht>S|() z74)4SO-6)wHuU@SWQ%<^Y;r3x*1or-A`0t^uN7MWeuBmMzYibK5vH8pA=2g-GwrKj zQHq+Y%LM3a0L#O5RGoF-3**$zEcY)7{aIhlWrsh>Y|VAK-6Utte7ycpoWBqe+wxAC zGvfvI$gENm;3RU2tw@^J$-h<#m;r_5>TZf#ij$@$PSHm{h8p0$%3(XN`pzkVNiPJr z!|gP*=Oe0YO*3fKkbq%(l8H=&MN~_Gg!%>#&#Jft`nSjD&mRE~3~=7ezN}xF**x7J zL!vYE%m!lFBjXcpRQ%kg&jYb%$^F>o+D_#3ybw3UgMvux9bo-5lIPVg+~lYCzY$>M zcg%fjdC6R!%+yT~;+~x%3(`#gG@J8u<#)vq)31~?LJ>+P*0V-6N_b6pG)5v=Z4rJ> zal61u%6w$RmLtEx$+HZ|_lWiSAcpmDZ>lx1*N3MQ{-<~<~Z5&h6WWN{i83lwSe>4MohHdRG;PDNy05c1ruy~djiHV67(>s;(&l!Iksh;jD z#vX6D=(~SO_6Uz>BPSK-`Ub91Dfr3$OcVpv(A!KiMmZGiYnYrb-?^g42Yvoii>Pat zi|pibOgbDOmSyQS z!oF)_;w>T;gCIZ>7?vu4ti7`<{!@ifHK|c2h}+Hwv&~?zBH(Ct;Y}Pk${+suUJbO? zqpnzcfciGXV^=bqgZidueJlR=MS4m$ov1o6Y4ELs)=uaRP#7>9TWrVn?jX7AM_D|m zdiRP5CMb7uh8KBRO}WxZirETjcux(%R8)^8-*j(B!a`u(Ab+Z+-3?9=V=B@XQRm?5 zb76wjc|1emL z&+;lV;QQuk&4pmD2-S*YsPm<@2T0^nII^^@*m{Afh*E=%)}rip&uhn4LL~^hbm7+> z0$@>q8lq@iodH5-nR(&v;GTjotH}`e)P$7kA~gLor|*|gmQ1&eg{tVIQSYjT>sojF zj)P`zq7CKf<%o)=%W*}y6@eW;_t-O-SsyR~J4JRY(1yXyDL#3*t4x0T$TWXtkrRBA zMw25zs72Fk1vI2XB@Rh%3THr?VxH;qhRD(b?I>V6dXqEBPT&6~;Ix=uec{epuGp%0o6L4w?&;Sls1FFMmyY9`; z=h`_I0$R$3wo6g*2;hDW1737GB{%Nsc4C*700+8-b3^DRX+ND5g|Ng)mW3{8`=6Eyu*a3{m6KfRDyNiEO2 zJ*>*d{7VYK`ozJ(YRIx!i1`%M&Kkba6vQe|@pO+6#lz0B?N8N53rZC)k-g{BmOwnIR1G0L+of3*nn976Fnfo6H6?JnYCb5BX}`y#`coP$}vv`l78p zU}liLKUBHz84tMqEQAeZW53WOjmgB;qw7%ETVjg_y{6y_JJJ#I&7B44-OdU@&wvV;UF%Vgaw-Mc6k<`=lw+yLr*Z&3EwCoOGRRNqEm0beg zLldanPxdN1aztRQGE7<&tFM8ydA>sOCGuoGo4LH#-nMe$7&1N=O%=XzvAu)CPe8I3 zjL^pV3r?aOcS4E9yz4*OfgdwWC0JPym z)>M8TIpk*DXf1?f<^8f0)qzTv5_Fmh%k>x~Q#U)z5>=pNWK68ztAEm=(-MaM1gh?O zuh-vb>T9}ONw4zPoxc>Cp*(ea`v{6L{_eW&&}z%`H)lq2v?p%gQvXp#UJ);LP0>PHjHXA5X6YtlztB3hR`BNp$!5T+a4Wu9&9Pf*Q3} z0~t(W1*~ur-NyY3ZJ-T#vVWMY&|9#(ESl*76glkAh|`2^JLvL@hz*9XQB9xx5_@G< zjw(!}WdGK2Ik_sx!54AA1k-GaHs4Np8-0^o0V<@E+@jii4clDbtxCnSpfLY6X?(W_OrG@?{^U3iaO@NBx>Q<%tIKXM3Ya{W%ut;x|s^HDamcj<+&>Iu~^2 zW8iH|WuRAMF~w@AL`)Xii4LlP(|DxRBCGy_?8C=y~&UzRPY{^xXS zWT8u!dlO1o^F)`m;5i|2HIWNY@oc26e_|XfGU)VQ1=4t@69^H9FNM-STr9$8c*`ev zsS$P8(&Hy%oe8#ItwXC5eJ4JP`AB?QARp1~HIQRy_IKPUK5#XSj$N6H0JhL%dSMpe zR%P1t(v}m>GZMva0uNW@uaWU6Y7B=I{5fZifqzF59)1$9{*Z%0#)p>>!YLJCmGc$w zjn}#nZy)%i!@y7#;Ad#&J0AQrK?a2r(nMDQxySVx6mXVsXe*0)VvJ=``q>mOvgWIy zF(%47muF$}p<%9N0)sU}LO*{EsL{18k>G+4BCv_OU&lXc7q0at^`CAd)sHe_E^2mQ z=xJ#&kdHPSVTD=i;AHeM$mrDC#5YMyjHsSvqR!h=yJg1NX)>i*TJZ+a{7r6N!qbz6 zD_sA=Z>oBLVna_(tw5Y-b^bQ(gN;)jkZ!<$wr157LGh*vto&|+X0bBm)P79ygRY?= ziE_U3h>{(Xs0U=YeW4>L2o}gQv7L7(cNQrhD$oHHw-{R8vi@A$WOg30>2&siZ%4Y; z*z0NAie#BgtKujLWPW%JFQZRzg$bYxNz>Ri+_Qt4v!vSfz&6`~{d+~d@*{pGUl1*I zl>HZM?i)}D=n?7^>L;KM8fvcX+Z)Z5$3%1BQsRcsR~h9pQMEj=&MyCqJIgU*4WvvH z(u)pp)Z_8~`qO1S_O%H}TimV&n=D5d`)`b_3XLQ;={_~`7j6{GMq3Wk+CCp) zxovgcQ69|Y*axT?zA?yfN+fKQHWok&Gk*nxfB=b^*>*3mr`|!@?`|VjVXfKp${G~B z^4e`J#dt({$@tG=4ig-gcQi=;>gggT7=_;b_Cz+Xz%7&Twn%3bHdAA)U^?pibX&6* z4k>R>GH=>%Eg@fJfxU=-UBIisPE+%han*azw=PU z@`IrfFw<8A@Y+z{9nhqwG`lhFXvnLyug~c9_U7yNS}2jO7s2>$Kp}qz(NQJuJ`4Wdlyq zwa%_Hk4L2WUdnRSz(rxZQz%>vxC@K|7O&z2jC**sg$NMb&fBZo#B&xB8LmdRgzOlL zJ=Iw+8R0$EnGm%x8t+xCC~dvZb}6B-d|r!6&=w~^yaZQ37E{D&M%?>-6E0?a5tCd@ zb>2L_R_Z?Ra#2gsw!&rNu^Tk1mh{$+9u-L9-wzOLV?*VC?x&Ebgw5#-@DE0hRu{*n z%Ts{)S6tPeAUtLXa%jsUo-e8fb}pC7MWo8TrdU=VqH2~d?N9nDi;^o1I=IA&^}ZW@ zkf8CSQOtS)tVhc%ut$EqP)Ow(+OUwaNyEAn5fRBR-V#&tz-Ti^jbYT`QmHcz$K%}p z$QwBSigywTqPuoal2sz6R}}HaQzrHu9c&dWHk7c@gSG z1$y)DfRT_NQ*%;@iKtU=@|NYq-&pywR%%$9NagwjC^$)ldJ=otI^u8Zz&QM_fJ+jz|(a9wu`f?vor5o1_COo1OU?<0JM%e#4+gP zt_O50L+8!X(VaJe#DQ~F0f99la-AYlgL$w&S8aRseb1F~tYM2>kDk2BX6Wk! z@|mO_!Jn9!2{yFqvwzE5g7wY7GmT5FG1Ae{V#2f8`M55SGhfN)CbW7S0&bwhVan00 z00I=5kH86(NWsR%6&4E8ew9&*aZtiRz(;-|rgl{-KiI6jX%60vf+FjO!G zbb--{JS*HM9a3M*2vYMau2gU=CCCN!dLIszwdZYNnf3y=v2_KcOJ8YkxWbBv6$RA9txJ$mxoeue$7AZ4-!t3rRmc(|{+2v{FQI=Y+@K|1 z@WPpB+z`e61YxRU8PTVY$2(y#8o^1ltxOU09mE#}3H zjSwOh8SFd|1^`Ki4x(8x`#GSG&}i2c@$@rJ-kkoGp^!-!?9|#tL4}7GN@g>)|F^3K z!B!UZC|1sY>PN)UYskN}EfaPuk&}H;`Uy{0s3E`Ke$M8= zMr3G`fY5q0Gs>{gRre5H%BckPx5#FO0<8DEtVun$}O z0Xvh7W|IGLnTDcS2E{TB6)-m}g8^zQkE-vC<#W_Q{uk@dVj63n6u+lFJv9xwA{Vy{ zAKVZ%VO)+wNUR&_1!i%TNXo`kc|)4%RxETzya%}!zpcv1Qt+}uT)(8HM@?{ODB{zM z*xizyg9FB|T9bWjK)KUA8w1E9sujA|9hJc1C3Ly=^!_fB&jSmP>*KiASb@d((`X&J zF=veI9x_yL!S*>}scSi&tH1G{cZcaYIx?BM_>2zD>}xVbQgbV$igXF>P=iI@Z&BsB zdG#au$cPxxr1#iqJp$Ckct)XW2zg=xC*qA(FTeBd(;~+Xf3sRyptyUgzUDt+E%J6;m?hI5_Z4MZuV@i?+m9YIQMujL+97!He^2A))^0g z3@;K*X2g%+T@8FbeIw15bxFCJrYJjijjj(y2>c$CF(9jtV>?9drM2?uj+J;~)m5fo zUbB7f=KRG}LD)tVJmMqZ=kfAm^I@UU5(my$&I_8H37VRNL2{K1hWs%rT2=b+T^4}& zi#+taV*TM3cNDVmHRV@R&V84!?+XsgacE%JGw$tBO~xttq$W@VmAT&ZJ9DMo7uO&~ zbP7!2>zSIu1m@{(BYp`?>D2-@^X*=|+KqPM<|F5frE$Pb4+!I5fJq{W1@t9z%t)~K zy(pZ#@9l3eYXD=`C>Gig-2e@iM{DEthME9MLsdu?-^6;bVM17kVa;T!#Wr_KfT z;%N!f33nn>UeeEKpPMC=voAm|a-ojeiCSfzFh3Q;Q zS2|yVY+;zzQ2tyVeIlaMN#Qyx6$ja7@B3!jdSz=^#2hnU_llMJeD+r61VQAh zlrBY5ZZy+L&cNSEL_JgFYR|&D;EdoI!rX=K;bIKF!`+C0`_}ZGc*IpsH64aIip5W5 zZD>DfDzWM)=R%I`Nk1D}(|)D--ay(!A-*cx6Z@U$`j^iwb|1S}Ta>3X$*HyLzwC?x zR+x%yo$6u4-kU`xvWmcO+an7KllLp}yWY`VHLh*CH0DXp-B&)rnE|3JPr|;-FFYMV zPl6mfGQ4uHDI1oxy@@sx-ljwgLIJ__!@K*-O+op5`E;J-X`mijV#6Y~ z%SbZ-wh{Noj$cMV1M5-$9Cu0}7tbMrL`g|030R+rM-Y4iE|hAGV5a)(3F{Kz9?qg1 zQdXjfl$S zUDLb*02IC+(os_^wmB=UM^3+`VCRt-}m>y%Xl~=4o_qb3Aov;N2WIl?qo?L zOx@+gZ6~bB6bz?J??|&NNMIbC{~{g_i0p%q9`Zw@fY%C5--O;TKZr(h@;fG(xzcV_n^P?`|(52=1&tE z*i7AEo*5dJ*<6q(##iyQNB!dAtFDG{!wzFFx#%?#mY^;xl^hb(KT4 zMbF~{+XHgD&ow zj64EfQT~q(_5rk37Znyaf@8Ipzp$%i>AioyX>>Kfs7_RQ zPjK+}FP(?U{GelI*=X7wA?TMxGg$jC69rpG5IFS)6ALJ2I- z<3SEEfW3wr_@tZ{A&4~<=Z8aiH>Hy66Q~I`rXdvDoBs;QK2m?$Z}Z1gY_fS?E-Q5- zqEaU>o}%50xAquNhm;E;DVDYbrY)@s&mmCjX|rQdtt0c0DbrKgv_O5Wp$~)gWdtd& zARW{qovDa_CvIL1Eva$F5kX|I4(waJ&dcZbftv393qOiungyj4y9|^UX^p)A@b-HG z!v%v(i;EE>|8%FQpgx&$JS(O`-r_7ay+AN$liwBnHRPYOu;tdxT_ybAM>B*9BqecF ziTNXP@=<4#$On7POkVwKn4FX43CrDWj;vm z_GA+9co_UHn%P)?^Y?SH2_8$}?;DHQT5=hr6VV@0ZLmZM7D@I80>};l78+gf0oBL0 zr0_x#!_MrvC9^H`Lk9ybbD=$tNM@mKGt_GW%^OlE$K%HK^mk7MY%ZGQS_%QJl{d1+ z@|rk%`oS~UW+4p<-juaz^Xi3{%qQX7ceqSEHr3Dap*ku>G8sD01(M9^f%#Vb4gU$a z2lKokcg>go|B-L(r85v{cf$f?=5@tclMkL{*W2RYkE;D{14{w7cH0=9TAI!yBv5|M8mw z>>5~}OVi(nALmYPQR_c)wmHU{O=o68B#!1 z;3ANUm4B36+q9&poqHnFCizx8iF4__ZLxjG6H+O1CQ(8xIG;tjzUt84mU>9-#W9xk zX*qkMd@Vs)YHgTPef|HpXL?0{FuEeUsUL<@3|FuvcQ-t*R30_>d^?enTCd1O3G31J zm{)FM6-_Z~pM@=g1kf=pJjO~}6{)|7!~zKJD?th{;yuMT~R-iLw^os$QBi zs$-hxwZVkO!yfV-#;nm|Rmq_JL}oyCd^GHK#-mG=JB$8*yImdK{^HzE* z@`wKo0SX4&o!|;$zc$Psw%`tEk| zhS7%4MUiacwt0KPk8mM{fBr`NtoH-I6$^$RNKk*|+ED-Kn^SbDt$u!w7Cd;~bnMt@ z1#lHM8Y+gxtLT+^dLA6~heh3EjY4G?mCdWUUFH*>?FMoJ!lUS?q6^TkgMEQ3==R!9 zLTL%%K}h4X^nNrGeA{z@SBs2U0VBn{l|PTPeGeO|UV=BxqWZ;XDSs68_jIYvK1NJE zSsNv2nM3^)@W01p`Z`G)Z6gL^((WlzZs|BiQU z;ec*Mr-8(P8Qww{h$S1)fzy4Vj@+%3jpNd1ORYkqB}>5w?Vs|Y{xf2I`g>d`rk&Hy z(wi6W3ww*ggr}ePk0F%VTL7%i6%jbC6u11uY5a#kmOER#nN;~=iSun$+KVZL)a7al zurV1jZK802x_rI+0rz!v!q9V&1tc!=s9*4h-8$bYr+s6+rs(TiYS)m#ZZ>Ru`pFw5 zO8@krEF=BOBb<4Ic`weLU-s0pZlR3gO^2nbs~Wknd?eI9w}xgq^4lKuePlX$^?#>Z z`;EPP^veu2fV6peGbXHa{sE||NYV^0bWZYl1M)qs0bgcCr^kZ#39H#r z3;abfEmFc1>q}m8mI5gujKOVF^Olgd^e1gx-JO$Y2U3_+rD8A8Dfl;QPMM_h0xPFF$a6wqv{R`-<~A&+8NS zCgpmhpG**Y_8Ae*gE2u^RMHjxuD<`oL)E<>@7Z zX2zI0?=E|U(dmA=NMO7xPOjFyd!>KZ;wbxOP4Vm=_`)a~8eCyDGb@|)sH^g-LD&_M z5FXH=i~LTHOZ+#K^p$WQrA4Lq{Lpy@-TIgS>ywS2k^+heM8s=ypwkTYNjmgi7sEUq zU!&OSOA63|?hkc72fLRTijEY|`du4eJECL`kWffZ>wr3Ny52iszbL zdaXiOJem;t_6leTnM2b$*bkLXbf~T{W$3&&JJoA$NZi7nbx6#*n`jdL3#KPmA3nwkQNy44ngP=RUqZ2y@N4 z)#D_1l;AQER-R}B-$s_!cyOMo)>O{xk@mMBZz!-1>@9?vUoAA{X`1>eSQ+u$;Ytv= zM~4es*!_2b_lgV%_Y?y>e1R_0cg=W_`%mn|3FnXeWP&Sy0tP4sQu;lzU$u2&N=GvP>=$a9O`=;R`;eS_#N-5^6Fk@kLpW=k+o3FrZd7)~eUz-6 z_02rB1i=AF{^P~A&IKtS7IbB65x5@G(>Y-_0iygm5Sjh*>be|N$DN&*VSK+vAl`ho8Z%aqfbnLSj|fgw)db zy2UH0S(_D!e{sHiws++Tv}`rv$0y9$8M4%qrq>K&2Y=#n_d?{R@oYlw8-`^A?o&H# zltDpdX!VH8FTB-a3T}P+J{I!ojpZ~q)e{@Y;FPP=L5y;APfG);-F3ObiTQSCU*@;^ z?mzj8VzgAfTtXTRxZ$SQDAo&3GI^UI?6y5}DDl=K^IOS|q(nrQ$yDTTX>%ui z_ap}olu{aeC;*0K-2k@-HEyUkHL8|~RyRj6e_vnTKE`@tgk9p6h&wWWmdg|@%1C{m zV6m|9bxz_tn{e%+0rFb3exQ6FQ|hIUA-KPuBZ z@{H~X`YQ#Jh!~on$U0p$#XP;_=I|heZj%-Y2k)A~qBOS@M4;lZD*YDnzo2gVi+UDv zL};^fhZ`0~b7*zumeNUxz0OCIgpDhQ(b_Jtbp{% zfg#i=VMHi|6M36ovv;Y#>(0FDW|<#mu4(K0(fO!CIMS>IqOPJ8f|};s(06NDgDnRY zgrqDLwFiUYSm6R_qlnk&4Wvh=M^oNHN$(1juG`7$3`LITdF749h1qsRia4|ers#5eK5xi zA)#P}XihrT?3%f}#`SRMtMhk0&fsretS&L3{O!F;DMZaBg_KReI;#ZD2E8-vlhPd*w>kXOX>_!|#mN zbGYP6&EZT{4mdUXLb~nV#LOs7-11-2M;5JN0gpG~?XTgx+xFv#*Wg zzcf?9BJ&LkJe{5x#;33bAd^UP)Td&m|8C3^lg(MVAQWkn^pt#%nHbv22ZGb+xE7vr z>CW8TL`8anMUYl=Xb3ADnI(qKql$leRQ9&*nkr5=6?wGkpFF-37@DjdbK%5tRkTfm z@n=sd?vG`rG*!DqAOv78FB0X#=e2l0t*sYF-(A*7&or3bdPm(≫KP3S$*|M-)!c z^LR^PX6{wvwd7ip!yKdCizmZ{Kn8-vZ0r7BiD|&Agh`NHQB+nwvE76mB&A{6<^s(R z#_q0KQ&~<-FlMnSL=a@q9!Z*w{L$>*%|{BZE_*?hLV?M0$7~{N{xhweWb|NMSY|+~ z^2G0#c{S46Vb$8ZM1{hHW#Sqkh{1*Y+3#lQZjWD`SEQ;d zhbQVEUqr9YCO+E7uU$WMy~rp)h>$-#wBROIDTV8E&nJx#S=PO5^-y9k(RwNondmz# zY@-fB=kA1E`=Oc@Ldr#Pi-`CNSss<}9txj6JdA4;4tJy#!5A(?JXXQR8LgIhu@H`_y7GP>TqRZkuz41Bui17nD~IC zxzf7na35c?S`m8JPP4&Rqu^+*B2UVBfSZEiECE6q#2CMz8xD^v}y}IOy(0q`lbX)J)m7AOeu7#ES=}2snI~{Ll z=4h>(;Jo@tL`2Ej^nSXb@;Ha5%67JnYg0bekrB+m(XeK>Uut1MNA7daf5=RaKiVi2 z6{g~W+@cUtsiw;qV{0;wxK$LhwtP>*6M0?s_?7mX@6F*60R$W($>eilChL?K+$xE! zKO`V^BDx5f5A)(i-tN6=uWATE#|Rj{-U%Y!tJvA+G%dc?!stx`feo^+r|y);FM3oI z&FlU&c!m_sj9&6==C1!t(&lH@6WRbOJ_%ve$6&dK!|ZLYUm#YRgZ}uU?Z@xxSkF+^r}}4*f;@5}R$5d)=*HB3tcmY+ zox$ncg%;-r&b?~BCUWJlR(ne;BTj`4#4w-?Lcx9YqY+EOU=5~iX%&lQXp10}T{ARD zV{(QtxIGom%As8a^Ofl1!M&ki%qCX4%@_a}gKQ1!%9N^6@Thp4x`h((0ei6YDQ~Yw zA`Ujx5W|g&7byy863Eyjkje~9 z-rQN`hh2J6S)`ip(8Y-GdKtej63{?k!82sQGea1IWU0;#>EUT9NnTkgw|jl2eZlc) zAC4zl5U#N8-U^54@`5&X>5krMa_w%CkD0#4RQ9NRDgTxOA|66n*XJU}SmYvl?a~34 zH4P*sm?fv6%>j1MpFlAp|NAcMF412VwMYS555s10rg_bWI2=^kRcY zd-dz$BeqLoe)uWp6wwNv%RdiH0%&qFBvh5y4P+Gb#rz8<30DIXNhuyBL1CsF%8qKr z3v{6!IZl+l=hqxGB)4Wd#nEt{3z<=q8+x2i_tlGN3(nl*=0y`hTBx3ki`!7*AF4R) z$F!xO0`+d$JtH^=E&+@{+u!G{)U?9z0LUtm=r4cIlFj{+4)9+f7Cz>N5 zKo+FuUM_QJs}kgS%o0|NtAd3w;XNI9^7Mp~qZ|)8m5}sjma%cp{>genALTs$^TK9# zbzG?KOH|F^vARrBIV+ciFpqF4Nz1HXjnMilcIsKuh3_S=P-AjOYMm)B6EFbSoD#4( z2V3(cEbgneiOKOJeG^$3_Jy#!>f2(E|;1jeB(>(brIgZs>k(Y zVlnn6U0Sq|S&Go)*)+x-;3i^Wxtn7r{)fW?0bj6Iv%43*4On{X>}J-bUJ8CXm{)`( zSapaw9YTBUbv6h8-Jd_ALu%brd0C9sQC>p6koZ;0t7#~uoiq3AcC#;%TkfKE{YeHd zqx0dRp;hMLTm$|Jl#JVT%?PtI!k6G4kV&|iJvWqQu<-KYAIi>jjit^C9`y2Sy=DJXm8 zvo^i8n6~|cN$HfIg+>OAmkG6*h**{&N9Fk7L0HI_aV@-K`eVE`MsYs(NadRzvt38pzNK>JXXeZ+o7iA3ZcxbFD=kj!@PL6BGy_ zmqNur$2%n*qmQcP`tmbMSxTqlvzO>;XpTT=7Am$`8Nx~0s}#Ifs<}q07so4Gt}g7> z)ban#)S!|x)m)&n?XVnmAR0ED=5oj+P)VYlbzOw(`O!|kjnC`fS8{d?krT;wZ0p_x z39HfXp`JCxxq*G_E_0*my7k;v?FK=afMTKv>?2nY(Nd`(X)hpm(F_S<0To4NFvH$z zUQ-N8%NwQ9HdpTM=pV^VgtafmOkegvKk5xCPy z+OQMRE2Pg9T0vNH&*jTCDx|wFK)x|$u~LoawvSVdvATn43l6ztl6}UV)=>**>6h23)O_I( zaj|-2xI-^l;#xTn+xs-adOs<7HU&tl28^h4K}TP8(lbIoG{~CuRGa7SgQ(BzWLICM zxkb)bRyBOOl^q;XPl6UAmq!dYsK#2QNPR)1sN_lF8Qmx}j=ZQAOiA382->JH6I7G^ zu}v%JGH=%LLY!Kxq-06cNY8Cju9ji(1ow*Mwb5n4PYJKO_qVG{*KDHP@eL~jzG5XV zfOK$_=YHgz&EJr@8ksR5IojiIsA^%IXP9KtdfSTz;z1_Qc=N;3ID6lVUP|$f@?J_6 zu`wO89EB52hBF6so>rVCn?ooOsi8-MA7RV&Ysefyq|(dSZO;}-m4x?^cm&y#ar2fK z-hb1ezB3$`1h%rxNPP|kJZV-BEx!NpyuCR-Elw~qHs+_Lw4nO0l^oLi`ExH^DD-`Z z@egV3jrlbexjc_VFjacy%&Gc*u1ope9Dk}a=veUub#?T_D3Wt2HjI7LlWpTsTqD6E z-ohOv2fKHK9;KNPNNJocgB2iWG z+@w~h>Il=Q&D}jdlwVa5XgulatEu zl^`?x8MWk~c9cTlEO)HJua_S@LtX1-5go9S={HH0)>u%zzrDduR-k8KFZaMut8aB6 z?d^ekW`*xa&PX~>iT&^|j#)0K1Fev2m1VG-f6dX#cKAC)iXM^Fl#a35W{xji8sS|6OzwLQXAoym~`=y2pw3PG_pC zG;R0L*ou$;Z_?3~q z^t)@~{RQ8Ih2IOs?lTd3Ma>cz6BwF{4 z1&F&Fow86kA`YXJXTIGNiZbcF`#gpV7XPY^lG1TC#wPZs&&%X3zD(w2ML;{p=~96s`3Xvdx8AwPszY>dd#ouXJ*lZ+CUvcN&(@FAwSMSZ!&g*} zurYHLzM;vOeMF|#)U!#QT}335-qk^1G==n}YlFk;03rj#g@#Uh7b0RcsN3_;FScz& zn#Qdn>DAkN{mU1AOUV|^BwHv(##SlChVs$z7nw=74w44_dh=WpKs~{>{d1G!A?5+|GUutnfSjb{x4Dge`67M Ze8&4B=a|j8ULhjzr=p-CUw+#x@Lzbj?Ysa0 literal 0 HcmV?d00001 diff --git a/doc/source/joblib.rst b/doc/source/joblib.rst index b5a3235a8..0900fe92f 100644 --- a/doc/source/joblib.rst +++ b/doc/source/joblib.rst @@ -1,10 +1,12 @@ +.. _ray-joblib: + Distributed Scikit-learn / Joblib ================================= .. _`issue on GitHub`: https://github.com/ray-project/ray/issues -Ray supports running distributed `scikit-learn`_ programs by -implementing a Ray backend for `joblib`_ using `Ray Actors `__ +Ray supports running distributed `scikit-learn`_ programs by +implementing a Ray backend for `joblib`_ using `Ray Actors `__ instead of local processes. This makes it easy to scale existing applications that use scikit-learn from a single node to a cluster. @@ -19,12 +21,12 @@ that use scikit-learn from a single node to a cluster. Quickstart ---------- -To get started, first `install Ray `__, then use +To get started, first `install Ray `__, then use ``from ray.util.joblib import register_ray`` and run ``register_ray()``. This will register Ray as a joblib backend for scikit-learn to use. -Then run your original scikit-learn code inside -``with joblib.parallel_backend('ray')``. This will start a local Ray cluster. -See the `Run on a Cluster`_ section below for instructions to run on +Then run your original scikit-learn code inside +``with joblib.parallel_backend('ray')``. This will start a local Ray cluster. +See the `Run on a Cluster`_ section below for instructions to run on a multi-node Ray cluster instead. .. code-block:: python @@ -62,6 +64,6 @@ You can also start Ray manually by calling ``ray.init()`` (with any of its suppo configuration options) before calling ``with joblib.parallel_backend('ray')``. .. warning:: - + If you do not set the ``RAY_ADDRESS`` environment variable and do not provide ``address`` in ``ray.init(address=

)`` then scikit-learn will run on a SINGLE node! diff --git a/doc/source/tune/_tutorials/overview.rst b/doc/source/tune/_tutorials/overview.rst index 7a09874a3..4a3572175 100644 --- a/doc/source/tune/_tutorials/overview.rst +++ b/doc/source/tune/_tutorials/overview.rst @@ -46,9 +46,9 @@ These pages will demonstrate the various features and configurations of Tune.
.. customgalleryitem:: - :tooltip: A guide to Tune features. + :tooltip: Tune User Guide :figure: /images/tune.png - :description: :doc:`A guide to Tune features ` + :description: :doc:`Tune User Guide ` .. customgalleryitem:: :tooltip: A simple guide to Population-based Training @@ -60,6 +60,11 @@ These pages will demonstrate the various features and configurations of Tune. :figure: /images/tune.png :description: :doc:`A guide to distributed hyperparameter tuning ` +.. customgalleryitem:: + :tooltip: Tune's Scikit-Learn Adapters + :figure: /images/tune-sklearn.png + :description: :doc:`Tune's Scikit-Learn Adapters ` + .. customgalleryitem:: :tooltip: Tuning PyTorch Lightning modules :figure: /images/pytorch_lightning_small.png @@ -81,6 +86,7 @@ These pages will demonstrate the various features and configurations of Tune. tune-usage.rst tune-advanced-tutorial.rst tune-distributed.rst + tune-sklearn.rst tune-pytorch-lightning.rst tune-xgboost.rst @@ -145,6 +151,7 @@ General Examples - `async_hyperband_example `__: Example of using a Trainable class with AsyncHyperBandScheduler. - `hyperband_example `__: Example of using a Trainable class with HyperBandScheduler. Also uses the Experiment class API for specifying the experiment configuration. Also uses the AsyncHyperBandScheduler. - `pbt_example `__: Example of using a Trainable class with PopulationBasedTraining scheduler. +- `PBT with Function API `__: Example of using the function API with a PopulationBasedTraining scheduler. - `pbt_ppo_example `__: Example of optimizing a distributed RLlib algorithm (PPO) with the PopulationBasedTraining scheduler. - `logging_example `__: Example of custom loggers and custom trial directory naming. diff --git a/doc/source/tune/_tutorials/tune-sklearn.py b/doc/source/tune/_tutorials/tune-sklearn.py new file mode 100644 index 000000000..d155ae5b4 --- /dev/null +++ b/doc/source/tune/_tutorials/tune-sklearn.py @@ -0,0 +1,161 @@ +# flake8: noqa +""" +Tune's Scikit Learn Adapters +============================ + +Scikit-Learn is one of the most widely used tools in the ML community for working with data, offering dozens of easy-to-use machine learning algorithms. However, to achieve high performance for these algorithms, you often need to perform **model selection**. + + +.. image:: /images/tune-sklearn.png + :align: center + :width: 50% + +Scikit-Learn `has an existing module for model selection `_, but the algorithms offered (Grid Search/``GridSearchCV`` and Random Search/``RandomizedSearchCV``) are often considered inefficient. In this tutorial, we'll cover ``tune-sklearn``, a drop-in replacement for Scikit-Learn's model selection module with state-of-the-art optimization features such as early stopping and Bayesian Optimization. + +.. tip:: Check out the `tune-sklearn code`_ and :ref:`documentation `. + +.. _`tune-sklearn code`: https://github.com/ray-project/tune-sklearn + +Overview +-------- + +``tune-sklearn`` is a module that integrates Ray Tune's hyperparameter tuning and scikit-learn's Classifier API. ``tune-sklearn`` has two APIs: :ref:`TuneSearchCV `, and :ref:`TuneGridSearchCV `. They are drop-in replacements for Scikit-learn's RandomizedSearchCV and GridSearchCV, so you only need to change less than 5 lines in a standard Scikit-Learn script to use the API. + +Ray Tune's Scikit-learn APIs allows you to easily leverage Bayesian Optimization, HyperBand, and other cutting edge tuning techniques by simply toggling a few parameters. It also supports and provides examples for many other frameworks with Scikit-Learn wrappers such as Skorch (Pytorch), KerasClassifiers (Keras), and XGBoostClassifiers (XGBoost). + +Run ``pip install ray[tune] tune-sklearn`` to get started. + +Walkthrough +----------- + +Let's compare Tune's Scikit-Learn APIs to the standard scikit-learn GridSearchCV. For this example, we'll be using ``TuneGridSearchCV`` with a `SGDClassifier`_. + +.. _`digits dataset`: https://scikit-learn.org/stable/modules/generated/sklearn.datasets.load_digits.html +.. _`SGDClassifier`: https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.SGDClassifier.html + +To start out, change the import statement to get tune-scikit-learn’s grid search cross validation interface: + +""" +# from sklearn.model_selection import GridSearchCV +from ray.tune.sklearn import TuneGridSearchCV + +####################################################################### +# And from there, we would proceed just like how we would in Scikit-Learn’s interface! +# +# The `SGDClassifier`_ has a ``partial_fit`` API, which enables it to stop fitting to the data for a certain hyperparameter configuration. +# If the estimator does not support early stopping, we would fall back to a parallel grid search. + +# Other imports +from sklearn.model_selection import train_test_split +from sklearn.linear_model import SGDClassifier +from sklearn.datasets import make_classification +import numpy as np + +# Create dataset +X, y = make_classification( + n_samples=11000, + n_features=1000, + n_informative=50, + n_redundant=0, + n_classes=10, + class_sep=2.5) +x_train, x_test, y_train, y_test = train_test_split(X, y, test_size=1000) + +# Example parameters to tune from SGDClassifier +parameter_grid = {"alpha": [1e-4, 1e-1, 1], "epsilon": [0.01, 0.1]} + +####################################################################### +# As you can see, the setup here is exactly how you would do it for Scikit-Learn. Now, let's try fitting a model. + +tune_search = TuneGridSearchCV( + SGDClassifier(), + parameter_grid, + early_stopping=True, + max_iters=10) + +import time # Just to compare fit times +start = time.time() +tune_search.fit(x_train, y_train) +end = time.time() +print("Tune GridSearch Fit Time:", end - start) +# Tune GridSearch Fit Time: 15.436315774917603 (for an 8 core laptop) + +####################################################################### +# Note the slight differences we introduced above: +# +# * a `early_stopping`, and +# * a specification of `max_iters` parameter +# +# The ``early_stopping`` parameter allows us to terminate unpromising configurations. If ``early_stopping=True``, +# TuneGridSearchCV will default to using Tune's ASHAScheduler. You can pass in a custom +# algorithm - see :ref:`Tune's documentation on schedulers ` here for a full list to choose from. +# ``max_iters`` is the maximum number of iterations a given hyperparameter set could run for; it may run for fewer iterations if it is early stopped. +# +# Try running this compared to the GridSearchCV equivalent, and see the speedup for yourself! + +from sklearn.model_selection import GridSearchCV +# n_jobs=-1 enables use of all cores like Tune does +sklearn_search = GridSearchCV(SGDClassifier(), parameter_grid, n_jobs=-1) + +start = time.time() +sklearn_search.fit(x_train, y_train) +end = time.time() +print("Sklearn Fit Time:", end - start) +# Sklearn Fit Time: 47.48055911064148 (for an 8 core laptop) + +################################################################### +# Using Bayesian Optimization +# --------------------------- +# +# In addition to the grid search interface, tune-sklearn also provides an interface, TuneSearchCV, for sampling from **distributions of hyperparameters**. +# +# In addition, you can easily enable Bayesian optimization over the distributions in only 2 lines of code: + +# First run `pip install bayesian-optimization` +from ray.tune.sklearn import TuneSearchCV +from sklearn.linear_model import SGDClassifier +from sklearn import datasets +from sklearn.model_selection import train_test_split +import numpy as np + +digits = datasets.load_digits() +x = digits.data +y = digits.target +x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=.2) + +clf = SGDClassifier() +parameter_grid = {"alpha": (1e-4, 1), "epsilon": (0.01, 0.1)} + +tune_search = TuneSearchCV( + clf, + parameter_grid, + search_optimization="bayesian", + n_iter=3, + early_stopping=True, + max_iters=10, +) +tune_search.fit(x_train, y_train) +print(tune_search.best_params_) +# {'alpha': 0.37460266483547777, 'epsilon': 0.09556428757689246} + +################################################################ +# As you can see, it’s very simple to integrate tune-sklearn into existing code. Distributed execution is also easy - you can simply run ``ray.init(address="auto")`` before +# TuneSearchCV to connect to the Ray cluster and parallelize tuning across multiple nodes, as you would in any other Ray Tune script. +# +# +# Code Examples +# ------------- +# +# Check out more detailed examples and get started with tune-sklearn! +# +# * `Skorch with tune-sklearn `_ +# * `Scikit-Learn Pipelines with tune-sklearn `_ +# * `XGBoost with tune-sklearn `_ +# * `KerasClassifier with tune-sklearn `_ +# * `LightGBM with tune-sklearn `_ +# +# +# Further Reading +# --------------- +# +# If you're using scikit-learn for other tasks, take a look at Ray’s :ref:`replacement for joblib `, which allows users to parallelize scikit learn jobs over multiple nodes. diff --git a/doc/source/tune/api_docs/execution.rst b/doc/source/tune/api_docs/execution.rst index 48e32d716..407d11a2e 100644 --- a/doc/source/tune/api_docs/execution.rst +++ b/doc/source/tune/api_docs/execution.rst @@ -1,5 +1,5 @@ -Training (tune.run, tune.Experiment) -==================================== +Execution (tune.run, tune.Experiment) +===================================== .. _tune-run-ref: diff --git a/doc/source/tune/api_docs/overview.rst b/doc/source/tune/api_docs/overview.rst index 66946bf7a..eff796d7b 100644 --- a/doc/source/tune/api_docs/overview.rst +++ b/doc/source/tune/api_docs/overview.rst @@ -18,6 +18,7 @@ on `Github`_. grid_random.rst suggestion.rst schedulers.rst + sklearn.rst logging.rst internals.rst client.rst diff --git a/doc/source/tune/api_docs/sklearn.rst b/doc/source/tune/api_docs/sklearn.rst new file mode 100644 index 000000000..0067a952c --- /dev/null +++ b/doc/source/tune/api_docs/sklearn.rst @@ -0,0 +1,14 @@ +.. _tune-sklearn-docs: + +Scikit-Learn API (tune.sklearn) +================================ + +.. _tunegridsearchcv-docs: + +.. autoclass:: ray.tune.sklearn.TuneGridSearchCV + :inherited-members: + +.. _tunesearchcv-docs: + +.. autoclass:: ray.tune.sklearn.TuneSearchCV + :inherited-members: diff --git a/docker/tune_test/requirements.txt b/docker/tune_test/requirements.txt index 413ac27e8..a797e0143 100644 --- a/docker/tune_test/requirements.txt +++ b/docker/tune_test/requirements.txt @@ -32,3 +32,4 @@ xgboost zoopt>=0.4.0 timm dataclasses +git+https://github.com/ray-project/tune-sklearn.git#egg=tune-sklearn diff --git a/python/ray/tune/examples/README.rst b/python/ray/tune/examples/README.rst index dc4efbcc3..b4fe8a84f 100644 --- a/python/ray/tune/examples/README.rst +++ b/python/ray/tune/examples/README.rst @@ -14,6 +14,7 @@ General Examples - `async_hyperband_example `__: Example of using a Trainable class with AsyncHyperBandScheduler. - `hyperband_example `__: Example of using a Trainable class with HyperBandScheduler. Also uses the Experiment class API for specifying the experiment configuration. Also uses the AsyncHyperBandScheduler. - `pbt_example `__: Example of using a Trainable class with PopulationBasedTraining scheduler. +- `PBT with Function API `__: Example of using the function API with a PopulationBasedTraining scheduler. - `pbt_ppo_example `__: Example of optimizing a distributed RLlib algorithm (PPO) with the PopulationBasedTraining scheduler. - `logging_example `__: Example of custom loggers and custom trial directory naming. diff --git a/python/ray/tune/sklearn.py b/python/ray/tune/sklearn.py new file mode 100644 index 000000000..f7131aa14 --- /dev/null +++ b/python/ray/tune/sklearn.py @@ -0,0 +1,14 @@ +import logging + +logger = logging.getLogger(__name__) + +TuneSearchCV = None +TuneGridSearchCV = None + +try: + from tune_sklearn import TuneSearchCV, TuneGridSearchCV +except ImportError: + logger.info("tune_sklearn is not installed. Please run " + "`pip install tune-sklearn`.") + +__all__ = ["TuneSearchCV", "TuneGridSearchCV"]