From c8495f8c4df9fa397fff192c13a687593c9b77a8 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Thu, 8 Jun 2017 11:57:41 +0800 Subject: [PATCH 0001/2305] update ctc_beam_search_decoder design doc --- doc/design/speech/README.MD | 13 ++++++++++++- doc/design/speech/image/beam_search.png | Bin 0 -> 474749 bytes 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 doc/design/speech/image/beam_search.png diff --git a/doc/design/speech/README.MD b/doc/design/speech/README.MD index 7304650e628..cc03aac7b49 100644 --- a/doc/design/speech/README.MD +++ b/doc/design/speech/README.MD @@ -140,7 +140,17 @@ TODO by Assignees ### Beam Search with CTC and LM -TODO by Assignees +
+
+Figure 2. Algorithm for Beam Search Decoder. +
+ +- The **Beam Search Decoder** for DS2 CTC-trained network follows the similar approach in \[[3](#references)\] with a modification for the ambiguous part, as shown in Figure 2. +- An **external defined scorer** would be passed into the decoder to evaluate a candidate prefix during decoding whenever a space character appended. +- Such scorer is a unified class, may consisting of language model, word count or any customed evaluators. +- The **language model** is built from Task 5, with a parameter should be carefully tuned to achieve minimum WER/CER (c.f. Task 7) +- This decoder needs to perform with **high efficiency** for the convenience of parameters tuning and speech recognition in reality. + ## Future Work @@ -153,3 +163,4 @@ TODO by Assignees 1. Dario Amodei, etc., [Deep Speech 2 : End-to-End Speech Recognition in English and Mandarin](http://proceedings.mlr.press/v48/amodei16.pdf). ICML 2016. 2. Dario Amodei, etc., [Deep Speech 2 : End-to-End Speech Recognition in English and Mandarin](https://arxiv.org/abs/1512.02595). arXiv:1512.02595. +3. Awni Y. Hannun, etc. [First-Pass Large Vocabulary Continuous Speech Recognition using Bi-Directional Recurrent DNNs](https://arxiv.org/abs/1408.2873). arXiv:1408.2873 diff --git a/doc/design/speech/image/beam_search.png b/doc/design/speech/image/beam_search.png new file mode 100644 index 0000000000000000000000000000000000000000..7f7e35f34223162d0f7f0ed97375909c43b830ae GIT binary patch literal 474749 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%0A#nzZ(vugN77v$ErdEBu$&%`M0d}E z+4Pe1jvkhSG@6}ea7{HDnUnQWm}Pv71K%onY-VfWNsm`=#D$9HVeG_*2GbB6In09V z_M)RS^waU9zzLL$gmL@G@!~^g+eO|k#U{r_`sO04%dCU`t`d?LxX8F7jEr|AsB??c ze$1-cw ziNTSQk;N$vm0fT+bMaZ(L3yH@OVf;P_x;JNd@?`FJGdxMOhmyK#Vcu^ov~bA$g>1a zEL~JcqBAs`o;+JDu{UQ*xSw$JkTGPYrt5&!Fp)IQW_}Oaf z7a_AFbXrA~mrg^X3$VSPx1^*rfv!Z-1c5QsuLGvl)wKlNl@TH-OqdC!mjfEFm-tJ6 z(FpYE60{@SaQ1f$h0=E$&AYpj#;;La#iWb4j?WTh`r|%(&F*_-&4vdi#}y>-gOZ{M zbj81=9izcdm4ZFi*?Qc%DKnPNiG}(2%F4+#w4}t$N`h?-aWa$1o%Q%hnT9jd&5A-7 zap~EXK*cGSUhm0yNn54m~^UtPY!cmc{1s$N$t5t&zH~i zW)EwLx_3(E z2L-WTyDDm7C2+)0vbr=ISz6Mzm@bzGHd-NDG<9b^UKx{Y1Q&s|#Bf(rQE22DRdc&A zm@+rROWBwzHN*2roMBlgl8{drCN+8(7+8oNqa|;WPcf|uiSoL?OQ7Ef`BrL*=zTOs zeW2tv-$du9WlB_g7nB?2F#R-Xizf?k`TSts3Tw=>Ar@1L7#|uH#QJ9H)Md0BV@+$? z1Ye7Fwkol>4m6n@TVKyi_;vnH5XzDGo|}XU=)tZIP%Xaf5H`gD3 zrr5%M&7N?c=r4q5JJuDz1=HOYtx)wA$dGlr`&6g8p6kL zm>Bf|(4`J2I6pX;-#vJS8ZjthdKA%QEq$WL-l*JmlB<(;zLH+wAFKz`=wqK91>D8G zV@I&ewNKu=ob;A%9aACsv^4Hp^L^n?9Pb&QPi(zLwQah|Q`7Fe1K;WSO6**U9V`_jVE9gmwQ*=>sS5y1al`sF8t>|_KdC}vM-S` zrJoy%xJJRi9YB`pX5>mK3~$0puuM2!Y*p1z{OLaXoCSV5e?`tsXtY+Ij_u80_*3R; z2w6V@1u+bN6!nii%H@p=8=YRRp=Zo&6L@!GFEf&Pe6^BgncErF;%F65lf_bII!mck z(s&XH7aGw#8Ev{*yTc;^E{A(kfICwr4!T&J!rdcHQ1GwNy0+Y5zG2R=zefIW>UDG7Jcu7Ql%;i)-Go1;$s zo_E7Igm1g=N>()PszhjXW-b?SUE<(=+9MOKPDXk=Yq5v1wZ`tA1T-JRnu@IqkH=H) z@-Hcbk#R}OFq+AV$KoNWlZD0WBg)BlI30FR|LW`xS$OYk#Z*NJ7gP(y2e_?L&lK6k zas?TxBZ{J0xvuOGFFL%v`^@}zeaOUKK?6la7E*3+QC3)wxzjS_QXzNbdr!LzI_*~e zmDl#|duQuXx8U=`^?tHapBs z&ZqooTi1M(r;50Uz$5|8`EpefaE+H+l^q&j8L4ja@7$yuF99w3x43kg(hYEEv%5~r z#YqvN?if6#7XI+4h@#b1b*LDYV$Q0~)~j*()D!D=hlzx2G3v20FJ}kKm2|9?QgNIP zY8f1mh!#jh!pG4AvMesgxasZN!dx?X$drBMJWW&w>!s-R_BO?%`Dr84B8nxl1!g^b z4=fbPK7iapv5eN~s*L=Kz<}9lZ1bUkF;#^Ad=OiTTuou3fSTit8dwip^ND+bsxc+|f$l01ZM-IUww!&mNjW{g@jyi`#wyhrOtvunvY886Z~c{5 zeiS~DF#H(PAfG2*I9nojy8J}#wDk$%&ih=jYX|K>)ps>tOxk3#5|6c1CP){}iiysc zOVBg2{n$GZnxDT$x1Ud`sxR`S*&e5LAXyj=QTXn9JrBBbf->t!F;z7K5p|p0vdV)SLv`(B`Qkz9Eo=M$#aQWE-m_vJVRi`nJR@m=Tp)D;iyV zl9?v96K36-R{czOZ5wJV%5qPAH9PF+U2s zZMj_fIRt>ZADmz4=5c)@vr?m5IHATf8u89zTAB-mO0AS+(NwCRV6s?i<4kpAltOln zv0jZ0ZRTh?lbfClxtYS#Y7I&mvVkUgtX$~?R=!h9xKuW5ZE31eTc=!-%g@Jm?~NvE z@4s?-W36QdprkKf$b*N)W=cFyu0LS~;kb2Gsn+J`;u{BSsY5-wi?dbRZB1*F-uMxO zFL5fRL8**2ITKr~*F!EfJNhY*= z09(rXqD2?J%i>kXxj>zs1us2*0y@S9omspY^1#nPyeVncGIP`{6gAFxlid}f`7-$a z)?gnV8DB?AN3*ZWKw>zGMd|C{5`HS6>S9ddhq4gRWY(1;VM>0<;l-Zt*h0a?5W_+b z6emY3=XvN@X!o$YTYV(?yy$}tHDM$c_^IA@(H+u+HV*MCH z+!&-RS{)HMuuMf{i5Q#gd<@^WDERbd;foROh-^!qXmz!66-mU;ZSJ}UePF2}>QBX3 z*6z5B`$5eT@qG5@FU53C`ET=x*2{)ceM+>n+~Sv6pmql%@Xuy2_jpY@Bx)k;vhsunL)v7s8kN9w=TWC4be%2fI(TnBTt$EHd0tUGcdkdm&Gi zO1C)}4P^W9W&=j`s2UEp)QVA~vUmfFqfYsauM~$MNw>D!(|vS1{HI?YBnvLhM~+N^ zYu>mH%$HwW$Ika<*E?@;z;2-&)1bJ=R7e}#c17g&kt9qBs}fmD0L9XUb6q;8Z^h1y zPq)TU#xu!^M-y?h5yiX!?lW^Qq&QMfDiw2?gW{P&^S}*-GaQ}(hx+EuPhM#JuhqY8 zIO8R5xv&#`pb7XNNsvB`u1ltmPcd3&E&<7v6;An>5gI_a>mfJ?*YoW=D~vZ4C}YnU z16O&o%6YuRZwkav#g^#!^Fq!vBEqjqOk7^9*IfR`lj)*57s-iW=(Q!1r8~Im-vjiG zueYQgHkwK6E|nbF{-x{3uA1GUozY0uvLgo@FiObxl>W#2YM0#qpo#JD5yh3)DcxjoKbmT_|rvwQxg-}35c$)bL~&q z<~vFJbcQgVL1fI{Uzug7YaC96XANX?t8vvvc;2DQAD!vTR$NkycTl{cT_RQ1YlaKS z>5rHKhN4mV$*5UI@Tr|m9&Z{UM8bk;(JY>QUg><3QQ-{$`Ss1Ti#&Z*$T)wer`?*y z=M}!QMm26s4KBr)z5}!SLy5&olQNoZ(s*{v;~Rm{l@G}bKu!q|l^pH5OK$X4MJ(&f z-|)pfd=nq}*C~r1nKYh6)rAUCM&b}e7Ns zN>-Och5?SHd<@~u!r>QgnXLPWlP!%5yY+gjKdaTcj6NY@eQiQvxo{x4Y;Jay_30&{ zu2pM>a1%C5;d&Cwq5A=oPTQ4K@sZa2C?v8uzajbkYI{^OewVO>ilvg!%6j45L+*rW zO}uR+<*ysY_+nXXk*f3Lc#$Qi^jD5~`QD_h$E|XD^M|_*l%mC0Oi#_V_2d%GChS

fJoD6N#bAx%Ts$yaoQ!%W|ck%4;NZwf??q4@tDrzMo7#f%lE*~S!KN~Ld%2;bvq<{aOK2Q7XPhJq6^OwOkKREt;q@jw;1_xIV7sgpb zrIsj?N+HvFW{P{W>ADs&e>7{33ZFKzDJ&WcGj%$eWz}rC0^0Jnkd9K1z1eI6Yg4y4 z+e=e&I^N{CXQh^~_KeAe3~W~zprV@`3StR9dh3TeljG0lv;KDiT@lmNlgUMo$NCP?i&D@lh zk8+fV{>3d&yWKH2W)q&I;~FS?1GuLC8!I0UTC4Avvwv|fYo^QhtX}CYcjKJX@r(?2 z^-V5U-wb6u*gHv4@;Xe~xbGe2D45$E!u+sXrX?MwHc>9dR-h>x9v_VhI@@qMR6__`h&ED)iN1dY+ z9&1&rZ&e(MR}XXw+eL6<_({Ha0Aljpoz?{ICK?GRRoBBSnd#{%wG}1>)Bq8E9~$5Y zez(E)Fjpo5dkf0&oi#ItBCm^!4|B7TVt+gcTb$6V;6}jPpdYz>2`l0pRoG_Vmm#2#mM48Vq?h zy{3b$A!bp|w(uMxSYGBf-cmZ~tRNAjVCp;gY*say+A z=l#yv_0IV-hxnt7&4oTQXgPZM?IIp*oXsMXj&>toWX<7})d-0duQP~TE)e1Q5wF>c z9Zd$evKO1(4L=Vo9VRu9hb?#Lo1X-HWo5)T!Jy$z ztBhY!5U0U202$jfUyfN!T|>PuPZ-(jc8<)AJLcpEh%`TD1iv1=HhVWZwOCig`g5~Y zOmeNsJH1{r^08MVCYP^Xuxo}|GPB!yBSwe{I8Pjfcj~)FA%K)7odJjrZkl4b5+BVj zS$by{;}*@BWH7kjdx!0Bn97-Xh0AEREUEnv6GFv-2F$B#*JF?C*^?2O{ZuGSj5_B! zIkM{dj8v`L5kRe02>7LaZ~*G-9p!U%G0;D#F6&b_MBrJ;#2T5vx!FGks>N{E#Y=H* zRWlQ!*^c>>em#<_TVd1Jy`Yu}t0m?mz{hsU#Ci%2j_zsJ^aZDK!v}#4|CN|ZB5COQ z!2&g_VF=s2XMXJ|5~uoh7Tv0pg6s2onmJ-3wgl%jl6;n zM)(JCV*E#R#H%1%dtm&GSiaf~62^3^#(hPb!BJ2HVN8BZ%cx=$BPhu?&nX(O%7=Zm8W_}x(r z=k3v-2jX%}^GpL3?I;ysx<)4Z`z8R0Xj5r4$jAt|`-Cmdzre*-m(C)Fu;~bBNTn+C zt~50_a;1YMPnk_jp8qB<5oiM@1|u@Q%`1y{+%>eMStTM?W(0VfU4aoJ&A&HeWp`_T z{I_!?-tPB)L=pJ#ek$-g;RHLYNuSvm)h&gDJ7^69Sw`*!)G~L1sa>T8F zJmc0*Zh5uGKk_LbeNRp7!7$T+z9U;CbSzsO%jDl1wZ&Md;22M&3SRauGL4$JJz)&x zDM7&eLP-JC3@-{)Da0|S+&>E~o^t=ZkaIVUB-I%0)=B*$UGvhnT8)*MyPYLsB>Dlr zIkOuhCt4t+gSR3R6-)S~FEH|jGXjnqFJyBtF;t+iUBsSg;WE^C1K7*g4 znMr~fiMMD8M1M4@(%kAcrwBv2Ummkh-&QWloZrk`dAvQKmmXuay_^Q-wpg_FImd4a zn{B@Wp4{N@-LdX$6jt~)N~;QH;7*TM_+%WeHkK*8-fzKXmIbHtYy#dQWG$kR22OZy zd{7@6 zz%TOM8&9wtX?OJK!s~iAnfVaTVZYBB1Os`6&yG61~;<1$=<&q>5Vu;OS4c5qodv7G4dd3 zQ6ny2>Ceoyu(~WuM{Mi~Yb(tH^kkdvaCI~YMLkWp{+dKGKHihtJ@skbG(|BbXsJ~W zG@+I`N4wdr-S!UzEVnzkNZ%4!O*9EZX2;c4bfKQR2mAE__&e^j#EEI+Tb?H_Q=y7< z6H}94+e7tJ*O=q6n#uVuY@j)6w7F^R2B$&zcDsujnLMs=rZe|FAH3cl3}0)H%qFm> z(PT%G`O<0grL*PqCZBFV0MMdIyD}xN?b}g-84^zq2L~QeR|i9zX#!|2xW;azmXCY{ zyC?if>}|LtYE}LSMCe<(gD|Jnd~X0}RGgbM|gTl@u?( z5V+;)@>$f8R`octZ!}=Axor5MLW{A^)Z1odJ+;=2*~=h4*9chtE#qs-oAjL5CzS)` zXlhEH%kf#AvJ4aeUPJ`h2T!lRR|rgTJnG*mShi}hul$|Zv+Dh;b=diarv@PL$Zz8R=e~1#?%xntrm;OXS#}f zyolrj)zqx#o*YfDCowl2<;G-w`XK+M&M$Nu(ku<>vWU5dl>uUkVSpQTl$*gAc zrdZ8-V-%5gG<0?_A}^VmfsnQx{ps#fSxdh9r`~P9kn}bfg!}F8%%50gHy>}VVtBLh zgC28H;3({TenVf`gFrgO78jpz|0&7d`Fxf)=`XC5mo}nb{t=>>;f9&0&5n#(>RPOW z|A()4j`F10)&{$5b=kIUblJ9T+cvvw+qU^DtIKwE+1Av#_q=Cj?w$Gm&6R6qW<>1B z9eY1`7SXz4-~cB4C3h$?7Y=Ka(=R!M@Sny(K2(=4P>fp5<<%Ebkd~@GLN!|23#Hkr zwD?R#Dxy6ee#`~5*JFib_KNtHfvwQd-fuDS95O|Xp-5{`7L&g+Xg99|WHR0rV~KaP z*E31G^l3^Xayw%%px*Msn@UhBit_QSuzB?W{5Vs2MgM?;CM2z zP2K%IS<$sFP{r4`Jk_*^ap3~X9NJ`}N@>%VX=&w*6~QmNwNGQXnw+nYv68}^x{!c! zxlO}1_TX-UXKoy&cxag~affEU+F?6A3XjDd^0%tAQ<`=HpunxYLXPDlnP@VT~P4WAz4=M3DC@&?)5E`yO-zAczO4;B8q{)QEEc2>toOIS1ZZ9RK93 z+AV-Jvv%}Q?bLoI8d&p$F708k_nQa?a{4<~9YSP9$GiTTH_2E*q~VNG>Uou%iLp>; z9DkOu{|Oc?&JWvl>1Y+wJmh4ZG2BZv?NddWO*@Hao^s-;`j_pkdR9LO?FWiS0QJnj z_8XAx-Y3Z@9@&i)x@yLk%%J!4NkQ|qCkp$g+(V4{mDnp4Q}^Q!501AqW)dlw4j%Dh z%W#X*sQ)16@*ttQT!CbepFq#hqebDgv=&aep!^!bltQ7J$$-UFr+#6ZbtYB2dtUvB zqSC4V8|T!}x*TPj^1aubgEsA8t25NIbG#MkUKgyTJM5D3WrBXidaY7W=HAajBS0?2B22G1nNUw^N$>?5H5H{R0JVNSHkE zJH66@J{Z5^PrR{SsI@goHB#gbffcWAvA-%D2)S?AA9miGo&F%$z?l0xBYb`y1Sm0U zL8kQ_w&gH|!4@l4Yc_a~68E^zI-{;{us6F;_8M?a+=>EdZhgK zl3OMxcCZLH37BYqK12rP}eGg#38O}Yb zs9~9%YKH0)KkH25rf+dY_wH=oZU7sU?xl)uTO+LqaY|iw^EG;-L9oDBcH0>rZx+EL z_44TfK{*kcw>WSeaXJ$v`9A4CyF#Y0Vi&oL22wkBF$Z)N%15eVhHh%n3yh$|(wMjD z3eEONyiQ2U7t(7cvkcpMbylhGle%J*q9ALTpN(G;S<1AqH@9-*c$a;Q z<20HzDZTp0MqZ50=F80u0Y+MM1K|t4%n&U}c!R+EELFcrS=?Y3bUkmQ`&r;FW z6GW6=n5vf?SXQz8qXak-G>bQKoK1ad7+ZzxuhH7EI>%klI3EQ;242cnlQv!n>nS34 zu=Im1)z|PxRh-l??&+bMu=pNx2pXU)EgI9N*`JI$G<0_Ap}MS5^~MR&({>aOj6FdWgJ$?Lsg ztYQrsJmN3@P5K?)WM0gUU(S~`68S9by7~7S>g97~r+jLT7aTJ6n z&{Wrpym!}E4qs1_bL_e0Orj2$UE@K7(~*s-he7^VMFHAu#qNiQ-=2hZ2fMap@=7IL zB1M|aTTxDC@q5yp%b}A7ip=Jv#6}4++85lHHE8O45@9n<$P4>_$2<%&noWQnjh)B= ztyE}1Op0F$nW2YIvD5x1N64FzzIVoK@(Wgnv7dDoJ2-25oCMpJ$qI)xw=dB0=H{;L zbB_ZII{atslP7C3FE4j=&IAGg$wE-!)eSAipG<~NrK|b=8o+FFGZxuO&Sj5NrAT^a zuyCI1FgRlmV|=8EdTurt0jV+>)YqqL1QpPFgDKY`#d%Y-9^6XIelnr8KYjNo6++{9 ziXvCy!w&Wkb;(U!#quZMkay2}N&0s5Fwe@Kj+A>&D&wk~k+olhtTP@%W zYWH>b5Te(|49W-7ml%fE=6Qnxf4Lci>-Q0e0O2gkinb-)kojD+!%(G{nXlwfwD*-9w{nTIbl}VWCyqB*RC?25s=Gr_I$6 zxg`4U03NL?&i)MKk;$6Tqnp^hu3i<>#6Ma9=3{n5Rzh@1GU~%qXQ`Y&4Pwv0!x*Z{7&g(NP{$RC;<*c!xbB|qT(?9pi zUyn`0JC7efzwMWs53SeBltVvl>fH#erKIQQ!}SQG4kX@Vn)mCvnsy;3dU)A;^4|3= z?lgFCDT5lm-BQ{gUZuQJsqE2aS;?WTNXeB-96SdFKte|(wT>Sb!-n#}5asQ{l?iF` zdQ5CD%HZeQ;XdM-blUP8V#T_j0-BnfVBulUax?^}7dSkZu}hcesqDX~n6Q^fP%sfd ztUXX>=S5n8;}A~e&tIHNwm_zuDEZXukbC0Gw|W&LlG+-xd1tjRJTCPk-A3vlGThP- zXJN6WaQ;nA?_vS2RT218PV7F<1yjv=nFa+wrd}^2^^Cy_Lm-px3vPNsi5>d`-ds&P zAGmr3MkkowLVSJ4h?b^{n8gvL9-l2hT+QX%jCjGJ-V7y|60Xe={s^7b^QDc?F1mI* z2=plT$%dxR=yKv>=Z-6$)Ye z>1lhIe;!&|UYc%Lg-IlWDS5WdL#hpP zNJVP@YzYVMY&rSmF|*~n9*88eMlc9;lxTQ!exrl4{>hzE2VrK`%4NPWggTYk8{^x} zRZwVU5!*Q0|Kvp3YVh$5a*I(yXiVS0fHqfIfw^jBwg@CwXqi@5@5MmEV}o5}8QA;E?IvWM#aUG1fsQR%f||P5g;> zARuVMz-WSf#bQY&%L6G9MVmBO%JjZg$9ucb(!L@Q3e&p-Av8zK%`GpsHE4zpFIWG? z|=;a#G=_>nY(`L|$=SY5r)V-$Dq+2P$&J^VsD-q?H# zO`s2QABd+PvSdjGdsazE9FKo+oGYv+BJ?Wm8UbAHhh!<(s)GLn03>h-$u$-j3+|rx zjA4HnUDp%<&2^fNLa&&rVzDYDBYlhY`RSVxvUkC74r*zV@xv-hPv*>r6FbzvyP$`dlG{ ze5LYnL%_KtO;?}M8p00WYH}Sg588z8kv9gs2I?`Pv1XP5JCM-r_FEs3m`aCd7RLM~ zLW=BW3>)TU65V?D0ICyZfu9S@0SX73>JO7&%9R9C2Q;?cCp)v+xBSOKCetTh zcBby+xGaIpM=(=T>q5!!cBMa|QcpkFjM(+v-NXYf4ic%CL5U@BdM`6~X2Pq!ZmWGs zid?DeP)zoAp532Yn%~&{P10J?Uq?UiByRE*dwCuX1V61f#zq_cM;zszH@nk#gIh&4 zPk2qV9&@94FXiGViE^3F*;FU-NbI;tr~pRGW*i=WV0!3xuu<^Rd&GYu23v(K>sx=h zQp`XRjH^~e=L~KGIyM2jyBBdg$4}SjaW*om6H_$k219kes}ZOM9aZOGyeRY@VU`>KC zh2N_O0mdBcLQlkWFf#h1i-8BWz4$4j3(B0+x7J{L!qxkC4uIZhb*RUF@BEJmNBqS2 z6#32@56)ctZku0#TMew`%vS%j`RgUhyNl{tXbxjdwL$xE{Xd+Pf2CQu{v|RaQx{pZ zmGQXNR2>EjdwTXeLzG!uE@Wlv zw1C?qotHEQIGciBbI|PHBK1JT*-?9Ge+R^&?hRh1_nl~*pZ+}n9^>q?@?#89T z`h>=*=|1Ud|60y%vT$<%!5Z?7YWdNU9VMWIZVly zJ^Wh|(_(E=e z9T}|Hpd+e61~ap!Q>dC&YrlHK8y0|UoNPM7Aph3GngeO9(;s|V&S!Q{dAyQ{Ms-Hel@tBB-bz20mHo`DH%eiLz<;t+B$m(0|Lxm3H8NWr`CujTzt zNYD z2&Vp6382tu--U>09%8eOKF9vvng2I_^U0Ek=Vo{VDQibCl3Eq6@W>cniT)ykCn7ab zeN>A>Mgu2%2G1OanN~zarnQsDUZ&H03n3}~>E>bYNxlOkHFdoJ*zEPg;fi*M z&xM-Y8YJYr*9v=L_-;<_WPdl)wL^7CUS4JBx=#n!$d7jSln8QJ1lTqKUy`IPM>n^WLtstlmtYxP=yowXh8{PCeG{6`>5T za?g*#`fx~$q4qk*HB2rqlD-;Eh;_{l2R4>VkizCRh9XZE0i>mB&G6bxd)#$>mne5U z&atCU)Y0J27i}#SFhR_!MqGlbEj`+!$xb*QeVI^&Ugi2~#dz<|BaB8H^MJ8*op9%$ z>vZ&tI(=L7w`#QDG6H>C6%#FrvXfo&V2u#wQS62%fw7>OW65rahL!@K=&KvTt<|-@ z571?Y)mNoYnOPz&us8IL4+0X1t?>Ttx19^RO~#`j&S!%^ zx*PYtq0c1K)?b0xjk2*16+XKj;3}wL|Eb}}g6bxIAEx^_A1TfhQ}@vq1|TLrhm5vD z)8Ay|!zgNr)40=kfhO-0#WaMOk7yjTHgB@*oHc*nOg2vusTy}?D@Kt6e0R?!P`@kC z+@>}h;JL?ykO;?6rsj}>f`N!QIMK7$kiq~m0*(9xUZ#d#JFA=b+wHXZZ1RrsRVEoL z{e=OU>)3HyuMji^ggE`zIyMHmYHT~Ji<$adTX542(6Il*P~% zi*(_MX6%UVSpHze$8e>c5xu*)QZ@*^oGu2T6ycLU8fnL30u%MpFP)bYR6)wU13lk) z{x)6S;vt4xRx?{BDBMZag1h@YGpyCwB-F2H`PK<{Yu>X=);8~P2o|1nj_Z@KG8Zqn5}{eb`WI1+C>4o9_VAr`4~r>sOqQ6jEj^ zSE=>)3mnR(3W6eM++?^bC3eU9D7a|n3f@>7#uUjMT*^t5tXn<+;Z)kq9POa9 z+x6~m5h=lTy&^ce=^lw_1tD<%u z+Q^L~{(T`A2};pDf0mA3^Ez|z^mVGvfBs@kwlR(pmR?U;mGL<8UVV&+lbLli^^M7Q z!6L=eTZyCu*&cWX%iR+dFkVAj*x)1oplzl<@R+cINB7Ql5vNap&v#p?fjGP9P;Lx( z_B@t(JW?voHMB2+-MzRxhofEW_Dp3|_mjLDuH-Lf@Oh)m+1k(4gJ508!5HM@}y1`Q)3VhRmm_W4rb%so*pfa4D)AY!G*85%HGFIdbr zSa(zx*42^CX;frjafn((^0~2hW8cguHDf!5EHQBeQlHSuH*^c#o~0l^O;YyN)TAPT zM$eULh%LzcE%W3}-e zGO!2RSsU0-u0iUdc}d^Xa-1s7bc{eFE3KrbV)rVEhhgRp9gI)G4MKsR8>}289K(^QZg7`q z8=8@*6eyY$B2xJx&>@OAk=(%;pkV@nf*UUER>%Bq#KMG7zdo7Xcz#bEZM8FBO=fl9 zZ89Ghq2xXuXQ~TJ(kMRaS;)=*zFOR5X~@>Gd{pXYy0G|cp++Ixw;~Olk7!0N*Klw; z<+MQJEQR)wSid>=Y)yMnt4(k8XEXh@^+Y0-Ld9d+L-Blr1z`mOrV4$s~WZ_!X`suR}4;`EZUN*3dX8mCaBHNrTy->|R#Xlp>op+Je7 zcu((>dvKd!0guq-^LxZa0+T%}#HoLarZK*RlT^$OB)eVv!u`NK`0mNUbcV%(fKM7k_)krcV%QI3BSzCk){0 z){!!nN*Muu2kNG%8sV?WU`@}Hd+{yO z)iL1(Z^v%)D|8R#d2FT$NE$0o#Uc=PCF{BsJ2uw*bT|^EHqZ(A4nB@q?U|R)VkR}r zcX0OnvdrKvL;y^wzoBjfn|ytE#g9i>i?9oU%uYF70GfGlsC}CnBn0|d9ss=Z@AN!J zuZ;Q&BCwt|iKy(7KS_3eg@AlY;I*?yiGZS+8`lbF6A3t@_(Mp*zNK|Po>P{v02MU} z40!)#?Zmhy#~9~}@?2j70qZ$q*OpF2OD4u}}tEIr978>k7;T^`ADd(I--mBiw`gd^Tl2 zE_OGz^Pgn@XalBMoU!8KkMlJh$ZY%$%IqeZ3QscWjnfH;WSX~LRgs2tg_PdRMdHp| zipGMj<}+S|%7hirdZ=4V#(J{|?9g18*A!d~Y2u!S7dnx=Xwn^+@{pW3u`OQJ?dNf( z;Feq22r5C+92Q{AG#PyulAGv;9CTLqo3W#o%{C}lxB}i7-(r)Pj==faMPbM|6MU9$ z?qRm+#RC-{&5CLT2B^(6IqJE+R3#brD{fPkQ5-lv9PBYy5j7;(tpmP*JQx> z;sQoU)MVkgz#g+U$Jow$Gpq}>Hn>bnv;w4=k41K^_$8IC8-ItXA8AGh;AE09glfBT zXS2DdoBZY=Ep-(6H`!z{l1g=rQvZxmw~kINCSUU0=I{I!RvhQZ%!)(beZ4{GxO@pTR9Bb$fIiL~7k`{oAJX`UL~l8uCFLrzvzEk# zkjV=|*BL!4#(#l$PMnDfb9SAPJpI!U$;KJL=aqv>`$;OJ=Np|!OaegtL!x+1jemP@ zU+CvnU6eCLeUp+%g3y#O&`L4#@sPEvh3{lLvD-bMmdx`xLkiBupdoi`;;DuF zO4}*PiNmwE3ufn@9iM+uLqf$At-ENnuBNJL3Jn}2(9j3_y@f^}uE9^&wW*dCL?bh~?70TvajY$@M>CP`Ix!IN_{iRMAq5Ra>K$*8fl^wL&-4Q9untd+>zt^~zwA z!4Zdrf}C`Avr`cL5`{{y8Cz{9t17mVd#7X}5v(`Qs%RQ0h5QVa({fA6I;@ zB4hP9z(SB%p*P=#kC3p=%nu!vWV`0U?RpwKvoZ_YYxPE)Q&31m8Q3bb#o3-5+FU!9 zV`!boq0!%#?DxlLn;>$h2q)g+Q^cNEuiIPD?5VbxF#9wq9PPe2fOKr=~pBa>%u$G*noM8j&s^t@OMl|KP$5MC2yZ2dV`NlBL0z9$ zlg*=e#n_m^eI!c(Qq*I@anz5<$mAO~hn%|x_sDU1)oj_p)ynChcOjy7nifvZOaG!z zan#wByP>|7Xw63)(cauPSw0@NpG6m?lpmB4DNu~}&m%w?qq?*$OCjaUNp>gW%(?`n zWlH$*4+nN`vpRZ@Ly$0}Py1S9H&PZftBL@^sJ~6?FwoFmgIz~gCD)~D7AHx3G%OBP zy$O#j&bzW5A1vX66co*Zy-W(C@wIrfTQ5rGIzgXJ_WQjD$XLJPF<*(rd@9xALS^2v zFL|Dhd#1AN$p)n*zA6_G#`bqOpN5NirkV=Egw({=T&81m!NL@krE$hk$DHCrc2jA( zX0RrKMxrzonwLKQmIkSf#vzMRtb9uWIiTXp%-A}%g$CF(1Jqmbq@xhH8{MgS<&DT!LPc}27t{X8A>iywJNtZXtiw4>53Vp#8~HQ!rGyr)oq zwT&lh31vPTNu+Pg;m$|U&`P)3KRI2z6i;l;E+F^)Y4y345G{~%+|ratM1OZi12#lL zKIjQx%rYFH=yksqlJyWY)!Hb7@cOOSQ`>xY!-P5Z>`PqO?Th|~c<2)Hjo}+*1b*S$ zsNrKhnM(!dhA+++f|&0M@G+ky5X0hkfv?`E%Z|ob$Enb9vhV)Q{5FHDzsuwWmq$U#M!+fZ)L?_iX8MwDeB|eq@!w5SUn9NW>6fZ>!n)0_geav1yn%Bo ziK^%0oqyv=Pf8mY8bZcMf~}i^Fb-NkYhrHqzdrEq&ot(Q$IAFi*MLHt9o}byGcd9S z$M+zu`~g-fjSljGQYzceF4lIRrY4PYVEHK0NErofB3W3J45a(=GH^VmH2QOqfq_ko zG|cty6$kXD*3vHrYGivh0Rhx#pEJLKR~d+FCN?yvThLKdOIl*yh+HUqKr`)pRd37Y z$Awft>u7PM^Ycfd`rUp9EKOQ|N(d6{!-Ub!!ATlZIkzO~9k~ih)|Y0O3%2iIyVv(2 zNljq8>F4QkC8#3U&T5o^vJE%JpAu=|untm#G6){tR$Ts?^=KuxKtyD0z=fpgG+&S? zSqTHvywC(tV$lCm3dLQDi?;ebs|#PR-qQd!w~#o)8iD;?}39?H}$1yQIgK_g@Tkk>I}(m%dYq zW$Lw18EjVJw<6JmaILM~%5(G%7vm}$_O635ne02~H)(((tdXN+gY7b;(@W*Zo5w!1_6FVlD=RzCqny9! z4CJB}cb>w2iur0is^r=5&Si5HhvzD$+ZC^XvNNoETAnDk*kmp{hz_=jKcSoRq!hw? zy&)#srw7)SV+*)7{p93Cp=>sn`AQ4wn&0Zr$HmAS4w;NAyMvO*-OsjIEjl+O47?x5 zTI}|J9)Jq=4+wOGl&jZKXfxS=l^1M$S#bR#j0&u*gt>I*JE$)Z2C5Ni2dAC$nuWYK zAM=jN_{BeUqlin=L%%VtKe43e@OKY)NK^F};L?ZTdIFrnR`mzc=ZyW$iiCC-UQov- zxZy$DW0~e8EXoVw8=LJ=Nw(NE1rK~Omdk*?m>5f?3bgwVbq?AWnca;BNn?wpz5AFp}j`#HittjZM!1cN*v4O+Bi`e z+}RQxY?c~5s3U84M+WQW%|ghVzVT%0I^P-8znB;USr_r+6+~Dqm*G7&SOt!h*lAd2 zK`8a^CmyJsZdZdE==93gOQ^_X-ypp=Du#V0#hRyjb@tZ=5X)rM-PI4Twqcs=P5g#x zrGe2Z;8SiE;p~n>{o}s7jAQ-Pq^o!qkR)c&oyCudl?D^)$J~yXzo1qu7qh4!;7x*u zhDYL%UJ;pLar+y^`pPF8_=95XL2J3SE*Ib%H6<5uIQP{RKgLxmLA+|z2GMM0D^!q` zo7j%8T3ArWFKK5o-&$+-$CJ;#8BieX?w`-YV|iBAkr@xz1NpwRbnAFBSnMJ(4I=t| zA&zW1Jl#H7fG;}^j}ONC@dcq{-H+MT9x56a)N8MU=#Q}2y#|2O%7SGGVWDWDS=1VZ zsV%Hb6~65aPq#yqjn;SVbve4#XBrq~zz2Wtpmx3fX_k@)HZD}Klm)BxA~?rKbD&00 z0)@#;>zixAh!pTGc*YW&+YTD{%w)c81u6Pu9SY56$}p-AqgYcJc{Au@`qyzg^{VqA z9R7AVyVJJqX#s)}>K%9JWgtdoN}*j8ccB{ageOAt!aWPTc;UgvRuMo%_Uf+X)_uiR z)O0AruaO;AdkluRtaZJt;Wo6kB>2P!!cZncyOMT7uy=@y!{ZEVR^!pfPk=Z#=dgAa zmY{N0AaTd`cjw?kG@7UEh<|6pCm{!r|r89cBNr*ttorNY2 z5dmGh*OPb=?QLY-13pD)GZb#_IjY2bxdTrc`F+=Q5Yx83`9ot3LaA&|Z+9p1>SjN5 z`zGxwb5$wnit(j0F#W2f`A=W9tx)L{K5SEk}AV-j2S#2QuWexfXv*aPM1ndK>?|v z^*HogZzRvR1%}OTJMiMw&%jDOzHFQyE|(muL=lLHWDt?U3otrQtCb&5L(hJyFMVO#zFvOs*H&5UU8D<|M4R%?Yu%Y5eRlX$3-nu81BBEv~4 zSbMv!-t26)d?PSgsFP8WM2L4%HOi8mFpFp55&I(rO4WuE1M6p!#OGVRnxPgN7#MZ> zNok`rL}?*#T<}@p^@ZyNX%^$a3WHC-^WAs6QN{e$IYe+Eusu#FM)iQW1 z^RrZ|z?-h|DeZkmz&)GX8Mucs_Og6r4Qcl;M+kJsf-u^ii>r5W1j*18Y`}^KCv!f< zE$H{y$@p3O@wsw9w_OdD&~j3`p+I+c1;=Ev?PsWQz~E^-c}3wV_PPlN0L0v*t%qst zSR+xAM^t~_FB=s>YCi26iM5JhPBi%>fM2d}Q3*LDglx!!(Gx_A2wPonNS}01l9%dY zA2wtor_YQ(OVDV?TK7NVLhRz2!8%O{Qslk8hQ>Q-*X#d`7%k&SV?Pa1AP&nHvdNU* zCo?LazlxlNSQ+1&%qe`*2xjn4PS(B6S_mN%q+$j>n({PcNnPEdJ7Zex;(&Va3A8Hx z9O>Cly`ka20|)n%h+>)VD@g$9xW^2Rr9JIy$%A6qwWzhI=`L}7zlC#FHU@^JW*bgJ z8l27ZCwINvh{&8N$g{H@dte4OBi+5zC^D&U08d>Vm4rXuN54@vg}w{y;DJau7v06G zX7qhmcP>){2j}4&ztmDc#M#EaxOvz%O!cW<#H$3SVkvvHAdS%OG2#Wi&c$SO!M>Ng=CWG zO{N564O#-}?5+f#D@)sMt_UdhNdRWUb31Rd>J* zgy%o(`hD7+uR37_R6%E=HU$F3xqwA|oT~tcD#1G7aU=Qk)c4N=_-|70klDygDT+!F zh(W;k)gyN56Msl`+G21uu}5~~4)<(ATqmJRlbwu58ZyqoW{1HJG5}&JOf0f!!<75R z?A&1(eGkGIOFw94{q*UT0}wv>+)>VdO}eHX2YVo)&{_h?IAaV1602J?h;%RMG`rSB z=xCTu`(zsvDMo!_RE0Uy2Wd^d))uVwhNKbtnf?8H{i%hQJCDO+vw|rUh9Cma zBsHL^K4tFW@I@;C*6VwDmw5O13(KPy+F-N%$Cv&l21r_JQS|NdW45pwB+4FId-Z`k zZrcxWRMXM+O?AqZF!F6_qlbFg0)uO5qC*z5Vw6a9yi3AQ2lo;bjz4XuO`Jlb6#^#= zUZ}C6rK?ZYvNZBSs%@KjgU1O2LQZF7zfb*QCG{4eTt#w}Rx;lpv&Q~>9^8d0*}wfB zG44$T^Am&H?Y!@D(5)HfuFHOEAr!~A4^Qa#uyEg7KDO+|YMc_f&Q9$Cgvq;4WuX!x z+Y(rNk$R3)eN{rG{Av%WCp=ChJ=RJ-MU>TAn9ZuTl;twK?Q#zB@(TRzQ-BH~3e`}# zc3X0}oc*)*i7p8fRRaFykbsc_a(CKUT@IYVM4;jkQqqgzRU=$_ZOQ{VmI=`SNSFQH zw%>E-ArT(Lkbs<8gO4{43_YnOn;5xo+Rot!fhwC5`p zcA&9zX7?@03Y0@rmP$Dc{(#=jv^wl&KWbcn_W}RByqGJI415 z5O+Q>&{w$!UY2x%#YBasVPYL^#0++q?-=SVG%gaamtCYgEaZ|VR_^HV9H7pBja;hY z_{G@ixiqoc9vvMqBzFCEMcF9O?2A|0mjuO5obrDL{VJR>GQXXo|UY zqTRB+Wt)?36Z=ialDdfN8p9w=2v2LwWyg|aF(B8L2_2qj=D~yx6Tyu7e)CHY8f@h)leyK9ifT^ds$o~0y-r*TD9{4V(E z@TNoJR0_^ZzJX%8GZNXvF=fCxe}a8({&0X zHI{Ys__UE@!fA;ltbi()JSy>(7)Y`OwcQ7Grv=0Q0~W5>Z{t&%jmRnDryiJB<%qcV zpkV*{aat{Wv^31X+7h@3G`eI1J7uUam0)2gQT#eCF4&fAWOx}yb!%l$JQ%dbx_|3% ze_*!)OC+dpBL?0Sn~vBv3Rv~C6SGi`tiu|c2uq8?eCn2+zM;dG9B1k_rCvK`gLz+X z-6?C8o>L{mTQ4mq!_RH8Uu!Qgvn-}idXzFmE~vfoLCN8LC6qIW#OO zJEE9$@c@vbMby1F>Cs^VD>|nQRzU978?NZ_$M^%V*67 zC4zUGv^FBcTd{Ub0Fw%>|^@ty%;M1dzAQh#omFsN7x}7hea0eot8d%q{BY*U5eJy`&&mvN=Ck;JlJA5* zh@Z0!U5}-A`AirPsq}-ltm_V`=)o>raeb2-%yad&78j3IC=LY81M@@~c8>c*-VUZ0 zRPS9r{2Lx`(Xzv~>em;*ljnfT^YQ}4Q;l*vl76ZaaN$?+pTN#dd%CAUxu@7Cp_DHp z5@Ec5or^-QN;TCKYQOM-dGI-Gp|+#Rp`;{$y&3?0UvkuZn;A@c7Wsv!6UCa6TeV&O zvz~<}YG((-3gh0(IfDh+_lQJ?$qnHN%2Dho$&Lf1*NNTd2VKc}gMr=o0loBRn zGf6b^_JIhr)D|RYvx~>Hui)?g1Q5U?lIx#(8fGe2upim;LtmY_U!^vQpeD(#(zikp z%5S3sEM7mQ_7(Zr_iu?^*2L_DRDE)--$)cF$H;DdVAokMgvOentwl}kD5&@I(|u=7 zH#R0~-FW$n1vQcr)$k}yj_%|lpGKxxsjojeg31H=r6FEHr(p4%M^enI{zIv#sY z|A@r@+ct2=i6jrUJBk&j^fs(I=P!pP^wDH;*BFQC-kBMDdY$=!@3Yger1#hU6~&?% zEPyjJitI278i*E|@+RQF`(^^Zh~Gwi!V1U%jaI5gs})mi)BRo#QY-=c!IWtXd8Fg( zBsRoe-9B=W0c1A3Q9`rK=&_}ZXxs=nLRH%MD%>&s>@X#TZi9;Hdm0B~W)V#P$nN~x z`Y+&%Bmk8ET%#47dif&6BdC^PZVU<+TjlWCEPb$3{33O40gSIZpqamF_)5$+fAg9$ z?Yp^PiAtZ=ad^i+Ccz^4-{ZGai1huQXx-onGuidAAl+9S_GpChS%(vKIA-T(lbCfJ zR@%dRbxh22rmqT4JauW|1Uxrw(Y>F1flllkQ3Qao2AT!fK{9TGfi_hNllZ|las2A_ z;SjL@w?$!qT=)+_^n(j;te)?x{|7o=UX>8UG&nCTt)*H(vx=i3{iA4N%jSep0n+s( zNbg4vGQ?LmSYw3^gYPZLG=~m^DyPKd9TcmUvdF*R`z!3*b}`{iQj#05KRvSskG~J| zuk64kwQy#LlpHk0wBAw(4QE?bF{#*Dtdl;Th2X!}2c|7^s;Es0YE$aJ`n>;q+98LODcXwT4f#sp8w64Z7doXlYmk zY;SCMqV3}I#}*V*Qq4LvNLbuJY78f}O^LC3u3)7w?!Tw)llt39GNpvP809>o0aV!D zl}yi$-ktYh4`uZe5;E!lRyeC!ZoCJe0UX1bm}pbZM4?yCRH0H6&O~aXuQD6R97Spd zE3H)GPW_J*;D1>-{>!$KlQ>V6l!%WCmQOcs$Kx1&qqcpo{)t-Hi3pU^D>?KEP%V`t zlr5^)x7CAJVbwT~hVrz_IY=G*^S|LR?hyYniv-R|B2}uE93L+^$W5?MNg$Nx(jNa= z2Ebgqzvq;~{`zB-+Q>e{KD7tENBloxMF4R$LEyH#O)%eIn@p%6keQ;CQ(kWShp6ZX z0|U!9+FS9+)`2_+Jno-&zs~s%*$dtP)Zza*oE|sQ=H)|M~2H zU%ZO=UItsp>Izl=^sE0rtUUYtg8CqkXIZe|ia5X(c^Gg0{q6tdP5Hl_LT`ioYierP zM7!_re7gD4{L!$mBE6n22?+>(2;cXt5s&|$j2fS#Ay-yb`lhGh=jC{LyPxK75y|3`TIKZ51?9de;o4?hS3A@u&w=;+_>AJ6*08PQ7eVId)e zgoMA`^SV=OP(uFyhwl0XiGh*v*X}@wfwgrsFc@?f`VV|*fkaDsn@yB;_H8Nt|KsCn zp#J{-EAaZnr-!dB;FSx9Wb%LV!h{6A3i{UsK&U8`{2wPE^!u()W*tf_qW`sk|L^I& z=KG#=T3VHP0ik!Gt4!MKoTurM?ke|OLH|67(;x7!PrgrdrP>PbD4*)u<|JooZEs`9ALqLfbenE&-*(hubh|kXroq_x$Fu~F7h7OcA3O5|K z(Kj_Fc-;ZHMNb6|#(jP+6leee0_qo+$Lw2M!a5=Y-wPN4T%jrahYx5A4|2OrxDN#NYIfpkKrz`r@4(bxbC5O<$@~+x4ti zWy+R_&;n|Z%~zvLC-?8oAl6D<-AMVfVmFG#3M;~T;V11Uq%9zPc<6N|6SZw9#sQZg zVT1LByw)_){9dub5(pLx?|Gps{})+b6&6>wEgK+6AVBco1b1!Ro!~Bw1$S?(u>ir{ z-5ml1cL@Xy?(Xhx4fJK7d-gu({`YY`uJx@kM$Hu<;mrhO((2r5 z^S5_c`|V}ECn}9P@NY!r%sT`~XL1I|e2(tl{rQdTeYBoyoGGHgicqj2mu-)rnI}(l zcBuWRO?Q0Xpg`)Lo<1Z&Aay2|=Fb;K#`mKBUqU0J1Jebwq7;~v|McT>aY-6knSb-4i&8p^hgB>us@7)AItOY0NpF=Higwdei9+)Wa^Wf9fsY0!s86Ey z-pm*_@+U*SX9&r`+9t|E5(^p@T*tVY=}7Nqtdf7W=$0=RkI0q;@FWIHK}qW`@41P+ zct+F0elt~cYYSIrD-w9*tVWvgpXGxx@*D}KJ|*|q&~uxxjn)wSd0Oi^!dc&(7r*?> z>WW*eU>||G7o0QOql{ckGPp+l#YiPI;^Uv~Wvz?xAr8k*C^lQo2TOi13GPT%pi~i4 zkVu00&Cb!O*Dp90mecO?EPL9iRLLAsg>QD$jb!`fN~-8{WM@eXbJdR|%V-k0*P%m> z=~E*YC!7gheaeUf7JL zOaWyEnAA@>DTAqerhf?kF(5YtF(2gN0RaL#XquIJ{MTg%J;??3G+2_={ze!7lO%^3*p_hFRV6WAW$6(TtRy);$8ze z>7{_#0kQ4k9m5@o%Ueclww1c1GC0v5_<7skDPby&=-vcnl^JV+#JSpfIuOBE z$7uhT(#y{`l4hy+dm4%3x)rZ-7ID-hXVShs!{^-FW|P&dR{ek*OFfLeoDXUZ!{Q_0c`XH-_r3UqFZo-bgk#dbC|d=d_xaLM9%UPaDx$ zSP`3TzdC?U4M|McDr6?&_Ubwn8)Ka}|FSJ0C((SYn{-5XzG*k-`lq ziOK2VXT?P@hc~iQ`}7vjV!X$H~#dn#4)oy!634SLPEw!A_+7GNBn8=X1rrEx^p z!>}gWYf(R5V=A-FM?F?&i-h{Qq6k-Mhbcf_CXIHm*R0pEG;G(@yoZ#A(MmVZ-9FlA z{p^XJetqb1dOHq~M1zY^iXzHNqQTL^b3ePwqYz`2PZ{Jh1W|gIXMYsMt@*hshq`_E z2dX3u>b9WEEu$bc{_8IH%X-;rhLj7%#X?E6V{7lc9+mm751#SSp2f6Cw-!;vFfm~( z=L6z*cY>M1=s%Ade`{=S-6-opf7%~8E;Uv$PZm?gzkLl#eN*QRSbZ}nMEc3N_8aRm z3z)fS8eeNyo;&B**#S#*Eu)1R^AhpP_7RvF`@Wv-4eyCCYISv@^MBfRfv?bnhJ})& zeHPARDGz_JXgNw?MAwwwoOTVQkA#U%D^o?LJ8D%&z@LTw~3D@H?XugK3#r&M`nz9|q zUL(~A_f^!@uOs5!68}e~kQsjP4u&Q8GV$^*nOP@#)xDC7LIYn6-86bqYAC*VwY@-+glE;1?kEPwpKjGEZQu9{sytrk!FriCpp zVGviZlrbo#mDcnpbTX7qOt2=n_v7=gTO@fK_ui8h7qwdY)~Lss_wDkZ(*4#$Dw`v7-eK@isW+JJipg!<}=I0TVD9?jaWxjg@1>iXu z6?PbMmCZ+Hmz9DJ_Z>?`E+cBvNriYil%e&Bp&bWn9C>mOncOenIm9 zcW^nCqLMgPw73C|Z-SfkM_i35V&So@4^>FyX=Qwr0!$;Elr6oKAm8z4rp4rR%QOP+uv#zs5-J+usJIJS_!Mwn5Cum>*(LhY@8 z6X^eLg(A#I-i|N|R1w`T%Xm(YcY;3#Z;W(XkqJ2MLyycV{eL*NdpM($plA08^B~X@ zV_{b)!{4DE<*I%qT&ul4GZ3Y`@5?TVsCh1VDc#_KyWUgW|E!A3i}y>LM=r7NivR%0 zcWv#$6X)G}%DCg+LRT}PLv%6Ia&Cu@t9k_xXZLU74_70X&*s40vNuo^ipOF=4uGLm zt^GKhTpxt*g=@;vTy&~ofbBvI+fMp>_602)0xzCN|2?2@I8PA{aptEUCPMqm{_j&8 zM<=iH)pSes30#~Lf?VHKYg&UDO6dEc`dKjPd6GX*vn@t)^&)&lr&>#w`DR3T&{g#- z5#ry|4P;z(ZZSJy5*HpF(QW4(>*rP_aS_OZ3y*NHj_(3Y(A$Pjg!S1F+`BwZ+9@>d z^g5&CA%UOoTgNi7mPc(Z zw~w}nJDFv(`*bUUXWf6fKd}d)AulFNj)GAa&=P|&BsapA6`GrcrDW=ngK_6*4J(C? zYKUpAOvcceoxN+GyR+}v6GeHRdS!n;8_`7aeH@5vshccLw|k|+nH+T553N@KQM_4C z_RJhkPqrfuR6{Jk%Bg#}`SNT2)Q)=5>QKq=Q}$ipzhUNbh{Pp@}bWDmgfgMaQL~t)inh$P+ z`8-!myweo3J=n#Kq1%qj@*4hSr-snJRZy%U*@w;Q%Cx>z2eUuzPw9$lzzUQrnHdAe);v`hpf+n*F5s@VCq-BUIm|dc1o~jnauYydR1QlP%^oM2wbHmyDR% zDd@(KGjT_>8i`e4<{`_@hfIfNF^wWBqL8})g>HBvAut;JFn@RQw%2N>CzcZE=o=5q z#}aw#Kx6YKUhHG%0d8na@^7s{Gh?)uL$!|&uuCPZA=f669SOwBRL&y;!YeIK2>j@oKaLaLeNHTUC#M5|6)?qQx{^HV2|9(&){ASQ?V%->5MR`v}k57}6>q?^43}2xem5&V1awS@>awq5JurDdQ?_K z?MHLnLL;n=uVC~-kKwQHTzD=a08t~gH^3+D5c*HD5QYin={t*OEFqH{f&3}4#q@8R zn1iF7bv5*mEmLcIQOWS6JY*ccgz4uF5af~pIU+Q-gd1&Q!9HqmxRwz$y&+EE*(40E zyvx@LP^eN723MKDw@PY28h@$(YgrhOr|ZMcYNe=_oR$CmJyj7C`}QSW7lT>NP0#5^ zCW5sH>m(*Qv-F$XufngAO(b5^qdvkD-Pr|)$5H!3^H8dm?8mJB8~d+)c6OzIOBX?R zRis0S)zv)_#7}Nw?(7^B397>JzP&K@I}3v0`+}MdUS&8np#2+jg4GgU)Kox7^jf=; zs1XMC-4=Bv$!g#*n_LCCWSMCh0swqx?%a8<$e$IgW(Vi9wqqru-_Met(aBZ_*3W9v zzoLNZD~z+)QVh)D43=8s0h^Dh`)Jj9kLsE<^x{5!<-Qv)vcH>CZ`+nw^4aCRpI(M+ z*zI<9b>wI$G&Sz7b|Y>P2Q%ckk+$Y`=IT^F`_QRGyo;(8{wb1q^kv%VLXW* zJ@j&Mwh>id!f{G%3zz|SkX6ubC*CSwbGFnCsoeEkR=$PM+K#U;ivqzy^l&1rR8}zG zq*Ka1HiHsD+weqNy)oF2-qt+P6z=Fg{}EA-Rr13PPUM*4EovHm-%J)gCIjzs!k1w>Dckv zEI_+H&Q$eE)E?9~BHx9qpVo{G1nnUOlv}9P)->il=}S-TsWs7&@nt6t%kb6NQllPl zCN&!RiiXMJ&|$UM?Ke(OG|ragO5zZ3opZP1Vdv1lmYL*CmIoD-r8eNCrRpeeCKp)U z4uvFMXkJQ*$Hk0Kt*Gc9GT`n-w{%P}m)58IvxlOMlSZl_dl!>{uP0P%j zjcMwvTHXw;b!cj%@t3_53L%hADSboUY)Y2i5vzpr58g{owW|hC3>8h6wggco;;Z(aB^-2*;$a`2i&hk)lM!;5OmP5;d=(=G zXSoAvIe4v$0(k5RsATUhD+Sh7O&a0ohtMehlq|tWlAMVAf?zlwO{b2G5KK2e`Xwk8 zo)E*6H^S9gwz}WBp9ZF@QUT+qx5RG*PEhYInG{C5gc$}oqQ2ktb<=^44BidZd0xdS zig1*QQoJb=N;W^-_!Y$Lk@Bd-|AS?`SVQvWd2>dTq)AaI>C_*XuHMg%)58$O^a$%&3>3$IOyu$vjvzXkBa7yc zYHYl<+#6oqAxvox9l=crSh@2PU$x1IW%J_jXyBxt(`i##i4OI!2<4lHkcQW=F zQi@9YO4GyDCCX5Y1j|R#k^?6hz4Gp4yIW2tkk0_@(PR}3Q=$N?D(5@rn4rp&u0 zK38$@_1>Aiofgi7?+qw|45BW1t= zw)(7&&@D<9@s&3;otDVkUMA^uzu#`WFg^n{0M-_ukc>7E({$XRMMkXQdYfr=b9&iL6zT|n5F z{2&a%$>+Ai*s|s)S^M)u5}dpETD%qV;ft>U2r}FSdTB6m6rQ*~2B`BFSt+MMOT12$%eI$BDUO)3?ID|wdS zUSLpqOS$B!a1kD{IE1g~I{EE!r=r~G#*y`1b0WK+$-vsD-qP#dpM7x z)PT|~A5!F-Tg+qlFF_>@&Q2xUUIX8BlUJ3Bc6yM-%rw>xz!oXWus))lTt{85>|F#Z zUK5{6#ub98>3R+>@nvB%;EW_*T1sA>GAKw|RlZ%oV$C32WU0=YOChx;a!e5U>+MWJ zc?A-vqjo6#_Rhbq7tt6pIoNCv2iQBJ*H-AHbe>FQ1iKwd>Yp5|%?4*# z?riDrZ1thc5>@$Py*Ny+uo=eriz*N_Z75wC8L`6Gy2i?+j7y5Qb%NuM^zg{ z<3hxDbXnbg5h&$mi$>&>ZPpFzlmC4CZ~$ib8s}D*5`;qWYUn+?i>tgh|V-I({rGXwajMiS~otT$Y(c{`Uq(an|=f~I4GX;b6r67!v`mdP2nPoh(9 z-aHq%QCQtY;It3A#Hm%x5BffCe+#co`ukfIStfgB;Q~VQ%W}wl;VFjW=r%o|I?aNg z|9GbHQhSramEG(C#m^B&w+R|tz}=B*Mc(sl_=Sv?sIsVW?7tZIxC#lM!XAbclLw>G z7@mj0kAlIGe@e4aRYufF*?iq=Jdbftgm+`OqOsWQWR+%qvH93MhJVxYZu9ssXMaBs zx_Hgzps1BNY?kX6AGFn92B+O*hF8_RCjBPZ!{l|bvI}`C)#*iRCFn0OOME#P`fHdd zOC>w=mRBO!S|HjIz&n1IzT5X!1tu}rmfYrVi-3$?9aEP@X`SS(K=f-Tw0-G(gech{ z3AF4^(4q5l^NT>MLVC>Ob#cBF2HI;jJ6o;ooK18~4cvP5EBt=P?8DUL2N!e2An_LR z%n`BHScJ9xIpf*$GukB(HZ__(f=%7d#wm87jo;6Wje9~z#l??U=A*qUzoV3I^aI*# zFfX=h@Qi1JX*FsDkO_`U5ZYoqK*51S)qnN^=an0JWkzyJ#KmG(I8 z-e*1d+^Y!4#)@Wt3VH{9_X7wRsOKe$T&r<93!JPYM!MjMx)rYM^;OcZKi&>i>Bz{h zFT&V*KHqL4UamR+flhEsFgEn@DVHS1=!M&z0Q(EcNF3a*!?jeklcFsm*f|%vLM2M3 z^VvQJ5`wjO(jTPj{t1IPLZS%m|9iY$*erlexm?R-3zQPY!9aTtf(!nx$ zVq`a@ZJ46miZ%Lp%O^>Vs4_>1F!35>Qr1`o!+AIk(<&G^?_Nx*>>WyAfFYyH&tL@vYKd<4_W z@sAUXsL*`7`03VwM=hShIE7wX_PVqUnUmmT~K&0C`dkFii%1{8bhX z=tAZgB6_nb>`u#tn5`2g$_#M##=8@#EBUbg+^g%lzGs!17x!F2e}!+S$D3uXQ7xjm zM_pJ_&>NmFgwx0@s&Q(qJetl~CKW~S_&Po|7Tqcjl%hF9aC&YcYRNXO@;;)e9$=~N z5*EYsuirjj-FLLEyLy%q|84K*q#CtsPQtilxKXI1s68(jRs{Ha+`Pcp6e~Q9gM5j7zZC z2%GjfRXN7VFriMVnQITYRKVV$HIQ%)$Ia*d{64D~Y?M)6WFBXoZ;SW*zRLz;sjyyC0TqgzxP1fgU3L5FsxQS%`#;oky{f!r4$;#Pl zmUpf=B7pFZrNq|IoPTc# z{?jVSFdk@O)A{!B?PQ|-`g;~c-R~r1w-@_xrb~Mc|6tNs8;_9_ek?WTrdjfXXw-6J zp>WzmtlD=xRTJ^T|GLF5_lE3v4kVnd`ic4aZxY#1S4dhrUt9oBP*v;jn3m4(q|i&d zmm$0m6DBl+Y9+%cLm6&^{zB>xu^UBygfj*&$Ft?cVsG+pjBEBThfo2(bntPZ@};}E zo}fEXMPm-Ctn6iIkpu#vIoghg^9kL2uCUL*VQ{#@J$$e^?mJM9qwDx6x zHwyFVAey|xJr&vKx(_IR1tC`3Iab&=Hz!;Kla1_NKhjjEg{FC2s#u1mLLIF5?z7fq z!#+0p2nNst(__D`X5WmyE`NRdRI+;yhLVW!vWkxh?%O^z@b>CuzQL3dDVrSeXiwb%54m^m^ehN#$qW{=iVOY z$+4;bcA`ZDryxsyp}Y1TV3+sRjg0yNKWl+Cq;-}_vJaViui41!FxrrEz;L?&NT%W&1xS``3CeGvyKWU(}i&i)Pet9Wy%dm(x1nZalnj5o4fk z!k&_Y35`^>`(X@T*&vf~EI}Xf^7TS4*Q2%Mbby`wtuDHaDr7d~qS{$*h|j z1L|wOz@gTu{I&b4w+Zf;$h;4r(MI9Two=Woqya>8;5y-`Idrf%R0R0xUm(HpLXAN+~B_7|PD+#~OGdZ50Og4Po?4J6n1Eu`epU7Q)&->RUy4gZAZ4O9sv4= zd7g8HXkc%jN@Z9m&)=1zW9`QP}5qE5faVwtZl@&ax^-?R8IZ=3(YxZTg^7<-3 z*L>5zddTkNUad!(v8Xp<1ivN7_OdLgl}JUE#rf!4&*ZT%EqG^=JPnwCnb?~}(^iSO z{5!;}-}sGFP`E7}%lu=m16NK-?o4GCf$T>=5q`Xn1?ie6Uo3uyDJBz9<%HIg7@g1! z1XRu;?3i}B4u6wtgkx`n3u~tm>O+$d6coP>ZlXrPGO?bBI4zHGXgv)$@(||hFr%BSpQZUM}vJ-f_&c>f9#jl^j*XH5;LFoab`zK#aS` z&L|(KjZL^7)iE62kZ(merph|NFg%7uadoMa&U=BvZiHU1xQ#pEO2=R(>)9T3`hGz8 zID`2$Eg1WQ5y*!Wh2t*S)W~QE<%|k+8ipN0d#W*$_=#A?0A29l&dwctMwq$Oc;cn3 zaQT~M=w*wJ4|IUE+IpUp*q}~P(iVcNTK!~RsMVeRqzFknd+&1BF!Q&JZbApBcS}`b z<~I>h%I)E>t1O}x z60Be1bhEF!HSsl|@O+jSP^)jozNhVs;Q%c8)+iy-$A6ixTJb4T6cJp z^&ONg=}CJdm=li&b9$@o;(AF|o#GG7BX{-KDeHf$0M21nz7B6L*F*LI^U8IRz^6tU z7deJ;3A<+iAavgEhOs#)A}q#V*~|c)ehzM}^bciAdWf$HDXV#{Bk=?VPO5 z2a7=7*TrH#(o0;yp@ihq?~Iw5=st2U8F;&P;mI?eJKlWf_}$k6LG9_LY`<@|E{UCu z2sd_n7io(=Mme0KpN%UEw|eb(BIP@mF@`>({!&XOt?pX9G&LIvaPqsDqU-1SLHTIyawY=|4hNQ||#( ztLqTL+n;e+wu~dCnoW82_nJ2k4?-KAP67?gMv`-V8B?VV7Q$mr#oppjMYlY9##|gT z_t{~I6%Cb+>4}OuLLPzk_c|^f4--@a2X^5*52^{|e*)3QoeL0niAyzg@s^xkt==5d zZ3PTZ6R2;f?9!KVWQ*!@M~Zy&$_Qina$=(QU;D=-ceJU{)6#&dF8fX(A-b0H&Lx>0 z2{DnkTftw*-Y(F;Nh-uP2FNaM;Q^~FU%|Yk3CHZM+hyC~#_r19+Z=#acPw944jP*_ zAi()f2zt{<#p;#Pyx zSO8Vs)b7Ghw?uWxB=8464Eod5IQg6AT=D|)8*$a!Mcsk!TjH4esm~Tn>-)kc+~U9R z^p_f9`Cj!xr%IX%j!yLt&N)a!RxROw^j1izBsilbCiP{w1@bPwV&!;bvXk-^qFM>E zPDl9;Whb#0D=D3Z)L`wIed4DjnI>qeQvcHvdJ~rbj?(!W<0?jk^Sjz|fXD4==18he zD5>61dPTNARAd1KDCk3*G@cRSoO~q1F=s&LXNOdEMQlg9o->?AsP<|ImfTN`p!jf* zS%A$y(U|S`7zerNEp0gO8S+{EY;CZR={$2JCWp2P84Sbrf{s5H7I#ZqSjyic5Dn{O z1&=Qf#;9GmEXkevPz?HX5OT8gpb0aRPn4cp@>SrtZj;TOO8#s}rsCi{Ig&!GYJVbo zcX*aLb8qN@8qy&kxeac}J$u7NMave0qb*cvj#VOZ{QO4(-mqC-2fxX4;?<3m4rAh0 zY0>c-ERO);V;vLt6>~wY(fXy5jDu6H?_FJ95>`k%3Y6`byDqfiTAqhfwN(>3u}o0b zCr#wXd~)C8!P36ENIzY7yI#dGYvyJ^PrCKd#7(8OC4O=1FE;%Eb1{qRurI1hxws_!OH@rui7$a9# zI70X|$Hj1y^{|cKvK0HBrhDU9O-hP=1l>K;!*PNcw8B%ZL9d zoVP0SCr#?s!kYkHyB_|I9R{3p%UiLzR5o5Z3wklj zRcwyC`yG`}2e!H9zb4F2 z!JWUm@_h+KtK34|$_ZCZW?U>Yx;MHx_axC0@U+Cv*0Su$#Wn4l6>Kq}Ps6bqLT^Z+ zxbCJZ3@Y_*AwS6FG940w%RPmt^SxXUuL`s$Kh>9t;s0bD{JdCpd_e^o4By)?&^0A+{b2;8!ahF_j?ae(tPA94xq#J1^`zopfk~Pj zme)fWah?a>J}$qe)@HjG$Vw%*y9+z3b(#j&<`rxz#OSFH`s@z7|MuJisYZ`}3g~@!!XB4}Eo8 zw$Rs>i%lL?&*>^QxKhmhtBMKpPp8dKPZV)ny!ZZ_r}S!#Bc5fF;~zi|$owMWy+h}d zFwV#Gflo@>qTB-QLy%D{zE)oZ*HX3gAU4GE`7m)hr4@3&N?lnC9lCahi4lzG#I^p& zoBI3j0YLX%E8IpkA=kJf_lpTo>Tulv|jn&ceZ}t zgI*xDxRv9lRsWr$CN>*aNnEn@Hhh=SaR=4cl&%paAeR$hM$&9S@Ko)kQc?W20)=eMm -7->U zD`&9;KBQ_QwTaQLtJZuU3t5Az-1qO&DvjCBK=V_d(u5d&FC5J!e&@psiojizUw+W}ciiOsmjB#HG?CYbb_j5B=_?o|}dSD@4-duC^_B>w&P`VorIh1#SQI;DQ zH2|yOkHFV^jMnj4OW_g32%__OV4^Y{c3!6mG~YsR=;o!~$NawYh5+r}N6g)REuW^^ z`(Q6RNDoBLuYrRH~zLB+Q&rWQ|QH4}UdVlr5e-X@%QVPY#aUzz78Hx=`ltN_0>QW$_r9MRBe=ZE3DRvT)KIPYP2&@2=r>q>yJJy!&^rzHyxDGIBqB@ z(nX)dL9M5iI3v%`Pz`gPr*Reb#B9>zXI?F zCfAe8WzP?W$2#>gxxTOO=>nDP4hT1Ut#cP#OC9>fKrbefu5|W_+{U^tN#S6HoQ)4p zvLl7YA#r~+DemM^l+!RyRlP<;-)gZG8KLP)-0KBs>=E@Al&BjR=pj6FdIH!pf zhYpxNoW#`o#j~pZa!>>8dM@zdpsiGHkpDi3PB&XeGuPSdNUaM4TEO9MAho9@`|C87 z^li*3eXwK;`QsavE2k%sTh zfKb1)voI%|u>7-N2PgTV7YY#<-j15OCmAJ~k3ojcWbC~bRU&2l%6-BgE$*%o1=$Vg z5lsBV)Oct?+qbZmWLj#-bd(BC4fEc4XZw*?b>_H)RvC#A>198qlgm!M?u*D}3)< zupQHoWUw90?0N4`!>u%V^|hnSb+uOA{~Ye|+O!yi&!I}fY^>br?MbiS^ud)FLWn2P ztVpt^mlm%?RIOcvR@7F~G__HcRch(4YMN7qikbt;Z09KySbH&P9l!S6slRxpR zmSOglS)TUK^n3EOpt|OBMcJr0;o2TIob_J&#i`+Ye~M4=MOak(-$$;0A;-c`53R82 zT<%lkTIGk|WD$ySEH`F|Z$=3+SvOA$~IL5_$vg%0J#E zM)#UXk6^FKUKHK3^KLr8Av$ix){afYBLAL75z@Bu^&ja`x-e**e5{@gp9*?ke=mGJ#Q8hMYIcHIJ$6-8p_#RJasNX|l68d-i6L9S(Oy#?`E8cvX&8DC zB_{E}{%a{3P0T>k!AXnF%?!o&fr91jaXUt;`+YIQFK-#kV}Sx|?Rne=20Oc>Mge?K z0YSk-ROsI3rs%o%^A?7z$=nh@_;%x?!V!OBzamgcY0*EK5r&+qYSU6ewn`O$m$^Py z6OLxm4BY@_@?aKOX-UxOlSV^rVb3i@D)^zp^Xj|(TB}m^bI$?iJ|D;$n9eBInD;ef zm7+JNAYEhfZ-Py!R!5Is$9ynf>u9mAlb$CVM`AnVdw>|M_u%%UiL-EL5ZK*TrUB?$ z(5HJhSya)~^7%WWC&a~G3^y8UzalF|`D(kx8|2B~(yUnWAwnrGMX_oi)wkbgb`8;c ztuuFdo_RVA%bS$PCGAYmN5P+wwVW5kzi`ha*y0n#oXIOW@F=w5yA9KEi5B_sSZ;V& zvGsdD8bByV?6)y(n}=|HWp107xpGR*)FmmMwutU)&G+IDp0b4fcX^n4p@m=Y%z41; z42{#;783+$)dHhQ?1XsZB9ceet43^n1RyuruqJO(kCz*?EMdPWk}lIpP7DiY*~~MS zC1nO)y@hKWeSWXdxk_z`=@NzFODvknpO12fIWHv-Q2(YA(9R>2^+V2d`^mqw{QsgN z{u|=`5Wz(9)__sS@bHVXL3C#2q@0egv>JX3JoxD|5$TO&!bVMvO zPVuwlPeI|Q+|71%m>9Il*t3pO=pW9T?vPb;V9HlxpV!i)RF;{Wn+x)z)!DS-s?G$+}np@D^o7_r9#bmC@aNU9fd9lY$ zo}V%qaHm&gSfHaRJMb4UgD783ust-1IOF$>-1@jAteX4eeW`K$1CoGnMK3WLu1K-0 zMcLL)6b@as2X8{ZC>7m-M8_2;9y+^XD}DH^IZQEIY~U;2V-Nh|J_C$4pqdRQ<}9J> z4{l~G9pw+TZua9J4v@}TccRliLa|@rMAMf&#*)I3kWoM6e}Ct9b$wm(z55&TH13KF z6^T`}KS<{P?atofu!b5U*Ina}<9wFaIrz#$$(eePTIsF4=u3YU$b>-^wbpDSnJYZ-|a#&43b(CG-kcnk6FzGJ!SXqM$mSX-tkUtX*X=cBODR}=7T$<5uaRd=D?4I^1u z9t#oB%@O0)@kAU;E0YTPgJf*`*emlKFkJen(f$-+I+$bJwP$|oB(gk)HZHWo{h z#r04Geu4N-ZhR9IT7iu49#PFw+k=riIJm;GDr-w@x7MOS-mC?AzhCa0SY#>OF@xr& zXbOKyZFP*o3VO?Z-uG2Qf>nQkJvcqNj>5|gY3!Sm_;G85y9xJTk{_OOgzO`&;c0gL zNvxaD^oF=ydzMn5VQsg!$vEuMN*<|#zAN+MF_&c3ri_4W>i|46pW`dX^%RrU*4$sd z%}xTa^9lM9kcJyNmq=~TC#ke@KN(x4PzG5rJS5;B_O?GS$~!GreLc!L@N2V|;M1fK zdpW!s^W4#95;l|7%Fd*=FK!Fl8-dh9y6X!3RH0Rh0N+D4aK`(6ZSM*(U8>uSpBOW3 zfhd(iU}1J)9dAg{ojdS16S+X7SODDN+HZ{+NXnL}K`soc-mjc&uO}558CppROa0$$ zJxRd^E;wTFq3g=1_>X@F_a`@afhOtIl<0qd6l~e=q4&u41Fxu9tzoUSAacsv*&`9aMo>9Twp8tzz^X=L@rFA=PSchip?AyzeN z?&X`YA?1Kmalf;!;m3GP53&fMI4K+%JT9f&y&?{kBy+~SET1tkrIB7E@(!c<;W#?+ zGMB%i^#S6@!aKIIJEAZ@Fvnx2`%sAu5WhiCLkdeLE{SPy!EI>O{|m&nYUJ%T=RO7+C$gR zPe=9yle-qf=?w_3_|1{g`t4uuNmokbq`A~c2c8m{ym*heo#0l@ohV)(=am=Q7yy$+ zIBkllRl(wzbK8sTaqt1P*sI5)vH!L-HJ1z3^HeJm&~4M+1PEto?6rcHm!OoI{MMG$GGsqqT zR^Cvq8JitY>qR64!i3u*B`Gw1KAi^FW;vZs1t>S+pwFTc)X~8$T2cs}SfQ%0+%8@P zWi!M)b&Ycl++X`#m+9WxX4;JXum%xHLF}z7gry7%SN4_(5iRx3r4s+<@$ug2OCTy5 z7J1#DPwo~hTYj6TPUNug?7XkcWvGV5qDAVyhZ#QxxR^>iJC=&9u4+z4&9~yDL9g)| z-~F#S_<#9%2F!`+Y?QL*I6k6Iq0Wn2us@a@G~^8|YylnL`<@BCdwIH(`WiAKlQTww zOC3D%e;9k`;L5&kZ8Wx%9ox3;q+@lG4m#?NZQHhOtK)Rcj&0jE?|#pB&b{CJJN;JO zs`byRTDA7tbIdvPjPVRvLm@Rdp*8+a^wGtS0NI!h0~kb%ykn(uV=~8+4UnJk#vw&% z(^M;D;;!pGC#}EgY}d)&<{XHF+t|gvd$1i*&7jIpkm~ z>zHs!9LeIp-)hhc;)a&%LUosYq0z*{J36~|+JNHc_XE&r7#)_q!fRc0ZrP*SRk1U5(G7c)i?S=f!egCa!T>@hr%0-vW2ush!Tmdfcv@#f&v zZ>xbdM;Dj0`sunEgOto9NScdq-+g@VaP>M;LOo+locTI0Y#gDeOGK3ZCCUQZoaijA z3*=L~6S8&nJ}5zz2;-0G^~Z|plVE@Jrq570LCMxnQ-~2=q;WoHiOII2*2}>uD!`lG zTK~i+!_cM_V_!rKfeKwLqYDkY={zi=c~U??2V|sBt8u&=5sY_u65GL+p)gc~!$pia zB&+wsi&}8!U|1vk)ewndqWL_YS9+3|JK?H^-oCj{=C>{+i^a?C^IYQ8RL#DXLCj!x zh)-V-h@gaT=hol`2dAMLJW!L z;8T^Q(@f1Mkj{EIT5X3m#~g#{QxrTK977YqK@A2GvU!4nAQ3HsR%N4GT3+p0nZX~t z=8qh}$2mebU*jdAOm7$VuUJle1UX#5?cX zJG92-=V`?LeBIhKw9%F!*T(oA9Xftrh`(XIC_rg*}8~vWP}#?nLm(VHy(R`t7&(2R>o8AdE6v zB!g8PPdhk%y|I5*(YHiwtP&e7)V=ZZf>Z$y&<{7W7{LJDsV)(mr3~DEX@ANnU#Q7R zv&9uw2;)m%LaM@CFWbk(q3_DtTzTHKzE?5jK2z%pH^AJQgR+H(8%SeQr9Og4HD zL1hMpMj;3|Gd&}6hwi&xq5*H)RqIsjVD6H|we^hwSydck@~%SbkT@oCb%K<;f+#m{ z?8cMJ2wivknr{|SVDuke8}}7GzZ~dlEsu=@_pJwdyU1j?mpm7K_v7|qc})>#2L0OF zQlfs^sGJ0A%Ola7Ec6S%1jTRqQ-#;pI|7&NX~P~0Rnegy@YE8^j5zj0Xkhm9WtyX^ z0~zA=b?ZI?%k}&?NLrl6_suP$_P&mX_s+rJ zVHa-rhZ)+b;H^YoazN0(`SSm|$q^x*hh%rUlGWk%0_7s=4Co9)jf{*UYcElwMI|JR zc65`;RT+RAPGYn>)c9?*6C!{pw1z6Aa(pe8)wytRmzE)k+8zjNmGR^I^*q#Xz z#rgTfZGpN)5H}+GnZsNJ#u363D8FBwIfm_XqdK<~6afjlS%*xcMJgojdE3+q`fitV*t*P$*VD_wMS?FWW+U<@ zq$`m?2X(X2jO2qs&147i(>h$sFNjP>{&o?d}?NPM2cn zlLI*cUJwp-$EJl2DmeL_kK=`EJuhYFtT$l2Ru&~UqJ<(|8pB3Nr@)ZC|Z47s}}{Gos&-TcqpCG7#Bq9-itVH8~=1@Tp!`nym|l!+hIaC!~O z&C#Aw_#wzMEdWO83Qwo?8s)?Nfm(%}iPhd!!~QN??I@l7Ift0|(XWAd9ODL)%UU2f zW)Kb%?m&W4`_=|yB^}dLn&{s{Q(qKmItX#$*)-T4TQNq`p*%yVKMufbJ%vqzh;IY} zbrSwT(a;-DzbpdPAARZ&PZrQtWKWh}CX20iFJfA%f;H$)gc5v)e+IlGC@7!|Ke=V- z{@)n*e;pzrYEaZE@2RjPgMW(qe~^4(m_h{LGb+Ia4W$40f&cGqR{>}!py$w8OGGxB z5Ib*&sQiE4;YSqE{YaIooR|wL zAdsy5<8>M~Rx&7bN(tiSwRdq7*^gvf=cos6nE*dlpn^l=Sgz`;FwC`QkVef$w_X`E zn**KZ>Pq+EATH*CF*E1f=k)aG(?1va|2RQ@reK>XzQxL<-LvT5Ngz(|j^j2~)Mzo4{h<0Ymc9)v1gsO$S5R)h(GzRCViZr+!Pc$)-2mhefFa0T27ZW@C!6se0 zQUdq*P~Bb5kzsq_2IKF47sp}(`leQcWIkUSyaS1^Z)Ht-dI|mAhn4-`GvEKcx-1Z# zWS~(f9|utC6B#i=lc#-lzHBee^sw+`;CA@~V+Y80<;uZ38_VmdU8Xe7SzaIl@A#$b zxinhM6;YzC_Qa}C9!r`yREpjCw)`G<*dOL>+rKX|0@6y_Jruf1J=wT^ahRpVm5jp z#fK@4BrzJ&|FJky%Mq>k7HU&^v;HXNyXEppd@zY%m_T zu7WQdHg@Uql7QCiqA;{))@Aq#0N_3lKyo$rP-l$8N$L@24s^q~yJMi2Sc{5B{lUC7@~=6QzHNpIuEqUDuNX!v`O~4G1>@vHnF=2dR3WDfd97wV40<@F^ri#b= z7$tNgr{KOu15^i(i(tEzo#U!H%Z24rNRHGvs3H*s&xImRDipeUzX{_Fy`K<+Y4zSx zcl}?bKL3+n^F?`Lfhn{DZs%<$#8H!pfqDk2D^6+@KR*$e!;`S6EJ@(xP<%kWHdSiZ z$xv!O1|g)fT7Ww&`Mb>aEJaQ09C|r>PfYEPUPqm^0fEuWnTKe#c2jVIoxOUzhOOa8p+mQkol>0S8Wm*4_+199>HWr&Xi`UhI~Bj>>v2~|9oB{B$%11@GSN9-q~g< ziwO%q+kEio7U)x1E{C;c7TVXeYiDX!jZ^ zG!dNKs3P%?!$(?$s6JB^w97s?Ap>bKCrEq!w|DO`nQ{aNereQP?>vj)uQ`D%id8B# z8WYSh0li;%fM|B+avV$6SG0vVg|5O-;qn?V9>2ftmj+Q{L^x9&?(SR4SpHJfN&Sz5WCJ!i~ZoYGodq$h^l8qiL=C%NjLax?grVeAY- z|3T%LB@3scPe}v(WmkGZBR+!W6&0}AbIHPgHbqu8JRjU(%4PB+Ry?fxvr2hI;&MTe z@u~SY!eECnGXG2!V!TWb`qow5|Le|**)x$&>+1yMEc@wNgzxJM>>1}9q6ev2MzY7+=mT~pZ<7L@QU0N{7pna z1z!wX$O0-4{^?Od3J;x$^e4JH1e19!m_u0I z0hM`2?~t@ti5uLtrliH6FH{q0pZ;-Y z?D)%_^p%1nxn;cpwx4TAVl7JXazwa05%?NouM8$y#g**`WPx+F%Jgl5jbD-O{Wv z-R!MGv=adKS656hMmQa9YHr)cR!N&%e?T|!NC*Wy>y)Iu=#NJp)!F_xxzv7xPzz*{ zJbK?wqd}v&VfA5yBy}-VAsbc|dz+b36vDHhjG@n-{DBI)CrR!TC;J0KglOt*{eT8@ zK~;yR{nZR;&AIe|B))Y6%^p;vAtb3!UPyy%>5?x5ejz3UyP9cX0n z1X}zyAVDUCK=)nPtStvAvQ99Po88>}1T2gD7YCSXAzxo+kq_NHr?2PzVZ)ZZO zJXVU4n6P#7uh_Wcch`=NuP(d)$xhbuN}-=>KGmRcWGj9SI9{UVX!(a}@^*>=tc z(X0|i9jP`0{o9*bt{D3jNdC2mu-Q*h@oM)oR*Q%=&Ov31U@%+HXK}f#R&0}*#=zC3 zY3zksYI-!3Q?mATk)lgzZ zEaC|t1bd39l6X*nd{8}_z%sHgcO?|A!ck;TCc=PCCq^cK#*VX zs}zfxYITiF5!$lMqC^GexH4liLwkFC`IOn#{Sy*nmQX;%;p5Iz8hnfMCJ|2fTcC3X zR8O6<6UMN4pQw4S7ljkCLRH2e(xp$^vou`!mo|gDS`q4`@$f9?6>rlk(@jS?Z7+15 z(^h@k!|>WD8q8e4)W;9dsEPzauF4qHOpVF$Ivvs=8qM6hhwf!?ZI+BIuM(TiK(i*p zQnf~0^G`LL#tL=eI{vhR$qd_^eXPGa)uLOkz+e^FPO}a2=wo7VacN1BbZdgbt&?FZZpY@fn=6Zn-2K7xlpi>Lfikz0G);IY_&%tY2=H8v!`6tZiho*m# zh0D(TC>IO@Qb0ID9HWusk70eR$;7Yab4tIpi5K-mH!kF5(;*csuj|^j5%ww&iO=4*w5lFQWmbMY$ zwJc%PrDd?)J;&&EFXeDfx;oQk<`dFb-+$O75^4&$p%qW+Mo}&EY_0^dV6SkpfJV`! zLwW4 z1w~dgSh#EDKFGN^U3Vb8L3F6+hCsD3zKC!)s$|rJi`{2a$95SAMVj;!e1!}_2E#2wIXi{d z>jDX^P*Y04sXVVVutxC{2DXx$7qJ_n%57qEIzQAlH~GRIR)()RMkA`y^j{1{G+P8j?pjaJr$s)lL+b9EhDQo;o$*2kl-L^NoODV;)s&T3q1 zA~Jwd2q)!hLo7j*$X)A2n zAI;(PXu0bGl%Hwww>hmy))6`@Zzd~5nwGzP^l>N^a{{Uke=9Y1zz;?eMy3a`lB4*4 zxoT}hW;0RfBl>LV>$H?bz~RIuFvkk%2r6qKZj}3p{x?f|1&;RA1cnQb&BXK(k@gF- zhs=uu0hbj-2r%{}g76CC3Q=vH-t9kRv*ab^hu`QS&KrzhvH!eU(QMdY(J`E}KH zo(qO#+fC^(rJOo_8&Iv=5tTxDoQnYO*VT)#U(O}mY<#cODb2W)`m65?f!EncRm;w4 zNUR)Ss_m!lp2k}5bO5j<^(S$CqG>cJW585l8>bv{ToU7@o}gcsx^-XKsbcPqGs4{* z%>Aucw!PC-Q4P9zG6M|R=j+nVkUGAz4}zBm8D@*<^-C6qd9$NgQfj2jGMH4cbB& zlct!unmb}2zjUUbCoM^DbL^-o57)K%jA*_0e|yv*mg{$?PTSg5hshnl)+z6-44S2~ zzH7XBa1m&=noiEAEhc}J7S=@>En7dYO|OouNP&0z7-k^(m9Guju_Yw-d^GmuIBgj0$x>BjtHXyvHPP?27&#>+g_9hA_4C8-*VaVALV$Nf50#KL|q7&c(eWdpl)$6U{}# zEt}+44xFgOgjF2%b}SYuSqMg6{@N^~A$_7a&*4us|2ZMo$QPaazW7}=@I-dy?M%V* z0Mx+IY#hd1Ybq7tmOYQ9VuFn=YC+@ScOL_ifv zbu}5>bRdk1-Qz7B_&MQZ;HaeFMZ>>~9`6A^Di+y@a z(07RIwy;^EXX?LOk2g(-2DhHhk78w2%_+8(t{=qnbn1b;h`Qn$0{%y{>CwU8vo?i0 z@Bn0FVqOzkn}L!wfwNd_07g46;X8W9@~&XbYGQ+1o`CqJUA$g$i(WYXZ;7Q1*MQ{E=st>&)X1V$D&y~4jN~nOI`mld|8*k?28oNqTAP2q zvj4QjGC$^zQ{cZgF_4$v?)mO$wkUe?eGb=zObV{*{J{Z%R{`jb7GE(yEY;@N#%jD7M!zTH|+zxV3^7Xcfx6 zqqaoMTQ9X%Nq8n(V{b|{J)xwN{jZ9#eB20`*JFnEkp{#05To(Z@0WIQy|NGy_TGZo zcPCX`khR+K1Rudo3i!fBRpZneMb3At$+vGARBj#~Qtuv*{>~FF#V2lKrFLC*j=U>? zqU~L0c|z%=v&!K-L+F?HO*{;I)cIdmTq&kI-Oh9Qldim;LkXmYI%+g^BWfRxjx_Bz z|EF!^=*$5#p=C#8Wz8|LoE*lqP^%0^BjhZOTW>YWd69ubzS2h z9m*P<4~en8?uA@yc~4BZuHX?}z~+fal9)f)kL8nMdAn0}Qlm{HyKx<8AJs{-V?C&H z4JRa%1jc&{wzYHPRs-kR@9^j)W8LXuaE^p5!H;G!{F(aS??0A8j*iL&ngniEuq6)X z!r{igKCku9g(X~{))P!=Vo1D0-76JYV%q*N+e3o73V022;P%)iyji&P>tO`HcF4QS z%*>wjFu*I8&kA#r3g9tG!CMP<7T&&l0cJxmjhTK&X5K>^>VIlUPkveGB&P7mgfT?q zuQ}VgRv4m@_!f!JgJxB6;BN;O_PmS#mLx<=>aZ@K#);hCdt$WL!h*eAhhJ$JF3>-V z7sZOQh*UEK3{-7*4)78yH?XbvxO*2Y_>!$UgMjk|@+BxNbe;}v@VT9EzMrg>&X$9_ z-&MvK^00N&vodM%BM!X)CA2;LeiG~2Wk|sIzmSyw&dFWI|Epw1O_00oOtDg@Mt;c} z0^PyGibZZ?gM>JiPH|;HB*(jBy2?OVA+<)^N;!;AhX%-idCz@M9T~$0oZ`BqEX(#v^3N{dNpz+MN;MV=%LKtm(8xgM07LccdJk#2W#a z%O>h?zF09@*CLamzeZH5ALex1!5GtS#rj6eDca7-pv>7$0soyPJ_VPy2oA0Ymy-uB zPy9N6*c$z-e1aHKo!x@t*xNf3^+-csDNEmUQz4v%$z%`g}_jCihA0@X= zVNI3D=$T=9dh(P`PsQ|dWHlw*8t0}=*{6;&dPjgbFwxf#a_U}x6by_=7{C+vd7biS z16aBiffGN15z(iJum|S3u*H2G`1$;^d}=>ZLnHLpBW_E2$rww#ec@TuDR*+>PBSnf z{XhL*Qt!K}Y)#jR$RrWO&OIN_m}dbUEth#~Hc_DTl~x7@Xu4k#)LcCc$tdMB!mJJ> zr++RfH8A0Yj?*dhwW;tSI9i8Vst;ZkwP0wgrNjL8et?-2_&OX<#${itSz1%Fx{1Lz zi^ZAfnI}`ALQGE%;4n(? z`Ta$d*uLdI=w(D$PYKZT(`ev~q6dhq;K(0X-|+V8enM+Ha&bfBDpY3&A3G%P{5CPW zBjQ35gPIeeR23@)uF!(+;C2qM)K_RX+-5Cw1-kuktiOTBXG^kL3tP|}Bbc2L8c$Hr zc$PZA)@>70=ayYEjaOcYH-!NqAAnc1DKM7Cg5!{K;VZVBqof&=o6^axg@6-U49M?Q zGFtiaM4ho81+&kMdG5%fpHv>T)ZCYNujv2a$Vzl8-!+7ZMGddKqWt7fB z@z$D~bfco` zXNpF1O}Bf8``zXY+1aCLF&?9BwwZf`?5g8Nvb#^33}JrS0q< z3()=xghJe{RgYMDL$i`Y_bmWITlZfn%-awoOUijvbM-~J8LQHKX=V?Na>~Cu+6CNX zN?kYvc8Gg02>tJJ#Q(}rnZOkfInSfhPgWI-83d zl}T;XQ!NGX!F-MUZS+kX@GS2UJ z@wlHZ;m!mPqPajR;}|&%skARtIHr2**vymZLLD*BXd~J|*h#p%+ypThbfMdGUUT&} zJ9VOFcRGZzTpm9s4q-5-iVa2(s(aml;RX_Z(6qI#5`ThQ{IaP(pp92zL{V_7K@cJhjDI5XX=e=oxLNtF8HZPfii-iH& zDxzpl6-<_qt`gTYMFD#QM;Vqt!=Ocslpiy&GC|hHtHw6ZjCBY{K|Km;{_f4(AQuyP zd@fW#y0@rDuXh;<5i|O!KXG@g+3|>3InvmTveFHXj*YG@tk>}UqV-0=q(FUR&!yoviaRQrb0q}0I2N=?)*SZn; zTZXnfM(V%*tjOb$O#1S8L4|%jb1`7(EeQ}rDrUclJ4BLD zYz2JAR^^l1>jRExUWO=Bw;3e@v!>_TL}XaK9&^R%BvK0iM@FVG)36wxV@wH)332t0 ziW}S1{X?sjlNu5jgsY&gF60Z{l@Jb#=AHh=MV+fgQeI^I;pm=|VY7F=NsUK0wVyVB z%>{0f`4K4!&$bQwgKs}xApp@f#L*@}$zO!;+pI0%;fKS+`4M*h`J<2AenQqc%lCkt z0O%A&gVXXT?j3Jrku!XD=^P}U@I}!_&MM_DL&4O+7U*5yDe68HjPk(FIvo1(NI`}l zgFM**4%c@x29~cTh)BBLFibVJfg#%YO3zxRt7QOlb?WfQrnBJJLeUrHzMG)`U^KE& zpROU;8;K?^HvFvUxVGocLtFv*foVY$G7F}D8^AQ9!t)yQ7h%WeiGvHFDP4aV93VLw z*w*el5~0n;R#gm9@BK`kYuyfYybxLB(tZkW2Gn=Oi><+`RadM+X!upf;p{%pZ}mR{ zs~RcfT1hSBSiN>YRLZ}K?DZ} z#~y~f?~p6H`)7j}C~%X{0AXMHT)Tm>D`to%*WGaoWLMYU{d_sXfi$p##S6okY*=s@ zVL?x8G*%nIt^{kosbvFay z$S9@D#TK^@?54_hc<4#pkgiUaF*!t3LQMZyCCEEOMCA@QMIbT=k_2xOT<6Ds=kqSn z2c|M*!SDG5_pZ;c)wLU)yeB zh=^8sZ!|9a( zVlmaFrIk7ldbav~d*R|pN~K`_fq>P=tG#a^82BERK+yYOoaDX?q&>*4} z-M&8Pxl~JhJZnIG%-?pr9~0G;6$E>D_qeVDv&9O(rOTsO=?ccr49B5b190V@Ey{z5 zw19n&YT=&eDG-MS7lM#!Yewqy?|AMH`vD(K45imEGu~qvT`*Fjx7}{tAdcnA%uZ|4 zdhAMwA&f6O2h;MYUA8O_?)xF5!^*Z+-$2y|Ka*^=5VNy3eE+DL?BqmTDwTbQZ?M?} z-4EZBaK~eF&OH_*xWl$Ep6t1#bKXko1##l#F$$FxN*p(U7aJ@*`0XtMoJ!+(Y)G%& zOyK=<#qcgw`ACzDNKU2CZ;&Lj$J_hr)-8+;4@-YKC&Tum5zQP)h+$5NE;mZ_G#rUE z*K4|w1RDNd$L-(YxNe`Q?q<}f#F{#{6LPtncDQEI#3^hI{gh?wNc(rlrj0`ulLNK4 z&TVQi(K%*LCg1M`gGNWWO~YnC@801}X_?M4c?f0?==-WHh5D?3DOR(ul)$C<^KBge zrbI=8xc?EcnsCFHVsz2)GRS0DxtVIP{%NK>v(JYzF?p_YsmqbJcw7%T<79maa+xRz z&N!%*rKZo6g6rt`i(D2vWS0$pFms3D@8}_eIpAcFFDeyRJ5T0v>4JDJtnB`)p{S;6 ztPUq(nsEc*7%la7q{U;YAp(!wShyC`K!^9xKi25q|L=d7=h@cDR!^14M}2(da_T0a zPiMyYmGTzF1qYzEQH?YvF(VIY{6-3b_4pP&;A1@)6au$Lvk1|Vm3bw*@5!u)I5eR_ zz>|B^8B~T&wYw6eUdO5z38IPXWq%|*W`O6s5wLi+(9xRn6rUv0**VR+${C~_R<{GC zJyAsb`d2?URCX%^#&o3lBDJ4%X6>s7>I4|BJy%aEEPbvMHMA_5!f!&%KoBG`cx4p1 zF)MuwBg9$FUWqjWPoW$y>x+T@KeM%#A$;aeL?iig%)pk1lo3N2w5L)}TT9pwoa$vi zkIcL8075XmiOqd06oeoHyE_MCllfTV;c6#P%DmP~A1uW0*~_6{*f5ES^;8jD2m@iDiz zAWDQq=IrkK%tvu=*10UR+lu9z*C>W=$S-P+N&$QAU#T}$?KQCd_o`I34=YC4KgI$) zaeN(4)%6DsI$X1AKqaI8#cslV{q95liBtA&8`{TpX!$#2WIedI$iZpJAxLxGY$lL@ zu>4N~v%|KYD_V8T;Jef>VqS=!zXqI+QM2*CNB?1qCe}Tlz~*fctk>+frrTe%PW0tW zBQR}GI2L{ttf@<+qG9-}DF2ly)!0CQ7m+r_?)|RYbEbD__~)QNiF#f*8_Je|7XmyT z5D*k6C_RadiHTVos+y(J*IG#05(Px$Ac6vkN$ZC}xQUWL<;GooWMxyeQ~SpHQ?o?d zRG<~v%}@w&oxGDI`-nI)Ph}B{WQZ^qICT(b@o^=n{lWZEFnipVHECJcIRlCs0grv; zX$BDxQCf(N>60b87Pvn6sVs=cBVOF=%5IsH)% zi(41ou#f&?-%5AJnYm<==mJy*W_tR4`=I6FcTyzfr)t$fQ|&g`1gf3@XrWw_7rQsh zs@%0kM2u@*{`$^YW)f705w^#__C(5PIh0!MArl#tFoBNiYz*dOl2) zg13V|o9a6ZAQT8yZ?FNeA!}yGF1!um5m9lS&v(iElJAFj<2j1h1neF2Z6+x7PmYQ2F4x^ zGggr|nd^OY56?hL%py$qhH>~{H5lKRVfXT4bTm$HkWs=6*WN~)5dn_jm*;AC$@!gH zZK?-spn_AhWaEA7dj-8cujY_^%p8>A``c!n>$5sPETUM094O#x$nhP;w39HG+Qb9O zYV-L(7PY|K9JdfHC8mG5o24af?xAgZFIO$7{c?#dKuH(oL+b`hy=qk~Nmr{hzZVq) zB{Wo1z>?KOs#@n;8I6E$?Ke#J%W3%awidFI^V1UwgL*d?;8ZF>9h>47N)`&v9y`Q1 z?yXbAoK4K#$~G8x9F1iB2ZnngGDNsUN3|%ET!o)#aK#&=?SYvuIr0kXpKJwHA|?oR z5MvDFv7YwQ=DAy3*v%6J`SZ$)hM&h3N~H*~TFc#0{;j=1x~PB`gvlKKkW5iSH-vlC zLK*oa&Wj7mR=5%o_-|UCLput6pQHbj0#Gh`S=}ZGQwX6zR)zAq@5JOsgz8n_qpF}8 zr!ZvFdtvF+Y-S;|Ryd?cpy3k7vSMRs>u;2cqIsVc+9P!;{y<<939L z4#p~p_m}DgMV5IyM1rQLC85_FkA~An*N6^~Bb7yT21qmv_eBP2qpDm{xynB^z2s2z z#BW&z>|9rTn4E6j3E_#oPtyKg{G{)(E_)HHfawl%`o-cit%cu?2ivL+b|}g*eVjsg zw9vr2#=1JgT2tq0_Xw8Eq!-4}u1HF+BB{!^i@74tFU+O{;zeiGX;jw6yO9Q|>HE*C zQy;Y4_QjH^LxGMRH%bXY2czJX6QEMo|D^YDr>b*@`%9faG!{Ndi(;-JN}&de*Qu1d z#kR0CW%G-svIRTbig*=gK>r0@OrU5LAef765k87lH1l;;mc49?oE*~_ESWQ3e7Do^ zJnWTJy#j0QR?f5$H&10S+$V++UEl-@Jvn~Nag1Kn+>C)0P|YOC$V;Bh&r4N|V9J=e z87WV@_WDUXArynECX%?5^1Y_u);vg^fWB!GpP1Gp1+Ky$abRh`L_AfLqLejCa1r(- zA$e=bK%+SfR^T)OZ6G|HR3f~vN6|pJ(wd0HW%Vn`qEg|FpT=CryP;+XZtu#BD2B@= z-9nBJZRzG3^uyDGV7Qy3Xyl2O;!-SX;ZF&@ONN~?9kQb@vMDJdB^W?bFcbpwLo8<% z_s(Mvql}vBtJE&VH#|zLbgs!5kt22)RM*Fb)E8N!xJh-c6kVyv zKI#}}>b;w$6WXU4b-DDjfS<3{qDJMu3Hi!3%2zix!uxCPnVQT*@E+*8;+u7+1GCuUtL3Z#DmpqW3N2W5=nc`nW&TiV ztsKVlIH+0TkVzy5oli!lRB=+~&*dkQq_j;0l+HTva!O9DChaVlWGID z3FRc+nV}hrc~L@i22xVenIkG4amgVVie<0bWH};|2gL^z4uZ;{poYjJKD%GXou2xo zU-bGHU&2Hv72a56=0=Uk=uNFccP`$`WG>x!)sJ!d6O2a1ABo`MWg|2u>EXhx3pBz! z;>_co3d&HuPX#1y<7c=wa~^4@X0G`j8aLcK8UOcM*{J%ySa^MC)Pu3>=02H->M8fA z(?*`cDfLpvZt6zrx_$2_N6m$m&t2M^mr87mhbup2Jx}P6G^x}7JZ~|}tYN%g2a(q* z>qz~58?1y~;*P5al`bd)nk@4(Q=z=bcPAiDbxn}yO|Pn@(W=u_d*iEgWq5?%U=jPczZ+`9%Cr>eMpIC@oR2pE&9=ZO#h6st^|pNtouZz zY4~NKV)jnPZ&}qaI*Y+j;{mJ9Km*|~(;jP^J9YGBW(6lP(5A;v1hN}W*XsY<_S44# z`;pc_7e-Pc0j`z1Trl6@+b>QU!URrZBy*LeQO4&QS zR+Q>?7kl#SvpDXBEXW6|_{m7Srj){8bG+Urd@eprM3u0c7!5dZ3ORy?62VEiIlY;> zpNteGeUpbDDXKb+uBCdreu2Q({^Jss#CpmHZy3kOO^P7O^{NCL$AG!}g58#6LpEqr zGWc9mF?tjY{FV-`Muw=%a?ZQpRPNly+L9nxIL;#q^=wb^+4uNVj4$&xxLVR8Bq{K2 z>F?Bm;k3ts-=#|cj}J-(FXn1ACL&2+y{N)(6@{#>1KBCx{Esg|xmNtqkGCnU@RSOj0uuxp$ z%sQ|%|8CeBG*NS@1TC$$sF>~8g9_2XUGf%TtddPhqaA@erUglo{ikI70X0i@V6f}lr#L|Xwf#UI0Hl-#H2VXJjjz&`eCqV= zg_O5nFU5~%ie?W=##{`0{e=eDcs%4=J*P zO@G!WTzUSib5iWeh5yk%^WjW?$yx3{^?9K0sJJT)*{G(;*9DlAy90-%SYiP>elLVe zeR-Ji=rTX=kQsG+i>BBgy8P=Yz<9E!n=17_+ixAe%3?*NL;fD(Pdh+N<6liSWH>g= zNuBuTC99koob-~QU%dQI>-RH0yOw~yRZQ~xkp5V$A9cp4A8qH(&BUaZpE1nP2Mzl6 z?JAnigfep9P|U#9L(;Y-&DGnOy4<@J2|L9aOMd~8%+Kb4RzWDm3!(15k{+c~#Ns5B z9m%ZlPmX4hjou*TQeQet`knGCG(N4n_kgQxc1U=x9Epz{_m1+)kz2nJT2OgQ1kvwM zB;;D4t8(As=uDism!#3l?Afg8#H3NI@F+r(9Mt!Ut+Fc3Rw074|Fw3I|7Y#w4$eKo zhp1TGW`2z6`5?&rQaQ3HpRtI)`08FyRl>4v0_Uyg0l`8`pxa8}eZjG!Hm>G(~||H_W3GP;;# zI3Rw>Vll8wT{NX)!@SEmnDYHW&GLbeymNAgd~7^2i3?4+*ubUkU`d`t)qF!ePx)(t z7AiRsJt3_(y&)4dS-w?Okm`K2;R>S=>U+82xTas>;;DG|vwXhduDvKN3)k0M467xn zN}hXZ>N3UD=%#1anp%ufv+v=E*+=6CoEiun zeZ>q@K%@&_YqbxT8HWx-q#{bMzFb$uakSrVc42*+~l>$g8*cGp|H(^V4k1~hK(M30c-w|d3l0wog>bQ+goSUrPV!5`o` zqFKXIhXeg2=MvRp3cc~7>_BG!=~^S7y8R`J>#Ji2)GJnDem6&Ld@U~dhHBl!Vw-e$ zSqqOij>XS@=1tw5O_(Ax^FroQiK_^zowiSao&GrcbiT2Hc_I;3ZtH?6&2No!%tIzU zv=5h@(5=)bKXF={1{UGle2^$ivC?&?KyFtqwSLPSyW~9SgBCahasWh+I|5%MS0}`6_(8FD#UpH71mL^=+_N-qw zoQbdxPp@bCs2XUCVkIQPq*>v(a0NO@77z=j5~>&dM>g%%kI9X#JL$pv`@~%#cm{^e z>VlEwpV>P|g4R6s!Z~3ms|HpfZoK#f$A1;7@^QVS^ zgbhw`CQs+!sT?MRocJx_rfFa@Ne}aP0rvINDWH9%%-R> zd$>&>b=arPL_K|})TO4*#pE7>7-OeU8dx;n`F(<^9xueo7R1bVeS4Em3UpJlP1Rrd z_U!9|`nfygu_C(;CkNgnGOpDq#VTnrF`QG2asMB-zB#(J+P8vHm zwr!(v(%430n~iPTHum;?cYn|BHvipwpXZ!=&dhiGnHh;P%fppgd}-4^3c2_}?(kwQ zVWzcNKEzJeC%WU=qS z7CcGyub{gVh0hAAPT*_T4*Hw?g0;zW$9&I6xa?si)EQB4#;taPNjq}LEp8+@P%Q;1 z)1g1pUEyn5&Np0+kGMWfc69N&WtB;9k1%0oiw~pvq`ceq_}4H;F~tQWA9}xvAeuou*H$>Pq)F~^imD|2ROMay@=}fr#x559QC@A zFEYFEE#8PpuU5oFOYml{XgRq#D5dBHpGGy!z`t(a)5&_mW1so@n!e&o_T^tzd?df* za2|6Y^e20QbXo5Y{C2z{Z^C@EW##31M(6{i0kCF{$bOdX;?FX#6Qz2l3vI9}5ya{i zN>~KzZK2mD(FWr`ryE~6vA(brX8HYQN=Ib9{`@GRD$+wyE?+4oismp1`Sqysmi=6Q zWB9C2tPcMcqamGO~LaXKE!J)7y=GM ze|wX*DqdY!sMH#RN(U3a`9|Q>4q{XH)ogcCDUUmos{nl%0ZV8?aSUyfFNI+?_O)To(SYb3D-#|Uy9VhY&UJY8 zaE-6;y;JDNHg|y-taX@C^-fi`#H4Okf$9hd5cPI}VAT@X2jKy&v+6nXjH{O&%v_(j z+qjHzUi7mPGz_{ATWrt^qX`GjdQ5B>iQqp=N~%pJb!ANx$zF!pSkL06fRF1A%%wLX z7x{Oo`HR_B7q4r+5l(vRdf!;Z0~-NY8~OM?{o{Irz>Qv{}`gP&NgAtZdd;ijLVA=js_jTumYNKu#U!af!6@xsw1pxFZ28{Aq+LEw|mQ0G4&zGdn}hfsR)heR0C}MDJP#LfPyr zoe3l&6uSRR18<*Jv$;qup!!Q2=hcPwL8cxq(ohmlI#m ztG`U}*5_|8D%d=*Z+JLD6c|t#eBIvrj~C*>2ESbm3p5ry zBsB1pucUd6tgX`_tfM$EfTt@?F!2vOPT3rIQ2jq1!GAMT4>W(@VP=_X3k)GSMLwdt zvQkR`%Rz3SpM-)23`PEDh60an$vj5k1lHNTf@taV*kK%Ua^l% zzQDybRHMTY=bhzZw0X3SkcsSOLM>$T%}J09zGS!d6SW|Q^6$gjFVH21HC5lXu%|Z9 z){NAu&A`Walm#eQ#Oqr{-e1RhCTpi?+)xdG+tJzn={ zQuUxh0r3+&UI4q}pRT^S*vt;bciSzL-~aH}P!1?53NG95>evhxX;H-;uTkKW%1jO` zY7uAZ=wnm8-O*?SX&mp}pFaH!IwRJNbvFxGzU&nHW`GB0TVIW9nW)OY+uptm zc^8w=zaY*%$n;0>H*9^+=*Qyqf)A==fD$k9+rj*RkyQdqa5g@PV2bI?AkPJ|Ju^+T zHcaZEyb?M0$(I*6t6;A9Tmq7yMv#k2{Pm#Dt<9a~9Q7jg6njFE! z1He>J)(F|qthG7W#GF>qfFuT?^+)1PjlKaw z-=q(T@Wxw*Fzw;h6O=ojHGvBlw*c@v@btUfiiFa&#t7I^vy3_6Jm7+}W-cz|7YEDt zXO(#F))wC|i)>RV|17_m&$^4rK2YLi%)=n84Hh6sCKc#q=`caKPpJG<#(%^!u8n7dPoj*=O!2cc^uF zcnrw5=WraDHP_hjSupX03Wm>Y;93I9j~lj{Rl*6~-So?nC`fEA@*T&cq;G|6&3W=+;O5s7c&R)%4dNh1BoHl4X78!>sgskpI(w zixod!Wx8ka;wlwd0REKg9H&tAy5;7QuPTQ=|32cJPiQxP8bq9~&>Uv&7I}R7y1D+L z30i}-@wuBLnOt`m7C3pf#Mz_o2)=p^(!KC0ae0bn^e7nX>3YUNvzUNF?RU#|MCSn7 zJanlOY2TvfV5UWPSi~UAu8_&xiFPr;Zyk-F+gagD0zkRbF>vZ2airm8MlzyqRh5g@ zoI$u}Xy7TontuZznp}lVOxb9Lt`c}fe^8E5Bk~1O1BP?$u_vo9w?>1>v0o^_@Et{% z2QtT4UR-mXhs(;SU!!_ZJ{OK6;#w$)TU7V94?^Hvb3Ey)P%xBMqd@;^CU8RaBzQ76 zINR(k{s!b(6EhZ45FNtAEFW((ThQh0fhnWaCDcTgaATtays^MW;C9Io*x(L>#;5`h zan($sVH|A8BYxT67Y^-F5$rqO^N&TmK=>=CJEMhx;6hfqhd0;{)3RBkSzTBU*Y`H{ zU}q;j|0kHIDudQeG&SjDW| z{}`(1c*+F32+mTn9&-ScLlC`SJEozkET#z__~CsmvvX|XgEs@cL4Kqa>M#w>hCsnl!aI;dquOrX9IW(`H0pVyg=2w zMjGlae1O(ysL8o;pKepMh~y9DBvOobSCGIC&S>2&Vog}K;Ebx?0C|DKkCFRT$(Ixg z>pP&`Q=>GZdsl0k$#c(+XJ`tCv@IyxXx5|ZjimH`rt$ZkY6*LqKkrdPsn(KWWqmY+ zGIs*rxOHaF(fWIDy@4undMVzDMuT`^5IS*%RiB^qy|*BdK!XyS$_gP~aT1;-P$!n+$#!iu+A z!Gp*5IqOh~dF5*ZzCAV1vf~r6W+|&!rI+VKp89vgy4j5d2UwlgI^ZHjXl`YD|0Y3y zs6uw#r`jJ2w(`-&;9>?3G4)hGN7+LYxauyJcOr-SK~9Ys9I%)$48$68&FUF#$*1!& zq2B^N;3abyP;5e1sM~0ep(5d+=vw@lU!n9Se4cFT8r)E?s4_GAD&qjKTSvkZfbgvG z1sI?;`D}%#v&Gsg!tkZIp-3l5+ZtqKL@(nc=2<)X?DFRB5&^{BlR4;6YZID29mMV_{^lvnE1C6$%lO0y_3tqaNLY4jgHOox) zcX$>&Zy*q6wY^`W1#K$D6ge#Od`|d0D4#lYv_OP|+_T)YDUsQ3enY9T_2Eu?uY-lV z@iJMBP}P=fz!r<@D+6l7_KOpe`CM`4@IAx8Zd>$vi#7o&`5-6dT)m#pJ3!a_Uyn3~ z9jMLQj=#Y{@$j|)?N)z|Mmm~KUA%mI+R-GRK4Rd%QB zX^k*SmP-iWkd9(a9zUpva_sYt$j+ljbiZ^?Eg|0B-C37FuCE`xI^U=x%$T7oDN$oS zT)?t3Xoq#&X9(wsqSRt<_zJ%M$_n`tT+#{;Y;lTMFTl-=Y@5I=3f9ZzWh(k1+PKiK zyeP`Q5FXm@Eu3D-r<_tJ=tXI|!a|y3>v1R|l~Rvv!rb7q5A2MbEFwFJg+6wuX0*{= z;~CLuB}g)2FIp&~_c~mJO$gh~M(4HEGq;5J6RkAd#@KoOuOS5fLPi0MOTf@Ur61_a zQrOX1-d>;WAnho3PgU#YCZZTGJFyBCVwNPkfmM<5yb<#eXe7}#A*6M*O&tzBRxJRW?%p;LR z0|Q6nJhc|eJjP>ws6NZhWHY!7U zc~nbxM6u^g^XzX7l&}hM1ceg=8#Ae$S!D~L))CV2j!NcWH3 zCc6kb<@}!r(rJ4hZ}mZe^2G={0Wg}{j)lY$^}OX-R(aa_wA7|HyQd>)#>Fvdl!KM=g)BrWO(oXX}3sr>>M zS7(1JO%&CREBQb}kJF_2}+^}~X>R)vQ zN)TE?i&+uu=VHCH=mo+N<2|J+e>eUpJpGmeRAJVwfK4B$!Y`}N7&79!Lh~7`@+~4xtsTfDw@}kTy6flm zK=8fsO6Y8k$8pz8%`hs0x;D~oB%O${_dy_n=5ONy zu`BL?;26iVgIil5$Me&VTf7ndkS`LhtldIF$QUT#`Ewv}lGG<@ zETryt7B;q3*)g(6t|FiG(P*G0C9zDfk8Z&faKCbLPubFs7O7L?Oxkf%dP;EM<3wUx zoZxR5TKgssBCqhZC6x3f>h4pgx-Hc3Ki#~+a9I`+x6L3jFfa@nzGtuLTtdFARf4&t zLxxQha=vjW=h8pps)E7g?E2_o@l^4_8o${n&$|ap?%@t0P@wYS1t#FseSTM9Z2i$O z+P_YDMmis@#WZHmxuo(3iPm8LT|`?jiCu5XkdSWO8S4F5R5>(*RBmXttM;3Z?e+ZY zc|+N1?o} zTELR7=)X7wKykmQ;V?MOVK>4~g>*4~#HuS`0)u4@74}Pp&Pu51w70lGA`TXh1CbfV zC9&*Lvi)L1psEdUISQ zTAV;qsHlzpYpPV(l02VL9CGuQi1ydzEVsp%DqtPIQU@{}HhcVRqjwEq;rBTmLExdT zXy$RT0?kx5h?m6@#H+@PST&wm+KV}LJwaOtmAFZAaa~A6gy>}I*}+sX`f%gY##;N>PodEqk2H?^k+^Iq%bksd+W3dJ#K(ur-mKW1*ZxEg~HHzkHu9vaR zE$jF_0^xK$>hu;X?v;_ADk~9c^JEVqnco>AlMS(KOa_yT1?oyLOQpUjwH88b%3|8rQn2-sLxwN9|jFduE6N zv|5GrwpqnOU5eXPcT;%;@PVkV7|^<(tL)U6^c5BNU54u^3$Bzb>WEAkr7RN0b&nfp z(}wG`YcU`AS^tesW)`%t?cmG{$9rSLabWo%4FsmcZ0ZXVz+*j3Z4C+;TMI|Jo%pfk zs<8Xz8kt+An_Q&V9if}|qjSH`={*Leb9(Y3Cps|<{@iwFHCu!)S6B6GJ7Gv<(2SrD zuFP?|*TdRmf?ikbafRESk;)c6SgrY6=b9sI3{YVEl_i-pR-{JvHRmj?J5%zlJL8(0 z^_Ulp0Ng9Ubwl_elQ)Vt7;$qV#ayYJVjwy_sMGxBn-c9#gm?6tD7rmM^}7n)RZ>M1 z4%^Z8zF7aoD17uef9JOQe3j8L#tD}#ZUSz-fc^c`prr|7n=(5ZqcToDtVwWuL1e+1 zXo6N)5+}PmMgijsuCZf_?#wr&8R`UoH(yT=9P^{GS8DfC8`s-PG^No#ZTef*-2S)R zuPQ#mtuZWb^0{aDLDBbUbSn-Rb?#9)u(k3?03ri|D zuCL@*6W#u@K2A5JqGel7U~NqxU&xksYm4yx^c?4*k|WBT-HDgZPg@&xgRK=?Q}OHW z-6_li#|I*%>-zvU<9)R3iG3qP?M`yXh(mUiL_^Gt@UHa$H5f2h?^3WcZpyJs0)TdDfgw2*pmA4sS*qEnXs^%>ZGO$+pD>3y*Y0y=|g6x39Xd zN2JY9K|aUSyZY|MKHJsNF8$@F8h>^))%FgF1nU8DB4InRzCIf;MU`^b(&*X>P-pr@ zb*IQ*ee@uVQmPcH<{IJRcI$(63&bui4ykzDciidU3i6}i+$rIyGs&@*XDHSlu}GYB zxX1>G4hKq$BpGKU0z;zUF@0sN zLRTdr!}=4_0?RenCQX(oZ1+Wz<%&KdIn?%NQFAK<1VV<^;F^RcDqq2cLO=jwuW}mZ z-E4vD;6z0Fpp9N$*+BJcRITO5Bxt-W_?7S)QRrr@KXbzh&GkXB=HuL>ACK{PqGw@? zRIM8`HU8kzBORh#LQJI{1X7p%%K)w3X*dx zSATK0`h_)95Hu*0EtSr`8aTewl@s^uoRd4CX1lY;g7WLzLJmwb8P79YHb{B=!B?v` z7aGYGB548)t$5C6m-wPz{5623b5+|rY6uB5crVvV7eQraFjl>ZFq3`uP-1%8(6S$} zA?&XnebiTpv}`0>Zv>l@<#7k+ZR#sX;TU?1gbGDm-)l>LDC~?J(%?7}5<6vdFd9l& zGGlE;mSRD;oAcwHkZ=36!hDcp`kUQvZv*-f*k@_H{)&a;n_G+5jV^tJ<2gohFmO9G zayYYZ^BC!0*+oy!uiB^S)_i<$O-wtcFT5|1F&Q;GZ#AM(O6L>>Gtpcn6U1-T(PP%j z(ah$`0%?p^x9;X4^p`79H(AWd?Hma~Wo2cRY*}QcS?55flz5Kv*%wXN33kqXnY6V5 z%8QN2z62caDPX`{>Vns-&lVeQmf)({%*;AicVlKC1e)rrO=87%^KU9IY(6yb=NU1T zR422Sg{;WF^zC0?%=_t1njb!?yH);nS3Npq|LiEyLUB+dMwp_Vy78yhFOS2R(LI8I zbYNmeRy}ZH(^f)}AXhwE?AOsF0LR3vFvRP~C3flj^mQ@+ zwerH1Xsj8VYR zb*|MP#Dhyo@c7|h=%e4wyQ%2^tN7=D=HG%2)dZTF2Zo+M(hH>o|~bK zA|AU8xV~1A5oG-kz(+->>%yC>)h|>Ai=KD)TIy&XbWdh6VNk)F&)xU&I#|u2qqjm{ zf1Zx|Rqx&hR}zbzC4bEgn6=~SaEE|uDC>Qjp1)Ag3ksP45up*Ft=J=f85vtT>FaBuCY;^qi2RjeUWyaE8oI(I5gt1BeH3{H&+4?v* zI2bU94#bg;7$N4G+a892N1rvO`rSpY?28*aZ z@iqrQc+>SL*0d_EE)cBmk3$qnjoH{a1^Ab<(V03<$7?+nM$<;>K79Eg2XY||N_<3dpiHVVJV;cSrFfb_QFh|mAR46B{X1N3lVtiwCuV|ZI4 zq~A3ImD|rJ8}+z2CvW((l{a_W?33#N*iN6gbM23x2EDhDRTx(T%uB!kybJz9wVCpq zrMz31Ii^AY*!|w2*ojRb(Wkk=JHB21|GZH~hdvG_k38 zrE6!fwpuP>v*^!^MNhrOu|gA`ZVqUup6o*D4HP_(bwYgN=5pse9&XxL)|x{V5t8K) z=i?t(ll2N<zQoZl9;m?@k&tTJ)MYQ1$6YLzue#=j?NO? zhXWVEgk}b@DE#nA{F2U}e#1Me5!e?86C@91jff3ys~eNWvD61rtBu={;o!A1@>gpg zFh2$!nAnZ=y)pfTg?#sbQIWRd+c9T3uzBq)Uu5Um$N`)*bEG2HC52YD((DH=sj9dC zw%wSS3y;I@74M8!57 z%_CJ@Y$e>U#Id7$*_r+C*zYxi{vLA?(w%xqUw>j8aZgHv{0@w|;;>4^+g8&mH9y!S zmB}sCcKd2sRl?i>#j5#glG_-`?^Q`C=TOlfwd1_u0x32%sue6gkf^_SrOZjF59cn9 z`7I|0g_Js=fY&|C6`P*DQajLp|E#Nj3`HG|OTUOi<2KVhMOfz3R-3z5thKkU zM!ec%+e)+j-jAjqa71{kIc+o?Oo&q_U~<1il`0)AOq*!Xmk}59W4SxEyUOM`7BF8- z*vvtP;2!!})tRxgJJQ`oG7;v^*sciB_32q*^ir~+!iF&}4rc)u`Sk{}{=FLd7)ev0 zNC_6kLcUl~!{K}q!|2*5=P-AuvPFVpZrn|m4FbTI%;U)Jv^o@l;othDzXndiq#@tS zatJp>@D4eg;;CCF9Z4$~NwBVU=A9wI$b);A#o`xpsM|_;*Q_SO2A3Y5SVOo*AWKS* z#M*`{?MHq!Xxv|^dmsWNFx9I0;^7cyo5s#$N)`HNcr`dW_@pZdFvJOp(tcL^MpheJ zxK@koTSrf?t1J6Dq)o{>BP>}eKUqH+5*8s?CLs)muh!k)g`J|6wh1s7jeJE2$u}5E zFq;Ua(#w*CnWk)b7db={RSkl-X;$M)xuOF=g=stQwGC7B*bP= zUl}>^&IVXMNJ&3Ys1h>d_Vc z@pizeTxIDqT#msLR4c|uc}OYi&vvIc-P^yh)^wW8W>3IQ=n7Jblg{L|^5y0|o2a}Z zb=dH%=J^Wd1LH(w01d1SF`c1CDhOby!mu%5N#Hk*P_=%qRHls-jP#W;6w^8z1>{dg z4~SqxGWCJan)lc`je5Ma+n`7B|0H%^P-1nCQQ0c5Z#StX33oM$?Gfupo@+k*)$F+aZP{Um2uk(d{}pDUlp zvLjZbWMZ7oQdVHg#Mo27?NtW%x-bm&DC*gk46Zg>1`c_D0m;s$DN?$lQmIBB zN{}G=#iHEf`G?ws#Owv*16L?7A9#pNi-&-N_#le*(^MiC4gdkc5Trz3kOen?%B2{I z9V+C@=h(2TDdcNEwZcPtI^fP*uL)I+z&nklY#t>-aoHRD*W}uwE5oQ}Z2ko~>717D!hHzl0p8?)9^>*91{zea)r^J3YVZAY)w5mypMUkWmI9`wKXGVw z3eeJ-)!`gCK&s!yT%oAnPAJgiCt#3&c`r%i%>x<40-KEP(#`TV4HSoiQ_I^TL2yeq z4V2I`28F@@!-GB`VWZJr%MY(E5*$FJM7)tq4)>c=-P>rq-y7V@14FRdaK#=t`7}e; zja`s7y5MT@#kwM7{NLW-i#i}njGN;i0m>30!NJ5AMj&8d8Kj&%kP_uSGfOC3S9`)g za{@O~*-*>q>6steI0Ufqqb@F>d#PsS%xC2K`h_ev!5Tase{%d(TC=OJ8?WCykqQF?rSIL!Fmqe$wAXY$<^%mYdjo& zj*u|QXB^1(61=xXu_!KM^T9O1-N zFQM?=qi_RNw!3a8{PyTSjX$gX_#sfPBvd@romMh#)^HFCtYhZbO`gQ48}7V^ByDa5 z@mD=mB5+=lLBi(4ax+)aEno=;BZ=zVf3B)ym7Uxss@>$QamCnstA;drSDwBck^e1& z3y;l=#ji{UV*Y(NDk&$hZL!g;_JrS|KO|dtqCJ+Dj=nv^gj!joh@s=m%#7Eq~#X zq5t6r+~v918;2P(2Nqb$D$sVtWpn+4g2(5M#N7ru_M;RyqS^Lfy_C;x>4))#87ahB z8c#&-fb90o_a`oA2+(SEwD%^${>WP)NmyzH9OQmJ{@}%FN`ti{Vd$hosymPq<^)me zaa*jZ2I?zLH7{~SJcN*DZybB?>%Q(tapVH}K;WW}9&-Td&hO_Xnp3{xcO=jyDw05(_a+em>2_rKD>iuzCw&YIoT)&s}n zJ}hOyef?S9jap*fG7{7uDl7Gs^o7YAQRU>)KjF10F2<$gBzJR_#v{5-wxya(Y58 zv9=fS`?T7l2=v^Pk)!C6Ba3U2HkEA1ZgM!?qM7(m>0{=ZNP|ln#67O zydXDc&tL*lo=vtKnBQV)0}+2$&h)_a_8t+}4Mh{=QcNiQMjSjR%dg+Vej$%~;siXCbG1zlN5OkF1Sk%@$Xezm&_Ut(svOY9her99 z63m}y74&XEzcTz1pbZX{GlWFGNi=$Ki5qQq-PbEH|c%`ka z{-}78&VuPpw;uEhmi%T9WAN3J^ahs|+R~d2LHvj^sagjt^2FC0>xhCp0`BakCE3VYlCV&L+m{=P+Rmsc|L z5N=K4sjpBjIBcWA$tN?|^=vRaI*1(<#>*{H5Rx6pqr(L7ii)P6LvP1LtO}5ty))lu z8Z36b8re@GZ4HXve*pja%-h&)1pv`^upe#Hb53*i>60+6qexfm?uAw{$>34UWm|xZ z@A31V!Q9HKKz7L>${UEg^I0E$IpVFGtAfv7=#gtW;C3qB^U>wF_Y*5x*6T2+x<2a* z!M|F-o8lr83XsI4w`Dl;qaD-Cz$bAX-@q#pZUT>fyQDyBdbkKFE+vKQwbeWGGlRZa z;sN{l`K?X|DYDC^HE?4PIxQkkr)&Z8!SqvRThr%MuR@h#z^np#Knl5qg{3a}D-O=_ zG;n3anBLk*3MWPyuyE!O@Hpg{%wfV+LBmQltN*1x`cfqB+|I_kf&)p|%qtXbqO};z z@}5#9%V^}>&}_s-=U)-H!FYfKXH|Dq%oizBfKc`!EErAp4aAKS5}p;Int<*Rhws_g zGtxErzAFM$)yqrWbKH9VE_xG!K$R^s`hZ?70RSY(Z2w6sV2~)c&^0%K!o@X*jiD>1 zIb5BI^rKi(Eml(WrZgC*LQyS{u+H%Q1YH4SQr!wucXea6%Ok5#3a}8B|Q)w?<9Q#ApE)| z^=YS~z$daoOEP^Eq2To93!v^x+){Vf5KKMm>~z-j#3fot0&RAIyFN159&qbIbxm*^ zexO}qCP(~qsW7mH%9UKxODM{H*B>KTccK5x`XHs{llyKacR^|3?=D-rhfad(IkV;- z=ckfB=MrroJwE!&ovu2d4#9KYg1qrI8tHMYYi)Tbvpc2ve8)X^hvNeP8If-%2UeEp zTqT5a-}X!zP;NLgE}&5iQ)B(4)h3zAXH~C13L0Yz>y*(? zTQ2IEmK?A>>HV<+m+KP&HnOllMWx*yU|H=3m@Y>x*{kln4sNhHTrqvf><9~%6-Jh6 zJ3O&R?{uC$&O%)Mb=3z)5}qr)^eL1uj+p_^#)#4QwH_oo4}HS=fM|w334()NwxAEw z*x;O=+-~+H3}(A+;xV{8Cbi<)0P0nRA9KPqJ_H`Fd)vKX!}*e=BK>|KE(XvmL8rSP ziw+T5LM=+Vw<40L}a1f2ra2u5mk}pA<()* zTt8LCEiihoLGF-NyV=)SaJypaCN;>ClRwDLJz{Fe{9*<#9c-yVU^}g+(^AWZpsZib@g6J8FXhEDJ6@j`zIsL1*;$^H;#plO4GyPoWO6!P0eW7Zv?_D%f3?k zVAAg?W>K=3osm}uh}Afo>ISv1y0BpNI+`T(PgTx+1_XU=dH1=GU#~i~BN_>3?ZVmy zgD-C{YtPZ%j$}5p{%?>$1$u-&QU@6JX0B1f)|0V}R`y(eNJ^`*Nf#}ou? z_#g=y22s?3&J*e|olJEzPeM*q-Gg%a`1fPuzueM^{3}t1D0b)ER%>oPJ{#WEM#m~A zyk6+sZ5dW2Xy`Z%BKG&0G2%B8SXWKX`B*^pL7dhIG`L&j>X=?mkP{)6SSK zA)bD95-@F`tIGG%aWjIq0xkuZJ!aoXUPnDB*Nn*(qrEwu$6Y;+7c0CRl>q0k}Ki40xcj zJxYM9<$&eZt0ZTe#dBr`ZO-8Imiz4&$7U<=K7vPpAfy3*Q&c|$c0^Y!fp5c$ZQnZ5 zd__jjSsRq?p>~$(vY+yMuzFw`^5X{04j0XacceyEzq9Cu1%`i(H=0(}swY!qx?m2>pSlk{9z=2n*-;c-c(coIB+qdzQ=DtK-TsjHRSE zM_4>9?l8FTYZ1KS3`ERg1ghu~su?am#y-qDUP~U7EVT?s8UF;C8#T~!J*U=Z`O`(B zWtzk!gqbL$ZcT8-@xy;2@)@bfnj_UItRO|huu`N30l4a#0tc*{TRSw?lVyg8dL}aY zrZHg>ku9MJ0~Mj)0@dX*D;kpoX?IY@dwn|~!=H@sAOh+A_6kD7PQ}8?_X%Oh+1_(9 zOoYmnLJmT{*m@3tO;)QA>zV!xjY^STSmq9;3-m_wG=3wsTHzz7xHo5DZo~eS4_NW` zG;iPHuayr>K(Ne;&JnDWfQ ziR)@ZYDV@M`YSjY^#{*yC396^emLYox3&X!i#}}svb4lY6RF4J66v{X>jiF}56_}% znoxkHgvYtGS^qqlUobg%fT*YVyXoTXS;23+ozH9OwG?v_X&mO9k6xs_fVVfye_k{~ z;(m`0N^6boiS1QpB3>Hwcv!#c-0D&x+wVnW28quLLr% z&fM|xPkdzeyQ$iWbGJjvO`DuZ*w=mM@X6C7SXErO>H2NO|MIv-jN@SnKfbNbm=5+c7r-PzwiW37Bur z!49Ovy!!DZ-H;(+3JPF;z_sK;LewyVF%^?c>{!)fHR*ka&+cmwbcH9cc zq}9Z{qtMFP6kjBpIl3$pjdmn8mSWd?(t)G&R{L5iuhb(Zwx{eq!LeK^SNmkd8UKpH zye{RXQ9D#H9OX%79Q!I@#Q6fpcs`zxV4qxsa`mUiFS>RF%1OVb*s-V`t<6lp@ zO2Ew5nOi_HoM?boI_4TQGX3zotf=T*c-o&V62|fKB5}v(yf9|!Ug~AvmHAg;c@mf5 zlYG^c!reG1IhZCoF4ix({r&~hn=RSj6O@O(Lg=iKg4IUH(xl{Vr_NF|aJe72>iZOu z_r*au?A>eCdY!5-wLf#NULuULIt-Q}{P4s$_YQ5hI~A&DRxDW>jpSkEqjaPY6jOON zxpjs7qxuPeT^|^+6qzho=s`O5@NIusS4mK*-cm|qF;V%K-EC75$xX-ASLwsGkmehw z+%^*e1rZ1MzW#oSj_q%o&ankiW|??d%6Xg998*11C>HIFrgn~d?;A3ZZeGT4ktcEq zh6;5V4$--I2yb@ut8lWo6dfn6Vm#JoGc~Eu6M3*>BVe^)9uzwD{v97Aj(n4V2|pvu ztV1EddxOZKRa{3e;YW?DEVX(XNlaiPU*RiQCawsFuG=)Fm?9Di?a5!B4kXj20 zpvdGFrn*xHRp-LlYnKU-BT@Q3p8yZKL>u+XYF_gNKqS2bzQpd1HtX;^JCXNk5;*eB z^fe30m?zENv?;`SZ#9$%d+C zXzKc!kG|+}Jdb^kP2kTL2ll4z!$?>L0aPGFx?Nzs={5&L3u?L>!Fz(=WPGy8{&0Q& zu=^04)!N0m#859)O>d#<-H_Xbg_Drx)Ah9(W{?4SpdPOR!hpz-6YJ^xG+K(8d{8a@ z=no$ixpBj#`U}~A6)as084y_P1e!__;^Y>QomvcDdqVMh{!>gLR`(CB5#L1Sfa%&x zqE!0_hQ9B#wiF#x!NL&uS@|jNWx>hsouD!9~?C4|1D}HhlBSzax7m*C4B$q7njcLU&B( z3}(ptMR}}KE`WNSskO@Ut6904T=5#obcLhk;D^$e*QUXAZbt&?aHJI}Mg03_B*7q% zQ}iB97K?Bx6@FU6Stg^A*juM`$KbYfyb${#2J?>kM2(1xm>Iv8AQ zR6sjxQ$=jRWGJidT3vw!_PFeNu9Wcgb7=0CXot3JVi=U+^9J3!LpVK zHHqt%gFmq;KzAd59UzsoM8+WvySIiJ4sF4EEKU>2ZhMdg(0^-VC9b{4C^zB*ja^nKD4viuZUn$7>sNx@q7iSMr|` zf$=fRJQVzO%+i!Fb2=5H^y*6zQF8}IzXq)LWe32s9nO3^jPmErpOLw6%EgO6gPKP3 z5n2!@F8Q4%wl-gj9h+d4c3)Lk50B{`j*ErTeo;L=YH?S&!bAczGKWJw@xw4M;c#km zQ4A=i2k=wl5{ePna5e;kt%74{_|h0oGi6Cz=5mnCoPnN(cg}kByYT)y-#7j!e1TC~ zl`I+3mBS5me)-UXEO(RWiBxM!x${J};`lgH^x~=bps{tChD>ov#78p!*b2gz1jzbw z$^1Wwh4rhDlZ@_f7<%R^=mwSTiZ@4cPxiB{W?I?MDdc=eSy$5h*|Sg;iV;}sFIVKs zlYNuh=H65Bvdb}Xb=278s@5uJtU>JI`$8poac6Lc3G3rKf1ECyKe8qA8|m;9H%Dr) zSKbmFGN(E6o*E&@Wcl0$B)8aYAyHbNd)Ci*b2dVDN^sj!!K`0Jr^TEKw?r)lmCD)y zraEa)QTByB`__}wK157p3;{2h&r>Pus*@pOb%R|~$%e*>J91^zO=C+k9N-4AG*<99 zu$Vm$1S@UA-sfsUo*J!;CCRa1walZ>lOE#+JC4T`tL|h$V@4V<373AxkD=`GcVkYu z&53q2>uU*f8CS&Tak!>x`^~JnOCDFS!_t92L*Qp6^d8A_jj-ik$^24)OC_hTH=q^5 zb)`@VML}gfhmI?yS#wOlH#0ORgURkmF)OD@-*v;#g$)w)1b6oBgQKjIg?Nv{O1QT3 zva7!Fodq5@98`P9EU3V8*PJ*~bRZKK()h;fa~i4nAf;cViGph+kUjrK0F1QaJMa_v zZRKZC#PK@fp!ZSdQqwS}Rh($ac>Yq7YIfQ~0IbZn9O&6#QS0k}C%;PyQW6t~oW4Yl zD>e}2`Vn6w`dauZ@xF3LDOwgb17nMJWFN^cg(u_~#;L%xFT?8o2m^~u`;+1nS43P@ zDi2X#N3jW_j*ute4efrn;+&^Q@qaOQmQis&+nNsqr*U@)7Tn!6I0Oyu?(Xi5I|O%^ z1a~LF-QC??r*rPiy=R^LXRY}-U+6EaUj0_RRkdsH-+rFL`-5qQ{b|TfI}ZnU2HDpF z{$})YsaMK(nSJjbDjm2hCu+D>@Ax8wGeLTCi>Vz*9#KYo`O8`+1DJh+r9J+LBP^8O zcuS{zb4>;X(ToxW>64%AwH8)eu`#8R)rxh8zl`A`mE-r4L^N2sJuc&-(dFqU%YJ_p z`(Q0$vb7lZjpyqiiqhe|V5_SUc27f@VXm(;2Mcl(O}2xMeIOg~ZS`Hb}DHIk3^PsC?AHxgnhh|M@D z!Nk_EA!bf)EQw2lIYt+=pxk_Mef37%nq-{boT$L{Rp3Ic2ZE1)2c~?I6n5HjSFVAI zGQBPodBiUi^a~K5y(;#l30xyJS8LSdV9gWe$;m1}%J_r?i)Bm%D@0(fy8nqT^n#Ze z(5+7rrd)<5Z)>t2wABv`zsBjV^+AfnVFO>aXc441O_|3c-xx|3OBHyRSfQH(`y_D} z_*nUA!TvKGQB0=aWjkK)+6rss~K4ch$3~=FD8KoWBnUunkeldtp+<6 zhzjnf153`D_E&o>n}k6%C2rV~#^Od$p2Q%V_x0Q_v1xU9AaE2^V~uZi_%fnH(I}TG z5>;~F39S>JPS$9A{xyw1MSc4{&$;!9O6}<5 zOtl(OeR(4Cyk^Yta_4}nW6DOu^#>W}`>u_>Bt3F0Y3VNvQ=~gjC?gmVpxMsn*-j6F*CnCs1I$a2K%4fKOv1=>j?f03d zGT9TCq1Oj7mD)WYIK0#x!b!2?g@)u&0vf&q zqP=sLw1)Z@w7<$ZAK;CU-6%iSUGRV)rG9zNBa9D@(h{j@ze!Sx7tC^0LrF)<2txpP zF}2#3JyMARk_>_DV4LHF+iRzIq<+nQXr>I?`hLPEmQXB8pU6s3b5zY0o?bP~7IWb> z*v!K$&ea4Jd1z3=$cVBNn`&G?s#d*yTvOy$=Ni(dkjrxLh!X$E*`a z9<;zwe5i26#r!H9=I(-!B~TT#|GSkDy*60KJUspMxyxBgeumcgd--+HI)A*8J0D|G zA=Fm^R$NGQI8!GAX4vh42BDeK>{^Fe+a^paIB84$O!f?UNr%nS%w{e{HJ|7!S7GjDuGGTUUbI~+W zf=PAAeOzo}m<+`Y>_W?GyzD0ndJpX0AE2yO+6eZ&k_-e4Q-?-StJ`mu4;I`|?vO{M z+4^HQMHoxk>pJPV1U2EZuRbyM_I~unO_W0m!`*tVc{{eP)eEL9nN=7`-}M)Y8VbLl zGhgR&Oxg0d6uL;=^w+1Oj(DamMVIF;COIO+^kRVq`Ye77^tN_7ox~VDY9co8@PSFd z*5dI5r<8Zkm4Q~1d9-3)YCBJL%XF3!aHCHJ1`E7G$-es7Wj~Tbv3ny?427!KnvskJ zVGfpDCQNLKq!etZaqAQ66ey7}Z+t#mX$o+}8r$AkEO!HWP}Nt`^}U!)4A5*6_Y7hZ z@Z@|sP{HT%0&oc5GLj8Ei-!x*4AplB{YGP?@67(qdjoVe{%b3A`}GAgQ{XE)$DcOG~#xBw>xO!5P^t1KPiF$?BLNJNst>7#6hp@IU1D7h0u zKcN}h-=nm8=Nn-L{h6D3rU)#xFM3NUVD^f+TQ6~8SQzX@dAOfJ(tdd1?wzP2q)L{a ziFWq>?S;IbdB1qZYQ2i)p;Em(Dm^^#3hOD;%2a4w_{@T&%m%>rC>TEI951A4N`;Wh zp<<|NR08hAg7bj{tcw0Ha1nxSYsyWO_nH~1M?aNeN@p$ft#y^Hjsa-(;6naFVUuBO z*fKkk^Ta@Fx?Kvc>I^jN2sju~Jv(ftqC5gp2(-&IH%rveB+@6Gxnta(lCbUSa z#C)E$l)kK4v2zQ%sMPIcHt*rE{>3)(=z<86#)veiU&@s~&nnTJyDJPnu+;LxH!z&F z^Qxveny(Ixh|U|5fy3)R_j+3jJKv}8S<_U-es~0Uadog9+_})cTtb{2-47ZyMpGHn zWW{8JBrL9RZ7oWMg_2UE>g(r~De&^(X2Q@xc80Iu-$jdUYUOc88VYZm6MYCC`|^+; zagEn2%G#q2Q94!snWQQz9KEEd?dnCQ2tVu}^L2aR=SF_XTw*r-k{7 z-&4Z`?xJ))of!|R66L*k&u96J3nqzs`)!bE>o@hVoS%X^TkeuxE(JF- zZIP%VCk6ZBG8QQhJg8CfFY-^+yT;)Hls0b>zH2?ER~YS^x_@8y(Gfv;CAdN74b>M) zNF6A%`yoJZYYFLdf#SE+PMAWFe4HDUz(`uCO6ypz6a&XdIuj&885UMAZ9P|s4t)Q) zz{ucAzvbmYVD!UOFTpm?Oi*RoA-wEV|JL4ms2ziF2(Ii>UOIN!b6(y^tlu&8TPW9c z@|LKZd7ZTiWXJ|~V(B~v8pX|kCn6?8qqn{2TgX3Ti`M?7_|R@S#`iW40s4`?hj{wG z`btx0s>z3dBwb6zxuKK8M7~v>E}jri8g->nIN^qZrQAiGGFHxE1qdC`X=P z<-XrS6qc^!*@wnIuz*K+mWIba; zwaGgwmot_)z<6`Bd#8{)(TQ30#Se_p7O52!WN<1|Ljz%xk@rJi%AF`i!HT5Ag60+F zD_)F0(F=qRNLYL3nBHfVY#Dqsz7WCZc0=}k!pR3kXm-a-Fk*DZ<{?-K^iZaa41M?T zYMVQT5;tiZ(Kt#_c^a2Dj$OMf2t?8UG!3MncV#UP*e~n7{AU^@>gp$&)}X1(QK7{m zbu^|hhH8ix5-S4sGNE#`flnpM15EZ~wPDYLHO|NEFQ>Nvs832{RQ-7X(tUQiN$hL@ zj^7(Wd51cVs>TN)47BLbM*8hv-Ibh<*G#AziRyesYJ)79IL7Z#1|6)1|8#lxx|gp(Kig{0iR&n6OE~ z50bR`G+B`tuI|l@X@CHonx58Mn+h+LXeWeXDXplITvyG`jt^!4IduTRx8uMPtG>x% zpCq4F*Ha9iky$Yf=uNzvDn&!apjU-OKt!SGr3l-`>w~Mtw4>I6E0k*P8XMvrejNEy z-e*Xp%d3`_tMzzAr4+Q!FYS%-==m4-0fxEpA?h3qlZx@rd(jIze?@3XCyI4ov4Lew zwRSY9YE~H1IeVssDRlvbI@MT%l-PJc4E=sD^KcqRFGosFa;2l7k zZGU86Gy*$K{k>2b%~TYq^X=@Py=XNrX$X@KqF~Vkdi|1BHjPXn(_c+bz5CBOK^6sf8c`#bo-6TDl~-g4wEFg z^RS%%leiyk?E0T6X`~{GfeaEHnbzd=J|jJY>h>{EWLBuZlbD&;vN_7AX zcm4A}%<4t*A38Q!i^^tinVh)N4<{cT2#FobiG&l*^&8+3FtUQGYS8_G3u)79Dm{2~rOLSqv08C!Z}FznbZ9$sy~NOoSz31%im zjk|=fg!Wv}{Jwkr`jqT$=lFNZ3hob<8@x4zof8cdzHsz~2$m4BwFoz$FI4DRgiSUn zA}ty^5t}GQ%TX33p(TS5>h_0;`6^t9hBo_sF-N_0ib}TwkU;?K8d27OWM*Las4NAU zhYr_Q#yVlarqE9*XlM``zr{0(i4gAb+Hihs>0#RaE z!&pCyUzr%iOe}b#`}Uf%##2B}w>b)?J+6e4C4id*5f(RKWd&T88=)@@MdWhQQ;cL> zkqs0~)HXHy!Z%p2$I!e@hGA@`sVcXoC0irSGgUp*d6TOb*&3>bdVCkaI`aq=Qr^G` zFKZ1bZSy?!+@!lD)26L@uSJE&s_OEeSW0EJgjxw@4ai&}19m;ooly{fL}fFaA58T(K?8y+duSeXU&q#%4{6 zt@}fN@6U(JPWlbZq;ck@wuqu9Gflh#{U?y8=*xRK* z+x?OLB@vF?Nt}43y#Rlu&K|)1X2}*YB-?MI1gJIN%7LkKEW`O`L8=>v8MA4uZOSMK z!{b5qdIKDG{3|K`XgVm_OoW(2o02WF%PZ{aRjrSxiBf$7)cwu#dWSLP_8TFnL@}3L zGBKY^G>4$h1${y;mY*-Lt1ayZ6tJp3iRVb1#>ehD4(cB3xSbxWbGl5F2iP0zejU+m zK{OT4%PFSD<`Zd=_sp%Z)(KeanG~>r{CidU%7YMdVLRDRAQ`_l+x@M1?-Z1BS-1(A z(P8~xDL)xce+rctWm5}+bUeP;3L;H(-7i(_`#eDG(1$K4u4A=A&vhOEYv^vzUWE(0i54eVbcV!4EcXaJ)1u zA6R&KCyIp*y^tn!_I2FsmDn8nl@aq`kH;b-U` zZIoF$*is_#{s!=4sX)t0zX|0_D2O5jb$LUbK?e!aG>Y|G)GL;I7^BxqYR2UwT=v|r zk9Eg8hawkGf?>26?46{g^4HK?83ir;>gscEAvHuWAY?O5q*g(_n=_AzTc)Rk&>DEL z?bou#i^-W8%xAI;Nqw)d`R%Ir#!_ow^gu_eeL;}?dXB`&h8JAjw_*#4lC#Nn7QCV} z$Wc#pxMg=dj-h_FEws@tZWn#gz4;nyuUjsnspg83)0{8)jVE=7@7P;Od*}M^O3;hM zpB;}DOSR&nM71=Qc+H3`Ft`wMWCw>*c|Js||HM`__U9Yp%vV3v4jL1$O!S<^p?K=q zFZjSdDF|`#fvrqk0x}5gC4bwF?=c7x-V;nr7jB+=0-s=a`iBqLwEFT8U{RV}9wmGHgE7s^D0R-ReUKZJPGqM@29wtdG4}gUgyEFR!6;6I zvitO)YLljXO}c%r>LX7Yx2Cwi5944B&=TVCatCm;p>Cn&OdW~Y82IV5AsA#)WrpKe zRe+?;N%rVjP)VYx!7gE`OTgP|^d?Cw)_hQL6`KR$GB5gPKBvxHi>Ut=J&ca~JVHmO zZ=N-tPz!tqi?7$rMdIy=sThrmnaqJB8@XLFazT8q~f;FgsP;1x4tk~^JkOUnyuoFJI z`Th((lG%w$No1~2n~yHll5Yz(LX(nkcggX`lnJXxi@cIoi&nh z5_u~xUW09Lls$!y?azSTVp*ErA!VdaY08WFQt2koUGA|go1 zT<;w~b1k8VJ7k^NGG--iQm7bzbntNJ;Hmm1WVU1z4V>u@urW!OgFXF1w0wdkSe6gi?lb5mcF zp!uy8VI7nRd(+TV*Z#g=Zmgq$0>Sm4V>dFnI;|I=3$x7-+x|rs?#0=dB-T z8dES4-H0&})k9|Y^C#_t@F?!gXfvYh-Z!X69^ar(+HoPahgKOyG!pY-nryEX`tOH| zE*SofM%9V`@I1Sy^qnq1gtnM+9X0#MDaaE6HwY|SK9pWgF;-PMT14VeYxXZ1_#a?y zJA!#!y^)cxU|R1_x;K?La?q0G{qUVzZ^Xx|2o%ROl+UoS#Ab1c@-#4*Qj}Zt!<%g# zea3u?Bqmmmn_O1mIc&AMx_bxh`gy2o$1(8gMXv8pkarzF){Z$(R!K9kR;u8LMH<-o zoK`W~Ja@uD^>ExIPZLdotJt~5I%#eD3w zvwycaS@s3UqxC6#^{1j)MsI(<>l!4bWx)^tz5p7G*9Jpp%kv`J#SA{A;2meI!h<&d z*pOy(_qpd-enJkU>b|Mn*4%0?Y%~f4vGysK*oYDE`2Cq>ryKe@W9}{y4S=Db$U+Bv zd5ekcz(3S|s3O4)9GrdHg}~ZXW$lN|;5y#1WC@r2cbL&iYa&Jc#927B3b3Q-Yn{2H z(zgt^sfNz+Wt~r@9GcCiT-;(pWHlvR1M;cFOUVis#A)dT4aRC-JaM)Tul5#a3M1&! z(r0sO*H7{&nQ*7}uK>995s8zE=uXFsvbZx(V%c<7Cnc#rS-g3>l3X&Hq`D*2ry^M6 z0JvAL*S@R;i=JdGD8?>5gLxYA=|8uJ;E75HL}%@Lx0-Lh{iTn%Je^dx-R28_T>@y3Yi{dRu02QEhHoM8i^8wz~4`dduL|hFj4SkAjvh?sXMW%G9NcrU9?Bc-Qeu?j{F%3LPnhSjR#4j>I}Vr8)4;>aH8bgloxczE$3G|;ztG+-BThgpt#ATh83P1p=X3$H zSX&$s;?vTV=Iz%f`c0V~>Ri^d_m9;C8EZfKqS<#NvO0g)AGTZIMkFVe~$G} z2}M~-Jd4GABD!y_X$>}uQksnv?RR*i1XWd{t|UxgQwy7K`y36rGy&7P54>D#a0F zb7UM+>GL4^kU4Mk=8J>=-KIRalq!SN)aqb*3 zTQJiF!c%d+&|r#%MTJusYYCU-W*^EYUuUy48QC*@kr=75)&V%K^{H8X2A!c^{vD3T zVstHtMZtJ)Ilg0A`HuTjo#WHN)6iyX7iY@9dsu71YxLiLJ6)LAbzpbo`9r?Yd<P$)*_55JBnz-5T`l_<8^HLi<7kI$B?8Mi#TlM3Cdf%;NQ1K{3T*Hrq4Z)QIn# z$cV4YBUgWaf4p!mv2k0fD;-krv`#U@B9vJ=@zNnvh(!_N1T!sbY*lCahbwdu&T=5v z#McnJbap~fN5u6)PIL0HJ~a>ynR{+2Lm5Q&idj3$lVKat4KHBRYlI`bD4=!kpv^!SsQ}c)sgMJ0;8GY-o7ksFcC=-KNJLe5)2)B+`-JF?-%7iyFn}%bZG33|2 z8-K>v!O{BBghh4_Snk;J`8CJbh{bFyP-`dHRUgP&ugi19u18JKautcH!}U!ETzi#0 zs)ce{)rupYJ?o167I?vY*aelDPTDJS5S~rM=W!tYCZvy^5-Z5GDRi^&%)i?19eTEU zP0?v8R1l_OOsIZXpO_O_@IgSg#e17uyWJzIYQ0Hz&F9UaU#-+Z0YskXHM91WY)#$-VzIekxLIf!^VHcHtB@bhVn%K?9GrOWh_|@OR zd`oowF|*Aln=stv`4L*FfHQ`k^0RIPrSIRhRVvnh&4Y9tXdVn6^g~W{P*QiAEkSco z{@qlmk-EXi;*BKg*#wIZ2v;P4)vQ!JmT!LP|GkvDUgzDadO8KLDGxVNihbr5U)!h!bqe9#-dgnlM#BGnL{e=Z7C8W(|tSF1E zQm5kVThXq9#&e3R!$dx+_;l$qw8PQfp3r_)1XtUY`b=UsxrnZ=hUvl`#=Xd02t&;C z)B2$##@~!&zd;(I!ap#QvoDU4Jm#eB=DiW$W;#?HEfk9B5nf*KmfR1JE1$;#L!3_K zR$HA5qpdX*V!#Z|`?DN(u+r{4@ain!JY7-{Z89Vb1x|$sc$|yqwOhaLN1v9szfZC{ zzA||rV~gO*a%Qg91(-w<3ZWIt<%TCvOvaMrA1r6POq_lwYznQrIXKbL;PvE_;lK7K2DcCDU%Pxg_`)Z{4mF(&q_Zy`V`xmtX5*(E22$6JOdCPUIPdW9@l z>U$}K@2|h}u}%NV$IeUOsusbuiJlp@gfUV!<4OSpGHQ3*9s)H#z1hi288NTQK~NZ) z{J|ef+E7%Krr00vrh+EcY*$L6)fcDQ#2KDU_SqsX?(@!9J(Pxh63pk5_Ww-pe#Mj> z&*Z)zFmF6l_l~QXA*Qh7pr3s!cFJYyqSFsPi?v9}IsQ9EsNtbkfi+tklKHTVTG^(a z$bi9@oVp)|+Ds=mjn`}FGy%XAW{;`9(5ccW!kOBTK$w97$&+29Yel3dm-%lIVe;*j z{x5H9g1t-16yF>eP49Fl)7v-kKcm(aOp8uT?xK~7eYQN}+O%xB;pLVG(vy)cfXX~ZbHx0t}|*htBef0Y~m9v{D_2dvKGfE_mUx$Mw;5b zU~F-{gs|4S!SG%xr&FQQjJE;>*1%&K?rTP^l?3i9uI6ZQ*VovgWCn@bWvAjvUD{l4 zN}KrgZRneM|8=w7p0t_LT?r%l#sUpG?u6%ipxNUU(cvIl>%ok3XP*j}l5%##pb2o`qAMV`ebG-Af| zhLqdokbAY`J}lsq0ZVDakTGR_Zk=lb-fVrM(&JrK-CZJ*d-lqgUherqu<5-7HHEXW z!tj_pzv7Oa+j$Dy$)ElQjt*4GD=GgX3Ym83Q~~YEA&_y|;{@Ct`f#@uG}N1sg?ch= z3EuQtonkSsfqf06yffvWFXnVvt&Lo%b^1bN`j4WM@A^y6IbFHE-Z1$PU{FQQg@3&_ zGh{w`V?-Tj0r6?0Buz{t5&P5Qk1I}{l$1#etBHXgb^$A)b)=fW9ZN&i8%39Si9+7#@gVl<9%J`*h7I5v}u@pD9;ttC#JM6X$d@-`z zjr3*y;#>-KF!}_$7fCb;L zKRAzapYS-Nf%%ib#3+O3VTT&AQ|)O1q8l(KEt_aO*y?-yt-oJ9!!kfX6k1WTE#XIx z^((fevPBZ$>K~LSV$W4VRTvv+ytH*tfvGjn_Pjg_Y}wQzVsrf<8nGqlj+Rz@a65sB zJYU7W+bd%9sJzDBaCL#6t5}Xm;K&Z|fFPlzCKTTy7B#V>(!rODW;a`MX%ClHlLOwSR6S=gTiJ|l#N6pozE&xcLZ^=ROx0Imtr)WL9O=%@ zPD4c?Ii-78lW7~bb!zW7Yw_Si+)4dRmrIP3hS6Hwk$o#z% zI(M`rJoRdkvP5Hs`Y;RQRhgeR91KUFP0_xhzXKsR3o^vYO;WEt;?RO;4=$|#=8!E06Te^@b?_71xfQYd z!M>{Il0PV0^)=wy-vYhX!B0zSY3Hz!fVP1Em`-km8VL7q?Bd3Mvx`j;j%m0gLP;O$ zr2?nU=Y@w6wI|9XMU*`|!6TNwg})iif$OhymL0E>5r_gETFw2yVpF;bhuIE1Nd)r3|?6ydTnL0X{N+T5dl5PFX6dEgYg zxSO3G$(~myjAzxMV!1Vk(`yrs7OiX_PTz1J#ARdkW?wj3Q(g&`aP#L>-Xe1RQ5n?g zTy}bJ9f?N$MUX+jE2C81BzDqqV}EhJfJm|U>}D)IZvtW|Ygik%y*N)zZkiArT5 z4TFFv?38}`6_>%90i45-2Axh7oc>PLyv*Qh!_cVOR5|k7%V#eyLJ~%5ObS(cw@#3C z2G#b5>z$#Tc^-NadeRU6LMUPL-y`^?Uz665y(fVD2?swh=kCNdNvTIRzK_w}Kitn<1CS6h1j*O;j%=kBSAub@xSkwKM8VnMP zE$N`zmQ?k={(fO&4M06EtKjOAF$rewm#6jz4w5SrA59ACWuL|9WfTq8V;)RhIai6{ zqe6Xh@cNtTk&mqK2iu0ZlOLI-X~NuJbk>Rwk-7x_HVH?U`opp98h;{6at^`}y&y}-{iaGDxHXB(b5j2SLr2$+(5PNEw;9Gh zX&~mMfqGSQJsm!N1wZ45&$_7FP*Ed&J_pSE%tZq2WW_4oSuwbI|Gw0em06nPoJ;_%qY=22(#meV@X5 zA}MQ_N8f^%k0+`%dt?^+J#~f6!eeUQWe00o<2LsPQpRgR=m!g{wN|j?_EyqPt3X9(X1a z!owW6wutk+vB9e-p9iO*lm`b3+r*zg{o;m*LX+tS;xRNgpLJ*=AFSZCcxJ>UHQ{R? z{0NVl^t9HRaV?QT@?8Yf0OyJyO$-(s4idgEG}#k0tj*=UQhvH{zkjk*crX}`0oHmf z>%!2iLIhGqc3;pVC=7H%?KzZED*<4mT7cvRDE?vsy5h$qHmU61RAuVC{X;}PeU9IZ zo8;_7ocBq;jnt>8QT@|n=70ia8me#n^q|M=fWcN)baOjrkA7>wZ1oWc!Yf0Xs8^E* zoJ>Lysn9{gc}6VnzxT9cdfMG&ARX18N9Y6!h%I}tDV7${C3ei$SU*Zx@ty@AOx;f8 zo4VduUGZ0ZhC-?)?Ai>J(7Vw=0^qR$=%0{lnGe=EU5;!sI2{pwlxZ`qt`Y=(Z--{t z=0s^29SrCf012jkj!?Gp8_VR*F&w5S(XK?5;t5Z(b0*=eaez#wT@46>lV!A<5u8cX z?Oj{9=`P$PuB{O^O+@e38CaaY3XVYfTNzzJ@XrMwnU)AdSrPj}+%Uz|8V_Sy@q0l* zF$Z9TV+bI4FlDQA0Nx5%4sLwhJP7-(YXyU9zkxd$H29QFp!S9WMBhU&^yF}Z_&mdg zi{E-w?IR^}Ywb`;l1objfeDLw8n1f?^UZt~Dm6XL1X&AS2V}y9V!T@-C~5_WP{1VX zQ0Ec~)drQ#vA9{#DrKk&))1t}d}c(sKEimdVS)0wg5~y6hr=xL2y*w-Umx!@xKHEx zQu8ygIrVF$*C_kJ`=KCfnIc@WK^WCPFm!A5f5^wG8zV5Hc6%fEh6U1q)_wu#Mw_e>Gf7QRPD{aB0i?FEMOBNFA@1%v3)KCl!F2S)|rL z4#XmdQ$^|0UYx^x8UIPjjIMv_SJ{phv6$X13bFk%6zE4!*iEvPHaEO^umeK_&&7#1 z9!Poq?{Lh;*q_Q)r+y7M(?>!VI2XiJucp}q)#AziRQxhTDLyoEML z2D$Rg$xeBqn{q|eWae!ErIbezLil*SGSlyIC#XCuB5VV%+G$5TIlhV9$3eb{27MLu zdNwSO4^sj|>;vAv4GtKl8^RyF;Ukd4`x?App7^5M{iVeO&BgP0axX!iDOXcTncCno zuSK{zTtZoy44uh-0@Qm#-r=7|E}iRSZkk#QyR_z&4JQU)C$+-u_@UEkAsqVY3Y*gi z+oQ20gcMUNoI;!EGssx%hVekm?UN5Xs(wW{j3HO#uVs)|16=@-PR|p9X3en;IIiAT7~kN@N)>Zjh(OHg?%j6`W8L1UxU-4 z%%)5Q@OFNjNd8LxpoDt#yu$T|)u*)Veb-|TN8A5dX2hL;08$1?{;^JCJwaeLL)3q3 z0sPc_aG!Ok;K7Mj&5rB}BC%Fj{EhiT*%w*-F{^v8a@p=+#G6ViEIDVkNADz3r^;}H zyN*Um-GM4`v45)Xv)%QMT!qGQIODSiHlGV%$y*sE`6@C=Fzxg``z7*5fwGT;nG|1N zA0;eY)=!7b%p|$NKwV3blWVbeX$qeirReh584?h7-5GfM{)}T84V~lD=x>KNML4T< zqMuB^smR;mcgn+Se{rKND*ibxXHL*o3$Pjz4YILYRU8-tr>ORPa@F&+_F}bp{|Vh$ zwI2Q9`UsobfsC8gjfnmH2>G;c|J##*Iw>+PZZx(rufX_umt&W<(SP^d_`iTIXLOxp zkDQS7Inppa>jKqXNae0QEvI@iY0M}HYmNcDt3=C_@>N?0;oF>^dvIe}?LnG8LX6b2 za7w1*Ju~v(dxgekslRE{%APplYqr7&SU;ilYqT2VEmO@<+h*IiS*d!y5AtKpDo&Ef z+ue>Zop3o|uDvOS3#Em_Sl${eiq{s2J%0|Ix5N7xem_cR&J7p;F+!* z*QZ}+ud1VA`E_!DsJrioJq_K0kp%k@9!bDnnU=Wq!W6eGL*eYrr$@8}&l|v*0D^o4 z@$UYkb_;A%_5V~1V>v)e!mJ*o>pjEy&q`iT zkF1e+vw$&GgSe2M^#?Dj+>&UxrqImJHzd3_PFUmGGPBiOw55m$Ue9Js|9kq)jX1QX zJ1T(l@@;=uT(4qdJp8&QS3CHYyN}Z?QiWC%pjcR&(QV5Vd#;opzp!7BnJYwx0Li$* ztteY-UhIW^vKiX{ z9Yv*qhe9?pYz$;pEu}2m0ngA*0ob2^jWCv;+sZIDz9Bdbxol6*7p^6J;q8Ry_;CMk zS}Rx5f-*(J4470a;7<@O&-+L-o#yt`(>V-u>#mu3DjujbmK|G#d3c^3g*whBzm#>7 zL)V)V@+-k*d76aA~!-Zv^)B=l7UY>OO9U>OBM&<=f zvhF67Hf~>8>rFrX;GmJz9-u37^ieF3g$5ZROAE;3$s|Q^D(udEI!wuLq~aF(Ud+;o z#&fK4v+>}7yVL+sF`G!hwCNPouT95a0k>#r_a#BF(2As`m71o^t}ejyXDXm8dA<^G zW&)t^dIp%pSjiVkv*+2bpAerbeFDHsmeCD1c_o&<$PC>F!jX|>Dwq0?--skaIqRG zfKzmbKa(VW>9&}zS-GyI|HKPg&lZp=PVSdd*v?8YzZVmTm`C@^5|S7SQ}B3W4I@Q| z-E#lcT~8+{7DyiuUHuTTlt?d?JSJ;CmG|*RrjABr{L!7htGp}pe;f||-$Ov9@b{?W z2colJe_@-m{NL@z@!E%b@22OpUHL5yRBf?Tu-$x3tx9cQ;+mc)WJf-9>mY2R$+!O8r|$dC)XO*^U?4DZ(*-yHAS>)TzHh4$+@(X&d$GY}My6s_Yx@Q9iob+8-(Do`skH;tJ7+FV%iy-oZzG zIm*r6@kG&K*J7=ras1Wsn;d6k7->a{SV_76n)2HR=dWUdC<+khVE_7f6M;zdyCc#V ziOG(LPQO5gdU&21oR9JE&Yfxd>^RBv6>V%I=fl&})6_hJUw7_i@|?1BT_Zv zb#z`w2~)-y+)o1Up$Q)BdWYvvcCV6}A>Q_2vJ%zLpL<@&D=vq@6{aun zdp^(d(Qmo9qcQ;XIz?NXhkd5No=@AkX`hIFF!!um>dl_Q^6sK{vj zy|ce=OYXTl<+lCwCFD^Z9f)6dPWhpR2Nl8i{1zHbI2X!UvW4 zQORG#mX7r1JvHd0{ssB<^@izmxQjU4eDoMFcfbZS^DZae??f!}PRDfW^4(FwMQ=~0 zaKZUSW6c#o<}y<99c!P`trbctsFm>Ewl!E7ZZn zu8g31UN-dZv5^P`Ld#8wNtMz_CEkRoG>agv_D8_c0_6?GN9{~cvN+JLldUDHqVt)| zr_^UgQc%plQbO_K&w-l8=2i+h&Ax+a56JcJ*sm&*C7^5(7KxO^K;si+ZW4fGq2i$3 zjp&P}CKQ>Fx%s@~R%tHx&{1Lh*rD~i!WhaAGgd7K zhw+;prgVGQ;nG_9nB&j%V~@^{xllS!@T`K7E!I1;F6ywtuOv^KIe zW~1@o6|SE>Bfp4$JI=da-rD*2L!(Z+j1j`jf57}kr`RU*4V@+ye&8lFbCgbf-50Ui z?+bRivCtZ+u|kWo{?4!TGrlNfgP7ZE0n;G(I|kG@>4r}t*FHaV2e+2<77JiS@WY7s z8MQgBqAPn|AZa$WYxu;YaiToP9euhfkTiG&S``hPP+T%RqgR|gzHGzNIF+fu6kW0^ zJgi*4Z05|y{l_a8;Dkv=1~u{iq?RH5QI5s^W`L=VGqiKcqhhIX>i#zJwG z-#jQSzi6QH>v3o49X=yDbv<7yxd**8}y~=)tTMg>pLOXMTzxK%>UwyKgG^xX& zkUc?-LB>4(;4T#fjX3g*A|N?f|BQ2NFfvn$J>QPWj8ho?DUxXl-gLeKq_qou!N`jW zdF0yH1+om!5p5t_fdIN;Vt!b*l&Ez)C(&|c63h*7*E@OzzdsicT8ej`EHz{?vTqIg zBq2vas%dX@bu-Z3dcR@x6-7G~AiS@FfznauU$)aVLEHHCZuFXh_0* zYM!L$l|ES#Yn1%Baa5oR%`Z0Fee1=@w4K2?-Y=IISqT4&tG57(qwDgAL-1h19fAjU*93xV zaCdii3-0bRxCD21*TLOwaCe)JXaBq3?(@Fqnm7z1$73UAWCU#i&9V^kkd62>`Ri5P;sW??v6!q2Aqd;#o0A%NtOdWWUAMP18rUwrbA_7 z3w4(HU&gaLpkOE0MDUxo&jF5-9b>X7Sd!CxCAzJK~-!+|y2j^ej zw*e=nb;|5gCL`Gizs8h|1{~;@e20I(J0D;X3;Ir;_}Ur(ur`@oE74B*-PvuqA0@a= zYyHVR{A3Y#8Z;^rB_&nY9?P_L2`-$$I77~*Qmi+41Wd-nXI?wZjxmlRw-HP+FIr5` zZ||mf#P|$^n@QXQ7m{pr=}KqaM=!RsRPe@uJTgqyjAsXXKj@c@yT4X0yM`|`0-yF4 z>?e=b!v|_XCOvr{8c8MRdmOfikazBZyZo1$ziHG+dV>dVrkA~rjKNg$=lH<-&Th>( zFCP=g7j~M#NaKE3>s@ybnBqv3^w)sT7ew0R>&ggqYfb!5V?AvoVRv!lkwL6s?uNcIbiVqqq80u%BFMJaGuQ z328LMLOq%JQpbRPFwMg}tx#+%;+FbY_{*z0MiDUq#rMF$nh}ytV}+YrI<+QOCDy=#rzi@hKs8&jz{lzJCklX|cW(%a zmD$Z&_W8wG9u4K0s*AmNnS-?Iqi6K2hYFM`XFz^=aWVBKC>p&wM?evZgwEPz{H9E zF^c;G5%xDDu(wXFW5&C!%uS)r+o20T@(FXBxruBJJ@Vzlp0`uh4c?Q=Wejbfk0 zvjqUb@;iRIamfe?>#YnyJf#682ZM~-H)7q_fZbM~vEC;OyRE&mdi#6um)8eUH#w;K z$ttM&^sL~UDQyr``X8@JH`&OdXX*b<%|8Nf%aS4os=mtESOC=Q^v>5}_L-NRcz82f zz(~BA7+96oEeb82wlJJ1^DMW$JVb;TappyQ|0ja--T;dX67Rr#oZ6CcO02&k23>U{ z+X?F8JY%2{b9S~_L`rtkhn@!V_{?^|y#ir(U7K}TB^|kdeRzxreQ|n$m+qaxH||Hf zqYrUK@PkPNGC#NeJaJX6(*`Soxs{#wkd=$sOgg#`w`8$euRp_h*ed0{sf2e?n!fsd z)`zuf5|ijN|86pI$W2CXkXomY7{jKkzs^;d0%_(^2J$11AgIAt_PW;@zZF(;aryy~ z8acpC$BZ2n5eZ(Rak-_b0q3~sMHKOF%wcP8-K&&XiVq|=quDAR4AXjZK0;6MF6@mp ztQmd=%W&`nTieu`UY_;g_mu#V;IM6`L+k=&K zi+fSy&Zx%;hK*)AHlZmi_hHpQu9=Ke-m$O zd5OcB5(L-n&NN7M>JAbl$GPHp#^R$p8Zywj)FBShshBA&?~3G8VO@$Y8jshgI@|eL zgBu?#V#Z)!{KVHbBii2ebk>l1Lyr+QR+?4+y8AW#oJ4*WmdNaTIro=Q%#CUvX;tDD z^C)Lm>JNFJgX?yLX^h*=I3SZMPbaIku<+IfwTc(2|LMH##tq2JgT$Y`wtofr;(l-- ztS*JhW2ntrlP^rj=xESI$;S45!e7zFE7gxOPfyM6leX^4NdFM)u3XS^xe_F7sALnm&f7C?}EDH(WjS z3A6YP4l#OuG$Quow%GZcHGjI%GZ%6_n# zgH28~xMR>Jr^0k?Gp|3|`zEd5$xZyz`YG}CV>0gPjW_i{eC-{c%!}i0?wHrX;WNA; zk6*3?14RsNj`CLsj|b&8s0VWbh%?2(8~oVguzv?_SD2p#>17%}sB?789MT?ip#8|g zFPTCQt@uEj%4^A?Vg-hM%0TX(8%da|bRc4miLoDCfZ^*&$E~a0TI9Ez!|cnUo@F-I zvfN`%uyLj47=c?`=7y{*%s5DRHF&3^ychIJ;M}kYtzyJIx5q~)IO?9c6N}9?QI$?Y z4qxx2k0*z`S_HXW=x!UM4~!8Ej3)lXQVifM+U`V&*&R}MtoGQ%OtfH!nx)jtb#KY6 z8k81)#=1TGq}VZ!JpC|2ak|t1OJ%!ELdy0vS}wbHvIo-{RzpAB$>o)@Y5*4Bo^>4B zw?ZRKmy_tnhRYk1k-To=SQ>)_XtsH?|2D1n#+B+ejY&P7tNxF%!y?x>J|~c9Ah^QQ z{`{DotHpzqiH)9S{%_b{Wtwg-=VOdDR_3zeX|vc4BYLmR0@P8pqLj%BiRCnA&-1B# zI1F>Vb&(wH`NuXpQMa`oFLTmrqjeIfz!X%+7l~@TMGSY=&^WJgd4cz|)0P+4^FG=c zi~*@6zcipVUDC;W`yEv6{5g%utIA`p2T1{IWXoTMOI(gneQ>WbuReA<3jfgP(qNPD z5ATP?7y<<&5uBfEb36^%?821?oM`o+q#=c?u z8KfE)eO$+#{IJXV!E5)K68*CuI7zlqS1gO!NK(CPT-HCaCEs6qAL1_WqS=1r##6*j ztUbzjJ@N}gx%XZz@`Dtr^{Yx4qASYw5+?he!zjPbYUpaMwqbid0J>St@3$^a!cBo^ zTNYZ7`INGO%D@;)w%iL752rJ{@(nvUb$sWa;2!TF??G}lK?t^5Q_!z7b>4b>wl6B^ ztGV(f37j`FXzfhIYaMO`8I53n)|9G`)i{G7k_E{@%jBS zrzVIAG4k~blD=I@BA%p4PefhDQkg#Ul_0B0M33OL1s|?_&pkF*sT^54UIB@N@6c5l za9We4W;! z;`!_BTS8auOVd-{QUkHT$D2PPnIG%$HMJP!{NQ<_tr-j?%v>piM!+pFuKSVTHlia5 zx0&%X;nM0SL6x0GSCO=>R_uCV!Cmg+>aOhLl3f#iJ>J@%;9zAgqZNn|mJhdz^o0xV zMf&XSYYGHrVIK5wWLg>n4HIBeiZQK{9`sX{*V8riyKala{*uL`7Ev1ricD;&WSTkSf|bf{?IC5=#)DO=f11GJ|G}HA|->NwvzUmGQiJn2kYabBsyrcmzn0#Z41hPK__6I`hQrC|H7wOfUyC>9(f>r&Uf$U1#7iC(AcQg zA`N|crGVt_T@-vDh{8!GZ##Wm5#zY*%-Vi*oxltI_QlXUIpx@2nwRL+kz$<@VCtMC z%wBSmqI!jwy$MUoZ?IBno&k?#G#{O|ticodK>fSxPcT@*cf&50uD(E;i|6F30aha} zIP9Vghz+b!72#L?pAzzaZIB+DzRKg8fhikq93zwbxw?Fa{;8d?GC)uH&akOPnIlO# zj?{WMdK2nouZO2O9vHqXQwd#hq_TK_8EmAcO@lSRHSVeEECtqkIL4A&esx%-wiPke z8+f8ySTx8MHHDmrN30in@(?x7ajwjkW7#$kMZ3{>^1fAKT-Y=4(eoz>-NyHp#IC-DGswc%ivvU#Q30jeBvGEwhI{zr1kOz z@?jxRsLmq7Yh+x=(CE8XCgJ7q^Oxw0jgUSnhONRX)W7U!q37#eVsV)!3bVq z18hZq~LRQNdYnN-6Niz&)F4 z^?D==n`3`|j9peaQ6;v}@@^xw*6SB8a$p!~16XT<|veOFX$!Am8Tb zx!8==F0tPU79+aObt6WBc($@Ky1K)akP5sMLpB85Z861DGF!sZZetg|;=}q1@tnXX z%?k1}CXzCWqw9H~N*c|6ag3|hh!nhdB$d-15y~J>i)r{Wkl}y-Yv(CJ{x_BKzr&dv z>YZK^fCmDRSe%<$7NEbYB{{e2wfHd=VH|T*{n6RRBh=GXk7K~hFSQW!{ z784Kog?)wLY4wuINT79ujX{G;h!+gyHog3gYfUV$*C!TZ4%+&u`(_$O=^%2sP-$+Z zy^wf-$!j;+*@h@dej-F=viV}te&$;gc76Q8^~zZr_xT;V(p}tJ$%~MRH&tNeIl18} zHi&GW&go}8Vq=LJnHf=RFHEOO_b+qg3sUXg^PAi!z+#7^T}3@ zr_*aBg)#f`a&+2CX*{!qreXUOiOT%tlzV;uMdBzO?{C~u6w=zyEoT`zRR0N9 zA@aV+!NV>Izxd90os>Iuv6h*Nj;psg+k+@B@)Zh9F{H)sp~2J1*A(4>j}?9_^wNpQ z7Sk5OMfsPv0HRLiq&o=KW||ScQ-?V4a{fo_R>~UryD3H9D=>Z9q7VA2+hfdE{$z+% zP|kFBEzLZ2)bAJG9q8FNn@QY2T{7^f=mDWw3Lw;aQECyhG-z}gXes4Kdq9FT#S0+kVD`$B3 zD(t(UMX?#)O>YdJ0QA!@fFRMc7oo7D0#E%8=NNwYW;vG%aoJC$u2tPtUx>_o!2c@QY!N8RbE1e zw~{omru6`pF)OQFkV)qAs{TfkwX&bF27)xbN|^IPVy?*VE2Jk_YcDXeEYgp=xWzS-LmT&cMZ0=6 zVJxNkkNP}6gm+`C$M?^+aEH+7w5Zazg=rL;;Uh1~L#~p2y=%6PlUp3)W-SXx4yigGshr1whg2OXiWc%zPx=wHXg~kqz|A@>P&5(Y>g(;z3j`cym=L}JvbP>>oCC8+Xq|8- z-dy{=1d<E81Vv^|torP8CvQuy)XIX@!|CB4$31qNYkK)qiFVgiex8udFPIUZ6)ASD> zOyY7I^x=V`>Rj&t^CpA<7;m0dlbLYvdup`kMK<}{8Jv| z1l>XbBWI>JO|P1kI_vmsdc=^Ei7ay`CmTaAgW`6k_ArF?Qjv@lM}qfkyQ1#orhU8|U%%0NMHJfqAjjNY?xmyUo3-d| z4Q3N66Y15;O+O40!JNcAPeaS#yw6uhN1MwJZFy|;%ZY{yS?;#jlXfv}T1>5Eby7#R z8^>8K*Nm1hfl$-;uOFGHC6(=pdq1tHueh|Bh0Z`doM2T@wI2v<=K&TSH?!!it$=YH zN@~s$Z6IObYw|{LwGkLKJ+I+$?Q95^!2EbT*Z3YTjHXi9)7$7NmxqO>3I;J8OJPGC z42GL7)_Df44kNGQMi#?}%QtmD?`)TwB@|`VTca0beJ<$tZ<}9I68n9GS?j`2S?yr~ zUPADunMV@RFKsK=v2b_vqgY0{c-mZ^K#JBvwHCX~GK!a9v2VX7apvegj4&ho8f7Iz z?tVIW#T4{AdJUC6{C5%ezYp(%aNi-$z=l}~TvFC_VAc^IJiY+c)6MJ4_(?|;xk~+F zkDxRaz8T56lc9+jLRPgv`psffF3UNTQ&tOdklfjjXc7X8?sRMUiwpvX)oj*>G!T@V zFw|n)1LvdK0(a9$FrYgrAjO70diN}gY^4w!+=6d+T2*}X;#W?EaGvKp>&0`O~YgFN5i<=7E#-G=$mUur$h z+pSi7DS>=;zEV~az2*Ybs`dWB7N6fJSRAZ+l@`{`r&aifp|%^(?1HW7>&F;HALgM( z2(sG)pjyyIsLW0o=H9Om5Q;lE8%dX|B?9LKi_o)sW+yHO_S7UN8Ztfxj)T zBRv^ylR*)yVm-j}!yEu$GeJ3_010=ejVrLt1gS>Nc)Laf(d1jbjm!-91 zUN-%!oii@$kxi#dq$o0sw#}6x1;W2f+8Z7OH0x}Ho96_0j;QWftKjj#56_wdg(DCA z9b2IF&5%R?qdMQ)lrj&fGr8>+c8IBqiurA3gbZF?9c@;C!OQ85PflEV)5A#2*PElb zyy|RIYUyS*lZ|OKFMF{VvTI_qCA=7eJw^IPbknpPphhmo zVKV;CPsPYbNG}rPG4s5JYNPc=i!F;k5v(jVGt7lu#(p|jdXsFI)_;561-uDzJ>AS) z+>Lxw6$8=oa+NEq7ctJI=BsAsS#F8BKt=I*lLzowyX!s6FVM<|MtRB{Y&H?*^W+S6 zKBu@yN)(dJQfsV$;Z(W_p6Vzj*XO*k@Lz~#nO&%a4kYcY$hx(5B0jH7Lt z;Sv#J;rWlJ`|B}YfKw@7DaoJB9=E#XPSv>4k$#UY4ZRJ*#;b1|dj8q9|Grphl|DflfK^10&Sx;brJS#Ll$= zMGDXXdSXlCV!SEeCX3&h4d(-!ub0|utX6V(LFcWaU5`*{i;i-6enkoRea>~F)mokH zk13#N~vx7ZQ#-MPQV{v7(ut)-sKC?rZgdn5Mf|7Z^#j&?IXlUZHNe8 zy*pVNpuWt|F7Ov=Wyn&S`M01rA4%qU?h?w@z0~gZ1#pU_yY3!%!U4D;&4cn30ixdI0 z?+$qUuoYeOs?le?e3S=P{8m_1+!CT|_`euAO2t=*-qfM|*LJjx2Z_+JMm3aE=-&~C^ zmAql4<8>^t8sW*+F06eZZYn9PR=XHD?{!MH@q0yl7Oomue*V zR{>|t1kq&rk&If$EDlkvi_D)xEO6M9Mjr+zd zbNBWaQ^XXO{o5islFuW9vnV(vjI7@50@g-c+T{6mULcS0WM(pa7viz}rn6nRNIU?n z7DpkzqcKZvYSt{DBB897U83y!@6H#0^l6wA{uUS!cZb5bH`SIWp>wW>(030;=8naj z7{VBaw0=?O!hS|!iN=erK+|$cw9E zcG}ObPWPFQ-4X}e-6j;NomvR8iAz`E^f_VRD|Gm z&X|(vkyse6s;gGbNSwJ$da~))A^Q3~&CbTwxOZUu zCz!7EGad!P=+g*4(P~qQCvsNtgw#dzejg8NfXjsb%e8uebO&pSxI~$+1K-0Q@h4gO z^llh+dbN(TDc1$6^O6LxB=+_IdKp#C4^*i}T&^ta+Y|g3PU@#(X@4>&M5oRk_uaw* zZYd8e%c-y~!VVeYj;*vvH(7uG62hlHrntj@K5Rvmp^X%t&NSi(?STc0E61 z)q64}BT2fbsC?d-FJ`HEU`-^f#L@l+gM->;@MqME{aXvjVK*%_^BboZm1?x{Iv-{E ziVqjcnuDx-|5d%fIJ~wcvv6y<6|WgjOy=5Qj-m#+gcs>aPi`}uHA@! z-f|YeD;rwVIzdRZ*_znL<2_UvFmwqVLn$O+7&N5bI3ZUSN(g93hYN;*4O@0|)4Gxt zm<h>x=x|+Iy2X*qS zg3R(RquE4T?oeLFj!;^xe%E1B54mFa?EuHN-UD!!7 zrcq#Ni=%5)f(Pq(52yY#3gn4H#JK^5ra!IvrWaTB(V6>`@-g%yQ0n*ct32 zwOH@!OW;+tb9yK`uI?isCF*sN~M1F`U6`9mim8)t^aRU6~h(G01p!C zAC#!xQc$P12L!te4>H}C>;<6icwYT%BR&X-Z?JY>WKjC%L}Hupr5%%nI!xB53Urh) zCNoe6%@w=Si;>geketMWVzEt`EElw1pHmn;qO&3;3|B}1K_s`EbI_}XC+%seT?W|K z^~6CVDuxPmInXl&vQcx_0HZ%0cT>IRx1zXY zrq+S3@%tK%3ZZk@640-~S7(+YFJC>lhp1E%uie2ukGng-?uc0kc93-k2HK}aoq<_`F0uD@!+k2G`o_OO+!K4mq43F~?*zcA%5E** zY1+*%AYlcK<~W5J4BHYQwhhJTS>F&8wMgYFKXjVS=it$?rj6na(4nzIG&|mZ^3iO< z1lf+gUynw*K!zBu1x9~QRqxC=wLjAH{m_F?X!==$A9F0~7D`lUFbS-QdzgnYp5+nm z?L+JrNK0nxS<~4{qVt|=yZbNicoYrTNO?rh81=UQOVonDf=GZ=RwI~Ww&0}TDUH1o zx-JFM-@Yaso@+)KRF4u~H(1sq$DSH0={keQ?c;m(p!~cZ9OFOVZBi|5OMtn#eePQA zwc$ng^!7Ep={P0~y<)0b1eyDu9h?56qi(eO4fc+o=8s!qcB1?hRpDf(;H&epVR^Z* zX+pIQ9~7eI6Fq&KNG0`%T#41n4-Ny1LjkA1(L;xOKNA=MWWyh9Dh_{3%t3%Isw8G| z^H>EDW}Xq@ySq5wwY$genQA;9Nry`T%PD5hW-*WD>~M4mJSXIG8o;U_{-n5DKLu6n zZ#?qXY`K)eLiu~J^ikwj%cIRm&>eaVZwQlab9volq<{WU@87maDu3=S0m{yBf@;0}XqsY8`z`OPcI1= zwIRR43m3392}ScI4_l_;<4)w;E^T(iV*%fFBGetR^tybZBLOTQ{bW=k(F%T#RJ1+x z&8BjCGAt0d{CtT^&}VPwMht-bB#f{(nglIca}lhY#n*onPQVIFd(ZcytOuRR+({K< z%m5EG$o*W#6Zq`8wO?oIw!IhpNe0iTf$mTI00PO2Cg;lZCROj}j_sWtBv3J^#sokX zu4Em|?2V2(wbvc0C&<-V--iH;{RkUOlH?EXu*Vpg7j#3CABU_XdtY7W2@mws3p|?X z9w+7ff&_F@V8yq%0TrQV}!l~*oJsOgs$W`k>^ z$P-2Ew3*<(`DxVL(peI*N-c5IOARe|=$Cn73rKrskijxa*Oq!RM=F>5=hg*A3@N6l zmp2q2Ox(yJl#8B)9|1opB=g+-$4Q|&()wWm9EHepfiT@8J;;qHR+o=K=w3!04J+a} zIK^V;UO@`AM(`NaiP&kgLzuw4Dmhd+@9mxAFN_bSOHJE|zd@ybI%F&P@&{%$H0rx^ z(A(F^1F21V(6XK55taFa`bJ%&Q|??_g%QN0PUa-)=EHHkD0K8U-x7kT5qFHK^KMHg zQW;4XbYe(x*gQTDzFund&-8$h>*0xXD3vHS^S2%j&Ad;UD# z)LhwfAjUFBzU9)?rXCMa6Rz2u#_(KR6ldosR4I^-M~MXS;pX>uEIK0DclsM6fObJy z93`9HIOU=bLPuIy_MJD+ZF%>kY~AR{K)_B=9q()3ZxY4xKi?I~nH)^M9$buHusbq_ z9Z4-c7V|r#n1AmwR*qYygMg4>z(PGmF%7|XiW8**udyL(lT4fpERA6q%(Kh{oH zxYy$Aqd6miRKEq?S0JU6DZ~fBPc!|2U?oll?8p>-$dbkFm;wTXaVQC)_6HfREXBhX zN}qPs855|-#ML*V%kG(t-LIEhkY@W`snI9O>bFlJ7tMVypg^*zphztbrKg9AnE5n{gDTo8+l$Ffbn8eV@=VPesX z_=h2|A?Rbv#EkXe=)Bc&vgo${Os(y0$ms>Q9W^n#l-JCS@UfR^{SIb5>V3S;OUyyu zo>R_voTIW)3`&KF&9!kYP#4jp790mg@2XrkI_g~?e0?zr6IlFpRx=eVFG@c2feASl zU4|Hp6JrqwF`}sQ@GRZzOL_9NG)s{Mf2F3vvaKQ4Vwk(*F^wS_Vycxj#)}KYxdXNA&n~fl;93K_&e#%8q8qSKwz|=fkFGAPJZPMxrNd@#V75W%<`oIK~AYO-BYybrXs?--^8=q`Xq?) zsb3P@JkGQ+H(m2LXJ?LPD5DAZ(pd{V%f(1xz*WF2o+4r00jIUM5>L$8>C6QD+4H9N zokQe#bCq)FAqk06)1SL>9S0ZZA{iN+ZoB7(JSS}yo$nt#M6T~ETTZ_fHvH-bTNOS6 zpvBXQ0qe>PZ}u3)@9o(rYiReVh&Rncg(}*qjHV=wgq@)&XYHHRmcA1^yYI*9K_eG{ zekaoRWMbE-7%mNBAc_C&d#KxcXjz%?f2HZBU2a#u9S$Mn2EgTQ8(=ylsYU|Q z-W1`TtP`|mw}Q=G)lT&yD{28F)ic9ZjpILsmU{&{bmXEhn|B$#=qTCOx3=iZm4>t&G7pQPi#TDt&6jsa&(Cri{}R{hH8 zh4g3ino~GHU9BV(Y9prE#)%UvMHE`*MP|!f(-me^#@j4arz9e+fnhxDC0WjNb`(>+ z@`|bQRT1x7J5k$UfbS&1D9ynKPsTd3+Ql%Zi4bKn>syvkoBcwgl{yzPa?f?C!dl_04{dMB9z=9y*E*v%5m(-V$Cs$N%vH z=vhT^^LTS|Ia-Nvbk$8Z`HXt&?Ub2N$ZAvSq21yImU#}x!9d0PUi6I5UG8ptF!o!P z?`GjLo^332-JjKDH15{JDP8e}qo~lB9yy~W1<0qkpeu)cH1>h$Zu?b|HNU&nU+9hB z2?Fal|7eMS1!Ql~WwDM;*PAIutJ!BFXZ!5fW6^x})w0*dJ|7L}nlLUqR#wlQ7GhZT zCL7e1*&QIe=2hm6!0kHYN`$rTC;%)<5oz*8Rp2{=>Nn-4xN|!wnBNX>Meog&vV$yK z_ImkM(Xg%rP>7r{|F6jLA11Y~m{5}4yK6kWpcm!b&eO0{+cpP?pNG|@m++`;Q+-1~+7M-@bX zJatTGByIKzSy~!HFD%HFzW4Q)CaO*z?Vv1y|HUZikAg!~&qi*6aQE1l+Vw08m%38V zR-CXriK+)Rk|!tvaEqkf%$O}0$HMN(+1#eKyG87~Gj6Q8_gW;N(P$GPnVc3!l0gxU z!4ST-14z=~`t{7;UCUipec1E{H@xz7*HpS^$!0$8#|6pYr%c`YuY&Yy&}Xh$Q@%o~ zsY_Xd#Jsko+DR^ltOfL=APHg=TWbFspHR#QkpBEaNXe>ofg7Mf?1u{}2FkN{BR`H5LYC+iGc3`QkBUSlby^a1EdO*tvQSk|fC7c@fsteQ z!<>Y-;=#)e^(e{;J7Hx`3hSdMLazAD(J+OFeIfM+F{b?L>l zwH~h$Ln`;iX1^V=X1RI|PFbT`zQc(R4u2lmD5ya^r=PPlL1|O*UlbF0sQ0e_f*~zC zcE^)f;2xizdNzlkQx>Xv|2p&9^?kH=dXDa%>IsmaWWi_oaO*+8CVzD4;HhhxK&8{} z{M>eWV~~^Di}K!Cp4!`Wy*v}N^)BdHV=&LL=D7H+qZA8zbV2CF##^CIngkVd!y54` zs~+JW0$YQ(ouJ3JSWdiq;xN>J^lzJDczTvUwe9UszLY<&;$p6#bjLPse#>|JJixd+ z5J_1GfbMMmW6kTbGh9dohuY`ttq9#G9@?eBRS<~KdRhNnp^2Tx^_bj1Xz=?5l{t54 zou|(rqor#frzScg+Qx(>mcN9OAuN6SxpwzpQrFfnKNXJ2BvjFe-|u<6T*Vj4@2XrU zWfuckVX}N9=Y+ap%TkNI+`^8LxU63${)ShRSe6itP<_Qc@Lz>p)SF-79sXTR3p0{U ze>++(NNqYhRO0K`)n7teX{8Y`f6}-c9wK<`gzwiwil|HzD~5P6up|JnS@u}DeRIg= z^R$m`R`ks`Rbm4UH`nf)sx+a*B~Ts^P#)jr6G2G?fc)6p4H7aqpAjo=Bpv1 z)A!x)M9)vc*lAzPXYs=GHSq#1Npq#jf_|i$FxupK=Ts3^{(1XT9AixCGL=W&+p)$96JM8ue65PgZgS)Ie;NTr zH%O6sqe-{#5?3H2FLD~5G4gJu6tjDXi;Ddk84aBQ>SXp<;eJ@>{5KM* zs(`XP1}m)_e4V0-sC$0ce3j)jCU^SpRNIMhKEP_vMTS#e5BN$(LUPfLuXKO@{#qay z&L-oPT(An3&Em-`3Z;5xNRx0k``Laua}KT5C#|2xNK99l`$PJGYCwJyTlW6m&gI6R zG6|w~e}u~9sYQ=+XZfV#mrN?o32)EFG^PN_jNZ2jR2HMjV6E$paV(Sgv!uY9;SC@A zT^yCyb}ZEZ$9yR;#Nn;Ax)F1NYzA@rwv%0J2Rgp0ZHVW=#Z;*)rxG>!ghYH=VG2fc zm`@1aqr?rID(9PrdEA1~&hnJ}9xGulMK&2J*?>R~; z496&zeUchUvq0*A_8X53T)P$lMNy%IWGY)7(^%wMvO7f)Yd2T6cVUcqmFZ%&$Nrbo z>M)_3^8luD8A3Hl$lyiGv_rnRkbm=+KF%(KKP1K*IgemZG~7!xkZjff5dzwMLgtoO}o_hRI;B`pUF`r+Ab0bhj9FWSAVG|t~BZQECmBN;U9)l>`&q# zrEgS(+>c-^uXtu@!@iP2651V|LThzBplO6*LP7?Vkm(MYEs$a=6w4A~q}1->pq_eD zrSOxGW?I1)$bNgzYi&E;RWtY?jUVzF9rJcuP-N%E7%yu+&+P2+kqUe3dwbw7SUo{z zA7DdIGuYzOzn=tH^s}VU3umRq zmnd~zA}kD?(5cJel8n8@Voa*85_~pDL&emzGz#vz{kto`eU+Oebo181mG)BY1%?xa zMviL?LrF5DKDfSrFdrT%_;I(ddkIQ(hY;%`%FKZ3F~+M1!6WR${!4!y5F_7cLVJ6* z09A-!^!l2{3r5YZD$a#R@MkNGg`!6v`5Hg7Wn{y2O!}1o2Lm(zIY6<4$6r9ynN+Kg znJqec+V^ZDseDd|b4}n4w()}Zqm>xaRD&k=TrAoy$^>47NhSKhAaVce-OAf4m}Lo~ z&Bdy(_3c5SApTL>#XS7_SFMP#(EBQ=Xt3&EFlr8*7yLg%fHpVJE*S5pBXZN5(8hVi z!bTXLSQ=`;osdBB5SIc*A*w`h>fN_|*upT0zQ!EO5xRdI;{yjMswzJ3K#^xdevl;I zt=r*Pe!ipa%hRRctofeTjxkP>7{lfuIP^5&KWu-sikvZ)%Gaxmym}@oI#r%u{x@F< z!ynA>@g9D2UAz|*tQ)?RA34u8O2>KAHH|48{wc*_Aa2IxWR9qBV0Ak!dQQzk=^PKH z{T#QgZ%|#8RL ztolm``!w3K>0+zLkyB)JSRf*_Xr8pfT_CYc4T9o}$wH*zJdHA50Xvh>O1*gwTQpP;#iM_Au}X0gP?1aqPyZ3~ z=bW3`ZWBON?``7-E}pKwB;=!`;KaDT$s@$FexkQX)@iP|87Uha5|VJBD0&F$K5yx) z(oaBKA5*n2y%9$;a;SahmATGgRapvLK6rSu1oR9}x|7IyF4F?Y3IEJe{+BlNf9~7z zpQlh|6BPOp7f{bny|e3tl{O5Q*GM92(N^ws3OPbLd&8L!qJP&I!pe$;-KrVa z2(q@0_ff1h6phBxU|SrF{7a`ns1fm8@=X1xzVzy$En`hvjkZFGcHw!OdBP!$X4)LE zGvV!tiDG}1N=1S{*OMj(1;g|$0wEcqk(-@|U|jB_;ZP8S<%Pce6p@ceKAOottNzeF zuw(2y77F8Wzt0NP!(HA7H^o$)T1Q1H_8Xu+^^)~M%+giY*NY${7@~#?-f;<>(akUa zd++~0oIU)1Vv@Zg-{?!wQ)}P-q%pzs?zhSHW)xd%nh`FkAUo6_Kgr;P4bkUbI}x65 zkHW%(>Uu9}$Mc@kV~>Y=;fU9`T9Hpa%np#+zEVd?N9?d`=Gks;JT`g>;P}e?Bxk?$ zaD$Lart4V|ob2Xg!eA@&W9S>yTic@ip1G{lUr~U%;44FQq&>ZCx($bb6x7v4?A5Wh z{kTIiN%)SBUA568r7H*(BAO!}Adt#qjOJ6KeK{LZr@=^|TZh$U`-b2Wk6WC8AvOBG z7{jU9(1^pAL%gCij1*8H%V91)*YqD#{=dcN{vVd?Q1vLi_teOwY(Ju#t6k_9Y?osk z8MC$+lU#mvnnwmFIdmT!n`gGZs?|C<4aeT$BWCeo3O{rHoOFT#6t0~sjz2nPvG!km zNoGim0yJd?y6PDzA*;4V?lW~;(1;>iKub9WRQ)ASPS;z~T8g2FNHzecdBP!biY_?N zGdb*t-`c9ftIy*4^rxRl1(2cQ5x_BGd-65EqcmP{`=Mi3poY)ju(^L3M6`ZAdPo|C z^rhDhRz7j*8yrILf%pM8D{8ukX~XlCRb=?&QAXz@pRD|*u&D4KndX1pzIfut97Y?s z!4))uA_6>PN`dGGv!xg=XYul+1CVie48B{WE^nt`j#Xa`TZ@Ps8_6F{JFhuOr*lD3 zuw-~C?*L87llP?D*ET}Z>JALvP)}4}Z$l15QtW37jA}R~jb>85P(C>U_9N2uI5XE9 zaUxQzyIYEV;5e&=_Dw!4_r~SDbPK=Ec!v_NtdgZf1N47%x?NquxKwKYf4sd_P@GZI zH5xp4aF;+ra2?#;g1aZUySoJU;1FO2cXxLP?(XjH`cK~P{HN+m-a413>baSVnVM(s z-n&L)J1)O?%r( zejJbiYs-xbp=;{rzPA32s{iw@_hf$zTC}3yK_Qa+mf;!AA<5FW)&{x;c4xxm7ZydN zMQ911TBYeLVac)^3dBcE+*C|~O!z4#YeyW+3dV@brm($;!%{V9Ph#ReIhH;B5x=AN zllw3dq@{M=*SP#x7-CMMu@QQiz7)Ml@15)VRH=5f+b$S*ZczPWMcHchIq|cDZpCwj zQWQ%()$-v3rvk3jsqw!1G#PfUD{L|5*qJ30cnVDUs}09pitANL>fl;ir-gD}C2h^e zz2Cq2{u%f=Fyk@me^pfeKkbP+_Wn~1EXf^faR6y~aTEY_Yl9>4)+c_@SKUaO{xAYeV z$)v+J(s)OAb_Zu0yf91H#P1&c%A{OT!v?lpg<%Mk`~s=ct@)6y5$67?RciY3T6#_H!8aUA z`@NUmyzSo(qgVWL*ISRL4k)W{>kcKHbP$>sEoixFWTcRPJ?KEJIFAlooUpf6cEn6@ zm(;HT@UB^K#%;V9Q@WPJ2?Eeg_JA^-aG|P2NS923cseauKH}a=`=VsuMVElD63y;t z{leXLT+}FHxj58w)*-M?MW7s$3-HcPq172t8T(wa&K`3{f32pGsy&FD%aiF#j=GWW zs@6;_m%|rvKTu_%>g^g z6a`GSL3))fT~T2oHt%M3R;;AdJsfP8@WJ&v&nn0o4Q;==TY8%D?KU6VyR4c|e=l!z zG5F7-I&Hh{$)scBm!dQk$a`KNj65)oAjTa~tV| z4OT;{rGJX)*LY4&eD}mnjYW=D({w|+6RG2<)VgMqGSS&lm zWyo=xO~uwu;Zpz7rJgvcHzznz$8q-R!YTE^tP=OTy<+E7H-T;$=M<(J?KNRVWoWEg zRsDPvOLy0coqSX0)DnR;&&-rQ^Z5faNWlWMXIEuwdQc$WIkN&qd`dV-bt+ETkL8FK z%;a-xa_{`$E@EuDeyBwBB6jt{~$ zxfJ?=erCK%hNi|8b0ECep&{23@`TGtC>T3o-5fcNBbm{3CZxGf2_u@YXEbsN&7BWD zMLTu3=S=IX@jXnucxfNqg<&>qfmSv3AnZdo?-yT|{Ev?PY{&CNVGUnd!G&yM=9&~) zThc(Kc0SkF#zP6NM$|XU#lW~EeNZjsx*mu^)6o_GBz0C)!>67M$?81?%F*P{O zo>84P|7~SDwJ^@^W@WtTA|;#|nnb^Tov&(N!;yOHPPozB*+#J$i#P`qOvK1L>**hF z38Frh7I&^J;E$$!a5DwR7|Y?+p@cDZ1%(qiWGr0x)~sCLJI*^z6-MFJ$YnRj+g9E$ zkwk?IxPH2DcOuTs6bMiy8u#lz`kk7vakis*4Uw%891b`8{q|U{(sJs%p(){p(jgVJ zoCorjcFLlT@ko%0tx?4xA!*q^xJ(k60b*Xx3WD1y`*dV6Vm*B7{FK0!hd#4OE5aSeWds9D)IYWAaqYp??;Vp zB_t~^#m+B4Oi0KUkRxe~5=(@;8KD@m1vILOKLr&6Xld^I0*Q~o+=Rn`{L#C$NlKi3 zC?66RQ~kIY+ZzPM4d}pP?!%h>2=9QPET!sH%7ZfhY$|N9;t1yo`z>%NR8nG0Ubq`e zMN`KKDV-7Wgu3_pRw^ivCvM@T3Jqr0mXwEK2xP&2m_3MA|A z5RN9e@T9Vi({n_Dg`$ErAj%*C!{+*{yy1O)Pr*d?M5`Ne!&OZew_8u}Yg8lu^N)l# zT_Hs&E@mgf$>auiuxD#w3dkSed;3(CH?SMgtA8Bvb%&7&3cpN(#V$tm;9$a)wSnK0 z-li+e>5Ys`0hQql-F9>QVsoK7WF`%Cc@eyZQOaaEj2m%0D%VllytBnQqT12%(RXlZkIOnfRlqT20) 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Ů))QCM}GSL~cj+x7fWX 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

Z^cnfz< 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 z z1ffx*>6IdJpDuc9Q4p;wjd$q0gV=qGoqc}M5znu0I7ybO=20w~jEuamAhSqWpXk0| zQLYejcf9_UXN5}Q#lciTtzP&BA&iJ6-H_Q@NBd-9&3nYGNIXtF=RGfQ1|%rGZu$b< z}O=g?NAhG6gMd!VXF-g>(Q~desY+dK9}s|Lx?)NQ_+@f?P$@YiR|n46dNuIQu(Pgcmbq~N#n%^>AF`fVg?lyHQ94nhtdR6 zm)h2}jGU_$)q%o}nC_`+W+!*2nbIPPMjYr&qQk+wDl2>YL-d+Wk3c<1m26Je_+p&V z;6*i)%1Y(?Vx5(QEC!`Ih+eL$$Ew}B5AOHyQ7elJnU z0!#yr1u5!SO5`jl8yP&p8mt$2ksC1&HWkKv6vVT^628Wq-?vxnu zH$zO7lny(nQnc_k#Zwoo$SzMbO=1GzFrA7fFKJTLfQhfxEcq6>UT5>BO}~w+(+9P% zZX4Ncl0Ao9dr2-vofr5eb|@$$rznXqr6dU@al$d=f3&MBPWu0`vfmxUTXA}W z=4b&`$lcI4j*N%9H_j2upUcZk{fK0Sc5sn6e7q>oSJ9T!%g0h$&Eg~}K+jb`bNJb) z#Mta^J)!tFkm6vif?s#@!Y;j`(5@oiWavqa*}__q5@}c5@*_I0B44)Nf%0ap12UiF zGux4*qOti7F6IRp-*1s+Qq~4{sVR~0&NU`EfaTswIY=V$&t{2|G@Ykxx6{SH$e2WV zfzVG0DDUo+TUfpFd_T6d;>EsY=HVuy{HNw=r%Vh92^t9!mb#%VT}4xirQj04Z#k)n zGa`;VA`7+Z7*=iQ+-cY`)zo_e3%oOE2Isr&6I%6pN3v!m%)YJ_YSRq#pYnO)FXJhQ z9hwF4r3+JGdgsq<*o-fdLEo2!L$WhX4sVp}?uVF6%?zbss0$TlDPbeUBXxtx3S(D| z_gBX6)C%dAsecLYu3*cFCj%+Tnf*#)Yj=50+wk}hn1+3dFPi^KmvO-#*n%b!yY-cG z`iUOj^>!gzi>K|U3T~Jj+ivl1mT7v3ZxQ4yylEYsE?=1#yht{WR3)d-QxH?WOO-2aC@<22qoJhDh%-co?z`Z9gV6gf!`Y~Zwrt@d zeNO%TPy9W7$pp$SA@x#EfXg&V3k2%%ee%Uxvk;vSbFoL`#awBC*1!&Vz%NUNKl-gc z?xhHY&FFx+W>>8BW@le5(8Rn9kO)kIB0-Y9BX8%KOM4r(<%(5-2{hL~&G94=7pkdb zq&EuV3 zN>sD?JAHh4ufToJTRsOj)Vjo6xINy!s}OYif|r31@4YPM6lsXcB1t`ZYFj@`l)j<8|Rj>YP8SQ`g+DRc?P?H z*EBw|t&1^J$Po^#y;Uqmo`V)hUps~{2t6;?>PWKmeeQ0OxW`lRPX#Q?u(EtvnQeOr zcH>uow(QR|U?13UT|?af-v<);Y+L0xr>a8;ZNM5aIW6T7%miDy&GQ&9PD{4vyNuA) z_#ama@~Nh@qebgZ8{EaOs11Xk7S-`lX^haMx>8WPHJ-}I|ulGYHZsuHg zN3+&YJ(mXQA`EGp!cB>uM8@VU-*zGnk(HV8GE~FV?9g|WoE^gT!1jx1S^hjm_-wt`yp+pxEIA`*Ai;l*Z$L{GF%!)<(asSP>qqY1U+!~Krxdm)?7I(S7=w4Q zi&h-DCX!4&WpB1Iujg~DeS3lTYcb8*2beX3T~F8aW{yU$vw;dM)nNmjhA)X(MUH!r zCq}>Bx64U?-He{1GgZ)$L!;fe1TpvXDYqq6SabOZ?F$T|hkF!r?GDf|6TGMp3=L>YMILKt0-}s?pkAca_0|N2UZjqTO!247amC zQlrGnMB30X*q;S(lFm9=PGwlcZEBjUwLnTPYzpT;fY9sS5X@~k_yKrGjk2v= z&+|vr%0ZdYjc<^E1qq;h{68k8Bxe)(%yW#|`WZ<%V+8#;a9G4Hp$}=qD4HUiV=-SV zn`#r^Gv^ERBKQjXgD&2UIx1j2q)I*C(j?HRdo}m~Q)q`_b-xFgUmUj4U2#yQKb#O| zmbC7Rbw8o%4ig5Dn5?dY?}!dH&+;sm3>5c_(DMxr9!gEFc}ZL=>0l#4C{N zk48Dpy!>z7_0KdaFJ z;bb{If9qy4bC~YTWx;U5YD{^~;}K%Hu`{(>3A{wMx=OAL?dtv;4$e#=p)8E0&;};b zddja6-bUX!b@Fm9v+>y;hduNmty#EmN&HNQ)#1~00X?#3Aks?_(VKK;w&WA8K%$>g z;ST$NLGuEy~tO+$(hpVWirR*@B z>aL&os3LNa{fj>UFHliG1ogEHCk?0 z@hiMsFlqeskM%?Cqh+h$2-xonI#Xrl=8x7G@WzUt`=>W;P3gGdc6sy`FYAynk^SLN@`-yjfh|g{ZqW? zj$41;G)42M3O?eyT!V|BeuZTPK{#8aDVK@5Ke%N(;SbzktiGZ?4{{KC+3pql2ISZ$ zUj?`?A;7@~MHZyo;FV`)6}_fZv?Fpd*-jAelX7teY=3aa7XN2105`!V9v_?#+L*?b z4%~|qcD0UVyQdBm+|qVFvy#&5RCYUCuMzE5rU>`wS=^bO*P zhs{uaZI=_~Oamp$5&}+EGe(w1tBDFy$D4&mNCo;ZZ=;F2v)0Mt0)39iDz4bb&0877 zx_yfs z2jHAG7G!sp=l2>_POXbw?4jgcFD3v(SZa)!IjxNF>YgT+%B%<3*uS(MKi*>oNvwXd zpS*?acgh77cpRmIy$+?ndmJ>H-BM6nWWrfbnP*;Rc`nmjpsIwngUa1z;#-A zvc3XwmuL87#e2Xyl~Ue?U*Rm&g99eMxuCe#qSH*>$9<7{I_=%=j8Gfk*W|~kVGUoO z*2y-boQdQt`Xw>jLua8hs4iW_ zY>&zNaJB;O3CA%)%Z=);nSzG23s2WHORqD{d&=TW%FpEDa`8BvcbnO@VrIu4SWeqn zp827uYi^f`xjp=z$?x1oqF3nmjh=oib_Er?5}zMQD@~#M=lP>1%vN;gORans?9RS2 z7TRL^LE$;kd=&+^HX0Z8)e>_;cYITBmQxD;l!0r@5F5@9#hUq+2rn-4CO420XBv#t z$}a`7_SHCfBK*`-btP!~dxbLIdTrHq50gpF);zy%8~WouJ%U2s6&u25Cc?^N;Ascw zI>&>Ijz5xE9gtT5RelVqYUFQL+l|^@-ETbE7|f=S>U+064R&}%Yt?ie7aQLI-A)W{ z59V)ngWui*!}+5E`w?%hs2?mhf@|>xisnzL!uKo^&|F%h8XCP(QG#<R2celuFD583Gof2f~oaN}YwQzEu(RhR)-D*-oUZuZ!| zfT;LV>FqUH(P!zmp(!RvNZex?eX9GVC;Dy8d3E;oAM_B7*R2rJk6 zHI+4qA(hGs1t(e9HcnUXcom9I=2Y3U_Kc0F)&@7=$rUNR>mmsNr~?-Oex!Tpj}8rf z>fQUgs5+5il?R6RH)UsD1JFzR5{fww6XT(vrhTHWA0--iHM~IfxjaLlP+qF*lga^M zDjw8RH8l$@hv_UeQ(hTUQMDK6vl)!pBt>ChR*mVLxUt{^lMyeI`f9!OCgbxAZT*(# z=4VZ40WBVbSAe3LA%lduP~u}{Og?~^>-Nz^#FPFZn%sq;wN5E@d^VVAgJsxc3Y)iw zR;loU?KQ~>ovh|TZTKh+_@g6jkFNn2qFONwINVv}%tCb2+a-|s$000gbE(deO7C>F z!8|IpU9IQ>zC?puK+pag=2xX0qzhgj=j~mHP6GTTRrTDTKipLJCG=`0-XDvOB0 zIC?&+^pT71%Nz2-L~?|5q0p3VA80t{4W`6_^pPL&IQVZDUg6srcunl zHma_i>QHZ$Y(@EabqLS1cw~G*+5RJ7L236BhDn>P(c(l z@*8wMA#&qmd0(50r1e!Krt2+whbt+oul@rtn5u1U?YT=n z=-ePjK@%-zBcXP?odiXnfn1eU35pl(Q5R@tbVGT(oO75 znH2vIxKV13z%^R4d-PlB^7qKA7MvU+pZGK*el@G5I0-Ic zNt0`-qS-Wy~E5ePyHTElFRLEAo^L$67{kP+Qi|V73Spivg1Tj~uh7DUd6I41bz!>JWfM_i75j?Y!*va3nqoJ~v;nmxWc!ur`Y_wc3-#|ZCzj+vTZ~NGP z-8-iut3Iw2az3c!mj|>_0J|@ZhXP3~SoK3MC0J9cgrj{}y^7yJ`@3xpT+EMFeQfE+ zS+0L43cL@#?KVoF4WO!Q*7LQtyWD(w-9kaNoWp!~ojY_O;+rI<n~(X;$@ zw;9h$OKItLHlI%v{9m^>?n|u=KYw>Kqzw?$;ZW5eB&+Z18kL^YI3WM1_C4CD_M$i1 zQ0E@fWV*<1ae8N59+@FOxQ98@lPo$nhnjC0B!E4VgJC^`RU~T@Pf4YLceyT-qs&sm z>{?pFAf=$ifC`1w{qKL0dnGhfp^2Ao_`bhL3{j@cwMXg>1?<6&sl|g#X2UI1 z;hssn}FtAG&qx-4-T1m#11URSn<=eJG|~E@ z_g*HEHM&1M*VU>|&Tb$(+Jt~bUF6D?z~<~rAYXe}-L+-)8}tIkV@C(8OpjfEK~F}> zfCOcEQ5CYo2nw~oM)_u{uC>kU(70?{O^o zdn_GC?DMd&U*va%X)HBcg0W`01=?D?=-Pi;76H=yVvOa3Pf|a4Sax%%L{W|L(|_g* zVE#aSdjfW>j=j_;yXfX42Svh}9v$IkGq{fVtl(RG}~Sp=n_+SX>^s{^EW;` ze!I_iJRrzJdSydH9GBSva1v9NUZeDuEcyK%8;R|@(t%qxDxUeYsbDnb;kXQQuoAV; z(hxs}tCfwflEg7|bQ`Lxw?h6tKcc8mq!IJ>iWN%22zwAzA}@PJykrPw8W>+5FGA2+ zMs4NL#yJ%q-MgmwtZ-F>ddcu}~Xmj*5i#EZ>2GC4q*0UNrWlh$#Kl$D!y zn0reYLQfnQwI?LeVx#aN91G%z$c#;3egb#B2KgokJG+z$9*>V)T$fxFZRPQ^K+Uwoad ze%(E?Z|r~37V&UUZWO#-J_%T8E1IKbbPf*`Q_ph0$0{10mS;jy5K*MJdUv+J24$sQhg;OauKU#c##!$aszQ2Ftu=ARN-oG_ zv(p7nz3vDF9&ox9mc2ebGyG#>g|XiBd6U<>+3s*xq(cn&Q$|v|1vU_8$I)Rplhus<}xu*E-0u!LgaR5-vx)WdXr$h*UnTyCjSnW zX)ePl?$eIlj+uvORj+6y;fGb@k(uoK_Jm;qckG1GdJ4e*>!6Gq1Wk>o@s41WSnUvX z`EPtshbT~H(BZP!EM=MvoIrBH`;_0ioAS?Y2~w^G63!!%ilu5abYh5VC4Y;hx zaDHc13@I3nEL#V%IEoY%H|G1ZC4vgY-(4`j*hf5fKH8x`UIUL7%9&pGXz&`PRSzi@ zQQI!o{bYlI!A1f6*>9})Z~18x4wKtZ@!P!YlNas)3$7qUn1{i|N-AD6xeOZ4-9~RR zi;RxrSS`B278FFVl#4Rre8iz_&^gF!*Wg+*_->9NbZPa5!>Pr9dey%Dse}C{Q?c#t z<@Tq?H{7H|6JK;`tgdd_DDuggc%0?NsK&1>(cIywo(n~*b#N(weF7Zya^YI#egTFb z)~$|sK?s{Z1_3R`tHlgw7E9liIUO*Pr#T~JliMMd^`<7Fr8QW^{7gw)s9aVw>V-13 z`zOv{x6L#$aa$ev!5ZYG8yQKNuyTy>Fk=HgZiQVrPM6CbQ@HhEH_eop+N;K5jV6*N zc@5NzTn}9?F-3QW>v!2b9ysFyO0wm1Ny*J7>9}oukC)wy?s;K|{a^Wd>+Q&_d;R1h zX^vCo?SFs9kzUp!D*!m5`arpLCAVjrt_34Z@3C^C6){nRYYFBuvOA9J4+)JNeh@%2 zYm$p&{kTFx42Hk?H(%wuOpaYB%(*;T&LWRkEER{asGRr#=p#q{j_;V#x$=MOv9$V zul&-6qfBCBMwvxre8ys8ggV9< zL5qxM3j(OA%c|;^B?aNoKAE3p+@o8s^NxgE!w@x+^VBwxaNgowNTAowc( zj)?eGO&1ap=)XVs$cB28Pw`UNNa_dt;|qV^{{OR?7X)GNtnd(lGC?3Tq%6`8S+M$S zsIhSx9ZOSKKLjNv3c&v*>wgIT)C3g_e7>7-IXKJd0E)-8={w5920AEA#7c>-EB_HZ zu^%idy^^DwPe{*%jJ7gJWNi}ikE%Np;zu=J7DHEvor5HQtMXb0H{WQ%m-X{S#KQ6! zI$8{-Sb^S@%J8w_DIrP>kvHWSvig7Y{~vw&`5j0;D<3BH*WCtGk)ZYc97P(pBdnNA zW_SH+6jCVwgoJCm72inb#9Muc2Drx118xQ&k@cPhqFNjl+Q*r<=JC(%l4&c58K3=f1G>nJzu`f0~E|> zFKc=hpg3EhIEys2G}Olj2o@Gj_qQ>qP%kQ>>B-m8A>ymJ;&o=q=T7K0BP<;3?hRQz zG$`$4(&+B(FyC>R{!RE^_&>=4^y81;*I3g)z|a{1(z6u$2pI`seSki$9uanoQ_e=i z;|&K23aYB=L^s@@|12(m0}cx4SsA6)q;S!UP4j`Le~Dh55`cn15HS-%OXOpLiw@8? zj=-7O5%mfC3z}pgJdlLuX4O+LL$^K2f}bWe4cHQc{-dmW*1$RZquan^^7CY9dObp0 z&wIQw89YI!PQQ?Fu)r`M~mfP7B5`EEL~nDGdTn_NmRX3 zCVlc;zw5UPmtSr6AW_;PSwBzdu6x6eG2}Yg69tkL2c-x}m;Wak{nun#_WMGmnjjqB z7xf~2U>mzj$Fh`G90<$ip3L$I$_29!fv>Pek6TW1J+n~yOCxqCXQ~B@6`#>Z?d}4l zucUf!xT1GvK7=+J)R>Cu(9EHI<(+_?#m(>A-kB_%2v|Ngy)8L4qCSR(pvvat-0KxO zn=5=(*O{|fExp(8PP5as6+Gc_fBP7vl`_Ns-vRT#x|oUnwPZciZ$9|Tm5I9UVG}-I zumR`XFjgBfCaP`V9Tl0(822_N}Sr z;7~}z#SXCo5O*9|?M|M2$R(@)%{VV0B7&(eSjYcDL6!^JnQ7^Na#+A;zn;UVgl{of zG^aOoXM+<;W$)=>7s&b++7%Gbx7)ZZX|qCw-kkQLGw|8bTl5gGn=hVN06dP+>ZJ>| z@>bu3ViJjrR?<@pkX9;@1CNXp`9o*riArt^VKz_2-QotRfT!E!Uhx^7}WuhP4`tu_&*_4Dzr_zH{a+$ z*li7~S(DfekOv-%Lw>c8PpR^J-*bdsRiW)^;ZfN-`0n#FvZq+dWX|!DH{tnQzB&bk z^ZqE}0(Ujro)*9CWq)z3c(+w=FZG{s?aP)y`~q=;zO#+5!~$+fpSyHPt)>XT`tdf< zb%JOoSvI+Wnn;NW#t0|m>S`dWr_@@7JNdH>v>KZ<{Gworl= zVm>uTTkn>RPyb+N+&V0Lvq$Zsc01|q9Mrx@4h{7(b5c7o%1KfigRSVl8NKemqvscps9!Q606OvU%=KaQz?kNO2TyXjZEwth%48@t z=q(ya&n8|Wpg0)Vb^gGz=7AZepqwsOUxzEjTwp4WW(=QBFSJWk?p4mF!3|>LdI~UK zb;Bc3clsiN(zkW=p_R><33~*Qwl3iogC4Dqk2cxu@2K&d`?J=Ej*}Q1m~*0N`o}nu zX2p|d|*VT?=;A2_m51H z@Xd=|VqXbN82m<bfyJj z&UOT}Pl}JF$5wNyo4nNxi~X~TpNb&A_V`?6qX&y{#x)2sM~ki=gH!A43p-)a)GYH| zOALk=7<&^XATT?Po?w<=p-GTW?biL<>#0>Hi%@-WNB+Z#q&ZfzFUYSMd~>O)Z@$} zG`@tCs0gx^y=x*=HctsZUOaAbNxhgL^QDBWZJk6&-m?-R6D~NfxEKeY1t6 zXB*ESrK^ACatN@lQ{H-s?qzq}A*R*rh}6KrAW$t2TW@w=TxT34%gShkOy7EZ|#2Wu~~E30yk^W!M+`DJlZIrv`xh# z6ml4|ig)Grm&ztj{x`ekW$G80Zm?a;ws5obyk+d3cdjaGM@fLpJ)74$_p+&8LnimER^+} zD_c6&mE6<}$ghbf1G7lq9XzwnKmYpwsQbSy1YXfE>*y6DKV^J14$IC?&G`woQlCc0!W>p#AEZMD zN?8?SXa;`Yj3ZkRii*446>~AI{ZDHiQ79U{?_pTs)fGHHbs?CWAQR(+-&JKU#5+KK4jo`)Nznut>7VSCtuod_9t+lL+APU^IQw z0n8QXVgM!kOd${~%NI|}xKo}%84oS-F-tzRGl7kL{7V!t$58tu&iju#@aJFna_ZF= zm=cix`M>|}pSJX0FtM+*Ek6J6owb_3J8DfvbsPcczia3}tzy{{+JFDf{a@_756=hv zN9*UW3_dtgU#$LzwSj*aS~2B6f3^TkDlzoFewYMH!Zw$0A!VSgb6ylY^Qd! zL`$L?n^=eWEE1_WKZy}oUC&g}#o-@}QEO15%@_}hy)#{Zo%#zzixp3?;5|uWQsQhmwOj^ZKQggT&o%w@gt;d6nxt*^Iuakwu+bELU)#4;DiO;9Rfr`kA_$n}s) z<$UK%i=I>sbYSi5Bvn(YV`w74LxE}F_=i0sd)~bo^(TG2kjyr}S@4~DW95hS2tq6j z*TkTx3Gp)-`f}UcAL-(7x_*B?Z4h15vXE=>M$^bPkd9uaSj5Mo9k%&tg08SuKw?LLVKs8I&S5`ZYy_0He(%ZcLBcu- z`*s^+yp{*2u0ji5%ghX(!#~^jN{+PU2?oJR*X=W!QDrst{r$yW%wdp_p6wa5j_GB` z_43$d%)g^29)zpl5;+lw_5B?XwVLO*4BdyLR|7Bm zQ&qTtWn>yOB7fl+LS^bUe9dk?ukzlHRRQwVGP1yX;~-O#TO%SE=7_R(n|*>&*ZYpo z-|LYE>7Nm0`n~R3uAjVIop@@b z{&$no+{*~Kr+wLt9<^3FQrHqGGn!Q-IOocu16f`xDDtgCM|h3zv;#h=AJ!*K$bPSm zLdiWYb+j^@S|p!_JV5n~s8KSPJvAa;J6cNefQ$%{s0Ih!#MjxSj^~6b!ZCzH_|J&` zQTU!tISY3VN5qx|T0icC%Y(>9li1Hv(WFJ8vy~>`QUq@PX;B{fhcL?`#TEDi6Y{;- z!*~ZH`)G%zK^pVT1~@lat)Rpxw#lds$Xct&>D@v-1tYS(divoOXo(3@% zJSHIAfeudC#>n?xK*g(ddSj^D>vQiaAME%`CrS0jV1>0<7rGezbT%>M=B%Q1gB?B~##dP7wuP-t zo>YBkB&q$!f2fYclQRb}fBqs|izw{3naWG?z<}5A6Is6#Jr`u5KyEsMo$=l4)sdK`A1Cs%4At(-3c9K9UbI> zo6gQ~zH$Kv4wYgn$qb0@;>;DA2O4Xry97;2i;F*h?HU^FaO2dw5eDa7FjKi57y$e3g)d=w~k=J+_^aX&=!5a`W8cb*GFL< z$0-_!kP=?^j$y^D1vvc399_PefZ;uxphz~e;L}gm1YhUIE{HdLso@1Y4ys>}rOw5|U4tFdYVf2(~m^82f?5s@_zo^q* znw)(v1lZ-ogELsYbP;ynP@_ffkr+Op1sn_IhIV&o`lf#`jgUfwfDjM@ADnf9NIthMO!|&dH%$qqI`|jjHpE2KJL_g+~NBb6u1Q%xPtI{9GRxHN4 zQvql=@CyuTUtPzoc^NA+KD6WPW}STDzm@76qJf8RzmLFuo!ceAA()O0hsjd7id(rVD?hb>MLuW zA8+qmhFPo>-2cD=y~a+Cvy>^DUo2&^w)g3myS{x@zuXg~miaRQjCWu49s4o9&REJy z=>=;Jz>Ix?PM$UupEoKak-Xm_%@*G%Ism73ufl>w{~)?p3yc^)5}$D}m^|!9SKCM5 z^xE=}b3#A}2!W4|0Q*Cdj}Q<7*_ePj(wp|r9YytuSyAhuQ|XkeA1PJp6l<)G@}wO< zkDwYhrex;Ok^Z;-GKIt_-=tSjR75BR`1z2Jj}Li!d&m80zWegITYG2IfSyBX%EF`M z?)mD!-!#8#{$9QErPrb1q)4Uw6f5=#{Gf_>N!xxIOEvSAq&5S8q~rHrQM4juS!D{6 zSZ~_%%Xq3=y*o|)^ECN|$50lRm?_oH%1rp-n~wZ3ftoqmQmX+I=ECOMS z=+r}Bicx;*cOorlKlmvn`M4aQABQ!kij{d@|7{n!y$Yq2MWt{058gnjfDjM@LLkct zXq<`5M+kf(1k|Kf^B+Gg@;^yFZ+_GKp6s_IN#ErC+ppp;N)25ZPQs~@QGt#WAo3 zwPv1$Cn*+>PAtbSzsgP%11&yGAqn_*M%-Smm5 zTRgW;j?;awugA};ISOYT-rhC1re51GNToy!JF{0P2}bP9-ptfQr!qD4h@F5dq9YN` za)^vpa4QU1Mpnp`%NiCO2~-gsfw0gpM8>FKU}OwSYin3rS!PRz`QgtlU=D9(`QuO^ zdX4%9(?)kf$-Ie!J!EQ+QTpHjmp5ylCz^~Ni-~>fBcEjs^*h7kG&VAv$7vX%lz^$Z z1+00TT9}!Dl(C59{t*!s1%*1nI5p>Sn$YJPDe(Br8s4He4`;$^WAwLEFuY?G*qG}O zVUf#M#Ks~jDiTqVQHWKl**75fMXraXReU{+-pmP_{vI0>6PL!w#1y7xrn=7;+}~rP zqu%uQ*!cc#u9=f^@tkA`6_16`P#z00N*Ed&!`w2yznhw*ux(qP4y6jkg}>%t)|_SV zE7J=TCQsvtoF!q&eQJ!%?-UjrNFonIX4?h3ooVZ&_>n zpY6rWS+lYBfD0P*jrV`nuykVoXE|23x^l}Y>2&O?8EQ@z&Yx54`NqYwR=pc>ia751PL=&i~oh zZ~UJXNaX*FgEjEn8pCs@+Q5kCOEZ{i+Nz`QRLnAdofa7vFvpO2T3Y8~p5`#(ZQN1e zVaz#_*Kmd~HZ$k**03};)p-q~V>LdGWa@C-+`9@3=Pt(MJPj~->{Rq=R+%HZr*Mzd zH~q&!Nm?NUgn$r8k$@=BQbZ#^d^`kr;TGKe(OHG zLwL^7^!NM@sKafoQz|#SZ(qWtoA=-u9D!*5&8|!hv}n}^)ymkjqAR13BGq_#aXmZH zpMyQ#6)yDL_kHSue@~u;%VSSO z#hSpL?~Bcw)kR_R7`WZJgj@IB;1{ZZQlWrt;WDV*v;}HbE`_}O^6o>vyVHH_$G2RY z|FePY|I9kUTt$j7xZk^lYtHu(Zj={QYu7=Qvc+MU=1T#M;w>EB?)Px^!gbt#`ikd& z1<#qLC|S7<^Q?nBN^e}bbP4yKcp^;mBGl3jH5;}>>-tquj3bqQ@E4(4JIIGTsm80D zyRdZOTx@%o7o7)>!-W3LP$HjYqI`IhDZnZzoU%ibWMK1IlJRE(y zwufVWs}HuARR-L{{GWco+&?d%#aC=iHf>&q5sPP$v55x>#gx&h0Vt;a>LaM(2Khp}IOfnGI=rmi?* zOPny@r#N_EKlbgv0z>P(C{?})qCD>4(mgNOR$!%tojE*iK0;K%La;G?h>K^>!PmGv z29BGKFZ%a@gKgSMy2P!_;v){!aOTh1Y~iyMfmQmj0w>LF8Vl7R<6Qn7_Z zoK;s0tBEup?pPU6ro}1Z3`}6fmH}2)dRlPuIDL9)FV-*KgJ&^TY@P5N_xZuoxO7uA zv=8xN^e<{?Qib)NdgAb?bz(nn+^&)Ts(`#UC?gV(!Y*sPp+4 zOr82AoQi8Kr&1U++;UZH6ukLi_r$TIIDP&GJi-iM=THU}O4z|L>^UyqeuzM21q>QI z5Iws%p@3y9Ty}58>SgP2_C*k^8}z`JUkyhew&b!(ZLD#B5BGnLqlXV+-@eOGnp(r5 zToJ_jKESnm9^x0ySxT{@3fa6qpwCt<|ry-~Jct`x_=uKg*@ z{S0Qaa_o--uhDxnTSE5q5QTCX!p+kYCRX`Ss-O*CKe~duk9}e8)D?pU_C?29ycRLj z-8w6^dg%X~9%DZ)^Q2atUvpI3Nl0qd8R>l+Th^_{+8w75thPqC;iEBpU>`W-wMsDW zB(JSn5rJp7kK)k2y}0~L1?xg3;}noLE?$F2s1=&DsEb_igS&4u^5wIJyUSIazsoab zLk?sxb_9Ahazw5V#xi+(vdfu>mshd!j|Eux&vP{FHWX7Q^hTB9d0~>mZBpO#*`=LQ z4DW5k+gpQ4LZH)SL#}$02y1`QPE;`s7LS7 zw074qa)0Gd0k0m>hTn!!4SRDc+j20iIr)epGQ;_}T1C8Niz33qDJ(RU!onjdMu&VO z*q77fVQtCOq8g3--!^)tarm92eF+Z}`adhf{?9h(;r~pNkI(fD^!2ET^y}KI6q<&= zqlD#>(w`3eJf7-VTapnB$+W>h`oF^u*sn%vwdIYQ93bKJhPl+SV;|}=Y9Z}Cd7pfP zg6R3Re`rLvdSp|eFcm9Mj@q}MNZXEGr9BJBP>YJDWMpMboyYu0S3FX7e*4zvQlfgk zmxEY(aA*+?YFvy8*Xv2Y?YKs-v(gV*27LHL`nuBoEq~CI$=}lW3FG;10!_%!pC*sr z=;E4Lk_ zM;<=p@BNsz%o|PhO6R7s&HB-@LwD$Plp@a8T%^Yp`rqg-lq+|2_QkV-T>TK;Od*%%3S2rJ&~*H`16vy=lY`f6~Qk|I)AHdy}2D1r;n@gle?zO2dC&Ne6E} zBA-BidVFCEjp6%U?qcPr?+?rAwr6mr1nN;KVktT@g2KbYC^R&TBBEk=E=)@Rx{v+1 z_N$}mwhsQ!Ub`Np?*=s^8%t9%G&ZGPNpgjB6dZoY`r!#Zth=n@$4R&IJ_NMTbhwknf5eg?K$=dnt~6~ zboR&8YWpF6dpkFzCDo*L)2xfv|2~c?7IL7^`cI}~E-!hln{qDtrcYX)918&Gukh~+}AIvjCNf{YH4{w~Oz1z3ZhCf%(mOUrP)iZ=t+MIVM zC@6rVC4JqqE}0f>Nt2iUO99cT)kRIOu5G6;J5?hSV>9YB@_V}cEI3u=L4u;Kjq$U-zbD)MCfpV9#XBxo^)Z~Bf5KSAN~H?V~2oE8FPnF4f7ZLPwgonjQORWn|0@ zJOGAyT&LADzopKfeMW8Dw#|gUR@A!fAey`82nV~+Ct{V&BMHMee89(13L9mUkK!UCVk$N ztV=Yfu{?ggIC5l)l2a+@)q}INU{WutRH6XO^R9XT06+jqL_t)Qs?m(TU$&E@lJU6F z^hH(-A6qez`V{>d;;CdvT3sao~!A zJihamsZAqiZ6N1ozC4e_-(%HbPiXCo;Z!!4CDraaigsLmPLcZDf3;RbML`~Jbm_!? z+Wz-OTCr*i9l3a)0^hu7(0YmHq|ReMK8M6nZQF5FTfSFox!*N9WVpOV1 z1DdvMD?JHH{pE%_CV-Bw{)u{Z=|VGB>?C*Ju=u{iWArL3_`9)!-`uPKH6QRJ9lPyG z-Y$pehcDZ5@EAL)&}LYifsk*gxy=OW7I&}(d z+;y7#(t2QcM=;qpwJMtYJf6~nhtJ4|6*O^5m1L=spYV0wPv7)vK-T3t(YGtkXnmka zepS*lYPMPor;wlk^7r>o`On9d{+d3DYFFz_iiiMm zbGc4uPM)IEr%z|X-$^=o`XW7W_lmRJOilcWTdSZ)|E!`>y~fe6>o1aDkO%FV#}BNffsZ*sARJm4L`fc4|@_5s)!~I{-*(1AX z#qxEu@5FUhcxn1a;#MU(;{NWueK8H}HHf}nb2Lt|_jY{vyBws6{Tq?Bg#|U}JC63> z_TUGi2zqwq6m6b6hi3l1mX4f#M8S~>=PFI^+=eiJdj9w!J@fRT2wfkd6uum}bpkcW zZOsa_Z|LMB-^2>ESZ~_HkzMQZ?;W%$*#2u+=EyBN=ouZ~yqJb|=}5IJSEMc@XV8J0 z8V0EW)koGERJvKpXqxsxWy^C}M0f}V`1_IP(+6~J-)fpUV;ZgBah$yLQcC|Pq&HvE z*;kTgqvJ6LexcCU0j&JAN>D*&qdU<0HjqhDQZhY74HJ0`|zeuc*j(SO3fB1^3<+h^YO$N~7 z!*?k(IyGf{!qQUu9!t+|9HS-w8%iD9)TWA6y3u#@dHxDcV@;s**pDwau~p|#w(5M8 z+^SQPR1r=uuAZe`D_79Et^4V^+p9D!)HQi8nKFet){hP?_=Xx6 zp8`8K%2g&1gG)3_|a{9n#nmuzSt=Vye9(ujAp(#OHX{ct-9ZESy385Czt+ApA{=a zZF&x+UDw=FRiNpeUiVf>N+BQwgg}lXAU@EtWMiykntcc;;C}QFPCbo=V~dukoBIV8 z{rZ1cxb_h8v>b--zn_i)^@_8fuDAZF6mZ!)AHQ`EF$K)B)F?B#K zpS5NGXWz5`Gbf$>pJ~fe?{n-wE6$e|JCRSpmBza)DH3;&9KqR_YE)^_f_=Ys+mIbkBE^{boM&r(_$Xntph=TC1O!|{_B;N}+#6-S=b##C)eobEbq-j77- zx>Zr5j13Gq(rlbRvn1v4b`RKBu8-EO+n|!h-^n|tS4mR7??VNi{d*i|o`k`^QB%~m z^}(8Xv$0^!KA6_)iz(BlVqoKhPN|i+e~5i_aiq;%SJmh+Vlw;P(L{-4?`U`4u)mWJ zUw*^v2Xa)>gg&m`HjQH7er_u}%3px3ryrqE?OvETZ3_Cgs)#)Q`OJxd_XB6#BpR5aw}#50r2_vVxuZ&&7@#2I%^g z#&^6ABRDO7U@42(8a6Imj;*_| zqGJEialtq0Iplj+r-b!?^ZmbZmX@qJePaJ-jq&xA>6kpIUdrS01Ibnu@d{_poW+G3 z4-pv}h^H=>*ztN8S`C|s$y5KIy|Vy_D(m9-Uj&g-v0K0vJFvS0yW4eD>~43h&(*bc z?T%H~?rvQR0RsdDMNpCMo;u%ogM$MM2#Txg-q}Uo4Da3e-TUsmciuVwGXSOXq^%jH z15?6BpF3AlEikP=DBIvz50{%B$|o!dxpzbZzDce88mEI5Bbd! z^iGvwFR+nyE|+dN1k~)t-w!h|s7X22I@9Cqrm>uOMFoQonZOhB*<|8;Ox7QH_#0d~ ze;OB_M5A)O7O-t>jeMN6DOJA7j?c(jWlR9<@84maUKfM}2jcB>2RJwdqPp!+Oq}); zs#@how)9r448pZd>#=;vCaCJRgWdF*Xi?b;rm1PQW_16IbWH3k2nYg#z`u>azs}?* z8Y~E8h=7V>!h*@)KZqjP6Y2fK!!)8}HFbz%+hKF)Z%-*yORGR-D4kzEjvC}=ovkb- zX~^_l}dBLuTO+frq$Yi;%^FyW)FoR4nIf|uEJw*k-d)^@faAEXhAc#-k@N98lM&` zdH8EO^Vb%dK5Z8L`s;M+)1)>P%4$s`XYVKPh);9Y-CHT->j;hPT%C$F8bm8jx==XJ zq4c~GJHCgNUu_#}Ta^_{PLw$3~ zlfM*|EmuM3e$ufP78cZc&}2H}z#b48@dDA^z)bv<&(rV_3J3_I2-b^x?{tDaHtMK7 zHd+jvLRVaqc}OY4=*l0{sYNkU%BE*Q{l;%3XWvM5xN;)va?~109iWyqd(8=5BMPS+?Z)vsov*$`$g{Jy91-)^)Ks%Sura8a< zLNmwor-qe^k#*alv|Z8_O+`;nXK`9{QRiIMd64wsH(WpEF|5-SKtUlaArR|JCzi4| zZ2`%fwlz)OaGiW3lYSqBx?iE`Lt3zQS2ilpxIe8t&)+p)?13gRkdepLjT~xTifVVY zqd(beJNi=#gck3#&wVbQz1|-kT28&HbEtjxk~C=AW_lhFl_A*(sgojv{Ycex7rY8L+&;{rB?46d@ikXS?F-c=p6zq1D7TC3;3XOZv?Csad zCp0!)Yj=`j()mUaEy;6Ym89K|m1rfe45Y&L3znMOQvSKnihW{j*LL6G9w^ zafqX5vJgjEh#RdO*M|yc)2FNkwQ2l<)AU}gfyO#6EXW%h8$+>iEX2!tL()sFL&3=b zVq#(_CN_@43{x}os3_F!9F6Z&pYm7ePK$Tlr=W-^J}#ExIqXX7+Av8c{Os9)V_S`~ zm1YAPA0ufd>3${CKePLV?s>Cbl3JI8wUN@eF4A4zxBFBP?m=rN4xl3045&o2A++V1 zJH={QIaSPF$v@hCQgL`^XpkSh<1n_M0C(CubtqLatVE;IGN5q*N`;){N%YP5Gu9T7 z%VZ?u5IV<$|D&!|^H78S6V#e+5wWbnmD*rwAyePj_v8@H3LRMk?Plsm9IazgZpM?} z%X@V8^d8!EU_b3YaDWcz_}Q^_H4SUul!_OwN-bE6YxTxGv~T}@-TUs?L5I&@r&oUA z>KhUjhl@!f(-EB5B0M}?{af1p{2!@|NQ#M*w8}nn(s8KWqa%D%Rx{zIY~a*QR_-tARGzFjQl8}C37WOvC zSBJ(dJwe`K31w*N`6qrWBRy%;tYKuu`bWhZ^`*5OzNLNhtYY1&B=cR;v69Pqe#$7? z*O8V@=tYH#H=|L%9VgGgNVTrgC&_EwB)9*GHPGCXX`u1nPjU!nT{f_N@+MjXNeYqt zf^iFTzf3cJYEDHfb)&hPZjujcenqR-M|rAqDZOLOD|J%+vmsOI28XT2No#MiwM5gM z%=n$*vE@otideN?m=CXIfxMO-{AD!Rm{q0WX&TU^wT#!TBx{*eSSHMs&jO*M*vK&Q zXB|pEPLlEJ&R;aPs|{7{F#L-QXgcR=s_m23&$}Pj&(zk$FIYcQty=33asH2Kxpa=2 zW+#i5gK0e{u1J!gMyB!jN7BK22^wh9^@WsNBQ{oZoxo={udAAC z5$XCNE?$W`IQNt2pINc~ z+2)(%9~q~;D;ZQu}^FFbocC&J1XKh zEIN?h`}k3S|4V*&GK*RksZ9gE(16xj(m;Edwg#G{2dXi+rEC9Bc1G+V2nYg#z`vJ( zFrfW=$4<07g9N0Dy+iX^|11}KHd%F~Uv@sA@FY*dRpieJ2S#?S!yYO6l&4I0TDsR! zy^lf`MqX|XbnW7Kx_av#xxewFh$t4S_i`iqzpv9p{`}IlTlDC;7X|ASNtTh@`4!Zs zej&1M*@u?yJWbawU!n6CuhAXH=kz`}if?>VUUK<493s1ptbta#1I^lXN8Of`doc)& z{#k_t2o#EwEC3Rrbe@-YB!qB-g>$OoKrhIjW z(oA@fT8nrpvuD^xj}5*l+bD@rYkLv0MxOI|n%K7qnHU*TZuZVxu=Oq*DHHbbe{_N- z_N>k9+p+%KwN!b;`#aJ62|XwW-`M7<)sq$<{X`bc5A#&#oJ+ks$^MdZzDIc8qFKY+ zC3sWxVQ-2j+TIjO3TMNx&wD=#XZ@Bg$=lt9936Q*Vy(XcwzbH(Kw}y(X)9g1 z?MNQHHbsBXxJy>~$G1FZj?EuOHmtd%Hjud|^YD}L7<|w`Ydd%%U2$V$laBT2V}V~f zZfr!jvYviwZb6k>bf<+|j?(3em(}az-wv+yE|9f_bkv{TvDWP=6HdYY?|CipReP2u zx!>0ASh}~rd~8!xm1yX!mZkws5fe)9Ub@m#7Y}+L7?r5umnm~4MWZtQfyPs?n(b#8 z&^|R+Gv1t}M|{EhnY18X@7Qq5>rsL?tueJ4Je95`OI)Ff3!w9>CQ=jjKsC&kn>vhG zNVh$LSW8Pz;Q`+C=>845c=;OLb8=^mt#I{&q8Cpc>Bf~Sbm9DEx^>5qJbi-Lqd4Vv zygcj$t)DT3EDBVmZXd_dL1IbD2)ey#0kyNrPsaSgO*#KukO;$>5}GKC<3ec#to>aD7JZuivu%q=uE8uR-3C+tWwn@ZbjRoIjdcl*~(Y z+jOR-`%ci^Cog!tk{)E}t~*IXzT{1!L8~?m{2T+CJb*5)olC8X7NC}_fp+Xu8fY2a z|4aHNjuiw10YTt9Cm=M?{%tY0qP?F@Al}aj%jYjp>z|po8iqNuXP}Qwu|$m>l_DH> zcdo&r-`C;VJ3`~W6IiQc1R9me34hnqICkuByz~i0v@!;{OV>lqlG)+!;exl3CNML} z!a6TL2+2|w?YnnF`vzsyS`vwJ6p9dB_+uUxE!&K@xhkM?#RjNgkq^=F!3Yc`*feX0 zj_n)5GH+6Sn_!nyY7Ml_*9|dx<}A#xYk_=5`YH7_;@{)Q@AL87s$Fo+%lc=(%)#Vt z7Px=v1UzMV(4<{E)?E2A9k)+`XKyCNvtAQBX(1cyH5p~khUAh>+GP;$9~~Kii0C*N z7@HxdnJJHj3LaNBV*dYF59*!@eJ9PvtZ73~$~-~)L=_*1%STV(HTS7;yS6B+OPfyN zi(_+{^@@9_Glg~Irguj%v*a3RO87XQ#Ni{yVgJSg?R)fK{?;(f8ij|aPvhLByYPt) z#tR1rJbUGfJl54v$+{d$R&Ic{ZQ7z<+59k6>sM*_NVBbi=dCTQY4tk}`c{G6#A%q; zqZaZQCvSsh|1bQl(X2W74vj{Q#pK?s`DL6fRVj;DrSg6~1)-_v#jh9szForeIMz=xHoIHP$H9!{lNUc-1 zV(TgB*X+UCJu_g}q%3RPrPLT?w;)7?g(60&2NScLFp+fd_%lhLj6;W@Z)gZ(L!R$? ziD!JW+E^`!=X*?4qMlY)jEK z^#@-xZV1QKElaRu*+#s~Zh;CFczv`ifOvT*g2Gg&+OQRD+c!cf)`c_Bw2^B~?^q+= z52w~F#*&pg@YtjQ#<1Scl%8eaeDwmeH9_5$tx>+<$FkX->zj!b?kypwpCzmWPm7dr!Sr>x<@->pK7$~Zp2l@ z7}kXB-Jm3lKb+5>D(RDR^-~3W>hBuHpRs;^s%RG8!;@p{)H;i&oQ=^_t$)@RrSqA7 z7(*2zU)!^u*YEga-zCHqt&I_rroygQQ{>kV#>I1|aNF?(f#JtX^w$?${pIMRGx3>q|G>;|i}WOR)abYgm-6fChESp@4}DVSz!2 z$XgXH+uNdT)l$fjsQJh}O82?X`e%<$s>|PY`V|`V8Ou79qfjpX34ecj!lYC^v}suu zIrw@d;iZBiKAts9M4Z9^COLAz)WiV9Yu66e z7hSe)KVqwQQR_1fZB|a*2VOA|^85x)oqq+Bl6BF#C4Zh+|HnGgGapqkzPNGu9FCp8 z3!~!I(4li@)GA#V##v+V?ACQ$yl@j9{yup1%muD5-oT)!6)IG;M%gO$(V|srG^kh{ zIlf54?qhp(ZY64xokzabJuznHKvc0}?YY#yf|3-YihhH?Hmt+a)q9bzfi1?f&S=w$ zmM}?011+QbC#g)l5d;JQLEuLt@SPj@MDqlJOd`M`jt(0AGd*hl(=@u~A!VBUP&|jS z1-!gX3kS8OqS=k9NY(cIG?7E^qWP(AtRL-PGmZuf9!ATT{zARmR-huKtf^6BTN*ZE z0&UoLl&)PrMJpzDqKcMPXypG6u=khdX=OtB@(@=J4ed>Zj7`a^MpLqzzLpLhyF>?f zucG#i>QRNJBWT+tSBl~WX+O7&efNN74Qo%v24!jJj9v6n%Alpa!-wW8f}YTlG2JK+ zhk%r_9Z9?HdsEoE`!sjt0Q!0QYI^J+rSrHpJ%S+K54whlJz289%wRhKDL1R*UL+}b2X(IOD>SVC)}z6#7}&PqjTZ45)Gc z@pMjG-$t4PukX?CBf4ec{~Nbb)^H_*Qw{}ebz~f=Ww~Ox6kR~fgLn& z(Hh!z`W|^no{2inO-;Q~zHW4E=SmtodL%8{w4WY%hfrL!KV4Wmk(!oZEw{XNY4q=> zK4!^d4~U=_H|Uq){b|_rmGr=y=N+F=F##{>_Ju>VX)WvHwW&`9jEhtKmILY6B`ay| zxwwGI&vBwn);`D0|9GV^5h}=44s5A^kjQ1?@Y0fe!9jOI=zvp^8n1(z;Vl6q0OC zrFX0q!7j9VN28$`#csB+k_W;xqhRu)gB}Dw&vW< zk(LLVoLtW=rJ+rWQ2sm(Y2v&S^d>wZ6ggvag+1s#gvTd@z$JtZr)yjC&P>txj~}GV zLalv$d&ob&-r6lDrDMGD& zTBy!aDrHFyczlSajT=J4W-g}LM(UIz-*s$aYG^vp+jhzo65I-1ItE>5*N4W%8|p0URsOK5O5W$&l< zbYj~O&ZFna;gvt@x+chNNFH?QI)`+ed)l7)+U-|w zf1w9jyg#proGG?Q!P3-W@Go@JjkDvX@uiW`{Zq78d>{x20)oH~L_maa{y>8%x{?L~ zZT&MnP`#dG=#&#*c(I_aQX!*AKX=-*aw=7=P=ZV?Z1}=*7dgHSQEOQ!0w2@Dsk3R? z;)C?)&Jh~Zy*3$}l%yuM^XcfdR}>b@Mm83{zre!x4f0#i;AtDlHJEQ;67MKjXx8-{ zjq6>XOv==up}+r0uJ1mS9p*->#CrvlMO|DF{`sr=`x@x$nWVz+Wfz9G-~V; zI(F$93+QUKRhln{Sud|zerh&gDqVUOknAQS4Px>jI=gBzH7t^o>UJAVN9?`6u3-O{ z-T+D$_-qhTWY&*#BcO9&(qN|fleYeuz8=|h9!7`mvA8iCASJ!JDAs*Bux<|3u3DOM zSk$DRvp3V-my&+h$Fbul<+OX=NUC67fI9v>Hz6EKhh<9{%b~uDr;ttl++<#*1I^ie zgZwz`S!%obribf`jS8bTuJ>risyWoWZFd^*>khjAnvE^;2sV;#pdt0EQ$9ZD%Xgnf zJ8nxFGO5f-QqapA+v(@lWy#nuD^>0|g7)2Zr`J!f(!3$6K_j6E<8WKS@J} z*QD@(S9F|3p4)XBOfz>}q<6{m8sk}8X$5O56|-o@YsV>ilL7s<;AaRDUMaq+q=7HbjR{kuxdw|xBE5)rDd4?!f^|IahawKZcgUq>rnp%+v(A3f3=ZX z5&nWUNFiv28qmlEhv_v7bblOaX?;xJSj(hv-SIuh%p^NC88Cs)KYc?kS2xp$etl@_ z@}u;cjh~6-@>=$-$5wMXd`#w$mH|yk{?D$mVPZ0k9rqg@Jo}h$^pmdl>6t5vxG)Oz z^&-z#ujo~}e_qndmoLf3KZwQYQ=9veF5xNGC9FrE6T!m(!!epaxD5>&Ig1ku z_;6x@#IridD5YmUD*0jF-3x!xoT-y&-kRNX=Y?;Q{;<{nxnAB%BidL~a}I&rbIqB< zJhk|%YgAGsCysgJ^*RIR)GPHl^^!wu1A;gdG?hMl)Fk`75Y8}VA6*uNsv)S_fLAXcqa%50)oJgML-zPeys5ny-9^Y68$qnL$avUgr=?7M~*LE zlh+F;Ik?JT0%Xl7ot)%d(oOB zk0_WQ*-4jw%19QzpVXgkgsaoYxrfM0x=>AQ3+tbqX8p4|MajC$7}|UPP2vr|Jj9un zj_X1BvsWg&U;ZMm$fP$uTrfXTokp!n8dL2aW9Yb(k4|k3MX)O^oYG&d=hSS#csh3d z2Ce#aFxd_mL%UgoKRUIGu*5cg;f5kWt(jDdh0r+wj!>=(&ED_8Ne@0!NLqu8oDQ&; zRwXmy%=1*grycD%b)GgXnn?Alm!rJ7bCFTO+BA6fE_(FBlU_Zur(GNVM;$u#q26=1 z({<0F)D2qcGNAF8#Yd9wn^)xS`IbUi_*nZoUYak57qMO*8%G9C+d$6(5)3Oz`=I-s zJnT8Goj!<)@l*e$oVc9Yi78AmTRd?veb=X2)n?zz;Fy{Bq-8Ab>0rj~h5MsIJQ zqAA^4QUQw!)OzS_I&#B_Ub7~apPvtTK7T^jj&5TwwL#ROOE>B@c{QE7|0=;q#!vsx zE}c#d^X8&#S+WpHx2MTF{$}sLWan=p4H-Q-x|sUc%1fsDS-4$8Xxq_Kv|-6KYFfJz z<`ipxIo~8|841~R#CnY6O0aQM6f^eR}( z$ds(a)W_{*vT>`UW87r);^KN5(xfCC7KhVT)=`T3;F%(M?yj9SfQsd)N%ZO@|>2<`I++P&OwcO+0kDYuh80sBdE*3QMC0u-}q`99x`ccH-2(#`=v6r zT5vVzcA6e&(pnZpes4X=!}ARVN3fAGi3K}-b0xy-DjnN4lcrCZKocgU|7ZO8@wAq; zpIjwR&s3KC^cXC(dabpYzPYM(K*l#Uj5FN>P2-ix2~CWRC|hIa=O0F;LX$5;vmdIV6It^bzw-faW z736hmFO6thiE4HpPP_TM(avtF@#?fPXXDfK9pui2yM*Tma&kMjmIgK~MwM)Trfuwv zsePOTFn#B~f?ix&M#GxtryN%7RsZ`Tx^ip{4e#2CvtMter<@U3dp-oaoTV898&WP4 z1L7z{YCCK)-F(HFBXvC7klGL*$X@j``cpo0Q_51L4h>njolYIuNYi>WrHX~}lR0}1 z*KFIDHl4Z4^WK9VUfWB*Pwqnf`uCy@N2N^0+7B_M5=bC^0s-@#=0VP8}bWXb9qNG$t_U4KHa-@j0YN|z_@M|04YVIE zCRg-FCxLiBN7g@EsMbHr$$D1hi|2$f>k#EBTnZ+t2)H@ABgP;Ps(0#&PVJk+#;O3a zYqUg^@ra65APcch&$>nUZS^S>={XTIrVT^&B4(_6M2PUXin$9GVT*lEOqw|Zo%se;}$XB!!tc=5P=hkC{>E%Y{DwfDD4}oW39L&o#M!z2IQNMg47-dbaYW1Of zePeKCTi0#}9ot67$&S;pZQFLzv2EKO+fK)}?R1=uZQtGRSGT^m>Yj6d?W+A_uCeAE zYs|stfjpk$muhdWN4zzeOs9l1eJ>dm`uQ!k3&o)b#zO;MpU9&52oQj@>k)oGoua?q zS&21(qpjp)8Pb=N_qDjeO^47&2A)vo-g|Jm(YXO(E+TsK<=TM_}=8@QfoZ+B_S zeH|lg3u~p0F$V8~5(wbTym#%38)TseXe<{i^h1=IlP#8n0?M(tJKf-{K2`!E5$K>8 z%VHH0pEd{-G2LaHkEhGFAD_NGRK0_}((wcsP+j~~F$fwq71-=ktz|Gh@AQ;IE@TR9 zDirFs3Q^MsI3$(FX6vly84Ma)!nX`?h}pY&S@N7BRte~r*`_hLGilaCWM&KYQuAx* z>zntSO6_h+h(;M#l29H;SFMCs-a{D8HByM#Ts6ApIue;jo0JuXt{FFIotOA~&@ zm0FSjcARS#`OLOZ_1hcG8v=AakW6jF^ylu7OI5z5YMuh3mKVvq=xjQWLc5H}4PK6Z zQDzHI(|H@o)Z1-z*rGL9%t#;y%)d4+VQ~$Rmql!WF3`Z^* zc$RZ8Cu<-*t#H>nF58cd0ij3pS{Y3-fi}Kf8bkF3^RQx>jH}0}aLso5=j`LzOi7=p zJKj!5mF2o)VDQS`@O?)8^NjH{hgPE=r9>Au&+Fi3Byk*ksQ3@1Mi2O`55d({v?yJq zr1@4J7;wZdI=@vWo&nzdJv#3u)(;NO2 z!Zt*p;G+W~N+=qoHGb0Xe(-6Hr0*6YhbyvfnUO;D%D%vJ(XX|zzijt34Zho8bWSF$ z@p$_1(`iMO28M+8yy7nF)$8}3H*RQi)!+m3CIg)=MsUM3*s4^4PPc;jd?B7gJw%!* zdc?hV07?6@{_RJkoi~02XQ$Y81`|F!K%-e|I3qvmemEJ`cQ0m=Zs9WGOaf61`ap#M;1 zuYM$Jo*~}I5VwmTEV`j#cS`TH&fys&NTX?>He6|Tvl!0mSJA>}(#iPIN4O0dZh#*V z5!72Qco4`rB)&ZN|D8U_gMG+}DMZk>NiL^BS66jY_BTU>_f}Bmi;O?qWt-Kg(b=+u6 zYCho}G%}5q_Aiki1O^JPTdExSo64-zGN)mN#em9+sKEwDuZ`WNmLhXMs)k|%w^vDq z`L?*^^4Le`fwfQCfrtV+b2bMY)2QQE#nxy(ZF661ed#OQm92I#$V0U-<3K%^acc98 z77$B~eVK0^@7G}yza!v%U|&NKT72KZWw}g68nXKtP3)?a;mj2ug|-cR2_LhvcXW(d z_8Iu|=2T{Deel)O26}cjb-4Gtga-$OrhAZrB~=4tey>rgaTCIQXKQb3i!NF-2x zaAFMVIU=b37B!elBCeex39aDagPNI+zFCwc!K{(tY>P~LMAT^Xf?hhm<&QMjV{mu) zL95^>bNR%EtD8-*I^pO}hBt|8c(!kWwNMyFP0 zsa8<;Ob?!Y>lpnX+UKf;t&sO?%LU8!J;4-v#+vnFXJF|5&<0<&=kx7EdI8FMK5IPceya&q} zaJD7Sug0U)ZkVTG*Gk2uz?QM0>K4C}l`+S?*x%DLQHMjNUEh(>P{0b_FiKV1zI@Z3 zDvh3ILsMZ$L1nuuw;2q_y+a~Ai?H$fs9CiKOMZzONE&r1)#%IYo^G5fzsr6tSMP9k z?&{rWPhI1aus9e*cd(zG|K}hAO@043-d@k>9$`C zFe(#oJd&LeuJo_I!+(3uTx6bzrEWSckfoIx==Ppi=8CZniu$5=apfu`pj@ho%Gxwm zpkJM|EEYa^Bl_~E0On>zowvrYSHp1OFj-)?A4l-&wnDwz^lMDVi2oFrn2~Tv#t6Z| z*T+W#hAB0aqR2lcn;J-jxIl{+1w@5&sxEvjDw$p;D`sG1UC7ELpY?O`PlUSbNz=#_ z=4TSMgg3Hu9)w^w+G<-=Z=XkAI)5?fTktO(Iwg`r%&LjHsA)jD&I1}kC{R8Wf5NdG zStn7X84-T1PkX*iWjAVh=81I?S}>}^;#`o z6~}1;7DidvRqmwYYof}>99}?E`~}zVyru@TnLd@*5t1O0Pjmq%(@phqtz5=vJ+F*S zk-@}(;88Fh!ntyZ-(`k%iVGHZ)NS`n0?VW#n8w6T-s{Si3QJLz&c6zT9y=8mALQ#) z)un~8g26)-mEo4N@@s3}K~5{QHD!fD5K4ky34xtK34Rqx$qn*t0J@C6K0zu{K*EY! zd`qk%`8`>tU|=8>fIDG#9w3Esq7s++N=UdT^IH5VoeWPC|GTpDj)^5BHo3ti(Bn1Y}l0WNfeEJ$)>^m)PX#2_&=13pt4Q|FD6sV5P^R$F`zv$C)u zcC)D50)ud0^rtVj@RA6P{1&ut92jhO!?LDfPS;q-M69gckrmj_Y!w?o@`IIr!^CNcIgn0(sCC?iY(}gL)bLTujs!(e8k)f1(3tMh8 z{HDgkd~FOl1@0|Dx2~_DLCCoEbBr!9Hysa<(RdbOx2{_fBtf@e6v+vPo^ZjAz5)H; zC_{uGzvuPOwc_Gp3*-?KpF9%`JF+&V7eM(8m54%Af5b!c+O=NjpI8+F3`{wmqsueX`s>T*scH>%0vwxjED zQ34G%zji7$EQ2kGnJQgWJeVr{3}Q_~CB=iDgz)WE93y^Id@8V{W}hCdjhFKvaAx!{VTF>@5u(Q>pZ zwUSvDSS-F{XH`hVPW80cH2PSc=4kxF6U43Q$`#jDGA!WD+(VvwRIIqoM-byWdZ1t{K#q-Dfxel&*U!h3j#SOFbTt=K3rp1Y0 z@j~e(*2`&E2`6+IfIKccQ*->#SA|+@J;i6;J$S<+hF(Kst0p(orZImUJL!28UP4&Imk~31d@za667&6=)*Y5`dkrs{uig*N+e4g; zUv}8|FFzBH3TK4o?~bH=C`e!AN8D}n;inxd4&}%5o65H0Oq?DEJrM>EngrTnW?7Wf zRL0yqoXrSxVw=ho&uv+JZ=F4G6O^@>Mh&ok^NsfIM*olLAP33we=v%vI7-F<=I$HL zRDa!_PI;c-!c_tL`$n-#Wa&#bonaILKN63l8t6y))HjSlWHV>0nSpbeq6Ht#p)uCl z(!XD$^4(u;72=Zg{dSM8yWAH_Y{^b|d>D0HJY+BE9?jPsmgLJ-LUEdLrC#ZIifY}W zK`7iE+4~P}fzye0B=yw4)5xnhWj6C9zPBe3URQ0>?7bbol{2y&Q7q}U-&3dC&Z}9Y z!@fW~yTzX-irsAk2UzE}Qtpo;>W5~XIPKr*ICve%E*}Ll7x)utWo4)3s!F<=q~Z+X zF<5N&oMDW?r23rA@J<`vC3Q4%CLc_fAe?Z8Nm9=WC@%Y)a8#8e*l$|8 zvO<2tzX;;cmj98-waH&Ksgf`8u49AmpbgO@WSl4s&N+^}~?`&cT6bO&b4wIm;MfHC>< zd12*F|D?&7eSh1!RsdvQ9E-*h^5iyXnJLwXrYJZ1K;kt2LHgbC7^lsxtCY)W5n5cd z=4|3sZAqAwcV1KPV|h1yV)skj$nv`zwT|aSPWh&q<+(~w+brQcM7#*2Za)S-SA@XUW z+t`Vhn!R_vm#m;vHQr0PyPs&yHxNG2izoFeVfHo>*IXHv=47UU{+!b>y&8W*zzA}E z_NrCu=$B@L;&R8w#E=z&l08$=7s4(dFG{qQ)^%4k)KH9xl!(cBw8s3w4ssd=(OQ@A*=BXibil}iwMq<2Z}&+3eV4I zrB~PON+s}6^(4PmU3T49kv>X25vP+4kjWF3-I+D$#yi;(G0&k}agzQ$m~3^n$FksuHRB$%f$~MQqVoqdY@)~k?PgQEbIzU} zh_NAuua8jBw_PIC5Zbf*b3+7gu5{1up8%oe;KK|=R#a+ zXS|r}{`LaH_=SGBM7&S3AL0<*YSnI-<-%PkMGDF=0p3>-;wAvuDxt}-cgwhFuz#I3@l5v4iA(D z$v)dzwgPoGdsC9zcg!@uzKg>1!^0v3zd){37<$=#T+zMR-|7f8}`l{cG5 z|C&50P`6D)+tH^MA>F91W!)&Bv{O(X9I@Y6?xtbQkz*WX}!=DnZ-h=~5h-2p|IiY; z%{qtsVcd1($V*woWA{x@tIlWL1S`oVJV*3B>dzv8*UPsQ>FJF8;m4+51%02sZY8+Y zvBQclh!xAl3=DjQRuR_e=rJhnPH(VO`mJ7?Eq%AwR}!KDLf|Vj*!3DRFf|BH zhD^?s%Q&MM@bv9(IZ=zS=x%a^JD;-c3FsR*5pu)4b1v@2sp)DtvoI9Ao>B^Wk^ZM@ zg(&g2YBgejo|OPK5ZV=bc$(_w^$hyKZ3n)b(=G5klr__ChPY6z2~s4bnMa9u!1{l& z0El1}OD+vnp}Wmo?WZ?kO-*d{m6Yb1=8jNCw@)9wJQ8yEof3V$gGpAka$c;RSvpA| z85xhh=g!=|K!y8yOzgUoP^w)}thqPB^8FgRDlZDbu6GAss_r}rx7-;!Tp_T%z$p)p z(>bdV;dPjGPpH%WZW_oWPY+Jp4lmsO@cr44ueIk@%`%1>#`Xbm=kAH4FL^bdCzR^R zmxw9i(+nS#+K-y`yF#ZXBbM(BS41+)o_fQ%P84?eQfUm+paTUCAKI*dUHn}_CJz!h zNg5_?=!uutDvJ9e8=l0aFVu~>FE@J|WCa_(nEZ}tE$$=p+H&1X+nSjk^Z}oawO1Np zM~Ck2=;AoL-4#@In_FMGt=8SyESR&EvLN;`_oT_4hG=gZKU?O?FO+pz9+6hnKn5>6 zdS%Kf!Sx(h$TaY1z!|M9!A5YP?`xh<`{vPd9_!6bbVW68JSn8<7%qq4ru+5|7$mnZ zDij%oDy2#tx#bdNg=z9eh?peDo8IqXf~C&YL!>B(PBBC6%LmG2Hs`$a0n_@Sg3~5c z^-c%KY-5^80g})HwLlmK17gcdX9k6l5E<{T%P(Z}s-8-$xgM!t#D_Y-X_y;Oxf_Aa!bAB>&D^P8$^kfMrZX%jK* zpSMyT^219;Hn9)g{mZ&-tR*C7hES7L$x?;Hug|xIdje9_hd#DOtTSZqDAMgDC*5OL z57*FVn%+o0ttSPKBOjUI0Q?gXc-qz=g~7^$lT^8Y)L{ zFr$yeb#aq2bNkw0EEj46TGI^@F2CIifn18Q*Bg;MYS8KWRci&(6}*;| zJR1@M%QQ&fK4_qQeh&pvg-2pApaulKdq;HzEpDq{{fT$N{!Dm~uR8W@aAQ{DG>SD> zXK}0-ySM$AP@(2PljM&b)VWvM@VlK?X$|1nR30g>L6opoU6yL2G&slf@ySGg>ChW< zaKbNomPQ~fiCk&(`flraD7u~_S|DbIi^j7mR1-3r`P^Vg%={4W zOr08!L!|e}=D9Mm$}HK-b~~qzO^S*mSDHD@!wW~?DyI)=#_x@$3H7VrU3+a&K%jwz z+qBDe{8ObU)N^xqG;w%E>wwC$Ea;o0+*ec*=KZljHqU#_JjcVSQk@KVP|j!H@(PD1 z-p&Y}ijN+6L=u)`E!d#y? z;iOpmYJGV0v)*~Cb-5=`t^P!>r&hao->c?(PS`dCg zn6vjm_2sjlgDi65=}l*I_9T#6Cign;TPF87u}X|hXH;5By+PGE^x-+(weBz>Q4sCg z~=&vdcnAjE2BTiE!Yb_NK zf&yP46D>S@9Y}WVBaFa<7nyuAPt)8)Qo1O|XEopG)x|Lx-r6q$U(eG#)nS99%`T40 z75;)((?45_?Y{XU!2{pdl#`+H8F(F$&Xb5*ghn$U&vz z+QYPHswF+%@$%>Wq0r#y^n2HD8-)gAA7iN!5M3R(hHFDsfBP58vGnNMLzboV^Ea)> zd$CGA4glPQ1u2>NM_}fTL-q~hKmEepg1}4rgNB6m z<+&e;=)ujQ-4o1giZ|H3Nz{C~3q!^-}ozW6&XD_2t%ilr@dim6&LX+LNjwjU85 zDggD8Ye%??N)_NVTX#)%pphSyC7W&VP;z7+1Nx8k3G>&(q-#Bb)9Iai9{fai$9vC{ z#n_n?-{ICzlDH2X?=VD<(d@qEo$V^YWX}89Tv3bt#3-{eZ5H5wb4V&yBWxZMUzAn&7c8i<} zXK8{b*+Y5JXECp8jBwLSqkM;JdUyt5L!TwSf`=bxKSStvF7UP$vM1v?E`8kXydSWb5 z#s&b|VcmqM$p4!K0gB5wta2y$H^};1L;r^#qC_YtFimoQv_aqu`QH-MKmNR>{jEz4 zdS}0%-{*@9Qlt%$E)5YZ$z)ZQySpW69BlA# zmWiy0fKj*+o0ER`zvhdvarfMCo#5<%Kd7RX;1pe*NQ4KUo>B~~h35M}AC zgkWH046chOhP%5@z$e>;`Ik-V;S>1#aOko87+*#ZvbWzkeaGRjn<4}J#76z<#sJWU zeX-~e_}O|>s4t)_fL44lF z)q`E&xVKn~$MZMkYGa`+6pi{nqO0Z3GaVxMNmTKOA$?)Ruz63?|8%gxj*0TWhgPwR z9{l+VJ+kd!i0N|n4%^%rfP{@4vy$c(ln;QA%?Q9ISs zP35~mrwT;qCpQ?#%`Vktp{OT!RtuEGj(w!Zv%fgQ*6y}Jbj3t`8gJ;A#||weA_O0k zk?)Wexx?W=3G{FlYz;6S<=*^w4-)yG_Kp8X_53fhfCub-NkY2z7!5sg{ijz&yVC*L zY+7#^-_wlMPGTqdfWh#gCXJ(jE8-^(Db5Ti0evi>J5uH*THFumtxlj7V>N(`=g*W>Sp9&|u>5)#Fb)N)Crc$>zEzlO<`V zN<}0Hjqe|}TUgPhy8p6&K&W1Q*qwp|Q|@Fd(ZZTPiR%%K^pVQ>Bc`^^>p zK4q|M|B%sABLRc`Y9LuRZU0WFf0)`yUgJ6CL>3P`j}?Z5>d@hX6yAS&_x|at1k(Ay znIDspJ-o)E#_i4ex2m_8f0b>Qfd1-)JCnc3-4kO%Q~NyQ{N#WFa)d70;9tP#?xnYa zCWp~i2Gd`3aC_BVglo?h6z%Y=Ww5rvk;d4Fuz!JBYx8V*)_D{~BtpP)#EgUx#lIITFSP4zwN-qA?jbw=7No68q|EcY6t>Pl*lmFGD|J zv5E5dUZroE20$(8Lk#^hHJpFIwcV$|t5Rgm_`1BBw%JZDt-Ry*u?<4NYcEzhhIe4K z(o-x-wa@fY+WcT-hu-=BDAE5IwJzVk0=&o0BCZJE z>L$UuC{+x{HQ>ANC_YcWLyew@?myn7I%AcKCCMz3BJ1cI6(6LMBTgkL zcZ7bTx!o*l1D>Mc&8y4YbFUfa?5VLsy+nzaG!eXwl=epqBF-}XZ;eoS&xRoy`)~j3 zHKTK_`jMM~X+loCm|zs&mzPbgqz7xo&0t;>bn)ITc%yv&qj?4X*~2IE*ML~;em=iI zNw&f<&c;JQ!wBo^L*?)7^DkA7SjNE*k()A1XW_O^I`)#b6W@K$Fvv`1Vqrlu*qM!^ zNdn#lapMOJ6Um5QVgH0$sG=L@e?vk+iMj$#D_rU?(=}NB$0^XMxe-WD!GN z)Y9&@hn%ACK*l7T9%A?~5Ykc=GE!Hm3{phEC#Zr+x5UQLe*=W5*BIv3c(P;#%A>f3@2YTeEs1O`k z1&31a3hg{UVN7N7hDI(|@61LvhbgMp5?B0<{$ch2b#AX$#G6l${cPV*F1KEZMe%tv z=KL_8?pRW))Ps4}V1UDsh7~WG?u{K+&CwPM#5~vAkvSfFst4?bllTf*Vp+l8AdZYC` zELeLZ|D}J1ifi z`_?`Li;6WAw{FM4&t`gi=dGL--!rDox?vpj(m>w#*L>H=dWNxlKY=O1T51&tk=wLV zp5>2qX7Rg!yW~Q^)md~p!2N5a`6c|T)hv8+OqExNXzEZgOlWnZMJw6GYd{Oaqou=Z zu9^`oChfGSlXY_L;sS9VehOjcJSV>O%a#K%>-f$o%FWIBi?tCTMHC+rnk^k+_PG@1*1l zK)Po&4qed-W-QT0DMxO9ylmyVC=c-S<#8vGw6DI7atS`GHkNFp#!TS|EW$U~>$QF);9ERoL!uEYc>$!Yq5sW)W1}E4VMe1D z*gi#(51G%Zf-RL9^%@|KvMf;aCl53~Cu5632~5r8uCXZ= zn3PNqSOOPPQ;l3=F?zLuiBT$Kh(KQUj61E$XLKnt;J^qPJ@Q%Yj~vro??4!lR0prp^T?Ln#25#PTXVX2t@U}!2q_tes0Jr^5C z-1=swrslBtV(+;lShBpT@Ds+Ge?GCJer>*z$CVE!SrKbSayAazbA$(w&6~D@f|F+0 zZA@TPPTAJr&eYT-&afbnZTR))yYOj^yL{!4ddcrax#W_jA8qjFqvOV4Yu2o}(1R<@X-RjZt#@fNN)&|B-y~&_1@(W7Vf4#Z1{>C#uBtH2Ro0%5A()^+8 zsh=E11{l?OUAXq%u$r&r@=Df$$k0%kN`5OYnl05pWhqyHTWzR!rfr>-2u@SQZP&ISsm>F{ae>H%jB{xU%{_Kq`krx4da%HMPp$GklVVofZ;Q0iYciWe%36m)Bt{cXESKJF5Th}Pvv%f!qi zk6Yn@)|>}T6BFmjk2d;!h%r1sQUpc=uhX$rusixv0)8zj_kNh5ELxNL_%btY8_KM@ zGR1s^dUEyqW1Jc|1+x4l(fL3P+H3O8`^UnXoCxtW{aj=3BxR^QtMgBN-PjO%PKj*d zy22T1sj@hQxMm(&tfgca94sWRzUvQk^QTI%JI!*oyrBVK@AKHWESshQK&Fk@L}7Pl zUt&_(CWkw&luPbv`=x?#M+_i0yeJa>-y-m*66nA1RK3wxdNYtBNrZHSzk^jyUAJQz zFb#L%qj{9OP-<4g%}!XXnKLOLTt0@Wig;DvpvGX>Az5wcl-bS%2XBlR^&5zEEq z)i7Wl$*EEc!8AlWi9b0aE!BtJ-I#F6c0H1-YdTL+0(*XJ`{!)p_WlCyq7x$kcd=@(K26`zCDr*r1XGt36;5O8Mt)Rom(cjhT!hHccW#v#8e_c&udnqX_^f(eyQgF@Ul-inQB$;l@a10qdg{68Vq{CP~ zOxmIHPE4e#DljD-oui|pQh~99*)V_Fmnc)YMv#Y=1|zDJA6!Ah;cE=D5?r!$sX}zI z0^i2+AF&>UaW^r@;2QG=9n`gJ39I$G{9sdm?8KbfJV6fh)f!W&Yo!P72F4XK370t} z$6fUy>gpoK=RWXD{S>0D0!|@$ zRF(S@LI!5U{j1TDwbq_Xyppt%!$SiDx!Si~X`JpTYsxeTHlyF={#}_8k%7uYA@q5m zt6n57sJtS@?S2$3CI^ayoC@wEW8@s`+Sf@4@CtndemL1`s(pU`_0eu4begyvlk${9#1ygh*+bL5p&(A3n?oX+D zbZmt-Oh|ek$$`W*6ud`_m@cmy4ptNv0-}n8#T4k*R=%PZJ{OaCq&uuG-dJLi01VGy zbY;CQyv376p{%uoyq{QbuCA`!2vH%`*~qY7Ynev!ZPf`p%f^(d)zp?;3jsyQS2zCa zTHeZtiLcm%goy9E2lB(cAP14Kh%EP{k-qyA;D+J`XAK3 ze@Gf^cS{17|6EK*-$VIlFX}&HjbH-RbsRG)ANk)*E^uxCPihe2+;=rI)u?~Dd;TK; zksgp>hgOdvLiC^C_SxQ#F50 zldrSsV9WpSkvBlH)R~*F1?KzR5`MYjGQkOrxm6W`0h*ehlLiqA(4ZPn;x6=N?U0Sm zD;Ka(?b88bKwOXo@BvQbkpIpW_oRvf_f&5!D)RbIwx47O#p4BuqJ5!h{z|oM|TTO5~45Mc{Tz@RoGX=)T z&+wAk&pv#TfYix;5Sq!{Y5bRO;Q#myxQCB@6tW+3sBw@+r?HN9Vy5A|N+tQ(D#YfP zPja#9aI#2W_KBaYKD=7QGIGaT!p-HYDer&sthLCK$` zu<|9#{Iv6g{R7r!mgvH~?9r48q0O_yEkHGOP6hkG&a;4YYCkeZa_(g{FP*=X<;FQy zGok7KT%PLIHi5yI`7;>`32UyS!S84cBBKUn&LN9l_Vhub7Vv}rq!kIllVkVgh|nkP z60%PAhByB0o#=OiGQU8VnFS`AZ~PdSkbTJirt-Zc|4Nl~H`sLR|3x4@BkBtC;|ft$ z5c#y8@`p`tw&62T9yG|g-h>>C?vux!h%ih>59Uy^tX?6A@n zh4>7k&~b!`0!z0;GBgOS*xjmk8V)Y_mKc4U`V8K|m}Y;UGN12-z^ab)**Gdl5jQll zzuYxDJF+R6WP!GY1kx1bj%Z~f_oUETFBO!>Q6%P0I0L;7!BUDyZp}|Q->VnQPUMpR zPLjo`0i_?OVYGiU6XB4$#|K4)%Q6P(C6%FdPJwiw;btg>=Ys~jM7MO>t(D$E)-{2VZCj5zFJ;Q&BJsR8& zk%Tdohy56W6MC(&?CTSMRB4jt5X|QGV_Wx@S7urFu3Wp2TII-BsaUFIWHY=@v&k;Z zvF0p->IWv&$b;=%u(3$f|vv*hi~}+Gw*wt60JtjC8ZQBUr@z&9zzp?wKEc zv^-JByZP(x9qH`~;^CteU5vXxzDz?7?tB*GyA7#lN|I@vEbpdup`L_)*f5*D)3797 zJ2F&7i1$phtLsWC4b{g;LX7x-rH=fkGl2Gw_w*P!YhIc^xCQgE&hyr?lIsBpeGRT> zUqMHqy-`GTkY5FFBj$Wy>bKbTH7S7mO)2J z>!TanhQ||dXD6REXQ1Nz~q0gW};N^AioqT`Q6&-NIxbH zG$4<4Uw{f^oO~Wnu{3V5i*u!Z+J_yfvs&`NPL{1*_j%0taLn%hD$SK#{UAf?CBt|t zqV)lWWVh`waeQpI-Gh&uJ$kIPO{tkOyIYfQS{+miQ2$Kgs(ESX;dL* zv)#o8$HrB(aQU|eT%2{*>8zSl+MuGNyns#~04#RB9pIj`|Ds8nra+`Z)m&iP|5A&|a&Va_-46tZN2pMwSJ^~N09 ziIPU!Pi05z)ff?yzF{vTI>@Q0|K$%6(f>UeOwHYu>A)|e*L}C_svjL#B{>;+TaCw9 zOHIU|hg_caA&LRDKR)m#w0`09{;^>^5B&kmndqSX2`zg%tbRItBXfHa5sD|vj{d~0 z3G2J3QB&t<7Sb8^ptQ)RDViL0lOQlph?{Bqn!%}lAZK_Iwe)$}j0R0_Z@kVuv1Nyl zGAY_OXn}N+JPEvtefj!gxxR=95>0(HVMr}y`j@+~vC9xdv8qV*@N>P2$x1>G#iLL7 zj$*jsunx6gGw*QM5=S&3Wn#JEV$y2|z4nV2ij!UIcB_*r4fNa(IR_PD^>@kQt7Ec` z>tZmJg%#_47n|oa(J!5%xNq~P_X_N!zsww%6N$9j^~ZjR%8JPA#K{dpqs!hh z77rP|P_yS_Krgad5K_TapjC-ZLxZ~Ejc&S4-5n6}cEaYu?(epUNJ#HwVV(?Dr%2cL z8juk&gp&Pe!h&VX7R_6GXE52-XDJ_eX8OJ{&1<+kQ1sv4+RR@+7-u#Nkyi~g;_1uT zS}hdpQf7$Ey26zgr^BBhq8voy*~Ky4Obkat4mjOPZ{0<{5@YDLvjrbT?Y~uj`#1Q?Pkvxp{F4M! z5~kBTZOElUP83~henRGuO1pTQ^Gk*E_U|E`TS6Pn!>)#VaqaM6AJoM$qi#eR~*1N*1+v+YA!uajLh>SFVyEFCVr zyt<$y!ecMJiDVMmANPRb2OCjS70ubCEjjtz0o}c;LC)E*KO|nc(r~%mhq9!Qu(+1Y zS7MxwpJZFh1DNMx{Pc9h|62(DOz=|ZwmlR)o zV}F%s&sdL?y1nLQ&0Ie<=A`>rRmos02Gd9_moWNcOZFKSlzFa@EbaOTDBeUM8|Ci? zz-M>G5Cjb#dda@r@AuwOz%nakb`?k=I#RV2<@}WEE_&_xZ1`Ow`aBn+5m;(yfP2DW zO5DmDnZNL+I^&idbH)9=@^_%AZI}2?_%k5a8>q$xTgSmkVo1%41TPXq(D33$G0r5_ z9wc@|p*KQJ!@|z}a_E;hILtUqI<;Q>72jLP)C63}yOaBlK8O4}=HvihS6Ghk%hw7y z3OlSr>j}Jd0^K&DFv@E zLw!7iGjdN`!tDU5sT-OJze~Q6k$t7E3Yalz_jQD~I%Dz8-S-qmVUChZB33C9^bhc` zY)`#F{mK!glG0-pz?WFDa&Zs0a={U$tjA`B9GppaRmZC|w6m5I$?#*xGDzhpe01I{ z-%1B}3bTZ2`$5ks>hPFydj757-+b(rCAzaOF=T?HA&bJ{sRr$V z?svsK{PTyAF?i==)2;U1sZJ=RA)&$T+p~Jd0}395!{RD-ze0hsfiskY%i%|NO(HmU z4Jz3osq(bK+GAOxY=T>sxRtoT*W9Y%oh7fF7r-*KoMhj8h5Cp~$B7%MM@)J^`W|QR zP-gexrhCDdt>_?-t>C|c2f*LpLA@*g-U*cYYry-(_FZe_l@Cgkax1~u%A&|P#=X2W z1u`#zn%!#`Up|UT+$~s3unHObFY{nc73&2-fwj*f=$*$H{i*40!*OSlsGRSx%%Y9_ z3h$NQv|h-HwtoEKd%aNkVE#R^$jO`&=l;qA&Z1(8_jc*Ny<|f;au?B;*F{pUQXA0A zRWlAMEXf`4)@iqML;J)F8yvApR&tWBJsDztufl^x#xtBulgXXQF_w@(%tr0+F}apo zW#lZYI>k}9DOEKJN96lU1zN85Fhd$>X6`UPmXyN~m^^SpYc-#7LlQec?w`tti6Wg}g*tRjT z)7Z9c+qSKS6X&0E?>+DN-TQuxFt=OGT|HZjh!ide zG>`cpH}Aub7r9U5x%SpGo5=3t>tT&=<4GJQlyqt^PTp#E0_D;C>~y0VIc~RS|3<RsHv@1#)?eT*xuXK@0yeRE@{u`~*LvkL zd^N$Y@+9d&Ny5!?*(_SdQ-jS9&Q&~P)>?rmO&!$DZ^1O=MA;M7ILX`=xTi_16B$Ec zT-a+ZkG@6Y-#W-;MF=c-=#oB)WD)F#8t*M?j6RGozcu4-VUr^eO`|QQ-*J;~h>*&5 z8%7(l+aJ?g^DOzRQ)(v6%l0dTUoh)dv4$u=p26OdXt~C1`|OHj#^J z`HVCXI@V<8tO6{*W_pIrAJ${>_<*s+!+ahM|MdkKzxn-Zun6S!_!$rQUj0nFvY}e4 z>CR3#pDA8So@GsMh?_0E&U?;u)y8LZ@xAcS>cPrcjjx7U{1^sxnEip|!)7d3z2oCE zASID;6szoTqA%A8OU|a-wYfP$mJlQdqGyZAP#DUwmI;JNUXOm@jA@>n#9^+^F@gBXWp3QP9-a)go(v1*T zhX>qHH2&Z{xqUKv6F+Q#QnR*$(V&RwYA-4oOy8ay&iF*+_3{{oYKOo6T5|YLDQv&Y z$g!Sec(-=peYDk%eYY09cdl{{+yI@uLW9Hp$?Bc3!WPz-rt^nL0wOU!4;#)YYr@Ur z=eEsKHLk=un2K#DVwyyMj@I*mvcB`p)Ey~l${h!DjKe9-z`7Dddhb$rGRJyReF*8y z)K4%Z7zDcnt7O;QZ0EPC$<*pjMBBQP`>@CyLzNVg^dD#o20RPrU){_Y?I!CS37Eju zQH$7B2?+I9ZqrjTvB1}90xxSK1X{~Wsx88CPkZE!$$jcfXKQwgdgqy=RD5uHjEF~c2M_-d77c@m%P zyc1k`B`TRF=+o@f7uMsY-TOKq*1vD}n}K0WKGhe)xn64KcHIM^Lzm1YOR$N3r*jnYj7@slg(l{-TF5V* z3fR>K>rh+;(@GJBQ)LLtzNxB@AYnY2?+b-2!OryqygpXlrxT+5WX2Q&ItoaZeH*LH zq680_At3JX14$JFpeA!C;bCA(-g8b^%C{*Zp8mceIur~vL@RjBdB@^#36-Gii~th@ zg>9OL->JX@A0AQAOgzA{g`5hVVV=6bdShz?#0m7I9Y7_5xZ4fOs4E1CJ`uOvJu3?B z-W6K?4P_Cj>+F!*psIi~X{6X%#<|^Q#;k#!PVHFw9FE*!vq8|#73CO@^q&8C80@Lq zvS;apUaC&}MZW1;87O%6ZhEUMsD?9UXFOy;t=WO+$Ec0HwwmJ=&3~*s1q1!A2E1>h zC2moYl*$00&AqVOKN=KP>t*Uu;xqrC#bhkN1b)Z#5B1y6czVutThj|TLN|_c4+n>| z$K;yB`&0L~MT9`c74sfS#uwgWaelXDk=nB`D`jkpNQlpnv#T-w;`Ons(V^{I;nfLZ zv4G>YA(!jlFLb?ms2EifIw23mM{=mf09QN+@#Yo>!nHShF3LAVGKYmN9>g)bWy``B zedBtAjAF^N!!r*3&eJPsvy#6YBNw26t5B9xl>0h+QK@Z|dpl}ROoN@ZtJvT0C$u+6 z5xZZH%+#NiucJ*T=HHbxRDq`;``kR2fVpqBzle{IUtyKD1d=q6oC_V0iS5geizVRl zxp^b=HG-VPry9|`y6(va4in^_C-f>WK5v&2utjKPCh|vBoU_PjQPbhYB($a7849Gc_cW99o&L0DXXr(Xf-R{9 zHrPR~goQW(Y4X~R+=%B*#f^ek(p;h5XE`RuL+k^@qviD zk#+%XT!0Ur8y8}MlCXx~SLJqIt4!{6s#I)+jY>n)s|pizqW9#|0ZMO%AnLt&#bHj<<*msp?QlaRt!6I!%(w|FRoki9Q(qg(*6kZD-q8wgqG5%KsMOUH?q-SvhCH7}IcQ1Cb zVbXufDnv5V#q4HK(J^2BJ=f`G2M=yd7i(!82Y(%Bq;D~|MS!RzLXnG1ypfO1^k7J9 zFAn>8-?)_RzrVfhxz@5VP?3T~16>}g%&ybY{jE@2RKHq zoO*SYN72Jz+(nQHd2(Wk``4A*4j0f}Z*5Yi2Qni{9rMlbn!g7ZQ@mUn5l8)J3g{;* z!1p-}D8r}39gFg19843O#_Ua^GJNd6roga=;{9Mm)|7e7_RbQ2w0>Z&FIGBlFoCj* zsv^*$(A&J>{b9lNWy_llFCgttKql2!5AXDVtv}X+DLIqLfHBB>dziBE^pElKdl=6T z8V{2Bro(kATy6c@v-hsGBC&2JRkr5Oh}+F|D5U$-Y>AZI>9HP2GQcuo89=_hs1z<( zHZ^4MV0?}Xu&@7_K!)V4-3|+iz|)^CzoPfjb4VuP9cma$+HvQ_{-b=FYi20AjLCA? z_5AC4_7;Cqw>=7|KN8~pgsob+>0fRj>N&I%|Ax1}-2jT3DHP)A_2c%iTht{YXmt`C zf?}N>gPf9Uf_MydShDigLo}HbJesXSl49~0J#f_q5JO~bpUfqNsEYk4j?)EiV{adl zBvs|#Y9|>@Y~p8_jJ=~RVjT%)qt=G1`i*x-z=F$KvsCkYM*u0dJ=~l?w3>z^&&sJR z;_u2n)?-@7;33@y#LR;>Lu5yOFmt_7aTjzWoo0YGv~`Yom))r+Z&V{D;P3@DzL%k; zLYilQ-kovHi&qBICLf^gdDt!kh4^B?G4 z5@>VAu-@dSr&C3X#AD$DNRFI|2ccX^HanhBc?O`NUf)nwZrKWYru<1hr)`l}y{|ngnhyk+@aSl&5;HBy-1Y@7!ceK6kE$)`;O< zpM3LU(RAK1c6FjnXCT;nUNmf*>iH#3=r4GF&E|7$jDEr(G0`U9LX5k#sCgE+bo>Pg z^1b+MKML6Z{+gpi8``D5KdOx;Hod4X8`bl~^JlZFjc~mhLV0g{Jc| zBv>*{Ho6I!fdL>hk~88uS_2G%~xdtTWWE9y7lRchSO-;ZXD^9 zaN_Fa-Y!X-tn}@ylCCa4Sd;9`;=tCd3$Lea9T`G4*+Rug<(uupq^DS@+cnPwGoi8s z1%Wza3a&;{(R+MStf(HfN53%#-<)wPFu8=m37;pC_VHjALOM|>S{r$0)oqg5Xp|&% ze_u#ufC9!?8jDLFp zgx1scaXslQ6#ZCXCEyEx*ve7sJUTeMeS=Zvj{|El&&&Pur^PWqi zicFlT5djp<}JPYfw?tZ8JV3bE1NHdUM*E ziZPtLVyr^R_sR%~l9JHn3lF0pT{tZ$W?A(2&n=Rk58tI>oNd;v|6}9Uq)N+w)D#|N( zA_R3J^dzDy%LVF17P3>){)LS7VUoO6L z!$yDJO%LbMScathWr}v@j&pU{3-%2vPzGYpda_Dw3AfZ(a!!Cp`K^#u@kyF|JsXMW6Fd3fyqtN{LKDNsTIfO)~R$3CvT z_)9s9#MF88^`(PAk3^YkU#=J&5U60hbS3*4H%5NcxL2f$skbBzfRaj!RG);9udMdg z5O72~*#(MUW*#yW0A+RN?E9s#usr+p%Wagql_*gN2qThmX7E?l@JYk4f^u5naDlR& z2m-dIByk5d%?%C^tRFQ*gH}+6$1(iXO*JAV-$>=>l}c$-CxqI;u}d^+qq=I9nWP4(?lsriQSnr+%J3)4+QYE!nv~|OpVm1=V&`P&5OntDlhK1GQHMpMHOK0FlB|7m&9G$6 zfEgEB&^mx(`Ni((AM-Z@fs(xJ`Ga6H231ItE4M^)$xHNiDmiQn!XAxpL zGqm86(KqqnXM$7&+!SgzfZs`|;aUrY_07}+qi2E{8Q|!2p?(tc;M9OsF1H%}dT7JV z75xCiUY`In8ch%w`6kTHtn9!}G8_NuAeOVjRwV#8W{yNT&X*N7J=C8vG^@eg=1($U@=6`BOfQ3MCOj~9@$ z&bOOoJubH!bp30pWDT{PdSyFoOfi9EM>^Q>Ll4-V>3Mt=RwKDwQ~l!(RF%>YuRuin zs20;Rg89o$c(1m3!5wf3^l26m`7_39DtYw6XB+e&y-m@4TZK>%r-1PQhntm%YnD?k zBp_Gt(c>ox_((~O?OGGY+WRu}g~*oP_I6LP;^v<&B8h74(<4)dQ=Y$`Hh11m(Pu-# z{6a7$gCwCe|G}^yEkC(*7j-FNbU8l*QKH|HnLun#?{%zw#NcKiO56JeM9_blT)4q42bWy(&s5YD#j=F5w zzt@S-@8Jv19+mE^1qPGn`ud%eF?77$rQ?A~r(g&6y@0l-DOD4!r@^?}T=*NlX*HnQ z*>bz1lTT`i${COlwzloP@9C*6F9;yEjZ)}-GH$&$2%`>Y?91D+&KpHP11Zh~HQ607 zCfg7AB!zv8IM)GLT54h6G$M{K%!|Me2ZeT0Du-|Bb!l6!xsgM^n!2IFg+S9LH}lZln28OPs%>VWGB}up-7rHLRm~CFJdz>n-OQf+(%GSLr@9JX`EcAaqWZE! zKXGtKuZwU#%(SmGUW#hTcy>i#qpwXtG)CV{_ybM!K_JlGnU@jejQ=8MpSDUhOHg$y7@NLpgdGP7+ZCG(xsWfabDp7u&0)t@(HRP8; z^dbRAPKTFUvb}f3(jqnTz|PYxeKaKV?Pa>dWRRQcc6)rv>gND?QJe#IUxIJ0UdFM0 zccqO8vbN zsfa1ULmj%xMM`ATd3ac+N2@H%CJIEQsx$uOEv3h{M=kXR2UM%x``}MdLuLrPQfHC= zgc+J55UEMu-%`1cUp5Hc;CGY;W+N-c%zl?0&KJw)c|O@WI_768VosYyvfi9=Q#E6UZkA&Jw)RvhIQbM^`z`?Oqk8@WT228yhBW-mEKM%tO=!FTtV z%pu#%YRk4Ew+w5d?2!)kj}(4XbGCDGBoJZ|N|i&s(FoM@3kopSo@E93WtU#BVMA12 zN(%pTApDC7`LH5f@@ae*(63qhLf^0;y7fjhlt6|rTM&1EDdHMU!}MZ{kVvEuiF}qWfKX6Ys$8dlzb*PzR960^_UUdP@B_R*f^hmL9fw%Ez99{ zYNNqYhX>$LDSCJCDO&TJQNQ}oDtNlHnVerAPsa6O`hz64Q@WzEldw3#A9?m0v=iMV zvzwPUzlGeQOHN05yZ{n>=1~8LT^#AX3Ra*T%T-^5r9>Oqx?sH6td2UAA>@lZ4^uqB zMKp_{Gq`Pgz4Z zIq7%%Y%hgZ-Ngar5jwLeS+l1pIP}kJw5oQYm<(Lz1Ch~WbUZ%4D%+ccu4*GOM4!UE z{<%PFzgximiCcgYqx}YZI7cE&{-_mjjwtCBAnxcKU2T7}${X#@p%xQmEscBIr{j)^ zC&{+-A;!(|=|oX-Vr(%DA&qIkrHKtinhJNk%akEu# z`=Ex)?f!#Y36$eH8IIu&qKG`3@pWu~SJ?AYJV}`Pr5~;-M-nvqv@ki}wui0JG90pZq~pwVcG>Q3Z7^V;AiXHCB3Xj-mXEqng%m!`_>bbJKovR-~^d#>DySjg(VyH;> zo!*%;h-pk`dztRy9L@^z6i7s8CwMXCL8%AB^Tp^5I1fx2*KoV*+7e-hTFy+t^(1vv zQOtUm#;v!^N^g24Azoq@#AWe z{SBZt0*WQ!CQG>}hsk-muNuaFjU)Qmg+QA3V@E>%Rqy$S?r4IkyX`HyRSGZg!kbAO z=+@`LT)c*@(b|`h%MwA!2A-;bDR6{rRcMArK23M_FrDFxO^Xu4lO{x&<6;JT z#9P;B#xDaOE$1t8&3M-QnF>b7eLNJp6%!hYc%RXpz@71aK*TFQRcfr% zu)w!!yPEcmU371au11EDX(ZZ7Aa2_|@o(W3K**w#Gb zkF>)#(~%mFl>QrP=rFc_o=Tqf|DvDoEB-xj{GJkT_rX{QuYPPA|3{rEwfZj!g3LcafJ=8Ly74 zj<^IaYhj*uG3TTuRYrKK@Pfdb!DppxTR~g^==&3poZe$3c<&^1P5-&m1)fT`83n+6 zCT;=JEs|?s)2HI*dgDa2(up#6BoxM@)4OZ;;u zQ<}0YVdZA3?jJsA;M}GcAi?3DR+zO80%_gxJ$Y4%iA}YAyZ8F1CRC-Cz#ka%4WF#8 z%UmG;)bVR@mYOY%usbBYTv?Bd<04yfN9gk96VLVh+xIu-d;&y5&Ttub$kKt4bN9^8 zyI*S&iBQmVyr?CpC)PZ=AR@C=t(!+E-h(asX5wWoF2IF?EJ+!%;9Y2wS9%VbwMc*z zZ|-pmX}XRp#AYS(sKSd#j_+m3S*GOfd>7CK9nJ}-A+VB5qAF}e z*}tWFyu`f-wf@9l z@iw0-hOW{k?>eI_rohW2weAg0*!V1wd{Nw3)BVG#cfWv_n%XWGN~@$XYfjrVvAKp6+sY%7{Oy0BA~)8jc- z!_&Eh(&;`i{@-fh?>~jE*I8zIKNTzEmQ&OJ5EX_)C(Xtn5tzM3uc>Db2Rz0z_;OhV zdE5lJRJHytR^^j07s(*^lxzauV+?vWvZ7@>fHa(NR zoy+`JMU;|GsCj(`t1T-HE6$dl8{v^9&iHek^3d3yN)I56%3YO|9bcwxTHye_4>j?h zosLid_xAgzsZjYvQ1Jn?sy9B*371-{vR#1te0Sy17{f87`nxK_)38W5aWkd< z>7WJL`NDd{du;CLN^bohcZuLBIyG|%+|z@v?!K9wl=T#QC(jQ=>L{#v&l@7Mm1of` zF#7mZ9>%DVmg*5wW8T7shgUguFRIhUm)fT1O?W3d-#z#aw3)`|(uLfKic$hnt2<6@ zPd(&bV&{(I&|8c7>7@}t`Ap+gMPtWzx>Up93Q#nJ~qlc1$RJ@cn zrPo5eFK9v99(y-sfw?>gG0|c<9P`4VNUTyUc@76~NAeQQlkEFI=sfvTN?an%QsGFS>G<`nyB;p>oQCX2-z6BT134D5b9=({RBR@HdjS$qpxZThb6$b|P)SNUI#+l*b!& z!yWVKc{8~s%Ruv=Q-Uc!Ukc*Co9g>Q5G}-flz$&RV7=e1D5JWV1!;h#6qe>+1b;dx zQR@dT^AT|&h>OJ9BOT8cplPs{hJOn}*es7}FZpv_n_`+8TAH7|NHSlebMN zxJRM9>~lF+Y6njg3FxV8A{Ep_{Fe zs}V{{FJmqUDs`jfA1CLCH-Q~b*P@o!>L+fxyF-p!n070(oBY-didPa#Bqf&r69JyS zzcxHfuPFaThQgb(;<2wJIaSNao-DYYI{fh{^DnHlupJ(!5>>1`4Dnm5Sr=@q&QObO zf7SA?bp$MvNuQgoBKpi!j=TOM6i=3mWnU(=ii6Q`0sGDT7v)mbU`W*oW@c7JEMhSj zx368>Lg=)L5Coh~LS*U`C9D=?A0f z@T%LM{@jC7PJ_v`4SLqu@v-;>R9`n%Lm_Xmw)N!HBOFts>^4sAj3(*>pK*L)?53*p zt_CrdJ-NOF7yQb}w}v5KtqOU77|iv$rJ~5sqC9aQ41H=$L_ZJx%mUZztDO7mhKo4F zv$s8-sEfB|tm_pgNMU#RH%qPIovvB*v@Z%?F=5}_TLbE_x~7ibKkL^g&&)qmS~-Dc z5EPnyBu1qNJ|xqza+#JL5u_x~_;Lx{0*pGa5MjmJ7x#`>lC~v{C}bD>+K5JAEUhiH zRUTT<`l>eon{dH^q?iHhofJSKS`v*hOUsj}fTOU?jy$K8y|GQ#rc)N&@b^aq8k{1S z)K}MyGsL<-zFw=7;TR@ zFX>;{wnkg~)t`2wx3%UkUiH{4sC&-~{9mzL7$}SbG%&(`PQE0!o}LQ}OH1vamUNsY z9gG%3#(X;okWM%C9{3Ypyy-%nUKGin!Dy^b1*fR+-I-R$7jg_ahZ^gftuqeLy%Dnt z-ZS4xwmg1mt{?BJL#Da<69rX;JA9d&iMhc(V@B*-QxP!_LG}!Y*R$5fNtD2xR8vi% zn>-SEN|(n%S$53(4keomb)-)tH_Hs%#aExiviYa-;Ia8r{PAGt=2N4Zm!Nm9`G^J& zqq(D4N<$Vw6iimI|M&J@kc+q7{0t{o>?EvMxrp%GlBUHV4azSl&emi}V*Jetr@ef& z&*+dE!Bk1-P45q6r9$u#$Y$r+37HEi6j+-LX3lImy0Cs~`l;|acm;Xoi3xQ>ur;@A zb?;;h$yM7+MxiKjT_y&55iVTy zO^l9L+#ZY_s2?`Ha~rCzRX4|~jg`X6I+^U9?~}h6&Hzdp2r1|KO3PH3r_X?c^wB}U zmNzkdkmhZw(;`OJLjyrr@@=Qn1JXP5gY&hbt=DML@y?kV(M!5ibg(3A1G6A_cNngu zbeGIEm96Kmyx?`B6&M%DBVsv_1buoz6txVl_(Trc`9Pv4VSuW^l`K*SBa$inaeZq; z-f7uYDr26oK-x)-{bBGj^3&?wMm50_fF7S7oNdO6ai$>1RTDL(TYJQsijeG!g9e>f zYV^bS9g0l(o6ci(#ky_Z-ZuL%{BfT7MzHC=8rFb%wf9CAo6J~KQ~RY3*G;!obQ=3J z$c|!+xqo{^ak|>_0US4g-?Exl%ciCGO#aI*nxZ^k6}I`J&DWvUMlm-#tV%Yn?J2`p zN;UY;3I2kHlU$WGh8m_!(^SN!Nux)J`$=z5xb2S?jzO99u+B&|_M_5BNVw{aE^z)E zY}X7xB-4>iPqVS$r}+_7^moR=CL1CrhDPT$q9|OZ5}6E2!^l7N480!Q859#=Q=3XsiCcV7wmD zFt}Y4Pv5MGriSS*XqxkTeXWV`7Lz&%`00n1&I%Ebrm9>ua93I4W1>O+PFLWR3*F`( z$@9)(cjQ#d`LP&ER284^(;;K7x86^AoM)Die&BR7wRZOP^=;e3oFzOn9L014LPjxt zW?UnP3EE}XJsC{H<#b@z4)(at^?6Z}v@auoy57D)nD^1Ba)Vik$34`;0 zRWUV;Cz7>oF7k&L_y|6Da^^?*`BI+QS%ZmI4qedi>l$>7fyCimG)k7FYa@o}$#{5j zLpDl9pa|W5{+WELeYkicJsM^3T6C23eDbdo!P%s5{dB2}fg-X`LF*1MJ5pBx#4L&5 z_6#UE7{hkI#jz(G+>@Wy^oxip{#HV$IWA_vSK{~y;7QNqe#qf$vYDxAm@6sE!$`E8 zk4#@8$Un0t+`srpCuaZ9az{@ke&%`ODPT*SS;|uhU@t%AKTJ0tar9V1p6_Udb;4y$ ziQ2L)x?NL|+p-QYd)?)}`%Wz{h@BA@*9QQt-)$;&s*&RE*T%LBY*EO>@U(vfY^B@^ ztTYlzSbVq*-*)eK!rGxvuwtSmiBYZ`?ki5gxLssS0jBYbL62F-kJ5DR$6n!$G#^nK ztmabEYtaQ2qU#F_M>?`@#$+Wkjt9|KC5F=QNCyNGK3bl9*IK?mUy_to7-J03NS7j zFJaiatIQVk%2dClbnjDy$S;lel_U|{ByS5Hj8uO+)KheWBISOrcHEH*6@vGz2 za0_zFi&7BG)!eyP19T}I4Z}bQxg}iJ4nu5P@QtU^csw4v_bIdWy3fcDMcKcM)NnnJ zH1g$YunDx~@OZ#O)2&7ta}BIr6n2{8-PgZMsRL>)PjI7+M1CVOf`Ge{>Sv}APTJvn zm_=G`KTXtU3e5t1xWN&9y|a)g?zKFA49R1Yy;Y%4H$)eGyu(Rd$mo2I7GSglMB7=mY2iR*lSGiqFRLc z*4A;2A<byhrtbxr9p>ZTk*1=zPzYabxr&8Gh9oHX@MZpBvh+7UzDfsT z*~8Y?ls~>Sz=hJ~yr~>z&3c;|n8i=u+i{E^fLUbDC7SNj3hUx^J7zN3%g>}$z&w2# zORhe4%UVUmU;#l|HOLtK)bR>Hcn>FngzO#X*XGit--p5AmH8^K%` zF=*wd;te~(1A7F2cnLl-MfTJ`VJ7>`?p;5k6Py8>=Cl60j>wcwyV)6Ky zt~~B>L?&QWR~Acn-Txx;+I)g(V>Y?D9a3Lmv;e4ZEeM1js(PEb80-g%*n;UMb`<(_ z%gH$WKtb4BT92CKzvmpRbXvZ=wEejU!z0+d`P`P{>Yl@n_p)u`awNdrn?;soXELb! z7v!Pr$pzk2R}%R9pt2XMN2q=QSy`gO!a{TW>+5SV2?>E2dltE(D2UN~pcz#>itQH0 zr3<+JZ%s5Lq(r%X_C0=)SZpwxr8Ha6ji3}&&v+^0slA$B!K`q0QU-nZkNj(Xokzn1 z8#SMEhEq^lL`hz2j>62j5Kxv=5unT67kovi0~~m*b{SVh?iiE~y1nZxmTRYnZ?f!b zVKn%pBNFy(T(A;$!ibhCz#EfV%6F((DK*l-#=9H@#SisdJR(sW>wj-H{TX408$NkI zX4icC5=I6<8(u4l+W_u8>*D<#Dxp_8!uNt`Tl8NMb*GSl^Y|LQ*0gg=jl0_FPS3!w z(=sCLdhVq|aH$p}99UywJBCQDEdmdtDQ2 z_~jZ=b(e-DAk(S_Cr>0C7G>1!PqtiGZ?p!HUyD>9#T-hIvU}bp`cp+C2LWGb)*syc z^OYbHs?_nE&Q|T%jiD3)TQ;8D>yOyHLZj^ENw?gqDy31Y__aC;86essI|+k(nsc>Q z^tH*LT>dTwY_@u|QLH1t)JeT%v1haPM}ZUZgbRtMYv(#Q5pmocGCHj?YQx!e`&vG1 zhK>Hw6);eb=v<=-<_!5I*F9;*7Iy_=K|);r#D?H)r;xv%`ak+le?CIkPh*?t?*1LO z@7+(MIy`P+L&#iq_Mgs_F8ko%yJgv9zMaPlWjv)S)W~a$>1t_2(LBGFJn+ z!f88-%V37&k-}3@7&ko-KauywuBVl_K+EBg;qM|!BaPTxCA_BzGN9wp>;5TF-$#uL zk*%^yBvH4I)}>Lvck2PL%Z`bZ!*Rb<50g~0#WW_nggpF|5~D_D#jy_1rpHfzcAB_;drD{me2OYSMR$)`-{k1X#Gra&+b#qU%BL80 zBUsIa7(`pY5FGZL2v5BcgpZ7hrCxcfZVD&nU8das8$54~u&=7z63lq$5$+9pj^ zSETF5u9&D{-%p?1+0#Z9tjIs%E7ryRtkla}XR_MCBucQl{sHB+jByZtIJr;VteR6k zuo0eC?K&wD$i5^R-P0joq-1Pz(h>H+{lOjKp7GSuRIMaXqQK3Nj|J6%P%oo*^4g;2 zOa_P8irn+j+QdJ?Wq(yi{#MH6y*m738zcoE7@V9hxH@t77aB=yb@5>EU{ieUCZw~aOFtXeuLcKZ zF4HT0KbqjRZrrn-ijb!_6*I{m^C2J@%b72vtvpd4Dm5coERgzI1U@~cir~$YXh>u& zJs^ztho$$TU+=jaGeZOxDg|d+Ytn;m25Ejs)Qz`Z?fcxB=i1%>)u4X7merDQ7EPK0 z<~WSu4&R0u|1i?l{Vg5M-DgKRf?~fI5fBcae*5Q>8%VIv^on%Dsm9HqHZuFb2Smy8 zV$4nj2D~?#uHPCuK4lYz9XJd>lls%m+8W}^P`{wAMM36Swt=~JiBGD<8Nmtf3B1EU^-r#R#%=_ zxR2}J5=Q>DtD-Gd%_&Z=p)^_SyhOR^xf+pw^4R_K-;@0=yD@KJ zu;b(T6Z7qroVFibsEuE6rr@5FV*4@w;Ee8rx*F&5_hlc@E*VZy5i?BGkQ)a+u5)tmAmTGu)sDGe9BU)`C{hG;T*XKe?)XlaXT#psYMO^pfx%2+lk=X_woG)Fp0`6oHsMXT_+s7Wk%| zSjo+|IQc5_S+s5M~fx=%bO3dzq@16S?>sc75}r;iT_$^g?Oh9C@_fLO2UEDgO6>` zFlY{^vmlj)ux+eI92-YK_)()V~~sK$8UW>ektnt`{RR55#O_z@7fW zd$q<>F1W5WsEwCK#w)N#{4)MJtYM4GNRIPw?;5S(ZFCScoX!G6g^96CJOKsaRV^8S zs|OLG$E@yW%i6L6Pn{eu33%&8X=xMV`X)x!GtCX-AObwKuryw_U-1 zNhHy!c4DmsAE?{Fez||omK55)|I*)sB)lG;Dhv26^YrCt;k_af*58MZJeq~u{^eyoNWML0y*Sfv!onS;9;VfeUGxl)HujN;|S z_Q$3JcIhT^CdUY8xwAh90T39mk^fmx|9+GIe#n*d1)~0F*FYbx-3|Ol(R=T{I+lF4 zHF5;D2Aa&`9~+&YE5!fb2Ko0>yEUmlzYPsx`}rppH}f5I3yy9dZ$c|Pz77M-hy`0E zN|MWVMnhd7*A_2mYzHiAO)OT(3yI(8&o9wy(q)dozm;9Kbt2Mt}>sav`14a%1Q1P6p# zuU+q#KrR%%5+(k`(79dgZ0^I9P@*9D$l@41A#%-LQXlVT4h^R2i9o#Y|FhJ?C7)Z~ z{&cv@?~S!EP;cpZVcD4q^Ip?MD2)tbn~_GbwHebi5_E?AEw&8|+7eNFAAB@eW{)t} zghVP71e0Q#BW%0ui9Y;d(oRl zg$H`=RY1P=LN9{oC`jfoh5sWeAhXOb*wxi$*;ktLVRya(zWLY#@y9jk^+d+_bM+T z!FLpIZ&G90d%;dzB0aGsDn^(%zqLMr!fk0V3r)`D6^*_dQblHM1!`i$xSaZ_pmqDu zi?Kxz_JGfOUd&ivU)5!Ax{a~k&T4$fy?3+gTsrmoex25%)IY|0yrIZ6}z| zjzn!nP+waq+uUq7}W$Yey)6kH|GY$UsRcRMMRI1Xzal6>JY!jt& zEk~VtWH@~0w_NdnZjo3Lz8xeyG%A~F-bZ{^<3n?RPQ#S$DSz_W^URjH}@ zVz825{t=#8$5uRK3HN_H%J0a}j#6&-n}&{5k8gRPBAL;O?2jckIS6xZRId-?g7?1n zu_i#SqgwFKgd-eh8(e&m)Ib&|!lP*f!f~bvltwF$u`;)r^67Sy{A739Gxwq{bM6w% zN+TU~u97t#1w!m{jREPRGPujJ z?WY(a@wK&LI9+JfI{G_}1c(AUx}a*}yasP7E<*nwUEdgA z=eKR$*tXT!Mx%yJ8ryanHnz>iR%6??ZQHiroqNxD@45Z=e#nR9m;LO8`OG=ym}AXF z)`fGOC(&KFqv!PAy2a6EgcNVl3|ae=7WNh!q5LbY%);bRv<$`1w76%_)r=rR8_(6P zP%sl!#qk*R($K5U@jTKJ&mTTnUkThu@A+^RCP3J({3eWBiO1f3#XL0xIM=Y zve+?qn0R1R)QWTrE&MUhM-#73S!jlOU~5W>$eBS%6~7@tFb=By|QW2Obv15I+MmC;Cd+ZWjZ^<11j4_5fsWko;VL%YHJRs+D2 z6d|D?`FK&kLBhfdW>fOV9AnYONl|EIqiXzWxjb$msI*G}2T8AiP*a+{uBqd_N?LjU z;S2atJ9;yA7a4jT7eVj~3|)Drc~u(`Efag*Y=@nwfs%#1xbIEU2b3)y!Erf$;yt-3 zPBu%^+gZsW%`x#$lVe3STie9aoC9H~HzKt+jtYQVne~W(cF&(vS+2B&KKKbWg3dz5 z#c?Ce@FIk`#tJlEoei8HeLk*q^TrA=1($x4;>UBx0noT1Xs+9r>27^b4y6%MJXcuE zr|gbw$d;pgokE=3<@;ZRvzV+1?{}6Sp#JoBI}!lY#rbn(JxUAa^E!`z@cM{%xv+2; zJ|}9pcoX5?6uR5>sRGTyX9U!VxXyn4zCp>gFIf%MqcMW$ST3e9de+xloVaW41g`aI z{1vbc8r;wwoqfPiNM;MW^dVc1wH!a42qIjTH?}KnuOIpmkOqoe(nTK%pEG!xvui$; zHm7MumJNQ^YQnZ0XD!pNOx zxm{lTNM3RJKV1v_hief8e+oazo7fmS6HesvWWQoL3rF(hmhJfp_IahpYTeWKe6O@; zRR!+o7T0Jkr%3hDO@>ETJf4UY+oS((V_cO_f4cA!N1SB$h;%=t|71S&e5wj$eCK()?+KEiGOv3n}!_E$Be*S3H#`%hE#E@C2cc&1E5<(>6W6 z$obtTCuPge0t-lqBR3A!qfE!Ap;nr;G8oOs<%oc~TA}BBnZ+nmlFBxC<=x63-4`|( z%GuSoOha4uoYO*eRDy;HtMUanvIdg$+*(`J#KWVy3f@mUWh#U9ElWI zCJOZm5|XcRzmWrZKCAb#FsL_HbWN;P?0MlZ_g0F4bliu4K3iJqm#yH+xOqNJOw{d) zB`nuauCg;LsbE)&-aG7w{_~xaIvXDcM&Gu86@o=^vsasED--o>4e%Z*DAw-JeQ2D@|upc*b8pmyz9 zNSE}hhUK7<5xBvB3%r1l9%9g$Bwa%)HiAW*g)Z`&%FV{)w#bDHl7#s_#g&g_oZu9E zXISDVcX|Gaw)jY_S=*>>8hRbBT6jJg%wH}}+wFi=S|d_?up&dci1u<#xbo+`md%Y^ z#RRwX)!lDsW*2P?nz?YCSnNzREAqZqdqC?^)J!b-p4@Q0P`cVxfAJ*c8%$2+hXI>u zW5lb^;P`0To-=Yga+JckcfP$t)7}DUvYlZ|*J1Zz0jM_jJ7$+Q%F>f&1F)6z>?*$t zSOxHDbl=3`IjPCqmt;9qDR+?|<9ef@%zM*7HPsTI!k*T{BH@ThImJGc<$qg;^;!=T zsY3hzY1@Cpg(`nQ*!aO1W~ECGJGk{Sl$5PMdoQ#;?t$tmoq>VT(&}ITc$|H~*(w=8 zot~Wu%dyj@lCAelq_YU1Wh; zS>c1hl$^~?s#_bKM#K9qEgkMEg~+`nsfMAGF|^5Zr0VA~(c%p11~;eX`P*!V^t$tD=+4HSEY-4=Ud^$klZwX)Eo zT-n|<&aOU%HBjE`h_ z(iwp=hKz1pc7)>1Ttjv3S4I~Be^{5TAHceB8Aq(t$ipGq8}MWD>3S|PMQ3P|5auYw zzOjJeBBUIFk_Q@qYH){a=UWkqBoN>Qx(s%M@(RQWz(<0D0R_G@LYNH0YxsWO21?}6 zr2%K70H|O@LPPFvZxHf*0#K%Q+ zHz)8&dV;yrsuIX;ii_r}2Qq6t^KiP;;SOs!b;s6muYWcrXj#r zAj%NU@k+g>CoW1uV5H1YG%1g^Sf*GfsGL_uTt zdQ~&|43dI=l&fi|i|zOmXxd%={B>h`=6;tG4xBdI61U5dnVnzxH$3la3(CsRz6C#g~*n5ulbD{~H9hHmXxCa1*GF!6f8mr|W4?EFV0d^I$fe+)3)Szlq9#(m)bx zw!w8|k2b`s@KAR*xLgXJwNv52dP|9nDwW@~D@ZTGvlUeB+4^@Lf2SwpF#sOEP6R~U zTZB74YQm?;H2Sjs&!IgJDoA)Z_vIc&ZENqDzcsoVmCn4CM(u*URzPT9k;i&uv~8^W ziEQ=+3cf+|lJvn{qO#?+_KMdbZO7w}JC(D@qttfl-7b91E~?e&GL@BteROGUh)KM( z9Ur+Jpdilx3zobN41zzeM_i{_p!G>_C{J=jj}J$~8(JQCy&yDmBp|DM@PpQDl7|g@ zLPbJD|NK&}iPqLO-;6D%>orgp!gPU?JwUw}J4H9+oQtbX*Kh|(d$mv@YBs7!zD$2c2w~&NJ6}ON8K78hP zB%8_2t@K0pM|*Y(388+{px=HbHv3mll?Hu?lbL$;|rrd4{e=`%ik#KsqT= zCU)9>moEih;jH_#?!gT5{eIP(%nHwqSBY->WE~mobeF09x!OsO&Z;wZQ2uiHsoL6f z217fXAn+}mNe!IRIFmNvJ4e1@iyw3G(O^v=9rCi#uwisH`bLSNe%Ol{u!yG$RJ}BT zXvG|U+|kU}NewpJIr%*EmviVNfeI12%~Up5MmOj|($u`%r27QxfYc|D7-Q!g{?)bz zZ7WflG)}zr_bPCrJ9`F+yTRk_`>6X$!}`1c>=;Y^&EJC$XZgDgIYj|k+V1}Jd{+KG z4v}<|qd)Tfh`{O*%KK{wl!?c3C`CNx5NbnmGi2l2$naj~M*Ct5t1I60KCh_3cJ*B9 zPgYwlO=$~u@!&3aehQ3mB_USwu2Q7RaRLGZ2015U?DhKHBlH^+aKgTrD0!ybSML!w zE7q43P~!3%Ak^S?1kaaxB;=+8n{$|(C}#DlMJbxG@N%&)%84p&O%-;)CzsCX9IXH~ zVNo*)f0W750%0jp#-4*>x7is$O1+MZEl%F8lK;X!Q=*9`9QT!@<<07))dNB5Ij}oN z1cb%89_eaPy)(;1n596QKvBG*Yv}3{EAjAo{QQrQBD2v5%7j%@i3wIE#81bAQXff& z{`!Rt1%&*StSm_?)lGbKDd@Z2-Fs880GpC+wIlhYSqJ-7&)9*e5wvNJTW@5yYY=hW zTvxUb45M4;SETsgXpz!gj7KQFm;NNeDRPJKNZ+D%&(`?>^s2Zc{^J1RHu;{%9^(8EQj-V+8vNTi4dxfBGcfs@NFNnA@2fnAo~>O*IC6E1`>6eoy<%@PrC zXFo=WG%q$889g`Y)@otLY{fX#PlI9*64jl1b-&e*G?Vq}& z#8Te(hHd`FgI5Zd+JR zp;BI-*uGu$P0BHx?krxV)gb=jhny;^jRO)|@eF$D7f1A05h>K`gcn*P34Ag$Wm^ox zlsokH)2Ms-6r*u0f3By9h1L_-$-PywZ1GV9zxf+~y?gIgy~Rj*q5P#dp>49XUILtm z&1{srw6$Vl@rI@lCbZ+wh*g*1HuvX{&2?JD=NEw;*pd8KB}1jS-9Krj z4R0^gNW3t&rUgf5QTFTBXlseIj0FHk$` z+tkr{ph@1dxzJ1+j9zB}UA8TLt_+2EsJGU4KTo?V=f^b89{_mqwweUHJ_(wEtd&uN zHfP(Yzm3q2>RKKU>J{S|D(zwy^L2m<36nly%g|}p+-Vw<8vczzj%C=Gga(Ect$Y6z zMK@SYm!ZJ_{77{xCp{xHshBPZ)D-{JQ%2%^T^u#e+o(5Swbu$~JoKVr#)d6Cf$~H= z0uH@7K?-BkEDn|JaYgw4^1A2I)n?ogIy%)+pQ!@O`|9@|QOV+INLEhrsoh2OWqC=$2lnGOOMc$O^h0-R3u-N8jxEm10$1^Zb#iST%l1u;!h2 z<3aa80?PU>V&H`7bH3VZ-)Qr!{a(`9rcPCJvIGLsT*qI^1fV(ejMp+>bzcQS7Y4ow z2?=dnwJAQhZ_1rC<3jsVgx|UI^>_iDced($MIA_hXvE#QYB)$yX|w~{d#Qw07AC4W zzeVDG&cqr*bMPM$2wa#@=cshYdQ)k_7Jc!}nnmL;rUSXS?8lRt&7rv3ABFouJxo(f z&Rk4qkHq_Mw`_IU?IUS2aSaoOyM!ppsB)cjzMv1;`{O095c@-5H-Myh&i>MG{@KB-}pxyqCrA|klHR75!;c2bR zQlN!9X6u~C#)#dPf47mpj-j8S{h1rvvAckXV9VEWMfVGAJaB-(-UJ0e3w> zc$j@#Mds@b76YQ-Gn=nyG_ZA*>y41~G}HLO0ELjS@aEcK+Jj*y@(-SE1ot!X16FH9 zaC=KHpEBo-qa&C1r5ayyVrtlq0{Yg8Y+Iy-#6Hqp_&JG;h{NTsNxg%2h9XzZ2DM(D z7&oiF627dmB}`2h2jSZ-=iSCC^orPG29k1u-lN%L?~wF|m(b0E^K@mpS{5E!vlZ?( ze9u>IjA8nM==D{>H9)rJ+WB&ZEz)k?a{lFX9jwSp0&=M9{6v z<+rhEh1{O?M{UKM1`)umfCZIo9Rdd|q%5@Re_=aYK*%s>M?{Z?Op`+JO`{%~Jr5e^ zJM&kn-Ay+<>_!o!kvd-rPIJBMKJPMofB+?#>$1Bd(#fd*Bpy!}Dz}Th`KmNJky!+b zu~Q56)kCG;fppGtDcE&Mqu@pgg{4FR_bvo=<&rZzbpD)>_=xUI2!fbn36i{D4$zN* zOwHlMZ*DXt(*R%n+Cv`zxu+FBMI(oxefQ`t$%yZ$GJ&fp(Z)|G*Yed&%EO#&|5AkN zAmK>7Q*HKq&L<6j-8)1I!*E_M30Y4Q5O1NL<>L-2<*F@uDYD8?p=^w`^&49N#lIS7 zPOtYUoZ|omX#bbuM1!Fu>Y#=1?~9a$x>F8I;Uk%%!{>v^Y8Y;Fr;bbQ{5;-P=}T{n z3deJ)lch$4wKioWR4YHmUTk6UIN|Md_la6}Y6WU4X%@-);8dbb_`B0JUl36#l8$AF z1gnAlA-i9B6Wz5crm^J;NW123p?i`knP#%nZ>C%tDn$3ADO@E?(7^C z*(_OgyQ3Q*D%3Gjkp5@W1R@}e`tgL#Ww#%kXut_ar;I;tFx?iisceT|NAX8{m>?%K z6efO~5YZ@9R4VdJo#%hQ`k&JNzo4R1JRqSmhPu2?K%eMe?FoQOskq+4is;_Q)Ttkz&zp}{tHvbs5bE`J)h zkF))GH+1ft?Q5(aJEWfsmaw#1Qnat?bm{0`;qmHn!fAswh|Qy5c$D*fUdlu{_aMi6 ztD4G%Gy|pw{7GlKPjbQYiRqUd3xgVkY>^a<-CHJ4(eR=|p5zTdn;*DtJ^gk^-YJL6 z7=kXP*+A>Wj>m(ziYkWaXu#;kp(+GESVqm{dv`L^3)$Ts)Q^=KFi|?v&aqA)DXA(I zH|{MIWF75Oo|*)GwdRE!pk&@Rhofqz2jY_xiTQ!iyV4R}uu``?VkFXX+KYi&Q=sIh zw5&ZW6FBa(aqMP-OvwbGrRIAO7Gr%xj}%Uj_4gXEU7ly1izwa#l?fo*tE5LmqWl0X z7PF2apvLQ6-x%1Y5rZyB3prRfgp!W)@4K+0DD0UDf#57>Z%E*GKYWky!E}nNBrvjh zASc+m8W&~U7>giQEV6Cx>=?ovz+`IGbQe_f={?(lUPAwo(S7p*T7s#(%x2f@D3B+S z9lH~g8^+>@gX8^*L5Yeg2P-p@llBZ-H91KdxS2D8$ zS1BqnBuLXSHh*QPkWhYtdr}&6Vp-Gf;TP9N}}VuHQlYrgGLM8jUMKTn;JotJF>P()1cyd zC0Jm=r2(k{qA;|9sy0gZX7rSV#Z!ZDPD^JZvx;I$`9qwrVF7TbcWhzE%ijQ$jx7*T z_uNCnx6}t?aF5h?a+cXAor#G}6Q#xm4a*`?jR(V*>uv6rx+~5UP(XEw~91LYtETJt*W!|C+Yb|eMx9dJ=5WHj125PzO58Qa#VXBfv^-O`s>~FWVsRr2O+gbb)BJQ zej6nWml0AigoEg02Ka^H7@~v+AI$hZq(HVAjjPT)Jivaqvv&@;bAL{6lD@j~Iv#{- z#sZez;*;g!)#j>{`{ZHTFYB6LDP@u+$#o0g)vNv06sxPd_e_owH};fOi!}X zqMZs%cNTZ4b?jiGQOaVwJP;!p`J;QTl-x!+$7d`tNN@Hi%l>$dB5fQ95Kk)Ym)Rax zLj2;wfr5j_3g$N4ADtz4;;M&Hn>aLhE78;)a65Kg2PdG|S$^z7jn}ctGR&v6akDk^ z(4HXtFHS#d6$~@e@E*zNor2$coX60Iucqq4`6d;;^ozTw|&C4c;f{Ki&J31HxVh-3asewPLc z7)bu4f%c!N$-f9-4S*;mKBh|GZy)COKKT%OdFIg1vVMwu{S88E{U?_+3*+UqN7{NxIKanqK&J>`ilfT%ih-Ll3V}NKrH6QM?n}*nP7FRqp;m=U<&5ed z+Nu~plpNKWM8*jeo%OZh*b~IDI~%EdvYSGX)nAbgl~M~kj>?&^y_h5xcCzYNzt7_j z#t5*n=@bA3S1`-Vh9uI2@RRQD6q-u=F)Z=+Hd02cUr!Wb1N&*Xu6}=}VeX4*XZ*y* zUFE9E)3U@%>piGtXm-JJ4ZR4$nXl2Uc!yLW|I?#cSAnR83L#x8X9P(g{ASNtcLORa zLikq*Nj)0m@J882OJ>*?JwmR!f=Gd}!|o<_;h@tG^ZH|jHri1E?pFJ#>Wl_yqF?1c#rrgkJjD8BtlW>pJ8=pjj^$JS?wS6v`Fvl@m(bs3Nh z_ggdo5BrtKb(aJvnTrhZnVqKZo{zpWI@EEugw0XGYyy>3{;Xra2gpg3RIqGM==7}n zVwQuxrYMS@wq*F%S|##7cI^|`njOFtt)3t&-4R___4Kn5t#vW>UT&zB&YC`@t$9`5 zgVc;2g0NUnC`l&y6joz&(hHN^^E037E8z(mc(EBNsG~Z6=4U3{xL8koIdxQV*x<73 zb91Uc)(OK|CIGW4?+iO!*FQ~SFbRits$ulE*E*1G{-&nVU->7(2iO#$C_q#+jMSaV z!&Ml`5g8oQ)`RvOXOfCS0^I_$K;ikr)noWu7YQV zM!`(CYohZ~crvRz<2mFGN8J^DEh|5f=mvMw`=(AA-i|LZr)$Yhb|mU@aM<*Q*W@9Y zQTbo*c{Ug8*r2&od8$Bf>{Bk@{%~#|LwdOfIFUEkZT5Tkt5~sVu^0i5LB1%L*j)dY z?;eBGWYbo48B0*|i_hyk4pC_gtvdD+{qZ}=omoi7Z{VhYja z@tQ7vj>At#%nw~rLRAKZg!qUo5Y`t{r|ycSg+N%uW~4lqz;qUg#W1?x={+O^vqaYI zeL5XJW8KWG^7Cg9AeVk+p`yu>E7W$|_2&uRv%XI!rxv=oazlv6Vze=xDV?9vlzd}jXPyss#(|+J z6z3{x>jSAeF{bmRx>NZa1&W7Fpz?vR7u!&4v0dbGc6m)*R3T#PtcP|%A#maf#k^$6 zpHqSDr+9umT9O&PSK)f7Va3M%@ZQW3-SAAG)uER@uBoc@O-rLykhZ*u;p)EslePpn z`vH*;M?a}!ltR^68_@FOU84>&r%f_Z8fA-RNoES=5E&f}(h|yv2|3WAm||ld z<4UH%#~Qz9%GF{Q$Q5GReo=X+7DBPjOVEn(D~KX|&}@T!PpL=3!lC!&!pcykGK?~W z>30*}+(Qiwr+)9d3Mbzksepw?Vj$ZXI-KTkA48C(G&5o4YMhaWi7tl4Zk@-xrAG6L zg9Qx_?&NSjqM>_w+B%slU9jI^@sZTkOusT&YeGv_qzA|G2X_9h^z}4(R+cz&m85!} zX?uj}FXn4N@Db#UX)+d3#>}IGBqx$Um@+>M0i?X z*)sUG;@~Q(fv(*2pMKGuzzWIq2-U*myc1+8Ru-R&V=IPZ;{G0i&|iCEPA&>^WMQu$-_4cvu+L5H6ygdq{ZvC2zrgsf`IF zf|Xz7%Gkn4fIfQ~;EJ|N$&=Wl%e4a6d6F_cj7VRUUc&r6t@=tC!0$X>Xgl~1@%!Z{iZrc3R>Sk5PK(k;|7-E<*7gFfn>m!&P#cwo4d zLl8=h@iOmfzC%yIUS4oE5va3Sb&st=Z2o*iM>d!u6JO=S%~Y5C>1Ff{va2vBfAi-q zQ^e^~MB1}7k+@U>iWJ&pk*0Y~i864V09^4)u57h~rUJzlY_BgkU)~VG-`+JKU|VMU zHd`+*Sqyr*ABp6=1@dwfXy{CyazWYlE+p`sU%j_vfK<2m_n9$*Qy5JRB}fIw2W|1DhMWgF%*C2IhNEyl&yBpO zD9}^*(qYXd3A8rxhz41-9SYRh>7%$9<+fPm2Hv~tdF8>xcPMwf=uztaG?+Rw~9>nn?gl_W8mIv#ejIFLySK`ks3Vwcmzdif| zf;XrkdM|)1{NrGkC74^)5b&uZ!1_WUP+IX3CPLWWWM~##B@3p<4Pwe76I{73(}mD~}AjoA2ye>4W7UE}-(5ZPt}^bNy6!*)c{ z6R;c=kTLB^-iCozgOab3jVihW>`A7HscuTk}7(N1bp5nH^+}r;VJ771ytJyR{ zECGZA)716|HT!h;7r-8ZfAKL=0PGo0BBB}j{n&UT1yDhp`~uJQQzahf%uqZ>VX&ow zEmu$4Ww4~wEL7^Fd(Y5*HM`YY#vaKuPY1G#hmY3S&D$byoeqfyoC#=lyLs)iE za1&{4zL>akDgy@0a|Qd zB13@n`dS=VD{tAbHwdR5*TqeJqT0FO88p(Qy|bJz7KeVAsjK) zlJDG@F*ax}@mfkZl{>jvRU=Dt_%m1UPGNWcLqz=n47`E4g$M&-o9M>0U)T->H>3NN z2GCM$b&bAGZgn5(z0b;A@4eqwGgb#onjkjCn%LMJ6El{`8#A6PxA*u*eRdAwH!GZ& zHaCn?Jl*sL&D}jre#eDDwB@T2Wg(zf7KvAnZ_^0j2K%`uBU_zWDw8MWgA(qFfYSUT z;3tv}n|#Lv-AX5`vCJcVW4T4A5Rbms4JUD%bVhAlo{Prno)3p?zDt=pi=W!1nq7+7 z{@@27xy*Uxt}boQs#bo%4)Lgc@%-lj2f(QoC~K(w<8vt&o2F?=vrwOTKM`rIH6mQo4Pyuu*DCuW=)aojJip7#cNIw=vB+i@f;V1 zZK=M4oTdM=NWKJ#KWhnrT+Ab$UF#-)$B9TU#mV%lZhf2p_GQD;tsmDro?29C&|`P= zxnUTBEbVc1XB5Y`oXF9oG zbvH+n{X*QIq zSj6M4zuNBDuRw-)$H_CsQcWVJYc^*KE*A})gKB7fA(%FP3*xEY)-~0K`2PL-fSAFR zA^$HfDAME`|AbVeyNF%Nk<>>XCHw{`|2RoAPI*c!BT`b*&Q?=xPJN5d+{GA-{Y6DA;tiwzL)O>bnU2uu9!NgwoHK9&Fc}1EBT4kSq08lzskfe)qvk_77H{> zk1#JUDOPmEFHSRF=g&42Dt7cq)r~XVwgr4<#ig$A#?Tkp?j|c+c$|hEd2vUx%p0)H z^GJjqn)hW>GUMr3N!W|K`Z}m;f>8Ph_Yc~Dko<$q49=kgDfcVT(YWRfbr+S|yc%`2 zRZ8frAxKZP$n`R`WLI$A*Q_zsz`^&|%xWSa>|ML->yu2eW+cRR7^ADGstbry0#-8t zehQ^3@5gn`SCHmHEODKOvqr=Gpq^J+5=D_5Sy_Rgj5IU zEs+HM(ei5zSm(iYOKA_Z~e@EB6~-Ky^#n4|mNY8`gYSybm)zNxxO%d6OL z5Nvwab3Y;3mO44>(F{;_-R&oY`-sMtK+fx|0_VrZ&Ja zK!T3pw++zS{xg1h0doohIL|RmvC}8auGt3$8CC}q_d8>SZy#)Jc5vwuu2)sS`{cr2b3oz~R{Wy~E zFs;~?wyK65{+fx`)ATBh!|G?!M*NGx5qA@i)&?6;`=i%wE;jIL1yMFm~aFxl0))e7Fqct94nf&@7_;|ruO%U zt#1VSt7lCfRkQGaZ?XS`Lhk9Cp^O`#WXvCfAgceOLeDai;bjQp*@e(fBg${Wje9lU zyFuTtW8F?RUmmJs3UJb16LAg%t2}@ zu8$cXQwkq`7T0tC?g*+A1n9agV9n4fg&*Q_7Akb9rL^D#y+hz8+kDB*5{3|Fzn?eX z88!Svk^ZrwEsGz{WB*}5o;>{O`vCnTgj{4?O_b*#Jr=xSjc{Az$txS*%bLR4&FzId zz^-bj2wq=QHQ8pLH-=}(^JwTc=?%Ezt0gkh=7}{OZ8^sVT=N)rAYVbrNT~_ zVc7WP)-Yh(*)A3!#Z$$tSdCukVvL-!480|rGqze-mM%&YEx-Jp7i@mMXHSUPl`oI3 z1)Thm@W?wFsgo`|7Q5eg{>$rcfB;yaa(!GwCjANXtax$>vj64<84|CUv0y3VlqxD~ z1ATpYepr;~m=-!=hIJN*+KMyHP^w`Xm>4i%W+rHht3&MA+WOUw#MvdIs%rmQp_d?G zU&XK;XCPHzW2>mI4@|2?i>+!4(`Fipu|M1iPN2NP9~`8d7|bw)lPN0Er7l+{eb76a zF-?5lmuehgGB?URl#{SfBzbsI8hPT_y?&lk}PFLRA>75Cvz0mui&Fmf%zcI0l)a6dId>pgm;aoy%yVE4#7bi)I%zZ=%g_5|e|rRoc+^{#%;{+&V{`}7&~ z`FYz&2hldJB*e(#Dcd}M&PfzpG;c!$!vp_k-dLyfcLzc$F>nnRtf;{R(a^Ss23MG! zVBW~~`cxt75-)WRQ4@XOjdM>NT^p_j#e&FmL6Pirh*24;_U>*5u`)#=TQ`ofG`?`( zW=Fl=c+HEub_ipv#JCGC}hx5fP-IAvw!g&~jQ@UsKn2YFbm8Q@1+c|^BU;E0oTZ&vEMjl^+4 z8(d0wM@Pp7j)jg)F&3P)_y7mt;T+xKv4>1|9F_t_c;~bq2JO4Hlh@`97Gn}Li<^PJ zIZj_)G~7g=m7q}PAk$IUcOMy=LP-Y10_$U8dJsg=Y9BbkBfEf2xAxxd)1H?M2eGf0|Z znMWb&f>pob7S@^J8cGsbKi`X z6e0=Re{#5sf$Q}n=F>cxC>soPLtPOU_*8sW}6kCas{IBK+d@mMfE(WL9V zQzuV+S%$!dzv9CK5xKcSWTnxHM-9^1MWIbb@8^#_gsdn_9SN@-Ie=y3jkLF0JI3m| zQC!}q`|bFuqSY$l(6Wl~FKElE-fKl7tz4NwP{qUgE_l`rn%ia%VEh1!LG|)Xv}x@y#+i_ROi02xsHP~Km~$&cg`*~rqFfFd>m91u4PWoz zM!1WQNwto3HO*djYr%&yTX7IrpzI;`b{7fGRT6#KUC^JbR3IAFS|RLgT~4LYc9E*UJD5a1 z?bR(b=!`B#&k7Nqe@Yw^>xfV20ox0N4I_)}!3{VO0%*g#A!?=aeTXg(&ah5Ds##;O zR*naTM>M4=Q8b+jfDitrP+!LcA!bXJLNU4LskeE#Oe{Qidyxwhy$haCt*qq6+)~l`(ez1@w#@QP^Tr92Um(_mONM62Q@(&1vKG1Q9ErrG-F02&ZnQKQ z^X`}z>CW?R447`H1ZEo{+)GDCM<2t%v3_BpY_-&{vUzW*(xIfdtf{HYdsTDc8%6eS zJAe}Qd}IPL2>Q(9EN#6Cmf33M*>haO^PG8~o19IRdxugCIDN{8vQ;dlJXj76Mu`p=&I>!cr7&*uzd zKb%-4>uJ#|x&fO_B{n`E(6CpkBv9s1KYAqzmswF| z4*DF7WEal(2@6`X6=7S2ZeDW~(*%JH+#DK<`Nn~@{wB!3%>ky|!ZglO9a)mNd#fxI zFd&98VLJq6xi#CXc^J?S`8DuB$OL`sEPY?DnaP}B`71~CQ)mo9202#v1L^-cQ%0ao z@4g|~H$K#hmfJ7!blTx0j;oze@@_*@KdjgBG`Z$|ve5HANuBq9B4Z85NZw+6DP*aq`Uiyao*Su<#`5O@wc$pwjPkL0O_=>a~}jw8&vBTl49*0$}G% zG`Pg9hiNhu&spPAh!=EpwwbgSP>H%BdkFqI4Zt5SS`i;2s*m4<<>jOLOVZW@V?-M! z_7=Mf6vRQmENdoGa8S6fBE)YMhc&vA5=aXR=lQvjK>xF)@qgUp{q73f1#*g`*>%CL zY&upk_q#Z4=2Y&`n@x99);3Z?;&x|<0~M?{@G<&&n%v-hDW+?y0me9_=oU(s=9VL4 zfo`ooB3|l9%->FqdqSc?xAQIx4n__kkMNzm-_-V;g`mC#HI*s!C&6RepwHn+QPNCJ zg=Gbirr4Osc*_ljRq`=Oh!Q8W-q7wn!>b%*)@R`G(XGNFzyzjLURsobq{X7cqH@a z5RPh9W@uGCw$ccHjd_@YI_py9tA#OEw1l@#j&7i3!N8B~%RY%Oi`spwk(H-*twU6h&0JopCEgDK@-m~9N zmXsMO2q=G#6E;*RB^_O}3rpn&rlex?Mn*xQWF(5n?gAdz-Gox($mCI^kckgaw8JXfP=TyS<WAC-Dj+MPG5~)Co8tV*3{e^WpAqRlUv9T` zP$3j=D^sHc%n(t}_h6p(v)*uaOUQU}*}f9erkjIEHn`%P*h*dlo6d_{m%x5y`i(w! zc9Yl>Z>%{$XLD^l)Y!nzH-DbgHIB{bQ!I^!JH)q@T=_zy+JiY9o+bo=;=OR$`Z}l= zJiHB)cTjez2WTdt;ur9C}q>D{F{B%CQP-V;RMZRbpc#3gI%p^9Udac%pd`D##D;bR45Y!Dah$MA{ z0=CgR&&$qpo%0PZ@_Va}yAfw@yDJEDje^OdFC(rF!n;gpvc+2NiHfwrz}U7AZQR~` zcV~SB7-^emm{6@(x08*oo;N0{IAp@N{L#7>wr!C)`~Q!xcMh)f+q#9jJ9Y;hvtxH` z8#}gb+qQOW8=dUfwv7%tHam9mrBB`a-gAESp6{>JPVK5^?X}jNb39{A&~4=fV!)y? z3M66kFxXs~!83y*dpWVip*?^YAl%ERTk6ajo=8ii%!f=^Ocm?k_5I&7`5FlGWvWQd z6Qd4nG7a@r#mkd|?L_}IcryKk2|E>1FnMn7PiQjPEm0-BI(D}=0^-fsqbgMqqs76< z7~2u|om z1tzO{Z$V_$s~zYkuCEYHZqA2fw&W>e%B!TMjhcS$bK~-&x^NNA{J*kz!@Fd4dmUX} z-5$XC_00m(oyX;hbFn|_&xyawBNV}s5OxpYbmqZO&Co$;SFR`M8L|*`oTPiX+>dUm zmQ*_|XyCT(K{{J&FL)_2N#a}nl;MfWFpPFb<;)lU3_!0deAu)@B7f%%&H&zO$Yn>k!cq!OjOUz$iT>vl+(*3uap4J6cWJcoSXUmr>S)j!MqizXr@U&1!t~YTLMSsG9_?q zN<-!7*I5XEIQ*KVUog)kreROD(cX&LawC(*Vns}R@NJsqidsH*_TMD0>ia0a zY#)6y%WIAVne=`Jk`u>bJOOUh|K@<-Q9kbI>zr!FZmdKh4LT_dzr}I-^vq01vWbGp zG9LpsD~usg_y8;V?I&k^AYy$zb59m{EJaeaTQQ8y6_&sH-+OTXb7h^De^*~c4@%AaZ_fDR@!d-(U%#v@p`bm({a^I>zbw#e>F?^b zb#)jx{{OZDL`n!=4J`mbHB`n&qJ&UOQb|K&;lvm?4v{Lp5)Qh#*o_ymYl z{oJ^ZkFn(xpO@fIb$Pyihd_fZ9_*f9SRkt2JJ>^Ecp`NYiSmJIux*glkgm9Ft)N4X z)e4ZE0%Wzo*tLL@v;OBDdw}}827;g*(-IS8@#L?8gkAh37TDdt3fbZ7)rH6EP`$dK z;s=6z*a+bnX@wGLbih4U(gnT7fn&4=H<4wd+r3*Dn2=>5pCd4+zj$lY63u%tOmMHs zW6gy#K;I#W26%i%qm{>naPEJKh)czoErHpnP%QcQ`kAQc56x2 z$kvZemi7J7ZtWnRH(pOLTfvB2!;+7>1`rJ_`gBlp(pVid1XNbo45%B6U=w=vnr-Vt zL{qU|%{70$q9_DqJw3if2t{glrG64X$3bjLacTD1!7aP*SlPgQ*Wlq2Vo-S$Y z=)PTJsoLAM+eBsnGxg;PzG<-82EdZS3UNWKW9VYk6#V0=#rl3+oMd0CuAVrUs|V<2 z-qr{noco=Y9Q>!r%$PhdjRGTq%3XM7tnXi5H+OLS!kXDhNcxEw8L>r1RRYz|Rwzad zW_%xYWU#mVyWZD;(OhS8?G~oe8GO){IJIHLq@#M*WPTqhq*^t3J|6uTu z!rGIiLmy88C2B(dPq(Fq*!?NyBj4ta=MC2E5)A6Cho-FxM3T+qI zfz7=IMRxb6@25Pk$e6g$vKNH)_?)P4*82sW;h#5#_vSvm%OYlA9)}ckU33@!o|0=U zfY?_@6ogYWJPeD^2XzK~7wy8%JN^{^11Xce9wWnbwWnF#c0AM6pH*2(5E!g;`OpY1 z9_ROIX!Tc{4ICY3f5BMu5hl-1u2GzmqRJvPxFO90yOp~y*&XkGew&1D?oj!Za3U%y z*E5A)$Y$?L7&;`~JC_ot+V9k-f>AS;FVbpGwqOapN9;bUQq+@V|8RM`;vZJavHghA zHS)&A;6M3bc`a$hb>Y`4w6FKpOjN4g>o@Udj?&=DjD4+sU>Q#Ja&yM<7gwiht zfbP!Co%1jIeJrXp+1^w8KKUl;t*A7`2u$`34&XHgX!9-5)a~dn!0(|KlX(J`RRvXk zOxXSRTaWvTrVMRl|21l8waY6jc%pf@gd@#Nfx0m!WykItts&aZKfQ4LUq|!KjD{Aj z@8;ydree(vJ0;oiP;3QDbDXsQGsNnA)Trkp;R8mcY9rG0`0U5?kVtjg+nir-c+2S(l;(nbJjYX+V1#fo%F`Q-IAGPGt86rb7f$Xs;|h_=^1J)-(v%E08w>j)n=`o5d|dl&wXxJOU&VF|Y{j8;u^I)KN&hk9o{*s+x<(ixtQ zu92QE@vr38*1GV92t@Laa%V7qRYAi4ay;wG`c}yQ@Qm}T(*gl;L%!Q52zqYTLx%2pL)TMW=(t zhux#-HMU6|zE)~8=SdltFB*NaX15R{Vn9*Yb&i#MIhvnUpD&c*t6lL<_+p6iew_mr zOcIZeTb;LVB0AsxZp8UEowZGRWd%ySik@CdF?$_(Ll-&!NBvP6i8pfpdKQ6 z(QT)UbSmP38;g|5%H;@I^@FT+w$+r z{TrRDm+jY_*;a{trB*rj^kyTB4y4GE%nKHhT_+D>b!J8)KqU(EiV1Z~h>t}MeNY(SY>=!+N_Zp%((ol&o+v7R_kzP5+?$Sgbp6` zCp=g03qjo}^~Q}5$}}o*gTJi4o4yar+NI29T(6odQ7g{QQ3~{#fQX^^*2zsL&fv)7 z>l;lP&Z!m_e4ksT(8TDGY;t=R#^i1hiOp6?#Ui3oe2Cy`GX5hF_z}kPfeXVNuuUM6 z43mb30cC@tCR$h;8uAJ%zyRliq7EE|C@>@_l)Kzy2mo5M>t~I0Y%8Oj&R7w|3n-|M z_L$h2_n^o}tq6`HT^_Xg(H?m*>;O2Ngx9J06@zuzNWGCa(_p5D97;gnUhE}O4Hu9s zM^%U?)X-#G6n5_cH7=DRI4Zg^X>ey{g>!mlzm}dgA%%pDFz%puGanZLkllnh1qggy znw6!UV|b6_Tdq6YPBvC+aY3r*1Oy_aXgQbbzxW=%Ct_UQk*X5;8YHsE>@pVBcq@3B z+r4(tz@PzY{462XD|-D-r1cvTMh^n9Q!Wo zihBJw9MxD$5Jd=e^uUR^gvdacf1u))zj0lQf3yOC79RJ<>lPMq(gu1Dr~iuxK(10o zTZzgztp6)Pb94JpXt$l+5$rrHn&_kCf_$5WI~r|i#v+6)w*Seq8oXu`&Sf;2{%1ID zo(aiP;pWr9v~AzUnAfS@Q{TItZ3xVbR+NvaVlYM|K(T2k`sNRiAYVa9rBoipIM7n3 zMR#70b_FgB{?z?=tLgRxDfB%-D6cA%G07rV2EdC1=LX8k+S)=50#`v?K!_-?sQefM z(*hp_`?BhlWMtP-izTI6HDK*-sz$2^YOZ3*OG)5Q!+Y?3a8_<#UhUgz@N9Lf=({vY zq+9RJou`t^m2+rM+Hs?fMYq*u43%bRjWw(vQXTrBn7E;JY|j56N%`rUE2gF=i*9LQ zANtxd9J@G@M{=Clu{2^8Pc#e`W;;LBC5BFbUrF&BA~RDxfZ^(J^qvWQjXZ0mknd!) zSSlY6=!AdEw!K{M!_l8Xp%6z99T>~Cn9wMdYsI`tGck z|3F252#Eh;LFXQci0-8(JG+I8=4-OA499QKlG7qtHhnW~66*b}yuHyS7hj8}{j0uX z+j{5gX%!(40`dH9DzWoZKf7A*w26rX!8N|M!&|y)izdLRs5R?xVka^V&m8j3AK@m` zXomBY=;kgAx2V;3!&*~)n~YUX!rX^l?Sr!0yf_J)rwk_AAvX+j7eiBAPdu<&lJr8w zIKBPiN|)0Uo-qj>v*Z^i`bBHwp*zg3@T9e?zb@`@m%bUzp#||HP zx!;J7%#Rd>#fp9Skie-F%p&1tou+{|9w@s5Z4yam>;RaZkbEJQ9ocm~UmyeamP2rz zvx@!@U~(~y0f{xCHD6lC*g`)U|Q2k*qt}fj4H@uzktC>PnNk->59P8BR|Y5lqVCf!R6U~9-A?3AS=ymbONVY z!nP4u&6c9Rs}-$Hj$h!z_g33C2#aTt%1^FcoJ+EXaPZC<6gUL>E%# zTFoD$ZcrFY*uRQ@56bE*!-@F=B*QebOHonPJxneS6$tdqEnNd^!f)s2cVmW2xY)$5 z@-OZf>am#p;Z&~3DRxP}DzSHf6Y)c9xfQ!&@jT!ssi}&_YVLEFedgl`KkmOzApR_{ zsSEgk)fap#Xk_0l?`>CtM02raeZ}_foYgiu8NDAfJx50?fx9 z3fGlY=R@3xCcG)*Ah_uc9rH;^IiUa`o{vr|dyK(s#hJ4Ed#nnj95`GmXu*{=9E5Gq-dKgpHRLQg3!TR|4^eaU?RH`1?|zj_Da=duGP!3~a{5$M>>>6q~D#U93E zKq_A@ErLR!juP_jEb!cf6#40f+2_?6!^%7QhY@UK5V2=(eW_23F1nXP{EPZie(IfP zF$xVEzMV)8Or5?V5BNgl0W&(57Tsx2`NE0)wmI&TOd1kSMR=+F_Z^l9yOt;xNurwz zu`n4Wy>2CHHBHStn=(NfcvChUK_@>rIs$dzTvL{OeGmW5*<9oj1=i&0MpUKelu)@L zc$UF{97x+Y>R1JfO$N8yW*=9x9BhAu#_u+^SSvn5dGI@k<)dvF%6x41j0+Rf>L;!< z#8ie2$eq^t03u&^BK&`_G~HbR{BLR%;RPmSUvJMLCtmfTqpMd$@7}hId@YC(6w;q8 zvE@nY^y|;NiLWB#$y{3#;;&nrnVZcSW@+ZK_b-|Sh^$I~GKV4hItH5{SOZo>G-onz zF(`k~8Rv|fO228MNFS)m7VHnM6e|COTej*D%7Y7w*p4uxYjwwGg^Um12=xUmn+ z8(qYSc&U0yJ1GJdNgHE7hqWJ-M&CU%k%X z%HS*L^k;B0q`&ZJ9StN5IlbQjJZ{#)UwXHBM;LVtl+Yzy?|@9Nno~9LymTM9nU&JAX9_@8!G^#c+7Kx(Yn7WaygRY z?%t2HV)6=>#4=r=B2+x5?n&TtaiTUx%7C_c8*`e&n+N4;GgDw1vx|x%R9hHKjpr97 zORQxZf15hVJ*mQMJV1FBvDv_|Lx%MpA2OW;xGwcJ2b-l`%r*ga^qxEqMPugk5X=@I zy_S{R>r-izqX0B&qBFlZ*f8hD4ooNP;Ft?vATm2YB6{q7OJ0-b$j-)l4u_nl7H?gF zDDwfO-7Iyu-fTUZ*FNSUU}3Np#z#ZVly~7Pn&Gi2O~RHNIy`e^lEJF+RsDHHL&>~Y7ZSpW1n663n*sgl~{ z51*nnB|_jUrDkS}13{kgNxmn6V|}G2z6XcfrFdUvb;6Jv(@*BX@vJ@_D>4b=ysIBt zzgxknf0azgC_pJ%`2^h-;{S+p>i$qoHm``;5-r!h1w!741YQLzV*y~(&=lU{Mo7XC z5rXOpP+!V^9J88*rJo{~Tn3x-*7joxiw=HTdHTW3{9x3Q69Hkmk@@6KsbcehS2Q?o zn8RVoOlz5VGL9h;#FowD6VtT5CAX7EIJqI&imChZQ9u(&WSYyu&r=ui^kws`at_3YxKi)fMPw_X!~wQo5V_ZplKq4D;f-Y)yEwTVc|l&(f+P^;HNz zDDq^<@RftFKARbf35r!9?#nV8vK%YI%Koo5`+U^vR+g zhN*7S#g8wGTHEZo<|21E(v#vz58EVD_6s3kVP^BXEJJbD6+M0#IbKIX=tseADRkT8 zMNoFPSHk*rlbF07G@K4)xi)u{y3k%e8cF_l8B#Ddgx$+VeGWF|vAe#F>912V->Z5) zsa@TsP3K|HKIfF}6C#A}JYKWAKGJ<@No+6#>6oc@p};y=o4KeeV84kDT&C;l!)G%h zbYLeQI7plHW}C9zJ4oBX>~ zxr;l8yTlk1dRWchT&-FJ8vojGyeL*Vd+Pr<;x@W384F({FCQjBa)^Z%#5*K2+GV|b zn&-iBxW}PP?B9VXl6K9l2DCynd_CQvD=`ee|EK+MlgB2Nlj)3EehJ^b8N%1FYKV*B zxh(!g2(`lA7a+~vSLXK2P=6ajZK_f@Wg0t^A(dfnCN(KH_ajf=k%O9uf)a;jb3`J2 zw(YaI>bX$l-KspADeUeIv^}@f926_5HGuY*docaH9d6Gm^7LNL1cAcW;X3Tjjh{

kLp-v>iE|govC!|?AP>;?baj4?GpK3hn;6S*z1jy-xesu zwTI~Q+HAHs27wOgId}z#ImiVgmuYR#q@xv>NkgIWJCV;$R~KD}o7of0wnm=i%H`jq z526%O^d|L2&-{2Jz_1|LH;N3o*rsz1Y#mG#wH(k7#BfZ!h)9LTr~N!)vV_>Q!5mZk zi(}<-SD9Mv?_@c>9*~}PfQ30ip@>DisvRyyNE7(88B`gK`95>Wv)O!ia|EHYjk3$K zkI$PuflgcdLRYX6lMD$`*vHboR=R>XcX-9FL|eN~`drZ#ivBlFlGCxvC&TOXhWkm} zy}>Sakm;1BrQghL1gNLl3+*QRU(-XmSAeIJ1i1{_8j2El#K+9DAyu4EU)k73F01|N zCucJSXoqtxY}1<(m*cb4FpTO@r-~J(1U4?$~#0eLBLzR;=>V~@d^T1Cu6OMN3mR+pLtQxLy z=38BSLYFr}%05CICpJ@g=2&LPVLdBHEk=BBk;g_PK`N9(E+amCbR$|TmmFb{lsg00 z7-#7wI9Vi?^H~TRN0NZMeV8v6qIf=oF<%Yn$?WTdF}paMzu0#$`|Y4R>iYQ*SHYp9 z6X=sYo$+*YBGtjkQX)wvs#I`b^mOfyA;D|!_&l?vpc5N1TNKSyYJ9ZKY_#s-f%P?m zD~~wKZ_#w+yT{GewIsWu%=^^qWSd*Ukoz=m#f(w|zjb=^!;cmbvf}k>Q?{-P22oOd z+r#OYqWLaczT>$P@`h67;0UwDT>I<0nbl3LL1mS0BYh|9P*UfgYUwm4KXb)&wi2l; zi9VjFzWVKX(&qlZU)lPxqU3HV&n#C3uc?&{2VLyCIP zR5R;<+j@yef~Uhs6(Uvxwj}jr*OSF(z=#0;g-EDRy0l5~@$9e1t~f_M_`O`~vgwbE z#uk+6!kc~W$iPZz6u+wJ%>K*jMi z)z22ZX4qNo=UGRQY2#4Iv(H?iW_$H-^B`Y>kBj z6uR#$ocFQ}O_rYtZ!phvJD)7SqKANHrZ=LLOWk9uTjXUk;QB$d zSx(*Y$^F?IHXJ}0t~83ftV)-m=mTUZj}?~pYr^Spzs_#&$UOWLFf!k)$CLg*`I(j! z|Izh0Fo)XvQHfzxU46AuG_Fj7I{oEZ_a=LQy=<0^%6Z2syAamwDbs%t->mA?KUt=K z(uCfJBW9hCLndQss$e-1eL5f*&RidzkEuAiiW#o;GaY{1l@8#xOppLnfW=IpdoxPt>B8Q*Kz$&9X7$RjX?l+5Jzz6KA?o+kr6W zc($v(qYKkS#PbFO$x&`F_No6$2k2F=#%t5dgQB)(8mu5>^aL4Kv6L)eeIbIoIt1!MTsPtA0JHSV3CUKz;`$@2;M}hn zWEo3_pDWpDw+F#ElI$b}b!G;Sz)I0l6p~MSjX7M`6}7bBMS?`^D5px8!W}}%<|aD7 zC*z-K%J5WRCM4VAq&GdnLXffX%i+rsMHdX)?L8*gP}FgXNkS@SWr8!Kml+iF4sQ&F z2J{uMTaq80ODBI7BJ%It-P@CpaeXhef6a(fX|Y*D+rsT|x!$)fP;8nwUMpWB@p1C% z5iTG4Vgee0JM>o-K4)BtN1_OaA;JhPaXQ%tv^t-t;J>Wp;xwHeMvD;+97vK#u+@Pg z*s$E!)&T`v%7x`wqO<#57x4)Ns2uE_WNa6oB6d$>f8NAj@QNx9R6KFOnc)_EH2B}W zIFS<2kJBn<5Y)NBabMPiT%#{_@l}coNHfnfw$xfLK?(CGj7Hd#VVsFG}jilpz zFrlzl5+^>K#J4<_4+cCm+?4Gg!Np6`w)F6OIbpiDw8SP{<2by36-H*b)XK*c5$M$E z$Jg22+?1|*QAY~j9Ipj_NepgzhWb_7Nw#pxoVbYFDRT$H9ho^oh!lTAn=IN>4?h*4 zpPa^xG%=FPnmBV^&(Fn`gd@$9srP<^rsG*CKsghed7_>~>lu8BD|c3HIo}iKU9vS{ zhX=LEe$X|aO^QnSYtWgk7t-LP5z%JdA`~jyB&q%2J&UvHMcT_ZJPq!?Xs|K>ND6)p z%UwNh0(9^voeXZ3EOd2pI1zHhht4}lTsQP|=t-8dxs1R1(_5K0#yi%^rJ)`qdiUG@ zhqCm+foURt1%O|zDbpQ*QB4#z(W-~sw#f>ud!YGf)%$&PCQ$^uN+1JOOI!>G=yB{P zDZiL>kQ>HLNZ(k=X&8USe>p}=wnO#b$VMOh2?3|iREz+7VrF8_j(6;YD3x5M5Vy*8_rfh%2~?^?3uk zgQlDsg9(?1Z?@r!6D#>%`gp=*{X&2xOn_#S$hGK#{5WBOfSb<%MM-t_atplH3>*T0 zwQ@-hkwYiJD?;9e(cWgkg7r^Vbef%!A45`k^xaSBhL3}D*H)bwU85=jS8w`{x)<;y9JlA%;C54)Iw{8 zRdal5Ds-6^-b(xVu^rgwi2EMhayRx*Iq5tMp&97XXC@)Nt?5+R_MI{9=g)!_Ml8%W zrQGjB?Yx3;c9ii^eaOJCY z9P2F>a9J!_wo`3_TZ=^;%;NL9q1yV!-4PI5lm`nrlB@A&m2NqPFAcMO{I)+tvHws! zVspSdlN87&6NCx-j|6Mq(>NQRd*Z8Ff(jo4FPolc1Z3(BSM+Q{<4UCwTYrPsr;6{o}x3$Zl}9sV1UtOXTFI> zLLo433y$7+I(EpiL7bzvFhm<2aYIqW-2DcsIDZr5a+M}p1We8V(dgQ|4O#B|TsQR? zQU{o~sSy(eg4r3?V*Tr-1k{v;y`b-oRznZ%+k+5eX5&Z&X8mEv1h4P}yxe`3El-}i z-&cIEV1kW?RmQ#cf$x)m_A_Y$Tg|y`0REY1+d)izL~E;3z953dh9f8Kt1$+xI(Ndc zGDv9RS$(FAT*vD-lSzHQn@PlsmG_*2!90m#RMqHr(Qjxt+fpF>^ zFTCZ`2rq4XZ>m_ieDg#te#kM$X)HRTri%mC_A|JrI+(+^XuV{KHi4udJw!_N-&<~$ zi0L+VZ>eTqA!*auk|Coq1c^hO3SbAyA z9hFdajL2<7`J}c1*TIdK*abx|s!j`Q&Jy2DmCb%-^6VZ?A)v}w=ht|XIj}OM2$YQJ zC=PEAZ%o9UXLE_Dg4T=fdGk0@dPY&*vU~(F-k!4VP8*7z*GNdnteQOA`Bka2zemA^(B19itgH|s61PkhHisLij>rV~Rcad*y#`T}vXTfU^*CRUS zqe{(|spiWqb+LI3NG8=SGNST|PntR-bo#Z6;+Ai`J{?1tyjAu!^ui+{Om8~Z*{lgaW+?pc8Ug+ z&CRnoy*l$l5=r4d#;{J}2#`7h%>MiY7+vIixK(Z*9y7rHLW4w|9C_`^u(8-ywv*eN z@CV1p`a0MvX27{c%sZThXm4(AIftUuB_Ouqbi^YTN}Np|*=d%E6mLz-2HTz=EY>qa4N^J@$md~@>GzF-%ysyNE{5%l&4=hNL{24F z4ttwcmyF*`qRnJpN?e)F8y1mhA$#4Zr7yrpob#b0iEwl1atqIS*5{$@9VqFTI;bhQ%k1ud&SY5*+E*UlS{@o=Sl-%(oxkS zc{tW-p_q^0gT3PpM3+4lNa6w(M<3|OJ8{~wC>sVybPR_L)Zx#mOaq`~&%ci6Ec`HC zG2)1qOuFEoTcQxzWfqW?P{!XAN9fFMXmD{Z zSP%m@$O$-~&lDLZmTPbJVcF!&bmKG-(*=hJjD0(A4pewdkKzR+?iE2nL^f*#|o=kj)%31E_ifByLQLqwPrs*277pZ3|Ye2 zz1fsaH<-!rsa})3^O?Z{Puo;&WH!_@oU~FTpL8qiWI~@)+&HxA=t2R@ncF^k6_-_w zl&dEKe3XgcFJ~3$iaM8Ihd?UvYbLC~XNi{Nw8)FA{c!K>9Q*Q{9ouR>M&-pNk(Yw$ zfKi&}47dhuHj2|AN1*C)*Bz6&@hmT0JRTx9d6UtTB@@YpFK8*dY}#xN;8IkXGTQYq zWP6>?`~IzBE=T3{HtPHA?>{CDLtS?be_x1Nek(bY3LCcpa`BorN=$&m`RlWdEq$e# zGI;6j4;1bulV&48%p{1Jqa0H?Lmutze8zy~Pa0k?5V}jJ`C%0QGJ>Zdzv8HwAAD>c zmxl}-9oKmjlg-izZsEP_TBK5UpQ(S32PD6mi{-7A{dj#|9!iShzBn%#;jvICYxO=n z0K2FBLC@5mNG|g)8z_4-9knb|Px6S-5K77JD%q_5@wEAr81pkr0PiHJfU4nmHnYL= zFv(TzDg`?HX8(fN$!iJOnn%u>8n&I?WT_nKpY&Ip$)4{k1PShOz0w4Tj%9Rt6Wk zT*_6*{GtKXvIDFBBA!^$uGbsJdAEAdO@&i&nkL6wh+&&MzxKh z0NSEJ#tAl_+3~48hBa=}OYlxevU$U`DW_Eyp!X|dj@%3Ff1or%XfLvl0@~!9+nThP zk5LPfDz)&^5oiJCw;pEe#iS8b0d@!D&g?#6f+I2B3=X?rQ`wk<1>z~?T8T&goIkPB z=+>aSmhsRmLs?6gV2|J%9)@;Ea*aRKv2YY zO%j*M;U2zlZ9dw8ANdyD^vs{L1~v7!zI^CU2@UrtG@}ToP* zpm#4Atue0py9q&cg(N{Wf*tiDUfuqr7zolhuToTkAw8ihgB#^OiC4$c^?UxJ-FQR2 zAn@swyfn$+Cg~Q7?_?p7W4{GTgY%u~nH`N<8AW6)wfCowV*O7Q_^k{4?T{?6-i3&K z%`PI$wJxOcB&x)hN8)}lQNi9Eee-E1E44aw-!=K6QgcTJZ!4Ts%wztn?LVi#5Pxda zLD)M(87#K9m}xfzjgm_r%^5fOvZz$P;+gT_JJOiu8t;*8G^>H2yl7SQ$HMjCvqvv^ zS8csGzrTE*y#3|{|c5xp98&D#Ys6@7q*bz81|ks!2(BL+yzTYtrdJwzQ| z_-r<~PJ7H@$N#PjGd{ShN9WF9)ovJ1yLdccFG`2Pv=l-mWEoLGsKLj`8(y$}i09`= zBH`HqF+(EAf1w_qVyZLQ9dkiFN28tUAGWjmw>Z=RhKimQ&bf2A_>H>yV4kTSn;Q~c zyPUB9kra1rhfrV0jP`81bg33>d}6i0V>y3${jmQkda!kGtY*)GB4n~LWTKy!qK^$o zAi-Tr!M?^FtY})b)!>V3bgW^v;7AC0a~56e<(RrPD2)9%)U%P@a|^ag$1`Tu8p^OZ z>05fD{>*Lx7`6HE=7mA1V#(xYULg54sRkX`K*bFEIt2aA4u=0$B9im?-UoNoN z#k~IXgV-I{%;(NBZ0_tMm6-@6R>`42FfZ`#57fm6vFl+(3T52!h_+!ZRyX2CmW!k*C&OQTr@Fep z)cs~#%m4B^hw@T8g9tcS{n7VEF~!zv0ZYk!j@v(*bYe%Bi@r}&+Y{xFARiVzKF4=O zELyl7v1E_xA8u7`%fmiK_GMI< zqa_iCzgmF1qH%hE5cinUp9HRNfv2L{qfZ|5J~gQ2x+Kp(9Q_M7SX^l_RKgW$ZjQ{R z%#=!RwD_GPYGAbIMZw77QK~HmeuH$BTZeepf-0{p`m{=o-y$o01~kP%N7g}y4F>)Z z^I)|+da3@f<kDyx6$W zWsPqFEJgR1eCA`GH>KKTXSmxT>As&M`_n3)I739_J4?Yama6pky?zhM4ry7!DX}c> zILpC;p2b>fx*;>K%Kcy*_YFT-SB#~($!gJlcb;C5rOMV)q|YIXKA9ESZK%0?AaF@c z$2IFByDzOyb*o!9OuRWypw7m78U*B^LLaNomL%SKIpm(yZ6CLPg&#;yHN0wnG*f42 z3;D_j-pu=vN_t>_G2q$XFXi@|Hdy8N)76Rwl6}mQ-TC;p_N#EwFQHk*GUm)@(+K+= zngt3YB%isX4F^qo=v(B!)Qy#e^T#kughtx66r+3xw$u7cFdZ0ZiS zd^vh4t(UAWdq-rwmCb$lB@PSwhgA3d6D%-hN1;e7lW;vp24qT*sQ^*h7q;v)Kq*IT88hA(#AKW}8{`%N4MW05Kmdyf zB;)nE5|X@R;AZ3FySltAsV@DMc`#^VDM`TIqBTgW<~y^yep~8tLFsgu4K+|F(U>`w z&G=AbKL<8tdVe%d0ChANXo`lLmHO1l{la3M%@QOX(U6;VG8r%H#0}xOmP;F%JiExl zHcZbY%M_f*eL+yj@Oyl?@QPoea=28oP%5RJ&~f+oG9*1^AU7oT zb>(~-Q?oG9h}S@Tj_Ww}Vsbkw(()c{!c0n7$~t5Ad12P@_`@KwpWn_3GEnOIOriSp zNcuEeR>V|_m0~B8TEd(NRKe<^-kAEciFw=TD|~=NP<-N=BgZeC-ju|>O?y_44=66u znBCnQYN@A758%0&o-Cp^ADbMbngY!O6`leeqC8gZDP6iml^QMhG#an*At*aw=WG)& zriwlrwRy2Nm@&tq@9U%b-@KU)tVZSDVq{S;k|pOikFNrkO1oq2B6g-cW3J9n8Qba~6HwPrrSxPCBK#M8KwrwAbG(iEW6x-Y+b&MCjFK3ejT?5rj zcJaGg{900{eR=9#E~?i4uSO3n3x?r%?_O>S){5E9hfkbc#`w>|*z#>XiihJ&AYAU& z-SP&W=?@&`_aj2(34H$trTb6v6ndGWv^w8>&x^#)R1T~0CDn9H**GGB-Kp__-h7Je zj@$UVP3M$%0Lzkg$}7L)2(+MBX5Ml& zZ#oN0xADRp2;Bd!^UsQ*t2Dnq{9~rwWudYcV#@7I9|>@PyqvFf3iq#vFp<)-cXREX zETvvq`1MBbc(8Bq2`@mzl&64z{9k^a>ksnGK3j+$c$qGJIjs`BJaJ_Ga%RytM8IZ1w*&$j~~1}4z@&m1oF@l&dHSU&m^B)(ga!XtdDsYBWyW^6f>Ynp7@Fe z6rW>tIy#Nq|Dd8y`#KpD3?1|?2s$9^sqdFb^ zF{|%7kS|!YI<^?iBSAL*)WE}N#;x42R90JTJl`_L=X5UV#>SuW~*?U^jNKsm|s0o)^$5(!TAAF~i zei!Wd284y5OF*Ax&)WjXFGrKoZ&SeOUZUGlhYp*1y#r;|7gbg^@zw3IY)XoiXJk*w zm=YLP%|lXGrB@YwT0m4@b?m3B%e%so#*D|Yz;vauK0OVGL&y%Fi;)u>Xc20V_Z6;V zg!y-yTss3S1aq~p@uA5=sVSX=>$5p#?{Q4*!Ryv{I`$K_y{Y7PDa~}2LKfxHnT+J~ zW{EcrHzOY`!v znUkdCdC9ElhuZFat5j>aWQJdb|7hhNq{Npq#UcG<{jJvQSI$~srEjO0^EJT_dtpPg z*^za6n%7R7n2I4ELjy(H-6PH1EI;7s}2zk(FCQbW^Ya-}j7``Ug7ma_+TkqOvVv3sc~d!jzppGX%O?R% z__i}Q99` zSV5Q$8WSH=sSGU!WaNpO@39#4a`e~N8m+u8B3o&9VbP_3n`u`@9e!lm&Q`<56I08z z%_#pdD{+xjiu+XnWh*ceYH@Q3Y)P*9Ol>@!6>@^w`0R7a09wD)KPgtWzqK7+qO_6G z{C47B*9bleeYLx`cEQN!&v?K5f84!elw{k|F5G3?w%ujhwrv|-wv8^^UAApkb=mB) z-Ni5WdGEpdo!uUE)(p6`~InveDnM7Yi)P(QOsAk-V;W0};JU61t8ypMGz7 zF(c9tL+0GEIwg5cdu=_r%iT}(Tz$(uTX|lmeXF~Jrhdc>U;^J z_osGY=li=J>W^+CR%>*eP0H1&Ep8=2DpDKJUCN7#Qx(#|oPOu70u(|FgB28N$dcVUq+i%A>v*Sm)0^=!G3DPuLYs*)vn!-Bd zyb-Z&6-FSn?2w^neLBB_wYK0y?}B(0DlsA1n5;d)L7DVu{v_t(f=)05;^?%)`q2DM z;N34>c;+8~$#JZl>_i{<7W=Ipx8P*&z1DV}O($|X$ZSeGU(QmIl+YH4!}Z?he5DT0 zD^5?WehqpNyNRue>O_rjQ-$oqjw2{L_?V&hpS1wEA9pt{AON|)z}0AT0+nrQHCC4M zz@H5J(HPeGRT>-+6*HuHl5zRZKeU}uF|0F9?cun1jezBiF0xEpD_z7(Yaj|NhYC2g z5%<8<>9_PX!e3ujQV=O=D`owzq>7as<;6*eh=7JF*n~2hZ54(%YbdPT5Wy)uPhcWY6mR`dj^3dr{=dUM%0VOKFtGH^6U|Awa%K9*U#Ej9ksG zEy^idJkLB;K7QtwE(As1mE62Lf4)pK0Z6eyKHp!+@IyniYh(?hPee+GGS=>0eA0g< zhO$Ks^RDOPnAkUh^}YXjrTS`Mg59y_#8qpzEENB18jSRB8Zt0W_c-#G+~LWg3J38? zJ{+bz3O+|K-~5=TT`jEFGG?@X1z}?J8FfhQ{qylsHpe*X{Q*a#BWhZmHWVUJ?48W~ z_t`2fC=GM!t?Pxlwq2Z14;?-iL@)PqG{GrArRi9Ixr;AHiNy*(yfs;@=742!)Wtqz zq8|Bs&O#es6{(?_c?M)q+h@xbjQEmqp`F1@W}X?K!5!r*<}WP^J8p7(L*Ja+^ZGXj zaOyNBK|ATF;;Qw1qL$!z%!{qHP$3JY$Gvxxu z-D9;$%{?$bA1YXsUCrg;Uj|PLHWBVWyVUv0Z+!38x)m!bidi5$P`3 zy&QY6%x}7u-XF(#ks)|o;~tTp$|h*G3!y%?uG>+8wNs@m6vv>JW_+gUxw9v`MvhaM zT3w7N4jJzH@k|}>KYt*!v!gI?{=`R{>f)6@ONX58jA?js(-%ujVa3H2oh`y()*GTN zE%VS;6s%`mYp#lYV~eDq0XaGhuq<%!obaUmuw_sQ`H{c;>6xc^kyy@LTAwo*(c`|q zOJW0@G^tqasN9OmsHW_b0`A28GwOxhYzs_xaN6(CaN?$ea>;`5<>4|`%BUpsMp1D~ z!yoM57a=>ega3D&nsJ)Pv3T6j{Y-WP0JYk|ei5y!$0D|%x&G-Wbxs}EUhc9Q_Mz`* z&+<+$9vIitX+Uhu_(0zq+Q3NG`7Fd5`gY-JPwfu}ecDfVyvOY?S}v!7aMswSrnY;4xnAabs-0&r?N#aF7B_|K{FQ+mPv>n6kl#? zg%p?f9BL6icLOODOm}}ouztDP7W5!i7+pmJy9;mP#-fL@>x6-+{qoPNw*z(O%Z2Xg zU7O~Aj^=-~ow~k2ShRg0J3{Vcvry0wFu< zOOWZDUTx~a@G<}M?f(6mBjkXu(|7E(Cu0VKiVK+=#s!Al4oHy;HR6s*DS==HK_0;p zhY%qxV8J4%jhVzxOvNuMr^TWO#@69|Z8nB|Ei#KA`^A`K)|65@2M69oMd-P}jy#3^ zN{(STH~;EqvrQBPJ(o`uF3A)GNXKw!WIaN>Fz$fz==@(3|BvywlJN&=bQk-qL^(rp zGiCT#uf9KQDrcd%33X^hj&1+n4-B|tuLpQ$ zcKfVf%<*jCVINtM>CUM;zBkpF?2g1Q>@+aeOfVNs>^`*9p0WTne5ZISjj|N8utS(llpZyhUlh@ z9{r>qzMGQ4+QTCc^&687?h6#XBNH&|kGO+v>#2^v)@*~xTaB*=ub2$ImuVp^pal7d zgV#fYD5iG-r?s|T5I{eD54w3Tf;!fkkOZ|@Juc%zQb({KdenV&Je?GMe%=ymBh7B(e-!wK9zpFXp-siCz77)5ZPFqSg zne<44qbmREu`e>y0bgWse9)pqDB0al*p5#h`bg&vQ*+zN}z)+Y7U1X6^7wC;64P(ncVf*AI;v0!S?&hZARjCb7mzbJa*xZv?&CGmvi3ke#} zh3YthI8*#vSN==7?*yUme4%64(No&};h6ATyyalE07`#DaXq7Ny@OI1Dt=_o_^p6q zO`RH8)NKg^Mfy0&5zVJg^5>6lW)uR2_hN&>K_zA){>_|p0?J1c{v|^XRZ)z#hqyLv zM!iRc6vlfif)383(=Xn1GQUUqU%5*R+BDzn4|gf~o8j6Irw#ebetJb2rs!eC z2!Fl@44pCL$t1y3;<&=oq*nFbV8Ugb7Q$qyrekzVq$Hm$WW%Mc?>Tw4_tRqokqwsc?1I;-5^F3(IkS&Y11JF+R0WBG9adW| zvj}kxR!*cm%feb~y_XkrI7Y2XKQ^^Wzr$;(xN2lrqe?ARm z{_69-Xw3=7<#zDl=oeu4Zoi!o&AZyWP-s@q=>7w!{kcw^51Z-fiPP+{*Sddcy#Hum z{xvj5PXR!4%TGID>C#B5MMz?C$f^{oe9x|juy5^$h(-$`H>)iuOmhtgJkXjEUyjM< z^F($E=c$n}kib8+-H}j{>G8o9(}7viH{eBeHw<$Rg80!tBK@AqnXWml^AZFhOpS1b z2%h(={Sf<&kww$UfmxWb&Tlb-GNb4^Q|X57dujr@PkNX2K;2j)Y!>ko=JS;*>Az@sp z2gQST)?(IMDe&`UC1e8E1mZIR#j2_Kvo@s)Y&bDcT^fVh&Sh15Nz07h>P&CqG~5~S zW5A<_F*bh?-}eH`uuo6l#Re)j^re0M2JE>=pVRfD^y)3=2>#!|=wIX4`$`6osU6*V zFD6;q+z5ASfU{7S^D&pMlsO_-b-6L*FyaV4;I16dHnhDXB8U}E)z*Ix1te;)ODEw* zy{gmiJ?{>1w4T8VB|bPf(17B=Kp~T;)>3QWl0jQJ>^>7^-PiS|qC}(Nb@Y#^yj$?xr>ftP0dqLmrYGkSbazkK9$K(0@N^;X-U++VJP;H|5P8)`%K+h+$Y z4>xK2az$(bCdp$?9*&pX=EurQz-Q`Pc5t|JqSdNH`=D~O*yzUa^j?Bu9Y+ly}k;GumgGg_F5>_P<^~r zMYDlNC%RJ{LMzXdOrJRc`wWFVylD|VuPkI z%r`hDYkl*S=;uhehTQZD+t&(&r%>VHn`UmU+kUj+($B|sjr)D(4b5vHvS2yQ@K|qo z2=!L<#cn_aiSeW9p}?cEg_S%)TxZ0+0!X`U)fs!DcpIIWR8l7-I>N85F>~X8yB7Xu zygh^jXp^Vcxlv{nuYa?$6d5~XGdeQq#>o(M5j8Q7Ur(^SX40#S3dtcK6gCGP&7Sys z{4DPJR7r)ig1%XmGwxU*6Az(lTYk^-U3W_;Napf)uUPx{U!^~ z9s;XPZsm?WRRkbrZJ-ETh$|Q5$T%2a9u(@l&(sAY&`(-W83B>i*DWmWv z`ZisX6SG9dsK$?UqHuZ8*oXdLz;IF;RI-e&b0afa7@f=#43s+uRo~A8Do>_V633`X zYwd?oC&EwXO2kBR#HSsI44wJO`JH#kNoL$j{2G(9P71UbkOwK3GUOb?<*^uzJEJ1J zRQz&hVbA)l=DrBN7>)RC95>JMWOo&grA$^eal+(a?$CP8VGT2y*GJCvnDZ8Q<2x$! zsx^c~Mi~u@d~k@Uh;|4zLXl|LRie`CJtslT?ys8uY5S7=3nh@oC1LW}^4?bSFg9?q z-*&npxr;7n#?$h=;tY8eNMkb_>{QpJKKyxLzY-<@p)jJx4{yTqdK_#s>_EYqA_#N# zpQ)Q`$`kqgbA8rCMJnZzr~!;VMZuo)3pLyc*^I!{KWE1>DJc~*k-W{U=-qwG<4T=N z5#|!^QYqE$Jb^4PaS5Ly|HE$oA1tyXj0P}6@$@uJSI9DWi%?i?=Hm|Sd1O-Q^-E~f zy+=nz!m9PUq+We9<+7Z(2@2 z(j-?d@^(wFS+nYDok?bo^OFe7kIa35k(3D{g+N}Mm%WB_x{%$k8YXg`1Al|JO(g-+ zO1^S?Ic(4}YEH|_cp#7;xfGjc{@P(4{pHKKhD<#jb z)xKn+)wmDrSFkpmCs!+}{qKhhw2VwJ^tWtPX)9sWskTDGzR#(0x}74aL+|k~M3)gs z+Lq@>bYqIa?#QhQpEsYg*=gq6Ff;1kTCR?hE@zA8ge_JY^~V@)Gy@DRPf(M%$W<5`GiZd2h>dM2WulT5KK9HyL0fX84$h~WjNT&{5HfseD8v& zmWAjV#WqeBag)aW%qJcWMY2Reh#545qZHHcbR40eb%6g`F?CQqDFzIaD{2ok4w{i?)pMzv zSeAz#P)H~Du%`c{16rj<5&W{)CVIE|1O)Lc3*c<}*s%1_Z$pTg2G9OvmsBx($4-IK zHdirYy|8C2!Y4t;N|Fl6vk=|9T{9RR$`uSw@=6X$tA@Y{z~iPw=ZOBR`M=d z2RaW_z`yq)(YTzhx_3`v zax=hNi8@Uk)*xqX9do&*_5S_#MOKAcwLBl?t`+t0kWoZu5EUtJO1m2ZyHu?_>h8%~ z&mWx{m}PERUoHGg6)t$x7rFe|?b6KOf1nd3c zm%Gp5`htr;cs4hQ3GEpxzKQ%ueDHNhU4d+vh>Z34wInLu6=Bu!>oL znmtT5$9puD9ibAn05jyKwlPLzv_y@Zh>I9FVa}1bmQ2F*uy*1s9QgNL-;xw)1~@D7 z9!h-C&x}B(IsqJ$?`(e^&Q`2LJe}(wTts?2=!!B=I=|Hbv=9*v*%=|M{n?l3*QNNw zlmHg*Cy;2>L41bx0G~X*BAHrneB1*?m;PJ&A9kNp&|eO(18T%XU^#>F`)vntMKoh^ zI6+^PRXU^Sqy}g`cevMu&6e4oxjAQ(KbT+}DHD%KptS*LWH% znEvHS6h<2F$c(9`a%6gldBvbR_DmJ$rTXdv`ns6!Pc=cje8PYDA7C2Kxahv&JR}MGiM=bE_xaFj?=wWc}ZUny8>Sjc~ct=y%-P!}H z$nnKXO@8DS?@6(09uxL>6*9y&XECSM&bq{CE$*)F?mM(!9n{y@qrQan0+95w!QUac2oqy$oc`d8#6mH2g4p@EVZg~EE2$^)ar)MG*{7m0As zaOtC&-HmVhgQS)QhPFU4N?_%p_$uYgUvd{J1A%Wrbx;j2L##g}maKv@(3lw2+nxTNjmJWThgW$J3>vUo@u(7h zayTMnA|Vn?!eLf!uc6Japh+G@cM z6h927KfYTXH6U8&an51vr~a%us%$8R=a$bA*7JjF6hF&dRcZ)OK3VC)jJ)fS=vcZc zDl`X{OrIK>?Pqus7hYFqQ{DA02=^xs{x0_%84CZIBNy2ZJf4;bsElAwZB$Mu$yW!y zRx_gyBAa_4EwY0TRi(dP+@5h*1X`l;LWv< zL9*oQb=%Izr6Rn{_Aavd;{e5aFF;4jSCmHXnP2U`#FIs#b z(zmtr62;@o`OUiR^XI>Cj^$KfH#wVQeX-W#*8ENd<-XyT$qczMch29fx#9@-WQ@3z9=Sr*Z)ZncGXErsx-HV*Q3y)6(UKW zqLA~m%JQ(Mlv3@ti9=yTw-}7rW0BE!Vhd+Wxx(*w#n&cqSYLlJf$Jh?e^|3LQT^6H z3J|Hw9TN~`cb_VqIYTF5a*oNk#}S3F7v(|!StJ-C7K275j$mO4tvKHJSGECGg{v9Y zsWjnDtAIEC1;>oj!gcEvP4B0-XEEg*)&JRkNW&-DJsEjEpBv$dae@+ zNG(e&7c+(T0y;s-sAR?A2DabEfxOd)oXNKY1RWr*5MoiHio^U-E+ zT@lmSt5W=M$AVSXk9oPD8=_Andy=n%7x8+X1;jpgn_AE^5yuENHGV64N7<08AY$F;X z+Z*BO-4RaDA5D@XW}VSyJWd5G>0-H9bFT@;ULW6~u+QLI!n+(q#aU=;tZfwu_>GBS zZ!xsHF$roq(I6i6pPOl@NWLesJ`(V4GBCUy30{IQPCwQn%_@0h5Qbh)0umi@Pm3tq zJB_J)mTHm5rixVBhf*7Fk9nmOEk%28MXYX-b;)!!ICIugkqmRX%B(oAAVmkkXHS{* z6(?rIX7!IiBr7)`64`Rz-S*xKS=7=rAsa${#O6NEc8kzeB~_7@EiTzXj?d~EPK+#w zu`=Vjypqix>?MappN|!LAl};^kVV6-N{3esz?txkS5lBMgkC?hMP;p9lSF~kPnj%< zbUiV1*`m%?eLPM(ESHrl)RbJVZPywdV12Y2sNtUNkTzlEk&nDiA#dOuHwS;aYSD#c zt&0c``j9L&k#MvW+)TtOt3)1`o~#vfnW~6SEo-QtZhmTrDuRIJJg0&;QcB}>r;C(E z@8;hi+EUu4F{V*dp{kTSQUD4)ajmuZFEDw{JIU#S>)pEB(+ zng*9)bJ@eHQfo$!FTMqHwb;wxX}gtzRmdr6B(~<}VvjQev(zPxJ)LCda%J@N{lO}` zQu1FG@S;JO#*W9uhzYMX;JNRKG1i-?b38&yOR)~v@`1<~?ZS4TQv0%0z}UM_^W8FO z9}x{LqU;(+W6XiHldt=O@xTIIPJ24|+{(>n6?7LjXP5(Se`gRGC>cUs739F9FvH?ZAC(f~31wq}~j z=qMjk$9XCgM7nBojaq{0oUIVQCA*RGo&IBH2gJ|^Tj&;}5CaPICBHX(!Y*VcMF0&Y zc6i{b%ndqrDSt)@xS%`G++xs@o)lxVZRy-P-*g{0JH*O(bmel^$zhfrY1G zh{BN?CbwH|G;5j+D1BJC!8mV;5QZH*>9g2+&r5t|hs13xF z%8do1Ww_tJ7vz0WM;eVD)^GTiJMf>417slh*{$)A*}l&WzVo#@xG*`rfv!BpLF*=f zJDGAv2OSUGH%KpAtH5N70tRI8^_%fkcNd4-FYM~=)*uK3V!ww{9=U~ zQ5qIo4UC~^nD?jyGNxxPw$B#$PuuM%gBV`wCA;VsG^yS-nTTGRqg`ii!~`aUYeI5F zA|~*a)$by|#uw)blrb9ZwjeKK3lLsFb@)4>&X(+kx8?x{kE%h%tb0Q*zo{vRg6+Lo z5`YGa?Fm{S8U+dEQ*fi+mPMb!t>;OPELo^jfYLUg(jxWyEE$fywQ2ZD1z5>uMP~4}58%GwVDl;PQI` z?)!;k92fSMp3x*u^Io_;DXvuH&|~JZ0FyXOQSkQ2v~KARMtOi&lG^cN2hN(hbCB&z zo#wfU(2glCPbFcwM6DY^^$h%hfD&3Nu|vjXZ3B{yT)j!5|pu zvrb=(7g0DGU~zhKaHR_UhQ_Bb87YK#2cK@Z0V*GTrHjXA35N6& zh6W%|rXgqJT?k-GHMq7;~16_6e2yQHAPf8@l1LHAC7ZR!mW ztPz_WsCaf?B7SRm#ROguh@h6eS!@ZK`=KYQtg4xC!q;33VbP&bbm!stVMj>~HMGi1 z6n{t+YPA^I%Om|XX)Jpy{U;Un58e-&1yWx2b$>IS7m`73n2(;ggou9$8o|xY4JAEr z{G+Wlz`~+7c8jaic6^#KIBUk^KuU)7@`B*Vh%y*>4izIYD-k8)VnX_b(}*NMLkUIe=B8`FaA66#_k=>B=#e(O$6808G58BBV<(f;G{ zO815Fg#{9p2aI_0MgI*9*;WN(}SxpJPxP`d@WS!Re1cU0jBgAheOdU|?U=dl9X z1-HAvI=Ef!xK(SBbnoJTcCG0;&<3PzfE=}W7n`mq;xfD(Jn0C%K0j_W7$4W@!s$IP*?5y(xLSqkf zZx9H!_ZZ#Mf%us!Tv*4JTKjF&gC~m+Lz1w(Zx>1qhz+lkgYPL^buvNHXC($NlMkco zX1K$#XAtI#oQhP7IOz1QOim)Q2TbgbNq-7ikNN@c)|)46vV?YQ3>oUIL09R3_>eimw0?JREwL@5_ko7@Y60$K$`h0EA?;h6 zQ%l;S#uw9y1%W7h-MJD;-DD)|f^W*uSbH6l%MSj~rV#yXqaeY)e&&Ux= zYmO+_H@#%5Lu#8#*#9QOhy%zlYvOKw-&zTN_`^RTM2rNxUy8=3^H$nILd`Kzs+NMR zEieQ{g*_Tvr4X(h)rD1g*sySsGn~3ugt6K37?@`fegAkqb-Q^!EzS%_Z+M7~dpdNj zi9EdE&J!eKSD2-;SqBi%0#$13*$4%9W5H$D@?G&TD3L*Sn_O|Gb)fue6;HOv}W7fyHYk z7yx6BN#6&b`@aV3fAoE?^j|SlDx|CbAG7ISAOD~GiTBFb>@PP6q=}XIy?+iX*Y<{y z0M?P06Wv%|m|P76QUt=Bu9CI{pnLmFz_-%+&^iGMHdGr@F z<-u2pVouLt+;g5khEo6&%}E1bYfXE0=P$uZKzfDuLLeX&E(p{t3sCS;^um)#rG|<} z0M{~3qzg=7E|e=HBjMpk=XE^YF!F%kSmD7Et1u_6=ch?id3dxI<`u%3;1+b{pG{5G z{}a6ZXPEbZ9025;<`+He^c!32%<-0NuKQQ>C`l z5NiHlPCb9I`985D@R5@v9K$2-N4m-GZbr6o95}^j`YXR;5(76v5eqmY1879NgXQAA zH>%Hu!!sJr2k?!dZ_kbE3Njh91R?LA1M&@r2J;~f7O+aKO;~|f6yFNWr;Or{W(y0i zhD>+l{y5g&$$!1Gda7;|AiMz3gZhB*f(FD?d=O9u5tKNFY+rOP#;!$~hWS)8)UZr8 z?2KV9;g}bI~dJ0uNQXU_5!1#N4jps$?Fn~%^PkTKFVIcyJ32S7?plJHlN7Oq)gjlb{*W^3tzOSYB z@Y(%4bGJPsYwDay$Q7QLaRvKgctUG>C!{Ueva2%BaF+Y zn-u*~q)H>2GHFs&wlI4F3BYQA>MXU*=ojfUgv?$aN}t1w@-_29#x^8WU{Xzxl_I^Q z36Xr7;d}rx%Y-$#@7p&Zx2@59aSk}GIejvfF@(DPqt&z)54vjW!Ec+H#s@2k&&@t) z8URu9rdC^DAFN{q+d4@8QoMX7qOTmEmKH8Dr(adZXaI{pW#Xivp*bMtt)W4ghgkCS zet$3@JR5r)m5I;L4$;>$dk(3#u^}ish79)9gf!gR$XlP9IV`0q2Gi@bNrJnh9Oez; z&yv3rE+CFUfEk=$i_C~5(D8lERj2104$-b6aV4^0st!2wjiI3>+z4?s<=HM!T;lX- z>WEb316c?WC+yKo?ue+qUtP~I3yE~0+CaVwAbzV36H?^Z>L{x+)`{Vt_AFAiK%AVE z7GW@oPhZTV&?IHbf#HTIsC|eiJGqB!s3IlAg8!mB60`n~UjLIsJrEUhDV57Lv zhPmh6Z1nJ>i35)`67rxI+ak0`A&c7&h_7H9!~Jq_Yv2fpScq<19e+xzMPU91nE_RQ z&D&*)LZPQ>>p{cUDKX{JK@!?ye1JI`pzVu*h?H#rJzF&V5s78R<_7OTdjP8b^pjHk zCm7h>nh<>;hw+#1$ZpD6cRb$jg?VaNAi^<_j8zJCdb%o97ybhJ1kXA>VQHXbMd*84 z9I0YW8LWz9U63}YMr<4F&tp5z|B`$Et09olD~_Irz$?|0-4u&U%0|7w%p$mC5hW`>ojtp&)5Le=fpb z#>Q^yb#DTHDuLk;S?z)&DxM=ySy0=;2*ly3n!kU)$$?rQqboFa7$ZBgz(f!yEgfG~ zrVAQe7n(T8ZvSnpaHYYR_c8mvZ-4`WgR`T5aS;hR0()Nna?qzs7~x{F@kI;&YTfoc*?CXOvja## zG@+m2J&fRJW_X-;IRTf!?B@yWUhw5HUoD1J%7VdB^+2oJJi(7v3)0AP%j6oCHCW6D zN#XW8xc zu*qPwiC-?sY}Q-B&)jkQH^b^E^}0aBJmdtf)D{gYV1~x3#Oq#nkg{(0;J39uX2S_5 z#tPdZ<6srYD+rw}L)GXq3JuXHNViu*L}5}5UnT2uC5j`LFJhpQDDvdz>q|z6WdU)4 zaA=M&yb9luJ-?^DdOsf0M>$IUIy6r=+c%56`xfMpk2puB6WCB ziO(U1Nw03k+X>F$Y@upy_<$pIS+y2i1IWtrC^euLt(QVGUBAkmZ~3oA zee~5uxf41%h=rhM--x=;Elb(erdlGoeImPkvmNBB0q~VE!K_;{j`@!LFqaz0#?dBV zOlujM@mB*t#N~JQ90@jV<514Z&_vsQ{vL|vMVr*2E)?T~v&&wIa}G!-sasn41F81o-U(Oy=7_V6RY23!idTOz9aRN~SJD*`gKD0X_m`S!}?mm%=U zLmVct9d6#0m#4Hq7wIG3VrZ!1>f`^h%bds&u{++zWr_KiYer9X`; zCTp177Ccc3pKZ?|H;etkk&41O1wJ?F%3{f`v%6xM63x=6{3Z)jU!&=+y2^yE+}@bn z4Q@F}iY0H>2cK^gvdHP9g|cG&3_SK?8$RcF^gHrv0W4g4<9A>b7OV^2wNEtt6CumH zK_V5^e8$y06Nrq?Qywxa7c4ledny;>BmIR!JI=&R$)Vp>qfNg)k84n#s6=V~AD_{^ zK1pS-mJ^|AOiH>B8Ps!qHM%o}P!^JhCB}d#ENwo~i{g*aY*!<}7EDwMY`ijBURX9C zx8z7X7f5huU=pMFepSsSnR)$IFeMDyo zbT-I>iPD+&CpGgQMGL&>q?YqS<M{Boy-#y}18PA` zxW-|~s49oxCt|R_W%T8}ZaEA;G*n4LW2G?e+JNhJ8Pe)3AJH%q(oIKuelVq!AVg9g z%z~_JrnTq9ESj1QQ@X=K{v!3;um*JYt2u2GUAi0oTq)1P>(33JmYj?^Q>&Zei>~3R z#5P-`JXvHQt$t%5t)dw>?4>p}u@#7p7Z#^WK4|%GEQObf2rLI3->`kuSP{lzLWk=X z&jp>`bfz#fwLmR$;=n9^{=Bff&2Y9nT2Q5mCuS;N?UrNo`1CV<{v}RwMGrnk@52~^3wE@KYxqVHvwd?aLZ(T zh1&O0m&a?sG@ZR+1z>C1N-l40l?~Lu;z7RcD4+J?tGj2;him?JTz;8PDC@Z2>C`U$ zRdnRoPgj~CZvBcVmoZfQG6S7GPXdK=L^Bk*nM<6n?V6T)N9T@Gf8HP0a$tNJ05ZHQ z7{7dWXxl`yFwb`jp&8{tv|{NLAwlk;!r3#;)PZj5_WNg8-Ha4T-8X*cmHW=2H~r>Z za?~@?GIr-Jrwv9hMobOK!^!KF|C}6Kb0TT7wxFFxSQS!2es|#m+lK z*V1S&aa(9=+(XD7)%egcND^YI^;pR7rknTr0d#P6^%;%8-|lxszu870g5)S!5H7H0 z@s1{~l!dF!trAN!B@2Uux6SN_EZKSLbzk-v07p zZZGrsqleolVBm6}5NYXK9p6BsA>*7AmbmGP(seJt(yfQcsCVV%tfXZ05U8cB84p^P zJCZD7o}#8w;9TBEak#s`TZ|RJ1T)wc?G|gi?x2fw2I+NWU0xFmmcJbNm5MPSMn&vr zixtYYUE3XYp}~TO64<)DO%7|RI)Jdr=0jMO%Y44QE;KEP_^Y+*>A(2QOhvo)<6k8T zqR8NgA_a^eo)G0}DBIST^{1zSsB$pJjr}@doh_(|7*E@@N(d-}6V4VYCXEK;Bz4bT0?y`C z3_Wt;3uTYEbVl! zq=A*xSc{C(bMcZDRJ|IycQ@r4@)zn_=PieI@35$zH~iR3#n_q|r6EconX}j1@Jbd1 zFAylg-mr2xrKk?lf1v$#CyYOr}~$u-67X%frL zn=@4WObP0gj0G#!Vp1{%jUBqRP7h+%YPG<~#baR@HvM9eCb9D*p$I1TmrROI}xJ-ttl2{$idU!EQXZps*{&nHI=_DUqGQ@SM&{nX2RBFHio z7oTQh(&mkc>0~FupZBzh?oJ^UaeQ}<&VBa0MuQR6$C?Y|6G8aUbyb#Pi>9=TohF89 zc2w`UOk7!bSnHV=Jogjs;HCzZ*8Mv{sP`O*>TS8>;t@5#WC@+uZZTR8n1=)$C=+us zLqWHm?|L=^aJ$N?+)@VDTC4A#IRinoco>?m`GVIAcd6OVm8jBqE0uClt2&@WT}dN( z>EEB^4r#Ot-cxaauX+EPt8~ScMssGmP!CJiyCz7~?uZqngoH>(`A1H0OG6BVe^uf{Oiswea6V)r?j7qPF zT=fbs)RUvD*wzZdGUwZRupgDlUSn{!WXs`ji!E{-&QpAvuN$`XT%>}fQmF%mHsnET zny8L5b2mJ;ZT@HNZv*Y^0)(9>-q?fCY_l5JB|bwkdqs~QDk^BD4NJDUX~>|wyVcbZ zzDTXGQANsUxrD#j?k2+85*R4-5=NlkwD25RGf}AIT+?Rf0mR0tA3BS8^NT% zG`LFE!1Oc&*IJX@Pi)KIrDwk;Z@Oj=EQVkmXSC#mgm{AV?TD7`{fWShpbNBz+ZQrs zsZe6|woIWL6VVk9sPD#KmWZ-Y_=b=Y^v41y-9^3nAfdeDmZGhQSG_t+mdozS*DLW! zy;~D~<&h$BC#SE}jrfo=b;h&k#j~XUtOZah1T;E$m$udl!Ew+8rpJx2E2cpBMi5NA zv;jX9g(i$dgNF|aPDCme^#+!`qgH^M(R>k3kB8k?6^~c6_sn=D`|P-cYs0e(Q9jvk zk~W@pnv|u3tsgCeC!e5>C!Yc7_cy>Q3;7umq)~;diwlY9qh;6U1#$ZKM(=<{Od6Qz zUV<7_5%Cn2+7D=K7W?>L6hQKI;H}H+2!#KXnjDwfl;??ss$zq_QNeV@ZV}IvpXirMZbUtcNN003VtE^^ ztW<`G8#sFO8MJUg)D0A|0;hPQe!ido5#B?w#qWxAV|18#BT%kF9ZMG|hR0Q)yPh#> zepA-fXr})bq2zEjxK#RL#WT9HT~DoH94wNfJ^7$EwWev*ABt5Z0V00Lq zdXrizI-vfGogI9e&ZFJSfVt_PHwP{t3z`mssg3H-aMB`xXRB_>d_DX6H zLj09MDJWz2-yHk@!?Yhj0Wj?cI!btm|L$G?FB**cNJ;SH2XFyuv`-15u~WxRf`4e1 zUI;P)5&39w;$1%y9-!N9AH%n&jy?}rviOXfp6%_HiIMkzPjo;FAc0HkdlA%WutjjN zn6ObMMhNs_Pzcs%e==EnOFc>-VZUrw-xuT_$b@In(dUl%1sU)*9dIiBA1zL3d}&GH z4giEUV9=f`u^~UavjO`x9oNkM#*BClVxQNRaciV>ny* zJ)wjl`7rT*-cE|0=KeMvcs3x95S=;(n=8AP6sdI-jgh^@KST(;A#Qd7l|1sx&HKBx z+S@BH6S$$_93hPtD(x{O%nkG7N9<9Go%Pm#(vWx10IOgE7~K-Z;cf-rX3ZDbZ80-n zdE~o8y=rGaL!BlE)K#Jn&`&~gQcwip{)G5~U}`e38z?SR2bvgY{L4{edS9%)?a=zG z*91JvSyEya4p5?mIt40K1&R>?D|O6jvWY?sU+vK?sc;EA6f6fo@o>%i+0DhZQC|F$rpEQ z+vwQI7u&XN+crBkJ2p;c?wot(%=riJv!8dbwO3WGDol*q7*a20h)$fQ^#dYi<=*~& z5XZGQPR|JEuvVwDADzuPe(r4^<|}#R+IUrcUr`q(f|&0D|L2k6<@F!D!bN%nHg282 zS*`IpfKJyNDG4%aL~Q1!bk~^l6Op5Yg>m1L_TTxw{gxFfhs_4is&6+GDoRMd6kF4W z8xM8(NgP`e7J;|Hi#U?CdJwp(M}ph~=Nr`0%5lWd$zeDAD2j=MjvO``+Yy^&9r9Cj zB+qQ0rItNJP2+L$Dw^jxXEVlTqj~@Sk|+o$zUxD#&|)PR`O(h#J!=N?(F$ijuU&7o z!D}@W_}Q6L^%qdH5qMz~k73}$HJs6+&X}7V4)2n)0;@goK%K%R%hsVx>F6O-3tVSvVs>f-y-kRZa z%O2&1!`6JRjhE+-c|cNc$3u)$aIPJYSWTs5VgwJ5e+8(EZ?Vl2u-g`(&I^Mw~+;anJcu-k+R$ZO({ zS5IqS8ViOJJ`i)4ecB>AzI0&lK)sk zl4&lO^UWz7HEv4Hed6Hc2hY-x6y^O*VFioLel8e~u!%tVP@#)u!3F2=VM1l2!zpaSIyPNZboq#giOH5)~P%m&h zCUZ|390Udo4T=g$fhbo<$iKG-^Jg%mCNq!XCp;DDR?76HeXb6WAy!SBgV{ntLdQ4X zLNa~f_p;^J@>L12d3ie~BYNe(uZwxA8WRl@`v z7w(pV0RseIhKL#JDOMM!4cQ3Vo-$P-vI!oejqG!8!$_>m?%MW5z=w=6<}!HMG&}jn z3;##8iCg%SFZ%Q&u^viDNXT`M6AJd`x3oIFUs;nC7#@^|6mRo&fp6wtW4wtHM{JgR zXgIw2@Tbt+EMFKX?=28N!yj6IQN|DyS=FHa%HIkIiN=-^g;4(#yy*rtq3|k{@#I{{ z4m2BMXNjsGayMReK%+PQH>rq!uEaf^Rpv(hU+3rt^7DTmY^pAk3;$N0{i}?VM#Sg1xNeTt#;^REO$(xF4EOJl6@iBEBU=uNL+AzHBOjC-ntrc{D2`$;jqJ0aSW z^9sjb#4oCdjWdxC8O9&!1OmR*_w`<%9=i)lYr>z}@i{=fe#WB@lgCe|+(6EGi=9Px zKrTGF@@9Pwd6LQgSF`(viBz}i(S{3YWYrJ7JTu{~nHo^Nj(<76)%pOv{;!!H(x=gx zr@>rGXZr<#_7v3!kNZkV|EKBMy1N)EofiJ70}_x&qxD%t-@1}hUvX9OlK+0i|MS=M zr2&BfxkTj>&R_i1?(Pi#;X@yrj+icK)Nwky6?JY73%oHtZ_L;DR*Af|v8uetl;3!> z^nT4jx})u1jBZJtpSDR`)$1|kK(Nn!Z3Ib@&;}tmwQjy{OiTt3(NDw z*R_M=f{7hmdBi*a(9mp$rp>xA7=4SPK@kZ8 zg9dz6mauSLcjFGiO?Z$K`#)%%zAU}uTIHDyG2G_`2HV<{P=roo(r#4D@M)xsy+fTM z?uqr4B3Dc%$@ro(FX+a{$Bp?N1o8As%BQiCm&o9oA^VI@Q1M4m%>kJ+&3Zz2KsFM5 zbP@x){#tPUau^R&9UqODfG((~8B=`In~x!-PgxC>tbcmR4^)5kOex718NIdQg|tSU zYQPg#;D`>T>y!;IjExlN!*mRQMdY)_&Gh5Db@i9Vu2xx%Cn+iFW{cfzzP{!*LGD%%K{NqsfNz3%#)pRj!tA*59W5=KLny}o2MCT&2kmr%d)gcPbVmv!2Hdr!=yARPW zLo)S?7+~H~mz;XX0n^6kCdddm9dEvm*q9K~QAhnkxTn`DgqE{bG*QjV1qq%lbIG7; z)Z&}r6!f(v;tB1_F8GhdZ!@;Vt(yM-t?vIvL){?^@e2^5%~LoqQH)(6*X-DbBTsY9 zYf~OkShz)Q40N)xS@#V?5gWc=h(ByJ-_P*)HGKQ~wwo(Q!gTi(oo zy~11qL9tX8;6Xqx0i1*E`2iClw)QMMc=NxfnejzP%Q^y?ae4A3)Z(6qcO;*!D}aWPGYkfq@JoBMAz4F*rK(ys zj@Ta!k6>glKN3rMo=zSjr1wu<1dBIE-TTB6xzM<_})^-Ml*mMBZb==cH>YKF8ifPe3sO(ld5Ve2aip!ZQqWZ}D)3{I{qbiwXRNB$HAAmo>C^{`(qcc{8}NgMiLL zYnez;!*t6Yi;ZR2DvJt5fA7Iy^fRKQq|KCK&03uCKs2&s4$FU2 z8ZBu4E~EQmLPqaUfz1 zsBmH`840&7zvt2`dA#8v3nHjA16~tbsz~>U04O zwnh@#YF+9{t#~`q*x^poBNY}hSQ*&zBJzA9gbz5F2nMp*q(&M=`fMOP0A8vkbC@&M zU9X`-e#U&4=C5~1{=L>hV^~3HkuC`SYNJ2LWtULg={NI>a13cIKhU1k^vN;P zB2TX9mH+)>|Gz5Eb&t@0J~pQ_67SXJdAip989YnlHUrw&@;$xhlf@R&k}_c}Hjr

h)RX@)4MF6c;BHcEihJE#zpHNzR{_FrXz&Z{ekjg%NKZpd*$qEk+kd zU&&w`vYd7;c(PXDZ2qF|dPFXt2b%CHnj>dD`)t-E_P{YSsH6cHLhj=KXj?+$5XQZ zc8W>X^c0`ak(aBE`9V{YKdwsU>Qf-`@qbX&YjbM$>UzLRE)su@tejpuNP6sF$r2mv zqNT)39m>*`<%xrq6T|yY75@##SOh|Yb+~?~*Dbbv4c_lwY4F%zGi`dEO6|^_CT3TN zSGE5sMvyI6{DB%?cPjlm>|b0KlH7N2*YCO6@b-rjsm+dNa$053d5i%5Q1k37^A zDmJ$G_<5(X&Jie_zg6fazFtyx+S&8K-e2L73k9a4#S8UA`=y>AG@UO~1@}Qvjy?~e zw%oeKE8Wb6+IcuCWx3)sWTuWRL3{OVqoeikYmZlQm+74O5K9K!~4>TlDz+1cWdDUuW zUq-SM%eAASm38KoFqH9P*!gq<&zf<56@)w$Axz136Kiy)zGpHl#BC!kG?mz*p`?kN z8q!;?xAD7}n+h590a%KDsbip{kc-caq&m-YnD@FplATvymP&1&{mzCqmC9$t=fCB} z=W~q%3GAn0mKAg1LCp7GwxMQROxtR!#|H~ibgN|0c*q(h8E9_6P?qDf7SB{=8dq>uLU>X$08h|0b$?I|k zpe&S0+c?$N*6z5(@DqkfoeYA7M}!rXUfYL9%NUC^VSI3@5$Q?Tr>57@0lhHwPMeq*MqGc4SF;wz3oFxy9u^y%B0gG(K*^ zmh9^u6Uai$PmIU;SR=MFoNUf&30~bU?%!qiDUg3$w11fAURzcLv2+X1Z1VvgL`Fe! zJw$Jf&x!ZJt6{Y30coAeja3D&l2@sSENW^OtOZC0^c2OBae@--+Bv+;x&lrXaF!~l zM8`(a;MW_4XR?=5J8UVlJ;A^2uVL)Il^ngQ!wSmFq8YZJH%2KxPd1#HmM{1qW4@L0 zVvu|5s-DhFWiTe>%8{u~KeFpi`7p4H-Qxj49h-g0H#&2<;XmnkwVuV-U-!!;%+rnwTt=EPN zR-i%wKY{Ye5LNFSZK3e@efw5sbr!==Y&9AYhfBiP=qgxgoP!7GS~ojgg84=T*SkyX zl{x%y4^ibphmcOr8Qq{yd?c}B4z6w3b=MwmNF%Q6M)n!)rKQ>sDp;OQ5gaFM&J3z{ z>HY@%R>CsM_?A@?2AAye#t}|&Nm$Vxlr6bd-^oQaMq$vju6x0DWc7~Zv7s6{kvFev zfr>RD4C&5|eV?>3$XV$+#;AM0w6*zuMm;PlNJwJSs6@@#U(;E4J)`|p4ErS;9s*n- zrh6+xKRdI^6|P`db$*8k*q?_v22yR|_DtbNtsk=GZTnzxY}$(&CS&DCg0*VD!7Eo) zDab0L!*3vt?BZVi+H)dDj&|q{UklVM$PA$|DgiMXwtZ#Px>@D_5mr_fityO`ZzDOc zDG8hHo_@|`9OJJ&w1lQywYDBfYmQ#9fox-*#a~dTCjk*{54qhJJ_nrl9G&5Qfh*hh-Bhs*CxZJ?XenI_r&eJvCLN z3U0%{9tZARQ~y2xq4i-L7nxb{+S>_L3nMy2MaR2TtqLfI%DMOInMSu4$>nA56PeJ% zFc2ySpIcH4!Y^vH_^m9$hnVUQe_&;rujZgJw-Ty)L89aRinbAiJZ1K=e|P!Gqk)}o ziVq9W?#5>n5F-*z3=Bfz#8c-4l|94Cb#0JSpLzX`dzA#lBI@$B2Jd$y_ciP2*qf6W z%|llB^7_j}tL>hggj%gs_uc$eq{FgcBKna6gmY@U7rKV=YPj?#$9v=|DE2MuCU^Mud!C&4p0nr*y^&8NlJ@gzr^VQmTmLFNYZ zhMcVa-(Cn@AK%DSxRVwRYVIUpq&nk!w>cD@ z^O9o4)(>mmPXuUxBl2o7>{L+Z}3Po!zg!8X01G+y75r z7(jj0Zt@Q4d_PEqv|Ts{b)e}&5ISWGLPmzBhVFVeN3|c#P!%i~r*WUPC#QA*jC;l+wz%FI?FM0XF4w((+ z(kSY2)$(Wo*5&bP8S7MYVEW-=8eV3I*3~Yccl?5E`hn&M!Q?hxO77gj zY4-pVm}$sCHVoY0WhyTC$+mQcMmh^qhg8&iH%$yUUjthjFBhGu2gSJR2>-EcfPlLC zDObW=7DHc|4nI_`qqd8gL}FI5U6N6o8pGA)MT29-IkcMAGeAlr{>rznZSyiLnB;bstj=A0?#QW zc}pUD(|_t`j`Y>cAzdQDvK=y z!Ln}%1_QLq(# z0l7)kz{i5*y=wVNJjFimu!2=53lyAVRK@|!t01$+9RpQoLdAPey7a%8nJ0J@T@SFc1}`lPh-=1FryvD{ia z!>vc*tjG^@_=vXKeXMGNgJz-M%t**401BJAJ4q{gaQS>|lpiR;?&%l`yqtizdmgdEigiw=>36v5^EMs6F{XAlQ94$YYiO$5lcDC6O|dp zo2|%VH_-oc6hTQqpszf~LnKBDCxK(2_P9JFQ*uKMitQ1@mAX(H4hx_ zBfp5bl3bmO$Tigm!Di=;>##rhzCp1vXjO;oaQB`3E-IS;bD?penK22kWRUqvgnGw2 z<^|V7`f((lhGu_t_xmF)hl*RAHZD3o8*V)krcQtOxwc%+k|x}6$i#H;Gp6euA^!Cp zKR24%AK9T^g05Q28F;Ya5>lL1rvmN*i3mYxoa`Dald;EvBIfsHDBHRD3C3|3P4ZNCx z!G2dzO%0RFHY0}Ms9gz>t-yBw`i<0gYtU!^>c*4pK1GjIM5GmwK$qX2y+I;Z>XK$h z+KuUQ5mKg^p!d{h#Nqup=jqSIDzk-~zPT5K9o`{UvC;&+@ai2Y)^-Cjs$BX7xmoZu zy=Jb=o&T!aHFJNVacHHUgDV7zp%GF}<}jIfnVmv@9-qHyKn^CMxPCa)P&=(~C*6Sq z8M+7WX!}H%5A54mvw~5;NF;Ql@(Y-zt}9kFJzk8*MVG2X3BIXacVOx}(j*F~u&x9V z!ZabMa-L08wR-J#*jO4Bmu+@fvdRR@S_LqK{?*{h5%S{GMo=!RBT|x4=caF;{c6)Q zl2aEsQgm+gP^$Wi!hhNG7Uj{_2r+S{l$UY~{=5XykH9mC`jlQ9l8AoicLcRO4ZdYBK9FsQvfeIR4Iw^B_Ag2XAoIwphVrzM3_`6Y=AA zjNhGqxq0ZX=u9`#S@H;n8;Y_GecsUi(YqGcgsy!WNC%m8$nid{eXU+J`-a>uIZ#I; z1W3|51DS1G?dImeE8*M_M4|UY9w3oDSVu5Hmcf`{ofAjSNDD@(CKqDjPG5S$))%BV zO&Tl8WP+1S$|CIsE5#D%V82GTXb{6-QXa9Z{AWM-&yW6oQY2)>Ys@hHr;Jq2Jj!uq z>?IpGV4JsOsSdM7w-6SLD}(g);NuIaf{s>#%hXn4PbPi5c(lFZl5O;u6$3F@T9=fJ z9MUJ*{hBbKxopZqF~lNech%*8L8#T1=gv{ZJw%b^tc5K9YeHBBK${q zKJq>Rh7%*3i4)!cXl$-!I*L^gZEPY494qkXt#*>ETy^>B1EQ!a68djBs)UolPR}p8 ziKbR9i2;YLX=qZsqV8^G+kWqYoX=|)j__%v^=pS&*OMtv#nAf|Y<$M&+9ZR&yaBfMG$KAyg?Ol(txDy$5*~j_t!7CZpgoa{?PpCdHbYzlhr;Mhf!EnM=@6mpU3IuzVRMr89nH$>}H4ydFwb>(uS2y#k+u4enj&KtUW z=YrSN#(OxCdAEk=3n~mm8?b#E6Kq(OX^Y{P^~6L4-5QGZM;wT^CQpa+Fi1nNdEw~B z_X?=Sns}#pENxv@Y+08eufQZ`>uChH!6V>$GKd;BhoKv{ZEvh;QW_d5g5(E_0cB&% z(d^RLOJ0R32f(y@^a&!dM~|Lj_L142B89V30nsb=bq+dW_|Gw)s^zTBur3=WY^V9{ zswe|&7<1mWn>%8PJi*{JJ%W0>Ju(KpdI0tSM6N>=4Z!t#fk5aDC5#kbMi++QvR*Rg zY+XTZe#o!vf*eD2hnzyvIFV>p%%S?(?CUs;&s|%ZASEO=UY9g%;9<&XHu+AeEICm;+n$*^&a+|_ zUgKyY^Qj8_*jt(WM5}T5L_?-F`*5w3TaQ6ttKNj3;_=j9Vu>c#F`>IwuwU2mg+QYS z+fFf~2GzWOcD|qAL4S%&J@w=!hPi)T6Z>PhrY|D~PxUoT{OrvI^=A)eNv$IQc6vrF z8hSn7MX1ElrSVJv>rVBupPdp@i~?$K(`@y^P~GQhD#fO}v`HJ6V)npk5WShYNdUHn z>f@*2wle9agZ-mM}o*>Dhh1S;uuGX7)cL&qm3v@bq~+7ak1n+Oq>0GOrXh z-BaLWNwYCG=)dZ|$R*a=wNudJW;!VO59NRPCxHJ!3DijL=;;M3$0rHN9V5=x923Nb z`|#ISDh88ruMPI_JMjNR9TaXs9jQ_>d)4D*pQYP8u zs7HqRpgC?qO&=R367*m=qxjpiGd$0XF{XBbXQCYXM%(Uq$ivO=D@CAPM$bl()<~bO zSdm;c+R3LDMoV)^$yxr77a%KJH13j-P*nxbUa5GK02Ac9NL$~3x*D$1=rf`iUacK* zmbOB!lLUAB^iX8|V7b_QPK3j=8oka@A$!Ok6rK^_W88(YcO%J3?W!xrP@*<_)}(cs z9>W}^*O-YxkN1ncQ6<9pw?dZ#teRcraux=3GfO_T1#5=#HOf$D0eXc`UKP+b&SB-I z(QAwZXEh&!_=>+ueJqEtTRb0!-y%$b+DZM!3ig%gIVoH0NGWD4 z!VM3$G|eA@S|G)5l2RpHWt&!Dhb zNZzndwF*-V9evi(x#vyp~w@M6`Hr~qp$<9BaO?`its)lYH2dRBgqj zDAF1ghuZ>zUY#p-((=UdVR;WTtXqw<%R`AiOB=?`J8uL~9HXH}@iGzQK_V9uNtIZ=ht{3+JpL%bYmHqt_- zsfJ;+ote?v)nMQgOmu^%8<dB9E^YAhz&AP=I!^@Mur{JL>b_j z9z&>cG6so}qmi>8+g-P19+Ocs;Q3@%yWPFA>-Mwa0i#T^QslTZ0B*Z%vR>2nnklg(IXbm+`hsgu>kSDyP66y=6JsG_X#D~t+% zOgoQNWz^(>Uk)>qt(yu`lbUb#d-x5D{mvvMFz06eSL;@vH8~BiseN9BElg}}MskuX z_OYD|zW%Lx^1 za_uxuPR^$U_WipPjIYM~da}Lg>6ep1_pzYxtRdsLC-w3d3-P&p-tZj{2lfyST0~iN zkrKrQ`w`mLhL`=qm0HXgqLf&|svVW|8;qtGQbac09@c~%mp7#0H8#nj+Y6nN{K+Lj zUZefG45-~sZE~C6_g(E{htl1jKF`<0siMCJcHZwXqA#|>!C6a;4q`t~=I8O)@7Y#G z8)!TtwPGZ@wej)}reKAh_j>f2%kpIo))R+8FDqW@&YIXH-LEz?i;J8R`9|g3<3TH_@q(uur=4?=qw%c;*rjDoidGgB7XRgTS-Am~2ow zX979y{r$-7kF!_&1)tzs!%G_hE#C@hKQaLU1%Jx;%P&+Tk?XTpWCRskUE=N79u1j^Npc27_v=x#c{h&Z};9wC2T_$%vLxHq~z%P`y}h0>@pc@*ikl zQg3u-#;DScDZsqyrDZF=j_{(<`}1((x;m2<9j0t*biU=yb&HEikl@pw%8a^ee*(it zx=@&5;b6)FqLXRwK?w9)n;Cc)pQyFI5&5=Cg0gzd8d*R@p0Az%m7~Vx>&ClWqX%57 zP&IgH2W`Fki)ex8IF5#r&zG|^S#w@zJEh5G%@gD~<9$0bCPT+7cNkj`h-*4fo=uGN z2Paw-934W?=2h^ykmxH7`?m^huAthgCnoVmkg0g*z!|h_=^*suxug0Pv_&*CbUaA* z?pYbE+bvBHXCyf%PZ9ENk4$b@8_8yK8)S!(k$QhcJk0|IBfq87A+Z~vY+F7wU#Z+J z9b=)UBjeQb4X_hDvEjikOH^(=V&)X-4K=hOV_eai9e18reC7`53mIgp{PmZpw1a=P9TrRvPm8+fUr-CHmof(wVjsRrYA6FDg<0u4^u4PpYhi+1^4u^LwO{K#eW<3Ctb}TA z2*&pekJ_G|o2iMd{zwp$FeBjT_&hV?=^z2pA0Uc^Jiw97nELM4gC?N0cytbyE|wWG zo3c`8c`f8@vxWWfx|%Bmii*&n8dqH<@}NpArtagsU6C>9=9{_Njv=#*H#pHdnBR|9 z*)v)AtFx<#5LSX6XraQM`hXQ7D%ZUo$M!YBp<)c;X!EHALXW5sCd}32O#^X{6)Fpv zi8OW4!`CcsbWk?U{nYGulVk}Xp^c-KnBs=2StSu+<)jCZTah;y+H+53b0BE`@_p8Eei zoO~-JGmxJDpqK+_4ou1V*KX9{&Ng5R)Gh-(jV+0>A2q8rMfU=Vm?}SWm0}5@~zB%YFzJ zmkW2awzuD#T8zP(nAW1r)=yD2{9sn!wdm_sApeA9L0ucMCl^&;t{kH^*CSq?Qa~(c z(o}#uuG}?=CR-Wo9u~ za|>o+Sx-b=V2woTVIX^rv`{QkyvKs;jU%9<%yjQz7aVKJ2CMnwDzfokdA(|;P{t_t zY`}uRl)tON4VXIg*j10vy%#kk&aKjo;TO&-mYP4#aRCmGH>GwO6zzKK;Xdub)!Q>v z!?5#yr=4Z#vNM^h&ZCgwD(z|m|KdO1pTL|KOq3>Z&9RWsD$X(TF$d6W=XJ2xtBg0OX}4q~Vx9aWFQw^1 zvMT5*k#C0k|7v1KU%U#xg=Bd{1O|GKLZI`aIY#uJK`I6!GCeO$7xvC?mEGhd)wylH z6zwaj`{AX0HOE&0TEDwfOzPe=jE1)$FZ`b@(B;Co(Cy@S^@WVQDQ53y_bDmH1qO-u z*Nd21Qk#S6lXJXuJE4Boi0(3C-w8|=4MDa?WzB@WbU6gk0vNJ8Kx#lt-0Qweii$Iy z0BFl!QjHq&ReItT*U~Jn4pfh&gp620g8_xxv_5ZGzYN>rxM+6QAhD z3DXxJwQYA+syZBwl6jR@JJj8Vc=U!YgsbkLBS71%|9eLXwl9YWOznete$pwDuf zoen-h0$NETIkzd^FW+k(7pKRr`#&V$uL2b({_M&ksabsBY-Hd^v(lF25yuDzmmBUR z(z_8-AQ{wmgenI(yr8L-H`xZHC(-oPD>~idaQQ`$RQ6sKjMVuo(Xll!1!$ziJx5(q zCP6bBgd9oLBfTCma6~o;zZJvJ`RYLcoO=+1eD-&^g$Ht=sDAStdN|A#uGVgmn;Rm_A$@dOzpqs^93_j0&%*Og2 z%paP0Se=0+&1r=qhhpv)azV>^n&rak zmDsJLTOsoK)okdzA2ETla^fY1tQZfv@AuvtlvddI=?d2D>QxQIWygh8Rsz6!{tRCH ztWqes#JXs`qKAK(l!EE~Y`W;ntMUKV4DRO3xcQtY5zLEb8%+ffGKUVR+Ia?%$}kw0 z1;yaxX#H<5fahR4)OP~?my>CxrpAG@f|*z%%x=xq6Gnm237CO6@cp7fQ*efho!(%4 z7!h)(d~P(ycsjKDiPRUjXJQ14o9X;Y!|`7kk~bocotA)`tm3Us1uQH!NI7W ztcryJJe!m|s|7!dZ;NGWi^RSp?XfmM+1Z!oayJ#;k4_@ht0`!La$%lE_dSij{kyN2 zA)ReZVWMb;vD%F>Gt*{A!#zkVt}Nz3P&sn?N3gcQFg(_8o*mPKyax%?mR)AOuC2Ab zMg|RiPE<8}Nhb+6gE{ZUPq-`*uIg7*5rE)kBOZo#jSi_SzNA}{(7HByl4nk4htOC(_~8dY=zZ7R#+beCqz`EJdAep zjw7v_e|HDikMw&+pk6=kXx^8q1-{h}&Vs+~;-N)Y6HjXNDx|LL!HKCV zconUC3u0A*;Y&;vq9qfV$S~^tyXkf~6BJXc3Wat36$J9L?qaDmDh$~v%du3Yg%b~f zPXcWsIg1cO)qkMBycs`0RxK-T6r3LBicvad$#=dB>IzC&$9bzJLK*dwEYQMrJB?DIBY2iAc??J?BL0(ZVj-Mk8^;pEYpnm0z@F z3-0>%ISB(-H}798MLGG`2^V<`o2BDWKq+8b|M;(P>4elOUbsfwBs78nI{7cmwsQ&C({B-D!KI#S6Gu!gDsH6Sg6RlOeS^@1zw#Pu%t{VO2V-7?{5D2a@GS?s(UVP31A#F(^uY$*h(4`N zwY|l7(1MSf#4!+Eni*|*!amdNwmj{3&T{I*xwlwx?eW11RE<1jQjb7HTvj&J7iF~i zd<|~8M$FLBxoxLh;&UA7*K7WC2jL0HpmBrtG0DvX}FbZK4J7UVMIuK$cca{wrv-p7!0Nd za`pysHD|54m}F`TZdep1OnCGU>>GcsHZyWfjU|7Lh-vlDb16R0qfq(;$${!6g~~Mt zY(kRonJzqCD^&+5%Y^O(hm*c1H&h=8g(1oRXV}aky8cHz%S9+H?ZpPtsm*<`OJ(|a z21WYlXtDH<60iM%Ad~8gHA5KT4iF}3?#HOTy*2Pk_%;C0(a~I*tzW^qXnThv*!me7 z&UigYbt`wq>q4y?gh7cLwWf7_HcgU!GJ@j~6lGcK7gb&sl)GV;j&RdleqUKc^g^g4J7 z2xC_Kpd2}!!Nh-dnCKZ4;YYY(ZaBzE{|8NLSHu(_UB2o0r`7z)7&*dd~UkaPqmKP zf>@TdRqwMn`^f~uu$Y8mgOm+&2}tN?h$%oyCR-t1izEPO#i#nqxh$6_I5CCPs$cQI z<%Iyi((-3u{qV_OPl0GX-w2}Vj)cU_pw#Ohe67#8uj+ThTfJ!Tl5~9zj~o*_vYv8_ zTC1>aug_bmI6P}O{SbpTth9=`ci4N--h3<3*)Oc z76bPjh=Z=VsyY*`C}+k&XdF>Qpp`MhM1uCz8y0skFPIRme6jce0MmwF-z`CxyxHsG zeGedw8w^Sk3K{L0AK0wTp(W5<=J0b!kW1~kB-?V7D_tEVBO@Oq>dg|(PhQJ&*>q&8 z+D4Lb#Rm#{{x7=TG03tlSQqWGZQFKr8C|w*+qP}nHoI)wwrzCz)jsFm`}RRctUoJa z#GE;Dj?DZ<5)NezB&0f9hYKNU%v_+egVg-@xxWyn%-&=OJaJ(mA1_EZ2Gdh*E+d>_GBP3pR_v+gX+5s#b#WBO z!q(b}st|+w+kI75H}c{sjWCl7KhlGua3@r*Om%y2((3YZv+$|rCw99J82_I>^5*HW z9t`l(%ihEA?a`&Oo5(P)Q@vg57g6M{y`sz9Q=12uk@M%Ic~lAV>1~hnwnM@j?z

ioaY|Yarr3=ucBvJrtBOX|CpqS%p-BVrrz+YpzDN3 zz?yc354rhav%%Gh0tC9QH?xOxfrHWShPV%pc@i*e!lrMy>@JyGmKStMPU?-FcoE5y1FNXOu?vX^|i zt&%%b8Hx9(6+3aVzJ4{OcqS@M0lDYXK_` z=%*E7*I^z4*5ANZ812xhi*~2SO1G`Zo3p*Z(WYZUBcAVXdQ8VKA|!GF55vpB!*vKXXN5yU$twjU%If zTFO(ptc!o~%p)9`iDu;dE|R&{2|S63-rJ!D_0V(~*~JZJaG&=EE)^f-j3!F(om~zX z1tTf$J{6 zvOQ7QWMk*$s$)<1QabEMTEwW#bsvCEWoef`_qNAd z-!t}|+*B`W=H0QMI;Dw5wA8D^fw2tYQl~elXYv&xEg<_3r zl~7G)aHhDkl-`Lgwp`Ph++M1Xx@s0RoiEMjVd)t%W#4AAWmA_Y?Vp)$s8};{bK8Y3 z*=&k1C}GOR&`y%~P6hy~Ug4EQ<$X>kw&+uROTPxIa4Y7_aL z(?u*p=kr~kLRP056M43f_lmQYkR|1EwopoHc0fAUK8|+speAB}7g`YBge5O>Zmg)K z62NExc=8%`jEqi3I8V!M_9wL0a5=C7w0_FLK&w^YgoDF#JmG;dsi2%ivP^D%X%5nd z(rvXqJa!6?yhzjG^|mB#q1yn5QbpB@A%SZ;rx?h?c^2hbwS2Xn?D0Y~Mjx+}gf|WW zCgT|i#?(e2J?K)RhUtYevyCN6?b~XwRLDrJtID%nG9tgtrd4ma3h$ax8m`rm!C>(O zUZgJ*;IK6BFVMT&$1t>ig=@FD%ZVByQyiMfO9>(|R_G)|V!7a-TWgi-RmCf5@`v$X zpY*jdIlcrV4f4leHe1U~o?*te5k~=T>6D;V_Y%*s7U6z`%*T2=8{P%z^1dX&JM5pl z9yvAM{t-l6WXns(T;-Z>94+-$Fqap}VZNI?-EYTwxaj0HD#wyXYwu-l>Bd>C&<;}s zULPATgj=SaA1xR@t*;z%m9doZD~eb=t_TmECOI){Ryqv2BoNpG$vmYd z#v>M`E##8CV%;5$*@tXa`FUFG_h8F~!X}VY71^Rde6}{Xx1S(n2#eFUnHHjw}rW(q5{>JN;P_tPLme#V*AbuMHXoFj7Q}KB$mu169Ltl91v78 z%&Ch;8y<2s)m5S_V?md9JlsCJ(mVhNkruG0CyTipPFS*F8QC4ENhIG5;YV+GsqQ|} zrGFAK9G0FcOv@#hT@COX=G90U)f1gQ?M^lx&w$*C?m$L}Q~pZw*)fqII@@Z3<4stL zS5vZ*pL}E8T%bgf)c9>#EV!&Gno$w0Y!FHP(keF{yKIST;+V_Xx#NT{R)7G5#T^Oi z$a-wvA&|5u-_fP7VEo7_f9>s6s^LKd>He5_b?g3elEt~M#r&m8W;_erZXsZ0WjWgB z1_SBrChTI(o9BYNg^ZoR2X7YJ6-SY##!6~2RBItZRbE&o>|<(d*={X^JNu#Ga{8%Z zat8~(Z{CmS-GxP;jAObI%D*t)#&Bv^G)sulN?5*b;QnVOOuHQ<9xH}`m`6^%HGWL_ z;BhpQ-3p-TdM=2=slIT`C~1VLyWGM!A;)Zq%fsBNubGs~7Lqbd8=~Y%p?H2HPjff< z`n#-^xyogQ?^show40x)+PXppC5fbv_)lw5k-WM(Vvz+Keg*)Lsjrh4CEdiTCrY76 z+gy#NmGkqg1#+5P(1myU=r)CLoKlr|j^YkY>5@B33!_vTYeaA}3+dtp_E;QznW=ef zo49w=jeM!f?63dfz#0EGEa1$3vaJ`Q%YVk*oz-aOmS*Gf?3F-4jY|BMO32LkCEfZv-kFq{;ZvOTIIZ3M1VJTL_2(Y zSfz9Mk)@(FRA$>~L`pj*B}p5P6hb&UR=?|ft>e9YJkq;$@fWF8-Xpws0{R=xC*9l9 z^raJuWr`4R&f|r9`GVvhiSsI{3A4hJPD3cy3aLo|h>`Cu=&RJ){iFjI^2L~pW$~(h zaQJYCN?Hcbtq@F>2{5>ITax!-*zJLFc)c=nhrRiM-k^P2KUUnVK!M&qT6|%dC5J~U90$|!68&}wrGv*vLZ!NwJ%bVptA*x@^yk7J{hoba36?b)YEnZ5H)D_s+pRJ97f9t^UkTii=8#y6KDibQ| zyB9|-%u;^wWm|lf78GNda$YR3kT7}HPlxQx0;STjA_5`z?xNpQ-UE6e9e#(CiRGA! z&E_JVuMc@c#-N2t{s8>agsr>|#3H1ao_&mNK*=JM=EjDbHu#TCRwD<}v!K@+j+8RF zPS`)=f}bsg8Ln(bR_T)Kh2lNapbHsS*P8Uwfc4di2@HkifwJ4#a6R$GRv{y~_sfan z^>UJhlQNu1G}gBkYKM+TcQPB%@4PR6v0ZZ$)#&be>yH^+La8n=+K6kpugXC|A(fX> z@IHf_){kEI8nvAz2sy1k=dvcvolH%hJ3f~&6&0GGKIQ4Ghlep>nXk9ND$x#Sc)9iK zBhpL^2SH4~Jq*pqtP~`&OQwvKmL_<28ZR`&;ILH41!r&Qx4T_PVpNjdCvTR*jT>kS zri8JRAd=AQxqonwm}AN>PgRkxe-U(dOH6%IRH`KZGKYILTr5XgU1`qaU~yH@$z9$+ zb{}2V?*2%fQyP;4Dcck$XOpX4S7bCbkYerhNWiGUVC)!7gg+9Aso5?jtI?uhqzPbm zK|U8rTCh-;7a`rS)JRoO!g<`YPo6_RfM_)S5SgXVP)^)Cm!85Sq+lXr+lJ+#gR0ke z)m%Ou^0FC|qY*&?;UrIxXoaALs@X@ua>~`Fj!25Lj(fM?M=Tjm=pL77d)%RDeGQfs zc82v!6iYhR?L5L+3AP;VYoVOxCJBGLNoDBb&qnyEc=ffy3FliHT@BOyu*J{MUqp_2 z(fJITJW$OrgeVaiCM?8Ey8T?V&JxC;gv8Rk;t|oA8wvt~oV4W;a(SQA4i!4&{}|)W z%D=SP>M+gaaHVy;$#kr`KHFpsh!VCo$Gu%=Z1U&TDZp(41AF6(ZSMfpaXaY>%%x@N zVg-q&1KXD62AFXEke;C-*G*;K1;?#)sg%Xj>qAzeO=f()(SXB=2b&Q}qODr$#DEMc z%@LRAck|8tc|da$W!w)!F;SEptj;}P%gr{JUIP19XA++(H5}AYSC1d|J6B$>vN?p%shzoLMK z3v^A*irRI`l}6&PQrv`1H|OEJUK@iNP+rzLSvTIE+btP`LA<)oXH9f@Hu3K6DrdV= z=pGlLrRN^ioGT&g{2Z-fYjmU;1IvOQUZi=91oo6iu@4SgXX?DfqZb8L!+e!j4zwgm z0IIS|5~L?65oWpw0ICeqe2qg_5`6Zs5NDiAS`7}`k#3xSyVnvft%-~tH%k_SQDJei z|5Tetljk@mC&Q~xsMhP>%c*mFmP1@tDqYJ(8tP)h^%7NdOa{KTXG@m{urgtWFvXJ` zym7u7X+?}&@$?&m!OBbTe9^ibhrdB0?Ey~xfusxg9oeP#8z#JY1viZeIuygA5+@u@ z&MjR;M(hKe(S8k1M>R|=0biZe%1zndd;OAgOd-yl z?34WJEwwh%B=kSE9PM-q4Xm}P-BU!^%I1P;5Pr_{LDn!`pAp7KF5dpN^FpjDzaTnq zY|Q}>46Ae~WN`RfN3ZuTpmeSU#k>^6(xPc-=G0(j?PDu6OC(Aoj7!}WjT}bL$O3MB z7=9af@gAv@PLX_KQYagG4J2VvYZjyp7sLxHM>MM>Y;j&Ajnp|{oL_*0)8j8MQKvLltctaM- zk?;svzCJ9a-M~1Cus;Kdl=NqTn(jITDttvIKMWAr#^|E2IebQS3STJjyORUuxDofM zt15N%XkYpp?5$yt2ZM-tkOhRFFfr52K87X|13yaGMce?YCPU$xdQsZ3>^z^DbWq7IFU2<;KzN!cv3JAaO<5J#7Glz;xlIOSY zoAUF73&4;@ZK|qpFMm(3J)|`AhQk()Ojc+d>1}bndfITn;!S~*Bh8fR(=|_L#JsHU z&AxO}q<$)Eu&c>BnjSDvYqtF0#UAg&y6yn`V))oqS{3&2lE_&X!iWQJiv>q|`@VMt z62?Ro`cR4Pt-@fhe}X+_M~5V7(43$M%l$eqQuS~e3WOugb!l(2ZDcL-KIsyat`O~( zjI-X}`JQQ>`tZpdd43jjgA!p&DikoZza_GsE{$yWVek4ZaZYH-{(p`*0O^f7ye`0H zn)E#mz-613N@T_Z0jRSzD42vGa^djqLUq$0qe)!i2-H->8vZDn z6cgp$T)e{;M`8iAcTlOa$5dy~Aehcn^A63T?l@0&$_rvo$bFa3aEpiHM7wXeWU-{dW4 z!ka}NPuCD=8!wq&+~K7)!G&NqZia5+(BFkkTGmo?$r+GMq_KyZrQQ%Ee=bRyv;0aZ z?%X+E$Fb5_@>goD3Jwa>Td6nWX0_)10YnDJY_pwaPn<@;O99!tD_8q;D(RL0nrqh7 zPBgCgldaBvl}+_^T+j7Ix{vzn`2@@Ct=`IK8X3T42zU%3mwIVUGk^l^_a4-m|NEO? z#R=CCo&(_fYe_PU zuQ)c)3$U+p3ButZ;kbN$uqRi>O;L8F|C8Y!qlZ9t#By z=rhTiH0R2CqcLE*(i&CmjpgAdrR%nLr~b43VEM&JR%qVg;Cpw}e}4WyDe3{%HUXs0 z<(^;dCdv(Bf98ARXwpl{iiA#rOnd;hcFK_ND(W&-o5o=!S;uoAl+hcW+5`_v=p9-T z&mbbB8b{cCeJtuQhZ$c>N~f4E@RUw0_1>OkN-UrRN+e5bYZgg><(}!55pm^MmyHYX z=r&U(yc4hbNz!6ySLAQ*7D&o#dmt{OvA3p@%ORjuuK8 zYT7x{#Asf7w|m}d&kh6FfIxDk1eB)?p3J}L$rWgi-@u`fO|N*j7V07V0Sj< z6<{hOkHdg4SY1sdtHC%Ny%*{~gIyEg4DWE<0O$E0&_n@eV9D{_GEAFagV}T*<>S>+ zfQFJw9^FMd2X0C;IxvpG3a02j!;QI^Zq0L6 zJR?L3uk}{X@!c95s!dwMDaqDsCfeTcNyNe?Wq7^#Q?c-m?!T;g#~QFIpNI0j1jbrf zDi>{afu`|&a$jzRqLc%vCw62mLDG0nY&aZxhOGRJiGx5>IO2+eG#SbvGgG0DESAFF zeu#5bF{EwcxM)ngA65z}gEwojEuG{J0PuonhdbsQoV>tm?tP0L6(~k4a-6Xe$Q4WH zOqYbs!}TUo)TbQxJ;3JeI2{E5OML&d9LWq2yLa~cjxq9_y`ro}OKw{V*qVIb$7iQU zUR@&HUG`ZT-Kp=YC8@0rAm-~va*CIAhUq;H&wN%A@?lxxIDZ*JQxIX~bvdQCPQo9d ze{J&{wBYvut{L>Wt12{gfVZlRCXQPYbbYz8NODN`5L1$EcQr#|9B={;5o@DcXzsgI!mIz(WOoEy;RAOC&*X5Dpf*i+Rg!Q37{cT(-_x*|l<-QK7*W0< zmP&LZ-h`C#3Uwz;lHut|KjeQ(eKZ!%DbY&8iGDn1)89`90`J828))PT zhXmsCBHQJ4IM#KQU_-~?+GLehbEb~A{sMwZsTnGZbyfZ8k?Y-FeM1@Dzu05Y60Zil zHW`N6U%WdXV^W~fDK0~^reynP;&wIVzV2#8ikwCIg#LhgB8v%>b~c0IIwNSso&W_hiihiic-ERV z__wW>`E0d+|FQA^Llgf#RNu$V)#@KvN>!*4?+*O>yoRFww1PW;Tx>v^VFqFEdhh;Y z|7njcqs(Wuh{E*s3}M{84GPlhv)Aq>QTyo%eYcQo%3;cA{3at1(W44rzCSli*BKsqlV=zC7K;xaeSN~|&}au)*n==Pz3N~291-e@ zGNn?<+q%CwUvOXtDmF2#y*vqa^U&<*TSbv3i$DBXBUgO3i@r$+&BeZvOr}rXR+RnHJguJ)r*2(iLbOD zahCD!aj&w)S_5A^UwgCwQez@d%o^-X`?914NDvP9$?tx$Vtn@(0j)XS&|i9!x@Lwy zxbyttDSgZzU~~-)fdlN9EG`#JO&dvc9hb~sPE!~umbKp$F?B{Q*6vfdmgnb}rTJG^ zik6FRQ|QUfhPwYI`!{Rwf4-1rM0?~akYh)~V`Y?RprDM_fM47}LFt*9gUixDsA*_) zjm@EffDoO;i+>4SMudmw$y($tmFCHnf_Pil;?V8loce7t2}<=FELk zQ%~qx8iLOq;@;rzN>v5R6NuQ=U2%!Ow)^rxM@i}$>j4zJ%X;2FvZ=P+{XiDqM4c)@ zoM9Hn^k6Uyf>`O)B}lGTjiCpDFw*9}nVpEa;qM-*rC$iCEBiSyIjO5S<;uUWLV-d~ zIU%C$4G^UPKmcLvPF#M8+o#!ypZBdJC^kY0RLk2Z$tNQ{m~&0{HX)XEcSsEi=Y6thqhNOAMN-qa3V4Q%U2*P z17UoXjcZ%a80^0lGojc&@yqYK=hVQ!vA?sFdBlIPTCzRTrd!MJyM`X0g$73Lr`wt+ z*NH~Y)+_+*(-A$sarI@0{#~Kijl}_=mk|IW5$*oMEYM259ov*pZ#2wR#!&|| z3hd7Mywcea1EcfH@Iok5EPW_a8Ex~*{9@e=8xN^+o6crI_nkZKo}dCofgT z%8#>Q8Y!sRzv;y{G#xgN$itf?alY;?!ERSM6xgx*&s+BYG+I0Sz}lt0q>3-#s|}bs zQ(v&{w7&#|UYg3Au*1_FObirj+-r+9b=E}eI6gl=z&x8C&@!XN{k%H8QyENPhq_lI z^K;1ac*IofO1=h;bte1Qh(=}OdkRW)pr6~kQ`wzBg<&mWO|X!}J?Y@x3^AHAVxmgs z$RS}&87wh*(vW<$A1URu>DQ*nkwx1YQ`V>lu03c?-|8!rv53gt4DL$#OVw86n0C0oi& zUvc2)VlqKOV8(8~apt0*(ca-{Ie$sS$vD` zVb*daSF=5BEM^Bpjt@Z=o218<32E+Z^O)7w8?=rB$`qA6$`q#8?X8a} zpGO5oWf^426@kdW)#9J`bD@<=6nqe?c2Be%S(F)sON*XDMPF(&E!%UO_ZW2AZO3%< zc=wYfjQOL9!3#N@9Yfl~M1`F@P{f%oZU4<>F}{M621>2%-r;YsDXC!+XtU|dV(}ZF zof{4`xryHNLKPZo0av;ss3BA2M&W8rfDTQ*-Ofw+qL5gb8WFLt`U*1-bg5GL`w7ERwlMlG4;4U*&Y34;33hBY_< zZ#xkCU9a~O_mN6ML-iVE=3)1FYxoblE~$Ep8qlCxXk@NGB(jys)rDVM6Sv&xP$c0S zu7{)N{}kZ=<3Q6^2c(nbo5w#|p$x~&h;bq-Fy$;td8H-UK^>8&%Pn=r8<7~ zwklkY(U?joB@>h9NqvBTFa@ybne01>E>)S^EqJBkzBc%(_Wic?(L-ZjUePbx)gsVJ zQ;D{ZZZ_4*s+L#gk1|-eY68J zD=o2jO8c+m=eY0{N0AfpONLKCfs-Q)h>K|5X~cy-61PkiSmo`K_-kyB6352*E_IX> zw`^khJ=xygWN*FH{O@q$`}#ox-oXbTg@5!1?@uJ+R}HUsJ27Atfw0UQd;*!Xe?pid z=1vYAzn+;nVuWPnw@>D6NEhl1iHCi-|#op z@QPn45T<7x;M7GlX;3~ZkrFVg2MT${HxnyFtnw9+*ZQXfg5sR>XdC+1 zEgAA9i|+vCJ|_zC_zl!Zy#hQ++K(b362*$$@`^q&j@f^Rr^Q{APIkqG8YwpVZbcWZ zIfis3FJG%`sn+X1*HvmsB2Q~MD_mK%Rx`f;lsod9b6D|awNrzA^G&~6F(@G%d}Guv z*>4l7dte#Lhu^kYG6Se;)EBOFhyTYr=1&Eq$#}|Ql9I%v@$2H;cWZZ0R`iEY?1{8T zp0jq*BxW_fik%BvroSi-mj6(}N?SDV%@qk~0004pa-&L+BFn-J z&7hNz-HPLm3EoFw-#>?Wa^vWPqp$=WYq= zKOgY5w`aMCwf7cX8YXspz15UK9#fY0sX?Oz^*Xt~5ry+*1W9ihDx{H<AUXEI`WYo$OQC|JV45iU-C{Y%r|Cz9vyPu4UhiBH z3s33?LviFOtO-3MM~_QP3tN^#xTJdMYCSOlOoanSbsNNME{tsa%eul z1rBPR8yeQ^7C-1T#N;h22ID>CNRI-ha;5q_$aG>92$lmOH_#Bmv(96Z z6PXOCPFB!)%2aWVc3==<5w%CQ%FVW;R}FPL^J~Cj`UBJ zTH#4pu)F5@kcsKvO?ZX%KvCGxx4K9xCnZ1YqV8sKiE@d1CDo8as&sq;gvOT%BAZRj zH$3!>eR+#vtU)=M6wtB0g8;PRu)SwFN4sy^E<}v2slT{n92arZ#Sb+j+GLr5h2L1EMIR11BJ%%TYBs?T`%R4-?t&Oz0O(jxMA;$KSX{MJ}wN2 z@=$mHsR=&Kr6v86cxjJDkN;|`f7Oe>FuJa<3Y z?51;XzU7Nb=P}*L<uiIrKX0$6-O14Q2J35Mf!D&G%Ab%D zv{Ku!(&B+Pr5zPiRt06g^fcHKR=`l|Be;S=0q=B*SRJf0+;q=b=P|9PDW#l%ZyM#? z`I_f4&04DPn+KD{+_&60=4W*NAbS|98MjHc{1D5C-Dk4(PPbZwN^jjzM;7L_}H#EGuTYcBI>>O~Q#Ygn? z^glr2W(A`J)Ce{>V+4K(1vW9V#{@y4>FqNh?nFvCT^1^vo2#gT$5a=P>sxWWve+$J zzbK*=?QJ|&t<>-TpcNJ(;2;3Ov_M-R*&SOHhtorlOC*wlQ<^Xo6$=$4wBlbBGo5So zbN3=MCt81Hb!@eOD!bYYc`p+mY$n;(-KEQbSF40mFyne4;ug26y5^MTEWUcA>Xp?X z=L`E&QBvzP?u^apgHrc9KQ{B*KW?F<=+xxL$b)+u78bkC(Yv^b^r+#;|K%4jQx-;M zDQTKf$q1s+^@&Rq5q^d!uDApC0nkdB zh?fZhfdI|!VZkj>A@4_!I1B->klJZ~&lXIShEt%;@X<}>*^h#F$b_G>Dzy&Fw1ESL zy&FrgL!_ki%CW17(^N!cKuPAmg>-mz6z64ns&-$-E7(Kaq#q?jsBNWp()$>N~7pVp#l^mLE21R&QbZcHMb@ooHm|gr$a7 zfb+N`rt}lVi7$#T4#a@V_x=E4_jt**8n!l_uVu|NlQ34(%_IfuT|dn<5s0<0hUHu< zr?6VC9QoN4^#|^MH(I_B9m2q$cp#1Ti4@0a;(3LcMjQa&gHhl~Nr&he7libaEMEGu zAz=<>iRz@p@^N!zo@Be2i3F{#=zkt6&(B!A63d$n^2irMm#+9~rwKgvO;B^jsgIcz z$0qEML74jb#nIZNCn84S{Y@e10?J75Y!l>iw|;mM|IIi5{k>hQ|8av>yS6&~Z71}8 zDOvvnDBhs!KmPu<>7;+6!YFhuQ-6Ud-yYX`r0O#64J17L-j%NvH+QFsu42tC zd?2WK6myCD4BJcz?@RB0Wh1WSH?3d|yDp!Pz`)GrW&nkLxk0htzedzKHu}K0ID!W^ zmWP_0UY{gC;^yd*k}Dg%TJT7~KAnQ}!~r zHXi1M5O{O&lYZYJ{Skp)gVroZo7qxDS<0kXiUZlQ&?5Za!4Y&)(qeiR*1%qi5k)G} zc;7#FTgD&5D8b4soUpT3OhP|IGncz<<6205i`427GKFfLLWL zYJTIN1)q3P*gQUEc|^p7`B)W2Xf-(m`+LRG12rg~#|~Wsfd~R^818^kckZ?s^1Haq z)WCyC?2&MzSTtk)?KO~Wmh;}l(YsO4hq_ra+1Rx3g9BtkG-R{O5!$5y1s0cuN5_Du zGQB}h^)^=d__)N(=#D*oe0vnQh1**J7Br7k%ud|B2;v*c1A7`UZXkoy^z^=wo5P4L#B$FK zt~WO43m%vR4y)M`9hnwR)gE77CR``Io40=n%XeJkWcK%&%e9!Bm8@xQ>}Z}WIC^?| z&oA}&8{W*EuL}t|9&?lfp_dU+8NodI00Scvc6Cgoui?xePO!8WE5TLma0Jv0C}zZk zgGK1M$1QLOJG_Wnu*ES5b@%TfpVvGA$aX@!hK1R9CHcQ{k6tylBtYP{Tyxzj$;wm1@hNdknQm|HernmE^xv&D&y|M9B08b) zn@I03oHDftBjqu;1O`;0NIn?0;9!R6_m7?YyrY5kXrMZ4UF$z#fhRTk8P?+dg4jox zpr?BsJtT$MzdngOL^r=eLC8eLgI<9~JrLdmw##jZXbcRT+tdye6;Kno{FHy}_b-{> zK>*g)0gzG%%;-h4BcTRc#AN*y09sA&0nshwGw}Xtv6bBXW~$f-QcezM7A6PY?v9c| zt0fdH2=wRiJfwO{n!YlWUe^0pI(DGtc97N^-2T8|L<=fz7`6hQWa%g+Z|V;r>cc{lTvaP*3oy=9tZv*{=0m^IyS-Y2V_Ix>dq@N{I-+-D>N|^vUp-`a z{XVJ=stF^X?|760A(?$$+;4Vw0`#;&nu3z@v>cJ|@D0Y30-SVY;kXq&fI3+rGhT1C zCf9nv!JtIM@1*gU?R$b}{a;new)NloFwcP^Q8WU!FM2y!cTvYA*5cF;*9NvX8gB*I`AE2?g}#jcy|uem_fw#f8yhx&lAE5T2QML{ z$+ur(UjeYZ&k=k56$#bn3rjkvICnAjxoKI2?0SsejX>ZC8Dww4qVEv(wX*{PABF8(>wKX6uX11mY`BQbjJ=aKzk_V8^yMI z;OQGWn=Up%ab-BM%b?*SjP{N(6%7_7p_|BVF!|D)uZc`)9?c~xQ{+b>vj4RTeJ_Av0u|(Nh zXksv&gO2p6VB?}vGp~VCT0{A66az1za#?h_{|FD&0r7u(0Wjzl^BPZ`_?5FFKfZn$ z+(B_v0P1m~8O-io*HRI~F*;dDX8r_XDt6&Vu}DbSyH_wh5N+}N0fk1}pWb;aN3M{s zeO*VK3$hc1%5pLupyC0l6I*a&_Aje+BaY~51;T7J1@nIQlA`+VK<+-B8m0CLv}A`h0b^Bcku4KlEGM+MIZf=v4i) zR;}pGNWVkzx5{o~B#3m!2CLU^vn|)`h$`>1Wzi05j`5Voq@S9PP~KWT;4h^SaY&@Q zS-Vy{hDPt(~q?=2I!k>wkzl+%8N+8xe3();uRzIVJ(x_2-I0XII&b%)akjV5E4(()}d z=>luFfCfczsyAAK*o=L*xM%lT#1lvh`BS>yf{_5LFnspyGn4MigLu&K&$AeLvIzO? zn{)C&NKcLSA3z@SCT-Tq0 zB-UxF1l1A8wX!C(CQfKx?}i-~b*Rhp%ID0*u->;`p>vOwIPsbZ7s%z|bAx)Qct>4( zuZNC9;G#?XYyf-*>sjL7Qub7aGS~4ERzGT00`-&c%K1x$elM+HO32MaH?X9Eetx$5hVVdY}EiyAY{$ zYnaam{NX*nH+w^(G#t$AD;MhCyY@djS#5yLkGAZ~(U8vV$>Zk9rO9mAvJM!dlcN27 zED*-|U#`Zk-~ik~iotgv+{~0e!z03FD`oi$loeQn$6|^=CBL(ht;|59I^f;hf*Oki zK)eJEQqwg0=K7$A8f`l*s>CAzo5TUqD#2e~UdSmE`Dq}8zC-+l`;wBAdS+$-ObQ0* zvcG&CD~WH&kq1Y_BnE=|Gj98BeD?kvnwGLCG3cK`d$I=sGmHPILYj%xwbp}eh_}gC za?H-p*C!v*jtaIc?qXJ~n;DrvTw+zGrwQX|@$e+M9HAFf(Bf=}zX@tz=OzV1{1Fiz zfls9zi-X9^k?oC@s@^k{Ufw^8sY5|0tfxm1eh68r2>1#bOwY{V7Ze0CAr+ZpmKdna zt}qgpljXM(YOC3458y`zxA*W!+%tv4a?T=hT+O|+TnACE)ar2^N6F()kDP{AkpP`Nr!?ta^e+8 zR8U@+I;&+qO)M>;iHL}*w&j4VKG{IBbi}MoNl%;vIke3UzymnVFL{GYxasVYP|ylQ zh3`dqY;<*hbyrd{i58y&$$i-p zb&45lzh`#*%U|pROZE$q?Uc)ffxufHPu+QrMdlJF^5nJn=YqodtL$9IOvFQaShAQ~ zVG%WM3V|r!IzLZQ6raPX^v@aN{Q`a%U4f)GVh!R3l49W@;k8!QLbvYjRAInwMLT8E z0*mA;JV%`Zy~K_6lTU#~pCW_tb^&rnprqZ zlhx7B+h3U7;YYIdzdP{Iz+N4B3#aMzD>{GZNqXaJWwg;5Ivyno500I5scBM&gkNnw=;d{iN}6dFGshHp zmo%lY7wT+<3`U^s#{jvV@ZJJR=J0^yIwzvVwk?FqAI#6$Tps&5#+2Nrr=`uX6o}MV zG(JO}GUa#Zh+aO9*g(TLIaC+lisq0QN@B2;MRoCL*(@AQ)y43#wY??#^Vpm^_}Y7l z=`f2eIz^ps>FI-~>(sBVKqnu4Pm#f{ohY9pbA%CJCVHt`$O*0ni<;S-mW(3tXv%4H z`Av{4upN37bd;(5bYrskQxzMW|4 z>zyXKY9l~1K{%D(02PqbQABC%@Cgxko|}_-1L@z^lP?IOLl&Tl{1DXNpAMgyBLFpN zZcj}8Pild82CM0TRmjQ=d|=?FuyYsmE9p^f%~cfXWJZBs2^4jaPwsqeVGzHaH*0); zKRm8C4qJgjY?=mmRO1ZUZr@C7AR!fJi^sAsHy)m%gY!RWE(dA1YnOq3WM9SN9&fg^ zT-m5}jj{P64b@#Ivqemq6J|yP+z(o_jY3S!W{N?G&QOJc+I%;I5}{Khb%Cg!kR&vH zikR$6R({f#>%HyYRrS5aINKm*o@(lTeCyG`P$6qHGj#@>3)Pl+T9sTdU=Btc{zYe= zd9&(%l8ESqAYocn#V0e(Ae86Fd+w&_*1|*hRBU+mAT%?fQDsRq8x+^7`@%3`VK@+0 zG7Z0SOF`B7C{Y*>Op1t}+|dPS!S=sP8^9wagWOo)7AzMKvx-6+SYdXLL|+=S=hsTv zZ>hgCwJbclpU}K+E5kV4btM`J0r7(N@2dn!8vZ*;C z^78URBV|h5$Yz64&#s)X^0lkXV~d6e2q{rdTR|G>3xf!0+OaDZ_-Jz6ZJp`?!t3Gj z+<@X3!bWI`8N}pQA135RCy{s_&HDyNUwM*5NbFRLo4BdD0OMwFN5<)VH4!bPCUo_# z0?l!?OBG5$DSxWY>#MqGiCR-xNlg_ngy_JPyu=7eS0la(n_ z_=|pdq`v8bD1WbMIV-c8`wi1ct~;9ZmoPG+wGbgmJ31y1nCpT?Xz-)Cti9)&chU0! zG)c4(;2PsvABB=?CM!gLMw$XbB(dR996IH<&tGS9X%>T}qht3>9%2c#_<4dv>6L0~ z8FdQ09YbtiN{F9em2$G6j^d(RR?#?%rfs4mglRmAvVOA{pHH#hIyX0mCVWUz}}M8T|gya z>EnfLr?l6?vz-WunFK*=1wMdGcz6^9Tz}NMoM@2;VP3%tk=*bG>SCw4!rT8x)jKw4 z+C|;kcan~sT(QxyZQHhO8&_=Gw$ZVTj%}x7+j+C=eX91e>-@0JKQL#lHRl+|$aEm< zCE0>+A8!bcj1hhqPFnp5BS+Ao66gd5F_=*N9&cULMI9y$MPJaN0=V!KCTLeM1c(QX zSgbGNxRr`2pqUUy`mnKf`ils5%vfE<4_TqnP`3|k{o%TpVwH%A5c4s@29Typ?sKP5 zmePkjoZu_LefJ}jsy-6KXTqu+& zGB;VObnm0NvoewD_r~7fBuFHoEp*2RGZXx-{sp0I|aas$rOF~e+es0B=D6{dsTHli+s5M@3~-rdS)Li+N=`2{qTo@d#fBf8A-fUb#JETg;0II#+f!6#^b{;;p|_PPg3RiqWbDXz z!|`OV9rZ~bh)nJ*;OZ`~mA)}3pc{Os-HId{7}0Dl?DB{gA!z?3HX*j8;B$bK`u#%f zhT9!J`2c5v7N1{`qvgB0$~~~X9XrHy3jU_HmCriT%^y)I%odQ+fStzf6K3!Cf*)pQ zMog|BnLZ0`j}Jt>8Nc`s&i1HMR-+DYCs0(wnJ~LBRKlbvq~+n}M0dVcwB1d9tf0V> zC6)dKVPXYeM9r|4?M>1<_KJkjPQsnTOm~E5k8>v-Keo`|2^OF=wu#Id+s(HrC7q)=INI;2EAG|1?7F^op@9&l$%_)EA` z6QYF3+9*tk#B_T@{a_k@_%Tmmz!t03pwR(ZV2-9Cc`(%#umgOD z@2%U3^+0iLgjFeDaOQw;wFUP)3(PzjMAs~`F& zMKTK{1(}+I+~R5p=`@5vLHT1~U?lycqYP8BD^M_om=Vj7q__%gnZhZmEMnb)bQx$O z#4)1F9f^&>sn2R*w5Gw`wUS@Kr9E6Qwj*rlL&BfpGTz1coU2eZ-fDJ|CsptSnd-LG zZnEAFnOsycLE*-6RxXcna^U`hbTplNve`r3;bs*yqNi#gOFW%Zi~l=6!9y)Qro8Id z(PGBVh8HW82TU48=F-qGUj$+;Q^6oos|Ld0u>Q+pXY+tDahTfi{6((%)tL1uyC<^k zP^SY(Gu!0M?P8&ZcFX7}F`>Mm0aq|7*>8J?T2=W-VbqM#;c6R3)?7IdTIj!|$=={L zMWFF`%qB8QN=U6AkeLH%MEKfE_QgaO8c!hdn~-q|B<3xq>@wAOw$l*pEm0umBH7|s z4o9%9b?dH{555gA@2-V0h$;}mZd1a)!r2xMlNIIDdlq+rm+|QWh?0azbYes?P#Z!_ zR`s8po~wk(eLxYGD4$Tk#RW}qSt$`GBXY&5CTY3aXn7pzttp4?eUpIVKI_GL52E-? zC~+$XVu;cZ8nmRSlQPRC>#w36Pq4?F6pr^5tLpFb{|eCn!=^&WKtKdIFS*OfQW{?? zmlA0*qsW}`bW8>j%!1v*)2r_KOuYqpK9L^>IxTv7-J%F@7q9r0W-?{*qhSdVZB zB2|Z}VoC(FwYu?Am!qCwN@rw^RT)IpM2{}m^8B0~6EESa&*Mz~rKHLleMkMQ)x2&vKrOa>p_X9V(;LaFwHlmR6oT%$EMzcKmIEJ%!zrcy z(k%_D)90|rA~W*3izK3lC^#G7%i;2c-YBt)QZJZ=-*d87#EM%2#UiM!a-#h>BAZnt zsf+Q}lAE}w&mD6>j<|c6;mqMm2AHGtY;pxxxm6^OhNnOtX*$?whI~iB>o1v5ufU7B z7QD=jA8cz;Yiqj(AmDPwI%CKau@FPRMA|sjtN`$*u_L47`|HKnvE|uaYz{*QwBPIZ z!%|kAL)Q~-j+f0iK#zXy*3>o|DjEtFXWu9O7mtJzOyX_X(Cy4W{TooIG{t4H)WX~0 zejOqw+x4SX`^cq8cN*oNC`$*V(v<@>DI#-= zdw}jAjNA2QL_Ui&T>1-Rgbk|VOhln1RlNk2TwEDUp%Py!*lLS6K_GFGVv(FK2X{yR zKn0x-h7g$33$T99 z4lqsHyD9U;+^jK+_>W999*u$Az#8d6A` zYOL%6A6=@$&kV6FHd2!XuuTelD8FB=Kc&a*!3+}Hf-tzr54dq+AZZYQ20O1T*ImF_ zl8jDiSyH_a)ZR6+I}lrC)!`t19oN*68VhBh5M#Yd$qm{b@#phZSXlyXCuO?Y>_R?w z!ylY7f^@-V$vtu)T=!59Z6HQvr|h_$=R^@=zwS%8Z#6tTl1rdUQEa!9zGMAeORy8e zORkh}!vU4|>%~~>`D`qb!h zH1)Bn{AOBKucv}c3qLlRZ8c{2o{?nBj2IqNDr4qWrshb%zAB zp;oIQWc+r)%+7=#3VYD&oo+T1PH6lGorW5b>8p11B(og;ks zvVl6d1qG!rQ~l(?#mcgT!}0k0lA|c7bkcvAPxgs-`x?X(gcWV5@4ct^5jD=G_Ix0II4!0SRjkc)~#;BQA>zQ+=i)liAaEs4^5D&(?=x-k{n>5I>fFL1aS&`;}h z#~$in$?$L+JtUvL7)Xe#kSBbGus@?8I* z=4@|3h;ldI{&{nmsFhwrytFZGEGmwfJQ94leI#1KAuZ$ALG!+wTfx;QT>XG-XqC$MSyYwq7HFeqoJt1_lGRECwm10*_YuSG9H|;?Xt0(Ni=I^BU}1d?v_p1Vn`yX`ll+B^lwVkN?Lmr}^JPRMh^zW_{Wuv22 zy~95cLNk4eO}M-CUtI3Whk&#Qr?%F z){g@$?bT-gwRfl(7!$(y+PpIqH#}ZWBlvp%ciS(sJsMGmob2t@;=T4zPh3X-B@0S^ ztSib-YRHoDdXTR$G$~@%-HLSk?Hb#WwI>@sgwaU1k3(^1D7;qFcW>P-T%+T-P$Ci@ zo!^&J)x~B*%?0zDpyB9&HUZ!LbsQ@-ww*%lgY{i^ZZ-G{*^I96>ZaXDR-t$-DP_8O zs8nE+uT^Xke^ErGnE?fU0}-wF3JSfVoh5Hke!cnAXt|tjU}QiAbMcDWtd@&1(OLS6 zF{EPuL_RMRHQ z4o2BNia!k^oNQY)L@@=zXsoO^a<^PX5V`*My*dS*?T<)*eMl<3{TV<&`>joCijl1I z?v#LqN0Hj~Tzb>QfT7C!7Fp-VC3&6JiGf}cY1|8>^jdL4Bh$+nI=ps|N`Lt1JgOu9 zNTtQLp(V?pyN^VsqQnJrOmHvc;w#R@q+&v_LnE6uG$!`Fp#+ERs z*?i5s7q+K_tmMbVGDCfTHs6)7`J8orWdB<__MrKR-fe6fIPU}r|z689rT z2qTXORN`*BS0)vDra`uMuO^<1wvKe?dlL;-T)oy^ku?l$YkbCFY*$C!GqNMjK66L< zqHc+T${Y;*fOGu3$uknt=btNIL%Pi~-4ONA8OiYu*&jb5-xlei)F|I1z&p`yD zr5Q7e1U&I6oFTRopS02s@6wSBZxX~N--mGXJ)XQyVjO>esKD76{FeFNQ!e>DjO^p* zN49S&VRQ2uSg)x_|M;inNP89g{dc}D!v1rI?CWUY&j$DYXb_A2zbomumZAq8#8+B+ zl<@-ZVRK_hm|o;R!=cK^#AWpl+iBF(A72qC`lDLu^Sqr~^^``(zuzuszON%7eeiiu zH`=bv@301)Z;`&u35SeWsZlDQF~3M!_S7QQu+Jxx}b{^~=HKb_nQ1&n&}$ zGihy}0S}3JAN*e&G{kFG1>c{BZceF1Bq}U~qUv2iuIG)qvi;f2YuZvYDNN!Fko}(M zQNUb;1_ysNMYedOAeh1)+(r3B5KW50?@;a^C$);`Wt0m$YXiiM`hMkL=G%$~8iJ2{ z?|3GgM^V3jBjv? z)Ml)2=Z3D6GddBE8ZH^~MnC+0JRZwK$A`5gyo!khlh=bf)bWS($|N`fjae-GjEt_( zwhnRVuWi}zV>w4cW-d{qv$Lq6U_v;o7CB$WXN5)!k%UPqEiqbQ*H$^9}UJqkMfFEyBJ6`A=bsC(On#TpIQ>v(See06sO;c`nm zucD%yL^ork_jhx<_dOiiB&MfP(sBb0ffY!4Up_zHxbGQp5iL$V(*s&5RB_4Vi_0q! zq4ZA7k8O8i4lY^lp6GC6qd6Cppv}sNGgr$W--?idRCpGEYx1~fJ1_i ze`2TD=o}2HCEv1c?Vxt&|uWPM^??Qx-9S_BC?dmcI=7$7Stc8V6pI{&UiARjd zJU3o`e+o3Uv;f4sp7y`jy%F0gYwf(f6_SZ%BO?n5!^6W@6$C_wK_k1ZFS>^`6z<8c zyLUdXvhdNQ3mJ#ErEMryxzTAPkkQaUs71Yw`@Z6Q|KlX2Dj%2K=jz2=r?cj@zVw~O z_MYT?mwnf#y!m3LWTuw&!o(uRL%d0o0}qjo%PW(nWW*rd$@u&&4U;YwyFKq`&qK>g z_I-hIje4Pd#C%#`7q{4)n`?5#Bk<;R{F}1iMW^!elFhBflsp-6cIpkAC=HaSieUsA)7Uj!4h~?anV50iD;rvQfthC}SmZ|7? zD{dBSf#Cev44H%*;6}GoHbu>r|98k%OhWDmxx_eSg_4(8>BG&L+$sO}BY)y`slcga zWTeJh<}EdMZe4~leQZHvOliQP859ABj;Dg#r0U2**QLSf`IaF4OB%2KxNl*lzH>=* zFoorHOz!-5t5GlDl|@tP_;Hk&`y-7^cjL2!r7(w*0HQ)t)+;t?IX66BX}dN2 zIZ#q{gTBR&|6afWfIEzfie@$d zO-)ofl}Oq`q`&;;BXfv-*qy}Y`pl-_IF*3M(~{6#Z%N_FhLqY-xbcXm=roB$@6tS_ zoc}W#S6l`y3837*+N?A--7-#WdRp9KvO~{EIr})_0|vP4x6qW9=hjvGmvSMrpCT5! z!jehIo|G!HpAb%cT_J&YptxtAG2Rt#CQF=}=glR?bk&HeTYkxpQcMp?_4=_(Td9ky zSC-n5vD$CQXTIy}OHHVMk=m}eXmbItDgpSo6yU$I<9~$K@xm41i=Q8;>p#T7uJ(X71f08BT7yS2?Z^pkBi*kDuaZmZ3)% zQJq;C(|)tz#q&mkws3*w<@lqRd38y#WYC%6C%rC(p<_k1W=1B#E_ad8qw}Ia|4p|| zn$rmskBo>-yb=kPWUtjtiM7)sjyXA^ICbb%hi0iHmV0ghtl242lz^NqEC! z6O4?czPCDo0-5aN((^?>aZoJ7#r0{!#p*lMkw| zTgl&lnw#jU0;O4f%`MNquM)4wXJspy;v&}*NJMkKG&9$$kVi#@8*ee`XxFB^=er?I z-_TH<UBB^wKqFU zRqDmv6Z}bUO$=rJ)nu$EEJ|9AH=)VNxsPT1Dn10CXrmsFI!HFZaQ^@5{hu=S(03M~ zO(O;y&gXwj!SI9se!Zin(RUZoESQ;fCFL{cE3eSa7_A=Y(g_LY{DZF#Y=+vH<~Nu( zYae%=3y>V`bhU;FvWi}!`MWYRCZ`5ECOdR%JnDF*RL+#(*ZM<%-!Z3K*TSry?PI=p zpTT%htF2F)fHABeO!oW4fjveH4&kGI=?)e7Sn_?jJGXDRl7LEjc9yTBe0lCgFC>N3 z8m~>+sy)M%n(nKMy!&+MOP*Pc1NSu~QYWTadnmc2+Sy4I?s}^VzF0JeLcA01n`4IR z03y0y{T>W%zurfVZh zjvT(-#S3E3mm4R-mR&gXekG;C1Gwhuvv}{P&c|caXMl-fcd8H^4D_ zP~QoG`PJ2qMCR?n8GjTfa+X))c>(5vXV?ftW|0=-G@jZIJ6)^(t`ui;GrgimCbVl(>~nfFK2CtM}_ zB`m>FQ0C!MwB$Uih6*Ew<#>b#3fj0;C8Gx*E&Sz$<;YD%{HTb#4aoz4Pab8D&kMb7 zBV@AMM2qZO8MMj)`29&-&Fg}887f=nfb&m4!jhvivppNVW+a1rY0kP5(V0_L$k|y~ zEi1)2pHcCxu!JN2BC_H>z3zGoGu+-|RhUwTW4-xRp$$=;4R(rBuf_ia|MVU0nT-J40GF{o^K9eIp zn?lG_dmeksd}Q=QXnr(M9XcdcPk*Tl10kCg1YW&MaV$XHy)BKCa!U%z&Hfdfg7dWR z=0XezAv<9g#DA9jVM2ITP&s^Q$4$vBN^(S9_A>75-9qum7^Teazx(${>ay6L!lHkBgt8^)JD$jRDmUquD@TkaAy6dLGHNC*W5 z`aj3_f1rNQAXBv~EHEgr4Dl&zm8wl|{-AsB=!zb@#0;cpuTl-wWY}zW(PC_+b+Rwi zj8VPH&jFY`9{*iTStaX6s}tr+!l3_1(#zF_S=&1brO+f*8F%B5X16B=O-Cx|?x;^j zqe5g%Ta`HA7Qxf&`9t7wW@Z9$zEGRym60+=t>aHK-!+AqIT<(Sp4gT3|0NvR2&3R zI{C(#o_{15E^B5e_6s2^!-F}4uq2=F$ zKp(f&_lEkx_^)zv^!0b3H&J_q7nGIZ)*sby<*U=v!-xVxqhMlF3C3XjYJK$Qk=oSI zd({q9SWr9TR8OM-`c!S0N&?0M&nS%b7n75tG5dT3FeUzGmWYr~HL1C7A+~C6Q-z#d zZO?(~($;Ev%L1nB+8t9_;4JMO_XpPRt3Sbl0HnZ4TM7QmT5GUV69#~#}2XtZa4g${3yxM zf296>CA(o`%%{ex4n_5=`(BBeoiUST$kb=W@K&@CsmFYST zhegMam4E8%5p-RzLYSB6nLAPUYsvRB|FEihc))>YTU}7S!bsAYEWj+P;$)FplYj9eC zRuj$%2fybN!x<#~b+H!vxbZ|nOohlxHdzG4jBI;`BTz#Kdy3_$P*)4ft6M{rr|w!! zbs#`ZEt2AL$qK49;OavOYjKW15=KW|0%lvzyp@8+F?*M{=RUo6ooX+jkqbuVCkMHn zO&6cbE83Dt2M51+%fSR7DrluN5j)WQQ09s14v|p9h?ZvzI2Ivr!irj!a?i#FZ|WYN z{;QB|$*v1*t<@72>OsI}w~t-FD{PEBDDj#~=5%{K061Nwjw3YfIQ(lzfS4=MoiZQEWbwwB^64SrCT(hSrA0i&URuNuJ}YU#O077H|K zHq&5O?2%?+n33873KtSpInYWVW+d-Dx_-prqf6F!7SM(o#Og4*=V1U}j7Z45R5V1X zQrp{=fdSAGs2Vc*vxzwUcH4<>jn^L11CA}19r`bDjJ(Q0>qaSk6+DYNG@U!-4tqxM z;gXxqgvge`NL(PE=B1`;zR#T4t8bwO+#I3bt7^tms@**qh^&yH8x`@)^CluIllJc4Y4s!9*vSy)bWu8gx6%L-5-AY>xkip^y?Rhpa9bU912OO*WYWH zCXD;AkpvSf7$FYpp0XpyP87v|hNb2ArPouPj(_!h9a(=g9-}zj!Ms>|%e*`=(>$EU zLA;@boJx*K#tsIRi;vYQ!amZTqjB1`aeRG7(cgUD$wE=1&ysyh^Gy?mI!HmS-GcYL z+Hn@gI-elqgNgl4mZfCzxzCAYP*d#u&C}kL6*%;~>T*}g5YT%n~P80~^tq>xJuo_9o@LqEkR!3h}LXWWGgb2^A5 zx1=DUDL|d6?*w)H5>j%Y8t(;xUw;`edY zhAh^=DRUDsNX-4s-)n%?2^AMM$OK&xR_RetPwYBJj~g^Py?MB8=uh->E+_%r{?^4* zzf_r&NaIUmctM^uqHdW#TaeHQ&EmM%-{EF-?A19u4Pn?B;X~U)q-kr1g0U8V;;aiL z!q#(*BvOe4LTeQg=@~HYV2&9l?G( zhFi0F&f0^&$-*G`>`vg8FFq$TJe<()Vyy?^T`pK)!II->O5dM|Zuw9tK;io@vhQOy z6&_@Y?TG+RA>1C#XxaS1>)%k78^~8*e)KQxRHKzSk0v|@cmjC0fQk>Sg1{QIab%5Q z>&fRd-|&g@iT7+=E3RKLvWM^F!cBRbLm|$`NB}p22&@nWwpRa)9xf+l=aWg=U(|o* zYic2Jyv3WWE`viKWeTK0c#_`1b9uw03!dZCOF(eAL#Nay1}?MQk@E^5vfOm%7OG$~ znI^S(`SIeeLiWy<2t{}#L|Rnv)~#R;ss7`Y7H_)VjYMZV8jFQj4aM2<%|uXp17|tU zFUi8uSznzut%(ih7KDQ93Vw%fkFQ(`Rn|6dvRz2`QrNMSbZ- zo$bl@Ckv~Z^IX#Sxd-3KAW&xJZ^>)MV8dUVviI|CGOMQ^010AhCdR=HQh!IA&^D#*oR2!hR;P1vP*o_r)hY}X)y@1UKCY#4w6*}_&(H1MRG zt8xJc?`Zzs3I3&2{zDkB@HiAKV+c)Y8&hQiuUJrDpX@%zohV;>aT(`gRNHdsP!llh zQi`RKxO#B5U&1|iqJh)*(C!ZgTIx!%Dy*mJt zvOPl+hLENr4(~k}yX_8Gcwy=`m$S$xx|+R3Aw8GHz@DcGp*d=8A#;^kvDk&e6pq|k zKoYu7=<7naV%ilEE*M&9dgr=aDvUWL;3Z^Qo1ioyN3dLR2`3G^G&zsBIFFlwiI{JrKV!E_VEyVzC^? z575rx-ZG1Zk!Tw$_{Tg2IZ2$_94OGOV+n*7Vq- zfEaJ(xq2kAq{`tzjZZ@t<^D)E^v@hB&9iQx=_DX;$koFYuKo);nn(cSR{fNbW2B>jhhl zUMpHvJ66ze9(uv|$Pgu@q>zCHjfiBaD1QNp|U`y`qRd0POY zG1R4cjtDNiQ4MQW{v?X7L*YiF)e#G-0oA$@JhDm>?Pr0b zK%cew3uWQMuW(4n_y+Stqa8uA;=}nahrnlc{;r zqrx+~?PR0Hz0}_?%TKhMKtv|{jyb4vnfyt4N`GzA=In$f?4CxXGKxwtqv_vY3%{c$ zK+RvKmn~tsttfz_LzBNY#sQjccq0&&&TU?hFd zp^~1PabhNZpX483Kb@ys&1!h~4>l@?EAAC+bZ|}wT`6F3wd&@X-=DV^a(6vOTVj?a zq&WwyqrFx9e#G^bg$wr>Ax}srmrwNS7L$7ncIrHf=y}}r7w%?QjyJ-A)|jmY=8C8= zw>KssSi|$R;8p>FnJ-hACAAd^=Ld@0#~TUgi)VjR2?kGR=U}52C>{Pr9PdifYRq0b zb14B$qy7Ha*QG}5GeF3Gbf6q)E@LP+=qS9U5LUQ7=*(R&gmBQ46vY+8WpG%Ie6o}E z9H1T^R%cH6MfHD26F(CQ1>^vJRe>a!yM(knL{G>2OLv*B?tj1h)cPvVG3f}8?*@y1 zyI`%W=j@M65V$)dyP9$T9|;oo%vS$>w$BNx+i?0gGeeKP`64^Be`;5KGE8y-f;gMmx5o!zYADn~lTEcpc4U0zE%;s0I>)<`kTm;Hpz zXUrB)xe@v9kMcu2Em2;AXMJC^=!hK{0GxoIRamUr5jOUEZNc4UNF)vEh|VZFo!s&j z*H09StlWy(l4tUrM$nc``eH5-+T`ieGVbBmzkfvh=ZpwG&v-f58^{S+Gxv-AC+)C| z?oUuGCgJJm7)IcKFY%4^GcKzcQt6U8|3J+>*XhzHc$Syb|=c!wLfC zi4_%!=tRR#F}aJ$*Bujzs47)hG^K&$|9DF42S3dDZN##D30~^|8)msSlM+|b`*8Is z0sKZBbUh)L!qBi@AhjtHaG~779i;f>$--Fm(0-WC%4shvx2fv$xWxWqc;Kkv6+LVY z2!v>=kOyX04Q~uF0)ED_7{&*;3@-S`!tA>|q5rTbw(Oj~G6x25H&a~)r_gN<7O1J! zT5+(9At&6osFPWD$e9)*R2!`y9z0_5ub6xUk0M~kyr$_ir_|2pLprNtxW>|s8|CE3 z2Ra_k4YbF4e?1%!^cOyxt&TOD@a`Cso@d0xwT&exU50&|aS>fE80?+^EOjN%jm#A$ z3VOGyssev3*#QJIkAmORZY~>uDQ?bVc`+rpj}@OFf|Uy~4KZ7fn4FN;1wNrFh|n&? z`b$qhRPPT;=X4jk$e-={Psk0{dSZK}2V9`ylybV$_azqFaiQjT2I%_s&vU=L1wK^f zGd=oUnPp!{$4&Y#kve$01Q0XYR@$THnkS%|>ndGj&k0TxyXXSy#YwRz+t5&JDkFV! z6!M^hB_u(PRVk6MlX>RH*S|yHH==n|?u7OCHj^3K@qLAuco?a>bV{HzwDd2-3`&}o z?dWzon>9X!yL&Md=}*3RAW;e@mLwEc7yN{J-QZg1ngan7v~yXkAoo$I$xw&Wbja0X zi9`!1-Ljr^vM5MhJi`$q^hPWd1jH#BukfO* zRD*w+cx?$L!PA3f=+DCNg?NbJ;nRl5~yyYFbnP7M*oax^}wSMxj}`HIVh&QRE|E*@!65-^}`LE=N$n(w3%6?;KW zrnBOFYH8nPU##HhJM~6g8CN=64UlImldKoD^ytJ3TzQYPN!N?yy6^pz)lM|mj$7s9_V(U!Sg)lN78@=^iF_c9qcN3yvv^eqaEJ00K z4}W@mcBqN}1VDvTSOnwqcJJAwLiCnIosRIwtapnUE>AK#G}H`323kpk^91Sp`tCi~ zVm%8g%(tg$)Sr5wo3+&RSUR_#q9R^MWp8!zS_mc!X5 z%O5wo$*rPmtNbs1D54`}ipU%%Grv}<6Eh9BkO#vM`}@qA^)FE3J!U$1|umhtrzXU#9SbfddAcjTmGym~nizS^0W+WWY41`0N+B=NbH=8EbZ8 zLKbw{cFar$AP*N?g9u(^XFe)@0%v&bg>DQhjyv&51Y?(&Y~PDxx8Y7c{{0>%^8(C_WW|tFa6>uY18F|L=k`L3lG+L~;1->A| zBS@xaBrYp;waXKJfTZ$!J}rhj#H4O|#s@&~#|fYwnx_{&9U8Mi=)+_S_oi&KoX}48n~3O& z@9Db<8>bbLaJdbBtN4JiwiQ(-TGr6a1`e$K%tq3QjS|(+eq2F4l&Ck|*p9yHm*4vV z`sNK6(!2WJB^0QjIfYtKt2Xu}89baBfkl;beV(z4;v2EO@)+ zDHF~2qr1qGMK~XUY~N%7?`&{kQYMX9h_2FflJ6)TaS&$=wkv*g0|{?}7v+EB9d4Lh zuGm^D>|$CTifGd@;)5A}6DB3@4Dh}#*5DSP;3`_0%W$;Vw*Be)UxTmKwW-}DS7$RsnmtGR|hz%}{R ziA|kST5hA0Hpv?Y+Yqi$E&x4qyaPM#=!#9>n)cGH<(Uf{!F*_KUFB5BFHYRY_hF4= zKX}bo^pg`-zybMVkTx~-w5_X2S$&TAXM@`TLUv~sL=>B^-ovneiiq-RT5uZ$OpSHD z<2jMYDW}0~c?0H78SB|1Q%FSvG+c_1^C{evWLuOk_cArL{&t~rt?~6<`r-;$O|`YK zx^SUKs(*dHvBtGK`Wageqwj+PN2ZdBjHJi|YfH#V(9Q! zWz4iwl;_Y|39Cq8YfPUw=e(!WyLpefw{@A-J;Ho_wZRQ zF$FYiBE*1hZb}nHU)5fyp(fYzVpua#15wB2Bm3%H{OPp+AjUeivE>v_r;cpKf%7E4 z*9asIT1;kN2GO~Zxl|nA>Xn1g^3lk^mrW$6xUJ;O2!`l@8{l?p0hU&Mu3pdj(R2Op7n3d*$`(p{=9Zq-0}TS3?a2{ zE-fd+bTxwDe@}>!epSls%A?Kh*It_r*y>*(`26Q8%SCu{wCm^7aHk?j#>9+c49GK^ z`79=%{AaR_<|1z7S`!cUUcYipQOg@Uyp=xcky{7>i=(}&u_U6lue<4&7<~o-KRZ^* z+XV?Pwv#)hIFawtpO%Lm!^_=jIPvt^Hq65^j@4dYU;G{;-|)w2NuhI;LI#f=w>yj3 zTyeZm%MKRf82Lk>)Ak+#x7QCRZeKZHa{*#1lQX{-NHmZ^{ySzoOIs6bUvkoS)8i%> ztxM`rlKSXS>x3B+59ycwPKo`nV1f&373KbC+X3nP5=(xOi3R{rWD*{V$r^?5=hhaf zX(X+1j84R~O1RUb6SVy=hUPIvt9JOB@;@Hs5*sRkuhEal0$E8%M~S}+{tX#ec#*m4R3CA0!&TQ-Qn#}QHKX3 zntQT6Z7d_pUJbIPMRvA5h~3N08cYOYZ)ncA`pFIT+4vy7AQf?eOU+`ljdHWgs~}6< zoMm@knU@}V1@WRkRu{F_XGy%D@gt%8`> zEtG&Vz3REHzm@nUb4pWf+;0_EqnnKO#d7N7?M449fc=WFMhu6JMm#^SllI{d^Y=e$ zSoUWJ#qKxqm#s+gPq*BgVrP=C3)RhuKLfLz2eZ9Fti!2^mr?FjLl~+cQ*A)ury`vPUa)&M%7CO-`5pHrVZl17@?vSR0Qp zy$WTMIuvHY2V*7*JkqnFX=#tnIxr&2;qX=)jY_*X+^!hmNWvKENJbehmlj1zT}S$ef@aHdc%%PydAjrlXLAM} zjLPk9S*R|vGTyj zizE@JCngICEU6OD=mC`S_m?b`IbJN9>U21w&dKM_bE@RXU>7omAe^IL&J7pAW@9s9 z0oyq<-zab)pD#zg{F$NTO|B@#&0U<$hPThaB|I=A&f!8P$~Pc{LlH!-Q{m%1uVLhJ z4qd%YQHY6)Zdqgl-KN&zHmsx7?DTH#EFh=M*&V)yD0R93Bj=yc?MF=X+2Gi$ZUUpZ zxm_5igm?3fFF6PUHSck!nA75RuFf8@#S=F30vxkT)w%Zoadx`lne%62o(MS#vZ63a z+SPULf=+S!?bb~L^{jna!=;fCC0+<>4)f*rFoC#d6=x=^<-g#v*y#n>8(7bTa2WO? z#G+N4p+FV@7}o}dIwg?tUH@~+`Y2gf+5O>c15%e;gNTa)i98l>aP8dC$1k?XkUmd0 z=~$P#?eVa?HU`{C!{TUl?!Ik0keBgRGXAc~(9RT$dldcT=pQtqJNe>b1&6(6287d9 zXfA)0GL4q=ofd=LIHx=s_-v&nMvE*3L>%U?x>{NTDhx2M1`_JR(A+^wJpy$h0>Wh5 zaNpeO>T+mZl2w)#apfcY^Vt$uF3PIp%5J3DMy=REnb7Xk@i6rpTyekrz7`YsO2(aS za+eTS!~d_a>kNmh>)J6$7{WvwiP1%LLX?rHLqsP!AzH)`b(DzFTL=l!dv6)hdrKHS zhzUZph)#&!$M_`I_dd@v?~mu*f6lf3oU`^?_geexweE9vQB8tPxPR?syV6*$<^g{l zsRpeF$}o=WA$!_(%A1ih2bfy$e8>5{=F^y&GM0wwPM>=BacG(?rccJ@8m(SdVHH?D-HJ0;LZ2 zev?^Mc`Jy&p-fLid~a<&iY{s(+Z03XtdBs_^eXlo&)2`;1#T+?8&`nz^6ih>QlJ{TUIs92bn{? z;sgX#=+E6h6m*)kZ#MaLZ`+Sz)Xhc?-2=1Jcv1DemBTLQt!EO*1V?b_$;D9{urdO| zq3(1t5K!diq+UT-v36oSLTt-|j{Itrc}{)^QaEm!Y}Mvq<>y8SC%1v~!j^W&!dry8 z6_DM{u*Yiq9;uDT^w4Az*+^-Iynh<6(irHz+b3K2?KEf%LYkYg*3=u_R0!syPbqw~ z@5^)_}tq?b96+e_jv0AAPHtTo9Q7Y_G|Hxgt zbg!ti?JT8Qj&jCo!mwx_8*>Q)^(0J&+&Zl_g&MqQn`uvyPrI@6mMiJxlwG--iYa}P zb$JGkLqggPAI79V7G_?{QEbxK+VKc^AJUg&L}5Wr;(v}o^o*1cz!uZ23@zT?TmGUo zR>RP|5>X?HZxc4bYqN5J~9 zJ2|kSSM8CpUzGPNZKE{{XIi3FC0_7=SYs*_N2$H?_2%6tIvUuUy}i)cti+VUn#~9b z+=9oh3)3EXWZYP#d`w<*g8H+bhQcdumI8A}F|jWPhC>2kko<|RHLK%{gJNAIO-KrD zwWZR0oghlSBj3(r;}2eUpTF>GOXwGMrH=Cfk~fAx^i@j+Ys+B|=A>Z5?eU~b-fg1^ zda(FF^TqD_{HM1MHYU5nRH-1TBTDUOT8}7OgCd`CrZ1~z)Ll(&$O@|yH=Y#FI<4Pp zJ#O6A!EMwaRHz3UF;6yD{a31PIoG#)T^aW{2sThsow3)LTlscns3iL{@EhbafcR8H z$uNa;!hJK`Ov>~+l`&EVX^QTvE|zrL-;Z}3iHs!A%U{j)p%i37(V~%hof5>Bg1Zx2 zG7-^*DML04dyCL~#U}VReO~_PkJgrmLp_*61tC95ufo3z^z*GH(?hd+0AI76<97!v2w){z77Xl4m#ylIgla@j4e8jNa#yY1plWp^H4NmajDpX4#Y41}#S;Pbzw`=^Xul;<)ZuLc9hMx-K-f=y3 z`ksGSS16qMm~L7#nI@Iy5!SAX`DEzH;+I54@=)R1VQ<#0i?a=a!VgzdR8$42$T&o8 zU1CbhK2JSggh(zjq-Ms3MDCI2Zxy_M`f=CA<-C$p+qcdDSHhF!o?0FaCG;7ZN@8_P zUhT0s+WUFB*N4`}QR-P9rJ#lVtmwK~rFa>R7mDAT1W_XOUb?#%piku)7}-#CYXAr z_F0}{5@nLwZGZ$KmZyRJHrJhz8TU)Sh67DoFN-ad~ z9{7l#Ir-e9Q@Cisy-oEEzE0k`t{`4426Mj&^ee6yGc32{_MLPmEX^&n8cdsGBeH8C znQRIMBr``gs&__0`(th`=j%3j6;8_p<*WKsoTyQqyiN&huv*!QLw7Vpr4{o3^rAr_ zqyrLM2+-1iHj`-fT~^F}0)2A!JszI=VYPZ|fb{V_2}FX?gPvQS)| z7$e$9`OV6n_0iqk9jSi%&~1@Ii;k?XLSOooZ0rC4PwdM~KPmED4fJgfVZ^#o3i>6W zaa}0>`?j^`A}*bw;@Rl^tXjs<$c&Fu@$Zvd0|l1$6oQF=J((F;c6MeC)($fHxT4rd zOq@elS1IvQur{;tja^CWUH2KJ8q?za#G-eTzsfWXs+7#y)VM`E3(^OSu|?||GGv?? zk%!T?@kiO0+wD3K5oZK`-&9I^xvO&-aM*Sp+eA|WBzkdnn_QD}c8`e|=u>QKdOnfy2so<4(9Bp9f6kWowNiAd0J z4naHMqq;eP+&H-HPD-0bvkOpRTg z$iDiDoc@Wlk}xcAQOgK1xwJBsdn?T-zlL)YUWtsS^D#l!hNn zOC`^bl6F0Dt-MY4%$SX>EEA0p0Xy@GX(uy3G)0339yJQd6x@CGlNQXyJ)DeGcr5uc z=n5=rbA{lu*@hNM*lWHI0>l1;asIolOAC;fMFIIM_^mWtLEg)pj?=H@?HqsHU6j+Y z!a=8vzuK;?T;dhc$LRK6W!E}SC#9KHJ|c!Zx!JmFP&PZ*LVr(_S0OtCib|Zw@>ynD zna;hNl312;?D?zG)|;3JxV5eidd(~oiZGC%{3R_b&x6og`zlA68tS~!17MJP@v--0 zaV?n^qJ~ug9qp3#G)Nmob3FS@#b;ItI3A=hgrA2F<{*- zURqj|XI<9SqceIph%GLIy0Y$ms3f@(tDIx32b6DKik{QNUz^G}vl5b%j|G@=4493u ziWe~?tp%W>Vki9J$#toY8fd{{z^NxW5b-W?%>-K2X!T=J}>D`PgYYX!)Xu!5mnI%RBy=c>i+;s!t?cE zw{3R1ojr>*!N~`oM*Yq;(y1M2|B<<5K}Es@tXX>3lgH)$4r|;hk#Ocr+v0BbY~49W zX~=X)Z3sVP`m}writG`DFH3bS@i{5pmIf(76zqTSBeG_t_;32N?pyz~Pkbyam&BQd z<=|S>YrVY>-q{dbSu!yofwt$v3dvq)SHJH9*cB|oAIBb;5+?YvF=55DLB(?0*Y;BFI* zd>I}-kAv8TgoJ2u(L{fUNc?-c_)q>LZAp_9p-`}kalGjRDa*0K!U4uAD!^agKfLMa zPljr7rTU#z55QvjQ zb2PHyr5yYy+wxP)GODs!87`zu5pw`KIBdw|UAKe7;eHW}eB9imbxz;!%gYCn%wu7p z4K*w`F&Lx*sD1RH1uX^a(KH}1dn2N2YZjEu;11{`Sm z-#o!r4PV;&aSa5R2oqn*>Be^uzNE*(LR8K3kt+4yT#tPI2a-_@D;59ZBUxIKqWrwP zmk?XMOPpf(q4AgF|Cr&Z{5O=)H=Hmg|2A75DiFY!HXwZ9-;gBD2e9ky?PUwzMc+XD dkNbg+{MPE$5MC@vnwKtyvZ97Usl0jM{{bn&eSrV~ literal 0 HcmV?d00001 -- GitLab From 48556ba3bbf228cbe418c3a0634df9f7c147b211 Mon Sep 17 00:00:00 2001 From: gongweibao Date: Wed, 11 Oct 2017 12:39:53 +0000 Subject: [PATCH 0002/2305] add block_expand_op --- paddle/operators/block_expand_op.cc | 80 ++++++++++++++++++++++++++ paddle/operators/block_expand_op.cu | 0 paddle/operators/block_expand_op.h | 89 +++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 paddle/operators/block_expand_op.cc create mode 100644 paddle/operators/block_expand_op.cu create mode 100644 paddle/operators/block_expand_op.h diff --git a/paddle/operators/block_expand_op.cc b/paddle/operators/block_expand_op.cc new file mode 100644 index 00000000000..0b36dc1ae54 --- /dev/null +++ b/paddle/operators/block_expand_op.cc @@ -0,0 +1,80 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/block_expand_op.h" + +namespace paddle { +namespace operators { + +class BlockExpandOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("block"), + "Input(block) of BlockExpandOp should not be null."); + PADDLE_ENFORCE(ctx->HasInput("padding"), + "Input(padding) of BlockExpandOp should not be null."); + PADDLE_ENFORCE(ctx->HasInput("stride"), + "Input(stride) of BlockExpandOp should not be null."); + // ctx->SetOutputDim("Out", {1}); + } +}; + +class BlockExpandOpMaker : public framework::OpProtoAndCheckerMaker { + public: + BlockExpandOpMaker(framework::OpProto* proto, + framework::OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("block", "The input of block_expand op"); + AddOutput("stride", "The output of block_expand op"); + AddComment(R"DOC( +Expand feature map to minibatch matrix. +- matrix width is: blockH_ * blockW_ * channels_ +- matirx height is: outputH_ * outputW_ + +outputH\_ = 1 + (2paddingH\_ + imgSizeH\_ - blockH\_ + strideH\_ - 1) / + strideH\_ \\ +outputW\_ = 1 + (2paddingW\_ + imgSizeW\_ - blockW\_ + strideW\_ - 1) / + strideW\_ + +The expand method is the same with ExpandConvLayer, but saved the transposed +value. After expanding, output_.sequenceStartPositions will store timeline. +The number of time steps are outputH_outputW_ and the dimension of each +time step is blockH_ * blockW_ * channels_. This layer can be used after +convolution neural network, and before recurrent neural network. +)DOC"); + } +}; + +class BlockExpandGradOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(framework::InferShapeContext* ctx) const override {} +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP(block_expand, ops::BlockExpandOp, ops::BlockExpandOpMaker, + block_expand_grad, ops::BlockExpandOpGrad); +REGISTER_OP_CPU_KERNEL( + block_expand, ops::BlockExpanddKernel); +REGISTER_OP_CPU_KERNEL( + block_expand_grad, + ops::BlockExpandGradKernel); diff --git a/paddle/operators/block_expand_op.cu b/paddle/operators/block_expand_op.cu new file mode 100644 index 00000000000..e69de29bb2d diff --git a/paddle/operators/block_expand_op.h b/paddle/operators/block_expand_op.h new file mode 100644 index 00000000000..54a9c5354f1 --- /dev/null +++ b/paddle/operators/block_expand_op.h @@ -0,0 +1,89 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + Licensed under the Apache License, Version 2.0 (the "License"); + You may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#pragma once + +#include "paddle/operators/math/math_function.h" + +#include "paddle/framework/eigen.h" +#include "paddle/framework/op_registry.h" + +namespace paddle { +namespace operators { + +template +class BlockExpandKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& context) const override { + using namespace framework; + const Tensor* input = context.Input("input"); + const Tensor* filter = context.Input("filter"); + const Tensor* stride = context.Input("stride"); + const Tensor* padding = context.Input("padding"); + Tensor* out = context.Output("Out"); + + auto input_dim = input->dims(); + size_t N = input_dim[0]; + size_t C = input_dim[1]; + PADDLE_ENFORCE_GE(N, 1, "Input batchsize must >= 1."); + PADDLE_ENFORCE_EQ(input_dim.size(), 4, "Input format must be NCHW."); + + size_t input_height = input_dim[2]; + size_t input_height = input_dim[3]; + + size_t filter_height = filter[0]; + size_t filter_width = filter[1]; + + size_t output_height = 1 + + (input_height + 2 * padding_height - block_height() + + stride_height - 1) / + stride_height; + + size_t output_width = + 1 + + (input_width + 2 * padding_width - block_width() + stride_width - 1) / + stride_width; + + Tensor col; + if (clo_format = KCFO) { + col.Resize( + {N, C, filter_height, filter_width, output_height, output_width}); + } else { + col.Resize( + {N, output_height, output_width, C, filter_height, filter_width}); + } + + for (size_t i = 0; i < N; i++) { + Im2ColFunctor(ctx, one_img, col, stride[0], + stride[1], padding[0], padding[1]); + } + } +}; + +template +class BlockExpandGradKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + using Tensor = framework::Tensor; + /* + int x_num_col_dims = ctx.template Attr("x_num_col_dims"); + int y_num_col_dims = ctx.template Attr("y_num_col_dims"); + const Tensor* x = ctx.Input("X"); + const Tensor* y = ctx.Input("Y"); + */ + } +}; + +} // namespace operators +} // namespace paddle -- GitLab From d2fda53217bf7c5370446f9a404b711ace9df130 Mon Sep 17 00:00:00 2001 From: gongweibao Date: Thu, 12 Oct 2017 09:34:28 +0000 Subject: [PATCH 0003/2305] add expand comment --- paddle/operators/block_expand_op.cc | 40 +++++++++++++++++----- paddle/operators/block_expand_op.h | 52 ++++++++++++++--------------- 2 files changed, 58 insertions(+), 34 deletions(-) diff --git a/paddle/operators/block_expand_op.cc b/paddle/operators/block_expand_op.cc index 0b36dc1ae54..69c5e02a658 100644 --- a/paddle/operators/block_expand_op.cc +++ b/paddle/operators/block_expand_op.cc @@ -23,12 +23,18 @@ class BlockExpandOp : public framework::OperatorWithKernel { protected: void InferShape(framework::InferShapeContext* ctx) const override { - PADDLE_ENFORCE(ctx->HasInput("block"), - "Input(block) of BlockExpandOp should not be null."); - PADDLE_ENFORCE(ctx->HasInput("padding"), - "Input(padding) of BlockExpandOp should not be null."); - PADDLE_ENFORCE(ctx->HasInput("stride"), - "Input(stride) of BlockExpandOp should not be null."); + using namespace framework; + PADDLE_ENFORCE(ctx->HasInput("input"), + "Input of BlockExpandOp should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("Out"), + "Output(Out) of BlockExpandOp op should not be null."); + + auto in_dim = ctx->GetInputDim("input"); + PADDLE_ENFORCE_EQ(in_dim.size(), 4, "Input format must be NCHW."); + PADDLE_ENFORCE_GE(in_dim[0], 1, "Input batchsize must >= 1."); + + ctx->ShareLoD("X", /*->*/ "Out"); + // ctx->SetOutputDim("Out", {1}); } }; @@ -38,8 +44,26 @@ class BlockExpandOpMaker : public framework::OpProtoAndCheckerMaker { BlockExpandOpMaker(framework::OpProto* proto, framework::OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { - AddInput("block", "The input of block_expand op"); - AddOutput("stride", "The output of block_expand op"); + AddInput("input", "The input of block_expand op"); + AddOutput("out", "The output of block_expand op"); + AddAttr("block_height", + R"DOC( + )DOC"); + AddAttr("block_width", + R"DOC( + )DOC"); + AddAttr("stride_height", + R"DOC( + )DOC"); + AddAttr("stride_width", + R"DOC( + )DOC"); + AddAttr("padding_height", + R"DOC( + )DOC"); + AddAttr("padding_width", + R"DOC( + )DOC"); AddComment(R"DOC( Expand feature map to minibatch matrix. - matrix width is: blockH_ * blockW_ * channels_ diff --git a/paddle/operators/block_expand_op.h b/paddle/operators/block_expand_op.h index 54a9c5354f1..c0521dbbadb 100644 --- a/paddle/operators/block_expand_op.h +++ b/paddle/operators/block_expand_op.h @@ -25,34 +25,34 @@ namespace operators { template class BlockExpandKernel : public framework::OpKernel { public: - void Compute(const framework::ExecutionContext& context) const override { + void Compute(const framework::ExecutionContext& ctx) const override { using namespace framework; - const Tensor* input = context.Input("input"); - const Tensor* filter = context.Input("filter"); - const Tensor* stride = context.Input("stride"); - const Tensor* padding = context.Input("padding"); - Tensor* out = context.Output("Out"); - - auto input_dim = input->dims(); - size_t N = input_dim[0]; - size_t C = input_dim[1]; - PADDLE_ENFORCE_GE(N, 1, "Input batchsize must >= 1."); - PADDLE_ENFORCE_EQ(input_dim.size(), 4, "Input format must be NCHW."); - - size_t input_height = input_dim[2]; - size_t input_height = input_dim[3]; - - size_t filter_height = filter[0]; - size_t filter_width = filter[1]; - - size_t output_height = 1 + - (input_height + 2 * padding_height - block_height() + - stride_height - 1) / - stride_height; - - size_t output_width = + const Tensor* in = ctx.Input("input"); + Tensor* out = ctx.Output("Out"); + out->mutable_data(ctx.GetPlace()); + + auto in_dim = in->dims(); + int N = in_dim[0]; + int C = in_dim[1]; + + int in_height = in_dim[2]; + int in_width = in_dim[3]; + + int block_height = ctx.Attr("block_height"); + int block_width = ctx.Attr("block_width"); + int stride_height = ctx.Attr("stride_height"); + int stride_width = ctx.Attr("stride_width"); + int padding_height = ctx.Attr("padding_height"); + int padding_width = ctx.Attr("padding_width"); + + int output_height = + 1 + + (in_height + 2 * padding_height - block_height + stride_height - 1) / + stride_height; + + int output_width = 1 + - (input_width + 2 * padding_width - block_width() + stride_width - 1) / + (in_width + 2 * padding_width - block_width + stride_width - 1) / stride_width; Tensor col; -- GitLab From f1ca3f7e5ed13fc23acb2ce79f756e939e604031 Mon Sep 17 00:00:00 2001 From: gongweibao Date: Mon, 16 Oct 2017 11:43:21 +0000 Subject: [PATCH 0004/2305] add block forward --- paddle/operators/block_expand_op.cc | 94 +++++++++++++++++------------ paddle/operators/block_expand_op.h | 81 +++++++++++++------------ 2 files changed, 98 insertions(+), 77 deletions(-) diff --git a/paddle/operators/block_expand_op.cc b/paddle/operators/block_expand_op.cc index 69c5e02a658..ec467374008 100644 --- a/paddle/operators/block_expand_op.cc +++ b/paddle/operators/block_expand_op.cc @@ -24,18 +24,43 @@ class BlockExpandOp : public framework::OperatorWithKernel { protected: void InferShape(framework::InferShapeContext* ctx) const override { using namespace framework; - PADDLE_ENFORCE(ctx->HasInput("input"), + PADDLE_ENFORCE(ctx->HasInput("X"), "Input of BlockExpandOp should not be null."); PADDLE_ENFORCE(ctx->HasOutput("Out"), - "Output(Out) of BlockExpandOp op should not be null."); + "Output of BlockExpandOp op should not be null."); - auto in_dim = ctx->GetInputDim("input"); + auto in_dim = ctx->GetInputDim("X"); PADDLE_ENFORCE_EQ(in_dim.size(), 4, "Input format must be NCHW."); PADDLE_ENFORCE_GE(in_dim[0], 1, "Input batchsize must >= 1."); - ctx->ShareLoD("X", /*->*/ "Out"); - - // ctx->SetOutputDim("Out", {1}); + int blockHeight = ctx->Attrs().Get("blockHeight"); + int blockWidth = ctx->Attrs().Get("blockWidth"); + int strideHeight = ctx->Attrs().Get("strideHeight"); + int strideWidth = ctx->Attrs().Get("strideWidth"); + int paddingHeight = ctx->Attrs().Get("paddingHeight"); + int paddingWidth = ctx->Attrs().Get("paddingWidth"); + + int N = in_dim[0]; + int C = in_dim[1]; + int imgHeight = in_dim[3]; + int imgWidth = in_dim[4]; + + int outputHeight = 0; + int outputWidth = 0; + + get_blockexpand_output_shape(imgHeight, imgWidth, blockHeight, blockWidth, + strideHeight, strideWidth, paddingHeight, + paddingWidth, outputHeight, outputWidth); + + // The result of im2col is [outputHeight, outputWidth, + // inputChannels, filterHeight, filterWidth], and it is easy to + // reshape into [seqLength, stepSize], where seqLength is equal + // outputHeight * outputWidth, stepSize is equal + // input_channels * blockHeight * blockWidth + ctx->SetOutputDim( + "Out", {N, outputHeight, outputWidth, C, blockHeight, blockWidth}); + + // ctx->ShareLoD("X", /*->*/ "Out"); } }; @@ -44,41 +69,36 @@ class BlockExpandOpMaker : public framework::OpProtoAndCheckerMaker { BlockExpandOpMaker(framework::OpProto* proto, framework::OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { - AddInput("input", "The input of block_expand op"); - AddOutput("out", "The output of block_expand op"); - AddAttr("block_height", - R"DOC( - )DOC"); - AddAttr("block_width", - R"DOC( - )DOC"); - AddAttr("stride_height", - R"DOC( - )DOC"); - AddAttr("stride_width", - R"DOC( - )DOC"); - AddAttr("padding_height", - R"DOC( - )DOC"); - AddAttr("padding_width", - R"DOC( - )DOC"); + AddInput("X", R"DOC( +(Tensor)The input tensor has NCHW format. + N: batch size + C: channels + H: height + W: width +)DOC"); + AddOutput("Out", "(LodTensor)The output data of block_expand op,"); + AddAttr("blockHeight", "(int)height of block."); + AddAttr("blockWidth", "(int)width of block."); + AddAttr("strideHeight", "(int)height of stride."); + AddAttr("strideWidth", "(int)width of stride."); + AddAttr("paddingHeight", "(int)height of padding."); + AddAttr("paddingWidth", "(int)width of padding."); AddComment(R"DOC( Expand feature map to minibatch matrix. -- matrix width is: blockH_ * blockW_ * channels_ -- matirx height is: outputH_ * outputW_ +- matirx height is: outputHeight * outputWidth +- matrix width is: blockHeight * blockWidth * channels -outputH\_ = 1 + (2paddingH\_ + imgSizeH\_ - blockH\_ + strideH\_ - 1) / - strideH\_ \\ -outputW\_ = 1 + (2paddingW\_ + imgSizeW\_ - blockW\_ + strideW\_ - 1) / - strideW\_ +outputHeight = + 1 + (2 * paddingHeight + imgHeight - blockHeight + strideHeight - 1) / + strideHeight; +outputWidth = + 1 + (2 * paddingWidth + imgWidth - blockWidth + strideWidth - 1) / + strideWidth; The expand method is the same with ExpandConvLayer, but saved the transposed -value. After expanding, output_.sequenceStartPositions will store timeline. -The number of time steps are outputH_outputW_ and the dimension of each -time step is blockH_ * blockW_ * channels_. This layer can be used after -convolution neural network, and before recurrent neural network. +value. After expanding, The number of time steps are outputHeight * outputWidth +and the dimension of each time step is blockHeight * blockWidth * channels. +This layer can be used after convolution neural network, and before recurrent neural network. )DOC"); } }; @@ -98,7 +118,7 @@ namespace ops = paddle::operators; REGISTER_OP(block_expand, ops::BlockExpandOp, ops::BlockExpandOpMaker, block_expand_grad, ops::BlockExpandOpGrad); REGISTER_OP_CPU_KERNEL( - block_expand, ops::BlockExpanddKernel); + block_expand, ops::BlockExpandKernel); REGISTER_OP_CPU_KERNEL( block_expand_grad, ops::BlockExpandGradKernel); diff --git a/paddle/operators/block_expand_op.h b/paddle/operators/block_expand_op.h index c0521dbbadb..58f9e4c6adf 100644 --- a/paddle/operators/block_expand_op.h +++ b/paddle/operators/block_expand_op.h @@ -18,10 +18,26 @@ #include "paddle/framework/eigen.h" #include "paddle/framework/op_registry.h" +#include "paddle/operators/math/img2col.h" namespace paddle { namespace operators { +inline void get_blockexpand_output_shape(int imgHeight, int imgWidth, + int blockHeight, int blockWidth, + int strideHeight, int strideWidth, + int paddingHeight, int paddingWidth, + int& outputHeight, int& outputWidth) { + outputHeight = + 1 + + (imgHeight + 2 * paddingHeight - blockHeight + strideHeight - 1) / + strideHeight; + + outputWidth = 1 + + (imgWidth + 2 * paddingWidth - blockWidth + strideWidth - 1) / + strideWidth; +} + template class BlockExpandKernel : public framework::OpKernel { public: @@ -34,39 +50,30 @@ class BlockExpandKernel : public framework::OpKernel { auto in_dim = in->dims(); int N = in_dim[0]; int C = in_dim[1]; - - int in_height = in_dim[2]; - int in_width = in_dim[3]; - - int block_height = ctx.Attr("block_height"); - int block_width = ctx.Attr("block_width"); - int stride_height = ctx.Attr("stride_height"); - int stride_width = ctx.Attr("stride_width"); - int padding_height = ctx.Attr("padding_height"); - int padding_width = ctx.Attr("padding_width"); - - int output_height = - 1 + - (in_height + 2 * padding_height - block_height + stride_height - 1) / - stride_height; - - int output_width = - 1 + - (in_width + 2 * padding_width - block_width + stride_width - 1) / - stride_width; - - Tensor col; - if (clo_format = KCFO) { - col.Resize( - {N, C, filter_height, filter_width, output_height, output_width}); - } else { - col.Resize( - {N, output_height, output_width, C, filter_height, filter_width}); - } - - for (size_t i = 0; i < N; i++) { - Im2ColFunctor(ctx, one_img, col, stride[0], - stride[1], padding[0], padding[1]); + int imgHeight = in_dim[2]; + int imgWidth = in_dim[3]; + + int blockHeight = ctx.Attr("blockHeight"); + int blockWidth = ctx.Attr("blockWidth"); + int strideHeight = ctx.Attr("strideHeight"); + int strideWidth = ctx.Attr("strideWidth"); + int paddingHeight = ctx.Attr("paddingHeight"); + int paddingWidth = ctx.Attr("paddingWidth"); + + int outputHeight = 0; + int outputWidth = 0; + + get_blockexpand_output_shape(imgHeight, imgWidth, blockHeight, blockWidth, + strideHeight, strideWidth, paddingHeight, + paddingWidth, outputHeight, outputWidth); + + for (int i = 0; i < N; i++) { + Tensor src = in->Slice(i, i + 1).Resize(C, imgHeight, imgWidth); + Tensor dst = out->Slice(i, i + 1).Resize(outputHeight, outputWidth, C, + blockHeight, blockWidth); + math::Im2ColFunctorGetPlace(), T>(ctx, src, dst, strideHeight, + strideWidth, paddingHeight, + paddingWidth); } } }; @@ -75,13 +82,7 @@ template class BlockExpandGradKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { - using Tensor = framework::Tensor; - /* - int x_num_col_dims = ctx.template Attr("x_num_col_dims"); - int y_num_col_dims = ctx.template Attr("y_num_col_dims"); - const Tensor* x = ctx.Input("X"); - const Tensor* y = ctx.Input("Y"); - */ + using namespace framework; } }; -- GitLab From 6197c09bf92324696b237bf0320ce43d28097c70 Mon Sep 17 00:00:00 2001 From: gongweibao Date: Mon, 16 Oct 2017 12:08:30 +0000 Subject: [PATCH 0005/2305] modify styles --- paddle/operators/block_expand_op.cc | 45 ++++++++++++------------ paddle/operators/block_expand_op.h | 53 +++++++++++++++-------------- 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/paddle/operators/block_expand_op.cc b/paddle/operators/block_expand_op.cc index ec467374008..b3fad3c81f4 100644 --- a/paddle/operators/block_expand_op.cc +++ b/paddle/operators/block_expand_op.cc @@ -33,32 +33,33 @@ class BlockExpandOp : public framework::OperatorWithKernel { PADDLE_ENFORCE_EQ(in_dim.size(), 4, "Input format must be NCHW."); PADDLE_ENFORCE_GE(in_dim[0], 1, "Input batchsize must >= 1."); - int blockHeight = ctx->Attrs().Get("blockHeight"); - int blockWidth = ctx->Attrs().Get("blockWidth"); - int strideHeight = ctx->Attrs().Get("strideHeight"); - int strideWidth = ctx->Attrs().Get("strideWidth"); - int paddingHeight = ctx->Attrs().Get("paddingHeight"); - int paddingWidth = ctx->Attrs().Get("paddingWidth"); + int block_height = ctx->Attrs().Get("blockHeight"); + int block_width = ctx->Attrs().Get("blockWidth"); + int stride_height = ctx->Attrs().Get("strideHeight"); + int stride_width = ctx->Attrs().Get("strideWidth"); + int padding_height = ctx->Attrs().Get("paddingHeight"); + int padding_width = ctx->Attrs().Get("paddingWidth"); int N = in_dim[0]; int C = in_dim[1]; - int imgHeight = in_dim[3]; - int imgWidth = in_dim[4]; + int img_height = in_dim[3]; + int img_width = in_dim[4]; - int outputHeight = 0; - int outputWidth = 0; + int output_height = 0; + int output_width = 0; - get_blockexpand_output_shape(imgHeight, imgWidth, blockHeight, blockWidth, - strideHeight, strideWidth, paddingHeight, - paddingWidth, outputHeight, outputWidth); + get_blockexpand_output_shape(img_height, img_width, block_height, + block_width, stride_height, stride_width, + padding_height, padding_width, output_height, + output_width); - // The result of im2col is [outputHeight, outputWidth, + // The result of im2col is [output_height, output_width, // inputChannels, filterHeight, filterWidth], and it is easy to // reshape into [seqLength, stepSize], where seqLength is equal - // outputHeight * outputWidth, stepSize is equal + // output_height * output_width, stepSize is equal // input_channels * blockHeight * blockWidth ctx->SetOutputDim( - "Out", {N, outputHeight, outputWidth, C, blockHeight, blockWidth}); + "Out", {N, output_height, output_width, C, block_height, block_width}); // ctx->ShareLoD("X", /*->*/ "Out"); } @@ -85,18 +86,18 @@ class BlockExpandOpMaker : public framework::OpProtoAndCheckerMaker { AddAttr("paddingWidth", "(int)width of padding."); AddComment(R"DOC( Expand feature map to minibatch matrix. -- matirx height is: outputHeight * outputWidth +- matirx height is: output_height * output_width - matrix width is: blockHeight * blockWidth * channels -outputHeight = - 1 + (2 * paddingHeight + imgHeight - blockHeight + strideHeight - 1) / +output_height = + 1 + (2 * paddingHeight + img_height - blockHeight + strideHeight - 1) / strideHeight; -outputWidth = - 1 + (2 * paddingWidth + imgWidth - blockWidth + strideWidth - 1) / +output_width = + 1 + (2 * paddingWidth + img_width - blockWidth + strideWidth - 1) / strideWidth; The expand method is the same with ExpandConvLayer, but saved the transposed -value. After expanding, The number of time steps are outputHeight * outputWidth +value. After expanding, The number of time steps are output_height * output_width and the dimension of each time step is blockHeight * blockWidth * channels. This layer can be used after convolution neural network, and before recurrent neural network. )DOC"); diff --git a/paddle/operators/block_expand_op.h b/paddle/operators/block_expand_op.h index 58f9e4c6adf..bd6b3078520 100644 --- a/paddle/operators/block_expand_op.h +++ b/paddle/operators/block_expand_op.h @@ -18,24 +18,25 @@ #include "paddle/framework/eigen.h" #include "paddle/framework/op_registry.h" -#include "paddle/operators/math/img2col.h" +#include "paddle/operators/math/im2col.h" namespace paddle { namespace operators { -inline void get_blockexpand_output_shape(int imgHeight, int imgWidth, - int blockHeight, int blockWidth, - int strideHeight, int strideWidth, - int paddingHeight, int paddingWidth, +inline void get_blockexpand_output_shape(int img_height, int img_width, + int block_height, int block_width, + int stride_height, int stride_width, + int padding_height, int padding_width, int& outputHeight, int& outputWidth) { outputHeight = 1 + - (imgHeight + 2 * paddingHeight - blockHeight + strideHeight - 1) / - strideHeight; + (img_height + 2 * padding_height - block_height + stride_height - 1) / + stride_height; - outputWidth = 1 + - (imgWidth + 2 * paddingWidth - blockWidth + strideWidth - 1) / - strideWidth; + outputWidth = + 1 + + (img_width + 2 * padding_width - block_width + stride_width - 1) / + stride_width; } template @@ -50,30 +51,30 @@ class BlockExpandKernel : public framework::OpKernel { auto in_dim = in->dims(); int N = in_dim[0]; int C = in_dim[1]; - int imgHeight = in_dim[2]; - int imgWidth = in_dim[3]; + int img_height = in_dim[2]; + int img_width = in_dim[3]; - int blockHeight = ctx.Attr("blockHeight"); - int blockWidth = ctx.Attr("blockWidth"); - int strideHeight = ctx.Attr("strideHeight"); - int strideWidth = ctx.Attr("strideWidth"); - int paddingHeight = ctx.Attr("paddingHeight"); - int paddingWidth = ctx.Attr("paddingWidth"); + int block_height = ctx.Attr("blockHeight"); + int block_width = ctx.Attr("blockWidth"); + int stride_height = ctx.Attr("strideHeight"); + int stride_width = ctx.Attr("strideWidth"); + int padding_height = ctx.Attr("paddingHeight"); + int padding_width = ctx.Attr("paddingWidth"); int outputHeight = 0; int outputWidth = 0; - get_blockexpand_output_shape(imgHeight, imgWidth, blockHeight, blockWidth, - strideHeight, strideWidth, paddingHeight, - paddingWidth, outputHeight, outputWidth); + get_blockexpand_output_shape( + img_height, img_width, block_height, block_width, stride_height, + stride_width, padding_height, padding_width, outputHeight, outputWidth); for (int i = 0; i < N; i++) { - Tensor src = in->Slice(i, i + 1).Resize(C, imgHeight, imgWidth); + Tensor src = in->Slice(i, i + 1).Resize(C, img_height, img_width); Tensor dst = out->Slice(i, i + 1).Resize(outputHeight, outputWidth, C, - blockHeight, blockWidth); - math::Im2ColFunctorGetPlace(), T>(ctx, src, dst, strideHeight, - strideWidth, paddingHeight, - paddingWidth); + block_height, block_width); + math::Im2ColFunctor( + ctx, src, dst, stride_height, stride_width, padding_height, + padding_width); } } }; -- GitLab From 5a9dd8ae5a5154a4e2a96becc057621a6221ca55 Mon Sep 17 00:00:00 2001 From: gongweibao Date: Tue, 17 Oct 2017 03:48:34 +0000 Subject: [PATCH 0006/2305] add gpu --- paddle/operators/block_expand_op.cc | 15 ++++++++-- paddle/operators/block_expand_op.cu | 24 +++++++++++++++ paddle/operators/block_expand_op.h | 46 +++++++++++++++++++++++++---- 3 files changed, 77 insertions(+), 8 deletions(-) diff --git a/paddle/operators/block_expand_op.cc b/paddle/operators/block_expand_op.cc index b3fad3c81f4..49c7011fe1f 100644 --- a/paddle/operators/block_expand_op.cc +++ b/paddle/operators/block_expand_op.cc @@ -109,7 +109,18 @@ class BlockExpandGradOp : public framework::OperatorWithKernel { using framework::OperatorWithKernel::OperatorWithKernel; protected: - void InferShape(framework::InferShapeContext* ctx) const override {} + void InferShape(framework::InferShapeContext* ctx) const override { + using namespace framework; + PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should not be null"); + PADDLE_ENFORCE(ctx->HasOutput("Out"), + "Output of BlockExpandOp op should not be null."); + PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Out")), + "Input(Out@GRAD) should not be null"); + + auto in_dim = ctx->GetInputDim("X"); + + ctx->SetOutputDim(GradVarName("Out"), in_dim); + } }; } // namespace operators @@ -117,7 +128,7 @@ class BlockExpandGradOp : public framework::OperatorWithKernel { namespace ops = paddle::operators; REGISTER_OP(block_expand, ops::BlockExpandOp, ops::BlockExpandOpMaker, - block_expand_grad, ops::BlockExpandOpGrad); + block_expand_grad, ops::BlockExpandGradOp); REGISTER_OP_CPU_KERNEL( block_expand, ops::BlockExpandKernel); REGISTER_OP_CPU_KERNEL( diff --git a/paddle/operators/block_expand_op.cu b/paddle/operators/block_expand_op.cu index e69de29bb2d..492ac0c9b2e 100644 --- a/paddle/operators/block_expand_op.cu +++ b/paddle/operators/block_expand_op.cu @@ -0,0 +1,24 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#define EIGEN_USE_GPU +#include "paddle/operators/block_expand_op.h" + +namespace ops = paddle::operators; + +REGISTER_OP_GPU_KERNEL( + block_expand, ops::BlockExpandKernel); +REGISTER_OP_GPU_KERNEL( + block_expand_grad, + ops::BlockExpandGradKernel); diff --git a/paddle/operators/block_expand_op.h b/paddle/operators/block_expand_op.h index bd6b3078520..b272582883b 100644 --- a/paddle/operators/block_expand_op.h +++ b/paddle/operators/block_expand_op.h @@ -69,12 +69,12 @@ class BlockExpandKernel : public framework::OpKernel { stride_width, padding_height, padding_width, outputHeight, outputWidth); for (int i = 0; i < N; i++) { - Tensor src = in->Slice(i, i + 1).Resize(C, img_height, img_width); - Tensor dst = out->Slice(i, i + 1).Resize(outputHeight, outputWidth, C, - block_height, block_width); - math::Im2ColFunctor( - ctx, src, dst, stride_height, stride_width, padding_height, - padding_width); + Tensor src = in->Slice(i, i + 1).Resize({C, img_height, img_width}); + Tensor dst = out->Slice(i, i + 1).Resize( + {outputHeight, outputWidth, C, block_height, block_width}); + math::Im2ColFunctor f; + f(ctx.device_context(), src, dst, stride_height, stride_width, + padding_height, padding_width); } } }; @@ -84,6 +84,40 @@ class BlockExpandGradKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { using namespace framework; + auto* in = ctx.Input("X"); + auto* out = ctx.Input("Out"); + auto* out_grad = ctx.Output(GradVarName("Out")); + out_grad->mutable_data(ctx.GetPlace()); + + auto in_dim = in->dims(); + int N = in_dim[0]; + int C = in_dim[1]; + int img_height = in_dim[2]; + int img_width = in_dim[3]; + + int block_height = ctx.Attr("blockHeight"); + int block_width = ctx.Attr("blockWidth"); + int stride_height = ctx.Attr("strideHeight"); + int stride_width = ctx.Attr("strideWidth"); + int padding_height = ctx.Attr("paddingHeight"); + int padding_width = ctx.Attr("paddingWidth"); + + int outputHeight = 0; + int outputWidth = 0; + + get_blockexpand_output_shape( + img_height, img_width, block_height, block_width, stride_height, + stride_width, padding_height, padding_width, outputHeight, outputWidth); + + for (int i = 0; i < N; i++) { + Tensor dst = + out_grad->Slice(i, i + 1).Resize({C, img_height, img_width}); + Tensor src = out->Slice(i, i + 1).Resize( + {outputHeight, outputWidth, C, block_height, block_width}); + math::Im2ColFunctor f; + f(ctx.device_context(), src, dst, stride_height, stride_width, + padding_height, padding_width); + } } }; -- GitLab From 45f16c90456775d80ec3fbff5c87d17c06558c5b Mon Sep 17 00:00:00 2001 From: gongweibao Date: Tue, 17 Oct 2017 07:15:19 +0000 Subject: [PATCH 0007/2305] add py test --- .../framework/tests/test_block_expand_op.py | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 python/paddle/v2/framework/tests/test_block_expand_op.py diff --git a/python/paddle/v2/framework/tests/test_block_expand_op.py b/python/paddle/v2/framework/tests/test_block_expand_op.py new file mode 100644 index 00000000000..aa4fa479a9e --- /dev/null +++ b/python/paddle/v2/framework/tests/test_block_expand_op.py @@ -0,0 +1,121 @@ +import unittest +import numpy as np +from op_test import OpTest + + +def get_output_shape(attrs, X): + img_height = X.shape[2] + img_width = X.shpe[3] + padding_height = attrs['padding_height'] + padding_width = attrs['padding_width'] + block_height = attrs['block_height'] + block_width = attrs['block_width'] + stride_height = attrs['stride_height'] + stride_width = attrs['stride_width'] + output_height = \ + 1 + \ + (img_height + 2 * padding_height - block_height + stride_height - 1) / \ + stride_height + + output_width = \ + 1 + \ + (img_width + 2 * padding_width - block_width + stride_width - 1) / \ + stride_width + + return output_height, output_width + + +""" +img: {CHW} +col: + {output_height, output_width, inputChannels, filterHeight, filterWidth} +""" + + +def img2col(attrs, im, col): + input_channels = im.shape.dims[0] + input_height = im.shape.dims[1] + input_width = im.shape.dims[2] + filter_height = col.shape.dims[3] + filter_width = col.shape.dims[4] + output_height = col.shape.dims[0] + output_width = col.shape.dims[1] + + for col_row_idx in range(0, output_height): + for col_col_idx in range(0, output_width): + for channel in range(0, input_channels): + for filter_row_idx in range(0, filter_height): + for filter_col_idx in range(0, filter_width): + im_row_offset = col_row_idx * stride_height \ + + filter_row_idx - padding_height + im_col_offset = col_col_idx * stride_width \ + + filter_col_idx - padding_width + if (im_row_offset < 0 or + im_row_offset >= input_height or + im_col_offset < 0 or + im_col_offset >= input_width): + col[col_row_idx][col_col_idx][channel][ + filter_row_idx][filter_col_idx] = 0.0 + else: + im_offset = (channel * input_height + im_row_offset + ) * input_width + im_col_offset + col[col_row_idx][col_col_idx][channel][ + filter_row_idx][filter_col_idx] = im[channel][ + im_row_offset][im_col_offset] + + +""" +img: {CHW} +col: + {output_height, output_width, inputChannels, filterHeight, filterWidth} +""" + + +def col2img(attrs, col, img): + input_channels = im.shape.dims[0] + input_height = im.shape.dims[1] + input_width = im.shape.dims[2] + filter_height = col.shape.dims[3] + filter_width = col.shape.dims[4] + output_height = col.shape.dims[0] + output_width = col.shape.dims[1] + + for col_row_idx in range(0, output_height): + for col_col_idx in range(0, output_width): + for channel in range(0, input_channels): + for filter_row_idx in range(0, filter_height): + for filter_col_idx in range(0, filter_width): + im_row_offset = \ + col_row_idx * stride_height + filter_row_idx - padding_height + im_col_offset = \ + col_col_idx * stride_width + filter_col_idx - padding_width + if (im_row_offset >= 0 and + im_row_offset < input_height and + im_col_offset >= 0 and + im_col_offset < input_width): + im[channel][im_row_offset][im_col_offset] = \ + col[col_row_idx][col_col_idx][channel][filter_row_idx][filter_col_idx] + + +class TestBlockExpandMulOp(OpTest): + def setUp(self): + self.op_type = "block_expand" + self.inputs = { + 'X': np.random.uniform(0.1, 1, [2, 3, 9, 9]).astype("float64"), + } + self.attrs = { + 'block_height': 3, + 'block_width': 3, + 'stride_height': 2, + 'stride_width': 2, + 'padding_height': 3, + 'padding_width': 3, + } + + self.outputs = {'Out': np.multiply(self.inputs['X'], self.inputs['Y'])} + + def test_check_output(self): + self.check_output() + + def test_check_grad_normal(self): + self.check_grad(['X'], 'Out') -- GitLab From 32db8db51c5384f213a0b1402d2632519da5416a Mon Sep 17 00:00:00 2001 From: gongweibao Date: Tue, 17 Oct 2017 08:12:56 +0000 Subject: [PATCH 0008/2305] fix bugs --- paddle/operators/block_expand_op.cc | 9 +- paddle/operators/block_expand_op.h | 9 +- .../framework/tests/test_block_expand_op.py | 176 +++++++++++------- 3 files changed, 120 insertions(+), 74 deletions(-) diff --git a/paddle/operators/block_expand_op.cc b/paddle/operators/block_expand_op.cc index 49c7011fe1f..37ea57f393b 100644 --- a/paddle/operators/block_expand_op.cc +++ b/paddle/operators/block_expand_op.cc @@ -23,6 +23,7 @@ class BlockExpandOp : public framework::OperatorWithKernel { protected: void InferShape(framework::InferShapeContext* ctx) const override { + printf("op infershape\n"); using namespace framework; PADDLE_ENFORCE(ctx->HasInput("X"), "Input of BlockExpandOp should not be null."); @@ -33,6 +34,7 @@ class BlockExpandOp : public framework::OperatorWithKernel { PADDLE_ENFORCE_EQ(in_dim.size(), 4, "Input format must be NCHW."); PADDLE_ENFORCE_GE(in_dim[0], 1, "Input batchsize must >= 1."); + printf("op infershape2\n"); int block_height = ctx->Attrs().Get("blockHeight"); int block_width = ctx->Attrs().Get("blockWidth"); int stride_height = ctx->Attrs().Get("strideHeight"); @@ -42,8 +44,8 @@ class BlockExpandOp : public framework::OperatorWithKernel { int N = in_dim[0]; int C = in_dim[1]; - int img_height = in_dim[3]; - int img_width = in_dim[4]; + int img_height = in_dim[2]; + int img_width = in_dim[3]; int output_height = 0; int output_width = 0; @@ -58,6 +60,8 @@ class BlockExpandOp : public framework::OperatorWithKernel { // reshape into [seqLength, stepSize], where seqLength is equal // output_height * output_width, stepSize is equal // input_channels * blockHeight * blockWidth + printf("N:%d, o_h:%d o_w:%d C:%d b_h:%d b_w:%d\n", N, output_height, + output_width, C, block_height, block_width); ctx->SetOutputDim( "Out", {N, output_height, output_width, C, block_height, block_width}); @@ -77,6 +81,7 @@ class BlockExpandOpMaker : public framework::OpProtoAndCheckerMaker { H: height W: width )DOC"); + printf("opmakeer\n"); AddOutput("Out", "(LodTensor)The output data of block_expand op,"); AddAttr("blockHeight", "(int)height of block."); AddAttr("blockWidth", "(int)width of block."); diff --git a/paddle/operators/block_expand_op.h b/paddle/operators/block_expand_op.h index b272582883b..69bd7d69878 100644 --- a/paddle/operators/block_expand_op.h +++ b/paddle/operators/block_expand_op.h @@ -44,7 +44,7 @@ class BlockExpandKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { using namespace framework; - const Tensor* in = ctx.Input("input"); + const Tensor* in = ctx.Input("X"); Tensor* out = ctx.Output("Out"); out->mutable_data(ctx.GetPlace()); @@ -68,7 +68,11 @@ class BlockExpandKernel : public framework::OpKernel { img_height, img_width, block_height, block_width, stride_height, stride_width, padding_height, padding_width, outputHeight, outputWidth); + printf("N:%d, o_h:%d o_w:%d C:%d b_h:%d b_w:%d\n", N, outputHeight, + outputWidth, C, block_height, block_width); + for (int i = 0; i < N; i++) { + printf("i:%d\n", i); Tensor src = in->Slice(i, i + 1).Resize({C, img_height, img_width}); Tensor dst = out->Slice(i, i + 1).Resize( {outputHeight, outputWidth, C, block_height, block_width}); @@ -109,6 +113,9 @@ class BlockExpandGradKernel : public framework::OpKernel { img_height, img_width, block_height, block_width, stride_height, stride_width, padding_height, padding_width, outputHeight, outputWidth); + printf("N:%d, o_h:%d o_w:%d C:%d b_h:%d b_w:%d\n", N, outputHeight, + outputWidth, C, block_height, block_width); + for (int i = 0; i < N; i++) { Tensor dst = out_grad->Slice(i, i + 1).Resize({C, img_height, img_width}); diff --git a/python/paddle/v2/framework/tests/test_block_expand_op.py b/python/paddle/v2/framework/tests/test_block_expand_op.py index aa4fa479a9e..f8f4afc880c 100644 --- a/python/paddle/v2/framework/tests/test_block_expand_op.py +++ b/python/paddle/v2/framework/tests/test_block_expand_op.py @@ -3,119 +3,153 @@ import numpy as np from op_test import OpTest -def get_output_shape(attrs, X): - img_height = X.shape[2] - img_width = X.shpe[3] - padding_height = attrs['padding_height'] - padding_width = attrs['padding_width'] - block_height = attrs['block_height'] - block_width = attrs['block_width'] - stride_height = attrs['stride_height'] - stride_width = attrs['stride_width'] - output_height = \ +def get_output_shape(attrs, x): + imgHeight = x.shape[1] + imgWidth = x.shape[2] + + paddingHeight = attrs['paddingHeight'] + paddingWidth = attrs['paddingWidth'] + blockHeight = attrs['blockHeight'] + blockWidth = attrs['blockWidth'] + strideHeight = attrs['strideHeight'] + strideWidth = attrs['strideWidth'] + + outputHeight = \ 1 + \ - (img_height + 2 * padding_height - block_height + stride_height - 1) / \ - stride_height + (imgHeight + 2 * paddingHeight - blockHeight + strideHeight - 1) / \ + strideHeight - output_width = \ + outputWidth = \ 1 + \ - (img_width + 2 * padding_width - block_width + stride_width - 1) / \ - stride_width + (imgWidth + 2 * paddingWidth - blockWidth + strideWidth - 1) / \ + strideWidth - return output_height, output_width + return outputHeight, outputWidth """ -img: {CHW} +im: {CHW} col: - {output_height, output_width, inputChannels, filterHeight, filterWidth} + {outputHeight, outputWidth, inputChannels, filterHeight, filterWidth} """ -def img2col(attrs, im, col): - input_channels = im.shape.dims[0] - input_height = im.shape.dims[1] - input_width = im.shape.dims[2] - filter_height = col.shape.dims[3] - filter_width = col.shape.dims[4] - output_height = col.shape.dims[0] - output_width = col.shape.dims[1] +def im2col(attrs, im, col): + input_channels = im.shape[0] + inputHeight = im.shape[1] + inputWidth = im.shape[2] + + outputHeight = col.shape[0] + outputWidth = col.shape[1] + filterHeight = col.shape[3] + filterWidth = col.shape[4] - for col_row_idx in range(0, output_height): - for col_col_idx in range(0, output_width): + strideHeight = attrs['strideHeight'] + strideWidth = attrs['strideWidth'] + paddingHeight = attrs['paddingHeight'] + paddingWidth = attrs['paddingWidth'] + + for col_row_idx in range(0, outputHeight): + for col_col_idx in range(0, outputWidth): for channel in range(0, input_channels): - for filter_row_idx in range(0, filter_height): - for filter_col_idx in range(0, filter_width): - im_row_offset = col_row_idx * stride_height \ - + filter_row_idx - padding_height - im_col_offset = col_col_idx * stride_width \ - + filter_col_idx - padding_width - if (im_row_offset < 0 or - im_row_offset >= input_height or + for filter_row_idx in range(0, filterHeight): + for filter_col_idx in range(0, filterWidth): + im_row_offset = col_row_idx * strideHeight \ + + filter_row_idx - paddingHeight + + im_col_offset = col_col_idx * strideWidth \ + + filter_col_idx - paddingWidth + + if (im_row_offset < 0 or im_row_offset >= inputHeight or im_col_offset < 0 or - im_col_offset >= input_width): - col[col_row_idx][col_col_idx][channel][ + im_col_offset >= inputWidth): + col[col_row_idx][col_col_idx][channel][\ filter_row_idx][filter_col_idx] = 0.0 else: - im_offset = (channel * input_height + im_row_offset - ) * input_width + im_col_offset - col[col_row_idx][col_col_idx][channel][ - filter_row_idx][filter_col_idx] = im[channel][ + im_offset = (channel * inputHeight + im_row_offset \ + ) * inputWidth + im_col_offset + + col[col_row_idx][col_col_idx][channel][\ + filter_row_idx][filter_col_idx] = im[channel][ \ im_row_offset][im_col_offset] """ img: {CHW} col: - {output_height, output_width, inputChannels, filterHeight, filterWidth} + {outputHeight, outputWidth, inputChannels, filterHeight, filterWidth} """ def col2img(attrs, col, img): - input_channels = im.shape.dims[0] - input_height = im.shape.dims[1] - input_width = im.shape.dims[2] - filter_height = col.shape.dims[3] - filter_width = col.shape.dims[4] - output_height = col.shape.dims[0] - output_width = col.shape.dims[1] - - for col_row_idx in range(0, output_height): - for col_col_idx in range(0, output_width): + input_channels = im.shape[0] + inputHeight = im.shape[1] + inputWidth = im.shape[2] + + outputHeight = col.shape[0] + outputWidth = col.shape[1] + filterHeight = col.shape[3] + filterWidth = col.shape[4] + + strideHeight = attrs['strideHeight'] + strideWidth = attrs['strideWidth'] + paddingHeight = attrs['paddingHeight'] + paddingWidth = attrs['paddingWidth'] + + for col_row_idx in range(0, outputHeight): + for col_col_idx in range(0, outputWidth): for channel in range(0, input_channels): - for filter_row_idx in range(0, filter_height): - for filter_col_idx in range(0, filter_width): + for filter_row_idx in range(0, filterHeight): + for filter_col_idx in range(0, filterWidth): im_row_offset = \ - col_row_idx * stride_height + filter_row_idx - padding_height + col_row_idx * strideHeight + filter_row_idx - paddingHeight im_col_offset = \ - col_col_idx * stride_width + filter_col_idx - padding_width + col_col_idx * strideWidth + filter_col_idx - paddingWidth if (im_row_offset >= 0 and - im_row_offset < input_height and + im_row_offset < inputHeight and im_col_offset >= 0 and - im_col_offset < input_width): + im_col_offset < inputWidth): im[channel][im_row_offset][im_col_offset] = \ col[col_row_idx][col_col_idx][channel][filter_row_idx][filter_col_idx] class TestBlockExpandMulOp(OpTest): def setUp(self): - self.op_type = "block_expand" - self.inputs = { - 'X': np.random.uniform(0.1, 1, [2, 3, 9, 9]).astype("float64"), - } - self.attrs = { - 'block_height': 3, - 'block_width': 3, - 'stride_height': 2, - 'stride_width': 2, - 'padding_height': 3, - 'padding_width': 3, + x = np.random.uniform(0.1, 1, [3, 9, 9]).astype("float32") + attrs = { + 'blockHeight': 3, + 'blockWidth': 3, + 'strideHeight': 2, + 'strideWidth': 2, + 'paddingHeight': 3, + 'paddingWidth': 3, } - self.outputs = {'Out': np.multiply(self.inputs['X'], self.inputs['Y'])} + outputHeight, outputWidth = get_output_shape(attrs, x) + out = np.random.uniform(0.1, 1,\ + [outputHeight, outputWidth, x.shape[0], \ + attrs['blockHeight'], attrs['blockWidth']]).astype("float32") + + self.op_type = "block_expand" + self.inputs = {'X': x.reshape(1, 3, 9, 9)} + self.attrs = attrs + + im2col(attrs, x, out) + self.outputs = { + 'Out':out.reshape(1, outputHeight, outputWidth, x.shape[0], \ + attrs['blockHeight'], attrs['blockWidth']) + } + #print out def test_check_output(self): self.check_output() + print 1 + """ def test_check_grad_normal(self): self.check_grad(['X'], 'Out') + """ + + +if __name__ == '__main__': + unittest.main() -- GitLab From d3ac3393fc803d210a1bab4f89249657b2e8786c Mon Sep 17 00:00:00 2001 From: gongweibao Date: Tue, 17 Oct 2017 12:43:32 +0000 Subject: [PATCH 0009/2305] fix bugs --- paddle/operators/block_expand_op.cc | 14 +------ paddle/operators/block_expand_op.h | 25 +++++------- .../framework/tests/test_block_expand_op.py | 40 ++++++++++++------- 3 files changed, 38 insertions(+), 41 deletions(-) diff --git a/paddle/operators/block_expand_op.cc b/paddle/operators/block_expand_op.cc index 37ea57f393b..d72c6b2de1d 100644 --- a/paddle/operators/block_expand_op.cc +++ b/paddle/operators/block_expand_op.cc @@ -23,7 +23,6 @@ class BlockExpandOp : public framework::OperatorWithKernel { protected: void InferShape(framework::InferShapeContext* ctx) const override { - printf("op infershape\n"); using namespace framework; PADDLE_ENFORCE(ctx->HasInput("X"), "Input of BlockExpandOp should not be null."); @@ -34,7 +33,6 @@ class BlockExpandOp : public framework::OperatorWithKernel { PADDLE_ENFORCE_EQ(in_dim.size(), 4, "Input format must be NCHW."); PADDLE_ENFORCE_GE(in_dim[0], 1, "Input batchsize must >= 1."); - printf("op infershape2\n"); int block_height = ctx->Attrs().Get("blockHeight"); int block_width = ctx->Attrs().Get("blockWidth"); int stride_height = ctx->Attrs().Get("strideHeight"); @@ -60,8 +58,6 @@ class BlockExpandOp : public framework::OperatorWithKernel { // reshape into [seqLength, stepSize], where seqLength is equal // output_height * output_width, stepSize is equal // input_channels * blockHeight * blockWidth - printf("N:%d, o_h:%d o_w:%d C:%d b_h:%d b_w:%d\n", N, output_height, - output_width, C, block_height, block_width); ctx->SetOutputDim( "Out", {N, output_height, output_width, C, block_height, block_width}); @@ -81,7 +77,6 @@ class BlockExpandOpMaker : public framework::OpProtoAndCheckerMaker { H: height W: width )DOC"); - printf("opmakeer\n"); AddOutput("Out", "(LodTensor)The output data of block_expand op,"); AddAttr("blockHeight", "(int)height of block."); AddAttr("blockWidth", "(int)width of block."); @@ -117,14 +112,9 @@ class BlockExpandGradOp : public framework::OperatorWithKernel { void InferShape(framework::InferShapeContext* ctx) const override { using namespace framework; PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) should not be null"); - PADDLE_ENFORCE(ctx->HasOutput("Out"), - "Output of BlockExpandOp op should not be null."); PADDLE_ENFORCE(ctx->HasInput(framework::GradVarName("Out")), - "Input(Out@GRAD) should not be null"); - - auto in_dim = ctx->GetInputDim("X"); - - ctx->SetOutputDim(GradVarName("Out"), in_dim); + "Input(Out@GRAD) shouldn't be null."); + ctx->SetOutputDim(framework::GradVarName("X"), ctx->GetInputDim("X")); } }; diff --git a/paddle/operators/block_expand_op.h b/paddle/operators/block_expand_op.h index 69bd7d69878..38d0626c730 100644 --- a/paddle/operators/block_expand_op.h +++ b/paddle/operators/block_expand_op.h @@ -68,11 +68,7 @@ class BlockExpandKernel : public framework::OpKernel { img_height, img_width, block_height, block_width, stride_height, stride_width, padding_height, padding_width, outputHeight, outputWidth); - printf("N:%d, o_h:%d o_w:%d C:%d b_h:%d b_w:%d\n", N, outputHeight, - outputWidth, C, block_height, block_width); - for (int i = 0; i < N; i++) { - printf("i:%d\n", i); Tensor src = in->Slice(i, i + 1).Resize({C, img_height, img_width}); Tensor dst = out->Slice(i, i + 1).Resize( {outputHeight, outputWidth, C, block_height, block_width}); @@ -89,9 +85,12 @@ class BlockExpandGradKernel : public framework::OpKernel { void Compute(const framework::ExecutionContext& ctx) const override { using namespace framework; auto* in = ctx.Input("X"); - auto* out = ctx.Input("Out"); - auto* out_grad = ctx.Output(GradVarName("Out")); - out_grad->mutable_data(ctx.GetPlace()); + auto* d_out = ctx.Input(framework::GradVarName("Out")); + auto* d_x = ctx.Output(GradVarName("X")); + d_x->mutable_data(ctx.GetPlace()); + + auto x_v = framework::EigenVector::Flatten(*d_x); + x_v.device(ctx.GetEigenDevice()) = x_v.constant(0.0); auto in_dim = in->dims(); int N = in_dim[0]; @@ -113,16 +112,12 @@ class BlockExpandGradKernel : public framework::OpKernel { img_height, img_width, block_height, block_width, stride_height, stride_width, padding_height, padding_width, outputHeight, outputWidth); - printf("N:%d, o_h:%d o_w:%d C:%d b_h:%d b_w:%d\n", N, outputHeight, - outputWidth, C, block_height, block_width); - for (int i = 0; i < N; i++) { - Tensor dst = - out_grad->Slice(i, i + 1).Resize({C, img_height, img_width}); - Tensor src = out->Slice(i, i + 1).Resize( + Tensor dst = d_x->Slice(i, i + 1).Resize({C, img_height, img_width}); + Tensor src = d_out->Slice(i, i + 1).Resize( {outputHeight, outputWidth, C, block_height, block_width}); - math::Im2ColFunctor f; - f(ctx.device_context(), src, dst, stride_height, stride_width, + math::Col2ImFunctor f; + f(ctx.device_context(), dst, src, stride_height, stride_width, padding_height, padding_width); } } diff --git a/python/paddle/v2/framework/tests/test_block_expand_op.py b/python/paddle/v2/framework/tests/test_block_expand_op.py index f8f4afc880c..c85f3a1ef1f 100644 --- a/python/paddle/v2/framework/tests/test_block_expand_op.py +++ b/python/paddle/v2/framework/tests/test_block_expand_op.py @@ -113,16 +113,30 @@ def col2img(attrs, col, img): col[col_row_idx][col_col_idx][channel][filter_row_idx][filter_col_idx] -class TestBlockExpandMulOp(OpTest): +class TestBlockExpandOp(OpTest): + def get_input_data(self, C, H, W): + x = np.random.uniform(0.1, 1, [C, H, W]).astype("float32") + for c in range(0, C): + for h in range(0, H): + for w in range(0, W): + #x[c][h][w] = c * H * W + h *W + w + x[c][h][w] = 0.2 + 0.01 * (c * H * W + h * W + w) + return x + def setUp(self): - x = np.random.uniform(0.1, 1, [3, 9, 9]).astype("float32") + C = 3 + H = 4 + W = 4 + x = self.get_input_data(C, H, W) + #print x + attrs = { - 'blockHeight': 3, - 'blockWidth': 3, - 'strideHeight': 2, - 'strideWidth': 2, - 'paddingHeight': 3, - 'paddingWidth': 3, + 'blockHeight': 2, + 'blockWidth': 2, + 'strideHeight': 1, + 'strideWidth': 1, + 'paddingHeight': 1, + 'paddingWidth': 1, } outputHeight, outputWidth = get_output_shape(attrs, x) @@ -131,7 +145,7 @@ class TestBlockExpandMulOp(OpTest): attrs['blockHeight'], attrs['blockWidth']]).astype("float32") self.op_type = "block_expand" - self.inputs = {'X': x.reshape(1, 3, 9, 9)} + self.inputs = {'X': x.reshape(1, C, H, W)} self.attrs = attrs im2col(attrs, x, out) @@ -139,16 +153,14 @@ class TestBlockExpandMulOp(OpTest): 'Out':out.reshape(1, outputHeight, outputWidth, x.shape[0], \ attrs['blockHeight'], attrs['blockWidth']) } - #print out + """ def test_check_output(self): self.check_output() - print 1 - """ + def test_check_grad_normal(self): - self.check_grad(['X'], 'Out') - """ + self.check_grad(['X'], 'Out', max_relative_error=0.01) if __name__ == '__main__': -- GitLab From 4422a556dca7c9461dd7fdcf91b96a3e429aaf66 Mon Sep 17 00:00:00 2001 From: gongweibao Date: Tue, 17 Oct 2017 12:46:33 +0000 Subject: [PATCH 0010/2305] rm not need --- .../framework/tests/test_block_expand_op.py | 28 ++++++++----------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/python/paddle/v2/framework/tests/test_block_expand_op.py b/python/paddle/v2/framework/tests/test_block_expand_op.py index c85f3a1ef1f..4c66493d6e9 100644 --- a/python/paddle/v2/framework/tests/test_block_expand_op.py +++ b/python/paddle/v2/framework/tests/test_block_expand_op.py @@ -27,14 +27,12 @@ def get_output_shape(attrs, x): return outputHeight, outputWidth -""" -im: {CHW} -col: - {outputHeight, outputWidth, inputChannels, filterHeight, filterWidth} -""" - - def im2col(attrs, im, col): + """ + im: {CHW} + col: + {outputHeight, outputWidth, inputChannels, filterHeight, filterWidth} + """ input_channels = im.shape[0] inputHeight = im.shape[1] inputWidth = im.shape[2] @@ -74,14 +72,12 @@ def im2col(attrs, im, col): im_row_offset][im_col_offset] -""" -img: {CHW} -col: - {outputHeight, outputWidth, inputChannels, filterHeight, filterWidth} -""" - - def col2img(attrs, col, img): + """ + img: {CHW} + col: + {outputHeight, outputWidth, inputChannels, filterHeight, filterWidth} + """ input_channels = im.shape[0] inputHeight = im.shape[1] inputWidth = im.shape[2] @@ -154,13 +150,11 @@ class TestBlockExpandOp(OpTest): attrs['blockHeight'], attrs['blockWidth']) } - """ def test_check_output(self): self.check_output() - """ def test_check_grad_normal(self): - self.check_grad(['X'], 'Out', max_relative_error=0.01) + self.check_grad(['X'], 'Out') if __name__ == '__main__': -- GitLab From db694172bed8ee621e468546e9bf4c4c42e92602 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Thu, 2 Nov 2017 10:28:27 +0800 Subject: [PATCH 0011/2305] Add edit distance operator --- paddle/operators/ctc_edit_distance_op.cc | 74 ++++++++++++++++++ paddle/operators/ctc_edit_distance_op.h | 78 +++++++++++++++++++ .../framework/tests/test_ctc_edit_distance.py | 60 ++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 paddle/operators/ctc_edit_distance_op.cc create mode 100644 paddle/operators/ctc_edit_distance_op.h create mode 100644 python/paddle/v2/framework/tests/test_ctc_edit_distance.py diff --git a/paddle/operators/ctc_edit_distance_op.cc b/paddle/operators/ctc_edit_distance_op.cc new file mode 100644 index 00000000000..7b45ccc72e9 --- /dev/null +++ b/paddle/operators/ctc_edit_distance_op.cc @@ -0,0 +1,74 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include "paddle/operators/ctc_edit_distance_op.h" + +namespace paddle { +namespace operators { + +class CTCEditDistanceOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext *ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("X1"), "Input(X1) shouldn't be null."); + PADDLE_ENFORCE(ctx->HasInput("X2"), "Input(X2) shouldn't be null."); + PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) shouldn't be null."); + ctx->SetOutputDim("Out", {1}); + } +}; + +class CTCEditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { + public: + CTCEditDistanceOpMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X1", + "(2-D tensor with shape [M x 1]) The indices for " + "hypothesis string"); + AddInput("X2", + "(2-D tensor with shape [batch_size x 1]) The indices " + "for reference string."); + AddAttr("normalized", + "(bool, default false) Indicated whether " + "normalize. the Output(Out) by the length of reference " + "string (X2).") + .SetDefault(false); + AddOutput("Out", + "(2-D tensor with shape [1 x 1]) " + "The output distance of CTCEditDistance operator."); + AddComment(R"DOC( + +CTCEditDistance operator computes the edit distance of two sequences, one named +hypothesis and another named reference. + +Edit distance measures how dissimilar two strings, one is hypothesis and another +is reference, are by counting the minimum number of operations to transform +one string into anthor. + +)DOC"); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; + +REGISTER_OP_WITHOUT_GRADIENT(ctc_edit_distance, ops::CTCEditDistanceOp, + ops::CTCEditDistanceOpMaker); +REGISTER_OP_CPU_KERNEL( + ctc_edit_distance, + ops::CTCEditDistanceKernel, + ops::CTCEditDistanceKernel); diff --git a/paddle/operators/ctc_edit_distance_op.h b/paddle/operators/ctc_edit_distance_op.h new file mode 100644 index 00000000000..d0494b4b1b1 --- /dev/null +++ b/paddle/operators/ctc_edit_distance_op.h @@ -0,0 +1,78 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#pragma once +#include +#include "paddle/framework/eigen.h" +#include "paddle/framework/op_registry.h" + +namespace paddle { +namespace operators { + +template +class CTCEditDistanceKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const { + auto* out_t = ctx.Output("Out"); + + auto* x1_t = ctx.Input("X1"); + auto* x2_t = ctx.Input("X2"); + + out_t->mutable_data(ctx.GetPlace()); + + auto normalized = ctx.Attr("normalized"); + + auto m = x1_t->numel(); + auto n = x2_t->numel(); + float distance = 0.0; + if (m == 0) { + distance = n; + } else if (n == 0) { + distance = m; + } else { + framework::Tensor dist_t; + dist_t.Resize({m + 1, n + 1}); + dist_t.mutable_data(ctx.GetPlace()); + auto dist = dist_t.data(); + auto x1 = x1_t->data(); + auto x2 = x2_t->data(); + for (int i = 0; i < m + 1; ++i) { + dist[i * (n + 1)] = i; // dist[i][0] = i; + } + for (int j = 0; j < n + 1; ++j) { + dist[j] = j; // dist[0][j] = j; + } + for (int i = 1; i < m + 1; ++i) { + for (int j = 1; j < n + 1; ++j) { + int cost = x1[i - 1] == x2[j - 1] ? 0 : 1; + int deletions = dist[(i - 1) * (n + 1) + j] + 1; + int insertions = dist[i * (n + 1) + (j - 1)] + 1; + int substitutions = dist[(i - 1) * (n + 1) + (j - 1)] + cost; + dist[i * (n + 1) + j] = + std::min(deletions, std::min(insertions, substitutions)); + } + } + distance = dist[m * (n + 1) + n]; + } + + if (normalized) { + distance = distance / n; + } + auto out = out_t->data(); + out[0] = distance; + } +}; + +} // namespace operators +} // namespace paddle diff --git a/python/paddle/v2/framework/tests/test_ctc_edit_distance.py b/python/paddle/v2/framework/tests/test_ctc_edit_distance.py new file mode 100644 index 00000000000..a6d9dfdf067 --- /dev/null +++ b/python/paddle/v2/framework/tests/test_ctc_edit_distance.py @@ -0,0 +1,60 @@ +import unittest +import numpy as np +from op_test import OpTest + + +def Levenshtein(hyp, ref): + """ Compute the Levenshtein distance between two strings. + + :param hyp: + :type hyp: list + :param ref: + :type ref: list + """ + m = len(hyp) + n = len(ref) + if m == 0: + return n + if n == 0: + return m + + dist = np.zeros((m + 1, n + 1)) + for i in range(0, m + 1): + dist[i][0] = i + for j in range(0, n + 1): + dist[0][j] = j + + for i in range(1, m + 1): + for j in range(1, n + 1): + cost = 0 if hyp[i - 1] == ref[j - 1] else 1 + deletion = dist[i - 1][j] + 1 + insertion = dist[i][j - 1] + 1 + substitution = dist[i - 1][j - 1] + cost + dist[i][j] = min(deletion, insertion, substitution) + return dist[m][n] + + +class TestCTCEditDistanceOp(OpTest): + def setUp(self): + self.op_type = "ctc_edit_distance" + normalized = True + x1 = np.array([0, 12, 3, 5]).astype("int64") + x2 = np.array([0, 12, 4, 7, 8]).astype("int64") + + distance = Levenshtein(hyp=x1, ref=x2) + if normalized is True: + distance = distance / len(x2) + print "distance = ", distance + self.attrs = {'normalized': normalized} + self.inputs = {'X1': x1, 'X2': x2} + self.outputs = {'Out': distance} + + def test_check_output(self): + self.check_output() + + +if __name__ == '__main__': + #x1 = ['c', 'a', 'f', 'e'] + #x2 = ['c', 'o', 'f', 'f', 'e', 'e'] + #print Levenshtein(x1, x2) + unittest.main() -- GitLab From b7a4e3d72c58a6ca39b6434888c8144558d0bdbb Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Thu, 2 Nov 2017 12:12:31 +0800 Subject: [PATCH 0012/2305] rename some variables in ctc_edit_distance_op --- paddle/operators/ctc_edit_distance_op.cc | 4 ++-- paddle/operators/ctc_edit_distance_op.h | 21 +++++++++---------- .../framework/tests/test_ctc_edit_distance.py | 8 ++----- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/paddle/operators/ctc_edit_distance_op.cc b/paddle/operators/ctc_edit_distance_op.cc index 7b45ccc72e9..fae5cfc1179 100644 --- a/paddle/operators/ctc_edit_distance_op.cc +++ b/paddle/operators/ctc_edit_distance_op.cc @@ -38,11 +38,11 @@ class CTCEditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { "(2-D tensor with shape [M x 1]) The indices for " "hypothesis string"); AddInput("X2", - "(2-D tensor with shape [batch_size x 1]) The indices " + "(2-D tensor with shape [N x 1]) The indices " "for reference string."); AddAttr("normalized", "(bool, default false) Indicated whether " - "normalize. the Output(Out) by the length of reference " + "normalize the Output(Out) by the length of reference " "string (X2).") .SetDefault(false); AddOutput("Out", diff --git a/paddle/operators/ctc_edit_distance_op.h b/paddle/operators/ctc_edit_distance_op.h index d0494b4b1b1..a52960f1ef7 100644 --- a/paddle/operators/ctc_edit_distance_op.h +++ b/paddle/operators/ctc_edit_distance_op.h @@ -47,20 +47,19 @@ class CTCEditDistanceKernel : public framework::OpKernel { auto dist = dist_t.data(); auto x1 = x1_t->data(); auto x2 = x2_t->data(); - for (int i = 0; i < m + 1; ++i) { - dist[i * (n + 1)] = i; // dist[i][0] = i; + for (size_t i = 0; i < m + 1; ++i) { + dist[i * (n + 1)] = i; } - for (int j = 0; j < n + 1; ++j) { - dist[j] = j; // dist[0][j] = j; + for (size_t j = 0; j < n + 1; ++j) { + dist[j] = j; } - for (int i = 1; i < m + 1; ++i) { - for (int j = 1; j < n + 1; ++j) { + for (size_t i = 1; i < m + 1; ++i) { + for (size_t j = 1; j < n + 1; ++j) { int cost = x1[i - 1] == x2[j - 1] ? 0 : 1; - int deletions = dist[(i - 1) * (n + 1) + j] + 1; - int insertions = dist[i * (n + 1) + (j - 1)] + 1; - int substitutions = dist[(i - 1) * (n + 1) + (j - 1)] + cost; - dist[i * (n + 1) + j] = - std::min(deletions, std::min(insertions, substitutions)); + int dels = dist[(i - 1) * (n + 1) + j] + 1; + int ins = dist[i * (n + 1) + (j - 1)] + 1; + int subs = dist[(i - 1) * (n + 1) + (j - 1)] + cost; + dist[i * (n + 1) + j] = std::min(dels, std::min(ins, subs)); } } distance = dist[m * (n + 1) + n]; diff --git a/python/paddle/v2/framework/tests/test_ctc_edit_distance.py b/python/paddle/v2/framework/tests/test_ctc_edit_distance.py index a6d9dfdf067..8a6b0b5390e 100644 --- a/python/paddle/v2/framework/tests/test_ctc_edit_distance.py +++ b/python/paddle/v2/framework/tests/test_ctc_edit_distance.py @@ -6,9 +6,9 @@ from op_test import OpTest def Levenshtein(hyp, ref): """ Compute the Levenshtein distance between two strings. - :param hyp: + :param hyp: hypothesis string in index :type hyp: list - :param ref: + :param ref: reference string in index :type ref: list """ m = len(hyp) @@ -44,7 +44,6 @@ class TestCTCEditDistanceOp(OpTest): distance = Levenshtein(hyp=x1, ref=x2) if normalized is True: distance = distance / len(x2) - print "distance = ", distance self.attrs = {'normalized': normalized} self.inputs = {'X1': x1, 'X2': x2} self.outputs = {'Out': distance} @@ -54,7 +53,4 @@ class TestCTCEditDistanceOp(OpTest): if __name__ == '__main__': - #x1 = ['c', 'a', 'f', 'e'] - #x2 = ['c', 'o', 'f', 'f', 'e', 'e'] - #print Levenshtein(x1, x2) unittest.main() -- GitLab From 7dc584f5c437f76383a9b83b6208c25bfaf4a82e Mon Sep 17 00:00:00 2001 From: xzl Date: Mon, 20 Nov 2017 11:54:44 +0800 Subject: [PATCH 0013/2305] add upsample layer --- paddle/cuda/include/hl_cnn.h | 42 ++++++ paddle/cuda/include/stub/hl_cnn_stub.h | 18 +++ paddle/cuda/src/hl_cuda_cnn.cu | 76 +++++++++++ paddle/gserver/layers/UpsampleLayer.cpp | 107 +++++++++++++++ paddle/gserver/layers/UpsampleLayer.h | 54 ++++++++ paddle/math/Matrix.cpp | 126 ++++++++++++++++++ paddle/math/Matrix.h | 52 ++++++++ proto/ModelConfig.proto | 11 ++ python/paddle/trainer/config_parser.py | 44 ++++++ .../paddle/trainer_config_helpers/layers.py | 77 +++++++++++ 10 files changed, 607 insertions(+) create mode 100644 paddle/gserver/layers/UpsampleLayer.cpp create mode 100644 paddle/gserver/layers/UpsampleLayer.h diff --git a/paddle/cuda/include/hl_cnn.h b/paddle/cuda/include/hl_cnn.h index 89c1f48edac..c8dd3eb91e1 100644 --- a/paddle/cuda/include/hl_cnn.h +++ b/paddle/cuda/include/hl_cnn.h @@ -366,4 +366,46 @@ extern void hl_maxout_backward(real* inGrad, size_t featLen, size_t groups); +/** + * @brief Upsample forward. + * @param[in] inputData input data. + * @param[out] maskData the mask data from MaxPoolWithMaskLayer. + * @param[out] batchSize the batch size of the input. + * @param[in] imgSizeH image height. + * @param[in] imgSizeW image width. + * @param[in] channels the input channels. + * @param[in] outputH the output height. + * @param[in] outputW the output widht. + * @param[out] outputData output data. + */ +extern void hl_upsample_forward(real *inputData, real *maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real *outputData); + +/** + * @brief Upsample backward. + * @param[in] outputGradData the output grad data. + * @param[out] maskData the mask data from MaxPoolWithMaskLayer. + * @param[out] batchSize the batch size of the input. + * @param[in] imgSizeH image height. + * @param[in] imgSizeW image width. + * @param[in] channels the input channels. + * @param[in] outputH the output height. + * @param[in] outputW the output widht. + * @param[out] inputGradData the input grad data. + */ +extern void hl_upsample_backward(real *outputGradData, real *maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real *inputGradData); + #endif // HL_CNN_H_ diff --git a/paddle/cuda/include/stub/hl_cnn_stub.h b/paddle/cuda/include/stub/hl_cnn_stub.h index 968ed4840ff..ef1f67980eb 100644 --- a/paddle/cuda/include/stub/hl_cnn_stub.h +++ b/paddle/cuda/include/stub/hl_cnn_stub.h @@ -222,4 +222,22 @@ inline void hl_maxout_backward(real* inGrad, size_t featLen, size_t group) {} +inline void hl_upsample_forward(real *inputData, real *maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real *outputData) {} + +inline void hl_upsample_backward(real *outputGradData, real *maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real *inputGradData) {} + #endif // HL_CNN_STUB_H_ diff --git a/paddle/cuda/src/hl_cuda_cnn.cu b/paddle/cuda/src/hl_cuda_cnn.cu index 3699b1e8ae9..966c406a868 100644 --- a/paddle/cuda/src/hl_cuda_cnn.cu +++ b/paddle/cuda/src/hl_cuda_cnn.cu @@ -1020,3 +1020,79 @@ void hl_maxout_backward(real* inGrad, num_kernels, inGrad, outGrad, idData, size, featLen, groups); CHECK_SYNC("hl_maxout_backward failed"); } + +__global__ void upsampleForwardCompute(real* input_data, + real* mask_data, + size_t nthreads, + size_t in_h, + size_t in_w, + size_t out_h, + size_t out_w, + real* output_data) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index < nthreads) { + int offset = index / (in_w * in_h) * out_h * out_w; + int upsample_idx = static_cast(mask_data[index]); + output_data[offset + upsample_idx] = input_data[index]; + } +} + +__global__ void upsampleBackwardCompute(real* out_grad, + real* mask_data, + size_t nthreads, + size_t in_h, + size_t in_w, + size_t out_h, + size_t out_w, + real* input_grad) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index < nthreads) { + int offset = index / (in_w * in_h) * out_h * out_w; + int upsample_idx = static_cast(mask_data[index]); + input_grad[index] = out_grad[offset + upsample_idx]; + } +} + +void hl_upsample_forward(real* inputData, + real* maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real* outputData) { + int num_kernels = batchSize * imgSizeH * imgSizeW * channels; + int blocks = (num_kernels + 1024 - 1) / 1024; + upsampleForwardCompute<<>>(inputData, + maskData, + num_kernels, + imgSizeH, + imgSizeW, + outputH, + outputW, + outputData); + CHECK_SYNC("hl_upsample_forward failed"); +} + +void hl_upsample_backward(real* outputGradData, + real* maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real* inputGradData) { + int num_kernels = batchSize * imgSizeH * imgSizeW * channels; + int blocks = (num_kernels + 1024 - 1) / 1024; + upsampleBackwardCompute<<>>(outputGradData, + maskData, + num_kernels, + imgSizeH, + imgSizeW, + outputH, + outputW, + inputGradData); + CHECK_SYNC("hl_upsample_backward failed"); +} diff --git a/paddle/gserver/layers/UpsampleLayer.cpp b/paddle/gserver/layers/UpsampleLayer.cpp new file mode 100644 index 00000000000..300bb82d688 --- /dev/null +++ b/paddle/gserver/layers/UpsampleLayer.cpp @@ -0,0 +1,107 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and + limitations under the License. */ + +#include "UpsampleLayer.h" +#include "iostream" + +namespace paddle { + +REGISTER_LAYER(upsample, UpsampleLayer); + +size_t UpsampleLayer::getOutputSize() { + if (upsampleSize_ == 0) { + upsampleSize_ = imgSize_ * scale_ - static_cast(padOutX_); + upsampleSizeY_ = imgSizeY_ * scaleY_ - static_cast(padOutY_); + } + return upsampleSize_ * upsampleSizeY_ * channels_; +} + +bool UpsampleLayer::init(const LayerMap& layerMap, + const ParameterMap& parameterMap) { + Layer::init(layerMap, parameterMap); + CHECK_EQ(inputLayers_.size(), 2U); + CHECK_EQ(config_.inputs_size(), 2); + const auto& conf = config_.inputs(0).upsample_conf(); + const auto& img_conf = conf.image_conf(); + + imgSizeY_ = + img_conf.has_img_size_y() ? img_conf.img_size_y() : img_conf.img_size(); + imgSize_ = img_conf.img_size(); + channels_ = img_conf.channels(); + + CHECK((conf.has_upsample_size()) || (conf.has_scale())) + << "scale or upsample_size is required."; + + if (conf.has_upsample_size()) { + upsampleSize_ = conf.upsample_size(); + upsampleSizeY_ = upsampleSize_; + if (conf.has_upsample_size_y()) { + upsampleSizeY_ = conf.upsample_size_y(); + } + } else { + if (!conf.has_scale_y()) { + scale_ = scaleY_ = conf.scale_y(); + CHECK_GT(static_cast(scale_), 1); + } else { + scale_ = conf.scale(); + scaleY_ = conf.scale_y(); + } + padOutX_ = conf.pad_out_x(); + padOutY_ = conf.pad_out_y(); + CHECK(!padOutX_ || scale_ == 2) + << "Output height padding compensation requires scale_ == 2"; + CHECK(!padOutY_ || scaleY_ == 2) + << "Output width padding compensation requires scaleY_ == 2"; + upsampleSize_ = upsampleSizeY_ = 0; + } + return true; +} + +void UpsampleLayer::forward(PassType passType) { + Layer::forward(passType); + + MatrixPtr input = getInputValue(0); + MatrixPtr mask = inputLayers_[1]->getOutput("mask").value; + + size_t batchSize = input->getHeight(); + size_t outSize = getOutputSize(); + + CHECK_EQ(input->getWidth(), mask->getWidth()); + CHECK_EQ(mask->getHeight(), batchSize); + resetOutput(batchSize, outSize); + + MatrixPtr output = getOutputValue(); + output->upsampleForward(*input, + *mask, + imgSize_, + imgSizeY_, + channels_, + upsampleSize_, + upsampleSizeY_); +} + +void UpsampleLayer::backward(const UpdateCallback& callback) { + MatrixPtr mask = inputLayers_[1]->getOutput("mask").value; + MatrixPtr inputGrad = getInputGrad(0); + MatrixPtr outputGrad = getOutputGrad(); + inputGrad->upsampleBackward(*outputGrad, + *mask, + imgSize_, + imgSizeY_, + channels_, + upsampleSize_, + upsampleSizeY_); +} + +} // namespace paddle diff --git a/paddle/gserver/layers/UpsampleLayer.h b/paddle/gserver/layers/UpsampleLayer.h new file mode 100644 index 00000000000..2ae9363439e --- /dev/null +++ b/paddle/gserver/layers/UpsampleLayer.h @@ -0,0 +1,54 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include +#include "Layer.h" +#include "paddle/math/Matrix.h" +#include "paddle/utils/Logging.h" +#include "paddle/utils/Stat.h" + +namespace paddle { + +/** + * This layer transpose the pooling process. + * It takes two input, the first input is the input data, and + * the second is the mask data from the max-pool-with-mask layer. + * + */ + +class UpsampleLayer : public Layer { +public: + explicit UpsampleLayer(const LayerConfig& config) : Layer(config) {} + + ~UpsampleLayer() {} + + bool init(const LayerMap& layerMap, + const ParameterMap& parameterMap) override; + + void forward(PassType passType) override; + void backward(const UpdateCallback& callback) override; + + size_t getOutputSize(); + +protected: + size_t scale_, scaleY_; + size_t upsampleSize_, upsampleSizeY_; + size_t padOutX_, padOutY_; + size_t imgSize_, imgSizeY_; + size_t channels_; +}; + +} // namespace paddle diff --git a/paddle/math/Matrix.cpp b/paddle/math/Matrix.cpp index 88e91806906..1f6458a2880 100644 --- a/paddle/math/Matrix.cpp +++ b/paddle/math/Matrix.cpp @@ -1023,6 +1023,64 @@ void GpuMatrix::check(std::ostream& os, Matrix& refMat, bool printDiff) { LOG(INFO) << "the diffCnt is " << diffCnt; } +void GpuMatrix::upsampleForward(Matrix& input, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + CHECK(input.useGpu_ == true) << "Matrix type are not equal"; + CHECK(mask.useGpu_ == true) << "Matrix type are not equal"; + + real *inputData = input.getData(); + real *maskData = mask.getData(); + real *outData = data_; + + size_t batch = input.getHeight(); + + CHECK(imgSizeH * imgSizeW * channels == input.getWidth()); + CHECK(imgSizeH * imgSizeW * channels == mask.getWidth()); + CHECK_EQ(batch, this->getHeight()); + CHECK(width_ == outputH * outputW * channels); + hl_upsample_forward(inputData, maskData, + batch, + imgSizeH, + imgSizeW, + channels, + outputH, + outputW, + outData); +} + +void GpuMatrix::upsampleBackward(Matrix& outputGrad, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + CHECK(outputGrad.useGpu_ == true) << "Matrix type are not equal"; + CHECK(mask.useGpu_ == true) << "Matrix type are not equal"; + + real *outputGradData = outputGrad.getData(); + real *maskData = mask.getData(); + real *inputGradData = data_; + size_t batch = outputGrad.getHeight(); + + CHECK(imgSizeH * imgSizeW == this->getWidth()/channels); + CHECK_EQ(batch, this->getHeight()); + CHECK_EQ(channels * outputH * outputW, outputGrad.getWidth()); + hl_upsample_backward(outputGradData, maskData, + batch, + imgSizeH, + imgSizeW, + channels, + outputH, + outputW, + inputGradData); +} + void GpuMatrix::maxPoolForward(Matrix& inputMat, size_t imgSizeH, size_t imgSizeW, @@ -1981,6 +2039,74 @@ void CpuMatrix::inverse(MatrixPtr& matInv, bool memAlloc) { CHECK_EQ(info, 0); } +void CpuMatrix::upsampleForward(Matrix& input, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + real *inputData = input.getData(); + real *maskData = mask.getData(); + real *outData = data_; + size_t inLength = imgSizeH * imgSizeW; + size_t outLength = outputH * outputW; + size_t batch = input.getHeight(); + CHECK(inLength == input.getWidth() / channels); + CHECK_EQ(batch, this->getHeight()); + CHECK_EQ(channels * outLength, this->getWidth()); + + for (size_t k = 0; k < batch; k++) { + for (size_t c = 0; c < channels; c++) { + for (size_t i = 0; i < inLength; i++) { + size_t out_index = static_cast(maskData[i]); + if (out_index >= outLength) { + LOG(FATAL) << "upsample index " << out_index + << " out of range."; + } + outData[out_index] = inputData[i]; + } + inputData += inLength; + maskData += inLength; + outData += outLength; + } + } +} + +void CpuMatrix::upsampleBackward(Matrix& outputGrad, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + real *outputGradData = outputGrad.getData(); + real *maskData = mask.getData(); + real *inputGradData = data_; + size_t inLength = imgSizeH * imgSizeW; + size_t outLength = outputH * outputW; + size_t batch = outputGrad.getHeight(); + CHECK(inLength == this->getWidth()/channels); + CHECK_EQ(batch, this->getHeight()); + CHECK_EQ(channels * outLength, outputGrad.getWidth()); + + for (size_t k = 0; k < batch; k++) { + for (size_t c = 0; c < channels; c++) { + for (size_t i = 0; i < inLength; i++) { + size_t out_index = static_cast(maskData[i]); + if (out_index >= outLength) { + LOG(FATAL) << "upsample index " << out_index + << " out of range."; + } + inputGradData[i] = outputGradData[out_index]; + } + inputGradData += inLength; + maskData += inLength; + outputGradData += outLength; + } + } +} + void CpuMatrix::maxPoolForward(Matrix& inputMat, size_t imgSizeH, size_t imgSizeW, diff --git a/paddle/math/Matrix.h b/paddle/math/Matrix.h index e273f112369..b4fcf05cb26 100644 --- a/paddle/math/Matrix.h +++ b/paddle/math/Matrix.h @@ -859,6 +859,26 @@ public: LOG(FATAL) << "Not implemented"; } + virtual void upsampleForward(Matrix& input, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + LOG(FATAL) << "Not implemeted"; + } + + virtual void upsampleBackward(Matrix& outputGrad, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + LOG(FATAL) << "Not implemeted"; + } + /** * Pooling forward operation, pick out the largest element * in the sizeX of value, if the maskMatP is not NULL, it will @@ -1417,6 +1437,22 @@ public: void classificationError(Matrix& output, IVector& label, size_t topkSize = 1); + void upsampleForward(Matrix& input, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW); + + void upsampleBackward(Matrix& outputGrad, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW); + void maxPoolForward(Matrix& inputMat, size_t imgSizeH, size_t imgSizeW, @@ -1689,6 +1725,22 @@ public: MatrixPtr clone(size_t height, size_t width, bool useGpu = false); + void upsampleForward(Matrix& input, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW); + + void upsampleBackward(Matrix& outputGrad, + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW); + void maxPoolForward(Matrix& inputMat, size_t imgSizeH, size_t imgSizeW, diff --git a/proto/ModelConfig.proto b/proto/ModelConfig.proto index 2c2cc624593..2cff25d0958 100644 --- a/proto/ModelConfig.proto +++ b/proto/ModelConfig.proto @@ -321,6 +321,16 @@ message ClipConfig { required double max = 2; } +message UpsampleConfig { + required ImageConfig image_conf = 1; + optional uint32 scale = 2 [ default = 2 ]; + optional uint32 scale_y = 3 [ default = 2 ]; + optional bool pad_out_x = 4 [ default = false ]; + optional bool pad_out_y = 5 [ default = false ]; + optional uint32 upsample_size = 6; + optional uint32 upsample_size_y = 7; +} + message ROIPoolConfig { required uint32 pooled_width = 1; required uint32 pooled_height = 2; @@ -357,6 +367,7 @@ message LayerInputConfig { optional ClipConfig clip_conf = 18; optional ScaleSubRegionConfig scale_sub_region_conf = 19; optional ROIPoolConfig roi_pool_conf = 20; + optional UpsampleConfig upsample_conf = 21; } message LayerConfig { diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 5bd68e211ac..067ca21d323 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -466,6 +466,7 @@ class Input(Cfg): maxout=None, spp=None, pad=None, + upsample=None, format=None, nnz=None, is_static=None, @@ -977,6 +978,11 @@ class Pad(Cfg): def __init__(self, channels, pad_c, pad_h, pad_w): self.add_keys(locals()) +@config_class +class Upsample(Cfg): + def __init__(self, scale, scale_y, pad_out_x, pad_out_y, upsample_size, + upsample_size_y): + self.add_keys(locals()) @config_class class Norm(Cfg): @@ -2387,6 +2393,44 @@ class SpatialPyramidPoolLayer(LayerBase): output_x = (pow(4, spp_conf.pyramid_height) - 1) / (4 - 1) self.set_cnn_layer(name, 1, output_x, spp_conf.image_conf.channels) +@config_layer('upsample') +class UpsampleLayer(LayerBase): + def __init__(self, name, inputs, **xargs): + super(UpsampleLayer, self).__init__( + name, 'upsample', 0, inputs=inputs, **xargs) + + input_layer = self.get_input_layer(0) + image_conf = self.config.inputs[0].upsample_conf.image_conf + image_conf.img_size = input_layer.width + image_conf.img_size_y = input_layer.height + image_conf.channels = input_layer.size / (input_layer.width * + input_layer.height) + + upsample = self.inputs[0].upsample + output_x = 0 + output_y = 0 + output_size = 0 + if upsample.scale: + self.config.inputs[0].upsample_conf.scale = upsample.scale + self.config.inputs[0].upsample_conf.scale_y = upsample.scale_y + output_x = input_layer.width * upsample.scale + output_y = input_layer.height * upsample.scale_y + self.config.inputs[0].upsample_conf.pad_out_x = upsample.pad_out_x + self.config.inputs[0].upsample_conf.pad_out_y = upsample.pad_out_y + if upsample.upsample_size: + self.config.inputs[ + 0].upsample_conf.upsample_size = upsample.upsample_size + self.config.inputs[ + 0].upsample_conf.upsample_size_y = upsample.upsample_size_y + output_x = upsample.upsample_size + output_y = upsample.upsample_size_y + + output_size = image_conf.channels * output_x * output_y + + + self.set_layer_height_width(output_y, output_x) + self.set_layer_depth(input_layer.depth) + self.set_layer_size(output_size) @config_layer('pad') class PadLayer(LayerBase): diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index 5de1c18950a..95369000bbd 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -146,6 +146,7 @@ __all__ = [ 'resize_layer', 'sub_seq_layer', 'scale_sub_region_layer', + 'upsample_layer', ] @@ -163,6 +164,7 @@ class LayerType(object): SEQUENCE_RESHAPE = 'seqreshape' POOLING_MAX = 'max' POOLING_AVG = 'average' + UPSAMPLE_LAYER = 'upsample' FC_LAYER = 'fc' COST = 'cost' COSINE_SIM_VEC = 'cos_vm' @@ -2879,6 +2881,81 @@ def img_pool3d_layer(input, num_filters=num_channels, size=l.config.size) +@wrap_name_default("upsample") +@layer_support() +def upsample_layer(input, + name=None, + scale=None, + scale_y=None, + upsample_size=None, + upsample_size_y=None, + pad_out_x=False, + pad_out_y=False, + layer_attr=None): + """ + The DePooling process. + Inputs should be a list of length 2. The first input is a layer, + and the second input should be the MaxWithMaskPoolingLayer + + The example usage is: + + .. code-block:: python + pool1 = paddle.v2.layer.img_pool(input=input, pool_size=2, stride=2, + pool_type=paddle.pooling.MaxWithMask()) + upsample = paddle.v2.layer.upsample(input=[layer1, pool1]) + + :param name: The name of this layer. It is optional. + :type name: basestring + :param input: contains an input layer and a MaxWithMaskPoolingLayer + :type input: list | tuple | collections.Sequence + :param scale: outputSize = scale * inputSize + :type scale: int | list | tuple | . + :param scale_y: scale_y will be equal to scale, if it's value is None, + :type scale: int | None. + :param upsample_size: specify the outputSize. + :type upsample_size: int | list | tuple. + :param upsample_size_y: specify the y dimension outputSize. + :type upsample_size_y: int. + :param pad_out_x: specify exact x dimension size. This parameter only works when scale is 2 + :type pad_out_x: bool. + :param pad_out_y: specify exact y dimension size. This parameter only works when scale is 2 + :type pad_out_y: bool. + :param layer_attr: Extra Layer Attribute. + :type layer_attr: ExtraLayerAttribute + :return: LayerOutput object. + :rtype: LayerOutput + """ + + assert (scale is not None) or (upsample_size is not None), \ + 'scale or upsample_size, there must be one to be designated' + + assert len(input) == 2, 'layer input size must be 2' + assert input[1].layer_type == LayerType.POOL_LAYER, \ + 'the second input should be the MaxPoolWithMaskLayer' + + scale_y = scale \ + if scale is not None else scale_y + upsample_size_y = upsample_size \ + if upsample_size is not None else upsample_size_y + + layer_type = LayerType.UPSAMPLE_LAYER + + layer = Layer( + name=name, + type=layer_type, + inputs=[ + Input( + input[0].name, + upsample=Upsample(scale, scale_y, pad_out_x, pad_out_y, + upsample_size, upsample_size_y)), + Input(input[1].name) + ], + **ExtraLayerAttribute.to_kwargs(layer_attr)) + + sz = layer.config.size + + return LayerOutput(name, layer_type=layer_type, parents=input, size=sz) + @wrap_name_default("spp") @layer_support() -- GitLab From 6da00da7b5b2449d6668a84728708e43ec030433 Mon Sep 17 00:00:00 2001 From: xzl Date: Mon, 20 Nov 2017 11:58:42 +0800 Subject: [PATCH 0014/2305] code format check --- paddle/cuda/include/hl_cnn.h | 34 +-- paddle/cuda/include/stub/hl_cnn_stub.h | 36 +-- paddle/gserver/layers/UpsampleLayer.cpp | 1 + paddle/gserver/layers/UpsampleLayer.h | 1 - paddle/math/Matrix.cpp | 220 +++++++++--------- paddle/math/Matrix.h | 72 +++--- python/paddle/trainer/config_parser.py | 8 +- .../paddle/trainer_config_helpers/layers.py | 2 + 8 files changed, 192 insertions(+), 182 deletions(-) diff --git a/paddle/cuda/include/hl_cnn.h b/paddle/cuda/include/hl_cnn.h index c8dd3eb91e1..8d0822471b6 100644 --- a/paddle/cuda/include/hl_cnn.h +++ b/paddle/cuda/include/hl_cnn.h @@ -378,14 +378,15 @@ extern void hl_maxout_backward(real* inGrad, * @param[in] outputW the output widht. * @param[out] outputData output data. */ -extern void hl_upsample_forward(real *inputData, real *maskData, - size_t batchSize, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW, - real *outputData); +extern void hl_upsample_forward(real* inputData, + real* maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real* outputData); /** * @brief Upsample backward. @@ -399,13 +400,14 @@ extern void hl_upsample_forward(real *inputData, real *maskData, * @param[in] outputW the output widht. * @param[out] inputGradData the input grad data. */ -extern void hl_upsample_backward(real *outputGradData, real *maskData, - size_t batchSize, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW, - real *inputGradData); +extern void hl_upsample_backward(real* outputGradData, + real* maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real* inputGradData); #endif // HL_CNN_H_ diff --git a/paddle/cuda/include/stub/hl_cnn_stub.h b/paddle/cuda/include/stub/hl_cnn_stub.h index ef1f67980eb..e83db71bb7f 100644 --- a/paddle/cuda/include/stub/hl_cnn_stub.h +++ b/paddle/cuda/include/stub/hl_cnn_stub.h @@ -222,22 +222,24 @@ inline void hl_maxout_backward(real* inGrad, size_t featLen, size_t group) {} -inline void hl_upsample_forward(real *inputData, real *maskData, - size_t batchSize, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW, - real *outputData) {} - -inline void hl_upsample_backward(real *outputGradData, real *maskData, - size_t batchSize, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW, - real *inputGradData) {} +inline void hl_upsample_forward(real* inputData, + real* maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real* outputData) {} + +inline void hl_upsample_backward(real* outputGradData, + real* maskData, + size_t batchSize, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW, + real* inputGradData) {} #endif // HL_CNN_STUB_H_ diff --git a/paddle/gserver/layers/UpsampleLayer.cpp b/paddle/gserver/layers/UpsampleLayer.cpp index 300bb82d688..3ff5332e640 100644 --- a/paddle/gserver/layers/UpsampleLayer.cpp +++ b/paddle/gserver/layers/UpsampleLayer.cpp @@ -30,6 +30,7 @@ size_t UpsampleLayer::getOutputSize() { bool UpsampleLayer::init(const LayerMap& layerMap, const ParameterMap& parameterMap) { Layer::init(layerMap, parameterMap); + CHECK_EQ(inputLayers_.size(), 2U); CHECK_EQ(config_.inputs_size(), 2); const auto& conf = config_.inputs(0).upsample_conf(); diff --git a/paddle/gserver/layers/UpsampleLayer.h b/paddle/gserver/layers/UpsampleLayer.h index 2ae9363439e..25efbac5e9e 100644 --- a/paddle/gserver/layers/UpsampleLayer.h +++ b/paddle/gserver/layers/UpsampleLayer.h @@ -32,7 +32,6 @@ namespace paddle { class UpsampleLayer : public Layer { public: explicit UpsampleLayer(const LayerConfig& config) : Layer(config) {} - ~UpsampleLayer() {} bool init(const LayerMap& layerMap, diff --git a/paddle/math/Matrix.cpp b/paddle/math/Matrix.cpp index 1f6458a2880..ad9a73a2bf8 100644 --- a/paddle/math/Matrix.cpp +++ b/paddle/math/Matrix.cpp @@ -1024,61 +1024,63 @@ void GpuMatrix::check(std::ostream& os, Matrix& refMat, bool printDiff) { } void GpuMatrix::upsampleForward(Matrix& input, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW) { - CHECK(input.useGpu_ == true) << "Matrix type are not equal"; - CHECK(mask.useGpu_ == true) << "Matrix type are not equal"; - - real *inputData = input.getData(); - real *maskData = mask.getData(); - real *outData = data_; - - size_t batch = input.getHeight(); - - CHECK(imgSizeH * imgSizeW * channels == input.getWidth()); - CHECK(imgSizeH * imgSizeW * channels == mask.getWidth()); - CHECK_EQ(batch, this->getHeight()); - CHECK(width_ == outputH * outputW * channels); - hl_upsample_forward(inputData, maskData, - batch, - imgSizeH, - imgSizeW, - channels, - outputH, - outputW, - outData); + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + CHECK(input.useGpu_ == true) << "Matrix type are not equal"; + CHECK(mask.useGpu_ == true) << "Matrix type are not equal"; + + real* inputData = input.getData(); + real* maskData = mask.getData(); + real* outData = data_; + + size_t batch = input.getHeight(); + + CHECK(imgSizeH * imgSizeW * channels == input.getWidth()); + CHECK(imgSizeH * imgSizeW * channels == mask.getWidth()); + CHECK_EQ(batch, this->getHeight()); + CHECK(width_ == outputH * outputW * channels); + hl_upsample_forward(inputData, + maskData, + batch, + imgSizeH, + imgSizeW, + channels, + outputH, + outputW, + outData); } void GpuMatrix::upsampleBackward(Matrix& outputGrad, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW) { - CHECK(outputGrad.useGpu_ == true) << "Matrix type are not equal"; - CHECK(mask.useGpu_ == true) << "Matrix type are not equal"; - - real *outputGradData = outputGrad.getData(); - real *maskData = mask.getData(); - real *inputGradData = data_; - size_t batch = outputGrad.getHeight(); - - CHECK(imgSizeH * imgSizeW == this->getWidth()/channels); - CHECK_EQ(batch, this->getHeight()); - CHECK_EQ(channels * outputH * outputW, outputGrad.getWidth()); - hl_upsample_backward(outputGradData, maskData, - batch, - imgSizeH, - imgSizeW, - channels, - outputH, - outputW, - inputGradData); + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + CHECK(outputGrad.useGpu_ == true) << "Matrix type are not equal"; + CHECK(mask.useGpu_ == true) << "Matrix type are not equal"; + + real* outputGradData = outputGrad.getData(); + real* maskData = mask.getData(); + real* inputGradData = data_; + size_t batch = outputGrad.getHeight(); + + CHECK(imgSizeH * imgSizeW == this->getWidth() / channels); + CHECK_EQ(batch, this->getHeight()); + CHECK_EQ(channels * outputH * outputW, outputGrad.getWidth()); + hl_upsample_backward(outputGradData, + maskData, + batch, + imgSizeH, + imgSizeW, + channels, + outputH, + outputW, + inputGradData); } void GpuMatrix::maxPoolForward(Matrix& inputMat, @@ -2040,71 +2042,69 @@ void CpuMatrix::inverse(MatrixPtr& matInv, bool memAlloc) { } void CpuMatrix::upsampleForward(Matrix& input, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW) { - real *inputData = input.getData(); - real *maskData = mask.getData(); - real *outData = data_; - size_t inLength = imgSizeH * imgSizeW; - size_t outLength = outputH * outputW; - size_t batch = input.getHeight(); - CHECK(inLength == input.getWidth() / channels); - CHECK_EQ(batch, this->getHeight()); - CHECK_EQ(channels * outLength, this->getWidth()); - - for (size_t k = 0; k < batch; k++) { - for (size_t c = 0; c < channels; c++) { - for (size_t i = 0; i < inLength; i++) { - size_t out_index = static_cast(maskData[i]); - if (out_index >= outLength) { - LOG(FATAL) << "upsample index " << out_index - << " out of range."; - } - outData[out_index] = inputData[i]; - } - inputData += inLength; - maskData += inLength; - outData += outLength; + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + real* inputData = input.getData(); + real* maskData = mask.getData(); + real* outData = data_; + size_t inLength = imgSizeH * imgSizeW; + size_t outLength = outputH * outputW; + size_t batch = input.getHeight(); + CHECK(inLength == input.getWidth() / channels); + CHECK_EQ(batch, this->getHeight()); + CHECK_EQ(channels * outLength, this->getWidth()); + + for (size_t k = 0; k < batch; k++) { + for (size_t c = 0; c < channels; c++) { + for (size_t i = 0; i < inLength; i++) { + size_t out_index = static_cast(maskData[i]); + if (out_index >= outLength) { + LOG(FATAL) << "upsample index " << out_index << " out of range."; } + outData[out_index] = inputData[i]; + } + inputData += inLength; + maskData += inLength; + outData += outLength; } + } } void CpuMatrix::upsampleBackward(Matrix& outputGrad, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW) { - real *outputGradData = outputGrad.getData(); - real *maskData = mask.getData(); - real *inputGradData = data_; - size_t inLength = imgSizeH * imgSizeW; - size_t outLength = outputH * outputW; - size_t batch = outputGrad.getHeight(); - CHECK(inLength == this->getWidth()/channels); - CHECK_EQ(batch, this->getHeight()); - CHECK_EQ(channels * outLength, outputGrad.getWidth()); - - for (size_t k = 0; k < batch; k++) { - for (size_t c = 0; c < channels; c++) { - for (size_t i = 0; i < inLength; i++) { - size_t out_index = static_cast(maskData[i]); - if (out_index >= outLength) { - LOG(FATAL) << "upsample index " << out_index - << " out of range."; - } - inputGradData[i] = outputGradData[out_index]; - } - inputGradData += inLength; - maskData += inLength; - outputGradData += outLength; + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { + real* outputGradData = outputGrad.getData(); + real* maskData = mask.getData(); + real* inputGradData = data_; + size_t inLength = imgSizeH * imgSizeW; + size_t outLength = outputH * outputW; + size_t batch = outputGrad.getHeight(); + CHECK(inLength == this->getWidth() / channels); + CHECK_EQ(batch, this->getHeight()); + CHECK_EQ(channels * outLength, outputGrad.getWidth()); + + for (size_t k = 0; k < batch; k++) { + for (size_t c = 0; c < channels; c++) { + for (size_t i = 0; i < inLength; i++) { + size_t out_index = static_cast(maskData[i]); + if (out_index >= outLength) { + LOG(FATAL) << "upsample index " << out_index << " out of range."; } + inputGradData[i] = outputGradData[out_index]; + } + inputGradData += inLength; + maskData += inLength; + outputGradData += outLength; } + } } void CpuMatrix::maxPoolForward(Matrix& inputMat, diff --git a/paddle/math/Matrix.h b/paddle/math/Matrix.h index b4fcf05cb26..6e9ea04d669 100644 --- a/paddle/math/Matrix.h +++ b/paddle/math/Matrix.h @@ -860,22 +860,22 @@ public: } virtual void upsampleForward(Matrix& input, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW) { + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { LOG(FATAL) << "Not implemeted"; } virtual void upsampleBackward(Matrix& outputGrad, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW) { + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW) { LOG(FATAL) << "Not implemeted"; } @@ -1438,20 +1438,20 @@ public: void classificationError(Matrix& output, IVector& label, size_t topkSize = 1); void upsampleForward(Matrix& input, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW); + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW); void upsampleBackward(Matrix& outputGrad, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW); + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW); void maxPoolForward(Matrix& inputMat, size_t imgSizeH, @@ -1726,20 +1726,20 @@ public: MatrixPtr clone(size_t height, size_t width, bool useGpu = false); void upsampleForward(Matrix& input, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW); + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW); void upsampleBackward(Matrix& outputGrad, - Matrix& mask, - size_t imgSizeH, - size_t imgSizeW, - size_t channels, - size_t outputH, - size_t outputW); + Matrix& mask, + size_t imgSizeH, + size_t imgSizeW, + size_t channels, + size_t outputH, + size_t outputW); void maxPoolForward(Matrix& inputMat, size_t imgSizeH, diff --git a/python/paddle/trainer/config_parser.py b/python/paddle/trainer/config_parser.py index 067ca21d323..7563368ad7a 100644 --- a/python/paddle/trainer/config_parser.py +++ b/python/paddle/trainer/config_parser.py @@ -978,12 +978,14 @@ class Pad(Cfg): def __init__(self, channels, pad_c, pad_h, pad_w): self.add_keys(locals()) + @config_class class Upsample(Cfg): def __init__(self, scale, scale_y, pad_out_x, pad_out_y, upsample_size, upsample_size_y): self.add_keys(locals()) + @config_class class Norm(Cfg): def __init__(self, @@ -2393,6 +2395,7 @@ class SpatialPyramidPoolLayer(LayerBase): output_x = (pow(4, spp_conf.pyramid_height) - 1) / (4 - 1) self.set_cnn_layer(name, 1, output_x, spp_conf.image_conf.channels) + @config_layer('upsample') class UpsampleLayer(LayerBase): def __init__(self, name, inputs, **xargs): @@ -2407,9 +2410,10 @@ class UpsampleLayer(LayerBase): input_layer.height) upsample = self.inputs[0].upsample - output_x = 0 + output_x = 0 output_y = 0 output_size = 0 + if upsample.scale: self.config.inputs[0].upsample_conf.scale = upsample.scale self.config.inputs[0].upsample_conf.scale_y = upsample.scale_y @@ -2427,11 +2431,11 @@ class UpsampleLayer(LayerBase): output_size = image_conf.channels * output_x * output_y - self.set_layer_height_width(output_y, output_x) self.set_layer_depth(input_layer.depth) self.set_layer_size(output_size) + @config_layer('pad') class PadLayer(LayerBase): def __init__(self, name, inputs, **xargs): diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index 95369000bbd..1ce603389dc 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -2881,6 +2881,7 @@ def img_pool3d_layer(input, num_filters=num_channels, size=l.config.size) + @wrap_name_default("upsample") @layer_support() def upsample_layer(input, @@ -2930,6 +2931,7 @@ def upsample_layer(input, 'scale or upsample_size, there must be one to be designated' assert len(input) == 2, 'layer input size must be 2' + assert input[1].layer_type == LayerType.POOL_LAYER, \ 'the second input should be the MaxPoolWithMaskLayer' -- GitLab From 0b72a27ee489d665ecd77dc229a6ad0b90e41df2 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Mon, 20 Nov 2017 16:40:35 +0800 Subject: [PATCH 0015/2305] update design of dist train refactor --- .../refactor/distributed_architecture.md | 94 +++++---- doc/design/refactor/session.md | 180 ------------------ .../src/distributed_architecture.graffle | Bin 3414 -> 3793 bytes .../refactor/src/distributed_architecture.png | Bin 47586 -> 190117 bytes .../refactor/src/local_architecture.graffle | Bin 2708 -> 3109 bytes .../refactor/src/local_architecture.png | Bin 29018 -> 104998 bytes 6 files changed, 61 insertions(+), 213 deletions(-) delete mode 100644 doc/design/refactor/session.md diff --git a/doc/design/refactor/distributed_architecture.md b/doc/design/refactor/distributed_architecture.md index ac7e98ccf1a..68db509a61e 100644 --- a/doc/design/refactor/distributed_architecture.md +++ b/doc/design/refactor/distributed_architecture.md @@ -64,29 +64,31 @@ the cases - the compiler will optimize the IR: -We can have our own IR too: PaddlePaddle can support model parallel by +We can have our own IR which is called [Program](../program.md). +PaddlePaddle can support model parallel by converting the IR so the user no longer need to manually do it in Python: -The IR for PaddlePaddle after refactor is called `Block`, it specifies -the computation dependency graph and the variables used in the -computation. ### Limitation 3 The user can not directly specify the parameter update rule for the -parameter server because the parameter server does not use the same -computation definition as the trainer. Instead, the update rule is -baked in the parameter server. The user can not specify the update -rule in the same way of specifying the trainer computation. +parameter server because the previous implementaion hard coded that +parameter server only do vector's optimization algorithm by +configuration. The user can not specify the parameter server's +computation layer by layer. -This could be fixed by making the parameter server run the same +This could be fixed by making the parameter server run a separated +IR according to the trainer's varialble (tensors, selectedrows) +defination. + +the same computation definition as the trainer. For a detailed explanation, please see -[Design Doc: Operation Graph Based Parameter Server](./dist_train.md) +[Design Doc: Operation Graph Based Parameter Server](./parameter_server.md) ## Distributed Training Architecture @@ -110,30 +112,63 @@ img, label = input[0], input[1] hidden = paddle.layer.fc(input=img, size=200, act=paddle.activation.Tanh()) prediction = paddle.layer.fc(input=img, size=10, act=paddle.activation.Softmax()) cost = paddle.layer.classification_cost(input=prediction, label=label) -optimizer = paddle.optimizer.SGD(cost, learning_rate=0.01) -session = paddle.session.NewRemote(num_trainer=3, num_ps=2, GPU_per_trainer=1) +optimizer = paddle.optimizer.SGD(learning_rate=0.01) +opts = optimizer.minimize(cost) +exe = RemoteExecutor(num_trainer=3, num_ps=2, GPU_per_trainer=2, sync_batches=1) +# this will init variable data on both server and trainer +exe.run(framework.default_startup_program()) +exe.sync() + for i in range(1000): - _, cost_val = session.eval(targets=[cost, optimizer]) - print cost_val + # feed data + ... + cost, acc = exe.run(framework.default_main_program(), + fetch_list=[avg_cost, acc_out]) + print cost, acc ``` The code above is a typical Python trainer code, the neural network topology is built using helper functions such as -`paddle.layer.fc`. The training is done by calling `session.eval` +`paddle.layer.fc`. The training is done by calling `Executor.run` iteratively. -#### session.eval +#### RemoteExecutor + +As shown in the graph, `RemoteExecutor.run` sends the IR to the +PaddlePaddle cluster for Execution. You can also use parameter +`fetch_list` to interactively fetch varirable back to local for +log printing. + +The Python `RemoteExecutor` is derived from `Executor` class. +For more information about `RemoteExecutor`, please +see [Design Doc: RemoteExecutor](./remote_executor.md). + +By default, `Executor.run` starts a PaddlePaddle Cloud +[TrainingJob](https://github.com/PaddlePaddle/cloud/blob/develop/doc/autoscale/README.md#training-job-resource), or you can run each component in the +executor by your own method: -As shown in the graph, `session.eval` sends the IR and the evaluation -inputs/targets to the PaddlePaddle cluster for evaluation. The -targets can be any variable in the computation graph. When the target -is the `optimizer` variable, the neural network will be optimized -once. When the target is the `cost` variable, `session.eval` returns -the cost value. +- Data Parrallelism + ```python + if os.getenv('PLACE_PSERVER'): + exe.run_pserver() + elif os.getenv('PLACE_TRAINER'): + exe.run_trainer() + ``` +- Model Parrallelism + ```python + for part in exe.get_parralle_parts(): + exe.run_part(part) + ``` -The Python `session` is a wrapper of the C++ `Session` class. For more -information about `Session`, please -see [Design Doc: Session](./session.md). +#### Program and Executor + +As mentioned above, the implementation of IR is [Program](../program.md). + +[Executor](../executor.md) converts and parses the IR to a prefered +graph for final execution. For local training you generally use +`Executor` to run the graph locally. For any kind of distributed +training, you can use `RemoteExecutor` to specify desired distributed +training method with some optional arguments. ### PaddlePaddle Converter @@ -183,13 +218,6 @@ device computation time and device communication time. Model parallelism requires the general placement algorithm. -### PaddlePaddle Runtime - -The PaddlePaddle runtime owns multiple devices (e.g., CPUs, GPUs) and -runs the IR. The runtime does not need to do OP placement since it's -already done by the converter. - - ### Local Training Architecture The local training architecture will be the same as the distributed @@ -210,7 +238,7 @@ the Python reader will need to read from the distributed filesystem network traffic. When doing distributed training, the user can still use Python data -reader: the training data are sent with `session.eval`. However should +reader: the training data are sent with `Executor.run`. However should be used for debugging purpose only. The users are encouraged to use the read data OPs. diff --git a/doc/design/refactor/session.md b/doc/design/refactor/session.md deleted file mode 100644 index 1d9a26683c1..00000000000 --- a/doc/design/refactor/session.md +++ /dev/null @@ -1,180 +0,0 @@ -# Design Doc: Session - -## Abstract - -The *session* object encapsulates the environment in which the -computation graph is executed. - -We will have the *local* session and *remote* session, they offer the -same [interface](#interface). The local session encapsulates the local -runtime environment and the remote session encapsulates the cluster -runtime environment. - -The local runtime environment contains: - -1. computation devices (i.e., CPU, GPU) handles, and -1. the [scope](../scope.md) which holds all variables. - -The remote runtime environment contains: - -1. computation devices (i.e., CPU and GPU on node 0, 1) in a cluster, - and -1. the distributed [scope](../scope.md) in a cluster which holds all - variables. - -The user can create a remote session on Paddle Cloud and evaluate the -computation graph with it. In this way, the user can control the -remote computation resource in a cluster from his local computer. - - -## Background - -The current design has an implicit global session in which -`paddle.eval()` is executed. The pain point is: - -Since the user is not able to explicitly switch between runtime -environments, the user cannot run a topology in two independent -environments. - -For example, in reinforcement learning, the user may want to have a -stale model for inference and a fresh model for training, and only -replace the stale model with the fresh model periodically. - -Furthermore, we have no concept that encapsulates a remote environment -that executes a computation graph. - -We need the session object to address above issues. - - -## Session - -A session is an object that owns the runtime environment. All -computations are executed through `session.eval()`. - - -### Interface - -```python -eval( - targets, - feed_dict=None, -) -``` - -Evaluates the target Operations or Variables in `targets`. - -- *targets*: the evaluation targets. Can be a single Operation or - Variable, or a list with the Operations or Variables as - elements. The value returned by `eval()` has the same shape as the - `target` argument. - - The PaddlePaddle program is represented by - the [ProgramDesc](../design/program.md), `eval()` will infer the - ProgramDesc from the given targets and run the PaddlePaddle - program. Please - see - [this graph](./distributed_architecture.md#local-training-architecture) for - the detailed illustration for the local session - and - [this graph](./distributed_architecture.md#distributed-training-architecture) for - the detailed illustration for the remote session. - -- *feed_dict*: a dictionary that contains the tensors which override - the edges of the computation graph. - - feed_dict not only can provide the input data, it can override any - OP's input as well: - - ```python - a = pd.constant(2.0, name="a") - b = pd.variable(name="b") - c = pd.mul(a,b) - sess.eval(targets=c, feed_dict={"b":3.0}) # returns 6.0 - ``` - -```python -close() -``` - -Closes the session and releases the scope that the session owns. - - -### Create a Local Session - -```python -session( - devices=None -) -``` - -Creates a new session. One session owns one global scope, so creating -multiple sessions will create different scopes. - -- *devices*: a single `string` or a list of `string` of device names, - the corresponding devices will be the computation devices for - `eval()`. If not specified, all available devices (e.g., all GPUs) - will be used. The user doesn't need to specify the CPU device since - it will be always used. Multiple sessions can use the same device. - - -#### Example - -```Python -a = paddle.constant(1.0) -b = paddle.constant(2.0) -c = a + b -sess = paddle.session(devices=["gpu:0", "gpu:1", "fpga:0"]) -sess.eval(c) -sess.close() -``` - -### Create a Remote Session - -```python -create_cloud_job( - name, - num_trainer, - mem_per_trainer, - gpu_per_trainer, - cpu_per_trainer, - num_ps, - mem_per_ps, - cpu_per_ps, -) -``` - -Creates a Paddle Cloud job. Fails if the job name exists. - -```python -get_cloud_job( - name -) -``` - -Gets a Paddle Cloud job. - -```python -remote_session( - job -) -``` - -- *job*: the Paddle Cloud job. - -#### Example - -```Python -reader = paddle.reader.recordio("/pfs/home/peter/mnist-train-*") # data stored on Paddle Cloud -image = reader.column(0) -label = reader.column(1) -fc1 = paddle.op.fc(image, size=256, act="sigmoid") -fc2 = paddle.op.fc(fc1, size=10, act="softmax") -cost = paddle.op.cross_entropy(fc2, label) -opt = paddle.optimizer.sgd(cost) - -job = paddle.create_cloud_job("test", 3, "1G", 1, 1, 2, "1G", 1) -sess = paddle.remote_ession(job) -for i in range(1000): - sess.eval(opt) -sess.close() -``` diff --git a/doc/design/refactor/src/distributed_architecture.graffle b/doc/design/refactor/src/distributed_architecture.graffle index f8496e57326c38de7468eb452a7713291d57653c..1ebbe70db04ebe0321f252a1de68aaefdaa32bd6 100644 GIT binary patch literal 3793 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 H51ar1g?@na literal 3414 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 diff --git a/doc/design/refactor/src/distributed_architecture.png b/doc/design/refactor/src/distributed_architecture.png index 410c4510c6aab301dec95e6427fe80ac24e105fe..0da49f44123be7c0b84bf420ad81c33f0ac0a105 100644 GIT binary patch literal 190117 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ð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 literal 47586 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 OmHz=6LV)yJQUCxDuSJjm diff --git a/doc/design/refactor/src/local_architecture.png b/doc/design/refactor/src/local_architecture.png index 4b999538b7825c805292ee28b5e3256d5543bd09..14adc9fd72b855bb9f74fbf2c84ac9ec0cf2b122 100644 GIT binary patch literal 104998 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¨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 literal 29018 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 Date: Mon, 20 Nov 2017 17:05:51 +0800 Subject: [PATCH 0016/2305] update grammar --- .../refactor/distributed_architecture.md | 65 +++++++++++++------ 1 file changed, 45 insertions(+), 20 deletions(-) diff --git a/doc/design/refactor/distributed_architecture.md b/doc/design/refactor/distributed_architecture.md index 68db509a61e..601772bff56 100644 --- a/doc/design/refactor/distributed_architecture.md +++ b/doc/design/refactor/distributed_architecture.md @@ -16,9 +16,9 @@ limitations: write the inter-model-shard communication code. 3. The user can not directly specify the parameter update rule: need - to modify the parameter server C++ code and compile a new - binary. This adds complication for researchers: A lot of extra - effort is required. Besides, the training job submission program + to modify the parameter server C++ code and compile a new binary. + This adds complication for researchers: A lot of extra effort is + required. Besides, the training job submission program may not allow running arbitrary binaries. This design doc discusses PaddlePaddle's new distributed training @@ -44,7 +44,7 @@ replicated Python instances are running on different nodes: both the training logic and the neural network computation is replicated. The tasks that should only run once all belong to the training logic, -if we only replicate the neural network computation, but do **not** +if we only replicate the neural network computation but do **not** replicate the training logic, the limitation could be solved. ### Limitation 2 @@ -53,13 +53,13 @@ Model parallelism means running a single model on multiple nodes by partitioning the model onto different nodes and managing the inter-model-shard communications. -PaddlePaddle should be able to modify the nerual network computation +PaddlePaddle should be able to modify the neural network computation definition to support model parallelism automatically. However, the -computation is only specified in Python code, and PaddlePaddle can not +computation is only specified in Python code, and PaddlePaddle cannot modify Python code. -Just like compiler uses a intermediate representation (IR) so that -programmer does not need to manually optimize their code in most of +Just like compiler uses an intermediate representation (IR) so that +the programmer does not need to manually optimize their code in most of the cases - the compiler will optimize the IR: @@ -75,20 +75,20 @@ Python: ### Limitation 3 The user can not directly specify the parameter update rule for the -parameter server because the previous implementaion hard coded that +parameter server because the previous implementation hard coded that parameter server only do vector's optimization algorithm by configuration. The user can not specify the parameter server's computation layer by layer. This could be fixed by making the parameter server run a separated -IR according to the trainer's varialble (tensors, selectedrows) -defination. +IR according to the trainer's variable (tensors, selectedrows) +definition. the same -computation definition as the trainer. For a detailed explanation, +computation definition of the trainer. For a detailed explanation, please see -[Design Doc: Operation Graph Based Parameter Server](./parameter_server.md) +[Design Doc: Operation Graph-Based Parameter Server](./parameter_server.md) ## Distributed Training Architecture @@ -136,18 +136,43 @@ iteratively. As shown in the graph, `RemoteExecutor.run` sends the IR to the PaddlePaddle cluster for Execution. You can also use parameter -`fetch_list` to interactively fetch varirable back to local for +`fetch_list` to interactively fetch variable back to local for log printing. The Python `RemoteExecutor` is derived from `Executor` class. For more information about `RemoteExecutor`, please see [Design Doc: RemoteExecutor](./remote_executor.md). +The `RemoteExecutor.run` interface defination is: + +```python +run(self, + program=None, + feed=None, + fetch_list=None, + feed_var_name='feed', + fetch_var_name='fetch', + job_desc=JobDesc( + jobname, + num_trainer, + num_pserver, + cpu_per_trainer, + gpu_per_trainer, + mem_per_trainer, + cpu_per_pserver, + mem_per_pserver + )) +``` + +`JobDesc` object describe the distributed job resource specification to run on +Cluster environment. + By default, `Executor.run` starts a PaddlePaddle Cloud -[TrainingJob](https://github.com/PaddlePaddle/cloud/blob/develop/doc/autoscale/README.md#training-job-resource), or you can run each component in the +[TrainingJob](https://github.com/PaddlePaddle/cloud/blob/develop/doc/autoscale/README.md#training-job-resource), +or you can run each component in the executor by your own method: -- Data Parrallelism +- Data Parallelism ```python if os.getenv('PLACE_PSERVER'): exe.run_pserver() @@ -164,10 +189,10 @@ executor by your own method: As mentioned above, the implementation of IR is [Program](../program.md). -[Executor](../executor.md) converts and parses the IR to a prefered +[Executor](../executor.md) converts and parses the IR to a preferred graph for final execution. For local training you generally use `Executor` to run the graph locally. For any kind of distributed -training, you can use `RemoteExecutor` to specify desired distributed +training, you can use `RemoteExecutor` to specify desired distributed training method with some optional arguments. ### PaddlePaddle Converter @@ -182,7 +207,7 @@ to different PaddlePaddle runtimes. Below are the steps: 1. Extract a new computation (sub)graph with `feed` and `fetch` OP as the boundary. The runtime does not need to run the OP that is not - dependent by the `fetch` OP. + dependent on the `fetch` OP. 1. Optimizes the computation graph. @@ -238,7 +263,7 @@ the Python reader will need to read from the distributed filesystem network traffic. When doing distributed training, the user can still use Python data -reader: the training data are sent with `Executor.run`. However should +reader: the training data are sent with `Executor.run`. However, should be used for debugging purpose only. The users are encouraged to use the read data OPs. -- GitLab From dbe0583cb0e79bfb156a9816b1ae2e5dfaf2c383 Mon Sep 17 00:00:00 2001 From: gongweibao Date: Tue, 21 Nov 2017 09:05:33 +0000 Subject: [PATCH 0017/2305] mv test position to fluid --- .../paddle/v2/{framework => fluid}/tests/test_block_expand_op.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename python/paddle/v2/{framework => fluid}/tests/test_block_expand_op.py (100%) diff --git a/python/paddle/v2/framework/tests/test_block_expand_op.py b/python/paddle/v2/fluid/tests/test_block_expand_op.py similarity index 100% rename from python/paddle/v2/framework/tests/test_block_expand_op.py rename to python/paddle/v2/fluid/tests/test_block_expand_op.py -- GitLab From 25a3d2d76f0146ac580cb484bb5a638ddc029bfa Mon Sep 17 00:00:00 2001 From: gongweibao Date: Wed, 22 Nov 2017 06:18:47 +0000 Subject: [PATCH 0018/2305] fix by comments --- paddle/operators/block_expand_op.cc | 3 +- paddle/operators/block_expand_op.h | 22 ++- .../v2/fluid/tests/test_block_expand_op.py | 175 +++++++++++------- 3 files changed, 123 insertions(+), 77 deletions(-) diff --git a/paddle/operators/block_expand_op.cc b/paddle/operators/block_expand_op.cc index d72c6b2de1d..f25cc4f9de9 100644 --- a/paddle/operators/block_expand_op.cc +++ b/paddle/operators/block_expand_op.cc @@ -30,7 +30,8 @@ class BlockExpandOp : public framework::OperatorWithKernel { "Output of BlockExpandOp op should not be null."); auto in_dim = ctx->GetInputDim("X"); - PADDLE_ENFORCE_EQ(in_dim.size(), 4, "Input format must be NCHW."); + PADDLE_ENFORCE_EQ(in_dim.size(), 4, + "Input(X) format must be 4D tensor, eg., NCHW."); PADDLE_ENFORCE_GE(in_dim[0], 1, "Input batchsize must >= 1."); int block_height = ctx->Attrs().Get("blockHeight"); diff --git a/paddle/operators/block_expand_op.h b/paddle/operators/block_expand_op.h index 38d0626c730..aa0db2705c4 100644 --- a/paddle/operators/block_expand_op.h +++ b/paddle/operators/block_expand_op.h @@ -68,13 +68,16 @@ class BlockExpandKernel : public framework::OpKernel { img_height, img_width, block_height, block_width, stride_height, stride_width, padding_height, padding_width, outputHeight, outputWidth); + std::vector stride({stride_height, stride_width}); + std::vector padding({padding_height, padding_width}); + for (int i = 0; i < N; i++) { - Tensor src = in->Slice(i, i + 1).Resize({C, img_height, img_width}); - Tensor dst = out->Slice(i, i + 1).Resize( + Tensor src = in->Slice(i, i + 1).Resize({C, img_height, img_width}); + Tensor dst = out->Slice(i, i + 1).Resize( {outputHeight, outputWidth, C, block_height, block_width}); + math::Im2ColFunctor f; - f(ctx.device_context(), src, dst, stride_height, stride_width, - padding_height, padding_width); + f(ctx.device_context(), src, stride, padding, &dst); } } }; @@ -112,13 +115,16 @@ class BlockExpandGradKernel : public framework::OpKernel { img_height, img_width, block_height, block_width, stride_height, stride_width, padding_height, padding_width, outputHeight, outputWidth); + std::vector stride({stride_height, stride_width}); + std::vector padding({padding_height, padding_width}); + // std::vector stride({stride_height, stride_width}); + for (int i = 0; i < N; i++) { - Tensor dst = d_x->Slice(i, i + 1).Resize({C, img_height, img_width}); - Tensor src = d_out->Slice(i, i + 1).Resize( + Tensor dst = d_x->Slice(i, i + 1).Resize({C, img_height, img_width}); + Tensor src = d_out->Slice(i, i + 1).Resize( {outputHeight, outputWidth, C, block_height, block_width}); math::Col2ImFunctor f; - f(ctx.device_context(), dst, src, stride_height, stride_width, - padding_height, padding_width); + f(ctx.device_context(), dst, stride, padding, &src); } } }; diff --git a/python/paddle/v2/fluid/tests/test_block_expand_op.py b/python/paddle/v2/fluid/tests/test_block_expand_op.py index 4c66493d6e9..b31ed53f4c7 100644 --- a/python/paddle/v2/fluid/tests/test_block_expand_op.py +++ b/python/paddle/v2/fluid/tests/test_block_expand_op.py @@ -4,27 +4,27 @@ from op_test import OpTest def get_output_shape(attrs, x): - imgHeight = x.shape[1] - imgWidth = x.shape[2] + img_height = x.shape[1] + img_width = x.shape[2] - paddingHeight = attrs['paddingHeight'] - paddingWidth = attrs['paddingWidth'] - blockHeight = attrs['blockHeight'] - blockWidth = attrs['blockWidth'] - strideHeight = attrs['strideHeight'] - strideWidth = attrs['strideWidth'] + padding_height = attrs['paddingHeight'] + padding_width = attrs['paddingWidth'] + block_height = attrs['blockHeight'] + block_width = attrs['blockWidth'] + stride_height = attrs['strideHeight'] + stride_width = attrs['strideWidth'] - outputHeight = \ + output_height = \ 1 + \ - (imgHeight + 2 * paddingHeight - blockHeight + strideHeight - 1) / \ + (img_height + 2 * padding_height - block_height + stride_height - 1) / \ strideHeight - outputWidth = \ + output_width = \ 1 + \ - (imgWidth + 2 * paddingWidth - blockWidth + strideWidth - 1) / \ - strideWidth + (img_width + 2 * padding_width - block_width + stride_width - 1) / \ + stride_width - return outputHeight, outputWidth + return output_height, output_width def im2col(attrs, im, col): @@ -34,38 +34,39 @@ def im2col(attrs, im, col): {outputHeight, outputWidth, inputChannels, filterHeight, filterWidth} """ input_channels = im.shape[0] - inputHeight = im.shape[1] - inputWidth = im.shape[2] + input_height = im.shape[1] + input_width = im.shape[2] - outputHeight = col.shape[0] - outputWidth = col.shape[1] - filterHeight = col.shape[3] - filterWidth = col.shape[4] + output_height = col.shape[0] + output_width = col.shape[1] + filter_height = col.shape[3] + filter_width = col.shape[4] - strideHeight = attrs['strideHeight'] - strideWidth = attrs['strideWidth'] - paddingHeight = attrs['paddingHeight'] - paddingWidth = attrs['paddingWidth'] + stride_height = attrs['strideHeight'] + stride_width = attrs['strideWidth'] + padding_height = attrs['paddingHeight'] + padding_width = attrs['paddingWidth'] - for col_row_idx in range(0, outputHeight): - for col_col_idx in range(0, outputWidth): + for col_row_idx in range(0, output_height): + for col_col_idx in range(0, output_width): for channel in range(0, input_channels): - for filter_row_idx in range(0, filterHeight): - for filter_col_idx in range(0, filterWidth): - im_row_offset = col_row_idx * strideHeight \ - + filter_row_idx - paddingHeight + for filter_row_idx in range(0, filter_height): + for filter_col_idx in range(0, filter_width): + im_row_offset = col_row_idx * stride_height \ + + filter_row_idx - padding_height - im_col_offset = col_col_idx * strideWidth \ - + filter_col_idx - paddingWidth + im_col_offset = col_col_idx * stride_width \ + + filter_col_idx - padding_width - if (im_row_offset < 0 or im_row_offset >= inputHeight or + if (im_row_offset < 0 or + im_row_offset >= input_height or im_col_offset < 0 or - im_col_offset >= inputWidth): + im_col_offset >= input_width): col[col_row_idx][col_col_idx][channel][\ filter_row_idx][filter_col_idx] = 0.0 else: - im_offset = (channel * inputHeight + im_row_offset \ - ) * inputWidth + im_col_offset + im_offset = (channel * input_height + im_row_offset \ + ) * input_width + im_col_offset col[col_row_idx][col_col_idx][channel][\ filter_row_idx][filter_col_idx] = im[channel][ \ @@ -76,55 +77,55 @@ def col2img(attrs, col, img): """ img: {CHW} col: - {outputHeight, outputWidth, inputChannels, filterHeight, filterWidth} + {output_height, outputWidth, inputChannels, filterHeight, filterWidth} """ input_channels = im.shape[0] - inputHeight = im.shape[1] - inputWidth = im.shape[2] + input_height = im.shape[1] + input_width = im.shape[2] - outputHeight = col.shape[0] - outputWidth = col.shape[1] - filterHeight = col.shape[3] - filterWidth = col.shape[4] + output_height = col.shape[0] + output_width = col.shape[1] + filter_height = col.shape[3] + filter_width = col.shape[4] - strideHeight = attrs['strideHeight'] - strideWidth = attrs['strideWidth'] - paddingHeight = attrs['paddingHeight'] - paddingWidth = attrs['paddingWidth'] + stride_height = attrs['strideHeight'] + stride_width = attrs['strideWidth'] + padding_height = attrs['paddingHeight'] + padding_width = attrs['paddingWidth'] - for col_row_idx in range(0, outputHeight): - for col_col_idx in range(0, outputWidth): + for col_row_idx in range(0, output_height): + for col_col_idx in range(0, output_width): for channel in range(0, input_channels): - for filter_row_idx in range(0, filterHeight): - for filter_col_idx in range(0, filterWidth): + for filter_row_idx in range(0, filter_height): + for filter_col_idx in range(0, filter_width): im_row_offset = \ - col_row_idx * strideHeight + filter_row_idx - paddingHeight + col_row_idx * stride_height + filter_row_idx - padding_height im_col_offset = \ - col_col_idx * strideWidth + filter_col_idx - paddingWidth + col_col_idx * stride_width + filter_col_idx - padding_width if (im_row_offset >= 0 and - im_row_offset < inputHeight and + im_row_offset < input_height and im_col_offset >= 0 and - im_col_offset < inputWidth): + im_col_offset < input_width): im[channel][im_row_offset][im_col_offset] = \ col[col_row_idx][col_col_idx][channel][filter_row_idx][filter_col_idx] -class TestBlockExpandOp(OpTest): - def get_input_data(self, C, H, W): - x = np.random.uniform(0.1, 1, [C, H, W]).astype("float32") - for c in range(0, C): - for h in range(0, H): - for w in range(0, W): - #x[c][h][w] = c * H * W + h *W + w - x[c][h][w] = 0.2 + 0.01 * (c * H * W + h * W + w) +def get_input_data(C, H, W): + x = np.random.uniform(0.1, 1, [C, H, W]).astype("float32") + for c in range(0, C): + for h in range(0, H): + for w in range(0, W): + #x[c][h][w] = c * H * W + h *W + w + x[c][h][w] = 0.2 + 0.01 * (c * H * W + h * W + w) return x + +class TestBlockExpandOp(OpTest): def setUp(self): C = 3 H = 4 W = 4 - x = self.get_input_data(C, H, W) - #print x + x = get_input_data(C, H, W) attrs = { 'blockHeight': 2, @@ -135,9 +136,47 @@ class TestBlockExpandOp(OpTest): 'paddingWidth': 1, } - outputHeight, outputWidth = get_output_shape(attrs, x) + output_height, output_width = get_output_shape(attrs, x) + out = np.random.uniform(0.1, 1,\ + [output_height, output_width, x.shape[0], \ + attrs['blockHeight'], attrs['blockWidth']]).astype("float32") + + self.op_type = "block_expand" + self.inputs = {'X': x.reshape(1, C, H, W)} + self.attrs = attrs + + im2col(attrs, x, out) + self.outputs = { + 'Out':out.reshape(1, output_height, output_width, x.shape[0], \ + attrs['blockHeight'], attrs['blockWidth']) + } + + def test_check_output(self): + self.check_output() + + def test_check_grad_normal(self): + self.check_grad(['X'], 'Out') + + +class TestBlockExpandOp2(OpTest): + def setUp(self): + C = 3 + H = 4 + W = 5 + x = get_input_data(C, H, W) + + attrs = { + 'blockHeight': 2, + 'blockWidth': 1, + 'strideHeight': 2, + 'strideWidth': 1, + 'paddingHeight': 2, + 'paddingWidth': 1, + } + + output_height, output_width = get_output_shape(attrs, x) out = np.random.uniform(0.1, 1,\ - [outputHeight, outputWidth, x.shape[0], \ + [output_height, output_width, x.shape[0], \ attrs['blockHeight'], attrs['blockWidth']]).astype("float32") self.op_type = "block_expand" @@ -146,7 +185,7 @@ class TestBlockExpandOp(OpTest): im2col(attrs, x, out) self.outputs = { - 'Out':out.reshape(1, outputHeight, outputWidth, x.shape[0], \ + 'Out':out.reshape(1, output_height, output_width, x.shape[0], \ attrs['blockHeight'], attrs['blockWidth']) } -- GitLab From 16ed4a92a5e441352582ff55ce784a78a2734b1a Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Mon, 27 Nov 2017 18:27:34 +0800 Subject: [PATCH 0019/2305] Add math function for sampling integers from: 1. uniform distribution 2. log uniform distribution --- paddle/operators/math/sampler.cc | 47 +++++++++++++++++ paddle/operators/math/sampler.h | 88 ++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 paddle/operators/math/sampler.cc create mode 100644 paddle/operators/math/sampler.h diff --git a/paddle/operators/math/sampler.cc b/paddle/operators/math/sampler.cc new file mode 100644 index 00000000000..52628c3b03b --- /dev/null +++ b/paddle/operators/math/sampler.cc @@ -0,0 +1,47 @@ +#include "sampler.h" + +namespace paddle { +namespace random { + +Sampler::~Sampler() {} + +UniformSampler::UniformSampler(int64 range) + : Sampler(range), inv_range_(1.0 / range) { + std::random_device r; + random_engine_ = std::make_shared(r()); + dist_ = std::make_shared>(0, range); +} + +int64 UniformSampler::Sample() const { return (*dist_)(*random_engine_); } + +float UniformSampler::Probability(int64 value) const { return inv_range_; } + +LogUniformSampler::LogUniformSampler(int64 range) + : Sampler(range), log_range_(log(range + 1)) { + std::random_device r; + random_engine_ = std::make_shared(r()); + dist_ = std::make_shared>(0, 1); +} + +int64 LogUniformSampler::Sample() const { + // Got Log Uniform distribution from uniform distribution by + // inverse_transform_sampling method + // More details: + // https://wanghaoshuang.github.io/2017/11/Log-uniform-distribution-sampler/ + const int64 value = + static_cast(exp((*dist_)(*random_engine_) * log_range_)) - 1; + // Mathematically, value should be <= range_, but might not be due to some + // floating point roundoff, so we mod by range_. + return value % range_; +} + +float LogUniformSampler::Probability(int64 value) const { + // Given f(x) = 1/[(x+1) * log_range_] + // The value's probability is integral of f(x) from value to (value + 1) + // More details: + // https://wanghaoshuang.github.io/2017/11/Log-uniform-distribution-sampler + return (log((value + 2.0) / (value + 1.0))) / log_range_; +} + +} // namespace random +} // namespace paddle diff --git a/paddle/operators/math/sampler.h b/paddle/operators/math/sampler.h new file mode 100644 index 00000000000..bcd7bead352 --- /dev/null +++ b/paddle/operators/math/sampler.h @@ -0,0 +1,88 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once +#include +#include +typedef long int64; +namespace paddle { +namespace operators { +namespace math { + +// TODO: Support for GPU + +/** +* Sample integers from [0, range). +*/ +class Sampler { + public: + explicit Sampler(int64 range) : range_(range) { /* check range > 0*/ + } + virtual ~Sampler(); + // Sample a single value + virtual int64 Sample() const = 0; + // The probability that a single call to Sample() returns the given value. + virtual float Probability(int64 value) const = 0; + + int64 range() { return range_; }; + + protected: + const int64 range_; +}; + +/** + * Sample integers from [0, range). + * And the distribution function is: + * P(x) = 1 / range + */ +class UniformSampler : public Sampler { + public: + explicit UniformSampler(int64 range); + + ~UniformSampler() override {} + + int64 Sample() const override; + + float Probability(int64 value) const override; + + private: + const float inv_range_; + std::shared_ptr random_engine_; + std::shared_ptr> dist_; +}; + +/** + * Sample integers from [0, range). + * And the distribution function is: + * P(x) = (1/ln(range+1)) * ln(1 + 1/(x + 1)) + */ +class LogUniformSampler : public Sampler { + public: + explicit LogUniformSampler(int64 range); + + ~LogUniformSampler() override {} + + int64 Sample() const override; + + float Probability(int64 value) const override; + + private: + const float log_range_; + std::shared_ptr random_engine_; + std::shared_ptr> dist_; +}; + +} // math +} // namespace operators +} // namespace paddle -- GitLab From 6bc6ccd187b3a0dcb6980a9d0c5090f3e5d16150 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 29 Nov 2017 05:49:44 +0000 Subject: [PATCH 0020/2305] add gpu kernel for ctc_edit_distance_op --- paddle/operators/ctc_edit_distance_op.cu | 130 ++++++++++++++++++ .../tests/test_ctc_edit_distance_op.py} | 8 +- 2 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 paddle/operators/ctc_edit_distance_op.cu rename python/paddle/v2/{framework/tests/test_ctc_edit_distance.py => fluid/tests/test_ctc_edit_distance_op.py} (84%) diff --git a/paddle/operators/ctc_edit_distance_op.cu b/paddle/operators/ctc_edit_distance_op.cu new file mode 100644 index 00000000000..872268296ef --- /dev/null +++ b/paddle/operators/ctc_edit_distance_op.cu @@ -0,0 +1,130 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include +#include "paddle/framework/op_registry.h" +#include "paddle/platform/cuda_helper.h" +#include "paddle/platform/gpu_info.h" + +namespace paddle { +namespace operators { + +using platform::PADDLE_CUDA_NUM_THREADS; + +template +__global__ void FillFirstRow(T* dist, const int N) { + int idx = blockDim.x * blockIdx.x + threadIdx.x; + if (idx < N + 1) { + dist[idx] = idx; + } +} + +template +__global__ void FillFirstColumn(T* dist, const int M, const int N) { + int idx = blockDim.x * blockIdx.x + threadIdx.x; + if (idx < M + 1) { + dist[idx * (N + 1)] = idx; + } +} + +template +__global__ void Levenshtein(T* dist, const T* x1, const T* x2, const int M, + const int N, const int start) { + int idx = blockDim.x * blockIdx.x + threadIdx.x; + int offset = N; + int index = start + idx * offset; + int row = index / (N + 1); + int col = index % (N + 1); + if (row > 0 && col > 0 && row < M + 1 && col < N + 1) { + int cost = x1[row - 1] == x2[col - 1] ? 0 : 1; + int dels = dist[(row - 1) * (N + 1) + col] + 1; + int ins = dist[row * (N + 1) + col - 1] + 1; + int subs = dist[(row - 1) * (N + 1) + (col - 1)] + cost; + dist[index] = min(dels, min(ins, subs)); + } +} + +template +class CTCEditDistanceGPUKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const { + auto* out_t = ctx.Output("Out"); + + auto* x1_t = ctx.Input("X1"); + auto* x2_t = ctx.Input("X2"); + + out_t->mutable_data(ctx.GetPlace()); + + auto normalized = ctx.Attr("normalized"); + auto stream = reinterpret_cast( + ctx.device_context()) + .stream(); + + auto m = x1_t->numel(); + auto n = x2_t->numel(); + T distance = 0; + if (m == 0) { + distance = n; + } else if (n == 0) { + distance = m; + } else { + framework::Tensor dist_t; + dist_t.Resize({m + 1, n + 1}); + dist_t.mutable_data(ctx.GetPlace()); + auto dist = dist_t.data(); + auto x1 = x1_t->data(); + auto x2 = x2_t->data(); + + FillFirstColumn<<<1 + m / PADDLE_CUDA_NUM_THREADS, + PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, m, n); + + FillFirstRow<<<1 + n / PADDLE_CUDA_NUM_THREADS, + PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, n); + // compute the elements of distance matrix in the anti-diagonal diretion + for (size_t slice = 2; slice < m + n + 1; ++slice) { + int z_m = slice < m + 1 ? 0 : slice - m; + int z_n = slice < n + 1 ? 0 : slice - n; + // number of elments in the same anti-diagonal line + int size = slice - (z_m + z_n) + 1; + int start = slice < n + 1 ? slice : z_n * (n + 1) - 1; + Levenshtein<<<1 + (size - 1) / PADDLE_CUDA_NUM_THREADS, + PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, x1, x2, m, + n, start); + } + + Place gpu_place = boost::get(ctx.GetPlace()); + memory::Copy(platform::CPUPlace(), &distance, gpu_place, + dist + m * (n + 1) + n, sizeof(T), stream); + } + + if (normalized) { + distance = distance / n; + } + auto out = out_t->data(); + Place gpu_place = boost::get(ctx.GetPlace()); + float dist_f = distance; + memory::Copy(gpu_place, out, platform::CPUPlace(), &dist_f, sizeof(float), + stream); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; + +REGISTER_OP_GPU_KERNEL( + ctc_edit_distance, + ops::CTCEditDistanceGPUKernel, + ops::CTCEditDistanceGPUKernel); diff --git a/python/paddle/v2/framework/tests/test_ctc_edit_distance.py b/python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py similarity index 84% rename from python/paddle/v2/framework/tests/test_ctc_edit_distance.py rename to python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py index 8a6b0b5390e..6694a6ee29d 100644 --- a/python/paddle/v2/framework/tests/test_ctc_edit_distance.py +++ b/python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py @@ -37,9 +37,11 @@ def Levenshtein(hyp, ref): class TestCTCEditDistanceOp(OpTest): def setUp(self): self.op_type = "ctc_edit_distance" - normalized = True - x1 = np.array([0, 12, 3, 5]).astype("int64") - x2 = np.array([0, 12, 4, 7, 8]).astype("int64") + normalized = False + #x1 = np.array([0, 12, 3, 5]).astype("int64") + #x2 = np.array([0, 12, 4, 7, 8]).astype("int64") + x1 = np.array([0, 12, 5]).astype("int64") + x2 = np.array([0, 12, 4]).astype("int64") distance = Levenshtein(hyp=x1, ref=x2) if normalized is True: -- GitLab From 116687a8ee8dab5938f8783428b4b5f416a443f5 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 29 Nov 2017 09:07:15 +0000 Subject: [PATCH 0021/2305] clean up code in ctc_edit_distance_op --- paddle/operators/ctc_edit_distance_op.cc | 10 +++- paddle/operators/ctc_edit_distance_op.cu | 59 ++++++++++--------- paddle/operators/ctc_edit_distance_op.h | 16 ++--- .../fluid/tests/test_ctc_edit_distance_op.py | 8 +-- 4 files changed, 49 insertions(+), 44 deletions(-) diff --git a/paddle/operators/ctc_edit_distance_op.cc b/paddle/operators/ctc_edit_distance_op.cc index fae5cfc1179..d2f4ce67c20 100644 --- a/paddle/operators/ctc_edit_distance_op.cc +++ b/paddle/operators/ctc_edit_distance_op.cc @@ -27,6 +27,13 @@ class CTCEditDistanceOp : public framework::OperatorWithKernel { PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) shouldn't be null."); ctx->SetOutputDim("Out", {1}); } + + protected: + framework::OpKernelType GetKernelType( + const framework::ExecutionContext &ctx) const override { + return framework::OpKernelType(framework::DataType::FP32, + ctx.device_context()); + } }; class CTCEditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { @@ -70,5 +77,4 @@ REGISTER_OP_WITHOUT_GRADIENT(ctc_edit_distance, ops::CTCEditDistanceOp, ops::CTCEditDistanceOpMaker); REGISTER_OP_CPU_KERNEL( ctc_edit_distance, - ops::CTCEditDistanceKernel, - ops::CTCEditDistanceKernel); + ops::CTCEditDistanceKernel); diff --git a/paddle/operators/ctc_edit_distance_op.cu b/paddle/operators/ctc_edit_distance_op.cu index 872268296ef..22871acc4ec 100644 --- a/paddle/operators/ctc_edit_distance_op.cu +++ b/paddle/operators/ctc_edit_distance_op.cu @@ -39,7 +39,7 @@ __global__ void FillFirstColumn(T* dist, const int M, const int N) { } template -__global__ void Levenshtein(T* dist, const T* x1, const T* x2, const int M, +__global__ void Levenshtein(T* dist, const int* x1, const int* x2, const int M, const int N, const int start) { int idx = blockDim.x * blockIdx.x + threadIdx.x; int offset = N; @@ -55,6 +55,15 @@ __global__ void Levenshtein(T* dist, const T* x1, const T* x2, const int M, } } +template +__global__ void SetOutput(T* out, const T* dist, const int M, const int N, + bool normalized) { + int idx = blockDim.x * blockIdx.x + threadIdx.x; + if (idx == 0) { + out[0] = normalized ? dist[M * (N + 1) + N] / N : dist[M * (N + 1) + N]; + } +} + template class CTCEditDistanceGPUKernel : public framework::OpKernel { public: @@ -64,7 +73,8 @@ class CTCEditDistanceGPUKernel : public framework::OpKernel { auto* x1_t = ctx.Input("X1"); auto* x2_t = ctx.Input("X2"); - out_t->mutable_data(ctx.GetPlace()); + out_t->mutable_data(ctx.GetPlace()); + auto out = out_t->data(); auto normalized = ctx.Attr("normalized"); auto stream = reinterpret_cast( @@ -73,49 +83,41 @@ class CTCEditDistanceGPUKernel : public framework::OpKernel { auto m = x1_t->numel(); auto n = x2_t->numel(); - T distance = 0; - if (m == 0) { - distance = n; - } else if (n == 0) { - distance = m; + T distance = 0.0; + if (m == 0 || n == 0) { + distance = std::max(m, n); + if (normalized) { + distance = distance / n; + } + memory::Copy(boost::get(ctx.GetPlace()), out, platform::CPUPlace(), + &distance, sizeof(T), stream); } else { framework::Tensor dist_t; dist_t.Resize({m + 1, n + 1}); dist_t.mutable_data(ctx.GetPlace()); auto dist = dist_t.data(); - auto x1 = x1_t->data(); - auto x2 = x2_t->data(); + auto x1 = x1_t->data(); + auto x2 = x2_t->data(); FillFirstColumn<<<1 + m / PADDLE_CUDA_NUM_THREADS, PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, m, n); FillFirstRow<<<1 + n / PADDLE_CUDA_NUM_THREADS, PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, n); - // compute the elements of distance matrix in the anti-diagonal diretion - for (size_t slice = 2; slice < m + n + 1; ++slice) { + // Compute the elements of distance matrix in the anti-diagonal diretion + for (int64_t slice = 2; slice < m + n + 1; ++slice) { int z_m = slice < m + 1 ? 0 : slice - m; int z_n = slice < n + 1 ? 0 : slice - n; - // number of elments in the same anti-diagonal line - int size = slice - (z_m + z_n) + 1; - int start = slice < n + 1 ? slice : z_n * (n + 1) - 1; + int size = slice - (z_m + z_n) + 1; // number of elments in the same + // anti-diagonal line to update + int start = slice < n + 1 ? slice : z_n * (n + 1) - 1; // start index + Levenshtein<<<1 + (size - 1) / PADDLE_CUDA_NUM_THREADS, PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, x1, x2, m, n, start); } - - Place gpu_place = boost::get(ctx.GetPlace()); - memory::Copy(platform::CPUPlace(), &distance, gpu_place, - dist + m * (n + 1) + n, sizeof(T), stream); - } - - if (normalized) { - distance = distance / n; + SetOutput<<<1, 1, 0, stream>>>(out, dist, m, n, normalized); } - auto out = out_t->data(); - Place gpu_place = boost::get(ctx.GetPlace()); - float dist_f = distance; - memory::Copy(gpu_place, out, platform::CPUPlace(), &dist_f, sizeof(float), - stream); } }; @@ -126,5 +128,4 @@ namespace ops = paddle::operators; REGISTER_OP_GPU_KERNEL( ctc_edit_distance, - ops::CTCEditDistanceGPUKernel, - ops::CTCEditDistanceGPUKernel); + ops::CTCEditDistanceGPUKernel); diff --git a/paddle/operators/ctc_edit_distance_op.h b/paddle/operators/ctc_edit_distance_op.h index a52960f1ef7..08f29cf24ac 100644 --- a/paddle/operators/ctc_edit_distance_op.h +++ b/paddle/operators/ctc_edit_distance_op.h @@ -35,7 +35,7 @@ class CTCEditDistanceKernel : public framework::OpKernel { auto m = x1_t->numel(); auto n = x2_t->numel(); - float distance = 0.0; + T distance = 0.0; if (m == 0) { distance = n; } else if (n == 0) { @@ -45,16 +45,16 @@ class CTCEditDistanceKernel : public framework::OpKernel { dist_t.Resize({m + 1, n + 1}); dist_t.mutable_data(ctx.GetPlace()); auto dist = dist_t.data(); - auto x1 = x1_t->data(); - auto x2 = x2_t->data(); - for (size_t i = 0; i < m + 1; ++i) { + auto x1 = x1_t->data(); + auto x2 = x2_t->data(); + for (int64_t i = 0; i < m + 1; ++i) { dist[i * (n + 1)] = i; } - for (size_t j = 0; j < n + 1; ++j) { + for (int64_t j = 0; j < n + 1; ++j) { dist[j] = j; } - for (size_t i = 1; i < m + 1; ++i) { - for (size_t j = 1; j < n + 1; ++j) { + for (int64_t i = 1; i < m + 1; ++i) { + for (int64_t j = 1; j < n + 1; ++j) { int cost = x1[i - 1] == x2[j - 1] ? 0 : 1; int dels = dist[(i - 1) * (n + 1) + j] + 1; int ins = dist[i * (n + 1) + (j - 1)] + 1; @@ -68,7 +68,7 @@ class CTCEditDistanceKernel : public framework::OpKernel { if (normalized) { distance = distance / n; } - auto out = out_t->data(); + auto out = out_t->data(); out[0] = distance; } }; diff --git a/python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py b/python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py index 6694a6ee29d..62c233b34f6 100644 --- a/python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py +++ b/python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py @@ -37,11 +37,9 @@ def Levenshtein(hyp, ref): class TestCTCEditDistanceOp(OpTest): def setUp(self): self.op_type = "ctc_edit_distance" - normalized = False - #x1 = np.array([0, 12, 3, 5]).astype("int64") - #x2 = np.array([0, 12, 4, 7, 8]).astype("int64") - x1 = np.array([0, 12, 5]).astype("int64") - x2 = np.array([0, 12, 4]).astype("int64") + normalized = True + x1 = np.array([0, 12, 3, 5]).astype("int32") + x2 = np.array([0, 12, 4, 7, 8]).astype("int32") distance = Levenshtein(hyp=x1, ref=x2) if normalized is True: -- GitLab From b82049bdca55aa596ecaf4e7390f96ef7e3982c7 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 29 Nov 2017 09:40:49 +0000 Subject: [PATCH 0022/2305] revise the doc in ctc_edit_distance_op --- paddle/operators/ctc_edit_distance_op.cc | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/paddle/operators/ctc_edit_distance_op.cc b/paddle/operators/ctc_edit_distance_op.cc index d2f4ce67c20..11e9983e243 100644 --- a/paddle/operators/ctc_edit_distance_op.cc +++ b/paddle/operators/ctc_edit_distance_op.cc @@ -58,12 +58,19 @@ class CTCEditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { AddComment(R"DOC( CTCEditDistance operator computes the edit distance of two sequences, one named -hypothesis and another named reference. +hypothesis with length M and another named reference with length N. -Edit distance measures how dissimilar two strings, one is hypothesis and another -is reference, are by counting the minimum number of operations to transform -one string into anthor. +Edit distance, also called Levenshtein distance, measures how dissimilar two strings +are by counting the minimum number of operations to transform one string into anthor. +Here the operations include insertion, deletion, and substitution. For example, +given hypothesis string A = "kitten" and reference B = "sitting", the edit distance +is 3 for A will be transformed into B at least after two substitutions and one +insertion: + + "kitten" -> "sitten" -> "sittin" -> "sitting" +If Attr(normalized) is true, the edit distance will be divided by the length of +reference string N. )DOC"); } }; -- GitLab From ee0113af31a0ac678ae62190cf62fbc7c3c098d6 Mon Sep 17 00:00:00 2001 From: wanghaox Date: Fri, 1 Dec 2017 14:04:37 +0800 Subject: [PATCH 0023/2305] implement of prior box operator for ssd --- paddle/operators/prior_box_op.cc | 167 +++++++++++++++ paddle/operators/prior_box_op.cu | 20 ++ paddle/operators/prior_box_op.h | 199 ++++++++++++++++++ .../v2/fluid/tests/test_prior_box_op.py | 179 ++++++++++++++++ 4 files changed, 565 insertions(+) create mode 100644 paddle/operators/prior_box_op.cc create mode 100755 paddle/operators/prior_box_op.cu create mode 100644 paddle/operators/prior_box_op.h create mode 100644 python/paddle/v2/fluid/tests/test_prior_box_op.py diff --git a/paddle/operators/prior_box_op.cc b/paddle/operators/prior_box_op.cc new file mode 100644 index 00000000000..fe1ccceb066 --- /dev/null +++ b/paddle/operators/prior_box_op.cc @@ -0,0 +1,167 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/prior_box_op.h" + +namespace paddle { +namespace operators { + +class PriorBoxOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("Input"), + "Input(X) of SequenceSliceOp should not be null."); + PADDLE_ENFORCE(ctx->HasInput("Image"), + "Input(Offset) of SequenceSliceOp should not be null."); + + auto image_dims = ctx->GetInputDim("Image"); + auto input_dims = ctx->GetInputDim("Input"); + PADDLE_ENFORCE(image_dims.size() == 4, + "The format of input tensor is NCHW."); + + auto min_sizes = ctx->Attrs().Get>("min_sizes"); + auto max_sizes = ctx->Attrs().Get>("max_sizes"); + auto variances = ctx->Attrs().Get>("variances"); + auto input_aspect_ratio = + ctx->Attrs().Get>("aspect_ratios"); + bool flip = ctx->Attrs().Get("flip"); + + PADDLE_ENFORCE_GT(min_sizes.size(), 0, "must provide min_size."); + for (size_t i = 0; i < min_sizes.size(); ++i) { + PADDLE_ENFORCE_GT(min_sizes[i], 0, "min_sizes[%d] must be positive.", i); + } + + std::vector aspect_ratios; + expand_aspect_ratios(input_aspect_ratio, flip, aspect_ratios); + + int num_priors = aspect_ratios.size() * min_sizes.size(); + if (max_sizes.size() > 0) { + PADDLE_ENFORCE_EQ(max_sizes.size(), min_sizes.size(), + "The length of min_size and max_size must be equal."); + for (size_t i = 0; i < min_sizes.size(); ++i) { + PADDLE_ENFORCE_GT(max_sizes[i], min_sizes[i], + "max_size[%d] must be greater than min_size[%d].", i, + i); + num_priors += 1; + } + } + + if (variances.size() > 1) { + PADDLE_ENFORCE_EQ(variances.size(), 4, + "Must and only provide 4 variance."); + for (size_t i = 0; i < variances.size(); ++i) { + PADDLE_ENFORCE_GT(variances[i], 0.0, + "variance[%d] must be greater than 0.", i); + } + } else if (variances.size() == 1) { + PADDLE_ENFORCE_GT(variances[0], 0.0, + "variance[0] must be greater than 0."); + } + + const int img_h = ctx->Attrs().Get("img_h"); + PADDLE_ENFORCE_GT(img_h, 0, "img_h should be larger than 0."); + const int img_w = ctx->Attrs().Get("img_w"); + PADDLE_ENFORCE_GT(img_w, 0, "img_w should be larger than 0."); + + const float step_h = ctx->Attrs().Get("step_h"); + PADDLE_ENFORCE_GT(step_h, 0.0, "step_h should be larger than 0."); + const float step_w = ctx->Attrs().Get("step_w"); + PADDLE_ENFORCE_GT(step_w, 0.0, "step_w should be larger than 0."); + + const int layer_height = input_dims[3]; + const int layer_width = input_dims[2]; + + std::vector dim_vec(3); + // Since all images in a batch has same height and width, we only need to + // generate one set of priors which can be shared across all images. + dim_vec[0] = 1; + // 2 channels. First channel stores the mean of each prior coordinate. + // Second channel stores the variance of each prior coordinate. + dim_vec[1] = 2; + dim_vec[2] = layer_width * layer_height * num_priors * 4; + PADDLE_ENFORCE_GT(dim_vec[2], 0, + "output_dim[2] must larger than 0." + "check your data dims"); + auto output_dim = framework::make_ddim(dim_vec); + ctx->SetOutputDim("Out", output_dim); + } + + protected: + framework::OpKernelType GetKernelType( + const framework::ExecutionContext& ctx) const override { + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Image")->type()), + ctx.device_context()); + } +}; + +class PriorBoxOpMaker : public framework::OpProtoAndCheckerMaker { + public: + PriorBoxOpMaker(framework::OpProto* proto, + framework::OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("Input", + "(Tensor), " + "the input feature data of PriorBoxOp."); + AddInput("Image", + "(Tensor), " + "the input image data of PriorBoxOp."); + AddOutput("Out", "(Tensor), the output prior boxes of PriorBoxOp."); + AddAttr>("min_sizes", "(vector) ", + "List of min sizes of generated prior boxes."); + AddAttr>("max_sizes", "(vector) ", + "List of max sizes of generated prior boxes."); + AddAttr>( + "aspect_ratios", "(vector) ", + "List of aspect ratios of generated prior boxes.") + .SetDefault({}); + AddAttr>( + "variances", "(vector) ", + "List of variances to be encoded in prior boxes.") + .SetDefault({0.1}); + AddAttr("flip", "(bool) ", "Whether to flip aspect ratios.") + .SetDefault(true); + AddAttr("clip", "(bool) ", "Whether to clip out-of-boundary boxes.") + .SetDefault(true); + AddAttr("img_w", "").SetDefault(0); + AddAttr("img_h", "").SetDefault(0); + AddAttr("step_w", + "Prior boxes step across width, 0 for auto calculation.") + .SetDefault(0.0); + AddAttr("step_h", + "Prior boxes step across height, 0 for auto calculation.") + .SetDefault(0.0); + AddAttr("offset", + "(float) " + "Prior boxes center offset.") + .SetDefault(0.5); + AddComment(R"DOC( +Prior box operator +Generate prior boxes for SSD(Single Shot MultiBox Detector) algorithm. +Please get more information from the following papers: +https://arxiv.org/abs/1512.02325. +)DOC"); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP_WITHOUT_GRADIENT(prior_box, ops::PriorBoxOp, ops::PriorBoxOpMaker); +REGISTER_OP_CPU_KERNEL( + prior_box, ops::PriorBoxOpKernel, + ops::PriorBoxOpKernel); diff --git a/paddle/operators/prior_box_op.cu b/paddle/operators/prior_box_op.cu new file mode 100755 index 00000000000..d1928462a2d --- /dev/null +++ b/paddle/operators/prior_box_op.cu @@ -0,0 +1,20 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/prior_box_op.h" + +namespace ops = paddle::operators; +REGISTER_OP_GPU_KERNEL( + prior_box, ops::PriorBoxOpKernel, + ops::PriorBoxOpKernel); diff --git a/paddle/operators/prior_box_op.h b/paddle/operators/prior_box_op.h new file mode 100644 index 00000000000..6dabba52650 --- /dev/null +++ b/paddle/operators/prior_box_op.h @@ -0,0 +1,199 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once +#include "paddle/framework/op_registry.h" +#include "paddle/operators/math/math_function.h" +// #include "paddle/operators/strided_memcpy.h" + +namespace paddle { +namespace operators { + +inline void expand_aspect_ratios(const std::vector input_aspect_ratior, + bool flip, + std::vector& output_aspect_ratior) { + constexpr float eps = 1e-6; + output_aspect_ratior.clear(); + output_aspect_ratior.push_back(1.); + for (size_t i = 0; i < input_aspect_ratior.size(); ++i) { + float ar = input_aspect_ratior[i]; + bool already_exist = false; + for (size_t j = 0; j < output_aspect_ratior.size(); ++j) { + if (fabs(ar - output_aspect_ratior[j]) < eps) { + already_exist = true; + break; + } + } + if (!already_exist) { + output_aspect_ratior.push_back(ar); + if (flip) { + output_aspect_ratior.push_back(1. / ar); + } + } + } +} + +template +class PriorBoxOpKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* input = ctx.Input("Input"); + auto* image = ctx.Input("Image"); + auto* out = ctx.Output("Out"); + + auto min_sizes = ctx.Attr>("min_sizes"); + auto max_sizes = ctx.Attr>("max_sizes"); + auto input_aspect_ratio = ctx.Attr>("aspect_ratios"); + auto variances = ctx.Attr>("variances"); + auto flip = ctx.Attr("flip"); + auto clip = ctx.Attr("clip"); + + std::vector aspect_ratios; + expand_aspect_ratios(input_aspect_ratio, flip, aspect_ratios); + + auto img_w = ctx.Attr("img_w"); + auto img_h = ctx.Attr("img_h"); + auto step_w = ctx.Attr("step_w"); + auto step_h = ctx.Attr("step_h"); + auto offset = ctx.Attr("offset"); + + int img_width, img_height; + if (img_h == 0 || img_w == 0) { + img_width = image->dims()[2]; + img_height = image->dims()[3]; + } else { + img_width = img_w; + img_height = img_h; + } + + const int layer_width = input->dims()[2]; + const int layer_height = input->dims()[3]; + + float step_width, step_height; + if (step_w == 0 || step_h == 0) { + step_width = static_cast(img_width) / layer_width; + step_height = static_cast(img_height) / layer_height; + } else { + step_width = step_w; + step_height = step_h; + } + + int num_priors = aspect_ratios.size() * min_sizes.size(); + if (max_sizes.size() > 0) { + num_priors += max_sizes.size(); + } + + int dim = layer_height * layer_width * num_priors * 4; + + T* output_data = nullptr; + framework::Tensor output_cpu; + out->mutable_data(ctx.GetPlace()); + if (platform::is_gpu_place(ctx.GetPlace())) { + output_data = + output_cpu.mutable_data(out->dims(), platform::CPUPlace()); + } else { + output_data = out->mutable_data(ctx.GetPlace()); + } + + int idx = 0; + for (int h = 0; h < layer_height; ++h) { + for (int w = 0; w < layer_width; ++w) { + float center_x = (w + offset) * step_width; + float center_y = (h + offset) * step_height; + float box_width, box_height; + for (size_t s = 0; s < min_sizes.size(); ++s) { + int min_size = min_sizes[s]; + // first prior: aspect_ratio = 1, size = min_size + box_width = box_height = min_size; + // xmin + output_data[idx++] = (center_x - box_width / 2.) / img_width; + // ymin + output_data[idx++] = (center_y - box_height / 2.) / img_height; + // xmax + output_data[idx++] = (center_x + box_width / 2.) / img_width; + // ymax + output_data[idx++] = (center_y + box_height / 2.) / img_height; + + if (max_sizes.size() > 0) { + int max_size = max_sizes[s]; + // second prior: aspect_ratio = 1, + // size = sqrt(min_size * max_size) + box_width = box_height = sqrt(min_size * max_size); + // xmin + output_data[idx++] = (center_x - box_width / 2.) / img_width; + // ymin + output_data[idx++] = (center_y - box_height / 2.) / img_height; + // xmax + output_data[idx++] = (center_x + box_width / 2.) / img_width; + // ymax + output_data[idx++] = (center_y + box_height / 2.) / img_height; + } + + // rest of priors + for (size_t r = 0; r < aspect_ratios.size(); ++r) { + float ar = aspect_ratios[r]; + if (fabs(ar - 1.) < 1e-6) { + continue; + } + box_width = min_size * sqrt(ar); + box_height = min_size / sqrt(ar); + // xmin + output_data[idx++] = (center_x - box_width / 2.) / img_width; + // ymin + output_data[idx++] = (center_y - box_height / 2.) / img_height; + // xmax + output_data[idx++] = (center_x + box_width / 2.) / img_width; + // ymax + output_data[idx++] = (center_y + box_height / 2.) / img_height; + } + } + } + } + + // clip the prior's coordidate such that it is within [0, 1] + if (clip) { + for (int d = 0; d < dim; ++d) { + output_data[d] = std::min(std::max(output_data[d], 0.), 1.); + } + } + + // set the variance. + auto output_stride = framework::stride(out->dims()); + output_data += output_stride[1]; + if (variances.size() == 1) { + for (int i = 0; i < dim; ++i) { + output_data[i] = variances[0]; + } + } else { + int count = 0; + for (int h = 0; h < layer_height; ++h) { + for (int w = 0; w < layer_width; ++w) { + for (int i = 0; i < num_priors; ++i) { + for (int j = 0; j < 4; ++j) { + output_data[count] = variances[j]; + ++count; + } + } + } + } + } + if (platform::is_gpu_place(ctx.GetPlace())) { + framework::CopyFrom(output_cpu, platform::CPUPlace(), + ctx.device_context(), out); + } + } +}; + +} // namespace operators +} // namespace paddle diff --git a/python/paddle/v2/fluid/tests/test_prior_box_op.py b/python/paddle/v2/fluid/tests/test_prior_box_op.py new file mode 100644 index 00000000000..2f821889529 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_prior_box_op.py @@ -0,0 +1,179 @@ +import unittest +import numpy as np +import sys +import math +from op_test import OpTest + + +class TestPriorBoxOp(OpTest): + def set_data(self): + self.init_test_params() + self.init_test_input() + self.init_test_output() + self.inputs = {'Input': self.input, 'Image': self.image} + + self.attrs = { + 'min_sizes': self.min_sizes, + 'max_sizes': self.max_sizes, + 'aspect_ratios': self.aspect_ratios, + 'variances': self.variances, + 'flip': self.flip, + 'clip': self.clip, + 'step_w': self.step_w, + 'step_h': self.step_h, + 'img_w': self.image_w, + 'img_h': self.image_h, + 'offset': self.offset + } + + self.outputs = {'Out': self.output} + + def test_check_output(self): + self.check_output() + + def test_check_grad(self): + return + + def setUp(self): + self.op_type = "prior_box" + self.set_data() + + def init_test_params(self): + self.layer_w = 4 + self.layer_h = 4 + + self.image_w = 20 + self.image_h = 20 + + self.step_w = float(self.image_w) / float(self.layer_w) + self.step_h = float(self.image_h) / float(self.layer_h) + + self.input_channels = 2 + self.image_channels = 3 + self.batch_size = 10 + + self.min_sizes = [2, 4] + self.min_sizes = np.array(self.min_sizes).astype('int64') + self.max_sizes = [5, 10] + self.max_sizes = np.array(self.max_sizes).astype('int64') + self.aspect_ratios = [2.0, 3.0] + self.flip = True + self.real_aspect_ratios = [1, 2.0, 1.0 / 2.0, 3.0, 1.0 / 3.0] + self.aspect_ratios = np.array( + self.aspect_ratios, dtype=np.float).flatten() + self.variances = [0.1, 0.1, 0.2, 0.2] + self.variances = np.array(self.variances, dtype=np.float).flatten() + + self.clip = True + + self.num_priors = len(self.real_aspect_ratios) * len(self.min_sizes) + if len(self.max_sizes) > 1: + self.num_priors += len(self.max_sizes) + self.offset = 0.5 + + def init_test_input(self): + self.image = np.random.random( + (self.batch_size, self.image_channels, self.image_w, + self.image_h)).astype('float32') + + self.input = np.random.random( + (self.batch_size, self.input_channels, self.layer_w, + self.layer_h)).astype('float32') + + def init_test_output(self): + dim = self.layer_w * self.layer_h * self.num_priors * 4 + out_dim = (1, 2, dim) + output = np.zeros(out_dim).astype('float32') + + idx = 0 + for h in range(self.layer_h): + for w in range(self.layer_w): + center_x = (w + self.offset) * self.step_w + center_y = (h + self.offset) * self.step_h + for s in range(len(self.min_sizes)): + min_size = self.min_sizes[s] + # first prior: aspect_ratio = 1, size = min_size + box_width = box_height = min_size + # xmin + output[0, 0, idx] = ( + center_x - box_width / 2.) / self.image_w + idx += 1 + # ymin + output[0, 0, idx] = ( + center_y - box_height / 2.) / self.image_h + idx += 1 + # xmax + output[0, 0, idx] = ( + center_x + box_width / 2.) / self.image_w + idx += 1 + # ymax + output[0, 0, idx] = ( + center_y + box_height / 2.) / self.image_h + idx += 1 + + if len(self.max_sizes) > 0: + max_size = self.max_sizes[s] + # second prior: aspect_ratio = 1, + # size = sqrt(min_size * max_size) + box_width = box_height = math.sqrt(min_size * max_size) + # xmin + output[0, 0, idx] = ( + center_x - box_width / 2.) / self.image_w + idx += 1 + # ymin + output[0, 0, idx] = ( + center_y - box_height / 2.) / self.image_h + idx += 1 + # xmax + output[0, 0, idx] = ( + center_x + box_width / 2.) / self.image_w + idx += 1 + # ymax + output[0, 0, idx] = ( + center_y + box_height / 2.) / self.image_h + idx += 1 + + # rest of priors + for r in range(len(self.real_aspect_ratios)): + ar = self.real_aspect_ratios[r] + if math.fabs(ar - 1.) < 1e-6: + continue + box_width = min_size * math.sqrt(ar) + box_height = min_size / math.sqrt(ar) + # xmin + output[0, 0, idx] = ( + center_x - box_width / 2.) / self.image_w + idx += 1 + # ymin + output[0, 0, idx] = ( + center_y - box_height / 2.) / self.image_h + idx += 1 + # xmax + output[0, 0, idx] = ( + center_x + box_width / 2.) / self.image_w + idx += 1 + # ymax + output[0, 0, idx] = ( + center_y + box_height / 2.) / self.image_h + idx += 1 + # clip the prior's coordidate such that it is within[0, 1] + if self.clip: + for d in range(dim): + output[0, 0, d] = min(max(output[0, 0, d], 0), 1) + # set the variance. + if len(self.variances) == 1: + for i in range(dim): + output[0, 1, i] = self.variances[0] + else: + count = 0 + for h in range(self.layer_h): + for w in range(self.layer_w): + for i in range(self.num_priors): + for j in range(4): + output[0, 1, count] = self.variances[j] + count += 1 + self.output = output.astype('float32') + + +if __name__ == '__main__': + unittest.main() -- GitLab From 62efc896e1fb1e895414d77d4193891fdee4ca99 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Thu, 7 Dec 2017 17:02:49 +0800 Subject: [PATCH 0024/2305] Refine code 1. Add copyright info 2. Overload structure for customized random seed --- paddle/operators/math/sampler.cc | 31 +++++++++++++++++++++++++++---- paddle/operators/math/sampler.h | 16 ++++++++++++++-- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/paddle/operators/math/sampler.cc b/paddle/operators/math/sampler.cc index 52628c3b03b..4f1cbfe31ac 100644 --- a/paddle/operators/math/sampler.cc +++ b/paddle/operators/math/sampler.cc @@ -1,3 +1,17 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + #include "sampler.h" namespace paddle { @@ -7,8 +21,13 @@ Sampler::~Sampler() {} UniformSampler::UniformSampler(int64 range) : Sampler(range), inv_range_(1.0 / range) { - std::random_device r; - random_engine_ = std::make_shared(r()); + random_engine_ = std::make_shared(seed_); + dist_ = std::make_shared>(0, range); +} + +UniformSampler::UniformSampler(int64 range, unsigned int seed) + : Sampler(range, seed), inv_range_(1.0 / range) { + random_engine_ = std::make_shared(seed_); dist_ = std::make_shared>(0, range); } @@ -18,11 +37,15 @@ float UniformSampler::Probability(int64 value) const { return inv_range_; } LogUniformSampler::LogUniformSampler(int64 range) : Sampler(range), log_range_(log(range + 1)) { - std::random_device r; - random_engine_ = std::make_shared(r()); + random_engine_ = std::make_shared(seed_); dist_ = std::make_shared>(0, 1); } +LogUniformSampler::LogUniformSampler(int64 range, unsigned int seed) + : Sampler(range, seed), log_range_(log(range + 1)) { + random_engine_ = std::make_shared(seed_); + dist_ = std::make_shared>(0, 1); +} int64 LogUniformSampler::Sample() const { // Got Log Uniform distribution from uniform distribution by // inverse_transform_sampling method diff --git a/paddle/operators/math/sampler.h b/paddle/operators/math/sampler.h index bcd7bead352..8f82089e7bd 100644 --- a/paddle/operators/math/sampler.h +++ b/paddle/operators/math/sampler.h @@ -20,14 +20,21 @@ namespace paddle { namespace operators { namespace math { -// TODO: Support for GPU +// TODO(wanghaoshuang): Support for GPU /** * Sample integers from [0, range). */ class Sampler { public: - explicit Sampler(int64 range) : range_(range) { /* check range > 0*/ + explicit Sampler(int64 range) : range_(range) { + PADDLE_ENFORCE_GT(range, 0); + std::random_device r; + seed_ = r(); + } + explicit Sampler(int64 range, unsigned int seed) + : range_(range), seed_(seed) { + PADDLE_ENFORCE_GT(range, 0); } virtual ~Sampler(); // Sample a single value @@ -39,6 +46,7 @@ class Sampler { protected: const int64 range_; + unsigned int seed_; }; /** @@ -50,6 +58,8 @@ class UniformSampler : public Sampler { public: explicit UniformSampler(int64 range); + explicit UniformSampler(int64 range, unsigned int seed); + ~UniformSampler() override {} int64 Sample() const override; @@ -71,6 +81,8 @@ class LogUniformSampler : public Sampler { public: explicit LogUniformSampler(int64 range); + explicit LogUniformSampler(int64 range, unsigned int seed); + ~LogUniformSampler() override {} int64 Sample() const override; -- GitLab From 03a02d695670ad4b989d8e167a494ae7ea6778b9 Mon Sep 17 00:00:00 2001 From: Yi Wang Date: Thu, 7 Dec 2017 15:54:45 -0800 Subject: [PATCH 0025/2305] Update deisgn doc --- doc/design/concurrent_programming.md | 161 +++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 doc/design/concurrent_programming.md diff --git a/doc/design/concurrent_programming.md b/doc/design/concurrent_programming.md new file mode 100644 index 00000000000..0fd37bd6974 --- /dev/null +++ b/doc/design/concurrent_programming.md @@ -0,0 +1,161 @@ +# Design Doc: Concurrent Programming with Fluid + +With PaddlePaddle Fluid, users describe a program other than a model. The program is a [`ProgramDesc`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/framework.proto) protobuf message. TensorFlow/MxNet/Caffe2 applications generate protobuf messages too, but their protobuf messages represent the model, a graph of operators, but not the program that trains/uses the model. + +Many know that when we program TensorFlow, we can specify the device on which each operator runs. This allows us to create a concurrent/parallel AI application. An interesting questions is **how does a `ProgramDesc` represents a concurrent program?** + +The answer relies on the fact that a `ProgramDesc` is similar to an abstract syntax tree (AST) that describes a program. So users just program a concurrent program that they do with any concurrent programming language, e.g., [Go](https://golang.org). + +## An Analogy + +The following table compares concepts in Fluid and Go + +| Go | Fluid | +|----|-------| +|user-defined functions | [layers](https://github.com/PaddlePaddle/Paddle/tree/develop/python/paddle/v2/fluid) | +| control-flow and built-in functions | [intrinsics/operators](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/operators) | +| goroutines, channels | [class ThreadPool](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/framework/thread_pool.h) | +| runtime | [class Executor](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/executor.h) | + +## An Example Concurrent Program + +To review all above concepts in an example, let us take a simple program and writes its distributed version. + +Suppose that we want to parallelize a naive Fluid program (written in Go and calling Fluid's Go binding) that multiplies two tensors. + +```go +import "fluid" + +func paddlepaddle() { + X = fluid.read(...) + W = fluid.Tensor(...) + Y = fluid.mult(X, W) +} +``` + +Please be aware that the Fluid's Go binding provides the default `main` function, which calls the `paddlepaddle` function, which, in this case, is defined in above program and creates the following `ProgramDesc` message. + +```protobuf +message ProgramDesc { + block[0] = Block { + vars = [X, W, Y], + ops = [ + read(output = X) + assign(input = ..., output = W) + mult(input = {X, W}, output = Y) + ], + } +} +``` + +Then, the default `main` function calls `fluid.run()`, which creates an instance of the [`class Executor`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/executor.h) and calls `Executor.Run(block[0])`, where `block[0]` is the first and only block defined in above `ProgramDesc` message. + +The default `main` function is defined as follows: + +```go +func main() { + paddlepaddle() + fluid.run() +} +``` + +## The Concurrent Version + +By parallelizing the above program, we could support very big tensor X by splitting into small pieces {x_1, x_2, ...} and sent each piece to worker process/node for parallel multiplication. + +In this case, we can write a transpiler that takes a `ProgramDesc` message that represents the above example program and outputs two `ProgramDesc` messages, one for running on the master process/node, and the other one for worker processes/nodes. + +### The Master Program + +The master program could look like the following: + +```protobuf +message ProgramDesc { + block[0] = Block { + vars = [X, L, Y], + ops = [ + read(output = X) + kube_get_workers_addrs(output = L) + Y = tensor_array(len(L)) + parallel_for(input = X, output = Y, + attrs = {L, block_id(1)}) # referring to block 1 + ] + } + + block[1] = Block { + vars = [x, y, index], + ops = [ + slice(input = [X, index], output = x) # index is initialized by parallel_for + send(input = x, attrs = L[index]) + recv(outputs = y, attrs = L[index]) + assign(input = y, output = Y[index]) + ] + } +} +``` + +The equivalent Fluid program (calling the Go binding) is: + +```go +func main() { //// block 0 + X = fluid.read(...) + L = fluid.k8s.get_worker_addrs() + Y = fluid.tensor_array(len(L)) + fluid.parallel_for(X, L, + func(index int) { //// block 1 + x = X[index] + fluid.send(L[index], x) + y = fluid.recv(L[index]) + Y[index] = y + }) +} +``` + +An explanation of the above program: + +- `fluid.k8s` is a package that provides access to Kubernetes API. +- `fluid.k8s.get_worker_addrs` returns the list of IP and ports of all pods of the current job except for the current one (the master pod). +- `fluid.tensor_array` creates a [tensor array](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/lod_tensor_array.h). `fluid.parallel_for` creates a `ParallelFor` intrinsic, which, when executed, + + 1. creates `len(L)` scopes, each for the concurrent running of the sub-block (block 1 in this case), and initializes a variable named "index" in the scope to an integer value in the range `[0, len(L)-1]`, and + 2. creates `len(L)` threads by calling into the `ThreadPool` singleton, each thread + 1. creates an Executor instance, and + 2. calls `Executor.Run(block)`, where `block` is block 1 as explained above. + +### The Worker Program + +The worker program looks like + +```go +func main() { + W = Tensor(...) + x = fluid.listen_and_do( + fluid.k8s.self_addr(), + func(input Tensor) { + output = fluid.mult(input, W) + }) +} +``` + +where + +- `fluid.listen_and_do` creates a `ListenAndDo` intrinsic, which, when executed, + 1. listens on the current pod's IP address, as returned by `fliud.k8s.self_addr()`, + 2. once a connection is established, + 1. creates a scope of two parameters, "input" and "output", + 2. reads a [Fluid variable](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/variable.h) and saves it into "input", + 3. creates an Executor instance and calls `Executor.Run(block)`, where the block is generated by running the lambda specified as the second parameter of `fluid.listen_and_do`. + +## Summarization + +From the above example, we see that: + +1. Fluid enables the imperative programming paradigm by: + 1. letting users describe a program, but not a model (a sequence of layers, or a graph of operators), and + 2. call the `fluid.run` function that runs the program implicitly. +1. The program is described as a `ProgramDesc` protobuf message. +2. Function `Executor.Run` takes a block, instead of a `ProgramDesc`, as its parameter. +3. `fluid.run` calls `Executor.Run` to run the first block in the `ProgramDesc` message. +4. `Executor.Run`'s implementation is extremely simple -- it doesn't plan the execution nor create threads; instead, it runs on the current thread and execute intrinsics/operators' `Run` method sequentially as they appear in the `Block.ops` array. +5. Intrinsics/operators' `Run` method might create threads. For example, the `ListenAndDo` operator creates a thread to handle each incoming request. +6. Threads are not necessarily OS thread; instead, they could be [green threads](https://en.wikipedia.org/wiki/Green_threads) managed by ThreadPool. Multiple green threads might run on the same OS thread. An example green threads is Go's [goroutines](https://tour.golang.org/concurrency/1). -- GitLab From 6a6d433cbed63f83924d5f03810a211a501618d0 Mon Sep 17 00:00:00 2001 From: Yi Wang Date: Thu, 7 Dec 2017 16:36:14 -0800 Subject: [PATCH 0026/2305] Update block parent-kid relationship --- doc/design/concurrent_programming.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/design/concurrent_programming.md b/doc/design/concurrent_programming.md index 0fd37bd6974..afc65e831d5 100644 --- a/doc/design/concurrent_programming.md +++ b/doc/design/concurrent_programming.md @@ -83,6 +83,7 @@ message ProgramDesc { } block[1] = Block { + parent = 0, vars = [x, y, index], ops = [ slice(input = [X, index], output = x) # index is initialized by parallel_for @@ -121,6 +122,7 @@ An explanation of the above program: 2. creates `len(L)` threads by calling into the `ThreadPool` singleton, each thread 1. creates an Executor instance, and 2. calls `Executor.Run(block)`, where `block` is block 1 as explained above. +1. Please be aware that block 1 is a sub-block of block 0, so ops in block 1 could refer to variables defined in block 0. ### The Worker Program -- GitLab From 76941d90b1c38b121d711a6e4455f73dfba8f14f Mon Sep 17 00:00:00 2001 From: xzl Date: Wed, 13 Dec 2017 16:31:52 +0800 Subject: [PATCH 0027/2305] add upsample cpu&gpu forward&backward compare test --- paddle/gserver/tests/CMakeLists.txt | 1 + paddle/gserver/tests/test_Upsample.cpp | 152 +++++++++++++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 paddle/gserver/tests/test_Upsample.cpp diff --git a/paddle/gserver/tests/CMakeLists.txt b/paddle/gserver/tests/CMakeLists.txt index c295ea19c9c..5ef2726764b 100644 --- a/paddle/gserver/tests/CMakeLists.txt +++ b/paddle/gserver/tests/CMakeLists.txt @@ -28,6 +28,7 @@ gserver_test(test_BatchNorm) gserver_test(test_KmaxSeqScore) gserver_test(test_Expand) gserver_test(test_MaxPoolingWithMaskOutput) +gserver_test(test_Upsample) ########## test_MKLDNN layers and activations ########## if(WITH_MKLDNN) diff --git a/paddle/gserver/tests/test_Upsample.cpp b/paddle/gserver/tests/test_Upsample.cpp new file mode 100644 index 00000000000..9d6fa1d130c --- /dev/null +++ b/paddle/gserver/tests/test_Upsample.cpp @@ -0,0 +1,152 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include +#include +#include + +#include "LayerGradUtil.h" +#include "paddle/math/MathUtils.h" +#include "paddle/testing/TestUtil.h" + +using namespace paddle; + +void setPoolConfig(TestConfig* config, + PoolConfig* pool, + const string& poolType) { + (*config).biasSize = 0; + (*config).layerConfig.set_type("pool"); + (*config).layerConfig.set_num_filters(1); + + int kw = 2, kh = 2; + int pw = 0, ph = 0; + int sw = 2, sh = 2; + pool->set_pool_type(poolType); + pool->set_channels(2); + pool->set_size_x(kw); + pool->set_size_y(kh); + pool->set_start(0); + pool->set_padding(pw); + pool->set_padding_y(ph); + pool->set_stride(sw); + pool->set_stride_y(sh); + + int ow = outputSize(pool->img_size(), kw, pw, sw, /* caffeMode */ false); + int oh = outputSize(pool->img_size_y(), kh, ph, sh, /* caffeMode */ false); + pool->set_output_x(ow); + pool->set_output_y(oh); +} + +LayerPtr doOneUpsampleTest(MatrixPtr& inputMat, + const string& poolType, + bool use_gpu, + real* tempGradData) { + /* prepare maxPoolWithMaskLayer */ + TestConfig config; + config.inputDefs.push_back({INPUT_DATA, "layer_0", 128, 0}); + LayerInputConfig* input = config.layerConfig.add_inputs(); + PoolConfig* pool = input->mutable_pool_conf(); + + pool->set_img_size(8); + pool->set_img_size_y(8); + setPoolConfig(&config, pool, "max-pool-with-mask"); + config.layerConfig.set_size(pool->output_x() * pool->output_y() * + pool->channels()); + + config.layerConfig.set_name("MaxPoolWithMask"); + + std::vector dataLayers; + LayerMap layerMap; + vector datas; + + initDataLayer(config, + &dataLayers, + &datas, + &layerMap, + "MaxPoolWithMask", + 1, + false, + use_gpu); + + dataLayers[0]->getOutputValue()->copyFrom(*inputMat); + + FLAGS_use_gpu = use_gpu; + std::vector parameters; + LayerPtr maxPoolingWithMaskOutputLayer; + initTestLayer(config, &layerMap, ¶meters, &maxPoolingWithMaskOutputLayer); + maxPoolingWithMaskOutputLayer->forward(PASS_GC); + + /* prepare the upsample layer */ + LayerConfig upsampleLayerConfig; + upsampleLayerConfig.set_type("upsample"); + LayerInputConfig* input1 = upsampleLayerConfig.add_inputs(); + upsampleLayerConfig.add_inputs(); + + UpsampleConfig* upsampleConfig = input1->mutable_upsample_conf(); + upsampleConfig->set_scale(2); + ImageConfig* imageConfig = upsampleConfig->mutable_image_conf(); + imageConfig->set_channels(2); + imageConfig->set_img_size(4); + imageConfig->set_img_size_y(4); + upsampleLayerConfig.set_size(2 * 8 * 8); + upsampleLayerConfig.set_name("upsample"); + + for (size_t i = 0; i < 2; i++) { + LayerInputConfig& inputTemp = *(upsampleLayerConfig.mutable_inputs(i)); + inputTemp.set_input_layer_name("MaxPoolWithMask"); + } + + LayerPtr upsampleLayer; + ParameterMap parameterMap; + upsampleLayer = Layer::create(upsampleLayerConfig); + layerMap[upsampleLayerConfig.name()] = upsampleLayer; + upsampleLayer->init(layerMap, parameterMap); + upsampleLayer->setNeedGradient(true); + upsampleLayer->forward(PASS_GC); + upsampleLayer->getOutputGrad()->copyFrom(tempGradData, 128); + upsampleLayer->backward(); + + return upsampleLayer; +} + +TEST(Layer, maxPoolingWithMaskOutputLayerFwd) { + bool useGpu = false; + MatrixPtr inputMat; + MatrixPtr inputGPUMat; + MatrixPtr tempGradMat; + + inputMat = Matrix::create(1, 128, false, useGpu); + inputMat->randomizeUniform(); + + tempGradMat = Matrix::create(1, 128, false, useGpu); + tempGradMat->randomizeUniform(); + real* data = inputMat->getData(); + real* tempGradData = tempGradMat->getData(); + + LayerPtr upsampleLayerCPU = + doOneUpsampleTest(inputMat, "max-pool-with-mask", useGpu, tempGradData); + +#ifdef PADDLE_WITH_CUDA + useGpu = true; + inputGPUMat = Matrix::create(1, 128, false, useGpu); + inputGPUMat->copyFrom(data, 128); + LayerPtr upsampleLayerGPU = doOneUpsampleTest( + inputGPUMat, "max-pool-with-mask", useGpu, tempGradData); + checkMatrixEqual(upsampleLayerCPU->getOutput("").value, + upsampleLayerGPU->getOutput("").value); + + checkMatrixEqual(upsampleLayerCPU->getPrev(0)->getOutputGrad(), + upsampleLayerGPU->getPrev(0)->getOutputGrad()); +#endif +} -- GitLab From 67cbb3e3b6bc5a00b66b3fb1c2de4991ad2e4a21 Mon Sep 17 00:00:00 2001 From: wanghaox Date: Wed, 13 Dec 2017 18:50:03 +0800 Subject: [PATCH 0028/2305] detection map evaluator for SSD --- paddle/operators/detection_map_op.cc | 77 +++++ paddle/operators/detection_map_op.cu | 20 ++ paddle/operators/detection_map_op.h | 316 ++++++++++++++++++ paddle/operators/math/detection_util.cc | 22 ++ paddle/operators/math/detection_util.cu | 23 ++ paddle/operators/math/detection_util.h | 128 +++++++ .../v2/fluid/tests/test_detection_map_op.py | 155 +++++++++ 7 files changed, 741 insertions(+) create mode 100644 paddle/operators/detection_map_op.cc create mode 100644 paddle/operators/detection_map_op.cu create mode 100644 paddle/operators/detection_map_op.h create mode 100644 paddle/operators/math/detection_util.cc create mode 100644 paddle/operators/math/detection_util.cu create mode 100644 paddle/operators/math/detection_util.h create mode 100644 python/paddle/v2/fluid/tests/test_detection_map_op.py diff --git a/paddle/operators/detection_map_op.cc b/paddle/operators/detection_map_op.cc new file mode 100644 index 00000000000..b59d3bfad96 --- /dev/null +++ b/paddle/operators/detection_map_op.cc @@ -0,0 +1,77 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/detection_map_op.h" + +namespace paddle { +namespace operators { + +using Tensor = framework::Tensor; + +class DetectionMAPOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext* ctx) const override { + auto map_dim = framework::make_ddim({1}); + ctx->SetOutputDim("MAP", map_dim); + } + + protected: + framework::OpKernelType GetKernelType( + const framework::ExecutionContext& ctx) const override { + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Label")->type()), + ctx.device_context()); + } +}; + +class DetectionMAPOpMaker : public framework::OpProtoAndCheckerMaker { + public: + DetectionMAPOpMaker(framework::OpProto* proto, + framework::OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("Detect", "The detection output."); + AddInput("Label", "The label data."); + AddOutput("MAP", "The MAP evaluate result of the detection."); + + AddAttr("overlap_threshold", "The overlap threshold.") + .SetDefault(.3f); + AddAttr("evaluate_difficult", + "Switch to control whether the difficult data is evaluated.") + .SetDefault(true); + AddAttr("ap_type", + "The AP algorithm type, 'Integral' or '11point'.") + .SetDefault("Integral"); + + AddComment(R"DOC( +Detection MAP Operator. + +Detection MAP evaluator for SSD(Single Shot MultiBox Detector) algorithm. +Please get more information from the following papers: +https://arxiv.org/abs/1512.02325. + +)DOC"); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP_WITHOUT_GRADIENT(detection_map, ops::DetectionMAPOp, + ops::DetectionMAPOpMaker); +REGISTER_OP_CPU_KERNEL( + detection_map, ops::DetectionMAPOpKernel, + ops::DetectionMAPOpKernel); diff --git a/paddle/operators/detection_map_op.cu b/paddle/operators/detection_map_op.cu new file mode 100644 index 00000000000..ab9a992c363 --- /dev/null +++ b/paddle/operators/detection_map_op.cu @@ -0,0 +1,20 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/detection_map_op.h" + +namespace ops = paddle::operators; +REGISTER_OP_CUDA_KERNEL( + detection_map, ops::DetectionMAPOpKernel, + ops::DetectionMAPOpKernel); diff --git a/paddle/operators/detection_map_op.h b/paddle/operators/detection_map_op.h new file mode 100644 index 00000000000..3e862abda64 --- /dev/null +++ b/paddle/operators/detection_map_op.h @@ -0,0 +1,316 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once +#include "paddle/framework/op_registry.h" +#include "paddle/operators/math/detection_util.h" +#include "paddle/operators/math/math_function.h" + +namespace paddle { +namespace operators { + +template +inline void GetAccumulation(std::vector> in_pairs, + std::vector* accu_vec) { + std::stable_sort(in_pairs.begin(), in_pairs.end(), + math::SortScorePairDescend); + accu_vec->clear(); + size_t sum = 0; + for (size_t i = 0; i < in_pairs.size(); ++i) { + // auto score = in_pairs[i].first; + auto count = in_pairs[i].second; + sum += count; + accu_vec->push_back(sum); + } +} + +template +class DetectionMAPOpKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* input_label = ctx.Input("Label"); + auto* input_detect = ctx.Input("Detect"); + auto* map_out = ctx.Output("MAP"); + + float overlap_threshold = ctx.Attr("overlap_threshold"); + float evaluate_difficult = ctx.Attr("evaluate_difficult"); + std::string ap_type = ctx.Attr("ap_type"); + + auto label_lod = input_label->lod(); + PADDLE_ENFORCE_EQ(label_lod.size(), 1UL, + "Only support one level sequence now."); + auto batch_size = label_lod[0].size() - 1; + + std::vector>>> gt_bboxes; + + std::vector< + std::map>>>> + detect_bboxes; + + if (platform::is_gpu_place(ctx.GetPlace())) { + framework::LoDTensor input_label_cpu; + framework::Tensor input_detect_cpu; + input_label_cpu.set_lod(input_label->lod()); + input_label_cpu.Resize(input_label->dims()); + input_detect_cpu.Resize(input_detect->dims()); + input_label_cpu.mutable_data(platform::CPUPlace()); + input_detect_cpu.mutable_data(platform::CPUPlace()); + framework::CopyFrom(*input_label, platform::CPUPlace(), + ctx.device_context(), &input_label_cpu); + framework::CopyFrom(*input_detect, platform::CPUPlace(), + ctx.device_context(), &input_detect_cpu); + GetBBoxes(input_label_cpu, input_detect_cpu, gt_bboxes, detect_bboxes); + } else { + GetBBoxes(*input_label, *input_detect, gt_bboxes, detect_bboxes); + } + + std::map label_pos_count; + std::map>> true_pos; + std::map>> false_pos; + + CalcTrueAndFalsePositive(batch_size, evaluate_difficult, overlap_threshold, + gt_bboxes, detect_bboxes, label_pos_count, + true_pos, false_pos); + + T map = CalcMAP(ap_type, label_pos_count, true_pos, false_pos); + + T* map_data = nullptr; + framework::Tensor map_cpu; + map_out->mutable_data(ctx.GetPlace()); + if (platform::is_gpu_place(ctx.GetPlace())) { + map_data = map_cpu.mutable_data(map_out->dims(), platform::CPUPlace()); + map_data[0] = map; + framework::CopyFrom(map_cpu, platform::CPUPlace(), ctx.device_context(), + map_out); + } else { + map_data = map_out->mutable_data(ctx.GetPlace()); + map_data[0] = map; + } + } + + protected: + void GetBBoxes( + const framework::LoDTensor& input_label, + const framework::Tensor& input_detect, + std::vector>>>& + gt_bboxes, + std::vector< + std::map>>>>& + detect_bboxes) const { + const T* label_data = input_label.data(); + const T* detect_data = input_detect.data(); + + auto label_lod = input_label.lod(); + auto batch_size = label_lod[0].size() - 1; + auto label_index = label_lod[0]; + + for (size_t n = 0; n < batch_size; ++n) { + std::map>> bboxes; + for (int i = label_index[n]; i < label_index[n + 1]; ++i) { + std::vector> bbox; + math::GetBBoxFromLabelData(label_data + i * 6, 1, bbox); + int label = static_cast(label_data[i * 6]); + bboxes[label].push_back(bbox[0]); + } + gt_bboxes.push_back(bboxes); + } + + size_t n = 0; + size_t detect_box_count = input_detect.dims()[0]; + for (size_t img_id = 0; img_id < batch_size; ++img_id) { + std::map>>> bboxes; + size_t cur_img_id = static_cast((detect_data + n * 7)[0]); + while (cur_img_id == img_id && n < detect_box_count) { + std::vector label; + std::vector score; + std::vector> bbox; + math::GetBBoxFromDetectData(detect_data + n * 7, 1, label, score, + bbox); + bboxes[label[0]].push_back(std::make_pair(score[0], bbox[0])); + ++n; + cur_img_id = static_cast((detect_data + n * 7)[0]); + } + detect_bboxes.push_back(bboxes); + } + } + + void CalcTrueAndFalsePositive( + size_t batch_size, bool evaluate_difficult, float overlap_threshold, + const std::vector>>>& + gt_bboxes, + const std::vector< + std::map>>>>& + detect_bboxes, + std::map& label_pos_count, + std::map>>& true_pos, + std::map>>& false_pos) const { + for (size_t n = 0; n < batch_size; ++n) { + auto image_gt_bboxes = gt_bboxes[n]; + for (auto it = image_gt_bboxes.begin(); it != image_gt_bboxes.end(); + ++it) { + size_t count = 0; + auto labeled_bboxes = it->second; + if (evaluate_difficult) { + count = labeled_bboxes.size(); + } else { + for (size_t i = 0; i < labeled_bboxes.size(); ++i) + if (!(labeled_bboxes[i].is_difficult)) ++count; + } + if (count == 0) { + continue; + } + int label = it->first; + if (label_pos_count.find(label) == label_pos_count.end()) { + label_pos_count[label] = count; + } else { + label_pos_count[label] += count; + } + } + } + + for (size_t n = 0; n < detect_bboxes.size(); ++n) { + auto image_gt_bboxes = gt_bboxes[n]; + auto detections = detect_bboxes[n]; + + if (image_gt_bboxes.size() == 0) { + for (auto it = detections.begin(); it != detections.end(); ++it) { + auto pred_bboxes = it->second; + int label = it->first; + for (size_t i = 0; i < pred_bboxes.size(); ++i) { + auto score = pred_bboxes[i].first; + true_pos[label].push_back(std::make_pair(score, 0)); + false_pos[label].push_back(std::make_pair(score, 1)); + } + } + continue; + } + + for (auto it = detections.begin(); it != detections.end(); ++it) { + int label = it->first; + auto pred_bboxes = it->second; + if (image_gt_bboxes.find(label) == image_gt_bboxes.end()) { + for (size_t i = 0; i < pred_bboxes.size(); ++i) { + auto score = pred_bboxes[i].first; + true_pos[label].push_back(std::make_pair(score, 0)); + false_pos[label].push_back(std::make_pair(score, 1)); + } + continue; + } + + auto matched_bboxes = image_gt_bboxes.find(label)->second; + std::vector visited(matched_bboxes.size(), false); + // Sort detections in descend order based on scores + std::sort(pred_bboxes.begin(), pred_bboxes.end(), + math::SortScorePairDescend>); + for (size_t i = 0; i < pred_bboxes.size(); ++i) { + float max_overlap = -1.0; + size_t max_idx = 0; + auto score = pred_bboxes[i].first; + for (size_t j = 0; j < matched_bboxes.size(); ++j) { + float overlap = + JaccardOverlap(pred_bboxes[i].second, matched_bboxes[j]); + if (overlap > max_overlap) { + max_overlap = overlap; + max_idx = j; + } + } + if (max_overlap > overlap_threshold) { + bool match_evaluate_difficult = + evaluate_difficult || + (!evaluate_difficult && !matched_bboxes[max_idx].is_difficult); + if (match_evaluate_difficult) { + if (!visited[max_idx]) { + true_pos[label].push_back(std::make_pair(score, 1)); + false_pos[label].push_back(std::make_pair(score, 0)); + visited[max_idx] = true; + } else { + true_pos[label].push_back(std::make_pair(score, 0)); + false_pos[label].push_back(std::make_pair(score, 1)); + } + } + } else { + true_pos[label].push_back(std::make_pair(score, 0)); + false_pos[label].push_back(std::make_pair(score, 1)); + } + } + } + } + } + + T CalcMAP( + std::string ap_type, const std::map& label_pos_count, + const std::map>>& true_pos, + const std::map>>& false_pos) const { + T mAP = 0.0; + int count = 0; + for (auto it = label_pos_count.begin(); it != label_pos_count.end(); ++it) { + int label = it->first; + int label_num_pos = it->second; + if (label_num_pos == 0 || true_pos.find(label) == true_pos.end()) + continue; + auto label_true_pos = true_pos.find(label)->second; + auto label_false_pos = false_pos.find(label)->second; + // Compute average precision. + std::vector tp_sum; + GetAccumulation(label_true_pos, &tp_sum); + std::vector fp_sum; + GetAccumulation(label_false_pos, &fp_sum); + std::vector precision, recall; + size_t num = tp_sum.size(); + // Compute Precision. + for (size_t i = 0; i < num; ++i) { + // CHECK_LE(tpCumSum[i], labelNumPos); + precision.push_back(static_cast(tp_sum[i]) / + static_cast(tp_sum[i] + fp_sum[i])); + recall.push_back(static_cast(tp_sum[i]) / label_num_pos); + } + // VOC2007 style + if (ap_type == "11point") { + std::vector max_precisions(11, 0.0); + int start_idx = num - 1; + for (int j = 10; j >= 0; --j) + for (int i = start_idx; i >= 0; --i) { + if (recall[i] < j / 10.) { + start_idx = i; + if (j > 0) max_precisions[j - 1] = max_precisions[j]; + break; + } else { + if (max_precisions[j] < precision[i]) + max_precisions[j] = precision[i]; + } + } + for (int j = 10; j >= 0; --j) mAP += max_precisions[j] / 11; + ++count; + } else if (ap_type == "Integral") { + // Nature integral + float average_precisions = 0.; + float prev_recall = 0.; + for (size_t i = 0; i < num; ++i) { + if (fabs(recall[i] - prev_recall) > 1e-6) + average_precisions += precision[i] * fabs(recall[i] - prev_recall); + prev_recall = recall[i]; + } + mAP += average_precisions; + ++count; + } else { + LOG(FATAL) << "Unkown ap version: " << ap_type; + } + } + if (count != 0) mAP /= count; + return mAP * 100; + } +}; // namespace operators + +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/math/detection_util.cc b/paddle/operators/math/detection_util.cc new file mode 100644 index 00000000000..4131a0cb0ef --- /dev/null +++ b/paddle/operators/math/detection_util.cc @@ -0,0 +1,22 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/math/detection_util.h" +#include "paddle/operators/math/math_function.h" + +namespace paddle { +namespace operators { +namespace math {} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/math/detection_util.cu b/paddle/operators/math/detection_util.cu new file mode 100644 index 00000000000..d2bb9923961 --- /dev/null +++ b/paddle/operators/math/detection_util.cu @@ -0,0 +1,23 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/math/detection_util.h" +#include "paddle/operators/math/math_function.h" +#include "paddle/platform/cuda_helper.h" + +namespace paddle { +namespace operators { +namespace math {} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/math/detection_util.h b/paddle/operators/math/detection_util.h new file mode 100644 index 00000000000..2a4dadc545e --- /dev/null +++ b/paddle/operators/math/detection_util.h @@ -0,0 +1,128 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ +#pragma once +#include "paddle/framework/selected_rows.h" +#include "paddle/platform/device_context.h" + +namespace paddle { +namespace operators { +namespace math { + +template +struct BBox { + BBox(T x_min, T y_min, T x_max, T y_max) + : x_min(x_min), + y_min(y_min), + x_max(x_max), + y_max(y_max), + is_difficult(false) {} + + BBox() {} + + T get_width() const { return x_max - x_min; } + + T get_height() const { return y_max - y_min; } + + T get_center_x() const { return (x_min + x_max) / 2; } + + T get_center_y() const { return (y_min + y_max) / 2; } + + T get_area() const { return get_width() * get_height(); } + + // coordinate of bounding box + T x_min; + T y_min; + T x_max; + T y_max; + // whether difficult object (e.g. object with heavy occlusion is difficult) + bool is_difficult; +}; + +template +void GetBBoxFromDetectData(const T* detect_data, const size_t num_bboxes, + std::vector& labels, std::vector& scores, + std::vector>& bboxes) { + size_t out_offset = bboxes.size(); + labels.resize(out_offset + num_bboxes); + scores.resize(out_offset + num_bboxes); + bboxes.resize(out_offset + num_bboxes); + for (size_t i = 0; i < num_bboxes; ++i) { + labels[out_offset + i] = *(detect_data + i * 7 + 1); + scores[out_offset + i] = *(detect_data + i * 7 + 2); + BBox bbox; + bbox.x_min = *(detect_data + i * 7 + 3); + bbox.y_min = *(detect_data + i * 7 + 4); + bbox.x_max = *(detect_data + i * 7 + 5); + bbox.y_max = *(detect_data + i * 7 + 6); + bboxes[out_offset + i] = bbox; + }; +} + +template +void GetBBoxFromLabelData(const T* label_data, const size_t num_bboxes, + std::vector>& bboxes) { + size_t out_offset = bboxes.size(); + bboxes.resize(bboxes.size() + num_bboxes); + for (size_t i = 0; i < num_bboxes; ++i) { + BBox bbox; + bbox.x_min = *(label_data + i * 6 + 1); + bbox.y_min = *(label_data + i * 6 + 2); + bbox.x_max = *(label_data + i * 6 + 3); + bbox.y_max = *(label_data + i * 6 + 4); + T is_difficult = *(label_data + i * 6 + 5); + if (std::abs(is_difficult - 0.0) < 1e-6) + bbox.is_difficult = false; + else + bbox.is_difficult = true; + bboxes[out_offset + i] = bbox; + } +} + +template +inline float JaccardOverlap(const BBox& bbox1, const BBox& bbox2) { + if (bbox2.x_min > bbox1.x_max || bbox2.x_max < bbox1.x_min || + bbox2.y_min > bbox1.y_max || bbox2.y_max < bbox1.y_min) { + return 0.0; + } else { + float inter_x_min = std::max(bbox1.x_min, bbox2.x_min); + float inter_y_min = std::max(bbox1.y_min, bbox2.y_min); + float inter_x_max = std::min(bbox1.x_max, bbox2.x_max); + float inter_y_max = std::min(bbox1.y_max, bbox2.y_max); + + float inter_width = inter_x_max - inter_x_min; + float inter_height = inter_y_max - inter_y_min; + float inter_area = inter_width * inter_height; + + float bbox_area1 = bbox1.get_area(); + float bbox_area2 = bbox2.get_area(); + + return inter_area / (bbox_area1 + bbox_area2 - inter_area); + } +} + +template +bool SortScorePairDescend(const std::pair& pair1, + const std::pair& pair2) { + return pair1.first > pair2.first; +} + +// template <> +// bool SortScorePairDescend(const std::pair& pair1, +// const std::pair& pair2) { +// return pair1.first > pair2.first; +// } + +} // namespace math +} // namespace operators +} // namespace paddle diff --git a/python/paddle/v2/fluid/tests/test_detection_map_op.py b/python/paddle/v2/fluid/tests/test_detection_map_op.py new file mode 100644 index 00000000000..50ce3afbb95 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_detection_map_op.py @@ -0,0 +1,155 @@ +import unittest +import numpy as np +import sys +import collections +import math +from op_test import OpTest + + +class TestDetectionMAPOp(OpTest): + def set_data(self): + self.init_test_case() + + self.mAP = [self.calc_map(self.tf_pos)] + self.label = np.array(self.label).astype('float32') + self.detect = np.array(self.detect).astype('float32') + self.mAP = np.array(self.mAP).astype('float32') + + self.inputs = { + 'Label': (self.label, self.label_lod), + 'Detect': self.detect + } + + self.attrs = { + 'overlap_threshold': self.overlap_threshold, + 'evaluate_difficult': self.evaluate_difficult, + 'ap_type': self.ap_type + } + + self.outputs = {'MAP': self.mAP} + + def init_test_case(self): + self.overlap_threshold = 0.3 + self.evaluate_difficult = True + self.ap_type = "Integral" + + self.label_lod = [[0, 2, 4]] + # label xmin ymin xmax ymax difficult + self.label = [[1, 0.1, 0.1, 0.3, 0.3, 0], [1, 0.6, 0.6, 0.8, 0.8, 1], + [2, 0.3, 0.3, 0.6, 0.5, 0], [1, 0.7, 0.1, 0.9, 0.3, 0]] + + # image_id label score xmin ymin xmax ymax difficult + self.detect = [ + [0, 1, 0.3, 0.1, 0.0, 0.4, 0.3], [0, 1, 0.7, 0.0, 0.1, 0.2, 0.3], + [0, 1, 0.9, 0.7, 0.6, 0.8, 0.8], [1, 2, 0.8, 0.2, 0.1, 0.4, 0.4], + [1, 2, 0.1, 0.4, 0.3, 0.7, 0.5], [1, 1, 0.2, 0.8, 0.1, 1.0, 0.3], + [1, 3, 0.2, 0.8, 0.1, 1.0, 0.3] + ] + + # image_id label score false_pos false_pos + # [-1, 1, 3, -1, -1], + # [-1, 2, 1, -1, -1] + self.tf_pos = [[0, 1, 0.9, 1, 0], [0, 1, 0.7, 1, 0], [0, 1, 0.3, 0, 1], + [1, 1, 0.2, 1, 0], [1, 2, 0.8, 0, 1], [1, 2, 0.1, 1, 0], + [1, 3, 0.2, 0, 1]] + + def calc_map(self, tf_pos): + mAP = 0.0 + count = 0 + + class_pos_count = {} + true_pos = {} + false_pos = {} + + def get_accumulation(pos_list): + sorted_list = sorted(pos_list, key=lambda pos: pos[0], reverse=True) + sum = 0 + accu_list = [] + for (score, count) in sorted_list: + sum += count + accu_list.append(sum) + return accu_list + + label_count = collections.Counter() + for (label, xmin, ymin, xmax, ymax, difficult) in self.label: + if self.evaluate_difficult: + label_count[label] += 1 + elif not difficult: + label_count[label] += 1 + + true_pos = collections.defaultdict(list) + false_pos = collections.defaultdict(list) + for (image_id, label, score, tp, fp) in tf_pos: + true_pos[label].append([score, tp]) + false_pos[label].append([score, fp]) + + for (label, label_pos_num) in label_count.items(): + if label_pos_num == 0 or label not in true_pos: + continue + + label_true_pos = true_pos[label] + label_false_pos = false_pos[label] + + accu_tp_sum = get_accumulation(label_true_pos) + accu_fp_sum = get_accumulation(label_false_pos) + + precision = [] + recall = [] + + for i in range(len(accu_tp_sum)): + precision.append( + float(accu_tp_sum[i]) / + float(accu_tp_sum[i] + accu_fp_sum[i])) + recall.append(float(accu_tp_sum[i]) / label_pos_num) + + if self.ap_type == "11point": + max_precisions = [11.0, 0.0] + start_idx = len(accu_tp_sum) - 1 + for j in range(10, 0, -1): + for i in range(start_idx, 0, -1): + if recall[i] < j / 10.0: + start_idx = i + if j > 0: + max_precisions[j - 1] = max_precisions[j] + break + else: + if max_precisions[j] < accu_precision[i]: + max_precisions[j] = accu_precision[i] + for j in range(10, 0, -1): + mAP += max_precisions[j] / 11 + count += 1 + elif self.ap_type == "Integral": + average_precisions = 0.0 + prev_recall = 0.0 + for i in range(len(accu_tp_sum)): + if math.fabs(recall[i] - prev_recall) > 1e-6: + average_precisions += precision[i] * \ + math.fabs(recall[i] - prev_recall) + prev_recall = recall[i] + + mAP += average_precisions + count += 1 + + if count != 0: mAP /= count + return mAP * 100.0 + + def setUp(self): + self.op_type = "detection_map" + self.set_data() + + def test_check_output(self): + self.check_output() + + +class TestDetectionMAPOpSkipDiff(TestDetectionMAPOp): + def init_test_case(self): + super(TestDetectionMAPOpSkipDiff, self).init_test_case() + + self.evaluate_difficult = False + + self.tf_pos = [[0, 1, 0.7, 1, 0], [0, 1, 0.3, 0, 1], [1, 1, 0.2, 1, 0], + [1, 2, 0.8, 0, 1], [1, 2, 0.1, 1, 0], [1, 3, 0.2, 0, 1]] + + +if __name__ == '__main__': + unittest.main() -- GitLab From a60b3a5d557ca5e040e8f360709d0136fd6d1645 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Thu, 14 Dec 2017 11:47:32 +0800 Subject: [PATCH 0029/2305] fix doc of seq_expand_op --- paddle/operators/seq_expand_op.cc | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/paddle/operators/seq_expand_op.cc b/paddle/operators/seq_expand_op.cc index ede97546974..8051ddd702f 100644 --- a/paddle/operators/seq_expand_op.cc +++ b/paddle/operators/seq_expand_op.cc @@ -59,7 +59,7 @@ This operator expands input(X) according to LOD of input(Y). Following are cases to better explain how this works: Case 1: -Given 2-level a LoDTensor input(X) +Given a 2-level LoDTensor input(X) X.lod = [[0, 2, 3], [0, 1, 3, 4]] X.data = [a, b, c, d] @@ -76,9 +76,8 @@ then we get 2-level LoDTensor Case 2: -Given a 0-level LoDTensor input(X) +Given a common Tensor input(X) X.data = [a, b, c] - X.lod = NULL X.dims = [3, 1] and input(Y) Y.lod = [[0, 2, 3, 6]] @@ -90,9 +89,8 @@ then we get 1-level LoDTensor Case 3: -Given a 0-level LoDTensor input(X) +Given a common Tensor input(X) X.data = [[a, b], [c, d], [e, f]] - X.lod = NULL X.dims = [3, 2] and input(Y) Y.lod = [[0, 2, 3, 6]] -- GitLab From 635a69ba4be15c924541f8d222ba1183b793e4c2 Mon Sep 17 00:00:00 2001 From: ying Date: Wed, 6 Dec 2017 17:50:38 +0800 Subject: [PATCH 0030/2305] add doc for how to use C-API. --- doc/howto/index_cn.rst | 1 + doc/howto/usage/capi/a_simple_example.md | 211 ++++++++++++++++++ doc/howto/usage/capi/compile_paddle_lib.md | 68 ++++++ doc/howto/usage/capi/core_concepts.md | 0 doc/howto/usage/capi/images/csr.png | Bin 0 -> 174346 bytes .../usage/capi/organization_of_the_inputs.md | 154 +++++++++++++ doc/howto/usage/capi/overview.md | 5 + .../examples/model_inference/dense/main.c | 60 +++-- .../model_inference/dense/merge_v2_model.py | 8 + .../model_inference/dense/mnist_v2.py | 117 ++++++++++ .../model_inference/sparse_binary/main.c | 11 +- python/paddle/utils/dump_v2_config.py | 62 +++++ python/paddle/utils/merge_model.py | 3 +- 13 files changed, 677 insertions(+), 23 deletions(-) create mode 100644 doc/howto/usage/capi/a_simple_example.md create mode 100644 doc/howto/usage/capi/compile_paddle_lib.md create mode 100644 doc/howto/usage/capi/core_concepts.md create mode 100644 doc/howto/usage/capi/images/csr.png create mode 100644 doc/howto/usage/capi/organization_of_the_inputs.md create mode 100644 doc/howto/usage/capi/overview.md create mode 100644 paddle/capi/examples/model_inference/dense/merge_v2_model.py create mode 100644 paddle/capi/examples/model_inference/dense/mnist_v2.py create mode 100644 python/paddle/utils/dump_v2_config.py diff --git a/doc/howto/index_cn.rst b/doc/howto/index_cn.rst index 991b9e2596a..174fb7a0744 100644 --- a/doc/howto/index_cn.rst +++ b/doc/howto/index_cn.rst @@ -12,6 +12,7 @@ usage/k8s/k8s_basis_cn.md usage/k8s/k8s_cn.md usage/k8s/k8s_distributed_cn.md + usage/capi/overview.md 开发标准 -------- diff --git a/doc/howto/usage/capi/a_simple_example.md b/doc/howto/usage/capi/a_simple_example.md new file mode 100644 index 00000000000..efcae3518ee --- /dev/null +++ b/doc/howto/usage/capi/a_simple_example.md @@ -0,0 +1,211 @@ +## 使用 C-API 开发预测程序 + +这篇文档通过一个最简单的例子:手写数字识别,来介绍 CPU 下单线程使用 PaddlePaddle C-API 开发预测服务,完整代码见[此目录](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/dense/)。 + +### 使用流程 + +使用 C-API 分为:准备工作和预测程序开发两部分。 +- 准备 + 1. 将神经网络模型结构进行序列化。 + - 调用C-API预测时,需要提供序列化之后的网络结构和训练好的模型参数文件。 + 1. 将PaddlePaddle训练出的模型参数文件(多个)合并成一个文件。 + - 神经网络模型结构和训练好的模型将被序列化合并入一个文件。 + - 预测时只需加载这一个文件,便于发布。 + - **注意**:以上两种方式只需选择其一即可。 +- 调用 PaddlePaddle C-API 开发预测序 + 1. 初始化PaddlePaddle运行环境。 + 1. 创建神经网络的输入,组织输入数据。 + 1. 加载模型。 + 1. 进行前向计算,获得计算结果。 + 1. 清理。 + + +这里我们以手写数字识别任务为例,介绍如何使用 C-API 进行预测,完整代码请查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)。 + +运行目录下的 `python mnist_v2.py` 可以使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。脚本中的模型定义了一个简单的含有[两个隐层的全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。训练好的模型默认保存在当前运行目录下的`models`目录中。下面,我们将调用 C-API 加载训练好的模型进行预测。 + + +### 外部准备 + +1. 序列化神经网络模型配置 + + PaddlePaddle 使用 protobuf 来传输网络配置文件中定义的网络结构和相关参数,在使用 C-API 进行预测时,也需将网络结构使用 protobuf 进行序列化,写入文件中。 + + 调用`paddle.utils.dump_v2_config`中的`dump_v2_config`函数能够将使用 PaddlePaddle V2 API 定义的神经网络结构 dump 到指定文件中。示例代码如下: + + ```python + from paddle.utils.dump_v2_config import dump_v2_config + from mnist_v2 import network + + predict = network(is_infer=True) + dump_v2_config(predict, "trainer_config.bin", True) + ``` + + 对本例,或运行 `python mnist_v2.py --task dump_config`,会对示例中的网络结构进行序列化,并将结果写入当前目录下的`trainer_config.bin`文件中。 + + 当选择使用这种方式调用 C-API 时,如果神经网络有多个可学习参数,请将它们全部放在同一文件夹内,C-API会从指定的目录寻找并加载训练好的模型。 + +2. 合并模型文件(可选) + + 一些情况下为了便于发布,希望能够将序列化后的神经网络结构和训练好的模型参数打包进一个文件,这时可以使用`paddle.utils.merge_model`中的`merge_v2_model`接口对神经网络结构和训练好的参数进行序列化,将序列化结果写入一个文件内,调用C-API时直接只需加载这一个文件。 + + 代码示例如下: + + ```python + from paddle.utils.merge_model import merge_v2_model + + from mnist_v2 import network + + net = network(is_infer=True) + param_file = "models/params_pass_4.tar" + output_file = "output.paddle.model" + merge_v2_model(net, param_file, output_file) + ``` + 对本例,或者直接运行 `python merge_v2_model.py`,序列化结果将会写入当前目录下的`output.paddle.model`文件中,该文件在调用C-API时,可被直接加载。 + +#### 注意事项 +1. C-API 需要序列化之后神经网络结构,在调用`dump_v2_config`时,参数`binary`必须指定为`True`。 +1. **预测使用的网络结构往往不同于训练**,通常需要去掉网络中的:(1)类别标签层;(2)损失函数层;(3)`evaluator`等,只留下核心计算层,请注意是否需要修改网络结构。 +1. 预测时,可以获取网络中定义的任意多个(大于等于一个)层前向计算的结果,需要哪些层的计算结果作为输出,就将这些层加入一个Python list中,作为调用`dump_v2_config`的第一个参数。 + +### 编写预测代码 + +#### step 1. 初始化及加载模型 + +1. 初始化PaddlePaddle运行环境。 + ```c + // Initalize the PaddlePaddle runtime environment. + char* argv[] = {"--use_gpu=False"}; + CHECK(paddle_init(1, (char**)argv)); + ``` + +1. 加载训练好的模型。 + + 这里需要介绍C-API使用中的一个重要概念:Gradient Machine。概念上,在 PaddlePaddle 内部,一个GradientMachine类的对象管理着一组计算层(PaddlePaddle Layers)来完成前向和反向计算,并处理与之相关的所有细节。特别的,在调用C-API预测时只需进行前向计算。这篇文档的之后部分我们会使用`gradient machine`来特指调用PaddlePaddle C-API创建的GradientMachine类的对象。 + + 每一个 `gradient machine` 都会管理维护一份训练好的模型,模型可以通过以下两种方式获取: + 1. 从磁盘加载;这时`gradient machine`会独立拥有一份训练好的模型; + 1. 共享自其它`gradient machine`的模型;这种情况多出现在使用多线程预测时; + + 下面的代码片段创建 `gradient machine`,并从指定路径加载训练好的模型。 + + ```c + // Read the binary configuration file generated by `convert_protobin.sh` + long size; + void* buf = read_config(CONFIG_BIN, &size); + + // Create the gradient machine for inference. + paddle_gradient_machine machine; + CHECK(paddle_gradient_machine_create_for_inference(&machine, buf, (int)size)); + + // Load the trained model. Modify the parameter MODEL_PATH to set the correct + // path of the trained model. + CHECK(paddle_gradient_machine_load_parameter_from_disk(machine, MODEL_PATH)); + ``` + +##### 注意事项 +1. 以上代码片段使用“仅序列化神经网络结构”的方式加载模型,需要同时指定模型参数存储的路径。 + - 使用PaddlePaddle V2 API训练,模型中所有可学习参数会被存为一个压缩文件,需要手动进行解压,将它们放在同一目录中,C-API不会直接加载 V2 API 存储的压缩文件。 +1. 如果使用`merge model`方式将神经网络结构和训练好的参数序列化到一个文件,请参考此[示例](https://github.com/PaddlePaddle/Mobile/blob/develop/Demo/linux/paddle_image_recognizer.cpp#L59)。 + +#### step 2. 创建神经网络输入,组织输入数据 + +基本使用概念: +- 在PaddlePaddle内部,神经网络中一个计算层的输入/输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输入,每一个输入/输入都会对应有自己的`Argument`。 +- `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 +- 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 + +*注:这篇文档使用的示例任务手写数字识别不涉及一维整型序列输入/输出,因此不讨论一维整型输入/输出数据相关的内容。更多信息请参考:[输入数据组织](organization_of_the_inputs.md)。* + +在这篇文档的后面部分,我们会使用`argument`来**特指** PaddlePaddle C-API中神经网的一个输入/输出,使用`matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象,用`ivector`特指`argument`中用于存储数据的`IVector`类的对象。 + +于是,在组织神经网络输入,获取输出时,需要思考完成以下工作: +1. 为每一个输入/输出创建`argument`; +1. 为每一个`argument`创建`matrix`或者`ivector`来存储数据; + +与输入不同的是,输出`argument`的`matrix`变量并不需在使用C-API时为之要分配存储空间。PaddlePaddle内部,神经网络进行前向计算时会自己分配/管理每个计算层的存储空间;这些细节C-API会代为处理,只需在概念上理解,并按照约定调用相关的 C-API 接口即可。 + +下面是示例代码片段。在这段代码中,生成了一条随机输入数据作为测试样本。 +```c +// Inputs and outputs of the network are organized as paddle_arguments object +// in C-API. In the comments below, "argument" specifically means one input of +// the neural network in PaddlePaddle C-API. +paddle_arguments in_args = paddle_arguments_create_none(); + +// There is only one data layer in this demo MNIST network, invoke this +// function to create one argument. +CHECK(paddle_arguments_resize(in_args, 1)); + +// Each argument needs one matrix or one ivector (integer vector, for sparse +// index input, usually used in NLP task) to holds the real input data. +// In the comments below, "matrix" specifically means the object needed by +// argument to hold the data. Here we create the matrix for the above created +// agument to store the testing samples. +paddle_matrix mat = + paddle_matrix_create(/* height = batch size */ 1, + /* width = dimensionality of the data layer */ 784, + /* whether to use GPU */ false); + +paddle_real* array; +// Get the pointer pointing to the start address of the first row of the +// created matrix. +CHECK(paddle_matrix_get_row(mat, 0, &array)); + +// Fill the matrix with a randomly generated test sample. +srand(time(0)); +for (int i = 0; i < 784; ++i) { + array[i] = rand() / ((float)RAND_MAX); +} + +// Assign the matrix to the argument. +CHECK(paddle_arguments_set_value(in_args, 0, mat)); +``` + +#### step 3. 前向计算 + +完成上述准备之后,通过调用 `paddle_gradient_machine_forward` 接口完成神经网络的前向计算。 +示例代码片段如下: + +```c +// Create the output argument. +paddle_arguments out_args = paddle_arguments_create_none(); + +// Invoke the forward computation. +CHECK(paddle_gradient_machine_forward(machine, + in_args, + out_args, + /* is train taks or not */ false)); + +// Create the matrix to hold the forward result of the neural network. +paddle_matrix prob = paddle_matrix_create_none(); +// Access the matrix of the output argument, the predicted result is stored in +// which. +CHECK(paddle_arguments_get_value(out_args, 0, prob)); + +uint64_t height; +uint64_t width; +CHECK(paddle_matrix_get_shape(prob, &height, &width)); +CHECK(paddle_matrix_get_row(prob, 0, &array)); + +printf("Prob: \n"); +for (int i = 0; i < height * width; ++i) { + printf("%.4f ", array[i]); + if ((i + 1) % width == 0) { + printf("\n"); + } +} +printf("\n"); +``` + +#### step 4. 清理 + +结束预测之后,对使用的中间变量和资源进行清理和释放: + +```c +// The cleaning up. +CHECK(paddle_matrix_destroy(prob)); +CHECK(paddle_arguments_destroy(out_args)); +CHECK(paddle_matrix_destroy(mat)); +CHECK(paddle_arguments_destroy(in_args)); +CHECK(paddle_gradient_machine_destroy(machine)); +``` diff --git a/doc/howto/usage/capi/compile_paddle_lib.md b/doc/howto/usage/capi/compile_paddle_lib.md new file mode 100644 index 00000000000..1ad5b906812 --- /dev/null +++ b/doc/howto/usage/capi/compile_paddle_lib.md @@ -0,0 +1,68 @@ +## 编译 PaddlePaddle 链接库 + +使用 C-API 进行预测依赖于将 PaddlePaddle 核心代码编译成链接库,只需在编译时指定编译选项:`-DWITH_C_API=ON`。同时,**建议将:`DWITH_PYTHON`,`DWITH_SWIG_PY`,`DWITH_GOLANG`,均设置为`OFF`**,以避免链接不必要的库。其它编译选项按需进行设定。 + +```shell +INSTALL_PREFIX=/path/of/capi/ +PADDLE_ROOT=/path/of/paddle_source/ +cmake $PADDLE_ROOT -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \ + -DCMAKE_BUILD_TYPE=Release \ + -DWITH_C_API=ON \ + -DWITH_SWIG_PY=OFF \ + -DWITH_GOLANG=OFF \ + -DWITH_PYTHON=OFF \ + -DWITH_MKLML=OFF \ + -DWITH_MKLDNN=OFF \ + -DWITH_GPU=OFF \ + ... +``` +在上面的代码片段中,`PADDLE_ROOT` 表示 PaddlePaddle 源码所在目录,生成Makefile文件后执行:`make && make install`。成功执行后,使用CAPI所需的依赖(包括:(1)编译出的PaddlePaddle 链接和头文件;(2)第三方链接库和头文件)均会存放于`INSTALL_PREFIX`目录中。 + +编译成功后在 `INSTALL_PREFIX` 下会看到如下目录结构(包括了编译出的PaddlePaddle头文件和链接库,以及第三方依赖链接库和头文件(如果需要,由链接方式决定)): + +```text +├── include +│   └── paddle +│   ├── arguments.h +│   ├── capi.h +│   ├── capi_private.h +│   ├── config.h +│   ├── error.h +│   ├── gradient_machine.h +│   ├── main.h +│   ├── matrix.h +│   ├── paddle_capi.map +│   └── vector.h +├── lib +│   ├── libpaddle_capi_engine.a +│   ├── libpaddle_capi_layers.a +│   ├── libpaddle_capi_shared.dylib +│   └── libpaddle_capi_whole.a +└── third_party + ├── ...... +``` + +## 链接方式说明 + +目前提供三种链接方式: + +1. 链接`libpaddle_capi_shared.so` 动态库 + - 使用 PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_shared.so`时,需注意: + 1. 如果编译时指定编译CPU版本,且使用`OpenBLAS`矩阵库,在使用CAPI开发预测程序时,只需要链接`libpaddle_capi_shared.so`这一个库。 + 1. 如果是用编译时指定CPU版本,且使用`MKL`矩阵库,由于`MKL`库有自己独立的动态库文件,在使用PaddlePaddle CAPI开发预测程序时,需要自己链接MKL链接库。 + 1. 如果编译时指定编译GPU版本,CUDA相关库会在预测程序运行时动态装载,需要将CUDA相关的库设置到`LD_LIBRARY_PATH`环境变量中。 + - 这种方式最为简便,链接相对容易,**在无特殊需求情况下,推荐使用此方式**。 + +2. 链接静态库 `libpaddle_capi_whole.a` + - 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意: + 1. 需要指定`-Wl,--whole-archive`链接选项。 + 1. 需要显式地链接 `gflags`、`glog`、`libz`、`protobuf` 等第三方库,可在`INSTALL_PREFIX\third_party`下找到。 + 1. 如果在编译 C-API 时使用OpenBLAS矩阵库,需要显示地链接`libopenblas.a`。 + 1. 如果在编译 C-API 是使用 MKL 矩阵库,需要显示地链接 MKL 的动态库。 + +3. 链接静态库 `libpaddle_capi_layers.a`和`libpaddle_capi_engine.a` + - 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意: + 1. 这种链接方式主要用于移动端预测。 + 1. 为了减少生成链接库的大小把`libpaddle_capi_whole.a`拆成以上两个静态链接库。 + 1. 需指定`-Wl,--whole-archive -lpaddle_capi_layers` 和 `-Wl,--no-whole-archive -lpaddle_capi_engine` 进行链接。 + 1. 第三方依赖库需要按照与方式2同样方法显示地进行链接。 diff --git a/doc/howto/usage/capi/core_concepts.md b/doc/howto/usage/capi/core_concepts.md new file mode 100644 index 00000000000..e69de29bb2d diff --git a/doc/howto/usage/capi/images/csr.png b/doc/howto/usage/capi/images/csr.png new file mode 100644 index 0000000000000000000000000000000000000000..16454e9261872e9ab337f2d95d1143b77b554ecb GIT binary patch literal 174346 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 z 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

g~%JBrZuR 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

q+i})N767L3xx;ldYfnTtiUYQ3S?K>XW^< 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 mLfkl +
图1. CSR存储示意图. +

+ + 在PaddlePaddle C-API中通过以下接口创建稀疏矩阵: + ```cpp + PD_API paddle_matrix paddle_matrix_create_sparse( + uint64_t height, uint64_t width, uint64_t nnz, bool isBinary, bool useGpu); + ``` + 1. 创建稀疏矩阵时需要显示地指定矩阵的(1)高度(`height`,在神经网络中等于一次预测处理的样本数)(2)宽度(`width`,`paddle.layer.data`的`size`)以及(3)非零元个数(`nnz`)。 + 1. 当上述接口第4个参数`isBinary`指定为`true`时,**只需要设置行偏移(`row_offset`)和列号(`colum indices`),不需要提供元素值(`values`)**,这时行偏移和列号指定的元素默认其值为1。 + + - 下面的代码片段创建了一个CPU上的二值稀疏矩阵: + + ```cpp + paddle_matrix mat = paddle_matrix_create_sparse(1, layer_size, nnz, true, false); + int colIndices[] = {9, 93, 109}; // layer_size here is greater than 109. + int rowOffset[] = {0, sizeof(colIndices) / sizeof(int)}; + + CHECK(paddle_matrix_sparse_copy_from(mat, + rowOffset, + sizeof(rowOffset) / sizeof(int), + colIndices, + sizeof(colIndices) / sizeof(int), + NULL /*values array is NULL.*/, + 0 /*size of the value arrary is 0.*/)); + CHECK(paddle_arguments_set_value(in_args, 0, mat)); + ``` + - 下面的代码片段在创建了一个CPU上的带元素值的稀疏矩阵: + ```cpp + paddle_matrix mat = paddle_matrix_create_sparse(1, layer_size, nnz, false, false); + int colIndices[] = {9, 93, 109}; // layer_size here is greater than 109. + int rowOffset[] = {0, sizeof(colIndices) / sizeof(int)}; + float values[] = {0.5, 0.5, 0.5}; + + CHECK(paddle_matrix_sparse_copy_from(mat, + rowOffset, + sizeof(rowOffset) / sizeof(int), + colIndices, + sizeof(colIndices) / sizeof(int), + values, + sizeof(values) / sizeof(float))); + ``` + +### 组织序列数据 + + + +### Python 端数据类型说明 + +下表列出了Python端训练接口暴露的数据类型(`paddle.layer.data`函数`type`字段的取值)对应于调用C-API时需要创建的数据类型: + + +Python 端数据类型 | C-API 输入数据类型| +:-------------: | :-------------: +`paddle.data_type.integer_value` |一维整型数组,无需附加序列信息| +`paddle.data_type.dense_vector` |二维浮点型稠密矩阵,无需附加序列信息| +`paddle.data_type.sparse_binary_vector` |二维浮点型稀疏矩阵,无需提供非零元的值,默认为1,无需附加序列信息| +`paddle.data_type.sparse_vector` |二维浮点型稀疏矩阵,需提供非零元的值,无需附加序列信息| +`paddle.data_type.integer_value_sequence` |一维整型数组,需附加序列信息| +`paddle.data_type.dense_vector_sequence` |二维浮点型稠密矩阵,需附加序列信息| +`paddle.data_type.sparse_binary_vector_sequence` |二维浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加序列信息| +`paddle.data_type.sparse_vector_sequence` |二维浮点型稀疏矩阵,需提供非零元的值,需附加序列信息| +`paddle.data_type.integer_value_sub_sequence` |一维整型数组,需附加双层序列信息| +`paddle.data_type.dense_vector_sub_sequence` |二维浮点型稠密矩阵,需附加双层序列信息| +`paddle.data_type.sparse_binary_vector_sub_sequence` |二维浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加双层序列信息| +`paddle.data_type.sparse_vector_sub_sequence` |二维浮点型稀疏矩阵,需提供非零元的值,需附加双层序列信息| diff --git a/doc/howto/usage/capi/overview.md b/doc/howto/usage/capi/overview.md new file mode 100644 index 00000000000..5ec3bd02849 --- /dev/null +++ b/doc/howto/usage/capi/overview.md @@ -0,0 +1,5 @@ +- [编译 PaddlePaddle 链接库](compile_paddle_lib.md) +- [C-API 使用示例](a_simple_example.md) +- [输入数据组织](organize_input_data.md) +- [核心概念介绍](core_concepts.md) +- [F&Q]() diff --git a/paddle/capi/examples/model_inference/dense/main.c b/paddle/capi/examples/model_inference/dense/main.c index 5eeaf7e31fa..376cd46fb09 100644 --- a/paddle/capi/examples/model_inference/dense/main.c +++ b/paddle/capi/examples/model_inference/dense/main.c @@ -3,59 +3,82 @@ #include "../common/common.h" +// Modify this path as needed. #define CONFIG_BIN "./trainer_config.bin" +// Modify this path as needed. +// This demo assumes that merged model is not used, then this path is the +// directory storing all the trained parameters. +// If the model is trained by PaddlePaddle V2 API, the model is saved as +// a compressed file. You need to uncompress the compressed file first. +#define MODEL_PATH "models/pass_4" int main() { - // Initalize Paddle + // Initalize the PaddlePaddle runtime environment. char* argv[] = {"--use_gpu=False"}; CHECK(paddle_init(1, (char**)argv)); - // Reading config binary file. It is generated by `convert_protobin.sh` + // Read the binary configuration file generated by `convert_protobin.sh` long size; void* buf = read_config(CONFIG_BIN, &size); - // Create a gradient machine for inference. + // Create the gradient machine for inference. paddle_gradient_machine machine; CHECK(paddle_gradient_machine_create_for_inference(&machine, buf, (int)size)); - CHECK(paddle_gradient_machine_randomize_param(machine)); - // Loading parameter. Uncomment the following line and change the directory. - // CHECK(paddle_gradient_machine_load_parameter_from_disk(machine, - // "./some_where_to_params")); + // Load the trained model. Modify the parameter MODEL_PATH to set the correct + // path of the trained model. + CHECK(paddle_gradient_machine_load_parameter_from_disk(machine, MODEL_PATH)); + + // Inputs and outputs of the network are organized as paddle_arguments object + // in C-API. In the comments below, "argument" specifically means one input of + // the neural network in PaddlePaddle C-API. paddle_arguments in_args = paddle_arguments_create_none(); - // There is only one input of this network. + // There is only one data layer in this demo MNIST network, invoke this + // function to create one argument. CHECK(paddle_arguments_resize(in_args, 1)); - // Create input matrix. - paddle_matrix mat = paddle_matrix_create(/* sample_num */ 1, - /* size */ 784, - /* useGPU */ false); - srand(time(0)); + // Each argument needs one matrix or one ivector (integer vector, for sparse + // index input, usually used in NLP task) to holds the real input data. + // In the comments below, "matrix" specifically means the object needed by + // argument to hold the data. Here we create the matrix for the above created + // agument to store the testing samples. + paddle_matrix mat = + paddle_matrix_create(/* height = batch size */ 1, + /* width = dimensionality of the data layer */ 784, + /* whether to use GPU */ false); paddle_real* array; - - // Get First row. + // Get the pointer pointing to the start address of the first row of the + // created matrix. CHECK(paddle_matrix_get_row(mat, 0, &array)); + // Fill the matrix with a randomly generated test sample. + srand(time(0)); for (int i = 0; i < 784; ++i) { array[i] = rand() / ((float)RAND_MAX); } + // Assign the matrix to the argument. CHECK(paddle_arguments_set_value(in_args, 0, mat)); + // Create the output argument. paddle_arguments out_args = paddle_arguments_create_none(); + + // Invoke the forward computation. CHECK(paddle_gradient_machine_forward(machine, in_args, out_args, - /* isTrain */ false)); - paddle_matrix prob = paddle_matrix_create_none(); + /* is train taks or not */ false)); + // Create the matrix to hold the forward result of the neural network. + paddle_matrix prob = paddle_matrix_create_none(); + // Access the matrix of the output argument, the predicted result is stored in + // which. CHECK(paddle_arguments_get_value(out_args, 0, prob)); uint64_t height; uint64_t width; - CHECK(paddle_matrix_get_shape(prob, &height, &width)); CHECK(paddle_matrix_get_row(prob, 0, &array)); @@ -68,6 +91,7 @@ int main() { } printf("\n"); + // The cleaning up. CHECK(paddle_matrix_destroy(prob)); CHECK(paddle_arguments_destroy(out_args)); CHECK(paddle_matrix_destroy(mat)); diff --git a/paddle/capi/examples/model_inference/dense/merge_v2_model.py b/paddle/capi/examples/model_inference/dense/merge_v2_model.py new file mode 100644 index 00000000000..c030d572cbd --- /dev/null +++ b/paddle/capi/examples/model_inference/dense/merge_v2_model.py @@ -0,0 +1,8 @@ +from paddle.utils.merge_model import merge_v2_model + +from mnist_v2 import network + +net = network(is_infer=True) +param_file = "models/params_pass_4.tar" +output_file = "output.paddle.model" +merge_v2_model(net, param_file, output_file) diff --git a/paddle/capi/examples/model_inference/dense/mnist_v2.py b/paddle/capi/examples/model_inference/dense/mnist_v2.py new file mode 100644 index 00000000000..ee28111153c --- /dev/null +++ b/paddle/capi/examples/model_inference/dense/mnist_v2.py @@ -0,0 +1,117 @@ +import os +import sys +import gzip +import logging +import argparse +from PIL import Image +import numpy as np + +import paddle.v2 as paddle +from paddle.utils.dump_v2_config import dump_v2_config + +logger = logging.getLogger("paddle") +logger.setLevel(logging.INFO) + + +def multilayer_perceptron(img, layer_size, lbl_dim): + for idx, size in enumerate(layer_size): + hidden = paddle.layer.fc(input=(img if not idx else hidden), + size=size, + act=paddle.activation.Relu()) + return paddle.layer.fc(input=hidden, + size=lbl_dim, + act=paddle.activation.Softmax()) + + +def network(input_dim=784, lbl_dim=10, is_infer=False): + images = paddle.layer.data( + name='pixel', type=paddle.data_type.dense_vector(input_dim)) + + predict = multilayer_perceptron( + images, layer_size=[128, 64], lbl_dim=lbl_dim) + + if is_infer: + return predict + else: + label = paddle.layer.data( + name='label', type=paddle.data_type.integer_value(lbl_dim)) + return paddle.layer.classification_cost(input=predict, label=label) + + +def main(task="train", use_gpu=False, trainer_count=1, save_dir="models"): + if task == "train": + if not os.path.exists(save_dir): + os.mkdir(save_dir) + + paddle.init(use_gpu=use_gpu, trainer_count=trainer_count) + cost = network() + parameters = paddle.parameters.create(cost) + optimizer = paddle.optimizer.Momentum( + learning_rate=0.1 / 128.0, + momentum=0.9, + regularization=paddle.optimizer.L2Regularization(rate=0.0005 * 128)) + + trainer = paddle.trainer.SGD(cost=cost, + parameters=parameters, + update_equation=optimizer) + + def event_handler(event): + if isinstance(event, paddle.event.EndIteration): + if event.batch_id % 100 == 0: + logger.info("Pass %d, Batch %d, Cost %f, %s" % + (event.pass_id, event.batch_id, event.cost, + event.metrics)) + if isinstance(event, paddle.event.EndPass): + with gzip.open( + os.path.join(save_dir, "params_pass_%d.tar" % + event.pass_id), "w") as f: + trainer.save_parameter_to_tar(f) + + trainer.train( + reader=paddle.batch( + paddle.reader.shuffle( + paddle.dataset.mnist.train(), buf_size=8192), + batch_size=128), + event_handler=event_handler, + num_passes=5) + elif task == "dump_config": + predict = network(is_infer=True) + dump_v2_config(predict, "trainer_config.bin", True) + else: + raise RuntimeError(("Error value for parameter task. " + "Available options are: train and dump_config.")) + + +def parse_cmd(): + parser = argparse.ArgumentParser( + description="PaddlePaddle MNIST demo for CAPI.") + parser.add_argument( + "--task", + type=str, + required=False, + help=("A string indicating the taks type. " + "Available options are: \"train\", \"dump_config\"."), + default="train") + parser.add_argument( + "--use_gpu", + type=bool, + help=("A bool flag indicating whether to use GPU device or not."), + default=False) + parser.add_argument( + "--trainer_count", + type=int, + help=("This parameter is only used in training task. It indicates " + "how many computing threads are created in training."), + default=1) + parser.add_argument( + "--save_dir", + type=str, + help=("This parameter is only used in training task. It indicates " + "path of the directory to save the trained models."), + default="models") + return parser.parse_args() + + +if __name__ == "__main__": + args = parse_cmd() + main(args.task, args.use_gpu, args.trainer_count, args.save_dir) diff --git a/paddle/capi/examples/model_inference/sparse_binary/main.c b/paddle/capi/examples/model_inference/sparse_binary/main.c index 8ba67aee560..029b94ee63b 100644 --- a/paddle/capi/examples/model_inference/sparse_binary/main.c +++ b/paddle/capi/examples/model_inference/sparse_binary/main.c @@ -1,5 +1,6 @@ #include #include + #include "../common/common.h" #define CONFIG_BIN "./trainer_config.bin" @@ -9,16 +10,18 @@ int main() { char* argv[] = {"--use_gpu=False"}; CHECK(paddle_init(1, (char**)argv)); - // Reading config binary file. It is generated by `convert_protobin.sh` + // Read the binary configuration file which is generated by + // `convert_protobin.sh` long size; void* buf = read_config(CONFIG_BIN, &size); - // Create a gradient machine for inference. + // Create the gradient machine for inference. paddle_gradient_machine machine; CHECK(paddle_gradient_machine_create_for_inference(&machine, buf, (int)size)); CHECK(paddle_gradient_machine_randomize_param(machine)); - // Loading parameter. Uncomment the following line and change the directory. + // Load the trained parameters. Uncomment the following line and change the + // directory as needed. // CHECK(paddle_gradient_machine_load_parameter_from_disk(machine, // "./some_where_to_params")); paddle_arguments in_args = paddle_arguments_create_none(); @@ -26,7 +29,7 @@ int main() { // There is only one input of this network. CHECK(paddle_arguments_resize(in_args, 1)); - // Create input matrix. + // Create the input matrix. paddle_matrix mat = paddle_matrix_create_sparse(1, 784, 3, true, false); srand(time(0)); paddle_real* array; diff --git a/python/paddle/utils/dump_v2_config.py b/python/paddle/utils/dump_v2_config.py new file mode 100644 index 00000000000..5dc2111e379 --- /dev/null +++ b/python/paddle/utils/dump_v2_config.py @@ -0,0 +1,62 @@ +# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import collections + +from paddle.trainer_config_helpers.layers import LayerOutput +from paddle.v2.layer import parse_network +from paddle.proto import TrainerConfig_pb2 + +__all__ = ["dump_v2_config"] + + +def dump_v2_config(topology, save_path, binary=False): + """ Dump the network topology to a specified file. + + This function is only used to dump network defined by using PaddlePaddle V2 + APIs. This function will NOT dump configurations related to PaddlePaddle + optimizer. + + :param topology: The output layers (can be more than one layers given in a + Python List or Tuple) of the entire network. Using the + specified layers (if more than one layer is given) as root, + traversing back to the data layer(s), all the layers + connected to the specified output layers will be dumped. + Layers not connceted to the specified will not be dumped. + :type topology: LayerOutput|List|Tuple + :param save_path: The path to save the dumped network topology. + :type save_path: str + :param binary: Whether to dump the serialized network topology or not. + The default value is false. NOTE that, if you call this + function to generate network topology for PaddlePaddle C-API, + a serialized version of network topology is required. When + using PaddlePaddle C-API, this flag MUST be set to True. + :type binary: bool + """ + + if isinstance(topology, LayerOutput): + topology = [topology] + elif isinstance(topology, collections.Sequence): + for out_layer in topology: + assert isinstance(out_layer, LayerOutput), ( + "The type of each element in the parameter topology " + "should be LayerOutput.") + else: + raise RuntimeError("Error input type for parameter topology.") + + model_str = parse_network(topology) + with open(save_path, "w") as fout: + if binary: + fout.write(model_str.SerializeToString()) + else: + fout.write(str(model_str)) diff --git a/python/paddle/utils/merge_model.py b/python/paddle/utils/merge_model.py index 421e953d277..2b100207728 100644 --- a/python/paddle/utils/merge_model.py +++ b/python/paddle/utils/merge_model.py @@ -30,7 +30,8 @@ def merge_v2_model(net, param_file, output_file): which ends with .tar.gz. @param net The output layer of the network for inference. - @param param_file Path of the parameters (.tar.gz) which is stored by v2 api. + @param param_file Path of the parameters (.tar.gz) which is stored by + v2 api. @param output_file Path of the merged file which will be generated. Usage: -- GitLab From 9d2c77e6262eae63a150425ab24402660c091bfb Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Tue, 19 Dec 2017 05:12:53 +0000 Subject: [PATCH 0031/2305] parallel_do skeleton pass compile --- paddle/operators/CMakeLists.txt | 2 + paddle/operators/parallel_do_op.cc | 147 +++++++++++++++++++++++++++++ paddle/operators/recurrent_op.cc | 20 ++-- paddle/operators/while_op.cc | 20 ++-- 4 files changed, 169 insertions(+), 20 deletions(-) create mode 100644 paddle/operators/parallel_do_op.cc diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index 5aaaf993323..fae2fc3c25d 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -185,6 +185,7 @@ set(DEPS_OPS cond_op cross_entropy_op recurrent_op + parallel_do_op softmax_with_cross_entropy_op softmax_op sequence_softmax_op @@ -256,6 +257,7 @@ op_library(lstm_op DEPS sequence2batch lstm_compute) op_library(conv_transpose_op DEPS vol2col) op_library(gru_op DEPS sequence2batch gru_compute) op_library(recurrent_op SRCS recurrent_op.cc DEPS executor) +op_library(parallel_do_op SRCS parallel_do_op.cc DEPS executor) # FIXME(typhoonzero): save/load depends lodtensor serialization functions op_library(save_op DEPS lod_tensor) diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc new file mode 100644 index 00000000000..a2a12cfdf0a --- /dev/null +++ b/paddle/operators/parallel_do_op.cc @@ -0,0 +1,147 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include +#include "paddle/framework/executor.h" +#include "paddle/framework/op_registry.h" +#include "paddle/framework/operator.h" + +namespace paddle { +namespace operators { + +constexpr char kInputs[] = "inputs"; +constexpr char kParameters[] = "parameters"; +constexpr char kPlaces[] = "places"; +constexpr char kParallelBlock[] = "parallel_block"; +constexpr char kOutputs[] = "outputs"; +constexpr char kParallelScopes[] = "parallel_scopes"; +// #define GRAD_SUFFIX "@GRAD" +// constexpr char kInputGrads[] = "inputs" GRAD_SUFFIX; +// constexpr char kOutputGrads[] = "outputs" GRAD_SUFFIX; +// constexpr char kParamGrads[] = "parameters" GRAD_SUFFIX; + +using ParallelScopeVar = std::vector; +using OperatorBase = framework::OperatorBase; + +class ParallelDoOp : public OperatorBase { + public: + ParallelDoOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : OperatorBase(type, inputs, outputs, attrs) {} + + void Run(const framework::Scope &scope, + const platform::DeviceContext &dev_ctx) const override { + // create scope + // copy parameters + } +}; + +class ParallelDoGradOp : public OperatorBase { + public: + ParallelDoGradOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : OperatorBase(type, inputs, outputs, attrs) {} + + void Run(const framework::Scope &scope, + const platform::DeviceContext &dev_ctx) const override {} +}; + +class ParallelDoOpProtoMaker : public framework::OpProtoAndCheckerMaker { + public: + ParallelDoOpProtoMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput(kInputs, "").AsDuplicable(); + AddInput(kParameters, "").AsDuplicable(); + AddInput(kPlaces, ""); + AddOutput(kOutputs, "").AsDuplicable(); + AddOutput(kParallelScopes, ""); + AddAttr(kParallelBlock, ""); + AddComment(R"DOC( +ParallelDo Operator. +)DOC"); + } +}; + +class ParallelDoGradOpDescMaker : public framework::SingleGradOpDescMaker { + public: + using framework::SingleGradOpDescMaker::SingleGradOpDescMaker; + + protected: + virtual std::unique_ptr Apply() const { + PADDLE_THROW("Not Implemented"); + auto *grad = new framework::OpDescBind(); + grad->SetType("recurrent_grad"); + for (auto &input_param : this->InputNames()) { + grad->SetInput(input_param, this->Input(input_param)); + grad->SetOutput(framework::GradVarName(input_param), + this->InputGrad(input_param)); + } + + for (auto &output_param : this->OutputNames()) { + if (output_param == kParallelScopes) { + grad->SetInput(output_param, this->Output(output_param)); + grad->SetInput(framework::GradVarName(output_param), + this->Output(output_param)); + } else { + grad->SetInput(output_param, this->Output(output_param)); + grad->SetInput(framework::GradVarName(output_param), + this->OutputGrad(output_param)); + } + } + grad->SetAttrMap(this->Attrs()); + grad->SetBlockAttr(kParallelBlock, *grad_block_[0]); + + return std::unique_ptr(grad); + } +}; + +class ParallelDoGradOpShapeInference : public framework::InferShapeBase { + public: + void operator()(framework::InferShapeContext *ctx) const override { + PADDLE_THROW("Not Implemented"); + // std::vector input{kInputs}; + // std::vector output{kOutputs}; + // for (auto &s : input) { + // PADDLE_ENFORCE(ctx->HasInputs(s)); + // PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(s)), + // "Cannot find the gradient variable %s", + // framework::GradVarName(s)); + // } + // for (auto &s : output) { + // PADDLE_ENFORCE(ctx->HasInputs(s)); + // } + // for (auto &s : input) { + // ctx->SetOutputsDim(framework::GradVarName(s), ctx->GetInputsDim(s)); + // } + // if (ctx->HasInputs(kParameters)) { + // PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(kParameters))); + // ctx->SetOutputsDim(framework::GradVarName(kParameters), + // ctx->GetInputsDim(kParameters)); + // } + } +}; + +} // namespace operators +} // namespace paddle + +REGISTER_OPERATOR(parallel_do, paddle::operators::ParallelDoOp, + paddle::operators::ParallelDoOpProtoMaker, + paddle::operators::ParallelDoGradOpDescMaker); +REGISTER_OPERATOR(parallel_do_grad, paddle::operators::ParallelDoGradOp, + paddle::operators::ParallelDoGradOpShapeInference); diff --git a/paddle/operators/recurrent_op.cc b/paddle/operators/recurrent_op.cc index 29f91636438..82ac5a27f73 100644 --- a/paddle/operators/recurrent_op.cc +++ b/paddle/operators/recurrent_op.cc @@ -22,10 +22,10 @@ constexpr char kInputs[] = "inputs"; constexpr char kInitialStates[] = "initial_states"; constexpr char kParameters[] = "parameters"; constexpr char kOutputs[] = "outputs"; -constexpr char kStepScopes[] = "step_scopes"; +constexpr char kParallelScopes[] = "step_scopes"; constexpr char kExStates[] = "ex_states"; constexpr char kStates[] = "states"; -constexpr char kStepBlock[] = "step_block"; +constexpr char kParallelBlock[] = "step_block"; constexpr char kReverse[] = "reverse"; constexpr char kIsTrain[] = "is_train"; #define GRAD_SUFFIX "@GRAD" @@ -234,7 +234,7 @@ class RecurrentOp : public RecurrentBase { auto reverse = Attr(kReverse); framework::Executor executor(dev_ctx); - auto *block = Attr(kStepBlock); + auto *block = Attr(kParallelBlock); auto *program = block->Program(); for (size_t i = 0; i < seq_len; ++i) { @@ -295,7 +295,7 @@ class RecurrentOp : public RecurrentBase { private: StepScopes CreateStepScopes(const framework::Scope &scope, size_t seq_len) const { - auto *var = scope.FindVar(Output(kStepScopes)); + auto *var = scope.FindVar(Output(kParallelScopes)); PADDLE_ENFORCE(var != nullptr); return StepScopes(scope, var->GetMutable(), Attr(kIsTrain), seq_len); @@ -317,7 +317,7 @@ class RecurrentGradOp : public RecurrentBase { auto reverse = Attr(kReverse); framework::Executor executor(dev_ctx); - auto *block = Attr(kStepBlock); + auto *block = Attr(kParallelBlock); auto *program = block->Program(); for (size_t step_id = 0; step_id < seq_len; ++step_id) { @@ -465,7 +465,7 @@ class RecurrentGradOp : public RecurrentBase { private: StepScopes CreateStepScopes(const framework::Scope &scope, size_t seq_len) const { - auto *var = scope.FindVar(Input(kStepScopes)); + auto *var = scope.FindVar(Input(kParallelScopes)); PADDLE_ENFORCE(var != nullptr); return StepScopes(scope, var->GetMutable(), Attr(kIsTrain), seq_len, true /*is_backward*/); @@ -510,7 +510,7 @@ class RecurrentOpProtoMaker : public framework::OpProtoAndCheckerMaker { AddOutput(kOutputs, "The output sequence of RNN. The sequence length must be same.") .AsDuplicable(); - AddOutput(kStepScopes, + AddOutput(kParallelScopes, "StepScopes contain all local variables in each time step."); AddAttr>(kExStates, string::Sprintf( @@ -523,7 +523,7 @@ The ex-state means the state value in the ex-timestep or the previous time step string::Sprintf( "The state variable names. [%s, %s, %s] must be the same order", kExStates, kStates, kInitStateGrads)); - AddAttr(kStepBlock, + AddAttr(kParallelBlock, "The step block inside RNN"); AddAttr(kReverse, R"DOC(Calculate RNN reversely or not. By default reverse=False @@ -576,7 +576,7 @@ class RecurrentGradOpDescMaker : public framework::SingleGradOpDescMaker { } for (auto &output_param : this->OutputNames()) { - if (output_param == kStepScopes) { + if (output_param == kParallelScopes) { grad->SetInput(output_param, this->Output(output_param)); grad->SetInput(framework::GradVarName(output_param), this->Output(output_param)); @@ -587,7 +587,7 @@ class RecurrentGradOpDescMaker : public framework::SingleGradOpDescMaker { } } grad->SetAttrMap(this->Attrs()); - grad->SetBlockAttr(kStepBlock, *grad_block_[0]); + grad->SetBlockAttr(kParallelBlock, *grad_block_[0]); return std::unique_ptr(grad); } diff --git a/paddle/operators/while_op.cc b/paddle/operators/while_op.cc index b8e44bcc5a9..f2b917b0fc0 100644 --- a/paddle/operators/while_op.cc +++ b/paddle/operators/while_op.cc @@ -25,9 +25,9 @@ namespace operators { using StepScopeVar = std::vector; using LoDTensor = framework::LoDTensor; -constexpr char kStepBlock[] = "step_block"; +constexpr char kParallelBlock[] = "step_block"; constexpr char kCondition[] = "Condition"; -constexpr char kStepScopes[] = "StepScopes"; +constexpr char kParallelScopes[] = "StepScopes"; constexpr char kParameters[] = "X"; constexpr char kParamGrads[] = "X@GRAD"; constexpr char kOutputs[] = "Out"; @@ -46,11 +46,11 @@ class WhileOp : public framework::OperatorBase { PADDLE_ENFORCE_EQ(cond.dims(), paddle::framework::make_ddim({1})); framework::Executor executor(dev_ctx); - auto *block = Attr(kStepBlock); + auto *block = Attr(kParallelBlock); auto *program = block->Program(); auto step_scopes = - scope.FindVar(Output(kStepScopes))->GetMutable(); + scope.FindVar(Output(kParallelScopes))->GetMutable(); while (cond.data()[0]) { auto ¤t_scope = scope.NewScope(); @@ -78,11 +78,11 @@ class WhileOpMaker : public framework::OpProtoAndCheckerMaker { "A set of variables, which will be assigned with values " "generated by the operators inside the block of While Op.") .AsDuplicable(); - AddOutput(kStepScopes, + AddOutput(kParallelScopes, "(StepScopeVar) A vector of local scope, which size equals the " "step number of While Op. The i'th scope storages temporary " "variables generated in the i'th step."); - AddAttr(kStepBlock, + AddAttr(kParallelBlock, "The step block inside WhileOp"); AddComment(R"DOC( )DOC"); @@ -99,11 +99,11 @@ class WhileGradOp : public framework::OperatorBase { void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { framework::Executor executor(dev_ctx); - auto *block = Attr(kStepBlock); + auto *block = Attr(kParallelBlock); auto *program = block->Program(); auto *step_scopes = - scope.FindVar(Input(kStepScopes))->GetMutable(); + scope.FindVar(Input(kParallelScopes))->GetMutable(); auto outside_og_names = Inputs(framework::GradVarName(kOutputs)); auto inside_og_names = @@ -272,9 +272,9 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { std::copy(extra_inputs.begin(), extra_inputs.end(), extra_inputs_list.begin()); grad->SetInput(framework::GradVarName(kOutputs), extra_inputs_list); - grad->SetInput(kStepScopes, Output(kStepScopes)); + grad->SetInput(kParallelScopes, Output(kParallelScopes)); grad->SetAttrMap(this->Attrs()); - grad->SetBlockAttr(kStepBlock, *grad_block_[0]); + grad->SetBlockAttr(kParallelBlock, *grad_block_[0]); // record the original output gradient names, since the gradient name of // while operator could be renamed. grad->SetAttr("original_output_grad", extra_inputs_list); -- GitLab From 9fbd94263e4615010b0379aca94fe1bfd07e7964 Mon Sep 17 00:00:00 2001 From: qijun Date: Tue, 19 Dec 2017 13:20:20 +0800 Subject: [PATCH 0032/2305] init --- paddle/operators/get_places_op.cc | 69 +++++++++++++++++++++++ python/paddle/v2/fluid/framework.py | 2 +- python/paddle/v2/fluid/layers/__init__.py | 3 + python/paddle/v2/fluid/layers/utils.py | 22 ++++++++ 4 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 paddle/operators/get_places_op.cc create mode 100644 python/paddle/v2/fluid/layers/utils.py diff --git a/paddle/operators/get_places_op.cc b/paddle/operators/get_places_op.cc new file mode 100644 index 00000000000..dd937488f4c --- /dev/null +++ b/paddle/operators/get_places_op.cc @@ -0,0 +1,69 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/framework/op_registry.h" +#include "paddle/platform/place.h" + +namespace paddle { +namespace operators { + +class GetPlacesOp : public framework::OperatorBase { + public: + GetPlacesOp(const std::string &type, const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : OperatorBase(type, inputs, outputs, attrs) {} + void Run(const framework::Scope &scope, + const platform::DeviceContext &dev_ctx) const override { + auto use_gpu = Attr("use_gpu"); + auto trainer_count = Attr("trainer_count"); + + auto out_var_name = Output("Out"); + auto *out_var = scope.FindVar(out_var_name); + PADDLE_ENFORCE(out_var != nullptr, "Output variable %s cannot be found", + out_var_name); + + auto &places = *(out_var->GetMutable>()); + places.reserve(trainer_count); + if (use_gpu) { + for (int i = 0; i < trainer_count; i++) { + places.emplace_back(platform::GPUPlace(i)); + } + } else { + for (int i = 0; i < trainer_count; i++) { + places.emplace_back(platform::CPUPlace()); + } + } + } +}; + +class GetPlacesOpProtoMaker : public framework::OpProtoAndCheckerMaker { + public: + GetPlacesOpProtoMaker(framework::OpProto *proto, + framework::OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddOutput("Out", "vector of Place"); + AddAttr("trainer_count", "(int)trainer count").SetDefault(1); + AddAttr("use_gpu", "(bool)use gpu").SetDefault(false); + AddComment(R"DOC( +GetPlaces Operator. + +)DOC"); + } +}; +} // namespace operators +} // namespace paddle +namespace ops = paddle::operators; + +REGISTER_OPERATOR(get_places, ops::GetPlacesOp, ops::GetPlacesOpProtoMaker); diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index bf0cd275b62..e9319cbe2a4 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -424,7 +424,7 @@ class Operator(object): self.desc.check_attrs() no_kernel_op_set = { 'feed', 'fetch', 'save', 'load', 'recurrent', - 'rnn_memory_helper_grad', 'conditional_block', 'while' + 'rnn_memory_helper_grad', 'conditional_block', 'while', 'get_places' } if type not in no_kernel_op_set: self.desc.infer_var_type(self.block.desc) diff --git a/python/paddle/v2/fluid/layers/__init__.py b/python/paddle/v2/fluid/layers/__init__.py index 249f570e13b..cb885306287 100644 --- a/python/paddle/v2/fluid/layers/__init__.py +++ b/python/paddle/v2/fluid/layers/__init__.py @@ -8,6 +8,8 @@ import tensor from tensor import * import control_flow from control_flow import * +import utils +from utils import * __all__ = [] __all__ += nn.__all__ @@ -15,3 +17,4 @@ __all__ += io.__all__ __all__ += tensor.__all__ __all__ += control_flow.__all__ __all__ += ops.__all__ +__all__ += utils.__all__ diff --git a/python/paddle/v2/fluid/layers/utils.py b/python/paddle/v2/fluid/layers/utils.py new file mode 100644 index 00000000000..aa6857e088b --- /dev/null +++ b/python/paddle/v2/fluid/layers/utils.py @@ -0,0 +1,22 @@ +""" +All util layers. +""" + +from ..layer_helper import LayerHelper +from ..framework import Variable + +__all__ = ['get_places'] + + +def get_places(use_gpu, trainer_count): + helper = LayerHelper('get_places', **locals()) + out_places = helper.create_tmp_variable(dtype=helper.input_dtype()) + helper.append_op( + type='get_places', + outputs={"Out": [out_places]}, + attrs={ + "use_gpu": use_gpu, + 'trainer_count': trainer_count, + }) + + return out_places -- GitLab From 31323f7911bf9cb87fb1875b036622e4d2704d80 Mon Sep 17 00:00:00 2001 From: qijun Date: Tue, 19 Dec 2017 13:29:06 +0800 Subject: [PATCH 0033/2305] add test --- paddle/operators/get_places_op.cc | 2 +- python/paddle/v2/fluid/tests/test_layers.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/paddle/operators/get_places_op.cc b/paddle/operators/get_places_op.cc index dd937488f4c..96a019ac799 100644 --- a/paddle/operators/get_places_op.cc +++ b/paddle/operators/get_places_op.cc @@ -35,7 +35,7 @@ class GetPlacesOp : public framework::OperatorBase { out_var_name); auto &places = *(out_var->GetMutable>()); - places.reserve(trainer_count); + places.resize(trainer_count); if (use_gpu) { for (int i = 0; i < trainer_count; i++) { places.emplace_back(platform::GPUPlace(i)); diff --git a/python/paddle/v2/fluid/tests/test_layers.py b/python/paddle/v2/fluid/tests/test_layers.py index 2286e94a90a..4a03ca68fff 100644 --- a/python/paddle/v2/fluid/tests/test_layers.py +++ b/python/paddle/v2/fluid/tests/test_layers.py @@ -170,6 +170,12 @@ class TestBook(unittest.TestCase): self.assertIsNotNone(layers.sequence_expand(x=x, y=y)) print(str(program)) + def test_get_places(self): + program = Program() + with program_guard(program): + x = layers.get_places(use_gpu=True, trainer_count=4) + print(str(program)) + if __name__ == '__main__': unittest.main() -- GitLab From 973aec2c51ad50b4d8d29971be84c8b91c84af64 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Tue, 19 Dec 2017 05:46:27 +0000 Subject: [PATCH 0034/2305] modify block name --- paddle/operators/parallel_do_op.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index a2a12cfdf0a..3ab4bd3df28 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -25,7 +25,7 @@ constexpr char kParameters[] = "parameters"; constexpr char kPlaces[] = "places"; constexpr char kParallelBlock[] = "parallel_block"; constexpr char kOutputs[] = "outputs"; -constexpr char kParallelScopes[] = "parallel_scopes"; +constexpr char kParallelScopes[] = "sub_block"; // #define GRAD_SUFFIX "@GRAD" // constexpr char kInputGrads[] = "inputs" GRAD_SUFFIX; // constexpr char kOutputGrads[] = "outputs" GRAD_SUFFIX; -- GitLab From 44bae42dd1dbcccbcab2dd888bf1514eb3eee4df Mon Sep 17 00:00:00 2001 From: qijun Date: Tue, 19 Dec 2017 13:55:28 +0800 Subject: [PATCH 0035/2305] follow comments --- paddle/operators/get_places_op.cc | 20 +++++++++++++++----- python/paddle/v2/fluid/layers/utils.py | 4 ++-- python/paddle/v2/fluid/tests/test_layers.py | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/paddle/operators/get_places_op.cc b/paddle/operators/get_places_op.cc index 96a019ac799..6373df2158d 100644 --- a/paddle/operators/get_places_op.cc +++ b/paddle/operators/get_places_op.cc @@ -14,6 +14,9 @@ limitations under the License. */ #include "paddle/framework/op_registry.h" #include "paddle/platform/place.h" +#ifdef PADDLE_WITH_CUDA +#include "paddle/platform/gpu_info.h" +#endif namespace paddle { namespace operators { @@ -26,7 +29,7 @@ class GetPlacesOp : public framework::OperatorBase { : OperatorBase(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { - auto use_gpu = Attr("use_gpu"); + std::string device_type = Attr("device_type"); auto trainer_count = Attr("trainer_count"); auto out_var_name = Output("Out"); @@ -36,11 +39,16 @@ class GetPlacesOp : public framework::OperatorBase { auto &places = *(out_var->GetMutable>()); places.resize(trainer_count); - if (use_gpu) { + if (device_type == "CUDA") { +#ifdef PADDLE_WITH_CUDA + PADDLE_ENFORCE_LT(trainer_count, GetCUDADeviceCount()); for (int i = 0; i < trainer_count; i++) { places.emplace_back(platform::GPUPlace(i)); } - } else { +#else + PADDLE_THROW("'GPUPlace' is not supported in CPU only device."); +#endif + } else if (device_type == "CPU") { for (int i = 0; i < trainer_count; i++) { places.emplace_back(platform::CPUPlace()); } @@ -55,9 +63,11 @@ class GetPlacesOpProtoMaker : public framework::OpProtoAndCheckerMaker { : OpProtoAndCheckerMaker(proto, op_checker) { AddOutput("Out", "vector of Place"); AddAttr("trainer_count", "(int)trainer count").SetDefault(1); - AddAttr("use_gpu", "(bool)use gpu").SetDefault(false); + AddAttr("device_type", + "(string), deivce type can be \"CPU\" and \"CUDA\"") + .InEnum({"CPU", "CUDA"}); AddComment(R"DOC( -GetPlaces Operator. +Returns a list of places based on flags. The list will be used for parallel execution. )DOC"); } diff --git a/python/paddle/v2/fluid/layers/utils.py b/python/paddle/v2/fluid/layers/utils.py index aa6857e088b..b71d8d93573 100644 --- a/python/paddle/v2/fluid/layers/utils.py +++ b/python/paddle/v2/fluid/layers/utils.py @@ -8,14 +8,14 @@ from ..framework import Variable __all__ = ['get_places'] -def get_places(use_gpu, trainer_count): +def get_places(trainer_count, device_type="CPU"): helper = LayerHelper('get_places', **locals()) out_places = helper.create_tmp_variable(dtype=helper.input_dtype()) helper.append_op( type='get_places', outputs={"Out": [out_places]}, attrs={ - "use_gpu": use_gpu, + "device_type": device_type, 'trainer_count': trainer_count, }) diff --git a/python/paddle/v2/fluid/tests/test_layers.py b/python/paddle/v2/fluid/tests/test_layers.py index 4a03ca68fff..82d7b03a8db 100644 --- a/python/paddle/v2/fluid/tests/test_layers.py +++ b/python/paddle/v2/fluid/tests/test_layers.py @@ -173,7 +173,7 @@ class TestBook(unittest.TestCase): def test_get_places(self): program = Program() with program_guard(program): - x = layers.get_places(use_gpu=True, trainer_count=4) + x = layers.get_places(trainer_count=4) print(str(program)) -- GitLab From aea5ccca703817f41b8fa0e258eb45ea7d0c4f29 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Tue, 19 Dec 2017 06:03:39 +0000 Subject: [PATCH 0036/2305] revise typo --- paddle/operators/recurrent_op.cc | 18 +++++++++--------- paddle/operators/while_op.cc | 18 +++++++++--------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/paddle/operators/recurrent_op.cc b/paddle/operators/recurrent_op.cc index e9d4b449fd9..232d926f7b9 100644 --- a/paddle/operators/recurrent_op.cc +++ b/paddle/operators/recurrent_op.cc @@ -22,7 +22,7 @@ constexpr char kInputs[] = "inputs"; constexpr char kInitialStates[] = "initial_states"; constexpr char kParameters[] = "parameters"; constexpr char kOutputs[] = "outputs"; -constexpr char kParallelScopes[] = "step_scopes"; +constexpr char kStepScopes[] = "step_scopes"; constexpr char kExStates[] = "ex_states"; constexpr char kStates[] = "states"; constexpr char kStepBlock[] = "sub_block"; @@ -234,7 +234,7 @@ class RecurrentOp : public RecurrentBase { auto reverse = Attr(kReverse); framework::Executor executor(dev_ctx); - auto *block = Attr(kParallelBlock); + auto *block = Attr(kStepBlock); auto *program = block->Program(); for (size_t i = 0; i < seq_len; ++i) { @@ -295,7 +295,7 @@ class RecurrentOp : public RecurrentBase { private: StepScopes CreateStepScopes(const framework::Scope &scope, size_t seq_len) const { - auto *var = scope.FindVar(Output(kParallelScopes)); + auto *var = scope.FindVar(Output(kStepScopes)); PADDLE_ENFORCE(var != nullptr); return StepScopes(scope, var->GetMutable(), Attr(kIsTrain), seq_len); @@ -317,7 +317,7 @@ class RecurrentGradOp : public RecurrentBase { auto reverse = Attr(kReverse); framework::Executor executor(dev_ctx); - auto *block = Attr(kParallelBlock); + auto *block = Attr(kStepBlock); auto *program = block->Program(); for (size_t step_id = 0; step_id < seq_len; ++step_id) { @@ -465,7 +465,7 @@ class RecurrentGradOp : public RecurrentBase { private: StepScopes CreateStepScopes(const framework::Scope &scope, size_t seq_len) const { - auto *var = scope.FindVar(Input(kParallelScopes)); + auto *var = scope.FindVar(Input(kStepScopes)); PADDLE_ENFORCE(var != nullptr); return StepScopes(scope, var->GetMutable(), Attr(kIsTrain), seq_len, true /*is_backward*/); @@ -510,7 +510,7 @@ class RecurrentOpProtoMaker : public framework::OpProtoAndCheckerMaker { AddOutput(kOutputs, "The output sequence of RNN. The sequence length must be same.") .AsDuplicable(); - AddOutput(kParallelScopes, + AddOutput(kStepScopes, "StepScopes contain all local variables in each time step."); AddAttr>(kExStates, string::Sprintf( @@ -523,7 +523,7 @@ The ex-state means the state value in the ex-timestep or the previous time step string::Sprintf( "The state variable names. [%s, %s, %s] must be the same order", kExStates, kStates, kInitStateGrads)); - AddAttr(kParallelBlock, + AddAttr(kStepBlock, "The step block inside RNN"); AddAttr(kReverse, R"DOC(Calculate RNN reversely or not. By default reverse=False @@ -576,7 +576,7 @@ class RecurrentGradOpDescMaker : public framework::SingleGradOpDescMaker { } for (auto &output_param : this->OutputNames()) { - if (output_param == kParallelScopes) { + if (output_param == kStepScopes) { grad->SetInput(output_param, this->Output(output_param)); grad->SetInput(framework::GradVarName(output_param), this->Output(output_param)); @@ -587,7 +587,7 @@ class RecurrentGradOpDescMaker : public framework::SingleGradOpDescMaker { } } grad->SetAttrMap(this->Attrs()); - grad->SetBlockAttr(kParallelBlock, *grad_block_[0]); + grad->SetBlockAttr(kStepBlock, *grad_block_[0]); return std::unique_ptr(grad); } diff --git a/paddle/operators/while_op.cc b/paddle/operators/while_op.cc index af992da5b0b..9a092a570ff 100644 --- a/paddle/operators/while_op.cc +++ b/paddle/operators/while_op.cc @@ -27,7 +27,7 @@ using LoDTensor = framework::LoDTensor; constexpr char kStepBlock[] = "sub_block"; constexpr char kCondition[] = "Condition"; -constexpr char kParallelScopes[] = "StepScopes"; +constexpr char kStepScopes[] = "StepScopes"; constexpr char kParameters[] = "X"; constexpr char kParamGrads[] = "X@GRAD"; constexpr char kOutputs[] = "Out"; @@ -46,11 +46,11 @@ class WhileOp : public framework::OperatorBase { PADDLE_ENFORCE_EQ(cond.dims(), paddle::framework::make_ddim({1})); framework::Executor executor(dev_ctx); - auto *block = Attr(kParallelBlock); + auto *block = Attr(kStepBlock); auto *program = block->Program(); auto step_scopes = - scope.FindVar(Output(kParallelScopes))->GetMutable(); + scope.FindVar(Output(kStepScopes))->GetMutable(); while (cond.data()[0]) { auto ¤t_scope = scope.NewScope(); @@ -78,11 +78,11 @@ class WhileOpMaker : public framework::OpProtoAndCheckerMaker { "A set of variables, which will be assigned with values " "generated by the operators inside the block of While Op.") .AsDuplicable(); - AddOutput(kParallelScopes, + AddOutput(kStepScopes, "(StepScopeVar) A vector of local scope, which size equals the " "step number of While Op. The i'th scope storages temporary " "variables generated in the i'th step."); - AddAttr(kParallelBlock, + AddAttr(kStepBlock, "The step block inside WhileOp"); AddComment(R"DOC( )DOC"); @@ -99,11 +99,11 @@ class WhileGradOp : public framework::OperatorBase { void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { framework::Executor executor(dev_ctx); - auto *block = Attr(kParallelBlock); + auto *block = Attr(kStepBlock); auto *program = block->Program(); auto *step_scopes = - scope.FindVar(Input(kParallelScopes))->GetMutable(); + scope.FindVar(Input(kStepScopes))->GetMutable(); auto outside_og_names = Inputs(framework::GradVarName(kOutputs)); auto inside_og_names = @@ -272,9 +272,9 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { std::copy(extra_inputs.begin(), extra_inputs.end(), extra_inputs_list.begin()); grad->SetInput(framework::GradVarName(kOutputs), extra_inputs_list); - grad->SetInput(kParallelScopes, Output(kParallelScopes)); + grad->SetInput(kStepScopes, Output(kStepScopes)); grad->SetAttrMap(this->Attrs()); - grad->SetBlockAttr(kParallelBlock, *grad_block_[0]); + grad->SetBlockAttr(kStepBlock, *grad_block_[0]); // record the original output gradient names, since the gradient name of // while operator could be renamed. grad->SetAttr("original_output_grad", extra_inputs_list); -- GitLab From 0d60b591bbae554ae61f391ccba4768816cc8070 Mon Sep 17 00:00:00 2001 From: qijun Date: Tue, 19 Dec 2017 14:28:04 +0800 Subject: [PATCH 0037/2305] fix ci --- paddle/operators/get_places_op.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/operators/get_places_op.cc b/paddle/operators/get_places_op.cc index 6373df2158d..5158af8f429 100644 --- a/paddle/operators/get_places_op.cc +++ b/paddle/operators/get_places_op.cc @@ -41,7 +41,7 @@ class GetPlacesOp : public framework::OperatorBase { places.resize(trainer_count); if (device_type == "CUDA") { #ifdef PADDLE_WITH_CUDA - PADDLE_ENFORCE_LT(trainer_count, GetCUDADeviceCount()); + PADDLE_ENFORCE_LT(trainer_count, platform::GetCUDADeviceCount()); for (int i = 0; i < trainer_count; i++) { places.emplace_back(platform::GPUPlace(i)); } -- GitLab From 3034edf1a7f41bb9b4ffe7f13c83314091989c90 Mon Sep 17 00:00:00 2001 From: qijun Date: Tue, 19 Dec 2017 15:28:44 +0800 Subject: [PATCH 0038/2305] fix typo --- python/paddle/v2/fluid/tests/test_layers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_layers.py b/python/paddle/v2/fluid/tests/test_layers.py index d40510b6b3d..4fceb0eca5b 100644 --- a/python/paddle/v2/fluid/tests/test_layers.py +++ b/python/paddle/v2/fluid/tests/test_layers.py @@ -170,12 +170,6 @@ class TestBook(unittest.TestCase): self.assertIsNotNone(layers.sequence_expand(x=x, y=y)) print(str(program)) - def test_get_places(self): - program = Program() - with program_guard(program): - x = layers.get_places(trainer_count=4) - print(str(program)) - def test_lstm_unit(self): program = Program() with program_guard(program): @@ -193,6 +187,12 @@ class TestBook(unittest.TestCase): x_t=x_t, hidden_t_prev=prev_hidden, cell_t_prev=prev_cell)) print(str(program)) + def test_get_places(self): + program = Program() + with program_guard(program): + x = layers.get_places(trainer_count=4) + print(str(program)) + if __name__ == '__main__': unittest.main() -- GitLab From 878e6f7c54fd4448698bd38f7f658063189150b1 Mon Sep 17 00:00:00 2001 From: ying Date: Tue, 19 Dec 2017 13:32:27 +0800 Subject: [PATCH 0039/2305] update the data organization part. --- doc/howto/usage/capi/a_simple_example.md | 8 +- doc/howto/usage/capi/core_concepts.md | 0 doc/howto/usage/capi/images/csr.png | Bin 174346 -> 370051 bytes doc/howto/usage/capi/images/sequence_data.png | Bin 0 -> 481460 bytes .../usage/capi/organization_of_the_inputs.md | 174 +++++++++++++----- doc/howto/usage/capi/overview.md | 4 +- 6 files changed, 134 insertions(+), 52 deletions(-) delete mode 100644 doc/howto/usage/capi/core_concepts.md create mode 100644 doc/howto/usage/capi/images/sequence_data.png diff --git a/doc/howto/usage/capi/a_simple_example.md b/doc/howto/usage/capi/a_simple_example.md index efcae3518ee..ae2eaa3ce28 100644 --- a/doc/howto/usage/capi/a_simple_example.md +++ b/doc/howto/usage/capi/a_simple_example.md @@ -115,15 +115,15 @@ - `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 - 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 -*注:这篇文档使用的示例任务手写数字识别不涉及一维整型序列输入/输出,因此不讨论一维整型输入/输出数据相关的内容。更多信息请参考:[输入数据组织](organization_of_the_inputs.md)。* +*注:这篇文档使用的示例任务手写数字识别不涉及一维整型序列输入/输出,因此不讨论一维整型输入/输出数据相关的内容。更多信息请参考:[输入/输出数据组织](organization_of_the_inputs.md)。* -在这篇文档的后面部分,我们会使用`argument`来**特指** PaddlePaddle C-API中神经网的一个输入/输出,使用`matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象,用`ivector`特指`argument`中用于存储数据的`IVector`类的对象。 +这篇文档的之后部分会使用`argument`来**特指** PaddlePaddle C-API中神经网的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 于是,在组织神经网络输入,获取输出时,需要思考完成以下工作: 1. 为每一个输入/输出创建`argument`; -1. 为每一个`argument`创建`matrix`或者`ivector`来存储数据; +1. 为每一个`argument`创建`paddle_matrix`来存储数据; -与输入不同的是,输出`argument`的`matrix`变量并不需在使用C-API时为之要分配存储空间。PaddlePaddle内部,神经网络进行前向计算时会自己分配/管理每个计算层的存储空间;这些细节C-API会代为处理,只需在概念上理解,并按照约定调用相关的 C-API 接口即可。 +与输入不同的是,输出`argument`的`paddle_matrix`变量并不需在使用C-API时为之分配存储空间。PaddlePaddle内部,神经网络进行前向计算时会自己分配/管理每个计算层的存储空间;这些细节C-API会代为处理,只需在概念上理解,并按照约定调用相关的 C-API 接口即可。 下面是示例代码片段。在这段代码中,生成了一条随机输入数据作为测试样本。 ```c diff --git a/doc/howto/usage/capi/core_concepts.md b/doc/howto/usage/capi/core_concepts.md deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/doc/howto/usage/capi/images/csr.png b/doc/howto/usage/capi/images/csr.png index 16454e9261872e9ab337f2d95d1143b77b554ecb..3dc10b8de4f6d3f517624956b1694b689405a031 100644 GIT binary patch literal 370051 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

g~%JBrZuR zDb&Q2*Y|Lm&iKM=c{q0A@eG;UEDs!Jtx^9~gQEaz}_*(0h~5g>&8quH8KRbHfI z%1UnyV9S$Iz~b=vpUuz;c+JqN>iDcY-S^igcqaT_{%gOQJ-CJCZ{k~2Lu*Y$TA}Zwd1n}z3S<0!E80} z@1m!-c7oO?rif-}qk_AXy zRc?chLQ|NdPQ)-Qtbs=n2s;JWG<1{Bf`i4bg3I#i(e&^FSx5W%qy$Fia9 z#}rn29upgYIj70;1C&L}&Eej_=2Drhg0li+0D#f`Ydp5q^SN|Ah7OD$8SovSH9JAn ziDUhkK;jhWWci^JTFQnB?A$7`P!F{8Q;)$Y&c2>{wcnR(tv6w3LzkUhkElMFx~OFv zf%TI0CGKz;t3wChVi!;-_A0Oco$%aXl%S$f`vPB6@Rb0nO&K97AmxUlHh?+H17>zh z`-8_1Id6|-Rj6c0m`!J_fk0i6EkLnF@%4c4u5Wqk?vMZI-{y7k`jub!-0qn-KApEm zrI#vTn~4pTjDFslWIv}a%L>-f-95>_OQjax-3Gm)(th{@?``|UKVSO7=XVdkbJ?rzy={~&}Ddkrf^EbVXO_4V;sl43Ig% z^vf?D=TbS!di@xeK)3%kEFT@yV)+HCs{->9DCu^Gb!wao74FucPe92-fUg_emp`5X zT32AefN41vUxp2;T*o1-yJ7i^&>AbT9N7EHZkFjz0!rtIpoK-VE`PdhCEtn0Oy)ob^C zQ*dX;QOk3#w(Gg|viF^`s`%>U6MfIrJ1~>HHz;`mNJ_N+g zma8Cy?PV_t-ZH$&<1C6WAUH+RFTk%I8a{W$7tlLhI-%RIl2m zvP32Zs=dhM&{WylslkH~&>Ove|6lxO-e$8Vz)F#}$@FJ``o&xN_EfocfExu!Vw(!u zx_^9+b+$k+tqwVmEkPEp$@wU~gwN(nbms%VS5`MS(de2quJqU}#%+K2)y(_&r zWm&QS!P5(S17>c7z2SmnVJbPT*-^(#Y-M`xX-ceVgK1%IP4!i!Hk#|+04N(kzwaeD zQ^4^+Y#PZ9I0o1~AbGW=Zquu=b{la+AD!=@P)9I0KwO~a)v_7@UeDQ|OZsgJzQ#`B zqQ4FBTkxF@l@I&&kQP8(T8GEij?-+%!5x$A;gQrGZul#YWw89F-2)r9IRkBbZ+Fl92x}~xp^SK*zT;xxR)2WQGL*Qq|cWKH0Ad};jPDlOgQG5V76nyy4xUZCu_+9g5UJ{JlMu3Ktc)O;+I)zm}3HtI!Dcs>h*V zUAIlkLrYB0;rN>06IKF3O$HD*vBjwRmf>M4f?Hq#%TVck{%y}T#VPR9>bw*;}J3=XNnQRQW@o+`B2*IPRSj1GV9El=m|L*a!I%0`TeC?T{d ztZARHN;Cx-fo`hMSNMGH^=F$x+XC6tI+2$9%Rm3K454km>38lIQ0!G*WQL&Xn*-fg zHyrRr*&Ybq{d_;y_6ubj=ZV+LA9>mOY;1KJ;(UEWPj8utZHv9(oYR)=9mqDbI&_=E zsPe`c9QILr4+40j)PB?13Tq%6p*QEUt`k!*jp2#(pw9OPGB=>}XdeEP2G2Jj3V|Ln zXP_K3I0}tM;cXxutCtFS2DlyUg|Izq?ELo&=V279XnTI$3bi~|f8GPMP8%_&W*Zet zpsDUZRPDI!0SY2jT|s9B%4W){_vU)5MtD7fj+ooNfrX*G?Xk|Fe6CpfVSGRQJL`Zr z2=(fv0oH|W$aNhQ%mK!t?L?qkzMH{Ul*2j9XkaW7aXCjI*OW=GSSB_>%EZ?3UaHeW z2WIR5wd@pOopAR^WJ%C_NA5!l;I7Pr`*oP;N9%$E%zjOl>cp1%;yX0HQ`r@Dv(J_L z2W1*6zGz5JC#Yo}UxKmmaz+pEIEiV(ejvK+Nq$*&fnwdQsK(kgX6o z?{A;jBB!Pr(ne`s-57ic3?78>`KFsw5LkMB?4uvf(5w;Zzx&UBGeP2iO2<*u+%m*e zhG(Jn7TD#xHtMxpVMm}ktC=upH~8$|FGKzcy=;`Ij9z_x%HIo6kg~n}izOu*^7}>r zid1EQ)aJ=`(EISi_vQV5@&`X+6y>`TeB%WG_KhSyQ$={ll{cy4=A#Zoh)0?L&PoCb3({qZmv3a|Jfk+RxUO}9|Z>WF;aATCx z02B=jHic5aZw77?NDosGc?hiQ;+`=6;5Mx7;7}X!2%No&=J4p51wPT{{_f!EF#XIk_mY1m)Z5gSX{#@mmFmwX-KCJ5v z$74n=d|h;Tkkd9yR);=eMVzWEnq0v9a5dUKE5zRJP+?WG2bgJzRq-R(`t12*0gIP$D+VtIh)7srZW(rk%<<)Kabk-#aZpc?x1oJnGx z^$V0w53~wcF0hRq2dg~V(FFsW8tmmEYXD#Eq}1bF0Om+7wzJw*9^nKaW`9}^zH>CU z7c`i!P|kE@#75Iw)whGM=1Enc=-&f)@p|oR9?Mn=O~dq_cNH(}Q?GwKTN(hn z_?!Pvv$K_i`!2U_JwJkP4nz|$9MoUQc7flZ@&XXQ^o7qAzisu|5_0cWC7BQ@mIqoA zj56yw9q<-6t3$nRAE`lhPe1us0bA4|UgNZVY!BQ1-A~_L6lm^~*OSW%OsndPbISIO z`n&ph|01dCzw^(3BL-iw7BG;Fitl+tZ=FUroRtkgtqSycSsY4z^J&Z0EDq-iyh~~? z%5Dn~N3*e+5fS>{;pa`$51+a?<>*cKL-zD$HKC}yoS_l-&hazi>6OND4cKN1?NqrH zb#s8a*?v95`Ub@}=JGkKw88J(_uBal@K>RiAhL80QQly$Fi+JdLX!d@!9W4C9*@bd ztO}OG*HYta%BVJ&GZ>D_d&{;P3`v6=?U&0LuoI|s=g)EuJ1I*rHh`n3o&nr8_|)s* z0ZXr&Ub_f(qw<}CiDXzHh^4<-XbAUHIuU~K#2wvkd&u0*4(}m%L8BnuNW%)Oj(Ds(u>#YUi)~<&&yP0 z3<7)a^Ur0K7qiS2m6yI=>!ViqZ;CI#H@XJW!O$7eXn}wwp+T~ME5R44ud1i~e+s?= zrm$DQTkKyrU!&&Aad+^o0duPMYo|xRR~vqUFShT~l>vP59xM(y+d}EEZC>WSO%J{3_#903hPkz2%-8FbRDtZR=eO0`FkJMne(}{3_-YA!wFEYl zK<`jlW!iwC@llVq4L|Y`j_5R)mQKRVpKUri1=5;|ngP%&?d6z;A$kMd3V;HB1N{3= zOrVNu$EU5|*E8oobf9cG+YYiSvph$u*2bW!9SV2+Q3t?7xIYKAEqF+eI#|<=n!DDr zZl1E3?`W2_sX`n1YklSguh>Q)>(t`uF(}kgwOoQ~C?>UHXUjwVAC&^2l%Y6JWm>X4 zz=mKg4;E;bz}H8jBWnX*uYck-Ekgiu@}iQxw`M>1_6oj$Uk0%anJygEBa;K*6xIX{ zGW5#0+8F4A9Rkdx?C2f7G35sytPbfIj2Ab6Yn16MtHYQ6(dT#9B*l3MQcK%KhKQp0 z3i$eK%8rx<-BsZJfB(yW%2_Pn`}Baj1il4&lTwJG^g5$o2H!oPx2G-Zb^|K=vs}Gn z?&viuL-hMNC9x{Mb{gzBZ|}a~y9U5ZitniM+B9#t7M#Kv*W&a=$kAJJS?6?Rxr18l z4S1hEQ)e)&is+I8%6om$o{%1fGxfA}0@a#O6$FmBsQtkIuS(mZ!WyYO@|{uaRMD8I zLs6MT=cwQL-}yP`NruH_VcfMf!3jp5G*~I_G9Puid1NGU;Fx7%d797$JyCrP`$lk z87cDyCP$DR>(^MX`D`=3{E20xAEV94Fet;VZb@0r>ZsOB55lY$dadfQMfcMX@8IjH zhwt?HP*dY(O05T61rZ0M(JXD*t4gJJ2F89L4t`qp2!UiXTt88U`LHGU;%`=d@fQV| z9_l%(!c_I`!^sN1W4b5-l%yIq@O|BD@}v}bnqdGpz&8OeUT;Xl|0VZv3eHL2XXy3n zwJVpi$=hqZxD!Yu_)_W3&`YH?P{^6mEcE!M^_Y4LL2iU>y{1p4bHNLWZ@!@bDQP`q zUC3}+Q<|B;xwH&|( zz@d7=8-2p?sWxBlf#mA7YA*qId1kiJlfLyFnZ;s@g<%W$MwuHfKY4QZs#~w$J$%bG z`C1@dS?-9Y2VV|iLylfRXRS}L0>KlnUezUdi#)CP?y_qy-QDx@v%5PUxNSM#?*h1a zkR8C*HG-dIY27)@oIe}ATVnOb0{N*!oXdDGR6nbBD z^n%@AKTe6YlGg3u8(4Z&@vBO$8SWmS$*Gba zbo3ls9T51`@1k|X&V+)pz`230Ag8SUz6OO+$HdeTe-3IP7&~DKi2Yt%0)N+`NXKK> zbDfXD(y&lQJolw*6#%~3zrQX7Z@+C1!s$2=7 z$DkfhKa?NY8zSJX;H5+Q+3Ju}pp|!y2v6(gn+bkjWO+#NtDvHOU$r{KL&`SiD3o5j z;O9kP4$2a6i}y#qtE4ph`bQsWSu+|$>N)Fq)bhlH6-+&rXIhKEH!X{xGTIIRpWctH z>s@-?Wo-sG;~f^Yo|9KV`8DazYDdImS8$!2;kSB@XoP8@K)N&&{Kj%{7KgU)YK{4w zfA$-po#D(Sm*g_WgdskFOzjOief2)zJ4{Z&v|GtC;RD%B=7zyabAF1i*2T3+Dz*YI zm)iooT*ia%EOWzUCr<3X`Rw((U%T(x-R_D*!~6fa?M1uKKXAkD>AP;&9ZmO5o`fb{ z>l}BGl-?7`(`zE$9keKrsYmvT>n`0r{!NeU-v1MC-2LpIJU_fY_K%<2z3qFS$S`cc zZ~=UMV3${A0lL?H`*-dR{mgId4*l!D8sF*rYv26L?wUJpmBwTNmW*=e)VNv>Jb&C< z2t;MalJ^<^{`=J7#P0-Q-+91u@q!Br@E!*ArkO9H`r1+ z+8Wj0s16A1M%N;&gIWdwAP?u`%FF<}USkE}pdR>V#B79Nu{<5@4^S&*#B&eo@gV{$ zJ+=m>vPxMXae%DI>L9R0b zS|0S-c@W?0^>^Mn%(Ujyk3oHf5{f`GWLxkZ5?)8{_l}gimoS~DKi1=@W&f~DGwf$t z!*)y>HFq8a-+U}`F7)@JG0wSx>-8|U4FO;cibErXMj7ei@Vf8bdyMLf{JnU6@H^g< zy}ZrSo9ry~^!k)#Tfw(0y>=?yS<13z!8or#HudX{L2cIR5PN&ST;Mx{-uK>p^X|W1 z|1}}-MiBg`x4&rj_}S}<4J+lgMy~5)>HZCe%qQPN*?sz5uiSm+-@GFP-UxyL#{gi| z!$=*C)J`2Xgra-s@BPm)>_z~bzPtLnzkhe>wUtJ1z$< ztvp@XXt`?{hT!XfH&9tK`0Dl1PLVHDa~-7a40T}v7R|nFM+nDZ!B@`RfK|}pjI1gl z;PKi=^~fo;?)sTvzvw;*>!Pb`^%}Hi)vVqcSd?un?pNzdbnS!d;_uV7`>35`-~P~9 zsoNMvIXtMRvw0k7A-=GE<*skI^JZmv@b6V$eV@0MwZbI0ig$iosPRl|iR@l~#;+n_y$uuDhz4!#& zU;SGz-aY%ev)Sg5-!Hv+_v{mQ4seY1o_y8qUFA*o2B^Gv=MgbH9|64hzy8h7%qcus zK!}D%wXlcmf_FbhDF?ToB@b6w2tD7{yXMcI@N?9WH-+lhnqs%Jh^pQDzDQWrypMOwMlucmXfJTg-SbyWOsr`fHqtHwEB>%CTmn9!BErv0Yr{M4xxuKzpMUU%DfmVT?41dERpkZn-hAhcgM#aa_yNVb9_$Yu zL}vhgQ-WKyG+g`kcNG=4HcAR3-0!t_+?K((t>@s^>`S)O(gW4T1#qisE^G|woO*EA zh8(}^s0-GA^W$fmwE@q=2E8X!$D_h;{;O{VLN<2V=xHPBe-D?Fq0C`>wPB35GgTp- zLa6I{g;#-v5az+g0Ptk!*{oB}XMiXJhE4&OhJH}d^|;zOOSuey9su@Ktrkcd&U-7o zOhy143=WE~FEbA?QeL(rHL8sU5~YsW;50p-vW>o7f38}FdaWV|bf9a_L+guPv&sGx zaMN~%;5yy5-;X{o97LNu!r{H7SuBDMQM;$3I$`VE*lFr8x~R=N8Yin7=yJd77*p$51mwKv-y==Vi|!Sj2v zPi)a_4|ReA!j{?^0LlnpL)Hc;Q_PTo&I8~V3M^kxft%;nL%J`IKmn!_&j}~WY-``y zfEDWM+AF6tOZ&olv^eB#sI@}MP?cItG{FWo!*4A++7v!q^Y-!>;4_Bm%Q}U>%;bRW zFfZ_+?7HoGrB^dJK;gwi75e(gPk(ZE=CaFDBc85z^4)3>+ic3|CGhpo+d4b7(}T~d z^d6kOfwl#k$zh&W@7!VcT)e#ufYY+06~sgDr|!S01Qe!w<@uy~rn? z>PPN$r>qXwU4CZwuO7IvL30GZ9$fMH@-*D9@CulX;MfB!mjNJu&o4gHl;W)XdI%CQ z!Fo`18@&FbKgi0hX+&$|5*-J=yY=f|-Id}BzhUDojX1aU;C(l>dlJemlwJB&{V_!Q zf8gF5cYpQUsdJLTzJ9tHqI9^v>cg2_k*_XR7 zLq7tNK4Uw0VY?`!tFD#L!HCH@E8vUwl9rL{z)8I{_oY#K<$4=fb{!kn$t{X6p_jnH z?=}RK4e7@engm*-q4v-sv1XH4QJ#I)0khw#`l6A|(3_97_?RS}|(*Tz&neCSV324CSbmv8cML;^KEGyPN@zv$YYNb$x|os=tTR z(CgNzhpF-+J)MDGQST!KbOyeD8e)ZBW^Ndu_q_eQ7I^)PY-@pRGq~TP{O%Lf=D}Ap zU3?NiRs-1n#yz*>l!3_Fa0)7K3S7gzb~u66QK(_{9?bU%082pe*B`v5ZRd?Iu3l7( z*3`P@q4Yf;f3iXA*FE)csPfK0nEM4ht4-pL`)l?Grz&DQP;HyNAwg?XdAC5DGziz- zcW;O16!QCJ-(*WLk_e~9c0l`;ZeQ|-@1h^S` zk5}*o^yX*X_(tGi^F_P;4dv}%#r*Cq3{pnkViE^BIXO!6q3YR8o+n3gD7lExd$*9=Cw5d=E2=7;%@s=*Sd$boMQvu1gOLPZSJXA z2h8rff$nepMpl#c^Dx;4uSflBL3H67$g@0KP5p!x5ui)?4fkWvQD_|-tuu?ACwhDZ zCL737>0PrXELb1Rw8Vfs@5!C)qpdenjF*(&4N58l-Ex+P?DMTz9-s!N3WQ-;rlsOv zfDxf52T0Y~rE()s&kd7+XkKLekMCN3-VMh+1z}Zy(>5=^{)!#*`l@o<0@R#Rj_;uq z!yC-y0loAI570chu2&~0P=G~)3*{DI8y!0W<5cBk`Z7Rt^ODyY-5M;jLy*qdPfRfd zV1dn}7p7|>IeO{o1(?m?F9P2lG&Y}{B2;=0IC}$+W9yW}TL3p&#zXIt?LpT^*(vni zcGYDKR)6ij>$=j5nb;B_Avf&nU-$U#i~q1sc!JRor22z;sGX*3ue`YVe*d53|JBYC zmm?6tx66RFw05rf_ijk%VWO?_~caEdW`V? zQaOf-94XBT5#L>BKEyo*k8gCoY~=eW%;$CwJ#=5buNcI3K?-7f;uUFn@v8cM_cy;d zE4+9gP0)MvSc-#4Z&)8n790RDu9F2|1RxTA{eBVX`%nsS9gfi{sbNDWji+{sID*IT z5aWRT=nL1lDP+5 zv&l||mnmaiS2`o6jHRRD&#}Rcs;sFD5vZ(_6#>YeO&Nzm<=u4}SjJiLb-m#H&1*}ZR$*ax^=HYqRPM85V18k!mL(9x|7@h4v z%WR@XH^n!BZwR^(ltyLkJ9d6_qt@wJ2iQV1&%>3`Y{bNlf0HXj8 zfYzEF0n66qRn2U9cfESMw!IbW!Zf&TwjRM+pb_VmbNLc_EBKaFXT?W@2RZ}Y7_E;R zmH%g@Hx1S1M!F8qQq}d~843Q0+w>V!AS< zCqqUD4@vsq1(@IkNEu+NJ~UyIAV_PfGaesOkr7k@z%$U+?->X>fNyP3T1JR3{K53N zxINxqxZgSm^r1s4L{VWQ^9jH=rz}fBY_K={>fb6>rlR_u$&icR@{ES*4M1)IZ`lTs z-U7hc?v<`Ff?Q`WGrUZNP=Th?Ry**#c=!H&|9Ucyp!3GLC+}b^yAuQH) zX65|xANa`FVH!Yo(!d*NbH`+kwSXwky@9Xnr$L;DAyL-Mw6Y!=1P+(V*~70bGYfGB zd_BB)RWG_W3YP}(MdkJoSw9=a*AJVUefxS6bXt~<)gN~>nlXdMuuKBQJ-ns?F#AqW z`aD`2CW?MEViDP2hWpeh&k@vX8TqmtpmsKzBiDr;X|3DAHR|B&Yz-P*v;=Ayas|@D z^(N}!bh{Dsd3E?)A)25rd3~ARm%y1V4|PU21}|msMc~_sG;>sXz#~OjOZ5g-HyyJ= zZ_Zlbts}G%M5^@cd|X*5fU^9qOZEl$bR)o3AkLoP6wrp{ec2CcEjvE!2st=LSw(7Y zlrjkoG*sUf$R>6QS}<5|vU-=we-rRl=xz1}nSxl;l~L&(vT}$5Y{9n#l5u%IBOATn zw7!^Jz2^nI-QqA#k}^(>?a$}S0liVWvZDC*@?zSu&m~{)snaLZY;4KXi(&HwN{7>% zDee%ASvrgtX74*1Zx5nWb(!tq*B`hhR6+r|gj4{lS4P+fQ|Toj>HZ=Brt0ezWNb$j zT38+w+9LG~>Z9&Ag1qeat!dAwAVUli-%o_~r=Z-}LQ(h?^fFmgL0Ui!FW&7hl z@ZnaT0A5~mqr`eBYszS`I&?*u!$G5 zpiQg}@c5=>_`JmSem|+UeBoZZ^K2>?tq1AY&=Kp)d_LbF+&{|Df^fHBpg!|H!t!lI zL#`3-aX#0%k9N<=b)Rl4Qs=b}Xs00pDwvAD zlmOU3%MHOt6KI(49FPy&p-N4abPHrdQOwJgo1NfWzJoQPco2^#$OY`e+JF~Ql{IUF zfNq{j6&--|Hv?=(E4HC-7Sq*p!qa-(*l{(SVI zq9Wr>7*>EnS~7yKW^mAD@SrD<;6cuYBibH;3z;3TT?D)hz{ZqhDQ~Y=sku%3UD|J? z64Nt$?c3iuRef=vF}+~44K}g_FIpR*@G=w1C0AT#6kNdXpZ?0NdAmRL{(IVphFXM} z6PwpN!!v9UP=HhCh7g3m5C~3QT7rI&<`CtLa&MsG6(SF{Yu;E{o@o;e+*bH)jr(j! zT;Rfh8t6KRH|gQ4g8du~so>yI+5)Mm;{>le3YD@!DDYf)8%v^D8=Ml#AYH3dUTX&q7|$2z(z+ z&*IrDcVbhyW4+m^2lh+vI_{C#x#x})fj_b}c(pvRm-LR5^GydT z;&UAWAEwrwD6|G(8TiCy*8o_T^We?Tx()tM@bw_9pScWd4(ScxJ$7kILv~5^7pCoJ z)i>P)P#8r0+6*Uw8;7V0j&*qWnvHrdFM zYRPQ^Xw}oLlbaZ8!+9&bb}Hg^rMFuO&L@aX%OHYZ_4m#urC|ByPD9OU{T(TgtpjR- zUIbC)AhvX^p+4gE*)$w}N&}*@@=~V0&i=^S0I|+KcIS0%yLa7u^^mCn*%tt)usIMy z^)LQM1BZt_R5CQ&9()@B3m_ptjzH}d;X}!e0Cg6r%RGE?B*W~tru8$*b^v3vJ0RC@ zS3G+oi0^*)(*=An0fkXT1t!YqNvdBG918IL-K6&77+_oA1Qqo00(i0AfBARsY4zWG z;{m@0fYY_71>zG`Rf4iZ4=^ndNO_1XH$#xx0KB5k)JD)YYSEBqU|*2o+z#Aj+eT?t zWs<*%+GMYRw495SSvih2RWv7Rk@KR0PN>RO2e+NBh$=`4 z{Hn5+|99$py#{&u@BktRd|7SxzfaV;5PXMimum{u6fe}-tc=!UBn;zuf%E1vMd2;y zK}8VYK~Vp}*@WTnCS>hCTvSkzPcps6@>IgW

q+i})N767L3xx;ldYfnTtiUYQ3S?K>XW^< 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 mLfkl^YXY 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

k_K4Xfm={<07q6nCmrF<0lfG~tc zgm*UeEF4H355eGl*Gvi%-ku;WTQuu5;|)e*tC7Ka#@nA!<1v1S<71`xd%q@TY=bNB zkp)lL6GoU4rUjBTf!)f#D1IJhuPDRp#U!$~U3bH7{IzP(80U6Z3kugI~q161At$Y<($^euMYvhWrWQR-VLN0oX$!9Ie4oz1`Xmc}sut zVs5r92>(8^qQEs)kwi)9kzaQ9_k0vhiuYzKj(n%j4d+#zG9@9Z7T9d8#vq!< zndn*j2skEan^1wc9~>bH;0}SQAT;TbVI0=20k*(b!Hh>=#+0tJ80HK0;VLRxcg--F zGlrmt=Tn(&r;QCc)6<{?eK@>!uEm?!(bI4|wljjAp*C$1019c4cZuaB3Jt+IeZtv& z>v^w|S@|TqdfI$H70>g+?H$NC;(a)H`lT;G7yiY{mv&LlQ^RP`TYHBVwic3iWM{na zs+|XIWvzQOGRe)@5Y({xGKDE=m`ZnqG>y985w{HR6-g|Z7E)#6B;!8}pc$Z#1Bhn$ zczau9jW`Kf)+@nh=kmM+T<(4A6qI1ebJ%?oERii5S)|2CJCm4(0e0DE<5kW1)2^Qj&UtE7Rg<{F34Q$zE6v# zRtrDp+@NKjXk-*&*wWlRyl;Q|@4A6okpkEbCV+hO!Gr>bM-j{*nnA17f~@t@iW!)M zmTG{(WCgXjQeG^+$2}PA=FkVdW?frxJcbeY(_>H6;Ydu!_H$of2X_2I9~4}5`xG(z zpH;ZaX+;Cb<(E67i2T57mvYd(gr39vAMSN>AuvP`(Ef@MtL5W)SYW@d23xh{W&FEq zpzvpplx*r?X@H`Ij~Aj=JWLx1_GG`f336Y-<))AC2Xb)bi{Q^9?NZRjW&?Ckwgozy zbu3jU^?@xH1ODLLvC~VtU_*62E=$nlKo>mSO^ThyG&BON7@;Sz$aAe#RMhtIg=-8< zQ)4=p_k1j&o?~Ktmm~B9FpkP|mG4Ez@na7)v)x1Te@z*2&c3ObqxFlQKjJ6qQDcaoN$Fri=wv@l6+McUcOyPf%J%;fxRvYrQs{Q}WXIa9^Xf za%a39)-u&(D#HNS5>$6(VvHUJZu1-CfF#Oxbi=i3L)<5GAJ6NL(y@M=2C4V~)2el% z5`{;!ncxTA)o`wpbBUKi%Rt*3bH}MB5ch6FONuK0R1shM376E5w+GBAoo`6Otpt|P zt`$c7>#uFNCiZ>mJP&c}Elhl6nDG)8rqTBOBHopGZE)acn#n80@b;Y?weJ6Gn&Bb`Y z{ZHwiT5nP~0DB;rzO-a}*6!Uqu1wbA1#{^3>X0NMx|5)B9V$@v#M~A7`%%AtS5l`A zHv80Kwks1-bj9BoH0Gq{GK&In@ACj2%yi1eb);87cacsqgHx7jrfQ2YVKiD$)AE?uO!nnNb4tq956F!O!6Xvzpg zaHbPU#AV)SFqQ7SY{i-veFFZZI?a3DnN#pyUHW4=dKj0QAkPgfK(P|2R*r^SHi{Ci z?EaK-M=tuJTftzJ?pohSUaLs%A}TQ%8Cv%0(v*{@l^jriy}@Jng|;+eB42`bI1 zR=^gXDY+haG7=ne5R5Sym@%-IJ#~QW!a1Icx$zd>(dGPKJ{*!LO76?{^LaW~XumY6 zw1kHX*H~&I?wYMYgi`2PJ2=Y5`41i}dv2I#|i_X$Iwv zpzRAsLgrSm+r2L)u}S3g=3=x2?RMQU{_?q3e+E!)u@I(;Z#m_8b!JWiQJ<%4b%P(Q z1H4k^;H-;5XNjp(&#^ri?iF#jTD)xr!86%I=a}kc?K9AGOqLlacBu#FVT_XTL7Cz&b(5qzko5R$UXpR8Pg9L^x)f&Id#giP=VP+bcMMEjWYmwAp;B$=L!s1H={6#I*O0WuiTyj z8q=Q>u5h9srNcT#p^+Uw1R1-Q^*lkoDU#Mf?IVK5^`DOSb|6intZCWJM`kd+la9j{ zPT=l2a@@fkUt7@=wX~1#SHx@#cG6A5rFoS1_2tRUTqi+_(wgKj-+Qd@&J>3U_ zo%4vc=!!v;4@v}mVBiM)&{F@Y40Yrli28mKi1J;^Z+?1quE>4GR6g0IB)W)SZ2qg> zh5@xOr7tf3jcZn>IsAl*;(fZAC#JshZTymF17@W0amO5`Yv)}$IRr+KdWOv0Z(raI5=@70VGlRTE9zWkyY(?w1cLXi3|=gc+m=1YsUw4ERf& zLi)(OJNEP9=5Y8Vw<&X{IeXgl$hVx_2CXkM#JmPN01fis$ujl45+ZSgnBCPs4b}~z zI5!k-63z8_tuVTOzm(e)bjMTct08|IuNlEY6}1=rb2C=UL33y(>ySLhq_%ZWfM$=K z=Ri(lA;s-^fQ=%6E-apohZZ6iSzSYe5J#i?Ri3X<>&}|ky4;E%4^I^^v{G!_>ulO> z(lkueXp@kn-0E+j{2SzpcEY_v)pl`$6UUjb}0t&F6>iTEkQ;-1c5-a zDg_CCMsVfT2hgZg(?3j;v$BiY&1Ewc0rqN{=$A}~-EVLp&qb3_-UZq+d8>3M` zr-M{RS#ZWwQ=UI)YOk$tV)6t81TxrmRWq;t!RvvfZO09kZp}!k zViIE}J&h=N)aNzpFqjiGnyl1q0$Cov$(q@vIbhJDjQQGI^z+h7&!yV~2PtmmzKAAL zoM@i`ejIF{7Swk;rE~h%w zyN?T*0@GquQasF%eS)S^P+lXo?hTC^l(x8a$O`7yYu&Ao=@*q0wX-nAV9xh9f`s~0 zTSQMz|5BhZ@5Oyn=@1@3LVblFc9uTm=2UOFWiXiT1297CE<^EP9z-<~>F<>fr?%LmmQod!7MXFg;eFn-a z$A8NB=-S$;KmzbsVk%znA=F13&5(8@5oyE@jguf%y~yg$8=lcsXzJtHFj5kst4)=) z9|=+=7>?b8P8pM6;|G`n?EI9X3%6xw!L;-XGfGlW6ZAg$U0n>sXPyjXb3y_m=*X&o#nEOkb$O9jF!W4l|M4rOQcBBKJULyX+ zofy+Wlf!re^KJU4okmAP7*YH3XKvk}VjGl%AZg&?}JjylkK~ zg=nsLU5@L77xO{e$H3izD~l0-gKn$}B7JqMTqNrw@c*@}mtCO#)xkl=AC|BM0*&Lo z={&)Y3p*(%N>7vOS7iFjX8xtyKn0=UlNGR!B7lIbhtt3mM$hB2^ZLvx+ zRbh(tT#r`gzz~VH(;l#e7=kX4UY*L;E)W7^5e7n3&g(+W9PNq}5Hg%--MJ0Zuk8MS zE9QG4zw*oc;gkvsRj_nxMo_!IS1SVlr9EiOVpa-iDsYg*L8qFr z{R};Yz=h2vx^KwQ7!;k!$s;;B|J&k>E5C=}Ax<5B`;wPY0t78PFQ2E?D_E%EosWDg zN>)m202&$+!@$6m4wb>z4^LmtC@mayfGSezk(Cu(OdLNpvIyFWSa6svviD*YonN+@{iWL)fF{LQlvkR#nuqNBX6?A$f-l5R=5hIe38p z#3Crw+A8C%tKfdWi`}rRnxStTd7v*{tBT!mW!5X1C8Oow$#cwV2 zDBCGne**LdUj8|4vOX%iu+a3UGJJM4Bjt~x7HW79hh)=!3xY%NdV-Eh|83;udnkT{>=Ah`?r%WD+sL*xsU_DMLhrLDPP_|bA0PY$v*%7LI3T~{G;D;>e_Mm zHFBDqxc~Rh1Ai#m83|ctGP7C#$3y({@(9SmW|P0yb?Sfq82_F|+}5D*4;{G=DbD zzsI6~6c7Jwnm?Q7&!+iTmZP4mB&VAy{)&7V#4XVX9` z{=?J!mWKJm)BFyu|JgKuHqGyf+22OhKbz*yrunmJpzZemM;qb&me!1O^t0j_$E}j) z=lm?o9?J&iS>9cD7&*O-iAZi8;>6^<&k5W#GfI%~^-X5aEgiPUUN+Hr$H?xTD=lt= z<2SLh`Izz}iL*UkQ@e8uE}hL&I~SjIuuJr)l8(yNPn^xKO$R@5kIb&SW3iPx>J<;T zT|5z)X}G$%`o+o7gXr^y<3Q9QEZkGBJO1H+2~6%goJL&G5fpj)AOFmM`Q6|XS2~`= zEVK6?{`vp%t8h;pF2I5jx#{c}z5n3@{q{GOCgC1VXb^qMa zF{1x3J}EODeA4=TY=Qs$#r>COO{aq%)ug(L{r&&)P5*dFYoYa;nE6_q@*n@ifB6u9 zw%h;hl>OOm|EJye54PKz5LcTt6)O+(><^I+mJPflRL;X>2{OykT$T1>Hirdv?8N>D z|26Di%1cpc$j1?qbLO{iIPEabT92>(;_#QlYBn#&FJ+o@QQo~;PWw;(!1gmdqkw~L zsY-kNx6kB8bo9hzQoC2y1+gJHwUg2&28A(Me_i@#AL6ipMl5zehYZg3ga0L{UAb7= zlc-0(JJGj2GFa{DCJp)8(nC>BV!m)`9O z!K`HmU0t3^SLOJ#lH&6%F#~;{*YdfIx!%{gk>t!j-1*yA^y42i?6K$2Wr!yduFYcYJ z6Ur5Da^Oa8U%T~k*gt!MH*cg*eD?x4WxkWZ{z0#AIOJU(ly~d1Ur_&a=t}ztXP3sG zWL>D4ddns+pcNrkqx#pCZ%cmEE%N@&{ebFohF{2XSuCwrJOLOp5&wv^V_FzyGQw-ci!95-q-eAjVj+(IwWWZ z9V)u|vEHON#T-)+9~0pYZkG z{o{)D1QoL%6SQ{B&8)uM_xk!u1k>Xd{280Oq`!#;r3%p{Qy~PYim8*2LwS}J`;PJ( zie_Xx3x+22tT|<-VZLX2ahX%r)X92JJ@kt?-umvqVnGK^TuqYu&iC)w&d$ARZYt#+ z#$3^T8?mrBidV6AaW5$@MI2;m`FO9@YRwCM_hI?yO#710VXvvew)TE}EZ3^8pGO`z zbwlpsnVNSZ4}SWEKXN{vxJFv0aw6A=$5^(CU6?{*zQs9lZb^#o8@nC*rd_i=fqmK% z*{S1Lxb$$pqzRc!FjtC@sLubxqD%}erlCO5)kKb~-O+*8GS>BUC=c zx$aixt0h{K7}dl&Vy;QovOFPbVb@6|A6XK+F;-I^OR@8(9rXPHaOtn@G);{ZsmbhJ zEu7q!UZ0@Dc;s7*ag7%+l2UHCbSRcE@+j`9v>iCOifXQe5sW_`fBPw$jwnV185E3W zA4ZUz@)ZH0S=v5J&al z(vcIb>ER4F&D!o+){W7@a%o)GNW0u+7>>8ZbB6J>T%L#2+1v#syXgxHnKX=&y_2~s zo2UOX;>A;qPEBmdDTVFtbHQh#`@V_*(S^Q6Q43uX-ui3nBe}G0xiwh%YnCW^rNPot z!7kl{H?!;UDc4eWe2sf;I2Q3^G(WvEW;`sjcV`V!vsAC^tSbawwY;a3dM_Ytds7lf z`)xn5t1G2jndDGP=SzDrPpu10&(2j4xg|W`TCq4=naa_3`9GxujommXQmQEztV{IR zy-jbV1JUIO%ttJW2_ zn0|iLDjc}RtRryvb6RrJ1s93+mS7JFVfTB~%$;W9`gV8EFci{B`9T11W5&`dOVCf) z>+jR@%RBnl{~X5h4o+g_*EiR_{CR^)@0^coYsghRZ+2#1kR()2Dq#_we8L-5C#6K{@jqB{3UMJa>+C3JQE)ifpUm9KP(fy#rmN zTysk#9OMrtjeETQUH(7pRyv*~+mqMMm)2}e#!7$K&5eLJKP;Y#@NHWx+WM}>ZudC# zdGUBVoxM>GyeX@3-Dk<$px&T;4vt zYr8(+*#E&f&t;A+&!s=PBc=Fhu22mr-5OsJ(|NfuawkRocY8C}BgRSPU2a4<_sxge zZw?;V81C4$)`ua<*I!tf$`vNjTliAECu^x}(Vk}Nw3CL@<1?dcis#+7*n0bDM%E$> z4qVX8Cr_;qlaT7qJ{svpwvkmN!EfjyVOq#xEw@;@&X*G5XR7d7bbZFZpdJ70&+p?q zj&!qQ_<9&eFWwtcKlX0yL2h{HT|5zuYgOhs%*6!{8P7W*C}V_6-zqX;%Cs;`y;eW> zyqYd?8J|RL^n(Z~z3nAhpF``f(qKcT)7)-&Fy~d_+eH1h?w)NnC) zT~$<_jPI0Lbb*AG@Rra$4v9^x-8~ksEXP+`y0d9%JG+R^4--wc!R=!Pd`?0-iv-=D z6cR7v4Be~lTYrh~Lo{MjSubf?!;;)}(`IO@DL?93#`WZ`nB`Gt-k?k|&q0a#rGyV} z@Ya$m4|VFtymJ_m#P4@wjG(DeaK2~v)>wc3_;J&S!k{AK1NI+A9%bt$b?Y3W)tjN3 z@3&oRTZs@_4BOc?wrQ*Ex3F1d9-tq+l*K+uuAbeoN!y`asOtO!JO1lz!B?d|c>66+ zwj^NMr;Frs1{yaPhS>Ud?+f3;ifD5P2pS=Iz}8J&X*seL7_$d``+nRL{_QhL>DS!` zj`&kDJ)FTy^|(hy#l)=wIl>hia)bb3Sjw+|i|9!L-X)VmOIc%G6gSwg^y+reUnV$wn>DUbY2e%|tJf^=v))aoDf?cC zXSg?fIfROcfed2rHSgvs>Wd*CmaA8Tv#*O^Tg!dWsTUI#s%HfZE-^@SY z>~`qU=*E(tzOkKxpLn*Ucw^m^(rCoBSI1``eZtb$Sgu2Nsk8<2-r%p^7$PPDiJc2h6Bp;Q^(Pc#(DQ%9u$LF{{$sywo zFRQT2*ooW$j~<=&zU2z*Ot2HmaDace8*L5;2ZL+`8S*}N0a;HpU0*zr;v`l%jcNuUpzmYnYU18%&jKJ zsBpJp{TN5i*x7{Eu~^+HRUTsFeA%A*D0zK1K`{>*!67-J&B3+77&ppHzyAycnvPuU zw!g-;&X|z!_V~4Xms6gd%}X6PqsSp}zpn?Yzdv(Lu~9?r?)D2ID0Gd{Xv0ax-d??V ztfVyJ`QVU)ziiD~!JRX+u8dpHWC&fR+hY=W?bnTA1lMYP#OV;t>IQjpIk|{}=*N|O z=gcNNzuopXIue8P7YuCt`W*-F_Y)_#-|FqDEOD6Gx=cyQ|MHNt#&fdnhe<=Zgl^ui zG|T%i<|zaES}~V`dvhhv>g-m)-`~E{WN0z8c!OwnNo&dS!Z?P+lxU+wp>l|Yu~~Yf zta}e;B2#^ajsEcw-T5_au+r4}DYoh?RJ~pL5nQWj?kF3ci9h@xU%*B0j;elvvE%Rw z3G?>x;U^6f>v?{+Z0gwM3&KYuw&>I4byK6>66-#Hxf*)V6yq4Q-n17xL%~$ONQ75V zPQw191(B#K5kLN$o`J>3zJz-5N$>uWd9Nw_D8=$*dxYSXT@C$RvL@E7VTi(yMrXATdjJ^)mnQ!=*5{V|6ss8Fe@sTWlbz|AC>jv?5Euf)v zsy8Il<)Y(_6^Aqr*bQ%jc1Zp8AIg@beG54pqxYh=N<}eEdW9tvUZ0#uiXqG9XdS)bl1MwnOxOE-eNnNV5r?6kz9nL?yofX*nMTI> zUd3pn4;F{hd}4P{=;7E;Z>0_`+l)mKZ>dv%t}((f&l5GYn)R#-e0;c;!_vWC#aF*B z=u4w#Y`E1#kBfs5J5-t$j-rPPxN(tH+J6h!7>d4kVoJ(2D9ydb5IS znrZ9McKaia;8NJ9rpFvg)qO!nWZ&>K#vIjItz2hNj53hDsp{T5On0_F^cXw;9{dNC zZg*SN?3Y@lvP^O@91$Lyaxr@iR2)Begsk~a z@t$cT9xus>m~tL={#Ym6=A8$BL0Mn-&|yO??~lg_%Ql7-wW9eou`A{4@Qx=DQ}M?k zG^#{6`*I*8>5E=O>S%LfQT|-JgqHhE6K(NKo0EuP;98mR!mP(h(FgDC4$a&y=`C4# z(#t>7Zm2N8Om&y58o?_qtOeN^mj%rjO6uDFzYh0nE-o*Qb?~OfkMQ?5nmY4F5A?ly z8YW~aOdU_|)MqFjUGfvWT2ee;wpq3LVsTEnkX=#4X8FX?*8^%U2EsesI#E^7nHRKx z{Zo<&v2v@YAPJB^hdF#iIL1V^o1aoqvwLXwBO2q9rymzIH1Y3nTQ}_M!i%sRececD z%<5plZHl^m{Tr<7VgVoKkSHaQ^d)oMS@6hqJDh+#xa`H@KdUF>L8+ivEHrz%XZw6`7w6TgVPK&LAz} z>3w>jhEs^lN9VJHw%$;sjDm@Q!YJjTIFHLF*uxK8-Eegs*bC8)BNh zLf3udS-&Kc&(MnabK! z)3m$S_AY3VovblGV&_cFyA~>ygAv8ePf2wLPWO|MDi%r>$LI!dPE=K>=$YQ`e=&6O zKOrXQiB!%j7M=~oNp&OP^PP?}QD+s%A7x#Mae7-KHh27gr;#8|k`$v1{+qfUA z-DK5Ib$R;yXwKFUU-08#kEkNeAJ~&X=m>`8y-Zb9dx}KUlPkS!orwzF%59EoJ}l=8 z?k%lY^*nX|d}(OfN%kGx<=Sf#!PF~~+Wzf+?OS(?L?&js@6P6~lb$>6m_=Y8a&75E zL#R{S^$q+D6Y+qLB_A?JFH4Et+58~q&@v%h{Ls~ft@AQ$LEz;$mQq=Gzw=yQA@(yW z=FYySISNdPMeh`yQuy3U<@`0g%BBe-zL+F`u8AdY9i>lb4dyDjaVXY=(a<`eoik@gDp~G6PtYQn}48Ot`iw8Fs{-Xdq>P ztubDYOiIRc$n|;r)Ipaw^*ct>>dCq(^h92TEfH1IRyR6vxjc7Cddb2?VyJrGfT`7B zCicy&1gVtf{^G3gRkzKds4$G&$?|NS3;qpKdV%T6nkfSY_ek6Fx3^uW&^C)-kI=Br}rFi`pY+0|ru;_9%$amm5V zIypOa>LtCk4KhFZX1QQGVQWj5;GTmz1{QA*4WCgXIiJ)RWuPA6a8^P|XHb6U6}N)Q zLQt&>SieQ??2v`S;=&+CAIc8 z50wicMkjN<|A1VOKF9kI@scMztI}O4Rl)5@sJN9#p^xepSr`1X|HjlX${pW zSl&()xg8&;U?td9o$^3Kqf;*4Fnc}l*5*|S4Vg>R-=Vv^F2yaehmS3)!xk}jRE&KF zd(GpjBtP=1nL$N2Bdf0%q< zE+S9dj?E(VLUcuOf&3nUJ+iraff?mjl8?8lUmNR}@bBp5d1*ntc}YoAp0_sMK*7o+ z-?W_G*kMg&A+Sw_{@_wSE$jQ`mzgGh&9ANK(>N}=tMBqp^Aixq?L2TPd^AgpFr~vl zrqT0|kLO-)KYgXpqU`)CZ$^Xsr1GA}RW=(g-Ku%AzS4_oXXV{pXonp(MzS=mq%w@6XX+_o~a;T1v1wq;D!(IzZKLGQLg#@!tG46U}X8MH@MmGe!9u^Tm4tzHA;E`{4J2 zj*{heM$K6J$+IQGyNXgL+QiiCvkQbxi_I>ZVyL)?mviQ3`q#IviM%^BeuupCr3dF< zhN}w%b!=36CIJ>j5hFW%);|Ikr}SEO7f!7S>~PUp*a}`-j#Mu)8)SGsvt0d(y%I+` zfnRYw+RV6p!s-5H>DNZ;VNJWQY<}LqFd8>yyr#5nXb;~jC3O8z;mlz7>Wa1hyF7|i z?{M5&+MQaDXL8t>67t3hDz#VfFUYd8FmkRr)+qRls3iF+?N*>ExvjQ`Yn9)Sy|e9Q z1J9%*!EV|fxx}ab4wrrNcE1~Pd7EZHU(;0Y$|ACL<)DssZ(OdjN;2lUuUM*irG0Z5guvPUL(A>^8cDLjfZ{MIQ`nRaPF`Y2_ZTWR6pG?E3bc3QlsJfXZE zOxrnz{evqkL*e;J<7!l830kzzNnUmtW4}sW-q2VwjM$Pa8>8dSFW2VOq=+waZQHL^ zF-uNDcVA`Wu20Gx7pa-aF1u9h)k_wy5*9X&=7|z-@DWco%aB{p^kQXY2b&t0X1v>z z9X=UQrV;|@-vG{_A3ERC2G1GBn@w*{zhP$&(`(Mo|&B~;X zAI)O^!nTM|_M3UsAEZ^O-C#{UY56kxi)XrPoQ)&ie3$R!zv`GGS5Au3*uvW-Wb2c< zl$9!^!L$04E3vr>2dLGrX_ttQF(~=R^cvF+#;k5E9JCy}qEtNR%yy`ijzivwo>OJ9 zDV5#&aL54GM#KU0n5mq_^VE9DCXuU!#Zl`I=1x8c+@tcEXjLk}L~pU~HYFiZ!_$+T zb_Xd{`)?zWFADheH&nw8 z^ASU@tC+fQoNqZ-nfzwi-FI1KIp&sKwp4G8UUI&Op%9kHmKu|Z_sofM+1Gx=7bse+ zBPTupu=}}K-M_bY9GO>e3i)g$;~oN5S%BY)n(a-v$lrsI~uc^nFbONHdg9O1IKwY%KH2pKk}*EW3vB4;ui z(+w$Nd{0GDo^}pq^9mJC`qJIC`i0ckW=dy*JYcDs5qGsnCzT!}o71y;lax5#s(a4L zS2e5t@Rv2AS@kEj1Fs};R@aBut{S_QFIaRdn(^B5RQh!r`&E>rS9&jR+3CEXdE;R7 zENZ8|Pgg9p7m<0#+9JpELZ^=>Nq$<)A8X58fJInSHzN_+H#5&OjmtO9J!QUYn#H$W z$-sW5^Mu0w$Ni}hGW9${jGaXaj)|t9EaMl=no8H$_8ADLJn_jDa%?kIC5u1%K%?+N zDMehN{-^vvH_!C@2QlGHe!Zs~b;h0VCz}lzPQ^`PqINGh6>f%C$37~)n(D`L-*7_d zK`Cd*7cw<{#^r)2zW#O%U=cmD=I~3_-##c+!+&Ldw(9dtMdysDQ2o{G_3R}RSk8K+ zF(XxL&a0M>igK_6*cNThXXvq+EOPj1hXh5iHIseb<$UXtWN@I=4mDc}+kz4OWHSj0hpCR_b&ZGaF?zmDV8xnJcF8ba6v$%=_h zf|a^!fyG-7eclT=q_z_jaT+nzOto??u2smF_|zv{?q;`ocddlIm?tuFcQRgj#`rB| zJ)M-tm&M|pn~_5tZw2FlG2F2$v18&L;}xJ^~P4$2a1#%QW@bRFcdBx$+W z^Z)4j%AhvXt?N??6)4b_0>#_nPI0G#v}lpw*5U*y5`t6U0L9%2p~VA16Wj^~in|ls zwLpSHzVzIC-uL!=@BPDs8HUL`viI6+?X@2dZe!ZIy_Sk3?x7Eut)gwWTbboe(AeU{ zr*qNcM9-M-V@|TAD{5yM{Cj=!S^;5~zbrX`W_>k5_)8uP8tt zP$FY&t6><^)`qe|`W)Eqa^3aapIzhtjGP_WN(0y{)mz8I?BIHJ3fx!1owoxKR_MVO z3J~F9-BQWQL}NOh#H8eI3)$yEUSp5Mk+x1H)90_F8QpJ*gVqq5&=fJ`K|}uAYv4) z#{B9LN5xOEO@qSvl~0-Ct}nL1NoVv)5d)~{H8-Pr-`64~ChD3NQP{IGFLKX4lMeaO z*1U{2rjq+dGTK(DCAiU1v-#*4Qt$6T1-(YDY6QChx&2MDvD@>XC+6ZA`Ill@u8pW` zpPCJO((vX-yxnF!yhswN_0cG_^rL<(#B46A(j4DM&3|qkQJt?2tiUUQ>jkrS?vFKQ z2jP1#`|gZ9Z4L=AF9)$nTa-o++_zh$QreNuJ(7Cb^s=GjLM&@EV%XA1vf;HbeKHZ7 zzY+d@-3?qz1P>bg$Cbiw0;HwZ!gxfIuzJb^+EX-pPqpg4IZ&f>d%Tr}b&Cr1({QB6 zH`|CO(VM-U_w5Gc2A!8Hc!Q}!Pp8Iz^z?*-v-i~DYA<_>L@LugpUxN}bSoU#ir5OD znFFSMI9R$5{&drY>RYQ0BOwI|mv!(-WcsOJWA67?U#Pl0*}OlcwVhcgiKKyL+>{p` zRt>BZY#w&Pp88SN)|dH&gD z6=b!Knp!M-X%?QuYPTx|mhBmrPzikIV#~WV-1PzXve4+b(FMqIrB0PRCGz_iuxHCv z$W6_$1+l*K6h588U2U?@0`55X*vttdrPwy_QGxJQI}bF=r|Vc1wYfg_(Vh2@RH0bI zLhRm*R^7r?aj6;1zJVv2wwwixriS|z00y4%#q6;<*X`i6*SBJj99~D%tJ+{?cFq-l zInKmdMxBtNfep*~GroAW+w3+!u)0<#di!1?k$`=uLR(wdFd5Yi^78N&6!~WZ$cHPi z9s2cf3X|KRg?Exk)0g=6`h+xu#(~p;B41M}Nj17~cAPTHz=HW&?R^c$0Px4@EUI3} zD3mi9c-GBiKV!lThBvOKcB?j;bDlQrrg}G*S9dkd&DnNiz0AUn_)2Rq=Z3MRnj2_l zlMDa$&@G>y)T_7KLY*vsMvuQDZR&n2(d{}tXVCv+Z@fR>&-}b;8=HI!YZc|JT39G5 zPWJKQILIFV{%3n!`-<7c`LM)q(DaEVejGi@%BQMXqEY%Nx^#LEyTI9Z2;m&p^JsA$ z8!`<#sn#kQv>W{>`C2|#m^DqgS|n56NzsEz`8&)>sqVbJtQry-)W5Y_d}pG7*51&H zK7qQLWvb)F5#Z|L#pl#BmUGd18l!o!hlecJvOrpcMDzDyEpkO%C2Tu9TJ5zzCv3j@ zO3-w;2h_8e)cf4Br7PhudT#ke(gt7FsBSSS@p;JfP3ZGErv@B@5cZ;_?WSh`z|qYU z><|Zy)aQvtuuS(j?m2ULjSxaZ8*ug#+i0!fR3f0Nhr7#}t;qb)F$!1WbgMkaUbMOq znE!7z`=6iuZSEE;qrO<#t39Hv>psnadtM(F4u9>*c`&jhcA{(QlvwXuW5&3H5UiOJt(MjH|43k2mfrckXLj4gfF_`ELyMnP^JVi8W z*HP>%NcAYxosK}|##Myo+BY%|;#W%sd%L@y_Pr&-#IpJo(=s8s&#o$UCyBs8x5i@X z6{nhRp1f52RWfCJiJY12%k?0~?mPjKlC>DbSF6uNBf5q!2XBBnnayDhg=`Dfr>KKE z98sT(_y{4=Hq^{7>1%v{YRbL(7;ofJ8CBXYJwv+lW-CVCUWw$mMIqoCou2rhx~O2Y zLUnHdjcusa)bCyvhFUqFDC_kNBi`f42i_enJd0_!EcGw>Z*_CVLWQM=4AL?B8+a<| zlF4eaey`lq_fenAmJrJ{ly;JBp!~RVh-VY$4>^q6c4?(Ic8zUtwRwgd-AJ$Q()A~< zg4M=VumoP4o1(wLZ@vM1994V*2qIcQ?`s0zHrCk_Pr8pC`^&yCN9KphS(z$v~ z;9cBk(}50^$%77qQ=8%vPbS{p&ui|_Pp));BF4+(*5QevVUf-~6|lAeYcbL#$T{SyevtsoACVn3w4e64Agrl2x`^^)A4Ck#@5x2>-9AxJmZO{(dfCKy*Q$SBTjEHiS*^wa@|JW_d~b}TJ#TOeG4LMM%ME~sYC1gi=q9Q8W^5_v29?i2SJ@ zd@R`PuADZn)2M!gol~&G&P54xua8>L%_Z5*DK1aNk{WosnHS$bk!z%4a^TY?K*>m3 z)D2fTdVC@JZW3F99haER;wCUhG9Skzh1)g3Ws+`N?lf{^?izs_^6z@7h z(dn1i(N%utBO@|6`047ivaDZy4)2oF`+vuIM+Q$;iKZ%qC@g0+F3yio;}W}0+lYO| z95J=6_e!z3)kTkY>=FjPe#pa=)M1TBT$)(NbEKB)X}s%k{30f#=It!jEOFxYz?Ro& z>UrnOTS7jX#p4Rn_MnaFLQ?p1y7y>q*YPSZKeT;wqRVT*&g}IK;Ekd$gsILY%Dk@k zRK>(AZ}Yn@>Xzw|=-BQLDb=}^zs(inu@I$oevIYMAP`5*QJTwdq}r3`;tEwmiZ_Gb zl6g2}l`nQXXgEbC_SHmoKZDB@tW=>QQqAvF&Fxfl%Ffc(9kI*kpnNrj*~{+ZhqnY6 z#~B%ayI+5|6AqE-B0lU!+%Ar)8dv;mky7u;yH4gVqujHu7Dp-NthxvsXJe6BikO*jCWlmWdD@@4E5g zN`>o|Ic|fm?eeFGIupD1B+Yze81d|$C5lo6*MvJag|BcI%8L5~W?X_ItUf;H!@3hF#NGRc=i6*{1I$(_964lM z3w{oYP`Lwb*v_&m#h}vit!8eq@U(u^fe2HoUP~pa&y^S{yUQ%L-NL)0{EKGWJd36I z`*Qy4!kP*bBsq?MD^nM`CWZ}^Xh*lxR9x{2of8OYuln*U-_Lh6qKA~?1Q63YNLs}- zI^&AW0eM|KAZTE~6|KoDNgK_{)SfyNKAM68a9_OB;6u58Q_W)J>Sgr2XD^vA$n(+#x zqnD_1FOPhw&!~I4Wf(4Z=(<^2ose?ucqkiOK~E^`{56DB4?ZS8;d0cHoZZDvrs~#OIG7PZntNV|l}g4yg)0%HZIf zhFNn7i=nF$^A&IfrKN;fnGWV6UDK6?lq+=>fgm4Y5H06>>K#YepR8Y(&=mStnTY=M zuQE{#fGZQ%;&m^W%*k+vpp5B73yhiN;Xg!|USfZ_bKR2mtgg^oIt7cZlklog#&06G z#@uu44ig?&AJl5m-1KZ~*7E5yKYnMhKU?9vET(n$dR?CfbIOo1xeYLDnt!wVJ+&qB zOUuod$IEd|Sx344L&}hC^^#6ef4QTs(NY-3u+iOX z>*pvj~BUj;nYT3GcsNu^M&6Qjt)D9Xb zrv@}nZdWpRkBk`cTFKSC?E3{1rGx3JS@aes9?_LzVb?w_0!;#}zHUD2ZzeyX%7ewL zIJ#OhLc_K73{@s;@{OE$E``D#cYS^XnpgpXyH+L}+W@(ouNlQ{ltsNagAKITqKc({ zrm~v}Kd26kxbr>k#iNjoJ%?V)2I-$=7&XktWLK4G#>F_Ul%sO~ry7b(YLBcSE=MeSmJGR(G-4r@0&HzjSDeaxv&IM?O=D*a(Aams+3Um>m} zTgz_2HJ-Mk&L}5+2R6cB+5Rx#aDvI=Q7%==$CrDhV+`@HvW@B`d8WO{~U*0$9w>bc%oNueWk52drpVpqN;u9Av@FS3kEgJ6lwKT={m`u zCha4&R}X;8?G)O@MFrM`F3qFa`+iaqhHbeS(Lj}gr{8KW5-dGxRdFv@NhlWW2Jd%` zKI+(lDI9XC#(B*9C3;Mjs0}#*5$#L6yA{Q@a@^)Zd?ZsKMP6txVA!2CDSzWid}R}Yo$+ga*xv{(n|CAioCB7 ziv~E*fnFV8I@<}E->E-!K9vP)aMgnga}i4WNWMBTeGOoE$*s`9o$b@mTu*tjt7_~A zxC;N8oyxqIZgueasBe>=ii(P@I4b98*8x>C_O&tnA=iYM#9gWLYA9kr6+mGtb`%e! z(3nz-D;F}NV0lnw!YB3%2fl8Rn9L4-?S*i4l^c8F47p7g^Gt~72|Be({c5FYJ4Ex9q&tdTE` zu-wDXM_+Hu_!h05SC9D()Lz{j$J4&544UE`A*ys%llY5~8@^7Q_Cm)!pBv5@rcLIj zB@v78qw2?lIP7a;j3r)9y|3%W8G%wn@ZL%^@}uIFW*$iy{Xb5gVUz1weC_M7!{ z3^nFtN6VN42a4u&OsX@MX9xVU1JjG6S9rqI-g&MW5~&i8qiE{P@kja-C&uQm*+XA8 zvi93-1o@ZSfcdfM#kgUX{*!Z81 z|HwQcJSaSuw~tDz0NR0a`PTJz8(jydIq<6#S$>!E{swDzdiDHQ(?Nr}>=k1o1%k?E z+m%Ha#CAa(*S)Wp=FttxN!`SYF20u;O^IHvP2%$>pEliWyKP5Yj@4?$&MT$uXObQZ z0&&>G!bbK8@{#i^&v2XCW3yh1`oQsJ@L+7-#hP-%)+b|8b2GGG=ol5#a$=H&?1A9e z1qfxA|MQIGO7DD}ilGMs=gn?VPJh)aoAC>Wbhr?=H>FO>|KU%-6_XXnYcxv3prrYu z6rhv1b&{Vh9;y1ydkyU|azwN#yMvQI^LZtG^y|05@~%{zGfD||i~FUY30cW-ZW`+w z()twW)%>I1cD2Qu{EdbKT#0;Y{8mzjr=Di9pBO^LAn?2SaaE?)2!7COf)Y}-9le>F zT#*ue>Q@b#hK+>zWZ~ku>aWjHDvbB|=T2mf9C1(2s@_^u?RxmpRIkOicZa08Fx-tt zB|&1(!4J^Zov}Ffj!qwXzfy;+G^jf8! znQ5!_(lUglW~R6?O@Uqz$hW{E6@r<-Utk7?9GR5yhAD0kod4#3w(UPYnJSsaN+$vD z1#9cxNy}iXenm{#WV>j*ltmqVs$8+bHnFZ@FKa}C)xc1!uX?SIZwMj;PHbyO83~AW zcL8SCE7qU_YWQqel+t-Xf3mNVfj5gCiMFLhpELm}lsHMeuCqakQp?&*S>))odRlMw z((Ss#-f^~DdlAy%F%(A1ioY7u$aFJd*~!6ZcQbet^oLI4{vRc6x!tdCCsuMZZ>$jG zK%%Xvua<{}aAs+k)XQf2_N706hR0PE)&BTW%ui#Im(9^JukJPcy7%h>Yv(Vsg{j(t z(Q#Zn^>0ZL*Pd7VH0JWm$9dW|%s?T6Nr9e9M&75%!yxKPD%a|8nnLs|5rMueWGI$t78UV^mq`Vb|2dWGCckpEWUOs4A-CB#bM1+fohNA9w?k}?> z&@Nkn%t4l!foHwuH`$1A$-DGL!!USfqATeEs?I}GWdD+;)>Y#o5Wk4zf54f18EjrI zdhG5Zr48_#`qbqJ(C%6l)o>%X$j%?e4w9ta9X#w zJX0G2DTJ-5a~@7yBAZ#?^i>a%mebnnAr#*>{qZ%V6gH*$vEYHN0ifQh?J1oI7IM_0!EG-1GOPiVZrL1}ROP zw$HIAe$2~Q?7WO&QOK|c!dn%!e6lg`JM1v^Wcu#FOmn|8wR*~dK{|pVvgm(Qt8Wj)9IL5Y5qcgQJQ3w7?rN-xbLtS{0PcrCY zdzpYP&wefV!QIxT@L4Tf z8}*TPOHq~C)O3akL*8w%x>M)!xSAr`4)!Xw`OjHi*Jq!UFitE zyEaV``<%RijC<8%p{lY0*e@zold^MO`F9>IWv^$Gez}=?mnF9^u}#Fa5Ood;bt@`8 zzGg7{YDM5js#D==1)x7u`0#1&_=eD+{r=zhnYXoO_NOvl_9^f3%K+Z7Sg zO*Zwn_+{Td;V7z-FN8nB`5#^0wcOE`s(78w-HXs)z0T9PY3U0Mr2G0-{n3WxaSlAB zhb3L(3*&qb0E4TfP6o`0rv3~5PNn#Fp8FPP6)>*-cb<#T!{s^j-Ik99EtthDxfF&5 zT%J>nB2LezkDeQkapNgLL!hn49+iC;r@-4at&vrIdnIEY zwFzG3VXmyUa_Ohu-moV1^z6JHY6D8-zo5#ym)DmS0FuyOwqO%~@Y&w~+IasKPcI*S zPOdB%HjYOz!}cH3_KY00IbPhetxLF=I!M&nCASd)0k^k1o=g~eA-7CQNqUj;D)mx0 zSMjHa6icsCgMa6glmWcPbw!*TsT)wln|mKF4lTvF&g^YEywh)r)O&@;Q9N}80(sz( zRQwk^>T~X2c}Tc(YgYWwTj75EjmCG2mD)u`@1Spf5?|hnv*`XZ1QOU^D+0*WQ}ZeA z)aI#5R2wC8HKo&iRz=AtuZ?cBRWPgT83FW*Wb~;h>0T?!;m{8I;D|u6#9B5-Uz75X0)-&>U@taraeG#r$Mbti%g2zz%3jME%s%i;I^R8_ zb>95_E(Yd$&$PfEbENRrXv4Z>4t~<{4;@2Bd0tJ+52()rg~P(1PPO~0{QlwR#G;o@#R}iL!X)Pj-XeY1Ko%#Pdp?bU4&_NRueDbn*fne+F6 zBZq8#%QQ$T53}>HgJL*^E$owym6Ef!HZ`eH*Ub}NV#t$jE2|}^EH;tndExt_alG?S z`?|whr`paa0N=YPY5aEtY+Kay&k8uoj1|26W=eN}MA^|^6x>AA1z@nN%re2op0M@VX}C)OF;ILhm4{B7u@zrqX+ zUS^ND(encpYsO{N+q&=CpR!|9^t!EfUU}4QBZjQPEER@4cGZM3VS2TI^>A9@Z5s_e z`;b0KT>kwXApPrC|1+*Kwu5+>5DON01QQ$0x<0tqMhO52d&o%Sf1D^*ZkajR4yA~w zHYd5X8}qx@>u))Te}VV4Nc);-jo9G>{S!Y$sF$aPyD|ElDp zb^)trw6fgbvkIIA)0+Y}AVbqrnp%z(u#cJoEWvB6dJcMc^}^F~bjp zNVmTFWx!X7q{qA=eZrA(D*$Z#hwZs#=SC66!;|2F8FP8zkvZ%h6?@35UvXuV(7gmi z#;zy zr=eT3Mfzd*sIaIy{(tG&x_^Xs;7kFS-p;L;#n4qdG96$3oUu*&v8%CX)v03)5q+E( zIX*ribdq;&Kh|s^?{#Azm4`NH4N*qcr@V?fm2R_A2?i>iF-J9DwSx zG8RA)eZt?<2boCx0d4fV;Xx_>DvJWL%Y-yu?D}xzB3kx9Z^pc@bVXhH&TB-E-QTC zZsWY|^L492W>@wmx|AWDVbQAko@oab=V^ zkIr>$ONm{!DD9jphAeaMR_>~Sdvl8W=+1RrT?);#Y)BifWK;O`2_T`L^LyL<{gisV zyU=$|QCDlVw#@~y7gKpFx{ZsBno&U#G2oSuv^lA!mjE1{r%6orvPbXinsmk(qwL(H zcd%3pudoG6HGRzc*3T-5ex6R!6f62oF+=h=3i80blf;K_$a5+l`O7~8RxyrkM{D& zEo=ABs+DAR$0Om9KMW0s(~Ck&ujle6N2Ab!z5qRoy-+f$RZ4`&k0)SHx6s|GeUybB zY&&z#ym~z*(ge zpsNy%#U6n@Ct~DQzG(tPud;M)?%_||oD*}0m45)k z6c;?Fb90BpK5a4N{!y3k5x!az+rj(9>BcUdW&wH0FDsNTeXpl@?=?TpJW zVv#g0>U*)fOUvV157J-OzpgHhl$&t~kNuXqSysCQWJ1Q=#vP2ZXA5IfakP4LkvXXYmp~~G=0g!e%=@~FXEuIcO72iRrrCqVGuE%U-Gl(9$<5mZ zs9abrcR8y^LPHT#+dYrEer|sRXAkSZ$G0q?gSB%X>B4oU8Qp?&x5rSFCB30Mr*>Q# z9m!Di6lvqs`BwTDvIr=~aKxjC@n#2g=@e9vYM=|(eO_aL_qtgV3aK+Na24Vh@D|*p zoG5;@12EbDo-LZB-((kxe9@EWCmDH{W)|)|0~}ANs#iMR)K$tCrzW+*BuN5Rth)42 zLd>XB|L>80&uB~zY&m8NwyL_s)-EJy(4j(lWSrl|>8Ep7mxFF!cw`UEdF2*rPCPpc zBh&_$nN{zUJg1qWCCvtSc~WD_5t7Z?37J?Jt_77HicaO7@gD4Dn=|^#8Vx!fYx^%l zJieE`FoYc^&O|7q7um2-Xqx4d_YKl9Hja|jUrz~$PZ96eM3z^}rt*e_aY<8ilw~&K zOgfm;sdg^?!P&(#!%eq={6;Lwtup49rR8(fr}YX|9c7ny!s6Tl+#qchxCq?mJrpb*unwb3`)3Tmu+?c6s z4=%FKrI*lp#545$td*5Mb22kDa&*9VN}I25R~)$VMka@zCcY)A#=xtieFs7RB! zxM;m3%EE7TqCBn#!BVa2Htt8aQ7}@0k=XBbzD&9d5zgnW)_QkL~1T_;Ttf{XEdK)f~`Kj8;u*6?ALS~z|;>GdlsmQG(T za@1834aG3=Mxq}=MGawzTzDu%1tG^NHvjiP_A^` zA4@RF5e|0R{Su2~KQD1jCYWW@UxDO5VwI^jLCLkTfK)^BX(4a7hGcj-$EH<$MCyv= zVdBrb-&IbYl+LQV7UotET@hF_%*g6`InI zJxdTjZwrzpE`p2e6bY-&UAk(I*&2K}O`mX^2sfw@bx*x%&x&AfR`y7*E@a$Dx2-+) zt{g7)vXp@5`+l&=Gk2UX@YX>hOAQfQYzwt-aRB3|(_+Oz_d>ftyM+k9};0oz%` z-a2%A;ProjD%_#xC5SlCs*fj&IvxRB$T9c72+?so{k*Gj7#ocJRa-Dk1 zUo#;1I_%@pahL1UCu0AfP&1KpFS`U0@#{-O*Af5l3#-5ko8t}|KP*%|1Ad}9@C<^O zqv$nMm1HE6atD>Hs3)GGE60ss=JVB)hbkOJ2#Mi2m`}&42rur)-~tDpHe(_q!|!;p z1x+w3Ggi8L@4jB4lX`_S_pCW53M)1OcnjAelT^u|zv|KarWPczRWjHC2nQ9EzYcu) zLAW8XQQV6Q%oURZ`pT7T8(yWllWNao$NkR3{5g{bFjct37$$|AQaB~>BwB4ovh8Y1 zQYT-ECdtOdxX&CDr2+Y&0+}T(2hB4yh)=rz)n;n6#6#D0#ZH;qQuOY8*_Q#i3#cyda8?nxUEdF4J5KW0TmEP4 z3z&OF`Pu4TdwOv1^rEb&<4L1(gPtk|jhRWStbvfMiERHlRRF_}(WJO8HOp;9az%r3 zBal6c`uIEnC~3?uIS8-I=TA)9J({wuUC$ONAK#C~*2Kk}j7iJ5#zDEs^l(v1{Y!bdq|tzhG~MFPMK*y&!I^$;@0!PB zpHj3NWW>Lhyu0KCJ~{0M)Ju|%^^#o|uM;wz^tvo5Lh`d{daPpZ!Rq1fcN)_vUwX|7 zb$E#!ed>2Rww^r2JMl@59ACz`3tHcEt%ksbn|dgLK1pX!KwU{(fNEEpkCTk0YR&RJ zkNLW-Z6xzp8EsZ{Lktp;EGi1rGouWLQWAx26u%#Uzw3uy9e+_D{^~8P)xPmUcFpz` zw%LX(4Z%VPC@>|yqg_zJru(hxW11HGC}TJU;`S@np2!_xkOGyI4Hz88-WS zPs9iI@!v>aq&kUtl;B5O=ZTy7e7tCdU2*m1Tt*slKZkE(3Pd4@(07!%W_WaGob1U(M#udYRpkBTX2kzF_D4^=84 z>$N3FR>+mHF-70ArvW1X(1P;Dz5d5w{|N6TJ4{^=H3df`>c>!PpK4~mPJS9HvOLo4 zI2~EU?P1bkZiwNBgSf{?9R>uR~LZn#sax z=#MG~Bv5DsYqe0kU}R$e5aQHfjAW|Mr9lNBKT{|ei%TU-ghJqb-$?6e;c2Cpv1G`c zuM$^^07R8?XgjiZ3rRPX_0?&}?`~vTqc*Y#>au+f^mQ8JlE6 z!({^yj1i@pXk{&2kJhHTTDLvjI7r(x zqCNt9pbhc7i1j_Q721wm5+U}*{%3kmno|#aOwmru0jni}vqRZ})?)#w)R3LDNQdwA z|M!hUzxHonXK(>k8Vzh&q)}XLZ#ZON*nCzTPReIjHTNfm9xXY+6{@?;P#MGZazyqI z-{jEt)#mf(y6E$lzfxgG*xAn(+At~rU9Y)zZ4});c%*qYrbkRu3uUT~TkYgjYPol! z%5N|4O-P-5n5`(ln`hqzc?*oZ;;!}3&~kCR{~`-|2hlLS+5z88^YE$@58Jeg4m2MW ziK3<%>F(Oo?n@Qbc+HXy-R>Iy`JA3{Z|9V>wcv*F>5R;)3#(&*P5YYETQg{*$H;lh zis9VTrz3Ic^=DZjM;HKgv#FUiB!}PUVonsQ-ktu{afdCuVB5~6i?zArXFehW`Q=v_ zG9`>D>jwR%%-wksBkVuE9I~=Ek%9DFOE{{Bif3+oPQAO|qHpLE(qSRC*9U5+;;gpS z{IYHNfS6jV(h05&hYxmyA*ZcpokWf;<{miarsG4MjzG3x4?7X|U6IkT!lxW>g=SUG z=1_Z?EVjjCH<=fYRjZQvY2jVrO2``P3VDTDf?>@ngZ8SIbDxbP^N*yw?YzFa&eyep-R6GmfgX*B&u@vY)gaSF97^czCEiPxOB~#GZD3xVF2|-!i!+Q> zOvOh;4@@>X3!Z#y6R+}8ieu7^?iL=~a-EUxgFeCv-*jV4>2xLx(;A!l8YsaLKbzHB z6B$sgEJ2c~v7Sw8-ThhC@60M6+qI3l<8Cb7fg<~{tS3jO=FBZSrcxuI?bs{CVQvIN z8^iryvPRblE^a*fyaY5GfP5yziMy&YX%k+2ZWTFtw zhP7uS5iCydNe#6ww=0cErVjgV$YYW}f@jEWv9&&Fi_$VIvqACj@obL1 zlONv%L?LJjqfS2H+o7Ee#@s`$tysN6yUyqKDbQBl-T{W;hQ$#h772y;AMWFGg@Wl{#WArm5gx(f&;4GVV0`FNT;l~B}1be^fTYUTwtpVs9uhu%n2ep{${{!m~?)sQd{|l{iHTegsbAE zZy+-Ua3lySJTlO6w6Rt%?28O;d#)c%QboIYxH5Z$Nf+|AjeA@o#=4v)2{epnjO%S^ zd$ZO>-~8k){DsqQp&MAl((S@W%i6H#V%UdULR^UB>EJ%TJ}F%18Vx8}Gx8}`sb;-s z`Eho$!%EY#?%YmsDoV3s-IOq{cQs;Mg&vM{aMlj5F4|oTa&mY``?w_ACOI$a;Fcp_ zj5*=kFewWO#CGPFG>ImOtHgpYxr)Mm4GIx+Um&?t`uDGkSiW*y=jGSPW{%tjXi~}x zI_mqmzg`;nmLXD-xL8t>n0eK!TXb-u+Z5m2`HXGp3PPox+mm;;AF=&f1bT%l--o_sF%H&G zCp^qHa>4Hx>dn(`4sc{wreuY=!+Y4 zdPP)k%ueV-2R}s05aQljhfGT`$-zjRY?_Zy2?>pF$6|83Rg#Kr@o^x*YZ6dl8&V2T zQ8=-di~aS*ytg>kthF~icg02v){fwZpqy=qt{n(&p!Piq|BUwpY1)DYN0dCHmuD_Q z_13{GkxI;ZV~=`<3hrtOdhL008;Z_v44kGJJ|- zkt7i%AN?0LA(PzNUEfdnB!0VsrNA{_WV4|s*@vsI3$_*`c7D2E%}O^@^{P!?g&`FV zk|5n|P!TO@t9ias(wBS9nxfwY^0|mYV!rYk|42i`YYPC(fPxQuiHV>GlS{jqjlbz=amlPRH8Y!L4)k zpv1l_mJk*8q2nbUm3n&X$!x6CPu(f95Z@sryf3!3`)V%^aOK0~C;nmdosFM{lh*#u z6TVOQ{RMTcMinrRLmHTGp1B`j*J~rZBo5H$E5b8baL(j6H|ls%QEv{YTu#Fd4yc{? z!fQ&41JVI{Nn0lO79DgPW$c)I`*pt6be+FBE!&3gc3!K+;r_gbs}DQ@*5VXUYgpyQ|=$l&Y+AKeT$nUAj;$diM%Q%P04_dq|r?KSVaQJyp5P?Js~ zwD&yjgFWu6h_j|g&}g!HuWrEZ78+^xl`(Ks6Cda3t&-uu*YLx(CT49_4g4VeaO-1FcG`z{(@E};H6IRY53>Mwi&RyF<6up( zL*&Ei1AokspbG%}%$1%zxo;w8(f-`Upak71ZeP4f?OHtRr_E`_`P!$^D7HDBz_f=&2g8 zE#hX|5r20kf|ZQL^QVy=+3|zNvUu0&-VA5FJ~rog>uP^QW1dUPRA6ax>+(!%oGSwS zJX}vlz0WWKizaNv_K`S&NEJ zV544<@}xbLSgv=3YC*)1K&MJfqYS6cP|Z)1g+l2*ulNJ-6n}_ca_aI$1nVc&@OUk% zG?Q84qdbh7wVVGHL&_S``TVpL>2yY$TbC2WoT%^7X{b_bi#R-TZkte=6>I0@V?YnW z$Od-Cb*y+4uco=VG5SbUK`04W&jWMQ6ufA+@jOQS(mT+19&Oc#)>!bYK_4zs8}O;_*IS0oxnS^#w-3R_l?UdDk)CU-c&N%M zt-H|7-NT1OeVbFa=sXs4j6LM)&>94Dyng8{4eQiKUMN8oj*ZApymr;Oe1Tu-9ZNsv z63L2bg$|spTa4GChYcG+t$;&ADM02)gJyQ!*LJtHc^S=SO_O=k%LTu_hl#wLTmBLs zrbyBcZVOz9zv-nEJDG1b^)^YREI+6dbicYDA)c^lE%+((iR>gPN@MwURNk`Z-kXIw zpP?FutNE>t2IJ>VJU&N>)K-S|`+obbjgA$l6EE?#L1grs{N4-WeQ}fj9R^yCr&2$T zv+X}S{#bq7Xb|xyAO!5JBL1%PE&K<4X{2_0tBQEFW9s=5YoP$f3q2SNfW5g)e+|r2 z-)BygK&*{)ph!lp)Hb3lcZ&*#b&`rfS3Pgk4r~D>4GNX41OXB1WP9VPMI^hIQQEw^ zi62$z9aHIbjq=V$2D{$Reas-j)Nj8PHiY#QA(yHQ1ep`%4(Z004Ar)p zYpT-Az~TDz+^g$A2^!pJn1AEPK-nkg3#%m)677u1l)z`t+3$z8-ajoMn;*4|xHg9!Y#VfVYi?9ewMZtxV2eff)1 zLnlxmeUpzycO$FgWO1JyBT{ngGI!Vur-?`nNOd++b=p0i(>SPhB;xk-Wg z8IRA;UgN-$XT^5I_ct9SitJPumtQdQScl9Vor0_NV;gxBZ{4S%Ozn5jwJ)X}4~?uZ z&&mc)kG=o^a%cK!9Wv`8bkg}2n!%gaW0D?8)U~8Gp5)XbC-ao+Yu9jAu+4#cX%UTz z-E^ZWb0sU)z@uoNGX$_t(1UXayB7)PCbv~+Qxo!wyO3${T*<@OG;2o)>~#ZK&dSc4D}C$}Poq!7dAwVQ?rr@!HXeT)4Z)a?8O?fw`&zG$d_47MK&-OLDb_FyX=p{zx>PW=X zwlOIC=HVhEtPr(CZplUPYKmC6kjNTCR+E4N<|KGQ?#>0sVz0?+4b* z`aJ*PP_`QDHgw#d6*@X66&-Rjn0~pWo_Ktqm);{hTuX4BrEe>dW)NS7&-ap`(S1*d zn9f!eTo7y`roG$|rT*=+Xl&=_N=1tiLnv0-{e{}tE&$a;J*L;3ed2zP2-tb2gu)Y` zrJIv(Np3s6@>W=+R?)(B`ak+027PWUne-Ef`Y@M0J0@@KLg21ZxpIWxgA2m{N7-A) zMV0-J!v>0|h{7rzN(l%E(ru6u0@5)^HzEw3f&x+s(%m_9!we15IdnG+4FeLxJQsJZ z{m6cQ&mZu*19R^^@9uLnSWSQV;>YO=1}}^4ib847cA$QHJBbp6lESq-9Z=wagyvE`kt#bUFq z*Ovs}fBgEHQsSD@#y}UjWOO#MY>X7fwV_mwR?&B)9S-0ECp)lAp*&MY;(2(HmYFb=p z4_P1%zbK*AO_M!fp_Y*G{7Ki4WpVmM0i5V)u(_CJCWC7F{X6y4QL3@-ezk$`BgQU^}fZXNrIg) z(DvNqAu^a9tnl?|op^IVhSvBn)%I%>l*4SmEAw^hzlKMK_6TH*c!J)iu_3p?-Qzn< z*?fj$C==D2NkEJ@V7wr=KdBV87u#Eq_?DpGe4S{}OyK~fy25tA+xeP0q*&EqRNmTV zc{2ggLT5{$Z)VoYu@IzJRjOyeFc=>Em}^OsR3L6-WE6FW^?@Iwj?Z=9T5pym#W=hl z8^9jMo!eqIFcDd8bH~ePzDQTE4O%GB(?0ec3iGhIpZN=0b`JvK)kEzd+Sp-8Zm0TZ zhbJG==Nb0XH&-rD+JuLs4o%z7(xuf`^3C_R$aD3jI>0nj>eHH>LW>=tj;%IJ8Q44X zC-&CUmVxW)lwEH>Y`U|VDy$16gcLTXj0NXfrlm@$->jf3tPHqXeAJH@xi;g`*?}D0 zoz8e3So~O|Ga@VOz}fmAAIPj|{b)54 zbJoZDH%}C|uv1^gDl*ag^*6wmf53u>#;s)>JcGM@7~d1}uki^1@oOHBs;VtOjGNmbe zfjQWtJo`$yHd<3_*<%IJ!V(+N#nkN}45TmbejK|f%MqimeAlRbSqeh(USKqDNp)ex z8*ti|X%v?;m4JDv;@V;Wczr^4yu4y4ST3$9X^J;kl6U(r)6%0Eo`+qIqR3R~*~ij! zF{QSB(>3KqB!~wx`8bFpL;0zhykbko&DGkKhC#2ivcjr@u}1qa>vh5LbsHj$mHkE{ z+XxKIe}yJLKVFVM2pU-_dP=+4uU5!G5Ui!Lc?n-`eHQKrPj%G}xR@d}}9(g(IXGJ7yeR^h&GK z0LNnEf#34vnrZ}+zcD1nt8e#UrbOP6_xV_7i`s@Z zay^^ZD$f**c%$bYjnO&X;KZ(W^@*h9{Ut}H>+Qy>O9tie*BQm5ewkTVR#5V2+n^w{ zu$jk%v+;S(b@c03VHnGE$E2}6D5v&y#ev>mn5 zw&rv<;y`oJ_e!t%laFc3gGamAtrZ=BiFe8HE75$f*q`;-R1{WZKTH74sYBLuO~A&k zLAs)q+~K8=9Ozd*1zKP055t8<=Hc34%E{ zyO?tl8d6Ca&sPo9DlK!?XnET1 z30f$ZWwb5IRrXY7rKaI*NFUVF0zRW&R^`!O`;43ci#E1(~gt&T;HC9Y@J} zm*5b+nEzB3TON@A5YHw#5)$>%+0;Z>r+qMfo&qYIJdJn5ogQiY6bT&^$ClemG3 z@R2)hVsHBk=;~Lm&64m}SiR%B#?eFG2; z>$Ulj#r6|ojDc1;1D;nI_E#1ee07hC_5*^ONSbNGli<%+E0w&|$l#x=hGs4Icms?r zOGMc%Kdjy0RmnPwNYaR$5TUBn5JVC3niX%C#wI%A5Td zCcRFg=9Gl*UeGW2_`BgEzWVA-^qagl4KQzK?lQ@Cdcr$LtZAQpwwM7u-n#i~f1SJa zRW@_AY@g>B$TiiW`$c3Ah(bEoHTK~nPI6tVu5vOb+NETZs>Fw5mX?+Y#>OjeQT8v@ zIY3tChu#9#^?&($pJ*i_Sb{k>mvvEnJezaDBbAGm@cv7JyGtywwIerUgG)4%mthURD0;}WkyP&BFPdkDVh+O8C{fT)+E>ZRh!pb?&&YNzr znWI@1&g=yQb6W&Arg7dnT!qrpKj@VzyvlC2O@)ovLSki|_ELVA;TkP`f@fG+q$7pe znG2@Z(GPKz_Sgjy$9#O-#T)EzBVLczM{4gp$K)jMRfZmYCW~G>=4B{1+Q_jaA-VCd z?%$vGclSBqKDLDU4}}>$TV*&1!M$q#j-#E!8UKh?wCkJFwbHZu5x|`a{^2E_eg~M; zLLbwcoozXnCE>%x+eT^XCb*v{7eueni}|n9uM;{n240q@X)3_oqQPzmca`CHnF92 zg3CCx_K`#2383W5>f8O zm~pJBM)9e@myFvg-igPtMsVsSWI{Weq@aqid{(`JViQAIAt#ziU95Py9z7e8B>XKH zj`|XX_D|^%1FvF(jAP6v(?YHpnDlyDM#>evz#->Oc~clLs{}E|VdKsr&0ONmoW}wd z7>M_BLMJNQc#maa1o2dZdNf@bl(o}Bf2r+}r9Y{3jPZ9~`vK*5t%?$I`>M1p40!Pm z@9l*gtG1*AuKf7*Q&(QXg;e&pu3Nd=U*sn8_$cqP?EuNZN3AU^i~2O_kvk*<`#@4L z+0Re1kD=MFi*=#{VPu#%d<3H_H?k=G28E%wj0TI}6a7Ej! z*k-nBl4E~`i(1a6#MdO_W@rflf}xARj~dlb z>uH*y_FH@;@p}2izh^CeC(6yg2&k~K1W+=w*TnIiYrl3a2^D%=?}`>zEv;tFtUgYM ze&sza4=X%uVK#@X!THoktF8B;k#I+NEmBE*>Jo zw}(jDT-!o8M7ckg4?ezXBCnhQG7Tr^thF7QLj?0-b2+qEIHMUw58`X}VNFE#c>Mgs zpxkGD&CN0r9fXf+8}Y!PZZyYHBCS~=XZ*m>!ZFvGZ~*RgLp5&hx%}J)fn`S07nYv{ z0-Pflf_=7f&lfi>LywoS|An*PKTsf=Iy+(({R=S7W!Db6Vq$;3G6^@RP|J zA-=n8xZ?+_)HdsloUk7>dDg2c^6q(%(nwjp;$pCpya`H?QEbnoC)907>0XR=7cxEs6~4}A_+5ZH0vnmMzr`||B$|dt9iMGNtDhz2XrM#O5f!X{V2JrdzV=E zdAy@j3fJnSZqrNHaL@VmV2)GS4sx`#cm$vx@$FbC3{b^o*|t`G4$d1f)Y!k3Mwq3k z@rY(if&b$G*InY%y#Ak;?tCBMash$-+^-zV%!Fb9jRTz{+JfD;a@~uXmeDujMb8qD zZ^U*NbAKF^M-M%en=_)F)a0=$lE}J2u%GLXZjIQKHDkW1I4sNLE zZB2o?aXk5*iBywoNe%>Kyo64qy1jK=&ppg~JLii7QrG6rw{g!3BtYj|m z6}y@RLBveZU`}ttba1iDpzLtUrv<4B4N;!JrP_mF+#B5vD}>z7A0JNLPo}y~pa+|PO+*Lp8m}GGujU8AQO8qfo(4D>dWY^`eo0(K*Un-3IC^qN~ z+u9x`eBzBN=Ki{(8E|Z&EpYxx?d2Gg$+S@eKM5WhljzUhSI?9;qgxawdiZF-N{9Nk zS<*HLlgM*th6CR5z)(-GEY^Q;A<&(A|0BCH6^*lL#MQ8Y)`P-lX1d2RYU4R89+lvu zqB$RvX)>on)Tj5G=0Ol@ybU>EG~NLTe|DR>XJar&;$^L@N?!tqE7L%M=wRVFL~26g zn(jJy>hR@Z)nl*7Sl)HJJC3oaV8u>?=`t6^iThFvZ+7_2!5u_uxK(F6fmP?Wa&wv* zU=@+OW4C7REf;qHuAfQt1bZsy*%AdLl&7v;kMvaV56=k6;?K-_9m{20eT=Qj>Rhy^ zxIR#-)TtQjoD!b2cDQrRyb02ixxSbv(w-p9TnZMm&u<&gyjWQ((*GRBt=GFW7m|KU zltMn&gR8FtI8cMgijavFIE&i^U0tTv_wSEDlwN!luDE8_bGYxvb>dQ zCMm%CGK8oxFCcWuhH4mSNN&AL?OW-lqCVsR_h=kvW?MQDOZ*wlfC6$50ko7SLw@cR zzt~ACrXbB(z=_IlTGr5U_>oQj)6JLNg#s5X^dhIz6EUq#kGQ4Oi&*&Ov(!>pb|%vc z14E6URgw97@h&tn6mn~-qu4_x1Zq>MI=s{|3Hta>v=B@HPn?w9U}7YukL_8;G{}li z>w?c$IjlKC)4HNg&eT4KFM;)bj2lwg(DZ5$?nSKi7Annvm==L32O5R}{ZE9WWTz5O zxR%#D?d$5Wc}jG^wv||=xf+=`xuXVrwJiL(eXe}Lu>nEI8P=NcY~I#BhHjI=#|~*R z2~hYlPDcbM`vBL@fG6QuFF`2)wk#iru2~PashNV-=Ub{y#ciEERqzZx|7HQ=(OB18 zJ%I?4rrq_00ghr@a4NfrvSSiV!&QJ03Wt*_nW#D>2A@2r0e|d-XZ2ZVeyQ?P7~qOA zkp!K|lnvEomswTZ%ET&^XaQrIZCNoGe)1sUayu$#^?fd` zsH)=N_eljy8A}h&s`j}^>8C(NdHg6p_6$Cb=H4Hewkbc+9NlR{FGGg8$-UTZ%!Q@S z1XUDpSxH!gUjmVkdrx6hgD~#V4bM{q+4w*0w$O`Hs@4V&tL)+%+q1 z^m0yXuF}a+Zjaw6RSbmhi_nxs+2=Qz7p0%EU&9rODG{xkP1WAc<>f+$@l2MV8kR2J z1r#K$rjEp!f&i?hhTk)?gpdSj@4)7zYd1m>8^pyUB-l<}45R z^CF>Yiu*(x3l)nIAT(>JWAM=kcm zx)ABc(o{$tR#J`b9dj*7QasANXkgV$v1C+X$w0z!~sU>-T$LeTDsst^Qu@Q_wqnNs+>%Wcx)br`JO~^%t)4Yn3B+V zk50k!N2L4*JYoEVG9Kz^<(avWWd2ws_{=?s{oIQ&mz*kN<6g~s4E-l7>1zkV1Fv{t zLaCaVTL#hP)RYrDk8fARYI;-=$1U}wWfwH%;J>?kCIaaIq_12RUCqi8mm#|gDEsmM zM`d486ECie&8+J&;JVK_J&vKqeM#7%Y|XP(L$}dBH!hPeO5oeXLvt!cPeP>dh^niA ztiPw8Xz6%v3OhJcnN4FdaHnWMRzAlnY*%RqkKZEfQ`Bo3ezliDi(gX2|Tf~wk4k z>sG3mtrp!2fj_op?Ak~D-371mAVN7I^)J;Dj`jYcPf6@N zuAzHujYmzo{_^oDSZAsz&P(2|m(Ke*4{M4yz)V$GH??TKfjRZA?(_cz=4Y%dk=D%h zgF^v)Usj2>%zYjQ+~wV@9PiN$pfCDvDUM3W-)+%NuJJlyTRf3_Ul1y|JTR0=T!Hh7 zN&J&oeMSw^OoZ-iPjj4=9HVmDQ$|Ln%(NHm3`e+1kXZYLmvx7vQk86jYM=6?ZLdDs z;aTb=L~4w_>iaM*SUw~;e$}6DoQ5-F@Sk>S_OGMW$%B6hZbh)KSDL*2bcXtJuVny2 z;k-RS`l>O#Pl>zd@;B6b?Debv3AH5dslu?=naKKr>BI4IEFC!cxpPo^ND`tR^I*K6 zVFR2rv(IfDs(8DZIY6y9PjNOar#oR`P99L%Sx3Zka zzGISE=4Spx!7`143q7LigKtb&u?#*ryaJMqq||t7lx%G1#*x)Smy?g7ep{D=aVzhn z&5OO)#5CeA)!fkiyq;|x_L?+uW`LSVvq;3Nv%yg=Q=t&3{YtREap1xlt4vX~x;8_b z{eit?)9&|)n_oa(dJHI}y|M<_J$0a3qqQ5>@hzvg!is_jr+tD7pDp0~11 zUL?#xAkCqfJ4=PgN+HM-9;7YR`MfCg@6OK5o7LH`l2QZP2P$H`tS4wxie{aHZOoXE zg6CPI$yKvg;DxjMMj>;Oy{*QabXjU(G{8TQSs3SO#WoRQ+197{@#qe;PuKw4q&m^L zg9AGfC&T)yHGJqVYW~l0MIz7MU5DPdr@!P>IBpLo%j7n;iJ%X5GHD{M$a&Hyx@f#r zVU{U4`~cLjADWBwTiG}5n`t?SEN7eH+53zbO_86mA-qo@W)eO3lI}-^`WFO!b0U^_ zPq73r&IP6h*lKQ|r|6Nuo}M%_;1e~#a5Xv37$Eo_$$MhFwl4t_AT z=M*84C|RcKn}~xb_LSW^ZC}U1T1w8n}aS(;plz2MC_jNfW^Q< z;wM$I(!#HOuT|xg%Rxtqov&5LLChiX29|md*qgxzw+XOP7o%hl!)Y#=xerutPtq;> z$%!koUnubpyl*>`m<-)lzZ7T_$DcUN6!hTH77v#$m7a{u$Is*NTuIsyPDAy?Yt=?! z@BOxH%p#}64kij6YNVPndzAWO!9tAxD$B5!Sx-i1{>ivJQTH{wxjujrZI@Q;CmfyP1lc_=hhu z#&C-WGh|xtN)D=$c6b%dyvJzCq?djAt`F!y$E7UFE~zzyKg|tV%LGSc8+TF}j5)=O zKu6%o#)|gr`gS?*b-xXNRphI+WahjN$c%n8J)0?opT1Spk7qERBjZtWyd=~g9kii4 z0PeP2a3gyYl5HG2EyBO8EB{uWKi3RL;A8w4%y4Z1&DL8n4!x=`82^54x#CJifCkVz z)CTlG2ezxu<`^{II?MZacVPrH`1CfAuHU|SBw-ix3}0V*wGAkLHUc(U7{t7KR#f%3 zH`8$SdaNhBiox3K)#|e7#-N!xeb((@1ipZ9-Onh40W?=kgS)Y^#&_<$(k+<{+Uz2`SjRGcf9HdD3 z<=Jn^TUC_XbP|M^Pb8)8z`SWlc_yvJ5R2G@K@iJo`+FQ9<}}&9V$hbde{NXvXsvX%di$HVfveBNBWv1mY>`j=K+6O3EGX$9+Fqj zSZURZWCeDFs*vh(E^6X@{vqwM&VjC(Vn?d#2}u+if)=|n|X}6W050JwG-mLL=WLf?DMiU0p}mt zhiiO(x!#=-Dh-j8FUTBT^ekt9yOcbRYE>5~?3d48JCa!z3jw5fk4gjA4iSQ0lnT$K zWTY)hdC)RzO=%#noJi^=gOFuVVJ~<9Uvmn>ezeQ;+WwBiPD=R+jRgt&fLR{~a%YaC zh*i7w$(o5^78E}J#**4|VTw)@0yJ5G=cYrJ#TD5d$tX-no@k$zAD0X+&`L*lcW_Q- zBXX-XtB>cIkA_zx{=iIA&w%X9J!)-`k2T5XTSsr)=88TX4TTL`HJwT^x5n$0Qz_=Z zx4B=9);L8YGRGGBT<~ixj`eiiJ2FBq>9b5o=>?B%tgNUTL^AHt3$6kBU)pR>`~vun zif*6|2S=1jWs1T1T}!*^Wpk8~JNXBP-PrPM_pou)uVa(t_x!6T!9mXOTTepvqA-ya zT~sbNr*pFCqPA3q+69q5v$FbPS=;#Qq^=kDrjUhM3}q;2RN&V<`;MA(t;#)#O8 z?)}#2pgcKBMj29k_n3d4kx8y6I*JYP9>MnNvu|^GDi{9%moQi4DXQqMzEn~|1OSIU z7N2Z-31;Uy%`+UNI^u|@ZSVY>S z7K%3?aN}TPq~Cm?qf^mbI-t--`p`zU`9~`N&?cbJ+My835+>kC$OL)++*~u3NA==P zON#^*(Mh~#A$J<{D|Wuv{7^`3G20D&;`cs_B<2xxYJ|3=x>qIXcdlsGFMvPlskfOq ze?^gUI}dQlc>w2a2~nUfpief?IYB`|Or(xbO^8~lbTEz7id@qlg7Wz4D?l*YBmtP$ z%bF`vKEBE32BWMOPsFvETPM@TqZ4Q6>8rQx)G4ARp!fwIs{qxxAnaS(0 zBU{!(*NFVk@w=^Mhw@hxzBmHu!28>f^n18jr&P0E|;|uoxgh+ z|Hj@&x(oPB>fXE|ot@GsfOq?4T(jj58s^{a8pn(!vQ;$69dDQ!`xuPwDNo1nvud z7Cg!bL+C7d71KS_Cp5PP74ey~+8y@$MnF35IcsfCbXZ)LMy^LZ8=4_6bEz15Mvx+R z$G9HZjKr@BP0YdCshIku4+uaK(CNa7F+-iLsv&^d?W3os@AVScz3V!&Qe@PZeQ)9n z_2f==o8ncmsQog@4+K%*VFX=T>)?(QhfJc@ZIPSxI9WwSFHHurdu}VK_ssTj;^_l> zKG&SP^$->}Dji2gL0fP158nwjT~)XK#MIgu5O+OVyAFImgCo&-EZGK+mVqMNV6Z!3vkGO4V|r*(6`?~=E+BzDw3F{fW^XBQ6q)i0eR?kDt9RC{`{qrox) z%jig6+Z;gb+3mOIP8?d>|8*|UH@6%3b?9>rn2Xd+2>;m(D711x+GB6wN-lIS5_ZsG zaTu&5cr@H)Jl`5ZB;aJX;?alu1OC&;h^5saauG-+kd#5-?z@`!TXrDfOITSR`o-L` z-yK0Mt!v?&+Tfj$Vv{g3$5j^R8N9geu{K+CS=<6 ze^jNL8}E5s#c_MtpU}sXUo8_?E|%U1QPPyv(@TNv#B3~0IBph^zmDy#>m;U~JvC(4?udTG}L?Nvis^=EKXWhgXp87{A zEy9qH6VIt4+T+W?a~(TZkZY`uV?51fX|9~F*(7Ql#v#OPt1KipM!bP$6LTo9p*AcR zOa{R6=13zOd}HG1xKY##P&DY{BfCO>0vt{Ro~p;xVZ*xD;dbB`^e=8AQ2V0FzH!XH zaQ!d72dM9 zfPZ?em4~IyW}VJt-=~Z2|+~_UH?WUSJ{mLOs|O%2n<5>SjOOA=Mdm96a?m#Utp2 z>Wou!uJ8P8T@;HFvVFTZK&9!hObt;lmm}sd-Nan)ICt`5Hmvk?o8jL$_+KBFq};6= zV499=?9K?a$=-d~K0g{;t%JB!)8$}3BknR*R>o-04;fg}b073~y;c$qXh2P zF5-inx*_k8#6d9-hO5`4z?vv^V7C<)n42W|kF`>i3-JI@-FH3Ni9p5m~Wcy^w-C?HLiTyCg25N56^AqjdHm`S1qTj&~A($ zj|Q|dzL>#k76c-tKW5Sv%2=^K;fMkq92_{i2DSeiUG8Kz%QgdDcZG7Tfvp*rZ`!f* zDhWn|Zmq0LRGRlk*X?`m4KE2^z(>Yqu*!ee0l-7#@Wc$dCn6uq%LlKQ&EC*(M#2xb z0$sDEab*~Z{zvV85p!>jHSO-CT|t3FW6|;=ou9;ii4Uiq#nWnoAP~smc35>bVC0Qa zhe(I{5T$5IYIQGVm7%wczr6Z8&E5aP;6aXN`d@);Rs76YwI%erx*X{1+SUAmtk0p zH}h^$?CE*b-Tmha0F%Uu0a|BztlUbq*f^xgpE%8n%;C#dwEsQJjbL2ae{H~bO3^PQ=e0o*g?HNjW&`)f54wr-a{)$@jYF*(AUkbg z@{>ms`=6~PWAk{tbH$`df3nN>t0mYuPKtHdtJK|78O&}t7bDudoRnz^HU}S$Ut82D zj%WR@Kt4)R7@gx+!DMR1A>~QNab9{#|KLG?pN!yIBDKPIef9mhLh+)P-oF-|AVZQ; z#o!$gQC;gW`a9_lj5+}x$5Juj$uy2@cZ}^sMpiCNRq%Kkr?ErX(LdJxJEea->YRHm zwGf|eO*SW1)N zLm$K;R|~sTy(+8l`Zu1`{e0|?D~d;#fC|Gx#Y2X_DR?d3 zK-Mxl@f0f~U;iRvzvj9z5b1?M)5EkFx&S9eh~3eHEUvoWgHTsE_Ml{MdfQ~G<)wPLWgm`zY@Qb7W||;Md~I#5wY9YXRq=Jk7uC#MVK_B5 zn#}xm?+$7THpHqzY*zu^D6>`&I}E` zGg=mQ8|Tq1Xkk#u9;vsASn5fNY)E={^SHh-66pK?3+}C%-N6IVFh(k5Sj8|1w`S_q1G~_is<3gqa6wnt4N{)Z zMZtHUzNfmrs&@Wl$d+N6eXpz!6>Er|@nxprpN!4cy1Zn&K9+IHw)9L)1E+@0pOkk% za88WU+chDHlCKK9zdYr5zs!OZXYjK&A{^L`V2BlAm&oJOYC2LG8P&J9wJ<)uw@`iw zq_MD;yy4B`Gt$iXyniAxd@EM&=$SKS+Y?lzjtWv|a;=FIL7e++4Six8H{!JB{$b@h z40D31g#&NOALpEkPJ|a_sm}gWy;_%SRJyrN+r4X$f|{?>`S(^l5B<3AWRqoD!1Ft4 zi!3??UvlT&Qn+)UL|b9{H_>91pxEs?@Qb`1+@gCbqE$TYe>ZZv=hY0t_Sl_gi6^g+ zdl%|8!Q}j(ri^rc2u!Jj9u)P#Tu^(Z)$nYU!d)5kuFTeeW35QDrx|Pv<$hKNpo!O5g+~?QD;61`H+^D_F$tJWk1PwxhKC z+wpaOO!<1c?h=rjr%8{H(Ppo@w z^I8dqHhg}!SFcjnl%VzD@oHr?^gysM3Im)CdkcO7zKD&K7|l{eck7(EWTUaM2B1z~ z^)k!QI_~xPIs~Fmof|*D3y5OWX)a!&q4!P4TWRCbhwj&JRijX8yMtk>9ghMXA|`wV zTw<6it<%wVOVJ(t2q&FMl(6EVPw~mg!ZgRNwWrRisAr#5bUas-2mPc-v&`ea5OH=^ z%4R%|E18Xg3ilU7@CYc!;;P?OYZyu^@*>7_y1WZsR8AedKP<_@}&(h${xtKN8%IP(thVq=~xLmtcu}8|%Jf%O#GrgxMIN+FW-ayc#wV-L(UZda6 z61GS_YG63#y{u-nvb`QB0UWfxKw!_5AD}460e-UMO*ENpSY_HNXf3T6r$`!gm{1|T zf^m5lUENl8YprsZQ1(u2&?Pd&Ff#fHYPfwey4hr{2^)N``AU1YtC_(;oQSUf?RDP% zGC&a#3Y6|YV2>u_{e`=c^V)zkvZV?fj`m=uEcJIRQhv>&^QE|8AU*Z0v@;(Z= zQ8->K2zFN~`3|85x?oN==WNut( zjOui;5~BWEPATIo#f1p^&0O@}h64)eo!sy3?E7uiwPsAc)rii zOrow6hg*a-s8~5DqNW_v1Ef^ab3(9IEzGIUM`8Pg!9fi+=`uhZ8k$AbOMhE&VbDlzkbd_j)Fdt za3nCVD4-EW+(CS@LB6{K;=thHIGF^hm4gVJ4%&Wu9cuv{eyK3LacA7}M} z6p&S!>|23nfxGzkJ^$zAmWSr?JkOpM6n+w-pmy){B?edYgvqH9{dnA!UGh^MSFG4Y z(035CRX7GIoycr$mC$(OQ%T%hRDE1I z74JPo36tP-)RNTmnL7z14M_L#F&XiSHhSaWoh!6lB3hgk)i2btT(cm>zg0sr2 zFf?C#$tRu5rB8L9eQqq-FZI0-$Sw)`-mGdTs|$0_$%u&qs_vbenl5^w_MWvyTz}kpCWgv-n6{&%*&qW0zX1f+pXUE@NVDa+CHN&nGnQ<4XqzUH4?s zwFOK&J2LOlKk@U;nZN$heM$y;5xILAZvQEdCp|PO_(!)?a+lM>BOb@S?Di>WU+y)L z%?=}tN(Y-YkShutT7T27O>H8vas#GTI&MQu_Gc=8Cpy!mQ{PcGNhIa3wgTT*!;KX) zq`syW<~u=xkCC90dC)PZF&G$tIn=9OmU8H0U$9cOzuUckVS)X)i4!KvOzhL2ODLXI zb#qDFeQ^QeC}c%X3l(I_@K%LAyTm85+`bY6BC2 z8}n+)^dmwf$MlY;M4SC*ZDxgO`Hpy5(Tf|*M$V7c__$)(VbxN3Q1F91lg|2Vi*>@> zDW~8;91gJz7`LRgBf9Klx?{FUNz%URP+!AUW$bX;inXLYh6?GSqlV(nw6G!OBNs(d zb}~ylXH{>OUpm_7^f$B!FmXOedlf>svCww^+WxJfK*;34UgU}0VeZMwQ@?|+pxT|5 z%2iyb^X}&6!SF8rsB0qXg-Ra!}d5C49h;O9Yn8BV$*O%HFt!e$8%r`B=VDzuQ^5;N*jCu1BI%;rZlZb{qXSlR6b< zCo&mCu>*}QtW7zjdtF~&2kLZ(`qeFnDAc=7?(Q1FeY-^ykl@KNz*U_rHYkWQ-e6=B zSU^8QfsEdlYtnnWrd;KQ`+iM1a9C3b`pe-$e>==E6RlO9dI?B(EXAHX$Q<8kiyxrM z*6-GSc>B8#to0DFCa$nuiV<( zy(EPGFBuhLzM`wA*8)6d5OlH~<|nR9%3&IgHpD~n zP)UcWn8L9JaA+7YBLSZodGY9kLlG^Gz-)f4E=Y5i*9iRyiSoec;+`3s>6l#MO|0_u zlD)$Na&^|Zu7pf-4&EnM5u71;P|dXClT99N(r}EMTdgp;I%XHKc5LQvi#C5Pm?&6z zK96EKe8|v%3uhb_jv|AW}yv_1{7$GaQaBH|zryb2TAAnIZTz$nA4svi3gpQ~sBP7oP(OH231zXzU;VcJmjK z!CdpYPIxbO5Fzt2h6kM4asBN1y1$<(6fiIBLYZ<;;V=YILG@1ABI!^faW)_bw2}aMOaKCdHDw(<7qK^NsGvNtkcaa*Eq&q(I_HL zb9EWX2!TJk+&`hnyDetB+D6hK`2tmk4~qxN!4o!;H-3`tcVtUrh$({;pZz*<%J?xS z-r3wdXT1W!G;TZT>@YeyF^~(4?*4i0|2$6!{g;^1stHh|JWVU0eKO_Kq}SpI+W&oK z>OTOoN&xirQ@ug+b0dOoybI&lBa4fR2*7X-e7McPrrqEj8-CCEthN8cH&bHqmHD~8 z%rIr(IPR=d8Us0{k)H6E|9@yq0gp!k%?CMpZJ`I%6T7LLNRx{+|EXgr9zMj7^LNH3 z<#`zgm;q}vY9B$mVb@$!GxgxlvOtjpAiE=dUW@f7$MfHv;xI%Hl)@B%lx?m6YFP$R z(8h%Dq3fajPjmxyXDs!y*R=|j&zYewPjmUF-iALJ|A>Mb{aL(eeoE{lVT~WM>h3}} z#rCQ=K3<4HDW{**b}S(}I@-lPNAe7+{R0@bsQ`DFF14`ZBg}z z_5Q^Rm;cf}XL%m$DaLIvjEiM%G=Al^W#{O(F2A-vH;b0&_SUq!;NH--oqoPR2t%ET zidBS}>S}tr&|jY)W8C^I`sHb+^Uf|B@4ySz z<2k$=Rq`P8;b0;0yPls4@mJ0kx{ooGNA$rc{+Dx#HLoVC6$$Q-Ss{1g(CQwSvE`(1 zpr7r%VM_m5VLA<8f6CI|SMlZ-mIq*w9A=rHC`DJ$$8qQ+d1>wxbAL;=GiWp=n8l8gm%u|@M<{Yi0w+hIPm_!%hw6w@ua!#MtUR-{^W%}UFG9GR6F7U>YU&~4O^qH zo6|f8bHO@#by|Pc0u&?{OqH>l`MR(D1Ow+%5I%4cDk9Z!1FCeI4d*uv$^7%}KcIgN zqOf1f0wV@0j@B!Y!EwjWkSlPj>c_@sP4}8!&3gr>`K$3K@)8*~Piq{8i;ny2&eloB zjWQ>{66iPm_xC?;3cI&rXD!A$<}~>cCb?h4q^I=)UG$w%?=|=d%`aC4Qf|BAfwZIFEA%dA^$)**iW!e_V*`xM+k91@b&tXweP&Y{;!jme_H_SO|5$4%Z4Ox0qS~+Yc+pZ zZZ+%GeA?70x78DHTL~4-ZsR?|xttimVB?w5mg?M+njd~6==v`-{mDN+uH@ZKRhe6X zZ%Pvo{uz7!n0oB<9N7akQ>QleCc8V-#2{0z>yuH1M)ownn(@L&FWdavj?UD43iRVqvbHs`sFVarurE0L1Cq2Nt}zM)dC-cbpAZpUEl2L zLnV%c`wIm^%T3ggOUa!x`Kn8Y@`~d==+}OYSTuTPGF0@nGAo&3`%02&NjLj93&KXF z7L zSjKopKp+cmO~+RphX?vPRKFDxer1~)(({}fbd`OS#w@s6v3@VbcAir#A@6a{3NGb; zWj84L=|z3lZNG3h)ipqIsZcHLSB5Q5Vs>7wz%Q~8bt^AOdPazIN!4a~wQP@L(s1D` zLH$Mb0JKoEHkT0YnOvie_xd2Vzyy_;IC7y~-VA5rfa&Iv!>6__@+D=bIBue|48%8R zG6B7$YIf_bk|VpbA^Fy8_)SUS5vGSqMn3na%7spb!wf$sDK@3&QB3ySYI-*wh@NWF z=6fP{=3>ThU!!9S)cBKPBZ0+8VQXSi-W+rIbhJf2ru;U7SM^7h?N?lWXBhg^#n9b) z8)sq=(ETOxrnM(i@@=i0UR>E49QP_4qC(@ywa`?^p-gdWzQ$NlJna^Fx3VOr_#e9Z z{$XB*3$bJJHYo-#<>cM$Gc#%tH5eCEnYXD6ctlq9VKFI7G0uVaY3`GdQwN6Q*rxD^sLZK z&GXM!ApMd|u0E7uXSX9Qa+^d5f4dROVcqa&bHaC={DPYj;=;ho}h?)cY3UTZ9n-9 zb~n89f`FJW%Pf@+D-z-Kqy$Hna!(hQFLIaa%+P3p7

-
图1. CSR存储示意图. +
图1. CSR存储示意图.

- 在PaddlePaddle C-API中通过以下接口创建稀疏矩阵: + CSR存储格式通过:(1)非零元素的值(上图中的`values`);(2)行偏移(上图中的`row offsets`):每一行元素在`values`中的起始偏移,`row offsets`中元素个数总是等于行数 + 1;(3)非零元素的列号(上图中的`column indices`)来确定稀疏矩阵的内容。 + + 在PaddlePaddle C-API中,通过调用以下接口创建稀疏矩阵: ```cpp PD_API paddle_matrix paddle_matrix_create_sparse( uint64_t height, uint64_t width, uint64_t nnz, bool isBinary, bool useGpu); ``` + 1. 创建稀疏矩阵时需要显示地指定矩阵的(1)高度(`height`,在神经网络中等于一次预测处理的样本数)(2)宽度(`width`,`paddle.layer.data`的`size`)以及(3)非零元个数(`nnz`)。 1. 当上述接口第4个参数`isBinary`指定为`true`时,**只需要设置行偏移(`row_offset`)和列号(`colum indices`),不需要提供元素值(`values`)**,这时行偏移和列号指定的元素默认其值为1。 @@ -129,26 +115,124 @@ PaddlePaddle 支持两种序列类型: sizeof(values) / sizeof(float))); ``` -### 组织序列数据 +- 注意事项: + 1. 移动端预测**不支持**稀疏矩阵及相关的接口。 +### 组织序列信息 +多个排成一列的元素(可以是整型、浮点数、浮点数向量等)构成一个序列,元素之间的顺序是序列所携带的重要信息。不同序列可能会含有不同数目个元素。在 PaddlePaddle 中,序列输入/输出数据是在上文介绍的**数据输入(一维整型数组,二维浮点数矩阵)基础上,附加上序列信息**。下面详细解释什么是“序列信息”。 -### Python 端数据类型说明 +我们将神经网络一次计算接受的所有输入样本称之为一个`batch`(可以含有一条或多条样本),每一个序列在整个`batch`中的偏移,就是PaddlePaddle中所指的**序列信息**,称之为“sequence start positions”。PaddlePaddle 支持两种序列类型: -下表列出了Python端训练接口暴露的数据类型(`paddle.layer.data`函数`type`字段的取值)对应于调用C-API时需要创建的数据类型: +1. 单层序列 + - 序列中的每一个元素是非序列,是进行计算的基本单位,不可再进行拆分。 + - 例如:自然语言中的句子是一个序列,序列中的元素是词语; +1. 双层序列 + - 序列中的每一个元素又是一个序列。 + - 例如:自然语言中的段落是一个双层序列;段落是由句子构成的序列;句子是由词语构成的序列。 + - 双层序列在处理长序列的任务或是构建层级模型时会发挥作用。 +这篇文档之后部分会统一使用`sequence_start_positions`来特指:PaddlePaddle中神经网络计算层输入/输出所携带的序列信息。 + +对双层序列来讲,不仅要提供每一个外层序列在整个`batch`中的偏移,每一个外层序列又含有若干个内层序列,需要同时提供每一个内层序列在整个`batch`中的偏移。也就是说:**双层序列需要设置分别为外层序列和内层序列分别设置`sequence_start_positions`信息**。 + +**注:** +1. 不论序列中的元素在内存中占用多少实际存储空间,`sequence_start_positions`表示的偏移是以“序列中的一个元素”作为统计的基本单位,而不是相对`batch`起始存储地址以数据的存储大小为单位的偏移。 +2. 非序列输入不携带`sequence_start_positions`,非序列输入无需构造`sequence_start_positions`。 +3. **不论是单层序列还是双层序列的序列信息,都使用`paddle_ivector`(也就是PaddlePaddle中的一维整型数组)来存储。** + +图2 是PaddlePaddle中单层序列和双层序列存储示意图。 +

+
图2. 序列输入示意图. +

+ +- 单层序列 + + 图2 (a) 展示了一个含有4个序列的`batch`输入: + 1. 4个序列的长度分别为:5、3、2、4; + 2. 这时的`sequence_start_positions`为:`[0, 5, 8, 10, 14]`; + 3. 不论数据域是`paddle_ivector`类型还是`paddle_matrix`类型,都可以通过调用下面的接口为原有的数据输入附加上序列信息,使之变为一个单层序列输入,代码片段如下: + + ```cpp + int seq_pos_array[] = {0, 5, 8, 10, 14}; + paddle_ivector seq_pos = paddle_ivector_create( + seq_pos_array, sizeof(seq_pos_array) / sizeof(int), false, false); + // Suppose the network only has one input data layer. + CHECK(paddle_arguments_set_sequence_start_pos(in_args, 0, 0, seq_pos)); + ``` + +- 双层序列 + + 图2 (b) 展示了一个含有4个序列的`batch`输入; + 1. 4个序列的长度分别为:5、3、2、4;这四个序列又分别含有3、2、1、2个子序列; + 1. 这时的需要同时提供: + - 1. 外层序列在`batch`中的起始偏移`:[0, 5, 8, 10, 14]`; + - 2. 内层序列在`batch`中的起始偏移:`[0, 2, 3, 5, 7, 8, 10, 13, 14]`; + + 1. 不论数据域是`paddle_ivector`类型还是`paddle_matrix`类型,这时需要调用创建序列信息和为`argument`设置序列信息的接口**两次**,分别为数据输入添加外层序列和内层序列的序列信息,使之变为一个双层序列输入,代码片段如下: + ```cpp + // set the sequence start positions for the outter sequences. + int outter_seq_pos_array[] = {0, 5, 8, 10, 14}; + paddle_ivector seq_pos = + paddle_ivector_create(outter_seq_pos_array, + sizeof(outter_pos_array) / sizeof(int), + false, + false); + // The third parameter of this API indicates the sequence level. + // 0 for the outter sequence. 1 for the inner sequence. + // If the input is a sequence not the nested sequence, the third parameter is + // fixed to be 0. + CHECK(paddle_arguments_set_sequence_start_pos(in_args, 0, 0, seq_pos)); + + // set the sequence start positions for the outter sequences. + int inner_seq_pos_array[] = {0, 2, 3, 5, 7, 8, 10, 13, 14}; + paddle_ivector seq_pos = paddle_ivector_create( + inner_pos_array, sizeof(inner_pos_array) / sizeof(int), false, false); + // The third parameter of this API indicates the sequence level. + // 0 for the outter sequence. 1 for the inner sequence. + CHECK(paddle_arguments_set_sequence_start_pos(in_args, 0, 1, seq_pos)); + ``` + +- 注意事项: + 1. 当一个`batch`中含有多个序列,**不支持序列长度为`0`的序列(也就是空输入)** 作为输入。不同计算层对空输入的处理策略有可能不同,潜在会引起未定义行为,或者引起行时错误,请在输入时进行合法性检查。 + +### Python 端数据类型说明 + +下表列出了Python端训练接口暴露的数据类型(`paddle.layer.data`函数`type`字段的取值)对应于调用C-API需要创建的数据类型: Python 端数据类型 | C-API 输入数据类型| :-------------: | :-------------: -`paddle.data_type.integer_value` |一维整型数组,无需附加序列信息| -`paddle.data_type.dense_vector` |二维浮点型稠密矩阵,无需附加序列信息| -`paddle.data_type.sparse_binary_vector` |二维浮点型稀疏矩阵,无需提供非零元的值,默认为1,无需附加序列信息| -`paddle.data_type.sparse_vector` |二维浮点型稀疏矩阵,需提供非零元的值,无需附加序列信息| -`paddle.data_type.integer_value_sequence` |一维整型数组,需附加序列信息| -`paddle.data_type.dense_vector_sequence` |二维浮点型稠密矩阵,需附加序列信息| -`paddle.data_type.sparse_binary_vector_sequence` |二维浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加序列信息| -`paddle.data_type.sparse_vector_sequence` |二维浮点型稀疏矩阵,需提供非零元的值,需附加序列信息| -`paddle.data_type.integer_value_sub_sequence` |一维整型数组,需附加双层序列信息| -`paddle.data_type.dense_vector_sub_sequence` |二维浮点型稠密矩阵,需附加双层序列信息| -`paddle.data_type.sparse_binary_vector_sub_sequence` |二维浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加双层序列信息| -`paddle.data_type.sparse_vector_sub_sequence` |二维浮点型稀疏矩阵,需提供非零元的值,需附加双层序列信息| +`paddle.data_type.integer_value` |整型数组,无需附加序列信息| +`paddle.data_type.dense_vector` |浮点型稠密矩阵,无需附加序列信息| +`paddle.data_type.sparse_binary_vector` |浮点型稀疏矩阵,无需提供非零元的值,默认为1,无需附加序列信息| +`paddle.data_type.sparse_vector` |浮点型稀疏矩阵,需提供非零元的值,无需附加序列信息| +`paddle.data_type.integer_value_sequence` |整型数组,需附加序列信息| +`paddle.data_type.dense_vector_sequence` |浮点型稠密矩阵,需附加序列信息| +`paddle.data_type.sparse_binary_vector_sequence` |浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加序列信息| +`paddle.data_type.sparse_vector_sequence` |浮点型稀疏矩阵,需提供非零元的值,需附加序列信息| +`paddle.data_type.integer_value_sub_sequence` |整型数组,需附加双层序列信息| +`paddle.data_type.dense_vector_sub_sequence` |浮点型稠密矩阵,需附加双层序列信息| +`paddle.data_type.sparse_binary_vector_sub_sequence` |浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加双层序列信息| +`paddle.data_type.sparse_vector_sub_sequence` |浮点型稀疏矩阵,需提供非零元的值,需附加双层序列信息| + +### 输出数据 + +PaddlePaddle中一个计算层的输出数据组织方式和输入数据组织方式完全相同。一个输出数据同样被组织为一个`argument`,`argument`通过`paddle_matrix`或`paddle_ivector`存数数据,如果输出是一个序列,那么会携带有`sequence_start_positions`信息。调用C-API相关接口,读取需要的结果即可。 + +### 总结 + +- 在PaddlePaddle内部,神经网络中一个计算层的输入/输出被组织为`argument`。 +- `argument`并不真正“存储”数据,而是将输入/输出信息有机地组织在一起。 +- 在`argument`内部由`paddle_ivector`(一维整型数组)和`paddle_matrix`(二维浮点型矩阵)来实际存储数据。 +如果是一个序列输入/输出由 `sequence start positions` 来记录输入/输出的序列信息。 + +于是,在组织神经网络输入时,需要思考完成以下工作: +1. 为每一个输入/输出创建`argument`。 + - C-API 中操作`argument`的接口请查看[argument.h](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/arguments.h)。 +1. 为每一个`argument`创建`paddle_matrix`或者`paddle_ivector`来存储数据。 + - C-API 中操作`paddle_ivector`的接口请查看 [vector.h](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/vector.h)。 + - C-API 中操作`paddle_matrix`的接口请查看[matrix.h](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/matrix.h)。 +1. 如果输入是序列数据,需要创建并填写`sequence_start_positions`信息。 + - 通过调用 [`paddle_arguments_set_sequence_start_pos`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/arguments.h#L137) 来为一个`argument`添加序列信息。 + - 通过调用 [`paddle_arguments_get_sequence_start_pos`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/arguments.h#L150) 来读取一个`argument`添加序列信息。 + - 接口说明请查看 [argument.h](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/arguments.h) 文件。 diff --git a/doc/howto/usage/capi/overview.md b/doc/howto/usage/capi/overview.md index 5ec3bd02849..c55d39bac69 100644 --- a/doc/howto/usage/capi/overview.md +++ b/doc/howto/usage/capi/overview.md @@ -1,5 +1,3 @@ - [编译 PaddlePaddle 链接库](compile_paddle_lib.md) +- [输入/输出数据组织](organization_of_the_inputs.md) - [C-API 使用示例](a_simple_example.md) -- [输入数据组织](organize_input_data.md) -- [核心概念介绍](core_concepts.md) -- [F&Q]() -- GitLab From b2ee91903daedcd7a2c0eda9096da588811dacae Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Tue, 19 Dec 2017 08:56:03 +0000 Subject: [PATCH 0040/2305] add parallel_do test --- paddle/operators/parallel_do_op.cc | 4 +- python/paddle/v2/fluid/layers/control_flow.py | 122 +++++++++++++++--- 2 files changed, 107 insertions(+), 19 deletions(-) diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index 3ab4bd3df28..4c026c2239e 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -23,9 +23,9 @@ namespace operators { constexpr char kInputs[] = "inputs"; constexpr char kParameters[] = "parameters"; constexpr char kPlaces[] = "places"; -constexpr char kParallelBlock[] = "parallel_block"; +constexpr char kParallelBlock[] = "sub_block"; constexpr char kOutputs[] = "outputs"; -constexpr char kParallelScopes[] = "sub_block"; +constexpr char kParallelScopes[] = "sub_scopes"; // #define GRAD_SUFFIX "@GRAD" // constexpr char kInputGrads[] = "inputs" GRAD_SUFFIX; // constexpr char kOutputGrads[] = "outputs" GRAD_SUFFIX; diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index dc6c0e7f518..4791d749700 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -5,12 +5,12 @@ from tensor import assign, fill_constant import contextlib __all__ = [ - 'split_lod_tensor', 'merge_lod_tensor', 'BlockGuard', 'StaticRNNGuard', - 'StaticRNNMemoryLink', 'WhileGuard', 'While', 'lod_rank_table', - 'max_sequence_len', 'topk', 'lod_tensor_to_array', 'array_to_lod_tensor', - 'increment', 'array_write', 'create_array', 'less_than', 'array_read', - 'shrink_memory', 'array_length', 'IfElse', 'DynamicRNN', 'ConditionalBlock', - 'StaticRNN' + 'split_lod_tensor', 'merge_lod_tensor', 'BlockGuard', + 'BlockGuardWithCompletion', 'StaticRNNMemoryLink', 'WhileGuard', 'While', + 'lod_rank_table', 'max_sequence_len', 'topk', 'lod_tensor_to_array', + 'array_to_lod_tensor', 'increment', 'array_write', 'create_array', + 'less_than', 'array_read', 'shrink_memory', 'array_length', 'IfElse', + 'DynamicRNN', 'ConditionalBlock', 'StaticRNN', 'ParallelDo' ] @@ -67,29 +67,117 @@ class BlockGuard(object): return True -class StaticRNNGuard(BlockGuard): +class ParallelDo(object): """ - StaticRNNGuard class. + ParallelDo class. - StaticRNNGuard class is used to create a StaticRNN block in a program. + ParallelDo class is used to create a ParallelDo. + """ + + def __init__(self, places, name=None): + self.helper = LayerHelper("parallel_do", name=name) + self.inputs = [] + self.places = places + self.outputs = [] + self.status = StaticRNN.BEFORE_RNN_BLOCK + + def do(self): + return BlockGuardWithCompletion(self) + + def parent_block(self): + prog = self.helper.main_program + parent_idx = prog.current_block().parent_idx + assert parent_idx >= 0 + parent_block = prog.block(parent_idx) + return parent_block + + def __call__(self, *args, **kwargs): + if self.status != StaticRNN.AFTER_RNN_BLOCK: + raise ValueError("RNN output can only be retrieved after rnn block") + if len(self.outputs) == 0: + raise ValueError("RNN has no output") + elif len(self.outputs) == 1: + return self.outputs[0] + else: + return self.outputs + + def read_input(self, var): + self.inputs.append(var) + + def write_output(self, var): + self.outputs.append(var) + + def get_parameters(self): + main_program = self.helper.main_program + current_block = main_program.current_block() + parent_block = self.parent_block() + + local_inputs = set() + + for op in current_block.ops: + for oname in op.output_names: + for out_var_name in op.output(oname): + local_inputs.add(out_var_name) + + for var in self.inputs: + local_inputs.add(var.name) + + params = list() + for op in current_block.ops: + for iname in op.input_names: + for in_var_name in op.input(iname): + if in_var_name not in local_inputs: + params.append(in_var_name) + + return [parent_block.var(name) for name in params] + + def complete_op(self): + main_program = self.helper.main_program + current_block = main_program.current_block() + parent_block = self.parent_block() + + step_scope = parent_block.create_var( + type=core.VarDesc.VarType.STEP_SCOPES) + + inputs = [parent_block.var(i.name) for i in self.inputs] + + parent_block.append_op( + type='parallel_do', + inputs={ + 'inputs': inputs, + 'parameters': self.get_parameters(), + 'places': self.places + }, + outputs={'outputs': self.outputs, + 'step_scopes': [step_scope]}, + attrs={'sub_block': current_block}) + + +class BlockGuardWithCompletion(BlockGuard): + """ + BlockGuardWithCompletion class. + + BlockGuardWithCompletion class is used to create an op with a block in a program. """ def __init__(self, rnn): - if not isinstance(rnn, StaticRNN): - raise TypeError("StaticRNNGuard takes a StaticRNN") - super(StaticRNNGuard, self).__init__(rnn.helper.main_program) + if not (isinstance(rnn, StaticRNN) or isinstance(rnn, ParallelDo)): + raise TypeError( + "BlockGuardWithCompletion takes a StaticRNN or ParallelDo") + super(BlockGuardWithCompletion, self).__init__(rnn.helper.main_program) self.rnn = rnn def __enter__(self): self.rnn.status = StaticRNN.IN_RNN_BLOCK - return super(StaticRNNGuard, self).__enter__() + return super(BlockGuardWithCompletion, self).__enter__() def __exit__(self, exc_type, exc_val, exc_tb): if exc_type is not None: return False self.rnn.status = StaticRNN.AFTER_RNN_BLOCK - self.rnn.complete_rnn_op() - return super(StaticRNNGuard, self).__exit__(exc_type, exc_val, exc_tb) + self.rnn.complete_op() + return super(BlockGuardWithCompletion, self).__exit__(exc_type, exc_val, + exc_tb) class StaticRNNMemoryLink(object): @@ -135,7 +223,7 @@ class StaticRNN(object): self.seq_len = None def step(self): - return StaticRNNGuard(self) + return BlockGuardWithCompletion(self) def _assert_in_rnn_block_(self, method): if self.status != StaticRNN.IN_RNN_BLOCK: @@ -251,7 +339,7 @@ class StaticRNN(object): else: return self.outputs - def complete_rnn_op(self): + def complete_op(self): main_program = self.helper.main_program rnn_block = main_program.current_block() parent_block = self.parent_block() -- GitLab From 2f56d4b3d4a048b98a19872a6f69f1b8c7cf29a0 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Tue, 19 Dec 2017 10:54:45 +0000 Subject: [PATCH 0041/2305] forward pass compile time --- paddle/operators/parallel_do_op.cc | 6 ++-- python/paddle/v2/fluid/framework.py | 3 +- python/paddle/v2/fluid/layers/control_flow.py | 3 +- .../paddle/v2/fluid/tests/test_parallel_op.py | 33 +++++++++++++++++++ 4 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 python/paddle/v2/fluid/tests/test_parallel_op.py diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index 4c026c2239e..bde59c7e7ad 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -23,9 +23,11 @@ namespace operators { constexpr char kInputs[] = "inputs"; constexpr char kParameters[] = "parameters"; constexpr char kPlaces[] = "places"; -constexpr char kParallelBlock[] = "sub_block"; + constexpr char kOutputs[] = "outputs"; -constexpr char kParallelScopes[] = "sub_scopes"; +constexpr char kParallelScopes[] = "parallel_scopes"; + +constexpr char kParallelBlock[] = "sub_block"; // #define GRAD_SUFFIX "@GRAD" // constexpr char kInputGrads[] = "inputs" GRAD_SUFFIX; // constexpr char kOutputGrads[] = "outputs" GRAD_SUFFIX; diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index bf0cd275b62..14e0734331a 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -424,7 +424,8 @@ class Operator(object): self.desc.check_attrs() no_kernel_op_set = { 'feed', 'fetch', 'save', 'load', 'recurrent', - 'rnn_memory_helper_grad', 'conditional_block', 'while' + 'rnn_memory_helper_grad', 'conditional_block', 'while', + 'parallel_do' } if type not in no_kernel_op_set: self.desc.infer_var_type(self.block.desc) diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index 4791d749700..09ab9726d1d 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -103,6 +103,7 @@ class ParallelDo(object): def read_input(self, var): self.inputs.append(var) + return var def write_output(self, var): self.outputs.append(var) @@ -149,7 +150,7 @@ class ParallelDo(object): 'places': self.places }, outputs={'outputs': self.outputs, - 'step_scopes': [step_scope]}, + 'parallel_scopes': [step_scope]}, attrs={'sub_block': current_block}) diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py new file mode 100644 index 00000000000..1e643032849 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -0,0 +1,33 @@ +import unittest + +import paddle.v2.fluid.layers as layers +import paddle.v2.fluid as fluid +from paddle.v2.fluid.framework import Program +from paddle.v2.fluid.executor import Executor +from paddle.v2.fluid.backward import append_backward_ops +import numpy as np +import paddle.v2.fluid.core as core + + +class ParallelOpTest(unittest.TestCase): + def setUp(self): + x = layers.data( + shape=[2, 3, 4], dtype='float32', name='x', append_batch_size=False) + + places = fluid.default_main_program().global_block().create_var() + pd = layers.ParallelDo(places=places) + + with pd.do(): + data = pd.read_input(x) + hidden = layers.fc(input=data, size=7) + pd.write_output(hidden) + data = pd() + print data + print fluid.default_main_program() + + def test_forward(self): + pass + + +if __name__ == '__main__': + unittest.main() -- GitLab From 93568cbec6932af15ef3a1750d5ac8419e227cc2 Mon Sep 17 00:00:00 2001 From: caoying03 Date: Tue, 19 Dec 2017 20:37:17 +0800 Subject: [PATCH 0042/2305] fix the build of the doc. --- doc/howto/usage/capi/a_simple_example.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/howto/usage/capi/a_simple_example.md b/doc/howto/usage/capi/a_simple_example.md index ae2eaa3ce28..847d8ef1e0c 100644 --- a/doc/howto/usage/capi/a_simple_example.md +++ b/doc/howto/usage/capi/a_simple_example.md @@ -115,7 +115,7 @@ - `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 - 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 -*注:这篇文档使用的示例任务手写数字识别不涉及一维整型序列输入/输出,因此不讨论一维整型输入/输出数据相关的内容。更多信息请参考:[输入/输出数据组织](organization_of_the_inputs.md)。* +注:本文使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅限于讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。 这篇文档的之后部分会使用`argument`来**特指** PaddlePaddle C-API中神经网的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 -- GitLab From e7d7d257467457a01f2f3d02590aa28fd3ce6d00 Mon Sep 17 00:00:00 2001 From: caoying03 Date: Tue, 19 Dec 2017 20:37:17 +0800 Subject: [PATCH 0043/2305] fix the build of the doc. --- doc/howto/usage/capi/a_simple_example.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/howto/usage/capi/a_simple_example.md b/doc/howto/usage/capi/a_simple_example.md index ae2eaa3ce28..847d8ef1e0c 100644 --- a/doc/howto/usage/capi/a_simple_example.md +++ b/doc/howto/usage/capi/a_simple_example.md @@ -115,7 +115,7 @@ - `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 - 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 -*注:这篇文档使用的示例任务手写数字识别不涉及一维整型序列输入/输出,因此不讨论一维整型输入/输出数据相关的内容。更多信息请参考:[输入/输出数据组织](organization_of_the_inputs.md)。* +注:本文使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅限于讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。 这篇文档的之后部分会使用`argument`来**特指** PaddlePaddle C-API中神经网的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 -- GitLab From 26d5a7aadf83b039cf82f24a8f0e006bfb44aea6 Mon Sep 17 00:00:00 2001 From: ying Date: Tue, 19 Dec 2017 20:55:45 +0800 Subject: [PATCH 0044/2305] fix the build failure. --- doc/howto/index_cn.rst | 2 +- doc/howto/usage/capi/a_simple_example.md | 4 ++-- doc/howto/usage/capi/compile_paddle_lib.md | 4 +++- doc/howto/usage/capi/index_cn.rst | 9 +++++++++ doc/howto/usage/capi/overview.md | 3 --- 5 files changed, 15 insertions(+), 7 deletions(-) create mode 100644 doc/howto/usage/capi/index_cn.rst delete mode 100644 doc/howto/usage/capi/overview.md diff --git a/doc/howto/index_cn.rst b/doc/howto/index_cn.rst index 678a00c9d34..e0c69f7a6a4 100644 --- a/doc/howto/index_cn.rst +++ b/doc/howto/index_cn.rst @@ -9,7 +9,7 @@ usage/cmd_parameter/index_cn.rst usage/cluster/cluster_train_cn.md - usage/capi/overview.md + usage/capi/index_cn.rst 开发标准 -------- diff --git a/doc/howto/usage/capi/a_simple_example.md b/doc/howto/usage/capi/a_simple_example.md index 847d8ef1e0c..d9f0a1d1280 100644 --- a/doc/howto/usage/capi/a_simple_example.md +++ b/doc/howto/usage/capi/a_simple_example.md @@ -1,4 +1,4 @@ -## 使用 C-API 开发预测程序 +## C-API CPU 单线程预测示例 这篇文档通过一个最简单的例子:手写数字识别,来介绍 CPU 下单线程使用 PaddlePaddle C-API 开发预测服务,完整代码见[此目录](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/dense/)。 @@ -115,7 +115,7 @@ - `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 - 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 -注:本文使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅限于讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。 +*注:本文档使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。* 这篇文档的之后部分会使用`argument`来**特指** PaddlePaddle C-API中神经网的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 diff --git a/doc/howto/usage/capi/compile_paddle_lib.md b/doc/howto/usage/capi/compile_paddle_lib.md index 1ad5b906812..d4ac4f27711 100644 --- a/doc/howto/usage/capi/compile_paddle_lib.md +++ b/doc/howto/usage/capi/compile_paddle_lib.md @@ -1,5 +1,7 @@ ## 编译 PaddlePaddle 链接库 +### 概述 + 使用 C-API 进行预测依赖于将 PaddlePaddle 核心代码编译成链接库,只需在编译时指定编译选项:`-DWITH_C_API=ON`。同时,**建议将:`DWITH_PYTHON`,`DWITH_SWIG_PY`,`DWITH_GOLANG`,均设置为`OFF`**,以避免链接不必要的库。其它编译选项按需进行设定。 ```shell @@ -42,7 +44,7 @@ cmake $PADDLE_ROOT -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \ ├── ...... ``` -## 链接方式说明 +### 链接方式说明 目前提供三种链接方式: diff --git a/doc/howto/usage/capi/index_cn.rst b/doc/howto/usage/capi/index_cn.rst new file mode 100644 index 00000000000..3b36f31bfd0 --- /dev/null +++ b/doc/howto/usage/capi/index_cn.rst @@ -0,0 +1,9 @@ +PaddlePaddle C-API +================== + +.. toctree:: + :maxdepth: 1 + + compile_paddle_lib.md + organization_of_the_inputs.md + a_simple_example.md diff --git a/doc/howto/usage/capi/overview.md b/doc/howto/usage/capi/overview.md deleted file mode 100644 index c55d39bac69..00000000000 --- a/doc/howto/usage/capi/overview.md +++ /dev/null @@ -1,3 +0,0 @@ -- [编译 PaddlePaddle 链接库](compile_paddle_lib.md) -- [输入/输出数据组织](organization_of_the_inputs.md) -- [C-API 使用示例](a_simple_example.md) -- GitLab From e8de775f8e3cd9f0dc93a43fde686843bc76aa5d Mon Sep 17 00:00:00 2001 From: qijun Date: Wed, 20 Dec 2017 11:29:45 +0800 Subject: [PATCH 0045/2305] rename trainer_count to device_count --- paddle/operators/get_places_op.cc | 12 ++++++------ python/paddle/v2/fluid/layers/utils.py | 4 ++-- python/paddle/v2/fluid/tests/test_layers.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/paddle/operators/get_places_op.cc b/paddle/operators/get_places_op.cc index 5158af8f429..959aae16a8d 100644 --- a/paddle/operators/get_places_op.cc +++ b/paddle/operators/get_places_op.cc @@ -30,7 +30,7 @@ class GetPlacesOp : public framework::OperatorBase { void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { std::string device_type = Attr("device_type"); - auto trainer_count = Attr("trainer_count"); + auto device_count = Attr("device_count"); auto out_var_name = Output("Out"); auto *out_var = scope.FindVar(out_var_name); @@ -38,18 +38,18 @@ class GetPlacesOp : public framework::OperatorBase { out_var_name); auto &places = *(out_var->GetMutable>()); - places.resize(trainer_count); + places.resize(device_count); if (device_type == "CUDA") { #ifdef PADDLE_WITH_CUDA - PADDLE_ENFORCE_LT(trainer_count, platform::GetCUDADeviceCount()); - for (int i = 0; i < trainer_count; i++) { + PADDLE_ENFORCE_LT(device_count, platform::GetCUDADeviceCount()); + for (int i = 0; i < device_count; i++) { places.emplace_back(platform::GPUPlace(i)); } #else PADDLE_THROW("'GPUPlace' is not supported in CPU only device."); #endif } else if (device_type == "CPU") { - for (int i = 0; i < trainer_count; i++) { + for (int i = 0; i < device_count; i++) { places.emplace_back(platform::CPUPlace()); } } @@ -62,7 +62,7 @@ class GetPlacesOpProtoMaker : public framework::OpProtoAndCheckerMaker { framework::OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddOutput("Out", "vector of Place"); - AddAttr("trainer_count", "(int)trainer count").SetDefault(1); + AddAttr("device_count", "(int)device count").SetDefault(1); AddAttr("device_type", "(string), deivce type can be \"CPU\" and \"CUDA\"") .InEnum({"CPU", "CUDA"}); diff --git a/python/paddle/v2/fluid/layers/utils.py b/python/paddle/v2/fluid/layers/utils.py index b71d8d93573..89c07584ef5 100644 --- a/python/paddle/v2/fluid/layers/utils.py +++ b/python/paddle/v2/fluid/layers/utils.py @@ -8,7 +8,7 @@ from ..framework import Variable __all__ = ['get_places'] -def get_places(trainer_count, device_type="CPU"): +def get_places(device_count, device_type="CPU"): helper = LayerHelper('get_places', **locals()) out_places = helper.create_tmp_variable(dtype=helper.input_dtype()) helper.append_op( @@ -16,7 +16,7 @@ def get_places(trainer_count, device_type="CPU"): outputs={"Out": [out_places]}, attrs={ "device_type": device_type, - 'trainer_count': trainer_count, + 'device_count': device_count, }) return out_places diff --git a/python/paddle/v2/fluid/tests/test_layers.py b/python/paddle/v2/fluid/tests/test_layers.py index c6f109bc959..c851f37b23f 100644 --- a/python/paddle/v2/fluid/tests/test_layers.py +++ b/python/paddle/v2/fluid/tests/test_layers.py @@ -199,7 +199,7 @@ class TestBook(unittest.TestCase): def test_get_places(self): program = Program() with program_guard(program): - x = layers.get_places(trainer_count=4) + x = layers.get_places(device_count=4) print(str(program)) -- GitLab From b915fde92a8a56f4011691411008f7795110769f Mon Sep 17 00:00:00 2001 From: ying Date: Tue, 19 Dec 2017 21:14:36 +0800 Subject: [PATCH 0046/2305] use html table. --- doc/howto/usage/capi/a_simple_example.md | 10 +- .../usage/capi/organization_of_the_inputs.md | 195 +++++++++++------- 2 files changed, 125 insertions(+), 80 deletions(-) diff --git a/doc/howto/usage/capi/a_simple_example.md b/doc/howto/usage/capi/a_simple_example.md index d9f0a1d1280..b1eceea38bc 100644 --- a/doc/howto/usage/capi/a_simple_example.md +++ b/doc/howto/usage/capi/a_simple_example.md @@ -143,8 +143,8 @@ CHECK(paddle_arguments_resize(in_args, 1)); // agument to store the testing samples. paddle_matrix mat = paddle_matrix_create(/* height = batch size */ 1, - /* width = dimensionality of the data layer */ 784, - /* whether to use GPU */ false); + /* width = dimensionality of the data layer */ 784, + /* whether to use GPU */ false); paddle_real* array; // Get the pointer pointing to the start address of the first row of the @@ -172,9 +172,9 @@ paddle_arguments out_args = paddle_arguments_create_none(); // Invoke the forward computation. CHECK(paddle_gradient_machine_forward(machine, - in_args, - out_args, - /* is train taks or not */ false)); + in_args, + out_args, + s/* is train taks or not */ false)); // Create the matrix to hold the forward result of the neural network. paddle_matrix prob = paddle_matrix_create_none(); diff --git a/doc/howto/usage/capi/organization_of_the_inputs.md b/doc/howto/usage/capi/organization_of_the_inputs.md index 1e573618a6b..7563e236da6 100644 --- a/doc/howto/usage/capi/organization_of_the_inputs.md +++ b/doc/howto/usage/capi/organization_of_the_inputs.md @@ -9,13 +9,13 @@ - 稠密矩阵 - 稀疏矩阵 - - 说明: - 1. 一维数组**仅支持整型值**; - - 常用于自然语言处理任务,例如:表示词语在词典中的序号; - - 分类任务中类别标签; - 1. 逻辑上高于二维的数据(例如含有多个通道的图片,视频等)在程序实现中都会转化为二维矩阵,转化方法在相应的领域都有通用解决方案,需要使用者自己了解并完成转化; - 1. 二维矩阵可以表示行向量和列向量,任何时候如果需要浮点型数组(向量),都应使用C-API中的矩阵来表示,而不是C-API中的一维数组。 - 1. 不论是一维整型数组还是二维浮点数矩阵,**为它们附加上序列信息将变成序列输入。PaddlePaddle 会通过判数据是否附带有序列信息来判断一个向量/矩阵是否是一个序列**。当非序列输入时,无需关心和处理序列信息。关于什么是“序列信息”,下文会详细进行介绍。 +说明: +1. 一维数组**仅支持整型值**; + - 常用于自然语言处理任务,例如:表示词语在词典中的序号; + - 分类任务中类别标签; +1. 逻辑上高于二维的数据(例如含有多个通道的图片,视频等)在程序实现中都会转化为二维矩阵,转化方法在相应的领域都有通用解决方案,需要使用者自己了解并完成转化; +1. 二维矩阵可以表示行向量和列向量,任何时候如果需要浮点型数组(向量),都应使用C-API中的矩阵来表示,而不是C-API中的一维数组。 +1. 不论是一维整型数组还是二维浮点数矩阵,**为它们附加上序列信息将变成序列输入。PaddlePaddle 会通过判数据是否附带有序列信息来判断一个向量/矩阵是否是一个序列**。当非序列输入时,无需关心和处理序列信息。关于什么是“序列信息”,下文会详细进行介绍。 ### 基本使用概念 @@ -32,7 +32,7 @@ - 一维整型数组 概念上可以将`paddle_ivector`理解为一个一维的整型数组,通常用于表示离散的类别标签,或是在自然语言处理任务中表示词语在字典中的序号。下面的代码片段创建了含有三个元素`1`、`2`、`3`的`paddle_ivector`。 - ```cpp + ```c int ids[] = {1, 2, 3}; paddle_ivector ids_array = paddle_ivector_create(ids, sizeof(ids) / sizeof(int), false, false); @@ -40,14 +40,14 @@ ``` - **稠密矩阵** - - 一个$m×n$的稠密矩阵是一个由$m$行$n$列元素排列成的矩形阵列,矩阵里的元素是浮点数。对神经网络来说,矩阵的高度$m$是一次预测接受的样本数目,宽度$n$是神经网络定义时,`paddle.layer.data`的`size`。 + - 一个`m×n`的稠密矩阵是一个由`m`行`n`列元素排列成的矩形阵列,矩阵里的元素是浮点数。对神经网络来说,矩阵的高度`m`是一次预测接受的样本数目,宽度$n$是神经网络定义时,`paddle.layer.data`的`size`。 - 下面的代码片段创建了一个高度为1,宽度为`layer_size`的稠密矩阵,矩阵中每个元素的值随机生成。 - ```cpp - paddle_matrix mat = - paddle_matrix_create(/* height = batch size */ 1, - /* width = dimensionality of the data layer */ layer_size, - /* whether to use GPU */ false); + ```c + paddle_matrix mat = paddle_matrix_create( + /* height = batch size */ 1, + /* width = dimensionality of the data layer */ layer_size, + /* whether to use GPU */ false); paddle_real* array; // Get the pointer pointing to the start address of the first row of the @@ -67,56 +67,55 @@ - **稀疏矩阵** PaddlePaddle C-API 中 稀疏矩阵使用[CSR(Compressed Sparse Row Format)](https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_(CSR,_CRS_or_Yale_format))格式存储。下图是CSR存储稀疏矩阵的示意图。 -

-
图1. CSR存储示意图. +
图1. 稀疏矩阵存储示意图

CSR存储格式通过:(1)非零元素的值(上图中的`values`);(2)行偏移(上图中的`row offsets`):每一行元素在`values`中的起始偏移,`row offsets`中元素个数总是等于行数 + 1;(3)非零元素的列号(上图中的`column indices`)来确定稀疏矩阵的内容。 在PaddlePaddle C-API中,通过调用以下接口创建稀疏矩阵: - ```cpp + + ```c PD_API paddle_matrix paddle_matrix_create_sparse( - uint64_t height, uint64_t width, uint64_t nnz, bool isBinary, bool useGpu); + uint64_t height, uint64_t width, uint64_t nnz, bool isBinary, bool useGpu); ``` 1. 创建稀疏矩阵时需要显示地指定矩阵的(1)高度(`height`,在神经网络中等于一次预测处理的样本数)(2)宽度(`width`,`paddle.layer.data`的`size`)以及(3)非零元个数(`nnz`)。 1. 当上述接口第4个参数`isBinary`指定为`true`时,**只需要设置行偏移(`row_offset`)和列号(`colum indices`),不需要提供元素值(`values`)**,这时行偏移和列号指定的元素默认其值为1。 - - 下面的代码片段创建了一个CPU上的二值稀疏矩阵: - - ```cpp - paddle_matrix mat = paddle_matrix_create_sparse(1, layer_size, nnz, true, false); - int colIndices[] = {9, 93, 109}; // layer_size here is greater than 109. - int rowOffset[] = {0, sizeof(colIndices) / sizeof(int)}; - - CHECK(paddle_matrix_sparse_copy_from(mat, - rowOffset, - sizeof(rowOffset) / sizeof(int), - colIndices, - sizeof(colIndices) / sizeof(int), - NULL /*values array is NULL.*/, - 0 /*size of the value arrary is 0.*/)); - CHECK(paddle_arguments_set_value(in_args, 0, mat)); - ``` - - 下面的代码片段在创建了一个CPU上的带元素值的稀疏矩阵: - ```cpp - paddle_matrix mat = paddle_matrix_create_sparse(1, layer_size, nnz, false, false); - int colIndices[] = {9, 93, 109}; // layer_size here is greater than 109. - int rowOffset[] = {0, sizeof(colIndices) / sizeof(int)}; - float values[] = {0.5, 0.5, 0.5}; - - CHECK(paddle_matrix_sparse_copy_from(mat, - rowOffset, - sizeof(rowOffset) / sizeof(int), - colIndices, - sizeof(colIndices) / sizeof(int), - values, - sizeof(values) / sizeof(float))); - ``` - -- 注意事项: - 1. 移动端预测**不支持**稀疏矩阵及相关的接口。 + 下面的代码片段创建了一个CPU上的二值稀疏矩阵: + + ```c + paddle_matrix mat = paddle_matrix_create_sparse(1, layer_size, nnz, true, false); + int colIndices[] = {9, 93, 109}; // layer_size here is greater than 109. + int rowOffset[] = {0, sizeof(colIndices) / sizeof(int)}; + + CHECK(paddle_matrix_sparse_copy_from(mat, + rowOffset, + sizeof(rowOffset) / sizeof(int), + colIndices, + (colIndices) / sizeof(int), + NULL /*values array is NULL.*/, + 0 /*size of the value arrary is 0.*/)); + CHECK(paddle_arguments_set_value(in_args, 0, mat)); + ``` + 下面的代码片段在创建了一个CPU上的带元素值的稀疏矩阵: + ```c + paddle_matrix mat = paddle_matrix_create_sparse(1, layer_size, nnz, false, false); + int colIndices[] = {9, 93, 109}; // layer_size here is greater than 109. + int rowOffset[] = {0, sizeof(colIndices) / sizeof(int)}; + float values[] = {0.5, 0.5, 0.5}; + + CHECK(paddle_matrix_sparse_copy_from(mat, + rowOffset, + sizeof(rowOffset) / sizeof(int), + colIndices, + sizeof(colIndices) / sizeof(int), + values, + sizeof(values) / sizeof(float))); + ``` + 注意事项: + 1. 移动端预测**不支持**稀疏矩阵及相关的接口。 ### 组织序列信息 @@ -143,7 +142,7 @@ 图2 是PaddlePaddle中单层序列和双层序列存储示意图。

-
图2. 序列输入示意图. +
图2. 序列输入示意图

- 单层序列 @@ -153,7 +152,7 @@ 2. 这时的`sequence_start_positions`为:`[0, 5, 8, 10, 14]`; 3. 不论数据域是`paddle_ivector`类型还是`paddle_matrix`类型,都可以通过调用下面的接口为原有的数据输入附加上序列信息,使之变为一个单层序列输入,代码片段如下: - ```cpp + ```c int seq_pos_array[] = {0, 5, 8, 10, 14}; paddle_ivector seq_pos = paddle_ivector_create( seq_pos_array, sizeof(seq_pos_array) / sizeof(int), false, false); @@ -166,11 +165,10 @@ 图2 (b) 展示了一个含有4个序列的`batch`输入; 1. 4个序列的长度分别为:5、3、2、4;这四个序列又分别含有3、2、1、2个子序列; 1. 这时的需要同时提供: - - 1. 外层序列在`batch`中的起始偏移`:[0, 5, 8, 10, 14]`; - - 2. 内层序列在`batch`中的起始偏移:`[0, 2, 3, 5, 7, 8, 10, 13, 14]`; - + - 外层序列在`batch`中的起始偏移`:[0, 5, 8, 10, 14]`; + - 内层序列在`batch`中的起始偏移:`[0, 2, 3, 5, 7, 8, 10, 13, 14]`; 1. 不论数据域是`paddle_ivector`类型还是`paddle_matrix`类型,这时需要调用创建序列信息和为`argument`设置序列信息的接口**两次**,分别为数据输入添加外层序列和内层序列的序列信息,使之变为一个双层序列输入,代码片段如下: - ```cpp + ```c // set the sequence start positions for the outter sequences. int outter_seq_pos_array[] = {0, 5, 8, 10, 14}; paddle_ivector seq_pos = @@ -193,28 +191,75 @@ CHECK(paddle_arguments_set_sequence_start_pos(in_args, 0, 1, seq_pos)); ``` -- 注意事项: - 1. 当一个`batch`中含有多个序列,**不支持序列长度为`0`的序列(也就是空输入)** 作为输入。不同计算层对空输入的处理策略有可能不同,潜在会引起未定义行为,或者引起行时错误,请在输入时进行合法性检查。 +注意事项: +1. 当一个`batch`中含有多个序列,**不支持序列长度为`0`的序列(也就是空输入)** 作为输入。不同计算层对空输入的处理策略有可能不同,潜在会引起未定义行为,或者引起行时错误,请在输入时进行合法性检查。 ### Python 端数据类型说明 下表列出了Python端训练接口暴露的数据类型(`paddle.layer.data`函数`type`字段的取值)对应于调用C-API需要创建的数据类型: -Python 端数据类型 | C-API 输入数据类型| -:-------------: | :-------------: -`paddle.data_type.integer_value` |整型数组,无需附加序列信息| -`paddle.data_type.dense_vector` |浮点型稠密矩阵,无需附加序列信息| -`paddle.data_type.sparse_binary_vector` |浮点型稀疏矩阵,无需提供非零元的值,默认为1,无需附加序列信息| -`paddle.data_type.sparse_vector` |浮点型稀疏矩阵,需提供非零元的值,无需附加序列信息| -`paddle.data_type.integer_value_sequence` |整型数组,需附加序列信息| -`paddle.data_type.dense_vector_sequence` |浮点型稠密矩阵,需附加序列信息| -`paddle.data_type.sparse_binary_vector_sequence` |浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加序列信息| -`paddle.data_type.sparse_vector_sequence` |浮点型稀疏矩阵,需提供非零元的值,需附加序列信息| -`paddle.data_type.integer_value_sub_sequence` |整型数组,需附加双层序列信息| -`paddle.data_type.dense_vector_sub_sequence` |浮点型稠密矩阵,需附加双层序列信息| -`paddle.data_type.sparse_binary_vector_sub_sequence` |浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加双层序列信息| -`paddle.data_type.sparse_vector_sub_sequence` |浮点型稀疏矩阵,需提供非零元的值,需附加双层序列信息| - + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Python 端数据类型C-API 输入数据类型
paddle.data_type.integer_value整型数组,无需附加序列信息
paddle.data_type.dense_vector浮点型稠密矩阵,无需附加序列信息
paddle.data_type.sparse_binary_vector浮点型稀疏矩阵,无需提供非零元的值,默认为1,无需附加序列信息
paddle.data_type.sparse_vector浮点型稀疏矩阵,需提供非零元的值,无需附加序列信息
paddle.data_type.integer_value_sequence整型数组,需附加序列信息
paddle.data_type.dense_vector_sequence浮点型稠密矩阵,需附加序列信息
paddle.data_type.sparse_binary_vector_sequence浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加序列信息
paddle.data_type.sparse_vector_sequence浮点型稀疏矩阵,需提供非零元的值,需附加序列信息
paddle.data_type.integer_value_sub_sequence整型数组,需附加双层序列信息
paddle.data_type.dense_vector_sub_sequence浮点型稠密矩阵,需附加双层序列信息
paddle.data_type.sparse_binary_vector_sub_sequence浮点型稀疏矩阵,无需提供非零元的值,默认为1,需附加双层序列信息
paddle.data_type.sparse_vector_sub_sequence浮点型稀疏矩阵,需提供非零元的值,需附加双层序列信息
+ +
### 输出数据 PaddlePaddle中一个计算层的输出数据组织方式和输入数据组织方式完全相同。一个输出数据同样被组织为一个`argument`,`argument`通过`paddle_matrix`或`paddle_ivector`存数数据,如果输出是一个序列,那么会携带有`sequence_start_positions`信息。调用C-API相关接口,读取需要的结果即可。 -- GitLab From cd77b628e0cb4e274eb39a9f1ef6345bbcad045c Mon Sep 17 00:00:00 2001 From: qijun Date: Wed, 20 Dec 2017 16:14:40 +0800 Subject: [PATCH 0047/2305] merge baidu/develop --- paddle/operators/get_places_op.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/paddle/operators/get_places_op.cc b/paddle/operators/get_places_op.cc index 959aae16a8d..4d5059c264e 100644 --- a/paddle/operators/get_places_op.cc +++ b/paddle/operators/get_places_op.cc @@ -58,8 +58,7 @@ class GetPlacesOp : public framework::OperatorBase { class GetPlacesOpProtoMaker : public framework::OpProtoAndCheckerMaker { public: - GetPlacesOpProtoMaker(framework::OpProto *proto, - framework::OpAttrChecker *op_checker) + GetPlacesOpProtoMaker(OpProto *proto, OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddOutput("Out", "vector of Place"); AddAttr("device_count", "(int)device count").SetDefault(1); -- GitLab From 5f5c00f24493a72af8d3e0f6b72980a3e557e3ff Mon Sep 17 00:00:00 2001 From: qijun Date: Thu, 21 Dec 2017 16:10:20 +0800 Subject: [PATCH 0048/2305] follow comments --- python/paddle/v2/fluid/layers/__init__.py | 6 +++--- python/paddle/v2/fluid/layers/{utils.py => devie.py} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename python/paddle/v2/fluid/layers/{utils.py => devie.py} (100%) diff --git a/python/paddle/v2/fluid/layers/__init__.py b/python/paddle/v2/fluid/layers/__init__.py index cb885306287..50ac0aba01a 100644 --- a/python/paddle/v2/fluid/layers/__init__.py +++ b/python/paddle/v2/fluid/layers/__init__.py @@ -8,8 +8,8 @@ import tensor from tensor import * import control_flow from control_flow import * -import utils -from utils import * +import device +from device import * __all__ = [] __all__ += nn.__all__ @@ -17,4 +17,4 @@ __all__ += io.__all__ __all__ += tensor.__all__ __all__ += control_flow.__all__ __all__ += ops.__all__ -__all__ += utils.__all__ +__all__ += device.__all__ diff --git a/python/paddle/v2/fluid/layers/utils.py b/python/paddle/v2/fluid/layers/devie.py similarity index 100% rename from python/paddle/v2/fluid/layers/utils.py rename to python/paddle/v2/fluid/layers/devie.py -- GitLab From f899150e0a45f97e836f02ad989e30c0f337310c Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 21 Dec 2017 08:24:17 +0000 Subject: [PATCH 0049/2305] pass forward runtime --- paddle/framework/backward.cc | 3 +- paddle/framework/lod_tensor.cc | 25 ++++ paddle/framework/lod_tensor.h | 3 + paddle/framework/operator.cc | 20 ++- paddle/framework/tensor.h | 11 ++ paddle/operators/parallel_do_op.cc | 132 ++++++++++++------ python/paddle/v2/fluid/layers/control_flow.py | 13 +- .../paddle/v2/fluid/tests/test_parallel_op.py | 18 ++- 8 files changed, 177 insertions(+), 48 deletions(-) diff --git a/paddle/framework/backward.cc b/paddle/framework/backward.cc index faf6e60cbd1..358ddae083d 100644 --- a/paddle/framework/backward.cc +++ b/paddle/framework/backward.cc @@ -429,7 +429,8 @@ std::vector> MakeBlockBackward( VLOG(5) << "Making backward " << (*it)->Type() << " op"; std::vector> op_grads; - if ((*it)->Type() == "recurrent" || (*it)->Type() == "while") { + if ((*it)->Type() == "recurrent" || (*it)->Type() == "while" || + (*it)->Type() == "parallel_do") { int step_block_idx = (*it)->GetBlockAttr("sub_block"); BlockDescBind* backward_block = CreateStepBlock( program_desc, no_grad_vars, grad_to_var, step_block_idx); diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index fdf6de4babf..4198847ad03 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -314,5 +314,30 @@ void DeserializeFromStream(std::istream &is, LoDTensor *tensor) { } } +void LoDTensor::MergeLoDTensor( + const std::vector &lod_tensors, platform::Place place) { + PADDLE_ENFORCE(platform::is_cpu_place(place)); + PADDLE_ENFORCE(!lod_tensors.empty()); + + framework::DDim new_dim = lod_tensors[0]->dims(); + std::type_index new_type = lod_tensors[0]->type(); + for (auto *lod : lod_tensors) { + PADDLE_ENFORCE(new_dim == lod->dims()); + PADDLE_ENFORCE(new_type == lod->type()); + PADDLE_ENFORCE(platform::is_cpu_place(lod->place())); + } + new_dim[0] *= lod_tensors.size(); + Resize(new_dim); + + auto *dst_ptr = reinterpret_cast(mutable_data(place, new_type)); + for (auto *src : lod_tensors) { + auto size = src->numel() * SizeOfType(src->type()); + memory::Copy(boost::get(place), dst_ptr, + boost::get(src->place()), + src->data(), size); + dst_ptr += size; + } +} + } // namespace framework } // namespace paddle diff --git a/paddle/framework/lod_tensor.h b/paddle/framework/lod_tensor.h index 9411c96aea4..989d8c1c4fe 100644 --- a/paddle/framework/lod_tensor.h +++ b/paddle/framework/lod_tensor.h @@ -144,6 +144,9 @@ class LoDTensor : public Tensor { */ void ShrinkInLevel(size_t level, size_t elem_begin, size_t elem_end); + void MergeLoDTensor(const std::vector& lod_tensors, + platform::Place place); + private: LoD lod_; }; diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index e83d7547831..26bc646753c 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -179,10 +179,13 @@ static const Tensor* GetTensorFromVar(const Variable* var) { const Tensor* t = nullptr; if (var->IsType()) { t = &(var->Get()); + } else if (var->IsType()) { + t = &(var->Get()); } else if (var->IsType()) { t = &(var->Get().value()); } else { - PADDLE_THROW("Variable type must be LoDTensor/SelectedRows."); + PADDLE_THROW("Variable type_id %s, expect LoDTensor/SelectedRows.", + var->Type().name()); } return t; } @@ -191,10 +194,13 @@ static Tensor* GetMutableTensorFromVar(Variable* var) { Tensor* t = nullptr; if (var->IsType()) { t = var->GetMutable(); + } else if (var->IsType()) { + t = var->GetMutable(); } else if (var->IsType()) { t = var->GetMutable()->mutable_value(); } else { - PADDLE_THROW("Variable type must be LoDTensor/SelectedRows."); + PADDLE_THROW("Variable type_id %s, expect LoDTensor/SelectedRows.", + var->Type().name()); } return t; } @@ -359,10 +365,13 @@ class RuntimeInferShapeContext : public InferShapeContext { Variable* var = scope_.FindVar(name); if (var->IsType()) { return var->Get().dims(); + } else if (var->IsType()) { + return var->Get().dims(); } else if (var->IsType()) { return var->Get().GetCompleteDims(); } else { - PADDLE_THROW("Variable type must be LoDTensor/SelectedRows."); + PADDLE_THROW("Variable %s type_id %s, expect LoDTensor/SelectedRows.", + name, var->Type().name()); } } @@ -370,10 +379,13 @@ class RuntimeInferShapeContext : public InferShapeContext { Variable* var = scope_.FindVar(name); if (var->IsType()) { var->GetMutable()->Resize(dim); + } else if (var->IsType()) { + var->GetMutable()->Resize(dim); } else if (var->IsType()) { var->GetMutable()->set_height(dim[0]); } else { - PADDLE_THROW("Variable type must be LoDTensor/SelectedRows."); + PADDLE_THROW("Variable %s type_id %s, expect LoDTensor/SelectedRows.", + name, var->Type().name()); } } diff --git a/paddle/framework/tensor.h b/paddle/framework/tensor.h index 6a0c5133c9a..837f63c7069 100644 --- a/paddle/framework/tensor.h +++ b/paddle/framework/tensor.h @@ -55,6 +55,8 @@ class Tensor { template inline const T* data() const; + inline void switch_place(platform::Place new_place); + /** * @brief Return a pointer to mutable memory block. * @note If not exist, then allocation. @@ -183,6 +185,15 @@ class Tensor { size_t offset_; }; +inline void Tensor::switch_place(platform::Place new_place) { + if (holder_->place() == new_place) { + return; + } + + // TODO(tonyyang-svail): do memcpy here. + PADDLE_THROW("Not Implemented"); +} + } // namespace framework } // namespace paddle diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index bde59c7e7ad..c0c1de7369a 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -13,9 +13,11 @@ limitations under the License. */ #include +#include "chunk_eval_op.h" #include "paddle/framework/executor.h" #include "paddle/framework/op_registry.h" #include "paddle/framework/operator.h" +#include "paddle/platform/place.h" namespace paddle { namespace operators { @@ -28,10 +30,6 @@ constexpr char kOutputs[] = "outputs"; constexpr char kParallelScopes[] = "parallel_scopes"; constexpr char kParallelBlock[] = "sub_block"; -// #define GRAD_SUFFIX "@GRAD" -// constexpr char kInputGrads[] = "inputs" GRAD_SUFFIX; -// constexpr char kOutputGrads[] = "outputs" GRAD_SUFFIX; -// constexpr char kParamGrads[] = "parameters" GRAD_SUFFIX; using ParallelScopeVar = std::vector; using OperatorBase = framework::OperatorBase; @@ -46,21 +44,66 @@ class ParallelDoOp : public OperatorBase { void Run(const framework::Scope &scope, const platform::DeviceContext &dev_ctx) const override { - // create scope - // copy parameters - } -}; + auto *block = Attr(kParallelBlock); + auto *program = block->Program(); -class ParallelDoGradOp : public OperatorBase { - public: - ParallelDoGradOp(const std::string &type, - const framework::VariableNameMap &inputs, - const framework::VariableNameMap &outputs, - const framework::AttributeMap &attrs) - : OperatorBase(type, inputs, outputs, attrs) {} + // TODO(tonyyang-svail): get places from input + std::vector places; + places.emplace_back(platform::CPUPlace()); + places.emplace_back(platform::CPUPlace()); - void Run(const framework::Scope &scope, - const platform::DeviceContext &dev_ctx) const override {} + std::vector sub_scopes; + for (int place_idx = 0; place_idx < places.size(); ++place_idx) { + VLOG(3) << "Run " << place_idx; + + sub_scopes.push_back(&scope.NewScope()); + + auto &place = places[place_idx]; + auto *cur_scope = sub_scopes[place_idx]; + + // copy parameter + if (dev_ctx.GetPlace() != place) { + PADDLE_THROW("Not Implemented"); + } + + // feed input + for (auto &argu : Inputs(kInputs)) { + auto *var = scope.FindVar(argu); + const auto &tensor = var->Get(); + if (!tensor.lod().empty()) { + PADDLE_THROW("Disable parallel lod for now"); + } else { + PADDLE_ENFORCE(tensor.dims()[0] % places.size() == 0, + "Batch size should be divided by places size"); + int begin = place_idx * tensor.dims()[0] / places.size(); + int end = (place_idx + 1) * tensor.dims()[0] / places.size(); + auto feed_tensor = tensor.Slice(begin, end); + feed_tensor.switch_place(place); + + auto *cur_var = cur_scope->Var(argu); + auto *cur_tensor = cur_var->GetMutable(); + *cur_tensor = feed_tensor; + } + } + + // execute + auto executor = framework::Executor(place); + executor.Run(*program, cur_scope, block->ID(), + false /*create_local_scope*/); + } + + // merge output + for (auto &o_name : Outputs(kOutputs)) { + std::vector lod_tensors; + for (auto *sub_scope : sub_scopes) { + lod_tensors.push_back(&sub_scope->FindVar(o_name)->Get()); + } + + auto *lod_tensor_to_be_merged = + scope.FindVar(o_name)->GetMutable(); + lod_tensor_to_be_merged->MergeLoDTensor(lod_tensors, dev_ctx.GetPlace()); + } + } }; class ParallelDoOpProtoMaker : public framework::OpProtoAndCheckerMaker { @@ -80,16 +123,28 @@ ParallelDo Operator. } }; +class ParallelDoGradOp : public OperatorBase { + public: + ParallelDoGradOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : OperatorBase(type, inputs, outputs, attrs) {} + + void Run(const framework::Scope &scope, + const platform::DeviceContext &dev_ctx) const override {} +}; + class ParallelDoGradOpDescMaker : public framework::SingleGradOpDescMaker { public: using framework::SingleGradOpDescMaker::SingleGradOpDescMaker; protected: virtual std::unique_ptr Apply() const { - PADDLE_THROW("Not Implemented"); auto *grad = new framework::OpDescBind(); - grad->SetType("recurrent_grad"); + grad->SetType("parallel_do_grad"); for (auto &input_param : this->InputNames()) { + LOG(INFO) << input_param; grad->SetInput(input_param, this->Input(input_param)); grad->SetOutput(framework::GradVarName(input_param), this->InputGrad(input_param)); @@ -116,26 +171,25 @@ class ParallelDoGradOpDescMaker : public framework::SingleGradOpDescMaker { class ParallelDoGradOpShapeInference : public framework::InferShapeBase { public: void operator()(framework::InferShapeContext *ctx) const override { - PADDLE_THROW("Not Implemented"); - // std::vector input{kInputs}; - // std::vector output{kOutputs}; - // for (auto &s : input) { - // PADDLE_ENFORCE(ctx->HasInputs(s)); - // PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(s)), - // "Cannot find the gradient variable %s", - // framework::GradVarName(s)); - // } - // for (auto &s : output) { - // PADDLE_ENFORCE(ctx->HasInputs(s)); - // } - // for (auto &s : input) { - // ctx->SetOutputsDim(framework::GradVarName(s), ctx->GetInputsDim(s)); - // } - // if (ctx->HasInputs(kParameters)) { - // PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(kParameters))); - // ctx->SetOutputsDim(framework::GradVarName(kParameters), - // ctx->GetInputsDim(kParameters)); - // } + std::vector input{kParameters, kInputs}; + std::vector output{kOutputs}; + for (auto &s : input) { + PADDLE_ENFORCE(ctx->HasInputs(s)); + PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(s)), + "Cannot find the gradient variable %s", + framework::GradVarName(s)); + } + for (auto &s : output) { + PADDLE_ENFORCE(ctx->HasInputs(s)); + } + for (auto &s : input) { + ctx->SetOutputsDim(framework::GradVarName(s), ctx->GetInputsDim(s)); + } + if (ctx->HasInputs(kParameters)) { + PADDLE_ENFORCE(ctx->HasOutputs(framework::GradVarName(kParameters))); + ctx->SetOutputsDim(framework::GradVarName(kParameters), + ctx->GetInputsDim(kParameters)); + } } }; diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index 09ab9726d1d..aafecdafa24 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -140,7 +140,18 @@ class ParallelDo(object): step_scope = parent_block.create_var( type=core.VarDesc.VarType.STEP_SCOPES) + self.outputs = [ + parent_block.create_var( + name=o.name, + shape=o.shape, + dtype=o.dtype, + lod_level=o.lod_level, + persistable=o.persistable, + stop_gradient=o.stop_gradient) for o in self.outputs + ] + inputs = [parent_block.var(i.name) for i in self.inputs] + outputs = [parent_block.var(o.name) for o in self.outputs] parent_block.append_op( type='parallel_do', @@ -149,7 +160,7 @@ class ParallelDo(object): 'parameters': self.get_parameters(), 'places': self.places }, - outputs={'outputs': self.outputs, + outputs={'outputs': outputs, 'parallel_scopes': [step_scope]}, attrs={'sub_block': current_block}) diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index 1e643032849..61126cc9da1 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -12,7 +12,11 @@ import paddle.v2.fluid.core as core class ParallelOpTest(unittest.TestCase): def setUp(self): x = layers.data( - shape=[2, 3, 4], dtype='float32', name='x', append_batch_size=False) + shape=[-1, 3, 4], + dtype='float32', + name='x', + append_batch_size=False, + stop_gradient=False) places = fluid.default_main_program().global_block().create_var() pd = layers.ParallelDo(places=places) @@ -22,8 +26,16 @@ class ParallelOpTest(unittest.TestCase): hidden = layers.fc(input=data, size=7) pd.write_output(hidden) data = pd() - print data - print fluid.default_main_program() + loss = layers.mean(x=data) + append_backward_ops(loss) + + exe = fluid.Executor(fluid.CPUPlace()) + exe.run(fluid.default_startup_program()) + exe.run(fluid.default_main_program(), + feed={ + x.name: np.random.uniform(0.1, 0.6, + (2, 3, 4)).astype("float32") + }) def test_forward(self): pass -- GitLab From f879ef23c3d2b364d5c590ff7b59ec8d16ccfb34 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Fri, 22 Dec 2017 03:51:11 +0000 Subject: [PATCH 0050/2305] pass forward backward runtime --- paddle/framework/lod_tensor.cc | 39 +++++++++++ paddle/framework/lod_tensor.h | 3 + paddle/operators/elementwise_op.h | 5 ++ paddle/operators/parallel_do_op.cc | 106 +++++++++++++++++++++-------- 4 files changed, 124 insertions(+), 29 deletions(-) diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index 4198847ad03..beb2edee23d 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -314,6 +314,45 @@ void DeserializeFromStream(std::istream &is, LoDTensor *tensor) { } } +std::vector LoDTensor::SplitLoDTensor( + const std::vector places) const { + check_memory_size(); + // PADDLE_ENFORCE(lod().empty() || (lod().size() == 1 && lod()[0].empty()) + // , "Disable parallel lod for now"); + PADDLE_ENFORCE(lod().empty(), "Disable parallel lod for now"); + PADDLE_ENFORCE(dims()[0] % places.size() == 0, + "Batch size should be divided by places size"); + + std::vector lods; + for (int place_idx = 0; place_idx < places.size(); ++place_idx) { + int begin = place_idx * dims()[0] / places.size(); + int end = (place_idx + 1) * dims()[0] / places.size(); + auto src = Slice(begin, end); + + LoDTensor dst; + dst.Resize(src.dims()); + auto &dst_place = places[place_idx]; + auto dst_ptr = dst.mutable_data(dst_place, src.type()); + + // TODO(tonyyang-svail): + // change the following to framework::CopyFrom + auto src_place = src.place(); + auto src_ptr = src.data(); + auto size = src.numel() * SizeOfType(src.type()); + if (platform::is_cpu_place(src_place) && + platform::is_cpu_place(dst_place)) { + memory::Copy(boost::get(dst_place), dst_ptr, + boost::get(src_place), src_ptr, size); + } else { + PADDLE_THROW("Not Implemented"); + } + + lods.emplace_back(dst); + } + + return lods; +} + void LoDTensor::MergeLoDTensor( const std::vector &lod_tensors, platform::Place place) { PADDLE_ENFORCE(platform::is_cpu_place(place)); diff --git a/paddle/framework/lod_tensor.h b/paddle/framework/lod_tensor.h index 989d8c1c4fe..fae36892f00 100644 --- a/paddle/framework/lod_tensor.h +++ b/paddle/framework/lod_tensor.h @@ -144,6 +144,9 @@ class LoDTensor : public Tensor { */ void ShrinkInLevel(size_t level, size_t elem_begin, size_t elem_end); + std::vector SplitLoDTensor( + const std::vector places) const; + void MergeLoDTensor(const std::vector& lod_tensors, platform::Place place); diff --git a/paddle/operators/elementwise_op.h b/paddle/operators/elementwise_op.h index ea533503e49..b8bbdb1e2a8 100644 --- a/paddle/operators/elementwise_op.h +++ b/paddle/operators/elementwise_op.h @@ -34,6 +34,8 @@ class ElementwiseOp : public framework::OperatorWithKernel { auto x_dim = ctx->GetInputDim("X"); auto y_dim = ctx->GetInputDim("Y"); + LOG(INFO) << x_dim; + LOG(INFO) << y_dim; PADDLE_ENFORCE_GE(x_dim.size(), y_dim.size(), "Rank of first input must >= rank of second input."); ctx->SetOutputDim("Out", x_dim); @@ -118,6 +120,9 @@ class ElementwiseOpGrad : public framework::OperatorWithKernel { auto x_dims = ctx->GetInputDim("X"); auto y_dims = ctx->GetInputDim("Y"); auto out_dims = ctx->GetInputDim(framework::GradVarName("Out")); + LOG(INFO) << x_dims; + LOG(INFO) << y_dims; + LOG(INFO) << out_dims; PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), "Rank of first input must >= rank of second input."); diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index c0c1de7369a..b15d171b9b6 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -13,11 +13,9 @@ limitations under the License. */ #include -#include "chunk_eval_op.h" + #include "paddle/framework/executor.h" #include "paddle/framework/op_registry.h" -#include "paddle/framework/operator.h" -#include "paddle/platform/place.h" namespace paddle { namespace operators { @@ -31,10 +29,31 @@ constexpr char kParallelScopes[] = "parallel_scopes"; constexpr char kParallelBlock[] = "sub_block"; -using ParallelScopeVar = std::vector; +// using ParallelScopeVar = std::vector; +using LoDTensor = framework::LoDTensor; using OperatorBase = framework::OperatorBase; -class ParallelDoOp : public OperatorBase { +void SplitTensorAndMoveTensorToScopes( + const framework::Scope &scope, + const std::vector &sub_scopes, + const std::vector &places, + const std::vector &names) { + for (auto &argu : names) { + auto *var = scope.FindVar(argu); + const auto &tensor = var->Get(); + auto lod_tensors = tensor.SplitLoDTensor(places); + + for (auto &lod : lod_tensors) { + LOG(INFO) << lod.dims(); + } + + for (int i = 0; i < sub_scopes.size(); ++i) { + *sub_scopes[i]->Var(argu)->GetMutable() = lod_tensors[i]; + } + } +} + +class ParallelDoOp : public framework::OperatorBase { public: ParallelDoOp(const std::string &type, const framework::VariableNameMap &inputs, @@ -52,11 +71,18 @@ class ParallelDoOp : public OperatorBase { places.emplace_back(platform::CPUPlace()); places.emplace_back(platform::CPUPlace()); - std::vector sub_scopes; + auto &sub_scopes = *scope.FindVar(Output(kParallelScopes)) + ->GetMutable>(); + // std::vector sub_scopes; for (int place_idx = 0; place_idx < places.size(); ++place_idx) { - VLOG(3) << "Run " << place_idx; - sub_scopes.push_back(&scope.NewScope()); + } + + SplitTensorAndMoveTensorToScopes(scope, sub_scopes, places, + Inputs(kInputs)); + + for (int place_idx = 0; place_idx < places.size(); ++place_idx) { + VLOG(3) << "Run " << place_idx; auto &place = places[place_idx]; auto *cur_scope = sub_scopes[place_idx]; @@ -66,26 +92,6 @@ class ParallelDoOp : public OperatorBase { PADDLE_THROW("Not Implemented"); } - // feed input - for (auto &argu : Inputs(kInputs)) { - auto *var = scope.FindVar(argu); - const auto &tensor = var->Get(); - if (!tensor.lod().empty()) { - PADDLE_THROW("Disable parallel lod for now"); - } else { - PADDLE_ENFORCE(tensor.dims()[0] % places.size() == 0, - "Batch size should be divided by places size"); - int begin = place_idx * tensor.dims()[0] / places.size(); - int end = (place_idx + 1) * tensor.dims()[0] / places.size(); - auto feed_tensor = tensor.Slice(begin, end); - feed_tensor.switch_place(place); - - auto *cur_var = cur_scope->Var(argu); - auto *cur_tensor = cur_var->GetMutable(); - *cur_tensor = feed_tensor; - } - } - // execute auto executor = framework::Executor(place); executor.Run(*program, cur_scope, block->ID(), @@ -132,7 +138,49 @@ class ParallelDoGradOp : public OperatorBase { : OperatorBase(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, - const platform::DeviceContext &dev_ctx) const override {} + const platform::DeviceContext &dev_ctx) const override { + auto *block = Attr(kParallelBlock); + auto *program = block->Program(); + + auto &sub_scopes = scope.FindVar(Input(kParallelScopes)) + ->Get>(); + + // TODO(tonyyang-svail): get places from input + std::vector places; + places.emplace_back(platform::CPUPlace()); + places.emplace_back(platform::CPUPlace()); + + // feed output@grad + SplitTensorAndMoveTensorToScopes(scope, sub_scopes, places, + Inputs(framework::GradVarName(kOutputs))); + + for (auto &s : Inputs(framework::GradVarName(kOutputs))) { + LOG(INFO) << s; + LOG(INFO) << scope.FindVar(s)->Get().dims(); + for (auto *sub_scope : sub_scopes) { + LOG(INFO) << sub_scope->FindVar(s)->Get().dims(); + } + } + // exe run + for (int place_idx = 0; place_idx < places.size(); ++place_idx) { + VLOG(3) << "Run " << place_idx; + + auto &place = places[place_idx]; + auto *cur_scope = sub_scopes[place_idx]; + + // copy parameter + if (dev_ctx.GetPlace() != place) { + PADDLE_THROW("Not Implemented"); + } + + // execute + auto executor = framework::Executor(place); + executor.Run(*program, cur_scope, block->ID(), + false /*create_local_scope*/); + } + + // merge grad + } }; class ParallelDoGradOpDescMaker : public framework::SingleGradOpDescMaker { -- GitLab From 079b82436dff0d2883ba0fb048bb1d1d405b6687 Mon Sep 17 00:00:00 2001 From: ying Date: Tue, 26 Dec 2017 15:55:11 +0800 Subject: [PATCH 0051/2305] follow comments. --- ...mple_example.md => a_simple_example_cn.md} | 81 ++++++------ doc/howto/usage/capi/compile_paddle_lib.md | 70 ---------- doc/howto/usage/capi/compile_paddle_lib_cn.md | 122 ++++++++++++++++++ doc/howto/usage/capi/index_cn.rst | 6 +- ...ts.md => organization_of_the_inputs_cn.md} | 2 + 5 files changed, 172 insertions(+), 109 deletions(-) rename doc/howto/usage/capi/{a_simple_example.md => a_simple_example_cn.md} (68%) delete mode 100644 doc/howto/usage/capi/compile_paddle_lib.md create mode 100644 doc/howto/usage/capi/compile_paddle_lib_cn.md rename doc/howto/usage/capi/{organization_of_the_inputs.md => organization_of_the_inputs_cn.md} (99%) diff --git a/doc/howto/usage/capi/a_simple_example.md b/doc/howto/usage/capi/a_simple_example_cn.md similarity index 68% rename from doc/howto/usage/capi/a_simple_example.md rename to doc/howto/usage/capi/a_simple_example_cn.md index b1eceea38bc..abe07a41cfa 100644 --- a/doc/howto/usage/capi/a_simple_example.md +++ b/doc/howto/usage/capi/a_simple_example_cn.md @@ -4,8 +4,8 @@ ### 使用流程 -使用 C-API 分为:准备工作和预测程序开发两部分。 -- 准备 +使用 C-API 分为:准备预测模型和预测程序开发两部分。 +- 准备预测模型 1. 将神经网络模型结构进行序列化。 - 调用C-API预测时,需要提供序列化之后的网络结构和训练好的模型参数文件。 1. 将PaddlePaddle训练出的模型参数文件(多个)合并成一个文件。 @@ -14,18 +14,17 @@ - **注意**:以上两种方式只需选择其一即可。 - 调用 PaddlePaddle C-API 开发预测序 1. 初始化PaddlePaddle运行环境。 - 1. 创建神经网络的输入,组织输入数据。 1. 加载模型。 + 1. 创建神经网络的输入,组织输入数据。 1. 进行前向计算,获得计算结果。 1. 清理。 +本文档以手写数字识别任务为例,介绍如何使用 C-API 进行预测,完整代码请查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)。 -这里我们以手写数字识别任务为例,介绍如何使用 C-API 进行预测,完整代码请查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)。 - -运行目录下的 `python mnist_v2.py` 可以使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。脚本中的模型定义了一个简单的含有[两个隐层的全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。训练好的模型默认保存在当前运行目录下的`models`目录中。下面,我们将调用 C-API 加载训练好的模型进行预测。 +### 准备预测模型 - -### 外部准备 +通过在终端执行`python mnist_v2.py` +运行[目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)下的 `mnist_v2.py` s可以使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。脚本中的模型定义了一个简单的含有[两个隐层的全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。训练好的模型默认保存在当前运行目录下的`models`目录中。下面,我们将调用 C-API 加载训练好的模型进行预测。 1. 序列化神经网络模型配置 @@ -52,8 +51,7 @@ 代码示例如下: ```python - from paddle.utils.merge_model import merge_v2_model - + from paddle.utils.merge_model import merge_v2_modelss from mnist_v2 import network net = network(is_infer=True) @@ -70,54 +68,65 @@ ### 编写预测代码 -#### step 1. 初始化及加载模型 +#### step 1. 初始化PaddlePaddle运行环境 +使用C-API第一步需首先调用[`paddle_init`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/main.h#L27) 初始化PaddlePaddle运行环境。接口接受两个参数:参数的个数和参数。 -1. 初始化PaddlePaddle运行环境。 - ```c - // Initalize the PaddlePaddle runtime environment. - char* argv[] = {"--use_gpu=False"}; - CHECK(paddle_init(1, (char**)argv)); - ``` +下面的代码片段在初始化PaddlePaddle运行环境时指定不使用GPU: -1. 加载训练好的模型。 +```c +// Initalize the PaddlePaddle runtime environment. +char* argv[] = {"--use_gpu=False"}; +CHECK(paddle_init(1, (char**)argv)); +``` - 这里需要介绍C-API使用中的一个重要概念:Gradient Machine。概念上,在 PaddlePaddle 内部,一个GradientMachine类的对象管理着一组计算层(PaddlePaddle Layers)来完成前向和反向计算,并处理与之相关的所有细节。特别的,在调用C-API预测时只需进行前向计算。这篇文档的之后部分我们会使用`gradient machine`来特指调用PaddlePaddle C-API创建的GradientMachine类的对象。 +下面的代码片段在初始化PaddlePaddle运行环境时指定了两个参数:不使用GPU和[使用MKLDNN](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/design/mkl/mkldnn.md): - 每一个 `gradient machine` 都会管理维护一份训练好的模型,模型可以通过以下两种方式获取: - 1. 从磁盘加载;这时`gradient machine`会独立拥有一份训练好的模型; - 1. 共享自其它`gradient machine`的模型;这种情况多出现在使用多线程预测时; +```c +char* argv[] = {"--use_gpu=False", "--use_mkldnn=True"}; +CHECK(paddle_init(2, (char**)argv)); +``` - 下面的代码片段创建 `gradient machine`,并从指定路径加载训练好的模型。 +#### step2. 加载模型 - ```c - // Read the binary configuration file generated by `convert_protobin.sh` - long size; - void* buf = read_config(CONFIG_BIN, &size); +这里介绍C-API使用中的一个重要概念:Gradient Machine。概念上,在 PaddlePaddle 内部一个GradientMachine类的对象管理着一组计算层(PaddlePaddle Layers)来完成前向和反向计算,并处理与之相关的所有细节。在调用C-API预测时,只需进行前向计算而无需调用反向计算。这篇文档的之后部分我们会使用`gradient machine`来特指调用PaddlePaddle C-API创建的GradientMachine类的对象。 - // Create the gradient machine for inference. - paddle_gradient_machine machine; - CHECK(paddle_gradient_machine_create_for_inference(&machine, buf, (int)size)); +每一个 `gradient machine` 都会管理维护一份训练好的模型,下面是两种最常用的模型加载方式: - // Load the trained model. Modify the parameter MODEL_PATH to set the correct - // path of the trained model. - CHECK(paddle_gradient_machine_load_parameter_from_disk(machine, MODEL_PATH)); - ``` +1. 从磁盘加载:这时`gradient machine`会独立拥有一份训练好的模型; +1. 共享自其它`gradient machine`的模型:这种情况多出现在使用多线程预测时,通过多个线程共享同一个模型来减少内存开销。可参考[此示例](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/multi_thread/main.c)。 + +下面的代码片段创建 `gradient machine`,并从指定路径加载训练好的模型。 + +```c +// Read the binary configuration file generated by `convert_protobin.sh` +long size; +void* buf = read_config(CONFIG_BIN, &size); + +// Create the gradient machine for inference. +paddle_gradient_machine machine; +CHECK(paddle_gradient_machine_create_for_inference(&machine, buf, (int)size)); + +// Load the trained model. Modify the parameter MODEL_PATH to set the correct +// path of the trained model. +CHECK(paddle_gradient_machine_load_parameter_from_disk(machine, MODEL_PATH)); +``` ##### 注意事项 1. 以上代码片段使用“仅序列化神经网络结构”的方式加载模型,需要同时指定模型参数存储的路径。 - 使用PaddlePaddle V2 API训练,模型中所有可学习参数会被存为一个压缩文件,需要手动进行解压,将它们放在同一目录中,C-API不会直接加载 V2 API 存储的压缩文件。 1. 如果使用`merge model`方式将神经网络结构和训练好的参数序列化到一个文件,请参考此[示例](https://github.com/PaddlePaddle/Mobile/blob/develop/Demo/linux/paddle_image_recognizer.cpp#L59)。 +1. 加载模型有多种方式,也可以在程序运行过程中再加载另外一个模型。 #### step 2. 创建神经网络输入,组织输入数据 基本使用概念: -- 在PaddlePaddle内部,神经网络中一个计算层的输入/输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输入,每一个输入/输入都会对应有自己的`Argument`。 +- 在PaddlePaddle内部,神经网络中一个计算层的输入输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输出,每一个输入/输出都会对应有自己的`Argument`。 - `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 - 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 *注:本文档使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。* -这篇文档的之后部分会使用`argument`来**特指** PaddlePaddle C-API中神经网的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 +这篇文档的之后部分会使用`argument`来**特指** PaddlePaddle C-API中神经网络的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 于是,在组织神经网络输入,获取输出时,需要思考完成以下工作: 1. 为每一个输入/输出创建`argument`; diff --git a/doc/howto/usage/capi/compile_paddle_lib.md b/doc/howto/usage/capi/compile_paddle_lib.md deleted file mode 100644 index d4ac4f27711..00000000000 --- a/doc/howto/usage/capi/compile_paddle_lib.md +++ /dev/null @@ -1,70 +0,0 @@ -## 编译 PaddlePaddle 链接库 - -### 概述 - -使用 C-API 进行预测依赖于将 PaddlePaddle 核心代码编译成链接库,只需在编译时指定编译选项:`-DWITH_C_API=ON`。同时,**建议将:`DWITH_PYTHON`,`DWITH_SWIG_PY`,`DWITH_GOLANG`,均设置为`OFF`**,以避免链接不必要的库。其它编译选项按需进行设定。 - -```shell -INSTALL_PREFIX=/path/of/capi/ -PADDLE_ROOT=/path/of/paddle_source/ -cmake $PADDLE_ROOT -DCMAKE_INSTALL_PREFIX=$INSTALL_PREFIX \ - -DCMAKE_BUILD_TYPE=Release \ - -DWITH_C_API=ON \ - -DWITH_SWIG_PY=OFF \ - -DWITH_GOLANG=OFF \ - -DWITH_PYTHON=OFF \ - -DWITH_MKLML=OFF \ - -DWITH_MKLDNN=OFF \ - -DWITH_GPU=OFF \ - ... -``` -在上面的代码片段中,`PADDLE_ROOT` 表示 PaddlePaddle 源码所在目录,生成Makefile文件后执行:`make && make install`。成功执行后,使用CAPI所需的依赖(包括:(1)编译出的PaddlePaddle 链接和头文件;(2)第三方链接库和头文件)均会存放于`INSTALL_PREFIX`目录中。 - -编译成功后在 `INSTALL_PREFIX` 下会看到如下目录结构(包括了编译出的PaddlePaddle头文件和链接库,以及第三方依赖链接库和头文件(如果需要,由链接方式决定)): - -```text -├── include -│   └── paddle -│   ├── arguments.h -│   ├── capi.h -│   ├── capi_private.h -│   ├── config.h -│   ├── error.h -│   ├── gradient_machine.h -│   ├── main.h -│   ├── matrix.h -│   ├── paddle_capi.map -│   └── vector.h -├── lib -│   ├── libpaddle_capi_engine.a -│   ├── libpaddle_capi_layers.a -│   ├── libpaddle_capi_shared.dylib -│   └── libpaddle_capi_whole.a -└── third_party - ├── ...... -``` - -### 链接方式说明 - -目前提供三种链接方式: - -1. 链接`libpaddle_capi_shared.so` 动态库 - - 使用 PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_shared.so`时,需注意: - 1. 如果编译时指定编译CPU版本,且使用`OpenBLAS`矩阵库,在使用CAPI开发预测程序时,只需要链接`libpaddle_capi_shared.so`这一个库。 - 1. 如果是用编译时指定CPU版本,且使用`MKL`矩阵库,由于`MKL`库有自己独立的动态库文件,在使用PaddlePaddle CAPI开发预测程序时,需要自己链接MKL链接库。 - 1. 如果编译时指定编译GPU版本,CUDA相关库会在预测程序运行时动态装载,需要将CUDA相关的库设置到`LD_LIBRARY_PATH`环境变量中。 - - 这种方式最为简便,链接相对容易,**在无特殊需求情况下,推荐使用此方式**。 - -2. 链接静态库 `libpaddle_capi_whole.a` - - 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意: - 1. 需要指定`-Wl,--whole-archive`链接选项。 - 1. 需要显式地链接 `gflags`、`glog`、`libz`、`protobuf` 等第三方库,可在`INSTALL_PREFIX\third_party`下找到。 - 1. 如果在编译 C-API 时使用OpenBLAS矩阵库,需要显示地链接`libopenblas.a`。 - 1. 如果在编译 C-API 是使用 MKL 矩阵库,需要显示地链接 MKL 的动态库。 - -3. 链接静态库 `libpaddle_capi_layers.a`和`libpaddle_capi_engine.a` - - 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意: - 1. 这种链接方式主要用于移动端预测。 - 1. 为了减少生成链接库的大小把`libpaddle_capi_whole.a`拆成以上两个静态链接库。 - 1. 需指定`-Wl,--whole-archive -lpaddle_capi_layers` 和 `-Wl,--no-whole-archive -lpaddle_capi_engine` 进行链接。 - 1. 第三方依赖库需要按照与方式2同样方法显示地进行链接。 diff --git a/doc/howto/usage/capi/compile_paddle_lib_cn.md b/doc/howto/usage/capi/compile_paddle_lib_cn.md new file mode 100644 index 00000000000..ac5ecffe2ea --- /dev/null +++ b/doc/howto/usage/capi/compile_paddle_lib_cn.md @@ -0,0 +1,122 @@ +## 编译 PaddlePaddle 预测库 + +### 概述 + +使用 C-API 进行预测依赖于将 PaddlePaddle 核心代码编译成链接库,只需在编译时需配制下面这些编译选项: + +必须配置选项: +- `WITH_C_API`,必须配置为`ON`。 + +推荐配置选项: +- `WITH_PYTHON`,推荐配置为`OFF` +- `WITH_SWIG_PY`,推荐配置为`OFF` +- `WITH_GOLANG`,推荐设置为`OFF` + +可选配置选项: +- `WITH_GPU`,可配置为`ON/OFF` +- `WITH_MKL`,可配置为`ON/OFF` + +对推荐配置中的选项建议按照设置,以避免链接不必要的库。其它可选编译选项按需进行设定。 + +下面的代码片段从github拉取最新代码,配制编译选项(需要将PADDLE_ROOT替换为PaddlePaddle预测库的安装路径): + +```shell +PADDLE_ROOT=/path/of/capi +git clone https://github.com/PaddlePaddle/Paddle.git +cd Paddle +mkdir build +cd build +cmake -DCMAKE_INSTALL_PREFIX=$PADDLE_ROOT \ + -DCMAKE_BUILD_TYPE=Release \ + -DWITH_C_API=ON \ + -DWITH_SWIG_PY=OFF \ + -DWITH_GOLANG=OFF \ + -DWITH_PYTHON=OFF \ + -DWITH_MKL=OFF \ + -DWITH_GPU=OFF \ + .. +``` + +执行上述代码生成Makefile文件后,执行:`make && make install`。成功编译后,使用C-API所需的依赖(包括:(1)编译出的PaddlePaddle预测库和头文件;(2)第三方链接库和头文件)均会存放于`PADDLE_ROOT`目录中。 + +编译成功后在 `PADDLE_ROOT` 下会看到如下目录结构(包括了编译出的PaddlePaddle头文件和链接库,以及第三方依赖链接库和头文件(如果需要,由链接方式决定)): + +```text +├── include +│   └── paddle +│   ├── arguments.h +│   ├── capi.h +│   ├── capi_private.h +│   ├── config.h +│   ├── error.h +│   ├── gradient_machine.h +│   ├── main.h +│   ├── matrix.h +│   ├── paddle_capi.map +│   └── vector.h +├── lib +│   ├── libpaddle_capi_engine.a +│   ├── libpaddle_capi_layers.a +│   ├── libpaddle_capi_shared.so +│   └── libpaddle_capi_whole.a +└── third_party + ├── gflags + │   ├── include + │   │   └── gflags + │   │   ├── gflags_completions.h + │   │   ├── gflags_declare.h + │   │   ... + │   └── lib + │   └── libgflags.a + ├── glog + │   ├── include + │   │   └── glog + │   │   ├── config.h + │   │   ... + │   └── lib + │   └── libglog.a + ├── openblas + │   ├── include + │   │   ├── cblas.h + │   │   ... + │   └── lib + │   ... + ├── protobuf + │   ├── include + │   │   └── google + │   │   └── protobuf + │   │   ... + │   └── lib + │   └── libprotobuf-lite.a + └── zlib + ├── include + │   ... + └── lib + ... + +``` + +### 链接说明 + +目前提供三种链接方式: + +1. 链接`libpaddle_capi_shared.so` 动态库 + - 使用 PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_shared.so`时,需注意: + 1. 如果编译时指定编译CPU版本,且使用`OpenBLAS`数学库,在使用C-API开发预测程序时,只需要链接`libpaddle_capi_shared.so`这一个库。 + 1. 如果是用编译时指定CPU版本,且使用`MKL`数学库,由于`MKL`库有自己独立的动态库文件,在使用PaddlePaddle C-API开发预测程序时,需要自己链接MKL链接库。 + 1. 如果编译时指定编译GPU版本,CUDA相关库会在预测程序运行时动态装载,需要将CUDA相关的库设置到`LD_LIBRARY_PATH`环境变量中。 + - 这种方式最为简便,链接相对容易,**在无特殊需求情况下,推荐使用此方式**。 + +2. 链接静态库 `libpaddle_capi_whole.a` + - 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意: + 1. 需要指定`-Wl,--whole-archive`链接选项。 + 1. 需要显式地链接 `gflags`、`glog`、`libz`、`protobuf` 等第三方库,可在`PADDLE_ROOT/third_party`下找到。 + 1. 如果在编译 C-API 时使用OpenBLAS数学库,需要显示地链接`libopenblas.a`。 + 1. 如果在编译 C-API 是使用MKL数学库,需要显示地链接MKL的动态库。 + +3. 链接静态库 `libpaddle_capi_layers.a`和`libpaddle_capi_engine.a` + - 使用PaddlePaddle C-API 开发预测程序链接`libpaddle_capi_whole.a`时,需注意: + 1. 这种链接方式主要用于移动端预测。 + 1. 为了减少生成链接库的大小把`libpaddle_capi_whole.a`拆成以上两个静态链接库。 + 1. 需指定`-Wl,--whole-archive -lpaddle_capi_layers` 和 `-Wl,--no-whole-archive -lpaddle_capi_engine` 进行链接。 + 1. 第三方依赖库需要按照与方式2同样方法显示地进行链接。 diff --git a/doc/howto/usage/capi/index_cn.rst b/doc/howto/usage/capi/index_cn.rst index 3b36f31bfd0..b2822fca2b7 100644 --- a/doc/howto/usage/capi/index_cn.rst +++ b/doc/howto/usage/capi/index_cn.rst @@ -4,6 +4,6 @@ PaddlePaddle C-API .. toctree:: :maxdepth: 1 - compile_paddle_lib.md - organization_of_the_inputs.md - a_simple_example.md + compile_paddle_lib_cn.md + organization_of_the_inputs_cn.md + a_simple_example_cn.md diff --git a/doc/howto/usage/capi/organization_of_the_inputs.md b/doc/howto/usage/capi/organization_of_the_inputs_cn.md similarity index 99% rename from doc/howto/usage/capi/organization_of_the_inputs.md rename to doc/howto/usage/capi/organization_of_the_inputs_cn.md index 7563e236da6..787af8b0a76 100644 --- a/doc/howto/usage/capi/organization_of_the_inputs.md +++ b/doc/howto/usage/capi/organization_of_the_inputs_cn.md @@ -260,6 +260,8 @@
+ + ### 输出数据 PaddlePaddle中一个计算层的输出数据组织方式和输入数据组织方式完全相同。一个输出数据同样被组织为一个`argument`,`argument`通过`paddle_matrix`或`paddle_ivector`存数数据,如果输出是一个序列,那么会携带有`sequence_start_positions`信息。调用C-API相关接口,读取需要的结果即可。 -- GitLab From 55b17c11710a595dc64ec9123012f329c6a36d62 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Tue, 26 Dec 2017 12:28:45 +0000 Subject: [PATCH 0052/2305] Add the parsing part for the profiling tool --- paddle/platform/profiler.cc | 59 ++++++++++++++++++++++++++++++++ paddle/platform/profiler.h | 12 ++++++- paddle/platform/profiler_test.cc | 22 ++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index 40b34b732c9..3d95097048c 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/platform/profiler.h" +#include namespace paddle { namespace platform { @@ -70,5 +71,63 @@ std::vector> DisableProfiler() { return result; } +void PushEvent(const std::string name, const platform::DeviceContext* dev_ctx) { + GetEventList().Record(EventKind::kPushRange, std::move(name), kThreadId, + dev_ctx); +} + +void PopEvent(const std::string name, const platform::DeviceContext* dev_ctx) { + GetEventList().Record(EventKind::kPopRange, std::move(name), kThreadId, + dev_ctx); +} + +void ParseEvents(std::vector> events) { + std::map> events_table; + for (size_t i = 0; i < events.size(); i++) { + std::list pushed_events; + for (size_t j = 0; j < events[i].size(); j++) { + if (events[i][j].kind() == "push") { + pushed_events.push_back(events[i][j]); + } + if (events[i][j].kind() == "pop") { + std::list::reverse_iterator rit = pushed_events.rbegin(); + while (rit->name() != events[i][j].name() && + rit != pushed_events.rend()) { + ++rit; + } + if (rit != pushed_events.rend()) { + Event pushed_event = *rit; + double cpu_time = rit->CpuElapsedUs(events[i][j]); + double cuda_time = 0; +#ifdef PADDLE_WITH_CUDA + cuda_time = rit->CudaElapsedUs(events[i][j]); +#endif + if (events_table.find(rit->name()) == events_table.end()) { + events_table[rit->name()] = std::make_tuple(1, cpu_time, cuda_time); + } else { + std::get<0>(events_table[rit->name()]) += 1; + std::get<1>(events_table[rit->name()]) += cpu_time; + std::get<2>(events_table[rit->name()]) += cuda_time; + } + // remove the start marker from the list + pushed_events.erase((++rit).base()); + } else { + std::cout << "Warning: can not find the start marker of event " + << events[i][j].name(); + } + } + } + } + // output events table + std::cout << "\nEvents\t\tCalls\t\tTotal CPU time\t\tTotal GPU time\n"; + for (std::map>::iterator it = + events_table.begin(); + it != events_table.end(); ++it) { + std::cout << it->first << "\t\t" << std::get<0>(it->second) << "\t\t" + << std::get<1>(it->second) << "\t\t" << std::get<2>(it->second) + << std::endl; + } +} + } // namespace platform } // namespace paddle diff --git a/paddle/platform/profiler.h b/paddle/platform/profiler.h index 2242635024c..5f21ff8c1c3 100644 --- a/paddle/platform/profiler.h +++ b/paddle/platform/profiler.h @@ -173,25 +173,35 @@ inline void Mark(const std::string name, GetEventList().Record(EventKind::kMark, std::move(name), kThreadId, dev_ctx); } +void PushEvent(const std::string name, + const platform::DeviceContext* dev_ctx = nullptr); + +void PopEvent(const std::string name, + const platform::DeviceContext* dev_ctx = nullptr); + struct RecordEvent { explicit RecordEvent(const std::string name, platform::DeviceContext* dev_ctx = nullptr) { if (kState == ProfilerState::kDisabled) return; dev_ctx_ = dev_ctx; + name_ = name; GetEventList().Record(EventKind::kPushRange, std::move(name), kThreadId, dev_ctx_); } ~RecordEvent() { if (kState == ProfilerState::kDisabled) return; - GetEventList().Record(EventKind::kPopRange, std::string(), kThreadId, + GetEventList().Record(EventKind::kPopRange, std::move(name_), kThreadId, dev_ctx_); } platform::DeviceContext* dev_ctx_; + std::string name_; }; void EnableProfiler(ProfilerState state); std::vector> DisableProfiler(); +void ParseEvents(std::vector>); + } // namespace platform } // namespace paddle diff --git a/paddle/platform/profiler_test.cc b/paddle/platform/profiler_test.cc index 5bd0a9d8599..b2f1dea4659 100644 --- a/paddle/platform/profiler_test.cc +++ b/paddle/platform/profiler_test.cc @@ -67,8 +67,29 @@ TEST(RecordEvent, RecordEvent) { #endif EnableProfiler(state); + /* Usage 1: + * PushEvent(evt_name, dev_ctx); + * ... + * code to time + * ... + * PopEvent(evt_name, dev_ctx); + */ for (int i = 1; i < 5; ++i) { std::string name = "op_" + std::to_string(i); + PushEvent(name, dev_ctx); + int counter = 1; + while (counter != i * 1000) counter++; + PopEvent(name, dev_ctx); + } + + /* Usage 2: + * { + * RecordEvent record_event(name, dev_ctx); + * ... + * } + */ + for (int i = 1; i < 5; ++i) { + std::string name = "evs_op_" + std::to_string(i); RecordEvent record_event(name, dev_ctx); int counter = 1; while (counter != i * 1000) counter++; @@ -77,6 +98,7 @@ TEST(RecordEvent, RecordEvent) { int cuda_startup_count = 0; int start_profiler_count = 0; int stop_profiler_count = 0; + ParseEvents(events); for (size_t i = 0; i < events.size(); ++i) { for (size_t j = 0; j < events[i].size(); ++j) { if (events[i][j].name() == "_cuda_startup_") ++cuda_startup_count; -- GitLab From 2c1adb060469e0b55dae966ec1edc260e1a2bfeb Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Fri, 29 Dec 2017 07:16:40 +0000 Subject: [PATCH 0053/2305] Rename ctc_edit_distance_op to edit_distance_op --- ...dit_distance_op.cc => edit_distance_op.cc} | 24 +++++++++---------- ...dit_distance_op.cu => edit_distance_op.cu} | 12 +++++----- ..._edit_distance_op.h => edit_distance_op.h} | 2 +- ...istance_op.py => test_edit_distance_op.py} | 2 +- 4 files changed, 19 insertions(+), 21 deletions(-) rename paddle/operators/{ctc_edit_distance_op.cc => edit_distance_op.cc} (77%) rename paddle/operators/{ctc_edit_distance_op.cu => edit_distance_op.cu} (93%) rename paddle/operators/{ctc_edit_distance_op.h => edit_distance_op.h} (97%) rename python/paddle/v2/fluid/tests/{test_ctc_edit_distance_op.py => test_edit_distance_op.py} (97%) diff --git a/paddle/operators/ctc_edit_distance_op.cc b/paddle/operators/edit_distance_op.cc similarity index 77% rename from paddle/operators/ctc_edit_distance_op.cc rename to paddle/operators/edit_distance_op.cc index 11e9983e243..843a6844cde 100644 --- a/paddle/operators/ctc_edit_distance_op.cc +++ b/paddle/operators/edit_distance_op.cc @@ -12,12 +12,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include "paddle/operators/ctc_edit_distance_op.h" +#include "paddle/operators/edit_distance_op.h" namespace paddle { namespace operators { -class CTCEditDistanceOp : public framework::OperatorWithKernel { +class EditDistanceOp : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; @@ -29,17 +29,16 @@ class CTCEditDistanceOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetKernelType( + framework::OpKernelType GetActualKernelType( const framework::ExecutionContext &ctx) const override { - return framework::OpKernelType(framework::DataType::FP32, + return framework::OpKernelType(framework::proto::DataType::FP32, ctx.device_context()); } }; -class CTCEditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { +class EditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { public: - CTCEditDistanceOpMaker(framework::OpProto *proto, - framework::OpAttrChecker *op_checker) + EditDistanceOpMaker(OpProto *proto, OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("X1", "(2-D tensor with shape [M x 1]) The indices for " @@ -54,10 +53,10 @@ class CTCEditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { .SetDefault(false); AddOutput("Out", "(2-D tensor with shape [1 x 1]) " - "The output distance of CTCEditDistance operator."); + "The output distance of EditDistance operator."); AddComment(R"DOC( -CTCEditDistance operator computes the edit distance of two sequences, one named +EditDistance operator computes the edit distance of two sequences, one named hypothesis with length M and another named reference with length N. Edit distance, also called Levenshtein distance, measures how dissimilar two strings @@ -80,8 +79,7 @@ reference string N. namespace ops = paddle::operators; -REGISTER_OP_WITHOUT_GRADIENT(ctc_edit_distance, ops::CTCEditDistanceOp, - ops::CTCEditDistanceOpMaker); +REGISTER_OPERATOR(edit_distance, ops::EditDistanceOp, ops::EditDistanceOpMaker, + paddle::framework::EmptyGradOpMaker); REGISTER_OP_CPU_KERNEL( - ctc_edit_distance, - ops::CTCEditDistanceKernel); + edit_distance, ops::EditDistanceKernel); diff --git a/paddle/operators/ctc_edit_distance_op.cu b/paddle/operators/edit_distance_op.cu similarity index 93% rename from paddle/operators/ctc_edit_distance_op.cu rename to paddle/operators/edit_distance_op.cu index 22871acc4ec..7fa6a60df4d 100644 --- a/paddle/operators/ctc_edit_distance_op.cu +++ b/paddle/operators/edit_distance_op.cu @@ -65,7 +65,7 @@ __global__ void SetOutput(T* out, const T* dist, const int M, const int N, } template -class CTCEditDistanceGPUKernel : public framework::OpKernel { +class EditDistanceGPUKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const { auto* out_t = ctx.Output("Out"); @@ -110,8 +110,8 @@ class CTCEditDistanceGPUKernel : public framework::OpKernel { int z_n = slice < n + 1 ? 0 : slice - n; int size = slice - (z_m + z_n) + 1; // number of elments in the same // anti-diagonal line to update - int start = slice < n + 1 ? slice : z_n * (n + 1) - 1; // start index - + // the start index at which computes from + int start = slice < n + 1 ? slice : (z_n + 1) * (n + 1) - 1; Levenshtein<<<1 + (size - 1) / PADDLE_CUDA_NUM_THREADS, PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, x1, x2, m, n, start); @@ -126,6 +126,6 @@ class CTCEditDistanceGPUKernel : public framework::OpKernel { namespace ops = paddle::operators; -REGISTER_OP_GPU_KERNEL( - ctc_edit_distance, - ops::CTCEditDistanceGPUKernel); +REGISTER_OP_CUDA_KERNEL( + edit_distance, + ops::EditDistanceGPUKernel); diff --git a/paddle/operators/ctc_edit_distance_op.h b/paddle/operators/edit_distance_op.h similarity index 97% rename from paddle/operators/ctc_edit_distance_op.h rename to paddle/operators/edit_distance_op.h index 08f29cf24ac..182a6e3bf5b 100644 --- a/paddle/operators/ctc_edit_distance_op.h +++ b/paddle/operators/edit_distance_op.h @@ -21,7 +21,7 @@ namespace paddle { namespace operators { template -class CTCEditDistanceKernel : public framework::OpKernel { +class EditDistanceKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const { auto* out_t = ctx.Output("Out"); diff --git a/python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py b/python/paddle/v2/fluid/tests/test_edit_distance_op.py similarity index 97% rename from python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py rename to python/paddle/v2/fluid/tests/test_edit_distance_op.py index 62c233b34f6..8866922f2ee 100644 --- a/python/paddle/v2/fluid/tests/test_ctc_edit_distance_op.py +++ b/python/paddle/v2/fluid/tests/test_edit_distance_op.py @@ -36,7 +36,7 @@ def Levenshtein(hyp, ref): class TestCTCEditDistanceOp(OpTest): def setUp(self): - self.op_type = "ctc_edit_distance" + self.op_type = "edit_distance" normalized = True x1 = np.array([0, 12, 3, 5]).astype("int32") x2 = np.array([0, 12, 4, 7, 8]).astype("int32") -- GitLab From 1a02369ad5c8db908cb14692b16c87aa57a013a7 Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Sat, 30 Dec 2017 14:30:07 +0800 Subject: [PATCH 0054/2305] Add releaseOutput interface for release the output memory of the middle layer. --- .../gradientmachines/GradientMachine.h | 7 ++++ .../gradientmachines/NeuralNetwork.cpp | 32 +++++++++++++++++++ .../gserver/gradientmachines/NeuralNetwork.h | 8 +++++ 3 files changed, 47 insertions(+) diff --git a/paddle/gserver/gradientmachines/GradientMachine.h b/paddle/gserver/gradientmachines/GradientMachine.h index ebfe0573cfd..4ab54a5022a 100644 --- a/paddle/gserver/gradientmachines/GradientMachine.h +++ b/paddle/gserver/gradientmachines/GradientMachine.h @@ -233,6 +233,13 @@ public: (void)numProcessed; } + /** + * @brief Release the middle layer's output memory. + * + * @note This function is used for memory optimization in inference. + */ + virtual void releaseOutput() {} + protected: virtual void onLoadParameter() {} diff --git a/paddle/gserver/gradientmachines/NeuralNetwork.cpp b/paddle/gserver/gradientmachines/NeuralNetwork.cpp index 68bf37d59db..3b6234a6e5e 100644 --- a/paddle/gserver/gradientmachines/NeuralNetwork.cpp +++ b/paddle/gserver/gradientmachines/NeuralNetwork.cpp @@ -187,6 +187,31 @@ void NeuralNetwork::init(const ModelConfig& config, CHECK(it != layerMap_.end()); outputLayers_.push_back(it->second); } + + for (const auto& layer : layers_) { + const auto name& = layer->getName(); + bool isMiddleLayer = true; + + // if data layer + for (const auto& dataLayer : dataLayers_) { + if (name == dataLayer->getName()) { + isMiddleLayer = false; + break; + } + } + + // if output layer + for (const auto& dataLayer : outputLayers_) { + if (name == dataLayer->getName()) { + isMiddleLayer = false; + break; + } + } + + if (isMiddleLayer) { + middleLayers_.push_back(layer); + } + } } void NeuralNetwork::connect(LayerPtr agentLayer, @@ -327,6 +352,13 @@ void NeuralNetwork::onPassEnd() { } } +void NeuralNetwork::releaseOutput() { + for (auto& layer : middleLayers_) { + Argument& arg = layer->getOutput(); + arg.value.reset(); + } +} + #ifndef PADDLE_MOBILE_INFERENCE class CombinedEvaluator : public Evaluator { diff --git a/paddle/gserver/gradientmachines/NeuralNetwork.h b/paddle/gserver/gradientmachines/NeuralNetwork.h index 68883802900..968e198cf66 100644 --- a/paddle/gserver/gradientmachines/NeuralNetwork.h +++ b/paddle/gserver/gradientmachines/NeuralNetwork.h @@ -137,6 +137,13 @@ public: /// some finish work, like convert the weight format of MKLDNNLayers void finish(); + /** + * @brief Release the middle layer's output memory. + * + * @note This function is used for memory optimization in inference. + */ + void releaseOutput(); + protected: /** * The constructor of NeuralNetwork. @@ -158,6 +165,7 @@ protected: std::vector dataLayers_; std::vector outputLayers_; + std::vector middleLayers_; static std::map dllInitMap; -- GitLab From 2e49facae9baa9cc161d23d064e792abbc4e6e84 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Mon, 1 Jan 2018 15:23:00 +0000 Subject: [PATCH 0055/2305] Rename inputs & format license --- paddle/operators/edit_distance_op.cc | 28 +++++++++---------- paddle/operators/edit_distance_op.cu | 22 +++++++-------- paddle/operators/edit_distance_op.h | 22 +++++++-------- .../v2/fluid/tests/test_edit_distance_op.py | 2 +- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/paddle/operators/edit_distance_op.cc b/paddle/operators/edit_distance_op.cc index 843a6844cde..6022a7a4bde 100644 --- a/paddle/operators/edit_distance_op.cc +++ b/paddle/operators/edit_distance_op.cc @@ -1,16 +1,16 @@ /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. */ +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #include "paddle/operators/edit_distance_op.h" @@ -22,8 +22,8 @@ class EditDistanceOp : public framework::OperatorWithKernel { using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext *ctx) const override { - PADDLE_ENFORCE(ctx->HasInput("X1"), "Input(X1) shouldn't be null."); - PADDLE_ENFORCE(ctx->HasInput("X2"), "Input(X2) shouldn't be null."); + PADDLE_ENFORCE(ctx->HasInput("Hyp"), "Input(Hyp) shouldn't be null."); + PADDLE_ENFORCE(ctx->HasInput("Ref"), "Input(Ref) shouldn't be null."); PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) shouldn't be null."); ctx->SetOutputDim("Out", {1}); } @@ -40,16 +40,16 @@ class EditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { public: EditDistanceOpMaker(OpProto *proto, OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { - AddInput("X1", + AddInput("Hyp", "(2-D tensor with shape [M x 1]) The indices for " "hypothesis string"); - AddInput("X2", + AddInput("Ref", "(2-D tensor with shape [N x 1]) The indices " "for reference string."); AddAttr("normalized", "(bool, default false) Indicated whether " "normalize the Output(Out) by the length of reference " - "string (X2).") + "string (Ref).") .SetDefault(false); AddOutput("Out", "(2-D tensor with shape [1 x 1]) " diff --git a/paddle/operators/edit_distance_op.cu b/paddle/operators/edit_distance_op.cu index 7fa6a60df4d..fed91ffb430 100644 --- a/paddle/operators/edit_distance_op.cu +++ b/paddle/operators/edit_distance_op.cu @@ -1,16 +1,16 @@ /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. */ +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #include #include "paddle/framework/op_registry.h" @@ -70,8 +70,8 @@ class EditDistanceGPUKernel : public framework::OpKernel { void Compute(const framework::ExecutionContext& ctx) const { auto* out_t = ctx.Output("Out"); - auto* x1_t = ctx.Input("X1"); - auto* x2_t = ctx.Input("X2"); + auto* x1_t = ctx.Input("Hyp"); + auto* x2_t = ctx.Input("Ref"); out_t->mutable_data(ctx.GetPlace()); auto out = out_t->data(); diff --git a/paddle/operators/edit_distance_op.h b/paddle/operators/edit_distance_op.h index 182a6e3bf5b..abde4fe97c4 100644 --- a/paddle/operators/edit_distance_op.h +++ b/paddle/operators/edit_distance_op.h @@ -1,16 +1,16 @@ /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. */ +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #pragma once #include @@ -26,8 +26,8 @@ class EditDistanceKernel : public framework::OpKernel { void Compute(const framework::ExecutionContext& ctx) const { auto* out_t = ctx.Output("Out"); - auto* x1_t = ctx.Input("X1"); - auto* x2_t = ctx.Input("X2"); + auto* x1_t = ctx.Input("Hyp"); + auto* x2_t = ctx.Input("Ref"); out_t->mutable_data(ctx.GetPlace()); diff --git a/python/paddle/v2/fluid/tests/test_edit_distance_op.py b/python/paddle/v2/fluid/tests/test_edit_distance_op.py index 8866922f2ee..df1ac620e79 100644 --- a/python/paddle/v2/fluid/tests/test_edit_distance_op.py +++ b/python/paddle/v2/fluid/tests/test_edit_distance_op.py @@ -45,7 +45,7 @@ class TestCTCEditDistanceOp(OpTest): if normalized is True: distance = distance / len(x2) self.attrs = {'normalized': normalized} - self.inputs = {'X1': x1, 'X2': x2} + self.inputs = {'Hyp': x1, 'Ref': x2} self.outputs = {'Out': distance} def test_check_output(self): -- GitLab From 430a91119985323e0dde56ffb68de9a10382f4b3 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Tue, 2 Jan 2018 18:38:52 +0800 Subject: [PATCH 0056/2305] add picture --- .../refactor/src/remote_executor.graffle | Bin 0 -> 6865 bytes doc/design/refactor/src/remote_executor.png | Bin 0 -> 137681 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/design/refactor/src/remote_executor.graffle create mode 100644 doc/design/refactor/src/remote_executor.png diff --git a/doc/design/refactor/src/remote_executor.graffle b/doc/design/refactor/src/remote_executor.graffle new file mode 100644 index 0000000000000000000000000000000000000000..ce2c18fee5687732053c48af9c8c290a994a8090 GIT binary patch literal 6865 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
fsz(p)e)I^ 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
$#dS|G|z#f}n(P~jiBOEuSJ{``DmvKMI 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≈Erh2bbER^kPlnY!(ZMg3QltaHU!(dtxKH;*_ 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 literal 0 HcmV?d00001 -- GitLab From e1cea8cabe5b7a043a29c25789724e97ae310af5 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Wed, 3 Jan 2018 10:35:38 +0800 Subject: [PATCH 0057/2305] follow comments --- .../distributed_architecture.md | 20 ++++----- .../{refactor => dist_refactor}/multi_cpu.md | 0 .../parameter_server.md | 38 +++++++----------- .../src/compiler.graffle | Bin .../src/compiler.png | Bin .../src/dist-graph.graffle | Bin .../src/dist-graph.png | Bin .../src/distributed_architecture.graffle | Bin 0 -> 3800 bytes .../src/distributed_architecture.png | Bin 190117 -> 193766 bytes .../src/local-graph.graffle | Bin .../src/local-graph.png | Bin .../src/local_architecture.graffle | Bin .../src/local_architecture.png | Bin .../src/multi-threads.graffle | Bin .../src/multi-threads/multi-threads@3x.png | Bin .../src/multi-threads/single-thread@3x.png | Bin .../src/paddle-compile.graffle | Bin .../src/paddle-compile.png | Bin .../src/remote_executor.graffle | Bin .../src/remote_executor.png | Bin .../src/distributed_architecture.graffle | Bin 3793 -> 0 bytes 21 files changed, 25 insertions(+), 33 deletions(-) rename doc/design/{refactor => dist_refactor}/distributed_architecture.md (95%) rename doc/design/{refactor => dist_refactor}/multi_cpu.md (100%) rename doc/design/{refactor => dist_refactor}/parameter_server.md (76%) rename doc/design/{refactor => dist_refactor}/src/compiler.graffle (100%) rename doc/design/{refactor => dist_refactor}/src/compiler.png (100%) rename doc/design/{refactor => dist_refactor}/src/dist-graph.graffle (100%) rename doc/design/{refactor => dist_refactor}/src/dist-graph.png (100%) create mode 100644 doc/design/dist_refactor/src/distributed_architecture.graffle rename doc/design/{refactor => dist_refactor}/src/distributed_architecture.png (58%) rename doc/design/{refactor => dist_refactor}/src/local-graph.graffle (100%) rename doc/design/{refactor => dist_refactor}/src/local-graph.png (100%) rename doc/design/{refactor => dist_refactor}/src/local_architecture.graffle (100%) rename doc/design/{refactor => dist_refactor}/src/local_architecture.png (100%) rename doc/design/{refactor => dist_refactor}/src/multi-threads.graffle (100%) rename doc/design/{refactor => dist_refactor}/src/multi-threads/multi-threads@3x.png (100%) rename doc/design/{refactor => dist_refactor}/src/multi-threads/single-thread@3x.png (100%) rename doc/design/{refactor => dist_refactor}/src/paddle-compile.graffle (100%) rename doc/design/{refactor => dist_refactor}/src/paddle-compile.png (100%) rename doc/design/{refactor => dist_refactor}/src/remote_executor.graffle (100%) rename doc/design/{refactor => dist_refactor}/src/remote_executor.png (100%) delete mode 100644 doc/design/refactor/src/distributed_architecture.graffle diff --git a/doc/design/refactor/distributed_architecture.md b/doc/design/dist_refactor/distributed_architecture.md similarity index 95% rename from doc/design/refactor/distributed_architecture.md rename to doc/design/dist_refactor/distributed_architecture.md index f228d481e7e..3a741f95866 100644 --- a/doc/design/refactor/distributed_architecture.md +++ b/doc/design/dist_refactor/distributed_architecture.md @@ -52,8 +52,9 @@ The IR for PaddlePaddle after refactoring is called a `Block`, it specifies the The user can not directly specify the parameter update rule for the parameter server in the Python module, since the parameter server does not use the same computation definition as the trainer. Instead, the update rule is baked inside the parameter server. The user can not specify the update rule explicitly. -This could be fixed by making the parameter server run the same computation definition as the trainer (the user's Python module). For a detailed explanation, refer to this document - -[Design Doc: Operation Graph Based Parameter Server](./parameter_server.md) +This could be fixed by making the parameter server also run an IR, which can be different to the trainer side +For a detailed explanation, refer to this document - +[Design Doc: Parameter Server](./parameter_server.md) ## Distributed Training Architecture @@ -113,7 +114,7 @@ Below are the steps that are followed : distributed training program: 1. Parse configurations from `RemoteExecutor`. 1. Determine the type of distributed program, can be DataParallelism, ModelParallelism or Streaming. - 1. Partition the `ProgramDesc` according to type and add `send` / `recv` OP pair on the boundaries. For + 1. Partition the `ProgramDesc` according to type and add `send` / `recv` OP pair on the boundaries. Take DataParallelism type for example, it removes the optimization operators and add a `send` OP to the "trainer" role, then add the optimization operators to the parameter server role within the `recv` OP. 1. Dispatch the partitioned graph to different `RemoteExecutor` in the cluster. @@ -129,12 +130,9 @@ log printing. The Python `RemoteExecutor` is derived from `Executor` class. ```python -run(self, - program=None, - feed=None, - fetch_list=None, - feed_var_name='feed', - fetch_var_name='fetch', +exe = RemoteExecutor( + feed=feeder.feed(data), + fetch_list=[avg_cost], job_desc=JobDesc( jobname, num_trainer, @@ -145,6 +143,10 @@ run(self, cpu_per_pserver, mem_per_pserver )) +for data in train_reader(): + loss, acc = exe.run(trainer_prog, + feed=feeder.feed(data), + fetch_list=[avg_cost]) ``` `JobDesc` object describe the distributed job resource specification to run on diff --git a/doc/design/refactor/multi_cpu.md b/doc/design/dist_refactor/multi_cpu.md similarity index 100% rename from doc/design/refactor/multi_cpu.md rename to doc/design/dist_refactor/multi_cpu.md diff --git a/doc/design/refactor/parameter_server.md b/doc/design/dist_refactor/parameter_server.md similarity index 76% rename from doc/design/refactor/parameter_server.md rename to doc/design/dist_refactor/parameter_server.md index fa3c5d79902..1094f06d461 100644 --- a/doc/design/refactor/parameter_server.md +++ b/doc/design/dist_refactor/parameter_server.md @@ -1,4 +1,4 @@ -# Design Doc: Operation Graph Based Parameter Server +# Design Doc: Parameter Server ## Abstract @@ -10,7 +10,7 @@ different purposes. ## Background The previous implementations of the parameter server does not run a -subgraph. parameter initialization, optimizer computation, network +fluid sub-program. Parameter initialization, optimizer computation, network communication and checkpointing are implemented twice on both the trainer and the parameter server. @@ -23,10 +23,10 @@ server becomes a natural extension. ## Design -### Graph Converter +### Distributed Transpiler -The *graph converter* converts the user-defined operation (OP) graph -into subgraphs to be scheduled on different nodes with the following +The *Distributed Transpiler* converts the user-defined fluid program +into sub-programs to be scheduled on different nodes with the following steps: 1. OP placement: the OPs will be placed on different nodes according @@ -34,7 +34,6 @@ steps: time. Currently we will use a simple heuristic that puts parameter varable on parameter server workers and everything else on trainer workers. - 1. Add communication OPs to enable the communication between nodes. We will need these OPs: *Send*, *Recv*, *Enqueue*, *Dequeue*. @@ -48,8 +47,8 @@ After converting: -1. The parameter variable W and it's optimizer subgraph are placed on the parameter server. -1. Operators are added to the subgraphs. +1. The parameter variable W and it's optimizer program are placed on the parameter server. +1. Operators are added to the program. - *Send* sends data to the connected *Recv* operator. The scheduler on the receive node will only schedule *Recv* operator to run when the *Send* operator has ran (the *Send* OP will mark @@ -64,39 +63,30 @@ After converting: ### Benefits - Model parallelism become easier to implement: it's an extension to - the trainer - parameter server approach. we already have the - communication OPs, but need to extend the graph converter's - placement functionality. - + the trainer - parameter server approach. We can have several "Transpilers" + to achieve different goals. - User-defined optimizer is easier to add - user can now express it as - a subgraph. - + a sub-program. - No more duplication logic inside the trainer and the parameter server mentioned in the background section. ### Challenges -- It might be hard for the graph converter to cut a general graph - (without any hint for which subgraph is the optimizer). We may need - to label which subgraph inside the OP graph is the optimizer. - - It's important to balance the parameter shards of on multiple parameter server. If a single parameter is very big (some word-embedding, fully connected, softmax layer), we need to automatically partition the single parameter onto different parameter servers when possible (only element-wise optimizer depends on the parameter variable). +- In the "Aync SGD" figure, the "W" variable on the parameter server + could be read and wrote concurrently. See + [here](https://github.com/PaddlePaddle/Paddle/pull/6394) for more + details about concurrent program in fluid. ### Discussion -- In the "Aync SGD" figure, the "W" variable on the parameter server - could be read and wrote concurrently, what is our locking strategy? - E.g., each variable have a lock cpp method to be invoked by every - OP, or, have a lock OP. - - Can the Enqueue OP be implemented under our current tensor design (puts the input tensor into the queue tensor)? - - *Dequeue* OP will have variable numbers of output (depends on the `min_count` attribute), does our current design support it? (similar question for the *Add* OP) diff --git a/doc/design/refactor/src/compiler.graffle b/doc/design/dist_refactor/src/compiler.graffle similarity index 100% rename from doc/design/refactor/src/compiler.graffle rename to doc/design/dist_refactor/src/compiler.graffle diff --git a/doc/design/refactor/src/compiler.png b/doc/design/dist_refactor/src/compiler.png similarity index 100% rename from doc/design/refactor/src/compiler.png rename to doc/design/dist_refactor/src/compiler.png diff --git a/doc/design/refactor/src/dist-graph.graffle b/doc/design/dist_refactor/src/dist-graph.graffle similarity index 100% rename from doc/design/refactor/src/dist-graph.graffle rename to doc/design/dist_refactor/src/dist-graph.graffle diff --git a/doc/design/refactor/src/dist-graph.png b/doc/design/dist_refactor/src/dist-graph.png similarity index 100% rename from doc/design/refactor/src/dist-graph.png rename to doc/design/dist_refactor/src/dist-graph.png diff --git a/doc/design/dist_refactor/src/distributed_architecture.graffle b/doc/design/dist_refactor/src/distributed_architecture.graffle new file mode 100644 index 0000000000000000000000000000000000000000..d1b60141342232e06227c2d430ebc60ec349a907 GIT binary patch literal 3800 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 literal 0 HcmV?d00001 diff --git a/doc/design/refactor/src/distributed_architecture.png b/doc/design/dist_refactor/src/distributed_architecture.png similarity index 58% rename from doc/design/refactor/src/distributed_architecture.png rename to doc/design/dist_refactor/src/distributed_architecture.png index 0da49f44123be7c0b84bf420ad81c33f0ac0a105..29c7b0c0783f97c6d33b1db1ed484d6a2b9dd356 100644 GIT binary patch delta 77404 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 PU7ot(fghqr-O$(;-8>@xLgY(l?Ub=--qq-i^ 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 Apa1{> diff --git a/doc/design/refactor/src/local-graph.graffle b/doc/design/dist_refactor/src/local-graph.graffle similarity index 100% rename from doc/design/refactor/src/local-graph.graffle rename to doc/design/dist_refactor/src/local-graph.graffle diff --git a/doc/design/refactor/src/local-graph.png b/doc/design/dist_refactor/src/local-graph.png similarity index 100% rename from doc/design/refactor/src/local-graph.png rename to doc/design/dist_refactor/src/local-graph.png diff --git a/doc/design/refactor/src/local_architecture.graffle b/doc/design/dist_refactor/src/local_architecture.graffle similarity index 100% rename from doc/design/refactor/src/local_architecture.graffle rename to doc/design/dist_refactor/src/local_architecture.graffle diff --git a/doc/design/refactor/src/local_architecture.png b/doc/design/dist_refactor/src/local_architecture.png similarity index 100% rename from doc/design/refactor/src/local_architecture.png rename to doc/design/dist_refactor/src/local_architecture.png diff --git a/doc/design/refactor/src/multi-threads.graffle b/doc/design/dist_refactor/src/multi-threads.graffle similarity index 100% rename from doc/design/refactor/src/multi-threads.graffle rename to doc/design/dist_refactor/src/multi-threads.graffle diff --git a/doc/design/refactor/src/multi-threads/multi-threads@3x.png b/doc/design/dist_refactor/src/multi-threads/multi-threads@3x.png similarity index 100% rename from doc/design/refactor/src/multi-threads/multi-threads@3x.png rename to doc/design/dist_refactor/src/multi-threads/multi-threads@3x.png diff --git a/doc/design/refactor/src/multi-threads/single-thread@3x.png b/doc/design/dist_refactor/src/multi-threads/single-thread@3x.png similarity index 100% rename from doc/design/refactor/src/multi-threads/single-thread@3x.png rename to doc/design/dist_refactor/src/multi-threads/single-thread@3x.png diff --git a/doc/design/refactor/src/paddle-compile.graffle b/doc/design/dist_refactor/src/paddle-compile.graffle similarity index 100% rename from doc/design/refactor/src/paddle-compile.graffle rename to doc/design/dist_refactor/src/paddle-compile.graffle diff --git a/doc/design/refactor/src/paddle-compile.png b/doc/design/dist_refactor/src/paddle-compile.png similarity index 100% rename from doc/design/refactor/src/paddle-compile.png rename to doc/design/dist_refactor/src/paddle-compile.png diff --git a/doc/design/refactor/src/remote_executor.graffle b/doc/design/dist_refactor/src/remote_executor.graffle similarity index 100% rename from doc/design/refactor/src/remote_executor.graffle rename to doc/design/dist_refactor/src/remote_executor.graffle diff --git a/doc/design/refactor/src/remote_executor.png b/doc/design/dist_refactor/src/remote_executor.png similarity index 100% rename from doc/design/refactor/src/remote_executor.png rename to doc/design/dist_refactor/src/remote_executor.png diff --git a/doc/design/refactor/src/distributed_architecture.graffle b/doc/design/refactor/src/distributed_architecture.graffle deleted file mode 100644 index 1ebbe70db04ebe0321f252a1de68aaefdaa32bd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3793 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 H51ar1g?@na -- GitLab From 3676789187f926ad848739c6cbff3592e8a542d3 Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Wed, 3 Jan 2018 14:28:15 +0800 Subject: [PATCH 0058/2305] Add a capi interface of release middle layer's output memory. --- paddle/capi/gradient_machine.cpp | 10 ++++++++++ paddle/capi/gradient_machine.h | 8 ++++++++ 2 files changed, 18 insertions(+) diff --git a/paddle/capi/gradient_machine.cpp b/paddle/capi/gradient_machine.cpp index 482b51e8a84..1f0e033c5b7 100644 --- a/paddle/capi/gradient_machine.cpp +++ b/paddle/capi/gradient_machine.cpp @@ -168,3 +168,13 @@ paddle_error paddle_gradient_machine_get_layer_output( out->args.push_back(layerOutput); return kPD_NO_ERROR; } + +paddle_error paddle_gradient_machine_release_layer_output( + paddle_gradient_machine machine) { + auto m = cast(machine); + if (m == nullptr || m->machine == nullptr) { + return kPD_NULLPTR; + } + m->machine->releaseOutput(); + return kPD_NO_ERROR; +} diff --git a/paddle/capi/gradient_machine.h b/paddle/capi/gradient_machine.h index 28eeb23e3bb..7e37dea00b2 100644 --- a/paddle/capi/gradient_machine.h +++ b/paddle/capi/gradient_machine.h @@ -113,6 +113,14 @@ paddle_gradient_machine_get_layer_output(paddle_gradient_machine machine, const char* layerName, paddle_arguments args); +/** + * @brief Release the middle layer's output memory of the gradient machine. + * @param [in] gradient machine that have run a inference + * @return paddle_error + */ +PD_API paddle_error +paddle_gradient_machine_release_layer_output(paddle_gradient_machine machine); + #ifdef __cplusplus } #endif -- GitLab From cb4caa5ccb673c3cd0b896607cfb9e2c2459794d Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Wed, 3 Jan 2018 14:39:16 +0800 Subject: [PATCH 0059/2305] Bug fix. --- paddle/gserver/gradientmachines/NeuralNetwork.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/gserver/gradientmachines/NeuralNetwork.cpp b/paddle/gserver/gradientmachines/NeuralNetwork.cpp index 3b6234a6e5e..1f2aa61b6f8 100644 --- a/paddle/gserver/gradientmachines/NeuralNetwork.cpp +++ b/paddle/gserver/gradientmachines/NeuralNetwork.cpp @@ -189,7 +189,7 @@ void NeuralNetwork::init(const ModelConfig& config, } for (const auto& layer : layers_) { - const auto name& = layer->getName(); + const auto& name = layer->getName(); bool isMiddleLayer = true; // if data layer -- GitLab From cb0b81f97b5791a29aab73797abfa2820b89f8a3 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Wed, 3 Jan 2018 07:21:40 +0000 Subject: [PATCH 0060/2305] add << lodtensor --- paddle/framework/lod_tensor.cc | 16 ++++++++++++++++ paddle/framework/lod_tensor.h | 1 + paddle/operators/parallel_do_op.cc | 5 +++-- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index beb2edee23d..dcde3705d6a 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -43,6 +43,22 @@ std::ostream &operator<<(std::ostream &os, const LoD &lod) { return os; } +std::ostream &operator<<(std::ostream &os, const LoDTensor &t) { + PADDLE_ENFORCE(platform::is_cpu_place(t.place())); + PADDLE_ENFORCE(t.type().hash_code() == typeid(float).hash_code()); + + os << "dim: " << t.dims() << "\n"; + os << "lod: " << t.lod() << "\n"; + + // only print first ten elements + int64_t size = t.numel() < 10 ? t.numel() : 10; + for (int64_t i = 0; i < size; ++i) { + os << t.data()[i] << " "; + } + + return os; +} + LoD SliceLevels(const LoD &in, size_t level_begin, size_t level_end) { LoD new_lod; new_lod.reserve(level_end - level_begin); diff --git a/paddle/framework/lod_tensor.h b/paddle/framework/lod_tensor.h index fae36892f00..7e44ffca3b8 100644 --- a/paddle/framework/lod_tensor.h +++ b/paddle/framework/lod_tensor.h @@ -58,6 +58,7 @@ using Vector = thrust::host_vector< using LoD = std::vector>; std::ostream& operator<<(std::ostream& os, const LoD& lod); +std::ostream& operator<<(std::ostream& os, const LoDTensor& t); /* * Slice levels from a LoD. diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index b15d171b9b6..fd48c1b54cb 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -156,11 +156,12 @@ class ParallelDoGradOp : public OperatorBase { for (auto &s : Inputs(framework::GradVarName(kOutputs))) { LOG(INFO) << s; - LOG(INFO) << scope.FindVar(s)->Get().dims(); + LOG(INFO) << scope.FindVar(s)->Get(); for (auto *sub_scope : sub_scopes) { - LOG(INFO) << sub_scope->FindVar(s)->Get().dims(); + LOG(INFO) << sub_scope->FindVar(s)->Get(); } } + // exe run for (int place_idx = 0; place_idx < places.size(); ++place_idx) { VLOG(3) << "Run " << place_idx; -- GitLab From 72652845b63bec4308e04fb112a338a607d87284 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 3 Jan 2018 16:53:57 +0800 Subject: [PATCH 0061/2305] add MKLDNNDeviceContext --- paddle/platform/device_context.cc | 64 +++++++++++++++++++++++++++++++ paddle/platform/device_context.h | 63 ++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/paddle/platform/device_context.cc b/paddle/platform/device_context.cc index ea07f2e002c..9d72569f51a 100644 --- a/paddle/platform/device_context.cc +++ b/paddle/platform/device_context.cc @@ -176,5 +176,69 @@ cudnnHandle_t CUDNNDeviceContext::cudnn_handle() const { return cudnn_handle_; } #endif +#ifdef PADDLE_WITH_MKLDNN +MKLDNNDeviceContext::MKLDNNDeviceContext(CPUPlace place) + : CPUDeviceContext(place), ready_(false) { + stream_.reset(new mkldnn::stream(mkldnn::stream::kind::eager)); + engine_.reset(new mkldnn::engine(mkldnn::engine::cpu, 0)); +} + +template +void MKLDNNDeviceContext::AddElement(const std::string& op_key, + const T& value) { + if (GetElement(op_key)) { + return; + } + GetElementPool().emplace(op_key, value); +} + +template +const T MKLDNNDeviceContext::GetElement(const std::string& op_key) const { + auto it = GetElementPool().find(op_key); + return it == GetElementPool().end() ? nullptr : it->second; +} + +template <> +const std::unordered_map>& +MKLDNNDeviceContext::GetElementPool() const { + return memory_pool_; +} + +template <> +const std::unordered_map>& +MKLDNNDeviceContext::GetElementPool() const { + return primitive_pool_; +} + +template <> +const std::unordered_map>& +MKLDNNDeviceContext::GetElementPool() const { + return primitive_desc_pool_; +} + +void MKLDNNDeviceContext::Execute(bool block) { + if (pipeline_.empty()) { + return; + } + ResetStream(); + stream_->submit(pipeline_).wait(block); + ready_ = false; + pipeline_.clear(); +} + +void MKLDNNDeviceContext::ResetStream() { + if (ready_) { + return; + } + // TODO(TJ): change me when mkldnn have specific method to reset this state + stream_.reset(new mkldnn::stream(mkldnn::stream::kind::eager)); + ready_ = true; +} + +#endif + } // namespace platform } // namespace paddle diff --git a/paddle/platform/device_context.h b/paddle/platform/device_context.h index 2b366e6383d..faabb8575e0 100644 --- a/paddle/platform/device_context.h +++ b/paddle/platform/device_context.h @@ -21,6 +21,10 @@ limitations under the License. */ #define EIGEN_USE_GPU #endif +#ifdef PADDLE_WITH_MKLDNN +#include "mkldnn.hpp" +#endif + #include "paddle/platform/enforce.h" #include "paddle/platform/place.h" #include "unsupported/Eigen/CXX11/Tensor" @@ -117,6 +121,65 @@ class CUDNNDeviceContext : public CUDADeviceContext { #endif +#ifdef PADDLE_WITH_MKLDNN +using MKLDNNStream = mkldnn::stream; +using MKLDNNEngine = mkldnn::engine; +using MKLDNNMemory = mkldnn::memory; +using MKLDNNPrimitive = mkldnn::primitive; +using MKLDNNPrimitiveDesc = mkldnn::handle; + +typedef std::shared_ptr MKLDNNEnginePtr; +typedef std::shared_ptr MKLDNNMemoryPtr; +typedef std::shared_ptr MKLDNNPrimitivePtr; +typedef std::shared_ptr MKLDNNPrimitiveDescPtr; +class MKLDNNDeviceContext : public CPUDeviceContext { + public: + explicit MKLDNNDeviceContext(CPUPlace place); + virtual ~MKLDNNDeviceContext(); + + /* \brief Add new element: memory, primitive or primitive desc */ + template + void AddElement(const std::string& op_key, const T& value); + + /* \brief Get existed element: memory, primitive or primitive desc */ + template + const T GetElement(const std::string& op_key) const; + + /* \brief Get element pool: memory, primitive or primitive desc pool */ + template + const std::unordered_map>& + GetElementPool() const; + + /* \brief Get the active engine */ + const MKLDNNEnginePtr GetEngine() const { return engine_; } + + /* \brief Submit primitive to pipeline */ + void Submit(const MKLDNNPrimitivePtr& p) { pipeline_.push_back(*p); } + + /*! \brief Execute all submitted primitives in pipeline */ + void Execute(bool block = true); + + protected: + /*! \brief Reset the stream to prepare next exectue */ + void ResetStream(); + + private: + std::unordered_map> + memory_pool_; + std::unordered_map> + primitive_pool_; + std::unordered_map> + primitive_desc_pool_; + std::vector pipeline_; + std::unique_ptr stream_; + MKLDNNEnginePtr engine_; + bool ready_; +}; +#endif + /*! \brief device context pool singleton */ class DeviceContextPool { public: -- GitLab From 03091ccbffcba56d2cc88b60b1c826398149db21 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 3 Jan 2018 16:55:13 +0800 Subject: [PATCH 0062/2305] add mkldnn_helper --- paddle/platform/device_context.h | 12 +---------- paddle/platform/mkldnn_helper.h | 34 ++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 paddle/platform/mkldnn_helper.h diff --git a/paddle/platform/device_context.h b/paddle/platform/device_context.h index faabb8575e0..68abe937b19 100644 --- a/paddle/platform/device_context.h +++ b/paddle/platform/device_context.h @@ -22,7 +22,7 @@ limitations under the License. */ #endif #ifdef PADDLE_WITH_MKLDNN -#include "mkldnn.hpp" +#include "paddle/platform/mkldnn_helper.h" #endif #include "paddle/platform/enforce.h" @@ -122,16 +122,6 @@ class CUDNNDeviceContext : public CUDADeviceContext { #endif #ifdef PADDLE_WITH_MKLDNN -using MKLDNNStream = mkldnn::stream; -using MKLDNNEngine = mkldnn::engine; -using MKLDNNMemory = mkldnn::memory; -using MKLDNNPrimitive = mkldnn::primitive; -using MKLDNNPrimitiveDesc = mkldnn::handle; - -typedef std::shared_ptr MKLDNNEnginePtr; -typedef std::shared_ptr MKLDNNMemoryPtr; -typedef std::shared_ptr MKLDNNPrimitivePtr; -typedef std::shared_ptr MKLDNNPrimitiveDescPtr; class MKLDNNDeviceContext : public CPUDeviceContext { public: explicit MKLDNNDeviceContext(CPUPlace place); diff --git a/paddle/platform/mkldnn_helper.h b/paddle/platform/mkldnn_helper.h new file mode 100644 index 00000000000..2649e4a5f37 --- /dev/null +++ b/paddle/platform/mkldnn_helper.h @@ -0,0 +1,34 @@ +/* Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "mkldnn.hpp" + +namespace paddle { +namespace platform { + +using MKLDNNStream = mkldnn::stream; +using MKLDNNEngine = mkldnn::engine; +using MKLDNNMemory = mkldnn::memory; +using MKLDNNPrimitive = mkldnn::primitive; +using MKLDNNPrimitiveDesc = mkldnn::handle; + +typedef std::shared_ptr MKLDNNEnginePtr; +typedef std::shared_ptr MKLDNNMemoryPtr; +typedef std::shared_ptr MKLDNNPrimitivePtr; +typedef std::shared_ptr MKLDNNPrimitiveDescPtr; + +} // namespace platform +} // namespace paddle -- GitLab From 31fda46cf4e936df006d102d16829cb1cea68dca Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 3 Jan 2018 17:25:00 +0800 Subject: [PATCH 0063/2305] fix mkldnn deps --- paddle/platform/CMakeLists.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/paddle/platform/CMakeLists.txt b/paddle/platform/CMakeLists.txt index 8c4803b9739..44f6d85cd15 100644 --- a/paddle/platform/CMakeLists.txt +++ b/paddle/platform/CMakeLists.txt @@ -21,10 +21,16 @@ ELSE() set(GPU_CTX_DEPS) ENDIF() +IF(WITH_MKLDNN) + set(MKLDNN_CTX_DEPS mkldnn) +ELSE() + set(MKLDNN_CTX_DEPS) +ENDIF() + # memcpy deoends on device_context, here add deps individually for # avoiding cycle dependencies cc_library(device_context SRCS device_context.cc DEPS memory buddy_allocator - system_allocator memory_block meta_data meta_cache place eigen3 ${GPU_CTX_DEPS}) + system_allocator memory_block meta_data meta_cache place eigen3 ${GPU_CTX_DEPS} ${MKLDNN_CTX_DEPS}) nv_test(device_context_test SRCS device_context_test.cu DEPS device_context gpu_info) nv_test(cudnn_helper_test SRCS cudnn_helper_test.cc DEPS dynload_cuda) -- GitLab From 6004a2ed4f74b864b8ff886d20e18891ac0a8ef3 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Wed, 3 Jan 2018 09:38:13 +0000 Subject: [PATCH 0064/2305] add copy skeleton --- paddle/operators/parallel_do_op.cc | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index fd48c1b54cb..6ac480b576c 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -47,7 +47,7 @@ void SplitTensorAndMoveTensorToScopes( LOG(INFO) << lod.dims(); } - for (int i = 0; i < sub_scopes.size(); ++i) { + for (size_t i = 0; i < sub_scopes.size(); ++i) { *sub_scopes[i]->Var(argu)->GetMutable() = lod_tensors[i]; } } @@ -73,15 +73,14 @@ class ParallelDoOp : public framework::OperatorBase { auto &sub_scopes = *scope.FindVar(Output(kParallelScopes)) ->GetMutable>(); - // std::vector sub_scopes; - for (int place_idx = 0; place_idx < places.size(); ++place_idx) { + for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { sub_scopes.push_back(&scope.NewScope()); } SplitTensorAndMoveTensorToScopes(scope, sub_scopes, places, Inputs(kInputs)); - for (int place_idx = 0; place_idx < places.size(); ++place_idx) { + for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { VLOG(3) << "Run " << place_idx; auto &place = places[place_idx]; @@ -163,17 +162,12 @@ class ParallelDoGradOp : public OperatorBase { } // exe run - for (int place_idx = 0; place_idx < places.size(); ++place_idx) { + for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { VLOG(3) << "Run " << place_idx; auto &place = places[place_idx]; auto *cur_scope = sub_scopes[place_idx]; - // copy parameter - if (dev_ctx.GetPlace() != place) { - PADDLE_THROW("Not Implemented"); - } - // execute auto executor = framework::Executor(place); executor.Run(*program, cur_scope, block->ID(), @@ -181,6 +175,21 @@ class ParallelDoGradOp : public OperatorBase { } // merge grad + for (auto &s : Outputs(framework::GradVarName(kParameters))) { + LOG(INFO) << s; + // std::string s_buf = s + "@BUF"; + // auto *t_buf = sub_scopes[0]->Var(s_buf)->GetMutable(); + for (size_t place_idx = 1; place_idx < places.size(); ++place_idx) { + LOG(INFO) << place_idx; + LOG(INFO) << sub_scopes[place_idx]->FindVar(s)->Get(); + // Copy grad[i] to grad_buf[0] + + // sum_op + } + + // Copy grad[0] to grad + // auto *t = scope.FindVar(s)->GetMutable(); + } } }; -- GitLab From 0250e54c2dc8ae4687a2ede661cd25dadfb66ce9 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 3 Jan 2018 09:48:29 +0000 Subject: [PATCH 0065/2305] Enable batch input in edit_distance_op --- paddle/operators/edit_distance_op.cc | 49 ++++++---- paddle/operators/edit_distance_op.cu | 98 +++++++++++-------- paddle/operators/edit_distance_op.h | 91 ++++++++++------- .../v2/fluid/tests/test_edit_distance_op.py | 52 ++++++++-- 4 files changed, 189 insertions(+), 101 deletions(-) diff --git a/paddle/operators/edit_distance_op.cc b/paddle/operators/edit_distance_op.cc index 6022a7a4bde..7b92148f0e1 100644 --- a/paddle/operators/edit_distance_op.cc +++ b/paddle/operators/edit_distance_op.cc @@ -22,10 +22,18 @@ class EditDistanceOp : public framework::OperatorWithKernel { using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext *ctx) const override { - PADDLE_ENFORCE(ctx->HasInput("Hyp"), "Input(Hyp) shouldn't be null."); - PADDLE_ENFORCE(ctx->HasInput("Ref"), "Input(Ref) shouldn't be null."); + PADDLE_ENFORCE(ctx->HasInput("Hyps"), "Input(Hyps) shouldn't be null."); + PADDLE_ENFORCE(ctx->HasInput("Refs"), "Input(Refs) shouldn't be null."); PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) shouldn't be null."); - ctx->SetOutputDim("Out", {1}); + auto hyp_dims = ctx->GetInputDim("Hyps"); + auto ref_dims = ctx->GetInputDim("Refs"); + PADDLE_ENFORCE(hyp_dims.size() == 2 && hyp_dims[1] == 1, + "Input(Hyps) must be a 2-D LoDTensor with the 2nd dimension " + "equal to 1."); + PADDLE_ENFORCE(ref_dims.size() == 2 && ref_dims[1] == 1, + "Input(Refs) must be a 2-D LoDTensor with the 2nd dimension " + "equal to 1."); + ctx->SetOutputDim("Out", ctx->GetInputDim("Refs")); } protected: @@ -40,24 +48,23 @@ class EditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { public: EditDistanceOpMaker(OpProto *proto, OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { - AddInput("Hyp", - "(2-D tensor with shape [M x 1]) The indices for " - "hypothesis string"); - AddInput("Ref", - "(2-D tensor with shape [N x 1]) The indices " - "for reference string."); + AddInput("Hyps", + "(2-D LoDTensor, 2nd dim. equal to 1) " + "The indices for hypothesis strings."); + AddInput("Refs", + "(2-D LoDTensor, 2nd dim. equal to 1) " + "The indices for reference strings."); AddAttr("normalized", - "(bool, default false) Indicated whether " - "normalize the Output(Out) by the length of reference " - "string (Ref).") + "(bool, default false) Indicated whether to normalize " + "the edit distance by the length of reference string.") .SetDefault(false); AddOutput("Out", - "(2-D tensor with shape [1 x 1]) " - "The output distance of EditDistance operator."); + "(2-D Tensor with shape [`batch_size` x 1]) " + "The output edit distances of EditDistance operator."); AddComment(R"DOC( -EditDistance operator computes the edit distance of two sequences, one named -hypothesis with length M and another named reference with length N. +EditDistance operator computes the edit distances between a batch of hypothesis +strings and their references. Edit distance, also called Levenshtein distance, measures how dissimilar two strings are by counting the minimum number of operations to transform one string into anthor. @@ -68,8 +75,14 @@ insertion: "kitten" -> "sitten" -> "sittin" -> "sitting" -If Attr(normalized) is true, the edit distance will be divided by the length of -reference string N. +Input(Hyps) is a LoDTensor consisting of all the hypothesis strings with the total +number denoted by `batch_size`, and the separation is specified by the LoD information. +And the `batch_size` reference strings are arranged in order in the same way in the +LoDTensor Input(Refs). + +Output(Out) contains the `batch_size` results and each stands for the edit stance +for a pair of strings respectively. If Attr(normalized) is true, the edit distance +will be divided by the length of reference string. )DOC"); } }; diff --git a/paddle/operators/edit_distance_op.cu b/paddle/operators/edit_distance_op.cu index fed91ffb430..b548345986c 100644 --- a/paddle/operators/edit_distance_op.cu +++ b/paddle/operators/edit_distance_op.cu @@ -70,53 +70,71 @@ class EditDistanceGPUKernel : public framework::OpKernel { void Compute(const framework::ExecutionContext& ctx) const { auto* out_t = ctx.Output("Out"); - auto* x1_t = ctx.Input("Hyp"); - auto* x2_t = ctx.Input("Ref"); - - out_t->mutable_data(ctx.GetPlace()); - auto out = out_t->data(); + auto* x1_t = ctx.Input("Hyps"); + auto* x2_t = ctx.Input("Refs"); auto normalized = ctx.Attr("normalized"); auto stream = reinterpret_cast( ctx.device_context()) .stream(); - auto m = x1_t->numel(); - auto n = x2_t->numel(); - T distance = 0.0; - if (m == 0 || n == 0) { - distance = std::max(m, n); - if (normalized) { - distance = distance / n; - } - memory::Copy(boost::get(ctx.GetPlace()), out, platform::CPUPlace(), - &distance, sizeof(T), stream); - } else { - framework::Tensor dist_t; - dist_t.Resize({m + 1, n + 1}); - dist_t.mutable_data(ctx.GetPlace()); - auto dist = dist_t.data(); - auto x1 = x1_t->data(); - auto x2 = x2_t->data(); - - FillFirstColumn<<<1 + m / PADDLE_CUDA_NUM_THREADS, - PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, m, n); - - FillFirstRow<<<1 + n / PADDLE_CUDA_NUM_THREADS, - PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, n); - // Compute the elements of distance matrix in the anti-diagonal diretion - for (int64_t slice = 2; slice < m + n + 1; ++slice) { - int z_m = slice < m + 1 ? 0 : slice - m; - int z_n = slice < n + 1 ? 0 : slice - n; - int size = slice - (z_m + z_n) + 1; // number of elments in the same - // anti-diagonal line to update - // the start index at which computes from - int start = slice < n + 1 ? slice : (z_n + 1) * (n + 1) - 1; - Levenshtein<<<1 + (size - 1) / PADDLE_CUDA_NUM_THREADS, - PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, x1, x2, m, - n, start); + auto hyp_lod = x1_t->lod()[0]; + auto ref_lod = x2_t->lod()[0]; + PADDLE_ENFORCE( + hyp_lod.size() == ref_lod.size(), + "Input(Hyps) and Input(Refs) must have the same batch size."); + for (size_t i = 1; i < ref_lod.size(); ++i) { + PADDLE_ENFORCE(ref_lod[i] > ref_lod[i - 1], + "Reference string %d is empty.", i); + } + + auto num_strs = hyp_lod.size() - 1; + out_t->Resize({static_cast(num_strs), 1}); + out_t->mutable_data(ctx.GetPlace()); + auto out = out_t->data(); + + std::vector distance(num_strs, 0.0); + for (size_t num = 0; num < num_strs; num++) { + auto m = static_cast(hyp_lod[num + 1] - hyp_lod[num]); + auto n = static_cast(ref_lod[num + 1] - ref_lod[num]); + if (m == 0 || n == 0) { + distance[num] = std::max(m, n); + if (normalized) { + PADDLE_ENFORCE(n > 0, + "The reference string (#%d) cannot be empty " + "when Attr(normalized) is enabled.", + n); + distance[num] = distance[num] / n; + } + memory::Copy(boost::get(ctx.GetPlace()), out + num, + platform::CPUPlace(), &distance[num], sizeof(T), stream); + } else { + framework::Tensor dist_t; + dist_t.Resize({m + 1, n + 1}); + dist_t.mutable_data(ctx.GetPlace()); + auto dist = dist_t.data(); + auto x1 = x1_t->data() + hyp_lod[num]; + auto x2 = x2_t->data() + ref_lod[num]; + + FillFirstColumn<<<1 + m / PADDLE_CUDA_NUM_THREADS, + PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, m, n); + + FillFirstRow<<<1 + n / PADDLE_CUDA_NUM_THREADS, + PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, n); + // Compute the elements of distance matrix in the anti-diagonal diretion + for (int64_t slice = 2; slice < m + n + 1; ++slice) { + int z_m = slice < m + 1 ? 0 : slice - m; + int z_n = slice < n + 1 ? 0 : slice - n; + int size = slice - (z_m + z_n) + 1; // number of elments in the same + // anti-diagonal line to update + // the start index at which computes from + int start = slice < n + 1 ? slice : (z_n + 1) * (n + 1) - 1; + Levenshtein<<<1 + (size - 1) / PADDLE_CUDA_NUM_THREADS, + PADDLE_CUDA_NUM_THREADS, 0, stream>>>(dist, x1, x2, + m, n, start); + } + SetOutput<<<1, 1, 0, stream>>>(out + num, dist, m, n, normalized); } - SetOutput<<<1, 1, 0, stream>>>(out, dist, m, n, normalized); } } }; diff --git a/paddle/operators/edit_distance_op.h b/paddle/operators/edit_distance_op.h index abde4fe97c4..6284f230e5b 100644 --- a/paddle/operators/edit_distance_op.h +++ b/paddle/operators/edit_distance_op.h @@ -26,50 +26,69 @@ class EditDistanceKernel : public framework::OpKernel { void Compute(const framework::ExecutionContext& ctx) const { auto* out_t = ctx.Output("Out"); - auto* x1_t = ctx.Input("Hyp"); - auto* x2_t = ctx.Input("Ref"); + auto* x1_t = ctx.Input("Hyps"); + auto* x2_t = ctx.Input("Refs"); + auto normalized = ctx.Attr("normalized"); + + auto hyp_lod = x1_t->lod()[0]; + auto ref_lod = x2_t->lod()[0]; + PADDLE_ENFORCE( + hyp_lod.size() == ref_lod.size(), + "Input(Hyps) and Input(Refs) must have the same batch size."); + for (size_t i = 1; i < ref_lod.size(); ++i) { + PADDLE_ENFORCE(ref_lod[i] > ref_lod[i - 1], + "Reference string %d is empty.", i); + } + auto num_strs = hyp_lod.size() - 1; + + out_t->Resize({static_cast(num_strs), 1}); out_t->mutable_data(ctx.GetPlace()); + auto out = out_t->data(); - auto normalized = ctx.Attr("normalized"); + std::vector distance(num_strs, 0.0); + for (size_t num = 0; num < num_strs; ++num) { + auto m = static_cast(hyp_lod[num + 1] - hyp_lod[num]); + auto n = static_cast(ref_lod[num + 1] - ref_lod[num]); - auto m = x1_t->numel(); - auto n = x2_t->numel(); - T distance = 0.0; - if (m == 0) { - distance = n; - } else if (n == 0) { - distance = m; - } else { - framework::Tensor dist_t; - dist_t.Resize({m + 1, n + 1}); - dist_t.mutable_data(ctx.GetPlace()); - auto dist = dist_t.data(); - auto x1 = x1_t->data(); - auto x2 = x2_t->data(); - for (int64_t i = 0; i < m + 1; ++i) { - dist[i * (n + 1)] = i; - } - for (int64_t j = 0; j < n + 1; ++j) { - dist[j] = j; - } - for (int64_t i = 1; i < m + 1; ++i) { - for (int64_t j = 1; j < n + 1; ++j) { - int cost = x1[i - 1] == x2[j - 1] ? 0 : 1; - int dels = dist[(i - 1) * (n + 1) + j] + 1; - int ins = dist[i * (n + 1) + (j - 1)] + 1; - int subs = dist[(i - 1) * (n + 1) + (j - 1)] + cost; - dist[i * (n + 1) + j] = std::min(dels, std::min(ins, subs)); + if (m == 0) { + distance[num] = n; + } else if (n == 0) { + distance[num] = m; + } else { + framework::Tensor dist_t; + dist_t.Resize({m + 1, n + 1}); + dist_t.mutable_data(ctx.GetPlace()); + auto dist = dist_t.data(); + auto x1 = x1_t->data() + hyp_lod[num]; + auto x2 = x2_t->data() + ref_lod[num]; + for (int64_t i = 0; i < m + 1; ++i) { + dist[i * (n + 1)] = i; + } + for (int64_t j = 0; j < n + 1; ++j) { + dist[j] = j; + } + for (int64_t i = 1; i < m + 1; ++i) { + for (int64_t j = 1; j < n + 1; ++j) { + int cost = x1[i - 1] == x2[j - 1] ? 0 : 1; + int dels = dist[(i - 1) * (n + 1) + j] + 1; + int ins = dist[i * (n + 1) + (j - 1)] + 1; + int subs = dist[(i - 1) * (n + 1) + (j - 1)] + cost; + dist[i * (n + 1) + j] = std::min(dels, std::min(ins, subs)); + } } + distance[num] = dist[m * (n + 1) + n]; } - distance = dist[m * (n + 1) + n]; - } - if (normalized) { - distance = distance / n; + if (normalized) { + PADDLE_ENFORCE(n > 0, + "The reference string (#%d) cannot be empty " + "when Attr(normalized) is enabled.", + n); + distance[num] = distance[num] / n; + } + out[num] = distance[num]; } - auto out = out_t->data(); - out[0] = distance; } }; diff --git a/python/paddle/v2/fluid/tests/test_edit_distance_op.py b/python/paddle/v2/fluid/tests/test_edit_distance_op.py index df1ac620e79..24f2f0c5c24 100644 --- a/python/paddle/v2/fluid/tests/test_edit_distance_op.py +++ b/python/paddle/v2/fluid/tests/test_edit_distance_op.py @@ -18,7 +18,7 @@ def Levenshtein(hyp, ref): if n == 0: return m - dist = np.zeros((m + 1, n + 1)) + dist = np.zeros((m + 1, n + 1)).astype("float32") for i in range(0, m + 1): dist[i][0] = i for j in range(0, n + 1): @@ -35,17 +35,55 @@ def Levenshtein(hyp, ref): class TestCTCEditDistanceOp(OpTest): + def setUp(self): + self.op_type = "edit_distance" + normalized = False + x1 = np.array([[0, 12, 3, 5, 8, 2]]).astype("int32") + x2 = np.array([[0, 12, 4, 7, 8]]).astype("int32") + x1 = np.transpose(x1) + x2 = np.transpose(x2) + x1_lod = [0, 1, 5] + x2_lod = [0, 3, 4] + + num_strs = len(x1_lod) - 1 + distance = np.zeros((num_strs, 1)).astype("float32") + for i in range(0, num_strs): + distance[i] = Levenshtein( + hyp=x1[x1_lod[i]:x1_lod[i + 1]], + ref=x2[x2_lod[i]:x2_lod[i + 1]]) + if normalized is True: + len_ref = x2_lod[i + 1] - x2_lod[i] + distance[i] = distance[i] / len_ref + self.attrs = {'normalized': normalized} + self.inputs = {'Hyps': (x1, [x1_lod]), 'Refs': (x2, [x2_lod])} + self.outputs = {'Out': distance} + + def test_check_output(self): + self.check_output() + + +class TestCTCEditDistanceOpNormalized(OpTest): def setUp(self): self.op_type = "edit_distance" normalized = True - x1 = np.array([0, 12, 3, 5]).astype("int32") - x2 = np.array([0, 12, 4, 7, 8]).astype("int32") + x1 = np.array([[0, 10, 3, 6, 5, 8, 2]]).astype("int32") + x2 = np.array([[0, 10, 4, 6, 7, 8]]).astype("int32") + x1 = np.transpose(x1) + x2 = np.transpose(x2) + x1_lod = [0, 1, 3, 6] + x2_lod = [0, 2, 3, 5] - distance = Levenshtein(hyp=x1, ref=x2) - if normalized is True: - distance = distance / len(x2) + num_strs = len(x1_lod) - 1 + distance = np.zeros((num_strs, 1)).astype("float32") + for i in range(0, num_strs): + distance[i] = Levenshtein( + hyp=x1[x1_lod[i]:x1_lod[i + 1]], + ref=x2[x2_lod[i]:x2_lod[i + 1]]) + if normalized is True: + len_ref = x2_lod[i + 1] - x2_lod[i] + distance[i] = distance[i] / len_ref self.attrs = {'normalized': normalized} - self.inputs = {'Hyp': x1, 'Ref': x2} + self.inputs = {'Hyps': (x1, [x1_lod]), 'Refs': (x2, [x2_lod])} self.outputs = {'Out': distance} def test_check_output(self): -- GitLab From 8ee17e965f8ff488c6ea4a0437653b504ccab9a2 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Wed, 3 Jan 2018 10:46:38 +0000 Subject: [PATCH 0066/2305] pass sgd at first iter --- paddle/operators/parallel_do_op.cc | 23 +++++++++++++------ .../paddle/v2/fluid/tests/test_parallel_op.py | 3 ++- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index b067e3bda4c..a81ddb25c4e 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -185,18 +185,27 @@ class ParallelDoGradOp : public OperatorBase { // merge grad for (auto &s : Outputs(framework::GradVarName(kParameters))) { LOG(INFO) << s; - // std::string s_buf = s + "@BUF"; - // auto *t_buf = sub_scopes[0]->Var(s_buf)->GetMutable(); + + auto &t = sub_scopes[0]->FindVar(s)->Get(); + LOG(INFO) << t; + + std::string s_buf = s + "@BUF"; + auto *t_buf = sub_scopes[0]->Var(s_buf)->GetMutable(); + for (size_t place_idx = 1; place_idx < places.size(); ++place_idx) { + auto &tt = sub_scopes[place_idx]->FindVar(s)->Get(); LOG(INFO) << place_idx; - LOG(INFO) << sub_scopes[place_idx]->FindVar(s)->Get(); - // Copy grad[i] to grad_buf[0] + LOG(INFO) << tt; + framework::CopyFrom(tt, places[0], t_buf); - // sum_op + auto sum_op = framework::OpRegistry::CreateOp( + "sum", {{"X", {s, s_buf}}}, {{"Out", {s}}}, + framework::AttributeMap{}); + sum_op->Run(*sub_scopes[0], place); } - // Copy grad[0] to grad - // auto *t = scope.FindVar(s)->GetMutable(); + LOG(INFO) << t; + framework::CopyFrom(t, place, scope.FindVar(s)->GetMutable()); } } }; diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index eec546107fa..c39040869d1 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -27,7 +27,8 @@ class ParallelOpTest(unittest.TestCase): pd.write_output(hidden) data = pd() loss = layers.mean(x=data) - append_backward(loss) + sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.001) + sgd_optimizer.minimize(loss) exe = fluid.Executor(fluid.CPUPlace()) exe.run(fluid.default_startup_program()) -- GitLab From 7411df34b8c2c1c21b0158489e5659d74284e9f3 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Wed, 3 Jan 2018 11:08:49 +0000 Subject: [PATCH 0067/2305] add multi thread --- paddle/operators/parallel_do_op.cc | 45 ++++++++++++------- .../paddle/v2/fluid/tests/test_parallel_op.py | 4 +- 2 files changed, 31 insertions(+), 18 deletions(-) diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index a81ddb25c4e..c32884f8c28 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -12,6 +12,7 @@ See the License for the specific language governing permissions and limitations under the License. */ +#include #include #include "paddle/framework/executor.h" @@ -44,7 +45,7 @@ void SplitTensorAndMoveTensorToScopes( auto lod_tensors = tensor.SplitLoDTensor(places); for (auto &lod : lod_tensors) { - LOG(INFO) << lod.dims(); + VLOG(3) << lod.dims(); } for (size_t i = 0; i < sub_scopes.size(); ++i) { @@ -84,6 +85,7 @@ class ParallelDoOp : public framework::OperatorBase { SplitTensorAndMoveTensorToScopes(scope, sub_scopes, places, Inputs(kInputs)); + std::vector workers; for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { VLOG(3) << "Run " << place_idx; @@ -96,9 +98,14 @@ class ParallelDoOp : public framework::OperatorBase { } // execute - auto executor = framework::Executor(place); - executor.Run(*program, cur_scope, block->ID(), - false /*create_local_scope*/); + workers.push_back(std::thread([program, cur_scope, place, block] { + auto executor = framework::Executor(place); + executor.Run(*program, cur_scope, block->ID(), + false /*create_local_scope*/); + })); + } + for (auto &worker : workers) { + worker.join(); } // merge output @@ -162,14 +169,15 @@ class ParallelDoGradOp : public OperatorBase { Inputs(framework::GradVarName(kOutputs))); for (auto &s : Inputs(framework::GradVarName(kOutputs))) { - LOG(INFO) << s; - LOG(INFO) << scope.FindVar(s)->Get(); + VLOG(3) << s; + VLOG(3) << scope.FindVar(s)->Get(); for (auto *sub_scope : sub_scopes) { - LOG(INFO) << sub_scope->FindVar(s)->Get(); + VLOG(3) << sub_scope->FindVar(s)->Get(); } } // exe run + std::vector workers; for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { VLOG(3) << "Run " << place_idx; @@ -177,25 +185,30 @@ class ParallelDoGradOp : public OperatorBase { auto *cur_scope = sub_scopes[place_idx]; // execute - auto executor = framework::Executor(place); - executor.Run(*program, cur_scope, block->ID(), - false /*create_local_scope*/); + workers.push_back(std::thread([program, cur_scope, place, block] { + auto executor = framework::Executor(place); + executor.Run(*program, cur_scope, block->ID(), + false /*create_local_scope*/); + })); + } + for (auto &worker : workers) { + worker.join(); } // merge grad for (auto &s : Outputs(framework::GradVarName(kParameters))) { - LOG(INFO) << s; + VLOG(3) << s; auto &t = sub_scopes[0]->FindVar(s)->Get(); - LOG(INFO) << t; + VLOG(3) << t; std::string s_buf = s + "@BUF"; auto *t_buf = sub_scopes[0]->Var(s_buf)->GetMutable(); for (size_t place_idx = 1; place_idx < places.size(); ++place_idx) { auto &tt = sub_scopes[place_idx]->FindVar(s)->Get(); - LOG(INFO) << place_idx; - LOG(INFO) << tt; + VLOG(3) << place_idx; + VLOG(3) << tt; framework::CopyFrom(tt, places[0], t_buf); auto sum_op = framework::OpRegistry::CreateOp( @@ -204,7 +217,7 @@ class ParallelDoGradOp : public OperatorBase { sum_op->Run(*sub_scopes[0], place); } - LOG(INFO) << t; + VLOG(3) << t; framework::CopyFrom(t, place, scope.FindVar(s)->GetMutable()); } } @@ -219,7 +232,7 @@ class ParallelDoGradOpDescMaker : public framework::SingleGradOpDescMaker { auto *grad = new framework::OpDesc(); grad->SetType("parallel_do_grad"); for (auto &input_param : this->InputNames()) { - LOG(INFO) << input_param; + VLOG(3) << input_param; grad->SetInput(input_param, this->Input(input_param)); grad->SetOutput(framework::GradVarName(input_param), this->InputGrad(input_param, false)); diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index c39040869d1..2788f4e519b 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -12,7 +12,7 @@ import paddle.v2.fluid.core as core class ParallelOpTest(unittest.TestCase): def setUp(self): x = layers.data( - shape=[-1, 3, 4], + shape=[-1, 30, 40], dtype='float32', name='x', append_batch_size=False, @@ -35,7 +35,7 @@ class ParallelOpTest(unittest.TestCase): exe.run(fluid.default_main_program(), feed={ x.name: np.random.uniform(0.1, 0.6, - (2, 3, 4)).astype("float32") + (20, 30, 40)).astype("float32") }) def test_forward(self): -- GitLab From b0ba2b061e5e130b8ef6648705a6a9cadd2c3b7b Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 3 Jan 2018 18:46:58 +0800 Subject: [PATCH 0068/2305] fix typo --- paddle/platform/device_context.h | 1 - paddle/platform/mkldnn_helper.h | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/paddle/platform/device_context.h b/paddle/platform/device_context.h index 68abe937b19..44f3ac1d392 100644 --- a/paddle/platform/device_context.h +++ b/paddle/platform/device_context.h @@ -125,7 +125,6 @@ class CUDNNDeviceContext : public CUDADeviceContext { class MKLDNNDeviceContext : public CPUDeviceContext { public: explicit MKLDNNDeviceContext(CPUPlace place); - virtual ~MKLDNNDeviceContext(); /* \brief Add new element: memory, primitive or primitive desc */ template diff --git a/paddle/platform/mkldnn_helper.h b/paddle/platform/mkldnn_helper.h index 2649e4a5f37..07a9c362c57 100644 --- a/paddle/platform/mkldnn_helper.h +++ b/paddle/platform/mkldnn_helper.h @@ -14,7 +14,7 @@ limitations under the License. */ #pragma once -#include "mkldnn.hpp" +#include namespace paddle { namespace platform { -- GitLab From 66ae0a8cb28fdd725edd8e10cf2e63a3bb44e761 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Wed, 3 Jan 2018 22:32:34 +0800 Subject: [PATCH 0069/2305] Enhence shrink_rnn_memory_op. --- paddle/operators/shrink_rnn_memory_op.cc | 21 ++++++++++++++----- .../v2/fluid/tests/test_shrink_rnn_memory.py | 8 +++---- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/paddle/operators/shrink_rnn_memory_op.cc b/paddle/operators/shrink_rnn_memory_op.cc index b37269b471b..cc9e3f90b42 100644 --- a/paddle/operators/shrink_rnn_memory_op.cc +++ b/paddle/operators/shrink_rnn_memory_op.cc @@ -46,8 +46,19 @@ class ShrinkRNNMemoryOp : public ArrayOp { auto *out_var = scope.FindVar(Output("Out")); PADDLE_ENFORCE(out_var != nullptr, "Output Out must be set"); auto &out_tensor = *out_var->GetMutable(); + + // should consider multiple levels + size_t height = dst_num_rows; + auto lod_level = lod_rank_table.level(); + if (x_tensor.lod().size() > lod_level && + x_tensor.lod()[lod_level].size() < dst_num_rows) { + auto lod_offset = framework::GetSubLoDAndAbsoluteOffset( + x_tensor.lod(), 0, dst_num_rows + 1, lod_level); + height = lod_offset.second.second; + } + if (dst_num_rows != 0) { - out_tensor.ShareDataWith(x_tensor.Slice(0, dst_num_rows)); + out_tensor.ShareDataWith(x_tensor.Slice(0, height)); } } }; @@ -64,11 +75,11 @@ class ShrinkRNNMemoryOpProtoMaker : public framework::OpProtoAndCheckerMaker { AddOutput("Out", "(LoDTensor) The shrinked RNN step memory."); AddComment( R"DOC( - In dynamic RNN, we are able to handle sequences of different lengths. - Because of the multiple lengths, the size of each step input can be + In dynamic RNN, we are able to handle sequences of different lengths. + Because of the multiple lengths, the size of each step input can be different, which may lead to a mismatching between the input of - the current step and the memory generated by the previous one. This - operator shrinks memory according to the size of the next step input, + the current step and the memory generated by the previous one. This + operator shrinks memory according to the size of the next step input, to make sure that they can match each other. )DOC"); } diff --git a/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py b/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py index be1588fc2d0..707dbd793a0 100644 --- a/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py +++ b/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py @@ -26,13 +26,13 @@ class TestShrinkRNNMemory(unittest.TestCase): cpu = core.CPUPlace() tensor = core.LoDTensor() tensor.set_lod([[0, 2, 5, 6]]) - tensor_np = numpy.random.random(size=(3, 100)).astype('float32') + tensor_np = numpy.random.random(size=(6, 100)).astype('float32') tensor.set(tensor_np, cpu) exe = Executor(cpu) outs = exe.run(feed={'x': tensor}, fetch_list=[mem1, mem2, mem3]) - self.assertTrue(numpy.allclose(tensor_np[0:3], outs[0])) - self.assertTrue(numpy.allclose(tensor_np[0:2], outs[1])) - self.assertTrue(numpy.allclose(tensor_np[0:1], outs[2])) + self.assertTrue(numpy.allclose(tensor_np[0:6], outs[0])) + self.assertTrue(numpy.allclose(tensor_np[0:5], outs[1])) + self.assertTrue(numpy.allclose(tensor_np[0:2], outs[2])) mem3_mean = layers.mean(x=mem3) append_backward(loss=mem3_mean) -- GitLab From 0a8775cc5dff90264dd663498e8ba8f86d8b660d Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 3 Jan 2018 23:37:53 +0800 Subject: [PATCH 0070/2305] fix shape_inference deps --- paddle/framework/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt index b4458eb9551..79a09986cd2 100644 --- a/paddle/framework/CMakeLists.txt +++ b/paddle/framework/CMakeLists.txt @@ -38,7 +38,7 @@ device_context) cc_library(op_proto_maker SRCS op_proto_maker.cc DEPS framework_proto attribute) cc_test(op_proto_maker_test SRCS op_proto_maker_test.cc DEPS op_proto_maker) cc_library(op_info SRCS op_info.cc DEPS attribute framework_proto) -cc_library(shape_inference SRCS shape_inference.cc DEPS ddim attribute) +cc_library(shape_inference SRCS shape_inference.cc DEPS ddim attribute device_context) cc_library(operator SRCS operator.cc DEPS op_info device_context tensor scope glog shape_inference data_transform) cc_test(operator_test SRCS operator_test.cc DEPS operator op_registry init) -- GitLab From 5bf5650dcf26f437346bc4dedb40a0dbd7138732 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Thu, 4 Jan 2018 10:08:56 +0800 Subject: [PATCH 0071/2305] generate mkldnn dummy target for static deps --- cmake/external/mkldnn.cmake | 16 ++++++++++++---- paddle/operators/tensor.save | Bin 462 -> 466 bytes 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cmake/external/mkldnn.cmake b/cmake/external/mkldnn.cmake index 5d24caebdcc..b67c559fdf7 100644 --- a/cmake/external/mkldnn.cmake +++ b/cmake/external/mkldnn.cmake @@ -63,9 +63,17 @@ ExternalProject_Add( -DMKLROOT:PATH=${MKLML_ROOT} ) -ADD_LIBRARY(mkldnn SHARED IMPORTED GLOBAL) -SET_PROPERTY(TARGET mkldnn PROPERTY IMPORTED_LOCATION ${MKLDNN_LIB}) -ADD_DEPENDENCIES(mkldnn ${MKLDNN_PROJECT}) +ADD_LIBRARY(shared_mkldnn SHARED IMPORTED GLOBAL) +SET_PROPERTY(TARGET shared_mkldnn PROPERTY IMPORTED_LOCATION ${MKLDNN_LIB}) +ADD_DEPENDENCIES(shared_mkldnn ${MKLDNN_PROJECT}) MESSAGE(STATUS "MKLDNN library: ${MKLDNN_LIB}") add_definitions(-DPADDLE_WITH_MKLDNN) -LIST(APPEND external_project_dependencies mkldnn) +LIST(APPEND external_project_dependencies shared_mkldnn) + +# generate a static dummy target to track mkldnn dependencies +# for cc_library(xxx SRCS xxx.c DEPS mkldnn) +SET(dummyfile ${CMAKE_CURRENT_BINARY_DIR}/mkldnn_dummy.c) +FILE(WRITE ${dummyfile} "const char * dummy = \"${dummyfile}\";") +ADD_LIBRARY(mkldnn STATIC ${dummyfile}) +TARGET_LINK_LIBRARIES(mkldnn ${MKLDNN_LIB} ${MKLML_LIB} ${MKLML_IOMP_LIB}) +ADD_DEPENDENCIES(mkldnn ${MKLDNN_PROJECT}) diff --git a/paddle/operators/tensor.save b/paddle/operators/tensor.save index c24308a7d0131b84c28c0a9857cce4949afb2091..de6cd5a66a26e9e8f2bee245cf17ec190c5c1e05 100644 GIT binary patch delta 13 UcmX@de2JNnVe)K7;|=o|0UwzJSpWb4 delta 11 Scmcb_e2#ge$vno1ECv7@nFH_u -- GitLab From fccbc2fc2816c7a8665cbe228f28dd4ffdb25ef4 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 4 Jan 2018 02:56:05 +0000 Subject: [PATCH 0072/2305] licence update --- paddle/operators/parallel_do_op.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index c32884f8c28..348356f28d6 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -1,16 +1,16 @@ /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. */ +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ #include #include -- GitLab From 97dc451f4a069c816722f37a79cc53e972b7e0dc Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 4 Jan 2018 03:06:12 +0000 Subject: [PATCH 0073/2305] clean up --- paddle/framework/operator.cc | 8 -------- paddle/operators/elementwise_op.h | 5 ----- 2 files changed, 13 deletions(-) diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index 60f31635ac2..9e3ef5f4ca9 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -182,8 +182,6 @@ static const Tensor* GetTensorFromVar(const Variable* var) { const Tensor* t = nullptr; if (var->IsType()) { t = &(var->Get()); - } else if (var->IsType()) { - t = &(var->Get()); } else if (var->IsType()) { t = &(var->Get().value()); } else { @@ -197,8 +195,6 @@ static Tensor* GetMutableTensorFromVar(Variable* var) { Tensor* t = nullptr; if (var->IsType()) { t = var->GetMutable(); - } else if (var->IsType()) { - t = var->GetMutable(); } else if (var->IsType()) { t = var->GetMutable()->mutable_value(); } else { @@ -362,8 +358,6 @@ class RuntimeInferShapeContext : public InferShapeContext { Variable* var = scope_.FindVar(name); if (var->IsType()) { return var->Get().dims(); - } else if (var->IsType()) { - return var->Get().dims(); } else if (var->IsType()) { return var->Get().GetCompleteDims(); } else { @@ -376,8 +370,6 @@ class RuntimeInferShapeContext : public InferShapeContext { Variable* var = scope_.FindVar(name); if (var->IsType()) { var->GetMutable()->Resize(dim); - } else if (var->IsType()) { - var->GetMutable()->Resize(dim); } else if (var->IsType()) { var->GetMutable()->set_height(dim[0]); } else { diff --git a/paddle/operators/elementwise_op.h b/paddle/operators/elementwise_op.h index 4128a3b1dcd..f308ee05e11 100644 --- a/paddle/operators/elementwise_op.h +++ b/paddle/operators/elementwise_op.h @@ -34,8 +34,6 @@ class ElementwiseOp : public framework::OperatorWithKernel { auto x_dim = ctx->GetInputDim("X"); auto y_dim = ctx->GetInputDim("Y"); - LOG(INFO) << x_dim; - LOG(INFO) << y_dim; PADDLE_ENFORCE_GE(x_dim.size(), y_dim.size(), "Rank of first input must >= rank of second input."); ctx->SetOutputDim("Out", x_dim); @@ -119,9 +117,6 @@ class ElementwiseOpGrad : public framework::OperatorWithKernel { auto x_dims = ctx->GetInputDim("X"); auto y_dims = ctx->GetInputDim("Y"); auto out_dims = ctx->GetInputDim(framework::GradVarName("Out")); - LOG(INFO) << x_dims; - LOG(INFO) << y_dims; - LOG(INFO) << out_dims; PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), "Rank of first input must >= rank of second input."); -- GitLab From f947c1537830c71e6698e65fd5ddf32781075fd7 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Thu, 4 Jan 2018 12:53:25 +0800 Subject: [PATCH 0074/2305] Consider multiple levels of LoD. --- paddle/operators/shrink_rnn_memory_op.cc | 19 ++++++++++++++++--- .../v2/fluid/tests/test_shrink_rnn_memory.py | 4 +++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/paddle/operators/shrink_rnn_memory_op.cc b/paddle/operators/shrink_rnn_memory_op.cc index cc9e3f90b42..29f88896e74 100644 --- a/paddle/operators/shrink_rnn_memory_op.cc +++ b/paddle/operators/shrink_rnn_memory_op.cc @@ -12,6 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/framework/lod_rank_table.h" +#include "paddle/framework/lod_tensor.h" #include "paddle/operators/array_operator.h" #include "paddle/operators/math/math_function.h" @@ -49,12 +50,24 @@ class ShrinkRNNMemoryOp : public ArrayOp { // should consider multiple levels size_t height = dst_num_rows; - auto lod_level = lod_rank_table.level(); + auto lod_level = rank_table.level(); + if (x_tensor.lod().size() > lod_level && - x_tensor.lod()[lod_level].size() < dst_num_rows) { + x_tensor.lod()[lod_level].size() > static_cast(dst_num_rows)) { auto lod_offset = framework::GetSubLoDAndAbsoluteOffset( - x_tensor.lod(), 0, dst_num_rows + 1, lod_level); + x_tensor.lod(), 0, dst_num_rows, lod_level); height = lod_offset.second.second; + auto out_lod = out_tensor.mutable_lod(); + auto x_lod = x_tensor.lod(); + out_lod->reserve(lod_level + lod_offset.first.size()); + for (size_t i = 0; i < lod_level; ++i) { + out_lod->emplace_back(x_lod.at(i)); + } + framework::LoD remain; + framework::AppendLoD(&remain, lod_offset.first); + for (size_t j = 0; j < remain.size(); ++j) { + out_lod->emplace_back(remain[j]); + } } if (dst_num_rows != 0) { diff --git a/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py b/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py index 707dbd793a0..9d8565b1681 100644 --- a/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py +++ b/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py @@ -29,7 +29,9 @@ class TestShrinkRNNMemory(unittest.TestCase): tensor_np = numpy.random.random(size=(6, 100)).astype('float32') tensor.set(tensor_np, cpu) exe = Executor(cpu) - outs = exe.run(feed={'x': tensor}, fetch_list=[mem1, mem2, mem3]) + outs = exe.run(feed={'x': tensor}, + fetch_list=[mem1, mem2, mem3], + return_numpy=False) self.assertTrue(numpy.allclose(tensor_np[0:6], outs[0])) self.assertTrue(numpy.allclose(tensor_np[0:5], outs[1])) self.assertTrue(numpy.allclose(tensor_np[0:2], outs[2])) -- GitLab From e32103645e22943914a99e303e63e0e84dabde57 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Thu, 4 Jan 2018 14:31:19 +0800 Subject: [PATCH 0075/2305] Only shrink for the first level LoD. --- paddle/operators/shrink_rnn_memory_op.cc | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/paddle/operators/shrink_rnn_memory_op.cc b/paddle/operators/shrink_rnn_memory_op.cc index 29f88896e74..b958e6c5959 100644 --- a/paddle/operators/shrink_rnn_memory_op.cc +++ b/paddle/operators/shrink_rnn_memory_op.cc @@ -48,26 +48,16 @@ class ShrinkRNNMemoryOp : public ArrayOp { PADDLE_ENFORCE(out_var != nullptr, "Output Out must be set"); auto &out_tensor = *out_var->GetMutable(); - // should consider multiple levels size_t height = dst_num_rows; - auto lod_level = rank_table.level(); - if (x_tensor.lod().size() > lod_level && - x_tensor.lod()[lod_level].size() > static_cast(dst_num_rows)) { - auto lod_offset = framework::GetSubLoDAndAbsoluteOffset( - x_tensor.lod(), 0, dst_num_rows, lod_level); + // do shrink for the top level LoD + if (x_tensor.lod().size() > 0 && + x_tensor.lod()[0].size() > static_cast(dst_num_rows)) { + auto lod_offset = framework::GetSubLoDAndAbsoluteOffset(x_tensor.lod(), 0, + dst_num_rows, 0); height = lod_offset.second.second; auto out_lod = out_tensor.mutable_lod(); - auto x_lod = x_tensor.lod(); - out_lod->reserve(lod_level + lod_offset.first.size()); - for (size_t i = 0; i < lod_level; ++i) { - out_lod->emplace_back(x_lod.at(i)); - } - framework::LoD remain; - framework::AppendLoD(&remain, lod_offset.first); - for (size_t j = 0; j < remain.size(); ++j) { - out_lod->emplace_back(remain[j]); - } + framework::AppendLoD(out_lod, lod_offset.first); } if (dst_num_rows != 0) { -- GitLab From 134c5c4db7b0d8e6188821cef41c028a790f26e9 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Thu, 4 Jan 2018 15:14:17 +0800 Subject: [PATCH 0076/2305] Support callback --- python/paddle/v2/fluid/backward.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/paddle/v2/fluid/backward.py b/python/paddle/v2/fluid/backward.py index ac60bf54360..b788a23eb60 100644 --- a/python/paddle/v2/fluid/backward.py +++ b/python/paddle/v2/fluid/backward.py @@ -188,7 +188,10 @@ def _append_backward_ops_(target, grad_to_var(dict)(output argument): key(str): grad variable name val(str): corresponding forward variable name + callback(callable object): a callable object used to decorate new generated grad ops """ + if callback is not None and not hasattr(callback, '__call__'): + raise ValueError("'callback' must be a callable object.") # grad_op_descs holds created grad_op, and will be appended to target_block grad_op_descs = [] program = block.program @@ -205,6 +208,8 @@ def _append_backward_ops_(target, # Getting op's corresponding grad_op grad_op_desc, op_grad_to_var = core.get_grad_op_desc( op.desc, no_grad_dict[block.idx], grad_sub_block_list) + if callback is not None: + grad_op_desc = callback(grad_op_desc) grad_op_descs.extend(grad_op_desc) grad_to_var.update(op_grad_to_var) -- GitLab From f9ef6d15190604e4a0780e76c7254a7875e7352e Mon Sep 17 00:00:00 2001 From: peterzhang2029 Date: Thu, 4 Jan 2018 16:11:50 +0800 Subject: [PATCH 0077/2305] init --- paddle/operators/adagrad_op.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/paddle/operators/adagrad_op.h b/paddle/operators/adagrad_op.h index 0d77dbcbacd..667c1939a2a 100644 --- a/paddle/operators/adagrad_op.h +++ b/paddle/operators/adagrad_op.h @@ -47,8 +47,8 @@ class AdagradOpKernel : public framework::OpKernel { *ctx.Input("Grad")); auto moment = framework::EigenVector::Flatten( *ctx.Input("Moment")); - auto lr = framework::EigenVector::Flatten( - *ctx.Input("LearningRate")); + auto* learning_rate = ctx.Input("LearningRate"); + auto* lr = learning_rate->data(); auto param_out = framework::EigenVector::Flatten(*param_out_tensor); auto moment_out = framework::EigenVector::Flatten(*moment_out_tensor); @@ -57,7 +57,7 @@ class AdagradOpKernel : public framework::OpKernel { moment_out.device(*place) = moment + grad * grad; Eigen::DSizes m_dsize(moment_out_tensor->numel()); param_out.device(*place) = - param - lr.broadcast(m_dsize) * grad / (moment_out.sqrt() + epsilon); + param - lr[0] * grad / (moment_out.sqrt() + epsilon); } else if (grad_var->IsType()) { auto* param_tensor = ctx.Input("Param"); PADDLE_ENFORCE_EQ(param_tensor, param_out_tensor); -- GitLab From c10d5d2aa16b23cc45e01cc399bfa8fbfd39e387 Mon Sep 17 00:00:00 2001 From: peterzhang2029 Date: Thu, 4 Jan 2018 16:50:18 +0800 Subject: [PATCH 0078/2305] refine code style --- paddle/operators/adagrad_op.h | 1 + 1 file changed, 1 insertion(+) diff --git a/paddle/operators/adagrad_op.h b/paddle/operators/adagrad_op.h index 667c1939a2a..be234cf4c40 100644 --- a/paddle/operators/adagrad_op.h +++ b/paddle/operators/adagrad_op.h @@ -47,6 +47,7 @@ class AdagradOpKernel : public framework::OpKernel { *ctx.Input("Grad")); auto moment = framework::EigenVector::Flatten( *ctx.Input("Moment")); + auto* learning_rate = ctx.Input("LearningRate"); auto* lr = learning_rate->data(); -- GitLab From 4ead8e1b57b2c8eca3bd3fc111d1251c59aa60a4 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Thu, 4 Jan 2018 20:11:19 +0800 Subject: [PATCH 0079/2305] Add doc for error clip --- doc/design/error_clip.md | 100 +++++++++++++++++++++++++++++++++ python/paddle/v2/fluid/clip.py | 35 ++++++++++++ 2 files changed, 135 insertions(+) create mode 100644 doc/design/error_clip.md diff --git a/doc/design/error_clip.md b/doc/design/error_clip.md new file mode 100644 index 00000000000..72ff7f611fc --- /dev/null +++ b/doc/design/error_clip.md @@ -0,0 +1,100 @@ +# Error Clip + +## Overview + +Error clip is widely used in model training to prevent gradient exploding. It takes a value as clip threshold. With error clip, all gradient values will be checked before they are taken by the next `grad_op`, and values greater than the threshold will be clipped. + +## Usage + +Users can enable clip and set related attributes via invoking `Optimizer`'s `minimize` API: + +```python +def minimize(self, + loss, + startup_program=None, + parameter_list=None, + no_grad_set=None, + error_clip=None): + # ... +``` + +The default value of `error_clip` is `None`, which means no error clip is employed. When it's not `None`, it should take an object of `BaseErrorClipAttr`'s derived class. So far, `BaseErrorClipAttr` has only one derived class: `ErrorClipByValue`, whose constructor is: + +```python +ErrorClipByValue(max, min=None) +``` + +`max` and `min` represent the maximal and minimal clip threshold respectively. When the `min` is None, the minimal threshold will be assigned with `-max`. + +So we can enable the error clip with threshold `[-5.0, 5.0]` by: + +```python +opt = fluid.optimizer.SGD(learning_rate=0.001) +opt.minimize(loss=avg_cost, error_clip=ErrorClipByValue(max=5.0)) +``` + +## Implementation + +The `BaseErrorClipAttr` and its derived class `ErrorClipByValue` are defined in *clip.py*. + +```python +class BaseErrorClipAttr(object): + def create_clip_op_desc(self, grad_name): + raise NotImplementedError() + + def prepend_clip_op_desc(self, op_descs): + grad_names = set() + for op_desc in op_descs: + grad_names.update(filter(lambda n: n.find( + core.grad_var_suffix()) != -1, op_desc.output_arg_names())) + for n in grad_names: + op_descs.append(self.create_clip_op_desc(grad_name=n)) + + +class ErrorClipByValue(BaseErrorClipAttr): + def __init__(self, max, min=None): + max = float(max) + if min is None: + min = -max + else: + min = float(min) + self.max = max + self.min = min + + def create_clip_op_desc(self, grad_name): + desc = core.OpDesc() + desc.set_type("clip") + desc.set_input("X", grad_name) + desc.set_output("Out", grad_name) + desc.set_attr("min", self.min) + desc.set_attr("max", self.max) + return desc +``` + +The `BaseErrorClipAttr` have two main member functions: + +- **`create_clip_op_desc(self, grad_name)`** + +> This function is used to create a C++ `OpDesc` object of `clip_op` and return its pointer to Python. For different error clips require different `clip_op`, the function is defined as virtual in the base class. All derived classes must implement their own versions of this function. + +- **`prepend_clip_op_desc(self, op_descs)`** + +> This function takes a list of C++ `OpDesc` as input. It checks each `OpDesc` in the list, creates `clip_op`s for every gradient outputs and then appends them to the input list. The input `op_descs` is supposed to be the backward of a certain forward op. It can contain one or more `OpDesc`s (Some op's backward is a combination of several other ops). + +This two functions take effort during the backward building. Just as we showed in the *Usage* section, `Optimizer`'s `minimize` function can take an object of `ErrorClipByValue`(or some other `BaseErrorClipAttr`'s derived class). Inside the `minimize` function, the `prepend_clip_op_desc` function will be send to backward building process as an callback function: + +```python +params_grads = append_backward(loss=loss, + parameter_list=parameter_list, + no_grad_set=no_grad_set, + callback=error_clip.prepend_clip_op_desc) +``` + +Each time we get the backward of a forward op, we invoke the callback function to append `clip_op` for all the new generated gradients(In the `_append_backward_ops_` function of *backward.py*): + +```python +grad_op_desc, op_grad_to_var = core.get_grad_op_desc( + op.desc, no_grad_dict[block.idx], grad_sub_block_list) +if callback is not None: + grad_op_desc = callback(grad_op_desc) +``` diff --git a/python/paddle/v2/fluid/clip.py b/python/paddle/v2/fluid/clip.py index d7ec2fbe13f..89972b8346f 100644 --- a/python/paddle/v2/fluid/clip.py +++ b/python/paddle/v2/fluid/clip.py @@ -1,9 +1,44 @@ import functools import layers +from . import core __all__ = ['GradientClipByValue', 'append_gradient_clip_ops'] +class BaseErrorClipAttr(object): + def create_clip_op_desc(self, grad_name): + raise NotImplementedError() + + def prepend_clip_op_desc(self, op_descs): + grad_names = set() + for op_desc in op_descs: + grad_names.update( + filter(lambda n: n.find(core.grad_var_suffix()) != -1, + op_desc.output_arg_names())) + for n in grad_names: + op_descs.append(self.create_clip_op_desc(grad_name=n)) + + +class ErrorClipByValue(BaseErrorClipAttr): + def __init__(self, max, min=None): + max = float(max) + if min is None: + min = -max + else: + min = float(min) + self.max = max + self.min = min + + def create_clip_op_desc(self, grad_name): + desc = core.OpDesc() + desc.set_type("clip") + desc.set_input("X", grad_name) + desc.set_output("Out", grad_name) + desc.set_attr("min", self.min) + desc.set_attr("max", self.max) + return desc + + class BaseGradientClipAttr(object): def process_context(self, context, p_g): raise NotImplementedError() -- GitLab From ebc616b0f9bb72d17668478b70a0df8a0a979771 Mon Sep 17 00:00:00 2001 From: emailweixu Date: Thu, 4 Jan 2018 11:08:07 -0800 Subject: [PATCH 0080/2305] Fix comment to layers.fc x_num_col_dims => num_flatten_dims --- python/paddle/v2/fluid/layers/nn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 1c1c09dd28b..417afbecae2 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -64,14 +64,14 @@ def fc(input, is flattened: the first `num_flatten_dims` dimensions will be flatten to form the first dimension of the final matrix (height of the - matrix), and the rest `rank(X) - num_col_dims` + matrix), and the rest `rank(X) - num_flatten_dims` dimensions are flattened to form the second dimension of the final matrix (width of the matrix). For example, suppose `X` is a 6-dimensional tensor with a shape [2, 3, 4, 5, 6], and - `x_num_col_dims` = 3. Then, the flattened matrix + `num_flatten_dims` = 3. Then, the flattened matrix will have a shape [2 x 3 x 4, 5 x 6] = [24, 30]. - By default, `x_num_col_dims` is set to 1. + By default, `num_flatten_dims` is set to 1. param_attr(ParamAttr|list): The parameter attribute for learnable parameters/weights of the fully connected layer. -- GitLab From 0f4410755fa2fd333aaf1f009388773b0c7bbd6c Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Fri, 5 Jan 2018 03:38:12 +0000 Subject: [PATCH 0081/2305] Confirm the contents in profiling report --- paddle/platform/profiler.cc | 52 ++++++++++++++++++++++---------- paddle/platform/profiler.h | 1 + paddle/platform/profiler_test.cc | 6 ++++ 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index 64b8bd1485a..f11c87f4df8 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -3,7 +3,7 @@ licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at -` + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/platform/profiler.h" +#include #include namespace paddle { @@ -183,7 +184,8 @@ void PopEvent(const std::string& name, DeviceContext* dev_ctx) { void ParseEvents(std::vector>& events) { // Event name :: counts :: ave :: min :: max :: total - std::map> events_table; + std::map> + events_table; for (size_t i = 0; i < events.size(); i++) { std::list pushed_events; for (size_t j = 0; j < events[i].size(); j++) { @@ -197,18 +199,28 @@ void ParseEvents(std::vector>& events) { ++rit; } if (rit != pushed_events.rend()) { - Event pushed_event = *rit; - double cpu_time = rit->CpuElapsedUs(events[i][j]); - double cuda_time = 0; #ifdef PADDLE_WITH_CUDA - cuda_time = rit->CudaElapsedUs(events[i][j]); + double event_time = rit->CudaElapsedUs(events[i][j]); +#else + double event_time = rit->CpuElapsedUs(events[i][j]); #endif - if (events_table.find(rit->name()) == events_table.end()) { - events_table[rit->name()] = std::make_tuple(1, cpu_time, cuda_time); + std::string event_name = + "thread" + std::to_string(rit->thread_id()) + "::" + rit->name(); + if (events_table.find(event_name) == events_table.end()) { + events_table[event_name] = + std::make_tuple(1, event_time, event_time, event_time, 0); } else { - std::get<0>(events_table[rit->name()]) += 1; - std::get<1>(events_table[rit->name()]) += cpu_time; - std::get<2>(events_table[rit->name()]) += cuda_time; + std::get<0>(events_table[event_name]) += 1; + // total time + std::get<1>(events_table[event_name]) += event_time; + // min time + if (std::get<2>(events_table[event_name]) > event_time) { + std::get<2>(events_table[event_name]) = event_time; + } + // max time + if (std::get<3>(events_table[event_name]) < event_time) { + std::get<3>(events_table[event_name]) = event_time; + } } // remove the start marker from the list pushed_events.erase((++rit).base()); @@ -220,13 +232,21 @@ void ParseEvents(std::vector>& events) { } } // output events table - std::cout << "\nEvents\t\tCalls\t\tTotal CPU time\t\tTotal GPU time\n"; - for (std::map>::iterator it = + std::cout << std::setw(20) << "Events" << std::setw(10) << "Calls" + << std::setw(10) << "Total" << std::setw(10) << "Min" + << std::setw(10) << "Max" << std::setw(10) << "Ave" << std::endl; + for (std::map>::iterator it = events_table.begin(); it != events_table.end(); ++it) { - std::cout << it->first << "\t\t" << std::get<0>(it->second) << "\t\t" - << std::get<1>(it->second) << "\t\t" << std::get<2>(it->second) - << std::endl; + // average time + std::get<4>(it->second) = std::get<1>(it->second) / std::get<0>(it->second); + std::cout << std::setw(20) << it->first << std::setw(10) + << std::get<0>(it->second) << std::setw(10) + << std::get<1>(it->second) << std::setw(10) + << std::get<2>(it->second) << std::setw(10) + << std::get<3>(it->second) << std::setw(10) + << std::get<4>(it->second) << std::endl; } } diff --git a/paddle/platform/profiler.h b/paddle/platform/profiler.h index eb36355f86d..eb176421a92 100644 --- a/paddle/platform/profiler.h +++ b/paddle/platform/profiler.h @@ -33,6 +33,7 @@ class Event { std::string kind() const; std::string name() const { return name_; } + uint32_t thread_id() const { return thread_id_; } bool has_cuda() const { return has_cuda_; } #ifdef PADDLE_WITH_CUDA diff --git a/paddle/platform/profiler_test.cc b/paddle/platform/profiler_test.cc index ff5e658f5bd..7966b35a16e 100644 --- a/paddle/platform/profiler_test.cc +++ b/paddle/platform/profiler_test.cc @@ -94,6 +94,12 @@ TEST(RecordEvent, RecordEvent) { int counter = 1; while (counter != i * 1000) counter++; } + for (int i = 1; i < 5; ++i) { + std::string name = "evs_op_" + std::to_string(i); + RecordEvent record_event(name, dev_ctx); + int counter = 1; + while (counter != i * 1000) counter++; + } std::vector> events = paddle::platform::DisableProfiler(); int cuda_startup_count = 0; int start_profiler_count = 0; -- GitLab From 2d94eca8a1f707830cabb6eb0afe8879c376ac5c Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Fri, 5 Jan 2018 05:45:45 +0000 Subject: [PATCH 0082/2305] Format profiling report --- paddle/platform/profiler.cc | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index f11c87f4df8..52312d5a576 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -186,6 +186,7 @@ void ParseEvents(std::vector>& events) { // Event name :: counts :: ave :: min :: max :: total std::map> events_table; + size_t max_name_width = 0; for (size_t i = 0; i < events.size(); i++) { std::list pushed_events; for (size_t j = 0; j < events[i].size(); j++) { @@ -206,6 +207,7 @@ void ParseEvents(std::vector>& events) { #endif std::string event_name = "thread" + std::to_string(rit->thread_id()) + "::" + rit->name(); + max_name_width = std::max(max_name_width, event_name.size()); if (events_table.find(event_name) == events_table.end()) { events_table[event_name] = std::make_tuple(1, event_time, event_time, event_time, 0); @@ -232,21 +234,24 @@ void ParseEvents(std::vector>& events) { } } // output events table - std::cout << std::setw(20) << "Events" << std::setw(10) << "Calls" - << std::setw(10) << "Total" << std::setw(10) << "Min" - << std::setw(10) << "Max" << std::setw(10) << "Ave" << std::endl; + std::cout.setf(std::ios::left); + const int data_width = 12; + std::cout << std::setw(max_name_width + 4) << "Event" << std::setw(data_width) + << "Calls" << std::setw(data_width) << "Total" + << std::setw(data_width) << "Min." << std::setw(data_width) + << "Max." << std::setw(data_width) << "Ave." << std::endl; for (std::map>::iterator it = events_table.begin(); it != events_table.end(); ++it) { // average time std::get<4>(it->second) = std::get<1>(it->second) / std::get<0>(it->second); - std::cout << std::setw(20) << it->first << std::setw(10) - << std::get<0>(it->second) << std::setw(10) - << std::get<1>(it->second) << std::setw(10) - << std::get<2>(it->second) << std::setw(10) - << std::get<3>(it->second) << std::setw(10) - << std::get<4>(it->second) << std::endl; + std::cout << std::setw(max_name_width + 4) << it->first + << std::setw(data_width) << std::get<0>(it->second) + << std::setw(data_width) << std::get<1>(it->second) + << std::setw(data_width) << std::get<2>(it->second) + << std::setw(data_width) << std::get<3>(it->second) + << std::setw(data_width) << std::get<4>(it->second) << std::endl; } } -- GitLab From 8496b2e41b2654a0904d2d46b547bea7c1df1eca Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Fri, 5 Jan 2018 14:32:55 +0800 Subject: [PATCH 0083/2305] Refine parallel_do --- paddle/framework/lod_tensor.cc | 8 +++---- paddle/operators/parallel_do_op.cc | 38 ++++++++++++++++-------------- python/paddle/v2/fluid/backward.py | 1 + 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index 6853b7ee5f9..ef85ed69dbe 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -270,10 +270,10 @@ std::vector LoDTensor::SplitLoDTensor( "Batch size should be divided by places size"); std::vector lods; - for (int place_idx = 0; place_idx < places.size(); ++place_idx) { - int begin = place_idx * dims()[0] / places.size(); - int end = (place_idx + 1) * dims()[0] / places.size(); - auto src = Slice(begin, end); + for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { + size_t begin = place_idx * dims()[0] / places.size(); + size_t end = (place_idx + 1) * dims()[0] / places.size(); + auto src = Slice(static_cast(begin), static_cast(end)); LoDTensor dst; dst.Resize(src.dims()); diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index 348356f28d6..077245cd83b 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -12,23 +12,23 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include #include #include "paddle/framework/executor.h" #include "paddle/framework/op_registry.h" +#include "paddle/framework/threadpool.h" namespace paddle { namespace operators { -constexpr char kInputs[] = "inputs"; -constexpr char kParameters[] = "parameters"; -constexpr char kPlaces[] = "places"; +static constexpr char kInputs[] = "inputs"; +static constexpr char kParameters[] = "parameters"; +static constexpr char kPlaces[] = "places"; -constexpr char kOutputs[] = "outputs"; -constexpr char kParallelScopes[] = "parallel_scopes"; +static constexpr char kOutputs[] = "outputs"; +static constexpr char kParallelScopes[] = "parallel_scopes"; -constexpr char kParallelBlock[] = "sub_block"; +static constexpr char kParallelBlock[] = "sub_block"; // using ParallelScopeVar = std::vector; using LoDTensor = framework::LoDTensor; @@ -85,7 +85,8 @@ class ParallelDoOp : public framework::OperatorBase { SplitTensorAndMoveTensorToScopes(scope, sub_scopes, places, Inputs(kInputs)); - std::vector workers; + std::vector> workers; + workers.reserve(places.size()); for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { VLOG(3) << "Run " << place_idx; @@ -93,26 +94,27 @@ class ParallelDoOp : public framework::OperatorBase { auto *cur_scope = sub_scopes[place_idx]; // copy parameter - if (dev_ctx.GetPlace() != place) { + // some version of boost lacks != for boost::variant + if (!(dev_ctx.GetPlace() == place)) { PADDLE_THROW("Not Implemented"); } - // execute - workers.push_back(std::thread([program, cur_scope, place, block] { - auto executor = framework::Executor(place); + workers.emplace_back(framework::Async([program, cur_scope, place, block] { + framework::Executor executor(place); executor.Run(*program, cur_scope, block->ID(), false /*create_local_scope*/); })); } for (auto &worker : workers) { - worker.join(); + worker.wait(); } // merge output for (auto &o_name : Outputs(kOutputs)) { std::vector lod_tensors; + lod_tensors.reserve(sub_scopes.size()); for (auto *sub_scope : sub_scopes) { - lod_tensors.push_back(&sub_scope->FindVar(o_name)->Get()); + lod_tensors.emplace_back(&sub_scope->FindVar(o_name)->Get()); } auto *lod_tensor_to_be_merged = @@ -177,7 +179,7 @@ class ParallelDoGradOp : public OperatorBase { } // exe run - std::vector workers; + std::vector> workers; for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { VLOG(3) << "Run " << place_idx; @@ -185,14 +187,14 @@ class ParallelDoGradOp : public OperatorBase { auto *cur_scope = sub_scopes[place_idx]; // execute - workers.push_back(std::thread([program, cur_scope, place, block] { - auto executor = framework::Executor(place); + workers.emplace_back(framework::Async([program, cur_scope, place, block] { + framework::Executor executor(place); executor.Run(*program, cur_scope, block->ID(), false /*create_local_scope*/); })); } for (auto &worker : workers) { - worker.join(); + worker.wait(); } // merge grad diff --git a/python/paddle/v2/fluid/backward.py b/python/paddle/v2/fluid/backward.py index ac60bf54360..88fe19da5e2 100644 --- a/python/paddle/v2/fluid/backward.py +++ b/python/paddle/v2/fluid/backward.py @@ -205,6 +205,7 @@ def _append_backward_ops_(target, # Getting op's corresponding grad_op grad_op_desc, op_grad_to_var = core.get_grad_op_desc( op.desc, no_grad_dict[block.idx], grad_sub_block_list) + grad_op_descs.extend(grad_op_desc) grad_to_var.update(op_grad_to_var) -- GitLab From 9c7cea81c8407ace7db46d781df5123fce60bd66 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 5 Jan 2018 14:50:27 +0800 Subject: [PATCH 0084/2305] follow comments, use unique_ptr and remove unused file --- paddle/operators/tensor.save | Bin 466 -> 0 bytes paddle/platform/device_context.cc | 4 ++-- paddle/platform/device_context.h | 6 +++--- paddle/platform/mkldnn_helper.h | 9 +++++---- 4 files changed, 10 insertions(+), 9 deletions(-) delete mode 100644 paddle/operators/tensor.save diff --git a/paddle/operators/tensor.save b/paddle/operators/tensor.save deleted file mode 100644 index de6cd5a66a26e9e8f2bee245cf17ec190c5c1e05..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 466 zcmYMqg-%035CzbsSSj{!hvM$;?(XjY{|}rckWO+YlRGzr5b}tV{oT3spQrw!{M;)P zxznj6lVoBTo3Rxe1r!n+#iS`A7E39koC+#QsG^!0YN?~11{!IinHE}Uqn!@2bkapP zJ@nE?KLZRh#4sa_GR8O)OftnZGt4r_JPRzc#4;h9qTyn)VH{5c^Jr6wc#4|6v^2R$KeDcLNKi(h{!T(op_key)) { return; } - GetElementPool().emplace(op_key, value); + GetElementPool().emplace(op_key, std::move(value)); } template -const T MKLDNNDeviceContext::GetElement(const std::string& op_key) const { +const T& MKLDNNDeviceContext::GetElement(const std::string& op_key) const { auto it = GetElementPool().find(op_key); return it == GetElementPool().end() ? nullptr : it->second; } diff --git a/paddle/platform/device_context.h b/paddle/platform/device_context.h index 44f3ac1d392..21a5ed1835a 100644 --- a/paddle/platform/device_context.h +++ b/paddle/platform/device_context.h @@ -132,7 +132,7 @@ class MKLDNNDeviceContext : public CPUDeviceContext { /* \brief Get existed element: memory, primitive or primitive desc */ template - const T GetElement(const std::string& op_key) const; + const T& GetElement(const std::string& op_key) const; /* \brief Get element pool: memory, primitive or primitive desc pool */ template @@ -140,7 +140,7 @@ class MKLDNNDeviceContext : public CPUDeviceContext { GetElementPool() const; /* \brief Get the active engine */ - const MKLDNNEnginePtr GetEngine() const { return engine_; } + const MKLDNNEngine& engine() const { return *engine_; } /* \brief Submit primitive to pipeline */ void Submit(const MKLDNNPrimitivePtr& p) { pipeline_.push_back(*p); } @@ -163,7 +163,7 @@ class MKLDNNDeviceContext : public CPUDeviceContext { std::hash> primitive_desc_pool_; std::vector pipeline_; - std::unique_ptr stream_; + MKLDNNStreamPtr stream_; MKLDNNEnginePtr engine_; bool ready_; }; diff --git a/paddle/platform/mkldnn_helper.h b/paddle/platform/mkldnn_helper.h index 07a9c362c57..cd52a8b4c43 100644 --- a/paddle/platform/mkldnn_helper.h +++ b/paddle/platform/mkldnn_helper.h @@ -25,10 +25,11 @@ using MKLDNNMemory = mkldnn::memory; using MKLDNNPrimitive = mkldnn::primitive; using MKLDNNPrimitiveDesc = mkldnn::handle; -typedef std::shared_ptr MKLDNNEnginePtr; -typedef std::shared_ptr MKLDNNMemoryPtr; -typedef std::shared_ptr MKLDNNPrimitivePtr; -typedef std::shared_ptr MKLDNNPrimitiveDescPtr; +typedef std::unique_ptr MKLDNNStreamPtr; +typedef std::unique_ptr MKLDNNEnginePtr; +typedef std::unique_ptr MKLDNNMemoryPtr; +typedef std::unique_ptr MKLDNNPrimitivePtr; +typedef std::unique_ptr MKLDNNPrimitiveDescPtr; } // namespace platform } // namespace paddle -- GitLab From dd09d8b2bfe68ff3a0df6a95fc6fd751434fa9cf Mon Sep 17 00:00:00 2001 From: peterzhang2029 Date: Fri, 5 Jan 2018 15:08:48 +0800 Subject: [PATCH 0085/2305] fix gpu error --- paddle/operators/adagrad_op.h | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/paddle/operators/adagrad_op.h b/paddle/operators/adagrad_op.h index be234cf4c40..66f5b0f449a 100644 --- a/paddle/operators/adagrad_op.h +++ b/paddle/operators/adagrad_op.h @@ -47,9 +47,7 @@ class AdagradOpKernel : public framework::OpKernel { *ctx.Input("Grad")); auto moment = framework::EigenVector::Flatten( *ctx.Input("Moment")); - auto* learning_rate = ctx.Input("LearningRate"); - auto* lr = learning_rate->data(); auto param_out = framework::EigenVector::Flatten(*param_out_tensor); auto moment_out = framework::EigenVector::Flatten(*moment_out_tensor); @@ -57,8 +55,16 @@ class AdagradOpKernel : public framework::OpKernel { moment_out.device(*place) = moment + grad * grad; Eigen::DSizes m_dsize(moment_out_tensor->numel()); - param_out.device(*place) = - param - lr[0] * grad / (moment_out.sqrt() + epsilon); + if (platform::is_cpu_place(ctx.GetPlace())) { + auto* lr = learning_rate->data(); + param_out.device(*place) = + param - lr[0] * grad / (moment_out.sqrt() + epsilon); + } else { + auto lr = framework::EigenVector::Flatten(*learning_rate); + param_out.device(*place) = + param - + lr.broadcast(m_dsize) * grad / (moment_out.sqrt() + epsilon); + } } else if (grad_var->IsType()) { auto* param_tensor = ctx.Input("Param"); PADDLE_ENFORCE_EQ(param_tensor, param_out_tensor); -- GitLab From 5593858dd95a5921fbd938fcc8d7bb8003a2fdbf Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Fri, 5 Jan 2018 15:19:11 +0800 Subject: [PATCH 0086/2305] Feature/use cudnn (#7141) * "add c++ side kernel selection" * "add multiple kernel op test" * "kernel selection only support cudnn" * "better formatter" * "small fix with UseCPU" * "depends on change interface Get(Place, Library)" * "fix CI" * "fix python cudnn test" * "leave the register cudnn op to another PR" * "fix CI" * "use all kernel by default" * "fix CI" --- paddle/framework/CMakeLists.txt | 3 +- paddle/framework/data_transform.cc | 24 ++++ paddle/framework/init.cc | 3 +- paddle/framework/op_registry_test.cc | 135 +++++++++++++++++- paddle/framework/operator.cc | 95 ++++++++++-- paddle/framework/operator.h | 32 ++++- paddle/operators/conv_cudnn_op.cu.cc | 5 +- paddle/operators/conv_op.h | 13 ++ paddle/pybind/const_value.cc | 5 - paddle/pybind/pybind.cc | 6 + python/paddle/v2/fluid/framework.py | 4 - .../paddle/v2/fluid/tests/test_conv2d_op.py | 8 ++ 12 files changed, 301 insertions(+), 32 deletions(-) diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt index fb8c9ab96d3..528e45b5109 100644 --- a/paddle/framework/CMakeLists.txt +++ b/paddle/framework/CMakeLists.txt @@ -73,8 +73,7 @@ cc_test(var_type_inference_test SRCS var_type_inference_test.cc DEPS op_registry cc_library(selected_rows SRCS selected_rows.cc DEPS tensor) cc_test(selected_rows_test SRCS selected_rows_test.cc DEPS selected_rows) - -cc_library(init SRCS init.cc DEPS gflags device_context place stringpiece) +cc_library(init SRCS init.cc DEPS gflags device_context place stringpiece operator) cc_test(init_test SRCS init_test.cc DEPS init) cc_test(op_kernel_type_test SRCS op_kernel_type_test.cc DEPS place device_context framework_proto) diff --git a/paddle/framework/data_transform.cc b/paddle/framework/data_transform.cc index ac6e40a3ae8..6b178096886 100644 --- a/paddle/framework/data_transform.cc +++ b/paddle/framework/data_transform.cc @@ -37,6 +37,28 @@ auto KernelNHWC = OpKernelType(proto::DataType::FP64, platform::CPUPlace(), auto KernelNCHW = OpKernelType(proto::DataType::FP64, platform::CPUPlace(), DataLayout::kNCHW, LibraryType::kPlain); +// TODO(dzhwinter): Only for testing multiple op kernel. +// Dummy transform function for library_type +// should be removed. +auto KernelPlain = OpKernelType(proto::DataType::FP32, platform::CUDAPlace(0), + DataLayout::kAnyLayout, LibraryType::kPlain); + +auto KernelCUDNN = OpKernelType(proto::DataType::FP32, platform::CUDAPlace(0), + DataLayout::kAnyLayout, LibraryType::kCUDNN); + +void DummyTrans(const platform::DeviceContext* ctx, + const KernelTypePair& kernel_pair, const Variable& in, + Variable* out) { + PADDLE_ENFORCE(in.IsType(), "Only Support Tensor transform!."); + PADDLE_ENFORCE( + platform::places_are_same_class(kernel_pair.first.place_, + kernel_pair.second.place_), + "TransDataType Only Support DataType transform on same place!"); + auto src = in.Get(); + auto* dst = out->GetMutable(); + *dst = src; +} + void TransDataType(const platform::DeviceContext* ctx, const KernelTypePair& kernel_pair, const Variable& in, Variable* out) { @@ -121,6 +143,8 @@ std::vector NCHW2NHWC = {0, 2, 3, 1}; } REGISTER_DATA_TRANSFORM_FN(f::KernelFP32, f::KernelFP64, f::TransDataType); +REGISTER_DATA_TRANSFORM_FN(f::KernelPlain, f::KernelCUDNN, f::DummyTrans); +REGISTER_DATA_TRANSFORM_FN(f::KernelCUDNN, f::KernelPlain, f::DummyTrans); REGISTER_DATA_TRANSFORM_FN(f::KernelNHWC, f::KernelNCHW, std::bind(f::TransDataLayout, NHWC2NCHW, std::placeholders::_1, diff --git a/paddle/framework/init.cc b/paddle/framework/init.cc index 3bea8f3d0a3..7ec8d18b0e8 100644 --- a/paddle/framework/init.cc +++ b/paddle/framework/init.cc @@ -15,6 +15,7 @@ limitations under the License. */ #include #include "paddle/framework/init.h" +#include "paddle/framework/operator.h" #include "paddle/platform/device_context.h" #include "paddle/platform/place.h" #include "paddle/string/piece.h" @@ -24,7 +25,6 @@ namespace framework { std::once_flag gflags_init_flag; -// TODO(qijun) move init gflags to init.cc void InitGflags(std::vector &argv) { std::call_once(gflags_init_flag, [&]() { int argc = argv.size(); @@ -72,6 +72,7 @@ bool InitDevices(const std::vector &devices) { LOG(WARNING) << "Not specified CPU device, create CPU by Default."; } platform::DeviceContextPool::Init(places); + framework::UseALL(); return true; } diff --git a/paddle/framework/op_registry_test.cc b/paddle/framework/op_registry_test.cc index cef530c6e63..a286925bbe4 100644 --- a/paddle/framework/op_registry_test.cc +++ b/paddle/framework/op_registry_test.cc @@ -12,13 +12,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -#include "paddle/framework/op_registry.h" +#include #include +#include "paddle/framework/op_registry.h" + namespace pd = paddle::framework; namespace paddle { namespace framework { + class CosineOp : public OperatorBase { public: using OperatorBase::OperatorBase; @@ -252,7 +255,6 @@ TEST(OperatorRegistrar, CPU) { op->Run(scope, cpu_place); } -#ifdef PADDLE_WITH_CUDA TEST(OperatorRegistrar, CUDA) { paddle::framework::proto::OpDesc op_desc; paddle::platform::CUDAPlace cuda_place(0); @@ -263,4 +265,131 @@ TEST(OperatorRegistrar, CUDA) { op->Run(scope, cuda_place); } -#endif + +static int op_test_value = 0; + +using paddle::platform::DeviceContext; +using paddle::platform::CPUDeviceContext; +using paddle::platform::CUDADeviceContext; + +namespace paddle { +namespace framework { + +class OpWithMultiKernelTest : public OperatorWithKernel { + public: + using OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(InferShapeContext* ctx) const override {} + + framework::OpKernelType GetActualKernelType( + const framework::ExecutionContext& ctx) const override { + return framework::OpKernelType(proto::DataType::FP32, ctx.device_context()); + } + + framework::OpKernelType GetExpectedKernelType( + const framework::OpKernelType& kernel) const override { + return framework::OpKernelType(kernel.data_type_, platform::CUDAPlace(0), + kernel.data_layout_, + framework::LibraryType::kCUDNN); + } +}; + +template +class OpMultiKernelTest : public paddle::framework::OpKernel { + public: + void Compute(const paddle::framework::ExecutionContext& ctx) const; +}; + +template +class OpMultiKernelTest + : public paddle::framework::OpKernel { + public: + void Compute(const paddle::framework::ExecutionContext& ctx) const { + ++op_test_value; + } +}; + +template +class OpMultiKernelTest + : public paddle::framework::OpKernel { + public: + void Compute(const paddle::framework::ExecutionContext& ctx) const { + --op_test_value; + } +}; + +template +class OpMultiKernelTest2 : public paddle::framework::OpKernel { + public: + void Compute(const paddle::framework::ExecutionContext& ctx) const; +}; + +template +class OpMultiKernelTest2 + : public paddle::framework::OpKernel { + public: + void Compute(const paddle::framework::ExecutionContext& ctx) const { + op_test_value += 10; + } +}; + +template +class OpMultiKernelTest2 + : public paddle::framework::OpKernel { + public: + void Compute(const paddle::framework::ExecutionContext& ctx) const { + op_test_value -= 10; + } +}; + +} // namespace framework +} // namespace paddle + +REGISTER_OP_WITHOUT_GRADIENT(op_with_multi_kernel, + paddle::framework::OpWithMultiKernelTest, + paddle::framework::OpKernelTestMaker); +REGISTER_OP_KERNEL( + op_with_multi_kernel, CPU, paddle::platform::CPUPlace, + paddle::framework::OpMultiKernelTest); +REGISTER_OP_KERNEL( + op_with_multi_kernel, MKLDNN, paddle::platform::CPUPlace, + paddle::framework::OpMultiKernelTest2); +REGISTER_OP_KERNEL( + op_with_multi_kernel, CUDA, paddle::platform::CUDAPlace, + paddle::framework::OpMultiKernelTest); +REGISTER_OP_KERNEL( + op_with_multi_kernel, CUDNN, paddle::platform::CUDAPlace, + paddle::framework::OpMultiKernelTest2); + +TEST(OperatorRegistrar, OpWithMultiKernel) { + paddle::framework::proto::OpDesc op_desc; + paddle::platform::CUDAPlace cuda_place(0); + paddle::platform::CPUPlace cpu_place; + paddle::framework::Scope scope; + + op_desc.set_type("op_with_multi_kernel"); + auto op = paddle::framework::OpRegistry::CreateOp(op_desc); + + // use all available kernels + paddle::framework::UseALL(); + op->Run(scope, cuda_place); + EXPECT_EQ(op_test_value, -10); + + // remove cuda kernels + paddle::framework::UseCPU(); + op->Run(scope, cpu_place); + + EXPECT_EQ(op_test_value, -9); + + // add cuda kernels + paddle::framework::UseCUDA(); + op->Run(scope, cuda_place); + + EXPECT_EQ(op_test_value, -10); + + // use cudnn kernel + paddle::framework::UseCUDNN(); + op->Run(scope, cuda_place); + EXPECT_EQ(op_test_value, -20); +} diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index fc7091f1c89..70a9c4b5554 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -11,6 +11,7 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +#include #include #include @@ -25,6 +26,53 @@ limitations under the License. */ namespace paddle { namespace framework { +std::vector> kKernelPriority; + +void UseCPU() { + kKernelPriority.clear(); + /*Plain CPU*/ + auto pair0 = std::make_tuple(platform::CPUPlace(), LibraryType::kPlain); + kKernelPriority.insert(kKernelPriority.begin(), pair0); +} + +void UseMKLDNN() { + UseCPU(); +#if PADDLE_WITH_MKLML + { + /*MKLDNN Kernel*/ + auto pair0 = std::make_tuple(platform::CPUPlace(), LibraryType::kMKLDNN); + kKernelPriority.insert(kKernelPriority.begin(), pair0); + } +#endif +} + +void UseCUDA() { + UseMKLDNN(); +#if PADDLE_WITH_CUDA + /*Plain GPU*/ + auto pair0 = std::make_tuple(platform::CUDAPlace(0), LibraryType::kPlain); + kKernelPriority.insert(kKernelPriority.begin(), pair0); +#endif +} + +void UseCUDNN() { + UseCUDA(); +#if PADDLE_WITH_CUDA + if (platform::dynload::HasCUDNN()) { + /*CUDNN Kernel*/ + auto pair0 = std::make_tuple(platform::CUDAPlace(0), LibraryType::kCUDNN); + kKernelPriority.insert(kKernelPriority.begin(), pair0); + } +#endif +} + +void UseALL() { + UseCPU(); + UseMKLDNN(); + UseCUDA(); + UseCUDNN(); +} + std::string OperatorBase::Input(const std::string& name) const { auto& ins = Inputs(name); PADDLE_ENFORCE_LE(ins.size(), 1UL, @@ -402,6 +450,12 @@ const platform::DeviceContext* GetDeviceContext( } } +const platform::DeviceContext* GetDeviceContext( + const framework::OpKernelType& kernel) { + platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance(); + return pool.Get(kernel.place_); +} + void OperatorWithKernel::Run(const Scope& scope, const platform::Place& place) const { RuntimeInferShapeContext infer_shape_ctx(*this, scope); @@ -422,13 +476,8 @@ void OperatorWithKernel::Run(const Scope& scope, ExecutionContext ctx(*this, scope, *dev_ctx); auto actual_kernel_key = GetActualKernelType(ctx); - auto expected_kernel_key = GetExpectedKernelType(actual_kernel_key); - auto kernel_iter = kernels.find(expected_kernel_key); - if (kernel_iter == kernels.end()) { - PADDLE_THROW("The operator %s does not support %s", type_, - expected_kernel_key); - } + auto expected_kernel_key = GetExpectedKernelType(actual_kernel_key); if (actual_kernel_key == expected_kernel_key) { PADDLE_ENFORCE_EQ(actual_kernel_key.place_, expected_kernel_key.place_, @@ -436,9 +485,24 @@ void OperatorWithKernel::Run(const Scope& scope, "CPU and other devices. For example, multi-GPU model " "parallelism will failed."); } else { + // find the best key candidate + const DataTransformFnMap& trans_map = DataTransformFnMap::Instance(); + for (auto& candidate : kKernelPriority) { + auto candidate_key = + OpKernelType(actual_kernel_key.data_type_, std::get<0>(candidate), + actual_kernel_key.data_layout_, std::get<1>(candidate)); + + auto candidate_pair = std::make_pair(actual_kernel_key, candidate_key); + if ((actual_kernel_key == candidate_key) || + (kernels.count(candidate_key) && + trans_map.GetNullable(candidate_pair))) { + expected_kernel_key = candidate_key; + break; + } + } + auto kernel_pair = std::make_pair(actual_kernel_key, expected_kernel_key); - const DataTransformFn* trans_fun = - DataTransformFnMap::Instance().GetNullable(kernel_pair); + const DataTransformFn* trans_fun = trans_map.GetNullable(kernel_pair); if (trans_fun) { auto input_vars = this->InputVars(); // TODO(qijun) filter the input vars that do not need to be transformed @@ -471,7 +535,20 @@ void OperatorWithKernel::Run(const Scope& scope, } } - kernel_iter->second->Compute(ctx); + VLOG(10) << "Actual kernel: " << actual_kernel_key + << "Expected kernel: " << expected_kernel_key; + + auto kernel_iter = kernels.find(expected_kernel_key); + + if (kernel_iter == kernels.end()) { + PADDLE_THROW("The operator %s does not support %s", type_, + expected_kernel_key); + } + + auto* expected_dev_ctx = GetDeviceContext(expected_kernel_key); + ExecutionContext expected_ctx(*this, scope, *expected_dev_ctx); + + kernel_iter->second->Compute(expected_ctx); } OpKernelType OperatorWithKernel::GetActualKernelType( diff --git a/paddle/framework/operator.h b/paddle/framework/operator.h index d0a9b643d56..1f5a4af58c5 100644 --- a/paddle/framework/operator.h +++ b/paddle/framework/operator.h @@ -17,6 +17,7 @@ limitations under the License. */ #include #include #include +#include #include #include @@ -52,10 +53,33 @@ constexpr char kGradVarSuffix[] = "@GRAD"; /// Variables with this suffix are supposed to be filled up with zeros. constexpr char kZeroVarSuffix[] = "@ZERO"; -// define some kernel hint -const std::string kUseCPU = "use_cpu"; -const std::string kUseCUDNN = "use_cudnn"; -const std::string kUseMKLDNN = "use_mkldnn"; +// define some kernel priority +extern std::vector> kKernelPriority; + +/** + * @brief Use cpu kernel only + */ +void UseCPU(); + +/** + * @brief Perfer MKLDNN kernel than Plain CPU kernel + */ +void UseMKLDNN(); + +/** + * @brief Perfer CUDA kernel than Plain CPU kernel + */ +void UseCUDA(); + +/** + * @brief Perfer cudnn kernel than Plain CUDA kernel + */ +void UseCUDNN(); + +/** + * @brief Use all available kernels + */ +void UseALL(); inline std::string GradVarName(const std::string& var_name) { return var_name + kGradVarSuffix; diff --git a/paddle/operators/conv_cudnn_op.cu.cc b/paddle/operators/conv_cudnn_op.cu.cc index 0aa7dd48caf..0c5ed3e4e80 100644 --- a/paddle/operators/conv_cudnn_op.cu.cc +++ b/paddle/operators/conv_cudnn_op.cu.cc @@ -315,10 +315,7 @@ class CudnnConvGradOpKernel : public framework::OpKernel { } // namespace operators } // namespace paddle -REGISTER_OP_KERNEL(conv2d, CUDNN, paddle::platform::CUDAPlace, - paddle::operators::CudnnConvOpKernel, - paddle::operators::CudnnConvOpKernel); - +// TODO(dzhwinter) : below register should be removed REGISTER_OP_CUDA_KERNEL(conv2d_cudnn, paddle::operators::CudnnConvOpKernel, paddle::operators::CudnnConvOpKernel); diff --git a/paddle/operators/conv_op.h b/paddle/operators/conv_op.h index 83786e2329e..fe3c0bc9302 100644 --- a/paddle/operators/conv_op.h +++ b/paddle/operators/conv_op.h @@ -62,12 +62,25 @@ class ConvOp : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; + framework::OpKernelType GetExpectedKernelType( + const framework::OpKernelType& kernel) const override { + return framework::OpKernelType(kernel.data_type_, platform::CUDAPlace(0), + kernel.data_layout_, + framework::LibraryType::kCUDNN); + } }; class ConvOpGrad : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; + + framework::OpKernelType GetExpectedKernelType( + const framework::OpKernelType& kernel) const override { + return framework::OpKernelType(kernel.data_type_, platform::CUDAPlace(0), + kernel.data_layout_, + framework::LibraryType::kCUDNN); + } }; template diff --git a/paddle/pybind/const_value.cc b/paddle/pybind/const_value.cc index 761635aa5e5..b13ad42ea29 100644 --- a/paddle/pybind/const_value.cc +++ b/paddle/pybind/const_value.cc @@ -23,11 +23,6 @@ void BindConstValue(pybind11::module& m) { m.def("kTempVarName", [] { return framework::kTempVarName; }); m.def("kGradVarSuffix", [] { return framework::kGradVarSuffix; }); m.def("kZeroVarSuffix", [] { return framework::kZeroVarSuffix; }); - - // for kernel_hint key - m.def("kUseCPU", [] { return framework::kUseCPU; }); - m.def("kUseCUDNN", [] { return framework::kUseCUDNN; }); - m.def("kUseMKLDNN", [] { return framework::kUseMKLDNN; }); } } // namespace pybind diff --git a/paddle/pybind/pybind.cc b/paddle/pybind/pybind.cc index 364db62cba6..5d170c66e97 100644 --- a/paddle/pybind/pybind.cc +++ b/paddle/pybind/pybind.cc @@ -430,6 +430,12 @@ All parameter, weight, gradient are variables in Paddle. m.def("init_glog", framework::InitGLOG); m.def("init_devices", &framework::InitDevices); + m.def("use_cpu", framework::UseCPU); + m.def("use_mkldnn", framework::UseMKLDNN); + m.def("use_cuda", framework::UseCUDA); + m.def("use_cudnn", framework::UseCUDNN); + m.def("use_all", framework::UseALL); + m.def("is_compile_gpu", IsCompileGPU); m.def("set_feed_variable", framework::SetFeedVariable); m.def("get_fetch_variable", framework::GetFetchVariable); diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index b66a8bce5f4..7340dd23d16 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -17,10 +17,6 @@ TEMP_VAR_NAME = core.kTempVarName() GRAD_VAR_SUFFIX = core.kGradVarSuffix() ZERO_VAR_SUFFIX = core.kZeroVarSuffix() -USE_CPU = core.kUseCPU() -USE_CUDNN = core.kUseMKLDNN() -USE_MKLDNN = core.kUseMKLDNN() - def grad_var_name(var_name): """ diff --git a/python/paddle/v2/fluid/tests/test_conv2d_op.py b/python/paddle/v2/fluid/tests/test_conv2d_op.py index e82e3ab0c9c..958300e655e 100644 --- a/python/paddle/v2/fluid/tests/test_conv2d_op.py +++ b/python/paddle/v2/fluid/tests/test_conv2d_op.py @@ -1,5 +1,7 @@ import unittest import numpy as np + +import paddle.v2.fluid.core as core from op_test import OpTest @@ -47,6 +49,7 @@ def conv2d_forward_naive(input, filter, group, conv_param): class TestConv2dOp(OpTest): def setUp(self): + core.use_cuda() self.init_op_type() self.init_group() self.init_dilation() @@ -167,26 +170,31 @@ class TestWithDilation(TestConv2dOp): #----------------Conv2dCudnn---------------- class TestCudnn(TestConv2dOp): def init_op_type(self): + core.use_cudnn() self.op_type = "conv2d_cudnn" class TestCudnnWithPad(TestWithPad): def init_op_type(self): + core.use_cudnn() self.op_type = "conv2d_cudnn" class TestCudnnWithStride(TestWithStride): def init_op_type(self): + core.use_cudnn() self.op_type = "conv2d_cudnn" class TestCudnnWithGroup(TestWithGroup): def init_op_type(self): + core.use_cudnn() self.op_type = "conv2d_cudnn" class TestCudnnWith1x1(TestWith1x1): def init_op_type(self): + core.use_cudnn() self.op_type = "conv2d_cudnn" -- GitLab From 298977c4e4c8ec8a13a8580c3bde5a7771d91ffe Mon Sep 17 00:00:00 2001 From: wangmeng28 Date: Fri, 5 Jan 2018 15:36:18 +0800 Subject: [PATCH 0087/2305] Fix doc of ParameterAttribute --- python/paddle/trainer_config_helpers/attrs.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/python/paddle/trainer_config_helpers/attrs.py b/python/paddle/trainer_config_helpers/attrs.py index ecba8719104..e6f87ce61b1 100644 --- a/python/paddle/trainer_config_helpers/attrs.py +++ b/python/paddle/trainer_config_helpers/attrs.py @@ -58,12 +58,12 @@ def is_compatible_with(x, Type): class HookAttribute(object): """ - Hook Attribute object. As a member of ParameterAttribute class, the hook is an auxiliary operation that occurs + Hook Attribute object. As a member of ParameterAttribute class, the hook is an auxiliary operation that occurs during training process of a layer with parameters, such as img_conv layer, fc layer. - :param type: Hook type, currently supported types: + :param type: Hook type, currently supported types: 'pruning' : user specify a sparsity_ratio before training started, and the - network will prune the parameters based on the sparsity_ratio. + network will prune the parameters based on the sparsity_ratio. eg: The definition of Hook object can be hk = HookAttribute('pruning', 0.6) The specific usage can be paddle.layer.img_conv(input=img, filter_size=3, num_channels=3, num_filters=64, @@ -71,10 +71,10 @@ class HookAttribute(object): The pruning details can be found https://arxiv.org/pdf/1506.02626.pdf :type type: string - :param sparsity_ratio: Must be specified if hook type is 'pruning', + :param sparsity_ratio: Must be specified if hook type is 'pruning', it represents the ratio of the zero elements to be set by the Parameter. :type sparsity_ratio: float or None - + """ def __init__(self, type, sparsity_ratio=None): @@ -130,10 +130,12 @@ class ParameterAttribute(object): :param sparse_update: Enable sparse update for this parameter. It will enable both local and remote sparse update. :type sparse_update: bool + :param update_hooks: A HookAttribute object. + :type update_hooks: HookAttribute :param initializer: If not None, it should be a callable object which accepts a parameter name and returns numpy array for the initial value of the parameter - :param initializer: callable object + :type initializer: callable object """ def __init__(self, -- GitLab From 0cfb5465cdcb25e821ce690e57c77a68d7e6fc54 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Fri, 5 Jan 2018 16:40:45 +0800 Subject: [PATCH 0088/2305] Add COWPtr and its unittest It will be used for LoD information in LoDTensor since LoD is a copy on write field. It is pretty slow for copying LoD information between operators. For resnet it will cost roughly 10% time of whole time, including reading data. --- paddle/framework/CMakeLists.txt | 1 + paddle/framework/details/cow_ptr.h | 94 ++++++++++++++++++++++++ paddle/framework/details/cow_ptr_test.cc | 39 ++++++++++ 3 files changed, 134 insertions(+) create mode 100644 paddle/framework/details/cow_ptr.h create mode 100644 paddle/framework/details/cow_ptr_test.cc diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt index fb8c9ab96d3..221186d780e 100644 --- a/paddle/framework/CMakeLists.txt +++ b/paddle/framework/CMakeLists.txt @@ -78,3 +78,4 @@ cc_library(init SRCS init.cc DEPS gflags device_context place stringpiece) cc_test(init_test SRCS init_test.cc DEPS init) cc_test(op_kernel_type_test SRCS op_kernel_type_test.cc DEPS place device_context framework_proto) +cc_test(cow_ptr_tests SRCS details/cow_ptr_test.cc) diff --git a/paddle/framework/details/cow_ptr.h b/paddle/framework/details/cow_ptr.h new file mode 100644 index 00000000000..6f1dcab40b5 --- /dev/null +++ b/paddle/framework/details/cow_ptr.h @@ -0,0 +1,94 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#pragma once +#include +#include + +namespace paddle { +namespace framework { +namespace details { + +// Change it to thread safe flags if needed. +class ThreadUnsafeOwnershipFlags { + public: + ThreadUnsafeOwnershipFlags(bool flag) : flag_(flag) {} + + ThreadUnsafeOwnershipFlags(const ThreadUnsafeOwnershipFlags& o) = delete; + ThreadUnsafeOwnershipFlags& operator=(const ThreadUnsafeOwnershipFlags& o) = + delete; + ThreadUnsafeOwnershipFlags(ThreadUnsafeOwnershipFlags&& o) = default; + + void SetOwnership(bool flag) { flag_ = flag; } + + template + void AcquireOwnershipOnce(Callback acquire) { + if (!flag_) { + acquire(); + flag_ = true; + } + } + + private: + bool flag_; +}; + +// Copy On Write pointer. +// It will hold a T* pointer, and only copy once when `MutableData` is invoked. +// +// The template parameter OwnershipFlags should have: +// * a constructor takes a bool. True if own. +// * SetOwnership(bool flag). +// * AcquireOwnershipOnce(Callback). It will invoke the callback if it is not +// owned. +template +class COWPtr { + public: + // Ctor from raw pointer. + explicit COWPtr(T* ptr) : payload_(ptr), ownership_{true} {} + + // Move methods. Steal ownership from origin + COWPtr(COWPtr&& o) + : payload_(o.payload_), ownership_{std::move(o.ownership_)} {} + COWPtr& operator=(COWPtr&& origin) = default; + + // Copy methods. Not own payload + COWPtr(const COWPtr& o) : payload_(o.payload_), ownership_{false} {} + COWPtr& operator=(const COWPtr& o) { + payload_ = o.payload_; + ownership_.SetOwnership(false); + return *this; + } + + const T& Data() const { return *payload_; } + + T* MutableData() { + ownership_.AcquireOwnershipOnce( + [this] { payload_.reset(new T(*payload_)); }); + return payload_.get(); + } + + void Reset() { + ownership_.AcquireOwnershipOnce([this] { payload_.reset(); }); + payload_.reset(new T()); + } + + private: + std::shared_ptr payload_; + OwnershipFlags ownership_; +}; + +} // namespace details +} // namespace framework +} // namespace paddle diff --git a/paddle/framework/details/cow_ptr_test.cc b/paddle/framework/details/cow_ptr_test.cc new file mode 100644 index 00000000000..080a0a0a448 --- /dev/null +++ b/paddle/framework/details/cow_ptr_test.cc @@ -0,0 +1,39 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include "paddle/framework/details/cow_ptr.h" +#include "gtest/gtest.h" + +namespace paddle { +namespace framework { +namespace details { + +TEST(COWPtr, all) { + COWPtr ptr(new int{0}); + ASSERT_EQ(ptr.Data(), 0); + COWPtr ptr2 = ptr; + ASSERT_EQ(ptr2.Data(), 0); + ASSERT_EQ(&ptr2.Data(), &ptr.Data()); + *ptr2.MutableData() = 10; + ASSERT_EQ(ptr.Data(), 0); + ASSERT_EQ(ptr2.Data(), 10); + + auto ptr_before = ptr2.MutableData(); + ptr2.Reset(); + ASSERT_NE(ptr2.MutableData(), ptr_before); +} + +} // namespace details +} // namespace framework +} // namespace paddle -- GitLab From 0aa03a822e17e905b75e2df5a6cadc31c4159cfc Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Fri, 5 Jan 2018 08:54:35 +0000 Subject: [PATCH 0089/2305] Enable sorting the profiling result by different keys --- paddle/platform/profiler.cc | 124 +++++++++++++++++++++++-------- paddle/platform/profiler.h | 17 ++++- paddle/platform/profiler_test.cc | 31 ++++---- 3 files changed, 124 insertions(+), 48 deletions(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index 52312d5a576..a35d6e94f07 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -182,47 +182,88 @@ void PopEvent(const std::string& name, DeviceContext* dev_ctx) { dev_ctx); } -void ParseEvents(std::vector>& events) { - // Event name :: counts :: ave :: min :: max :: total - std::map> - events_table; +void ParseEvents(std::vector>& events, + EventSortingKey sorted_by) { + // Output header information + std::cout << "------------------------->" + << " Profiling Report " + << "<-------------------------" + << "\n\n"; +#ifdef PADDLE_WITH_CUDA + std::cout << "Place: GPU" << std::endl; +#else + std::cout << "Place: CPU" << std::endl; +#endif + std::cout << "Time unit: ms" << std::endl; + std::string sort_domain = "event end time"; + switch (sorted_by) { + case EventSortingKey::kCalls: + sort_domain = "number of calls"; + break; + case EventSortingKey::kTotal: + sort_domain = "total time"; + break; + case EventSortingKey::kMin: + sort_domain = "minimum time"; + break; + case EventSortingKey::kMax: + sort_domain = "maximum time"; + break; + case EventSortingKey::kAve: + sort_domain = "average time"; + break; + default: + if (sorted_by != EventSortingKey::kDefault) { + std::cout << "Warning: unkown sorting key. "; + sorted_by = EventSortingKey::kDefault; + } + } + std::cout << "Sorted by " << sort_domain + << " in descending order in the same thread\n\n"; + + // Parse events + std::vector> events_table; size_t max_name_width = 0; for (size_t i = 0; i < events.size(); i++) { std::list pushed_events; + std::vector event_items; + std::unordered_map event_idx; + for (size_t j = 0; j < events[i].size(); j++) { if (events[i][j].kind() == "push") { pushed_events.push_back(events[i][j]); - } - if (events[i][j].kind() == "pop") { + } else if (events[i][j].kind() == "pop") { std::list::reverse_iterator rit = pushed_events.rbegin(); while (rit->name() != events[i][j].name() && rit != pushed_events.rend()) { ++rit; } if (rit != pushed_events.rend()) { +// get event time in ms #ifdef PADDLE_WITH_CUDA - double event_time = rit->CudaElapsedUs(events[i][j]); + double event_time = rit->CudaElapsedUs(events[i][j]) / 1000.0; #else - double event_time = rit->CpuElapsedUs(events[i][j]); + double event_time = rit->CpuElapsedUs(events[i][j]) / 1000.0; #endif std::string event_name = "thread" + std::to_string(rit->thread_id()) + "::" + rit->name(); max_name_width = std::max(max_name_width, event_name.size()); - if (events_table.find(event_name) == events_table.end()) { - events_table[event_name] = - std::make_tuple(1, event_time, event_time, event_time, 0); + if (event_idx.find(event_name) == event_idx.end()) { + event_idx[event_name] = event_items.size(); + EventItem event_item = {event_name, 1, event_time, + event_time, event_time, event_time}; + event_items.push_back(event_item); } else { - std::get<0>(events_table[event_name]) += 1; + int index = event_idx[event_name]; + event_items[index].calls += 1; // total time - std::get<1>(events_table[event_name]) += event_time; + event_items[index].total_time += event_time; // min time - if (std::get<2>(events_table[event_name]) > event_time) { - std::get<2>(events_table[event_name]) = event_time; - } + event_items[index].min_time = + std::min(event_time, event_items[index].min_time); // max time - if (std::get<3>(events_table[event_name]) < event_time) { - std::get<3>(events_table[event_name]) = event_time; - } + event_items[index].max_time = + std::max(event_time, event_items[index].max_time); } // remove the start marker from the list pushed_events.erase((++rit).base()); @@ -232,6 +273,29 @@ void ParseEvents(std::vector>& events) { } } } + // average time + for (auto& item : event_items) { + item.ave_time = item.total_time / item.calls; + } + // sort + if (sorted_by != EventSortingKey::kDefault) { + std::sort(event_items.begin(), event_items.end(), + [&](EventItem& a, EventItem& b) { + switch (sorted_by) { + case EventSortingKey::kCalls: + return a.calls > b.calls; + case EventSortingKey::kTotal: + return a.total_time > b.total_time; + case EventSortingKey::kMin: + return a.min_time > b.min_time; + case EventSortingKey::kMax: + return a.max_time > b.max_time; + default: + return a.ave_time > b.ave_time; + } + }); + } + events_table.push_back(event_items); } // output events table std::cout.setf(std::ios::left); @@ -240,18 +304,16 @@ void ParseEvents(std::vector>& events) { << "Calls" << std::setw(data_width) << "Total" << std::setw(data_width) << "Min." << std::setw(data_width) << "Max." << std::setw(data_width) << "Ave." << std::endl; - for (std::map>::iterator it = - events_table.begin(); - it != events_table.end(); ++it) { - // average time - std::get<4>(it->second) = std::get<1>(it->second) / std::get<0>(it->second); - std::cout << std::setw(max_name_width + 4) << it->first - << std::setw(data_width) << std::get<0>(it->second) - << std::setw(data_width) << std::get<1>(it->second) - << std::setw(data_width) << std::get<2>(it->second) - << std::setw(data_width) << std::get<3>(it->second) - << std::setw(data_width) << std::get<4>(it->second) << std::endl; + for (size_t i = 0; i < events_table.size(); ++i) { + for (size_t j = 0; j < events_table[i].size(); ++j) { + EventItem& event_item = events_table[i][j]; + std::cout << std::setw(max_name_width + 4) << event_item.name + << std::setw(data_width) << event_item.calls + << std::setw(data_width) << event_item.total_time + << std::setw(data_width) << event_item.min_time + << std::setw(data_width) << event_item.max_time + << std::setw(data_width) << event_item.ave_time << std::endl; + } } } diff --git a/paddle/platform/profiler.h b/paddle/platform/profiler.h index eb176421a92..7504aff5cd9 100644 --- a/paddle/platform/profiler.h +++ b/paddle/platform/profiler.h @@ -117,7 +117,22 @@ void EnableProfiler(ProfilerState state); // event_lists, event_lists[i][j] represents the j-th Event of i-th thread. std::vector> DisableProfiler(); -void ParseEvents(std::vector>&); +// The information of each event given in the profiling report +struct EventItem { + std::string name; + int calls; + double total_time; + double min_time; + double max_time; + double ave_time; +}; + +// Candidate keys to sort the profiling report +enum EventSortingKey { kDefault, kCalls, kTotal, kMin, kMax, kAve }; + +// Parse the event list and output the profiling report +void ParseEvents(std::vector>&, + EventSortingKey sorted_by = EventSortingKey::kDefault); } // namespace platform } // namespace paddle diff --git a/paddle/platform/profiler_test.cc b/paddle/platform/profiler_test.cc index 7966b35a16e..68b75e70637 100644 --- a/paddle/platform/profiler_test.cc +++ b/paddle/platform/profiler_test.cc @@ -55,6 +55,7 @@ TEST(RecordEvent, RecordEvent) { using paddle::platform::EventKind; using paddle::platform::RecordEvent; using paddle::platform::ProfilerState; + using paddle::platform::EventSortingKey; ProfilerState state = ProfilerState::kCPU; DeviceContext* dev_ctx = nullptr; @@ -70,22 +71,26 @@ TEST(RecordEvent, RecordEvent) { /* Usage 1: * PushEvent(evt_name, dev_ctx); * ... - * code to time + * code to analyze * ... * PopEvent(evt_name, dev_ctx); */ - for (int i = 1; i < 5; ++i) { - std::string name = "op_" + std::to_string(i); - PushEvent(name, dev_ctx); - int counter = 1; - while (counter != i * 1000) counter++; - PopEvent(name, dev_ctx); + for (int loop = 0; loop < 3; ++loop) { + for (int i = 1; i < 5; ++i) { + std::string name = "op_" + std::to_string(i); + PushEvent(name, dev_ctx); + int counter = 1; + while (counter != i * 1000) counter++; + PopEvent(name, dev_ctx); + } } /* Usage 2: * { * RecordEvent record_event(name, dev_ctx); * ... + * code to analyze + * ... * } */ for (int i = 1; i < 5; ++i) { @@ -94,19 +99,13 @@ TEST(RecordEvent, RecordEvent) { int counter = 1; while (counter != i * 1000) counter++; } - for (int i = 1; i < 5; ++i) { - std::string name = "evs_op_" + std::to_string(i); - RecordEvent record_event(name, dev_ctx); - int counter = 1; - while (counter != i * 1000) counter++; - } std::vector> events = paddle::platform::DisableProfiler(); + // Will remove from test before merging + ParseEvents(events, EventSortingKey::kTotal); + int cuda_startup_count = 0; int start_profiler_count = 0; int stop_profiler_count = 0; - - ParseEvents(events); - for (size_t i = 0; i < events.size(); ++i) { for (size_t j = 0; j < events[i].size(); ++j) { if (events[i][j].name() == "_cuda_startup_") ++cuda_startup_count; -- GitLab From d7e56847c7063c0281e13cfda0cc6ba78761a618 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Fri, 5 Jan 2018 09:04:27 +0000 Subject: [PATCH 0090/2305] fix typos --- paddle/platform/profiler_test.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paddle/platform/profiler_test.cc b/paddle/platform/profiler_test.cc index 68b75e70637..d4fd4c88503 100644 --- a/paddle/platform/profiler_test.cc +++ b/paddle/platform/profiler_test.cc @@ -71,7 +71,7 @@ TEST(RecordEvent, RecordEvent) { /* Usage 1: * PushEvent(evt_name, dev_ctx); * ... - * code to analyze + * code to be analyzed * ... * PopEvent(evt_name, dev_ctx); */ @@ -89,7 +89,7 @@ TEST(RecordEvent, RecordEvent) { * { * RecordEvent record_event(name, dev_ctx); * ... - * code to analyze + * code to be analyzed * ... * } */ -- GitLab From 5a0a4617536aa4c79a30f195f88653794fe98678 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Fri, 5 Jan 2018 09:57:28 +0000 Subject: [PATCH 0091/2305] Make time calc funcs return ms instead of us --- paddle/platform/profiler.cc | 13 ++++++------- paddle/platform/profiler.h | 4 ++-- paddle/platform/profiler_test.cc | 8 ++++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index a35d6e94f07..ea16e560767 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -74,11 +74,11 @@ std::string Event::kind() const { PADDLE_THROW("Unknown EventKind."); } -double Event::CpuElapsedUs(const Event& e) const { - return (e.cpu_ns_ - cpu_ns_) / (1000.0); +double Event::CpuElapsedMs(const Event& e) const { + return (e.cpu_ns_ - cpu_ns_) / (1000000.0); } -double Event::CudaElapsedUs(const Event& e) const { +double Event::CudaElapsedMs(const Event& e) const { #ifdef PADDLE_WITH_CUDA PADDLE_ENFORCE(e.has_cuda() && has_cuda()); PADDLE_ENFORCE(e.device() == device()); @@ -86,7 +86,7 @@ double Event::CudaElapsedUs(const Event& e) const { PADDLE_ENFORCE(cudaEventSynchronize(e.event())); float ms; PADDLE_ENFORCE(cudaEventElapsedTime(&ms, event_, e.event())); - return ms * 1000.0; + return ms; #else PADDLE_THROW("CUDA is not enabled"); #endif @@ -239,11 +239,10 @@ void ParseEvents(std::vector>& events, ++rit; } if (rit != pushed_events.rend()) { -// get event time in ms #ifdef PADDLE_WITH_CUDA - double event_time = rit->CudaElapsedUs(events[i][j]) / 1000.0; + double event_time = rit->CudaElapsedMs(events[i][j]); #else - double event_time = rit->CpuElapsedUs(events[i][j]) / 1000.0; + double event_time = rit->CpuElapsedMs(events[i][j]); #endif std::string event_name = "thread" + std::to_string(rit->thread_id()) + "::" + rit->name(); diff --git a/paddle/platform/profiler.h b/paddle/platform/profiler.h index 7504aff5cd9..d51ec644321 100644 --- a/paddle/platform/profiler.h +++ b/paddle/platform/profiler.h @@ -41,8 +41,8 @@ class Event { int device() const { return device_; } #endif - double CpuElapsedUs(const Event& e) const; - double CudaElapsedUs(const Event& e) const; + double CpuElapsedMs(const Event& e) const; + double CudaElapsedMs(const Event& e) const; private: EventKind kind_; diff --git a/paddle/platform/profiler_test.cc b/paddle/platform/profiler_test.cc index d4fd4c88503..3800f3914f1 100644 --- a/paddle/platform/profiler_test.cc +++ b/paddle/platform/profiler_test.cc @@ -26,7 +26,7 @@ TEST(Event, CpuElapsedTime) { counter++; } Event stop_event(EventKind::kPopRange, "test", 0, nullptr); - EXPECT_GT(start_event.CpuElapsedUs(stop_event), 0); + EXPECT_GT(start_event.CpuElapsedMs(stop_event), 0); } #ifdef PADDLE_WITH_CUDA @@ -45,7 +45,7 @@ TEST(Event, CudaElapsedTime) { counter++; } Event stop_event(EventKind::kPopRange, "test", 0, dev_ctx); - EXPECT_GT(start_event.CudaElapsedUs(stop_event), 0); + EXPECT_GT(start_event.CudaElapsedMs(stop_event), 0); } #endif @@ -114,9 +114,9 @@ TEST(RecordEvent, RecordEvent) { if (events[i][j].name() == "push") { EXPECT_EQ(events[i][j + 1].name(), "pop"); #ifdef PADDLE_WITH_CUDA - EXPECT_GT(events[i][j].CudaElapsedUs(events[i][j + 1]), 0); + EXPECT_GT(events[i][j].CudaElapsedMs(events[i][j + 1]), 0); #else - EXPECT_GT(events[i][j].CpuElapsedUs(events[i][j + 1]), 0); + EXPECT_GT(events[i][j].CpuElapsedMs(events[i][j + 1]), 0); #endif } } -- GitLab From dea52631792ca765b0007a9389bf1203a8b31aab Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Fri, 5 Jan 2018 17:57:42 +0800 Subject: [PATCH 0092/2305] update error clip --- python/paddle/v2/fluid/backward.py | 12 ++++++--- python/paddle/v2/fluid/clip.py | 38 +++++++++++++++-------------- python/paddle/v2/fluid/framework.py | 13 ++++++++++ 3 files changed, 42 insertions(+), 21 deletions(-) diff --git a/python/paddle/v2/fluid/backward.py b/python/paddle/v2/fluid/backward.py index b788a23eb60..b3f6887e4c1 100644 --- a/python/paddle/v2/fluid/backward.py +++ b/python/paddle/v2/fluid/backward.py @@ -190,8 +190,15 @@ def _append_backward_ops_(target, val(str): corresponding forward variable name callback(callable object): a callable object used to decorate new generated grad ops """ - if callback is not None and not hasattr(callback, '__call__'): + if callback is None: + + def empty_callback(block): + pass + + callback = empty_callback + elif not hasattr(callback, '__call__'): raise ValueError("'callback' must be a callable object.") + # grad_op_descs holds created grad_op, and will be appended to target_block grad_op_descs = [] program = block.program @@ -208,8 +215,6 @@ def _append_backward_ops_(target, # Getting op's corresponding grad_op grad_op_desc, op_grad_to_var = core.get_grad_op_desc( op.desc, no_grad_dict[block.idx], grad_sub_block_list) - if callback is not None: - grad_op_desc = callback(grad_op_desc) grad_op_descs.extend(grad_op_desc) grad_to_var.update(op_grad_to_var) @@ -230,6 +235,7 @@ def _append_backward_ops_(target, for op_desc in grad_op_descs: new_op_desc = target_block.desc.append_op() new_op_desc.copy_from(op_desc) + callback(block=target_block, context=grad_to_var) def _append_backward_vars_(block, start_op_idx, grad_to_var, grad_info_map): diff --git a/python/paddle/v2/fluid/clip.py b/python/paddle/v2/fluid/clip.py index 89972b8346f..8fb27d69fab 100644 --- a/python/paddle/v2/fluid/clip.py +++ b/python/paddle/v2/fluid/clip.py @@ -6,18 +6,9 @@ __all__ = ['GradientClipByValue', 'append_gradient_clip_ops'] class BaseErrorClipAttr(object): - def create_clip_op_desc(self, grad_name): + def append_clip_op(self, block, grad_name): raise NotImplementedError() - def prepend_clip_op_desc(self, op_descs): - grad_names = set() - for op_desc in op_descs: - grad_names.update( - filter(lambda n: n.find(core.grad_var_suffix()) != -1, - op_desc.output_arg_names())) - for n in grad_names: - op_descs.append(self.create_clip_op_desc(grad_name=n)) - class ErrorClipByValue(BaseErrorClipAttr): def __init__(self, max, min=None): @@ -29,14 +20,25 @@ class ErrorClipByValue(BaseErrorClipAttr): self.max = max self.min = min - def create_clip_op_desc(self, grad_name): - desc = core.OpDesc() - desc.set_type("clip") - desc.set_input("X", grad_name) - desc.set_output("Out", grad_name) - desc.set_attr("min", self.min) - desc.set_attr("max", self.max) - return desc + def append_clip_op(self, block, grad_name): + block.append_op( + type="clip", + inputs={"X": grad_name}, + outputs={"Out": grad_name}, + attrs={"min": self.min, + "max": self.max}) + + +def error_clip_callback(block, context): + # the context is a grad_to_var map + grad_to_var = context + op_desc = block.desc.op(block.desc.op_size() - 1) + for grad_n in filter(lambda n: grad_to_var.has_key(n), + op_desc.output_arg_names()): + fwd_var = block.var_recursive(grad_to_var[grad_n]) + error_clip = getattr(fwd_var, "error_clip", None) + if error_clip is not None: + error_clip.append_clip_op(block, grad_n) class BaseGradientClipAttr(object): diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index b66a8bce5f4..4b01a2d0465 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -147,6 +147,7 @@ class Variable(object): dtype=None, lod_level=None, persistable=None, + error_clip=None, stop_gradient=False, **kwargs): self.block = block @@ -626,6 +627,17 @@ class Block(object): raise ValueError("var %s not in this block" % name) return v + def var_recursive(self, name): + if self.has_var(name): + return self.var(name) + else: + if self.idx == 0: + raise ValueError("var %s is not in block(%d) nor its parents." % + name, self.idx) + else: + parent_block = self.program.block(self.parent_idx) + return parent_block.var_recursive(name) + def all_parameters(self): return list(self.iter_parameters()) @@ -744,6 +756,7 @@ class Block(object): optimize_attr=p.optimize_attr, regularizer=p.regularizer, clip_attr=p.clip_attr, + error_clip=p.error_clip, name=v.name) self.vars[new_p.name] = new_p -- GitLab From 33cb12a89b7dfaa9eb2f764ce41d61595edd5453 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Fri, 5 Jan 2018 18:08:51 +0800 Subject: [PATCH 0093/2305] update error clip --- python/paddle/v2/fluid/backward.py | 4 ++-- python/paddle/v2/fluid/clip.py | 4 +++- python/paddle/v2/fluid/framework.py | 1 + python/paddle/v2/fluid/optimizer.py | 5 +++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/python/paddle/v2/fluid/backward.py b/python/paddle/v2/fluid/backward.py index b3f6887e4c1..33f6c103845 100644 --- a/python/paddle/v2/fluid/backward.py +++ b/python/paddle/v2/fluid/backward.py @@ -278,7 +278,7 @@ def _append_backward_vars_(block, start_op_idx, grad_to_var, grad_info_map): _infer_var_data_type_(arg, block) -def append_backward(loss, parameter_list=None, no_grad_set=None): +def append_backward(loss, parameter_list=None, no_grad_set=None, callback=None): """ Append backward part to main_program @@ -322,7 +322,7 @@ def append_backward(loss, parameter_list=None, no_grad_set=None): grad_to_var = dict() _append_backward_ops_(loss, root_block, root_block, no_grad_dict, - grad_to_var) + grad_to_var, callback) _append_backward_vars_(root_block, fwd_op_num, grad_to_var, grad_info_map) program.current_block_idx = current_block_idx diff --git a/python/paddle/v2/fluid/clip.py b/python/paddle/v2/fluid/clip.py index 8fb27d69fab..b1fd1c2b65f 100644 --- a/python/paddle/v2/fluid/clip.py +++ b/python/paddle/v2/fluid/clip.py @@ -2,7 +2,9 @@ import functools import layers from . import core -__all__ = ['GradientClipByValue', 'append_gradient_clip_ops'] +__all__ = [ + 'GradientClipByValue', 'append_gradient_clip_ops', 'error_clip_callback' +] class BaseErrorClipAttr(object): diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index 4b01a2d0465..24b2630abf8 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -151,6 +151,7 @@ class Variable(object): stop_gradient=False, **kwargs): self.block = block + self.error_clip = error_clip if name is None: name = Variable._unique_var_name_() diff --git a/python/paddle/v2/fluid/optimizer.py b/python/paddle/v2/fluid/optimizer.py index ff3e5315a2c..40721b5e97a 100644 --- a/python/paddle/v2/fluid/optimizer.py +++ b/python/paddle/v2/fluid/optimizer.py @@ -6,7 +6,7 @@ from framework import unique_name, program_guard from initializer import Constant from layer_helper import LayerHelper from regularizer import append_regularization_ops -from clip import append_gradient_clip_ops +from clip import append_gradient_clip_ops, error_clip_callback __all__ = ['SGD', 'Momentum', 'Adagrad', 'Adam', 'Adamax', 'DecayedAdagrad'] @@ -197,7 +197,8 @@ class Optimizer(object): This method combines interface `append_backward()` and `create_optimization_pass()` into one. """ - params_grads = append_backward(loss, parameter_list, no_grad_set) + params_grads = append_backward(loss, parameter_list, no_grad_set, + error_clip_callback) params_grads = append_gradient_clip_ops(params_grads) -- GitLab From 643ff03fbc5e100176070760c9c73f11a695caf3 Mon Sep 17 00:00:00 2001 From: Yancey Date: Fri, 5 Jan 2018 18:51:49 +0800 Subject: [PATCH 0094/2305] capi package (#7237) --- paddle/scripts/docker/build.sh | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/paddle/scripts/docker/build.sh b/paddle/scripts/docker/build.sh index 92039ec6b05..f1e244772f3 100644 --- a/paddle/scripts/docker/build.sh +++ b/paddle/scripts/docker/build.sh @@ -193,6 +193,25 @@ EOF EOF } +function gen_capi_package() { + if [[ ${WITH_C_API} == "ON" ]]; then + install_prefix="/paddle/build/capi_output" + rm -rf $install_prefix + + make DESTDIR="$install_prefix" install + + if [[ ${WITH_MKL:-OFF} == "ON" ]]; then + find ./third_party/install -name 'libmklml_gnu.so' -exec cp {} $install_prefix/usr/local/lib \; + find ./third_party/install -name 'libmklml_intel.so' -exec cp {} $install_prefix/usr/local/lib \; + cp -P ./third_party/install/mkldnn/lib/* $install_prefix/usr/local/lib/ + fi + + find ./third_party/install -name 'libiomp5.so' -exec cp {} $install_prefix/usr/local/lib \; + cd $install_prefix/usr/local + ls | egrep -v "^Found.*item$" | xargs tar /paddle/build/paddle.tgz + fi +} + set -xe cmake_gen ${PYTHON_ABI:-""} @@ -200,6 +219,11 @@ run_build run_test gen_docs gen_dockerfile - -printf "If you need to install PaddlePaddle in develop docker image," -printf "please make install or pip install build/python/dist/*.whl.\n" +gen_capi_package + +if [[ ${WITH_C_API:-OFF} == "ON" ]]; then + printf "PaddlePaddle C-API libraries was generated on build/paddle.tgz\n" +else + printf "If you need to install PaddlePaddle in develop docker image," + printf "please make install or pip install build/python/dist/*.whl.\n" +fi -- GitLab From be218bfac4ab4232d56e407b22ea581cc7b1f8e5 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Fri, 5 Jan 2018 19:16:35 +0800 Subject: [PATCH 0095/2305] fix bug --- python/paddle/v2/fluid/backward.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/backward.py b/python/paddle/v2/fluid/backward.py index 33f6c103845..f095d414cb9 100644 --- a/python/paddle/v2/fluid/backward.py +++ b/python/paddle/v2/fluid/backward.py @@ -192,7 +192,7 @@ def _append_backward_ops_(target, """ if callback is None: - def empty_callback(block): + def empty_callback(block, context): pass callback = empty_callback -- GitLab From ed55f1b9d49ed3f6609246159594da85810a9a2e Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Fri, 5 Jan 2018 19:25:30 +0800 Subject: [PATCH 0096/2305] transpiler_split_tensor --- .../paddle/v2/fluid/distribute_transpiler.py | 241 +++++++++++------- 1 file changed, 148 insertions(+), 93 deletions(-) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index 49ece7b725e..e5314cf2729 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -1,51 +1,20 @@ +from __future__ import print_function import framework from framework import Program, default_main_program, Parameter, Variable import optimizer from layer_helper import LayerHelper +from distributed_spliter import * -def hash_name_to_server(params_grads, pserver_endpoints): - """ - :param param_grads: - :return: a map of pserver endpoint -> - params -> [param list] - grads -> [grad list] - """ +class VarBlock: + def __init__(self, varname, offset, size): + self.varname = varname + # NOTE: real offset is offset * size + self.offset = offset + self.size = size - def _hash_param(param_name, total): - return hash(param_name) % total - - param_grad_map = dict() - for param, grad in params_grads: - if param.trainable is True and grad is not None: - server_id = _hash_param(param.name, len(pserver_endpoints)) - server_for_param = pserver_endpoints[server_id] - if not param_grad_map.has_key(server_for_param): - param_grad_map[server_for_param] = {"params": [], "grads": []} - param_grad_map[server_for_param]["params"].append(param) - param_grad_map[server_for_param]["grads"].append(grad) - - return param_grad_map - - -def round_robin(params_grads, pserver_endpoints): - assert (len(params_grads) > len(pserver_endpoints)) - - param_grad_map = dict() - pserver_idx = 0 - for param, grad in params_grads: - if param.trainable is True: - server_for_param = pserver_endpoints[pserver_idx] - if not param_grad_map.has_key(server_for_param): - param_grad_map[server_for_param] = {"params": [], "grads": []} - - param_grad_map[server_for_param]["params"].append(param) - param_grad_map[server_for_param]["grads"].append(grad) - - pserver_idx += 1 - if pserver_idx >= len(pserver_endpoints): - pserver_idx = 0 - return param_grad_map + def __str__(self): + return "%s:%d:%d" % (self.varname, self.offset, self.size) class DistributeTranspiler: @@ -58,7 +27,6 @@ class DistributeTranspiler: split_method=round_robin): """ Transpile the program to a distributed data-parallelism programs. - The main_program will be transform to use a remote parameter server to do parameter optimization. And the optimization graph will be put in to a parameter server program. @@ -66,45 +34,84 @@ class DistributeTranspiler: Use different methods to split trainable varialbles to different parameter servers. - Example to run: - - exe = fluid.Executor(place) - t = fluid.DistributeTranspiler() - t.transpile(optimize_ops, params_grads, pservers="127.0.0.1:6174", trainers=1) - - pserver_endpoint = os.getenv("PSERVER") - if pserver_endpoint: - pserver_prog = t.get_pserver_program(pserver_endpoint, optimize_ops) - exe.run(fluid.default_startup_program()) - exe.run(pserver_prog) - else: - feeder = fluid.DataFeeder(feed_list=[images, label], place=place) - exe.run(fluid.default_startup_program()) - - for pass_id in range(PASS_NUM): - ... - :param optimize_ops: op list of optimization, should be the return value of Optimizer.minimize :type optimize_ops: list :param program: program to optimize, default default_main_program :param pservers: parameter server endpoints like "m1:6174,m2:6174" :type pservers: string - :return: return a list of programs """ + assert (callable(split_method)) if program is None: program = default_main_program() self.program = program self.trainers = trainers self.optimize_ops = optimize_ops - self._optimize_distributed( - optimize_ops, - program, - params_grads, - pservers=pservers, - trainers=trainers, - split_method=split_method) + # steps to transpile: + # 1. split variable to multiple blocks, align by product(dim[1:]) (width). + # 2. modify trainer program add split_op to each Grad. + # 3. append send_op to trainer. + # 4. append concat_op to trainer to update local weights. + # 5. create new program as parameter server. + # 5. create parameter server program by split_method generated endpoint->VarBlock + # 6. run compile time infershape for parameter server program + + if kwargs.has_key("split_method"): + split_method = kwargs["split_method"] + else: + split_method = round_robin + pserver_endpoints = kwargs["pservers"].split(",") + + grad2param = dict() + for param, grad in params_and_grads: + grad2param[grad.name()] = param.name() + + # step1 + param_list = [pg[0] for pg in params_and_grads] + grad_list = [pg[1] for pg in params_and_grads] + # TODO: add split selected rows support + grad_blocks = _split_dense_variable(grad_list, len(pserver_endpoints)) + param_blocks = _split_dense_variable(param_list, len(pserver_endpoints)) + ep2gradblock = split_method(grad_blocks, pserver_endpoints) + # self.param_grad_map + # step2 + var2splited = self._split_trainer_vars(program, grad_blocks) + + # step3 + send_inputs = [] + send_outputs = [] + for _, splited in var2splited.iteritems(): + send_inputs.extend(splited) + send_outputs = self._create_vars_from_blocklist(program, param_blocks) + + send_op = program.global_block().append_op( + type="send", + inputs={"X": send_inputs}, + outputs={"Out": send_outputs}, + attrs={"endpoints": pserver_endpoints, + "epmap": epmap}) + + def _create_vars_from_blocklist(self, program, block_list): + block_map = dict() + ret_vars = [] + for block_str in block_list: + varname, offset, size = block_str.split(":") + if not block_map.has_key(varname): + block_map[varname] = [] + block_map[varname].append((long(offset), long(size))) + + for varname, splited in block_map.iteritems(): + orig_var = program.global_block().vars[varname] + for block in splited: + size = block[1] + var = program.global_block().create_var( + name="%s.block%d" % (varname, i), + psersistable=False, + dtype=orig_var.dtype, + shape=[1, size]) # flattend splited var + ret_vars.append(var) + return ret_vars def _clone_param(self, block, v): assert isinstance(v, Parameter) @@ -131,32 +138,80 @@ class DistributeTranspiler: lod_level=var.lod_level, persistable=var.persistable) - def _optimize_distributed(self, optimize_ops, program, params_and_grads, - **kwargs): - if kwargs.has_key("split_method"): - split_method = kwargs["split_method"] - else: - split_method = round_robin + def _split_dense_variable(self, + var_list, + pserver_count, + min_block_size=1024, + max_block_size=1048576): + """ + We may need to split dense tensor to one or several blocks and put + them equally onto parameter server. One block is a sub-tensor + aligned by dim[0] of the tensor. + + We need to have a minimal block size so that the calculations in + the parameter server side can gain better performance. By default + mininum block size is 1024. The max block size is used to prevent + too large block that may causing send error. + """ + block_sizes = [] + blocks = [] + for grad in var_list: + dim1 = reduce(lambda x, y: x * y, grad.shape[1:]) + grad_numel = reduce(lambda x, y: x * y, grad.shape) + if grad_numel < min_block_size: + block_sizes.append(grad_numel) + block_size = grad_numel / min_block_size + if block_size < min_block_size: + block_size = min_block_size + # align by dim1(width) + remains = block_size % dim1 + if remains != 0: + block_size += dim1 - remains + block_sizes.append(block_size) + num_blocks = grad_numel / block_size + print("grad numel :%d, blocksize: %d" % grad_numel, block_size) + for block_id in xrange(num_blocks): + block = VarBlock(grad.name(), block_id, block_size) + blocks.append(str(block)) + return blocks - assert (callable(split_method)) - pserver_endpoints = kwargs["pservers"].split(",") - self.param_grad_map = split_method(params_and_grads, pserver_endpoints) - - send_op_ordered_inputs = [] - send_op_ordered_outputs = [] - epmap = [] - for ep, v in self.param_grad_map.iteritems(): - send_op_ordered_inputs.extend(v["grads"]) - send_op_ordered_outputs.extend(v["params"]) - for i in v["grads"]: - epmap.append(ep) - send_op = program.global_block().append_op( - type="send", - inputs={"X": send_op_ordered_inputs - }, # inputs is a list of tensors to be send - outputs={"Out": send_op_ordered_outputs}, - attrs={"endpoints": pserver_endpoints, - "epmap": epmap}) + def _split_trainer_vars(self, program, gradblocks, params_and_grads): + var2blocks = dict() + splited = dict() + for block_str in gradblocks: + varname, offset, size = block_str.split(":") + if not var2blocks.has_key(varname): + var2blocks[varname] = [] + var2blocks[varname].append((long(offset), long(size))) + for varname, blocks in var2blocks.iteritems(): + orig_var = program.global_block().vars[varname] + split_outs = [] + for i in xrange(len(blocks)): + size = blocks[i][1] + var = program.global_block().create_var( + name="%s.block%d" % (varname, i), + psersistable=False, + dtype=orig_var.dtype, + shape=[1, size]) # flattend splited var + split_outs.append(var) + + splited[varname] = split_outs + program.global_block().append_op( + type="split", + inputs={"X": orig_var}, + outputs={"Out": split_outs}, + attrs={"num": len(blocks)} # assume split evenly + ) + return splited + + def _concat_trainer_vars(self, program, splited): + for varname, to_merge_list in splited.iteritems(): + orig_var = program.global_block().vars[varname] + program.global_block().append_op( + type="concat", + inputs={"X": to_merge_list}, + outputs={"Out": orig_var}, + attrs={}) def get_trainer_program(self): # remove optimize ops and add a send op to main_program -- GitLab From c70ea1cc30cfa54228d10e56934119e3a68e3e51 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Fri, 5 Jan 2018 19:35:52 +0800 Subject: [PATCH 0097/2305] add splitter --- .../paddle/v2/fluid/distribute_transpiler.py | 1 - python/paddle/v2/fluid/distributed_spliter.py | 38 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 python/paddle/v2/fluid/distributed_spliter.py diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index e5314cf2729..4c90b4a8535 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -80,7 +80,6 @@ class DistributeTranspiler: # step3 send_inputs = [] - send_outputs = [] for _, splited in var2splited.iteritems(): send_inputs.extend(splited) send_outputs = self._create_vars_from_blocklist(program, param_blocks) diff --git a/python/paddle/v2/fluid/distributed_spliter.py b/python/paddle/v2/fluid/distributed_spliter.py new file mode 100644 index 00000000000..e7ba53390d4 --- /dev/null +++ b/python/paddle/v2/fluid/distributed_spliter.py @@ -0,0 +1,38 @@ +def hash_name(varblocks, pserver_endpoints): + """ + :param varblocks: a list of VarBlock string indicating + sub blocks of variables + :return: a map of pserver endpoint -> varblock_str + """ + + def _hash_block(block_str, total): + return hash(block_str) % total + + ep2block = dict() + for varblock_str in varblocks: + if param.trainable is True and grad is not None: + server_id = _hash_block(varblock_str, len(pserver_endpoints)) + server_for_param = pserver_endpoints[server_id] + if not ep2block.has_key(server_for_param): + ep2block[server_for_param] = [] + ep2block[server_for_param].append(varblock_str) + + return ep2block + + +def round_robin(varblocks, pserver_endpoints): + assert (len(varblocks) > len(pserver_endpoints)) + + ep2block = dict() + pserver_idx = 0 + for varblock_str in varblocks: + if param.trainable is True: + server_for_param = pserver_endpoints[pserver_idx] + if not ep2block.has_key(server_for_param): + ep2block[server_for_param] = [] + ep2block[server_for_param].append(varblock_str) + + pserver_idx += 1 + if pserver_idx >= len(pserver_endpoints): + pserver_idx = 0 + return ep2block -- GitLab From e21923543a1667bb5655db0316090bf5cafec735 Mon Sep 17 00:00:00 2001 From: guosheng Date: Fri, 5 Jan 2018 20:56:43 +0800 Subject: [PATCH 0098/2305] Enhance reorder_lod_tensor_by_rank_op to support Tensor --- paddle/operators/reduce_op.cc | 1 + .../reorder_lod_tensor_by_rank_op.cc | 33 ++- python/paddle/v2/fluid/layers/control_flow.py | 2 +- .../v2/fluid/tests/test_reorder_lod_tensor.py | 202 +++++++++++++++--- 4 files changed, 196 insertions(+), 42 deletions(-) diff --git a/paddle/operators/reduce_op.cc b/paddle/operators/reduce_op.cc index a3ff4a6ca0e..172d28bb3b6 100644 --- a/paddle/operators/reduce_op.cc +++ b/paddle/operators/reduce_op.cc @@ -77,6 +77,7 @@ class ReduceGradOp : public framework::OperatorWithKernel { auto x_grad_name = framework::GradVarName("X"); if (ctx->HasOutput(x_grad_name)) { ctx->SetOutputDim(x_grad_name, x_dims); + ctx->ShareLoD("X", /*->*/ x_grad_name); } } }; diff --git a/paddle/operators/reorder_lod_tensor_by_rank_op.cc b/paddle/operators/reorder_lod_tensor_by_rank_op.cc index 8d652ff8064..0fa615d8743 100644 --- a/paddle/operators/reorder_lod_tensor_by_rank_op.cc +++ b/paddle/operators/reorder_lod_tensor_by_rank_op.cc @@ -88,20 +88,33 @@ class ReorderLoDTensorByRankTableBase : public framework::OperatorBase { std::vector GetAbsoluteOffsetAndLengthByLoDRankTable( const framework::LoDTensor &x) const { std::vector absolute_table; - size_t level = 0; - size_t size = x.lod()[level].size(); - for (size_t i = 0; i < size - 1; ++i) { - auto lod_offset = - framework::GetSubLoDAndAbsoluteOffset(x.lod(), i, i + 1, level); + if (x.lod().empty()) { + // For Tensor without lod, such as the output of sequence_pool_op + size_t size = x.dims()[0]; + absolute_table.reserve(size); + for (size_t i = 0; i < size; ++i) { + absolute_table.emplace_back(); + absolute_table.back().length = 1; + absolute_table.back().offset = i; + } + } else { + size_t level = 0; + size_t size = x.lod()[level].size(); + + for (size_t i = 0; i < size - 1; ++i) { + auto lod_offset = + framework::GetSubLoDAndAbsoluteOffset(x.lod(), i, i + 1, level); - auto &offset = lod_offset.second; + auto &offset = lod_offset.second; - absolute_table.emplace_back(); - absolute_table.back().length = offset.second - offset.first; - absolute_table.back().offset = offset.first; - absolute_table.back().lod = lod_offset.first; + absolute_table.emplace_back(); + absolute_table.back().length = offset.second - offset.first; + absolute_table.back().offset = offset.first; + absolute_table.back().lod = lod_offset.first; + } } + return absolute_table; } diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index acc22bef98b..bee57eec834 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -464,7 +464,7 @@ def lod_rank_table(x, level=0): """LoD Rank Table Operator. Given an input variable **x** and a level number of LoD, this layer creates a LodRankTable object. A LoDRankTable object contains a list of bi-element tuples. Each tuple consists of an index and - a length, both of which are int type. Reffering to specified level of LoD, + a length, both of which are int type. Refering to specified level of LoD, the index is the sequence index number and the length representes the sequence length. Please note that the list is ranked in descending order by the length. The following is an example: diff --git a/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py b/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py index 7c136f6360c..8b79d448e26 100644 --- a/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py +++ b/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py @@ -1,46 +1,186 @@ import unittest import paddle.v2.fluid as fluid +import paddle.v2.fluid.core as core import numpy class TestReorderLoDTensor(unittest.TestCase): - def test_reorder(self): - dat = fluid.layers.data(name='input', shape=[1], lod_level=2) + num_seq = 5 + # [name, dim, lod_level] pair indicating data info of source and target + data_desc = (['input', 9, 0], ['ref', 5, 1]) + + @classmethod + def setUpClass(cls): + cls.set_program() + + @classmethod + def set_program(cls): + dat = fluid.layers.data( + name=cls.data_desc[0][0], shape=[cls.data_desc[0][1]]) dat.stop_gradient = False - rank_dat = fluid.layers.data(name='ref', shape=[1], lod_level=1) + rank_dat = fluid.layers.data( + name=cls.data_desc[1][0], shape=[cls.data_desc[1][1]]) table = fluid.layers.lod_rank_table(rank_dat) new_dat = fluid.layers.reorder_lod_tensor_by_rank( x=dat, rank_table=table) - loss = fluid.layers.mean(x=new_dat) + loss = fluid.layers.reduce_sum(new_dat) fluid.backward.append_backward(loss=loss) + cls.fetch_list = [new_dat, cls.data_desc[0][0] + '@GRAD'] + + def run_program(self): + outputs = [] + input_grads = [] + places = [core.CPUPlace()] + if core.is_compile_gpu(): + places.append(core.CUDAPlace(0)) + for place in places: + self.set_inputs(place) + exe = fluid.Executor(place) + output, input_grad = exe.run(fluid.default_main_program(), + feed=self.inputs, + fetch_list=self.fetch_list, + return_numpy=False) + outputs.append(output) + input_grads.append(input_grad) + self.actual_outputs = outputs + self.actual_grads = input_grads + + def set_data(self): + self.data = {} + for desc in self.data_desc: + data_name = desc[0] + data_dim = desc[1] + data_lod_level = desc[2] + data_lod = [] + for i in range(data_lod_level): + lod_level_i = numpy.random.randint( + low=1, + high=5, + size=self.num_seq if i == 0 else lod_level_i[-1]) + lod_level_i = [0] + numpy.cumsum(lod_level_i).tolist() + data_lod.append(lod_level_i) + data_value = numpy.random.random(size=[ + data_lod[-1][-1] if data_lod else self.num_seq, data_dim + ]).astype('float32') + self.data[data_name] = (data_value, data_lod) + + def set_inputs(self, place): + self.inputs = {} + for desc in self.data_desc: + tensor = fluid.Tensor() + tensor.set(self.data[desc[0]][0], place) + if self.data[desc[0]][1]: + tensor.set_lod(self.data[desc[0]][1]) + self.inputs[desc[0]] = tensor + + def reorder(self): + level = 0 + + # compute the rank_table according to ref_lod + ref_lod = self.data[self.data_desc[1][0]][1][level] + rank_table = [] # list of (index, length) + for i in range(len(ref_lod) - 1): + rank_table.append((i, ref_lod[i + 1] - ref_lod[i])) + rank_table = sorted(rank_table, lambda x, y: y[1] - x[1]) + + # compute the input sequence info according to input_lod + input_value, input_lod = self.data[self.data_desc[0][0]] + + input_table = [] # list of (offset, length, sub_lod) + if input_lod: + for i in range(len(input_lod[level]) - 1): + start_idx = i + end_idx = i + 1 + sub_lod = [] + for lod_level_i in input_lod[level:]: + sub_lod_i = [] + for idx in range(start_idx, end_idx): + sub_lod_i.append(lod_level_i[idx + 1] - lod_level_i[ + idx]) + sub_lod.append(sub_lod_i) + start_idx = lod_level_i[start_idx] + end_idx = lod_level_i[end_idx] + input_table.append((start_idx, end_idx - start_idx, sub_lod)) + else: + input_table = [(i, 1, []) for i in range(len(rank_table))] + + # reorder by rank_table + output_value = numpy.zeros_like(input_value) + output_lod = [] + offset = 0 + for index, length in rank_table: + input_seq_start = input_table[index][0] + input_seq_len = input_table[index][1] + input_seq_end = input_seq_start + input_seq_len + output_value[offset:offset + input_seq_len] = input_value[ + input_seq_start:input_seq_end] + offset += input_seq_len + + input_seq_sub_lod = input_table[index][2] + if len(output_lod) == 0: + output_lod = [[0] for i in input_seq_sub_lod] + for i, sub_lod_i in enumerate(input_seq_sub_lod): + for idx_sub in sub_lod_i: + output_lod[i].append(output_lod[i][-1] + idx_sub) + return output_value, output_lod + + def test_reorder_lod_tensor(self): + self.data_desc[0][-1] = 2 # input is lod_tensor + self.set_data() + self.run_program() + # check output + expect_output, expect_output_lod = self.reorder() + for actual_output in self.actual_outputs: + self.assertTrue( + numpy.allclose( + numpy.array(actual_output), expect_output, atol=0.001)) + self.assertEqual(expect_output_lod, actual_output.lod()) + # check gradient + expect_grad = numpy.ones_like(self.data[self.data_desc[0][0]][0]) + expect_grad_lod = self.data[self.data_desc[0][0]][1] + for actual_grad in self.actual_grads: + self.assertTrue( + numpy.allclose( + numpy.array(actual_grad), expect_grad, atol=0.001)) + self.assertEqual(expect_grad_lod, actual_grad.lod()) + + def test_reorder_tensor(self): + self.data_desc[0][-1] = 0 # input is tensor + self.set_data() + self.run_program() + # check output + expect_output, expect_output_lod = self.reorder() + for actual_output in self.actual_outputs: + self.assertTrue( + numpy.allclose( + numpy.array(actual_output), expect_output, atol=0.001)) + self.assertEqual(expect_output_lod, actual_output.lod()) + # check gradient + expect_grad = numpy.ones_like(self.data[self.data_desc[0][0]][0]) + expect_grad_lod = self.data[self.data_desc[0][0]][1] + for actual_grad in self.actual_grads: + self.assertTrue( + numpy.allclose( + numpy.array(actual_grad), expect_grad, atol=0.001)) + self.assertEqual(expect_grad_lod, actual_grad.lod()) + global outputs_from_tensor_implicit_lod + outputs_from_tensor_implicit_lod = self.actual_outputs - cpu = fluid.CPUPlace() - exe = fluid.Executor(cpu) - exe.run(fluid.default_startup_program()) - - ref = fluid.Tensor() - ref_lod = [0, 3, 4, 7, 8, 14] - ref.set_lod([ref_lod]) - - ref.set(numpy.random.random(size=[14, 1]).astype('float32'), cpu) - input = fluid.Tensor() - lod_level_0 = numpy.random.randint(low=1, high=5, size=5) - lod_level_0 = [0] + numpy.cumsum(lod_level_0).tolist() - lod_level_1 = numpy.random.randint(low=1, high=5, size=lod_level_0[-1]) - lod_level_1 = [0] + numpy.cumsum(lod_level_1).tolist() - - input.set_lod([lod_level_0, lod_level_1]) - input.set( - numpy.random.random(size=[lod_level_1[-1], 1]).astype('float32'), - cpu) - - ig = exe.run(fluid.default_main_program(), - feed={'input': input, - 'ref': ref}, - fetch_list=['input@GRAD'], - return_numpy=False)[0] - self.assertAlmostEqual(numpy.array(ig).sum(), 1.0, delta=0.001) - self.assertEqual(input.lod(), ig.lod()) + # compare outputs between LodTensors with explicit and implicit lod + # use the same data but set the input lod explicitly + input_lod = [[ + i for i in range(len(self.data[self.data_desc[0][0]][0]) + 1) + ]] + self.inputs[self.data_desc[0][0]].set_lod(input_lod) + # preserve the output of LodTensor with implicit lod to compare + expect_output = [ + numpy.array(actual_output) for actual_output in self.actual_outputs + ] + self.run_program() + for actual_output in self.actual_outputs: + self.assertTrue( + numpy.allclose( + numpy.array(actual_output), expect_output, atol=0.001)) if __name__ == '__main__': -- GitLab From 5ab271821b54bf7933976cf3f44ccf4546e43991 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 5 Jan 2018 22:18:11 +0800 Subject: [PATCH 0099/2305] fix crash when generating c-api package --- paddle/scripts/docker/build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/scripts/docker/build.sh b/paddle/scripts/docker/build.sh index f1e244772f3..12fd7b1bdaf 100644 --- a/paddle/scripts/docker/build.sh +++ b/paddle/scripts/docker/build.sh @@ -208,7 +208,7 @@ function gen_capi_package() { find ./third_party/install -name 'libiomp5.so' -exec cp {} $install_prefix/usr/local/lib \; cd $install_prefix/usr/local - ls | egrep -v "^Found.*item$" | xargs tar /paddle/build/paddle.tgz + ls | egrep -v "^Found.*item$" | xargs tar -cf /paddle/build/paddle.tgz fi } -- GitLab From df3b250c7000269489522fcaa1f62e18e2f44ee8 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Fri, 5 Jan 2018 14:52:03 +0000 Subject: [PATCH 0100/2305] Fix bad_alloc bug & refine code in profiler --- paddle/platform/profiler.cc | 150 ++++++++++++++++--------------- paddle/platform/profiler.h | 4 + paddle/platform/profiler_test.cc | 6 +- 3 files changed, 88 insertions(+), 72 deletions(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index ea16e560767..abb1e6a89cf 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -15,12 +15,16 @@ limitations under the License. */ #include "paddle/platform/profiler.h" #include #include +#include "gflags/gflags.h" +#include "glog/logging.h" namespace paddle { namespace platform { // The profiler state, the initial value is ProfilerState::kDisabled static ProfilerState g_state = ProfilerState::kDisabled; +// To record which timer the profiler used, CUDA or CPU. +static std::string g_profiler_place = ""; // The thread local event list only can be accessed by the specific thread // The thread index of each thread static thread_local int32_t g_thread_id; @@ -45,10 +49,7 @@ inline uint64_t GetTimeInNsec() { Event::Event(EventKind kind, std::string name, uint32_t thread_id, DeviceContext* dev_ctx) - : kind_(kind), - name_(std::move(name)), - thread_id_(thread_id), - has_cuda_(false) { + : kind_(kind), name_(name), thread_id_(thread_id), has_cuda_(false) { #ifdef PADDLE_WITH_CUDA auto* cuda_dev_ctx = static_cast(dev_ctx); if (cuda_dev_ctx) { @@ -115,22 +116,27 @@ inline EventList& GetEventList() { } void Mark(const std::string& name, DeviceContext* dev_ctx) { - GetEventList().Record(EventKind::kMark, std::move(name), g_thread_id, - dev_ctx); + GetEventList().Record(EventKind::kMark, name, g_thread_id, dev_ctx); +} + +void PushEvent(const std::string& name, DeviceContext* dev_ctx) { + GetEventList().Record(EventKind::kPushRange, name, g_thread_id, dev_ctx); +} + +void PopEvent(const std::string& name, DeviceContext* dev_ctx) { + GetEventList().Record(EventKind::kPopRange, name, g_thread_id, dev_ctx); } RecordEvent::RecordEvent(const std::string& name, DeviceContext* dev_ctx) { if (g_state == ProfilerState::kDisabled) return; dev_ctx_ = dev_ctx; name_ = name; - GetEventList().Record(EventKind::kPushRange, std::move(name), g_thread_id, - dev_ctx_); + PushEvent(name_, dev_ctx_); } RecordEvent::~RecordEvent() { if (g_state == ProfilerState::kDisabled) return; - GetEventList().Record(EventKind::kPopRange, std::move(name_), g_thread_id, - dev_ctx_); + PopEvent(name_, dev_ctx_); } void EnableProfiler(ProfilerState state) { @@ -141,6 +147,7 @@ void EnableProfiler(ProfilerState state) { "The profiling state should be disabled when calling ", "EnableProfiler."); g_state = state; + g_profiler_place = (g_state == ProfilerState::kCUDA) ? "CUDA" : "CPU"; #ifdef PADDLE_WITH_CUDA if (g_state == ProfilerState::kCUDA) { // Generate some dummy evenets first to reduce the startup overhead. @@ -172,56 +179,8 @@ std::vector> DisableProfiler() { return result; } -void PushEvent(const std::string& name, DeviceContext* dev_ctx) { - GetEventList().Record(EventKind::kPushRange, std::move(name), g_thread_id, - dev_ctx); -} - -void PopEvent(const std::string& name, DeviceContext* dev_ctx) { - GetEventList().Record(EventKind::kPopRange, std::move(name), g_thread_id, - dev_ctx); -} - void ParseEvents(std::vector>& events, EventSortingKey sorted_by) { - // Output header information - std::cout << "------------------------->" - << " Profiling Report " - << "<-------------------------" - << "\n\n"; -#ifdef PADDLE_WITH_CUDA - std::cout << "Place: GPU" << std::endl; -#else - std::cout << "Place: CPU" << std::endl; -#endif - std::cout << "Time unit: ms" << std::endl; - std::string sort_domain = "event end time"; - switch (sorted_by) { - case EventSortingKey::kCalls: - sort_domain = "number of calls"; - break; - case EventSortingKey::kTotal: - sort_domain = "total time"; - break; - case EventSortingKey::kMin: - sort_domain = "minimum time"; - break; - case EventSortingKey::kMax: - sort_domain = "maximum time"; - break; - case EventSortingKey::kAve: - sort_domain = "average time"; - break; - default: - if (sorted_by != EventSortingKey::kDefault) { - std::cout << "Warning: unkown sorting key. "; - sorted_by = EventSortingKey::kDefault; - } - } - std::cout << "Sorted by " << sort_domain - << " in descending order in the same thread\n\n"; - - // Parse events std::vector> events_table; size_t max_name_width = 0; for (size_t i = 0; i < events.size(); i++) { @@ -234,19 +193,19 @@ void ParseEvents(std::vector>& events, pushed_events.push_back(events[i][j]); } else if (events[i][j].kind() == "pop") { std::list::reverse_iterator rit = pushed_events.rbegin(); - while (rit->name() != events[i][j].name() && - rit != pushed_events.rend()) { + while (rit != pushed_events.rend() && + rit->name() != events[i][j].name()) { ++rit; } + if (rit != pushed_events.rend()) { -#ifdef PADDLE_WITH_CUDA - double event_time = rit->CudaElapsedMs(events[i][j]); -#else - double event_time = rit->CpuElapsedMs(events[i][j]); -#endif + double event_time = (g_state == ProfilerState::kCUDA) + ? rit->CudaElapsedMs(events[i][j]) + : rit->CpuElapsedMs(events[i][j]); std::string event_name = "thread" + std::to_string(rit->thread_id()) + "::" + rit->name(); max_name_width = std::max(max_name_width, event_name.size()); + if (event_idx.find(event_name) == event_idx.end()) { event_idx[event_name] = event_items.size(); EventItem event_item = {event_name, 1, event_time, @@ -264,11 +223,13 @@ void ParseEvents(std::vector>& events, event_items[index].max_time = std::max(event_time, event_items[index].max_time); } + // remove the start marker from the list pushed_events.erase((++rit).base()); } else { - std::cout << "Warning: can not find the start marker of event " - << events[i][j].name(); + LOG(WARNING) << "Cannot find the push marker of event \'" + << events[i][j].name() + << "\', which will be ignored in profiling report."; } } } @@ -294,19 +255,65 @@ void ParseEvents(std::vector>& events, } }); } + events_table.push_back(event_items); + // To check whether there are events with `push` but without `pop` + std::list::reverse_iterator rit = pushed_events.rbegin(); + while (rit != pushed_events.rend()) { + if (rit->kind() == "push") { + LOG(WARNING) << "Cannot find the pop marker of event \'" << rit->name() + << "\', which will be ignored in profiling report."; + } + ++rit; + } } - // output events table + + // Print report + PrintProfilingReport(events_table, sorted_by, max_name_width + 4, 12); +} + +void PrintProfilingReport(std::vector>& events_table, + EventSortingKey sorted_by, const size_t name_width, + const size_t data_width) { + if (g_profiler_place == "") return; + // Output header information + std::cout << "\n------------------------->" + << " Profiling Report " + << "<-------------------------\n\n"; + std::cout << "Place: " << g_profiler_place << std::endl; + std::cout << "Time unit: ms" << std::endl; + std::string sort_domain = "event end time"; + switch (sorted_by) { + case EventSortingKey::kCalls: + sort_domain = "number of calls"; + break; + case EventSortingKey::kTotal: + sort_domain = "total time"; + break; + case EventSortingKey::kMin: + sort_domain = "minimum time"; + break; + case EventSortingKey::kMax: + sort_domain = "maximum time"; + break; + case EventSortingKey::kAve: + sort_domain = "average time"; + break; + default: + break; + } + std::cout << "Sorted by " << sort_domain + << " in descending order in the same thread\n\n"; + // Output events table std::cout.setf(std::ios::left); - const int data_width = 12; - std::cout << std::setw(max_name_width + 4) << "Event" << std::setw(data_width) + std::cout << std::setw(name_width) << "Event" << std::setw(data_width) << "Calls" << std::setw(data_width) << "Total" << std::setw(data_width) << "Min." << std::setw(data_width) << "Max." << std::setw(data_width) << "Ave." << std::endl; for (size_t i = 0; i < events_table.size(); ++i) { for (size_t j = 0; j < events_table[i].size(); ++j) { EventItem& event_item = events_table[i][j]; - std::cout << std::setw(max_name_width + 4) << event_item.name + std::cout << std::setw(name_width) << event_item.name << std::setw(data_width) << event_item.calls << std::setw(data_width) << event_item.total_time << std::setw(data_width) << event_item.min_time @@ -314,6 +321,7 @@ void ParseEvents(std::vector>& events, << std::setw(data_width) << event_item.ave_time << std::endl; } } + std::cout << std::endl; } } // namespace platform diff --git a/paddle/platform/profiler.h b/paddle/platform/profiler.h index d51ec644321..f97a5867877 100644 --- a/paddle/platform/profiler.h +++ b/paddle/platform/profiler.h @@ -134,5 +134,9 @@ enum EventSortingKey { kDefault, kCalls, kTotal, kMin, kMax, kAve }; void ParseEvents(std::vector>&, EventSortingKey sorted_by = EventSortingKey::kDefault); +// Print results +void PrintProfilingReport(std::vector>& events_table, + EventSortingKey sorted_by, const size_t name_width, + const size_t data_width); } // namespace platform } // namespace paddle diff --git a/paddle/platform/profiler_test.cc b/paddle/platform/profiler_test.cc index 3800f3914f1..13dea713c71 100644 --- a/paddle/platform/profiler_test.cc +++ b/paddle/platform/profiler_test.cc @@ -99,8 +99,12 @@ TEST(RecordEvent, RecordEvent) { int counter = 1; while (counter != i * 1000) counter++; } + + // Bad Usage: + PushEvent("event_without_pop", dev_ctx); + PopEvent("event_without_push", dev_ctx); std::vector> events = paddle::platform::DisableProfiler(); - // Will remove from test before merging + // Will remove parsing-related code from test later ParseEvents(events, EventSortingKey::kTotal); int cuda_startup_count = 0; -- GitLab From 11ed2f2f93edd955cbb1d253d1c7fbeda3048191 Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Fri, 5 Jan 2018 23:04:57 +0800 Subject: [PATCH 0101/2305] package right mkldnn and mklml libs if enabled in capi --- cmake/external/mkldnn.cmake | 5 +++++ cmake/external/mklml.cmake | 4 ++++ paddle/scripts/docker/build.sh | 9 --------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmake/external/mkldnn.cmake b/cmake/external/mkldnn.cmake index b67c559fdf7..471de3858a5 100644 --- a/cmake/external/mkldnn.cmake +++ b/cmake/external/mkldnn.cmake @@ -77,3 +77,8 @@ FILE(WRITE ${dummyfile} "const char * dummy = \"${dummyfile}\";") ADD_LIBRARY(mkldnn STATIC ${dummyfile}) TARGET_LINK_LIBRARIES(mkldnn ${MKLDNN_LIB} ${MKLML_LIB} ${MKLML_IOMP_LIB}) ADD_DEPENDENCIES(mkldnn ${MKLDNN_PROJECT}) + +IF(WITH_C_API) + INSTALL(FILES ${MKLDNN_LIB} DESTINATION lib) +ENDIF() + diff --git a/cmake/external/mklml.cmake b/cmake/external/mklml.cmake index 20dbc32a738..15a07ea3daf 100644 --- a/cmake/external/mklml.cmake +++ b/cmake/external/mklml.cmake @@ -66,3 +66,7 @@ ADD_LIBRARY(mklml SHARED IMPORTED GLOBAL) SET_PROPERTY(TARGET mklml PROPERTY IMPORTED_LOCATION ${MKLML_LIB}) ADD_DEPENDENCIES(mklml ${MKLML_PROJECT}) LIST(APPEND external_project_dependencies mklml) + +IF(WITH_C_API) + INSTALL(FILES ${MKLML_LIB} ${MKLML_IOMP_LIB} DESTINATION lib) +ENDIF() diff --git a/paddle/scripts/docker/build.sh b/paddle/scripts/docker/build.sh index 12fd7b1bdaf..e70d04d9017 100644 --- a/paddle/scripts/docker/build.sh +++ b/paddle/scripts/docker/build.sh @@ -197,16 +197,7 @@ function gen_capi_package() { if [[ ${WITH_C_API} == "ON" ]]; then install_prefix="/paddle/build/capi_output" rm -rf $install_prefix - make DESTDIR="$install_prefix" install - - if [[ ${WITH_MKL:-OFF} == "ON" ]]; then - find ./third_party/install -name 'libmklml_gnu.so' -exec cp {} $install_prefix/usr/local/lib \; - find ./third_party/install -name 'libmklml_intel.so' -exec cp {} $install_prefix/usr/local/lib \; - cp -P ./third_party/install/mkldnn/lib/* $install_prefix/usr/local/lib/ - fi - - find ./third_party/install -name 'libiomp5.so' -exec cp {} $install_prefix/usr/local/lib \; cd $install_prefix/usr/local ls | egrep -v "^Found.*item$" | xargs tar -cf /paddle/build/paddle.tgz fi -- GitLab From 6de20496511f6cd7a032291721392f4ea74a3b7d Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Sat, 6 Jan 2018 00:45:17 +0800 Subject: [PATCH 0102/2305] use the mkldnn shared lib so.0 --- cmake/external/mkldnn.cmake | 10 +++++++++- python/CMakeLists.txt | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/cmake/external/mkldnn.cmake b/cmake/external/mkldnn.cmake index 471de3858a5..89fc34796a0 100644 --- a/cmake/external/mkldnn.cmake +++ b/cmake/external/mkldnn.cmake @@ -78,7 +78,15 @@ ADD_LIBRARY(mkldnn STATIC ${dummyfile}) TARGET_LINK_LIBRARIES(mkldnn ${MKLDNN_LIB} ${MKLML_LIB} ${MKLML_IOMP_LIB}) ADD_DEPENDENCIES(mkldnn ${MKLDNN_PROJECT}) +# copy the real so.0 lib to install dir +# it can be directly contained in wheel or capi +SET(MKLDNN_SHARED_LIB ${MKLDNN_INSTALL_DIR}/libmkldnn.so.0) +ADD_CUSTOM_COMMAND(OUTPUT ${MKLDNN_SHARED_LIB} + COMMAND cp ${MKLDNN_LIB} ${MKLDNN_SHARED_LIB} + DEPENDS mkldnn) +ADD_CUSTOM_TARGET(mkldnn_shared_lib ALL DEPENDS ${MKLDNN_SHARED_LIB}) + IF(WITH_C_API) - INSTALL(FILES ${MKLDNN_LIB} DESTINATION lib) + INSTALL(FILES ${MKLDNN_SHARED_LIB} DESTINATION lib) ENDIF() diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 6f589e91697..36919ab00bf 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -29,8 +29,8 @@ if(WITH_MKLML) endif() if(WITH_MKLDNN) - list(APPEND MKL_SHARED_LIBS "${MKLDNN_LIB}" "${MKLDNN_LIB}.0") - list(APPEND MKL_DEPENDS mkldnn) + list(APPEND MKL_SHARED_LIBS "${MKLDNN_SHARED_LIB}") + list(APPEND MKL_DEPENDS mkldnn mkldnn_shared_lib) endif() if(WITH_GPU) -- GitLab From 787d1d36d00568672e99abbb3fb594af27607b30 Mon Sep 17 00:00:00 2001 From: xuwei06 Date: Fri, 5 Jan 2018 18:22:14 -0800 Subject: [PATCH 0103/2305] Fix the output of conditional_block --- python/paddle/v2/fluid/layers/control_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index acc22bef98b..eacaf7a8e81 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -897,7 +897,7 @@ class ConditionalBlock(object): out_list = [ parent_block.var(var_name) for var_name in parent_block.vars - if var_name not in intermediate + if var_name in intermediate ] step_scope = parent_block.create_var( -- GitLab From 0c2af3a50eb1ee61e38ec94122c38f3b08e595a6 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Sat, 6 Jan 2018 13:02:50 +0800 Subject: [PATCH 0104/2305] update releasing doc --- doc/design/releasing_process.md | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/doc/design/releasing_process.md b/doc/design/releasing_process.md index 14c081ea842..102420d0ed1 100644 --- a/doc/design/releasing_process.md +++ b/doc/design/releasing_process.md @@ -7,11 +7,9 @@ PaddlePaddle每次发新的版本,遵循以下流程: 1. 从`develop`分支派生出新的分支,分支名为`release/版本号`。例如,`release/0.10.0` 1. 将新分支的版本打上tag,tag为`版本号rc.Patch号`。第一个tag为`0.10.0rc1`,第二个为`0.10.0rc2`,依次类推。 1. 对这个版本的提交,做如下几个操作: + * 使用Regression Test List作为检查列表,测试本次release的正确性。 + * 如果失败,记录下所有失败的例子,在这个`release/版本号`分支中,修复所有bug后,Patch号加一,到第二步 * 修改`python/setup.py.in`中的版本信息,并将`istaged`字段设为`True`。 - * 编译这个版本的Docker发行镜像,发布到dockerhub。如果失败,修复Docker编译镜像问题,Patch号加一,返回第二步 - * 编译这个版本的Ubuntu Deb包。如果失败,修复Ubuntu Deb包编译问题,Patch号加一,返回第二步。 - * 使用Regression Test List作为检查列表,测试Docker镜像/ubuntu安装包的功能正确性 - * 如果失败,记录下所有失败的例子,在这个`release/版本号`分支中,修复所有bug后,Patch号加一,返回第二步 * 编译这个版本的python wheel包,并发布到pypi。 * 由于pypi.python.org目前遵循[严格的命名规范PEP 513](https://www.python.org/dev/peps/pep-0513),在使用twine上传之前,需要重命名wheel包中platform相关的后缀,比如将`linux_x86_64`修改成`manylinux1_x86_64`。 * pypi上的package名称为paddlepaddle和paddlepaddle_gpu,如果要上传GPU版本的包,需要修改build/python/setup.py中,name: "paddlepaddle_gpu"并重新打包wheel包:`python setup.py bdist_wheel`。 @@ -21,8 +19,8 @@ PaddlePaddle每次发新的版本,遵循以下流程: pip install twine twine upload dist/[package to upload] ``` + * 编译这个版本的Docker发行镜像,发布到dockerhub。如果失败,修复Docker编译镜像问题,Patch号加一,返回第二步 1. 第三步完成后,将`release/版本号`分支合入master分支,并删除`release/版本号`分支。将master分支的合入commit打上tag,tag为`版本号`。同时再将`master`分支合入`develop`分支。最后删除`release/版本号`分支。 -1. 编译master分支的Docker发行镜像,发布到dockerhub。编译ubuntu的deb包,发布到github release页面 1. 协同完成Release Note的书写 @@ -31,6 +29,30 @@ PaddlePaddle每次发新的版本,遵循以下流程: * `release/版本号`分支一旦建立,一般不允许再从`develop`分支合入`release/版本号`。这样保证`release/版本号`分支功能的封闭,方便测试人员测试PaddlePaddle的行为。 * 在`release/版本号`分支存在的时候,如果有bugfix的行为,需要将bugfix的分支同时merge到`master`, `develop`和`release/版本号`这三个分支。 +## 发布whl包到pypi + +使用[PaddlePaddle CI](https://paddleci.ngrok.io/project.html?projectId=Manylinux1&tab=projectOverview) +完成自动化二进制编译,参考下图,选择需要发布的版本(通常包含一个CPU版本和一个GPU版本),点击"run"右侧的"..."按钮,可以 +弹出下面的选择框,在第二个tab (Changes)里选择需要发布的分支,这里选择0.11.0,然后点击"Run Build"按钮。等待编译完成后 +可以在此页面的"Artifacts"下拉框中找到生成的3个二进制文件,分别对应CAPI,`cp27m`和`cp27mu`的版本。然后按照上述的方法 +使用`twine`工具上传即可。 + + + +* 注:CI环境使用 https://github.com/PaddlePaddle/buildtools 这里的DockerImage作为编译环境以支持更多的Linux + 发型版,如果需要手动编译,也可以使用这些镜像。这些镜像也可以从 https://hub.docker.com/r/paddlepaddle/paddle_manylinux_devel/tags/ 下载得到。 +* pypi不支持覆盖上传,所以一个版本号的whl包发布之后,不可以更改。下一个whl包需要更新版本号才可以上传。 + +## 发布Docker镜像 + +上述PaddlePaddle CI编译whl完成后会自动将Docker镜像push到DockerHub,所以,发布Docker镜像只需要对自动push的镜像打上 +版本号对应的tag即可: + +1. 进入 https://hub.docker.com/r/paddlepaddle/paddle/tags/ 查看latest tag的更新时间是否在上述编译whl包完成后是否最新。 +1. 执行 `docker pull paddlepaddle/paddle:[latest tag]`,latest tag可以是latest或latest-gpu等。 +1. 执行 `docker tag paddlepaddle/paddle:[latest tag] paddlepaddle/paddle:[version]` +1. 执行 `docker push paddlepaddle/paddle:[version]` + ## PaddlePaddle 分支规范 PaddlePaddle开发过程使用[git-flow](http://nvie.com/posts/a-successful-git-branching-model/)分支规范,并适应github的特性做了一些区别。 -- GitLab From 7a4f3be9f3f44781f789b60620a77f3857a6cd15 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Sat, 6 Jan 2018 06:14:48 +0000 Subject: [PATCH 0105/2305] Fix profiler place bug --- paddle/platform/profiler.cc | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index abb1e6a89cf..239df23128e 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -181,6 +181,7 @@ std::vector> DisableProfiler() { void ParseEvents(std::vector>& events, EventSortingKey sorted_by) { + if (g_profiler_place == "") return; std::vector> events_table; size_t max_name_width = 0; for (size_t i = 0; i < events.size(); i++) { @@ -199,7 +200,7 @@ void ParseEvents(std::vector>& events, } if (rit != pushed_events.rend()) { - double event_time = (g_state == ProfilerState::kCUDA) + double event_time = (g_profiler_place == "CUDA") ? rit->CudaElapsedMs(events[i][j]) : rit->CpuElapsedMs(events[i][j]); std::string event_name = @@ -224,7 +225,7 @@ void ParseEvents(std::vector>& events, std::max(event_time, event_items[index].max_time); } - // remove the start marker from the list + // remove the push marker from the list pushed_events.erase((++rit).base()); } else { LOG(WARNING) << "Cannot find the push marker of event \'" @@ -257,13 +258,11 @@ void ParseEvents(std::vector>& events, } events_table.push_back(event_items); - // To check whether there are events with `push` but without `pop` + // log warning if there are events with `push` but without `pop` std::list::reverse_iterator rit = pushed_events.rbegin(); while (rit != pushed_events.rend()) { - if (rit->kind() == "push") { - LOG(WARNING) << "Cannot find the pop marker of event \'" << rit->name() - << "\', which will be ignored in profiling report."; - } + LOG(WARNING) << "Cannot find the pop marker of event \'" << rit->name() + << "\', which will be ignored in profiling report."; ++rit; } } @@ -275,7 +274,6 @@ void ParseEvents(std::vector>& events, void PrintProfilingReport(std::vector>& events_table, EventSortingKey sorted_by, const size_t name_width, const size_t data_width) { - if (g_profiler_place == "") return; // Output header information std::cout << "\n------------------------->" << " Profiling Report " -- GitLab From 2b3ba40e6a504b5526208a945e8fff6594dd7904 Mon Sep 17 00:00:00 2001 From: gx_wind Date: Sat, 6 Jan 2018 17:33:34 +0800 Subject: [PATCH 0106/2305] add adversarial sample --- adversarial/advbox/__init__.py | 17 +++ adversarial/advbox/attacks/base.py | 42 +++++++ adversarial/advbox/attacks/gradientsign.py | 36 ++++++ adversarial/advbox/models/__init__.py | 16 +++ adversarial/advbox/models/base.py | 91 ++++++++++++++ adversarial/advbox/models/paddle.py | 106 ++++++++++++++++ .../advbox/tutorials/tutorial_model.py | 32 +++++ adversarial/fluid_mnist.py | 91 ++++++++++++++ adversarial/mnist_fgsm.py | 113 ++++++++++++++++++ adversarial/mnist_tutorial_fgsm.py | 94 +++++++++++++++ 10 files changed, 638 insertions(+) create mode 100644 adversarial/advbox/__init__.py create mode 100644 adversarial/advbox/attacks/base.py create mode 100644 adversarial/advbox/attacks/gradientsign.py create mode 100644 adversarial/advbox/models/__init__.py create mode 100644 adversarial/advbox/models/base.py create mode 100644 adversarial/advbox/models/paddle.py create mode 100644 adversarial/advbox/tutorials/tutorial_model.py create mode 100644 adversarial/fluid_mnist.py create mode 100644 adversarial/mnist_fgsm.py create mode 100644 adversarial/mnist_tutorial_fgsm.py diff --git a/adversarial/advbox/__init__.py b/adversarial/advbox/__init__.py new file mode 100644 index 00000000000..4beb6be0a20 --- /dev/null +++ b/adversarial/advbox/__init__.py @@ -0,0 +1,17 @@ +# Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" + A set of tools for generating adversarial example on paddle platform +""" diff --git a/adversarial/advbox/attacks/base.py b/adversarial/advbox/attacks/base.py new file mode 100644 index 00000000000..9cc2bfb8543 --- /dev/null +++ b/adversarial/advbox/attacks/base.py @@ -0,0 +1,42 @@ +""" +The base model of the model. +""" +from abc import ABCMeta +#from advbox.base import Model +import abc + +abstractmethod = abc.abstractmethod + +class Attack(object): + """ + Abstract base class for adversarial attacks. `Attack` represent an adversarial attack + which search an adversarial example. subclass should implement the _apply() method. + + Args: + model(Model): an instance of the class advbox.base.Model. + + """ + __metaclass__ = ABCMeta + + def __init__(self, model): + self.model = model + + def __call__(self, image_batch): + """ + Generate the adversarial sample. + + Args: + image_batch(list): The image and label tuple list. + """ + adv_img = self._apply(image_batch) + return adv_img + + @abstractmethod + def _apply(self, image_batch): + """ + Search an adversarial example. + + Args: + image_batch(list): The image and label tuple list. + """ + raise NotImplementedError diff --git a/adversarial/advbox/attacks/gradientsign.py b/adversarial/advbox/attacks/gradientsign.py new file mode 100644 index 00000000000..6c188f62498 --- /dev/null +++ b/adversarial/advbox/attacks/gradientsign.py @@ -0,0 +1,36 @@ +""" +This module provide the attack method for FGSM's implement. +""" +from __future__ import division +import numpy as np +from collections import Iterable +from .base import Attack + +class GradientSignAttack(Attack): + """ + This attack was originally implemented by Goodfellow et al. (2015) with the + infinity norm (and is known as the "Fast Gradient Sign Method"). This is therefore called + the Fast Gradient Method. + Paper link: https://arxiv.org/abs/1412.6572 + """ + + def _apply(self, image_batch, epsilons=1000): + pre_label = np.argmax(self.model.predict(image_batch)) + + min_, max_ = self.model.bounds() + gradient = self.model.gradient(image_batch) + gradient_sign = np.sign(gradient) * (max_ - min_) + + if not isinstance(epsilons, Iterable): + epsilons = np.linspace(0, 1, num = epsilons + 1) + + for epsilon in epsilons: + adv_img = image_batch[0][0].reshape(gradient_sign.shape) + epsilon * gradient_sign + adv_img = np.clip(adv_img, min_, max_) + adv_label = np.argmax(self.model.predict([(adv_img, 0)])) + #print("pre_label="+str(pre_label)+ " adv_label="+str(adv_label)) + if pre_label != adv_label: + #print(epsilon, pre_label, adv_label) + return adv_img + +FGSM = GradientSignAttack diff --git a/adversarial/advbox/models/__init__.py b/adversarial/advbox/models/__init__.py new file mode 100644 index 00000000000..eee0f6efd47 --- /dev/null +++ b/adversarial/advbox/models/__init__.py @@ -0,0 +1,16 @@ +# Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" +Paddle model for target of attack +""" diff --git a/adversarial/advbox/models/base.py b/adversarial/advbox/models/base.py new file mode 100644 index 00000000000..91b6fe4a3c9 --- /dev/null +++ b/adversarial/advbox/models/base.py @@ -0,0 +1,91 @@ +""" +The base model of the model. +""" +from abc import ABCMeta +import abc + +abstractmethod = abc.abstractmethod + +class Model(object): + + """ + Base class of model to provide attack. + + + Args: + bounds(tuple): The lower and upper bound for the image pixel. + channel_axis(int): The index of the axis that represents the color channel. + preprocess(tuple): Two element tuple used to preprocess the input. First + substract the first element, then divide the second element. + """ + __metaclass__ = ABCMeta + + def __init__(self, bounds, channel_axis, preprocess=None): + assert len(bounds) == 2 + assert channel_axis in [0, 1, 2, 3] + + if preprocess is None: + preprocess = (0, 1) + self._bounds = bounds + self._channel_axis = channel_axis + self._preprocess = preprocess + + def bounds(self): + """ + Return the upper and lower bounds of the model. + """ + return self._bounds + + def channel_axis(self): + """ + Return the channel axis of the model. + """ + return self._channel_axis + + def _process_input(self, input_): + res = input_ + sub, div = self._preprocess + if sub != 0: + res = input_ - sub + assert div != 0 + if div != 1: + res /= div + return res + + @abstractmethod + def predict(self, image_batch): + """ + Calculate the prediction of the image batch. + + Args: + image_batch(numpy.ndarray): image batch of shape (batch_size, height, width, channels). + + Return: + numpy.ndarray: predictions of the images with shape (batch_size, num_of_classes). + """ + raise NotImplementedError + + @abstractmethod + def num_classes(self): + """ + Determine the number of the classes + + Return: + int: the number of the classes + """ + raise NotImplementedError + + @abstractmethod + def gradient(self, image_batch): + """ + Calculate the gradient of the cross-entropy loss w.r.t the image. + + Args: + image(numpy.ndarray): image with shape (height, width, channel) + label(int): image label used to cal gradient. + + Return: + numpy.ndarray: gradient of the cross-entropy loss w.r.t the image with + the shape (height, width, channel). + """ + raise NotImplementedError diff --git a/adversarial/advbox/models/paddle.py b/adversarial/advbox/models/paddle.py new file mode 100644 index 00000000000..831fa6a3627 --- /dev/null +++ b/adversarial/advbox/models/paddle.py @@ -0,0 +1,106 @@ +from __future__ import absolute_import + +import numpy as np +import paddle.v2 as paddle +import paddle.v2.fluid as fluid +from paddle.v2.fluid.framework import program_guard + +from .base import Model + +class PaddleModel(Model): + """ + Create a PaddleModel instance. + When you need to generate a adversarial sample, you should construct an instance of PaddleModel. + + Args: + program(paddle.v2.fluid.framework.Program): The program of the model which generate the adversarial sample. + input_name(string): The name of the input. + logits_name(string): The name of the logits. + predict_name(string): The name of the predict. + cost_name(string): The name of the loss in the program. + """ + + def __init__(self, + program, + input_name, + logits_name, + predict_name, + cost_name, + bounds, + channel_axis=3, + preprocess=None): + super(PaddleModel, self).__init__( + bounds=bounds, + channel_axis=channel_axis, + preprocess=preprocess) + + if preprocess is None: + preprocess = (0, 1) + + self._program = program + self._place = fluid.CPUPlace() + self._exe = fluid.Executor(self._place) + + self._input_name = input_name + self._logits_name = logits_name + self._predict_name = predict_name + self._cost_name = cost_name + + # gradient + loss = self._program.block(0).var(self._cost_name) + param_grads = fluid.backward.append_backward(loss, parameter_list=[self._input_name]) + self._gradient = param_grads[0][1] + + def predict(self, image_batch): + """ + Predict the label of the image_batch. + + Args: + image_batch(list): The image and label tuple list. + Return: + numpy.ndarray: predictions of the images with shape (batch_size, num_of_classes). + """ + feeder = fluid.DataFeeder( + feed_list=[self._input_name, self._logits_name], + place=self._place, + program=self._program + ) + predict_var = self._program.block(0).var(self._predict_name) + predict = self._exe.run( + self._program, + feed=feeder.feed(image_batch), + fetch_list=[predict_var] + ) + return predict + + def num_classes(self): + """ + Calculate the number of classes of the output label. + + Return: + int: the number of classes + """ + predict_var = self._program.block(0).var(self._predict_name) + assert len(predict_var.shape) == 2 + return predict_var.shape[1] + + def gradient(self, image_batch): + """ + Calculate the gradient of the loss w.r.t the input. + + Args: + image_batch(list): The image and label tuple list. + Return: + list: The list of the gradient of the image. + """ + feeder = fluid.DataFeeder( + feed_list=[self._input_name, self._logits_name], + place=self._place, + program=self._program + ) + + grad, = self._exe.run( + self._program, + feed=feeder.feed(image_batch), + fetch_list=[self._gradient]) + return grad diff --git a/adversarial/advbox/tutorials/tutorial_model.py b/adversarial/advbox/tutorials/tutorial_model.py new file mode 100644 index 00000000000..425f09a0569 --- /dev/null +++ b/adversarial/advbox/tutorials/tutorial_model.py @@ -0,0 +1,32 @@ +################################################################################ +# +# Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved +# +################################################################################ +""" + +A pure Paddlepaddle implementation of a neural network. + +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals + +import paddle.v2 as paddle +import paddle.v2.fluid as fluid +from advbox import Model + +def main(): + """ + example main function + """ + model_dir = "./mnist_model" + place = fluid.CPUPlace() + exe = fluid.Executor(place) + program, feed_var_names, fetch_vars = fluid.io.load_inferfence_model(model_dir, exe) + print(program) + +if __name__ == "__main__": + main() diff --git a/adversarial/fluid_mnist.py b/adversarial/fluid_mnist.py new file mode 100644 index 00000000000..d46defda55e --- /dev/null +++ b/adversarial/fluid_mnist.py @@ -0,0 +1,91 @@ +""" +CNN on mnist data using fluid api of paddlepaddle +""" +import paddle.v2 as paddle +import paddle.v2.fluid as fluid + +def mnist_cnn_model(img): + """ + Mnist cnn model + + Args: + img(Varaible): the input image to be recognized + + Returns: + Variable: the label prediction + """ + #conv1 = fluid.nets.conv2d() + conv_pool_1 = fluid.nets.simple_img_conv_pool( + input=img, + num_filters=20, + filter_size=5, + pool_size=2, + pool_stride=2, + act='relu') + + conv_pool_2 = fluid.nets.simple_img_conv_pool( + input=conv_pool_1, + num_filters=50, + filter_size=5, + pool_size=2, + pool_stride=2, + act='relu') + + logits = fluid.layers.fc( + input=conv_pool_2, + size=10, + act='softmax') + return logits + + +def main(): + """ + Train the cnn model on mnist datasets + """ + img = fluid.layers.data(name='img', shape=[1, 28, 28], dtype='float32') + label = fluid.layers.data(name='label', shape=[1], dtype='int64') + logits = mnist_cnn_model(img) + cost = fluid.layers.cross_entropy(input=logits, label=label) + avg_cost = fluid.layers.mean(x=cost) + optimizer = fluid.optimizer.Adam(learning_rate=0.01) + optimizer.minimize(avg_cost) + + accuracy = fluid.evaluator.Accuracy(input=logits, label=label) + + BATCH_SIZE = 50 + PASS_NUM = 3 + ACC_THRESHOLD = 0.98 + LOSS_THRESHOLD = 10.0 + train_reader = paddle.batch( + paddle.reader.shuffle( + paddle.dataset.mnist.train(), buf_size=500), + batch_size=BATCH_SIZE) + + place = fluid.CPUPlace() + exe = fluid.Executor(place) + feeder = fluid.DataFeeder(feed_list=[img, label], place=place) + exe.run(fluid.default_startup_program()) + + for pass_id in range(PASS_NUM): + accuracy.reset(exe) + for data in train_reader(): + loss, acc = exe.run(fluid.default_main_program(), + feed=feeder.feed(data), + fetch_list=[avg_cost] + accuracy.metrics) + pass_acc = accuracy.eval(exe) + print("pass_id=" + str(pass_id) + " acc=" + str(acc) + " pass_acc=" + + str(pass_acc)) + # print loss, acc + if loss < LOSS_THRESHOLD and pass_acc > ACC_THRESHOLD: + # if avg cost less than 10.0 and accuracy is larger than 0.9, we think our code is good. + break +# exit(0) + + pass_acc = accuracy.eval(exe) + print("pass_id=" + str(pass_id) + " pass_acc=" + str(pass_acc)) + fluid.io.save_params(exe, dirname='./mnist', main_program=fluid.default_main_program()) + print('train mnist done') + exit(1) + +if __name__ == '__main__': + main() diff --git a/adversarial/mnist_fgsm.py b/adversarial/mnist_fgsm.py new file mode 100644 index 00000000000..187f37b82ef --- /dev/null +++ b/adversarial/mnist_fgsm.py @@ -0,0 +1,113 @@ +""" +This attack was originally implemented by Goodfellow et al. (2015) with the +infinity norm (and is known as the "Fast Gradient Sign Method"). This is therefore called +the Fast Gradient Method. +Paper link: https://arxiv.org/abs/1412.6572 +""" + +import numpy as np +import paddle.v2 as paddle +import paddle.v2.fluid as fluid + +BATCH_SIZE = 50 +PASS_NUM = 1 +EPS = 0.3 +CLIP_MIN = -1 +CLIP_MAX = 1 +PASS_NUM = 1 + +def mnist_cnn_model(img): + """ + Mnist cnn model + + Args: + img(Varaible): the input image to be recognized + + Returns: + Variable: the label prediction + """ + #conv1 = fluid.nets.conv2d() + conv_pool_1 = fluid.nets.simple_img_conv_pool( + input=img, + num_filters=20, + filter_size=5, + pool_size=2, + pool_stride=2, + act='relu') + + conv_pool_2 = fluid.nets.simple_img_conv_pool( + input=conv_pool_1, + num_filters=50, + filter_size=5, + pool_size=2, + pool_stride=2, + act='relu') + + logits = fluid.layers.fc( + input=conv_pool_2, + size=10, + act='softmax') + return logits + + +def main(): + """ + Generate adverserial example and evaluate accuracy on mnist using FGSM + """ + + images = fluid.layers.data(name='pixel', shape=[1, 28, 28], dtype='float32') + # The gradient should flow + images.stop_gradient = False + label = fluid.layers.data(name='label', shape=[1], dtype='int64') + + predict = mnist_cnn_model(images) + cost = fluid.layers.cross_entropy(input=predict, label=label) + avg_cost = fluid.layers.mean(x=cost) + + # Cal gradient of input + params_grads = fluid.backward.append_backward_ops(avg_cost, parameter_list=['pixel']) + # data batch + train_reader = paddle.batch( + paddle.reader.shuffle( + paddle.dataset.mnist.train(), buf_size=500), + batch_size=BATCH_SIZE) + + accuracy = fluid.evaluator.Accuracy(input=predict, label=label) + place = fluid.CPUPlace() + exe = fluid.Executor(place) + accuracy.reset(exe) + #exe.run(fluid.default_startup_program()) + feeder = fluid.DataFeeder(feed_list=[images, label], place=place) + for pass_id in range(PASS_NUM): + fluid.io.load_params(exe, "./mnist/", main_program=fluid.default_main_program()) + for data in train_reader(): + # cal gradient and eval accuracy + ps, acc = exe.run( + fluid.default_main_program(), + feed=feeder.feed(data), + fetch_list=[params_grads[0][1]]+accuracy.metrics) + labels = [] + for idx, _ in enumerate(data): + labels.append(data[idx][1]) + # generate adversarial example + batch_num = ps.shape[0] + new_data = [] + for i in range(batch_num): + adv_img = np.reshape(data[0][0], (1, 28, 28)) + EPS * np.sign(ps[i]) + adv_img = np.clip(adv_img, CLIP_MIN, CLIP_MAX) + #adv_imgs.append(adv_img) + t = (adv_img, data[0][1]) + new_data.append(t) + + # predict label + predict_label, = exe.run( + fluid.default_main_program(), + feed=feeder.feed(new_data), + fetch_list=[predict]) + adv_labels = np.argmax(predict_label, axis=1) + batch_accuracy = np.mean(np.equal(labels, adv_labels)) + print "pass_id=" + str(pass_id) + " acc=" + str(acc)+ " adv_acc=" + str(batch_accuracy) + + +if __name__ == "__main__": + main() diff --git a/adversarial/mnist_tutorial_fgsm.py b/adversarial/mnist_tutorial_fgsm.py new file mode 100644 index 00000000000..665062afd0e --- /dev/null +++ b/adversarial/mnist_tutorial_fgsm.py @@ -0,0 +1,94 @@ +""" +FGSM demos on mnist using advbox tool. +""" +import paddle.v2 as paddle +import paddle.v2.fluid as fluid +import matplotlib.pyplot as plt +import numpy as np + +from advbox.models.paddle import PaddleModel +from advbox.attacks.gradientsign import GradientSignAttack + +def cnn_model(img): + """ + Mnist cnn model + Args: + img(Varaible): the input image to be recognized + Returns: + Variable: the label prediction + """ + #conv1 = fluid.nets.conv2d() + conv_pool_1 = fluid.nets.simple_img_conv_pool( + input=img, + num_filters=20, + filter_size=5, + pool_size=2, + pool_stride=2, + act='relu') + + conv_pool_2 = fluid.nets.simple_img_conv_pool( + input=conv_pool_1, + num_filters=50, + filter_size=5, + pool_size=2, + pool_stride=2, + act='relu') + + logits = fluid.layers.fc( + input=conv_pool_2, + size=10, + act='softmax') + return logits + + +def main(): + """ + Advbox demo which demonstrate how to use advbox. + """ + IMG_NAME = 'img' + LABEL_NAME = 'label' + + img = fluid.layers.data(name=IMG_NAME, shape=[1, 28, 28], dtype='float32') + # gradient should flow + img.stop_gradient = False + label = fluid.layers.data(name=LABEL_NAME, shape=[1], dtype='int64') + logits = cnn_model(img) + cost = fluid.layers.cross_entropy(input=logits, label=label) + avg_cost = fluid.layers.mean(x=cost) + + place = fluid.CPUPlace() + exe = fluid.Executor(place) + + BATCH_SIZE = 1 + train_reader = paddle.batch( + paddle.reader.shuffle( + paddle.dataset.mnist.train(), buf_size=500), + batch_size=BATCH_SIZE) + feeder = fluid.DataFeeder( + feed_list=[IMG_NAME, LABEL_NAME], + place=place, + program=fluid.default_main_program() + ) + + fluid.io.load_params(exe, "./mnist/", main_program=fluid.default_main_program()) + + # advbox demo + m = PaddleModel( + fluid.default_main_program(), + IMG_NAME, + LABEL_NAME, + logits.name, + avg_cost.name, + (-1, 1) + ) + att = GradientSignAttack(m) + for data in train_reader(): + # fgsm attack + adv_img = att(data) + plt.imshow(n[0][0], cmap='Greys_r') + plt.show() + #np.save('adv_img', adv_img) + break + +if __name__ == '__main__': + main() -- GitLab From 35210a044d046a69a6869c53022ed7c95236f382 Mon Sep 17 00:00:00 2001 From: gx_wind Date: Sat, 6 Jan 2018 17:35:41 +0800 Subject: [PATCH 0107/2305] delete unused files --- .../advbox/tutorials/tutorial_model.py | 32 ----- adversarial/mnist_fgsm.py | 113 ------------------ 2 files changed, 145 deletions(-) delete mode 100644 adversarial/advbox/tutorials/tutorial_model.py delete mode 100644 adversarial/mnist_fgsm.py diff --git a/adversarial/advbox/tutorials/tutorial_model.py b/adversarial/advbox/tutorials/tutorial_model.py deleted file mode 100644 index 425f09a0569..00000000000 --- a/adversarial/advbox/tutorials/tutorial_model.py +++ /dev/null @@ -1,32 +0,0 @@ -################################################################################ -# -# Copyright (c) 2017 Baidu.com, Inc. All Rights Reserved -# -################################################################################ -""" - -A pure Paddlepaddle implementation of a neural network. - -""" - -from __future__ import absolute_import -from __future__ import division -from __future__ import print_function -from __future__ import unicode_literals - -import paddle.v2 as paddle -import paddle.v2.fluid as fluid -from advbox import Model - -def main(): - """ - example main function - """ - model_dir = "./mnist_model" - place = fluid.CPUPlace() - exe = fluid.Executor(place) - program, feed_var_names, fetch_vars = fluid.io.load_inferfence_model(model_dir, exe) - print(program) - -if __name__ == "__main__": - main() diff --git a/adversarial/mnist_fgsm.py b/adversarial/mnist_fgsm.py deleted file mode 100644 index 187f37b82ef..00000000000 --- a/adversarial/mnist_fgsm.py +++ /dev/null @@ -1,113 +0,0 @@ -""" -This attack was originally implemented by Goodfellow et al. (2015) with the -infinity norm (and is known as the "Fast Gradient Sign Method"). This is therefore called -the Fast Gradient Method. -Paper link: https://arxiv.org/abs/1412.6572 -""" - -import numpy as np -import paddle.v2 as paddle -import paddle.v2.fluid as fluid - -BATCH_SIZE = 50 -PASS_NUM = 1 -EPS = 0.3 -CLIP_MIN = -1 -CLIP_MAX = 1 -PASS_NUM = 1 - -def mnist_cnn_model(img): - """ - Mnist cnn model - - Args: - img(Varaible): the input image to be recognized - - Returns: - Variable: the label prediction - """ - #conv1 = fluid.nets.conv2d() - conv_pool_1 = fluid.nets.simple_img_conv_pool( - input=img, - num_filters=20, - filter_size=5, - pool_size=2, - pool_stride=2, - act='relu') - - conv_pool_2 = fluid.nets.simple_img_conv_pool( - input=conv_pool_1, - num_filters=50, - filter_size=5, - pool_size=2, - pool_stride=2, - act='relu') - - logits = fluid.layers.fc( - input=conv_pool_2, - size=10, - act='softmax') - return logits - - -def main(): - """ - Generate adverserial example and evaluate accuracy on mnist using FGSM - """ - - images = fluid.layers.data(name='pixel', shape=[1, 28, 28], dtype='float32') - # The gradient should flow - images.stop_gradient = False - label = fluid.layers.data(name='label', shape=[1], dtype='int64') - - predict = mnist_cnn_model(images) - cost = fluid.layers.cross_entropy(input=predict, label=label) - avg_cost = fluid.layers.mean(x=cost) - - # Cal gradient of input - params_grads = fluid.backward.append_backward_ops(avg_cost, parameter_list=['pixel']) - # data batch - train_reader = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.mnist.train(), buf_size=500), - batch_size=BATCH_SIZE) - - accuracy = fluid.evaluator.Accuracy(input=predict, label=label) - place = fluid.CPUPlace() - exe = fluid.Executor(place) - accuracy.reset(exe) - #exe.run(fluid.default_startup_program()) - feeder = fluid.DataFeeder(feed_list=[images, label], place=place) - for pass_id in range(PASS_NUM): - fluid.io.load_params(exe, "./mnist/", main_program=fluid.default_main_program()) - for data in train_reader(): - # cal gradient and eval accuracy - ps, acc = exe.run( - fluid.default_main_program(), - feed=feeder.feed(data), - fetch_list=[params_grads[0][1]]+accuracy.metrics) - labels = [] - for idx, _ in enumerate(data): - labels.append(data[idx][1]) - # generate adversarial example - batch_num = ps.shape[0] - new_data = [] - for i in range(batch_num): - adv_img = np.reshape(data[0][0], (1, 28, 28)) + EPS * np.sign(ps[i]) - adv_img = np.clip(adv_img, CLIP_MIN, CLIP_MAX) - #adv_imgs.append(adv_img) - t = (adv_img, data[0][1]) - new_data.append(t) - - # predict label - predict_label, = exe.run( - fluid.default_main_program(), - feed=feeder.feed(new_data), - fetch_list=[predict]) - adv_labels = np.argmax(predict_label, axis=1) - batch_accuracy = np.mean(np.equal(labels, adv_labels)) - print "pass_id=" + str(pass_id) + " acc=" + str(acc)+ " adv_acc=" + str(batch_accuracy) - - -if __name__ == "__main__": - main() -- GitLab From bbb03fceb38b63ece9f864e1cf7aa346ba207056 Mon Sep 17 00:00:00 2001 From: gx_wind Date: Sat, 6 Jan 2018 18:02:05 +0800 Subject: [PATCH 0108/2305] add readme --- adversarial/README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 adversarial/README.md diff --git a/adversarial/README.md b/adversarial/README.md new file mode 100644 index 00000000000..7c9502828f3 --- /dev/null +++ b/adversarial/README.md @@ -0,0 +1,3 @@ +# Advbox + +Advbox is a Python toolbox to create adversarial examples that fool neural networks. It requires Python and paddle. \ No newline at end of file -- GitLab From e85c51330700a4125f8574e8c0927407c6d9e3d0 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Sun, 7 Jan 2018 13:14:54 +0000 Subject: [PATCH 0109/2305] Add sequencee erase operator --- paddle/operators/sequence_erase_op.cc | 61 ++++++++++++++ paddle/operators/sequence_erase_op.h | 80 +++++++++++++++++++ .../v2/fluid/tests/test_sequence_erase_op.py | 58 ++++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 paddle/operators/sequence_erase_op.cc create mode 100644 paddle/operators/sequence_erase_op.h create mode 100644 python/paddle/v2/fluid/tests/test_sequence_erase_op.py diff --git a/paddle/operators/sequence_erase_op.cc b/paddle/operators/sequence_erase_op.cc new file mode 100644 index 00000000000..e611ef0571d --- /dev/null +++ b/paddle/operators/sequence_erase_op.cc @@ -0,0 +1,61 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/sequence_erase_op.h" + +namespace paddle { +namespace operators { + +class SequenceEraseOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("X"), + "Input(X) of SequenceEraseOp should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("Out"), + "Output(Out) of SequenceEraseOp should not be null."); + ctx->SetOutputDim("Out", ctx->GetInputDim("X")); + } +}; + +class SequenceEraseOpMaker : public framework::OpProtoAndCheckerMaker { + public: + SequenceEraseOpMaker(OpProto* proto, OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("X", + "(LoDTensor) 2-D input LoDTensor with the 2-nd dimension " + "of length 1."); + AddOutput("Out", + "(LoDTensor) 2-D output LoDTensor with the 2-nd dimension " + "of length 1."); + AddAttr>("tokens", + "(vector) " + "Tokens to be removed from input."); + AddComment(R"DOC( +Sequence Erase Operator. + +)DOC"); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP_WITHOUT_GRADIENT(sequence_erase, ops::SequenceEraseOp, + ops::SequenceEraseOpMaker); +REGISTER_OP_CPU_KERNEL( + sequence_erase, + ops::SequenceEraseKernel); diff --git a/paddle/operators/sequence_erase_op.h b/paddle/operators/sequence_erase_op.h new file mode 100644 index 00000000000..937b9870aa9 --- /dev/null +++ b/paddle/operators/sequence_erase_op.h @@ -0,0 +1,80 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/framework/op_registry.h" +#include "paddle/operators/math/softmax.h" + +namespace paddle { +namespace operators { + +using Tensor = framework::Tensor; +using LoDTensor = framework::LoDTensor; + +template +class SequenceEraseKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* in = ctx.Input("X"); + auto* out = ctx.Output("Out"); + + auto lod = in->lod(); + PADDLE_ENFORCE_EQ(lod.size(), 1UL, "Only support one level sequence now."); + // auto dims = x->dims(); + /* + const size_t level = lod.size() - 1; + PADDLE_ENFORCE_EQ(dims[0], static_cast(lod[level].back()), + "The first dimension of Input(X) should be equal to the " + "sum of all sequences' lengths."); + PADDLE_ENFORCE_EQ(dims[0], x->numel(), + "The width of each timestep in Input(X) of " + "SequenceEraseOp should be 1."); + out->mutable_data(ctx.GetPlace()); + */ + auto tokens = ctx.Attr>("tokens"); + auto in_len = in->numel(); + auto in_dat = in->data(); + auto lod0 = lod[0]; + std::vector num_erased(in_len + 1, 0); + for (int64_t i = 1; i < in_len + 1; ++i) { + num_erased[i] = num_erased[i - 1]; + if (std::find(tokens.begin(), tokens.end(), in_dat[i - 1]) != + tokens.end()) { + num_erased[i] += 1; + } + } + + std::vector out_lod0(lod0.size(), 0); + for (size_t i = 1; i < lod0.size(); ++i) { + out_lod0[i] = lod0[i] - num_erased[lod0[i]]; + } + + auto out_len = in_len - num_erased[in_len]; + out->Resize({static_cast(out_len), 1}); + auto out_dat = out->mutable_data(ctx.GetPlace()); + + for (size_t i = 0; i < in_len; ++i) { + if (num_erased[i] == num_erased[i + 1]) { + out_dat[i - num_erased[i]] = in_dat[i]; + } + } + framework::LoD out_lod; + out_lod.push_back(out_lod0); + out->set_lod(out_lod); + } +}; + +} // namespace operators +} // namespace paddle diff --git a/python/paddle/v2/fluid/tests/test_sequence_erase_op.py b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py new file mode 100644 index 00000000000..74274cf0ad4 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py @@ -0,0 +1,58 @@ +import unittest +import numpy as np +from op_test import OpTest + + +def sequence_erase(in_seq, lod0, tokens): + # num_erased[i]: the number of elments to be removed before #i elements + num_erased = [0] * (len(in_seq) + 1) + for i in range(1, len(in_seq) + 1): + num_erased[i] = num_erased[i - 1] + if in_seq[i - 1] in tokens: + num_erased[i] += 1 + + # recalculate lod information + new_lod0 = [0] * len(lod0) + for i in range(1, len(lod0)): + new_lod0[i] = lod0[i] - num_erased[lod0[i]] + + out_seq = np.zeros( + (len(in_seq) - num_erased[len(in_seq)], 1)).astype("int32") + for i in range(0, len(in_seq)): + if num_erased[i] == num_erased[i + 1]: + out_seq[i - num_erased[i]] = in_seq[i] + # else in_seq[i] needs to be removed + return out_seq, new_lod0 + + +class TestSequenceEraseOp(OpTest): + def setUp(self): + self.op_type = "sequence_erase" + in_seq = np.random.randint(0, 10, (30, 1)).astype("int32") + lod = [[0, 5, 15, 30]] + tokens = [2, 5] + out_seq, new_lod0 = sequence_erase(in_seq, lod[0], tokens) + + self.attrs = {'tokens': tokens} + self.inputs = {'X': (in_seq, lod)} + self.outputs = {'Out': (out_seq, [new_lod0])} + + def test_check_output(self): + self.check_output() + + +if __name__ == '__main__': + """ + in_seq = np.random.randint(0, 10, (30, 1)).astype("int32") + lod0 = [0, 5, 15, 30] + tokens = [2, 5] + out_seq, new_lod = sequence_erase(in_seq, lod0, tokens) + + print lod0, new_lod + print("compare") + for i in range(0, len(lod0)-1): + print(np.transpose(in_seq[lod0[i] : lod0[i+1]])) + print(np.transpose(out_seq[new_lod[i] : new_lod[i+1]])) + print("\n") + """ + unittest.main() -- GitLab From b865545a00f23dc3e05ec8b04f5ec938c30e820f Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Sun, 7 Jan 2018 22:46:16 +0800 Subject: [PATCH 0110/2305] follow comments --- doc/design/ci_build_whl.png | Bin 0 -> 287162 bytes doc/design/releasing_process.md | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 doc/design/ci_build_whl.png diff --git a/doc/design/ci_build_whl.png b/doc/design/ci_build_whl.png new file mode 100644 index 0000000000000000000000000000000000000000..232762b82a9ae3e979a1f38a7beb715c87438f40 GIT binary patch literal 287162 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%0A&-&{?FF24yJ8Go;#1U~d$&ISRCpipW0 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�p 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 RmJ7s}qP*I(vM0tL{|BvGA$ +
图1. C-API使用流程示意图 +

+ +- 准备预测模型 + 1. 将神经网络模型结构进行序列化。 + - 调用C-API预测时,需要提供序列化之后的网络结构和训练好的模型参数文件。 + 1. 将PaddlePaddle训练出的模型参数文件(多个)合并成一个文件。 + - 神经网络模型结构和训练好的模型将被序列化合并入一个文件。 + - 预测时只需加载这一个文件,便于发布。 + - **注意**:以上两种方式只需选择其一即可。 +- 调用 PaddlePaddle C-API 开发预测序 + 1. 初始化PaddlePaddle运行环境。 + 1. 加载模型。 + 1. 创建神经网络的输入,组织输入数据。 + 1. 进行前向计算,获得计算结果。 + 1. 清理。 + +### 准备预测模型 + +在准备预测模型部分,我们以手写数字识别任务为例,这个任务定义了一个含有[两个隐层的简单全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。完整代码可以查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 中的相关脚本。 + +调用C-API开发预测程序需要一个训练好的模型,在终端执行`python mnist_v2.py` +运行[目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 会使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。训练好的模型默认保存在当前运行目录下的`models`目录中。 + +下面,我们将训练好的模型转换成预测模型。 + +1. 序列化神经网络模型配置 + + PaddlePaddle 使用 protobuf 来传输网络配置文件中定义的网络结构和相关参数,在使用 C-API 进行预测时,也需将网络结构使用 protobuf 进行序列化,写入文件中。 + + 调用`paddle.utils.dump_v2_config`中的`dump_v2_config`函数能够将使用 PaddlePaddle V2 API 定义的神经网络结构 dump 到指定文件中。示例代码如下: + + ```python + from paddle.utils.dump_v2_config import dump_v2_config + from mnist_v2 import network + + predict = network(is_infer=True) + dump_v2_config(predict, "trainer_config.bin", True) + ``` + + 对本例,或运行 `python mnist_v2.py --task dump_config`,会对示例中的网络结构进行序列化,并将结果写入当前目录下的`trainer_config.bin`文件中。 + + 使用这种方式,需要**在运行时将神经网络的多个可学习参数放在同一个目录中**,C-API可以通过分别指定序列化后的网络结构文件和参数目录来加载训练好的模型。 + +2. 合并模型文件(可选) + + 一些情况下为了便于发布,希望能够将序列化后的神经网络结构和训练好的模型参数打包进一个文件,这时可以使用`paddle.utils.merge_model`中的`merge_v2_model`接口对神经网络结构和训练好的参数进行序列化,将序列化结果写入一个文件内。 + + 代码示例如下: + + ```python + from paddle.utils.merge_model import merge_v2_modelss + from mnist_v2 import network + + net = network(is_infer=True) + param_file = "models/params_pass_4.tar" + output_file = "output.paddle.model" + merge_v2_model(net, param_file, output_file) + ``` + 对本例,或者直接运行 `python merge_v2_model.py`,序列化结果将会写入当前目录下的`output.paddle.model`文件中。使用这种方式,运行时C-API可以通过指定output.paddle.model文件来加载模型。 + +#### 注意事项 +1. 将训练模型转换成预测模型,需要序列化神经网络结构。在调用`dump_v2_config`时,参数`binary`必须指定为`True`。 +1. **预测使用的网络结构往往不同于训练**,通常需要去掉网络中的:(1)类别标签层;(2)损失函数层;(3)`evaluator`等,只留下核心计算层,请注意是否需要修改网络结构。 +1. 预测时,可以获取网络中定义的任意多个(大于等于一个)层前向计算的结果,需要哪些层的计算结果作为输出,就将这些层加入一个Python list中,作为调用`dump_v2_config`的第一个参数。 + +### 编写预测代码 + +预测代码更多详细示例代码请参考[C-API使用示例](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference) 目录下的代码示例。这一节对图1中预测代码编写的5个步骤进行介绍和说明。 + +#### step 1. 初始化PaddlePaddle运行环境 +第一步需调用[`paddle_init`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/main.h#L27) 初始化PaddlePaddle运行环境。该接口接受两个参数:参数的个数和参数列表。 + +#### step2. 加载模型 + +这里介绍C-API使用中的一个重要概念:Gradient Machine。概念上,在 PaddlePaddle 内部一个GradientMachine类的对象管理着一组计算层(PaddlePaddle Layers)来完成前向和反向计算,并处理与之相关的所有细节。在调用C-API预测时,只需进行前向计算而无需调用反向计算。这篇文档的之后部分我们会使用`gradient machine`来特指调用PaddlePaddle C-API创建的GradientMachine类的对象。 + +每一个 `gradient machine` 都会管理维护一份训练好的模型,下面是两种最常用的模型加载方式: + +1. 从磁盘加载:这时`gradient machine`会独立拥有一份训练好的模型; +1. 共享自其它`gradient machine`的模型:这种情况多出现在使用多线程预测时,通过多个线程共享同一个模型来减少内存开销。可参考[此示例](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/multi_thread/main.c)。 + +- 注意事项 + 1. 使用PaddlePaddle V2 API训练,模型中所有可学习参数会被存为一个压缩文件,需要手动进行解压,将它们放在同一目录中,C-API不会直接加载 V2 API 存储的压缩文件。 + 1. 如果使用`merge model`方式将神经网络结构和训练好的参数序列化到一个文件,请参考此[示例](https://github.com/PaddlePaddle/Mobile/blob/develop/Demo/linux/paddle_image_recognizer.cpp#L59)。 + 1. 加载模型有多种方式,也可以在程序运行过程中再加载另外一个模型。 + +#### step 3. 创建神经网络输入,组织输入数据 + +基本使用概念: +- 在PaddlePaddle内部,神经网络中一个计算层的输入输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输出,每一个输入/输出都会对应有自己的`Argument`。 +- `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 +- 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 + +*注:本文档使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。* + +这篇文档的之后部分会使用`argument`来**特指** PaddlePaddle C-API中神经网络的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 + +于是,在组织神经网络输入,获取输出时,需要思考完成以下工作: +1. 为每一个输入/输出创建`argument`; +1. 为每一个`argument`创建`paddle_matrix`来存储数据; + +与输入不同的是,输出`argument`的`paddle_matrix`变量并不需在使用C-API时为之分配存储空间。PaddlePaddle内部,神经网络进行前向计算时会自己分配/管理每个计算层的存储空间;这些细节C-API会代为处理,只需在概念上理解,并按照约定调用相关的 C-API 接口即可。 + +#### step 4. 前向计算 + +完成上述准备之后,通过调用 `paddle_gradient_machine_forward` 接口完成神经网络的前向计算。 + +#### step 5. 清理 + +结束预测之后,对使用的中间变量和资源进行清理和释放。 -- GitLab From d09503b2bef5b134b50e7a9069426276bcf73762 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Mon, 8 Jan 2018 04:29:57 +0000 Subject: [PATCH 0116/2305] Remove the redundant switch case statement --- paddle/platform/profiler.cc | 80 +++++++++++++++++++------------------ paddle/platform/profiler.h | 2 +- 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index 239df23128e..2562b2b5f07 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -182,6 +182,44 @@ std::vector> DisableProfiler() { void ParseEvents(std::vector>& events, EventSortingKey sorted_by) { if (g_profiler_place == "") return; + + std::string sorted_domain; + std::function sorted_func; + switch (sorted_by) { + case EventSortingKey::kCalls: + sorted_domain = "number of calls"; + sorted_func = [](EventItem& a, EventItem& b) { + return a.calls > b.calls; + }; + break; + case EventSortingKey::kTotal: + sorted_domain = "total time"; + sorted_func = [](EventItem& a, EventItem& b) { + return a.total_time > b.total_time; + }; + break; + case EventSortingKey::kMin: + sorted_domain = "minimum time"; + sorted_func = [](EventItem& a, EventItem& b) { + return a.min_time > b.min_time; + }; + break; + case EventSortingKey::kMax: + sorted_domain = "maximum time"; + sorted_func = [](EventItem& a, EventItem& b) { + return a.max_time > b.max_time; + }; + break; + case EventSortingKey::kAve: + sorted_domain = "average time"; + sorted_func = [](EventItem& a, EventItem& b) { + return a.ave_time > b.ave_time; + }; + break; + default: + sorted_domain = "event end time"; + } + std::vector> events_table; size_t max_name_width = 0; for (size_t i = 0; i < events.size(); i++) { @@ -240,21 +278,7 @@ void ParseEvents(std::vector>& events, } // sort if (sorted_by != EventSortingKey::kDefault) { - std::sort(event_items.begin(), event_items.end(), - [&](EventItem& a, EventItem& b) { - switch (sorted_by) { - case EventSortingKey::kCalls: - return a.calls > b.calls; - case EventSortingKey::kTotal: - return a.total_time > b.total_time; - case EventSortingKey::kMin: - return a.min_time > b.min_time; - case EventSortingKey::kMax: - return a.max_time > b.max_time; - default: - return a.ave_time > b.ave_time; - } - }); + std::sort(event_items.begin(), event_items.end(), sorted_func); } events_table.push_back(event_items); @@ -268,11 +292,11 @@ void ParseEvents(std::vector>& events, } // Print report - PrintProfilingReport(events_table, sorted_by, max_name_width + 4, 12); + PrintProfilingReport(events_table, sorted_domain, max_name_width + 4, 12); } void PrintProfilingReport(std::vector>& events_table, - EventSortingKey sorted_by, const size_t name_width, + std::string& sorted_domain, const size_t name_width, const size_t data_width) { // Output header information std::cout << "\n------------------------->" @@ -280,27 +304,7 @@ void PrintProfilingReport(std::vector>& events_table, << "<-------------------------\n\n"; std::cout << "Place: " << g_profiler_place << std::endl; std::cout << "Time unit: ms" << std::endl; - std::string sort_domain = "event end time"; - switch (sorted_by) { - case EventSortingKey::kCalls: - sort_domain = "number of calls"; - break; - case EventSortingKey::kTotal: - sort_domain = "total time"; - break; - case EventSortingKey::kMin: - sort_domain = "minimum time"; - break; - case EventSortingKey::kMax: - sort_domain = "maximum time"; - break; - case EventSortingKey::kAve: - sort_domain = "average time"; - break; - default: - break; - } - std::cout << "Sorted by " << sort_domain + std::cout << "Sorted by " << sorted_domain << " in descending order in the same thread\n\n"; // Output events table std::cout.setf(std::ios::left); diff --git a/paddle/platform/profiler.h b/paddle/platform/profiler.h index f97a5867877..6df48ef8806 100644 --- a/paddle/platform/profiler.h +++ b/paddle/platform/profiler.h @@ -136,7 +136,7 @@ void ParseEvents(std::vector>&, // Print results void PrintProfilingReport(std::vector>& events_table, - EventSortingKey sorted_by, const size_t name_width, + std::string& sorted_domain, const size_t name_width, const size_t data_width); } // namespace platform } // namespace paddle -- GitLab From ea0280b4a17ae3862c127bcdda1e7900b5ba1e11 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Mon, 8 Jan 2018 04:33:46 +0000 Subject: [PATCH 0117/2305] Remove unused included header gflags --- paddle/platform/profiler.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index 2562b2b5f07..4283724d28a 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -15,7 +15,6 @@ limitations under the License. */ #include "paddle/platform/profiler.h" #include #include -#include "gflags/gflags.h" #include "glog/logging.h" namespace paddle { -- GitLab From 0f353ab46ed75ab99213f5ee38fcd1867838ef5f Mon Sep 17 00:00:00 2001 From: Qiao Longfei Date: Mon, 8 Jan 2018 13:10:44 +0800 Subject: [PATCH 0118/2305] cpu gpu transform function (#7191) * add rename guard * add device_data_transform * add device_data_transform_test * modify GetExpectedKernelType * update operator.run * support test test_label_semantic_roles * optimize code * optimize code * rename GetActualKernelType to GetExpectedKernelType * fix chunk_eval_op and device_data_transform_test * add is_same_place to place * optimize code, refine rename_guard * refine rename guard, add GetKernelTypeForVar * optimize code * add some log * rename guard * use sub scope to create var * fix compile * add IsInitialized for Tensor * add VarIsTensor * fix op_registry_test * test * tmp disable priority * restore switch_kernel.md * code clean --- paddle/framework/CMakeLists.txt | 7 +- paddle/framework/data_transform.cc | 33 ++++ paddle/framework/data_transform.h | 8 + paddle/framework/device_data_transform.cc | 46 +++++ paddle/framework/device_data_transform.h | 27 +++ .../framework/device_data_transform_test.cu | 168 ++++++++++++++++++ paddle/framework/op_registry_test.cc | 22 +-- paddle/framework/operator.cc | 151 +++++----------- paddle/framework/operator.h | 7 +- paddle/framework/operator_test.cc | 3 +- paddle/framework/scope.cc | 1 + paddle/framework/scope.h | 2 +- paddle/framework/tensor.h | 2 + paddle/framework/tensor_impl.h | 2 + paddle/framework/variable.h | 2 + paddle/operators/accuracy_op.cc | 2 +- paddle/operators/auc_op.cc | 2 +- paddle/operators/batch_norm_op.cc | 2 +- paddle/operators/chunk_eval_op.cc | 4 +- paddle/operators/chunk_eval_op.h | 13 +- paddle/operators/compare_op.cc | 4 +- paddle/operators/conv_op.h | 13 -- paddle/operators/crf_decoding_op.cc | 10 +- paddle/operators/crf_decoding_op.h | 3 - paddle/operators/cross_entropy_op.cc | 4 +- .../fill_constant_batch_size_like_op.cc | 2 +- paddle/operators/gather_op.cc | 4 +- paddle/operators/gaussian_random_op.cc | 2 +- paddle/operators/linear_chain_crf_op.cc | 4 +- paddle/operators/lod_reset_op.cc | 4 +- paddle/operators/logical_op.cc | 4 +- paddle/operators/lookup_table_op.cc | 4 +- paddle/operators/lstm_op.cc | 4 +- paddle/operators/multiplex_op.cc | 4 +- paddle/operators/nce_op.cc | 4 +- paddle/operators/pool_with_index_op.cc | 4 +- paddle/operators/positive_negative_pair_op.cc | 2 +- paddle/operators/precision_recall_op.cc | 2 +- paddle/operators/roi_pool_op.cc | 4 +- paddle/operators/scatter_op.cc | 4 +- paddle/operators/sequence_pool_op.cc | 2 +- paddle/operators/sequence_slice_op.cc | 4 +- .../softmax_with_cross_entropy_op.cc | 4 +- paddle/operators/sum_op.cc | 2 +- paddle/operators/uniform_random_op.cc | 2 +- paddle/operators/unpool_op.cc | 4 +- paddle/platform/place.cc | 12 ++ paddle/platform/place.h | 1 + .../tests/book/test_label_semantic_roles.py | 8 +- 49 files changed, 429 insertions(+), 200 deletions(-) create mode 100644 paddle/framework/device_data_transform.cc create mode 100644 paddle/framework/device_data_transform.h create mode 100644 paddle/framework/device_data_transform_test.cu diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt index 3967a40136d..46439c2d277 100644 --- a/paddle/framework/CMakeLists.txt +++ b/paddle/framework/CMakeLists.txt @@ -32,7 +32,9 @@ cc_test(threadpool_test SRCS threadpool_test.cc DEPS threadpool) cc_library(scope SRCS scope.cc DEPS glog threadpool) cc_test(scope_test SRCS scope_test.cc DEPS scope) -cc_library(data_transform SRCS data_transform.cc DEPS math_function tensor framework_proto) +cc_library(device_data_transform SRCS device_data_transform.cc DEPS tensor) + +cc_library(data_transform SRCS data_transform.cc DEPS math_function tensor framework_proto selected_rows device_data_transform) cc_test(data_transform_test SRCS data_transform_test.cc DEPS data_transform device_context) cc_library(attribute SRCS attribute.cc DEPS framework_proto) @@ -77,3 +79,6 @@ cc_library(init SRCS init.cc DEPS gflags device_context place stringpiece operat cc_test(init_test SRCS init_test.cc DEPS init) cc_test(op_kernel_type_test SRCS op_kernel_type_test.cc DEPS place device_context framework_proto) + +nv_test(device_data_transform_test SRCS device_data_transform_test.cu + DEPS operator op_registry init math_function) diff --git a/paddle/framework/data_transform.cc b/paddle/framework/data_transform.cc index 6b178096886..55825c5b7d2 100644 --- a/paddle/framework/data_transform.cc +++ b/paddle/framework/data_transform.cc @@ -14,7 +14,9 @@ limitations under the License. */ #include #include "paddle/framework/data_transform.h" +#include "paddle/framework/device_data_transform.h" #include "paddle/framework/lod_tensor.h" +#include "paddle/framework/selected_rows.h" #include "paddle/platform/device_context.h" namespace paddle { @@ -25,6 +27,37 @@ DataTransformFnMap& DataTransformFnMap::Instance() { return data_transform_map; } +Tensor* DataTransform(const OpKernelType& expected_kernel_type, + const OpKernelType& kernel_type_for_var, + const Tensor& input_tensor) { + Tensor* out = nullptr; + if (!platform::is_same_place(kernel_type_for_var.place_, + expected_kernel_type.place_)) { + out = DeviceTransform(input_tensor, expected_kernel_type.place_); + } + PADDLE_ENFORCE_NOT_NULL(out, "out should not be null"); + return out; +} + +void CopyVariableWithTensor(const Variable& in_var, const Tensor& tensor, + Variable& out_var) { + if (in_var.IsType()) { + auto& in_lod_tensor = in_var.Get(); + auto* tran_lod_tensor = out_var.GetMutable(); + tran_lod_tensor->set_lod(in_lod_tensor.lod()); + tran_lod_tensor->set_layout(in_lod_tensor.layout()); + tran_lod_tensor->ShareDataWith(tensor); + } else if (in_var.IsType()) { + auto& in_selected_rows = in_var.Get(); + auto* trans_selected_rows = out_var.GetMutable(); + trans_selected_rows->set_height(in_selected_rows.height()); + trans_selected_rows->set_rows(in_selected_rows.rows()); + trans_selected_rows->mutable_value()->ShareDataWith(tensor); + } else { + PADDLE_THROW("unknown var type"); + } +} + auto KernelFP32 = OpKernelType(proto::DataType::FP32, platform::CPUPlace(), DataLayout::kNHWC, LibraryType::kPlain); diff --git a/paddle/framework/data_transform.h b/paddle/framework/data_transform.h index 56ebc80f438..42fc5f4d7e8 100644 --- a/paddle/framework/data_transform.h +++ b/paddle/framework/data_transform.h @@ -19,6 +19,7 @@ limitations under the License. */ #include #include "paddle/framework/op_kernel_type.h" +#include "paddle/framework/selected_rows.h" #include "paddle/framework/tensor.h" #include "paddle/framework/variable.h" #include "paddle/operators/math/math_function.h" @@ -49,6 +50,13 @@ struct KernelTypePairHash { } }; +Tensor* DataTransform(const OpKernelType& expected_kernel_type, + const OpKernelType& kernel_type_for_var, + const Tensor& input_tensor); + +void CopyVariableWithTensor(const Variable& in_var, const Tensor& tensor, + Variable& out_var); + template struct CastDataTypeFunctor { HOSTDEVICE inline OutType operator()(InType in) const { diff --git a/paddle/framework/device_data_transform.cc b/paddle/framework/device_data_transform.cc new file mode 100644 index 00000000000..4f9b7e96a28 --- /dev/null +++ b/paddle/framework/device_data_transform.cc @@ -0,0 +1,46 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/framework/device_data_transform.h" + +namespace paddle { +namespace framework { + +static const platform::DeviceContext* GetDeviceContext( + const platform::Place& src_place, const platform::Place& dst_place) { + platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance(); + + if (platform::is_gpu_place(src_place) && platform::is_cpu_place(dst_place)) { + return pool.Get(src_place); + } else if (platform::is_cpu_place(src_place) && + platform::is_gpu_place(dst_place)) { + return pool.Get(dst_place); + } else { + PADDLE_THROW( + "Currently, model parallelism is only supported between CPU and CUDA"); + } +} + +Tensor* DeviceTransform(const Tensor& in, const platform::Place& dst_place) { + VLOG(3) << "DeviceTransform in, src_place " << in.place() + << " dst_place: " << dst_place; + Tensor* out = new Tensor(); + auto* dev_ctx = GetDeviceContext(in.place(), dst_place); + dev_ctx->Wait(); + CopyFrom(in, dst_place, *dev_ctx, out); + dev_ctx->Wait(); + return out; +} + +} // namespace framework +} // namespace paddle diff --git a/paddle/framework/device_data_transform.h b/paddle/framework/device_data_transform.h new file mode 100644 index 00000000000..bebf0d1b320 --- /dev/null +++ b/paddle/framework/device_data_transform.h @@ -0,0 +1,27 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/framework/lod_tensor.h" +#include "paddle/framework/tensor.h" +#include "paddle/framework/tensor_util.h" +#include "paddle/platform/device_context.h" + +namespace paddle { +namespace framework { + +Tensor* DeviceTransform(const Tensor& in, const platform::Place& dst_place); + +} // namespace framework +} // namespace paddle diff --git a/paddle/framework/device_data_transform_test.cu b/paddle/framework/device_data_transform_test.cu new file mode 100644 index 00000000000..e9100053d52 --- /dev/null +++ b/paddle/framework/device_data_transform_test.cu @@ -0,0 +1,168 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "gtest/gtest.h" + +#include "paddle/framework/init.h" +#include "paddle/framework/lod_tensor.h" +#include "paddle/framework/op_info.h" +#include "paddle/framework/op_registry.h" +#include "paddle/operators/elementwise_op_function.h" +#include "paddle/operators/math/math_function.h" +#include "paddle/platform/device_context.h" + +namespace paddle { +namespace framework { + +template +struct AddFunctor { + inline HOSTDEVICE T operator()(T a, T b) const { return a + b; } +}; + +class OpKernelTestProtoAndCheckerMaker : public OpProtoAndCheckerMaker { + public: + OpKernelTestProtoAndCheckerMaker(OpProto* proto, OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("input", "input1 of test op"); + AddOutput("output", "output of test op"); + AddAttr("use_gpu", "force to use gpu kernel").SetDefault(false); + AddComment("This is test op"); + } +}; + +class TestOpWithKernel : public OperatorWithKernel { + public: + using OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(framework::InferShapeContext* ctx) const override {} + OpKernelType GetExpectedKernelType( + const ExecutionContext& ctx) const override { + if (Attr("use_gpu")) { + VLOG(3) << "force use gpu kernel"; + return OpKernelType(proto::DataType::FP32, platform::CUDAPlace(0)); + } else { + VLOG(3) << "use default kernel"; + return OpKernelType(proto::DataType::FP32, + ctx.Input("input")->place()); + } + } +}; + +template +class TestKernel : public OpKernel { + public: + void Compute(const ExecutionContext& ctx) const { + std::cout << ctx.op().DebugString() << std::endl; + + const Tensor* input = ctx.Input("input"); + + std::cout << "input place:" << input->place() << std::endl; + auto* output = ctx.Output("output"); + output->Resize(input->dims()); + output->mutable_data(ctx.GetPlace()); + + operators::TransformFunctor, T, DeviceContext> functor( + input, input, output, ctx.template device_context(), + AddFunctor()); + functor.Run(); + } +}; + +} // namespace framework +} // namespace paddle + +REGISTER_OP_WITHOUT_GRADIENT( + test_op, paddle::framework::TestOpWithKernel, + paddle::framework::OpKernelTestProtoAndCheckerMaker); +REGISTER_OP_CPU_KERNEL( + test_op, + paddle::framework::TestKernel); +REGISTER_OP_CUDA_KERNEL( + test_op, + paddle::framework::TestKernel); + +static void BuildVar(const std::string& param_name, + std::initializer_list arguments, + paddle::framework::proto::OpDesc::Var* var) { + var->set_parameter(param_name); + for (auto& arg_name : arguments) { + *var->mutable_arguments()->Add() = arg_name; + } +} + +TEST(Operator, CPUtoGPU) { + using namespace paddle::framework; + using namespace paddle::platform; + + ASSERT_EQ(InitDevices({"CPU", "GPU:0"}), true); + + paddle::framework::Scope scope; + paddle::platform::CPUPlace cpu_place; + + // create an op to run on CPU + paddle::framework::proto::OpDesc cpu_op_desc; + cpu_op_desc.set_type("test_op"); + BuildVar("input", {"IN1"}, cpu_op_desc.add_inputs()); + BuildVar("output", {"OUT1"}, cpu_op_desc.add_outputs()); + + auto cpu_op = paddle::framework::OpRegistry::CreateOp(cpu_op_desc); + // prepare input + auto* in_t = scope.Var("IN1")->GetMutable(); + auto* src_ptr = in_t->mutable_data({2, 3}, CPUPlace()); + for (int i = 0; i < 2 * 3; ++i) { + src_ptr[i] = static_cast(i); + } + + // get output + auto* output = scope.Var("OUT1"); + cpu_op->Run(scope, cpu_place); + + auto* output_ptr = output->Get().data(); + for (int i = 0; i < 2 * 3; ++i) { + ASSERT_EQ(output_ptr[i], static_cast(i) * 2); + } + + // create an op to run on GPU + paddle::framework::proto::OpDesc gpu_op_desc; + gpu_op_desc.set_type("test_op"); + BuildVar("input", {"OUT1"}, gpu_op_desc.add_inputs()); + BuildVar("output", {"OUT2"}, gpu_op_desc.add_outputs()); + + auto attr = gpu_op_desc.mutable_attrs()->Add(); + attr->set_name("use_gpu"); + attr->set_type(paddle::framework::proto::AttrType::BOOLEAN); + attr->set_b(true); + + auto gpu_op = paddle::framework::OpRegistry::CreateOp(gpu_op_desc); + + paddle::platform::CUDAPlace cuda_place(0); + // get output + auto* output2 = scope.Var("OUT2"); + gpu_op->Run(scope, cuda_place); + + // auto* output2_ptr = output2->Get().data(); + DeviceContextPool& pool = DeviceContextPool::Instance(); + auto dev_ctx = pool.Get(cuda_place); + + paddle::framework::Tensor output_tensor; + CopyFrom(output2->Get(), paddle::platform::CPUPlace(), *dev_ctx, + &output_tensor); + + dev_ctx->Wait(); + float* output2_ptr = output_tensor.data(); + for (int i = 0; i < 2 * 3; ++i) { + ASSERT_EQ(output2_ptr[i], static_cast(i) * 4); + } +} diff --git a/paddle/framework/op_registry_test.cc b/paddle/framework/op_registry_test.cc index a286925bbe4..f7a10ada809 100644 --- a/paddle/framework/op_registry_test.cc +++ b/paddle/framework/op_registry_test.cc @@ -218,7 +218,7 @@ class OpWithKernelTest : public OperatorWithKernel { protected: void InferShape(InferShapeContext* ctx) const override {} - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType(proto::DataType::FP32, ctx.device_context()); } @@ -282,16 +282,11 @@ class OpWithMultiKernelTest : public OperatorWithKernel { protected: void InferShape(InferShapeContext* ctx) const override {} - framework::OpKernelType GetActualKernelType( - const framework::ExecutionContext& ctx) const override { - return framework::OpKernelType(proto::DataType::FP32, ctx.device_context()); - } - framework::OpKernelType GetExpectedKernelType( - const framework::OpKernelType& kernel) const override { - return framework::OpKernelType(kernel.data_type_, platform::CUDAPlace(0), - kernel.data_layout_, - framework::LibraryType::kCUDNN); + const framework::ExecutionContext& ctx) const override { + return framework::OpKernelType( + proto::DataType::FP32, platform::CUDAPlace(0), DataLayout::kAnyLayout, + framework::LibraryType::kCUDNN); } }; @@ -371,6 +366,7 @@ TEST(OperatorRegistrar, OpWithMultiKernel) { op_desc.set_type("op_with_multi_kernel"); auto op = paddle::framework::OpRegistry::CreateOp(op_desc); + // TODO(qiao) add priority back // use all available kernels paddle::framework::UseALL(); op->Run(scope, cuda_place); @@ -380,16 +376,16 @@ TEST(OperatorRegistrar, OpWithMultiKernel) { paddle::framework::UseCPU(); op->Run(scope, cpu_place); - EXPECT_EQ(op_test_value, -9); + EXPECT_EQ(op_test_value, -20); // add cuda kernels paddle::framework::UseCUDA(); op->Run(scope, cuda_place); - EXPECT_EQ(op_test_value, -10); + EXPECT_EQ(op_test_value, -30); // use cudnn kernel paddle::framework::UseCUDNN(); op->Run(scope, cuda_place); - EXPECT_EQ(op_test_value, -20); + EXPECT_EQ(op_test_value, -40); } diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index 4ef0c2523ca..adc85b1049f 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -14,11 +14,10 @@ limitations under the License. */ #include #include -#include #include "paddle/framework/data_transform.h" +#include "paddle/framework/device_data_transform.h" #include "paddle/framework/executor.h" -#include "paddle/framework/lod_tensor_array.h" #include "paddle/framework/operator.h" #include "paddle/framework/shape_inference.h" #include "paddle/framework/var_type.h" @@ -243,6 +242,10 @@ void OperatorBase::GenerateTemporaryNames() { } } +static bool VarIsTensor(const Variable* var) { + return var->IsType() || var->IsType(); +} + static const Tensor* GetTensorFromVar(const Variable* var) { const Tensor* t = nullptr; if (var->IsType()) { @@ -453,30 +456,6 @@ class RuntimeInferShapeContext : public InferShapeContext { const Scope& scope_; }; -const platform::DeviceContext* GetDeviceContext( - framework::KernelTypePair& kernel_pair) { - auto& actual_kernel_key = kernel_pair.first; - auto& expected_kernel_key = kernel_pair.second; - platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance(); - - if (platform::is_gpu_place(actual_kernel_key.place_) && - platform::is_cpu_place(expected_kernel_key.place_)) { - return pool.Get(actual_kernel_key.place_); - } else if (platform::is_cpu_place(actual_kernel_key.place_) && - platform::is_gpu_place(expected_kernel_key.place_)) { - return pool.Get(expected_kernel_key.place_); - } else { - PADDLE_THROW( - "Currently, model parallelism is only supported between CPU and CUDA"); - } -} - -const platform::DeviceContext* GetDeviceContext( - const framework::OpKernelType& kernel) { - platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance(); - return pool.Get(kernel.place_); -} - void OperatorWithKernel::Run(const Scope& scope, const platform::Place& place) const { RuntimeInferShapeContext infer_shape_ctx(*this, scope); @@ -492,94 +471,43 @@ void OperatorWithKernel::Run(const Scope& scope, "There are no kernels which are registered in the %s operator.", type_); } - // check if op[type] have kernel for kernel_key - OpKernelMap& kernels = kernels_iter->second; - ExecutionContext ctx(*this, scope, *dev_ctx); - auto actual_kernel_key = GetActualKernelType(ctx); - - auto expected_kernel_key = GetExpectedKernelType(actual_kernel_key); - - if (actual_kernel_key == expected_kernel_key) { - PADDLE_ENFORCE_EQ(actual_kernel_key.place_, expected_kernel_key.place_, - "Currently, model parallelism is only supported between " - "CPU and other devices. For example, multi-GPU model " - "parallelism will failed."); - } else { - // find the best key candidate - const DataTransformFnMap& trans_map = DataTransformFnMap::Instance(); - for (auto& candidate : kKernelPriority) { - auto candidate_key = - OpKernelType(actual_kernel_key.data_type_, std::get<0>(candidate), - actual_kernel_key.data_layout_, std::get<1>(candidate)); - - auto candidate_pair = std::make_pair(actual_kernel_key, candidate_key); - if ((actual_kernel_key == candidate_key) || - (kernels.count(candidate_key) && - trans_map.GetNullable(candidate_pair))) { - expected_kernel_key = candidate_key; - break; - } - } - - auto kernel_pair = std::make_pair(actual_kernel_key, expected_kernel_key); - const DataTransformFn* trans_fun = trans_map.GetNullable(kernel_pair); - if (trans_fun) { - auto input_vars = this->InputVars(); - // TODO(qijun) filter the input vars that do not need to be transformed - - // filter vars that has been transformed - std::vector need_trans; - for (auto var_name : input_vars) { - auto var_name_trans = - var_name + framework::KernelTypeToString(expected_kernel_key); - if (!scope.FindVar(var_name_trans)) { - const_cast(scope).Var(var_name_trans); - need_trans.push_back(var_name); - } - } - - if (!need_trans.empty()) { - auto trans_dev_ctx = GetDeviceContext(kernel_pair); - - // Wait for transform starting - dev_ctx->Wait(); - - for (auto var_name : need_trans) { - (*trans_fun)(trans_dev_ctx, kernel_pair, *(scope.FindVar(var_name)), - scope.FindVar(var_name + framework::KernelTypeToString( - expected_kernel_key))); + auto expected_kernel_key = this->GetExpectedKernelType(ctx); + + Scope& new_scope = scope.NewScope(); + + for (auto& var_name_item : this->Inputs()) { + for (auto& var_name : var_name_item.second) { + auto* var = scope.FindVar(var_name); + if (var && VarIsTensor(var)) { + auto* tensor_in = GetTensorFromVar(var); + if (tensor_in->IsInitialized()) { + auto kernel_type_for_var = this->GetKernelTypeForVar( + var_name_item.first, *tensor_in, expected_kernel_key); + if (kernel_type_for_var != expected_kernel_key) { + auto out_var_names = OutputVars(true); + if (std::find(out_var_names.begin(), out_var_names.end(), + var_name) != out_var_names.end()) { + PADDLE_THROW( + "var %s is both input and output, " + "does not support transform", + var_name); + } + VLOG(3) << "need to do transform for var " << var_name; + auto* trans_var = new_scope.Var(var_name); + auto* out = DataTransform(expected_kernel_key, kernel_type_for_var, + *tensor_in); + CopyVariableWithTensor(*var, *out, *trans_var); + } } - // Wait for data transform finishing - trans_dev_ctx->Wait(); } } } - VLOG(10) << "Actual kernel: " << actual_kernel_key - << "Expected kernel: " << expected_kernel_key; - + OpKernelMap& kernels = kernels_iter->second; auto kernel_iter = kernels.find(expected_kernel_key); - if (kernel_iter == kernels.end()) { - PADDLE_THROW("The operator %s does not support %s", type_, - expected_kernel_key); - } - - auto* expected_dev_ctx = GetDeviceContext(expected_kernel_key); - ExecutionContext expected_ctx(*this, scope, *expected_dev_ctx); - - kernel_iter->second->Compute(expected_ctx); -} - -OpKernelType OperatorWithKernel::GetActualKernelType( - const ExecutionContext& ctx) const { - return OpKernelType(IndicateDataType(ctx), ctx.GetPlace()); -} - -OpKernelType OperatorWithKernel::GetExpectedKernelType( - const OpKernelType& actual_kernel_type) const { - return actual_kernel_type; + kernel_iter->second->Compute(ExecutionContext(*this, new_scope, *dev_ctx)); } proto::DataType OperatorWithKernel::IndicateDataType( @@ -611,5 +539,16 @@ proto::DataType OperatorWithKernel::IndicateDataType( return static_cast(data_type); } +OpKernelType OperatorWithKernel::GetExpectedKernelType( + const ExecutionContext& ctx) const { + return OpKernelType(IndicateDataType(ctx), ctx.GetPlace()); +} + +OpKernelType OperatorWithKernel::GetKernelTypeForVar( + const std::string& var_name, const Tensor& tensor, + const OpKernelType& expected_kernel_type) const { + return OpKernelType(expected_kernel_type.data_type_, tensor.place()); +} + } // namespace framework } // namespace paddle diff --git a/paddle/framework/operator.h b/paddle/framework/operator.h index 800397c077b..d5feb598649 100644 --- a/paddle/framework/operator.h +++ b/paddle/framework/operator.h @@ -408,9 +408,10 @@ class OperatorWithKernel : public OperatorBase { } protected: - virtual OpKernelType GetActualKernelType(const ExecutionContext& ctx) const; - virtual OpKernelType GetExpectedKernelType( - const OpKernelType& actual_kernel_type) const; + virtual OpKernelType GetExpectedKernelType(const ExecutionContext& ctx) const; + virtual OpKernelType GetKernelTypeForVar( + const std::string& var_name, const Tensor& tensor, + const OpKernelType& expected_kernel_type) const; private: // indicate kernel DataType by input data. Defaultly all input data must be diff --git a/paddle/framework/operator_test.cc b/paddle/framework/operator_test.cc index 4d38a7ada91..d002f3f2388 100644 --- a/paddle/framework/operator_test.cc +++ b/paddle/framework/operator_test.cc @@ -114,7 +114,8 @@ class OpWithKernelTest : public OperatorWithKernel { protected: void InferShape(framework::InferShapeContext* ctx) const override {} - OpKernelType GetActualKernelType(const ExecutionContext& ctx) const override { + OpKernelType GetExpectedKernelType( + const ExecutionContext& ctx) const override { return OpKernelType(proto::DataType::FP32, ctx.GetPlace()); } }; diff --git a/paddle/framework/scope.cc b/paddle/framework/scope.cc index 4e80e3d974e..2bd0ac8f5a9 100644 --- a/paddle/framework/scope.cc +++ b/paddle/framework/scope.cc @@ -109,6 +109,7 @@ std::string Scope::Rename(const std::string& origin_name) const { Rename(origin_name, var_name); return var_name; } + Variable* Scope::FindVarLocally(const std::string& name) const { auto it = vars_.find(name); if (it != vars_.end()) return it->second; diff --git a/paddle/framework/scope.h b/paddle/framework/scope.h index 10143326dfa..a1da81cc797 100644 --- a/paddle/framework/scope.h +++ b/paddle/framework/scope.h @@ -75,9 +75,9 @@ class Scope { // Rename variable to a new name and return the new name std::string Rename(const std::string& origin_name) const; - private: Variable* FindVarLocally(const std::string& name) const; + private: // Call Scope::NewScope for a sub-scope. explicit Scope(Scope const* parent) : parent_(parent) {} diff --git a/paddle/framework/tensor.h b/paddle/framework/tensor.h index 02b125cbbee..4aaa29d794c 100644 --- a/paddle/framework/tensor.h +++ b/paddle/framework/tensor.h @@ -55,6 +55,8 @@ class Tensor { template inline const T* data() const; + inline bool IsInitialized() const; + inline void switch_place(platform::Place new_place); /** diff --git a/paddle/framework/tensor_impl.h b/paddle/framework/tensor_impl.h index 6c6f298edc1..1340c5e4852 100644 --- a/paddle/framework/tensor_impl.h +++ b/paddle/framework/tensor_impl.h @@ -84,6 +84,8 @@ inline const T* Tensor::data() const { reinterpret_cast(holder_->ptr()) + offset_); } +inline bool Tensor::IsInitialized() const { return holder_ != nullptr; } + template inline T* Tensor::data() { check_memory_size(); diff --git a/paddle/framework/variable.h b/paddle/framework/variable.h index e5a94759f92..36b76fb196c 100644 --- a/paddle/framework/variable.h +++ b/paddle/framework/variable.h @@ -32,6 +32,8 @@ class Variable { return *static_cast(holder_->Ptr()); } + bool IsInitialized() const { return holder_ != nullptr; } + template T* GetMutable() { if (!IsType()) { diff --git a/paddle/operators/accuracy_op.cc b/paddle/operators/accuracy_op.cc index d7baa6e9053..8e8a3c7dd30 100644 --- a/paddle/operators/accuracy_op.cc +++ b/paddle/operators/accuracy_op.cc @@ -53,7 +53,7 @@ class AccuracyOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Out")->type()), diff --git a/paddle/operators/auc_op.cc b/paddle/operators/auc_op.cc index c16bc11931e..b6494f95097 100644 --- a/paddle/operators/auc_op.cc +++ b/paddle/operators/auc_op.cc @@ -39,7 +39,7 @@ class AucOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Out")->type()), diff --git a/paddle/operators/batch_norm_op.cc b/paddle/operators/batch_norm_op.cc index dd7b038b008..0e984c38ba7 100644 --- a/paddle/operators/batch_norm_op.cc +++ b/paddle/operators/batch_norm_op.cc @@ -306,7 +306,7 @@ class BatchNormGradOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { const auto *var = ctx.InputVar(framework::GradVarName("Y")); if (var == nullptr) { diff --git a/paddle/operators/chunk_eval_op.cc b/paddle/operators/chunk_eval_op.cc index a040404266c..44f667aead9 100644 --- a/paddle/operators/chunk_eval_op.cc +++ b/paddle/operators/chunk_eval_op.cc @@ -55,10 +55,10 @@ class ChunkEvalOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType(framework::proto::DataType::FP32, - ctx.device_context()); + platform::CPUPlace()); } }; diff --git a/paddle/operators/chunk_eval_op.h b/paddle/operators/chunk_eval_op.h index 74ab435c860..300aff90c0a 100644 --- a/paddle/operators/chunk_eval_op.h +++ b/paddle/operators/chunk_eval_op.h @@ -145,6 +145,7 @@ class ChunkEvalKernel : public framework::OpKernel { context.Attr>("excluded_chunk_types").end()); auto* inference = context.Input("Inference"); + auto place = inference->place(); auto* label = context.Input("Label"); auto* precision = context.Output("Precision"); auto* recall = context.Output("Recall"); @@ -155,15 +156,15 @@ class ChunkEvalKernel : public framework::OpKernel { const int64_t* inference_data = inference->data(); const int64_t* label_data = label->data(); - T* precision_data = precision->mutable_data(context.GetPlace()); - T* racall_data = recall->mutable_data(context.GetPlace()); - T* f1_data = f1->mutable_data(context.GetPlace()); + T* precision_data = precision->mutable_data(place); + T* racall_data = recall->mutable_data(place); + T* f1_data = f1->mutable_data(place); int64_t* num_infer_chunks_data = - num_infer_chunks->mutable_data(context.GetPlace()); + num_infer_chunks->mutable_data(place); int64_t* num_label_chunks_data = - num_label_chunks->mutable_data(context.GetPlace()); + num_label_chunks->mutable_data(place); int64_t* num_correct_chunks_data = - num_correct_chunks->mutable_data(context.GetPlace()); + num_correct_chunks->mutable_data(place); *num_infer_chunks_data = 0; *num_label_chunks_data = 0; *num_correct_chunks_data = 0; diff --git a/paddle/operators/compare_op.cc b/paddle/operators/compare_op.cc index 44665b7872a..daa2c193b48 100644 --- a/paddle/operators/compare_op.cc +++ b/paddle/operators/compare_op.cc @@ -66,9 +66,9 @@ class CompareOp : public framework::OperatorWithKernel { using framework::OperatorWithKernel::OperatorWithKernel; protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { - framework::OpKernelType kt = OperatorWithKernel::GetActualKernelType(ctx); + framework::OpKernelType kt = OperatorWithKernel::GetExpectedKernelType(ctx); // CompareOp kernel's device type is decided by input tensor place kt.place_ = ctx.Input("X")->place(); return kt; diff --git a/paddle/operators/conv_op.h b/paddle/operators/conv_op.h index fe3c0bc9302..83786e2329e 100644 --- a/paddle/operators/conv_op.h +++ b/paddle/operators/conv_op.h @@ -62,25 +62,12 @@ class ConvOp : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; - framework::OpKernelType GetExpectedKernelType( - const framework::OpKernelType& kernel) const override { - return framework::OpKernelType(kernel.data_type_, platform::CUDAPlace(0), - kernel.data_layout_, - framework::LibraryType::kCUDNN); - } }; class ConvOpGrad : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; - - framework::OpKernelType GetExpectedKernelType( - const framework::OpKernelType& kernel) const override { - return framework::OpKernelType(kernel.data_type_, platform::CUDAPlace(0), - kernel.data_layout_, - framework::LibraryType::kCUDNN); - } }; template diff --git a/paddle/operators/crf_decoding_op.cc b/paddle/operators/crf_decoding_op.cc index 024e1d061a5..30626028c13 100644 --- a/paddle/operators/crf_decoding_op.cc +++ b/paddle/operators/crf_decoding_op.cc @@ -120,17 +120,11 @@ class CRFDecodingOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Emission")->type()), - ctx.device_context()); - } - - framework::OpKernelType GetExpectedKernelType( - const framework::OpKernelType& actual_kernel_type) const override { - return framework::OpKernelType(actual_kernel_type.data_type_, - platform::CPUPlace()); + platform::CPUPlace()); } }; } // namespace operators diff --git a/paddle/operators/crf_decoding_op.h b/paddle/operators/crf_decoding_op.h index f6827b7b112..ce2f4e6622c 100644 --- a/paddle/operators/crf_decoding_op.h +++ b/paddle/operators/crf_decoding_op.h @@ -28,9 +28,6 @@ template class CRFDecodingOpKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { - PADDLE_ENFORCE(platform::is_cpu_place(ctx.GetPlace()), - "The crf_decoding operator can only run on CPU."); - auto* emission_weights = ctx.Input("Emission"); auto* transition_weights = ctx.Input("Transition"); auto* label = ctx.Input("Label"); diff --git a/paddle/operators/cross_entropy_op.cc b/paddle/operators/cross_entropy_op.cc index fe39cb481aa..7abd5b1c61d 100644 --- a/paddle/operators/cross_entropy_op.cc +++ b/paddle/operators/cross_entropy_op.cc @@ -51,7 +51,7 @@ class CrossEntropyOp : public framework::OperatorWithKernel { protected: // Explicitly set that the data type of computation kernel of cross_entropy // is determined by its input "X". - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), @@ -101,7 +101,7 @@ class CrossEntropyGradientOp : public framework::OperatorWithKernel { protected: // Explicitly set that the data type of computation kernel of cross_entropy // is determined by its input "X". - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), diff --git a/paddle/operators/fill_constant_batch_size_like_op.cc b/paddle/operators/fill_constant_batch_size_like_op.cc index 852ecdfe45e..c74a5b6ced3 100644 --- a/paddle/operators/fill_constant_batch_size_like_op.cc +++ b/paddle/operators/fill_constant_batch_size_like_op.cc @@ -49,7 +49,7 @@ class FillConstantBatchSizeLikeOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( static_cast(ctx.Attr("dtype")), diff --git a/paddle/operators/gather_op.cc b/paddle/operators/gather_op.cc index 45e9d8df702..597fdad0794 100644 --- a/paddle/operators/gather_op.cc +++ b/paddle/operators/gather_op.cc @@ -40,7 +40,7 @@ class GatherOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), @@ -57,7 +57,7 @@ class GatherGradOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), diff --git a/paddle/operators/gaussian_random_op.cc b/paddle/operators/gaussian_random_op.cc index 9ed493a7d02..2dca05760ec 100644 --- a/paddle/operators/gaussian_random_op.cc +++ b/paddle/operators/gaussian_random_op.cc @@ -60,7 +60,7 @@ class GaussianRandomOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( static_cast(ctx.Attr("dtype")), diff --git a/paddle/operators/linear_chain_crf_op.cc b/paddle/operators/linear_chain_crf_op.cc index 666207ea076..975e394c78d 100644 --- a/paddle/operators/linear_chain_crf_op.cc +++ b/paddle/operators/linear_chain_crf_op.cc @@ -183,7 +183,7 @@ class LinearChainCRFOp : public framework::OperatorWithKernel { protected: // Explicitly set that the data type of computation kernel of linear_chain_crf // is determined by its input "Emission". - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Emission")->type()), @@ -242,7 +242,7 @@ class LinearChainCRFGradOp : public framework::OperatorWithKernel { protected: // Explicitly set that the data type of output of the linear_chain_crf_grad // operator is determined by its input: gradients of LogLikelihood. - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType( diff --git a/paddle/operators/lod_reset_op.cc b/paddle/operators/lod_reset_op.cc index f3c0badf2a7..3d7b15edcfe 100644 --- a/paddle/operators/lod_reset_op.cc +++ b/paddle/operators/lod_reset_op.cc @@ -38,7 +38,7 @@ class LoDResetOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), @@ -97,7 +97,7 @@ class LoDResetGradOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), diff --git a/paddle/operators/logical_op.cc b/paddle/operators/logical_op.cc index 7417192479a..fedd325cf4f 100644 --- a/paddle/operators/logical_op.cc +++ b/paddle/operators/logical_op.cc @@ -99,9 +99,9 @@ class LogicalOp : public framework::OperatorWithKernel { using framework::OperatorWithKernel::OperatorWithKernel; protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { - framework::OpKernelType kt = OperatorWithKernel::GetActualKernelType(ctx); + framework::OpKernelType kt = OperatorWithKernel::GetExpectedKernelType(ctx); // LogicalOp kernel's device type is decided by input tensor place kt.place_ = ctx.Input("X")->place(); return kt; diff --git a/paddle/operators/lookup_table_op.cc b/paddle/operators/lookup_table_op.cc index 6e5cbd6f8ce..bb03def4391 100644 --- a/paddle/operators/lookup_table_op.cc +++ b/paddle/operators/lookup_table_op.cc @@ -41,7 +41,7 @@ class LookupTableOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("W")->type()), @@ -98,7 +98,7 @@ class LookupTableOpGrad : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("W")->type()), diff --git a/paddle/operators/lstm_op.cc b/paddle/operators/lstm_op.cc index b8fcec0f29b..3b90b64b4ef 100644 --- a/paddle/operators/lstm_op.cc +++ b/paddle/operators/lstm_op.cc @@ -92,7 +92,7 @@ class LSTMOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Input")->type()), @@ -260,7 +260,7 @@ class LSTMGradOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Input")->type()), diff --git a/paddle/operators/multiplex_op.cc b/paddle/operators/multiplex_op.cc index 11e047b5d57..78263da2fbf 100644 --- a/paddle/operators/multiplex_op.cc +++ b/paddle/operators/multiplex_op.cc @@ -51,7 +51,7 @@ class MultiplexOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.MultiInput("X")[0]->type()), @@ -102,7 +102,7 @@ class MultiplexGradOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.MultiInput("X")[0]->type()), diff --git a/paddle/operators/nce_op.cc b/paddle/operators/nce_op.cc index d39ca87d535..84ba3ead2b5 100644 --- a/paddle/operators/nce_op.cc +++ b/paddle/operators/nce_op.cc @@ -63,7 +63,7 @@ class NCEOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Input")->type()), @@ -166,7 +166,7 @@ class NCEOpGrad : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Input")->type()), diff --git a/paddle/operators/pool_with_index_op.cc b/paddle/operators/pool_with_index_op.cc index 76c5123527c..1d31d813af4 100644 --- a/paddle/operators/pool_with_index_op.cc +++ b/paddle/operators/pool_with_index_op.cc @@ -69,7 +69,7 @@ class MaxPoolWithIndexOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), @@ -90,7 +90,7 @@ class MaxPoolWithIndexOpGrad : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), diff --git a/paddle/operators/positive_negative_pair_op.cc b/paddle/operators/positive_negative_pair_op.cc index a6b23c995b8..5aa5167dbb8 100644 --- a/paddle/operators/positive_negative_pair_op.cc +++ b/paddle/operators/positive_negative_pair_op.cc @@ -85,7 +85,7 @@ class PositiveNegativePairOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Score")->type()), diff --git a/paddle/operators/precision_recall_op.cc b/paddle/operators/precision_recall_op.cc index c5753147eff..f1598d53cae 100644 --- a/paddle/operators/precision_recall_op.cc +++ b/paddle/operators/precision_recall_op.cc @@ -80,7 +80,7 @@ class PrecisionRecallOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("MaxProbs")->type()), diff --git a/paddle/operators/roi_pool_op.cc b/paddle/operators/roi_pool_op.cc index ef1804d9762..a7351f11c5d 100644 --- a/paddle/operators/roi_pool_op.cc +++ b/paddle/operators/roi_pool_op.cc @@ -68,7 +68,7 @@ class ROIPoolOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), @@ -89,7 +89,7 @@ class ROIPoolGradOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), diff --git a/paddle/operators/scatter_op.cc b/paddle/operators/scatter_op.cc index 806dccc6ca7..b6533489063 100644 --- a/paddle/operators/scatter_op.cc +++ b/paddle/operators/scatter_op.cc @@ -49,7 +49,7 @@ class ScatterOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Ref")->type()), @@ -68,7 +68,7 @@ class ScatterGradOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Ref")->type()), diff --git a/paddle/operators/sequence_pool_op.cc b/paddle/operators/sequence_pool_op.cc index aea98744d8f..34e1a125915 100644 --- a/paddle/operators/sequence_pool_op.cc +++ b/paddle/operators/sequence_pool_op.cc @@ -107,7 +107,7 @@ class SequencePoolGradOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), diff --git a/paddle/operators/sequence_slice_op.cc b/paddle/operators/sequence_slice_op.cc index 98bd8854903..f79106ff0f7 100644 --- a/paddle/operators/sequence_slice_op.cc +++ b/paddle/operators/sequence_slice_op.cc @@ -48,7 +48,7 @@ class SequenceSliceOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), @@ -69,7 +69,7 @@ class SequenceSliceGradOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), diff --git a/paddle/operators/softmax_with_cross_entropy_op.cc b/paddle/operators/softmax_with_cross_entropy_op.cc index 41e65b701e6..7135780c92d 100644 --- a/paddle/operators/softmax_with_cross_entropy_op.cc +++ b/paddle/operators/softmax_with_cross_entropy_op.cc @@ -118,7 +118,7 @@ class SoftmaxWithCrossEntropyOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("Logits")->type()), @@ -159,7 +159,7 @@ class SoftmaxWithCrossEntropyOpGrad : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType( diff --git a/paddle/operators/sum_op.cc b/paddle/operators/sum_op.cc index b86e8266425..a4c08430d85 100644 --- a/paddle/operators/sum_op.cc +++ b/paddle/operators/sum_op.cc @@ -53,7 +53,7 @@ class SumOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { auto x_vars = ctx.MultiInputVar("X"); if (x_vars[0]->IsType()) { diff --git a/paddle/operators/uniform_random_op.cc b/paddle/operators/uniform_random_op.cc index 4d5dd86cb81..3a314bdb9b0 100644 --- a/paddle/operators/uniform_random_op.cc +++ b/paddle/operators/uniform_random_op.cc @@ -63,7 +63,7 @@ class UniformRandomOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( static_cast(ctx.Attr("dtype")), diff --git a/paddle/operators/unpool_op.cc b/paddle/operators/unpool_op.cc index aeed9679b2a..50cee11a7a2 100644 --- a/paddle/operators/unpool_op.cc +++ b/paddle/operators/unpool_op.cc @@ -71,7 +71,7 @@ int OutputSize(int input_size, int ksize, int padding, int stride) { class UnpoolOp : public framework::OperatorWithKernel { protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), @@ -110,7 +110,7 @@ class UnpoolOp : public framework::OperatorWithKernel { class UnpoolOpGrad : public framework::OperatorWithKernel { protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext& ctx) const override { return framework::OpKernelType( framework::ToDataType(ctx.Input("X")->type()), diff --git a/paddle/platform/place.cc b/paddle/platform/place.cc index 249527e3e13..f05260ccac4 100644 --- a/paddle/platform/place.cc +++ b/paddle/platform/place.cc @@ -51,6 +51,18 @@ bool places_are_same_class(const Place &p1, const Place &p2) { return p1.which() == p2.which(); } +bool is_same_place(const Place &p1, const Place &p2) { + if (places_are_same_class(p1, p2)) { + if (is_cpu_place(p1)) { + return true; + } else { + return boost::get(p1) == boost::get(p2); + } + } else { + return false; + } +} + std::ostream &operator<<(std::ostream &os, const Place &p) { detail::PlacePrinter printer(os); boost::apply_visitor(printer, p); diff --git a/paddle/platform/place.h b/paddle/platform/place.h index 76b5c502cc4..ba32dd3be61 100644 --- a/paddle/platform/place.h +++ b/paddle/platform/place.h @@ -61,6 +61,7 @@ const CPUPlace default_cpu(); bool is_gpu_place(const Place &); bool is_cpu_place(const Place &); bool places_are_same_class(const Place &, const Place &); +bool is_same_place(const Place &, const Place &); std::ostream &operator<<(std::ostream &, const Place &); diff --git a/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py b/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py index 8acd470c5ed..74ca56182c4 100644 --- a/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py +++ b/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py @@ -4,6 +4,7 @@ import numpy as np import paddle.v2 as paddle import paddle.v2.dataset.conll05 as conll05 import paddle.v2.fluid as fluid +import time word_dict, verb_dict, label_dict = conll05.get_dict() word_dict_len = len(word_dict) @@ -160,7 +161,8 @@ def main(): paddle.reader.shuffle( paddle.dataset.conll05.test(), buf_size=8192), batch_size=BATCH_SIZE) - place = fluid.CPUPlace() + #place = fluid.CPUPlace() + place = fluid.CUDAPlace(0) feeder = fluid.DataFeeder( feed_list=[ word, ctx_n2, ctx_n1, ctx_0, ctx_p1, ctx_p2, predicate, mark, target @@ -174,6 +176,7 @@ def main(): embedding_param.set( load_parameter(conll05.get_embedding(), word_dict_len, word_dim), place) + start_time = time.time() batch_id = 0 for pass_id in xrange(PASS_NUM): chunk_evaluator.reset(exe) @@ -191,6 +194,9 @@ def main(): f1_score) + " pass_precision:" + str( pass_precision) + " pass_recall:" + str(pass_recall) + " pass_f1_score:" + str(pass_f1_score)) + if batch_id != 0: + print("second per batch: " + str((time.time() - start_time) + / batch_id)) # exit early for CI exit(0) -- GitLab From 872b1c888439a92df72ee2b8516e92e198ed6e03 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Mon, 8 Jan 2018 13:31:10 +0800 Subject: [PATCH 0119/2305] Stop gradient when pool_type=='max' --- python/paddle/v2/fluid/layers/nn.py | 99 +++++++++++++++-------------- 1 file changed, 52 insertions(+), 47 deletions(-) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 55b35ad543b..8b5cc30c25f 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -151,7 +151,7 @@ def embedding(input, size, is_sparse=False, param_attr=None, dtype='float32'): Args: input(Variable): Input to the function - size(tuple|list|None): Shape of the look up table parameter + size(tuple|list|None): Shape of the look up table parameter is_sparse(bool): Boolean flag that specifying whether the input is sparse param_attr(ParamAttr): Parameters for this layer dtype(np.dtype|core.DataType|str): The type of data : float32, float_16, int etc @@ -366,9 +366,9 @@ def cross_entropy(input, label, **kwargs): 1) One-hot cross-entropy: `soft_label = False`, `Label[i, 0]` indicates the class index for sample i: - + .. math:: - + Y[i] = -\log(X[i, Label[i]]) 2) Soft-label cross-entropy: @@ -386,15 +386,15 @@ def cross_entropy(input, label, **kwargs): As a special case of 2), when each row of 'label' has only one non-zero element which is equal to 1, soft-label cross-entropy degenerates to a one-hot cross-entropy with one-hot label representation. - + Args: - input (Variable|list): a 2-D tensor with shape [N x D], where N is the - batch size and D is the number of classes. This input is a probability + input (Variable|list): a 2-D tensor with shape [N x D], where N is the + batch size and D is the number of classes. This input is a probability computed by the previous operator, which is almost always the result of a softmax operator. - label (Variable|list): the ground truth which is a 2-D tensor. When - `soft_label` is set to `False`, `label` is a tensor with shape - [N x 1]. When `soft_label` is set to `True`, `label` is a + label (Variable|list): the ground truth which is a 2-D tensor. When + `soft_label` is set to `False`, `label` is a tensor with shape + [N x 1]. When `soft_label` is set to `True`, `label` is a tensor with shape [N x D]. soft_label (bool, via `**kwargs`): a flag indicating whether to interpretate the given labels as soft labels, default `False`. @@ -403,7 +403,7 @@ def cross_entropy(input, label, **kwargs): A 2-D tensor with shape [N x 1], the cross entropy loss. Raises: - `ValueError`: 1) the 1st dimension of `input` and `label` are not equal; 2) when \ + `ValueError`: 1) the 1st dimension of `input` and `label` are not equal; 2) when \ `soft_label == True`, and the 2nd dimension of `input` and `label` are not \ equal; 3) when `soft_label == False`, and the 2nd dimension of `label` is not 1. @@ -699,9 +699,9 @@ def conv2d(input, def sequence_pool(input, pool_type, **kwargs): """ - This function add the operator for sequence pooling. - It pools features of all time-steps of each instance, and is applied - on top of the input using pool_type mentioned in the parameters. + This function add the operator for sequence pooling. + It pools features of all time-steps of each instance, and is applied + on top of the input using pool_type mentioned in the parameters. It supports four pool_type: @@ -730,7 +730,7 @@ def sequence_pool(input, pool_type, **kwargs): Args: input(variable): The input variable which is a LoDTensor. - pool_type (string): The pooling type of sequence_pool. + pool_type (string): The pooling type of sequence_pool. It supports average, sum, sqrt and max. Returns: @@ -740,7 +740,7 @@ def sequence_pool(input, pool_type, **kwargs): .. code-block:: python - x = fluid.layers.data(name='x', shape=[7, 1], + x = fluid.layers.data(name='x', shape=[7, 1], dtype='float32', lod_level=1) avg_x = fluid.layers.sequence_pool(input=x, pool_type='average') sum_x = fluid.layers.sequence_pool(input=x, pool_type='sum') @@ -759,6 +759,11 @@ def sequence_pool(input, pool_type, **kwargs): "MaxIndex": max_index}, attrs={"pooltype": pool_type.upper()}) + # when pool_type is max, variable max_index is initialized, + # so we stop the gradient explicitly here + if pool_type == 'max': + max_index.stop_gradient = True + return pool_out @@ -788,7 +793,7 @@ def sequence_first_step(input, **kwargs): .. code-block:: python - x = fluid.layers.data(name='x', shape=[7, 1], + x = fluid.layers.data(name='x', shape=[7, 1], dtype='float32', lod_level=1) x_first_step = fluid.layers.sequence_first_step(input=x) """ @@ -821,7 +826,7 @@ def sequence_last_step(input, **kwargs): .. code-block:: python - x = fluid.layers.data(name='x', shape=[7, 1], + x = fluid.layers.data(name='x', shape=[7, 1], dtype='float32', lod_level=1) x_last_step = fluid.layers.sequence_last_step(input=x) """ @@ -1240,17 +1245,17 @@ def lstm_unit(x_t, def reduce_sum(input, dim=None, keep_dim=False): """ - Computes the sum of tensor elements over the given dimension. + Computes the sum of tensor elements over the given dimension. Args: input (Variable): The input variable which is a Tensor or LoDTensor. - dim (int|None): The dimension along which the sum is performed. If - :attr:`None`, sum all elements of :attr:`input` and return a - Tensor variable with a single element, otherwise must be in the - range :math:`[-rank(input), rank(input))`. If :math:`dim < 0`, + dim (int|None): The dimension along which the sum is performed. If + :attr:`None`, sum all elements of :attr:`input` and return a + Tensor variable with a single element, otherwise must be in the + range :math:`[-rank(input), rank(input))`. If :math:`dim < 0`, the dimension to reduce is :math:`rank + dim`. - keep_dim (bool): Whether to reserve the reduced dimension in the - output Tensor. The result tensor will have one fewer dimension + keep_dim (bool): Whether to reserve the reduced dimension in the + output Tensor. The result tensor will have one fewer dimension than the :attr:`input` unless :attr:`keep_dim` is true. Returns: @@ -1284,17 +1289,17 @@ def reduce_sum(input, dim=None, keep_dim=False): def reduce_mean(input, dim=None, keep_dim=False): """ - Computes the mean of tensor elements over the given dimension. + Computes the mean of tensor elements over the given dimension. Args: input (Variable): The input variable which is a Tensor or LoDTensor. - dim (int|None): The dimension along which the mean is computed. If - :attr:`None`, compute the mean over all elements of :attr:`input` - and return a Tensor variable with a single element, otherwise - must be in the range :math:`[-rank(input), rank(input))`. If + dim (int|None): The dimension along which the mean is computed. If + :attr:`None`, compute the mean over all elements of :attr:`input` + and return a Tensor variable with a single element, otherwise + must be in the range :math:`[-rank(input), rank(input))`. If :math:`dim < 0`, the dimension to reduce is :math:`rank + dim`. - keep_dim (bool): Whether to reserve the reduced dimension in the - output Tensor. The result tensor will have one fewer dimension + keep_dim (bool): Whether to reserve the reduced dimension in the + output Tensor. The result tensor will have one fewer dimension than the :attr:`input` unless :attr:`keep_dim` is true. Returns: @@ -1328,22 +1333,22 @@ def reduce_mean(input, dim=None, keep_dim=False): def reduce_max(input, dim=None, keep_dim=False): """ - Computes the maximum of tensor elements over the given dimension. + Computes the maximum of tensor elements over the given dimension. Args: input (Variable): The input variable which is a Tensor or LoDTensor. - dim (int|None): The dimension along which the maximum is computed. - If :attr:`None`, compute the maximum over all elements of - :attr:`input` and return a Tensor variable with a single element, - otherwise must be in the range :math:`[-rank(input), rank(input))`. + dim (int|None): The dimension along which the maximum is computed. + If :attr:`None`, compute the maximum over all elements of + :attr:`input` and return a Tensor variable with a single element, + otherwise must be in the range :math:`[-rank(input), rank(input))`. If :math:`dim < 0`, the dimension to reduce is :math:`rank + dim`. - keep_dim (bool): Whether to reserve the reduced dimension in the - output Tensor. The result tensor will have one fewer dimension + keep_dim (bool): Whether to reserve the reduced dimension in the + output Tensor. The result tensor will have one fewer dimension than the :attr:`input` unless :attr:`keep_dim` is true. Returns: Variable: The reduced Tensor variable. - + Examples: .. code-block:: python @@ -1372,22 +1377,22 @@ def reduce_max(input, dim=None, keep_dim=False): def reduce_min(input, dim=None, keep_dim=False): """ - Computes the minimum of tensor elements over the given dimension. + Computes the minimum of tensor elements over the given dimension. Args: input (Variable): The input variable which is a Tensor or LoDTensor. - dim (int|None): The dimension along which the minimum is computed. - If :attr:`None`, compute the minimum over all elements of - :attr:`input` and return a Tensor variable with a single element, - otherwise must be in the range :math:`[-rank(input), rank(input))`. + dim (int|None): The dimension along which the minimum is computed. + If :attr:`None`, compute the minimum over all elements of + :attr:`input` and return a Tensor variable with a single element, + otherwise must be in the range :math:`[-rank(input), rank(input))`. If :math:`dim < 0`, the dimension to reduce is :math:`rank + dim`. - keep_dim (bool): Whether to reserve the reduced dimension in the - output Tensor. The result tensor will have one fewer dimension + keep_dim (bool): Whether to reserve the reduced dimension in the + output Tensor. The result tensor will have one fewer dimension than the :attr:`input` unless :attr:`keep_dim` is true. Returns: Variable: The reduced Tensor variable. - + Examples: .. code-block:: python -- GitLab From 63ff0b4ba94ea9f45b36b50530994594eb81e12f Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Mon, 8 Jan 2018 13:36:18 +0800 Subject: [PATCH 0120/2305] Refine get_places --- paddle/framework/block_desc.cc | 4 +- paddle/framework/block_desc.h | 2 +- paddle/framework/executor.cc | 6 +- paddle/framework/framework.proto | 1 + paddle/framework/op_desc.cc | 2 +- paddle/operators/get_places_op.cc | 78 ++++++++++++++----- paddle/operators/lod_rank_table_op.cc | 2 +- paddle/operators/sum_op.cc | 11 ++- .../operators/tensor_array_read_write_op.cc | 3 +- paddle/platform/place.h | 2 + paddle/pybind/protobuf.cc | 3 +- python/paddle/v2/fluid/framework.py | 2 +- .../v2/fluid/layers/{devie.py => device.py} | 6 +- .../v2/fluid/tests/test_get_places_op.py | 17 ++++ python/paddle/v2/fluid/tests/test_layers.py | 1 + 15 files changed, 100 insertions(+), 40 deletions(-) rename python/paddle/v2/fluid/layers/{devie.py => device.py} (68%) create mode 100644 python/paddle/v2/fluid/tests/test_get_places_op.py diff --git a/paddle/framework/block_desc.cc b/paddle/framework/block_desc.cc index 0668b08ff7a..54498e175da 100644 --- a/paddle/framework/block_desc.cc +++ b/paddle/framework/block_desc.cc @@ -53,12 +53,12 @@ VarDesc *BlockDesc::FindVarRecursive(const std::string &name) const { return it->second.get(); } -VarDesc *BlockDesc::FindRecursiveOrCreateVar(const std::string &name_bytes) { +VarDesc &BlockDesc::FindRecursiveOrCreateVar(const std::string &name_bytes) { VarDesc *res = FindVarRecursive(name_bytes); if (res == nullptr) { res = Var(name_bytes); } - return res; + return *res; } bool BlockDesc::HasVarRecursive(const std::string &name) const { diff --git a/paddle/framework/block_desc.h b/paddle/framework/block_desc.h index 6c8c81b332d..4b609e4bcb6 100644 --- a/paddle/framework/block_desc.h +++ b/paddle/framework/block_desc.h @@ -57,7 +57,7 @@ class BlockDesc { VarDesc *FindVarRecursive(const std::string &name_bytes) const; - VarDesc *FindRecursiveOrCreateVar(const std::string &name_bytes); + VarDesc &FindRecursiveOrCreateVar(const std::string &name_bytes); bool HasVarRecursive(const std::string &var_name) const; diff --git a/paddle/framework/executor.cc b/paddle/framework/executor.cc index bf1f0471ccb..ace653b3eeb 100644 --- a/paddle/framework/executor.cc +++ b/paddle/framework/executor.cc @@ -21,6 +21,7 @@ limitations under the License. */ #include "paddle/framework/lod_rank_table.h" #include "paddle/framework/lod_tensor_array.h" #include "paddle/framework/op_registry.h" +#include "paddle/platform/place.h" DEFINE_bool(check_nan_inf, false, "Checking whether operator produce NAN/INF or not. It will be " @@ -49,10 +50,13 @@ static void CreateTensor(Variable* var, proto::VarDesc::VarType var_type) { var->GetMutable(); } else if (var_type == proto::VarDesc::LOD_TENSOR_ARRAY) { var->GetMutable(); + } else if (var_type == proto::VarDesc::PLACE_LIST) { + var->GetMutable(); } else { PADDLE_THROW( "Variable type %d is not in " - "[LoDTensor, SelectedRows, FEED_MINIBATCH, FETCH_LIST, LOD_RANK_TABLE]", + "[LoDTensor, SelectedRows, FEED_MINIBATCH, FETCH_LIST, LOD_RANK_TABLE," + " PLACE_LIST]", var_type); } } diff --git a/paddle/framework/framework.proto b/paddle/framework/framework.proto index 4f2746e4b86..ea69b87e2ac 100644 --- a/paddle/framework/framework.proto +++ b/paddle/framework/framework.proto @@ -123,6 +123,7 @@ message VarDesc { STEP_SCOPES = 5; LOD_RANK_TABLE = 6; LOD_TENSOR_ARRAY = 7; + PLACE_LIST = 8; } required string name = 1; required VarType type = 2; diff --git a/paddle/framework/op_desc.cc b/paddle/framework/op_desc.cc index e02e572af2c..c0f414128c8 100644 --- a/paddle/framework/op_desc.cc +++ b/paddle/framework/op_desc.cc @@ -383,7 +383,7 @@ void OpDesc::InferVarType(BlockDesc *block) const { for (auto &out_pair : this->outputs_) { for (auto &out_var_name : out_pair.second) { block->FindRecursiveOrCreateVar(out_var_name) - ->SetType(proto::VarDesc::LOD_TENSOR); + .SetType(proto::VarDesc::LOD_TENSOR); } } } diff --git a/paddle/operators/get_places_op.cc b/paddle/operators/get_places_op.cc index 4d5059c264e..291bbbcb3a7 100644 --- a/paddle/operators/get_places_op.cc +++ b/paddle/operators/get_places_op.cc @@ -12,7 +12,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +#include #include "paddle/framework/op_registry.h" +#include "paddle/operators/detail/safe_ref.h" #include "paddle/platform/place.h" #ifdef PADDLE_WITH_CUDA #include "paddle/platform/gpu_info.h" @@ -21,6 +23,14 @@ limitations under the License. */ namespace paddle { namespace operators { +static size_t CUDADevCount() { +#ifdef PADDLE_WITH_CUDA + return platform::GetCUDADeviceCount(); +#else + return 0UL; +#endif +} + class GetPlacesOp : public framework::OperatorBase { public: GetPlacesOp(const std::string &type, const framework::VariableNameMap &inputs, @@ -28,28 +38,34 @@ class GetPlacesOp : public framework::OperatorBase { const framework::AttributeMap &attrs) : OperatorBase(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, - const platform::DeviceContext &dev_ctx) const override { + const platform::Place &place) const override { std::string device_type = Attr("device_type"); - auto device_count = Attr("device_count"); + auto device_count = static_cast(Attr("device_count")); + if (device_count == 0) { + if (device_type == "CUDA") { + device_count = CUDADevCount(); + } else if (device_type == "CPU") { + device_count = std::thread::hardware_concurrency(); + } + } + PADDLE_ENFORCE_NE(device_count, 0, "Cannot indicate %s device count", + device_type); auto out_var_name = Output("Out"); - auto *out_var = scope.FindVar(out_var_name); - PADDLE_ENFORCE(out_var != nullptr, "Output variable %s cannot be found", - out_var_name); - - auto &places = *(out_var->GetMutable>()); - places.resize(device_count); + auto &places = + *(detail::Ref(scope.FindVar(out_var_name), + "Output variable %s cannot be found", out_var_name) + .GetMutable()); + places.reserve(device_count); if (device_type == "CUDA") { -#ifdef PADDLE_WITH_CUDA - PADDLE_ENFORCE_LT(device_count, platform::GetCUDADeviceCount()); - for (int i = 0; i < device_count; i++) { - places.emplace_back(platform::GPUPlace(i)); + PADDLE_ENFORCE_LE(device_count, CUDADevCount(), + "Only %d CUDA devices found, cannot set to %d", + CUDADevCount(), device_count); + for (size_t i = 0; i < device_count; ++i) { + places.emplace_back(platform::CUDAPlace(i)); } -#else - PADDLE_THROW("'GPUPlace' is not supported in CPU only device."); -#endif } else if (device_type == "CPU") { - for (int i = 0; i < device_count; i++) { + for (size_t i = 0; i < device_count; ++i) { places.emplace_back(platform::CPUPlace()); } } @@ -61,18 +77,38 @@ class GetPlacesOpProtoMaker : public framework::OpProtoAndCheckerMaker { GetPlacesOpProtoMaker(OpProto *proto, OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddOutput("Out", "vector of Place"); - AddAttr("device_count", "(int)device count").SetDefault(1); + AddAttr("device_count", "device count").SetDefault(1); AddAttr("device_type", - "(string), deivce type can be \"CPU\" and \"CUDA\"") + R"(device type must be in ["CPU", "CUDA"])") .InEnum({"CPU", "CUDA"}); AddComment(R"DOC( -Returns a list of places based on flags. The list will be used for parallel execution. - +Returns a list of places based on flags. The list will be used for parallel +execution. )DOC"); } }; + +class GetPlacesInferVarType : public framework::VarTypeInference { + public: + void operator()(const framework::OpDesc &op_desc, + framework::BlockDesc *block) const override { + for (auto &o_name : op_desc.Output("Out")) { + block->FindRecursiveOrCreateVar(o_name).SetType( + framework::proto::VarDesc::PLACE_LIST); + } + } +}; + +class GetPlacesInferShape : public framework::InferShapeBase { + public: + void operator()(framework::InferShapeContext *context) const override { + // Do nothing + } +}; + } // namespace operators } // namespace paddle namespace ops = paddle::operators; -REGISTER_OPERATOR(get_places, ops::GetPlacesOp, ops::GetPlacesOpProtoMaker); +REGISTER_OPERATOR(get_places, ops::GetPlacesOp, ops::GetPlacesOpProtoMaker, + ops::GetPlacesInferVarType, ops::GetPlacesInferShape); diff --git a/paddle/operators/lod_rank_table_op.cc b/paddle/operators/lod_rank_table_op.cc index 8711dd62c88..692b9bf3710 100644 --- a/paddle/operators/lod_rank_table_op.cc +++ b/paddle/operators/lod_rank_table_op.cc @@ -66,7 +66,7 @@ class LoDRankTableInferVarType : public framework::VarTypeInference { void operator()(const framework::OpDesc &op_desc, framework::BlockDesc *block) const override { for (auto &o : op_desc.Output("Out")) { - block->FindRecursiveOrCreateVar(o)->SetType( + block->FindRecursiveOrCreateVar(o).SetType( framework::proto::VarDesc::LOD_RANK_TABLE); } } diff --git a/paddle/operators/sum_op.cc b/paddle/operators/sum_op.cc index b86e8266425..a2bea6a0e62 100644 --- a/paddle/operators/sum_op.cc +++ b/paddle/operators/sum_op.cc @@ -122,17 +122,17 @@ class SumOpVarTypeInference : public framework::VarTypeInference { for (auto& name : op_desc.Input("X")) { VLOG(10) << name << " " - << block->FindRecursiveOrCreateVar(name)->GetType(); + << block->FindRecursiveOrCreateVar(name).GetType(); } bool any_input_is_lod_tensor = std::any_of( inputs.begin(), inputs.end(), [block](const std::string& name) { - return block->FindRecursiveOrCreateVar(name)->GetType() == + return block->FindRecursiveOrCreateVar(name).GetType() == framework::proto::VarDesc::LOD_TENSOR; }); auto is_tensor_array = [block](const std::string& name) { - return detail::Ref(block->FindRecursiveOrCreateVar(name)).GetType() == + return block->FindRecursiveOrCreateVar(name).GetType() == framework::proto::VarDesc::LOD_TENSOR_ARRAY; }; @@ -146,8 +146,7 @@ class SumOpVarTypeInference : public framework::VarTypeInference { std::ostringstream os; for (auto& each : inputs) { os << " " << each << " type is " - << detail::Ref(block->FindRecursiveOrCreateVar(each)).GetType() - << "\n"; + << block->FindRecursiveOrCreateVar(each).GetType() << "\n"; } PADDLE_ENFORCE(all_inputs_are_tensor_array, "Not all inputs are tensor array:\n%s", os.str()); @@ -158,7 +157,7 @@ class SumOpVarTypeInference : public framework::VarTypeInference { } auto out_var_name = op_desc.Output("Out").front(); - auto& out_var = detail::Ref(block->FindRecursiveOrCreateVar(out_var_name)); + auto& out_var = block->FindRecursiveOrCreateVar(out_var_name); out_var.SetType(var_type); auto& in_var = detail::Ref(block->FindVarRecursive(inputs.front())); out_var.SetDataType(in_var.GetDataType()); diff --git a/paddle/operators/tensor_array_read_write_op.cc b/paddle/operators/tensor_array_read_write_op.cc index d5ff3e3fce2..a6dceb2e3a1 100644 --- a/paddle/operators/tensor_array_read_write_op.cc +++ b/paddle/operators/tensor_array_read_write_op.cc @@ -106,8 +106,7 @@ class WriteToArrayInferVarType : public framework::VarTypeInference { auto x_name = op_desc.Input("X")[0]; auto out_name = op_desc.Output("Out")[0]; VLOG(10) << "Set Variable " << out_name << " as LOD_TENSOR_ARRAY"; - auto &out = detail::Ref(block->FindRecursiveOrCreateVar(out_name), - "Cannot found %s", out_name); + auto &out = block->FindRecursiveOrCreateVar(out_name); out.SetType(framework::proto::VarDesc::LOD_TENSOR_ARRAY); auto *x = block->FindVarRecursive(x_name); if (x != nullptr) { diff --git a/paddle/platform/place.h b/paddle/platform/place.h index 76b5c502cc4..fb30241d799 100644 --- a/paddle/platform/place.h +++ b/paddle/platform/place.h @@ -52,6 +52,8 @@ struct IsCUDAPlace : public boost::static_visitor { typedef boost::variant Place; +using PlaceList = std::vector; + void set_place(const Place &); const Place &get_place(); diff --git a/paddle/pybind/protobuf.cc b/paddle/pybind/protobuf.cc index 564a3700011..4f959481537 100644 --- a/paddle/pybind/protobuf.cc +++ b/paddle/pybind/protobuf.cc @@ -231,7 +231,8 @@ void BindVarDsec(py::module &m) { .value("FETCH_LIST", proto::VarDesc::FETCH_LIST) .value("STEP_SCOPES", proto::VarDesc::STEP_SCOPES) .value("LOD_RANK_TABLE", proto::VarDesc::LOD_RANK_TABLE) - .value("LOD_TENSOR_ARRAY", proto::VarDesc::LOD_TENSOR_ARRAY); + .value("LOD_TENSOR_ARRAY", proto::VarDesc::LOD_TENSOR_ARRAY) + .value("PLACE_LIST", proto::VarDesc::PLACE_LIST); } void BindOpDesc(py::module &m) { diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index d5ad8bb5138..2dfb8b62416 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -448,7 +448,7 @@ class Operator(object): no_kernel_op_set = { 'feed', 'fetch', 'save', 'load', 'recurrent', 'rnn_memory_helper_grad', 'conditional_block', 'while', 'send', - 'recv', 'get_places', 'parallel_do' + 'recv', 'parallel_do' } if type not in no_kernel_op_set: self.desc.infer_var_type(self.block.desc) diff --git a/python/paddle/v2/fluid/layers/devie.py b/python/paddle/v2/fluid/layers/device.py similarity index 68% rename from python/paddle/v2/fluid/layers/devie.py rename to python/paddle/v2/fluid/layers/device.py index 89c07584ef5..c2355ed8020 100644 --- a/python/paddle/v2/fluid/layers/devie.py +++ b/python/paddle/v2/fluid/layers/device.py @@ -3,14 +3,14 @@ All util layers. """ from ..layer_helper import LayerHelper -from ..framework import Variable +from ..framework import unique_name __all__ = ['get_places'] -def get_places(device_count, device_type="CPU"): +def get_places(device_count=0, device_type="CPU"): helper = LayerHelper('get_places', **locals()) - out_places = helper.create_tmp_variable(dtype=helper.input_dtype()) + out_places = helper.create_variable(name=unique_name(helper.name + ".out")) helper.append_op( type='get_places', outputs={"Out": [out_places]}, diff --git a/python/paddle/v2/fluid/tests/test_get_places_op.py b/python/paddle/v2/fluid/tests/test_get_places_op.py new file mode 100644 index 00000000000..c4346f6786c --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_get_places_op.py @@ -0,0 +1,17 @@ +import paddle.v2.fluid as fluid +import decorators +import unittest + + +class TestGetPlaces(unittest.TestCase): + @decorators.prog_scope() + def test_get_places(self): + places = fluid.layers.get_places() + cpu = fluid.CPUPlace() + exe = fluid.Executor(cpu) + exe.run(fluid.default_main_program()) + self.assertEqual(places.type, fluid.core.VarDesc.VarType.PLACE_LIST) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/paddle/v2/fluid/tests/test_layers.py b/python/paddle/v2/fluid/tests/test_layers.py index 69cb23524fb..a56277d216c 100644 --- a/python/paddle/v2/fluid/tests/test_layers.py +++ b/python/paddle/v2/fluid/tests/test_layers.py @@ -200,6 +200,7 @@ class TestBook(unittest.TestCase): program = Program() with program_guard(program): x = layers.get_places(device_count=4) + self.assertIsNotNone(x) print(str(program)) -- GitLab From e94db381baa469d73806e4921a87daab260f5060 Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Mon, 8 Jan 2018 14:05:45 +0800 Subject: [PATCH 0121/2305] Feature/add shared layout (#7233) * "reuse ShareLoD with no regret" * "removed base class shareLayout" * "fix CI" --- paddle/framework/data_transform.cc | 5 ++--- paddle/framework/op_desc.cc | 1 + paddle/framework/operator.cc | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/paddle/framework/data_transform.cc b/paddle/framework/data_transform.cc index 55825c5b7d2..fed958db158 100644 --- a/paddle/framework/data_transform.cc +++ b/paddle/framework/data_transform.cc @@ -146,9 +146,6 @@ void TransDataLayout(const std::vector& axis, auto* dst = out->GetMutable(); PADDLE_ENFORCE(arity(src.dims()) == 4, "Input Arity Only Suppport 4!"); - auto place = kernel_pair.second.place_; - CopyFrom(src, place, *ctx, dst); - auto src_dim = src.dims(); std::vector dst_dim; @@ -158,6 +155,8 @@ void TransDataLayout(const std::vector& axis, } dst->Resize(make_ddim(dst_dim)); + auto place = kernel_pair.second.place_; + dst->mutable_data(place, src.type()); auto src_type = kernel_pair.first.data_type_; framework::VisitDataType(src_type, CastDataLayout(ctx, axis, src, dst)); diff --git a/paddle/framework/op_desc.cc b/paddle/framework/op_desc.cc index e02e572af2c..47c91290e4b 100644 --- a/paddle/framework/op_desc.cc +++ b/paddle/framework/op_desc.cc @@ -66,6 +66,7 @@ class CompileTimeInferShapeContext : public InferShapeContext { out); out_var->SetLoDLevel(in_var->GetLoDLevel()); } + bool IsRuntime() const override; protected: diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index adc85b1049f..a1f1be5f342 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -417,6 +417,25 @@ class RuntimeInferShapeContext : public InferShapeContext { auto in_tensor = in_var->Get(); auto* out_tensor = out_var->GetMutable(); out_tensor->set_lod(in_tensor.lod()); + + // TODO(dzhwinter) : reuse ShareLoD in most operators. + // Need to call ShareLayout explicitly in sequence related ops. + // Shall we have a better method to shared info between in/out Tensor? + out_tensor->set_layout(in_tensor.layout()); + } + + void ShareLayout(const std::string& in, const std::string& out, size_t i = 0, + size_t j = 0) const { + PADDLE_ENFORCE_LT(i, Inputs(in).size()); + PADDLE_ENFORCE_LT(j, Outputs(out).size()); + Variable* in_var = scope_.FindVar(Inputs(in)[i]); + Variable* out_var = scope_.FindVar(Outputs(out)[j]); + if (!in_var->IsType()) return; + PADDLE_ENFORCE(out_var->IsType(), + "The %d-th output of Output(%s) must be LoDTensor.", j, out); + auto in_tensor = in_var->Get(); + auto* out_tensor = out_var->GetMutable(); + out_tensor->set_layout(in_tensor.layout()); } bool IsRuntime() const override { return true; } -- GitLab From 8e8e5a89f8f015b8a8c0e0a3a6e129c8276f92b1 Mon Sep 17 00:00:00 2001 From: gx_wind Date: Mon, 8 Jan 2018 14:06:17 +0800 Subject: [PATCH 0122/2305] fix coding standard --- adversarial/README.md | 8 ++++- adversarial/advbox/__init__.py | 1 - adversarial/advbox/attacks/base.py | 1 + adversarial/advbox/attacks/gradientsign.py | 9 ++++-- adversarial/advbox/models/base.py | 2 +- adversarial/advbox/models/paddle.py | 37 ++++++++++------------ 6 files changed, 31 insertions(+), 27 deletions(-) diff --git a/adversarial/README.md b/adversarial/README.md index 7c9502828f3..51da21918a9 100644 --- a/adversarial/README.md +++ b/adversarial/README.md @@ -1,3 +1,9 @@ # Advbox -Advbox is a Python toolbox to create adversarial examples that fool neural networks. It requires Python and paddle. \ No newline at end of file +Advbox is a Python toolbox to create adversarial examples that fool neural networks. It requires Python and paddle. + +## How to use + +1. train a model and save it's parameters. (like fluid_mnist.py) +2. load the parameters which is trained in step1, then reconstruct the model.(like mnist_tutorial_fgsm.py) +3. use advbox to generate the adversarial sample. diff --git a/adversarial/advbox/__init__.py b/adversarial/advbox/__init__.py index 4beb6be0a20..f56f14f18da 100644 --- a/adversarial/advbox/__init__.py +++ b/adversarial/advbox/__init__.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - """ A set of tools for generating adversarial example on paddle platform """ diff --git a/adversarial/advbox/attacks/base.py b/adversarial/advbox/attacks/base.py index 9cc2bfb8543..dab1dbbeb02 100644 --- a/adversarial/advbox/attacks/base.py +++ b/adversarial/advbox/attacks/base.py @@ -7,6 +7,7 @@ import abc abstractmethod = abc.abstractmethod + class Attack(object): """ Abstract base class for adversarial attacks. `Attack` represent an adversarial attack diff --git a/adversarial/advbox/attacks/gradientsign.py b/adversarial/advbox/attacks/gradientsign.py index 6c188f62498..37fbdb11328 100644 --- a/adversarial/advbox/attacks/gradientsign.py +++ b/adversarial/advbox/attacks/gradientsign.py @@ -5,7 +5,8 @@ from __future__ import division import numpy as np from collections import Iterable from .base import Attack - + + class GradientSignAttack(Attack): """ This attack was originally implemented by Goodfellow et al. (2015) with the @@ -22,10 +23,11 @@ class GradientSignAttack(Attack): gradient_sign = np.sign(gradient) * (max_ - min_) if not isinstance(epsilons, Iterable): - epsilons = np.linspace(0, 1, num = epsilons + 1) + epsilons = np.linspace(0, 1, num=epsilons + 1) for epsilon in epsilons: - adv_img = image_batch[0][0].reshape(gradient_sign.shape) + epsilon * gradient_sign + adv_img = image_batch[0][0].reshape( + gradient_sign.shape) + epsilon * gradient_sign adv_img = np.clip(adv_img, min_, max_) adv_label = np.argmax(self.model.predict([(adv_img, 0)])) #print("pre_label="+str(pre_label)+ " adv_label="+str(adv_label)) @@ -33,4 +35,5 @@ class GradientSignAttack(Attack): #print(epsilon, pre_label, adv_label) return adv_img + FGSM = GradientSignAttack diff --git a/adversarial/advbox/models/base.py b/adversarial/advbox/models/base.py index 91b6fe4a3c9..2e5c397dc47 100644 --- a/adversarial/advbox/models/base.py +++ b/adversarial/advbox/models/base.py @@ -6,8 +6,8 @@ import abc abstractmethod = abc.abstractmethod -class Model(object): +class Model(object): """ Base class of model to provide attack. diff --git a/adversarial/advbox/models/paddle.py b/adversarial/advbox/models/paddle.py index 831fa6a3627..a72eb148bc6 100644 --- a/adversarial/advbox/models/paddle.py +++ b/adversarial/advbox/models/paddle.py @@ -7,6 +7,7 @@ from paddle.v2.fluid.framework import program_guard from .base import Model + class PaddleModel(Model): """ Create a PaddleModel instance. @@ -30,9 +31,7 @@ class PaddleModel(Model): channel_axis=3, preprocess=None): super(PaddleModel, self).__init__( - bounds=bounds, - channel_axis=channel_axis, - preprocess=preprocess) + bounds=bounds, channel_axis=channel_axis, preprocess=preprocess) if preprocess is None: preprocess = (0, 1) @@ -48,7 +47,8 @@ class PaddleModel(Model): # gradient loss = self._program.block(0).var(self._cost_name) - param_grads = fluid.backward.append_backward(loss, parameter_list=[self._input_name]) + param_grads = fluid.backward.append_backward( + loss, parameter_list=[self._input_name]) self._gradient = param_grads[0][1] def predict(self, image_batch): @@ -61,16 +61,13 @@ class PaddleModel(Model): numpy.ndarray: predictions of the images with shape (batch_size, num_of_classes). """ feeder = fluid.DataFeeder( - feed_list=[self._input_name, self._logits_name], - place=self._place, - program=self._program - ) + feed_list=[self._input_name, self._logits_name], + place=self._place, + program=self._program) predict_var = self._program.block(0).var(self._predict_name) - predict = self._exe.run( - self._program, - feed=feeder.feed(image_batch), - fetch_list=[predict_var] - ) + predict = self._exe.run(self._program, + feed=feeder.feed(image_batch), + fetch_list=[predict_var]) return predict def num_classes(self): @@ -95,12 +92,10 @@ class PaddleModel(Model): """ feeder = fluid.DataFeeder( feed_list=[self._input_name, self._logits_name], - place=self._place, - program=self._program - ) - - grad, = self._exe.run( - self._program, - feed=feeder.feed(image_batch), - fetch_list=[self._gradient]) + place=self._place, + program=self._program) + + grad, = self._exe.run(self._program, + feed=feeder.feed(image_batch), + fetch_list=[self._gradient]) return grad -- GitLab From a79a5bddf0ab9ff1648a0e8954e370a47a04a718 Mon Sep 17 00:00:00 2001 From: ying Date: Mon, 8 Jan 2018 14:03:32 +0800 Subject: [PATCH 0123/2305] update and follow comments. --- doc/howto/usage/capi/index_cn.rst | 2 +- .../capi/organization_of_the_inputs_cn.md | 16 +-- doc/howto/usage/capi/workflow_of_capi.md | 120 ------------------ doc/howto/usage/capi/workflow_of_capi_cn.md | 120 ++++++++++++++++++ 4 files changed, 129 insertions(+), 129 deletions(-) delete mode 100644 doc/howto/usage/capi/workflow_of_capi.md create mode 100644 doc/howto/usage/capi/workflow_of_capi_cn.md diff --git a/doc/howto/usage/capi/index_cn.rst b/doc/howto/usage/capi/index_cn.rst index dfd2009c32b..fd774fbc742 100644 --- a/doc/howto/usage/capi/index_cn.rst +++ b/doc/howto/usage/capi/index_cn.rst @@ -6,4 +6,4 @@ PaddlePaddle C-API compile_paddle_lib_cn.md organization_of_the_inputs_cn.md - workflow_of_capi.md + workflow_of_capi_cn.md diff --git a/doc/howto/usage/capi/organization_of_the_inputs_cn.md b/doc/howto/usage/capi/organization_of_the_inputs_cn.md index 787af8b0a76..563ec5ca21e 100644 --- a/doc/howto/usage/capi/organization_of_the_inputs_cn.md +++ b/doc/howto/usage/capi/organization_of_the_inputs_cn.md @@ -1,11 +1,11 @@ ## 输入/输出数据组织 -这篇文档介绍在使用 PaddlePaddle C-API 时如何组织输入数据,以及如何解析神经网络前向计算的输出数据。 +这篇文档介绍在使用 PaddlePaddle C-API 时如何组织输入数据,以及如何解析神经网络前向计算的输出结果。 ### 输入/输出数据类型 在C-API中,按照基本数据类型在PaddlePaddle内部的定义和实现,输入数据可分为: 1. 一维整型数组 -2. 二维浮点型矩阵 +1. 二维浮点型矩阵 - 稠密矩阵 - 稀疏矩阵 @@ -25,8 +25,8 @@ - **注**: 1. 这篇文档之后部分将会统一使用`argument`来特指PaddlePaddle中神经网络计算层一个输入/输出数据。 - 2. 使用`paddle_ivector`来特指PaddlePaddle中的一维整型数组。 - 3. 使用`paddle_matrix`来特指PaddlePaddle中的二维浮点型矩阵。 + 1. 使用`paddle_ivector`来特指PaddlePaddle中的一维整型数组。 + 1. 使用`paddle_matrix`来特指PaddlePaddle中的二维浮点型矩阵。 ### 组织输入数据 - 一维整型数组 @@ -137,8 +137,8 @@ **注:** 1. 不论序列中的元素在内存中占用多少实际存储空间,`sequence_start_positions`表示的偏移是以“序列中的一个元素”作为统计的基本单位,而不是相对`batch`起始存储地址以数据的存储大小为单位的偏移。 -2. 非序列输入不携带`sequence_start_positions`,非序列输入无需构造`sequence_start_positions`。 -3. **不论是单层序列还是双层序列的序列信息,都使用`paddle_ivector`(也就是PaddlePaddle中的一维整型数组)来存储。** +1. 非序列输入不携带`sequence_start_positions`,非序列输入无需构造`sequence_start_positions`。 +1. **不论是单层序列还是双层序列的序列信息,都使用`paddle_ivector`(也就是PaddlePaddle中的一维整型数组)来存储。** 图2 是PaddlePaddle中单层序列和双层序列存储示意图。

@@ -149,8 +149,8 @@ 图2 (a) 展示了一个含有4个序列的`batch`输入: 1. 4个序列的长度分别为:5、3、2、4; - 2. 这时的`sequence_start_positions`为:`[0, 5, 8, 10, 14]`; - 3. 不论数据域是`paddle_ivector`类型还是`paddle_matrix`类型,都可以通过调用下面的接口为原有的数据输入附加上序列信息,使之变为一个单层序列输入,代码片段如下: + 1. 这时的`sequence_start_positions`为:`[0, 5, 8, 10, 14]`; + 1. 本地训练. 不论数据域是`paddle_ivector`类型还是`paddle_matrix`类型,都可以通过调用下面的接口为原有的数据输入附加上序列信息,使之变为一个单层序列输入,代码片段如下: ```c int seq_pos_array[] = {0, 5, 8, 10, 14}; diff --git a/doc/howto/usage/capi/workflow_of_capi.md b/doc/howto/usage/capi/workflow_of_capi.md deleted file mode 100644 index 56841bdcef9..00000000000 --- a/doc/howto/usage/capi/workflow_of_capi.md +++ /dev/null @@ -1,120 +0,0 @@ -## C-API 使用流程 - -这篇文档介绍 PaddlePaddle C-API 开发预测服务的整体使用流程。 - -### 使用流程 - -使用 C-API 的整体工作流程分为准备预测模型和预测程序开发两部分, 如图1所示。 - -

-
图1. C-API使用流程示意图 -

- -- 准备预测模型 - 1. 将神经网络模型结构进行序列化。 - - 调用C-API预测时,需要提供序列化之后的网络结构和训练好的模型参数文件。 - 1. 将PaddlePaddle训练出的模型参数文件(多个)合并成一个文件。 - - 神经网络模型结构和训练好的模型将被序列化合并入一个文件。 - - 预测时只需加载这一个文件,便于发布。 - - **注意**:以上两种方式只需选择其一即可。 -- 调用 PaddlePaddle C-API 开发预测序 - 1. 初始化PaddlePaddle运行环境。 - 1. 加载模型。 - 1. 创建神经网络的输入,组织输入数据。 - 1. 进行前向计算,获得计算结果。 - 1. 清理。 - -### 准备预测模型 - -在准备预测模型部分,我们以手写数字识别任务为例,这个任务定义了一个含有[两个隐层的简单全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。完整代码可以查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 中的相关脚本。 - -调用C-API开发预测程序需要一个训练好的模型,在终端执行`python mnist_v2.py` -运行[目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 会使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。训练好的模型默认保存在当前运行目录下的`models`目录中。 - -下面,我们将训练好的模型转换成预测模型。 - -1. 序列化神经网络模型配置 - - PaddlePaddle 使用 protobuf 来传输网络配置文件中定义的网络结构和相关参数,在使用 C-API 进行预测时,也需将网络结构使用 protobuf 进行序列化,写入文件中。 - - 调用`paddle.utils.dump_v2_config`中的`dump_v2_config`函数能够将使用 PaddlePaddle V2 API 定义的神经网络结构 dump 到指定文件中。示例代码如下: - - ```python - from paddle.utils.dump_v2_config import dump_v2_config - from mnist_v2 import network - - predict = network(is_infer=True) - dump_v2_config(predict, "trainer_config.bin", True) - ``` - - 对本例,或运行 `python mnist_v2.py --task dump_config`,会对示例中的网络结构进行序列化,并将结果写入当前目录下的`trainer_config.bin`文件中。 - - 使用这种方式,需要**在运行时将神经网络的多个可学习参数放在同一个目录中**,C-API可以通过分别指定序列化后的网络结构文件和参数目录来加载训练好的模型。 - -2. 合并模型文件(可选) - - 一些情况下为了便于发布,希望能够将序列化后的神经网络结构和训练好的模型参数打包进一个文件,这时可以使用`paddle.utils.merge_model`中的`merge_v2_model`接口对神经网络结构和训练好的参数进行序列化,将序列化结果写入一个文件内。 - - 代码示例如下: - - ```python - from paddle.utils.merge_model import merge_v2_modelss - from mnist_v2 import network - - net = network(is_infer=True) - param_file = "models/params_pass_4.tar" - output_file = "output.paddle.model" - merge_v2_model(net, param_file, output_file) - ``` - 对本例,或者直接运行 `python merge_v2_model.py`,序列化结果将会写入当前目录下的`output.paddle.model`文件中。使用这种方式,运行时C-API可以通过指定output.paddle.model文件来加载模型。 - -#### 注意事项 -1. 将训练模型转换成预测模型,需要序列化神经网络结构。在调用`dump_v2_config`时,参数`binary`必须指定为`True`。 -1. **预测使用的网络结构往往不同于训练**,通常需要去掉网络中的:(1)类别标签层;(2)损失函数层;(3)`evaluator`等,只留下核心计算层,请注意是否需要修改网络结构。 -1. 预测时,可以获取网络中定义的任意多个(大于等于一个)层前向计算的结果,需要哪些层的计算结果作为输出,就将这些层加入一个Python list中,作为调用`dump_v2_config`的第一个参数。 - -### 编写预测代码 - -预测代码更多详细示例代码请参考[C-API使用示例](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference) 目录下的代码示例。这一节对图1中预测代码编写的5个步骤进行介绍和说明。 - -#### step 1. 初始化PaddlePaddle运行环境 -第一步需调用[`paddle_init`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/main.h#L27) 初始化PaddlePaddle运行环境。该接口接受两个参数:参数的个数和参数列表。 - -#### step2. 加载模型 - -这里介绍C-API使用中的一个重要概念:Gradient Machine。概念上,在 PaddlePaddle 内部一个GradientMachine类的对象管理着一组计算层(PaddlePaddle Layers)来完成前向和反向计算,并处理与之相关的所有细节。在调用C-API预测时,只需进行前向计算而无需调用反向计算。这篇文档的之后部分我们会使用`gradient machine`来特指调用PaddlePaddle C-API创建的GradientMachine类的对象。 - -每一个 `gradient machine` 都会管理维护一份训练好的模型,下面是两种最常用的模型加载方式: - -1. 从磁盘加载:这时`gradient machine`会独立拥有一份训练好的模型; -1. 共享自其它`gradient machine`的模型:这种情况多出现在使用多线程预测时,通过多个线程共享同一个模型来减少内存开销。可参考[此示例](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/multi_thread/main.c)。 - -- 注意事项 - 1. 使用PaddlePaddle V2 API训练,模型中所有可学习参数会被存为一个压缩文件,需要手动进行解压,将它们放在同一目录中,C-API不会直接加载 V2 API 存储的压缩文件。 - 1. 如果使用`merge model`方式将神经网络结构和训练好的参数序列化到一个文件,请参考此[示例](https://github.com/PaddlePaddle/Mobile/blob/develop/Demo/linux/paddle_image_recognizer.cpp#L59)。 - 1. 加载模型有多种方式,也可以在程序运行过程中再加载另外一个模型。 - -#### step 3. 创建神经网络输入,组织输入数据 - -基本使用概念: -- 在PaddlePaddle内部,神经网络中一个计算层的输入输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输出,每一个输入/输出都会对应有自己的`Argument`。 -- `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 -- 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 - -*注:本文档使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。* - -这篇文档的之后部分会使用`argument`来**特指** PaddlePaddle C-API中神经网络的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 - -于是,在组织神经网络输入,获取输出时,需要思考完成以下工作: -1. 为每一个输入/输出创建`argument`; -1. 为每一个`argument`创建`paddle_matrix`来存储数据; - -与输入不同的是,输出`argument`的`paddle_matrix`变量并不需在使用C-API时为之分配存储空间。PaddlePaddle内部,神经网络进行前向计算时会自己分配/管理每个计算层的存储空间;这些细节C-API会代为处理,只需在概念上理解,并按照约定调用相关的 C-API 接口即可。 - -#### step 4. 前向计算 - -完成上述准备之后,通过调用 `paddle_gradient_machine_forward` 接口完成神经网络的前向计算。 - -#### step 5. 清理 - -结束预测之后,对使用的中间变量和资源进行清理和释放。 diff --git a/doc/howto/usage/capi/workflow_of_capi_cn.md b/doc/howto/usage/capi/workflow_of_capi_cn.md new file mode 100644 index 00000000000..c1c2c86d0c5 --- /dev/null +++ b/doc/howto/usage/capi/workflow_of_capi_cn.md @@ -0,0 +1,120 @@ +## C-API 使用流程 + +这篇文档介绍 PaddlePaddle C-API 整体使用流程。 + +### 使用流程 + +使用 C-API 的工作流程如图1所示,分为(1)准备预测模型和(2)预测程序开发两大部分。 + +

+
图1. C-API使用流程示意图 +

+ +- 准备预测模型 + 1. 只将神经网络结构进行序列化。 + - 只对神经网络结构进行序列化,加载模型需同时指定:网络结构的序列化结果和模型参数存储目录。 + 1. 将网络结构定义和训练结束存储下来的模型参数文件(多个)合并入一个文件。 + - 神经网络模型结构和训练好的模型将被序列化合并入一个文件。 + - 预测时只需加载一个文件便于发布。 + - **注意**:以上两种方式只需选择其一即可。 +- 调用 C-API 开发预测序 + 1. 初始化PaddlePaddle运行环境。 + 1. 加载预测模型。 + 1. 创建神经网络输入,组织输入数据。 + 1. 进行前向计算,获得计算结果。 + 1. 清理和结束。 + +### 准备预测模型 + +在准备预测模型部分的介绍,我们以手写数字识别任务为例。手写数字识别任务定义了一个含有[两个隐层的简单全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。完整代码可以查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 中的相关脚本。 + +调用C-API开发预测程序需要一个训练好的模型,在终端执行`python mnist_v2.py` +运行[目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 会使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。训练好的模型默认保存在当前运行目录下的`models`目录中。 + +下面,我们将训练结束后存储下来的模型转换成预测模型。 + +1. 序列化神经网络模型配置 + + PaddlePaddle 使用 protobuf 来传输网络配置文件中定义的网络结构和相关参数,使用 C-API 进行预测时,需要将网络结构使用 protobuf 进行序列化,写入文件中。 + + 调用[`paddle.utils.dump_v2_config`](https://github.com/PaddlePaddle/Paddle/tree/develop/python/paddle/utils/dump_v2_config.py)中的`dump_v2_config`函数能够将使用 PaddlePaddle V2 API 定义的神经网络结构 dump 到指定文件中,示例代码如下: + + ```python + from paddle.utils.dump_v2_config import dump_v2_config + from mnist_v2 import network + + predict = network(is_infer=True) + dump_v2_config(predict, "trainer_config.bin", True) + ``` + + 对[手写数字识别](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)这个示例,[`mnist_v2.py`](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense/mnist_v2.py)脚本集成了序列化神经网络结构的过程,可以直接运行 `python mnist_v2.py --task dump_config` 对神经网络结构进行序列化,结果会写入当前运行目录下的`trainer_config.bin`文件中。 + + 使用这种方式,需要**在运行时将神经网络的多个可学习参数放在同一个目录中**,C-API可以通过分别指定序列化后的网络结构文件和参数目录来加载训练好的模型。 + +2. 合并模型文件(可选) + + 一些情况为了便于发布,希望能够将序列化后的神经网络结构和训练好的模型参数打包进一个文件。对于这样的需求,可以使用`paddle.utils.merge_model`中的`merge_v2_model`接口对神经网络结构和训练好的参数进行序列化,将序列化结果写入一个文件内。 + + 代码示例如下: + + ```python + from paddle.utils.merge_model import merge_v2_modelss + from mnist_v2 import network + + net = network(is_infer=True) + param_file = "models/params_pass_4.tar" + output_file = "output.paddle.model" + merge_v2_model(net, param_file, output_file) + ``` + 对[手写数字识别](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)这个示例,可直接运行 `python` [merge_v2_model.py](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense/merge_v2_model.py)。序列化结果会写入当前运行目录下的`output.paddle.model`文件中。使用这种方式,运行时C-API可以通过指定`output.paddle.model`文件的路径来加载预测模型。 + +#### 注意事项 +1. 为使用C-API,在调用`dump_v2_config`序列化神经网络结构时,参数`binary`必须指定为`True`。 +1. **预测使用的网络结构往往不同于训练**,通常需要去掉网络中的:(1)类别标签层;(2)损失函数层;(3)`evaluator`等,只留下核心计算层,请注意是否需要修改网络结构。 +1. 预测时,可以获取网络中定义的任意多个(大于等于一个)层前向计算的结果,需要哪些层的计算结果作为输出,就将这些层加入一个Python list中,作为调用`dump_v2_config`的第一个参数。 + +### 编写预测代码 + +预测代码更多详细示例代码请参考[C-API使用示例](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference) 目录下的代码示例。这一节对图1中预测代码编写的5个步骤进行介绍和说明。 + +#### step 1. 初始化PaddlePaddle运行环境 +第一步需调用[`paddle_init`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/main.h#L27) 初始化PaddlePaddle运行环境,该接口接受两个参数:参数的个数和参数列表。 + +#### step2. 加载模型 + +这里介绍C-API使用中的一个重要概念:Gradient Machine。 + +概念上,在 PaddlePaddle 内部,一个GradientMachine类的对象管理着一组计算层(PaddlePaddle Layers)来完成前向和反向计算,并处理与之相关的所有细节。在调用C-API预测时,只需进行前向计算而无需调用反向计算。这篇文档之后部分会使用`gradient machine`来特指调用PaddlePaddle C-API创建的GradientMachine类的对象。每一个 `gradient machine` 都会管理维护一份训练好的模型,下面是C-API提供的,两种常用的模型加载方式: + +1. 调用[`paddle_gradient_machine_load_parameter_from_disk`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/gradient_machine.h#L61)接口,从磁盘加载预测模型。这时`gradient machine`会独立拥有一份训练好的模型; +1. 调用[`paddle_gradient_machine_create_shared_param`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/gradient_machine.h#L88)接口,与其它`gradient machine`的共享已经加载的预测模型。这种情况多出现在使用多线程预测时,通过多个线程共享同一个模型来减少内存开销。可参考[此示例](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/multi_thread/main.c)。 + +- 注意事项 + 1. 使用PaddlePaddle V2 API训练,模型中所有可学习参数会被存为一个压缩文件,需要手动进行解压,将它们放在同一目录中,C-API不会直接加载 V2 API 存储的压缩文件。 + 1. 如果使用`merge model`方式将神经网络结构和训练好的参数序列化到一个文件,请参考此[示例](https://github.com/PaddlePaddle/Mobile/blob/develop/Demo/linux/paddle_image_recognizer.cpp#L59)。 + 1. 通过灵活使用以上两个接口,加载模型可其它多种方式,例如也可在程序运行过程中再加载另外一个模型。 + +#### step 3. 创建神经网络输入,组织输入数据 + +基本使用概念: +- 在PaddlePaddle内部,神经网络中一个计算层的输入输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输出,每一个输入/输出都会对应有自己的`Argument`。 +- `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 +- 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 + +C-API支持的所有输入数据类型和他们的组织方式,请参考“输入/输出数据组织”一节。 + +这篇文档的之后部分会使用`argument`来特指PaddlePaddle C-API中神经网络的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 + +在组织神经网络输入,获取输出时,需要思考完成以下工作: +1. 为每一个输入/输出创建`argument`; +1. 为每一个`argument`创建`paddle_matrix`来存储数据; + +与输入不同的是,不需在使用C-API时为输出`argument`的`paddle_matrix`对象分配空间。前向计算之后PaddlePaddle内部已经分配/管理了每个计算层输出的存储空间。 + +#### step 4. 前向计算 + +完成上述准备之后,通过调用 `[paddle_gradient_machine_forward](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/gradient_machine.h#L73)` 接口完成神经网络的前向计算。 + +#### step 5. 清理 + +结束预测之后,对使用的中间变量和资源进行清理和释放。 -- GitLab From 3b0afae3d132df16b82ffde72631289c36ef3a36 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Mon, 8 Jan 2018 14:21:45 +0800 Subject: [PATCH 0124/2305] Add more comments --- paddle/framework/details/cow_ptr.h | 34 +++++++++++++----------- paddle/framework/details/cow_ptr_test.cc | 4 --- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/paddle/framework/details/cow_ptr.h b/paddle/framework/details/cow_ptr.h index 6f1dcab40b5..7e308ffb5a4 100644 --- a/paddle/framework/details/cow_ptr.h +++ b/paddle/framework/details/cow_ptr.h @@ -25,13 +25,14 @@ class ThreadUnsafeOwnershipFlags { public: ThreadUnsafeOwnershipFlags(bool flag) : flag_(flag) {} - ThreadUnsafeOwnershipFlags(const ThreadUnsafeOwnershipFlags& o) = delete; - ThreadUnsafeOwnershipFlags& operator=(const ThreadUnsafeOwnershipFlags& o) = - delete; - ThreadUnsafeOwnershipFlags(ThreadUnsafeOwnershipFlags&& o) = default; + ThreadUnsafeOwnershipFlags(const ThreadUnsafeOwnershipFlags& other) = delete; + ThreadUnsafeOwnershipFlags& operator=( + const ThreadUnsafeOwnershipFlags& other) = delete; + ThreadUnsafeOwnershipFlags(ThreadUnsafeOwnershipFlags&& other) = default; void SetOwnership(bool flag) { flag_ = flag; } + // Invoke the callback if it is not owned. template void AcquireOwnershipOnce(Callback acquire) { if (!flag_) { @@ -44,7 +45,7 @@ class ThreadUnsafeOwnershipFlags { bool flag_; }; -// Copy On Write pointer. +// Copy-On-Write pointer. // It will hold a T* pointer, and only copy once when `MutableData` is invoked. // // The template parameter OwnershipFlags should have: @@ -52,6 +53,8 @@ class ThreadUnsafeOwnershipFlags { // * SetOwnership(bool flag). // * AcquireOwnershipOnce(Callback). It will invoke the callback if it is not // owned. +// +// https://en.wikipedia.org/wiki/Copy-on-write template class COWPtr { public: @@ -59,33 +62,34 @@ class COWPtr { explicit COWPtr(T* ptr) : payload_(ptr), ownership_{true} {} // Move methods. Steal ownership from origin - COWPtr(COWPtr&& o) - : payload_(o.payload_), ownership_{std::move(o.ownership_)} {} + COWPtr(COWPtr&& other) + : payload_(other.payload_), ownership_{std::move(other.ownership_)} {} COWPtr& operator=(COWPtr&& origin) = default; // Copy methods. Not own payload - COWPtr(const COWPtr& o) : payload_(o.payload_), ownership_{false} {} - COWPtr& operator=(const COWPtr& o) { - payload_ = o.payload_; + COWPtr(const COWPtr& other) : payload_(other.payload_), ownership_{false} {} + COWPtr& operator=(const COWPtr& other) { + payload_ = other.payload_; ownership_.SetOwnership(false); return *this; } + // Access read only data. const T& Data() const { return *payload_; } + // Access mutable data. If the data is not owned, the data will be copied + // before. T* MutableData() { ownership_.AcquireOwnershipOnce( [this] { payload_.reset(new T(*payload_)); }); return payload_.get(); } - void Reset() { - ownership_.AcquireOwnershipOnce([this] { payload_.reset(); }); - payload_.reset(new T()); - } - private: + // Actual data pointer. std::shared_ptr payload_; + + // Ownership flag. OwnershipFlags ownership_; }; diff --git a/paddle/framework/details/cow_ptr_test.cc b/paddle/framework/details/cow_ptr_test.cc index 080a0a0a448..936954a2333 100644 --- a/paddle/framework/details/cow_ptr_test.cc +++ b/paddle/framework/details/cow_ptr_test.cc @@ -28,10 +28,6 @@ TEST(COWPtr, all) { *ptr2.MutableData() = 10; ASSERT_EQ(ptr.Data(), 0); ASSERT_EQ(ptr2.Data(), 10); - - auto ptr_before = ptr2.MutableData(); - ptr2.Reset(); - ASSERT_NE(ptr2.MutableData(), ptr_before); } } // namespace details -- GitLab From 343b32a0d143f5a3bffa43e418dfecf274ffec58 Mon Sep 17 00:00:00 2001 From: gx_wind Date: Mon, 8 Jan 2018 14:51:54 +0800 Subject: [PATCH 0125/2305] fix coding standard --- adversarial/fluid_mnist.py | 14 ++++---- adversarial/mnist_tutorial_fgsm.py | 53 +++++++++++++----------------- 2 files changed, 30 insertions(+), 37 deletions(-) diff --git a/adversarial/fluid_mnist.py b/adversarial/fluid_mnist.py index d46defda55e..031928e994c 100644 --- a/adversarial/fluid_mnist.py +++ b/adversarial/fluid_mnist.py @@ -4,6 +4,7 @@ CNN on mnist data using fluid api of paddlepaddle import paddle.v2 as paddle import paddle.v2.fluid as fluid + def mnist_cnn_model(img): """ Mnist cnn model @@ -31,10 +32,7 @@ def mnist_cnn_model(img): pool_stride=2, act='relu') - logits = fluid.layers.fc( - input=conv_pool_2, - size=10, - act='softmax') + logits = fluid.layers.fc(input=conv_pool_2, size=10, act='softmax') return logits @@ -73,17 +71,19 @@ def main(): feed=feeder.feed(data), fetch_list=[avg_cost] + accuracy.metrics) pass_acc = accuracy.eval(exe) - print("pass_id=" + str(pass_id) + " acc=" + str(acc) + " pass_acc=" + - str(pass_acc)) + print("pass_id=" + str(pass_id) + " acc=" + str(acc) + " pass_acc=" + + str(pass_acc)) # print loss, acc if loss < LOSS_THRESHOLD and pass_acc > ACC_THRESHOLD: # if avg cost less than 10.0 and accuracy is larger than 0.9, we think our code is good. break + # exit(0) pass_acc = accuracy.eval(exe) print("pass_id=" + str(pass_id) + " pass_acc=" + str(pass_acc)) - fluid.io.save_params(exe, dirname='./mnist', main_program=fluid.default_main_program()) + fluid.io.save_params( + exe, dirname='./mnist', main_program=fluid.default_main_program()) print('train mnist done') exit(1) diff --git a/adversarial/mnist_tutorial_fgsm.py b/adversarial/mnist_tutorial_fgsm.py index 665062afd0e..8b29346b8cd 100644 --- a/adversarial/mnist_tutorial_fgsm.py +++ b/adversarial/mnist_tutorial_fgsm.py @@ -9,6 +9,7 @@ import numpy as np from advbox.models.paddle import PaddleModel from advbox.attacks.gradientsign import GradientSignAttack + def cnn_model(img): """ Mnist cnn model @@ -19,25 +20,22 @@ def cnn_model(img): """ #conv1 = fluid.nets.conv2d() conv_pool_1 = fluid.nets.simple_img_conv_pool( - input=img, - num_filters=20, - filter_size=5, - pool_size=2, - pool_stride=2, - act='relu') + input=img, + num_filters=20, + filter_size=5, + pool_size=2, + pool_stride=2, + act='relu') conv_pool_2 = fluid.nets.simple_img_conv_pool( - input=conv_pool_1, - num_filters=50, - filter_size=5, - pool_size=2, - pool_stride=2, - act='relu') + input=conv_pool_1, + num_filters=50, + filter_size=5, + pool_size=2, + pool_stride=2, + act='relu') - logits = fluid.layers.fc( - input=conv_pool_2, - size=10, - act='softmax') + logits = fluid.layers.fc(input=conv_pool_2, size=10, act='softmax') return logits @@ -65,22 +63,16 @@ def main(): paddle.dataset.mnist.train(), buf_size=500), batch_size=BATCH_SIZE) feeder = fluid.DataFeeder( - feed_list=[IMG_NAME, LABEL_NAME], - place=place, - program=fluid.default_main_program() - ) + feed_list=[IMG_NAME, LABEL_NAME], + place=place, + program=fluid.default_main_program()) - fluid.io.load_params(exe, "./mnist/", main_program=fluid.default_main_program()) + fluid.io.load_params( + exe, "./mnist/", main_program=fluid.default_main_program()) # advbox demo - m = PaddleModel( - fluid.default_main_program(), - IMG_NAME, - LABEL_NAME, - logits.name, - avg_cost.name, - (-1, 1) - ) + m = PaddleModel(fluid.default_main_program(), IMG_NAME, LABEL_NAME, + logits.name, avg_cost.name, (-1, 1)) att = GradientSignAttack(m) for data in train_reader(): # fgsm attack @@ -89,6 +81,7 @@ def main(): plt.show() #np.save('adv_img', adv_img) break - + + if __name__ == '__main__': main() -- GitLab From 24920c32eb8b84efbf778e6ae29a5c99cdb36900 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Mon, 8 Jan 2018 14:50:09 +0800 Subject: [PATCH 0126/2305] update openblas benchmark when export OPENBLAS_MAIN_FREE=1 --- benchmark/IntelOptimizedPaddle.md | 20 ++++++++++---------- benchmark/paddle/image/run_openblas_infer.sh | 1 + 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/benchmark/IntelOptimizedPaddle.md b/benchmark/IntelOptimizedPaddle.md index 084d3237d9c..c5cb6430bc4 100644 --- a/benchmark/IntelOptimizedPaddle.md +++ b/benchmark/IntelOptimizedPaddle.md @@ -7,11 +7,11 @@ Machine: System: CentOS release 6.3 (Final), Docker 1.12.1. -PaddlePaddle: (TODO: will rerun after 0.11.0) -- paddlepaddle/paddle:latest (for MKLML and MKL-DNN) +PaddlePaddle: +- paddlepaddle/paddle:0.11.0 (for MKLML and MKL-DNN) - MKL-DNN tag v0.11 - MKLML 2018.0.1.20171007 -- paddlepaddle/paddle:latest-openblas (for OpenBLAS) +- paddlepaddle/paddle:0.11.0-openblas (for OpenBLAS) - OpenBLAS v0.2.20 On each machine, we will test and compare the performance of training on single node using MKL-DNN / MKLML / OpenBLAS respectively. @@ -56,11 +56,11 @@ Input image size - 3 * 224 * 224, Time: images/second -- Alexnet +- AlexNet | BatchSize | 64 | 128 | 256 | |--------------|--------| ------ | -------| -| OpenBLAS | 2.13 | 2.45 | 2.68 | +| OpenBLAS | 45.62 | 72.79 | 107.22 | | MKLML | 66.37 | 105.60 | 144.04 | | MKL-DNN | 399.00 | 498.94 | 626.53 | @@ -72,7 +72,7 @@ Test on batch size 1, 2, 4, 8, 16 on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz | BatchSize | 1 | 2 | 4 | 8 | 16 | |-----------|-------|-------|-------|-------|-------| -| OpenBLAS | 1.07 | 1.08 | 1.06 | 0.88 | 0.65 | +| OpenBLAS | 1.10 | 1.96 | 3.62 | 3.63 | 2.25 | | MKLML | 5.58 | 9.80 | 15.15 | 21.21 | 28.67 | | MKL-DNN | 75.07 | 88.64 | 82.58 | 92.29 | 96.75 | @@ -80,7 +80,7 @@ Test on batch size 1, 2, 4, 8, 16 on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz | BatchSize | 1 | 2 | 4 | 8 | 16 | |-----------|-------|--------|--------|--------|--------| -| OpenBLAS | 3.35 | 3.19 | 3.09 | 2.55 | 1.96 | +| OpenBLAS | 3.31 | 6.72 | 11.59 | 13.17 | 9.27 | | MKLML | 6.33 | 12.02 | 22.88 | 40.53 | 63.09 | | MKL-DNN | 107.83| 148.84 | 177.78 | 189.35 | 217.69 | @@ -89,15 +89,15 @@ Test on batch size 1, 2, 4, 8, 16 on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz | BatchSize | 1 | 2 | 4 | 8 | 16 | |-----------|--------|--------|--------|--------|--------| -| OpenBLAS | 12.04 | 11.31 | 10.00 | 9.07 | 4.34 | +| OpenBLAS | 12.06 | 23.56 | 34.48 | 36.45 | 23.12 | | MKLML | 22.74 | 41.56 | 81.22 | 133.47 | 210.53 | | MKL-DNN | 175.10 | 272.92 | 450.70 | 512.00 | 600.94 | -- Alexnet +- AlexNet | BatchSize | 1 | 2 | 4 | 8 | 16 | |-----------|--------|--------|--------|--------|--------| -| OpenBLAS | | | | | | +| OpenBLAS | 3.53 | 6.23 | 15.04 | 26.06 | 31.62 | | MKLML | 21.32 | 36.55 | 73.06 | 131.15 | 192.77 | | MKL-DNN | 442.91 | 656.41 | 719.10 | 847.68 | 850.51 | diff --git a/benchmark/paddle/image/run_openblas_infer.sh b/benchmark/paddle/image/run_openblas_infer.sh index 71a49231a55..a9a7b8a6671 100755 --- a/benchmark/paddle/image/run_openblas_infer.sh +++ b/benchmark/paddle/image/run_openblas_infer.sh @@ -8,6 +8,7 @@ function clock_to_seconds() { } function infer() { + export OPENBLAS_MAIN_FREE=1 topology=$1 layer_num=$2 bs=$3 -- GitLab From 01ee42b121b8226cae28630356a7dee5d30fbaf0 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Mon, 8 Jan 2018 15:11:50 +0800 Subject: [PATCH 0127/2305] fix compile error in profiler.cc --- paddle/platform/profiler.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index 4283724d28a..7e2e2d968ef 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -183,35 +183,35 @@ void ParseEvents(std::vector>& events, if (g_profiler_place == "") return; std::string sorted_domain; - std::function sorted_func; + std::function sorted_func; switch (sorted_by) { case EventSortingKey::kCalls: sorted_domain = "number of calls"; - sorted_func = [](EventItem& a, EventItem& b) { + sorted_func = [](const EventItem& a, const EventItem& b) { return a.calls > b.calls; }; break; case EventSortingKey::kTotal: sorted_domain = "total time"; - sorted_func = [](EventItem& a, EventItem& b) { + sorted_func = [](const EventItem& a, const EventItem& b) { return a.total_time > b.total_time; }; break; case EventSortingKey::kMin: sorted_domain = "minimum time"; - sorted_func = [](EventItem& a, EventItem& b) { + sorted_func = [](const EventItem& a, const EventItem& b) { return a.min_time > b.min_time; }; break; case EventSortingKey::kMax: sorted_domain = "maximum time"; - sorted_func = [](EventItem& a, EventItem& b) { + sorted_func = [](const EventItem& a, const EventItem& b) { return a.max_time > b.max_time; }; break; case EventSortingKey::kAve: sorted_domain = "average time"; - sorted_func = [](EventItem& a, EventItem& b) { + sorted_func = [](const EventItem& a, const EventItem& b) { return a.ave_time > b.ave_time; }; break; -- GitLab From 12ed53c1f29775ec9c957f8f15f1b92ff11d0031 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Mon, 8 Jan 2018 15:54:12 +0800 Subject: [PATCH 0128/2305] Inherit LoD from x to x_grad and enhance the unit test. --- paddle/operators/shrink_rnn_memory_op.cc | 1 + .../v2/fluid/tests/test_shrink_rnn_memory.py | 95 +++++++++++++------ 2 files changed, 69 insertions(+), 27 deletions(-) diff --git a/paddle/operators/shrink_rnn_memory_op.cc b/paddle/operators/shrink_rnn_memory_op.cc index b958e6c5959..47948adde32 100644 --- a/paddle/operators/shrink_rnn_memory_op.cc +++ b/paddle/operators/shrink_rnn_memory_op.cc @@ -146,6 +146,7 @@ class ShrinkRNNMemoryGradInferShape : public framework::InferShapeBase { PADDLE_ENFORCE(context->HasOutput(framework::GradVarName("X"))); context->SetOutputDim(framework::GradVarName("X"), context->GetInputDim("X")); + context->ShareLoD("X", framework::GradVarName("X")); } }; diff --git a/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py b/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py index 9d8565b1681..a14721b9aac 100644 --- a/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py +++ b/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py @@ -3,45 +3,86 @@ import paddle.v2.fluid.core as core from paddle.v2.fluid.executor import Executor import paddle.v2.fluid.layers as layers from paddle.v2.fluid.backward import append_backward -from paddle.v2.fluid.framework import default_main_program -import numpy +from paddle.v2.fluid.framework import default_main_program, switch_main_program +from paddle.v2.fluid.framework import Program +import numpy as np -main_program = default_main_program() - -class TestShrinkRNNMemory(unittest.TestCase): - def test_shrink_rnn_memory(self): +class TestShrinkRNNMemoryBase(unittest.TestCase): + def setUp(self): + self.main_program = Program() + switch_main_program(self.main_program) x = layers.data('x', shape=[100], dtype='float32') x.stop_gradient = False - table = layers.lod_rank_table(x=x) + rank_table_tensor = layers.data( + 'rank_table_tensor', shape=[1], dtype='float32', lod_level=1) + table = layers.lod_rank_table(x=rank_table_tensor) i = layers.zeros(dtype='int64', shape=[1]) - mem1 = layers.shrink_memory(x=x, i=i, table=table) + self.mem1 = layers.shrink_memory(x=x, i=i, table=table) i = layers.increment(x=i) i.stop_gradient = True - mem2 = layers.shrink_memory(x=mem1, i=i, table=table) + self.mem2 = layers.shrink_memory(x=self.mem1, i=i, table=table) i = layers.increment(x=i) i.stop_gradient = True - mem3 = layers.shrink_memory(x=mem2, i=i, table=table) + self.mem3 = layers.shrink_memory(x=self.mem2, i=i, table=table) + mem3_mean = layers.mean(x=self.mem3) + append_backward(loss=mem3_mean) + self.x_grad = self.main_program.global_block().var('x@GRAD') + + def sum_lodtensor(self, tensor): + sum_res = 0.0 + for i in xrange(np.product(tensor.get_dims())): + sum_res += tensor.get_float_element(i) + return sum_res + +class TestShrinkRNNMemoryReferLoD(TestShrinkRNNMemoryBase): + def test_refer_lod(self): cpu = core.CPUPlace() - tensor = core.LoDTensor() - tensor.set_lod([[0, 2, 5, 6]]) - tensor_np = numpy.random.random(size=(6, 100)).astype('float32') - tensor.set(tensor_np, cpu) + x_tensor = core.LoDTensor() + x_tensor.set_lod([[0, 2, 5, 6]]) + tensor_np = np.random.random(size=(6, 100)).astype('float32') + x_tensor.set(tensor_np, cpu) + + rank_table_tensor = core.LoDTensor() + rank_table_tensor.set_lod([[0, 1, 3, 6]]) + rank_table_tensor.set(np.random.random(size=(6, 1)).astype('float32'), + cpu) + exe = Executor(cpu) - outs = exe.run(feed={'x': tensor}, - fetch_list=[mem1, mem2, mem3], - return_numpy=False) - self.assertTrue(numpy.allclose(tensor_np[0:6], outs[0])) - self.assertTrue(numpy.allclose(tensor_np[0:5], outs[1])) - self.assertTrue(numpy.allclose(tensor_np[0:2], outs[2])) - - mem3_mean = layers.mean(x=mem3) - append_backward(loss=mem3_mean) - x_grad = exe.run( - feed={'x': tensor}, - fetch_list=[main_program.global_block().var('x@GRAD')])[0] - self.assertAlmostEqual(1.0, x_grad.sum(), delta=0.1) + outs = exe.run( + feed={'x': x_tensor, + 'rank_table_tensor': rank_table_tensor}, + fetch_list=[self.mem1, self.mem2, self.mem3, self.x_grad], + return_numpy=False) + self.assertTrue(np.allclose(tensor_np[0:6], outs[0])) + self.assertTrue(np.allclose(tensor_np[0:5], outs[1])) + self.assertTrue(np.allclose(tensor_np[0:2], outs[2])) + self.assertAlmostEqual(1.0, self.sum_lodtensor(outs[3]), delta=0.01) + + +class TestShrinkRNNMemoryNoLoD(TestShrinkRNNMemoryBase): + def test_no_lod(self): + cpu = core.CPUPlace() + x_tensor = core.LoDTensor() + tensor_np = np.random.random(size=(3, 100)).astype('float32') + x_tensor.set(tensor_np, cpu) + + rank_table_tensor = core.LoDTensor() + rank_table_tensor.set_lod([[0, 1, 3, 6]]) + rank_table_tensor.set(np.random.random(size=(6, 1)).astype('float32'), + cpu) + + exe = Executor(cpu) + outs = exe.run( + feed={'x': x_tensor, + 'rank_table_tensor': rank_table_tensor}, + fetch_list=[self.mem1, self.mem2, self.mem3, self.x_grad], + return_numpy=False) + self.assertTrue(np.allclose(tensor_np[0:3], outs[0])) + self.assertTrue(np.allclose(tensor_np[0:2], outs[1])) + self.assertTrue(np.allclose(tensor_np[0:1], outs[2])) + self.assertAlmostEqual(1.0, self.sum_lodtensor(outs[3]), delta=0.01) if __name__ == '__main__': -- GitLab From ca90356b0e81133a06816d1348208b9aba87302b Mon Sep 17 00:00:00 2001 From: qiaolongfei Date: Mon, 8 Jan 2018 15:56:26 +0800 Subject: [PATCH 0129/2305] add back priority --- paddle/framework/op_registry_test.cc | 6 +++--- paddle/framework/operator.cc | 15 ++++++++++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/paddle/framework/op_registry_test.cc b/paddle/framework/op_registry_test.cc index f7a10ada809..66f07b6757f 100644 --- a/paddle/framework/op_registry_test.cc +++ b/paddle/framework/op_registry_test.cc @@ -376,16 +376,16 @@ TEST(OperatorRegistrar, OpWithMultiKernel) { paddle::framework::UseCPU(); op->Run(scope, cpu_place); - EXPECT_EQ(op_test_value, -20); + EXPECT_EQ(op_test_value, -9); // add cuda kernels paddle::framework::UseCUDA(); op->Run(scope, cuda_place); - EXPECT_EQ(op_test_value, -30); + EXPECT_EQ(op_test_value, -10); // use cudnn kernel paddle::framework::UseCUDNN(); op->Run(scope, cuda_place); - EXPECT_EQ(op_test_value, -40); + EXPECT_EQ(op_test_value, -20); } diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index adc85b1049f..3744eae6968 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -474,6 +474,20 @@ void OperatorWithKernel::Run(const Scope& scope, ExecutionContext ctx(*this, scope, *dev_ctx); auto expected_kernel_key = this->GetExpectedKernelType(ctx); + OpKernelMap& kernels = kernels_iter->second; + + for (auto& candidate : kKernelPriority) { + auto candidate_key = + OpKernelType(expected_kernel_key.data_type_, std::get<0>(candidate), + expected_kernel_key.data_layout_, std::get<1>(candidate)); + + if ((candidate_key == expected_kernel_key) || + (kernels.count(candidate_key))) { + expected_kernel_key = candidate_key; + break; + } + } + Scope& new_scope = scope.NewScope(); for (auto& var_name_item : this->Inputs()) { @@ -504,7 +518,6 @@ void OperatorWithKernel::Run(const Scope& scope, } } - OpKernelMap& kernels = kernels_iter->second; auto kernel_iter = kernels.find(expected_kernel_key); kernel_iter->second->Compute(ExecutionContext(*this, new_scope, *dev_ctx)); -- GitLab From 02275240dcfea1648381230cd69b9f3c2a4aece0 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Mon, 8 Jan 2018 15:33:54 +0800 Subject: [PATCH 0130/2305] add benchmark graph for IntelOptimizedPaddle --- benchmark/IntelOptimizedPaddle.md | 9 +++++++-- benchmark/figs/alexnet-cpu-infer.png | Bin 0 -> 15428 bytes benchmark/figs/alexnet-cpu-train.png | Bin 0 -> 16013 bytes benchmark/figs/googlenet-cpu-infer.png | Bin 0 -> 14391 bytes benchmark/figs/googlenet-cpu-train.png | Bin 18254 -> 19250 bytes benchmark/figs/resnet-cpu-infer.png | Bin 0 -> 14004 bytes benchmark/figs/resnet-cpu-train.png | Bin 20243 -> 17991 bytes benchmark/figs/vgg-cpu-infer.png | Bin 0 -> 13985 bytes benchmark/figs/vgg-cpu-train.png | Bin 18336 -> 17134 bytes 9 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 benchmark/figs/alexnet-cpu-infer.png create mode 100644 benchmark/figs/alexnet-cpu-train.png create mode 100644 benchmark/figs/googlenet-cpu-infer.png create mode 100644 benchmark/figs/resnet-cpu-infer.png create mode 100644 benchmark/figs/vgg-cpu-infer.png diff --git a/benchmark/IntelOptimizedPaddle.md b/benchmark/IntelOptimizedPaddle.md index c5cb6430bc4..8b7dc5b7db8 100644 --- a/benchmark/IntelOptimizedPaddle.md +++ b/benchmark/IntelOptimizedPaddle.md @@ -64,7 +64,7 @@ Input image size - 3 * 224 * 224, Time: images/second | MKLML | 66.37 | 105.60 | 144.04 | | MKL-DNN | 399.00 | 498.94 | 626.53 | -chart TBD + #### Inference Test on batch size 1, 2, 4, 8, 16 on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz @@ -76,6 +76,8 @@ Test on batch size 1, 2, 4, 8, 16 on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz | MKLML | 5.58 | 9.80 | 15.15 | 21.21 | 28.67 | | MKL-DNN | 75.07 | 88.64 | 82.58 | 92.29 | 96.75 | + + - ResNet-50 | BatchSize | 1 | 2 | 4 | 8 | 16 | @@ -84,6 +86,7 @@ Test on batch size 1, 2, 4, 8, 16 on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz | MKLML | 6.33 | 12.02 | 22.88 | 40.53 | 63.09 | | MKL-DNN | 107.83| 148.84 | 177.78 | 189.35 | 217.69 | + - GoogLeNet @@ -93,6 +96,8 @@ Test on batch size 1, 2, 4, 8, 16 on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz | MKLML | 22.74 | 41.56 | 81.22 | 133.47 | 210.53 | | MKL-DNN | 175.10 | 272.92 | 450.70 | 512.00 | 600.94 | + + - AlexNet | BatchSize | 1 | 2 | 4 | 8 | 16 | @@ -101,7 +106,7 @@ Test on batch size 1, 2, 4, 8, 16 on Intel(R) Xeon(R) Gold 6148 CPU @ 2.40GHz | MKLML | 21.32 | 36.55 | 73.06 | 131.15 | 192.77 | | MKL-DNN | 442.91 | 656.41 | 719.10 | 847.68 | 850.51 | -chart TBD + ### Laptop TBD diff --git a/benchmark/figs/alexnet-cpu-infer.png b/benchmark/figs/alexnet-cpu-infer.png new file mode 100644 index 0000000000000000000000000000000000000000..6215ae4e4288f969a909c258ddd5b5f51e6abb3f GIT binary patch literal 15428 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 scQ!ljL;5(7c*=#Vq*$0OxPlb^rhX literal 0 HcmV?d00001 diff --git a/benchmark/figs/alexnet-cpu-train.png b/benchmark/figs/alexnet-cpu-train.png new file mode 100644 index 0000000000000000000000000000000000000000..b3200bbc049a9d75857fb5692902d7b475aa8f68 GIT binary patch literal 16013 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 literal 0 HcmV?d00001 diff --git a/benchmark/figs/googlenet-cpu-infer.png b/benchmark/figs/googlenet-cpu-infer.png new file mode 100644 index 0000000000000000000000000000000000000000..19478d433bae651f4506153ded11a96d5137b409 GIT binary patch literal 14391 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 lnuqV?{%77{~KUcujl{( diff --git a/benchmark/figs/resnet-cpu-infer.png b/benchmark/figs/resnet-cpu-infer.png new file mode 100644 index 0000000000000000000000000000000000000000..bc43d4b8d20c600d6f1046a5986a6c62adfa6b44 GIT binary patch literal 14004 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�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 V;vVS}|0nB^;vF^F!rLZK{trLdlsW(a literal 0 HcmV?d00001 diff --git a/benchmark/figs/resnet-cpu-train.png b/benchmark/figs/resnet-cpu-train.png index b96ecd5ff940c0d000613b1ed1f11fb16796cf47..96746b1759fa17d25ac5f40ed3678e16086364ba 100644 GIT binary patch literal 17991 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 VTumpypC`ePdLsY0NL1hN{{R#w^I!k~ literal 20243 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 lkO1}p1*qU{{u{0P1OJZ diff --git a/benchmark/figs/vgg-cpu-infer.png b/benchmark/figs/vgg-cpu-infer.png new file mode 100644 index 0000000000000000000000000000000000000000..3a51ec6c474f0e0f0c4384c8ccd1e08c4382230b GIT binary patch literal 13985 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 IFWwINKRB8BQ2+n{ literal 18336 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 Date: Mon, 8 Jan 2018 17:06:57 +0800 Subject: [PATCH 0131/2305] init --- python/paddle/trainer_config_helpers/layers.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index 19e2ab1b7da..df4a630077b 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -2542,14 +2542,14 @@ def img_conv_layer(input, what-are-deconvolutional-layers/>`_ . The num_channel means input image's channel number. It may be 1 or 3 when input is raw pixels of image(mono or RGB), or it may be the previous layer's - num_filters * num_group. + num_filters. There are several groups of filters in PaddlePaddle implementation. Each group will process some channels of the input. For example, if num_channel = 256, group = 4, num_filter=32, the PaddlePaddle will create - 32*4 = 128 filters to process the input. The channels will be split into 4 - pieces. First 256/4 = 64 channels will be processed by first 32 filters. The - rest channels will be processed by the rest groups of filters. + 32 filters to process the input. The input channels will be split into 4 + pieces. First 256/4 = 64 channels will be processed by first 32/4 = 8 filters. + The rest channels will be processed by the rest groups of filters. The example usage is: @@ -2575,7 +2575,8 @@ def img_conv_layer(input, :param filter_size_y: The dimension of the filter kernel on the y axis. If the parameter is not set, it will be set automatically according to filter_size. :type filter_size_y: int - :param num_filters: Each filter group's number of filter + :param num_filters: The number of filters. It is as same as the output image channel. + :type num_filters: int :param act: Activation type. ReluActivation is the default activation. :type act: BaseActivation :param groups: The group number. 1 is the default group number. @@ -7177,7 +7178,7 @@ def img_conv3d_layer(input, :param filter_size: The dimensions of the filter kernel along three axises. If the parameter is set to one integer, the three dimensions will be same. :type filter_size: int | tuple | list - :param num_filters: The number of filters in each group. + :param num_filters: The number of filters. It is as same as the output image channel. :type num_filters: int :param act: Activation type. ReluActivation is the default activation. :type act: BaseActivation -- GitLab From 3ff53e062282ee6ec0906c07191b952dafa0a580 Mon Sep 17 00:00:00 2001 From: chenguoyan01 Date: Mon, 8 Jan 2018 17:12:41 +0800 Subject: [PATCH 0132/2305] fix url in cluster_train_cn.md --- doc/howto/usage/cluster/cluster_train_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/howto/usage/cluster/cluster_train_cn.md b/doc/howto/usage/cluster/cluster_train_cn.md index 659bae9c0ce..9328b4cca45 100644 --- a/doc/howto/usage/cluster/cluster_train_cn.md +++ b/doc/howto/usage/cluster/cluster_train_cn.md @@ -60,7 +60,7 @@ $ stdbuf -oL /usr/bin/nohup paddle pserver --port=7164 --ports_num=1 --ports_num $ python train.py ``` -trainer需要和pserver保持网络联通以完成训练。trainer启动需要传入端口、pserver地址等参数使trainer可以正确连接到pserver。这些参数可以通过环境变量(https://zh.wikipedia.org/wiki/环境变量 )或编写程序时`paddle.init()`中传入参数。如果同时使用`paddle.init()`参数和环境变量,将会优先使用`paddle.init()`中传入的参数。 +trainer需要和pserver保持网络联通以完成训练。trainer启动需要传入端口、pserver地址等参数使trainer可以正确连接到pserver。这些参数可以通过[环境变量](https://zh.wikipedia.org/wiki/环境变量)或编写程序时`paddle.init()`中传入参数。如果同时使用`paddle.init()`参数和环境变量,将会优先使用`paddle.init()`中传入的参数。 使用环境变量: -- GitLab From aa75f1e2c5d44230441aa4660c43500edd0753b9 Mon Sep 17 00:00:00 2001 From: Yancey Date: Mon, 8 Jan 2018 17:48:35 +0800 Subject: [PATCH 0133/2305] Create tensor in recv op (#7286) * create tensor in recv op * static global function to global function --- paddle/framework/executor.cc | 2 +- paddle/framework/executor.h | 2 ++ paddle/operators/recv_op.cc | 9 +++++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/paddle/framework/executor.cc b/paddle/framework/executor.cc index c0418c9266e..d8ef9a0fbaa 100644 --- a/paddle/framework/executor.cc +++ b/paddle/framework/executor.cc @@ -35,7 +35,7 @@ const std::string kFetchOpType = "fetch"; Executor::Executor(const platform::Place& place) : place_(place) {} -static void CreateTensor(Variable* var, proto::VarDesc::VarType var_type) { +void CreateTensor(Variable* var, proto::VarDesc::VarType var_type) { if (var_type == proto::VarDesc::LOD_TENSOR) { var->GetMutable(); } else if (var_type == proto::VarDesc::SELECTED_ROWS) { diff --git a/paddle/framework/executor.h b/paddle/framework/executor.h index d869e18901b..0b2b5780fed 100644 --- a/paddle/framework/executor.h +++ b/paddle/framework/executor.h @@ -45,5 +45,7 @@ class Executor { const platform::Place place_; }; +void CreateTensor(Variable* var, proto::VarDesc::VarType var_type); + } // namespace framework } // namespace paddle diff --git a/paddle/operators/recv_op.cc b/paddle/operators/recv_op.cc index 82fceb3da7d..6f65b87d3b0 100644 --- a/paddle/operators/recv_op.cc +++ b/paddle/operators/recv_op.cc @@ -19,7 +19,6 @@ limitations under the License. */ #include -#include "paddle/framework/data_type.h" #include "paddle/framework/executor.h" #include "paddle/framework/framework.pb.h" #include "paddle/framework/lod_tensor.h" @@ -111,9 +110,11 @@ class RecvOp : public framework::OperatorBase { << " updating param: " << param_var_name; auto *merged_grad = recv_scope.FindVar(grad_var_name); if (merged_grad == nullptr) { - // create output of merged var. - auto merged_var = recv_scope.Var(grad_var_name); - merged_var->GetMutable(); + auto *ptr = recv_scope.Var(grad_var_name); + framework::CreateTensor(ptr, + framework::ToVarType(merged_grad->Type())); + VLOG(3) << "Create Variable " << grad_var_name + << " on recv scope, which pointer is " << ptr; } if (trainer_count > 1) { -- GitLab From ae29e8bd12c17caf450c68a763d67351b2e2f30a Mon Sep 17 00:00:00 2001 From: chenguoyan01 Date: Mon, 8 Jan 2018 18:08:46 +0800 Subject: [PATCH 0134/2305] fix ports_num_for_sparse defaut value in cluster_train doc --- doc/howto/usage/cluster/cluster_train_cn.md | 4 ++-- doc/howto/usage/cluster/cluster_train_en.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/howto/usage/cluster/cluster_train_cn.md b/doc/howto/usage/cluster/cluster_train_cn.md index 9328b4cca45..c2fc86687d7 100644 --- a/doc/howto/usage/cluster/cluster_train_cn.md +++ b/doc/howto/usage/cluster/cluster_train_cn.md @@ -51,7 +51,7 @@ $ stdbuf -oL /usr/bin/nohup paddle pserver --port=7164 --ports_num=1 --ports_num - port:**必选,默认7164**,pserver监听的起始端口,根据ports_num决定总端口个数,从起始端口监听多个端口用于通信 - ports_num:**必选,默认1**,监听的端口个数 -- ports_num_for_sparse:**必选,默认1**,用于稀疏类型参数通信的端口个数 +- ports_num_for_sparse:**必选,默认0**,用于稀疏类型参数通信的端口个数 - num_gradient_servers:**必选,默认1**,当前训练任务pserver总数 ### 启动计算节点 @@ -95,7 +95,7 @@ paddle.init( - trainer_count:**必选,默认1**,当前训练任务trainer总个数 - port:**必选,默认7164**,连接到pserver的端口 - ports_num:**必选,默认1**,连接到pserver的端口个数 -- ports_num_for_sparse:**必选,默认1**,和pserver之间用于稀疏类型参数通信的端口个数 +- ports_num_for_sparse:**必选,默认0**,和pserver之间用于稀疏类型参数通信的端口个数 - num_gradient_servers:**必选,默认1**,当前训练任务pserver总数 - trainer_id:**必选,默认0**,每个trainer的唯一ID,从0开始的整数 - pservers:**必选,默认127.0.0.1**,当前训练任务启动的pserver的IP列表,多个IP使用“,”隔开 diff --git a/doc/howto/usage/cluster/cluster_train_en.md b/doc/howto/usage/cluster/cluster_train_en.md index 915405ca5b4..28cd1fa7903 100644 --- a/doc/howto/usage/cluster/cluster_train_en.md +++ b/doc/howto/usage/cluster/cluster_train_en.md @@ -52,7 +52,7 @@ Parameter Description - port: **required, default 7164**, port which parameter server will listen on. If ports_num greater than 1, parameter server will listen on multiple ports for more network throughput. - ports_num: **required, default 1**, total number of ports will listen on. -- ports_num_for_sparse: **required, default 1**, number of ports which serves sparse parameter update. +- ports_num_for_sparse: **required, default 0**, number of ports which serves sparse parameter update. - num_gradient_servers: **required, default 1**, total number of gradient servers. ### Starting trainer @@ -98,7 +98,7 @@ Parameter Description - trainer_count: **required, default 1**, total count of trainers in the training job. - port: **required, default 7164**, port to connect to parameter server. - ports_num: **required, default 1**, number of ports for communication. -- ports_num_for_sparse: **required, default 1**, number of ports for sparse type caculation. +- ports_num_for_sparse: **required, default 0**, number of ports for sparse type caculation. - num_gradient_servers: **required, default 1**, total number of gradient server. - trainer_id: **required, default 0**, ID for every trainer, start from 0. - pservers: **required, default 127.0.0.1**, list of IPs of parameter servers, separated by ",". -- GitLab From 8ab59dac1694c71c418c5e3e2f59b5f155e9cedd Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 8 Jan 2018 18:57:46 +0800 Subject: [PATCH 0135/2305] Update doc --- doc/design/error_clip.md | 85 +++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 49 deletions(-) diff --git a/doc/design/error_clip.md b/doc/design/error_clip.md index 72ff7f611fc..8e845462cce 100644 --- a/doc/design/error_clip.md +++ b/doc/design/error_clip.md @@ -2,20 +2,13 @@ ## Overview -Error clip is widely used in model training to prevent gradient exploding. It takes a value as clip threshold. With error clip, all gradient values will be checked before they are taken by the next `grad_op`, and values greater than the threshold will be clipped. - +Error clip is widely used in model training to prevent gradient exploding. It takes some specific rules to adjust variables' gradients and prevent them from being too large. With it, values of a gradient will be checked before they are taken by the next `grad_op` and be shrunk if necessary. ## Usage -Users can enable clip and set related attributes via invoking `Optimizer`'s `minimize` API: +Users are allowed to assign different error clip methods or attributes to different `Variable`s. Users can specify it as a parameter of `Variable`'s constructor: ```python -def minimize(self, - loss, - startup_program=None, - parameter_list=None, - no_grad_set=None, - error_clip=None): - # ... +var = framework.Variable(..., error_clip=myErrorClip, ...) ``` The default value of `error_clip` is `None`, which means no error clip is employed. When it's not `None`, it should take an object of `BaseErrorClipAttr`'s derived class. So far, `BaseErrorClipAttr` has only one derived class: `ErrorClipByValue`, whose constructor is: @@ -24,13 +17,12 @@ The default value of `error_clip` is `None`, which means no error clip is employ ErrorClipByValue(max, min=None) ``` -`max` and `min` represent the maximal and minimal clip threshold respectively. When the `min` is None, the minimal threshold will be assigned with `-max`. +`max` and `min` represent the maximal and minimal clip threshold respectively. In backward pass, all values of `var`'s gradient greater than `max` or less than `min` will be clipped to `max` and `min` respectively. When the `min` is None, the minimal threshold will be assigned with `-max` automatically. -So we can enable the error clip with threshold `[-5.0, 5.0]` by: +So we can enable the error clip with threshold `[-5.0, 5.0]` for variable `var` by: ```python -opt = fluid.optimizer.SGD(learning_rate=0.001) -opt.minimize(loss=avg_cost, error_clip=ErrorClipByValue(max=5.0)) +var = framework.Variable(..., error_clip=ErrorClipByValue(max=5.0), ...) ``` ## Implementation @@ -39,17 +31,9 @@ The `BaseErrorClipAttr` and its derived class `ErrorClipByValue` are defined in ```python class BaseErrorClipAttr(object): - def create_clip_op_desc(self, grad_name): + def append_clip_op(self, block, grad_name): raise NotImplementedError() - def prepend_clip_op_desc(self, op_descs): - grad_names = set() - for op_desc in op_descs: - grad_names.update(filter(lambda n: n.find( - core.grad_var_suffix()) != -1, op_desc.output_arg_names())) - for n in grad_names: - op_descs.append(self.create_clip_op_desc(grad_name=n)) - class ErrorClipByValue(BaseErrorClipAttr): def __init__(self, max, min=None): @@ -61,40 +45,43 @@ class ErrorClipByValue(BaseErrorClipAttr): self.max = max self.min = min - def create_clip_op_desc(self, grad_name): - desc = core.OpDesc() - desc.set_type("clip") - desc.set_input("X", grad_name) - desc.set_output("Out", grad_name) - desc.set_attr("min", self.min) - desc.set_attr("max", self.max) - return desc + def append_clip_op(self, block, grad_name): + block.append_op( + type="clip", + inputs={"X": grad_name}, + outputs={"Out": grad_name}, + attrs={"min": self.min, + "max": self.max}) ``` -The `BaseErrorClipAttr` have two main member functions: - -- **`create_clip_op_desc(self, grad_name)`** - -> This function is used to create a C++ `OpDesc` object of `clip_op` and return its pointer to Python. For different error clips require different `clip_op`, the function is defined as virtual in the base class. All derived classes must implement their own versions of this function. +The `BaseErrorClipAttr` have one main member functions: `append_clip_op(self, block, grad_name)`. -- **`prepend_clip_op_desc(self, op_descs)`** +This function is used to create a `clip_op` and append it to the end of given `block`. For different error clip algorithm require different `clip_op`, the function is defined as virtual in the base class. All derived classes must implement their own versions of this function. -> This function takes a list of C++ `OpDesc` as input. It checks each `OpDesc` in the list, creates `clip_op`s for every gradient outputs and then appends them to the input list. The input `op_descs` is supposed to be the backward of a certain forward op. It can contain one or more `OpDesc`s (Some op's backward is a combination of several other ops). - -This two functions take effort during the backward building. Just as we showed in the *Usage* section, `Optimizer`'s `minimize` function can take an object of `ErrorClipByValue`(or some other `BaseErrorClipAttr`'s derived class). Inside the `minimize` function, the `prepend_clip_op_desc` function will be send to backward building process as an callback function: +These `clip_op`s should be inserted after `grad_op`s whose output gradients need to be clipped. It is equivalent to appending some `clip_op`s to the end of the target block every time a new `grad_op` is added. ```python -params_grads = append_backward(loss=loss, - parameter_list=parameter_list, - no_grad_set=no_grad_set, - callback=error_clip.prepend_clip_op_desc) +for op_desc in grad_op_descs: + new_op_desc = target_block.desc.append_op() + new_op_desc.copy_from(op_desc) + callback(block=target_block, context=grad_to_var) ``` -Each time we get the backward of a forward op, we invoke the callback function to append `clip_op` for all the new generated gradients(In the `_append_backward_ops_` function of *backward.py*): +Here we employ a callback function to complete this kind of jobs. In `_append_backward_ops_` function, each time after a `grad_op` is added to the `target_block`, a callback function is invoked. The logic of `clip_op` appending can be implemented inside the callback function. + +The callback function for `clip_op` appending is defined in *clip.py*: ```python -grad_op_desc, op_grad_to_var = core.get_grad_op_desc( - op.desc, no_grad_dict[block.idx], grad_sub_block_list) -if callback is not None: - grad_op_desc = callback(grad_op_desc) +def error_clip_callback(block, context): + # the context is a grad_to_var map + grad_to_var = context + op_desc = block.desc.op(block.desc.op_size() - 1) + for grad_n in filter(lambda n: grad_to_var.has_key(n), + op_desc.output_arg_names()): + fwd_var = block.var_recursive(grad_to_var[grad_n]) + error_clip = getattr(fwd_var, "error_clip", None) + if error_clip is not None: + error_clip.append_clip_op(block, grad_n) ``` + +This function takes a `block` and a `context`(which is actually a grad\_to\_var map) as inputs. It checks each output of the last `OpDesc` in the `block`. Notice that the last `OpDesc` of the `block` must be a `grad_op` and its outputs must be some forward variables' gradients. If an output gradient's corresponding forward variable has an attribute of `error_clip`, `error_clip_callback` will call the `error_clip`'s `append_clip_op` function to append the required `clip_op` into the `block`. -- GitLab From 207642657e353003507243bdf863b1e27b7a04cd Mon Sep 17 00:00:00 2001 From: ranqiu Date: Mon, 8 Jan 2018 19:32:07 +0800 Subject: [PATCH 0136/2305] Add script to plot learning curve --- benchmark/paddle/image/plotlog.py | 87 +++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 benchmark/paddle/image/plotlog.py diff --git a/benchmark/paddle/image/plotlog.py b/benchmark/paddle/image/plotlog.py new file mode 100644 index 00000000000..9ac78d69101 --- /dev/null +++ b/benchmark/paddle/image/plotlog.py @@ -0,0 +1,87 @@ +#coding=utf-8 + +import sys +import argparse +import matplotlib.pyplot as plt + + +def parse_args(): + parser = argparse.ArgumentParser('Parse Log') + parser.add_argument( + '--file_path', '-f', type=str, help='the path of the log file') + parser.add_argument( + '--sample_rate', + '-s', + type=float, + default=1.0, + help='the rate to take samples from log') + parser.add_argument( + '--log_period', '-p', type=int, default=1, help='the period of log') + + args = parser.parse_args() + return args + + +def parse_file(file_name): + loss = [] + error = [] + with open(file_name) as f: + for i, line in enumerate(f): + line = line.strip() + if not line.startswith('pass'): + continue + line_split = line.split(' ') + if len(line_split) != 5: + continue + + loss_str = line_split[2][:-1] + cur_loss = float(loss_str.split('=')[-1]) + loss.append(cur_loss) + + err_str = line_split[3][:-1] + cur_err = float(err_str.split('=')[-1]) + error.append(cur_err) + + accuracy = [1.0 - err for err in error] + + return loss, accuracy + + +def sample(metric, sample_rate): + interval = int(1.0 / sample_rate) + if interval > len(metric): + return metric[:1] + + num = len(metric) / interval + idx = [interval * i for i in range(num)] + metric_sample = [metric[id] for id in idx] + return metric_sample + + +def plot_metric(metric, batch_id, graph_title): + plt.figure() + plt.title(graph_title) + plt.plot(batch_id, metric) + plt.xlabel('batch') + plt.ylabel(graph_title) + plt.savefig(graph_title + '.jpg') + plt.close() + + +def main(): + args = parse_args() + assert args.sample_rate > 0. and args.sample_rate <= 1.0, "The sample rate should in the range (0, 1]." + + loss, accuracy = parse_file(args.file_path) + batch = [args.log_period * i for i in range(len(loss))] + + batch_sample = sample(batch, args.sample_rate) + loss_sample = sample(loss, args.sample_rate) + accuracy_sample = sample(accuracy, args.sample_rate) + + plot_metric(loss_sample, batch_sample, 'loss') + plot_metric(accuracy_sample, batch_sample, 'accuracy') + + +if __name__ == '__main__': + main() -- GitLab From f35c56060c4af6a8b36ceb8ad9021c447d6dc2a0 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Mon, 8 Jan 2018 19:59:44 +0800 Subject: [PATCH 0137/2305] split tensor to pservers --- .../paddle/v2/fluid/distribute_transpiler.py | 187 +++++++------- .../v2/fluid/distribute_transpiler_simple.py | 242 ++++++++++++++++++ python/paddle/v2/fluid/distributed_spliter.py | 51 ++-- .../tests/book_distribute/test_split_var.py | 38 +++ 4 files changed, 398 insertions(+), 120 deletions(-) create mode 100644 python/paddle/v2/fluid/distribute_transpiler_simple.py create mode 100644 python/paddle/v2/fluid/tests/book_distribute/test_split_var.py diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index 4c90b4a8535..58d32bac125 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -4,6 +4,7 @@ from framework import Program, default_main_program, Parameter, Variable import optimizer from layer_helper import LayerHelper from distributed_spliter import * +import math class VarBlock: @@ -17,6 +18,47 @@ class VarBlock: return "%s:%d:%d" % (self.varname, self.offset, self.size) +def split_dense_variable(var_list, + pserver_count, + min_block_size=1024, + max_block_size=1048576): + """ + We may need to split dense tensor to one or several blocks and put + them equally onto parameter server. One block is a sub-tensor + aligned by dim[0] of the tensor. + + We need to have a minimal block size so that the calculations in + the parameter server side can gain better performance. By default + mininum block size is 1024. The max block size is used to prevent + too large block that may causing send error. + """ + blocks = [] + for var in var_list: + split_count = pserver_count + var_numel = reduce(lambda x, y: x * y, var.shape) + max_pserver_count = int(math.floor(var_numel / float(min_block_size))) + if max_pserver_count == 0: + max_pserver_count = 1 + if max_pserver_count < pserver_count: + split_count = max_pserver_count + block_size = int(math.ceil(var_numel / float(split_count))) + + if len(var.shape) >= 2: + # align by dim1(width) + dim1 = reduce(lambda x, y: x * y, var.shape[1:]) + remains = block_size % dim1 + if remains != 0: + block_size += dim1 - remains + # update split_count after align + split_count = int(math.ceil(var_numel / float(block_size))) + for block_id in xrange(split_count): + curr_block_size = min(block_size, var_numel - ( + (block_id) * block_size)) + block = VarBlock(var.name, block_id, curr_block_size) + blocks.append(str(block)) + return blocks + + class DistributeTranspiler: def transpile(self, optimize_ops, @@ -57,43 +99,49 @@ class DistributeTranspiler: # 5. create parameter server program by split_method generated endpoint->VarBlock # 6. run compile time infershape for parameter server program - if kwargs.has_key("split_method"): - split_method = kwargs["split_method"] - else: - split_method = round_robin - pserver_endpoints = kwargs["pservers"].split(",") - - grad2param = dict() - for param, grad in params_and_grads: - grad2param[grad.name()] = param.name() + pserver_endpoints = pservers.split(",") # step1 - param_list = [pg[0] for pg in params_and_grads] - grad_list = [pg[1] for pg in params_and_grads] + param_list = [pg[0] for pg in params_grads] + grad_list = [pg[1] for pg in params_grads] # TODO: add split selected rows support - grad_blocks = _split_dense_variable(grad_list, len(pserver_endpoints)) - param_blocks = _split_dense_variable(param_list, len(pserver_endpoints)) - ep2gradblock = split_method(grad_blocks, pserver_endpoints) - # self.param_grad_map + grad_blocks = split_dense_variable(grad_list, len(pserver_endpoints)) + param_blocks = split_dense_variable(param_list, len(pserver_endpoints)) # step2 - var2splited = self._split_trainer_vars(program, grad_blocks) + grad_var_mapping = self._append_split_op(program, grad_blocks) # step3 send_inputs = [] - for _, splited in var2splited.iteritems(): + send_outputs = [] + for _, splited in grad_var_mapping.iteritems(): send_inputs.extend(splited) - send_outputs = self._create_vars_from_blocklist(program, param_blocks) + param_var_mapping = self._create_vars_from_blocklist(program, + param_blocks) + for _, splited in param_var_mapping.iteritems(): + send_outputs.extend(splited) + # let send_op know which endpoint to send which var, eplist is of the same + # order of send_inputs. + eplist = split_method(send_inputs, pserver_endpoints) send_op = program.global_block().append_op( type="send", inputs={"X": send_inputs}, outputs={"Out": send_outputs}, attrs={"endpoints": pserver_endpoints, - "epmap": epmap}) + "epmap": eplist}) + + # step4 + for varname, splited_var in param_var_mapping.iteritems(): + orig_param = program.global_block().vars[varname] + concat = program.global_block().append_op( + type="concat", + inputs={"X": send_outputs}, + outputs={"Out": orig_param}, + attrs={"axis": 0}) def _create_vars_from_blocklist(self, program, block_list): block_map = dict() - ret_vars = [] + var_mapping = dict() for block_str in block_list: varname, offset, size = block_str.split(":") if not block_map.has_key(varname): @@ -102,15 +150,26 @@ class DistributeTranspiler: for varname, splited in block_map.iteritems(): orig_var = program.global_block().vars[varname] - for block in splited: + orig_shape = orig_var.shape + orig_dim1_flatten = 1 + if len(orig_shape) >= 2: + orig_dim1_flatten = reduce(lambda x, y: x * y, orig_shape[1:]) + var_list = [] + for i, block in enumerate(splited): size = block[1] + rows = size / orig_dim1_flatten + splited_shape = [rows] + if len(orig_shape) >= 2: + splited_shape.extend(orig_shape[1:]) + print("block, splited shape:", block, splited_shape) var = program.global_block().create_var( name="%s.block%d" % (varname, i), psersistable=False, dtype=orig_var.dtype, - shape=[1, size]) # flattend splited var - ret_vars.append(var) - return ret_vars + shape=splited_shape) # flattend splited var + var_list.append(var) + var_mapping[varname] = var_list + return var_mapping def _clone_param(self, block, v): assert isinstance(v, Parameter) @@ -137,80 +196,22 @@ class DistributeTranspiler: lod_level=var.lod_level, persistable=var.persistable) - def _split_dense_variable(self, - var_list, - pserver_count, - min_block_size=1024, - max_block_size=1048576): - """ - We may need to split dense tensor to one or several blocks and put - them equally onto parameter server. One block is a sub-tensor - aligned by dim[0] of the tensor. - - We need to have a minimal block size so that the calculations in - the parameter server side can gain better performance. By default - mininum block size is 1024. The max block size is used to prevent - too large block that may causing send error. - """ - block_sizes = [] - blocks = [] - for grad in var_list: - dim1 = reduce(lambda x, y: x * y, grad.shape[1:]) - grad_numel = reduce(lambda x, y: x * y, grad.shape) - if grad_numel < min_block_size: - block_sizes.append(grad_numel) - block_size = grad_numel / min_block_size - if block_size < min_block_size: - block_size = min_block_size - # align by dim1(width) - remains = block_size % dim1 - if remains != 0: - block_size += dim1 - remains - block_sizes.append(block_size) - num_blocks = grad_numel / block_size - print("grad numel :%d, blocksize: %d" % grad_numel, block_size) - for block_id in xrange(num_blocks): - block = VarBlock(grad.name(), block_id, block_size) - blocks.append(str(block)) - return blocks - - def _split_trainer_vars(self, program, gradblocks, params_and_grads): - var2blocks = dict() - splited = dict() - for block_str in gradblocks: - varname, offset, size = block_str.split(":") - if not var2blocks.has_key(varname): - var2blocks[varname] = [] - var2blocks[varname].append((long(offset), long(size))) - for varname, blocks in var2blocks.iteritems(): + def _append_split_op(self, program, gradblocks): + var_mapping = self._create_vars_from_blocklist(program, gradblocks) + for varname, splited_vars in var_mapping.iteritems(): + if len(splited_vars) == 1: + continue orig_var = program.global_block().vars[varname] - split_outs = [] - for i in xrange(len(blocks)): - size = blocks[i][1] - var = program.global_block().create_var( - name="%s.block%d" % (varname, i), - psersistable=False, - dtype=orig_var.dtype, - shape=[1, size]) # flattend splited var - split_outs.append(var) - - splited[varname] = split_outs + sections = [] + for v in splited_vars: + sections.append(v.shape[0]) program.global_block().append_op( type="split", inputs={"X": orig_var}, - outputs={"Out": split_outs}, - attrs={"num": len(blocks)} # assume split evenly + outputs={"Out": splited_vars}, + attrs={"sections": sections} # assume split evenly ) - return splited - - def _concat_trainer_vars(self, program, splited): - for varname, to_merge_list in splited.iteritems(): - orig_var = program.global_block().vars[varname] - program.global_block().append_op( - type="concat", - inputs={"X": to_merge_list}, - outputs={"Out": orig_var}, - attrs={}) + return var_mapping def get_trainer_program(self): # remove optimize ops and add a send op to main_program diff --git a/python/paddle/v2/fluid/distribute_transpiler_simple.py b/python/paddle/v2/fluid/distribute_transpiler_simple.py new file mode 100644 index 00000000000..49ece7b725e --- /dev/null +++ b/python/paddle/v2/fluid/distribute_transpiler_simple.py @@ -0,0 +1,242 @@ +import framework +from framework import Program, default_main_program, Parameter, Variable +import optimizer +from layer_helper import LayerHelper + + +def hash_name_to_server(params_grads, pserver_endpoints): + """ + :param param_grads: + :return: a map of pserver endpoint -> + params -> [param list] + grads -> [grad list] + """ + + def _hash_param(param_name, total): + return hash(param_name) % total + + param_grad_map = dict() + for param, grad in params_grads: + if param.trainable is True and grad is not None: + server_id = _hash_param(param.name, len(pserver_endpoints)) + server_for_param = pserver_endpoints[server_id] + if not param_grad_map.has_key(server_for_param): + param_grad_map[server_for_param] = {"params": [], "grads": []} + param_grad_map[server_for_param]["params"].append(param) + param_grad_map[server_for_param]["grads"].append(grad) + + return param_grad_map + + +def round_robin(params_grads, pserver_endpoints): + assert (len(params_grads) > len(pserver_endpoints)) + + param_grad_map = dict() + pserver_idx = 0 + for param, grad in params_grads: + if param.trainable is True: + server_for_param = pserver_endpoints[pserver_idx] + if not param_grad_map.has_key(server_for_param): + param_grad_map[server_for_param] = {"params": [], "grads": []} + + param_grad_map[server_for_param]["params"].append(param) + param_grad_map[server_for_param]["grads"].append(grad) + + pserver_idx += 1 + if pserver_idx >= len(pserver_endpoints): + pserver_idx = 0 + return param_grad_map + + +class DistributeTranspiler: + def transpile(self, + optimize_ops, + params_grads, + program=None, + pservers="127.0.0.1:6174", + trainers=1, + split_method=round_robin): + """ + Transpile the program to a distributed data-parallelism programs. + + The main_program will be transform to use a remote parameter server + to do parameter optimization. And the optimization graph will be put + in to a parameter server program. + + Use different methods to split trainable varialbles to different + parameter servers. + + Example to run: + + exe = fluid.Executor(place) + t = fluid.DistributeTranspiler() + t.transpile(optimize_ops, params_grads, pservers="127.0.0.1:6174", trainers=1) + + pserver_endpoint = os.getenv("PSERVER") + if pserver_endpoint: + pserver_prog = t.get_pserver_program(pserver_endpoint, optimize_ops) + exe.run(fluid.default_startup_program()) + exe.run(pserver_prog) + else: + feeder = fluid.DataFeeder(feed_list=[images, label], place=place) + exe.run(fluid.default_startup_program()) + + for pass_id in range(PASS_NUM): + ... + + :param optimize_ops: op list of optimization, should be the + return value of Optimizer.minimize + :type optimize_ops: list + :param program: program to optimize, default default_main_program + :param pservers: parameter server endpoints like "m1:6174,m2:6174" + :type pservers: string + + :return: return a list of programs + """ + if program is None: + program = default_main_program() + self.program = program + self.trainers = trainers + self.optimize_ops = optimize_ops + self._optimize_distributed( + optimize_ops, + program, + params_grads, + pservers=pservers, + trainers=trainers, + split_method=split_method) + + def _clone_param(self, block, v): + assert isinstance(v, Parameter) + new_p = Parameter( + block=block, + shape=v.shape, + dtype=v.dtype, + type=v.type, + lod_level=v.lod_level, + stop_gradient=v.stop_gradient, + trainable=v.trainable, + optimize_attr=v.optimize_attr, + regularizer=v.regularizer, + name=v.name) + block.vars[new_p.name] = new_p + + def _clone_var(self, block, var): + assert isinstance(var, Variable) + return block.create_var( + name=var.name, + shape=var.shape, + dtype=var.dtype, + type=var.type, + lod_level=var.lod_level, + persistable=var.persistable) + + def _optimize_distributed(self, optimize_ops, program, params_and_grads, + **kwargs): + if kwargs.has_key("split_method"): + split_method = kwargs["split_method"] + else: + split_method = round_robin + + assert (callable(split_method)) + pserver_endpoints = kwargs["pservers"].split(",") + self.param_grad_map = split_method(params_and_grads, pserver_endpoints) + + send_op_ordered_inputs = [] + send_op_ordered_outputs = [] + epmap = [] + for ep, v in self.param_grad_map.iteritems(): + send_op_ordered_inputs.extend(v["grads"]) + send_op_ordered_outputs.extend(v["params"]) + for i in v["grads"]: + epmap.append(ep) + send_op = program.global_block().append_op( + type="send", + inputs={"X": send_op_ordered_inputs + }, # inputs is a list of tensors to be send + outputs={"Out": send_op_ordered_outputs}, + attrs={"endpoints": pserver_endpoints, + "epmap": epmap}) + + def get_trainer_program(self): + # remove optimize ops and add a send op to main_program + self.program.global_block().delete_ops(self.optimize_ops) + return self.program + + def _create_var_for_trainers(self, block, var, trainers): + var_list = [] + for i in xrange(trainers): + var_each = block.create_var( + name="%s.trainer_%d" % (var.name, i), + psersistable=var.persistable, + dtype=var.dtype, + shape=var.shape) + var_list.append(var_each) + return var_list + + def get_pserver_program(self, endpoint, optimize_ops): + pserver_program = Program() + for v in self.param_grad_map[endpoint]["params"]: + self._clone_param(pserver_program.global_block(), v) + + optimize_sub_program = Program() + grad_var_names = [ + var.name for var in self.param_grad_map[endpoint]["grads"] + ] + for opt_op in optimize_ops: + for _, var in opt_op.inputs.iteritems(): + # NOTE: append operators to merge gradients from multiple + # trainers. If trainers == 1, this is not needed. + if self.trainers > 1 and var.name in grad_var_names: + vars2merge = self._create_var_for_trainers( + optimize_sub_program.global_block(), var, self.trainers) + merged_var = optimize_sub_program.global_block().create_var( + name=var.name, + persistable=var.persistable, + dtype=var.dtype, + shape=var.shape) + optimize_sub_program.global_block().append_op( + type="sum", + inputs={"X": vars2merge}, + outputs={"Out": merged_var}) + optimize_sub_program.global_block().append_op( + type="scale", + inputs={"X": merged_var}, + outputs={"Out": merged_var}, + attrs={"scale": 1.0 / float(self.trainers)}) + else: + optimize_sub_program.global_block().create_var( + name=var.name, + persistable=var.persistable, + dtype=var.dtype, + shape=var.shape) + + if opt_op.inputs.has_key("Grad"): + if opt_op.inputs["Grad"].name in grad_var_names: + optimize_sub_program.global_block().append_op( + type=opt_op.type, + inputs=opt_op.inputs, + outputs=opt_op.outputs, + attrs=opt_op.attrs) + else: + optimize_sub_program.global_block().append_op( + type=opt_op.type, + inputs=opt_op.inputs, + outputs=opt_op.outputs, + attrs=opt_op.attrs) + pserver_program.global_block().append_op( + type="recv", + inputs={"RX": + self.param_grad_map[endpoint]["grads"]}, # grads to recv + outputs={}, + attrs={ + "OptimizeProgram": optimize_sub_program.desc, + "endpoint": endpoint, + "ParamList": + [p.name for p in self.param_grad_map[endpoint]["params"]], + "GradList": + [p.name for p in self.param_grad_map[endpoint]["grads"]], + "Trainers": self.trainers + }) + pserver_program.sync_with_cpp() + return pserver_program diff --git a/python/paddle/v2/fluid/distributed_spliter.py b/python/paddle/v2/fluid/distributed_spliter.py index e7ba53390d4..eff30f7bb66 100644 --- a/python/paddle/v2/fluid/distributed_spliter.py +++ b/python/paddle/v2/fluid/distributed_spliter.py @@ -1,38 +1,35 @@ -def hash_name(varblocks, pserver_endpoints): +def hash_name(varlist, pserver_endpoints): """ - :param varblocks: a list of VarBlock string indicating - sub blocks of variables - :return: a map of pserver endpoint -> varblock_str + hash variable names to several endpoints. + + :param varlist: a list of Variables + :return: a map of pserver endpoint -> varname """ def _hash_block(block_str, total): return hash(block_str) % total - ep2block = dict() - for varblock_str in varblocks: - if param.trainable is True and grad is not None: - server_id = _hash_block(varblock_str, len(pserver_endpoints)) - server_for_param = pserver_endpoints[server_id] - if not ep2block.has_key(server_for_param): - ep2block[server_for_param] = [] - ep2block[server_for_param].append(varblock_str) - - return ep2block + eplist = [] + for var in varlist: + server_id = _hash_block(var.name(), len(pserver_endpoints)) + server_for_param = pserver_endpoints[server_id] + eplist.append(server_for_param) + return eplist -def round_robin(varblocks, pserver_endpoints): - assert (len(varblocks) > len(pserver_endpoints)) +def round_robin(varlist, pserver_endpoints): + """ + distribute variables to several endpoints. + """ + assert (len(varlist) > len(pserver_endpoints)) - ep2block = dict() + eplist = [] pserver_idx = 0 - for varblock_str in varblocks: - if param.trainable is True: - server_for_param = pserver_endpoints[pserver_idx] - if not ep2block.has_key(server_for_param): - ep2block[server_for_param] = [] - ep2block[server_for_param].append(varblock_str) + for var in varlist: + server_for_param = pserver_endpoints[pserver_idx] + eplist.append(server_for_param) - pserver_idx += 1 - if pserver_idx >= len(pserver_endpoints): - pserver_idx = 0 - return ep2block + pserver_idx += 1 + if pserver_idx >= len(pserver_endpoints): + pserver_idx = 0 + return eplist diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py b/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py new file mode 100644 index 00000000000..1355e13e1c1 --- /dev/null +++ b/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py @@ -0,0 +1,38 @@ +import math +import unittest +from paddle.v2.fluid.distribute_transpiler import split_dense_variable +import paddle.v2.fluid as fluid +import random + + +class TestSplitVar(unittest.TestCase): + def test_check_output(self): + # split below shapes to 10 servers + shapes = [[3, 5], [1024], [28, 784], [8, 1020], [800, 10]] + expected_sizes = [ + [15], [1024], + [2352, 2352, 2352, 2352, 2352, 2352, 2352, 2352, 2352, 784], + [2040, 2040, 2040, 2040], + [1150, 1150, 1150, 1150, 1150, 1150, 1100] + ] + var_list = [] + program = fluid.Program() + for shape in shapes: + var = program.global_block().create_var( + name=str(random.randint(10000)), + persistable=True, + dtype=core.VarDesc.VarType.LOD_TENSOR, + shape=shape) + var_list.append(var) + blocks = split_dense_variable(var_list, 10) + all_sizes = [] + for s in expected_sizes: + for s2 in s: + all_sizes.append(s2) + for i, block_str in enumerate(blocks): + varname, block_id, size = block_str.split(":") + self.assertEqual(int(size), all_sizes[i]) + + +if __name__ == '__main__': + unittest.main() -- GitLab From 0b52cc886f2fbc0e491c9a73ff5ee3e856915b55 Mon Sep 17 00:00:00 2001 From: qiaolongfei Date: Mon, 8 Jan 2018 13:06:56 +0000 Subject: [PATCH 0138/2305] fix priority --- paddle/framework/operator.cc | 5 ++++- paddle/operators/fetch_op.cc | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index 3744eae6968..febad37b42b 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -488,6 +488,8 @@ void OperatorWithKernel::Run(const Scope& scope, } } + VLOG(3) << "expected_kernel_key:" << expected_kernel_key; + Scope& new_scope = scope.NewScope(); for (auto& var_name_item : this->Inputs()) { @@ -520,7 +522,8 @@ void OperatorWithKernel::Run(const Scope& scope, auto kernel_iter = kernels.find(expected_kernel_key); - kernel_iter->second->Compute(ExecutionContext(*this, new_scope, *dev_ctx)); + kernel_iter->second->Compute(ExecutionContext( + *this, new_scope, *pool.Get(expected_kernel_key.place_))); } proto::DataType OperatorWithKernel::IndicateDataType( diff --git a/paddle/operators/fetch_op.cc b/paddle/operators/fetch_op.cc index 387d1e0a747..48c01f984f8 100644 --- a/paddle/operators/fetch_op.cc +++ b/paddle/operators/fetch_op.cc @@ -53,7 +53,7 @@ class FetchOp : public framework::OperatorBase { // FIXME(yuyang18): Should we assume the fetch operator always generate // CPU outputs? platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); - auto &dev_ctx = *pool.Get(place); + auto &dev_ctx = *pool.Get(src_item.place()); CopyFrom(src_item, platform::CPUPlace(), dev_ctx, &dst_item); dev_ctx.Wait(); -- GitLab From 5b94948b320dd7499f8b847bd535278f89db2a8e Mon Sep 17 00:00:00 2001 From: qiaolongfei Date: Mon, 8 Jan 2018 14:56:16 +0000 Subject: [PATCH 0139/2305] disable UseAll when init --- paddle/framework/init.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/framework/init.cc b/paddle/framework/init.cc index 7ec8d18b0e8..e7087e063cb 100644 --- a/paddle/framework/init.cc +++ b/paddle/framework/init.cc @@ -72,7 +72,7 @@ bool InitDevices(const std::vector &devices) { LOG(WARNING) << "Not specified CPU device, create CPU by Default."; } platform::DeviceContextPool::Init(places); - framework::UseALL(); + // framework::UseALL(); return true; } -- GitLab From 37f933b8ad85fb17fa903e59074ab6225ef4eec3 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Mon, 8 Jan 2018 15:25:45 +0000 Subject: [PATCH 0140/2305] Add gpu kernel for sequence_erase_op --- paddle/operators/sequence_erase_op.cu | 136 ++++++++++++++++++ paddle/operators/sequence_erase_op.h | 4 +- .../v2/fluid/tests/test_sequence_erase_op.py | 1 - 3 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 paddle/operators/sequence_erase_op.cu diff --git a/paddle/operators/sequence_erase_op.cu b/paddle/operators/sequence_erase_op.cu new file mode 100644 index 00000000000..5d314586d4d --- /dev/null +++ b/paddle/operators/sequence_erase_op.cu @@ -0,0 +1,136 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include +#include +#include +#include +#include "paddle/operators/sequence_erase_op.h" +#include "paddle/platform/cuda_helper.h" +#include "paddle/platform/gpu_info.h" + +namespace paddle { +namespace operators { +using platform::PADDLE_CUDA_NUM_THREADS; +using Tensor = framework::Tensor; +using LoDTensor = framework::LoDTensor; + +template +__global__ void LabelErasedIdx(const T* in_dat, const int in_len, + const T* tokens, const int tokens_len, + int* num_erased) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index < in_len) { + int erased = 0; + for (int i = 0; i < tokens_len; ++i) { + if (in_dat[index] == tokens[i]) { + erased = 1; + } + } + num_erased[index + 1] = erased; + if (index == 0) { + num_erased[0] = 0; + } + } +} + +template +__global__ void GetOutLod(const T* num_erased, const int* in_lod, + const int lod_len, int* out_lod0) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index < lod_len) { + out_lod0[index] = in_lod[index] - num_erased[in_lod[index]]; + } +} + +template +__global__ void SetOutput(const T* in_dat, const int in_len, + const int* num_erased, T* out_dat) { + int index = blockIdx.x * blockDim.x + threadIdx.x; + if (index < in_len) { + if (in_dat[index] != in_dat[index + 1]) { + out_dat[index - num_erased[index]] = in_dat[index]; + } + } +} + +template +class SequenceEraseOpCUDAKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* in = ctx.Input("X"); + auto* out = ctx.Output("Out"); + + auto lod = in->lod(); + PADDLE_ENFORCE_EQ(lod.size(), 1UL, "Only support one level sequence now."); + auto tokens = ctx.Attr>("tokens"); + auto tokens_len = tokens.size(); + auto in_len = in->numel(); + auto in_dat = in->data(); + auto lod0 = lod[0]; + + thrust::host_vector host_tokens(tokens_len); + for (size_t i = 0; i < tokens.size(); ++i) { + host_tokens[i] = tokens[i]; + } + thrust::device_vector dev_tokens = host_tokens; + thrust::device_vector num_erased(in_len + 1); + + T* dev_tokens_ptr = thrust::raw_pointer_cast(dev_tokens.data()); + int* num_erased_ptr = thrust::raw_pointer_cast(num_erased.data()); + + auto stream = ctx.cuda_device_context().stream(); + LabelErasedIdx<<<(in_len - 1) / PADDLE_CUDA_NUM_THREADS + 1, + PADDLE_CUDA_NUM_THREADS, 0, stream>>>( + in_dat, in_len, dev_tokens_ptr, tokens_len, num_erased_ptr); + thrust::inclusive_scan(num_erased.begin() + 1, num_erased.end(), + num_erased.begin() + 1); + + // Reset LoD + auto lod_len = lod0.size(); + thrust::host_vector host_lod(lod_len); + for (size_t i = 0; i < lod_len; ++i) { + host_lod[i] = lod0[i]; + } + thrust::device_vector dev_in_lod = host_lod; + thrust::device_vector dev_out_lod(lod_len); + int* dev_in_lod_ptr = thrust::raw_pointer_cast(dev_in_lod.data()); + int* dev_out_lod_ptr = thrust::raw_pointer_cast(dev_out_lod.data()); + GetOutLod<<<(lod_len - 1) / PADDLE_CUDA_NUM_THREADS + 1, + PADDLE_CUDA_NUM_THREADS, 0, stream>>>( + num_erased_ptr, dev_in_lod_ptr, lod_len, dev_out_lod_ptr); + thrust::host_vector host_out_lod = dev_out_lod; + std::vector out_lod0(lod_len, 0); + for (size_t i = 0; i < lod_len; i++) { + out_lod0[i] = host_out_lod[i]; + } + framework::LoD out_lod; + out_lod.push_back(out_lod0); + + out->Resize({out_lod0.back(), 1}); + // Set output + auto out_dat = out->mutable_data(ctx.GetPlace()); + SetOutput<<<(in_len - 1) / PADDLE_CUDA_NUM_THREADS + 1, + PADDLE_CUDA_NUM_THREADS, 0, stream>>>(in_dat, in_len, + num_erased_ptr, out_dat); + // Set LoD + out->set_lod(out_lod); + } +}; + +} // namespace operators +} // namespace paddle + +REGISTER_OP_CUDA_KERNEL(sequence_erase, + paddle::operators::SequenceEraseOpCUDAKernel); diff --git a/paddle/operators/sequence_erase_op.h b/paddle/operators/sequence_erase_op.h index 937b9870aa9..caf168a93de 100644 --- a/paddle/operators/sequence_erase_op.h +++ b/paddle/operators/sequence_erase_op.h @@ -27,8 +27,8 @@ template class SequenceEraseKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { - auto* in = ctx.Input("X"); - auto* out = ctx.Output("Out"); + auto* in = ctx.Input("X"); + auto* out = ctx.Output("Out"); auto lod = in->lod(); PADDLE_ENFORCE_EQ(lod.size(), 1UL, "Only support one level sequence now."); diff --git a/python/paddle/v2/fluid/tests/test_sequence_erase_op.py b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py index 74274cf0ad4..e730f2f4b78 100644 --- a/python/paddle/v2/fluid/tests/test_sequence_erase_op.py +++ b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py @@ -32,7 +32,6 @@ class TestSequenceEraseOp(OpTest): lod = [[0, 5, 15, 30]] tokens = [2, 5] out_seq, new_lod0 = sequence_erase(in_seq, lod[0], tokens) - self.attrs = {'tokens': tokens} self.inputs = {'X': (in_seq, lod)} self.outputs = {'Out': (out_seq, [new_lod0])} -- GitLab From 8b1a81a9bf402698f36a8e7ce5e79aac8017e462 Mon Sep 17 00:00:00 2001 From: qiaolongfei Date: Tue, 9 Jan 2018 00:20:16 +0800 Subject: [PATCH 0141/2305] fix GetDims bug --- paddle/framework/operator.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index a1f1be5f342..fe8096835d6 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -74,7 +74,9 @@ void UseALL() { static DDim GetDims(const Scope& scope, const std::string& name) { Variable* var = scope.FindVar(name); - if (var->IsType()) { + if (var == nullptr) { + return DDim({-1}); + } else if (var->IsType()) { return var->Get().dims(); } else if (var->IsType()) { return var->Get().GetCompleteDims(); -- GitLab From 7b9d5b325c7c513815085e9ab1f59a42600aecfc Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Mon, 8 Jan 2018 16:56:31 +0000 Subject: [PATCH 0142/2305] Add document for sequence_erase_op --- paddle/operators/sequence_erase_op.cc | 42 +++++++++++++++---- paddle/operators/sequence_erase_op.cu | 11 ++--- paddle/operators/sequence_erase_op.h | 17 +------- .../v2/fluid/tests/test_sequence_erase_op.py | 6 +-- 4 files changed, 42 insertions(+), 34 deletions(-) diff --git a/paddle/operators/sequence_erase_op.cc b/paddle/operators/sequence_erase_op.cc index e611ef0571d..331970b3f8e 100644 --- a/paddle/operators/sequence_erase_op.cc +++ b/paddle/operators/sequence_erase_op.cc @@ -26,7 +26,11 @@ class SequenceEraseOp : public framework::OperatorWithKernel { "Input(X) of SequenceEraseOp should not be null."); PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) of SequenceEraseOp should not be null."); - ctx->SetOutputDim("Out", ctx->GetInputDim("X")); + auto x_dims = ctx->GetInputDim("X"); + PADDLE_ENFORCE(x_dims.size() == 2 && x_dims[1] == 1, + "Input(X) of SequenceEraseOp should be a 2-D LoDTensor " + "with the 2nd dimension equal to 1."); + ctx->SetOutputDim("Out", x_dims); } }; @@ -35,17 +39,41 @@ class SequenceEraseOpMaker : public framework::OpProtoAndCheckerMaker { SequenceEraseOpMaker(OpProto* proto, OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("X", - "(LoDTensor) 2-D input LoDTensor with the 2-nd dimension " - "of length 1."); + "(2-D LoDTensor with the 2nd dim. equal to 1) " + "Input LoDTensor of SequenceEraseOp."); AddOutput("Out", - "(LoDTensor) 2-D output LoDTensor with the 2-nd dimension " - "of length 1."); + "(2-D LoDTensor with the 2nd dim. equal to 1) " + "Output LoDTensor of SequenceEraseOp."); AddAttr>("tokens", - "(vector) " - "Tokens to be removed from input."); + "(vector) Tokens need to be erased from " + "input sequences."); AddComment(R"DOC( Sequence Erase Operator. +Sequence erase operator erases tokens specified by Attr(tokens) in the input +sequences Input(X), and outputs the remaining data and modifies the LoD +information at the same time. For example, given a 2-D LoDTensor + + X = [[2, 2, 6, 1, 3, 9, 6, 1, 0, 1]]^T + +with lod = [[0, 3, 6, 10]], there are three sequences in the input: + + X1 = [[2, 2, 6]]^T, X2 = [[1, 3, 9]]^T and X3 = [[6, 1, 0, 1]]^T. + +If the tokens to be erased are Attr(tokens) = [2, 3, 5], after the erasing +operation, the three sequences become + + X1' = [[6]]^T, X2' = [[1, 9]]^T and X3' = [[6, 1, 0, 1]]^T. + +Hence the LoDTensor Output(Out) should be + + Out = [[6, 1, 9, 6, 1, 0, 1]]^T, + +with lod = [[0, 1, 3, 7]]. + +An example usage for this operator is to remove the special tokens when +computing the edit distance between two strings, such as blank, start token, +and end token. )DOC"); } }; diff --git a/paddle/operators/sequence_erase_op.cu b/paddle/operators/sequence_erase_op.cu index 5d314586d4d..3695a24cb73 100644 --- a/paddle/operators/sequence_erase_op.cu +++ b/paddle/operators/sequence_erase_op.cu @@ -13,17 +13,13 @@ See the License for the specific language governing permissions and limitations under the License. */ #include -#include #include -#include #include "paddle/operators/sequence_erase_op.h" #include "paddle/platform/cuda_helper.h" -#include "paddle/platform/gpu_info.h" namespace paddle { namespace operators { using platform::PADDLE_CUDA_NUM_THREADS; -using Tensor = framework::Tensor; using LoDTensor = framework::LoDTensor; template @@ -97,7 +93,7 @@ class SequenceEraseOpCUDAKernel : public framework::OpKernel { thrust::inclusive_scan(num_erased.begin() + 1, num_erased.end(), num_erased.begin() + 1); - // Reset LoD + // Calc LoD auto lod_len = lod0.size(); thrust::host_vector host_lod(lod_len); for (size_t i = 0; i < lod_len; ++i) { @@ -117,15 +113,14 @@ class SequenceEraseOpCUDAKernel : public framework::OpKernel { } framework::LoD out_lod; out_lod.push_back(out_lod0); + out->set_lod(out_lod); - out->Resize({out_lod0.back(), 1}); // Set output + out->Resize({out_lod0.back(), 1}); auto out_dat = out->mutable_data(ctx.GetPlace()); SetOutput<<<(in_len - 1) / PADDLE_CUDA_NUM_THREADS + 1, PADDLE_CUDA_NUM_THREADS, 0, stream>>>(in_dat, in_len, num_erased_ptr, out_dat); - // Set LoD - out->set_lod(out_lod); } }; diff --git a/paddle/operators/sequence_erase_op.h b/paddle/operators/sequence_erase_op.h index caf168a93de..92aa4a82b09 100644 --- a/paddle/operators/sequence_erase_op.h +++ b/paddle/operators/sequence_erase_op.h @@ -15,14 +15,10 @@ limitations under the License. */ #pragma once #include "paddle/framework/op_registry.h" -#include "paddle/operators/math/softmax.h" namespace paddle { namespace operators { -using Tensor = framework::Tensor; -using LoDTensor = framework::LoDTensor; - template class SequenceEraseKernel : public framework::OpKernel { public: @@ -32,17 +28,6 @@ class SequenceEraseKernel : public framework::OpKernel { auto lod = in->lod(); PADDLE_ENFORCE_EQ(lod.size(), 1UL, "Only support one level sequence now."); - // auto dims = x->dims(); - /* - const size_t level = lod.size() - 1; - PADDLE_ENFORCE_EQ(dims[0], static_cast(lod[level].back()), - "The first dimension of Input(X) should be equal to the " - "sum of all sequences' lengths."); - PADDLE_ENFORCE_EQ(dims[0], x->numel(), - "The width of each timestep in Input(X) of " - "SequenceEraseOp should be 1."); - out->mutable_data(ctx.GetPlace()); - */ auto tokens = ctx.Attr>("tokens"); auto in_len = in->numel(); auto in_dat = in->data(); @@ -65,7 +50,7 @@ class SequenceEraseKernel : public framework::OpKernel { out->Resize({static_cast(out_len), 1}); auto out_dat = out->mutable_data(ctx.GetPlace()); - for (size_t i = 0; i < in_len; ++i) { + for (int64_t i = 0; i < in_len; ++i) { if (num_erased[i] == num_erased[i + 1]) { out_dat[i - num_erased[i]] = in_dat[i]; } diff --git a/python/paddle/v2/fluid/tests/test_sequence_erase_op.py b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py index e730f2f4b78..78105334f5f 100644 --- a/python/paddle/v2/fluid/tests/test_sequence_erase_op.py +++ b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py @@ -28,9 +28,9 @@ def sequence_erase(in_seq, lod0, tokens): class TestSequenceEraseOp(OpTest): def setUp(self): self.op_type = "sequence_erase" - in_seq = np.random.randint(0, 10, (30, 1)).astype("int32") - lod = [[0, 5, 15, 30]] - tokens = [2, 5] + in_seq = np.random.randint(0, 10, (10, 1)).astype("int32") + lod = [[0, 3, 6, 10]] + tokens = [2, 3, 5] out_seq, new_lod0 = sequence_erase(in_seq, lod[0], tokens) self.attrs = {'tokens': tokens} self.inputs = {'X': (in_seq, lod)} -- GitLab From 452fe1e1be8d947a213295f85425de6b7c127096 Mon Sep 17 00:00:00 2001 From: Xi Chen Date: Mon, 8 Jan 2018 15:35:16 -0800 Subject: [PATCH 0143/2305] init checkin for distributed book chapter 6 --- .../test_understand_sentiment_conv_dist.py | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py b/python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py new file mode 100644 index 00000000000..0f0d84be0a6 --- /dev/null +++ b/python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py @@ -0,0 +1,109 @@ +from __future__ import print_function +import numpy as np +import paddle.v2 as paddle +import paddle.v2.fluid as fluid + + +def convolution_net(data, label, input_dim, class_dim=2, emb_dim=32, + hid_dim=32): + emb = fluid.layers.embedding(input=data, size=[input_dim, emb_dim]) + conv_3 = fluid.nets.sequence_conv_pool( + input=emb, + num_filters=hid_dim, + filter_size=3, + act="tanh", + pool_type="sqrt") + conv_4 = fluid.nets.sequence_conv_pool( + input=emb, + num_filters=hid_dim, + filter_size=4, + act="tanh", + pool_type="sqrt") + prediction = fluid.layers.fc(input=[conv_3, conv_4], + size=class_dim, + act="softmax") + cost = fluid.layers.cross_entropy(input=prediction, label=label) + avg_cost = fluid.layers.mean(x=cost) + adam_optimizer = fluid.optimizer.Adam(learning_rate=0.002) + optimize_ops, params_grads = adam_optimizer.minimize(avg_cost) + accuracy = fluid.evaluator.Accuracy(input=prediction, label=label) + return avg_cost, accuracy, accuracy.metrics[0], optimize_ops, params_grads + + +def to_lodtensor(data, place): + seq_lens = [len(seq) for seq in data] + cur_len = 0 + lod = [cur_len] + for l in seq_lens: + cur_len += l + lod.append(cur_len) + flattened_data = np.concatenate(data, axis=0).astype("int64") + flattened_data = flattened_data.reshape([len(flattened_data), 1]) + res = fluid.LoDTensor() + res.set(flattened_data, place) + res.set_lod([lod]) + return res + + +def main(): + BATCH_SIZE = 100 + PASS_NUM = 5 + + word_dict = paddle.dataset.imdb.word_dict() + dict_dim = len(word_dict) + class_dim = 2 + + data = fluid.layers.data( + name="words", shape=[1], dtype="int64", lod_level=1) + label = fluid.layers.data(name="label", shape=[1], dtype="int64") + cost, accuracy, acc_out, optimize_ops, params_grads = convolution_net( + data, label, input_dim=dict_dim, class_dim=class_dim) + + train_data = paddle.batch( + paddle.reader.shuffle( + paddle.dataset.imdb.train(word_dict), buf_size=1000), + batch_size=BATCH_SIZE) + place = fluid.CPUPlace() + exe = fluid.Executor(place) + + t = fluid.DistributeTranspiler() + + # all parameter server endpoints list for spliting parameters + pserver_endpoints = os.getenv("PSERVERS") + # server endpoint for current node + current_endpoint = os.getenv("SERVER_ENDPOINT") + # run as trainer or parameter server + training_role = os.getenv( + "TRAINING_ROLE", "TRAINER") # get the training role: trainer/pserver + t.transpile( + optimize_ops, params_grads, pservers=pserver_endpoints, trainers=2) + + exe.run(fluid.default_startup_program()) + + if training_role == "PSERVER": + if not current_endpoint: + print("need env SERVER_ENDPOINT") + exit(1) + pserver_prog = t.get_pserver_program(current_endpoint, optimize_ops) + exe.run(pserver_prog) + elif training_role == "TRAINER": + trainer_prog = t.get_trainer_program() + feeder = fluid.DataFeeder(feed_list=[data, label], place=place) + + for pass_id in xrange(PASS_NUM): + accuracy.reset(exe) + for data in train_data(): + cost_val, acc_val = exe.run(trainer_prog, + feed=feeder.feed(data), + fetch_list=[cost, acc_out]) + pass_acc = accuracy.eval(exe) + print("cost=" + str(cost_val) + " acc=" + str(acc_val) + + " pass_acc=" + str(pass_acc)) + if cost_val < 1.0 and pass_acc > 0.8: + exit(0) + else: + print("environment var TRAINER_ROLE should be TRAINER os PSERVER") + + +if __name__ == '__main__': + main() -- GitLab From f59ad3cee4d4baa9314384fbcada2d28020f5c38 Mon Sep 17 00:00:00 2001 From: ranqiu Date: Tue, 9 Jan 2018 10:20:28 +0800 Subject: [PATCH 0144/2305] Add copyright notice to plotlog.py --- benchmark/paddle/image/plotlog.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/benchmark/paddle/image/plotlog.py b/benchmark/paddle/image/plotlog.py index 9ac78d69101..ce9d6ac24ad 100644 --- a/benchmark/paddle/image/plotlog.py +++ b/benchmark/paddle/image/plotlog.py @@ -1,4 +1,16 @@ -#coding=utf-8 +# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. import sys import argparse -- GitLab From 1dad4bb2e9dbe791a03e1052e2a5e3df5d790c84 Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Tue, 9 Jan 2018 10:46:36 +0800 Subject: [PATCH 0145/2305] Remove unused LoDTensor methods (#7247) * Remove unused LoDTensor methods * Update --- paddle/framework/lod_tensor.cc | 49 ---------------- paddle/framework/lod_tensor.h | 37 ------------- paddle/framework/lod_tensor_test.cc | 86 ----------------------------- 3 files changed, 172 deletions(-) diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index ef85ed69dbe..5234b74daba 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -59,18 +59,6 @@ std::ostream &operator<<(std::ostream &os, const LoDTensor &t) { return os; } -LoD SliceLevels(const LoD &in, size_t level_begin, size_t level_end) { - LoD new_lod; - new_lod.reserve(level_end - level_begin); - for (size_t i = level_begin; i < level_end; i++) { - new_lod.emplace_back(in.at(i)); - } - // transform the lowest level to absolute offset. - LoD abs_offset_lod = ToAbsOffset(in); - new_lod.back() = abs_offset_lod[level_end - 1]; - return new_lod; -} - LoD SliceInLevel(const LoD &in, size_t level, size_t elem_begin, size_t elem_end) { PADDLE_ENFORCE_LT(level, in.size()); @@ -131,43 +119,6 @@ bool operator==(const LoD &a, const LoD &b) { return true; } -size_t LoDTensor::NumElements(size_t level, size_t idx) const { - PADDLE_ENFORCE_LT(level, NumLevels()); - PADDLE_ENFORCE_LT(idx, NumElements(level)); - return lod_[level][idx + 1] - lod_[level][idx]; -} - -size_t LoDTensor::NumInstancesInElement(size_t level, size_t idx) const { - PADDLE_ENFORCE_LT(level, NumLevels()); - PADDLE_ENFORCE_LT(idx, NumElements(level)); - auto abs_lod = ToAbsOffset(lod()); - size_t begin = abs_lod[level][idx]; - size_t end = abs_lod[level][idx + 1]; - return end - begin; -} - -void LoDTensor::ShrinkLevels(size_t level_begin, size_t level_end) { - auto new_lod = framework::SliceLevels(lod_, level_begin, level_end); - lod_ = new_lod; -} - -void LoDTensor::ShrinkInLevel(size_t level, size_t elem_begin, - size_t elem_end) { - PADDLE_ENFORCE_LT(level, NumLevels()); - PADDLE_ENFORCE_LT(elem_begin, NumElements(level)); - PADDLE_ENFORCE_LT(elem_end, NumElements(level) + 1); - - auto abs_lod = framework::ToAbsOffset(lod()); - auto new_lod = framework::SliceInLevel(lod_, level, elem_begin, elem_end); - lod_ = new_lod; - - // slice the underlying tensor - size_t begin = abs_lod[level][elem_begin]; - size_t end = abs_lod[level][elem_end]; - PADDLE_ENFORCE_LT(begin, end, "Cannot shrink, the result tensor is empty."); - ShareDataWith(Slice(begin, end)); -} - using LoDAndOffset = std::pair>; LoDAndOffset GetSubLoDAndAbsoluteOffset(const LoD &lod, size_t start_idx, size_t end_idx, size_t start_level) { diff --git a/paddle/framework/lod_tensor.h b/paddle/framework/lod_tensor.h index b27936c1986..4ec72428aa8 100644 --- a/paddle/framework/lod_tensor.h +++ b/paddle/framework/lod_tensor.h @@ -60,14 +60,6 @@ using LoD = std::vector>; std::ostream& operator<<(std::ostream& os, const LoD& lod); std::ostream& operator<<(std::ostream& os, const LoDTensor& t); -/* - * Slice levels from a LoD. - * NOTE the lowest level should always be the absolute offsets of the underlying - * tensor instances. So if higher layers are sliced without the lowest level, - * the lower level of the sliced LoD will be transformed to the absolute offset. - */ -LoD SliceLevels(const LoD& in, size_t level_begin, size_t level_end); - LoD SliceInLevel(const LoD& in, size_t level, size_t elem_begin, size_t elem_end); /* @@ -116,35 +108,6 @@ class LoDTensor : public Tensor { return (lod_)[level].size() - 1; } - /* - * Number of lower-level elements. - * For example, a 2-level lod-tensor - * - * 0-th level | | - * 1-th level || ||| - * - * NumElements(0, 0) get 2 - * NumElements(0, 1) get 3 - */ - size_t NumElements(size_t level, size_t idx) const; - - /* - * Get the number of instances in the underlying tensor in the `idx`-th - * element. - */ - size_t NumInstancesInElement(size_t level, size_t idx) const; - - /* - * Shrink levels[level_begin:level_end] - */ - void ShrinkLevels(size_t level_begin, size_t level_end); - - /* - * Shrink elements of a level, [elem_begin: elem_end] - * @note: low performance in slice lod_. - */ - void ShrinkInLevel(size_t level, size_t elem_begin, size_t elem_end); - std::vector SplitLoDTensor( const std::vector places) const; diff --git a/paddle/framework/lod_tensor_test.cc b/paddle/framework/lod_tensor_test.cc index 0868c1f6e69..52b87f48e53 100644 --- a/paddle/framework/lod_tensor_test.cc +++ b/paddle/framework/lod_tensor_test.cc @@ -54,92 +54,6 @@ class LoDTensorTester : public ::testing::Test { LoDTensor lod_tensor_; }; -TEST_F(LoDTensorTester, NumLevels) { ASSERT_EQ(lod_tensor_.NumLevels(), 3UL); } - -TEST_F(LoDTensorTester, NumElements) { - ASSERT_EQ(lod_tensor_.NumElements(0), 2UL); - ASSERT_EQ(lod_tensor_.NumElements(1), 3UL); - ASSERT_EQ(lod_tensor_.NumElements(2), 8UL); -} - -TEST_F(LoDTensorTester, NumElements2) { - ASSERT_EQ(lod_tensor_.NumElements(0, 0), 2UL); - ASSERT_EQ(lod_tensor_.NumElements(0, 1), 1UL); - ASSERT_EQ(lod_tensor_.NumElements(1, 1), 3UL); -} - -TEST_F(LoDTensorTester, ShrinkLevels) { - // slice 1 level - for (size_t level = 0; level < 3UL; ++level) { - LoDTensor new_lod_tensor = lod_tensor_; - new_lod_tensor.ShrinkLevels(level, level + 1); - ASSERT_EQ(new_lod_tensor.NumLevels(), 1UL); - ASSERT_EQ(new_lod_tensor.data(), lod_tensor_.data()); - } - // shrink 2 level - for (size_t level = 0; level < 2UL; ++level) { - LoDTensor new_lod_tensor = lod_tensor_; - new_lod_tensor.ShrinkLevels(level, level + 2); - // the lowest level's last element should be the tensor's batch_size. - ASSERT_EQ(new_lod_tensor.lod().back().back(), - lod_tensor_.lod().back().back()); - ASSERT_EQ(new_lod_tensor.NumLevels(), 2UL); - ASSERT_EQ(new_lod_tensor.data(), lod_tensor_.data()); - } -} - -TEST_F(LoDTensorTester, ShrinkInLevel) { - size_t level = 0; - LoDTensor new_lod_tensor = lod_tensor_; - new_lod_tensor.ShrinkInLevel(level, 0, 1); - ASSERT_EQ(new_lod_tensor.NumLevels(), 3UL); - ASSERT_EQ(new_lod_tensor.NumElements(0), 1UL); - ASSERT_EQ(new_lod_tensor.NumElements(1), 2UL); - ASSERT_EQ(new_lod_tensor.NumElements(2), 5UL); - ASSERT_EQ(new_lod_tensor.dims()[0], 12); - for (int i = 0; i < 12 * 128; i++) { - ASSERT_EQ(new_lod_tensor.data()[i], i); - } - - level = 1; - new_lod_tensor = lod_tensor_; - new_lod_tensor.ShrinkInLevel(level, 1, 2); - ASSERT_EQ(new_lod_tensor.NumLevels(), 2UL); - ASSERT_EQ(new_lod_tensor.NumElements(0), 1UL); - ASSERT_EQ(new_lod_tensor.NumElements(1), 3UL); - ASSERT_EQ(new_lod_tensor.dims()[0], 7); - for (int i = 5 * 128; i < 12 * 128; i++) { - ASSERT_EQ(new_lod_tensor.data()[i - 5 * 128], i); - } - - LoDTensor t1; - t1.set_lod(lod_tensor_.lod()); - t1.ShareDataWith(lod_tensor_); - - LoDTensor t2; - t2.set_lod(lod_tensor_.lod()); - t2.ShareDataWith(lod_tensor_); - - t1.ShrinkInLevel(0, 1, 2); - t2.ShrinkInLevel(0, 0, 1); - EXPECT_NE(t1.data(), t2.data()); - EXPECT_NE(t1.data(), lod_tensor_.data()); -} - -TEST_F(LoDTensorTester, SerializeAndDeserialize) { - LoDTensor dst_tensor; - platform::CPUDeviceContext cpu_ctx((platform::CPUPlace())); - std::ostringstream oss; - SerializeToStream(oss, lod_tensor_, cpu_ctx); - std::istringstream iss(oss.str()); - DeserializeFromStream(iss, &dst_tensor, cpu_ctx); - float* dst_ptr = dst_tensor.mutable_data(platform::CPUPlace()); - for (int i = 0; i < kLodTensorSize; ++i) { - EXPECT_EQ(dst_ptr[i], i); - } - EXPECT_EQ(dst_tensor.lod(), lod_tensor_.lod()); -} - TEST(LodExpand, test) { LoD lod{{0, 2}}; LoDTensor tensor; -- GitLab From b3db8ef871d3e2cdf13b2583de44866dc6e1b5b8 Mon Sep 17 00:00:00 2001 From: peterzhang2029 Date: Tue, 9 Jan 2018 11:34:27 +0800 Subject: [PATCH 0146/2305] refine the description --- python/paddle/trainer_config_helpers/layers.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/python/paddle/trainer_config_helpers/layers.py b/python/paddle/trainer_config_helpers/layers.py index df4a630077b..eac2cb31683 100644 --- a/python/paddle/trainer_config_helpers/layers.py +++ b/python/paddle/trainer_config_helpers/layers.py @@ -2545,12 +2545,18 @@ def img_conv_layer(input, num_filters. There are several groups of filters in PaddlePaddle implementation. - Each group will process some channels of the input. For example, if - num_channel = 256, group = 4, num_filter=32, the PaddlePaddle will create - 32 filters to process the input. The input channels will be split into 4 - pieces. First 256/4 = 64 channels will be processed by first 32/4 = 8 filters. - The rest channels will be processed by the rest groups of filters. - + If the groups attribute is greater than 1, for example groups=2, + the input will be splitted into 2 parts along the channel axis, and + the filters will also be splitted into 2 parts. The first half of the filters + is only connected to the first half of the input channels, while the second + half of the filters is only connected to the second half of the input. After + the computation of convolution for each part of input, + the output will be obtained by concatenating the two results. + + The details of grouped convolution, please refer to: + `ImageNet Classification with Deep Convolutional Neural Networks + `_ + The example usage is: .. code-block:: python -- GitLab From bf1e03721bb84b7390231fe6c16e646edd7a5a76 Mon Sep 17 00:00:00 2001 From: gx_wind Date: Tue, 9 Jan 2018 12:58:44 +0800 Subject: [PATCH 0147/2305] delete comment --- adversarial/advbox/attacks/base.py | 6 +----- adversarial/advbox/attacks/gradientsign.py | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/adversarial/advbox/attacks/base.py b/adversarial/advbox/attacks/base.py index dab1dbbeb02..2058e3cfeb1 100644 --- a/adversarial/advbox/attacks/base.py +++ b/adversarial/advbox/attacks/base.py @@ -1,11 +1,7 @@ """ The base model of the model. """ -from abc import ABCMeta -#from advbox.base import Model -import abc - -abstractmethod = abc.abstractmethod +from abc import ABCMeta, abstractmethod class Attack(object): diff --git a/adversarial/advbox/attacks/gradientsign.py b/adversarial/advbox/attacks/gradientsign.py index 37fbdb11328..dff518811ef 100644 --- a/adversarial/advbox/attacks/gradientsign.py +++ b/adversarial/advbox/attacks/gradientsign.py @@ -30,9 +30,7 @@ class GradientSignAttack(Attack): gradient_sign.shape) + epsilon * gradient_sign adv_img = np.clip(adv_img, min_, max_) adv_label = np.argmax(self.model.predict([(adv_img, 0)])) - #print("pre_label="+str(pre_label)+ " adv_label="+str(adv_label)) if pre_label != adv_label: - #print(epsilon, pre_label, adv_label) return adv_img -- GitLab From ce6dad3b35c85d133e7c35249cced9df4dc0f05e Mon Sep 17 00:00:00 2001 From: Yu Yang Date: Tue, 9 Jan 2018 13:50:25 +0800 Subject: [PATCH 0148/2305] Rename CopyFrom to Copy for tensors (#7292) * Rename Tensor::CopyFrom to Tensor::Copy * Fix CI * Fix compile --- paddle/framework/data_transform.h | 2 +- paddle/framework/device_data_transform.cc | 2 +- .../framework/device_data_transform_test.cu | 4 +-- paddle/framework/lod_tensor.cc | 2 +- paddle/framework/lod_tensor.h | 4 +-- paddle/framework/tensor_util.cc | 2 +- paddle/framework/tensor_util.h | 14 ++++---- paddle/framework/tensor_util_test.cc | 20 +++++------ paddle/operators/array_operator.h | 2 +- paddle/operators/array_to_lod_tensor_op.cc | 4 +-- paddle/operators/assign_op.cc | 4 +-- paddle/operators/detection_output_op.h | 16 ++++----- paddle/operators/expand_op.h | 3 +- paddle/operators/feed_op.cc | 2 +- paddle/operators/fetch_op.cc | 2 +- paddle/operators/fill_op.cc | 2 +- paddle/operators/linear_chain_crf_op.h | 14 ++++---- paddle/operators/load_op.cc | 2 +- paddle/operators/lod_reset_op.h | 4 +-- paddle/operators/lod_tensor_to_array_op.cc | 6 ++-- paddle/operators/math/context_project.h | 4 +-- paddle/operators/math/im2col_test.cc | 14 ++++---- paddle/operators/math/math_function_test.cu | 36 +++++++++---------- .../math/selected_rows_functor_test.cu | 8 ++--- paddle/operators/math/vol2col_test.cc | 8 ++--- paddle/operators/merge_lod_tensor_op.cc | 6 ++-- paddle/operators/multiplex_op.cu | 4 +-- paddle/operators/parallel_do_op.cc | 4 +-- paddle/operators/recurrent_op.cc | 8 ++--- .../reorder_lod_tensor_by_rank_op.cc | 2 +- paddle/operators/reshape_op.h | 4 +-- paddle/operators/sequence_slice_op.h | 16 ++++----- paddle/operators/shrink_rnn_memory_op.cc | 2 +- paddle/operators/split_lod_tensor_op.cc | 8 ++--- paddle/operators/sum_op.h | 4 +-- .../operators/tensor_array_read_write_op.cc | 4 +-- 36 files changed, 121 insertions(+), 122 deletions(-) diff --git a/paddle/framework/data_transform.h b/paddle/framework/data_transform.h index 42fc5f4d7e8..e4e5c30a96a 100644 --- a/paddle/framework/data_transform.h +++ b/paddle/framework/data_transform.h @@ -88,7 +88,7 @@ struct CastDataType { trans(*context, in_begin, in_end, out_begin, CastDataTypeFunctor()); } else { - // TODO(dzhwinter): enhance CopyFrom CPU<->GPU with different data type? + // TODO(dzhwinter): enhance Copy CPU<->GPU with different data type? PADDLE_THROW("Unsupport CPU <-> GPU!"); } } diff --git a/paddle/framework/device_data_transform.cc b/paddle/framework/device_data_transform.cc index 4f9b7e96a28..cd5104cc6f2 100644 --- a/paddle/framework/device_data_transform.cc +++ b/paddle/framework/device_data_transform.cc @@ -37,7 +37,7 @@ Tensor* DeviceTransform(const Tensor& in, const platform::Place& dst_place) { Tensor* out = new Tensor(); auto* dev_ctx = GetDeviceContext(in.place(), dst_place); dev_ctx->Wait(); - CopyFrom(in, dst_place, *dev_ctx, out); + Copy(in, dst_place, *dev_ctx, out); dev_ctx->Wait(); return out; } diff --git a/paddle/framework/device_data_transform_test.cu b/paddle/framework/device_data_transform_test.cu index e9100053d52..9fb26f09c7e 100644 --- a/paddle/framework/device_data_transform_test.cu +++ b/paddle/framework/device_data_transform_test.cu @@ -157,8 +157,8 @@ TEST(Operator, CPUtoGPU) { auto dev_ctx = pool.Get(cuda_place); paddle::framework::Tensor output_tensor; - CopyFrom(output2->Get(), paddle::platform::CPUPlace(), *dev_ctx, - &output_tensor); + Copy(output2->Get(), paddle::platform::CPUPlace(), *dev_ctx, + &output_tensor); dev_ctx->Wait(); float* output2_ptr = output_tensor.data(); diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index 5234b74daba..506fde44053 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -232,7 +232,7 @@ std::vector LoDTensor::SplitLoDTensor( auto dst_ptr = dst.mutable_data(dst_place, src.type()); // TODO(tonyyang-svail): - // change the following to framework::CopyFrom + // change the following to framework::Copy auto src_place = src.place(); auto src_ptr = src.data(); auto size = src.numel() * SizeOfType(src.type()); diff --git a/paddle/framework/lod_tensor.h b/paddle/framework/lod_tensor.h index 4ec72428aa8..37753f5f4dd 100644 --- a/paddle/framework/lod_tensor.h +++ b/paddle/framework/lod_tensor.h @@ -147,8 +147,8 @@ LoDTensor LodExpand(const LoDTensor& source, const LoD& lod, size_t level, for (size_t ins = 0; ins < num_instances; ins++) { for (size_t elem = lod_level[ins]; elem < lod_level[ins + 1]; elem++) { auto slice = tensor.Slice(elem, elem + 1); - CopyFrom(source.Slice(ins, ins + 1), platform::CPUPlace(), - platform::CPUDeviceContext(), &slice); + Copy(source.Slice(ins, ins + 1), platform::CPUPlace(), + platform::CPUDeviceContext(), &slice); } } return tensor; diff --git a/paddle/framework/tensor_util.cc b/paddle/framework/tensor_util.cc index 7efc649d0bc..a5b83eaa07a 100644 --- a/paddle/framework/tensor_util.cc +++ b/paddle/framework/tensor_util.cc @@ -69,7 +69,7 @@ struct AnyVisitor : public boost::static_visitor { tmp.mutable_data(cpu); auto gpuctx = platform::DeviceContextPool::Instance().Get(gpu); gpuctx->Wait(); - CopyFrom(out, cpu, *gpuctx, &tmp); + Copy(out, cpu, *gpuctx, &tmp); gpuctx->Wait(); return GetResult(tmp, cpu); } diff --git a/paddle/framework/tensor_util.h b/paddle/framework/tensor_util.h index 5ac13cba4da..7c56ccf17f9 100644 --- a/paddle/framework/tensor_util.h +++ b/paddle/framework/tensor_util.h @@ -29,11 +29,11 @@ namespace framework { * @param[in] dst_place The dst place. * @param[in] ctx The device context contains device resources. * - * @note CopyFrom supports CPU <-> GPU, GPU <-> GPU. + * @note Copy supports CPU <-> GPU, GPU <-> GPU. */ -inline void CopyFrom(const Tensor& src, const platform::Place& dst_place, - const platform::DeviceContext& ctx, Tensor* dst) { +inline void Copy(const Tensor& src, const platform::Place& dst_place, + const platform::DeviceContext& ctx, Tensor* dst) { src.check_memory_size(); dst->Resize(src.dims()); @@ -88,10 +88,10 @@ inline void CopyFrom(const Tensor& src, const platform::Place& dst_place, } /** - * @brief CopyFrom support CPU <-> CPU + * @brief Copy supports CPU <-> CPU */ -inline void CopyFrom(const Tensor& src, const platform::Place& dst_place, - Tensor* dst) { +inline void Copy(const Tensor& src, const platform::Place& dst_place, + Tensor* dst) { src.check_memory_size(); dst->Resize(src.dims()); dst->set_layout(src.layout()); @@ -316,7 +316,7 @@ inline void DeserializeFromStream(std::istream& is, Tensor* tensor, DeserializedDataFunctor(&buf, &cpu_tensor, ctx.GetPlace())); is.read(static_cast(buf), cpu_tensor.memory_size()); auto cpu_place = new platform::CPUPlace(); - framework::CopyFrom(cpu_tensor, *cpu_place, dev_ctx, tensor); + framework::Copy(cpu_tensor, *cpu_place, dev_ctx, tensor); delete cpu_place; #else PADDLE_THROW("Unexpected branch"); diff --git a/paddle/framework/tensor_util_test.cc b/paddle/framework/tensor_util_test.cc index 15cd2bd09c4..3636125f205 100644 --- a/paddle/framework/tensor_util_test.cc +++ b/paddle/framework/tensor_util_test.cc @@ -19,7 +19,7 @@ namespace paddle { namespace framework { -TEST(CopyFrom, Tensor) { +TEST(Copy, Tensor) { Tensor src_tensor; Tensor dst_tensor; platform::CPUDeviceContext cpu_ctx((platform::CPUPlace())); @@ -32,7 +32,7 @@ TEST(CopyFrom, Tensor) { src_tensor.set_layout(DataLayout::kAnyLayout); auto cpu_place = new platform::CPUPlace(); - CopyFrom(src_tensor, *cpu_place, &dst_tensor); + Copy(src_tensor, *cpu_place, &dst_tensor); const int* dst_ptr = dst_tensor.data(); ASSERT_NE(src_ptr, dst_ptr); @@ -43,7 +43,7 @@ TEST(CopyFrom, Tensor) { EXPECT_TRUE(dst_tensor.layout() == src_tensor.layout()); Tensor slice_tensor = src_tensor.Slice(1, 2); - CopyFrom(slice_tensor, *cpu_place, &dst_tensor); + Copy(slice_tensor, *cpu_place, &dst_tensor); const int* slice_ptr = slice_tensor.data(); dst_ptr = dst_tensor.data(); ASSERT_NE(dst_ptr, slice_ptr); @@ -67,11 +67,11 @@ TEST(CopyFrom, Tensor) { // CPU Tensor to GPU Tensor auto gpu_place = new platform::CUDAPlace(0); platform::CUDADeviceContext gpu_ctx(*gpu_place); - CopyFrom(src_tensor, *gpu_place, gpu_ctx, &gpu_tensor); + Copy(src_tensor, *gpu_place, gpu_ctx, &gpu_tensor); // GPU Tensor to CPU Tensor auto cpu_place = new platform::CPUPlace(); - CopyFrom(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor); + Copy(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor); // Sync before Compare Tensors gpu_ctx.Wait(); @@ -84,10 +84,10 @@ TEST(CopyFrom, Tensor) { Tensor slice_tensor = src_tensor.Slice(1, 2); // CPU Slice Tensor to GPU Tensor - CopyFrom(slice_tensor, *gpu_place, gpu_ctx, &gpu_tensor); + Copy(slice_tensor, *gpu_place, gpu_ctx, &gpu_tensor); // GPU Tensor to CPU Tensor - CopyFrom(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor); + Copy(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor); // Sync before Compare Slice Tensors gpu_ctx.Wait(); @@ -155,7 +155,7 @@ TEST(CopyFromVector, Tensor) { CUDADeviceContext gpu_ctx(*gpu_place); CopyFromVector(src_vec, gpu_ctx, &gpu_tensor); // Copy from GPU to CPU tensor for comparison - CopyFrom(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor); + Copy(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor); // Sync before Compare Tensors gpu_ctx.Wait(); @@ -175,7 +175,7 @@ TEST(CopyFromVector, Tensor) { CopyFromVector(src_vec, cpu_ctx, &cpu_tensor); gpu_tensor.Resize(make_ddim({2, 2})); CopyFromVector(src_vec, gpu_ctx, &gpu_tensor); - CopyFrom(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor); + Copy(gpu_tensor, *cpu_place, gpu_ctx, &dst_tensor); // Sync before Compare Tensors gpu_ctx.Wait(); @@ -287,7 +287,7 @@ TEST(Tensor, SerializeAndDeserialize) { auto gpu_place = new platform::CUDAPlace(); platform::CUDADeviceContext gpu_ctx(*gpu_place); - CopyFrom(src_tensor, *gpu_place, gpu_ctx, &gpu_tensor); + Copy(src_tensor, *gpu_place, gpu_ctx, &gpu_tensor); std::ostringstream oss; SerializeToStream(oss, gpu_tensor, gpu_ctx); diff --git a/paddle/operators/array_operator.h b/paddle/operators/array_operator.h index e0eef5d9f93..3fdad5ad9b1 100644 --- a/paddle/operators/array_operator.h +++ b/paddle/operators/array_operator.h @@ -42,7 +42,7 @@ class ArrayOp : public framework::OperatorBase { if (platform::is_gpu_place(i_tensor.place())) { // FIXME: Avoid copy from GPU to CPU framework::Tensor t; - framework::CopyFrom(i_tensor, platform::CPUPlace(), dev_ctx, &t); + framework::Copy(i_tensor, platform::CPUPlace(), dev_ctx, &t); dev_ctx.Wait(); offset = static_cast(*t.data()); } else { diff --git a/paddle/operators/array_to_lod_tensor_op.cc b/paddle/operators/array_to_lod_tensor_op.cc index 49366fee8df..ba5c6bd3c68 100644 --- a/paddle/operators/array_to_lod_tensor_op.cc +++ b/paddle/operators/array_to_lod_tensor_op.cc @@ -110,8 +110,8 @@ class ArrayToLoDTensorOp : public framework::OperatorBase { platform::DeviceContextPool::Instance(); auto &dev_ctx = *pool.Get(place); - framework::CopyFrom(x[x_idx].Slice(start_offset, end_offset), place, - dev_ctx, &slice); + framework::Copy(x[x_idx].Slice(start_offset, end_offset), place, + dev_ctx, &slice); out_offset += len; } } diff --git a/paddle/operators/assign_op.cc b/paddle/operators/assign_op.cc index 7d77be3be10..e04aa2d28cf 100644 --- a/paddle/operators/assign_op.cc +++ b/paddle/operators/assign_op.cc @@ -45,7 +45,7 @@ class AssignFunctor { out_rows.set_height(rows.height()); auto &t = rows.value(); auto *m = out_rows.mutable_value(); - framework::CopyFrom(t, t.place(), dev_ctx_, m); + framework::Copy(t, t.place(), dev_ctx_, m); } template @@ -57,7 +57,7 @@ class AssignFunctor { void copy_tensor(const framework::LoDTensor &lod_tensor, framework::LoDTensor *out) const { auto &out_tensor = *out; - CopyFrom(lod_tensor, lod_tensor.place(), dev_ctx_, &out_tensor); + Copy(lod_tensor, lod_tensor.place(), dev_ctx_, &out_tensor); out_tensor.set_lod(lod_tensor.lod()); } diff --git a/paddle/operators/detection_output_op.h b/paddle/operators/detection_output_op.h index f8abd5b6406..86285b748a7 100644 --- a/paddle/operators/detection_output_op.h +++ b/paddle/operators/detection_output_op.h @@ -98,16 +98,16 @@ class DetectionOutputKernel : public framework::OpKernel { T* conf_data = conf_tensor.data(); if (platform::is_gpu_place(context.GetPlace())) { loc_cpu.mutable_data(loc_tensor.dims(), platform::CPUPlace()); - framework::CopyFrom(loc_tensor, platform::CPUPlace(), - context.device_context(), &loc_cpu); + framework::Copy(loc_tensor, platform::CPUPlace(), + context.device_context(), &loc_cpu); loc_data = loc_cpu.data(); conf_cpu.mutable_data(conf_tensor.dims(), platform::CPUPlace()); - framework::CopyFrom(conf_tensor, platform::CPUPlace(), - context.device_context(), &conf_cpu); + framework::Copy(conf_tensor, platform::CPUPlace(), + context.device_context(), &conf_cpu); conf_data = conf_cpu.data(); priorbox_cpu.mutable_data(in_priorbox->dims(), platform::CPUPlace()); - framework::CopyFrom(*in_priorbox, platform::CPUPlace(), - context.device_context(), &priorbox_cpu); + framework::Copy(*in_priorbox, platform::CPUPlace(), + context.device_context(), &priorbox_cpu); priorbox_data = priorbox_cpu.data(); } // get decode bboxes @@ -158,8 +158,8 @@ class DetectionOutputKernel : public framework::OpKernel { batch_size, all_indices, all_decoded_bboxes, out_data); if (platform::is_gpu_place(context.GetPlace())) { - framework::CopyFrom(out_cpu, platform::CUDAPlace(), - context.device_context(), out); + framework::Copy(out_cpu, platform::CUDAPlace(), context.device_context(), + out); } } }; diff --git a/paddle/operators/expand_op.h b/paddle/operators/expand_op.h index 1d9012cd4a4..a4994cf3a5b 100644 --- a/paddle/operators/expand_op.h +++ b/paddle/operators/expand_op.h @@ -126,8 +126,7 @@ class ExpandGradKernel : public framework::OpKernel { auto* in0 = context.Input(framework::GradVarName("Out")); auto* out0 = context.Output(framework::GradVarName("X")); out0->mutable_data(context.GetPlace()); - framework::CopyFrom(*in0, context.GetPlace(), context.device_context(), - out0); + framework::Copy(*in0, context.GetPlace(), context.device_context(), out0); } else { switch (dims) { REP_EXPAND_GRAD_TEMPLATE(72) diff --git a/paddle/operators/feed_op.cc b/paddle/operators/feed_op.cc index 48da52c3b68..d738e1850ca 100644 --- a/paddle/operators/feed_op.cc +++ b/paddle/operators/feed_op.cc @@ -52,7 +52,7 @@ class FeedOp : public framework::OperatorBase { platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); auto &dev_ctx = *pool.Get(place); - framework::CopyFrom(feed_item, place, dev_ctx, out_item); + framework::Copy(feed_item, place, dev_ctx, out_item); out_item->set_lod(feed_item.lod()); } }; diff --git a/paddle/operators/fetch_op.cc b/paddle/operators/fetch_op.cc index 48c01f984f8..7205ee2a879 100644 --- a/paddle/operators/fetch_op.cc +++ b/paddle/operators/fetch_op.cc @@ -55,7 +55,7 @@ class FetchOp : public framework::OperatorBase { platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); auto &dev_ctx = *pool.Get(src_item.place()); - CopyFrom(src_item, platform::CPUPlace(), dev_ctx, &dst_item); + Copy(src_item, platform::CPUPlace(), dev_ctx, &dst_item); dev_ctx.Wait(); dst_item.set_lod(src_item.lod()); diff --git a/paddle/operators/fill_op.cc b/paddle/operators/fill_op.cc index 084ba1db62d..4f5a2ed1695 100644 --- a/paddle/operators/fill_op.cc +++ b/paddle/operators/fill_op.cc @@ -72,7 +72,7 @@ class FillOp : public framework::OperatorBase { platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); auto &dev_ctx = *pool.Get(place); - framework::CopyFrom(tensor, place, dev_ctx, &out); + framework::Copy(tensor, place, dev_ctx, &out); } } }; diff --git a/paddle/operators/linear_chain_crf_op.h b/paddle/operators/linear_chain_crf_op.h index 19c6715ec87..f502ebefde1 100644 --- a/paddle/operators/linear_chain_crf_op.h +++ b/paddle/operators/linear_chain_crf_op.h @@ -196,7 +196,7 @@ class LinearChainCRFOpKernel : public framework::OpKernel { auto copyLoDTensor = [](const platform::DeviceContext& ctx, const LoDTensor& src, LoDTensor* dst) { dst->mutable_data(src.dims(), platform::CPUPlace()); - framework::CopyFrom(src, platform::CPUPlace(), ctx, dst); + framework::Copy(src, platform::CPUPlace(), ctx, dst); }; copyLoDTensor(ctx, emission_weights_src, emission_weights_dst); @@ -204,8 +204,8 @@ class LinearChainCRFOpKernel : public framework::OpKernel { transition_weights_dst->mutable_data(transition_weights_src.dims(), platform::CPUPlace()); - framework::CopyFrom(transition_weights_src, platform::CPUPlace(), ctx, - transition_weights_dst); + framework::Copy(transition_weights_src, platform::CPUPlace(), ctx, + transition_weights_dst); } void CopyOutputsToGpuMemory(const platform::DeviceContext& ctx, @@ -220,7 +220,7 @@ class LinearChainCRFOpKernel : public framework::OpKernel { auto copyTensor = [](const platform::DeviceContext& ctx, const Tensor& src, Tensor* dst) { dst->mutable_data(platform::CUDAPlace()); - framework::CopyFrom(src, platform::CUDAPlace(), ctx, dst); + framework::Copy(src, platform::CUDAPlace(), ctx, dst); }; copyTensor(ctx, emission_exps_src, emission_exps_dst); copyTensor(ctx, transition_exps_src, transition_exps_dst); @@ -410,12 +410,12 @@ class LinearChainCRFGradOpKernel : public framework::OpKernel { // Copy the inputs from GPU memory to CPU memory when this operators runs on // GPU device. label_dst->mutable_data(label_src.dims(), platform::CPUPlace()); - framework::CopyFrom(label_src, platform::CPUPlace(), ctx, label_dst); + framework::Copy(label_src, platform::CPUPlace(), ctx, label_dst); auto copyTensor = [](const platform::DeviceContext& ctx, const Tensor& src, Tensor* dst) { dst->mutable_data(src.dims(), platform::CPUPlace()); - framework::CopyFrom(src, platform::CPUPlace(), ctx, dst); + framework::Copy(src, platform::CPUPlace(), ctx, dst); }; copyTensor(ctx, emission_exps_src, emission_exps_dst); copyTensor(ctx, transition_exps_src, transition_exps_dst); @@ -434,7 +434,7 @@ class LinearChainCRFGradOpKernel : public framework::OpKernel { Tensor* dst) { if (src && dst) { dst->mutable_data(platform::CUDAPlace()); - framework::CopyFrom(*src, platform::CUDAPlace(), ctx, dst); + framework::Copy(*src, platform::CUDAPlace(), ctx, dst); } }; copyTensor(ctx, emission_grad_src, emission_grad_dst); diff --git a/paddle/operators/load_op.cc b/paddle/operators/load_op.cc index 7f551f101f3..f886b423ac7 100644 --- a/paddle/operators/load_op.cc +++ b/paddle/operators/load_op.cc @@ -53,7 +53,7 @@ class LoadOp : public framework::OperatorBase { out_var->Clear(); tensor = out_var->GetMutable(); tensor->set_lod(cpu_tensor.lod()); - CopyFrom(cpu_tensor, place, dev_ctx, tensor); + Copy(cpu_tensor, place, dev_ctx, tensor); } } }; diff --git a/paddle/operators/lod_reset_op.h b/paddle/operators/lod_reset_op.h index 306373fb1fb..c1bbba7a83a 100644 --- a/paddle/operators/lod_reset_op.h +++ b/paddle/operators/lod_reset_op.h @@ -33,8 +33,8 @@ class LoDResetKernel : public framework::OpKernel { auto* lod = lod_t->data(); if (platform::is_gpu_place(ctx.GetPlace())) { framework::Tensor lod_cpu; - framework::CopyFrom(*lod_t, platform::CPUPlace(), ctx.device_context(), - &lod_cpu); + framework::Copy(*lod_t, platform::CPUPlace(), ctx.device_context(), + &lod_cpu); lod = lod_cpu.data(); } level0 = std::vector(lod, lod + lod_t->numel()); diff --git a/paddle/operators/lod_tensor_to_array_op.cc b/paddle/operators/lod_tensor_to_array_op.cc index 8d164b4abc5..685a807a8ac 100644 --- a/paddle/operators/lod_tensor_to_array_op.cc +++ b/paddle/operators/lod_tensor_to_array_op.cc @@ -92,9 +92,9 @@ class LoDTensorToArrayOp : public framework::OperatorBase { platform::DeviceContextPool::Instance(); auto &dev_ctx = *pool.Get(place); - framework::CopyFrom(x.Slice(static_cast(each_range.begin), - static_cast(each_range.end)), - x.place(), dev_ctx, &slice); + framework::Copy(x.Slice(static_cast(each_range.begin), + static_cast(each_range.end)), + x.place(), dev_ctx, &slice); offset += len; } } diff --git a/paddle/operators/math/context_project.h b/paddle/operators/math/context_project.h index 4036614086e..218de9fb956 100644 --- a/paddle/operators/math/context_project.h +++ b/paddle/operators/math/context_project.h @@ -149,7 +149,7 @@ class ContextProjectFunctor { Tensor out_t_sub = out_t.Slice(k * context_length, k * context_length + padding_size); Tensor w_sub = padding_data.Slice(k, k + padding_size); - framework::CopyFrom(w_sub, context.GetPlace(), context, &out_t_sub); + framework::Copy(w_sub, context.GetPlace(), context, &out_t_sub); } } if (down_pad > 0) { // add down pad @@ -179,7 +179,7 @@ class ContextProjectFunctor { (down_pad_begin_row + t) * context_length); Tensor w_sub = padding_data.Slice( up_pad + padding_idx, up_pad + padding_idx + padding_size); - framework::CopyFrom(w_sub, context.GetPlace(), context, &out_t_sub); + framework::Copy(w_sub, context.GetPlace(), context, &out_t_sub); } } out_t.Resize({sequence_height, context_length * sequence_width}); diff --git a/paddle/operators/math/im2col_test.cc b/paddle/operators/math/im2col_test.cc index 26c038e4358..a0c02817fd8 100644 --- a/paddle/operators/math/im2col_test.cc +++ b/paddle/operators/math/im2col_test.cc @@ -63,7 +63,7 @@ void testIm2col() { if (paddle::platform::is_cpu_place(*place)) { input = input_tmp; } else { - CopyFrom(input_tmp, *place, *context, &input); + Copy(input_tmp, *place, *context, &input); } output_cfo.mutable_data( {1, filter_size, filter_size, output_height, output_width}, *place); @@ -88,7 +88,7 @@ void testIm2col() { if (paddle::platform::is_cpu_place(*place)) { out_cfo_ptr = output_cfo.data(); } else { - CopyFrom(output_cfo, paddle::platform::CPUPlace(), *context, &output_tmp); + Copy(output_cfo, paddle::platform::CPUPlace(), *context, &output_tmp); out_cfo_ptr = output_tmp.data(); } for (int i = 0; i < 6; ++i) { @@ -99,7 +99,7 @@ void testIm2col() { if (paddle::platform::is_cpu_place(*place)) { out_ocf_ptr = output_ocf.data(); } else { - CopyFrom(output_ocf, paddle::platform::CPUPlace(), *context, &output_tmp); + Copy(output_ocf, paddle::platform::CPUPlace(), *context, &output_tmp); out_ocf_ptr = output_tmp.data(); } for (int i = 0; i < 6; ++i) { @@ -119,7 +119,7 @@ void testIm2col() { if (paddle::platform::is_cpu_place(*place)) { input = input_tmp; } else { - CopyFrom(input_tmp, *place, *context, &input); + Copy(input_tmp, *place, *context, &input); } col2im(*context, output_cfo, dilation, stride, padding, &input); @@ -128,7 +128,7 @@ void testIm2col() { if (paddle::platform::is_cpu_place(*place)) { in_ptr = input.data(); } else { - CopyFrom(input, paddle::platform::CPUPlace(), *context, &input_tmp); + Copy(input, paddle::platform::CPUPlace(), *context, &input_tmp); in_ptr = input_tmp.data(); } for (int i = 0; i < 6; ++i) { @@ -140,7 +140,7 @@ void testIm2col() { if (paddle::platform::is_cpu_place(*place)) { input = input_tmp; } else { - CopyFrom(input_tmp, *place, *context, &input); + Copy(input_tmp, *place, *context, &input); } col2im_ocf(*context, output_ocf, dilation, stride, padding, &input); @@ -148,7 +148,7 @@ void testIm2col() { if (paddle::platform::is_cpu_place(*place)) { in_ptr = input.data(); } else { - CopyFrom(input, paddle::platform::CPUPlace(), *context, &input_tmp); + Copy(input, paddle::platform::CPUPlace(), *context, &input_tmp); in_ptr = input_tmp.data(); } for (int i = 0; i < 6; ++i) { diff --git a/paddle/operators/math/math_function_test.cu b/paddle/operators/math/math_function_test.cu index 4325a79664f..d1139ac988c 100644 --- a/paddle/operators/math/math_function_test.cu +++ b/paddle/operators/math/math_function_test.cu @@ -16,15 +16,15 @@ TEST(math_function, notrans_mul_trans) { auto* gpu_place = new paddle::platform::CUDAPlace(0); paddle::platform::CUDADeviceContext context(*gpu_place); - paddle::framework::CopyFrom(input1, *gpu_place, context, &input1_gpu); - paddle::framework::CopyFrom(input1, *gpu_place, context, &input2_gpu); + paddle::framework::Copy(input1, *gpu_place, context, &input1_gpu); + paddle::framework::Copy(input1, *gpu_place, context, &input2_gpu); out_gpu.mutable_data({2, 2}, *gpu_place); paddle::operators::math::matmul( context, input1_gpu, false, input2_gpu, true, 1, &out_gpu, 0); - paddle::framework::CopyFrom(out_gpu, *cpu_place, context, &out); + paddle::framework::Copy(out_gpu, *cpu_place, context, &out); float* out_ptr = out.data(); context.Wait(); @@ -50,15 +50,15 @@ TEST(math_function, trans_mul_notrans) { auto* gpu_place = new paddle::platform::CUDAPlace(0); paddle::platform::CUDADeviceContext context(*gpu_place); - paddle::framework::CopyFrom(input1, *gpu_place, context, &input1_gpu); - paddle::framework::CopyFrom(input1, *gpu_place, context, &input2_gpu); + paddle::framework::Copy(input1, *gpu_place, context, &input1_gpu); + paddle::framework::Copy(input1, *gpu_place, context, &input2_gpu); out_gpu.mutable_data({3, 3}, *gpu_place); paddle::operators::math::matmul( context, input1_gpu, true, input2_gpu, false, 1, &out_gpu, 0); - paddle::framework::CopyFrom(out_gpu, *cpu_place, context, &out); + paddle::framework::Copy(out_gpu, *cpu_place, context, &out); float* out_ptr = out.data(); context.Wait(); @@ -99,9 +99,9 @@ TEST(math_function, gemm_notrans_cublas) { auto* gpu_place = new paddle::platform::CUDAPlace(0); paddle::platform::CUDADeviceContext context(*gpu_place); - paddle::framework::CopyFrom(input1, *gpu_place, context, &input1_gpu); - paddle::framework::CopyFrom(input2, *gpu_place, context, &input2_gpu); - paddle::framework::CopyFrom(input3, *gpu_place, context, &input3_gpu); + paddle::framework::Copy(input1, *gpu_place, context, &input1_gpu); + paddle::framework::Copy(input2, *gpu_place, context, &input2_gpu); + paddle::framework::Copy(input3, *gpu_place, context, &input3_gpu); float* a = input1_gpu.data(); float* b = input2_gpu.data(); float* c = input3_gpu.mutable_data(*gpu_place); @@ -109,7 +109,7 @@ TEST(math_function, gemm_notrans_cublas) { paddle::operators::math::gemm( context, false, false, m, n, k, 1, a, 3, b + 1, 4, 1, c + 1, 4); - paddle::framework::CopyFrom(input3_gpu, *cpu_place, context, &input3); + paddle::framework::Copy(input3_gpu, *cpu_place, context, &input3); // numpy code: // a = np.arange(6).reshape(2, 3) @@ -154,9 +154,9 @@ TEST(math_function, gemm_trans_cublas) { auto* gpu_place = new paddle::platform::CUDAPlace(0); paddle::platform::CUDADeviceContext context(*gpu_place); - paddle::framework::CopyFrom(input1, *gpu_place, context, &input1_gpu); - paddle::framework::CopyFrom(input2, *gpu_place, context, &input2_gpu); - paddle::framework::CopyFrom(input3, *gpu_place, context, &input3_gpu); + paddle::framework::Copy(input1, *gpu_place, context, &input1_gpu); + paddle::framework::Copy(input2, *gpu_place, context, &input2_gpu); + paddle::framework::Copy(input3, *gpu_place, context, &input3_gpu); float* a = input1_gpu.data(); float* b = input2_gpu.data(); float* c = input3_gpu.mutable_data(*gpu_place); @@ -164,7 +164,7 @@ TEST(math_function, gemm_trans_cublas) { paddle::operators::math::gemm( context, false, true, m, n, k, 1, a, 3, b + 3, 3, 1, c + 1, 4); - paddle::framework::CopyFrom(input3_gpu, *cpu_place, context, &input3); + paddle::framework::Copy(input3_gpu, *cpu_place, context, &input3); context.Wait(); EXPECT_EQ(input3_ptr[0], 0); @@ -205,15 +205,15 @@ void GemvTest(int m, int n, bool trans) { } paddle::platform::CUDADeviceContext context(*gpu_place); - paddle::framework::CopyFrom(mat_a, *gpu_place, context, &g_mat_a); - paddle::framework::CopyFrom(vec_b, *gpu_place, context, &g_vec_b); + paddle::framework::Copy(mat_a, *gpu_place, context, &g_mat_a); + paddle::framework::Copy(vec_b, *gpu_place, context, &g_vec_b); paddle::operators::math::gemv( context, trans, static_cast(m), static_cast(n), 1., g_data_a, g_data_b, 0., g_data_c); - paddle::framework::CopyFrom(g_vec_c, paddle::platform::CPUPlace(), context, - &vec_c); + paddle::framework::Copy(g_vec_c, paddle::platform::CPUPlace(), context, + &vec_c); if (!trans) { for (int i = 0; i < m; ++i) { diff --git a/paddle/operators/math/selected_rows_functor_test.cu b/paddle/operators/math/selected_rows_functor_test.cu index 0a2e36f68ac..38808e13014 100644 --- a/paddle/operators/math/selected_rows_functor_test.cu +++ b/paddle/operators/math/selected_rows_functor_test.cu @@ -67,7 +67,7 @@ TEST(selected_rows_functor, gpu_add) { EXPECT_EQ(out_rows[6], 9); Tensor out_cpu; - CopyFrom(*out_value, cpu_place, ctx, &out_cpu); + Copy(*out_value, cpu_place, ctx, &out_cpu); ctx.Wait(); auto* out_cpu_data = out_cpu.data(); @@ -94,7 +94,7 @@ TEST(selected_rows_functor, gpu_add) { add_tensor_functor(ctx, *output, *tensor1, tensor2.get()); Tensor tensor2_cpu; - CopyFrom(*tensor2, cpu_place, ctx, &tensor2_cpu); + Copy(*tensor2, cpu_place, ctx, &tensor2_cpu); ctx.Wait(); auto* tensor2_cpu_data = tensor2_cpu.data(); @@ -167,7 +167,7 @@ TEST(selected_rows_functor, gpu_add_to) { EXPECT_EQ(out_rows[6], 9); Tensor out_cpu; - CopyFrom(*out_value, cpu_place, ctx, &out_cpu); + Copy(*out_value, cpu_place, ctx, &out_cpu); ctx.Wait(); auto* out_cpu_data = out_cpu.data(); @@ -191,7 +191,7 @@ TEST(selected_rows_functor, gpu_add_to) { add_to_tensor_functor(ctx, *output, tensor1.get()); Tensor tensor1_cpu; - CopyFrom(*tensor1, cpu_place, ctx, &tensor1_cpu); + Copy(*tensor1, cpu_place, ctx, &tensor1_cpu); ctx.Wait(); auto* tensor1_cpu_data = tensor1_cpu.data(); diff --git a/paddle/operators/math/vol2col_test.cc b/paddle/operators/math/vol2col_test.cc index 3794f0e52d2..7a308ca8140 100644 --- a/paddle/operators/math/vol2col_test.cc +++ b/paddle/operators/math/vol2col_test.cc @@ -71,7 +71,7 @@ void testVol2col() { if (paddle::platform::is_cpu_place(*place)) { input = input_tmp; } else { - CopyFrom(input_tmp, *place, *context, &input); + Copy(input_tmp, *place, *context, &input); } output.mutable_data({1, filter_size, filter_size, filter_size, output_depth, output_height, output_width}, @@ -85,7 +85,7 @@ void testVol2col() { if (paddle::platform::is_cpu_place(*place)) { out_cfo_ptr = output.data(); } else { - CopyFrom(output, paddle::platform::CPUPlace(), *context, &output_tmp); + Copy(output, paddle::platform::CPUPlace(), *context, &output_tmp); out_cfo_ptr = output_tmp.data(); } @@ -99,7 +99,7 @@ void testVol2col() { if (paddle::platform::is_cpu_place(*place)) { input = input_tmp; } else { - CopyFrom(input_tmp, *place, *context, &input); + Copy(input_tmp, *place, *context, &input); } paddle::operators::math::Col2VolFunctor col2vol; @@ -109,7 +109,7 @@ void testVol2col() { if (paddle::platform::is_cpu_place(*place)) { in_ptr = input.data(); } else { - CopyFrom(input, paddle::platform::CPUPlace(), *context, &input_tmp); + Copy(input, paddle::platform::CPUPlace(), *context, &input_tmp); in_ptr = input_tmp.data(); } diff --git a/paddle/operators/merge_lod_tensor_op.cc b/paddle/operators/merge_lod_tensor_op.cc index 3f999e404f8..87644d316d4 100644 --- a/paddle/operators/merge_lod_tensor_op.cc +++ b/paddle/operators/merge_lod_tensor_op.cc @@ -49,7 +49,7 @@ class MergeLoDTensorOp : public framework::OperatorBase { cpu_mask->ShareDataWith(mask); } else if (platform::is_gpu_place(mask.place())) { #ifdef PADDLE_WITH_CUDA - framework::CopyFrom(mask, platform::CPUPlace(), dev_ctx, cpu_mask.get()); + framework::Copy(mask, platform::CPUPlace(), dev_ctx, cpu_mask.get()); #else PADDLE_THROW("Not supported GPU, Please compile WITH_GPU option"); #endif @@ -104,8 +104,8 @@ class MergeLoDTensorOp : public framework::OperatorBase { continue; } auto slice = out->Slice(out_offset, out_offset + len); - framework::CopyFrom(input->Slice(start_offset, end_offset), place, - dev_ctx, &slice); + framework::Copy(input->Slice(start_offset, end_offset), place, dev_ctx, + &slice); out_offset += len; (*in_idx) += 1; } diff --git a/paddle/operators/multiplex_op.cu b/paddle/operators/multiplex_op.cu index f49ee71f104..4372dc2c65e 100644 --- a/paddle/operators/multiplex_op.cu +++ b/paddle/operators/multiplex_op.cu @@ -33,7 +33,7 @@ class MultiplexGPUKernel : public framework::OpKernel { auto cols = ins[0]->numel() / rows; // copy index to cpu Tensor index_t_cpu; - CopyFrom(*ids, platform::CPUPlace(), ctx.device_context(), &index_t_cpu); + Copy(*ids, platform::CPUPlace(), ctx.device_context(), &index_t_cpu); auto* index = index_t_cpu.data(); auto stream = ctx.cuda_device_context().stream(); platform::CUDAPlace place = boost::get(ctx.GetPlace()); @@ -69,7 +69,7 @@ class MultiplexGradGPUKernel : public framework::OpKernel { auto cols = ins[0]->numel() / rows; // copy index to cpu Tensor index_t_cpu; - CopyFrom(*ids, platform::CPUPlace(), ctx.device_context(), &index_t_cpu); + Copy(*ids, platform::CPUPlace(), ctx.device_context(), &index_t_cpu); auto* index = index_t_cpu.data(); auto stream = ctx.cuda_device_context().stream(); diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index 077245cd83b..a6bc70f4c89 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -211,7 +211,7 @@ class ParallelDoGradOp : public OperatorBase { auto &tt = sub_scopes[place_idx]->FindVar(s)->Get(); VLOG(3) << place_idx; VLOG(3) << tt; - framework::CopyFrom(tt, places[0], t_buf); + framework::Copy(tt, places[0], t_buf); auto sum_op = framework::OpRegistry::CreateOp( "sum", {{"X", {s, s_buf}}}, {{"Out", {s}}}, @@ -220,7 +220,7 @@ class ParallelDoGradOp : public OperatorBase { } VLOG(3) << t; - framework::CopyFrom(t, place, scope.FindVar(s)->GetMutable()); + framework::Copy(t, place, scope.FindVar(s)->GetMutable()); } } }; diff --git a/paddle/operators/recurrent_op.cc b/paddle/operators/recurrent_op.cc index 056fa46949c..a136c5b447d 100644 --- a/paddle/operators/recurrent_op.cc +++ b/paddle/operators/recurrent_op.cc @@ -290,7 +290,7 @@ class RecurrentOp : public RecurrentBase { auto dst_out = dst_tensor->Slice(seq_offset, seq_offset + 1); // Explicit copy output since the local RNN scope can be destroyed // early. - framework::CopyFrom(src_tensor, place, dev_ctx, &dst_out); + framework::Copy(src_tensor, place, dev_ctx, &dst_out); }); scopes.Next(); @@ -376,7 +376,7 @@ class RecurrentGradOp : public RecurrentBase { auto *cur_grad_var = cur_scope.Var(cur_grad); auto cur_grad_tensor = cur_grad_var->GetMutable(); - framework::CopyFrom(ex_tensor, place, dev_ctx, cur_grad_tensor); + framework::Copy(ex_tensor, place, dev_ctx, cur_grad_tensor); } } @@ -450,7 +450,7 @@ class RecurrentGradOp : public RecurrentBase { } auto dst = outside->Slice(seq_offset, seq_offset + 1); - framework::CopyFrom(inside, place, dev_ctx, &dst); + framework::Copy(inside, place, dev_ctx, &dst); }); VLOG(5) << "Link outside gradient finished "; @@ -463,7 +463,7 @@ class RecurrentGradOp : public RecurrentBase { framework::LoDTensor *outside) { outside->Resize(inside.dims()); outside->mutable_data(place, inside.type()); - framework::CopyFrom(inside, place, dev_ctx, outside); + framework::Copy(inside, place, dev_ctx, outside); }); VLOG(5) << "Link initialize state gradient finished "; } diff --git a/paddle/operators/reorder_lod_tensor_by_rank_op.cc b/paddle/operators/reorder_lod_tensor_by_rank_op.cc index 0fa615d8743..a055cdf7e89 100644 --- a/paddle/operators/reorder_lod_tensor_by_rank_op.cc +++ b/paddle/operators/reorder_lod_tensor_by_rank_op.cc @@ -146,7 +146,7 @@ class ReorderLoDTensorByRankTableBase : public framework::OperatorBase { platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); auto &dev_ctx = *pool.Get(place); - framework::CopyFrom(x_sliced, out_sliced.place(), dev_ctx, &out_sliced); + framework::Copy(x_sliced, out_sliced.place(), dev_ctx, &out_sliced); out_offset += len; return out_offset; } diff --git a/paddle/operators/reshape_op.h b/paddle/operators/reshape_op.h index a4eb34a0ad1..d884b03cadb 100644 --- a/paddle/operators/reshape_op.h +++ b/paddle/operators/reshape_op.h @@ -28,7 +28,7 @@ class ReshapeKernel : public framework::OpKernel { auto* in = ctx.Input("X"); auto out_dims = out->dims(); out->mutable_data(ctx.GetPlace()); - framework::CopyFrom(*in, ctx.GetPlace(), ctx.device_context(), out); + framework::Copy(*in, ctx.GetPlace(), ctx.device_context(), out); out->Resize(out_dims); } }; @@ -42,7 +42,7 @@ class ReshapeGradKernel : public framework::OpKernel { d_x->mutable_data(ctx.GetPlace()); auto in_dims = d_x->dims(); - framework::CopyFrom(*d_out, ctx.GetPlace(), ctx.device_context(), d_x); + framework::Copy(*d_out, ctx.GetPlace(), ctx.device_context(), d_x); d_x->Resize(in_dims); } }; diff --git a/paddle/operators/sequence_slice_op.h b/paddle/operators/sequence_slice_op.h index 14bcaebbb40..0e4e4cf65fc 100644 --- a/paddle/operators/sequence_slice_op.h +++ b/paddle/operators/sequence_slice_op.h @@ -66,13 +66,13 @@ class SequenceSliceOpKernel : public framework::OpKernel { if (platform::is_gpu_place(ctx.GetPlace())) { offset_cpu.mutable_data(offset->dims(), platform::CPUPlace()); - framework::CopyFrom(*offset, platform::CPUPlace(), ctx.device_context(), - &offset_cpu); + framework::Copy(*offset, platform::CPUPlace(), ctx.device_context(), + &offset_cpu); offset_data = offset_cpu.data(); length_cpu.mutable_data(length->dims(), platform::CPUPlace()); - framework::CopyFrom(*length, platform::CPUPlace(), ctx.device_context(), - &length_cpu); + framework::Copy(*length, platform::CPUPlace(), ctx.device_context(), + &length_cpu); length_data = length_cpu.data(); } @@ -127,13 +127,13 @@ class SequenceSliceGradOpKernel : public framework::OpKernel { if (platform::is_gpu_place(ctx.GetPlace())) { offset_cpu.mutable_data(offset->dims(), platform::CPUPlace()); - framework::CopyFrom(*offset, platform::CPUPlace(), ctx.device_context(), - &offset_cpu); + framework::Copy(*offset, platform::CPUPlace(), ctx.device_context(), + &offset_cpu); offset_data = offset_cpu.data(); length_cpu.mutable_data(length->dims(), platform::CPUPlace()); - framework::CopyFrom(*length, platform::CPUPlace(), ctx.device_context(), - &length_cpu); + framework::Copy(*length, platform::CPUPlace(), ctx.device_context(), + &length_cpu); length_data = length_cpu.data(); } diff --git a/paddle/operators/shrink_rnn_memory_op.cc b/paddle/operators/shrink_rnn_memory_op.cc index b37269b471b..821754a0a63 100644 --- a/paddle/operators/shrink_rnn_memory_op.cc +++ b/paddle/operators/shrink_rnn_memory_op.cc @@ -115,7 +115,7 @@ class ShrinkRNNMemoryGradOp : public ArrayOp { auto &dout_tensor = dout_var->Get(); auto height = dout_tensor.dims()[0]; auto slice = dx_tensor.Slice(0, static_cast(height)); - framework::CopyFrom(dout_tensor, dout_tensor.place(), dev_ctx, &slice); + framework::Copy(dout_tensor, dout_tensor.place(), dev_ctx, &slice); if (dx_tensor.dims()[0] > height) { auto rest_tensor = dx_tensor.Slice( static_cast(height), static_cast(dx_tensor.dims()[0])); diff --git a/paddle/operators/split_lod_tensor_op.cc b/paddle/operators/split_lod_tensor_op.cc index 2d8787d740c..bd93c492015 100644 --- a/paddle/operators/split_lod_tensor_op.cc +++ b/paddle/operators/split_lod_tensor_op.cc @@ -53,7 +53,7 @@ class SplitLoDTensorOp : public framework::OperatorBase { cpu_mask->ShareDataWith(mask); } else if (platform::is_gpu_place(mask.place())) { #ifdef PADDLE_WITH_CUDA - framework::CopyFrom(mask, platform::CPUPlace(), dev_ctx, cpu_mask.get()); + framework::Copy(mask, platform::CPUPlace(), dev_ctx, cpu_mask.get()); #else PADDLE_THROW("Not supported GPU, Please compile WITH_GPU option"); #endif @@ -111,9 +111,9 @@ class SplitLoDTensorOp : public framework::OperatorBase { // out[offset: offset+len] = x[each_range.begin: each_range.end] auto slice = out->Slice(static_cast(offset), static_cast(offset + len)); - framework::CopyFrom(x.Slice(static_cast(each_range.begin), - static_cast(each_range.end)), - x.place(), dev_ctx, &slice); + framework::Copy(x.Slice(static_cast(each_range.begin), + static_cast(each_range.end)), + x.place(), dev_ctx, &slice); offset += len; } } diff --git a/paddle/operators/sum_op.h b/paddle/operators/sum_op.h index 552b48f608b..2c43097d717 100644 --- a/paddle/operators/sum_op.h +++ b/paddle/operators/sum_op.h @@ -107,8 +107,8 @@ class SumKernel : public framework::OpKernel { out_array.resize(i + 1); } if (out_array[i].numel() == 0) { - framework::CopyFrom(in_array[i], in_array[i].place(), - context.device_context(), &out_array[i]); + framework::Copy(in_array[i], in_array[i].place(), + context.device_context(), &out_array[i]); out_array[i].set_lod(in_array[i].lod()); } else { PADDLE_ENFORCE(out_array[i].lod() == in_array[i].lod()); diff --git a/paddle/operators/tensor_array_read_write_op.cc b/paddle/operators/tensor_array_read_write_op.cc index a6dceb2e3a1..a70be8b8752 100644 --- a/paddle/operators/tensor_array_read_write_op.cc +++ b/paddle/operators/tensor_array_read_write_op.cc @@ -44,7 +44,7 @@ class WriteToArrayOp : public ArrayOp { platform::DeviceContextPool::Instance(); auto &dev_ctx = *pool.Get(place); - CopyFrom(x_tensor, place, dev_ctx, out_tensor); + Copy(x_tensor, place, dev_ctx, out_tensor); out_tensor->set_lod(x_tensor.lod()); } else { VLOG(10) << "WARNING: The input tensor 'x_tensor' holds no memory, so " @@ -135,7 +135,7 @@ class ReadFromArrayOp : public ArrayOp { platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); auto &dev_ctx = *pool.Get(place); - framework::CopyFrom(x_array[offset], place, dev_ctx, out_tensor); + framework::Copy(x_array[offset], place, dev_ctx, out_tensor); out_tensor->set_lod(x_array[offset].lod()); } else { VLOG(10) << "offset " << offset << " >= " << x_array.size(); -- GitLab From 71337d20eaba897df0ffb2b5c352948f2d49fb9a Mon Sep 17 00:00:00 2001 From: zhangchao Date: Tue, 9 Jan 2018 15:35:08 +0800 Subject: [PATCH 0149/2305] Update the plotlog.py add multi lines in one figure and provide some line-styles --- benchmark/paddle/image/plotlog.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/benchmark/paddle/image/plotlog.py b/benchmark/paddle/image/plotlog.py index ce9d6ac24ad..34043c49948 100644 --- a/benchmark/paddle/image/plotlog.py +++ b/benchmark/paddle/image/plotlog.py @@ -70,12 +70,19 @@ def sample(metric, sample_rate): return metric_sample -def plot_metric(metric, batch_id, graph_title): +def plot_metric(metric, batch_id, graph_title, line_style='b-', + line_label='y', + line_num=1): plt.figure() plt.title(graph_title) - plt.plot(batch_id, metric) + if line_num == 1: + plt.plot(batch_id, metric, line_style, line_label) + else: + for i in line_num: + plt.plot(batch_id, metric[i], line_style[i], line_label[i]) plt.xlabel('batch') plt.ylabel(graph_title) + plt.legend() plt.savefig(graph_title + '.jpg') plt.close() @@ -91,8 +98,8 @@ def main(): loss_sample = sample(loss, args.sample_rate) accuracy_sample = sample(accuracy, args.sample_rate) - plot_metric(loss_sample, batch_sample, 'loss') - plot_metric(accuracy_sample, batch_sample, 'accuracy') + plot_metric(loss_sample, batch_sample, 'loss', line_label='loss') + plot_metric(accuracy_sample, batch_sample, 'accuracy', line_style='g-', line_label='accuracy') if __name__ == '__main__': -- GitLab From 56e758fc10e2db646a82bc8a93c44d6427718c11 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Tue, 9 Jan 2018 16:23:41 +0800 Subject: [PATCH 0151/2305] trainer ok --- .../paddle/v2/fluid/distribute_transpiler.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index 58d32bac125..7f3da674633 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -56,6 +56,8 @@ def split_dense_variable(var_list, (block_id) * block_size)) block = VarBlock(var.name, block_id, curr_block_size) blocks.append(str(block)) + print("$$ splited var: ", var.name, var.shape, split_count, len(blocks), + block_size) return blocks @@ -132,10 +134,12 @@ class DistributeTranspiler: # step4 for varname, splited_var in param_var_mapping.iteritems(): + if len(splited_var) <= 1: + continue orig_param = program.global_block().vars[varname] concat = program.global_block().append_op( type="concat", - inputs={"X": send_outputs}, + inputs={"X": splited_var}, outputs={"Out": orig_param}, attrs={"axis": 0}) @@ -147,28 +151,29 @@ class DistributeTranspiler: if not block_map.has_key(varname): block_map[varname] = [] block_map[varname].append((long(offset), long(size))) - for varname, splited in block_map.iteritems(): orig_var = program.global_block().vars[varname] + var_mapping[varname] = [] + if len(splited) == 1: + var_mapping[varname] = [orig_var] + continue orig_shape = orig_var.shape orig_dim1_flatten = 1 if len(orig_shape) >= 2: orig_dim1_flatten = reduce(lambda x, y: x * y, orig_shape[1:]) - var_list = [] + for i, block in enumerate(splited): size = block[1] rows = size / orig_dim1_flatten splited_shape = [rows] if len(orig_shape) >= 2: splited_shape.extend(orig_shape[1:]) - print("block, splited shape:", block, splited_shape) var = program.global_block().create_var( name="%s.block%d" % (varname, i), psersistable=False, dtype=orig_var.dtype, shape=splited_shape) # flattend splited var - var_list.append(var) - var_mapping[varname] = var_list + var_mapping[varname].append(var) return var_mapping def _clone_param(self, block, v): @@ -199,7 +204,8 @@ class DistributeTranspiler: def _append_split_op(self, program, gradblocks): var_mapping = self._create_vars_from_blocklist(program, gradblocks) for varname, splited_vars in var_mapping.iteritems(): - if len(splited_vars) == 1: + # variable that don't need to split have empty splited_vars + if len(splited_vars) <= 1: continue orig_var = program.global_block().vars[varname] sections = [] -- GitLab From 97724c2a1462625ab7dad7d2f1cbf8f7fc2a325f Mon Sep 17 00:00:00 2001 From: gx_wind Date: Tue, 9 Jan 2018 16:35:52 +0800 Subject: [PATCH 0152/2305] fix bugs and modify func param name --- adversarial/advbox/attacks/base.py | 10 +++++----- adversarial/advbox/attacks/gradientsign.py | 9 +++++---- adversarial/advbox/models/base.py | 3 +-- adversarial/advbox/models/paddle.py | 2 +- adversarial/fluid_mnist.py | 7 +------ 5 files changed, 13 insertions(+), 18 deletions(-) diff --git a/adversarial/advbox/attacks/base.py b/adversarial/advbox/attacks/base.py index 2058e3cfeb1..98a65f2fddf 100644 --- a/adversarial/advbox/attacks/base.py +++ b/adversarial/advbox/attacks/base.py @@ -18,22 +18,22 @@ class Attack(object): def __init__(self, model): self.model = model - def __call__(self, image_batch): + def __call__(self, image_label): """ Generate the adversarial sample. Args: - image_batch(list): The image and label tuple list. + image_label(list): The image and label tuple list with one element. """ - adv_img = self._apply(image_batch) + adv_img = self._apply(image_label) return adv_img @abstractmethod - def _apply(self, image_batch): + def _apply(self, image_label): """ Search an adversarial example. Args: - image_batch(list): The image and label tuple list. + image_batch(list): The image and label tuple list with one element. """ raise NotImplementedError diff --git a/adversarial/advbox/attacks/gradientsign.py b/adversarial/advbox/attacks/gradientsign.py index dff518811ef..15b1d176cb1 100644 --- a/adversarial/advbox/attacks/gradientsign.py +++ b/adversarial/advbox/attacks/gradientsign.py @@ -15,18 +15,19 @@ class GradientSignAttack(Attack): Paper link: https://arxiv.org/abs/1412.6572 """ - def _apply(self, image_batch, epsilons=1000): - pre_label = np.argmax(self.model.predict(image_batch)) + def _apply(self, image_label, epsilons=1000): + assert len(image_label) == 1 + pre_label = np.argmax(self.model.predict(image_label)) min_, max_ = self.model.bounds() - gradient = self.model.gradient(image_batch) + gradient = self.model.gradient(image_label) gradient_sign = np.sign(gradient) * (max_ - min_) if not isinstance(epsilons, Iterable): epsilons = np.linspace(0, 1, num=epsilons + 1) for epsilon in epsilons: - adv_img = image_batch[0][0].reshape( + adv_img = image_label[0][0].reshape( gradient_sign.shape) + epsilon * gradient_sign adv_img = np.clip(adv_img, min_, max_) adv_label = np.argmax(self.model.predict([(adv_img, 0)])) diff --git a/adversarial/advbox/models/base.py b/adversarial/advbox/models/base.py index 2e5c397dc47..74e1045def7 100644 --- a/adversarial/advbox/models/base.py +++ b/adversarial/advbox/models/base.py @@ -81,8 +81,7 @@ class Model(object): Calculate the gradient of the cross-entropy loss w.r.t the image. Args: - image(numpy.ndarray): image with shape (height, width, channel) - label(int): image label used to cal gradient. + image_batch(list): The image and label tuple list. Return: numpy.ndarray: gradient of the cross-entropy loss w.r.t the image with diff --git a/adversarial/advbox/models/paddle.py b/adversarial/advbox/models/paddle.py index a72eb148bc6..33b2a3d5c69 100644 --- a/adversarial/advbox/models/paddle.py +++ b/adversarial/advbox/models/paddle.py @@ -49,7 +49,7 @@ class PaddleModel(Model): loss = self._program.block(0).var(self._cost_name) param_grads = fluid.backward.append_backward( loss, parameter_list=[self._input_name]) - self._gradient = param_grads[0][1] + self._gradient = dict(param_grads)[self._input_name] def predict(self, image_batch): """ diff --git a/adversarial/fluid_mnist.py b/adversarial/fluid_mnist.py index 031928e994c..db4d4b51868 100644 --- a/adversarial/fluid_mnist.py +++ b/adversarial/fluid_mnist.py @@ -15,7 +15,6 @@ def mnist_cnn_model(img): Returns: Variable: the label prediction """ - #conv1 = fluid.nets.conv2d() conv_pool_1 = fluid.nets.simple_img_conv_pool( input=img, num_filters=20, @@ -73,19 +72,15 @@ def main(): pass_acc = accuracy.eval(exe) print("pass_id=" + str(pass_id) + " acc=" + str(acc) + " pass_acc=" + str(pass_acc)) - # print loss, acc if loss < LOSS_THRESHOLD and pass_acc > ACC_THRESHOLD: - # if avg cost less than 10.0 and accuracy is larger than 0.9, we think our code is good. break -# exit(0) - pass_acc = accuracy.eval(exe) print("pass_id=" + str(pass_id) + " pass_acc=" + str(pass_acc)) fluid.io.save_params( exe, dirname='./mnist', main_program=fluid.default_main_program()) print('train mnist done') - exit(1) + if __name__ == '__main__': main() -- GitLab From fe341bacde6e6a981ebf60561e228b612faaad43 Mon Sep 17 00:00:00 2001 From: QI JUN Date: Tue, 9 Jan 2018 17:14:35 +0800 Subject: [PATCH 0153/2305] refine batch norm python layer (#7348) --- python/paddle/v2/fluid/layer_helper.py | 5 +++-- python/paddle/v2/fluid/layers/nn.py | 14 ++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/python/paddle/v2/fluid/layer_helper.py b/python/paddle/v2/fluid/layer_helper.py index 4469f7285ef..325735e6793 100644 --- a/python/paddle/v2/fluid/layer_helper.py +++ b/python/paddle/v2/fluid/layer_helper.py @@ -120,11 +120,12 @@ class LayerHelper(object): raise ValueError("no Parameter name %s found" % name) return param - def create_tmp_variable(self, dtype): + def create_tmp_variable(self, dtype, stop_gradient=False): return self.main_program.current_block().create_var( name=unique_name(".".join([self.name, 'tmp'])), dtype=dtype, - persistable=False) + persistable=False, + stop_gradient=stop_gradient) def create_variable(self, *args, **kwargs): return self.main_program.current_block().create_var(*args, **kwargs) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 180a760150b..b1534c5a886 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -971,11 +971,17 @@ def batch_norm(input, attr=helper.param_attr, shape=param_shape, dtype=dtype, is_bias=True) mean = helper.create_global_variable( - dtype=input.dtype, shape=param_shape, persistable=True) + dtype=input.dtype, + shape=param_shape, + persistable=True, + stop_gradient=True) helper.set_variable_initializer(var=mean, initializer=Constant(0.0)) variance = helper.create_global_variable( - dtype=input.dtype, shape=param_shape, persistable=True) + dtype=input.dtype, + shape=param_shape, + persistable=True, + stop_gradient=True) helper.set_variable_initializer(var=variance, initializer=Constant(1.0)) # create output @@ -983,8 +989,8 @@ def batch_norm(input, mean_out = mean # variance and variance out share the same memory variance_out = variance - saved_mean = helper.create_tmp_variable(dtype) - saved_variance = helper.create_tmp_variable(dtype) + saved_mean = helper.create_tmp_variable(dtype=dtype, stop_gradient=True) + saved_variance = helper.create_tmp_variable(dtype=dtype, stop_gradient=True) batch_norm_out = helper.create_tmp_variable(dtype) -- GitLab From cd9374fba293f504e1b0a1112ca212fff77da7ef Mon Sep 17 00:00:00 2001 From: zhangchao Date: Tue, 9 Jan 2018 17:40:48 +0800 Subject: [PATCH 0154/2305] Update plotlog.py --- benchmark/paddle/image/plotlog.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/benchmark/paddle/image/plotlog.py b/benchmark/paddle/image/plotlog.py index 34043c49948..4ac8edfeafc 100644 --- a/benchmark/paddle/image/plotlog.py +++ b/benchmark/paddle/image/plotlog.py @@ -70,15 +70,18 @@ def sample(metric, sample_rate): return metric_sample -def plot_metric(metric, batch_id, graph_title, line_style='b-', - line_label='y', - line_num=1): +def plot_metric(metric, + batch_id, + graph_title, + line_style='b-', + line_label='y', + line_num=1): plt.figure() plt.title(graph_title) if line_num == 1: plt.plot(batch_id, metric, line_style, line_label) else: - for i in line_num: + for i in range(line_num): plt.plot(batch_id, metric[i], line_style[i], line_label[i]) plt.xlabel('batch') plt.ylabel(graph_title) @@ -99,7 +102,11 @@ def main(): accuracy_sample = sample(accuracy, args.sample_rate) plot_metric(loss_sample, batch_sample, 'loss', line_label='loss') - plot_metric(accuracy_sample, batch_sample, 'accuracy', line_style='g-', line_label='accuracy') + plot_metric(accuracy_sample, + batch_sample, + 'accuracy', + line_style='g-', + line_label='accuracy') if __name__ == '__main__': -- GitLab From 8f962f74338833abe8a5186270f57d1e1897bbdb Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Tue, 9 Jan 2018 18:53:15 +0800 Subject: [PATCH 0155/2305] Update --- paddle/operators/while_op.cc | 79 +++++++++++++++++++----------------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/paddle/operators/while_op.cc b/paddle/operators/while_op.cc index 65d827e0e0c..3b78dd128f8 100644 --- a/paddle/operators/while_op.cc +++ b/paddle/operators/while_op.cc @@ -211,59 +211,64 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { protected: std::unique_ptr Apply() const override { - auto *grad = new framework::OpDesc(); - grad->SetType("while_grad"); - grad->SetInput(kX, Input(kX)); + auto *while_grad = new framework::OpDesc(); + while_grad->SetType("while_grad"); + while_grad->SetInput(kX, Input(kX)); + while_grad->SetInput(kOutputs, Output(kOutputs)); + while_grad->SetInput(kStepScopes, Output(kStepScopes)); + + auto *grad_block = this->grad_block_[0]; + auto *fwd_block = grad_block->ParentBlock(); + // auto *parent_block = fwd_block->ParentBlock(); // Not all of IGs will be generated by inner gradient operators of while op. // Ignore IGs that is not generated by the inside block. - auto igs = InputGrad(kX, /*do not drop empty gradient*/ false); - std::unordered_set all_outs; - for (size_t i = 0; i < grad_block_[0]->OpSize(); ++i) { - for (auto &oname : grad_block_[0]->Op(i)->OutputArgumentNames()) { - all_outs.insert(oname); + std::unordered_set inner_op_outputs; + LOG(INFO) << "FUCK1"; + for (const auto *op : grad_block->AllOps()) { + for (auto &oname : op->OutputArgumentNames()) { + inner_op_outputs.insert(oname); } } + LOG(INFO) << "FUCK2"; + auto igs = InputGrad(kX, /*do not drop empty gradient*/ false); for (auto &each_ig : igs) { - if (all_outs.find(each_ig) == all_outs.end()) { + if (inner_op_outputs.find(each_ig) == inner_op_outputs.end()) { VLOG(10) << "Ignore " << each_ig; each_ig = framework::kEmptyVarName; } } - - grad->SetOutput(framework::GradVarName(kX), igs); - - grad->SetInput(kOutputs, Output(kOutputs)); + while_grad->SetOutput(framework::GradVarName(kX), igs); // OG should be re-calculated by step blocks, since many outputs of while op // do not need to calculate gradients. std::unordered_set block_ins; - auto *fwd_block = this->grad_block_[0]->ParentBlock(); - { - for (auto &p : Input(kX)) { - block_ins.insert(p); - } - for (auto &o : Output(kOutputs)) { - block_ins.insert(o); - } - } + std::copy(Input(kX).begin(), Input(kX).end(), + std::inserter(block_ins, block_ins.end())); + std::copy(Output(kOutputs).begin(), Output(kOutputs).end(), + std::inserter(block_ins, block_ins.end())); + std::unordered_set extra_inputs; - for (size_t i = 0; i < grad_block_[0]->OpSize(); ++i) { - for (auto &input_name : grad_block_[0]->Op(i)->InputArgumentNames()) { - if (block_ins.find(input_name) != block_ins.end()) { + for (const auto *op : grad_block->AllOps()) { + for (auto &input_name : op->InputArgumentNames()) { + // If the input of Op has been recorded or is generated by the forward + // block, do not make it as input again. + if (block_ins.find(input_name) != block_ins.end() || + fwd_block->FindVar(input_name) != nullptr) { continue; } - // If the input of Op is generated by the forward block, do not make it - // as input again. - if (fwd_block->FindVar(input_name) != nullptr) { + /* + if (parent_block->FindVarRecursive(input_name) == nullptr) { + VLOG(5) << "WARNING! Variable '" << input_name + << "' is the input of '" << op->Type() + << "'. But can not be found in any block."; continue; } - + */ extra_inputs.insert(input_name); } - - for (auto &output_name : grad_block_[0]->Op(i)->OutputArgumentNames()) { + for (auto &output_name : op->OutputArgumentNames()) { block_ins.insert(output_name); } } @@ -272,15 +277,15 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { extra_inputs_list.resize(extra_inputs.size()); std::copy(extra_inputs.begin(), extra_inputs.end(), extra_inputs_list.begin()); - grad->SetInput(framework::GradVarName(kOutputs), extra_inputs_list); - grad->SetInput(kStepScopes, Output(kStepScopes)); - grad->SetAttrMap(this->Attrs()); - grad->SetBlockAttr(kStepBlock, *grad_block_[0]); + while_grad->SetInput(framework::GradVarName(kOutputs), extra_inputs_list); + + while_grad->SetAttrMap(this->Attrs()); + while_grad->SetBlockAttr(kStepBlock, *grad_block); // record the original output gradient names, since the gradient name of // while operator could be renamed. - grad->SetAttr("original_output_grad", extra_inputs_list); + while_grad->SetAttr("original_output_grad", extra_inputs_list); - return std::unique_ptr(grad); + return std::unique_ptr(while_grad); } }; -- GitLab From b5fda2723f7889c102f6ffaa5cd8a901b29b612d Mon Sep 17 00:00:00 2001 From: Yiqun Liu Date: Tue, 9 Jan 2018 18:58:05 +0800 Subject: [PATCH 0156/2305] Port WarpCTC Operator (#5107) * Add Seq2BatchFunctor, which will be used in WarpCTCOp. * Implement WrapCTCFunctor and WrapCTCKernel. * Add unittest of warpctc_op. * Modify the check_output inferface in python unittest framework to allow check a subset of outputs. * Use absolute offset lod in warpctc_op and related functors. * Refine the comments of warpctc_op. * The new python unittest supports checking a subset of the outputs, so revoke the previous change. * Rename the transform from LoDTensor to Tensor with shape [max_sequence_length, num_sequences, sequence_width] to PaddingSequenceFunctor. * Update to the newest codes. * Rename the PaddingSequenceFunctor to PaddingLoDTensorFunctor and remove the computation of dimensions out of the functos. --- cmake/external/warpctc.cmake | 2 +- paddle/operators/CMakeLists.txt | 1 + paddle/operators/conv_op.cc | 1 - paddle/operators/math/CMakeLists.txt | 3 + paddle/operators/math/im2col_test.cc | 5 +- paddle/operators/math/sequence_padding.cc | 144 ++++++++++++ paddle/operators/math/sequence_padding.cu | 209 +++++++++++++++++ paddle/operators/math/sequence_padding.h | 79 +++++++ .../operators/math/sequence_padding_test.cc | 104 +++++++++ paddle/operators/warpctc_op.cc | 141 +++++++++++ paddle/operators/warpctc_op.cu.cc | 22 ++ paddle/operators/warpctc_op.h | 218 ++++++++++++++++++ paddle/platform/dynload/CMakeLists.txt | 1 + paddle/platform/dynload/cublas.cc | 2 +- paddle/platform/dynload/warpctc.cc | 30 +++ paddle/platform/dynload/warpctc.h | 63 +++++ .../fluid/tests/test_sequence_softmax_op.py | 8 +- .../paddle/v2/fluid/tests/test_warpctc_op.py | 200 ++++++++++++++++ 18 files changed, 1222 insertions(+), 11 deletions(-) create mode 100644 paddle/operators/math/sequence_padding.cc create mode 100644 paddle/operators/math/sequence_padding.cu create mode 100644 paddle/operators/math/sequence_padding.h create mode 100644 paddle/operators/math/sequence_padding_test.cc create mode 100644 paddle/operators/warpctc_op.cc create mode 100644 paddle/operators/warpctc_op.cu.cc create mode 100644 paddle/operators/warpctc_op.h create mode 100644 paddle/platform/dynload/warpctc.cc create mode 100644 paddle/platform/dynload/warpctc.h create mode 100644 python/paddle/v2/fluid/tests/test_warpctc_op.py diff --git a/cmake/external/warpctc.cmake b/cmake/external/warpctc.cmake index a8e1aca49c9..7cb4efa7bff 100644 --- a/cmake/external/warpctc.cmake +++ b/cmake/external/warpctc.cmake @@ -63,7 +63,7 @@ ExternalProject_Add( MESSAGE(STATUS "warp-ctc library: ${WARPCTC_LIBRARIES}") INCLUDE_DIRECTORIES(${WARPCTC_INCLUDE_DIR}) -ADD_LIBRARY(warpctc STATIC IMPORTED GLOBAL) +ADD_LIBRARY(warpctc SHARED IMPORTED GLOBAL) SET_PROPERTY(TARGET warpctc PROPERTY IMPORTED_LOCATION ${WARPCTC_LIBRARIES}) ADD_DEPENDENCIES(warpctc extern_warpctc) diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index f1ce5233232..5889a50db09 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -151,6 +151,7 @@ op_library(lstm_op DEPS sequence2batch lstm_compute) op_library(conv_transpose_op DEPS vol2col) op_library(gru_op DEPS sequence2batch gru_compute) op_library(recurrent_op DEPS executor) +op_library(warpctc_op DEPS dynload_warpctc sequence_padding math_function) op_library(cos_sim_op DEPS cos_sim_functor) op_library(parallel_do_op DEPS executor) # FIXME(typhoonzero): save/load depends lodtensor serialization functions diff --git a/paddle/operators/conv_op.cc b/paddle/operators/conv_op.cc index ad84524e178..1468e3eb960 100644 --- a/paddle/operators/conv_op.cc +++ b/paddle/operators/conv_op.cc @@ -230,7 +230,6 @@ void ConvOpGrad::InferShape(framework::InferShapeContext* ctx) const { namespace ops = paddle::operators; REGISTER_OP(conv2d, ops::ConvOp, ops::Conv2DOpMaker, conv2d_grad, ops::ConvOpGrad); -namespace ops = paddle::operators; REGISTER_OP(conv3d, ops::ConvOp, ops::Conv3DOpMaker, conv3d_grad, ops::ConvOpGrad); diff --git a/paddle/operators/math/CMakeLists.txt b/paddle/operators/math/CMakeLists.txt index 7ebcfb9ab9f..fd59eef7d65 100644 --- a/paddle/operators/math/CMakeLists.txt +++ b/paddle/operators/math/CMakeLists.txt @@ -12,6 +12,7 @@ if(WITH_GPU) nv_library(vol2col SRCS vol2col.cc vol2col.cu DEPS device_context tensor) nv_library(context_project SRCS context_project.cc context_project.cu DEPS device_context math_function) nv_library(sequence2batch SRCS sequence2batch.cc sequence2batch.cu DEPS device_context tensor) + nv_library(sequence_padding SRCS sequence_padding.cc sequence_padding.cu DEPS lod_tensor device_context) nv_library(lstm_compute SRCS lstm_compute.cc lstm_compute.cu DEPS device_context activation_functions) nv_library(maxouting SRCS maxouting.cc maxouting.cu DEPS device_context) nv_library(unpooling SRCS unpooling.cc unpooling.cu DEPS device_context) @@ -27,6 +28,7 @@ else() cc_library(vol2col SRCS vol2col.cc DEPS device_context tensor) cc_library(context_project SRCS context_project.cc DEPS device_context math_function) cc_library(sequence2batch SRCS sequence2batch.cc DEPS device_context tensor) + cc_library(sequence_padding SRCS sequence_padding.cc DEPS lod_tensor device_context) cc_library(lstm_compute SRCS lstm_compute.cc DEPS device_context activation_functions) cc_library(maxouting SRCS maxouting.cc DEPS device_context) cc_library(unpooling SRCS unpooling.cc DEPS device_context) @@ -38,3 +40,4 @@ cc_test(math_function_test SRCS math_function_test.cc DEPS math_function tensor) cc_test(selected_rows_functor_test SRCS selected_rows_functor_test.cc DEPS selected_rows_functor) cc_test(im2col_test SRCS im2col_test.cc DEPS math_function tensor) cc_test(vol2col_test SRCS vol2col_test.cc DEPS vol2col tensor) +cc_test(sequence_padding_test SRCS sequence_padding_test.cc DEPS sequence_padding) diff --git a/paddle/operators/math/im2col_test.cc b/paddle/operators/math/im2col_test.cc index a0c02817fd8..1ba24325ffe 100644 --- a/paddle/operators/math/im2col_test.cc +++ b/paddle/operators/math/im2col_test.cc @@ -14,7 +14,6 @@ limitations under the License. */ #include "paddle/operators/math/im2col.h" #include -#include template void testIm2col() { @@ -102,6 +101,7 @@ void testIm2col() { Copy(output_ocf, paddle::platform::CPUPlace(), *context, &output_tmp); out_ocf_ptr = output_tmp.data(); } + for (int i = 0; i < 6; ++i) { EXPECT_EQ(out_ocf_ptr[i], out_ocf_data[i]); } @@ -154,6 +154,9 @@ void testIm2col() { for (int i = 0; i < 6; ++i) { EXPECT_EQ(in_ptr[i], col2im_data[i]); } + + delete place; + delete context; } TEST(math, im2col) { diff --git a/paddle/operators/math/sequence_padding.cc b/paddle/operators/math/sequence_padding.cc new file mode 100644 index 00000000000..fd66455eaef --- /dev/null +++ b/paddle/operators/math/sequence_padding.cc @@ -0,0 +1,144 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/math/sequence_padding.h" + +namespace paddle { +namespace operators { +namespace math { + +template +class PaddingLoDTensorFunctor { + public: + void operator()(const platform::CPUDeviceContext& context, + const framework::LoDTensor& seq, framework::Tensor& padding, + bool norm_by_times) { + auto lod = seq.lod(); + PADDLE_ENFORCE_GT(lod.size(), 0UL, + "The LoD of LoDTensor seq should not be null."); + + const size_t level = 0; + framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); + + auto seq_dims = seq.dims(); + PADDLE_ENFORCE_EQ(seq_dims[0], abs_offset_lod[level].back(), + "The first dimension of LoDTensor seq should be " + "equal to the sum of all sequences's length."); + + auto padding_dims = padding.dims(); + PADDLE_ENFORCE_EQ(padding_dims.size(), 3UL, + "The input padding should be a 3-D Tensor of shape " + "[max_sequence_length, num_sequences, sequence_width]."); + + const size_t max_sequence_length = MaximumSequenceLength(lod, level); + PADDLE_ENFORCE_EQ(padding_dims[0], max_sequence_length, + "The first dimension of Tensor padding should be the " + "maximum length of all sequences in LoDTensor seq."); + + const size_t num_sequences = abs_offset_lod[level].size() - 1; + PADDLE_ENFORCE_EQ(padding_dims[1], num_sequences, + "The second dimension of Tensor padding should be the " + "number of sequences in LoDTensor seq."); + + const size_t sequence_width = seq.numel() / seq_dims[0]; + PADDLE_ENFORCE_EQ(padding_dims[2], sequence_width, + "The third dimension of Tensor padding should be the " + "width of sequence in LoDTensor seq."); + + const T* seq_data = seq.data(); + T* padding_data = padding.data(); + for (size_t i = 0; i < max_sequence_length; ++i) { + for (size_t j = 0; j < num_sequences; ++j) { + size_t start_pos = abs_offset_lod[level][j]; + size_t sequence_length = abs_offset_lod[level][j + 1] - start_pos; + if (i < sequence_length) { + // i > 0 => sequence_length > 0 + T scale = + norm_by_times ? (1.0f / static_cast(sequence_length)) : 1.0f; + for (size_t k = 0; k < sequence_width; ++k) { + padding_data[(i * num_sequences + j) * sequence_width + k] = + seq_data[(start_pos + i) * sequence_width + k] * scale; + } + } else { + memset(padding_data + (i * num_sequences + j) * sequence_width, 0, + sequence_width * sizeof(T)); + } + } + } + } +}; + +template +class UnpaddingLoDTensorFunctor { + public: + void operator()(const platform::CPUDeviceContext& context, + framework::LoDTensor& seq, const framework::Tensor& padding, + bool norm_by_times) { + auto lod = seq.lod(); + PADDLE_ENFORCE_GT(lod.size(), 0UL, + "The LoD of LoDTensor seq should not be null."); + + const size_t level = 0; + framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); + + auto seq_dims = seq.dims(); + PADDLE_ENFORCE_EQ(seq_dims[0], abs_offset_lod[level].back(), + "The first dimension of LoDTensor seq should be " + "equal to the sum of all sequences's length."); + + auto padding_dims = padding.dims(); + PADDLE_ENFORCE_EQ(padding_dims.size(), 3UL, + "The input padding should be a 3-D Tensor of shape " + "[max_sequnece_length, num_sequences, sequence_width]."); + + const size_t max_sequence_length = MaximumSequenceLength(lod, level); + PADDLE_ENFORCE_EQ(padding_dims[0], max_sequence_length, + "The first dimension of Tensor padding should be " + "the maximum length of all sequences in LoDTensor seq."); + + const size_t num_sequences = abs_offset_lod[level].size() - 1; + PADDLE_ENFORCE_EQ(padding_dims[1], num_sequences, + "The second dimension of Tensor padding should be " + "the number of sequences in LoDTensor seq."); + + const size_t sequence_width = seq.numel() / seq_dims[0]; + PADDLE_ENFORCE_EQ(padding_dims[2], sequence_width, + "The third dimension of Tensor padding should be the " + "width of sequence in LoDTensor seq."); + + const T* padding_data = padding.data(); + T* seq_data = seq.data(); + for (size_t i = 0; i < num_sequences; ++i) { + size_t start_pos = abs_offset_lod[level][i]; + size_t sequence_length = abs_offset_lod[level][i + 1] - start_pos; + for (size_t j = 0; j < sequence_length; ++j) { + // sequence_width > j > 0 + T scale = + norm_by_times ? (1.0f / static_cast(sequence_length)) : 1.0f; + for (size_t k = 0; k < sequence_width; ++k) { + seq_data[(start_pos + j) * sequence_width + k] = + padding_data[(j * num_sequences + i) * sequence_width + k] * + scale; + } + } + } + } +}; + +template class PaddingLoDTensorFunctor; +template class UnpaddingLoDTensorFunctor; + +} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/math/sequence_padding.cu b/paddle/operators/math/sequence_padding.cu new file mode 100644 index 00000000000..e4be178f815 --- /dev/null +++ b/paddle/operators/math/sequence_padding.cu @@ -0,0 +1,209 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/math/sequence_padding.h" + +namespace paddle { +namespace operators { +namespace math { + +template +__global__ void SequencePaddingKernel(T* padding, T* sequence, + const size_t* sequence_start_positions, + const size_t sequence_width, + const size_t max_sequence_length, + const size_t num_sequences) { + size_t padding_idx = blockIdx.y; + size_t start_pos = sequence_start_positions[padding_idx]; + size_t sequence_length = + sequence_start_positions[padding_idx + 1] - start_pos; + + size_t sequence_idx = blockIdx.x * blockDim.y + threadIdx.y; + size_t padding_base_idx = + (sequence_idx * num_sequences + padding_idx) * sequence_width; + size_t sequence_base_idx = (start_pos + sequence_idx) * sequence_width; + + if (sequence_idx < sequence_length) { + T scale = NormByTimes ? (1.0f / static_cast(sequence_length)) : 1.0f; + if (Padding) { + /* sequence -> padding */ + for (size_t i = threadIdx.x; i < sequence_width; i += blockDim.x) { + padding[padding_base_idx + i] = scale * sequence[sequence_base_idx + i]; + } + } else { + /* padding -> sequence */ + for (size_t i = threadIdx.x; i < sequence_width; i += blockDim.x) { + sequence[sequence_base_idx + i] = scale * padding[padding_base_idx + i]; + } + } + } else if (sequence_idx < max_sequence_length) { + if (Padding) { + /* sequence -> padding */ + for (size_t i = threadIdx.x; i < sequence_width; i += blockDim.x) { + padding[padding_base_idx + i] = 0; + } + } + } +} + +template +class PaddingLoDTensorFunctor { + public: + void operator()(const platform::CUDADeviceContext& context, + const framework::LoDTensor& seq, framework::Tensor& padding, + bool norm_by_times) { + auto lod = seq.lod(); + PADDLE_ENFORCE_GT(lod.size(), 0UL, + "The lod of LoDTensor seq should not be null."); + + const size_t level = 0; + framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); + + auto seq_dims = seq.dims(); + PADDLE_ENFORCE_EQ(seq_dims[0], abs_offset_lod[level].back(), + "The first dimension of LoDTensor seq should be " + "equal to the sum of all sequences's length."); + + auto padding_dims = padding.dims(); + PADDLE_ENFORCE_EQ(padding_dims.size(), 3UL, + "The input padding should be a 3-D Tensor of shape " + "[max_sequence_length, num_sequences, sequence_width]."); + + size_t max_sequence_length = MaximumSequenceLength(lod, level); + PADDLE_ENFORCE_EQ(padding_dims[0], max_sequence_length, + "The first dimension of Tensor padding should be the " + "maximum length of all sequences in LoDTensor seq."); + + const size_t num_sequences = abs_offset_lod[level].size() - 1; + PADDLE_ENFORCE_EQ(padding_dims[1], num_sequences, + "The second dimension of Tensor padding should be the " + "number of sequences in LoDTensor seq."); + + const size_t sequence_width = seq.numel() / seq_dims[0]; + PADDLE_ENFORCE_EQ(padding_dims[2], sequence_width, + "The third dimension of Tensor padding should be the " + "width of sequence in LoDTensor seq."); + + if (!norm_by_times && num_sequences == 1UL) { + Copy(seq, context.GetPlace(), context, &padding); + padding.Resize(padding_dims); + return; + } + + const size_t kBlockSize = 512; + + /* At least use 32 threads to copy sequence_width elements, + * and at least 8 elements for each thread. + */ + size_t block_dim_x = + std::min(((((sequence_width + 7) >> 3) + 31) >> 5) << 5, kBlockSize); + size_t block_dim_y = kBlockSize / block_dim_x; + dim3 threads(block_dim_x, block_dim_y); + + size_t grid_dim_x = (max_sequence_length + block_dim_y - 1) / block_dim_y; + size_t grid_dim_y = num_sequences; + dim3 grid(grid_dim_x, grid_dim_y); + + const T* seq_data = seq.data(); + T* padding_data = padding.data(); + if (norm_by_times) { + SequencePaddingKernel<<>>( + padding_data, const_cast(seq_data), abs_offset_lod[level].data(), + sequence_width, max_sequence_length, num_sequences); + } else { + SequencePaddingKernel<<>>( + padding_data, const_cast(seq_data), abs_offset_lod[level].data(), + sequence_width, max_sequence_length, num_sequences); + } + } +}; + +template +class UnpaddingLoDTensorFunctor { + public: + void operator()(const platform::CUDADeviceContext& context, + framework::LoDTensor& seq, const framework::Tensor& padding, + bool norm_by_times) { + auto lod = seq.lod(); + PADDLE_ENFORCE_GT(lod.size(), 0UL, + "The lod of LoDTensor seq should not be null."); + + const size_t level = 0; + framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); + + auto seq_dims = seq.dims(); + PADDLE_ENFORCE_EQ(seq_dims[0], abs_offset_lod[level].back(), + "The first dimension of LoDTensor seq should be " + "equal to the sum of all sequences's length."); + + auto padding_dims = padding.dims(); + PADDLE_ENFORCE_EQ(padding_dims.size(), 3UL, + "The input padding should be a 3-D Tensor of shape " + "[max_sequnece_length, num_sequences, sequence_width]."); + + size_t max_sequence_length = MaximumSequenceLength(lod, level); + PADDLE_ENFORCE_EQ(padding_dims[0], max_sequence_length, + "The first dimension of Tensor padding should be " + "the maximum length of all sequences in LoDTensor seq."); + + const size_t num_sequences = abs_offset_lod[level].size() - 1; + PADDLE_ENFORCE_EQ(padding_dims[1], num_sequences, + "The second dimension of Tensor padding should be " + "the number of sequences in LoDTensor seq."); + + const size_t sequence_width = seq.numel() / seq_dims[0]; + PADDLE_ENFORCE_EQ(padding_dims[2], sequence_width, + "The third dimension of Tensor padding should be the " + "width of sequence in LoDTensor seq."); + + if (!norm_by_times && num_sequences == 1UL) { + Copy(padding, context.GetPlace(), context, &seq); + seq.Resize(seq_dims); + return; + } + + const size_t kBlockSize = 512; + + /* At least use 32 threads to copy sequence_width elements, + * and at least 8 elements for each thread. + */ + size_t block_dim_x = + std::min(((((sequence_width + 7) >> 3) + 31) >> 5) << 5, kBlockSize); + size_t block_dim_y = kBlockSize / block_dim_x; + dim3 threads(block_dim_x, block_dim_y); + + size_t grid_dim_x = (max_sequence_length + block_dim_y - 1) / block_dim_y; + size_t grid_dim_y = num_sequences; + dim3 grid(grid_dim_x, grid_dim_y); + + const T* padding_data = padding.data(); + T* seq_data = seq.data(); + if (norm_by_times) { + SequencePaddingKernel<<>>( + const_cast(padding_data), seq_data, abs_offset_lod[level].data(), + sequence_width, max_sequence_length, num_sequences); + } else { + SequencePaddingKernel<<>>( + const_cast(padding_data), seq_data, abs_offset_lod[level].data(), + sequence_width, max_sequence_length, num_sequences); + } + } +}; + +template class PaddingLoDTensorFunctor; +template class UnpaddingLoDTensorFunctor; + +} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/math/sequence_padding.h b/paddle/operators/math/sequence_padding.h new file mode 100644 index 00000000000..8f586c5eb46 --- /dev/null +++ b/paddle/operators/math/sequence_padding.h @@ -0,0 +1,79 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/framework/lod_tensor.h" +#include "paddle/platform/device_context.h" + +namespace paddle { +namespace operators { +namespace math { + +inline static size_t MaximumSequenceLength(const framework::LoD& lod, + const size_t level) { + const size_t num_sequences = lod[level].size() - 1; + size_t max_sequence_length = 0; + framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); + for (size_t i = 0; i < num_sequences; ++i) { + max_sequence_length = + std::max(max_sequence_length, + abs_offset_lod[level][i + 1] - abs_offset_lod[level][i]); + } + return max_sequence_length; +} + +/* + * \brief Padding/Unpadding LoDTensor to/from normal Tensor of the shape + * [max_sequence_length, num_sequences, sequence_width]. + * + * Padding sequence: + * padding[i] = seq[lod[level][i]] + * Unpadding sequence: + * seq[lod[level][i]] = padding[i] + * + * All sequences will be padded to the same length and stored in a transposed + * shape. + * Example: + * seq (s0, s0, s0, s0; s1, s1; s2, s2, s2; s3) + * padding (s0, s1, s2, s3; s0, s1, s2, 0; s0, 0, s2, 0; s0, 0, 0, 0) + * + * \param context device context of this functor. + * \param seq LoDTensor which is stored in sequence format, the shape + * is [total_sequence_length, sequence_width] where + * total_sequence_length is the sum of all sequences' + * length. + * \param padding Tensor which is padded to the same length, the shape is + * [max_sequence_length, num_sequences, sequence_width]. + * \param norm_by_times whether dividing sequence's length. + * + * \note transposition is also done in this functor. + */ +template +class PaddingLoDTensorFunctor { + public: + void operator()(const DeviceContext& context, const framework::LoDTensor& seq, + framework::Tensor& padding, bool norm_by_times); +}; + +template +class UnpaddingLoDTensorFunctor { + public: + void operator()(const DeviceContext& context, framework::LoDTensor& seq, + const framework::Tensor& padding, bool norm_by_times); +}; + +} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/math/sequence_padding_test.cc b/paddle/operators/math/sequence_padding_test.cc new file mode 100644 index 00000000000..9799bcd65dc --- /dev/null +++ b/paddle/operators/math/sequence_padding_test.cc @@ -0,0 +1,104 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/math/sequence_padding.h" +#include + +template +void TestSequencePadding(const paddle::framework::LoD& lod, + const size_t sequence_width) { + paddle::framework::LoDTensor cpu_seq; + paddle::framework::LoDTensor cpu_seq_back; + paddle::framework::LoDTensor seq; + paddle::framework::LoDTensor seq_back; + paddle::framework::Tensor padding; + + const size_t level = lod.size() - 1; + auto seq_dims = + paddle::framework::make_ddim({static_cast(lod[level].back()), + static_cast(sequence_width)}); + + cpu_seq.set_lod(lod); + cpu_seq.mutable_data(seq_dims, paddle::platform::CPUPlace()); + for (size_t i = 0; i < cpu_seq.numel(); ++i) { + cpu_seq.data()[i] = static_cast(i); + } + + auto* place = new Place(); + DeviceContext* context = new DeviceContext(*place); + if (paddle::platform::is_cpu_place(*place)) { + seq = cpu_seq; + } else { + Copy(cpu_seq, *place, *context, &seq); + seq.set_lod(lod); + } + + const size_t max_sequence_length = + paddle::operators::math::MaximumSequenceLength(lod, level); + const size_t num_sequences = lod[level].size() - 1; + auto padding_dims = + paddle::framework::make_ddim({static_cast(max_sequence_length), + static_cast(num_sequences), + static_cast(sequence_width)}); + padding.mutable_data(padding_dims, *place); + paddle::operators::math::PaddingLoDTensorFunctor()( + *context, seq, padding, false); + + seq_back.set_lod(lod); + seq_back.mutable_data(seq_dims, *place); + paddle::operators::math::UnpaddingLoDTensorFunctor()( + *context, seq_back, padding, false); + + if (paddle::platform::is_cpu_place(*place)) { + cpu_seq_back = seq_back; + } else { + Copy(seq_back, paddle::platform::CPUPlace(), *context, &cpu_seq_back); + cpu_seq_back.set_lod(lod); + } + + EXPECT_EQ(cpu_seq.numel(), cpu_seq_back.numel()); + EXPECT_EQ(cpu_seq.dims(), cpu_seq_back.dims()); + for (size_t i = 0; i < cpu_seq.numel(); ++i) { + EXPECT_EQ(cpu_seq.data()[i], cpu_seq_back.data()[i]); + } + + delete place; + delete context; +}; + +TEST(Seq2BatchPadding, CPU) { + paddle::framework::LoD lod1; + lod1.push_back(std::vector{0, 10}); + TestSequencePadding(lod1, 16); + + paddle::framework::LoD lod2; + lod2.push_back(std::vector{0, 2, 7, 10}); + TestSequencePadding(lod2, 128); +} + +#ifdef PADDLE_WITH_CUDA +TEST(SequencePadding, CUDA) { + paddle::framework::LoD lod1; + lod1.push_back(std::vector{0, 10}); + TestSequencePadding(lod1, 16); + + paddle::framework::LoD lod2; + lod2.push_back(std::vector{0, 2, 7, 10}); + TestSequencePadding(lod2, 128); +} +#endif diff --git a/paddle/operators/warpctc_op.cc b/paddle/operators/warpctc_op.cc new file mode 100644 index 00000000000..bd0c5f99576 --- /dev/null +++ b/paddle/operators/warpctc_op.cc @@ -0,0 +1,141 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/warpctc_op.h" + +namespace paddle { +namespace operators { + +class WarpCTCOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("Logits"), + "Input(Logits) of WarpCTCOp should not be null."); + PADDLE_ENFORCE(ctx->HasInput("Label"), + "Input(Label) of WarpCTCOp should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("WarpCTCGrad"), + "Output(WarpCTCGrad) of WarpCTCOp should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("Loss"), + "Output(Loss) of WarpCTCOp should not be null."); + + auto logits_dims = ctx->GetInputDim("Logits"); + int sequence_width = + static_cast(framework::product(logits_dims) / logits_dims[0]); + int blank = ctx->Attrs().Get("blank"); + PADDLE_ENFORCE((blank >= 0) && (blank < sequence_width), + "The value of Attr(blank) should be in interval [0, %d).", + sequence_width); + // TODO(liuyiqun): it is tricky to set the wrong dimension here. + ctx->SetOutputDim("Loss", {logits_dims[0], 1}); + } + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override { + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Logits")->type()), + ctx.device_context()); + } +}; + +class WarpCTCOpMaker : public framework::OpProtoAndCheckerMaker { + public: + WarpCTCOpMaker(OpProto* proto, OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("Logits", + "(LodTensor, default: LoDTensor), the unscaled " + "probabilities of variable-length sequences, which is a 2-D " + "Tensor with LoD information. It's shape is " + "[Lp, num_classes + 1], where Lp is the sum of all input " + "sequences' length and num_classes is the true number of classes " + "(not including the blank label)."); + AddInput("Label", + "(LodTensor, default: LoDTensor), the ground truth " + "of variable-length sequence, which is a 2-D Tensor with LoD " + "information. It is of the shape [Lg, 1], where Lg is th sum of " + "all labels' length."); + AddOutput("WarpCTCGrad", + "(Tensor, default: Tensor), a temporary " + "output Tensor to store the gradients of warp-ctc, which is " + "computed with loss together in one call. It is a 3-D Tensor of " + "the shape [max_sequence_length, batch_size, num_classes + 1].") + .AsIntermediate(); + AddOutput("Loss", + "(Tensor, default: Tensor), the Connectionist " + "Temporal Classification (CTC) loss, which is a 2-D Tensor of " + "the shape [batch_size, 1]"); + AddAttr("blank", + "(int, default: 0), the blank label of Connectionist " + "Temporal Classification (CTC) loss, which is in the " + "half-opened interval [0, num_classes + 1).") + .SetDefault(0); + AddAttr("norm_by_times", + "(bool, default: false), whether to " + "normalize the gradients by the number of time-step, " + "which is also the sequence's length.") + .SetDefault(false); + AddComment(R"DOC( +An operator integrating the open-source +[warp-ctc](https://github.com/baidu-research/warp-ctc) library, which is used in +[Deep Speech 2: End-toEnd Speech Recognition in English and Mandarin]( +https://arxiv.org/pdf/1512.02595v1.pdf), +to compute Connectionist Temporal Classification (CTC) loss. +It can be aliased as softmax with ctc, since a native softmax activation is +interated to the warp-ctc library, to to normlize values for each row of the +input tensor. + +More detail of CTC loss can be found by refering to +[Connectionist Temporal Classification: Labelling Unsegmented Sequence Data with +Recurrent Neural Networks]( +http://machinelearning.wustl.edu/mlpapers/paper_files/icml2006_GravesFGS06.pdf). +)DOC"); + } +}; + +class WarpCTCGradOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("WarpCTCGrad"), + "Input(WarpCTCGrad) of WarpCTCGradOp should not be null."); + PADDLE_ENFORCE(ctx->HasOutput(framework::GradVarName("Logits")), + "Output(Logits@GRAD) of WarpCTCGradOp should not be null."); + ctx->SetOutputDim(framework::GradVarName("Logits"), + ctx->GetInputDim("Logits")); + ctx->ShareLoD("Logits", /*->*/ framework::GradVarName("Logits")); + } + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override { + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Logits")->type()), + ctx.device_context()); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP(warpctc, ops::WarpCTCOp, ops::WarpCTCOpMaker, warpctc_grad, + ops::WarpCTCGradOp); +REGISTER_OP_CPU_KERNEL( + warpctc, ops::WarpCTCKernel); +REGISTER_OP_CPU_KERNEL( + warpctc_grad, + ops::WarpCTCGradKernel); diff --git a/paddle/operators/warpctc_op.cu.cc b/paddle/operators/warpctc_op.cu.cc new file mode 100644 index 00000000000..7d8527ac75f --- /dev/null +++ b/paddle/operators/warpctc_op.cu.cc @@ -0,0 +1,22 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/warpctc_op.h" + +namespace ops = paddle::operators; +REGISTER_OP_CUDA_KERNEL( + warpctc, ops::WarpCTCKernel); +REGISTER_OP_CUDA_KERNEL( + warpctc_grad, + ops::WarpCTCGradKernel); diff --git a/paddle/operators/warpctc_op.h b/paddle/operators/warpctc_op.h new file mode 100644 index 00000000000..41899c7fe0c --- /dev/null +++ b/paddle/operators/warpctc_op.h @@ -0,0 +1,218 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/framework/op_registry.h" +#include "paddle/operators/math/math_function.h" +#include "paddle/operators/math/sequence_padding.h" +#include "paddle/platform/dynload/warpctc.h" + +namespace paddle { +namespace operators { + +using Tensor = framework::Tensor; +using LoDTensor = framework::LoDTensor; + +template +class WarpCTCFunctor { + public: + /* + * \brief Compute the connectionist temporal classification loss, + * and optionally compute the gradient with respect to the inputs. + * + * If gradient is nullptr, it only computes the ctc loss, + * or computes both ctc loss and gradient. + * + * \param ctx execution context of this functor + * \param input batch matrix of input probabilities, in + * max_sequence_length x num_sequences x + * sequence_width, (row-major) format + * \param gradient batch matrix of gradient, with the same shape as + * input. + * \param cpu_labels labels always in CPU memory. + * \param cpu_label_lengths length of all labels in CPU memory. + * \param cpu_input_lengths length of all sequences in CPU memory. + * \param sequence_width number of possible output symbols. + * \param num_sequences number of sequence. + * \param blank blank label used in ctc loss function. + * \param cpu_losss cost of each sequence in CPU memory. + */ + void operator()(const framework::ExecutionContext& ctx, const float* input, + float* gradient, const int* cpu_labels, + const int* cpu_label_lengths, const int* cpu_input_lengths, + const size_t sequence_width, const size_t num_sequences, + const size_t blank, float* cpu_loss) { + // Init warp-ctc options + init(ctx, blank); + + // Compute the required workspace size. + // There is no memory allocated operations within warp-ctc. + size_t workspace_bytes = 0; + ctcStatus_t status = platform::dynload::get_workspace_size( + cpu_label_lengths, cpu_input_lengths, static_cast(sequence_width), + static_cast(num_sequences), options_, &workspace_bytes); + PADDLE_ENFORCE_EQ(CTC_STATUS_SUCCESS, status, + "warp-ctc [version %d] Error in get_workspace_size: ", + warpctc_version_, + platform::dynload::ctcGetStatusString(status)); + PADDLE_ENFORCE_GT(workspace_bytes, 0UL, + "Bytes of workspace got by warp-ctc function, " + "get_workspace_size(), should be larger than 0."); + + Tensor workspace; + size_t workspace_elements = workspace_bytes / sizeof(float) + 1UL; + float* workspace_data = workspace.mutable_data( + framework::make_ddim({static_cast(workspace_elements)}), + ctx.GetPlace()); + math::SetConstant()( + ctx.template device_context(), &workspace, + static_cast(0)); + + // compute loss and gradient + status = platform::dynload::compute_ctc_loss( + input, gradient, cpu_labels, cpu_label_lengths, cpu_input_lengths, + static_cast(sequence_width), static_cast(num_sequences), + cpu_loss, workspace_data, options_); + PADDLE_ENFORCE_EQ(CTC_STATUS_SUCCESS, status, + "warp-ctc [version %d] Error in compute_ctc_loss: ", + warpctc_version_, + platform::dynload::ctcGetStatusString(status)); + } + + protected: + void init(const framework::ExecutionContext& ctx, const size_t blank) { + warpctc_version_ = platform::dynload::get_warpctc_version(); + + if (platform::is_gpu_place(ctx.GetPlace())) { +#ifdef PADDLE_WITH_CUDA + options_.loc = CTC_GPU; + options_.stream = reinterpret_cast( + ctx.device_context()) + .stream(); +#else + PADDLE_THROW("[warpctc init] GPU is not enabled."); +#endif + } else { + options_.loc = CTC_CPU; + options_.num_threads = 1; + } + + options_.blank_label = blank; + } + + private: + int warpctc_version_; + ctcOptions options_; +}; + +template +class WarpCTCKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* logits = ctx.Input("Logits"); + auto* label = ctx.Input("Label"); + auto* warpctc_grad = ctx.Output("WarpCTCGrad"); + auto* loss = ctx.Output("Loss"); + + const size_t level = 0; + + auto logits_lod = framework::ToAbsOffset(logits->lod()); + auto logits_dims = logits->dims(); + PADDLE_ENFORCE_EQ(logits_dims[0], + static_cast(logits_lod[level].back()), + "The first dimension of Input(Logits) should be equal to " + "the sum of all sequences' lengths."); + + auto label_lod = framework::ToAbsOffset(label->lod()); + auto label_dims = label->dims(); + PADDLE_ENFORCE_EQ( + label_dims[0], label->numel(), + "The width of each timestep in Input(Label) should be 1."); + + const size_t num_sequences = logits_lod[level].size() - 1; + PADDLE_ENFORCE_EQ(num_sequences, label_lod[level].size() - 1, + "The number of sequences of Input(Logits) should be " + "equal to that of Input(Label)."); + + const size_t sequence_width = logits->numel() / logits_dims[0]; + auto loss_dims = + framework::make_ddim({static_cast(num_sequences), 1}); + + // warpctc needs sequences data stored in transposed padding format + Tensor warpctc_logits; + const size_t max_sequence_length = + math::MaximumSequenceLength(logits_lod, level); + auto warpctc_logits_dims = + framework::make_ddim({static_cast(max_sequence_length), + static_cast(num_sequences), + static_cast(sequence_width)}); + warpctc_logits.mutable_data(warpctc_logits_dims, ctx.GetPlace()); + math::PaddingLoDTensorFunctor()( + ctx.template device_context(), *logits, warpctc_logits, + false); + const T* warpctc_logits_data = warpctc_logits.data(); + + std::vector warpctc_label_lengths(num_sequences); + std::vector warpctc_logits_lengths(num_sequences); + + for (size_t i = 0; i < num_sequences; ++i) { + warpctc_label_lengths[i] = label_lod[level][i + 1] - label_lod[level][i]; + warpctc_logits_lengths[i] = + logits_lod[level][i + 1] - logits_lod[level][i]; + } + + // warpctc computes loss and gradient in one call, gradient data also stored + // in batch format + T* warpctc_grad_data = + warpctc_grad->mutable_data(warpctc_logits.dims(), ctx.GetPlace()); + + // warpctc accesses labels in CPU memory + Tensor warpctc_label; + Copy(*label, platform::CPUPlace(), ctx.device_context(), &warpctc_label); + const int* warpctc_label_data = warpctc_label.data(); + + // warpctc stores loss in CPU memory + Tensor warpctc_loss; + T* warpctc_loss_data = + warpctc_loss.mutable_data(loss_dims, platform::CPUPlace()); + + const size_t blank = static_cast(ctx.Attr("blank")); + + WarpCTCFunctor()( + ctx, warpctc_logits_data, warpctc_grad_data, warpctc_label_data, + warpctc_label_lengths.data(), warpctc_logits_lengths.data(), + sequence_width, num_sequences, blank, warpctc_loss_data); + + // Copy the loss back + Copy(warpctc_loss, ctx.GetPlace(), ctx.device_context(), loss); + } +}; + +template +class WarpCTCGradKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* warpctc_grad = ctx.Input("WarpCTCGrad"); + auto* logits_grad = ctx.Output(framework::GradVarName("Logits")); + + bool norm_by_times = ctx.Attr("norm_by_times"); + math::UnpaddingLoDTensorFunctor()( + ctx.template device_context(), *logits_grad, + *warpctc_grad, norm_by_times); + } +}; + +} // namespace operators +} // namespace paddle diff --git a/paddle/platform/dynload/CMakeLists.txt b/paddle/platform/dynload/CMakeLists.txt index f4fda65907d..cf2081b4349 100644 --- a/paddle/platform/dynload/CMakeLists.txt +++ b/paddle/platform/dynload/CMakeLists.txt @@ -1,3 +1,4 @@ cc_library(dynamic_loader SRCS dynamic_loader.cc DEPS glog gflags enforce) nv_library(dynload_cuda SRCS cublas.cc cudnn.cc curand.cc nccl.cc DEPS dynamic_loader nccl) +cc_library(dynload_warpctc SRCS warpctc.cc DEPS dynamic_loader warpctc) diff --git a/paddle/platform/dynload/cublas.cc b/paddle/platform/dynload/cublas.cc index 9cd2a1f5655..6aca716657c 100644 --- a/paddle/platform/dynload/cublas.cc +++ b/paddle/platform/dynload/cublas.cc @@ -12,7 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include +#include "paddle/platform/dynload/cublas.h" namespace paddle { namespace platform { diff --git a/paddle/platform/dynload/warpctc.cc b/paddle/platform/dynload/warpctc.cc new file mode 100644 index 00000000000..9b7d01a6e8f --- /dev/null +++ b/paddle/platform/dynload/warpctc.cc @@ -0,0 +1,30 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/platform/dynload/warpctc.h" + +namespace paddle { +namespace platform { +namespace dynload { + +std::once_flag warpctc_dso_flag; +void* warpctc_dso_handle = nullptr; + +#define DEFINE_WRAP(__name) DynLoad__##__name __name + +WARPCTC_ROUTINE_EACH(DEFINE_WRAP); + +} // namespace dynload +} // namespace platform +} // namespace paddle diff --git a/paddle/platform/dynload/warpctc.h b/paddle/platform/dynload/warpctc.h new file mode 100644 index 00000000000..acafcaff2cc --- /dev/null +++ b/paddle/platform/dynload/warpctc.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include +#include +#include "ctc.h" +#include "paddle/platform/dynload/dynamic_loader.h" + +namespace paddle { +namespace platform { +namespace dynload { + +extern std::once_flag warpctc_dso_flag; +extern void* warpctc_dso_handle; + +/** + * The following macro definition can generate structs + * (for each function) to dynamic load warpctc routine + * via operator overloading. + */ +#define DYNAMIC_LOAD_WARPCTC_WRAP(__name) \ + struct DynLoad__##__name { \ + template \ + auto operator()(Args... args) -> decltype(__name(args...)) { \ + using warpctcFunc = decltype(__name(args...)) (*)(Args...); \ + std::call_once(warpctc_dso_flag, \ + paddle::platform::dynload::GetWarpCTCDsoHandle, \ + &warpctc_dso_handle); \ + void* p_##_name = dlsym(warpctc_dso_handle, #__name); \ + return reinterpret_cast(p_##_name)(args...); \ + } \ + }; \ + extern DynLoad__##__name __name + +#define DECLARE_DYNAMIC_LOAD_WARPCTC_WRAP(__name) \ + DYNAMIC_LOAD_WARPCTC_WRAP(__name) + +#define WARPCTC_ROUTINE_EACH(__macro) \ + __macro(get_warpctc_version); \ + __macro(ctcGetStatusString); \ + __macro(compute_ctc_loss); \ + __macro(get_workspace_size) + +WARPCTC_ROUTINE_EACH(DECLARE_DYNAMIC_LOAD_WARPCTC_WRAP); + +#undef DYNAMIC_LOAD_WARPCTC_WRAP + +} // namespace dynload +} // namespace platform +} // namespace paddle diff --git a/python/paddle/v2/fluid/tests/test_sequence_softmax_op.py b/python/paddle/v2/fluid/tests/test_sequence_softmax_op.py index b54a56aa6d3..8bffdd58569 100644 --- a/python/paddle/v2/fluid/tests/test_sequence_softmax_op.py +++ b/python/paddle/v2/fluid/tests/test_sequence_softmax_op.py @@ -1,13 +1,7 @@ import unittest import numpy as np from op_test import OpTest - - -def stable_softmax(x): - """Compute the softmax of vector x in a numerically stable way.""" - shiftx = x - np.max(x).clip(-64.) - exps = np.exp(shiftx) - return exps / np.sum(exps) +from test_softmax_op import stable_softmax class TestSequenceSoftmaxOp(OpTest): diff --git a/python/paddle/v2/fluid/tests/test_warpctc_op.py b/python/paddle/v2/fluid/tests/test_warpctc_op.py new file mode 100644 index 00000000000..59390d5303b --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_warpctc_op.py @@ -0,0 +1,200 @@ +import sys +import unittest +import numpy as np +from op_test import OpTest +from test_softmax_op import stable_softmax + + +class CTCForward(object): + def __init__(self, softmax, softmax_lod, labels, labels_lod, blank, + norm_by_times): + self.softmax = softmax + self.softmax_lod = softmax_lod + assert labels.shape[1] == 1 + self.labels = labels + self.labels_lod = labels_lod + self.blank = blank + self.norm_by_times = norm_by_times + + self.level = 0 + self.num_classes = softmax.shape[1] + self.batch_size = len(softmax_lod[self.level]) - 1 + assert self.batch_size == len(labels_lod[self.level]) - 1 + + self.loss = np.zeros([self.batch_size, 1], dtype="float32") + self.gradient = np.zeros(self.softmax.shape, dtype="float32") + + # float64 + self.EXP_MAX = sys.float_info.max + self.EXP_MIN = sys.float_info.min + self.LOG_ZERO = np.log(self.EXP_MIN) + self.LOG_INFINITY = np.log(self.EXP_MAX) + + def safe_exp(self, x): + if x <= self.LOG_ZERO: + return 0.0 + if x >= self.LOG_INFINITY: + return self.EXP_MAX + return np.exp(x) + + def safe_log(self, x): + if x <= self.EXP_MIN: + return self.LOG_ZERO + return np.log(x) + + # x = lna and y = lnb are in log scale, ln(a / b) = lna - lnb + def log_div(self, x, y): + res = x - y + if res <= self.LOG_ZERO: + return self.LOG_ZERO + if res >= self.LOG_INFINITY: + return self.LOG_INFINITY + return res + + # x = lna and y = lnb are in log scale, ln(a * b) = lna + lnb + def log_mul(self, x, y): + res = x + y + if res <= self.LOG_ZERO: + return self.LOG_ZERO + if res >= self.LOG_INFINITY: + return self.LOG_INFINITY + return res + + # x = lna and y = lnb are in log scale, + # ln(a + b) = lna + ln(1 + exp(lnb - lna)), where b > a + def log_add(self, x, y): + if x < y: + t = y + y = x + x = t + return x + self.safe_log(1 + self.safe_exp(y - x)) + + def segment_range(self, time, total_times, total_segments): + start = max(0, total_segments - (2 * (total_times - time))) + end = min(total_segments, 2 * (time + 1)) + return start, end + + def forward_a_sequence(self, softmax_a_sequence, labels_a_sequence): + total_times = softmax_a_sequence.shape[0] + total_segments = labels_a_sequence.shape[0] * 2 + 1 + + required_times = labels_a_sequence.shape[0] + old_label = -1 + for i in range(labels_a_sequence.shape[0]): + # two contingous labels with the same value + if labels_a_sequence[i, 0] == old_label: + required_times = required_times + 1 + old_label = labels_a_sequence[i, 0] + + if total_times < required_times: + return 0 + + # calculate the forward and backward variables, + # reference Chapter 7.3 of "Alex Grave, Supervised Sequence + # Labelling with Recurrent Neural Networks" + log_acts = np.zeros([total_times, self.num_classes], dtype="float32") + for i in range(total_times): + for j in range(self.num_classes): + log_acts[i, j] = self.safe_log(softmax_a_sequence[i, j]) + + # calculate the forward variables + forward_vars = np.zeros([total_times, total_segments], dtype="float32") + for i in range(total_times): + for j in range(total_segments): + forward_vars[i, j] = self.LOG_ZERO + + for i in range(total_times): + # dp initialization at t0 + if i == 0: + forward_vars[i, 0] = log_acts[0, self.blank] + if total_segments > 1: + forward_vars[i, 1] = log_acts[0, labels_a_sequence[i, 0]] + continue + + # dp from t1 + start, end = self.segment_range(i, total_times, total_segments) + for k in range(end - start): + j = k + start + if j & 1 == 1: + label_idx = j / 2 + label_val = labels_a_sequence[label_idx, 0] + fv = self.log_add(forward_vars[i - 1, j], + forward_vars[i - 1, j - 1]) + if j > 1 and label_val != labels_a_sequence[label_idx - 1, + 0]: + fv = self.log_add(fv, forward_vars[i - 1, j - 2]) + fv = self.log_mul(fv, log_acts[i, label_val]) + else: + fv = forward_vars[i - 1, j] + if j > 0: + fv = self.log_add(fv, forward_vars[i - 1, j - 1]) + fv = self.log_mul(fv, log_acts[i, self.blank]) + forward_vars[i, j] = fv + + # sum the last two value as log_prob + log_prob = forward_vars[total_times - 1, total_segments - 1] + if total_segments > 1: + log_prob = self.log_add( + log_prob, forward_vars[total_times - 1, total_segments - 2]) + + return -log_prob + + def forward(self): + for i in range(self.batch_size): + softmax_start_i = self.softmax_lod[self.level][i] + softmax_end_i = self.softmax_lod[self.level][i + 1] + labels_start_i = self.labels_lod[self.level][i] + labels_end_i = self.labels_lod[self.level][i + 1] + + softmax_a_sequence = self.softmax[softmax_start_i:softmax_end_i, :] + labels_a_sequence = self.labels[labels_start_i:labels_end_i, :] + self.loss[i] = self.forward_a_sequence(softmax_a_sequence, + labels_a_sequence) + return self.loss + + +class TestWarpCTCOp(OpTest): + def setUp(self): + self.op_type = "warpctc" + + batch_size = 4 + num_classes = 8 + logits_lod = [[0, 4, 5, 8, 11]] + logits = np.random.uniform(0.1, 1.0, + [11, num_classes]).astype("float32") + softmax = np.apply_along_axis(stable_softmax, 1, logits) + labels_lod = [[0, 3, 4, 8, 12]] + # labels should not be blank + labels = np.random.randint(0, num_classes - 1, [12, 1], dtype="int32") + + blank = num_classes - 1 + norm_by_times = False + + ctc = CTCForward(softmax, logits_lod, labels, labels_lod, blank, + norm_by_times) + loss = ctc.forward() + + max_sequence_length = 0 + for i in range(batch_size): + max_sequence_length = max(max_sequence_length, + logits_lod[0][i + 1] - logits_lod[0][i]) + gradient = np.zeros( + [max_sequence_length, batch_size, num_classes], dtype="float32") + + self.inputs = { + "Logits": (logits, logits_lod), + "Label": (labels, labels_lod) + } + self.outputs = {"Loss": loss} + self.attrs = {"blank": blank, "norm_by_times": norm_by_times} + + def test_check_output(self): + self.check_output() + + +# def test_check_grad(self): +# self.outputs["WarpCTCGrad"] = None +# self.check_grad(["Logits"], "Loss", max_relative_error=0.01) + +if __name__ == "__main__": + unittest.main() -- GitLab From 452caa0954a855df13ef72bf65f5a3872eaf5ee7 Mon Sep 17 00:00:00 2001 From: zhangchao Date: Tue, 9 Jan 2018 19:08:56 +0800 Subject: [PATCH 0157/2305] Update plotlog.py --- benchmark/paddle/image/plotlog.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/benchmark/paddle/image/plotlog.py b/benchmark/paddle/image/plotlog.py index 4ac8edfeafc..41f535d9850 100644 --- a/benchmark/paddle/image/plotlog.py +++ b/benchmark/paddle/image/plotlog.py @@ -79,10 +79,10 @@ def plot_metric(metric, plt.figure() plt.title(graph_title) if line_num == 1: - plt.plot(batch_id, metric, line_style, line_label) + plt.plot(batch_id, metric, line_style, label=line_label) else: for i in range(line_num): - plt.plot(batch_id, metric[i], line_style[i], line_label[i]) + plt.plot(batch_id, metric[i], line_style[i], label=line_label[i]) plt.xlabel('batch') plt.ylabel(graph_title) plt.legend() @@ -102,12 +102,12 @@ def main(): accuracy_sample = sample(accuracy, args.sample_rate) plot_metric(loss_sample, batch_sample, 'loss', line_label='loss') - plot_metric(accuracy_sample, - batch_sample, - 'accuracy', - line_style='g-', - line_label='accuracy') - + plot_metric( + accuracy_sample, + batch_sample, + 'accuracy', + line_style='g-', + line_label='accuracy') if __name__ == '__main__': main() -- GitLab From 893a15f5e25fbe347a12b066a45101b7e4bbce3d Mon Sep 17 00:00:00 2001 From: zhangchao Date: Tue, 9 Jan 2018 19:26:02 +0800 Subject: [PATCH 0158/2305] Update plotlog.py --- benchmark/paddle/image/plotlog.py | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmark/paddle/image/plotlog.py b/benchmark/paddle/image/plotlog.py index 41f535d9850..8679d4f272d 100644 --- a/benchmark/paddle/image/plotlog.py +++ b/benchmark/paddle/image/plotlog.py @@ -109,5 +109,6 @@ def main(): line_style='g-', line_label='accuracy') + if __name__ == '__main__': main() -- GitLab From e249ad1211e15f035c9c1385779f3d71e0c05a5b Mon Sep 17 00:00:00 2001 From: Yancey Date: Tue, 9 Jan 2018 19:44:36 +0800 Subject: [PATCH 0159/2305] Test dist word2vec (#7334) * test dist word2vec * multiple trainers work --- paddle/framework/executor.cc | 2 +- paddle/framework/executor.h | 2 - paddle/operators/recv_op.cc | 20 +++- paddle/operators/sum_op.h | 1 + .../book_distribute/test_dist_word2vec.py | 96 +++++++++++++++++++ 5 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 python/paddle/v2/fluid/tests/book_distribute/test_dist_word2vec.py diff --git a/paddle/framework/executor.cc b/paddle/framework/executor.cc index d8ef9a0fbaa..c0418c9266e 100644 --- a/paddle/framework/executor.cc +++ b/paddle/framework/executor.cc @@ -35,7 +35,7 @@ const std::string kFetchOpType = "fetch"; Executor::Executor(const platform::Place& place) : place_(place) {} -void CreateTensor(Variable* var, proto::VarDesc::VarType var_type) { +static void CreateTensor(Variable* var, proto::VarDesc::VarType var_type) { if (var_type == proto::VarDesc::LOD_TENSOR) { var->GetMutable(); } else if (var_type == proto::VarDesc::SELECTED_ROWS) { diff --git a/paddle/framework/executor.h b/paddle/framework/executor.h index 0b2b5780fed..d869e18901b 100644 --- a/paddle/framework/executor.h +++ b/paddle/framework/executor.h @@ -45,7 +45,5 @@ class Executor { const platform::Place place_; }; -void CreateTensor(Variable* var, proto::VarDesc::VarType var_type); - } // namespace framework } // namespace paddle diff --git a/paddle/operators/recv_op.cc b/paddle/operators/recv_op.cc index 6f65b87d3b0..9331c7b5634 100644 --- a/paddle/operators/recv_op.cc +++ b/paddle/operators/recv_op.cc @@ -32,6 +32,20 @@ limitations under the License. */ namespace paddle { namespace operators { +static void CreateTensorFromMessageType(framework::Variable *var, + sendrecv::VarType var_type) { + if (var_type == sendrecv::VarType::LOD_TENSOR) { + var->GetMutable(); + } else if (var_type == sendrecv::VarType::SELECTED_ROWS) { + var->GetMutable(); + } else { + PADDLE_THROW( + "VraibleMessage type %d is not in " + "[LoDTensor, SelectedRows]", + var_type); + } +} + void RunServer(Server **rpc_server, std::shared_ptr service, const std::string &server_address) { @@ -111,10 +125,10 @@ class RecvOp : public framework::OperatorBase { auto *merged_grad = recv_scope.FindVar(grad_var_name); if (merged_grad == nullptr) { auto *ptr = recv_scope.Var(grad_var_name); - framework::CreateTensor(ptr, - framework::ToVarType(merged_grad->Type())); + CreateTensorFromMessageType(ptr, v.second.type()); VLOG(3) << "Create Variable " << grad_var_name - << " on recv scope, which pointer is " << ptr; + << " on recv scope, which pointer is " << ptr << " type is " + << v.second.type(); } if (trainer_count > 1) { diff --git a/paddle/operators/sum_op.h b/paddle/operators/sum_op.h index 2c43097d717..48201b344de 100644 --- a/paddle/operators/sum_op.h +++ b/paddle/operators/sum_op.h @@ -70,6 +70,7 @@ class SumKernel : public framework::OpKernel { } else if (out_var->IsType()) { PADDLE_ENFORCE(!in_place, "SelectedRows not support inplace sum now"); auto *out = context.Output("Out"); + out->mutable_rows()->clear(); auto *out_value = out->mutable_value(); // Runtime InferShape diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_dist_word2vec.py b/python/paddle/v2/fluid/tests/book_distribute/test_dist_word2vec.py new file mode 100644 index 00000000000..b41853784d6 --- /dev/null +++ b/python/paddle/v2/fluid/tests/book_distribute/test_dist_word2vec.py @@ -0,0 +1,96 @@ +from __future__ import print_function +import numpy as np +import paddle.v2 as paddle +import paddle.v2.fluid as fluid +import os + +PASS_NUM = 100 +EMBED_SIZE = 32 +HIDDEN_SIZE = 256 +N = 5 +BATCH_SIZE = 32 +IS_SPARSE = True +TRAINERS = 2 + +word_dict = paddle.dataset.imikolov.build_dict() +dict_size = len(word_dict) + +first_word = fluid.layers.data(name='firstw', shape=[1], dtype='int64') +second_word = fluid.layers.data(name='secondw', shape=[1], dtype='int64') +third_word = fluid.layers.data(name='thirdw', shape=[1], dtype='int64') +forth_word = fluid.layers.data(name='forthw', shape=[1], dtype='int64') +next_word = fluid.layers.data(name='nextw', shape=[1], dtype='int64') + +embed_first = fluid.layers.embedding( + input=first_word, + size=[dict_size, EMBED_SIZE], + dtype='float32', + is_sparse=IS_SPARSE, + param_attr='shared_w') +embed_second = fluid.layers.embedding( + input=second_word, + size=[dict_size, EMBED_SIZE], + dtype='float32', + is_sparse=IS_SPARSE, + param_attr='shared_w') +embed_third = fluid.layers.embedding( + input=third_word, + size=[dict_size, EMBED_SIZE], + dtype='float32', + is_sparse=IS_SPARSE, + param_attr='shared_w') +embed_forth = fluid.layers.embedding( + input=forth_word, + size=[dict_size, EMBED_SIZE], + dtype='float32', + is_sparse=IS_SPARSE, + param_attr='shared_w') + +concat_embed = fluid.layers.concat( + input=[embed_first, embed_second, embed_third, embed_forth], axis=1) +hidden1 = fluid.layers.fc(input=concat_embed, size=HIDDEN_SIZE, act='sigmoid') +predict_word = fluid.layers.fc(input=hidden1, size=dict_size, act='softmax') +cost = fluid.layers.cross_entropy(input=predict_word, label=next_word) +avg_cost = fluid.layers.mean(x=cost) +sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.001) +optimize_ops, params_grads = sgd_optimizer.minimize(avg_cost) +train_reader = paddle.batch( + paddle.dataset.imikolov.train(word_dict, N), BATCH_SIZE) + +place = fluid.CPUPlace() +exe = fluid.Executor(place) + +t = fluid.DistributeTranspiler() +# all parameter server endpoints list for spliting parameters +pserver_endpoints = os.getenv("PSERVERS") +# server endpoint for current node +current_endpoint = os.getenv("SERVER_ENDPOINT") +# run as trainer or parameter server +training_role = os.getenv("TRAINING_ROLE", + "TRAINER") # get the training role: trainer/pserver +t.transpile( + optimize_ops, params_grads, pservers=pserver_endpoints, trainers=TRAINERS) +if training_role == "PSERVER": + if not current_endpoint: + print("need env SERVER_ENDPOINT") + exit(1) + pserver_prog = t.get_pserver_program(current_endpoint, optimize_ops) + exe.run(fluid.default_startup_program()) + exe.run(pserver_prog) +elif training_role == "TRAINER": + feeder = fluid.DataFeeder( + feed_list=[first_word, second_word, third_word, forth_word, next_word], + place=place) + exe.run(fluid.default_startup_program()) + for pass_id in range(PASS_NUM): + for data in train_reader(): + avg_cost_np = exe.run(fluid.default_main_program(), + feed=feeder.feed(data), + fetch_list=[avg_cost]) + print("avg_cost_np", avg_cost_np) + if avg_cost_np[0] < 5.0: + exit( + 0) # if avg cost less than 10.0, we think our code is good. +else: + print("environment var TRAINER_ROLE should be TRAINER os PSERVER") +exit(1) -- GitLab From fbc30215d4f92a593288aea7f0deb2f54dcff786 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Tue, 9 Jan 2018 19:54:34 +0800 Subject: [PATCH 0160/2305] refine WhileGradOp code --- paddle/operators/while_op.cc | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/paddle/operators/while_op.cc b/paddle/operators/while_op.cc index 3b78dd128f8..7a3400919ef 100644 --- a/paddle/operators/while_op.cc +++ b/paddle/operators/while_op.cc @@ -219,18 +219,15 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { auto *grad_block = this->grad_block_[0]; auto *fwd_block = grad_block->ParentBlock(); - // auto *parent_block = fwd_block->ParentBlock(); // Not all of IGs will be generated by inner gradient operators of while op. // Ignore IGs that is not generated by the inside block. std::unordered_set inner_op_outputs; - LOG(INFO) << "FUCK1"; for (const auto *op : grad_block->AllOps()) { for (auto &oname : op->OutputArgumentNames()) { inner_op_outputs.insert(oname); } } - LOG(INFO) << "FUCK2"; auto igs = InputGrad(kX, /*do not drop empty gradient*/ false); for (auto &each_ig : igs) { if (inner_op_outputs.find(each_ig) == inner_op_outputs.end()) { @@ -243,11 +240,13 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { // OG should be re-calculated by step blocks, since many outputs of while op // do not need to calculate gradients. std::unordered_set block_ins; - std::copy(Input(kX).begin(), Input(kX).end(), - std::inserter(block_ins, block_ins.end())); - std::copy(Output(kOutputs).begin(), Output(kOutputs).end(), - std::inserter(block_ins, block_ins.end())); - + block_ins.reserve(Input(kX).size() + Output(kOutputs).size()); + for (auto &p : Input(kX)) { + block_ins.insert(p); + } + for (auto &o : Output(kOutputs)) { + block_ins.insert(o); + } std::unordered_set extra_inputs; for (const auto *op : grad_block->AllOps()) { for (auto &input_name : op->InputArgumentNames()) { @@ -257,15 +256,6 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { fwd_block->FindVar(input_name) != nullptr) { continue; } - - /* - if (parent_block->FindVarRecursive(input_name) == nullptr) { - VLOG(5) << "WARNING! Variable '" << input_name - << "' is the input of '" << op->Type() - << "'. But can not be found in any block."; - continue; - } - */ extra_inputs.insert(input_name); } for (auto &output_name : op->OutputArgumentNames()) { -- GitLab From 99a6c5d40edd29c4fedc8f50fe4db75177fb255d Mon Sep 17 00:00:00 2001 From: wanghaox Date: Tue, 9 Jan 2018 20:22:17 +0800 Subject: [PATCH 0161/2305] change output shape to [2, layer_height, layer_width, num_priors, 4] --- paddle/operators/prior_box_op.cc | 20 +++--- paddle/operators/prior_box_op.h | 71 ++++++++++--------- .../v2/fluid/tests/test_prior_box_op.py | 58 +++++++-------- 3 files changed, 72 insertions(+), 77 deletions(-) diff --git a/paddle/operators/prior_box_op.cc b/paddle/operators/prior_box_op.cc index 04182cb1b7a..2ffea67bdd6 100644 --- a/paddle/operators/prior_box_op.cc +++ b/paddle/operators/prior_box_op.cc @@ -93,17 +93,12 @@ class PriorBoxOp : public framework::OperatorWithKernel { const int layer_height = input_dims[2]; const int layer_width = input_dims[3]; - std::vector dim_vec(3); - // Since all images in a batch has same height and width, we only need to - // generate one set of priors which can be shared across all images. - dim_vec[0] = 1; - // 2 channels. First channel stores the mean of each prior coordinate. - // Second channel stores the variance of each prior coordinate. - dim_vec[1] = 2; - dim_vec[2] = layer_width * layer_height * num_priors * 4; - PADDLE_ENFORCE_GT(dim_vec[2], 0, - "output_dim[2] must larger than 0." - "check your data dims"); + std::vector dim_vec(5); + dim_vec[0] = 2; + dim_vec[1] = layer_height; + dim_vec[2] = layer_width; + dim_vec[3] = num_priors; + dim_vec[4] = 4; auto output_dim = framework::make_ddim(dim_vec); ctx->SetOutputDim("Out", output_dim); } @@ -130,7 +125,8 @@ class PriorBoxOpMaker : public framework::OpProtoAndCheckerMaker { "the input image data of PriorBoxOp, The format is NCHW."); AddOutput("Out", "(Tensor, default Tensor), the output prior boxes of " - "PriorBoxOp."); + "PriorBoxOp. The format is [2, layer_height, layer_width, " + "num_priors, 4]"); AddAttr>("min_sizes", "(vector) ", "List of min sizes of generated prior boxes."); AddAttr>("max_sizes", "(vector) ", diff --git a/paddle/operators/prior_box_op.h b/paddle/operators/prior_box_op.h index 142e738a939..86399b53c3a 100644 --- a/paddle/operators/prior_box_op.h +++ b/paddle/operators/prior_box_op.h @@ -15,7 +15,6 @@ limitations under the License. */ #pragma once #include "paddle/framework/op_registry.h" #include "paddle/operators/math/math_function.h" -// #include "paddle/operators/strided_memcpy.h" namespace paddle { namespace operators { @@ -94,50 +93,52 @@ class PriorBoxOpKernel : public framework::OpKernel { num_priors += max_sizes.size(); } - int dim = layer_height * layer_width * num_priors * 4; - T* output_data = nullptr; framework::Tensor output_cpu; + framework::Tensor* output_tensor; out->mutable_data(ctx.GetPlace()); if (platform::is_gpu_place(ctx.GetPlace())) { - output_data = - output_cpu.mutable_data(out->dims(), platform::CPUPlace()); + output_cpu.mutable_data(out->dims(), platform::CPUPlace()); + output_tensor = &output_cpu; } else { - output_data = out->mutable_data(ctx.GetPlace()); + output_tensor = out; } - int idx = 0; + auto e_out = framework::EigenTensor::From(*output_tensor); for (int h = 0; h < layer_height; ++h) { for (int w = 0; w < layer_width; ++w) { float center_x = (w + offset) * step_width; float center_y = (h + offset) * step_height; float box_width, box_height; + int idx = 0; for (size_t s = 0; s < min_sizes.size(); ++s) { int min_size = min_sizes[s]; // first prior: aspect_ratio = 1, size = min_size box_width = box_height = min_size; // xmin - output_data[idx++] = (center_x - box_width / 2.) / img_width; + e_out(0, h, w, idx, 0) = (center_x - box_width / 2.) / img_width; // ymin - output_data[idx++] = (center_y - box_height / 2.) / img_height; + e_out(0, h, w, idx, 1) = (center_y - box_height / 2.) / img_height; // xmax - output_data[idx++] = (center_x + box_width / 2.) / img_width; + e_out(0, h, w, idx, 2) = (center_x + box_width / 2.) / img_width; // ymax - output_data[idx++] = (center_y + box_height / 2.) / img_height; + e_out(0, h, w, idx, 3) = (center_y + box_height / 2.) / img_height; + idx++; if (max_sizes.size() > 0) { int max_size = max_sizes[s]; // second prior: aspect_ratio = 1, // size = sqrt(min_size * max_size) box_width = box_height = sqrt(min_size * max_size); // xmin - output_data[idx++] = (center_x - box_width / 2.) / img_width; + e_out(0, h, w, idx, 0) = (center_x - box_width / 2.) / img_width; // ymin - output_data[idx++] = (center_y - box_height / 2.) / img_height; + e_out(0, h, w, idx, 1) = (center_y - box_height / 2.) / img_height; // xmax - output_data[idx++] = (center_x + box_width / 2.) / img_width; + e_out(0, h, w, idx, 2) = (center_x + box_width / 2.) / img_width; // ymax - output_data[idx++] = (center_y + box_height / 2.) / img_height; + e_out(0, h, w, idx, 3) = (center_y + box_height / 2.) / img_height; + idx++; } // rest of priors @@ -149,13 +150,14 @@ class PriorBoxOpKernel : public framework::OpKernel { box_width = min_size * sqrt(ar); box_height = min_size / sqrt(ar); // xmin - output_data[idx++] = (center_x - box_width / 2.) / img_width; + e_out(0, h, w, idx, 0) = (center_x - box_width / 2.) / img_width; // ymin - output_data[idx++] = (center_y - box_height / 2.) / img_height; + e_out(0, h, w, idx, 1) = (center_y - box_height / 2.) / img_height; // xmax - output_data[idx++] = (center_x + box_width / 2.) / img_width; + e_out(0, h, w, idx, 2) = (center_x + box_width / 2.) / img_width; // ymax - output_data[idx++] = (center_y + box_height / 2.) / img_height; + e_out(0, h, w, idx, 3) = (center_y + box_height / 2.) / img_height; + idx++; } } } @@ -163,26 +165,31 @@ class PriorBoxOpKernel : public framework::OpKernel { // clip the prior's coordidate such that it is within [0, 1] if (clip) { - for (int d = 0; d < dim; ++d) { - output_data[d] = std::min(std::max(output_data[d], 0.), 1.); + for (int h = 0; h < layer_height; ++h) { + for (int w = 0; w < layer_width; ++w) { + for (int i = 0; i < num_priors; ++i) { + for (int j = 0; j < 4; ++j) { + e_out(0, h, w, i, j) = + std::min(std::max(e_out(0, h, w, i, j), 0.), 1.); + } + } + } } - } - // set the variance. - auto output_stride = framework::stride(out->dims()); - output_data += output_stride[1]; - if (variances.size() == 1) { - for (int i = 0; i < dim; ++i) { - output_data[i] = variances[0]; + // set the variance. + auto output_stride = framework::stride(out->dims()); + output_data += output_stride[1]; + if (variances.size() == 1) { + variances.resize(4); + variances[1] = variances[0]; + variances[2] = variances[0]; + variances[3] = variances[0]; } - } else { - int count = 0; for (int h = 0; h < layer_height; ++h) { for (int w = 0; w < layer_width; ++w) { for (int i = 0; i < num_priors; ++i) { for (int j = 0; j < 4; ++j) { - output_data[count] = variances[j]; - ++count; + e_out(1, h, w, i, j) = variances[j]; } } } diff --git a/python/paddle/v2/fluid/tests/test_prior_box_op.py b/python/paddle/v2/fluid/tests/test_prior_box_op.py index 2f821889529..e00bc4bae4c 100644 --- a/python/paddle/v2/fluid/tests/test_prior_box_op.py +++ b/python/paddle/v2/fluid/tests/test_prior_box_op.py @@ -81,8 +81,7 @@ class TestPriorBoxOp(OpTest): self.layer_h)).astype('float32') def init_test_output(self): - dim = self.layer_w * self.layer_h * self.num_priors * 4 - out_dim = (1, 2, dim) + out_dim = (2, self.layer_h, self.layer_w, self.num_priors, 4) output = np.zeros(out_dim).astype('float32') idx = 0 @@ -90,24 +89,22 @@ class TestPriorBoxOp(OpTest): for w in range(self.layer_w): center_x = (w + self.offset) * self.step_w center_y = (h + self.offset) * self.step_h + idx = 0 for s in range(len(self.min_sizes)): min_size = self.min_sizes[s] # first prior: aspect_ratio = 1, size = min_size box_width = box_height = min_size # xmin - output[0, 0, idx] = ( + output[0, h, w, idx, 0] = ( center_x - box_width / 2.) / self.image_w - idx += 1 # ymin - output[0, 0, idx] = ( + output[0, h, w, idx, 1] = ( center_y - box_height / 2.) / self.image_h - idx += 1 # xmax - output[0, 0, idx] = ( + output[0, h, w, idx, 2] = ( center_x + box_width / 2.) / self.image_w - idx += 1 # ymax - output[0, 0, idx] = ( + output[0, h, w, idx, 3] = ( center_y + box_height / 2.) / self.image_h idx += 1 @@ -117,19 +114,16 @@ class TestPriorBoxOp(OpTest): # size = sqrt(min_size * max_size) box_width = box_height = math.sqrt(min_size * max_size) # xmin - output[0, 0, idx] = ( + output[0, h, w, idx, 0] = ( center_x - box_width / 2.) / self.image_w - idx += 1 # ymin - output[0, 0, idx] = ( + output[0, h, w, idx, 1] = ( center_y - box_height / 2.) / self.image_h - idx += 1 # xmax - output[0, 0, idx] = ( + output[0, h, w, idx, 2] = ( center_x + box_width / 2.) / self.image_w - idx += 1 # ymax - output[0, 0, idx] = ( + output[0, h, w, idx, 3] = ( center_y + box_height / 2.) / self.image_h idx += 1 @@ -141,37 +135,35 @@ class TestPriorBoxOp(OpTest): box_width = min_size * math.sqrt(ar) box_height = min_size / math.sqrt(ar) # xmin - output[0, 0, idx] = ( + output[0, h, w, idx, 0] = ( center_x - box_width / 2.) / self.image_w - idx += 1 # ymin - output[0, 0, idx] = ( + output[0, h, w, idx, 1] = ( center_y - box_height / 2.) / self.image_h - idx += 1 # xmax - output[0, 0, idx] = ( + output[0, h, w, idx, 2] = ( center_x + box_width / 2.) / self.image_w - idx += 1 # ymax - output[0, 0, idx] = ( + output[0, h, w, idx, 3] = ( center_y + box_height / 2.) / self.image_h idx += 1 # clip the prior's coordidate such that it is within[0, 1] if self.clip: - for d in range(dim): - output[0, 0, d] = min(max(output[0, 0, d], 0), 1) - # set the variance. - if len(self.variances) == 1: - for i in range(dim): - output[0, 1, i] = self.variances[0] - else: - count = 0 for h in range(self.layer_h): for w in range(self.layer_w): for i in range(self.num_priors): for j in range(4): - output[0, 1, count] = self.variances[j] - count += 1 + output[0, h, w, i, j] = min( + max(output[0, h, w, i, j], 0), 1) + # set the variance. + for h in range(self.layer_h): + for w in range(self.layer_w): + for i in range(self.num_priors): + for j in range(4): + if len(self.variances) == 1: + output[1, h, w, i, j] = self.variances[0] + else: + output[1, h, w, i, j] = self.variances[j] self.output = output.astype('float32') -- GitLab From 45e77154cf5a46f789bb89aeb30c6de769a18bfb Mon Sep 17 00:00:00 2001 From: QI JUN Date: Tue, 9 Jan 2018 20:24:09 +0800 Subject: [PATCH 0162/2305] add general memory usage interface for both CPU/CUDA (#7352) --- paddle/memory/memory.cc | 16 ++++++++++++++++ paddle/memory/memory.h | 7 +++++++ paddle/memory/memory_test.cc | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/paddle/memory/memory.cc b/paddle/memory/memory.cc index c4bb6baee7e..1a73a94567e 100644 --- a/paddle/memory/memory.cc +++ b/paddle/memory/memory.cc @@ -114,5 +114,21 @@ void Free(platform::CUDAPlace place, void* p) { #endif +size_t Usage::operator()(const platform::CPUPlace& cpu) const { + return Used(cpu); +} + +size_t Usage::operator()(const platform::CUDAPlace& gpu) const { +#ifdef PADDLE_WITH_CUDA + return Used(gpu); +#else + PADDLE_THROW("'CUDAPlace' is not supported in CPU only device."); +#endif +} + +size_t memory_usage(const platform::Place& p) { + return boost::apply_visitor(Usage(), p); +} + } // namespace memory } // namespace paddle diff --git a/paddle/memory/memory.h b/paddle/memory/memory.h index 11bbb881874..7012b6d331d 100644 --- a/paddle/memory/memory.h +++ b/paddle/memory/memory.h @@ -54,6 +54,13 @@ void Free(Place place, void* ptr); template size_t Used(Place place); +struct Usage : public boost::static_visitor { + size_t operator()(const platform::CPUPlace& cpu) const; + size_t operator()(const platform::CUDAPlace& gpu) const; +}; + +size_t memory_usage(const platform::Place& p); + /** * \brief Free memory block in one place. * diff --git a/paddle/memory/memory_test.cc b/paddle/memory/memory_test.cc index f476bf71264..b3f699f9b7e 100644 --- a/paddle/memory/memory_test.cc +++ b/paddle/memory/memory_test.cc @@ -44,6 +44,9 @@ TEST(BuddyAllocator, CPUAllocation) { EXPECT_NE(p, nullptr); + paddle::platform::Place place = cpu; + EXPECT_EQ(paddle::memory::Used(cpu), paddle::memory::memory_usage(place)); + paddle::memory::Free(cpu, p); } @@ -99,6 +102,9 @@ TEST(BuddyAllocator, GPUAllocation) { EXPECT_NE(p, nullptr); + paddle::platform::Place place = gpu; + EXPECT_EQ(paddle::memory::Used(gpu), paddle::memory::memory_usage(place)); + paddle::memory::Free(gpu, p); } -- GitLab From 9c0b1cf1c2e28f7f6fb7b7a39c8bdffe07981bcd Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Tue, 9 Jan 2018 20:32:51 +0800 Subject: [PATCH 0163/2305] update wip pserver transpile --- .../paddle/v2/fluid/distribute_transpiler.py | 152 ++++++++++++------ 1 file changed, 106 insertions(+), 46 deletions(-) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index 7f3da674633..ac13a7cb60a 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -98,8 +98,7 @@ class DistributeTranspiler: # 3. append send_op to trainer. # 4. append concat_op to trainer to update local weights. # 5. create new program as parameter server. - # 5. create parameter server program by split_method generated endpoint->VarBlock - # 6. run compile time infershape for parameter server program + # 6. create parameter server program by split_method generated endpoint->VarBlock pserver_endpoints = pservers.split(",") @@ -124,6 +123,15 @@ class DistributeTranspiler: # let send_op know which endpoint to send which var, eplist is of the same # order of send_inputs. eplist = split_method(send_inputs, pserver_endpoints) + # create mapping of endpoint -> var to create pserver side program + self.param_grad_ep_mapping = dict() + for i, ep in enumerate(eplist): + param = send_outputs[i] + grad = send_inputs[i] + if not self.param_grad_ep_mapping.has_key(ep): + self.param_grad_ep_mapping[ep] = {"params": [], "grads": []} + self.param_grad_ep_mapping[ep]["params"].append(param) + self.param_grad_ep_mapping[ep]["grads"].append(grad) send_op = program.global_block().append_op( type="send", @@ -235,27 +243,29 @@ class DistributeTranspiler: var_list.append(var_each) return var_list - def get_pserver_program(self, endpoint, optimize_ops): - pserver_program = Program() - for v in self.param_grad_map[endpoint]["params"]: - self._clone_param(pserver_program.global_block(), v) - - optimize_sub_program = Program() - grad_var_names = [ - var.name for var in self.param_grad_map[endpoint]["grads"] - ] - for opt_op in optimize_ops: - for _, var in opt_op.inputs.iteritems(): - # NOTE: append operators to merge gradients from multiple - # trainers. If trainers == 1, this is not needed. - if self.trainers > 1 and var.name in grad_var_names: + def _append_pserver_ops(self, opt_op, endpoint): + new_inputs = dict() + for key, var in opt_op.inputs.iteritems(): + if key == "Grad": + grad_block = None + for g in self.param_grad_ep_mapping[endpoint]["grads"]: + if g.name.startswith(var.name): + grad_block = g + break + if not grad_block: + # do not append this op if current endpoint + # is not dealing with this grad block + return + merged_var = optimize_sub_program.global_block().create_var( + name=grad_block.name, + persistable=grad_block.persistable, + dtype=grad_block.dtype, + shape=grad_block.shape) + # append merging ops if trainers > 1 + if self.trainers > 1: vars2merge = self._create_var_for_trainers( - optimize_sub_program.global_block(), var, self.trainers) - merged_var = optimize_sub_program.global_block().create_var( - name=var.name, - persistable=var.persistable, - dtype=var.dtype, - shape=var.shape) + optimize_sub_program.global_block(), grad_block, + self.trainers) optimize_sub_program.global_block().append_op( type="sum", inputs={"X": vars2merge}, @@ -265,38 +275,88 @@ class DistributeTranspiler: inputs={"X": merged_var}, outputs={"Out": merged_var}, attrs={"scale": 1.0 / float(self.trainers)}) - else: - optimize_sub_program.global_block().create_var( - name=var.name, - persistable=var.persistable, - dtype=var.dtype, - shape=var.shape) + new_inputs[key] = merged_var + elif key == "Param": + # param is already created on global program + param_block = None + for p in self.param_grad_ep_mapping[endpoint]["params"]: + if p.name.startswith(var.name): + param_block = p + break + if not param_block: + return + tmpvar = optimize_sub_program.global_block().create_var( + name=param_block.name, + persistable=param_block.persistable, + dtype=param_block.dtype, + shape=param_block.shape) + new_inputs[key] = tmpvar + else: + tmpvar = optimize_sub_program.global_block().create_var( + name=var.name, + persistable=var.persistable, + dtype=var.dtype, + shape=var.shape) + new_inputs[key] = tmpvar - if opt_op.inputs.has_key("Grad"): - if opt_op.inputs["Grad"].name in grad_var_names: - optimize_sub_program.global_block().append_op( - type=opt_op.type, - inputs=opt_op.inputs, - outputs=opt_op.outputs, - attrs=opt_op.attrs) + # FIXME: change outputs ParamOut + optimize_sub_program.global_block().append_op( + type=opt_op.type, + inputs=new_inputs, + outputs=opt_op.outputs, + attrs=opt_op.attrs) + + def _append_pserver_non_opt_ops(self, opt_op): + for _, var in opt_op.inputs.iteritems(): + optimize_sub_program.global_block().create_var( + name=var.name, + persistable=var.persistable, + dtype=var.dtype, + shape=var.shape) + optimize_sub_program.global_block().append_op( + type=opt_op.type, + inputs=new_inputs, + outputs=opt_op.outputs, + attrs=opt_op.attrs) + + def get_pserver_program(self, endpoint, optimize_ops): + """ + get pserver side program by endpoint + + NOTE: assume blocks of the same variable is not distributed + on the same pserver, only change param/grad varnames for + trainers to fetch. For each pserver endpoint, server side + program must be a sub-set of the original optimization program. + """ + # step5 + pserver_program = Program() + for v in self.param_grad_ep_mapping[endpoint]["params"]: + self._clone_param(pserver_program.global_block(), v) + # step6 + optimize_sub_program = Program() + for opt_op in optimize_ops: + if opt_ops.inputs.has_key("Grad"): + # append optimize_op + self._append_pserver_ops(opt_op, endpoint) else: - optimize_sub_program.global_block().append_op( - type=opt_op.type, - inputs=opt_op.inputs, - outputs=opt_op.outputs, - attrs=opt_op.attrs) + self._append_pserver_non_opt_ops(opt_op) + pserver_program.global_block().append_op( type="recv", - inputs={"RX": - self.param_grad_map[endpoint]["grads"]}, # grads to recv + inputs={"RX": self.param_grad_ep_mapping[endpoint]["grads"] + }, # grads to recv outputs={}, attrs={ "OptimizeProgram": optimize_sub_program.desc, "endpoint": endpoint, - "ParamList": - [p.name for p in self.param_grad_map[endpoint]["params"]], - "GradList": - [p.name for p in self.param_grad_map[endpoint]["grads"]], + "ParamList": [ + p.name + for p in self.param_grad_ep_mapping[endpoint]["params"] + ], + "GradList": [ + p.name + for p in self.param_grad_ep_mapping[endpoint]["grads"] + ], "Trainers": self.trainers }) pserver_program.sync_with_cpp() -- GitLab From 427e47459b7cc8b51fd4b4b5c05c99941d364aa0 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Tue, 9 Jan 2018 20:48:54 +0800 Subject: [PATCH 0164/2305] Add grad_op_maker for sequence_pool. --- paddle/operators/sequence_pool_op.cc | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/paddle/operators/sequence_pool_op.cc b/paddle/operators/sequence_pool_op.cc index 34e1a125915..549d9620ef2 100644 --- a/paddle/operators/sequence_pool_op.cc +++ b/paddle/operators/sequence_pool_op.cc @@ -115,12 +115,32 @@ class SequencePoolGradOp : public framework::OperatorWithKernel { } }; +class SequencePoolGradOpMaker : public framework::SingleGradOpDescMaker { + public: + using framework::SingleGradOpDescMaker::SingleGradOpDescMaker; + + protected: + std::unique_ptr Apply() const override { + auto* op_desc_ptr = new framework::OpDesc(); + op_desc_ptr->SetType("sequence_pool_grad"); + op_desc_ptr->SetInput("X", Input("X")); + if (boost::get(GetAttr("pooltype")) == "MAX") { + op_desc_ptr->SetInput("MaxIndex", Output("MaxIndex")); + } + op_desc_ptr->SetInput(framework::GradVarName("Out"), OutputGrad("Out")); + op_desc_ptr->SetOutput(framework::GradVarName("X"), InputGrad("X")); + op_desc_ptr->SetAttrMap(Attrs()); + return std::unique_ptr(op_desc_ptr); + } +}; + } // namespace operators } // namespace paddle namespace ops = paddle::operators; -REGISTER_OP(sequence_pool, ops::SequencePoolOp, ops::SequencePoolOpMaker, - sequence_pool_grad, ops::SequencePoolGradOp); +REGISTER_OPERATOR(sequence_pool, ops::SequencePoolOp, ops::SequencePoolOpMaker, + ops::SequencePoolGradOpMaker); +REGISTER_OPERATOR(sequence_pool_grad, ops::SequencePoolGradOp); REGISTER_OP_CPU_KERNEL( sequence_pool, ops::SequencePoolKernel); -- GitLab From 6ecbf083729c65a3c651e6b3aa88262ff25a1c68 Mon Sep 17 00:00:00 2001 From: QI JUN Date: Tue, 9 Jan 2018 22:01:52 +0800 Subject: [PATCH 0165/2305] add memory optimization transpiler (#7356) --- python/paddle/v2/fluid/__init__.py | 3 +- python/paddle/v2/fluid/framework.py | 3 + .../fluid/memory_optimization_transpiler.py | 115 ++++++++++++++++++ .../test_memory_optimization_transpiler.py | 33 +++++ 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 python/paddle/v2/fluid/memory_optimization_transpiler.py create mode 100644 python/paddle/v2/fluid/tests/test_memory_optimization_transpiler.py diff --git a/python/paddle/v2/fluid/__init__.py b/python/paddle/v2/fluid/__init__.py index 5e01b871980..3f178e252c3 100644 --- a/python/paddle/v2/fluid/__init__.py +++ b/python/paddle/v2/fluid/__init__.py @@ -19,12 +19,13 @@ from data_feeder import DataFeeder from core import LoDTensor, CPUPlace, CUDAPlace from distribute_transpiler import DistributeTranspiler import clip +from memory_optimization_transpiler import memory_optimize Tensor = LoDTensor __all__ = framework.__all__ + executor.__all__ + [ 'io', 'initializer', 'layers', 'nets', 'optimizer', 'backward', 'regularizer', 'LoDTensor', 'CPUPlace', 'CUDAPlace', 'Tensor', 'ParamAttr' - 'DataFeeder', 'clip', 'DistributeTranspiler' + 'DataFeeder', 'clip', 'DistributeTranspiler', 'memory_optimize' ] diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index 85c1e6eb7ba..2fb388acfc0 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -773,6 +773,9 @@ class Program(object): proto = framework_pb2.ProgramDesc.FromString(str(protostr)) return _debug_string_(proto, throw_on_error) + def get_desc(self): + return self.desc + def clone(self): p = Program() p.desc = core.ProgramDesc(self.desc) diff --git a/python/paddle/v2/fluid/memory_optimization_transpiler.py b/python/paddle/v2/fluid/memory_optimization_transpiler.py new file mode 100644 index 00000000000..571fce7fac6 --- /dev/null +++ b/python/paddle/v2/fluid/memory_optimization_transpiler.py @@ -0,0 +1,115 @@ +from collections import defaultdict +import framework +from framework import Program, default_main_program, Parameter, Variable +import backward +from backward import _rename_arg_ + + +class ControlFlowGraph(object): + def __init__(self, Program): + self._program = Program + self._succesors = defaultdict(set) + self._presucessors = defaultdict(set) + self._uses = defaultdict(set) + self._defs = defaultdict(set) + self._live_in = defaultdict(set) + self._live_out = defaultdict(set) + + def _add_connections(self, connections): + for node1, node2 in connections: + self._add(node1, node2) + + def _add(self, node1, node2): + self._succesors[node1].add(node2) + self._presucessors[node2].add(node1) + + def _build_graph(self): + program_desc = self._program.get_desc() + block_size = program_desc.num_blocks() + + # TODO(qijun) handle Program with if/while operators + self.global_block = program_desc.block(0) + self.op_size = self.global_block.op_size() + + op_node_connections = [(i, i + 1) for i in range(self.op_size - 1)] + self._add_connections(op_node_connections) + + self.ops = [self.global_block.op(i) for i in range(self.op_size)] + + for i in range(self.op_size): + self._uses[i].update(self.ops[i].input_arg_names()) + self._defs[i].update(self.ops[i].output_arg_names()) + + def _reach_fixed_point(self, live_in, live_out): + if len(live_in) != len(self._live_in): + return False + if len(live_out) != len(self._live_out): + return False + for i in range(self.op_size): + if live_in[i] != self._live_in[i]: + return False + for i in range(self.op_size): + if live_out[i] != self._live_out[i]: + return False + return True + + def _dataflow_analyze(self): + self._build_graph() + live_in = defaultdict(set) + live_out = defaultdict(set) + while True: + for i in range(self.op_size): + live_in[i] = set(self._live_in[i]) + live_out[i] = set(self._live_out[i]) + self._live_in[i] = self._uses[i] | ( + self._live_out[i] - self._defs[i]) + for s in self._succesors[i]: + self._live_out[i] |= self._live_in[s] + + if self._reach_fixed_point(live_in, live_out): + break + + def _get_diff(self, a, b): + u = a & b + return a - u, b - u + + def memory_optimize(self): + self._build_graph() + self._dataflow_analyze() + self.pool = [] + for i in range(self.op_size): + if self.pool: + out_pair = [(x, self.global_block.var(str(x)).shape()) + for x in self._defs[i]] + for x, x_shape in out_pair: + for index, cache_pair in enumerate(self.pool): + cache_var = cache_pair[0] + cache_shape = cache_pair[1] + if x_shape == cache_shape: + print( + "Hit Cache !!!! cache pool index is %d, var name is %s, cached var name is %s, var shape is %s " + % (index, x, cache_var, str(cache_shape))) + self.pool.pop(index) + _rename_arg_(self.ops, x, cache_var, begin_idx=i) + self._dataflow_analyze() + break + + in_diff, out_diff = self._get_diff(self._live_in[i], + self._live_out[i]) + can_optimize = filter( + lambda x: not self.global_block.var(str(x)).persistable(), + in_diff) + if can_optimize: + for var_name in can_optimize: + self.pool.append(( + var_name, self.global_block.var(str(var_name)).shape())) + + def get_program(self): + return self._program + + +def memory_optimize(input_program): + graph = ControlFlowGraph(input_program) + graph.memory_optimize() + result_program = graph.get_program() + return result_program diff --git a/python/paddle/v2/fluid/tests/test_memory_optimization_transpiler.py b/python/paddle/v2/fluid/tests/test_memory_optimization_transpiler.py new file mode 100644 index 00000000000..5cce75ddb8d --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_memory_optimization_transpiler.py @@ -0,0 +1,33 @@ +from __future__ import print_function +import unittest + +import paddle.v2.fluid.layers as layers +import paddle.v2.fluid.optimizer as optimizer +from paddle.v2.fluid.framework import Program, program_guard +from paddle.v2.fluid.memory_optimization_transpiler import memory_optimize + + +class TestControlFlowGraph(unittest.TestCase): + def setUp(self): + program = Program() + with program_guard(program, startup_program=Program()): + x = layers.data(name='x', shape=[13], dtype='float32') + y_predict = layers.fc(input=x, size=1, act=None) + y = layers.data(name='y', shape=[1], dtype='float32') + cost = layers.square_error_cost(input=y_predict, label=y) + avg_cost = layers.mean(x=cost) + opt = optimizer.SGD(learning_rate=0.001) + opt = opt.minimize(avg_cost) + + self.program = program + + def test_control_flow_graph(self): + print("before optimization") + print(str(self.program)) + result_program = memory_optimize(self.program) + print("after optimization") + print(str(result_program)) + + +if __name__ == "__main__": + unittest.main() -- GitLab From 4a3580ac751fdfdba52575d83ef151bed75b0396 Mon Sep 17 00:00:00 2001 From: Xi Chen Date: Tue, 9 Jan 2018 14:49:08 -0800 Subject: [PATCH 0166/2305] add missing dependency --- .../tests/book_distribute/test_understand_sentiment_conv_dist.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py b/python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py index 0f0d84be0a6..db419e23abc 100644 --- a/python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py +++ b/python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py @@ -1,4 +1,5 @@ from __future__ import print_function +import os import numpy as np import paddle.v2 as paddle import paddle.v2.fluid as fluid -- GitLab From 0ef9dc612251a53cd33ee58be427d6db72b59b00 Mon Sep 17 00:00:00 2001 From: xuwei06 Date: Fri, 5 Jan 2018 23:42:14 -0800 Subject: [PATCH 0167/2305] Fix comment for norm_op --- paddle/operators/norm_op.cc | 2 +- paddle/operators/norm_op.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/paddle/operators/norm_op.cc b/paddle/operators/norm_op.cc index b198b76cd49..0eeafcaae0a 100644 --- a/paddle/operators/norm_op.cc +++ b/paddle/operators/norm_op.cc @@ -39,7 +39,7 @@ class NormOpMaker : public framework::OpProtoAndCheckerMaker { "M = C * H * W"); AddComment(R"DOC( "Input shape: $(N, C, H, W)$ - Sclae shape: $(C, 1)$ + Scale shape: $(C, 1)$ Output shape: $(N, C, H, W)$ Where forward diff --git a/paddle/operators/norm_op.h b/paddle/operators/norm_op.h index 7bee48919e9..5759d6f1f07 100644 --- a/paddle/operators/norm_op.h +++ b/paddle/operators/norm_op.h @@ -66,7 +66,7 @@ class NormKernel : public framework::OpKernel { context.GetPlace()); auto tmp = framework::EigenVector::Flatten(tmp_tensor); - // get colsum and sqrt , inverse + // get colsum and sqrt , inverse auto dim = Eigen::array({{0}}); tmp.device(*place) = x_square_batch_eigen.sum(dim); tmp.device(*place) = (tmp + epsilon).sqrt().inverse(); -- GitLab From 585dec3dc2b40a3f12137280baa099c451d01f59 Mon Sep 17 00:00:00 2001 From: xuwei06 Date: Sat, 6 Jan 2018 00:08:22 -0800 Subject: [PATCH 0168/2305] Calculating gradients for partial graph Added backward.calc_gradient to backpropagate gradient from given targets to inputs. --- paddle/framework/grad_op_desc_maker.h | 6 +- paddle/framework/op_desc.h | 2 +- python/paddle/v2/fluid/backward.py | 265 ++++++++++++++++++++---- python/paddle/v2/fluid/layers/ops.py | 12 +- python/paddle/v2/fluid/layers/tensor.py | 33 ++- 5 files changed, 275 insertions(+), 43 deletions(-) diff --git a/paddle/framework/grad_op_desc_maker.h b/paddle/framework/grad_op_desc_maker.h index 2de52428318..2082f8bb76f 100644 --- a/paddle/framework/grad_op_desc_maker.h +++ b/paddle/framework/grad_op_desc_maker.h @@ -87,7 +87,11 @@ class GradOpDescMakerBase { auto onames = this->Output(name); ret_val.reserve(onames.size()); std::transform(onames.begin(), onames.end(), std::back_inserter(ret_val), - GradVarName); + [this](const std::string& fwd_var_name) -> std::string { + auto g_name = GradVarName(fwd_var_name); + (*this->grad_to_var_)[g_name] = fwd_var_name; + return g_name; + }); return ret_val; } diff --git a/paddle/framework/op_desc.h b/paddle/framework/op_desc.h index 4cf784a0d0d..a5ffb162928 100644 --- a/paddle/framework/op_desc.h +++ b/paddle/framework/op_desc.h @@ -129,7 +129,7 @@ class OpDesc { } proto::OpDesc desc_; - // input arg name => output variable names + // input arg name => input variable names VariableNameMap inputs_; // output arg name => output variable names VariableNameMap outputs_; diff --git a/python/paddle/v2/fluid/backward.py b/python/paddle/v2/fluid/backward.py index 8d0eb53b8e1..cea2d1e0906 100644 --- a/python/paddle/v2/fluid/backward.py +++ b/python/paddle/v2/fluid/backward.py @@ -1,8 +1,9 @@ from paddle.v2.fluid import framework as framework from . import core import collections +import copy -__all__ = ['append_backward'] +__all__ = ['append_backward', 'calc_gradient'] def _rename_arg_(op_descs, old_name, new_name, begin_idx=None, end_idx=None): @@ -65,6 +66,18 @@ def _all_in_set_(cands, s): return True +def _some_in_set_(cands, s): + """ + Test if some elements of 'cands' are in set 's' + """ + if len(cands) == 0: + return False + for c in cands: + if c in s: + return True + return False + + def _strip_grad_suffix_(name): """ Strip the grad suffix from the given varibale name @@ -169,8 +182,8 @@ def _remove_no_grad_branch_(op_descs, no_grad_set): return op_descs -def _append_backward_ops_(target, - block, +def _append_backward_ops_(block, + ops, target_block, no_grad_dict, grad_to_var, @@ -179,8 +192,8 @@ def _append_backward_ops_(target, Create all grad ops, and insert them into given block Args: - target(Variable): the target variable of forward pass block(Block): the block where forward ops are + ops(Op): the forward operators whose backward ops need to be added target_block(Block): the block which is going to hold new generated grad ops no_grad_dict(dict): key(int) block index @@ -202,14 +215,14 @@ def _append_backward_ops_(target, # grad_op_descs holds created grad_op, and will be appended to target_block grad_op_descs = [] program = block.program - for op in reversed(block.ops): + for op in reversed(ops): grad_sub_block_list = [] # If the op has its own sub-block, deal with the sub-block first if op.has_attr("sub_block"): sub_block = program.block(op.block_attr("sub_block")) grad_sub_block = program.create_block(parent_idx=sub_block.idx) - _append_backward_ops_(target, sub_block, grad_sub_block, - no_grad_dict, grad_to_var, callback) + _append_backward_ops_(sub_block, sub_block.ops, grad_sub_block, + no_grad_dict, grad_to_var) grad_sub_block_list.append(grad_sub_block.desc) # Getting op's corresponding grad_op @@ -224,14 +237,6 @@ def _append_backward_ops_(target, grad_op_descs = _remove_no_grad_branch_(grad_op_descs, no_grad_dict[block.idx]) - if target_block.idx == 0: - grad_op_descs.insert( - 0, - _create_op_desc_("fill_constant", {}, { - "Out": [_append_grad_suffix_(target.name)] - }, {"shape": [1], - "value": 1.0, - "dtype": target.dtype})) # append op_desc in grad_op_descs to target_block for op_desc in grad_op_descs: new_op_desc = target_block.desc.append_op() @@ -252,7 +257,7 @@ def _append_backward_vars_(block, start_op_idx, grad_to_var, grad_info_map): In most cases, this dict is generated by _append_backward_ops_() grad_info_map(dict)(output argument): key(str): forward variable name - val(tuple): a tuple of (str, int), str is the corresponding grad name, int is the block index + val(tuple): a tuple of (str, Block), str is the corresponding grad name, Block is the block containing grad variable """ for op_idx in range(start_op_idx, block.desc.op_size()): op_desc = block.desc.op(op_idx) @@ -279,41 +284,63 @@ def _append_backward_vars_(block, start_op_idx, grad_to_var, grad_info_map): _infer_var_data_type_(arg, block) +def _rename_grad_(block, start_op_idx, grad_to_var, target_grad_map): + var_map = copy.copy(target_grad_map) + for op_idx in range(start_op_idx, block.desc.op_size()): + op_desc = block.desc.op(op_idx) + for name in op_desc.input_arg_names(): + if name in var_map: + op_desc.rename_input(name, var_map[name]) + + for name in op_desc.output_arg_names(): + if block.desc.find_var(name.encode("ascii")): + new_name = "%s_%s" % (name, core.unique_integer(name)) + op_desc.rename_output(name, new_name) + var_map[name] = new_name + + for g, ng in var_map.iteritems(): + if g in grad_to_var: + grad_to_var[ng] = grad_to_var[g] + grad_to_var.pop(g) + + +def _get_stop_gradients_(program): + no_grad_dict = dict() + assert isinstance(program, framework.Program) + for block in program.blocks: + assert isinstance(block, framework.Block) + block_no_grad_set = set() + for var in block.vars.itervalues(): + assert isinstance(var, framework.Variable) + if var.stop_gradient: + block_no_grad_set.add(_append_grad_suffix_(var.name)) + no_grad_dict[block.idx] = block_no_grad_set + return no_grad_dict + + def append_backward(loss, parameter_list=None, no_grad_set=None, callback=None): """ Append backward part to main_program Args: loss(Variable): The variable generated by cost function. - parameter_list(list): Parameters that need to be updated by optimizer. - If None, it means all parameters need to be updated. + parameter_list(list[string]): Parameters that need to be updated by + optimizer. If None, it means all parameters need to be updated. no_grad_set(set): Variables that have no gradients in Block 0. - If None, the set will be generated inside the function and - contains all variables with `step_gradient=True` from all blocks. + All variables with `step_gradient=True` from all blocks will be + automatically added. Return: - (list[Variable]): list of (parameters, gradients) pair. + (list[(Variable,Variable)]): list of (parameter, gradient) pair. """ assert isinstance(loss, framework.Variable) program = loss.block.program - no_grad_dict = dict() if no_grad_set is None: - assert isinstance(program, framework.Program) - for block in program.blocks: - assert isinstance(block, framework.Block) - block_no_grad_set = set() - for var in block.vars.itervalues(): - assert isinstance(var, framework.Variable) - if var.stop_gradient: - block_no_grad_set.add(_append_grad_suffix_(var.name)) - no_grad_dict[block.idx] = block_no_grad_set - elif isinstance(no_grad_set, set): - no_grad_dict = { - 0: set([_append_grad_suffix_(name) for name in no_grad_set]) - } - else: - raise ValueError("'no_grad_set' should be a set or None.") + no_grad_set = set() + no_grad_set = copy.copy(no_grad_set) + no_grad_dict = _get_stop_gradients_(program) + no_grad_dict[0].update(map(_append_grad_suffix_, no_grad_set)) grad_info_map = dict() root_block = program.block(0) @@ -322,8 +349,25 @@ def append_backward(loss, parameter_list=None, no_grad_set=None, callback=None): current_block_idx = program.current_block_idx grad_to_var = dict() - _append_backward_ops_(loss, root_block, root_block, no_grad_dict, + op_desc = _create_op_desc_("fill_constant", {}, { + "Out": [_append_grad_suffix_(loss.name)] + }, {"shape": [1], + "value": 1.0, + "dtype": loss.dtype}) + root_block.desc.append_op().copy_from(op_desc) + + block_no_grad_set = set(map(_strip_grad_suffix_, no_grad_dict[0])) + op_path = _find_op_path_(root_block, [loss], [], block_no_grad_set) + no_grad_dict[0].update(map(_append_grad_suffix_, block_no_grad_set)) + + _append_backward_ops_(root_block, op_path, root_block, no_grad_dict, grad_to_var, callback) + + # Because calc_gradient may be called multiple times, + # we need rename the internal gradient variables so that they have + # different names. + _rename_grad_(root_block, fwd_op_num, grad_to_var, {}) + _append_backward_vars_(root_block, fwd_op_num, grad_to_var, grad_info_map) program.current_block_idx = current_block_idx @@ -334,6 +378,7 @@ def append_backward(loss, parameter_list=None, no_grad_set=None, callback=None): else: params = program.global_block().all_parameters() parameters = [param.name for param in params] + params_and_grads = [] for param in parameters: if param not in grad_info_map: @@ -351,3 +396,147 @@ def append_backward(loss, parameter_list=None, no_grad_set=None, callback=None): else: params_and_grads.append((param_var, None)) return params_and_grads + + +def _as_list(x): + if x is None: + return [] + return list(x) if isinstance(x, collections.Sequence) else [x] + + +def _find_op_path_(block, outputs, inputs, no_grad_set): + """ + no_grad_set will also be changed + """ + input_names = set([inp.name for inp in inputs]) + output_names = set([out.name for out in outputs]) + + relevant_op_flags = [True] * len(block.ops) + + # All the inputs of the block are used if inputs is empty, + if inputs: + for i, op in enumerate(block.ops): + if _some_in_set_(op.desc.input_arg_names(), input_names): + for name in op.desc.output_arg_names(): + if name not in no_grad_set: + input_names.add(name) + else: + relevant_op_flags[i] = False + + for i, op in reversed(list(enumerate(block.ops))): + if _some_in_set_(op.desc.output_arg_names(), output_names): + for name in op.desc.input_arg_names(): + if name not in no_grad_set: + output_names.add(name) + else: + relevant_op_flags[i] = False + + op_path = [ + block.ops[i] for i in range(len(block.ops)) if relevant_op_flags[i] + ] + + if inputs: + for op in op_path: + for name in op.desc.input_arg_names(): + if name not in input_names: + no_grad_set.add(name) + + return op_path + + +def calc_gradient(targets, inputs, target_gradients=None, no_grad_set=None): + """ + Backpropagate the graidents of targets to inputs. + + Args: + targets(Variable|list[Variable]): The target variables + inputs(Variable|list[Variable]): The input variables + no_grad_set(set[string]): The names of variables that have no gradients + in Block 0. All variables with `stop_gradient=True` from all blocks + will be automatically added. + + Return: + (list[Variable]): list of gradients for inputs + If an input does not affect targets, the corresponding gradient variable + will be None + """ + targets = _as_list(targets) + inputs = _as_list(inputs) + target_gradients = _as_list(target_gradients) + + block = targets[0].block + prog = block.program + block_idx = block.idx + + if not target_gradients: + target_gradients = [None] * len(targets) + + if len(targets) != len(target_gradients): + raise ValueError( + "Should have the same number of target_gradients as targets") + + if no_grad_set is None: + no_grad_set = set() + no_grad_set = copy.copy(no_grad_set) + no_grad_dict = _get_stop_gradients_(prog) + no_grad_dict[0].update(map(_append_grad_suffix_, no_grad_set)) + + fwd_op_num = block.desc.op_size() + + target_grad_map = {} + for i, grad in enumerate(target_gradients): + target = targets[i] + if grad is None: + grad_name = _append_grad_suffix_(target.name) + op_desc = _create_op_desc_("fill_constant_batch_size_like", + {"Input": [target.name]}, + {"Out": [grad_name]}, { + "shape": target.shape, + "value": 1.0, + "dtype": target.dtype, + 'input_dim_idx': 0, + 'output_dim_idx': 0 + }) + block.desc.append_op().copy_from(op_desc) + else: + if target.block.idx != block_idx or target.block.program != prog: + raise ValueError("all targets must be in the same block") + if target.shape != grad.shape: + raise ValueError( + "The shapes of target and grad are different: %s %s" % ( + target.name, grad.name)) + target_grad_map[_append_grad_suffix_(target.name)] = grad.name + + for input in inputs: + if input.block.program != prog: + raise "input must be in the same program as targets" + + block_no_grad_set = set(map(_strip_grad_suffix_, no_grad_dict[0])) + op_path = _find_op_path_(block, targets, inputs, block_no_grad_set) + no_grad_dict[0].update(map(_append_grad_suffix_, block_no_grad_set)) + grad_to_var = dict() + grad_info_map = dict() + _append_backward_ops_(block, op_path, block, no_grad_dict, grad_to_var) + + # Because calc_gradient may be called multiple times, + # we need rename the internal gradient variables so that they have + # different names. + _rename_grad_(block, fwd_op_num, grad_to_var, target_grad_map) + + _append_backward_vars_(block, fwd_op_num, grad_to_var, grad_info_map) + prog.sync_with_cpp() + + grad_vars = [] + for input_var in inputs: + if input_var.name not in grad_info_map: + grad_vars.append(None) + else: + grad_info = grad_info_map[input_var.name] + grad_block = grad_info[1] + grad_var = grad_block.var(grad_info[0]) + grad_vars.append(grad_var) + + if len(grad_vars) == 1: + return grad_vars[0] + else: + return grad_vars diff --git a/python/paddle/v2/fluid/layers/ops.py b/python/paddle/v2/fluid/layers/ops.py index 23fe13f9bbf..544623c4bce 100644 --- a/python/paddle/v2/fluid/layers/ops.py +++ b/python/paddle/v2/fluid/layers/ops.py @@ -1,7 +1,17 @@ from ..registry import register_layer __activations__ = [ - 'abs', 'tanh', 'sigmoid', 'relu', 'sqrt', 'ceil', 'floor', 'log', 'round' + 'abs', + 'ceil', + 'exp', + 'floor', + 'log', + 'relu', + 'round', + 'sigmoid', + 'sqrt', + 'square', + 'tanh', ] __all__ = [ diff --git a/python/paddle/v2/fluid/layers/tensor.py b/python/paddle/v2/fluid/layers/tensor.py index 9ce25a9e083..5f12ecfc14f 100644 --- a/python/paddle/v2/fluid/layers/tensor.py +++ b/python/paddle/v2/fluid/layers/tensor.py @@ -1,7 +1,8 @@ from ..layer_helper import LayerHelper +from ..param_attr import ParamAttr __all__ = [ - 'create_tensor', 'cast', 'concat', 'sums', 'assign', + 'create_tensor', 'create_parameter', 'cast', 'concat', 'sums', 'assign', 'fill_constant_batch_size_like', 'fill_constant', 'ones', 'zeros' ] @@ -11,6 +12,33 @@ def create_tensor(dtype, name=None): return helper.create_variable(name=helper.name, dtype=dtype) +def create_parameter(shape, + dtype, + attr=None, + is_bias=False, + default_initializer=None): + """ + Create a parameter + Args: + shape(list[int]): shape of the parameter + dtype(string): element type of the parameter + attr(ParamAttr): attributes of the parameter + is_bias(bool): This can affect which default initializer is chosen + when default_initializer is None. If is_bias, + initializer.Constant(0.0) will be used. Otherwise, + Xavier() will be used. + default_initializer(Initializer): initializer for the parameter + + Returns: + Parameter: the created parameter + """ + helper = LayerHelper("create_parameter") + if attr is None: + attr = ParamAttr() + return helper.create_parameter(attr, shape, dtype, is_bias, + default_initializer) + + def cast(x, dtype): """ This function takes in the input with input_dtype @@ -180,7 +208,8 @@ def fill_constant_batch_size_like(input, Examples: .. code-block:: python - data = fluid.layers.fill_constant(shape=[1], value=0, dtype='int64') + data = fluid.layers.fill_constant_batch_size_like( + input=like, shape=[1], value=0, dtype='int64') """ helper = LayerHelper("fill_constant_batch_size_like", **locals()) out = helper.create_tmp_variable(dtype=dtype) -- GitLab From 6e5eae137da7624ba5f0304b71c0cb38b8208f38 Mon Sep 17 00:00:00 2001 From: xuwei06 Date: Mon, 8 Jan 2018 17:05:38 -0800 Subject: [PATCH 0169/2305] Unittest for fluid.backward.calc_gradient() --- .../v2/fluid/tests/test_calc_gradient.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 python/paddle/v2/fluid/tests/test_calc_gradient.py diff --git a/python/paddle/v2/fluid/tests/test_calc_gradient.py b/python/paddle/v2/fluid/tests/test_calc_gradient.py new file mode 100644 index 00000000000..c34c8ff6d14 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_calc_gradient.py @@ -0,0 +1,25 @@ +import unittest + +import paddle.v2.fluid as fluid +import paddle.v2.fluid.layers as layers +import paddle.v2.fluid.framework as framework +import paddle.v2.fluid.optimizer as optimizer +from paddle.v2.fluid.backward import calc_gradient + + +class TestCalcGradient(unittest.TestCase): + def test_calc_gradient(self): + x = layers.create_parameter(dtype="float32", shape=[5, 10]) + y = layers.create_parameter(dtype="float32", shape=[10, 8]) + mul_out = layers.mul(x=x, y=y) + mean_out = layers.mean(x=mul_out) + a = calc_gradient(mean_out, mul_out) + b = calc_gradient(mean_out, x) + place = fluid.CPUPlace() + exe = fluid.Executor(place) + exe.run(fluid.default_startup_program()) + exe.run(fluid.default_main_program(), feed={}, fetch_list=[a, b]) + + +if __name__ == "__main__": + unittest.main() -- GitLab From 50a02adf5e08abaa24f4c74d904be833c2f9e68a Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Wed, 10 Jan 2018 11:59:59 +0800 Subject: [PATCH 0170/2305] transpile program ok --- .../paddle/v2/fluid/distribute_transpiler.py | 93 +++++++++++++------ 1 file changed, 66 insertions(+), 27 deletions(-) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index ac13a7cb60a..76e8734f137 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -114,12 +114,15 @@ class DistributeTranspiler: # step3 send_inputs = [] send_outputs = [] - for _, splited in grad_var_mapping.iteritems(): - send_inputs.extend(splited) + for b in grad_blocks: # append by order + varname, block_id, _ = b.split(":") + send_inputs.append(grad_var_mapping[varname][int(block_id)]) + param_var_mapping = self._create_vars_from_blocklist(program, param_blocks) - for _, splited in param_var_mapping.iteritems(): - send_outputs.extend(splited) + for b in param_blocks: + varname, block_id, _ = b.split(":") + send_outputs.append(param_var_mapping[varname][int(block_id)]) # let send_op know which endpoint to send which var, eplist is of the same # order of send_inputs. eplist = split_method(send_inputs, pserver_endpoints) @@ -243,8 +246,37 @@ class DistributeTranspiler: var_list.append(var_each) return var_list - def _append_pserver_ops(self, opt_op, endpoint): + def _get_optimizer_input_shape(self, op_type, varkey, orig_shape, + param_shape): + """ + Returns the shape for optimizer inputs that need to be reshaped when + Param and Grad is splited to multiple servers. + """ + # HACK(typhoonzero): Should use functions of corresponding optimizer in + # optimizer.py to get the shape, do not bind this in the transpiler. + if op_type == "adam": + if varkey in ["Moment1", "Moment2"]: + return param_shape + elif op_type == "adagrad": + if varkey == "Moment": + return param_shape + elif op_type == "adamax": + if varkey in ["Moment", "InfNorm"]: + return param_shape + elif op_type == "momentum": + if varkey == "Velocity": + return param_shape + elif op_type == "": + if varkey == "Moment": + return param_shape + elif op_type == "sgd": + pass + return orig_shape + + def _append_pserver_ops(self, program, opt_op, endpoint): new_inputs = dict() + # update param/grad shape first, then other inputs like + # moment can use the updated shape for key, var in opt_op.inputs.iteritems(): if key == "Grad": grad_block = None @@ -256,7 +288,7 @@ class DistributeTranspiler: # do not append this op if current endpoint # is not dealing with this grad block return - merged_var = optimize_sub_program.global_block().create_var( + merged_var = program.global_block().create_var( name=grad_block.name, persistable=grad_block.persistable, dtype=grad_block.dtype, @@ -264,13 +296,12 @@ class DistributeTranspiler: # append merging ops if trainers > 1 if self.trainers > 1: vars2merge = self._create_var_for_trainers( - optimize_sub_program.global_block(), grad_block, - self.trainers) - optimize_sub_program.global_block().append_op( + program.global_block(), grad_block, self.trainers) + program.global_block().append_op( type="sum", inputs={"X": vars2merge}, outputs={"Out": merged_var}) - optimize_sub_program.global_block().append_op( + program.global_block().append_op( type="scale", inputs={"X": merged_var}, outputs={"Out": merged_var}, @@ -285,37 +316,45 @@ class DistributeTranspiler: break if not param_block: return - tmpvar = optimize_sub_program.global_block().create_var( + tmpvar = program.global_block().create_var( name=param_block.name, persistable=param_block.persistable, dtype=param_block.dtype, shape=param_block.shape) new_inputs[key] = tmpvar - else: - tmpvar = optimize_sub_program.global_block().create_var( - name=var.name, - persistable=var.persistable, - dtype=var.dtype, - shape=var.shape) - new_inputs[key] = tmpvar + + for key, var in opt_op.inputs.iteritems(): + if key in ["Param", "Grad"]: + continue + # update accumulator variable shape + param_shape = new_inputs["Param"].shape + new_shape = self._get_optimizer_input_shape(opt_op.type, key, + var.shape, param_shape) + print("var, new shape", key, var.name, new_shape) + tmpvar = program.global_block().create_var( + name=var.name, + persistable=var.persistable, + dtype=var.dtype, + shape=new_shape) + new_inputs[key] = tmpvar # FIXME: change outputs ParamOut - optimize_sub_program.global_block().append_op( + program.global_block().append_op( type=opt_op.type, inputs=new_inputs, outputs=opt_op.outputs, attrs=opt_op.attrs) - def _append_pserver_non_opt_ops(self, opt_op): + def _append_pserver_non_opt_ops(self, program, opt_op): for _, var in opt_op.inputs.iteritems(): - optimize_sub_program.global_block().create_var( + program.global_block().create_var( name=var.name, persistable=var.persistable, dtype=var.dtype, shape=var.shape) - optimize_sub_program.global_block().append_op( + program.global_block().append_op( type=opt_op.type, - inputs=new_inputs, + inputs=opt_op.inputs, outputs=opt_op.outputs, attrs=opt_op.attrs) @@ -331,15 +370,15 @@ class DistributeTranspiler: # step5 pserver_program = Program() for v in self.param_grad_ep_mapping[endpoint]["params"]: - self._clone_param(pserver_program.global_block(), v) + self._clone_var(pserver_program.global_block(), v) # step6 optimize_sub_program = Program() for opt_op in optimize_ops: - if opt_ops.inputs.has_key("Grad"): + if opt_op.inputs.has_key("Grad"): # append optimize_op - self._append_pserver_ops(opt_op, endpoint) + self._append_pserver_ops(optimize_sub_program, opt_op, endpoint) else: - self._append_pserver_non_opt_ops(opt_op) + self._append_pserver_non_opt_ops(optimize_sub_program, opt_op) pserver_program.global_block().append_op( type="recv", -- GitLab From 91f80f792d985fa2fe36385f53eae20dcc780470 Mon Sep 17 00:00:00 2001 From: Qiao Longfei Date: Wed, 10 Jan 2018 12:59:43 +0800 Subject: [PATCH 0171/2305] Topk share lod (#7373) * add lod tensor ToAbsOffset test * add share lod to topk op and softmax op --- paddle/framework/lod_tensor_test.cc | 16 ++++++++++++++++ paddle/operators/softmax_op.cc | 1 + paddle/operators/top_k_op.cc | 2 ++ 3 files changed, 19 insertions(+) diff --git a/paddle/framework/lod_tensor_test.cc b/paddle/framework/lod_tensor_test.cc index 52b87f48e53..baad9c6f98a 100644 --- a/paddle/framework/lod_tensor_test.cc +++ b/paddle/framework/lod_tensor_test.cc @@ -115,5 +115,21 @@ TEST(LoD, AppendLoD) { EXPECT_EQ(origin, expected); } +TEST(LoD, ToAbsOffset) { + LoD relative_lod; + relative_lod.push_back(std::vector({0, 2})); + relative_lod.push_back(std::vector({0, 1, 3})); + relative_lod.push_back(std::vector({0, 2, 4, 5})); + + LoD abs_lod = paddle::framework::ToAbsOffset(relative_lod); + + LoD expected; + expected.push_back(std::vector({0, 5})); + expected.push_back(std::vector({0, 2, 5})); + expected.push_back(std::vector({0, 2, 4, 5})); + + EXPECT_EQ(abs_lod, expected); +} + } // namespace framework } // namespace paddle diff --git a/paddle/operators/softmax_op.cc b/paddle/operators/softmax_op.cc index e7306bc5f13..cef1f1fc99d 100644 --- a/paddle/operators/softmax_op.cc +++ b/paddle/operators/softmax_op.cc @@ -31,6 +31,7 @@ class SoftmaxOp : public framework::OperatorWithKernel { PADDLE_ENFORCE(x_dims.size() == 2UL, "The input of softmax op must be a matrix."); ctx->SetOutputDim("Out", x_dims); + ctx->ShareLoD("X", /*->*/ "Out"); } }; diff --git a/paddle/operators/top_k_op.cc b/paddle/operators/top_k_op.cc index bb72210bb67..a8ddd729732 100644 --- a/paddle/operators/top_k_op.cc +++ b/paddle/operators/top_k_op.cc @@ -41,6 +41,8 @@ class TopkOp : public framework::OperatorWithKernel { dims[dims.size() - 1] = k; ctx->SetOutputDim("Out", dims); ctx->SetOutputDim("Indices", dims); + ctx->ShareLoD("X", "Out"); + ctx->ShareLoD("X", "Indices"); } }; -- GitLab From efe06caa3d296c612258aa47567cdaaec7021cc8 Mon Sep 17 00:00:00 2001 From: Qiao Longfei Date: Wed, 10 Jan 2018 13:00:04 +0800 Subject: [PATCH 0172/2305] change data type of beam_search op (#7374) --- paddle/operators/beam_search_op.cc | 6 +++--- python/paddle/v2/fluid/tests/test_beam_search_op.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/paddle/operators/beam_search_op.cc b/paddle/operators/beam_search_op.cc index 2e0513b37a2..ed2e7b738ac 100644 --- a/paddle/operators/beam_search_op.cc +++ b/paddle/operators/beam_search_op.cc @@ -39,7 +39,7 @@ void BeamSearch::operator()(const framework::LoDTensor &pre_ids, std::map> hash; framework::LoD new_lod; - auto *ids_data = selected_ids->mutable_data(platform::CPUPlace()); + auto *ids_data = selected_ids->mutable_data(platform::CPUPlace()); auto *scores_data = selected_scores->mutable_data(platform::CPUPlace()); @@ -66,7 +66,7 @@ void BeamSearch::operator()(const framework::LoDTensor &pre_ids, void BeamSearch::PruneEndidCandidates(const framework::LoDTensor &pre_ids, std::vector> *items) { - auto *pre_ids_data = pre_ids.data(); + auto *pre_ids_data = pre_ids.data(); for (size_t offset = 0; offset < items->size(); offset++) { auto prefix_id = pre_ids_data[offset]; @@ -127,7 +127,7 @@ bool BeamSearch::NextItemSet(std::vector *items) { auto abs_lod = framework::ToAbsOffset(ids.lod()); PADDLE_ENFORCE_GE(source_abs_two_level_lod.size(), 2UL); - auto *ids_data = ids.data(); + auto *ids_data = ids.data(); auto *scores_data = scores.data(); size_t instance_dim = 1; diff --git a/python/paddle/v2/fluid/tests/test_beam_search_op.py b/python/paddle/v2/fluid/tests/test_beam_search_op.py index 595f132fa85..319a7e49e35 100644 --- a/python/paddle/v2/fluid/tests/test_beam_search_op.py +++ b/python/paddle/v2/fluid/tests/test_beam_search_op.py @@ -37,13 +37,13 @@ class BeamSearchOpTester(unittest.TestCase): print 'lod', selected_ids.lod() def _create_pre_ids(self): - np_data = np.array([[1, 2, 3, 4]], dtype='int32') + np_data = np.array([[1, 2, 3, 4]], dtype='int64') tensor = create_tensor(self.scope, "pre_ids", np_data) def _create_ids(self): self.lod = [[0, 1, 4], [0, 1, 2, 3, 4]] np_data = np.array( - [[4, 2, 5], [2, 1, 3], [3, 5, 2], [8, 2, 1]], dtype='int32') + [[4, 2, 5], [2, 1, 3], [3, 5, 2], [8, 2, 1]], dtype='int64') tensor = create_tensor(self.scope, "ids", np_data) tensor.set_lod(self.lod) -- GitLab From 5f98500009e058a22211e55b7d9c5884d4d7a89f Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Wed, 10 Jan 2018 13:05:56 +0800 Subject: [PATCH 0174/2305] Make init device on all gpu by default (#7345) * "init use all default devices" * "fix init test" --- .../framework/device_data_transform_test.cu | 3 +- paddle/framework/init.cc | 39 ++++++------------- paddle/framework/init.h | 2 +- paddle/framework/init_test.cc | 17 +++----- paddle/framework/operator_test.cc | 8 ++-- paddle/inference/inference.cc | 2 +- paddle/platform/device_context.h | 2 + paddle/testing/paddle_gtest_main.cc | 6 +-- python/paddle/v2/fluid/__init__.py | 6 +-- .../v2/fluid/tests/test_batch_norm_op.py | 3 -- 10 files changed, 30 insertions(+), 58 deletions(-) diff --git a/paddle/framework/device_data_transform_test.cu b/paddle/framework/device_data_transform_test.cu index 9fb26f09c7e..5d89f5546fa 100644 --- a/paddle/framework/device_data_transform_test.cu +++ b/paddle/framework/device_data_transform_test.cu @@ -105,8 +105,7 @@ static void BuildVar(const std::string& param_name, TEST(Operator, CPUtoGPU) { using namespace paddle::framework; using namespace paddle::platform; - - ASSERT_EQ(InitDevices({"CPU", "GPU:0"}), true); + InitDevices(); paddle::framework::Scope scope; paddle::platform::CPUPlace cpu_place; diff --git a/paddle/framework/init.cc b/paddle/framework/init.cc index e7087e063cb..e12bac1d78e 100644 --- a/paddle/framework/init.cc +++ b/paddle/framework/init.cc @@ -40,40 +40,23 @@ void InitGflags(std::vector &argv) { }); } -bool InitDevices(const std::vector &devices) { - // device format - // CPU - // GPU:1 - // TODO(dzhwinter) : add device format annotation for users. +void InitDevices() { + /*Init all avaiable devices by default */ + std::vector places; - for (auto &device : devices) { - auto p = string::Piece(device); - if (string::HasPrefix(p, "CPU")) { - places.emplace_back(platform::CPUPlace()); - } else if (string::HasPrefix(p, "GPU")) { + places.emplace_back(platform::CPUPlace()); + #ifdef PADDLE_WITH_CUDA - auto pos = string::RFind(p, ':', string::Piece::npos); - auto number = device.substr(pos + 1); - places.emplace_back(platform::CUDAPlace(std::stoi(number))); + int count = platform::GetCUDADeviceCount(); + for (int i = 0; i < count; ++i) { + places.emplace_back(platform::CUDAPlace(i)); + } #else - LOG(WARNING) - << "'GPU' is not supported, Please re-compile with WITH_GPU option"; + LOG(WARNING) + << "'GPU' is not supported, Please re-compile with WITH_GPU option"; #endif - } else { - return false; - } - } - if (std::find_if(places.begin(), places.end(), - [&](const platform::Place &place) { - return platform::is_cpu_place(place); - }) == places.end()) { - places.emplace_back(platform::CPUPlace()); - LOG(WARNING) << "Not specified CPU device, create CPU by Default."; - } platform::DeviceContextPool::Init(places); - // framework::UseALL(); - return true; } void InitGLOG(const std::string &prog_name) { diff --git a/paddle/framework/init.h b/paddle/framework/init.h index 9c84a03ded5..c8fd964d006 100644 --- a/paddle/framework/init.h +++ b/paddle/framework/init.h @@ -24,7 +24,7 @@ void InitGflags(std::vector &argv); void InitGLOG(const std::string &prog_name); -bool InitDevices(const std::vector &devices); +void InitDevices(); } // namespace framework } // namespace paddle diff --git a/paddle/framework/init_test.cc b/paddle/framework/init_test.cc index f0788051d48..f837a965d3b 100644 --- a/paddle/framework/init_test.cc +++ b/paddle/framework/init_test.cc @@ -14,18 +14,13 @@ limitations under the License. */ #include "gtest/gtest.h" #include "paddle/framework/init.h" +#include "paddle/platform/device_context.h" -TEST(Init, InitDevices) { +TEST(InitDevices, CPU) { using paddle::framework::InitDevices; - std::vector ds1 = {"CPU"}; - ASSERT_EQ(InitDevices(ds1), true); + using paddle::platform::DeviceContextPool; -#ifdef PADDLE_WITH_CUDA - std::vector ds2 = {"CPU", "GPU:0", "GPU:1"}; - ASSERT_EQ(InitDevices(ds2), true); - - // test re-init - std::vector ds3 = {"GPU:0", "GPU:1"}; - ASSERT_EQ(InitDevices(ds3), true); -#endif + InitDevices(); + DeviceContextPool& pool = DeviceContextPool::Instance(); + ASSERT_GE(pool.size(), 1U); } diff --git a/paddle/framework/operator_test.cc b/paddle/framework/operator_test.cc index d002f3f2388..b69d7c7a740 100644 --- a/paddle/framework/operator_test.cc +++ b/paddle/framework/operator_test.cc @@ -69,7 +69,7 @@ REGISTER_OP_WITHOUT_GRADIENT(test_operator, paddle::framework::OpWithoutKernelCheckerMaker); TEST(OperatorBase, all) { - paddle::framework::InitDevices({"CPU"}); + paddle::framework::InitDevices(); paddle::framework::proto::OpDesc op_desc; op_desc.set_type("test_operator"); BuildVar("input", {"IN1"}, op_desc.add_inputs()); @@ -195,7 +195,7 @@ REGISTER_OP_CPU_KERNEL(op_with_kernel, // test with single input TEST(OpKernel, all) { - paddle::framework::InitDevices({"CPU"}); + paddle::framework::InitDevices(); paddle::framework::proto::OpDesc op_desc; op_desc.set_type("op_with_kernel"); BuildVar("x", {"IN1"}, op_desc.add_inputs()); @@ -225,7 +225,7 @@ REGISTER_OP_CPU_KERNEL(op_multi_inputs_with_kernel, TEST(OpKernel, multi_inputs) { using namespace paddle::framework; - paddle::framework::InitDevices({"CPU"}); + paddle::framework::InitDevices(); proto::OpDesc op_desc; op_desc.set_type("op_multi_inputs_with_kernel"); @@ -264,7 +264,7 @@ class OperatorClone : public paddle::framework::OperatorBase { }; TEST(Operator, Clone) { - paddle::framework::InitDevices({"CPU"}); + paddle::framework::InitDevices(); OperatorClone a("ABC", paddle::framework::VariableNameMap{}, paddle::framework::VariableNameMap{}, paddle::framework::AttributeMap{}); diff --git a/paddle/inference/inference.cc b/paddle/inference/inference.cc index 49e39358e81..37b8b20ddfc 100644 --- a/paddle/inference/inference.cc +++ b/paddle/inference/inference.cc @@ -169,7 +169,7 @@ void InferenceEngine::Execute(const std::vector& feeds, } auto* place = new platform::CPUPlace(); - framework::InitDevices({"CPU"}); + framework::InitDevices(); framework::Executor* executor = new framework::Executor(*place); framework::Scope* scope = new framework::Scope(); diff --git a/paddle/platform/device_context.h b/paddle/platform/device_context.h index 7a0040c9c22..9826a642768 100644 --- a/paddle/platform/device_context.h +++ b/paddle/platform/device_context.h @@ -185,6 +185,8 @@ class DeviceContextPool { const typename DefaultDeviceContextType::TYPE*>(Get(place)); } + size_t size() const { return device_contexts_.size(); } + private: static DeviceContextPool* pool; constexpr static int LEFT_SHIFT = 8; diff --git a/paddle/testing/paddle_gtest_main.cc b/paddle/testing/paddle_gtest_main.cc index 108ff335bf6..a7fb50ee414 100644 --- a/paddle/testing/paddle_gtest_main.cc +++ b/paddle/testing/paddle_gtest_main.cc @@ -34,11 +34,11 @@ int main(int argc, char** argv) { google::ParseCommandLineFlags(&new_argc, &new_argv_address, false); testing::InitGoogleTest(&argc, argv); paddle::memory::Used(paddle::platform::CPUPlace()); - std::vector devs = {"CPU"}; + #ifdef PADDLE_WITH_CUDA paddle::memory::Used(paddle::platform::CUDAPlace(0)); - devs.push_back("GPU:0"); #endif - paddle::framework::InitDevices(devs); + + paddle::framework::InitDevices(); return RUN_ALL_TESTS(); } diff --git a/python/paddle/v2/fluid/__init__.py b/python/paddle/v2/fluid/__init__.py index 3f178e252c3..ccd5998e359 100644 --- a/python/paddle/v2/fluid/__init__.py +++ b/python/paddle/v2/fluid/__init__.py @@ -62,11 +62,7 @@ def __bootstrap__(): core.init_gflags([sys.argv[0]] + ["--tryfromenv=" + ",".join(read_env_flags)]) core.init_glog(sys.argv[0]) - - if core.is_compile_gpu(): - core.init_devices(["CPU", "GPU:0"]) - else: - core.init_devices(["CPU"]) + core.init_devices() __bootstrap__() diff --git a/python/paddle/v2/fluid/tests/test_batch_norm_op.py b/python/paddle/v2/fluid/tests/test_batch_norm_op.py index abbd48d2b84..ac9418549f4 100644 --- a/python/paddle/v2/fluid/tests/test_batch_norm_op.py +++ b/python/paddle/v2/fluid/tests/test_batch_norm_op.py @@ -341,9 +341,6 @@ class TestBatchNormOp(OpTest): if core.is_compile_gpu() and core.op_support_gpu("batch_norm"): places.append(core.CUDAPlace(0)) - core.init_devices(["CPU", "GPU:0"]) - else: - core.init_devices(["CPU"]) for place in places: for data_format in ["NCHW", "NHWC"]: test_with_place(place, data_format, [2, 3, 4, 5]) -- GitLab From 4059c9ca7fcff892ab198350e1decda71e516e01 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Wed, 10 Jan 2018 13:18:40 +0800 Subject: [PATCH 0175/2305] Polish GetPlacesOp --- paddle/operators/get_places_op.cc | 33 ++++++++++++++----------- python/paddle/v2/fluid/layers/device.py | 17 +++++++------ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/paddle/operators/get_places_op.cc b/paddle/operators/get_places_op.cc index 291bbbcb3a7..24fafb23074 100644 --- a/paddle/operators/get_places_op.cc +++ b/paddle/operators/get_places_op.cc @@ -39,17 +39,19 @@ class GetPlacesOp : public framework::OperatorBase { : OperatorBase(type, inputs, outputs, attrs) {} void Run(const framework::Scope &scope, const platform::Place &place) const override { - std::string device_type = Attr("device_type"); + bool is_gpu; + if (Attr("device_type") == "AUTO") { + is_gpu = platform::is_gpu_place(place); + } else { + is_gpu = Attr("device_type") == "CUDA"; + } auto device_count = static_cast(Attr("device_count")); if (device_count == 0) { - if (device_type == "CUDA") { - device_count = CUDADevCount(); - } else if (device_type == "CPU") { - device_count = std::thread::hardware_concurrency(); - } + device_count = + is_gpu ? CUDADevCount() : std::thread::hardware_concurrency(); } PADDLE_ENFORCE_NE(device_count, 0, "Cannot indicate %s device count", - device_type); + is_gpu ? "GPU" : "CPU"); auto out_var_name = Output("Out"); auto &places = @@ -57,14 +59,14 @@ class GetPlacesOp : public framework::OperatorBase { "Output variable %s cannot be found", out_var_name) .GetMutable()); places.reserve(device_count); - if (device_type == "CUDA") { + if (is_gpu) { PADDLE_ENFORCE_LE(device_count, CUDADevCount(), "Only %d CUDA devices found, cannot set to %d", CUDADevCount(), device_count); for (size_t i = 0; i < device_count; ++i) { - places.emplace_back(platform::CUDAPlace(i)); + places.emplace_back(platform::CUDAPlace(static_cast(i))); } - } else if (device_type == "CPU") { + } else { for (size_t i = 0; i < device_count; ++i) { places.emplace_back(platform::CPUPlace()); } @@ -77,10 +79,10 @@ class GetPlacesOpProtoMaker : public framework::OpProtoAndCheckerMaker { GetPlacesOpProtoMaker(OpProto *proto, OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddOutput("Out", "vector of Place"); - AddAttr("device_count", "device count").SetDefault(1); - AddAttr("device_type", - R"(device type must be in ["CPU", "CUDA"])") - .InEnum({"CPU", "CUDA"}); + AddAttr("device_count", "device count").SetDefault(0); + AddAttr("device_type", "device type") + .InEnum({"CUDA", "CPU", "AUTO"}) + .SetDefault("AUTO"); AddComment(R"DOC( Returns a list of places based on flags. The list will be used for parallel execution. @@ -111,4 +113,5 @@ class GetPlacesInferShape : public framework::InferShapeBase { namespace ops = paddle::operators; REGISTER_OPERATOR(get_places, ops::GetPlacesOp, ops::GetPlacesOpProtoMaker, - ops::GetPlacesInferVarType, ops::GetPlacesInferShape); + ops::GetPlacesInferVarType, ops::GetPlacesInferShape, + paddle::framework::EmptyGradOpMaker); diff --git a/python/paddle/v2/fluid/layers/device.py b/python/paddle/v2/fluid/layers/device.py index c2355ed8020..775d40e5b5e 100644 --- a/python/paddle/v2/fluid/layers/device.py +++ b/python/paddle/v2/fluid/layers/device.py @@ -4,19 +4,22 @@ All util layers. from ..layer_helper import LayerHelper from ..framework import unique_name +from ..registry import autodoc __all__ = ['get_places'] -def get_places(device_count=0, device_type="CPU"): +@autodoc +def get_places(device_count=None, device_type=None): helper = LayerHelper('get_places', **locals()) out_places = helper.create_variable(name=unique_name(helper.name + ".out")) + attrs = dict() + if device_count is not None: + attrs['device_count'] = int(device_count) + if device_type is not None: + attrs['device_type'] = str(device_type) + helper.append_op( - type='get_places', - outputs={"Out": [out_places]}, - attrs={ - "device_type": device_type, - 'device_count': device_count, - }) + type='get_places', outputs={"Out": [out_places]}, attrs=attrs) return out_places -- GitLab From 42daf4c3020f358c6798cb90d87a320bc869737e Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Tue, 9 Jan 2018 23:12:26 -0800 Subject: [PATCH 0176/2305] "add sync op" --- python/paddle/v2/fluid/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/__init__.py b/python/paddle/v2/fluid/__init__.py index ccd5998e359..c163d9a92bb 100644 --- a/python/paddle/v2/fluid/__init__.py +++ b/python/paddle/v2/fluid/__init__.py @@ -58,7 +58,7 @@ def __bootstrap__(): read_env_flags = ['use_pinned_memory', 'check_nan_inf'] if core.is_compile_gpu(): - read_env_flags.append('fraction_of_gpu_memory_to_use') + read_env_flags.append(['fraction_of_gpu_memory_to_use', 'op_sync']) core.init_gflags([sys.argv[0]] + ["--tryfromenv=" + ",".join(read_env_flags)]) core.init_glog(sys.argv[0]) -- GitLab From f0316bdbbd351cff24b49b9376fab9b56f962e3d Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Tue, 9 Jan 2018 23:13:01 -0800 Subject: [PATCH 0177/2305] "add flags" --- paddle/framework/operator.cc | 10 ++++++++-- paddle/platform/gpu_info.cc | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index 35ebe48ba68..c1a6d0221ba 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -543,8 +543,14 @@ void OperatorWithKernel::Run(const Scope& scope, auto kernel_iter = kernels.find(expected_kernel_key); - kernel_iter->second->Compute(ExecutionContext( - *this, new_scope, *pool.Get(expected_kernel_key.place_))); + auto* new_dev_ctx = pool.Get(expected_kernel_key.place_); + kernel_iter->second->Compute( + ExecutionContext(*this, new_scope, *new_dev_ctx)); + + /*For profiling/benchmark only*/ + if (FLAGS_op_sync) { + new_dev_ctx->Wait(); + } } proto::DataType OperatorWithKernel::IndicateDataType( diff --git a/paddle/platform/gpu_info.cc b/paddle/platform/gpu_info.cc index 7037551d754..9d3147362ab 100644 --- a/paddle/platform/gpu_info.cc +++ b/paddle/platform/gpu_info.cc @@ -22,6 +22,10 @@ DEFINE_double(fraction_of_gpu_memory_to_use, 0.92, "Default use 92% of GPU memory for PaddlePaddle," "reserve the rest for page tables, etc"); +DEFINE_bool(op_sync, false, + "Default cuda is asynchronous device, set to True will" + "force op run in synchronous mode."); + namespace paddle { namespace platform { -- GitLab From a6edc0389e11787c57aa1881ae01bebf025715f2 Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Tue, 9 Jan 2018 23:19:22 -0800 Subject: [PATCH 0178/2305] "fix CI" --- paddle/framework/operator.cc | 5 +++++ paddle/platform/gpu_info.cc | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index c1a6d0221ba..0f6071a69e4 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -11,6 +11,7 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +#include #include #include @@ -22,6 +23,10 @@ limitations under the License. */ #include "paddle/framework/shape_inference.h" #include "paddle/framework/var_type.h" +DEFINE_bool(op_sync, false, + "Default cuda is asynchronous device, set to True will" + "force op run in synchronous mode."); + namespace paddle { namespace framework { diff --git a/paddle/platform/gpu_info.cc b/paddle/platform/gpu_info.cc index 9d3147362ab..7037551d754 100644 --- a/paddle/platform/gpu_info.cc +++ b/paddle/platform/gpu_info.cc @@ -22,10 +22,6 @@ DEFINE_double(fraction_of_gpu_memory_to_use, 0.92, "Default use 92% of GPU memory for PaddlePaddle," "reserve the rest for page tables, etc"); -DEFINE_bool(op_sync, false, - "Default cuda is asynchronous device, set to True will" - "force op run in synchronous mode."); - namespace paddle { namespace platform { -- GitLab From 4bcc0b64cbb7533cf8f03bc3f1ba39a6254477ee Mon Sep 17 00:00:00 2001 From: "Yang Yang(Tony)" Date: Wed, 10 Jan 2018 15:33:10 +0800 Subject: [PATCH 0179/2305] [WIP] feature/parallel_gpu (#7293) feature/parallel_gpu --- paddle/framework/lod_tensor.cc | 61 +++++++++---------- paddle/framework/tensor_util.h | 36 +++++------ paddle/framework/var_desc.cc | 2 +- paddle/operators/get_places_op.cc | 3 +- paddle/operators/parallel_do_op.cc | 56 +++++++++++------ .../paddle/v2/fluid/tests/test_parallel_op.py | 2 +- 6 files changed, 89 insertions(+), 71 deletions(-) diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index 506fde44053..7ae94c64653 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -44,9 +44,19 @@ std::ostream &operator<<(std::ostream &os, const LoD &lod) { } std::ostream &operator<<(std::ostream &os, const LoDTensor &t) { - PADDLE_ENFORCE(platform::is_cpu_place(t.place())); PADDLE_ENFORCE(t.type().hash_code() == typeid(float).hash_code()); + if (!platform::is_cpu_place(t.place())) { + LoDTensor tt; + framework::Copy(t, platform::CPUPlace(), &tt); + platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); + auto &dev_ctx = *pool.Get(t.place()); + dev_ctx.Wait(); + + os << tt; + return os; + } + os << "dim: " << t.dims() << "\n"; os << "lod: " << t.lod() << "\n"; @@ -211,38 +221,23 @@ void DeserializeFromStream(std::istream &is, LoDTensor *tensor, DeserializeFromStream(is, static_cast(tensor), dev_ctx); } +// TODO(tonyyang-svail): make this function support LoD std::vector LoDTensor::SplitLoDTensor( const std::vector places) const { check_memory_size(); - // PADDLE_ENFORCE(lod().empty() || (lod().size() == 1 && lod()[0].empty()) - // , "Disable parallel lod for now"); PADDLE_ENFORCE(lod().empty(), "Disable parallel lod for now"); PADDLE_ENFORCE(dims()[0] % places.size() == 0, "Batch size should be divided by places size"); std::vector lods; for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { - size_t begin = place_idx * dims()[0] / places.size(); - size_t end = (place_idx + 1) * dims()[0] / places.size(); - auto src = Slice(static_cast(begin), static_cast(end)); + int begin = place_idx * dims()[0] / places.size(); + int end = (place_idx + 1) * dims()[0] / places.size(); - LoDTensor dst; - dst.Resize(src.dims()); + auto src = Slice(begin, end); auto &dst_place = places[place_idx]; - auto dst_ptr = dst.mutable_data(dst_place, src.type()); - - // TODO(tonyyang-svail): - // change the following to framework::Copy - auto src_place = src.place(); - auto src_ptr = src.data(); - auto size = src.numel() * SizeOfType(src.type()); - if (platform::is_cpu_place(src_place) && - platform::is_cpu_place(dst_place)) { - memory::Copy(boost::get(dst_place), dst_ptr, - boost::get(src_place), src_ptr, size); - } else { - PADDLE_THROW("Not Implemented"); - } + LoDTensor dst; + framework::Copy(src, dst_place, &dst); lods.emplace_back(dst); } @@ -250,28 +245,30 @@ std::vector LoDTensor::SplitLoDTensor( return lods; } +// TODO(tonyyang-svail): make this function support LoD void LoDTensor::MergeLoDTensor( - const std::vector &lod_tensors, platform::Place place) { - PADDLE_ENFORCE(platform::is_cpu_place(place)); + const std::vector &lod_tensors, + platform::Place dst_place) { PADDLE_ENFORCE(!lod_tensors.empty()); - framework::DDim new_dim = lod_tensors[0]->dims(); std::type_index new_type = lod_tensors[0]->type(); + auto new_layout = lod_tensors[0]->layout(); for (auto *lod : lod_tensors) { PADDLE_ENFORCE(new_dim == lod->dims()); PADDLE_ENFORCE(new_type == lod->type()); - PADDLE_ENFORCE(platform::is_cpu_place(lod->place())); + PADDLE_ENFORCE(new_layout == lod->layout()); } new_dim[0] *= lod_tensors.size(); Resize(new_dim); + set_layout(new_layout); - auto *dst_ptr = reinterpret_cast(mutable_data(place, new_type)); + mutable_data(dst_place, new_type); + int begin = 0; for (auto *src : lod_tensors) { - auto size = src->numel() * SizeOfType(src->type()); - memory::Copy(boost::get(place), dst_ptr, - boost::get(src->place()), - src->data(), size); - dst_ptr += size; + int end = begin + src->dims()[0]; + auto dst = Slice(begin, end); + framework::Copy(*src, dst_place, &dst); + begin = end; } } diff --git a/paddle/framework/tensor_util.h b/paddle/framework/tensor_util.h index 7c56ccf17f9..f541d2ba693 100644 --- a/paddle/framework/tensor_util.h +++ b/paddle/framework/tensor_util.h @@ -31,9 +31,10 @@ namespace framework { * * @note Copy supports CPU <-> GPU, GPU <-> GPU. */ - inline void Copy(const Tensor& src, const platform::Place& dst_place, const platform::DeviceContext& ctx, Tensor* dst) { + VLOG(3) << "Copy " << src.dims() << " from " << src.place() << " to " + << dst_place; src.check_memory_size(); dst->Resize(src.dims()); @@ -88,26 +89,25 @@ inline void Copy(const Tensor& src, const platform::Place& dst_place, } /** - * @brief Copy supports CPU <-> CPU + * @brief Wrapper on + * Copy(const Tensor& src, const platform::Place& dst_place, + * const platform::DeviceContext& ctx, Tensor* dst); + * + * @param[in] src The external tensor. + * @param[in] dst_place The dst place. + * + * @note Copy supports CPU <-> GPU, GPU <-> GPU. */ inline void Copy(const Tensor& src, const platform::Place& dst_place, Tensor* dst) { - src.check_memory_size(); - dst->Resize(src.dims()); - dst->set_layout(src.layout()); - - auto src_place = src.place(); - auto src_ptr = src.data(); - - auto dst_ptr = dst->mutable_data(dst_place, src.type()); - - auto size = src.numel() * SizeOfType(src.type()); - - PADDLE_ENFORCE(platform::is_cpu_place(src_place) && - platform::is_cpu_place(dst_place)); - - memory::Copy(boost::get(dst_place), dst_ptr, - boost::get(src_place), src_ptr, size); + platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance(); + const platform::DeviceContext* dev_ctx; + if (platform::is_gpu_place(src.place())) { + dev_ctx = pool.Get(src.place()); + } else { + dev_ctx = pool.Get(dst_place); + } + Copy(src, dst_place, *dev_ctx, dst); } /** diff --git a/paddle/framework/var_desc.cc b/paddle/framework/var_desc.cc index aeab18d7214..62ab6593ef2 100644 --- a/paddle/framework/var_desc.cc +++ b/paddle/framework/var_desc.cc @@ -74,7 +74,7 @@ const proto::TensorDesc &VarDesc::tensor_desc() const { case proto::VarDesc::LOD_TENSOR_ARRAY: return desc_.tensor_array().tensor(); default: - PADDLE_THROW("The type of var '", this->Name(), "' is unsupported."); + PADDLE_THROW("The type of var %s is unsupported.", this->Name()); } } diff --git a/paddle/operators/get_places_op.cc b/paddle/operators/get_places_op.cc index 291bbbcb3a7..2c714ac43d4 100644 --- a/paddle/operators/get_places_op.cc +++ b/paddle/operators/get_places_op.cc @@ -111,4 +111,5 @@ class GetPlacesInferShape : public framework::InferShapeBase { namespace ops = paddle::operators; REGISTER_OPERATOR(get_places, ops::GetPlacesOp, ops::GetPlacesOpProtoMaker, - ops::GetPlacesInferVarType, ops::GetPlacesInferShape); + ops::GetPlacesInferVarType, ops::GetPlacesInferShape, + paddle::framework::EmptyGradOpMaker); diff --git a/paddle/operators/parallel_do_op.cc b/paddle/operators/parallel_do_op.cc index a6bc70f4c89..e1bec0421e7 100644 --- a/paddle/operators/parallel_do_op.cc +++ b/paddle/operators/parallel_do_op.cc @@ -39,6 +39,7 @@ void SplitTensorAndMoveTensorToScopes( const std::vector &sub_scopes, const std::vector &places, const std::vector &names) { + PADDLE_ENFORCE_EQ(sub_scopes.size(), places.size()); for (auto &argu : names) { auto *var = scope.FindVar(argu); const auto &tensor = var->Get(); @@ -54,6 +55,15 @@ void SplitTensorAndMoveTensorToScopes( } } +void WaitOnPlaces(const std::vector places) { + platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); + + for (auto &place : places) { + auto &dev_ctx = *pool.Get(place); + dev_ctx.Wait(); + } +} + class ParallelDoOp : public framework::OperatorBase { public: ParallelDoOp(const std::string &type, @@ -71,10 +81,7 @@ class ParallelDoOp : public framework::OperatorBase { auto *block = Attr(kParallelBlock); auto *program = block->Program(); - // TODO(tonyyang-svail): get places from input - std::vector places; - places.emplace_back(platform::CPUPlace()); - places.emplace_back(platform::CPUPlace()); + auto &places = scope.FindVar(Input(kPlaces))->Get(); auto &sub_scopes = *scope.FindVar(Output(kParallelScopes)) ->GetMutable>(); @@ -82,8 +89,22 @@ class ParallelDoOp : public framework::OperatorBase { sub_scopes.push_back(&scope.NewScope()); } + // split input SplitTensorAndMoveTensorToScopes(scope, sub_scopes, places, Inputs(kInputs)); + // copy parameter + for (auto ¶m : Inputs(kParameters)) { + PADDLE_ENFORCE(scope.FindVar(param)->IsType(), + "Only support parameter type as LoDTensor"); + auto &src = scope.FindVar(param)->Get(); + for (size_t i = 0; i < places.size(); ++i) { + auto &place = places[i]; + auto *sub_scope = sub_scopes[i]; + auto *dst = sub_scope->Var(param)->GetMutable(); + framework::Copy(src, place, dst); + } + } + WaitOnPlaces(places); std::vector> workers; workers.reserve(places.size()); @@ -93,12 +114,6 @@ class ParallelDoOp : public framework::OperatorBase { auto &place = places[place_idx]; auto *cur_scope = sub_scopes[place_idx]; - // copy parameter - // some version of boost lacks != for boost::variant - if (!(dev_ctx.GetPlace() == place)) { - PADDLE_THROW("Not Implemented"); - } - workers.emplace_back(framework::Async([program, cur_scope, place, block] { framework::Executor executor(place); executor.Run(*program, cur_scope, block->ID(), @@ -108,6 +123,7 @@ class ParallelDoOp : public framework::OperatorBase { for (auto &worker : workers) { worker.wait(); } + WaitOnPlaces(places); // merge output for (auto &o_name : Outputs(kOutputs)) { @@ -121,6 +137,7 @@ class ParallelDoOp : public framework::OperatorBase { scope.FindVar(o_name)->GetMutable(); lod_tensor_to_be_merged->MergeLoDTensor(lod_tensors, dev_ctx.GetPlace()); } + WaitOnPlaces(places); } }; @@ -161,15 +178,14 @@ class ParallelDoGradOp : public OperatorBase { auto &sub_scopes = scope.FindVar(Input(kParallelScopes)) ->Get>(); - // TODO(tonyyang-svail): get places from input - std::vector places; - places.emplace_back(platform::CPUPlace()); - places.emplace_back(platform::CPUPlace()); + auto &places = scope.FindVar(Input(kPlaces))->Get(); // feed output@grad SplitTensorAndMoveTensorToScopes(scope, sub_scopes, places, Inputs(framework::GradVarName(kOutputs))); + WaitOnPlaces(places); + // for debugging for (auto &s : Inputs(framework::GradVarName(kOutputs))) { VLOG(3) << s; VLOG(3) << scope.FindVar(s)->Get(); @@ -196,10 +212,11 @@ class ParallelDoGradOp : public OperatorBase { for (auto &worker : workers) { worker.wait(); } + WaitOnPlaces(places); // merge grad for (auto &s : Outputs(framework::GradVarName(kParameters))) { - VLOG(3) << s; + VLOG(3) << "merge grad " << s; auto &t = sub_scopes[0]->FindVar(s)->Get(); VLOG(3) << t; @@ -216,7 +233,8 @@ class ParallelDoGradOp : public OperatorBase { auto sum_op = framework::OpRegistry::CreateOp( "sum", {{"X", {s, s_buf}}}, {{"Out", {s}}}, framework::AttributeMap{}); - sum_op->Run(*sub_scopes[0], place); + sum_op->Run(*sub_scopes[0], places[0]); + WaitOnPlaces(places); } VLOG(3) << t; @@ -236,8 +254,10 @@ class ParallelDoGradOpDescMaker : public framework::SingleGradOpDescMaker { for (auto &input_param : this->InputNames()) { VLOG(3) << input_param; grad->SetInput(input_param, this->Input(input_param)); - grad->SetOutput(framework::GradVarName(input_param), - this->InputGrad(input_param, false)); + if (input_param != kPlaces) { + grad->SetOutput(framework::GradVarName(input_param), + this->InputGrad(input_param, false)); + } } for (auto &output_param : this->OutputNames()) { diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index 2788f4e519b..59ed041e7fa 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -18,7 +18,7 @@ class ParallelOpTest(unittest.TestCase): append_batch_size=False, stop_gradient=False) - places = fluid.default_main_program().global_block().create_var() + places = layers.get_places(device_count=4) pd = layers.ParallelDo(places=places) with pd.do(): -- GitLab From 10779460c5c127c257203d95d5f4740db4d55cad Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 10 Jan 2018 08:03:08 +0000 Subject: [PATCH 0180/2305] Simplify calc in test_sequence_erase_op --- paddle/operators/sequence_erase_op.cc | 2 +- paddle/operators/sequence_erase_op.cu | 2 + paddle/operators/sequence_erase_op.h | 25 ++++++---- .../v2/fluid/tests/test_sequence_erase_op.py | 46 +++++-------------- 4 files changed, 30 insertions(+), 45 deletions(-) diff --git a/paddle/operators/sequence_erase_op.cc b/paddle/operators/sequence_erase_op.cc index 331970b3f8e..d17b2686238 100644 --- a/paddle/operators/sequence_erase_op.cc +++ b/paddle/operators/sequence_erase_op.cc @@ -50,7 +50,7 @@ class SequenceEraseOpMaker : public framework::OpProtoAndCheckerMaker { AddComment(R"DOC( Sequence Erase Operator. -Sequence erase operator erases tokens specified by Attr(tokens) in the input +Sequence erase operator erases tokens specified by Attr(tokens) from the input sequences Input(X), and outputs the remaining data and modifies the LoD information at the same time. For example, given a 2-D LoDTensor diff --git a/paddle/operators/sequence_erase_op.cu b/paddle/operators/sequence_erase_op.cu index 3695a24cb73..5da8eba3e1a 100644 --- a/paddle/operators/sequence_erase_op.cu +++ b/paddle/operators/sequence_erase_op.cu @@ -70,6 +70,8 @@ class SequenceEraseOpCUDAKernel : public framework::OpKernel { auto lod = in->lod(); PADDLE_ENFORCE_EQ(lod.size(), 1UL, "Only support one level sequence now."); + PADDLE_ENFORCE_EQ(lod[0].back(), (size_t)in->numel(), + "The actual size mismatches with the LoD information."); auto tokens = ctx.Attr>("tokens"); auto tokens_len = tokens.size(); auto in_len = in->numel(); diff --git a/paddle/operators/sequence_erase_op.h b/paddle/operators/sequence_erase_op.h index 92aa4a82b09..cb2d7be009d 100644 --- a/paddle/operators/sequence_erase_op.h +++ b/paddle/operators/sequence_erase_op.h @@ -28,22 +28,27 @@ class SequenceEraseKernel : public framework::OpKernel { auto lod = in->lod(); PADDLE_ENFORCE_EQ(lod.size(), 1UL, "Only support one level sequence now."); + PADDLE_ENFORCE_EQ(lod[0].back(), (size_t)in->numel(), + "The actual size mismatches with the LoD information."); auto tokens = ctx.Attr>("tokens"); auto in_len = in->numel(); auto in_dat = in->data(); auto lod0 = lod[0]; + std::vector num_erased(in_len + 1, 0); - for (int64_t i = 1; i < in_len + 1; ++i) { - num_erased[i] = num_erased[i - 1]; - if (std::find(tokens.begin(), tokens.end(), in_dat[i - 1]) != - tokens.end()) { - num_erased[i] += 1; + std::vector out_lod0(1, 0); + for (size_t i = 0; i < lod0.size() - 1; ++i) { + size_t num_out = 0; + for (auto j = lod0[i] + 1; j <= lod0[i + 1]; ++j) { + num_erased[j] = num_erased[j - 1]; + if (std::find(tokens.begin(), tokens.end(), in_dat[j - 1]) != + tokens.end()) { + num_erased[j] += 1; + } else { + num_out += 1; + } } - } - - std::vector out_lod0(lod0.size(), 0); - for (size_t i = 1; i < lod0.size(); ++i) { - out_lod0[i] = lod0[i] - num_erased[lod0[i]]; + out_lod0.push_back(out_lod0.back() + num_out); } auto out_len = in_len - num_erased[in_len]; diff --git a/python/paddle/v2/fluid/tests/test_sequence_erase_op.py b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py index 78105334f5f..bf257fefea0 100644 --- a/python/paddle/v2/fluid/tests/test_sequence_erase_op.py +++ b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py @@ -4,32 +4,23 @@ from op_test import OpTest def sequence_erase(in_seq, lod0, tokens): - # num_erased[i]: the number of elments to be removed before #i elements - num_erased = [0] * (len(in_seq) + 1) - for i in range(1, len(in_seq) + 1): - num_erased[i] = num_erased[i - 1] - if in_seq[i - 1] in tokens: - num_erased[i] += 1 - - # recalculate lod information - new_lod0 = [0] * len(lod0) - for i in range(1, len(lod0)): - new_lod0[i] = lod0[i] - num_erased[lod0[i]] - - out_seq = np.zeros( - (len(in_seq) - num_erased[len(in_seq)], 1)).astype("int32") - for i in range(0, len(in_seq)): - if num_erased[i] == num_erased[i + 1]: - out_seq[i - num_erased[i]] = in_seq[i] - # else in_seq[i] needs to be removed - return out_seq, new_lod0 + new_lod0 = [0] + out_seq = [] + for i in range(0, len(lod0) - 1): + num_out = 0 + for dat in in_seq[lod0[i]:lod0[i + 1]]: + if dat not in tokens: + out_seq.append(dat) + num_out += 1 + new_lod0.append(new_lod0[-1] + num_out) + return np.array(out_seq).astype("int32"), new_lod0 class TestSequenceEraseOp(OpTest): def setUp(self): self.op_type = "sequence_erase" - in_seq = np.random.randint(0, 10, (10, 1)).astype("int32") - lod = [[0, 3, 6, 10]] + in_seq = np.random.randint(0, 10, (30, 1)).astype("int32") + lod = [[0, 9, 13, 24, 30]] tokens = [2, 3, 5] out_seq, new_lod0 = sequence_erase(in_seq, lod[0], tokens) self.attrs = {'tokens': tokens} @@ -41,17 +32,4 @@ class TestSequenceEraseOp(OpTest): if __name__ == '__main__': - """ - in_seq = np.random.randint(0, 10, (30, 1)).astype("int32") - lod0 = [0, 5, 15, 30] - tokens = [2, 5] - out_seq, new_lod = sequence_erase(in_seq, lod0, tokens) - - print lod0, new_lod - print("compare") - for i in range(0, len(lod0)-1): - print(np.transpose(in_seq[lod0[i] : lod0[i+1]])) - print(np.transpose(out_seq[new_lod[i] : new_lod[i+1]])) - print("\n") - """ unittest.main() -- GitLab From c7eb199b2e94292fc14b10cb75efd0b4bda47e56 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Wed, 10 Jan 2018 16:13:10 +0800 Subject: [PATCH 0181/2305] Init commit --- .../paddle/v2/fluid/tests/test_parallel_op.py | 108 +++++++++++------- 1 file changed, 68 insertions(+), 40 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index 59ed041e7fa..3736d5ea5a5 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -1,45 +1,73 @@ import unittest - -import paddle.v2.fluid.layers as layers import paddle.v2.fluid as fluid -from paddle.v2.fluid.framework import Program -from paddle.v2.fluid.executor import Executor -from paddle.v2.fluid.backward import append_backward -import numpy as np -import paddle.v2.fluid.core as core - - -class ParallelOpTest(unittest.TestCase): - def setUp(self): - x = layers.data( - shape=[-1, 30, 40], - dtype='float32', - name='x', - append_batch_size=False, - stop_gradient=False) - - places = layers.get_places(device_count=4) - pd = layers.ParallelDo(places=places) - - with pd.do(): - data = pd.read_input(x) - hidden = layers.fc(input=data, size=7) - pd.write_output(hidden) - data = pd() - loss = layers.mean(x=data) - sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.001) - sgd_optimizer.minimize(loss) - - exe = fluid.Executor(fluid.CPUPlace()) - exe.run(fluid.default_startup_program()) - exe.run(fluid.default_main_program(), - feed={ - x.name: np.random.uniform(0.1, 0.6, - (20, 30, 40)).astype("float32") - }) - - def test_forward(self): - pass +import numpy + + +class BaseParallelForTest(unittest.TestCase): + def main(self, callback, feed, fetch): + cpu = fluid.CPUPlace() + result_cpu = self._main_impl_( + callback=callback, + feed=feed, + fetch=fetch, + place=cpu, + use_parallel=False) + print result_cpu + + def _main_impl_(self, callback, feed, fetch, place, use_parallel=False): + main = fluid.Program() + startup = fluid.Program() + # Fix seed + main.random_seed = 10 + startup.random_seed = 10 + + with fluid.program_guard(main, startup): + generator = callback() + # Automatically insert parallel do if use_parallel = True + if use_parallel: + places = fluid.layers.get_places() + pd = fluid.layers.ParallelDo(places) + data = next(generator) + + if isinstance(data, fluid.Variable): + data = [data] + with pd.do(): + ins = map(pd.read_input, data) + if len(ins) == 1: + ins = ins[0] + generator.send(ins) # patch input + loss = next(generator) + pd.write_output(loss) + + loss = pd() + else: + data = next(generator) + generator.send(data) + loss = next(generator) + + avg_loss = fluid.layers.mean(x=loss) + fluid.backward.append_backward(loss=avg_loss) + + exe = fluid.Executor(place) + exe.run(startup) + return exe.run(main, feed=feed, fetch_list=fetch) + + +class ParallelOpTest(BaseParallelForTest): + def test_simple_fc(self): + def __network__(): + x = fluid.layers.data(shape=[784], dtype='float32', name='img') + x = yield x + hidden = fluid.layers.fc(input=x, size=200, param_attr='fc1.w') + loss = fluid.layers.mean(x=hidden) + yield loss + + self.main( + callback=__network__, + feed={ + 'img': numpy.random.random(size=(128, 784)).astype('float32') + }, + fetch='fc1.w@GRAD') if __name__ == '__main__': -- GitLab From 2f56995f7c97e0da8bc63406a1fc42f43815135f Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Wed, 10 Jan 2018 16:15:54 +0800 Subject: [PATCH 0182/2305] Fix InitGLOG glog will not hold ARGV[0] inside. --- paddle/framework/init.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/paddle/framework/init.cc b/paddle/framework/init.cc index e12bac1d78e..4ef82a541ef 100644 --- a/paddle/framework/init.cc +++ b/paddle/framework/init.cc @@ -11,6 +11,7 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +#include // for strdup #include #include @@ -60,7 +61,9 @@ void InitDevices() { } void InitGLOG(const std::string &prog_name) { - google::InitGoogleLogging(prog_name.c_str()); + // glog will not hold the ARGV[0] inside. + // Use strdup to alloc a new string. + google::InitGoogleLogging(strdup(prog_name.c_str())); google::InstallFailureSignalHandler(); } -- GitLab From 2412f2f4124250ef0a0b8eb66efbfa08900c5d74 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Wed, 10 Jan 2018 16:37:42 +0800 Subject: [PATCH 0183/2305] Polish Unittest for ParallelFor --- .../paddle/v2/fluid/tests/test_parallel_op.py | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index 3736d5ea5a5..049ae0fe28d 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -12,9 +12,34 @@ class BaseParallelForTest(unittest.TestCase): fetch=fetch, place=cpu, use_parallel=False) - print result_cpu + result_cpu_parallel = self._main_impl_( + callback=callback, + feed=feed, + fetch=fetch, + place=cpu, + use_parallel=True) + if fluid.core.is_compile_gpu(): + gpu = fluid.CUDAPlace(0) + result_gpu = self._main_impl_( + callback=callback, + feed=feed, + fetch=fetch, + place=gpu, + use_parallel=False) + result_gpu_parallel = self._main_impl_( + callback=callback, + feed=feed, + fetch=fetch, + place=gpu, + use_parallel=True) + self._assert_same_(fetch, result_cpu, result_cpu_parallel, + result_gpu, result_gpu_parallel) + else: + self._assert_same_(fetch, result_cpu, result_cpu_parallel) def _main_impl_(self, callback, feed, fetch, place, use_parallel=False): + if isinstance(fetch, basestring): + fetch = [fetch] main = fluid.Program() startup = fluid.Program() # Fix seed @@ -31,20 +56,19 @@ class BaseParallelForTest(unittest.TestCase): if isinstance(data, fluid.Variable): data = [data] + with pd.do(): ins = map(pd.read_input, data) if len(ins) == 1: ins = ins[0] - generator.send(ins) # patch input - loss = next(generator) + loss = generator.send(ins) # patch input pd.write_output(loss) loss = pd() else: data = next(generator) - generator.send(data) - loss = next(generator) - + loss = generator.send(data) + self.assertIsNotNone(loss) avg_loss = fluid.layers.mean(x=loss) fluid.backward.append_backward(loss=avg_loss) @@ -52,11 +76,25 @@ class BaseParallelForTest(unittest.TestCase): exe.run(startup) return exe.run(main, feed=feed, fetch_list=fetch) + def _assert_same_(self, fetch, *args): + def _impl_(a, b, fetch_id, item_id): + item_str = ['CPU', 'ParallelCPU', 'GPU', 'ParallelGPU'] + flag = numpy.allclose(a, b, rtol=0.1) + self.assertTrue(flag, "The {0} are different in {1}".format( + fetch[fetch_id], item_str[item_id])) + + for i, items in enumerate(zip(*args)): + self.assertGreater(len(items), 0) + for j in range(1, len(items)): + _impl_(items[0], items[j], fetch_id=i, item_id=j) + class ParallelOpTest(BaseParallelForTest): def test_simple_fc(self): def __network__(): x = fluid.layers.data(shape=[784], dtype='float32', name='img') + # FIXME: This is a bug of parallel.do + x.stop_gradient = False x = yield x hidden = fluid.layers.fc(input=x, size=200, param_attr='fc1.w') loss = fluid.layers.mean(x=hidden) -- GitLab From 929d22c62213e3fa1e05109e2f88788f7b1f7501 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Wed, 10 Jan 2018 16:39:07 +0800 Subject: [PATCH 0184/2305] auto set openblas env --- paddle/scripts/submit_local.sh.in | 3 +++ python/paddle/v2/__init__.py | 35 +++++++++++++++++++------------ python/setup.py.in | 7 ++++++- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/paddle/scripts/submit_local.sh.in b/paddle/scripts/submit_local.sh.in index 8a352b0078d..bb47ad614ed 100755 --- a/paddle/scripts/submit_local.sh.in +++ b/paddle/scripts/submit_local.sh.in @@ -92,6 +92,9 @@ function threads_config() { if [ -z "$OPENBLAS_NUM_THREADS" ]; then export OPENBLAS_NUM_THREADS=$threads fi + if [ $threads -gt 1 ] && [ -z "$OPENBLAS_MAIN_FREE" ]; then + export OPENBLAS_MAIN_FREE=1 + fi fi } diff --git a/python/paddle/v2/__init__.py b/python/paddle/v2/__init__.py index 0de417df2cb..df710c33d0c 100644 --- a/python/paddle/v2/__init__.py +++ b/python/paddle/v2/__init__.py @@ -62,12 +62,15 @@ __all__ = [ cp.begin_parse() -def set_omp_mkl_env_vars(trainer_count): +def set_env_vars(trainer_count): '''Auto set CPU environment if have not set before. - export KMP_AFFINITY, OMP_DYNAMIC according to the Hyper Threading status. - export OMP_NUM_THREADS, MKL_NUM_THREADS according to trainer_count. + For MKL: + export KMP_AFFINITY, OMP_DYNAMIC according to the Hyper Threading status. + export OMP_NUM_THREADS, MKL_NUM_THREADS according to trainer_count. + For OpenBLAS: + export OPENBLAS_NUM_THREADS, OPENBLAS_MAIN_FREE according to trainer_count. ''' - import platform + import platform, paddle if not platform.system() in ['Linux', 'Darwin']: return @@ -103,16 +106,22 @@ def set_omp_mkl_env_vars(trainer_count): num_cores = num_physical_cores() num_processors = num_logical_processors() - if num_processors > num_cores: # Hyper Threading is enabled - set_env("OMP_DYNAMIC", "true") - set_env("KMP_AFFINITY", "granularity=fine,compact,1,0") - else: - set_env("OMP_DYNAMIC", "false") - set_env("KMP_AFFINITY", "granularity=fine,compact,0,0") + if paddle.version.mkl() == 'ON': + if num_processors > num_cores: # Hyper Threading is enabled + set_env("OMP_DYNAMIC", "true") + set_env("KMP_AFFINITY", "granularity=fine,compact,1,0") + else: + set_env("OMP_DYNAMIC", "false") + set_env("KMP_AFFINITY", "granularity=fine,compact,0,0") threads = num_processors / trainer_count threads = '1' if threads < 1 else str(threads) - set_env("OMP_NUM_THREADS", threads) - set_env("MKL_NUM_THREADS", threads) + if paddle.version.mkl() == 'ON': + set_env("OMP_NUM_THREADS", threads) + set_env("MKL_NUM_THREADS", threads) + else: + set_env("OPENBLAS_NUM_THREADS", threads) + if threads > 1: + set_env("OPENBLAS_MAIN_FREE", '1') def init(**kwargs): @@ -129,7 +138,7 @@ def init(**kwargs): for key in args_dict.keys(): args.append('--%s=%s' % (key, str(args_dict[key]))) - set_omp_mkl_env_vars(kwargs.get('trainer_count', 1)) + set_env_vars(kwargs.get('trainer_count', 1)) if 'use_gpu' in kwargs: cp.g_command_config_args['use_gpu'] = kwargs['use_gpu'] diff --git a/python/setup.py.in b/python/setup.py.in index 66ccfe80876..65ec58ecf98 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -31,6 +31,7 @@ patch = '%(patch)d' rc = '%(rc)d' istaged = %(istaged)s commit = '%(commit)s' +with_mkl = '%(with_mkl)s' def show(): if istaged: @@ -41,6 +42,9 @@ def show(): print 'rc:', rc else: print 'commit:', commit + +def mkl(): + return with_mkl ''' commit = git_commit() with open(filename, 'w') as f: @@ -51,7 +55,8 @@ def show(): 'rc': RC, 'version': '${PADDLE_VERSION}', 'commit': commit, - 'istaged': ISTAGED}) + 'istaged': ISTAGED, + 'with_mkl': '@WITH_MKL@'}) write_version_py(filename='@PADDLE_SOURCE_DIR@/python/paddle/version.py') -- GitLab From f594ca436939b1ef0133727eadf0d5470ff74f67 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 10 Jan 2018 09:17:03 +0000 Subject: [PATCH 0185/2305] Reuse the usable variable in edit_distance_op --- paddle/operators/edit_distance_op.cc | 4 ++-- paddle/operators/edit_distance_op.cu | 8 ++++---- paddle/operators/edit_distance_op.h | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/paddle/operators/edit_distance_op.cc b/paddle/operators/edit_distance_op.cc index 7b92148f0e1..441ae2aa003 100644 --- a/paddle/operators/edit_distance_op.cc +++ b/paddle/operators/edit_distance_op.cc @@ -49,10 +49,10 @@ class EditDistanceOpMaker : public framework::OpProtoAndCheckerMaker { EditDistanceOpMaker(OpProto *proto, OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("Hyps", - "(2-D LoDTensor, 2nd dim. equal to 1) " + "(2-D LoDTensor, 2nd dim. equal to 1) " "The indices for hypothesis strings."); AddInput("Refs", - "(2-D LoDTensor, 2nd dim. equal to 1) " + "(2-D LoDTensor, 2nd dim. equal to 1) " "The indices for reference strings."); AddAttr("normalized", "(bool, default false) Indicated whether to normalize " diff --git a/paddle/operators/edit_distance_op.cu b/paddle/operators/edit_distance_op.cu index b548345986c..cf5ebc5c38f 100644 --- a/paddle/operators/edit_distance_op.cu +++ b/paddle/operators/edit_distance_op.cu @@ -93,21 +93,21 @@ class EditDistanceGPUKernel : public framework::OpKernel { out_t->mutable_data(ctx.GetPlace()); auto out = out_t->data(); - std::vector distance(num_strs, 0.0); + T distance = 0.0; for (size_t num = 0; num < num_strs; num++) { auto m = static_cast(hyp_lod[num + 1] - hyp_lod[num]); auto n = static_cast(ref_lod[num + 1] - ref_lod[num]); if (m == 0 || n == 0) { - distance[num] = std::max(m, n); + distance = std::max(m, n); if (normalized) { PADDLE_ENFORCE(n > 0, "The reference string (#%d) cannot be empty " "when Attr(normalized) is enabled.", n); - distance[num] = distance[num] / n; + distance = distance / n; } memory::Copy(boost::get(ctx.GetPlace()), out + num, - platform::CPUPlace(), &distance[num], sizeof(T), stream); + platform::CPUPlace(), &distance, sizeof(T), stream); } else { framework::Tensor dist_t; dist_t.Resize({m + 1, n + 1}); diff --git a/paddle/operators/edit_distance_op.h b/paddle/operators/edit_distance_op.h index 6284f230e5b..537e70281a5 100644 --- a/paddle/operators/edit_distance_op.h +++ b/paddle/operators/edit_distance_op.h @@ -46,15 +46,15 @@ class EditDistanceKernel : public framework::OpKernel { out_t->mutable_data(ctx.GetPlace()); auto out = out_t->data(); - std::vector distance(num_strs, 0.0); + T distance = 0.0; for (size_t num = 0; num < num_strs; ++num) { auto m = static_cast(hyp_lod[num + 1] - hyp_lod[num]); auto n = static_cast(ref_lod[num + 1] - ref_lod[num]); if (m == 0) { - distance[num] = n; + distance = n; } else if (n == 0) { - distance[num] = m; + distance = m; } else { framework::Tensor dist_t; dist_t.Resize({m + 1, n + 1}); @@ -77,7 +77,7 @@ class EditDistanceKernel : public framework::OpKernel { dist[i * (n + 1) + j] = std::min(dels, std::min(ins, subs)); } } - distance[num] = dist[m * (n + 1) + n]; + distance = dist[m * (n + 1) + n]; } if (normalized) { @@ -85,9 +85,9 @@ class EditDistanceKernel : public framework::OpKernel { "The reference string (#%d) cannot be empty " "when Attr(normalized) is enabled.", n); - distance[num] = distance[num] / n; + distance = distance / n; } - out[num] = distance[num]; + out[num] = distance; } } }; -- GitLab From a1935b23c48e3b7e46f70d568442b6bf5340b999 Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 10 Jan 2018 09:26:53 +0000 Subject: [PATCH 0186/2305] Remove unnecessary prefix in test name of edit_distance_op --- python/paddle/v2/fluid/tests/test_edit_distance_op.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_edit_distance_op.py b/python/paddle/v2/fluid/tests/test_edit_distance_op.py index 24f2f0c5c24..38e87728b38 100644 --- a/python/paddle/v2/fluid/tests/test_edit_distance_op.py +++ b/python/paddle/v2/fluid/tests/test_edit_distance_op.py @@ -34,7 +34,7 @@ def Levenshtein(hyp, ref): return dist[m][n] -class TestCTCEditDistanceOp(OpTest): +class TestEditDistanceOp(OpTest): def setUp(self): self.op_type = "edit_distance" normalized = False @@ -62,7 +62,7 @@ class TestCTCEditDistanceOp(OpTest): self.check_output() -class TestCTCEditDistanceOpNormalized(OpTest): +class TestEditDistanceOpNormalized(OpTest): def setUp(self): self.op_type = "edit_distance" normalized = True -- GitLab From 1f5f79cbf58615d1b3310164b3143eb5cf45aa46 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Wed, 10 Jan 2018 18:03:27 +0800 Subject: [PATCH 0187/2305] fix error_clip errors and add unit test --- python/paddle/v2/fluid/clip.py | 15 +++--- python/paddle/v2/fluid/framework.py | 3 ++ python/paddle/v2/fluid/tests/test_clip.py | 59 +++++++++++++++++++++++ 3 files changed, 70 insertions(+), 7 deletions(-) create mode 100644 python/paddle/v2/fluid/tests/test_clip.py diff --git a/python/paddle/v2/fluid/clip.py b/python/paddle/v2/fluid/clip.py index b1fd1c2b65f..6ed97cbe647 100644 --- a/python/paddle/v2/fluid/clip.py +++ b/python/paddle/v2/fluid/clip.py @@ -3,7 +3,8 @@ import layers from . import core __all__ = [ - 'GradientClipByValue', 'append_gradient_clip_ops', 'error_clip_callback' + 'GradientClipByValue', 'ErrorClipByValue', 'append_gradient_clip_ops', + 'error_clip_callback' ] @@ -23,12 +24,12 @@ class ErrorClipByValue(BaseErrorClipAttr): self.min = min def append_clip_op(self, block, grad_name): - block.append_op( - type="clip", - inputs={"X": grad_name}, - outputs={"Out": grad_name}, - attrs={"min": self.min, - "max": self.max}) + clip_op_desc = block.desc.append_op() + clip_op_desc.set_type("clip") + clip_op_desc.set_input("X", [grad_name]) + clip_op_desc.set_output("Out", [grad_name]) + clip_op_desc.set_attr("min", self.min) + clip_op_desc.set_attr("max", self.max) def error_clip_callback(block, context): diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index 2fb388acfc0..47506401f53 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -271,6 +271,9 @@ class Variable(object): uid = core.unique_integer(prefix) # unique during whole process. return "_".join([prefix, str(uid)]) + def set_error_clip(self, error_clip): + self.error_clip = error_clip + def get_all_op_protos(): """ diff --git a/python/paddle/v2/fluid/tests/test_clip.py b/python/paddle/v2/fluid/tests/test_clip.py new file mode 100644 index 00000000000..7e72112a838 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_clip.py @@ -0,0 +1,59 @@ +from __future__ import print_function +import numpy as np +import paddle.v2 as paddle +import paddle.v2.fluid as fluid + +BATCH_SIZE = 128 +CLIP_MAX = 2e-6 +CLIP_MIN = -1e-6 + +prog = fluid.framework.Program() + +with fluid.program_guard(main_program=prog): + image = fluid.layers.data(name='x', shape=[784], dtype='float32') + + hidden1 = fluid.layers.fc(input=image, size=128, act='relu') + hidden2 = fluid.layers.fc(input=hidden1, size=64, act='relu') + predict = fluid.layers.fc(input=hidden2, size=10, act='softmax') + + label = fluid.layers.data(name='y', shape=[1], dtype='int64') + + cost = fluid.layers.cross_entropy(input=predict, label=label) + avg_cost = fluid.layers.mean(x=cost) + +prog_clip = prog.clone() +prog_clip.block(0).var(hidden1.name).set_error_clip( + fluid.clip.ErrorClipByValue( + max=CLIP_MAX, min=CLIP_MIN)) + +avg_cost_clip = prog_clip.block(0).var(avg_cost.name) +fluid.backward.append_backward(loss=avg_cost) +fluid.backward.append_backward( + loss=avg_cost_clip, callback=fluid.clip.error_clip_callback) + +hidden1_grad = prog.block(0).var(hidden1.name + "@GRAD") +hidden1_grad_clip = prog_clip.block(0).var(hidden1.name + "@GRAD") + +train_reader = paddle.batch( + paddle.reader.shuffle( + paddle.dataset.mnist.train(), buf_size=8192), + batch_size=BATCH_SIZE) + +place = fluid.CPUPlace() +exe = fluid.Executor(place) +feeder = fluid.DataFeeder(feed_list=[image, label], place=place) +exe.run(fluid.default_startup_program()) + +count = 0 +for data in train_reader(): + count += 1 + if count > 5: + break + out = exe.run(prog, feed=feeder.feed(data), fetch_list=[hidden1_grad]) + out_clip = exe.run(prog_clip, + feed=feeder.feed(data), + fetch_list=[hidden1_grad_clip]) + if not (out[0].clip(min=CLIP_MIN, max=CLIP_MAX) == out_clip[0]).all(): + exit(1) + +exit(0) -- GitLab From 377424bf21bd6f9911aa55f3ba8161363382a115 Mon Sep 17 00:00:00 2001 From: Qiao Longfei Date: Wed, 10 Jan 2018 19:12:06 +0800 Subject: [PATCH 0188/2305] reorganize data transform related code (#7391) * init data_type_transform * split data_layout_transform * tmp rm data_transform_test * change device_data_transform to data_device_transform * clean code * clean code --- paddle/framework/CMakeLists.txt | 10 +- ..._transform.cc => data_device_transform.cc} | 2 +- ...ta_transform.h => data_device_transform.h} | 0 ..._test.cu => data_device_transform_test.cu} | 0 paddle/framework/data_layout.h | 1 - paddle/framework/data_layout_transform.cc | 82 +++++++++ paddle/framework/data_layout_transform.h | 31 ++++ paddle/framework/data_transform.cc | 141 +-------------- paddle/framework/data_transform.h | 140 --------------- paddle/framework/data_transform_test.cc | 168 ------------------ paddle/framework/data_type_transform.cc | 99 +++++++++++ paddle/framework/data_type_transform.h | 31 ++++ paddle/framework/operator.cc | 1 - 13 files changed, 252 insertions(+), 454 deletions(-) rename paddle/framework/{device_data_transform.cc => data_device_transform.cc} (96%) rename paddle/framework/{device_data_transform.h => data_device_transform.h} (100%) rename paddle/framework/{device_data_transform_test.cu => data_device_transform_test.cu} (100%) create mode 100644 paddle/framework/data_layout_transform.cc create mode 100644 paddle/framework/data_layout_transform.h delete mode 100644 paddle/framework/data_transform_test.cc create mode 100644 paddle/framework/data_type_transform.cc create mode 100644 paddle/framework/data_type_transform.h diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt index af4079875a5..ed5f6310f4e 100644 --- a/paddle/framework/CMakeLists.txt +++ b/paddle/framework/CMakeLists.txt @@ -32,10 +32,12 @@ cc_test(threadpool_test SRCS threadpool_test.cc DEPS threadpool) cc_library(scope SRCS scope.cc DEPS glog threadpool) cc_test(scope_test SRCS scope_test.cc DEPS scope) -cc_library(device_data_transform SRCS device_data_transform.cc DEPS tensor) +cc_library(data_device_transform SRCS data_device_transform.cc DEPS tensor) +cc_library(data_type_transform SRCS data_type_transform.cc DEPS tensor) +cc_library(data_layout_transform SRCS data_layout_transform.cc DEPS tensor math_function) -cc_library(data_transform SRCS data_transform.cc DEPS math_function tensor framework_proto selected_rows device_data_transform) -cc_test(data_transform_test SRCS data_transform_test.cc DEPS data_transform device_context) +cc_library(data_transform SRCS data_transform.cc DEPS math_function tensor + framework_proto selected_rows data_device_transform data_type_transform data_layout_transform) cc_library(attribute SRCS attribute.cc DEPS framework_proto) cc_test(program_desc_test SRCS program_desc_test.cc DEPS proto_desc @@ -80,5 +82,5 @@ cc_test(init_test SRCS init_test.cc DEPS init) cc_test(op_kernel_type_test SRCS op_kernel_type_test.cc DEPS place device_context framework_proto) cc_test(cow_ptr_tests SRCS details/cow_ptr_test.cc) -nv_test(device_data_transform_test SRCS device_data_transform_test.cu +nv_test(data_device_transform_test SRCS data_device_transform_test.cu DEPS operator op_registry init math_function) diff --git a/paddle/framework/device_data_transform.cc b/paddle/framework/data_device_transform.cc similarity index 96% rename from paddle/framework/device_data_transform.cc rename to paddle/framework/data_device_transform.cc index cd5104cc6f2..b3fd48ae12c 100644 --- a/paddle/framework/device_data_transform.cc +++ b/paddle/framework/data_device_transform.cc @@ -11,7 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include "paddle/framework/device_data_transform.h" +#include "paddle/framework/data_device_transform.h" namespace paddle { namespace framework { diff --git a/paddle/framework/device_data_transform.h b/paddle/framework/data_device_transform.h similarity index 100% rename from paddle/framework/device_data_transform.h rename to paddle/framework/data_device_transform.h diff --git a/paddle/framework/device_data_transform_test.cu b/paddle/framework/data_device_transform_test.cu similarity index 100% rename from paddle/framework/device_data_transform_test.cu rename to paddle/framework/data_device_transform_test.cu diff --git a/paddle/framework/data_layout.h b/paddle/framework/data_layout.h index 4a8669c3a41..3ab976ecac4 100644 --- a/paddle/framework/data_layout.h +++ b/paddle/framework/data_layout.h @@ -13,7 +13,6 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once -#include "paddle/platform/enforce.h" #include #include "paddle/platform/enforce.h" diff --git a/paddle/framework/data_layout_transform.cc b/paddle/framework/data_layout_transform.cc new file mode 100644 index 00000000000..96794cae97d --- /dev/null +++ b/paddle/framework/data_layout_transform.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/framework/data_layout_transform.h" + +#include "paddle/framework/tensor.h" +#include "paddle/operators/math/math_function.h" + +namespace paddle { +namespace framework { + +struct CastDataLayout { + CastDataLayout(const platform::DeviceContext* ctx, + const std::vector& axis, const framework::Tensor& in, + framework::Tensor* out) + : in_(in), out_(out), ctx_(ctx), axis_(axis) {} + const framework::Tensor in_; + framework::Tensor* out_; + const platform::DeviceContext* ctx_; + const std::vector axis_; + + template + void operator()() { + auto place = ctx_->GetPlace(); + + if (platform::is_cpu_place(place)) { + operators::math::Transpose trans4; + auto* context = static_cast(ctx_); + trans4(*context, in_, out_, axis_); + } else { + PADDLE_THROW("Unsupport CPU <-> GPU!"); + } + } +}; + +void TransDataLayout(const std::vector& axis, + const platform::DeviceContext* ctx, + const KernelTypePair& kernel_pair, const Variable& in, + Variable* out) { + PADDLE_ENFORCE(in.IsType(), "Only support Tensor transform!."); + PADDLE_ENFORCE( + platform::places_are_same_class(kernel_pair.first.place_, + kernel_pair.second.place_), + "TransDataLayout only support DataLayout transform on same place!"); + PADDLE_ENFORCE(kernel_pair.first.data_type_ == kernel_pair.second.data_type_, + "TransDataLayout only support Datatype are same!"); + + auto src = in.Get(); + auto* dst = out->GetMutable(); + PADDLE_ENFORCE(arity(src.dims()) == 4, "Input Arity Only Suppport 4!"); + + auto src_dim = src.dims(); + std::vector dst_dim; + + dst_dim.resize(axis.size()); + for (size_t i = 0; i < axis.size(); i++) { + dst_dim[i] = src_dim[axis[i]]; + } + + dst->Resize(make_ddim(dst_dim)); + auto place = kernel_pair.second.place_; + dst->mutable_data(place, src.type()); + + auto src_type = kernel_pair.first.data_type_; + framework::VisitDataType(src_type, CastDataLayout(ctx, axis, src, dst)); + + dst->set_layout(kernel_pair.second.data_layout_); +} + +} // namespace framework +} // namespace paddle diff --git a/paddle/framework/data_layout_transform.h b/paddle/framework/data_layout_transform.h new file mode 100644 index 00000000000..befae1f6361 --- /dev/null +++ b/paddle/framework/data_layout_transform.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/framework/op_kernel_type.h" +#include "paddle/framework/variable.h" + +namespace paddle { +namespace framework { + +using KernelTypePair = std::pair; + +void TransDataLayout(const std::vector& axis, + const platform::DeviceContext* ctx, + const KernelTypePair& kernel_pair, const Variable& in, + Variable* out); + +} // namespace framework +} // namespace paddle diff --git a/paddle/framework/data_transform.cc b/paddle/framework/data_transform.cc index fed958db158..e56edb95396 100644 --- a/paddle/framework/data_transform.cc +++ b/paddle/framework/data_transform.cc @@ -11,22 +11,14 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include #include "paddle/framework/data_transform.h" -#include "paddle/framework/device_data_transform.h" -#include "paddle/framework/lod_tensor.h" -#include "paddle/framework/selected_rows.h" -#include "paddle/platform/device_context.h" + +#include "paddle/framework/data_device_transform.h" namespace paddle { namespace framework { -DataTransformFnMap& DataTransformFnMap::Instance() { - static DataTransformFnMap data_transform_map; - return data_transform_map; -} - Tensor* DataTransform(const OpKernelType& expected_kernel_type, const OpKernelType& kernel_type_for_var, const Tensor& input_tensor) { @@ -58,134 +50,5 @@ void CopyVariableWithTensor(const Variable& in_var, const Tensor& tensor, } } -auto KernelFP32 = OpKernelType(proto::DataType::FP32, platform::CPUPlace(), - DataLayout::kNHWC, LibraryType::kPlain); - -auto KernelFP64 = OpKernelType(proto::DataType::FP64, platform::CPUPlace(), - DataLayout::kNHWC, LibraryType::kPlain); - -auto KernelNHWC = OpKernelType(proto::DataType::FP64, platform::CPUPlace(), - DataLayout::kNHWC, LibraryType::kPlain); - -auto KernelNCHW = OpKernelType(proto::DataType::FP64, platform::CPUPlace(), - DataLayout::kNCHW, LibraryType::kPlain); - -// TODO(dzhwinter): Only for testing multiple op kernel. -// Dummy transform function for library_type -// should be removed. -auto KernelPlain = OpKernelType(proto::DataType::FP32, platform::CUDAPlace(0), - DataLayout::kAnyLayout, LibraryType::kPlain); - -auto KernelCUDNN = OpKernelType(proto::DataType::FP32, platform::CUDAPlace(0), - DataLayout::kAnyLayout, LibraryType::kCUDNN); - -void DummyTrans(const platform::DeviceContext* ctx, - const KernelTypePair& kernel_pair, const Variable& in, - Variable* out) { - PADDLE_ENFORCE(in.IsType(), "Only Support Tensor transform!."); - PADDLE_ENFORCE( - platform::places_are_same_class(kernel_pair.first.place_, - kernel_pair.second.place_), - "TransDataType Only Support DataType transform on same place!"); - auto src = in.Get(); - auto* dst = out->GetMutable(); - *dst = src; -} - -void TransDataType(const platform::DeviceContext* ctx, - const KernelTypePair& kernel_pair, const Variable& in, - Variable* out) { - PADDLE_ENFORCE(in.IsType(), "Only Support Tensor transform!."); - PADDLE_ENFORCE( - platform::places_are_same_class(kernel_pair.first.place_, - kernel_pair.second.place_), - "TransDataType Only Support DataType transform on same place!"); - - auto src = in.Get(); - auto* dst = out->GetMutable(); - - auto dims = src.dims(); - dst->Resize(dims); - auto dst_type = kernel_pair.second.data_type_; - auto src_type = kernel_pair.first.data_type_; - - switch (src_type) { - case proto::DataType::FP32: - framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); - break; - case proto::DataType::FP64: - framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); - break; - case proto::DataType::INT32: - framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); - break; - case proto::DataType::INT64: - framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); - break; - case proto::DataType::BOOL: - framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); - break; - default: - PADDLE_THROW("Not support type %d", src_type); - } -} - -void TransDataLayout(const std::vector& axis, - const platform::DeviceContext* ctx, - const KernelTypePair& kernel_pair, const Variable& in, - Variable* out) { - PADDLE_ENFORCE(in.IsType(), "Only support Tensor transform!."); - PADDLE_ENFORCE( - platform::places_are_same_class(kernel_pair.first.place_, - kernel_pair.second.place_), - "TransDataLayout only support DataLayout transform on same place!"); - PADDLE_ENFORCE(kernel_pair.first.data_type_ == kernel_pair.second.data_type_, - "TransDataLayout only support Datatype are same!"); - - auto src = in.Get(); - auto* dst = out->GetMutable(); - PADDLE_ENFORCE(arity(src.dims()) == 4, "Input Arity Only Suppport 4!"); - - auto src_dim = src.dims(); - std::vector dst_dim; - - dst_dim.resize(axis.size()); - for (size_t i = 0; i < axis.size(); i++) { - dst_dim[i] = src_dim[axis[i]]; - } - - dst->Resize(make_ddim(dst_dim)); - auto place = kernel_pair.second.place_; - dst->mutable_data(place, src.type()); - - auto src_type = kernel_pair.first.data_type_; - framework::VisitDataType(src_type, CastDataLayout(ctx, axis, src, dst)); - - dst->set_layout(kernel_pair.second.data_layout_); -} - } // namespace framework } // namespace paddle - -namespace f = paddle::framework; - -namespace { -std::vector NHWC2NCHW = {0, 3, 1, 2}; -std::vector NCHW2NHWC = {0, 2, 3, 1}; -} - -REGISTER_DATA_TRANSFORM_FN(f::KernelFP32, f::KernelFP64, f::TransDataType); -REGISTER_DATA_TRANSFORM_FN(f::KernelPlain, f::KernelCUDNN, f::DummyTrans); -REGISTER_DATA_TRANSFORM_FN(f::KernelCUDNN, f::KernelPlain, f::DummyTrans); -REGISTER_DATA_TRANSFORM_FN(f::KernelNHWC, f::KernelNCHW, - std::bind(f::TransDataLayout, NHWC2NCHW, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3, - std::placeholders::_4)); -REGISTER_DATA_TRANSFORM_FN(f::KernelNCHW, f::KernelNHWC, - std::bind(f::TransDataLayout, NCHW2NHWC, - std::placeholders::_1, - std::placeholders::_2, - std::placeholders::_3, - std::placeholders::_4)); diff --git a/paddle/framework/data_transform.h b/paddle/framework/data_transform.h index e4e5c30a96a..ee95c7e8564 100644 --- a/paddle/framework/data_transform.h +++ b/paddle/framework/data_transform.h @@ -30,26 +30,6 @@ limitations under the License. */ namespace paddle { namespace framework { -using KernelTypePair = std::pair; - -using DataTransformFn = - std::function; - -struct KernelTypePairHash { - static void HashCombine(const OpKernelType& t, std::size_t* seed) { - OpKernelType::Hash kernel_type_hasher; - (*seed) ^= kernel_type_hasher(t) + 0x9e3779b9 + (*seed << 6) + (*seed >> 2); - } - - size_t operator()(const KernelTypePair& kernel_pair) const { - std::size_t seed = 0; - HashCombine(kernel_pair.first, &seed); - HashCombine(kernel_pair.second, &seed); - return seed; - } -}; - Tensor* DataTransform(const OpKernelType& expected_kernel_type, const OpKernelType& kernel_type_for_var, const Tensor& input_tensor); @@ -57,125 +37,5 @@ Tensor* DataTransform(const OpKernelType& expected_kernel_type, void CopyVariableWithTensor(const Variable& in_var, const Tensor& tensor, Variable& out_var); -template -struct CastDataTypeFunctor { - HOSTDEVICE inline OutType operator()(InType in) const { - return static_cast(in); - } -}; - -template -struct CastDataType { - CastDataType(const framework::Tensor& in, framework::Tensor* out, - const platform::DeviceContext* ctx) - : in_(in), out_(out), ctx_(ctx) {} - const framework::Tensor in_; - framework::Tensor* out_; - const platform::DeviceContext* ctx_; - - template - void operator()() { - auto place = ctx_->GetPlace(); - - auto* in_begin = in_.data(); - auto numel = in_.numel(); - auto* in_end = in_begin + numel; - auto* out_begin = out_->mutable_data(place); - - if (platform::is_cpu_place(place)) { - platform::Transform trans; - auto* context = static_cast(ctx_); - trans(*context, in_begin, in_end, out_begin, - CastDataTypeFunctor()); - } else { - // TODO(dzhwinter): enhance Copy CPU<->GPU with different data type? - PADDLE_THROW("Unsupport CPU <-> GPU!"); - } - } -}; - -struct CastDataLayout { - CastDataLayout(const platform::DeviceContext* ctx, - const std::vector& axis, const framework::Tensor& in, - framework::Tensor* out) - : in_(in), out_(out), ctx_(ctx), axis_(axis) {} - const framework::Tensor in_; - framework::Tensor* out_; - const platform::DeviceContext* ctx_; - const std::vector axis_; - - template - void operator()() { - auto place = ctx_->GetPlace(); - - if (platform::is_cpu_place(place)) { - operators::math::Transpose trans4; - auto* context = static_cast(ctx_); - trans4(*context, in_, out_, axis_); - } else { - PADDLE_THROW("Unsupport CPU <-> GPU!"); - } - } -}; - -using DataTransformMap = - std::unordered_map; - -class DataTransformFnMap { - public: - static DataTransformFnMap& Instance(); - - bool Has(const KernelTypePair& key_pair) const { - return map_.find(key_pair) != map_.end(); - } - - void Insert(const OpKernelType& left, const OpKernelType& right, - const DataTransformFn& data_tranform_fn) { - Insert(std::make_pair(left, right), data_tranform_fn); - } - - void Insert(const KernelTypePair& kernel_type_pair, - const DataTransformFn& data_tranform_fn) { - PADDLE_ENFORCE(!Has(kernel_type_pair), - "KernelTypePair %s has been registered", ""); - map_.insert({kernel_type_pair, data_tranform_fn}); - } - - const DataTransformFn& Get(const KernelTypePair& key_pair) const { - auto data_transformer = GetNullable(key_pair); - PADDLE_ENFORCE_NOT_NULL(data_transformer, - "DataTransformFn should not be NULL"); - return *data_transformer; - } - - const DataTransformFn* GetNullable(const KernelTypePair& key_pair) const { - auto it = map_.find(key_pair); - if (it == map_.end()) { - return nullptr; - } else { - return &(it->second); - } - } - - const DataTransformMap& Map() const { return map_; } - - private: - DataTransformFnMap() = default; - DataTransformMap map_; - DISABLE_COPY_AND_ASSIGN(DataTransformFnMap); -}; - -// generate unique name with __LINE__ -// refs https://stackoverflow.com/questions/1597007 -#define TOKENPASTE(x, y) x##y -#define TOKENPASTE2(x, y) TOKENPASTE(x, y) -#define REGISTER_DATA_TRANSFORM_FN(from, to, fn) \ - static int TOKENPASTE2(fn_, __LINE__)() { \ - ::paddle::framework::DataTransformFnMap::Instance().Insert(from, to, fn); \ - return 0; \ - } \ - static int TOKENPASTE2(var_, __LINE__) __attribute__((unused)) = \ - TOKENPASTE2(fn_, __LINE__)() - } // namespace framework } // namespace paddle diff --git a/paddle/framework/data_transform_test.cc b/paddle/framework/data_transform_test.cc deleted file mode 100644 index edd305fd17a..00000000000 --- a/paddle/framework/data_transform_test.cc +++ /dev/null @@ -1,168 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ -#include -#include - -#include - -#include "paddle/framework/data_transform.h" -#include "paddle/platform/device_context.h" - -namespace paddle { -namespace framework { -using namespace platform; - -/** - * @brief cross validation of different kernel type transform - * We use four bit map represent different combination. - * If the field has multiple possible value, only choose two of them. - * For DataType, only test the FP32(float), FP64(double). - * e.g. 0000 -> FP32, CPUPlace, kNHWC, kPlain - * 1111 -> FP64, GPUPlace, kNCHW, kMKLDNN - */ - -std::array kDataType = { - {proto::DataType::FP32, proto::DataType::FP64}}; - -std::array kPlace = {{CPUPlace(), CUDAPlace(0)}}; - -std::array kDataLayout = {{ - DataLayout::kNHWC, DataLayout::kNCHW, -}}; - -std::array kLibraryType = {{ - LibraryType::kPlain, LibraryType::kMKLDNN, -}}; - -OpKernelType GenFromBit(const std::vector bits) { - return OpKernelType(kDataType[bits[0]], kPlace[bits[1]], kDataLayout[bits[2]], - kLibraryType[bits[3]]); -} - -int test_value = 0; - -auto kernel0 = GenFromBit({0, 0, 0, 0}); -auto kernel1 = GenFromBit({0, 0, 0, 1}); -auto kernel2 = GenFromBit({0, 0, 1, 0}); -auto kernel3 = GenFromBit({0, 0, 1, 1}); - -void TransDataType_t(const platform::DeviceContext* ctx, - const KernelTypePair& p, const Variable& in, - Variable* out) { - test_value++; -} - -void TransDataLayout_t(const platform::DeviceContext* ctx, - const KernelTypePair& p, const Variable& in, - Variable* out) { - test_value--; -} - -void TransLibraryType_t(const platform::DeviceContext* ctx, - const KernelTypePair& p, const Variable& in, - Variable* out) { - test_value += 2; -} - -} // namespace framework -} // namespace paddle - -namespace frw = paddle::framework; - -REGISTER_DATA_TRANSFORM_FN(frw::kernel0, frw::kernel1, frw::TransDataType_t); -REGISTER_DATA_TRANSFORM_FN(frw::kernel1, frw::kernel2, frw::TransDataLayout_t); -REGISTER_DATA_TRANSFORM_FN(frw::kernel0, frw::kernel2, frw::TransLibraryType_t); - -TEST(DataTransform, Register) { - using namespace paddle::framework; - using namespace paddle::platform; - - auto& instance = DataTransformFnMap::Instance(); - paddle::framework::Variable in; - paddle::framework::Variable out; - - DeviceContext* ctx = new CPUDeviceContext(); - auto pair0 = std::make_pair(frw::kernel0, frw::kernel1); - instance.Get(pair0)(ctx, pair0, in, &out); - ASSERT_EQ(test_value, 1); - - auto pair1 = std::make_pair(frw::kernel1, frw::kernel2); - instance.Get(pair1)(ctx, pair1, in, &out); - ASSERT_EQ(test_value, 0); - - auto pair3 = std::make_pair(frw::kernel0, frw::kernel2); - instance.Get(pair3)(ctx, pair3, in, &out); - ASSERT_EQ(test_value, 2); -} - -TEST(DataTransform, DataLayout) { - using namespace paddle::framework; - using namespace paddle::platform; - - auto& instance = DataTransformFnMap::Instance(); - Variable in; - Variable out; - Tensor* src = in.GetMutable(); - src->mutable_data(make_ddim({2, 3, 1, 2}), CPUPlace()); - src->set_layout(DataLayout::kNHWC); - - DeviceContext* ctx = new CPUDeviceContext(); - - { - auto kernel1 = GenFromBit({1, 0, 0, 0}); - auto kernel2 = GenFromBit({1, 0, 1, 0}); - auto pair0 = std::make_pair(kernel1, kernel2); - instance.Get(pair0)(ctx, pair0, in, &out); - } - - Tensor dst = out.Get(); - - EXPECT_TRUE(dst.layout() == DataLayout::kNCHW); - EXPECT_TRUE(dst.dims() == make_ddim({2, 2, 3, 1})); - - { - auto kernel1 = GenFromBit({1, 0, 1, 0}); - auto kernel2 = GenFromBit({1, 0, 0, 0}); - auto pair0 = std::make_pair(kernel1, kernel2); - instance.Get(pair0)(ctx, pair0, out, &in); - } - - EXPECT_TRUE(src->layout() == DataLayout::kNHWC); - EXPECT_TRUE(src->dims() == make_ddim({2, 3, 1, 2})); -} - -TEST(DataTransform, DataType) { - using namespace paddle::framework; - using namespace paddle::platform; - - auto& instance = DataTransformFnMap::Instance(); - DeviceContext* ctx = new CPUDeviceContext(); - - Variable in; - Variable out; - Tensor* src = in.GetMutable(); - float* ptr = src->mutable_data(make_ddim({2, 3}), CPUPlace()); - for (int i = 0; i < 6; ++i) { - ptr[i] = i / 3; - } - - { - auto kernel1 = GenFromBit({0, 0, 0, 0}); - auto kernel2 = GenFromBit({1, 0, 0, 0}); - auto pair0 = std::make_pair(kernel1, kernel2); - instance.Get(pair0)(ctx, pair0, in, &out); - } - Tensor dst = out.Get(); - EXPECT_TRUE(dst.data() != nullptr); -} diff --git a/paddle/framework/data_type_transform.cc b/paddle/framework/data_type_transform.cc new file mode 100644 index 00000000000..63373232e91 --- /dev/null +++ b/paddle/framework/data_type_transform.cc @@ -0,0 +1,99 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/framework/data_type_transform.h" + +#include "paddle/framework/selected_rows.h" +#include "paddle/platform/transform.h" + +namespace paddle { +namespace framework { + +template +struct CastDataTypeFunctor { + HOSTDEVICE inline OutType operator()(InType in) const { + return static_cast(in); + } +}; + +template +struct CastDataType { + CastDataType(const framework::Tensor& in, framework::Tensor* out, + const platform::DeviceContext* ctx) + : in_(in), out_(out), ctx_(ctx) {} + const framework::Tensor in_; + framework::Tensor* out_; + const platform::DeviceContext* ctx_; + + template + void operator()() { + auto place = ctx_->GetPlace(); + + auto* in_begin = in_.data(); + auto numel = in_.numel(); + auto* in_end = in_begin + numel; + auto* out_begin = out_->mutable_data(place); + + if (platform::is_cpu_place(place)) { + platform::Transform trans; + auto* context = static_cast(ctx_); + trans(*context, in_begin, in_end, out_begin, + CastDataTypeFunctor()); + } else { + // TODO(dzhwinter): enhance Copy CPU<->GPU with different data type? + PADDLE_THROW("Unsupport CPU <-> GPU!"); + } + } +}; + +void TransDataType(const platform::DeviceContext* ctx, + const KernelTypePair& kernel_pair, const Variable& in, + Variable* out) { + PADDLE_ENFORCE(in.IsType(), "Only Support Tensor transform!."); + PADDLE_ENFORCE( + platform::places_are_same_class(kernel_pair.first.place_, + kernel_pair.second.place_), + "TransDataType Only Support DataType transform on same place!"); + + auto src = in.Get(); + auto* dst = out->GetMutable(); + + auto dims = src.dims(); + dst->Resize(dims); + auto dst_type = kernel_pair.second.data_type_; + auto src_type = kernel_pair.first.data_type_; + + switch (src_type) { + case proto::DataType::FP32: + framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); + break; + case proto::DataType::FP64: + framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); + break; + case proto::DataType::INT32: + framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); + break; + case proto::DataType::INT64: + framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); + break; + case proto::DataType::BOOL: + framework::VisitDataType(dst_type, CastDataType(src, dst, ctx)); + break; + default: + PADDLE_THROW("Not support type %d", src_type); + } +} + +} // namespace framework +} // namespace paddle diff --git a/paddle/framework/data_type_transform.h b/paddle/framework/data_type_transform.h new file mode 100644 index 00000000000..8ec90742256 --- /dev/null +++ b/paddle/framework/data_type_transform.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/framework/op_kernel_type.h" +#include "paddle/framework/variable.h" +#include "paddle/platform/device_context.h" + +namespace paddle { +namespace framework { + +using KernelTypePair = std::pair; + +void TransDataType(const platform::DeviceContext* ctx, + const KernelTypePair& kernel_pair, const Variable& in, + Variable* out); + +} // namespace framework +} // namespace paddle diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index 35ebe48ba68..ef2c55cc379 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -16,7 +16,6 @@ limitations under the License. */ #include #include "paddle/framework/data_transform.h" -#include "paddle/framework/device_data_transform.h" #include "paddle/framework/executor.h" #include "paddle/framework/operator.h" #include "paddle/framework/shape_inference.h" -- GitLab From fe0ef91a3f3db8a806a462a030392a57b208d4ad Mon Sep 17 00:00:00 2001 From: Yibing Liu Date: Wed, 10 Jan 2018 11:26:50 +0000 Subject: [PATCH 0189/2305] fix ci error in edit_distance_op --- paddle/operators/edit_distance_op.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/operators/edit_distance_op.cc b/paddle/operators/edit_distance_op.cc index 441ae2aa003..e383f07fa9b 100644 --- a/paddle/operators/edit_distance_op.cc +++ b/paddle/operators/edit_distance_op.cc @@ -37,7 +37,7 @@ class EditDistanceOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType(framework::proto::DataType::FP32, ctx.device_context()); -- GitLab From 52dad013ce914f27a7bc296dbd0435090f23d9b2 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Wed, 10 Jan 2018 20:23:02 +0800 Subject: [PATCH 0190/2305] Add static_input. --- python/paddle/v2/fluid/layers/control_flow.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index 9ad021fa992..f134e56cda6 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -1210,6 +1210,26 @@ class DynamicRNN(object): outputs={'Out': input_array}) return array_read(array=input_array, i=self.step_idx) + def static_input(self, x): + self._assert_in_rnn_block_("static_input") + if not isinstance(x, Variable): + raise TypeError( + "static_input() can only take a Variable as its input") + if self.lod_rank_table is None: + raise RuntimeError( + "static_input() must be called after step_input().") + parent_block = self._parent_block_() + x_reordered = parent_block.create_var( + name=unique_name("dynamic_rnn_static_input_reordered"), + type=core.VarDesc.VarType.LOD_TENSOR, + dtype=x.dtype) + parent_block.append_op( + type='reorder_lod_tensor_by_rank', + inputs={'X': [x], + 'RankTable': [self.lod_rank_table]}, + outputs={'Out': [x_reordered]}) + return shrink_memory(x_reordered, self.step_idx, self.lod_rank_table) + @contextlib.contextmanager def block(self): if self.status != DynamicRNN.BEFORE_RNN: -- GitLab From 6fa56b9d014d1c82c1fe41d7395bce8484c4ba2e Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Wed, 10 Jan 2018 20:40:54 +0800 Subject: [PATCH 0191/2305] left startup program bug --- .../paddle/v2/fluid/distribute_transpiler.py | 82 +++++++++++++------ 1 file changed, 59 insertions(+), 23 deletions(-) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index 76e8734f137..009f079e839 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -56,8 +56,6 @@ def split_dense_variable(var_list, (block_id) * block_size)) block = VarBlock(var.name, block_id, curr_block_size) blocks.append(str(block)) - print("$$ splited var: ", var.name, var.shape, split_count, len(blocks), - block_size) return blocks @@ -126,7 +124,7 @@ class DistributeTranspiler: # let send_op know which endpoint to send which var, eplist is of the same # order of send_inputs. eplist = split_method(send_inputs, pserver_endpoints) - # create mapping of endpoint -> var to create pserver side program + # create mapping of endpoint -> splited var to create pserver side program self.param_grad_ep_mapping = dict() for i, ep in enumerate(eplist): param = send_outputs[i] @@ -142,7 +140,6 @@ class DistributeTranspiler: outputs={"Out": send_outputs}, attrs={"endpoints": pserver_endpoints, "epmap": eplist}) - # step4 for varname, splited_var in param_var_mapping.iteritems(): if len(splited_var) <= 1: @@ -187,21 +184,6 @@ class DistributeTranspiler: var_mapping[varname].append(var) return var_mapping - def _clone_param(self, block, v): - assert isinstance(v, Parameter) - new_p = Parameter( - block=block, - shape=v.shape, - dtype=v.dtype, - type=v.type, - lod_level=v.lod_level, - stop_gradient=v.stop_gradient, - trainable=v.trainable, - optimize_attr=v.optimize_attr, - regularizer=v.regularizer, - name=v.name) - block.vars[new_p.name] = new_p - def _clone_var(self, block, var): assert isinstance(var, Variable) return block.create_var( @@ -210,7 +192,9 @@ class DistributeTranspiler: dtype=var.dtype, type=var.type, lod_level=var.lod_level, - persistable=var.persistable) + # HACK: let all param in pserver persistable so child + # program in recv can get them + persistable=True) def _append_split_op(self, program, gradblocks): var_mapping = self._create_vars_from_blocklist(program, gradblocks) @@ -318,9 +302,10 @@ class DistributeTranspiler: return tmpvar = program.global_block().create_var( name=param_block.name, - persistable=param_block.persistable, + persistable=True, dtype=param_block.dtype, shape=param_block.shape) + new_inputs[key] = tmpvar for key, var in opt_op.inputs.iteritems(): @@ -330,7 +315,6 @@ class DistributeTranspiler: param_shape = new_inputs["Param"].shape new_shape = self._get_optimizer_input_shape(opt_op.type, key, var.shape, param_shape) - print("var, new shape", key, var.name, new_shape) tmpvar = program.global_block().create_var( name=var.name, persistable=var.persistable, @@ -338,7 +322,8 @@ class DistributeTranspiler: shape=new_shape) new_inputs[key] = tmpvar - # FIXME: change outputs ParamOut + # change outputs ParamOut variable + opt_op.outputs["ParamOut"] = new_inputs["Param"] program.global_block().append_op( type=opt_op.type, inputs=new_inputs, @@ -380,6 +365,7 @@ class DistributeTranspiler: else: self._append_pserver_non_opt_ops(optimize_sub_program, opt_op) + print("####", optimize_sub_program) pserver_program.global_block().append_op( type="recv", inputs={"RX": self.param_grad_ep_mapping[endpoint]["grads"] @@ -400,3 +386,53 @@ class DistributeTranspiler: }) pserver_program.sync_with_cpp() return pserver_program + + def get_startup_program(self, endpoint): + """ + Get startup program for current parameter server. + Modify operator input variables if there are variables that + was splited to several blocks. + """ + s_prog = Program() + orig_s_prog = framework.default_startup_program() + params = self.param_grad_ep_mapping[endpoint]["params"] + + def _get_splited_name_and_shape(varname): + for idx, splited_param in enumerate(params): + pname = splited_param.name + if pname.startswith(varname) and varname != pname: + return pname, splited_param.shape + return "", [] + + # 1. create vars + created_var_map = dict() + for var in params: + print("%%%% append var", var.name, var.shape) + tmpvar = s_prog.global_block().create_var( + name=var.name, + persistable=True, + dtype=var.dtype, + shape=var.shape) + created_var_map[var.name] = tmpvar + + # 2. rename op outputs + for op in orig_s_prog.global_block().ops: + new_outputs = dict() + for key, var in op.outputs.iteritems(): + newname, _ = _get_splited_name_and_shape(var.name) + if newname: + new_outputs[key] = created_var_map[newname] + else: + new_outputs[key] = var + # do not append startup op if var is not on this pserver + var_on_pserver = False + for _, var in new_outputs.iteritems(): + if var.name in created_var_map: + var_on_pserver = True + if var_on_pserver: + s_prog.global_block().append_op( + type=op.type, + inputs=op.inputs, + outputs=new_outputs, + attrs=op.attrs) + return s_prog -- GitLab From a7e847b648fb0084990ee914e1365b017436ce4e Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 10 Jan 2018 16:31:05 +0800 Subject: [PATCH 0192/2305] fix ds2 issue --- paddle/gserver/layers/MKLDNNLayer.cpp | 2 ++ paddle/gserver/layers/MKLDNNLayer.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/paddle/gserver/layers/MKLDNNLayer.cpp b/paddle/gserver/layers/MKLDNNLayer.cpp index 6fbf3c7fdec..2d0fff608c3 100644 --- a/paddle/gserver/layers/MKLDNNLayer.cpp +++ b/paddle/gserver/layers/MKLDNNLayer.cpp @@ -132,6 +132,8 @@ void MKLDNNLayer::reshapeInput(int& batchsize, if (w != 0) { width = w; } + height = height != 0 ? height : 1; + width = width != 0 ? width : 1; } void MKLDNNLayer::reshapeOutput(size_t height, size_t width) { diff --git a/paddle/gserver/layers/MKLDNNLayer.h b/paddle/gserver/layers/MKLDNNLayer.h index e48b9b5a91f..3ba39f18b6a 100644 --- a/paddle/gserver/layers/MKLDNNLayer.h +++ b/paddle/gserver/layers/MKLDNNLayer.h @@ -98,6 +98,8 @@ protected: public: explicit MKLDNNLayer(const LayerConfig& config) : Layer(config), + ih_(0), + iw_(0), condition_(0), needResetBwd_(true), outputOnlyMKLDNN_(false), -- GitLab From 2b202f754bcb4f035eb22b316aea6f9d96c7386b Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Wed, 10 Jan 2018 21:40:46 +0800 Subject: [PATCH 0193/2305] Optimize maxPoolForward. --- paddle/math/Matrix.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/paddle/math/Matrix.cpp b/paddle/math/Matrix.cpp index 1ec4336cabb..cc86b12be08 100644 --- a/paddle/math/Matrix.cpp +++ b/paddle/math/Matrix.cpp @@ -2015,13 +2015,6 @@ void CpuMatrix::maxPoolForward(Matrix& inputMat, CHECK_EQ(channels * outLength, maskMatP->getWidth()); } - /* initialize the data_ */ - for (size_t i = 0; i < height_; i++) { - for (size_t j = 0; j < width_; j++) { - outData[i * outStride + j] = -(real)FLT_MAX; - } - } - /* pool max one by one */ for (size_t n = 0; n < num; ++n) { // frame by frame if (!isContiguous()) { @@ -2030,19 +2023,24 @@ void CpuMatrix::maxPoolForward(Matrix& inputMat, for (size_t c = 0; c < channels; ++c) { // channel by channel for (size_t ph = 0; ph < outputH; ++ph) { int hstart = ph * strideH - paddingH; - int hend = std::min(hstart + sizeY, imgSizeH); - hstart = std::max(hstart, 0); + int hend = hstart + sizeY; + hstart = hstart < 0 ? 0 : hstart; + hend = hend < (int)imgSizeH ? hend : (int)imgSizeH; for (size_t pw = 0; pw < outputW; ++pw) { int wstart = pw * strideW - paddingW; - int wend = std::min(wstart + sizeX, imgSizeW); - wstart = std::max(wstart, 0); + int wend = wstart + sizeX; + wstart = wstart < 0 ? 0 : wstart; + wend = wend < (int)imgSizeW ? wend : (int)imgSizeW; if (maskData == NULL) { + real tmp = -(real)FLT_MAX; for (int h = hstart; h < hend; ++h) { for (int w = wstart; w < wend; ++w) { - outData[ph * outputW + pw] = std::max( - outData[ph * outputW + pw], inputData[h * imgSizeW + w]); + tmp = tmp < inputData[h * imgSizeW + w] + ? inputData[h * imgSizeW + w] + : tmp; } } + outData[ph * outputW + pw] = tmp; } else { for (int h = hstart; h < hend; ++h) { for (int w = wstart; w < wend; ++w) { -- GitLab From 2827607fa891328e31f84dc328301754b3c6ba1c Mon Sep 17 00:00:00 2001 From: "yi.wu" Date: Wed, 10 Jan 2018 23:04:05 +0800 Subject: [PATCH 0194/2305] fix startup program shape --- python/paddle/v2/fluid/distribute_transpiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index 009f079e839..b064220ca29 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -365,7 +365,6 @@ class DistributeTranspiler: else: self._append_pserver_non_opt_ops(optimize_sub_program, opt_op) - print("####", optimize_sub_program) pserver_program.global_block().append_op( type="recv", inputs={"RX": self.param_grad_ep_mapping[endpoint]["grads"] @@ -407,7 +406,6 @@ class DistributeTranspiler: # 1. create vars created_var_map = dict() for var in params: - print("%%%% append var", var.name, var.shape) tmpvar = s_prog.global_block().create_var( name=var.name, persistable=True, @@ -430,6 +428,8 @@ class DistributeTranspiler: if var.name in created_var_map: var_on_pserver = True if var_on_pserver: + # gaussian_random use attr to determine tensor shape + op.attrs["shape"] = new_outputs["Out"].shape s_prog.global_block().append_op( type=op.type, inputs=op.inputs, -- GitLab From b1af5e435fb1ec971247f63ea6a234c4fb4e9505 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Thu, 11 Jan 2018 00:22:29 +0800 Subject: [PATCH 0195/2305] 1. Fix warpctc grad op 2. Add check grad test --- paddle/operators/CMakeLists.txt | 2 +- paddle/operators/math/CMakeLists.txt | 2 + paddle/operators/math/sequence_scale.cc | 46 ++++++++++++++ paddle/operators/math/sequence_scale.cu | 61 +++++++++++++++++++ paddle/operators/math/sequence_scale.h | 53 ++++++++++++++++ paddle/operators/warpctc_op.h | 29 ++++++++- .../paddle/v2/fluid/tests/test_warpctc_op.py | 12 ++-- 7 files changed, 196 insertions(+), 9 deletions(-) create mode 100644 paddle/operators/math/sequence_scale.cc create mode 100644 paddle/operators/math/sequence_scale.cu create mode 100644 paddle/operators/math/sequence_scale.h diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index 5889a50db09..2d9055a06a4 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -151,7 +151,7 @@ op_library(lstm_op DEPS sequence2batch lstm_compute) op_library(conv_transpose_op DEPS vol2col) op_library(gru_op DEPS sequence2batch gru_compute) op_library(recurrent_op DEPS executor) -op_library(warpctc_op DEPS dynload_warpctc sequence_padding math_function) +op_library(warpctc_op DEPS dynload_warpctc sequence_padding sequence_scale math_function) op_library(cos_sim_op DEPS cos_sim_functor) op_library(parallel_do_op DEPS executor) # FIXME(typhoonzero): save/load depends lodtensor serialization functions diff --git a/paddle/operators/math/CMakeLists.txt b/paddle/operators/math/CMakeLists.txt index fd59eef7d65..c607704efac 100644 --- a/paddle/operators/math/CMakeLists.txt +++ b/paddle/operators/math/CMakeLists.txt @@ -13,6 +13,7 @@ if(WITH_GPU) nv_library(context_project SRCS context_project.cc context_project.cu DEPS device_context math_function) nv_library(sequence2batch SRCS sequence2batch.cc sequence2batch.cu DEPS device_context tensor) nv_library(sequence_padding SRCS sequence_padding.cc sequence_padding.cu DEPS lod_tensor device_context) + nv_library(sequence_scale SRCS sequence_scale.cc sequence_scale.cu DEPS lod_tensor device_context) nv_library(lstm_compute SRCS lstm_compute.cc lstm_compute.cu DEPS device_context activation_functions) nv_library(maxouting SRCS maxouting.cc maxouting.cu DEPS device_context) nv_library(unpooling SRCS unpooling.cc unpooling.cu DEPS device_context) @@ -29,6 +30,7 @@ else() cc_library(context_project SRCS context_project.cc DEPS device_context math_function) cc_library(sequence2batch SRCS sequence2batch.cc DEPS device_context tensor) cc_library(sequence_padding SRCS sequence_padding.cc DEPS lod_tensor device_context) + cc_library(sequence_scale SRCS sequence_scale.cc DEPS lod_tensor device_context) cc_library(lstm_compute SRCS lstm_compute.cc DEPS device_context activation_functions) cc_library(maxouting SRCS maxouting.cc DEPS device_context) cc_library(unpooling SRCS unpooling.cc DEPS device_context) diff --git a/paddle/operators/math/sequence_scale.cc b/paddle/operators/math/sequence_scale.cc new file mode 100644 index 00000000000..0f66e43a1a6 --- /dev/null +++ b/paddle/operators/math/sequence_scale.cc @@ -0,0 +1,46 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/math/sequence_scale.h" + +namespace paddle { +namespace operators { +namespace math { + +template +class ScaleLoDTensorFunctor { + public: + void operator()(const platform::CPUDeviceContext& context, + framework::LoDTensor& seq, const T* scales, + const size_t num_seq) { + const size_t level = 0; + auto lod = seq.lod(); + size_t seq_width = seq.dims()[1]; + framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); + + T* seq_data = seq.mutable_data(context.GetPlace()); + for (size_t i = 0; i < num_seq; ++i) { + for (size_t j = lod[level][i] * seq_width; + j < lod[level][i + 1] * seq_width; ++j) { + seq_data[j] *= scales[i]; + } + } + } +}; + +template class ScaleLoDTensorFunctor; + +} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/math/sequence_scale.cu b/paddle/operators/math/sequence_scale.cu new file mode 100644 index 00000000000..23b0cce13ff --- /dev/null +++ b/paddle/operators/math/sequence_scale.cu @@ -0,0 +1,61 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/math/sequence_scale.h" + +namespace paddle { +namespace operators { +namespace math { + +template +__global__ void SequenceScaleKernel(T* seq, size_t* lod, const T* scales, + const size_t num_seq, + const size_t seq_width) { + size_t idx = blockIdx.x * blockDim.y + threadIdx.y; + + if (idx < lod[num_seq]) { + size_t i = 0; + for (i = 0; i < num_seq; ++i) { + if (idx < lod[i + 1] * seq_width) { + break; + } + } + seq[i] *= scales[i]; + } +} + +template +class ScaleLoDTensorFunctor { + public: + void operator()(const platform::CUDADeviceContext& context, + framework::LoDTensor& seq, const T* scales, + const size_t num_seq) { + auto lod = seq.lod(); + const size_t seq_width = seq.dims()[1]; + const size_t level = 0; + framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); + T* seq_data = seq.mutable_data(context.GetPlace()); + + int threads = 1024; + int grid = (seq.numel() * seq_width + threads - 1) / threads; + SequenceScaleKernel<<>>( + seq_data, abs_offset_lod[level].data(), scales, num_seq, seq_width); + } +}; + +template class ScaleLoDTensorFunctor; + +} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/math/sequence_scale.h b/paddle/operators/math/sequence_scale.h new file mode 100644 index 00000000000..a42fc6d0db2 --- /dev/null +++ b/paddle/operators/math/sequence_scale.h @@ -0,0 +1,53 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/framework/lod_tensor.h" +#include "paddle/platform/device_context.h" + +namespace paddle { +namespace operators { +namespace math { + +/* + * \brief Scale a sequence. + * + * All sequences will be padded to the same length and stored in a transposed + * shape. + * Example: + * seq (s0, s0, s0, s0; s1, s1; s2, s2, s2; s3) + * padding (s0, s1, s2, s3; s0, s1, s2, 0; s0, 0, s2, 0; s0, 0, 0, 0) + * + * \param context device context of this functor. + * \param seq LoDTensor which is stored in sequence format, the shape + * is [total_sequence_length, sequence_width] where + * total_sequence_length is the sum of all sequences' + * length. + * \param padding Tensor which is padded to the same length, the shape is + * [max_sequence_length, num_sequences, sequence_width]. + * \param norm_by_times whether dividing sequence's length. + * + * \note transposition is also done in this functor. + */ +template +class ScaleLoDTensorFunctor { + public: + void operator()(const DeviceContext& context, framework::LoDTensor& seq, + const T* scales, const size_t num_seq); +}; + +} // namespace math +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/warpctc_op.h b/paddle/operators/warpctc_op.h index 41899c7fe0c..c2bbceb6d15 100644 --- a/paddle/operators/warpctc_op.h +++ b/paddle/operators/warpctc_op.h @@ -14,9 +14,11 @@ limitations under the License. */ #pragma once +#include "paddle/framework/eigen.h" #include "paddle/framework/op_registry.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/sequence_padding.h" +#include "paddle/operators/math/sequence_scale.h" #include "paddle/platform/dynload/warpctc.h" namespace paddle { @@ -182,7 +184,6 @@ class WarpCTCKernel : public framework::OpKernel { Tensor warpctc_label; Copy(*label, platform::CPUPlace(), ctx.device_context(), &warpctc_label); const int* warpctc_label_data = warpctc_label.data(); - // warpctc stores loss in CPU memory Tensor warpctc_loss; T* warpctc_loss_data = @@ -206,11 +207,37 @@ class WarpCTCGradKernel : public framework::OpKernel { void Compute(const framework::ExecutionContext& ctx) const override { auto* warpctc_grad = ctx.Input("WarpCTCGrad"); auto* logits_grad = ctx.Output(framework::GradVarName("Logits")); + const Tensor* loss_grad = ctx.Input(framework::GradVarName("Loss")); + + // LOG(ERROR) << "loss_grad_dims: " << loss_grad_dims; + // for (int i=0; inumel();i++) { + // LOG(ERROR) << "loss_grad: " << loss_grad_data[i]; + //} + // T* logits_grad_data = + logits_grad->mutable_data(ctx.GetPlace()); bool norm_by_times = ctx.Attr("norm_by_times"); math::UnpaddingLoDTensorFunctor()( ctx.template device_context(), *logits_grad, *warpctc_grad, norm_by_times); + + const T* loss_grad_data = loss_grad->data(); + const size_t num_seq = loss_grad->dims()[0]; + math::ScaleLoDTensorFunctor()( + ctx.template device_context(), *logits_grad, + loss_grad_data, num_seq); + /* + int level = 0; + auto logits_grad_lod = framework::ToAbsOffset(logits_grad->lod()); + const size_t num_sequences = logits_grad_lod[level].size() - 1; + for (int seq_index = 0; seq_index < num_sequences; ++seq_index) { + for (int token_index = logits_grad_lod[level][seq_index]; + token_index < logits_grad_lod[level][seq_index + 1]; + ++token_index) { + logits_grad_data[token_index] *= loss_grad_data[seq_index]; + } + } + */ } }; diff --git a/python/paddle/v2/fluid/tests/test_warpctc_op.py b/python/paddle/v2/fluid/tests/test_warpctc_op.py index 59390d5303b..6496b55031e 100644 --- a/python/paddle/v2/fluid/tests/test_warpctc_op.py +++ b/python/paddle/v2/fluid/tests/test_warpctc_op.py @@ -185,16 +185,14 @@ class TestWarpCTCOp(OpTest): "Logits": (logits, logits_lod), "Label": (labels, labels_lod) } - self.outputs = {"Loss": loss} + self.outputs = {"Loss": loss, "WarpCTCGrad": gradient} self.attrs = {"blank": blank, "norm_by_times": norm_by_times} - def test_check_output(self): - self.check_output() +# def test_check_output(self): +# self.check_output() - -# def test_check_grad(self): -# self.outputs["WarpCTCGrad"] = None -# self.check_grad(["Logits"], "Loss", max_relative_error=0.01) + def test_check_grad(self): + self.check_grad(["Logits"], "Loss", max_relative_error=0.01) if __name__ == "__main__": unittest.main() -- GitLab From da3087ada1de62caea7ea2b2f819eb24ea5a6088 Mon Sep 17 00:00:00 2001 From: gongweibao Date: Thu, 11 Jan 2018 09:28:24 +0800 Subject: [PATCH 0196/2305] Async GRPC sendrecv (#7133) Async GRPC sendrecv --- paddle/operators/detail/CMakeLists.txt | 2 +- paddle/operators/detail/grpc_client.cc | 147 ++++++++++++ paddle/operators/detail/grpc_client.h | 147 ++++++++++++ paddle/operators/detail/grpc_server.cc | 237 ++++++++++++++++++++ paddle/operators/detail/grpc_server.h | 91 ++++++++ paddle/operators/detail/recv_impl.cc | 65 ------ paddle/operators/detail/send_impl.cc | 67 ------ paddle/operators/detail/send_recv.proto | 2 - paddle/operators/detail/send_recv_impl.h | 141 ------------ paddle/operators/detail/sendrecvop_utils.cc | 68 ++++++ paddle/operators/detail/sendrecvop_utils.h | 42 ++++ paddle/operators/recv_op.cc | 43 ++-- paddle/operators/send_op.cc | 56 ++--- paddle/operators/send_recv_op_test.cc | 2 +- 14 files changed, 775 insertions(+), 335 deletions(-) create mode 100644 paddle/operators/detail/grpc_client.cc create mode 100644 paddle/operators/detail/grpc_client.h create mode 100644 paddle/operators/detail/grpc_server.cc create mode 100644 paddle/operators/detail/grpc_server.h delete mode 100644 paddle/operators/detail/recv_impl.cc delete mode 100644 paddle/operators/detail/send_impl.cc delete mode 100644 paddle/operators/detail/send_recv_impl.h create mode 100644 paddle/operators/detail/sendrecvop_utils.cc create mode 100644 paddle/operators/detail/sendrecvop_utils.h diff --git a/paddle/operators/detail/CMakeLists.txt b/paddle/operators/detail/CMakeLists.txt index f6bdc63cc2c..571a75c9dcd 100644 --- a/paddle/operators/detail/CMakeLists.txt +++ b/paddle/operators/detail/CMakeLists.txt @@ -1 +1 @@ -grpc_library(sendrecvop_grpc SRCS recv_impl.cc send_impl.cc PROTO send_recv.proto DEPS lod_tensor selected_rows) +grpc_library(sendrecvop_grpc SRCS sendrecvop_utils.cc grpc_client.cc grpc_server.cc PROTO send_recv.proto DEPS lod_tensor selected_rows) diff --git a/paddle/operators/detail/grpc_client.cc b/paddle/operators/detail/grpc_client.cc new file mode 100644 index 00000000000..5a4db2d7e68 --- /dev/null +++ b/paddle/operators/detail/grpc_client.cc @@ -0,0 +1,147 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "grpc_client.h" +namespace paddle { +namespace operators { +namespace detail { + +bool RPCClient::AsyncSendVariable(const std::string& ep, + const platform::DeviceContext& ctx, + const framework::Scope& scope, + const std::string& var_name, + int64_t time_out) { + sendrecv::VariableMessage req; + auto* var = scope.FindVar(var_name); + SerializeToMessage(var_name, var, ctx, &req); + + // varhandle + VarHandle var_h; + var_h.ep = ep; + var_h.scope = &scope; + var_h.name = var_name; + var_h.ctx = &ctx; + + // stub context + auto ch = GetChannel(ep); + SendProcessor* s = new SendProcessor(ch); + s->Prepare(var_h, time_out); + s->response_call_back_ = NULL; + + auto rpc = s->stub_->AsyncSendVariable(s->context_.get(), req, &cq_); + rpc->Finish(&s->reply_, &s->status_, (void*)s); + + req_count_++; + + return true; +} + +void ProcGetResponse(const VarHandle& var_h, + const sendrecv::VariableMessage& ret_msg) { + auto* outvar = var_h.scope->FindVar(var_h.name); + + std::istringstream iss(ret_msg.serialized()); + DeserializeFromMessage(ret_msg, *var_h.ctx, outvar); +} + +bool RPCClient::AsyncGetVariable(const std::string& ep, + const platform::DeviceContext& ctx, + const framework::Scope& scope, + const std::string& var_name, + int64_t time_out) { + sendrecv::VariableMessage req; + req.set_varname(var_name); + + auto* var = scope.FindVar(var_name); + SerializeToMessage(var_name, var, ctx, &req); + + // varhandle + VarHandle var_h; + var_h.ep = ep; + var_h.scope = &scope; + var_h.name = var_name; + var_h.ctx = &ctx; + + // stub context + auto ch = GetChannel(ep); + GetProcessor* s = new GetProcessor(ch); + s->Prepare(var_h, time_out); + s->response_call_back_ = ProcGetResponse; + + auto rpc = s->stub_->AsyncGetVariable(s->context_.get(), req, &cq_); + rpc->Finish(&s->reply_, &s->status_, (void*)s); + + req_count_++; + + return true; +} + +bool RPCClient::wait() { + bool ok = true; + + while (true) { + if (req_count_ <= 0) { + break; + } + + if (!Proceed()) { + LOG(ERROR) << "Get meets CompletionQueue error"; + return false; + } + } + + return ok; +} + +bool RPCClient::Proceed() { + void* tag = NULL; + bool ok = false; + + // request counts. + if (!cq_.Next(&tag, &ok)) { + return false; + } + req_count_--; + + GPR_ASSERT(ok); + PADDLE_ENFORCE(tag); + + // TODO(gongwb): add more retries. + ClientBase* c = static_cast(tag); + if (!c->status_.ok()) { + delete c; + return true; + } + + c->Process(); + delete c; + return true; +} + +std::shared_ptr RPCClient::GetChannel(const std::string& ep) { + auto it = channels_.find(ep); + if (it != channels_.end()) { + return it->second; + } + + auto ch = std::shared_ptr( + grpc::CreateChannel(ep, grpc::InsecureChannelCredentials())); + + channels_[ep] = ch; + return ch; +} + +} // namespace detail +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/detail/grpc_client.h b/paddle/operators/detail/grpc_client.h new file mode 100644 index 00000000000..d27b5ced9ec --- /dev/null +++ b/paddle/operators/detail/grpc_client.h @@ -0,0 +1,147 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "paddle/framework/data_type.h" +#include "paddle/framework/lod_tensor.h" +#include "paddle/framework/scope.h" +#include "paddle/framework/selected_rows.h" +#include "paddle/operators/detail/sendrecvop_utils.h" +#include "paddle/operators/detail/simple_block_queue.h" + +namespace paddle { +namespace operators { +namespace detail { + +struct VarHandle { + std::string ep; + const platform::DeviceContext* ctx; + const framework::Scope* scope; + std::string name; + + std::string String() const { + std::ostringstream s; + s << "name:[" << name << "] ep:[" << ep << "]"; + return s.str(); + } +}; + +void ProcGetResponse(const VarHandle& var_h, + const sendrecv::VariableMessage& msg); + +class ClientBase { + public: + explicit ClientBase(std::shared_ptr ch) { + stub_ = sendrecv::SendRecvService::NewStub(ch); + context_ = NULL; + } + + virtual ~ClientBase() {} + + virtual void Prepare(const VarHandle& var_info, int64_t time_out) { + context_.reset(new grpc::ClientContext()); + var_h_ = var_info; + + std::chrono::system_clock::time_point deadline = + std::chrono::system_clock::now() + std::chrono::milliseconds(time_out); + + context_->set_deadline(deadline); + } + + virtual void Process() = 0; + + std::unique_ptr stub_; + std::unique_ptr context_; + grpc::Status status_; + VarHandle var_h_; +}; + +typedef std::function + RequestSendCallBack; + +class SendProcessor : public ClientBase { + public: + explicit SendProcessor(std::shared_ptr ch) : ClientBase(ch) {} + + virtual ~SendProcessor() {} + + virtual void Process() { + if (response_call_back_) { + response_call_back_(var_h_, reply_); + } + } + + sendrecv::VoidMessage reply_; + RequestSendCallBack response_call_back_ = NULL; +}; + +typedef std::function + RequestGetCallBack; + +class GetProcessor : public ClientBase { + public: + explicit GetProcessor(std::shared_ptr ch) : ClientBase(ch) {} + + virtual ~GetProcessor() {} + + virtual void Process() { + if (response_call_back_) { + response_call_back_(var_h_, reply_); + } + } + + sendrecv::VariableMessage reply_; + RequestGetCallBack response_call_back_ = ProcGetResponse; +}; + +class RPCClient { + public: + bool AsyncSendVariable(const std::string& ep, + const platform::DeviceContext& ctx, + const framework::Scope& scope, + const std::string& var_name, + int64_t time_out = 600 * 1000); + + bool AsyncGetVariable(const std::string& ep, + const platform::DeviceContext& ctx, + const framework::Scope& scope, + const std::string& var_name, + int64_t time_out = 600 * 1000); + bool wait(); + + private: + bool Proceed(); + std::shared_ptr GetChannel(const std::string& ep); + + private: + grpc::CompletionQueue cq_; + std::map> channels_; + int64_t req_count_ = 0; +}; + +} // namespace detail +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/detail/grpc_server.cc b/paddle/operators/detail/grpc_server.cc new file mode 100644 index 00000000000..e8d561a57ff --- /dev/null +++ b/paddle/operators/detail/grpc_server.cc @@ -0,0 +1,237 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/detail/grpc_server.h" + +using grpc::ServerAsyncResponseWriter; + +namespace paddle { +namespace operators { +namespace detail { + +enum CallStatus { PROCESS = 0, FINISH }; + +// reference: +// https://stackoverflow.com/questions/41732884/grpc-multiple-services-in-cpp-async-server +class RequestBase { + public: + explicit RequestBase(sendrecv::SendRecvService::AsyncService* service, + grpc::ServerCompletionQueue* cq) + : service_(service), cq_(cq), status_(PROCESS) {} + virtual ~RequestBase() {} + virtual void Process() { assert(false); } + + CallStatus Status() { return status_; } + void SetStatus(CallStatus status) { status_ = status; } + + protected: + grpc::ServerContext ctx_; + sendrecv::SendRecvService::AsyncService* service_; + grpc::ServerCompletionQueue* cq_; + CallStatus status_; +}; + +typedef std::pair MessageWithName; + +class RequestSend final : public RequestBase { + public: + explicit RequestSend(sendrecv::SendRecvService::AsyncService* service, + grpc::ServerCompletionQueue* cq, + SimpleBlockQueue* queue) + : RequestBase(service, cq), queue_(queue), responder_(&ctx_) { + service_->RequestSendVariable(&ctx_, &request_, &responder_, cq_, cq_, + this); + } + + virtual ~RequestSend() {} + + virtual void Process() { + MessageWithName msg_with_name = + std::make_pair(request_.varname(), std::move(request_)); + queue_->Push(std::move(msg_with_name)); + // TODO(gongwb): check var's info. + responder_.Finish(reply_, grpc::Status::OK, this); + } + + protected: + sendrecv::VariableMessage request_; + sendrecv::VoidMessage reply_; + SimpleBlockQueue* queue_; + ServerAsyncResponseWriter responder_; +}; + +class RequestGet final : public RequestBase { + public: + explicit RequestGet(sendrecv::SendRecvService::AsyncService* service, + grpc::ServerCompletionQueue* cq, framework::Scope* scope) + : RequestBase(service, cq), responder_(&ctx_), scope_(scope) { + service_->RequestGetVariable(&ctx_, &request_, &responder_, cq_, cq_, this); + } + + virtual ~RequestGet() {} + + virtual void Process() { + // proc request. + std::string var_name = request_.varname(); + auto* var = scope_->FindVar(var_name); + SerializeToMessage(var_name, var, platform::CPUDeviceContext(), &reply_); + // TODO(gongwb): check var's info. + responder_.Finish(reply_, grpc::Status::OK, this); + } + + protected: + sendrecv::VariableMessage request_; + sendrecv::VariableMessage reply_; + ServerAsyncResponseWriter responder_; + framework::Scope* scope_; +}; + +void AsyncGRPCServer::RunSyncUpdate() { + grpc::ServerBuilder builder; + builder.AddListeningPort(address_, grpc::InsecureServerCredentials()); + builder.RegisterService(&service_); + + cq_send_ = builder.AddCompletionQueue(); + cq_get_ = builder.AddCompletionQueue(); + server_ = builder.BuildAndStart(); + LOG(INFO) << "Server listening on " << address_ << std::endl; + + std::function send_register = + std::bind(&AsyncGRPCServer::TryToRegisterNewSendOne, this); + std::function get_register = + std::bind(&AsyncGRPCServer::TryToRegisterNewGetOne, this); + + t_send_.reset( + new std::thread(std::bind(&AsyncGRPCServer::HandleRequest, this, false, + cq_send_.get(), "cq_send", send_register))); + + t_get_.reset( + new std::thread(std::bind(&AsyncGRPCServer::HandleRequest, this, true, + cq_get_.get(), "cq_get", get_register))); + + // wait server + server_->Wait(); + t_send_->join(); + t_get_->join(); +} + +void AsyncGRPCServer::ShutdownQueue() { + std::unique_lock lock(cq_mutex_); + cq_send_->Shutdown(); + cq_get_->Shutdown(); + is_shut_down_ = true; +} + +// This URL explains why shutdown is complicate: +// https://stackoverflow.com/questions/35708348/grpc-what-is-the-recommended-way-to-shut-down-an-asynchronous-server-in-c +void AsyncGRPCServer::ShutDown() { + server_->Shutdown(); + ShutdownQueue(); +} + +void AsyncGRPCServer::TryToRegisterNewSendOne() { + std::unique_lock lock(cq_mutex_); + if (is_shut_down_) { + return; + } + RequestSend* send = + new RequestSend(&service_, cq_send_.get(), &var_recv_queue_); + VLOG(4) << "create RequestSend status:" << send->Status(); +} + +void AsyncGRPCServer::TryToRegisterNewGetOne() { + std::unique_lock lock(cq_mutex_); + if (is_shut_down_) { + return; + } + RequestGet* get = new RequestGet(&service_, cq_get_.get(), scope_); + VLOG(4) << "create Requestget status:" << get->Status(); +} + +void AsyncGRPCServer::SetFinishOrDelete(RequestBase*& last) { + std::unique_lock lock(cq_mutex_); + if (is_shut_down_) { + delete last; + last = NULL; + return; + } + + last->SetStatus(FINISH); + return; +} + +void AsyncGRPCServer::HandleRequest(bool wait, grpc::ServerCompletionQueue* cq, + std::string cq_name, + std::function TryToRegisterNewOne) { + TryToRegisterNewOne(); + + void* tag = NULL; + bool ok = false; + while (true) { + if (!cq->Next(&tag, &ok)) { + LOG(INFO) << cq_name << " get CompletionQueue shutdown!"; + break; + } + + if (wait && !done_) { + Wait(); + } + + RequestBase* base = (RequestBase*)tag; + if (!ok) { + VLOG(4) << cq_name << " recv no regular event"; + TryToRegisterNewOne(); + delete base; + continue; + } + + switch (base->Status()) { + case PROCESS: { + VLOG(4) << cq_name << " status:" << base->Status(); + TryToRegisterNewOne(); + base->Process(); + SetFinishOrDelete(base); + break; + } + case FINISH: { + VLOG(4) << cq_name << " status:" << base->Status(); + delete base; + break; + } + default: { assert(false); } + } + } +} + +void AsyncGRPCServer::Wait() { + std::unique_lock lock(this->mutex_); + condition_.wait(lock, [=] { return this->done_ == true; }); +} + +void AsyncGRPCServer::Reset() { + std::lock_guard lock(this->mutex_); + done_ = false; +} + +void AsyncGRPCServer::Done() { + { + std::lock_guard lock(this->mutex_); + done_ = true; + } + condition_.notify_all(); +} + +} // namespace detail +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/detail/grpc_server.h b/paddle/operators/detail/grpc_server.h new file mode 100644 index 00000000000..041fe05b2e9 --- /dev/null +++ b/paddle/operators/detail/grpc_server.h @@ -0,0 +1,91 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/framework/lod_tensor.h" +#include "paddle/framework/scope.h" +#include "paddle/framework/selected_rows.h" +#include "paddle/framework/var_type.h" +#include "paddle/operators/detail/simple_block_queue.h" + +#include "paddle/operators/detail/send_recv.grpc.pb.h" +#include "paddle/operators/detail/send_recv.pb.h" + +#include +#include +#include +#include "paddle/operators/detail/sendrecvop_utils.h" + +namespace paddle { +namespace operators { +namespace detail { + +typedef std::pair MessageWithName; +class RequestBase; + +class AsyncGRPCServer final : public sendrecv::SendRecvService::Service { + public: + explicit AsyncGRPCServer(std::string address) { address_ = address; } + + void RunSyncUpdate(); + + void Reset(); + + void Done(); + + void SetScope(framework::Scope *scope) { scope_ = scope; } + + const MessageWithName Get() { return this->var_recv_queue_.Pop(); } + + void Push(const MessageWithName &msg) { this->var_recv_queue_.Push(msg); } + + void ShutDown(); + + protected: + void Wait(); + void HandleRequest(bool wait, grpc::ServerCompletionQueue *cq, + std::string cq_name, + std::function TryToRegisterNewOne); + void TryToRegisterNewSendOne(); + void TryToRegisterNewGetOne(); + void SetFinishOrDelete(RequestBase *&last); + void ShutdownQueue(); + + private: + std::mutex cq_mutex_; + volatile bool is_shut_down_ = false; + std::unique_ptr cq_send_; + std::unique_ptr cq_get_; + + sendrecv::SendRecvService::AsyncService service_; + std::unique_ptr server_; + + std::string address_; + framework::Scope *scope_; + // received variable from RPC, operators fetch variable from this queue. + SimpleBlockQueue var_recv_queue_; + + // condition of the sub program + std::mutex mutex_; + volatile mutable bool done_; + std::condition_variable condition_; + + std::unique_ptr t_send_; + std::unique_ptr t_get_; +}; + +}; // namespace detail +}; // namespace operators +}; // namespace paddle diff --git a/paddle/operators/detail/recv_impl.cc b/paddle/operators/detail/recv_impl.cc deleted file mode 100644 index 319404e56a5..00000000000 --- a/paddle/operators/detail/recv_impl.cc +++ /dev/null @@ -1,65 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include "send_recv_impl.h" - -namespace paddle { -namespace operators { -namespace detail { - -Status SendRecvServerImpl::SendVariable(ServerContext *context, - const VariableMessage *in_var, - VoidMessage *out_var) { - MessageWithName msg_with_name = - std::make_pair(in_var->varname(), std::move(*in_var)); - var_recv_queue_.Push(std::move(msg_with_name)); - return Status::OK; -} - -Status SendRecvServerImpl::GetVariable(ServerContext *context, - const VariableMessage *in_var, - VariableMessage *out_var) { - std::string get_var_name = in_var->varname(); - auto *var = scope_->FindVar(get_var_name); - - SerializeToMessage(get_var_name, var, platform::CPUDeviceContext(), out_var); - return Status::OK; -} - -Status SendRecvServerImpl::Wait(ServerContext *context, - const VoidMessage *in_var, - VoidMessage *out_var) { - { - std::unique_lock lock(this->mutex_); - condition_.wait(lock, [=] { return this->done_ == true; }); - } - return Status::OK; -} - -void SendRecvServerImpl::Reset() { - std::lock_guard lock(this->mutex_); - done_ = false; -} - -void SendRecvServerImpl::Done() { - { - std::lock_guard lock(this->mutex_); - done_ = true; - } - condition_.notify_all(); -} - -} // namespace detail -} // namespace operators -} // namespace paddle diff --git a/paddle/operators/detail/send_impl.cc b/paddle/operators/detail/send_impl.cc deleted file mode 100644 index ae85cf2cec2..00000000000 --- a/paddle/operators/detail/send_impl.cc +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include "send_recv_impl.h" - -namespace paddle { -namespace operators { -namespace detail { - -bool RPCClient::SendVariable(const framework::Scope& scope, - const std::string& inname) { - ClientContext context; - VariableMessage msg; - VoidMessage out_msg; - // FIXME(typhoonzero): pass device context to here. - auto ctx = platform::CPUDeviceContext(); - auto* var = scope.FindVar(inname); - PADDLE_ENFORCE(var); - SerializeToMessage(inname, var, ctx, &msg); - - Status status = stub_->SendVariable(&context, msg, &out_msg); - if (!status.ok()) { - LOG(ERROR) << "gRPC error: " << status.error_message(); - return false; - } - return true; -} - -bool RPCClient::GetVariable(const framework::Scope& scope, - const std::string& outname) { - ClientContext context; - VariableMessage call_msg, ret_msg; - call_msg.set_varname(outname); - auto ctx = platform::CPUDeviceContext(); - Status status = stub_->GetVariable(&context, call_msg, &ret_msg); - auto* outvar = scope.FindVar(outname); - if (!status.ok()) { - LOG(ERROR) << "gRPC error: " << status.error_message(); - return false; - } - - std::istringstream iss(ret_msg.serialized()); - DeserializeFromMessage(ret_msg, ctx, outvar); - - return true; -} - -void RPCClient::Wait() { - ClientContext context; - VoidMessage call_msg, ret_msg; - stub_->Wait(&context, call_msg, &ret_msg); -} - -} // namespace detail -} // namespace operators -} // namespace paddle diff --git a/paddle/operators/detail/send_recv.proto b/paddle/operators/detail/send_recv.proto index f141c755ce1..8f962b4c69c 100644 --- a/paddle/operators/detail/send_recv.proto +++ b/paddle/operators/detail/send_recv.proto @@ -21,8 +21,6 @@ service SendRecvService { rpc SendVariable(VariableMessage) returns (VoidMessage) {} // Argument VariableMessage for GetVariable should only contain varname. rpc GetVariable(VariableMessage) returns (VariableMessage) {} - // wait for one execution of the program - rpc Wait(VoidMessage) returns (VoidMessage) {} } // VariableMessage is serialized paddle variable message. diff --git a/paddle/operators/detail/send_recv_impl.h b/paddle/operators/detail/send_recv_impl.h deleted file mode 100644 index 1fe54f1f053..00000000000 --- a/paddle/operators/detail/send_recv_impl.h +++ /dev/null @@ -1,141 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#pragma once - -#include "paddle/framework/lod_tensor.h" -#include "paddle/framework/scope.h" -#include "paddle/framework/selected_rows.h" -#include "paddle/framework/var_type.h" -#include "paddle/operators/detail/simple_block_queue.h" - -#include "paddle/operators/detail/send_recv.grpc.pb.h" -#include "paddle/operators/detail/send_recv.pb.h" - -#include - -using grpc::Channel; -using grpc::Server; -using grpc::ServerContext; -using grpc::ServerReader; -using grpc::ServerBuilder; - -using grpc::ClientContext; -using grpc::ClientReader; -using grpc::ClientReaderWriter; -using grpc::ClientWriter; -using grpc::Status; -using sendrecv::SendRecvService; -using sendrecv::VariableMessage; -using sendrecv::VoidMessage; - -namespace paddle { -namespace operators { -namespace detail { - -typedef std::pair MessageWithName; - -class SendRecvServerImpl final : public SendRecvService::Service { - public: - explicit SendRecvServerImpl() {} - - Status SendVariable(ServerContext *context, const VariableMessage *in_var, - VoidMessage *out_var) override; - Status GetVariable(ServerContext *context, const VariableMessage *in_var, - VariableMessage *out_var) override; - Status Wait(ServerContext *context, const VoidMessage *in_var, - VoidMessage *out_var) override; - void Reset(); - void Done(); - void SetScope(framework::Scope *scope) { scope_ = scope; }; - - const MessageWithName Get() { return this->var_recv_queue_.Pop(); } - - void Push(const MessageWithName &msg) { this->var_recv_queue_.Push(msg); } - - private: - // received variable from RPC, operators fetch variable from this queue. - SimpleBlockQueue var_recv_queue_; - framework::Scope *scope_; - // condition of the sub program - std::mutex mutex_; - bool done_; - std::condition_variable condition_; -}; - -// RPCClient is a class to send tensors to pserver sub-network -// using different hashing methods. -class RPCClient { - public: - RPCClient(std::shared_ptr channel) - : stub_(SendRecvService::NewStub(channel)) {} - - bool SendVariable(const framework::Scope &scope, const std::string &inname); - bool GetVariable(const framework::Scope &scope, const std::string &outname); - void Wait(); - - private: - std::unique_ptr stub_; -}; - -inline void SerializeToMessage(const std::string &name, - const framework::Variable *var, - const platform::DeviceContext &ctx, - VariableMessage *msg) { - msg->set_varname(name); - std::ostringstream oss; - switch (framework::ToVarType(var->Type())) { - case framework::proto::VarDesc_VarType_LOD_TENSOR: - msg->set_type(sendrecv::VarType::LOD_TENSOR); - framework::SerializeToStream(oss, var->Get(), ctx); - break; - case framework::proto::VarDesc_VarType_SELECTED_ROWS: - msg->set_type(sendrecv::VarType::SELECTED_ROWS); - framework::SerializeToStream(oss, var->Get(), - ctx); - break; - default: { - PADDLE_THROW("Serialize does not support type: %s", - typeid(var->Type()).name()); - break; - } - } - msg->set_serialized(oss.str()); -} - -inline void DeserializeFromMessage(const VariableMessage &msg, - const platform::DeviceContext &ctx, - framework::Variable *var) { - using namespace paddle::framework::proto; - std::istringstream iss(msg.serialized()); - switch (msg.type()) { - case sendrecv::VarType::LOD_TENSOR: - DeserializeFromStream(iss, var->GetMutable(), ctx); - break; - case sendrecv::VarType::SELECTED_ROWS: { - DeserializeFromStream(iss, var->GetMutable(), - ctx); - break; - } - default: { - PADDLE_THROW("Deserialize does not support type: %s", - typeid(var->Type()).name()); - break; - } - } -} - -} // namespace detail -} // namespace operators -} // namespace paddle diff --git a/paddle/operators/detail/sendrecvop_utils.cc b/paddle/operators/detail/sendrecvop_utils.cc new file mode 100644 index 00000000000..7635b9e8dbd --- /dev/null +++ b/paddle/operators/detail/sendrecvop_utils.cc @@ -0,0 +1,68 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/detail/sendrecvop_utils.h" + +namespace paddle { +namespace operators { +namespace detail { + +void SerializeToMessage(const std::string& name, const framework::Variable* var, + const platform::DeviceContext& ctx, + sendrecv::VariableMessage* msg) { + msg->set_varname(name); + std::ostringstream oss; + switch (framework::ToVarType(var->Type())) { + case framework::proto::VarDesc_VarType_LOD_TENSOR: + msg->set_type(sendrecv::VarType::LOD_TENSOR); + framework::SerializeToStream(oss, var->Get(), ctx); + break; + case framework::proto::VarDesc_VarType_SELECTED_ROWS: + msg->set_type(sendrecv::VarType::SELECTED_ROWS); + framework::SerializeToStream(oss, var->Get(), + ctx); + break; + default: { + PADDLE_THROW("Serialize does not support type: %s", + typeid(var->Type()).name()); + break; + } + } + msg->set_serialized(oss.str()); +} + +void DeserializeFromMessage(const sendrecv::VariableMessage& msg, + const platform::DeviceContext& ctx, + framework::Variable* var) { + std::istringstream iss(msg.serialized()); + switch (msg.type()) { + case sendrecv::VarType::LOD_TENSOR: + DeserializeFromStream(iss, var->GetMutable(), ctx); + break; + case sendrecv::VarType::SELECTED_ROWS: { + DeserializeFromStream(iss, var->GetMutable(), + ctx); + break; + } + default: { + PADDLE_THROW("Deserialize does not support type: %s", + typeid(var->Type()).name()); + break; + } + } +} + +} // namespace detail +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/detail/sendrecvop_utils.h b/paddle/operators/detail/sendrecvop_utils.h new file mode 100644 index 00000000000..bc6581afab9 --- /dev/null +++ b/paddle/operators/detail/sendrecvop_utils.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once +#include +#include +#include + +#include "paddle/framework/data_type.h" +#include "paddle/framework/lod_tensor.h" +#include "paddle/framework/scope.h" +#include "paddle/framework/selected_rows.h" +#include "paddle/framework/var_type.h" + +#include "paddle/operators/detail/send_recv.grpc.pb.h" +#include "paddle/operators/detail/send_recv.pb.h" + +namespace paddle { +namespace operators { +namespace detail { + +void SerializeToMessage(const std::string& name, const framework::Variable* var, + const platform::DeviceContext& ctx, + sendrecv::VariableMessage* msg); + +void DeserializeFromMessage(const sendrecv::VariableMessage& msg, + const platform::DeviceContext& ctx, + framework::Variable* var); +} // namespace detail +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/recv_op.cc b/paddle/operators/recv_op.cc index 9331c7b5634..55b33343af4 100644 --- a/paddle/operators/recv_op.cc +++ b/paddle/operators/recv_op.cc @@ -24,7 +24,8 @@ limitations under the License. */ #include "paddle/framework/lod_tensor.h" #include "paddle/framework/op_registry.h" #include "paddle/framework/proto_desc.h" -#include "paddle/operators/detail/send_recv_impl.h" +#include "paddle/operators/detail/grpc_server.h" +#include "paddle/operators/detail/sendrecvop_utils.h" #include "paddle/operators/detail/simple_block_queue.h" #define LISTEN_TERMINATE_MESSAGE "TERMINATE@RECV" @@ -32,6 +33,11 @@ limitations under the License. */ namespace paddle { namespace operators { +void RunServer(std::shared_ptr service) { + service->RunSyncUpdate(); + VLOG(4) << "RunServer thread end"; +} + static void CreateTensorFromMessageType(framework::Variable *var, sendrecv::VarType var_type) { if (var_type == sendrecv::VarType::LOD_TENSOR) { @@ -46,18 +52,6 @@ static void CreateTensorFromMessageType(framework::Variable *var, } } -void RunServer(Server **rpc_server, - std::shared_ptr service, - const std::string &server_address) { - ServerBuilder builder; - builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); - builder.RegisterService(service.get()); - std::unique_ptr server(builder.BuildAndStart()); - *rpc_server = server.get(); - LOG(INFO) << "Server listening on " << server_address; - server->Wait(); -} - class RecvOp : public framework::OperatorBase { public: RecvOp(const std::string &type, const framework::VariableNameMap &inputs, @@ -65,10 +59,9 @@ class RecvOp : public framework::OperatorBase { const framework::AttributeMap &attrs) : OperatorBase(type, inputs, outputs, attrs) { if (!rpc_service_) { - rpc_service_.reset(new detail::SendRecvServerImpl()); std::string endpoint = Attr("endpoint"); - server_thread_.reset( - new std::thread(RunServer, &rpc_server_, rpc_service_, endpoint)); + rpc_service_.reset(new detail::AsyncGRPCServer(endpoint)); + server_thread_.reset(new std::thread(RunServer, rpc_service_)); } } @@ -76,7 +69,7 @@ class RecvOp : public framework::OperatorBase { detail::MessageWithName term_msg; term_msg.first = LISTEN_TERMINATE_MESSAGE; rpc_service_->Push(term_msg); - rpc_server_->Shutdown(); + rpc_service_->ShutDown(); server_thread_->join(); } @@ -99,10 +92,12 @@ class RecvOp : public framework::OperatorBase { auto grad_list = Attr>("GradList"); auto trainer_count = Attr("Trainers"); size_t param_count = param_list.size(); + rpc_service_->Reset(); // TODO(typhoonzero): change this to a while_op for every cluster-batch. bool exit_flag = false; while (!exit_flag) { + // TODO(gognwb): simply this loop. // Get from multiple trainers, we don't care about order in which // the gradient arrives, just add suffix 0~n then average the gradient. for (size_t i = 0; i < param_count * trainer_count; ++i) { @@ -110,6 +105,7 @@ class RecvOp : public framework::OperatorBase { const detail::MessageWithName &v = rpc_service_->Get(); auto grad_var_name = v.first; if (grad_var_name == LISTEN_TERMINATE_MESSAGE) { + VLOG(4) << "received LISTEN_TERMINATE_MESSAGE and RunOp.Run() exit"; exit_flag = true; break; } @@ -118,10 +114,12 @@ class RecvOp : public framework::OperatorBase { if (it != grad_list.end()) { param_var_name = param_list[it - grad_list.begin()]; } else { - LOG(ERROR) << "grad have no paired param found!"; + LOG(ERROR) << "grad have no paired param found!\"" << grad_var_name + << "\""; } VLOG(3) << "recved grad: " << grad_var_name << " updating param: " << param_var_name; + auto *merged_grad = recv_scope.FindVar(grad_var_name); if (merged_grad == nullptr) { auto *ptr = recv_scope.Var(grad_var_name); @@ -141,9 +139,11 @@ class RecvOp : public framework::OperatorBase { auto &dev_ctx = *pool.Get(dev_place); detail::DeserializeFromMessage(v.second, dev_ctx, var); } + if (exit_flag) { break; } + rpc_service_->Reset(); std::string program_str = Attr("OptimizeProgram"); @@ -158,17 +158,14 @@ class RecvOp : public framework::OperatorBase { } catch (std::exception &e) { LOG(ERROR) << "run sub program error " << e.what(); } + rpc_service_->Done(); grads_counter_.clear(); } // while(true) } protected: - // grpc server instance to track status and gracefully shutdown. - // borrow an pointer from server thread. - Server *rpc_server_{nullptr}; - // grpc send/recv service implement to register. - std::shared_ptr rpc_service_; + std::shared_ptr rpc_service_; std::shared_ptr server_thread_; mutable std::unordered_map grads_counter_; }; diff --git a/paddle/operators/send_op.cc b/paddle/operators/send_op.cc index 95c207221a7..4d145250bdc 100644 --- a/paddle/operators/send_op.cc +++ b/paddle/operators/send_op.cc @@ -19,59 +19,45 @@ limitations under the License. */ #include "paddle/framework/lod_tensor.h" #include "paddle/framework/op_registry.h" -#include "paddle/operators/detail/send_recv_impl.h" -#include "paddle/operators/detail/simple_block_queue.h" +#include +#include "paddle/operators/detail/grpc_client.h" namespace paddle { namespace operators { -// TODO(typhoonzero): this is a simple implementation which only send -// one tensor class SendOp : public framework::OperatorBase { public: - SendOp(const std::string &type, const framework::VariableNameMap &inputs, - const framework::VariableNameMap &outputs, - const framework::AttributeMap &attrs) - : OperatorBase(type, inputs, outputs, attrs) { - // init client when the operator is created at runtime. - std::vector endpoints = - Attr>("endpoints"); - for (auto ep : endpoints) { - client_map_[ep].reset(new detail::RPCClient( - grpc::CreateChannel(ep, grpc::InsecureChannelCredentials()))); - } - } + SendOp(const std::string& type, const framework::VariableNameMap& inputs, + const framework::VariableNameMap& outputs, + const framework::AttributeMap& attrs) + : OperatorBase(type, inputs, outputs, attrs) {} - void Run(const framework::Scope &scope, - const platform::Place &dev_place) const override { + void Run(const framework::Scope& scope, + const platform::Place& dev_place) const override { auto ins = Inputs("X"); auto outs = Outputs("Out"); std::vector epmap = Attr>("epmap"); - // TODO(typhoonzero): use async calls to send multiple variable asyncly. - for (size_t i = 0; i < ins.size(); ++i) { - bool ret = client_map_[epmap[i]]->SendVariable(scope, ins[i]); - if (!ret) { - LOG(ERROR) << "send variable error: " << ins[i]; - } + + // FIXME(gongwb): DeviceContext? + auto ctx = platform::CPUDeviceContext(); + for (size_t i = 0; i < ins.size(); i++) { + client_.AsyncSendVariable(epmap[i], ctx, scope, ins[i]); } - // TODO(typhoonzero): support async optimization - client_map_[epmap[0]]->Wait(); - for (size_t i = 0; i < outs.size(); ++i) { - bool ret = client_map_[epmap[i]]->GetVariable(scope, outs[i]); - if (!ret) { - LOG(ERROR) << "GetVariable error: " << outs[i]; - } + + for (size_t i = 0; i < outs.size(); i++) { + client_.AsyncGetVariable(epmap[i], ctx, scope, outs[i]); } + + client_.wait(); } - protected: - mutable std::unordered_map> - client_map_; + private: + mutable detail::RPCClient client_; }; class SendOpMaker : public framework::OpProtoAndCheckerMaker { public: - SendOpMaker(OpProto *proto, OpAttrChecker *op_checker) + SendOpMaker(OpProto* proto, OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("X", "(Tensor) Input tensor to be send").AsDuplicable(); AddOutput("Out", "(Tensor) Output tensor to get from server") diff --git a/paddle/operators/send_recv_op_test.cc b/paddle/operators/send_recv_op_test.cc index fa94424bf9e..ea091694798 100644 --- a/paddle/operators/send_recv_op_test.cc +++ b/paddle/operators/send_recv_op_test.cc @@ -140,7 +140,7 @@ void StartServerNet(bool is_sparse) { TEST(SendRecvOp, CPUDense) { std::thread server_thread(StartServerNet, false); - sleep(3); // wait server to start + sleep(10); // wait server to start // local net f::Scope scope; p::CPUPlace place; -- GitLab From 89de5d5e6628ffaebe9e1fb7070d5308e5b8ab9a Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Thu, 11 Jan 2018 10:10:03 +0800 Subject: [PATCH 0197/2305] Fix cuda kernel of sequence scale functor --- paddle/operators/math/sequence_scale.cu | 6 +++--- paddle/operators/math/sequence_scale.h | 16 +++++++++------- paddle/operators/warpctc_op.h | 19 ------------------- 3 files changed, 12 insertions(+), 29 deletions(-) diff --git a/paddle/operators/math/sequence_scale.cu b/paddle/operators/math/sequence_scale.cu index 23b0cce13ff..fd1370c1184 100644 --- a/paddle/operators/math/sequence_scale.cu +++ b/paddle/operators/math/sequence_scale.cu @@ -22,16 +22,16 @@ template __global__ void SequenceScaleKernel(T* seq, size_t* lod, const T* scales, const size_t num_seq, const size_t seq_width) { - size_t idx = blockIdx.x * blockDim.y + threadIdx.y; + const int idx = threadIdx.x + blockIdx.x * blockDim.x; - if (idx < lod[num_seq]) { + if (idx < lod[num_seq] * seq_width) { size_t i = 0; for (i = 0; i < num_seq; ++i) { if (idx < lod[i + 1] * seq_width) { break; } } - seq[i] *= scales[i]; + seq[idx] *= scales[i]; } } diff --git a/paddle/operators/math/sequence_scale.h b/paddle/operators/math/sequence_scale.h index a42fc6d0db2..8c47179b55d 100644 --- a/paddle/operators/math/sequence_scale.h +++ b/paddle/operators/math/sequence_scale.h @@ -27,19 +27,21 @@ namespace math { * All sequences will be padded to the same length and stored in a transposed * shape. * Example: - * seq (s0, s0, s0, s0; s1, s1; s2, s2, s2; s3) - * padding (s0, s1, s2, s3; s0, s1, s2, 0; s0, 0, s2, 0; s0, 0, 0, 0) + * Given: + * seq = (s0, s0, s0, s0; s1, s1; s2, s2, s2; s3) + * scales = (2, 3, 4, 5) + * then: + * result = (2*s0, 2*s0, 2*s0, 2*s0; 3*s1, 3*s1; 4*s2, 4*s2, 4*s2; 5*s3) + * - * \param context device context of this functor. + * \param context Device context of this functor. * \param seq LoDTensor which is stored in sequence format, the shape * is [total_sequence_length, sequence_width] where * total_sequence_length is the sum of all sequences' * length. - * \param padding Tensor which is padded to the same length, the shape is - * [max_sequence_length, num_sequences, sequence_width]. - * \param norm_by_times whether dividing sequence's length. + * \param scales Array. The i-th sequence will be scaled by scales[i]. + * \param num_seq Number of sequence * - * \note transposition is also done in this functor. */ template class ScaleLoDTensorFunctor { diff --git a/paddle/operators/warpctc_op.h b/paddle/operators/warpctc_op.h index c2bbceb6d15..d41752e7333 100644 --- a/paddle/operators/warpctc_op.h +++ b/paddle/operators/warpctc_op.h @@ -14,7 +14,6 @@ limitations under the License. */ #pragma once -#include "paddle/framework/eigen.h" #include "paddle/framework/op_registry.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/sequence_padding.h" @@ -209,12 +208,6 @@ class WarpCTCGradKernel : public framework::OpKernel { auto* logits_grad = ctx.Output(framework::GradVarName("Logits")); const Tensor* loss_grad = ctx.Input(framework::GradVarName("Loss")); - // LOG(ERROR) << "loss_grad_dims: " << loss_grad_dims; - // for (int i=0; inumel();i++) { - // LOG(ERROR) << "loss_grad: " << loss_grad_data[i]; - //} - - // T* logits_grad_data = logits_grad->mutable_data(ctx.GetPlace()); bool norm_by_times = ctx.Attr("norm_by_times"); math::UnpaddingLoDTensorFunctor()( @@ -226,18 +219,6 @@ class WarpCTCGradKernel : public framework::OpKernel { math::ScaleLoDTensorFunctor()( ctx.template device_context(), *logits_grad, loss_grad_data, num_seq); - /* - int level = 0; - auto logits_grad_lod = framework::ToAbsOffset(logits_grad->lod()); - const size_t num_sequences = logits_grad_lod[level].size() - 1; - for (int seq_index = 0; seq_index < num_sequences; ++seq_index) { - for (int token_index = logits_grad_lod[level][seq_index]; - token_index < logits_grad_lod[level][seq_index + 1]; - ++token_index) { - logits_grad_data[token_index] *= loss_grad_data[seq_index]; - } - } - */ } }; -- GitLab From 0064f6d9762668f37340d39f63017c55b93b97ee Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Thu, 11 Jan 2018 10:24:18 +0800 Subject: [PATCH 0198/2305] refine test --- python/paddle/v2/fluid/tests/test_clip.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_clip.py b/python/paddle/v2/fluid/tests/test_clip.py index 7e72112a838..a71823f7e82 100644 --- a/python/paddle/v2/fluid/tests/test_clip.py +++ b/python/paddle/v2/fluid/tests/test_clip.py @@ -34,6 +34,9 @@ fluid.backward.append_backward( hidden1_grad = prog.block(0).var(hidden1.name + "@GRAD") hidden1_grad_clip = prog_clip.block(0).var(hidden1.name + "@GRAD") +hidden2_grad = prog.block(0).var(hidden2.name + "@GRAD") +hidden2_grad_clip = prog_clip.block(0).var(hidden2.name + "@GRAD") + train_reader = paddle.batch( paddle.reader.shuffle( paddle.dataset.mnist.train(), buf_size=8192), @@ -49,11 +52,16 @@ for data in train_reader(): count += 1 if count > 5: break - out = exe.run(prog, feed=feeder.feed(data), fetch_list=[hidden1_grad]) - out_clip = exe.run(prog_clip, - feed=feeder.feed(data), - fetch_list=[hidden1_grad_clip]) - if not (out[0].clip(min=CLIP_MIN, max=CLIP_MAX) == out_clip[0]).all(): + out1, out2 = exe.run(prog, + feed=feeder.feed(data), + fetch_list=[hidden1_grad, hidden2_grad]) + out1_clip, out2_clip = exe.run( + prog_clip, + feed=feeder.feed(data), + fetch_list=[hidden1_grad_clip, hidden2_grad_clip]) + if not ((out1.clip( + min=CLIP_MIN, max=CLIP_MAX) == out1_clip).all() and + (out2 == out2_clip).all()): exit(1) exit(0) -- GitLab From 87f9b5836359d607cc4ef1c9684c067ed7e7b1e0 Mon Sep 17 00:00:00 2001 From: QI JUN Date: Thu, 11 Jan 2018 10:49:48 +0800 Subject: [PATCH 0199/2305] set stop gradient for mask in dropout layer (#7390) --- python/paddle/v2/fluid/layers/nn.py | 17 ++++++++++++++++- python/paddle/v2/fluid/layers/ops.py | 13 +------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index b1534c5a886..48a6bee5588 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -14,7 +14,7 @@ __all__ = [ 'chunk_eval', 'sequence_conv', 'conv2d', 'sequence_pool', 'pool2d', 'batch_norm', 'beam_search_decode', 'conv2d_transpose', 'sequence_expand', 'lstm_unit', 'reduce_sum', 'reduce_mean', 'reduce_max', 'reduce_min', - 'sequence_first_step', 'sequence_last_step' + 'sequence_first_step', 'sequence_last_step', 'dropout' ] @@ -386,6 +386,21 @@ def cos_sim(X, Y, **kwargs): return out +def dropout(x, dropout_prob, is_test=False, seed=0, **kwargs): + helper = LayerHelper('dropout', **kwargs) + out = helper.create_tmp_variable(dtype=x.dtype) + mask = helper.create_tmp_variable(dtype=x.dtype, stop_gradient=True) + helper.append_op( + type='dropout', + inputs={'X': [x]}, + outputs={'Out': [out], + 'Mask': [mask]}, + attrs={'dropout_prob': dropout_prob, + 'is_test': is_test, + 'seed': seed}) + return out + + def cross_entropy(input, label, **kwargs): """ **Cross Entropy Layer** diff --git a/python/paddle/v2/fluid/layers/ops.py b/python/paddle/v2/fluid/layers/ops.py index 544623c4bce..d3a5b707859 100644 --- a/python/paddle/v2/fluid/layers/ops.py +++ b/python/paddle/v2/fluid/layers/ops.py @@ -1,23 +1,12 @@ from ..registry import register_layer __activations__ = [ - 'abs', - 'ceil', - 'exp', - 'floor', - 'log', - 'relu', - 'round', - 'sigmoid', - 'sqrt', - 'square', - 'tanh', + 'abs', 'tanh', 'sigmoid', 'relu', 'sqrt', 'ceil', 'floor', 'log', 'round' ] __all__ = [ 'mean', 'mul', - 'dropout', 'reshape', 'scale', 'transpose', -- GitLab From 12aca860bf24ddc05a06722c1fc11ff3cfedc893 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Thu, 11 Jan 2018 11:03:54 +0800 Subject: [PATCH 0200/2305] Add comment --- .../paddle/v2/fluid/tests/test_parallel_op.py | 58 ++++++++++++++++--- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index 049ae0fe28d..dde7206e4fd 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -4,15 +4,35 @@ import numpy class BaseParallelForTest(unittest.TestCase): - def main(self, callback, feed, fetch): + def run_test(self, callback, feed, fetch): + """ + Run the unittest for parallel.for + Args: + callback(callable): A callable function returns a generator. There + are two yields in the generator function. The first yield + returns the data layers, and the second yield returns the loss. + The modified data variables will be sent back during the first + yield. + + feed(dict): The executor feeding dictionary. + fetch(list|basestr): The fetch name lists. + + Returns: + None + + Raises: + AssertionError when the computation of cpu, parallel.for in cpu, + gpu, parallel.for in gpu are different. + + """ cpu = fluid.CPUPlace() - result_cpu = self._main_impl_( + result_cpu = self._run_test_impl_( callback=callback, feed=feed, fetch=fetch, place=cpu, use_parallel=False) - result_cpu_parallel = self._main_impl_( + result_cpu_parallel = self._run_test_impl_( callback=callback, feed=feed, fetch=fetch, @@ -20,13 +40,13 @@ class BaseParallelForTest(unittest.TestCase): use_parallel=True) if fluid.core.is_compile_gpu(): gpu = fluid.CUDAPlace(0) - result_gpu = self._main_impl_( + result_gpu = self._run_test_impl_( callback=callback, feed=feed, fetch=fetch, place=gpu, use_parallel=False) - result_gpu_parallel = self._main_impl_( + result_gpu_parallel = self._run_test_impl_( callback=callback, feed=feed, fetch=fetch, @@ -37,7 +57,17 @@ class BaseParallelForTest(unittest.TestCase): else: self._assert_same_(fetch, result_cpu, result_cpu_parallel) - def _main_impl_(self, callback, feed, fetch, place, use_parallel=False): + def _run_test_impl_(self, callback, feed, fetch, place, use_parallel=False): + """ + Run a single test, returns the fetch values + Args: + place(Place): the computation place. + use_parallel(bool): Whether use parallel.for or not. + + Returns: + Fetched numpy arrays. + + """ if isinstance(fetch, basestring): fetch = [fetch] main = fluid.Program() @@ -77,6 +107,20 @@ class BaseParallelForTest(unittest.TestCase): return exe.run(main, feed=feed, fetch_list=fetch) def _assert_same_(self, fetch, *args): + """ + Assert the return values of `run_test` are same. + Args: + fetch: Fetch list. Used for print error message + *args: The fetch result lists of each situations. + + Returns: + None + + Raises: + AssertionError + + """ + def _impl_(a, b, fetch_id, item_id): item_str = ['CPU', 'ParallelCPU', 'GPU', 'ParallelGPU'] flag = numpy.allclose(a, b, rtol=0.1) @@ -100,7 +144,7 @@ class ParallelOpTest(BaseParallelForTest): loss = fluid.layers.mean(x=hidden) yield loss - self.main( + self.run_test( callback=__network__, feed={ 'img': numpy.random.random(size=(128, 784)).astype('float32') -- GitLab From 6f79bbc93ea1b02ddf9d82699f5f80e0a759efd7 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Thu, 11 Jan 2018 10:48:51 +0800 Subject: [PATCH 0201/2305] Add unittest except gradient checking (since bugs). --- .../fluid/tests/test_dynrnn_static_input.py | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 python/paddle/v2/fluid/tests/test_dynrnn_static_input.py diff --git a/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py new file mode 100644 index 00000000000..b68d7ca49b6 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py @@ -0,0 +1,192 @@ +import unittest +import paddle.v2 as paddle +import paddle.v2.fluid.core as core +import paddle.v2.fluid as fluid +from paddle.v2.fluid.backward import append_backward +import paddle.v2.fluid.framework as framework +from paddle.v2.fluid.framework import Program, switch_main_program +import bisect +import numpy as np + +fluid.default_startup_program().random_seed = 0 +np.random.seed(0) + + +class TestDyRnnStaticInput(unittest.TestCase): + def setUp(self): + self._delta = 0.005 + self._max_sequence_len = 3 + self._program = Program() + switch_main_program(self._program) + self.output_dim = 10 + self.place = core.CPUPlace() + self.prepare_x_tensor() + self.prepare_static_input_tensor() + self.exe = fluid.Executor(self.place) + + def prepare_x_tensor(self): + self.x_tensor_dim = 10 + lod = [[0, 2, 3, 6]] + shape = [lod[0][-1], self.x_tensor_dim] + self.x_tensor_data = np.random.random(shape).astype('float32') + self.x_tensor = core.LoDTensor() + self.x_tensor.set_lod(lod) + self.x_tensor.set(self.x_tensor_data, self.place) + + def prepare_static_input_tensor(self): + self.static_input_tensor_dim = 4 + lod = [[0, 1, 3, 6]] + shape = [lod[0][-1], self.static_input_tensor_dim] + self.static_input_data = np.random.random(shape).astype('float32') + self.static_input_tensor = core.LoDTensor() + self.static_input_tensor.set_lod(lod) + self.static_input_tensor.set(self.static_input_data, self.place) + + def fetch_value(self, var): + fetch_outs = self.exe.run(feed={ + 'x_tensor': self.x_tensor, + 'static_input_tensor': self.static_input_tensor + }, + fetch_list=[var], + return_numpy=False) + return self._lodtensor_to_ndarray(fetch_outs[0]) + + def _lodtensor_to_ndarray(self, lod_tensor): + dims = lod_tensor.get_dims() + ndarray = np.zeros(shape=dims).astype('float32') + for i in xrange(np.product(dims)): + ndarray.ravel()[i] = lod_tensor.get_float_element(i) + return ndarray + + def build_graph(self, only_forward=False): + x_tensor = fluid.layers.data( + name='x_tensor', + shape=[self.x_tensor_dim], + dtype='float32', + lod_level=1) + x_tensor.stop_gradient = False + + static_input_tensor = fluid.layers.data( + name='static_input_tensor', + shape=[self.static_input_tensor_dim], + dtype='float32', + lod_level=1) + static_input_tensor.stop_gradient = False + + if only_forward: + static_input_out_array = self._program.global_block().create_var( + name='static_input_out_array', + type=core.VarDesc.VarType.LOD_TENSOR_ARRAY, + dtype='float32') + static_input_out_array.stop_gradient = True + + rnn = fluid.layers.DynamicRNN() + with rnn.block(): + step_x = rnn.step_input(x_tensor) + step_static_input = rnn.static_input(static_input_tensor) + if only_forward: + fluid.layers.array_write( + x=step_static_input, + i=rnn.step_idx, + array=static_input_out_array) + last = fluid.layers.sequence_pool( + input=step_static_input, pool_type='last') + projected = fluid.layers.fc(input=[step_x, last], + size=self.output_dim) + rnn.output(projected) + + if only_forward: + static_input_step_outs = [] + step_idx = fluid.layers.fill_constant( + shape=[1], dtype='int64', value=0) + step_idx.stop_gradient = True + + for i in xrange(self._max_sequence_len): + step_out = fluid.layers.array_read(static_input_out_array, + step_idx) + step_out.stop_gradient = True + static_input_step_outs.append(step_out) + fluid.layers.increment(x=step_idx, value=1.0, in_place=True) + + if only_forward: + return static_input_step_outs + + last = fluid.layers.sequence_pool(input=rnn(), pool_type='last') + loss = fluid.layers.mean(x=last) + append_backward(loss) + static_input_grad = self._program.global_block().var( + framework.grad_var_name('static_input_tensor')) + return static_input_grad, loss + + def get_seq_len_from_lod(self, lod): + return [lod[0][i + 1] - lod[0][i] for i in xrange(len(lod[0]) - 1)] + + def get_expected_static_step_outs(self): + x_lod = self.x_tensor.lod() + x_seq_len = self.get_seq_len_from_lod(x_lod) + x_seq_len_sorted = sorted(x_seq_len) + x_sorted_indices = np.argsort(x_seq_len)[::-1] + + static_lod = self.static_input_tensor.lod() + static_sliced = [ + self.static_input_data[static_lod[0][i]:static_lod[0][i + 1]] + for i in xrange(len(static_lod[0]) - 1) + ] + static_seq_len = self.get_seq_len_from_lod(static_lod) + static_reordered = [] + for i in xrange(len(x_sorted_indices)): + static_reordered.extend(static_sliced[x_sorted_indices[i]].tolist()) + static_seq_len_reordered = [ + static_seq_len[x_sorted_indices[i]] + for i in xrange(len(x_sorted_indices)) + ] + + static_step_outs = [] + + for i in xrange(self._max_sequence_len): + end = len(x_seq_len) - bisect.bisect_left(x_seq_len_sorted, i + 1) + end = sum(static_seq_len_reordered[:end]) + static_step_outs.append( + np.array(static_reordered[:end]).astype('float32')) + + return static_step_outs + + def test_step_out(self): + static_step_outs = self.build_graph(only_forward=True) + self.exe.run(framework.default_startup_program()) + expected_step_outs = self.get_expected_static_step_outs() + for i in xrange(self._max_sequence_len): + step_out = self.fetch_value(static_step_outs[i]) + self.assertTrue(np.allclose(step_out, expected_step_outs[i])) + + def test_network_gradient(self): + pass #still have bug (seed doesn't work) + ''' + static_input_grad, loss = self.build_graph() + self.exe.run(framework.default_startup_program()) + + actual_gradients = self.fetch_value(static_input_grad) + + static_input_shape = self.static_input_tensor.get_dims() + numeric_gradients = np.zeros(shape=static_input_shape).astype('float32') + #print(actual_gradient) + print(actual_gradients) + # calculate numeric gradients + tensor_size = np.product(static_input_shape) + for i in xrange(tensor_size): + origin = self.static_input_tensor.get_float_element(i) + x_pos = origin + self._delta + self.static_input_tensor.set_float_element(i, x_pos) + y_pos = self.fetch_value(loss)[0] + x_neg = origin - self._delta + self.static_input_tensor.set_float_element(i, x_neg) + y_neg = self.fetch_value(loss)[0] + self.static_input_tensor.set_float_element(i, origin) + numeric_gradients.ravel()[i] = (y_pos - y_neg) / self._delta / 2 + + print(numeric_gradients) + ''' + + +if __name__ == '__main__': + unittest.main() -- GitLab From 92eb247f07a47a6ed6af61d39d305935fb2fcd76 Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Wed, 10 Jan 2018 19:13:29 -0800 Subject: [PATCH 0202/2305] "fix stupid error" --- python/paddle/v2/fluid/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/__init__.py b/python/paddle/v2/fluid/__init__.py index c163d9a92bb..422aa0a5ba2 100644 --- a/python/paddle/v2/fluid/__init__.py +++ b/python/paddle/v2/fluid/__init__.py @@ -58,7 +58,7 @@ def __bootstrap__(): read_env_flags = ['use_pinned_memory', 'check_nan_inf'] if core.is_compile_gpu(): - read_env_flags.append(['fraction_of_gpu_memory_to_use', 'op_sync']) + read_env_flags += ['fraction_of_gpu_memory_to_use', 'op_sync'] core.init_gflags([sys.argv[0]] + ["--tryfromenv=" + ",".join(read_env_flags)]) core.init_glog(sys.argv[0]) -- GitLab From 1797f3db850206cc7e87ee0ff2c06816b1dfcae7 Mon Sep 17 00:00:00 2001 From: QI JUN Date: Thu, 11 Jan 2018 11:15:06 +0800 Subject: [PATCH 0203/2305] Refine memory optimization transpiler (#7394) * add update graph method for memory optimization transpiler to avoid rebuild graph everytime * clean code * reset var desc if hit cache --- python/paddle/v2/fluid/framework.py | 3 + .../fluid/memory_optimization_transpiler.py | 77 ++++++++++++++----- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index 2fb388acfc0..3ef6b33192d 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -236,6 +236,9 @@ class Variable(object): __repr__ = __str__ + def set_desc(self, input): + self.desc = input + @property def persistable(self): return self.desc.persistable() diff --git a/python/paddle/v2/fluid/memory_optimization_transpiler.py b/python/paddle/v2/fluid/memory_optimization_transpiler.py index 571fce7fac6..6800d7ddbb1 100644 --- a/python/paddle/v2/fluid/memory_optimization_transpiler.py +++ b/python/paddle/v2/fluid/memory_optimization_transpiler.py @@ -3,6 +3,17 @@ import framework from framework import Program, default_main_program, Parameter, Variable import backward from backward import _rename_arg_ +from . import core + +dtype_to_size = { + core.DataType.FP16: 2, + core.DataType.FP32: 4, + core.DataType.FP64: 8, + core.DataType.INT16: 2, + core.DataType.INT32: 4, + core.DataType.INT64: 8, + core.DataType.BOOL: 1 +} class ControlFlowGraph(object): @@ -28,18 +39,33 @@ class ControlFlowGraph(object): block_size = program_desc.num_blocks() # TODO(qijun) handle Program with if/while operators - self.global_block = program_desc.block(0) - self.op_size = self.global_block.op_size() + self.global_block_desc = program_desc.block(0) + self.op_size = self.global_block_desc.op_size() op_node_connections = [(i, i + 1) for i in range(self.op_size - 1)] self._add_connections(op_node_connections) - self.ops = [self.global_block.op(i) for i in range(self.op_size)] + self.ops = [self.global_block_desc.op(i) for i in range(self.op_size)] for i in range(self.op_size): self._uses[i].update(self.ops[i].input_arg_names()) self._defs[i].update(self.ops[i].output_arg_names()) + def _update_graph(self, old_name, new_name, begin_idx=0): + for i in range(begin_idx, self.op_size): + if old_name in self._uses[i]: + self._uses[i].remove(old_name) + self._uses[i].add(new_name) + if old_name in self._defs[i]: + self._defs[i].remove(old_name) + self._defs[i].add(new_name) + if old_name in self._live_in[i]: + self._live_in[i].remove(old_name) + self._live_out[i].add(new_name) + if old_name in self._live_out[i]: + self._live_out[i].remove(old_name) + self._live_out[i].add(new_name) + def _reach_fixed_point(self, live_in, live_out): if len(live_in) != len(self._live_in): return False @@ -79,30 +105,45 @@ class ControlFlowGraph(object): self.pool = [] for i in range(self.op_size): if self.pool: - out_pair = [(x, self.global_block.var(str(x)).shape()) + out_pair = [(x, self.global_block_desc.var(str(x)).shape()) for x in self._defs[i]] for x, x_shape in out_pair: - for index, cache_pair in enumerate(self.pool): - cache_var = cache_pair[0] - cache_shape = cache_pair[1] - if x_shape == cache_shape: - print( - "Hit Cache !!!! cache pool index is %d, var name is %s, cached var name is %s, var shape is %s " - % (index, x, cache_var, str(cache_shape))) - self.pool.pop(index) - _rename_arg_(self.ops, x, cache_var, begin_idx=i) - self._dataflow_analyze() - break + if not self.global_block_desc.var(str(x)).persistable(): + for index, cache_pair in enumerate(self.pool): + cache_var = cache_pair[0] + cache_shape = cache_pair[1] + if x_shape == cache_shape: + x_dtype = self.global_block_desc.var(str( + x)).dtype() + cache_dtype = self.global_block_desc.var( + str(cache_var)).dtype() + # TODO(qijun): actually, we should compare dtype_to_size[x_dtype] + # and dtype_to_size[cache_dtype] + if x_dtype == cache_dtype: + print( + "Hit Cache !!!! cache pool index is %d, var name is %s, cached var name is %s, var shape is %s " + % + (index, x, cache_var, str(cache_shape))) + self.pool.pop(index) + _rename_arg_( + self.ops, x, cache_var, begin_idx=i) + self._program.current_block().var(str( + x)).desc = self.global_block_desc.var( + str(cache_var)) + self._update_graph( + x, cache_var, begin_idx=i) + break in_diff, out_diff = self._get_diff(self._live_in[i], self._live_out[i]) can_optimize = filter( - lambda x: not self.global_block.var(str(x)).persistable(), + lambda x: not self.global_block_desc.var(str(x)).persistable(), in_diff) if can_optimize: for var_name in can_optimize: - self.pool.append(( - var_name, self.global_block.var(str(var_name)).shape())) + self.pool.append( + (var_name, + self.global_block_desc.var(str(var_name)).shape())) def get_program(self): return self._program -- GitLab From 24cde57ca0edd9b734ab7ea9fc0c077bb76567b6 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Thu, 11 Jan 2018 11:26:10 +0800 Subject: [PATCH 0204/2305] Extend return value for layer functions Make users can access parameters of layers and their gradients. --- doc/design/python_api.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/doc/design/python_api.md b/doc/design/python_api.md index cb5fdc765b7..73f6d7b90c7 100644 --- a/doc/design/python_api.md +++ b/doc/design/python_api.md @@ -279,6 +279,26 @@ class LayerHelper(object): return tmp ``` +### Return value of layer functions + +The layer will return a Variable, which is also the output of an operator. However, outputs of a layer function have more attributes than an operator. There are parameter variables, and their gradient variables need to return. To return them is useful. For example, + +1. Users can debug the network by printing parameter gradients. +2. Users can append attributes to a parameter, such as, `param.stop_gradient=True` will make a parameter stop generate the gradient. We can fix the parameter value during training by using this attribute. + +However, it is good to return a Variable for layers, since all layers and operators use Variables as their parameters. We can just append a `param` field and a `grad` field for layer function since the Python is dynamic typing. + +The sample usage is + +```python +data = fluid.layers.data(...) +hidden = fluid.layers.fc(data, ...) +... + +executor.run(fetch_list=[hidden.param, hidden.param.grad], ...) +``` + + ## Optimizer [Optimizer Design Doc](./optimizer.md) -- GitLab From fb62f8cb0fcf359aec6f4d4265e6d13f0d1c2b35 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Thu, 11 Jan 2018 11:52:55 +0800 Subject: [PATCH 0205/2305] Add python api for warp-ctc op --- python/paddle/v2/fluid/layers/nn.py | 65 +++++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 48a6bee5588..97056570ee1 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -14,7 +14,7 @@ __all__ = [ 'chunk_eval', 'sequence_conv', 'conv2d', 'sequence_pool', 'pool2d', 'batch_norm', 'beam_search_decode', 'conv2d_transpose', 'sequence_expand', 'lstm_unit', 'reduce_sum', 'reduce_mean', 'reduce_max', 'reduce_min', - 'sequence_first_step', 'sequence_last_step', 'dropout' + 'sequence_first_step', 'sequence_last_step', 'dropout', 'warpctc' ] @@ -248,13 +248,13 @@ def gru_unit(input, h_t & = dot((1-u_t), m_t) + dot(u_t, h_{t-1}) The inputs of gru unit includes :math:`z_t`, :math:`h_{t-1}`. In terms - of the equation above, the :math:`z_t` is split into 3 parts - - :math:`xu_t`, :math:`xr_t` and :math:`xm_t`. This means that in order to - implement a full GRU unit operator for an input, a fully + of the equation above, the :math:`z_t` is split into 3 parts - + :math:`xu_t`, :math:`xr_t` and :math:`xm_t`. This means that in order to + implement a full GRU unit operator for an input, a fully connected layer has to be applied, such that :math:`z_t = W_{fc}x_t`. - The terms :math:`u_t` and :math:`r_t` represent the update and reset gates - of the GRU cell. Unlike LSTM, GRU has one lesser gate. However, there is + The terms :math:`u_t` and :math:`r_t` represent the update and reset gates + of the GRU cell. Unlike LSTM, GRU has one lesser gate. However, there is an intermediate candidate hidden output, which is denoted by :math:`m_t`. This layer has three outputs :math:`h_t`, :math:`dot(r_t, h_{t-1})` and concatenation of :math:`u_t`, :math:`r_t` and :math:`m_t`. @@ -276,7 +276,7 @@ def gru_unit(input, .. code-block:: python # assuming we have x_t_data and prev_hidden of size=10 - x_t = fluid.layers.fc(input=x_t_data, size=30) + x_t = fluid.layers.fc(input=x_t_data, size=30) hidden_val, r_h_val, gate_val = fluid.layers.gru_unit(input=x_t, hidden = prev_hidden) @@ -1504,3 +1504,54 @@ def reduce_min(input, dim=None, keep_dim=False): 'reduce_all': True if dim == None else False }) return out + + +def warpctc(input, label, blank=0, norm_by_times=False, **kwargs): + """ + An operator integrating the open source warp-ctc library + to compute Connectionist Temporal Classification (CTC) loss. + It can be aliased as softmax with ctc, since a native softmax activation is + interated to the warp-ctc library, to to normlize values for each row of the + input tensor. + + Args: + input(Variable): (LodTensor, default: LoDTensor), + the unscaled probabilities of variable-length sequences, + which is a 2-D Tensor with LoD information. + It's shape is [Lp, num_classes + 1], where Lp is the sum of all input + sequences' length and num_classes is the true number of classes. + (not including the blank label). + label(Variable): (LodTensor, default: LoDTensor), the ground truth + of variable-length sequence, which is a 2-D Tensor with LoD + information. It is of the shape [Lg, 1], where Lg is th sum of + all labels' length. + blank: (int, default: 0), the blank label of Connectionist + Temporal Classification (CTC) loss, which is in the + half-opened interval [0, num_classes + 1). + norm_by_times: (bool, default: false), whether to + normalize the gradients by the number of time-step, + which is also the sequence's length. + + Returns: + Variable: The Connectionist Temporal Classification (CTC) loss, which is a 2-D Tensor of the shape [batch_size, 1]. + + Examples: + .. code-block:: python + + y = layers.data(name='y', shape=[11, 8], dtype='float32', lod_level=1) + y_predict = layers.data(name='y_predict', shape=[11, 1], dtype='float32') + cost = layers.warpctc(input=y_predict, label=y) + + """ + helper = LayerHelper('warpctc', **kwargs) + loss_out = helper.create_tmp_variable(dtype=input.dtype) + grad_out = helper.create_tmp_variable(dtype=input.dtype) + helper.append_op( + type='warpctc', + inputs={'Logits': [input], + 'Label': [label]}, + outputs={'WarpCTCGrad': [grad_out], + 'Loss': [loss_out]}, + attrs={'blank': blank, + 'norm_by_times': norm_by_times}) + return loss_out -- GitLab From 2fd7675cd3d37964a46fbaacd5707945e7c86684 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Thu, 11 Jan 2018 12:01:46 +0800 Subject: [PATCH 0206/2305] Add lod checking for forward. --- .../fluid/tests/test_dynrnn_static_input.py | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py index b68d7ca49b6..657876eb743 100644 --- a/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py +++ b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py @@ -56,7 +56,7 @@ class TestDyRnnStaticInput(unittest.TestCase): ndarray = np.zeros(shape=dims).astype('float32') for i in xrange(np.product(dims)): ndarray.ravel()[i] = lod_tensor.get_float_element(i) - return ndarray + return ndarray, lod_tensor.lod() def build_graph(self, only_forward=False): x_tensor = fluid.layers.data( @@ -142,22 +142,28 @@ class TestDyRnnStaticInput(unittest.TestCase): ] static_step_outs = [] + static_step_lods = [] for i in xrange(self._max_sequence_len): end = len(x_seq_len) - bisect.bisect_left(x_seq_len_sorted, i + 1) - end = sum(static_seq_len_reordered[:end]) + lod = [0] + for i in xrange(end): + lod.append(static_seq_len_reordered[i] + lod[-1]) + static_step_lods.append([lod]) + end = lod[-1] static_step_outs.append( np.array(static_reordered[:end]).astype('float32')) - return static_step_outs + return static_step_outs, static_step_lods def test_step_out(self): static_step_outs = self.build_graph(only_forward=True) self.exe.run(framework.default_startup_program()) - expected_step_outs = self.get_expected_static_step_outs() + expected_outs, expected_lods = self.get_expected_static_step_outs() for i in xrange(self._max_sequence_len): - step_out = self.fetch_value(static_step_outs[i]) - self.assertTrue(np.allclose(step_out, expected_step_outs[i])) + step_out, lod = self.fetch_value(static_step_outs[i]) + self.assertTrue(np.allclose(step_out, expected_outs[i])) + self.assertTrue(np.allclose(lod, expected_lods[i])) def test_network_gradient(self): pass #still have bug (seed doesn't work) @@ -165,11 +171,10 @@ class TestDyRnnStaticInput(unittest.TestCase): static_input_grad, loss = self.build_graph() self.exe.run(framework.default_startup_program()) - actual_gradients = self.fetch_value(static_input_grad) + actual_gradients, actual_lod = self.fetch_value(static_input_grad) static_input_shape = self.static_input_tensor.get_dims() numeric_gradients = np.zeros(shape=static_input_shape).astype('float32') - #print(actual_gradient) print(actual_gradients) # calculate numeric gradients tensor_size = np.product(static_input_shape) @@ -177,13 +182,12 @@ class TestDyRnnStaticInput(unittest.TestCase): origin = self.static_input_tensor.get_float_element(i) x_pos = origin + self._delta self.static_input_tensor.set_float_element(i, x_pos) - y_pos = self.fetch_value(loss)[0] + y_pos = self.fetch_value(loss)[0][0] x_neg = origin - self._delta self.static_input_tensor.set_float_element(i, x_neg) - y_neg = self.fetch_value(loss)[0] + y_neg = self.fetch_value(loss)[0][0] self.static_input_tensor.set_float_element(i, origin) numeric_gradients.ravel()[i] = (y_pos - y_neg) / self._delta / 2 - print(numeric_gradients) ''' -- GitLab From fd24e195a3e977cf004d4987c70b9acf5049b37d Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Thu, 11 Jan 2018 12:06:59 +0800 Subject: [PATCH 0207/2305] Uncomment check output in unitest --- python/paddle/v2/fluid/tests/test_warpctc_op.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_warpctc_op.py b/python/paddle/v2/fluid/tests/test_warpctc_op.py index 6496b55031e..a1c4e40111f 100644 --- a/python/paddle/v2/fluid/tests/test_warpctc_op.py +++ b/python/paddle/v2/fluid/tests/test_warpctc_op.py @@ -178,21 +178,23 @@ class TestWarpCTCOp(OpTest): for i in range(batch_size): max_sequence_length = max(max_sequence_length, logits_lod[0][i + 1] - logits_lod[0][i]) - gradient = np.zeros( + self.gradient = np.zeros( [max_sequence_length, batch_size, num_classes], dtype="float32") self.inputs = { "Logits": (logits, logits_lod), "Label": (labels, labels_lod) } - self.outputs = {"Loss": loss, "WarpCTCGrad": gradient} + self.outputs = {"Loss": loss} self.attrs = {"blank": blank, "norm_by_times": norm_by_times} -# def test_check_output(self): -# self.check_output() + def test_check_output(self): + self.check_output() def test_check_grad(self): + self.outputs['WarpCTCGrad'] = self.gradient self.check_grad(["Logits"], "Loss", max_relative_error=0.01) + if __name__ == "__main__": unittest.main() -- GitLab From 74c5e7c52d8910e79038e549872ba6a83d927deb Mon Sep 17 00:00:00 2001 From: tensor-tang Date: Wed, 10 Jan 2018 22:08:00 +0800 Subject: [PATCH 0208/2305] enbale auto set env of V1 on Mac --- paddle/scripts/submit_local.sh.in | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/paddle/scripts/submit_local.sh.in b/paddle/scripts/submit_local.sh.in index bb47ad614ed..80fa0c72af6 100755 --- a/paddle/scripts/submit_local.sh.in +++ b/paddle/scripts/submit_local.sh.in @@ -49,7 +49,18 @@ function cpu_config() { if [ "@WITH_MKL@" == "OFF" ]; then return 0 fi - ht=`lscpu |grep "per core"|awk -F':' '{print $2}'|xargs` + platform="`uname -s`" + ht=0 + if [ $platform == "Linux" ]; then + ht=`lscpu |grep "per core"|awk -F':' '{print $2}'|xargs` + elif [ $platform == "Darwin" ]; then + if [`sysctl -n hw.physicalcpu` -eq `sysctl -n hw.logicalcpu`]; then + # HT is OFF + ht=1 + fi + else + return 0 + fi if [ $ht -eq 1 ]; then # HT is OFF if [ -z "$KMP_AFFINITY" ]; then export KMP_AFFINITY="granularity=fine,compact,0,0" @@ -72,7 +83,15 @@ function threads_config() { # according to trainer_count and total processors # only when MKL enabled # auto set OPENBLAS_NUM_THREADS when do not use MKL - processors=`grep "processor" /proc/cpuinfo|sort -u|wc -l` + platform="`uname -s`" + processors=0 + if [ $platform == "Linux" ]; then + processors=`grep "processor" /proc/cpuinfo|sort -u|wc -l` + elif [ $platform == "Darwin" ]; then + processors=`sysctl -n hw.logicalcpu` + else + return 0 + fi trainers=`grep -Eo 'trainer_count.[0-9]+' <<< "$@" |grep -Eo '[0-9]+'|xargs` if [ -z $trainers ]; then trainers=1 @@ -148,11 +167,7 @@ else: sys.exit(0) EOF -if [ "`uname -s`" == "Linux" ]; then - # only support on linux yet, with mac can use v2 - cpu_config -fi - +cpu_config # echo $KMP_AFFINITY $OMP_DYNAMIC case "$1" in -- GitLab From 83c72536e662b5e8be7eb1a3e69a4f02ac584b20 Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Thu, 11 Jan 2018 12:35:18 +0800 Subject: [PATCH 0209/2305] Update batch_size --- python/paddle/v2/fluid/tests/test_parallel_op.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index dde7206e4fd..2b51a1f5047 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -147,7 +147,8 @@ class ParallelOpTest(BaseParallelForTest): self.run_test( callback=__network__, feed={ - 'img': numpy.random.random(size=(128, 784)).astype('float32') + 'img': + numpy.random.random(size=(128 * 3, 784)).astype('float32') }, fetch='fc1.w@GRAD') -- GitLab From a795a0d743de776e0008e21ae7266690f989971d Mon Sep 17 00:00:00 2001 From: Abhinav Arora Date: Wed, 10 Jan 2018 20:48:01 -0800 Subject: [PATCH 0210/2305] Refine the document for memory optimization (#7420) --- doc/design/memory_optimization.md | 32 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/design/memory_optimization.md b/doc/design/memory_optimization.md index 00f514711a4..1f68cef4cc2 100644 --- a/doc/design/memory_optimization.md +++ b/doc/design/memory_optimization.md @@ -5,28 +5,28 @@ In a lecture from Andrew Ng, he attributes the recent sucess of AI due to a combination of these: -- availability of Big Data -- supercomputing power to process this Big Data over very large neural networks -- modern algorithms +- Availability of Big Data +- Supercomputing power to process this Big Data over very large neural networks +- Modern algorithms Following graph shows the details: ![](images/deep_learning.png) -Larger model usually brings better performance. However, GPU memory is certain limited. For example, the memory size of a GTX TITAN X is only 12GB. To train complex and large model, we have to take care of memory using. Besides, memory optimization is also necessary in both online/mobile inference. +Larger model usually bring better performance. However, GPU memory is limited. For example, the memory size of a GTX TITAN X is only 12GB. To train complex and large models, we have to take care of memory usage. Besides, memory optimization is also necessary in both online/mobile inference. ## Solution ### Basic Strategy -There are some basic strategies to make memory optimization, including in-place operation and memory sharing. +There are some basic strategies to improve memory usage, including in-place operations and memory sharing. #### In-place Operation In a relu activation operator: $y = \max(x, 0)$ -If the variable x is not used in any other operator, we can make an in-place operation. In other words, the memory block of variable y and variable x are the same. In-place operation will save 50% memory occupancy immediately. +If the variable x is not used in any other operator, we can make an in-place operation. In other words, the memory block of variable y and variable x will be the same. In-place operations will save 50% memory occupancy immediately. #### Memory Sharing @@ -40,18 +40,18 @@ d = op2(a) e = op3(d, f) ``` -In this case, variable a is no longer used, and op2 does not support in-place operation. After op2 finished, we can put the memory of variable a to a memory pool. Then, variable e can share the memory of variable a from the pool. +In this case, variable a is no longer used, and op2 does not support in-place operation. After op2 finishes, we can put the memory of variable a to a memory pool. Then, variable e can share the memory of variable a from the pool. ### Live Variable Analysis -It's not enough to only have some basic strategies. The prerequisite of memory optimization is to know if a variable is still "live" after an operation. +It's not enough to only have some basic strategies. The pre-requisite of memory optimization is to know if a variable is still "live" after an operation. In our design, the neural network topology is defined as a program. Luckily, [live variable analysis](https://en.wikipedia.org/wiki/Live_variable_analysis) is a classic problem in compilers which can be used in many stages, such as register allocation. -In compilers, the front end of the compilers translates programs into an intermediate language with an unbounded number of temporaries. This program must run on a machine with a bounded number of registers. Two temporaries a and b can fit into the same register, if a and b are never "in use" at the same time. Thus, many temporaries can fit in few registers; if they don't all fit, the excess temporaries can be kept in memory. +In compilers, the front end of the compiler translates programs into an intermediate language with an unbounded number of temporary variables. This program must run on a machine with a bounded number of registers. Two temporary variables a and b can fit into the same register, if a and b are never "in use" at the same time. Thus, many temporary variables can fit in few registers; if they don't all fit, the excess tempory variables can be kept in memory. -Therefore, the compiler needs to analyze the intermediate-representation program to determine which temporaries are in use at the same time. We say a variable is "live" if it holds a value that may be needed in the future, so this analysis is called liveness analysis. +Therefore, the compiler needs to analyze the intermediate-representation program to determine which temporary variables are in use at the same time. We say a variable is "live" if it holds a value that may be needed in the future, so this analysis is called liveness analysis. We can leran these techniques from compilers. There are mainly two stages to make live variable analysis: @@ -60,7 +60,7 @@ We can leran these techniques from compilers. There are mainly two stages to mak #### Control Flow Graph -To preform analyses on a program, it is often useful to make a control flow graph. A [control flow graph](https://en.wikipedia.org/wiki/Control_flow_graph) (CFG) in computer science is a representation, using graph notation, of all paths that might be traversed through a program during its execution. Each statement in the program is a node in the flow graph; if statemment x can be followed by statement y, there is an egde from x to y. +To perform analysis on a program, it is often useful to make a control flow graph. A [control flow graph](https://en.wikipedia.org/wiki/Control_flow_graph) (CFG) in computer science is a representation, using graph notation, of all paths that might be traversed through a program during its execution. Each statement in the program is a node in the flow graph; if statemment x can be followed by statement y, there is an egde from x to y. Following is the flow graph for a simple loop. @@ -68,18 +68,18 @@ Following is the flow graph for a simple loop. #### Dataflow Analysis -liveness of variable "flows" around the edges of the control flow graph; determining the live range of each variable is an example of a dataflow problem. [Dataflow analysis](https://en.wikipedia.org/wiki/Data-flow_analysis) is a technique for gathering information about the possible set of values calculated at various points in a computer program. +Liveness of variable "flows" around the edges of the control flow graph; determining the live range of each variable is an example of a dataflow problem. [Dataflow analysis](https://en.wikipedia.org/wiki/Data-flow_analysis) is a technique for gathering information about the possible set of values calculated at various points in a computer program. A simple way to perform data-flow analysis of programs is to set up dataflow equations for each node of the control flow graph and solve them by repeatedly calculating the output from the input locally at each node until the whole system stabilizes. - Flow Graph Terminology -A flow graph node has out-edges that lead to sucessor nodes, and in-edges that come from presucessor nodes. The set *pred[n]* is all the predecessors of node n, and *succ[n]* is the set of sucessors. +A flow graph node has out-edges that lead to sucessor nodes, and in-edges that come from predecessor nodes. The set *pred[n]* is all the predecessors of node n, and *succ[n]* is the set of sucessors. In former control flow graph, the out-edges of node 5 are 5 --> 6 and 5 --> 2, and *succ[5]* = {2, 6}. The in-edges of 2 are 5 --> 2 and 1 --> 2, and *pred[2]* = {1, 5}. - Uses and Defs -An assignmemt to a variable or temporary defines that variable. An occurence of a variable on the right-hand side of an assginment(or in other expressions) uses the variable. We can speak the *def* of a variable as the set of graph nodes that define it; or the *def* of a graph node as the set of variables that it defines; and the similarly for the *use* of a variable or graph node. In former control flow graph, *def(3)* = {c}, *use(3)* = {b, c}. +An assignmemt to a variable or temporary defines that variable. An occurence of a variable on the right-hand side of an assginment(or in other expressions) uses the variable. We can define the *def* of a variable as the set of graph nodes that define it; or the *def* of a graph node as the set of variables that it defines; and the similarly for the *use* of a variable or graph node. In former control flow graph, *def(3)* = {c}, *use(3)* = {b, c}. - Liveness @@ -168,9 +168,9 @@ class ControlFlowGraph(object): return self._program ``` -#### make dataflow analysis +#### Make dataflow analysis -We follow guide from compilers and try to solve the dataflow equation to get liveness of every variable. If the live-in of an operator node is different from the live-out, then we can make memory sharing. +We follow the guide from compilers and try to solve the dataflow equation to get liveness of every variable. If the live-in of an operator node is different from the live-out, then we can make memory sharing. For example: -- GitLab From f784dae3da58788da3117f8ea825f16552f046db Mon Sep 17 00:00:00 2001 From: Abhinav Arora Date: Wed, 10 Jan 2018 21:28:26 -0800 Subject: [PATCH 0211/2305] Fix the documentation for elementwise op in fluid layers --- doc/api/v2/fluid/layers.rst | 10 ++++++++++ paddle/operators/elementwise_add_op.cc | 2 +- paddle/operators/elementwise_div_op.cc | 2 +- paddle/operators/elementwise_mul_op.cc | 2 +- paddle/operators/elementwise_op.h | 20 +++++++++++--------- paddle/operators/elementwise_sub_op.cc | 2 +- 6 files changed, 25 insertions(+), 13 deletions(-) diff --git a/doc/api/v2/fluid/layers.rst b/doc/api/v2/fluid/layers.rst index a7c8670f66c..696a8012aa4 100644 --- a/doc/api/v2/fluid/layers.rst +++ b/doc/api/v2/fluid/layers.rst @@ -38,6 +38,16 @@ elementwise_add .. autofunction:: paddle.v2.fluid.layers.elementwise_add :noindex: +elementwise_sub +--------------- +.. autofunction:: paddle.v2.fluid.layers.elementwise_sub + :noindex: + +elementwise_mul +--------------- +.. autofunction:: paddle.v2.fluid.layers.elementwise_mul + :noindex: + elementwise_div --------------- .. autofunction:: paddle.v2.fluid.layers.elementwise_div diff --git a/paddle/operators/elementwise_add_op.cc b/paddle/operators/elementwise_add_op.cc index 70b7c9f2ec1..37951fa7587 100644 --- a/paddle/operators/elementwise_add_op.cc +++ b/paddle/operators/elementwise_add_op.cc @@ -21,7 +21,7 @@ class ElementwiseAddOpMaker : public ElementwiseOpMaker { public: ElementwiseAddOpMaker(OpProto* proto, OpAttrChecker* op_checker) : ElementwiseOpMaker(proto, op_checker) { - SetComment("Add", "$Out = X + Y$"); + SetComment("Add", "Out = X + Y"); AddComment(comment_); } }; diff --git a/paddle/operators/elementwise_div_op.cc b/paddle/operators/elementwise_div_op.cc index 1fa960866fa..6ebd58b1b3d 100644 --- a/paddle/operators/elementwise_div_op.cc +++ b/paddle/operators/elementwise_div_op.cc @@ -21,7 +21,7 @@ class ElementwiseDivOpMaker : public ElementwiseOpMaker { public: ElementwiseDivOpMaker(OpProto* proto, OpAttrChecker* op_checker) : ElementwiseOpMaker(proto, op_checker) { - SetComment("Div", "$Out = X / Y$"); + SetComment("Div", "Out = X / Y"); AddComment(comment_); } }; diff --git a/paddle/operators/elementwise_mul_op.cc b/paddle/operators/elementwise_mul_op.cc index a6d11736194..450dd05c796 100644 --- a/paddle/operators/elementwise_mul_op.cc +++ b/paddle/operators/elementwise_mul_op.cc @@ -22,7 +22,7 @@ class ElementwiseMulOpMaker : public ElementwiseOpMaker { public: ElementwiseMulOpMaker(OpProto* proto, OpAttrChecker* op_checker) : ElementwiseOpMaker(proto, op_checker) { - SetComment("Mul", "$Out = X \\odot\\ Y$"); + SetComment("Mul", "Out = X \\odot\\ Y"); AddComment(comment_); } }; diff --git a/paddle/operators/elementwise_op.h b/paddle/operators/elementwise_op.h index f308ee05e11..a342595b546 100644 --- a/paddle/operators/elementwise_op.h +++ b/paddle/operators/elementwise_op.h @@ -58,7 +58,8 @@ Limited Elementwise {name} Operator. The equation is: -{equation} +.. math:: + {equation} X is a tensor of any dimension and the dimensions of tensor Y must be smaller than or equal to the dimensions of X. @@ -71,15 +72,16 @@ For case 2: Y will be broadcasted to match the shape of X and axis should be the starting dimension index for broadcasting Y onto X. -example: - shape(X) = (2, 3, 4, 5), shape(Y) = (,) - shape(X) = (2, 3, 4, 5), shape(Y) = (5,) - shape(X) = (2, 3, 4, 5), shape(Y) = (4, 5) - shape(X) = (2, 3, 4, 5), shape(Y) = (3, 4), with axis=1 - shape(X) = (2, 3, 4, 5), shape(Y) = (2), with axis=0 +For example + .. code-block:: python -Both the input X and Y can carry the LoD (Level of Details) information, -or not. But the output only shares the LoD information with input X. + shape(X) = (2, 3, 4, 5), shape(Y) = (,) + shape(X) = (2, 3, 4, 5), shape(Y) = (5,) + shape(X) = (2, 3, 4, 5), shape(Y) = (4, 5) + shape(X) = (2, 3, 4, 5), shape(Y) = (3, 4), with axis=1 + shape(X) = (2, 3, 4, 5), shape(Y) = (2), with axis=0 + +Either of the inputs X and Y or none can carry the LoD (Level of Details) information. However, the output only shares the LoD information with input X. )DOC"; AddComment(comment_); diff --git a/paddle/operators/elementwise_sub_op.cc b/paddle/operators/elementwise_sub_op.cc index 2a8d0845b18..d3c51f0a697 100644 --- a/paddle/operators/elementwise_sub_op.cc +++ b/paddle/operators/elementwise_sub_op.cc @@ -21,7 +21,7 @@ class ElementwiseSubOpMaker : public ElementwiseOpMaker { public: ElementwiseSubOpMaker(OpProto* proto, OpAttrChecker* op_checker) : ElementwiseOpMaker(proto, op_checker) { - SetComment("Sub", "$Out = X - Y$"); + SetComment("Sub", "Out = X - Y"); AddComment(comment_); } }; -- GitLab From ed0a564c909353c862bcb1533e41420fdd87eb9e Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Thu, 11 Jan 2018 13:42:40 +0800 Subject: [PATCH 0212/2305] Optimize GemmConvMobileFunction. --- paddle/function/GemmConvOp.cpp | 63 +++++++++++++++++++--------------- paddle/function/Im2Col.h | 53 ++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 28 deletions(-) diff --git a/paddle/function/GemmConvOp.cpp b/paddle/function/GemmConvOp.cpp index cbdbf5335d3..a9876cec2aa 100644 --- a/paddle/function/GemmConvOp.cpp +++ b/paddle/function/GemmConvOp.cpp @@ -178,19 +178,22 @@ public: real* inputData = inputs[0].data(); real* filterData = inputs[1].data(); real* outputData = outputs[0].data(); + real* colData = NULL; bool needIm2col = isNeedIm2col(filter); TensorShape imShape = TensorShape({inputChannels / groups_, inputHeight, inputWidth}); - TensorShape colShape; - real* colData = NULL; - size_t colHeight = inputChannels / groups_ * filterHeight * filterWidth; - size_t colWidth = outputHeight * outputWidth; - // Max col matrix height 256, Max col matrix width 1024 - size_t stepColHeight = std::min(colHeight, static_cast(256)); - size_t stepColWidth = std::min(colWidth, static_cast(2048)); + // Max col matrix width 4096, Max col matrix size 4M. + size_t outputHeightSteps = + std::min(std::max(4096 / outputWidth, (size_t)1), outputHeight); + size_t maxColWidth = outputHeightSteps * outputWidth; + size_t channelSteps = + std::min(std::max((1048576 / maxColWidth) / filterHeight * filterWidth, + (size_t)1), + inputChannels / groups_); + size_t maxColHeight = channelSteps * filterHeight * filterWidth; if (needIm2col) { colShape = TensorShape({inputChannels / groups_, @@ -199,7 +202,7 @@ public: outputHeight, outputWidth}); - resizeBuffer(stepColHeight * stepColWidth * sizeof(real)); + resizeBuffer(maxColHeight * maxColWidth * sizeof(real)); colData = reinterpret_cast(memory_->getBuf()); } @@ -209,20 +212,24 @@ public: (outputChannels / groups_) * outputHeight * outputWidth; size_t filterOffset = filter.getElements() / groups_; - int nStride = colWidth; - int kStride = colHeight; + int nStride = outputHeight * outputWidth; + int kStride = inputChannels / groups_ * filterHeight * filterWidth; for (size_t i = 0; i < batchSize; i++) { + filterData = inputs[1].data(); for (size_t g = 0; g < groups_; g++) { if (needIm2col) { real beta_ = beta; - for (size_t colHeightStart = 0; colHeightStart < colHeight; - colHeightStart += stepColHeight) { - for (size_t colWidthStart = 0; colWidthStart < colWidth; - colWidthStart += stepColWidth) { - int N = std::min(colWidth - colWidthStart, stepColWidth); - int K = std::min(colHeight - colHeightStart, stepColHeight); + for (size_t ic = 0; ic < inputChannels / groups_; + ic += channelSteps) { + int channels = std::min(inputChannels / groups_ - ic, channelSteps); + for (size_t oh = 0; oh < outputHeight; oh += outputHeightSteps) { + int height = std::min(outputHeight - oh, outputHeightSteps); + + int M = outputChannels / groups_; + int N = height * outputWidth; + int K = channels * filterHeight * filterWidth; // im2col - im2col(inputData + g * inputOffset, + im2col(inputData, imShape, colData, colShape, @@ -232,13 +239,12 @@ public: paddingW(), dilationH(), dilationW(), - colHeightStart, - K, - colWidthStart, + channels, + oh, + height, N); // gemm - int M = outputChannels / groups_; BlasGemm::compute( false, false, @@ -246,12 +252,12 @@ public: N, K, 1.0f, - filterData + g * filterOffset + colHeightStart, + filterData + ic * filterHeight * filterWidth, kStride, colData, N, beta_, - outputData + g * outputOffset + colWidthStart, + outputData + oh * outputWidth, nStride); } beta_ = 1.0; @@ -266,17 +272,18 @@ public: N, K, 1.0f, - filterData + g * filterOffset, + filterData, K, - inputData + g * inputOffset, + inputData, N, beta, - outputData + g * outputOffset, + outputData, N); } + inputData += inputOffset; + outputData += outputOffset; + filterData += filterOffset; } - inputData += inputChannels * inputHeight * inputWidth; - outputData += outputChannels * outputHeight * outputWidth; } memory_.reset(); diff --git a/paddle/function/Im2Col.h b/paddle/function/Im2Col.h index 36a9bcf84e4..361ba4c18a2 100644 --- a/paddle/function/Im2Col.h +++ b/paddle/function/Im2Col.h @@ -98,6 +98,7 @@ public: int dilationWidth = 1); }; +#if 0 template class Im2ColMobileFunctor { public: @@ -147,5 +148,57 @@ public: } } }; +#endif + +template +class Im2ColMobileFunctor { +public: + void operator()(const T* imData, + const TensorShape& imShape, + T* colData, + const TensorShape& colShape, + int strideHeight, + int strideWidth, + int paddingHeight, + int paddingWidth, + int dilationHeight, + int dilationWidth, + int inputChannels, + int colOffset, + int colOutputHeight, + int colWidth) { + int inputHeight = imShape[1]; + int inputWidth = imShape[2]; + int filterHeight = colShape[1]; + int filterWidth = colShape[2]; + int outputWidth = colShape[4]; + + for (int ic = 0; ic < inputChannels; ic++) { + for (int oh = 0; oh < colOutputHeight; oh++) { + T* dstData = colData + oh * outputWidth; + for (int fh = 0; fh < filterHeight; fh++) { + for (int fw = 0; fw < filterWidth; fw++) { + int imRowIdx = (oh + colOffset) * strideHeight + fh - paddingHeight; + if (imRowIdx < 0 || imRowIdx >= inputHeight) { + memset(dstData, 0, outputWidth * sizeof(T)); + } else { + for (int ow = 0; ow < outputWidth; ow++) { + int imColIdx = ow * strideWidth + fw - paddingWidth; + if (imColIdx < 0 || imColIdx >= inputWidth) { + dstData[ow] = T(0); + } else { + dstData[ow] = imData[imRowIdx * inputWidth + imColIdx]; + } + } + } + dstData += colWidth; + } + } + } + colData += filterHeight * filterWidth * colWidth; + imData += inputHeight * inputWidth; + } + } +}; } // namespace paddle -- GitLab From 784e59406c541def813e75db5fd11bb0361eccef Mon Sep 17 00:00:00 2001 From: hedaoyuan Date: Thu, 11 Jan 2018 14:02:57 +0800 Subject: [PATCH 0213/2305] Bug fix of Im2ColMobileFunctor. --- paddle/function/Im2Col.h | 58 +++------------------------------- paddle/function/Im2ColTest.cpp | 6 ++-- 2 files changed, 7 insertions(+), 57 deletions(-) diff --git a/paddle/function/Im2Col.h b/paddle/function/Im2Col.h index 361ba4c18a2..915119e291c 100644 --- a/paddle/function/Im2Col.h +++ b/paddle/function/Im2Col.h @@ -98,58 +98,6 @@ public: int dilationWidth = 1); }; -#if 0 -template -class Im2ColMobileFunctor { -public: - void operator()(const T* imData, - const TensorShape& imShape, - T* colData, - const TensorShape& colShape, - int strideHeight, - int strideWidth, - int paddingHeight, - int paddingWidth, - int dilationHeight, - int dilationWidth, - int colHeightStart, - int colHeightSize, - int colWidthStart, - int colWidthSize) { - int inputHeight = imShape[1]; - int inputWidth = imShape[2]; - int filterHeight = colShape[1]; - int filterWidth = colShape[2]; - int outputWidth = colShape[4]; - - for (int colh = 0; colh < colHeightSize; colh++) { - int wOffset = (colHeightStart + colh) % filterWidth; - int hOffset = ((colHeightStart + colh) / filterWidth) % filterHeight; - int c_im = (colHeightStart + colh) / filterWidth / filterHeight; - - for (int colw = 0; colw < colWidthSize; colw++) { - int h = (colWidthStart + colw) / outputWidth; - int w = (colWidthStart + colw) % outputWidth; - - int imRowIdx = h * strideHeight + hOffset * dilationHeight; - int imColIdx = w * strideWidth + wOffset * dilationWidth; - if ((imRowIdx - paddingHeight) < 0 || - (imRowIdx - paddingHeight) >= inputHeight || - (imColIdx - paddingWidth) < 0 || - (imColIdx - paddingWidth) >= inputWidth) { - colData[colh * colWidthSize + colw] = static_cast(0); - } else { - imRowIdx += c_im * inputHeight - paddingHeight; - imColIdx -= paddingWidth; - colData[colh * colWidthSize + colw] = - imData[imRowIdx * inputWidth + imColIdx]; - } - } - } - } -}; -#endif - template class Im2ColMobileFunctor { public: @@ -178,12 +126,14 @@ public: T* dstData = colData + oh * outputWidth; for (int fh = 0; fh < filterHeight; fh++) { for (int fw = 0; fw < filterWidth; fw++) { - int imRowIdx = (oh + colOffset) * strideHeight + fh - paddingHeight; + int imRowIdx = (oh + colOffset) * strideHeight + + fh * dilationHeight - paddingHeight; if (imRowIdx < 0 || imRowIdx >= inputHeight) { memset(dstData, 0, outputWidth * sizeof(T)); } else { for (int ow = 0; ow < outputWidth; ow++) { - int imColIdx = ow * strideWidth + fw - paddingWidth; + int imColIdx = + ow * strideWidth + fw * dilationWidth - paddingWidth; if (imColIdx < 0 || imColIdx >= inputWidth) { dstData[ow] = T(0); } else { diff --git a/paddle/function/Im2ColTest.cpp b/paddle/function/Im2ColTest.cpp index 3ba866dcdd8..fe44a8bf790 100644 --- a/paddle/function/Im2ColTest.cpp +++ b/paddle/function/Im2ColTest.cpp @@ -202,10 +202,10 @@ void TestIm2ColMobileFunctor() { padding, dilation, dilation, + channels, 0, - height, - 0, - width); + outputHeight, + outputHeight * outputWidth); autotest::TensorCheckEqual(*output1, *output2); } -- GitLab From e71e4a3d134aee08016ce638c47f47fd332a271b Mon Sep 17 00:00:00 2001 From: "Yang Yang(Tony)" Date: Thu, 11 Jan 2018 14:44:09 +0800 Subject: [PATCH 0214/2305] Update read_source.md (#7406) --- doc/howto/read_source.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/howto/read_source.md b/doc/howto/read_source.md index e4211abb3be..31987920f32 100644 --- a/doc/howto/read_source.md +++ b/doc/howto/read_source.md @@ -26,16 +26,16 @@ sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.001) sgd_optimizer.minimize(avg_cost) ``` -- Variables: `x`, `y`, `y_predict`, `cost` and `avg_cost`. [Python](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/fluid/framework.py#L93) -- Layers: `fluid.layers.data`, `fluid.layers.fc` and `fluid.layers.mean` are layers. [Python](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/fluid/layers.py) +- Variables: `x`, `y`, `y_predict`, `cost` and `avg_cost`. [Python](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/fluid/framework.py#) +- Layers: `fluid.layers.data`, `fluid.layers.fc` and `fluid.layers.mean` are layers. [Python](https://github.com/PaddlePaddle/Paddle/tree/develop/python/paddle/v2/fluid/layers) - Every Layer has one or more operators and variables/parameters - All the operators are defined at [`paddle/operators/`](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/operators). Other worth-looking files: - Base class: [`paddle/framework/operator.h`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/operator.h) - Operator Registration: [`paddle/framework/op_registry.h`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/op_registry.h) - Operator Lookup: [`paddle/framework/op_info.h`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/op_info.h) - Optimizer: `fluid.optimizer.SGD`. It does the following - - Add backward operators. [[Python](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/fluid/backward.py), [C++](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/framework/backward.cc)] - - Add optimizer operators. [[Python](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/fluid/optimizer.py), [C++](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/optimizer)] + - Add backward operators. [[Python](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/fluid/backward.py)] + - Add optimizer operators. [[Python](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/fluid/optimizer.py)] # Run Time -- GitLab From 9867a3796e2460e0cee899926974ea7e8bac590c Mon Sep 17 00:00:00 2001 From: kexinzhao Date: Wed, 10 Jan 2018 23:39:25 -0800 Subject: [PATCH 0215/2305] refine op_kernel_type doc (#7448) --- doc/design/operator_kernel_type.md | 32 +++++++++++++++--------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/design/operator_kernel_type.md b/doc/design/operator_kernel_type.md index aa82e96bf79..f86e6b7a564 100644 --- a/doc/design/operator_kernel_type.md +++ b/doc/design/operator_kernel_type.md @@ -1,6 +1,6 @@ # Design Doc: The Keys of Operator Kernel Type ## Problem -An operator can have different kernel implementations, and each operator will have a map to store the related kernels. Fluid uses `OpKernelType` as a key to identify a unique Kernel. Before an operator runs, an certain kernel must be chosen by a key of `OpKernelType`. Currently, `OpKernelType` is defined as follows: +An operator can have different kernel implementations, and each operator will have a map to store the related kernels. Fluid uses `OpKernelType` as a key to identify a unique kernel. Before an operator runs, a certain type of kernel must be chosen via a key of `OpKernelType`. Currently, `OpKernelType` is defined as follows: ```cpp struct OpKernelType { @@ -10,13 +10,13 @@ struct OpKernelType { ``` For more details, please refer to [codes](https://github.com/PaddlePaddle/Paddle/blob/2d5ec16bc8a09fb8e0f62c89b116b0cd1d333907/paddle/framework/operator.h#L348-L374) in github. -It contains two keys, `Place` and `DataType`. And these two keys will be hashed to a unique key to represent a certain type of kernel. However, these two keys are not enough. We need a more complete representation of `OpKernelType`. +It contains two keys, `Place` and `DataType`. And these two keys will be hashed to a unique key to represent a certain type of kernel. However, these two keys do not provide enough information. We need a more complete representation of `OpKernelType`. -We often implement a kernel of an operator with some computing library in certain device(place). Please remind that computing library and device are not one-to-one corresponding. A device can have a lot of computing libraries and a computing library can also support several devices. +We often implement a kernel of an operator with some computing library on certain device(place). Please note that computing library and device do not have a one-to-one correspondence. A device can have a lot of computing libraries and a computing library can also support different devices. -For example, Eigen library can support Nvidia GPU/AMD GPU/CPU. And MKLDNN library can support Intel CPU/Intel FPGA. Both `Place` and `Library` should be a key of `OpKernelType`. +For example, Eigen library supports Nvidia GPU/AMD GPU/CPU and MKLDNN library supports Intel CPU/Intel FPGA. Both `Place` and `Library` should be a key of `OpKernelType`. -It's obvious that different DataTypes, like fp64/fp32/int8 will have different kernels. But the data layout of a Tensor will also lead to different implementation. Please refer to the batch norm operator [kernels](https://github.com/PaddlePaddle/Paddle/blob/a948fac4d0ad7e0412d373b8aabeb711c2899563/paddle/operators/batch_norm_op.cc#L180-L209). Data Layout should also be taken into consideration. +Different DataTypes, such as fp64/fp32/int8, will obviously have different kernels. But different data layout of a Tensor will also lead to different implementations. Please refer to the batch norm operator [kernels](https://github.com/PaddlePaddle/Paddle/blob/a948fac4d0ad7e0412d373b8aabeb711c2899563/paddle/operators/batch_norm_op.cc#L180-L209) as an example. Data layout should also be taken into consideration. ## Solution @@ -31,17 +31,17 @@ struct OpKernelType { }; ``` -Following is the details: +The details are as follows: ### Place -`Place` is defined as follows: +`Place` is defined as: ```cpp typedef boost::variant Place; ``` -`Place` is to represent the device memory where data is locating. +`Place` represents the device memory where data is located. ### Library @@ -52,10 +52,10 @@ One operator kernel is usually implemented based on one library. `Library` is de enum Library { Plain, MKLDNN, CUDNN }; ``` -We use `Plain` enumerator to represent default library. Since most operators in Fluid are implemented based on `Eigen` library, we take `Eigen` library as the `Plain` enumerator. -A library usually has a corresponding `DeviceContext` which contains some handles needed by computation. Fluid now have two default DeviceContexts in CPU and CUDA, `CPUDeviceContext` and `CUDADeviceContext`. `CPUDeviceContext` contains a Eigen library handle and `CDUADeviceContext` contains a Eigen library handle and cuBLAS handle. +We use `Plain` enumerator to represent default library. Since most operators in Fluid are implemented based on the `Eigen` library, we take `Eigen` library as the `Plain` enumerator. +A library usually has a corresponding `DeviceContext` which contains some handles needed for computation. Fluid now has two default DeviceContexts for CPU and CUDA, namely, `CPUDeviceContext` and `CUDADeviceContext`. `CPUDeviceContext` contains an Eigen library handle and `CDUADeviceContext` contains an Eigen library handle and a cuBLAS handle. -If we want to support new Library, a new enumerator need to be added to `Library` and a new corresponding `LibraryDeviceContext` will be created. +If we want to support new library, a new enumerator need to be added to `Library` and a corresponding new `LibraryDeviceContext` need to be created. ### DataType @@ -67,15 +67,15 @@ If we want to support new Library, a new enumerator need to be added to `Library Actually, a Tensor is a view of a block of memory. Besides a pointer to the memory, we also have to get some other descriptions of this block of memory, such as shape(ddim), stride, and layout. -Different layout leads to different implementation of operator kernel. There are mainly 4 principles we have to follow to support layout in our fluid framework. +Different layout leads to different implementation of the operator kernel. There are mainly 4 principles we have to follow to support layout in our Fluid framework. -- We take layout as a data member of Tensor. Layout is actually a enum variable. If fluid is built with MKLDNN, then, the memory format in MKLDNN will be added into this enum variable too. +- We take layout as a data member of Tensor. Layout is actually a enum variable. If Fluid is built with MKLDNN, then the memory format in MKLDNN will also be added into this enum variable. -- Users have to set layout for input data. And some operators like fill_constant/random, also have to set layout of generating data. Of course, we can have some default layout, like NCHW. +- Users have to set layout for input data. And some operators like fill_constant/random, also have to set layout for generating data. Of course, we can have some default layout, like NCHW. -- The inference of Layout is at run-time, not compile-time. +- The inference of Layout is at run-time, not at compile-time. -- Every operator have to implement different kernels for different layouts. Let's take MKLDNN as an example, if we want to implement a MKLDNN convolution operator, we have to realize all the kernels for different layout, list at [here](http://01org.github.io/mkl-dnn/structmkldnn_1_1memory.html). And we will have a special macro to do registering kernels for MKLDNN operators. +- Every operator has to implement different kernels for different layouts. Let's take MKLDNN as an example. If we want to implement an MKLDNN convolution operator, we have to implement all the kernels for different layouts, which are listed [here](http://01org.github.io/mkl-dnn/structmkldnn_1_1memory.html). And we will have a special macro to register kernels for MKLDNN operators. `Layout` is also defined as a enum variable: -- GitLab From 7e95793bc6db8962edc47c5b25590f5fa4a9ac44 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 11 Jan 2018 15:46:29 +0800 Subject: [PATCH 0216/2305] =add bim method for generating adversarail sample --- adversarial/advbox/attacks/gradientsign.py | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/adversarial/advbox/attacks/gradientsign.py b/adversarial/advbox/attacks/gradientsign.py index 15b1d176cb1..33fd92e71b7 100644 --- a/adversarial/advbox/attacks/gradientsign.py +++ b/adversarial/advbox/attacks/gradientsign.py @@ -36,3 +36,38 @@ class GradientSignAttack(Attack): FGSM = GradientSignAttack + + +class IteratorGradientSignAttack(Attack): + """ + This attack was originally implemented by Alexey Kurakin(Google Brain). + Paper link: https://arxiv.org/pdf/1607.02533.pdf + """ + def _apply(self, image_label, epsilons=100, steps=10): + """ + Apply the iterative gradient sign attack. + Args: + image_label(list): The image and label tuple list of one element. + epsilons(list|tuple|int): The epsilon (input variation parameter). + steps(int): The number of iterator steps. + Return: + numpy.ndarray: The adversarail sample generated by the algorithm. + """ + assert len(image_label) == 1 + pre_label = np.argmax(self.model.predict(image_label)) + gradient = self.model.gradient(image_label) + min_, max_ = self.model.bounds() + + if not isinstance(epsilons, Iterable): + epsilons = np.linspace(0, 1, num=epsilons + 1) + + for epsilon in epsilons: + adv_img = image_label[0][0].reshape(gradient.shape) + for _ in range(steps): + gradient = self.model.gradient([(adv_img, image_label[0][1])]) + gradient_sign = np.sign(gradient) * (max_ - min_) + adv_img = adv_img + epsilon * gradient_sign + adv_img = np.clip(adv_img, min_, max_) + adv_label = np.argmax(self.model.predict([(adv_img, 0)])) + if pre_label != adv_label: + return adv_img -- GitLab From e5b23785fe67d99fed19d4190070ef5df9e4992d Mon Sep 17 00:00:00 2001 From: Yang Yu Date: Thu, 11 Jan 2018 16:09:49 +0800 Subject: [PATCH 0217/2305] Fix random seed of dynamic rnn gradient check --- .../fluid/tests/test_dynrnn_gradient_check.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_dynrnn_gradient_check.py b/python/paddle/v2/fluid/tests/test_dynrnn_gradient_check.py index c02c59284e1..cdbf582a339 100644 --- a/python/paddle/v2/fluid/tests/test_dynrnn_gradient_check.py +++ b/python/paddle/v2/fluid/tests/test_dynrnn_gradient_check.py @@ -197,7 +197,24 @@ class BaseRNN(object): return numpy.array([o.mean() for o in outs.itervalues()]).mean() -class TestSimpleMul(unittest.TestCase): +class SeedFixedTestCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + """Fix random seeds to remove randomness from tests""" + cls._np_rand_state = numpy.random.get_state() + cls._py_rand_state = random.getstate() + + numpy.random.seed(123) + random.seed(124) + + @classmethod + def tearDownClass(cls): + """Restore random seeds""" + numpy.random.set_state(cls._np_rand_state) + random.setstate(cls._py_rand_state) + + +class TestSimpleMul(SeedFixedTestCase): DATA_NAME = 'X' DATA_WIDTH = 32 PARAM_NAME = 'W' @@ -263,7 +280,7 @@ class TestSimpleMul(unittest.TestCase): self.assertTrue(numpy.allclose(i_g_num, i_g, rtol=0.05)) -class TestSimpleMulWithMemory(unittest.TestCase): +class TestSimpleMulWithMemory(SeedFixedTestCase): DATA_WIDTH = 32 HIDDEN_WIDTH = 20 DATA_NAME = 'X' -- GitLab From 7a0f3fdd8d4e71ba638fa6528e3890b4d832b393 Mon Sep 17 00:00:00 2001 From: root Date: Thu, 11 Jan 2018 16:42:11 +0800 Subject: [PATCH 0218/2305] modify code style --- adversarial/advbox/attacks/gradientsign.py | 1 + 1 file changed, 1 insertion(+) diff --git a/adversarial/advbox/attacks/gradientsign.py b/adversarial/advbox/attacks/gradientsign.py index 33fd92e71b7..2d7a4450202 100644 --- a/adversarial/advbox/attacks/gradientsign.py +++ b/adversarial/advbox/attacks/gradientsign.py @@ -43,6 +43,7 @@ class IteratorGradientSignAttack(Attack): This attack was originally implemented by Alexey Kurakin(Google Brain). Paper link: https://arxiv.org/pdf/1607.02533.pdf """ + def _apply(self, image_label, epsilons=100, steps=10): """ Apply the iterative gradient sign attack. -- GitLab From 8c1025d66f3574e53cc41a0df9620d53c76d3ec5 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Thu, 11 Jan 2018 09:10:29 +0000 Subject: [PATCH 0219/2305] first commit --- paddle/framework/lod_tensor.cc | 32 ++++++++++++++++++++++------- paddle/framework/lod_tensor_test.cc | 28 +++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index 7ae94c64653..3d1b75597e5 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -225,20 +225,38 @@ void DeserializeFromStream(std::istream &is, LoDTensor *tensor, std::vector LoDTensor::SplitLoDTensor( const std::vector places) const { check_memory_size(); - PADDLE_ENFORCE(lod().empty(), "Disable parallel lod for now"); PADDLE_ENFORCE(dims()[0] % places.size() == 0, "Batch size should be divided by places size"); std::vector lods; for (size_t place_idx = 0; place_idx < places.size(); ++place_idx) { - int begin = place_idx * dims()[0] / places.size(); - int end = (place_idx + 1) * dims()[0] / places.size(); + size_t batch_size = lod().empty() ? dims()[0] : NumElements(0); + size_t begin = place_idx * batch_size / places.size(); + size_t end = (place_idx + 1) * batch_size / places.size(); - auto src = Slice(begin, end); - auto &dst_place = places[place_idx]; LoDTensor dst; - framework::Copy(src, dst_place, &dst); - + if (lod().empty()) { + auto src = Slice(begin, end); + auto &dst_place = places[place_idx]; + framework::Copy(src, dst_place, &dst); + } else { + auto lod_and_offset = GetSubLoDAndAbsoluteOffset(lod(), begin, end, 0); + + auto &offset = lod_and_offset.second; + auto src = Slice(offset.first, offset.second); + auto &dst_place = places[place_idx]; + framework::Copy(src, dst_place, &dst); + + LoD my_lod; + for (auto &l : lod_and_offset.first) { + std::vector v{0}; + for (auto &ll : l) { + v.push_back(ll + v.back()); + } + my_lod.emplace_back(v); + } + dst.set_lod(my_lod); + } lods.emplace_back(dst); } diff --git a/paddle/framework/lod_tensor_test.cc b/paddle/framework/lod_tensor_test.cc index baad9c6f98a..5ff7dca564a 100644 --- a/paddle/framework/lod_tensor_test.cc +++ b/paddle/framework/lod_tensor_test.cc @@ -131,5 +131,33 @@ TEST(LoD, ToAbsOffset) { EXPECT_EQ(abs_lod, expected); } +TEST(LoD, SplitLoDTensor) { + LoD lod; + lod.push_back(std::vector({0, 2, 4, 5, 6})); + lod.push_back(std::vector({0, 1, 6, 8, 13, 15, 20})); + + platform::CPUPlace place; + LoDTensor lod_tensor; + lod_tensor.Resize({20, 1}); + float* dst_ptr = lod_tensor.mutable_data(place); + for (int i = 0; i < lod_tensor.numel(); ++i) { + dst_ptr[i] = i; + } + lod_tensor.set_lod(lod); + + std::vector places{platform::CPUPlace(), + platform::CPUPlace()}; + LoD lod0; + lod0.push_back(std::vector({0, 2, 4})); + lod0.push_back(std::vector({0, 1, 6, 8, 13})); + LoD lod1; + lod1.push_back(std::vector({0, 1, 2})); + lod1.push_back(std::vector({0, 2, 7})); + + auto lods = lod_tensor.SplitLoDTensor(places); + EXPECT_EQ(lods[0].lod(), lod0); + EXPECT_EQ(lods[1].lod(), lod1); +} + } // namespace framework } // namespace paddle -- GitLab From 5325313e4cb1fb8c45cffcc223239cf5d85620af Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Thu, 11 Jan 2018 20:06:13 +0800 Subject: [PATCH 0220/2305] debugging shape match --- .../paddle/v2/fluid/distribute_transpiler.py | 95 +++++++++++++++---- 1 file changed, 79 insertions(+), 16 deletions(-) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index b064220ca29..75e103cb80c 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -257,7 +257,45 @@ class DistributeTranspiler: pass return orig_shape - def _append_pserver_ops(self, program, opt_op, endpoint): + def _is_op_on_pserver(self, endpoint, all_ops, idx): + """ + Recursively check if the op need to run on current server. + Assume that ops are in the execution order. + """ + param_names = [ + p.name for p in self.param_grad_ep_mapping[endpoint]["params"] + ] + op = all_ops[idx] + if op.inputs.has_key("Param"): + if op.inputs["Param"].name in param_names: + return True + else: + for n in param_names: + if n.startswith(op.inputs["Param"].name+".block") and \ + n != op.inputs["Param"].name: + return True + return False + else: + j = idx - 1 + while j >= 0: + prev_op = all_ops[j] + prev_output_names = [o.name for o in prev_op.outputs.values()] + prev_input_names = [o.name for o in prev_op.inputs.values()] + found1 = False + found2 = False + for _, v in op.inputs.iteritems(): + if v.name in prev_output_names: + found1 = self._is_op_on_pserver(endpoint, all_ops, j) + # later ops may produce output for prev op's next batch use. + for _, v in op.outputs.iteritems(): + if v.name in prev_input_names: + found2 = self._is_op_on_pserver(endpoint, all_ops, j) + if found1 or found2: + return True + j -= 1 + return False + + def _append_pserver_ops(self, program, pserver_program, opt_op, endpoint): new_inputs = dict() # update param/grad shape first, then other inputs like # moment can use the updated shape @@ -321,6 +359,14 @@ class DistributeTranspiler: dtype=var.dtype, shape=new_shape) new_inputs[key] = tmpvar + # create var in pserver program global block. + # TODO(typhoonzero): put blocks in one program to avoid create two + # variables. + pserver_program.global_block().create_var( + name=var.name, + persistable=var.persistable, + dtype=var.dtype, + shape=new_shape) # change outputs ParamOut variable opt_op.outputs["ParamOut"] = new_inputs["Param"] @@ -330,13 +376,18 @@ class DistributeTranspiler: outputs=opt_op.outputs, attrs=opt_op.attrs) - def _append_pserver_non_opt_ops(self, program, opt_op): + def _append_pserver_non_opt_ops(self, program, pserver_program, opt_op): for _, var in opt_op.inputs.iteritems(): program.global_block().create_var( name=var.name, persistable=var.persistable, dtype=var.dtype, shape=var.shape) + pserver_program.global_block().create_var( + name=var.name, + persistable=var.persistable, + dtype=var.dtype, + shape=var.shape) program.global_block().append_op( type=opt_op.type, inputs=opt_op.inputs, @@ -358,13 +409,18 @@ class DistributeTranspiler: self._clone_var(pserver_program.global_block(), v) # step6 optimize_sub_program = Program() - for opt_op in optimize_ops: + for idx, opt_op in enumerate(optimize_ops): + is_op_on_pserver = self._is_op_on_pserver(endpoint, optimize_ops, + idx) + if not is_op_on_pserver: + continue if opt_op.inputs.has_key("Grad"): - # append optimize_op - self._append_pserver_ops(optimize_sub_program, opt_op, endpoint) + self._append_pserver_ops(optimize_sub_program, pserver_program, + opt_op, endpoint) else: - self._append_pserver_non_opt_ops(optimize_sub_program, opt_op) - + self._append_pserver_non_opt_ops(optimize_sub_program, + pserver_program, opt_op) + print("****subprogram", optimize_sub_program) pserver_program.global_block().append_op( type="recv", inputs={"RX": self.param_grad_ep_mapping[endpoint]["grads"] @@ -386,7 +442,7 @@ class DistributeTranspiler: pserver_program.sync_with_cpp() return pserver_program - def get_startup_program(self, endpoint): + def get_startup_program(self, endpoint, pserver_program): """ Get startup program for current parameter server. Modify operator input variables if there are variables that @@ -405,13 +461,17 @@ class DistributeTranspiler: # 1. create vars created_var_map = dict() - for var in params: + for _, var in pserver_program.global_block().vars.iteritems(): + print("create var for startup", var.name, var.shape) tmpvar = s_prog.global_block().create_var( name=var.name, - persistable=True, + persistable=var.persistable, dtype=var.dtype, shape=var.shape) created_var_map[var.name] = tmpvar + optimize_op_input_var_names = [ + v.name for v in pserver_program.global_block().vars.values() + ] # 2. rename op outputs for op in orig_s_prog.global_block().ops: @@ -423,13 +483,16 @@ class DistributeTranspiler: else: new_outputs[key] = var # do not append startup op if var is not on this pserver - var_on_pserver = False - for _, var in new_outputs.iteritems(): - if var.name in created_var_map: - var_on_pserver = True - if var_on_pserver: + op_on_pserver = False + for _, var in op.outputs.iteritems(): + if var.name in optimize_op_input_var_names: + op_on_pserver = True + break + + if op_on_pserver: # gaussian_random use attr to determine tensor shape - op.attrs["shape"] = new_outputs["Out"].shape + if op.type in ["gaussian_random", "fill_constant"]: + op.attrs["shape"] = new_outputs["Out"].shape s_prog.global_block().append_op( type=op.type, inputs=op.inputs, -- GitLab From 5d901d00bf9c93225548d707b1c3b79634b801b4 Mon Sep 17 00:00:00 2001 From: "yi.wu" Date: Thu, 11 Jan 2018 22:41:24 +0800 Subject: [PATCH 0221/2305] update --- .../paddle/v2/fluid/distribute_transpiler.py | 33 +++++++++++-------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index 75e103cb80c..59e74e0d6f2 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -459,9 +459,10 @@ class DistributeTranspiler: return pname, splited_param.shape return "", [] - # 1. create vars + # 1. create vars in pserver program to startup program + pserver_vars = pserver_program.global_block().vars created_var_map = dict() - for _, var in pserver_program.global_block().vars.iteritems(): + for _, var in pserver_vars.iteritems(): print("create var for startup", var.name, var.shape) tmpvar = s_prog.global_block().create_var( name=var.name, @@ -469,30 +470,36 @@ class DistributeTranspiler: dtype=var.dtype, shape=var.shape) created_var_map[var.name] = tmpvar - optimize_op_input_var_names = [ - v.name for v in pserver_program.global_block().vars.values() - ] # 2. rename op outputs for op in orig_s_prog.global_block().ops: new_outputs = dict() + # do not append startup op if var is not on this pserver + op_on_pserver = False for key, var in op.outputs.iteritems(): newname, _ = _get_splited_name_and_shape(var.name) if newname: + op_on_pserver = True new_outputs[key] = created_var_map[newname] - else: - new_outputs[key] = var - # do not append startup op if var is not on this pserver - op_on_pserver = False - for _, var in op.outputs.iteritems(): - if var.name in optimize_op_input_var_names: + elif var.name in pserver_vars: op_on_pserver = True - break + new_outputs[key] = pserver_vars[var.name] + + # newname, _ = _get_splited_name_and_shape(var.name) + # if newname: + # print("updating output", newname, created_var_map[newname]) + # new_outputs[key] = created_var_map[newname] + # else: + # print("no update output", key, var) + # new_outputs[key] = var + # if var.name in created_var_map or \ + # newname: + # op_on_pserver = True if op_on_pserver: - # gaussian_random use attr to determine tensor shape if op.type in ["gaussian_random", "fill_constant"]: op.attrs["shape"] = new_outputs["Out"].shape + print("updated shape", op.attrs["shape"]) s_prog.global_block().append_op( type=op.type, inputs=op.inputs, -- GitLab From ea782e38a66bfd494fe08610037e65093ef65489 Mon Sep 17 00:00:00 2001 From: Abhinav Arora Date: Thu, 11 Jan 2018 10:29:24 -0800 Subject: [PATCH 0222/2305] Fix typo in batch norm bias initialization (#7449) --- python/paddle/v2/fluid/layers/nn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 48a6bee5588..1fb6523f552 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -983,7 +983,7 @@ def batch_norm(input, default_initializer=Constant(1.0)) bias = helper.create_parameter( - attr=helper.param_attr, shape=param_shape, dtype=dtype, is_bias=True) + attr=helper.bias_attr, shape=param_shape, dtype=dtype, is_bias=True) mean = helper.create_global_variable( dtype=input.dtype, -- GitLab From 6c0723661e7a3197f0589386cc985a08e3ea581f Mon Sep 17 00:00:00 2001 From: Helin Wang Date: Thu, 11 Jan 2018 11:17:40 -0800 Subject: [PATCH 0223/2305] Add distributed label semantic role book chapter --- .../test_dist_label_semantic_roles.py | 225 ++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 python/paddle/v2/fluid/tests/book_distribute/test_dist_label_semantic_roles.py diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_dist_label_semantic_roles.py b/python/paddle/v2/fluid/tests/book_distribute/test_dist_label_semantic_roles.py new file mode 100644 index 00000000000..5fa5e0e5f34 --- /dev/null +++ b/python/paddle/v2/fluid/tests/book_distribute/test_dist_label_semantic_roles.py @@ -0,0 +1,225 @@ +import math + +import numpy as np +import paddle.v2 as paddle +import paddle.v2.dataset.conll05 as conll05 +import paddle.v2.fluid as fluid +import time +import os + +word_dict, verb_dict, label_dict = conll05.get_dict() +word_dict_len = len(word_dict) +label_dict_len = len(label_dict) +pred_len = len(verb_dict) + +mark_dict_len = 2 +word_dim = 32 +mark_dim = 5 +hidden_dim = 512 +depth = 8 +mix_hidden_lr = 1e-3 + +IS_SPARSE = True +PASS_NUM = 10 +BATCH_SIZE = 20 + +embedding_name = 'emb' + + +def load_parameter(file_name, h, w): + with open(file_name, 'rb') as f: + f.read(16) # skip header. + return np.fromfile(f, dtype=np.float32).reshape(h, w) + + +def db_lstm(word, predicate, ctx_n2, ctx_n1, ctx_0, ctx_p1, ctx_p2, mark, + **ignored): + # 8 features + predicate_embedding = fluid.layers.embedding( + input=predicate, + size=[pred_len, word_dim], + dtype='float32', + is_sparse=IS_SPARSE, + param_attr='vemb') + + mark_embedding = fluid.layers.embedding( + input=mark, + size=[mark_dict_len, mark_dim], + dtype='float32', + is_sparse=IS_SPARSE) + + word_input = [word, ctx_n2, ctx_n1, ctx_0, ctx_p1, ctx_p2] + emb_layers = [ + fluid.layers.embedding( + size=[word_dict_len, word_dim], + input=x, + param_attr=fluid.ParamAttr( + name=embedding_name, trainable=False)) for x in word_input + ] + emb_layers.append(predicate_embedding) + emb_layers.append(mark_embedding) + + hidden_0_layers = [ + fluid.layers.fc(input=emb, size=hidden_dim) for emb in emb_layers + ] + + hidden_0 = fluid.layers.sums(input=hidden_0_layers) + + lstm_0 = fluid.layers.dynamic_lstm( + input=hidden_0, + size=hidden_dim, + candidate_activation='relu', + gate_activation='sigmoid', + cell_activation='sigmoid') + + # stack L-LSTM and R-LSTM with direct edges + input_tmp = [hidden_0, lstm_0] + + for i in range(1, depth): + mix_hidden = fluid.layers.sums(input=[ + fluid.layers.fc(input=input_tmp[0], size=hidden_dim), + fluid.layers.fc(input=input_tmp[1], size=hidden_dim) + ]) + + lstm = fluid.layers.dynamic_lstm( + input=mix_hidden, + size=hidden_dim, + candidate_activation='relu', + gate_activation='sigmoid', + cell_activation='sigmoid', + is_reverse=((i % 2) == 1)) + + input_tmp = [mix_hidden, lstm] + + feature_out = fluid.layers.sums(input=[ + fluid.layers.fc(input=input_tmp[0], size=label_dict_len), + fluid.layers.fc(input=input_tmp[1], size=label_dict_len) + ]) + + return feature_out + + +def to_lodtensor(data, place): + seq_lens = [len(seq) for seq in data] + cur_len = 0 + lod = [cur_len] + for l in seq_lens: + cur_len += l + lod.append(cur_len) + flattened_data = np.concatenate(data, axis=0).astype("int64") + flattened_data = flattened_data.reshape([len(flattened_data), 1]) + res = fluid.LoDTensor() + res.set(flattened_data, place) + res.set_lod([lod]) + return res + + +def main(): + # define network topology + word = fluid.layers.data( + name='word_data', shape=[1], dtype='int64', lod_level=1) + predicate = fluid.layers.data( + name='verb_data', shape=[1], dtype='int64', lod_level=1) + ctx_n2 = fluid.layers.data( + name='ctx_n2_data', shape=[1], dtype='int64', lod_level=1) + ctx_n1 = fluid.layers.data( + name='ctx_n1_data', shape=[1], dtype='int64', lod_level=1) + ctx_0 = fluid.layers.data( + name='ctx_0_data', shape=[1], dtype='int64', lod_level=1) + ctx_p1 = fluid.layers.data( + name='ctx_p1_data', shape=[1], dtype='int64', lod_level=1) + ctx_p2 = fluid.layers.data( + name='ctx_p2_data', shape=[1], dtype='int64', lod_level=1) + mark = fluid.layers.data( + name='mark_data', shape=[1], dtype='int64', lod_level=1) + feature_out = db_lstm(**locals()) + target = fluid.layers.data( + name='target', shape=[1], dtype='int64', lod_level=1) + crf_cost = fluid.layers.linear_chain_crf( + input=feature_out, + label=target, + param_attr=fluid.ParamAttr( + name='crfw', learning_rate=mix_hidden_lr)) + avg_cost = fluid.layers.mean(x=crf_cost) + + # TODO(qiao) + # check other optimizers and check why out will be NAN + sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.0001) + optimize_ops, params_grads = sgd_optimizer.minimize(avg_cost) + + # TODO(qiao) + # add dependency track and move this config before optimizer + crf_decode = fluid.layers.crf_decoding( + input=feature_out, param_attr=fluid.ParamAttr(name='crfw')) + + chunk_evaluator = fluid.evaluator.ChunkEvaluator( + input=crf_decode, + label=target, + chunk_scheme="IOB", + num_chunk_types=int(math.ceil((label_dict_len - 1) / 2.0))) + + train_data = paddle.batch( + paddle.reader.shuffle( + paddle.dataset.conll05.test(), buf_size=8192), + batch_size=BATCH_SIZE) + place = fluid.CPUPlace() + feeder = fluid.DataFeeder( + feed_list=[ + word, ctx_n2, ctx_n1, ctx_0, ctx_p1, ctx_p2, predicate, mark, target + ], + place=place) + exe = fluid.Executor(place) + + t = fluid.DistributeTranspiler() + pserver_endpoints = os.getenv("PSERVERS") + # server endpoint for current node + current_endpoint = os.getenv("SERVER_ENDPOINT") + # run as trainer or parameter server + training_role = os.getenv( + "TRAINING_ROLE", "TRAINER") # get the training role: trainer/pserver + t.transpile( + optimize_ops, params_grads, pservers=pserver_endpoints, trainers=2) + + if training_role == "PSERVER": + if not current_endpoint: + print("need env SERVER_ENDPOINT") + exit(1) + pserver_prog = t.get_pserver_program(current_endpoint, optimize_ops) + exe.run(fluid.default_startup_program()) + exe.run(pserver_prog) + elif training_role == "TRAINER": + trainer_prog = t.get_trainer_program() + start_time = time.time() + batch_id = 0 + exe.run(fluid.default_startup_program()) + embedding_param = fluid.global_scope().find_var( + embedding_name).get_tensor() + embedding_param.set( + load_parameter(conll05.get_embedding(), word_dict_len, word_dim), + place) + for pass_id in xrange(PASS_NUM): + chunk_evaluator.reset(exe) + for data in train_data(): + cost, precision, recall, f1_score = exe.run( + trainer_prog, + feed=feeder.feed(data), + fetch_list=[avg_cost] + chunk_evaluator.metrics) + pass_precision, pass_recall, pass_f1_score = chunk_evaluator.eval( + exe) + + if batch_id % 10 == 0: + print("avg_cost:" + str(cost) + " precision:" + str( + precision) + " recall:" + str(recall) + " f1_score:" + + str(f1_score) + " pass_precision:" + str( + pass_precision) + " pass_recall:" + str( + pass_recall) + " pass_f1_score:" + str( + pass_f1_score)) + if batch_id != 0: + print("second per batch: " + str((time.time( + ) - start_time) / batch_id)) + + batch_id = batch_id + 1 + + +if __name__ == '__main__': + main() -- GitLab From ce233796ea33e029304b710c98ed933930c76efb Mon Sep 17 00:00:00 2001 From: xuwei06 Date: Mon, 8 Jan 2018 17:37:37 -0800 Subject: [PATCH 0224/2305] assign_value operator We need this operator to assign value to a tensor and the values are stored in the program so that they can be used independent of python. --- paddle/operators/assign_value_op.cc | 82 +++++++++++++++++++ paddle/operators/assign_value_op.cu.cc | 36 ++++++++ paddle/operators/assign_value_op.h | 55 +++++++++++++ python/paddle/v2/fluid/layers/tensor.py | 37 +++++++-- .../v2/fluid/tests/test_assign_value_op.py | 38 +++++++++ 5 files changed, 242 insertions(+), 6 deletions(-) create mode 100644 paddle/operators/assign_value_op.cc create mode 100644 paddle/operators/assign_value_op.cu.cc create mode 100644 paddle/operators/assign_value_op.h create mode 100644 python/paddle/v2/fluid/tests/test_assign_value_op.py diff --git a/paddle/operators/assign_value_op.cc b/paddle/operators/assign_value_op.cc new file mode 100644 index 00000000000..a0bce99ff3d --- /dev/null +++ b/paddle/operators/assign_value_op.cc @@ -0,0 +1,82 @@ +/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/assign_value_op.h" + +namespace paddle { +namespace operators { + +class AssignValueOp : public framework::OperatorWithKernel { + public: + AssignValueOp(const std::string &type, + const framework::VariableNameMap &inputs, + const framework::VariableNameMap &outputs, + const framework::AttributeMap &attrs) + : OperatorWithKernel(type, inputs, outputs, attrs) {} + + void InferShape(framework::InferShapeContext *ctx) const override { + PADDLE_ENFORCE(ctx->HasOutput("Out"), + "Output(Out) of AssignValueOp should not be null."); + auto shape = ctx->Attrs().Get>("shape"); + ctx->SetOutputDim("Out", framework::make_ddim(shape)); + } + + protected: + framework::OpKernelType GetActualKernelType( + const framework::ExecutionContext &ctx) const override { + return framework::OpKernelType( + framework::proto::DataType(ctx.Attr("dtype")), ctx.GetPlace()); + } +}; + +class AssignValueOpMaker : public framework::OpProtoAndCheckerMaker { + public: + AssignValueOpMaker(OpProto *proto, OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddOutput("Out", "(Tensor) Output tensor of assign_value operator."); + AddAttr>("shape", + "(vector) " + "Shape of values."); + AddAttr("dtype", "data type of values") + .InEnum({framework::proto::DataType::INT32, + framework::proto::DataType::FP32}); + AddAttr>("fp32_values", "store the float values") + .SetDefault({}); + AddAttr>("int32_values", "store the int values") + .SetDefault({}); + AddComment(R"DOC( +AssignValue operator + +$$Out = values$$ +)DOC"); + } +}; + +template +class AssignValueCPUKernel : public AssignValueKernel { + protected: + virtual void Copy(void *dst, const void *src, size_t size, + const framework::ExecutionContext &ctx) const { + std::memcpy(dst, src, size); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; + +REGISTER_OPERATOR(assign_value, ops::AssignValueOp, ops::AssignValueOpMaker); +REGISTER_OP_CPU_KERNEL(assign_value, ops::AssignValueCPUKernel, + ops::AssignValueCPUKernel) diff --git a/paddle/operators/assign_value_op.cu.cc b/paddle/operators/assign_value_op.cu.cc new file mode 100644 index 00000000000..8afb032037f --- /dev/null +++ b/paddle/operators/assign_value_op.cu.cc @@ -0,0 +1,36 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +Indicesou may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/assign_value_op.h" + +namespace paddle { +namespace operators { + +template +class AssignValueGPUKernel : public AssignValueKernel { + protected: + virtual void Copy(void* dst, const void* src, size_t size, + const framework::ExecutionContext& ctx) const { + auto& dev_ctx = ctx.template device_context(); + paddle::platform::GpuMemcpyAsync(dst, src, size, cudaMemcpyHostToDevice, + dev_ctx.stream()); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP_CUDA_KERNEL(assign_value, ops::AssignValueGPUKernel, + ops::AssignValueGPUKernel); diff --git a/paddle/operators/assign_value_op.h b/paddle/operators/assign_value_op.h new file mode 100644 index 00000000000..bdb5bce272f --- /dev/null +++ b/paddle/operators/assign_value_op.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/framework/eigen.h" +#include "paddle/framework/op_registry.h" +#include "paddle/platform/enforce.h" + +namespace paddle { +namespace operators { + +template +class AssignValueKernel : public framework::OpKernel { + public: + virtual void Compute(const framework::ExecutionContext& ctx) const { + auto shape = ctx.Attr>("shape"); + auto* out = ctx.Output("Out"); + out->Resize(framework::make_ddim(shape)); + auto* dst = out->mutable_data(ctx.GetPlace()); + int dtype = ctx.Attr("dtype"); + const char* value_name = nullptr; + switch (dtype) { + case framework::proto::DataType::INT32: + value_name = "int32_values"; + break; + case framework::proto::DataType::FP32: + value_name = "fp32_values"; + break; + default: + PADDLE_THROW("Unsupported dtype for assign_value_op: %d", dtype); + break; + } + auto values = ctx.Attr>(value_name); + Copy(dst, values.data(), sizeof(T) * values.size(), ctx); + } + + protected: + virtual void Copy(void* dst, const void* src, size_t size, + const framework::ExecutionContext& ctx) const = 0; +}; + +} // namespace operators +} // namespace paddle diff --git a/python/paddle/v2/fluid/layers/tensor.py b/python/paddle/v2/fluid/layers/tensor.py index 5f12ecfc14f..639f8b03ede 100644 --- a/python/paddle/v2/fluid/layers/tensor.py +++ b/python/paddle/v2/fluid/layers/tensor.py @@ -1,5 +1,9 @@ from ..layer_helper import LayerHelper from ..param_attr import ParamAttr +from ..framework import convert_np_dtype_to_dtype_ +from ..framework import Variable +from ..core import DataType +import numpy __all__ = [ 'create_tensor', 'create_parameter', 'cast', 'concat', 'sums', 'assign', @@ -121,7 +125,7 @@ def assign(input, output): This function copies the *input* Variable to the *output* Variable. Args: - input(Variable): The source variable + input(Variable|numpy.ndarray): The source variable output(Variable): The destination variable Returns: @@ -134,11 +138,32 @@ def assign(input, output): fluid.layers.assign(hidden, out) """ helper = LayerHelper('assign', **locals()) - helper.append_op( - type='scale', - inputs={'X': [input]}, - outputs={'Out': [output]}, - attrs={'scale': 1.0}) + if isinstance(input, Variable): + helper.append_op( + type='scale', + inputs={'X': [input]}, + outputs={'Out': [output]}, + attrs={'scale': 1.0}) + elif isinstance(input, numpy.ndarray): + dtype = convert_np_dtype_to_dtype_(input.dtype) + if dtype == DataType.FP32: + value_name = "fp32_values" + elif dtype == DataType.INT32: + value_name = "int32_values" + else: + raise ValueError("Unsupported dtype %s", input.dtype) + + helper.append_op( + type='assign_value', + outputs={'Out': [output]}, + attrs={ + 'dtype': dtype, + 'shape': list(input.shape), + value_name: [float(v) for v in input.flat] + }) + else: + raise ValueError("Wrong type for assign input: %s" % type(input)) + return output diff --git a/python/paddle/v2/fluid/tests/test_assign_value_op.py b/python/paddle/v2/fluid/tests/test_assign_value_op.py new file mode 100644 index 00000000000..c3f3f87839a --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_assign_value_op.py @@ -0,0 +1,38 @@ +import paddle.v2.fluid as fluid +import paddle.v2.fluid.layers as layers +import op_test +import numpy +import unittest +import paddle.v2.fluid.framework as framework + + +class TestAssignValueOp(op_test.OpTest): + def setUp(self): + self.op_type = "assign_value" + x = numpy.random.random(size=(2, 5)).astype(numpy.float32) + self.inputs = {} + self.outputs = {'Out': x} + self.attrs = { + 'shape': x.shape, + 'dtype': framework.convert_np_dtype_to_dtype_(x.dtype), + 'fp32_values': [float(v) for v in x.flat] + } + + def test_forward(self): + self.check_output() + + def test_assign(self): + val = numpy.random.random(size=(2, 5)).astype(numpy.float32) + x = layers.create_tensor(dtype="float32") + layers.assign(input=val, output=x) + exe = fluid.Executor(fluid.CPUPlace()) + fetched_x = exe.run(fluid.default_main_program(), + feed={}, + fetch_list=[x]) + self.assertTrue( + numpy.allclose(fetched_x, val), + "fetch_x=%s val=%s" % (fetched_x, val)) + + +if __name__ == '__main__': + unittest.main() -- GitLab From 7306aab61d9ca6c85b8a707d520059a325a97d5c Mon Sep 17 00:00:00 2001 From: xuwei06 Date: Wed, 10 Jan 2018 10:00:49 -0800 Subject: [PATCH 0225/2305] GetActualKernelType => GetExpectedKernelType --- paddle/operators/assign_value_op.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/paddle/operators/assign_value_op.cc b/paddle/operators/assign_value_op.cc index a0bce99ff3d..7f6d351f505 100644 --- a/paddle/operators/assign_value_op.cc +++ b/paddle/operators/assign_value_op.cc @@ -33,7 +33,7 @@ class AssignValueOp : public framework::OperatorWithKernel { } protected: - framework::OpKernelType GetActualKernelType( + framework::OpKernelType GetExpectedKernelType( const framework::ExecutionContext &ctx) const override { return framework::OpKernelType( framework::proto::DataType(ctx.Attr("dtype")), ctx.GetPlace()); -- GitLab From 237385cf414bf2e176a52c46e650e37a2cfc40a7 Mon Sep 17 00:00:00 2001 From: xuwei06 Date: Wed, 10 Jan 2018 11:39:03 -0800 Subject: [PATCH 0226/2305] Correctly handle int values for assign_value_op --- python/paddle/v2/fluid/layers/tensor.py | 7 ++++++- python/paddle/v2/fluid/tests/test_assign_value_op.py | 8 +++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/python/paddle/v2/fluid/layers/tensor.py b/python/paddle/v2/fluid/layers/tensor.py index 639f8b03ede..57668a7983b 100644 --- a/python/paddle/v2/fluid/layers/tensor.py +++ b/python/paddle/v2/fluid/layers/tensor.py @@ -148,10 +148,15 @@ def assign(input, output): dtype = convert_np_dtype_to_dtype_(input.dtype) if dtype == DataType.FP32: value_name = "fp32_values" + values = [float(v) for v in input.flat] elif dtype == DataType.INT32: value_name = "int32_values" + values = [int(v) for v in input.flat] else: raise ValueError("Unsupported dtype %s", input.dtype) + if input.size > 1024 * 1024: + raise ValueError("The size of input is too big. Please consider " + "saving it to file and 'load_op' to load it") helper.append_op( type='assign_value', @@ -159,7 +164,7 @@ def assign(input, output): attrs={ 'dtype': dtype, 'shape': list(input.shape), - value_name: [float(v) for v in input.flat] + value_name: values }) else: raise ValueError("Wrong type for assign input: %s" % type(input)) diff --git a/python/paddle/v2/fluid/tests/test_assign_value_op.py b/python/paddle/v2/fluid/tests/test_assign_value_op.py index c3f3f87839a..51b99d09182 100644 --- a/python/paddle/v2/fluid/tests/test_assign_value_op.py +++ b/python/paddle/v2/fluid/tests/test_assign_value_op.py @@ -22,16 +22,18 @@ class TestAssignValueOp(op_test.OpTest): self.check_output() def test_assign(self): - val = numpy.random.random(size=(2, 5)).astype(numpy.float32) + val = ( + -100 + 200 * numpy.random.random(size=(2, 5))).astype(numpy.int32) x = layers.create_tensor(dtype="float32") layers.assign(input=val, output=x) exe = fluid.Executor(fluid.CPUPlace()) fetched_x = exe.run(fluid.default_main_program(), feed={}, - fetch_list=[x]) + fetch_list=[x])[0] self.assertTrue( - numpy.allclose(fetched_x, val), + numpy.array_equal(fetched_x, val), "fetch_x=%s val=%s" % (fetched_x, val)) + self.assertEqual(fetched_x.dtype, val.dtype) if __name__ == '__main__': -- GitLab From 25ecd2061ae13656653fbd89c2216375e0cd9e55 Mon Sep 17 00:00:00 2001 From: xuwei06 Date: Thu, 11 Jan 2018 12:52:54 -0800 Subject: [PATCH 0227/2305] Use CopyFromVector for assign_value_op --- paddle/framework/tensor_util.h | 4 ++-- paddle/operators/assign_value_op.cc | 13 ++----------- paddle/operators/assign_value_op.cu.cc | 21 ++------------------- paddle/operators/assign_value_op.h | 9 ++------- 4 files changed, 8 insertions(+), 39 deletions(-) diff --git a/paddle/framework/tensor_util.h b/paddle/framework/tensor_util.h index f541d2ba693..091b63bf0f9 100644 --- a/paddle/framework/tensor_util.h +++ b/paddle/framework/tensor_util.h @@ -116,8 +116,8 @@ inline void Copy(const Tensor& src, const platform::Place& dst_place, * @param[in] src The external tensor. * @param[in] ctx The device context contains device resources. * - * * @note CopyFromVector assumes that the tensor has been resized - * before invoking. + * * @note CopyFromVector will resize dst to an 1D tensor with the same + * size as src. */ template inline void CopyFromVector(const std::vector& src, diff --git a/paddle/operators/assign_value_op.cc b/paddle/operators/assign_value_op.cc index 7f6d351f505..d5671c1183a 100644 --- a/paddle/operators/assign_value_op.cc +++ b/paddle/operators/assign_value_op.cc @@ -63,20 +63,11 @@ $$Out = values$$ } }; -template -class AssignValueCPUKernel : public AssignValueKernel { - protected: - virtual void Copy(void *dst, const void *src, size_t size, - const framework::ExecutionContext &ctx) const { - std::memcpy(dst, src, size); - } -}; - } // namespace operators } // namespace paddle namespace ops = paddle::operators; REGISTER_OPERATOR(assign_value, ops::AssignValueOp, ops::AssignValueOpMaker); -REGISTER_OP_CPU_KERNEL(assign_value, ops::AssignValueCPUKernel, - ops::AssignValueCPUKernel) +REGISTER_OP_CPU_KERNEL(assign_value, ops::AssignValueKernel, + ops::AssignValueKernel); diff --git a/paddle/operators/assign_value_op.cu.cc b/paddle/operators/assign_value_op.cu.cc index 8afb032037f..b17e2015005 100644 --- a/paddle/operators/assign_value_op.cu.cc +++ b/paddle/operators/assign_value_op.cu.cc @@ -14,23 +14,6 @@ limitations under the License. */ #include "paddle/operators/assign_value_op.h" -namespace paddle { -namespace operators { - -template -class AssignValueGPUKernel : public AssignValueKernel { - protected: - virtual void Copy(void* dst, const void* src, size_t size, - const framework::ExecutionContext& ctx) const { - auto& dev_ctx = ctx.template device_context(); - paddle::platform::GpuMemcpyAsync(dst, src, size, cudaMemcpyHostToDevice, - dev_ctx.stream()); - } -}; - -} // namespace operators -} // namespace paddle - namespace ops = paddle::operators; -REGISTER_OP_CUDA_KERNEL(assign_value, ops::AssignValueGPUKernel, - ops::AssignValueGPUKernel); +REGISTER_OP_CUDA_KERNEL(assign_value, ops::AssignValueKernel, + ops::AssignValueKernel); diff --git a/paddle/operators/assign_value_op.h b/paddle/operators/assign_value_op.h index bdb5bce272f..db2e4307799 100644 --- a/paddle/operators/assign_value_op.h +++ b/paddle/operators/assign_value_op.h @@ -27,8 +27,6 @@ class AssignValueKernel : public framework::OpKernel { virtual void Compute(const framework::ExecutionContext& ctx) const { auto shape = ctx.Attr>("shape"); auto* out = ctx.Output("Out"); - out->Resize(framework::make_ddim(shape)); - auto* dst = out->mutable_data(ctx.GetPlace()); int dtype = ctx.Attr("dtype"); const char* value_name = nullptr; switch (dtype) { @@ -43,12 +41,9 @@ class AssignValueKernel : public framework::OpKernel { break; } auto values = ctx.Attr>(value_name); - Copy(dst, values.data(), sizeof(T) * values.size(), ctx); + framework::CopyFromVector(values, ctx.device_context(), out); + out->Resize(framework::make_ddim(shape)); } - - protected: - virtual void Copy(void* dst, const void* src, size_t size, - const framework::ExecutionContext& ctx) const = 0; }; } // namespace operators -- GitLab From 1baca7ffba7c603aaac9220f5aec6caf4299c2a4 Mon Sep 17 00:00:00 2001 From: Yi Wang Date: Thu, 11 Jan 2018 13:11:27 -0800 Subject: [PATCH 0228/2305] Move content in the buildtools repo into paddle repo (#7326) * Import content from the buildtools repo * Renmae Dockerfile-x86_64 into Dockerfile.x64 * yapf * Remove subdirectory root --- tools/manylinux1/Dockerfile.android | 55 ++++++ tools/manylinux1/Dockerfile.x64 | 54 ++++++ tools/manylinux1/README.md | 30 +++ tools/manylinux1/build_all.sh | 26 +++ tools/manylinux1/build_scripts/build.sh | 152 +++++++++++++++ tools/manylinux1/build_scripts/build_utils.sh | 173 ++++++++++++++++++ .../build_scripts/manylinux1-check.py | 56 ++++++ .../build_scripts/python-tag-abi-tag.py | 7 + tools/manylinux1/build_scripts/ssl-check.py | 32 ++++ 9 files changed, 585 insertions(+) create mode 100644 tools/manylinux1/Dockerfile.android create mode 100644 tools/manylinux1/Dockerfile.x64 create mode 100644 tools/manylinux1/README.md create mode 100755 tools/manylinux1/build_all.sh create mode 100644 tools/manylinux1/build_scripts/build.sh create mode 100755 tools/manylinux1/build_scripts/build_utils.sh create mode 100644 tools/manylinux1/build_scripts/manylinux1-check.py create mode 100644 tools/manylinux1/build_scripts/python-tag-abi-tag.py create mode 100644 tools/manylinux1/build_scripts/ssl-check.py diff --git a/tools/manylinux1/Dockerfile.android b/tools/manylinux1/Dockerfile.android new file mode 100644 index 00000000000..b6cae228a0c --- /dev/null +++ b/tools/manylinux1/Dockerfile.android @@ -0,0 +1,55 @@ +FROM ubuntu:16.04 +MAINTAINER PaddlePaddle Authors + +ARG UBUNTU_MIRROR +RUN /bin/bash -c 'if [[ -n ${UBUNTU_MIRROR} ]]; then sed -i 's#http://archive.ubuntu.com/ubuntu#${UBUNTU_MIRROR}#g' /etc/apt/sources.list; fi' + +# ENV variables +ARG ANDROID_ABI +ARG ANDROID_API + +ENV ANDROID_ABI=${ANDROID_ABI:-"armeabi-v7a"} +ENV ANDROID_API=${ANDROID_API:-21} + +ENV HOME=/root \ + ANDROID_NDK_HOME=/opt/android-ndk-linux \ + ANDROID_TOOLCHAINS_DIR=/opt/toolchains + +RUN apt-get update && \ + apt-get install -y \ + git python-dev python-pip python-numpy \ + wget curl tar unzip gcc g++ locales clang-format-3.8 swig cmake && \ + apt-get clean -y + +# Install Go and glide +RUN wget -qO- go.tgz https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz | \ + tar -xz -C /usr/local && \ + mkdir /root/gopath && \ + mkdir /root/gopath/bin && \ + mkdir /root/gopath/src +ENV GOROOT=/usr/local/go GOPATH=/root/gopath +# should not be in the same line with GOROOT definition, otherwise docker build could not find GOROOT. +ENV PATH=${PATH}:${GOROOT}/bin:${GOPATH}/bin + +# git credential to skip password typing +RUN git config --global credential.helper store + +# Fix locales to en_US.UTF-8 +RUN localedef -i en_US -f UTF-8 en_US.UTF-8 + +RUN pip install --upgrade pip && \ + pip install -U 'protobuf==3.1.0' && \ + pip install -U wheel sphinx && \ + pip install pre-commit + +# Android NDK +RUN mkdir -p ${ANDROID_TOOLCHAINS_DIR} && \ + mkdir -p /opt/android-ndk-tmp && \ + cd /opt/android-ndk-tmp && \ + wget -q https://dl.google.com/android/repository/android-ndk-r14b-linux-x86_64.zip && \ + unzip -q android-ndk-r14b-linux-x86_64.zip && \ + mv android-ndk-r14b ${ANDROID_NDK_HOME} && \ + rm -rf /opt/android-ndk-tmp + +CMD ["bash", "/paddle/paddle/scripts/docker/build_android.sh"] + diff --git a/tools/manylinux1/Dockerfile.x64 b/tools/manylinux1/Dockerfile.x64 new file mode 100644 index 00000000000..2c6ba650a5d --- /dev/null +++ b/tools/manylinux1/Dockerfile.x64 @@ -0,0 +1,54 @@ +# NOTE The manylinux1 policy mandates CentOS-5. We replace it with CentOS-6 in +# order to satisfy the build of capnproto library (a nupic.core dependency), +# which requires some headers and symbols not present on CentOS-5 (e.g., +# signalfd.h, pipe2, O_NONBLOCK, SOCK_NONBLOCK, etc.). See +# https://github.com/sandstorm-io/capnproto/issues/350. +FROM nvidia/cuda: +MAINTAINER Numenta, based on the ManyLinux project + +ENV LC_ALL en_US.UTF-8 +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US.UTF-8 +ENV PATH /opt/rh/devtoolset-2/root/usr/bin:$PATH +ENV LD_LIBRARY_PATH /opt/rh/devtoolset-2/root/usr/lib64:/opt/rh/devtoolset-2/root/usr/lib:/usr/local/lib64:/usr/local/lib:${LD_LIBRARY_PATH} +ENV PKG_CONFIG_PATH=/usr/local/lib/pkgconfig + +COPY build_scripts /build_scripts +RUN bash build_scripts/build.sh && rm -r build_scripts + +ENV SSL_CERT_FILE=/opt/_internal/certs.pem + +# for paddle +RUN wget --no-check-certificate -qO- https://storage.googleapis.com/golang/go1.8.1.linux-amd64.tar.gz | \ + tar -xz -C /usr/local && \ + mkdir /root/gopath && \ + mkdir /root/gopath/bin && \ + mkdir /root/gopath/src + + +ENV GOROOT=/usr/local/go GOPATH=/root/gopath +ENV PATH=${GOROOT}/bin:${GOPATH}/bin:${PATH} + +# protobuf 3.1.0 +RUN cd /opt && wget -q --no-check-certificate https://github.com/google/protobuf/releases/download/v3.1.0/protobuf-cpp-3.1.0.tar.gz && \ + tar xzf protobuf-cpp-3.1.0.tar.gz && \ + cd protobuf-3.1.0 && ./configure && make -j4 && make install && cd .. && rm -f protobuf-cpp-3.1.0.tar.gz + + +RUN yum install -y sqlite-devel zlib-devel openssl-devel boost boost-devel pcre-devel vim tk-devel tkinter libtool + +RUN wget -O /root/requirements.txt https://raw.githubusercontent.com/PaddlePaddle/Paddle/develop/python/requirements.txt + +RUN LD_LIBRARY_PATH=/opt/_internal/cpython-2.7.11-ucs4/lib:${LD_LIBRARY_PATH} /opt/python/cp27-cp27mu/bin/pip install -r /root/requirements.txt && \ + LD_LIBRARY_PATH=/opt/_internal/cpython-2.7.11-ucs2/lib:${LD_LIBRARY_PATH} /opt/python/cp27-cp27m/bin/pip install -r /root/requirements.txt && \ + go get github.com/Masterminds/glide && \ + rm -rf /root/requirements.txt + +RUN LD_LIBRARY_PATH=/opt/_internal/cpython-2.7.11-ucs4/lib:${LD_LIBRARY_PATH} /opt/python/cp27-cp27mu/bin/pip install pre-commit 'ipython==5.3.0' opencv-python && \ + LD_LIBRARY_PATH=/opt/_internal/cpython-2.7.11-ucs2/lib:${LD_LIBRARY_PATH} /opt/python/cp27-cp27m/bin/pip install pre-commit 'ipython==5.3.0' opencv-python + +RUN wget -O /opt/swig-2.0.12.tar.gz https://sourceforge.net/projects/swig/files/swig/swig-2.0.12/swig-2.0.12.tar.gz/download && \ + cd /opt && tar xzf swig-2.0.12.tar.gz && cd /opt/swig-2.0.12 && ./configure && make && make install && cd /opt && rm swig-2.0.12.tar.gz + +RUN mkdir -p /src && cd /src && git clone https://github.com/NVIDIA/nccl.git nccl && cd nccl &&\ + make -j `nproc` install && cd .. && rm -rf nccl diff --git a/tools/manylinux1/README.md b/tools/manylinux1/README.md new file mode 100644 index 00000000000..cb0a9ac22cd --- /dev/null +++ b/tools/manylinux1/README.md @@ -0,0 +1,30 @@ +# buildtools + +We release PaddlePaddle and PaddlePaddle Fluid as shared libraries, +which, we hope could be released as wheel packages on PyPI, so we need +to make sure that the build follows the +[manulinux1](https://www.python.org/dev/peps/pep-0513/) standard. + +The manylinux standard suggests building Python modules on an old +system, because that a module would anyway depend on some shared +libraries, and Linux's shared library standard states that those built +with newer version compilers cannot work with those with older +versions. The suggested building environment is as old as CentOS 5. +However, PaddlePaddle relies on CUDA, and the earlies version of +[CentOS works with CUDA is 6](https://hub.docker.com/r/nvidia/cuda/). +So, here we provide a Docker image basing on CentOS 6 and CUDA for +building PaddlePaddle and making the release supports "as-manylinux as +possible." or "sufficiently many Linux" according to [this +discussion](https://mail.python.org/pipermail/wheel-builders/2016-July/000175.html). + +The build output of our Docker image includes multiple wheel files -- +some contain the CPU-only binary, some others support CUDA; some are +compatible with the cp27m Python ABI, some others with cp27. + +To build these wheels, please run the following commands: + +```bash +git clone https://github.com/paddlepaddle/paddle +cd paddle/tools/manylinux1 +REPO=[yourrepo] ./build_all.sh +``` diff --git a/tools/manylinux1/build_all.sh b/tools/manylinux1/build_all.sh new file mode 100755 index 00000000000..097bedb5265 --- /dev/null +++ b/tools/manylinux1/build_all.sh @@ -0,0 +1,26 @@ +#!/bin/bash +set -xe + +REPO="${REPO:-typhoon1986}" + +# NOTE: version matches are determined! +sed 's//7.5-cudnn5-devel-centos6/g' Dockerfile.x64 | \ +sed 's//NVCC_GENCODE="-gencode=arch=compute_35,code=sm_35 -gencode=arch=compute_50,code=sm_50 -gencode=arch=compute_52,code=sm_52"/g'> Dockerfile.tmp +docker build -t ${REPO}/paddle_manylinux_devel:cuda7.5_cudnn5 -f Dockerfile.tmp . +docker push ${REPO}/paddle_manylinux_devel:cuda7.5_cudnn5 + +sed 's//8.0-cudnn5-devel-centos6/g' Dockerfile.x64 | \ +sed 's//NVCC_GENCODE="-gencode=arch=compute_35,code=sm_35 -gencode=arch=compute_50,code=sm_50 -gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_60,code=sm_60 -gencode=arch=compute_60,code=compute_60 -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_62,code=sm_62"/g'> Dockerfile.tmp +docker build -t ${REPO}/paddle_manylinux_devel:cuda8.0_cudnn5 -f Dockerfile.tmp . +docker push ${REPO}/paddle_manylinux_devel:cuda8.0_cudnn5 + +sed 's//8.0-cudnn7-devel-centos6/g' Dockerfile.x64 | \ +sed 's//NVCC_GENCODE="-gencode=arch=compute_35,code=sm_35 -gencode=arch=compute_50,code=sm_50 -gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_60,code=sm_60 -gencode=arch=compute_60,code=compute_60 -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_62,code=sm_62"/g'> Dockerfile.tmp + +docker build -t ${REPO}/paddle_manylinux_devel:cuda8.0_cudnn7 -f Dockerfile.tmp . +docker push ${REPO}/paddle_manylinux_devel:cuda8.0_cudnn7 + +sed 's//9.0-cudnn7-devel-centos6/g' Dockerfile.x64 | \ +sed 's//NVCC_GENCODE="-gencode=arch=compute_35,code=sm_35 -gencode=arch=compute_50,code=sm_50 -gencode=arch=compute_52,code=sm_52 -gencode=arch=compute_60,code=sm_60 -gencode=arch=compute_60,code=compute_60 -gencode=arch=compute_61,code=sm_61 -gencode=arch=compute_62,code=sm_62 -gencode=arch=compute_70,code=sm_70"/g'> Dockerfile.tmp +docker build -t ${REPO}/paddle_manylinux_devel:cuda9.0_cudnn7 -f Dockerfile.tmp . +docker push ${REPO}/paddle_manylinux_devel:cuda9.0_cudnn7 diff --git a/tools/manylinux1/build_scripts/build.sh b/tools/manylinux1/build_scripts/build.sh new file mode 100644 index 00000000000..93591fa9dda --- /dev/null +++ b/tools/manylinux1/build_scripts/build.sh @@ -0,0 +1,152 @@ +#!/bin/bash +# Top-level build script called from Dockerfile + +# Stop at any error, show all commands +set -ex + +# Python versions to be installed in /opt/$VERSION_NO +# NOTE Only need python 2.7.11 for nupic.core/nupic.bindings at this time, so +# remove others to expedite build and reduce docker image size. The original +# manylinux docker image project builds many python versions. +# NOTE We added back 3.5.1, since auditwheel requires python 3.3+ +CPYTHON_VERSIONS="2.7.11 3.5.1" + +# openssl version to build, with expected sha256 hash of .tar.gz +# archive +OPENSSL_ROOT=openssl-1.0.2l +OPENSSL_HASH=ce07195b659e75f4e1db43552860070061f156a98bb37b672b101ba6e3ddf30c +EPEL_RPM_HASH=e5ed9ecf22d0c4279e92075a64c757ad2b38049bcf5c16c4f2b75d5f6860dc0d +DEVTOOLS_HASH=a8ebeb4bed624700f727179e6ef771dafe47651131a00a78b342251415646acc +PATCHELF_HASH=d9afdff4baeacfbc64861454f368b7f2c15c44d245293f7587bbf726bfe722fb +CURL_ROOT=curl-7.49.1 +CURL_HASH=eb63cec4bef692eab9db459033f409533e6d10e20942f4b060b32819e81885f1 +AUTOCONF_ROOT=autoconf-2.69 +AUTOCONF_HASH=954bd69b391edc12d6a4a51a2dd1476543da5c6bbf05a95b59dc0dd6fd4c2969 + +# Dependencies for compiling Python that we want to remove from +# the final image after compiling Python +PYTHON_COMPILE_DEPS="zlib-devel bzip2-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel" + +# Libraries that are allowed as part of the manylinux1 profile +MANYLINUX1_DEPS="glibc-devel libstdc++-devel glib2-devel libX11-devel libXext-devel libXrender-devel mesa-libGL-devel libICE-devel libSM-devel ncurses-devel" + +# Get build utilities +MY_DIR=$(dirname "${BASH_SOURCE[0]}") +source $MY_DIR/build_utils.sh + +# EPEL support +yum -y install wget curl +curl -sLO https://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm +check_sha256sum epel-release-6-8.noarch.rpm $EPEL_RPM_HASH + +# Dev toolset (for LLVM and other projects requiring C++11 support) +curl -sLO http://people.centos.org/tru/devtools-2/devtools-2.repo +check_sha256sum devtools-2.repo $DEVTOOLS_HASH +mv devtools-2.repo /etc/yum.repos.d/devtools-2.repo +rpm -Uvh --replacepkgs epel-release-6*.rpm +rm -f epel-release-6*.rpm + +# Development tools and libraries +yum -y install bzip2 make git patch unzip bison yasm diffutils \ + automake which file \ + kernel-devel-`uname -r` \ + devtoolset-2-binutils devtoolset-2-gcc \ + devtoolset-2-gcc-c++ devtoolset-2-gcc-gfortran \ + ${PYTHON_COMPILE_DEPS} + +# Install more recent version of cmake +# curl -O https://cmake.org/files/v3.8/cmake-3.8.1-Linux-x86_64.sh +# /bin/sh cmake-3.8.1-Linux-x86_64.sh --prefix=/usr/local --skip-license +# rm cmake-3.8.1-Linux-x86_64.sh + +wget -q https://cmake.org/files/v3.5/cmake-3.5.2.tar.gz && tar xzf cmake-3.5.2.tar.gz && \ +cd cmake-3.5.2 && ./bootstrap && \ +make -j4 && make install && cd .. && rm cmake-3.5.2.tar.gz + + +# Install newest autoconf +build_autoconf $AUTOCONF_ROOT $AUTOCONF_HASH +autoconf --version + +# Compile the latest Python releases. +# (In order to have a proper SSL module, Python is compiled +# against a recent openssl [see env vars above], which is linked +# statically. We delete openssl afterwards.) +build_openssl $OPENSSL_ROOT $OPENSSL_HASH +mkdir -p /opt/python +build_cpythons $CPYTHON_VERSIONS + +PY35_BIN=/opt/python/cp35-cp35m/bin +# NOTE Since our custom manylinux image builds pythons with shared +# libpython, we need to add libpython's dir to LD_LIBRARY_PATH before running +# python. +ORIGINAL_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}" +LD_LIBRARY_PATH="${ORIGINAL_LD_LIBRARY_PATH}:$(dirname ${PY35_BIN})/lib" + +# Our openssl doesn't know how to find the system CA trust store +# (https://github.com/pypa/manylinux/issues/53) +# And it's not clear how up-to-date that is anyway +# So let's just use the same one pip and everyone uses +LD_LIBRARY_PATH="${ORIGINAL_LD_LIBRARY_PATH}:$(dirname ${PY35_BIN})/lib" $PY35_BIN/pip install certifi +ln -s $($PY35_BIN/python -c 'import certifi; print(certifi.where())') \ + /opt/_internal/certs.pem +# If you modify this line you also have to modify the versions in the +# Dockerfiles: +export SSL_CERT_FILE=/opt/_internal/certs.pem + +# Install newest curl +build_curl $CURL_ROOT $CURL_HASH +rm -rf /usr/local/include/curl /usr/local/lib/libcurl* /usr/local/lib/pkgconfig/libcurl.pc +hash -r +curl --version +curl-config --features + +# Now we can delete our built SSL +rm -rf /usr/local/ssl + +# Install patchelf (latest with unreleased bug fixes) +curl -sLO https://nipy.bic.berkeley.edu/manylinux/patchelf-0.9njs2.tar.gz +check_sha256sum patchelf-0.9njs2.tar.gz $PATCHELF_HASH +tar -xzf patchelf-0.9njs2.tar.gz +(cd patchelf-0.9njs2 && ./configure && make && make install) +rm -rf patchelf-0.9njs2.tar.gz patchelf-0.9njs2 + +# Install latest pypi release of auditwheel +LD_LIBRARY_PATH="${ORIGINAL_LD_LIBRARY_PATH}:$(dirname ${PY35_BIN})/lib" $PY35_BIN/pip install auditwheel +ln -s $PY35_BIN/auditwheel /usr/local/bin/auditwheel + +# Clean up development headers and other unnecessary stuff for +# final image +yum -y erase wireless-tools gtk2 libX11 hicolor-icon-theme \ + avahi freetype bitstream-vera-fonts \ + ${PYTHON_COMPILE_DEPS} > /dev/null 2>&1 +yum -y install ${MANYLINUX1_DEPS} +yum -y clean all > /dev/null 2>&1 +yum list installed +# we don't need libpython*.a, and they're many megabytes +find /opt/_internal -name '*.a' -print0 | xargs -0 rm -f +# Strip what we can -- and ignore errors, because this just attempts to strip +# *everything*, including non-ELF files: +find /opt/_internal -type f -print0 \ + | xargs -0 -n1 strip --strip-unneeded 2>/dev/null || true +# We do not need the Python test suites, or indeed the precompiled .pyc and +# .pyo files. Partially cribbed from: +# https://github.com/docker-library/python/blob/master/3.4/slim/Dockerfile +find /opt/_internal \ + \( -type d -a -name test -o -name tests \) \ + -o \( -type f -a -name '*.pyc' -o -name '*.pyo' \) \ + -print0 | xargs -0 rm -f + +for PYTHON in /opt/python/*/bin/python; do + # Add matching directory of libpython shared library to library lookup path + LD_LIBRARY_PATH="${ORIGINAL_LD_LIBRARY_PATH}:$(dirname $(dirname ${PYTHON}))/lib" + + # Smoke test to make sure that our Pythons work, and do indeed detect as + # being manylinux compatible: + LD_LIBRARY_PATH="${ORIGINAL_LD_LIBRARY_PATH}:$(dirname $(dirname ${PYTHON}))/lib" $PYTHON $MY_DIR/manylinux1-check.py + # Make sure that SSL cert checking works + LD_LIBRARY_PATH="${ORIGINAL_LD_LIBRARY_PATH}:$(dirname $(dirname ${PYTHON}))/lib" $PYTHON $MY_DIR/ssl-check.py +done + +# Restore LD_LIBRARY_PATH +LD_LIBRARY_PATH="${ORIGINAL_LD_LIBRARY_PATH}" diff --git a/tools/manylinux1/build_scripts/build_utils.sh b/tools/manylinux1/build_scripts/build_utils.sh new file mode 100755 index 00000000000..10422ae3bd0 --- /dev/null +++ b/tools/manylinux1/build_scripts/build_utils.sh @@ -0,0 +1,173 @@ +#!/bin/bash +# Helper utilities for build + +PYTHON_DOWNLOAD_URL=https://www.python.org/ftp/python +# XXX: the official https server at www.openssl.org cannot be reached +# with the old versions of openssl and curl in Centos 5.11 hence the fallback +# to the ftp mirror: +# OPENSSL_DOWNLOAD_URL=ftp://ftp.openssl.org/source +OPENSSL_DOWNLOAD_URL=https://www.openssl.org/source +# Ditto the curl sources +CURL_DOWNLOAD_URL=http://curl.askapache.com/download + +GET_PIP_URL=https://bootstrap.pypa.io/get-pip.py + +AUTOCONF_DOWNLOAD_URL=http://ftp.gnu.org/gnu/autoconf + + +function check_var { + if [ -z "$1" ]; then + echo "required variable not defined" + exit 1 + fi +} + + +function lex_pyver { + # Echoes Python version string padded with zeros + # Thus: + # 3.2.1 -> 003002001 + # 3 -> 003000000 + echo $1 | awk -F "." '{printf "%03d%03d%03d", $1, $2, $3}' +} + + +function do_cpython_build { + local py_ver=$1 + check_var $py_ver + local ucs_setting=$2 + check_var $ucs_setting + tar -xzf Python-$py_ver.tgz + pushd Python-$py_ver + if [ "$ucs_setting" = "none" ]; then + unicode_flags="" + dir_suffix="" + else + local unicode_flags="--enable-unicode=$ucs_setting" + local dir_suffix="-$ucs_setting" + fi + local prefix="/opt/_internal/cpython-${py_ver}${dir_suffix}" + mkdir -p ${prefix}/lib + # -Wformat added for https://bugs.python.org/issue17547 on Python 2.6 + + # NOTE --enable-shared for generating libpython shared library needed for + # linking of some of the nupic.core test executables. + CFLAGS="-Wformat" ./configure --prefix=${prefix} --enable-shared $unicode_flags > /dev/null + make -j2 > /dev/null + make install > /dev/null + popd + echo "ZZZ looking for libpython" + find / -name 'libpython*.so*' + rm -rf Python-$py_ver + # Some python's install as bin/python3. Make them available as + # bin/python. + if [ -e ${prefix}/bin/python3 ]; then + ln -s python3 ${prefix}/bin/python + fi + # NOTE Make libpython shared library visible to python calls below + LD_LIBRARY_PATH="${prefix}/lib" ${prefix}/bin/python get-pip.py + LD_LIBRARY_PATH="${prefix}/lib" ${prefix}/bin/pip install wheel + local abi_tag=$(LD_LIBRARY_PATH="${prefix}/lib" ${prefix}/bin/python ${MY_DIR}/python-tag-abi-tag.py) + ln -s ${prefix} /opt/python/${abi_tag} +} + + +function build_cpython { + local py_ver=$1 + check_var $py_ver + check_var $PYTHON_DOWNLOAD_URL + wget -q $PYTHON_DOWNLOAD_URL/$py_ver/Python-$py_ver.tgz + if [ $(lex_pyver $py_ver) -lt $(lex_pyver 3.3) ]; then + # NOTE We only need wide unicode for nupic.bindings wheel + do_cpython_build $py_ver ucs2 + do_cpython_build $py_ver ucs4 + else + do_cpython_build $py_ver none + fi + rm -f Python-$py_ver.tgz +} + + +function build_cpythons { + check_var $GET_PIP_URL + curl -sLO $GET_PIP_URL + for py_ver in $@; do + build_cpython $py_ver + done + rm get-pip.py +} + + +function do_openssl_build { + ./config no-ssl2 no-shared -fPIC --prefix=/usr/local/ssl > /dev/null + make > /dev/null + make install > /dev/null +} + + +function check_sha256sum { + local fname=$1 + check_var ${fname} + local sha256=$2 + check_var ${sha256} + + echo "${sha256} ${fname}" > ${fname}.sha256 + sha256sum -c ${fname}.sha256 + rm ${fname}.sha256 +} + + +function build_openssl { + local openssl_fname=$1 + check_var ${openssl_fname} + local openssl_sha256=$2 + check_var ${openssl_sha256} + check_var ${OPENSSL_DOWNLOAD_URL} + curl -sLO ${OPENSSL_DOWNLOAD_URL}/${openssl_fname}.tar.gz + check_sha256sum ${openssl_fname}.tar.gz ${openssl_sha256} + tar -xzf ${openssl_fname}.tar.gz + (cd ${openssl_fname} && do_openssl_build) + rm -rf ${openssl_fname} ${openssl_fname}.tar.gz +} + + +function do_curl_build { + LIBS=-ldl ./configure --with-ssl --disable-shared > /dev/null + make > /dev/null + make install > /dev/null +} + + +function build_curl { + local curl_fname=$1 + check_var ${curl_fname} + local curl_sha256=$2 + check_var ${curl_sha256} + check_var ${CURL_DOWNLOAD_URL} + curl -sLO ${CURL_DOWNLOAD_URL}/${curl_fname}.tar.bz2 + check_sha256sum ${curl_fname}.tar.bz2 ${curl_sha256} + tar -jxf ${curl_fname}.tar.bz2 + (cd ${curl_fname} && do_curl_build) + rm -rf ${curl_fname} ${curl_fname}.tar.bz2 +} + + +function do_standard_install { + ./configure > /dev/null + make > /dev/null + make install > /dev/null +} + + +function build_autoconf { + local autoconf_fname=$1 + check_var ${autoconf_fname} + local autoconf_sha256=$2 + check_var ${autoconf_sha256} + check_var ${AUTOCONF_DOWNLOAD_URL} + curl -sLO ${AUTOCONF_DOWNLOAD_URL}/${autoconf_fname}.tar.gz + check_sha256sum ${autoconf_fname}.tar.gz ${autoconf_sha256} + tar -zxf ${autoconf_fname}.tar.gz + (cd ${autoconf_fname} && do_standard_install) + rm -rf ${autoconf_fname} ${autoconf_fname}.tar.gz +} diff --git a/tools/manylinux1/build_scripts/manylinux1-check.py b/tools/manylinux1/build_scripts/manylinux1-check.py new file mode 100644 index 00000000000..47fd3d673be --- /dev/null +++ b/tools/manylinux1/build_scripts/manylinux1-check.py @@ -0,0 +1,56 @@ +# Logic copied from PEP 513 + + +def is_manylinux1_compatible(): + # Only Linux, and only x86-64 / i686 + from distutils.util import get_platform + if get_platform() not in ["linux-x86_64", "linux-i686"]: + return False + + # Check for presence of _manylinux module + try: + import _manylinux + return bool(_manylinux.manylinux1_compatible) + except (ImportError, AttributeError): + # Fall through to heuristic check below + pass + + # Check glibc version. CentOS 5 uses glibc 2.5. + return have_compatible_glibc(2, 5) + + +def have_compatible_glibc(major, minimum_minor): + import ctypes + + process_namespace = ctypes.CDLL(None) + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return False + + # Call gnu_get_libc_version, which returns a string like "2.5". + gnu_get_libc_version.restype = ctypes.c_char_p + version_str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + # Parse string and check against requested version. + version = [int(piece) for piece in version_str.split(".")] + assert len(version) == 2 + if major != version[0]: + return False + if minimum_minor > version[1]: + return False + return True + + +import sys +if is_manylinux1_compatible(): + print("%s is manylinux1 compatible" % (sys.executable, )) + sys.exit(0) +else: + print("%s is NOT manylinux1 compatible" % (sys.executable, )) + sys.exit(1) diff --git a/tools/manylinux1/build_scripts/python-tag-abi-tag.py b/tools/manylinux1/build_scripts/python-tag-abi-tag.py new file mode 100644 index 00000000000..301fbf07a47 --- /dev/null +++ b/tools/manylinux1/build_scripts/python-tag-abi-tag.py @@ -0,0 +1,7 @@ +# Utility script to print the python tag + the abi tag for a Python +# See PEP 425 for exactly what these are, but an example would be: +# cp27-cp27mu + +from wheel.pep425tags import get_abbr_impl, get_impl_ver, get_abi_tag + +print("{0}{1}-{2}".format(get_abbr_impl(), get_impl_ver(), get_abi_tag())) diff --git a/tools/manylinux1/build_scripts/ssl-check.py b/tools/manylinux1/build_scripts/ssl-check.py new file mode 100644 index 00000000000..a85d91978c5 --- /dev/null +++ b/tools/manylinux1/build_scripts/ssl-check.py @@ -0,0 +1,32 @@ +# cf. https://github.com/pypa/manylinux/issues/53 + +GOOD_SSL = "https://google.com" +BAD_SSL = "https://self-signed.badssl.com" + +import sys + +print("Testing SSL certificate checking for Python:", sys.version) + +if (sys.version_info[:2] < (2, 7) or sys.version_info[:2] < (3, 4)): + print("This version never checks SSL certs; skipping tests") + sys.exit(0) + +if sys.version_info[0] >= 3: + from urllib.request import urlopen + EXC = OSError +else: + from urllib import urlopen + EXC = IOError + +print("Connecting to %s should work" % (GOOD_SSL, )) +urlopen(GOOD_SSL) +print("...it did, yay.") + +print("Connecting to %s should fail" % (BAD_SSL, )) +try: + urlopen(BAD_SSL) + # If we get here then we failed: + print("...it DIDN'T!!!!!11!!1one!") + sys.exit(1) +except EXC: + print("...it did, yay.") -- GitLab From b1f24660bf14fe0f174b9e59f796ad5eaf2627f4 Mon Sep 17 00:00:00 2001 From: Xi Chen Date: Thu, 11 Jan 2018 15:40:41 -0800 Subject: [PATCH 0229/2305] add dist demo fit_a_line --- .../book_distribute/test_dist_fit_a_line.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py b/python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py new file mode 100644 index 00000000000..592986b137a --- /dev/null +++ b/python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py @@ -0,0 +1,63 @@ +import numpy as np +import paddle.v2 as paddle +import paddle.v2.fluid as fluid +import os + +x = fluid.layers.data(name='x', shape=[13], dtype='float32') + +y_predict = fluid.layers.fc(input=x, size=1, act=None) + +y = fluid.layers.data(name='y', shape=[1], dtype='float32') + +cost = fluid.layers.square_error_cost(input=y_predict, label=y) +avg_cost = fluid.layers.mean(x=cost) + +sgd_optimizer = fluid.optimizer.SGD(learning_rate=0.001) +optimize_ops, params_grads = sgd_optimizer.minimize(avg_cost) + +BATCH_SIZE = 20 + +train_reader = paddle.batch( + paddle.reader.shuffle( + paddle.dataset.uci_housing.train(), buf_size=500), + batch_size=BATCH_SIZE) + +place = fluid.CPUPlace() +feeder = fluid.DataFeeder(place=place, feed_list=[x, y]) +exe = fluid.Executor(place) + +t = fluid.DistributeTranspiler() +# all parameter server endpoints list for spliting parameters +pserver_endpoints = os.getenv("PSERVERS") +# server endpoint for current node +current_endpoint = os.getenv("SERVER_ENDPOINT") +# run as trainer or parameter server +training_role = os.getenv("TRAINING_ROLE", + "TRAINER") # get the training role: trainer/pserver +t.transpile(optimize_ops, params_grads, pservers=pserver_endpoints, trainers=2) + +if training_role == "PSERVER": + if not current_endpoint: + print("need env SERVER_ENDPOINT") + exit(1) + pserver_prog = t.get_pserver_program(current_endpoint, optimize_ops) + exe.run(fluid.default_startup_program()) + exe.run(pserver_prog) +else: + trainer_prog = t.get_trainer_program() + + exe.run(fluid.default_startup_program()) + + PASS_NUM = 100 + for pass_id in range(PASS_NUM): + fluid.io.save_persistables(exe, "./fit_a_line.model/") + fluid.io.load_persistables(exe, "./fit_a_line.model/") + for data in train_reader(): + avg_loss_value, = exe.run(trainer_prog, + feed=feeder.feed(data), + fetch_list=[avg_cost]) + + if avg_loss_value[0] < 10.0: + exit( + 0) # if avg cost less than 10.0, we think our code is good. +exit(1) -- GitLab From cadb95fbebad0a5cab32870add3223aca1169cfb Mon Sep 17 00:00:00 2001 From: Xi Chen Date: Thu, 11 Jan 2018 15:45:17 -0800 Subject: [PATCH 0230/2305] minor tweaks --- .../v2/fluid/tests/book_distribute/test_dist_fit_a_line.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py b/python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py index 592986b137a..bb339c440bd 100644 --- a/python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py +++ b/python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py @@ -58,6 +58,5 @@ else: fetch_list=[avg_cost]) if avg_loss_value[0] < 10.0: - exit( - 0) # if avg cost less than 10.0, we think our code is good. + exit(0) exit(1) -- GitLab From 777036d7d3033b7de136e07073a41fb9de49fbd2 Mon Sep 17 00:00:00 2001 From: Abhinav Arora Date: Thu, 11 Jan 2018 17:05:20 -0800 Subject: [PATCH 0231/2305] Fix some errors in the Block design doc (#7460) --- doc/design/block.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/design/block.md b/doc/design/block.md index fab7f2dc481..907a2def557 100644 --- a/doc/design/block.md +++ b/doc/design/block.md @@ -202,8 +202,8 @@ This `OpDesc` value is in the `ops` field of the `BlockDesc` value representing During the generation of the Protobuf message, the Block should store VarDesc (the Protobuf message which describes Variable) and OpDesc (the Protobuf message which describes Operator). -VarDesc in a block should have its name scope to avoid local variables affect parent block's name scope. -Child block's name scopes should inherit the parent's so that OpDesc in child block can reference a VarDesc that stored in parent block. For example: +VarDesc in a block should have its name scope to avoid local variables affecting parent block's name scope. +Child block's name scopes should inherit the parent's so that OpDesc in child block can reference a VarDesc that is stored in the parent block. For example: ```python a = pd.Variable(shape=[20, 20]) -- GitLab From e0ae76f222c5dd2484ed0ee9b5d013ff937c5cbb Mon Sep 17 00:00:00 2001 From: Abhinav Arora Date: Thu, 11 Jan 2018 17:05:41 -0800 Subject: [PATCH 0232/2305] Fixing concepts in the Var Desc design document (#7462) --- doc/design/var_desc.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/design/var_desc.md b/doc/design/var_desc.md index 0b2958c1b10..89fa95326c5 100644 --- a/doc/design/var_desc.md +++ b/doc/design/var_desc.md @@ -1,12 +1,12 @@ ## Background -PaddlePaddle divides the description of neural network computation graph into two stages: compile time and runtime. +PaddlePaddle divides the description of neural network computation into two stages: compile time and runtime. At compile time, the neural network computation is described as a `ProgramDesc` whereas at runtime an `Executor` interprets the `ProgramDesc` to compute the operations. -PaddlePaddle use proto message to describe compile time graph because +PaddlePaddle use proto message to describe compile time program because -1. Computation graph should be able to be saved to a file. -1. In distributed training, the graph will be serialized and send to multiple workers. +1. The computation program description must be serializable and saved in a file. +1. During distributed training, the sreialized program will be sent to multiple workers. It should also be possible to break the program into different components, each of which can be executed on different workers. -The computation graph is constructed by Data Node and Operation Node. The concept to represent them is in the table below. +The computation `Program` consists of nested `Blocks`. Each `Block` will consist of data(i.e. `Variable`) and `Operations`. The concept to represent them is in the table below. | |compile time|runtime| |---|---|---| -- GitLab From 25c8739362e9a9de14260bdeaa5cb6310cf6b052 Mon Sep 17 00:00:00 2001 From: Kavya Srinet Date: Thu, 11 Jan 2018 17:59:57 -0800 Subject: [PATCH 0233/2305] Adding the rst file to render IO under Fluid in paddlepaddle.org --- doc/api/v2/fluid.rst | 2 +- doc/api/v2/fluid/io.rst | 10 ++++++++++ python/paddle/v2/fluid/io.py | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 doc/api/v2/fluid/io.rst diff --git a/doc/api/v2/fluid.rst b/doc/api/v2/fluid.rst index 43fc19dc492..5f15cad2b53 100644 --- a/doc/api/v2/fluid.rst +++ b/doc/api/v2/fluid.rst @@ -15,4 +15,4 @@ Fluid fluid/param_attr.rst fluid/profiler.rst fluid/regularizer.rst - + fluid/io.rst diff --git a/doc/api/v2/fluid/io.rst b/doc/api/v2/fluid/io.rst new file mode 100644 index 00000000000..861822d36d8 --- /dev/null +++ b/doc/api/v2/fluid/io.rst @@ -0,0 +1,10 @@ +=========== +IO +=========== + + + +isParameter +----------- +.. autofunction:: paddle.v2.fluid.io.is_parameter + :noindex: diff --git a/python/paddle/v2/fluid/io.py b/python/paddle/v2/fluid/io.py index c63567601ac..0d5f68336ac 100644 --- a/python/paddle/v2/fluid/io.py +++ b/python/paddle/v2/fluid/io.py @@ -11,6 +11,16 @@ __all__ = [ def is_parameter(var): + """Check whether the variable is a Parameter + + This function checks whether the input variable is a Parameter. + + Args: + var : The input variable. + + Returns: + boolean result whether the variable is a Parameter + """ return isinstance(var, Parameter) -- GitLab From 5dbd537048f153863d1cac0fbb433bb62fe561b4 Mon Sep 17 00:00:00 2001 From: Yancey Date: Fri, 12 Jan 2018 11:25:53 +0800 Subject: [PATCH 0234/2305] Fluid distributed training benchmark (#7410) * add cluster training bencharmk design * update by comment * update by comment --- benchmark/cluster/README.md | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 benchmark/cluster/README.md diff --git a/benchmark/cluster/README.md b/benchmark/cluster/README.md new file mode 100644 index 00000000000..b619613ea7a --- /dev/null +++ b/benchmark/cluster/README.md @@ -0,0 +1,78 @@ +# Cluster Training Benchmark + +## Setup + +- Platform + - Kubernetes: v1.6.2 + - Linux Kernel: v3.10.0 + +- Resource + - CPU: 10 Cores per Pod + - Memory: 5GB per Pod + +- Docker Image + + We use different base Docker Image to run the benchmark on Kubernetes: + - PaddlePaddle v2: paddlepaddle/paddle:0.11.0 + - PaddlePaddle Fluid: paddlepaddle/paddle:[commit-id] + - TensorFlow: tensorflow/tensorflow:1.5.0-rc0 + +- Model + vgg16 is used in this benchmark. + +## Cases + +- Variable + - Batch Size of training data. + - PServer count of the training job. + - The number of trainers. + +- Invariant + - The resource of trainer/pserver Pod. + +### Measure the Performance for Different Batch Size + +- PServer Count: 40 +- Trainer Count: 100 +- Metrics: mini-batch / sec + +| Batch Size | 32 | 64 | 128 | 256 | +| -- | -- | -- | -- | -- | +| PaddlePaddle Fluid | - | - | - | - | +| PaddlePaddle v2 | - | - | - | - | +| TensorFlow | - | - | - | - | + +### Measure the Performance for Different PServer Count + +- Trainer Count: 100 +- Batch Size: 64 +- Metrics: mini-batch / sec + +| PServer Count | 10 | 20 | 40 | 60 | +| -- | -- | -- | -- | -- | +| PaddlePaddle Fluid | - | - | - | - | +| PaddlePaddle v2 | - | - | - | - | +| TensorFlow | - | - | - | - | + +### Measure Parallel Efficiency By Increasing Trainer Count + +- PServer Count: 20 +- Batch Size: 64 +- Metrics: + +$S = \div(T1, TN)$ + +which S is the ratio of T1 over TN, training time of 1 and N trainers. +The parallel efficiency is: + +$E = \div(S, N)$ + +| Trainer Counter | 1 | 10 | 20 | 30 | 40 | 50 | 60 | 70 | 80 | 90 | 100 | +| -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | +| PaddlePaddle Fluid | - | - | - | - | - | - | - | - | - | - | - | +| PaddlePaddle v2 | - | - | - | - | - | - | - | - | - | - | - | - | +| TensorFlow | - | - | - | - | - | - | - | - | - | - | - | - | - | + +## Reproduce the benchmark + +TODO -- GitLab From 208f950ccc7b5508415a5dfb720348a659189f99 Mon Sep 17 00:00:00 2001 From: Yang Yang Date: Fri, 12 Jan 2018 03:30:06 +0000 Subject: [PATCH 0235/2305] delete todo --- paddle/framework/lod_tensor.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index 3d1b75597e5..a6a445efe89 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -221,7 +221,6 @@ void DeserializeFromStream(std::istream &is, LoDTensor *tensor, DeserializeFromStream(is, static_cast(tensor), dev_ctx); } -// TODO(tonyyang-svail): make this function support LoD std::vector LoDTensor::SplitLoDTensor( const std::vector places) const { check_memory_size(); -- GitLab From 54791af658d6fe0281ae4fc7ccc0723c21ef282a Mon Sep 17 00:00:00 2001 From: Kavya Srinet Date: Thu, 11 Jan 2018 19:54:00 -0800 Subject: [PATCH 0236/2305] Addressing review comments --- doc/api/v2/fluid/io.rst | 2 +- python/paddle/v2/fluid/io.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/api/v2/fluid/io.rst b/doc/api/v2/fluid/io.rst index 861822d36d8..67f68c4e9e1 100644 --- a/doc/api/v2/fluid/io.rst +++ b/doc/api/v2/fluid/io.rst @@ -4,7 +4,7 @@ IO -isParameter +is_parameter ----------- .. autofunction:: paddle.v2.fluid.io.is_parameter :noindex: diff --git a/python/paddle/v2/fluid/io.py b/python/paddle/v2/fluid/io.py index 0d5f68336ac..eef1e283c2c 100644 --- a/python/paddle/v2/fluid/io.py +++ b/python/paddle/v2/fluid/io.py @@ -11,7 +11,7 @@ __all__ = [ def is_parameter(var): - """Check whether the variable is a Parameter + """Check whether the variable is a Parameter. This function checks whether the input variable is a Parameter. @@ -19,7 +19,7 @@ def is_parameter(var): var : The input variable. Returns: - boolean result whether the variable is a Parameter + boolean result whether the variable is a Parameter. """ return isinstance(var, Parameter) -- GitLab From 0fad7647d7eb883c849ce6468ba5e781276e9358 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Fri, 12 Jan 2018 12:06:32 +0800 Subject: [PATCH 0237/2305] add how to update whl in build_from_source.md --- doc/getstarted/build_and_install/build_from_source_cn.rst | 5 +++++ doc/getstarted/build_and_install/build_from_source_en.rst | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/doc/getstarted/build_and_install/build_from_source_cn.rst b/doc/getstarted/build_and_install/build_from_source_cn.rst index 41ac07ca567..1e75087462b 100644 --- a/doc/getstarted/build_and_install/build_from_source_cn.rst +++ b/doc/getstarted/build_and_install/build_from_source_cn.rst @@ -32,6 +32,11 @@ PaddlePaddle主要使用 `CMake `_ 以及GCC, G++作为编译 pip install build/python/dist/*.whl +如果机器中已经安装过PaddlePaddle,请卸载再安装: + +.. code-block:: bash + + pip install build/python/dist/*.whl -U .. _run_test: diff --git a/doc/getstarted/build_and_install/build_from_source_en.rst b/doc/getstarted/build_and_install/build_from_source_en.rst index 92211aee8c3..fde8a18daa2 100644 --- a/doc/getstarted/build_and_install/build_from_source_en.rst +++ b/doc/getstarted/build_and_install/build_from_source_en.rst @@ -36,6 +36,11 @@ machine or copy it to the target machine. pip install build/python/dist/*.whl +If the machine has installed PaddlePaddle before, please uninstall it first and reinstall it again. + +.. code-block:: bash + + pip install build/python/dist/*.whl -U .. _run_test: -- GitLab From 5faebab375d5e039f5f7cc3169b8de8167494d31 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Fri, 12 Jan 2018 14:18:55 +0800 Subject: [PATCH 0238/2305] Done, need support selectedrows --- .../paddle/v2/fluid/distribute_transpiler.py | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index 59e74e0d6f2..d17f9815cca 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -148,7 +148,7 @@ class DistributeTranspiler: concat = program.global_block().append_op( type="concat", inputs={"X": splited_var}, - outputs={"Out": orig_param}, + outputs={"Out": [orig_param]}, attrs={"axis": 0}) def _create_vars_from_blocklist(self, program, block_list): @@ -420,7 +420,6 @@ class DistributeTranspiler: else: self._append_pserver_non_opt_ops(optimize_sub_program, pserver_program, opt_op) - print("****subprogram", optimize_sub_program) pserver_program.global_block().append_op( type="recv", inputs={"RX": self.param_grad_ep_mapping[endpoint]["grads"] @@ -463,7 +462,6 @@ class DistributeTranspiler: pserver_vars = pserver_program.global_block().vars created_var_map = dict() for _, var in pserver_vars.iteritems(): - print("create var for startup", var.name, var.shape) tmpvar = s_prog.global_block().create_var( name=var.name, persistable=var.persistable, @@ -485,21 +483,11 @@ class DistributeTranspiler: op_on_pserver = True new_outputs[key] = pserver_vars[var.name] - # newname, _ = _get_splited_name_and_shape(var.name) - # if newname: - # print("updating output", newname, created_var_map[newname]) - # new_outputs[key] = created_var_map[newname] - # else: - # print("no update output", key, var) - # new_outputs[key] = var - # if var.name in created_var_map or \ - # newname: - # op_on_pserver = True - if op_on_pserver: - if op.type in ["gaussian_random", "fill_constant"]: + if op.type in [ + "gaussian_random", "fill_constant", "uniform_random" + ]: op.attrs["shape"] = new_outputs["Out"].shape - print("updated shape", op.attrs["shape"]) s_prog.global_block().append_op( type=op.type, inputs=op.inputs, -- GitLab From 72091aa5f8d02e45bd6eeed4e4058bf0c4ab1dd7 Mon Sep 17 00:00:00 2001 From: ying Date: Fri, 12 Jan 2018 14:31:38 +0800 Subject: [PATCH 0239/2305] fix display error of C-API doc. --- doc/howto/usage/capi/workflow_of_capi_cn.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/howto/usage/capi/workflow_of_capi_cn.md b/doc/howto/usage/capi/workflow_of_capi_cn.md index c1c2c86d0c5..449bda76e81 100644 --- a/doc/howto/usage/capi/workflow_of_capi_cn.md +++ b/doc/howto/usage/capi/workflow_of_capi_cn.md @@ -26,10 +26,9 @@ ### 准备预测模型 -在准备预测模型部分的介绍,我们以手写数字识别任务为例。手写数字识别任务定义了一个含有[两个隐层的简单全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。完整代码可以查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 中的相关脚本。 +准备预测模型部分,我们以手写数字识别任务为例进行介绍。手写数字识别任务定义了一个含有[两个隐层的简单全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。完整代码可以查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 中的相关脚本。 -调用C-API开发预测程序需要一个训练好的模型,在终端执行`python mnist_v2.py` -运行[目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 会使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。训练好的模型默认保存在当前运行目录下的`models`目录中。 +调用C-API开发预测程序需要一个训练好的模型,运行[MNIST手写数字识别目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)下的[mnist_v2.py](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/dense/mnist_v2.py)脚本,在终端执行`python mnist_v2.py`,会使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。训练好的模型默认保存在当前运行目录下的`models`目录中。 下面,我们将训练结束后存储下来的模型转换成预测模型。 @@ -113,7 +112,7 @@ C-API支持的所有输入数据类型和他们的组织方式,请参考“输 #### step 4. 前向计算 -完成上述准备之后,通过调用 `[paddle_gradient_machine_forward](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/gradient_machine.h#L73)` 接口完成神经网络的前向计算。 +完成上述准备之后,通过调用 [`paddle_gradient_machine_forward`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/gradient_machine.h#L73) 接口完成神经网络的前向计算。 #### step 5. 清理 -- GitLab From 7b117c1ab28ff9e6e49c8d17f87d6a2ef5cf584b Mon Sep 17 00:00:00 2001 From: ying Date: Fri, 12 Jan 2018 15:00:10 +0800 Subject: [PATCH 0240/2305] follow comments. --- doc/howto/usage/capi/workflow_of_capi_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/howto/usage/capi/workflow_of_capi_cn.md b/doc/howto/usage/capi/workflow_of_capi_cn.md index 449bda76e81..e0a42fff12c 100644 --- a/doc/howto/usage/capi/workflow_of_capi_cn.md +++ b/doc/howto/usage/capi/workflow_of_capi_cn.md @@ -28,7 +28,7 @@ 准备预测模型部分,我们以手写数字识别任务为例进行介绍。手写数字识别任务定义了一个含有[两个隐层的简单全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。完整代码可以查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense) 中的相关脚本。 -调用C-API开发预测程序需要一个训练好的模型,运行[MNIST手写数字识别目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)下的[mnist_v2.py](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/dense/mnist_v2.py)脚本,在终端执行`python mnist_v2.py`,会使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。训练好的模型默认保存在当前运行目录下的`models`目录中。 +调用C-API开发预测程序需要一个训练好的模型,运行[MNIST手写数字识别目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)下的[mnist_v2.py](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/dense/mnist_v2.py)脚本,在终端执行`python mnist_v2.py`,会使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。训练好的模型默认保存在当前运行目录下的`models`目录中。 下面,我们将训练结束后存储下来的模型转换成预测模型。 -- GitLab From 3423022e84fdb9eef76d051ed35524422f7a02b4 Mon Sep 17 00:00:00 2001 From: Yan Chunwei Date: Fri, 12 Jan 2018 15:24:07 +0800 Subject: [PATCH 0241/2305] feature/add print op (#6799) --- paddle/operators/CMakeLists.txt | 1 + paddle/operators/print_op.cc | 206 ++++++++++++++++++ python/paddle/v2/fluid/layers/control_flow.py | 57 ++++- python/paddle/v2/fluid/tests/test_print_op.py | 21 ++ 4 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 paddle/operators/print_op.cc create mode 100644 python/paddle/v2/fluid/tests/test_print_op.py diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index 5889a50db09..e1b695e8cd3 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -135,6 +135,7 @@ op_library(detection_output_op DEPS softmax) op_library(sequence_softmax_op DEPS softmax) op_library(sum_op DEPS selected_rows_functor) op_library(sgd_op DEPS selected_rows_functor) +op_library(print_op DEPS lod_tensor) op_library(adagrad_op DEPS selected_rows_functor) op_library(conv_op DEPS vol2col) op_library(pool_op DEPS pooling) diff --git a/paddle/operators/print_op.cc b/paddle/operators/print_op.cc new file mode 100644 index 00000000000..89e41d806c7 --- /dev/null +++ b/paddle/operators/print_op.cc @@ -0,0 +1,206 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. */ + +#include +#include + +#include "paddle/framework/op_registry.h" + +namespace paddle { +namespace operators { + +#define CLOG std::cout + +struct Formater { + std::string message; + std::string name; + std::vector dims; + std::type_index dtype{typeid(char)}; + framework::LoD lod; + int summarize; + void* data{nullptr}; + + void operator()(size_t size) { + PrintMessage(); + PrintName(); + PrintDims(); + PrintDtype(); + PrintLod(); + PrintData(size); + } + + private: + void PrintMessage() { CLOG << std::time(nullptr) << "\t" << message; } + void PrintName() { + if (!name.empty()) { + CLOG << "Tensor[" << name << "]" << std::endl; + } + } + void PrintDims() { + if (!dims.empty()) { + CLOG << "\tshape: ["; + for (auto i : dims) { + CLOG << i << ","; + } + CLOG << "]" << std::endl; + } + } + void PrintDtype() { + if (dtype.hash_code() != typeid(char).hash_code()) { + CLOG << "\tdtype: " << dtype.name() << std::endl; + } + } + void PrintLod() { + if (!lod.empty()) { + CLOG << "\tLoD: ["; + for (auto level : lod) { + CLOG << "[ "; + for (auto i : level) { + CLOG << i << ","; + } + CLOG << " ]"; + } + CLOG << "]" << std::endl; + } + } + + void PrintData(size_t size) { + PADDLE_ENFORCE_NOT_NULL(data); + // print float + if (dtype.hash_code() == typeid(float).hash_code()) { + Display(size); + } + if (dtype.hash_code() == typeid(double).hash_code()) { + Display(size); + } + if (dtype.hash_code() == typeid(int).hash_code()) { + Display(size); + } + if (dtype.hash_code() == typeid(int64_t).hash_code()) { + Display(size); + } + } + + template + void Display(size_t size) { + auto* d = (T*)data; + CLOG << "\tdata: "; + if (summarize != -1) { + summarize = std::min(size, (size_t)summarize); + for (int i = 0; i < summarize; i++) { + CLOG << d[i] << ","; + } + } else { + for (size_t i = 0; i < size; i++) { + CLOG << d[i] << ","; + } + } + CLOG << std::endl; + } +}; + +// TODO(ChunweiYan) there should be some other printers for TensorArray +class TensorPrintOp : public framework::OperatorBase { + public: + TensorPrintOp(const std::string& type, + const framework::VariableNameMap& inputs, + const framework::VariableNameMap& outputs, + const framework::AttributeMap& attrs) + : OperatorBase(type, inputs, outputs, attrs) {} + + TensorPrintOp(const TensorPrintOp& o) + : framework::OperatorBase( + static_cast(o)) { + PADDLE_THROW("Not implemented"); + } + + void Run(const framework::Scope& scope, + const platform::Place& place) const override { + // Only run the `first_n` times. + int first_n = Attr("first_n"); + if (first_n > 0 && ++times_ > first_n) return; + + PADDLE_ENFORCE(!Inputs("input").empty(), "input should be set"); + auto* input_var = scope.FindVar(Input("input")); + PADDLE_ENFORCE_NOT_NULL(input_var); + auto& tensor = input_var->Get(); + + // TODO(ChunweiYan) support GPU + PADDLE_ENFORCE(platform::is_cpu_place(tensor.place())); + + Formater formater; + if (Attr("print_tensor_name")) { + formater.name = Inputs("input").front(); + } + if (Attr("print_tensor_type")) { + formater.dtype = tensor.type(); + } + if (Attr("print_tensor_shape")) { + formater.dims.assign(tensor.dims()[0], + tensor.dims()[tensor.dims().size() - 1]); + } + if (Attr("print_tensor_lod")) { + formater.lod = tensor.lod(); + } + formater.summarize = Attr("summarize"); + formater.data = (void*)tensor.data(); + formater(tensor.numel()); + } + + private: + mutable int times_{0}; +}; + +class PrintOpProtoAndCheckMaker : public framework::OpProtoAndCheckerMaker { + public: + PrintOpProtoAndCheckMaker(OpProto* proto, OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("input", "the tensor that will be displayed."); + AddAttr("first_n", "Only log `first_n` number of times."); + AddAttr("message", "A string message to print as a prefix."); + AddAttr("summarize", "Print this number of elements in the tensor."); + AddAttr("print_tensor_name", "Whether to print the tensor name."); + AddAttr("print_tensor_type", "Whether to print the tensor's dtype."); + AddAttr("print_tensor_shape", "Whether to print the tensor's shape."); + AddAttr("print_tensor_lod", "Whether to print the tensor's lod."); + AddComment(R"DOC( + Creates a print op that will print when a tensor is accessed. + + Wraps the tensor passed in so that whenever that a tensor is accessed, + the message `message` is printed, along with the current value of the + tensor `t`.)DOC"); + } +}; + +class InferShape : public framework::InferShapeBase { + public: + void operator()(framework::InferShapeContext* context) const override { + PADDLE_ENFORCE(context->HasInput("input"), "input should be set"); + } +}; + +class InferVarType : public framework::VarTypeInference { + public: + void operator()(const framework::OpDesc& op_desc, + framework::BlockDesc* block) const override {} +}; + +} // namespace operators +} // namespace paddle + +REGISTER_OPERATOR(print, paddle::operators::TensorPrintOp, + paddle::operators::PrintOpProtoAndCheckMaker, + paddle::operators::InferShape, + paddle::operators::InferVarType, + paddle::framework::EmptyGradOpMaker); diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index 9ad021fa992..0cf17f3083a 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -12,7 +12,7 @@ __all__ = [ 'array_to_lod_tensor', 'increment', 'array_write', 'create_array', 'less_than', 'array_read', 'shrink_memory', 'array_length', 'IfElse', 'DynamicRNN', 'ConditionalBlock', 'StaticRNN', 'reorder_lod_tensor_by_rank', - 'ParallelDo' + 'ParallelDo', 'Print' ] @@ -110,6 +110,61 @@ def merge_lod_tensor(in_true, in_false, x, mask, level=0): return out +def Print(input, + first_n=-1, + message=None, + summarize=-1, + print_tensor_name=True, + print_tensor_type=True, + print_tensor_shape=True, + print_tensor_lod=True): + ''' + **Print operator** + + This creates a print op that will print when a tensor is accessed. + + Wraps the tensor passed in so that whenever that a tensor is accessed, + the message `message` is printed, along with the current value of the + tensor `t`. + + Args: + input(Variable): A Tensor to print. + summarize(int): Print this number of elements in the tensor, will print all + if left negative. + message(str): A string message to print as a prefix. + first_n(int): Only log `first_n` number of times. + print_tensor_name(bool): Print the tensor name. + print_tensor_type(bool): Print the tensor type. + print_tensor_shape(bool): Print the tensor shape. + print_tensor_lod(bool): Print the tensor lod. + + Returns: + None + + Examples: + .. code-block:: python + + value = some_layer(...) + Print(value, summarize=10, + message="The content of some_layer: ") + ''' + helper = LayerHelper('print', **locals()) + out = helper.create_tmp_variable(dtype='int32') + helper.append_op( + type='print', + inputs={'input': input}, + attrs={ + 'first_n': first_n, + 'summarize': summarize, + 'message': message or "", + 'print_tensor_name': print_tensor_name, + 'print_tensor_type': print_tensor_type, + 'print_tensor_shape': print_tensor_shape, + 'print_tensor_lod': print_tensor_lod, + }) + return out + + class BlockGuard(object): """ BlockGuard class. diff --git a/python/paddle/v2/fluid/tests/test_print_op.py b/python/paddle/v2/fluid/tests/test_print_op.py new file mode 100644 index 00000000000..86a701a020f --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_print_op.py @@ -0,0 +1,21 @@ +import unittest +import numpy as np +from paddle.v2.fluid.executor import Executor +import paddle.v2.fluid.core as core +import paddle.v2.fluid.layers as pd + + +class TestSumOp(unittest.TestCase): + def test_tensor(self): + i = pd.zeros(shape=[2, 10], dtype='float32') + + pd.Print(i, message="I am a message", summarize=10) + + cpu = core.CPUPlace() + exe = Executor(cpu) + + exe.run() + + +if __name__ == '__main__': + unittest.main() -- GitLab From 697f92ef86d3079fd82ec2fb9b8dc52fb8ef462a Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Fri, 12 Jan 2018 15:36:47 +0800 Subject: [PATCH 0242/2305] Add type check --- python/paddle/v2/fluid/clip.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/python/paddle/v2/fluid/clip.py b/python/paddle/v2/fluid/clip.py index 6ed97cbe647..3dccd74bd8e 100644 --- a/python/paddle/v2/fluid/clip.py +++ b/python/paddle/v2/fluid/clip.py @@ -40,6 +40,11 @@ def error_clip_callback(block, context): op_desc.output_arg_names()): fwd_var = block.var_recursive(grad_to_var[grad_n]) error_clip = getattr(fwd_var, "error_clip", None) + if not (error_clip is None or isinstance(error_clip, + BaseErrorClipAttr)): + raise TypeError( + "Variable's error_clip should be an instance of BaseErrorClipAttr or None." + ) if error_clip is not None: error_clip.append_clip_op(block, grad_n) -- GitLab From 23df6c447868e9c5d1fe2f0b04342ae377b7fafb Mon Sep 17 00:00:00 2001 From: Qiao Longfei Date: Fri, 12 Jan 2018 15:37:15 +0800 Subject: [PATCH 0243/2305] Add get lod for debug (#7375) * add GetLoD for debug * add LoDToString * optimize if * typo * add lod_tensor to operator's dependency --- paddle/framework/CMakeLists.txt | 2 +- paddle/framework/lod_tensor.cc | 6 ++++++ paddle/framework/lod_tensor.h | 2 ++ paddle/framework/operator.cc | 25 ++++++++++++++++++++++--- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt index ed5f6310f4e..597ea959f23 100644 --- a/paddle/framework/CMakeLists.txt +++ b/paddle/framework/CMakeLists.txt @@ -47,7 +47,7 @@ cc_test(op_proto_maker_test SRCS op_proto_maker_test.cc DEPS op_proto_maker) cc_library(op_info SRCS op_info.cc DEPS attribute framework_proto) cc_library(shape_inference SRCS shape_inference.cc DEPS ddim attribute device_context) cc_library(operator SRCS operator.cc DEPS op_info device_context tensor scope glog - shape_inference data_transform) + shape_inference data_transform lod_tensor) cc_test(operator_test SRCS operator_test.cc DEPS operator op_registry init) cc_library(proto_desc SRCS var_desc.cc op_desc.cc block_desc.cc program_desc.cc DEPS shape_inference op_info operator glog) diff --git a/paddle/framework/lod_tensor.cc b/paddle/framework/lod_tensor.cc index 7ae94c64653..87a57d09514 100644 --- a/paddle/framework/lod_tensor.cc +++ b/paddle/framework/lod_tensor.cc @@ -69,6 +69,12 @@ std::ostream &operator<<(std::ostream &os, const LoDTensor &t) { return os; } +std::string LoDToString(const LoD &lod) { + std::ostringstream stream; + stream << lod; + return stream.str(); +} + LoD SliceInLevel(const LoD &in, size_t level, size_t elem_begin, size_t elem_end) { PADDLE_ENFORCE_LT(level, in.size()); diff --git a/paddle/framework/lod_tensor.h b/paddle/framework/lod_tensor.h index 37753f5f4dd..88ea78f2682 100644 --- a/paddle/framework/lod_tensor.h +++ b/paddle/framework/lod_tensor.h @@ -60,6 +60,8 @@ using LoD = std::vector>; std::ostream& operator<<(std::ostream& os, const LoD& lod); std::ostream& operator<<(std::ostream& os, const LoDTensor& t); +std::string LoDToString(const LoD& lod); + LoD SliceInLevel(const LoD& in, size_t level, size_t elem_begin, size_t elem_end); /* diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index 7756a52ca9b..be1373dc2a8 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -80,7 +80,9 @@ static DDim GetDims(const Scope& scope, const std::string& name) { Variable* var = scope.FindVar(name); if (var == nullptr) { return DDim({-1}); - } else if (var->IsType()) { + } + + if (var->IsType()) { return var->Get().dims(); } else if (var->IsType()) { return var->Get().GetCompleteDims(); @@ -89,6 +91,21 @@ static DDim GetDims(const Scope& scope, const std::string& name) { } } +static LoD GetLoD(const Scope& scope, const std::string& name) { + Variable* var = scope.FindVar(name); + auto default_lod = LoD({{}}); + + if (var == nullptr) { + return default_lod; + } + + if (var->IsType()) { + return var->Get().lod(); + } else { + return default_lod; + } +} + std::string OperatorBase::Input(const std::string& name) const { auto& ins = Inputs(name); PADDLE_ENFORCE_LE(ins.size(), 1UL, @@ -130,7 +147,8 @@ std::string OperatorBase::DebugStringEx(const Scope* scope) const { for (size_t i = 0; i < input.second.size(); ++i) { ss << input.second[i]; if (scope) { - ss << "(" << GetDims(*scope, input.second[i]) << ")"; + ss << "[" << GetDims(*scope, input.second[i]) << "]"; + ss << "(" << GetLoD(*scope, input.second[i]) << ")"; } if (i != input.second.size() - 1) { ss << ", "; @@ -149,7 +167,8 @@ std::string OperatorBase::DebugStringEx(const Scope* scope) const { for (size_t i = 0; i < output.second.size(); ++i) { ss << output.second[i]; if (scope) { - ss << "(" << GetDims(*scope, output.second[i]) << ")"; + ss << "[" << GetDims(*scope, output.second[i]) << "]"; + ss << "(" << GetLoD(*scope, output.second[i]) << ")"; } if (i != output.second.size() - 1) { ss << ", "; -- GitLab From c24da0d3eefa1cf0a9dcee9b96fa120ca2e421b3 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Fri, 12 Jan 2018 16:30:45 +0800 Subject: [PATCH 0244/2305] update unit test split var --- python/paddle/v2/fluid/tests/CMakeLists.txt | 1 + .../paddle/v2/fluid/tests/book_distribute/test_split_var.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/python/paddle/v2/fluid/tests/CMakeLists.txt b/python/paddle/v2/fluid/tests/CMakeLists.txt index e795627bfe9..9a0240cbf65 100644 --- a/python/paddle/v2/fluid/tests/CMakeLists.txt +++ b/python/paddle/v2/fluid/tests/CMakeLists.txt @@ -5,3 +5,4 @@ foreach(src ${TEST_OPS}) endforeach() add_subdirectory(book) +add_subdirectory(book_distribute) diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py b/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py index 1355e13e1c1..cfb48a59154 100644 --- a/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py +++ b/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py @@ -2,6 +2,7 @@ import math import unittest from paddle.v2.fluid.distribute_transpiler import split_dense_variable import paddle.v2.fluid as fluid +import paddle.v2.fluid.core as core import random @@ -19,9 +20,9 @@ class TestSplitVar(unittest.TestCase): program = fluid.Program() for shape in shapes: var = program.global_block().create_var( - name=str(random.randint(10000)), + name=str(random.randint(10000, 99999)), persistable=True, - dtype=core.VarDesc.VarType.LOD_TENSOR, + # dtype=core.VarDesc.VarType.LOD_TENSOR, shape=shape) var_list.append(var) blocks = split_dense_variable(var_list, 10) -- GitLab From d23ea4ef8ebc637534c5abafca995be257f83751 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Fri, 12 Jan 2018 16:47:57 +0800 Subject: [PATCH 0245/2305] add gradient clip by norm --- python/paddle/v2/fluid/clip.py | 12 ++++++++++++ python/paddle/v2/fluid/layers/ops.py | 1 + 2 files changed, 13 insertions(+) diff --git a/python/paddle/v2/fluid/clip.py b/python/paddle/v2/fluid/clip.py index b1fd1c2b65f..eb75018d779 100644 --- a/python/paddle/v2/fluid/clip.py +++ b/python/paddle/v2/fluid/clip.py @@ -77,6 +77,18 @@ class GradientClipByValue(BaseGradientClipAttr): return param, new_grad +class GradientClipByNorm(BaseGradientClipAttr): + def __init__(self, clip_norm): + self.clip_norm = clip_norm + + def process_context(self, context, p_g): + pass + + def create_operators(self, param, grad): + new_grad = layers.clip_by_norm(x=grad, max_norm=self.clip_norm) + return param, new_grad + + def append_gradient_clip_ops(param_grad): context = dict() create_op_callbacks = [] diff --git a/python/paddle/v2/fluid/layers/ops.py b/python/paddle/v2/fluid/layers/ops.py index d3a5b707859..884e84011d9 100644 --- a/python/paddle/v2/fluid/layers/ops.py +++ b/python/paddle/v2/fluid/layers/ops.py @@ -16,6 +16,7 @@ __all__ = [ 'elementwise_sub', 'elementwise_mul', 'clip', + 'clip_by_norm', 'sequence_softmax', ] + __activations__ -- GitLab From 06b326b681e2d9a831e02a7f2f0a1e8c18c1bdeb Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Fri, 12 Jan 2018 17:07:11 +0800 Subject: [PATCH 0246/2305] follow comments --- python/paddle/v2/fluid/__init__.py | 3 ++- python/paddle/v2/fluid/distribute_transpiler_simple.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/python/paddle/v2/fluid/__init__.py b/python/paddle/v2/fluid/__init__.py index 5e01b871980..a14422ee92f 100644 --- a/python/paddle/v2/fluid/__init__.py +++ b/python/paddle/v2/fluid/__init__.py @@ -18,13 +18,14 @@ from param_attr import ParamAttr from data_feeder import DataFeeder from core import LoDTensor, CPUPlace, CUDAPlace from distribute_transpiler import DistributeTranspiler +from distribute_transpiler_simple import SimpleDistributeTranspiler import clip Tensor = LoDTensor __all__ = framework.__all__ + executor.__all__ + [ 'io', 'initializer', 'layers', 'nets', 'optimizer', 'backward', 'regularizer', 'LoDTensor', 'CPUPlace', 'CUDAPlace', 'Tensor', 'ParamAttr' - 'DataFeeder', 'clip', 'DistributeTranspiler' + 'DataFeeder', 'clip', 'SimpleDistributeTranspiler', 'DistributeTranspiler' ] diff --git a/python/paddle/v2/fluid/distribute_transpiler_simple.py b/python/paddle/v2/fluid/distribute_transpiler_simple.py index 49ece7b725e..32db3df9aa2 100644 --- a/python/paddle/v2/fluid/distribute_transpiler_simple.py +++ b/python/paddle/v2/fluid/distribute_transpiler_simple.py @@ -48,7 +48,7 @@ def round_robin(params_grads, pserver_endpoints): return param_grad_map -class DistributeTranspiler: +class SimpleDistributeTranspiler: def transpile(self, optimize_ops, params_grads, -- GitLab From 5528ec13212a1171c8274769b3b6e1842cc6861f Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Fri, 12 Jan 2018 17:10:52 +0800 Subject: [PATCH 0247/2305] add another method of update whl --- doc/getstarted/build_and_install/build_from_source_cn.rst | 7 ++++++- doc/getstarted/build_and_install/build_from_source_en.rst | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/getstarted/build_and_install/build_from_source_cn.rst b/doc/getstarted/build_and_install/build_from_source_cn.rst index 1e75087462b..71904dc41ed 100644 --- a/doc/getstarted/build_and_install/build_from_source_cn.rst +++ b/doc/getstarted/build_and_install/build_from_source_cn.rst @@ -32,10 +32,15 @@ PaddlePaddle主要使用 `CMake `_ 以及GCC, G++作为编译 pip install build/python/dist/*.whl -如果机器中已经安装过PaddlePaddle,请卸载再安装: +如果机器中已经安装过PaddlePaddle,有两种方法: .. code-block:: bash + 1. 先卸载之前的版本,再重新安装 + pip uninstall paddlepaddle + pip install build/python/dist/*.whl + + 2. 直接升级到更新的版本 pip install build/python/dist/*.whl -U .. _run_test: diff --git a/doc/getstarted/build_and_install/build_from_source_en.rst b/doc/getstarted/build_and_install/build_from_source_en.rst index fde8a18daa2..27f73b2e2c0 100644 --- a/doc/getstarted/build_and_install/build_from_source_en.rst +++ b/doc/getstarted/build_and_install/build_from_source_en.rst @@ -36,10 +36,15 @@ machine or copy it to the target machine. pip install build/python/dist/*.whl -If the machine has installed PaddlePaddle before, please uninstall it first and reinstall it again. +If the machine has installed PaddlePaddle before, there are two methods: .. code-block:: bash + 1. uninstall and reinstall + pip uninstall paddlepaddle + pip install build/python/dist/*.whl + + 2. upgrade directly pip install build/python/dist/*.whl -U .. _run_test: -- GitLab From 3cf23bece3f9917eedf7b5d5548aedbfd301854d Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Fri, 12 Jan 2018 18:18:21 +0800 Subject: [PATCH 0248/2305] create paddle_fluid_shared.so library --- paddle/inference/CMakeLists.txt | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/paddle/inference/CMakeLists.txt b/paddle/inference/CMakeLists.txt index 8437b2b2194..b017283ec3b 100644 --- a/paddle/inference/CMakeLists.txt +++ b/paddle/inference/CMakeLists.txt @@ -1,12 +1,20 @@ -set(FLUID_CORE_MODULES - backward proto_desc paddle_memory executor prune init ${GLOB_OP_LIB}) +set(FLUID_CORE_MODULES proto_desc paddle_memory executor prune init) cc_library(paddle_fluid_api SRCS inference.cc - DEPS ${FLUID_CORE_MODULES}) + DEPS ${FLUID_CORE_MODULES} ${GLOB_OP_LIB}) -# Merge all modules into a simgle static library -cc_library(paddle_fluid DEPS paddle_fluid_api ${FLUID_CORE_MODULES}) +# Merge all modules into a single static library +cc_library(paddle_fluid DEPS paddle_fluid_api ${FLUID_CORE_MODULES} ${GLOB_OP_LIB}) + +# Create shared library +add_library(paddle_fluid_shared SHARED inference.cc) + +target_circle_link_libraries(paddle_fluid_shared + ARCHIVE_START + ${GLOB_OP_LIB} + ARCHIVE_END + ${FLUID_CORE_MODULES}) # ptools # just for testing, we may need to change the storing format for inference_model -- GitLab From 8ac744f372ed8980007d53ee3cbc1acd7db51a56 Mon Sep 17 00:00:00 2001 From: ying Date: Fri, 12 Jan 2018 14:57:23 +0800 Subject: [PATCH 0249/2305] add wrapper for elementwise math operator. --- doc/api/v2/fluid/layers.rst | 129 ++++++++++++++++++ python/paddle/v2/fluid/__init__.py | 19 ++- python/paddle/v2/fluid/backward.py | 5 +- python/paddle/v2/fluid/clip.py | 4 +- python/paddle/v2/fluid/default_scope_funcs.py | 26 ++-- python/paddle/v2/fluid/evaluator.py | 35 ++--- python/paddle/v2/fluid/framework.py | 12 +- python/paddle/v2/fluid/initializer.py | 7 +- python/paddle/v2/fluid/io.py | 12 +- python/paddle/v2/fluid/layers/nn.py | 45 ++++-- python/paddle/v2/fluid/layers/ops.py | 29 +++- python/paddle/v2/fluid/layers/tensor.py | 12 +- .../fluid/memory_optimization_transpiler.py | 6 +- python/paddle/v2/fluid/nets.py | 5 +- python/paddle/v2/fluid/registry.py | 17 ++- python/paddle/v2/fluid/regularizer.py | 6 +- 16 files changed, 304 insertions(+), 65 deletions(-) diff --git a/doc/api/v2/fluid/layers.rst b/doc/api/v2/fluid/layers.rst index 696a8012aa4..24bdf08fffd 100644 --- a/doc/api/v2/fluid/layers.rst +++ b/doc/api/v2/fluid/layers.rst @@ -358,3 +358,132 @@ reduce_min .. autofunction:: paddle.v2.fluid.layers.reduce_min :noindex: +logsigmoid +---------- +.. autofunction:: paddle.v2.fluid.layers.logsigmoid + :noindex: + +exp +--- +.. autofunction:: paddle.v2.fluid.layers.exp + :noindex: + +relu +---- +.. autofunction:: paddle.v2.fluid.layers.relu + :noindex: + +tanh +---- +.. autofunction:: paddle.v2.fluid.layers.tanh + :noindex: + +tanh_shrink +----------- +.. autofunction:: paddle.v2.fluid.layers.tanh_shrink + :noindex: + +softshrink +---------- +.. autofunction:: paddle.v2.fluid.layers.softshrink + :noindex: + +sqrt +---- +.. autofunction:: paddle.v2.fluid.layers.sqrt + :noindex: + +abs +---- +.. autofunction:: paddle.v2.fluid.layers.abs + :noindex: + +ceil +---- +.. autofunction:: paddle.v2.fluid.layers.ceil + :noindex: + +floor +----- +.. autofunction:: paddle.v2.fluid.layers.floor + :noindex: + +round +----- +.. autofunction:: paddle.v2.fluid.layers.round + :noindex: + +reciprocal +---------- +.. autofunction:: paddle.v2.fluid.layers.reciprocal + :noindex: + +log +--- +.. autofunction:: paddle.v2.fluid.layers.log + :noindex: + +square +------ +.. autofunction:: paddle.v2.fluid.layers.square + :noindex: + +softplus +-------- +.. autofunction:: paddle.v2.fluid.layers.softplus + :noindex: + +softsign +--------- +.. autofunction:: paddle.v2.fluid.layers.softsign + :noindex: + +brelu +----- +.. autofunction:: paddle.v2.fluid.layers.brelu + :noindex: + +leaky_relu +---------- +.. autofunction:: paddle.v2.fluid.layers.leaky_relu + :noindex: + +soft_relu +--------- +.. autofunction:: paddle.v2.fluid.layers.soft_relu + :noindex: + +elu +---- +.. autofunction:: paddle.v2.fluid.layers.elu + :noindex: + +relu6 +----- +.. autofunction:: paddle.v2.fluid.layers.relu6 + :noindex: + +pow +---- +.. autofunction:: paddle.v2.fluid.layers.pow + :noindex: + +hard_shrink +----------- +.. autofunction:: paddle.v2.fluid.layers.hard_shrink + :noindex: + +thresholded_relu +---------------- +.. autofunction:: paddle.v2.fluid.layers.thresholded_relu + :noindex: + +hard_sigmoid +------------- +.. autofunction:: paddle.v2.fluid.layers.hard_sigmoid + :noindex: + +swish +------ +.. autofunction:: paddle.v2.fluid.layers.swish + :noindex: diff --git a/python/paddle/v2/fluid/__init__.py b/python/paddle/v2/fluid/__init__.py index 422aa0a5ba2..ec5159fca16 100644 --- a/python/paddle/v2/fluid/__init__.py +++ b/python/paddle/v2/fluid/__init__.py @@ -23,9 +23,22 @@ from memory_optimization_transpiler import memory_optimize Tensor = LoDTensor __all__ = framework.__all__ + executor.__all__ + [ - 'io', 'initializer', 'layers', 'nets', 'optimizer', 'backward', - 'regularizer', 'LoDTensor', 'CPUPlace', 'CUDAPlace', 'Tensor', 'ParamAttr' - 'DataFeeder', 'clip', 'DistributeTranspiler', 'memory_optimize' + 'io', + 'initializer', + 'layers', + 'nets', + 'optimizer', + 'backward', + 'regularizer', + 'LoDTensor', + 'CPUPlace', + 'CUDAPlace', + 'Tensor', + 'ParamAttr' + 'DataFeeder', + 'clip', + 'DistributeTranspiler', + 'memory_optimize', ] diff --git a/python/paddle/v2/fluid/backward.py b/python/paddle/v2/fluid/backward.py index cea2d1e0906..43f6133a653 100644 --- a/python/paddle/v2/fluid/backward.py +++ b/python/paddle/v2/fluid/backward.py @@ -3,7 +3,10 @@ from . import core import collections import copy -__all__ = ['append_backward', 'calc_gradient'] +__all__ = [ + 'append_backward', + 'calc_gradient', +] def _rename_arg_(op_descs, old_name, new_name, begin_idx=None, end_idx=None): diff --git a/python/paddle/v2/fluid/clip.py b/python/paddle/v2/fluid/clip.py index b1fd1c2b65f..776c0f3f027 100644 --- a/python/paddle/v2/fluid/clip.py +++ b/python/paddle/v2/fluid/clip.py @@ -3,7 +3,9 @@ import layers from . import core __all__ = [ - 'GradientClipByValue', 'append_gradient_clip_ops', 'error_clip_callback' + 'GradientClipByValue', + 'append_gradient_clip_ops', + 'error_clip_callback', ] diff --git a/python/paddle/v2/fluid/default_scope_funcs.py b/python/paddle/v2/fluid/default_scope_funcs.py index 60c6165b6bd..9aebc07f8e8 100644 --- a/python/paddle/v2/fluid/default_scope_funcs.py +++ b/python/paddle/v2/fluid/default_scope_funcs.py @@ -1,16 +1,16 @@ """ Default scope function. -`Paddle` manages Scope as programming language's scope. It just a -thread-local stack of Scope. Top of that stack is current scope, the bottom -of that stack is all scopes' parent. +`Paddle` manages Scope as programming language's scope. It just a +thread-local stack of Scope. Top of that stack is current scope, the bottom +of that stack is all scopes' parent. -Invoking `var/find_var` can `new/find` variable in current scope. -Invoking `enter_local_scope/leave_local_scope` can create or destroy local -scope. +Invoking `var/find_var` can `new/find` variable in current scope. +Invoking `enter_local_scope/leave_local_scope` can create or destroy local +scope. -A `scoped_function` will take a `function` as input. That function will be -invoked in a new local scope. +A `scoped_function` will take a `function` as input. That function will be +invoked in a new local scope. """ import paddle.v2.fluid.core @@ -19,8 +19,12 @@ import threading __tl_scope__ = threading.local() __all__ = [ - 'get_cur_scope', 'enter_local_scope', 'leave_local_scope', 'var', - 'find_var', 'scoped_function' + 'get_cur_scope', + 'enter_local_scope', + 'leave_local_scope', + 'var', + 'find_var', + 'scoped_function', ] @@ -71,7 +75,7 @@ def find_var(name): def scoped_function(func): """ invoke `func` in new scope. - + :param func: a callable function that will be run in new scope. :type func: callable """ diff --git a/python/paddle/v2/fluid/evaluator.py b/python/paddle/v2/fluid/evaluator.py index e186ee96c38..dc083f37b5f 100644 --- a/python/paddle/v2/fluid/evaluator.py +++ b/python/paddle/v2/fluid/evaluator.py @@ -4,7 +4,10 @@ import layers from framework import Program, unique_name, Variable, program_guard from layer_helper import LayerHelper -__all__ = ['Accuracy', 'ChunkEvaluator'] +__all__ = [ + 'Accuracy', + 'ChunkEvaluator', +] def _clone_var_(block, var): @@ -21,19 +24,19 @@ def _clone_var_(block, var): class Evaluator(object): """ Base Class for all evaluators - + Args: - name(str): The name of evaluator. such as, "accuracy". Used for generate + name(str): The name of evaluator. such as, "accuracy". Used for generate temporary variable name. - main_program(Program, optional): The evaluator should be added to this + main_program(Program, optional): The evaluator should be added to this main_program. Default default_main_program() - startup_program(Program, optional):The parameter should be added to this + startup_program(Program, optional):The parameter should be added to this startup_program. Default default_startup_program() - + Attributes: - states(list): The list of state variables. states will be reset to zero + states(list): The list of state variables. states will be reset to zero when `reset` is invoked. - metrics(list): The list of metrics variables. They will be calculate + metrics(list): The list of metrics variables. They will be calculate every mini-batch """ @@ -66,14 +69,14 @@ class Evaluator(object): def create_state(self, suffix, dtype, shape): """ - Create state variable. - + Create state variable. + NOTE: It is not a public API. - + Args: - suffix(str): the state suffix. - dtype(str|core.DataType): the state data type - shape(tuple|list): the shape of state + suffix(str): the state suffix. + dtype(str|core.DataType): the state data type + shape(tuple|list): the shape of state Returns: State variable @@ -127,8 +130,8 @@ class Accuracy(Evaluator): class ChunkEvaluator(Evaluator): """ - Accumulate counter numbers output by chunk_eval from mini-batches and - compute the precision recall and F1-score using the accumulated counter + Accumulate counter numbers output by chunk_eval from mini-batches and + compute the precision recall and F1-score using the accumulated counter numbers. """ diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index 3ef6b33192d..bdbfe9da077 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -7,9 +7,15 @@ import proto.framework_pb2 as framework_pb2 from . import core __all__ = [ - 'Block', 'Variable', 'Program', 'Operator', 'default_startup_program', - 'default_main_program', 'program_guard', 'switch_startup_program', - 'switch_main_program' + 'Block', + 'Variable', + 'Program', + 'Operator', + 'default_startup_program', + 'default_main_program', + 'program_guard', + 'switch_startup_program', + 'switch_main_program', ] EMPTY_VAR_NAME = core.kEmptyVarName() diff --git a/python/paddle/v2/fluid/initializer.py b/python/paddle/v2/fluid/initializer.py index c0839caaf2b..c3ed1a90896 100644 --- a/python/paddle/v2/fluid/initializer.py +++ b/python/paddle/v2/fluid/initializer.py @@ -1,7 +1,12 @@ import framework import numpy as np -__all__ = ['Constant', 'Uniform', 'Normal', 'Xavier'] +__all__ = [ + 'Constant', + 'Uniform', + 'Normal', + 'Xavier', +] class Initializer(object): diff --git a/python/paddle/v2/fluid/io.py b/python/paddle/v2/fluid/io.py index c63567601ac..1d28e9c5a6f 100644 --- a/python/paddle/v2/fluid/io.py +++ b/python/paddle/v2/fluid/io.py @@ -4,9 +4,15 @@ import cPickle as pickle from paddle.v2.fluid.framework import Program, Parameter, default_main_program, Variable __all__ = [ - 'save_vars', 'save_params', 'save_persistables', 'load_vars', 'load_params', - 'load_persistables', "save_inference_model", "load_inference_model", - "get_inference_program" + 'save_vars', + 'save_params', + 'save_persistables', + 'load_vars', + 'load_params', + 'load_persistables', + 'save_inference_model', + 'load_inference_model', + 'get_inference_program', ] diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 1fb6523f552..94184d59f6f 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -9,12 +9,33 @@ from ..param_attr import ParamAttr from tensor import concat __all__ = [ - 'fc', 'embedding', 'dynamic_lstm', 'gru_unit', 'linear_chain_crf', - 'crf_decoding', 'cos_sim', 'cross_entropy', 'square_error_cost', 'accuracy', - 'chunk_eval', 'sequence_conv', 'conv2d', 'sequence_pool', 'pool2d', - 'batch_norm', 'beam_search_decode', 'conv2d_transpose', 'sequence_expand', - 'lstm_unit', 'reduce_sum', 'reduce_mean', 'reduce_max', 'reduce_min', - 'sequence_first_step', 'sequence_last_step', 'dropout' + 'fc', + 'embedding', + 'dynamic_lstm', + 'gru_unit', + 'linear_chain_crf', + 'crf_decoding', + 'cos_sim', + 'cross_entropy', + 'square_error_cost', + 'accuracy', + 'chunk_eval', + 'sequence_conv', + 'conv2d', + 'sequence_pool', + 'pool2d', + 'batch_norm', + 'beam_search_decode', + 'conv2d_transpose', + 'sequence_expand', + 'lstm_unit', + 'reduce_sum', + 'reduce_mean', + 'reduce_max', + 'reduce_min', + 'sequence_first_step', + 'sequence_last_step', + 'dropout', ] @@ -248,13 +269,13 @@ def gru_unit(input, h_t & = dot((1-u_t), m_t) + dot(u_t, h_{t-1}) The inputs of gru unit includes :math:`z_t`, :math:`h_{t-1}`. In terms - of the equation above, the :math:`z_t` is split into 3 parts - - :math:`xu_t`, :math:`xr_t` and :math:`xm_t`. This means that in order to - implement a full GRU unit operator for an input, a fully + of the equation above, the :math:`z_t` is split into 3 parts - + :math:`xu_t`, :math:`xr_t` and :math:`xm_t`. This means that in order to + implement a full GRU unit operator for an input, a fully connected layer has to be applied, such that :math:`z_t = W_{fc}x_t`. - The terms :math:`u_t` and :math:`r_t` represent the update and reset gates - of the GRU cell. Unlike LSTM, GRU has one lesser gate. However, there is + The terms :math:`u_t` and :math:`r_t` represent the update and reset gates + of the GRU cell. Unlike LSTM, GRU has one lesser gate. However, there is an intermediate candidate hidden output, which is denoted by :math:`m_t`. This layer has three outputs :math:`h_t`, :math:`dot(r_t, h_{t-1})` and concatenation of :math:`u_t`, :math:`r_t` and :math:`m_t`. @@ -276,7 +297,7 @@ def gru_unit(input, .. code-block:: python # assuming we have x_t_data and prev_hidden of size=10 - x_t = fluid.layers.fc(input=x_t_data, size=30) + x_t = fluid.layers.fc(input=x_t_data, size=30) hidden_val, r_h_val, gate_val = fluid.layers.gru_unit(input=x_t, hidden = prev_hidden) diff --git a/python/paddle/v2/fluid/layers/ops.py b/python/paddle/v2/fluid/layers/ops.py index d3a5b707859..51a85dbbd33 100644 --- a/python/paddle/v2/fluid/layers/ops.py +++ b/python/paddle/v2/fluid/layers/ops.py @@ -1,7 +1,34 @@ from ..registry import register_layer __activations__ = [ - 'abs', 'tanh', 'sigmoid', 'relu', 'sqrt', 'ceil', 'floor', 'log', 'round' + 'sigmoid', + 'logsigmoid', + 'exp', + 'relu', + 'tanh', + 'tanh_shrink', + 'softshrink', + 'sqrt', + 'abs', + 'ceil', + 'floor', + 'round', + 'reciprocal', + 'log', + 'square', + 'softplus', + 'softsign', + 'brelu', + 'leaky_relu', + 'soft_relu', + 'elu', + 'relu6', + 'pow', + 'stanh', + 'hard_shrink', + 'thresholded_relu', + 'hard_sigmoid', + 'swish', ] __all__ = [ diff --git a/python/paddle/v2/fluid/layers/tensor.py b/python/paddle/v2/fluid/layers/tensor.py index 5f12ecfc14f..3f8ebeeb484 100644 --- a/python/paddle/v2/fluid/layers/tensor.py +++ b/python/paddle/v2/fluid/layers/tensor.py @@ -2,8 +2,16 @@ from ..layer_helper import LayerHelper from ..param_attr import ParamAttr __all__ = [ - 'create_tensor', 'create_parameter', 'cast', 'concat', 'sums', 'assign', - 'fill_constant_batch_size_like', 'fill_constant', 'ones', 'zeros' + 'create_tensor', + 'create_parameter', + 'cast', + 'concat', + 'sums', + 'assign', + 'fill_constant_batch_size_like', + 'fill_constant', + 'ones', + 'zeros', ] diff --git a/python/paddle/v2/fluid/memory_optimization_transpiler.py b/python/paddle/v2/fluid/memory_optimization_transpiler.py index 6800d7ddbb1..293b116957f 100644 --- a/python/paddle/v2/fluid/memory_optimization_transpiler.py +++ b/python/paddle/v2/fluid/memory_optimization_transpiler.py @@ -121,8 +121,10 @@ class ControlFlowGraph(object): # and dtype_to_size[cache_dtype] if x_dtype == cache_dtype: print( - "Hit Cache !!!! cache pool index is %d, var name is %s, cached var name is %s, var shape is %s " - % + ("Hit Cache !!!! cache pool index " + "is %d, var name is %s, " + "cached var name is %s, " + "var shape is %s ") % (index, x, cache_var, str(cache_shape))) self.pool.pop(index) _rename_arg_( diff --git a/python/paddle/v2/fluid/nets.py b/python/paddle/v2/fluid/nets.py index 54886a8f2cc..47b550bf4d8 100644 --- a/python/paddle/v2/fluid/nets.py +++ b/python/paddle/v2/fluid/nets.py @@ -1,6 +1,9 @@ import layers -__all__ = ["simple_img_conv_pool", "sequence_conv_pool"] +__all__ = [ + "simple_img_conv_pool", + "sequence_conv_pool", +] def simple_img_conv_pool(input, diff --git a/python/paddle/v2/fluid/registry.py b/python/paddle/v2/fluid/registry.py index 7aa82906114..94b16bca8c9 100644 --- a/python/paddle/v2/fluid/registry.py +++ b/python/paddle/v2/fluid/registry.py @@ -8,7 +8,11 @@ import proto.framework_pb2 as framework_pb2 from framework import OpProtoHolder, Variable, Program, Operator from paddle.v2.fluid.layer_helper import LayerHelper, unique_name -__all__ = ['deprecated', 'register_layer', 'autodoc'] +__all__ = [ + 'deprecated', + 'register_layer', + 'autodoc', +] def _convert_(name): @@ -80,11 +84,10 @@ def _generate_doc_string_(op_proto): def register_layer(op_type): - """ - Register an Python layer for an Operator + """Register the Python layer for an Operator. Args: - op_type: The name of the operator to be created + op_type: The name of the operator to be created. This function takes in the operator type (sigmoid, mean , average etc) and creates the operator functionality. @@ -98,16 +101,16 @@ def register_layer(op_type): if len(not_intermediate_outputs) != 1: raise ValueError("Only one non intermediate output operator can be", - "automatically generated") + "automatically generated.") if not_intermediate_outputs[0].duplicable: raise ValueError( - "Only non duplicable op can be automatically generated") + "Only non duplicable op can be automatically generated.") for output in intermediate_outputs: if output.duplicable: raise ValueError("The op can be automatically generated only when ", - "all intermediate ops are not duplicable") + "all intermediate ops are not duplicable.") o_name = not_intermediate_outputs[0].name intermediate_output_names = [output.name for output in intermediate_outputs] diff --git a/python/paddle/v2/fluid/regularizer.py b/python/paddle/v2/fluid/regularizer.py index d1955b00479..117c45c49f1 100644 --- a/python/paddle/v2/fluid/regularizer.py +++ b/python/paddle/v2/fluid/regularizer.py @@ -1,6 +1,10 @@ import framework -__all__ = ['append_regularization_ops', 'L1Decay', 'L2Decay'] +__all__ = [ + 'append_regularization_ops', + 'L1Decay', + 'L2Decay', +] def append_regularization_ops(parameters_and_grads, regularization=None): -- GitLab From db65f497ffce0dcc8b71a06d8a303e1d75b864ca Mon Sep 17 00:00:00 2001 From: Cao Ying Date: Sat, 13 Jan 2018 02:35:13 +0800 Subject: [PATCH 0250/2305] Update comments for two operators. (#7457) * update code comments. * update the comments. * follow comments. --- .../reorder_lod_tensor_by_rank_op.cc | 44 ++++++++++++++----- paddle/operators/shrink_rnn_memory_op.cc | 22 +++++----- python/paddle/v2/fluid/layers/control_flow.py | 40 ++++++++++------- python/paddle/v2/fluid/layers/tensor.py | 17 +++---- 4 files changed, 77 insertions(+), 46 deletions(-) diff --git a/paddle/operators/reorder_lod_tensor_by_rank_op.cc b/paddle/operators/reorder_lod_tensor_by_rank_op.cc index a055cdf7e89..3c304479494 100644 --- a/paddle/operators/reorder_lod_tensor_by_rank_op.cc +++ b/paddle/operators/reorder_lod_tensor_by_rank_op.cc @@ -26,22 +26,44 @@ class ReorderLoDTensorByRankTableOpProtoMaker ReorderLoDTensorByRankTableOpProtoMaker(OpProto *proto, OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { - AddInput("X", "(LoDTensor) the input lod tensor need to be reordered."); + AddInput("X", + "(LoDTensor), the input lod tensor to be reordered according to " + "Input(RankTable)."); AddInput("RankTable", - "(LoDRankTable) the rank table that input need follow"); - AddOutput("Out", "(LoDTensor) reordered lod tensor"); - AddComment(R"DOC(ReorderLoDTensorByRankTable + "(LoDRankTable), the rank table according to which Input(X) is " + "reordered."); + AddOutput("Out", "(LoDTensor), the reordered lod tensor."); + AddComment(R"DOC(ReorderLoDTensorByRankTable operator. -Reorder the input X by the rank of `RankTable`. If `RankTable` is ordered by -index [3, 0, 2, 1]. Input X will reorder its sequence, the third sequence of -X will be the first sequence of Output. - -NOTE: The RankTable does not need to be calculated by X. +Input(X) is a batch of sequences. Input(RankTable) stores new orders of the +input sequence batch. The reorder_lod_tensor_by_rank operator reorders the +Input(X) according to the information provided by Input(RankTable). For example: -The X = [Seq0, Seq1, Seq2, Seq3]. The indices of RankTable are [3, 0, 2, 1]. -The Out = [Seq3, Seq0, Seq2, Seq1] with correct LoD information. +If the indices stored in the Input(RankTable) are [3, 0, 2, 1], the +Input(X) will be reordered that the fourth sequence in Input(X) will become the +first one, and then followed by the original first, third, and the second one. + +This is: +X = [Seq0, Seq1, Seq2, Seq3]. The indices in RankTable are [3, 0, 2, 1]. +Out = [Seq3, Seq0, Seq2, Seq1] with a new LoD information. + +If the LoD information of Input(X) is empty, this means Input(X) is not sequence +data. This is also identical to a batch of sequences where each sequence has a +fixed length 1. In this case, the reorder_lod_tensor_by_rank operator reorders +each slice of Input(X) along the first axis according to Input(RankTable). + +This is: +X = [Slice0, Slice1, Slice2, Slice3] and its LoD information is empty. The +indices in RankTable are [3, 0, 2, 1]. +Out = [Slice3, Slice0, Slice2, Slice1] with no LoD information is appended. + +NOTE: This operator sorts Input(X) according to a given LoDRankTable which does +not need to be calculated according to Input(X). It can be calculated according +to another different sequence, and then this operator sorts Input(X) according +to the given LoDRankTable. + )DOC"); } }; diff --git a/paddle/operators/shrink_rnn_memory_op.cc b/paddle/operators/shrink_rnn_memory_op.cc index 3f5b2a9b843..ade94b40bed 100644 --- a/paddle/operators/shrink_rnn_memory_op.cc +++ b/paddle/operators/shrink_rnn_memory_op.cc @@ -45,7 +45,7 @@ class ShrinkRNNMemoryOp : public ArrayOp { rank_items.begin(); auto *out_var = scope.FindVar(Output("Out")); - PADDLE_ENFORCE(out_var != nullptr, "Output Out must be set"); + PADDLE_ENFORCE(out_var != nullptr, "Output(Out) must be set."); auto &out_tensor = *out_var->GetMutable(); size_t height = dst_num_rows; @@ -76,15 +76,17 @@ class ShrinkRNNMemoryOpProtoMaker : public framework::OpProtoAndCheckerMaker { "(LoDTensor) The step index. The RNN step memory 'X' will be " "shrinked to match the size of the input of the index'th step."); AddOutput("Out", "(LoDTensor) The shrinked RNN step memory."); - AddComment( - R"DOC( - In dynamic RNN, we are able to handle sequences of different lengths. - Because of the multiple lengths, the size of each step input can be - different, which may lead to a mismatching between the input of - the current step and the memory generated by the previous one. This - operator shrinks memory according to the size of the next step input, - to make sure that they can match each other. - )DOC"); + AddComment(R"DOC( +This operator is used to shrink output batch of memory defined in dynamic RNN. + +Dynamic RNN is able to handle variable-length sequences, in which, sequences in +a mini-batch are sorted by their lengths first. After that, the longest sequence +becomes the first one in the sorted batch, followed by the second longest, the +third longest, and so on. Dynamic RNN then slices a batch input timestep by +timestep from the sorted input. Once any sequence in the input batch reaches its +end, memory defined in dynamicRNN has to shrink its outputs to adapt to the input +batch size for the next time step. +)DOC"); } }; diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index 0cf17f3083a..4b363ecbe78 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -742,11 +742,10 @@ def topk(input, k): def lod_tensor_to_array(x, table): - """This function performs the operation that converts an LOD_Tensor to - an array. + """ Convert a LOD_TENSOR to an LOD_TENSOR_ARRAY. Args: - x (Variable|list): The tensor that needs to be converted to an array. + x (Variable|list): The LOD tensor to be converted to a LOD tensor array. table (ParamAttr|list): The variable that stores the level of lod which is ordered by sequence length in descending order. @@ -776,11 +775,10 @@ def lod_tensor_to_array(x, table): def array_to_lod_tensor(x, table): - """This function performs the operations that converts an array to - an LOD_Tensor. + """Convert a LoD_Tensor_Aarry to an LoDTensor. Args: - x (Variable|list): The array that needs to be converted to a tensor. + x (Variable|list): The lod tensor array to be converted to a tensor. table (ParamAttr|list): The variable that stores the level of lod which is ordered by sequence length in descending order. @@ -808,7 +806,8 @@ def array_to_lod_tensor(x, table): def increment(x, value=1.0, in_place=True): - """This function performs an operation that increments each value in the + """ + This function performs an operation that increments each value in the input :math:`x` by an amount: :math:`value` as mentioned in the input parameter. This operation is performed in-place by default. @@ -841,17 +840,24 @@ def increment(x, value=1.0, in_place=True): def array_write(x, i, array=None): - """This function performs the operation to write the data out as an - LOD_TENSOR_ARRAY. + """ + This function writes the given input variable to the specified position + indicating by the arrary index to an output LOD_TENSOR_ARRAY. If the + output LOD_TENSOR_ARRAY is not given(None), a new one will be created and + returned. Args: x (Variable|list): The input tensor from which the data will be read. - i (Variable|list): The subscript index in tensor array, that points the - place from which data will be read. - array (Variable|list): The data can be read into this variable if - this is assigned. + i (Variable|list): The index of the output LOD_TENSOR_ARRAY, pointing to + the position to which the input tensor will be + written. + array (Variable|list): The output LOD_TENSOR_ARRAY to which the input + tensor will be written. If this parameter is + NONE, a new LOD_TENSOR_ARRAY will be created and + returned. + Returns: - Variable: The tensor type variable that has the data written to it. + Variable: The output LOD_TENSOR_ARRAY where the input tensor is written. Examples: .. code-block::python @@ -1228,7 +1234,7 @@ class DynamicRNN(object): self._assert_in_rnn_block_("step_input") if not isinstance(x, Variable): raise TypeError( - "step_input() can only take a Variable as its input") + "step_input() can only take a Variable as its input.") parent_block = self._parent_block_() if self.lod_rank_table is None: self.lod_rank_table = parent_block.create_var( @@ -1289,8 +1295,8 @@ class DynamicRNN(object): def __call__(self, *args, **kwargs): if self.status != DynamicRNN.AFTER_RNN: - raise ValueError( - "Dynamic RNN outputs can only be retrieved after rnn block") + raise ValueError(("Output of the dynamic RNN can only be visited " + "outside the rnn block.")) if len(self.outputs) == 1: return self.outputs[0] else: diff --git a/python/paddle/v2/fluid/layers/tensor.py b/python/paddle/v2/fluid/layers/tensor.py index 57668a7983b..438df33afbe 100644 --- a/python/paddle/v2/fluid/layers/tensor.py +++ b/python/paddle/v2/fluid/layers/tensor.py @@ -176,25 +176,26 @@ def fill_constant(shape, dtype, value, out=None): """ **fill_constant** - This function creates a tensor of specified *shape* and - *dtype*, and initializes this with a constant supplied in *value*. + This function creates a tensor with specified `shape` and `dtype`, and + initializes it with a constant specifed by `value`. - It also sets *stop_gradient* to True. + The attribute `stop_gradient` of the created tensor is set to True. Args: - shape(tuple|list|None): Shape of output tensor - dtype(np.dtype|core.DataType|str): Data type of output tensor - value(float): Constant value to initialize the output tensor - out(Variable): Output Variable to initialize + shape(tuple|list|None): Shape of the output tensor. + dtype(np.dtype|core.DataType|str): Data type of the output tensor. + value(float): The constant value used to initialize the output tensor. + out(Variable): The output tensor. Returns: - Variable: The tensor variable storing the output + Variable: The tensor variable storing the output. Examples: .. code-block:: python data = fluid.layers.fill_constant(shape=[1], value=0, dtype='int64') """ + helper = LayerHelper("fill_constant", **locals()) if out is None: out = helper.create_tmp_variable(dtype=dtype) -- GitLab From 137f0dfc21d2d165b03d9bb2961370f84ed287b6 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Sat, 13 Jan 2018 21:15:43 +0800 Subject: [PATCH 0251/2305] 1. Fix warpctc grad tensor initial bug. 2. Remove num_seq arguments. 3. Refine CUDA kernel of ScaleLoDTensorFunctor. 4. Change max_relative_error of gradient unitest to 0.007 --- paddle/operators/math/sequence_scale.cc | 4 +-- paddle/operators/math/sequence_scale.cu | 27 +++++++------------ paddle/operators/math/sequence_scale.h | 2 +- paddle/operators/warpctc_op.h | 7 +++-- .../paddle/v2/fluid/tests/test_warpctc_op.py | 2 +- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/paddle/operators/math/sequence_scale.cc b/paddle/operators/math/sequence_scale.cc index 0f66e43a1a6..7e439e9a2ce 100644 --- a/paddle/operators/math/sequence_scale.cc +++ b/paddle/operators/math/sequence_scale.cc @@ -22,10 +22,10 @@ template class ScaleLoDTensorFunctor { public: void operator()(const platform::CPUDeviceContext& context, - framework::LoDTensor& seq, const T* scales, - const size_t num_seq) { + framework::LoDTensor& seq, const T* scales) { const size_t level = 0; auto lod = seq.lod(); + const size_t num_seq = lod[level].size() - 1; size_t seq_width = seq.dims()[1]; framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); diff --git a/paddle/operators/math/sequence_scale.cu b/paddle/operators/math/sequence_scale.cu index fd1370c1184..bc89711fcb9 100644 --- a/paddle/operators/math/sequence_scale.cu +++ b/paddle/operators/math/sequence_scale.cu @@ -20,18 +20,10 @@ namespace math { template __global__ void SequenceScaleKernel(T* seq, size_t* lod, const T* scales, - const size_t num_seq, const size_t seq_width) { - const int idx = threadIdx.x + blockIdx.x * blockDim.x; - - if (idx < lod[num_seq] * seq_width) { - size_t i = 0; - for (i = 0; i < num_seq; ++i) { - if (idx < lod[i + 1] * seq_width) { - break; - } - } - seq[idx] *= scales[i]; + if (threadIdx.x < (lod[blockIdx.x + 1] - lod[blockIdx.x]) * seq_width) { + int idx = lod[blockIdx.x] * seq_width + threadIdx.x; + seq[idx] *= scales[blockIdx.x]; } } @@ -39,18 +31,17 @@ template class ScaleLoDTensorFunctor { public: void operator()(const platform::CUDADeviceContext& context, - framework::LoDTensor& seq, const T* scales, - const size_t num_seq) { - auto lod = seq.lod(); - const size_t seq_width = seq.dims()[1]; + framework::LoDTensor& seq, const T* scales) { const size_t level = 0; + auto lod = seq.lod(); + const size_t num_seq = lod[level].size() - 1; + const size_t seq_width = seq.numel() / seq.dims()[0]; framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); T* seq_data = seq.mutable_data(context.GetPlace()); int threads = 1024; - int grid = (seq.numel() * seq_width + threads - 1) / threads; - SequenceScaleKernel<<>>( - seq_data, abs_offset_lod[level].data(), scales, num_seq, seq_width); + SequenceScaleKernel<<>>( + seq_data, abs_offset_lod[level].data(), scales, seq_width); } }; diff --git a/paddle/operators/math/sequence_scale.h b/paddle/operators/math/sequence_scale.h index 8c47179b55d..ecd9a57c3f4 100644 --- a/paddle/operators/math/sequence_scale.h +++ b/paddle/operators/math/sequence_scale.h @@ -47,7 +47,7 @@ template class ScaleLoDTensorFunctor { public: void operator()(const DeviceContext& context, framework::LoDTensor& seq, - const T* scales, const size_t num_seq); + const T* scales); }; } // namespace math diff --git a/paddle/operators/warpctc_op.h b/paddle/operators/warpctc_op.h index d41752e7333..8aea061c00c 100644 --- a/paddle/operators/warpctc_op.h +++ b/paddle/operators/warpctc_op.h @@ -179,6 +179,10 @@ class WarpCTCKernel : public framework::OpKernel { T* warpctc_grad_data = warpctc_grad->mutable_data(warpctc_logits.dims(), ctx.GetPlace()); + math::SetConstant()( + ctx.template device_context(), warpctc_grad, + static_cast(0)); + // warpctc accesses labels in CPU memory Tensor warpctc_label; Copy(*label, platform::CPUPlace(), ctx.device_context(), &warpctc_label); @@ -215,10 +219,9 @@ class WarpCTCGradKernel : public framework::OpKernel { *warpctc_grad, norm_by_times); const T* loss_grad_data = loss_grad->data(); - const size_t num_seq = loss_grad->dims()[0]; math::ScaleLoDTensorFunctor()( ctx.template device_context(), *logits_grad, - loss_grad_data, num_seq); + loss_grad_data); } }; diff --git a/python/paddle/v2/fluid/tests/test_warpctc_op.py b/python/paddle/v2/fluid/tests/test_warpctc_op.py index a1c4e40111f..07be05d2b03 100644 --- a/python/paddle/v2/fluid/tests/test_warpctc_op.py +++ b/python/paddle/v2/fluid/tests/test_warpctc_op.py @@ -193,7 +193,7 @@ class TestWarpCTCOp(OpTest): def test_check_grad(self): self.outputs['WarpCTCGrad'] = self.gradient - self.check_grad(["Logits"], "Loss", max_relative_error=0.01) + self.check_grad(["Logits"], "Loss", max_relative_error=0.007) if __name__ == "__main__": -- GitLab From 9d52ad4c3a0ad17b7aaf7cc913f1b9d8eeaa0311 Mon Sep 17 00:00:00 2001 From: Yancey1989 Date: Sun, 14 Jan 2018 14:09:04 +0800 Subject: [PATCH 0252/2305] fix op doc --- doc/howto/dev/new_op_cn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/howto/dev/new_op_cn.md b/doc/howto/dev/new_op_cn.md index 3109d72001f..92996585674 100644 --- a/doc/howto/dev/new_op_cn.md +++ b/doc/howto/dev/new_op_cn.md @@ -24,7 +24,7 @@ - `framework::OperatorWithKernel`:继承自OperatorBase,Op有计算函数,称作有Kernel。 - `class OpProtoAndCheckerMaker`:描述该Op的输入、输出、属性、注释,主要用于Python API接口生成 -依据是否包含kernel,可以将Op分为两种:包含Kernel的Op和不包含kernel的Op,前者Op的定义继承自`OperatorBase`,后者继承自`OperatorWithKernel`。本教程主要介绍带Kernel的Op如何写,简单总结Op需要包含的内容如下: +依据是否包含kernel,可以将Op分为两种:包含Kernel的Op和不包含kernel的Op,前者Op的定义继承自`OperatorWithKernel`,后者继承自`OperatorBase`。本教程主要介绍带Kernel的Op如何写,简单总结Op需要包含的内容如下: 内容 | 定义位置 -- GitLab From 5ad1aef051349a73b00b8d611f0ae2508f02490b Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Sun, 14 Jan 2018 16:54:04 +0800 Subject: [PATCH 0253/2305] "cudnn operators change to cudnn kernel" (#6660) * "unified operators" * "add CUDNN register" * "add use cudnn attribute" * "add attribute" * "test conv tranpose op" * "remove duplicated attr" * "fix op test" * "add attribute to set cudnn" * "add more log" * "need layout op register support" * "add more log" * "change GetExpectedKernelType " * "fix Get attr in conv_op" * "fix CI" * "fix tests" * "removed kernel priority fallback" * "fix CI" * "fix stack pointer bug" * "refine buggy interface" * "add const cast to save life" * "fix get_output_with_grad" * "fix op test with dataformat" * ""fix pooling * "fix pooling test" * "fix CI" * "fix with_gpu error" * "add transform needed functional check" * "fix unpack list error" * "comment out parallel.do temporary" * "fix CI" * "fix compile doc error" * "make threshold larger" --- paddle/framework/data_device_transform.cc | 5 +- paddle/framework/data_device_transform.h | 3 +- paddle/framework/data_layout.h | 19 +++- paddle/framework/data_transform.cc | 10 +- paddle/framework/data_transform.h | 6 +- paddle/framework/op_kernel_type.h | 5 + paddle/framework/op_registry_test.cc | 18 ---- paddle/framework/operator.cc | 100 +++++------------- paddle/framework/operator.h | 26 +---- paddle/operators/CMakeLists.txt | 19 +++- paddle/operators/conv_cudnn_op.cc | 74 ------------- paddle/operators/conv_cudnn_op.cu.cc | 31 +++--- paddle/operators/conv_op.cc | 73 +++++++++++++ paddle/operators/conv_op.h | 8 ++ paddle/operators/conv_transpose_cudnn_op.cc | 78 -------------- .../operators/conv_transpose_cudnn_op.cu.cc | 36 +++---- paddle/operators/conv_transpose_op.cc | 72 +++++++++++++ paddle/operators/conv_transpose_op.h | 8 ++ paddle/operators/math/sequence2batch.cc | 1 + paddle/operators/pool_cudnn_op.cc | 39 ------- paddle/operators/pool_cudnn_op.cu.cc | 29 ++--- paddle/operators/pool_cudnn_op.h | 19 ---- paddle/operators/pool_op.cc | 65 +++++++++++- paddle/operators/pool_op.h | 8 ++ paddle/platform/dynload/cudnn.cc | 2 +- paddle/platform/dynload/cudnn.h | 2 +- paddle/platform/dynload/dynamic_loader.cc | 2 +- paddle/platform/dynload/dynamic_loader.h | 2 +- paddle/pybind/pybind.cc | 7 +- paddle/pybind/tensor_py.h | 23 +++- python/paddle/v2/fluid/layers/nn.py | 2 +- python/paddle/v2/fluid/tests/op_test.py | 64 ++++++----- .../paddle/v2/fluid/tests/test_conv2d_op.py | 93 ++++++++++------ .../fluid/tests/test_conv2d_transpose_op.py | 80 ++++++++++---- .../paddle/v2/fluid/tests/test_conv3d_op.py | 85 ++++++++++----- .../fluid/tests/test_conv3d_transpose_op.py | 80 ++++++++++---- .../paddle/v2/fluid/tests/test_parallel_op.py | 8 +- .../paddle/v2/fluid/tests/test_pool2d_op.py | 49 ++++++--- .../paddle/v2/fluid/tests/test_pool3d_op.py | 49 ++++++--- 39 files changed, 732 insertions(+), 568 deletions(-) delete mode 100644 paddle/operators/conv_cudnn_op.cc delete mode 100644 paddle/operators/conv_transpose_cudnn_op.cc delete mode 100644 paddle/operators/pool_cudnn_op.cc delete mode 100644 paddle/operators/pool_cudnn_op.h diff --git a/paddle/framework/data_device_transform.cc b/paddle/framework/data_device_transform.cc index b3fd48ae12c..d38d87927fc 100644 --- a/paddle/framework/data_device_transform.cc +++ b/paddle/framework/data_device_transform.cc @@ -31,15 +31,14 @@ static const platform::DeviceContext* GetDeviceContext( } } -Tensor* DeviceTransform(const Tensor& in, const platform::Place& dst_place) { +void DeviceTransform(const Tensor& in, const platform::Place& dst_place, + Tensor* out) { VLOG(3) << "DeviceTransform in, src_place " << in.place() << " dst_place: " << dst_place; - Tensor* out = new Tensor(); auto* dev_ctx = GetDeviceContext(in.place(), dst_place); dev_ctx->Wait(); Copy(in, dst_place, *dev_ctx, out); dev_ctx->Wait(); - return out; } } // namespace framework diff --git a/paddle/framework/data_device_transform.h b/paddle/framework/data_device_transform.h index bebf0d1b320..b21ed0be34a 100644 --- a/paddle/framework/data_device_transform.h +++ b/paddle/framework/data_device_transform.h @@ -21,7 +21,8 @@ limitations under the License. */ namespace paddle { namespace framework { -Tensor* DeviceTransform(const Tensor& in, const platform::Place& dst_place); +void DeviceTransform(const Tensor& in, const platform::Place& dst_place, + Tensor* out); } // namespace framework } // namespace paddle diff --git a/paddle/framework/data_layout.h b/paddle/framework/data_layout.h index 3ab976ecac4..31817251ed0 100644 --- a/paddle/framework/data_layout.h +++ b/paddle/framework/data_layout.h @@ -14,7 +14,9 @@ limitations under the License. */ #pragma once -#include +#include +#include + #include "paddle/platform/enforce.h" namespace paddle { @@ -27,12 +29,19 @@ enum class DataLayout { }; inline DataLayout StringToDataLayout(const std::string& str) { - if (str == "NHWC" || str == "nhwc") { + std::string s(str); + for (size_t i = 0; i < s.size(); ++i) { + s[i] = toupper(s[i]); + } + + if (s == "NHWC") { return DataLayout::kNHWC; - } else if (str == "NCHW" || str == "nchw") { + } else if (s == "NCHW") { return DataLayout::kNCHW; + } else if (s == "ANYLAYOUT") { + return DataLayout::kAnyLayout; } else { - PADDLE_THROW("Unknown storage order string: %s", str); + PADDLE_THROW("Unknown storage order string: %s", s); } } @@ -49,7 +58,7 @@ inline std::string DataLayoutToString(const DataLayout& data_layout) { } } -inline std::ostream& operator<<(std::ostream& out, DataLayout l) { +inline std::ostream& operator<<(std::ostream& out, const DataLayout& l) { out << DataLayoutToString(l); return out; } diff --git a/paddle/framework/data_transform.cc b/paddle/framework/data_transform.cc index e56edb95396..d826f0edace 100644 --- a/paddle/framework/data_transform.cc +++ b/paddle/framework/data_transform.cc @@ -19,16 +19,14 @@ limitations under the License. */ namespace paddle { namespace framework { -Tensor* DataTransform(const OpKernelType& expected_kernel_type, - const OpKernelType& kernel_type_for_var, - const Tensor& input_tensor) { - Tensor* out = nullptr; +void DataTransform(const OpKernelType& expected_kernel_type, + const OpKernelType& kernel_type_for_var, + const Tensor& input_tensor, Tensor* out) { if (!platform::is_same_place(kernel_type_for_var.place_, expected_kernel_type.place_)) { - out = DeviceTransform(input_tensor, expected_kernel_type.place_); + DeviceTransform(input_tensor, expected_kernel_type.place_, out); } PADDLE_ENFORCE_NOT_NULL(out, "out should not be null"); - return out; } void CopyVariableWithTensor(const Variable& in_var, const Tensor& tensor, diff --git a/paddle/framework/data_transform.h b/paddle/framework/data_transform.h index ee95c7e8564..a4b78902379 100644 --- a/paddle/framework/data_transform.h +++ b/paddle/framework/data_transform.h @@ -30,9 +30,9 @@ limitations under the License. */ namespace paddle { namespace framework { -Tensor* DataTransform(const OpKernelType& expected_kernel_type, - const OpKernelType& kernel_type_for_var, - const Tensor& input_tensor); +void DataTransform(const OpKernelType& expected_kernel_type, + const OpKernelType& kernel_type_for_var, + const Tensor& input_tensor, Tensor* out); void CopyVariableWithTensor(const Variable& in_var, const Tensor& tensor, Variable& out_var); diff --git a/paddle/framework/op_kernel_type.h b/paddle/framework/op_kernel_type.h index 053897784c1..312bd5f892a 100644 --- a/paddle/framework/op_kernel_type.h +++ b/paddle/framework/op_kernel_type.h @@ -85,5 +85,10 @@ inline std::string KernelTypeToString(const OpKernelType& kernel_key) { return stream.str(); } +inline bool TransFromNeeded(const OpKernelType& l, const OpKernelType& r) { + return (!platform::places_are_same_class(l.place_, r.place_)) || + (l.data_type_ != r.data_type_) || (l.data_layout_ != r.data_layout_); +} + } // namespace framework } // namespace paddle diff --git a/paddle/framework/op_registry_test.cc b/paddle/framework/op_registry_test.cc index 66f07b6757f..341da8befd4 100644 --- a/paddle/framework/op_registry_test.cc +++ b/paddle/framework/op_registry_test.cc @@ -368,24 +368,6 @@ TEST(OperatorRegistrar, OpWithMultiKernel) { // TODO(qiao) add priority back // use all available kernels - paddle::framework::UseALL(); op->Run(scope, cuda_place); EXPECT_EQ(op_test_value, -10); - - // remove cuda kernels - paddle::framework::UseCPU(); - op->Run(scope, cpu_place); - - EXPECT_EQ(op_test_value, -9); - - // add cuda kernels - paddle::framework::UseCUDA(); - op->Run(scope, cuda_place); - - EXPECT_EQ(op_test_value, -10); - - // use cudnn kernel - paddle::framework::UseCUDNN(); - op->Run(scope, cuda_place); - EXPECT_EQ(op_test_value, -20); } diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index be1373dc2a8..84c010df7c3 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -29,52 +29,12 @@ DEFINE_bool(op_sync, false, namespace paddle { namespace framework { -std::vector> kKernelPriority; - -void UseCPU() { - kKernelPriority.clear(); - /*Plain CPU*/ - auto pair0 = std::make_tuple(platform::CPUPlace(), LibraryType::kPlain); - kKernelPriority.insert(kKernelPriority.begin(), pair0); -} - -void UseMKLDNN() { - UseCPU(); -#if PADDLE_WITH_MKLML - { - /*MKLDNN Kernel*/ - auto pair0 = std::make_tuple(platform::CPUPlace(), LibraryType::kMKLDNN); - kKernelPriority.insert(kKernelPriority.begin(), pair0); - } -#endif -} - -void UseCUDA() { - UseMKLDNN(); -#if PADDLE_WITH_CUDA - /*Plain GPU*/ - auto pair0 = std::make_tuple(platform::CUDAPlace(0), LibraryType::kPlain); - kKernelPriority.insert(kKernelPriority.begin(), pair0); -#endif -} - -void UseCUDNN() { - UseCUDA(); -#if PADDLE_WITH_CUDA - if (platform::dynload::HasCUDNN()) { - /*CUDNN Kernel*/ - auto pair0 = std::make_tuple(platform::CUDAPlace(0), LibraryType::kCUDNN); - kKernelPriority.insert(kKernelPriority.begin(), pair0); - } -#endif -} - -void UseALL() { - UseCPU(); - UseMKLDNN(); - UseCUDA(); - UseCUDNN(); -} +std::vector> kKernelPriority = { + std::make_tuple(platform::CUDAPlace(0), LibraryType::kCUDNN), + std::make_tuple(platform::CUDAPlace(0), LibraryType::kPlain), + std::make_tuple(platform::CPUPlace(), LibraryType::kMKLDNN), + std::make_tuple(platform::CPUPlace(), LibraryType::kPlain), +}; static DDim GetDims(const Scope& scope, const std::string& name) { Variable* var = scope.FindVar(name); @@ -271,36 +231,33 @@ static bool VarIsTensor(const Variable* var) { return var->IsType() || var->IsType(); } -static const Tensor* GetTensorFromVar(const Variable* var) { - const Tensor* t = nullptr; +static const Tensor* GetTensorFromVar(Variable* var) { if (var->IsType()) { - t = &(var->Get()); + return var->GetMutable(); } else if (var->IsType()) { - t = &(var->Get().value()); + return var->GetMutable()->mutable_value(); } else { PADDLE_THROW("Variable type_id %s, expect LoDTensor/SelectedRows.", var->Type().name()); } - return t; } static Tensor* GetMutableTensorFromVar(Variable* var) { - Tensor* t = nullptr; if (var->IsType()) { - t = var->GetMutable(); + return var->GetMutable(); } else if (var->IsType()) { - t = var->GetMutable()->mutable_value(); + return var->GetMutable()->mutable_value(); } else { PADDLE_THROW("Variable type_id %s, expect LoDTensor/SelectedRows.", var->Type().name()); } - return t; } template <> const Tensor* ExecutionContext::Input(const std::string& name) const { auto* var = InputVar(name); - return var == nullptr ? nullptr : GetTensorFromVar(var); + return var == nullptr ? nullptr + : GetTensorFromVar(const_cast(var)); } template <> @@ -343,6 +300,7 @@ bool OpSupportGPU(const std::string& op_type) { auto it = all_kernels.find(op_type); if (it == all_kernels.end()) { // All control operator must support GPU + return true; } for (auto& kern_pair : it->second) { @@ -516,21 +474,17 @@ void OperatorWithKernel::Run(const Scope& scope, } ExecutionContext ctx(*this, scope, *dev_ctx); - auto expected_kernel_key = this->GetExpectedKernelType(ctx); OpKernelMap& kernels = kernels_iter->second; - for (auto& candidate : kKernelPriority) { - auto candidate_key = - OpKernelType(expected_kernel_key.data_type_, std::get<0>(candidate), - expected_kernel_key.data_layout_, std::get<1>(candidate)); + // TODO(dzhwinter) : kernel fallback mechanism will be added when all the + // transform functions are ready. - if ((candidate_key == expected_kernel_key) || - (kernels.count(candidate_key))) { - expected_kernel_key = candidate_key; - break; - } - } + // for (auto& candidate : kKernelPriority) { + // Do selection + // } + + auto expected_kernel_key = this->GetExpectedKernelType(ctx); VLOG(3) << "expected_kernel_key:" << expected_kernel_key; @@ -544,7 +498,7 @@ void OperatorWithKernel::Run(const Scope& scope, if (tensor_in->IsInitialized()) { auto kernel_type_for_var = this->GetKernelTypeForVar( var_name_item.first, *tensor_in, expected_kernel_key); - if (kernel_type_for_var != expected_kernel_key) { + if (TransFromNeeded(kernel_type_for_var, expected_kernel_key)) { auto out_var_names = OutputVars(true); if (std::find(out_var_names.begin(), out_var_names.end(), var_name) != out_var_names.end()) { @@ -553,11 +507,13 @@ void OperatorWithKernel::Run(const Scope& scope, "does not support transform", var_name); } - VLOG(3) << "need to do transform for var " << var_name; + VLOG(3) << "Transform Variable " << var_name << " from " + << kernel_type_for_var << " to " << expected_kernel_key; auto* trans_var = new_scope.Var(var_name); - auto* out = DataTransform(expected_kernel_key, kernel_type_for_var, - *tensor_in); - CopyVariableWithTensor(*var, *out, *trans_var); + std::shared_ptr out(new Tensor); + DataTransform(expected_kernel_key, kernel_type_for_var, *tensor_in, + out.get()); + CopyVariableWithTensor(*var, *(out.get()), *trans_var); } } } diff --git a/paddle/framework/operator.h b/paddle/framework/operator.h index d5feb598649..c9140f304c8 100644 --- a/paddle/framework/operator.h +++ b/paddle/framework/operator.h @@ -54,33 +54,9 @@ constexpr char kGradVarSuffix[] = "@GRAD"; constexpr char kZeroVarSuffix[] = "@ZERO"; // define some kernel priority +/* Define multiple kernel type fallback order*/ extern std::vector> kKernelPriority; -/** - * @brief Use cpu kernel only - */ -void UseCPU(); - -/** - * @brief Perfer MKLDNN kernel than Plain CPU kernel - */ -void UseMKLDNN(); - -/** - * @brief Perfer CUDA kernel than Plain CPU kernel - */ -void UseCUDA(); - -/** - * @brief Perfer cudnn kernel than Plain CUDA kernel - */ -void UseCUDNN(); - -/** - * @brief Use all available kernels - */ -void UseALL(); - inline std::string GradVarName(const std::string& var_name) { return var_name + kGradVarSuffix; } diff --git a/paddle/operators/CMakeLists.txt b/paddle/operators/CMakeLists.txt index e1b695e8cd3..2569535c257 100644 --- a/paddle/operators/CMakeLists.txt +++ b/paddle/operators/CMakeLists.txt @@ -137,8 +137,6 @@ op_library(sum_op DEPS selected_rows_functor) op_library(sgd_op DEPS selected_rows_functor) op_library(print_op DEPS lod_tensor) op_library(adagrad_op DEPS selected_rows_functor) -op_library(conv_op DEPS vol2col) -op_library(pool_op DEPS pooling) op_library(maxout_op DEPS maxouting) op_library(unpool_op DEPS unpooling) op_library(pool_with_index_op DEPS pooling) @@ -149,12 +147,27 @@ op_library(max_sequence_len_op DEPS lod_rank_table) op_library(sequence_conv_op DEPS context_project) op_library(sequence_pool_op DEPS sequence_pooling) op_library(lstm_op DEPS sequence2batch lstm_compute) -op_library(conv_transpose_op DEPS vol2col) op_library(gru_op DEPS sequence2batch gru_compute) op_library(recurrent_op DEPS executor) op_library(warpctc_op DEPS dynload_warpctc sequence_padding math_function) op_library(cos_sim_op DEPS cos_sim_functor) op_library(parallel_do_op DEPS executor) + +# Regist multiple Kernel to pybind +if (WITH_GPU) +op_library(conv_op SRCS conv_op.cc conv_op.cu.cc conv_cudnn_op.cu.cc DEPS vol2col) +op_library(pool_op SRCS pool_op.cc pool_op.cu.cc pool_cudnn_op.cu.cc DEPS pooling) +op_library(conv_transpose_op SRCS conv_transpose_op.cc conv_transpose_op.cu.cc + conv_transpose_cudnn_op.cu.cc DEPS vol2col) +file(APPEND ${pybind_file} "USE_OP_DEVICE_KERNEL(conv2d, CUDNN);\n") +file(APPEND ${pybind_file} "USE_OP_DEVICE_KERNEL(pool2d, CUDNN);\n") +file(APPEND ${pybind_file} "USE_OP_DEVICE_KERNEL(conv2d_transpose, CUDNN);\n") +else() +op_library(conv_op SRCS conv_op.cc DEPS vol2col) +op_library(pool_op SRCS pool_op.cc DEPS pooling) +op_library(conv_transpose_op SRCS conv_transpose_op.cc DEPS vol2col) +endif() + # FIXME(typhoonzero): save/load depends lodtensor serialization functions op_library(save_op DEPS lod_tensor) op_library(load_op DEPS lod_tensor) diff --git a/paddle/operators/conv_cudnn_op.cc b/paddle/operators/conv_cudnn_op.cc deleted file mode 100644 index 84d9ce1973a..00000000000 --- a/paddle/operators/conv_cudnn_op.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include "paddle/operators/conv_op.h" - -namespace paddle { -namespace operators { - -class CudnnConv2DOpMaker : public Conv2DOpMaker { - public: - CudnnConv2DOpMaker(OpProto* proto, OpAttrChecker* op_checker) - : Conv2DOpMaker(proto, op_checker) { - AddAttr("workspace_size_MB", - "workspace size for cudnn, in MB, " - "workspace is a section of GPU memory which will be " - "allocated/freed each time the operator runs, larger " - "workspace size can increase performance but also requires " - "better hardware. This size should be chosen carefully.") - .SetDefault(4096); - } -}; - -class CudnnConv3DOpMaker : public Conv3DOpMaker { - public: - CudnnConv3DOpMaker(OpProto* proto, OpAttrChecker* op_checker) - : Conv3DOpMaker(proto, op_checker) { - AddAttr("workspace_size_MB", - "workspace size for cudnn, in MB, " - "workspace is a section of GPU memory which will be " - "allocated/freed each time the operator runs, larger " - "workspace size can increase performance but also requires " - "better hardware. This size should be chosen carefully.") - .SetDefault(4096); - } -}; - -} // namespace operators -} // namespace paddle - -namespace ops = paddle::operators; -REGISTER_OP(conv2d_cudnn, ops::ConvOp, ops::CudnnConv2DOpMaker, - conv2d_cudnn_grad, ops::ConvOpGrad); - -REGISTER_OP(conv3d_cudnn, ops::ConvOp, ops::CudnnConv3DOpMaker, - conv3d_cudnn_grad, ops::ConvOpGrad); - -REGISTER_OP_CPU_KERNEL( - conv2d_cudnn, - ops::GemmConvKernel, - ops::GemmConvKernel); -REGISTER_OP_CPU_KERNEL( - conv2d_cudnn_grad, - ops::GemmConvGradKernel, - ops::GemmConvGradKernel); - -REGISTER_OP_CPU_KERNEL( - conv3d_cudnn, - ops::GemmConvKernel, - ops::GemmConvKernel); -REGISTER_OP_CPU_KERNEL( - conv3d_cudnn_grad, - ops::GemmConvGradKernel, - ops::GemmConvGradKernel); diff --git a/paddle/operators/conv_cudnn_op.cu.cc b/paddle/operators/conv_cudnn_op.cu.cc index 0c5ed3e4e80..3a5409a7e3f 100644 --- a/paddle/operators/conv_cudnn_op.cu.cc +++ b/paddle/operators/conv_cudnn_op.cu.cc @@ -32,7 +32,7 @@ static constexpr size_t kCONV_CUDNN_WORKSPACE_LIMIT_BYTES = static_cast(1024) * 1024 * 1024; template -class CudnnConvOpKernel : public framework::OpKernel { +class CUDNNConvOpKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()), @@ -147,7 +147,7 @@ class CudnnConvOpKernel : public framework::OpKernel { }; template -class CudnnConvGradOpKernel : public framework::OpKernel { +class CUDNNConvGradOpKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()), @@ -315,17 +315,16 @@ class CudnnConvGradOpKernel : public framework::OpKernel { } // namespace operators } // namespace paddle -// TODO(dzhwinter) : below register should be removed -REGISTER_OP_CUDA_KERNEL(conv2d_cudnn, - paddle::operators::CudnnConvOpKernel, - paddle::operators::CudnnConvOpKernel); -REGISTER_OP_CUDA_KERNEL(conv2d_cudnn_grad, - paddle::operators::CudnnConvGradOpKernel, - paddle::operators::CudnnConvGradOpKernel); - -REGISTER_OP_CUDA_KERNEL(conv3d_cudnn, - paddle::operators::CudnnConvOpKernel, - paddle::operators::CudnnConvOpKernel); -REGISTER_OP_CUDA_KERNEL(conv3d_cudnn_grad, - paddle::operators::CudnnConvGradOpKernel, - paddle::operators::CudnnConvGradOpKernel); +REGISTER_OP_KERNEL(conv2d, CUDNN, ::paddle::platform::CUDAPlace, + paddle::operators::CUDNNConvOpKernel, + paddle::operators::CUDNNConvOpKernel); +REGISTER_OP_KERNEL(conv2d_grad, CUDNN, ::paddle::platform::CUDAPlace, + paddle::operators::CUDNNConvGradOpKernel, + paddle::operators::CUDNNConvGradOpKernel); + +REGISTER_OP_KERNEL(conv3d, CUDNN, ::paddle::platform::CUDAPlace, + paddle::operators::CUDNNConvOpKernel, + paddle::operators::CUDNNConvOpKernel); +REGISTER_OP_KERNEL(conv3d_grad, CUDNN, ::paddle::platform::CUDAPlace, + paddle::operators::CUDNNConvGradOpKernel, + paddle::operators::CUDNNConvGradOpKernel); diff --git a/paddle/operators/conv_op.cc b/paddle/operators/conv_op.cc index 1468e3eb960..424eccdb7dc 100644 --- a/paddle/operators/conv_op.cc +++ b/paddle/operators/conv_op.cc @@ -67,6 +67,23 @@ void ConvOp::InferShape(framework::InferShapeContext* ctx) const { ctx->ShareLoD("Input", "Output"); } +framework::OpKernelType ConvOp::GetExpectedKernelType( + const framework::ExecutionContext& ctx) const { + bool use_cudnn = ctx.Attr("use_cudnn"); + framework::LibraryType library_; + if (use_cudnn) { + library_ = framework::LibraryType::kCUDNN; + } else { + library_ = framework::LibraryType::kPlain; + } + + std::string data_format = ctx.Attr("data_format"); + framework::DataLayout layout_ = framework::StringToDataLayout(data_format); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Input")->type()), ctx.GetPlace(), + layout_, library_); +} + Conv2DOpMaker::Conv2DOpMaker(OpProto* proto, OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput( @@ -108,6 +125,26 @@ Conv2DOpMaker::Conv2DOpMaker(OpProto* proto, OpAttrChecker* op_checker) "dilations(h_dilation, w_dilation) of " "convolution operator.") .SetDefault({1, 1}); + AddAttr( + "use_cudnn", + "(bool, default false) Only used in cudnn kernel, need install cudnn") + .SetDefault(false); + AddAttr( + "data_format", + "(string, default NCHW) Only used in " + "An optional string from: \"NHWC\", \"NCHW\". " + "Defaults to \"NHWC\". Specify the data format of the output data, " + "the input will be transformed automatically. ") + .SetDefault("AnyLayout"); + // TODO(dzhwinter): need to registered layout transform function + AddAttr("workspace_size_MB", + "Only used in cudnn kernel. Need set use_cudnn to true." + "workspace size for cudnn, in MB, " + "workspace is a section of GPU memory which will be " + "allocated/freed each time the operator runs, larger " + "workspace size can increase performance but also requires " + "better hardware. This size should be chosen carefully.") + .SetDefault(4096); AddComment(R"DOC( Convolution Operator. @@ -181,6 +218,25 @@ Conv3DOpMaker::Conv3DOpMaker(OpProto* proto, OpAttrChecker* op_checker) "dilations(d_dilation, h_dilation, w_dilation) of " "convolution operator.") .SetDefault({1, 1, 1}); + AddAttr( + "use_cudnn", + "(bool, default false) Only used in cudnn kernel, need install cudnn") + .SetDefault(false); + AddAttr( + "data_format", + "(string, default NCHW) Only used in " + "An optional string from: \"NHWC\", \"NCHW\". " + "Defaults to \"NHWC\". Specify the data format of the output data, " + "the input will be transformed automatically. ") + .SetDefault("AnyLayout"); + // TODO(dzhwinter): need to registered layout transform function + AddAttr("workspace_size_MB", + "Only used in cudnn kernel. workspace size for cudnn, in MB, " + "workspace is a section of GPU memory which will be " + "allocated/freed each time the operator runs, larger " + "workspace size can increase performance but also requires " + "better hardware. This size should be chosen carefully.") + .SetDefault(4096); AddComment(R"DOC( Convolution3D Operator. @@ -224,6 +280,23 @@ void ConvOpGrad::InferShape(framework::InferShapeContext* ctx) const { } } +framework::OpKernelType ConvOpGrad::GetExpectedKernelType( + const framework::ExecutionContext& ctx) const { + bool use_cudnn = ctx.Attr("use_cudnn"); + framework::LibraryType library_; + if (use_cudnn) { + library_ = framework::LibraryType::kCUDNN; + } else { + library_ = framework::LibraryType::kPlain; + } + + std::string data_format = ctx.Attr("data_format"); + framework::DataLayout layout_ = framework::StringToDataLayout(data_format); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Input")->type()), ctx.GetPlace(), + layout_, library_); +} + } // namespace operators } // namespace paddle diff --git a/paddle/operators/conv_op.h b/paddle/operators/conv_op.h index 83786e2329e..5a8933e7915 100644 --- a/paddle/operators/conv_op.h +++ b/paddle/operators/conv_op.h @@ -62,12 +62,20 @@ class ConvOp : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override; }; class ConvOpGrad : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override; }; template diff --git a/paddle/operators/conv_transpose_cudnn_op.cc b/paddle/operators/conv_transpose_cudnn_op.cc deleted file mode 100644 index 2e5333a265f..00000000000 --- a/paddle/operators/conv_transpose_cudnn_op.cc +++ /dev/null @@ -1,78 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include "paddle/operators/conv_transpose_op.h" - -namespace paddle { -namespace operators { - -class CudnnConv2DTransposeOpMaker : public Conv2DTransposeOpMaker { - public: - CudnnConv2DTransposeOpMaker(OpProto* proto, OpAttrChecker* op_checker) - : Conv2DTransposeOpMaker(proto, op_checker) { - AddAttr("workspace_size_MB", - "workspace size for cudnn, in MB, " - "workspace is a section of GPU memory which will be " - "allocated/freed each time the operator runs, larger " - "workspace size can increase performance but also requires " - "better hardward. This size should be carefully setted.") - .SetDefault(4096); - } -}; - -class CudnnConv3DTransposeOpMaker : public Conv3DTransposeOpMaker { - public: - CudnnConv3DTransposeOpMaker(OpProto* proto, OpAttrChecker* op_checker) - : Conv3DTransposeOpMaker(proto, op_checker) { - AddAttr("workspace_size_MB", - "workspace size for cudnn, in MB, " - "workspace is a section of GPU memory which will be " - "allocated/freed each time the operator runs, larger " - "workspace size can increase performance but also requires " - "better hardward. This size should be carefully setted.") - .SetDefault(4096); - } -}; - -} // namespace operators -} // namespace paddle - -namespace ops = paddle::operators; -REGISTER_OP(conv2d_transpose_cudnn, ops::ConvTransposeOp, - ops::CudnnConv2DTransposeOpMaker, conv2d_transpose_cudnn_grad, - ops::ConvTransposeOpGrad); - -REGISTER_OP_CPU_KERNEL( - conv2d_transpose_cudnn, - ops::GemmConvTransposeKernel, - ops::GemmConvTransposeKernel); -REGISTER_OP_CPU_KERNEL( - conv2d_transpose_cudnn_grad, - ops::GemmConvTransposeGradKernel, - ops::GemmConvTransposeGradKernel); - -REGISTER_OP(conv3d_transpose_cudnn, ops::ConvTransposeOp, - ops::CudnnConv3DTransposeOpMaker, conv3d_transpose_cudnn_grad, - ops::ConvTransposeOpGrad); - -REGISTER_OP_CPU_KERNEL( - conv3d_transpose_cudnn, - ops::GemmConvTransposeKernel, - ops::GemmConvTransposeKernel); -REGISTER_OP_CPU_KERNEL( - conv3d_transpose_cudnn_grad, - ops::GemmConvTransposeGradKernel, - ops::GemmConvTransposeGradKernel); diff --git a/paddle/operators/conv_transpose_cudnn_op.cu.cc b/paddle/operators/conv_transpose_cudnn_op.cu.cc index fc37776ba1e..23bc97e13c1 100644 --- a/paddle/operators/conv_transpose_cudnn_op.cu.cc +++ b/paddle/operators/conv_transpose_cudnn_op.cu.cc @@ -28,10 +28,10 @@ using ScopedFilterDescriptor = platform::ScopedFilterDescriptor; using ScopedConvolutionDescriptor = platform::ScopedConvolutionDescriptor; using DataLayout = platform::DataLayout; -static constexpr size_t kConvCudnnWorkspaceLimitBytes = 1024 * 1024 * 1024; +static constexpr size_t kConvCUDNNWorkspaceLimitBytes = 1024 * 1024 * 1024; template -class CudnnConvTransposeOpKernel : public framework::OpKernel { +class CUDNNConvTransposeOpKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()), @@ -77,7 +77,7 @@ class CudnnConvTransposeOpKernel : public framework::OpKernel { // ------------------- cudnn conv workspace --------------------- void* cudnn_workspace = nullptr; size_t workspace_size_in_bytes; // final workspace to allocate. - size_t workspace_size_limit = kConvCudnnWorkspaceLimitBytes; + size_t workspace_size_limit = kConvCUDNNWorkspaceLimitBytes; if (user_workspace_size > 0) { workspace_size_limit = user_workspace_size * 1024 * 1024; } @@ -116,7 +116,7 @@ class CudnnConvTransposeOpKernel : public framework::OpKernel { }; template -class CudnnConvTransposeGradOpKernel : public framework::OpKernel { +class CUDNNConvTransposeGradOpKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()), @@ -161,7 +161,7 @@ class CudnnConvTransposeGradOpKernel : public framework::OpKernel { cudnnConvolutionBwdFilterAlgo_t filter_algo; size_t bwd_filter_ws_size, fwd_ws_size; size_t workspace_size_in_bytes = 0; - size_t workspace_size_limit = kConvCudnnWorkspaceLimitBytes; + size_t workspace_size_limit = kConvCUDNNWorkspaceLimitBytes; if (user_workspace_size > 0) { workspace_size_limit = user_workspace_size * 1024 * 1024; } @@ -236,16 +236,16 @@ class CudnnConvTransposeGradOpKernel : public framework::OpKernel { namespace ops = paddle::operators; -REGISTER_OP_CUDA_KERNEL(conv2d_transpose_cudnn, - ops::CudnnConvTransposeOpKernel, - ops::CudnnConvTransposeOpKernel); -REGISTER_OP_CUDA_KERNEL(conv2d_transpose_cudnn_grad, - ops::CudnnConvTransposeGradOpKernel, - ops::CudnnConvTransposeGradOpKernel); - -REGISTER_OP_CUDA_KERNEL(conv3d_transpose_cudnn, - ops::CudnnConvTransposeOpKernel, - ops::CudnnConvTransposeOpKernel); -REGISTER_OP_CUDA_KERNEL(conv3d_transpose_cudnn_grad, - ops::CudnnConvTransposeGradOpKernel, - ops::CudnnConvTransposeGradOpKernel); +REGISTER_OP_KERNEL(conv2d_transpose, CUDNN, ::paddle::platform::CUDAPlace, + ops::CUDNNConvTransposeOpKernel, + ops::CUDNNConvTransposeOpKernel); +REGISTER_OP_KERNEL(conv2d_transpose_grad, CUDNN, ::paddle::platform::CUDAPlace, + ops::CUDNNConvTransposeGradOpKernel, + ops::CUDNNConvTransposeGradOpKernel); + +REGISTER_OP_KERNEL(conv3d_transpose, CUDNN, ::paddle::platform::CUDAPlace, + ops::CUDNNConvTransposeOpKernel, + ops::CUDNNConvTransposeOpKernel); +REGISTER_OP_KERNEL(conv3d_transpose_grad, CUDNN, ::paddle::platform::CUDAPlace, + ops::CUDNNConvTransposeGradOpKernel, + ops::CUDNNConvTransposeGradOpKernel); diff --git a/paddle/operators/conv_transpose_op.cc b/paddle/operators/conv_transpose_op.cc index 74636d138f1..cf4e8c0a303 100644 --- a/paddle/operators/conv_transpose_op.cc +++ b/paddle/operators/conv_transpose_op.cc @@ -58,6 +58,23 @@ void ConvTransposeOp::InferShape(framework::InferShapeContext* ctx) const { ctx->SetOutputDim("Output", framework::make_ddim(output_shape)); } +framework::OpKernelType ConvTransposeOp::GetExpectedKernelType( + const framework::ExecutionContext& ctx) const { + bool use_cudnn = ctx.Attr("use_cudnn"); + framework::LibraryType library_; + if (use_cudnn) { + library_ = framework::LibraryType::kCUDNN; + } else { + library_ = framework::LibraryType::kPlain; + } + + std::string data_format = ctx.Attr("data_format"); + framework::DataLayout layout_ = framework::StringToDataLayout(data_format); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Input")->type()), ctx.GetPlace(), + layout_, library_); +} + Conv2DTransposeOpMaker::Conv2DTransposeOpMaker(OpProto* proto, OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { @@ -94,6 +111,25 @@ Conv2DTransposeOpMaker::Conv2DTransposeOpMaker(OpProto* proto, "(vector default:{0, 0}), the paddings(h_pad, w_pad) of convolution " "transpose operator.") .SetDefault({0, 0}); + AddAttr( + "use_cudnn", + "(bool, default false) Only used in cudnn kernel, need install cudnn") + .SetDefault(false); + AddAttr( + "data_format", + "(string, default NCHW) Only used in " + "An optional string from: \"NHWC\", \"NCHW\". " + "Defaults to \"NHWC\". Specify the data format of the output data, " + "the input will be transformed automatically. ") + .SetDefault("AnyLayout"); + // TODO(dzhwinter): need to registered layout transform function + AddAttr("workspace_size_MB", + "Used in cudnn kernel only. workspace size for cudnn, in MB, " + "workspace is a section of GPU memory which will be " + "allocated/freed each time the operator runs, larger " + "workspace size can increase performance but also requires " + "better hardward. This size should be carefully setted.") + .SetDefault(4096); AddComment(R"DOC( Convolution2D Transpose Operator. @@ -163,6 +199,25 @@ Conv3DTransposeOpMaker::Conv3DTransposeOpMaker(OpProto* proto, "(vector default:{0, 0, 0}), paddings(d_pad, " "h_pad, w_pad) of convolution transpose operator.") .SetDefault({0, 0, 0}); + AddAttr( + "use_cudnn", + "(bool, default false) Only used in cudnn kernel, need install cudnn") + .SetDefault(false); + AddAttr( + "data_format", + "(string, default NCHW) Only used in " + "An optional string from: \"NHWC\", \"NCHW\". " + "Defaults to \"NHWC\". Specify the data format of the output data, " + "the input will be transformed automatically. ") + .SetDefault("AnyLayout"); + // TODO(dzhwinter): need to registered layout transform function + AddAttr("workspace_size_MB", + "Used in cudnn kernel only. workspace size for cudnn, in MB, " + "workspace is a section of GPU memory which will be " + "allocated/freed each time the operator runs, larger " + "workspace size can increase performance but also requires " + "better hardward. This size should be carefully setted.") + .SetDefault(4096); AddComment(R"DOC( Convolution3D Transpose Operator. @@ -205,6 +260,23 @@ void ConvTransposeOpGrad::InferShape(framework::InferShapeContext* ctx) const { } } +framework::OpKernelType ConvTransposeOpGrad::GetExpectedKernelType( + const framework::ExecutionContext& ctx) const { + bool use_cudnn = ctx.Attr("use_cudnn"); + framework::LibraryType library_; + if (use_cudnn) { + library_ = framework::LibraryType::kCUDNN; + } else { + library_ = framework::LibraryType::kPlain; + } + + std::string data_format = ctx.Attr("data_format"); + framework::DataLayout layout_ = framework::StringToDataLayout(data_format); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Input")->type()), ctx.GetPlace(), + layout_, library_); +} + } // namespace operators } // namespace paddle diff --git a/paddle/operators/conv_transpose_op.h b/paddle/operators/conv_transpose_op.h index 4c8f8a80672..a42ade41b16 100644 --- a/paddle/operators/conv_transpose_op.h +++ b/paddle/operators/conv_transpose_op.h @@ -42,12 +42,20 @@ class ConvTransposeOp : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override; }; class ConvTransposeOpGrad : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override; }; template diff --git a/paddle/operators/math/sequence2batch.cc b/paddle/operators/math/sequence2batch.cc index 88977be1f8c..e459a42ca25 100644 --- a/paddle/operators/math/sequence2batch.cc +++ b/paddle/operators/math/sequence2batch.cc @@ -13,6 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/operators/math/sequence2batch.h" +#include "paddle/operators/math/math_function.h" namespace paddle { namespace operators { diff --git a/paddle/operators/pool_cudnn_op.cc b/paddle/operators/pool_cudnn_op.cc deleted file mode 100644 index 77407f5cdf7..00000000000 --- a/paddle/operators/pool_cudnn_op.cc +++ /dev/null @@ -1,39 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#include "paddle/operators/pool_cudnn_op.h" - -namespace ops = paddle::operators; - -REGISTER_OP(pool2d_cudnn, ops::PoolOp, ops::Pool2dOpMaker, pool2d_cudnn_grad, - ops::PoolOpGrad); - -REGISTER_OP_CPU_KERNEL( - pool2d_cudnn, ops::PoolKernel, - ops::PoolKernel); -REGISTER_OP_CPU_KERNEL( - pool2d_cudnn_grad, - ops::PoolGradKernel, - ops::PoolGradKernel) - -REGISTER_OP(pool3d_cudnn, ops::PoolOp, ops::Pool3dOpMaker, pool3d_cudnn_grad, - ops::PoolOpGrad); - -REGISTER_OP_CPU_KERNEL( - pool3d_cudnn, ops::PoolKernel, - ops::PoolKernel); -REGISTER_OP_CPU_KERNEL( - pool3d_cudnn_grad, - ops::PoolGradKernel, - ops::PoolGradKernel) diff --git a/paddle/operators/pool_cudnn_op.cu.cc b/paddle/operators/pool_cudnn_op.cu.cc index 2d0001ba118..446fb0819d9 100644 --- a/paddle/operators/pool_cudnn_op.cu.cc +++ b/paddle/operators/pool_cudnn_op.cu.cc @@ -12,7 +12,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include "paddle/operators/pool_cudnn_op.h" +#include "paddle/framework/op_registry.h" +#include "paddle/operators/pool_op.h" #include "paddle/platform/cudnn_helper.h" namespace paddle { @@ -25,7 +26,7 @@ using DataLayout = platform::DataLayout; using PoolingMode = platform::PoolingMode; template -class PoolCudnnOpKernel : public framework::OpKernel { +class PoolCUDNNOpKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext &ctx) const override { PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()), @@ -86,7 +87,7 @@ class PoolCudnnOpKernel : public framework::OpKernel { }; template -class PoolCudnnGradOpKernel : public framework::OpKernel { +class PoolCUDNNGradOpKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext &ctx) const override { PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()), @@ -162,12 +163,16 @@ class PoolCudnnGradOpKernel : public framework::OpKernel { namespace ops = paddle::operators; -REGISTER_OP_CUDA_KERNEL(pool2d_cudnn, ops::PoolCudnnOpKernel, - ops::PoolCudnnOpKernel); -REGISTER_OP_CUDA_KERNEL(pool2d_cudnn_grad, ops::PoolCudnnGradOpKernel, - ops::PoolCudnnGradOpKernel); - -REGISTER_OP_CUDA_KERNEL(pool3d_cudnn, ops::PoolCudnnOpKernel, - ops::PoolCudnnOpKernel); -REGISTER_OP_CUDA_KERNEL(pool3d_cudnn_grad, ops::PoolCudnnGradOpKernel, - ops::PoolCudnnGradOpKernel); +REGISTER_OP_KERNEL(pool2d, CUDNN, ::paddle::platform::CUDAPlace, + ops::PoolCUDNNOpKernel, + ops::PoolCUDNNOpKernel); +REGISTER_OP_KERNEL(pool2d_grad, CUDNN, ::paddle::platform::CUDAPlace, + ops::PoolCUDNNGradOpKernel, + ops::PoolCUDNNGradOpKernel); + +REGISTER_OP_KERNEL(pool3d, CUDNN, ::paddle::platform::CUDAPlace, + ops::PoolCUDNNOpKernel, + ops::PoolCUDNNOpKernel); +REGISTER_OP_KERNEL(pool3d_grad, CUDNN, ::paddle::platform::CUDAPlace, + ops::PoolCUDNNGradOpKernel, + ops::PoolCUDNNGradOpKernel); diff --git a/paddle/operators/pool_cudnn_op.h b/paddle/operators/pool_cudnn_op.h deleted file mode 100644 index 5adf27f5bcc..00000000000 --- a/paddle/operators/pool_cudnn_op.h +++ /dev/null @@ -1,19 +0,0 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ - -#pragma once - -#include "paddle/framework/op_registry.h" -#include "paddle/operators/pool_op.h" - -namespace paddle { -namespace operators {} // namespace operators -} // namespace paddle diff --git a/paddle/operators/pool_op.cc b/paddle/operators/pool_op.cc index d3cf5fa638c..3e567efd082 100644 --- a/paddle/operators/pool_op.cc +++ b/paddle/operators/pool_op.cc @@ -61,6 +61,23 @@ void PoolOp::InferShape(framework::InferShapeContext *ctx) const { ctx->ShareLoD("X", "Out"); } +framework::OpKernelType PoolOp::GetExpectedKernelType( + const framework::ExecutionContext &ctx) const { + bool use_cudnn = ctx.Attr("use_cudnn"); + framework::LibraryType library_; + if (use_cudnn) { + library_ = framework::LibraryType::kCUDNN; + } else { + library_ = framework::LibraryType::kPlain; + } + + std::string data_format = ctx.Attr("data_format"); + framework::DataLayout layout_ = framework::StringToDataLayout(data_format); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("X")->type()), ctx.GetPlace(), + layout_, library_); +} + void PoolOpGrad::InferShape(framework::InferShapeContext *ctx) const { PADDLE_ENFORCE(ctx->HasInput("X"), "Input(X) must not be null."); PADDLE_ENFORCE(ctx->HasOutput(framework::GradVarName("X")), @@ -68,6 +85,23 @@ void PoolOpGrad::InferShape(framework::InferShapeContext *ctx) const { ctx->SetOutputDim(framework::GradVarName("X"), ctx->GetInputDim("X")); } +framework::OpKernelType PoolOpGrad::GetExpectedKernelType( + const framework::ExecutionContext &ctx) const { + bool use_cudnn = ctx.Attr("use_cudnn"); + framework::LibraryType library_; + if (use_cudnn) { + library_ = framework::LibraryType::kCUDNN; + } else { + library_ = framework::LibraryType::kPlain; + } + + std::string data_format = ctx.Attr("data_format"); + framework::DataLayout layout_ = framework::StringToDataLayout(data_format); + return framework::OpKernelType( + framework::ToDataType(ctx.Input("X")->type()), ctx.GetPlace(), + layout_, library_); +} + Pool2dOpMaker::Pool2dOpMaker(OpProto *proto, OpAttrChecker *op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput( @@ -101,15 +135,27 @@ Pool2dOpMaker::Pool2dOpMaker(OpProto *proto, OpAttrChecker *op_checker) AddAttr>("strides", "(vector, default {1, 1}), strides(height, " "width) of pooling operator.") - .SetDefault({1, 1}); // TODO(Chengduo): Add checker. (Currently, + .SetDefault({1, 1}); + // TODO(Chengduo): Add checker. (Currently, // TypedAttrChecker don't support vector type.) AddAttr>( "paddings", "(vector, default {0,0}), paddings(height, width) of pooling " "operator." "If global_pooling = true, paddings and ksize will be ignored.") - .SetDefault({0, 0}); // TODO(Chengduo): Add checker. (Currently, - // TypedAttrChecker don't support vector type.) + .SetDefault({0, 0}); + AddAttr( + "use_cudnn", + "(bool, default false) Only used in cudnn kernel, need install cudnn") + .SetDefault(false); + AddAttr( + "data_format", + "(string, default NCHW) Only used in " + "An optional string from: \"NHWC\", \"NCHW\". " + "Defaults to \"NHWC\". Specify the data format of the output data, " + "the input will be transformed automatically. ") + .SetDefault("AnyLayout"); + // TODO(dzhwinter): need to registered layout transform function AddComment(R"DOC( Pool2d Operator. @@ -182,6 +228,19 @@ Pool3dOpMaker::Pool3dOpMaker(OpProto *proto, OpAttrChecker *op_checker) .SetDefault({0, 0, 0}); // TODO(Chengduo): Add checker. (Currently, // TypedAttrChecker don't support vector type.) + AddAttr( + "use_cudnn", + "(bool, default false) Only used in cudnn kernel, need install cudnn") + .SetDefault(false); + AddAttr( + "data_format", + "(string, default NCHW) Only used in " + "An optional string from: \"NHWC\", \"NCHW\". " + "Defaults to \"NHWC\". Specify the data format of the output data, " + "the input will be transformed automatically. ") + .SetDefault("AnyLayout"); + // TODO(dzhwinter): need to registered layout transform function + AddComment(R"DOC( Pool3d Operator. diff --git a/paddle/operators/pool_op.h b/paddle/operators/pool_op.h index 3860e295f4b..c3d82ecbdeb 100644 --- a/paddle/operators/pool_op.h +++ b/paddle/operators/pool_op.h @@ -29,6 +29,10 @@ class PoolOp : public framework::OperatorWithKernel { using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override; }; class PoolOpGrad : public framework::OperatorWithKernel { @@ -36,6 +40,10 @@ class PoolOpGrad : public framework::OperatorWithKernel { using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override; + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override; }; class Pool2dOpMaker : public framework::OpProtoAndCheckerMaker { diff --git a/paddle/platform/dynload/cudnn.cc b/paddle/platform/dynload/cudnn.cc index 76ec82e1084..701f6240fef 100644 --- a/paddle/platform/dynload/cudnn.cc +++ b/paddle/platform/dynload/cudnn.cc @@ -44,7 +44,7 @@ CUDNN_DNN_ROUTINE_EACH_R7(DEFINE_WRAP); #ifdef PADDLE_USE_DSO bool HasCUDNN() { - std::call_once(cudnn_dso_flag, GetCudnnDsoHandle, &cudnn_dso_handle); + std::call_once(cudnn_dso_flag, GetCUDNNDsoHandle, &cudnn_dso_handle); return cudnn_dso_handle != nullptr; } diff --git a/paddle/platform/dynload/cudnn.h b/paddle/platform/dynload/cudnn.h index 8c937b37d71..b9263479494 100644 --- a/paddle/platform/dynload/cudnn.h +++ b/paddle/platform/dynload/cudnn.h @@ -36,7 +36,7 @@ extern void EnforceCUDNNLoaded(const char* fn_name); auto operator()(Args... args) -> decltype(__name(args...)) { \ using cudnn_func = decltype(__name(args...)) (*)(Args...); \ std::call_once(cudnn_dso_flag, \ - paddle::platform::dynload::GetCudnnDsoHandle, \ + paddle::platform::dynload::GetCUDNNDsoHandle, \ &cudnn_dso_handle); \ EnforceCUDNNLoaded(#__name); \ void* p_##__name = dlsym(cudnn_dso_handle, #__name); \ diff --git a/paddle/platform/dynload/dynamic_loader.cc b/paddle/platform/dynload/dynamic_loader.cc index 7a82d06a0ac..c8c09ae608f 100644 --- a/paddle/platform/dynload/dynamic_loader.cc +++ b/paddle/platform/dynload/dynamic_loader.cc @@ -134,7 +134,7 @@ void GetCublasDsoHandle(void** dso_handle) { #endif } -void GetCudnnDsoHandle(void** dso_handle) { +void GetCUDNNDsoHandle(void** dso_handle) { #if defined(__APPLE__) || defined(__OSX__) GetDsoHandleFromSearchPath(FLAGS_cudnn_dir, "libcudnn.dylib", dso_handle, false); diff --git a/paddle/platform/dynload/dynamic_loader.h b/paddle/platform/dynload/dynamic_loader.h index c0e5452e5ae..7b0c8c16d74 100644 --- a/paddle/platform/dynload/dynamic_loader.h +++ b/paddle/platform/dynload/dynamic_loader.h @@ -32,7 +32,7 @@ void GetCublasDsoHandle(void** dso_handle); * @param **dso_handle dso handler * */ -void GetCudnnDsoHandle(void** dso_handle); +void GetCUDNNDsoHandle(void** dso_handle); /** * @brief load the DSO of CURAND diff --git a/paddle/pybind/pybind.cc b/paddle/pybind/pybind.cc index 5d170c66e97..c5d70bc9f91 100644 --- a/paddle/pybind/pybind.cc +++ b/paddle/pybind/pybind.cc @@ -430,13 +430,8 @@ All parameter, weight, gradient are variables in Paddle. m.def("init_glog", framework::InitGLOG); m.def("init_devices", &framework::InitDevices); - m.def("use_cpu", framework::UseCPU); - m.def("use_mkldnn", framework::UseMKLDNN); - m.def("use_cuda", framework::UseCUDA); - m.def("use_cudnn", framework::UseCUDNN); - m.def("use_all", framework::UseALL); - m.def("is_compile_gpu", IsCompileGPU); + m.def("set_feed_variable", framework::SetFeedVariable); m.def("get_fetch_variable", framework::GetFetchVariable); diff --git a/paddle/pybind/tensor_py.h b/paddle/pybind/tensor_py.h index 6b4290972ba..3b5210e2b91 100644 --- a/paddle/pybind/tensor_py.h +++ b/paddle/pybind/tensor_py.h @@ -14,7 +14,7 @@ limitations under the License. */ #pragma once #include -#include "paddle/framework/tensor.h" +#include "paddle/framework/lod_tensor.h" #include "paddle/memory/memcpy.h" #include "paddle/platform/device_context.h" #include "pybind11/numpy.h" @@ -97,14 +97,27 @@ inline py::buffer_info CastToPyBuffer(framework::Tensor &tensor) { template T TensorGetElement(framework::Tensor &self, size_t offset) { - PADDLE_ENFORCE(platform::is_cpu_place(self.place())); - return self.data()[offset]; + if (platform::is_cpu_place(self.place())) { + return self.data()[offset]; + } else { + std::shared_ptr dst(new framework::Tensor); + framework::Copy(self, platform::CPUPlace(), dst.get()); + return dst->data()[offset]; + } } +// TODO(dzhwinter) : fix the redundent Tensor allocate and free template void TensorSetElement(framework::Tensor &self, size_t offset, T elem) { - PADDLE_ENFORCE(platform::is_cpu_place(self.place())); - self.data()[offset] = elem; + if (platform::is_gpu_place(self.place())) { + std::shared_ptr dst(new framework::Tensor); + framework::Copy(self, platform::CPUPlace(), dst.get()); + dst->data()[offset] = elem; + framework::Copy(*dst.get(), self.place(), &self); + + } else if (platform::is_cpu_place(self.place())) { + self.data()[offset] = elem; + } } template diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 94184d59f6f..99a40ce45a2 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -775,7 +775,7 @@ def conv2d(input, pre_bias = helper.create_tmp_variable(dtype) helper.append_op( - type='conv2d_cudnn', + type='conv2d', inputs={ 'Input': input, 'Filter': filter_param, diff --git a/python/paddle/v2/fluid/tests/op_test.py b/python/paddle/v2/fluid/tests/op_test.py index b77d2b1268f..276cf2c5f2d 100644 --- a/python/paddle/v2/fluid/tests/op_test.py +++ b/python/paddle/v2/fluid/tests/op_test.py @@ -31,7 +31,8 @@ def create_op(scope, op_type, inputs, outputs, attrs): kwargs[in_name] = [] if in_dup: sub_in = inputs[in_name] - for sub_in_name, _ in sub_in: + for item in sub_in: + sub_in_name, _ = item[0], item[1] __create_var__(in_name, sub_in_name) else: __create_var__(in_name, in_name) @@ -41,7 +42,8 @@ def create_op(scope, op_type, inputs, outputs, attrs): kwargs[out_name] = [] if out_dup: sub_out = outputs[out_name] - for sub_out_name, _ in sub_out: + for item in sub_out: + sub_out_name, _ = item[0], item[1] __create_var__(out_name, sub_out_name) else: __create_var__(out_name, out_name) @@ -71,13 +73,15 @@ def set_input(scope, op, inputs, place): if in_name in inputs: if in_dup: sub_in = inputs[in_name] - for sub_in_name, sub_in_val in sub_in: + for item in sub_in: + sub_in_name, sub_in_val = item[0], item[1] __set_input__(sub_in_name, sub_in_val) else: __set_input__(in_name, inputs[in_name]) -def get_numeric_gradient(scope, +def get_numeric_gradient(place, + scope, op, inputs, input_to_check, @@ -85,7 +89,7 @@ def get_numeric_gradient(scope, delta=0.005, in_place=False): # FIXME: change this method by compile time concepts - set_input(scope, op, inputs, core.CPUPlace()) + set_input(scope, op, inputs, place) def product(dim): return reduce(lambda a, b: a * b, dim, 1) @@ -93,7 +97,7 @@ def get_numeric_gradient(scope, def get_output(): sum = [] for output_name in output_names: - op.run(scope, core.CPUPlace()) + op.run(scope, place) sum.append( np.array(scope.find_var(output_name).get_tensor()).mean()) return np.array(sum).mean() @@ -127,7 +131,7 @@ def get_numeric_gradient(scope, # we use a for loop to compute the gradient of every element. for i in xrange(tensor_size): if in_place: - set_input(scope, op, inputs, core.CPUPlace()) + set_input(scope, op, inputs, place) # get one input element throw it's index i. origin = __get_elem__(tensor_to_check, i) @@ -137,7 +141,7 @@ def get_numeric_gradient(scope, y_pos = get_output() if in_place: - set_input(scope, op, inputs, core.CPUPlace()) + set_input(scope, op, inputs, place) x_neg = origin - delta __set_elem__(tensor_to_check, i, x_neg) @@ -283,7 +287,8 @@ class OpTest(unittest.TestCase): if not isinstance(sub_out, list): raise AssertionError("sub_out type %s is not list", type(sub_out)) - for sub_out_name, expect in sub_out: + for item in sub_out: + sub_out_name, expect = item[0], item[1] idx = find_actual(sub_out_name, fetch_list) actual = outs[idx] actual_t = np.array(actual) @@ -347,6 +352,24 @@ class OpTest(unittest.TestCase): in_place=False, max_relative_error=0.005, user_defined_grads=None): + places = [core.CPUPlace()] + if core.is_compile_gpu() and core.op_support_gpu(self.op_type): + places.append(core.CUDAPlace(0)) + for place in places: + self.check_grad_with_place(place, inputs_to_check, output_names, + no_grad_set, numeric_grad_delta, + in_place, max_relative_error, + user_defined_grads) + + def check_grad_with_place(self, + place, + inputs_to_check, + output_names, + no_grad_set=None, + numeric_grad_delta=0.005, + in_place=False, + max_relative_error=0.005, + user_defined_grads=None): self.scope = core.Scope() op_inputs = self.inputs if hasattr(self, "inputs") else dict() op_outputs = self.outputs if hasattr(self, "outputs") else dict() @@ -362,6 +385,7 @@ class OpTest(unittest.TestCase): numeric_grads = user_defined_grads or [ get_numeric_gradient( + place, self.scope, self.op, self.inputs, @@ -370,22 +394,12 @@ class OpTest(unittest.TestCase): delta=numeric_grad_delta, in_place=in_place) for input_to_check in inputs_to_check ] - cpu_place = core.CPUPlace() - cpu_analytic_grads = self._get_gradient(inputs_to_check, cpu_place, - output_names, no_grad_set) - - self.__assert_is_close(numeric_grads, cpu_analytic_grads, - inputs_to_check, max_relative_error, - "Gradient Check On %s" % str(cpu_place)) - - if core.is_compile_gpu() and self.op.support_gpu(): - gpu_place = core.CUDAPlace(0) - gpu_analytic_grads = self._get_gradient(inputs_to_check, gpu_place, - output_names, no_grad_set) - - self.__assert_is_close(numeric_grads, gpu_analytic_grads, - inputs_to_check, max_relative_error, - "Gradient Check On %s" % str(gpu_place)) + analytic_grads = self._get_gradient(inputs_to_check, place, + output_names, no_grad_set) + + self.__assert_is_close(numeric_grads, analytic_grads, inputs_to_check, + max_relative_error, + "Gradient Check On %s" % str(place)) @staticmethod def _create_var_descs_(block, var_dict): diff --git a/python/paddle/v2/fluid/tests/test_conv2d_op.py b/python/paddle/v2/fluid/tests/test_conv2d_op.py index 958300e655e..e9a19d1774f 100644 --- a/python/paddle/v2/fluid/tests/test_conv2d_op.py +++ b/python/paddle/v2/fluid/tests/test_conv2d_op.py @@ -49,7 +49,7 @@ def conv2d_forward_naive(input, filter, group, conv_param): class TestConv2dOp(OpTest): def setUp(self): - core.use_cuda() + self.use_cudnn = False self.init_op_type() self.init_group() self.init_dilation() @@ -70,30 +70,59 @@ class TestConv2dOp(OpTest): 'strides': self.stride, 'paddings': self.pad, 'groups': self.groups, - 'dilations': self.dilations + 'dilations': self.dilations, + 'use_cudnn': self.use_cudnn } self.outputs = {'Output': output} def test_check_output(self): - self.check_output() + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_output_with_place(place, atol=1e-5) + else: + self.check_output() def test_check_grad(self): - self.check_grad( - set(['Input', 'Filter']), 'Output', max_relative_error=0.02) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, + set(['Input', 'Filter']), + 'Output', + max_relative_error=0.02) + else: + self.check_grad( + set(['Input', 'Filter']), 'Output', max_relative_error=0.02) def test_check_grad_no_filter(self): - self.check_grad( - ['Input'], - 'Output', - max_relative_error=0.02, - no_grad_set=set(['Filter'])) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, ['Input'], + 'Output', + max_relative_error=0.02, + no_grad_set=set(['Filter'])) + else: + self.check_grad( + ['Input'], + 'Output', + max_relative_error=0.02, + no_grad_set=set(['Filter'])) def test_check_grad_no_input(self): - self.check_grad( - ['Filter'], - 'Output', - max_relative_error=0.02, - no_grad_set=set(['Input'])) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, ['Filter'], + 'Output', + max_relative_error=0.02, + no_grad_set=set(['Input'])) + else: + self.check_grad( + ['Filter'], + 'Output', + max_relative_error=0.02, + no_grad_set=set(['Input'])) def init_test_case(self): self.pad = [0, 0] @@ -167,39 +196,39 @@ class TestWithDilation(TestConv2dOp): self.groups = 3 -#----------------Conv2dCudnn---------------- -class TestCudnn(TestConv2dOp): +#----------------Conv2dCUDNN---------------- +class TestCUDNN(TestConv2dOp): def init_op_type(self): - core.use_cudnn() - self.op_type = "conv2d_cudnn" + self.use_cudnn = True + self.op_type = "conv2d" -class TestCudnnWithPad(TestWithPad): +class TestCUDNNWithPad(TestWithPad): def init_op_type(self): - core.use_cudnn() - self.op_type = "conv2d_cudnn" + self.use_cudnn = True + self.op_type = "conv2d" -class TestCudnnWithStride(TestWithStride): +class TestCUDNNWithStride(TestWithStride): def init_op_type(self): - core.use_cudnn() - self.op_type = "conv2d_cudnn" + self.use_cudnn = True + self.op_type = "conv2d" -class TestCudnnWithGroup(TestWithGroup): +class TestCUDNNWithGroup(TestWithGroup): def init_op_type(self): - core.use_cudnn() - self.op_type = "conv2d_cudnn" + self.use_cudnn = True + self.op_type = "conv2d" -class TestCudnnWith1x1(TestWith1x1): +class TestCUDNNWith1x1(TestWith1x1): def init_op_type(self): - core.use_cudnn() - self.op_type = "conv2d_cudnn" + self.use_cudnn = True + self.op_type = "conv2d" # cudnn v5 does not support dilation conv. -# class TestCudnnWithDilation(TestWithDilation): +# class TestCUDNNWithDilation(TestWithDilation): # def init_op_type(self): # self.op_type = "conv_cudnn" diff --git a/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py b/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py index d59537b924d..4aec32fc6e7 100644 --- a/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py +++ b/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py @@ -1,5 +1,7 @@ import unittest import numpy as np + +import paddle.v2.fluid.core as core from op_test import OpTest @@ -37,6 +39,7 @@ def conv2dtranspose_forward_naive(input_, filter_, attrs): class TestConv2dTransposeOp(OpTest): def setUp(self): # init as conv transpose + self.use_cudnn = False self.init_op_type() self.init_test_case() @@ -47,7 +50,9 @@ class TestConv2dTransposeOp(OpTest): self.attrs = { 'strides': self.stride, 'paddings': self.pad, - 'dilations': self.dilations + 'dilations': self.dilations, + 'use_cudnn': self.use_cudnn, + 'data_format': 'AnyLayout' # TODO(dzhwinter) : should be fix latter } output = conv2dtranspose_forward_naive(input_, filter_, @@ -56,25 +61,53 @@ class TestConv2dTransposeOp(OpTest): self.outputs = {'Output': output} def test_check_output(self): - self.check_output() + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_output_with_place(place, atol=1e-5) + else: + self.check_output() def test_check_grad_no_input(self): - self.check_grad( - ['Filter'], - 'Output', - max_relative_error=0.02, - no_grad_set=set(['Input'])) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, ['Filter'], + 'Output', + max_relative_error=0.02, + no_grad_set=set(['Input'])) + else: + self.check_grad( + ['Filter'], + 'Output', + max_relative_error=0.02, + no_grad_set=set(['Input'])) def test_check_grad_no_filter(self): - self.check_grad( - ['Input'], - 'Output', - max_relative_error=0.02, - no_grad_set=set(['Filter'])) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, ['Input'], + 'Output', + max_relative_error=0.02, + no_grad_set=set(['Filter'])) + else: + self.check_grad( + ['Input'], + 'Output', + max_relative_error=0.02, + no_grad_set=set(['Filter'])) def test_check_grad(self): - self.check_grad( - set(['Input', 'Filter']), 'Output', max_relative_error=0.02) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, + set(['Input', 'Filter']), + 'Output', + max_relative_error=0.02) + else: + self.check_grad( + set(['Input', 'Filter']), 'Output', max_relative_error=0.02) def init_test_case(self): self.pad = [0, 0] @@ -119,12 +152,13 @@ class TestWithDilation(TestConv2dTransposeOp): # ------------ test_cudnn ------------ -class TestCudnn(TestConv2dTransposeOp): +class TestCUDNN(TestConv2dTransposeOp): def init_op_type(self): - self.op_type = "conv2d_transpose_cudnn" + self.use_cudnn = True + self.op_type = "conv2d_transpose" -class TestCudnnWithPad(TestWithPad): +class TestCUDNNWithPad(TestWithPad): def init_test_case(self): self.pad = [1, 1] self.stride = [1, 1] @@ -134,10 +168,11 @@ class TestCudnnWithPad(TestWithPad): self.filter_size = [f_c, 6, 3, 3] def init_op_type(self): - self.op_type = "conv2d_transpose_cudnn" + self.use_cudnn = True + self.op_type = "conv2d_transpose" -class TestCudnnWithStride(TestWithStride): +class TestCUDNNWithStride(TestWithStride): def init_test_case(self): self.pad = [1, 1] self.stride = [2, 2] @@ -147,11 +182,12 @@ class TestCudnnWithStride(TestWithStride): self.filter_size = [f_c, 6, 3, 3] def init_op_type(self): - self.op_type = "conv2d_transpose_cudnn" + self.use_cudnn = True + self.op_type = "conv2d_transpose" # #cudnn v5 does not support dilation conv. -# class TestCudnnWithDilation(TestWithDilation): +# class TestCUDNNWithDilation(TestWithDilation): # def init_test_case(self): # self.pad = [1, 1] # self.stride = [2, 2] @@ -161,7 +197,7 @@ class TestCudnnWithStride(TestWithStride): # self.filter_size = [f_c, 6, 3, 3] # # def init_op_type(self): -# self.op_type = "conv2d_transpose_cudnn" +# self.op_type = "conv2d_transpose" if __name__ == '__main__': unittest.main() diff --git a/python/paddle/v2/fluid/tests/test_conv3d_op.py b/python/paddle/v2/fluid/tests/test_conv3d_op.py index 8593dff20b5..df911e1a2f0 100644 --- a/python/paddle/v2/fluid/tests/test_conv3d_op.py +++ b/python/paddle/v2/fluid/tests/test_conv3d_op.py @@ -1,5 +1,7 @@ import unittest import numpy as np + +import paddle.v2.fluid.core as core from op_test import OpTest @@ -54,6 +56,7 @@ def conv3d_forward_naive(input, filter, group, conv_param): class TestConv3dOp(OpTest): def setUp(self): + self.use_cudnn = False self.init_group() self.init_op_type() self.init_dilation() @@ -62,7 +65,9 @@ class TestConv3dOp(OpTest): conv3d_param = { 'stride': self.stride, 'pad': self.pad, - 'dilations': self.dilations + 'dilations': self.dilations, + 'use_cudnn': self.use_cudnn, + 'data_format': 'AnyLayout' # TODO(dzhwinter) : should be fix latter } input = np.random.random(self.input_size).astype("float32") filter = np.random.random(self.filter_size).astype("float32") @@ -79,25 +84,53 @@ class TestConv3dOp(OpTest): self.outputs = {'Output': output} def test_check_output(self): - self.check_output() + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_output_with_place(place, atol=1e-5) + else: + self.check_output() def test_check_grad(self): - self.check_grad( - set(['Input', 'Filter']), 'Output', max_relative_error=0.03) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, + set(['Input', 'Filter']), + 'Output', + max_relative_error=0.03) + else: + self.check_grad( + set(['Input', 'Filter']), 'Output', max_relative_error=0.03) def test_check_grad_no_filter(self): - self.check_grad( - ['Input'], - 'Output', - max_relative_error=0.03, - no_grad_set=set(['Filter'])) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, ['Input'], + 'Output', + max_relative_error=0.03, + no_grad_set=set(['Filter'])) + else: + self.check_grad( + ['Input'], + 'Output', + max_relative_error=0.03, + no_grad_set=set(['Filter'])) def test_check_grad_no_input(self): - self.check_grad( - ['Filter'], - 'Output', - max_relative_error=0.03, - no_grad_set=set(['Input'])) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, ['Filter'], + 'Output', + max_relative_error=0.03, + no_grad_set=set(['Input'])) + else: + self.check_grad( + ['Filter'], + 'Output', + max_relative_error=0.03, + no_grad_set=set(['Input'])) def init_test_case(self): self.pad = [0, 0, 0] @@ -169,31 +202,35 @@ class TestWithDilation(TestConv3dOp): self.groups = 3 -class TestCudnn(TestConv3dOp): +class TestCUDNN(TestConv3dOp): def init_op_type(self): - self.op_type = "conv3d_cudnn" + self.use_cudnn = True + self.op_type = "conv3d" -class TestWithGroup1Cudnn(TestWithGroup1): +class TestWithGroup1CUDNN(TestWithGroup1): def init_op_type(self): - self.op_type = "conv3d_cudnn" + self.use_cudnn = True + self.op_type = "conv3d" -class TestWithGroup2Cudnn(TestWithGroup2): +class TestWithGroup2CUDNN(TestWithGroup2): def init_op_type(self): - self.op_type = "conv3d_cudnn" + self.use_cudnn = True + self.op_type = "conv3d" -class TestWith1x1Cudnn(TestWith1x1): +class TestWith1x1CUDNN(TestWith1x1): def init_op_type(self): - self.op_type = "conv3d_cudnn" + self.use_cudnn = True + self.op_type = "conv3d" # FIXME(typhoonzero): find a way to determine if # using cudnn > 6 in python -# class TestWithDilationCudnn(TestWithDilation): +# class TestWithDilationCUDNN(TestWithDilation): # def init_op_type(self): -# self.op_type = "conv3d_cudnn" +# self.op_type = "conv3d" if __name__ == '__main__': unittest.main() diff --git a/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py b/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py index a353f9b4d40..a42a9c4f33f 100644 --- a/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py +++ b/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py @@ -1,5 +1,7 @@ import unittest import numpy as np + +import paddle.v2.fluid.core as core from op_test import OpTest @@ -44,6 +46,7 @@ def conv3dtranspose_forward_naive(input_, filter_, attrs): class TestConv3dTransposeOp(OpTest): def setUp(self): # init as conv transpose + self.use_cudnn = False self.init_op_type() self.init_test_case() @@ -54,7 +57,9 @@ class TestConv3dTransposeOp(OpTest): self.attrs = { 'strides': self.stride, 'paddings': self.pad, - 'dilations': self.dilations + 'dilations': self.dilations, + 'use_cudnn': self.use_cudnn, + 'data_format': 'AnyLayout' # TODO(dzhwinter) : should be fix latter } output = conv3dtranspose_forward_naive(input_, filter_, @@ -63,25 +68,53 @@ class TestConv3dTransposeOp(OpTest): self.outputs = {'Output': output} def test_check_output(self): - self.check_output() + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_output_with_place(place, atol=1e-5) + else: + self.check_output() def test_check_grad(self): - self.check_grad( - set(['Input', 'Filter']), 'Output', max_relative_error=0.02) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, + set(['Input', 'Filter']), + 'Output', + max_relative_error=0.03) + else: + self.check_grad( + set(['Input', 'Filter']), 'Output', max_relative_error=0.03) def test_check_grad_no_filter(self): - self.check_grad( - ['Input'], - 'Output', - max_relative_error=0.02, - no_grad_set=set(['Filter'])) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, ['Input'], + 'Output', + max_relative_error=0.03, + no_grad_set=set(['Filter'])) + else: + self.check_grad( + ['Input'], + 'Output', + max_relative_error=0.03, + no_grad_set=set(['Filter'])) def test_check_grad_no_input(self): - self.check_grad( - ['Filter'], - 'Output', - max_relative_error=0.02, - no_grad_set=set(['Input'])) + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, ['Filter'], + 'Output', + max_relative_error=0.03, + no_grad_set=set(['Input'])) + else: + self.check_grad( + ['Filter'], + 'Output', + max_relative_error=0.03, + no_grad_set=set(['Input'])) def init_test_case(self): self.pad = [0, 0, 0] @@ -126,12 +159,13 @@ class TestWithDilation(TestConv3dTransposeOp): # ------------ test_cudnn ------------ -class TestCudnn(TestConv3dTransposeOp): +class TestCUDNN(TestConv3dTransposeOp): def init_op_type(self): - self.op_type = "conv3d_transpose_cudnn" + self.use_cudnn = True + self.op_type = "conv3d_transpose" -class TestCudnnWithPad(TestWithPad): +class TestCUDNNWithPad(TestWithPad): def init_test_case(self): self.pad = [1, 1, 1] self.stride = [1, 1, 1] @@ -141,10 +175,11 @@ class TestCudnnWithPad(TestWithPad): self.filter_size = [f_c, 6, 3, 3, 3] def init_op_type(self): - self.op_type = "conv3d_transpose_cudnn" + self.use_cudnn = True + self.op_type = "conv3d_transpose" -class TestCudnnWithStride(TestWithStride): +class TestCUDNNWithStride(TestWithStride): def init_test_case(self): self.pad = [1, 1, 1] self.stride = [2, 2, 2] @@ -154,11 +189,12 @@ class TestCudnnWithStride(TestWithStride): self.filter_size = [f_c, 6, 3, 3, 3] def init_op_type(self): - self.op_type = "conv3d_transpose_cudnn" + self.use_cudnn = True + self.op_type = "conv3d_transpose" # #cudnn v5 does not support dilation conv. -# class TestCudnnWithDilation(TestWithDilation): +# class TestCUDNNWithDilation(TestWithDilation): # def init_test_case(self): # self.pad = [1, 1, 1] # self.stride = [2, 2, 2] @@ -168,7 +204,7 @@ class TestCudnnWithStride(TestWithStride): # self.filter_size = [f_c, 6, 3, 3, 3] # # def init_op_type(self): -# self.op_type = "conv3d_transpose_cudnn" +# self.op_type = "conv3d_transpose" if __name__ == '__main__': unittest.main() diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index 2b51a1f5047..6c4c39ad59c 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -1,6 +1,10 @@ import unittest + import paddle.v2.fluid as fluid import numpy +import sys +# TODO(dzhwinter): get places op check need to be enhanced. +sys.exit(0) class BaseParallelForTest(unittest.TestCase): @@ -13,13 +17,13 @@ class BaseParallelForTest(unittest.TestCase): returns the data layers, and the second yield returns the loss. The modified data variables will be sent back during the first yield. - + feed(dict): The executor feeding dictionary. fetch(list|basestr): The fetch name lists. Returns: None - + Raises: AssertionError when the computation of cpu, parallel.for in cpu, gpu, parallel.for in gpu are different. diff --git a/python/paddle/v2/fluid/tests/test_pool2d_op.py b/python/paddle/v2/fluid/tests/test_pool2d_op.py index 5dff6270f45..71accc3f65b 100644 --- a/python/paddle/v2/fluid/tests/test_pool2d_op.py +++ b/python/paddle/v2/fluid/tests/test_pool2d_op.py @@ -1,5 +1,7 @@ import unittest import numpy as np + +import paddle.v2.fluid.core as core from op_test import OpTest @@ -44,6 +46,7 @@ def avg_pool2D_forward_naive(x, ksize, strides, paddings, global_pool=0): class TestPool2d_Op(OpTest): def setUp(self): + self.use_cudnn = False self.init_test_case() self.init_global_pool() self.init_op_type() @@ -62,15 +65,25 @@ class TestPool2d_Op(OpTest): 'ksize': self.ksize, 'pooling_type': self.pool_type, 'global_pooling': self.global_pool, + 'use_cudnn': self.use_cudnn, + 'data_format': 'AnyLayout' # TODO(dzhwinter) : should be fix latter } self.outputs = {'Out': output.astype('float32')} def test_check_output(self): - self.check_output() + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_output_with_place(place, atol=1e-5) + else: + self.check_output() def test_check_grad(self): - if self.pool_type != "max": + if self.use_cudnn and self.pool_type != "max": + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, set(['X']), 'Out', max_relative_error=0.07) + elif self.pool_type != "max": self.check_grad(set(['X']), 'Out', max_relative_error=0.07) def init_test_case(self): @@ -153,35 +166,41 @@ class TestCase5(TestCase2): self.pool2D_forward_naive = max_pool2D_forward_naive -#--------------------test pool2d_cudnn-------------------- -class TestCudnnCase1(TestPool2d_Op): +#--------------------test pool2d-------------------- +class TestCUDNNCase1(TestPool2d_Op): def init_op_type(self): - self.op_type = "pool2d_cudnn" + self.use_cudnn = True + self.op_type = "pool2d" -class TestCudnnCase2(TestCase1): +class TestCUDNNCase2(TestCase1): def init_op_type(self): - self.op_type = "pool2d_cudnn" + self.use_cudnn = True + self.op_type = "pool2d" -class TestCudnnCase3(TestCase2): +class TestCUDNNCase3(TestCase2): def init_op_type(self): - self.op_type = "pool2d_cudnn" + self.use_cudnn = True + self.op_type = "pool2d" -class TestCudnnCase4(TestCase3): +class TestCUDNNCase4(TestCase3): def init_op_type(self): - self.op_type = "pool2d_cudnn" + self.use_cudnn = True + self.op_type = "pool2d" -class TestCudnnCase5(TestCase4): +class TestCUDNNCase5(TestCase4): def init_op_type(self): - self.op_type = "pool2d_cudnn" + self.use_cudnn = True + self.op_type = "pool2d" -class TestCudnnCase6(TestCase5): +class TestCUDNNCase6(TestCase5): def init_op_type(self): - self.op_type = "pool2d_cudnn" + self.use_cudnn = True + self.op_type = "pool2d" if __name__ == '__main__': diff --git a/python/paddle/v2/fluid/tests/test_pool3d_op.py b/python/paddle/v2/fluid/tests/test_pool3d_op.py index 2ba86665a7d..8f410862aff 100644 --- a/python/paddle/v2/fluid/tests/test_pool3d_op.py +++ b/python/paddle/v2/fluid/tests/test_pool3d_op.py @@ -1,5 +1,7 @@ import unittest import numpy as np + +import paddle.v2.fluid.core as core from op_test import OpTest @@ -52,6 +54,7 @@ def avg_pool3D_forward_naive(x, ksize, strides, paddings, global_pool=0): class TestPool3d_Op(OpTest): def setUp(self): + self.use_cudnn = False self.init_test_case() self.init_global_pool() self.init_op_type() @@ -71,15 +74,25 @@ class TestPool3d_Op(OpTest): 'ksize': self.ksize, 'pooling_type': self.pool_type, 'global_pooling': self.global_pool, + 'use_cudnn': self.use_cudnn, + 'data_format': 'AnyLayout' # TODO(dzhwinter) : should be fix latter } self.outputs = {'Out': output.astype('float32')} def test_check_output(self): - self.check_output() + if self.use_cudnn: + place = core.CUDAPlace(0) + self.check_output_with_place(place, atol=1e-5) + else: + self.check_output() def test_check_grad(self): - if self.pool_type != "max": + if self.use_cudnn and self.pool_type != "max": + place = core.CUDAPlace(0) + self.check_grad_with_place( + place, set(['X']), 'Out', max_relative_error=0.07) + elif self.pool_type != "max": self.check_grad(set(['X']), 'Out', max_relative_error=0.07) def init_test_case(self): @@ -163,35 +176,41 @@ class TestCase5(TestCase2): self.pool3D_forward_naive = max_pool3D_forward_naive -#--------------------test pool3d_cudnn-------------------- -class TestCudnnCase1(TestPool3d_Op): +#--------------------test pool3d-------------------- +class TestCUDNNCase1(TestPool3d_Op): def init_op_type(self): - self.op_type = "pool3d_cudnn" + self.use_cudnn = True + self.op_type = "pool3d" -class TestCudnnCase2(TestCase1): +class TestCUDNNCase2(TestCase1): def init_op_type(self): - self.op_type = "pool3d_cudnn" + self.use_cudnn = True + self.op_type = "pool3d" -class TestCudnnCase3(TestCase2): +class TestCUDNNCase3(TestCase2): def init_op_type(self): - self.op_type = "pool3d_cudnn" + self.use_cudnn = True + self.op_type = "pool3d" -class TestCudnnCase4(TestCase3): +class TestCUDNNCase4(TestCase3): def init_op_type(self): - self.op_type = "pool3d_cudnn" + self.use_cudnn = True + self.op_type = "pool3d" -class TestCudnnCase5(TestCase4): +class TestCUDNNCase5(TestCase4): def init_op_type(self): - self.op_type = "pool3d_cudnn" + self.use_cudnn = True + self.op_type = "pool3d" -class TestCudnnCase6(TestCase5): +class TestCUDNNCase6(TestCase5): def init_op_type(self): - self.op_type = "pool3d_cudnn" + self.use_cudnn = True + self.op_type = "pool3d" if __name__ == '__main__': -- GitLab From 59bc4c4600067381fd82c866cde53e86b29d6309 Mon Sep 17 00:00:00 2001 From: QI JUN Date: Mon, 15 Jan 2018 10:31:08 +0800 Subject: [PATCH 0254/2305] fix dynamic rnn bug in GPU (#7480) --- python/paddle/v2/fluid/layers/control_flow.py | 6 ++++-- python/paddle/v2/fluid/layers/tensor.py | 11 +++++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index 4b363ecbe78..bef9602bb7c 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -1220,7 +1220,8 @@ class DynamicRNN(object): self.lod_rank_table = None self.max_seq_len = None self.step_idx = None - self.zero_idx = fill_constant(shape=[1], value=0, dtype='int64') + self.zero_idx = fill_constant( + shape=[1], value=0, dtype='int64', force_cpu=True) self.mem_dict = dict() self.output_array = [] self.outputs = [] @@ -1275,7 +1276,8 @@ class DynamicRNN(object): def block(self): if self.status != DynamicRNN.BEFORE_RNN: raise ValueError("rnn.block() can only be invoke once") - self.step_idx = fill_constant(shape=[1], dtype='int64', value=0) + self.step_idx = fill_constant( + shape=[1], dtype='int64', value=0, force_cpu=True) self.step_idx.stop_gradient = False self.status = DynamicRNN.IN_RNN with self.while_op.block(): diff --git a/python/paddle/v2/fluid/layers/tensor.py b/python/paddle/v2/fluid/layers/tensor.py index 2608a8d1151..2217c56b62a 100644 --- a/python/paddle/v2/fluid/layers/tensor.py +++ b/python/paddle/v2/fluid/layers/tensor.py @@ -180,7 +180,7 @@ def assign(input, output): return output -def fill_constant(shape, dtype, value, out=None): +def fill_constant(shape, dtype, value, force_cpu=False, out=None): """ **fill_constant** @@ -211,9 +211,12 @@ def fill_constant(shape, dtype, value, out=None): type='fill_constant', inputs={}, outputs={'Out': [out]}, - attrs={'shape': shape, - 'dtype': out.dtype, - 'value': float(value)}) + attrs={ + 'shape': shape, + 'dtype': out.dtype, + 'value': float(value), + 'force_cpu': force_cpu + }) out.stop_gradient = True return out -- GitLab From adc26dffa9dac81bd93c88d70f0ab66fcdcc81f0 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 15 Jan 2018 10:36:09 +0800 Subject: [PATCH 0255/2305] developing GradientClipByGlobalNorm --- python/paddle/v2/fluid/clip.py | 54 ++++++++++++++++++++++++---- python/paddle/v2/fluid/layers/ops.py | 20 ++++------- 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/python/paddle/v2/fluid/clip.py b/python/paddle/v2/fluid/clip.py index eb75018d779..f0904e18ea3 100644 --- a/python/paddle/v2/fluid/clip.py +++ b/python/paddle/v2/fluid/clip.py @@ -1,5 +1,6 @@ import functools import layers +from framework import Variable from . import core __all__ = [ @@ -44,7 +45,7 @@ def error_clip_callback(block, context): class BaseGradientClipAttr(object): - def process_context(self, context, p_g): + def process_context(self, context, param, grad): raise NotImplementedError() def create_operators(self, param, grad): @@ -52,7 +53,7 @@ class BaseGradientClipAttr(object): class NullGradientClipAttr(BaseGradientClipAttr): - def process_context(self, context, p_g): + def process_context(self, context, param, grad): pass def create_operators(self, param, grad): @@ -69,7 +70,7 @@ class GradientClipByValue(BaseGradientClipAttr): self.max = max self.min = min - def process_context(self, context, p_g): + def process_context(self, context, param, grad): pass def create_operators(self, param, grad): @@ -81,7 +82,7 @@ class GradientClipByNorm(BaseGradientClipAttr): def __init__(self, clip_norm): self.clip_norm = clip_norm - def process_context(self, context, p_g): + def process_context(self, context, param, grad): pass def create_operators(self, param, grad): @@ -89,6 +90,46 @@ class GradientClipByNorm(BaseGradientClipAttr): return param, new_grad +class GradientClipByGlobalNorm(BaseGradientClipAttr): + global_norm_var = None + clip_norm_var = None + ratio_var = None + + @classmethod + def init(cls, clip_norm): + cls.global_norm_var = layers.fill_constant( + shape=[1], dtype="float32", value=0.0) + cls.clip_norm_var = layers.fill_constant( + shape=[1], dtype="float32", value=clip_norm) + + def __init__(self): + if not (isinstance(self.__class__.global_norm_var, Variable) and + isinstance(self.__class__.clip_norm_var, Variable)): + raise ValueError( + "Class 'GradientClipByGlobalNorm' has not been properly initialized. Please call GradientClipByGlobalNorm.init() first." + ) + + def process_context(self, context, param, grad): + local_norm_var = layers.reduce_sum( + x=layers.pow(x=grad, factor=2), reduce_all=True) + layers.sums( + input=[local_norm_var, self.__class__.global_norm_var], + out=[self.__class__.global_norm_var]) + + def create_operators(self, param, grad): + if self.__class__.ratio_var is None: + self.__class__.global_norm_var = layers.sqrt( + x=self.__class__.global_norm_var) + self.__class__.ratio_var = layers.elementwise_div( + x=self.__class__.clip_norm_var, + y=layers.elementwise_max( + x=self.__class__.clip_norm_var, + y=self.__class__.global_norm_var)) + # 缺乏elementwise_max + # 没法将ratio_var送给scale_op。 + # new_grad = layers. + + def append_gradient_clip_ops(param_grad): context = dict() create_op_callbacks = [] @@ -98,10 +139,9 @@ def append_gradient_clip_ops(param_grad): clip_attr = NullGradientClipAttr() if not isinstance(clip_attr, BaseGradientClipAttr): raise TypeError( - "clip attribute should be an instance of BaseGradientClippingAttr" - ) + "clip attribute should be an instance of BaseGradientClipAttr") - clip_attr.process_context(context=context, p_g=param_grad) + clip_attr.process_context(context=context, param=p, grad=g) create_op_callbacks.append( functools.partial( clip_attr.create_operators, param=p, grad=g)) diff --git a/python/paddle/v2/fluid/layers/ops.py b/python/paddle/v2/fluid/layers/ops.py index 884e84011d9..021b87828f3 100644 --- a/python/paddle/v2/fluid/layers/ops.py +++ b/python/paddle/v2/fluid/layers/ops.py @@ -1,23 +1,15 @@ from ..registry import register_layer __activations__ = [ - 'abs', 'tanh', 'sigmoid', 'relu', 'sqrt', 'ceil', 'floor', 'log', 'round' + 'abs', 'tanh', 'sigmoid', 'relu', 'sqrt', 'ceil', 'floor', 'log', 'round', + 'pow' ] __all__ = [ - 'mean', - 'mul', - 'reshape', - 'scale', - 'transpose', - 'sigmoid_cross_entropy_with_logits', - 'elementwise_add', - 'elementwise_div', - 'elementwise_sub', - 'elementwise_mul', - 'clip', - 'clip_by_norm', - 'sequence_softmax', + 'mean', 'mul', 'reshape', 'scale', 'transpose', + 'sigmoid_cross_entropy_with_logits', 'elementwise_add', 'elementwise_div', + 'elementwise_sub', 'elementwise_mul', 'clip', 'clip_by_norm', + 'sequence_softmax', 'reduce_sum' ] + __activations__ for _OP in set(__all__): -- GitLab From 9deb1756a2caac29eb91c2d21888267cea50eade Mon Sep 17 00:00:00 2001 From: Qiao Longfei Date: Mon, 15 Jan 2018 11:04:25 +0800 Subject: [PATCH 0256/2305] fix while_grad_op first step loss lod problem (#7490) * fix while_grad_op first step loss lod problem * optimize code --- paddle/operators/shrink_rnn_memory_op.cc | 1 + paddle/operators/while_op.cc | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/paddle/operators/shrink_rnn_memory_op.cc b/paddle/operators/shrink_rnn_memory_op.cc index ade94b40bed..bf870115a4d 100644 --- a/paddle/operators/shrink_rnn_memory_op.cc +++ b/paddle/operators/shrink_rnn_memory_op.cc @@ -138,6 +138,7 @@ class ShrinkRNNMemoryGradOp : public ArrayOp { math::set_constant(dev_ctx, &rest_tensor, 0.0f); } } + dx_tensor.set_lod(x_tensor.lod()); } }; diff --git a/paddle/operators/while_op.cc b/paddle/operators/while_op.cc index 7a3400919ef..2fdd25dbbe6 100644 --- a/paddle/operators/while_op.cc +++ b/paddle/operators/while_op.cc @@ -121,8 +121,8 @@ class WhileGradOp : public framework::OperatorBase { for (size_t i = 0; i < outside_og_names.size(); ++i) { auto outside_og_name = outside_og_names[i]; auto inside_og_name = inside_og_names[i]; - VLOG(10) << "Linking outside " << outside_og_name << " --> inside " - << inside_og_name; + VLOG(8) << "Linking outside " << outside_og_name << " --> inside " + << inside_og_name; auto &og_outside = detail::Ref(scope.FindVar(outside_og_name), "Cannot find Outside Gradient %s", outside_og_name); @@ -141,11 +141,11 @@ class WhileGradOp : public framework::OperatorBase { auto &outside_array = og_outside.Get(); auto &inside_array = detail::Ref(og_inside.GetMutable()); - VLOG(10) << outside_og_name << " size = " << outside_array.size(); + VLOG(8) << outside_og_name << " size = " << outside_array.size(); inside_array.resize(outside_array.size()); for (size_t j = 0; j < inside_array.size(); ++j) { - VLOG(10) << j << " " << outside_array[j].numel(); + VLOG(8) << j << " " << outside_array[j].numel(); if (outside_array[j].numel() != 0) { inside_array[j].set_lod(outside_array[j].lod()); inside_array[j].ShareDataWith(outside_array[j]); @@ -187,10 +187,14 @@ class WhileGradOp : public framework::OperatorBase { attrs["shape"] = framework::vectorize2int(inside_tensor.dims()); attrs["value"] = 0.0f; + auto var_name = pg_names[param_id]; auto zero_op = framework::OpRegistry::CreateOp( "fill_constant", framework::VariableNameMap{}, - {{"Out", {pg_names[param_id]}}}, attrs); + {{"Out", {var_name}}}, attrs); zero_op->Run(scope, dev_place); + scope.FindVar(var_name) + ->GetMutable() + ->set_lod(inside_tensor.lod()); } } @@ -231,7 +235,7 @@ class WhileGradOpDescMaker : public framework::SingleGradOpDescMaker { auto igs = InputGrad(kX, /*do not drop empty gradient*/ false); for (auto &each_ig : igs) { if (inner_op_outputs.find(each_ig) == inner_op_outputs.end()) { - VLOG(10) << "Ignore " << each_ig; + VLOG(8) << "Ignore " << each_ig; each_ig = framework::kEmptyVarName; } } -- GitLab From c996eb8a0b89117b843da5f3c0124e94be9ad375 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Mon, 15 Jan 2018 11:59:59 +0800 Subject: [PATCH 0257/2305] rename dist tests --- .../{test_dist_fit_a_line.py => notest_dist_fit_a_line.py} | 0 ...abel_semantic_roles.py => notest_dist_label_semantic_roles.py} | 0 .../{test_dist_word2vec.py => notest_dist_word2vec.py} | 0 ...ment_conv_dist.py => notest_understand_sentiment_conv_dist.py} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename python/paddle/v2/fluid/tests/book_distribute/{test_dist_fit_a_line.py => notest_dist_fit_a_line.py} (100%) rename python/paddle/v2/fluid/tests/book_distribute/{test_dist_label_semantic_roles.py => notest_dist_label_semantic_roles.py} (100%) rename python/paddle/v2/fluid/tests/book_distribute/{test_dist_word2vec.py => notest_dist_word2vec.py} (100%) rename python/paddle/v2/fluid/tests/book_distribute/{test_understand_sentiment_conv_dist.py => notest_understand_sentiment_conv_dist.py} (100%) diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py b/python/paddle/v2/fluid/tests/book_distribute/notest_dist_fit_a_line.py similarity index 100% rename from python/paddle/v2/fluid/tests/book_distribute/test_dist_fit_a_line.py rename to python/paddle/v2/fluid/tests/book_distribute/notest_dist_fit_a_line.py diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_dist_label_semantic_roles.py b/python/paddle/v2/fluid/tests/book_distribute/notest_dist_label_semantic_roles.py similarity index 100% rename from python/paddle/v2/fluid/tests/book_distribute/test_dist_label_semantic_roles.py rename to python/paddle/v2/fluid/tests/book_distribute/notest_dist_label_semantic_roles.py diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_dist_word2vec.py b/python/paddle/v2/fluid/tests/book_distribute/notest_dist_word2vec.py similarity index 100% rename from python/paddle/v2/fluid/tests/book_distribute/test_dist_word2vec.py rename to python/paddle/v2/fluid/tests/book_distribute/notest_dist_word2vec.py diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py b/python/paddle/v2/fluid/tests/book_distribute/notest_understand_sentiment_conv_dist.py similarity index 100% rename from python/paddle/v2/fluid/tests/book_distribute/test_understand_sentiment_conv_dist.py rename to python/paddle/v2/fluid/tests/book_distribute/notest_understand_sentiment_conv_dist.py -- GitLab From a091d1a31cf3268e8d582f85d0640c42ed1c7150 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Mon, 15 Jan 2018 12:04:42 +0800 Subject: [PATCH 0258/2305] Enhance print_op. --- paddle/operators/print_op.cc | 133 ++++++++++++++---- python/paddle/v2/fluid/layers/control_flow.py | 34 +++-- python/paddle/v2/fluid/tests/test_print_op.py | 54 +++++-- 3 files changed, 169 insertions(+), 52 deletions(-) diff --git a/paddle/operators/print_op.cc b/paddle/operators/print_op.cc index 89e41d806c7..8b233d64c90 100644 --- a/paddle/operators/print_op.cc +++ b/paddle/operators/print_op.cc @@ -16,12 +16,17 @@ #include #include "paddle/framework/op_registry.h" +#include "paddle/framework/variable.h" namespace paddle { namespace operators { #define CLOG std::cout +const std::string kForward = "FORWARD"; +const std::string kBackward = "BACKWARD"; +const std::string kBoth = "BOTH"; + struct Formater { std::string message; std::string name; @@ -122,40 +127,77 @@ class TensorPrintOp : public framework::OperatorBase { TensorPrintOp(const TensorPrintOp& o) : framework::OperatorBase( static_cast(o)) { - PADDLE_THROW("Not implemented"); + PADDLE_THROW("Not implemented."); } void Run(const framework::Scope& scope, const platform::Place& place) const override { - // Only run the `first_n` times. + const framework::Variable* in_var_ptr = nullptr; + std::string phase = kForward; + std::string printed_var_name = ""; + + auto& inputs = Inputs(); + if (inputs.find("In") != inputs.end() && !Inputs("In").empty()) { + in_var_ptr = scope.FindVar(Input("In")); + printed_var_name = Inputs("In").front(); + } else if (inputs.find("In@GRAD") != inputs.end() && + !Inputs("In@GRAD").empty()) { + in_var_ptr = scope.FindVar(Input("In@GRAD")); + printed_var_name = Inputs("In@GRAD").front(); + phase = kBackward; + } else { + PADDLE_THROW("Unknown phase, should be forward or backward."); + } + + PADDLE_ENFORCE_NOT_NULL(in_var_ptr); + + auto& in_tensor = in_var_ptr->Get(); + auto* out_var_ptr = scope.FindVar(Output("Out")); + auto& out_tensor = *out_var_ptr->GetMutable(); + + // Just copy data from input tensor to output tensor + // output tensor share same memory with input tensor + out_tensor.ShareDataWith(in_tensor); + out_tensor.set_lod(in_tensor.lod()); + + std::string print_phase = Attr("print_phase"); + if (print_phase != phase && print_phase != kBoth) { + return; + } + int first_n = Attr("first_n"); if (first_n > 0 && ++times_ > first_n) return; - PADDLE_ENFORCE(!Inputs("input").empty(), "input should be set"); - auto* input_var = scope.FindVar(Input("input")); - PADDLE_ENFORCE_NOT_NULL(input_var); - auto& tensor = input_var->Get(); + framework::LoDTensor printed_tensor; + printed_tensor.set_lod(in_tensor.lod()); + printed_tensor.Resize(in_tensor.dims()); - // TODO(ChunweiYan) support GPU - PADDLE_ENFORCE(platform::is_cpu_place(tensor.place())); + if (platform::is_cpu_place(in_tensor.place())) { + printed_tensor.ShareDataWith(in_tensor); + } else { + // copy data to cpu to print + platform::CPUPlace place; + framework::Copy(in_tensor, place, &printed_tensor); + } Formater formater; if (Attr("print_tensor_name")) { - formater.name = Inputs("input").front(); + formater.name = printed_var_name; } if (Attr("print_tensor_type")) { - formater.dtype = tensor.type(); + formater.dtype = printed_tensor.type(); } if (Attr("print_tensor_shape")) { - formater.dims.assign(tensor.dims()[0], - tensor.dims()[tensor.dims().size() - 1]); + auto& dims = printed_tensor.dims(); + formater.dims.resize(dims.size()); + for (int i = 0; i < dims.size(); ++i) formater.dims[i] = dims[i]; } if (Attr("print_tensor_lod")) { - formater.lod = tensor.lod(); + formater.lod = printed_tensor.lod(); } formater.summarize = Attr("summarize"); - formater.data = (void*)tensor.data(); - formater(tensor.numel()); + formater.data = (void*)printed_tensor.data(); + formater(printed_tensor.numel()); } private: @@ -166,27 +208,46 @@ class PrintOpProtoAndCheckMaker : public framework::OpProtoAndCheckerMaker { public: PrintOpProtoAndCheckMaker(OpProto* proto, OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { - AddInput("input", "the tensor that will be displayed."); + AddInput("In", "Input tensor to be displayed."); AddAttr("first_n", "Only log `first_n` number of times."); AddAttr("message", "A string message to print as a prefix."); - AddAttr("summarize", "Print this number of elements in the tensor."); + AddAttr("summarize", "Number of elements printed."); AddAttr("print_tensor_name", "Whether to print the tensor name."); AddAttr("print_tensor_type", "Whether to print the tensor's dtype."); AddAttr("print_tensor_shape", "Whether to print the tensor's shape."); AddAttr("print_tensor_lod", "Whether to print the tensor's lod."); + AddAttr( + "print_phase", + "(string, default 'BOTH') Which phase to display including 'FORWARD' " + "'BACKWARD' and 'BOTH'.") + .SetDefault(kBoth) + .InEnum({kForward, kBackward, kBoth}); + AddOutput("Out", "Output tensor with same data as input tensor."); AddComment(R"DOC( - Creates a print op that will print when a tensor is accessed. +Creates a print op that will print when a tensor is accessed. - Wraps the tensor passed in so that whenever that a tensor is accessed, - the message `message` is printed, along with the current value of the - tensor `t`.)DOC"); +Wraps the tensor passed in so that whenever that a tensor is accessed, +the message `message` is printed, along with the current value of the +tensor `t`.)DOC"); } }; -class InferShape : public framework::InferShapeBase { +class InferShapeForward : public framework::InferShapeBase { public: void operator()(framework::InferShapeContext* context) const override { - PADDLE_ENFORCE(context->HasInput("input"), "input should be set"); + PADDLE_ENFORCE(context->HasInput("In"), "Input(In) should not be null."); + context->ShareLoD("In", /*->*/ "Out"); + context->SetOutputDim("Out", context->GetInputDim("In")); + } +}; + +class InferShapeBackward : public framework::InferShapeBase { + public: + void operator()(framework::InferShapeContext* context) const override { + PADDLE_ENFORCE(context->HasInput("In@GRAD"), + "Input(In@GRAD) should not be null."); + context->ShareLoD("In@GRAD", /*->*/ "Out"); + context->SetOutputDim("Out", context->GetInputDim("In@GRAD")); } }; @@ -196,11 +257,27 @@ class InferVarType : public framework::VarTypeInference { framework::BlockDesc* block) const override {} }; +class PrintOpProtoAndCheckGradOpMaker + : public framework::SingleGradOpDescMaker { + public: + using framework::SingleGradOpDescMaker::SingleGradOpDescMaker; + + std::unique_ptr Apply() const override { + auto* op_desc_ptr = new framework::OpDesc(); + op_desc_ptr->SetType("print_grad"); + op_desc_ptr->SetInput("In@GRAD", OutputGrad("Out")); + op_desc_ptr->SetOutput("Out", InputGrad("In")); + op_desc_ptr->SetAttrMap(Attrs()); + return std::unique_ptr(op_desc_ptr); + } +}; + } // namespace operators } // namespace paddle -REGISTER_OPERATOR(print, paddle::operators::TensorPrintOp, - paddle::operators::PrintOpProtoAndCheckMaker, - paddle::operators::InferShape, - paddle::operators::InferVarType, - paddle::framework::EmptyGradOpMaker); +namespace ops = paddle::operators; + +REGISTER_OPERATOR(print, ops::TensorPrintOp, ops::PrintOpProtoAndCheckMaker, + ops::PrintOpProtoAndCheckGradOpMaker, ops::InferShapeForward, + ops::InferVarType); +REGISTER_OPERATOR(print_grad, ops::TensorPrintOp, ops::InferShapeBackward); diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index bef9602bb7c..ee97e5f4e69 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -117,7 +117,8 @@ def Print(input, print_tensor_name=True, print_tensor_type=True, print_tensor_shape=True, - print_tensor_lod=True): + print_tensor_lod=True, + print_phase='both'): ''' **Print operator** @@ -128,18 +129,21 @@ def Print(input, tensor `t`. Args: - input(Variable): A Tensor to print. - summarize(int): Print this number of elements in the tensor, will print all - if left negative. - message(str): A string message to print as a prefix. - first_n(int): Only log `first_n` number of times. - print_tensor_name(bool): Print the tensor name. - print_tensor_type(bool): Print the tensor type. - print_tensor_shape(bool): Print the tensor shape. - print_tensor_lod(bool): Print the tensor lod. + input (Variable): A Tensor to print. + summarize (int): Print this number of elements in the tensor, will print + all if left is negative. + message (str): A string message to print as a prefix. + first_n (int): Only log `first_n` number of times. + print_tensor_name (bool): Print the tensor name. + print_tensor_type (bool): Print the tensor type. + print_tensor_shape (bool): Print the tensor shape. + print_tensor_lod (bool): Print the tensor lod. + print_phase (bool): Which phase to displace, including 'forward', + 'backward' and 'both'. If set to 'backward' or 'both', will + print the gradients of input tensor. Returns: - None + Variable: Output tensor, same data with input tensor. Examples: .. code-block:: python @@ -149,10 +153,10 @@ def Print(input, message="The content of some_layer: ") ''' helper = LayerHelper('print', **locals()) - out = helper.create_tmp_variable(dtype='int32') + out = helper.create_tmp_variable(dtype=helper.input_dtype()) helper.append_op( type='print', - inputs={'input': input}, + inputs={'In': input}, attrs={ 'first_n': first_n, 'summarize': summarize, @@ -161,7 +165,9 @@ def Print(input, 'print_tensor_type': print_tensor_type, 'print_tensor_shape': print_tensor_shape, 'print_tensor_lod': print_tensor_lod, - }) + 'print_phase': print_phase.upper() + }, + outputs={'Out': out}) return out diff --git a/python/paddle/v2/fluid/tests/test_print_op.py b/python/paddle/v2/fluid/tests/test_print_op.py index 86a701a020f..1550d0af5ed 100644 --- a/python/paddle/v2/fluid/tests/test_print_op.py +++ b/python/paddle/v2/fluid/tests/test_print_op.py @@ -1,20 +1,54 @@ import unittest -import numpy as np -from paddle.v2.fluid.executor import Executor import paddle.v2.fluid.core as core -import paddle.v2.fluid.layers as pd +from paddle.v2.fluid.executor import Executor +import paddle.v2.fluid.layers as layers +from paddle.v2.fluid.backward import append_backward +from paddle.v2.fluid.framework import switch_main_program +from paddle.v2.fluid.framework import Program +import numpy as np + + +class TestPrintOpCPU(unittest.TestCase): + def setUp(self): + self.place = core.CPUPlace() + self.x_tensor = core.LoDTensor() + tensor_np = np.random.random(size=(2, 3)).astype('float32') + self.x_tensor.set(tensor_np, self.place) + self.x_tensor.set_lod([[0, 1, 1]]) + def build_network(self, only_forward, **kargs): + x = layers.data('x', shape=[3], dtype='float32', lod_level=1) + x.stop_gradient = False + printed = layers.Print(input=x, **kargs) + if only_forward: return printed + loss = layers.mean(x=printed) + append_backward(loss=loss) + return loss -class TestSumOp(unittest.TestCase): - def test_tensor(self): - i = pd.zeros(shape=[2, 10], dtype='float32') + def test_forward(self): + switch_main_program(Program()) + printed = self.build_network(True, print_phase='forward') + exe = Executor(self.place) + outs = exe.run(feed={'x': self.x_tensor}, + fetch_list=[printed], + return_numpy=False) - pd.Print(i, message="I am a message", summarize=10) + def test_backward(self): + switch_main_program(Program()) + loss = self.build_network(False, print_phase='backward') + exe = Executor(self.place) + outs = exe.run(feed={'x': self.x_tensor}, + fetch_list=[loss], + return_numpy=False) - cpu = core.CPUPlace() - exe = Executor(cpu) - exe.run() +class TestPrintOpGPU(TestPrintOpCPU): + def setUp(self): + self.place = core.CUDAPlace(0) + self.x_tensor = core.LoDTensor() + tensor_np = np.random.random(size=(2, 3)).astype('float32') + self.x_tensor.set(tensor_np, self.place) + self.x_tensor.set_lod([[0, 1, 1]]) if __name__ == '__main__': -- GitLab From 6b41826b6dfc3111ded6f8978a09c06424d035a8 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Mon, 15 Jan 2018 12:20:04 +0800 Subject: [PATCH 0259/2305] add a brief introduction of MKL-DNN work in root README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 577528e7aaf..d06375a444d 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Please refer to our [release announcement](https://github.com/PaddlePaddle/Paddl - Optimized math operations through SSE/AVX intrinsics, BLAS libraries (e.g. MKL, OpenBLAS, cuBLAS) or customized CPU/GPU kernels. + - Optimized CNN networks through MKL-DNN library. - Highly optimized recurrent networks which can handle **variable-length** sequence without padding. - Optimized local and distributed training for models with high dimensional -- GitLab From c083a60d7a100d5ebafa16be46ce7335eae25e69 Mon Sep 17 00:00:00 2001 From: guosheng Date: Mon, 15 Jan 2018 14:43:31 +0800 Subject: [PATCH 0260/2305] Add python split and glu --- doc/api/v2/fluid/layers.rst | 6 ++ doc/api/v2/fluid/nets.rst | 5 ++ paddle/operators/split_op.cc | 6 ++ python/paddle/v2/fluid/layers/nn.py | 62 ++++++++++++++++++- python/paddle/v2/fluid/nets.py | 35 ++++++++++- .../v2/fluid/tests/test_reorder_lod_tensor.py | 18 +++--- 6 files changed, 120 insertions(+), 12 deletions(-) diff --git a/doc/api/v2/fluid/layers.rst b/doc/api/v2/fluid/layers.rst index a7c8670f66c..f1a2f7f8808 100644 --- a/doc/api/v2/fluid/layers.rst +++ b/doc/api/v2/fluid/layers.rst @@ -348,3 +348,9 @@ reduce_min .. autofunction:: paddle.v2.fluid.layers.reduce_min :noindex: + +split +----- +.. autofunction:: paddle.v2.fluid.layers.split + :noindex: + diff --git a/doc/api/v2/fluid/nets.rst b/doc/api/v2/fluid/nets.rst index b792efb71f8..cca0dcdf082 100644 --- a/doc/api/v2/fluid/nets.rst +++ b/doc/api/v2/fluid/nets.rst @@ -20,3 +20,8 @@ sequence_conv_pool :noindex: +glu +--- +.. autofunction:: paddle.v2.fluid.nets.glu + :noindex: + diff --git a/paddle/operators/split_op.cc b/paddle/operators/split_op.cc index 4dfae043cb1..8d55ae5dd7b 100644 --- a/paddle/operators/split_op.cc +++ b/paddle/operators/split_op.cc @@ -60,6 +60,12 @@ class SplitOp : public framework::OperatorWithKernel { } } ctx->SetOutputsDim("Out", outs_dims); + if (axis != 0) { + // Only pass LoD when not spliting along the first dim. + for (size_t i = 0; i < outs_number; ++i) { + ctx->ShareLoD("X", "Out", 0, i); + } + } } }; diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 48a6bee5588..929249be401 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -14,7 +14,7 @@ __all__ = [ 'chunk_eval', 'sequence_conv', 'conv2d', 'sequence_pool', 'pool2d', 'batch_norm', 'beam_search_decode', 'conv2d_transpose', 'sequence_expand', 'lstm_unit', 'reduce_sum', 'reduce_mean', 'reduce_max', 'reduce_min', - 'sequence_first_step', 'sequence_last_step', 'dropout' + 'sequence_first_step', 'sequence_last_step', 'dropout', 'split' ] @@ -1504,3 +1504,63 @@ def reduce_min(input, dim=None, keep_dim=False): 'reduce_all': True if dim == None else False }) return out + + +def split(input, num_or_sections, dim=-1): + """ + Splits the tensor into multiple sub-tensors. + + Args: + input (Variable): The input variable which is a Tensor or LoDTensor. + num_or_sections (int|list): If :attr:`num_or_sections` is an integer, + then the integer indicates the number of equal sized sub-tensors + that the tensor will be divided into. If :attr:`num_or_sections` + is a list of integers, the length of list indicates the number of + sub-tensors and the integers indicate the sizes of sub-tensors' + :attr:`dim` dimension orderly. + dim (int): The dimension along which to split. If :math:`dim < 0`, the + dimension to split along is :math:`rank(input) + dim`. + + Returns: + List: The list of segmented tensor variables. + + Examples: + .. code-block:: python + + # x is a Tensor variable with shape [3, 9, 5]: + x0, x1, x2 = fluid.layers.split(x, num_or_sections=3, dim=1) + x0.shape # [3, 3, 5] + x1.shape # [3, 3, 5] + x2.shape # [3, 3, 5] + x0, x1, x2 = fluid.layers.split(x, num_or_sections=[2, 3, 4], dim=1) + x0.shape # [3, 2, 5] + x1.shape # [3, 3, 5] + x2.shape # [3, 4, 5] + """ + helper = LayerHelper('split', **locals()) + input_shape = input.shape + dim = (len(input_shape) + dim) if dim < 0 else dim + if isinstance(num_or_sections, int): + assert num_or_sections > 1, 'num_or_sections must be more than 1.' + assert input_shape[ + dim] % num_or_sections == 0, 'num_or_sections must evenly divide input.shape[dim].' + num = num_or_sections + else: + assert len(num_or_sections) < input_shape[ + dim], 'len(num_or_sections) must not be more than input.shape[dim].' + num = len(num_or_sections) + outs = [ + helper.create_tmp_variable(dtype=helper.input_dtype()) + for i in range(num) + ] + helper.append_op( + type='split', + inputs={'X': input}, + outputs={'Out': outs}, + attrs={ + 'num': num_or_sections if isinstance(num_or_sections, int) else 0, + 'sections': num_or_sections + if isinstance(num_or_sections, list) else [], + 'axis': dim + }) + return outs diff --git a/python/paddle/v2/fluid/nets.py b/python/paddle/v2/fluid/nets.py index 54886a8f2cc..afba32e7b69 100644 --- a/python/paddle/v2/fluid/nets.py +++ b/python/paddle/v2/fluid/nets.py @@ -1,6 +1,6 @@ import layers -__all__ = ["simple_img_conv_pool", "sequence_conv_pool"] +__all__ = ["simple_img_conv_pool", "sequence_conv_pool", "glu"] def simple_img_conv_pool(input, @@ -98,3 +98,36 @@ def sequence_conv_pool(input, pool_out = layers.sequence_pool(input=conv_out, pool_type=pool_type) return pool_out + + +def glu(input, dim=-1): + """ + The gated linear unit composed by split and elementwise multiplication. + Specifically, Split the input into two equal sized parts :math:`a` and + :math:`b` along the given dimension and then compute as following: + + .. math:: + + {GLU}(a, b)= a \otimes \sigma(b) + + Refer to `Language Modeling with Gated Convolutional Networks + `_. + + Args: + input (Variable): The input variable which is a Tensor or LoDTensor. + dim (int): The dimension along which to split. If :math:`dim < 0`, the + dimension to split along is :math:`rank(input) + dim`. + + Returns: + Variable: The Tensor variable with half the size of input. + + Examples: + .. code-block:: python + + # x is a Tensor variable with shape [3, 6, 9] + fluid.nets.glu(input=x, dim=-1) # shape of output: [3, 3, 9] + """ + + a, b = layers.split(input, num_or_sections=2, dim=dim) + out = layers.elementwise_mul(x=a, y=b) + return out diff --git a/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py b/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py index 8b79d448e26..215accd4c66 100644 --- a/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py +++ b/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py @@ -6,8 +6,8 @@ import numpy class TestReorderLoDTensor(unittest.TestCase): num_seq = 5 - # [name, dim, lod_level] pair indicating data info of source and target - data_desc = (['input', 9, 0], ['ref', 5, 1]) + # [name, shape, lod_level] pair indicating data info of source and target + data_desc = (['input', [9], 0], ['ref', [5], 1]) @classmethod def setUpClass(cls): @@ -16,10 +16,10 @@ class TestReorderLoDTensor(unittest.TestCase): @classmethod def set_program(cls): dat = fluid.layers.data( - name=cls.data_desc[0][0], shape=[cls.data_desc[0][1]]) + name=cls.data_desc[0][0], shape=cls.data_desc[0][1]) dat.stop_gradient = False rank_dat = fluid.layers.data( - name=cls.data_desc[1][0], shape=[cls.data_desc[1][1]]) + name=cls.data_desc[1][0], shape=cls.data_desc[1][1]) table = fluid.layers.lod_rank_table(rank_dat) new_dat = fluid.layers.reorder_lod_tensor_by_rank( x=dat, rank_table=table) @@ -49,7 +49,7 @@ class TestReorderLoDTensor(unittest.TestCase): self.data = {} for desc in self.data_desc: data_name = desc[0] - data_dim = desc[1] + data_shape = desc[1] data_lod_level = desc[2] data_lod = [] for i in range(data_lod_level): @@ -59,9 +59,9 @@ class TestReorderLoDTensor(unittest.TestCase): size=self.num_seq if i == 0 else lod_level_i[-1]) lod_level_i = [0] + numpy.cumsum(lod_level_i).tolist() data_lod.append(lod_level_i) - data_value = numpy.random.random(size=[ - data_lod[-1][-1] if data_lod else self.num_seq, data_dim - ]).astype('float32') + data_value = numpy.random.random( + size=[data_lod[-1][-1] if data_lod else self.num_seq + ] + data_shape).astype('float32') self.data[data_name] = (data_value, data_lod) def set_inputs(self, place): @@ -163,8 +163,6 @@ class TestReorderLoDTensor(unittest.TestCase): numpy.allclose( numpy.array(actual_grad), expect_grad, atol=0.001)) self.assertEqual(expect_grad_lod, actual_grad.lod()) - global outputs_from_tensor_implicit_lod - outputs_from_tensor_implicit_lod = self.actual_outputs # compare outputs between LodTensors with explicit and implicit lod # use the same data but set the input lod explicitly -- GitLab From 579f684661d1badf34957b8a48ffe7d713547ead Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Mon, 15 Jan 2018 15:49:46 +0800 Subject: [PATCH 0261/2305] Add ctc_greedy_decode_op --- paddle/operators/ctc_greedy_decode_op.cc | 88 +++++++++++ paddle/operators/ctc_greedy_decode_op.cu | 138 ++++++++++++++++++ paddle/operators/ctc_greedy_decode_op.h | 82 +++++++++++ .../v2/fluid/tests/test_ctc_greedy_decode.py | 56 +++++++ 4 files changed, 364 insertions(+) create mode 100644 paddle/operators/ctc_greedy_decode_op.cc create mode 100644 paddle/operators/ctc_greedy_decode_op.cu create mode 100644 paddle/operators/ctc_greedy_decode_op.h create mode 100644 python/paddle/v2/fluid/tests/test_ctc_greedy_decode.py diff --git a/paddle/operators/ctc_greedy_decode_op.cc b/paddle/operators/ctc_greedy_decode_op.cc new file mode 100644 index 00000000000..3c9b705f7f6 --- /dev/null +++ b/paddle/operators/ctc_greedy_decode_op.cc @@ -0,0 +1,88 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/ctc_greedy_decode_op.h" + +namespace paddle { +namespace operators { + +class CTCGreedyDecodeOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + void InferShape(framework::InferShapeContext* ctx) const override { + PADDLE_ENFORCE(ctx->HasInput("Input"), + "Input of CTCGreedyDecodeOp should not be null."); + PADDLE_ENFORCE(ctx->HasOutput("Output"), + "Output of CTCGreedyDecodeOp should not be null."); + + auto input_dims = ctx->GetInputDim("Input"); + + int sequence_width = + static_cast(framework::product(input_dims) / input_dims[0]); + int blank = ctx->Attrs().Get("blank"); + PADDLE_ENFORCE((blank >= 0) && (blank < sequence_width), + "The value of Attr(blank) should be in interval [0, %d).", + sequence_width); + // TODO(wanghaoshuang): it is tricky to set the wrong dimension here. + ctx->SetOutputDim("Output", {input_dims[0], 1}); + } + + protected: + framework::OpKernelType GetExpectedKernelType( + const framework::ExecutionContext& ctx) const override { + return framework::OpKernelType( + framework::ToDataType(ctx.Input("Input")->type()), + ctx.device_context()); + } +}; + +class CTCGreedyDecodeOpMaker : public framework::OpProtoAndCheckerMaker { + public: + CTCGreedyDecodeOpMaker(OpProto* proto, OpAttrChecker* op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput("Input", + "(LodTensor, default: LoDTensor), the unscaled " + "probabilities of variable-length sequences, which is a 2-D " + "Tensor with LoD information. It's shape is " + "[Lp, num_classes + 1], where Lp is the sum of all input " + "sequences' length and num_classes is the true number of classes " + "(not including the blank label)."); + AddOutput("Output", "(Tensor, default: Tensor), the decode result "); + AddAttr("blank", + "(int, default: 0), the blank label setted in Connectionist " + "Temporal Classification (CTC) op, and it is in the " + "half-opened interval [0, num_classes + 1).") + .SetDefault(0); + AddAttr("merge_repeated", + "(bool, default: true), whether to " + "merge repeated elements between two blanks. ") + .SetDefault(true); + AddComment(R"DOC( +CTCGreedyDecoder is an implementation of the simple best path decoding +algorithm, selecting at each timestep the most likely class at each timestep. +)DOC"); + } +}; + +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OPERATOR(ctc_greedy_decode, ops::CTCGreedyDecodeOp, + ops::CTCGreedyDecodeOpMaker, + paddle::framework::EmptyGradOpMaker); +REGISTER_OP_CPU_KERNEL( + ctc_greedy_decode, + ops::CTCGreedyDecodeKernel); diff --git a/paddle/operators/ctc_greedy_decode_op.cu b/paddle/operators/ctc_greedy_decode_op.cu new file mode 100644 index 00000000000..43a78745aca --- /dev/null +++ b/paddle/operators/ctc_greedy_decode_op.cu @@ -0,0 +1,138 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include +#include +#include +#include "paddle/operators/ctc_greedy_decode_op.h" +#include "paddle/platform/cuda_helper.h" +#include "paddle/platform/gpu_info.h" + +namespace paddle { +namespace operators { +using platform::PADDLE_CUDA_NUM_THREADS; + +__device__ static float atomicMaxF(float* address, float val) { + int* address_as_i = (int*)address; + int old = *address_as_i, assumed; + do { + assumed = old; + old = ::atomicCAS(address_as_i, assumed, + __float_as_int(::fmaxf(val, __int_as_float(assumed)))); + } while (assumed != old); + return __int_as_float(old); +} + +template +__global__ void ArgmaxCudaKernel(const size_t seq_width, const T* logits, + int* output) { + T local_max_value = 0; + int local_max_index = 0; + __shared__ T max_value; + if (threadIdx.x == 0) { + max_value = 0; + } + __syncthreads(); + + for (int i = threadIdx.x; i < seq_width; i += BlockSize) { + T value = logits[blockIdx.x * seq_width + i]; + if (value > local_max_value) { + local_max_value = value; + local_max_index = i; + } + } + + atomicMaxF(&max_value, local_max_value); + + __syncthreads(); + + if (local_max_value == max_value) { + output[blockIdx.x] = local_max_index; + } +} + +template +__global__ void MergeAndDelCudaKernel(const int64_t num_token, int* tokens, + const size_t num_seq, size_t* lod0, + const int blank, const int merge_repeated, + size_t* out_lod0, int* output) { + int ouput_idx = 0; + out_lod0[0] = 0; + + for (int i = 0; i < num_seq; ++i) { + int pre_token = -1; + for (int j = lod0[i]; j < lod0[i + 1]; ++j) { + if (tokens[j] != blank && !(merge_repeated && tokens[j] == pre_token)) { + output[ouput_idx] = tokens[j]; + ++ouput_idx; + } + pre_token = tokens[j]; + } + out_lod0[i + 1] = ouput_idx; + } +} + +template +class CTCGreedyDecodeOpCUDAKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()), + "It must use CUDAPlace."); + auto* input = ctx.Input("Input"); + auto* output = ctx.Output("Output"); + + const int64_t num_tokens = input->dims()[0]; + const size_t seq_width = input->numel() / num_tokens; + const T* logits = input->data(); + Tensor tmp; + int* tokens = tmp.mutable_data({num_tokens, 1}, ctx.GetPlace()); + // get argmax + // platform::GpuMemsetAsync(args, 0, sizeof(float), stream); + + auto stream = ctx.cuda_device_context().stream(); + ArgmaxCudaKernel<<< + num_tokens, PADDLE_CUDA_NUM_THREADS, 0, stream>>>(seq_width, logits, + tokens); + + const size_t level = 0; + auto input_lod = framework::ToAbsOffset(input->lod()); + const size_t num_seq = input_lod[level].size() - 1; + const int blank = ctx.Attr("blank"); + const int merge_repeated = + static_cast(ctx.Attr("merge_repeated")); + + thrust::device_vector dev_out_lod0(input_lod[level].size()); + size_t* dev_out_lod0_ptr = thrust::raw_pointer_cast(dev_out_lod0.data()); + + int* output_data = + output->mutable_data({num_tokens, 1}, ctx.GetPlace()); + MergeAndDelCudaKernel<<<1, 1, 0, stream>>>( + num_tokens, tokens, num_seq, input_lod[level].data(), blank, + merge_repeated, dev_out_lod0_ptr, output_data); + + thrust::host_vector host_out_lod0(dev_out_lod0.begin(), + dev_out_lod0.end()); + framework::LoD out_lod; + out_lod.push_back(host_out_lod0); + output->set_lod(out_lod); + + output->Resize({static_cast(host_out_lod0.back()), 1}); + } +}; + +} // namespace operators +} // namespace paddle + +REGISTER_OP_CUDA_KERNEL(ctc_greedy_decode, + paddle::operators::CTCGreedyDecodeOpCUDAKernel); diff --git a/paddle/operators/ctc_greedy_decode_op.h b/paddle/operators/ctc_greedy_decode_op.h new file mode 100644 index 00000000000..f12ea6c541b --- /dev/null +++ b/paddle/operators/ctc_greedy_decode_op.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include +#include "paddle/framework/op_registry.h" +#include "unsupported/Eigen/CXX11/Tensor" +namespace paddle { +namespace operators { + +using Tensor = framework::Tensor; +using LoDTensor = framework::LoDTensor; + +template +class CTCGreedyDecodeKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + auto* input = ctx.Input("Input"); + auto* output = ctx.Output("Output"); + const size_t level = 0; + + auto input_lod = framework::ToAbsOffset(input->lod()); + auto input_dims = input->dims(); + PADDLE_ENFORCE_EQ(input_dims[0], + static_cast(input_lod[level].back()), + "The first dimension of Input(Input) should be equal to " + "the sum of all sequences' lengths."); + + const size_t num_sequences = input_lod[level].size() - 1; + const size_t sequence_width = input->numel() / input_dims[0]; + size_t blank = static_cast(ctx.Attr("blank")); + bool merge_repeated = ctx.Attr("merge_repeated"); + std::vector> pathes(num_sequences); + std::vector output_lod0(1, 0); + + const T* input_data = input->data(); + Eigen::Map< + Eigen::Matrix> + input_mat(const_cast(input_data), input->numel() / sequence_width, + sequence_width); + + size_t max_class_idx; + size_t prev_class_idx = -1; + for (size_t seq_idx = 0; seq_idx < num_sequences; ++seq_idx) { + for (size_t i = input_lod[level][seq_idx]; + i < input_lod[level][seq_idx + 1]; ++i) { + input_mat.row(i).maxCoeff(&max_class_idx); + if (max_class_idx != blank && + !(merge_repeated && max_class_idx == prev_class_idx)) { + pathes[seq_idx].push_back(max_class_idx); + } + prev_class_idx = max_class_idx; + } + output_lod0.push_back(output_lod0.back() + pathes[seq_idx].size()); + } + framework::LoD output_lod; + output_lod.push_back(output_lod0); + output->set_lod(output_lod); + int64_t num_step = static_cast(output_lod0.back()); + int* output_data = output->mutable_data({num_step, 1}, ctx.GetPlace()); + + for (int i = 0; i < num_sequences; ++i) { + memcpy(output_data + output_lod0[i], pathes[i].data(), + sizeof(int) * pathes[i].size()); + } + } +}; + +} // namespace operators +} // namespace paddle diff --git a/python/paddle/v2/fluid/tests/test_ctc_greedy_decode.py b/python/paddle/v2/fluid/tests/test_ctc_greedy_decode.py new file mode 100644 index 00000000000..23fceb6dcdb --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_ctc_greedy_decode.py @@ -0,0 +1,56 @@ +import sys +import unittest +import numpy as np +from op_test import OpTest +from test_softmax_op import stable_softmax + + +def CTCGreedyDecode(softmax, blank, merge_repeated): + prev_token = -1 + result = [] + for token in np.argmax(softmax, axis=1): + if (token != blank) and not (merge_repeated and token == prev_token): + result.append(token) + return np.array(result).reshape([len(result), 1]) + + +class TestCTCGreedyDecodeOp(OpTest): + def config(self): + self.op_type = "ctc_greedy_decode" + self.batch_size = 4 + self.num_classes = 8 + self.input_lod = [[0, 4, 5, 8, 11]] + self.blank = 7 + self.merge_repeated = True + + def setUp(self): + self.config() + input = np.random.uniform( + 0.1, 1.0, + [self.input_lod[0][-1], self.num_classes]).astype("float32") + softmax = np.apply_along_axis(stable_softmax, 1, input) + output = CTCGreedyDecode(softmax, self.blank, self.merge_repeated) + + self.inputs = {"Input": (softmax, self.input_lod), } + self.outputs = {"Output": output} + self.attrs = { + "blank": self.blank, + "merge_repeated": self.merge_repeated + } + + def test_check_output(self): + self.check_output() + + +class TestCTCGreedyDecodeOpCase1(TestCTCGreedyDecodeOp): + def config(self): + self.op_type = "ctc_greedy_decode" + self.batch_size = 4 + self.num_classes = 1025 + self.input_lod = [[0, 4, 5, 8, 11]] + self.blank = 0 + self.merge_repeated = True + + +if __name__ == "__main__": + unittest.main() -- GitLab From 0237b7e99a9173c54e6e64dc4195e8ff75b9b9b1 Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Mon, 15 Jan 2018 16:06:22 +0800 Subject: [PATCH 0262/2305] "remove random shuffle" (#7521) --- python/paddle/v2/dataset/common.py | 2 -- python/paddle/v2/dataset/imdb.py | 2 -- python/paddle/v2/dataset/mq2007.py | 8 ++------ 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/python/paddle/v2/dataset/common.py b/python/paddle/v2/dataset/common.py index 191d9ecfb12..fab8a68b0be 100644 --- a/python/paddle/v2/dataset/common.py +++ b/python/paddle/v2/dataset/common.py @@ -23,7 +23,6 @@ import paddle.v2.dataset import cPickle import glob import cPickle as pickle -import random __all__ = [ 'DATA_HOME', 'download', 'md5file', 'split', 'cluster_files_reader', @@ -206,7 +205,6 @@ def convert(output_path, reader, line_count, name_prefix): indx_f = 0 def write_data(indx_f, lines): - random.shuffle(lines) filename = "%s/%s-%05d" % (output_path, name_prefix, indx_f) writer = recordio.writer(filename) for l in lines: diff --git a/python/paddle/v2/dataset/imdb.py b/python/paddle/v2/dataset/imdb.py index 21ed7f7a5ce..37c4296f9bc 100644 --- a/python/paddle/v2/dataset/imdb.py +++ b/python/paddle/v2/dataset/imdb.py @@ -25,7 +25,6 @@ import collections import tarfile import re import string -import random __all__ = ['build_dict', 'train', 'test', 'convert'] @@ -83,7 +82,6 @@ def reader_creator(pos_pattern, neg_pattern, word_idx): load(pos_pattern, INS, 0) load(neg_pattern, INS, 1) - random.shuffle(INS) def reader(): for doc, label in INS: diff --git a/python/paddle/v2/dataset/mq2007.py b/python/paddle/v2/dataset/mq2007.py index b705c9109b2..d3b3dd524c3 100644 --- a/python/paddle/v2/dataset/mq2007.py +++ b/python/paddle/v2/dataset/mq2007.py @@ -24,7 +24,6 @@ http://research.microsoft.com/en-us/um/beijing/projects/letor/LETOR4.0/Data/MQ20 """ import os -import random import functools import rarfile from common import download @@ -265,7 +264,7 @@ def query_filter(querylists): return filter_query -def load_from_text(filepath, shuffle=True, fill_missing=-1): +def load_from_text(filepath, shuffle=False, fill_missing=-1): """ parse data file into querys """ @@ -287,17 +286,14 @@ def load_from_text(filepath, shuffle=True, fill_missing=-1): querylist._add_query(query) if querylist is not None: querylists.append(querylist) - if shuffle == True: - random.shuffle(querylists) return querylists -def __reader__(filepath, format="pairwise", shuffle=True, fill_missing=-1): +def __reader__(filepath, format="pairwise", shuffle=False, fill_missing=-1): """ Parameters -------- filename : string - shuffle : shuffle query-doc pair under the same query fill_missing : fill the missing value. default in MQ2007 is -1 Returns -- GitLab From 25fee871544212b74f70ce35a071237e0c0149b5 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Mon, 15 Jan 2018 16:11:42 +0800 Subject: [PATCH 0263/2305] Change program.seed from 0 to 1. --- .../paddle/v2/fluid/tests/test_dynrnn_static_input.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py index 657876eb743..9b138a6207f 100644 --- a/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py +++ b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py @@ -8,8 +8,7 @@ from paddle.v2.fluid.framework import Program, switch_main_program import bisect import numpy as np -fluid.default_startup_program().random_seed = 0 -np.random.seed(0) +fluid.default_startup_program().random_seed = 1 class TestDyRnnStaticInput(unittest.TestCase): @@ -166,8 +165,6 @@ class TestDyRnnStaticInput(unittest.TestCase): self.assertTrue(np.allclose(lod, expected_lods[i])) def test_network_gradient(self): - pass #still have bug (seed doesn't work) - ''' static_input_grad, loss = self.build_graph() self.exe.run(framework.default_startup_program()) @@ -175,7 +172,6 @@ class TestDyRnnStaticInput(unittest.TestCase): static_input_shape = self.static_input_tensor.get_dims() numeric_gradients = np.zeros(shape=static_input_shape).astype('float32') - print(actual_gradients) # calculate numeric gradients tensor_size = np.product(static_input_shape) for i in xrange(tensor_size): @@ -188,8 +184,8 @@ class TestDyRnnStaticInput(unittest.TestCase): y_neg = self.fetch_value(loss)[0][0] self.static_input_tensor.set_float_element(i, origin) numeric_gradients.ravel()[i] = (y_pos - y_neg) / self._delta / 2 - print(numeric_gradients) - ''' + self.assertTrue(np.allclose(actual_gradients, numeric_gradients, 0.001)) + self.assertTrue(np.allclose(actual_lod, self.static_input_tensor.lod())) if __name__ == '__main__': -- GitLab From 8f37c3c2a7c7cb7e44fcc576b8a0618294585a4d Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Mon, 15 Jan 2018 16:53:27 +0800 Subject: [PATCH 0264/2305] Fix sequence scale functor cuda kernel 1. Fix kernel 2. Add more test case --- paddle/operators/math/sequence_scale.cu | 15 +++-- .../paddle/v2/fluid/tests/test_warpctc_op.py | 56 ++++++++++++------- 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/paddle/operators/math/sequence_scale.cu b/paddle/operators/math/sequence_scale.cu index bc89711fcb9..ceaabd8e0fd 100644 --- a/paddle/operators/math/sequence_scale.cu +++ b/paddle/operators/math/sequence_scale.cu @@ -13,16 +13,21 @@ See the License for the specific language governing permissions and limitations under the License. */ #include "paddle/operators/math/sequence_scale.h" +#include "paddle/platform/cuda_helper.h" namespace paddle { namespace operators { namespace math { -template +using platform::PADDLE_CUDA_NUM_THREADS; + +template __global__ void SequenceScaleKernel(T* seq, size_t* lod, const T* scales, const size_t seq_width) { - if (threadIdx.x < (lod[blockIdx.x + 1] - lod[blockIdx.x]) * seq_width) { - int idx = lod[blockIdx.x] * seq_width + threadIdx.x; + for (int i = threadIdx.x; + i < (lod[blockIdx.x + 1] - lod[blockIdx.x]) * seq_width; + i += BlockSize) { + int idx = lod[blockIdx.x] * seq_width + i; seq[idx] *= scales[blockIdx.x]; } } @@ -39,8 +44,8 @@ class ScaleLoDTensorFunctor { framework::LoD abs_offset_lod = framework::ToAbsOffset(lod); T* seq_data = seq.mutable_data(context.GetPlace()); - int threads = 1024; - SequenceScaleKernel<<>>( + SequenceScaleKernel<<< + num_seq, PADDLE_CUDA_NUM_THREADS, 0, context.stream()>>>( seq_data, abs_offset_lod[level].data(), scales, seq_width); } }; diff --git a/python/paddle/v2/fluid/tests/test_warpctc_op.py b/python/paddle/v2/fluid/tests/test_warpctc_op.py index 07be05d2b03..5afd8af561b 100644 --- a/python/paddle/v2/fluid/tests/test_warpctc_op.py +++ b/python/paddle/v2/fluid/tests/test_warpctc_op.py @@ -4,6 +4,8 @@ import numpy as np from op_test import OpTest from test_softmax_op import stable_softmax +CUDA_BLOCK_SIZE = 512 + class CTCForward(object): def __init__(self, softmax, softmax_lod, labels, labels_lod, blank, @@ -154,39 +156,45 @@ class CTCForward(object): class TestWarpCTCOp(OpTest): + def config(self): + self.batch_size = 4 + self.num_classes = 8 + self.logits_lod = [[0, 4, 5, 8, 11]] + self.labels_lod = [[0, 3, 4, 8, 12]] + self.blank = self.num_classes - 1 + self.norm_by_times = False + def setUp(self): self.op_type = "warpctc" + self.config() - batch_size = 4 - num_classes = 8 - logits_lod = [[0, 4, 5, 8, 11]] - logits = np.random.uniform(0.1, 1.0, - [11, num_classes]).astype("float32") + logits = np.random.uniform( + 0.1, 1.0, + [self.logits_lod[0][-1], self.num_classes]).astype("float32") softmax = np.apply_along_axis(stable_softmax, 1, logits) - labels_lod = [[0, 3, 4, 8, 12]] # labels should not be blank - labels = np.random.randint(0, num_classes - 1, [12, 1], dtype="int32") - - blank = num_classes - 1 - norm_by_times = False + labels = np.random.randint( + 0, self.num_classes - 1, [self.labels_lod[0][-1], 1], dtype="int32") - ctc = CTCForward(softmax, logits_lod, labels, labels_lod, blank, - norm_by_times) + ctc = CTCForward(softmax, self.logits_lod, labels, self.labels_lod, + self.blank, self.norm_by_times) loss = ctc.forward() max_sequence_length = 0 - for i in range(batch_size): - max_sequence_length = max(max_sequence_length, - logits_lod[0][i + 1] - logits_lod[0][i]) + for i in range(self.batch_size): + max_sequence_length = max( + max_sequence_length, + self.logits_lod[0][i + 1] - self.logits_lod[0][i]) self.gradient = np.zeros( - [max_sequence_length, batch_size, num_classes], dtype="float32") + [max_sequence_length, self.batch_size, self.num_classes], + dtype="float32") self.inputs = { - "Logits": (logits, logits_lod), - "Label": (labels, labels_lod) + "Logits": (logits, self.logits_lod), + "Label": (labels, self.labels_lod) } self.outputs = {"Loss": loss} - self.attrs = {"blank": blank, "norm_by_times": norm_by_times} + self.attrs = {"blank": self.blank, "norm_by_times": self.norm_by_times} def test_check_output(self): self.check_output() @@ -196,5 +204,15 @@ class TestWarpCTCOp(OpTest): self.check_grad(["Logits"], "Loss", max_relative_error=0.007) +class TestWarpCTCOpCase1(TestWarpCTCOp): + def config(self): + self.batch_size = 4 + self.num_classes = CUDA_BLOCK_SIZE + 2 + self.logits_lod = [[0, 4, 5, 8, 11]] + self.labels_lod = [[0, 3, 4, 8, 12]] + self.blank = 0 + self.norm_by_times = False + + if __name__ == "__main__": unittest.main() -- GitLab From 736842e447669ae92b8ebd580338101d6d31458d Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 15 Jan 2018 16:58:15 +0800 Subject: [PATCH 0265/2305] wip --- paddle/operators/elementwise_max_op.cc | 45 +++++++++++ paddle/operators/elementwise_max_op.h | 107 +++++++++++++++++++++++++ paddle/operators/elementwise_min_op.cc | 45 +++++++++++ 3 files changed, 197 insertions(+) create mode 100644 paddle/operators/elementwise_max_op.cc create mode 100644 paddle/operators/elementwise_max_op.h create mode 100644 paddle/operators/elementwise_min_op.cc diff --git a/paddle/operators/elementwise_max_op.cc b/paddle/operators/elementwise_max_op.cc new file mode 100644 index 00000000000..b5c6b11ba3b --- /dev/null +++ b/paddle/operators/elementwise_max_op.cc @@ -0,0 +1,45 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/elementwise_max_op.h" +#include "paddle/operators/elementwise_op.h" + +namespace paddle { +namespace operators { +class ElementwiseMaxOpMaker : public ElementwiseOpMaker { + public: + ElementwiseMaxOpMaker(OpProto* proto, OpAttrChecker* op_checker) + : ElementwiseOpMaker(proto, op_checker) { + SetComment("Max", "Out = max(X, Y)"); + AddComment(comment_); + } +}; +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP(elementwise_max, ops::ElementwiseOp, ops::ElementwiseMaxOpMaker, + elementwise_max_grad, ops::ElementwiseOpGrad); +REGISTER_OP_CPU_KERNEL( + elementwise_max, + ops::ElementwiseMaxKernel, + ops::ElementwiseMaxKernel, + ops::ElementwiseMaxKernel, + ops::ElementwiseMaxKernel); +REGISTER_OP_CPU_KERNEL( + elementwise_max_grad, + ops::ElementwiseMaxGradKernel, + ops::ElementwiseMaxGradKernel, + ops::ElementwiseMaxGradKernel, + ops::ElementwiseMaxGradKernel); \ No newline at end of file diff --git a/paddle/operators/elementwise_max_op.h b/paddle/operators/elementwise_max_op.h new file mode 100644 index 00000000000..5c685b75e50 --- /dev/null +++ b/paddle/operators/elementwise_max_op.h @@ -0,0 +1,107 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/operators/elementwise_op_function.h" + +namespace paddle { +namespace operators { + +template +struct MaxFunctor { + inline HOSTDEVICE T operator()(T a, T b) const { return a > b ? a : b; } +}; + +template +class ElementwiseMaxKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + using Tensor = framework::Tensor; + + auto* x = ctx.Input("X"); + auto* y = ctx.Input("Y"); + auto* z = ctx.Output("Out"); + z->mutable_data(ctx.GetPlace()); + TransformFunctor, T, DeviceContext> functor( + x, y, z, ctx.template device_context(), MaxFunctor()); + + auto x_dims = x->dims(); + auto y_dims = y->dims(); + PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), + "Rank of first input must >= rank of second input."); + + if (x_dims == y_dims) { + functor.Run(); + return; + } + + int axis = ctx.Attr("axis"); + axis = (axis == -1 ? x_dims.size() - y_dims.size() : axis); + PADDLE_ENFORCE(axis >= 0 && axis < x_dims.size(), + "Axis should be in range [0, x_dims)"); + + int pre, n, post; + get_mid_dims(x_dims, y_dims, axis, pre, n, post); + if (post == 1) { + functor.RunRowWise(n, pre); + return; + } else { + functor.RunMidWise(n, pre, post); + return; + } + } +}; + +template +struct ElementwiseSubGradFunctor { + template + void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz) { + auto dz_e = framework::EigenVector::Flatten(*dz); + auto x_e = framework::EigenVector::Flatten(*x); + auto y_e = framework::EigenVector::Flatten(*y); + + if (dx) { + auto dx_e = framework::EigenVector::Flatten(*dx); + dx_e.device(d) = (x_e > y_e) * dz_e; + } + if (dy) { + auto dy_e = framework::EigenVector::Flatten(*dy); + dy_e.device(d) = (y_e >= x_e) * dz_e; + } + } +}; + +template +struct ElementwiseSubOneGradFunctor { + template + void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz) { + auto dz_e = framework::EigenVector::Flatten(*dz); + auto x_e = framework::EigenVector::Flatten(*x); + auto y_e = framework::EigenVector::Flatten(*y); + if (dx) { + auto dx_e = framework::EigenVector::Flatten(*dx); + dx_e.device(d) = dz_e; + } + if (dy) { + auto dy_e = framework::EigenVector::Flatten(*dy); + dy_e.device(d) = (-1.0) * dz_e.sum(); + } + } +}; + +} // namespace operators +} // namespace paddle diff --git a/paddle/operators/elementwise_min_op.cc b/paddle/operators/elementwise_min_op.cc new file mode 100644 index 00000000000..b78846f17ae --- /dev/null +++ b/paddle/operators/elementwise_min_op.cc @@ -0,0 +1,45 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/elementwise_min_op.h" +#include "paddle/operators/elementwise_op.h" + +namespace paddle { +namespace operators { +class ElementwiseMinOpMaker : public ElementwiseOpMaker { + public: + ElementwiseMinOpMaker(OpProto* proto, OpAttrChecker* op_checker) + : ElementwiseOpMaker(proto, op_checker) { + SetComment("Max", "Out = min(X, Y)"); + AddComment(comment_); + } +}; +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP(elementwise_min, ops::ElementwiseOp, ops::ElementwiseMinOpMaker, + elementwise_min_grad, ops::ElementwiseOpGrad); +REGISTER_OP_CPU_KERNEL( + elementwise_min, + ops::ElementwiseMinKernel, + ops::ElementwiseMinKernel, + ops::ElementwiseMinKernel, + ops::ElementwiseMinKernel); +REGISTER_OP_CPU_KERNEL( + elementwise_min_grad, + ops::ElementwiseMinGradKernel, + ops::ElementwiseMinGradKernel, + ops::ElementwiseMinGradKernel, + ops::ElementwiseMinGradKernel); \ No newline at end of file -- GitLab From 6ee8a2e1dbdc35a347677ee15a68963af7731b77 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 15 Jan 2018 17:03:33 +0800 Subject: [PATCH 0266/2305] remove unnecessary functor1 --- paddle/operators/elementwise_add_op.h | 18 ------------------ paddle/operators/elementwise_div_op.h | 1 - paddle/operators/elementwise_mul_op.h | 1 - paddle/operators/elementwise_op_function.h | 3 +-- paddle/operators/elementwise_sub_op.h | 18 ------------------ 5 files changed, 1 insertion(+), 40 deletions(-) diff --git a/paddle/operators/elementwise_add_op.h b/paddle/operators/elementwise_add_op.h index 59abbb57d1d..6478e1e0c2e 100644 --- a/paddle/operators/elementwise_add_op.h +++ b/paddle/operators/elementwise_add_op.h @@ -81,23 +81,6 @@ struct ElementwiseAddGradFunctor { } }; -template -struct ElementwiseAddOneGradFunctor { - template - void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz) { - auto dz_e = framework::EigenVector::Flatten(*dz); - if (dx) { - auto dx_e = framework::EigenVector::Flatten(*dx); - dx_e.device(d) = dz_e; - } - if (dy) { - auto dy_e = framework::EigenVector::Flatten(*dy); - dy_e.device(d) = dz_e.sum(); - } - } -}; - template struct ElementwiseAddBroadCastGradFunctor { template { public: void Compute(const framework::ExecutionContext& ctx) const override { ElementwiseGradCompute, - ElementwiseAddOneGradFunctor, ElementwiseAddBroadCastGradFunctor, ElementwiseAddBroadCast2GradFunctor>(ctx); } diff --git a/paddle/operators/elementwise_div_op.h b/paddle/operators/elementwise_div_op.h index 875abd313ff..7783875e24e 100644 --- a/paddle/operators/elementwise_div_op.h +++ b/paddle/operators/elementwise_div_op.h @@ -107,7 +107,6 @@ class ElementwiseDivGradKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { ElementwiseGradCompute, - ElementwiseDivGradFunctor, ElementwiseDivBroadCastGradFunctor, ElementwiseDivBroadCast2GradFunctor>(ctx); } diff --git a/paddle/operators/elementwise_mul_op.h b/paddle/operators/elementwise_mul_op.h index 3ee50207c07..0e6559eacc0 100644 --- a/paddle/operators/elementwise_mul_op.h +++ b/paddle/operators/elementwise_mul_op.h @@ -106,7 +106,6 @@ class ElementwiseMulGradKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { ElementwiseGradCompute, - ElementwiseMulGradFunctor, ElementwiseMulBroadCastGradFunctor, ElementwiseMulBroadCast2GradFunctor>(ctx); } diff --git a/paddle/operators/elementwise_op_function.h b/paddle/operators/elementwise_op_function.h index 560247cb108..0c75276b031 100644 --- a/paddle/operators/elementwise_op_function.h +++ b/paddle/operators/elementwise_op_function.h @@ -311,8 +311,7 @@ EIGEN_FUNCTOR(Mul, EIGEN_MUL); EIGEN_FUNCTOR(Div, EIGEN_DIV); template + typename broadcastfunctor, typename broadcast2functor> void ElementwiseGradCompute(const framework::ExecutionContext& ctx) { using Tensor = framework::Tensor; diff --git a/paddle/operators/elementwise_sub_op.h b/paddle/operators/elementwise_sub_op.h index 66edf8672d1..347e92f87c7 100644 --- a/paddle/operators/elementwise_sub_op.h +++ b/paddle/operators/elementwise_sub_op.h @@ -43,23 +43,6 @@ struct ElementwiseSubGradFunctor { } }; -template -struct ElementwiseSubOneGradFunctor { - template - void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz) { - auto dz_e = framework::EigenVector::Flatten(*dz); - if (dx) { - auto dx_e = framework::EigenVector::Flatten(*dx); - dx_e.device(d) = dz_e; - } - if (dy) { - auto dy_e = framework::EigenVector::Flatten(*dy); - dy_e.device(d) = (-1.0) * dz_e.sum(); - } - } -}; - template struct ElementwiseSubBroadCastGradFunctor { template { public: void Compute(const framework::ExecutionContext& ctx) const override { ElementwiseGradCompute, - ElementwiseSubOneGradFunctor, ElementwiseSubBroadCastGradFunctor, ElementwiseSubBroadCast2GradFunctor>(ctx); } -- GitLab From acf37ad67504ee2f4ce5f601906c1b5102ede124 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 15 Jan 2018 17:53:36 +0800 Subject: [PATCH 0267/2305] Complete elementwise_max_op --- paddle/operators/elementwise_max_op.cc | 2 +- paddle/operators/elementwise_max_op.cu | 32 +++++++++++++ paddle/operators/elementwise_max_op.h | 63 ++++++++++++++++++++++---- 3 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 paddle/operators/elementwise_max_op.cu diff --git a/paddle/operators/elementwise_max_op.cc b/paddle/operators/elementwise_max_op.cc index b5c6b11ba3b..53c27ae5be4 100644 --- a/paddle/operators/elementwise_max_op.cc +++ b/paddle/operators/elementwise_max_op.cc @@ -42,4 +42,4 @@ REGISTER_OP_CPU_KERNEL( ops::ElementwiseMaxGradKernel, ops::ElementwiseMaxGradKernel, ops::ElementwiseMaxGradKernel, - ops::ElementwiseMaxGradKernel); \ No newline at end of file + ops::ElementwiseMaxGradKernel); diff --git a/paddle/operators/elementwise_max_op.cu b/paddle/operators/elementwise_max_op.cu new file mode 100644 index 00000000000..5ff4af17477 --- /dev/null +++ b/paddle/operators/elementwise_max_op.cu @@ -0,0 +1,32 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#define EIGEN_USE_GPU +#include "paddle/operators/elementwise_max_op.h" + +namespace ops = paddle::operators; + +REGISTER_OP_CUDA_KERNEL( + elementwise_max, + ops::ElementwiseMaxKernel, + ops::ElementwiseMaxKernel, + ops::ElementwiseMaxKernel, + ops::ElementwiseMaxKernel); +REGISTER_OP_CUDA_KERNEL( + elementwise_max_grad, + ops::ElementwiseMaxGradKernel, + ops::ElementwiseMaxGradKernel, + ops::ElementwiseMaxGradKernel, + ops::ElementwiseMaxGradKernel); diff --git a/paddle/operators/elementwise_max_op.h b/paddle/operators/elementwise_max_op.h index 5c685b75e50..e370aeb3080 100644 --- a/paddle/operators/elementwise_max_op.h +++ b/paddle/operators/elementwise_max_op.h @@ -65,43 +65,88 @@ class ElementwiseMaxKernel : public framework::OpKernel { }; template -struct ElementwiseSubGradFunctor { +struct ElementwiseMaxGradFunctor { template void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz) { - auto dz_e = framework::EigenVector::Flatten(*dz); auto x_e = framework::EigenVector::Flatten(*x); auto y_e = framework::EigenVector::Flatten(*y); + auto dz_e = framework::EigenVector::Flatten(*dz); if (dx) { auto dx_e = framework::EigenVector::Flatten(*dx); - dx_e.device(d) = (x_e > y_e) * dz_e; + dx_e.device(d) = (x_e > y_e).template cast() * dz_e; } if (dy) { auto dy_e = framework::EigenVector::Flatten(*dy); - dy_e.device(d) = (y_e >= x_e) * dz_e; + dy_e.device(d) = (y_e >= x_e).template cast() * dz_e; } } }; template -struct ElementwiseSubOneGradFunctor { +struct ElementwiseMaxBroadCastGradFunctor { template - void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz) { + typename dY, typename dZ, typename Pre, typename N> + void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz, Pre pre, N n) { + auto x_e = framework::EigenVector::Flatten(*x); + auto y_e = framework::EigenVector::Flatten(*y); auto dz_e = framework::EigenVector::Flatten(*dz); + + auto y_e_bcast = y_e.reshape(Eigen::DSizes(1, n)) + .broadcast(Eigen::DSizes(pre, 1)) + .reshape(Eigen::DSizes(x_e.size())); + + if (dx) { + auto dx_e = framework::EigenVector::Flatten(*dx); + dx_e.device(d) = (x_e > y_e_bcast).template cast() * dz_e; + } + + if (dy) { + auto dy_e = framework::EigenVector::Flatten(*dy); + dy_e.device(d) = ((y_e_bcast >= x_e).template cast() * dz_e) + .reshape(Eigen::DSizes(pre, n)) + .sum(Eigen::array{{0}}); + } + } +}; + +template +struct ElementwiseMaxBroadCast2GradFunctor { + template + void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz, Pre pre, N n, + Post post) { auto x_e = framework::EigenVector::Flatten(*x); auto y_e = framework::EigenVector::Flatten(*y); + auto dz_e = framework::EigenVector::Flatten(*dz); + + auto y_e_bcast = y_e.reshape(Eigen::DSizes(1, n, 1)) + .broadcast(Eigen::DSizes(pre, 1, post)) + .reshape(Eigen::DSizes(x_e.size())); if (dx) { auto dx_e = framework::EigenVector::Flatten(*dx); - dx_e.device(d) = dz_e; + dx_e.device(d) = (x_e > y_e_bcast).template cast() * dz_e; } + if (dy) { auto dy_e = framework::EigenVector::Flatten(*dy); - dy_e.device(d) = (-1.0) * dz_e.sum(); + dy_e.device(d) = ((y_e_bcast >= x_e).template cast() * dz_e) + .reshape(Eigen::DSizes(pre, n, post)) + .sum(Eigen::array{{0, 2}}); } } }; +template +class ElementwiseMaxGradKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + ElementwiseGradCompute, + ElementwiseMaxBroadCastGradFunctor, + ElementwiseMaxBroadCast2GradFunctor>(ctx); + } +}; + } // namespace operators } // namespace paddle -- GitLab From 234013a9d761190119f0f350fdde5e60ab8e3992 Mon Sep 17 00:00:00 2001 From: guosheng Date: Mon, 15 Jan 2018 18:13:01 +0800 Subject: [PATCH 0268/2305] Add python wrapper for matmul_op --- python/paddle/v2/fluid/layers/nn.py | 82 +++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 96a64af3709..bc373d4b37f 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -1584,3 +1584,85 @@ def split(input, num_or_sections, dim=-1): 'axis': dim }) return outs + + +def matmul(x, y): + """ + Applies matrix multipication to two tensors. + + This operator is used to perform (batched) matrix multiplication + over the last two dimensions of the input tensors `X` and `Y`. + + If a transpose flag is specified, the last two dimensions of the + tensor are transposed. If the tensor is rank-1 of shape [D], then + for `X` it is treated as [1, D] in nontransposed form and as [D, 1] + in transposed form, whereas for `Y` it is the opposite: It is treated + as [D, 1] in nontransposed form and as [1, D] in transposed form. + + Examples without transpose: + - X: [K], Y: [K] => Out: [1] + - X: [K], Y: [K, N] => Out: [N] + - X: [B, M, K], Y: [K] => Out: [B, M] + - X: [M, K], Y: [B, K, N] => Out: [B, M, N] + - X: [B, M, K], Y: [B, K, N] => Out: [B, M, N] + + The behavior is designed to be similar to the `numpy.matmul` function. + The differences are: + - Currently only rank 1 to rank 3 input tensors are supported. + - We add `transpose_X` and `transpose_Y` flags. + + Both the input `X` and `Y` can carry the LoD (Level of Details) information, + or not. But the output only shares the LoD information with input `X`. + + Args: + x (Variable): The input variable which is a Tensor or LoDTensor. + y (Variable): If :attr:`num_or_sections` is an integer, + then the integer indicates the number of equal sized sub-tensors + that the tensor will be divided into. If :attr:`num_or_sections` + is a list of integers, the length of list indicates the number of + sub-tensors and the integers indicate the sizes of sub-tensors' + :attr:`dim` dimension orderly. + dim (int): The dimension along which to split. If :math:`dim < 0`, the + dimension to split along is :math:`rank(input) + dim`. + + Returns: + List: The list of segmented tensor variables. + + Examples: + .. code-block:: python + + # x is a Tensor variable with shape [3, 9, 5]: + x0, x1, x2 = fluid.layers.split(x, num_or_sections=3, dim=1) + x0.shape # [3, 3, 5] + x1.shape # [3, 3, 5] + x2.shape # [3, 3, 5] + x0, x1, x2 = fluid.layers.split(x, num_or_sections=[2, 3, 4], dim=1) + x0.shape # [3, 2, 5] + x1.shape # [3, 3, 5] + x2.shape # [3, 4, 5] + """ + helper = LayerHelper('split', **locals()) + input_shape = input.shape + dim = (len(input_shape) + dim) if dim < 0 else dim + if isinstance(num_or_sections, int): + assert num_or_sections > 1, 'num_or_sections must be more than 1.' + num = num_or_sections + else: + assert len(num_or_sections) < input_shape[ + dim], 'len(num_or_sections) must not be more than input.shape[dim].' + num = len(num_or_sections) + outs = [ + helper.create_tmp_variable(dtype=helper.input_dtype()) + for i in range(num) + ] + helper.append_op( + type='split', + inputs={'X': input}, + outputs={'Out': outs}, + attrs={ + 'num': num_or_sections if isinstance(num_or_sections, int) else 0, + 'sections': num_or_sections + if isinstance(num_or_sections, list) else [], + 'axis': dim + }) + return outs -- GitLab From f2e1f3e1143a9231bbc642c98b22b6bae3f94096 Mon Sep 17 00:00:00 2001 From: guosheng Date: Mon, 15 Jan 2018 18:45:29 +0800 Subject: [PATCH 0269/2305] Fix activation for glu --- python/paddle/v2/fluid/nets.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/python/paddle/v2/fluid/nets.py b/python/paddle/v2/fluid/nets.py index d515429216c..d60ae43675f 100644 --- a/python/paddle/v2/fluid/nets.py +++ b/python/paddle/v2/fluid/nets.py @@ -106,9 +106,10 @@ def sequence_conv_pool(input, def glu(input, dim=-1): """ - The gated linear unit composed by split and elementwise multiplication. - Specifically, Split the input into two equal sized parts :math:`a` and - :math:`b` along the given dimension and then compute as following: + The gated linear unit composed by split, sigmoid activation and elementwise + multiplication. Specifically, Split the input into two equal sized parts + :math:`a` and :math:`b` along the given dimension and then compute as + following: .. math:: @@ -133,5 +134,6 @@ def glu(input, dim=-1): """ a, b = layers.split(input, num_or_sections=2, dim=dim) - out = layers.elementwise_mul(x=a, y=b) + act_b = layers.sigmoid(x=b) + out = layers.elementwise_mul(x=a, y=act_b) return out -- GitLab From f5cd9619002e2b392ac5195da0e3feef67c0c4c4 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 15 Jan 2018 18:56:17 +0800 Subject: [PATCH 0270/2305] complete elementwise_min_op --- paddle/operators/elementwise_max_op.h | 6 +- paddle/operators/elementwise_min_op.cc | 2 +- paddle/operators/elementwise_min_op.cu | 32 ++++++ paddle/operators/elementwise_min_op.h | 152 +++++++++++++++++++++++++ 4 files changed, 188 insertions(+), 4 deletions(-) create mode 100644 paddle/operators/elementwise_min_op.cu create mode 100644 paddle/operators/elementwise_min_op.h diff --git a/paddle/operators/elementwise_max_op.h b/paddle/operators/elementwise_max_op.h index e370aeb3080..92152f7cb6e 100644 --- a/paddle/operators/elementwise_max_op.h +++ b/paddle/operators/elementwise_max_op.h @@ -79,7 +79,7 @@ struct ElementwiseMaxGradFunctor { } if (dy) { auto dy_e = framework::EigenVector::Flatten(*dy); - dy_e.device(d) = (y_e >= x_e).template cast() * dz_e; + dy_e.device(d) = (x_e <= y_e).template cast() * dz_e; } } }; @@ -104,7 +104,7 @@ struct ElementwiseMaxBroadCastGradFunctor { if (dy) { auto dy_e = framework::EigenVector::Flatten(*dy); - dy_e.device(d) = ((y_e_bcast >= x_e).template cast() * dz_e) + dy_e.device(d) = ((x_e <= y_e_bcast).template cast() * dz_e) .reshape(Eigen::DSizes(pre, n)) .sum(Eigen::array{{0}}); } @@ -131,7 +131,7 @@ struct ElementwiseMaxBroadCast2GradFunctor { if (dy) { auto dy_e = framework::EigenVector::Flatten(*dy); - dy_e.device(d) = ((y_e_bcast >= x_e).template cast() * dz_e) + dy_e.device(d) = ((x_e <= y_e_bcast).template cast() * dz_e) .reshape(Eigen::DSizes(pre, n, post)) .sum(Eigen::array{{0, 2}}); } diff --git a/paddle/operators/elementwise_min_op.cc b/paddle/operators/elementwise_min_op.cc index b78846f17ae..99482e1bf60 100644 --- a/paddle/operators/elementwise_min_op.cc +++ b/paddle/operators/elementwise_min_op.cc @@ -42,4 +42,4 @@ REGISTER_OP_CPU_KERNEL( ops::ElementwiseMinGradKernel, ops::ElementwiseMinGradKernel, ops::ElementwiseMinGradKernel, - ops::ElementwiseMinGradKernel); \ No newline at end of file + ops::ElementwiseMinGradKernel); diff --git a/paddle/operators/elementwise_min_op.cu b/paddle/operators/elementwise_min_op.cu new file mode 100644 index 00000000000..3547e6ccb77 --- /dev/null +++ b/paddle/operators/elementwise_min_op.cu @@ -0,0 +1,32 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#define EIGEN_USE_GPU +#include "paddle/operators/elementwise_min_op.h" + +namespace ops = paddle::operators; + +REGISTER_OP_CUDA_KERNEL( + elementwise_min, + ops::ElementwiseMinKernel, + ops::ElementwiseMinKernel, + ops::ElementwiseMinKernel, + ops::ElementwiseMinKernel); +REGISTER_OP_CUDA_KERNEL( + elementwise_min_grad, + ops::ElementwiseMinGradKernel, + ops::ElementwiseMinGradKernel, + ops::ElementwiseMinGradKernel, + ops::ElementwiseMinGradKernel); diff --git a/paddle/operators/elementwise_min_op.h b/paddle/operators/elementwise_min_op.h new file mode 100644 index 00000000000..53b7f59fa07 --- /dev/null +++ b/paddle/operators/elementwise_min_op.h @@ -0,0 +1,152 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once + +#include "paddle/operators/elementwise_op_function.h" + +namespace paddle { +namespace operators { + +template +struct MinFunctor { + inline HOSTDEVICE T operator()(T a, T b) const { return a < b ? a : b; } +}; + +template +class ElementwiseMinKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + using Tensor = framework::Tensor; + + auto* x = ctx.Input("X"); + auto* y = ctx.Input("Y"); + auto* z = ctx.Output("Out"); + z->mutable_data(ctx.GetPlace()); + TransformFunctor, T, DeviceContext> functor( + x, y, z, ctx.template device_context(), MinFunctor()); + + auto x_dims = x->dims(); + auto y_dims = y->dims(); + PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), + "Rank of first input must >= rank of second input."); + + if (x_dims == y_dims) { + functor.Run(); + return; + } + + int axis = ctx.Attr("axis"); + axis = (axis == -1 ? x_dims.size() - y_dims.size() : axis); + PADDLE_ENFORCE(axis >= 0 && axis < x_dims.size(), + "Axis should be in range [0, x_dims)"); + + int pre, n, post; + get_mid_dims(x_dims, y_dims, axis, pre, n, post); + if (post == 1) { + functor.RunRowWise(n, pre); + return; + } else { + functor.RunMidWise(n, pre, post); + return; + } + } +}; + +template +struct ElementwiseMinGradFunctor { + template + void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz) { + auto x_e = framework::EigenVector::Flatten(*x); + auto y_e = framework::EigenVector::Flatten(*y); + auto dz_e = framework::EigenVector::Flatten(*dz); + + if (dx) { + auto dx_e = framework::EigenVector::Flatten(*dx); + dx_e.device(d) = (x_e < y_e).template cast() * dz_e; + } + if (dy) { + auto dy_e = framework::EigenVector::Flatten(*dy); + dy_e.device(d) = (x_e >= y_e).template cast() * dz_e; + } + } +}; + +template +struct ElementwiseMinBroadCastGradFunctor { + template + void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz, Pre pre, N n) { + auto x_e = framework::EigenVector::Flatten(*x); + auto y_e = framework::EigenVector::Flatten(*y); + auto dz_e = framework::EigenVector::Flatten(*dz); + + auto y_e_bcast = y_e.reshape(Eigen::DSizes(1, n)) + .broadcast(Eigen::DSizes(pre, 1)) + .reshape(Eigen::DSizes(x_e.size())); + + if (dx) { + auto dx_e = framework::EigenVector::Flatten(*dx); + dx_e.device(d) = (x_e < y_e_bcast).template cast() * dz_e; + } + + if (dy) { + auto dy_e = framework::EigenVector::Flatten(*dy); + dy_e.device(d) = ((x_e >= y_e_bcast).template cast() * dz_e) + .reshape(Eigen::DSizes(pre, n)) + .sum(Eigen::array{{0}}); + } + } +}; + +template +struct ElementwiseMinBroadCast2GradFunctor { + template + void operator()(Device d, X x, Y y, Z z, dX dx, dY dy, dZ dz, Pre pre, N n, + Post post) { + auto x_e = framework::EigenVector::Flatten(*x); + auto y_e = framework::EigenVector::Flatten(*y); + auto dz_e = framework::EigenVector::Flatten(*dz); + + auto y_e_bcast = y_e.reshape(Eigen::DSizes(1, n, 1)) + .broadcast(Eigen::DSizes(pre, 1, post)) + .reshape(Eigen::DSizes(x_e.size())); + if (dx) { + auto dx_e = framework::EigenVector::Flatten(*dx); + dx_e.device(d) = (x_e < y_e_bcast).template cast() * dz_e; + } + + if (dy) { + auto dy_e = framework::EigenVector::Flatten(*dy); + dy_e.device(d) = ((x_e >= y_e_bcast).template cast() * dz_e) + .reshape(Eigen::DSizes(pre, n, post)) + .sum(Eigen::array{{0, 2}}); + } + } +}; + +template +class ElementwiseMinGradKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + ElementwiseGradCompute, + ElementwiseMinBroadCastGradFunctor, + ElementwiseMinBroadCast2GradFunctor>(ctx); + } +}; + +} // namespace operators +} // namespace paddle -- GitLab From 78dc93430ca8eb40e8ad4c1315dfeef6d1889c77 Mon Sep 17 00:00:00 2001 From: chengduoZH Date: Mon, 15 Jan 2018 19:22:31 +0800 Subject: [PATCH 0271/2305] expose use_cudnn --- python/paddle/v2/fluid/layers/nn.py | 27 +++++++++++++++++++++------ python/paddle/v2/fluid/nets.py | 19 +++++++++++++------ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 99a40ce45a2..df2233089c2 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -660,6 +660,7 @@ def conv2d(input, groups=None, param_attr=None, bias_attr=None, + use_cudnn=False, act=None): """ **Convlution2D Layer** @@ -758,6 +759,8 @@ def conv2d(input, stride = [stride, stride] if isinstance(padding, int): padding = [padding, padding] + if not isinstance(use_cudnn, bool): + raise ValueError("use_cudnn should be True or False") input_shape = input.shape filter_shape = [num_filters, num_filter_channels] + filter_size @@ -781,9 +784,12 @@ def conv2d(input, 'Filter': filter_param, }, outputs={"Output": pre_bias}, - attrs={'strides': stride, - 'paddings': padding, - 'groups': groups}) + attrs={ + 'strides': stride, + 'paddings': padding, + 'groups': groups, + 'use_cudnn': use_cudnn + }) pre_act = helper.append_bias_op(pre_bias, dim_start=1, dim_end=2) @@ -931,7 +937,8 @@ def pool2d(input, pool_type, pool_stride=None, pool_padding=None, - global_pooling=False): + global_pooling=False, + use_cudnn=False): """ This function adds the operator for pooling in 2 dimensions, using the pooling configurations mentioned in input parameters. @@ -950,6 +957,8 @@ def pool2d(input, pool_stride = [pool_stride, pool_stride] if isinstance(pool_padding, int): pool_padding = [pool_padding, pool_padding] + if not isinstance(use_cudnn, bool): + raise ValueError("use_cudnn should be True or False") helper = LayerHelper('pool2d', **locals()) dtype = helper.input_dtype() @@ -964,7 +973,8 @@ def pool2d(input, "ksize": pool_size, "global_pooling": global_pooling, "strides": pool_stride, - "paddings": pool_padding + "paddings": pool_padding, + "use_cudnn": use_cudnn }) return pool_out @@ -1077,7 +1087,8 @@ def conv2d_transpose(input, padding=None, stride=None, dilation=None, - param_attr=None): + param_attr=None, + use_cudnn=False): """ The transpose of conv2d layer. @@ -1132,6 +1143,10 @@ def conv2d_transpose(input, elif dilation is not None: op_attr['dilations'] = dilation + if not isinstance(use_cudnn, bool): + raise ValueError("use_cudnn should be True or False") + op_attr['use_cudnn'] = use_cudnn + if filter_size is None: if output_size is None: raise ValueError("output_size must be set when filter_size is None") diff --git a/python/paddle/v2/fluid/nets.py b/python/paddle/v2/fluid/nets.py index 47b550bf4d8..8ac58fd7334 100644 --- a/python/paddle/v2/fluid/nets.py +++ b/python/paddle/v2/fluid/nets.py @@ -13,19 +13,22 @@ def simple_img_conv_pool(input, pool_stride, act, param_attr=None, - pool_type='max'): + pool_type='max', + use_cudnn=False): conv_out = layers.conv2d( input=input, num_filters=num_filters, filter_size=filter_size, param_attr=param_attr, - act=act) + act=act, + use_cudnn=use_cudnn) pool_out = layers.pool2d( input=conv_out, pool_size=pool_size, pool_type=pool_type, - pool_stride=pool_stride) + pool_stride=pool_stride, + use_cudnn=use_cudnn) return pool_out @@ -38,8 +41,10 @@ def img_conv_group(input, param_attr=None, conv_with_batchnorm=False, conv_batchnorm_drop_rate=None, + conv_use_cudnn=False, pool_stride=1, - pool_type=None): + pool_type=None, + pool_use_cudnn=False): """ Image Convolution Group, Used for vgg net. """ @@ -70,7 +75,8 @@ def img_conv_group(input, filter_size=conv_filter_size[i], padding=conv_padding[i], param_attr=param_attr[i], - act=local_conv_act) + act=local_conv_act, + use_cudnn=conv_use_cudnn) if conv_with_batchnorm[i]: tmp = layers.batch_norm(input=tmp, act=conv_act) @@ -82,7 +88,8 @@ def img_conv_group(input, input=tmp, pool_size=pool_size, pool_type=pool_type, - pool_stride=pool_stride) + pool_stride=pool_stride, + use_cudnn=pool_use_cudnn) return pool_out -- GitLab From 79aa51229ad41c4633056ad0f7056bbb88648a87 Mon Sep 17 00:00:00 2001 From: chengduoZH Date: Mon, 15 Jan 2018 19:46:03 +0800 Subject: [PATCH 0272/2305] fix conv, pool, conv_trans to decide use cudnn or not --- paddle/operators/conv_op.cc | 2 ++ paddle/operators/conv_op.h | 1 + paddle/operators/conv_transpose_op.cc | 2 ++ paddle/operators/conv_transpose_op.h | 1 + paddle/operators/pool_op.cc | 2 ++ paddle/operators/pool_op.h | 1 + paddle/platform/dynload/cudnn.cc | 4 ++++ 7 files changed, 13 insertions(+) diff --git a/paddle/operators/conv_op.cc b/paddle/operators/conv_op.cc index 424eccdb7dc..a6d3ebb4f52 100644 --- a/paddle/operators/conv_op.cc +++ b/paddle/operators/conv_op.cc @@ -70,6 +70,7 @@ void ConvOp::InferShape(framework::InferShapeContext* ctx) const { framework::OpKernelType ConvOp::GetExpectedKernelType( const framework::ExecutionContext& ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); + use_cudnn &= platform::dynload::HasCUDNN(); framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; @@ -283,6 +284,7 @@ void ConvOpGrad::InferShape(framework::InferShapeContext* ctx) const { framework::OpKernelType ConvOpGrad::GetExpectedKernelType( const framework::ExecutionContext& ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); + use_cudnn &= platform::dynload::HasCUDNN(); framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; diff --git a/paddle/operators/conv_op.h b/paddle/operators/conv_op.h index 5a8933e7915..2a6fad65826 100644 --- a/paddle/operators/conv_op.h +++ b/paddle/operators/conv_op.h @@ -19,6 +19,7 @@ limitations under the License. */ #include "paddle/operators/math/im2col.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/vol2col.h" +#include "paddle/platform/dynload/cudnn.h" namespace paddle { namespace operators { diff --git a/paddle/operators/conv_transpose_op.cc b/paddle/operators/conv_transpose_op.cc index cf4e8c0a303..53153c02963 100644 --- a/paddle/operators/conv_transpose_op.cc +++ b/paddle/operators/conv_transpose_op.cc @@ -61,6 +61,7 @@ void ConvTransposeOp::InferShape(framework::InferShapeContext* ctx) const { framework::OpKernelType ConvTransposeOp::GetExpectedKernelType( const framework::ExecutionContext& ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); + use_cudnn &= platform::dynload::HasCUDNN(); framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; @@ -263,6 +264,7 @@ void ConvTransposeOpGrad::InferShape(framework::InferShapeContext* ctx) const { framework::OpKernelType ConvTransposeOpGrad::GetExpectedKernelType( const framework::ExecutionContext& ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); + use_cudnn &= platform::dynload::HasCUDNN(); framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; diff --git a/paddle/operators/conv_transpose_op.h b/paddle/operators/conv_transpose_op.h index a42ade41b16..f43d652fe1f 100644 --- a/paddle/operators/conv_transpose_op.h +++ b/paddle/operators/conv_transpose_op.h @@ -19,6 +19,7 @@ limitations under the License. */ #include "paddle/operators/math/im2col.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/vol2col.h" +#include "paddle/platform/dynload/cudnn.h" namespace paddle { namespace operators { diff --git a/paddle/operators/pool_op.cc b/paddle/operators/pool_op.cc index 3e567efd082..e0f9ea7aa0d 100644 --- a/paddle/operators/pool_op.cc +++ b/paddle/operators/pool_op.cc @@ -64,6 +64,7 @@ void PoolOp::InferShape(framework::InferShapeContext *ctx) const { framework::OpKernelType PoolOp::GetExpectedKernelType( const framework::ExecutionContext &ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); + use_cudnn &= platform::dynload::HasCUDNN(); framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; @@ -88,6 +89,7 @@ void PoolOpGrad::InferShape(framework::InferShapeContext *ctx) const { framework::OpKernelType PoolOpGrad::GetExpectedKernelType( const framework::ExecutionContext &ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); + use_cudnn &= platform::dynload::HasCUDNN(); framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; diff --git a/paddle/operators/pool_op.h b/paddle/operators/pool_op.h index c3d82ecbdeb..e0668f1360d 100644 --- a/paddle/operators/pool_op.h +++ b/paddle/operators/pool_op.h @@ -18,6 +18,7 @@ limitations under the License. */ #include "paddle/framework/op_registry.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/pooling.h" +#include "paddle/platform/dynload/cudnn.h" namespace paddle { namespace operators { diff --git a/paddle/platform/dynload/cudnn.cc b/paddle/platform/dynload/cudnn.cc index 701f6240fef..fafdf5e78a9 100644 --- a/paddle/platform/dynload/cudnn.cc +++ b/paddle/platform/dynload/cudnn.cc @@ -57,6 +57,10 @@ void EnforceCUDNNLoaded(const char* fn_name) { bool HasCUDNN() { return true; } #endif +#ifndef PADDLE_WITH_CUDA +bool HasCUDNN() { return false; } +#endif + } // namespace dynload } // namespace platform } // namespace paddle -- GitLab From 9de18095901fc1e54b7683ee4a26a1ee4adbcff7 Mon Sep 17 00:00:00 2001 From: Yancey1989 Date: Mon, 15 Jan 2018 20:14:10 +0800 Subject: [PATCH 0273/2305] fluid distributed on CUDA place --- paddle/framework/tensor_util.h | 5 ++--- paddle/operators/detail/grpc_server.cc | 13 +++++++++---- paddle/operators/detail/grpc_server.h | 5 ++++- paddle/operators/recv_op.cc | 7 ++++--- paddle/operators/send_op.cc | 6 ++++-- 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/paddle/framework/tensor_util.h b/paddle/framework/tensor_util.h index 091b63bf0f9..b49c6144998 100644 --- a/paddle/framework/tensor_util.h +++ b/paddle/framework/tensor_util.h @@ -315,9 +315,8 @@ inline void DeserializeFromStream(std::istream& is, Tensor* tensor, desc.data_type(), DeserializedDataFunctor(&buf, &cpu_tensor, ctx.GetPlace())); is.read(static_cast(buf), cpu_tensor.memory_size()); - auto cpu_place = new platform::CPUPlace(); - framework::Copy(cpu_tensor, *cpu_place, dev_ctx, tensor); - delete cpu_place; + auto dst_place = dev_ctx.GetPlace(); + framework::Copy(cpu_tensor, dst_place, dev_ctx, tensor); #else PADDLE_THROW("Unexpected branch"); #endif diff --git a/paddle/operators/detail/grpc_server.cc b/paddle/operators/detail/grpc_server.cc index e8d561a57ff..0b2defdf226 100644 --- a/paddle/operators/detail/grpc_server.cc +++ b/paddle/operators/detail/grpc_server.cc @@ -74,8 +74,12 @@ class RequestSend final : public RequestBase { class RequestGet final : public RequestBase { public: explicit RequestGet(sendrecv::SendRecvService::AsyncService* service, - grpc::ServerCompletionQueue* cq, framework::Scope* scope) - : RequestBase(service, cq), responder_(&ctx_), scope_(scope) { + grpc::ServerCompletionQueue* cq, framework::Scope* scope, + const platform::DeviceContext* dev_ctx) + : RequestBase(service, cq), + responder_(&ctx_), + scope_(scope), + dev_ctx_(dev_ctx) { service_->RequestGetVariable(&ctx_, &request_, &responder_, cq_, cq_, this); } @@ -85,7 +89,7 @@ class RequestGet final : public RequestBase { // proc request. std::string var_name = request_.varname(); auto* var = scope_->FindVar(var_name); - SerializeToMessage(var_name, var, platform::CPUDeviceContext(), &reply_); + SerializeToMessage(var_name, var, *dev_ctx_, &reply_); // TODO(gongwb): check var's info. responder_.Finish(reply_, grpc::Status::OK, this); } @@ -95,6 +99,7 @@ class RequestGet final : public RequestBase { sendrecv::VariableMessage reply_; ServerAsyncResponseWriter responder_; framework::Scope* scope_; + const platform::DeviceContext* dev_ctx_; }; void AsyncGRPCServer::RunSyncUpdate() { @@ -155,7 +160,7 @@ void AsyncGRPCServer::TryToRegisterNewGetOne() { if (is_shut_down_) { return; } - RequestGet* get = new RequestGet(&service_, cq_get_.get(), scope_); + RequestGet* get = new RequestGet(&service_, cq_get_.get(), scope_, dev_ctx_); VLOG(4) << "create Requestget status:" << get->Status(); } diff --git a/paddle/operators/detail/grpc_server.h b/paddle/operators/detail/grpc_server.h index 041fe05b2e9..40655881e82 100644 --- a/paddle/operators/detail/grpc_server.h +++ b/paddle/operators/detail/grpc_server.h @@ -37,7 +37,7 @@ class RequestBase; class AsyncGRPCServer final : public sendrecv::SendRecvService::Service { public: - explicit AsyncGRPCServer(std::string address) { address_ = address; } + explicit AsyncGRPCServer(const std::string &address) : address_(address) {} void RunSyncUpdate(); @@ -47,6 +47,8 @@ class AsyncGRPCServer final : public sendrecv::SendRecvService::Service { void SetScope(framework::Scope *scope) { scope_ = scope; } + void SetDevCtx(const platform::DeviceContext *dev_ctx) { dev_ctx_ = dev_ctx; } + const MessageWithName Get() { return this->var_recv_queue_.Pop(); } void Push(const MessageWithName &msg) { this->var_recv_queue_.Push(msg); } @@ -74,6 +76,7 @@ class AsyncGRPCServer final : public sendrecv::SendRecvService::Service { std::string address_; framework::Scope *scope_; + const platform::DeviceContext *dev_ctx_; // received variable from RPC, operators fetch variable from this queue. SimpleBlockQueue var_recv_queue_; diff --git a/paddle/operators/recv_op.cc b/paddle/operators/recv_op.cc index 55b33343af4..dad5cf372e4 100644 --- a/paddle/operators/recv_op.cc +++ b/paddle/operators/recv_op.cc @@ -87,7 +87,11 @@ class RecvOp : public framework::OperatorBase { const platform::Place &dev_place) const override { // FIXME(typhoonzero): no new scopes for every run. framework::Scope &recv_scope = scope.NewScope(); + platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); + auto &dev_ctx = *pool.Get(dev_place); + rpc_service_->SetScope(&recv_scope); + rpc_service_->SetDevCtx(&dev_ctx); auto param_list = Attr>("ParamList"); auto grad_list = Attr>("GradList"); auto trainer_count = Attr("Trainers"); @@ -134,9 +138,6 @@ class RecvOp : public framework::OperatorBase { } auto *var = recv_scope.Var(grad_var_name); - platform::DeviceContextPool &pool = - platform::DeviceContextPool::Instance(); - auto &dev_ctx = *pool.Get(dev_place); detail::DeserializeFromMessage(v.second, dev_ctx, var); } diff --git a/paddle/operators/send_op.cc b/paddle/operators/send_op.cc index 4d145250bdc..a82c3430e9d 100644 --- a/paddle/operators/send_op.cc +++ b/paddle/operators/send_op.cc @@ -33,13 +33,15 @@ class SendOp : public framework::OperatorBase { : OperatorBase(type, inputs, outputs, attrs) {} void Run(const framework::Scope& scope, - const platform::Place& dev_place) const override { + const platform::Place& place) const override { auto ins = Inputs("X"); auto outs = Outputs("Out"); std::vector epmap = Attr>("epmap"); // FIXME(gongwb): DeviceContext? - auto ctx = platform::CPUDeviceContext(); + // auto ctx = platform::CPUDeviceContext(); + platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance(); + auto& ctx = *pool.Get(place); for (size_t i = 0; i < ins.size(); i++) { client_.AsyncSendVariable(epmap[i], ctx, scope, ins[i]); } -- GitLab From 329f1e0f79001f32aadc4e6344be06e5ba00ef63 Mon Sep 17 00:00:00 2001 From: Yancey1989 Date: Mon, 15 Jan 2018 20:18:34 +0800 Subject: [PATCH 0274/2305] add some comment --- paddle/operators/recv_op.cc | 1 + paddle/operators/send_op.cc | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/paddle/operators/recv_op.cc b/paddle/operators/recv_op.cc index dad5cf372e4..978ce92d241 100644 --- a/paddle/operators/recv_op.cc +++ b/paddle/operators/recv_op.cc @@ -90,6 +90,7 @@ class RecvOp : public framework::OperatorBase { platform::DeviceContextPool &pool = platform::DeviceContextPool::Instance(); auto &dev_ctx = *pool.Get(dev_place); + // FIXME(Yancey1989): initialize rpc server with laze mode. rpc_service_->SetScope(&recv_scope); rpc_service_->SetDevCtx(&dev_ctx); auto param_list = Attr>("ParamList"); diff --git a/paddle/operators/send_op.cc b/paddle/operators/send_op.cc index a82c3430e9d..1e6d659180b 100644 --- a/paddle/operators/send_op.cc +++ b/paddle/operators/send_op.cc @@ -38,8 +38,6 @@ class SendOp : public framework::OperatorBase { auto outs = Outputs("Out"); std::vector epmap = Attr>("epmap"); - // FIXME(gongwb): DeviceContext? - // auto ctx = platform::CPUDeviceContext(); platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance(); auto& ctx = *pool.Get(place); for (size_t i = 0; i < ins.size(); i++) { -- GitLab From 1e6e5ac64c27d105a1176f3876e4415040fae0a4 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 15 Jan 2018 20:34:18 +0800 Subject: [PATCH 0275/2305] add unit test --- .../v2/fluid/tests/test_elementwise_max_op.py | 107 ++++++++++++++++++ .../v2/fluid/tests/test_elementwise_min_op.py | 107 ++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 python/paddle/v2/fluid/tests/test_elementwise_max_op.py create mode 100644 python/paddle/v2/fluid/tests/test_elementwise_min_op.py diff --git a/python/paddle/v2/fluid/tests/test_elementwise_max_op.py b/python/paddle/v2/fluid/tests/test_elementwise_max_op.py new file mode 100644 index 00000000000..52bd123d802 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_elementwise_max_op.py @@ -0,0 +1,107 @@ +import unittest +import numpy as np +from op_test import OpTest + + +class TestElementwiseOp(OpTest): + def setUp(self): + self.op_type = "elementwise_max" + # If x and y have the same value, the max() is not differentiable. + # So we generate test data by the following method + # to avoid them being too close to each other. + x = np.random.uniform(0.1, 1, [13, 17]).astype("float32") + sgn = np.random.choice([-1, 1], [13, 17]).astype("float32") + y = x + sgn * np.random.uniform(0.1, 1, [13, 17]).astype("float32") + self.inputs = {'X': x, 'Y': y} + self.outputs = {'Out': np.maximum(self.inputs['X'], self.inputs['Y'])} + + def test_check_output(self): + self.check_output() + + def test_check_grad_normal(self): + self.check_grad(['X', 'Y'], 'Out', max_relative_error=0.005) + + def test_check_grad_ingore_x(self): + self.check_grad( + ['Y'], 'Out', max_relative_error=0.005, no_grad_set=set("X")) + + def test_check_grad_ingore_y(self): + self.check_grad( + ['X'], 'Out', max_relative_error=0.005, no_grad_set=set('Y')) + + +class TestElementwiseMaxOp_Vector(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_max" + x = np.random.random((32, )).astype("float32") + sgn = np.random.choice([-1, 1], (32, )).astype("float32") + y = x + sgn * np.random.uniform(0.1, 1, (32, )).astype("float32") + self.inputs = {'X': x, 'Y': y} + self.outputs = {'Out': np.maximum(self.inputs['X'], self.inputs['Y'])} + + +class TestElementwiseMaxOp_broadcast_0(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_max" + x = np.random.uniform(0.5, 1, (2, 3, 4)).astype(np.float32) + sgn = np.random.choice([-1, 1], (2, )).astype(np.float32) + y = x[:, 0, 0] + sgn * \ + np.random.uniform(1, 2, (2, )).astype(np.float32) + self.inputs = {'X': x, 'Y': y} + + self.attrs = {'axis': 0} + self.outputs = { + 'Out': + np.maximum(self.inputs['X'], self.inputs['Y'].reshape(2, 1, 1)) + } + + +class TestElementwiseMaxOp_broadcast_1(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_max" + x = np.random.uniform(0.5, 1, (2, 3, 4)).astype(np.float32) + sgn = np.random.choice([-1, 1], (3, )).astype(np.float32) + y = x[0, :, 0] + sgn * \ + np.random.uniform(1, 2, (3, )).astype(np.float32) + self.inputs = {'X': x, 'Y': y} + + self.attrs = {'axis': 1} + self.outputs = { + 'Out': + np.maximum(self.inputs['X'], self.inputs['Y'].reshape(1, 3, 1)) + } + + +class TestElementwiseMaxOp_broadcast_2(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_max" + x = np.random.uniform(0.5, 1, (2, 3, 4)).astype(np.float32) + sgn = np.random.choice([-1, 1], (4, )).astype(np.float32) + y = x[0, 0, :] + sgn * \ + np.random.uniform(1, 2, (4, )).astype(np.float32) + self.inputs = {'X': x, 'Y': y} + + self.outputs = { + 'Out': + np.maximum(self.inputs['X'], self.inputs['Y'].reshape(1, 1, 4)) + } + + +class TestElementwiseMaxOp_broadcast_3(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_max" + x = np.random.uniform(0.5, 1, (2, 3, 4, 5)).astype(np.float32) + sgn = np.random.choice([-1, 1], (3, 4)).astype(np.float32) + y = x[0, :, :, 0] + sgn * \ + np.random.uniform(1, 2, (3, 4)).astype(np.float32) + self.inputs = {'X': x, 'Y': y} + + self.attrs = {'axis': 1} + self.outputs = { + 'Out': + np.maximum(self.inputs['X'], self.inputs['Y'].reshape(1, 3, 4, 1)) + } + + +if __name__ == '__main__': + unittest.main() diff --git a/python/paddle/v2/fluid/tests/test_elementwise_min_op.py b/python/paddle/v2/fluid/tests/test_elementwise_min_op.py new file mode 100644 index 00000000000..5ff5a400139 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_elementwise_min_op.py @@ -0,0 +1,107 @@ +import unittest +import numpy as np +from op_test import OpTest + + +class TestElementwiseOp(OpTest): + def setUp(self): + self.op_type = "elementwise_min" + # If x and y have the same value, the max() is not differentiable. + # So we generate test data by the following method + # to avoid them being too close to each other. + x = np.random.uniform(0.1, 1, [13, 17]).astype("float32") + sgn = np.random.choice([-1, 1], [13, 17]).astype("float32") + y = x + sgn * np.random.uniform(0.1, 1, [13, 17]).astype("float32") + self.inputs = {'X': x, 'Y': y} + self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])} + + def test_check_output(self): + self.check_output() + + def test_check_grad_normal(self): + self.check_grad(['X', 'Y'], 'Out', max_relative_error=0.005) + + def test_check_grad_ingore_x(self): + self.check_grad( + ['Y'], 'Out', max_relative_error=0.005, no_grad_set=set("X")) + + def test_check_grad_ingore_y(self): + self.check_grad( + ['X'], 'Out', max_relative_error=0.005, no_grad_set=set('Y')) + + +class TestElementwiseMaxOp_Vector(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_min" + x = np.random.random((32, )).astype("float32") + sgn = np.random.choice([-1, 1], (32, )).astype("float32") + y = x + sgn * np.random.uniform(0.1, 1, (32, )).astype("float32") + self.inputs = {'X': x, 'Y': y} + self.outputs = {'Out': np.minimum(self.inputs['X'], self.inputs['Y'])} + + +class TestElementwiseMaxOp_broadcast_0(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_min" + x = np.random.uniform(0.5, 1, (2, 3, 4)).astype(np.float32) + sgn = np.random.choice([-1, 1], (2, )).astype(np.float32) + y = x[:, 0, 0] + sgn * \ + np.random.uniform(1, 2, (2, )).astype(np.float32) + self.inputs = {'X': x, 'Y': y} + + self.attrs = {'axis': 0} + self.outputs = { + 'Out': + np.minimum(self.inputs['X'], self.inputs['Y'].reshape(2, 1, 1)) + } + + +class TestElementwiseMaxOp_broadcast_1(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_min" + x = np.random.uniform(0.5, 1, (2, 3, 4)).astype(np.float32) + sgn = np.random.choice([-1, 1], (3, )).astype(np.float32) + y = x[0, :, 0] + sgn * \ + np.random.uniform(1, 2, (3, )).astype(np.float32) + self.inputs = {'X': x, 'Y': y} + + self.attrs = {'axis': 1} + self.outputs = { + 'Out': + np.minimum(self.inputs['X'], self.inputs['Y'].reshape(1, 3, 1)) + } + + +class TestElementwiseMaxOp_broadcast_2(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_min" + x = np.random.uniform(0.5, 1, (2, 3, 4)).astype(np.float32) + sgn = np.random.choice([-1, 1], (4, )).astype(np.float32) + y = x[0, 0, :] + sgn * \ + np.random.uniform(1, 2, (4, )).astype(np.float32) + self.inputs = {'X': x, 'Y': y} + + self.outputs = { + 'Out': + np.minimum(self.inputs['X'], self.inputs['Y'].reshape(1, 1, 4)) + } + + +class TestElementwiseMaxOp_broadcast_3(TestElementwiseOp): + def setUp(self): + self.op_type = "elementwise_min" + x = np.random.uniform(0.5, 1, (2, 3, 4, 5)).astype(np.float32) + sgn = np.random.choice([-1, 1], (3, 4)).astype(np.float32) + y = x[0, :, :, 0] + sgn * \ + np.random.uniform(1, 2, (3, 4)).astype(np.float32) + self.inputs = {'X': x, 'Y': y} + + self.attrs = {'axis': 1} + self.outputs = { + 'Out': + np.minimum(self.inputs['X'], self.inputs['Y'].reshape(1, 3, 4, 1)) + } + + +if __name__ == '__main__': + unittest.main() -- GitLab From c6d7ad368931f51766030bf9dc777106b1bdce83 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 15 Jan 2018 20:49:26 +0800 Subject: [PATCH 0276/2305] fix typo --- python/paddle/v2/fluid/tests/test_elementwise_min_op.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/tests/test_elementwise_min_op.py b/python/paddle/v2/fluid/tests/test_elementwise_min_op.py index 5ff5a400139..eeafab5b188 100644 --- a/python/paddle/v2/fluid/tests/test_elementwise_min_op.py +++ b/python/paddle/v2/fluid/tests/test_elementwise_min_op.py @@ -6,7 +6,7 @@ from op_test import OpTest class TestElementwiseOp(OpTest): def setUp(self): self.op_type = "elementwise_min" - # If x and y have the same value, the max() is not differentiable. + # If x and y have the same value, the min() is not differentiable. # So we generate test data by the following method # to avoid them being too close to each other. x = np.random.uniform(0.1, 1, [13, 17]).astype("float32") -- GitLab From ee8e5374d8cf079e389b79aef0ebf871c7b55545 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Mon, 15 Jan 2018 20:53:29 +0800 Subject: [PATCH 0277/2305] add max min layer --- python/paddle/v2/fluid/layers/ops.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/paddle/v2/fluid/layers/ops.py b/python/paddle/v2/fluid/layers/ops.py index 51a85dbbd33..e60daa4f9f3 100644 --- a/python/paddle/v2/fluid/layers/ops.py +++ b/python/paddle/v2/fluid/layers/ops.py @@ -42,6 +42,8 @@ __all__ = [ 'elementwise_div', 'elementwise_sub', 'elementwise_mul', + 'elementwise_max', + 'elementwise_min', 'clip', 'sequence_softmax', ] + __activations__ -- GitLab From bcfb82d33e431d621317f97d3c0703d9b002a8ee Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Mon, 15 Jan 2018 20:55:48 +0800 Subject: [PATCH 0278/2305] dist train support split selectedrows --- .../paddle/v2/fluid/distribute_transpiler.py | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index d17f9815cca..00fe3e68c90 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -59,6 +59,51 @@ def split_dense_variable(var_list, return blocks +def split_selected_rows(var, + pserver_count, + min_block_size=1024, + max_block_size=1048576): + assert ((len(var.shape)) <= 1) + + split_count = pserver_count + indices = var.desc.selected_rows().dims() + var_width = reduce(lambda x, y: x * y, var.shape[1:]) + row_count = len(indices) + rows_per_block = 1 + if var_width < min_block_size: + rows_per_block = 1 + split_count = row_count + else: + rows_per_block = row_count / pserver_count + if not rows_per_block % pserver_count: + rows_per_block += 1 + split_count = row_count / rows_per_block + if not row_count % rows_per_block: + split_count += 1 + blocks = [] + for block_id in xrange(split_count): + curr_block_rows = min(rows_per_block, + row_count - (block_id * rows_per_block)) + block = VarBlock(var.name, block_id, curr_block_rows) + blocks.append(block) + return blocks + + +def split_variable(var_list, + pserver_count, + min_block_size=1024, + max_block_size=1048576): + for var in var_list: + if var.type == core.VarDesc.VarType.LOD_TENSOR: + split_dense_variable(var_list, pserver_count, min_block_size, + max_block_size) + elif var.type == core.VarDesc.VarType.SELECTED_ROWS: + split_selected_rows(var_list, pserver_count, min_block_size, + max_block_size) + else: + raise TypeError("variable must be lodtensor or selected rows") + + class DistributeTranspiler: def transpile(self, optimize_ops, -- GitLab From 251c6032fb6bce72751b6b32e34368a17b407e6e Mon Sep 17 00:00:00 2001 From: chengduoZH Date: Mon, 15 Jan 2018 19:49:14 +0800 Subject: [PATCH 0279/2305] set use_cudnn as default --- paddle/operators/conv_op.cc | 8 ++++++-- paddle/operators/conv_op.h | 1 - paddle/operators/conv_transpose_op.cc | 8 ++++++-- paddle/operators/conv_transpose_op.h | 1 - paddle/operators/pool_op.cc | 8 ++++++-- paddle/operators/pool_op.h | 1 - paddle/platform/dynload/cudnn.cc | 4 ---- python/paddle/v2/fluid/layers/nn.py | 6 +++--- python/paddle/v2/fluid/nets.py | 6 +++--- 9 files changed, 24 insertions(+), 19 deletions(-) diff --git a/paddle/operators/conv_op.cc b/paddle/operators/conv_op.cc index a6d3ebb4f52..9ae3e872812 100644 --- a/paddle/operators/conv_op.cc +++ b/paddle/operators/conv_op.cc @@ -70,7 +70,9 @@ void ConvOp::InferShape(framework::InferShapeContext* ctx) const { framework::OpKernelType ConvOp::GetExpectedKernelType( const framework::ExecutionContext& ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); - use_cudnn &= platform::dynload::HasCUDNN(); + if (paddle::platform::is_cpu_place(ctx.GetPlace())) { + use_cudnn = false; + } framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; @@ -284,7 +286,9 @@ void ConvOpGrad::InferShape(framework::InferShapeContext* ctx) const { framework::OpKernelType ConvOpGrad::GetExpectedKernelType( const framework::ExecutionContext& ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); - use_cudnn &= platform::dynload::HasCUDNN(); + if (paddle::platform::is_cpu_place(ctx.GetPlace())) { + use_cudnn = false; + } framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; diff --git a/paddle/operators/conv_op.h b/paddle/operators/conv_op.h index 2a6fad65826..5a8933e7915 100644 --- a/paddle/operators/conv_op.h +++ b/paddle/operators/conv_op.h @@ -19,7 +19,6 @@ limitations under the License. */ #include "paddle/operators/math/im2col.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/vol2col.h" -#include "paddle/platform/dynload/cudnn.h" namespace paddle { namespace operators { diff --git a/paddle/operators/conv_transpose_op.cc b/paddle/operators/conv_transpose_op.cc index 53153c02963..46f79b1701f 100644 --- a/paddle/operators/conv_transpose_op.cc +++ b/paddle/operators/conv_transpose_op.cc @@ -61,7 +61,9 @@ void ConvTransposeOp::InferShape(framework::InferShapeContext* ctx) const { framework::OpKernelType ConvTransposeOp::GetExpectedKernelType( const framework::ExecutionContext& ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); - use_cudnn &= platform::dynload::HasCUDNN(); + if (paddle::platform::is_cpu_place(ctx.GetPlace())) { + use_cudnn = false; + } framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; @@ -264,7 +266,9 @@ void ConvTransposeOpGrad::InferShape(framework::InferShapeContext* ctx) const { framework::OpKernelType ConvTransposeOpGrad::GetExpectedKernelType( const framework::ExecutionContext& ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); - use_cudnn &= platform::dynload::HasCUDNN(); + if (paddle::platform::is_cpu_place(ctx.GetPlace())) { + use_cudnn = false; + } framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; diff --git a/paddle/operators/conv_transpose_op.h b/paddle/operators/conv_transpose_op.h index f43d652fe1f..a42ade41b16 100644 --- a/paddle/operators/conv_transpose_op.h +++ b/paddle/operators/conv_transpose_op.h @@ -19,7 +19,6 @@ limitations under the License. */ #include "paddle/operators/math/im2col.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/vol2col.h" -#include "paddle/platform/dynload/cudnn.h" namespace paddle { namespace operators { diff --git a/paddle/operators/pool_op.cc b/paddle/operators/pool_op.cc index e0f9ea7aa0d..648a1dfb56a 100644 --- a/paddle/operators/pool_op.cc +++ b/paddle/operators/pool_op.cc @@ -64,7 +64,9 @@ void PoolOp::InferShape(framework::InferShapeContext *ctx) const { framework::OpKernelType PoolOp::GetExpectedKernelType( const framework::ExecutionContext &ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); - use_cudnn &= platform::dynload::HasCUDNN(); + if (paddle::platform::is_cpu_place(ctx.GetPlace())) { + use_cudnn = false; + } framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; @@ -89,7 +91,9 @@ void PoolOpGrad::InferShape(framework::InferShapeContext *ctx) const { framework::OpKernelType PoolOpGrad::GetExpectedKernelType( const framework::ExecutionContext &ctx) const { bool use_cudnn = ctx.Attr("use_cudnn"); - use_cudnn &= platform::dynload::HasCUDNN(); + if (paddle::platform::is_cpu_place(ctx.GetPlace())) { + use_cudnn = false; + } framework::LibraryType library_; if (use_cudnn) { library_ = framework::LibraryType::kCUDNN; diff --git a/paddle/operators/pool_op.h b/paddle/operators/pool_op.h index e0668f1360d..c3d82ecbdeb 100644 --- a/paddle/operators/pool_op.h +++ b/paddle/operators/pool_op.h @@ -18,7 +18,6 @@ limitations under the License. */ #include "paddle/framework/op_registry.h" #include "paddle/operators/math/math_function.h" #include "paddle/operators/math/pooling.h" -#include "paddle/platform/dynload/cudnn.h" namespace paddle { namespace operators { diff --git a/paddle/platform/dynload/cudnn.cc b/paddle/platform/dynload/cudnn.cc index fafdf5e78a9..701f6240fef 100644 --- a/paddle/platform/dynload/cudnn.cc +++ b/paddle/platform/dynload/cudnn.cc @@ -57,10 +57,6 @@ void EnforceCUDNNLoaded(const char* fn_name) { bool HasCUDNN() { return true; } #endif -#ifndef PADDLE_WITH_CUDA -bool HasCUDNN() { return false; } -#endif - } // namespace dynload } // namespace platform } // namespace paddle diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index df2233089c2..0cfa0110363 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -660,7 +660,7 @@ def conv2d(input, groups=None, param_attr=None, bias_attr=None, - use_cudnn=False, + use_cudnn=True, act=None): """ **Convlution2D Layer** @@ -938,7 +938,7 @@ def pool2d(input, pool_stride=None, pool_padding=None, global_pooling=False, - use_cudnn=False): + use_cudnn=True): """ This function adds the operator for pooling in 2 dimensions, using the pooling configurations mentioned in input parameters. @@ -1088,7 +1088,7 @@ def conv2d_transpose(input, stride=None, dilation=None, param_attr=None, - use_cudnn=False): + use_cudnn=True): """ The transpose of conv2d layer. diff --git a/python/paddle/v2/fluid/nets.py b/python/paddle/v2/fluid/nets.py index 8ac58fd7334..327d2ff2da1 100644 --- a/python/paddle/v2/fluid/nets.py +++ b/python/paddle/v2/fluid/nets.py @@ -14,7 +14,7 @@ def simple_img_conv_pool(input, act, param_attr=None, pool_type='max', - use_cudnn=False): + use_cudnn=True): conv_out = layers.conv2d( input=input, num_filters=num_filters, @@ -41,10 +41,10 @@ def img_conv_group(input, param_attr=None, conv_with_batchnorm=False, conv_batchnorm_drop_rate=None, - conv_use_cudnn=False, + conv_use_cudnn=True, pool_stride=1, pool_type=None, - pool_use_cudnn=False): + pool_use_cudnn=True): """ Image Convolution Group, Used for vgg net. """ -- GitLab From 373f8ba036ff60d6781c9ec2717102336de89b0f Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Mon, 15 Jan 2018 21:03:13 +0800 Subject: [PATCH 0280/2305] add v2 dist benchmark vgg --- benchmark/cluster/v2/Dockerfile | 4 + benchmark/cluster/v2/pserver.yaml | 64 +++++++++++++++ benchmark/cluster/v2/reader.py | 56 +++++++++++++ benchmark/cluster/v2/trainer.yaml | 63 +++++++++++++++ benchmark/cluster/v2/vgg16.py | 125 ++++++++++++++++++++++++++++++ 5 files changed, 312 insertions(+) create mode 100644 benchmark/cluster/v2/Dockerfile create mode 100644 benchmark/cluster/v2/pserver.yaml create mode 100644 benchmark/cluster/v2/reader.py create mode 100644 benchmark/cluster/v2/trainer.yaml create mode 100644 benchmark/cluster/v2/vgg16.py diff --git a/benchmark/cluster/v2/Dockerfile b/benchmark/cluster/v2/Dockerfile new file mode 100644 index 00000000000..c52acd51a20 --- /dev/null +++ b/benchmark/cluster/v2/Dockerfile @@ -0,0 +1,4 @@ +FROM registry.baidu.com/paddlepaddle/rawjob +RUN mkdir -p /workspace && mkdir -p /root/.cache/paddle/dataset/flowers/ +ADD vgg16.py reader.py /workspace/ +ADD 102flowers.tgz imagelabels.mat setid.mat /root/.cache/paddle/dataset/flowers/ diff --git a/benchmark/cluster/v2/pserver.yaml b/benchmark/cluster/v2/pserver.yaml new file mode 100644 index 00000000000..ed1671bbbd4 --- /dev/null +++ b/benchmark/cluster/v2/pserver.yaml @@ -0,0 +1,64 @@ +apiVersion: extensions/v1beta1 +kind: ReplicaSet +metadata: + name: vgg16job-pserver +spec: + replicas: 10 + template: + metadata: + labels: + paddle-job-pserver: vgg16job + spec: + hostNetwork: true + imagePullSecrets: + - name: job-registry-secret + containers: + - name: pserver + image: "registry.baidu.com/paddlepaddle/rawjob:vgg16" + imagePullPolicy: Always + ports: + - name: jobport-30236 + containerPort: 30236 + env: + - name: PADDLE_JOB_NAME + value: vgg16job + - name: TRAINERS + value: "20" + - name: PSERVERS + value: "10" + - name: TOPOLOGY + value: "" + - name: ENTRY + value: "python train.py" + - name: TRAINER_PACKAGE + value: "/workspace" + - name: PADDLE_INIT_PORT + value: "30236" + - name: PADDLE_INIT_NICS + value: "xgbe0" + - name: PADDLE_INIT_TRAINER_COUNT + value: "1" + - name: PADDLE_INIT_PORTS_NUM + value: "1" + - name: PADDLE_INIT_PORTS_NUM_FOR_SPARSE + value: "1" + - name: PADDLE_INIT_NUM_GRADIENT_SERVERS + value: "20" + - name: PADDLE_INIT_NUM_PASSES + value: "1" + - name: PADDLE_INIT_USE_GPU + value: "0" + - name: LD_LIBRARY_PATH + value: "/usr/local/nvidia/lib64" + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: "metadata.namespace" + command: ["paddle_k8s", "start_pserver"] + resources: + requests: + memory: 10Gi + cpu: 4 + limits: + memory: 10Gi + cpu: 4 diff --git a/benchmark/cluster/v2/reader.py b/benchmark/cluster/v2/reader.py new file mode 100644 index 00000000000..a5a2d54841c --- /dev/null +++ b/benchmark/cluster/v2/reader.py @@ -0,0 +1,56 @@ +import random +from paddle.v2.image import load_and_transform +import paddle.v2 as paddle +from multiprocessing import cpu_count + + +def train_mapper(sample): + ''' + map image path to type needed by model input layer for the training set + ''' + img, label = sample + img = paddle.image.load_image(img) + img = paddle.image.simple_transform(img, 256, 224, True) + return img.flatten().astype('float32'), label + + +def test_mapper(sample): + ''' + map image path to type needed by model input layer for the test set + ''' + img, label = sample + img = paddle.image.load_image(img) + img = paddle.image.simple_transform(img, 256, 224, True) + return img.flatten().astype('float32'), label + + +def train_reader(train_list, buffered_size=1024): + def reader(): + with open(train_list, 'r') as f: + lines = [line.strip() for line in f] + for line in lines: + img_path, lab = line.strip().split('\t') + yield img_path, int(lab) + + return paddle.reader.xmap_readers(train_mapper, reader, + cpu_count(), buffered_size) + + +def test_reader(test_list, buffered_size=1024): + def reader(): + with open(test_list, 'r') as f: + lines = [line.strip() for line in f] + for line in lines: + img_path, lab = line.strip().split('\t') + yield img_path, int(lab) + + return paddle.reader.xmap_readers(test_mapper, reader, + cpu_count(), buffered_size) + + +if __name__ == '__main__': + #for im in train_reader('train.list'): + # print len(im[0]) + #for im in train_reader('test.list'): + # print len(im[0]) + paddle.dataset.flowers.train() diff --git a/benchmark/cluster/v2/trainer.yaml b/benchmark/cluster/v2/trainer.yaml new file mode 100644 index 00000000000..33c95df365a --- /dev/null +++ b/benchmark/cluster/v2/trainer.yaml @@ -0,0 +1,63 @@ +apiVersion: batch/v1 +kind: Job +metadata: + name: vgg16job-trainer +spec: + parallelism: 20 + completions: 20 + template: + metadata: + labels: + paddle-job: vgg16job + spec: + imagePullSecrets: + - name: job-registry-secret + hostNetwork: true + containers: + - name: trainer + image: "registry.baidu.com/paddlepaddle/rawjob:vgg16" + imagePullPolicy: Always + command: ["paddle_k8s", "start_trainer", "v2"] + env: + - name: PADDLE_JOB_NAME + value: vgg16job + - name: TRAINERS + value: "20" + - name: PSERVERS + value: "10" + - name: TOPOLOGY + value: "" + - name: ENTRY + value: "cd /workspace && python /workspace/vgg16.py" + - name: TRAINER_PACKAGE + value: "/workspace" + - name: PADDLE_INIT_PORT + value: "30236" + - name: PADDLE_INIT_NICS + value: "xgbe0" + - name: PADDLE_INIT_TRAINER_COUNT + value: "1" + - name: PADDLE_INIT_PORTS_NUM + value: "1" + - name: PADDLE_INIT_PORTS_NUM_FOR_SPARSE + value: "1" + - name: PADDLE_INIT_NUM_GRADIENT_SERVERS + value: "20" + - name: PADDLE_INIT_NUM_PASSES + value: "1" + - name: PADDLE_INIT_USE_GPU + value: "0" + - name: LD_LIBRARY_PATH + value: "/usr/local/nvidia/lib64" + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: "metadata.namespace" + resources: + requests: + memory: 40Gi + cpu: 2 + limits: + memory: 40Gi + cpu: 2 + restartPolicy: Never diff --git a/benchmark/cluster/v2/vgg16.py b/benchmark/cluster/v2/vgg16.py new file mode 100644 index 00000000000..699fc07628f --- /dev/null +++ b/benchmark/cluster/v2/vgg16.py @@ -0,0 +1,125 @@ +import gzip + +import paddle.v2.dataset.flowers as flowers +import paddle.v2 as paddle +import reader + +DATA_DIM = 3 * 224 * 224 # Use 3 * 331 * 331 or 3 * 299 * 299 for Inception-ResNet-v2. +CLASS_DIM = 102 +BATCH_SIZE = 128 + + +def vgg(input, nums, class_dim): + def conv_block(input, num_filter, groups, num_channels=None): + return paddle.networks.img_conv_group( + input=input, + num_channels=num_channels, + pool_size=2, + pool_stride=2, + conv_num_filter=[num_filter] * groups, + conv_filter_size=3, + conv_act=paddle.activation.Relu(), + pool_type=paddle.pooling.Max()) + + assert len(nums) == 5 + # the channel of input feature is 3 + conv1 = conv_block(input, 64, nums[0], 3) + conv2 = conv_block(conv1, 128, nums[1]) + conv3 = conv_block(conv2, 256, nums[2]) + conv4 = conv_block(conv3, 512, nums[3]) + conv5 = conv_block(conv4, 512, nums[4]) + + fc_dim = 4096 + fc1 = paddle.layer.fc(input=conv5, + size=fc_dim, + act=paddle.activation.Relu(), + layer_attr=paddle.attr.Extra(drop_rate=0.5)) + fc2 = paddle.layer.fc(input=fc1, + size=fc_dim, + act=paddle.activation.Relu(), + layer_attr=paddle.attr.Extra(drop_rate=0.5)) + out = paddle.layer.fc(input=fc2, + size=class_dim, + act=paddle.activation.Softmax()) + return out + + +def vgg13(input, class_dim): + nums = [2, 2, 2, 2, 2] + return vgg(input, nums, class_dim) + + +def vgg16(input, class_dim): + nums = [2, 2, 3, 3, 3] + return vgg(input, nums, class_dim) + + +def vgg19(input, class_dim): + nums = [2, 2, 4, 4, 4] + return vgg(input, nums, class_dim) + + +def main(): + paddle.init(use_gpu=True, trainer_count=1) + image = paddle.layer.data( + name="image", type=paddle.data_type.dense_vector(DATA_DIM)) + lbl = paddle.layer.data( + name="label", type=paddle.data_type.integer_value(CLASS_DIM)) + + extra_layers = None + learning_rate = 0.01 + out = vgg16(image, class_dim=CLASS_DIM) + cost = paddle.layer.classification_cost(input=out, label=lbl) + + # Create parameters + parameters = paddle.parameters.create(cost) + + # Create optimizer + optimizer = paddle.optimizer.Momentum( + momentum=0.9, + regularization=paddle.optimizer.L2Regularization(rate=0.0005 * + BATCH_SIZE), + learning_rate=learning_rate / BATCH_SIZE, + learning_rate_decay_a=0.1, + learning_rate_decay_b=128000 * 35, + learning_rate_schedule="discexp", ) + + train_reader = paddle.batch( + paddle.reader.shuffle( + flowers.train(), + # To use other data, replace the above line with: + # reader.train_reader('train.list'), + buf_size=1000), + batch_size=BATCH_SIZE) + test_reader = paddle.batch( + flowers.valid(), + # To use other data, replace the above line with: + # reader.test_reader('val.list'), + batch_size=BATCH_SIZE) + + # Create trainer + trainer = paddle.trainer.SGD(cost=cost, + parameters=parameters, + update_equation=optimizer, + extra_layers=extra_layers, + is_local=False) + + # End batch and end pass event handler + def event_handler(event): + if isinstance(event, paddle.event.EndIteration): + if event.batch_id % 1 == 0: + print "\nPass %d, Batch %d, Cost %f, %s" % ( + event.pass_id, event.batch_id, event.cost, event.metrics) + if isinstance(event, paddle.event.EndPass): + with gzip.open('params_pass_%d.tar.gz' % event.pass_id, 'w') as f: + trainer.save_parameter_to_tar(f) + + result = trainer.test(reader=test_reader) + print "\nTest with Pass %d, %s" % (event.pass_id, result.metrics) + + trainer.train( + reader=train_reader, num_passes=200, event_handler=event_handler) + + +if __name__ == '__main__': + main() -- GitLab From b9b75377a2db78651023707ef9b6a342c661eaf4 Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Mon, 15 Jan 2018 21:07:51 +0800 Subject: [PATCH 0281/2305] Feature/hooks (#7513) * add copyright hook * add copyright hook * refine copyright hook * "test copyright hook" * fix check style * fix ci --- .copyright.hook | 106 ++++++++++++++++++ .pre-commit-config.yaml | 8 ++ adversarial/advbox/attacks/base.py | 13 +++ adversarial/advbox/attacks/gradientsign.py | 13 +++ adversarial/advbox/models/base.py | 13 +++ adversarial/advbox/models/paddle.py | 13 +++ adversarial/fluid_mnist.py | 13 +++ adversarial/mnist_tutorial_fgsm.py | 13 +++ benchmark/paddle/image/alexnet.py | 13 +++ benchmark/paddle/image/googlenet.py | 13 +++ benchmark/paddle/image/provider.py | 13 +++ benchmark/paddle/image/resnet.py | 13 +++ .../paddle/image/smallnet_mnist_cifar.py | 13 +++ benchmark/paddle/image/vgg.py | 13 +++ benchmark/paddle/rnn/provider.py | 13 +++ benchmark/paddle/rnn/rnn.py | 13 +++ benchmark/tensorflow/image/alexnet.py | 13 +++ .../tensorflow/image/alexnet_multi_gpu.py | 13 +++ benchmark/tensorflow/image/googlenet.py | 13 +++ .../tensorflow/image/smallnet_mnist_cifar.py | 13 +++ benchmark/tensorflow/rnn/reader.py | 13 +++ benchmark/tensorflow/rnn/rnn.py | 13 +++ benchmark/tensorflow/rnn/rnn_multi_gpu.py | 13 +++ cmake/make_resource.py | 13 +++ doc/api/v1/data_provider/src/mnist_config.py | 13 +++ .../data_provider/src/mnist_provider.dict.py | 13 +++ .../data_provider/src/sentimental_config.py | 13 +++ .../data_provider/src/sentimental_provider.py | 13 +++ doc/faq/local/src/reduce_min_pool_size.py | 13 +++ doc/faq/local/src/word2vec_config.py | 13 +++ doc/faq/local/src/word2vec_dataprovider.py | 13 +++ doc/getstarted/concepts/src/train.py | 13 +++ .../cluster/src/k8s_train/start_paddle.py | 13 +++ .../cluster/src/word2vec/api_train_v2.py | 13 +++ .../src/word2vec/api_train_v2_cluster.py | 13 +++ go/pserver/client/c/test/test_mnist.py | 13 +++ go/pserver/client/c/test/test_train.py | 13 +++ paddle/api/test/testTrainConfig.py | 13 +++ .../examples/model_inference/common/common.h | 13 +++ .../model_inference/dense/merge_v2_model.py | 13 +++ .../model_inference/dense/mnist_v2.py | 13 +++ .../model_inference/dense/trainer_config.py | 13 +++ .../sequence/trainer_config.py | 13 +++ paddle/capi/tests/test_predict_network.py | 13 +++ paddle/cuda/src/avx_mathfun.h | 13 +++ paddle/framework/backward_test.cc | 26 ++--- paddle/framework/dim.h | 13 +++ paddle/framework/dim_test.cu | 13 +++ paddle/framework/eigen_test.cc | 13 +++ paddle/framework/lod_tensor_test.cc | 13 +++ paddle/framework/lod_tensor_test.cu | 13 +++ paddle/framework/tensor_test.cc | 13 +++ paddle/framework/tensor_util_test.cc | 13 +++ paddle/framework/variable.h | 13 +++ paddle/gserver/tests/img_conv_cudnn.py | 13 +++ paddle/gserver/tests/img_conv_exconv.py | 13 +++ paddle/gserver/tests/sequence_recurrent.py | 13 +++ .../tests/sequence_rnn_matched_inputs.py | 13 +++ .../tests/sequence_rnn_mixed_inputs.py | 13 +++ .../sequence_rnn_multi_unequalength_inputs.py | 13 +++ paddle/operators/math/math_function_test.cc | 13 +++ paddle/operators/math/math_function_test.cu | 13 +++ paddle/operators/net_op.cc | 13 +++ paddle/operators/net_op_test.cc | 13 +++ paddle/optimizer/lr_policy.h | 13 +++ paddle/optimizer/parameter_optimizer_test.cc | 13 +++ paddle/optimizer/serialization_test.cc | 13 +++ paddle/optimizer/tensor.h | 13 +++ paddle/platform/cpu_info_test.cc | 13 +++ paddle/platform/hostdevice.h | 13 +++ paddle/platform/place_test.cc | 13 +++ paddle/pybind/print_operators_doc.cc | 13 +++ paddle/scripts/cluster_train/paddle.py | 13 +++ paddle/scripts/cpplint.py | 13 +++ paddle/string/piece.cc | 13 +++ paddle/string/piece.h | 13 +++ paddle/string/piece_test.cc | 13 +++ paddle/string/printf.h | 13 +++ paddle/string/printf_test.cc | 13 +++ paddle/string/tinyformat/tinyformat.h | 13 +++ .../tests/simple_sparse_neural_network.py | 13 +++ .../tests/simple_sparse_neural_network_dp.py | 13 +++ paddle/utils/enable_virtualenv.py | 13 +++ proto/OptimizerConfig.proto | 13 +++ .../tests/configs/img_layers.py | 13 +++ .../tests/configs/img_trans_layers.py | 13 +++ .../tests/configs/last_first_seq.py | 13 +++ .../tests/configs/layer_activations.py | 13 +++ .../tests/configs/math_ops.py | 13 +++ .../tests/configs/projections.py | 13 +++ .../tests/configs/shared_fc.py | 13 +++ .../tests/configs/shared_gru.py | 13 +++ .../tests/configs/shared_lstm.py | 13 +++ .../tests/configs/simple_rnn_layers.py | 13 +++ .../tests/configs/test_BatchNorm3D.py | 13 +++ .../tests/configs/test_bi_grumemory.py | 13 +++ .../tests/configs/test_bilinear_interp.py | 13 +++ .../tests/configs/test_clip_layer.py | 13 +++ .../test_config_parser_for_non_file_config.py | 13 +++ .../tests/configs/test_conv3d_layer.py | 13 +++ .../tests/configs/test_cost_layers.py | 13 +++ .../configs/test_cost_layers_with_weight.py | 13 +++ .../tests/configs/test_crop.py | 13 +++ .../configs/test_cross_entropy_over_beam.py | 13 +++ .../tests/configs/test_deconv3d_layer.py | 13 +++ .../configs/test_detection_output_layer.py | 13 +++ .../tests/configs/test_dot_prod_layer.py | 13 +++ .../tests/configs/test_expand_layer.py | 13 +++ .../configs/test_factorization_machine.py | 13 +++ .../tests/configs/test_fc.py | 13 +++ .../tests/configs/test_gated_unit_layer.py | 13 +++ .../tests/configs/test_grumemory_layer.py | 13 +++ .../tests/configs/test_hsigmoid.py | 13 +++ .../configs/test_kmax_seq_socre_layer.py | 13 +++ .../tests/configs/test_l2_distance_layer.py | 13 +++ .../tests/configs/test_lstmemory_layer.py | 13 +++ .../tests/configs/test_maxout.py | 13 +++ .../tests/configs/test_multibox_loss_layer.py | 13 +++ .../tests/configs/test_multiplex_layer.py | 13 +++ .../tests/configs/test_ntm_layers.py | 13 +++ .../tests/configs/test_pad.py | 13 +++ .../tests/configs/test_pooling3D_layer.py | 13 +++ .../tests/configs/test_prelu_layer.py | 13 +++ .../tests/configs/test_print_layer.py | 13 +++ .../tests/configs/test_recursive_topology.py | 13 +++ .../tests/configs/test_repeat_layer.py | 13 +++ .../tests/configs/test_resize_layer.py | 13 +++ .../tests/configs/test_rnn_group.py | 13 +++ .../tests/configs/test_roi_pool_layer.py | 13 +++ .../tests/configs/test_row_conv.py | 13 +++ .../tests/configs/test_row_l2_norm_layer.py | 13 +++ .../tests/configs/test_scale_shift_layer.py | 13 +++ .../configs/test_scale_sub_region_layer.py | 13 +++ .../tests/configs/test_seq_concat_reshape.py | 13 +++ .../tests/configs/test_seq_slice_layer.py | 13 +++ .../tests/configs/test_sequence_pooling.py | 13 +++ .../tests/configs/test_smooth_l1.py | 13 +++ .../tests/configs/test_split_datasource.py | 13 +++ .../tests/configs/test_spp_layer.py | 13 +++ .../test_sub_nested_seq_select_layer.py | 13 +++ .../tests/configs/unused_layers.py | 13 +++ .../tests/configs/util_layers.py | 13 +++ python/paddle/utils/image_multiproc.py | 13 +++ python/paddle/utils/plotcurve.py | 13 +++ python/paddle/v2/dataset/sentiment.py | 13 +++ .../paddle/v2/dataset/tests/imikolov_test.py | 13 +++ .../paddle/v2/dataset/tests/test_sentiment.py | 13 +++ python/paddle/v2/event.py | 13 +++ python/paddle/v2/fluid/__init__.py | 13 +++ python/paddle/v2/fluid/backward.py | 13 +++ python/paddle/v2/fluid/clip.py | 13 +++ python/paddle/v2/fluid/data_feeder.py | 13 +++ python/paddle/v2/fluid/default_scope_funcs.py | 13 +++ .../paddle/v2/fluid/distribute_transpiler.py | 13 +++ .../v2/fluid/distribute_transpiler_simple.py | 13 +++ python/paddle/v2/fluid/distributed_spliter.py | 13 +++ python/paddle/v2/fluid/evaluator.py | 13 +++ python/paddle/v2/fluid/executor.py | 13 +++ python/paddle/v2/fluid/framework.py | 13 +++ python/paddle/v2/fluid/initializer.py | 13 +++ python/paddle/v2/fluid/io.py | 13 +++ python/paddle/v2/fluid/layer_helper.py | 13 +++ python/paddle/v2/fluid/layers/__init__.py | 13 +++ python/paddle/v2/fluid/layers/control_flow.py | 13 +++ python/paddle/v2/fluid/layers/device.py | 13 +++ python/paddle/v2/fluid/layers/io.py | 13 +++ python/paddle/v2/fluid/layers/nn.py | 13 +++ python/paddle/v2/fluid/layers/ops.py | 13 +++ python/paddle/v2/fluid/layers/tensor.py | 13 +++ .../fluid/memory_optimization_transpiler.py | 13 +++ python/paddle/v2/fluid/net_drawer.py | 13 +++ python/paddle/v2/fluid/nets.py | 13 +++ python/paddle/v2/fluid/op.py | 13 +++ python/paddle/v2/fluid/optimizer.py | 13 +++ python/paddle/v2/fluid/param_attr.py | 13 +++ python/paddle/v2/fluid/profiler.py | 13 +++ python/paddle/v2/fluid/registry.py | 13 +++ python/paddle/v2/fluid/regularizer.py | 13 +++ python/paddle/v2/fluid/tests/__init__.py | 13 +++ .../v2/fluid/tests/book/test_fit_a_line.py | 13 +++ .../book/test_image_classification_train.py | 13 +++ .../tests/book/test_label_semantic_roles.py | 13 +++ .../tests/book/test_machine_translation.py | 13 +++ .../tests/book/test_recognize_digits_conv.py | 13 +++ .../tests/book/test_recognize_digits_mlp.py | 13 +++ .../tests/book/test_recommender_system.py | 13 +++ .../book/test_understand_sentiment_conv.py | 13 +++ .../test_understand_sentiment_dynamic_lstm.py | 13 +++ .../book/test_understand_sentiment_lstm.py | 13 +++ .../v2/fluid/tests/book/test_word2vec.py | 13 +++ .../book_distribute/notest_dist_fit_a_line.py | 13 +++ .../notest_dist_label_semantic_roles.py | 13 +++ .../book_distribute/notest_dist_word2vec.py | 13 +++ .../notest_recognize_digits_conv_dist.py | 13 +++ .../notest_understand_sentiment_conv_dist.py | 13 +++ .../tests/book_distribute/test_split_var.py | 13 +++ python/paddle/v2/fluid/tests/decorators.py | 13 +++ python/paddle/v2/fluid/tests/demo/fc_gan.py | 13 +++ python/paddle/v2/fluid/tests/op_test.py | 13 +++ .../paddle/v2/fluid/tests/test_accuracy_op.py | 13 +++ .../v2/fluid/tests/test_activation_op.py | 13 +++ .../paddle/v2/fluid/tests/test_adadelta_op.py | 13 +++ .../paddle/v2/fluid/tests/test_adagrad_op.py | 13 +++ python/paddle/v2/fluid/tests/test_adam_op.py | 13 +++ .../paddle/v2/fluid/tests/test_adamax_op.py | 13 +++ .../fluid/tests/test_array_read_write_op.py | 13 +++ .../paddle/v2/fluid/tests/test_assign_op.py | 13 +++ .../v2/fluid/tests/test_assign_value_op.py | 13 +++ python/paddle/v2/fluid/tests/test_auc_op.py | 13 +++ .../v2/fluid/tests/test_batch_norm_op.py | 13 +++ .../fluid/tests/test_beam_search_decode_op.py | 13 +++ .../v2/fluid/tests/test_beam_search_op.py | 13 +++ .../tests/test_bilinear_tensor_product_op.py | 13 +++ .../v2/fluid/tests/test_calc_gradient.py | 13 +++ python/paddle/v2/fluid/tests/test_cast_op.py | 13 +++ .../v2/fluid/tests/test_chunk_eval_op.py | 13 +++ python/paddle/v2/fluid/tests/test_clip.py | 13 +++ .../v2/fluid/tests/test_clip_by_norm_op.py | 13 +++ python/paddle/v2/fluid/tests/test_clip_op.py | 13 +++ .../paddle/v2/fluid/tests/test_compare_op.py | 13 +++ .../paddle/v2/fluid/tests/test_concat_op.py | 13 +++ python/paddle/v2/fluid/tests/test_cond_op.py | 13 +++ .../v2/fluid/tests/test_conditional_block.py | 13 +++ .../paddle/v2/fluid/tests/test_const_value.py | 13 +++ .../paddle/v2/fluid/tests/test_conv2d_op.py | 13 +++ .../fluid/tests/test_conv2d_transpose_op.py | 13 +++ .../paddle/v2/fluid/tests/test_conv3d_op.py | 13 +++ .../fluid/tests/test_conv3d_transpose_op.py | 13 +++ .../v2/fluid/tests/test_conv_shift_op.py | 13 +++ .../paddle/v2/fluid/tests/test_cos_sim_op.py | 13 +++ .../fluid/tests/test_create_op_doc_string.py | 13 +++ .../v2/fluid/tests/test_crf_decoding_op.py | 13 +++ python/paddle/v2/fluid/tests/test_crop_op.py | 13 +++ .../v2/fluid/tests/test_cross_entropy_op.py | 13 +++ .../paddle/v2/fluid/tests/test_data_feeder.py | 13 +++ .../v2/fluid/tests/test_decayed_adagrad_op.py | 13 +++ .../fluid/tests/test_default_scope_funcs.py | 13 +++ .../fluid/tests/test_detection_output_op.py | 13 +++ .../paddle/v2/fluid/tests/test_dropout_op.py | 13 +++ python/paddle/v2/fluid/tests/test_dyn_rnn.py | 13 +++ .../fluid/tests/test_dynrnn_gradient_check.py | 13 +++ .../v2/fluid/tests/test_elementwise_add_op.py | 13 +++ .../v2/fluid/tests/test_elementwise_div_op.py | 13 +++ .../v2/fluid/tests/test_elementwise_mul_op.py | 13 +++ .../v2/fluid/tests/test_elementwise_sub_op.py | 13 +++ .../paddle/v2/fluid/tests/test_exception.py | 13 +++ .../v2/fluid/tests/test_executor_and_mul.py | 13 +++ .../paddle/v2/fluid/tests/test_expand_op.py | 13 +++ .../v2/fluid/tests/test_feed_fetch_method.py | 13 +++ .../test_fill_constant_batch_size_like_op.py | 13 +++ .../v2/fluid/tests/test_fill_constant_op.py | 13 +++ python/paddle/v2/fluid/tests/test_fill_op.py | 13 +++ .../v2/fluid/tests/test_fill_zeros_like_op.py | 13 +++ .../fluid/tests/test_framework_debug_str.py | 13 +++ python/paddle/v2/fluid/tests/test_ftrl_op.py | 13 +++ .../paddle/v2/fluid/tests/test_gather_op.py | 13 +++ .../v2/fluid/tests/test_gaussian_random_op.py | 13 +++ .../v2/fluid/tests/test_get_places_op.py | 13 +++ python/paddle/v2/fluid/tests/test_gru_op.py | 13 +++ .../paddle/v2/fluid/tests/test_gru_unit_op.py | 13 +++ .../v2/fluid/tests/test_hinge_loss_op.py | 13 +++ .../v2/fluid/tests/test_huber_loss_op.py | 13 +++ .../tests/test_image_classification_layer.py | 13 +++ .../paddle/v2/fluid/tests/test_infer_shape.py | 13 +++ .../v2/fluid/tests/test_inference_model_io.py | 13 +++ .../paddle/v2/fluid/tests/test_initializer.py | 13 +++ .../paddle/v2/fluid/tests/test_is_empty_op.py | 13 +++ .../paddle/v2/fluid/tests/test_l1_norm_op.py | 13 +++ python/paddle/v2/fluid/tests/test_layers.py | 13 +++ .../fluid/tests/test_linear_chain_crf_op.py | 13 +++ .../fluid/tests/test_lod_array_length_op.py | 13 +++ .../v2/fluid/tests/test_lod_rank_table.py | 13 +++ .../v2/fluid/tests/test_lod_reset_op.py | 13 +++ .../v2/fluid/tests/test_lod_tensor_array.py | 13 +++ .../fluid/tests/test_lod_tensor_array_ops.py | 13 +++ .../paddle/v2/fluid/tests/test_log_loss_op.py | 13 +++ .../paddle/v2/fluid/tests/test_logical_op.py | 13 +++ .../v2/fluid/tests/test_lookup_table_op.py | 13 +++ python/paddle/v2/fluid/tests/test_lrn_op.py | 13 +++ python/paddle/v2/fluid/tests/test_lstm_op.py | 13 +++ .../v2/fluid/tests/test_lstm_unit_op.py | 13 +++ .../fluid/tests/test_margin_rank_loss_op.py | 13 +++ .../paddle/v2/fluid/tests/test_matmul_op.py | 13 +++ .../paddle/v2/fluid/tests/test_maxout_op.py | 13 +++ python/paddle/v2/fluid/tests/test_mean_op.py | 13 +++ .../test_memory_optimization_transpiler.py | 13 +++ python/paddle/v2/fluid/tests/test_minus_op.py | 13 +++ .../v2/fluid/tests/test_mnist_if_else_op.py | 13 +++ .../tests/test_modified_huber_loss_op.py | 13 +++ .../paddle/v2/fluid/tests/test_momentum_op.py | 13 +++ python/paddle/v2/fluid/tests/test_mul_op.py | 13 +++ .../v2/fluid/tests/test_multiplex_op.py | 13 +++ python/paddle/v2/fluid/tests/test_nce.py | 13 +++ python/paddle/v2/fluid/tests/test_net.py | 13 +++ python/paddle/v2/fluid/tests/test_norm_op.py | 13 +++ .../v2/fluid/tests/test_op_support_gpu.py | 13 +++ python/paddle/v2/fluid/tests/test_operator.py | 13 +++ .../v2/fluid/tests/test_operator_desc.py | 13 +++ .../paddle/v2/fluid/tests/test_optimizer.py | 13 +++ python/paddle/v2/fluid/tests/test_pad_op.py | 13 +++ .../paddle/v2/fluid/tests/test_parallel_op.py | 13 +++ .../paddle/v2/fluid/tests/test_parameter.py | 13 +++ .../paddle/v2/fluid/tests/test_pool2d_op.py | 13 +++ .../paddle/v2/fluid/tests/test_pool3d_op.py | 13 +++ .../paddle/v2/fluid/tests/test_pool_max_op.py | 13 +++ .../tests/test_positive_negative_pair_op.py | 13 +++ .../fluid/tests/test_precision_recall_op.py | 13 +++ python/paddle/v2/fluid/tests/test_prelu_op.py | 13 +++ python/paddle/v2/fluid/tests/test_print_op.py | 13 +++ python/paddle/v2/fluid/tests/test_profiler.py | 13 +++ python/paddle/v2/fluid/tests/test_program.py | 13 +++ python/paddle/v2/fluid/tests/test_protobuf.py | 13 +++ .../v2/fluid/tests/test_protobuf_descs.py | 13 +++ .../fluid/tests/test_proximal_adagrad_op.py | 13 +++ .../v2/fluid/tests/test_proximal_gd_op.py | 13 +++ .../v2/fluid/tests/test_rank_loss_op.py | 13 +++ .../v2/fluid/tests/test_recurrent_op.py | 13 +++ .../paddle/v2/fluid/tests/test_reduce_op.py | 13 +++ python/paddle/v2/fluid/tests/test_registry.py | 13 +++ .../paddle/v2/fluid/tests/test_regularizer.py | 13 +++ .../v2/fluid/tests/test_reorder_lod_tensor.py | 13 +++ .../paddle/v2/fluid/tests/test_reshape_op.py | 13 +++ .../paddle/v2/fluid/tests/test_rmsprop_op.py | 13 +++ .../fluid/tests/test_rnn_memory_helper_op.py | 13 +++ .../paddle/v2/fluid/tests/test_roi_pool_op.py | 13 +++ .../paddle/v2/fluid/tests/test_row_conv_op.py | 13 +++ python/paddle/v2/fluid/tests/test_scale_op.py | 13 +++ .../paddle/v2/fluid/tests/test_scatter_op.py | 13 +++ python/paddle/v2/fluid/tests/test_scope.py | 13 +++ .../v2/fluid/tests/test_selected_rows.py | 13 +++ .../v2/fluid/tests/test_seq_concat_op.py | 13 +++ python/paddle/v2/fluid/tests/test_seq_conv.py | 13 +++ python/paddle/v2/fluid/tests/test_seq_pool.py | 13 +++ .../v2/fluid/tests/test_sequence_erase_op.py | 13 +++ .../v2/fluid/tests/test_sequence_expand.py | 13 +++ .../v2/fluid/tests/test_sequence_slice_op.py | 13 +++ .../fluid/tests/test_sequence_softmax_op.py | 13 +++ python/paddle/v2/fluid/tests/test_sgd_op.py | 13 +++ .../v2/fluid/tests/test_shrink_rnn_memory.py | 13 +++ ...st_sigmoid_cross_entropy_with_logits_op.py | 13 +++ python/paddle/v2/fluid/tests/test_sign_op.py | 13 +++ .../v2/fluid/tests/test_smooth_l1_loss_op.py | 13 +++ .../paddle/v2/fluid/tests/test_softmax_op.py | 13 +++ .../test_softmax_with_cross_entropy_op.py | 13 +++ .../test_split_and_merge_lod_tensor_op.py | 13 +++ python/paddle/v2/fluid/tests/test_split_op.py | 13 +++ python/paddle/v2/fluid/tests/test_spp_op.py | 13 +++ .../tests/test_squared_l2_distance_op.py | 13 +++ .../v2/fluid/tests/test_squared_l2_norm_op.py | 13 +++ python/paddle/v2/fluid/tests/test_sum_op.py | 13 +++ python/paddle/v2/fluid/tests/test_tensor.py | 13 +++ python/paddle/v2/fluid/tests/test_top_k_op.py | 13 +++ .../v2/fluid/tests/test_transpose_op.py | 13 +++ .../v2/fluid/tests/test_uniform_random_op.py | 13 +++ .../paddle/v2/fluid/tests/test_unpool_op.py | 13 +++ python/paddle/v2/fluid/tests/test_variable.py | 13 +++ .../paddle/v2/fluid/tests/test_warpctc_op.py | 13 +++ python/paddle/v2/fluid/tests/test_while_op.py | 13 +++ python/paddle/v2/image.py | 13 +++ python/paddle/v2/inference.py | 13 +++ python/paddle/v2/master/__init__.py | 13 +++ python/paddle/v2/master/client.py | 13 +++ python/paddle/v2/reader/tests/__init__.py | 13 +++ python/paddle/v2/tests/test_parameters.py | 13 +++ python/paddle/v2/trainer.py | 13 +++ .../build_scripts/manylinux1-check.py | 13 +++ tools/manylinux1/build_scripts/ssl-check.py | 13 +++ v1_api_demo/mnist/api_train.py | 13 +++ v1_api_demo/mnist/mnist_provider.py | 13 +++ .../data/proc_from_raw_data/preprocess.py | 13 +++ .../quick_start/trainer_config.bidi-lstm.py | 13 +++ v1_api_demo/quick_start/trainer_config.cnn.py | 13 +++ v1_api_demo/quick_start/trainer_config.emb.py | 13 +++ v1_api_demo/quick_start/trainer_config.lr.py | 13 +++ .../quick_start/trainer_config.lstm.py | 13 +++ .../quick_start/trainer_config.resnet-lstm.py | 13 +++ 376 files changed, 4976 insertions(+), 13 deletions(-) create mode 100644 .copyright.hook diff --git a/.copyright.hook b/.copyright.hook new file mode 100644 index 00000000000..e28e88e2664 --- /dev/null +++ b/.copyright.hook @@ -0,0 +1,106 @@ +from __future__ import absolute_import +from __future__ import print_function +from __future__ import unicode_literals + +import argparse +import io, re +import sys, os +import subprocess +import platform + +COPYRIGHT = ''' + Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +LANG_COMMENT_MARK = None + +NEW_LINE_MARK = None + +COPYRIGHT_HEADER = None + +if platform.system() == "Windows": + NEW_LINE_MARK = "\r\n" +else: + NEW_LINE_MARK = '\n' + COPYRIGHT_HEADER = COPYRIGHT.split(NEW_LINE_MARK)[1] + p = re.search('(\d{4})', COPYRIGHT_HEADER).group(0) + process = subprocess.Popen(["date", "+%Y"], stdout=subprocess.PIPE) + date, err = process.communicate() + date = date.decode("utf-8").rstrip("\n") + COPYRIGHT_HEADER = COPYRIGHT_HEADER.replace(p, date) + + +def generate_copyright(template, lang='C'): + if lang == 'Python': + LANG_COMMENT_MARK = '#' + else: + LANG_COMMENT_MARK = "//" + + lines = template.split(NEW_LINE_MARK) + ans = LANG_COMMENT_MARK + COPYRIGHT_HEADER + NEW_LINE_MARK + for lino, line in enumerate(lines): + if lino == 0 or lino == 1 or lino == len(lines) - 1: continue + ans += LANG_COMMENT_MARK + line + NEW_LINE_MARK + + return ans + + +def lang_type(filename): + if filename.endswith(".py"): + return "Python" + elif filename.endswith(".h"): + return "C" + elif filename.endswith(".hpp"): + return "C" + elif filename.endswith(".cc"): + return "C" + elif filename.endswith(".cpp"): + return "C" + elif filename.endswith(".cu"): + return "C" + elif filename.endswith(".cuh"): + return "C" + elif filename.endswith(".go"): + return "C" + elif filename.endswith(".proto"): + return "C" + else: + print("Unsupported filetype") + exit(0) + + +def main(argv=None): + parser = argparse.ArgumentParser( + description='Checker for copyright declaration.') + parser.add_argument('filenames', nargs='*', help='Filenames to check') + args = parser.parse_args(argv) + + retv = 0 + for filename in args.filenames: + first_line = io.open(filename).readline() + if "Copyright" in first_line: continue + original_contents = io.open(filename).read() + new_contents = generate_copyright( + COPYRIGHT, lang_type(filename)) + original_contents + print('Auto Insert Copyright Header {}'.format(filename)) + retv = 1 + with io.open(filename, 'w') as output_file: + output_file.write(new_contents) + + return retv + + +if __name__ == '__main__': + exit(main()) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 59661c9c1da..89c620bb2f7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,3 +31,11 @@ - id: go-fmt types: - go +- repo: local + hooks: + - id: copyright_checker + name: copyright_checker + entry: python ./.copyright.hook + language: system + files: \.(c|cc|cxx|cpp|cu|h|hpp|hxx|proto|py)$ + exclude: (?!.*third_party)^.*$ | (?!.*book)^.*$ diff --git a/adversarial/advbox/attacks/base.py b/adversarial/advbox/attacks/base.py index 98a65f2fddf..000baa48f62 100644 --- a/adversarial/advbox/attacks/base.py +++ b/adversarial/advbox/attacks/base.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ The base model of the model. """ diff --git a/adversarial/advbox/attacks/gradientsign.py b/adversarial/advbox/attacks/gradientsign.py index 15b1d176cb1..77d93bd7939 100644 --- a/adversarial/advbox/attacks/gradientsign.py +++ b/adversarial/advbox/attacks/gradientsign.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ This module provide the attack method for FGSM's implement. """ diff --git a/adversarial/advbox/models/base.py b/adversarial/advbox/models/base.py index 74e1045def7..084e563f7b4 100644 --- a/adversarial/advbox/models/base.py +++ b/adversarial/advbox/models/base.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ The base model of the model. """ diff --git a/adversarial/advbox/models/paddle.py b/adversarial/advbox/models/paddle.py index 33b2a3d5c69..4048b47f897 100644 --- a/adversarial/advbox/models/paddle.py +++ b/adversarial/advbox/models/paddle.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import absolute_import import numpy as np diff --git a/adversarial/fluid_mnist.py b/adversarial/fluid_mnist.py index db4d4b51868..f8c7fe8d0ef 100644 --- a/adversarial/fluid_mnist.py +++ b/adversarial/fluid_mnist.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ CNN on mnist data using fluid api of paddlepaddle """ diff --git a/adversarial/mnist_tutorial_fgsm.py b/adversarial/mnist_tutorial_fgsm.py index 8b29346b8cd..c63e030cd82 100644 --- a/adversarial/mnist_tutorial_fgsm.py +++ b/adversarial/mnist_tutorial_fgsm.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ FGSM demos on mnist using advbox tool. """ diff --git a/benchmark/paddle/image/alexnet.py b/benchmark/paddle/image/alexnet.py index cad6051f141..07f478d8fa4 100644 --- a/benchmark/paddle/image/alexnet.py +++ b/benchmark/paddle/image/alexnet.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python from paddle.trainer_config_helpers import * diff --git a/benchmark/paddle/image/googlenet.py b/benchmark/paddle/image/googlenet.py index 2a850ccb7f2..3241be9c5f5 100644 --- a/benchmark/paddle/image/googlenet.py +++ b/benchmark/paddle/image/googlenet.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python from paddle.trainer_config_helpers import * diff --git a/benchmark/paddle/image/provider.py b/benchmark/paddle/image/provider.py index 1018ec9ce1e..220c4bee35c 100644 --- a/benchmark/paddle/image/provider.py +++ b/benchmark/paddle/image/provider.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import io, os import random import numpy as np diff --git a/benchmark/paddle/image/resnet.py b/benchmark/paddle/image/resnet.py index 2846e4763f1..acc6d31d4bb 100644 --- a/benchmark/paddle/image/resnet.py +++ b/benchmark/paddle/image/resnet.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python from paddle.trainer_config_helpers import * diff --git a/benchmark/paddle/image/smallnet_mnist_cifar.py b/benchmark/paddle/image/smallnet_mnist_cifar.py index 58879c454f3..64a5da3220b 100644 --- a/benchmark/paddle/image/smallnet_mnist_cifar.py +++ b/benchmark/paddle/image/smallnet_mnist_cifar.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python from paddle.trainer_config_helpers import * diff --git a/benchmark/paddle/image/vgg.py b/benchmark/paddle/image/vgg.py index ca0a6798fb8..a357207a628 100644 --- a/benchmark/paddle/image/vgg.py +++ b/benchmark/paddle/image/vgg.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python from paddle.trainer_config_helpers import * diff --git a/benchmark/paddle/rnn/provider.py b/benchmark/paddle/rnn/provider.py index 928ca75daf8..c03df3a0026 100644 --- a/benchmark/paddle/rnn/provider.py +++ b/benchmark/paddle/rnn/provider.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import io, os import random import numpy as np diff --git a/benchmark/paddle/rnn/rnn.py b/benchmark/paddle/rnn/rnn.py index 83eb3e56547..97005f2c351 100755 --- a/benchmark/paddle/rnn/rnn.py +++ b/benchmark/paddle/rnn/rnn.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python from paddle.trainer_config_helpers import * diff --git a/benchmark/tensorflow/image/alexnet.py b/benchmark/tensorflow/image/alexnet.py index f6a39ef778e..edf462e6a18 100644 --- a/benchmark/tensorflow/image/alexnet.py +++ b/benchmark/tensorflow/image/alexnet.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from six.moves import xrange # pylint: disable=redefined-builtin from datetime import datetime import math diff --git a/benchmark/tensorflow/image/alexnet_multi_gpu.py b/benchmark/tensorflow/image/alexnet_multi_gpu.py index 7b5ee78f4dd..90b8f16bca0 100644 --- a/benchmark/tensorflow/image/alexnet_multi_gpu.py +++ b/benchmark/tensorflow/image/alexnet_multi_gpu.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from six.moves import xrange # pylint: disable=redefined-builtin from datetime import datetime import math diff --git a/benchmark/tensorflow/image/googlenet.py b/benchmark/tensorflow/image/googlenet.py index decf855b544..55431eceb3c 100644 --- a/benchmark/tensorflow/image/googlenet.py +++ b/benchmark/tensorflow/image/googlenet.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from six.moves import xrange from datetime import datetime import math diff --git a/benchmark/tensorflow/image/smallnet_mnist_cifar.py b/benchmark/tensorflow/image/smallnet_mnist_cifar.py index 1a625134a6c..0858b5f9c9c 100644 --- a/benchmark/tensorflow/image/smallnet_mnist_cifar.py +++ b/benchmark/tensorflow/image/smallnet_mnist_cifar.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from six.moves import xrange # pylint: disable=redefined-builtin from datetime import datetime import math diff --git a/benchmark/tensorflow/rnn/reader.py b/benchmark/tensorflow/rnn/reader.py index f538329a15e..710940c9ae2 100755 --- a/benchmark/tensorflow/rnn/reader.py +++ b/benchmark/tensorflow/rnn/reader.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import os.path import io import numpy as np diff --git a/benchmark/tensorflow/rnn/rnn.py b/benchmark/tensorflow/rnn/rnn.py index f288083e136..507481b9ccd 100755 --- a/benchmark/tensorflow/rnn/rnn.py +++ b/benchmark/tensorflow/rnn/rnn.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python from six.moves import xrange # pylint: disable=redefined-builtin import math diff --git a/benchmark/tensorflow/rnn/rnn_multi_gpu.py b/benchmark/tensorflow/rnn/rnn_multi_gpu.py index eabee4fa8fe..f24cbaef62d 100755 --- a/benchmark/tensorflow/rnn/rnn_multi_gpu.py +++ b/benchmark/tensorflow/rnn/rnn_multi_gpu.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python from six.moves import xrange # pylint: disable=redefined-builtin import re diff --git a/cmake/make_resource.py b/cmake/make_resource.py index a9241b0e3e3..d71e82eca2c 100644 --- a/cmake/make_resource.py +++ b/cmake/make_resource.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import os import re import sys diff --git a/doc/api/v1/data_provider/src/mnist_config.py b/doc/api/v1/data_provider/src/mnist_config.py index 429338c57f8..427e0465a68 100644 --- a/doc/api/v1/data_provider/src/mnist_config.py +++ b/doc/api/v1/data_provider/src/mnist_config.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * define_py_data_sources2( diff --git a/doc/api/v1/data_provider/src/mnist_provider.dict.py b/doc/api/v1/data_provider/src/mnist_provider.dict.py index 2ba0b126a0d..3fbb783e2f6 100644 --- a/doc/api/v1/data_provider/src/mnist_provider.dict.py +++ b/doc/api/v1/data_provider/src/mnist_provider.dict.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer.PyDataProvider2 import * diff --git a/doc/api/v1/data_provider/src/sentimental_config.py b/doc/api/v1/data_provider/src/sentimental_config.py index 7ce71608a23..edbf3cf1400 100644 --- a/doc/api/v1/data_provider/src/sentimental_config.py +++ b/doc/api/v1/data_provider/src/sentimental_config.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * dictionary = dict() diff --git a/doc/api/v1/data_provider/src/sentimental_provider.py b/doc/api/v1/data_provider/src/sentimental_provider.py index 14bd0e05a92..03ad1fe7d8c 100644 --- a/doc/api/v1/data_provider/src/sentimental_provider.py +++ b/doc/api/v1/data_provider/src/sentimental_provider.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer.PyDataProvider2 import * diff --git a/doc/faq/local/src/reduce_min_pool_size.py b/doc/faq/local/src/reduce_min_pool_size.py index 5715397cc11..96073633d2b 100644 --- a/doc/faq/local/src/reduce_min_pool_size.py +++ b/doc/faq/local/src/reduce_min_pool_size.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. @provider(min_pool_size=0, ...) def process(settings, filename): os.system('shuf %s > %s.shuf' % (filename, filename)) # shuffle before. diff --git a/doc/faq/local/src/word2vec_config.py b/doc/faq/local/src/word2vec_config.py index 866b40c3d4c..03619b2628f 100644 --- a/doc/faq/local/src/word2vec_config.py +++ b/doc/faq/local/src/word2vec_config.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. ... # the settings and define data provider is omitted. DICT_DIM = 3000 # dictionary dimension. word_ids = data_layer('word_ids', size=DICT_DIM) diff --git a/doc/faq/local/src/word2vec_dataprovider.py b/doc/faq/local/src/word2vec_dataprovider.py index ec2753a7d01..a439a8f52eb 100644 --- a/doc/faq/local/src/word2vec_dataprovider.py +++ b/doc/faq/local/src/word2vec_dataprovider.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. DICT_DIM = 3000 diff --git a/doc/getstarted/concepts/src/train.py b/doc/getstarted/concepts/src/train.py index 4bccbfca3c7..d9c0c66b8a7 100644 --- a/doc/getstarted/concepts/src/train.py +++ b/doc/getstarted/concepts/src/train.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2 as paddle import numpy as np diff --git a/doc/howto/usage/cluster/src/k8s_train/start_paddle.py b/doc/howto/usage/cluster/src/k8s_train/start_paddle.py index 935c12bb67e..1774f8b640c 100755 --- a/doc/howto/usage/cluster/src/k8s_train/start_paddle.py +++ b/doc/howto/usage/cluster/src/k8s_train/start_paddle.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/python # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved # diff --git a/doc/howto/usage/cluster/src/word2vec/api_train_v2.py b/doc/howto/usage/cluster/src/word2vec/api_train_v2.py index c0940f0e56e..d449e02023f 100644 --- a/doc/howto/usage/cluster/src/word2vec/api_train_v2.py +++ b/doc/howto/usage/cluster/src/word2vec/api_train_v2.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import gzip import math diff --git a/doc/howto/usage/cluster/src/word2vec/api_train_v2_cluster.py b/doc/howto/usage/cluster/src/word2vec/api_train_v2_cluster.py index 2e6d8887124..a5dd347f0b5 100644 --- a/doc/howto/usage/cluster/src/word2vec/api_train_v2_cluster.py +++ b/doc/howto/usage/cluster/src/word2vec/api_train_v2_cluster.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import math import os import paddle.v2 as paddle diff --git a/go/pserver/client/c/test/test_mnist.py b/go/pserver/client/c/test/test_mnist.py index c3a3af55e28..7b50a10afc6 100644 --- a/go/pserver/client/c/test/test_mnist.py +++ b/go/pserver/client/c/test/test_mnist.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2 as paddle import gzip diff --git a/go/pserver/client/c/test/test_train.py b/go/pserver/client/c/test/test_train.py index 8d9c6b9b20f..7ef0fca496e 100644 --- a/go/pserver/client/c/test/test_train.py +++ b/go/pserver/client/c/test/test_train.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2 as paddle import paddle.v2.dataset.uci_housing as uci_housing import paddle.v2.master as master diff --git a/paddle/api/test/testTrainConfig.py b/paddle/api/test/testTrainConfig.py index 77e0cd37d56..ab9a83e4a35 100644 --- a/paddle/api/test/testTrainConfig.py +++ b/paddle/api/test/testTrainConfig.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=100, learning_method=AdamOptimizer()) diff --git a/paddle/capi/examples/model_inference/common/common.h b/paddle/capi/examples/model_inference/common/common.h index e32f2f9836f..9efcbc387e6 100644 --- a/paddle/capi/examples/model_inference/common/common.h +++ b/paddle/capi/examples/model_inference/common/common.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #ifndef __CAPI_EXAMPLE_COMMON_H__ #define __CAPI_EXAMPLE_COMMON_H__ #include diff --git a/paddle/capi/examples/model_inference/dense/merge_v2_model.py b/paddle/capi/examples/model_inference/dense/merge_v2_model.py index c030d572cbd..760a485a53f 100644 --- a/paddle/capi/examples/model_inference/dense/merge_v2_model.py +++ b/paddle/capi/examples/model_inference/dense/merge_v2_model.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.utils.merge_model import merge_v2_model from mnist_v2 import network diff --git a/paddle/capi/examples/model_inference/dense/mnist_v2.py b/paddle/capi/examples/model_inference/dense/mnist_v2.py index ee28111153c..174436bd1d7 100644 --- a/paddle/capi/examples/model_inference/dense/mnist_v2.py +++ b/paddle/capi/examples/model_inference/dense/mnist_v2.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import os import sys import gzip diff --git a/paddle/capi/examples/model_inference/dense/trainer_config.py b/paddle/capi/examples/model_inference/dense/trainer_config.py index 873ec119e7a..fbf08903578 100644 --- a/paddle/capi/examples/model_inference/dense/trainer_config.py +++ b/paddle/capi/examples/model_inference/dense/trainer_config.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * img = data_layer(name='pixel', size=784) diff --git a/paddle/capi/examples/model_inference/sequence/trainer_config.py b/paddle/capi/examples/model_inference/sequence/trainer_config.py index 6bbc7a909aa..c1326bb9555 100644 --- a/paddle/capi/examples/model_inference/sequence/trainer_config.py +++ b/paddle/capi/examples/model_inference/sequence/trainer_config.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * WORD_DIM = 3000 diff --git a/paddle/capi/tests/test_predict_network.py b/paddle/capi/tests/test_predict_network.py index 82ef5cb1a70..46a985d4765 100644 --- a/paddle/capi/tests/test_predict_network.py +++ b/paddle/capi/tests/test_predict_network.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=100) diff --git a/paddle/cuda/src/avx_mathfun.h b/paddle/cuda/src/avx_mathfun.h index 2412ed5abc1..a0ba71faba9 100644 --- a/paddle/cuda/src/avx_mathfun.h +++ b/paddle/cuda/src/avx_mathfun.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* AVX implementation of sin, cos, sincos, exp and log diff --git a/paddle/framework/backward_test.cc b/paddle/framework/backward_test.cc index 692406b1c37..72743b5fd0b 100644 --- a/paddle/framework/backward_test.cc +++ b/paddle/framework/backward_test.cc @@ -1,16 +1,16 @@ -/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. */ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include "paddle/framework/backward.h" diff --git a/paddle/framework/dim.h b/paddle/framework/dim.h index 04d4b0e604e..ec17d7c6156 100644 --- a/paddle/framework/dim.h +++ b/paddle/framework/dim.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #pragma once #include diff --git a/paddle/framework/dim_test.cu b/paddle/framework/dim_test.cu index 0a6a87669c9..2bcab7c5c2e 100644 --- a/paddle/framework/dim_test.cu +++ b/paddle/framework/dim_test.cu @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include #include diff --git a/paddle/framework/eigen_test.cc b/paddle/framework/eigen_test.cc index bc4a2db32cf..c6f77ecfabd 100644 --- a/paddle/framework/eigen_test.cc +++ b/paddle/framework/eigen_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/paddle/framework/lod_tensor_test.cc b/paddle/framework/lod_tensor_test.cc index baad9c6f98a..19ae7815cc1 100644 --- a/paddle/framework/lod_tensor_test.cc +++ b/paddle/framework/lod_tensor_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/paddle/framework/lod_tensor_test.cu b/paddle/framework/lod_tensor_test.cu index e8508ad2658..0f46e9b1e39 100644 --- a/paddle/framework/lod_tensor_test.cu +++ b/paddle/framework/lod_tensor_test.cu @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/paddle/framework/tensor_test.cc b/paddle/framework/tensor_test.cc index a1b4a03289e..c04cd38697f 100644 --- a/paddle/framework/tensor_test.cc +++ b/paddle/framework/tensor_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/paddle/framework/tensor_util_test.cc b/paddle/framework/tensor_util_test.cc index 3636125f205..f541927c0e7 100644 --- a/paddle/framework/tensor_util_test.cc +++ b/paddle/framework/tensor_util_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/paddle/framework/variable.h b/paddle/framework/variable.h index 36b76fb196c..03992c86086 100644 --- a/paddle/framework/variable.h +++ b/paddle/framework/variable.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/paddle/gserver/tests/img_conv_cudnn.py b/paddle/gserver/tests/img_conv_cudnn.py index 3934607fa41..e424261bda2 100644 --- a/paddle/gserver/tests/img_conv_cudnn.py +++ b/paddle/gserver/tests/img_conv_cudnn.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved # diff --git a/paddle/gserver/tests/img_conv_exconv.py b/paddle/gserver/tests/img_conv_exconv.py index ad5a8ba2bde..3b59cd80b8b 100644 --- a/paddle/gserver/tests/img_conv_exconv.py +++ b/paddle/gserver/tests/img_conv_exconv.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved # diff --git a/paddle/gserver/tests/sequence_recurrent.py b/paddle/gserver/tests/sequence_recurrent.py index 4895df186bf..d20d3331ae6 100644 --- a/paddle/gserver/tests/sequence_recurrent.py +++ b/paddle/gserver/tests/sequence_recurrent.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved # diff --git a/paddle/gserver/tests/sequence_rnn_matched_inputs.py b/paddle/gserver/tests/sequence_rnn_matched_inputs.py index 59e8c91733c..b156fa9baaf 100644 --- a/paddle/gserver/tests/sequence_rnn_matched_inputs.py +++ b/paddle/gserver/tests/sequence_rnn_matched_inputs.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved # diff --git a/paddle/gserver/tests/sequence_rnn_mixed_inputs.py b/paddle/gserver/tests/sequence_rnn_mixed_inputs.py index 6fe9dca6e2c..c2c98aae5f5 100644 --- a/paddle/gserver/tests/sequence_rnn_mixed_inputs.py +++ b/paddle/gserver/tests/sequence_rnn_mixed_inputs.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved # diff --git a/paddle/gserver/tests/sequence_rnn_multi_unequalength_inputs.py b/paddle/gserver/tests/sequence_rnn_multi_unequalength_inputs.py index 786a0c6d780..c812c6ced24 100644 --- a/paddle/gserver/tests/sequence_rnn_multi_unequalength_inputs.py +++ b/paddle/gserver/tests/sequence_rnn_multi_unequalength_inputs.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved # diff --git a/paddle/operators/math/math_function_test.cc b/paddle/operators/math/math_function_test.cc index 7c6f098ca90..c9f322b92e5 100644 --- a/paddle/operators/math/math_function_test.cc +++ b/paddle/operators/math/math_function_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include "paddle/operators/math/math_function.h" #include "gtest/gtest.h" diff --git a/paddle/operators/math/math_function_test.cu b/paddle/operators/math/math_function_test.cu index d1139ac988c..6f16d667924 100644 --- a/paddle/operators/math/math_function_test.cu +++ b/paddle/operators/math/math_function_test.cu @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include "gtest/gtest.h" #include "paddle/operators/math/math_function.h" diff --git a/paddle/operators/net_op.cc b/paddle/operators/net_op.cc index 03302f5cbf5..f12074a5f2d 100644 --- a/paddle/operators/net_op.cc +++ b/paddle/operators/net_op.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. diff --git a/paddle/operators/net_op_test.cc b/paddle/operators/net_op_test.cc index dfd86546e83..9358f29f62f 100644 --- a/paddle/operators/net_op_test.cc +++ b/paddle/operators/net_op_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include "paddle/operators/net_op.h" #include diff --git a/paddle/optimizer/lr_policy.h b/paddle/optimizer/lr_policy.h index bbb1ee48214..9a44a776f2b 100644 --- a/paddle/optimizer/lr_policy.h +++ b/paddle/optimizer/lr_policy.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #pragma once #include diff --git a/paddle/optimizer/parameter_optimizer_test.cc b/paddle/optimizer/parameter_optimizer_test.cc index 83757a39178..795d2de1d65 100644 --- a/paddle/optimizer/parameter_optimizer_test.cc +++ b/paddle/optimizer/parameter_optimizer_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. diff --git a/paddle/optimizer/serialization_test.cc b/paddle/optimizer/serialization_test.cc index 940e941e904..0f1b14eec13 100644 --- a/paddle/optimizer/serialization_test.cc +++ b/paddle/optimizer/serialization_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. diff --git a/paddle/optimizer/tensor.h b/paddle/optimizer/tensor.h index 86fa625e01b..e999e9bda12 100644 --- a/paddle/optimizer/tensor.h +++ b/paddle/optimizer/tensor.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #pragma once /** * @brief tensor used by optimizer diff --git a/paddle/platform/cpu_info_test.cc b/paddle/platform/cpu_info_test.cc index 8fb195aa7c0..1bfe62c1fb6 100644 --- a/paddle/platform/cpu_info_test.cc +++ b/paddle/platform/cpu_info_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include "paddle/platform/cpu_info.h" #include "paddle/string/printf.h" diff --git a/paddle/platform/hostdevice.h b/paddle/platform/hostdevice.h index eb2df291cce..fa4659ed298 100644 --- a/paddle/platform/hostdevice.h +++ b/paddle/platform/hostdevice.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #pragma once #ifdef __CUDACC__ diff --git a/paddle/platform/place_test.cc b/paddle/platform/place_test.cc index 4f1eba01df5..150b2d3b1fb 100644 --- a/paddle/platform/place_test.cc +++ b/paddle/platform/place_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include "paddle/platform/place.h" #include #include "gtest/gtest.h" diff --git a/paddle/pybind/print_operators_doc.cc b/paddle/pybind/print_operators_doc.cc index f4f281229e6..99694fa5920 100644 --- a/paddle/pybind/print_operators_doc.cc +++ b/paddle/pybind/print_operators_doc.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include #include // std::stringstream #include diff --git a/paddle/scripts/cluster_train/paddle.py b/paddle/scripts/cluster_train/paddle.py index 9b03ed1d8f6..e44bb4505b9 100644 --- a/paddle/scripts/cluster_train/paddle.py +++ b/paddle/scripts/cluster_train/paddle.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/python # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved # diff --git a/paddle/scripts/cpplint.py b/paddle/scripts/cpplint.py index dff4339ea33..d0cbb070c43 100644 --- a/paddle/scripts/cpplint.py +++ b/paddle/scripts/cpplint.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python # # Copyright (c) 2009 Google Inc. All rights reserved. diff --git a/paddle/string/piece.cc b/paddle/string/piece.cc index b80afdec82d..2a553e28320 100644 --- a/paddle/string/piece.cc +++ b/paddle/string/piece.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. diff --git a/paddle/string/piece.h b/paddle/string/piece.h index 7362ce02c7c..fc952633792 100644 --- a/paddle/string/piece.h +++ b/paddle/string/piece.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. diff --git a/paddle/string/piece_test.cc b/paddle/string/piece_test.cc index cf5152ff5a3..fb8b9729880 100644 --- a/paddle/string/piece_test.cc +++ b/paddle/string/piece_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. diff --git a/paddle/string/printf.h b/paddle/string/printf.h index 8b5ce63a8e8..70d25115316 100644 --- a/paddle/string/printf.h +++ b/paddle/string/printf.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. diff --git a/paddle/string/printf_test.cc b/paddle/string/printf_test.cc index 2586264046a..b5ad35513bd 100644 --- a/paddle/string/printf_test.cc +++ b/paddle/string/printf_test.cc @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. #include "paddle/string/printf.h" #include diff --git a/paddle/string/tinyformat/tinyformat.h b/paddle/string/tinyformat/tinyformat.h index 3516777d9f9..092c04c3153 100644 --- a/paddle/string/tinyformat/tinyformat.h +++ b/paddle/string/tinyformat/tinyformat.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. // tinyformat.h // Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com] // diff --git a/paddle/trainer/tests/simple_sparse_neural_network.py b/paddle/trainer/tests/simple_sparse_neural_network.py index 30346ef299d..ba554d5872d 100644 --- a/paddle/trainer/tests/simple_sparse_neural_network.py +++ b/paddle/trainer/tests/simple_sparse_neural_network.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=17, learning_method=AdaGradOptimizer(), learning_rate=1e-4) diff --git a/paddle/trainer/tests/simple_sparse_neural_network_dp.py b/paddle/trainer/tests/simple_sparse_neural_network_dp.py index 86b272edfe1..44e96873f0d 100644 --- a/paddle/trainer/tests/simple_sparse_neural_network_dp.py +++ b/paddle/trainer/tests/simple_sparse_neural_network_dp.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer.PyDataProvider2 import provider, integer_sequence, integer_value import random diff --git a/paddle/utils/enable_virtualenv.py b/paddle/utils/enable_virtualenv.py index ccfaa7c147b..29f8deb3245 100644 --- a/paddle/utils/enable_virtualenv.py +++ b/paddle/utils/enable_virtualenv.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import os diff --git a/proto/OptimizerConfig.proto b/proto/OptimizerConfig.proto index d27b1bcf800..b341d78d194 100644 --- a/proto/OptimizerConfig.proto +++ b/proto/OptimizerConfig.proto @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. syntax = "proto2"; option optimize_for = LITE_RUNTIME; diff --git a/python/paddle/trainer_config_helpers/tests/configs/img_layers.py b/python/paddle/trainer_config_helpers/tests/configs/img_layers.py index 01d31ef3fad..c944a96042a 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/img_layers.py +++ b/python/paddle/trainer_config_helpers/tests/configs/img_layers.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-3, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/img_trans_layers.py b/python/paddle/trainer_config_helpers/tests/configs/img_trans_layers.py index 91849b40a08..27b11ffdfc7 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/img_trans_layers.py +++ b/python/paddle/trainer_config_helpers/tests/configs/img_trans_layers.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-3, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/last_first_seq.py b/python/paddle/trainer_config_helpers/tests/configs/last_first_seq.py index f87237f9b59..6a900518272 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/last_first_seq.py +++ b/python/paddle/trainer_config_helpers/tests/configs/last_first_seq.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/layer_activations.py b/python/paddle/trainer_config_helpers/tests/configs/layer_activations.py index 7012dbf6a0b..06115d62e77 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/layer_activations.py +++ b/python/paddle/trainer_config_helpers/tests/configs/layer_activations.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. ''' Test all activations. ''' diff --git a/python/paddle/trainer_config_helpers/tests/configs/math_ops.py b/python/paddle/trainer_config_helpers/tests/configs/math_ops.py index a607a62c99f..f5e90fdd899 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/math_ops.py +++ b/python/paddle/trainer_config_helpers/tests/configs/math_ops.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/projections.py b/python/paddle/trainer_config_helpers/tests/configs/projections.py index dc8975cb311..c683d378caa 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/projections.py +++ b/python/paddle/trainer_config_helpers/tests/configs/projections.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. ''' Test mixed layer, projections and operators. ''' diff --git a/python/paddle/trainer_config_helpers/tests/configs/shared_fc.py b/python/paddle/trainer_config_helpers/tests/configs/shared_fc.py index 7c848ef3fcd..bf90d1762c8 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/shared_fc.py +++ b/python/paddle/trainer_config_helpers/tests/configs/shared_fc.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/shared_gru.py b/python/paddle/trainer_config_helpers/tests/configs/shared_gru.py index c19bb9685aa..7cfab838552 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/shared_gru.py +++ b/python/paddle/trainer_config_helpers/tests/configs/shared_gru.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/shared_lstm.py b/python/paddle/trainer_config_helpers/tests/configs/shared_lstm.py index 565e281a6e1..8a425c7062e 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/shared_lstm.py +++ b/python/paddle/trainer_config_helpers/tests/configs/shared_lstm.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/simple_rnn_layers.py b/python/paddle/trainer_config_helpers/tests/configs/simple_rnn_layers.py index a5b5bb30b1d..8ee213a4935 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/simple_rnn_layers.py +++ b/python/paddle/trainer_config_helpers/tests/configs/simple_rnn_layers.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-4) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_BatchNorm3D.py b/python/paddle/trainer_config_helpers/tests/configs/test_BatchNorm3D.py index a991b22252b..cbd3c3e97f8 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_BatchNorm3D.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_BatchNorm3D.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-4) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_bi_grumemory.py b/python/paddle/trainer_config_helpers/tests/configs/test_bi_grumemory.py index cd7f609638e..bed9154fe3e 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_bi_grumemory.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_bi_grumemory.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-4) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_bilinear_interp.py b/python/paddle/trainer_config_helpers/tests/configs/test_bilinear_interp.py index be83f4f83c5..7e1da753f53 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_bilinear_interp.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_bilinear_interp.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_clip_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_clip_layer.py index f066fe1fb30..0a719b07354 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_clip_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_clip_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * data = data_layer(name='input', size=300) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_config_parser_for_non_file_config.py b/python/paddle/trainer_config_helpers/tests/configs/test_config_parser_for_non_file_config.py index 9b791a0222d..7003872700b 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_config_parser_for_non_file_config.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_config_parser_for_non_file_config.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved. # diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_conv3d_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_conv3d_layer.py index aa0a2c0d5fe..fb2cacd4433 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_conv3d_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_conv3d_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_cost_layers.py b/python/paddle/trainer_config_helpers/tests/configs/test_cost_layers.py index 7ce375c708a..a8b5c860ef5 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_cost_layers.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_cost_layers.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_cost_layers_with_weight.py b/python/paddle/trainer_config_helpers/tests/configs/test_cost_layers_with_weight.py index caa6aaa9430..eba2e1e4834 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_cost_layers_with_weight.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_cost_layers_with_weight.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_crop.py b/python/paddle/trainer_config_helpers/tests/configs/test_crop.py index 8314a7e9a55..870388faf74 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_crop.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_crop.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_cross_entropy_over_beam.py b/python/paddle/trainer_config_helpers/tests/configs/test_cross_entropy_over_beam.py index 4a5bdf1181d..253244dcd42 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_cross_entropy_over_beam.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_cross_entropy_over_beam.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python #coding=utf-8 diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_deconv3d_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_deconv3d_layer.py index a113279fc17..db950093b37 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_deconv3d_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_deconv3d_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_detection_output_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_detection_output_layer.py index 3572a2cb07d..d304a298591 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_detection_output_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_detection_output_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_dot_prod_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_dot_prod_layer.py index e52d48dde00..2e5dde2da23 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_dot_prod_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_dot_prod_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * vec1 = data_layer(name='vector1', size=10) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_expand_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_expand_layer.py index c53f10e0a41..345fb2b6aba 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_expand_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_expand_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_factorization_machine.py b/python/paddle/trainer_config_helpers/tests/configs/test_factorization_machine.py index b249de0fee3..3a489a39da1 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_factorization_machine.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_factorization_machine.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * data = data_layer(name='data', size=1024) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_fc.py b/python/paddle/trainer_config_helpers/tests/configs/test_fc.py index 2842d3429c9..90b0e37270f 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_fc.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_fc.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_gated_unit_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_gated_unit_layer.py index 9dab45519c6..2bd4ab2da4e 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_gated_unit_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_gated_unit_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * data = data_layer(name='input', size=256) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_grumemory_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_grumemory_layer.py index 474e4f36bad..451909ee183 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_grumemory_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_grumemory_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-4) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_hsigmoid.py b/python/paddle/trainer_config_helpers/tests/configs/test_hsigmoid.py index dff1c535b3e..3ebe40aadc7 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_hsigmoid.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_hsigmoid.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_kmax_seq_socre_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_kmax_seq_socre_layer.py index 171da10f75d..c762467febc 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_kmax_seq_socre_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_kmax_seq_socre_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python #coding=utf-8 from paddle.trainer_config_helpers import * diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_l2_distance_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_l2_distance_layer.py index b36a5c6d122..58bf3de1046 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_l2_distance_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_l2_distance_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * outputs( diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_lstmemory_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_lstmemory_layer.py index 7ca1cc2db36..8d570706dfb 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_lstmemory_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_lstmemory_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_maxout.py b/python/paddle/trainer_config_helpers/tests/configs/test_maxout.py index eb14270baa0..3b6117d297a 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_maxout.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_maxout.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_multibox_loss_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_multibox_loss_layer.py index c3376c47bde..083d0643678 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_multibox_loss_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_multibox_loss_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_multiplex_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_multiplex_layer.py index d2500019325..9c144558419 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_multiplex_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_multiplex_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_ntm_layers.py b/python/paddle/trainer_config_helpers/tests/configs/test_ntm_layers.py index b7a15666f0a..046698fb4e2 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_ntm_layers.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_ntm_layers.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_pad.py b/python/paddle/trainer_config_helpers/tests/configs/test_pad.py index 491e8c8caab..1046db2f098 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_pad.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_pad.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_pooling3D_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_pooling3D_layer.py index 0dbb921d419..37805d43767 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_pooling3D_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_pooling3D_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=100, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_prelu_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_prelu_layer.py index 45b02fbf325..10d759f6d90 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_prelu_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_prelu_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * data = data_layer(name='input', size=300, height=10, width=10) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_print_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_print_layer.py index 8da26ff44b1..22e0ce3e5ac 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_print_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_print_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_recursive_topology.py b/python/paddle/trainer_config_helpers/tests/configs/test_recursive_topology.py index 1a693f8dff0..d1d97f1c5e1 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_recursive_topology.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_recursive_topology.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_repeat_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_repeat_layer.py index 004e2a5dd4e..6818b91f969 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_repeat_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_repeat_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_resize_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_resize_layer.py index 09a6f507338..ce8a22ebb17 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_resize_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_resize_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * data = data_layer(name='input', size=300) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_rnn_group.py b/python/paddle/trainer_config_helpers/tests/configs/test_rnn_group.py index 91010759e48..79dad5e2508 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_rnn_group.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_rnn_group.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_roi_pool_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_roi_pool_layer.py index b739a81b850..264341f899e 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_roi_pool_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_roi_pool_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * data = data_layer(name='data', size=3 * 14 * 14, height=14, width=14) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_row_conv.py b/python/paddle/trainer_config_helpers/tests/configs/test_row_conv.py index ab33c496b06..342a5029a83 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_row_conv.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_row_conv.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_row_l2_norm_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_row_l2_norm_layer.py index ac8badb26a4..9521fa6c471 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_row_l2_norm_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_row_l2_norm_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * data = data_layer(name='input', size=300) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_scale_shift_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_scale_shift_layer.py index dd589116fa9..698d19d0375 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_scale_shift_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_scale_shift_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * data = data_layer(name='data', size=100) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_scale_sub_region_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_scale_sub_region_layer.py index 8d4bf28bf1e..22fb25d0f23 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_scale_sub_region_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_scale_sub_region_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_seq_concat_reshape.py b/python/paddle/trainer_config_helpers/tests/configs/test_seq_concat_reshape.py index 5c161ba805f..1883ed9d4ed 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_seq_concat_reshape.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_seq_concat_reshape.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_seq_slice_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_seq_slice_layer.py index 510ad322089..12d7f1f33b1 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_seq_slice_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_seq_slice_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python #coding=utf-8 from paddle.trainer_config_helpers import * diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_sequence_pooling.py b/python/paddle/trainer_config_helpers/tests/configs/test_sequence_pooling.py index 3c205eabd80..8cf5fd70e3f 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_sequence_pooling.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_sequence_pooling.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_smooth_l1.py b/python/paddle/trainer_config_helpers/tests/configs/test_smooth_l1.py index 66629662dd9..7188d82a534 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_smooth_l1.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_smooth_l1.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * data = data_layer(name='input', size=300) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_split_datasource.py b/python/paddle/trainer_config_helpers/tests/configs/test_split_datasource.py index 318b4459bab..a628272196a 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_split_datasource.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_split_datasource.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * define_py_data_sources2( diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_spp_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_spp_layer.py index e0b0d0d3be2..58c1675e6b6 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_spp_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_spp_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=100, learning_rate=1e-5) diff --git a/python/paddle/trainer_config_helpers/tests/configs/test_sub_nested_seq_select_layer.py b/python/paddle/trainer_config_helpers/tests/configs/test_sub_nested_seq_select_layer.py index 6d1c3175ba9..64d1d7b6eeb 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/test_sub_nested_seq_select_layer.py +++ b/python/paddle/trainer_config_helpers/tests/configs/test_sub_nested_seq_select_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/env python #coding=utf-8 from paddle.trainer_config_helpers import * diff --git a/python/paddle/trainer_config_helpers/tests/configs/unused_layers.py b/python/paddle/trainer_config_helpers/tests/configs/unused_layers.py index ebb39219bdc..6294cb04ef5 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/unused_layers.py +++ b/python/paddle/trainer_config_helpers/tests/configs/unused_layers.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(batch_size=1000, learning_rate=1e-4) diff --git a/python/paddle/trainer_config_helpers/tests/configs/util_layers.py b/python/paddle/trainer_config_helpers/tests/configs/util_layers.py index 27f1c8e9938..89b881b3611 100644 --- a/python/paddle/trainer_config_helpers/tests/configs/util_layers.py +++ b/python/paddle/trainer_config_helpers/tests/configs/util_layers.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer_config_helpers import * settings(learning_rate=1e-4, batch_size=1000) diff --git a/python/paddle/utils/image_multiproc.py b/python/paddle/utils/image_multiproc.py index e8db525ff5c..1acf40df58e 100644 --- a/python/paddle/utils/image_multiproc.py +++ b/python/paddle/utils/image_multiproc.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import os, sys import numpy as np from PIL import Image diff --git a/python/paddle/utils/plotcurve.py b/python/paddle/utils/plotcurve.py index 27bd8157d39..27a69b6a5c8 100644 --- a/python/paddle/utils/plotcurve.py +++ b/python/paddle/utils/plotcurve.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. #!/usr/bin/python # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved # diff --git a/python/paddle/v2/dataset/sentiment.py b/python/paddle/v2/dataset/sentiment.py index b0b9757c1a7..7174413018c 100644 --- a/python/paddle/v2/dataset/sentiment.py +++ b/python/paddle/v2/dataset/sentiment.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # /usr/bin/env python # -*- coding:utf-8 -*- diff --git a/python/paddle/v2/dataset/tests/imikolov_test.py b/python/paddle/v2/dataset/tests/imikolov_test.py index 4e52810e6b9..9b3ab72feb0 100644 --- a/python/paddle/v2/dataset/tests/imikolov_test.py +++ b/python/paddle/v2/dataset/tests/imikolov_test.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.dataset.imikolov import unittest diff --git a/python/paddle/v2/dataset/tests/test_sentiment.py b/python/paddle/v2/dataset/tests/test_sentiment.py index 40740529073..f107948801d 100644 --- a/python/paddle/v2/dataset/tests/test_sentiment.py +++ b/python/paddle/v2/dataset/tests/test_sentiment.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # /usr/bin/env python # -*- coding:utf-8 -*- diff --git a/python/paddle/v2/event.py b/python/paddle/v2/event.py index a0ffd31c545..f322bffe133 100644 --- a/python/paddle/v2/event.py +++ b/python/paddle/v2/event.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ Testing and training events. diff --git a/python/paddle/v2/fluid/__init__.py b/python/paddle/v2/fluid/__init__.py index 5afc663822c..8c29ee741cb 100644 --- a/python/paddle/v2/fluid/__init__.py +++ b/python/paddle/v2/fluid/__init__.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function # import all class inside framework into fluid module import framework diff --git a/python/paddle/v2/fluid/backward.py b/python/paddle/v2/fluid/backward.py index 43f6133a653..27cf637c48b 100644 --- a/python/paddle/v2/fluid/backward.py +++ b/python/paddle/v2/fluid/backward.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.v2.fluid import framework as framework from . import core import collections diff --git a/python/paddle/v2/fluid/clip.py b/python/paddle/v2/fluid/clip.py index be0c2735f24..e4d9ed599ee 100644 --- a/python/paddle/v2/fluid/clip.py +++ b/python/paddle/v2/fluid/clip.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import functools import layers from . import core diff --git a/python/paddle/v2/fluid/data_feeder.py b/python/paddle/v2/fluid/data_feeder.py index 24036c3e75b..bfdd00e3ef7 100644 --- a/python/paddle/v2/fluid/data_feeder.py +++ b/python/paddle/v2/fluid/data_feeder.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import core import numpy diff --git a/python/paddle/v2/fluid/default_scope_funcs.py b/python/paddle/v2/fluid/default_scope_funcs.py index 9aebc07f8e8..2218bb140ac 100644 --- a/python/paddle/v2/fluid/default_scope_funcs.py +++ b/python/paddle/v2/fluid/default_scope_funcs.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ Default scope function. diff --git a/python/paddle/v2/fluid/distribute_transpiler.py b/python/paddle/v2/fluid/distribute_transpiler.py index d17f9815cca..06a7b6fb02f 100644 --- a/python/paddle/v2/fluid/distribute_transpiler.py +++ b/python/paddle/v2/fluid/distribute_transpiler.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import framework from framework import Program, default_main_program, Parameter, Variable diff --git a/python/paddle/v2/fluid/distribute_transpiler_simple.py b/python/paddle/v2/fluid/distribute_transpiler_simple.py index 32db3df9aa2..bd88f02bde0 100644 --- a/python/paddle/v2/fluid/distribute_transpiler_simple.py +++ b/python/paddle/v2/fluid/distribute_transpiler_simple.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import framework from framework import Program, default_main_program, Parameter, Variable import optimizer diff --git a/python/paddle/v2/fluid/distributed_spliter.py b/python/paddle/v2/fluid/distributed_spliter.py index eff30f7bb66..e647f760e9d 100644 --- a/python/paddle/v2/fluid/distributed_spliter.py +++ b/python/paddle/v2/fluid/distributed_spliter.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. def hash_name(varlist, pserver_endpoints): """ hash variable names to several endpoints. diff --git a/python/paddle/v2/fluid/evaluator.py b/python/paddle/v2/fluid/evaluator.py index dc083f37b5f..adf174a07da 100644 --- a/python/paddle/v2/fluid/evaluator.py +++ b/python/paddle/v2/fluid/evaluator.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import layers diff --git a/python/paddle/v2/fluid/executor.py b/python/paddle/v2/fluid/executor.py index 1b2075dcd5e..a99c5157b28 100644 --- a/python/paddle/v2/fluid/executor.py +++ b/python/paddle/v2/fluid/executor.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import contextlib from framework import Program, default_main_program diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index f78f2a331a6..8042febfed7 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import collections import contextlib diff --git a/python/paddle/v2/fluid/initializer.py b/python/paddle/v2/fluid/initializer.py index c3ed1a90896..2e8cfa3177b 100644 --- a/python/paddle/v2/fluid/initializer.py +++ b/python/paddle/v2/fluid/initializer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import framework import numpy as np diff --git a/python/paddle/v2/fluid/io.py b/python/paddle/v2/fluid/io.py index 54b6978ebaa..499df05e592 100644 --- a/python/paddle/v2/fluid/io.py +++ b/python/paddle/v2/fluid/io.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import os import cPickle as pickle diff --git a/python/paddle/v2/fluid/layer_helper.py b/python/paddle/v2/fluid/layer_helper.py index 325735e6793..191d2349b5e 100644 --- a/python/paddle/v2/fluid/layer_helper.py +++ b/python/paddle/v2/fluid/layer_helper.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import copy import itertools diff --git a/python/paddle/v2/fluid/layers/__init__.py b/python/paddle/v2/fluid/layers/__init__.py index 50ac0aba01a..c190af33294 100644 --- a/python/paddle/v2/fluid/layers/__init__.py +++ b/python/paddle/v2/fluid/layers/__init__.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import ops from ops import * import nn diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index ee97e5f4e69..cda97b69e9a 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from ..layer_helper import LayerHelper, unique_name from ..framework import Program, Variable, Operator from .. import core diff --git a/python/paddle/v2/fluid/layers/device.py b/python/paddle/v2/fluid/layers/device.py index 775d40e5b5e..ef74b2b2f08 100644 --- a/python/paddle/v2/fluid/layers/device.py +++ b/python/paddle/v2/fluid/layers/device.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ All util layers. """ diff --git a/python/paddle/v2/fluid/layers/io.py b/python/paddle/v2/fluid/layers/io.py index 56c3f7b7b7f..6177f0b4d71 100644 --- a/python/paddle/v2/fluid/layers/io.py +++ b/python/paddle/v2/fluid/layers/io.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from .. import core from ..layer_helper import LayerHelper diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 96a64af3709..4e8fd407c99 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ All layers just related to the neural network. """ diff --git a/python/paddle/v2/fluid/layers/ops.py b/python/paddle/v2/fluid/layers/ops.py index 51a85dbbd33..73d7c895806 100644 --- a/python/paddle/v2/fluid/layers/ops.py +++ b/python/paddle/v2/fluid/layers/ops.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from ..registry import register_layer __activations__ = [ diff --git a/python/paddle/v2/fluid/layers/tensor.py b/python/paddle/v2/fluid/layers/tensor.py index 2217c56b62a..255b9d46783 100644 --- a/python/paddle/v2/fluid/layers/tensor.py +++ b/python/paddle/v2/fluid/layers/tensor.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from ..layer_helper import LayerHelper from ..param_attr import ParamAttr from ..framework import convert_np_dtype_to_dtype_ diff --git a/python/paddle/v2/fluid/memory_optimization_transpiler.py b/python/paddle/v2/fluid/memory_optimization_transpiler.py index 293b116957f..89ffe26ed1a 100644 --- a/python/paddle/v2/fluid/memory_optimization_transpiler.py +++ b/python/paddle/v2/fluid/memory_optimization_transpiler.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from collections import defaultdict import framework from framework import Program, default_main_program, Parameter, Variable diff --git a/python/paddle/v2/fluid/net_drawer.py b/python/paddle/v2/fluid/net_drawer.py index 94fdd5e3897..7448975b59b 100644 --- a/python/paddle/v2/fluid/net_drawer.py +++ b/python/paddle/v2/fluid/net_drawer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import argparse import json import logging diff --git a/python/paddle/v2/fluid/nets.py b/python/paddle/v2/fluid/nets.py index d60ae43675f..b5c26e713db 100644 --- a/python/paddle/v2/fluid/nets.py +++ b/python/paddle/v2/fluid/nets.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import layers __all__ = [ diff --git a/python/paddle/v2/fluid/op.py b/python/paddle/v2/fluid/op.py index 5828803497e..4bc0f79c648 100644 --- a/python/paddle/v2/fluid/op.py +++ b/python/paddle/v2/fluid/op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.core as core import paddle.v2.fluid.proto.framework_pb2 as framework_pb2 diff --git a/python/paddle/v2/fluid/optimizer.py b/python/paddle/v2/fluid/optimizer.py index 40721b5e97a..8bd62ef0c02 100644 --- a/python/paddle/v2/fluid/optimizer.py +++ b/python/paddle/v2/fluid/optimizer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from collections import defaultdict import framework diff --git a/python/paddle/v2/fluid/param_attr.py b/python/paddle/v2/fluid/param_attr.py index ab4561b0423..3af0190590e 100644 --- a/python/paddle/v2/fluid/param_attr.py +++ b/python/paddle/v2/fluid/param_attr.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from initializer import Initializer, Xavier, Constant from regularizer import WeightDecayRegularizer diff --git a/python/paddle/v2/fluid/profiler.py b/python/paddle/v2/fluid/profiler.py index dcecd76224e..f049498b9ff 100644 --- a/python/paddle/v2/fluid/profiler.py +++ b/python/paddle/v2/fluid/profiler.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.core as core from contextlib import contextmanager import os diff --git a/python/paddle/v2/fluid/registry.py b/python/paddle/v2/fluid/registry.py index 94b16bca8c9..6c0c3a35185 100644 --- a/python/paddle/v2/fluid/registry.py +++ b/python/paddle/v2/fluid/registry.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import re import cStringIO import warnings diff --git a/python/paddle/v2/fluid/regularizer.py b/python/paddle/v2/fluid/regularizer.py index 117c45c49f1..e53dee98fd0 100644 --- a/python/paddle/v2/fluid/regularizer.py +++ b/python/paddle/v2/fluid/regularizer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import framework __all__ = [ diff --git a/python/paddle/v2/fluid/tests/__init__.py b/python/paddle/v2/fluid/tests/__init__.py index e69de29bb2d..2619c1c0e9d 100644 --- a/python/paddle/v2/fluid/tests/__init__.py +++ b/python/paddle/v2/fluid/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. diff --git a/python/paddle/v2/fluid/tests/book/test_fit_a_line.py b/python/paddle/v2/fluid/tests/book/test_fit_a_line.py index fbf46ac6cba..904df66dc18 100644 --- a/python/paddle/v2/fluid/tests/book/test_fit_a_line.py +++ b/python/paddle/v2/fluid/tests/book/test_fit_a_line.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import paddle.v2 as paddle import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/book/test_image_classification_train.py b/python/paddle/v2/fluid/tests/book/test_image_classification_train.py index 3d336ffe958..a06486aa087 100644 --- a/python/paddle/v2/fluid/tests/book/test_image_classification_train.py +++ b/python/paddle/v2/fluid/tests/book/test_image_classification_train.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import sys diff --git a/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py b/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py index 74ca56182c4..42971da0f04 100644 --- a/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py +++ b/python/paddle/v2/fluid/tests/book/test_label_semantic_roles.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import math import numpy as np diff --git a/python/paddle/v2/fluid/tests/book/test_machine_translation.py b/python/paddle/v2/fluid/tests/book/test_machine_translation.py index e79864b3977..deeb6b1badc 100644 --- a/python/paddle/v2/fluid/tests/book/test_machine_translation.py +++ b/python/paddle/v2/fluid/tests/book/test_machine_translation.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import paddle.v2 as paddle import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py b/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py index 35bf8da924d..1d5defbed33 100644 --- a/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py +++ b/python/paddle/v2/fluid/tests/book/test_recognize_digits_conv.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import numpy as np import paddle.v2 as paddle diff --git a/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py b/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py index 51bfe2973db..02da2fcc854 100644 --- a/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py +++ b/python/paddle/v2/fluid/tests/book/test_recognize_digits_mlp.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import numpy as np import paddle.v2 as paddle diff --git a/python/paddle/v2/fluid/tests/book/test_recommender_system.py b/python/paddle/v2/fluid/tests/book/test_recommender_system.py index e3cc2a89371..47e2afcd83b 100644 --- a/python/paddle/v2/fluid/tests/book/test_recommender_system.py +++ b/python/paddle/v2/fluid/tests/book/test_recommender_system.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import paddle.v2 as paddle import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py index f103358edca..b44d2b41e36 100644 --- a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py +++ b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_conv.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import numpy as np import paddle.v2 as paddle diff --git a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py index cd28f04b857..5a139c1dcd4 100644 --- a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py +++ b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_dynamic_lstm.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import paddle.v2 as paddle import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py index 633de66bea2..fab8a82f85d 100644 --- a/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py +++ b/python/paddle/v2/fluid/tests/book/test_understand_sentiment_lstm.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import paddle.v2 as paddle import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/book/test_word2vec.py b/python/paddle/v2/fluid/tests/book/test_word2vec.py index 8b928ff9eed..3d4bbccd33d 100644 --- a/python/paddle/v2/fluid/tests/book/test_word2vec.py +++ b/python/paddle/v2/fluid/tests/book/test_word2vec.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import paddle.v2 as paddle import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/book_distribute/notest_dist_fit_a_line.py b/python/paddle/v2/fluid/tests/book_distribute/notest_dist_fit_a_line.py index bb339c440bd..b886071f947 100644 --- a/python/paddle/v2/fluid/tests/book_distribute/notest_dist_fit_a_line.py +++ b/python/paddle/v2/fluid/tests/book_distribute/notest_dist_fit_a_line.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import paddle.v2 as paddle import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/book_distribute/notest_dist_label_semantic_roles.py b/python/paddle/v2/fluid/tests/book_distribute/notest_dist_label_semantic_roles.py index 5fa5e0e5f34..2b5a098ff25 100644 --- a/python/paddle/v2/fluid/tests/book_distribute/notest_dist_label_semantic_roles.py +++ b/python/paddle/v2/fluid/tests/book_distribute/notest_dist_label_semantic_roles.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import math import numpy as np diff --git a/python/paddle/v2/fluid/tests/book_distribute/notest_dist_word2vec.py b/python/paddle/v2/fluid/tests/book_distribute/notest_dist_word2vec.py index b41853784d6..dc04af5b7b6 100644 --- a/python/paddle/v2/fluid/tests/book_distribute/notest_dist_word2vec.py +++ b/python/paddle/v2/fluid/tests/book_distribute/notest_dist_word2vec.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import numpy as np import paddle.v2 as paddle diff --git a/python/paddle/v2/fluid/tests/book_distribute/notest_recognize_digits_conv_dist.py b/python/paddle/v2/fluid/tests/book_distribute/notest_recognize_digits_conv_dist.py index 20b4a8b34cd..27512c4f781 100644 --- a/python/paddle/v2/fluid/tests/book_distribute/notest_recognize_digits_conv_dist.py +++ b/python/paddle/v2/fluid/tests/book_distribute/notest_recognize_digits_conv_dist.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import numpy as np import paddle.v2 as paddle diff --git a/python/paddle/v2/fluid/tests/book_distribute/notest_understand_sentiment_conv_dist.py b/python/paddle/v2/fluid/tests/book_distribute/notest_understand_sentiment_conv_dist.py index db419e23abc..74f20f3f4cc 100644 --- a/python/paddle/v2/fluid/tests/book_distribute/notest_understand_sentiment_conv_dist.py +++ b/python/paddle/v2/fluid/tests/book_distribute/notest_understand_sentiment_conv_dist.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import os import numpy as np diff --git a/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py b/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py index cfb48a59154..f979f642d8f 100644 --- a/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py +++ b/python/paddle/v2/fluid/tests/book_distribute/test_split_var.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import math import unittest from paddle.v2.fluid.distribute_transpiler import split_dense_variable diff --git a/python/paddle/v2/fluid/tests/decorators.py b/python/paddle/v2/fluid/tests/decorators.py index 154619b0e93..3b314a15e1b 100644 --- a/python/paddle/v2/fluid/tests/decorators.py +++ b/python/paddle/v2/fluid/tests/decorators.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid as fluid __all__ = ['many_times', 'prog_scope'] diff --git a/python/paddle/v2/fluid/tests/demo/fc_gan.py b/python/paddle/v2/fluid/tests/demo/fc_gan.py index cae959593e8..5f9e8f95077 100644 --- a/python/paddle/v2/fluid/tests/demo/fc_gan.py +++ b/python/paddle/v2/fluid/tests/demo/fc_gan.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import errno import math import os diff --git a/python/paddle/v2/fluid/tests/op_test.py b/python/paddle/v2/fluid/tests/op_test.py index 276cf2c5f2d..c3b2220e6e2 100644 --- a/python/paddle/v2/fluid/tests/op_test.py +++ b/python/paddle/v2/fluid/tests/op_test.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np import random diff --git a/python/paddle/v2/fluid/tests/test_accuracy_op.py b/python/paddle/v2/fluid/tests/test_accuracy_op.py index 6f72918b717..a20abac8a0c 100644 --- a/python/paddle/v2/fluid/tests/test_accuracy_op.py +++ b/python/paddle/v2/fluid/tests/test_accuracy_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_activation_op.py b/python/paddle/v2/fluid/tests/test_activation_op.py index 03eb7deb9a3..a6a6eb9d635 100644 --- a/python/paddle/v2/fluid/tests/test_activation_op.py +++ b/python/paddle/v2/fluid/tests/test_activation_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_adadelta_op.py b/python/paddle/v2/fluid/tests/test_adadelta_op.py index 7105593a98a..8de6a1f9a9d 100644 --- a/python/paddle/v2/fluid/tests/test_adadelta_op.py +++ b/python/paddle/v2/fluid/tests/test_adadelta_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_adagrad_op.py b/python/paddle/v2/fluid/tests/test_adagrad_op.py index 7b2d02fbf42..30ed092d489 100644 --- a/python/paddle/v2/fluid/tests/test_adagrad_op.py +++ b/python/paddle/v2/fluid/tests/test_adagrad_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_adam_op.py b/python/paddle/v2/fluid/tests/test_adam_op.py index 7dbc2fa0858..32d00cf702e 100644 --- a/python/paddle/v2/fluid/tests/test_adam_op.py +++ b/python/paddle/v2/fluid/tests/test_adam_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_adamax_op.py b/python/paddle/v2/fluid/tests/test_adamax_op.py index 8e5a15aa3d1..35b2bc47ed6 100644 --- a/python/paddle/v2/fluid/tests/test_adamax_op.py +++ b/python/paddle/v2/fluid/tests/test_adamax_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_array_read_write_op.py b/python/paddle/v2/fluid/tests/test_array_read_write_op.py index 01321de8eac..8775cd4f9fb 100644 --- a/python/paddle/v2/fluid/tests/test_array_read_write_op.py +++ b/python/paddle/v2/fluid/tests/test_array_read_write_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core import paddle.v2.fluid.layers as layers diff --git a/python/paddle/v2/fluid/tests/test_assign_op.py b/python/paddle/v2/fluid/tests/test_assign_op.py index 1b0c145f1a6..4ac173c96bd 100644 --- a/python/paddle/v2/fluid/tests/test_assign_op.py +++ b/python/paddle/v2/fluid/tests/test_assign_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import op_test import numpy import unittest diff --git a/python/paddle/v2/fluid/tests/test_assign_value_op.py b/python/paddle/v2/fluid/tests/test_assign_value_op.py index 51b99d09182..f4e2ff9bdeb 100644 --- a/python/paddle/v2/fluid/tests/test_assign_value_op.py +++ b/python/paddle/v2/fluid/tests/test_assign_value_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid as fluid import paddle.v2.fluid.layers as layers import op_test diff --git a/python/paddle/v2/fluid/tests/test_auc_op.py b/python/paddle/v2/fluid/tests/test_auc_op.py index 26ea905d880..aa74d224d5d 100644 --- a/python/paddle/v2/fluid/tests/test_auc_op.py +++ b/python/paddle/v2/fluid/tests/test_auc_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_batch_norm_op.py b/python/paddle/v2/fluid/tests/test_batch_norm_op.py index ac9418549f4..fe82b7d7f31 100644 --- a/python/paddle/v2/fluid/tests/test_batch_norm_op.py +++ b/python/paddle/v2/fluid/tests/test_batch_norm_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_beam_search_decode_op.py b/python/paddle/v2/fluid/tests/test_beam_search_decode_op.py index f329214dce4..9ef6e08cc18 100644 --- a/python/paddle/v2/fluid/tests/test_beam_search_decode_op.py +++ b/python/paddle/v2/fluid/tests/test_beam_search_decode_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_beam_search_op.py b/python/paddle/v2/fluid/tests/test_beam_search_op.py index 319a7e49e35..f31c737ba6c 100644 --- a/python/paddle/v2/fluid/tests/test_beam_search_op.py +++ b/python/paddle/v2/fluid/tests/test_beam_search_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import logging from paddle.v2.fluid.op import Operator, DynamicRecurrentOp import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_bilinear_tensor_product_op.py b/python/paddle/v2/fluid/tests/test_bilinear_tensor_product_op.py index 080ca43b826..aed1bf4d3ae 100644 --- a/python/paddle/v2/fluid/tests/test_bilinear_tensor_product_op.py +++ b/python/paddle/v2/fluid/tests/test_bilinear_tensor_product_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_calc_gradient.py b/python/paddle/v2/fluid/tests/test_calc_gradient.py index c34c8ff6d14..b99eeb09cdb 100644 --- a/python/paddle/v2/fluid/tests/test_calc_gradient.py +++ b/python/paddle/v2/fluid/tests/test_calc_gradient.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/test_cast_op.py b/python/paddle/v2/fluid/tests/test_cast_op.py index 4e431bb88da..3795b96dbf0 100644 --- a/python/paddle/v2/fluid/tests/test_cast_op.py +++ b/python/paddle/v2/fluid/tests/test_cast_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import op_test import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_chunk_eval_op.py b/python/paddle/v2/fluid/tests/test_chunk_eval_op.py index 53bf6f815b8..59ef2bbb2fe 100644 --- a/python/paddle/v2/fluid/tests/test_chunk_eval_op.py +++ b/python/paddle/v2/fluid/tests/test_chunk_eval_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_clip.py b/python/paddle/v2/fluid/tests/test_clip.py index a71823f7e82..63353a10963 100644 --- a/python/paddle/v2/fluid/tests/test_clip.py +++ b/python/paddle/v2/fluid/tests/test_clip.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import numpy as np import paddle.v2 as paddle diff --git a/python/paddle/v2/fluid/tests/test_clip_by_norm_op.py b/python/paddle/v2/fluid/tests/test_clip_by_norm_op.py index 02f6108a3a6..5147e750462 100644 --- a/python/paddle/v2/fluid/tests/test_clip_by_norm_op.py +++ b/python/paddle/v2/fluid/tests/test_clip_by_norm_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_clip_op.py b/python/paddle/v2/fluid/tests/test_clip_op.py index a7e1bf17440..3338dc61b38 100644 --- a/python/paddle/v2/fluid/tests/test_clip_op.py +++ b/python/paddle/v2/fluid/tests/test_clip_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_compare_op.py b/python/paddle/v2/fluid/tests/test_compare_op.py index 5d0dfab6ffd..fbf8921e405 100644 --- a/python/paddle/v2/fluid/tests/test_compare_op.py +++ b/python/paddle/v2/fluid/tests/test_compare_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import op_test import unittest import numpy diff --git a/python/paddle/v2/fluid/tests/test_concat_op.py b/python/paddle/v2/fluid/tests/test_concat_op.py index a792d1c106a..3e413e15404 100644 --- a/python/paddle/v2/fluid/tests/test_concat_op.py +++ b/python/paddle/v2/fluid/tests/test_concat_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_cond_op.py b/python/paddle/v2/fluid/tests/test_cond_op.py index 32e54084e48..5312fa51a25 100644 --- a/python/paddle/v2/fluid/tests/test_cond_op.py +++ b/python/paddle/v2/fluid/tests/test_cond_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import logging import paddle.v2.fluid.core as core import unittest diff --git a/python/paddle/v2/fluid/tests/test_conditional_block.py b/python/paddle/v2/fluid/tests/test_conditional_block.py index 7d815123f34..965e7d39c80 100644 --- a/python/paddle/v2/fluid/tests/test_conditional_block.py +++ b/python/paddle/v2/fluid/tests/test_conditional_block.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.layers as layers import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_const_value.py b/python/paddle/v2/fluid/tests/test_const_value.py index f8c17c2c986..190bfa779b4 100644 --- a/python/paddle/v2/fluid/tests/test_const_value.py +++ b/python/paddle/v2/fluid/tests/test_const_value.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.framework as framework diff --git a/python/paddle/v2/fluid/tests/test_conv2d_op.py b/python/paddle/v2/fluid/tests/test_conv2d_op.py index e9a19d1774f..8b03a3ae165 100644 --- a/python/paddle/v2/fluid/tests/test_conv2d_op.py +++ b/python/paddle/v2/fluid/tests/test_conv2d_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py b/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py index 4aec32fc6e7..b7b86c58fb8 100644 --- a/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py +++ b/python/paddle/v2/fluid/tests/test_conv2d_transpose_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_conv3d_op.py b/python/paddle/v2/fluid/tests/test_conv3d_op.py index df911e1a2f0..5b0397cc690 100644 --- a/python/paddle/v2/fluid/tests/test_conv3d_op.py +++ b/python/paddle/v2/fluid/tests/test_conv3d_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py b/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py index a42a9c4f33f..b08969062a8 100644 --- a/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py +++ b/python/paddle/v2/fluid/tests/test_conv3d_transpose_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_conv_shift_op.py b/python/paddle/v2/fluid/tests/test_conv_shift_op.py index b9ab21a06a1..14b2640e24c 100644 --- a/python/paddle/v2/fluid/tests/test_conv_shift_op.py +++ b/python/paddle/v2/fluid/tests/test_conv_shift_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_cos_sim_op.py b/python/paddle/v2/fluid/tests/test_cos_sim_op.py index 47557ccb41d..f6e5e2cbe9e 100644 --- a/python/paddle/v2/fluid/tests/test_cos_sim_op.py +++ b/python/paddle/v2/fluid/tests/test_cos_sim_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_create_op_doc_string.py b/python/paddle/v2/fluid/tests/test_create_op_doc_string.py index 42b6f7a3616..6c922642210 100644 --- a/python/paddle/v2/fluid/tests/test_create_op_doc_string.py +++ b/python/paddle/v2/fluid/tests/test_create_op_doc_string.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.layers as layers diff --git a/python/paddle/v2/fluid/tests/test_crf_decoding_op.py b/python/paddle/v2/fluid/tests/test_crf_decoding_op.py index ab573da31df..40e80a824a2 100644 --- a/python/paddle/v2/fluid/tests/test_crf_decoding_op.py +++ b/python/paddle/v2/fluid/tests/test_crf_decoding_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import random import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_crop_op.py b/python/paddle/v2/fluid/tests/test_crop_op.py index 62c883bdc13..a0b2fc954dd 100644 --- a/python/paddle/v2/fluid/tests/test_crop_op.py +++ b/python/paddle/v2/fluid/tests/test_crop_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_cross_entropy_op.py b/python/paddle/v2/fluid/tests/test_cross_entropy_op.py index b81af9364d6..f05e6b23565 100644 --- a/python/paddle/v2/fluid/tests/test_cross_entropy_op.py +++ b/python/paddle/v2/fluid/tests/test_cross_entropy_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest, randomize_probability diff --git a/python/paddle/v2/fluid/tests/test_data_feeder.py b/python/paddle/v2/fluid/tests/test_data_feeder.py index 45496932032..5574766f8fa 100644 --- a/python/paddle/v2/fluid/tests/test_data_feeder.py +++ b/python/paddle/v2/fluid/tests/test_data_feeder.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/test_decayed_adagrad_op.py b/python/paddle/v2/fluid/tests/test_decayed_adagrad_op.py index 674c3fda5c8..5e745a28431 100644 --- a/python/paddle/v2/fluid/tests/test_decayed_adagrad_op.py +++ b/python/paddle/v2/fluid/tests/test_decayed_adagrad_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_default_scope_funcs.py b/python/paddle/v2/fluid/tests/test_default_scope_funcs.py index 738e69529ea..7a62168be90 100644 --- a/python/paddle/v2/fluid/tests/test_default_scope_funcs.py +++ b/python/paddle/v2/fluid/tests/test_default_scope_funcs.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.v2.fluid.default_scope_funcs import * import unittest diff --git a/python/paddle/v2/fluid/tests/test_detection_output_op.py b/python/paddle/v2/fluid/tests/test_detection_output_op.py index 080a9743b01..147a43628c6 100644 --- a/python/paddle/v2/fluid/tests/test_detection_output_op.py +++ b/python/paddle/v2/fluid/tests/test_detection_output_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_dropout_op.py b/python/paddle/v2/fluid/tests/test_dropout_op.py index 24832002126..f401050dcc3 100644 --- a/python/paddle/v2/fluid/tests/test_dropout_op.py +++ b/python/paddle/v2/fluid/tests/test_dropout_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_dyn_rnn.py b/python/paddle/v2/fluid/tests/test_dyn_rnn.py index 8090c5f4781..a946fea58d6 100644 --- a/python/paddle/v2/fluid/tests/test_dyn_rnn.py +++ b/python/paddle/v2/fluid/tests/test_dyn_rnn.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid as fluid import paddle.v2 as paddle import unittest diff --git a/python/paddle/v2/fluid/tests/test_dynrnn_gradient_check.py b/python/paddle/v2/fluid/tests/test_dynrnn_gradient_check.py index cdbf582a339..95cc80739d6 100644 --- a/python/paddle/v2/fluid/tests/test_dynrnn_gradient_check.py +++ b/python/paddle/v2/fluid/tests/test_dynrnn_gradient_check.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy import random import collections diff --git a/python/paddle/v2/fluid/tests/test_elementwise_add_op.py b/python/paddle/v2/fluid/tests/test_elementwise_add_op.py index 57daddd5698..1e88231877f 100644 --- a/python/paddle/v2/fluid/tests/test_elementwise_add_op.py +++ b/python/paddle/v2/fluid/tests/test_elementwise_add_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_elementwise_div_op.py b/python/paddle/v2/fluid/tests/test_elementwise_div_op.py index 41cb2b7767e..fbabc79be24 100644 --- a/python/paddle/v2/fluid/tests/test_elementwise_div_op.py +++ b/python/paddle/v2/fluid/tests/test_elementwise_div_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_elementwise_mul_op.py b/python/paddle/v2/fluid/tests/test_elementwise_mul_op.py index 261ca9cb3da..ef3a829abc6 100644 --- a/python/paddle/v2/fluid/tests/test_elementwise_mul_op.py +++ b/python/paddle/v2/fluid/tests/test_elementwise_mul_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_elementwise_sub_op.py b/python/paddle/v2/fluid/tests/test_elementwise_sub_op.py index be982e8c57b..db24db7b304 100644 --- a/python/paddle/v2/fluid/tests/test_elementwise_sub_op.py +++ b/python/paddle/v2/fluid/tests/test_elementwise_sub_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_exception.py b/python/paddle/v2/fluid/tests/test_exception.py index b871f40c4a0..98c4cbe3f2f 100644 --- a/python/paddle/v2/fluid/tests/test_exception.py +++ b/python/paddle/v2/fluid/tests/test_exception.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.core as core import unittest diff --git a/python/paddle/v2/fluid/tests/test_executor_and_mul.py b/python/paddle/v2/fluid/tests/test_executor_and_mul.py index b1ef87c5cb1..e8baf631e52 100644 --- a/python/paddle/v2/fluid/tests/test_executor_and_mul.py +++ b/python/paddle/v2/fluid/tests/test_executor_and_mul.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy diff --git a/python/paddle/v2/fluid/tests/test_expand_op.py b/python/paddle/v2/fluid/tests/test_expand_op.py index 0440f7a2bb1..0524f2041fb 100644 --- a/python/paddle/v2/fluid/tests/test_expand_op.py +++ b/python/paddle/v2/fluid/tests/test_expand_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_feed_fetch_method.py b/python/paddle/v2/fluid/tests/test_feed_fetch_method.py index 178c85b0dd5..718311517df 100644 --- a/python/paddle/v2/fluid/tests/test_feed_fetch_method.py +++ b/python/paddle/v2/fluid/tests/test_feed_fetch_method.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.core as core import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_fill_constant_batch_size_like_op.py b/python/paddle/v2/fluid/tests/test_fill_constant_batch_size_like_op.py index 99de6b5d052..0adc487c04a 100644 --- a/python/paddle/v2/fluid/tests/test_fill_constant_batch_size_like_op.py +++ b/python/paddle/v2/fluid/tests/test_fill_constant_batch_size_like_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_fill_constant_op.py b/python/paddle/v2/fluid/tests/test_fill_constant_op.py index dff7b615aa3..50d4ccb3bdd 100644 --- a/python/paddle/v2/fluid/tests/test_fill_constant_op.py +++ b/python/paddle/v2/fluid/tests/test_fill_constant_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_fill_op.py b/python/paddle/v2/fluid/tests/test_fill_op.py index 88337598c89..42b06ec87c2 100644 --- a/python/paddle/v2/fluid/tests/test_fill_op.py +++ b/python/paddle/v2/fluid/tests/test_fill_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_fill_zeros_like_op.py b/python/paddle/v2/fluid/tests/test_fill_zeros_like_op.py index cd91769a22f..a28bed96973 100644 --- a/python/paddle/v2/fluid/tests/test_fill_zeros_like_op.py +++ b/python/paddle/v2/fluid/tests/test_fill_zeros_like_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_framework_debug_str.py b/python/paddle/v2/fluid/tests/test_framework_debug_str.py index a4cbabdb363..6c82e67220f 100644 --- a/python/paddle/v2/fluid/tests/test_framework_debug_str.py +++ b/python/paddle/v2/fluid/tests/test_framework_debug_str.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest from paddle.v2.fluid.framework import Program diff --git a/python/paddle/v2/fluid/tests/test_ftrl_op.py b/python/paddle/v2/fluid/tests/test_ftrl_op.py index f77ac4659a9..599233efd93 100644 --- a/python/paddle/v2/fluid/tests/test_ftrl_op.py +++ b/python/paddle/v2/fluid/tests/test_ftrl_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_gather_op.py b/python/paddle/v2/fluid/tests/test_gather_op.py index b0ab429ef1b..95093f9b846 100644 --- a/python/paddle/v2/fluid/tests/test_gather_op.py +++ b/python/paddle/v2/fluid/tests/test_gather_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_gaussian_random_op.py b/python/paddle/v2/fluid/tests/test_gaussian_random_op.py index 6f6a60ccb3f..bf4785211e8 100644 --- a/python/paddle/v2/fluid/tests/test_gaussian_random_op.py +++ b/python/paddle/v2/fluid/tests/test_gaussian_random_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy diff --git a/python/paddle/v2/fluid/tests/test_get_places_op.py b/python/paddle/v2/fluid/tests/test_get_places_op.py index c4346f6786c..b44011fb76b 100644 --- a/python/paddle/v2/fluid/tests/test_get_places_op.py +++ b/python/paddle/v2/fluid/tests/test_get_places_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid as fluid import decorators import unittest diff --git a/python/paddle/v2/fluid/tests/test_gru_op.py b/python/paddle/v2/fluid/tests/test_gru_op.py index fa2c5a53ec4..a6647d1bf28 100644 --- a/python/paddle/v2/fluid/tests/test_gru_op.py +++ b/python/paddle/v2/fluid/tests/test_gru_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np import math diff --git a/python/paddle/v2/fluid/tests/test_gru_unit_op.py b/python/paddle/v2/fluid/tests/test_gru_unit_op.py index 501d5aa5797..53f10c32c7c 100644 --- a/python/paddle/v2/fluid/tests/test_gru_unit_op.py +++ b/python/paddle/v2/fluid/tests/test_gru_unit_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import math import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_hinge_loss_op.py b/python/paddle/v2/fluid/tests/test_hinge_loss_op.py index a8757a891fa..dc7774d01c0 100644 --- a/python/paddle/v2/fluid/tests/test_hinge_loss_op.py +++ b/python/paddle/v2/fluid/tests/test_hinge_loss_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_huber_loss_op.py b/python/paddle/v2/fluid/tests/test_huber_loss_op.py index a24fcbec6cc..18a48bb18ce 100644 --- a/python/paddle/v2/fluid/tests/test_huber_loss_op.py +++ b/python/paddle/v2/fluid/tests/test_huber_loss_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_image_classification_layer.py b/python/paddle/v2/fluid/tests/test_image_classification_layer.py index b621d1525e3..9d676e87594 100644 --- a/python/paddle/v2/fluid/tests/test_image_classification_layer.py +++ b/python/paddle/v2/fluid/tests/test_image_classification_layer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/test_infer_shape.py b/python/paddle/v2/fluid/tests/test_infer_shape.py index 9f6695ce02d..0c2a6f1423c 100644 --- a/python/paddle/v2/fluid/tests/test_infer_shape.py +++ b/python/paddle/v2/fluid/tests/test_infer_shape.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_inference_model_io.py b/python/paddle/v2/fluid/tests/test_inference_model_io.py index 71ca3e6c105..c5cad2166bd 100644 --- a/python/paddle/v2/fluid/tests/test_inference_model_io.py +++ b/python/paddle/v2/fluid/tests/test_inference_model_io.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_initializer.py b/python/paddle/v2/fluid/tests/test_initializer.py index 3175010f482..fa3c2afeedc 100644 --- a/python/paddle/v2/fluid/tests/test_initializer.py +++ b/python/paddle/v2/fluid/tests/test_initializer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import unittest diff --git a/python/paddle/v2/fluid/tests/test_is_empty_op.py b/python/paddle/v2/fluid/tests/test_is_empty_op.py index 0a4dd0f4faf..d6876a885f5 100644 --- a/python/paddle/v2/fluid/tests/test_is_empty_op.py +++ b/python/paddle/v2/fluid/tests/test_is_empty_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from paddle.v2.fluid.op import Operator diff --git a/python/paddle/v2/fluid/tests/test_l1_norm_op.py b/python/paddle/v2/fluid/tests/test_l1_norm_op.py index 3a1d1689fe6..92484c49f03 100644 --- a/python/paddle/v2/fluid/tests/test_l1_norm_op.py +++ b/python/paddle/v2/fluid/tests/test_l1_norm_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import unittest from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_layers.py b/python/paddle/v2/fluid/tests/test_layers.py index a56277d216c..a4e155b534a 100644 --- a/python/paddle/v2/fluid/tests/test_layers.py +++ b/python/paddle/v2/fluid/tests/test_layers.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import unittest diff --git a/python/paddle/v2/fluid/tests/test_linear_chain_crf_op.py b/python/paddle/v2/fluid/tests/test_linear_chain_crf_op.py index c26634ff20c..cd917dff7f2 100644 --- a/python/paddle/v2/fluid/tests/test_linear_chain_crf_op.py +++ b/python/paddle/v2/fluid/tests/test_linear_chain_crf_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import random import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_lod_array_length_op.py b/python/paddle/v2/fluid/tests/test_lod_array_length_op.py index 8a4be545eda..f80136cb0d8 100644 --- a/python/paddle/v2/fluid/tests/test_lod_array_length_op.py +++ b/python/paddle/v2/fluid/tests/test_lod_array_length_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.layers as layers from paddle.v2.fluid.executor import Executor diff --git a/python/paddle/v2/fluid/tests/test_lod_rank_table.py b/python/paddle/v2/fluid/tests/test_lod_rank_table.py index 30d619fe318..673605d79c7 100644 --- a/python/paddle/v2/fluid/tests/test_lod_rank_table.py +++ b/python/paddle/v2/fluid/tests/test_lod_rank_table.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.v2.fluid.layers import lod_rank_table, data from paddle.v2.fluid.executor import Executor import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_lod_reset_op.py b/python/paddle/v2/fluid/tests/test_lod_reset_op.py index 652ccecfa44..d799dbfa217 100644 --- a/python/paddle/v2/fluid/tests/test_lod_reset_op.py +++ b/python/paddle/v2/fluid/tests/test_lod_reset_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_lod_tensor_array.py b/python/paddle/v2/fluid/tests/test_lod_tensor_array.py index d6d3e23fd88..c593b1e0613 100644 --- a/python/paddle/v2/fluid/tests/test_lod_tensor_array.py +++ b/python/paddle/v2/fluid/tests/test_lod_tensor_array.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core import numpy diff --git a/python/paddle/v2/fluid/tests/test_lod_tensor_array_ops.py b/python/paddle/v2/fluid/tests/test_lod_tensor_array_ops.py index c552cb033f1..5887f9799a1 100644 --- a/python/paddle/v2/fluid/tests/test_lod_tensor_array_ops.py +++ b/python/paddle/v2/fluid/tests/test_lod_tensor_array_ops.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core import numpy diff --git a/python/paddle/v2/fluid/tests/test_log_loss_op.py b/python/paddle/v2/fluid/tests/test_log_loss_op.py index 2eeaa90758c..fde99bfaa16 100644 --- a/python/paddle/v2/fluid/tests/test_log_loss_op.py +++ b/python/paddle/v2/fluid/tests/test_log_loss_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_logical_op.py b/python/paddle/v2/fluid/tests/test_logical_op.py index ac90bf839cb..8c9e8de739f 100644 --- a/python/paddle/v2/fluid/tests/test_logical_op.py +++ b/python/paddle/v2/fluid/tests/test_logical_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import op_test import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_lookup_table_op.py b/python/paddle/v2/fluid/tests/test_lookup_table_op.py index a56a549e69e..1ff6b305bc9 100644 --- a/python/paddle/v2/fluid/tests/test_lookup_table_op.py +++ b/python/paddle/v2/fluid/tests/test_lookup_table_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_lrn_op.py b/python/paddle/v2/fluid/tests/test_lrn_op.py index 9abb09e53a7..051704617e7 100644 --- a/python/paddle/v2/fluid/tests/test_lrn_op.py +++ b/python/paddle/v2/fluid/tests/test_lrn_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_lstm_op.py b/python/paddle/v2/fluid/tests/test_lstm_op.py index 77f062e8c88..76ea8def7cb 100644 --- a/python/paddle/v2/fluid/tests/test_lstm_op.py +++ b/python/paddle/v2/fluid/tests/test_lstm_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_lstm_unit_op.py b/python/paddle/v2/fluid/tests/test_lstm_unit_op.py index 6bad2e1f7c3..c97c1e72aaa 100644 --- a/python/paddle/v2/fluid/tests/test_lstm_unit_op.py +++ b/python/paddle/v2/fluid/tests/test_lstm_unit_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_margin_rank_loss_op.py b/python/paddle/v2/fluid/tests/test_margin_rank_loss_op.py index 63378cbc4ec..3d8c1d19f90 100644 --- a/python/paddle/v2/fluid/tests/test_margin_rank_loss_op.py +++ b/python/paddle/v2/fluid/tests/test_margin_rank_loss_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_matmul_op.py b/python/paddle/v2/fluid/tests/test_matmul_op.py index d51572c8ab7..0220cfe1287 100644 --- a/python/paddle/v2/fluid/tests/test_matmul_op.py +++ b/python/paddle/v2/fluid/tests/test_matmul_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_maxout_op.py b/python/paddle/v2/fluid/tests/test_maxout_op.py index 5fbed43e254..ed8c0d2b671 100644 --- a/python/paddle/v2/fluid/tests/test_maxout_op.py +++ b/python/paddle/v2/fluid/tests/test_maxout_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_mean_op.py b/python/paddle/v2/fluid/tests/test_mean_op.py index 7823abd8f81..f9d7d6921e4 100644 --- a/python/paddle/v2/fluid/tests/test_mean_op.py +++ b/python/paddle/v2/fluid/tests/test_mean_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_memory_optimization_transpiler.py b/python/paddle/v2/fluid/tests/test_memory_optimization_transpiler.py index 5cce75ddb8d..76f3c4eb644 100644 --- a/python/paddle/v2/fluid/tests/test_memory_optimization_transpiler.py +++ b/python/paddle/v2/fluid/tests/test_memory_optimization_transpiler.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import unittest diff --git a/python/paddle/v2/fluid/tests/test_minus_op.py b/python/paddle/v2/fluid/tests/test_minus_op.py index c56d7cb5487..99c0d9056a7 100644 --- a/python/paddle/v2/fluid/tests/test_minus_op.py +++ b/python/paddle/v2/fluid/tests/test_minus_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_mnist_if_else_op.py b/python/paddle/v2/fluid/tests/test_mnist_if_else_op.py index 33558c61054..18e3991b948 100644 --- a/python/paddle/v2/fluid/tests/test_mnist_if_else_op.py +++ b/python/paddle/v2/fluid/tests/test_mnist_if_else_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.layers as layers from paddle.v2.fluid.framework import Program, program_guard, default_main_program, default_startup_program from paddle.v2.fluid.executor import Executor diff --git a/python/paddle/v2/fluid/tests/test_modified_huber_loss_op.py b/python/paddle/v2/fluid/tests/test_modified_huber_loss_op.py index 33de8ff7219..40955283e6a 100644 --- a/python/paddle/v2/fluid/tests/test_modified_huber_loss_op.py +++ b/python/paddle/v2/fluid/tests/test_modified_huber_loss_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_momentum_op.py b/python/paddle/v2/fluid/tests/test_momentum_op.py index 638095f7564..8008a5586f1 100644 --- a/python/paddle/v2/fluid/tests/test_momentum_op.py +++ b/python/paddle/v2/fluid/tests/test_momentum_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_mul_op.py b/python/paddle/v2/fluid/tests/test_mul_op.py index 57d6d7e7e09..3033b8ef70d 100644 --- a/python/paddle/v2/fluid/tests/test_mul_op.py +++ b/python/paddle/v2/fluid/tests/test_mul_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_multiplex_op.py b/python/paddle/v2/fluid/tests/test_multiplex_op.py index 5937eb5aa46..5746ab391e8 100644 --- a/python/paddle/v2/fluid/tests/test_multiplex_op.py +++ b/python/paddle/v2/fluid/tests/test_multiplex_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_nce.py b/python/paddle/v2/fluid/tests/test_nce.py index 8aeba697695..ce66a7c6b35 100644 --- a/python/paddle/v2/fluid/tests/test_nce.py +++ b/python/paddle/v2/fluid/tests/test_nce.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_net.py b/python/paddle/v2/fluid/tests/test_net.py index d9fe55a8af5..cc78cb4a56d 100644 --- a/python/paddle/v2/fluid/tests/test_net.py +++ b/python/paddle/v2/fluid/tests/test_net.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.core as core from paddle.v2.fluid.op import Operator import unittest diff --git a/python/paddle/v2/fluid/tests/test_norm_op.py b/python/paddle/v2/fluid/tests/test_norm_op.py index 7d56320489b..b053522d72b 100644 --- a/python/paddle/v2/fluid/tests/test_norm_op.py +++ b/python/paddle/v2/fluid/tests/test_norm_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_op_support_gpu.py b/python/paddle/v2/fluid/tests/test_op_support_gpu.py index a0eb4bd5fd2..741686a8746 100644 --- a/python/paddle/v2/fluid/tests/test_op_support_gpu.py +++ b/python/paddle/v2/fluid/tests/test_op_support_gpu.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_operator.py b/python/paddle/v2/fluid/tests/test_operator.py index c059a2b88b1..e75ee41149c 100644 --- a/python/paddle/v2/fluid/tests/test_operator.py +++ b/python/paddle/v2/fluid/tests/test_operator.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.op as op diff --git a/python/paddle/v2/fluid/tests/test_operator_desc.py b/python/paddle/v2/fluid/tests/test_operator_desc.py index ce34d95ac8c..ed18fafe339 100644 --- a/python/paddle/v2/fluid/tests/test_operator_desc.py +++ b/python/paddle/v2/fluid/tests/test_operator_desc.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_optimizer.py b/python/paddle/v2/fluid/tests/test_optimizer.py index 1eadb7d9126..dbec3a59441 100644 --- a/python/paddle/v2/fluid/tests/test_optimizer.py +++ b/python/paddle/v2/fluid/tests/test_optimizer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.framework as framework diff --git a/python/paddle/v2/fluid/tests/test_pad_op.py b/python/paddle/v2/fluid/tests/test_pad_op.py index 55f1774e575..1036b6bcad3 100644 --- a/python/paddle/v2/fluid/tests/test_pad_op.py +++ b/python/paddle/v2/fluid/tests/test_pad_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_parallel_op.py b/python/paddle/v2/fluid/tests/test_parallel_op.py index 6c4c39ad59c..3c190477d16 100644 --- a/python/paddle/v2/fluid/tests/test_parallel_op.py +++ b/python/paddle/v2/fluid/tests/test_parallel_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/test_parameter.py b/python/paddle/v2/fluid/tests/test_parameter.py index 694344acbbd..e0db3183450 100644 --- a/python/paddle/v2/fluid/tests/test_parameter.py +++ b/python/paddle/v2/fluid/tests/test_parameter.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest from paddle.v2.fluid.framework import default_main_program import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_pool2d_op.py b/python/paddle/v2/fluid/tests/test_pool2d_op.py index 71accc3f65b..ac8b24e7ad5 100644 --- a/python/paddle/v2/fluid/tests/test_pool2d_op.py +++ b/python/paddle/v2/fluid/tests/test_pool2d_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_pool3d_op.py b/python/paddle/v2/fluid/tests/test_pool3d_op.py index 8f410862aff..54b8df8465b 100644 --- a/python/paddle/v2/fluid/tests/test_pool3d_op.py +++ b/python/paddle/v2/fluid/tests/test_pool3d_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_pool_max_op.py b/python/paddle/v2/fluid/tests/test_pool_max_op.py index 9d2d61c4386..c4ec0e50cc9 100644 --- a/python/paddle/v2/fluid/tests/test_pool_max_op.py +++ b/python/paddle/v2/fluid/tests/test_pool_max_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_positive_negative_pair_op.py b/python/paddle/v2/fluid/tests/test_positive_negative_pair_op.py index f6a6c428a26..b75f7152efb 100644 --- a/python/paddle/v2/fluid/tests/test_positive_negative_pair_op.py +++ b/python/paddle/v2/fluid/tests/test_positive_negative_pair_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import itertools import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_precision_recall_op.py b/python/paddle/v2/fluid/tests/test_precision_recall_op.py index d3dbdb6e2ab..87c7fcb4b5f 100644 --- a/python/paddle/v2/fluid/tests/test_precision_recall_op.py +++ b/python/paddle/v2/fluid/tests/test_precision_recall_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_prelu_op.py b/python/paddle/v2/fluid/tests/test_prelu_op.py index 7be932ac8f6..38bd260bc92 100644 --- a/python/paddle/v2/fluid/tests/test_prelu_op.py +++ b/python/paddle/v2/fluid/tests/test_prelu_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_print_op.py b/python/paddle/v2/fluid/tests/test_print_op.py index 1550d0af5ed..4e42863af45 100644 --- a/python/paddle/v2/fluid/tests/test_print_op.py +++ b/python/paddle/v2/fluid/tests/test_print_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core from paddle.v2.fluid.executor import Executor diff --git a/python/paddle/v2/fluid/tests/test_profiler.py b/python/paddle/v2/fluid/tests/test_profiler.py index e3f3ac58ef9..4b439a16aa2 100644 --- a/python/paddle/v2/fluid/tests/test_profiler.py +++ b/python/paddle/v2/fluid/tests/test_profiler.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np import paddle.v2.fluid as fluid diff --git a/python/paddle/v2/fluid/tests/test_program.py b/python/paddle/v2/fluid/tests/test_program.py index 447c746aacc..bcaeede93e4 100644 --- a/python/paddle/v2/fluid/tests/test_program.py +++ b/python/paddle/v2/fluid/tests/test_program.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from __future__ import print_function import unittest diff --git a/python/paddle/v2/fluid/tests/test_protobuf.py b/python/paddle/v2/fluid/tests/test_protobuf.py index e064374176f..5f0646d0360 100644 --- a/python/paddle/v2/fluid/tests/test_protobuf.py +++ b/python/paddle/v2/fluid/tests/test_protobuf.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.proto.framework_pb2 as framework_pb2 import unittest diff --git a/python/paddle/v2/fluid/tests/test_protobuf_descs.py b/python/paddle/v2/fluid/tests/test_protobuf_descs.py index d8abe17606c..24638dc0e8a 100644 --- a/python/paddle/v2/fluid/tests/test_protobuf_descs.py +++ b/python/paddle/v2/fluid/tests/test_protobuf_descs.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_proximal_adagrad_op.py b/python/paddle/v2/fluid/tests/test_proximal_adagrad_op.py index f89a493ab7a..c197d850f97 100644 --- a/python/paddle/v2/fluid/tests/test_proximal_adagrad_op.py +++ b/python/paddle/v2/fluid/tests/test_proximal_adagrad_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_proximal_gd_op.py b/python/paddle/v2/fluid/tests/test_proximal_gd_op.py index 9ca79ce6b3b..15452558252 100644 --- a/python/paddle/v2/fluid/tests/test_proximal_gd_op.py +++ b/python/paddle/v2/fluid/tests/test_proximal_gd_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_rank_loss_op.py b/python/paddle/v2/fluid/tests/test_rank_loss_op.py index 0e41ab1b3fd..b4ba7920cd7 100644 --- a/python/paddle/v2/fluid/tests/test_rank_loss_op.py +++ b/python/paddle/v2/fluid/tests/test_rank_loss_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_recurrent_op.py b/python/paddle/v2/fluid/tests/test_recurrent_op.py index 84f4e36fa73..bcc3457aa3a 100644 --- a/python/paddle/v2/fluid/tests/test_recurrent_op.py +++ b/python/paddle/v2/fluid/tests/test_recurrent_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.layers as layers diff --git a/python/paddle/v2/fluid/tests/test_reduce_op.py b/python/paddle/v2/fluid/tests/test_reduce_op.py index a021d4dd91b..57ee307ba66 100644 --- a/python/paddle/v2/fluid/tests/test_reduce_op.py +++ b/python/paddle/v2/fluid/tests/test_reduce_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_registry.py b/python/paddle/v2/fluid/tests/test_registry.py index f8328f31cf8..dba11896307 100644 --- a/python/paddle/v2/fluid/tests/test_registry.py +++ b/python/paddle/v2/fluid/tests/test_registry.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import warnings diff --git a/python/paddle/v2/fluid/tests/test_regularizer.py b/python/paddle/v2/fluid/tests/test_regularizer.py index 890c881a126..9eaae1904a0 100644 --- a/python/paddle/v2/fluid/tests/test_regularizer.py +++ b/python/paddle/v2/fluid/tests/test_regularizer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.framework as framework diff --git a/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py b/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py index 215accd4c66..0bcdfafcf44 100644 --- a/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py +++ b/python/paddle/v2/fluid/tests/test_reorder_lod_tensor.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid as fluid import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_reshape_op.py b/python/paddle/v2/fluid/tests/test_reshape_op.py index 18ee3aece65..d6e6797043d 100644 --- a/python/paddle/v2/fluid/tests/test_reshape_op.py +++ b/python/paddle/v2/fluid/tests/test_reshape_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_rmsprop_op.py b/python/paddle/v2/fluid/tests/test_rmsprop_op.py index 237bcfcccee..27a1ea21371 100644 --- a/python/paddle/v2/fluid/tests/test_rmsprop_op.py +++ b/python/paddle/v2/fluid/tests/test_rmsprop_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_rnn_memory_helper_op.py b/python/paddle/v2/fluid/tests/test_rnn_memory_helper_op.py index d1bb20f37a3..378d7f85230 100644 --- a/python/paddle/v2/fluid/tests/test_rnn_memory_helper_op.py +++ b/python/paddle/v2/fluid/tests/test_rnn_memory_helper_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest from paddle.v2.fluid.framework import Program diff --git a/python/paddle/v2/fluid/tests/test_roi_pool_op.py b/python/paddle/v2/fluid/tests/test_roi_pool_op.py index a28d9c7f82d..6d7a698b09e 100644 --- a/python/paddle/v2/fluid/tests/test_roi_pool_op.py +++ b/python/paddle/v2/fluid/tests/test_roi_pool_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np import math diff --git a/python/paddle/v2/fluid/tests/test_row_conv_op.py b/python/paddle/v2/fluid/tests/test_row_conv_op.py index 1ed86e23ac2..1234d289cb2 100644 --- a/python/paddle/v2/fluid/tests/test_row_conv_op.py +++ b/python/paddle/v2/fluid/tests/test_row_conv_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_scale_op.py b/python/paddle/v2/fluid/tests/test_scale_op.py index 2ea1e185470..9847d3d3619 100644 --- a/python/paddle/v2/fluid/tests/test_scale_op.py +++ b/python/paddle/v2/fluid/tests/test_scale_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_scatter_op.py b/python/paddle/v2/fluid/tests/test_scatter_op.py index 1032269d5df..b6c4162f6f4 100644 --- a/python/paddle/v2/fluid/tests/test_scatter_op.py +++ b/python/paddle/v2/fluid/tests/test_scatter_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_scope.py b/python/paddle/v2/fluid/tests/test_scope.py index e4857b590aa..adaaf169062 100644 --- a/python/paddle/v2/fluid/tests/test_scope.py +++ b/python/paddle/v2/fluid/tests/test_scope.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.core import unittest diff --git a/python/paddle/v2/fluid/tests/test_selected_rows.py b/python/paddle/v2/fluid/tests/test_selected_rows.py index 93daf37aa2c..3179a3caaec 100644 --- a/python/paddle/v2/fluid/tests/test_selected_rows.py +++ b/python/paddle/v2/fluid/tests/test_selected_rows.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.core as core import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_seq_concat_op.py b/python/paddle/v2/fluid/tests/test_seq_concat_op.py index dccc6ed8afe..1f026fd76e8 100644 --- a/python/paddle/v2/fluid/tests/test_seq_concat_op.py +++ b/python/paddle/v2/fluid/tests/test_seq_concat_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np import sys diff --git a/python/paddle/v2/fluid/tests/test_seq_conv.py b/python/paddle/v2/fluid/tests/test_seq_conv.py index 14edc5f9530..c7e50851944 100644 --- a/python/paddle/v2/fluid/tests/test_seq_conv.py +++ b/python/paddle/v2/fluid/tests/test_seq_conv.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np import random diff --git a/python/paddle/v2/fluid/tests/test_seq_pool.py b/python/paddle/v2/fluid/tests/test_seq_pool.py index 512d8b315f2..bb15495373f 100644 --- a/python/paddle/v2/fluid/tests/test_seq_pool.py +++ b/python/paddle/v2/fluid/tests/test_seq_pool.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_sequence_erase_op.py b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py index bf257fefea0..650984009a7 100644 --- a/python/paddle/v2/fluid/tests/test_sequence_erase_op.py +++ b/python/paddle/v2/fluid/tests/test_sequence_erase_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_sequence_expand.py b/python/paddle/v2/fluid/tests/test_sequence_expand.py index 0f22612d3db..aacdabf295d 100644 --- a/python/paddle/v2/fluid/tests/test_sequence_expand.py +++ b/python/paddle/v2/fluid/tests/test_sequence_expand.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_sequence_slice_op.py b/python/paddle/v2/fluid/tests/test_sequence_slice_op.py index ccd9a05343b..94062431f0b 100644 --- a/python/paddle/v2/fluid/tests/test_sequence_slice_op.py +++ b/python/paddle/v2/fluid/tests/test_sequence_slice_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np import sys diff --git a/python/paddle/v2/fluid/tests/test_sequence_softmax_op.py b/python/paddle/v2/fluid/tests/test_sequence_softmax_op.py index 8bffdd58569..8170e4d7f18 100644 --- a/python/paddle/v2/fluid/tests/test_sequence_softmax_op.py +++ b/python/paddle/v2/fluid/tests/test_sequence_softmax_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_sgd_op.py b/python/paddle/v2/fluid/tests/test_sgd_op.py index 14d41e172a2..4a71fb30a9c 100644 --- a/python/paddle/v2/fluid/tests/test_sgd_op.py +++ b/python/paddle/v2/fluid/tests/test_sgd_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py b/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py index a14721b9aac..1825a5258fa 100644 --- a/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py +++ b/python/paddle/v2/fluid/tests/test_shrink_rnn_memory.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core from paddle.v2.fluid.executor import Executor diff --git a/python/paddle/v2/fluid/tests/test_sigmoid_cross_entropy_with_logits_op.py b/python/paddle/v2/fluid/tests/test_sigmoid_cross_entropy_with_logits_op.py index c42f578f72c..132502c9cba 100644 --- a/python/paddle/v2/fluid/tests/test_sigmoid_cross_entropy_with_logits_op.py +++ b/python/paddle/v2/fluid/tests/test_sigmoid_cross_entropy_with_logits_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np from op_test import OpTest from scipy.special import logit diff --git a/python/paddle/v2/fluid/tests/test_sign_op.py b/python/paddle/v2/fluid/tests/test_sign_op.py index c6b59bcfd8b..f649cb9e7cd 100644 --- a/python/paddle/v2/fluid/tests/test_sign_op.py +++ b/python/paddle/v2/fluid/tests/test_sign_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_smooth_l1_loss_op.py b/python/paddle/v2/fluid/tests/test_smooth_l1_loss_op.py index b7f13c56999..1052eaa8b0e 100644 --- a/python/paddle/v2/fluid/tests/test_smooth_l1_loss_op.py +++ b/python/paddle/v2/fluid/tests/test_smooth_l1_loss_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_softmax_op.py b/python/paddle/v2/fluid/tests/test_softmax_op.py index 136fc0283af..d03e50b2f1e 100644 --- a/python/paddle/v2/fluid/tests/test_softmax_op.py +++ b/python/paddle/v2/fluid/tests/test_softmax_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_softmax_with_cross_entropy_op.py b/python/paddle/v2/fluid/tests/test_softmax_with_cross_entropy_op.py index c2f07f9096c..330467081b4 100644 --- a/python/paddle/v2/fluid/tests/test_softmax_with_cross_entropy_op.py +++ b/python/paddle/v2/fluid/tests/test_softmax_with_cross_entropy_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_split_and_merge_lod_tensor_op.py b/python/paddle/v2/fluid/tests/test_split_and_merge_lod_tensor_op.py index 2e4defd55d7..4e90404eca4 100644 --- a/python/paddle/v2/fluid/tests/test_split_and_merge_lod_tensor_op.py +++ b/python/paddle/v2/fluid/tests/test_split_and_merge_lod_tensor_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.core as core import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_split_op.py b/python/paddle/v2/fluid/tests/test_split_op.py index 37c6ebb89d1..000c300446f 100644 --- a/python/paddle/v2/fluid/tests/test_split_op.py +++ b/python/paddle/v2/fluid/tests/test_split_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_spp_op.py b/python/paddle/v2/fluid/tests/test_spp_op.py index 007723f0e35..f09bb94b449 100644 --- a/python/paddle/v2/fluid/tests/test_spp_op.py +++ b/python/paddle/v2/fluid/tests/test_spp_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_squared_l2_distance_op.py b/python/paddle/v2/fluid/tests/test_squared_l2_distance_op.py index dc6ebf5d303..7b80d81d728 100644 --- a/python/paddle/v2/fluid/tests/test_squared_l2_distance_op.py +++ b/python/paddle/v2/fluid/tests/test_squared_l2_distance_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_squared_l2_norm_op.py b/python/paddle/v2/fluid/tests/test_squared_l2_norm_op.py index 5a52c6a66c7..80994f5937e 100644 --- a/python/paddle/v2/fluid/tests/test_squared_l2_norm_op.py +++ b/python/paddle/v2/fluid/tests/test_squared_l2_norm_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy as np import unittest from numpy import linalg as LA diff --git a/python/paddle/v2/fluid/tests/test_sum_op.py b/python/paddle/v2/fluid/tests/test_sum_op.py index 60254291e2a..366708ac839 100644 --- a/python/paddle/v2/fluid/tests/test_sum_op.py +++ b/python/paddle/v2/fluid/tests/test_sum_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_tensor.py b/python/paddle/v2/fluid/tests/test_tensor.py index 9f870d9eb34..62a48b206cd 100644 --- a/python/paddle/v2/fluid/tests/test_tensor.py +++ b/python/paddle/v2/fluid/tests/test_tensor.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2.fluid.core as core import unittest import numpy diff --git a/python/paddle/v2/fluid/tests/test_top_k_op.py b/python/paddle/v2/fluid/tests/test_top_k_op.py index 6e8fbefa6ea..86968dba140 100644 --- a/python/paddle/v2/fluid/tests/test_top_k_op.py +++ b/python/paddle/v2/fluid/tests/test_top_k_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_transpose_op.py b/python/paddle/v2/fluid/tests/test_transpose_op.py index 9409cbaa00f..ff2541f450c 100644 --- a/python/paddle/v2/fluid/tests/test_transpose_op.py +++ b/python/paddle/v2/fluid/tests/test_transpose_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_uniform_random_op.py b/python/paddle/v2/fluid/tests/test_uniform_random_op.py index dbe4d6bcd06..332ac4f07ff 100644 --- a/python/paddle/v2/fluid/tests/test_uniform_random_op.py +++ b/python/paddle/v2/fluid/tests/test_uniform_random_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy diff --git a/python/paddle/v2/fluid/tests/test_unpool_op.py b/python/paddle/v2/fluid/tests/test_unpool_op.py index e87f283042c..988c0c75063 100644 --- a/python/paddle/v2/fluid/tests/test_unpool_op.py +++ b/python/paddle/v2/fluid/tests/test_unpool_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import numpy as np from op_test import OpTest diff --git a/python/paddle/v2/fluid/tests/test_variable.py b/python/paddle/v2/fluid/tests/test_variable.py index f1e4c0ba21d..199fd4a8c26 100644 --- a/python/paddle/v2/fluid/tests/test_variable.py +++ b/python/paddle/v2/fluid/tests/test_variable.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest from paddle.v2.fluid.framework import default_main_program, Program, convert_np_dtype_to_dtype_ import paddle.v2.fluid.core as core diff --git a/python/paddle/v2/fluid/tests/test_warpctc_op.py b/python/paddle/v2/fluid/tests/test_warpctc_op.py index 59390d5303b..272e52c982a 100644 --- a/python/paddle/v2/fluid/tests/test_warpctc_op.py +++ b/python/paddle/v2/fluid/tests/test_warpctc_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import sys import unittest import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_while_op.py b/python/paddle/v2/fluid/tests/test_while_op.py index 7c5593cc5e5..72de0a03612 100644 --- a/python/paddle/v2/fluid/tests/test_while_op.py +++ b/python/paddle/v2/fluid/tests/test_while_op.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2.fluid.layers as layers from paddle.v2.fluid.executor import Executor diff --git a/python/paddle/v2/image.py b/python/paddle/v2/image.py index 7408ea8ef61..a6fa0cecb87 100644 --- a/python/paddle/v2/image.py +++ b/python/paddle/v2/image.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ This file contains some common interfaces for image preprocess. Many users are confused about the image layout. We introduce diff --git a/python/paddle/v2/inference.py b/python/paddle/v2/inference.py index 9148cb56cf7..39d1bfff0c8 100644 --- a/python/paddle/v2/inference.py +++ b/python/paddle/v2/inference.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import numpy import collections import topology diff --git a/python/paddle/v2/master/__init__.py b/python/paddle/v2/master/__init__.py index c8975b5d4a3..09daaaa75e0 100644 --- a/python/paddle/v2/master/__init__.py +++ b/python/paddle/v2/master/__init__.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from client import * __all__ = ['client'] diff --git a/python/paddle/v2/master/client.py b/python/paddle/v2/master/client.py index fc718f031e2..b874c2f3490 100644 --- a/python/paddle/v2/master/client.py +++ b/python/paddle/v2/master/client.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import ctypes import os diff --git a/python/paddle/v2/reader/tests/__init__.py b/python/paddle/v2/reader/tests/__init__.py index e69de29bb2d..2619c1c0e9d 100644 --- a/python/paddle/v2/reader/tests/__init__.py +++ b/python/paddle/v2/reader/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. diff --git a/python/paddle/v2/tests/test_parameters.py b/python/paddle/v2/tests/test_parameters.py index 7ba8a939fbd..ab6863620fe 100644 --- a/python/paddle/v2/tests/test_parameters.py +++ b/python/paddle/v2/tests/test_parameters.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import sys diff --git a/python/paddle/v2/trainer.py b/python/paddle/v2/trainer.py index db01ab7374e..1a70a7203b7 100644 --- a/python/paddle/v2/trainer.py +++ b/python/paddle/v2/trainer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ Module Trainer """ diff --git a/tools/manylinux1/build_scripts/manylinux1-check.py b/tools/manylinux1/build_scripts/manylinux1-check.py index 47fd3d673be..e4bde065a29 100644 --- a/tools/manylinux1/build_scripts/manylinux1-check.py +++ b/tools/manylinux1/build_scripts/manylinux1-check.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # Logic copied from PEP 513 diff --git a/tools/manylinux1/build_scripts/ssl-check.py b/tools/manylinux1/build_scripts/ssl-check.py index a85d91978c5..900185cef14 100644 --- a/tools/manylinux1/build_scripts/ssl-check.py +++ b/tools/manylinux1/build_scripts/ssl-check.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # cf. https://github.com/pypa/manylinux/issues/53 GOOD_SSL = "https://google.com" diff --git a/v1_api_demo/mnist/api_train.py b/v1_api_demo/mnist/api_train.py index ea1caa7dd96..e42c6cbb7e0 100644 --- a/v1_api_demo/mnist/api_train.py +++ b/v1_api_demo/mnist/api_train.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. """ A very basic example for how to use current Raw SWIG API to train mnist network. diff --git a/v1_api_demo/mnist/mnist_provider.py b/v1_api_demo/mnist/mnist_provider.py index 888cfef1e7e..41923398376 100644 --- a/v1_api_demo/mnist/mnist_provider.py +++ b/v1_api_demo/mnist/mnist_provider.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from paddle.trainer.PyDataProvider2 import * from mnist_util import read_from_mnist diff --git a/v1_api_demo/quick_start/data/proc_from_raw_data/preprocess.py b/v1_api_demo/quick_start/data/proc_from_raw_data/preprocess.py index 72bd95f21d8..5706351a21f 100755 --- a/v1_api_demo/quick_start/data/proc_from_raw_data/preprocess.py +++ b/v1_api_demo/quick_start/data/proc_from_raw_data/preprocess.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # -*- coding: UTF-8 -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved diff --git a/v1_api_demo/quick_start/trainer_config.bidi-lstm.py b/v1_api_demo/quick_start/trainer_config.bidi-lstm.py index ca1d1f8d099..3deff4aa00b 100644 --- a/v1_api_demo/quick_start/trainer_config.bidi-lstm.py +++ b/v1_api_demo/quick_start/trainer_config.bidi-lstm.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved diff --git a/v1_api_demo/quick_start/trainer_config.cnn.py b/v1_api_demo/quick_start/trainer_config.cnn.py index f8c3d511f32..e09e41484d3 100644 --- a/v1_api_demo/quick_start/trainer_config.cnn.py +++ b/v1_api_demo/quick_start/trainer_config.cnn.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved diff --git a/v1_api_demo/quick_start/trainer_config.emb.py b/v1_api_demo/quick_start/trainer_config.emb.py index 7410397ef65..f69f98ff7fc 100644 --- a/v1_api_demo/quick_start/trainer_config.emb.py +++ b/v1_api_demo/quick_start/trainer_config.emb.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved diff --git a/v1_api_demo/quick_start/trainer_config.lr.py b/v1_api_demo/quick_start/trainer_config.lr.py index e5105aa8953..b7b694940e3 100644 --- a/v1_api_demo/quick_start/trainer_config.lr.py +++ b/v1_api_demo/quick_start/trainer_config.lr.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved diff --git a/v1_api_demo/quick_start/trainer_config.lstm.py b/v1_api_demo/quick_start/trainer_config.lstm.py index 43b4ddac2dc..8967d78807b 100644 --- a/v1_api_demo/quick_start/trainer_config.lstm.py +++ b/v1_api_demo/quick_start/trainer_config.lstm.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved diff --git a/v1_api_demo/quick_start/trainer_config.resnet-lstm.py b/v1_api_demo/quick_start/trainer_config.resnet-lstm.py index 89a837abb7c..32d0596f250 100644 --- a/v1_api_demo/quick_start/trainer_config.resnet-lstm.py +++ b/v1_api_demo/quick_start/trainer_config.resnet-lstm.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. # edit-mode: -*- python -*- # Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -- GitLab From c01bb26f1d623a9180ade5ba53316321ff13685c Mon Sep 17 00:00:00 2001 From: yangyaming Date: Mon, 15 Jan 2018 21:04:15 +0800 Subject: [PATCH 0282/2305] Add reorder flag for DynamicRNN's memory function. --- python/paddle/v2/fluid/layers/control_flow.py | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/python/paddle/v2/fluid/layers/control_flow.py b/python/paddle/v2/fluid/layers/control_flow.py index ee97e5f4e69..182b62a3afa 100644 --- a/python/paddle/v2/fluid/layers/control_flow.py +++ b/python/paddle/v2/fluid/layers/control_flow.py @@ -1310,20 +1310,44 @@ class DynamicRNN(object): else: return self.outputs - def memory(self, init=None, shape=None, value=0.0, dtype='float32'): + def memory(self, + init=None, + shape=None, + value=0.0, + need_reorder=False, + dtype='float32'): self._assert_in_rnn_block_('memory') if init is not None: if not isinstance(init, Variable): raise TypeError( "The input arg `init` of memory() must be a Variable") parent_block = self._parent_block_() + init_tensor = init + if need_reorder == True: + if self.lod_rank_table is None: + raise ValueError( + 'If set need_reorder to True, make sure step_input be ' + 'invoked before ' + 'memory(init=init, need_reordered=True, ...).') + init_reordered = parent_block.create_var( + name=unique_name('dynamic_rnn_mem_init_reordered'), + type=core.VarDesc.VarType.LOD_TENSOR, + dtype=init.dtype) + parent_block.append_op( + type='reorder_lod_tensor_by_rank', + inputs={ + 'X': [init_tensor], + 'RankTable': [self.lod_rank_table] + }, + outputs={'Out': [init_reordered]}) + init_tensor = init_reordered mem_array = parent_block.create_var( name=unique_name('dynamic_rnn_mem_array'), type=core.VarDesc.VarType.LOD_TENSOR_ARRAY, dtype=init.dtype) parent_block.append_op( type='write_to_array', - inputs={'X': init, + inputs={'X': init_tensor, 'I': self.zero_idx}, outputs={'Out': mem_array}) retv = array_read(array=mem_array, i=self.step_idx) -- GitLab From 535fefb7e8d94731618d3c36404069b36ec43d28 Mon Sep 17 00:00:00 2001 From: gongweibao Date: Mon, 15 Jan 2018 22:17:54 +0800 Subject: [PATCH 0283/2305] Fix grpc bugs (#7435) Fix grpc bugs --- cmake/external/grpc.cmake | 2 +- paddle/operators/detail/grpc_client.cc | 16 ++++++++---- paddle/operators/detail/grpc_client.h | 2 +- paddle/operators/detail/grpc_server.cc | 35 ++++++++++++++------------ paddle/operators/detail/grpc_server.h | 1 - paddle/operators/recv_op.cc | 2 ++ paddle/operators/send_op.cc | 2 +- 7 files changed, 35 insertions(+), 25 deletions(-) diff --git a/cmake/external/grpc.cmake b/cmake/external/grpc.cmake index abee6698e30..79b2449fe66 100644 --- a/cmake/external/grpc.cmake +++ b/cmake/external/grpc.cmake @@ -33,7 +33,7 @@ ExternalProject_Add( extern_grpc DEPENDS protobuf zlib GIT_REPOSITORY "https://github.com/grpc/grpc.git" - GIT_TAG "v1.7.x" + GIT_TAG "v1.8.x" PREFIX ${GRPC_SOURCES_DIR} UPDATE_COMMAND "" CONFIGURE_COMMAND "" diff --git a/paddle/operators/detail/grpc_client.cc b/paddle/operators/detail/grpc_client.cc index 5a4db2d7e68..aee56ffe018 100644 --- a/paddle/operators/detail/grpc_client.cc +++ b/paddle/operators/detail/grpc_client.cc @@ -87,7 +87,7 @@ bool RPCClient::AsyncGetVariable(const std::string& ep, return true; } -bool RPCClient::wait() { +bool RPCClient::Wait() { bool ok = true; while (true) { @@ -96,7 +96,6 @@ bool RPCClient::wait() { } if (!Proceed()) { - LOG(ERROR) << "Get meets CompletionQueue error"; return false; } } @@ -110,9 +109,9 @@ bool RPCClient::Proceed() { // request counts. if (!cq_.Next(&tag, &ok)) { + LOG(ERROR) << "Get meets CompletionQueue error"; return false; } - req_count_--; GPR_ASSERT(ok); PADDLE_ENFORCE(tag); @@ -120,12 +119,15 @@ bool RPCClient::Proceed() { // TODO(gongwb): add more retries. ClientBase* c = static_cast(tag); if (!c->status_.ok()) { + LOG(ERROR) << "proc param error:" << c->var_h_.String() + << " grpc error:" << c->status_.error_message(); delete c; - return true; + return false; } c->Process(); delete c; + req_count_--; return true; } @@ -135,8 +137,12 @@ std::shared_ptr RPCClient::GetChannel(const std::string& ep) { return it->second; } + grpc::ChannelArguments args; + args.SetMaxSendMessageSize(std::numeric_limits::max()); + args.SetMaxReceiveMessageSize(std::numeric_limits::max()); + auto ch = std::shared_ptr( - grpc::CreateChannel(ep, grpc::InsecureChannelCredentials())); + grpc::CreateCustomChannel(ep, grpc::InsecureChannelCredentials(), args)); channels_[ep] = ch; return ch; diff --git a/paddle/operators/detail/grpc_client.h b/paddle/operators/detail/grpc_client.h index d27b5ced9ec..a62e70a2533 100644 --- a/paddle/operators/detail/grpc_client.h +++ b/paddle/operators/detail/grpc_client.h @@ -130,7 +130,7 @@ class RPCClient { const framework::Scope& scope, const std::string& var_name, int64_t time_out = 600 * 1000); - bool wait(); + bool Wait(); private: bool Proceed(); diff --git a/paddle/operators/detail/grpc_server.cc b/paddle/operators/detail/grpc_server.cc index e8d561a57ff..ac4bb5cb822 100644 --- a/paddle/operators/detail/grpc_server.cc +++ b/paddle/operators/detail/grpc_server.cc @@ -28,12 +28,15 @@ class RequestBase { public: explicit RequestBase(sendrecv::SendRecvService::AsyncService* service, grpc::ServerCompletionQueue* cq) - : service_(service), cq_(cq), status_(PROCESS) {} + : service_(service), cq_(cq), status_(PROCESS) { + PADDLE_ENFORCE(cq_); + } virtual ~RequestBase() {} virtual void Process() { assert(false); } CallStatus Status() { return status_; } void SetStatus(CallStatus status) { status_ = status; } + virtual std::string GetReqName() { assert(false); } protected: grpc::ServerContext ctx_; @@ -56,12 +59,14 @@ class RequestSend final : public RequestBase { virtual ~RequestSend() {} + virtual std::string GetReqName() { return request_.varname(); } + virtual void Process() { MessageWithName msg_with_name = std::make_pair(request_.varname(), std::move(request_)); queue_->Push(std::move(msg_with_name)); - // TODO(gongwb): check var's info. responder_.Finish(reply_, grpc::Status::OK, this); + status_ = FINISH; } protected: @@ -81,6 +86,8 @@ class RequestGet final : public RequestBase { virtual ~RequestGet() {} + virtual std::string GetReqName() { return request_.varname(); } + virtual void Process() { // proc request. std::string var_name = request_.varname(); @@ -88,6 +95,7 @@ class RequestGet final : public RequestBase { SerializeToMessage(var_name, var, platform::CPUDeviceContext(), &reply_); // TODO(gongwb): check var's info. responder_.Finish(reply_, grpc::Status::OK, this); + status_ = FINISH; } protected: @@ -100,6 +108,8 @@ class RequestGet final : public RequestBase { void AsyncGRPCServer::RunSyncUpdate() { grpc::ServerBuilder builder; builder.AddListeningPort(address_, grpc::InsecureServerCredentials()); + builder.SetMaxSendMessageSize(std::numeric_limits::max()); + builder.SetMaxReceiveMessageSize(std::numeric_limits::max()); builder.RegisterService(&service_); cq_send_ = builder.AddCompletionQueue(); @@ -159,18 +169,6 @@ void AsyncGRPCServer::TryToRegisterNewGetOne() { VLOG(4) << "create Requestget status:" << get->Status(); } -void AsyncGRPCServer::SetFinishOrDelete(RequestBase*& last) { - std::unique_lock lock(cq_mutex_); - if (is_shut_down_) { - delete last; - last = NULL; - return; - } - - last->SetStatus(FINISH); - return; -} - void AsyncGRPCServer::HandleRequest(bool wait, grpc::ServerCompletionQueue* cq, std::string cq_name, std::function TryToRegisterNewOne) { @@ -184,13 +182,19 @@ void AsyncGRPCServer::HandleRequest(bool wait, grpc::ServerCompletionQueue* cq, break; } + PADDLE_ENFORCE(tag); if (wait && !done_) { Wait(); } RequestBase* base = (RequestBase*)tag; + // reference: + // https://github.com/tensorflow/tensorflow/issues/5596 + // https://groups.google.com/forum/#!topic/grpc-io/xftlRy-IQwM + // https://groups.google.com/forum/#!topic/grpc-io/ywATt88Ef_I if (!ok) { - VLOG(4) << cq_name << " recv no regular event"; + LOG(WARNING) << cq_name << " recv no regular event:argument name" + << base->GetReqName(); TryToRegisterNewOne(); delete base; continue; @@ -201,7 +205,6 @@ void AsyncGRPCServer::HandleRequest(bool wait, grpc::ServerCompletionQueue* cq, VLOG(4) << cq_name << " status:" << base->Status(); TryToRegisterNewOne(); base->Process(); - SetFinishOrDelete(base); break; } case FINISH: { diff --git a/paddle/operators/detail/grpc_server.h b/paddle/operators/detail/grpc_server.h index 041fe05b2e9..694e18ef498 100644 --- a/paddle/operators/detail/grpc_server.h +++ b/paddle/operators/detail/grpc_server.h @@ -60,7 +60,6 @@ class AsyncGRPCServer final : public sendrecv::SendRecvService::Service { std::function TryToRegisterNewOne); void TryToRegisterNewSendOne(); void TryToRegisterNewGetOne(); - void SetFinishOrDelete(RequestBase *&last); void ShutdownQueue(); private: diff --git a/paddle/operators/recv_op.cc b/paddle/operators/recv_op.cc index 55b33343af4..cf69c12b686 100644 --- a/paddle/operators/recv_op.cc +++ b/paddle/operators/recv_op.cc @@ -96,6 +96,8 @@ class RecvOp : public framework::OperatorBase { rpc_service_->Reset(); // TODO(typhoonzero): change this to a while_op for every cluster-batch. bool exit_flag = false; + VLOG(4) << "param_count:" << param_count + << " trainer_count:" << trainer_count; while (!exit_flag) { // TODO(gognwb): simply this loop. // Get from multiple trainers, we don't care about order in which diff --git a/paddle/operators/send_op.cc b/paddle/operators/send_op.cc index 4d145250bdc..203000f5aaf 100644 --- a/paddle/operators/send_op.cc +++ b/paddle/operators/send_op.cc @@ -48,7 +48,7 @@ class SendOp : public framework::OperatorBase { client_.AsyncGetVariable(epmap[i], ctx, scope, outs[i]); } - client_.wait(); + PADDLE_ENFORCE(client_.Wait()); } private: -- GitLab From ba2f6f71ad6e8a9518d53cd9df961ee070651625 Mon Sep 17 00:00:00 2001 From: sidgoyal78 Date: Mon, 15 Jan 2018 16:32:50 -0800 Subject: [PATCH 0284/2305] Modify directory structure to show plots --- paddle/operators/op_documentation/batch_norm_op.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/paddle/operators/op_documentation/batch_norm_op.md b/paddle/operators/op_documentation/batch_norm_op.md index 80948adf2b9..d1392619c42 100644 --- a/paddle/operators/op_documentation/batch_norm_op.md +++ b/paddle/operators/op_documentation/batch_norm_op.md @@ -66,7 +66,7 @@ As most C++ operators do, `batch_norm_op` is defined by inputs, outputs, attribu The following graph showes the training computational process of `batch_norm_op`: - + cudnn provides APIs to finish the whole series of computation, we can use them in our GPU kernel. @@ -124,7 +124,7 @@ for pass_id in range(PASS_NUM): `is_infer` is an attribute. Once an operator is created, its attributes can not be changed. It suggests us that we shall maintain two `batch_norm_op` in the model, one's `is_infer` is `True`(we call it `infer_batch_norm_op`) and the other one's is `False`(we call it `train_batch_norm_op`). They share all parameters and variables, but be placed in two different branches. That is to say, if a network contains a `batch_norm_op`, it will fork into two branches, one go through `train_batch_norm_op` and the other one go through `infer_batch_norm_op`:

- +
Just like what is shown in the above graph, the net forks before `batch_norm_op` and will never merge again. All the operators after `batch_norm_op` will duplicate. -- GitLab From bbff57e085675edefc27f6bdc34e8baac5b59a05 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Tue, 16 Jan 2018 08:52:06 +0800 Subject: [PATCH 0285/2305] update docker file --- benchmark/cluster/v2/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/cluster/v2/Dockerfile b/benchmark/cluster/v2/Dockerfile index c52acd51a20..3377cf0100a 100644 --- a/benchmark/cluster/v2/Dockerfile +++ b/benchmark/cluster/v2/Dockerfile @@ -1,4 +1,4 @@ -FROM registry.baidu.com/paddlepaddle/rawjob +FROM paddlepaddle/paddlecloud-job RUN mkdir -p /workspace && mkdir -p /root/.cache/paddle/dataset/flowers/ ADD vgg16.py reader.py /workspace/ ADD 102flowers.tgz imagelabels.mat setid.mat /root/.cache/paddle/dataset/flowers/ -- GitLab From 9ad149a928e1c9916ffd421bf9e365045108c482 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Tue, 16 Jan 2018 08:54:21 +0800 Subject: [PATCH 0286/2305] fix copyright check --- benchmark/cluster/v2/reader.py | 14 ++++++++++++++ benchmark/cluster/v2/vgg16.py | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/benchmark/cluster/v2/reader.py b/benchmark/cluster/v2/reader.py index a5a2d54841c..060bf2bda2f 100644 --- a/benchmark/cluster/v2/reader.py +++ b/benchmark/cluster/v2/reader.py @@ -1,3 +1,17 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. + import random from paddle.v2.image import load_and_transform import paddle.v2 as paddle diff --git a/benchmark/cluster/v2/vgg16.py b/benchmark/cluster/v2/vgg16.py index 699fc07628f..dc9573bd799 100644 --- a/benchmark/cluster/v2/vgg16.py +++ b/benchmark/cluster/v2/vgg16.py @@ -1,3 +1,17 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. + import gzip import paddle.v2.dataset.flowers as flowers -- GitLab From 24144bdb2c82f696c3eba65a2ab629756570b444 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Tue, 16 Jan 2018 10:31:23 +0800 Subject: [PATCH 0287/2305] Add notice about norm_by_times and change 'ctc' to 'CTC' --- python/paddle/v2/fluid/layers/nn.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 97056570ee1..8a9c42b4302 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -1508,10 +1508,11 @@ def reduce_min(input, dim=None, keep_dim=False): def warpctc(input, label, blank=0, norm_by_times=False, **kwargs): """ - An operator integrating the open source warp-ctc library + An operator integrating the open source Warp-CTC library + (https://github.com/baidu-research/warp-ctc) to compute Connectionist Temporal Classification (CTC) loss. - It can be aliased as softmax with ctc, since a native softmax activation is - interated to the warp-ctc library, to to normlize values for each row of the + It can be aliased as softmax with CTC, since a native softmax activation is + interated to the Warp-CTC library, to to normlize values for each row of the input tensor. Args: @@ -1525,12 +1526,12 @@ def warpctc(input, label, blank=0, norm_by_times=False, **kwargs): of variable-length sequence, which is a 2-D Tensor with LoD information. It is of the shape [Lg, 1], where Lg is th sum of all labels' length. - blank: (int, default: 0), the blank label of Connectionist + blank: (int, default: 0), the blank label index of Connectionist Temporal Classification (CTC) loss, which is in the half-opened interval [0, num_classes + 1). - norm_by_times: (bool, default: false), whether to - normalize the gradients by the number of time-step, - which is also the sequence's length. + norm_by_times: (bool, default: false), whether to normalize the gradients + by the number of time-step,which is also the sequence's length. + There is no need to normalize the gradients if warpctc layer was follewed by a mean_op. Returns: Variable: The Connectionist Temporal Classification (CTC) loss, which is a 2-D Tensor of the shape [batch_size, 1]. -- GitLab From 311d159e11a004c11676a47a5e7945dfadc718b5 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Tue, 16 Jan 2018 11:02:14 +0800 Subject: [PATCH 0288/2305] add copyright for newly merged files --- benchmark/cluster/v2/Dockerfile | 3 ++- benchmark/cluster/v2/vgg16.py | 2 +- benchmark/tensorflow/image/googlenet_multi_gpu.py | 13 +++++++++++++ doc/getstarted/concepts/src/infer.py | 13 +++++++++++++ paddle/gserver/layers/MultiBoxLossLayer.h | 13 +++++++++++++ .../v2/fluid/tests/test_dynrnn_static_input.py | 13 +++++++++++++ 6 files changed, 55 insertions(+), 2 deletions(-) diff --git a/benchmark/cluster/v2/Dockerfile b/benchmark/cluster/v2/Dockerfile index 3377cf0100a..32e68b6150f 100644 --- a/benchmark/cluster/v2/Dockerfile +++ b/benchmark/cluster/v2/Dockerfile @@ -1,4 +1,5 @@ FROM paddlepaddle/paddlecloud-job RUN mkdir -p /workspace && mkdir -p /root/.cache/paddle/dataset/flowers/ ADD vgg16.py reader.py /workspace/ -ADD 102flowers.tgz imagelabels.mat setid.mat /root/.cache/paddle/dataset/flowers/ +COPY 102flowers.tgz imagelabels.mat setid.mat /root/.cache/paddle/dataset/flowers/ + diff --git a/benchmark/cluster/v2/vgg16.py b/benchmark/cluster/v2/vgg16.py index dc9573bd799..8644a547b33 100644 --- a/benchmark/cluster/v2/vgg16.py +++ b/benchmark/cluster/v2/vgg16.py @@ -74,7 +74,7 @@ def vgg19(input, class_dim): def main(): - paddle.init(use_gpu=True, trainer_count=1) + paddle.init(use_gpu=False, trainer_count=1) image = paddle.layer.data( name="image", type=paddle.data_type.dense_vector(DATA_DIM)) lbl = paddle.layer.data( diff --git a/benchmark/tensorflow/image/googlenet_multi_gpu.py b/benchmark/tensorflow/image/googlenet_multi_gpu.py index 31466faa37c..44de3800a8a 100644 --- a/benchmark/tensorflow/image/googlenet_multi_gpu.py +++ b/benchmark/tensorflow/image/googlenet_multi_gpu.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from six.moves import xrange # pylint: disable=redefined-builtin from datetime import datetime import math diff --git a/doc/getstarted/concepts/src/infer.py b/doc/getstarted/concepts/src/infer.py index 4cc58dfee0b..ee71cd7a9a4 100644 --- a/doc/getstarted/concepts/src/infer.py +++ b/doc/getstarted/concepts/src/infer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2 as paddle import numpy as np diff --git a/paddle/gserver/layers/MultiBoxLossLayer.h b/paddle/gserver/layers/MultiBoxLossLayer.h index 9935da56446..40df312a254 100644 --- a/paddle/gserver/layers/MultiBoxLossLayer.h +++ b/paddle/gserver/layers/MultiBoxLossLayer.h @@ -1,3 +1,16 @@ +// Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* copyright (c) 2016 paddlepaddle authors. all rights reserve. licensed under the apache license, version 2.0 (the "license"); diff --git a/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py index 9b138a6207f..d6878f0b6d0 100644 --- a/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py +++ b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2 as paddle import paddle.v2.fluid.core as core -- GitLab From 4031e88dc4ec6559dd78e134a2830b7251759b1a Mon Sep 17 00:00:00 2001 From: dzhwinter Date: Mon, 15 Jan 2018 19:21:31 -0800 Subject: [PATCH 0289/2305] "fix the copyright hook" --- .copyright.hook | 2 +- benchmark/tensorflow/image/googlenet_multi_gpu.py | 13 +++++++++++++ doc/getstarted/concepts/src/infer.py | 13 +++++++++++++ .../v2/fluid/tests/test_dynrnn_static_input.py | 13 +++++++++++++ 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/.copyright.hook b/.copyright.hook index e28e88e2664..de97ce90ac4 100644 --- a/.copyright.hook +++ b/.copyright.hook @@ -90,7 +90,7 @@ def main(argv=None): retv = 0 for filename in args.filenames: first_line = io.open(filename).readline() - if "Copyright" in first_line: continue + if "COPYRIGHT" in first_line.upper() : continue original_contents = io.open(filename).read() new_contents = generate_copyright( COPYRIGHT, lang_type(filename)) + original_contents diff --git a/benchmark/tensorflow/image/googlenet_multi_gpu.py b/benchmark/tensorflow/image/googlenet_multi_gpu.py index 31466faa37c..44de3800a8a 100644 --- a/benchmark/tensorflow/image/googlenet_multi_gpu.py +++ b/benchmark/tensorflow/image/googlenet_multi_gpu.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. from six.moves import xrange # pylint: disable=redefined-builtin from datetime import datetime import math diff --git a/doc/getstarted/concepts/src/infer.py b/doc/getstarted/concepts/src/infer.py index 4cc58dfee0b..ee71cd7a9a4 100644 --- a/doc/getstarted/concepts/src/infer.py +++ b/doc/getstarted/concepts/src/infer.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import paddle.v2 as paddle import numpy as np diff --git a/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py index 9b138a6207f..d6878f0b6d0 100644 --- a/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py +++ b/python/paddle/v2/fluid/tests/test_dynrnn_static_input.py @@ -1,3 +1,16 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. import unittest import paddle.v2 as paddle import paddle.v2.fluid.core as core -- GitLab From f382aa773c7d91bf517a2ff9cbd958349ae862d5 Mon Sep 17 00:00:00 2001 From: gx_wind Date: Tue, 16 Jan 2018 14:13:50 +0800 Subject: [PATCH 0290/2305] move adversarial directory to PaddlePaddle model repo --- adversarial/README.md | 9 -- adversarial/advbox/__init__.py | 16 --- adversarial/advbox/attacks/base.py | 52 ---------- adversarial/advbox/attacks/gradientsign.py | 51 --------- adversarial/advbox/models/__init__.py | 16 --- adversarial/advbox/models/base.py | 103 ------------------- adversarial/advbox/models/paddle.py | 114 --------------------- adversarial/fluid_mnist.py | 99 ------------------ adversarial/mnist_tutorial_fgsm.py | 100 ------------------ 9 files changed, 560 deletions(-) delete mode 100644 adversarial/README.md delete mode 100644 adversarial/advbox/__init__.py delete mode 100644 adversarial/advbox/attacks/base.py delete mode 100644 adversarial/advbox/attacks/gradientsign.py delete mode 100644 adversarial/advbox/models/__init__.py delete mode 100644 adversarial/advbox/models/base.py delete mode 100644 adversarial/advbox/models/paddle.py delete mode 100644 adversarial/fluid_mnist.py delete mode 100644 adversarial/mnist_tutorial_fgsm.py diff --git a/adversarial/README.md b/adversarial/README.md deleted file mode 100644 index 51da21918a9..00000000000 --- a/adversarial/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Advbox - -Advbox is a Python toolbox to create adversarial examples that fool neural networks. It requires Python and paddle. - -## How to use - -1. train a model and save it's parameters. (like fluid_mnist.py) -2. load the parameters which is trained in step1, then reconstruct the model.(like mnist_tutorial_fgsm.py) -3. use advbox to generate the adversarial sample. diff --git a/adversarial/advbox/__init__.py b/adversarial/advbox/__init__.py deleted file mode 100644 index f56f14f18da..00000000000 --- a/adversarial/advbox/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" - A set of tools for generating adversarial example on paddle platform -""" diff --git a/adversarial/advbox/attacks/base.py b/adversarial/advbox/attacks/base.py deleted file mode 100644 index 000baa48f62..00000000000 --- a/adversarial/advbox/attacks/base.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -""" -The base model of the model. -""" -from abc import ABCMeta, abstractmethod - - -class Attack(object): - """ - Abstract base class for adversarial attacks. `Attack` represent an adversarial attack - which search an adversarial example. subclass should implement the _apply() method. - - Args: - model(Model): an instance of the class advbox.base.Model. - - """ - __metaclass__ = ABCMeta - - def __init__(self, model): - self.model = model - - def __call__(self, image_label): - """ - Generate the adversarial sample. - - Args: - image_label(list): The image and label tuple list with one element. - """ - adv_img = self._apply(image_label) - return adv_img - - @abstractmethod - def _apply(self, image_label): - """ - Search an adversarial example. - - Args: - image_batch(list): The image and label tuple list with one element. - """ - raise NotImplementedError diff --git a/adversarial/advbox/attacks/gradientsign.py b/adversarial/advbox/attacks/gradientsign.py deleted file mode 100644 index 77d93bd7939..00000000000 --- a/adversarial/advbox/attacks/gradientsign.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -""" -This module provide the attack method for FGSM's implement. -""" -from __future__ import division -import numpy as np -from collections import Iterable -from .base import Attack - - -class GradientSignAttack(Attack): - """ - This attack was originally implemented by Goodfellow et al. (2015) with the - infinity norm (and is known as the "Fast Gradient Sign Method"). This is therefore called - the Fast Gradient Method. - Paper link: https://arxiv.org/abs/1412.6572 - """ - - def _apply(self, image_label, epsilons=1000): - assert len(image_label) == 1 - pre_label = np.argmax(self.model.predict(image_label)) - - min_, max_ = self.model.bounds() - gradient = self.model.gradient(image_label) - gradient_sign = np.sign(gradient) * (max_ - min_) - - if not isinstance(epsilons, Iterable): - epsilons = np.linspace(0, 1, num=epsilons + 1) - - for epsilon in epsilons: - adv_img = image_label[0][0].reshape( - gradient_sign.shape) + epsilon * gradient_sign - adv_img = np.clip(adv_img, min_, max_) - adv_label = np.argmax(self.model.predict([(adv_img, 0)])) - if pre_label != adv_label: - return adv_img - - -FGSM = GradientSignAttack diff --git a/adversarial/advbox/models/__init__.py b/adversarial/advbox/models/__init__.py deleted file mode 100644 index eee0f6efd47..00000000000 --- a/adversarial/advbox/models/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2017 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Paddle model for target of attack -""" diff --git a/adversarial/advbox/models/base.py b/adversarial/advbox/models/base.py deleted file mode 100644 index 084e563f7b4..00000000000 --- a/adversarial/advbox/models/base.py +++ /dev/null @@ -1,103 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -""" -The base model of the model. -""" -from abc import ABCMeta -import abc - -abstractmethod = abc.abstractmethod - - -class Model(object): - """ - Base class of model to provide attack. - - - Args: - bounds(tuple): The lower and upper bound for the image pixel. - channel_axis(int): The index of the axis that represents the color channel. - preprocess(tuple): Two element tuple used to preprocess the input. First - substract the first element, then divide the second element. - """ - __metaclass__ = ABCMeta - - def __init__(self, bounds, channel_axis, preprocess=None): - assert len(bounds) == 2 - assert channel_axis in [0, 1, 2, 3] - - if preprocess is None: - preprocess = (0, 1) - self._bounds = bounds - self._channel_axis = channel_axis - self._preprocess = preprocess - - def bounds(self): - """ - Return the upper and lower bounds of the model. - """ - return self._bounds - - def channel_axis(self): - """ - Return the channel axis of the model. - """ - return self._channel_axis - - def _process_input(self, input_): - res = input_ - sub, div = self._preprocess - if sub != 0: - res = input_ - sub - assert div != 0 - if div != 1: - res /= div - return res - - @abstractmethod - def predict(self, image_batch): - """ - Calculate the prediction of the image batch. - - Args: - image_batch(numpy.ndarray): image batch of shape (batch_size, height, width, channels). - - Return: - numpy.ndarray: predictions of the images with shape (batch_size, num_of_classes). - """ - raise NotImplementedError - - @abstractmethod - def num_classes(self): - """ - Determine the number of the classes - - Return: - int: the number of the classes - """ - raise NotImplementedError - - @abstractmethod - def gradient(self, image_batch): - """ - Calculate the gradient of the cross-entropy loss w.r.t the image. - - Args: - image_batch(list): The image and label tuple list. - - Return: - numpy.ndarray: gradient of the cross-entropy loss w.r.t the image with - the shape (height, width, channel). - """ - raise NotImplementedError diff --git a/adversarial/advbox/models/paddle.py b/adversarial/advbox/models/paddle.py deleted file mode 100644 index 4048b47f897..00000000000 --- a/adversarial/advbox/models/paddle.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -from __future__ import absolute_import - -import numpy as np -import paddle.v2 as paddle -import paddle.v2.fluid as fluid -from paddle.v2.fluid.framework import program_guard - -from .base import Model - - -class PaddleModel(Model): - """ - Create a PaddleModel instance. - When you need to generate a adversarial sample, you should construct an instance of PaddleModel. - - Args: - program(paddle.v2.fluid.framework.Program): The program of the model which generate the adversarial sample. - input_name(string): The name of the input. - logits_name(string): The name of the logits. - predict_name(string): The name of the predict. - cost_name(string): The name of the loss in the program. - """ - - def __init__(self, - program, - input_name, - logits_name, - predict_name, - cost_name, - bounds, - channel_axis=3, - preprocess=None): - super(PaddleModel, self).__init__( - bounds=bounds, channel_axis=channel_axis, preprocess=preprocess) - - if preprocess is None: - preprocess = (0, 1) - - self._program = program - self._place = fluid.CPUPlace() - self._exe = fluid.Executor(self._place) - - self._input_name = input_name - self._logits_name = logits_name - self._predict_name = predict_name - self._cost_name = cost_name - - # gradient - loss = self._program.block(0).var(self._cost_name) - param_grads = fluid.backward.append_backward( - loss, parameter_list=[self._input_name]) - self._gradient = dict(param_grads)[self._input_name] - - def predict(self, image_batch): - """ - Predict the label of the image_batch. - - Args: - image_batch(list): The image and label tuple list. - Return: - numpy.ndarray: predictions of the images with shape (batch_size, num_of_classes). - """ - feeder = fluid.DataFeeder( - feed_list=[self._input_name, self._logits_name], - place=self._place, - program=self._program) - predict_var = self._program.block(0).var(self._predict_name) - predict = self._exe.run(self._program, - feed=feeder.feed(image_batch), - fetch_list=[predict_var]) - return predict - - def num_classes(self): - """ - Calculate the number of classes of the output label. - - Return: - int: the number of classes - """ - predict_var = self._program.block(0).var(self._predict_name) - assert len(predict_var.shape) == 2 - return predict_var.shape[1] - - def gradient(self, image_batch): - """ - Calculate the gradient of the loss w.r.t the input. - - Args: - image_batch(list): The image and label tuple list. - Return: - list: The list of the gradient of the image. - """ - feeder = fluid.DataFeeder( - feed_list=[self._input_name, self._logits_name], - place=self._place, - program=self._program) - - grad, = self._exe.run(self._program, - feed=feeder.feed(image_batch), - fetch_list=[self._gradient]) - return grad diff --git a/adversarial/fluid_mnist.py b/adversarial/fluid_mnist.py deleted file mode 100644 index f8c7fe8d0ef..00000000000 --- a/adversarial/fluid_mnist.py +++ /dev/null @@ -1,99 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -""" -CNN on mnist data using fluid api of paddlepaddle -""" -import paddle.v2 as paddle -import paddle.v2.fluid as fluid - - -def mnist_cnn_model(img): - """ - Mnist cnn model - - Args: - img(Varaible): the input image to be recognized - - Returns: - Variable: the label prediction - """ - conv_pool_1 = fluid.nets.simple_img_conv_pool( - input=img, - num_filters=20, - filter_size=5, - pool_size=2, - pool_stride=2, - act='relu') - - conv_pool_2 = fluid.nets.simple_img_conv_pool( - input=conv_pool_1, - num_filters=50, - filter_size=5, - pool_size=2, - pool_stride=2, - act='relu') - - logits = fluid.layers.fc(input=conv_pool_2, size=10, act='softmax') - return logits - - -def main(): - """ - Train the cnn model on mnist datasets - """ - img = fluid.layers.data(name='img', shape=[1, 28, 28], dtype='float32') - label = fluid.layers.data(name='label', shape=[1], dtype='int64') - logits = mnist_cnn_model(img) - cost = fluid.layers.cross_entropy(input=logits, label=label) - avg_cost = fluid.layers.mean(x=cost) - optimizer = fluid.optimizer.Adam(learning_rate=0.01) - optimizer.minimize(avg_cost) - - accuracy = fluid.evaluator.Accuracy(input=logits, label=label) - - BATCH_SIZE = 50 - PASS_NUM = 3 - ACC_THRESHOLD = 0.98 - LOSS_THRESHOLD = 10.0 - train_reader = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.mnist.train(), buf_size=500), - batch_size=BATCH_SIZE) - - place = fluid.CPUPlace() - exe = fluid.Executor(place) - feeder = fluid.DataFeeder(feed_list=[img, label], place=place) - exe.run(fluid.default_startup_program()) - - for pass_id in range(PASS_NUM): - accuracy.reset(exe) - for data in train_reader(): - loss, acc = exe.run(fluid.default_main_program(), - feed=feeder.feed(data), - fetch_list=[avg_cost] + accuracy.metrics) - pass_acc = accuracy.eval(exe) - print("pass_id=" + str(pass_id) + " acc=" + str(acc) + " pass_acc=" - + str(pass_acc)) - if loss < LOSS_THRESHOLD and pass_acc > ACC_THRESHOLD: - break - - pass_acc = accuracy.eval(exe) - print("pass_id=" + str(pass_id) + " pass_acc=" + str(pass_acc)) - fluid.io.save_params( - exe, dirname='./mnist', main_program=fluid.default_main_program()) - print('train mnist done') - - -if __name__ == '__main__': - main() diff --git a/adversarial/mnist_tutorial_fgsm.py b/adversarial/mnist_tutorial_fgsm.py deleted file mode 100644 index c63e030cd82..00000000000 --- a/adversarial/mnist_tutorial_fgsm.py +++ /dev/null @@ -1,100 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -""" -FGSM demos on mnist using advbox tool. -""" -import paddle.v2 as paddle -import paddle.v2.fluid as fluid -import matplotlib.pyplot as plt -import numpy as np - -from advbox.models.paddle import PaddleModel -from advbox.attacks.gradientsign import GradientSignAttack - - -def cnn_model(img): - """ - Mnist cnn model - Args: - img(Varaible): the input image to be recognized - Returns: - Variable: the label prediction - """ - #conv1 = fluid.nets.conv2d() - conv_pool_1 = fluid.nets.simple_img_conv_pool( - input=img, - num_filters=20, - filter_size=5, - pool_size=2, - pool_stride=2, - act='relu') - - conv_pool_2 = fluid.nets.simple_img_conv_pool( - input=conv_pool_1, - num_filters=50, - filter_size=5, - pool_size=2, - pool_stride=2, - act='relu') - - logits = fluid.layers.fc(input=conv_pool_2, size=10, act='softmax') - return logits - - -def main(): - """ - Advbox demo which demonstrate how to use advbox. - """ - IMG_NAME = 'img' - LABEL_NAME = 'label' - - img = fluid.layers.data(name=IMG_NAME, shape=[1, 28, 28], dtype='float32') - # gradient should flow - img.stop_gradient = False - label = fluid.layers.data(name=LABEL_NAME, shape=[1], dtype='int64') - logits = cnn_model(img) - cost = fluid.layers.cross_entropy(input=logits, label=label) - avg_cost = fluid.layers.mean(x=cost) - - place = fluid.CPUPlace() - exe = fluid.Executor(place) - - BATCH_SIZE = 1 - train_reader = paddle.batch( - paddle.reader.shuffle( - paddle.dataset.mnist.train(), buf_size=500), - batch_size=BATCH_SIZE) - feeder = fluid.DataFeeder( - feed_list=[IMG_NAME, LABEL_NAME], - place=place, - program=fluid.default_main_program()) - - fluid.io.load_params( - exe, "./mnist/", main_program=fluid.default_main_program()) - - # advbox demo - m = PaddleModel(fluid.default_main_program(), IMG_NAME, LABEL_NAME, - logits.name, avg_cost.name, (-1, 1)) - att = GradientSignAttack(m) - for data in train_reader(): - # fgsm attack - adv_img = att(data) - plt.imshow(n[0][0], cmap='Greys_r') - plt.show() - #np.save('adv_img', adv_img) - break - - -if __name__ == '__main__': - main() -- GitLab From ead7059bf93e362978c8463350ad9551db43b2b9 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Tue, 16 Jan 2018 14:35:26 +0800 Subject: [PATCH 0291/2305] Refine code --- paddle/operators/elementwise_max_op.h | 34 +------------------ paddle/operators/elementwise_min_op.h | 34 +------------------ paddle/operators/elementwise_op_function.h | 38 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 66 deletions(-) diff --git a/paddle/operators/elementwise_max_op.h b/paddle/operators/elementwise_max_op.h index 92152f7cb6e..255728e8e62 100644 --- a/paddle/operators/elementwise_max_op.h +++ b/paddle/operators/elementwise_max_op.h @@ -28,39 +28,7 @@ template class ElementwiseMaxKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { - using Tensor = framework::Tensor; - - auto* x = ctx.Input("X"); - auto* y = ctx.Input("Y"); - auto* z = ctx.Output("Out"); - z->mutable_data(ctx.GetPlace()); - TransformFunctor, T, DeviceContext> functor( - x, y, z, ctx.template device_context(), MaxFunctor()); - - auto x_dims = x->dims(); - auto y_dims = y->dims(); - PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), - "Rank of first input must >= rank of second input."); - - if (x_dims == y_dims) { - functor.Run(); - return; - } - - int axis = ctx.Attr("axis"); - axis = (axis == -1 ? x_dims.size() - y_dims.size() : axis); - PADDLE_ENFORCE(axis >= 0 && axis < x_dims.size(), - "Axis should be in range [0, x_dims)"); - - int pre, n, post; - get_mid_dims(x_dims, y_dims, axis, pre, n, post); - if (post == 1) { - functor.RunRowWise(n, pre); - return; - } else { - functor.RunMidWise(n, pre, post); - return; - } + ElementwiseComputeEx, DeviceContext, T>(ctx); } }; diff --git a/paddle/operators/elementwise_min_op.h b/paddle/operators/elementwise_min_op.h index 53b7f59fa07..e6627a0f1bb 100644 --- a/paddle/operators/elementwise_min_op.h +++ b/paddle/operators/elementwise_min_op.h @@ -28,39 +28,7 @@ template class ElementwiseMinKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { - using Tensor = framework::Tensor; - - auto* x = ctx.Input("X"); - auto* y = ctx.Input("Y"); - auto* z = ctx.Output("Out"); - z->mutable_data(ctx.GetPlace()); - TransformFunctor, T, DeviceContext> functor( - x, y, z, ctx.template device_context(), MinFunctor()); - - auto x_dims = x->dims(); - auto y_dims = y->dims(); - PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), - "Rank of first input must >= rank of second input."); - - if (x_dims == y_dims) { - functor.Run(); - return; - } - - int axis = ctx.Attr("axis"); - axis = (axis == -1 ? x_dims.size() - y_dims.size() : axis); - PADDLE_ENFORCE(axis >= 0 && axis < x_dims.size(), - "Axis should be in range [0, x_dims)"); - - int pre, n, post; - get_mid_dims(x_dims, y_dims, axis, pre, n, post); - if (post == 1) { - functor.RunRowWise(n, pre); - return; - } else { - functor.RunMidWise(n, pre, post); - return; - } + ElementwiseComputeEx, DeviceContext, T>(ctx); } }; diff --git a/paddle/operators/elementwise_op_function.h b/paddle/operators/elementwise_op_function.h index 0c75276b031..be11d5cc9de 100644 --- a/paddle/operators/elementwise_op_function.h +++ b/paddle/operators/elementwise_op_function.h @@ -356,5 +356,43 @@ void ElementwiseGradCompute(const framework::ExecutionContext& ctx) { return; } } + +template +void ElementwiseComputeEx(const framework::ExecutionContext& ctx) { + using Tensor = framework::Tensor; + + auto* x = ctx.Input("X"); + auto* y = ctx.Input("Y"); + auto* z = ctx.Output("Out"); + z->mutable_data(ctx.GetPlace()); + TransformFunctor functor( + x, y, z, ctx.template device_context(), Functor()); + + auto x_dims = x->dims(); + auto y_dims = y->dims(); + PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), + "Rank of first input must >= rank of second input."); + + if (x_dims == y_dims) { + functor.Run(); + return; + } + + int axis = ctx.Attr("axis"); + axis = (axis == -1 ? x_dims.size() - y_dims.size() : axis); + PADDLE_ENFORCE(axis >= 0 && axis < x_dims.size(), + "Axis should be in range [0, x_dims)"); + + int pre, n, post; + get_mid_dims(x_dims, y_dims, axis, pre, n, post); + if (post == 1) { + functor.RunRowWise(n, pre); + return; + } else { + functor.RunMidWise(n, pre, post); + return; + } +} + } // namespace operators } // namespace paddle -- GitLab From f59599a3dd46def0215d441ddfb023c3ec3d2632 Mon Sep 17 00:00:00 2001 From: fengjiayi Date: Tue, 16 Jan 2018 14:39:14 +0800 Subject: [PATCH 0292/2305] refine elementwise_add_op --- paddle/operators/elementwise_add_op.h | 34 +-------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/paddle/operators/elementwise_add_op.h b/paddle/operators/elementwise_add_op.h index 6478e1e0c2e..a8389429f26 100644 --- a/paddle/operators/elementwise_add_op.h +++ b/paddle/operators/elementwise_add_op.h @@ -28,39 +28,7 @@ template class ElementwiseAddKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { - using Tensor = framework::Tensor; - - auto* x = ctx.Input("X"); - auto* y = ctx.Input("Y"); - auto* z = ctx.Output("Out"); - z->mutable_data(ctx.GetPlace()); - TransformFunctor, T, DeviceContext> functor( - x, y, z, ctx.template device_context(), AddFunctor()); - - auto x_dims = x->dims(); - auto y_dims = y->dims(); - PADDLE_ENFORCE_GE(x_dims.size(), y_dims.size(), - "Rank of first input must >= rank of second input."); - - if (x_dims == y_dims) { - functor.Run(); - return; - } - - int axis = ctx.Attr("axis"); - axis = (axis == -1 ? x_dims.size() - y_dims.size() : axis); - PADDLE_ENFORCE(axis >= 0 && axis < x_dims.size(), - "Axis should be in range [0, x_dims)"); - - int pre, n, post; - get_mid_dims(x_dims, y_dims, axis, pre, n, post); - if (post == 1) { - functor.RunRowWise(n, pre); - return; - } else { - functor.RunMidWise(n, pre, post); - return; - } + ElementwiseComputeEx, DeviceContext, T>(ctx); } }; -- GitLab From 0d4b8ae13364f5129b726894a18f5586714cd0e7 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Tue, 16 Jan 2018 14:42:47 +0800 Subject: [PATCH 0293/2305] remove v1_api_demo --- v1_api_demo/README.md | 5 - v1_api_demo/gan/.gitignore | 11 - v1_api_demo/gan/README.md | 13 - v1_api_demo/gan/data/download_cifar.sh | 18 - v1_api_demo/gan/data/get_mnist_data.sh | 17 - v1_api_demo/gan/gan_conf.py | 151 -------- v1_api_demo/gan/gan_conf_image.py | 298 --------------- v1_api_demo/gan/gan_trainer.py | 349 ------------------ v1_api_demo/mnist/.gitignore | 10 - v1_api_demo/mnist/api_train.py | 209 ----------- v1_api_demo/mnist/data/generate_list.py | 21 -- v1_api_demo/mnist/data/get_mnist_data.sh | 21 -- v1_api_demo/mnist/light_mnist.py | 79 ---- v1_api_demo/mnist/mnist_provider.py | 25 -- v1_api_demo/mnist/mnist_util.py | 30 -- v1_api_demo/mnist/train.sh | 32 -- v1_api_demo/mnist/vgg_16_mnist.py | 50 --- v1_api_demo/model_zoo/embedding/.gitignore | 2 - .../model_zoo/embedding/extract_para.py | 113 ------ .../model_zoo/embedding/paraconvert.py | 159 -------- .../model_zoo/embedding/pre_DictAndModel.sh | 32 -- v1_api_demo/model_zoo/resnet/.gitignore | 5 - v1_api_demo/model_zoo/resnet/classify.py | 312 ---------------- .../model_zoo/resnet/example/.gitignore | 1 - .../model_zoo/resnet/example/__init__.py | 13 - v1_api_demo/model_zoo/resnet/example/cat.jpg | Bin 12881 -> 0 bytes v1_api_demo/model_zoo/resnet/example/dog.jpg | Bin 71483 -> 0 bytes .../resnet/example/image_list_provider.py | 102 ----- .../model_zoo/resnet/example/test.list | 2 - .../model_zoo/resnet/extract_fea_c++.sh | 40 -- .../model_zoo/resnet/extract_fea_py.sh | 29 -- v1_api_demo/model_zoo/resnet/get_model.sh | 32 -- v1_api_demo/model_zoo/resnet/load_feature.py | 63 ---- v1_api_demo/model_zoo/resnet/net_diagram.sh | 39 -- v1_api_demo/model_zoo/resnet/predict.sh | 23 -- v1_api_demo/model_zoo/resnet/resnet.py | 271 -------------- v1_api_demo/quick_start/.gitignore | 15 - v1_api_demo/quick_start/api_predict.py | 147 -------- v1_api_demo/quick_start/api_predict.sh | 30 -- v1_api_demo/quick_start/api_train.py | 122 ------ v1_api_demo/quick_start/api_train.sh | 29 -- .../quick_start/cluster/cluster_train.sh | 45 --- v1_api_demo/quick_start/cluster/env.sh | 28 -- v1_api_demo/quick_start/cluster/pserver.sh | 26 -- v1_api_demo/quick_start/data/README.md | 9 - v1_api_demo/quick_start/data/get_data.sh | 27 -- .../data/proc_from_raw_data/get_data.sh | 79 ---- .../data/proc_from_raw_data/preprocess.py | 236 ------------ v1_api_demo/quick_start/dataprovider_bow.py | 86 ----- v1_api_demo/quick_start/dataprovider_emb.py | 52 --- v1_api_demo/quick_start/predict.sh | 32 -- v1_api_demo/quick_start/train.sh | 34 -- .../quick_start/trainer_config.bidi-lstm.py | 74 ---- v1_api_demo/quick_start/trainer_config.cnn.py | 68 ---- .../quick_start/trainer_config.db-lstm.py | 74 ---- v1_api_demo/quick_start/trainer_config.emb.py | 64 ---- v1_api_demo/quick_start/trainer_config.lr.py | 85 ----- .../quick_start/trainer_config.lstm.py | 70 ---- .../quick_start/trainer_config.resnet-lstm.py | 104 ------ v1_api_demo/sequence_tagging/data/get_data.sh | 21 -- v1_api_demo/sequence_tagging/data/test.list | 1 - v1_api_demo/sequence_tagging/data/train.list | 1 - v1_api_demo/sequence_tagging/dataprovider.py | 260 ------------- v1_api_demo/sequence_tagging/linear_crf.py | 83 ----- v1_api_demo/sequence_tagging/readme.md | 45 --- v1_api_demo/sequence_tagging/rnn_crf.py | 121 ------ v1_api_demo/sequence_tagging/train.sh | 12 - v1_api_demo/sequence_tagging/train_linear.sh | 11 - v1_api_demo/traffic_prediction/README | 7 - .../traffic_prediction/data/get_data.sh | 34 -- .../traffic_prediction/dataprovider.py | 82 ---- v1_api_demo/traffic_prediction/gen_result.py | 61 --- v1_api_demo/traffic_prediction/predict.sh | 30 -- v1_api_demo/traffic_prediction/train.sh | 27 -- .../traffic_prediction/trainer_config.py | 52 --- v1_api_demo/vae/README.md | 13 - v1_api_demo/vae/data/get_mnist_data.sh | 17 - v1_api_demo/vae/dataloader.py | 60 --- v1_api_demo/vae/vae_conf.py | 116 ------ v1_api_demo/vae/vae_train.py | 175 --------- 80 files changed, 5342 deletions(-) delete mode 100644 v1_api_demo/README.md delete mode 100644 v1_api_demo/gan/.gitignore delete mode 100644 v1_api_demo/gan/README.md delete mode 100755 v1_api_demo/gan/data/download_cifar.sh delete mode 100755 v1_api_demo/gan/data/get_mnist_data.sh delete mode 100644 v1_api_demo/gan/gan_conf.py delete mode 100644 v1_api_demo/gan/gan_conf_image.py delete mode 100644 v1_api_demo/gan/gan_trainer.py delete mode 100644 v1_api_demo/mnist/.gitignore delete mode 100644 v1_api_demo/mnist/api_train.py delete mode 100644 v1_api_demo/mnist/data/generate_list.py delete mode 100755 v1_api_demo/mnist/data/get_mnist_data.sh delete mode 100644 v1_api_demo/mnist/light_mnist.py delete mode 100644 v1_api_demo/mnist/mnist_provider.py delete mode 100644 v1_api_demo/mnist/mnist_util.py delete mode 100755 v1_api_demo/mnist/train.sh delete mode 100644 v1_api_demo/mnist/vgg_16_mnist.py delete mode 100644 v1_api_demo/model_zoo/embedding/.gitignore delete mode 100755 v1_api_demo/model_zoo/embedding/extract_para.py delete mode 100755 v1_api_demo/model_zoo/embedding/paraconvert.py delete mode 100755 v1_api_demo/model_zoo/embedding/pre_DictAndModel.sh delete mode 100644 v1_api_demo/model_zoo/resnet/.gitignore delete mode 100755 v1_api_demo/model_zoo/resnet/classify.py delete mode 100644 v1_api_demo/model_zoo/resnet/example/.gitignore delete mode 100644 v1_api_demo/model_zoo/resnet/example/__init__.py delete mode 100644 v1_api_demo/model_zoo/resnet/example/cat.jpg delete mode 100644 v1_api_demo/model_zoo/resnet/example/dog.jpg delete mode 100644 v1_api_demo/model_zoo/resnet/example/image_list_provider.py delete mode 100644 v1_api_demo/model_zoo/resnet/example/test.list delete mode 100755 v1_api_demo/model_zoo/resnet/extract_fea_c++.sh delete mode 100755 v1_api_demo/model_zoo/resnet/extract_fea_py.sh delete mode 100755 v1_api_demo/model_zoo/resnet/get_model.sh delete mode 100644 v1_api_demo/model_zoo/resnet/load_feature.py delete mode 100755 v1_api_demo/model_zoo/resnet/net_diagram.sh delete mode 100755 v1_api_demo/model_zoo/resnet/predict.sh delete mode 100644 v1_api_demo/model_zoo/resnet/resnet.py delete mode 100644 v1_api_demo/quick_start/.gitignore delete mode 100755 v1_api_demo/quick_start/api_predict.py delete mode 100755 v1_api_demo/quick_start/api_predict.sh delete mode 100644 v1_api_demo/quick_start/api_train.py delete mode 100755 v1_api_demo/quick_start/api_train.sh delete mode 100755 v1_api_demo/quick_start/cluster/cluster_train.sh delete mode 100644 v1_api_demo/quick_start/cluster/env.sh delete mode 100755 v1_api_demo/quick_start/cluster/pserver.sh delete mode 100644 v1_api_demo/quick_start/data/README.md delete mode 100755 v1_api_demo/quick_start/data/get_data.sh delete mode 100755 v1_api_demo/quick_start/data/proc_from_raw_data/get_data.sh delete mode 100755 v1_api_demo/quick_start/data/proc_from_raw_data/preprocess.py delete mode 100644 v1_api_demo/quick_start/dataprovider_bow.py delete mode 100755 v1_api_demo/quick_start/dataprovider_emb.py delete mode 100755 v1_api_demo/quick_start/predict.sh delete mode 100755 v1_api_demo/quick_start/train.sh delete mode 100644 v1_api_demo/quick_start/trainer_config.bidi-lstm.py delete mode 100644 v1_api_demo/quick_start/trainer_config.cnn.py delete mode 100644 v1_api_demo/quick_start/trainer_config.db-lstm.py delete mode 100644 v1_api_demo/quick_start/trainer_config.emb.py delete mode 100644 v1_api_demo/quick_start/trainer_config.lr.py delete mode 100644 v1_api_demo/quick_start/trainer_config.lstm.py delete mode 100644 v1_api_demo/quick_start/trainer_config.resnet-lstm.py delete mode 100755 v1_api_demo/sequence_tagging/data/get_data.sh delete mode 100644 v1_api_demo/sequence_tagging/data/test.list delete mode 100644 v1_api_demo/sequence_tagging/data/train.list delete mode 100644 v1_api_demo/sequence_tagging/dataprovider.py delete mode 100644 v1_api_demo/sequence_tagging/linear_crf.py delete mode 100644 v1_api_demo/sequence_tagging/readme.md delete mode 100644 v1_api_demo/sequence_tagging/rnn_crf.py delete mode 100755 v1_api_demo/sequence_tagging/train.sh delete mode 100755 v1_api_demo/sequence_tagging/train_linear.sh delete mode 100644 v1_api_demo/traffic_prediction/README delete mode 100755 v1_api_demo/traffic_prediction/data/get_data.sh delete mode 100644 v1_api_demo/traffic_prediction/dataprovider.py delete mode 100644 v1_api_demo/traffic_prediction/gen_result.py delete mode 100755 v1_api_demo/traffic_prediction/predict.sh delete mode 100755 v1_api_demo/traffic_prediction/train.sh delete mode 100755 v1_api_demo/traffic_prediction/trainer_config.py delete mode 100644 v1_api_demo/vae/README.md delete mode 100755 v1_api_demo/vae/data/get_mnist_data.sh delete mode 100644 v1_api_demo/vae/dataloader.py delete mode 100644 v1_api_demo/vae/vae_conf.py delete mode 100644 v1_api_demo/vae/vae_train.py diff --git a/v1_api_demo/README.md b/v1_api_demo/README.md deleted file mode 100644 index 0460a85fae0..00000000000 --- a/v1_api_demo/README.md +++ /dev/null @@ -1,5 +0,0 @@ -The examples in v1_api_demo are using v1_api currently, and will be upgraded to v2_api later. -Thus, v1_api_demo is a temporary directory. We decide not to maintain it and will delete it in future. - -Please go to [PaddlePaddle/book](https://github.com/PaddlePaddle/book) and -[PaddlePaddle/models](https://github.com/PaddlePaddle/models) to learn PaddlePaddle. diff --git a/v1_api_demo/gan/.gitignore b/v1_api_demo/gan/.gitignore deleted file mode 100644 index 93a6f5080a1..00000000000 --- a/v1_api_demo/gan/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -output/ -uniform_params/ -cifar_params/ -mnist_params/ -*.png -.pydevproject -.project -*.log -*.pyc -data/mnist_data/ -data/cifar-10-batches-py/ diff --git a/v1_api_demo/gan/README.md b/v1_api_demo/gan/README.md deleted file mode 100644 index 1908b534b0c..00000000000 --- a/v1_api_demo/gan/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Generative Adversarial Networks (GAN) - -This demo implements GAN training described in the original GAN paper (https://arxiv.org/abs/1406.2661) and DCGAN (https://arxiv.org/abs/1511.06434). - -The general training procedures are implemented in gan_trainer.py. The neural network configurations are specified in gan_conf.py (for synthetic data) and gan_conf_image.py (for image data). - -In order to run the model, first download the corresponding data by running the shell script in ./data. -Then you can run the command below. The flag -d specifies the training data (cifar, mnist or uniform) and flag --useGpu specifies whether to use gpu for training (0 is cpu, 1 is gpu). - -$python gan_trainer.py -d cifar --use_gpu 1 - -The generated images will be stored in ./cifar_samples/ -The corresponding models will be stored in ./cifar_params/ diff --git a/v1_api_demo/gan/data/download_cifar.sh b/v1_api_demo/gan/data/download_cifar.sh deleted file mode 100755 index bbadc7c10c7..00000000000 --- a/v1_api_demo/gan/data/download_cifar.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e -wget https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz -tar zxf cifar-10-python.tar.gz -rm cifar-10-python.tar.gz diff --git a/v1_api_demo/gan/data/get_mnist_data.sh b/v1_api_demo/gan/data/get_mnist_data.sh deleted file mode 100755 index a77c81bf5af..00000000000 --- a/v1_api_demo/gan/data/get_mnist_data.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env sh -# This script downloads the mnist data and unzips it. -set -e -DIR="$( cd "$(dirname "$0")" ; pwd -P )" -rm -rf "$DIR/mnist_data" -mkdir "$DIR/mnist_data" -cd "$DIR/mnist_data" - -echo "Downloading..." - -for fname in train-images-idx3-ubyte train-labels-idx1-ubyte t10k-images-idx3-ubyte t10k-labels-idx1-ubyte -do - if [ ! -e $fname ]; then - wget --no-check-certificate http://yann.lecun.com/exdb/mnist/${fname}.gz - gunzip ${fname}.gz - fi -done diff --git a/v1_api_demo/gan/gan_conf.py b/v1_api_demo/gan/gan_conf.py deleted file mode 100644 index 86ac2dffe5f..00000000000 --- a/v1_api_demo/gan/gan_conf.py +++ /dev/null @@ -1,151 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from paddle.trainer_config_helpers import * - -mode = get_config_arg("mode", str, "generator") -assert mode in set([ - "generator", "discriminator", "generator_training", "discriminator_training" -]) - -is_generator_training = mode == "generator_training" -is_discriminator_training = mode == "discriminator_training" -is_generator = mode == "generator" -is_discriminator = mode == "discriminator" - -# The network structure below follows the ref https://arxiv.org/abs/1406.2661 -# Here we used two hidden layers and batch_norm - -print('mode=%s' % mode) -# the dim of the noise (z) as the input of the generator network -noise_dim = 10 -# the dim of the hidden layer -hidden_dim = 10 -# the dim of the generated sample -sample_dim = 2 - -settings( - batch_size=128, - learning_rate=1e-4, - learning_method=AdamOptimizer(beta1=0.5)) - - -def discriminator(sample): - """ - discriminator ouputs the probablity of a sample is from generator - or real data. - The output has two dimenstional: dimension 0 is the probablity - of the sample is from generator and dimension 1 is the probabblity - of the sample is from real data. - """ - param_attr = ParamAttr(is_static=is_generator_training) - bias_attr = ParamAttr( - is_static=is_generator_training, initial_mean=1.0, initial_std=0) - - hidden = fc_layer( - input=sample, - name="dis_hidden", - size=hidden_dim, - bias_attr=bias_attr, - param_attr=param_attr, - act=ReluActivation()) - - hidden2 = fc_layer( - input=hidden, - name="dis_hidden2", - size=hidden_dim, - bias_attr=bias_attr, - param_attr=param_attr, - act=LinearActivation()) - - hidden_bn = batch_norm_layer( - hidden2, - act=ReluActivation(), - name="dis_hidden_bn", - bias_attr=bias_attr, - param_attr=ParamAttr( - is_static=is_generator_training, initial_mean=1.0, - initial_std=0.02), - use_global_stats=False) - - return fc_layer( - input=hidden_bn, - name="dis_prob", - size=2, - bias_attr=bias_attr, - param_attr=param_attr, - act=SoftmaxActivation()) - - -def generator(noise): - """ - generator generates a sample given noise - """ - param_attr = ParamAttr(is_static=is_discriminator_training) - bias_attr = ParamAttr( - is_static=is_discriminator_training, initial_mean=1.0, initial_std=0) - - hidden = fc_layer( - input=noise, - name="gen_layer_hidden", - size=hidden_dim, - bias_attr=bias_attr, - param_attr=param_attr, - act=ReluActivation()) - - hidden2 = fc_layer( - input=hidden, - name="gen_hidden2", - size=hidden_dim, - bias_attr=bias_attr, - param_attr=param_attr, - act=LinearActivation()) - - hidden_bn = batch_norm_layer( - hidden2, - act=ReluActivation(), - name="gen_layer_hidden_bn", - bias_attr=bias_attr, - param_attr=ParamAttr( - is_static=is_discriminator_training, - initial_mean=1.0, - initial_std=0.02), - use_global_stats=False) - - return fc_layer( - input=hidden_bn, - name="gen_layer1", - size=sample_dim, - bias_attr=bias_attr, - param_attr=param_attr, - act=LinearActivation()) - - -if is_generator_training: - noise = data_layer(name="noise", size=noise_dim) - sample = generator(noise) - -if is_discriminator_training: - sample = data_layer(name="sample", size=sample_dim) - -if is_generator_training or is_discriminator_training: - label = data_layer(name="label", size=1) - prob = discriminator(sample) - cost = cross_entropy(input=prob, label=label) - classification_error_evaluator( - input=prob, label=label, name=mode + '_error') - outputs(cost) - -if is_generator: - noise = data_layer(name="noise", size=noise_dim) - outputs(generator(noise)) diff --git a/v1_api_demo/gan/gan_conf_image.py b/v1_api_demo/gan/gan_conf_image.py deleted file mode 100644 index c469227994c..00000000000 --- a/v1_api_demo/gan/gan_conf_image.py +++ /dev/null @@ -1,298 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from paddle.trainer_config_helpers import * - -mode = get_config_arg("mode", str, "generator") -dataSource = get_config_arg("data", str, "mnist") -assert mode in set([ - "generator", "discriminator", "generator_training", "discriminator_training" -]) - -is_generator_training = mode == "generator_training" -is_discriminator_training = mode == "discriminator_training" -is_generator = mode == "generator" -is_discriminator = mode == "discriminator" - -# The network structure below follows the dcgan paper -# (https://arxiv.org/abs/1511.06434) - -print('mode=%s' % mode) -# the dim of the noise (z) as the input of the generator network -noise_dim = 100 -# the number of filters in the layer in generator/discriminator that is -# closet to the image -gf_dim = 64 -df_dim = 64 -if dataSource == "mnist": - sample_dim = 28 # image dim - c_dim = 1 # image color -else: - sample_dim = 32 - c_dim = 3 -s2, s4 = int(sample_dim / 2), int(sample_dim / 4), -s8, s16 = int(sample_dim / 8), int(sample_dim / 16) - -settings( - batch_size=128, - learning_rate=2e-4, - learning_method=AdamOptimizer(beta1=0.5)) - - -def conv_bn(input, - channels, - imgSize, - num_filters, - output_x, - stride, - name, - param_attr, - bias_attr, - param_attr_bn, - bn, - trans=False, - act=ReluActivation()): - """ - conv_bn is a utility function that constructs a convolution/deconv layer - with an optional batch_norm layer - - :param bn: whether to use batch_norm_layer - :type bn: bool - :param trans: whether to use conv (False) or deconv (True) - :type trans: bool - """ - - # calculate the filter_size and padding size based on the given - # imgSize and ouput size - tmp = imgSize - (output_x - 1) * stride - if tmp <= 1 or tmp > 5: - raise ValueError("conv input-output dimension does not fit") - elif tmp <= 3: - filter_size = tmp + 2 - padding = 1 - else: - filter_size = tmp - padding = 0 - - print(imgSize, output_x, stride, filter_size, padding) - - if trans: - nameApx = "_convt" - else: - nameApx = "_conv" - - if bn: - conv = img_conv_layer( - input, - filter_size=filter_size, - num_filters=num_filters, - name=name + nameApx, - num_channels=channels, - act=LinearActivation(), - groups=1, - stride=stride, - padding=padding, - bias_attr=bias_attr, - param_attr=param_attr, - shared_biases=True, - layer_attr=None, - filter_size_y=None, - stride_y=None, - padding_y=None, - trans=trans) - - conv_bn = batch_norm_layer( - conv, - act=act, - name=name + nameApx + "_bn", - bias_attr=bias_attr, - param_attr=param_attr_bn, - use_global_stats=False) - - return conv_bn - else: - conv = img_conv_layer( - input, - filter_size=filter_size, - num_filters=num_filters, - name=name + nameApx, - num_channels=channels, - act=act, - groups=1, - stride=stride, - padding=padding, - bias_attr=bias_attr, - param_attr=param_attr, - shared_biases=True, - layer_attr=None, - filter_size_y=None, - stride_y=None, - padding_y=None, - trans=trans) - return conv - - -def generator(noise): - """ - generator generates a sample given noise - """ - param_attr = ParamAttr( - is_static=is_discriminator_training, initial_mean=0.0, initial_std=0.02) - bias_attr = ParamAttr( - is_static=is_discriminator_training, initial_mean=0.0, initial_std=0.0) - - param_attr_bn = ParamAttr( - is_static=is_discriminator_training, initial_mean=1.0, initial_std=0.02) - - h1 = fc_layer( - input=noise, - name="gen_layer_h1", - size=s8 * s8 * gf_dim * 4, - bias_attr=bias_attr, - param_attr=param_attr, - act=LinearActivation()) - - h1_bn = batch_norm_layer( - h1, - act=ReluActivation(), - name="gen_layer_h1_bn", - bias_attr=bias_attr, - param_attr=param_attr_bn, - use_global_stats=False) - - h2_bn = conv_bn( - h1_bn, - channels=gf_dim * 4, - output_x=s8, - num_filters=gf_dim * 2, - imgSize=s4, - stride=2, - name="gen_layer_h2", - param_attr=param_attr, - bias_attr=bias_attr, - param_attr_bn=param_attr_bn, - bn=True, - trans=True) - - h3_bn = conv_bn( - h2_bn, - channels=gf_dim * 2, - output_x=s4, - num_filters=gf_dim, - imgSize=s2, - stride=2, - name="gen_layer_h3", - param_attr=param_attr, - bias_attr=bias_attr, - param_attr_bn=param_attr_bn, - bn=True, - trans=True) - - return conv_bn( - h3_bn, - channels=gf_dim, - output_x=s2, - num_filters=c_dim, - imgSize=sample_dim, - stride=2, - name="gen_layer_h4", - param_attr=param_attr, - bias_attr=bias_attr, - param_attr_bn=param_attr_bn, - bn=False, - trans=True, - act=TanhActivation()) - - -def discriminator(sample): - """ - discriminator ouputs the probablity of a sample is from generator - or real data. - The output has two dimenstional: dimension 0 is the probablity - of the sample is from generator and dimension 1 is the probabblity - of the sample is from real data. - """ - param_attr = ParamAttr( - is_static=is_generator_training, initial_mean=0.0, initial_std=0.02) - bias_attr = ParamAttr( - is_static=is_generator_training, initial_mean=0.0, initial_std=0.0) - - param_attr_bn = ParamAttr( - is_static=is_generator_training, initial_mean=1.0, initial_std=0.02) - - h0 = conv_bn( - sample, - channels=c_dim, - imgSize=sample_dim, - num_filters=df_dim, - output_x=s2, - stride=2, - name="dis_h0", - param_attr=param_attr, - bias_attr=bias_attr, - param_attr_bn=param_attr_bn, - bn=False) - - h1_bn = conv_bn( - h0, - channels=df_dim, - imgSize=s2, - num_filters=df_dim * 2, - output_x=s4, - stride=2, - name="dis_h1", - param_attr=param_attr, - bias_attr=bias_attr, - param_attr_bn=param_attr_bn, - bn=True) - - h2_bn = conv_bn( - h1_bn, - channels=df_dim * 2, - imgSize=s4, - num_filters=df_dim * 4, - output_x=s8, - stride=2, - name="dis_h2", - param_attr=param_attr, - bias_attr=bias_attr, - param_attr_bn=param_attr_bn, - bn=True) - - return fc_layer( - input=h2_bn, - name="dis_prob", - size=2, - bias_attr=bias_attr, - param_attr=param_attr, - act=SoftmaxActivation()) - - -if is_generator_training: - noise = data_layer(name="noise", size=noise_dim) - sample = generator(noise) - -if is_discriminator_training: - sample = data_layer(name="sample", size=sample_dim * sample_dim * c_dim) - -if is_generator_training or is_discriminator_training: - label = data_layer(name="label", size=1) - prob = discriminator(sample) - cost = cross_entropy(input=prob, label=label) - classification_error_evaluator( - input=prob, label=label, name=mode + '_error') - outputs(cost) - -if is_generator: - noise = data_layer(name="noise", size=noise_dim) - outputs(generator(noise)) diff --git a/v1_api_demo/gan/gan_trainer.py b/v1_api_demo/gan/gan_trainer.py deleted file mode 100644 index 4a26c230f7a..00000000000 --- a/v1_api_demo/gan/gan_trainer.py +++ /dev/null @@ -1,349 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse -import random -import numpy -import cPickle -import sys, os -from PIL import Image - -from paddle.trainer.config_parser import parse_config -from paddle.trainer.config_parser import logger -import py_paddle.swig_paddle as api -import matplotlib.pyplot as plt - - -def plot2DScatter(data, outputfile): - ''' - Plot the data as a 2D scatter plot and save to outputfile - data needs to be two dimensinoal - ''' - x = data[:, 0] - y = data[:, 1] - logger.info("The mean vector is %s" % numpy.mean(data, 0)) - logger.info("The std vector is %s" % numpy.std(data, 0)) - - heatmap, xedges, yedges = numpy.histogram2d(x, y, bins=50) - extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]] - - plt.clf() - plt.scatter(x, y) - plt.savefig(outputfile, bbox_inches='tight') - - -def CHECK_EQ(a, b): - assert a == b, "a=%s, b=%s" % (a, b) - - -def copy_shared_parameters(src, dst): - ''' - copy the parameters from src to dst - :param src: the source of the parameters - :type src: GradientMachine - :param dst: the destination of the parameters - :type dst: GradientMachine - ''' - src_params = [src.getParameter(i) for i in xrange(src.getParameterSize())] - src_params = dict([(p.getName(), p) for p in src_params]) - - for i in xrange(dst.getParameterSize()): - dst_param = dst.getParameter(i) - src_param = src_params.get(dst_param.getName(), None) - if src_param is None: - continue - src_value = src_param.getBuf(api.PARAMETER_VALUE) - dst_value = dst_param.getBuf(api.PARAMETER_VALUE) - CHECK_EQ(len(src_value), len(dst_value)) - dst_value.copyFrom(src_value) - dst_param.setValueUpdated() - - -def print_parameters(src): - src_params = [src.getParameter(i) for i in xrange(src.getParameterSize())] - - print "***************" - for p in src_params: - print "Name is %s" % p.getName() - print "value is %s \n" % p.getBuf(api.PARAMETER_VALUE).copyToNumpyArray( - ) - - -def load_mnist_data(imageFile): - f = open(imageFile, "rb") - f.read(16) - - # Define number of samples for train/test - if "train" in imageFile: - n = 60000 - else: - n = 10000 - - data = numpy.fromfile(f, 'ubyte', count=n * 28 * 28).reshape((n, 28 * 28)) - data = data / 255.0 * 2.0 - 1.0 - - f.close() - return data.astype('float32') - - -def load_cifar_data(cifar_path): - batch_size = 10000 - data = numpy.zeros((5 * batch_size, 32 * 32 * 3), dtype="float32") - for i in range(1, 6): - file = cifar_path + "/data_batch_" + str(i) - fo = open(file, 'rb') - dict = cPickle.load(fo) - fo.close() - data[(i - 1) * batch_size:(i * batch_size), :] = dict["data"] - - data = data / 255.0 * 2.0 - 1.0 - return data - - -# synthesize 2-D uniform data -def load_uniform_data(): - data = numpy.random.rand(1000000, 2).astype('float32') - return data - - -def merge(images, size): - if images.shape[1] == 28 * 28: - h, w, c = 28, 28, 1 - else: - h, w, c = 32, 32, 3 - img = numpy.zeros((h * size[0], w * size[1], c)) - for idx in xrange(size[0] * size[1]): - i = idx % size[1] - j = idx // size[1] - img[j*h:j*h+h, i*w:i*w+w, :] = \ - ((images[idx, :].reshape((h, w, c), order="F").transpose(1, 0, 2) + 1.0) / 2.0 * 255.0) - return img.astype('uint8') - - -def save_images(images, path): - merged_img = merge(images, [8, 8]) - if merged_img.shape[2] == 1: - im = Image.fromarray(numpy.squeeze(merged_img)).convert('RGB') - else: - im = Image.fromarray(merged_img, mode="RGB") - im.save(path) - - -def get_real_samples(batch_size, data_np): - return data_np[numpy.random.choice( - data_np.shape[0], batch_size, replace=False), :] - - -def get_noise(batch_size, noise_dim): - return numpy.random.normal(size=(batch_size, noise_dim)).astype('float32') - - -def get_fake_samples(generator_machine, batch_size, noise): - gen_inputs = api.Arguments.createArguments(1) - gen_inputs.setSlotValue(0, api.Matrix.createDenseFromNumpy(noise)) - gen_outputs = api.Arguments.createArguments(0) - generator_machine.forward(gen_inputs, gen_outputs, api.PASS_TEST) - fake_samples = gen_outputs.getSlotValue(0).copyToNumpyMat() - return fake_samples - - -def get_training_loss(training_machine, inputs): - outputs = api.Arguments.createArguments(0) - training_machine.forward(inputs, outputs, api.PASS_TEST) - loss = outputs.getSlotValue(0).copyToNumpyMat() - return numpy.mean(loss) - - -def prepare_discriminator_data_batch_pos(batch_size, data_np): - real_samples = get_real_samples(batch_size, data_np) - labels = numpy.ones(batch_size, dtype='int32') - inputs = api.Arguments.createArguments(2) - inputs.setSlotValue(0, api.Matrix.createDenseFromNumpy(real_samples)) - inputs.setSlotIds(1, api.IVector.createVectorFromNumpy(labels)) - return inputs - - -def prepare_discriminator_data_batch_neg(generator_machine, batch_size, noise): - fake_samples = get_fake_samples(generator_machine, batch_size, noise) - labels = numpy.zeros(batch_size, dtype='int32') - inputs = api.Arguments.createArguments(2) - inputs.setSlotValue(0, api.Matrix.createDenseFromNumpy(fake_samples)) - inputs.setSlotIds(1, api.IVector.createVectorFromNumpy(labels)) - return inputs - - -def prepare_generator_data_batch(batch_size, noise): - label = numpy.ones(batch_size, dtype='int32') - inputs = api.Arguments.createArguments(2) - inputs.setSlotValue(0, api.Matrix.createDenseFromNumpy(noise)) - inputs.setSlotIds(1, api.IVector.createVectorFromNumpy(label)) - return inputs - - -def find(iterable, cond): - for item in iterable: - if cond(item): - return item - return None - - -def get_layer_size(model_conf, layer_name): - layer_conf = find(model_conf.layers, lambda x: x.name == layer_name) - assert layer_conf is not None, "Cannot find '%s' layer" % layer_name - return layer_conf.size - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("-d", "--data_source", help="mnist or cifar or uniform") - parser.add_argument( - "--use_gpu", default="1", help="1 means use gpu for training") - parser.add_argument("--gpu_id", default="0", help="the gpu_id parameter") - args = parser.parse_args() - data_source = args.data_source - use_gpu = args.use_gpu - assert data_source in ["mnist", "cifar", "uniform"] - assert use_gpu in ["0", "1"] - - if not os.path.exists("./%s_samples/" % data_source): - os.makedirs("./%s_samples/" % data_source) - - if not os.path.exists("./%s_params/" % data_source): - os.makedirs("./%s_params/" % data_source) - - api.initPaddle('--use_gpu=' + use_gpu, '--dot_period=10', - '--log_period=100', '--gpu_id=' + args.gpu_id, - '--save_dir=' + "./%s_params/" % data_source) - - if data_source == "uniform": - conf = "gan_conf.py" - num_iter = 10000 - else: - conf = "gan_conf_image.py" - num_iter = 1000 - - gen_conf = parse_config(conf, "mode=generator_training,data=" + data_source) - dis_conf = parse_config(conf, - "mode=discriminator_training,data=" + data_source) - generator_conf = parse_config(conf, "mode=generator,data=" + data_source) - batch_size = dis_conf.opt_config.batch_size - noise_dim = get_layer_size(gen_conf.model_config, "noise") - - if data_source == "mnist": - data_np = load_mnist_data("./data/mnist_data/train-images-idx3-ubyte") - elif data_source == "cifar": - data_np = load_cifar_data("./data/cifar-10-batches-py/") - else: - data_np = load_uniform_data() - - # this creates a gradient machine for discriminator - dis_training_machine = api.GradientMachine.createFromConfigProto( - dis_conf.model_config) - # this create a gradient machine for generator - gen_training_machine = api.GradientMachine.createFromConfigProto( - gen_conf.model_config) - - # generator_machine is used to generate data only, which is used for - # training discriminator - logger.info(str(generator_conf.model_config)) - generator_machine = api.GradientMachine.createFromConfigProto( - generator_conf.model_config) - - dis_trainer = api.Trainer.create(dis_conf, dis_training_machine) - - gen_trainer = api.Trainer.create(gen_conf, gen_training_machine) - - dis_trainer.startTrain() - gen_trainer.startTrain() - - # Sync parameters between networks (GradientMachine) at the beginning - copy_shared_parameters(gen_training_machine, dis_training_machine) - copy_shared_parameters(gen_training_machine, generator_machine) - - # constrain that either discriminator or generator can not be trained - # consecutively more than MAX_strike times - curr_train = "dis" - curr_strike = 0 - MAX_strike = 5 - - for train_pass in xrange(100): - dis_trainer.startTrainPass() - gen_trainer.startTrainPass() - for i in xrange(num_iter): - # Do forward pass in discriminator to get the dis_loss - noise = get_noise(batch_size, noise_dim) - data_batch_dis_pos = prepare_discriminator_data_batch_pos( - batch_size, data_np) - dis_loss_pos = get_training_loss(dis_training_machine, - data_batch_dis_pos) - - data_batch_dis_neg = prepare_discriminator_data_batch_neg( - generator_machine, batch_size, noise) - dis_loss_neg = get_training_loss(dis_training_machine, - data_batch_dis_neg) - - dis_loss = (dis_loss_pos + dis_loss_neg) / 2.0 - - # Do forward pass in generator to get the gen_loss - data_batch_gen = prepare_generator_data_batch(batch_size, noise) - gen_loss = get_training_loss(gen_training_machine, data_batch_gen) - - if i % 100 == 0: - print "d_pos_loss is %s d_neg_loss is %s" % (dis_loss_pos, - dis_loss_neg) - print "d_loss is %s g_loss is %s" % (dis_loss, gen_loss) - - # Decide which network to train based on the training history - # And the relative size of the loss - if (not (curr_train == "dis" and curr_strike == MAX_strike)) and \ - ((curr_train == "gen" and curr_strike == MAX_strike) or dis_loss > gen_loss): - if curr_train == "dis": - curr_strike += 1 - else: - curr_train = "dis" - curr_strike = 1 - dis_trainer.trainOneDataBatch(batch_size, data_batch_dis_neg) - dis_trainer.trainOneDataBatch(batch_size, data_batch_dis_pos) - copy_shared_parameters(dis_training_machine, - gen_training_machine) - - else: - if curr_train == "gen": - curr_strike += 1 - else: - curr_train = "gen" - curr_strike = 1 - gen_trainer.trainOneDataBatch(batch_size, data_batch_gen) - # TODO: add API for paddle to allow true parameter sharing between different GradientMachines - # so that we do not need to copy shared parameters. - copy_shared_parameters(gen_training_machine, - dis_training_machine) - copy_shared_parameters(gen_training_machine, generator_machine) - - dis_trainer.finishTrainPass() - gen_trainer.finishTrainPass() - # At the end of each pass, save the generated samples/images - fake_samples = get_fake_samples(generator_machine, batch_size, noise) - if data_source == "uniform": - plot2DScatter(fake_samples, "./%s_samples/train_pass%s.png" % - (data_source, train_pass)) - else: - save_images(fake_samples, "./%s_samples/train_pass%s.png" % - (data_source, train_pass)) - dis_trainer.finishTrain() - gen_trainer.finishTrain() - - -if __name__ == '__main__': - main() diff --git a/v1_api_demo/mnist/.gitignore b/v1_api_demo/mnist/.gitignore deleted file mode 100644 index 7e61d5e3a0c..00000000000 --- a/v1_api_demo/mnist/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -data/raw_data -data/*.list -mnist_vgg_model -plot.png -train.log -*pyc -.ipynb_checkpoints -params.pkl -params.tar -params.tar.gz diff --git a/v1_api_demo/mnist/api_train.py b/v1_api_demo/mnist/api_train.py deleted file mode 100644 index e42c6cbb7e0..00000000000 --- a/v1_api_demo/mnist/api_train.py +++ /dev/null @@ -1,209 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -""" -A very basic example for how to use current Raw SWIG API to train mnist network. - -Current implementation uses Raw SWIG, which means the API call is directly \ -passed to C++ side of Paddle. - -The user api could be simpler and carefully designed. -""" -import random - -import numpy as np -import paddle.v2 as paddle_v2 -import py_paddle.swig_paddle as api -from paddle.trainer_config_helpers import * -from py_paddle import DataProviderConverter - -from mnist_util import read_from_mnist - - -def init_parameter(network): - assert isinstance(network, api.GradientMachine) - for each_param in network.getParameters(): - assert isinstance(each_param, api.Parameter) - array_size = len(each_param) - array = np.random.uniform(-1.0, 1.0, array_size).astype('float32') - each_param.getBuf(api.PARAMETER_VALUE).copyFromNumpyArray(array) - - -def generator_to_batch(generator, batch_size): - ret_val = list() - for each_item in generator: - ret_val.append(each_item) - if len(ret_val) == batch_size: - yield ret_val - ret_val = list() - if len(ret_val) != 0: - yield ret_val - - -class BatchPool(object): - def __init__(self, generator, batch_size): - self.data = list(generator) - self.batch_size = batch_size - - def __call__(self): - random.shuffle(self.data) - for offset in xrange(0, len(self.data), self.batch_size): - limit = min(offset + self.batch_size, len(self.data)) - yield self.data[offset:limit] - - -def input_order_converter(generator): - for each_item in generator: - yield each_item['pixel'], each_item['label'] - - -def main(): - api.initPaddle("-use_gpu=false", "-trainer_count=4") # use 4 cpu cores - - optimizer = paddle_v2.optimizer.Adam( - learning_rate=1e-4, - batch_size=1000, - model_average=ModelAverage(average_window=0.5), - regularization=L2Regularization(rate=0.5)) - - # Create Local Updater. Local means not run in cluster. - # For a cluster training, here we can change to createRemoteUpdater - # in future. - updater = optimizer.create_local_updater() - assert isinstance(updater, api.ParameterUpdater) - - # define network - images = paddle_v2.layer.data( - name='pixel', type=paddle_v2.data_type.dense_vector(784)) - label = paddle_v2.layer.data( - name='label', type=paddle_v2.data_type.integer_value(10)) - hidden1 = paddle_v2.layer.fc(input=images, size=200) - hidden2 = paddle_v2.layer.fc(input=hidden1, size=200) - inference = paddle_v2.layer.fc(input=hidden2, - size=10, - act=paddle_v2.activation.Softmax()) - cost = paddle_v2.layer.classification_cost(input=inference, label=label) - - # Create Simple Gradient Machine. - model_config = paddle_v2.layer.parse_network(cost) - m = api.GradientMachine.createFromConfigProto(model_config, - api.CREATE_MODE_NORMAL, - optimizer.enable_types()) - - # This type check is not useful. Only enable type hint in IDE. - # Such as PyCharm - assert isinstance(m, api.GradientMachine) - - # Initialize Parameter by numpy. - init_parameter(network=m) - - # Initialize ParameterUpdater. - updater.init(m) - - # DataProvider Converter is a utility convert Python Object to Paddle C++ - # Input. The input format is as same as Paddle's DataProvider. - converter = DataProviderConverter(input_types=[images.type, label.type]) - - train_file = './data/raw_data/train' - test_file = './data/raw_data/t10k' - - # start gradient machine. - # the gradient machine must be started before invoke forward/backward. - # not just for training, but also for inference. - m.start() - - # evaluator can print error rate, etc. It is a C++ class. - batch_evaluator = m.makeEvaluator() - test_evaluator = m.makeEvaluator() - - # Get Train Data. - # TrainData will stored in a data pool. Currently implementation is not care - # about memory, speed. Just a very naive implementation. - train_data_generator = input_order_converter(read_from_mnist(train_file)) - train_data = BatchPool(train_data_generator, 512) - - # outArgs is Neural Network forward result. Here is not useful, just passed - # to gradient_machine.forward - outArgs = api.Arguments.createArguments(0) - - for pass_id in xrange(2): # we train 2 passes. - updater.startPass() - - for batch_id, data_batch in enumerate(train_data()): - # data_batch is input images. - # here, for online learning, we could get data_batch from network. - - # Start update one batch. - pass_type = updater.startBatch(len(data_batch)) - - # Start BatchEvaluator. - # batch_evaluator can be used between start/finish. - batch_evaluator.start() - - # forwardBackward is a shortcut for forward and backward. - # It is sometimes faster than invoke forward/backward separately, - # because in GradientMachine, it may be async. - m.forwardBackward(converter(data_batch), outArgs, pass_type) - - for each_param in m.getParameters(): - updater.update(each_param) - - # Get cost. We use numpy to calculate total cost for this batch. - cost_vec = outArgs.getSlotValue(0) - cost_vec = cost_vec.copyToNumpyMat() - cost = cost_vec.sum() / len(data_batch) - - # Make evaluator works. - m.eval(batch_evaluator) - - # Print logs. - print 'Pass id', pass_id, 'Batch id', batch_id, 'with cost=', \ - cost, batch_evaluator - - batch_evaluator.finish() - # Finish batch. - # * will clear gradient. - # * ensure all values should be updated. - updater.finishBatch(cost) - - # testing stage. use test data set to test current network. - updater.apply() - test_evaluator.start() - test_data_generator = input_order_converter(read_from_mnist(test_file)) - for data_batch in generator_to_batch(test_data_generator, 512): - # in testing stage, only forward is needed. - m.forward(converter(data_batch), outArgs, api.PASS_TEST) - m.eval(test_evaluator) - - # print error rate for test data set - print 'Pass', pass_id, ' test evaluator: ', test_evaluator - test_evaluator.finish() - updater.restore() - - updater.catchUpWith() - params = m.getParameters() - for each_param in params: - assert isinstance(each_param, api.Parameter) - value = each_param.getBuf(api.PARAMETER_VALUE) - value = value.copyToNumpyArray() - - # Here, we could save parameter to every where you want - print each_param.getName(), value - - updater.finishPass() - - m.finish() - - -if __name__ == '__main__': - main() diff --git a/v1_api_demo/mnist/data/generate_list.py b/v1_api_demo/mnist/data/generate_list.py deleted file mode 100644 index 49981cc7a93..00000000000 --- a/v1_api_demo/mnist/data/generate_list.py +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -o = open("./" + "train.list", "w") -o.write("./data/raw_data/train" + "\n") -o.close() - -o = open("./" + "test.list", "w") -o.write("./data/raw_data/t10k" + "\n") -o.close() diff --git a/v1_api_demo/mnist/data/get_mnist_data.sh b/v1_api_demo/mnist/data/get_mnist_data.sh deleted file mode 100755 index 5a2e34026d4..00000000000 --- a/v1_api_demo/mnist/data/get_mnist_data.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env sh -# This scripts downloads the mnist data and unzips it. -set -e -DIR="$( cd "$(dirname "$0")" ; pwd -P )" -rm -rf "$DIR/raw_data" -mkdir "$DIR/raw_data" -cd "$DIR/raw_data" - -echo "Downloading..." - -for fname in train-images-idx3-ubyte train-labels-idx1-ubyte t10k-images-idx3-ubyte t10k-labels-idx1-ubyte -do - if [ ! -e $fname ]; then - wget --no-check-certificate http://yann.lecun.com/exdb/mnist/${fname}.gz - gunzip ${fname}.gz - fi -done - -cd $DIR -rm -f *.list -python generate_list.py diff --git a/v1_api_demo/mnist/light_mnist.py b/v1_api_demo/mnist/light_mnist.py deleted file mode 100644 index 33409054357..00000000000 --- a/v1_api_demo/mnist/light_mnist.py +++ /dev/null @@ -1,79 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -is_predict = get_config_arg("is_predict", bool, False) - -####################Data Configuration ################## - -if not is_predict: - data_dir = './data/' - define_py_data_sources2( - train_list=data_dir + 'train.list', - test_list=data_dir + 'test.list', - module='mnist_provider', - obj='process') - -######################Algorithm Configuration ############# -settings(batch_size=50, learning_rate=0.001, learning_method=AdamOptimizer()) - -#######################Network Configuration ############# - -data_size = 1 * 28 * 28 -label_size = 10 -img = data_layer(name='pixel', size=data_size) - - -# light cnn -# A shallower cnn model: [CNN, BN, ReLU, Max-Pooling] x4 + FC x1 -# Easier to train for mnist dataset and quite efficient -# Final performance is close to deeper ones on tasks such as digital and character classification -def light_cnn(input_image, num_channels, num_classes): - def __light__(ipt, - num_filter=128, - times=1, - conv_filter_size=3, - dropouts=0, - num_channels_=None): - return img_conv_group( - input=ipt, - num_channels=num_channels_, - pool_size=2, - pool_stride=2, - conv_padding=0, - conv_num_filter=[num_filter] * times, - conv_filter_size=conv_filter_size, - conv_act=ReluActivation(), - conv_with_batchnorm=True, - conv_batchnorm_drop_rate=dropouts, - pool_type=MaxPooling()) - - tmp = __light__(input_image, num_filter=128, num_channels_=num_channels) - tmp = __light__(tmp, num_filter=128) - tmp = __light__(tmp, num_filter=128) - tmp = __light__(tmp, num_filter=128, conv_filter_size=1) - - tmp = fc_layer(input=tmp, size=num_classes, act=SoftmaxActivation()) - return tmp - - -predict = light_cnn(input_image=img, num_channels=1, num_classes=label_size) - -if not is_predict: - lbl = data_layer(name="label", size=label_size) - inputs(img, lbl) - outputs(classification_cost(input=predict, label=lbl)) -else: - outputs(predict) diff --git a/v1_api_demo/mnist/mnist_provider.py b/v1_api_demo/mnist/mnist_provider.py deleted file mode 100644 index 41923398376..00000000000 --- a/v1_api_demo/mnist/mnist_provider.py +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -from paddle.trainer.PyDataProvider2 import * -from mnist_util import read_from_mnist - - -# Define a py data provider -@provider( - input_types={'pixel': dense_vector(28 * 28), - 'label': integer_value(10)}, - cache=CacheType.CACHE_PASS_IN_MEM) -def process(settings, filename): # settings is not used currently. - for each in read_from_mnist(filename): - yield each diff --git a/v1_api_demo/mnist/mnist_util.py b/v1_api_demo/mnist/mnist_util.py deleted file mode 100644 index 3fd88ae7edc..00000000000 --- a/v1_api_demo/mnist/mnist_util.py +++ /dev/null @@ -1,30 +0,0 @@ -import numpy - -__all__ = ['read_from_mnist'] - - -def read_from_mnist(filename): - imgf = filename + "-images-idx3-ubyte" - labelf = filename + "-labels-idx1-ubyte" - f = open(imgf, "rb") - l = open(labelf, "rb") - - f.read(16) - l.read(8) - - # Define number of samples for train/test - if "train" in filename: - n = 60000 - else: - n = 10000 - - images = numpy.fromfile( - f, 'ubyte', count=n * 28 * 28).reshape((n, 28 * 28)).astype('float32') - images = images / 255.0 * 2.0 - 1.0 - labels = numpy.fromfile(l, 'ubyte', count=n).astype("int") - - for i in xrange(n): - yield {"pixel": images[i, :], 'label': labels[i]} - - f.close() - l.close() diff --git a/v1_api_demo/mnist/train.sh b/v1_api_demo/mnist/train.sh deleted file mode 100755 index ca2b1ad9eb9..00000000000 --- a/v1_api_demo/mnist/train.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e -config=vgg_16_mnist.py -output=./mnist_vgg_model -log=train.log - -paddle train \ ---config=$config \ ---dot_period=10 \ ---log_period=100 \ ---test_all_data_in_one_period=1 \ ---use_gpu=0 \ ---trainer_count=1 \ ---num_passes=100 \ ---save_dir=$output \ -2>&1 | tee $log -paddle usage -l $log -e $? -n "mnist_train" >/dev/null 2>&1 - -python -m paddle.utils.plotcurve -i $log > plot.png diff --git a/v1_api_demo/mnist/vgg_16_mnist.py b/v1_api_demo/mnist/vgg_16_mnist.py deleted file mode 100644 index a819b391c69..00000000000 --- a/v1_api_demo/mnist/vgg_16_mnist.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -is_predict = get_config_arg("is_predict", bool, False) - -####################Data Configuration ################## - -if not is_predict: - data_dir = './data/' - define_py_data_sources2( - train_list=data_dir + 'train.list', - test_list=data_dir + 'test.list', - module='mnist_provider', - obj='process') - -######################Algorithm Configuration ############# -settings( - batch_size=128, - learning_rate=0.1 / 128.0, - learning_method=MomentumOptimizer(0.9), - regularization=L2Regularization(0.0005 * 128)) - -#######################Network Configuration ############# - -data_size = 1 * 28 * 28 -label_size = 10 -img = data_layer(name='pixel', size=data_size) - -# small_vgg is predined in trainer_config_helpers.network -predict = small_vgg(input_image=img, num_channels=1, num_classes=label_size) - -if not is_predict: - lbl = data_layer(name="label", size=label_size) - inputs(img, lbl) - outputs(classification_cost(input=predict, label=lbl)) -else: - outputs(predict) diff --git a/v1_api_demo/model_zoo/embedding/.gitignore b/v1_api_demo/model_zoo/embedding/.gitignore deleted file mode 100644 index 908f5a3fb2f..00000000000 --- a/v1_api_demo/model_zoo/embedding/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -baidu.dict -model_*.emb diff --git a/v1_api_demo/model_zoo/embedding/extract_para.py b/v1_api_demo/model_zoo/embedding/extract_para.py deleted file mode 100755 index 570b90c1f77..00000000000 --- a/v1_api_demo/model_zoo/embedding/extract_para.py +++ /dev/null @@ -1,113 +0,0 @@ -#!/bin/env python -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Example: - python extract_para.py --preModel PREMODEL --preDict PREDICT \ - --usrModel USRMODEL --usrDict USRDICT -d DIM - -Options: - -h, --help show this help message and exit - --preModel PREMODEL the name of pretrained embedding model - --preDict PREDICT the name of pretrained dictionary - --usrModel usrModel the name of output usr embedding model - --usrDict usrDict the name of user specified dictionary - -d DIM dimension of parameter -""" -from optparse import OptionParser -import struct - - -def get_row_index(preDict, usrDict): - """ - Get the row positions for all words in user dictionary from pre-trained dictionary. - return: a list of row positions - Example: preDict='a\nb\nc\n', usrDict='a\nc\n', then return [0,2] - """ - pos = [] - index = dict() - with open(preDict, "r") as f: - for line_index, line in enumerate(f): - word = line.strip().split()[0] - index[word] = line_index - with open(usrDict, "r") as f: - for line in f: - word = line.strip().split()[0] - pos.append(index[word]) - return pos - - -def extract_parameters_by_usrDict(preModel, preDict, usrModel, usrDict, - paraDim): - """ - Extract desired parameters from a pretrained embedding model based on user dictionary - """ - if paraDim not in [32, 64, 128, 256]: - raise RuntimeError("We only support 32, 64, 128, 256 dimensions now") - - fi = open(preModel, "rb") - fo = open(usrModel, "wb") - - # write filehead - rowIndex = get_row_index(preDict, usrDict) - newHead = struct.pack("iil", 0, 4, len(rowIndex) * paraDim) - fo.write(newHead) - bytes = 4 * paraDim - for i in range(0, len(rowIndex)): - # find the absolute position of input file - fi.seek(rowIndex[i] * bytes + 16, 0) - fo.write(fi.read(bytes)) - - print "extract parameters finish, total", len(rowIndex), "lines" - fi.close() - - -def main(): - """ - Main entry for running paraconvert.py - """ - usage = "usage: \n" \ - "python %prog --preModel PREMODEL --preDict PREDICT" \ - " --usrModel USRMODEL --usrDict USRDICT -d DIM" - parser = OptionParser(usage) - parser.add_option( - "--preModel", - action="store", - dest="preModel", - help="the name of pretrained embedding model") - parser.add_option( - "--preDict", - action="store", - dest="preDict", - help="the name of pretrained dictionary") - parser.add_option( - "--usrModel", - action="store", - dest="usrModel", - help="the name of output usr embedding model") - parser.add_option( - "--usrDict", - action="store", - dest="usrDict", - help="the name of user specified dictionary") - parser.add_option( - "-d", action="store", dest="dim", help="dimension of parameter") - (options, args) = parser.parse_args() - extract_parameters_by_usrDict(options.preModel, options.preDict, - options.usrModel, options.usrDict, - int(options.dim)) - - -if __name__ == '__main__': - main() diff --git a/v1_api_demo/model_zoo/embedding/paraconvert.py b/v1_api_demo/model_zoo/embedding/paraconvert.py deleted file mode 100755 index ce7a70efc43..00000000000 --- a/v1_api_demo/model_zoo/embedding/paraconvert.py +++ /dev/null @@ -1,159 +0,0 @@ -#!/bin/env python -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -Example: - python paraconvert.py --b2t -i INPUT -o OUTPUT -d DIM - python paraconvert.py --t2b -i INPUT -o OUTPUT - -Options: - -h, --help show this help message and exit - --b2t convert parameter file of embedding model from binary to text - --t2b convert parameter file of embedding model from text to binary - -i INPUT input parameter file name - -o OUTPUT output parameter file name - -d DIM dimension of parameter -""" -from optparse import OptionParser -import struct - - -def binary2text(input, output, paraDim): - """ - Convert a binary parameter file of embedding model to be a text file. - input: the name of input binary parameter file, the format is: - 1) the first 16 bytes is filehead: - version(4 bytes): version of paddle, default = 0 - floatSize(4 bytes): sizeof(float) = 4 - paraCount(8 bytes): total number of parameter - 2) the next (paraCount * 4) bytes is parameters, each has 4 bytes - output: the name of output text parameter file, for example: - 0,4,32156096 - -0.7845433,1.1937413,-0.1704215,... - 0.0000909,0.0009465,-0.0008813,... - ... - the format is: - 1) the first line is filehead: - version=0, floatSize=4, paraCount=32156096 - 2) other lines print the paramters - a) each line prints paraDim paramters splitted by ',' - b) there is paraCount/paraDim lines (embedding words) - paraDim: dimension of parameters - """ - fi = open(input, "rb") - fo = open(output, "w") - """ - """ - version, floatSize, paraCount = struct.unpack("iil", fi.read(16)) - newHead = ','.join([str(version), str(floatSize), str(paraCount)]) - print >> fo, newHead - - bytes = 4 * int(paraDim) - format = "%df" % int(paraDim) - context = fi.read(bytes) - line = 0 - - while context: - numbers = struct.unpack(format, context) - lst = [] - for i in numbers: - lst.append('%8.7f' % i) - print >> fo, ','.join(lst) - context = fi.read(bytes) - line += 1 - fi.close() - fo.close() - print "binary2text finish, total", line, "lines" - - -def get_para_count(input): - """ - Compute the total number of embedding parameters in input text file. - input: the name of input text file - """ - numRows = 1 - paraDim = 0 - with open(input) as f: - line = f.readline() - paraDim = len(line.split(",")) - for line in f: - numRows += 1 - return numRows * paraDim - - -def text2binary(input, output, paddle_head=True): - """ - Convert a text parameter file of embedding model to be a binary file. - input: the name of input text parameter file, for example: - -0.7845433,1.1937413,-0.1704215,... - 0.0000909,0.0009465,-0.0008813,... - ... - the format is: - 1) it doesn't have filehead - 2) each line stores the same dimension of parameters, - the separator is commas ',' - output: the name of output binary parameter file, the format is: - 1) the first 16 bytes is filehead: - version(4 bytes), floatSize(4 bytes), paraCount(8 bytes) - 2) the next (paraCount * 4) bytes is parameters, each has 4 bytes - """ - fi = open(input, "r") - fo = open(output, "wb") - - newHead = struct.pack("iil", 0, 4, get_para_count(input)) - fo.write(newHead) - - count = 0 - for line in fi: - line = line.strip().split(",") - for i in range(0, len(line)): - binary_data = struct.pack("f", float(line[i])) - fo.write(binary_data) - count += 1 - fi.close() - fo.close() - print "text2binary finish, total", count, "lines" - - -def main(): - """ - Main entry for running paraconvert.py - """ - usage = "usage: \n" \ - "python %prog --b2t -i INPUT -o OUTPUT -d DIM \n" \ - "python %prog --t2b -i INPUT -o OUTPUT" - parser = OptionParser(usage) - parser.add_option( - "--b2t", - action="store_true", - help="convert parameter file of embedding model from binary to text") - parser.add_option( - "--t2b", - action="store_true", - help="convert parameter file of embedding model from text to binary") - parser.add_option( - "-i", action="store", dest="input", help="input parameter file name") - parser.add_option( - "-o", action="store", dest="output", help="output parameter file name") - parser.add_option( - "-d", action="store", dest="dim", help="dimension of parameter") - (options, args) = parser.parse_args() - if options.b2t: - binary2text(options.input, options.output, options.dim) - if options.t2b: - text2binary(options.input, options.output) - - -if __name__ == '__main__': - main() diff --git a/v1_api_demo/model_zoo/embedding/pre_DictAndModel.sh b/v1_api_demo/model_zoo/embedding/pre_DictAndModel.sh deleted file mode 100755 index f61c65a935c..00000000000 --- a/v1_api_demo/model_zoo/embedding/pre_DictAndModel.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e -set -x -BASE_URL='http://paddlepaddle.cdn.bcebos.com/model_zoo/embedding' - -DOWNLOAD_ITEMS=(baidu.dict model_32.emb model_64.emb model_128.emb model_256.emb) -ITEM_MD5=(fa03a12321eaab6c30a8fcc9442eaea3 - f88c8325ee6da6187f1080e8fe66c1cd - 927cf70f27f860aff1a5703ebf7f1584 - a52e43655cd25d279777ed509a1ae27b - b92c67fe9ff70fea53596080e351ac80) - -for ((i=0; i<${#ITEM_MD5[@]}; i++)) -do - FILENAME=${DOWNLOAD_ITEMS[${i}]} - REAL_MD5=`wget ${BASE_URL}/${FILENAME} -O - | tee ${FILENAME} | md5sum | cut -d ' ' -f 1` - EXPECTED_MD5=${ITEM_MD5[${i}]} - [ "${EXPECTED_MD5}" = "${REAL_MD5}" ] -done diff --git a/v1_api_demo/model_zoo/resnet/.gitignore b/v1_api_demo/model_zoo/resnet/.gitignore deleted file mode 100644 index 7a64209b623..00000000000 --- a/v1_api_demo/model_zoo/resnet/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -fea_output/ -features/ -model.list -ResNet_50.dot -ResNet_50.png diff --git a/v1_api_demo/model_zoo/resnet/classify.py b/v1_api_demo/model_zoo/resnet/classify.py deleted file mode 100755 index 6074cc1d3a8..00000000000 --- a/v1_api_demo/model_zoo/resnet/classify.py +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import sys -import cPickle -import logging -from PIL import Image -import numpy as np -from optparse import OptionParser - -import paddle.utils.image_util as image_util - -from py_paddle import swig_paddle, DataProviderConverter -from paddle.trainer.PyDataProvider2 import dense_vector -from paddle.trainer.config_parser import parse_config - -logging.basicConfig( - format='[%(levelname)s %(asctime)s %(filename)s:%(lineno)s] %(message)s') -logging.getLogger().setLevel(logging.INFO) - - -class ImageClassifier(): - def __init__(self, - train_conf, - model_dir=None, - resize_dim=256, - crop_dim=224, - use_gpu=True, - mean_file=None, - output_layer=None, - oversample=False, - is_color=True): - """ - train_conf: network configure. - model_dir: string, directory of model. - resize_dim: int, resized image size. - crop_dim: int, crop size. - mean_file: string, image mean file. - oversample: bool, oversample means multiple crops, namely five - patches (the four corner patches and the center - patch) as well as their horizontal reflections, - ten crops in all. - """ - self.train_conf = train_conf - self.model_dir = model_dir - if model_dir is None: - self.model_dir = os.path.dirname(train_conf) - - self.resize_dim = resize_dim - self.crop_dims = [crop_dim, crop_dim] - self.oversample = oversample - self.is_color = is_color - - self.output_layer = output_layer - if self.output_layer: - assert isinstance(self.output_layer, basestring) - self.output_layer = self.output_layer.split(",") - - self.transformer = image_util.ImageTransformer(is_color=is_color) - self.transformer.set_transpose((2, 0, 1)) - self.transformer.set_channel_swap((2, 1, 0)) - - self.mean_file = mean_file - if self.mean_file is not None: - mean = np.load(self.mean_file)['data_mean'] - mean = mean.reshape(3, self.crop_dims[0], self.crop_dims[1]) - self.transformer.set_mean(mean) # mean pixel - else: - # if you use three mean value, set like: - # this three mean value is calculated from ImageNet. - self.transformer.set_mean(np.array([103.939, 116.779, 123.68])) - - conf_args = "is_test=1,use_gpu=%d,is_predict=1" % (int(use_gpu)) - conf = parse_config(train_conf, conf_args) - swig_paddle.initPaddle("--use_gpu=%d" % (int(use_gpu))) - self.network = swig_paddle.GradientMachine.createFromConfigProto( - conf.model_config) - assert isinstance(self.network, swig_paddle.GradientMachine) - self.network.loadParameters(self.model_dir) - - data_size = 3 * self.crop_dims[0] * self.crop_dims[1] - slots = [dense_vector(data_size)] - self.converter = DataProviderConverter(slots) - - def get_data(self, img_path): - """ - 1. load image from img_path. - 2. resize or oversampling. - 3. transformer data: transpose, channel swap, sub mean. - return K x H x W ndarray. - - img_path: image path. - """ - image = image_util.load_image(img_path, self.is_color) - # Another way to extract oversampled features is that - # cropping and averaging from large feature map which is - # calculated by large size of image. - # This way reduces the computation. - if self.oversample: - # image_util.resize_image: short side is self.resize_dim - image = image_util.resize_image(image, self.resize_dim) - image = np.array(image) - input = np.zeros( - (1, image.shape[0], image.shape[1], 3), dtype=np.float32) - input[0] = image.astype(np.float32) - input = image_util.oversample(input, self.crop_dims) - else: - image = image.resize(self.crop_dims, Image.ANTIALIAS) - input = np.zeros( - (1, self.crop_dims[0], self.crop_dims[1], 3), dtype=np.float32) - input[0] = np.array(image).astype(np.float32) - - data_in = [] - for img in input: - img = self.transformer.transformer(img).flatten() - data_in.append([img.tolist()]) - # paddle input: [[[]],[[]],...], [[]] is one sample. - return data_in - - def forward(self, input_data): - """ - return output arguments which are the Outputs() in network configure. - - input_data: py_paddle input data. - call forward. - """ - in_arg = self.converter(input_data) - return self.network.forwardTest(in_arg) - - def forward(self, data, output_layer): - """ - return output arguments which are the Outputs() in network configure. - - input_data: py_paddle input data. - call forward. - """ - input = self.converter(data) - self.network.forwardTest(input) - output = self.network.getLayerOutputs(output_layer) - res = {} - if isinstance(output_layer, basestring): - output_layer = [output_layer] - for name in output_layer: - # For oversampling, average predictions across crops. - # If not, the shape of output[name]: (1, class_number), - # the mean is also applicable. - res[name] = output[name]['value'].mean(0) - - return res - - def predict(self, data_file): - """ - call forward and predicting. - - data_file: input image list. - """ - image_files = open(data_file, 'rb').readlines() - results = {} - if self.output_layer is None: - self.output_layer = ["output"] - for line in image_files: - image = line.split()[0] - data = self.get_data(image) - prob = self.forward(data, self.output_layer) - lab = np.argsort(-prob[self.output_layer[0]]) - results[image] = lab[0] - logging.info("Label of %s is: %d", image, lab[0]) - return results - - def extract(self, data_file, output_dir, batch_size=10000): - """ - extract and save features of output layers, which are - specify in Outputs() in network configure. - - data_file: file name of input data. - output_dir: saved directory of extracted features. - batch_size: sample number of one batch file. - """ - if not os.path.exists(output_dir): - os.mkdir(output_dir) - - sample_num = 0 - batch_num = 0 - image_feature = {} - image_files = open(data_file, 'rb').readlines() - for idx, line in enumerate(image_files): - image = line.split()[0] - data = self.get_data(image) - feature = self.forward(data, self.output_layer) - # save extracted features - file_name = image.split("/")[-1] - image_feature[file_name] = feature - sample_num += 1 - if sample_num == batch_size: - batch_name = os.path.join(output_dir, 'batch_%d' % (batch_num)) - self.save_file(image_feature, batch_name) - logging.info('Finish batch %d', batch_num) - batch_num += 1 - sample_num = 0 - image_feature = {} - if idx % 1000 == 0: - logging.info('%d/%d, %s', idx, len(image_files), file_name) - if sample_num > 0: - batch_name = os.path.join(output_dir, 'batch_%d' % (batch_num)) - self.save_file(image_feature, batch_name) - logging.info('Finish batch %d', batch_num) - logging.info('Done: make image feature batch') - - def save_file(self, data, file): - of = open(file, 'wb') - cPickle.dump(data, of, protocol=cPickle.HIGHEST_PROTOCOL) - - -def option_parser(): - """ - Main entry for predciting - """ - usage = "%prog -c config -i data_list -w model_dir [options]" - parser = OptionParser(usage="usage: %s" % usage) - parser.add_option( - "-j", - "--job", - action="store", - dest="job_type", - help="job type: predict, extract\ - predict: predicting,\ - extract: extract features") - parser.add_option( - "-c", - "--conf", - action="store", - dest="train_conf", - help="network config") - parser.add_option( - "-i", "--data", action="store", dest="data_file", help="image list") - parser.add_option( - "-w", - "--model", - action="store", - dest="model_path", - default=None, - help="model path") - parser.add_option( - "-g", - "--use_gpu", - action="store", - dest="use_gpu", - default=True, - help="Whether to use gpu mode.") - parser.add_option( - "-o", - "--output_dir", - action="store", - dest="output_dir", - default="output", - help="output path") - parser.add_option( - "-m", - "--mean", - action="store", - dest="mean", - default=None, - help="mean file.") - parser.add_option( - "-p", - "--multi_crop", - action="store_true", - dest="multi_crop", - default=False, - help="Wether to use multiple crops on image.") - parser.add_option("-l", "--output_layer", action="store", - dest="output_layer", default=None, - help="--job=extract, specify layers to extract "\ - "features, --job=predict, specify layer of " - "classification probability, output in resnet.py.") - return parser.parse_args() - - -def main(): - """ - 1. parse input arguments. - 2. predicting or extract features according job type. - """ - options, args = option_parser() - obj = ImageClassifier( - options.train_conf, - options.model_path, - use_gpu=options.use_gpu, - mean_file=options.mean, - output_layer=options.output_layer, - oversample=options.multi_crop) - if options.job_type == "predict": - obj.predict(options.data_file) - - elif options.job_type == "extract": - obj.extract(options.data_file, options.output_dir) - - -if __name__ == '__main__': - main() diff --git a/v1_api_demo/model_zoo/resnet/example/.gitignore b/v1_api_demo/model_zoo/resnet/example/.gitignore deleted file mode 100644 index 4a2b5962a68..00000000000 --- a/v1_api_demo/model_zoo/resnet/example/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*image_list_provider_copy_1.py diff --git a/v1_api_demo/model_zoo/resnet/example/__init__.py b/v1_api_demo/model_zoo/resnet/example/__init__.py deleted file mode 100644 index f662d682632..00000000000 --- a/v1_api_demo/model_zoo/resnet/example/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. diff --git a/v1_api_demo/model_zoo/resnet/example/cat.jpg b/v1_api_demo/model_zoo/resnet/example/cat.jpg deleted file mode 100644 index 47b01db90eddc46ff845f10bc2accaf2364c272d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12881 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�L)kp1S05+XNhGI(P4E#Q}z*Z2th zy6Q46OVUZ4y`&zYiPABNj0ubFEU!dvPnZ(Lv1rov3s<0<>#tx+dzt-N_?6l!*tJPj z<_=21Y-&NSLLP4uwsto&?B`FB9rc%szLZs;hC$Danlj31G11TL@|iU+NllGwZ8VFe ziv2xHH$+yQEaxLK66Uv%ptKDfCuy3g`JZiszs&NiPLY>%t9uP$&j5!@p9u6|cvShe zSV5F+>U^8i6$MmtA8Y0;O($rXr{V{G1u ztN)ODQ@z>^x>+!1PnhE9RUX&^F-IV}tpn%V;*ZUjU-?#IW}kPf{t~eGmvMUQT?OXi z*9Y!G^gz=&fxpLB>TIyxQg1>@fKfsOpVzCi`JN#|+B}w@nob*`bthNMI%PW?9F97V z{sOut9a_{R#MLM)A6X@B%J92t+JdeZ)q{LB1?CwW4)OsOnZBVn4+D>E{5hVaT@L^B z{26t0UKvq9Dgn~v$ZTg8vqrNxZ2tGPPtf7Bssyv_7!M^)nu3GR^=8gk=#eu638Zay zSBm}%fRG;cscV+C^%JLMsc*^6bT8@2a7}%xj4nRj%KVnOhKEI~EVbl^%u(@dtszRs zpQr7<)yB(bs!lv+J#&KVgT8s!FRA{;Rn0<=xW<{vT+0F|O$ovFNDD~`)YxA@SRAd1 zf|+#D=TsV}O$~f#e}4&?e%_-CRnHH1TVu@b z1Y6rSxG7k)CmSW};u0-2xN_Ix^-a@aMb-B5@Kl)8GZ7d~*40f4V$wP;Yvrdfv3Ej^4{;uv+25qQo9O= zd#MH98M3nH>!9FDBwQ^3z00F1yGxoVqk=}Jw81C8ia(kkx0tv6q>}qMb36SmXVXIx zoFM{>BkG%abaL~WXTjc*2WH2-Z?56Q8kpF zPWu-i1qNj;R%$oCHujnP)L{62Vc)~@MT`*1N{az=%tnL$0K9DFU71P#qpc<%r8gaN zWLuq&=C{p4TpWnVE2jt?yGf`W3+|Dx@Sv3s?Q)@JPKFdu!{Q?UC8P8pdkH?3-Ir^X z-#HNAfjB9<94Ix?3QT>pxT z1VaI9@CZ>6tczsDXa8%?|0K!&``*tt3_yjDe2voo8bxPn~4cX06h|}PeKb(KyWu=ofj5BRZ3EhW; zUC;1X8-1*<%i;0hU2`S`%5My?vzv4|S3kAJ2h^4W$Qb><8t9-EOfQsw)6O@JTYEoC zdb>s3qF)SNq?3iw9@85*h?yUQb6iqik41=v{skbt`1IK;ys~z?3(x7}b*i#}kawed zN$uPdG|XcbWFdQ#?)%c>uJTz|9~-RT;j~La539h3+%)gB!7W-dg|&Z(&Z`t->#Fim z(Je=W%!_+Wk9oPt?BZD%_?=OqbyYkeT^IC~WmC?xTV(C-q%PE@k1F1;ReWG(&|qaf zoRYY7=k=8_liZ>L>ElOPD}sf)s%HBl-C)PDm4X?CBb1}tNwFh7TavS(enQIYmU9l9 zMz@PH77h2{I9E|A>6-&l*#(>SkFI3dE33zC6ya?Ug}FtTPcgy2I`p}yR$0S>Id5tY%x_0;MczdIXh8N-Ni zulqs~+I7J@xa623>Sd|L-H&)) zA~7ju13bPd{|hj`aubhG+ONS`HtvL&J_eeTw~qb=sPk7^fXqEIQO3!yN&>L=$bgkU zxt1;HkiKr>cgAVZ!y6Zs1A8NGL`c-O%XV0p85T@95?@0}Pa1%wlYP6`@9N}_9jr(^ zAWfPKCTsP`I32OfUMu4*x~d%ux&rcJ~aEREl^UX%h JXZ`*3e*j_iS4jW> diff --git a/v1_api_demo/model_zoo/resnet/example/dog.jpg b/v1_api_demo/model_zoo/resnet/example/dog.jpg deleted file mode 100644 index b9cc33cf069da5c453b97dbb7383838edd07c199..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71483 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

- - - - - - - - - - - - - - - - - - - - - - -
Model nameNumber of parametersF1 score
linear_crf 1.8M 0.937
rnn_crf 960K 0.941
-
-
diff --git a/v1_api_demo/sequence_tagging/rnn_crf.py b/v1_api_demo/sequence_tagging/rnn_crf.py deleted file mode 100644 index 937a34df103..00000000000 --- a/v1_api_demo/sequence_tagging/rnn_crf.py +++ /dev/null @@ -1,121 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -import math - -define_py_data_sources2( - train_list="data/train.list", - test_list="data/test.list", - module="dataprovider", - obj="process") - -batch_size = 16 -settings( - learning_method=MomentumOptimizer(), - batch_size=batch_size, - regularization=L2Regularization(batch_size * 1e-5), - model_average=ModelAverage(0.5), - learning_rate=2e-3, - learning_rate_decay_a=5e-7, - learning_rate_decay_b=0.5, ) - -word_dim = 128 -hidden_dim = 128 -with_rnn = True - -initial_std = 1 / math.sqrt(hidden_dim) -param_attr = ParamAttr(initial_std=initial_std) -cpu_layer_attr = ExtraLayerAttribute(device=-1) - -default_device(0) - -num_label_types = 23 - -features = data_layer(name="features", size=76328) -word = data_layer(name="word", size=6778) -pos = data_layer(name="pos", size=44) -chunk = data_layer( - name="chunk", size=num_label_types, layer_attr=cpu_layer_attr) - -emb = embedding_layer( - input=word, size=word_dim, param_attr=ParamAttr(initial_std=0)) - -hidden1 = mixed_layer( - size=hidden_dim, - act=STanhActivation(), - bias_attr=True, - input=[ - full_matrix_projection(emb), table_projection( - pos, param_attr=param_attr) - ]) - -if with_rnn: - rnn1 = recurrent_layer( - act=ReluActivation(), - bias_attr=True, - input=hidden1, - param_attr=ParamAttr(initial_std=0), ) - -hidden2 = mixed_layer( - size=hidden_dim, - act=STanhActivation(), - bias_attr=True, - input=[full_matrix_projection(hidden1)] + - ([full_matrix_projection( - rnn1, param_attr=ParamAttr(initial_std=0))] if with_rnn else []), ) - -if with_rnn: - rnn2 = recurrent_layer( - reverse=True, - act=ReluActivation(), - bias_attr=True, - input=hidden2, - param_attr=ParamAttr(initial_std=0), ) - -crf_input = mixed_layer( - size=num_label_types, - bias_attr=False, - input=[full_matrix_projection(hidden2), ] + - ([full_matrix_projection( - rnn2, param_attr=ParamAttr(initial_std=0))] if with_rnn else []), ) - -crf = crf_layer( - input=crf_input, - label=chunk, - param_attr=ParamAttr( - name="crfw", initial_std=0), - layer_attr=cpu_layer_attr, ) - -crf_decoding = crf_decoding_layer( - size=num_label_types, - input=crf_input, - label=chunk, - param_attr=ParamAttr(name="crfw"), - layer_attr=cpu_layer_attr, ) - -sum_evaluator( - name="error", - input=crf_decoding, ) - -chunk_evaluator( - name="chunk_f1", - input=crf_decoding, - label=chunk, - chunk_scheme="IOB", - num_chunk_types=11, ) - -inputs(word, pos, chunk, features) -outputs(crf) diff --git a/v1_api_demo/sequence_tagging/train.sh b/v1_api_demo/sequence_tagging/train.sh deleted file mode 100755 index 37e196c8420..00000000000 --- a/v1_api_demo/sequence_tagging/train.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -paddle train \ - --config rnn_crf.py \ - --parallel_nn=1 \ - --use_gpu=1 \ - --dot_period=10 \ - --log_period=1000 \ - --test_period=0 \ - --num_passes=10 \ -2>&1 | tee 'train.log' -paddle usage -l 'train.log' -e $? -n "sequence_tagging_train" >/dev/null 2>&1 diff --git a/v1_api_demo/sequence_tagging/train_linear.sh b/v1_api_demo/sequence_tagging/train_linear.sh deleted file mode 100755 index ad6e2d8ee7f..00000000000 --- a/v1_api_demo/sequence_tagging/train_linear.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -paddle train \ - --config linear_crf.py \ - --use_gpu=0 \ - --dot_period=100 \ - --log_period=10000 \ - --test_period=0 \ - --num_passes=10 -2>&1 | tee 'train_linear.log' -paddle usage -l 'train_linear.log' -e $? -n "sequence_tagging_train_linear" >/dev/null 2>&1 diff --git a/v1_api_demo/traffic_prediction/README b/v1_api_demo/traffic_prediction/README deleted file mode 100644 index 4c951885835..00000000000 --- a/v1_api_demo/traffic_prediction/README +++ /dev/null @@ -1,7 +0,0 @@ -run by: -cd ./data -sh get_data.sh -cd .. -sh train.sh -sh predict.sh - diff --git a/v1_api_demo/traffic_prediction/data/get_data.sh b/v1_api_demo/traffic_prediction/data/get_data.sh deleted file mode 100755 index f2fa548d470..00000000000 --- a/v1_api_demo/traffic_prediction/data/get_data.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors, Inc. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -set -e -set -x - -DIR="$( cd "$(dirname "$0")" ; pwd -P )" -cd $DIR - -#download the dataset -echo "Downloading traffic data..." -wget http://paddlepaddle.cdn.bcebos.com/demo/traffic/traffic_data.tar.gz - -#extract package -echo "Unzipping..." -tar -zxvf traffic_data.tar.gz - -echo "data/speeds.csv" > train.list -echo "data/speeds.csv" > test.list -echo "data/speeds.csv" > pred.list - -echo "Done." diff --git a/v1_api_demo/traffic_prediction/dataprovider.py b/v1_api_demo/traffic_prediction/dataprovider.py deleted file mode 100644 index c7883b6950c..00000000000 --- a/v1_api_demo/traffic_prediction/dataprovider.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors, Inc. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer.PyDataProvider2 import * -import sys -import numpy as np -TERM_NUM = 24 -FORECASTING_NUM = 24 -LABEL_VALUE_NUM = 4 - - -def initHook(settings, file_list, **kwargs): - """ - Init hook is invoked before process data. It will set obj.slots and store data meta. - - :param settings: global object. It will passed to process routine. - :type obj: object - :param file_list: the meta file object, which passed from trainer_config.py,but unused in this function. - :param kwargs: unused other arguments. - """ - del kwargs #unused - - settings.pool_size = sys.maxint - #Use a time seires of the past as feature. - #Dense_vector's expression form is [float,float,...,float] - settings.input_types = [dense_vector(TERM_NUM)] - #There are next FORECASTING_NUM fragments you need predict. - #Every predicted condition at time point has four states. - for i in range(FORECASTING_NUM): - settings.input_types.append(integer_value(LABEL_VALUE_NUM)) - - -@provider( - init_hook=initHook, cache=CacheType.CACHE_PASS_IN_MEM, should_shuffle=True) -def process(settings, file_name): - with open(file_name) as f: - #abandon fields name - f.next() - for row_num, line in enumerate(f): - speeds = map(int, line.rstrip('\r\n').split(",")[1:]) - # Get the max index. - end_time = len(speeds) - # Scanning and generating samples - for i in range(TERM_NUM, end_time - FORECASTING_NUM): - # For dense slot - pre_spd = map(float, speeds[i - TERM_NUM:i]) - - # Integer value need predicting, values start from 0, so every one minus 1. - fol_spd = [j - 1 for j in speeds[i:i + FORECASTING_NUM]] - - # Predicting label is missing, abandon the sample. - if -1 in fol_spd: - continue - yield [pre_spd] + fol_spd - - -def predict_initHook(settings, file_list, **kwargs): - settings.pool_size = sys.maxint - settings.input_types = [dense_vector(TERM_NUM)] - - -@provider(init_hook=predict_initHook, should_shuffle=False) -def process_predict(settings, file_name): - with open(file_name) as f: - #abandon fields name - f.next() - for row_num, line in enumerate(f): - speeds = map(int, line.rstrip('\r\n').split(",")) - end_time = len(speeds) - pre_spd = map(float, speeds[end_time - TERM_NUM:end_time]) - yield pre_spd diff --git a/v1_api_demo/traffic_prediction/gen_result.py b/v1_api_demo/traffic_prediction/gen_result.py deleted file mode 100644 index 3da70b30315..00000000000 --- a/v1_api_demo/traffic_prediction/gen_result.py +++ /dev/null @@ -1,61 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors, Inc. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -res = [] -with open('./rank-00000') as f: - for line in f: - pred = map(int, line.strip('\r\n;').split(";")) - #raw prediction range from 0 to 3 - res.append([i + 1 for i in pred]) - -file_name = open('./data/pred.list').read().strip('\r\n') - -FORECASTING_NUM = 24 -header = [ - 'id', - '201604200805', - '201604200810', - '201604200815', - '201604200820', - '201604200825', - '201604200830', - '201604200835', - '201604200840', - '201604200845', - '201604200850', - '201604200855', - '201604200900', - '201604200905', - '201604200910', - '201604200915', - '201604200920', - '201604200925', - '201604200930', - '201604200935', - '201604200940', - '201604200945', - '201604200950', - '201604200955', - '201604201000', -] -################### -## To CSV format ## -################### -with open(file_name) as f: - f.next() - print ','.join(header) - for row_num, line in enumerate(f): - fields = line.rstrip('\r\n').split(',') - linkid = fields[0] - print linkid + ',' + ','.join(map(str, res[row_num])) diff --git a/v1_api_demo/traffic_prediction/predict.sh b/v1_api_demo/traffic_prediction/predict.sh deleted file mode 100755 index 2dbd5e8805d..00000000000 --- a/v1_api_demo/traffic_prediction/predict.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors, Inc. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -cfg=trainer_config.py -# pass choice -model="output/pass-00000" -paddle train \ - --config=$cfg \ - --use_gpu=false \ - --job=test \ - --init_model_path=$model \ - --config_args=is_predict=1 \ - --predict_output_dir=. - -python gen_result.py > result.csv - -rm -rf rank-00000 diff --git a/v1_api_demo/traffic_prediction/train.sh b/v1_api_demo/traffic_prediction/train.sh deleted file mode 100755 index 48dfc5604f8..00000000000 --- a/v1_api_demo/traffic_prediction/train.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors, Inc. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -cfg=trainer_config.py -paddle train \ - --config=$cfg \ - --save_dir=./output \ - --trainer_count=4 \ - --log_period=1000 \ - --dot_period=10 \ - --num_passes=10 \ - --use_gpu=false \ - --show_parameter_stats_period=3000 \ - 2>&1 | tee 'train.log' diff --git a/v1_api_demo/traffic_prediction/trainer_config.py b/v1_api_demo/traffic_prediction/trainer_config.py deleted file mode 100755 index 52d678624af..00000000000 --- a/v1_api_demo/traffic_prediction/trainer_config.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors, Inc. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from paddle.trainer_config_helpers import * - -################################### DATA Configuration ############################################# -is_predict = get_config_arg('is_predict', bool, False) -trn = './data/train.list' if not is_predict else None -tst = './data/test.list' if not is_predict else './data/pred.list' -process = 'process' if not is_predict else 'process_predict' -define_py_data_sources2( - train_list=trn, test_list=tst, module="dataprovider", obj=process) -################################### Parameter Configuaration ####################################### -TERM_NUM = 24 -FORECASTING_NUM = 24 -emb_size = 16 -batch_size = 128 if not is_predict else 1 -settings( - batch_size=batch_size, - learning_rate=1e-3, - learning_method=RMSPropOptimizer()) -################################### Algorithm Configuration ######################################## - -output_label = [] - -link_encode = data_layer(name='link_encode', size=TERM_NUM) -for i in xrange(FORECASTING_NUM): - # Each task share same weight. - link_param = ParamAttr( - name='_link_vec.w', initial_max=1.0, initial_min=-1.0) - link_vec = fc_layer(input=link_encode, size=emb_size, param_attr=link_param) - score = fc_layer(input=link_vec, size=4, act=SoftmaxActivation()) - if is_predict: - maxid = maxid_layer(score) - output_label.append(maxid) - else: - # Multi-task training. - label = data_layer(name='label_%dmin' % ((i + 1) * 5), size=4) - cls = classification_cost( - input=score, name="cost_%dmin" % ((i + 1) * 5), label=label) - output_label.append(cls) -outputs(output_label) diff --git a/v1_api_demo/vae/README.md b/v1_api_demo/vae/README.md deleted file mode 100644 index e55d483b023..00000000000 --- a/v1_api_demo/vae/README.md +++ /dev/null @@ -1,13 +0,0 @@ -#Variational Autoencoder (VAE) - -This demo implements VAE training described in the original paper (https://arxiv.org/abs/1312.6114). - - -In order to run the model, first download the MNIST dataset by running the shell script in ./data. - -Then you can run the command below. The flag --useGpu specifies whether to use gpu for training (0 is cpu, 1 is gpu). - -$python vae_train.py [--use_gpu 1] - -The generated images will be stored in ./samples/ -The corresponding models will be stored in ./params/ diff --git a/v1_api_demo/vae/data/get_mnist_data.sh b/v1_api_demo/vae/data/get_mnist_data.sh deleted file mode 100755 index a77c81bf5af..00000000000 --- a/v1_api_demo/vae/data/get_mnist_data.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env sh -# This script downloads the mnist data and unzips it. -set -e -DIR="$( cd "$(dirname "$0")" ; pwd -P )" -rm -rf "$DIR/mnist_data" -mkdir "$DIR/mnist_data" -cd "$DIR/mnist_data" - -echo "Downloading..." - -for fname in train-images-idx3-ubyte train-labels-idx1-ubyte t10k-images-idx3-ubyte t10k-labels-idx1-ubyte -do - if [ ! -e $fname ]; then - wget --no-check-certificate http://yann.lecun.com/exdb/mnist/${fname}.gz - gunzip ${fname}.gz - fi -done diff --git a/v1_api_demo/vae/dataloader.py b/v1_api_demo/vae/dataloader.py deleted file mode 100644 index e9ff95d44f8..00000000000 --- a/v1_api_demo/vae/dataloader.py +++ /dev/null @@ -1,60 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import numpy as np - - -class MNISTloader(): - def __init__(self, - data_path="./data/mnist_data/", - batch_size=60, - process='train'): - self.batch_size = batch_size - self.data_path = data_path - self._pointer = 0 - self.image_batches = np.array([]) - self.process = process - - def _extract_images(self, filename, n): - f = open(filename, 'rb') - f.read(16) - data = np.fromfile(f, 'ubyte', count=n * 28 * 28).reshape((n, 28 * 28)) - #Mapping data into [-1, 1] - data = data / 255. * 2. - 1 - data_batches = np.split(data, 60000 / self.batch_size, 0) - - f.close() - - return data_batches - - @property - def pointer(self): - return self._pointer - - def load_data(self): - TRAIN_IMAGES = '%s/train-images-idx3-ubyte' % self.data_path - TEST_IMAGES = '%s/t10k-images-idx3-ubyte' % self.data_path - - if self.process == 'train': - self.image_batches = self._extract_images(TRAIN_IMAGES, 60000) - else: - self.image_batches = self._extract_images(TEST_IMAGES, 10000) - - def next_batch(self): - batch = self.image_batches[self._pointer] - self._pointer = (self._pointer + 1) % (60000 / self.batch_size) - return np.array(batch) - - def reset_pointer(self): - self._pointer = 0 diff --git a/v1_api_demo/vae/vae_conf.py b/v1_api_demo/vae/vae_conf.py deleted file mode 100644 index 301dd23793d..00000000000 --- a/v1_api_demo/vae/vae_conf.py +++ /dev/null @@ -1,116 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * -import numpy as np - -is_generating = get_config_arg("is_generating", bool, False) - -settings(batch_size=32, learning_rate=1e-3, learning_method=AdamOptimizer()) - -X_dim = 28 * 28 -h_dim = 128 -z_dim = 100 - - -def reparameterization(mu, logvar): - eps = ParamAttr(initial_mean=0., initial_std=1) - with mixed_layer() as sigma: - sigma += dotmul_projection(layer_math.exp(logvar) * 0.5, param_attr=eps) - return mu + sigma - - -def q_func(X): - """ - xavier initialization - """ - param_attr = ParamAttr( - name='share.w', initial_mean=0., initial_std=1. / np.sqrt(X_dim / 2.)) - mu_param = ParamAttr( - name='mu.w', initial_mean=0., initial_std=1. / np.sqrt(h_dim / 2.)) - logvar_param = ParamAttr( - name='logvar.w', initial_mean=0., initial_std=1. / np.sqrt(h_dim / 2.)) - - bias_attr = ParamAttr(name='share.bias', initial_mean=0., initial_std=0.) - mu_bias = ParamAttr(name='mu.bias', initial_mean=0., initial_std=0.) - logvar_bias = ParamAttr(name='logvar.bias', initial_mean=0., initial_std=0.) - - share_layer = fc_layer( - X, - size=h_dim, - param_attr=param_attr, - bias_attr=bias_attr, - act=ReluActivation()) - - return (fc_layer( - share_layer, - size=z_dim, - param_attr=mu_param, - bias_attr=mu_bias, - act=LinearActivation()), fc_layer( - share_layer, - size=z_dim, - param_attr=logvar_param, - bias_attr=logvar_bias, - act=LinearActivation())) - - -def generator(z): - - hidden_param = ParamAttr( - name='hidden.w', initial_mean=0., initial_std=1. / np.sqrt(z_dim / 2.)) - hidden_bias = ParamAttr(name='hidden.bias', initial_mean=0., initial_std=0.) - prob_param = ParamAttr( - name='prob.w', initial_mean=0., initial_std=1. / np.sqrt(h_dim / 2.)) - prob_bias = ParamAttr(name='prob.bias', initial_mean=0., initial_std=0.) - - hidden_layer = fc_layer( - z, - size=h_dim, - act=ReluActivation(), - param_attr=hidden_param, - bias_attr=hidden_bias) - prob = fc_layer( - hidden_layer, - size=X_dim, - act=SigmoidActivation(), - param_attr=prob_param, - bias_attr=prob_bias) - - return prob - - -def reconstruct_error(prob, X): - cost = multi_binary_label_cross_entropy(input=prob, label=X) - return cost - - -def KL_loss(mu, logvar): - with mixed_layer() as mu_square: - mu_square += dotmul_operator(mu, mu, scale=1.) - - cost = 0.5 * sum_cost(layer_math.exp(logvar) + mu_square - 1. - logvar) - - return cost - - -if not is_generating: - x_batch = data_layer(name='x_batch', size=X_dim) - mu, logvar = q_func(x_batch) - z_samples = reparameterization(mu, logvar) - prob = generator(z_samples) - outputs(reconstruct_error(prob, x_batch) + KL_loss(mu, logvar)) -else: - z_samples = data_layer(name='noise', size=z_dim) - outputs(generator(z_samples)) diff --git a/v1_api_demo/vae/vae_train.py b/v1_api_demo/vae/vae_train.py deleted file mode 100644 index 1babb011c77..00000000000 --- a/v1_api_demo/vae/vae_train.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse -import random -import numpy as np -import cPickle -import sys, os -from PIL import Image - -from paddle.trainer.config_parser import parse_config -from paddle.trainer.config_parser import logger -import py_paddle.swig_paddle as api -import dataloader -import matplotlib.pyplot as plt - - -def plot_samples(samples): - fig = plt.figure(figsize=(4, 4)) - gs = gridspec.GridSpec(4, 4) - gs.update(wspace=0.05, hspace=0.05) - for i, sample in enumerate(samples): - plt.subplot(gs[i]) - plt.axis('off') - plt.imshow(sample.reshape(28, 28), cmap='Greys_r') - - return fig - - -def CHECK_EQ(a, b): - assert a == b, "a=%s, b=%s" % (a, b) - - -def get_fake_samples(generator_machine, batch_size, noise): - gen_inputs = api.Arguments.createArguments(1) - gen_inputs.setSlotValue(0, api.Matrix.createDenseFromNumpy(noise)) - gen_outputs = api.Arguments.createArguments(0) - generator_machine.forward(gen_inputs, gen_outputs, api.PASS_TEST) - fake_samples = gen_outputs.getSlotValue(0).copyToNumpyMat() - return fake_samples - - -def copy_shared_parameters(src, dst): - ''' - copy the parameters from src to dst - :param src: the source of the parameters - :type src: GradientMachine - :param dst: the destination of the parameters - :type dst: GradientMachine - ''' - src_params = [src.getParameter(i) for i in xrange(src.getParameterSize())] - src_params = dict([(p.getName(), p) for p in src_params]) - - for i in xrange(dst.getParameterSize()): - dst_param = dst.getParameter(i) - src_param = src_params.get(dst_param.getName(), None) - if src_param is None: - continue - src_value = src_param.getBuf(api.PARAMETER_VALUE) - dst_value = dst_param.getBuf(api.PARAMETER_VALUE) - CHECK_EQ(len(src_value), len(dst_value)) - dst_value.copyFrom(src_value) - dst_param.setValueUpdated() - - -def find(iterable, cond): - for item in iterable: - if cond(item): - return item - return None - - -def get_layer_size(model_conf, layer_name): - layer_conf = find(model_conf.layers, lambda x: x.name == layer_name) - assert layer_conf is not None, "Cannot find '%s' layer" % layer_name - return layer_conf.size - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument( - "--use_gpu", default="1", help="1 means use gpu for training") - parser.add_argument("--gpu_id", default="0", help="the gpu_id parameter") - args = parser.parse_args() - use_gpu = args.use_gpu - assert use_gpu in ["0", "1"] - - if not os.path.exists("./samples/"): - os.makedirs("./samples/") - - if not os.path.exists("./params/"): - os.makedirs("./params/") - - api.initPaddle('--use_gpu=' + use_gpu, '--dot_period=10', - '--log_period=1000', '--gpu_id=' + args.gpu_id, - '--save_dir=' + "./params/") - - conf = "vae_conf.py" - - trainer_conf = parse_config(conf, "is_generating=False") - gener_conf = parse_config(conf, "is_generating=True") - - batch_size = trainer_conf.opt_config.batch_size - - noise_dim = get_layer_size(gener_conf.model_config, "noise") - - mnist = dataloader.MNISTloader(batch_size=batch_size) - mnist.load_data() - - training_machine = api.GradientMachine.createFromConfigProto( - trainer_conf.model_config) - - generator_machine = api.GradientMachine.createFromConfigProto( - gener_conf.model_config) - - trainer = api.Trainer.create(trainer_conf, training_machine) - - trainer.startTrain() - - for train_pass in xrange(100): - trainer.startTrainPass() - mnist.reset_pointer() - i = 0 - it = 0 - while mnist.pointer != 0 or i == 0: - X = mnist.next_batch().astype('float32') - - inputs = api.Arguments.createArguments(1) - inputs.setSlotValue(0, api.Matrix.createDenseFromNumpy(X)) - - trainer.trainOneDataBatch(batch_size, inputs) - - if it % 1000 == 0: - - outputs = api.Arguments.createArguments(0) - training_machine.forward(inputs, outputs, api.PASS_TEST) - loss = np.mean(outputs.getSlotValue(0).copyToNumpyMat()) - print "\niter: {}".format(str(it).zfill(3)) - print "VAE loss: {}".format(str(loss).zfill(3)) - - #Sync parameters between networks (GradientMachine) at the beginning - copy_shared_parameters(training_machine, generator_machine) - - z_samples = np.random.randn(batch_size, - noise_dim).astype('float32') - samples = get_fake_samples(generator_machine, batch_size, - z_samples) - - #Generating the first 16 images for a picture. - figure = plot_samples(samples[:16]) - plt.savefig( - "./samples/{}_{}.png".format( - str(train_pass).zfill(3), str(i).zfill(3)), - bbox_inches='tight') - plt.close(figure) - i += 1 - it += 1 - - trainer.finishTrainPass() - trainer.finishTrain() - - -if __name__ == '__main__': - main() -- GitLab From a0ac133987a925df1907f3804ccb3cbc32b763b7 Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Tue, 16 Jan 2018 14:58:22 +0800 Subject: [PATCH 0294/2305] update job --- benchmark/cluster/v2/trainer.yaml | 6 ++++-- benchmark/cluster/v2/vgg16.py | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/benchmark/cluster/v2/trainer.yaml b/benchmark/cluster/v2/trainer.yaml index 33c95df365a..a4958b22788 100644 --- a/benchmark/cluster/v2/trainer.yaml +++ b/benchmark/cluster/v2/trainer.yaml @@ -21,6 +21,8 @@ spec: env: - name: PADDLE_JOB_NAME value: vgg16job + - name: OMP_NUM_THREADS + value: "1" - name: TRAINERS value: "20" - name: PSERVERS @@ -36,7 +38,7 @@ spec: - name: PADDLE_INIT_NICS value: "xgbe0" - name: PADDLE_INIT_TRAINER_COUNT - value: "1" + value: "2" - name: PADDLE_INIT_PORTS_NUM value: "1" - name: PADDLE_INIT_PORTS_NUM_FOR_SPARSE @@ -44,7 +46,7 @@ spec: - name: PADDLE_INIT_NUM_GRADIENT_SERVERS value: "20" - name: PADDLE_INIT_NUM_PASSES - value: "1" + value: "2" - name: PADDLE_INIT_USE_GPU value: "0" - name: LD_LIBRARY_PATH diff --git a/benchmark/cluster/v2/vgg16.py b/benchmark/cluster/v2/vgg16.py index 8644a547b33..85502c38e4d 100644 --- a/benchmark/cluster/v2/vgg16.py +++ b/benchmark/cluster/v2/vgg16.py @@ -74,14 +74,14 @@ def vgg19(input, class_dim): def main(): - paddle.init(use_gpu=False, trainer_count=1) + paddle.init(use_gpu=False) image = paddle.layer.data( name="image", type=paddle.data_type.dense_vector(DATA_DIM)) lbl = paddle.layer.data( name="label", type=paddle.data_type.integer_value(CLASS_DIM)) extra_layers = None - learning_rate = 0.01 + learning_rate = 1e-3 out = vgg16(image, class_dim=CLASS_DIM) cost = paddle.layer.classification_cost(input=out, label=lbl) -- GitLab From 281e93bcbb3e67996f3b7a2f76df1da0071969db Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Tue, 16 Jan 2018 15:15:10 +0800 Subject: [PATCH 0295/2305] Remove 'top 1' from CPU and GPU kernel 1. Remove 'top 1'(or argmax) from CPU and GPU kernel 2. Add a new test case 3. Refine doc --- ...c_greedy_decode_op.cc => ctc_decode_op.cc} | 44 ++++++----- ...c_greedy_decode_op.cu => ctc_decode_op.cu} | 77 ++++--------------- ...ctc_greedy_decode_op.h => ctc_decode_op.h} | 34 ++++---- .../paddle/v2/fluid/tests/test_ctc_decode.py | 62 +++++++++++++++ .../v2/fluid/tests/test_ctc_greedy_decode.py | 56 -------------- 5 files changed, 118 insertions(+), 155 deletions(-) rename paddle/operators/{ctc_greedy_decode_op.cc => ctc_decode_op.cc} (67%) rename paddle/operators/{ctc_greedy_decode_op.cu => ctc_decode_op.cu} (60%) rename paddle/operators/{ctc_greedy_decode_op.h => ctc_decode_op.h} (75%) create mode 100644 python/paddle/v2/fluid/tests/test_ctc_decode.py delete mode 100644 python/paddle/v2/fluid/tests/test_ctc_greedy_decode.py diff --git a/paddle/operators/ctc_greedy_decode_op.cc b/paddle/operators/ctc_decode_op.cc similarity index 67% rename from paddle/operators/ctc_greedy_decode_op.cc rename to paddle/operators/ctc_decode_op.cc index 3c9b705f7f6..b290b11d1d1 100644 --- a/paddle/operators/ctc_greedy_decode_op.cc +++ b/paddle/operators/ctc_decode_op.cc @@ -29,14 +29,8 @@ class CTCGreedyDecodeOp : public framework::OperatorWithKernel { auto input_dims = ctx->GetInputDim("Input"); - int sequence_width = - static_cast(framework::product(input_dims) / input_dims[0]); - int blank = ctx->Attrs().Get("blank"); - PADDLE_ENFORCE((blank >= 0) && (blank < sequence_width), - "The value of Attr(blank) should be in interval [0, %d).", - sequence_width); // TODO(wanghaoshuang): it is tricky to set the wrong dimension here. - ctx->SetOutputDim("Output", {input_dims[0], 1}); + ctx->SetOutputDim("Output", input_dims); } protected: @@ -53,25 +47,37 @@ class CTCGreedyDecodeOpMaker : public framework::OpProtoAndCheckerMaker { CTCGreedyDecodeOpMaker(OpProto* proto, OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("Input", - "(LodTensor, default: LoDTensor), the unscaled " - "probabilities of variable-length sequences, which is a 2-D " - "Tensor with LoD information. It's shape is " - "[Lp, num_classes + 1], where Lp is the sum of all input " - "sequences' length and num_classes is the true number of classes " - "(not including the blank label)."); - AddOutput("Output", "(Tensor, default: Tensor), the decode result "); + "(LodTensor, default: LoDTensor), Its shape is " + "[Lp, 1], where Lp is the sum of all input sequences' length."); + AddOutput("Output", "(Tensor, default: Tensor), The decode result."); AddAttr("blank", "(int, default: 0), the blank label setted in Connectionist " - "Temporal Classification (CTC) op, and it is in the " - "half-opened interval [0, num_classes + 1).") + "Temporal Classification (CTC) op.") .SetDefault(0); AddAttr("merge_repeated", "(bool, default: true), whether to " "merge repeated elements between two blanks. ") .SetDefault(true); AddComment(R"DOC( -CTCGreedyDecoder is an implementation of the simple best path decoding -algorithm, selecting at each timestep the most likely class at each timestep. +CTCDecoder is used to merge repeated elements between two blanks +and then delete all blanks in sequence. + +Given: + Input.data = [0, 1, 2, 2, 0, 4, 0, 4, 5, 0, 6, + 6, 0, 0, 7, 7, 7, 0] + Input.dims = {18, 1} + Input.LoD = [[0, 11, 18]] + +And: + blank = 0 + merge_repeated = True + +Then: + Output.data = [1, 2, 4, 4, 5, 6, + 6, 7] + Output.dims = {8, 1} + Output.LoD = [[0, 6, 8]] + )DOC"); } }; @@ -85,4 +91,4 @@ REGISTER_OPERATOR(ctc_greedy_decode, ops::CTCGreedyDecodeOp, paddle::framework::EmptyGradOpMaker); REGISTER_OP_CPU_KERNEL( ctc_greedy_decode, - ops::CTCGreedyDecodeKernel); + ops::CTCGreedyDecodeKernel); diff --git a/paddle/operators/ctc_greedy_decode_op.cu b/paddle/operators/ctc_decode_op.cu similarity index 60% rename from paddle/operators/ctc_greedy_decode_op.cu rename to paddle/operators/ctc_decode_op.cu index 43a78745aca..e9cdad7c26b 100644 --- a/paddle/operators/ctc_greedy_decode_op.cu +++ b/paddle/operators/ctc_decode_op.cu @@ -16,62 +16,20 @@ limitations under the License. */ #include #include #include "paddle/operators/ctc_greedy_decode_op.h" -#include "paddle/platform/cuda_helper.h" -#include "paddle/platform/gpu_info.h" namespace paddle { namespace operators { -using platform::PADDLE_CUDA_NUM_THREADS; - -__device__ static float atomicMaxF(float* address, float val) { - int* address_as_i = (int*)address; - int old = *address_as_i, assumed; - do { - assumed = old; - old = ::atomicCAS(address_as_i, assumed, - __float_as_int(::fmaxf(val, __int_as_float(assumed)))); - } while (assumed != old); - return __int_as_float(old); -} - -template -__global__ void ArgmaxCudaKernel(const size_t seq_width, const T* logits, - int* output) { - T local_max_value = 0; - int local_max_index = 0; - __shared__ T max_value; - if (threadIdx.x == 0) { - max_value = 0; - } - __syncthreads(); - - for (int i = threadIdx.x; i < seq_width; i += BlockSize) { - T value = logits[blockIdx.x * seq_width + i]; - if (value > local_max_value) { - local_max_value = value; - local_max_index = i; - } - } - - atomicMaxF(&max_value, local_max_value); - - __syncthreads(); - - if (local_max_value == max_value) { - output[blockIdx.x] = local_max_index; - } -} template -__global__ void MergeAndDelCudaKernel(const int64_t num_token, int* tokens, +__global__ void MergeAndDelCudaKernel(const int64_t num_token, const T* tokens, const size_t num_seq, size_t* lod0, const int blank, const int merge_repeated, - size_t* out_lod0, int* output) { + size_t* out_lod0, T* output) { int ouput_idx = 0; out_lod0[0] = 0; for (int i = 0; i < num_seq; ++i) { - int pre_token = -1; + T pre_token = -1; for (int j = lod0[i]; j < lod0[i + 1]; ++j) { if (tokens[j] != blank && !(merge_repeated && tokens[j] == pre_token)) { output[ouput_idx] = tokens[j]; @@ -89,44 +47,39 @@ class CTCGreedyDecodeOpCUDAKernel : public framework::OpKernel { void Compute(const framework::ExecutionContext& ctx) const override { PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()), "It must use CUDAPlace."); + const size_t level = 0; auto* input = ctx.Input("Input"); auto* output = ctx.Output("Output"); + auto input_lod = framework::ToAbsOffset(input->lod()); + const T* tokens = input->data(); const int64_t num_tokens = input->dims()[0]; - const size_t seq_width = input->numel() / num_tokens; - const T* logits = input->data(); - Tensor tmp; - int* tokens = tmp.mutable_data({num_tokens, 1}, ctx.GetPlace()); - // get argmax - // platform::GpuMemsetAsync(args, 0, sizeof(float), stream); - - auto stream = ctx.cuda_device_context().stream(); - ArgmaxCudaKernel<<< - num_tokens, PADDLE_CUDA_NUM_THREADS, 0, stream>>>(seq_width, logits, - tokens); - - const size_t level = 0; - auto input_lod = framework::ToAbsOffset(input->lod()); const size_t num_seq = input_lod[level].size() - 1; + const int blank = ctx.Attr("blank"); const int merge_repeated = static_cast(ctx.Attr("merge_repeated")); + // prepare a lod to record lod information while merging elements thrust::device_vector dev_out_lod0(input_lod[level].size()); size_t* dev_out_lod0_ptr = thrust::raw_pointer_cast(dev_out_lod0.data()); - int* output_data = - output->mutable_data({num_tokens, 1}, ctx.GetPlace()); + // merge elements and delete blank + T* output_data = output->mutable_data({num_tokens, 1}, ctx.GetPlace()); + + auto stream = ctx.cuda_device_context().stream(); MergeAndDelCudaKernel<<<1, 1, 0, stream>>>( num_tokens, tokens, num_seq, input_lod[level].data(), blank, merge_repeated, dev_out_lod0_ptr, output_data); + // set output lod thrust::host_vector host_out_lod0(dev_out_lod0.begin(), dev_out_lod0.end()); framework::LoD out_lod; out_lod.push_back(host_out_lod0); output->set_lod(out_lod); + // resize output dims output->Resize({static_cast(host_out_lod0.back()), 1}); } }; @@ -135,4 +88,4 @@ class CTCGreedyDecodeOpCUDAKernel : public framework::OpKernel { } // namespace paddle REGISTER_OP_CUDA_KERNEL(ctc_greedy_decode, - paddle::operators::CTCGreedyDecodeOpCUDAKernel); + paddle::operators::CTCGreedyDecodeOpCUDAKernel); diff --git a/paddle/operators/ctc_greedy_decode_op.h b/paddle/operators/ctc_decode_op.h similarity index 75% rename from paddle/operators/ctc_greedy_decode_op.h rename to paddle/operators/ctc_decode_op.h index f12ea6c541b..30bb53e157f 100644 --- a/paddle/operators/ctc_greedy_decode_op.h +++ b/paddle/operators/ctc_decode_op.h @@ -16,7 +16,6 @@ limitations under the License. */ #include #include "paddle/framework/op_registry.h" -#include "unsupported/Eigen/CXX11/Tensor" namespace paddle { namespace operators { @@ -30,8 +29,9 @@ class CTCGreedyDecodeKernel : public framework::OpKernel { auto* input = ctx.Input("Input"); auto* output = ctx.Output("Output"); const size_t level = 0; - auto input_lod = framework::ToAbsOffset(input->lod()); + + // check input dims and lod auto input_dims = input->dims(); PADDLE_ENFORCE_EQ(input_dims[0], static_cast(input_lod[level].back()), @@ -39,38 +39,36 @@ class CTCGreedyDecodeKernel : public framework::OpKernel { "the sum of all sequences' lengths."); const size_t num_sequences = input_lod[level].size() - 1; - const size_t sequence_width = input->numel() / input_dims[0]; size_t blank = static_cast(ctx.Attr("blank")); bool merge_repeated = ctx.Attr("merge_repeated"); + + // merge repeated tokens and delete blank std::vector> pathes(num_sequences); std::vector output_lod0(1, 0); - const T* input_data = input->data(); - Eigen::Map< - Eigen::Matrix> - input_mat(const_cast(input_data), input->numel() / sequence_width, - sequence_width); - - size_t max_class_idx; - size_t prev_class_idx = -1; for (size_t seq_idx = 0; seq_idx < num_sequences; ++seq_idx) { + T prev_token = -1; for (size_t i = input_lod[level][seq_idx]; i < input_lod[level][seq_idx + 1]; ++i) { - input_mat.row(i).maxCoeff(&max_class_idx); - if (max_class_idx != blank && - !(merge_repeated && max_class_idx == prev_class_idx)) { - pathes[seq_idx].push_back(max_class_idx); + if (input_data[i] != blank && + !(merge_repeated && input_data[i] == prev_token)) { + pathes[seq_idx].push_back(input_data[i]); } - prev_class_idx = max_class_idx; + prev_token = input_data[i]; } output_lod0.push_back(output_lod0.back() + pathes[seq_idx].size()); } + + // set output lod framework::LoD output_lod; output_lod.push_back(output_lod0); output->set_lod(output_lod); - int64_t num_step = static_cast(output_lod0.back()); - int* output_data = output->mutable_data({num_step, 1}, ctx.GetPlace()); + // resize output dims + T* output_data = output->mutable_data( + {static_cast(output_lod0.back()), 1}, ctx.GetPlace()); + + // copy result to output for (int i = 0; i < num_sequences; ++i) { memcpy(output_data + output_lod0[i], pathes[i].data(), sizeof(int) * pathes[i].size()); diff --git a/python/paddle/v2/fluid/tests/test_ctc_decode.py b/python/paddle/v2/fluid/tests/test_ctc_decode.py new file mode 100644 index 00000000000..3b7486cfb98 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_ctc_decode.py @@ -0,0 +1,62 @@ +import sys +import unittest +import numpy as np +from op_test import OpTest +from test_softmax_op import stable_softmax + + +def CTCDecode(input, lod, blank, merge_repeated): + lod0 = lod[0] + result = [] + for i in range(len(lod0) - 1): + prev_token = -1 + for j in range(lod0[i], lod0[i + 1]): + token = input[j][0] + if (token != blank) and not (merge_repeated and + token == prev_token): + result.append(token) + prev_token = token + result = np.array(result).reshape([len(result), 1]).astype("int32") + return result + + +class TestCTCDecodeOp(OpTest): + def config(self): + self.op_type = "ctc_greedy_decode" + self.input_lod = [[0, 11, 18]] + self.blank = 0 + self.merge_repeated = False + self.input = np.array( + [0, 1, 2, 2, 0, 4, 0, 4, 5, 0, 6, 6, 0, 0, 7, 7, 7, 0]).reshape( + [18, 1]).astype("int32") + + def setUp(self): + self.config() + output = CTCDecode(self.input, self.input_lod, self.blank, + self.merge_repeated) + + self.inputs = {"Input": (self.input, self.input_lod), } + self.outputs = {"Output": output} + self.attrs = { + "blank": self.blank, + "merge_repeated": self.merge_repeated + } + + def test_check_output(self): + self.check_output() + pass + + +class TestCTCDecodeOpCase1(TestCTCDecodeOp): + def config(self): + self.op_type = "ctc_greedy_decode" + self.input_lod = [[0, 11, 18]] + self.blank = 0 + self.merge_repeated = True + self.input = np.array( + [0, 1, 2, 2, 0, 4, 0, 4, 5, 0, 6, 6, 0, 0, 7, 7, 7, 0]).reshape( + [18, 1]).astype("int32") + + +if __name__ == "__main__": + unittest.main() diff --git a/python/paddle/v2/fluid/tests/test_ctc_greedy_decode.py b/python/paddle/v2/fluid/tests/test_ctc_greedy_decode.py deleted file mode 100644 index 23fceb6dcdb..00000000000 --- a/python/paddle/v2/fluid/tests/test_ctc_greedy_decode.py +++ /dev/null @@ -1,56 +0,0 @@ -import sys -import unittest -import numpy as np -from op_test import OpTest -from test_softmax_op import stable_softmax - - -def CTCGreedyDecode(softmax, blank, merge_repeated): - prev_token = -1 - result = [] - for token in np.argmax(softmax, axis=1): - if (token != blank) and not (merge_repeated and token == prev_token): - result.append(token) - return np.array(result).reshape([len(result), 1]) - - -class TestCTCGreedyDecodeOp(OpTest): - def config(self): - self.op_type = "ctc_greedy_decode" - self.batch_size = 4 - self.num_classes = 8 - self.input_lod = [[0, 4, 5, 8, 11]] - self.blank = 7 - self.merge_repeated = True - - def setUp(self): - self.config() - input = np.random.uniform( - 0.1, 1.0, - [self.input_lod[0][-1], self.num_classes]).astype("float32") - softmax = np.apply_along_axis(stable_softmax, 1, input) - output = CTCGreedyDecode(softmax, self.blank, self.merge_repeated) - - self.inputs = {"Input": (softmax, self.input_lod), } - self.outputs = {"Output": output} - self.attrs = { - "blank": self.blank, - "merge_repeated": self.merge_repeated - } - - def test_check_output(self): - self.check_output() - - -class TestCTCGreedyDecodeOpCase1(TestCTCGreedyDecodeOp): - def config(self): - self.op_type = "ctc_greedy_decode" - self.batch_size = 4 - self.num_classes = 1025 - self.input_lod = [[0, 4, 5, 8, 11]] - self.blank = 0 - self.merge_repeated = True - - -if __name__ == "__main__": - unittest.main() -- GitLab From b315a408e915e49fe8ffe4cf66bddfc512348e9d Mon Sep 17 00:00:00 2001 From: typhoonzero Date: Tue, 16 Jan 2018 15:42:49 +0800 Subject: [PATCH 0296/2305] update --- benchmark/cluster/v2/vgg16.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/cluster/v2/vgg16.py b/benchmark/cluster/v2/vgg16.py index 85502c38e4d..400dcf1b41c 100644 --- a/benchmark/cluster/v2/vgg16.py +++ b/benchmark/cluster/v2/vgg16.py @@ -81,7 +81,7 @@ def main(): name="label", type=paddle.data_type.integer_value(CLASS_DIM)) extra_layers = None - learning_rate = 1e-3 + learning_rate = 1e-3 / 20 out = vgg16(image, class_dim=CLASS_DIM) cost = paddle.layer.classification_cost(input=out, label=lbl) -- GitLab From 10dd632659012374f827ae0208c05b0eb5c17fb6 Mon Sep 17 00:00:00 2001 From: wanghaoshuang Date: Tue, 16 Jan 2018 15:56:52 +0800 Subject: [PATCH 0297/2305] Rename 'ctc_greedy_decode' to 'ctc_decode' --- paddle/operators/ctc_decode_op.cc | 18 ++++++++---------- paddle/operators/ctc_decode_op.cu | 8 ++++---- paddle/operators/ctc_decode_op.h | 2 +- .../paddle/v2/fluid/tests/test_ctc_decode.py | 4 ++-- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/paddle/operators/ctc_decode_op.cc b/paddle/operators/ctc_decode_op.cc index b290b11d1d1..480c9ae133c 100644 --- a/paddle/operators/ctc_decode_op.cc +++ b/paddle/operators/ctc_decode_op.cc @@ -12,20 +12,20 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -#include "paddle/operators/ctc_greedy_decode_op.h" +#include "paddle/operators/ctc_decode_op.h" namespace paddle { namespace operators { -class CTCGreedyDecodeOp : public framework::OperatorWithKernel { +class CTCDecodeOp : public framework::OperatorWithKernel { public: using framework::OperatorWithKernel::OperatorWithKernel; void InferShape(framework::InferShapeContext* ctx) const override { PADDLE_ENFORCE(ctx->HasInput("Input"), - "Input of CTCGreedyDecodeOp should not be null."); + "Input of CTCDecodeOp should not be null."); PADDLE_ENFORCE(ctx->HasOutput("Output"), - "Output of CTCGreedyDecodeOp should not be null."); + "Output of CTCDecodeOp should not be null."); auto input_dims = ctx->GetInputDim("Input"); @@ -42,9 +42,9 @@ class CTCGreedyDecodeOp : public framework::OperatorWithKernel { } }; -class CTCGreedyDecodeOpMaker : public framework::OpProtoAndCheckerMaker { +class CTCDecodeOpMaker : public framework::OpProtoAndCheckerMaker { public: - CTCGreedyDecodeOpMaker(OpProto* proto, OpAttrChecker* op_checker) + CTCDecodeOpMaker(OpProto* proto, OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("Input", "(LodTensor, default: LoDTensor), Its shape is " @@ -86,9 +86,7 @@ Then: } // namespace paddle namespace ops = paddle::operators; -REGISTER_OPERATOR(ctc_greedy_decode, ops::CTCGreedyDecodeOp, - ops::CTCGreedyDecodeOpMaker, +REGISTER_OPERATOR(ctc_decode, ops::CTCDecodeOp, ops::CTCDecodeOpMaker, paddle::framework::EmptyGradOpMaker); REGISTER_OP_CPU_KERNEL( - ctc_greedy_decode, - ops::CTCGreedyDecodeKernel); + ctc_decode, ops::CTCDecodeKernel); diff --git a/paddle/operators/ctc_decode_op.cu b/paddle/operators/ctc_decode_op.cu index e9cdad7c26b..b10db100f7f 100644 --- a/paddle/operators/ctc_decode_op.cu +++ b/paddle/operators/ctc_decode_op.cu @@ -15,7 +15,7 @@ limitations under the License. */ #include #include #include -#include "paddle/operators/ctc_greedy_decode_op.h" +#include "paddle/operators/ctc_decode_op.h" namespace paddle { namespace operators { @@ -42,7 +42,7 @@ __global__ void MergeAndDelCudaKernel(const int64_t num_token, const T* tokens, } template -class CTCGreedyDecodeOpCUDAKernel : public framework::OpKernel { +class CTCDecodeOpCUDAKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { PADDLE_ENFORCE(platform::is_gpu_place(ctx.GetPlace()), @@ -87,5 +87,5 @@ class CTCGreedyDecodeOpCUDAKernel : public framework::OpKernel { } // namespace operators } // namespace paddle -REGISTER_OP_CUDA_KERNEL(ctc_greedy_decode, - paddle::operators::CTCGreedyDecodeOpCUDAKernel); +REGISTER_OP_CUDA_KERNEL(ctc_decode, + paddle::operators::CTCDecodeOpCUDAKernel); diff --git a/paddle/operators/ctc_decode_op.h b/paddle/operators/ctc_decode_op.h index 30bb53e157f..bc8dfab9f62 100644 --- a/paddle/operators/ctc_decode_op.h +++ b/paddle/operators/ctc_decode_op.h @@ -23,7 +23,7 @@ using Tensor = framework::Tensor; using LoDTensor = framework::LoDTensor; template -class CTCGreedyDecodeKernel : public framework::OpKernel { +class CTCDecodeKernel : public framework::OpKernel { public: void Compute(const framework::ExecutionContext& ctx) const override { auto* input = ctx.Input("Input"); diff --git a/python/paddle/v2/fluid/tests/test_ctc_decode.py b/python/paddle/v2/fluid/tests/test_ctc_decode.py index 3b7486cfb98..6e798a8465c 100644 --- a/python/paddle/v2/fluid/tests/test_ctc_decode.py +++ b/python/paddle/v2/fluid/tests/test_ctc_decode.py @@ -22,7 +22,7 @@ def CTCDecode(input, lod, blank, merge_repeated): class TestCTCDecodeOp(OpTest): def config(self): - self.op_type = "ctc_greedy_decode" + self.op_type = "ctc_decode" self.input_lod = [[0, 11, 18]] self.blank = 0 self.merge_repeated = False @@ -49,7 +49,7 @@ class TestCTCDecodeOp(OpTest): class TestCTCDecodeOpCase1(TestCTCDecodeOp): def config(self): - self.op_type = "ctc_greedy_decode" + self.op_type = "ctc_decode" self.input_lod = [[0, 11, 18]] self.blank = 0 self.merge_repeated = True -- GitLab From c0f0f2337e8374fa42097b43c3197be5bbebf699 Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Tue, 16 Jan 2018 16:24:51 +0800 Subject: [PATCH 0298/2305] add WITH_FLUID option and third party INSTALL for fluid api --- CMakeLists.txt | 1 + cmake/external/eigen.cmake | 10 ++++++++-- cmake/external/gflags.cmake | 2 +- cmake/external/glog.cmake | 2 +- cmake/external/protobuf.cmake | 2 +- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00996cb7ed5..b701eb00e8b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ option(WITH_COVERAGE "Compile PaddlePaddle with code coverage" OFF) option(COVERALLS_UPLOAD "Package code coverage data to coveralls" OFF) option(ON_TRAVIS "Exclude special unit test on Travis CI" OFF) option(WITH_C_API "Compile PaddlePaddle with C-API(Prediction)" OFF) +option(WITH_FLUID "Compile PaddlePaddle fluid only" ON) option(WITH_GOLANG "Compile PaddlePaddle with GOLANG" OFF) option(GLIDE_INSTALL "Download and install go dependencies " ON) option(USE_NNPACK "Compile PaddlePaddle with NNPACK library" OFF) diff --git a/cmake/external/eigen.cmake b/cmake/external/eigen.cmake index c4712f19eb8..d49c8d60110 100644 --- a/cmake/external/eigen.cmake +++ b/cmake/external/eigen.cmake @@ -1,8 +1,8 @@ INCLUDE(ExternalProject) SET(EIGEN_SOURCE_DIR ${THIRD_PARTY_PATH}/eigen3) - -INCLUDE_DIRECTORIES(${EIGEN_SOURCE_DIR}/src/extern_eigen3) +SET(EIGEN_INCLUDE_DIR ${EIGEN_SOURCE_DIR}/src/extern_eigen3) +INCLUDE_DIRECTORIES(${EIGEN_INCLUDE_DIR}) ExternalProject_Add( extern_eigen3 @@ -28,3 +28,9 @@ endif() add_dependencies(eigen3 extern_eigen3) LIST(APPEND external_project_dependencies eigen3) + +IF(NOT WITH_C_API AND WITH_FLUID) + INSTALL(FILES ${EIGEN_INCLUDE_DIR}/Eigen/Core DESTINATION third_party/eigen3/Eigen) + INSTALL(DIRECTORY ${EIGEN_INCLUDE_DIR}/Eigen/src DESTINATION third_party/eigen3/Eigen) + INSTALL(DIRECTORY ${EIGEN_INCLUDE_DIR}/unsupported/Eigen DESTINATION third_party/eigen3/unsupported) +ENDIF() diff --git a/cmake/external/gflags.cmake b/cmake/external/gflags.cmake index d4f252bb9f6..60946304541 100644 --- a/cmake/external/gflags.cmake +++ b/cmake/external/gflags.cmake @@ -52,7 +52,7 @@ ADD_DEPENDENCIES(gflags extern_gflags) LIST(APPEND external_project_dependencies gflags) -IF(WITH_C_API) +IF(WITH_C_API OR WITH_FLUID) INSTALL(DIRECTORY ${GFLAGS_INCLUDE_DIR} DESTINATION third_party/gflags) IF(ANDROID) INSTALL(FILES ${GFLAGS_LIBRARIES} DESTINATION third_party/gflags/lib/${ANDROID_ABI}) diff --git a/cmake/external/glog.cmake b/cmake/external/glog.cmake index 0c6b3aafcb4..382fbda3b5c 100644 --- a/cmake/external/glog.cmake +++ b/cmake/external/glog.cmake @@ -68,7 +68,7 @@ LINK_LIBRARIES(glog gflags) LIST(APPEND external_project_dependencies glog) -IF(WITH_C_API) +IF(WITH_C_API OR WITH_FLUID) INSTALL(DIRECTORY ${GLOG_INCLUDE_DIR} DESTINATION third_party/glog) IF(ANDROID) INSTALL(FILES ${GLOG_LIBRARIES} DESTINATION third_party/glog/lib/${ANDROID_ABI}) diff --git a/cmake/external/protobuf.cmake b/cmake/external/protobuf.cmake index ff5855052da..365a370a9cf 100644 --- a/cmake/external/protobuf.cmake +++ b/cmake/external/protobuf.cmake @@ -250,7 +250,7 @@ IF(NOT PROTOBUF_FOUND) SET(PROTOBUF_PROTOC_LIBRARY ${extern_protobuf_PROTOC_LIBRARY} CACHE FILEPATH "protoc library." FORCE) - IF(WITH_C_API) + IF(WITH_C_API OR WITH_FLUID) INSTALL(DIRECTORY ${PROTOBUF_INCLUDE_DIR} DESTINATION third_party/protobuf) IF(ANDROID) INSTALL(FILES ${PROTOBUF_LITE_LIBRARY} DESTINATION third_party/protobuf/lib/${ANDROID_ABI}) -- GitLab From 2be7cf909a6680cc53151b2fe422dddbef13f8da Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Tue, 16 Jan 2018 18:30:25 +0800 Subject: [PATCH 0299/2305] add paddle INSTALL for fluid api --- paddle/framework/CMakeLists.txt | 6 ++++++ paddle/inference/CMakeLists.txt | 6 ++++++ paddle/memory/CMakeLists.txt | 7 +++++++ paddle/platform/CMakeLists.txt | 8 ++++++++ paddle/string/CMakeLists.txt | 7 ++++++- 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt index 597ea959f23..fcfac5a3e68 100644 --- a/paddle/framework/CMakeLists.txt +++ b/paddle/framework/CMakeLists.txt @@ -84,3 +84,9 @@ cc_test(op_kernel_type_test SRCS op_kernel_type_test.cc DEPS place device_contex cc_test(cow_ptr_tests SRCS details/cow_ptr_test.cc) nv_test(data_device_transform_test SRCS data_device_transform_test.cu DEPS operator op_registry init math_function) + +if(NOT WITH_C_API AND WITH_FLUID) + file(GLOB FRAMEWORK_HEADERS *.h) + install(FILES ${FRAMEWORK_HEADERS} DESTINATION include/paddle/framework) + install(FILES details/cow_ptr.h details/op_registry.h DESTINATION include/paddle/framework/details) +endif() diff --git a/paddle/inference/CMakeLists.txt b/paddle/inference/CMakeLists.txt index b017283ec3b..af882f252b7 100644 --- a/paddle/inference/CMakeLists.txt +++ b/paddle/inference/CMakeLists.txt @@ -16,6 +16,12 @@ target_circle_link_libraries(paddle_fluid_shared ARCHIVE_END ${FLUID_CORE_MODULES}) +# install library & headers +if(NOT WITH_C_API AND WITH_FLUID) + install(FILES inference.h DESTINATION include/paddle/inference) + install(TARGETS paddle_fluid_shared DESTINATION lib) +endif() + # ptools # just for testing, we may need to change the storing format for inference_model # and move the dependent of pickle. diff --git a/paddle/memory/CMakeLists.txt b/paddle/memory/CMakeLists.txt index 8841c14ee08..061ee1a4d4c 100644 --- a/paddle/memory/CMakeLists.txt +++ b/paddle/memory/CMakeLists.txt @@ -14,3 +14,10 @@ cc_library(paddle_memory system_allocator) cc_test(memory_test SRCS memory_test.cc DEPS place paddle_memory) + +if(NOT WITH_C_API AND WITH_FLUID) + file(GLOB MEMORY_HEADERS *.h) + file(GLOB MEMORY_DETAIL_HEADERS detail/*.h) + install(FILES ${MEMORY_HEADERS} DESTINATION include/paddle/memory) + install(FILES ${MEMORY_DETAIL_HEADERS} DESTINATION include/paddle/memory/detail) +endif() diff --git a/paddle/platform/CMakeLists.txt b/paddle/platform/CMakeLists.txt index 44f6d85cd15..3742594a504 100644 --- a/paddle/platform/CMakeLists.txt +++ b/paddle/platform/CMakeLists.txt @@ -39,3 +39,11 @@ nv_test(nccl_test SRCS nccl_test.cu DEPS dynload_cuda gpu_info device_context) cc_library(profiler SRCS profiler.cc DEPS device_context) cc_test(profiler_test SRCS profiler_test.cc DEPS profiler) + +if(NOT WITH_C_API AND WITH_FLUID) + file(GLOB PLATFORM_HEADERS *.h) + file(GLOB PLATFORM_dynload_HEADERS dynload/*.h) + install(FILES ${PLATFORM_HEADERS} DESTINATION include/paddle/platform) + install(FILES ${PLATFORM_HEADERS} DESTINATION include/paddle/platform/dynload) + install(FILES details/device_ptr_cast.h DESTINATION include/paddle/platform/details) +endif() diff --git a/paddle/string/CMakeLists.txt b/paddle/string/CMakeLists.txt index 60667b72873..0fa846c4eda 100644 --- a/paddle/string/CMakeLists.txt +++ b/paddle/string/CMakeLists.txt @@ -1,5 +1,10 @@ cc_library(stringpiece SRCS piece.cc) cc_test(stringpiece_test SRCS piece_test.cc DEPS stringpiece glog gflags) - cc_test(stringprintf_test SRCS printf_test.cc DEPS glog gflags) cc_test(to_string_test SRCS to_string_test.cc) + +if(NOT WITH_C_API AND WITH_FLUID) + file(GLOB STRING_HEADERS *.h) + install(FILES ${STRING_HEADERS} DESTINATION include/paddle/memory) + install(FILES tinyformat/tinyformat.h DESTINATION include/paddle/memory/tinyformat) +endif() -- GitLab From 363538803a5138d3a554e34f30faf9c99156a0ef Mon Sep 17 00:00:00 2001 From: Luo Tao Date: Tue, 16 Jan 2018 19:07:37 +0800 Subject: [PATCH 0300/2305] set WITH_FLUID=OFF when WITH_C_API=ON --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index b701eb00e8b..ad1b6f23c9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,6 +108,10 @@ if (WITH_C_API AND WITH_PYTHON) "different Python interpreter from compiling.") endif() +if (WITH_C_API) + set(WITH_FLUID OFF CACHE STRING "Disable install fluid when compile the C_API" FORCE) +endif() + if(MOBILE_INFERENCE) set(THIRD_PARTY_BUILD_TYPE MinSizeRel) else() -- GitLab From 6497bff901e20615411b0095efb791dfab36acbf Mon Sep 17 00:00:00 2001 From: caoying03 Date: Tue, 16 Jan 2018 19:47:15 +0800 Subject: [PATCH 0301/2305] add python wrapper for l2 normalize. --- doc/api/v2/fluid/layers.rst | 5 + paddle/operators/clip_op.cc | 4 +- paddle/operators/elementwise_op.h | 32 ++-- paddle/operators/expand_op.cc | 18 +-- .../trainer_config_helpers/evaluators.py | 23 ++- python/paddle/v2/fluid/framework.py | 25 ++-- python/paddle/v2/fluid/layers/io.py | 4 +- python/paddle/v2/fluid/layers/nn.py | 140 +++++++++++++++--- .../fluid/tests/test_normalization_wrapper.py | 95 ++++++++++++ 9 files changed, 278 insertions(+), 68 deletions(-) create mode 100644 python/paddle/v2/fluid/tests/test_normalization_wrapper.py diff --git a/doc/api/v2/fluid/layers.rst b/doc/api/v2/fluid/layers.rst index 62c154e65dc..290d4098798 100644 --- a/doc/api/v2/fluid/layers.rst +++ b/doc/api/v2/fluid/layers.rst @@ -493,3 +493,8 @@ swish ------ .. autofunction:: paddle.v2.fluid.layers.swish :noindex: + +l2_normalize +------------ +.. autofunction:: paddle.v2.fluid.layers.l2_normalize + :noindex: diff --git a/paddle/operators/clip_op.cc b/paddle/operators/clip_op.cc index 573bb9c7dfd..7adb74eab78 100644 --- a/paddle/operators/clip_op.cc +++ b/paddle/operators/clip_op.cc @@ -51,8 +51,8 @@ class ClipOpMaker : public framework::OpProtoAndCheckerMaker { AddComment(R"DOC( Clip Operator. -The clip operator limits the value of given input within an interval. The interval is -specified with arguments 'min' and 'max': +The clip operator limits the value of given input within an interval. The +interval is specified with arguments 'min' and 'max': $$ Out = \min(\max(X, min), max) diff --git a/paddle/operators/elementwise_op.h b/paddle/operators/elementwise_op.h index a342595b546..1a0131d8b94 100644 --- a/paddle/operators/elementwise_op.h +++ b/paddle/operators/elementwise_op.h @@ -26,9 +26,9 @@ class ElementwiseOp : public framework::OperatorWithKernel { using Tensor = framework::Tensor; void InferShape(framework::InferShapeContext* ctx) const override { PADDLE_ENFORCE(ctx->HasInput("X"), - "Input(X) of elementwise op should not be null"); + "Input(X) of elementwise op should not be null."); PADDLE_ENFORCE(ctx->HasInput("Y"), - "Input(Y) of elementwise op should not be null"); + "Input(Y) of elementwise op should not be null."); PADDLE_ENFORCE(ctx->HasOutput("Out"), "Output(Out) of elementwise op should not be null."); @@ -45,12 +45,12 @@ class ElementwiseOpMaker : public framework::OpProtoAndCheckerMaker { public: ElementwiseOpMaker(OpProto* proto, OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { - AddInput("X", "(Tensor) The first input tensor of elementwise op"); - AddInput("Y", "(Tensor) The second input tensor of elementwise op"); - AddOutput("Out", "The output of elementwise op"); + AddInput("X", "(Tensor), The first input tensor of elementwise op."); + AddInput("Y", "(Tensor), The second input tensor of elementwise op."); + AddOutput("Out", "The output of elementwise op."); AddAttr("axis", - "(int, default -1) The starting dimension index " - "for broadcasting Y onto X") + "(int, default -1). The start dimension index " + "for broadcasting Y onto X.") .SetDefault(-1) .EqualGreaterThan(-1); comment_ = R"DOC( @@ -58,19 +58,18 @@ Limited Elementwise {name} Operator. The equation is: -.. math:: - {equation} +$${equation}$$ -X is a tensor of any dimension and the dimensions of tensor Y must be smaller than -or equal to the dimensions of X. +$X$ is a tensor of any dimension and the dimensions of tensor $Y$ must be +smaller than or equal to the dimensions of $X$. There are two cases for this operator: -1. The shape of Y is same with X; -2. The shape of Y is a subset of X. +1. The shape of $Y$ is same with $X$; +2. The shape of $Y$ is a subset of $X$. For case 2: -Y will be broadcasted to match the shape of X and axis should be -the starting dimension index for broadcasting Y onto X. +$Y$ will be broadcasted to match the shape of $X$ and axis should be +set to index of the start dimension to broadcast $Y$ onto $X$. For example .. code-block:: python @@ -81,7 +80,8 @@ For example shape(X) = (2, 3, 4, 5), shape(Y) = (3, 4), with axis=1 shape(X) = (2, 3, 4, 5), shape(Y) = (2), with axis=0 -Either of the inputs X and Y or none can carry the LoD (Level of Details) information. However, the output only shares the LoD information with input X. +Either of the inputs $X$ and $Y$ or none can carry the LoD (Level of Details) +information. However, the output only shares the LoD information with input $X$. )DOC"; AddComment(comment_); diff --git a/paddle/operators/expand_op.cc b/paddle/operators/expand_op.cc index 08fa91ed72a..043c93654d3 100644 --- a/paddle/operators/expand_op.cc +++ b/paddle/operators/expand_op.cc @@ -58,21 +58,21 @@ class ExpandOpMaker : public framework::OpProtoAndCheckerMaker { ExpandOpMaker(OpProto* proto, OpAttrChecker* op_checker) : OpProtoAndCheckerMaker(proto, op_checker) { AddInput("X", - "(Tensor, default Tensor) A tensor with rank in [1, 6]." - "X is the input tensor to be expanded."); + "(Tensor, default Tensor). A tensor with rank in [1, 6]." + "X is the input to be expanded."); AddOutput("Out", - "(Tensor, default Tensor) A tensor with rank in [1, 6]." - "The rank of Output(Out) is same as Input(X) except that each " - "dimension size of Output(Out) is equal to corresponding " - "dimension size of Input(X) multiplying corresponding value of " - "Attr(expand_times)."); + "(Tensor, default Tensor). A tensor with rank in [1, 6]." + "The rank of Output(Out) have the same with Input(X). " + "After expanding, size of each dimension of Output(Out) is equal " + "to size of the corresponding dimension of Input(X) multiplying " + "the corresponding value given by Attr(expand_times)."); AddAttr>("expand_times", "Expand times number for each dimension."); AddComment(R"DOC( Expand operator tiles the input by given times number. You should set times number for each dimension by providing attribute 'expand_times'. The rank of X -should be in [1, 6]. Please notice that size of 'expand_times' must be same with -X's rank. Following is a using case: +should be in [1, 6]. Please note that size of 'expand_times' must be the same +with X's rank. Following is a using case: Input(X) is a 3-D tensor with shape [2, 3, 1]: diff --git a/python/paddle/trainer_config_helpers/evaluators.py b/python/paddle/trainer_config_helpers/evaluators.py index 95797fba8f6..0eeaf7eabb1 100644 --- a/python/paddle/trainer_config_helpers/evaluators.py +++ b/python/paddle/trainer_config_helpers/evaluators.py @@ -16,13 +16,22 @@ from paddle.trainer.config_parser import * from default_decorators import * __all__ = [ - "evaluator_base", "classification_error_evaluator", "auc_evaluator", - "pnpair_evaluator", "precision_recall_evaluator", "ctc_error_evaluator", - "chunk_evaluator", "sum_evaluator", "column_sum_evaluator", - "value_printer_evaluator", "gradient_printer_evaluator", - "maxid_printer_evaluator", "maxframe_printer_evaluator", - "seqtext_printer_evaluator", "classification_error_printer_evaluator", - "detection_map_evaluator" + "evaluator_base", + "classification_error_evaluator", + "auc_evaluator", + "pnpair_evaluator", + "precision_recall_evaluator", + "ctc_error_evaluator", + "chunk_evaluator", + "sum_evaluator", + "column_sum_evaluator", + "value_printer_evaluator", + "gradient_printer_evaluator", + "maxid_printer_evaluator", + "maxframe_printer_evaluator", + "seqtext_printer_evaluator", + "classification_error_printer_evaluator", + "detection_map_evaluator", ] diff --git a/python/paddle/v2/fluid/framework.py b/python/paddle/v2/fluid/framework.py index 8042febfed7..4f8366b6403 100644 --- a/python/paddle/v2/fluid/framework.py +++ b/python/paddle/v2/fluid/framework.py @@ -116,8 +116,8 @@ def _debug_string_(proto, throw_on_error=True): """ error_fields = list() if not proto.IsInitialized(error_fields) and throw_on_error: - raise ValueError("{0} are not initialized\nThe message is {1}".format( - error_fields, proto)) + raise ValueError("{0} are not initialized.\nThe message is {1}:\n". + format(error_fields, proto)) return proto.__str__() @@ -374,12 +374,13 @@ class Operator(object): >>> outputs={"Out": [var1]}) Args: - block(Block): The block has the current operator - desc(core.OpDesc): The protobuf description + block(Block): The block has the current operator. + desc(core.OpDesc): The protobuf description. type(str): The type of operator. inputs(dict): The input dictionary. Key is the input parameter name. Value is a list of variables. - outputs(dict): The output dictionary. Has same format with inputs + outputs(dict): The output dictionary which has the same format with + inputs. attrs(dict): The attributes dictionary. Key is attribute name. Value is the attribute value. The attribute type should be as same as the type registered in C++ @@ -436,10 +437,11 @@ class Operator(object): for m in proto.outputs: need.add(m.name) if not given == need: - raise ValueError( - "Incorrect setting for output(s) of operator \"%s\". Need: [%s] Given: [%s]" - % (type, ", ".join(str(e) for e in need), ", ".join( - str(e) for e in given))) + raise ValueError(("Incorrect setting for output(s) of " + "operator \"%s\". Need: [%s] Given: [%s]") % + (type, ", ".join(str(e) + for e in need), ", ".join( + str(e) for e in given))) for out_proto in proto.outputs: out_args = outputs[out_proto.name] @@ -818,9 +820,8 @@ class Program(object): if isinstance(t, Variable): t = t.op else: - raise ValueError( - "All targets of prune() can only be Variable or Operator." - ) + raise ValueError(("All targets of prune() can only be " + "Variable or Operator.")) targets_idx.append([t.block.idx, t.idx]) res = Program() diff --git a/python/paddle/v2/fluid/layers/io.py b/python/paddle/v2/fluid/layers/io.py index 6177f0b4d71..a43e0ee4def 100644 --- a/python/paddle/v2/fluid/layers/io.py +++ b/python/paddle/v2/fluid/layers/io.py @@ -28,9 +28,9 @@ def data(name, **Data Layer** This function takes in the input and based on whether data has - to be returned back as a minibatch, it creates the global variable using + to be returned back as a minibatch, it creates the global variable by using the helper functions. The global variables can be accessed by all the - following operations and layers in the graph. + following operators in the graph. All the input variables of this function are passed in as local variables to the LayerHelper constructor. diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 4e8fd407c99..cfa60d2924a 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -50,6 +50,7 @@ __all__ = [ 'sequence_last_step', 'dropout', 'split', + 'l2_normalize', ] @@ -945,7 +946,8 @@ def pool2d(input, pool_type, pool_stride=None, pool_padding=None, - global_pooling=False): + global_pooling=False, + name=None): """ This function adds the operator for pooling in 2 dimensions, using the pooling configurations mentioned in input parameters. @@ -991,7 +993,8 @@ def batch_norm(input, epsilon=1e-05, param_attr=None, bias_attr=None, - data_layout='NCHW'): + data_layout='NCHW', + name=None): """ This function helps create an operator to implement the BatchNorm layer using the configurations from the input parameters. @@ -1067,7 +1070,7 @@ def batch_norm(input, return helper.append_activation(batch_norm_out) -def beam_search_decode(ids, scores): +def beam_search_decode(ids, scores, name=None): helper = LayerHelper('beam_search_decode', **locals()) sentence_ids = helper.create_tmp_variable(dtype=ids.dtype) sentence_scores = helper.create_tmp_variable(dtype=ids.dtype) @@ -1091,7 +1094,8 @@ def conv2d_transpose(input, padding=None, stride=None, dilation=None, - param_attr=None): + param_attr=None, + name=None): """ The transpose of conv2d layer. @@ -1118,8 +1122,8 @@ def conv2d_transpose(input, contain two integers, (dilation_H, dilation_W). Otherwise, the dilation_H = dilation_W = dilation. param_attr: Parameter Attribute. - main_program(Program): the main program - startup_program(Program): the startup program + name(str|None): A name for this layer(optional). If set None, the layer + will be named automatically. Returns: Variable: Output image. @@ -1183,7 +1187,7 @@ def conv2d_transpose(input, return out -def sequence_expand(x, y): +def sequence_expand(x, y, name=None): """Sequence Expand Layer. This layer will expand the input variable **x** according to LoD information of **y**. And the following examples will explain how sequence_expand works: @@ -1227,6 +1231,8 @@ def sequence_expand(x, y): Args: x (Variable): The input variable which is a Tensor or LoDTensor. y (Variable): The input variable which is a LoDTensor. + name(str|None): A name for this layer(optional). If set None, the layer + will be named automatically. Returns: Variable: The expanded variable which is a LoDTensor. @@ -1253,7 +1259,8 @@ def lstm_unit(x_t, cell_t_prev, forget_bias=0.0, param_attr=None, - bias_attr=None): + bias_attr=None, + name=None): """Lstm unit layer. The equation of a lstm step is: .. math:: @@ -1300,6 +1307,8 @@ def lstm_unit(x_t, initializer, name etc. bias_attr (ParamAttr): The attributes of bias weights, if not False, bias weights will be created and be set to default value. + name(str|None): A name for this layer(optional). If set None, the layer + will be named automatically. Returns: tuple: The hidden value and cell value of lstm unit. @@ -1365,7 +1374,7 @@ def lstm_unit(x_t, return h, c -def reduce_sum(input, dim=None, keep_dim=False): +def reduce_sum(input, dim=None, keep_dim=False, name=None): """ Computes the sum of tensor elements over the given dimension. @@ -1379,6 +1388,8 @@ def reduce_sum(input, dim=None, keep_dim=False): keep_dim (bool): Whether to reserve the reduced dimension in the output Tensor. The result tensor will have one fewer dimension than the :attr:`input` unless :attr:`keep_dim` is true. + name(str|None): A name for this layer(optional). If set None, the layer + will be named automatically. Returns: Variable: The reduced Tensor variable. @@ -1409,7 +1420,7 @@ def reduce_sum(input, dim=None, keep_dim=False): return out -def reduce_mean(input, dim=None, keep_dim=False): +def reduce_mean(input, dim=None, keep_dim=False, name=None): """ Computes the mean of tensor elements over the given dimension. @@ -1423,6 +1434,8 @@ def reduce_mean(input, dim=None, keep_dim=False): keep_dim (bool): Whether to reserve the reduced dimension in the output Tensor. The result tensor will have one fewer dimension than the :attr:`input` unless :attr:`keep_dim` is true. + name(str|None): A name for this layer(optional). If set None, the layer + will be named automatically. Returns: Variable: The reduced Tensor variable. @@ -1453,7 +1466,7 @@ def reduce_mean(input, dim=None, keep_dim=False): return out -def reduce_max(input, dim=None, keep_dim=False): +def reduce_max(input, dim=None, keep_dim=False, name=None): """ Computes the maximum of tensor elements over the given dimension. @@ -1467,6 +1480,8 @@ def reduce_max(input, dim=None, keep_dim=False): keep_dim (bool): Whether to reserve the reduced dimension in the output Tensor. The result tensor will have one fewer dimension than the :attr:`input` unless :attr:`keep_dim` is true. + name(str|None): A name for this layer(optional). If set None, the layer + will be named automatically. Returns: Variable: The reduced Tensor variable. @@ -1497,7 +1512,7 @@ def reduce_max(input, dim=None, keep_dim=False): return out -def reduce_min(input, dim=None, keep_dim=False): +def reduce_min(input, dim=None, keep_dim=False, name=None): """ Computes the minimum of tensor elements over the given dimension. @@ -1511,6 +1526,8 @@ def reduce_min(input, dim=None, keep_dim=False): keep_dim (bool): Whether to reserve the reduced dimension in the output Tensor. The result tensor will have one fewer dimension than the :attr:`input` unless :attr:`keep_dim` is true. + name(str|None): A name for this layer(optional). If set None, the layer + will be named automatically. Returns: Variable: The reduced Tensor variable. @@ -1541,20 +1558,22 @@ def reduce_min(input, dim=None, keep_dim=False): return out -def split(input, num_or_sections, dim=-1): +def split(input, num_or_sections, dim=-1, name=None): """ - Splits the tensor into multiple sub-tensors. + Split the input tensor into multiple sub-tensors. Args: input (Variable): The input variable which is a Tensor or LoDTensor. - num_or_sections (int|list): If :attr:`num_or_sections` is an integer, - then the integer indicates the number of equal sized sub-tensors - that the tensor will be divided into. If :attr:`num_or_sections` - is a list of integers, the length of list indicates the number of - sub-tensors and the integers indicate the sizes of sub-tensors' + num_or_sections (int|list): If :attr:`num_or_sections` is an integer, + then the integer indicates the number of equal sized sub-tensors + that the tensor will be divided into. If :attr:`num_or_sections` + is a list of integers, the length of list indicates the number of + sub-tensors and the integers indicate the sizes of sub-tensors' :attr:`dim` dimension orderly. - dim (int): The dimension along which to split. If :math:`dim < 0`, the + dim (int): The dimension along which to split. If :math:`dim < 0`, the dimension to split along is :math:`rank(input) + dim`. + name(str|None): A name for this layer(optional). If set None, the layer + will be named automatically. Returns: List: The list of segmented tensor variables. @@ -1597,3 +1616,84 @@ def split(input, num_or_sections, dim=-1): 'axis': dim }) return outs + + +def l2_normalize(x, axis, epsilon=1e-12, name=None): + """ + **L2 normalize Layer** + + The l2 normalize layer normalizes `x` along dimension `axis` using an L2 + norm. For a 1-D tensor (`dim` is fixed to 0), this layer computes + + output = x / sqrt(max(sum(x**2), epsilon)) + + For `x` with more dimensions, this layer independently normalizes each 1-D + slice along dimension `axis`. + + Args: + x(Variable|list): The input tensor to l2_normalize layer. + axis(int): Dimension along which to normalize the input. + epsilon(float): A lower bound value for `x`'s l2 norm. sqrt(epsilon) will + be used as the divisor if the l2 norm of `x` is less than + sqrt(epsilon). + name(str|None): A name for this layer(optional). If set None, the layer + will be named automatically. + + + Returns: + Variable: The output tensor variable. + + Examples: + .. code-block:: python + + data = fluid.layers.data(name="data", + shape=(3, 17, 13), + dtype="float32") + fc = fluid.layers.l2_normalize(x=data, axis=1) + """ + + if len(x.shape) == 1: axis = 0 + + helper = LayerHelper("l2_normalize", **locals()) + + square = helper.create_tmp_variable(dtype=x.dtype) + helper.append_op(type="square", inputs={"X": x}, outputs={"Out": square}) + + reduced_sum = helper.create_tmp_variable(dtype=x.dtype) + helper.append_op( + type="reduce_sum", + inputs={"X": square}, + outputs={"Out": reduced_sum}, + attrs={ + "dim": 1 if axis is None else axis, + "keep_dim": True, + "reduce_all": False + }) + + # TODO(caoying) A lower bound value epsilon for the norm is needed to + # imporve the numeric stability of reciprocal. This requires a maximum_op. + rsquare = helper.create_tmp_variable(dtype=x.dtype) + helper.append_op( + type="reciprocal", inputs={"X": reduced_sum}, outputs={"Out": rsquare}) + + # TODO(caoying) the current elementwise_mul operator does not support a + # general broadcast rule which broadcasts input(Y) to have the same + # dimension with Input(X) starting from a specified dimension. So this + # exanpsion is requred. Once a general broadcast relu is spported, this + # expanding canbe removed. + rsquare_expanded = helper.create_tmp_variable(dtype=x.dtype) + expand_times = [1] * len(x.shape) + expand_times[axis] = int(x.shape[axis]) + helper.append_op( + type="expand", + inputs={"X": rsquare}, + outputs={"Out": rsquare_expanded}, + attrs={"expand_times": expand_times}) + + out = helper.create_tmp_variable(dtype=x.dtype) + helper.append_op( + type="elementwise_mul", + inputs={"X": x, + "Y": rsquare_expanded}, + outputs={"Out": out}) + return out diff --git a/python/paddle/v2/fluid/tests/test_normalization_wrapper.py b/python/paddle/v2/fluid/tests/test_normalization_wrapper.py new file mode 100644 index 00000000000..caff63011d0 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_normalization_wrapper.py @@ -0,0 +1,95 @@ +# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. +# +#Licensed under the Apache License, Version 2.0 (the "License"); +#you may not use this file except in compliance with the License. +#You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +#Unless required by applicable law or agreed to in writing, software +#distributed under the License is distributed on an "AS IS" BASIS, +#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +#See the License for the specific language governing permissions and +#limitations under the License. +import unittest +import paddle.v2.fluid as fluid +import paddle.v2.fluid.core as core +import numpy as np + + +class TestNormalization(unittest.TestCase): + data_desc = {"name": "input", "shape": (2, 3, 7)} + + def gen_random_input(self): + """Generate random input data. + """ + self.data = np.random.random( + size=self.data_desc["shape"]).astype("float32") + + def set_program(self, axis, epsilon): + """Build the test program. + """ + data = fluid.layers.data( + name=self.data_desc["name"], + shape=self.data_desc["shape"], + dtype="float32", + append_batch_size=False) + data.stop_gradient = False + l2_norm = fluid.layers.l2_normalize(x=data, axis=axis, epsilon=epsilon) + out = fluid.layers.reduce_sum(l2_norm, dim=None) + + fluid.backward.append_backward(loss=out) + self.fetch_list = [l2_norm] + + def run_program(self): + """Run the test program. + """ + places = [core.CPUPlace()] + if core.is_compile_gpu(): + places.append(core.CUDAPlace(0)) + + for place in places: + self.set_inputs(place) + exe = fluid.Executor(place) + + output = exe.run(fluid.default_main_program(), + feed=self.inputs, + fetch_list=self.fetch_list, + return_numpy=True) + self.op_output = output + + def set_inputs(self, place): + """Set the randomly generated data to the test program. + """ + self.inputs = {} + tensor = fluid.Tensor() + tensor.set(self.data, place) + self.inputs[self.data_desc["name"]] = tensor + + def l2_normalize(self, data, axis, epsilon): + """ Compute the groundtruth. + """ + output = data * np.reciprocal( + np.sum(np.square(data), axis=axis, keepdims=True)) + return output + + def test_l2_normalize(self): + """ Test the python wrapper for l2_normalize. + """ + axis = 1 + #TODO(caoying) epsilon is not supported due to lack of a maximum_op. + epsilon = 1e-6 + + self.gen_random_input() + + self.set_program(axis, epsilon) + self.run_program() + + expect_output = self.l2_normalize(self.data, axis, epsilon) + + # check output + self.assertTrue(np.allclose(self.op_output, expect_output, atol=0.001)) + + +if __name__ == '__main__': + unittest.main() -- GitLab From 2ad5a6f0d155a3a7224819b70c86f91e78fde5f1 Mon Sep 17 00:00:00 2001 From: wanghaox Date: Tue, 16 Jan 2018 18:37:13 +0800 Subject: [PATCH 0302/2305] add iou similarity operator --- paddle/operators/iou_similarity_op.cc | 74 ++++++++++++++++ paddle/operators/iou_similarity_op.h | 87 +++++++++++++++++++ .../v2/fluid/tests/test_iou_similarity_op.py | 36 ++++++++ 3 files changed, 197 insertions(+) create mode 100755 paddle/operators/iou_similarity_op.cc create mode 100644 paddle/operators/iou_similarity_op.h create mode 100755 python/paddle/v2/fluid/tests/test_iou_similarity_op.py diff --git a/paddle/operators/iou_similarity_op.cc b/paddle/operators/iou_similarity_op.cc new file mode 100755 index 00000000000..247549a8ff5 --- /dev/null +++ b/paddle/operators/iou_similarity_op.cc @@ -0,0 +1,74 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#include "paddle/operators/iou_similarity_op.h" + +namespace paddle { +namespace operators { + +class IOUSimilarityOp : public framework::OperatorWithKernel { + public: + using framework::OperatorWithKernel::OperatorWithKernel; + + protected: + void InferShape(framework::InferShapeContext *ctx) const override { + auto x_dims = ctx->GetInputDim("X"); + auto y_dims = ctx->GetInputDim("Y"); + + PADDLE_ENFORCE_EQ(x_dims.size(), 2UL, "The shape of X is [N, 4]"); + PADDLE_ENFORCE_EQ(x_dims[1], 4UL, "The shape of X is [N, 4]"); + PADDLE_ENFORCE_EQ(y_dims.size(), 2UL, "The shape of Y is [M, 4]"); + PADDLE_ENFORCE_EQ(y_dims[1], 4UL, "The shape of Y is [M, 4]"); + + ctx->SetOutputDim("Out", framework::make_ddim({x_dims[0], y_dims[0]})); + } +}; + +class IOUSimilarityOpMaker : public framework::OpProtoAndCheckerMaker { + public: + IOUSimilarityOpMaker(OpProto *proto, OpAttrChecker *op_checker) + : OpProtoAndCheckerMaker(proto, op_checker) { + AddInput( + "X", + "(Tensor, default Tensor) " + "BoxList X holding N boxes, each box is " + "represented as [xmin, ymin, xmax, ymax], the shape of X is [N, 4]."); + AddInput( + "Y", + "(Tensor, default Tensor) " + "BoxList Y holding M boxes, each box is " + "represented as [xmin, ymin, xmax, ymax], the shape of X is [N, 4]."); + + AddOutput( + "Out", + "(Tensor) The output of iou_similarity op, a tensor with shape [N, M] " + "representing pairwise iou scores."); + + AddComment(R"DOC( +IOU Similarity Operator. +Computes pairwise intersection-over-union between box collections. +)DOC"); + } +}; +} // namespace operators +} // namespace paddle + +namespace ops = paddle::operators; +REGISTER_OP_WITHOUT_GRADIENT(iou_similarity, ops::IOUSimilarityOp, + ops::IOUSimilarityOpMaker); + +REGISTER_OP_CPU_KERNEL( + iou_similarity, + ops::IOUSimilarityKernel, + ops::IOUSimilarityKernel); diff --git a/paddle/operators/iou_similarity_op.h b/paddle/operators/iou_similarity_op.h new file mode 100644 index 00000000000..b3b6219415f --- /dev/null +++ b/paddle/operators/iou_similarity_op.h @@ -0,0 +1,87 @@ +/* Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserve. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. */ + +#pragma once +#include "paddle/framework/op_registry.h" +#include "paddle/platform/for_range.h" + +template +inline T IOUSimilarity(T xmin1, T ymin1, T xmax1, T ymax1, T xmin2, T ymin2, + T xmax2, T ymax2) { + T area1 = (ymax1 - ymin1) * (xmax1 - xmin1); + T area2 = (ymax2 - ymin2) * (xmax2 - xmin2); + T inter_xmax = std::min(xmax1, xmax2); + T inter_ymax = std::min(ymax1, ymax2); + T inter_xmin = std::max(xmin1, xmin2); + T inter_ymin = std::max(ymin1, ymin2); + T inter_height = std::max(inter_ymax - inter_ymin, static_cast(0)); + T inter_width = std::max(inter_xmax - inter_xmin, static_cast(0)); + T inter_area = inter_width * inter_height; + T union_area = area1 + area2 - inter_area; + T sim_score = inter_area / union_area; + return sim_score; +} + +template +struct IOUSimilarityFunctor { + IOUSimilarityFunctor(const T* x, const T* y, T* z, int cols) + : x_(x), y_(y), z_(z), cols_(static_cast(cols)) {} + + inline HOSTDEVICE void operator()(size_t row_id) const { + T x_min1 = x_[row_id * 4]; + T y_min1 = x_[row_id * 4 + 1]; + T x_max1 = x_[row_id * 4 + 2]; + T y_max1 = x_[row_id * 4 + 3]; + for (int i = 0; i < cols_; ++i) { + T x_min2 = y_[i * 4]; + T y_min2 = y_[i * 4 + 1]; + T x_max2 = y_[i * 4 + 2]; + T y_max2 = y_[i * 4 + 3]; + + T sim = IOUSimilarity(x_min1, y_min1, x_max1, y_max1, x_min2, y_min2, + x_max2, y_max2); + + z_[row_id * cols_ + i] = sim; + } + } + const T* x_; + const T* y_; + T* z_; + const size_t cols_; +}; + +namespace paddle { +namespace operators { + +template +class IOUSimilarityKernel : public framework::OpKernel { + public: + void Compute(const framework::ExecutionContext& ctx) const override { + const framework::Tensor* in_x = ctx.Input("X"); + const framework::Tensor* in_y = ctx.Input("Y"); + framework::Tensor* out = ctx.Output("Out"); + + int x_n = in_x->dims()[0]; + int y_n = in_y->dims()[0]; + IOUSimilarityFunctor functor(in_x->data(), in_y->data(), + out->mutable_data(ctx.GetPlace()), y_n); + + platform::ForRange for_range( + static_cast(ctx.device_context()), x_n); + for_range(functor); + } +}; // namespace operators + +} // namespace operators +} // namespace paddle diff --git a/python/paddle/v2/fluid/tests/test_iou_similarity_op.py b/python/paddle/v2/fluid/tests/test_iou_similarity_op.py new file mode 100755 index 00000000000..a4eb5000206 --- /dev/null +++ b/python/paddle/v2/fluid/tests/test_iou_similarity_op.py @@ -0,0 +1,36 @@ +import unittest +import numpy as np +import sys +import math +from op_test import OpTest + + +class TestIOUSimilarityOp(OpTest): + def set_data(self): + self.init_test_data() + self.inputs = {'X': self.boxes1, 'Y': self.boxes2} + + self.outputs = {'Out': self.output} + + def test_check_output(self): + self.check_output() + + def test_check_grad(self): + return + + def setUp(self): + self.op_type = "iou_similarity" + self.set_data() + + def init_test_data(self): + self.boxes1 = np.array( + [[4.0, 3.0, 7.0, 5.0], [5.0, 6.0, 10.0, 7.0]]).astype('float32') + self.boxes2 = np.array([[3.0, 4.0, 6.0, 8.0], [14.0, 14.0, 15.0, 15.0], + [0.0, 0.0, 20.0, 20.0]]).astype('float32') + self.output = np.array( + [[2.0 / 16.0, 0, 6.0 / 400.0], + [1.0 / 16.0, 0.0, 5.0 / 400.0]]).astype('float32') + + +if __name__ == '__main__': + unittest.main() -- GitLab From 8266fcc3be869cfef42ab0ec597b2b4ce08dd37d Mon Sep 17 00:00:00 2001 From: yangyaming Date: Tue, 16 Jan 2018 21:31:08 +0800 Subject: [PATCH 0303/2305] Add pyton wrapper for row conv operator. --- doc/api/v2/fluid/layers.rst | 5 +++ python/paddle/v2/fluid/layers/nn.py | 65 ++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 6 deletions(-) diff --git a/doc/api/v2/fluid/layers.rst b/doc/api/v2/fluid/layers.rst index 62c154e65dc..ad3c70a6f1e 100644 --- a/doc/api/v2/fluid/layers.rst +++ b/doc/api/v2/fluid/layers.rst @@ -493,3 +493,8 @@ swish ------ .. autofunction:: paddle.v2.fluid.layers.swish :noindex: + +row_conv +-------- +.. autofunction:: paddle.v2.fluid.layers.row_conv + :noindex: diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 4e8fd407c99..7c694ed7770 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -50,6 +50,7 @@ __all__ = [ 'sequence_last_step', 'dropout', 'split', + 'row_conv', ] @@ -1547,13 +1548,13 @@ def split(input, num_or_sections, dim=-1): Args: input (Variable): The input variable which is a Tensor or LoDTensor. - num_or_sections (int|list): If :attr:`num_or_sections` is an integer, - then the integer indicates the number of equal sized sub-tensors - that the tensor will be divided into. If :attr:`num_or_sections` - is a list of integers, the length of list indicates the number of - sub-tensors and the integers indicate the sizes of sub-tensors' + num_or_sections (int|list): If :attr:`num_or_sections` is an integer, + then the integer indicates the number of equal sized sub-tensors + that the tensor will be divided into. If :attr:`num_or_sections` + is a list of integers, the length of list indicates the number of + sub-tensors and the integers indicate the sizes of sub-tensors' :attr:`dim` dimension orderly. - dim (int): The dimension along which to split. If :math:`dim < 0`, the + dim (int): The dimension along which to split. If :math:`dim < 0`, the dimension to split along is :math:`rank(input) + dim`. Returns: @@ -1597,3 +1598,55 @@ def split(input, num_or_sections, dim=-1): 'axis': dim }) return outs + + +def row_conv(input, future_context_size, param_attr=None, act=None): + """Row Conv Operator. This layer will apply lookahead convolution to + **input**. The input variable should be a 2D LoDTensor with shape [T, D]. + Parameters with shape [future_context_size + 1, D] will be created. The math + equation of row convolution is as following: + + .. math:: + Out_{i} = \sum_{j = i} ^ {i + \\tau} X_{j} \odot W_{i - j} + + In the above equation: + + * :math:`Out_{i}`: The i-th row of output variable with shape [1, D]. + * :math:`\\tau`: Future context size. + * :math:`X_{j}`: The j-th row of input variable with shape [1, D]. + * :math:`W_{i-j}`: The (i-j)-th row of parameters with shape [1, D]. + + More details about row_conv please refer to the paper \ + (http://www.cs.cmu.edu/~dyogatam/papers/wang+etal.iclrworkshop2016.pdf) and + the design document \ + (https://github.com/PaddlePaddle/Paddle/issues/2228#issuecomment-303903645). + + Args: + input (Variable): Input variable, a 2D LoDTensor with shape [T, D]. + future_context_size (int): Future context size. + param_attr (ParamAttr): Attributes of parameters, including + name, initializer etc. + act (str): Non-linear activation to be applied to output variable. + + Returns: + Variable: The output tensor with same shape as input tensor. + + Examples: + .. code-block:: python + + x = fluid.layers.data(name='x', shape=[16], + dtype='float32', lod_level=1) + out = fluid.layers.row_conv(input=x, future_context_size=2) + """ + helper = LayerHelper('row_conv', **locals()) + dtype = helper.input_dtype() + filter_shape = [future_context_size + 1, input.shape[1]] + filter_param = helper.create_parameter( + attr=helper.param_attr, shape=filter_shape, dtype=dtype) + out = helper.create_tmp_variable(dtype) + helper.append_op( + type='row_conv', + inputs={'X': [input], + 'Filter': [filter_param]}, + outputs={'Out': [out]}) + return out -- GitLab From 2a0a576130b3b04f3555479f1850fd91dbba4d9a Mon Sep 17 00:00:00 2001 From: yangyaming Date: Tue, 16 Jan 2018 21:40:34 +0800 Subject: [PATCH 0304/2305] Add non-linear activation. --- python/paddle/v2/fluid/layers/nn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index 7c694ed7770..4546616d1af 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -1649,4 +1649,4 @@ def row_conv(input, future_context_size, param_attr=None, act=None): inputs={'X': [input], 'Filter': [filter_param]}, outputs={'Out': [out]}) - return out + return helper.append_activation(out) -- GitLab From d2a70243f1179654fd7224a4114cff5d984d424e Mon Sep 17 00:00:00 2001 From: dangqingqing Date: Tue, 16 Jan 2018 13:33:13 +0800 Subject: [PATCH 0305/2305] Refine profiler and expose to Python. --- cmake/external/pybind11.cmake | 2 +- paddle/framework/CMakeLists.txt | 3 +- paddle/framework/executor.cc | 6 ++ paddle/platform/profiler.cc | 37 +++++++--- paddle/platform/profiler.h | 22 ++++-- paddle/platform/profiler_test.cc | 10 ++- paddle/pybind/CMakeLists.txt | 2 +- paddle/pybind/protobuf.cc | 70 +++---------------- paddle/pybind/protobuf.h | 1 + paddle/pybind/pybind.cc | 27 ++++++- python/paddle/v2/fluid/profiler.py | 45 ++++++++++++ python/paddle/v2/fluid/tests/test_profiler.py | 37 +++++++++- 12 files changed, 171 insertions(+), 91 deletions(-) diff --git a/cmake/external/pybind11.cmake b/cmake/external/pybind11.cmake index 4e87dc49d89..ab23663695c 100644 --- a/cmake/external/pybind11.cmake +++ b/cmake/external/pybind11.cmake @@ -26,7 +26,7 @@ ExternalProject_Add( extern_pybind ${EXTERNAL_PROJECT_LOG_ARGS} GIT_REPOSITORY "https://github.com/pybind/pybind11.git" - GIT_TAG "v2.1.1" + GIT_TAG "v2.2.1" PREFIX ${PYBIND_SOURCE_DIR} UPDATE_COMMAND "" CONFIGURE_COMMAND "" diff --git a/paddle/framework/CMakeLists.txt b/paddle/framework/CMakeLists.txt index 597ea959f23..9bf712250d0 100644 --- a/paddle/framework/CMakeLists.txt +++ b/paddle/framework/CMakeLists.txt @@ -68,7 +68,8 @@ cc_library(backward SRCS backward.cc DEPS net_op) cc_test(backward_test SRCS backward_test.cc DEPS backward recurrent_op device_context fill_constant_op) cc_library(lod_rank_table SRCS lod_rank_table.cc DEPS lod_tensor) -cc_library(executor SRCS executor.cc DEPS op_registry device_context scope framework_proto backward glog lod_rank_table) +cc_library(executor SRCS executor.cc DEPS op_registry device_context scope +framework_proto backward glog lod_rank_table profiler) cc_library(prune SRCS prune.cc DEPS framework_proto) cc_test(prune_test SRCS prune_test.cc DEPS op_info prune recurrent_op device_context) diff --git a/paddle/framework/executor.cc b/paddle/framework/executor.cc index c0418c9266e..d7233882e71 100644 --- a/paddle/framework/executor.cc +++ b/paddle/framework/executor.cc @@ -22,6 +22,7 @@ limitations under the License. */ #include "paddle/framework/lod_tensor_array.h" #include "paddle/framework/op_registry.h" #include "paddle/platform/place.h" +#include "paddle/platform/profiler.h" DEFINE_bool(check_nan_inf, false, "Checking whether operator produce NAN/INF or not. It will be " @@ -116,6 +117,11 @@ void Executor::Run(const ProgramDesc& pdesc, Scope* scope, int block_id, for (auto& op_desc : block.AllOps()) { auto op = paddle::framework::OpRegistry::CreateOp(*op_desc); VLOG(3) << op->DebugStringEx(local_scope); + + platform::DeviceContextPool& pool = platform::DeviceContextPool::Instance(); + auto dev_ctx = const_cast(pool.Get(place_)); + platform::RecordEvent record_event(op->Type(), dev_ctx); + op->Run(*local_scope, place_); if (FLAGS_check_nan_inf) { for (auto& vname : op->OutputVars(true)) { diff --git a/paddle/platform/profiler.cc b/paddle/platform/profiler.cc index 7e2e2d968ef..8175b827c3e 100644 --- a/paddle/platform/profiler.cc +++ b/paddle/platform/profiler.cc @@ -163,14 +163,17 @@ void EnableProfiler(ProfilerState state) { Mark("_start_profiler_", nullptr); } -std::vector> DisableProfiler() { - PADDLE_ENFORCE(g_state != ProfilerState::kDisabled, - "Can't disable profiling, since it's not starting."); - // Mark the profiling stop. - Mark("_stop_profiler_", nullptr); - g_state = ProfilerState::kDisabled; - std::vector> result; +void ResetProfiler() { std::lock_guard guard(g_all_event_lists_mutex); + for (auto it = g_all_event_lists.begin(); it != g_all_event_lists.end(); + ++it) { + (*it)->Clear(); + } +} + +std::vector> GetAllEvents() { + std::lock_guard guard(g_all_event_lists_mutex); + std::vector> result; for (auto it = g_all_event_lists.begin(); it != g_all_event_lists.end(); ++it) { result.emplace_back((*it)->Reduce()); @@ -178,6 +181,18 @@ std::vector> DisableProfiler() { return result; } +void DisableProfiler(EventSortingKey sorted_key) { + PADDLE_ENFORCE(g_state != ProfilerState::kDisabled, + "Can't disable profiling, since it's not starting."); + // Mark the profiling stop. + Mark("_stop_profiler_", nullptr); + g_state = ProfilerState::kDisabled; + + std::vector> all_events = GetAllEvents(); + ParseEvents(all_events, sorted_key); + ResetProfiler(); +} + void ParseEvents(std::vector>& events, EventSortingKey sorted_by) { if (g_profiler_place == "") return; @@ -291,12 +306,12 @@ void ParseEvents(std::vector>& events, } // Print report - PrintProfilingReport(events_table, sorted_domain, max_name_width + 4, 12); + PrintProfiler(events_table, sorted_domain, max_name_width + 4, 12); } -void PrintProfilingReport(std::vector>& events_table, - std::string& sorted_domain, const size_t name_width, - const size_t data_width) { +void PrintProfiler(std::vector>& events_table, + std::string& sorted_domain, const size_t name_width, + const size_t data_width) { // Output header information std::cout << "\n------------------------->" << " Profiling Report " diff --git a/paddle/platform/profiler.h b/paddle/platform/profiler.h index 6df48ef8806..85823af1d7d 100644 --- a/paddle/platform/profiler.h +++ b/paddle/platform/profiler.h @@ -84,6 +84,8 @@ struct EventList { return result; } + void Clear() { event_blocks.clear(); } + std::forward_list> event_blocks; }; @@ -110,12 +112,9 @@ struct RecordEvent { std::string name_; }; -// Enable the profiling function. -void EnableProfiler(ProfilerState state); - // Return the event list of all threads. Asummed the returned value calls // event_lists, event_lists[i][j] represents the j-th Event of i-th thread. -std::vector> DisableProfiler(); +std::vector> GetAllEvents(); // The information of each event given in the profiling report struct EventItem { @@ -130,13 +129,22 @@ struct EventItem { // Candidate keys to sort the profiling report enum EventSortingKey { kDefault, kCalls, kTotal, kMin, kMax, kAve }; +// Enable the profiling function. +void EnableProfiler(ProfilerState state); + +// Clear the g_all_event_lists, which is total event lists of all threads. +void ResetProfiler(); + +void DisableProfiler(EventSortingKey sorted_key); + // Parse the event list and output the profiling report void ParseEvents(std::vector>&, EventSortingKey sorted_by = EventSortingKey::kDefault); // Print results -void PrintProfilingReport(std::vector>& events_table, - std::string& sorted_domain, const size_t name_width, - const size_t data_width); +void PrintProfiler(std::vector>& events_table, + std::string& sorted_domain, const size_t name_width, + const size_t data_width); + } // namespace platform } // namespace paddle diff --git a/paddle/platform/profiler_test.cc b/paddle/platform/profiler_test.cc index 13dea713c71..81f10c91342 100644 --- a/paddle/platform/profiler_test.cc +++ b/paddle/platform/profiler_test.cc @@ -103,18 +103,14 @@ TEST(RecordEvent, RecordEvent) { // Bad Usage: PushEvent("event_without_pop", dev_ctx); PopEvent("event_without_push", dev_ctx); - std::vector> events = paddle::platform::DisableProfiler(); - // Will remove parsing-related code from test later - ParseEvents(events, EventSortingKey::kTotal); + std::vector> events = paddle::platform::GetAllEvents(); int cuda_startup_count = 0; int start_profiler_count = 0; - int stop_profiler_count = 0; for (size_t i = 0; i < events.size(); ++i) { for (size_t j = 0; j < events[i].size(); ++j) { if (events[i][j].name() == "_cuda_startup_") ++cuda_startup_count; if (events[i][j].name() == "_start_profiler_") ++start_profiler_count; - if (events[i][j].name() == "_stop_profiler_") ++stop_profiler_count; if (events[i][j].name() == "push") { EXPECT_EQ(events[i][j + 1].name(), "pop"); #ifdef PADDLE_WITH_CUDA @@ -127,5 +123,7 @@ TEST(RecordEvent, RecordEvent) { } EXPECT_EQ(cuda_startup_count % 5, 0); EXPECT_EQ(start_profiler_count, 1); - EXPECT_EQ(stop_profiler_count, 1); + + // Will remove parsing-related code from test later + DisableProfiler(EventSortingKey::kTotal); } diff --git a/paddle/pybind/CMakeLists.txt b/paddle/pybind/CMakeLists.txt index 7b374307071..e78673e0baa 100644 --- a/paddle/pybind/CMakeLists.txt +++ b/paddle/pybind/CMakeLists.txt @@ -1,7 +1,7 @@ if(WITH_PYTHON) cc_library(paddle_pybind SHARED SRCS pybind.cc exception.cc protobuf.cc const_value.cc - DEPS pybind python backward proto_desc paddle_memory executor prune init + DEPS pybind python backward proto_desc paddle_memory executor prune init profiler ${GLOB_OP_LIB}) if(NOT APPLE AND NOT ANDROID) target_link_libraries(paddle_pybind rt) diff --git a/paddle/pybind/protobuf.cc b/paddle/pybind/protobuf.cc index 4f959481537..d80f6b71e9b 100644 --- a/paddle/pybind/protobuf.cc +++ b/paddle/pybind/protobuf.cc @@ -21,74 +21,24 @@ limitations under the License. */ #include "paddle/framework/program_desc.h" #include "paddle/framework/var_desc.h" -// Cast boost::variant for PyBind. -// Copy from -// https://github.com/pybind/pybind11/issues/576#issuecomment-269563199 +using boost::variant; + namespace pybind11 { namespace detail { -// Can be replaced by a generic lambda in C++14 -struct variant_caster_visitor : public boost::static_visitor { - return_value_policy policy; - handle parent; - - variant_caster_visitor(return_value_policy policy, handle parent) - : policy(policy), parent(parent) {} - - template - handle operator()(T const &src) const { - return make_caster::cast(src, policy, parent); - } -}; - -template -struct variant_caster; - -template

yKh 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 diff --git a/v1_api_demo/model_zoo/resnet/example/image_list_provider.py b/v1_api_demo/model_zoo/resnet/example/image_list_provider.py deleted file mode 100644 index 2cd8eb8bf85..00000000000 --- a/v1_api_demo/model_zoo/resnet/example/image_list_provider.py +++ /dev/null @@ -1,102 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.utils.image_util import * -from paddle.trainer.PyDataProvider2 import * - - -def hook(settings, image_size, crop_size, color, file_list, is_train, **kwargs): - """ - Description: Init with a list of data file - file_list is the name list of input files. - kwargs["load_data_args"] is the value of 'load_data_args' - which can be set in config. - Each args is separated by a column. - image_size: the crop image size. - mean_meta: the path of the meta file to store the mean image. - mean_value: can be mean value, not a file. - can not set mean_meta and mean_value at the same time. - color: 'color' means a color image. Otherwise, it means a gray image. - is_train: whether the data provider is used for training. - Data argumentation might be different for training and testing. - """ - settings.img_size = image_size - settings.crop_size = crop_size - settings.mean_img_size = settings.crop_size - settings.color = color # default is color - settings.is_train = is_train - - settings.is_swap_channel = kwargs.get('swap_channel', None) - if settings.is_swap_channel is not None: - settings.swap_channel = settings.is_swap_channel - settings.is_swap_channel = True - - if settings.color: - settings.img_input_size = settings.crop_size * settings.crop_size * 3 - else: - settings.img_input_size = settings.crop_size * settings.crop_size - - settings.file_list = file_list - settings.mean_meta = kwargs.get('mean_meta', None) - settings.mean_value = kwargs.get('mean_value', None) - # can not specify both mean_meta and mean_value. - assert not (settings.mean_meta and settings.mean_value) - if not settings.mean_meta: - settings.mean_value = kwargs.get('mean_value') - sz = settings.crop_size * settings.crop_size - settings.img_mean = np.zeros(sz * 3, dtype=np.single) - for idx, value in enumerate(settings.mean_value): - settings.img_mean[idx * sz:(idx + 1) * sz] = value - settings.img_mean = settings.img_mean.reshape(3, settings.crop_size, - settings.crop_size) - - else: - settings.img_mean = load_meta(settings.mean_meta, - settings.mean_img_size, - settings.crop_size, settings.color) - - settings.input_types = [ - dense_vector(settings.img_input_size), # image feature - integer_value(1) - ] # labels - - settings.logger.info('Image short side: %s', settings.img_size) - settings.logger.info('Crop size: %s', settings.crop_size) - settings.logger.info('Meta path: %s', settings.mean_meta) - if settings.is_swap_channel: - settings.logger.info('swap channel: %s', settings.swap_channel) - settings.logger.info('DataProvider Initialization finished') - - -@provider(init_hook=hook, should_shuffle=False) -def processData(settings, file_list): - """ - The main function for loading data. - Load the batch, iterate all the images and labels in this batch. - file_name: the batch file name. - """ - img_path, lab = file_list.strip().split(' ') - img = Image.open(img_path) - img.load() - img = img.resize((settings.img_size, settings.img_size), Image.ANTIALIAS) - img = np.array(img).astype(np.float32) - if len(img.shape) == 3: - img = np.swapaxes(img, 1, 2) - img = np.swapaxes(img, 1, 0) - # swap channel - if settings.is_swap_channel: - img = img[settings.swap_channel, :, :] - img_feat = preprocess_img(img, settings.img_mean, settings.crop_size, - settings.is_train, settings.color) - yield img_feat.tolist(), int(lab.strip()) diff --git a/v1_api_demo/model_zoo/resnet/example/test.list b/v1_api_demo/model_zoo/resnet/example/test.list deleted file mode 100644 index 30bbf630b64..00000000000 --- a/v1_api_demo/model_zoo/resnet/example/test.list +++ /dev/null @@ -1,2 +0,0 @@ -example/dog.jpg 0 -example/cat.jpg 0 diff --git a/v1_api_demo/model_zoo/resnet/extract_fea_c++.sh b/v1_api_demo/model_zoo/resnet/extract_fea_c++.sh deleted file mode 100755 index 5447aa92dfb..00000000000 --- a/v1_api_demo/model_zoo/resnet/extract_fea_c++.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -#set names of layer which you want to extract feature -#in Outputs() of resnet.py -#like: Outputs("res5_3_branch2c_conv", "res5_3_branch2c_bn") -layer_num=50 -configure=./resnet.py -model_path=./model/resnet_$layer_num -fea_dir=fea_output -#Output is text file. -#Each line is one sample's features. -#If you set N layer names in Outputs() -#each line contains N features sperated by ";". - -# create model list file. -model_list=./model.list -touch $model_list | echo $model_path > $model_list - -paddle train \ - --local=true \ - --job=test \ - --config=$configure \ - --model_list=$model_list \ - --use_gpu=1 \ - --predict_output_dir=$fea_dir \ - --config_args=is_test=1,layer_num=$layer_num diff --git a/v1_api_demo/model_zoo/resnet/extract_fea_py.sh b/v1_api_demo/model_zoo/resnet/extract_fea_py.sh deleted file mode 100755 index 2e87152f7f8..00000000000 --- a/v1_api_demo/model_zoo/resnet/extract_fea_py.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -#Note if you use CPU mode, you need to set use_gpu=0 in classify.py. like this: -#conf_args = "is_test=0,use_gpu=1,is_predict=1" -#conf = parse_config(train_conf, conf_args) -#swig_paddle.initPaddle("--use_gpu=0") -python classify.py \ - --job=extract \ - --conf=resnet.py \ - --use_gpu=1 \ - --mean=model/mean_meta_224/mean.meta \ - --model=model/resnet_50 \ - --data=./example/test.list \ - --output_layer="res5_3_branch2c_conv,res5_3_branch2c_bn" \ - --output_dir=features diff --git a/v1_api_demo/model_zoo/resnet/get_model.sh b/v1_api_demo/model_zoo/resnet/get_model.sh deleted file mode 100755 index b33d8178ab7..00000000000 --- a/v1_api_demo/model_zoo/resnet/get_model.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -DIR="$( cd "$(dirname "$0")" ; pwd -P )" -cd $DIR - -mkdir model -cd model - -echo "Downloading ResNet models..." - -for file in resnet_50.tar.gz resnet_101.tar.gz resnet_152.tar.gz mean_meta_224.tar.gz -do - wget http://paddlepaddle.bj.bcebos.com/model_zoo/imagenet/$file - tar -xvf $file - rm $file -done - -echo "Done." diff --git a/v1_api_demo/model_zoo/resnet/load_feature.py b/v1_api_demo/model_zoo/resnet/load_feature.py deleted file mode 100644 index 5d3d0c0d30e..00000000000 --- a/v1_api_demo/model_zoo/resnet/load_feature.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import sys -import cPickle -import logging - -logging.basicConfig( - format='[%(levelname)s %(asctime)s %(filename)s:%(lineno)s] %(message)s') -logging.getLogger().setLevel(logging.INFO) - - -def load_feature_c(file): - """ - Load feature extracted by C++ interface. - Return a list. - file: feature file. - """ - features = [] - f = open(file, 'r') - for line in f: - sample = [] - for slot in line.strip().split(";"): - fea = [float(val) for val in slot.strip().split()] - if fea: - sample.append(fea) - features.append(sample) - f.close() - return features - - -def load_feature_py(feature_dir): - """ - Load feature extracted by python interface. - Return a dictionary. - feature_dir: directory of feature file. - """ - file_list = os.listdir(feature_dir) - file_list = [os.path.join(feature_dir, f) for f in file_list] - features = {} - for file_name in file_list: - with open(file_name, 'rb') as f: - feature = cPickle.load(f) - features.update(feature) - logging.info('Load feature file %s', file_name) - return features - - -if __name__ == '__main__': - print load_feature_py(sys.argv[1]) - #print load_feature_c(sys.argv[1]) diff --git a/v1_api_demo/model_zoo/resnet/net_diagram.sh b/v1_api_demo/model_zoo/resnet/net_diagram.sh deleted file mode 100755 index 1b06ffa44ee..00000000000 --- a/v1_api_demo/model_zoo/resnet/net_diagram.sh +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -:' -Visual deep residual network -1. Using make_model_diagram.py to generate dot file. -2. Using graphviz to convert dot file. - -Usage: -./net_diagram.sh -' - -set -e - -DIR="$( cd "$(dirname "$0")" ; pwd -P )" -cd $DIR - -img_type=png -img_fileprefix=ResNet_50 -conf_filename=resnet.py -dot_filename=ResNet_50.dot -config_str="layer_num=50,data_provider=0" - -python -m paddle.utils.make_model_diagram $conf_filename $dot_filename $config_str - -# If you have installed graphviz, running like this: -# dot -Tpng -o ResNet.png ResNet.dot diff --git a/v1_api_demo/model_zoo/resnet/predict.sh b/v1_api_demo/model_zoo/resnet/predict.sh deleted file mode 100755 index 2b67b17c48c..00000000000 --- a/v1_api_demo/model_zoo/resnet/predict.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -python classify.py \ - --job=predict \ - --conf=resnet.py\ - --model=model/resnet_50 \ - --multi_crop \ - --use_gpu=1 \ - --data=./example/test.list diff --git a/v1_api_demo/model_zoo/resnet/resnet.py b/v1_api_demo/model_zoo/resnet/resnet.py deleted file mode 100644 index 6fdd97fefc6..00000000000 --- a/v1_api_demo/model_zoo/resnet/resnet.py +++ /dev/null @@ -1,271 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * -""" -paper: https://arxiv.org/abs/1512.03385 -""" -is_test = get_config_arg("is_test", bool, False) -is_predict = get_config_arg("is_predict", bool, False) -data_provider = get_config_arg("data_provider", bool, True) -layer_num = get_config_arg("layer_num", int, 50) - -if not is_predict and data_provider: - train_list = 'train.list' if not is_test else None - # mean.meta is mean file of ImageNet dataset. - # mean.meta size : 3 x 224 x 224. - # If you use three mean value, set like: - # "mean_value:103.939,116.779,123.68;" - args = { - 'mean_meta': "model/mean_meta_224/mean.meta", - 'image_size': 224, - 'crop_size': 224, - 'color': True, - 'swap_channel:': [2, 1, 0] - } - define_py_data_sources2( - train_list, - 'example/test.list', - module="example.image_list_provider", - obj="processData", - args=args) - -batch_size = 1 -learning_rate = 0.1 / batch_size -momentum = 0.9 -weight_decay = 0.0001 * batch_size -default_momentum(momentum) -default_decay_rate(weight_decay) - -Settings( - algorithm='sgd', - batch_size=batch_size, - learning_rate=learning_rate, - - # set the appropriate parameters according your schedule - learning_method='momentum', - learning_rate_decay_a=0.5, - learning_rate_decay_b=1200000 * 10, - learning_rate_schedule="discexp", ) - - -def conv_bn_layer(name, - input, - filter_size, - num_filters, - stride, - padding, - channels=None, - active_type=ReluActivation()): - """ - A wrapper for conv layer with batch normalization layers. - Note: - conv layer has no activation. - """ - - tmp = img_conv_layer( - name=name + "_conv", - input=input, - filter_size=filter_size, - num_channels=channels, - num_filters=num_filters, - stride=stride, - padding=padding, - act=LinearActivation(), - bias_attr=False) - return batch_norm_layer( - name=name + "_bn", input=tmp, act=active_type, use_global_stats=is_test) - - -def bottleneck_block(name, input, num_filters1, num_filters2): - """ - A wrapper for bottlenect building block in ResNet. - Last conv_bn_layer has no activation. - Addto layer has activation of relu. - """ - last_name = conv_bn_layer( - name=name + '_branch2a', - input=input, - filter_size=1, - num_filters=num_filters1, - stride=1, - padding=0) - last_name = conv_bn_layer( - name=name + '_branch2b', - input=last_name, - filter_size=3, - num_filters=num_filters1, - stride=1, - padding=1) - last_name = conv_bn_layer( - name=name + '_branch2c', - input=last_name, - filter_size=1, - num_filters=num_filters2, - stride=1, - padding=0, - active_type=LinearActivation()) - - return addto_layer( - name=name + "_addto", input=[input, last_name], act=ReluActivation()) - - -def mid_projection(name, input, num_filters1, num_filters2, stride=2): - """ - A wrapper for middile projection in ResNet. - projection shortcuts are used for increasing dimensions, - and other shortcuts are identity - branch1: projection shortcuts are used for increasing - dimensions, has no activation. - branch2x: bottleneck building block, shortcuts are identity. - """ - # stride = 2 - branch1 = conv_bn_layer( - name=name + '_branch1', - input=input, - filter_size=1, - num_filters=num_filters2, - stride=stride, - padding=0, - active_type=LinearActivation()) - - last_name = conv_bn_layer( - name=name + '_branch2a', - input=input, - filter_size=1, - num_filters=num_filters1, - stride=stride, - padding=0) - last_name = conv_bn_layer( - name=name + '_branch2b', - input=last_name, - filter_size=3, - num_filters=num_filters1, - stride=1, - padding=1) - - last_name = conv_bn_layer( - name=name + '_branch2c', - input=last_name, - filter_size=1, - num_filters=num_filters2, - stride=1, - padding=0, - active_type=LinearActivation()) - - return addto_layer( - name=name + "_addto", input=[branch1, last_name], act=ReluActivation()) - - -def deep_res_net(res2_num=3, res3_num=4, res4_num=6, res5_num=3): - """ - A wrapper for 50,101,152 layers of ResNet. - res2_num: number of blocks stacked in conv2_x - res3_num: number of blocks stacked in conv3_x - res4_num: number of blocks stacked in conv4_x - res5_num: number of blocks stacked in conv5_x - """ - # For ImageNet - # conv1: 112x112 - img = data_layer(name='input', size=224 * 224 * 3) - tmp = conv_bn_layer( - "conv1", - img, - filter_size=7, - channels=3, - num_filters=64, - stride=2, - padding=3) - tmp = img_pool_layer(name="pool1", input=tmp, pool_size=3, stride=2) - - # conv2_x: 56x56 - tmp = mid_projection( - name="res2_1", input=tmp, num_filters1=64, num_filters2=256, stride=1) - for i in xrange(2, res2_num + 1, 1): - tmp = bottleneck_block( - name="res2_" + str(i), input=tmp, num_filters1=64, num_filters2=256) - - # conv3_x: 28x28 - tmp = mid_projection( - name="res3_1", input=tmp, num_filters1=128, num_filters2=512) - for i in xrange(2, res3_num + 1, 1): - tmp = bottleneck_block( - name="res3_" + str(i), - input=tmp, - num_filters1=128, - num_filters2=512) - - # conv4_x: 14x14 - tmp = mid_projection( - name="res4_1", input=tmp, num_filters1=256, num_filters2=1024) - for i in xrange(2, res4_num + 1, 1): - tmp = bottleneck_block( - name="res4_" + str(i), - input=tmp, - num_filters1=256, - num_filters2=1024) - - # conv5_x: 7x7 - tmp = mid_projection( - name="res5_1", input=tmp, num_filters1=512, num_filters2=2048) - for i in xrange(2, res5_num + 1, 1): - tmp = bottleneck_block( - name="res5_" + str(i), - input=tmp, - num_filters1=512, - num_filters2=2048) - - tmp = img_pool_layer( - name='avgpool', - input=tmp, - pool_size=7, - stride=1, - pool_type=AvgPooling()) - - output = fc_layer( - name='output', input=tmp, size=1000, act=SoftmaxActivation()) - - if not is_predict: - classification_cost( - input=output, label=data_layer( - name='label', size=1)) - - -def res_net_50(): - deep_res_net(3, 4, 6, 3) - - -def res_net_101(): - deep_res_net(3, 4, 23, 3) - - -def res_net_152(): - deep_res_net(3, 8, 36, 3) - - -if not is_predict: - Inputs("input", "label") -else: - Inputs("input") -# Outputs("cost-softmax" if not is_predict else "output") -Outputs("res5_3_branch2c_conv", "res5_3_branch2c_bn") - -if layer_num == 50: - res_net_50() -elif layer_num == 101: - res_net_101() -elif layer_num == 152: - res_net_152() -else: - print("Wrong layer number.") diff --git a/v1_api_demo/quick_start/.gitignore b/v1_api_demo/quick_start/.gitignore deleted file mode 100644 index f71662563ff..00000000000 --- a/v1_api_demo/quick_start/.gitignore +++ /dev/null @@ -1,15 +0,0 @@ -*.pyc -data/dict.txt -data/dict_all.txt -data/labels.list -data/mosesdecoder-master/ -data/reviews_Electronics_5.json.gz -data/test.list -data/test.txt -data/train.list -data/train.txt -data/pred.list -data/pred.txt -dataprovider_copy_1.py -train.log -output diff --git a/v1_api_demo/quick_start/api_predict.py b/v1_api_demo/quick_start/api_predict.py deleted file mode 100755 index 9bdffe10062..00000000000 --- a/v1_api_demo/quick_start/api_predict.py +++ /dev/null @@ -1,147 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os, sys -import numpy as np -from optparse import OptionParser -from py_paddle import swig_paddle, DataProviderConverter -from paddle.trainer.PyDataProvider2 import sparse_binary_vector -from paddle.trainer.config_parser import parse_config -""" -Usage: run following command to show help message. - python api_predict.py -h -""" - - -class QuickStartPrediction(): - def __init__(self, train_conf, dict_file, model_dir=None, label_file=None): - """ - train_conf: trainer configure. - dict_file: word dictionary file name. - model_dir: directory of model. - """ - self.train_conf = train_conf - self.dict_file = dict_file - self.word_dict = {} - self.dict_dim = self.load_dict() - self.model_dir = model_dir - if model_dir is None: - self.model_dir = os.path.dirname(train_conf) - - self.label = None - if label_file is not None: - self.load_label(label_file) - - conf = parse_config(train_conf, "is_predict=1") - self.network = swig_paddle.GradientMachine.createFromConfigProto( - conf.model_config) - self.network.loadParameters(self.model_dir) - input_types = [sparse_binary_vector(self.dict_dim)] - self.converter = DataProviderConverter(input_types) - - def load_dict(self): - """ - Load dictionary from self.dict_file. - """ - for line_count, line in enumerate(open(self.dict_file, 'r')): - self.word_dict[line.strip().split('\t')[0]] = line_count - return len(self.word_dict) - - def load_label(self, label_file): - """ - Load label. - """ - self.label = {} - for v in open(label_file, 'r'): - self.label[int(v.split('\t')[1])] = v.split('\t')[0] - - def get_index(self, data): - """ - transform word into integer index according to the dictionary. - """ - words = data.strip().split() - word_slot = [self.word_dict[w] for w in words if w in self.word_dict] - return word_slot - - def batch_predict(self, data_batch): - input = self.converter(data_batch) - output = self.network.forwardTest(input) - prob = output[0]["id"].tolist() - print("predicting labels is:") - print prob - - -def option_parser(): - usage = "python predict.py -n config -w model_dir -d dictionary -i input_file " - parser = OptionParser(usage="usage: %s [options]" % usage) - parser.add_option( - "-n", - "--tconf", - action="store", - dest="train_conf", - help="network config") - parser.add_option( - "-d", - "--dict", - action="store", - dest="dict_file", - help="dictionary file") - parser.add_option( - "-b", - "--label", - action="store", - dest="label", - default=None, - help="dictionary file") - parser.add_option( - "-c", - "--batch_size", - type="int", - action="store", - dest="batch_size", - default=1, - help="the batch size for prediction") - parser.add_option( - "-w", - "--model", - action="store", - dest="model_path", - default=None, - help="model path") - return parser.parse_args() - - -def main(): - options, args = option_parser() - train_conf = options.train_conf - batch_size = options.batch_size - dict_file = options.dict_file - model_path = options.model_path - label = options.label - swig_paddle.initPaddle("--use_gpu=0") - predict = QuickStartPrediction(train_conf, dict_file, model_path, label) - - batch = [] - labels = [] - for line in sys.stdin: - [label, text] = line.split("\t") - labels.append(int(label)) - batch.append([predict.get_index(text)]) - print("labels is:") - print labels - predict.batch_predict(batch) - - -if __name__ == '__main__': - main() diff --git a/v1_api_demo/quick_start/api_predict.sh b/v1_api_demo/quick_start/api_predict.sh deleted file mode 100755 index 4d9aa9e8854..00000000000 --- a/v1_api_demo/quick_start/api_predict.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -#Note the default model is pass-00002, you shold make sure the model path -#exists or change the mode path. -#only test on trainer_config.lr.py -model=output/model/pass-00001/ -config=trainer_config.lr.py -label=data/labels.list -dict=data/dict.txt -batch_size=20 -head -n$batch_size data/test.txt | python api_predict.py \ - --tconf=$config\ - --model=$model \ - --label=$label \ - --dict=$dict \ - --batch_size=$batch_size diff --git a/v1_api_demo/quick_start/api_train.py b/v1_api_demo/quick_start/api_train.py deleted file mode 100644 index 5699789daa4..00000000000 --- a/v1_api_demo/quick_start/api_train.py +++ /dev/null @@ -1,122 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import argparse -import itertools -import random - -from paddle.trainer.config_parser import parse_config -from py_paddle import swig_paddle as api -from py_paddle import DataProviderConverter -from paddle.trainer.PyDataProvider2 \ - import integer_value, integer_value_sequence, sparse_binary_vector - - -def parse_arguments(): - parser = argparse.ArgumentParser() - parser.add_argument( - "--train_data", type=str, required=False, help="train data file") - parser.add_argument("--test_data", type=str, help="test data file") - parser.add_argument( - "--config", type=str, required=True, help="config file name") - parser.add_argument("--dict_file", required=True, help="dictionary file") - parser.add_argument( - "--seq", default=1, type=int, help="whether use sequence training") - parser.add_argument( - "--use_gpu", default=0, type=int, help="whether use GPU for training") - parser.add_argument( - "--trainer_count", - default=1, - type=int, - help="Number of threads for training") - parser.add_argument( - "--num_passes", default=5, type=int, help="Number of training passes") - return parser.parse_args() - - -UNK_IDX = 0 - - -def load_data(file_name, word_dict): - with open(file_name, 'r') as f: - for line in f: - label, comment = line.strip().split('\t') - words = comment.split() - word_slot = [word_dict.get(w, UNK_IDX) for w in words] - yield word_slot, int(label) - - -def load_dict(dict_file): - word_dict = dict() - with open(dict_file, 'r') as f: - for i, line in enumerate(f): - w = line.strip().split()[0] - word_dict[w] = i - return word_dict - - -def main(): - options = parse_arguments() - api.initPaddle("--use_gpu=%s" % options.use_gpu, - "--trainer_count=%s" % options.trainer_count) - - word_dict = load_dict(options.dict_file) - train_dataset = list(load_data(options.train_data, word_dict)) - if options.test_data: - test_dataset = list(load_data(options.test_data, word_dict)) - else: - test_dataset = None - - trainer_config = parse_config(options.config, - "dict_file=%s" % options.dict_file) - # No need to have data provider for trainer - trainer_config.ClearField('data_config') - trainer_config.ClearField('test_data_config') - - # create a GradientMachine from the model configuratin - model = api.GradientMachine.createFromConfigProto( - trainer_config.model_config) - # create a trainer for the gradient machine - trainer = api.Trainer.create(trainer_config, model) - - # create a data converter which converts data to PaddlePaddle - # internal format - input_types = [ - integer_value_sequence(len(word_dict)) if options.seq else - sparse_binary_vector(len(word_dict)), integer_value(2) - ] - converter = DataProviderConverter(input_types) - - batch_size = trainer_config.opt_config.batch_size - trainer.startTrain() - for train_pass in xrange(options.num_passes): - trainer.startTrainPass() - random.shuffle(train_dataset) - for pos in xrange(0, len(train_dataset), batch_size): - batch = itertools.islice(train_dataset, pos, pos + batch_size) - size = min(batch_size, len(train_dataset) - pos) - trainer.trainOneDataBatch(size, converter(batch)) - trainer.finishTrainPass() - if test_dataset: - trainer.startTestPeriod() - for pos in xrange(0, len(test_dataset), batch_size): - batch = itertools.islice(test_dataset, pos, pos + batch_size) - size = min(batch_size, len(test_dataset) - pos) - trainer.testOneDataBatch(size, converter(batch)) - trainer.finishTestPeriod() - trainer.finishTrain() - - -if __name__ == '__main__': - main() diff --git a/v1_api_demo/quick_start/api_train.sh b/v1_api_demo/quick_start/api_train.sh deleted file mode 100755 index 9b2a4e2f224..00000000000 --- a/v1_api_demo/quick_start/api_train.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -# Note: if using trainer_config.emb.py, trainer_config.cnn.py -# or trainer_config.lstm.py, you need to change --seq to --seq=1 -# because they are sequence models. -python api_train.py \ - --config=trainer_config.lr.py \ - --trainer_count=2 \ - --num_passes=15 \ - --use_gpu=0 \ - --seq=0 \ - --train_data=data/train.txt \ - --test_data=data/test.txt \ - --dict_file=data/dict.txt \ - 2>&1 | tee 'train.log' diff --git a/v1_api_demo/quick_start/cluster/cluster_train.sh b/v1_api_demo/quick_start/cluster/cluster_train.sh deleted file mode 100755 index a7b1f01064b..00000000000 --- a/v1_api_demo/quick_start/cluster/cluster_train.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -# Should run pserver.sh before run this script. -bin_dir=$(cd `dirname $0`; pwd) -home_dir=$(cd "${bin_dir}/.."; pwd) -source "$bin_dir/env.sh" - -model_dir="$bin_dir/output" -log_file="$bin_dir/train.log" - -pushd "$home_dir" -cfg=trainer_config.lr.py -paddle train \ - --start_pserver=false \ - --config=$cfg \ - --save_dir=${model_dir} \ - --trainer_count=4 \ - --local=0 \ - --log_period=100 \ - --num_passes=15 \ - --use_gpu=false \ - --show_parameter_stats_period=100 \ - --test_all_data_in_one_period=1 \ - --num_gradient_servers=1 \ - --nics=`get_nics` \ - --port=7164 \ - --ports_num=1 \ - --pservers="127.0.0.1" \ - --comment="paddle_trainer" \ - 2>&1 | tee "$log_file" -popd diff --git a/v1_api_demo/quick_start/cluster/env.sh b/v1_api_demo/quick_start/cluster/env.sh deleted file mode 100644 index a404993835d..00000000000 --- a/v1_api_demo/quick_start/cluster/env.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -function get_nics() { - machine=`uname -s` - local nics="" - if [ "$machine" == "Linux" ]; then - nics="lo" - elif [ "$machine" == "Darwin" ]; then - nics="lo0" - else - nics="unsupport" - fi - echo $nics -} diff --git a/v1_api_demo/quick_start/cluster/pserver.sh b/v1_api_demo/quick_start/cluster/pserver.sh deleted file mode 100755 index b187c1d9b91..00000000000 --- a/v1_api_demo/quick_start/cluster/pserver.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e -bin_dir=$(cd `dirname $0`; pwd) -source "$bin_dir/env.sh" - -paddle pserver \ - --nics=`get_nics` \ - --port=7164 \ - --ports_num=1 \ - --ports_num_for_sparse=1 \ - --num_gradient_servers=1 \ - --comment="paddle_pserver" \ - 2>&1 | tee 'pserver.log' diff --git a/v1_api_demo/quick_start/data/README.md b/v1_api_demo/quick_start/data/README.md deleted file mode 100644 index 63abcf7ebf3..00000000000 --- a/v1_api_demo/quick_start/data/README.md +++ /dev/null @@ -1,9 +0,0 @@ -This dataset consists of electronics product reviews associated with -binary labels (positive/negative) for sentiment classification. - -The preprocessed data can be downloaded by script `get_data.sh`. -The data was derived from reviews_Electronics_5.json.gz at - -http://snap.stanford.edu/data/amazon/productGraph/categoryFiles/reviews_Electronics_5.json.gz - -If you want to process the raw data, you can use the script `proc_from_raw_data/get_data.sh`. diff --git a/v1_api_demo/quick_start/data/get_data.sh b/v1_api_demo/quick_start/data/get_data.sh deleted file mode 100755 index a09a18f919e..00000000000 --- a/v1_api_demo/quick_start/data/get_data.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -DIR="$( cd "$(dirname "$0")" ; pwd -P )" -cd $DIR - -# Download the preprocessed data -wget http://paddlepaddle.bj.bcebos.com/demo/quick_start_preprocessed_data/preprocessed_data.tar.gz - -# Extract package -tar zxvf preprocessed_data.tar.gz - -# Remove compressed package -rm preprocessed_data.tar.gz diff --git a/v1_api_demo/quick_start/data/proc_from_raw_data/get_data.sh b/v1_api_demo/quick_start/data/proc_from_raw_data/get_data.sh deleted file mode 100755 index d976eaebfaa..00000000000 --- a/v1_api_demo/quick_start/data/proc_from_raw_data/get_data.sh +++ /dev/null @@ -1,79 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# 1. size of pos : neg = 1:1. -# 2. size of testing set = min(25k, len(all_data) * 0.1), others is traning set. -# 3. distinct train set and test set. - -set -e - -DIR="$( cd "$(dirname "$0")" ; pwd -P )" -cd $DIR - -# Download data -echo "Downloading Amazon Electronics reviews data..." -# http://jmcauley.ucsd.edu/data/amazon/ -wget http://snap.stanford.edu/data/amazon/productGraph/categoryFiles/reviews_Electronics_5.json.gz -echo "Downloading mosesdecoder..." -# https://github.com/moses-smt/mosesdecoder -wget https://github.com/moses-smt/mosesdecoder/archive/master.zip - -unzip master.zip -rm master.zip - -################## -# Preprocess data -echo "Preprocess data..." -export LC_ALL=C -UNAME_STR=`uname` - -if [ ${UNAME_STR} == 'Linux' ]; then - SHUF_PROG='shuf' -else - SHUF_PROG='gshuf' -fi - -mkdir -p tmp -python preprocess.py -i reviews_Electronics_5.json.gz -# uniq and shuffle -cd tmp -echo 'Uniq and shuffle...' -cat pos_*|sort|uniq|${SHUF_PROG}> pos.shuffed -cat neg_*|sort|uniq|${SHUF_PROG}> neg.shuffed - -min_len=`sed -n '$=' neg.shuffed` -test_num=$((min_len/10)) -if [ $test_num -gt 12500 ];then - test_num=12500 -fi -train_num=$((min_len-test_num)) - -head -n$train_num pos.shuffed >train.pos -head -n$train_num neg.shuffed >train.neg -tail -n$test_num pos.shuffed >test.pos -tail -n$test_num neg.shuffed >test.neg - -cat train.pos train.neg | ${SHUF_PROG} >../train.txt -cat test.pos test.neg | ${SHUF_PROG} >../test.txt - -cd - -echo 'train.txt' > train.list -echo 'test.txt' > test.list - -# use 30k dict -rm -rf tmp -mv dict.txt dict_all.txt -cat dict_all.txt | head -n 30001 > dict.txt -echo 'Done.' diff --git a/v1_api_demo/quick_start/data/proc_from_raw_data/preprocess.py b/v1_api_demo/quick_start/data/proc_from_raw_data/preprocess.py deleted file mode 100755 index 5706351a21f..00000000000 --- a/v1_api_demo/quick_start/data/proc_from_raw_data/preprocess.py +++ /dev/null @@ -1,236 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -# -*- coding: UTF-8 -*- - -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -1. Tokenize the words and punctuation -2. pos sample : rating score 5; neg sample: rating score 1-2. - -Usage: - python preprocess.py -i data_file [random seed] -""" - -import sys -import os -import operator -import gzip -from subprocess import Popen, PIPE -from optparse import OptionParser -import json -from multiprocessing import Queue -from multiprocessing import Pool -import multiprocessing - -batch_size = 5000 -word_count = {} -num_tokenize = max(1, - multiprocessing.cpu_count() - 2) # parse + tokenize + save -max_queue_size = 8 -parse_queue = Queue(maxsize=max_queue_size + num_tokenize) -tokenize_queue = Queue(maxsize=max_queue_size + num_tokenize) - - -def create_dict(data): - """ - Create dictionary based on data, and saved in data_dir/dict.txt. - The first line is unk \t -1. - data: list, input data by batch. - """ - for seq in data: - try: - for w in seq.lower().split(): - if w not in word_count: - word_count[w] = 1 - else: - word_count[w] += 1 - except: - sys.stderr.write(seq + "\tERROR\n") - - -def parse(path): - """ - Open .gz file. - """ - sys.stderr.write(path) - g = gzip.open(path, 'r') - for l in g: - yield json.loads(l) - g.close() - - -def tokenize(sentences): - """ - Use tokenizer.perl to tokenize input sentences. - tokenizer.perl is tool of Moses. - sentences : a list of input sentences. - return: a list of processed text. - """ - dir = './mosesdecoder-master/scripts/tokenizer/tokenizer.perl' - if not os.path.exists(dir): - sys.exit( - "The ./mosesdecoder-master/scripts/tokenizer/tokenizer.perl does not exists." - ) - tokenizer_cmd = [dir, '-l', 'en', '-q', '-'] - assert isinstance(sentences, list) - text = "\n".join(sentences) - tokenizer = Popen(tokenizer_cmd, stdin=PIPE, stdout=PIPE) - tok_text, _ = tokenizer.communicate(text) - toks = tok_text.split('\n')[:-1] - return toks - - -def save_data(instance, data_dir, pre_fix, batch_num): - """ - save data by batch - """ - label = ['1' if pre_fix == 'pos' else '0' for i in range(len(instance))] - lines = ['%s\t%s' % (label[i], instance[i]) for i in range(len(label))] - file_name = os.path.join(data_dir, "%s_%s.txt" % (pre_fix, batch_num)) - file(file_name, 'w').write('\n'.join(lines) + '\n') - - -def tokenize_batch(id): - """ - tokenize data by batch - """ - while True: - num_batch, instance, pre_fix = parse_queue.get() - if num_batch == -1: ### parse_queue finished - tokenize_queue.put((-1, None, None)) - sys.stderr.write("Thread %s finish\n" % (id)) - break - tokenize_instance = tokenize(instance) - tokenize_queue.put((num_batch, tokenize_instance, pre_fix)) - sys.stderr.write('.') - - -def save_batch(data_dir, num_tokenize, data_dir_dict): - """ - save data by batch - build dict.txt - """ - token_count = 0 - while True: - num_batch, instance, pre_fix = tokenize_queue.get() - if num_batch == -1: - token_count += 1 - if token_count == num_tokenize: #### tokenize finished. - break - else: - continue - save_data(instance, data_dir, pre_fix, num_batch) - create_dict(instance) ## update dict - - sys.stderr.write("save file finish\n") - f = open(data_dir_dict, 'w') - f.write('%s\t%s\n' % ('unk', '-1')) - for k, v in sorted(word_count.items(), key=operator.itemgetter(1), \ - reverse=True): - f.write('%s\t%s\n' % (k, v)) - f.close() - sys.stderr.write("build dict finish\n") - - -def parse_batch(data, num_tokenize): - """ - parse data by batch - parse -> tokenize -> save - """ - raw_txt = parse(data) - neg, pos = [], [] - count = 0 - sys.stderr.write("extract raw data\n") - for l in raw_txt: - rating = l["overall"] - text = l["reviewText"].lower() # # convert words to lower case - if rating == 5.0 and text: - pos.append(text) - if rating < 3.0 and text: - neg.append(text) - if len(pos) == batch_size or len(neg) == batch_size: - if len(pos) == batch_size: - batch = pos - pre_fix = 'pos' - else: - batch = neg - pre_fix = 'neg' - - parse_queue.put((count, batch, pre_fix)) - count += 1 - if pre_fix == 'pos': - pos = [] - else: - neg = [] - - if len(pos) > 0: - parse_queue.put((count, pos, 'pos')) - count += 1 - if len(neg) > 0: - parse_queue.put((count, neg, 'neg')) - count += 1 - for i in range(num_tokenize): - parse_queue.put((-1, None, None)) #### for tokenize's input finished - sys.stderr.write("parsing finish\n") - - -def option_parser(): - parser = OptionParser(usage="usage: python preprcoess.py "\ - "-i data_path [options]") - parser.add_option( - "-i", "--data", action="store", dest="input", help="Input data path.") - parser.add_option( - "-s", - "--seed", - action="store", - dest="seed", - default=1024, - help="Set random seed.") - return parser.parse_args() - - -def main(): - reload(sys) - sys.setdefaultencoding('utf-8') - options, args = option_parser() - data = options.input - seed = options.seed - data_dir_dict = os.path.join(os.path.dirname(data), 'dict.txt') - data_dir = os.path.join(os.path.dirname(data), 'tmp') - pool = Pool(processes=num_tokenize + 2) - pool.apply_async(parse_batch, args=(data, num_tokenize)) - for i in range(num_tokenize): - pool.apply_async(tokenize_batch, args=(str(i), )) - pool.apply_async(save_batch, args=(data_dir, num_tokenize, data_dir_dict)) - pool.close() - pool.join() - - file(os.path.join(os.path.dirname(data), 'labels.list'), - 'w').write('neg\t0\npos\t1\n') - - -if __name__ == '__main__': - main() diff --git a/v1_api_demo/quick_start/dataprovider_bow.py b/v1_api_demo/quick_start/dataprovider_bow.py deleted file mode 100644 index 27454955864..00000000000 --- a/v1_api_demo/quick_start/dataprovider_bow.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer.PyDataProvider2 import * - -# id of the word not in dictionary -UNK_IDX = 0 - - -# initializer is called by the framework during initialization. -# It allows the user to describe the data types and setup the -# necessary data structure for later use. -# `settings` is an object. initializer need to properly fill settings.input_types. -# initializer can also store other data structures needed to be used at process(). -# In this example, dictionary is stored in settings. -# `dictionay` and `kwargs` are arguments passed from trainer_config.lr.py -def initializer(settings, dictionary, **kwargs): - # Put the word dictionary into settings - settings.word_dict = dictionary - - # setting.input_types specifies what the data types the data provider - # generates. - settings.input_types = { - # The first input is a sparse_binary_vector, - # which means each dimension of the vector is either 0 or 1. It is the - # bag-of-words (BOW) representation of the texts. - 'word': sparse_binary_vector(len(dictionary)), - # The second input is an integer. It represents the category id of the - # sample. 2 means there are two labels in the dataset. - # (1 for positive and 0 for negative) - 'label': integer_value(2) - } - - -# Delaring a data provider. It has an initializer 'data_initialzer'. -# It will cache the generated data of the first pass in memory, so that -# during later pass, no on-the-fly data generation will be needed. -# `setting` is the same object used by initializer() -# `file_name` is the name of a file listed train_list or test_list file given -# to define_py_data_sources2(). See trainer_config.lr.py. -@provider(init_hook=initializer, cache=CacheType.CACHE_PASS_IN_MEM) -def process(settings, file_name): - # Open the input data file. - with open(file_name, 'r') as f: - # Read each line. - for line in f: - # Each line contains the label and text of the comment, separated by \t. - label, comment = line.strip().split('\t') - - # Split the words into a list. - words = comment.split() - - # convert the words into a list of ids by looking them up in word_dict. - word_vector = [settings.word_dict.get(w, UNK_IDX) for w in words] - - # Return the features for the current comment. The first is a list - # of ids representing a 0-1 binary sparse vector of the text, - # the second is the integer id of the label. - yield {'word': word_vector, 'label': int(label)} - - -def predict_initializer(settings, dictionary, **kwargs): - settings.word_dict = dictionary - settings.input_types = {'word': sparse_binary_vector(len(dictionary))} - - -# Declaring a data provider for prediction. The difference with process -# is that label is not generated. -@provider(init_hook=predict_initializer, should_shuffle=False) -def process_predict(settings, file_name): - with open(file_name, 'r') as f: - for line in f: - comment = line.strip().split() - word_vector = [settings.word_dict.get(w, UNK_IDX) for w in comment] - yield {'word': word_vector} diff --git a/v1_api_demo/quick_start/dataprovider_emb.py b/v1_api_demo/quick_start/dataprovider_emb.py deleted file mode 100755 index ddfa3ce9b73..00000000000 --- a/v1_api_demo/quick_start/dataprovider_emb.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer.PyDataProvider2 import * - -UNK_IDX = 0 - - -def initializer(settings, dictionary, **kwargs): - settings.word_dict = dictionary - settings.input_types = { - # Define the type of the first input as sequence of integer. - # The value of the integers range from 0 to len(dictrionary)-1 - 'word': integer_value_sequence(len(dictionary)), - # Define the second input for label id - 'label': integer_value(2) - } - - -@provider(init_hook=initializer, cache=CacheType.CACHE_PASS_IN_MEM) -def process(settings, file_name): - with open(file_name, 'r') as f: - for line in f: - label, comment = line.strip().split('\t') - words = comment.split() - word_slot = [settings.word_dict.get(w, UNK_IDX) for w in words] - yield {'word': word_slot, 'label': int(label)} - - -def predict_initializer(settings, dictionary, **kwargs): - settings.word_dict = dictionary - settings.input_types = {'word': integer_value_sequence(len(dictionary))} - - -@provider(init_hook=predict_initializer, should_shuffle=False) -def process_predict(settings, file_name): - with open(file_name, 'r') as f: - for line in f: - comment = line.strip().split() - word_slot = [settings.word_dict.get(w, UNK_IDX) for w in comment] - yield {'word': word_slot} diff --git a/v1_api_demo/quick_start/predict.sh b/v1_api_demo/quick_start/predict.sh deleted file mode 100755 index e47c2dd01fb..00000000000 --- a/v1_api_demo/quick_start/predict.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -cfg=trainer_config.lr.py -#cfg=trainer_config.emb.py -#cfg=trainer_config.cnn.py -#cfg=trainer_config.lstm.py -model="output/pass-00003" -paddle train \ - --config=$cfg \ - --use_gpu=false \ - --job=test \ - --init_model_path=$model \ - --config_args=is_predict=1 \ - --predict_output_dir=. \ -2>&1 | tee 'predict.log' -paddle usage -l 'predict.log' -e $? -n "quick_start_predict_${cfg}" >/dev/null 2>&1 - -mv rank-00000 result.txt diff --git a/v1_api_demo/quick_start/train.sh b/v1_api_demo/quick_start/train.sh deleted file mode 100755 index 01697fed480..00000000000 --- a/v1_api_demo/quick_start/train.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -cfg=trainer_config.lr.py -#cfg=trainer_config.emb.py -#cfg=trainer_config.cnn.py -#cfg=trainer_config.lstm.py -#cfg=trainer_config.bidi-lstm.py -#cfg=trainer_config.db-lstm.py -#cfg=trainer_config.resnet-lstm.py -paddle train \ - --config=$cfg \ - --save_dir=./output \ - --trainer_count=4 \ - --log_period=100 \ - --num_passes=15 \ - --use_gpu=false \ - --show_parameter_stats_period=100 \ - --test_all_data_in_one_period=1 \ - 2>&1 | tee 'train.log' -paddle usage -l "train.log" -e $? -n "quick_start_${cfg}" >/dev/null 2>&1 diff --git a/v1_api_demo/quick_start/trainer_config.bidi-lstm.py b/v1_api_demo/quick_start/trainer_config.bidi-lstm.py deleted file mode 100644 index 3deff4aa00b..00000000000 --- a/v1_api_demo/quick_start/trainer_config.bidi-lstm.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -# edit-mode: -*- python -*- - -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -dict_file = "./data/dict.txt" -word_dict = dict() -with open(dict_file, 'r') as f: - for i, line in enumerate(f): - w = line.strip().split()[0] - word_dict[w] = i - -is_predict = get_config_arg('is_predict', bool, False) -trn = 'data/train.list' if not is_predict else None -tst = 'data/test.list' if not is_predict else 'data/pred.list' -process = 'process' if not is_predict else 'process_predict' -define_py_data_sources2( - train_list=trn, - test_list=tst, - module="dataprovider_emb", - obj=process, - args={"dictionary": word_dict}) - -batch_size = 128 if not is_predict else 1 -settings( - batch_size=batch_size, - learning_rate=2e-3, - learning_method=AdamOptimizer(), - regularization=L2Regularization(8e-4), - gradient_clipping_threshold=25) - -bias_attr = ParamAttr(initial_std=0., l2_rate=0.) -data = data_layer(name="word", size=len(word_dict)) -emb = embedding_layer(input=data, size=128) - -bi_lstm = bidirectional_lstm(input=emb, size=128) -dropout = dropout_layer(input=bi_lstm, dropout_rate=0.5) - -output = fc_layer( - input=dropout, size=2, bias_attr=bias_attr, act=SoftmaxActivation()) - -if is_predict: - maxid = maxid_layer(output) - outputs([maxid, output]) -else: - label = data_layer(name="label", size=2) - cls = classification_cost(input=output, label=label) - outputs(cls) diff --git a/v1_api_demo/quick_start/trainer_config.cnn.py b/v1_api_demo/quick_start/trainer_config.cnn.py deleted file mode 100644 index e09e41484d3..00000000000 --- a/v1_api_demo/quick_start/trainer_config.cnn.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -# edit-mode: -*- python -*- - -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -dict_file = "./data/dict.txt" -word_dict = dict() -with open(dict_file, 'r') as f: - for i, line in enumerate(f): - w = line.strip().split()[0] - word_dict[w] = i - -is_predict = get_config_arg('is_predict', bool, False) -trn = 'data/train.list' if not is_predict else None -tst = 'data/test.list' if not is_predict else 'data/pred.list' -process = 'process' if not is_predict else 'process_predict' -define_py_data_sources2( - train_list=trn, - test_list=tst, - module="dataprovider_emb", - obj=process, - args={"dictionary": word_dict}) - -batch_size = 128 if not is_predict else 1 -settings( - batch_size=batch_size, - learning_rate=2e-3, - learning_method=AdamOptimizer(), - regularization=L2Regularization(8e-4), - gradient_clipping_threshold=25) - -data = data_layer(name="word", size=len(word_dict)) -embedding = embedding_layer(input=data, size=128) -conv = sequence_conv_pool(input=embedding, context_len=3, hidden_size=512) -output = fc_layer(input=conv, size=2, act=SoftmaxActivation()) -if is_predict: - maxid = maxid_layer(output) - outputs([maxid, output]) -else: - label = data_layer(name="label", size=2) - cls = classification_cost(input=output, label=label) - outputs(cls) diff --git a/v1_api_demo/quick_start/trainer_config.db-lstm.py b/v1_api_demo/quick_start/trainer_config.db-lstm.py deleted file mode 100644 index fba802b4600..00000000000 --- a/v1_api_demo/quick_start/trainer_config.db-lstm.py +++ /dev/null @@ -1,74 +0,0 @@ -# edit-mode: -*- python -*- - -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -dict_file = "./data/dict.txt" -word_dict = dict() -with open(dict_file, 'r') as f: - for i, line in enumerate(f): - w = line.strip().split()[0] - word_dict[w] = i - -is_predict = get_config_arg('is_predict', bool, False) -trn = 'data/train.list' if not is_predict else None -tst = 'data/test.list' if not is_predict else 'data/pred.list' -process = 'process' if not is_predict else 'process_predict' -define_py_data_sources2( - train_list=trn, - test_list=tst, - module="dataprovider_emb", - obj=process, - args={"dictionary": word_dict}) - -batch_size = 128 if not is_predict else 1 -settings( - batch_size=batch_size, - learning_rate=2e-3, - learning_method=AdamOptimizer(), - regularization=L2Regularization(8e-4), - gradient_clipping_threshold=25) - -bias_attr = ParamAttr(initial_std=0., l2_rate=0.) - -data = data_layer(name="word", size=len(word_dict)) -emb = embedding_layer(input=data, size=128) - -hidden_0 = mixed_layer(size=128, input=[full_matrix_projection(input=emb)]) -lstm_0 = lstmemory(input=hidden_0, layer_attr=ExtraAttr(drop_rate=0.1)) - -input_layers = [hidden_0, lstm_0] - -for i in range(1, 8): - fc = fc_layer(input=input_layers, size=128) - lstm = lstmemory( - input=fc, - layer_attr=ExtraAttr(drop_rate=0.1), - reverse=(i % 2) == 1, ) - input_layers = [fc, lstm] - -lstm_last = pooling_layer(input=lstm, pooling_type=MaxPooling()) - -output = fc_layer( - input=lstm_last, size=2, bias_attr=bias_attr, act=SoftmaxActivation()) - -if is_predict: - maxid = maxid_layer(output) - outputs([maxid, output]) -else: - label = data_layer(name="label", size=2) - cls = classification_cost(input=output, label=label) - outputs(cls) diff --git a/v1_api_demo/quick_start/trainer_config.emb.py b/v1_api_demo/quick_start/trainer_config.emb.py deleted file mode 100644 index f69f98ff7fc..00000000000 --- a/v1_api_demo/quick_start/trainer_config.emb.py +++ /dev/null @@ -1,64 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -# edit-mode: -*- python -*- - -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -dict_file = "./data/dict.txt" -word_dict = dict() -with open(dict_file, 'r') as f: - for i, line in enumerate(f): - w = line.strip().split()[0] - word_dict[w] = i - -is_predict = get_config_arg('is_predict', bool, False) -trn = 'data/train.list' if not is_predict else None -tst = 'data/test.list' if not is_predict else 'data/pred.list' -process = 'process' if not is_predict else 'process_predict' -define_py_data_sources2( - train_list=trn, - test_list=tst, - module="dataprovider_emb", - obj=process, - args={"dictionary": word_dict}) - -batch_size = 128 if not is_predict else 1 -settings( - batch_size=batch_size, learning_rate=2e-3, learning_method=AdamOptimizer()) - -data = data_layer(name="word", size=len(word_dict)) -embedding = embedding_layer(input=data, size=128) -avg = pooling_layer(input=embedding, pooling_type=AvgPooling()) -output = fc_layer(input=avg, size=2, act=SoftmaxActivation()) -if is_predict: - maxid = maxid_layer(output) - outputs([maxid, output]) -else: - label = data_layer(name="label", size=2) - cls = classification_cost(input=output, label=label) - outputs(cls) diff --git a/v1_api_demo/quick_start/trainer_config.lr.py b/v1_api_demo/quick_start/trainer_config.lr.py deleted file mode 100644 index b7b694940e3..00000000000 --- a/v1_api_demo/quick_start/trainer_config.lr.py +++ /dev/null @@ -1,85 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -# edit-mode: -*- python -*- - -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -dict_file = get_config_arg('dict_file', str, "./data/dict.txt") -word_dict = dict() -with open(dict_file, 'r') as f: - for i, line in enumerate(f): - w = line.strip().split()[0] - word_dict[w] = i - -is_predict = get_config_arg('is_predict', bool, False) -trn = 'data/train.list' if not is_predict else None -tst = 'data/test.list' if not is_predict else 'data/pred.list' -process = 'process' if not is_predict else 'process_predict' - -# define the data sources for the model. -# We need to use different process for training and prediction. -# For training, the input data includes both word IDs and labels. -# For prediction, the input data only includs word Ids. -define_py_data_sources2( - train_list=trn, - test_list=tst, - module="dataprovider_bow", - obj=process, - args={"dictionary": word_dict}) - -batch_size = 128 if not is_predict else 1 -settings( - batch_size=batch_size, - learning_rate=2e-3, - learning_method=AdamOptimizer(), - regularization=L2Regularization(8e-4), - gradient_clipping_threshold=25) - -# Define the data for text features. The size of the data layer is the number -# of words in the dictionary. -data = data_layer(name="word", size=len(word_dict)) - -# Define a fully connected layer with logistic activation. -# (also called softmax activation). -output = fc_layer(input=data, size=2, act=SoftmaxActivation()) - -if not is_predict: - # For training, we need label and cost - - # define the category id for each example. - # The size of the data layer is the number of labels. - label = data_layer(name="label", size=2) - - # Define cross-entropy classification loss and error. - cls = classification_cost(input=output, label=label) - outputs(cls) -else: - # For prediction, no label is needed. We need to output - # We need to output classification result, and class probabilities. - maxid = maxid_layer(output) - outputs([maxid, output]) diff --git a/v1_api_demo/quick_start/trainer_config.lstm.py b/v1_api_demo/quick_start/trainer_config.lstm.py deleted file mode 100644 index 8967d78807b..00000000000 --- a/v1_api_demo/quick_start/trainer_config.lstm.py +++ /dev/null @@ -1,70 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -# edit-mode: -*- python -*- - -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -dict_file = "./data/dict.txt" -word_dict = dict() -with open(dict_file, 'r') as f: - for i, line in enumerate(f): - w = line.strip().split()[0] - word_dict[w] = i - -is_predict = get_config_arg('is_predict', bool, False) -trn = 'data/train.list' if not is_predict else None -tst = 'data/test.list' if not is_predict else 'data/pred.list' -process = 'process' if not is_predict else 'process_predict' -define_py_data_sources2( - train_list=trn, - test_list=tst, - module="dataprovider_emb", - obj=process, - args={"dictionary": word_dict}) - -batch_size = 128 if not is_predict else 1 -settings( - batch_size=batch_size, - learning_rate=2e-3, - learning_method=AdamOptimizer(), - regularization=L2Regularization(8e-4), - gradient_clipping_threshold=25) - -data = data_layer(name="word", size=len(word_dict)) -emb = embedding_layer(input=data, size=128) -lstm = simple_lstm( - input=emb, size=128, lstm_cell_attr=ExtraAttr(drop_rate=0.25)) -lstm_max = pooling_layer(input=lstm, pooling_type=MaxPooling()) -output = fc_layer(input=lstm_max, size=2, act=SoftmaxActivation()) -if is_predict: - maxid = maxid_layer(output) - outputs([maxid, output]) -else: - label = data_layer(name="label", size=2) - cls = classification_cost(input=output, label=label) - outputs(cls) diff --git a/v1_api_demo/quick_start/trainer_config.resnet-lstm.py b/v1_api_demo/quick_start/trainer_config.resnet-lstm.py deleted file mode 100644 index 32d0596f250..00000000000 --- a/v1_api_demo/quick_start/trainer_config.resnet-lstm.py +++ /dev/null @@ -1,104 +0,0 @@ -# Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve. -# -#Licensed under the Apache License, Version 2.0 (the "License"); -#you may not use this file except in compliance with the License. -#You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -#Unless required by applicable law or agreed to in writing, software -#distributed under the License is distributed on an "AS IS" BASIS, -#WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -#See the License for the specific language governing permissions and -#limitations under the License. -# edit-mode: -*- python -*- - -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -""" -This configuration is a demonstration of how to implement the stacked LSTM -with residual connections, i.e. an LSTM layer takes the sum of the hidden states -and inputs of the previous LSTM layer instead of only the hidden states. -This architecture is from: -Yonghui Wu, Mike Schuster, Zhifeng Chen, Quoc V. Le, Mohammad Norouzi, -Wolfgang Macherey, Maxim Krikun, Yuan Cao, Qin Gao, Klaus Macherey, -Jeff Klingner, Apurva Shah, Melvin Johnson, Xiaobing Liu, Lukasz Kaiser, -Stephan Gouws, Yoshikiyo Kato, Taku Kudo, Hideto Kazawa, Keith Stevens, -George Kurian, Nishant Patil, Wei Wang, Cliff Young, Jason Smith, Jason Riesa, -Alex Rudnick, Oriol Vinyals, Greg Corrado, Macduff Hughes, Jeffrey Dean. 2016. -Google's Neural Machine Translation System: Bridging the Gap between Human and -Machine Translation. In arXiv https://arxiv.org/pdf/1609.08144v2.pdf -Different from the architecture described in the paper, we use a stack single -direction LSTM layers as the first layer instead of bi-directional LSTM. Also, -since this is a demo code, to reduce computation time, we stacked 4 layers -instead of 8 layers. -""" - -from paddle.trainer_config_helpers import * - -dict_file = "./data/dict.txt" -word_dict = dict() -with open(dict_file, 'r') as f: - for i, line in enumerate(f): - w = line.strip().split()[0] - word_dict[w] = i - -is_predict = get_config_arg('is_predict', bool, False) -trn = 'data/train.list' if not is_predict else None -tst = 'data/test.list' if not is_predict else 'data/pred.list' -process = 'process' if not is_predict else 'process_predict' -define_py_data_sources2( - train_list=trn, - test_list=tst, - module="dataprovider_emb", - obj=process, - args={"dictionary": word_dict}) - -batch_size = 128 if not is_predict else 1 -settings( - batch_size=batch_size, - learning_rate=2e-3, - learning_method=AdamOptimizer(), - regularization=L2Regularization(8e-4), - gradient_clipping_threshold=25) - -bias_attr = ParamAttr(initial_std=0., l2_rate=0.) - -data = data_layer(name="word", size=len(word_dict)) -emb = embedding_layer(input=data, size=128) -lstm = simple_lstm(input=emb, size=128, lstm_cell_attr=ExtraAttr(drop_rate=0.1)) - -previous_input, previous_hidden_state = emb, lstm - -for i in range(3): - # The input to the current layer is the sum of the hidden state - # and input of the previous layer. - current_input = addto_layer(input=[previous_input, previous_hidden_state]) - hidden_state = simple_lstm( - input=current_input, size=128, lstm_cell_attr=ExtraAttr(drop_rate=0.1)) - previous_input, previous_hidden_state = current_input, hidden_state - -lstm = previous_hidden_state - -lstm_last = pooling_layer(input=lstm, pooling_type=MaxPooling()) -output = fc_layer( - input=lstm_last, size=2, bias_attr=bias_attr, act=SoftmaxActivation()) - -if is_predict: - maxid = maxid_layer(output) - outputs([maxid, output]) -else: - label = data_layer(name="label", size=2) - cls = classification_cost(input=output, label=label) - outputs(cls) diff --git a/v1_api_demo/sequence_tagging/data/get_data.sh b/v1_api_demo/sequence_tagging/data/get_data.sh deleted file mode 100755 index 0cdb394035e..00000000000 --- a/v1_api_demo/sequence_tagging/data/get_data.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -set -e - -DIR="$( cd "$(dirname "$0")" ; pwd -P )" -cd $DIR - -wget http://www.cnts.ua.ac.be/conll2000/chunking/train.txt.gz -wget http://www.cnts.ua.ac.be/conll2000/chunking/test.txt.gz diff --git a/v1_api_demo/sequence_tagging/data/test.list b/v1_api_demo/sequence_tagging/data/test.list deleted file mode 100644 index 073c0a0c906..00000000000 --- a/v1_api_demo/sequence_tagging/data/test.list +++ /dev/null @@ -1 +0,0 @@ -data/test.txt.gz diff --git a/v1_api_demo/sequence_tagging/data/train.list b/v1_api_demo/sequence_tagging/data/train.list deleted file mode 100644 index 43c24d5f648..00000000000 --- a/v1_api_demo/sequence_tagging/data/train.list +++ /dev/null @@ -1 +0,0 @@ -data/train.txt.gz diff --git a/v1_api_demo/sequence_tagging/dataprovider.py b/v1_api_demo/sequence_tagging/dataprovider.py deleted file mode 100644 index bb4b4465bc7..00000000000 --- a/v1_api_demo/sequence_tagging/dataprovider.py +++ /dev/null @@ -1,260 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer.PyDataProvider2 import * -import gzip -import logging - -logging.basicConfig( - format='[%(levelname)s %(asctime)s %(filename)s:%(lineno)s] %(message)s', ) -logger = logging.getLogger('paddle') -logger.setLevel(logging.INFO) - -OOV_POLICY_IGNORE = 0 -OOV_POLICY_USE = 1 -OOV_POLICY_ERROR = 2 - -num_original_columns = 3 - -# Feature combination patterns. -# [[-1,0], [0,0]] means previous token at column 0 and current token at -# column 0 are combined as one feature. -patterns = [ - [[-2, 0]], - [[-1, 0]], - [[0, 0]], - [[1, 0]], - [[2, 0]], - [[-1, 0], [0, 0]], - [[0, 0], [1, 0]], - [[-2, 1]], - [[-1, 1]], - [[0, 1]], - [[1, 1]], - [[2, 1]], - [[-2, 1], [-1, 1]], - [[-1, 1], [0, 1]], - [[0, 1], [1, 1]], - [[1, 1], [2, 1]], - [[-2, 1], [-1, 1], [0, 1]], - [[-1, 1], [0, 1], [1, 1]], - [[0, 1], [1, 1], [2, 1]], -] - -dict_label = { - 'B-ADJP': 0, - 'I-ADJP': 1, - 'B-ADVP': 2, - 'I-ADVP': 3, - 'B-CONJP': 4, - 'I-CONJP': 5, - 'B-INTJ': 6, - 'I-INTJ': 7, - 'B-LST': 8, - 'I-LST': 9, - 'B-NP': 10, - 'I-NP': 11, - 'B-PP': 12, - 'I-PP': 13, - 'B-PRT': 14, - 'I-PRT': 15, - 'B-SBAR': 16, - 'I-SBAR': 17, - 'B-UCP': 18, - 'I-UCP': 19, - 'B-VP': 20, - 'I-VP': 21, - 'O': 22 -} - - -def make_features(sequence): - length = len(sequence) - num_features = len(sequence[0]) - - def get_features(pos): - if pos < 0: - return ['#B%s' % -pos] * num_features - if pos >= length: - return ['#E%s' % (pos - length + 1)] * num_features - return sequence[pos] - - for i in xrange(length): - for pattern in patterns: - fname = '/'.join([get_features(i + pos)[f] for pos, f in pattern]) - sequence[i].append(fname) - - -''' -Source file format: -Each line is for one timestep. The features are separated by space. -An empty line indicates end of a sequence. - -cutoff: a list of numbers. If count of a feature is smaller than this, - it will be ignored. -if oov_policy[i] is OOV_POLICY_USE, id 0 is reserved for OOV features of -i-th column. - -return a list of dict for each column -''' - - -def create_dictionaries(filename, cutoff, oov_policy): - def add_to_dict(sequence, dicts): - num_features = len(dicts) - for features in sequence: - l = len(features) - assert l == num_features, "Wrong number of features " + line - for i in xrange(l): - if features[i] in dicts[i]: - dicts[i][features[i]] += 1 - else: - dicts[i][features[i]] = 1 - - num_features = len(cutoff) - dicts = [] - for i in xrange(num_features): - dicts.append(dict()) - - f = gzip.open(filename, 'rb') - - sequence = [] - - for line in f: - line = line.strip() - if not line: - make_features(sequence) - add_to_dict(sequence, dicts) - sequence = [] - continue - features = line.split(' ') - sequence.append(features) - - for i in xrange(num_features): - dct = dicts[i] - n = 1 if oov_policy[i] == OOV_POLICY_USE else 0 - todo = [] - for k, v in dct.iteritems(): - if v < cutoff[i]: - todo.append(k) - else: - dct[k] = n - n += 1 - - if oov_policy[i] == OOV_POLICY_USE: - # placeholder so that len(dct) will be the number of features - # including OOV - dct['#OOV#'] = 0 - - logger.info('column %d dict size=%d, ignored %d' % (i, n, len(todo))) - for k in todo: - del dct[k] - - f.close() - return dicts - - -def initializer(settings, **xargs): - cutoff = [3, 1, 0] - cutoff += [3] * len(patterns) - oov_policy = [OOV_POLICY_IGNORE, OOV_POLICY_ERROR, OOV_POLICY_ERROR] - oov_policy += [OOV_POLICY_IGNORE] * len(patterns) - dicts = create_dictionaries('data/train.txt.gz', cutoff, oov_policy) - dicts[2] = dict_label - settings.dicts = dicts - settings.oov_policy = oov_policy - input_types = [] - num_features = len(dicts) - for i in xrange(num_original_columns): - input_types.append(integer_sequence(len(dicts[i]))) - logger.info("slot %s size=%s" % (i, len(dicts[i]))) - if patterns: - dim = 0 - for i in xrange(num_original_columns, num_features): - dim += len(dicts[i]) - input_types.append(sparse_binary_vector_sequence(dim)) - logger.info("feature size=%s" % dim) - settings.input_types = input_types - - -''' -if oov_policy[i] == OOV_POLICY_USE, features in i-th column which are not -existed in dicts[i] will be assigned to id 0. -if oov_policy[i] == OOV_POLICY_ERROR, all features in i-th column MUST exist -in dicts[i]. -''' - - -@provider(init_hook=initializer, cache=CacheType.CACHE_PASS_IN_MEM) -def process(settings, filename): - input_file = filename - dicts = settings.dicts - oov_policy = settings.oov_policy - - def gen_sample(sequence): - num_features = len(dicts) - sample = [list() for i in xrange(num_original_columns)] - if patterns: - sample.append([]) - for features in sequence: - assert len(features) == num_features, \ - "Wrong number of features: " + line - for i in xrange(num_original_columns): - id = dicts[i].get(features[i], -1) - if id != -1: - sample[i].append(id) - elif oov_policy[i] == OOV_POLICY_IGNORE: - sample[i].append(0xffffffff) - elif oov_policy[i] == OOV_POLICY_ERROR: - logger.fatal("Unknown token: %s" % features[i]) - else: - sample[i].append(0) - - if patterns: - dim = 0 - vec = [] - for i in xrange(num_original_columns, num_features): - id = dicts[i].get(features[i], -1) - if id != -1: - vec.append(dim + id) - elif oov_policy[i] == OOV_POLICY_IGNORE: - pass - elif oov_policy[i] == OOV_POLICY_ERROR: - logger.fatal("Unknown token: %s" % features[i]) - else: - vec.ids.append(dim + 0) - - dim += len(dicts[i]) - sample[-1].append(vec) - return sample - - num_features = len(dicts) - f = gzip.open(input_file, 'rb') - - num_sequences = 0 - sequence = [] - for line in f: - line = line.strip() - if not line: - make_features(sequence) - yield gen_sample(sequence) - sequence = [] - num_sequences += 1 - continue - features = line.split(' ') - sequence.append(features) - - f.close() - - logger.info("num_sequences=%s" % num_sequences) diff --git a/v1_api_demo/sequence_tagging/linear_crf.py b/v1_api_demo/sequence_tagging/linear_crf.py deleted file mode 100644 index ea012ba1ae9..00000000000 --- a/v1_api_demo/sequence_tagging/linear_crf.py +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright (c) 2016 PaddlePaddle Authors. All Rights Reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from paddle.trainer_config_helpers import * - -import math - -define_py_data_sources2( - train_list="data/train.list", - test_list="data/test.list", - module="dataprovider", - obj="process") - -batch_size = 1 -settings( - learning_method=MomentumOptimizer(), - batch_size=batch_size, - regularization=L2Regularization(batch_size * 1e-4), - model_average=ModelAverage(0.5), - learning_rate=1e-1, - learning_rate_decay_a=1e-5, - learning_rate_decay_b=0.25, ) - -num_label_types = 23 - - -def get_simd_size(size): - return int(math.ceil(float(size) / 8)) * 8 - - -# Currently, in order to use sparse_update=True, -# the size has to be aligned. -num_label_types = get_simd_size(num_label_types) - -features = data_layer(name="features", size=76328) -word = data_layer(name="word", size=6778) -pos = data_layer(name="pos", size=44) -chunk = data_layer(name="chunk", size=num_label_types) - -crf_input = fc_layer( - input=features, - size=num_label_types, - act=LinearActivation(), - bias_attr=False, - param_attr=ParamAttr( - initial_std=0, sparse_update=True)) - -crf = crf_layer( - input=crf_input, - label=chunk, - param_attr=ParamAttr( - name="crfw", initial_std=0), ) - -crf_decoding = crf_decoding_layer( - size=num_label_types, - input=crf_input, - label=chunk, - param_attr=ParamAttr(name="crfw"), ) - -sum_evaluator( - name="error", - input=crf_decoding, ) - -chunk_evaluator( - name="chunk_f1", - input=crf_decoding, - label=chunk, - chunk_scheme="IOB", - num_chunk_types=11, ) - -inputs(word, pos, chunk, features) -outputs(crf) diff --git a/v1_api_demo/sequence_tagging/readme.md b/v1_api_demo/sequence_tagging/readme.md deleted file mode 100644 index 2e17fffb83c..00000000000 --- a/v1_api_demo/sequence_tagging/readme.md +++ /dev/null @@ -1,45 +0,0 @@ -# Sequence Tagging - -This demo is a sequence model for assigning tags to each token in a sentence. The task is described at CONLL2000 Text Chunking task. - -## Download data -```bash -cd demo/sequence_tagging -./data/get_data.sh -``` - -## Train model -```bash -cd demo/sequence_tagging -./train.sh -``` - -## Model description - -We provide two models. One is a linear CRF model (linear_crf.py) with is equivalent to the one at leon.bottou.org/projects/sgd. The second one is a stacked bidirectional RNN and CRF model (rnn_crf.py). -

}6Qu`2pGC0PaQd6Amn%i&@2OXQ#z*hy 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�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 Date: Sun, 7 Jan 2018 17:52:08 -0800 Subject: [PATCH 0111/2305] Modify inference.cc to run example without pickletools (#7262) --- paddle/inference/inference.cc | 23 ++++++++--------------- python/paddle/v2/fluid/io.py | 5 +++++ 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/paddle/inference/inference.cc b/paddle/inference/inference.cc index 48a51efcd25..49e39358e81 100644 --- a/paddle/inference/inference.cc +++ b/paddle/inference/inference.cc @@ -38,23 +38,16 @@ void InferenceEngine::LoadInferenceModel( LOG(INFO) << "program_desc_str's size: " << program_desc_str.size(); // PicklingTools cannot parse the vector of strings correctly. #else - // program_desc_str - // the inference.model is stored by following python codes: - // inference_program = fluid.io.get_inference_program(predict) - // model_filename = "recognize_digits_mlp.inference.model/inference.model" - // with open(model_filename, "w") as f: - // program_str = inference_program.desc.serialize_to_string() - // f.write(struct.pack('q', len(program_str))) - // f.write(program_str) - std::string model_filename = dirname + "/inference.model"; + std::string model_filename = dirname + "/__model__.dat"; LOG(INFO) << "loading model from " << model_filename; - std::ifstream fs(model_filename, std::ios_base::binary); - int64_t size = 0; - fs.read(reinterpret_cast(&size), sizeof(int64_t)); - LOG(INFO) << "program_desc_str's size: " << size; + std::ifstream inputfs(model_filename, std::ios::in | std::ios::binary); std::string program_desc_str; - program_desc_str.resize(size); - fs.read(&program_desc_str[0], size); + inputfs.seekg(0, std::ios::end); + program_desc_str.resize(inputfs.tellg()); + inputfs.seekg(0, std::ios::beg); + LOG(INFO) << "program_desc_str's size: " << program_desc_str.size(); + inputfs.read(&program_desc_str[0], program_desc_str.size()); + inputfs.close(); #endif program_ = new framework::ProgramDesc(program_desc_str); GenerateLoadProgram(dirname); diff --git a/python/paddle/v2/fluid/io.py b/python/paddle/v2/fluid/io.py index 926327b70c7..c63567601ac 100644 --- a/python/paddle/v2/fluid/io.py +++ b/python/paddle/v2/fluid/io.py @@ -212,6 +212,11 @@ def save_inference_model(dirname, "fetch_var_names": fetch_var_names }, f, -1) + # Save only programDesc of inference_program in binary format + # in another file: __model__.dat + with open(model_file_name + ".dat", "wb") as fp: + fp.write(inference_program.desc.serialize_to_string()) + save_params(executor, dirname, main_program) -- GitLab From 05c08214e33cadac54966250b47b7903d175c404 Mon Sep 17 00:00:00 2001 From: yangyaming Date: Mon, 8 Jan 2018 10:50:40 +0800 Subject: [PATCH 0112/2305] Bug fix when inserting fill_zeros_like_op. --- python/paddle/v2/fluid/backward.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/paddle/v2/fluid/backward.py b/python/paddle/v2/fluid/backward.py index 88fe19da5e2..66a7f737574 100644 --- a/python/paddle/v2/fluid/backward.py +++ b/python/paddle/v2/fluid/backward.py @@ -7,7 +7,7 @@ __all__ = ['append_backward'] def _rename_arg_(op_descs, old_name, new_name, begin_idx=None, end_idx=None): """ - Traverse all ops in op_descs[begin_idx : end_idx], + Traverse all ops in op_descs[begin_idx : end_idx], if any op has inputs/outputs named "old_name", rename it as 'new_name' """ if begin_idx is None: @@ -162,7 +162,7 @@ def _remove_no_grad_branch_(op_descs, no_grad_set): if core.grad_var_suffix() in arg and arg in no_grad_set: to_insert.append((_create_op_desc_("fill_zeros_like", { "X": [_strip_grad_suffix_(arg)] - }, {"Y": [arg]}, {}), idx)) + }, {"Out": [arg]}, {}), idx)) map(lambda p: op_descs.insert(p[1], p[0]), reversed(to_insert)) @@ -182,7 +182,7 @@ def _append_backward_ops_(target, target(Variable): the target variable of forward pass block(Block): the block where forward ops are target_block(Block): the block which is going to hold new generated grad ops - no_grad_dict(dict): + no_grad_dict(dict): key(int) block index val(set) a set of varibale names. These varibales have no gradient grad_to_var(dict)(output argument): @@ -276,8 +276,8 @@ def append_backward(loss, parameter_list=None, no_grad_set=None): loss(Variable): The variable generated by cost function. parameter_list(list): Parameters that need to be updated by optimizer. If None, it means all parameters need to be updated. - no_grad_set(set): Variables that have no gradients in Block 0. - If None, the set will be generated inside the function and + no_grad_set(set): Variables that have no gradients in Block 0. + If None, the set will be generated inside the function and contains all variables with `step_gradient=True` from all blocks. Return: -- GitLab From 691b5cac6164f6db6b421b6c809e6163afdea534 Mon Sep 17 00:00:00 2001 From: Siddharth Goyal Date: Sun, 7 Jan 2018 19:31:14 -0800 Subject: [PATCH 0113/2305] Fix equation for gru op (#7274) --- python/paddle/v2/fluid/layers/nn.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/python/paddle/v2/fluid/layers/nn.py b/python/paddle/v2/fluid/layers/nn.py index cc32a0a19a0..7feb479d2e3 100644 --- a/python/paddle/v2/fluid/layers/nn.py +++ b/python/paddle/v2/fluid/layers/nn.py @@ -243,18 +243,21 @@ def gru_unit(input, r_t & = actGate(xr_{t} + W_r h_{t-1} + b_r) - ch_t & = actNode(xc_t + W_c dot(r_t, h_{t-1}) + b_c) + m_t & = actNode(xm_t + W_c dot(r_t, h_{t-1}) + b_m) - h_t & = dot((1-u_t), ch_{t-1}) + dot(u_t, h_t) + h_t & = dot((1-u_t), m_t) + dot(u_t, h_{t-1}) The inputs of gru unit includes :math:`z_t`, :math:`h_{t-1}`. In terms of the equation above, the :math:`z_t` is split into 3 parts - - :math:`xu_t`, :math:`xr_t` and :math:`xc_t`. This means that in order to + :math:`xu_t`, :math:`xr_t` and :math:`xm_t`. This means that in order to implement a full GRU unit operator for an input, a fully connected layer has to be applied, such that :math:`z_t = W_{fc}x_t`. - This layer has three outputs :math:`h_t`, :math:`dot(r_t, h_{t - 1})` - and concatenation of :math:`u_t`, :math:`r_t` and :math:`ch_t`. + The terms :math:`u_t` and :math:`r_t` represent the update and reset gates + of the GRU cell. Unlike LSTM, GRU has one lesser gate. However, there is + an intermediate candidate hidden output, which is denoted by :math:`m_t`. + This layer has three outputs :math:`h_t`, :math:`dot(r_t, h_{t-1})` + and concatenation of :math:`u_t`, :math:`r_t` and :math:`m_t`. Args: input (Variable): The fc transformed input value of current step. -- GitLab From 8814bec0c5275036e50f9bf12ceb67648419d04f Mon Sep 17 00:00:00 2001 From: emailweixu Date: Sun, 7 Jan 2018 19:54:54 -0800 Subject: [PATCH 0114/2305] Show argument dimensions with operator::DebugStringEx (#7268) This can make it easier to locate error. --- paddle/framework/executor.cc | 2 +- paddle/framework/operator.cc | 19 ++++++++++++++++++- paddle/framework/operator.h | 5 ++++- paddle/operators/net_op.cc | 6 +++--- paddle/operators/net_op.h | 3 ++- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/paddle/framework/executor.cc b/paddle/framework/executor.cc index bf1f0471ccb..844d98916ea 100644 --- a/paddle/framework/executor.cc +++ b/paddle/framework/executor.cc @@ -111,7 +111,7 @@ void Executor::Run(const ProgramDesc& pdesc, Scope* scope, int block_id, for (auto& op_desc : block.AllOps()) { auto op = paddle::framework::OpRegistry::CreateOp(*op_desc); - VLOG(3) << op->DebugString(); + VLOG(3) << op->DebugStringEx(local_scope); op->Run(*local_scope, place_); if (FLAGS_check_nan_inf) { for (auto& vname : op->OutputVars(true)) { diff --git a/paddle/framework/operator.cc b/paddle/framework/operator.cc index b9dcf16da5d..4ef0c2523ca 100644 --- a/paddle/framework/operator.cc +++ b/paddle/framework/operator.cc @@ -73,6 +73,17 @@ void UseALL() { UseCUDNN(); } +static DDim GetDims(const Scope& scope, const std::string& name) { + Variable* var = scope.FindVar(name); + if (var->IsType()) { + return var->Get().dims(); + } else if (var->IsType()) { + return var->Get().GetCompleteDims(); + } else { + return DDim({-1}); + } +} + std::string OperatorBase::Input(const std::string& name) const { auto& ins = Inputs(name); PADDLE_ENFORCE_LE(ins.size(), 1UL, @@ -105,7 +116,7 @@ const std::vector& OperatorBase::Outputs( return it->second; } -std::string OperatorBase::DebugString() const { +std::string OperatorBase::DebugStringEx(const Scope* scope) const { std::stringstream ss; ss << "Op(" << type_ << "), inputs:{"; for (auto it = inputs_.begin(); it != inputs_.end();) { @@ -113,6 +124,9 @@ std::string OperatorBase::DebugString() const { ss << input.first << "["; for (size_t i = 0; i < input.second.size(); ++i) { ss << input.second[i]; + if (scope) { + ss << "(" << GetDims(*scope, input.second[i]) << ")"; + } if (i != input.second.size() - 1) { ss << ", "; } @@ -129,6 +143,9 @@ std::string OperatorBase::DebugString() const { ss << output.first << "["; for (size_t i = 0; i < output.second.size(); ++i) { ss << output.second[i]; + if (scope) { + ss << "(" << GetDims(*scope, output.second[i]) << ")"; + } if (i != output.second.size() - 1) { ss << ", "; } diff --git a/paddle/framework/operator.h b/paddle/framework/operator.h index 1f5a4af58c5..800397c077b 100644 --- a/paddle/framework/operator.h +++ b/paddle/framework/operator.h @@ -108,7 +108,10 @@ class OperatorBase { return boost::get(attrs_.at(name)); } - virtual std::string DebugString() const; + /// if scope is not null, also show dimensions of arguments + virtual std::string DebugStringEx(const Scope* scope) const; + + std::string DebugString() const { return DebugStringEx(nullptr); } /// Net will call this function to Run an op. virtual void Run(const Scope& scope, const platform::Place& place) const = 0; diff --git a/paddle/operators/net_op.cc b/paddle/operators/net_op.cc index 78b5e276784..03302f5cbf5 100644 --- a/paddle/operators/net_op.cc +++ b/paddle/operators/net_op.cc @@ -56,11 +56,11 @@ void NetOp::CompleteAddOp(bool calc) { std::copy(output_set.begin(), output_set.end(), std::back_inserter(outputs)); } -std::string NetOp::DebugString() const { +std::string NetOp::DebugStringEx(const framework::Scope* scope) const { std::ostringstream os; - os << OperatorBase::DebugString() << std::endl; + os << OperatorBase::DebugStringEx(scope) << std::endl; for (auto& op : ops_) { - std::istringstream is(op->DebugString()); + std::istringstream is(op->DebugStringEx(scope)); for (std::string line; std::getline(is, line);) { os << " " << line << std::endl; } diff --git a/paddle/operators/net_op.h b/paddle/operators/net_op.h index 85d0153b32c..b24042f5ef5 100644 --- a/paddle/operators/net_op.h +++ b/paddle/operators/net_op.h @@ -106,7 +106,8 @@ class NetOp : public framework::OperatorBase { void CompleteAddOp(bool calculate = true); - std::string DebugString() const override; + std::string DebugStringEx( + const framework::Scope* scope = nullptr) const override; bool IsNetOp() const override; std::vector OutputVars(bool has_intermediate) const override; -- GitLab From 4d4df084a70276ecb230fed056aaa1b454eaf203 Mon Sep 17 00:00:00 2001 From: ying Date: Mon, 8 Jan 2018 11:55:28 +0800 Subject: [PATCH 0115/2305] follow comments. --- doc/howto/usage/capi/a_simple_example_cn.md | 220 ------------------ .../usage/capi/images/workflow_of_CAPI.png | Bin 0 -> 458577 bytes doc/howto/usage/capi/index_cn.rst | 2 +- doc/howto/usage/capi/workflow_of_capi.md | 120 ++++++++++ 4 files changed, 121 insertions(+), 221 deletions(-) delete mode 100644 doc/howto/usage/capi/a_simple_example_cn.md create mode 100644 doc/howto/usage/capi/images/workflow_of_CAPI.png create mode 100644 doc/howto/usage/capi/workflow_of_capi.md diff --git a/doc/howto/usage/capi/a_simple_example_cn.md b/doc/howto/usage/capi/a_simple_example_cn.md deleted file mode 100644 index abe07a41cfa..00000000000 --- a/doc/howto/usage/capi/a_simple_example_cn.md +++ /dev/null @@ -1,220 +0,0 @@ -## C-API CPU 单线程预测示例 - -这篇文档通过一个最简单的例子:手写数字识别,来介绍 CPU 下单线程使用 PaddlePaddle C-API 开发预测服务,完整代码见[此目录](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/dense/)。 - -### 使用流程 - -使用 C-API 分为:准备预测模型和预测程序开发两部分。 -- 准备预测模型 - 1. 将神经网络模型结构进行序列化。 - - 调用C-API预测时,需要提供序列化之后的网络结构和训练好的模型参数文件。 - 1. 将PaddlePaddle训练出的模型参数文件(多个)合并成一个文件。 - - 神经网络模型结构和训练好的模型将被序列化合并入一个文件。 - - 预测时只需加载这一个文件,便于发布。 - - **注意**:以上两种方式只需选择其一即可。 -- 调用 PaddlePaddle C-API 开发预测序 - 1. 初始化PaddlePaddle运行环境。 - 1. 加载模型。 - 1. 创建神经网络的输入,组织输入数据。 - 1. 进行前向计算,获得计算结果。 - 1. 清理。 - -本文档以手写数字识别任务为例,介绍如何使用 C-API 进行预测,完整代码请查看[此目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)。 - -### 准备预测模型 - -通过在终端执行`python mnist_v2.py` -运行[目录](https://github.com/PaddlePaddle/Paddle/tree/develop/paddle/capi/examples/model_inference/dense)下的 `mnist_v2.py` s可以使用 PaddlePaddle 内置的 [MNIST 数据集](http://yann.lecun.com/exdb/mnist/)进行训练。脚本中的模型定义了一个简单的含有[两个隐层的全连接网络](https://github.com/PaddlePaddle/book/blob/develop/02.recognize_digits/README.cn.md#softmax回归softmax-regression),网络接受一幅图片作为输入,将图片分类到 0 ~ 9 类别标签之一。训练好的模型默认保存在当前运行目录下的`models`目录中。下面,我们将调用 C-API 加载训练好的模型进行预测。 - -1. 序列化神经网络模型配置 - - PaddlePaddle 使用 protobuf 来传输网络配置文件中定义的网络结构和相关参数,在使用 C-API 进行预测时,也需将网络结构使用 protobuf 进行序列化,写入文件中。 - - 调用`paddle.utils.dump_v2_config`中的`dump_v2_config`函数能够将使用 PaddlePaddle V2 API 定义的神经网络结构 dump 到指定文件中。示例代码如下: - - ```python - from paddle.utils.dump_v2_config import dump_v2_config - from mnist_v2 import network - - predict = network(is_infer=True) - dump_v2_config(predict, "trainer_config.bin", True) - ``` - - 对本例,或运行 `python mnist_v2.py --task dump_config`,会对示例中的网络结构进行序列化,并将结果写入当前目录下的`trainer_config.bin`文件中。 - - 当选择使用这种方式调用 C-API 时,如果神经网络有多个可学习参数,请将它们全部放在同一文件夹内,C-API会从指定的目录寻找并加载训练好的模型。 - -2. 合并模型文件(可选) - - 一些情况下为了便于发布,希望能够将序列化后的神经网络结构和训练好的模型参数打包进一个文件,这时可以使用`paddle.utils.merge_model`中的`merge_v2_model`接口对神经网络结构和训练好的参数进行序列化,将序列化结果写入一个文件内,调用C-API时直接只需加载这一个文件。 - - 代码示例如下: - - ```python - from paddle.utils.merge_model import merge_v2_modelss - from mnist_v2 import network - - net = network(is_infer=True) - param_file = "models/params_pass_4.tar" - output_file = "output.paddle.model" - merge_v2_model(net, param_file, output_file) - ``` - 对本例,或者直接运行 `python merge_v2_model.py`,序列化结果将会写入当前目录下的`output.paddle.model`文件中,该文件在调用C-API时,可被直接加载。 - -#### 注意事项 -1. C-API 需要序列化之后神经网络结构,在调用`dump_v2_config`时,参数`binary`必须指定为`True`。 -1. **预测使用的网络结构往往不同于训练**,通常需要去掉网络中的:(1)类别标签层;(2)损失函数层;(3)`evaluator`等,只留下核心计算层,请注意是否需要修改网络结构。 -1. 预测时,可以获取网络中定义的任意多个(大于等于一个)层前向计算的结果,需要哪些层的计算结果作为输出,就将这些层加入一个Python list中,作为调用`dump_v2_config`的第一个参数。 - -### 编写预测代码 - -#### step 1. 初始化PaddlePaddle运行环境 -使用C-API第一步需首先调用[`paddle_init`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/main.h#L27) 初始化PaddlePaddle运行环境。接口接受两个参数:参数的个数和参数。 - -下面的代码片段在初始化PaddlePaddle运行环境时指定不使用GPU: - -```c -// Initalize the PaddlePaddle runtime environment. -char* argv[] = {"--use_gpu=False"}; -CHECK(paddle_init(1, (char**)argv)); -``` - -下面的代码片段在初始化PaddlePaddle运行环境时指定了两个参数:不使用GPU和[使用MKLDNN](https://github.com/PaddlePaddle/Paddle/blob/develop/doc/design/mkl/mkldnn.md): - -```c -char* argv[] = {"--use_gpu=False", "--use_mkldnn=True"}; -CHECK(paddle_init(2, (char**)argv)); -``` - -#### step2. 加载模型 - -这里介绍C-API使用中的一个重要概念:Gradient Machine。概念上,在 PaddlePaddle 内部一个GradientMachine类的对象管理着一组计算层(PaddlePaddle Layers)来完成前向和反向计算,并处理与之相关的所有细节。在调用C-API预测时,只需进行前向计算而无需调用反向计算。这篇文档的之后部分我们会使用`gradient machine`来特指调用PaddlePaddle C-API创建的GradientMachine类的对象。 - -每一个 `gradient machine` 都会管理维护一份训练好的模型,下面是两种最常用的模型加载方式: - -1. 从磁盘加载:这时`gradient machine`会独立拥有一份训练好的模型; -1. 共享自其它`gradient machine`的模型:这种情况多出现在使用多线程预测时,通过多个线程共享同一个模型来减少内存开销。可参考[此示例](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/examples/model_inference/multi_thread/main.c)。 - -下面的代码片段创建 `gradient machine`,并从指定路径加载训练好的模型。 - -```c -// Read the binary configuration file generated by `convert_protobin.sh` -long size; -void* buf = read_config(CONFIG_BIN, &size); - -// Create the gradient machine for inference. -paddle_gradient_machine machine; -CHECK(paddle_gradient_machine_create_for_inference(&machine, buf, (int)size)); - -// Load the trained model. Modify the parameter MODEL_PATH to set the correct -// path of the trained model. -CHECK(paddle_gradient_machine_load_parameter_from_disk(machine, MODEL_PATH)); -``` - -##### 注意事项 -1. 以上代码片段使用“仅序列化神经网络结构”的方式加载模型,需要同时指定模型参数存储的路径。 - - 使用PaddlePaddle V2 API训练,模型中所有可学习参数会被存为一个压缩文件,需要手动进行解压,将它们放在同一目录中,C-API不会直接加载 V2 API 存储的压缩文件。 -1. 如果使用`merge model`方式将神经网络结构和训练好的参数序列化到一个文件,请参考此[示例](https://github.com/PaddlePaddle/Mobile/blob/develop/Demo/linux/paddle_image_recognizer.cpp#L59)。 -1. 加载模型有多种方式,也可以在程序运行过程中再加载另外一个模型。 - -#### step 2. 创建神经网络输入,组织输入数据 - -基本使用概念: -- 在PaddlePaddle内部,神经网络中一个计算层的输入输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输出,每一个输入/输出都会对应有自己的`Argument`。 -- `Argument` 并不真正“存储”数据,而是将输入/输出数据有机地组织在一起。 -- 在`Argument`内部由:1. `Matrix`(二维矩阵,存储浮点类型输入/输出);2. `IVector`(一维数组,**仅用于存储整型值**,多用于自然语言处理任务)来实际存储数据。 - -*注:本文档使用的示例任务手写数字识别不涉及一维整型数组作为输入,因此,本文档仅讨论二维稠密矩阵作为输入的情形。更多输入数据格式请参考输入/输出数据一节的内容。* - -这篇文档的之后部分会使用`argument`来**特指** PaddlePaddle C-API中神经网络的一个输入/输出,使用`paddle_matrix`**特指**`argument`中用于存储数据的`Matrix`类的对象。 - -于是,在组织神经网络输入,获取输出时,需要思考完成以下工作: -1. 为每一个输入/输出创建`argument`; -1. 为每一个`argument`创建`paddle_matrix`来存储数据; - -与输入不同的是,输出`argument`的`paddle_matrix`变量并不需在使用C-API时为之分配存储空间。PaddlePaddle内部,神经网络进行前向计算时会自己分配/管理每个计算层的存储空间;这些细节C-API会代为处理,只需在概念上理解,并按照约定调用相关的 C-API 接口即可。 - -下面是示例代码片段。在这段代码中,生成了一条随机输入数据作为测试样本。 -```c -// Inputs and outputs of the network are organized as paddle_arguments object -// in C-API. In the comments below, "argument" specifically means one input of -// the neural network in PaddlePaddle C-API. -paddle_arguments in_args = paddle_arguments_create_none(); - -// There is only one data layer in this demo MNIST network, invoke this -// function to create one argument. -CHECK(paddle_arguments_resize(in_args, 1)); - -// Each argument needs one matrix or one ivector (integer vector, for sparse -// index input, usually used in NLP task) to holds the real input data. -// In the comments below, "matrix" specifically means the object needed by -// argument to hold the data. Here we create the matrix for the above created -// agument to store the testing samples. -paddle_matrix mat = - paddle_matrix_create(/* height = batch size */ 1, - /* width = dimensionality of the data layer */ 784, - /* whether to use GPU */ false); - -paddle_real* array; -// Get the pointer pointing to the start address of the first row of the -// created matrix. -CHECK(paddle_matrix_get_row(mat, 0, &array)); - -// Fill the matrix with a randomly generated test sample. -srand(time(0)); -for (int i = 0; i < 784; ++i) { - array[i] = rand() / ((float)RAND_MAX); -} - -// Assign the matrix to the argument. -CHECK(paddle_arguments_set_value(in_args, 0, mat)); -``` - -#### step 3. 前向计算 - -完成上述准备之后,通过调用 `paddle_gradient_machine_forward` 接口完成神经网络的前向计算。 -示例代码片段如下: - -```c -// Create the output argument. -paddle_arguments out_args = paddle_arguments_create_none(); - -// Invoke the forward computation. -CHECK(paddle_gradient_machine_forward(machine, - in_args, - out_args, - s/* is train taks or not */ false)); - -// Create the matrix to hold the forward result of the neural network. -paddle_matrix prob = paddle_matrix_create_none(); -// Access the matrix of the output argument, the predicted result is stored in -// which. -CHECK(paddle_arguments_get_value(out_args, 0, prob)); - -uint64_t height; -uint64_t width; -CHECK(paddle_matrix_get_shape(prob, &height, &width)); -CHECK(paddle_matrix_get_row(prob, 0, &array)); - -printf("Prob: \n"); -for (int i = 0; i < height * width; ++i) { - printf("%.4f ", array[i]); - if ((i + 1) % width == 0) { - printf("\n"); - } -} -printf("\n"); -``` - -#### step 4. 清理 - -结束预测之后,对使用的中间变量和资源进行清理和释放: - -```c -// The cleaning up. -CHECK(paddle_matrix_destroy(prob)); -CHECK(paddle_arguments_destroy(out_args)); -CHECK(paddle_matrix_destroy(mat)); -CHECK(paddle_arguments_destroy(in_args)); -CHECK(paddle_gradient_machine_destroy(machine)); -``` diff --git a/doc/howto/usage/capi/images/workflow_of_CAPI.png b/doc/howto/usage/capi/images/workflow_of_CAPI.png new file mode 100644 index 0000000000000000000000000000000000000000..a4399ade048b3fe10d2d9c714bc34333ca068edb GIT binary patch literal 458577 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%0AB_yYkM|QgLFjtF@ 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ĝ#t*Ihu>tZ6&av!=m9^jgA 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·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 z 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

R1xT9sYusp922Rl{?RsvXZcj8_wPv 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 SkNySx9!NjDmv`6b`Tqds0oX49 literal 0 HcmV?d00001 diff --git a/doc/howto/usage/capi/organization_of_the_inputs.md b/doc/howto/usage/capi/organization_of_the_inputs.md index b306c522e60..1e573618a6b 100644 --- a/doc/howto/usage/capi/organization_of_the_inputs.md +++ b/doc/howto/usage/capi/organization_of_the_inputs.md @@ -13,40 +13,22 @@ 1. 一维数组**仅支持整型值**; - 常用于自然语言处理任务,例如:表示词语在词典中的序号; - 分类任务中类别标签; - 1. 逻辑上高于二维的数据(例如含有多个通道的图片,视频等)在程序实现中都会转化为二维矩阵,转化方法在相应的领域都有通用解决方案,需要使用者自己了解相关的转化表示方法; - 1. 二维矩阵可以表示行向量和列向量,任何时候,如果需要浮点型数组(向量)时,都应使用C-API中的矩阵来表示,而不是C-API中的一维数组。 - -不论是一维整型数组还是二维浮点数矩阵,**为它们附加上序列信息,将变成序列输入。PaddlePaddle 会通过判数据是否附带有序列信息来判断一个向量/矩阵是否是一个序列**。关于什么是“序列信息”,下文会进行详细地介绍。 - -PaddlePaddle 支持两种序列类型: -1. 单层序列 - - 序列中的每一个元素是非序列,是进行计算的基本单位,不可再进行拆分。 - - 例如:自然语言中的句子是一个序列,序列中的元素是词语; -1. 双层序列 - - 序列中的每一个元素又是一个序列。 - - 例如:自然语言中的段落是一个双层序列;段落是有句子构成的序列;句子是由词语构成的序列。 - - 双层序列在处理长序列的任务或是构建层级模型时会发挥作用。 + 1. 逻辑上高于二维的数据(例如含有多个通道的图片,视频等)在程序实现中都会转化为二维矩阵,转化方法在相应的领域都有通用解决方案,需要使用者自己了解并完成转化; + 1. 二维矩阵可以表示行向量和列向量,任何时候如果需要浮点型数组(向量),都应使用C-API中的矩阵来表示,而不是C-API中的一维数组。 + 1. 不论是一维整型数组还是二维浮点数矩阵,**为它们附加上序列信息将变成序列输入。PaddlePaddle 会通过判数据是否附带有序列信息来判断一个向量/矩阵是否是一个序列**。当非序列输入时,无需关心和处理序列信息。关于什么是“序列信息”,下文会详细进行介绍。 ### 基本使用概念 - 在PaddlePaddle内部,神经网络中一个计算层的输入/输出被组织为一个 `Argument` 结构体,如果神经网络有多个输入或者多个输入,每一个输入/输入都会对应有自己的`Argument`。 - `Argument` 并不真正“存储”数据,而是将输入/输出信息有机地组织在一起。 -- 在`Argument`内部由`IVector`(对应着上文提到的一维整型数组)和`Matrix`(对应着上文提到的二维浮点型矩阵)来实际存储数据;由 `sequence start position` (下文详细解释) 来记录输入/输出的序列信息。 +- 在`Argument`内部由`IVector`(对应着上文提到的一维整型数组)和`Matrix`(对应着上文提到的二维浮点型矩阵)来实际存储数据;由 `Sequence Start Positions` (下文详细解释) 来描述输入/输出的序列信息。 -**注意:这篇文档之后部分将会统一使用`argument`来特指PaddlePaddle中神经网络计算层一个输入/输出数据;使用`ivector`来特指PaddlePaddle中的一维整型数组;使用`matrix`来特指PaddlePaddle中的二维浮点型矩阵;使用`sequence_start_position`来特指PaddlePaddle中的序列信息。** +- **注**: + 1. 这篇文档之后部分将会统一使用`argument`来特指PaddlePaddle中神经网络计算层一个输入/输出数据。 + 2. 使用`paddle_ivector`来特指PaddlePaddle中的一维整型数组。 + 3. 使用`paddle_matrix`来特指PaddlePaddle中的二维浮点型矩阵。 -于是,在组织神经网络输入时,需要思考完成以下工作: -1. 为每一个输入/输出创建`argument`。 - - C-API 中操作`argument`的接口请查看[argument.h](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/arguments.h)。 -1. 为每一个`argument`创建`matrix`或者`ivector`来存储数据。 - - C-API 中操作`ivector`的接口请查看 [vector.h](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/vector.h)。 - - C-API 中操作`matrix`的接口请查看[matrix.h](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/matrix.h)。 -1. 如果输入是序列数据,需要创建并填写`sequence_start_position`信息。 - - 通过调用 [`paddle_arguments_set_sequence_start_pos`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/arguments.h#L137) 来为一个`argument`添加序列信息; - - 通过调用 [`paddle_arguments_get_sequence_start_pos`](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/arguments.h#L150) 来读取一个`argument`添加序列信息; - - 接口说明请查看 [argument.h](https://github.com/PaddlePaddle/Paddle/blob/develop/paddle/capi/arguments.h) 文件。 - -### 组织非序列数据 +### 组织输入数据 - 一维整型数组 概念上可以将`paddle_ivector`理解为一个一维的整型数组,通常用于表示离散的类别标签,或是在自然语言处理任务中表示词语在字典中的序号。下面的代码片段创建了含有三个元素`1`、`2`、`3`的`paddle_ivector`。 @@ -56,6 +38,7 @@ PaddlePaddle 支持两种序列类型: paddle_ivector_create(ids, sizeof(ids) / sizeof(int), false, false); CHECK(paddle_arguments_set_ids(in_args, 0, ids_array)); ``` + - **稠密矩阵** - 一个$m×n$的稠密矩阵是一个由$m$行$n$列元素排列成的矩形阵列,矩阵里的元素是浮点数。对神经网络来说,矩阵的高度$m$是一次预测接受的样本数目,宽度$n$是神经网络定义时,`paddle.layer.data`的`size`。 - 下面的代码片段创建了一个高度为1,宽度为`layer_size`的稠密矩阵,矩阵中每个元素的值随机生成。 @@ -83,17 +66,20 @@ PaddlePaddle 支持两种序列类型: - **稀疏矩阵** - PaddlePaddle C-API 中 稀疏矩阵使用[CSR(Compressed Sparse Row Format)](https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_(CSR,_CRS_or_Yale_format))格式存储。下图是CSR存储稀疏矩阵的示意图,在CSR表示方式中,通过(1)行偏移;(2)列号;(3)值;来决定矩阵的内容。 + PaddlePaddle C-API 中 稀疏矩阵使用[CSR(Compressed Sparse Row Format)](https://en.wikipedia.org/wiki/Sparse_matrix#Compressed_sparse_row_(CSR,_CRS_or_Yale_format))格式存储。下图是CSR存储稀疏矩阵的示意图。

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�` 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 Pr_}FiE0^7|dHnwXQoS8< literal 174346 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 z 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