From 9486db87d8885bf0bfaee20866c880c4630eb97d Mon Sep 17 00:00:00 2001 From: qiaolongfei Date: Mon, 27 Mar 2017 10:13:30 +0800 Subject: [PATCH] add draw line in 01.fit-a-line --- 01.fit_a_line/README.en.ipynb | 48 ++++++++++++++++++------ 01.fit_a_line/README.en.md | 46 +++++++++++++++++------ 01.fit_a_line/README.ipynb | 49 +++++++++++++++++++------ 01.fit_a_line/README.md | 45 ++++++++++++++++++----- 01.fit_a_line/image/train-and-test.png | Bin 0 -> 17970 bytes 01.fit_a_line/index.en.html | 46 +++++++++++++++++------ 01.fit_a_line/index.html | 45 ++++++++++++++++++----- 7 files changed, 213 insertions(+), 66 deletions(-) create mode 100644 01.fit_a_line/image/train-and-test.png diff --git a/01.fit_a_line/README.en.ipynb b/01.fit_a_line/README.en.ipynb index a819dc5..8330e57 100644 --- a/01.fit_a_line/README.en.ipynb +++ b/01.fit_a_line/README.en.ipynb @@ -7,7 +7,7 @@ "# Linear Regression\n", "Let us begin the tutorial with a classical problem called Linear Regression \\[[1](#References)\\]. In this chapter, we will train a model from a realistic dataset to predict home prices. Some important concepts in Machine Learning will be covered through this example.\n", "\n", - "The source code for this tutorial lives on [book/fit_a_line](https://github.com/PaddlePaddle/book/tree/develop/fit_a_line). For instructions on getting started with PaddlePaddle, see [PaddlePaddle installation guide](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/getstarted/build_and_install/docker_install_en.rst).\n", + "The source code for this tutorial lives on [book/fit_a_line](https://github.com/PaddlePaddle/book/tree/develop/fit_a_line). For instructions on getting started with PaddlePaddle, see [PaddlePaddle installation guide](https://github.com/PaddlePaddle/book/blob/develop/README.en.md).\n", "\n", "## Problem Setup\n", "Suppose we have a dataset of $n$ real estate properties. These real estate properties will be referred to as *homes* in this chapter for clarity.\n", @@ -308,19 +308,41 @@ "editable": true }, "source": [ - "# event_handler to print training and testing info\n", + "import matplotlib.pyplot as plt\n", + "from IPython import display\n", + "import cPickle\n", + "\n", + "step=0\n", + "\n", + "train_costs=[],[]\n", + "test_costs=[],[]\n", + "\n", "def event_handler(event):\n", + " global step\n", + " global train_costs\n", + " global test_costs\n", " if isinstance(event, paddle.event.EndIteration):\n", - " if event.batch_id % 100 == 0:\n", - " print \"Pass %d, Batch %d, Cost %f\" % (\n", - " event.pass_id, event.batch_id, event.cost)\n", - "\n", - " if isinstance(event, paddle.event.EndPass):\n", - " result = trainer.test(\n", - " reader=paddle.batch(\n", - " uci_housing.test(), batch_size=2),\n", - " feeding=feeding)\n", - " print \"Test %d, Cost %f\" % (event.pass_id, result.cost)\n" + " need_plot = False\n", + " if step % 10 == 0: # every 10 batches, record a train cost\n", + " train_costs[0].append(step)\n", + " train_costs[1].append(event.cost)\n", + "\n", + " if step % 1000 == 0: # every 1000 batches, record a test cost\n", + " result = trainer.test(\n", + " reader=paddle.batch(\n", + " uci_housing.test(), batch_size=2),\n", + " feeding=feeding)\n", + " test_costs[0].append(step)\n", + " test_costs[1].append(result.cost)\n", + "\n", + " if step % 100 == 0: # every 100 batches, update cost plot\n", + " plt.plot(*train_costs)\n", + " plt.plot(*test_costs)\n", + " plt.legend(['Train Cost', 'Test Cost'], loc='upper left')\n", + " display.clear_output(wait=True)\n", + " display.display(plt.gcf())\n", + " plt.gcf().clear()\n", + " step += 1\n" ], "outputs": [ { @@ -372,6 +394,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "\n", + "![png](./image/train-and-test.png)\n", "\n", "## Summary\n", "This chapter introduces *Linear Regression* and how to train and test this model with PaddlePaddle, using the UCI Housing Data Set. Because a large number of more complex models and techniques are derived from linear regression, it is important to understand its underlying theory and limitation.\n", diff --git a/01.fit_a_line/README.en.md b/01.fit_a_line/README.en.md index 6d06d6a..4d4d0a2 100644 --- a/01.fit_a_line/README.en.md +++ b/01.fit_a_line/README.en.md @@ -163,19 +163,41 @@ feeding={'x': 0, 'y': 1} Moreover, an event handler is provided to print the training progress: ```python -# event_handler to print training and testing info +import matplotlib.pyplot as plt +from IPython import display +import cPickle + +step=0 + +train_costs=[],[] +test_costs=[],[] + def event_handler(event): + global step + global train_costs + global test_costs if isinstance(event, paddle.event.EndIteration): - if event.batch_id % 100 == 0: - print "Pass %d, Batch %d, Cost %f" % ( - event.pass_id, event.batch_id, event.cost) - - if isinstance(event, paddle.event.EndPass): - result = trainer.test( - reader=paddle.batch( - uci_housing.test(), batch_size=2), - feeding=feeding) - print "Test %d, Cost %f" % (event.pass_id, result.cost) + need_plot = False + if step % 10 == 0: # every 10 batches, record a train cost + train_costs[0].append(step) + train_costs[1].append(event.cost) + + if step % 1000 == 0: # every 1000 batches, record a test cost + result = trainer.test( + reader=paddle.batch( + uci_housing.test(), batch_size=2), + feeding=feeding) + test_costs[0].append(step) + test_costs[1].append(result.cost) + + if step % 100 == 0: # every 100 batches, update cost plot + plt.plot(*train_costs) + plt.plot(*test_costs) + plt.legend(['Train Cost', 'Test Cost'], loc='upper left') + display.clear_output(wait=True) + display.display(plt.gcf()) + plt.gcf().clear() + step += 1 ``` ### Start Training @@ -191,6 +213,8 @@ trainer.train( num_passes=30) ``` +![png](./image/train-and-test.png) + ## Summary This chapter introduces *Linear Regression* and how to train and test this model with PaddlePaddle, using the UCI Housing Data Set. Because a large number of more complex models and techniques are derived from linear regression, it is important to understand its underlying theory and limitation. diff --git a/01.fit_a_line/README.ipynb b/01.fit_a_line/README.ipynb index 07f15fa..38b23e6 100644 --- a/01.fit_a_line/README.ipynb +++ b/01.fit_a_line/README.ipynb @@ -7,7 +7,7 @@ "# 线性回归\n", "让我们从经典的线性回归(Linear Regression \\[[1](#参考文献)\\])模型开始这份教程。在这一章里,你将使用真实的数据集建立起一个房价预测模型,并且了解到机器学习中的若干重要概念。\n", "\n", - "本教程源代码目录在[book/fit_a_line](https://github.com/PaddlePaddle/book/tree/develop/fit_a_line), 初次使用请参考PaddlePaddle[安装教程](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/getstarted/build_and_install/docker_install_cn.rst)。\n", + "本教程源代码目录在[book/fit_a_line](https://github.com/PaddlePaddle/book/tree/develop/fit_a_line), 初次使用请参考PaddlePaddle[安装教程](https://github.com/PaddlePaddle/book/blob/develop/README.md)。\n", "\n", "## 背景介绍\n", "给定一个大小为$n$的数据集 ${\\{y_{i}, x_{i1}, ..., x_{id}\\}}_{i=1}^{n}$,其中$x_{i1}, \\ldots, x_{id}$是第$i$个样本$d$个属性上的取值,$y_i$是该样本待预测的目标。线性回归模型假设目标$y_i$可以被属性间的线性组合描述,即\n", @@ -35,7 +35,7 @@ "\n", "$\\hat{Y}$ 表示模型的预测结果,用来和真实值$Y$区分。模型要学习的参数即:$\\omega_1, \\ldots, \\omega_{13}, b$。\n", "\n", - "建立模型后,我们需要给模型一个优化目标,使得学到的参数能够让预测值$\\hat{Y}$尽可能地接近真实值$Y$。这里我们引入损失函数([Loss Function](https://en.wikipedia.org/wiki/Loss_function),或Cost Function)这个概念。 输入任意一个数据样本的目标值$y_{i}$和模型给出的预测值$\\hat{y_{i}}$,损失函数输出一个非负的实值。这个实质通常用来反映模型误差的大小。\n", + "建立模型后,我们需要给模型一个优化目标,使得学到的参数能够让预测值$\\hat{Y}$尽可能地接近真实值$Y$。这里我们引入损失函数([Loss Function](https://en.wikipedia.org/wiki/Loss_function),或Cost Function)这个概念。 输入任意一个数据样本的目标值$y_{i}$和模型给出的预测值$\\hat{y_{i}}$,损失函数输出一个非负的实值。这个实值通常用来反映模型误差的大小。\n", "\n", "对于线性回归模型来讲,最常见的损失函数就是均方误差(Mean Squared Error, [MSE](https://en.wikipedia.org/wiki/Mean_squared_error))了,它的形式是:\n", "\n", @@ -304,18 +304,41 @@ }, "source": [ "# event_handler to print training and testing info\n", + "import matplotlib.pyplot as plt\n", + "from IPython import display\n", + "import cPickle\n", + "\n", + "step=0\n", + "\n", + "train_costs=[],[]\n", + "test_costs=[],[]\n", + "\n", "def event_handler(event):\n", + " global step\n", + " global train_costs\n", + " global test_costs\n", " if isinstance(event, paddle.event.EndIteration):\n", - " if event.batch_id % 100 == 0:\n", - " print \"Pass %d, Batch %d, Cost %f\" % (\n", - " event.pass_id, event.batch_id, event.cost)\n", - "\n", - " if isinstance(event, paddle.event.EndPass):\n", - " result = trainer.test(\n", - " reader=paddle.batch(\n", - " uci_housing.test(), batch_size=2),\n", - " feeding=feeding)\n", - " print \"Test %d, Cost %f\" % (event.pass_id, result.cost)\n" + " need_plot = False\n", + " if step % 10 == 0: # every 10 batches, record a train cost\n", + " train_costs[0].append(step)\n", + " train_costs[1].append(event.cost)\n", + "\n", + " if step % 1000 == 0: # every 1000 batches, record a test cost\n", + " result = trainer.test(\n", + " reader=paddle.batch(\n", + " uci_housing.test(), batch_size=2),\n", + " feeding=feeding)\n", + " test_costs[0].append(step)\n", + " test_costs[1].append(result.cost)\n", + "\n", + " if step % 100 == 0: # every 100 batches, update cost plot\n", + " plt.plot(*train_costs)\n", + " plt.plot(*test_costs)\n", + " plt.legend(['Train Cost', 'Test Cost'], loc='upper left')\n", + " display.clear_output(wait=True)\n", + " display.display(plt.gcf())\n", + " plt.gcf().clear()\n", + " step += 1\n" ], "outputs": [ { @@ -367,6 +390,8 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "\n", + "![png](./image/train-and-test.png)\n", "\n", "## 总结\n", "在这章里,我们借助波士顿房价这一数据集,介绍了线性回归模型的基本概念,以及如何使用PaddlePaddle实现训练和测试的过程。很多的模型和技巧都是从简单的线性回归模型演化而来,因此弄清楚线性模型的原理和局限非常重要。\n", diff --git a/01.fit_a_line/README.md b/01.fit_a_line/README.md index a7967db..56fecf7 100644 --- a/01.fit_a_line/README.md +++ b/01.fit_a_line/README.md @@ -159,18 +159,41 @@ feeding={'x': 0, 'y': 1} ```python # event_handler to print training and testing info +import matplotlib.pyplot as plt +from IPython import display +import cPickle + +step=0 + +train_costs=[],[] +test_costs=[],[] + def event_handler(event): + global step + global train_costs + global test_costs if isinstance(event, paddle.event.EndIteration): - if event.batch_id % 100 == 0: - print "Pass %d, Batch %d, Cost %f" % ( - event.pass_id, event.batch_id, event.cost) - - if isinstance(event, paddle.event.EndPass): - result = trainer.test( - reader=paddle.batch( - uci_housing.test(), batch_size=2), - feeding=feeding) - print "Test %d, Cost %f" % (event.pass_id, result.cost) + need_plot = False + if step % 10 == 0: # every 10 batches, record a train cost + train_costs[0].append(step) + train_costs[1].append(event.cost) + + if step % 1000 == 0: # every 1000 batches, record a test cost + result = trainer.test( + reader=paddle.batch( + uci_housing.test(), batch_size=2), + feeding=feeding) + test_costs[0].append(step) + test_costs[1].append(result.cost) + + if step % 100 == 0: # every 100 batches, update cost plot + plt.plot(*train_costs) + plt.plot(*test_costs) + plt.legend(['Train Cost', 'Test Cost'], loc='upper left') + display.clear_output(wait=True) + display.display(plt.gcf()) + plt.gcf().clear() + step += 1 ``` ### 开始训练 @@ -186,6 +209,8 @@ trainer.train( num_passes=30) ``` +![png](./image/train-and-test.png) + ## 总结 在这章里,我们借助波士顿房价这一数据集,介绍了线性回归模型的基本概念,以及如何使用PaddlePaddle实现训练和测试的过程。很多的模型和技巧都是从简单的线性回归模型演化而来,因此弄清楚线性模型的原理和局限非常重要。 diff --git a/01.fit_a_line/image/train-and-test.png b/01.fit_a_line/image/train-and-test.png new file mode 100644 index 0000000000000000000000000000000000000000..bcd304a6a0baf30ecfbc43e08fc0aca179d05958 GIT binary patch literal 17970 zcmagG1yo!?^Dl@qfnXuQonXO&YjF1f!7afV+}$mBa0%`*xVr>*cXxNUy?pQgz4La@ z?pZih7xQs$W%C-`@&y;wVUXNKjBvD3TH)icnC{zQ79>gaCZPi3NKFT;S}4 zB$YwHpF7AP7REz>A3d3-l#KkD|O5Xtd{y&?&X{ zxV?X_sB)@m0{)L{Jyy$RTP$~yjE!1HkA^N?(oQF1XYd5k;YjUlJ}*GM-BbjlgM3L0 zILY9ANkF&}ARiKtP$0Pw3CK5U{eQkPWrUL|>=ao&wf*c)A5dD`QNDyhwp4)W3l0vi2Qns4Lmn4Uj49+IQa4;Ul3pL1ZXE=r9C=#E8 z;ph z8VvjC(7%F+oxwiYAl;@4v+1cJeyWA1luK> z-t%Rr)$Zd~U>vAqaPQW}#fi5c4$F3X@U3!w6st5po-_@ma72;{2P!Bi2qZ9Q*T23z z78V!7E(vAE{<_>5hFY>6q+jxSeb%z=r>HiY4w;nW371V_S6?|yv1FxIo$F+-4)C>$e*ll<4#|t&tUav1$^2;a+{eor{Q zxU4J+txCySH@2?9*;9Rup!;SmurH*je)?3GElD2DL%Sp z5H#w#$ve?C#f%U2vf!3~oW?k31vc|uCXn8L%%sIq1Bq>>UUZDOsw2FYG7N1`wou6& zRwMmX=}Xon^67j_F1x8if4NvP(BBhs$HvHUzw-tdog_}=@{f6?)#Gt*B3D{~huvb~ zpD#S}-OZwv=}ZZ(9|Fn-aBF-tSMk^5W`1jHtK4##`{sC2{PE_P>v~FwT(*(~BpH&5 z*Rj|>*v^wz9^`GRahM;ch=%+4il_y1#qlBsuLCc~=D%sP_E`WnG)^(5dIs@VTvT>b zZNzkL@)qAW*Q>Jy-6|s#ub0bo^=4R?d4!HtRPqk>IscyDPJ(rk zocFJ;cUaqLxjTwfIPvRG7<$dxt%lvS&C5~#$OpdNbT7WL<_n~^(Ht*KpE}Q$YR@-0 zF$SR$nxXqaH0#*wqY>WYv;)$O9NgaMLgU|B0GDmI`@l}>PytR%?f4MSh5@4J}55BD<}ZvPfDt* zqK_P~Ugh@tiU2p3$C^w5nH?=RVFz^yrm$NmNUd9Qavqg; zDM(e=Z1yD1(SU?Df*s08i+^RIemL=h&P9q-T)4zmI*d;AdVO3C!(|BswAW@<+sOt{ z=-#bHDH^`}Elh^yGoe6KLaDi)En(-_+Rfwq2D+Je3osuJMyMp`Dv~pcB9|0 zy&2n{qO&8oLjYRm32jdS?=UH&Qc|P=&3C?A4|>xe8ft32@hnlM3RQIfxt}A_af$)Z zoAXJ?J=Q25mS%K1$bYkIR({rd8ks;4sdL5f7B`2_dA&3ao5HNioHIX=*`ef8Uf{nI z+As-e2&9DW3OoMUZk%_f8J9vTP`J+hfKabMGp#~1ja5Ge!USyThp^O>>`j}5Js#@Z z5S!Z<7rZ+G8VAJkPxT|gFBB)G*@rex#4UsZ;1*q)Yq&LB2iDF>YdXMnea1=()Ck7^ zPhU}{1@M5|S^WR^X@HR?nUVhCiof4NJx}?52OHG8qnh1jqB^nv+Ld2`og;PUUM^VYI=HE5E}7`Q(1ou z)gczW#vUP$BV4udU_AXpv^2xG#bWLB^W*KTRok;;SH*ml@gb%hr(m7gw3O4qlqCPv zn6L&I!sB>U0R2Yk^78UJnnKz<_uCIoazP34u+vc$z7)Re=Q1MS)WJ=!^pW093VCsd z-^S@4@c_wqfZ$}QHqS}xqvgw0K0lM|mC1_xrR>4s;e4wH4;L5L$4{UBq@)bCy*^v* z3?+4W+%EUh*Dd{WSuRw{w*`QRE4{|%pu?aFfv*gDRnfZy8;V;Na% zeLUIxBTls5cvyO=8E?DR?o&yZOOivgPY%bE&=o)byS5XHTs&e6aB~)F)H~td@;_|j4-{Z4~Rp|HH_TU;v z#l&>U^FMzcR74UA{KF?Sc0U(eA32i@Fwtf|H^CKhv2EKhS7$}+v}A(_SUrQiu}r`r zSRX>_F8*Q4N5#c`x>z_~tTVnon1+r_h>abq&=&`+`wIF@m9c!DYzjg<&@nX~-BvJp zM22pgXBZBXZzw|}b;uNwZxm8aB)Z6csp*$kMf<~dn!-@Q)uq{BRNza(NF5 zvfq}xTz<^$s#iL=v_ULB_oM%N0ap|X@21n%lprG4f=uaMK=+b(U6|KyF3U7(kP-$( z0UOajGGbYPVt+72vo{fypzVlj{MQCSRomf{bOJ;7zP+M`#=szB+t6Hv{{CzkaWA10 zK_uS;H2`Gj)GPg&^m|yZ3|x0po!;1`uCDGmsK}ivS8KV9w@XD15)vw3Ec#o(KT$n9 zHYv{!dAvT%*Q_-!1iV!-GBR?_j7Hn@&0-_Kp+?bAtVX4Lx&NsAzP%ZwCXGoy@MN*B zjDebZ42}29d;7VlsOXA3k~hok_5`9?ZIbDBb5wZRc*vlwrBzZh<2soq`>+wpI0Fw4 z|3>ivQnzBpc?&4iBp|9;V0BNyBBD;|x}S@7RUi_(D>&_q3BIj1>`$NOrQC|%FkS?@ zSQ)8$q`-g8$~6eEQ2*~{C8km+Dm7IWkU-WjI*fv*W@y)+%CZC!gO41V}{0{|RNp9w02C{mrQ%0vSMaK#f9;f+3Hv zh~7M5N+(dLCXh$yQT+30m^U0B)h}|0H-L~#n_KCF)sDKh+b-U7&eZ;+=O(U)ujl+y zIl9D$x+M@8i2)fF4CLBzjZ2T#5}Fzw3l_0vCXI_Y#K*G7kwY)V#UOm;z6^j&Ta=&@~S)c;Kmi@#_#a!i|8P4PN zfg_0QrjT0`$f38q_oNR@*AKWcLTaYkP#>Z|n|T3zy|z=)nwbC%@b-OZXDb4h)%<8p z*@)pq<%hw|EOr1ylkYSugSBy}by$VUKjCD>n*F?c7==}Vp7hBS`wKXf8XOHKi@x3t~trUXj{>~p{@jKj?!ds=iw|i=I5MR)6N@F!5n^4y1 zH)HF(Z^Z7b*D|S(B52590eLacXJ*5RfuA|g>gCVG);@;TPq#aaFUbQ^3;xDjiL=l4 z86;#xE(8VbAN+|U{rt(O)xr~fV}{^zl@+iRR7igYG3XMD~bO<4WaQuG4+Cg zuF23zLEg$hXMG<WHA%Ux{+*fY_d?S z+(sT=-m4}M#qG-nix2=1ODxo47#KDLWUOpUl~erQy?7njQGmoS*>O}o{0_ymhydD% z^-P0G@B$&)!uwsB8dOj}97fUM<4wrD(3B70p@{55#k)GUh&aBp(IZz^$|dB2huELc#OibW%VUf#??DYocpl z$n~H0^LDc}QoZDvM*gqEbBOhDdsa`&@0#(pTl_W(G44>waoY4!bhVPe5^s$8MXDfG zgD?WK0yCyD3&boisxbOtCi>pRVaEXGu1+ELhRIb<2&P99tR-)>fy;W+Mx^m6&;yj4 z_qbFcS?*MdRH1uD%#16d_xbBra!-zSEdoaN&JCdqb{N{6D@K4DnBnLKuzudEH8WH& zK_>RF>X~$%PnCMP%9)B%ZZBl)3<71OU;_exRr^6)j6bA4A%tLuqkn|$);PTHc@eJ< z_?ghVt^c%Y5whB4@9C8B?>0;0PzX|U@g!>Y*Lb2q;?nL9v>>`7p`A`q4a577jK4_7 zG*01aX>t*81b3l}y(QbflAGh4B~^TTS3kY?jTMgix;|<{&@tWvr9!8fFR{Z;&eJqKEf(A45nkXnW|hc9K>@O-C*RD$>=yxyj3QcMqVP)Asi3a~rE!BRl1 zBJC4Hhni}Pp$dB|X4hujhbj&#i4y?6CYN~p)BT5x8xT)kRIL!YN_czatO6Yb1Be=R z0F~fv2|UVRtJg{vO}SOlP~Sz3`H$KQEg%WjTfm);tagsg?%4E)uJ(?90W8KAIiSQ# z?7qExcJgAXiW(@N+rTEvrM67PUULYfoN@{CHHgvrJgOoNRZ}0(EWj_iH7WX1H+vH-iq)< z2v{q*XGE`tssDRKuBBY~2pA2K1tgy#wvYHH9%aFW#ZKpARD+mrE69cn7+i@BTzq(U z797-IEul>HyIN{UQB}>2KMCmNjozeKMom1PAX3FIHySE_t~ju6Y6^4slW&yFTjy+# z1@bGT@PktuFXy6DGfirZ$0R(1MQ=+lhrpB;Zhe?-tE#M%5P&D*_}8AR6uMQ)hdKO^AU4J z>#kXb*EnE?1dz?X_(Ef#Z5Dwz$z2Rmq@=$TdM6by7rfahy>RJ|O6U{O)x-35m0gm_ z2G)+~guwDm&7BU@EsyhytNG2ID=D$a?)%pSyA1nDVn(i}-z*`jQR{htz%f>Z#YO$y z>9eCm*6?p0Q%&-|5hUY~cO9r?i9&hhU1}L-CxIUCYI95_^hu*sy3}Hjgrbnxk%TJ$ z0OkZ=E?j^aO*6+Anev|g^v?#slw))XoP7#a&v;dQqYm!R@h$*o6Tr^e|HFBT7$9K8Q^dZ+)+!y>ee^dV zc{7F2FmsR^9dp#$;35lxP~`RZU!*Bu4ExTpf|X5_<>4zn_#2QX<8|P+f08O3Zhx%T z7Gi{gm!cUh$y@gMpx=bE2dzh71$%VcQt&^}2xIwIw@%H%2d{sn$*GtaXQ%k?ri9s3cFiN;9C~ z)Fx?QTAiXeGT5fTC$ziPx$)+`!t*~lzTra5=UdptYIRM%Rw^gIT>fG^NvTX|f{q#k z6(QjyVM8MvrxBEl@&UcEGVGQMZoxK}bqe2=h`B4HzDkAFB6$xi3>|gE&H&!|?I&3| zixQkW4`SgAx&8NlhepYhJO}L3Q=r?kh7p}#9xy+E!${yrAz(O&1(KlBG^mGhZs0Yq zEb_tAM@wC$qrw42)ZHxRPuNNEZ_!0e3ttjA3ZSv$EV)U@Nj7b5!o?ZYAoSi;Bd#!_ zQ{l@yvXO^oK_-0*@OGZ+o_hRFd?}7eUed z39yHL$u*J8yW+2wM2W>Pp6p24O39iv6x>^(wshLuI&nx~afP zJzf73!Qx4F|EuWdwtzxGhtwG47nYc+^i;t&Cpm1Y=w9WgoRZ4<0U&yz(NQ4`y;pR@ z!gnj#SV5(CMTrfAVl}kTN5MFK8>1p-G%XC?EqRRvc*K9l%)FEL zXh85TEcTmK8FJncynpH?jPIov#|oN53sOFdGwKvy{HQ#`J`!nPKsmg-dhU?^KrrI( z^u+_nFi*KeGF84XL!mky{d7O!-}~p;R!}$trbf51A`gC=qU_@En4CS?DXj=qtEwvb zeJl}>Zy4!Z4;Qn4f0sAfCN>s3^EvS96AV=Do$<45^AE3gP%Q*a&WyK`+_vqs8XsC> zzOQ4jLfG_oEO$1$yO|&#K2P@IgH|K&ZpM-CjkXy|Jq#^l7XH9Qe#!iNdHILkw_4;} zGSzmit<8% zn`>hv9jd`bCS{rqC+5<-@u(8{-hom9_zdDuv>uDb#5_t^e<*?PYW!g~*SpYTs}-p> z4e}GHbxkDWA#F{~$({YhcY5EMMm?)$m?Kgr>ZwL<5Ak$IauV$Bmk$t~VB_A>5 zxFK*Sxpndu+cLaP-G=HIh%{JX3wt`cuG`khch#W?w&w<#8eTu=Gq}X(BlCKQ?Abv! z?gB+?h=H;)4_bXl;R>1AR#S(+s#+!;(IW_I;|kb~}rKbvZFk{$=N zud{rr>FMxuaAF>JL4blKOlh#_veuaN&(%oB?i~q}aFkL=;~XHPYll3)sU*Puo-4Md z=LmS_pZbc^pbr*4DQXGMw?C#nm(*rwrBaOh8bC1Wt6w>OO&5Q?k^fwg(}HfGSt7aC z*`a%IVpM4df?6(~`AT_e^~~6d8I4E-!|i|pAY%Vv#Y;c;EQ>u zULDg!zFMV}ocPleAIY13kdK}o-kVrBU6!-Iy~ra;#uNN@W(>Lh-j2SelRGuuF5C`) z2rs?l3N-6?W2<*fn9gh=Zi_u5jome065I}6+GTcap#G-Sgv{E#2ou>9i9IG7?0yT? zz8&w=HM`vQOSMtxbCoPzZ+%AnO;ZD8-JhBDrM2zxWsa`{V2i){BuiD|uM;O!WL*`TJ zk8YI5C7`r?b zSt6&Y=YacCa%fKe;@|{39hiKtVT|ZG!T;6*tj$_6ZY=(2!g(;;>*>`yRn+zlFoK!K zxg&~~Lg$WHK1_0q+1|X{e3WjviOEeSG8I$hG^LnrVUOgORm0=9MT+^%A47ZsZOHeN zlyCI2EE3dzp{Vj3w(p>a*{q&P$LVO|Wgy;cGd56U8tbxmeT%u1{~L4b6VH`k+&xAA zVBMUpK`ZZbfP=y|r1yAq!|*FIV9qE+V`kN{I|hB0L6GN85mGy4+L%mx#Bk}U>%apU&`1wP#QePt-( z|4y~F1b#fjD%Su%6HjIWA-%pE5Yp2;sbt@dp=V5O3WZR0G)B6XyV9VI|6MdA&gq*T zB`iN+NpvGxFO&9<=)%p1WSQUP;wH?DnR|fxJZs7a%Ob~Cy zXqY!1#lLYG*KB*amZ-!3r=FGf@)|Gw>polT#nx`%Or!dtFC)WuDd~EL5B9cB^oIMK zcTZ^4M`)4n<86|`a6Z387r@lfoG7XB@}6VOFA zB7QrBXEc_cdxYCt>%2{swy6ZkOw!ErAK)#j&9Q89dsDEE*JegDHou=dQ=FcO+?X7( z%OgBu9sb(7so2GQyj`a(bB-Mg(GI1%v07VQb99$K zzPzq;aT@)AueFr4%K9G*EbHQr?JA;+kqI*f4;vM&lS54sYDmfEJqKbD-o?tUr zsh@eJ3%l5=&-6Qzw3tnJU^TYylNvI#K=Fw6;;y-)1I!x7ylzKqukTK!pc_iJiGcd% zQFcWk=3TO3DQa({$nETJ+WONLGS7V$7L)pg99-=`o^lNDy}HTm}?FkcE${?g?%Yb4WQ+eWh0JOZ}xC->Pl-rBs78C zo9{bnP8zGNr{whyKJ^#`^7f*hJ1+aT5Ex*7S3g{3TRKqxTnwgFoX`QGg5$6g4jZ+S zKX6;?FK2%|L_KL&e+|Ty*X*_OwSjZw%d^(eT|Fq{Lb(<+W7-L0Qsd?E|DygFab{lR z7^b&Dc;s|vIc)iI_M&J>SVNKpZ%bw)De@W4hN1GY6|0qe4O7$*^6LG)*hxW4n5G9_ z+-YY^$3ovzTi&!m&1oxdp-l*^|I(Z!-*r3s(2W+)HAP{HFIXgdTgzonx&irFVJ^Xb$jls_^yvW-VcMT~GOmqQk#hj|W<%#9X5)D1E|r7I3ng zct}R7<>HtS(LYmkl9npDYj%)YLTCEde?}>bn8S-Uw2naUCGXk2GH0$8Wt4_TU~L0@r5Vl@|9)MidoPbh~Eu{M{I#S+~r2K1Il`kObnmp0{*h=RGh=hsdp}z z8pRCSh*=B-)mUwcRRvZz8X|mX0wd6evHMxQBJnpPz`d6LV#^rXtV6}(DCybIiy48h zA8XkR0l>A5i`1yNL22bFq`d7ybB`Uyifn)W>b9Tue!I`*{i|NmYDKr^jotbsi;LR` zWtlb432zhceYcsu?D{n6rmJU6%O^J8`v9l?*(FPxj>}cM#l3)LCa{voG3(Ci%B^+ev9D|X`Y`);McAWpgDsaf4`2P@t>CnGDpqQHl=T%RWMkN2H9C*QQSbk(2wvX$qKZ`YUD{15VhZp+}y zJ5yEofEW*EZ6%|o&oeE4kMS)@M)_PjJh`W`XR>F`8-APZ1fIJsg*_usePCvC2ETA%wmhO*LKRjXmEYFdGJ_rANv zw%4?F_pYLQH6KgmO>>mrFVnFkF2Js2m{p@sirM}I) zzk}}T5vOF>t?#sYHtTN9{s(PFhmhp*ZSw1+qHTGIX|a>&(#S_NW<^ledQ8^wlW8aO ziCY=9{Z=VeXBnlC4j3w}-_x&{u!`)k`{!orxdB`UbGx-G%JG-W(n9~3e%Erm<0l|w zyiu7_P|+SOm`{O8J!qBxix;ZRF#L#Ir)a6~z=gIGomr->5}u-Sdx?V;j*j`%;I{D? zuNTyc}RojiShyJMA`x6%#Byg84pu(p6=YRV=3 z)Yi1@(W}B8aT<#uSK#mSk^KoyRq(hSyIpx@yxXc~=i#bVxrZljp9E>J(UNSD1Qi|xej8j(F8tU1HyRVbGAI`?c;r>DhFYeC!`@c}41tK)LF zYZ7#<{68;G2@VL~UGvr+6`Rl14vg_@H4lT^R&t+LUO9~C@zHl}_XAh=`x#X)ft2L^ zMlT`n^)_CcOKoy=*hgQJV3UYHMEPG^U%fbUF8dUdSC3qhr3rm@11mZs(5Dt`Py!rO zb@B1)j^n5BwS@CV=Z>lz{neycl^dU_Qmc%!Cvz(*LJF~sppok|P9=lpTNQ8i&f1eY<>9NiW~s26zxH1y6hE)AS#DKYU~nQ0M0fD|5RSo2SMb!6JJGRITRNOJ-~? ztU$hs_12q3knc9*c$jZk4mfYR$aE)%^JDW@wlnVXoe5(gcW3cP+Y<4}D5JlEN&&Y{ zkOGCJp~GI$n1qSjTmdy5u_5#)-P_Tq|S$KlVBQh1-H(*Ffgq(8p%jB@H1 z_byY1rfM!RJ^zQ8*JKL+*S+5~PipRCr_SZOmc89dr?&UGt)Kngm*Lvo30caAeUo&kl$z{F)F zSn~8CE6u7C`SsX?Aq@v!c6ak_c`U{rYoE4{LgJLbgC9)Fcg?Y2R^QKCc9p40vWr*| z9a@I2*J0f0lJET#8+m`SQ+^<Q96FUbU3;Z4yf}lc9)LYoC9_uea_ds7zNF zH(q`%t+| z(fPiYe8;E>+sfv45N$i+;>j`6qxJ)cUQET04x%N{x-R9iSXouFaOwj07=Em9I^lWh zRlrV$)*)85h>_|1=Z_HjaOb4Y0;q(!=N!3~`Rv@v8StTu&`I0e5lsg#65%71s$959 zmoWTxPdMWq!_GQqYq|8N`s-IKUwf+$@yw`m5vZAr0t%GeM)(Z|{D&#*bXktrv1SoX z$U*Uv%8TY(wT;J)U8Clu6;oW=g3GLO`CfjGsgvDHP;6xKf+WiOyb}$+MTZbXI!}%2 zZ+n-ZIwRP-xQ&iQLSs+M)>9P+i_HeMYX<&5A+;#o`xmd;K(61nMVAbYMkFI_vMBZ% zNXr)%*(q|m#I1(1bUBdSPCff*q>82$ZHE8#vTM{AZGQ}$7SppcQYf#Zj+kz!oh2Vj zLy$R)Rw$S-^+Qi=ba9pSLN~aw`})UDJ@$aa`S&m&fuZLeucNwCx?e}l!?8cu^16x) zO!HQ@`Q^ueJ%immj$O^3+kZ8WGF`bn5^Yusube*%4SS9|%&IL59Fb^$o?8Zimc-UH z70xV1)mzA|h3g3qJQ^c~_F%$;^vFJ1$Wq{mfXp{sY+wzcF4>Qe7h?W>QA*yy8#+Ba z$=BgA$vbeS-Q|)isKfFrM~Dp=7egNz#O`N~N^|x(!7Ku&D}ir*Or3cVbIc?*&x)R5 zz)Y4-Q-8m7fbb3IVxo2>b)oQGd(>{V#PHXjXGvWB0FYoJ+<^WmLDL*qOhoraRm01DwO(%L zrSY*~7B#t8BdyTmL+u?JRHaf#6-z|>E%(lZV!_aI@neDPY!p>C>eOIQQMJiVQrV34 z`sJ$I@Um5H7l-goPK)(~l;`D0R=XotO)hA9s;QvVPUD8?oalAUu5?yQ3fk&WR2vE( z&;0#&{j=Q6M-!0Ex5s(hpTkgxKYJFw5fl~H(Y?Pns!hkT*c~Ni*!10PGd7eg)OBB_ z-7?~8?lkjAvvP4rRpoyafE@rl=3V%M*S{W#U2E%GybBxg>4y5PZuO^)wkq{zL;fx? z`n9S2yvliFc4gglj;2E0)CQk~{IXqn%=iiiT?)f#2ie*gACQmi%k!~3kS;}e`1x|% zbd9puirYWq5!X77e>cJAzwU6mHWh-U+*O}hKHN0*i;ADRtkC#jVL6`SD;i^pIb8a9 zWu~ibV(sw_9ukn4W}6)?A~Za@r0GcZf^Ea|Ie)d7l!=dbG_}|-{Q_W>PGC-Fm`Zcy zy-ikkZh*5RzMpl94;IBf%w%P>H4er_A?2{V73yj$MhkSUC z$w+`z#)B_Xi^O@SBDOqIezy@xTimH7+*Ge49w_1#@Xc=YPX|i3eFjHOzbuIf-OW4A z(*wmWLvf3)8m3q)lC|A*H=f`W6ipv!lcUp<{0V|y6ZI8e<#VEjBs1xVKe-CV>ZA} zXE0yu7yBcK|Fw~jmsm?w_8THheHpKej>^Ot^x`Ssfk(QU;;H@pbaV9#^s@DeqdKzB zyjQE4c+#qk_n=PlF^8_FY)dGnxXk*A)3I?6xv`}|e|-BS8Bdozil}e_UMwzc%FTTV z(@)3W=CuVVniRBS-rtJHTczs=4S=dTa?431pC4tJ&(Je-XJL#5MGhA?e`TM_ZQ3Ai ziA_K1tZ>sEW`_-O(ABOF$`^B4m9vkB(r>Jfi|xpDX!cng7_?rp-1752_ULqXm4#i} z@^nXY=L0Z2LbsVUbn@k1yYKETlx06E1nn>0u9>QZITRl28PZqH9A7_@u)QQj=W0W1 zQ9F9oiDKb8i{fC;x!jG$wr3ZK56=_K$5LWl9Vg27s`#+P>Ya%2*m&S)aGZkOBUIX2 zrkxC0OYiI6Z3R9(-;o)WEFafo$SUcv&5mV4jWY5KrdGVwiU3f5nkNiL>kYBxb^*#Rid3a2`dTJ0A$eZ;n|Ov`T{2a{^HDW zOM^h|#Sf+{uxq@t{$62NI2^J8`$LQ0R;LRpQ-QTaouG#~%5kp#7QL?R_!PnI`7zwR zVadw}*Q=>Ix9PwxwA*w;^6Pq1bBRBPJ9C7@{xWGg3^BH_p~bhV*<%1dBMi}zn44B-mq#xtQO0YdpQo_a9 z(HKa(59h8Ps`{ozkC)sW4wUFKosmVsHy74;HuhV5Z3qOa28MUrUXyfbs2CT2CVihH z4Ex2AebwaI61q-Ir{57Dqn^YT*0vj#nOd~gwS-|lrfrisR?zdh;4@f#%C*z^Gvsg{ zK~~=#8@18>7US6|_Ok3qW746d)D)@Mam-yCRorOSZMD|`x#u_pz0@~5IRncUnPnuF&f-n_G=TxwE?li#2Z1~Ul_^XgX;C+&CptnMe_VNHYsRNckH z^Vrg4bZrJ!`7AkC21euCXX_RsDM}BJDpTW5A_&>38H@8o{cjHz$Yz&kXE0Woo6L$% zB%!1V>DH*$U@~yjKuZNRsMHBkG=yOM*(18m4FJ!dRlN}6xb+q)&vJ2HSm|>7GyRkD zshZf8n!C=jXN1{}QR?eS--B=4c39{GH&Dt-eaqkds{6tEkXlpFV8r3ZENFXhhhJB0d|`PBE5~g=FrUHjI)YgWRmEm^t{ZyO1dlY}G?$-9+{qA({Sw|;?j*)N9y0mPw|0mL=;|EiXZNu|{VXBKorcmp~ zztpVaj%NJ}+S{JH&Q@Rzj={X8z47V2*m1tsWk(o#fgx*sN}Zq21fZ|Lt#`W?&e z(vE$!7o*Y>x_xC=z8MbLKyIHcxN6<-Yl#b(7syzR3CI7Oh8t4)%@ffAiB@sQr#h?p?4h4tz~9sv|NNZ@*pe2*nHu!K{W%LPPbYX_vhXa%h}62ui~|sM#6D-FK?uFV@m5@DW;;?BsNb>P?{RW={1VJHGJFXJa~VtNN)|UPB>s92@^O6)6|5 zhbrZFLeGtJIO5-RbkhQ!5G)Cz%j&F#cEi-^gGdZR_D+%jyI0ar>FNaJ`%4z{NW#C=TF$-Qm?Px z*~Uj0&liX0AaL*&O+h=`N+`xp>+ks%4j;K&%9B>K;5x!ngbCDOa@A_$zpaUzDwgJx z>$`1nyKUSSXq+FKTXB?Vkwi`(JzPJYjr6%}8F1m;7kB)%6A@2)F_ZNpf&M#Q_RVgF zBLbmyFH!EJj?H`ERBPr>nclo6MnfkT6k@ui9>$1MsZ-SZEsLdIV2hiTclQv%X}7C8 ze3X((`upybmZ$q{U};`!_0ZtW;jzQ)yvI}CMyrEC~Iaj-^72K#WjE!wsEwA%EdS8fFdRm?(IVTGBl!mUs5&!#)nwNYU zInuNG`{umr@r3K9K`sS#ak+TEQuyJ$sPx{tV#kZ|jbDMfU%knB>N?jI0E4RDk{t%b z-^KuBi8z=@UVSRz5O**g-k^VU*(t6en1rx-HCRIp+g8p2F|COcAIr3Z+(jiJN(&-T zg{Ck@7DSMaZ7}lCQf->{riL&&;Kdgy{k2I;vEliD^un&Tv+5prW@1E+396R^S15m3 zGRTdI$Nwa|Vgob*R8;w_XXvNKz{p^@Bj5C&VWI&|-Nc7{Ot;fB`aJ+ZGJ~2LRBF{)D8!%k<_LQVczqqSd9% zgz7gnO=_xW_pb733l;YbIAW#0s&$6v6UYJRa*4CtlnAKhYbh##zSrxO3FC8L~F!ALhyqqt8M5iLsX)hZ+0yGSfv*ZVnF63Xo z-v$&EeDm9L0p|L(jjuEHi+L&OX)_IV*a0+4K_z{@)-9gJ#>;n7TIXFX%UR*RP1++Q6; zjWTI#zGWZxr}A)@k&`DG_!AaK_C$3Yx3tCSJ$u{05M=sI_6l@^O-njHw7H0;iaSck z^#IB4zhjp@T}VYU>_bUPGykv4=V0Jy z0~~4B{~T&!76iSdnZW)M`9B{-@H6Jd;_bl(Le|WgZ=)cA6ZC(p+3x}hq!pyJOJX&O zV>QZRU8%=!)G}l7a$^@Du^K;-!2ge$9x+!>WhoTqRFmS4S)UZEEP)A|5+}bu&;rJzYdO5#Ab8Qc#mHht0*c}II6@; zm=dQr7^euFt_=+&FESMy$8DmGw7X-X^{|+ zY7Fh?{VA{93mOe~5e<42XE6arcU|Y!1@v_lWQAzX`clVMyU1?C-3a9ucC1pbn#sd0 zI+^7i-{Xk-b+!B)b`632iEYiP=jK~GxaS6?9ZvtzWV2-u))oI$Uj=5RV!hq1eYeN6 zC{Ledtb>Yfdxfzpf~nmq>{u&0^LfP0M81u{n9#g~#sD zdGgrHNjocWOl&t$n3R#$4}$2k<44s9g9f|}C0^5PDoO=p_1v@MB2%uO&oEG_n$fG_rJiY2v;h#q?-e@Wi{dy>3t%7 z_{|`LzM?fC>E!=wTQb(pje0FyFnxtTW8bE(v_Jl*wrDJ^V|;ThO5cn<+Qs)($qWnU zY+H^UEIYzAOT#AYI5}rp&YgD;AbVm8b~iCyRLS%VSJYsun(=KnvnuB%)tEBPrMrL| z&da4AHeQ+{k{Rmt^nqAoVbp8?o(EHWy4lx$bxdH(yPwk-bjsLs@y&H?vwI)sHvCnw z(@kdx*H~)C{Qhwn|6YezOPLwgmUe!uH#ZR6?y&sP?@%6*?6uFOf6G4X>6zOv%drVglTioozHF zh^vA-V(r#{{VxNwmMvWzwCcc(Jia;nYuJ`CJNIVn%lx>cP~*egGV5G*Mstb1g;mUd z8(DoPb6tqCU$gbyx~=b8fB*dtTI;SkjgMhz$iAReQ`4otS8r?*so4))G1i#twUW{8 zLgfK1p}EtR<$cLK*|qGc#ex$ar@0;&D(~Tc$5UYYL2*KQ zi@EW)>(a-6&sg_0T)8*9(Oc-pyt&TF>laS(ZcqGd^f-U%^J>4N-%oFN$dWMeV_$98 z?(6