From 9afecf50749301451934e9b6956e7ebc77365f77 Mon Sep 17 00:00:00 2001 From: Daniel Palenicek Date: Fri, 3 May 2024 11:50:18 +0200 Subject: [PATCH 01/32] Implemented CrossQ --- README.md | 1 + docs/images/crossQ_performance.png | Bin 0 -> 266220 bytes docs/index.rst | 1 + docs/modules/crossq.rst | 99 ++++++ sb3_contrib/__init__.py | 2 + sb3_contrib/common/network_layers.py | 98 ++++++ sb3_contrib/crossq/__init__.py | 4 + sb3_contrib/crossq/crossq.py | 330 ++++++++++++++++++++ sb3_contrib/crossq/policies.py | 450 +++++++++++++++++++++++++++ tests/test_run.py | 12 +- 10 files changed, 996 insertions(+), 1 deletion(-) create mode 100644 docs/images/crossQ_performance.png create mode 100644 docs/modules/crossq.rst create mode 100644 sb3_contrib/common/network_layers.py create mode 100644 sb3_contrib/crossq/__init__.py create mode 100644 sb3_contrib/crossq/crossq.py create mode 100644 sb3_contrib/crossq/policies.py diff --git a/README.md b/README.md index f54ae854..5b0503ba 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ See documentation for the full list of included features. - [PPO with recurrent policy (RecurrentPPO aka PPO LSTM)](https://ppo-details.cleanrl.dev//2021/11/05/ppo-implementation-details/) - [Truncated Quantile Critics (TQC)](https://arxiv.org/abs/2005.04269) - [Trust Region Policy Optimization (TRPO)](https://arxiv.org/abs/1502.05477) +- [Batch Normalization in Deep Reinforcement Learning (CrossQ)](https://openreview.net/forum?id=PczQtTsTIX) **Gym Wrappers**: - [Time Feature Wrapper](https://arxiv.org/abs/1712.00378) diff --git a/docs/images/crossQ_performance.png b/docs/images/crossQ_performance.png new file mode 100644 index 0000000000000000000000000000000000000000..2d78d93ce8a4466954513f2e66cda094bbda6f21 GIT binary patch literal 266220 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Í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≥Pl)&1|)x}1d=n)wU5AsPrBXe_h5(WDHH3k$viRol7;1g$j4XmT= z=C=(D%wZ&4-4FU$joz;Gb$zGLMRh5Mibzj2u8PJ49(8C_M^i_KZJ!1ds5BV2vsXJ* zlV_Rzk^(FskC5HGV^ElU1IO~Zy0`&v))J3LAhFEFlFSg_`6wUu5)>H>ogl)uEAfD| zXkdyK=mX{6!_G8}BvLdAZ@tU4Evqr{u$BmrKqPa-osd$1&0)e>@6??nE=LALP()Jz%Q#r z#vDfqXJ(E?l1H=i3*Rsy^kpJ-vb}@F4&xKaamcQVry$9|Co59IupfT7mK9@i*Si)B z;+-bTTFTlH=YK&)_`bd#|I_^0_xZi3Z4eRu?H15;>;MKSyLp|*d5}WxNo^T8S=PkB zN!GDF=G*}2OkfZUjI~YY$A@6z9zjH|@lelV%L@=o*~ z-g%Vd>rU-?Aw<4Y?m2eLlzF>(y7nH%1!Xx;auNF8;@URdOrZ;z5MuThb4~RIb+p&S z(^Rb=LI%_f3>c6Bn;q8XEt-0z{I|4^t>dhdsi}?accCCCgG~_gTFVmSSOX=+*E#e1Nq|fPhDvjmY{3byI%qX z>ltSbdoC#|-KZ?&8}@gfw7wh2!`VCifnfEK%5P+}RJL(O_F^n5j65!=B&9 zWn+WEZ2$i3ZdeRM_PPmarVp>4NLSgX`PZqn|134|(D4v)Y&2fYjdse-M&_8}tay4q z+6=xq_F*UDc>!iAi1)ied{CWQ$;j&wN1d8Ur-yj~)q~IG52Zs^s6wm;JGM7+KoxTZ z%lv7Ym=oO?h(^!#)m)y`_<){;hw-oP{|y=+=T4cWF3U0J`yYg_qP8Mzlv}8?@0E8N zaR?4aqr$dNa_EMgdGb;ROhJ+esh0~axy+N4c z5;d|PIfFweoG43(%M_CuC04dUXr|&sD?i34)d*LVlU)tO{%%o4vHX=x-&UaRBM#S% zENIhSmvBKJ1cuVJRNLg*(|@ruQbj9^-OP0>L5oD&^~&?V#)~GcJXkv{jp=VDT+}4| zyxgeqC#z>^<<~&@#GXaV%eQ3Oe|^gqrL%4mzfb=Rcb@IANbcH*moEZY@N_-T$oK(E z8$CE#w-=?LaE^TY=$(6#VO1z;51rupJ!;>RxIf&oHi{lq22i{o;!yUL`K7 zc)oTjz2bdb{0>JXKHWQmAbg|csAK*X5e(0H^jNcI5^puWJ=zLbC+L#zW8Q;Lb<3dF zVaj{Rk`}@E8odRtne?JBmpSCg8a%&zDt>o*%iqFMv-d~Wg>!@P`K3u_gIx+LV$r?b zTm`091_w$dNmR+W^9Ue&$A5^Dw^E-wG{$@&YAZbIxepfcQ4|lw(`(1KE*E5Wpv^xS z_35;X4-pGw1+$WW2UvjFP?65B8y*38P)6H&<1rh^2|oIc#_qi381ZX6VZ0|iYW;G} z*@kv2-JMgHyy^qBYI0`|;oN@ku}Ge?xMP&1BW_HX;|})RqpEzwdKm2xw0?^~)OoBH=|Qd(=|t|y z<>*mA&rJ+t{KIrXIDZ^qe4&80Xo zF)=9A_(en-7i=6tB2834J!5(ckrMa8oVw2>ckAV=4Q57sNlxq)nd8nM`yHQtQk|A= z66XlU5q>7+iz9rFghxh)+2o#gtR}1DEv4WHmklr7??I^(3|#4Y-x-q~k`#tY122dS zKZfzSqT^#Z(qy_X)&=CaIWf&e)ps0eoh=36xU7r3@;z>>IeG(fF_;U=vMUeC>*HE=93E_dU9+pFikiQgk-KS9K$QZZHM42hhV#wC+xz*@xbDEK-qF>-4b_+1p7L*QV>nJf98X z2X;OC`uM#c371)i8I6&)B-!VFli4F>j!W>FzxTHY@%4kQJ5u{A{jW(&N;hoo~@& z#rPck6&!`Cx-i|E$e+7+Rc+;Wgnb~^Aa1Y z&|3#WZXn&VXw$@d97%fKJMG>CcE&hvDJHOza{n{`<~5EhLo#=D}Sz#KVv8aJMVX_uu+Ri)4RoC7U~$X!=(#R{psozcci-W49RnwCo7v!7g(< z(V{yH7u})1pbG1SQ*Q7Q-Tbf9&dbwdFo5qn2B0i1pS#&umVy`?u2GXa=>Zn<>bTr# z_9!du4LGYz_i zs)%Wjvjup}fW3J8ew zO-+d{EiHi>=e0{XN5Y#)7jAA|K^YnCDueP7*Y~D1WA04?BzS|uG<3Ak{KA>jcMe_~ z1yYqVH8BLHPSgXo*1S>5rPqsX7kR^E35_#*OYUp^)z+5oO39&-~9+T zF1+-e-{3(0-JI|Z??=?M;m&^h2E6_K&59lSPeY%6>Hny!N`UrJa&epQXb*i>7f(e) zGsyzuBn&syAyLq0ep5SyYfi0(%eJPfW&#V6rCYr&mra;WH1u_7bvulo%(z>4scTnP zH-=DQk3&A|10U~tOHyV``e&~lp7>$@81Lq(+-O5da#)diEG0g*ZX_lCs@k45d9=cq#E$$T=Hkz4ok1K%u$?IhTrB?DkU#USA;XCq3)+) zDp)00Da;>xUuY+ju`X#)D-H0o(#OpFIi;Yzn#o{EaIX<#6(nt36HlQe))K~KU1UtH zEEpX@q#%##)@Cl~UiMMRjsCH3_=7FUROEKp<2$&iX!$Tz+iCq@8HA-|#N#kg#@I6o zK_kOGgg&)AwH!Hf1Wv|Thau*bv+I1)8-t~JV`guMy*mB90~(#cm>GH zJDkj@<>FX%n2*^F1c?ypE_zF(48B%;%C>ygNZ$Zq?9u=sf|o+UA5)6lr&@IzANKwOHIm$l z)RJQWO_@2o7-J!Ikq|e<^*T+1wwoiaL^(=UHHx(n6K41k|Gg^yH^t|PzWJZ{32)$8 zOk#cS^3^h_CYLMS)>L-<;-MLmkwMuHK170#5d!5wC3ca2pzY1w3cHop=P%#aYmVlBI#)ZF`iYfVFiIMb#e2(AnMo0 zA>0P{!#nheIG4UrT*p2|I>pA^BmNZh*Y7p?fMLR84(BXVoJD$xB(|h}%3Hb0rF4qX zT70o$b0p?2%;o zE0;$ZAYoyz=&bhg;Lws(pX*b(&(9y9*W_1Qv>J@Ji2FLWSt3_Mn=tXFl=3=Y!;+2W zBF%nmGbI+di{#T#xZ|g`TJ3;M>kD+NdSy3KAjM*g_o|@?QIdyE78jG+Z15uTyInc@3}8 ztD4Ml6n-52#EZv`@vVpk_bp}_Sw*jUcXIql zx#yS`d^uH)5-}IK(x~QC!pEB0A86|hk0@vb$=pu~*!=DmIZZ{U#3V7RVUvl~;y98< z$Io!2NUY=Y`Q;UvR0&|rWE`4|OQWfb)~8n^V|rCJyX&{a5@pE3qhgdyD592cKPU_k zc+70)t0lq2RzHO+H}eGR-Kx3I=lGG2c?GAXDiG|yhxS-<#knaGx)?ZNVdV-K;V z#+_Qat(RBWxjNsf%dO+xhe5I#6r?LplS3O;C4l zpY8><`w`n$Z`E{JBVBqeC;b4tW!4&#m^$>dDLj6EShMpKWO{Z+KQ{c7Ra8(C460H| zW3EQ?hc*lsiaVKbIBymTeIM*Z4syo^W8fTK_p|NSsrk0)c;`C^u>wumb4+j>dh0LW zq9moHA-Y<2Jx^Gxw4VhGmh*{ z8|hkwu>?i{sdy$ZnPK08kh8hUNqNw&@X)fQeU04^XT=lc&O z&=LR@Zepw9^e~6)Qv^J(ze@`_YAwM2gJpS|3%z;-k4k=UWQF z1Vxl4+>x;eIYu@sYm*2kCSN?wJJj;=@fMu1Ol-=m0^K+~*s{kJ4RYl9yRWz+WgW#+ zs;iDhbBE$APJS`l&!l+Oe&t}^k$ibuDpqUq+ zGwU?i-oY#AG!`bk^5F4ibKc&A((se%TlLnHui>WznhLbEge>lK?+nFQ7oEBq1yqaO z%M@7;ZWpD~Z|$GVZ$&F3!(3=8P5TV>u1M~mBxYu&-b9}TOg07HX1*8_zM% z`1QtBAqORH=yTQ42Wqs}osA4E(rETPXGxs+b$GKjP!W1wqBlrrxqVx5mW(r%igZ3Q zZA$7@MjKQ{Rc`>08NgOXDE0VfhojgaMq<$$)anG=iNO|0OT!!cC(>Q*=G$@Py{*Qk zkHZd=FIJNS*3>P&(t(ns?w*9#B7RNacbDqZ)d?Us$p_x5*eJf>u+F3S&R>Zh!8Vt* zfJjv`11N!Qobfn=AGLr_T*k}1KYC0PhX~MfDBmhYPj_+O-MPv~D6DD1Hi|6XdVTlv zbXTW&jnDcIPD)BjN0B@PYNqRuh8%Hi-e;>g3AQBef;_6|Au`F37bRxaWr zA10`*#t>X>H{+T%+%UAO8SBJ#fo(F z@52CQH+Y$aVIlM^`Le6<{AXdU1FGP`>|VkKHEP$f2@NPb7d@OL-C~~w?z)8I$Pjwj zMPBnNdc&v)On0@C=D>mvc~6HrH(SJUj58J>?)DN z=)4*vXpWnUdxJt{8ktE}@$#8;IxVSL+Gi^()i)c2oV5>mI`{~gu-oI|q%4HPcnsaQ zh7k-rJ5R1BGM=OirMQv(yqRLNlm+|P-ThGw+k5ehJ>~5=vuj06r4xsl3K1FAutxFg zl2RPQ< zB^2h>X}B_xT18SuJI1m_Q$|ZxPAX(oe2+{}LX5HU-@qN~Y2wBm{4B~NFH{h6z2ZG0 zxfs6OO<02N-Sih|F{{^%!J$&H%~ecNSe?Q?wKKwc2#^R!-2LWHhsgg{vf2vdSDoh{ z8hKcjat5Y_gDl5Q9fe9OHVZISZ|^LgULZ%uGT>mtbV$h>8){7Mcih?v+O2CSe2~w8 z?1#B+T_p~s0hQZcyJ;`Y7)l?&2M2-CRyq~>yvI^wjw8X+p_vrjmg#?f1q-{4P2-|B z>=MnlNsJ$s8hvP~K8(*PCm3o4AcD`1WR1*Fc_zb(ci@-^&~t==BIfdLHKr!G#Pcx# zZqIrxmZ2U@PImS$3DAHzJq*RhTP(i_S$$z>X!u%(162HN3oU!|QAaP%Aa=(xMNI$! z)D6&uVY_9{x`RPw^BWA15-&Xd=D<6B&DM>p$KImu=j2=r=rMh^v?%S{TqCk=DHFkq zpt|WqFw}L&(>co0h!SLxd?57;9C2c<$h&XA@1?ukviX?K8jpq+z$mP@5RWl-SeVNEnn9XfuBqEvLh^ zkQH~dBG?Y6>C|WD$avYyoNHIK_8?`BnQ-JYjuatTM=~XTZ46mt4C952T+F;Xi&@sp z6i2Ij`%3Xp_avQ1KsVhHF!Iw9t*t$R`gIU1XKugxDHiJ1R~DQ$Hnk^$9`VI)=miR* zlT=i2LQO^hfjj~n*MiECvxEoFLHmLS*4U-0Cg*znrTa6 z?VS`+P4|*~Og=2N_O&XjuHbt4$N17&#E1dgpGK~Zs#1C7TAV5svSY0*`6h*qarVhI z%La6IzKDHm#JIbaZwt1Woq}Xi@i~`atmU|0X?6c8tNasM>}3YBe=FYFge7sCMAs0(G&>@E9&!-{U+ux!y{p*~gioGn-=S`POcY{*49%>Y<@;OzlGV)pXEdN%7L5dSo50~72a7UT! zeA>j}dB?ddSEM_8u0Gg4JPPZxc|#0(VIJ+z3ji=(Yd9lVBwAIKO3AH`Y0Q8{c{jI} zh5t3aHIpbA`oN96+77_ThTNd^KS30n{6&(@JK^Mz7Bej58$U4TuHXf|!V_KeitWO& z^3vPuZ4l?@U$2X9{zw^0z5fD(t<--+ip%qD3{@zW3X3R&j-A?ML;`dKWwkA{J*lk7 z`lfDi0tcq-=bde!ql8fj-BSq!{^+3VqK#PB%i2?LYcR&VGYH{@Ta2U+Hi-MTzufPK zG5Ct#1t!=h_kY&&ohWsts#f?Gng^FwRyCXc30K~5az-6b-^RbvV4Qe-9C#Bnx`9sM zJ1`stO9nCj}*cxSThquar9vHoFipD`a~?8#XcSTrWNGh@7(vy%FG6Z z{$~d%mod!31pECTlei$>gf>@TI{#+WEN9dNuh=S97~L+?S(49^lwX5{uTKuIHGaq zJO6tUyjlKX-@BS)BdSD3Y;I}rKIw3{brchqYO&jAoD%c#94P6e{qadp=+d}XHt|&am zw+we*g&>_9rujx&X`r_1Fjaa4W}4z$eKWx*O%#|R@C$-T;a%bX15ExWk36=O>9+&I6ekZF-ynf8Rs{W`7B)!d|; zdLp*e$DXg#@+EvE=-%pD#~r2muq60i^oasW*(ruys`2HhIp5k0f1{SD?8H>k+~ETtECyahQUZ9b^33 zsGQJnJ!;a?^4kzx(kJ?Jju5$4VOL8`cszi@?mQ&zt%_8Q8?4A!bXvPN!J`}mQn6SU z^qV5CW52W85G61H9tULp$<>-APrLayiJOs52fe(8*Mrq89MB@bl<-tqKBj{|kMh+$ za(|w|b*k5}-a22aI)lo67H7SYDgJ~GoLuEr|5h_!vw7cz+QgU2?~sdbTmwk5?&=LV zHzi;dAayuE9xuw;bF)LbK~gn2Z~7|I>0(m8sL6rLDcYlaZ0*`t;9pl$>+>l5?y zU5X$**#6ch0P^W`xdlq_ica>_oIv@*C06D_1?n^4?s!~ob*?~d!?!~nz{2>nqG*x@ zNK_brhGl8XBUFMfXe9nfTeRf>4f%+UV&pO`B$N{Pf@Vi(BG%tEF&*wl8oXet;^h(tEu$YD> zY|W`B>F%;e2aSz0k((>YBXGu5kUp-gJaEMl<*%KRj5{ZQ9>RyVZ#$#6OVrg#_;`e* zWl)N@{Ujbq(3Lxz6O>lpAtzEyil}#EMk`PIZ{Bh;Q62oVongb`c5P+q0ZJR+tc(u7 z;6)O%gAuiSPsKPM=b+Pi!_DkZ#1=>L$`)tV7h|E%7e zouFU2{E8lMT**ZO@6EaPfGB0Jc0X_p-<$sqOClp5IgvwRAk*-KU#|QxC9X^@qo-Pr zsb)NJ6lk%+W7OC%SIl7bJ-D)pWRXm{@HZUHuyz>QEy17e`|hIS*>a#48E0(8BC(#? z9cpB(JN4@|ue}btaI4ojlr2RzWQ9HkAD_QrWBdJA@OKW)w;Y;RdgTL!(ury3Qq|0( z!nTWHbZg@9G1A5;Z*RrSsCw>VTZKFI>xqgw`h*lJ>&Q%^OpGpou=p^GTa-*UA~>V0Zw`1RtdUM;VpEz2^Vq<8=&xV>8bd|CTm# zz-bmao5^b5v^(3=z7z&L;6+dlv)tD%z+Y7R+An=jE0*|2e5La&>+&;{;eu&naySqn z%EpLNW=}z;2jYW6lK)*`5EL-hZ)Q^EEmYH*J~{6=<9MEO!dDg!u|#f7RZu82p|?~9 z0T57YXd4hf&B>N&=QgnWX51OlltQdJJ#DF8z-|)rTI`va=Agd1IZH>i2P9NIz$F1N zQWc*aGT+&DCNRzPwuA|Ybbk8&;->d*k;0~wZ+1lvO&GNsUV)rDZmuXj*!o(*E(;NBHVF4}~iQ zzO)oy(!rpUvFWe_-b}j-)#YjXA$9~69DRA3d^u2LtHFOGf=cSJ`E1I?yn7bIF~M9C zV_zco%;`D>K4BSun)5Dc=yr#`@3?qK9E_+8NKV zR*6eyyXPo>|0q8!Kw{jIl&7&1tl2H38En?C&3rX%YDf{5!0}m?`}Q{uW1f1VE3t*I zkWQfWH|D;y7qlvG8O@}eO}>>XC|yX+o@-M>Xd4{{bFVXc@PMFU$|#RVKn$+ z)#^oPJKSuYIOityhRKrY4XgX++xNjBxyz3=pyF|* z?=C@G@J$jtJpQOp`b5N@rL2W#BCKD;=#*5MhDq=gIJw&IDYW0qSl-1T3L6}}34Arv zkW0gWyhM4Oj4$%$o?fYw*VQ=WLN2@6V6j8)UMf1Q+9rxgi&W=tTS(`VG?DaHGot*P z%p@r(ikz{ew7KlJZ>g79yms*P2EZROUigcx_`S3qqmgZK%R$1r5k|Pq09AEsth;jG zIBViG$nMXvq7WU==xNHnDIwtPfzf*>G-6KrT<-$*rW+Am&*?SC0EPpa5RV)5uQF&? z9ad-Fe^Pr~0@ifPoADHgc-cx))htdg6Y?tb za{E#W@LM}O)-NVI4jRrTJkBXQPbxdVO3WW?T`VwMEO0gOFDZlBCLpW;1(8gk_Lw_d zSG1iF@DD-v^$-rb*47ZxvxZPtSAX@$CmJX`JlFS}Zr&5`xhOAK{I)h`a(D&C_Ho|; zjFS&&JwFIQw?56#lsE2i7}SZgZvEafLocEI$`X~rGUL1K_(4W~g-FGp^AjH>7RENy zg%F^`KVvOcaWs|*GO)25eZyA#Zq-kq^=_oTBdp-z^&64OwT7j}RmF5v9RaQBgN$Rs zf#-+8w`wj6wu^qcFC9x~z^`wZlGVc&Cf9#x<;x%6TQMyNE_%K&MOai7bg#$SHC>TV zIrdFus#>#qE_LH<4L?IP*{oKceR~q-fV#p!V8~CGNtpk}uwPJ$?U&twD}MH%VVPnPvX<&NM1PEr$L*@j}~U6n?0 zk&ucX76H3yy-mAHmhSjvpqWW}B;A1_I|^wdxbnvE9Yap`YeU|p4RK90GG$PAaODkV z_thR^JqmZQyZc9U^s1?A7b^1Aw%*phXJ!`X!#r}d8?1^Fp!m-1S`Fsq!&}zb?8Q2t z3bS838G#+8WVU_EVi9Ng5#UA3%dFa(8aqFF@V}e>NtGV6A1*81Y?=BppY1>`?3TC6 zMaXQt$WU|tnI`97xlg{8y$AO3hz7eqZ^mhe&UUp*SuBw{zYNPhW8twaZexO)nIK6L z&y0G){da+X`SFuKC3d!^O_5ep$Vth% zH<7$bSGKE|N~d~v=chnzR0h9}Zmc7yNbnONi2zIY((FbMPWj|e9w%!+m6s909K&fX z!xE%tYFaj&++bL}FLSIWiLwa}K6}?6f~5*gf$EkKjo_i^Xous=X8x86hCDbS} zV1tfn*9d5)S=SCsO&QK_+w%a-0=V0R1EsqU*biE$Z=NT6PWGr*OjFDrQwwIT)GdeR zuOzk=JNFIFW?P*SlgFs=^r; z+{CIo%9NA?W<^|+u$U{@#^)v+i&Vr%QDVjFnS*awflYjUNTjY77Yw(amFRxr@QCnT zvq@5Ug-KLl2aTvKnNlhv$*Nx~C~k}s#h1Ffm)#_iUZ;LwIEESDm2S4sK2a09$($H8 zek0v3+|5JvHK)aD_>V)ZgM(2)p$Z>4^;|nKi^XfhDo_*Dx>a7DY9bUP7+DF=Lc)m3 z7ORK-@Wv8|^{AuCR2mP1gxCG(Q8w-&1wwGahvKyHZ53wm$f~&28^MLmKaDoAUeQP- zpk$}V^@`QBOe?b1e~*BfWq{ujPgj$;~{Q!E)J7g@xJ)B^0HCW zk@^TmwjYF**C~1qdg$RgV>R(=_FBcPW_UI?@WQ2$sKne2|>H`(v3k-h|QHlsg5fJBX>7KE1{K z@W%B>yd}^6Mk5Z&ommO=$5fZ;KKW80vh;U1-bOtdwoRp6Yczq;|=*1Y+jf{Qz z9?IwDXH%}hB23`5aL#dt2T`O)9my$%K{?1+JEJV6ZyI>^(_11|)CVO`#}=(8i;-SD z4w*F3UBkmVGU}j}y)P6w+AK?3Wx`GAF;HwoW0LHYt6w>Ou?++0fh4v`_4K1LbaY?< zE&d21e_vkAU*C4)P)6FgVQX_ct0K{_((8sW7aJ^xs?B z>58a4ixJi2rJ`b^qf-Fi*|Dqj7ZoD%`@85JceL_T+el;H{=Z7*Kg8PawI<&+iG43c zyZw|wD$XZk%OoAyTdY*8s-JKuh^=;^V>cUP9H7!UmQ1pw>keQh^!hWA7zW~WE{4L4E zql-6#6iK79NOT2O1ZNVj5_uJO#YtvWTz--)`bzXjQ--HmR=2jyjeYU^$@f-DH$K=n zJF}&Va#}|?-M-$19V@pw^>5so)AX~AG;4i&D5X(#gEAP}F{v>xM-$5Z9`7rk?o;;TGVMI~x9$R{dkd);T#!6^xvZy6u}DAQ-o$6z z7EDotM0TP$j&6mjA(t7vr*$XzYqAt>tgmw8 zsH&?exsnKzieqY?wn|>SbHFa&efnNZ#D9Ofp5Q3DE5|uQXnY-MQK|NmDom=IaUgEN zRM3HoV#w6`s}m31u3+`;h^P;6U8+^$=88{wajk0;pvN~k5N)+rlh1U3qp5enw;#ky zkv7KcTw#|AsvY`NaAxI6@?6%L#|Q zZx)*}=N8!48oa0#$XxfcZ#83o@#3`Ll|#EIFHyMb1=1-rw)fnvaJhnoMQ==wFy@e~f@Yl&#EOpwWIbw+8G| ztLx61hhB**aj>;M{Znel{Q>w!X$*h=Xsycm2sCFwc36*g0|XkZ#(kLArn?~`Uv0@&a33m2q zB1g2yjsBvP!4c0_9XI5qvPudDM;f;}8Z<|CssH#=yEIHSwtYLAp>N&rBkD+wVJ?N! z-z)rO^2}qg%fw@0Z(txA{egm{!B}HrBY}rcXu|QO`skV2Sn<--k6nWGUfc%x6ysW1 zk6)rsAEl_-9@`UJ-+U@Q^Wpuc1LMRm2Jxww6k=R?M*X;I(t5xAMyp|O`srr++2Dfe zB~}rYFW&a#y^YtNJLLW#CUB0zt-7JSHQk}zJXx~Zl5G`v8|!g@zfOcXmWgGL_{oVNb;gbU3 z$(sY|9?$YZcRd0IP3As>X|&sV75drv`74lK2m&Bx!Qk$3cl*AFkN}vEukPbVpjz1| zKNQaU9zqo6hE5-}+mr`2gJk#XDkpAd+AV|la)!W2=p$nOj9mqE2`x*~7V|u!sX9o3 z0yv=c^pDv2g`}%}zPjp{oxg&5LP8q(IaZk1B?JmBrIKv!g+j~O7Ci?$EHSaDPdQdu zc{Kwz0E;q}POfk=bnTqPWoXYGxRv?|U~z8yuqf(!DuR_OvC4)W?8Sr&)hh=TVW9ckx`2MIR1PxwI9vk)R* z2}-tG>e4S)&7;z}oqiUvRqCufrW|~x>~MV!*biF|JmFSV#zVWBa56a#(nzmVwrWHe zeJQxHFTy%r&)OCBc$M{mU;1UiR3UIkLIem)ll+NndlO!^E8vtNKbo5jE-%|9+t|M% z;E$TM+2OTAlFdYli(F%({^?>iHjVzUm*ld`-1 z#-!6;9tYCh`Gg=6GrgW3iY`6QgOkD;X+7DSOjz=0nPC~mPbn7_M7On~AMdq=%?4&J zR&%f4a6{Cavs+_BY@l%BghUx(0(PwRsIRnvz0^{uRK`W+d$NHrY4+GVdMNx;!n83<$2gtl2P{ zc@4I5BX}5VGQ_Q^vFe*<&>IZ~ZXC>HX(&dzd2ev9U(RbqD~mpS3>5ZjBwui+0FFHF z7tGFy3&vL#7Z)B5I#0j}OI`5-o9((?t<7WrbafBxKACthJHo5A8B;XZn7WAB!-?>E zs%YdWL(G?NB_rsakYLJhq!myPqFQ4(TrHg~p7gHp1wcM|>zY~C^$_)Q*(+~2HND^a zXbqy;Ns3IA1FH{qfvYkwIH-~C;m`JFEdUNR4o*&xH;B{xe|NRNScsqG*laJFPr+wf zq`OC!wF<+rKlSYoJ+JewCnCqkJ(&TgL)b;6z(A(a^kI?s-IzC?`2F?CU1}h8$iBGC zXDSvoJ`sMz#=dfOjU$A(4$ z-~2fKpDXC}&W^z}s-ofs^Iz{CS~E{il3aTyq+ql&5>Otr%5}{Xd0p8f!hUjnfn z3lC)TC>+*ZYlbu%5CHhZFD?+Y?v3W=wo#^&0{qdNML9P6e~_azO{8*RhPWlW9E!2s z8;sL`4)&XN3lRYtPU*}K71pcrBc785Up!pD5w%7S? z1r)d5OaJVE8+&l>0|!8`*b52ox9fi4d=Z)KCH*Wn**oRos?nRYy=t^2z)ygd7O4bX z#OuD`*?UpT!^n{gL2Qm(&#(u({U$0HpeZ_^U8f-Q_+k`OAa?ZK6IovCx&MkBqr$vO zpS4Pz-KA7>RlDxTkVA5lCZT4T-vW zNM1am;`!dIj-{m-A@zzE!itV!y?nGhDyJcS!x|h$U&|AP-4jgE%@dc51xx7PXTt9K z;qtom(;5a&SR#D{FwjaDefe;THvN;Z|0kh^3qeXsoRU&;pIogcg|iPi-1g~KyFavH z;d8**h@J1B6&1Dh6vd$CrC6Iq9EJlyvELrBoUF~Qo=^?bThY2EgYQQ*+N^oFPx&Rq zqpZ_enJ|;OXFQ|_q_$jl>Z4&$R;0WE3Fopc%qHaK)Ya)E-+n`hDbphU$2h*_vvvK# zuVD*t;kPLXd{q?c=`njvUT=QYRQfe1qZV4@Le{b9@&n8zACbwOi$BGA+@cRmL;Ek< z54Ufyrvo*KG{}d4iaaHF|IYa}cbj^QZ~U9DN0+^rSi!baB8iTZbi z9t1%_bmBAZx0yW@2%Gh?8r-Dv^10CTo(H&}g8LQEG>JC%OVT#iuZQFK{|0#+{3tD53EiA)lAC7FmCj#aJYXfC9wM4|`11Yjv zt}*JlJTwHb_2YEQ`hb}82%tJI4X|i&DKndHQ;ZIs%Qn$qZNAD(p}m|<|J zskrLdQk#+@T+^xyrYh5?eC3AGCS`J!izai1oXrPsF z1~{(5U0-`bcb#b6iUkB@t=*G;48CqvtB)^XWA;3>@*BVt=n3PL^^p)Yf6WCqGCaE7 z70eJ21*thcQNGW{C2`AE4p%li(+*b(u?a6-KDU*Vz>2;Ax0Ywi$9@G@PP5_-SJ17T$xl@_5}(PRf`Q;9h+gA7ja6qQp@rZt!YDn)5131m zhXlhu_n#7m(C`RK%D2y%RmUleaN`qW+4Zf3L|z8fkx!iR8IE>Q=3q?rLdm6RsO4Lf z3HVFW$m9rsX$q`gu$cWvx1SfJef_$tz^bOK%#-qW%T-2Z;e3tM?HJpXw!u>D0N9rz zrtL}Wg6OaeZc;Kt=d%wCCZ5F0Y_h%Hk%oro3wVk z+5_}jowY;5_~Ih_%z6$Z70U^M-QS{{JXWkYP5&LHnTHuvRzh0J1$|m@(`ljP^z1~( zFQQpOL%l7mf*#ArP?l@&;Qci=$R3Dn|Et#ecQR*7?B%UN$nOi>wu=*EvFn|^fWZ69 z`6FA{7Xu7ERCr@pdsSdlZIH;zSV^H!L9s}E{Z~HJ1o$yg_{wU}4i=T%!DPaA?51yv z+Mnp(2@RNm(2$$nvFGSTa5EAVq*qwZ33^J#CL_$BIyKw>bZpqIH+0WXnXj9_>0Wpd zG;?q|UL@HklXFA#!4KJR@;m2-tscO#Zr&*N)uTO*><0|)j+>Py*FP0rMu@AOJ1{a8 zspf3`+MPU3t~7G>eOpHk-VRz%o@Sqk6y(w|10t3CttXiO4yGz5u_x<0RZi&(dnYul z4}291pcEP!R%Fv=SKeP38b!h^XPg`0XVY)zz|qs?WHRy_(&1!7uj7ZM=|8^M`^lHQ zA5S;rsB9%*L8t;`84Nfwqa0kWcHUpplQ_JoqvgL;Y=bL|trJd*`l|+K`c5@c#{}F? zOc{kOYp?P;hX)&r`MCkj^sVPZZ$DU zPDnFG{|B^H@=7y-7pS~6Xf`NYsdBfnIF~SV8eD}AwJ#jXb0|`ep`?s}8^gKA= zNpzXWl>YQ;($0ar9sf$xb!+2|va zvR!BxeOjWG1n$VI?*bwhFf?tsgMJAtxRNz!=8B0cR~i;*m5-O;<+_SEvBC4=G7IC~ zC_|`1Tb&v3iLkxdli7n^G0hDr3^`{6s0P9%4u7Kl)RezF8t`-qDV59+|dFxUV;*lu>BuS@MzkAq!}y91NEeYDl5Pmawp|=N`JK&=S>iV$SVK`b0fRGbBgr?hY^q} zX70Wu*G3h6t@Ts8cNh1v!MAqZqbUjesjpF`0$*8qC#x^Ta%?< zx8C3@s*5l!UwMj4fQS1GM+dW1C+|0YQpC<1v_v*0Z#J@UdX8mlCYp)yDtUD zJWz!9lP-rvRJ3_itT9}hn-1-vg2}@eD;V|c?2IU~uV(3G1U(5UDS!@axQcj!HUt<2 z%jLegcR;p7C1MS3Nwh`!z8hbMHE@r9-kFsM(>Zdp)7HuXYf|80J<;b}8hy9-1j*!d zpSDqu!Utor#tH<0e(K64DRd5#D`C84>lZ&dH6COOp0G2Ub_k<=^{#<;-kI_E*Jzd> zsPeg<-(V~ng+txXCe_aFZxQz+mv;xtFm}yL8ek*6J_osIOF)g0GvImzf8={?MhRkM z0F@ls!nVn$tlYU_)vCjOvZhsr&Z5>YeVG0l!I?Yr1z4DtD%>Kb*kztQdyd>-1b=DE zD@+LBlF7LPT@!&ArWEN82X^Q`SL?RnL5ghDe*xZO3qT$(0aMBEcF!bqQ<$|t7>phM zcD%fLtV$zV3!@M)wsl7eDkBTwRj+)%bks>HBW=JLQ5gYnXNGm;BLxL#@|dQWniRo~ z;s1^AM62PWWr8`=;}K@JPR*j8sO^bqk4(+*T5{Pe(3jBe2D=*x*EItTZ5LjR9Hx#n6yfM+j65^>pT%ls@}OE7i#~IEN}JR%0q*S259K(b#e3JO4=E z_G^{wi;YC(ERm7%Zu)*vNsqUm^w=lnYo2_VQT@AX-@R*}CyhdfR&Q
    {Bp3+Hg^ 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 KRcQOe!9M}E9ce`X literal 0 HcmV?d00001 diff --git a/docs/index.rst b/docs/index.rst index 5e322652..3e7c7a4e 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -32,6 +32,7 @@ RL Baselines3 Zoo also offers a simple interface to train, evaluate agents and d :caption: RL Algorithms modules/ars + modules/crossq modules/ppo_mask modules/ppo_recurrent modules/qrdqn diff --git a/docs/modules/crossq.rst b/docs/modules/crossq.rst new file mode 100644 index 00000000..e81aaa25 --- /dev/null +++ b/docs/modules/crossq.rst @@ -0,0 +1,99 @@ +.. _crossq: + +.. automodule:: sb3_contrib.crossq + + +CrossQ +====== + +Implementation of CrossQ proposed in: + +`Bhatt A.* & Palenicek D.* et al. Batch Normalization in Deep Reinforcement Learning for Greater Sample Efficiency and Simplicity. ICLR 2024.` + +CrossQ is a simple and efficient algorithm that uses batch normalization to improve the sample efficiency of off-policy deep reinforcement learning algorithms. +It is based on the idea of carefully introducing batch normalization layers in the critic network and dropping target networks. +This yield a simpler and more sample-efficient algorithm without requiring high update-to-data ratios. + +.. rubric:: Available Policies + +.. autosummary:: + :nosignatures: + + MlpPolicy + + +Notes +----- + +- Original paper: https://openreview.net/pdf?id=PczQtTsTIX +- Original Implementation: https://github.com/adityab/CrossQ + + +Can I use? +---------- + +- Recurrent policies: ❌ +- Multi processing: ✔️ +- Gym spaces: + + +============= ====== =========== +Space Action Observation +============= ====== =========== +Discrete ❌ ✔️ +Box ✔️ ✔️ +MultiDiscrete ❌ ✔️ +MultiBinary ❌ ✔️ +Dict ❌ ✔️ +============= ====== =========== + + +Example +------- + +.. code-block:: python + + import gymnasium as gym + import numpy as np + + from sb3_contrib import CrossQ + + model = CrossQ("MlpPolicy", "Walker2d-v4") + model.learn(total_timesteps=1_000_000) + model.save("crossq_walker") + + +Results +------- + +Performance evaluation of CrossQ on six MuJoCo environments. +Compared to results from the original paper as well as a version from SBX. + +.. image:: ../images/crossQ_performance.png + +Comments +-------- + +This implementation is based on SB3 SAC implementation. + + +Parameters +---------- + +.. autoclass:: CrossQ + :members: + :inherited-members: + +.. _crossq_policies: + +CrossQ Policies +--------------- + +.. autoclass:: MlpPolicy + :members: + :inherited-members: + +.. autoclass:: sb3_contrib.crossq.policies.CrossQPolicy + :members: + :noindex: + diff --git a/sb3_contrib/__init__.py b/sb3_contrib/__init__.py index 3fbd28d8..5a5f6243 100644 --- a/sb3_contrib/__init__.py +++ b/sb3_contrib/__init__.py @@ -1,6 +1,7 @@ import os from sb3_contrib.ars import ARS +from sb3_contrib.crossq import CrossQ from sb3_contrib.ppo_mask import MaskablePPO from sb3_contrib.ppo_recurrent import RecurrentPPO from sb3_contrib.qrdqn import QRDQN @@ -14,6 +15,7 @@ __all__ = [ "ARS", + "CrossQ", "MaskablePPO", "RecurrentPPO", "QRDQN", diff --git a/sb3_contrib/common/network_layers.py b/sb3_contrib/common/network_layers.py new file mode 100644 index 00000000..524c5274 --- /dev/null +++ b/sb3_contrib/common/network_layers.py @@ -0,0 +1,98 @@ +import torch + +__all__ = ["BatchRenorm1d"] + + +import torch +import torch.nn as nn + + +class BatchRenorm(torch.jit.ScriptModule): + """ + BatchRenorm Module (https://arxiv.org/abs/1702.03275). + Adapted from flax.linen.normalization.BatchNorm + + BatchRenorm is an improved version of vanilla BatchNorm. Contrary to BatchNorm, + BatchRenorm uses the running statistics for normalizing the batches after a warmup phase. + This makes it less prone to suffer from "outlier" batches that can happen + during very long training runs and, therefore, is more robust during long training runs. + + During the warmup phase, it behaves exactly like a BatchNorm layer. + + Args: + num_features: Number of features in the input tensor. + eps: A value added to the variance for numerical stability. + momentum: The value used for the running_mean and running_var computation. + affine: A boolean value that when set to True, this module has learnable + affine parameters. Default: True + """ + + def __init__( + self, + num_features: int, + eps: float = 0.001, + momentum: float = 0.01, + affine: bool = True, + ): + super().__init__() + self.register_buffer("running_mean", torch.zeros(num_features, dtype=torch.float)) + self.register_buffer("running_var", torch.ones(num_features, dtype=torch.float)) + self.register_buffer("num_batches_tracked", torch.tensor(0, dtype=torch.long)) + self.scale = torch.nn.Parameter(torch.ones(num_features, dtype=torch.float)) + self.bias = torch.nn.Parameter(torch.zeros(num_features, dtype=torch.float)) + + self.affine = affine + self.eps = eps + self.step = 0 + self.momentum = momentum + self.rmax = 3.0 + self.dmax = 5.0 + + def _check_input_dim(self, x: torch.Tensor) -> None: + raise NotImplementedError() + + def forward(self, x: torch.Tensor) -> torch.Tensor: + + if self.training: + batch_mean = x.mean(0) + batch_var = x.var(0) + batch_std = (batch_var + self.eps).sqrt() + + # Use batch statistics during initial warm up phase. + if self.num_batches_tracked > 100_000: + + running_std = (self.running_var + self.eps).sqrt() + running_mean = self.running_mean + + r = (batch_std / running_std).detach() + r = r.clamp(1 / self.rmax, self.rmax) + d = ((batch_mean - running_mean) / running_std).detach() + d = d.clamp(-self.dmax, self.dmax) + + m = batch_mean - d * batch_var.sqrt() / r + v = batch_var / (r**2) + + else: + m, v = batch_mean, batch_var + + # Update Running Statistics + self.running_mean += self.momentum * (batch_mean.detach() - self.running_mean) + self.running_var += self.momentum * (batch_var.detach() - self.running_var) + self.num_batches_tracked += 1 + + else: + m, v = self.running_mean, self.running_var + + # Normalize + x = (x - m[None]) / (v[None] + self.eps).sqrt() + + if self.affine: + x = self.scale * x + self.bias + + return x + + +class BatchRenorm1d(BatchRenorm): + def _check_input_dim(self, x: torch.Tensor) -> None: + if x.dim() == 1: + raise ValueError("expected 2D or 3D input (got {x.dim()}D input)") diff --git a/sb3_contrib/crossq/__init__.py b/sb3_contrib/crossq/__init__.py new file mode 100644 index 00000000..9a1e4561 --- /dev/null +++ b/sb3_contrib/crossq/__init__.py @@ -0,0 +1,4 @@ +from sb3_contrib.crossq.crossq import CrossQ +from sb3_contrib.crossq.policies import MlpPolicy + +__all__ = ["CrossQ", "MlpPolicy"] diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py new file mode 100644 index 00000000..de739af5 --- /dev/null +++ b/sb3_contrib/crossq/crossq.py @@ -0,0 +1,330 @@ +from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type, TypeVar, Union + +import numpy as np +import torch as th +from gymnasium import spaces +from stable_baselines3.common.buffers import ReplayBuffer +from stable_baselines3.common.noise import ActionNoise +from stable_baselines3.common.off_policy_algorithm import OffPolicyAlgorithm +from stable_baselines3.common.policies import BasePolicy, ContinuousCritic +from stable_baselines3.common.type_aliases import GymEnv, MaybeCallback, Schedule +from stable_baselines3.common.utils import get_parameters_by_name, polyak_update +from torch.nn import functional as F + +from sb3_contrib.crossq.policies import Actor, CrossQPolicy, CrossQCritic, MlpPolicy + +SelfCrossQ = TypeVar("SelfCrossQ", bound="CrossQ") + + +class CrossQ(OffPolicyAlgorithm): + """ + Implementation of the CrossQ algorithm. + Paper: https://openreview.net/pdf?id=PczQtTsTIX + + :param policy: The policy model to use (MlpPolicy) + :param env: The environment to learn from (if registered in Gym, can be str) + :param learning_rate: learning rate for adam optimizer, + the same learning rate will be used for all networks (Q-Values, Actor and Value function) + it can be a function of the current progress remaining (from 1 to 0) + :param buffer_size: size of the replay buffer + :param learning_starts: how many steps of the model to collect transitions for before learning starts + :param batch_size: Minibatch size for each gradient update + :param gamma: the discount factor + :param train_freq: Update the model every ``train_freq`` steps. Alternatively pass a tuple of frequency and unit + like ``(5, "step")`` or ``(2, "episode")``. + :param gradient_steps: How many gradient steps to do after each rollout (see ``train_freq``) + Set to ``-1`` means to do as many gradient steps as steps done in the environment + during the rollout. + :param action_noise: the action noise type (None by default), this can help + for hard exploration problem. Cf common.noise for the different action noise type. + :param replay_buffer_class: Replay buffer class to use (for instance ``HerReplayBuffer``). + If ``None``, it will be automatically selected. + :param replay_buffer_kwargs: Keyword arguments to pass to the replay buffer on creation. + :param optimize_memory_usage: Enable a memory efficient variant of the replay buffer + at a cost of more complexity. + See https://github.com/DLR-RM/stable-baselines3/issues/37#issuecomment-637501195 + :param ent_coef: Entropy regularization coefficient. (Equivalent to + inverse of reward scale in the original SAC paper.) Controlling exploration/exploitation trade-off. + Set it to 'auto' to learn it automatically (and 'auto_0.1' for using 0.1 as initial value) + :param target_entropy: target entropy when learning ``ent_coef`` (``ent_coef = 'auto'``) + :param use_sde: Whether to use generalized State Dependent Exploration (gSDE) + instead of action noise exploration (default: False) + :param sde_sample_freq: Sample a new noise matrix every n steps when using gSDE + Default: -1 (only sample at the beginning of the rollout) + :param use_sde_at_warmup: Whether to use gSDE instead of uniform sampling + during the warm up phase (before learning starts) + :param stats_window_size: Window size for the rollout logging, specifying the number of episodes to average + the reported success rate, mean episode length, and mean reward over + :param tensorboard_log: the log location for tensorboard (if None, no logging) + :param policy_kwargs: additional arguments to be passed to the policy on creation + :param verbose: Verbosity level: 0 for no output, 1 for info messages (such as device or wrappers used), 2 for + debug messages + :param seed: Seed for the pseudo random generators + :param device: Device (cpu, cuda, ...) on which the code should be run. + Setting it to auto, the code will be run on the GPU if possible. + :param _init_setup_model: Whether or not to build the network at the creation of the instance + """ + + policy_aliases: ClassVar[Dict[str, Type[BasePolicy]]] = { + "MlpPolicy": MlpPolicy, + } + policy: CrossQPolicy + actor: Actor + critic: CrossQCritic + + def __init__( + self, + policy: Union[str, Type[CrossQPolicy]], + env: Union[GymEnv, str], + learning_rate: Union[float, Schedule] = 1e-3, + buffer_size: int = 1_000_000, # 1e6 + learning_starts: int = 100, + batch_size: int = 256, + gamma: float = 0.99, + train_freq: Union[int, Tuple[int, str]] = 1, + gradient_steps: int = 1, + action_noise: Optional[ActionNoise] = None, + replay_buffer_class: Optional[Type[ReplayBuffer]] = None, + replay_buffer_kwargs: Optional[Dict[str, Any]] = None, + optimize_memory_usage: bool = False, + ent_coef: Union[str, float] = "auto", + target_entropy: Union[str, float] = "auto", + use_sde: bool = False, + sde_sample_freq: int = -1, + use_sde_at_warmup: bool = False, + stats_window_size: int = 100, + tensorboard_log: Optional[str] = None, + policy_kwargs: Optional[Dict[str, Any]] = None, + verbose: int = 0, + seed: Optional[int] = None, + device: Union[th.device, str] = "auto", + _init_setup_model: bool = True, + ): + super().__init__( + policy, + env, + learning_rate, + buffer_size, + learning_starts, + batch_size, + 1.0, + gamma, + train_freq, + gradient_steps, + action_noise, + replay_buffer_class=replay_buffer_class, + replay_buffer_kwargs=replay_buffer_kwargs, + policy_kwargs=policy_kwargs, + stats_window_size=stats_window_size, + tensorboard_log=tensorboard_log, + verbose=verbose, + device=device, + seed=seed, + use_sde=use_sde, + sde_sample_freq=sde_sample_freq, + use_sde_at_warmup=use_sde_at_warmup, + optimize_memory_usage=optimize_memory_usage, + supported_action_spaces=(spaces.Box,), + support_multi_env=True, + ) + + self.target_entropy = target_entropy + self.log_ent_coef = None # type: Optional[th.Tensor] + # Entropy coefficient / Entropy temperature + # Inverse of the reward scale + self.ent_coef = ent_coef + self.ent_coef_optimizer: Optional[th.optim.Adam] = None + + if _init_setup_model: + self._setup_model() + + def _setup_model(self) -> None: + super()._setup_model() + self._create_aliases() + # Running mean and running var + self.batch_norm_stats = get_parameters_by_name(self.critic, ["running_"]) + # Target entropy is used when learning the entropy coefficient + if self.target_entropy == "auto": + # automatically set target entropy if needed + self.target_entropy = float(-np.prod(self.env.action_space.shape).astype(np.float32)) # type: ignore + else: + # Force conversion + # this will also throw an error for unexpected string + self.target_entropy = float(self.target_entropy) + + # The entropy coefficient or entropy can be learned automatically + # see Automating Entropy Adjustment for Maximum Entropy RL section + # of https://arxiv.org/abs/1812.05905 + if isinstance(self.ent_coef, str) and self.ent_coef.startswith("auto"): + # Default initial value of ent_coef when learned + init_value = 1.0 + if "_" in self.ent_coef: + init_value = float(self.ent_coef.split("_")[1]) + assert init_value > 0.0, "The initial value of ent_coef must be greater than 0" + + # Note: we optimize the log of the entropy coeff which is slightly different from the paper + # as discussed in https://github.com/rail-berkeley/softlearning/issues/37 + self.log_ent_coef = th.log(th.ones(1, device=self.device) * init_value).requires_grad_(True) + self.ent_coef_optimizer = th.optim.Adam([self.log_ent_coef], lr=self.lr_schedule(1)) + else: + # Force conversion to float + # this will throw an error if a malformed string (different from 'auto') + # is passed + self.ent_coef_tensor = th.tensor(float(self.ent_coef), device=self.device) + + def _create_aliases(self) -> None: + self.actor = self.policy.actor + self.critic = self.policy.critic + + def train(self, gradient_steps: int, batch_size: int = 64) -> None: + # Switch to train mode (this affects batch norm / dropout) + self.policy.set_training_mode(True) + # Update optimizers learning rate + optimizers = [self.actor.optimizer, self.critic.optimizer] + if self.ent_coef_optimizer is not None: + optimizers += [self.ent_coef_optimizer] + + # Update learning rate according to lr schedule + self._update_learning_rate(optimizers) + + ent_coef_losses, ent_coefs = [], [] + actor_losses, critic_losses = [], [] + + for gradient_step in range(gradient_steps): + # Sample replay buffer + replay_data = self.replay_buffer.sample(batch_size, env=self._vec_normalize_env) # type: ignore[union-attr] + + # We need to sample because `log_std` may have changed between two gradient steps + if self.use_sde: + self.actor.reset_noise() + + # Note, in the following lines we always need to make sure to set train/eval modes + # of actor and critic carefully. This is because of the BatchNorm layers in the networks + # which behave differently in train and eval modes. + self.actor.set_training_mode(True) + actions_pi, log_prob = self.actor.action_log_prob(replay_data.observations) + log_prob = log_prob.reshape(-1, 1) + self.actor.set_training_mode(False) + + ent_coef_loss = None + if self.ent_coef_optimizer is not None and self.log_ent_coef is not None: + # Important: detach the variable from the graph + # so we don't change it with other losses + # see https://github.com/rail-berkeley/softlearning/issues/60 + ent_coef = th.exp(self.log_ent_coef.detach()) + ent_coef_loss = -(self.log_ent_coef * (log_prob + self.target_entropy).detach()).mean() + ent_coef_losses.append(ent_coef_loss.item()) + else: + ent_coef = self.ent_coef_tensor + + ent_coefs.append(ent_coef.item()) + + # Optimize entropy coefficient, also called + # entropy temperature or alpha in the paper + if ent_coef_loss is not None and self.ent_coef_optimizer is not None: + self.ent_coef_optimizer.zero_grad() + ent_coef_loss.backward() + self.ent_coef_optimizer.step() + + with th.no_grad(): + # Select action according to policy + self.actor.set_training_mode(False) + next_actions, next_log_prob = self.actor.action_log_prob(replay_data.next_observations) + self.actor.set_training_mode(False) + + # Joint forward pass of obs/next_obs and actions/next_state_actions to have only + # one forward pass with shape (n_critics, 2 * batch_size, 1). + # + # This has two reasons: + # 1. According to the paper obs/actions and next_obs/next_state_actions are differently + # distributed which is the reason why "naively" applying Batch Normalization in SAC fails. + # The batch statistics have to instead be calculated for the mixture distribution of obs/next_obs + # and actions/next_state_actions. Otherwise, next_obs/next_state_actions are perceived as + # out-of-distribution to the Batch Normalization layer, since running statistics are only polyak averaged + # over from the live network and have never seen the next batch which is known to be unstable. + # Without target networks, the joint forward pass is a simple solution to calculate + # the joint batch statistics directly with a single forward pass. + # + # 2. From a computational perspective a single forward pass is simply more efficient than + # two sequential forward passes. + all_obs = th.cat([replay_data.observations, replay_data.next_observations], dim=0) + all_acts = th.cat([replay_data.actions, next_actions], dim=0) + + self.critic.set_training_mode(True) + all_q_values = th.cat(self.critic(all_obs, all_acts), dim=1) + self.critic.set_training_mode(False) + current_q_values, next_q_values = th.split(all_q_values, batch_size, dim=0) + current_q_values = th.split(current_q_values, [1, 1], dim=1) # type: ignore + + with th.no_grad(): + # Compute the target Q value + next_q_values = next_q_values.detach() + next_q_values, _ = th.min(next_q_values, dim=1, keepdim=True) + + # add entropy term + next_q_values = next_q_values - ent_coef * next_log_prob.reshape(-1, 1) + # td error + entropy term + target_q_values = replay_data.rewards + (1 - replay_data.dones) * self.gamma * next_q_values + + # Compute critic loss + critic_loss = 0.5 * sum(F.mse_loss(current_q, target_q_values.detach()) for current_q in current_q_values) + assert isinstance(critic_loss, th.Tensor) # for type checker + critic_losses.append(critic_loss.item()) # type: ignore[union-attr] + + # Optimize the critic + self.critic.optimizer.zero_grad() + critic_loss.backward() + self.critic.optimizer.step() + + # Compute actor loss + self.critic.set_training_mode(False) + q_values_pi = th.cat(self.critic(replay_data.observations, actions_pi), dim=1) + self.critic.set_training_mode(False) + + min_qf_pi, _ = th.min(q_values_pi, dim=1, keepdim=True) + actor_loss = (ent_coef * log_prob.reshape(-1, 1) - min_qf_pi).mean() + actor_losses.append(actor_loss.item()) + + # Optimize the actor + self.actor.optimizer.zero_grad() + actor_loss.backward() + self.actor.optimizer.step() + + self._n_updates += gradient_steps + + self.logger.record("train/n_updates", self._n_updates, exclude="tensorboard") + self.logger.record("train/ent_coef", np.mean(ent_coefs)) + self.logger.record("train/actor_loss", np.mean(actor_losses)) + self.logger.record("train/critic_loss", np.mean(critic_losses)) + if len(ent_coef_losses) > 0: + self.logger.record("train/ent_coef_loss", np.mean(ent_coef_losses)) + + def learn( + self: SelfCrossQ, + total_timesteps: int, + callback: MaybeCallback = None, + log_interval: int = 4, + tb_log_name: str = "CrossQ", + reset_num_timesteps: bool = True, + progress_bar: bool = False, + ) -> SelfCrossQ: + return super().learn( + total_timesteps=total_timesteps, + callback=callback, + log_interval=log_interval, + tb_log_name=tb_log_name, + reset_num_timesteps=reset_num_timesteps, + progress_bar=progress_bar, + ) + + def _excluded_save_params(self) -> List[str]: + return super()._excluded_save_params() + ["actor", "critic"] + + def _get_torch_save_params(self) -> Tuple[List[str], List[str]]: + state_dicts = ["policy", "actor.optimizer", "critic.optimizer"] + if self.ent_coef_optimizer is not None: + saved_pytorch_variables = ["log_ent_coef"] + state_dicts.append("ent_coef_optimizer") + else: + saved_pytorch_variables = ["ent_coef_tensor"] + return state_dicts, saved_pytorch_variables diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py new file mode 100644 index 00000000..12b3f92b --- /dev/null +++ b/sb3_contrib/crossq/policies.py @@ -0,0 +1,450 @@ +from typing import Any, Dict, List, Optional, Tuple, Type, Union + +import torch as th +from gymnasium import spaces +from stable_baselines3.common.distributions import SquashedDiagGaussianDistribution, StateDependentNoiseDistribution +from stable_baselines3.common.policies import BaseModel, BasePolicy +from stable_baselines3.common.preprocessing import get_action_dim +from stable_baselines3.common.torch_layers import ( + BaseFeaturesExtractor, + CombinedExtractor, + FlattenExtractor, + NatureCNN, + create_mlp, + get_actor_critic_arch, +) +from stable_baselines3.common.type_aliases import PyTorchObs, Schedule +from torch import nn + +from sb3_contrib.common.network_layers import BatchRenorm1d + +# CAP the standard deviation of the actor +LOG_STD_MAX = 2 +LOG_STD_MIN = -20 + + +class Actor(BasePolicy): + """ + Actor network (policy) for CrossQ. + It contains BatchRenorm layers to stabilize and accelerate training. + + :param observation_space: Obervation space + :param action_space: Action space + :param net_arch: Network architecture + :param features_extractor: Network to extract features + (a CNN when using images, a nn.Flatten() layer otherwise) + :param features_dim: Number of features + :param activation_fn: Activation function + :param use_sde: Whether to use State Dependent Exploration or not + :param log_std_init: Initial value for the log standard deviation + :param full_std: Whether to use (n_features x n_actions) parameters + for the std instead of only (n_features,) when using gSDE. + :param use_expln: Use ``expln()`` function instead of ``exp()`` when using gSDE to ensure + a positive standard deviation (cf paper). It allows to keep variance + above zero and prevent it from growing too fast. In practice, ``exp()`` is usually enough. + :param clip_mean: Clip the mean output when using gSDE to avoid numerical instability. + :param normalize_images: Whether to normalize images or not, + dividing by 255.0 (True by default) + """ + + action_space: spaces.Box + + def __init__( + self, + observation_space: spaces.Space, + action_space: spaces.Box, + net_arch: List[int], + features_extractor: nn.Module, + features_dim: int, + activation_fn: Type[nn.Module] = nn.ReLU, + use_sde: bool = False, + log_std_init: float = -3, + full_std: bool = True, + use_expln: bool = False, + clip_mean: float = 2.0, + normalize_images: bool = True, + batch_norm: bool = True, + ): + super().__init__( + observation_space, + action_space, + features_extractor=features_extractor, + normalize_images=normalize_images, + squash_output=True, + ) + + # Save arguments to re-create object at loading + self.use_sde = use_sde + self.sde_features_extractor = None + self.net_arch = net_arch + self.features_dim = features_dim + self.activation_fn = activation_fn + self.log_std_init = log_std_init + self.use_expln = use_expln + self.full_std = full_std + self.clip_mean = clip_mean + + action_dim = get_action_dim(self.action_space) + latent_pi_net = create_mlp(features_dim, -1, net_arch, activation_fn) + + if batch_norm: + # If batchnorm, then we want to add torch.nn.Batch_Norm layers before every linear layer + net : List[Union[nn.Module, BatchRenorm1d]] = [] + for layer in latent_pi_net: + if isinstance(layer, nn.Linear): + net.append(BatchRenorm1d(layer.in_features, eps=0.001, momentum=0.01)) + net.append(layer) + net.append(BatchRenorm1d(net_arch[-1], eps=0.001, momentum=0.01)) + latent_pi_net = net + + self.latent_pi = nn.Sequential(*latent_pi_net) + last_layer_dim = net_arch[-1] if len(net_arch) > 0 else features_dim + + if self.use_sde: + self.action_dist = StateDependentNoiseDistribution( + action_dim, full_std=full_std, use_expln=use_expln, learn_features=True, squash_output=True + ) + self.mu, self.log_std = self.action_dist.proba_distribution_net( + latent_dim=last_layer_dim, latent_sde_dim=last_layer_dim, log_std_init=log_std_init + ) + # Avoid numerical issues by limiting the mean of the Gaussian + # to be in [-clip_mean, clip_mean] + if clip_mean > 0.0: + self.mu = nn.Sequential(self.mu, nn.Hardtanh(min_val=-clip_mean, max_val=clip_mean)) + else: + self.action_dist = SquashedDiagGaussianDistribution(action_dim) # type: ignore[assignment] + self.mu = nn.Linear(last_layer_dim, action_dim) + self.log_std = nn.Linear(last_layer_dim, action_dim) # type: ignore[assignment] + + def _get_constructor_parameters(self) -> Dict[str, Any]: + data = super()._get_constructor_parameters() + + data.update( + dict( + net_arch=self.net_arch, + features_dim=self.features_dim, + activation_fn=self.activation_fn, + use_sde=self.use_sde, + log_std_init=self.log_std_init, + full_std=self.full_std, + use_expln=self.use_expln, + features_extractor=self.features_extractor, + clip_mean=self.clip_mean, + ) + ) + return data + + def get_std(self) -> th.Tensor: + """ + Retrieve the standard deviation of the action distribution. + Only useful when using gSDE. + It corresponds to ``th.exp(log_std)`` in the normal case, + but is slightly different when using ``expln`` function + (cf StateDependentNoiseDistribution doc). + + :return: + """ + msg = "get_std() is only available when using gSDE" + assert isinstance(self.action_dist, StateDependentNoiseDistribution), msg + return self.action_dist.get_std(self.log_std) + + def reset_noise(self, batch_size: int = 1) -> None: + """ + Sample new weights for the exploration matrix, when using gSDE. + + :param batch_size: + """ + msg = "reset_noise() is only available when using gSDE" + assert isinstance(self.action_dist, StateDependentNoiseDistribution), msg + self.action_dist.sample_weights(self.log_std, batch_size=batch_size) + + def get_action_dist_params(self, obs: PyTorchObs) -> Tuple[th.Tensor, th.Tensor, Dict[str, th.Tensor]]: + """ + Get the parameters for the action distribution. + + :param obs: + :return: + Mean, standard deviation and optional keyword arguments. + """ + features = self.extract_features(obs, self.features_extractor) + latent_pi = self.latent_pi(features) + mean_actions = self.mu(latent_pi) + + if self.use_sde: + return mean_actions, self.log_std, dict(latent_sde=latent_pi) + # Unstructured exploration (Original implementation) + log_std = self.log_std(latent_pi) # type: ignore[operator] + # Original Implementation to cap the standard deviation + log_std = th.clamp(log_std, LOG_STD_MIN, LOG_STD_MAX) + return mean_actions, log_std, {} + + def forward(self, obs: PyTorchObs, deterministic: bool = False) -> th.Tensor: + mean_actions, log_std, kwargs = self.get_action_dist_params(obs) + # Note: the action is squashed + return self.action_dist.actions_from_params(mean_actions, log_std, deterministic=deterministic, **kwargs) + + def action_log_prob(self, obs: PyTorchObs) -> Tuple[th.Tensor, th.Tensor]: + mean_actions, log_std, kwargs = self.get_action_dist_params(obs) + # return action and associated log prob + return self.action_dist.log_prob_from_params(mean_actions, log_std, **kwargs) + + def _predict(self, observation: PyTorchObs, deterministic: bool = False) -> th.Tensor: + return self(observation, deterministic) + + +class CrossQCritic(BaseModel): + """ + Critic network(s) for CrossQ. + The differnce with standard critic networks used by SAC/TD3 is that it uses BatchRenorm layers. + + By default, it creates two critic networks used to reduce overestimation + thanks to clipped Q-learning (cf TD3 paper). + + :param observation_space: Obervation space + :param action_space: Action space + :param net_arch: Network architecture + :param features_extractor: Network to extract features + (a CNN when using images, a nn.Flatten() layer otherwise) + :param features_dim: Number of features + :param activation_fn: Activation function + :param normalize_images: Whether to normalize images or not, + dividing by 255.0 (True by default) + :param n_critics: Number of critic networks to create. + :param share_features_extractor: Whether the features extractor is shared or not + between the actor and the critic (this saves computation time) + """ + + features_extractor: BaseFeaturesExtractor + + def __init__( + self, + observation_space: spaces.Space, + action_space: spaces.Box, + net_arch: List[int], + features_extractor: BaseFeaturesExtractor, + features_dim: int, + activation_fn: Type[nn.Module] = nn.ReLU, + normalize_images: bool = True, + n_critics: int = 2, + share_features_extractor: bool = True, + batch_norm: bool = True, + ): + super().__init__( + observation_space, + action_space, + features_extractor=features_extractor, + normalize_images=normalize_images, + ) + + action_dim = get_action_dim(self.action_space) + + self.share_features_extractor = share_features_extractor + self.n_critics = n_critics + self.q_networks: List[nn.Module] = [] + for idx in range(n_critics): + q_net_list = create_mlp(features_dim + action_dim, 1, net_arch, activation_fn) + + if batch_norm: + # If batchnorm, then we want to add torch.nn.Batch_Norm layers before every linear layer + net : List[Union[nn.Module, BatchRenorm1d]] = [] + for layer in q_net_list: + if isinstance(layer, nn.Linear): + net.append(BatchRenorm1d(layer.in_features, eps=0.001, momentum=0.01)) + net.append(layer) + q_net_list = net + + q_net = nn.Sequential(*q_net_list) + self.add_module(f"qf{idx}", q_net) + self.q_networks.append(q_net) + + def forward(self, obs: th.Tensor, actions: th.Tensor) -> Tuple[th.Tensor, ...]: + # Learn the features extractor using the policy loss only + # when the features_extractor is shared with the actor + with th.set_grad_enabled(not self.share_features_extractor): + features = self.extract_features(obs, self.features_extractor) + qvalue_input = th.cat([features, actions], dim=1) + return tuple(q_net(qvalue_input) for q_net in self.q_networks) + + +class CrossQPolicy(BasePolicy): + """ + Policy class (with both actor and critic) for CrossQ. + + :param observation_space: Observation space + :param action_space: Action space + :param lr_schedule: Learning rate schedule (could be constant) + :param net_arch: The specification of the policy and value networks. + :param activation_fn: Activation function + :param use_sde: Whether to use State Dependent Exploration or not + :param log_std_init: Initial value for the log standard deviation + :param use_expln: Use ``expln()`` function instead of ``exp()`` when using gSDE to ensure + a positive standard deviation (cf paper). It allows to keep variance + above zero and prevent it from growing too fast. In practice, ``exp()`` is usually enough. + :param clip_mean: Clip the mean output when using gSDE to avoid numerical instability. + :param features_extractor_class: Features extractor to use. + :param features_extractor_kwargs: Keyword arguments + to pass to the features extractor. + :param normalize_images: Whether to normalize images or not, + dividing by 255.0 (True by default) + :param optimizer_class: The optimizer to use, + ``th.optim.Adam`` by default + :param optimizer_kwargs: Additional keyword arguments, + excluding the learning rate, to pass to the optimizer + :param n_critics: Number of critic networks to create. + :param share_features_extractor: Whether to share or not the features extractor + between the actor and the critic (this saves computation time) + """ + + actor: Actor + critic: CrossQCritic + + def __init__( + self, + observation_space: spaces.Space, + action_space: spaces.Box, + lr_schedule: Schedule, + net_arch: Optional[Union[List[int], Dict[str, List[int]]]] = None, + activation_fn: Type[nn.Module] = nn.ReLU, + use_sde: bool = False, + log_std_init: float = -3, + use_expln: bool = False, + clip_mean: float = 2.0, + features_extractor_class: Type[BaseFeaturesExtractor] = FlattenExtractor, + features_extractor_kwargs: Optional[Dict[str, Any]] = None, + normalize_images: bool = True, + optimizer_class: Type[th.optim.Optimizer] = th.optim.Adam, + optimizer_kwargs: Optional[Dict[str, Any]] = None, + n_critics: int = 2, + share_features_extractor: bool = False, + ): + super().__init__( + observation_space, + action_space, + features_extractor_class, + features_extractor_kwargs, + optimizer_class=optimizer_class, + optimizer_kwargs=optimizer_kwargs, + squash_output=True, + normalize_images=normalize_images, + ) + + if net_arch is None: + net_arch = {"pi": [256, 256], "qf": [2048, 2048]} + + actor_arch, critic_arch = get_actor_critic_arch(net_arch) + + self.net_arch = net_arch + self.activation_fn = activation_fn + self.net_args = { + "observation_space": self.observation_space, + "action_space": self.action_space, + "net_arch": actor_arch, + "activation_fn": self.activation_fn, + "normalize_images": normalize_images, + "batch_norm": True, + } + self.actor_kwargs = self.net_args.copy() + + sde_kwargs = { + "use_sde": use_sde, + "log_std_init": log_std_init, + "use_expln": use_expln, + "clip_mean": clip_mean, + } + self.actor_kwargs.update(sde_kwargs) + self.critic_kwargs = self.net_args.copy() + self.critic_kwargs.update( + { + "n_critics": n_critics, + "net_arch": critic_arch, + "share_features_extractor": share_features_extractor, + "batch_norm": True, + } + ) + + self.share_features_extractor = share_features_extractor + + self._build(lr_schedule) + + def _build(self, lr_schedule: Schedule) -> None: + self.actor = self.make_actor() + self.actor.optimizer = self.optimizer_class( + self.actor.parameters(), + lr=lr_schedule(1), # type: ignore[call-arg] + **self.optimizer_kwargs, + ) + + if self.share_features_extractor: + self.critic = self.make_critic(features_extractor=self.actor.features_extractor) + # Do not optimize the shared features extractor with the critic loss + # otherwise, there are gradient computation issues + critic_parameters = [param for name, param in self.critic.named_parameters() if "features_extractor" not in name] + else: + # Create a separate features extractor for the critic + # this requires more memory and computation + self.critic = self.make_critic(features_extractor=None) + critic_parameters = list(self.critic.parameters()) + + self.critic.optimizer = self.optimizer_class( + critic_parameters, + lr=lr_schedule(1), # type: ignore[call-arg] + **self.optimizer_kwargs, + ) + + def _get_constructor_parameters(self) -> Dict[str, Any]: + data = super()._get_constructor_parameters() + + data.update( + dict( + net_arch=self.net_arch, + activation_fn=self.net_args["activation_fn"], + use_sde=self.actor_kwargs["use_sde"], + log_std_init=self.actor_kwargs["log_std_init"], + use_expln=self.actor_kwargs["use_expln"], + clip_mean=self.actor_kwargs["clip_mean"], + n_critics=self.critic_kwargs["n_critics"], + lr_schedule=self._dummy_schedule, # dummy lr schedule, not needed for loading policy alone + optimizer_class=self.optimizer_class, + optimizer_kwargs=self.optimizer_kwargs, + features_extractor_class=self.features_extractor_class, + features_extractor_kwargs=self.features_extractor_kwargs, + ) + ) + return data + + def reset_noise(self, batch_size: int = 1) -> None: + """ + Sample new weights for the exploration matrix, when using gSDE. + + :param batch_size: + """ + self.actor.reset_noise(batch_size=batch_size) + + def make_actor(self, features_extractor: Optional[BaseFeaturesExtractor] = None) -> Actor: + actor_kwargs = self._update_features_extractor(self.actor_kwargs, features_extractor) + return Actor(**actor_kwargs).to(self.device) + + def make_critic(self, features_extractor: Optional[BaseFeaturesExtractor] = None) -> CrossQCritic: + critic_kwargs = self._update_features_extractor(self.critic_kwargs, features_extractor) + return CrossQCritic(**critic_kwargs).to(self.device) + + def forward(self, obs: PyTorchObs, deterministic: bool = False) -> th.Tensor: + return self._predict(obs, deterministic=deterministic) + + def _predict(self, observation: PyTorchObs, deterministic: bool = False) -> th.Tensor: + return self.actor(observation, deterministic) + + def set_training_mode(self, mode: bool) -> None: + """ + Put the policy in either training or evaluation mode. + + This affects certain modules, such as batch normalisation and dropout. + + :param mode: if true, set to training mode, else set to evaluation mode + """ + self.actor.set_training_mode(mode) + self.critic.set_training_mode(mode) + self.training = mode + + +MlpPolicy = CrossQPolicy diff --git a/tests/test_run.py b/tests/test_run.py index a8cec79a..ed524f22 100644 --- a/tests/test_run.py +++ b/tests/test_run.py @@ -3,11 +3,21 @@ from stable_baselines3.common.env_util import make_vec_env from stable_baselines3.common.vec_env import VecNormalize -from sb3_contrib import ARS, QRDQN, TQC, TRPO, MaskablePPO +from sb3_contrib import ARS, QRDQN, TQC, TRPO, CrossQ, MaskablePPO from sb3_contrib.common.envs import InvalidActionEnvDiscrete from sb3_contrib.common.vec_env import AsyncEval +def test(): + model = CrossQ( + "MlpPolicy", + "Pendulum-v1", + learning_starts=100, + verbose=1, + ) + model.learn(total_timesteps=300) + + @pytest.mark.parametrize("ent_coef", ["auto", 0.01, "auto_0.01"]) def test_tqc(ent_coef): model = TQC( From 4fa78a79fefa4cb81564b8e591bca514739318e6 Mon Sep 17 00:00:00 2001 From: Daniel Palenicek Date: Sun, 5 May 2024 11:57:53 +0200 Subject: [PATCH 02/32] Fixed code style --- sb3_contrib/common/network_layers.py | 4 ---- sb3_contrib/crossq/crossq.py | 6 +++--- sb3_contrib/crossq/policies.py | 6 ++---- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/sb3_contrib/common/network_layers.py b/sb3_contrib/common/network_layers.py index 524c5274..28aab64d 100644 --- a/sb3_contrib/common/network_layers.py +++ b/sb3_contrib/common/network_layers.py @@ -3,10 +3,6 @@ __all__ = ["BatchRenorm1d"] -import torch -import torch.nn as nn - - class BatchRenorm(torch.jit.ScriptModule): """ BatchRenorm Module (https://arxiv.org/abs/1702.03275). diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py index de739af5..54d31b61 100644 --- a/sb3_contrib/crossq/crossq.py +++ b/sb3_contrib/crossq/crossq.py @@ -6,12 +6,12 @@ from stable_baselines3.common.buffers import ReplayBuffer from stable_baselines3.common.noise import ActionNoise from stable_baselines3.common.off_policy_algorithm import OffPolicyAlgorithm -from stable_baselines3.common.policies import BasePolicy, ContinuousCritic +from stable_baselines3.common.policies import BasePolicy from stable_baselines3.common.type_aliases import GymEnv, MaybeCallback, Schedule -from stable_baselines3.common.utils import get_parameters_by_name, polyak_update +from stable_baselines3.common.utils import get_parameters_by_name from torch.nn import functional as F -from sb3_contrib.crossq.policies import Actor, CrossQPolicy, CrossQCritic, MlpPolicy +from sb3_contrib.crossq.policies import Actor, CrossQCritic, CrossQPolicy, MlpPolicy SelfCrossQ = TypeVar("SelfCrossQ", bound="CrossQ") diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index 12b3f92b..deb588a4 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -7,9 +7,7 @@ from stable_baselines3.common.preprocessing import get_action_dim from stable_baselines3.common.torch_layers import ( BaseFeaturesExtractor, - CombinedExtractor, FlattenExtractor, - NatureCNN, create_mlp, get_actor_critic_arch, ) @@ -89,7 +87,7 @@ def __init__( if batch_norm: # If batchnorm, then we want to add torch.nn.Batch_Norm layers before every linear layer - net : List[Union[nn.Module, BatchRenorm1d]] = [] + net: List[Union[nn.Module, BatchRenorm1d]] = [] for layer in latent_pi_net: if isinstance(layer, nn.Linear): net.append(BatchRenorm1d(layer.in_features, eps=0.001, momentum=0.01)) @@ -246,7 +244,7 @@ def __init__( if batch_norm: # If batchnorm, then we want to add torch.nn.Batch_Norm layers before every linear layer - net : List[Union[nn.Module, BatchRenorm1d]] = [] + net: List[Union[nn.Module, BatchRenorm1d]] = [] for layer in q_net_list: if isinstance(layer, nn.Linear): net.append(BatchRenorm1d(layer.in_features, eps=0.001, momentum=0.01)) From 7ce57de8b5afffde52df0a61a2abca7880903c34 Mon Sep 17 00:00:00 2001 From: Daniel Palenicek Date: Sun, 12 May 2024 02:48:24 +0200 Subject: [PATCH 03/32] Clean up, comments and refactored to sbx variable names --- sb3_contrib/common/network_layers.py | 61 +++++++++++++++++++--------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/sb3_contrib/common/network_layers.py b/sb3_contrib/common/network_layers.py index 28aab64d..5bb3b263 100644 --- a/sb3_contrib/common/network_layers.py +++ b/sb3_contrib/common/network_layers.py @@ -6,21 +6,26 @@ class BatchRenorm(torch.jit.ScriptModule): """ BatchRenorm Module (https://arxiv.org/abs/1702.03275). - Adapted from flax.linen.normalization.BatchNorm + Adapted to Pytorch from sbx.sbx.common.jax_layers.BatchRenorm BatchRenorm is an improved version of vanilla BatchNorm. Contrary to BatchNorm, BatchRenorm uses the running statistics for normalizing the batches after a warmup phase. This makes it less prone to suffer from "outlier" batches that can happen during very long training runs and, therefore, is more robust during long training runs. - During the warmup phase, it behaves exactly like a BatchNorm layer. + During the warmup phase, it behaves exactly like a BatchNorm layer. After the warmup phase, + the running statistics are used for normalization. The running statistics are updated during + training mode. During evaluation mode, the running statistics are used for normalization but + not updated. Args: num_features: Number of features in the input tensor. eps: A value added to the variance for numerical stability. - momentum: The value used for the running_mean and running_var computation. + momentum: The value used for the ra_mean and ra_var computation. affine: A boolean value that when set to True, this module has learnable affine parameters. Default: True + warmup_steps: Number of warum steps that are performed before the running statistics + are used form normalization. During the warump phase, the batch statistics are used. """ def __init__( @@ -29,11 +34,12 @@ def __init__( eps: float = 0.001, momentum: float = 0.01, affine: bool = True, + warmup_steps: int = 100_000, ): super().__init__() - self.register_buffer("running_mean", torch.zeros(num_features, dtype=torch.float)) - self.register_buffer("running_var", torch.ones(num_features, dtype=torch.float)) - self.register_buffer("num_batches_tracked", torch.tensor(0, dtype=torch.long)) + self.register_buffer("ra_mean", torch.zeros(num_features, dtype=torch.float)) + self.register_buffer("ra_var", torch.ones(num_features, dtype=torch.float)) + self.register_buffer("steps", torch.tensor(0, dtype=torch.long)) self.scale = torch.nn.Parameter(torch.ones(num_features, dtype=torch.float)) self.bias = torch.nn.Parameter(torch.zeros(num_features, dtype=torch.float)) @@ -43,11 +49,21 @@ def __init__( self.momentum = momentum self.rmax = 3.0 self.dmax = 5.0 + self.warmup_steps = warmup_steps def _check_input_dim(self, x: torch.Tensor) -> None: raise NotImplementedError() def forward(self, x: torch.Tensor) -> torch.Tensor: + """ + Normalize the input tensor. + + Args: + x: Input tensor + + Returns: + Normalized tensor. + """ if self.training: batch_mean = x.mean(0) @@ -55,32 +71,39 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: batch_std = (batch_var + self.eps).sqrt() # Use batch statistics during initial warm up phase. - if self.num_batches_tracked > 100_000: - - running_std = (self.running_var + self.eps).sqrt() - running_mean = self.running_mean + # Note: in the original paper, after some warmup phase (batch norm phase of 5k steps) + # the constraints are linearly relaxed to r_max/d_max over 40k steps + # Here we only have a warmup phase + if self.steps > self.warmup_steps: + running_std = (self.ra_var + self.eps).sqrt() + # scale r = (batch_std / running_std).detach() r = r.clamp(1 / self.rmax, self.rmax) - d = ((batch_mean - running_mean) / running_std).detach() + # bias + d = ((batch_mean - self.ra_mean) / running_std).detach() d = d.clamp(-self.dmax, self.dmax) - m = batch_mean - d * batch_var.sqrt() / r - v = batch_var / (r**2) + # BatchNorm normalization, using minibatch stats and running average stats + # Because we use _normalize, this is equivalent to + # ((x - x_mean) / sigma) * r + d = ((x - x_mean) * r + d * sigma) / sigma + # where sigma = sqrt(var) + custom_mean = batch_mean - d * batch_var.sqrt() / r + custom_var = batch_var / (r**2) else: - m, v = batch_mean, batch_var + custom_mean, custom_var = batch_mean, batch_var # Update Running Statistics - self.running_mean += self.momentum * (batch_mean.detach() - self.running_mean) - self.running_var += self.momentum * (batch_var.detach() - self.running_var) - self.num_batches_tracked += 1 + self.ra_mean += self.momentum * (batch_mean.detach() - self.ra_mean) + self.ra_var += self.momentum * (batch_var.detach() - self.ra_var) + self.steps += 1 else: - m, v = self.running_mean, self.running_var + custom_mean, custom_var = self.ra_mean, self.ra_var # Normalize - x = (x - m[None]) / (v[None] + self.eps).sqrt() + x = (x - custom_mean[None]) / (custom_var[None] + self.eps).sqrt() if self.affine: x = self.scale * x + self.bias From 9c339b881a631875b1c139f2a0eb0eb08945cdb7 Mon Sep 17 00:00:00 2001 From: Daniel Palenicek Date: Sun, 12 May 2024 03:13:52 +0200 Subject: [PATCH 04/32] 1024 neuron Q function (sbx default) --- sb3_contrib/crossq/policies.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index deb588a4..9b1c7726 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -327,7 +327,10 @@ def __init__( ) if net_arch is None: - net_arch = {"pi": [256, 256], "qf": [2048, 2048]} + # While CrossQ already works with a [256,256] critic network, + # the authors found that a wider network significantly improves performance. + # We use a slightly smaller net for faster computation, [1024, 1024] instead of [2048, 2048] in the paper. + net_arch = {"pi": [256, 256], "qf": [1024, 1024]} actor_arch, critic_arch = get_actor_critic_arch(net_arch) From 2b1ff5e0073256738d315b601a0e4aa06ea5c6c1 Mon Sep 17 00:00:00 2001 From: Daniel Palenicek Date: Sun, 12 May 2024 03:14:26 +0200 Subject: [PATCH 05/32] batch norm parameters as function arguments --- sb3_contrib/crossq/policies.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index 9b1c7726..7bba8190 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -62,6 +62,8 @@ def __init__( clip_mean: float = 2.0, normalize_images: bool = True, batch_norm: bool = True, + batch_norm_momentum: float = 0.01, + batch_norm_eps: float = 0.001 ): super().__init__( observation_space, @@ -90,9 +92,9 @@ def __init__( net: List[Union[nn.Module, BatchRenorm1d]] = [] for layer in latent_pi_net: if isinstance(layer, nn.Linear): - net.append(BatchRenorm1d(layer.in_features, eps=0.001, momentum=0.01)) + net.append(BatchRenorm1d(layer.in_features, eps=batch_norm_eps, momentum=batch_norm_momentum)) net.append(layer) - net.append(BatchRenorm1d(net_arch[-1], eps=0.001, momentum=0.01)) + net.append(BatchRenorm1d(net_arch[-1], eps=batch_norm_eps, momentum=batch_norm_momentum)) latent_pi_net = net self.latent_pi = nn.Sequential(*latent_pi_net) @@ -226,6 +228,8 @@ def __init__( n_critics: int = 2, share_features_extractor: bool = True, batch_norm: bool = True, + batch_norm_momentum: float = 0.01, + batch_norm_eps: float = 0.001 ): super().__init__( observation_space, @@ -247,7 +251,7 @@ def __init__( net: List[Union[nn.Module, BatchRenorm1d]] = [] for layer in q_net_list: if isinstance(layer, nn.Linear): - net.append(BatchRenorm1d(layer.in_features, eps=0.001, momentum=0.01)) + net.append(BatchRenorm1d(layer.in_features, eps=batch_norm_eps, momentum=batch_norm_momentum)) net.append(layer) q_net_list = net From aace2acc195b5552b0b51b74f6f7573bef11aba3 Mon Sep 17 00:00:00 2001 From: Daniel Palenicek Date: Sun, 12 May 2024 03:15:54 +0200 Subject: [PATCH 06/32] clean up. reshape instead of split --- sb3_contrib/crossq/crossq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py index 54d31b61..98982d11 100644 --- a/sb3_contrib/crossq/crossq.py +++ b/sb3_contrib/crossq/crossq.py @@ -254,7 +254,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: all_q_values = th.cat(self.critic(all_obs, all_acts), dim=1) self.critic.set_training_mode(False) current_q_values, next_q_values = th.split(all_q_values, batch_size, dim=0) - current_q_values = th.split(current_q_values, [1, 1], dim=1) # type: ignore + current_q_values = current_q_values.T[..., None] with th.no_grad(): # Compute the target Q value From 4df7111feaa2e76ef1125b71f2c723cd8c9d8455 Mon Sep 17 00:00:00 2001 From: Daniel Palenicek Date: Sun, 12 May 2024 03:16:15 +0200 Subject: [PATCH 07/32] Added policy delay --- sb3_contrib/crossq/crossq.py | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py index 98982d11..2848bf0e 100644 --- a/sb3_contrib/crossq/crossq.py +++ b/sb3_contrib/crossq/crossq.py @@ -89,6 +89,7 @@ def __init__( optimize_memory_usage: bool = False, ent_coef: Union[str, float] = "auto", target_entropy: Union[str, float] = "auto", + policy_delay: int = 3, use_sde: bool = False, sde_sample_freq: int = -1, use_sde_at_warmup: bool = False, @@ -134,6 +135,7 @@ def __init__( # Inverse of the reward scale self.ent_coef = ent_coef self.ent_coef_optimizer: Optional[th.optim.Adam] = None + self.policy_delay = policy_delay if _init_setup_model: self._setup_model() @@ -198,9 +200,10 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: if self.use_sde: self.actor.reset_noise() - # Note, in the following lines we always need to make sure to set train/eval modes + # Note: in the following lines we always need to make sure to set train/eval modes # of actor and critic carefully. This is because of the BatchNorm layers in the networks # which behave differently in train and eval modes. + self.actor.set_training_mode(True) actions_pi, log_prob = self.actor.action_log_prob(replay_data.observations) log_prob = log_prob.reshape(-1, 1) @@ -221,7 +224,9 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: # Optimize entropy coefficient, also called # entropy temperature or alpha in the paper - if ent_coef_loss is not None and self.ent_coef_optimizer is not None: + if ent_coef_loss is not None and self.ent_coef_optimizer is not None \ + and self._n_updates % self.policy_delay == 0: + self.ent_coef_optimizer.zero_grad() ent_coef_loss.backward() self.ent_coef_optimizer.step() @@ -276,19 +281,20 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: critic_loss.backward() self.critic.optimizer.step() - # Compute actor loss - self.critic.set_training_mode(False) - q_values_pi = th.cat(self.critic(replay_data.observations, actions_pi), dim=1) - self.critic.set_training_mode(False) + if self._n_updates % self.policy_delay == 0: + # Compute actor loss + self.critic.set_training_mode(False) + q_values_pi = th.cat(self.critic(replay_data.observations, actions_pi), dim=1) + self.critic.set_training_mode(False) - min_qf_pi, _ = th.min(q_values_pi, dim=1, keepdim=True) - actor_loss = (ent_coef * log_prob.reshape(-1, 1) - min_qf_pi).mean() - actor_losses.append(actor_loss.item()) + min_qf_pi, _ = th.min(q_values_pi, dim=1, keepdim=True) + actor_loss = (ent_coef * log_prob.reshape(-1, 1) - min_qf_pi).mean() + actor_losses.append(actor_loss.item()) - # Optimize the actor - self.actor.optimizer.zero_grad() - actor_loss.backward() - self.actor.optimizer.step() + # Optimize the actor + self.actor.optimizer.zero_grad() + actor_loss.backward() + self.actor.optimizer.step() self._n_updates += gradient_steps From 5583225931daebcb603edb259b0bba7886371f11 Mon Sep 17 00:00:00 2001 From: Daniel Palenicek Date: Sun, 12 May 2024 15:26:16 +0200 Subject: [PATCH 08/32] fixed commit-checks --- sb3_contrib/common/network_layers.py | 4 ++-- sb3_contrib/crossq/crossq.py | 7 +++---- sb3_contrib/crossq/policies.py | 4 ++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/sb3_contrib/common/network_layers.py b/sb3_contrib/common/network_layers.py index 5bb3b263..7d1e715b 100644 --- a/sb3_contrib/common/network_layers.py +++ b/sb3_contrib/common/network_layers.py @@ -57,10 +57,10 @@ def _check_input_dim(self, x: torch.Tensor) -> None: def forward(self, x: torch.Tensor) -> torch.Tensor: """ Normalize the input tensor. - + Args: x: Input tensor - + Returns: Normalized tensor. """ diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py index 2848bf0e..b5a5fd0d 100644 --- a/sb3_contrib/crossq/crossq.py +++ b/sb3_contrib/crossq/crossq.py @@ -192,7 +192,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: ent_coef_losses, ent_coefs = [], [] actor_losses, critic_losses = [], [] - for gradient_step in range(gradient_steps): + for _ in range(gradient_steps): # Sample replay buffer replay_data = self.replay_buffer.sample(batch_size, env=self._vec_normalize_env) # type: ignore[union-attr] @@ -224,8 +224,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: # Optimize entropy coefficient, also called # entropy temperature or alpha in the paper - if ent_coef_loss is not None and self.ent_coef_optimizer is not None \ - and self._n_updates % self.policy_delay == 0: + if ent_coef_loss is not None and self.ent_coef_optimizer is not None and self._n_updates % self.policy_delay == 0: self.ent_coef_optimizer.zero_grad() ent_coef_loss.backward() @@ -324,7 +323,7 @@ def learn( ) def _excluded_save_params(self) -> List[str]: - return super()._excluded_save_params() + ["actor", "critic"] + return [*super()._excluded_save_params(), "actor", "critic"] def _get_torch_save_params(self) -> Tuple[List[str], List[str]]: state_dicts = ["policy", "actor.optimizer", "critic.optimizer"] diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index 7bba8190..dfd6cff0 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -63,7 +63,7 @@ def __init__( normalize_images: bool = True, batch_norm: bool = True, batch_norm_momentum: float = 0.01, - batch_norm_eps: float = 0.001 + batch_norm_eps: float = 0.001, ): super().__init__( observation_space, @@ -229,7 +229,7 @@ def __init__( share_features_extractor: bool = True, batch_norm: bool = True, batch_norm_momentum: float = 0.01, - batch_norm_eps: float = 0.001 + batch_norm_eps: float = 0.001, ): super().__init__( observation_space, From 567c2fb8a50ee3904e71e1958e6a698a7f5bfcd5 Mon Sep 17 00:00:00 2001 From: Antonin Raffin Date: Mon, 13 May 2024 10:09:28 +0200 Subject: [PATCH 09/32] Fix f-string --- sb3_contrib/common/network_layers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sb3_contrib/common/network_layers.py b/sb3_contrib/common/network_layers.py index 7d1e715b..03d2f192 100644 --- a/sb3_contrib/common/network_layers.py +++ b/sb3_contrib/common/network_layers.py @@ -37,6 +37,7 @@ def __init__( warmup_steps: int = 100_000, ): super().__init__() + # Running average mean and variance self.register_buffer("ra_mean", torch.zeros(num_features, dtype=torch.float)) self.register_buffer("ra_var", torch.ones(num_features, dtype=torch.float)) self.register_buffer("steps", torch.tensor(0, dtype=torch.long)) @@ -114,4 +115,4 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: class BatchRenorm1d(BatchRenorm): def _check_input_dim(self, x: torch.Tensor) -> None: if x.dim() == 1: - raise ValueError("expected 2D or 3D input (got {x.dim()}D input)") + raise ValueError(f"Expected 2D or 3D input (got {x.dim()}D input)") From 8970ed087a610989dc6e34d62679c5f90fad82f7 Mon Sep 17 00:00:00 2001 From: Antonin Raffin Date: Mon, 13 May 2024 10:09:38 +0200 Subject: [PATCH 10/32] Update documentation --- docs/guide/examples.rst | 13 +++++++++++++ docs/misc/changelog.rst | 26 +++++++++++++++++++++++++- docs/modules/crossq.rst | 8 ++++---- sb3_contrib/version.txt | 2 +- setup.py | 6 ++++-- 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/docs/guide/examples.rst b/docs/guide/examples.rst index 83546cf6..5cd4d949 100644 --- a/docs/guide/examples.rst +++ b/docs/guide/examples.rst @@ -113,3 +113,16 @@ Train a PPO agent with a recurrent policy on the CartPole environment. obs, rewards, dones, info = vec_env.step(action) episode_starts = dones vec_env.render("human") + + CrossQ + ------ + + Train a CrossQ agent on the Pendulum environment. + + .. code-block:: python + + from sb3_contrib import CrossQ + + model = CrossQ("MlpPolicy", "Pendulum-v1", verbose=1, policy_kwargs=dict(net_arch=dict(pi=[256, 256], qf=[1024, 1024]))) + model.learn(total_timesteps=5_000, log_interval=4) + model.save("crossq_pendulum") diff --git a/docs/misc/changelog.rst b/docs/misc/changelog.rst index 109b2eb5..f575792a 100644 --- a/docs/misc/changelog.rst +++ b/docs/misc/changelog.rst @@ -3,6 +3,30 @@ Changelog ========== + +Release 2.4.0a0 (WIP) +-------------------------- + +Breaking Changes: +^^^^^^^^^^^^^^^^^ + +New Features: +^^^^^^^^^^^^^ +- Added CrossQ (@danielpalen) + +Bug Fixes: +^^^^^^^^^^ + +Deprecations: +^^^^^^^^^^^^^ + +Others: +^^^^^^^ + +Documentation: +^^^^^^^^^^^^^^ + + Release 2.3.0 (2024-03-31) -------------------------- @@ -554,4 +578,4 @@ Contributors: ------------- @ku2482 @guyk1971 @minhlong94 @ayeright @kronion @glmcdona @cyprienc @sgillen @Gregwar @rnederstigt @qgallouedec -@mlodel @CppMaster @burakdmb @honglu2875 @ZikangXiong @AlexPasqua @jonasreiher @icheered @Armandpl +@mlodel @CppMaster @burakdmb @honglu2875 @ZikangXiong @AlexPasqua @jonasreiher @icheered @Armandpl @danielpalen diff --git a/docs/modules/crossq.rst b/docs/modules/crossq.rst index e81aaa25..4e2e24da 100644 --- a/docs/modules/crossq.rst +++ b/docs/modules/crossq.rst @@ -21,6 +21,10 @@ This yield a simpler and more sample-efficient algorithm without requiring high MlpPolicy +.. note:: + + Compared to the original implementation, the default network architecture for the q-value function is ``[1024, 1024]`` + instead of ``[2048, 2048]``` as it provides a good compromise between speed and performance. Notes ----- @@ -53,9 +57,6 @@ Example .. code-block:: python - import gymnasium as gym - import numpy as np - from sb3_contrib import CrossQ model = CrossQ("MlpPolicy", "Walker2d-v4") @@ -96,4 +97,3 @@ CrossQ Policies .. autoclass:: sb3_contrib.crossq.policies.CrossQPolicy :members: :noindex: - diff --git a/sb3_contrib/version.txt b/sb3_contrib/version.txt index 276cbf9e..e96f44fb 100644 --- a/sb3_contrib/version.txt +++ b/sb3_contrib/version.txt @@ -1 +1 @@ -2.3.0 +2.4.0a0 diff --git a/setup.py b/setup.py index 49b24588..d2e02c07 100644 --- a/setup.py +++ b/setup.py @@ -30,11 +30,13 @@ See documentation for the full list of included features. **RL Algorithms**: -- [Truncated Quantile Critics (TQC)](https://arxiv.org/abs/2005.04269) +- [Augmented Random Search (ARS)](https://arxiv.org/abs/1803.07055) - [Quantile Regression DQN (QR-DQN)](https://arxiv.org/abs/1710.10044) - [PPO with invalid action masking (MaskablePPO)](https://arxiv.org/abs/2006.14171) +- [PPO with recurrent policy (RecurrentPPO aka PPO LSTM)](https://ppo-details.cleanrl.dev//2021/11/05/ppo-implementation-details/) +- [Truncated Quantile Critics (TQC)](https://arxiv.org/abs/2005.04269) - [Trust Region Policy Optimization (TRPO)](https://arxiv.org/abs/1502.05477) -- [Augmented Random Search (ARS)](https://arxiv.org/abs/1803.07055) +- [Batch Normalization in Deep Reinforcement Learning (CrossQ)](https://openreview.net/forum?id=PczQtTsTIX) **Gym Wrappers**: - [Time Feature Wrapper](https://arxiv.org/abs/1712.00378) From 8792621ab0732a44efd2f3e8fb5ae370442c614a Mon Sep 17 00:00:00 2001 From: Antonin Raffin Date: Mon, 13 May 2024 10:27:05 +0200 Subject: [PATCH 11/32] Rename to torch layers --- sb3_contrib/common/{network_layers.py => torch_layers.py} | 0 sb3_contrib/crossq/policies.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename sb3_contrib/common/{network_layers.py => torch_layers.py} (100%) diff --git a/sb3_contrib/common/network_layers.py b/sb3_contrib/common/torch_layers.py similarity index 100% rename from sb3_contrib/common/network_layers.py rename to sb3_contrib/common/torch_layers.py diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index dfd6cff0..17fe1820 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -14,7 +14,7 @@ from stable_baselines3.common.type_aliases import PyTorchObs, Schedule from torch import nn -from sb3_contrib.common.network_layers import BatchRenorm1d +from sb3_contrib.common.torch_layers import BatchRenorm1d # CAP the standard deviation of the actor LOG_STD_MAX = 2 From 230a94889cae4481971b22e2af26b9ae1fd54154 Mon Sep 17 00:00:00 2001 From: Antonin Raffin Date: Mon, 13 May 2024 10:27:21 +0200 Subject: [PATCH 12/32] Fix for policy delay and minor edits --- sb3_contrib/crossq/crossq.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py index b5a5fd0d..8db3b7a5 100644 --- a/sb3_contrib/crossq/crossq.py +++ b/sb3_contrib/crossq/crossq.py @@ -8,7 +8,6 @@ from stable_baselines3.common.off_policy_algorithm import OffPolicyAlgorithm from stable_baselines3.common.policies import BasePolicy from stable_baselines3.common.type_aliases import GymEnv, MaybeCallback, Schedule -from stable_baselines3.common.utils import get_parameters_by_name from torch.nn import functional as F from sb3_contrib.crossq.policies import Actor, CrossQCritic, CrossQPolicy, MlpPolicy @@ -18,8 +17,8 @@ class CrossQ(OffPolicyAlgorithm): """ - Implementation of the CrossQ algorithm. - Paper: https://openreview.net/pdf?id=PczQtTsTIX + Implementation of Batch Normalization in Deep Reinforcement Learning (CrossQ). + Paper: https://openreview.net/forum?id=PczQtTsTIX :param policy: The policy model to use (MlpPolicy) :param env: The environment to learn from (if registered in Gym, can be str) @@ -143,8 +142,6 @@ def __init__( def _setup_model(self) -> None: super()._setup_model() self._create_aliases() - # Running mean and running var - self.batch_norm_stats = get_parameters_by_name(self.critic, ["running_"]) # Target entropy is used when learning the entropy coefficient if self.target_entropy == "auto": # automatically set target entropy if needed @@ -193,6 +190,9 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: actor_losses, critic_losses = [], [] for _ in range(gradient_steps): + self._n_updates += 1 + # delayed updates + update_actor_and_temperature = self._n_updates % self.policy_delay == 0 # Sample replay buffer replay_data = self.replay_buffer.sample(batch_size, env=self._vec_normalize_env) # type: ignore[union-attr] @@ -224,7 +224,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: # Optimize entropy coefficient, also called # entropy temperature or alpha in the paper - if ent_coef_loss is not None and self.ent_coef_optimizer is not None and self._n_updates % self.policy_delay == 0: + if ent_coef_loss is not None and self.ent_coef_optimizer is not None and update_actor_and_temperature: self.ent_coef_optimizer.zero_grad() ent_coef_loss.backward() @@ -258,6 +258,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: all_q_values = th.cat(self.critic(all_obs, all_acts), dim=1) self.critic.set_training_mode(False) current_q_values, next_q_values = th.split(all_q_values, batch_size, dim=0) + # (batch_size, n_critics) -> (n_critics, batch_size, 1) current_q_values = current_q_values.T[..., None] with th.no_grad(): @@ -280,7 +281,8 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: critic_loss.backward() self.critic.optimizer.step() - if self._n_updates % self.policy_delay == 0: + # Delayed policy updates + if update_actor_and_temperature: # Compute actor loss self.critic.set_training_mode(False) q_values_pi = th.cat(self.critic(replay_data.observations, actions_pi), dim=1) @@ -295,11 +297,10 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: actor_loss.backward() self.actor.optimizer.step() - self._n_updates += gradient_steps - self.logger.record("train/n_updates", self._n_updates, exclude="tensorboard") self.logger.record("train/ent_coef", np.mean(ent_coefs)) - self.logger.record("train/actor_loss", np.mean(actor_losses)) + if len(actor_losses) > 0: + self.logger.record("train/actor_loss", np.mean(actor_losses)) self.logger.record("train/critic_loss", np.mean(critic_losses)) if len(ent_coef_losses) > 0: self.logger.record("train/ent_coef_loss", np.mean(ent_coef_losses)) From cd8bd7dea1b244e8e8b3e169ab5548240f4d7df5 Mon Sep 17 00:00:00 2001 From: Antonin Raffin Date: Mon, 13 May 2024 10:27:37 +0200 Subject: [PATCH 13/32] Update tests --- tests/test_run.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/tests/test_run.py b/tests/test_run.py index ed524f22..50e6ac3c 100644 --- a/tests/test_run.py +++ b/tests/test_run.py @@ -8,14 +8,9 @@ from sb3_contrib.common.vec_env import AsyncEval -def test(): - model = CrossQ( - "MlpPolicy", - "Pendulum-v1", - learning_starts=100, - verbose=1, - ) - model.learn(total_timesteps=300) +def test_crossq(): + model = CrossQ("MlpPolicy", "Pendulum-v1", learning_starts=100, verbose=1, policy_kwargs=dict(net_arch=[64])) + model.learn(total_timesteps=110) @pytest.mark.parametrize("ent_coef", ["auto", 0.01, "auto_0.01"]) @@ -28,7 +23,7 @@ def test_tqc(ent_coef): verbose=1, ent_coef=ent_coef, ) - model.learn(total_timesteps=300, progress_bar=True) + model.learn(total_timesteps=110, progress_bar=True) @pytest.mark.parametrize("n_critics", [1, 3]) @@ -41,7 +36,7 @@ def test_n_critics(n_critics): learning_starts=100, verbose=1, ) - model.learn(total_timesteps=300) + model.learn(total_timesteps=110) def test_sde(): @@ -53,7 +48,7 @@ def test_sde(): learning_starts=100, verbose=1, ) - model.learn(total_timesteps=300) + model.learn(total_timesteps=110) model.policy.reset_noise() model.policy.actor.get_std() @@ -62,7 +57,7 @@ def test_qrdqn(): model = QRDQN( "MlpPolicy", "CartPole-v1", - policy_kwargs=dict(n_quantiles=25, net_arch=[64, 64]), + policy_kwargs=dict(n_quantiles=25, net_arch=[64]), learning_starts=100, buffer_size=500, learning_rate=3e-4, From 27a96f647234716558dc685c354efb242cbd7b18 Mon Sep 17 00:00:00 2001 From: Antonin Raffin Date: Mon, 13 May 2024 10:36:18 +0200 Subject: [PATCH 14/32] Update documentation --- docs/common/torch_layers.rst | 7 +++++++ docs/guide/examples.rst | 16 ++++++++-------- docs/index.rst | 1 + docs/modules/crossq.rst | 5 +++-- sb3_contrib/common/torch_layers.py | 20 ++++++++------------ 5 files changed, 27 insertions(+), 22 deletions(-) create mode 100644 docs/common/torch_layers.rst diff --git a/docs/common/torch_layers.rst b/docs/common/torch_layers.rst new file mode 100644 index 00000000..a6d39a27 --- /dev/null +++ b/docs/common/torch_layers.rst @@ -0,0 +1,7 @@ +.. _th_layers: + +Torch Layers +============ + +.. automodule:: sb3_contrib.common.torch_layers + :members: diff --git a/docs/guide/examples.rst b/docs/guide/examples.rst index 5cd4d949..ebe80446 100644 --- a/docs/guide/examples.rst +++ b/docs/guide/examples.rst @@ -114,15 +114,15 @@ Train a PPO agent with a recurrent policy on the CartPole environment. episode_starts = dones vec_env.render("human") - CrossQ - ------ +CrossQ +------ - Train a CrossQ agent on the Pendulum environment. +Train a CrossQ agent on the Pendulum environment. - .. code-block:: python +.. code-block:: python - from sb3_contrib import CrossQ + from sb3_contrib import CrossQ - model = CrossQ("MlpPolicy", "Pendulum-v1", verbose=1, policy_kwargs=dict(net_arch=dict(pi=[256, 256], qf=[1024, 1024]))) - model.learn(total_timesteps=5_000, log_interval=4) - model.save("crossq_pendulum") + model = CrossQ("MlpPolicy", "Pendulum-v1", verbose=1, policy_kwargs=dict(net_arch=dict(pi=[256, 256], qf=[1024, 1024]))) + model.learn(total_timesteps=5_000, log_interval=4) + model.save("crossq_pendulum") diff --git a/docs/index.rst b/docs/index.rst index 3e7c7a4e..ff32f1fe 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,6 +43,7 @@ RL Baselines3 Zoo also offers a simple interface to train, evaluate agents and d :maxdepth: 1 :caption: Common + common/torch_layers common/utils common/wrappers diff --git a/docs/modules/crossq.rst b/docs/modules/crossq.rst index 4e2e24da..51c507c1 100644 --- a/docs/modules/crossq.rst +++ b/docs/modules/crossq.rst @@ -24,13 +24,14 @@ This yield a simpler and more sample-efficient algorithm without requiring high .. note:: Compared to the original implementation, the default network architecture for the q-value function is ``[1024, 1024]`` - instead of ``[2048, 2048]``` as it provides a good compromise between speed and performance. + instead of ``[2048, 2048]`` as it provides a good compromise between speed and performance. Notes ----- - Original paper: https://openreview.net/pdf?id=PczQtTsTIX - Original Implementation: https://github.com/adityab/CrossQ +- SBX Implementation: https://github.com/araffin/sbx Can I use? @@ -68,7 +69,7 @@ Results ------- Performance evaluation of CrossQ on six MuJoCo environments. -Compared to results from the original paper as well as a version from SBX. +Compared to results from the original paper as well as a version from `SBX `_. .. image:: ../images/crossQ_performance.png diff --git a/sb3_contrib/common/torch_layers.py b/sb3_contrib/common/torch_layers.py index 03d2f192..64dec401 100644 --- a/sb3_contrib/common/torch_layers.py +++ b/sb3_contrib/common/torch_layers.py @@ -1,6 +1,6 @@ import torch -__all__ = ["BatchRenorm1d"] +__all__ = ["BatchRenorm1d", "BatchRenorm"] class BatchRenorm(torch.jit.ScriptModule): @@ -18,13 +18,12 @@ class BatchRenorm(torch.jit.ScriptModule): training mode. During evaluation mode, the running statistics are used for normalization but not updated. - Args: - num_features: Number of features in the input tensor. - eps: A value added to the variance for numerical stability. - momentum: The value used for the ra_mean and ra_var computation. - affine: A boolean value that when set to True, this module has learnable + :param num_features: Number of features in the input tensor. + :param eps: A value added to the variance for numerical stability. + :param momentum: The value used for the ra_mean and ra_var computation. + :param affine: A boolean value that when set to True, this module has learnable affine parameters. Default: True - warmup_steps: Number of warum steps that are performed before the running statistics + :param warmup_steps: Number of warum steps that are performed before the running statistics are used form normalization. During the warump phase, the batch statistics are used. """ @@ -59,11 +58,8 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: """ Normalize the input tensor. - Args: - x: Input tensor - - Returns: - Normalized tensor. + :param x: Input tensor + :return: Normalized tensor. """ if self.training: From 3927a70b262d32c755367379790aa6ed4b8f2c2d Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Sat, 6 Jul 2024 19:15:14 +0200 Subject: [PATCH 15/32] Update doc --- docs/guide/algos.rst | 1 + docs/misc/changelog.rst | 5 +++-- docs/modules/crossq.rst | 38 +++++++++++++++++++++++++++++++++++--- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/docs/guide/algos.rst b/docs/guide/algos.rst index 8181382e..48e8fa4f 100644 --- a/docs/guide/algos.rst +++ b/docs/guide/algos.rst @@ -10,6 +10,7 @@ Name ``Box`` ``Discrete`` ``MultiDiscrete`` ``MultiBinary`` Multi Pr ============ =========== ============ ================= =============== ================ ARS ✔️ ❌️ ❌ ❌ ✔️ MaskablePPO ❌ ✔️ ✔️ ✔️ ✔️ +CrossQ ✔️ ❌ ❌ ❌ ✔️ QR-DQN ️❌ ️✔️ ❌ ❌ ✔️ RecurrentPPO ✔️ ✔️ ✔️ ✔️ ✔️ TQC ✔️ ❌ ❌ ❌ ✔️ diff --git a/docs/misc/changelog.rst b/docs/misc/changelog.rst index 5dd41838..c435fc48 100644 --- a/docs/misc/changelog.rst +++ b/docs/misc/changelog.rst @@ -3,7 +3,7 @@ Changelog ========== -Release 2.4.0a4 (WIP) +Release 2.4.0a5 (WIP) -------------------------- Breaking Changes: @@ -12,7 +12,8 @@ Breaking Changes: New Features: ^^^^^^^^^^^^^ -- Added CrossQ (@danielpalen) +- Added ``CrossQ`` algorithm, from "Batch Normalization in Deep Reinforcement Learning" paper (@danielpalen) +- Added ``BatchRenorm`` PyTorch layer used in ``CrossQ`` (@danielpalen) Bug Fixes: ^^^^^^^^^^ diff --git a/docs/modules/crossq.rst b/docs/modules/crossq.rst index 51c507c1..719f5ec4 100644 --- a/docs/modules/crossq.rst +++ b/docs/modules/crossq.rst @@ -25,13 +25,19 @@ This yield a simpler and more sample-efficient algorithm without requiring high Compared to the original implementation, the default network architecture for the q-value function is ``[1024, 1024]`` instead of ``[2048, 2048]`` as it provides a good compromise between speed and performance. + We also use the default hyperparameters for Adam, since they have little impact on performance. + +.. note:: + + There is currently no ``CnnPolicy`` for using CrossQ with images. We welcome help from contributors to add this feature. + Notes ----- - Original paper: https://openreview.net/pdf?id=PczQtTsTIX - Original Implementation: https://github.com/adityab/CrossQ -- SBX Implementation: https://github.com/araffin/sbx +- SBX (SB3 Jax) Implementation: https://github.com/araffin/sbx Can I use? @@ -49,7 +55,7 @@ Discrete ❌ ✔️ Box ✔️ ✔️ MultiDiscrete ❌ ✔️ MultiBinary ❌ ✔️ -Dict ❌ ✔️ +Dict ❌ ❌ ============= ====== =========== @@ -68,11 +74,37 @@ Example Results ------- -Performance evaluation of CrossQ on six MuJoCo environments. +Performance evaluation of CrossQ on six MuJoCo environments, see `PR #243 `_. Compared to results from the original paper as well as a version from `SBX `_. .. image:: ../images/crossQ_performance.png + +How to replicate the results? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Clone RL-Zoo: + +.. code-block:: bash + + git clone https://github.com/DLR-RM/rl-baselines3-zoo + cd rl-baselines3-zoo/ + +Run the benchmark (replace ``$ENV_ID`` by the envs mentioned above): + +.. code-block:: bash + + python train.py --algo crossq --env $ENV_ID --n-eval-envs 5 --eval-episodes 20 --eval-freq 25000 + + +Plot the results: + +.. code-block:: bash + + python scripts/all_plots.py -a crossq -e HalfCheetah Ant Hopper Walker2D -f logs/ -o logs/crossq_results + python scripts/plot_from_file.py -i logs/crossq_results.pkl -latex -l CrossQ + + Comments -------- From 2019327dce57986af71e4a6ac9d94d79fe5446be Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Sat, 6 Jul 2024 19:15:24 +0200 Subject: [PATCH 16/32] Add more tests for crossQ --- tests/test_run.py | 15 +++++++++++---- tests/test_save_load.py | 21 ++++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/tests/test_run.py b/tests/test_run.py index 50e6ac3c..b31ed445 100644 --- a/tests/test_run.py +++ b/tests/test_run.py @@ -9,7 +9,13 @@ def test_crossq(): - model = CrossQ("MlpPolicy", "Pendulum-v1", learning_starts=100, verbose=1, policy_kwargs=dict(net_arch=[64])) + model = CrossQ( + "MlpPolicy", + "Pendulum-v1", + learning_starts=100, + verbose=1, + policy_kwargs=dict(net_arch=[32], renorm_warmup_steps=1), + ) model.learn(total_timesteps=110) @@ -39,11 +45,12 @@ def test_n_critics(n_critics): model.learn(total_timesteps=110) -def test_sde(): - model = TQC( +@pytest.mark.parametrize("model_class", [TQC, CrossQ]) +def test_sde(model_class): + model = model_class( "MlpPolicy", "Pendulum-v1", - policy_kwargs=dict(net_arch=[64]), + policy_kwargs=dict(net_arch=[16]), use_sde=True, learning_starts=100, verbose=1, diff --git a/tests/test_save_load.py b/tests/test_save_load.py index 502d2394..693169ea 100644 --- a/tests/test_save_load.py +++ b/tests/test_save_load.py @@ -12,9 +12,16 @@ from stable_baselines3.common.utils import get_device from stable_baselines3.common.vec_env import DummyVecEnv -from sb3_contrib import ARS, QRDQN, TQC, TRPO +from sb3_contrib import ARS, QRDQN, TQC, TRPO, CrossQ -MODEL_LIST = [ARS, QRDQN, TQC, TRPO] +MODEL_LIST = [ARS, QRDQN, TQC, TRPO, CrossQ] + + +def rand_like(tensor: th.Tensor) -> th.Tensor: + if th.is_floating_point(tensor): + return th.rand_like(tensor) + # int tensor + return th.randint_like(tensor, high=10) def select_env(model_class: BaseAlgorithm) -> gym.Env: @@ -93,7 +100,7 @@ def test_save_load(tmp_path, model_class): else: # Again, skip the last item in state-dict random_params[object_name] = OrderedDict( - (param_name, th.rand_like(param)) for param_name, param in list(params.items())[:-1] + (param_name, rand_like(param)) for param_name, param in list(params.items())[:-1] ) # Update model parameters with the new random values @@ -267,8 +274,8 @@ def test_save_load_policy(tmp_path, model_class, policy_str): """ kwargs = dict(policy_kwargs=dict(net_arch=[16])) - if policy_str == "CnnPolicy" and model_class is ARS: - pytest.skip("ARS does not support CnnPolicy") + if policy_str == "CnnPolicy" and model_class in [ARS, CrossQ]: + pytest.skip(f"{model_class.__name__} does not support CnnPolicy") if policy_str == "MlpPolicy": env = select_env(model_class) @@ -312,7 +319,7 @@ def test_save_load_policy(tmp_path, model_class, policy_str): params = deepcopy(policy.state_dict()) # Modify all parameters to be random values - random_params = {param_name: th.rand_like(param) for param_name, param in params.items()} + random_params = {param_name: rand_like(param) for param_name, param in params.items()} # Update model parameters with the new random values policy.load_state_dict(random_params) @@ -409,7 +416,7 @@ def test_save_load_q_net(tmp_path, model_class, policy_str): params = deepcopy(q_net.state_dict()) # Modify all parameters to be random values - random_params = {param_name: th.rand_like(param) for param_name, param in params.items()} + random_params = {param_name: rand_like(param) for param_name, param in params.items()} # Update model parameters with the new random values q_net.load_state_dict(random_params) From b0213ec3b6eb3da6963463a152e4527e30fa6cdb Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Sat, 6 Jul 2024 19:15:43 +0200 Subject: [PATCH 17/32] Improve doc and expose batchnorm params --- sb3_contrib/common/torch_layers.py | 19 +++++++----- sb3_contrib/crossq/policies.py | 49 +++++++++++++++++++++++++++--- sb3_contrib/version.txt | 2 +- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/sb3_contrib/common/torch_layers.py b/sb3_contrib/common/torch_layers.py index 64dec401..9a73eddf 100644 --- a/sb3_contrib/common/torch_layers.py +++ b/sb3_contrib/common/torch_layers.py @@ -6,7 +6,8 @@ class BatchRenorm(torch.jit.ScriptModule): """ BatchRenorm Module (https://arxiv.org/abs/1702.03275). - Adapted to Pytorch from sbx.sbx.common.jax_layers.BatchRenorm + Adapted to Pytorch from + https://github.com/araffin/sbx/blob/master/sbx/common/jax_layers.py BatchRenorm is an improved version of vanilla BatchNorm. Contrary to BatchNorm, BatchRenorm uses the running statistics for normalizing the batches after a warmup phase. @@ -20,11 +21,12 @@ class BatchRenorm(torch.jit.ScriptModule): :param num_features: Number of features in the input tensor. :param eps: A value added to the variance for numerical stability. - :param momentum: The value used for the ra_mean and ra_var computation. + :param momentum: The value used for the ra_mean and ra_var (running average) computation. + It controls the rate of convergence for the batch renormalization statistics. :param affine: A boolean value that when set to True, this module has learnable affine parameters. Default: True :param warmup_steps: Number of warum steps that are performed before the running statistics - are used form normalization. During the warump phase, the batch statistics are used. + are used for normalization. During the warump phase, the batch statistics are used. """ def __init__( @@ -47,6 +49,7 @@ def __init__( self.eps = eps self.step = 0 self.momentum = momentum + # Clip scale and bias of the affine transform self.rmax = 3.0 self.dmax = 5.0 self.warmup_steps = warmup_steps @@ -63,8 +66,8 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: """ if self.training: - batch_mean = x.mean(0) - batch_var = x.var(0) + batch_mean = x.mean(dim=0) + batch_var = x.var(dim=0) batch_std = (batch_var + self.eps).sqrt() # Use batch statistics during initial warm up phase. @@ -82,9 +85,6 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: d = d.clamp(-self.dmax, self.dmax) # BatchNorm normalization, using minibatch stats and running average stats - # Because we use _normalize, this is equivalent to - # ((x - x_mean) / sigma) * r + d = ((x - x_mean) * r + d * sigma) / sigma - # where sigma = sqrt(var) custom_mean = batch_mean - d * batch_var.sqrt() / r custom_var = batch_var / (r**2) @@ -107,6 +107,9 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: return x + # def extra_repr(self) -> str: + # return f"num_features={self.num_features}, momentum={self.momentum}, warmup_steps={self.warmup_steps}" + class BatchRenorm1d(BatchRenorm): def _check_input_dim(self, x: torch.Tensor) -> None: diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index 17fe1820..1bc3e404 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -43,6 +43,11 @@ class Actor(BasePolicy): :param clip_mean: Clip the mean output when using gSDE to avoid numerical instability. :param normalize_images: Whether to normalize images or not, dividing by 255.0 (True by default) + :param batch_norm: Whether to use Batch Renorm layers (default=True) + :param batch_norm_momentum: The rate of convergence for the batch renormalization statistics + :param batch_norm_eps: A small value added to the variance to prevent division by zero + :param renorm_warmup_steps: Number of steps to warm up BatchRenorm statistics before the running statistics + are used for normalization. """ action_space: spaces.Box @@ -64,6 +69,7 @@ def __init__( batch_norm: bool = True, batch_norm_momentum: float = 0.01, batch_norm_eps: float = 0.001, + renorm_warmup_steps: int = 100_000, ): super().__init__( observation_space, @@ -87,14 +93,20 @@ def __init__( action_dim = get_action_dim(self.action_space) latent_pi_net = create_mlp(features_dim, -1, net_arch, activation_fn) + batch_norm_params = { + "momentum": batch_norm_momentum, + "eps": batch_norm_eps, + "warmup_steps": renorm_warmup_steps, + } + if batch_norm: # If batchnorm, then we want to add torch.nn.Batch_Norm layers before every linear layer net: List[Union[nn.Module, BatchRenorm1d]] = [] for layer in latent_pi_net: if isinstance(layer, nn.Linear): - net.append(BatchRenorm1d(layer.in_features, eps=batch_norm_eps, momentum=batch_norm_momentum)) + net.append(BatchRenorm1d(layer.in_features, **batch_norm_params)) net.append(layer) - net.append(BatchRenorm1d(net_arch[-1], eps=batch_norm_eps, momentum=batch_norm_momentum)) + net.append(BatchRenorm1d(net_arch[-1], **batch_norm_params)) latent_pi_net = net self.latent_pi = nn.Sequential(*latent_pi_net) @@ -212,6 +224,11 @@ class CrossQCritic(BaseModel): :param n_critics: Number of critic networks to create. :param share_features_extractor: Whether the features extractor is shared or not between the actor and the critic (this saves computation time) + :param batch_norm: Whether to use Batch Renorm layers (default=True) + :param batch_norm_momentum: The rate of convergence for the batch renormalization statistics + :param batch_norm_eps: A small value added to the variance to prevent division by zero + :param renorm_warmup_steps: Number of steps to warm up BatchRenorm statistics before the running statistics + are used for normalization. """ features_extractor: BaseFeaturesExtractor @@ -230,6 +247,7 @@ def __init__( batch_norm: bool = True, batch_norm_momentum: float = 0.01, batch_norm_eps: float = 0.001, + renorm_warmup_steps: int = 100_000, ): super().__init__( observation_space, @@ -239,6 +257,11 @@ def __init__( ) action_dim = get_action_dim(self.action_space) + batch_norm_params = { + "momentum": batch_norm_momentum, + "eps": batch_norm_eps, + "warmup_steps": renorm_warmup_steps, + } self.share_features_extractor = share_features_extractor self.n_critics = n_critics @@ -251,7 +274,7 @@ def __init__( net: List[Union[nn.Module, BatchRenorm1d]] = [] for layer in q_net_list: if isinstance(layer, nn.Linear): - net.append(BatchRenorm1d(layer.in_features, eps=batch_norm_eps, momentum=batch_norm_momentum)) + net.append(BatchRenorm1d(layer.in_features, **batch_norm_params)) net.append(layer) q_net_list = net @@ -277,6 +300,11 @@ class CrossQPolicy(BasePolicy): :param lr_schedule: Learning rate schedule (could be constant) :param net_arch: The specification of the policy and value networks. :param activation_fn: Activation function + :param batch_norm: Whether to use Batch Renorm layers (default=True) + :param batch_norm_momentum: The rate of convergence for the batch renormalization statistics + :param batch_norm_eps: A small value added to the variance to prevent division by zero + :param renorm_warmup_steps: Number of steps to warm up BatchRenorm statistics before the running statistics + are used for normalization. :param use_sde: Whether to use State Dependent Exploration or not :param log_std_init: Initial value for the log standard deviation :param use_expln: Use ``expln()`` function instead of ``exp()`` when using gSDE to ensure @@ -307,6 +335,10 @@ def __init__( lr_schedule: Schedule, net_arch: Optional[Union[List[int], Dict[str, List[int]]]] = None, activation_fn: Type[nn.Module] = nn.ReLU, + batch_norm: bool = True, + batch_norm_momentum: float = 0.99, + batch_norm_eps: float = 0.001, + renorm_warmup_steps: int = 100_000, use_sde: bool = False, log_std_init: float = -3, use_expln: bool = False, @@ -338,6 +370,13 @@ def __init__( actor_arch, critic_arch = get_actor_critic_arch(net_arch) + self.batch_norm_params = { + "batch_norm": batch_norm, + "batch_norm_momentum": batch_norm_momentum, + "batch_norm_eps": batch_norm_eps, + "renorm_warmup_steps": renorm_warmup_steps, + } + self.net_arch = net_arch self.activation_fn = activation_fn self.net_args = { @@ -346,7 +385,7 @@ def __init__( "net_arch": actor_arch, "activation_fn": self.activation_fn, "normalize_images": normalize_images, - "batch_norm": True, + **self.batch_norm_params, } self.actor_kwargs = self.net_args.copy() @@ -363,7 +402,6 @@ def __init__( "n_critics": n_critics, "net_arch": critic_arch, "share_features_extractor": share_features_extractor, - "batch_norm": True, } ) @@ -413,6 +451,7 @@ def _get_constructor_parameters(self) -> Dict[str, Any]: optimizer_kwargs=self.optimizer_kwargs, features_extractor_class=self.features_extractor_class, features_extractor_kwargs=self.features_extractor_kwargs, + **self.batch_norm_params, ) ) return data diff --git a/sb3_contrib/version.txt b/sb3_contrib/version.txt index 3ebd20b1..a1fd35b5 100644 --- a/sb3_contrib/version.txt +++ b/sb3_contrib/version.txt @@ -1 +1 @@ -2.4.0a4 \ No newline at end of file +2.4.0a5 From 454224d088f9fc79e5f4d4dec1cb38473daf6342 Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Sat, 6 Jul 2024 19:36:31 +0200 Subject: [PATCH 18/32] Add some comments and todos and fix type check --- sb3_contrib/crossq/crossq.py | 17 +++++++++-------- sb3_contrib/crossq/policies.py | 8 +++++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py index 8db3b7a5..bec161d8 100644 --- a/sb3_contrib/crossq/crossq.py +++ b/sb3_contrib/crossq/crossq.py @@ -66,6 +66,7 @@ class CrossQ(OffPolicyAlgorithm): policy_aliases: ClassVar[Dict[str, Type[BasePolicy]]] = { "MlpPolicy": MlpPolicy, + # TODO: Implement CnnPolicy and MultiInputPolicy } policy: CrossQPolicy actor: Actor @@ -107,7 +108,7 @@ def __init__( buffer_size, learning_starts, batch_size, - 1.0, + 1.0, # no target networks, tau=1.0 gamma, train_freq, gradient_steps, @@ -191,7 +192,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: for _ in range(gradient_steps): self._n_updates += 1 - # delayed updates + # Delayed updates update_actor_and_temperature = self._n_updates % self.policy_delay == 0 # Sample replay buffer replay_data = self.replay_buffer.sample(batch_size, env=self._vec_normalize_env) # type: ignore[union-attr] @@ -204,6 +205,8 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: # of actor and critic carefully. This is because of the BatchNorm layers in the networks # which behave differently in train and eval modes. + # TODO: replace with more precise, set_training_mode_bn(), to allow the use of Dropout + # TODO: double check, should be moved with the ent coef to the delayed update? self.actor.set_training_mode(True) actions_pi, log_prob = self.actor.action_log_prob(replay_data.observations) log_prob = log_prob.reshape(-1, 1) @@ -252,10 +255,10 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: # 2. From a computational perspective a single forward pass is simply more efficient than # two sequential forward passes. all_obs = th.cat([replay_data.observations, replay_data.next_observations], dim=0) - all_acts = th.cat([replay_data.actions, next_actions], dim=0) + all_actions = th.cat([replay_data.actions, next_actions], dim=0) self.critic.set_training_mode(True) - all_q_values = th.cat(self.critic(all_obs, all_acts), dim=1) + all_q_values = th.cat(self.critic(all_obs, all_actions), dim=1) self.critic.set_training_mode(False) current_q_values, next_q_values = th.split(all_q_values, batch_size, dim=0) # (batch_size, n_critics) -> (n_critics, batch_size, 1) @@ -263,10 +266,8 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: with th.no_grad(): # Compute the target Q value - next_q_values = next_q_values.detach() - next_q_values, _ = th.min(next_q_values, dim=1, keepdim=True) - - # add entropy term + next_q_values, _ = th.min(next_q_values.detach(), dim=1, keepdim=True) + # Add entropy term next_q_values = next_q_values - ent_coef * next_log_prob.reshape(-1, 1) # td error + entropy term target_q_values = replay_data.rewards + (1 - replay_data.dones) * self.gamma * next_q_values diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index 1bc3e404..85797072 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -99,14 +99,15 @@ def __init__( "warmup_steps": renorm_warmup_steps, } + # TODO(antonin): refactor once we can have batch norm/custom layers easily in SB3 if batch_norm: # If batchnorm, then we want to add torch.nn.Batch_Norm layers before every linear layer net: List[Union[nn.Module, BatchRenorm1d]] = [] for layer in latent_pi_net: if isinstance(layer, nn.Linear): - net.append(BatchRenorm1d(layer.in_features, **batch_norm_params)) + net.append(BatchRenorm1d(layer.in_features, **batch_norm_params)) # type: ignore[arg-type] net.append(layer) - net.append(BatchRenorm1d(net_arch[-1], **batch_norm_params)) + net.append(BatchRenorm1d(net_arch[-1], **batch_norm_params)) # type: ignore[arg-type] latent_pi_net = net self.latent_pi = nn.Sequential(*latent_pi_net) @@ -269,12 +270,13 @@ def __init__( for idx in range(n_critics): q_net_list = create_mlp(features_dim + action_dim, 1, net_arch, activation_fn) + # TODO(antonin): refactor once we can have batch norm/custom layers easily in SB3 if batch_norm: # If batchnorm, then we want to add torch.nn.Batch_Norm layers before every linear layer net: List[Union[nn.Module, BatchRenorm1d]] = [] for layer in q_net_list: if isinstance(layer, nn.Linear): - net.append(BatchRenorm1d(layer.in_features, **batch_norm_params)) + net.append(BatchRenorm1d(layer.in_features, **batch_norm_params)) # type: ignore[arg-type] net.append(layer) q_net_list = net From bbd654caf9e4c54467210ab146081ef6a612e6b3 Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Fri, 19 Jul 2024 14:28:59 +0200 Subject: [PATCH 19/32] Use torch module for BN --- sb3_contrib/common/torch_layers.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/sb3_contrib/common/torch_layers.py b/sb3_contrib/common/torch_layers.py index 9a73eddf..9927cd6a 100644 --- a/sb3_contrib/common/torch_layers.py +++ b/sb3_contrib/common/torch_layers.py @@ -3,7 +3,7 @@ __all__ = ["BatchRenorm1d", "BatchRenorm"] -class BatchRenorm(torch.jit.ScriptModule): +class BatchRenorm(torch.nn.Module): """ BatchRenorm Module (https://arxiv.org/abs/1702.03275). Adapted to Pytorch from @@ -49,6 +49,7 @@ def __init__( self.eps = eps self.step = 0 self.momentum = momentum + self.num_features = num_features # Clip scale and bias of the affine transform self.rmax = 3.0 self.dmax = 5.0 @@ -107,8 +108,11 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: return x - # def extra_repr(self) -> str: - # return f"num_features={self.num_features}, momentum={self.momentum}, warmup_steps={self.warmup_steps}" + def extra_repr(self) -> str: + return ( + f"num_features={self.num_features}, momentum={self.momentum}, " + f"warmup_steps={self.warmup_steps}, affine={self.affine}" + ) class BatchRenorm1d(BatchRenorm): From bb80218b1730c88bea3e18b362bb17811dc0f2d3 Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Fri, 19 Jul 2024 14:29:33 +0200 Subject: [PATCH 20/32] Re-organize losses --- sb3_contrib/crossq/crossq.py | 44 ++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py index bec161d8..9c754e4e 100644 --- a/sb3_contrib/crossq/crossq.py +++ b/sb3_contrib/crossq/crossq.py @@ -192,8 +192,6 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: for _ in range(gradient_steps): self._n_updates += 1 - # Delayed updates - update_actor_and_temperature = self._n_updates % self.policy_delay == 0 # Sample replay buffer replay_data = self.replay_buffer.sample(batch_size, env=self._vec_normalize_env) # type: ignore[union-attr] @@ -205,39 +203,21 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: # of actor and critic carefully. This is because of the BatchNorm layers in the networks # which behave differently in train and eval modes. - # TODO: replace with more precise, set_training_mode_bn(), to allow the use of Dropout - # TODO: double check, should be moved with the ent coef to the delayed update? - self.actor.set_training_mode(True) - actions_pi, log_prob = self.actor.action_log_prob(replay_data.observations) - log_prob = log_prob.reshape(-1, 1) - self.actor.set_training_mode(False) - - ent_coef_loss = None - if self.ent_coef_optimizer is not None and self.log_ent_coef is not None: + if self.log_ent_coef is not None: # Important: detach the variable from the graph # so we don't change it with other losses # see https://github.com/rail-berkeley/softlearning/issues/60 ent_coef = th.exp(self.log_ent_coef.detach()) - ent_coef_loss = -(self.log_ent_coef * (log_prob + self.target_entropy).detach()).mean() - ent_coef_losses.append(ent_coef_loss.item()) else: ent_coef = self.ent_coef_tensor ent_coefs.append(ent_coef.item()) - # Optimize entropy coefficient, also called - # entropy temperature or alpha in the paper - if ent_coef_loss is not None and self.ent_coef_optimizer is not None and update_actor_and_temperature: - - self.ent_coef_optimizer.zero_grad() - ent_coef_loss.backward() - self.ent_coef_optimizer.step() - with th.no_grad(): # Select action according to policy + # TODO: replace with more precise, set_training_mode_bn(), to allow the use of Dropout self.actor.set_training_mode(False) next_actions, next_log_prob = self.actor.action_log_prob(replay_data.next_observations) - self.actor.set_training_mode(False) # Joint forward pass of obs/next_obs and actions/next_state_actions to have only # one forward pass with shape (n_critics, 2 * batch_size, 1). @@ -256,7 +236,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: # two sequential forward passes. all_obs = th.cat([replay_data.observations, replay_data.next_observations], dim=0) all_actions = th.cat([replay_data.actions, next_actions], dim=0) - + # Update critic BN stats self.critic.set_training_mode(True) all_q_values = th.cat(self.critic(all_obs, all_actions), dim=1) self.critic.set_training_mode(False) @@ -283,11 +263,25 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: self.critic.optimizer.step() # Delayed policy updates - if update_actor_and_temperature: + if self._n_updates % self.policy_delay == 0: + # Sample action according to policy and update actor BN stats + self.actor.set_training_mode(True) + actions_pi, log_prob = self.actor.action_log_prob(replay_data.observations) + log_prob = log_prob.reshape(-1, 1) + self.actor.set_training_mode(False) + + # Optimize entropy coefficient, also called entropy temperature or alpha in the paper + if self.ent_coef_optimizer is not None: + ent_coef_loss = -(self.log_ent_coef * (log_prob + self.target_entropy).detach()).mean() + ent_coef_losses.append(ent_coef_loss.item()) + + self.ent_coef_optimizer.zero_grad() + ent_coef_loss.backward() + self.ent_coef_optimizer.step() + # Compute actor loss self.critic.set_training_mode(False) q_values_pi = th.cat(self.critic(replay_data.observations, actions_pi), dim=1) - self.critic.set_training_mode(False) min_qf_pi, _ = th.min(q_values_pi, dim=1, keepdim=True) actor_loss = (ent_coef * log_prob.reshape(-1, 1) - min_qf_pi).mean() From a717d13e4e6cca1380c176ab24d833fc45612d20 Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Fri, 19 Jul 2024 14:46:36 +0200 Subject: [PATCH 21/32] Add set_bn_training_mode --- sb3_contrib/common/torch_layers.py | 2 ++ sb3_contrib/crossq/crossq.py | 14 +++++++------- sb3_contrib/crossq/policies.py | 22 ++++++++++++++++++++++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/sb3_contrib/common/torch_layers.py b/sb3_contrib/common/torch_layers.py index 9927cd6a..76a93bb7 100644 --- a/sb3_contrib/common/torch_layers.py +++ b/sb3_contrib/common/torch_layers.py @@ -67,6 +67,7 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: """ if self.training: + # Compute batch statistics batch_mean = x.mean(dim=0) batch_var = x.var(dim=0) batch_std = (batch_var + self.eps).sqrt() @@ -98,6 +99,7 @@ def forward(self, x: torch.Tensor) -> torch.Tensor: self.steps += 1 else: + # Use running statistics during evaluation mode custom_mean, custom_var = self.ra_mean, self.ra_var # Normalize diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py index 9c754e4e..2a744cfd 100644 --- a/sb3_contrib/crossq/crossq.py +++ b/sb3_contrib/crossq/crossq.py @@ -215,8 +215,8 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: with th.no_grad(): # Select action according to policy - # TODO: replace with more precise, set_training_mode_bn(), to allow the use of Dropout - self.actor.set_training_mode(False) + # Use more precise set_training_mode to allow the use of Dropout + self.actor.set_bn_training_mode(False) next_actions, next_log_prob = self.actor.action_log_prob(replay_data.next_observations) # Joint forward pass of obs/next_obs and actions/next_state_actions to have only @@ -237,9 +237,9 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: all_obs = th.cat([replay_data.observations, replay_data.next_observations], dim=0) all_actions = th.cat([replay_data.actions, next_actions], dim=0) # Update critic BN stats - self.critic.set_training_mode(True) + self.critic.set_bn_training_mode(True) all_q_values = th.cat(self.critic(all_obs, all_actions), dim=1) - self.critic.set_training_mode(False) + self.critic.set_bn_training_mode(False) current_q_values, next_q_values = th.split(all_q_values, batch_size, dim=0) # (batch_size, n_critics) -> (n_critics, batch_size, 1) current_q_values = current_q_values.T[..., None] @@ -265,10 +265,10 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: # Delayed policy updates if self._n_updates % self.policy_delay == 0: # Sample action according to policy and update actor BN stats - self.actor.set_training_mode(True) + self.actor.set_bn_training_mode(True) actions_pi, log_prob = self.actor.action_log_prob(replay_data.observations) log_prob = log_prob.reshape(-1, 1) - self.actor.set_training_mode(False) + self.actor.set_bn_training_mode(False) # Optimize entropy coefficient, also called entropy temperature or alpha in the paper if self.ent_coef_optimizer is not None: @@ -280,7 +280,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: self.ent_coef_optimizer.step() # Compute actor loss - self.critic.set_training_mode(False) + self.critic.set_bn_training_mode(False) q_values_pi = th.cat(self.critic(replay_data.observations, actions_pi), dim=1) min_qf_pi, _ = th.min(q_values_pi, dim=1, keepdim=True) diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index 85797072..7a585a58 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -204,6 +204,17 @@ def action_log_prob(self, obs: PyTorchObs) -> Tuple[th.Tensor, th.Tensor]: def _predict(self, observation: PyTorchObs, deterministic: bool = False) -> th.Tensor: return self(observation, deterministic) + def set_bn_training_mode(self, mode: bool) -> None: + """ + Set the training mode of the BatchRenorm layers. + When training is True, the running statistics are updated. + + :param mode: Whether to set the layers in training mode or not + """ + for module in self.modules(): + if isinstance(module, BatchRenorm1d): + module.train(mode) + class CrossQCritic(BaseModel): """ @@ -292,6 +303,17 @@ def forward(self, obs: th.Tensor, actions: th.Tensor) -> Tuple[th.Tensor, ...]: qvalue_input = th.cat([features, actions], dim=1) return tuple(q_net(qvalue_input) for q_net in self.q_networks) + def set_bn_training_mode(self, mode: bool) -> None: + """ + Set the training mode of the BatchRenorm layers. + When training is True, the running statistics are updated. + + :param mode: Whether to set the layers in training mode or not + """ + for module in self.modules(): + if isinstance(module, BatchRenorm1d): + module.train(mode) + class CrossQPolicy(BasePolicy): """ From cb1bc8faf02e6cfd7b570d072976e8a0abfa1a68 Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Sat, 20 Jul 2024 16:09:27 +0200 Subject: [PATCH 22/32] Simplify network creation with new SB3 version, and fix default momentum --- docs/misc/changelog.rst | 2 +- sb3_contrib/crossq/policies.py | 73 ++++++++++++++++++---------------- sb3_contrib/version.txt | 2 +- setup.py | 2 +- 4 files changed, 42 insertions(+), 37 deletions(-) diff --git a/docs/misc/changelog.rst b/docs/misc/changelog.rst index e8b55237..cec72857 100644 --- a/docs/misc/changelog.rst +++ b/docs/misc/changelog.rst @@ -3,7 +3,7 @@ Changelog ========== -Release 2.4.0a5 (WIP) +Release 2.4.0a6 (WIP) -------------------------- Breaking Changes: diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index 7a585a58..04858fc1 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -1,3 +1,4 @@ +from functools import partial from typing import Any, Dict, List, Optional, Tuple, Type, Union import torch as th @@ -91,24 +92,28 @@ def __init__( self.clip_mean = clip_mean action_dim = get_action_dim(self.action_space) - latent_pi_net = create_mlp(features_dim, -1, net_arch, activation_fn) - batch_norm_params = { - "momentum": batch_norm_momentum, - "eps": batch_norm_eps, - "warmup_steps": renorm_warmup_steps, - } - - # TODO(antonin): refactor once we can have batch norm/custom layers easily in SB3 + pre_linear_modules = [] if batch_norm: - # If batchnorm, then we want to add torch.nn.Batch_Norm layers before every linear layer - net: List[Union[nn.Module, BatchRenorm1d]] = [] - for layer in latent_pi_net: - if isinstance(layer, nn.Linear): - net.append(BatchRenorm1d(layer.in_features, **batch_norm_params)) # type: ignore[arg-type] - net.append(layer) - net.append(BatchRenorm1d(net_arch[-1], **batch_norm_params)) # type: ignore[arg-type] - latent_pi_net = net + pre_linear_modules = [ + partial( + BatchRenorm1d, + momentum=batch_norm_momentum, + eps=batch_norm_eps, + warmup_steps=renorm_warmup_steps, + ) + ] + + latent_pi_net = create_mlp( + features_dim, + -1, + net_arch, + activation_fn, + pre_linear_modules=pre_linear_modules, # type: ignore[arg-type] + ) + + if batch_norm and net_arch: + latent_pi_net.append(pre_linear_modules[0](net_arch[-1])) self.latent_pi = nn.Sequential(*latent_pi_net) last_layer_dim = net_arch[-1] if len(net_arch) > 0 else features_dim @@ -269,28 +274,28 @@ def __init__( ) action_dim = get_action_dim(self.action_space) - batch_norm_params = { - "momentum": batch_norm_momentum, - "eps": batch_norm_eps, - "warmup_steps": renorm_warmup_steps, - } + pre_linear_modules = [] + if batch_norm: + pre_linear_modules = [ + partial( + BatchRenorm1d, + momentum=batch_norm_momentum, + eps=batch_norm_eps, + warmup_steps=renorm_warmup_steps, + ) + ] self.share_features_extractor = share_features_extractor self.n_critics = n_critics self.q_networks: List[nn.Module] = [] for idx in range(n_critics): - q_net_list = create_mlp(features_dim + action_dim, 1, net_arch, activation_fn) - - # TODO(antonin): refactor once we can have batch norm/custom layers easily in SB3 - if batch_norm: - # If batchnorm, then we want to add torch.nn.Batch_Norm layers before every linear layer - net: List[Union[nn.Module, BatchRenorm1d]] = [] - for layer in q_net_list: - if isinstance(layer, nn.Linear): - net.append(BatchRenorm1d(layer.in_features, **batch_norm_params)) # type: ignore[arg-type] - net.append(layer) - q_net_list = net - + q_net_list = create_mlp( + features_dim + action_dim, + 1, + net_arch, + activation_fn, + pre_linear_modules=pre_linear_modules, # type: ignore[arg-type] + ) q_net = nn.Sequential(*q_net_list) self.add_module(f"qf{idx}", q_net) self.q_networks.append(q_net) @@ -360,7 +365,7 @@ def __init__( net_arch: Optional[Union[List[int], Dict[str, List[int]]]] = None, activation_fn: Type[nn.Module] = nn.ReLU, batch_norm: bool = True, - batch_norm_momentum: float = 0.99, + batch_norm_momentum: float = 0.01, # Note: Jax implementation is 1 - momentum = 0.99 batch_norm_eps: float = 0.001, renorm_warmup_steps: int = 100_000, use_sde: bool = False, diff --git a/sb3_contrib/version.txt b/sb3_contrib/version.txt index a1fd35b5..464a5c4d 100644 --- a/sb3_contrib/version.txt +++ b/sb3_contrib/version.txt @@ -1 +1 @@ -2.4.0a5 +2.4.0a6 diff --git a/setup.py b/setup.py index cdb9d67a..c8e00ec7 100644 --- a/setup.py +++ b/setup.py @@ -67,7 +67,7 @@ packages=[package for package in find_packages() if package.startswith("sb3_contrib")], package_data={"sb3_contrib": ["py.typed", "version.txt"]}, install_requires=[ - "stable_baselines3>=2.4.0a4,<3.0", + "stable_baselines3>=2.4.0a6,<3.0", ], description="Contrib package of Stable Baselines3, experimental code.", author="Antonin Raffin", From a88a19b234db1dcf276192d2088bda1b26afb3ee Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Sat, 20 Jul 2024 16:23:46 +0200 Subject: [PATCH 23/32] Use different b1 for Adam as in original implementation --- sb3_contrib/crossq/policies.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sb3_contrib/crossq/policies.py b/sb3_contrib/crossq/policies.py index 04858fc1..3b5f0de2 100644 --- a/sb3_contrib/crossq/policies.py +++ b/sb3_contrib/crossq/policies.py @@ -365,6 +365,7 @@ def __init__( net_arch: Optional[Union[List[int], Dict[str, List[int]]]] = None, activation_fn: Type[nn.Module] = nn.ReLU, batch_norm: bool = True, + # TODO: double check the default value of batch_norm_momentum batch_norm_momentum: float = 0.01, # Note: Jax implementation is 1 - momentum = 0.99 batch_norm_eps: float = 0.001, renorm_warmup_steps: int = 100_000, @@ -380,6 +381,14 @@ def __init__( n_critics: int = 2, share_features_extractor: bool = False, ): + if optimizer_kwargs is None: + # Note: the default value for b1 is 0.9 in Adam. + # b1=0.5 is used in the original CrossQ implementation + # but shows only little overall improvement. + optimizer_kwargs = {} + if optimizer_class in [th.optim.Adam, th.optim.AdamW]: + optimizer_kwargs["betas"] = (0.5, 0.999) + super().__init__( observation_space, action_space, From 32f66fe0388c1800156bd7e2549ac469810ef8ac Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Sat, 20 Jul 2024 16:35:43 +0200 Subject: [PATCH 24/32] Reformat TOML file --- pyproject.toml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7aaad514..72e01933 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ ignore = ["B028", "RUF013"] [tool.ruff.lint.per-file-ignores] # ClassVar, implicit optional check not needed for tests -"./tests/*.py"= ["RUF012", "RUF013"] +"./tests/*.py" = ["RUF012", "RUF013"] [tool.ruff.lint.mccabe] # Unlike Flake8, ruff default to a complexity level of 10. @@ -35,17 +35,13 @@ exclude = """(?x)( [tool.pytest.ini_options] # Deterministic ordering for tests; useful for pytest-xdist. -env = [ - "PYTHONHASHSEED=0" -] +env = ["PYTHONHASHSEED=0"] filterwarnings = [ # Tensorboard warnings "ignore::DeprecationWarning:tensorboard", ] -markers = [ - "slow: marks tests as slow (deselect with '-m \"not slow\"')" -] +markers = ["slow: marks tests as slow (deselect with '-m \"not slow\"')"] [tool.coverage.run] disable_warnings = ["couldnt-parse"] @@ -53,4 +49,11 @@ branch = false omit = ["tests/*", "setup.py"] [tool.coverage.report] -exclude_lines = [ "pragma: no cover", "raise NotImplementedError()", "if typing.TYPE_CHECKING:"] +exclude_lines = [ + "pragma: no cover", + "raise NotImplementedError()", + "if typing.TYPE_CHECKING:", +] + +# [tool.pyright] +# extraPaths = ["../torchy-baselines/"] From 03db09ef40acaeb08a7a453023e872c2b8f4906e Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Mon, 22 Jul 2024 13:56:00 +0200 Subject: [PATCH 25/32] Update CI workflow, skip mypy for 3.8 --- .github/workflows/ci.yml | 78 ++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f81953f..dd8d92cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,9 @@ name: CI on: push: - branches: [ master ] + branches: [master] pull_request: - branches: [ master ] + branches: [master] jobs: build: @@ -22,42 +22,44 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11"] steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - # cpu version of pytorch - pip install torch==2.1.1 --index-url https://download.pytorch.org/whl/cpu + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + # cpu version of pytorch + pip install torch==2.1.1 --index-url https://download.pytorch.org/whl/cpu - # Install Atari Roms - pip install autorom - wget https://gist.githubusercontent.com/jjshoots/61b22aefce4456920ba99f2c36906eda/raw/00046ac3403768bfe45857610a3d333b8e35e026/Roms.tar.gz.b64 - base64 Roms.tar.gz.b64 --decode &> Roms.tar.gz - AutoROM --accept-license --source-file Roms.tar.gz + # Install Atari Roms + pip install autorom + wget https://gist.githubusercontent.com/jjshoots/61b22aefce4456920ba99f2c36906eda/raw/00046ac3403768bfe45857610a3d333b8e35e026/Roms.tar.gz.b64 + base64 Roms.tar.gz.b64 --decode &> Roms.tar.gz + AutoROM --accept-license --source-file Roms.tar.gz - # Install master version - # and dependencies for docs and tests - pip install "stable_baselines3[extra_no_roms,tests,docs] @ git+https://github.com/DLR-RM/stable-baselines3" - pip install . - # Use headless version - pip install opencv-python-headless + # Install master version + # and dependencies for docs and tests + pip install "stable_baselines3[extra_no_roms,tests,docs] @ git+https://github.com/DLR-RM/stable-baselines3" + pip install . + # Use headless version + pip install opencv-python-headless - - name: Lint with ruff - run: | - make lint - - name: Check codestyle - run: | - make check-codestyle - - name: Build the doc - run: | - make doc - - name: Type check - run: | - make type - - name: Test with pytest - run: | - make pytest + - name: Lint with ruff + run: | + make lint + - name: Check codestyle + run: | + make check-codestyle + - name: Build the doc + run: | + make doc + - name: Type check + run: | + make type + # Do not run for python 3.8 (mypy internal error) + if: matrix.python-version != '3.8' + - name: Test with pytest + run: | + make pytest From 72abe85a2149e8617b4ab4035096735b393e1bca Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Thu, 24 Oct 2024 08:45:54 +0200 Subject: [PATCH 26/32] Update CrossQ doc --- docs/guide/examples.rst | 12 +++++++++++- docs/misc/changelog.rst | 4 +++- docs/modules/crossq.rst | 8 +++++--- docs/modules/ppo_recurrent.rst | 1 - sb3_contrib/crossq/crossq.py | 1 + sb3_contrib/crossq/policies.py | 5 ++--- sb3_contrib/version.txt | 2 +- 7 files changed, 23 insertions(+), 10 deletions(-) diff --git a/docs/guide/examples.rst b/docs/guide/examples.rst index ebe80446..5e152265 100644 --- a/docs/guide/examples.rst +++ b/docs/guide/examples.rst @@ -123,6 +123,16 @@ Train a CrossQ agent on the Pendulum environment. from sb3_contrib import CrossQ - model = CrossQ("MlpPolicy", "Pendulum-v1", verbose=1, policy_kwargs=dict(net_arch=dict(pi=[256, 256], qf=[1024, 1024]))) + model = CrossQ( + "MlpPolicy", + "Pendulum-v1", + verbose=1, + policy_kwargs=dict( + net_arch=dict( + pi=[256, 256], + qf=[1024, 1024], + ) + ), + ) model.learn(total_timesteps=5_000, log_interval=4) model.save("crossq_pendulum") diff --git a/docs/misc/changelog.rst b/docs/misc/changelog.rst index eac5b51a..a023ccbf 100644 --- a/docs/misc/changelog.rst +++ b/docs/misc/changelog.rst @@ -3,9 +3,11 @@ Changelog ========== -Release 2.4.0a9 (WIP) +Release 2.4.0a10 (WIP) -------------------------- +**New algorithm: added CrossQ** + Breaking Changes: ^^^^^^^^^^^^^^^^^ - Upgraded to Stable-Baselines3 >= 2.4.0 diff --git a/docs/modules/crossq.rst b/docs/modules/crossq.rst index 719f5ec4..de3c0de5 100644 --- a/docs/modules/crossq.rst +++ b/docs/modules/crossq.rst @@ -10,9 +10,9 @@ Implementation of CrossQ proposed in: `Bhatt A.* & Palenicek D.* et al. Batch Normalization in Deep Reinforcement Learning for Greater Sample Efficiency and Simplicity. ICLR 2024.` -CrossQ is a simple and efficient algorithm that uses batch normalization to improve the sample efficiency of off-policy deep reinforcement learning algorithms. +CrossQ is an algorithm that uses batch normalization to improve the sample efficiency of off-policy deep reinforcement learning algorithms. It is based on the idea of carefully introducing batch normalization layers in the critic network and dropping target networks. -This yield a simpler and more sample-efficient algorithm without requiring high update-to-data ratios. +This results in a simpler and more sample-efficient algorithm without requiring high update-to-data ratios. .. rubric:: Available Policies @@ -25,7 +25,6 @@ This yield a simpler and more sample-efficient algorithm without requiring high Compared to the original implementation, the default network architecture for the q-value function is ``[1024, 1024]`` instead of ``[2048, 2048]`` as it provides a good compromise between speed and performance. - We also use the default hyperparameters for Adam, since they have little impact on performance. .. note:: @@ -80,6 +79,9 @@ Compared to results from the original paper as well as a version from `SBX th.Tensor: but is slightly different when using ``expln`` function (cf StateDependentNoiseDistribution doc). - :return: + :return: Standard deviation of the action dist when available. """ msg = "get_std() is only available when using gSDE" assert isinstance(self.action_dist, StateDependentNoiseDistribution), msg @@ -224,7 +224,7 @@ def set_bn_training_mode(self, mode: bool) -> None: class CrossQCritic(BaseModel): """ Critic network(s) for CrossQ. - The differnce with standard critic networks used by SAC/TD3 is that it uses BatchRenorm layers. + The difference with standard critic networks used by SAC/TD3 is that it uses BatchRenorm layers. By default, it creates two critic networks used to reduce overestimation thanks to clipped Q-learning (cf TD3 paper). @@ -365,7 +365,6 @@ def __init__( net_arch: Optional[Union[List[int], Dict[str, List[int]]]] = None, activation_fn: Type[nn.Module] = nn.ReLU, batch_norm: bool = True, - # TODO: double check the default value of batch_norm_momentum batch_norm_momentum: float = 0.01, # Note: Jax implementation is 1 - momentum = 0.99 batch_norm_eps: float = 0.001, renorm_warmup_steps: int = 100_000, diff --git a/sb3_contrib/version.txt b/sb3_contrib/version.txt index 7c252f11..852a32b3 100644 --- a/sb3_contrib/version.txt +++ b/sb3_contrib/version.txt @@ -1 +1 @@ -2.4.0a9 \ No newline at end of file +2.4.0a10 From f1fc8f5a8738ff6ffabc20429010175c6c791b1b Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Thu, 24 Oct 2024 08:47:52 +0200 Subject: [PATCH 27/32] Use uv to download packages on github CI --- .github/workflows/ci.yml | 12 +++++++----- docs/misc/changelog.rst | 1 + 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd8d92cf..72304a1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,21 +30,23 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip + # Use uv for faster downloads + pip install uv # cpu version of pytorch - pip install torch==2.1.1 --index-url https://download.pytorch.org/whl/cpu + uv pip install torch==2.1.1 --index-url https://download.pytorch.org/whl/cpu # Install Atari Roms - pip install autorom + uv pip install autorom wget https://gist.githubusercontent.com/jjshoots/61b22aefce4456920ba99f2c36906eda/raw/00046ac3403768bfe45857610a3d333b8e35e026/Roms.tar.gz.b64 base64 Roms.tar.gz.b64 --decode &> Roms.tar.gz AutoROM --accept-license --source-file Roms.tar.gz # Install master version # and dependencies for docs and tests - pip install "stable_baselines3[extra_no_roms,tests,docs] @ git+https://github.com/DLR-RM/stable-baselines3" - pip install . + uv pip install "stable_baselines3[extra_no_roms,tests,docs] @ git+https://github.com/DLR-RM/stable-baselines3" + uv pip install . # Use headless version - pip install opencv-python-headless + uv pip install opencv-python-headless - name: Lint with ruff run: | diff --git a/docs/misc/changelog.rst b/docs/misc/changelog.rst index a023ccbf..d86094c7 100644 --- a/docs/misc/changelog.rst +++ b/docs/misc/changelog.rst @@ -31,6 +31,7 @@ Others: ^^^^^^^ - Updated PyTorch version on CI to 2.3.1 - Remove unnecessary SDE noise resampling in PPO/TRPO update +- Switched to uv to download packages on GitHub CI Documentation: ^^^^^^^^^^^^^^ From 497f5fe9086ae37737416d14cbe87d8b637f56e3 Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Thu, 24 Oct 2024 08:53:57 +0200 Subject: [PATCH 28/32] System install for Github CI --- .github/workflows/ci.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72304a1e..41d9a5c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,20 +33,20 @@ jobs: # Use uv for faster downloads pip install uv # cpu version of pytorch - uv pip install torch==2.1.1 --index-url https://download.pytorch.org/whl/cpu + uv pip install --system torch==2.1.1 --index-url https://download.pytorch.org/whl/cpu # Install Atari Roms - uv pip install autorom + uv pip install --system autorom wget https://gist.githubusercontent.com/jjshoots/61b22aefce4456920ba99f2c36906eda/raw/00046ac3403768bfe45857610a3d333b8e35e026/Roms.tar.gz.b64 base64 Roms.tar.gz.b64 --decode &> Roms.tar.gz AutoROM --accept-license --source-file Roms.tar.gz # Install master version # and dependencies for docs and tests - uv pip install "stable_baselines3[extra_no_roms,tests,docs] @ git+https://github.com/DLR-RM/stable-baselines3" - uv pip install . + uv pip install --system "stable_baselines3[extra_no_roms,tests,docs] @ git+https://github.com/DLR-RM/stable-baselines3" + uv pip install --system . # Use headless version - uv pip install opencv-python-headless + uv pip install --system opencv-python-headless - name: Lint with ruff run: | From 6e378053c8e4883c73cd8924ddc157819fbb5219 Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Thu, 24 Oct 2024 09:10:13 +0200 Subject: [PATCH 29/32] Fix for pytorch install --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41d9a5c2..34d8de9f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,8 @@ jobs: # Use uv for faster downloads pip install uv # cpu version of pytorch - uv pip install --system torch==2.1.1 --index-url https://download.pytorch.org/whl/cpu + # See https://github.com/astral-sh/uv/issues/1497 + uv pip install --system torch==2.5.0.* --index https://download.pytorch.org/whl/cpu # Install Atari Roms uv pip install --system autorom From 94af8533170d14c216cc6b71f4bb0ad21f752a66 Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Thu, 24 Oct 2024 09:12:04 +0200 Subject: [PATCH 30/32] Use +cpu version --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34d8de9f..84e3371c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: pip install uv # cpu version of pytorch # See https://github.com/astral-sh/uv/issues/1497 - uv pip install --system torch==2.5.0.* --index https://download.pytorch.org/whl/cpu + uv pip install --system torch==2.5.0+cpu --index https://download.pytorch.org/whl/cpu # Install Atari Roms uv pip install --system autorom From 6cd924ecf5118ffbd15583300c6eadb1cdbe340b Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Thu, 24 Oct 2024 09:13:41 +0200 Subject: [PATCH 31/32] Pytorch 2.5.0 doesn't support python 3.8 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84e3371c..8f924ec0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: pip install uv # cpu version of pytorch # See https://github.com/astral-sh/uv/issues/1497 - uv pip install --system torch==2.5.0+cpu --index https://download.pytorch.org/whl/cpu + uv pip install --system torch==2.4.1+cpu --index https://download.pytorch.org/whl/cpu # Install Atari Roms uv pip install --system autorom From 125a8ca0e2839cb531a6bae787820fd1e6d66e47 Mon Sep 17 00:00:00 2001 From: Antonin RAFFIN Date: Thu, 24 Oct 2024 09:38:24 +0200 Subject: [PATCH 32/32] Update comments --- sb3_contrib/crossq/crossq.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sb3_contrib/crossq/crossq.py b/sb3_contrib/crossq/crossq.py index 572d4fbb..73204eb4 100644 --- a/sb3_contrib/crossq/crossq.py +++ b/sb3_contrib/crossq/crossq.py @@ -221,7 +221,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: next_actions, next_log_prob = self.actor.action_log_prob(replay_data.next_observations) # Joint forward pass of obs/next_obs and actions/next_state_actions to have only - # one forward pass with shape (n_critics, 2 * batch_size, 1). + # one forward pass. # # This has two reasons: # 1. According to the paper obs/actions and next_obs/next_state_actions are differently @@ -241,6 +241,7 @@ def train(self, gradient_steps: int, batch_size: int = 64) -> None: self.critic.set_bn_training_mode(True) all_q_values = th.cat(self.critic(all_obs, all_actions), dim=1) self.critic.set_bn_training_mode(False) + # (2 * batch_size, n_critics) -> (batch_size, n_critics), (batch_size, n_critics) current_q_values, next_q_values = th.split(all_q_values, batch_size, dim=0) # (batch_size, n_critics) -> (n_critics, batch_size, 1) current_q_values = current_q_values.T[..., None]