diff --git a/package.json b/package.json index 8927fc13..7dfddaf2 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "@types/node": "^22.9.0", "prettier": "^3.4.2", "sharp": "^0.33.5", - "simple-git-hooks": "^2.11.1" + "simple-git-hooks": "^2.11.1", + "markdown-it-mathjax3": "^4.3.2" }, "simple-git-hooks": { "pre-commit": "pnpm run fmt" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 621f7aed..4e2bd1db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -31,7 +31,7 @@ importers: version: 0.21.1(vite@5.4.14(@types/node@22.13.10)(lightningcss@1.29.2)(terser@5.39.0))(workbox-build@7.1.1)(workbox-window@7.3.0) vitepress: specifier: ^1.6.3 - version: 1.6.3(@algolia/client-search@5.20.0)(@types/node@22.13.10)(lightningcss@1.29.2)(postcss@8.4.49)(search-insights@2.15.0)(terser@5.39.0)(typescript@5.5.3) + version: 1.6.3(@algolia/client-search@5.20.0)(@types/node@22.13.10)(lightningcss@1.29.2)(markdown-it-mathjax3@4.3.2)(postcss@8.4.49)(search-insights@2.15.0)(terser@5.39.0)(typescript@5.5.3) vue: specifier: ^3.5.12 version: 3.5.13(typescript@5.5.3) @@ -48,6 +48,9 @@ importers: '@types/node': specifier: ^22.9.0 version: 22.13.10 + markdown-it-mathjax3: + specifier: ^4.3.2 + version: 4.3.2 prettier: specifier: ^3.4.2 version: 3.5.3 @@ -1681,6 +1684,10 @@ packages: resolution: {integrity: sha512-groO71Fvi5SWpxjI9Ia+chy0QBwT61mg6yxJV27f5YFf+Mw+STT75K6SHySpP8Co5LsCrtsbCH5dJZSRtkSKaQ==} engines: {node: '>= 14.0.0'} + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1857,6 +1864,13 @@ packages: character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + cheerio-select@1.6.0: + resolution: {integrity: sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==} + + cheerio@1.0.0-rc.10: + resolution: {integrity: sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==} + engines: {node: '>= 6'} + ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -1885,10 +1899,18 @@ packages: commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@6.2.1: + resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} + engines: {node: '>= 6'} + commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} + commander@9.2.0: + resolution: {integrity: sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==} + engines: {node: ^12.20.0 || >=14} + commander@9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} @@ -1918,6 +1940,13 @@ packages: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} @@ -2017,16 +2046,30 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + domhandler@3.3.0: + resolution: {integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==} + engines: {node: '>= 4'} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + domhandler@5.0.3: resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} engines: {node: '>= 4'} + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + domutils@3.1.0: resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} @@ -2049,6 +2092,9 @@ packages: resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} engines: {node: '>=10.13.0'} + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2121,6 +2167,10 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-goat@3.0.0: + resolution: {integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==} + engines: {node: '>=10'} + escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -2288,6 +2338,10 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + esm@3.2.25: + resolution: {integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==} + engines: {node: '>=6'} + espree@10.3.0: resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2571,6 +2625,12 @@ packages: html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + htmlparser2@5.0.1: + resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} + + htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -2912,6 +2972,11 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + juice@8.1.0: + resolution: {integrity: sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA==} + engines: {node: '>=10.0.0'} + hasBin: true + katex@0.16.11: resolution: {integrity: sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ==} hasBin: true @@ -3043,6 +3108,9 @@ packages: mark.js@8.11.1: resolution: {integrity: sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ==} + markdown-it-mathjax3@4.3.2: + resolution: {integrity: sha512-TX3GW5NjmupgFtMJGRauioMbbkGsOXAAt1DZ/rzzYmTHqzkO1rNAdiMD4NiruurToPApn2kYy76x02QN26qr2w==} + markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} @@ -3050,6 +3118,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mathjax-full@3.2.2: + resolution: {integrity: sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==} + mdast-util-directive@2.2.4: resolution: {integrity: sha512-sK3ojFP+jpj1n7Zo5ZKvoxP1MvLyzVG63+gm40Z/qI00avzdPCYxt7RBMgofwAva9gBjbDBWVRB/i+UD+fUCzQ==} @@ -3101,10 +3172,16 @@ packages: mdast-util-to-string@3.2.0: resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==} + mensch@0.3.4: + resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} + mhchemparser@4.2.1: + resolution: {integrity: sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==} + micromark-core-commonmark@1.1.0: resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==} @@ -3220,6 +3297,11 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + min-indent@1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} @@ -3240,6 +3322,9 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + mj-context-menu@0.6.1: + resolution: {integrity: sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -3265,6 +3350,15 @@ packages: node-addon-api@3.2.1: resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-gyp-build@4.8.2: resolution: {integrity: sha512-IRUxE4BVsHWXkV/SFOut4qTlagw2aM8T5/vnTsmrHJvVoKueJHRc/JaFND7QDDc61kLYUJ6qlZM3sqTSyx2dTw==} hasBin: true @@ -3369,6 +3463,12 @@ packages: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} + parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -3708,6 +3808,9 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slick@1.12.2: + resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} + smob@1.5.0: resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} @@ -3749,6 +3852,10 @@ packages: resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} engines: {node: '>=0.10.0'} + speech-rule-engine@4.0.7: + resolution: {integrity: sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g==} + hasBin: true + string.prototype.matchall@4.0.11: resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} engines: {node: '>= 0.4'} @@ -3856,6 +3963,9 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -4030,6 +4140,10 @@ packages: engines: {node: '>=8'} hasBin: true + valid-data-url@3.0.1: + resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} + engines: {node: '>=10'} + validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -4114,9 +4228,19 @@ packages: typescript: optional: true + web-resource-inliner@6.0.1: + resolution: {integrity: sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==} + engines: {node: '>=10.0.0'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} @@ -4152,6 +4276,9 @@ packages: engines: {node: '>= 8'} hasBin: true + wicked-good-xpath@1.3.0: + resolution: {integrity: sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -4222,6 +4349,10 @@ packages: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} + xmldom-sre@0.1.31: + resolution: {integrity: sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==} + engines: {node: '>=0.1'} + yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} @@ -5975,6 +6106,8 @@ snapshots: '@algolia/requester-fetch': 5.20.0 '@algolia/requester-node-http': 5.20.0 + ansi-colors@4.1.3: {} + ansi-regex@5.0.1: {} ansi-styles@4.3.0: @@ -6187,6 +6320,24 @@ snapshots: character-reference-invalid@2.0.1: {} + cheerio-select@1.6.0: + dependencies: + css-select: 4.3.0 + css-what: 6.1.0 + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + + cheerio@1.0.0-rc.10: + dependencies: + cheerio-select: 1.6.0 + dom-serializer: 1.4.1 + domhandler: 4.3.1 + htmlparser2: 6.1.0 + parse5: 6.0.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + tslib: 2.8.1 + ci-info@3.9.0: {} clean-regexp@1.0.0: @@ -6213,8 +6364,12 @@ snapshots: commander@2.20.3: {} + commander@6.2.1: {} + commander@8.3.0: {} + commander@9.2.0: {} + commander@9.5.0: {} common-tags@1.8.2: {} @@ -6239,6 +6394,16 @@ snapshots: crypto-random-string@2.0.0: {} + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-what@6.1.0: {} + cssesc@3.0.0: {} csstype@3.1.3: {} @@ -6329,6 +6494,12 @@ snapshots: dependencies: esutils: 2.0.3 + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -6337,10 +6508,24 @@ snapshots: domelementtype@2.3.0: {} + domhandler@3.3.0: + dependencies: + domelementtype: 2.3.0 + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils@3.1.0: dependencies: dom-serializer: 2.0.0 @@ -6366,6 +6551,8 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.2.1 + entities@2.2.0: {} + entities@4.5.0: {} error-ex@1.3.2: @@ -6594,6 +6781,8 @@ snapshots: escalade@3.2.0: {} + escape-goat@3.0.0: {} + escape-string-regexp@1.0.5: {} escape-string-regexp@4.0.0: {} @@ -6850,6 +7039,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm@3.2.25: {} + espree@10.3.0: dependencies: acorn: 8.14.1 @@ -7160,6 +7351,20 @@ snapshots: html-void-elements@3.0.0: {} + htmlparser2@5.0.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 3.3.0 + domutils: 2.8.0 + entities: 2.2.0 + + htmlparser2@6.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 @@ -7490,6 +7695,16 @@ snapshots: object.assign: 4.1.5 object.values: 1.2.0 + juice@8.1.0: + dependencies: + cheerio: 1.0.0-rc.10 + commander: 6.2.1 + mensch: 0.3.4 + slick: 1.12.2 + web-resource-inliner: 6.0.1 + transitivePeerDependencies: + - encoding + katex@0.16.11: dependencies: commander: 8.3.0 @@ -7594,10 +7809,24 @@ snapshots: mark.js@8.11.1: {} + markdown-it-mathjax3@4.3.2: + dependencies: + juice: 8.1.0 + mathjax-full: 3.2.2 + transitivePeerDependencies: + - encoding + markdown-table@3.0.4: {} math-intrinsics@1.1.0: {} + mathjax-full@3.2.2: + dependencies: + esm: 3.2.25 + mhchemparser: 4.2.1 + mj-context-menu: 0.6.1 + speech-rule-engine: 4.0.7 + mdast-util-directive@2.2.4: dependencies: '@types/mdast': 3.0.15 @@ -7734,8 +7963,12 @@ snapshots: dependencies: '@types/mdast': 3.0.15 + mensch@0.3.4: {} + merge2@1.4.1: {} + mhchemparser@4.2.1: {} + micromark-core-commonmark@1.1.0: dependencies: decode-named-character-reference: 1.0.2 @@ -7983,6 +8216,8 @@ snapshots: braces: 3.0.3 picomatch: 2.3.1 + mime@2.6.0: {} + min-indent@1.0.1: {} minimatch@3.1.2: @@ -7999,6 +8234,8 @@ snapshots: mitt@3.0.1: {} + mj-context-menu@0.6.1: {} + mri@1.2.0: {} ms@2.1.3: {} @@ -8018,6 +8255,10 @@ snapshots: node-addon-api@3.2.1: optional: true + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + node-gyp-build@4.8.2: optional: true @@ -8159,6 +8400,12 @@ snapshots: json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 + parse5-htmlparser2-tree-adapter@6.0.1: + dependencies: + parse5: 6.0.1 + + parse5@6.0.1: {} + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -8606,6 +8853,8 @@ snapshots: slash@3.0.0: {} + slick@1.12.2: {} + smob@1.5.0: {} source-map-js@1.2.1: {} @@ -8641,6 +8890,12 @@ snapshots: speakingurl@14.0.1: {} + speech-rule-engine@4.0.7: + dependencies: + commander: 9.2.0 + wicked-good-xpath: 1.3.0 + xmldom-sre: 0.1.31 + string.prototype.matchall@4.0.11: dependencies: call-bind: 1.0.7 @@ -8785,6 +9040,8 @@ snapshots: dependencies: is-number: 7.0.0 + tr46@0.0.3: {} + tr46@1.0.1: dependencies: punycode: 2.3.1 @@ -9005,6 +9262,8 @@ snapshots: kleur: 4.1.5 sade: 1.8.1 + valid-data-url@3.0.1: {} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 @@ -9054,7 +9313,7 @@ snapshots: lightningcss: 1.29.2 terser: 5.39.0 - vitepress@1.6.3(@algolia/client-search@5.20.0)(@types/node@22.13.10)(lightningcss@1.29.2)(postcss@8.4.49)(search-insights@2.15.0)(terser@5.39.0)(typescript@5.5.3): + vitepress@1.6.3(@algolia/client-search@5.20.0)(@types/node@22.13.10)(lightningcss@1.29.2)(markdown-it-mathjax3@4.3.2)(postcss@8.4.49)(search-insights@2.15.0)(terser@5.39.0)(typescript@5.5.3): dependencies: '@docsearch/css': 3.8.2 '@docsearch/js': 3.8.2(@algolia/client-search@5.20.0)(search-insights@2.15.0) @@ -9075,6 +9334,7 @@ snapshots: vite: 5.4.14(@types/node@22.13.10)(lightningcss@1.29.2)(terser@5.39.0) vue: 3.5.13(typescript@5.5.3) optionalDependencies: + markdown-it-mathjax3: 4.3.2 postcss: 8.4.49 transitivePeerDependencies: - '@algolia/client-search' @@ -9126,8 +9386,26 @@ snapshots: optionalDependencies: typescript: 5.5.3 + web-resource-inliner@6.0.1: + dependencies: + ansi-colors: 4.1.3 + escape-goat: 3.0.0 + htmlparser2: 5.0.1 + mime: 2.6.0 + node-fetch: 2.7.0 + valid-data-url: 3.0.1 + transitivePeerDependencies: + - encoding + + webidl-conversions@3.0.1: {} + webidl-conversions@4.0.2: {} + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + whatwg-url@7.1.0: dependencies: lodash.sortby: 4.7.0 @@ -9210,6 +9488,8 @@ snapshots: dependencies: isexe: 2.0.0 + wicked-good-xpath@1.3.0: {} + word-wrap@1.2.5: {} workbox-background-sync@7.1.0: @@ -9340,6 +9620,8 @@ snapshots: xml-name-validator@4.0.0: {} + xmldom-sre@0.1.31: {} + yallist@3.1.1: {} yaml-eslint-parser@1.2.3: diff --git a/src/.vitepress/config.ts b/src/.vitepress/config.ts index 674edf6f..e6578d43 100644 --- a/src/.vitepress/config.ts +++ b/src/.vitepress/config.ts @@ -2,6 +2,7 @@ import { defineConfigWithTheme } from 'vitepress' import { withPwa } from '@vite-pwa/vitepress' import tailwindcss from '@tailwindcss/vite' import { genFeed } from './genFeed.js' +import mathjax3 from 'markdown-it-mathjax3' interface ThemeConfig { postsPerPage?: number @@ -23,6 +24,12 @@ export default withPwa( description: metaInfo.description, lang: 'zh-CN', cleanUrls: true, + markdown: { + math: true, + config: (md) => { + md.use(mathjax3) + }, + }, head: [ [ 'link', diff --git a/src/.vitepress/theme/index.ts b/src/.vitepress/theme/index.ts index 5babe1a4..abec8919 100644 --- a/src/.vitepress/theme/index.ts +++ b/src/.vitepress/theme/index.ts @@ -1,4 +1,5 @@ import './style.css' +import './mathjax3.css' import { h } from 'vue' import Layout from './Layout.vue' diff --git a/src/.vitepress/theme/mathjax3.css b/src/.vitepress/theme/mathjax3.css new file mode 100644 index 00000000..d69f5d73 --- /dev/null +++ b/src/.vitepress/theme/mathjax3.css @@ -0,0 +1,8 @@ +mjx-container { + display: inline-block; + margin: auto 2px -2px; +} +mjx-container > svg { + margin: auto; + display: inline-block; +} diff --git a/src/images/swagger-deepseek-r1/PPO.png b/src/images/swagger-deepseek-r1/PPO.png new file mode 100644 index 00000000..4e00dc67 Binary files /dev/null and b/src/images/swagger-deepseek-r1/PPO.png differ diff --git a/src/images/swagger-deepseek-r1/advantage.png b/src/images/swagger-deepseek-r1/advantage.png new file mode 100644 index 00000000..324892cd Binary files /dev/null and b/src/images/swagger-deepseek-r1/advantage.png differ diff --git a/src/images/swagger-deepseek-r1/deepseek-r1-zero.png b/src/images/swagger-deepseek-r1/deepseek-r1-zero.png new file mode 100644 index 00000000..e550072c Binary files /dev/null and b/src/images/swagger-deepseek-r1/deepseek-r1-zero.png differ diff --git a/src/images/swagger-deepseek-r1/deepseek-r1.png b/src/images/swagger-deepseek-r1/deepseek-r1.png new file mode 100644 index 00000000..1b84c2a2 Binary files /dev/null and b/src/images/swagger-deepseek-r1/deepseek-r1.png differ diff --git a/src/images/swagger-deepseek-r1/distillation.png b/src/images/swagger-deepseek-r1/distillation.png new file mode 100644 index 00000000..cd133e25 Binary files /dev/null and b/src/images/swagger-deepseek-r1/distillation.png differ diff --git a/src/images/swagger-deepseek-r1/ex3.png b/src/images/swagger-deepseek-r1/ex3.png new file mode 100644 index 00000000..969ae909 Binary files /dev/null and b/src/images/swagger-deepseek-r1/ex3.png differ diff --git a/src/images/swagger-deepseek-r1/exp1.png b/src/images/swagger-deepseek-r1/exp1.png new file mode 100644 index 00000000..da514e8f Binary files /dev/null and b/src/images/swagger-deepseek-r1/exp1.png differ diff --git a/src/images/swagger-deepseek-r1/exp2.png b/src/images/swagger-deepseek-r1/exp2.png new file mode 100644 index 00000000..20cfdd24 Binary files /dev/null and b/src/images/swagger-deepseek-r1/exp2.png differ diff --git a/src/images/swagger-deepseek-r1/grpo.png b/src/images/swagger-deepseek-r1/grpo.png new file mode 100644 index 00000000..7cdc4a6b Binary files /dev/null and b/src/images/swagger-deepseek-r1/grpo.png differ diff --git a/src/images/swagger-deepseek-r1/grpo_formula.png b/src/images/swagger-deepseek-r1/grpo_formula.png new file mode 100644 index 00000000..0f0dbd03 Binary files /dev/null and b/src/images/swagger-deepseek-r1/grpo_formula.png differ diff --git a/src/images/swagger-deepseek-r1/opm.png b/src/images/swagger-deepseek-r1/opm.png new file mode 100644 index 00000000..66a5dff6 Binary files /dev/null and b/src/images/swagger-deepseek-r1/opm.png differ diff --git a/src/images/swagger-deepseek-r1/ppo_clip.png b/src/images/swagger-deepseek-r1/ppo_clip.png new file mode 100644 index 00000000..a6cdb705 Binary files /dev/null and b/src/images/swagger-deepseek-r1/ppo_clip.png differ diff --git a/src/images/swagger-deepseek-r1/r1-zero_exp1.png b/src/images/swagger-deepseek-r1/r1-zero_exp1.png new file mode 100644 index 00000000..378b27fb Binary files /dev/null and b/src/images/swagger-deepseek-r1/r1-zero_exp1.png differ diff --git a/src/images/swagger-deepseek-r1/r1-zero_exp2.png b/src/images/swagger-deepseek-r1/r1-zero_exp2.png new file mode 100644 index 00000000..16250b7c Binary files /dev/null and b/src/images/swagger-deepseek-r1/r1-zero_exp2.png differ diff --git a/src/images/swagger-deepseek-r1/r1-zero_exp3.png b/src/images/swagger-deepseek-r1/r1-zero_exp3.png new file mode 100644 index 00000000..d7b33d4a Binary files /dev/null and b/src/images/swagger-deepseek-r1/r1-zero_exp3.png differ diff --git a/src/images/swagger-deepseek-r1/r1-zero_exp4.png b/src/images/swagger-deepseek-r1/r1-zero_exp4.png new file mode 100644 index 00000000..8b901e21 Binary files /dev/null and b/src/images/swagger-deepseek-r1/r1-zero_exp4.png differ diff --git a/src/images/swagger-deepseek-r1/training_template.png b/src/images/swagger-deepseek-r1/training_template.png new file mode 100644 index 00000000..b063608e Binary files /dev/null and b/src/images/swagger-deepseek-r1/training_template.png differ diff --git a/src/posts/swagger-deepseek-r1.md b/src/posts/swagger-deepseek-r1.md new file mode 100644 index 00000000..4a5502f8 --- /dev/null +++ b/src/posts/swagger-deepseek-r1.md @@ -0,0 +1,370 @@ +--- +title: DeepSeek-R1 论文解读及相关技术杂谈 +date: 2025-03-16 +author: + name: swagger + github: swagger-coder +--- + +本篇博客旨在讲解论文 **《DeepSeek-R1: Incentivizing Reasoning Capability in LLMs via Reinforcement Learning》** ,如有错误,欢迎指正。 + + + + + +## 1. 引入 + +近年来,大型语言模型(LLMs)正在经历快速迭代和演进,逐渐缩小了通往通用人工智能(AGI)的差距。OpenAI 的 o1 系列发布是一个里程碑事件,o1 系列强大的推理能力让人们看到了预训练(Pre-Training)和微调(Supervised Fine-Tuning)以外的模型优化方式。先前很多工作探索了强化学习以及蒙特卡洛树搜索和束搜索等搜索算法等,但是都没有达到 OpenAI 的 o1 系列模型相当的一般推理性能。 + +> OpenAI 的 o1 系列的技术要点主要包括**策略初始化、奖赏设计、搜索和学习**,具体可以查阅参考资料[1] + +《DeepSeek-R1》成功探索出用纯强化学习(RL)提高语言模型推理能力的路径。具体而言,《DeepSeek-R1》研究的重点是在没有任何监督数据的情况下发展推理能力的潜力,重点关注它们通过纯 RL 过程的自我演化。 + +由此《DeepSeek-R1》使用 **DeepSeek-V3-Base** 作为基础模型,并采用 GRPO 作为强化学习框架,以提高模型在推理方面的性能,通过这单阶段的强化学习过程得到了 **DeepSeek-R1-Zero** 模型,DeepSeek-R1-Zero 在训练过程中涌现了"Aha moment"现象,并有强大的推理能力,但是存在着可读性差和语言混合等挑战。为了解决这些问题并进一步提高推理性能,《DeepSeek-R1》使用了少量冷启动数据和多阶段训练阶段,得到最终性能和 OpenAI-o1-1217 相当的 **DeepSeek-R1** 模型。同时《DeepSeek-R1》探索了提升小模型推理能力的方式,发现在小模型上使用 **蒸馏(Distillation)** 效果优于直接使用强化学习。 + +> 为了更好地理解自我演化(self-evolution),可以参考 InstructGPT[2] 中提出的 RLHF(Reinforcement Learning from Human Feedback)。RLHF 通过利用人类反馈进行强化学习训练,帮助模型不断优化和提升推理能力。 + +下面是《DeepSeek-R1》的贡献&工作总结表: + +| 模型 | 贡献重点 | 区别与优势 | +| :------------------: | :---------------------------------------------------- | :--------------------------------------------- | +| **DeepSeek-R1-Zero** | 直接应用强化学习(RL)于基础模型,无需监督微调(SFT) | 展示自我验证、反思和长链式思维(CoT)能力 | +| **DeepSeek-R1** | 包含两个强化学习阶段和两个监督微调阶段 | 改进推理模式并与人类偏好保持一致 | +| **Distilled Model** | 大型模型的推理模式成功蒸馏至小型模型 | 发现在小模型上使用蒸馏效果优于直接使用强化学习 | + +## 2. 研究方法 + +在接下来的部分中,将介绍:**2.1** DeepSeek-R1-Zero,它直接将强化学习应用于基础模型,而没有任何 SFT 数据;**2.2** DeepSeek-R1,它从一个经过数千个长链思维(Long CoT)示例微调的 checkpoint 开始应用强化学习;**2.3** 从 DeepSeek-R1 中蒸馏推理能力到小型密集模型 + +### 2.1 DeepSeek-R1-Zero + +DeepSeek-R1-Zero 探索了大型语言模型在没有任何监督数据的情况下发展推理能力的潜力,重点关注通过纯粹的强化学习过程进行自我演化。如图 1,在训练流程上,DeepSeek-R1-Zero 很简单,使用基于规则的 Reasoning Data 进行纯强化学习,这里面涉及到三个重点:**强化学习范式**、**Reasoning Data 构造**和**有趣的实验发现**。 + +
+
+ +
图1:DeepSeek-R1-Zero的训练流程, 基础模型直接通过强化学习进行训练
+
+
+ +#### 2.1.1 强化学习范式 + +**从 PPO 到 GRPO** + +
+
+ +
图2:PPO和GRPO对比图[4]
+
+
+PPO(Proximal Policy Optimization)是一种常用的强化学习算法,能够在策略优化中保持稳定性和效率。特别地,在大语言模型场景下,它通过最大化以下目标来优化LLMs: + +
+
+ +
+
+ +不要看公式复杂,这里是想让优势 $A$ 越大越好的同时约束新的参数 $\theta$ 和旧的参数 $\theta_{\text{old}}$ 不要相差太远。其中 $\text{clip} = \max(\min(\frac{\pi_{\theta}}{\pi_{\theta_{\text{old}}}}, 1+\epsilon), 1-\epsilon)$,即把 $\frac{\pi_{\theta}}{\pi_{\theta_{\text{old}}}}$ 的值限制在 $1-\epsilon$ 和 $1+\epsilon$ 之间。如果 $A$ 为正,那么说明当前动作价值高于平均,最大化式子会增大 $\frac{\pi_{\theta}}{\pi_{\theta_{\text{old}}}}$,但是不会超过 $1+\epsilon$,如果 $A$ 为负,说明当前动作价值低于平均,最大化式子会减小 $\frac{\pi_{\theta}}{\pi_{\theta_{\text{old}}}}$,但是不会超过 $1-\epsilon$。下面是李宏毅老师[3]的讲解视频中的截图,可以参考: + +
+
+ +
图3:PPO clip值范围示意图[3]
+
+
+ +PPO 在大语言模型中的整体流程包括以下几个关键组件: + +1. **策略模型(Policy Model)**:负责生成动作的模型,基于当前状态选择最优动作。PPO 通过优化策略模型的参数来提高策略的表现。对应公式中的 $\pi_{\theta}$。 + +2. **参考模型(Reference Model)**:用于对比和评估策略模型的表现,确保新策略不会偏离旧策略太多,从而维持训练的稳定性。对应公式中的 $\pi_{\theta_{\text{old}}}$。 + +3. **奖励模型(Reward Model)**:用于评估每个动作的好坏,提供反馈信号以指导策略模型的优化。奖励信号通常基于任务的具体目标进行设计。对应公式中的 $r$。 + +4. **值模型(Value Model)**:用于估计给定状态的价值,帮助策略模型更好地评估未来的奖励。值模型通过最小化预测误差进行更新。对应公式中的 $v$。 + +5. **广义优势估计(GAE, Generalized Advantage Estimation)**:用于计算每个状态-动作对的优势函数,结合了即时奖励$r$和未来奖励$v$的折现和估计,提供更稳定的优势信号。最终能得到优势 $A$。 + +其中 Value Model 通常适合 Policy Model 差不多参数量的模型,这对显存和计算带来了额外的开销,同时在大语言模型场景下,通常只是最后一个 token 计算 reward,这可能会加大考虑所有 token 的 Value Model 的训练难度。 + +因此在 **GRPO(Group Relative Policy Optimization)** 中直接去除了 Value Model,这对应的带来了另一个问题:如何评估未来的奖励?GRPO 的做法是利用多次采样取平均 reward 的方式实现这个效果。GRPO 从旧策略 $\pi_{\theta_{\text{old}}}$ 中采样一组输出 $\{o_1, o_2, \ldots, o_G\}$,然后通过最大化以下目标来优化策略模型 $\pi_{\theta}$。 + +
+
+ +
+
+ +其中 $\epsilon$ 和 $\beta$ 是超参数,$A_i$ 是优势,使用一组奖励 $\{r_1, r_2, \ldots, r_G\}$ 计算得出,这些奖励对应于每组内的输出: + +
+
+ +
+
+ +$A_i$随着 Value Model 的消失,计算方式也变得更为简单,就是简单的均值方差计算。 + +**奖励函数** + +在强化学习中奖励的设计至关重要,**DeepSeek-R1-Zero**中设计了基于规则(rule-based)的奖励系统,该系统主要包含一下两种奖励的类型: + +- **准确性奖励(Accuracy rewards)**:准确性奖励模型评估响应是否正确。例如,在具有确定性结果的数学问题中,模型需要以指定格式(例如,在方框内)提供最终答案,从而实现基于规则的可靠正确性验证。类似地,对于 LeetCode 问题,可以使用编译器根据预定义的测试用例生成反馈。 +- **格式奖励(Format rewards)**:除了准确性奖励模型之外,还使用了一个格式奖励模型,该模型强制模型将思考过程放在"``"和"``"标签之间。 + +《DeepSeek-R1》没有在开发 DeepSeek-R1-Zero 中应用结果或过程神经奖励模型(outcome or process neural reward model),因为《DeepSeek-R1》通过实验发现神经奖励模型在大型强化学习过程中可能会遭受奖励黑客攻击,而重新训练奖励模型需要额外的训练资源,并且会使整个训练流程变得复杂。 + +> 结果或过程神经奖励模型(outcome or process neural reward model)是 LLM 强化学习中常用的奖励设计,可以通过下面的图发现和 rule-based 奖励的区别在于:神经奖励模型通过最终的结果来提供反馈(二元值,0 错 1 对),而规则型奖励则基于预定义的固定规则进行评估(可以是百分比)。 + +
+
+ +
图 4:ORM 和 PRM 示意图[1]
+
+
+ +#### 2.1.2 Reasoning Data 构造 + +数据构造的重点是数据的形式和构造方式,《DeepSeek-R1》使用了下面的训练模板(Training Template)作为 prompt 构造数据,不过具体用的哪个模型生成没有说。在 DeepSeek-R1-Zero 阶段,用来生成 Long CoT 的都是有明确答案的 reasoning data。 + +
+
+ +
图 5:训练模板
+
+
+ +#### 2.1.3 有趣的实验现象 + +**Aha Moment** + +在 DeepSeek-R1-Zero 训练过程中观察到一个有趣的现象:**"Aha Moment"**。模型能够在推理过程中能发现自己的错误,并继续思考纠正这个错误,这是以往 SFT 没有出现的能力。 + +> 按照论文,训练过程中并没有使用包含自我纠错格式的数据,因此"Aha Moment"的出现属于数据分布外的现象,证明了强化学习能够带来意想不到的推理能力提升。 + +
+
+ +
图6:Aha Moment
+
+
+ +**自进化过程** + +随着训练的深入,模型的思考时间逐渐增加。这一进步并非源于外部调整,而是模型自我发展的结果。DeepSeek-R1-Zero 通过延长测试时间,自然地提升了解决复杂推理任务的能力。自我进化的一个显著特征是,随着测试时间和计算量的增加,**复杂行为自然涌现**。例如,模型会自发反思,重新审视和评估先前的步骤,并探索解决问题的替代方法。这些行为并非通过显式编程实现,而是模型与强化学习环境交互的结果。 + +
+
+ +
图7:在RL过程中,DeepSeek-R1-Zero在训练集上的平均响应长度。DeepSeek-R1-Zero自然地学会了用更多的思考时间来解决推理任务。
+
+
+ +**优越的推理性能** + +下图展示了 DeepSeek-R1-Zero 在数学和代码 benchmark 上的优异性能,整体 reasoning 能力超越了 OpenAI-o1-0912。 + +
+
+ +
图8:DeepSeek-R1-Zero和OpenAI o1模型在推理相关基准上的比较
+
+
+ +下图展示了随着训练的进行,在 AIME benchmark 的准确度在不断提升,表明持续训练带来的有效性。 + +
+
+ +
图9:训练过程中DeepSeek-R1-Zero在AIME上的准确率。对于每个问题,抽样了16个答案,并计算了总体平均准确率,以确保评估的稳定性。
+
+
+ +### 2.2 DeepSeek-R1 + +尽管 DeepSeek-R1-Zero 展现了强大的推理能力,并自主发展出意想不到且强大的推理行为,但它面临着几个问题。例如,DeepSeek-R1-Zero 在可读性差和语言混合等挑战上表现不佳。为了解决上述问题,DeepSeek-R1 应运而生。 + +DeepSeek-R1 主要探索了两个问题: + +- 通过纳入少量高质量数据作为冷启动,能否进一步提高推理性能或加速收敛? +- 如何训练一个用户友好的模型,它不仅能产生清晰连贯的思维链(CoT),而且具有很强的通用能力? + +#### 2.2.1 整体概述 + +如图 10,为了解决这些问题,作者设计了一个训练 DeepSeek-R1 的 pipeline。该 pipeline 由四个阶段组成, **Stage1:** 冷启动微调(Cold Start SFT)-> **Stage2:** 面向推理的强化学习(Reasoning-oriented Reinforcement Learning)-> **Stage3:** 拒绝采样和微调(Rejection Sampling and Supervised Fine-Tuning) -> **Stage4:** 全场景强化学习(Reinforcement Learning for all Scenarios)。 + +其实这可以分成两部分来看,**DeepSeek-V3 Base**经过 Stage1 和 Stage2 得到中间模型,这个模型是为了生成更多高质量的 Reasoning Data;接着**DeepSeek-V3 Base**经过 Stage3+Stage4 得到最终的**DeepSeek-R1**。Step1 和 Step3 都是 SFT 阶段,其实都是 **Policy Initialization** 阶段,都是为了让模型在进行强化学习前有更好的基础能力(指令跟随、 推理能力)。由于这部分内容理论性较少,更侧重于工程实践,下面将以列点的形式进行讲解。 + +
+
+ +
图10:DeepSeek-R1训练pipeline
+
+
+ +#### 2.2.2 Stage1: 冷启动微调 + +- **基础模型:** DeepSeek-V3-Base +- **数据构建:** 构建并收集少量的长链式思维(CoT)数据,以用于微调模型增强作为强化学习 Agent 的能力,通过下面三种方式构造数据 + - 使用少样本提示(few-shot prompting),以长链式思维(long CoT)作为示例 + - 直接提示模型生成包含反思与验证的详细答案 + - 以可读格式收集 DeepSeek-R1-Zero 的输出,并通过人工标注进行后处理优化 +- **冷启动数据的优势** + - **可读性:** |special_token|``|special_token|`` + - **潜力:** 通过精心设计具有人类先验的冷启动数据的模式,观察到比 DeepSeek-R1-Zero 更好的性能。 + +#### 2.2.3 Stage2: 面向推理的强化学习 + +- 该阶段在 Stage1 之后,侧重于增强模型的推理能力,特别是在推理密集型任务中,如编码、数学、科学和逻辑推理,这些任务涉及定义良好的问题,并有明确的解决方案,这部分实际和 DeepSeek-R1-zero 的训练是一致的。 +- 实验观察到,CoT 经常表现出语言混合,特别是当 RL 提示涉及多种语言时。为此在 RL 训练过程中的语言一致性奖励(language consistency reward) + +#### 2.2.4 Stage3: 拒绝采样和微调 + +- **基础模型:** DeepSeek-V3-Base,在基础模型上进行 Stage3,微调 2 个 epoch +- **数据生成** + - **推理数据(600k)** + - DeepSeek-V3 作为判断标准:将真实值和模型预测输入 DeepSeek-V3,筛选数据 + - 过滤掉混合语言、长段落和代码块的思维链 + - **非推理数据(200k)** + - 使用 DeepSeek-V3 pipeline 并且使用部分 DeepSeek-V3 SFT 数据 + - 利用 DeepSeek-V3 生成长链式思维(long CoT)数据 + +#### 2.2.5 Stage4: 全场景强化学习 + +- 针对不同类型数据采用差异化训练策略: + - **推理数据(Reasoning data):** + - 沿用 DeepSeek-R1-Zero 的训练方法,通过规则化奖励(rule-based rewards)引导模型优化推理能力 + - **通用数据(General data):** 引入多维度评估机制 + - **有用性(Helpfulness):** 重点评估模型最终总结的实用价值和问题解决能力 + - **安全性(Harmlessness):** 全面评估模型的完整响应(包括推理过程和总结),识别并减轻潜在风险、偏见或有害内容 + - **模型奖励(Model reward):** 通过整合多元奖励信号和多样化数据分布,培养出一个推理能力卓越且兼顾实用性和安全性的模型 + +### 2.3 蒸馏(Distillation) + +
+
+ +
图11:蒸馏流程
+
+
+ +本文的另一个重要发现是关于模型蒸馏的效果:将更强大的模型蒸馏成更小的模型能产生出色的结果。相比之下,直接对小模型进行大规模强化学习训练不仅需要巨大的计算资源,而且性能可能无法达到蒸馏方法的水平。 + +具体而言,这里采用的是 **数据蒸馏(data distillation)** 方法: + +1. 首先使用更强大的模型生成高质量的长链式思维(Long CoT)数据 +2. 然后利用这些数据在小模型上进行监督微调(SFT) + +这种方法既节省计算资源,又能有效地将大模型的推理能力迁移到小模型中。 + +## 3. 实验总结 + +### 3.1 实验设置 + +**Benchmark:** 基准测试包括 MMLU、MMLU-Redux、MMLU-Pro、C-Eval 等多个标准数据集,以及使用 LLM 作为评判的开放式生成任务评估。对于蒸馏模型,在 AIME 2024、MATH-500 等关键数据集上报告结果。 + +> 小细节:使用 LLM 作为 Judegement 时,只将``内的内容送给 LLM。 + +**评估设置:** 遵循 DeepSeek-V3,最大生成长度为**32,768**个 token。为避免贪婪解码导致的重复率问题,采用**pass@k**评估方法,使用 0.6 的采样温度和 0.95 的 top-p 值。 + +### 3.2 DeepSeek-R1 结果 + +
+
+ +
图12:DeepSeek-R1实验结果
+
+
+ +- 在教育知识基准(MMLU、GPQA Diamond 等)上,DeepSeek-R1 优于 DeepSeek-V3,特别是在 STEM 相关问题上。模型在 FRAMES 等长上下文任务和 SimpleQA 等事实基准上表现出色,但在中文 SimpleQA 上因**安全强化学习后**倾向**拒绝某些查询**而表现不如 DeepSeek-V3。 + +- DeepSeek-R1 在 IF-Eval **(格式指令遵循能力)** 和 AlpacaEval2.0、ArenaHard **(写作任务和开放域问答)** 上表现优异,生成的摘要简洁有效,避免了长度偏差。 + +- 在**数学任务**上,DeepSeek-R1 与 OpenAI-o1-1217 相当,显著超过其他模型。在**编码算法任务**(LiveCodeBench、Codeforces)上也表现突出,但在工程导向编码任务上与 OpenAI-o1-1217 相比各有优势,未来版本有望进一步提升工程性能。 + +### 3.3 蒸馏模型结果 + +
+
+ +
图13:蒸馏模型实验结果
+
+
+ +蒸馏模型实验结果显示: + +- DeepSeek-R1-7B(DeepSeek-R1-Distill-Qwen-7B)通过蒸馏**在所有方面优于**非推理模型(如 GPT-4o-0513)。 +- DeepSeek-R1-14B 在所有评估指标上超过 QwQ-32BPreview;DeepSeek-R1-32B 和 DeepSeek-R1-70B 在大多数基准测试中显著超过 o1-mini。 +- 蒸馏方法展现出巨大潜力,对蒸馏模型应用强化学习可带来显著进一步收益。这里的实验仅展示简单 SFT 蒸馏模型结果,值得进一步探索。 + +## 4. 讨论&总结 + +### 4.1 蒸馏 vs 强化学习 + +
+
+ +
图14:蒸馏 vs 强化学习 实验结果
+
+
+ +实验证明,对于小型模型, **蒸馏方法比直接应用强化学习更有效** 。 + +在 Qwen-32B-Base 上进行了大规模 RL 训练(超过 10K 步),得到的 DeepSeek-R1-Zero-Qwen-32B 性能仅与 QwQ-32B-Preview 相当。而通过蒸馏 DeepSeek-R1 得到的 DeepSeek-R1-Distill-Qwen-32B 在所有基准测试中都显著优于直接 RL 训练的模型。 + +因此可以得到两个结论: + +1. 将更强大的模型蒸馏成更小的模型会产生出色的结果,而依赖于本文提到的大规模 RL 的更小的模型需要巨大的计算能力,甚至可能无法达到蒸馏的性能。 +2. 虽然蒸馏策略既经济又有效,但超越智能边界的推进可能仍需要更强大的基础模型和更大规模的强化学习。 + +### 4.2 不成功的尝试 + +- **过程奖励模型(PRM)的局限性** + - PRM 是引导模型改进推理任务解决方法的合理方法 + - 实践中存在三个主要限制: + - 在一般推理中难以明确定义细粒度步骤 + - 难以确定中间步骤的正确性(自动标注效果不佳,人工标注难以扩展) + - 基于模型的 PRM 容易导致奖励黑客行为,重新训练奖励模型需要额外资源 + - 虽然 PRM 在重新排序或辅助搜索方面表现良好,但相比大规模强化学习的计算开销,优势有限 + +> 奖励黑客(Reward Hacking)行为指模型学会了欺骗奖励系统而非真正提高能力,例如通过特定格式或关键词获取高奖励而不实际解决问题,这种行为会随着训练进行而加剧。此外,为防止这种现象,需要定期重新训练奖励模型,这不仅需要额外的计算资源,还会使整个训练流程变得更加复杂。 + +- **蒙特卡洛树搜索(MCTS)的探索与挑战** + - 受 AlphaGo 和 AlphaZero 启发,尝试使用 MCTS 增强测试时计算可扩展性 + - 实现方式:将答案分解成小部分,系统化探索解空间,使用标签对应特定推理步骤 + - 训练过程中遇到的挑战: + - 令牌生成的搜索空间远大于棋类游戏,设置最大扩展限制导致局部最优 + - 价值模型直接影响生成质量,但训练细粒度价值模型本身困难 + - 由于令牌生成的复杂性,难以复制 AlphaGo 的成功经验 + +--- + +以上就是主体内容了,本文对 DeepSeek-R1 论文进行了详细解读,重点分析了其在强化学习方面的创新应用,以及在推理能力提升方面的突破性成果。通过 DeepSeek-R1-Zero 和 DeepSeek-R1 两个阶段的模型训练,展示了纯强化学习和混合训练方法在提升大语言模型推理能力方面的潜力。同时,文章也探讨了蒸馏技术在小型模型上的应用效果,为未来大语言模型的发展提供了新的思路。 + +希望本文能帮助朋友们更好地理解 DeepSeek-R1 的技术原理和实现方法,以及强化学习在大语言模型训练中的应用价值。如有任何问题或建议,欢迎交流讨论。 + +版本时间线 + +- 2025.03.16 第一版 + +## 参考资料 + +[1] Zeng Z, Cheng Q, Yin Z, et al. Scaling of search and learning: A roadmap to reproduce o1 from reinforcement learning perspective[J]. arXiv preprint arXiv:2412.14135, 2024. + +[2] Ouyang L, Wu J, Jiang X, et al. Training language models to follow instructions with human feedback[J]. Advances in neural information processing systems, 2022, 35: 27730-27744. + +[3] 【李宏毅 PPO】:https://www.youtube.com/watch?v=OAKAZhFmYoI + +[4] Shao Z, Wang P, Zhu Q, et al. Deepseekmath: Pushing the limits of mathematical reasoning in open language models[J]. arXiv preprint arXiv:2402.03300, 2024.