From 05267766c07cd6296f272e5e38296dfb639fca5e Mon Sep 17 00:00:00 2001 From: R-icntay <63848664+R-icntay@users.noreply.github.com> Date: Tue, 31 Aug 2021 22:41:03 +0300 Subject: [PATCH] Add R resources for lesson 12: More classifiers (#329) * Add artwork for lesson 12: More classifiers * Delete README.md * Add R Notebooks for lesson 12: More classifiers * Update README.md --- .../3-Classifiers-2/images/r_learners_sm.jpeg | Bin 0 -> 40334 bytes .../3-Classifiers-2/images/svm.png | Bin 0 -> 38653 bytes .../3-Classifiers-2/solution/R/README.md | 1 - .../solution/R/lesson_12-R.ipynb | 645 ++++++++++++++++++ .../3-Classifiers-2/solution/R/lesson_12.Rmd | 452 ++++++++++++ README.md | 6 +- 6 files changed, 1100 insertions(+), 4 deletions(-) create mode 100644 4-Classification/3-Classifiers-2/images/r_learners_sm.jpeg create mode 100644 4-Classification/3-Classifiers-2/images/svm.png delete mode 100644 4-Classification/3-Classifiers-2/solution/R/README.md create mode 100644 4-Classification/3-Classifiers-2/solution/R/lesson_12-R.ipynb create mode 100644 4-Classification/3-Classifiers-2/solution/R/lesson_12.Rmd diff --git a/4-Classification/3-Classifiers-2/images/r_learners_sm.jpeg b/4-Classification/3-Classifiers-2/images/r_learners_sm.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..ff8d2945dc6ec7751987c5224f1d3139beae8467 GIT binary patch literal 40334 zcmd422T;>N)Gr#OcTkEnBTYI;??gpGKtMoxlP1j&=`9ci0qFt)0s%nKngE@_kQ1eZ|2Q?JK1C>+1>0p`#)!Yr|jjg%QXOtzK)&_fQ(cm z$s_@Q%XL8RgAmVW0Dz$(KpX%7&;Y2&r~u@o78&3Q>Fw%&ZA=OWODQ*n5$eivVrF6*97aum4`i zDarp8Dhdj6N@^-<>VK7nmY$Y|hK`1snvRiZ}`7aq!$AX zHO;>s|If1>-%O@wlLRu^(1&N7d5v1P%6s(k2`DO1?T{Cf_7Vu@0dzDs1BY3aAhuw4%B_!|c z7e!0Q!FipFTlkiUsF=8dqLQ+Ts+#uw2RgbB_4Li2m|Iv{S=+d{x;=CE@bvNz2n-4i z2@Q*W9TOY(CO#oO<6UM}_WPXNPsJsrpUcWCDjOP`;LRWN>U(Gf4G4qJaOZNt3w%(N78gzo-&@Fdx`rU0>ms)#wgT zVAaC1SMGe0BIi_q1kHFR-)!$);l9v}r*GcsUh|K29=KzcuKU&U8wm9R8oRny_w8I+ zE){F@wqGYi{exO2fEDe_qnLTfbQpAO55T5mrr$O1<6M`B5*E|Gr%te^`%}Q zwt&L~BIQ(|L1s&w<$RrF$AgdONj;`K@oTMLQx-m;G)Chp7;Zl#2wwuCo-RdHVK`BO zenF+X{>0JK-b;W!1kY}1Gzo`bZ`BQjgTvlHibGk zs9t?Oq*B*fF`)2e;X}jswQ`*3Xzx?=a^o~`G?#iPw9yfNo(Pbuhch{#m(oXTKvNwL`Gf$dZ?iM6&{gH z=-;!83KRQN0exD2jZ3xz>W0yK@x>sNtm8*>G~O}$@wY$hs8&dnlyy*>(dsSakp{ju z$5N;CVqDFn3tAyr0%M+S^cr`S5R8H4LX2 z`O>my74;&sE)OS!S8qMb>2E#f>tTfY7)~%_<_T z87L1&gOvm^&2P_#vIAT7v9^&KZ*C;$lJ-%w0zP+$zx2N=E*C~VI_F( z2OY3{SMCg;i9b94Y|NX=W2-edRA5@6tS_d_wg}euTd+AZ>0DfNhd8pvTG^xzJegfS zs+;Yez{o79V?r7T*Zs~#pR$HaqG+LNU6q? zy02QyUWJP=S`Df2v#x~_8{4j#OPKdS+iy{fo94duTN6^+TjP z5KnvO;GJ2oHs1J1ml0eIn50TD|62cv_ zata^iZm(R=Mm+KqSRZ$~1W^1UX@fGXF_DgW{d^ssqAAm#<%JZkY&;LXBFAB0H0zpR zweU_uj`5qCT(;@Mb7~9&X+Qy9fe;f(^$RcDICCVMOO&QzogjO(DG>kkbQ|>m^U+g{DwL!9d>Nr)Gy>9bl zU35%LxUs9|!m;ji+#-;*OEe^Soin!33athIykq06l8WSPe3SMe>Yj9{`%PZ)tq;6( zJy>&`F6DljlyJG1t%EoF~@{<_7?e}!a^t%*t54H1;j0Fmphpx&*W24u) z`(O*UM7n{Zol5}iq2eg8x6P=cXu;ld{sw>BOwGKR!SdOWtzv(}!}7O=V1`$&i`${- zh6*F-5-0V#*Gz$`CRJs`4Zr5sd^oUw7lx>DtjOpkQ^PuCBjR0bO}IEW6)SfoV2EK(G(xO5grgnISUUBeh zD@>4_w$4B{Fdi%1+fwTfmNos({sCau=vt^$V357uaKTle<(FRd^BuqIA*cJHdP!FH zWb<;3WVnit?PwuTuHoc^+7@^H=T*q8CQV?erB7-qOse5~LhE;P=h;tU%W&>c)#)xZPY{!3e~@K?$ZXSbRj5US ziqw8n!a!7JB`+|sf%2iQ0rm@&(QLhwl;Ry;BrwW^ov0frX>LL_vX z3$(xXN*z>{RkhrCQ7)&(%c8?1(WN1ivEj?oSGHfNGWtU{x3c(0=iF8oc<{_KV0rIU zL*vhNs8m@%{#3){>WNknp%k{(xk?v#1)Jint{$Gs;)#(j?cA=xq&8aXSgq@SNzrcl z5EDw_f&J0Qz%4{qeme;1%uJFkiG#t$=?FM(*rN41+4-!pZ=)o;sv}2DzQ(C7VFI%eS+jPPV6(W;o+D@P^QyvxFPn$zX%wv<^MjitCvbQ zgmPX2D0c{jGEiom1XLc~@=625>%fG)R@rgU9oJ;bp1=pvE*LXVmHrqd`%-c?4I>_7 zl~k=?$&I>AFF549^R!s!$#kxfPq1I=vl`beWOD++$OdL!VX81=XMWbTH$Fe&qP%g4 z=b+w<+aE@&_f10|VI@M>Q^~Py=aNl%!~5e4&$1r9N9G=>$8*!iZHazuP7KxIXS<@i z6W0d=dXiL~7I3Fac*;h1I&e?44w;nLe3$nPFBR{4>S9hvM`4K1knYrT zFF=;A9(AOHI|nx6n+3T8so1*)*;Ao9IwX>0klzKWr*eAsOHZ$&J+z*5GJT; z&S!U0CFia|Rgm55Yt`}f@sFq&9jMXK7;!=wXf1C=@P=?>v?;b`9jr{SOwyqA!4wEg zZ=CyMM#ZdhSki-eo~0@AyWjm_iB!!0pcYtSfkEq1cF+i>sLY7#e0(ZO{t~c8HdRtr zS-e5zV!&z91Qs_JS}cO|S zKZIJkY=W>dy?rDjgJJ;v46cL*#zOC|*Vr#S{+gfy-(!|Vef%K#eBbvM_sJo7dPoE|h|ComZif7Yq)1|*YS6(C5yw%+3 zn>ZzPHd~t&mNz%3Q~LY+*MdV>pX;jsG3PhZ0(A~k9UCkM3~dduoM`VpHJRQBp95?D zXjAV!ux;Pm)WakCCsyPS!g+7$9wBsN@9@^5XZmmfupTMA@fw6Xa}fLxJeyDN0{O>K>+&TV+<-r2Pk-79~C z!eTnEw~6*ER5l=V=ybTo(1Xfygh6ID6GzaSpL_GYlRaZg@4kKw$Bi9a0_3yIOGS0^ z5Xvn-B}fm0pgaEB>HzEubD!gNPc4bBQ`U!YJ;{^22(4$WcR$-MJ7`vq?N`B9)Zc1! z@I?s=$NR-&+}Ks1KRS68?Sz#N%ztkm{| zW1L1vV(*b>P#f^ovw11JcBsE^vn`F!)ZoL?6v<~U+gAGst{1NN9@-CtfUx3!_JL^I ze#fSkC=xa_e#K$3w<3ok=5xf2h^OW;BvTPqHWd*`ko~mw>*= zs7Z$`>iGIrOYH^3%#(COdqw3?v0!83bwq(&i7FTaffSr+i4h9m-H3L~d+e+Ab1lJ; zpv|?_Xgny8sWiycmc1KIuJbL?Icq-qz7AHv_;!E0G0pio80t%ihI*nciFD^{-HE8- z5?efI63XS|5~@CCgL3@kl^tx-`{k*do551NvJjFSZzx?`3RnCm857*`Lv!9}?;Ga5DIZgsMm?n;jqnk>3W(`J{QqbQiNi z>|grZB?w1+$5=j~$yFJCiYxZf2fOh{%S^npOp6|v>77pzNdEfipf+PRRFgq$=0*C4 zVBJ?F?V;A$e_FhR& zv6Eobew|~J?-;(gx z33qtDht7#Xe`Y#Ct^jzADo$0ETuwjeNO0!b_`VbVxs`dId#0+c<+@U=W*dCMpuc|s;j0R@SlTGJ`GSd7_aCmZQuIC3`-iO7) zIjI1$VUbskyu>LG>ampe;6+jaJJz8^ym6Z=Fz{aE_^wpXwjz0TP4rCd5;X`oXczbn zHORPetd`}2|KiA&yWbw1(O~Z|7$~kow~tdwz+USCHd_ur7S2DlpxLB0lDXyBc+Qq@ zX>uU_f`J!ayg17#5Op^M5=T-uKb5Av7}q;ZjkHE8Gh_HVuRcEqXMCfG1ufU*tTS_A z9p4CEpRBET0(%CQ=oYgdbkI{j7ZBUv7ethoSTM*j-Q}VPF0GqQ?oAN=c;$auN!Hn- z?&d)$PKr39rG$M|g7x_`&=wcdbR5c*O)q!;$FCw-yxKyvk4xw&E$iSPcxWpA+BA_) z)&=K?7o37au38VRYr5F9dq>)H-&M5Ug*UZ5zvzM=WeXlTXKZQ-Xa@sXZF{8no(Dyx5*v%iNL zt=4DvBS|K2-8w8OQWPCnkX(fQBsG^yVOuA2a?SI3d%&*)Iaa|O4$o&t$2?rMW-f)& zTY!ku|J{S<=G~<-1(gr@;<(F!nSPizrv!Ol`*4g}5|Vz(oBWK6Cc(Ed#A2!{Xk2 z$%QW6C1A%iK?hqU&z#ZMHbiL3orS#{e5rOJkqn7PBu0!gle*SsPu3lc>O>0dJ&&}f zHv%5+R)gMAy;S#-h=T#KuZRbEpN395vy2HPaGsFy{_!)NXdt06*O-vwLEiDHHG+9C zY%`?Ju(2ycFL&j9ONYf~nCJ7ag&u~fFGOA!_ND(JaCWdkNJ=CRmT}#`G9&?OTXn#J znM^)Yt_4L2eP0UY5{j{l>SyvE1$jXh>$I_KjXM`I3#W(dqT&-iw-${xllIkG2=Zp_ zDf(Av&yA+P5&5x;8*yk*%Hg53@Yf!zQgHhB4{u-TP{=ZT5PumeLaquLCyHaynWzEP zH58+HKXT=EWDvHLFmr-4v>DH87S?@u;EA`uZf-&rL3ZA5mw6qlyU3=0D#c7N2YD`&KxsF7x z+t&|quRjhaGjqXjbdMECZ9W=*)D<^$20P5}wn54sUQD*^opYam&=Rkq^*6~#JR*dr zhn_`iIFhi92WjVD2S3SsGU=p>W2M@i_xM9ab-p?7&k8)WY{%dD=f(>0?=P?~ObWoI zGOk3-wZBvx%>%xm&beU>uBHyE6=DE}4WJ$S@_~azxx^nz`LUG!Frhvog&KiJ5Yhd7 zEfv_B9e3`jQN6`vGcmAk0(!g_)G^OUwu90 zMtN>*`lZWhs$0ZFK3{;!qTPG>+~CIK*r=nGNM#}ahyrWCmIU*uU#dGG+_DlcOIqjq zy#khYLzxYcxRN&4Z{8!K1D$iAj)`X0RYk`hU*7XjUOP%FWsmquIMl^(GCR8 z5*VY0oX`O-@x7x)S7-I56bRgt`FfvgqY^}Gi(=SJ?oj?;R9;S``6v63Y>Xo|?el`5 zUTArUty%M{yFH#arp!&w)9m?H@{8~`DDi;^m$JQFs;^$@a5%Ef&UX3!n%bc*rPU24 z8pRraFdwq%?I^b)!w-|mdB3)_C>qVXd=+g$g^&_Yl#R_@#+sUHZ-(2wuLNV*oXnrR zU;_ZiUs3`>zhMNgY;N1JitA6=iZO2! zdthzYn4;Yqlpfd!eGA;PDD3VA@ixcx3xLnma{Lbb@4Lqo+|Me&HJ%eI)=PJTJ^i8{ zK8w98P8t!BbZ*sb(3}F^)-2g)&>;f{V{(Uj-f- zGrSHG_$UW8Lou&7GT`q_PpAK|y+6xJU4INd5YqYzH+D+7qbZ$mehIK3?Qb5QANBl9 zDN-CwQE+YU`CHAgoKWgTAAX88MvTq|Cj{e?_TEK^Y}5}%2U}|ZQW9nG!Hp=Y{Bp0X zu-K0;7gxMk2Ebs6_UG|RISj+wUjlW~6)oHlOdFJ7Zg%drM6C?^o`nNJW=VTqVO=A! z7HK`d1&Ma0>ivu$%b=Nyh6J)t^;*yWPXV`D_f6m+kQqveg;a(#4sgE?dzM%{ustq+ z^fOG-qtijxnf;01&pYhr+8c#fN%x^c_Q=T=&I0Zh_ynFc!o#NtCxO&b&$BWY60IK* z80sE#>#IMWR-e&Hs@%^UDU%P<9d+-pe)*&FUJCz;UwebE;RA@ssAx%>l+ViWLgAOp z?Y8-$iVoX~C68{V=TFfV6%aw&MyHqQy;jv_{w$Bb z%=yq6boWcnLAA4(K;iaqgH(F2IMYkV0Oqby%V^_)+UMen}mMYDf{t^a$6NGqs;KjMyRVA|pv! z{{wI^F%PO5$CJWgh6<$f487ob3D~IA9GaO~QMK=81sk883=xEliQ*k`10=l#?5&G) zh#pVsFL1Llij2)Z?9My)UHU0k5X5bNKA!TswZvP2&4IpfQEv;>o!mn6jK#o>0 zP}Q0(DVDm3!jEQcYJ%)R=pm98_ZSPp*iCnULO0b%MzzNK5!GQ*?@$Y!{m#F8+^p$R z-?Ce%r(V}NskrXslVlIP!0#{U>6`D4g}zX|%2 z!O5@u{1|;Sy+TB4-#A7 zvVAwvyGh>qGP2N3w4XIfsQxV%varvO7*ADlKQH}oQBS<{lv$rBg5DgejwENd zjq@vSv#S|2MVK~Yyw_8Ux3fd4ipTq%`OXE4e&Xe^!tQI^DY(ZThj6S~Ko;&)JJ zHv;sM&p1R2atW|kq4V@2au2vjID4sO=C7XC4^qls0(L)s7G*G$4y^^>F>HE%2~a6B zy}i`*67oY6re>V=p^_uk@MP$t%=AemVl2jOLju%D@-MGw&%0K%Ls~WOo?Tab&Oj%c z^qX^(^O>Lsk#@6EZ*3d_8o1DeXU=YcUDT={cR0O_{mpLV8c6-yf2knybJwJnWG?75 z_f{hie?^1@?_3qp%==#>S9l2B{4GH7-FSc26ZIO6O8|jZ;xOzIU`k9rkDF^%qCXcA zJ}V!;1o&u53KACHtf;pn65LW+fYXLKmw*#?g>kIaMG$Or)!xznlf$d?bMwvzk(j{i zAXLLiAnuHlnR>o&&OY_WbS_a_KNpG9b7CjvEyz1;Hb60BPgi(hS)JbxQWFH^=@AqCNyM8i|;0Q8X0Hj=xB_|`lZ7&(w7|u zC;LW+3J}3+-+hX|a-}@n0`7lN6b^+99NvIRVQI@e8bU6N>2^kI?Gou#r2|eSf_;l9wPaPHS%oS8 zV1!t^{a=5Y=?qJQ@B8JoJiUm>!VTN_;#deP;{BcH&Go)FZ=T@BLNPVPR@}CyJcgtkgH*GW3t6FF}@zmt@ep+GlI9swsxx5;P)jk|rM(cDG zPmeT$#;$lmsUre|y~}k21BLgjS4SYcQvwnVwJYuHmZ~Bj+4os|CP}e<%MlpK(S~Q6 z8pk}qCVH4}yY7#0|H_pMUODQhdttf{&4iVL<7nC!Sa*qM|nlOlg@{}1XE^c3YD^#ZD$_r&_0 z>W*ogyrtI>SoHJ8G||1-k@oM376~m>LqoBzx-_LBv{I6&YN2Lnlkljlg-c(m{JU0ew;F~40jABx??zO`#L}wV{9m^ zuqW;^aSxAwAlmg-{F)CfUE-&qe5E_PpH_O8gHD8l)3D86$!C3xOqT$g8ThRR`f{&H zA?0hdT+tLfPhJas1Q$K|^4Q)~(fQs@J|Ci}ogXDaNH3YRe2)q`UEaULL+JU=cR!U0dr1QyB(dKtep8lAq zJHHIagvNf-DxG)x(nd;qh;t5OeK&rdW{h>BFsoRm#r;dbsXO1xrhu6dsL!C2xg#cuIo zJz1nl_e>+A6Cjg1A8`2ZM)`~C9}7p;&H@=mS?GQXJ!$@_*{ zme=*lP05<-+)9u$<@a%8(&f5|w2OL3t%yED29rR{pA2j7aV9*)ROGL3?@Q5*SWkF>UcC_hXt z%(HtRh_$dOM;T*0x`zviA_aHf56URPhPsxjWYV_XlUIW8gI+)dNNi2Oi(rq749Y`5 z*7F^DjOE7gTRH1siz9%cbs5O%$LdTaJ`x?vK-({we{4zJKnZ5A`7y9<7x-!*XN31a7iv~!^hZB zrIdK>(`Tw=L&1ZfWx@qf;s6w8hABnu?_2NzH}0ngnXU2pNnQfJl{A^-zn3Ww)z}>8 zE3e1(8|usXZeKsav1tBSw(rMYh-uBDR`+2fZf_@uRpXV>dj_fZ+n#GPCkB*r8)|KB z0+zmg4Y68t+gy5GFDziQ6@L=!fqi+2cI{e>KO(|P6#&M3UZryh|T zJ~bO+LalL2J&@-+t9M^$!4o%;>iq@RP|r#?n1N86xIQ(eRC2o&gLNbapPIs(z^j^* z9cl5n;vceKg}^;GEvvk9e;39E*p3>QwzqtlKEXO{?{070W62axOh)-?Z>9YmlxCeOqI8wx+5;5CZTkpMw6fThBV01BSoq^O* zMS90?73k4rYrJ{;X7l<3U_>D&SV_TZ)7+e{@;2Z)Ak3`lXN~nt=0$++QIN~~4Owi? z2HJ1?L;fOx)3w>)6NA~aSoQaqhw%czmSk~T-o;o;RG9;IakCx77~(Ywfrqald5z`n z6`2^wbNB(s?rN3sUJ0qQ#$EzQ(*7GgzrTt41mW}!Ug7I%fEQf%ai0F1YuR#DX~54x zfhYK#gL-TACYNmv&${2END)R2H#*Zy6~|mXkLudfBb)Z;skv}qT&{HaVo80BwUM^& zJMoD6p}&sSBzuHE66EbbL#T9I$JA^{g+D=E0umKGdGz2jG9_PArjAzSknwhzN!RcH zZhqD7<*su5{KE^eHpkS1NCx~pRAEZ>oNg=1ryf$?z~5&`*LGeX!RHsZkR0UXJvwTZ z(G~_vYhm1;8j%-Q`Q9!%>~C8gl#Z*7JTx&siLhC8m;ytpw^O#qj)V$mi&d>%o^n|M z@6jkax1}Q4rY-N1Ja4x@C5V#0!M;?&;tW8m)3x1t;BIW?@py7!!)o6+!o|=Q$QvS~ zjn-isVw>l{L2jBMB;NcOzXAIdSa%u`$uk^^}n>7hbrA9Y8%QP##Wjwm>6t)NRmmOp%)e%O11 z=Xs#KqMSWhD2{d@eNGSf~o)ov}yO2IAJ*}-E`=48$1Y5hVbQ879p zs+2P)#)z}Wjf7&*TA5=5$=!K!o^_@@0rt@nFc3KAeM|Cc!hd`R*G)^BiRmN<^88jy?7?-{qn}wwfmZ@C^``c;*IRkl zzan8?hcR91giMf6A`Aigro}oFdD9~dlhOuEZjzER*wy1MF{$Yn{n#iy5^iyB-VASq zAQ~Wdm~1gC==njJMF=Kx7J98Za@au<-qz~cI<07pH*pWXmEl*-oSV2+p`IP^oSbo3 zkl|$L7qB4_TTcABa>ba>)*cZuxT+0rnGay3^^57qQY0lrEp+b8e|WJv*L7csUXADgvdo}hTq z$U+$iL|HIWBef_|DP=80O~{|d@v=M?7@_FG@+XWG9^r?AaqQlfw)c~LBo*1dk6VAz z0iE?%d*kOX?z!Hs+F+jjeC}fN*m7kF{kaZ;wnO_K1uSj1Nf41ogr-TR#?P0IfcKS!K4Hp(YuSl*#R9LroMPFUVxBUEY#qL5ARZH5N8pU)SYdPm(* zW%aTB!HOKhe{nE?jTn9KPbow_c6@CbQWNlM1KTkOlR@f?1vV~!fYpe#KyWaB-h&~< zACbR)YAE82pycSQnZFP-xvo>>N3XMN-5es6M#L^GXSX`k?On8Lavr~nC{_FL;fKlD zJKhXihfyhwQ(dX&_d`|x>ZvP+2eoM$^f)#J$7zJ4Tlx#g>QkR^|A|>YK3MU7ku~*l zvMLnZS}#8J@jUbHutY1vJgmtw+Yah)FM(-~TVM3xc-vL;iSe=5-tFUqW@>t#TRpvR zwgB0hDpxwnHiD668{@oCFSK$DUU1`(9I8Hn=Mih_nA6Z!5HQ$#`?EH!y66?z@0mJ@$6eFVkC3WnL$Q`5<82 z{07;OQTOa`(G~e5ckeE%VSn*_8LGNjh1vWZxu0U?$I_#9N^N0<9^MFKYK%g~A>4LR zQ+jabS$N*?9E}s#(9ivQDIr^Pj!{}-A?_QlmW=DGEFNt;@Sy@p8JAWM|A{ZA`q97A z#=Xv!F%zhg6)tQl%6)w_C50%A+RlC&?^ztt>WSX7e7ItC!>qTSE>5IyRfU2}-e^LL zJ*0U34^m{!k;6U&vVPcu3s3AR({sAf6^(P|xM{%hK#IqpzLng={pk1@thk^Zh4TNV z%POhcZ7Tmxiy}8M$Vl1_Ijn;mP~=%y`I+N#tJ1H0C)YT#dCxoWyim_$eyDe~dHmHO z=gk!d>!CtNMZ_;+Y}fX5O&v7IM}{P$GEF$eob`r;iBG>x61aF9@&|JC$UoZol5(iX z(R1f`2{ccy+Kl^1`j~m9RTA>{&vYiu7R%)VHqyz}rv-q|-kK@SHipBGBZt}FTfTk! zB5&k|0gL>r@lu#a#8otL{$~Gr9ZH}15^%)`gmXH%u3W6^>(2JpxT5+Um zB&8HMzq<1hI+OFgyRS$09>+q|^IV)?P3ZmwLw?AIlm-+C=VaIYUD6J=U9 z+{UxEr#n!Sf4K*gICNh5Xz?}vy3^{@I3UH-CjE?XNTA(6v1V-MWjy@*RgV38+Xr(;2S{tmKlV*PD(mn6M4v09-w&;R| z$CT>518Q#-r&x1AQk0jJ*>eeafOW&^M~)H|PzO0KCD>dDXP>v?XlYyjeAiiSuHco|d_}?){Rf^tPtV{HCy$Q??Lg{uDP| z;n#Z+&NVc%X4`Q>BH}mZGl$ZT5)Vo_GW5sBj1TEOPY&a_{rV(h4usEFy?$?LweG7k zTmm%awsR^TRQ($ArT0m>`}W~bi+)XYh{cdYtDbbxu}~72Le)W2MN!?(dM6F>ZR)O9 zqe>LTGy2Xi56w?iOkZd+MjH2p8iBK2#GlT1OpjCy$`;Xx;U0L~k9G%lB7>Imf;%q( z+!ZSN8i?b-eVf|g^`c{iPJQF^zg4TS%@(FYoRp72`C?BvW8)~@lAVNt2`AYWeR4lj z85h^kqdCsr=3@;J!Aronp~2&f*`)>?Y_nUV$z^T5ou{M z25j2BloM*^<|cK_N5n>d9Psz>{n|lL4lpGnPM64)5t?C|nNEkzj|~MU7c4Sn zt~XXI55?5b;^D@J+2SW4HH$~J-cuh585&@beOxeF&Raqq$Y2}#ywJP#$j4_R_=)wW zd$%XE+{h*Ax7ZkHM0V;H+=;Zw!t;an{*Te6dCb&Ucgt4zRJ91{DoTnD>&Q*e(P5L= z5WY#3`ZqOg^Uz<|pCOj_FX} z4Qfay@~}%lv#V@FVz7(pla-VpVY!3sr6B#@^lFNM zWi{UA5|D0N2b<5k1c)CyGQqyLmyx{4)s7c{y7>@gX8k8+)&u?`t~d$7HE7Ui{ky7( zo~iO#Rhd`>+otjvrT1hA^zKP8?c(9aD(D zwh@y*Kq&a~Y4gFvqf`bNpHPFms1o_}4{|y*hOgxzt8mS+n;Vih3}Y@)P>d6t?_5w{wmXw`8P>VkPHT_a5b!vIqoxh{#9+A zOb2&kby#eEz@Tk1=^!iL6S01}bdPUU?P^IjWsZg&HMd!TK*}}QVo6Q?VHJOcncDr# z*%}dijnvd~i97GTwxrp-U)Z*gtGiSwPf{K(Ii+R$eK}*Fi5xF%TJ4AYineZ@>*5JX zFpyGwzvOk}#tgZ=r&{c7t0rqoyBKn)o6xsGl#0geV>D@D3GSq~I#1BaHAmZ|o{0g| z0NLAS(aVYf!6q5qg0q^_q=bBu+bRohfmTLRk}RJo67HhnXq~`=HYyZI5_D|`;}3~1 z{`y+)#^0b=kIna@bayCA?%fqFfjUuk1idZstt|0PF0Y!rb5b`&GRYh%IWcjh85Kg5 zdkSpyd~2a9(^KQ2y)_7%A*n{7TaF;4w_66vSlL{+?yYJk_oL$X!|K~VMtZ1MU&6SF z0^{X_`C)I%N?cThI%<*URzz)dp;RUV(%JmnOJ`)bXQ6b#x@>n*Q5Ytz1UX2W?;uXD zeyJ_Of$&VIx|Cux#SMY!(?cP98p)qx&d(BmzKNAyapsjf;8ztJ8hy1+o#t4wS|2f% zk8^?bXdZM>S3lC4ELrT1bN5qLY{_mm5S#VC1W3*g4~9t5Gxb}h>WEt!`~MD+7w+Aj z>VN80x2gu73ig^x>}+Z5wp|%kN*!N~?!HzVNsqTg9VSXa`$XanH`@cXV{>o%O1coR ziDi>j7@kegtC~o4JT;mrhF;s3IzAxY8J=AU2^o$xIf+PN{!%w+%!$=CK`|%54XLeK zaMBgUOT+HLw5kgNN^?C2Wn2_5?ozyX5ZMIl(SrIAwFoIt+E59f+_7(eQ^!F&4VquZ zzgoQ`D%P?eQblyX4#^gO-%-fY^v-AcFT~GaJJ)yuAN#sKIaor%B~vg{v!^`JmU1>= zB=H%pg|#X4`lZtdQe@&xX=LD6X~P)6oc7#~c{y z0W6^#6%#qRqKx9}&jUm4%XDk4Vj=JBZ0q&Rb~Sc0XXxP|Fj>ubABQ@AjD{pT&EVD7YeYtHK1G_XSrShV z>MNYCsRQgY{gYf1Xg_Syd^KE=)yd280BWYV>s@7C>)|4MkQsWJUTa5hk`lr)@>z9BRc^UZhXlJzZY7Ag)mbJFx z5|GUo8ri0}dI{hu(GrLWTio3vVlo3jn0$ceaG+!byd*7qqF#|N6o?eTxnlh>79O5S)+qH|mj$9fNn@ADl1yg6) zUY0t(yXo(kPZ2ZR)-$BXe$m=U{=;p}0g0+#gDMH8-g`zY?_YWiPz%=c zC;)_nER-y{fEL2w@L|ky7m!fpC;@#q4O&+sBtY+aasCWK!M`WLLf2vvm~p%Oj^4yY z1V5U3ziLzT&Ul7LHte;$t}H1!DzRl8#X7J7jIMG~9;J`?Ja$L*+^!x}l8L82sV+J- z66$@%O0@AiA2cJd8Os2fp3Mh9aX2yJKo>n0htu#N3ZTQi5Zg7?w!`k=7cyJM``#hK z=_{g6cY>gwG=HU8`wb-p_M#vO9R}r*y&$d*eqsDY36ZRW__L**A{|4oMq5ms@w?i@ z=#J2aGX2>TvIV>tA+AdlDvE^$L~w4PP!}uTN5jGHWjuL-Kgy49ru`V^{qS@jVZ?So zVcAU5>5X%VICVVfp4)4GLr+D@*0cjf`l=H9i`TV9Rt-m2z-0IQuA~@T{kfX(hu0*w zRBhHl*x9oA_1}9_a7Efv3HX~@Zi~V83syE;#4wa_C7tT&T;D0 zJC#Ly-QH$q(d(Mku#3>drzL?@v0*pFWdF;pSeolsY3iKwh_s`q6c`)dBdpJhYNAtv z?RnM&(2cQG2FKsLndFjiO3>}e)oT3MS3)BWP9a-k4%w_4+AI@8Gv`IspBv zK`L3h))loLNkj2pk)Aq=_PRG7-WA%|n4NxCdaIxTEaG<1mC3-cWakF1X@~uG+X`Vu za`^&D_q;xWC{D2pk8EZ*ZA@=p0^QQ${9#{1c6oe0@8cd@j!Fr!}}Uz+Eji;L^5(5aa!vkJz?91Ei;k zsbY=5xjr5PaW;tH>HFeoF*jeo>GRhuWK$2IZe(gyeNI@U{tQiD@Sp8U0m| zTcTi>tO%o-R4lghA;xyBcz%0nS<8dv$i52Ut0V6K_7NHAuzzI1Ck{mgAd-$CPAQLm9R^DXmI^0~S_sGYp1 zN5KxR`f6-nd6$BUH@naA*XN-=AWIV|9XO|0YD;MbJx@@9Z|k7Sn8a-~Yzd zdq=bR|Np}}RBM*nE3HwR+SEu@?V`1}x1vf)sI4I_MeU*}N~-ou?VZ$KMeRLeZ;25> z2(QTHkFtl{M!5}+G3LGeLGg_WmuS2h zP`E|Gz*oOooQvP6-iz$~>t>~on5*EOkg2P=uCfcdnRI8qBjNdldH$9?J>R%Elt`d= zs0}aFPhSXEp(|G8{?=9=1G*=kgGn1tWtlD7Uq4fN2hIIOi-pxp6>oyRaWcIW(iy{hZJbFk9^?mV zXbwVNXHIfD3*z{^i(e32i{5#Qe}n&nkZr!4tUrT}@0214IY79=^81bP0>~Tp@#o3i z;J}+DYH`U|t<%2`43s9}(KSkg(&q68122+5^#+wG{z|6928!gtpI5iX^2TeFY$1CQ zM@(5|1=io{U8s0MZ#bf)yuL3lpOQ@tuPJMM?pgH;;51`4agIzfe;1pp(0?EmV{nm| zX&^jRJ7=(zHC~X*#CPbONKkz}=}{|YXaQhUcX(228b6M?O#dX4;)zA19JgeDN&-jJ zqUe{2s+A6-W*IgY2PtzGlkHn_QAoLhU;C(}YaXMwc!O>DZG9-=YXy&fybEl|o*Ghd$4aSD zL>Tq|F6Ke=ki>}(aPoU-oYCrUl1yxH-6N+lSs5d-+EN(CITo?9*Kc6jptxMW#VL_# zuoTwKf+-uVZz~|{L8{fZ92-_DQoH+50xvRDSs$`u&lnKp zJ_)*QZ&e;M@IZJf&8x2wmNSwn>#8of%tprcAe4x{d<>HrK}o<~kND8t4G10Xcq}^~ zUV#)ycMvZfPUevB=TAN;_PD0lahD$b9Q+0xugY>28A4_a&|fn%Pycq{L9|=AI_8Zh zE&ju)jZ3`A*+FT(6xE+`NnRF|FE_Fbo2G~-=N?1$Yz^^cTOFUUjJCRjW*@(o8%@MiqNj{pmM3HdRH88;sFc22hNOQ zjk{j-+m5Up%tAn$5W-ekwQMJm{Nu9jR(_354asHk!I6|d$a1uRHz20THz?h}lA4L! z;BkF3U*Q9bS;-RZy%nR9X}>E_iIOKha2*;(u}&DqW=Cd9VDM5oZZgS&$*O8=7a z93cKBv#W$}U2o-GRqJUV^d|u|;!?-bkp!^PPWM?zA4ELPG&S3C$t@8!9zbg7oeD}@$q6kM{>NHf0Db<|MSuV8HLjYV{_6B>51GfAxn%B z@U*qzO-}!wTX^(2D8e3+{H&gZx#F+>>zlutu#Qga+81|4F31V%nD_Rd!PkhkxjVOi zPFeh|*e|3ZQt{E|U1@q}cba3)_Hwb05Ai8iIWVE+OY6XgVaBaGvJAJ#TVvmHZ)lPZ zc&T$02C5{?m@WW}fn4J1M>*K#aOPf@JdJ1KE;nCo^3%~*3sHR87&f*kQlq9*nn1$B z$MFx|I9)>7n8a@kI>zs0!CO}dj1oJyTIqQ|_Q`B>ML#dX+G6_D=_bk4unT@9vI9Qd z7a)bYU5Iy4KEC$RMA;m;Z>U`0z5)J|hv>KOvui=|szq1Rz~<^*u{2G$jJ}Gp0(iVW z?b2fwdS#Sw>R@r-a^ewSGIll`on|;Lh~UK-{;_ z{$Ek24~IKo4*jUwlwo?N!Q5!CK%-ReRlm=A)lHVM{#B$&Z;Rfu{x*depPO9Ok>&N(uapas zL6B%^!D0- zA`)wm26SagD`)$P%Yj*O-7z@bUAt!jSGFjm3k3F!`$%~opsSN*(P>=i7 z)zwi2*(?6^T{qpRq^mi}UtHr${y?q|{&alQ2(Nl7u~z{sF^@~+k?2P3)Y|ZV3b44r zgs97>0hfA);UPRtSeA|j;S*c_anEn3hV-|ytc08>XqZ~Ubu6Uvdcx$6rLEgmKn%+8 z6=a;WzCfI^^UA+uEWtT3eYE+dgLtW)VrzwHtDcj1e(s53Q4`~;apJ#Z@;Gaec9Rrg z`V_O8rmC3p-E-+P)mai%PohJHkQMVRhEuL%%j@s>1fld&fHcl6eigkZs|}+nBv7)+ zc7NWvXTNopM)QtmX}yB^oay>`Lfj7*f29UwiRvwQ8}#|2^sf<;aDd!YN07?+&qYHP z*V)i4xsh)eyNyNl4eO5YHzd;UKVPpa5*23srsrMFhm#QOE*e9EU5sqGW|uSuoluvM zCGRc3&YJ^lD4`xi4>8Vnj%~A1FSb}k0>x@SfIG`0D2ndW_aY4mRM?(&^lcL3n^|xu zQ7MmL|HRrSn}bGf#vP;16Y=^%$Z@5j%iC(sB%O^G1c?$6ObhfMHwOuWUhXpf%xBzm zElEO-|7;7(SW@SCs!`mJuiZmXh=Sl9j#-~65H{FjUZW?4`)KN}I}|CXY;eFvNcqq>x^4_u@je=vN}JS@G% zEepC7LXMoN@h}0xq<_NZ%{P#H+iw=RaGmNqfMdptnbk{U&O) zFMFm-6Y>_*EoQ7@2PDNEL1*XiVJ!qI43%_&M+4xi#*>frWrm_PWal$=jy#48!>iqsnH`+3Whh0VZ z9>xXbsR|QP_!r_+*JRi&|Hzx`jNiI(>vqDA&dB5=&p~`-BuvQxBgQbNKe`7*VW&G$E7{Ui0L1z}WvD^+4qy_>7QhpPH4m%Dbp~g7^}F28h=Hf*uv7{Y4Bq1hRY^R50M$UoUpfEMyOW9`A3T@ zOBBIWaKZ%Z7omp++15STO6pz!$jft;zt=e+hR)=C%T`x^QSA8TYM+A0fAl*^D-iR( zXzfM+fV-5)jlW?V@=RC+kx`v=hXZTAy18$Oom!b}b$lPtv=p`PW5{UT<1+{;CmUP~ z{QaHWEN1V>F*Od5SF(mg-$=C246ttt5a(zgZ(lAMdYKfq{&jqni=%}-)+2AOSx3Ox zsqAnm{%b+5k>2~=?WMnKvK&Z8jkVxXL+sU}v%SbbDm2v)^ZJ~20<^fv{e7akgwVvl zsg5Ng4j@tqf9K#<+k(guK?g2n4XXq5&GpeftNyui+p{T;NUD@kElG*bu;)C^P#Wvh z`yXCSF#UorYuDy3bFo8d16VZ14&-8DIBturQNFvu=T=CuBVrZ3&-Wh`T4VqXv7b@u z0M`QWMqi8K%I(?34F;FdQ*9HDtcRMh-}>%UuMf9NaLFOS0a7b13hjc9n##V3>nfSsHFNw^*Z4fs-EKaKA;98l{Vm8- zUFcfMhXS^iqA_n)%uUV4b4Q9Kvi;<3`wIIyiM?P9dX5+4{U&JWtuKUOie0>#(xYM2 z<2Fd@NM3XL1up5v2U^n!E;twl6xKKMuYM@27Y#ZG(}fbjY*xbL5&0`aYqeZeKmBb)i4e!si!A1|2959$W?y9t>q*oN&XPXaEBO@hr3E@P!1?knzij@kQyj)$+K0*VHXMh6S5&x+W)Gi_m(R}bFjGFx{Se5R@Iq}n%zgxw zSzx_|m$aI-?tr*o--{P~p~dO=S8$mwKhS>P{*sO;dsU+>+T%qFk7d zD-Ojhqh6K8H}<{AtAmiH?6ErN=%p%&*;*)F{#2LYHT5)kIl0YC$n#kE@O$UJL=(_y zq6Y;;T2!wKHjxt$G2EH@>b=P4&F4-ZLYyz+m-^8janBK-v1T^4#j07xM1Gbtr9kP` zbzPP)vmbw^ zTc}@&@OkUYDVUZErWr1EB5WwtN@tZ1i#yyvFvIMDeNnC&Or=cGru=Q+`HmOxH9@TON$ zAsivyAxw{#`~W;O#+nSw&Xq z4C?M)OnEwd&R?qPCXJ->!s72R`QtOLzMp_*>z5Y`mk|s)!O#LXD0RU~*NAv|wvd@d zz|6=vX9cavx@Bb0VG(lUxh9V(M7O0r8EUXUfBx2)J)p=SE1+1@U*E^|AYsTJ1aG5l zG0YcDB1o*{$qyhFW(bkb+x{i{X!Gn3TrfNUnSfr-vaez3c$ip#6-$VAf4~Mo(z$v4 zp&J#V{Im4Q_wtU0gJ8Qoqxt%|1#t<4QC0su8J!|trD=(_X>19KrV1I&*FLKdV-JpJ=EU&I9lp`}R z?QNp&$B~{K<2H~L4*nU9)j#|QXDmGSg+_$1$G@7J)t7rX3fSyKC(8jR5VN;MiD&ZnS6zOkG( z7q0!pj}+BkjK6@uB_q#%Ju8YX`Fy?PnkfOqr~>h%U9_; zpzLHNpG(M0P07e^k+GICp;Fi5d#_x=1MF!MmnXU7tAmDY0W0dWCh=_dx_-f*i6@x< zn*gECNhuO@l|zyT#0J%oZBT}QRl5|&F_@ww|JZ}za_{@B@i&(@bex3PKim;rCVL}Y zYR0&{W8$Y{TR*~k*uf1`5K)I~m?b+LKKZ1VYhnmY~pET>r5#P9%ACC{##zwMjF4E=)d)NfB z8@NRB3Hhru(AC54=7gw_M@b-p?x~1hgf&FlF?y(5Zl;dWlnp9m?vn9m=$!vsTuBW- zf@#eZAyYF=(prfZbj{s}NsY$VS49w5p~g2cT`0Nh**-5(cMPH^O|f>Bj4l1$8D3ve zrUFrtJ_TxpCX44zo&kM|PYate-H-ky6TjkcmmNI=+X2PGNy7Gj^PiDlLIC)qO{GPL zBK8!@6hP5QIc1eHKUL*=MuQJz%Jb&sdzX+n<$ag=$qzEAZWL-z{VErVU#bqoUgdJM zl&^-Ucuk@VV-x@GHMcr0C+K4Rgj=xR*|wg zOuJ-h<6Wmtm(2IW(-OY^J$K`11Xfh z=iGZ*@H|}MfujkBffPIo63lU|%9!QBhY^mqNMT`T(UmG#D}Nk)m}C^o5vvLcf^B;n zo1as+E70sr8=ZE=Mf2$I(vOWz#pgB*)9ai4CzjstWS_Yp z`}z)MdmJ?WA-=>V!;}f`NXP)AcVFJLo-d=poRNzuKV#xd=9}JAq!NMY(o~`c6m3

Ud7PCVZL^oinEs~=*XVdn>feYg?mVuAa{=n?kX>s z&)g_8QLr{XrsFp1*KDRduL%v$iHe}jLkFs)WH;|_7p}c$k_%j|`?!`ks<3J_bkJr> zwxgp=rUH@fT9h6m(&t~5zCaA9C$bAJ@C|Oo%#*^D6K(1{62k4|u4;S-Q(tD$wc)GD zvyYxwG2K7mqCSdm)?^Ckvk9G77HVrm+;w~qHc+hCZ}{_)Sk_cGYHOvU-PJe4SPP9} zpX#E!*MmtQo3V9cIddvRP-3Qi`0xCVyQS&$j%?WD&IJ^2aTVr+2s4Bt0}_8swOEw# zYqmjb6)!d(bx1r6JZ5$Bs*&I$`G>TFu%4iff62nLCEZy%LDcr`iED{!^Xy_E{LP_VP1t~z3R6XneP6^>=x(t}z0@I)!(4RB%PsXOV7n#PKahyh4>HO9)I^^@h=?A0T zpKk@psX&I&%PC;2F%^35xPzN-6lRDq!zOe}T&H?jVLgY=EL^xl{F^jax8Y2lle{Ln z>Z)nUPm_3p=%x#Q4C=8DSLMUnN0o;x02@EEYdyO`E}1un&P`m62UDc*oCPL=@;iMf zIqV})NDC6Y@___kCLGVeTYo!o>{3E4m>hDEsa3afdHEo9a#~wW*zdP##NQI-v9`X~ zQhW6j;T`%8TX|m{ncNjk>>HhZ8sBbY$1Su^>SV7|eicZah%=dA$}>99I37V zvQFjySgZcuQ2YP;IdosvlmI+o6>t(vuPg8j*UbgF%5Hn_w0zU3q^hRGBx0}JchhV0YC4iRM zXDfnP!zt!nQEZiC4xLE>y^B#YQ=gKE=LnR#b+z<(n!P2Lg>3wGGA@X633i{TR2$}ZXU z@7|?0R(Z|Dg2Zd{(IlHpJu(hQM+JpeQ^AI29k`*%H4R3S5C1zr(|luz$Wm&Cn~Ftr zgn+O^nO=Z!eE8$Z87>mgWcQ}awE2pz4CsAQl@x1342e)4QEBcU-9U`$63$3e-mA9& z3BjIM;kIydWOR%3&uNeY&G6ju`Aou4bs6#&Y?#&f)6rY48h3u?lU;YC6OX=gPL%q!>l=ZNXYNy=^s<;4E17@F`sr@Eln0uu_a96QuP<^B7OxY~WhS}o zC4=t<+=KT1B@?S?>AjCiT#hS@S8#DM=JP_G#{H5PRQat)cE-eGw^Ci0>{{VR7nSi= z-aoY|{4_fy_|;Kh>X|S zU1f6hq&Nqng^D%lsSH_vUo9SOmG^qF`SP=|o|x4~sst2Pk27=2K=?n$xXSuuWi^$~ z%$&?=H8nk^g5=1!4Sat@eaEPgB3o@On2x77U}fAsK-$P&oXsizp0bd*A6qVuc6BJN za+lpF&{Lte8;c>e6bW1$zu8Coc%D^R=(D=qC;Jm_j%4HeK=JNLp4ZczpI(a>AoK-u z;Z^NV%f&z_-7H3@rT%i{;Kj7+szRx0`tpKl?J%XQf0bsX%KCTXD#7*~C$sy((pJ6) zpt%u$op46;1kxP3CXFZ!ZWF2%;iU>lwrOD33}eg{_E^(`%SPh(B(+5Uw0PXzyUBd3 zE=SY1L^DO!U%<(GwX(+ZTi{A1?cY#$G=K5&5|PmxKN|_8WUFD}?F?>FMHW4HD5D#z z_MVaA{?J?Pfb>l<&XR)%N%KWQQBg}=Ftr~rCTv=yOsRDM4E(c?KciwU_17mGW>C)t zIZg9C=jD{1ayiSlQGG98^W-=XNH&4qLjT$trzXK zNgOqo&r|U~*a}5=*?BfUrumX5P)Y@Mi=yei&CQ}@AoP8n!e8JFT{-x zC`JUVMypq8P_AfX8VLxe>Z3n%-iUP<)w{RG{0f4)?Au_P=IgdP_S*ZqOm*9O6w@7Yi`o9H{P6s&YZ14 z_irq7lxwy7q4>vDX|4*dn0-IZE(R3v{bLs3PQ~U}`|29`yCJgsv}`gYAgZSwO)gV9 z?L&97BbV?I2bD<>t!GfBTS&4@FZ}%vT}ngj8AZmGY3QPo1%twfW4tV7NiO9NPcp6# zWx?i{`p8-|qvdW|26u{Ynd_$9)aY!kjoRG$J9%oC_yFXFodWi8w(F+|^Q2ds;cW%q ze{B!U^exzVuYa}GX%-kX&)Ujmdnzmxk{F4(FxlMSvGNV-%ZF#zI$7m_8D;%eAk6*-)KKIYOO!{$u$oj^{ z%kNcTy)cifTp#o&dCzSTn(q7+ku7Tcq?bS+XP_{D`p|W)21l~ASF{rf3YP|)7!>>R z+md%bR&TfW=j&Lfw~A)9nu{{*{%mf(C_@W%nisRPx_KBSHI4aBELBxE&sZV`>GmD5 zs?eE2{13v%Wwx9oa%@4Xud`dlqhZ$#mfB>{{#=W=%6IcIG*-)yAi{sP$Gh4~0_0wc42rs){GXF_MvJ~bTuYv088y_L5^)#fO99TE zAJyCp-N8@6cA;N6P1jleAe~Df5Y_VFEmFL)^`(nk%<3)|uRb0g<>p`%yra zSmYU~8W~$lBd21tIBQAbbi1g?q%EprC{g0D7j~#qXOViAS1cLzllJ4Q8z=9~Bk@L8 z2w%vwZ=7X(n2UEX|8Z5Hm+YT1+kVlHiLk!;{n*eFr=HfpnI5WOd-_ua5rb{9t2KPE z|LlNEp+21ZkCfn8(J$<%f7Q|=YWSq;O7HspX3*z@BPBr5$eoll31@j5ZWJCd84=El_cm3 z`0jzS&ZT}p(h|UwvB*M@jF2x2eeazM_5AF0uq0vv&m=kJM?ZISKKb-t9%3ILdkyB1 zA{)B7Xfe4)>)qgn)H25$C%^zMdRbI&}9)1kX!%@=`L{1D-su>TcZwF z5VL}e@XwSQ+nK~Iy2dVs9*nG;a7TDC8<6$g2MzDiNKNU6_1D(d*M9cVcC;7|A$wJJ zBmR4^$O#=G@*L99{q=Att+N8T`Fbs(`ABlbef(6#*esDIxZ<74G8>~<}lf-Jk`G_avVB>sPFwc+j$~CWk+^BW0Ngo zK2g*LcU*+*qZ#~u@b(H~j}InvcwI6Q)q%EkuKmI=%jIsM>>xvL59Ka%v(6XIve;tJ zM|?)vmWjD0{*;{wAW7^-tmjcG?K^KhjfZ8#8ojkZ#mLC7j!%u+h74Taz z+F8xn@*-0<+sAl#)gLT9qzVU1%mLA6s(b*3#){(HvJ=IguDG#-qC_8zmx(hv4n~;J zev1FG92?fF2$(&Bdl;okN@>11Rlh6P9{V(=uVDI#(JD3S{fJIMO|o&dyON7xrYWyq zw8*fkV8|qxHiX)(a+;9Vb)lIVEWRREpI*S>No(}zPisIg+a_CEPbbFH7bmNd%1--k zpKHmmgd_qCAU+LB4bXTgJt{3px7U5@6Yb`?V_$hzHC=og!Hs`Vtmq)I zqcM9Ie;x-XM9b`3t-KjEnuTx-1knE$95ZBn)XvJ8AUnmI_&xfd{L6-q=?-R->nt{P z?qu5+ZaSaBhgV^U?)$6kpF^m4ouD`AlclS{5k0%mBYkZ*{9$eBz*l|>R}rbuoN-@{ zU|z%QgPk|!`|gge1*v9~i9E5@lFDRce96Y_l+}7q+qqHxUbj}n-astp6;q2nKWy1|F?YmR}WtR}xA){NHUiW!0S|?^Sibu&b4 zQRgOg#6M~xa5{S|vK78u4*Es2p4Ahrstn}(G(szTphW?N9Z}93fJC*68Uj_>Gx8wk z7BP+2j1^iGQ+V(#f?(R|UpF#Hoj6F{&lj&f2gHSb`F(biGt5rk&4ZyDoE%Ucxx04$ zso2DBEz>i2HCPo=qS)NqD~kwprrFYHh!9GZU(9}W>-J)<3SFy@c}B>_wu$ptd=Pst z&UJhz`dSQXFV%7UVq#8mP&DYh%Rxpn#G&ToNBX##n1zI_58>hPN<<}Q>9)OS97UY$ zmGl$I?m2G5n#GQLMjtE=DZA-+n-hXNTz2~M21V! zosqIFlqSxcH5CX<`hjz0)31;*Z++7H`$KHum?P zDdU4Pb2Xfe+AhPuN zpO&jaRrYNLeV?Q4Mb7a_{)ieI2;AJ zHB$#6Lq=6Zz+*>D4z&imS3FoV37!456jW0iV8H&Xzy_>{oEDZ#Cq_OM>TC{%IIoED z>>7@{i}}*1Ua-_R)i#B{4&4wSx0BZ-x3plO1Q4iDVep**DmAS9C-)Ps8#+~3QrsJz zh+!SWUmEm$)l7Hy+xU_UjSLL*)jif^-&pB(zT`GLn9SETjt%5}m~sf&_MMs<_SVy` zsUIyHU7WlPuoHY`UV^4Klm2)rCFuwh^Cc%i4(JGcbTJaObT^5bBjRQ&*0utjk~Xsj zBl~e?E8X)o!V6R)-9uV09@?A^!oY4)OXC@r&7|9>%K$?WGBi6C%M2tZP4#5#P2sOU zF*TaJ89zu_yh)AKUH&4)<|!E(0*!ShPkYxopMnPcy<%yn^eyb9&4W7M@djH%k1J2} z0o3d(Vtlh7O^Kom@Lv|#cfrlA?9C1p7HdW^g?HY2R$w8JAt>P-D%X1~#aFHUHJsP; zsWn*w6QL)s4+m`v5gmi+6xjzOdzj7rtn$}mN&1~4UiaEHTE3~;r*Q1YF7Hnl-rQ7O zzkExS9U&^M0wFDgNQ2}TW4?w=cA3F$vy<;qAMaROD}T}RrdtTa1YnNVyRo^hqD*oD z3DGYz)VJ+>>>j;N5#0FICunsyU%+$*9puZmmWj6L?(=h>Q@^+u<~h>+!eai}_k1Jz zh++}VjJ0*Mt+mW!oAR9mz%v;dLh85x?Fn2e>tQzrY{pyc3+LU=*jwt^^R#p9OylIV z*w-|pe-(G@h>yx??&qQiF_%JHaC#ZVa%K^w80%Q+X*bDp{E%92+x?XLM|Y^z4utjn z+n{BHT#bpG$=Y(BRV`Qgq(01}r8eGYM{3I6T0AL^H{p+OrrcUiy6;c3;A&Ozw>g1( z;@r>Wo1^f`r0YJSK6&Gv-QL&ak%DrUY6H3Ylmt|}adDc_1?tYbvG^#DjCzy9ZRBEz z18D;O>tC{1v@-xJg5#AXxVjMq+boi9sa2NJ!MV>;73q`S%!zUt=_fRB76d*i%>av)-@~G<9oxhHBELzt8^j`QNuKZMGHK=zE(n@`;1=BW&s+*yc0_jdk?27 z7a$HXO(C%(+{`v=TDM^p@;4%oMhbwfq<1w?w#wF8XWxDA5Mrwux!bcx9FzZTf0H9_ z>CI#E$jt0!)G)U$<2$8L)pyM|H;(B@l!>?`aEyH&bqJ__-Ule#G6Jjo=jWU! zWN%{{5WGr;$#3eS%f~qyr*@A|>PsSOQ1V+|Z@{%_J$F)fF7S{gF1wZ_+lM`BRSzw` zx{%)sG<7)pDx{26SkN*S?8zW~#mw_PI(Mh^oJ#>`PM}*6BP}Z7;nYoW3hw=g!JVbW zes<)C!&>{e41t(jod4&tlLZ0&>~<)vx*+@OT)s~!4Jq$?(^t{G--K^O?&q{D*%`0G zsS;RsLqOxNCQelVe7vI`>UDDxLUrE9_gc}xH$&d2qLAYQZQk`V#bF@pTFgtTB-`@C z4b|Emqx=COORIyk*2ABdb+vUn^)7|F2+@3JNb}+uD&}Bzat>+c`|2MJMbP)+wUCr! z0J6-zgb}pxjX1#cU{5m(Nv-E*@)Z-iYU<(Id%hR?Y_=2m2kra&tyFf%Kd3{RZotJ{ z?h@YhvUAL|NJm%v0x94^NXK}Yc*#t-F&M?5O8aI=0%)MV7e>2UBWEsGuj<=vb+_k! zgXy_9HalMSE;av^O`-8p$qU5XZ&DL2`G9fJqHO#&wcS}&CMjJlToTur{DGnZf>Y9H zsvHH31!5Dj8$MLrm4ttopY<)>@r z-=5L8Gjz(@n|m*zG-odtqSu`+Wf|-C!pzt_dF#JlBkbjZuVIbxbp-#*XTzK!G&tG1 zdcG#-x?d$<^y%g5CEleTh2U7YU1r4DOo)FH4{tb_sm9r-0)yOuoqw|nIWxDKM0`%j zs|#UaH~pI7m-R#68(QjiFYS}sMoVSO7i*qpi9cCw^PM^FL_g}2-_eJ8c!hU3;KWlI z3TqPJs#7x;xogsI#X0Cc`(SGoSC@us@gYS3rFk^`=)Wo`VBjG!1du?5dcgDvDcCf# z>ZaC_4OeAn?9kB72OeTbr^E`M!8YCBsb`@^R4(*-4JW2x=+r8j$at?)2pL#J{vx1jfx^BLP&wf#8Oc!e%2@b@(iJM@CKK4HX+K^ggafW!}FX z6Zq-N^&|~{D}mTDqE3#bo1fGjF^Qj+7}ZV}ig_Q^<*5yD=P=>HW^9YsrHdO$G_re^ zQuxweUaZ#H1~N~mnrdg;kBVrKf1|kw}APAl*fwV3}T8_FZEIf zh9;*9rCXa24-5=h6odV8&E{&a=*FX*s443v&6bPTKt&}&m_dG!=a|ew=O*~I9XdtB zza%p%s|osQnycDo4a@=qKg?8F*=@3)arf025rEee9Z6(3T%TAH;}C!4N#@Mj ziyQ^bB;zaoqFwdbee2?C5cIA^K|8b>WF~;zCyz5SA+l0C{S8}%K5{Es&z}w8 zSz~-$GGKQw$M#?h!v;4JOJM$5nVqkNq~$(hE?q>5>#&dednYTK#b@N}pGRMUI#Aq| z*o4SEpDgi74leoU_3@*lvrKbbEs{mgG`Lbs$S8gwJ8-O4>8iH>nGy_B_kxtUJ~e|A zxU}b*OcrtJc({|sK^|KH3b`TsMk;C2TOit_3Qdik@J9ePzM&IMy0vr2gH94Imj zjyO0$?$a;}aUM9nE#-SRC*%REab!>O<3SGC2kAey?zYmh825dxaJ^C%4P2h=HdKFi zQ9JJ%r_?RSVWfeu^X)z>t+Y(12GOCk${u55J=29C*^(>6Uc-3AFS;(|huOm!48Y?U@yd#Lzjm3Na*gnIiC2RqN`! zS@JnEn`j*#fS}n4{sw4ko!SLvrM}yDJfM!!iYnK;X;%>rcKcWk{7pjJr8+P^FL`+m zNx3CYGt|72{}{$?0S*x{BwS|lrT{aLRza;VyzSlKZ3;v;6wP0hOb-nrovWwYkf~oQ ztNyje)H7ZAx*@be zIMR$47i(pank1E8G!lULD7}}H#c};^nv(v>pDM~Hj%t0~_cF|;2@XENwXdfP-P=sx zdE2#O-{ZM({+$W4@yD1U{r*3DJWIaa8qFi z;_lAl$z(TQx;snHr;Ie99J1YsB9*Rpp3v^T>#SSf5bLC0kLx3@uOe=&0I&>rV0lwe z+$zfL+;ld{excc@uFiEY{Q?;Y(=Z4>x;WCaZbOwHg3NOhg3b1M+r;K}LV}MO`fhFnvHT!`9Hs)He`vuY6^}u9|*WY2X zvX+h(*@c*;XeZJC-E;S7afVQ5rX}37M-B!rVT6HjNBiJ*s$)Wd=%k3y6Sbj&%hRMeN)i8D;R`r#!F(;!ez7Qy4W|8 z3wBkNN6uP;y1UKlL|Fr0#vU`=a{t)YA&t7B#C|2+PNL{>3CvRV=Z)u1*6-WO8?)Q< z@%L-J5HGj2Uk%8N_B?kN@&+&JO1;$cnYS`>+b%j^kRQdncwvoy2NiZmL_9W#DkmU} zaPh^CY%GskPavFA9u}47nx45zCeljeWYUcT+$GT>mqBrYW1)pKNje@Uc2M(U@b_J_ z6ssg4zho|mzx2pVZ$t!m1gzM?nTBBoxHUL+i{R`+r>Y0SMVaS3%yDn>kSBNe@46#R zYvdVyWiJ^(tw5~)C37uLn|*NU*y&>7pCFlgQ^@o2{>_gFBN4DWt-rxVDHw&n+#{_t z@(&D-5$lNqjD5hc0QO*NDUqj!UFg}z{kvL#3|GqaZ1L5S8Zb;F)$$s(LxXmO*tAQ@ zx4b8}SE=GBHXgS<$UH?h1Jhx^*PQ>>mb0ufWdOMebsQEj*%icv-={zO4nc-a@viF; z>f65e7?>X-Kg$lvhCP~#^-9nseZkmtXG@FaD?k~4PPs5ad*GVzXP(^-)UT}#JQsyD zDh5U1Z6_`gVja-pGNc7~RnNDOctyyst#)4!*-H2!Mi6Lkk`IT(fk>fB?^vJpys-zkVRVl;=o%3VDX5ZJ4_F zG^_u4Y*SwT*Dp)a)uTC|67Bm^6&4J1ygA6;VcOeeFi8*h)D;d(mchDce>%QLh*RyZ zCq`{@5%Eg6~mb;!WmBy@6k<5fMqh(uZiZ9oEdW+ z-p_Bj?8Vbig_WDh?eVvC1a@58F&{6-ll}!v(^k4yBO*J)2m$NzjtUWqmjriAuyGIb z|LN_#qMBN_J&vM+f+B2sRjJZMI#Qx2K}4hogpSgrBhqU?MWhG<8;~y2O9;|?BoIRH zf`lH56axuJ2&C-0_PFDmea3ydPv;>S-&o1h8gqSXeslik@82cfhM$e~?l5r3w{7|= zn{_TbrVc0dwnb`F0`u_O^KRiz4{Us2DR1tt(zN=n&RLTpu_hVoMauWa(xrX+nbzvH z9Vy7eH2F(otOle2+4^M5WmANX9pK)wbPFjhyu{bz^pU+Wqu@aW|9I6J)V^QaJ%-+au< z@0c<`4;N6yjrem|Lnfci`k%;nn|8-AElZo@bLUm^Kw( z^{y{Ck^@W=R!!FYGMcV%xFd__j{_bNA$yaHfKOhamMnfBc`F%O55>hP1V8d(>03Sr zc!7S|x1W`fHOBz831phxP}k<&UqjM62r019K+8>Vyh5Dy4#Cze87cDwknBZ>HnyGa z$r`SJ`Wj?tOcJ23>8#FF0Yrt^`#vP{*;*_haIFuj57Hk9&>y75GC>sitatPDy|cy0 zCKW!5$}{u}>%RTFb}`TK1$qsJ%hIz3yCQER6Ow0|6-Rid!0$_79m+e)VGdE&J7F{> zUCZQ^HLOUccjZS#ZqRM#LsKHTL+EhC-}G;nGoz74zwIpv-~a?&sYdWCLf<>3SMZfRoaj0Bej@PE1foUkDZsH0I9pkQA^!Nka7;qd6;!$})~9Uc1Ip4|8Q7Ux9WTrOrupTnNuq zxy06!tXF5~)}ulJ80Zo|**5Y;K~9)%bai>vP=I!2DDOPcYVS! zU+)$?lHE-~gjJ_U*Q?6ic>Ol=E0rjBQT6d}j(sLVj-mKwV;B9L5l~zDPGG?Ofgi)V zv=f?vN_JlX1L@9;A$xF`LHkt4(^a>|A=i!6nJq9tVbCwV!NV*&dskIemQZVQhU0gC zfD4jF-)Sx>IiBsd{i1RL29i=*7?VkRTBV07F7d~>DTx%AwFMb?!)kt(cM}|SbN2N# zK>ONHK+-cy=~eT)fgZjdk|RuPDs;NS(}Fr7f@!M}_pS3Bn;UaBX(}dP;jND7BlttS z?(4tkR^RbVc?WWRhp)ldAm6z;X5DMv(F9HR^&64D02w8q z6GDR#Qr&DbkTa@N9|fUV$&_<~AO(v|(ACDOlI!qAQ{n?NH1>k)S)qBBRpo>#fG>6R zC?Sl`R;AaRCIl#xzT<9OUAnKKe4z2f$%gCFiU2%alkr-L4dk2QTo^JRfFOLxv1;s^ z_59sV=2KVq0-w6AL&B}=ODBB1WuLee)cc6H7lF|t`_I8ok`j!?Xh()bO zguZ20yIb*`gj1>n?0dHiz!l=g1VyZXB@1FZJb*dncL<>)F9H7u_gy4SGM{PJ%W7oZ zsXj%zusNVb7$5>18y|Y9N`il-+YcSuz@=fix?J(!%Fe5HkD5O1$gY4R*Vl$uT5&^R zmUGGo@Q^Qezq5SLILfN%ZRwXv%knaP{Y6n(GnIiz4>4F^+G!awl6g{tp-8OY%XVmF znj;^Vb(ngc2EW*%Yj4T%>v_+hX2SDQQ@v z%ZCt?(6hjB6U!jNpzH}dw$MN%BTk0R?jhx%MDNKHKdc8xX zada%O$jI<>fH$#F;^~#b3Q}_Sode%Cx&aY zfbf4a*G!kkq}2Ew6~(Q4oRB_f^5;~8qngB^+=lJ4LgO!tW_44kpxnzE+N~b_$eW07 zvbv89m>kZQ z=_|(M#GTcFstkBDE?zr1QB^fgY5DmP0KM0XqdfRnFxa(KT3R7y`xHdfA%4kxxc_rN zv!ThTt^cBWO3c*NSBwX-$F*8PJ$9hAj-8TAEgj4+-d2Q7H`fQ682_1x5W#qnJK_d( z=C=Kq&G}f);bg*mPK6ADhmcl5A1DeHuBV+JYD$WCh;?&z7@IzkcRLra7N^|F=i#xa zU?>+u+yp#~N#ZjIGN+|}STPza#2c>kp*WRk3zmIAMvlMOXlj43C_N5F1ma9r?^$Yl zT8dJAgl`mP%!c&u{z&>xwfso9A}&>hli20zup6TkxCA(b--{cPJe-)h72H{HqBTt6 zoACaP;ITvp6uWcsmUI-|6fHqMhLI{XL664Y^rYVWi_Ua8Jz@^nH@m6H;Vf%HKlrej zi8A50R$(e=j9|f)GB>Qgtt{h_hwtSDd3`dzaL!6Jh0)>O6_04@-(B=vdj{H#@F&y~ zSh!B%{E){N%H0ki6&c_8W?mRn{c~~fPFH6!Npg6>9h06>z}u2=JQ?HSmv{rd^g@l> z?0gdV4BUa5ig>mNrCnl(YYdZtc0(C)-Vn0(0c`~4(_y>OukvI50m>wOC@DcN zyZM`v=vYzO)be_heVCSq3gJ?T>m3V;SIam}n!fXKodD>`8@;dZW-#0^9InWsFka*$ zV6O~F0iytfji#i#1bi4CVR_PY3N&i|tyrgzrIvKD4G`v6RN^&6eS9yM%rrWT&yUE7 zscxRraQ-^FxYK%#jlppeAr~NYc~=V{flY+#lrC+DZf5i`eX9B5I!v}KI$Z1L!v-Z8 z)U=voUK&KUL!xO5z^I5Kz>!goF5Ar)N>zt)2Rn-@Pjqo=+FjuBP~*D>+=-+)MpR2r zDo7uHoG6@OcA9jv1YCqrIT)Nl6xA6vQ3WU+Q)iB{?e=?{04C1i8te4P6$1|&(#DvC32NdBr-o!Xz7vt%L!V)1PK|tBTsxu6 znqv2z%o#a)CmC&+kR4eVR0C4;N%v`RClE#3_xCAmt8iLcmWI{0%QL# zDFRcDecSuRN9U(mCZI6St)(kd{uecdtWEW9C*O{9p^W;H%=6gk{xvr+=U|C-T{rW8 z36M?h(@(S7e~e@uS4w^ZIo*KAIyKN{CO{1ue^gp6zdKN}4znz70K4$G{$vFwrqM0P zF;}1VnwP>Z3?aKW-ZI42-xp>f&jg9&EqR%vsn@Qh<~$XN`xDE%la za%+=SY+qGiQdLq_oF6Qh9_szUwL)hvf7snXy{JE6jL#109qh7fJ{}%^3^7-Zv|+QL zA|!_IJ``yV5x#vi-}>i4kYsN3Zv^F!=5KYuv1l!IQV;x|Sei(hjccfR!EN*G!SBUM zJ6Owj&Zit^Qeg0oPuA(W2_g2L>%O#*>O9?5ttuC&-};g)D2AxA{zC{B$oBV${dowrg~mZL zBnFZy@S|ju@yObU_5Ga32J$nbVVSS7!GHGF4RQ@?2Z;~bLx;6Yf@_3;5PF?Kx<$LV zP3XZA-w=d*wAAZoG~BSogm*dooaSFqnjiWy%X;|&P`wZ8hJ55(jJjHqEzq~xc1L2 z^p$NjU5M1+Nb^u*M#AD8@B=FRA|ThGM38h=#{`03x?kO*N!00+Z#DK1!=m}vE^M3w z6nRzhwLMbu>5j|W`KsR=GB2pl72EGU4o7J%9DvSB5=geN$DH6Kfyoyy-Rg#pa3?Me zzYE@&s4YeWmm?OUoC-*Pux#6L=yHjzRJY0e>I}^n1=iUAUFYPVCA0@OsZF|?-^3A9 zpQ>B%(`@fRbS!}yNM}LJN-)ki`s3@pk=6nMh2KQjrBv;IZyke#e zneuq+n8?bCZ*m34fgy#xZWnuuHk>L?>YEOxtEi+xg4Cnm zWbx$2l;l!!2K-Se%eAMz?_{fNsc%gpp=cj`i=8b(h;-qY!a)~)4R0QO0fuFYxYs4= zanG|u!Fjd#Pze|MN*m4ck^W-gIl8_FUPY3z!%j~R@^gSf!w~5Nn=wIgTNAj_xBKh4 zPj$zwlAQ@%tvt!xH>#fC1K+#~sZdVz-tO+LsDgTi7|;uQ0e3;kNiOplz`*!H(^w5D ziFy?Ah$(>ks*d8^ZS)RT#tNHuxxH%wb_ve*43?XZW9_GWg3PXHfGbi~i=qc|D%(aY zXGX%=u_fddS$NikZHllsl_#(4wU;USvF5u0Qlay@Mo-W$A`xaLC<4ti1@ zk;h(mx`(pE&wd+wFDQ@TY}4&Q@EEG?_$Z>MuG`xT1#G|t@Onh}_`^s!_mqOA`;Irr z!a$}qUX@EaCfnPFvyc?vz~jc?t|iej@ys&QV5(HiBeB2SkP&4}&lPwHWh~H?>(I#$ zinpMcSv;@(;Topb$XzD>LTnP!Ze`z*-c~>I7aas&W!)YM`08o-PlHx+xXeM917z&6 z&|Vm5buZ4xQ?xw?oMNKS+7dyYk75PO6-(P>eDjRhXCgs?JAMjk(xK)hBXNG%Hi|^u zvobUQ=*laab};edOexjnKeMJL)b;7y23|#MW`UZ3aAh(i-OR*YjHn2Ang|%4)Ib+XTl6^-tl-bA@#9!aLF!_-WUD<3VooS0Xox>X3i6Ji42Zj_jdbb0Y>o~%v^ zBAStuib!0@st9UwQQRfH5Kw`yuSlw5>X7iW%cFb4+A+qtl9f?KZIu}BdXMTps~Id` zj9oi78^U;=P^Cl)l;W`d!zwuOL2jAyhS-r;bKv~mQuUsdEAalVru$n&@ZbJ0_E$&v ze9Lxe7Zho>a2gHoHx;Id^V5{R>d^T@gwEpzs3Vjub)Q6)1C(jbO2-Z;sJxdpa?zgp zY4)@6T|wI*e3eC$PpW!Y^e%r35LsW1Sl+d*0`4NoWG^9L7q&~r3Yax)nHj%Uc&(os zo{(-wc_H1APdD(BTdBHHKj9fnI*8Bty!NA>%z) z=RSpxDHdA5!Q*qwR4T*1Vkd^$Q{KHK;S@*Cb3r&Lt(X*sV>Q!JZHz(Hg!ZybFq+R~h)4 z9TSZ%fMRHT)+86IY{t%xb&N^b$FH@*xzPgxctayisp9IaaC;wqCb0pnX9Dybwp-x) z%qg=B^cCpGemb-98Iu@2s|%({Pg!4hRi&gIZK=}_5Jl)bj7 z`p=>uiC22fcK++}@t^Ss4{{`C38y#(J11Yad>O3Te~qXrNO_upvihN`a{A;7*4gNl zU;ZbwK&89H*_)n)1%X>dUULIdW2$2ZLV%IM&8vK75COS6Ps=}Fkm+zOG*>jEi#q$8 z0sixD^M@x7+*!)mPbJP&@b(s(nVEs<=(&Mp^Yevj_r&4K!kmn20jE>t|3}IUNSi@_ G&HWE+c~2Ms literal 0 HcmV?d00001 diff --git a/4-Classification/3-Classifiers-2/images/svm.png b/4-Classification/3-Classifiers-2/images/svm.png new file mode 100644 index 0000000000000000000000000000000000000000..f4f0424619e635206c5239cc43b6d94b5a2f6dd7 GIT binary patch literal 38653 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 H9FYDWq0kBX literal 0 HcmV?d00001 diff --git a/4-Classification/3-Classifiers-2/solution/R/README.md b/4-Classification/3-Classifiers-2/solution/R/README.md deleted file mode 100644 index f59c07cc0..000000000 --- a/4-Classification/3-Classifiers-2/solution/R/README.md +++ /dev/null @@ -1 +0,0 @@ -this is a temporary placeholder \ No newline at end of file diff --git a/4-Classification/3-Classifiers-2/solution/R/lesson_12-R.ipynb b/4-Classification/3-Classifiers-2/solution/R/lesson_12-R.ipynb new file mode 100644 index 000000000..d1a6fbf2b --- /dev/null +++ b/4-Classification/3-Classifiers-2/solution/R/lesson_12-R.ipynb @@ -0,0 +1,645 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "lesson_12-R.ipynb", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "name": "ir", + "display_name": "R" + }, + "language_info": { + "name": "R" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "jsFutf_ygqSx" + }, + "source": [ + "# Build a classification model: Delicious Asian and Indian Cuisines" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HD54bEefgtNO" + }, + "source": [ + "## Cuisine classifiers 2\n", + "\n", + "In this second classification lesson, we will explore `more ways` to classify categorical data. We will also learn about the ramifications for choosing one classifier over the other.\n", + "\n", + "### [**Pre-lecture quiz**](https://white-water-09ec41f0f.azurestaticapps.net/quiz/23/)\n", + "\n", + "### **Prerequisite**\n", + "\n", + "We assume that you have completed the previous lessons since we will be carrying forward some concepts we learned before.\n", + "\n", + "For this lesson, we'll require the following packages:\n", + "\n", + "- `tidyverse`: The [tidyverse](https://www.tidyverse.org/) is a [collection of R packages](https://www.tidyverse.org/packages) designed to makes data science faster, easier and more fun!\n", + "\n", + "- `tidymodels`: The [tidymodels](https://www.tidymodels.org/) framework is a [collection of packages](https://www.tidymodels.org/packages/) for modeling and machine learning.\n", + "\n", + "- `themis`: The [themis package](https://themis.tidymodels.org/) provides Extra Recipes Steps for Dealing with Unbalanced Data.\n", + "\n", + "You can have them installed as:\n", + "\n", + "`install.packages(c(\"tidyverse\", \"tidymodels\", \"kernlab\", \"themis\", \"ranger\", \"xgboost\", \"kknn\"))`\n", + "\n", + "Alternatively, the script below checks whether you have the packages required to complete this module and installs them for you in case they are missing." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "vZ57IuUxgyQt" + }, + "source": [ + "suppressWarnings(if (!require(\"pacman\"))install.packages(\"pacman\"))\n", + "\n", + "pacman::p_load(tidyverse, tidymodels, themis, kernlab, ranger, xgboost, kknn)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "z22M-pj4g07x" + }, + "source": [ + "Now, let's hit the ground running!\n", + "\n", + "## **1. A classification map**\n", + "\n", + "In our [previous lesson](https://github.com/microsoft/ML-For-Beginners/tree/main/4-Classification/2-Classifiers-1), we tried to address the question: how do we choose between multiple models? To a great extent, it depends on the characteristics of the data and the type of problem we want to solve (for instance classification or regression?)\n", + "\n", + "Previously, we learned about the various options you have when classifying data using Microsoft's cheat sheet. Python's Machine Learning framework, Scikit-learn, offers a similar but more granular cheat sheet that can further help narrow down your estimators (another term for classifiers):\n", + "\n", + "

\n", + " \n", + "

\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "u1i3xRIVg7vG" + }, + "source": [ + "> Tip: [visit this map online](https://scikit-learn.org/stable/tutorial/machine_learning_map/) and click along the path to read documentation.\n", + ">\n", + "> The [Tidymodels reference site](https://www.tidymodels.org/find/parsnip/#models) also provides an excellent documentation about different types of model.\n", + "\n", + "### **The plan** πŸ—ΊοΈ\n", + "\n", + "This map is very helpful once you have a clear grasp of your data, as you can 'walk' along its paths to a decision:\n", + "\n", + "- We have \\>50 samples\n", + "\n", + "- We want to predict a category\n", + "\n", + "- We have labeled data\n", + "\n", + "- We have fewer than 100K samples\n", + "\n", + "- ✨ We can choose a Linear SVC\n", + "\n", + "- If that doesn't work, since we have numeric data\n", + "\n", + " - We can try a ✨ KNeighbors Classifier\n", + "\n", + " - If that doesn't work, try ✨ SVC and ✨ Ensemble Classifiers\n", + "\n", + "This is a very helpful trail to follow. Now, let's get right into it using the [tidymodels](https://www.tidymodels.org/) modelling framework: a consistent and flexible collection of R packages developed to encourage good statistical practice 😊.\n", + "\n", + "## 2. Split the data and deal with imbalanced data set.\n", + "\n", + "From our previous lessons, we learnt that there were a set of common ingredients across our cuisines. Also, there was quite an unequal distribution in the number of cuisines.\n", + "\n", + "We'll deal with these by\n", + "\n", + "- Dropping the most common ingredients that create confusion between distinct cuisines, using `dplyr::select()`.\n", + "\n", + "- Use a `recipe` that preprocesses the data to get it ready for modelling by applying an `over-sampling` algorithm.\n", + "\n", + "We already looked at the above in the previous lesson so this should be a breeze πŸ₯³!" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "6tj_rN00hClA" + }, + "source": [ + "# Load the core Tidyverse and Tidymodels packages\n", + "library(tidyverse)\n", + "library(tidymodels)\n", + "\n", + "# Load the original cuisines data\n", + "df <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/4-Classification/data/cuisines.csv\")\n", + "\n", + "# Drop id column, rice, garlic and ginger from our original data set\n", + "df_select <- df %>% \n", + " select(-c(1, rice, garlic, ginger)) %>%\n", + " # Encode cuisine column as categorical\n", + " mutate(cuisine = factor(cuisine))\n", + "\n", + "\n", + "# Create data split specification\n", + "set.seed(2056)\n", + "cuisines_split <- initial_split(data = df_select,\n", + " strata = cuisine,\n", + " prop = 0.7)\n", + "\n", + "# Extract the data in each split\n", + "cuisines_train <- training(cuisines_split)\n", + "cuisines_test <- testing(cuisines_split)\n", + "\n", + "# Display distribution of cuisines in the training set\n", + "cuisines_train %>% \n", + " count(cuisine) %>% \n", + " arrange(desc(n))" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zFin5yw3hHb1" + }, + "source": [ + "### Deal with imbalanced data\n", + "\n", + "Imbalanced data often has negative effects on the model performance. Many models perform best when the number of observations is equal and, thus, tend to struggle with unbalanced data.\n", + "\n", + "There are majorly two ways of dealing with imbalanced data sets:\n", + "\n", + "- adding observations to the minority class: `Over-sampling` e.g using a SMOTE algorithm which synthetically generates new examples of the minority class using nearest neighbors of these cases.\n", + "\n", + "- removing observations from majority class: `Under-sampling`\n", + "\n", + "In our previous lesson, we demonstrated how to deal with imbalanced data sets using a `recipe`. A recipe can be thought of as a blueprint that describes what steps should be applied to a data set in order to get it ready for data analysis. In our case, we want to have an equal distribution in the number of our cuisines for our `training set`. Let's get right into it." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "cRzTnHolhLWd" + }, + "source": [ + "# Load themis package for dealing with imbalanced data\n", + "library(themis)\n", + "\n", + "# Create a recipe for preprocessing training data\n", + "cuisines_recipe <- recipe(cuisine ~ ., data = cuisines_train) %>%\n", + " step_smote(cuisine) \n", + "\n", + "# Print recipe\n", + "cuisines_recipe" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KxOQ2ORhhO81" + }, + "source": [ + "Now we are ready to train models πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»!\n", + "\n", + "## 3. Beyond multinomial regression models\n", + "\n", + "In our previous lesson, we looked at multinomial regression models. Let's explore some more flexible models for classification.\n", + "\n", + "### Support Vector Machines.\n", + "\n", + "In the context of classification, `Support Vector Machines` is a machine learning technique that tries to find a *hyperplane* that \"best\" separates the classes. Let's look at a simple example:\n", + "\n", + "

\n", + " \n", + "

https://commons.wikimedia.org/w/index.php?curid=22877598
\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "C4Wsd0vZhXYu" + }, + "source": [ + "H1~ does not separate the classes. H2~ does, but only with a small margin. H3~ separates them with the maximal margin.\n", + "\n", + "#### Linear Support Vector Classifier\n", + "\n", + "Support-Vector clustering (SVC) is a child of the Support-Vector machines family of ML techniques. In SVC, the hyperplane is chosen to correctly separate `most` of the training observations, but `may misclassify` a few observations. By allowing some points to be on the wrong side, the SVM becomes more robust to outliers hence better generalization to new data. The parameter that regulates this violation is referred to as `cost` which has a default value of 1 (see `help(\"svm_poly\")`).\n", + "\n", + "Let's create a linear SVC by setting `degree = 1` in a polynomial SVM model." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "vJpp6nuChlBz" + }, + "source": [ + "# Make a linear SVC specification\n", + "svc_linear_spec <- svm_poly(degree = 1) %>% \n", + " set_engine(\"kernlab\") %>% \n", + " set_mode(\"classification\")\n", + "\n", + "# Bundle specification and recipe into a worklow\n", + "svc_linear_wf <- workflow() %>% \n", + " add_recipe(cuisines_recipe) %>% \n", + " add_model(svc_linear_spec)\n", + "\n", + "# Print out workflow\n", + "svc_linear_wf" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rDs8cWNkhoqu" + }, + "source": [ + "Now that we have captured the preprocessing steps and model specification into a *workflow*, we can go ahead and train the linear SVC and evaluate results while at it. For performance metrics, let's create a metric set that will evaluate: `accuracy`, `sensitivity`, `Positive Predicted Value` and `F Measure`\n", + "\n", + "> `augment()` will add column(s) for predictions to the given data." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "81wiqcwuhrnq" + }, + "source": [ + "# Train a linear SVC model\n", + "svc_linear_fit <- svc_linear_wf %>% \n", + " fit(data = cuisines_train)\n", + "\n", + "# Create a metric set\n", + "eval_metrics <- metric_set(ppv, sens, accuracy, f_meas)\n", + "\n", + "\n", + "# Make predictions and Evaluate model performance\n", + "svc_linear_fit %>% \n", + " augment(new_data = cuisines_test) %>% \n", + " eval_metrics(truth = cuisine, estimate = .pred_class)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0UFQvHf-huo3" + }, + "source": [ + "#### Support Vector Machine\n", + "\n", + "The support vector machine (SVM) is an extension of the support vector classifier in order to accommodate a non-linear boundary between the classes. In essence, SVMs use the *kernel trick* to enlarge the feature space to adapt to nonlinear relationships between classes. One popular and extremely flexible kernel function used by SVMs is the *Radial basis function.* Let's see how it will perform on our data." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "-KX4S8mzhzmp" + }, + "source": [ + "set.seed(2056)\n", + "\n", + "# Make an RBF SVM specification\n", + "svm_rbf_spec <- svm_rbf() %>% \n", + " set_engine(\"kernlab\") %>% \n", + " set_mode(\"classification\")\n", + "\n", + "# Bundle specification and recipe into a worklow\n", + "svm_rbf_wf <- workflow() %>% \n", + " add_recipe(cuisines_recipe) %>% \n", + " add_model(svm_rbf_spec)\n", + "\n", + "\n", + "# Train an RBF model\n", + "svm_rbf_fit <- svm_rbf_wf %>% \n", + " fit(data = cuisines_train)\n", + "\n", + "\n", + "# Make predictions and Evaluate model performance\n", + "svm_rbf_fit %>% \n", + " augment(new_data = cuisines_test) %>% \n", + " eval_metrics(truth = cuisine, estimate = .pred_class)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QBFSa7WSh4HQ" + }, + "source": [ + "Much better 🀩!\n", + "\n", + "> βœ… Please see:\n", + ">\n", + "> - [*Support Vector Machines*](https://bradleyboehmke.github.io/HOML/svm.html), Hands-on Machine Learning with R\n", + ">\n", + "> - [*Support Vector Machines*](https://www.statlearning.com/), An Introduction to Statistical Learning with Applications in R\n", + ">\n", + "> for further reading.\n", + "\n", + "### Nearest Neighbor classifiers\n", + "\n", + "*K*-nearest neighbor (KNN) is an algorithm in which each observation is predicted based on its *similarity* to other observations.\n", + "\n", + "Let's fit one to our data." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "k4BxxBcdh9Ka" + }, + "source": [ + "# Make a KNN specification\n", + "knn_spec <- nearest_neighbor() %>% \n", + " set_engine(\"kknn\") %>% \n", + " set_mode(\"classification\")\n", + "\n", + "# Bundle recipe and model specification into a workflow\n", + "knn_wf <- workflow() %>% \n", + " add_recipe(cuisines_recipe) %>% \n", + " add_model(knn_spec)\n", + "\n", + "# Train a boosted tree model\n", + "knn_wf_fit <- knn_wf %>% \n", + " fit(data = cuisines_train)\n", + "\n", + "\n", + "# Make predictions and Evaluate model performance\n", + "knn_wf_fit %>% \n", + " augment(new_data = cuisines_test) %>% \n", + " eval_metrics(truth = cuisine, estimate = .pred_class)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HaegQseriAcj" + }, + "source": [ + "It appears that this model is not performing that well. Probably changing the model's arguments (see `help(\"nearest_neighbor\")` will improve model performance. Be sure to try it out.\n", + "\n", + "> βœ… Please see:\n", + ">\n", + "> - [Hands-on Machine Learning with R](https://bradleyboehmke.github.io/HOML/)\n", + ">\n", + "> - [An Introduction to Statistical Learning with Applications in R](https://www.statlearning.com/)\n", + ">\n", + "> to learn more about *K*-Nearest Neighbors classifiers.\n", + "\n", + "### Ensemble classifiers\n", + "\n", + "Ensemble algorithms work by combining multiple base estimators to produce an optimal model either by:\n", + "\n", + "`bagging`: applying an *averaging function* to a collection of base models\n", + "\n", + "`boosting`: building a sequence of models that build on one another to improve predictive performance.\n", + "\n", + "Let's start by trying out a Random Forest model, which builds a large collection of decision trees then applies an averaging function to for a better overall model." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "49DPoVs6iK1M" + }, + "source": [ + "# Make a random forest specification\n", + "rf_spec <- rand_forest() %>% \n", + " set_engine(\"ranger\") %>% \n", + " set_mode(\"classification\")\n", + "\n", + "# Bundle recipe and model specification into a workflow\n", + "rf_wf <- workflow() %>% \n", + " add_recipe(cuisines_recipe) %>% \n", + " add_model(rf_spec)\n", + "\n", + "# Train a random forest model\n", + "rf_wf_fit <- rf_wf %>% \n", + " fit(data = cuisines_train)\n", + "\n", + "\n", + "# Make predictions and Evaluate model performance\n", + "rf_wf_fit %>% \n", + " augment(new_data = cuisines_test) %>% \n", + " eval_metrics(truth = cuisine, estimate = .pred_class)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RGVYwC_aiUWc" + }, + "source": [ + "Good job πŸ‘!\n", + "\n", + "Let's also experiment with a Boosted Tree model.\n", + "\n", + "Boosted Tree defines an ensemble method that creates a series of sequential decision trees where each tree depends on the results of previous trees in an attempt to incrementally reduce the error. It focuses on the weights of incorrectly classified items and adjusts the fit for the next classifier to correct.\n", + "\n", + "There are different ways to fit this model (see `help(\"boost_tree\")`). In this example, we'll fit Boosted trees via `xgboost` engine." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Py1YWo-micWs" + }, + "source": [ + "# Make a boosted tree specification\n", + "boost_spec <- boost_tree(trees = 200) %>% \n", + " set_engine(\"xgboost\") %>% \n", + " set_mode(\"classification\")\n", + "\n", + "# Bundle recipe and model specification into a workflow\n", + "boost_wf <- workflow() %>% \n", + " add_recipe(cuisines_recipe) %>% \n", + " add_model(boost_spec)\n", + "\n", + "# Train a boosted tree model\n", + "boost_wf_fit <- boost_wf %>% \n", + " fit(data = cuisines_train)\n", + "\n", + "\n", + "# Make predictions and Evaluate model performance\n", + "boost_wf_fit %>% \n", + " augment(new_data = cuisines_test) %>% \n", + " eval_metrics(truth = cuisine, estimate = .pred_class)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zNQnbuejigZM" + }, + "source": [ + "\n", + "> βœ… Please see:\n", + ">\n", + "> - [Machine Learning for Social Scientists](https://cimentadaj.github.io/ml_socsci/tree-based-methods.html#random-forests)\n", + ">\n", + "> - [Hands-on Machine Learning with R](https://bradleyboehmke.github.io/HOML/)\n", + ">\n", + "> - [An Introduction to Statistical Learning with Applications in R](https://www.statlearning.com/)\n", + ">\n", + "> - - Explores the AdaBoost model which is a good alternative to xgboost.\n", + ">\n", + "> to learn more about Ensemble classifiers.\n", + "\n", + "## 4. Extra - comparing multiple models\n", + "\n", + "We have fitted quite a number of models in this lab πŸ™Œ. It can become tedious or onerous to create a lot of workflows from different sets of preprocessors and/or model specifications and then calculate the performance metrics one by one.\n", + "\n", + "Let's see if we can address this by creating a function that fits a list of workflows on the training set then returns the performance metrics based on the test set. We'll get to use `map()` and `map_dfr()` from the [purrr](https://purrr.tidyverse.org/) package to apply functions to each element in list.\n", + "\n", + "> [`map()`](https://purrr.tidyverse.org/reference/map.html) functions allow you to replace many for loops with code that is both more succinct and easier to read. The best place to learn about the [`map()`](https://purrr.tidyverse.org/reference/map.html) functions is the [iteration chapter](http://r4ds.had.co.nz/iteration.html) in R for data science." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Qzb7LyZnimd2" + }, + "source": [ + "set.seed(2056)\n", + "\n", + "# Create a metric set\n", + "eval_metrics <- metric_set(ppv, sens, accuracy, f_meas)\n", + "\n", + "# Define a function that returns performance metrics\n", + "compare_models <- function(workflow_list, train_set, test_set){\n", + " \n", + " suppressWarnings(\n", + " # Fit each model to the train_set\n", + " map(workflow_list, fit, data = train_set) %>% \n", + " # Make predictions on the test set\n", + " map_dfr(augment, new_data = test_set, .id = \"model\") %>%\n", + " # Select desired columns\n", + " select(model, cuisine, .pred_class) %>% \n", + " # Evaluate model performance\n", + " group_by(model) %>% \n", + " eval_metrics(truth = cuisine, estimate = .pred_class) %>% \n", + " ungroup()\n", + " )\n", + " \n", + "} # End of function" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Fwa712sNisDA" + }, + "source": [ + "Let's call our function and compare the accuracy across the models." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "3i4VJOi2iu-a" + }, + "source": [ + "# Make a list of workflows\n", + "workflow_list <- list(\n", + " \"svc\" = svc_linear_wf,\n", + " \"svm\" = svm_rbf_wf,\n", + " \"knn\" = knn_wf,\n", + " \"random_forest\" = rf_wf,\n", + " \"xgboost\" = boost_wf)\n", + "\n", + "# Call the function\n", + "set.seed(2056)\n", + "perf_metrics <- compare_models(workflow_list = workflow_list, train_set = cuisines_train, test_set = cuisines_test)\n", + "\n", + "# Print out performance metrics\n", + "perf_metrics %>% \n", + " group_by(.metric) %>% \n", + " arrange(desc(.estimate)) %>% \n", + " slice_head(n=7)\n", + "\n", + "# Compare accuracy\n", + "perf_metrics %>% \n", + " filter(.metric == \"accuracy\") %>% \n", + " arrange(desc(.estimate))\n" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KuWK_lEli4nW" + }, + "source": [ + "\n", + "[**workflowset**](https://workflowsets.tidymodels.org/) package allow users to create and easily fit a large number of models but is mostly designed to work with resampling techniques such as `cross-validation`, an approach we are yet to cover.\n", + "\n", + "## **πŸš€Challenge**\n", + "\n", + "Each of these techniques has a large number of parameters that you can tweak for instance `cost` in SVMs, `neighbors` in KNN, `mtry` (Randomly Selected Predictors) in Random Forest.\n", + "\n", + "Research each one's default parameters and think about what tweaking these parameters would mean for the model's quality.\n", + "\n", + "To find out more about a particular model and its parameters, use: `help(\"model\")` e.g `help(\"rand_forest\")`\n", + "\n", + "> In practice, we usually *estimate* the *best values* for these by training many models on a `simulated data set` and measuring how well all these models perform. This process is called **tuning**.\n", + "\n", + "### [**Post-lecture quiz**](https://white-water-09ec41f0f.azurestaticapps.net/quiz/24/)\n", + "\n", + "### **Review & Self Study**\n", + "\n", + "There's a lot of jargon in these lessons, so take a minute to review [this list](https://docs.microsoft.com/dotnet/machine-learning/resources/glossary?WT.mc_id=academic-15963-cxa) of useful terminology!\n", + "\n", + "#### THANK YOU TO:\n", + "\n", + "[`Allison Horst`](https://twitter.com/allison_horst/) for creating the amazing illustrations that make R more welcoming and engaging. Find more illustrations at her [gallery](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM).\n", + "\n", + "[Cassie Breviu](https://www.twitter.com/cassieview) and [Jen Looper](https://www.twitter.com/jenlooper) for creating the original Python version of this module β™₯️\n", + "\n", + "Happy Learning,\n", + "\n", + "[Eric](https://twitter.com/ericntay), Gold Microsoft Learn Student Ambassador.\n", + "\n", + "

\n", + " \n", + "

Artwork by @allison_horst
\n" + ] + } + ] +} \ No newline at end of file diff --git a/4-Classification/3-Classifiers-2/solution/R/lesson_12.Rmd b/4-Classification/3-Classifiers-2/solution/R/lesson_12.Rmd new file mode 100644 index 000000000..3a6f6ba43 --- /dev/null +++ b/4-Classification/3-Classifiers-2/solution/R/lesson_12.Rmd @@ -0,0 +1,452 @@ +--- +title: 'Build a classification model: Delicious Asian and Indian Cuisines' +output: + html_document: + df_print: paged + theme: flatly + highlight: breezedark + toc: yes + toc_float: yes + code_download: yes +--- + +## Cuisine classifiers 2 + +In this second classification lesson, we will explore `more ways` to classify categorical data. We will also learn about the ramifications for choosing one classifier over the other. + +### [**Pre-lecture quiz**](https://white-water-09ec41f0f.azurestaticapps.net/quiz/23/) + +### **Prerequisite** + +We assume that you have completed the previous lessons since we will be carrying forward some concepts we learned before. + +For this lesson, we'll require the following packages: + +- `tidyverse`: The [tidyverse](https://www.tidyverse.org/) is a [collection of R packages](https://www.tidyverse.org/packages) designed to makes data science faster, easier and more fun! + +- `tidymodels`: The [tidymodels](https://www.tidymodels.org/) framework is a [collection of packages](https://www.tidymodels.org/packages/) for modeling and machine learning. + +- `themis`: The [themis package](https://themis.tidymodels.org/) provides Extra Recipes Steps for Dealing with Unbalanced Data. + +You can have them installed as: + +`install.packages(c("tidyverse", "tidymodels", "kernlab", "themis", "ranger", "xgboost", "kknn"))` + +Alternatively, the script below checks whether you have the packages required to complete this module and installs them for you in case they are missing. + +```{r, message=F, warning=F} +suppressWarnings(if (!require("pacman"))install.packages("pacman")) + +pacman::p_load(tidyverse, tidymodels, themis, kernlab, ranger, xgboost, kknn) +``` + +Now, let's hit the ground running! + +## **1. A classification map** + +In our [previous lesson](https://github.com/microsoft/ML-For-Beginners/tree/main/4-Classification/2-Classifiers-1), we tried to address the question: how do we choose between multiple models? To a great extent, it depends on the characteristics of the data and the type of problem we want to solve (for instance classification or regression?) + +Previously, we learned about the various options you have when classifying data using Microsoft's cheat sheet. Python's Machine Learning framework, Scikit-learn, offers a similar but more granular cheat sheet that can further help narrow down your estimators (another term for classifiers): + +![](../../images/map.png){width="650"}\ + +> Tip: [visit this map online](https://scikit-learn.org/stable/tutorial/machine_learning_map/) and click along the path to read documentation. +> +> The [Tidymodels reference site](https://www.tidymodels.org/find/parsnip/#models) also provides an excellent documentation about different types of model. + +### **The plan** πŸ—ΊοΈ + +This map is very helpful once you have a clear grasp of your data, as you can 'walk' along its paths to a decision: + +- We have \>50 samples + +- We want to predict a category + +- We have labeled data + +- We have fewer than 100K samples + +- ✨ We can choose a Linear SVC + +- If that doesn't work, since we have numeric data + + - We can try a ✨ KNeighbors Classifier + + - If that doesn't work, try ✨ SVC and ✨ Ensemble Classifiers + +This is a very helpful trail to follow. Now, let's get right into it using the [tidymodels](https://www.tidymodels.org/) modelling framework: a consistent and flexible collection of R packages developed to encourage good statistical practice 😊. + +## 2. Split the data and deal with imbalanced data set. + +From our previous lessons, we learnt that there were a set of common ingredients across our cuisines. Also, there was quite an unequal distribution in the number of cuisines. + +We'll deal with these by + +- Dropping the most common ingredients that create confusion between distinct cuisines, using `dplyr::select()`. + +- Use a `recipe` that preprocesses the data to get it ready for modelling by applying an `over-sampling` algorithm. + +We already looked at the above in the previous lesson so this should be a breeze πŸ₯³! + +```{r clean_imbalance} +# Load the core Tidyverse and Tidymodels packages +library(tidyverse) +library(tidymodels) + +# Load the original cuisines data +df <- read_csv(file = "https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/4-Classification/data/cuisines.csv") + +# Drop id column, rice, garlic and ginger from our original data set +df_select <- df %>% + select(-c(1, rice, garlic, ginger)) %>% + # Encode cuisine column as categorical + mutate(cuisine = factor(cuisine)) + + +# Create data split specification +set.seed(2056) +cuisines_split <- initial_split(data = df_select, + strata = cuisine, + prop = 0.7) + +# Extract the data in each split +cuisines_train <- training(cuisines_split) +cuisines_test <- testing(cuisines_split) + +# Display distribution of cuisines in the training set +cuisines_train %>% + count(cuisine) %>% + arrange(desc(n)) + + +``` + +### Deal with imbalanced data + +Imbalanced data often has negative effects on the model performance. Many models perform best when the number of observations is equal and, thus, tend to struggle with unbalanced data. + +There are majorly two ways of dealing with imbalanced data sets: + +- adding observations to the minority class: `Over-sampling` e.g using a SMOTE algorithm which synthetically generates new examples of the minority class using nearest neighbors of these cases. + +- removing observations from majority class: `Under-sampling` + +In our previous lesson, we demonstrated how to deal with imbalanced data sets using a `recipe`. A recipe can be thought of as a blueprint that describes what steps should be applied to a data set in order to get it ready for data analysis. In our case, we want to have an equal distribution in the number of our cuisines for our `training set`. Let's get right into it. + +```{r recap_balance} +# Load themis package for dealing with imbalanced data +library(themis) + +# Create a recipe for preprocessing training data +cuisines_recipe <- recipe(cuisine ~ ., data = cuisines_train) %>% + step_smote(cuisine) + +# Print recipe +cuisines_recipe + +``` + +Now we are ready to train models πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»! + +## 3. Beyond multinomial regression models + +In our previous lesson, we looked at multinomial regression models. Let's explore some more flexible models for classification. + +### Support Vector Machines. + +In the context of classification, `Support Vector Machines` is a machine learning technique that tries to find a *hyperplane* that "best" separates the classes. Let's look at a simple example: + +![By User:ZackWeinberg:This file was derived from: ](../../images/svm.png){width="300"} + +H~1~ does not separate the classes. H~2~ does, but only with a small margin. H~3~ separates them with the maximal margin. + +#### Linear Support Vector Classifier + +Support-Vector clustering (SVC) is a child of the Support-Vector machines family of ML techniques. In SVC, the hyperplane is chosen to correctly separate `most` of the training observations, but `may misclassify` a few observations. By allowing some points to be on the wrong side, the SVM becomes more robust to outliers hence better generalization to new data. The parameter that regulates this violation is referred to as `cost` which has a default value of 1 (see `help("svm_poly")`). + +Let's create a linear SVC by setting `degree = 1` in a polynomial SVM model. + +```{r svc_spec} +# Make a linear SVC specification +svc_linear_spec <- svm_poly(degree = 1) %>% + set_engine("kernlab") %>% + set_mode("classification") + +# Bundle specification and recipe into a worklow +svc_linear_wf <- workflow() %>% + add_recipe(cuisines_recipe) %>% + add_model(svc_linear_spec) + +# Print out workflow +svc_linear_wf +``` + +Now that we have captured the preprocessing steps and model specification into a *workflow*, we can go ahead and train the linear SVC and evaluate results while at it. For performance metrics, let's create a metric set that will evaluate: `accuracy`, `sensitivity`, `Positive Predicted Value` and `F Measure` + +> `augment()` will add column(s) for predictions to the given data. + +```{r svc_train} +# Train a linear SVC model +svc_linear_fit <- svc_linear_wf %>% + fit(data = cuisines_train) + +# Create a metric set +eval_metrics <- metric_set(ppv, sens, accuracy, f_meas) + + +# Make predictions and Evaluate model performance +svc_linear_fit %>% + augment(new_data = cuisines_test) %>% + eval_metrics(truth = cuisine, estimate = .pred_class) + + + +``` + +#### + +#### Support Vector Machine + +The support vector machine (SVM) is an extension of the support vector classifier in order to accommodate a non-linear boundary between the classes. In essence, SVMs use the *kernel trick* to enlarge the feature space to adapt to nonlinear relationships between classes. One popular and extremely flexible kernel function used by SVMs is the *Radial basis function.* Let's see how it will perform on our data. + +```{r svm_rbf} +set.seed(2056) + +# Make an RBF SVM specification +svm_rbf_spec <- svm_rbf() %>% + set_engine("kernlab") %>% + set_mode("classification") + +# Bundle specification and recipe into a worklow +svm_rbf_wf <- workflow() %>% + add_recipe(cuisines_recipe) %>% + add_model(svm_rbf_spec) + + +# Train an RBF model +svm_rbf_fit <- svm_rbf_wf %>% + fit(data = cuisines_train) + + +# Make predictions and Evaluate model performance +svm_rbf_fit %>% + augment(new_data = cuisines_test) %>% + eval_metrics(truth = cuisine, estimate = .pred_class) +``` + +Much better 🀩! + +> βœ… Please see: +> +> - [*Support Vector Machines*](https://bradleyboehmke.github.io/HOML/svm.html), Hands-on Machine Learning with R +> +> - [*Support Vector Machines*](https://www.statlearning.com/), An Introduction to Statistical Learning with Applications in R +> +> for further reading. + +### Nearest Neighbor classifiers + +*K*-nearest neighbor (KNN) is an algorithm in which each observation is predicted based on its *similarity* to other observations. + +Let's fit one to our data. + +```{r knn} +# Make a KNN specification +knn_spec <- nearest_neighbor() %>% + set_engine("kknn") %>% + set_mode("classification") + +# Bundle recipe and model specification into a workflow +knn_wf <- workflow() %>% + add_recipe(cuisines_recipe) %>% + add_model(knn_spec) + +# Train a boosted tree model +knn_wf_fit <- knn_wf %>% + fit(data = cuisines_train) + + +# Make predictions and Evaluate model performance +knn_wf_fit %>% + augment(new_data = cuisines_test) %>% + eval_metrics(truth = cuisine, estimate = .pred_class) +``` + +It appears that this model is not performing that well. Probably changing the model's arguments (see `help("nearest_neighbor")` will improve model performance. Be sure to try it out. + +> βœ… Please see: +> +> - [Hands-on Machine Learning with R](https://bradleyboehmke.github.io/HOML/) +> +> - [An Introduction to Statistical Learning with Applications in R](https://www.statlearning.com/) +> +> to learn more about *K*-Nearest Neighbors classifiers. + +### Ensemble classifiers + +Ensemble algorithms work by combining multiple base estimators to produce an optimal model either by: + +`bagging`: applying an *averaging function* to a collection of base models + +`boosting`: building a sequence of models that build on one another to improve predictive performance. + +Let's start by trying out a Random Forest model, which builds a large collection of decision trees then applies an averaging function to for a better overall model. + +```{r rf} +# Make a random forest specification +rf_spec <- rand_forest() %>% + set_engine("ranger") %>% + set_mode("classification") + +# Bundle recipe and model specification into a workflow +rf_wf <- workflow() %>% + add_recipe(cuisines_recipe) %>% + add_model(rf_spec) + +# Train a random forest model +rf_wf_fit <- rf_wf %>% + fit(data = cuisines_train) + + +# Make predictions and Evaluate model performance +rf_wf_fit %>% + augment(new_data = cuisines_test) %>% + eval_metrics(truth = cuisine, estimate = .pred_class) + + +``` + +Good job πŸ‘! + +Let's also experiment with a Boosted Tree model. + +Boosted Tree defines an ensemble method that creates a series of sequential decision trees where each tree depends on the results of previous trees in an attempt to incrementally reduce the error. It focuses on the weights of incorrectly classified items and adjusts the fit for the next classifier to correct. + +There are different ways to fit this model (see `help("boost_tree")`). In this example, we'll fit Boosted trees via `xgboost` engine. + +```{r boosted_tree} +# Make a boosted tree specification +boost_spec <- boost_tree(trees = 200) %>% + set_engine("xgboost") %>% + set_mode("classification") + +# Bundle recipe and model specification into a workflow +boost_wf <- workflow() %>% + add_recipe(cuisines_recipe) %>% + add_model(boost_spec) + +# Train a boosted tree model +boost_wf_fit <- boost_wf %>% + fit(data = cuisines_train) + + +# Make predictions and Evaluate model performance +boost_wf_fit %>% + augment(new_data = cuisines_test) %>% + eval_metrics(truth = cuisine, estimate = .pred_class) +``` + +> βœ… Please see: +> +> - [Machine Learning for Social Scientists](https://cimentadaj.github.io/ml_socsci/tree-based-methods.html#random-forests) +> +> - [Hands-on Machine Learning with R](https://bradleyboehmke.github.io/HOML/) +> +> - [An Introduction to Statistical Learning with Applications in R](https://www.statlearning.com/) +> +> - - Explores the AdaBoost model which is a good alternative to xgboost. +> +> to learn more about Ensemble classifiers. + +## 4. Extra - comparing multiple models + +We have fitted quite a number of models in this lab πŸ™Œ. It can become tedious or onerous to create a lot of workflows from different sets of preprocessors and/or model specifications and then calculate the performance metrics one by one. + +Let's see if we can address this by creating a function that fits a list of workflows on the training set then returns the performance metrics based on the test set. We'll get to use `map()` and `map_dfr()` from the [purrr](https://purrr.tidyverse.org/) package to apply functions to each element in list. + +> [`map()`](https://purrr.tidyverse.org/reference/map.html) functions allow you to replace many for loops with code that is both more succinct and easier to read. The best place to learn about the [`map()`](https://purrr.tidyverse.org/reference/map.html) functions is the [iteration chapter](http://r4ds.had.co.nz/iteration.html) in R for data science. + +```{r compare_models} +set.seed(2056) + +# Create a metric set +eval_metrics <- metric_set(ppv, sens, accuracy, f_meas) + +# Define a function that returns performance metrics +compare_models <- function(workflow_list, train_set, test_set){ + + suppressWarnings( + # Fit each model to the train_set + map(workflow_list, fit, data = train_set) %>% + # Make predictions on the test set + map_dfr(augment, new_data = test_set, .id = "model") %>% + # Select desired columns + select(model, cuisine, .pred_class) %>% + # Evaluate model performance + group_by(model) %>% + eval_metrics(truth = cuisine, estimate = .pred_class) %>% + ungroup() + ) + +} # End of function + + +``` + +Let's call our function and compare the accuracy across the models. + +```{r call_fn} +# Make a list of workflows +workflow_list <- list( + "svc" = svc_linear_wf, + "svm" = svm_rbf_wf, + "knn" = knn_wf, + "random_forest" = rf_wf, + "xgboost" = boost_wf) + +# Call the function +set.seed(2056) +perf_metrics <- compare_models(workflow_list = workflow_list, train_set = cuisines_train, test_set = cuisines_test) + +# Print out performance metrics +perf_metrics %>% + group_by(.metric) %>% + arrange(desc(.estimate)) %>% + slice_head(n=7) + +# Compare accuracy +perf_metrics %>% + filter(.metric == "accuracy") %>% + arrange(desc(.estimate)) + +``` + +[**workflowset**](https://workflowsets.tidymodels.org/) package allow users to create and easily fit a large number of models but is mostly designed to work with resampling techniques such as `cross-validation`, an approach we are yet to cover. + +## **πŸš€Challenge** + +Each of these techniques has a large number of parameters that you can tweak for instance `cost` in SVMs, `neighbors` in KNN, `mtry` (Randomly Selected Predictors) in Random Forest. + +Research each one's default parameters and think about what tweaking these parameters would mean for the model's quality. + +To find out more about a particular model and its parameters, use: `help("model")` e.g `help("rand_forest")` + +> In practice, we usually *estimate* the *best values* for these by training many models on a `simulated data set` and measuring how well all these models perform. This process is called **tuning**. + +### [**Post-lecture quiz**](https://white-water-09ec41f0f.azurestaticapps.net/quiz/24/) + +### **Review & Self Study** + +There's a lot of jargon in these lessons, so take a minute to review [this list](https://docs.microsoft.com/dotnet/machine-learning/resources/glossary?WT.mc_id=academic-15963-cxa) of useful terminology! + +#### THANK YOU TO: + +[`Allison Horst`](https://twitter.com/allison_horst/) for creating the amazing illustrations that make R more welcoming and engaging. Find more illustrations at her [gallery](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM). + +[Cassie Breviu](https://www.twitter.com/cassieview) and [Jen Looper](https://www.twitter.com/jenlooper) for creating the original Python version of this module β™₯️ + +Happy Learning, + +[Eric](https://twitter.com/ericntay), Gold Microsoft Learn Student Ambassador. + +![Artwork by \@allison_horst](../../images/r_learners_sm.jpeg) diff --git a/README.md b/README.md index 148ddd824..50fe2399d 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,9 @@ By ensuring that the content aligns with projects, the process is made more enga | 07 | North American pumpkin prices πŸŽƒ | [Regression](2-Regression/README.md) | Build linear and polynomial regression models |
  • [Python](2-Regression/3-Linear/README.md)
  • [R](2-Regression/3-Linear/solution/R/lesson_3-R.ipynb)
|
  • Jen
  • Eric Wanjau
| | 08 | North American pumpkin prices πŸŽƒ | [Regression](2-Regression/README.md) | Build a logistic regression model |
  • [Python](2-Regression/4-Logistic/README.md)
  • [R](2-Regression/4-Logistic/solution/R/lesson_4-R.ipynb)
|
  • Jen
  • Eric Wanjau
| | 09 | A Web App πŸ”Œ | [Web App](3-Web-App/README.md) | Build a web app to use your trained model | [Python](3-Web-App/1-Web-App/README.md) | Jen | -| 10 | Introduction to classification | [Classification](4-Classification/README.md) | Clean, prep, and visualize your data; introduction to classification | [Python](4-Classification/1-Introduction/README.md) | Jen and Cassie | -| 11 | Delicious Asian and Indian cuisines 🍜 | [Classification](4-Classification/README.md) | Introduction to classifiers | [Python](4-Classification/2-Classifiers-1/README.md) | Jen and Cassie | -| 12 | Delicious Asian and Indian cuisines 🍜 | [Classification](4-Classification/README.md) | More classifiers | [Python](4-Classification/3-Classifiers-2/README.md) | Jen and Cassie | +| 10 | Introduction to classification | [Classification](4-Classification/README.md) | Clean, prep, and visualize your data; introduction to classification |
  • [Python](4-Classification/1-Introduction/README.md)
  • [R](4-Classification/1-Introduction/solution/R/lesson_10-R.ipynb) |
    • Jen and Cassie
    • Eric Wanjau
    | +| 11 | Delicious Asian and Indian cuisines 🍜 | [Classification](4-Classification/README.md) | Introduction to classifiers |
    • [Python](4-Classification/2-Classifiers-1/README.md)
    • [R](4-Classification/2-Classifiers-1/solution/R/lesson_11-R.ipynb) |
      • Jen and Cassie
      • Eric Wanjau
      | +| 12 | Delicious Asian and Indian cuisines 🍜 | [Classification](4-Classification/README.md) | More classifiers |
      • [Python](4-Classification/3-Classifiers-2/README.md)
      • [R](4-Classification/3-Classifiers-2/solution/R/lesson_12-R.ipynb) |
        • Jen and Cassie
        • Eric Wanjau
        | | 13 | Delicious Asian and Indian cuisines 🍜 | [Classification](4-Classification/README.md) | Build a recommender web app using your model | [Python](4-Classification/4-Applied/README.md) | Jen | | 14 | Introduction to clustering | [Clustering](5-Clustering/README.md) | Clean, prep, and visualize your data; Introduction to clustering | [Python](5-Clustering/1-Visualize/README.md) | Jen | | 15 | Exploring Nigerian Musical Tastes 🎧 | [Clustering](5-Clustering/README.md) | Explore the K-Means clustering method | [Python](5-Clustering/2-K-Means/README.md) | Jen |