-
Notifications
You must be signed in to change notification settings - Fork 0
/
db.json
1 lines (1 loc) · 441 KB
/
db.json
1
{"meta":{"version":1,"warehouse":"4.0.1"},"models":{"Asset":[{"_id":"themes/next/source/css/main.styl","path":"css/main.styl","modified":0,"renderable":1},{"_id":"themes/next/source/images/algolia_logo.svg","path":"images/algolia_logo.svg","modified":0,"renderable":1},{"_id":"themes/next/source/images/apple-touch-icon-next.png","path":"images/apple-touch-icon-next.png","modified":0,"renderable":1},{"_id":"themes/next/source/images/avatar.gif","path":"images/avatar.gif","modified":0,"renderable":1},{"_id":"themes/next/source/images/cc-by-nc-nd.svg","path":"images/cc-by-nc-nd.svg","modified":0,"renderable":1},{"_id":"themes/next/source/images/cc-by-nc-sa.svg","path":"images/cc-by-nc-sa.svg","modified":0,"renderable":1},{"_id":"themes/next/source/images/cc-by-nc.svg","path":"images/cc-by-nc.svg","modified":0,"renderable":1},{"_id":"themes/next/source/images/cc-by-nd.svg","path":"images/cc-by-nd.svg","modified":0,"renderable":1},{"_id":"themes/next/source/images/cc-by-sa.svg","path":"images/cc-by-sa.svg","modified":0,"renderable":1},{"_id":"themes/next/source/images/cc-by.svg","path":"images/cc-by.svg","modified":0,"renderable":1},{"_id":"themes/next/source/images/cc-zero.svg","path":"images/cc-zero.svg","modified":0,"renderable":1},{"_id":"themes/next/source/images/favicon-16x16-next.png","path":"images/favicon-16x16-next.png","modified":0,"renderable":1},{"_id":"themes/next/source/images/favicon-32x32-next.png","path":"images/favicon-32x32-next.png","modified":0,"renderable":1},{"_id":"themes/next/source/images/logo.svg","path":"images/logo.svg","modified":0,"renderable":1},{"_id":"themes/next/source/js/algolia-search.js","path":"js/algolia-search.js","modified":0,"renderable":1},{"_id":"themes/next/source/js/bookmark.js","path":"js/bookmark.js","modified":0,"renderable":1},{"_id":"themes/next/source/js/local-search.js","path":"js/local-search.js","modified":0,"renderable":1},{"_id":"themes/next/source/js/motion.js","path":"js/motion.js","modified":0,"renderable":1},{"_id":"themes/next/source/js/next-boot.js","path":"js/next-boot.js","modified":0,"renderable":1},{"_id":"themes/next/source/js/utils.js","path":"js/utils.js","modified":0,"renderable":1},{"_id":"themes/next/source/lib/anime.min.js","path":"lib/anime.min.js","modified":0,"renderable":1},{"_id":"themes/next/source/js/schemes/muse.js","path":"js/schemes/muse.js","modified":0,"renderable":1},{"_id":"themes/next/source/js/schemes/pisces.js","path":"js/schemes/pisces.js","modified":0,"renderable":1},{"_id":"themes/next/source/lib/velocity/velocity.min.js","path":"lib/velocity/velocity.min.js","modified":0,"renderable":1},{"_id":"themes/next/source/lib/velocity/velocity.ui.min.js","path":"lib/velocity/velocity.ui.min.js","modified":0,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/css/all.min.css","path":"lib/font-awesome/css/all.min.css","modified":0,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/webfonts/fa-brands-400.woff2","path":"lib/font-awesome/webfonts/fa-brands-400.woff2","modified":0,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/webfonts/fa-regular-400.woff2","path":"lib/font-awesome/webfonts/fa-regular-400.woff2","modified":0,"renderable":1},{"_id":"themes/next/source/lib/font-awesome/webfonts/fa-solid-900.woff2","path":"lib/font-awesome/webfonts/fa-solid-900.woff2","modified":0,"renderable":1},{"_id":"source/CNAME","path":"CNAME","modified":0,"renderable":0},{"_id":"source/about/me.jpg","path":"about/me.jpg","modified":0,"renderable":0},{"_id":"source/images/luffy.jpeg","path":"images/luffy.jpeg","modified":0,"renderable":0},{"_id":"source/images/tree.png","path":"images/tree.png","modified":0,"renderable":0}],"Cache":[{"_id":"source/.DS_Store","hash":"718dc635395afc5dd2e57cde9efdbad76dc6da3b","modified":1652079849459},{"_id":"source/CNAME","hash":"215b8f9388cfd3e9643bd739a98e9821d0512101","modified":1650816910949},{"_id":"source/categories/index.md","hash":"8e7a24bd9e287065792222330e29536ae14dd94c","modified":1650967721443},{"_id":"source/_posts/BayesianOptimization.md","hash":"21c76a5a567cb008c66c6efb4ddd4c76a649f4e8","modified":1652261873447},{"_id":"source/_posts/Dbscan.md","hash":"d0e2f318be66b95ca508750b55e0f9976978ed6e","modified":1652028814484},{"_id":"source/_posts/MicroCause.md","hash":"c4b04cb89b8275b3e51ce6deedee97eb5e17c097","modified":1652757763041},{"_id":"source/_posts/DTW.md","hash":"b3940f18c2023d80ef4b6f77c10a54d299ca5864","modified":1652077002630},{"_id":"source/_posts/GraphRCA.md","hash":"2e063824be2b055c415ec498584d064b5a37e7b7","modified":1652358992704},{"_id":"source/_posts/IsolationTree.md","hash":"f40de274224cb907d18c10b37ece26beeb5a2067","modified":1652024372109},{"_id":"source/about/index.md","hash":"0b6084bcbf0f5ac58fcdd2979a58d41c61282bc0","modified":1651943669854},{"_id":"source/_posts/PageRank.md","hash":"e861857061c9a83691b19545200813652f7bd587","modified":1652547816145},{"_id":"source/_posts/SVM.md","hash":"975596de2054a42a7a54a1665a2e35a34450fcc7","modified":1652279408178},{"_id":"source/images/.DS_Store","hash":"82f22c361789de56dc09e6d596d37b76009ade1f","modified":1650723558697},{"_id":"source/images/luffy.jpeg","hash":"662b7f753206d9c3d48698ee590da883e988d045","modified":1650721628618},{"_id":"source/tags/index.md","hash":"dd2b115a70e0704857856e375d888526a6d643bc","modified":1650967778921},{"_id":"source/images/tree.png","hash":"562e0918a8ed0f3f2138ef904ac0da888e85b233","modified":1650707259327},{"_id":"themes/next/.editorconfig","hash":"8570735a8d8d034a3a175afd1dd40b39140b3e6a","modified":1652757539269},{"_id":"themes/next/.eslintrc.json","hash":"cc5f297f0322672fe3f684f823bc4659e4a54c41","modified":1652757539269},{"_id":"themes/next/.gitattributes","hash":"a54f902957d49356376b59287b894b1a3d7a003f","modified":1652757539269},{"_id":"themes/next/.gitignore","hash":"56f3470755c20311ddd30d421b377697a6e5e68b","modified":1652757539272},{"_id":"themes/next/.stylintrc","hash":"2cf4d637b56d8eb423f59656a11f6403aa90f550","modified":1652757539272},{"_id":"themes/next/.travis.yml","hash":"ecca3b919a5b15886e3eca58aa84aafc395590da","modified":1652757539272},{"_id":"themes/next/LICENSE.md","hash":"18144d8ed58c75af66cb419d54f3f63374cd5c5b","modified":1652757539272},{"_id":"themes/next/crowdin.yml","hash":"e026078448c77dcdd9ef50256bb6635a8f83dca6","modified":1652757539273},{"_id":"themes/next/package.json","hash":"62fad6de02adbbba9fb096cbe2dcc15fe25f2435","modified":1652757539300},{"_id":"themes/next/README.md","hash":"9b4b7d66aca47f9c65d6321b14eef48d95c4dff1","modified":1652757539272},{"_id":"themes/next/.github/CODE_OF_CONDUCT.md","hash":"aa4cb7aff595ca628cb58160ee1eee117989ec4e","modified":1652757539270},{"_id":"themes/next/.github/PULL_REQUEST_TEMPLATE.md","hash":"1a435c20ae8fa183d49bbf96ac956f7c6c25c8af","modified":1652757539271},{"_id":"themes/next/.github/CONTRIBUTING.md","hash":"e554931b98f251fd49ff1d2443006d9ea2c20461","modified":1652757539270},{"_id":"themes/next/gulpfile.js","hash":"1b4fc262b89948937b9e3794de812a7c1f2f3592","modified":1652757539277},{"_id":"themes/next/_config.yml","hash":"81a479e06bd27198bde2688c6e0f121a7fa6ff07","modified":1652758309056},{"_id":"themes/next/.github/config.yml","hash":"1d3f4e8794986817c0fead095c74f756d45f91ed","modified":1652757539271},{"_id":"themes/next/.github/issue-close-app.yml","hash":"7cba457eec47dbfcfd4086acd1c69eaafca2f0cd","modified":1652757539271},{"_id":"themes/next/.github/issue_label_bot.yaml","hash":"fca600ddef6f80c5e61aeed21722d191e5606e5b","modified":1652757539271},{"_id":"themes/next/.github/lock.yml","hash":"61173b9522ebac13db2c544e138808295624f7fd","modified":1652757539271},{"_id":"themes/next/.github/mergeable.yml","hash":"0ee56e23bbc71e1e76427d2bd255a9879bd36e22","modified":1652757539271},{"_id":"themes/next/.github/release-drafter.yml","hash":"3cc10ce75ecc03a5ce86b00363e2a17eb65d15ea","modified":1652757539271},{"_id":"themes/next/.github/stale.yml","hash":"fdf82de9284f8bc8e0b0712b4cc1cb081a94de59","modified":1652757539271},{"_id":"themes/next/.github/support.yml","hash":"d75db6ffa7b4ca3b865a925f9de9aef3fc51925c","modified":1652757539272},{"_id":"themes/next/docs/ALGOLIA-SEARCH.md","hash":"c7a994b9542040317d8f99affa1405c143a94a38","modified":1652757539273},{"_id":"themes/next/docs/AUTHORS.md","hash":"10135a2f78ac40e9f46b3add3e360c025400752f","modified":1652757539273},{"_id":"themes/next/docs/INSTALLATION.md","hash":"af88bcce035780aaa061261ed9d0d6c697678618","modified":1652757539274},{"_id":"themes/next/docs/DATA-FILES.md","hash":"cddbdc91ee9e65c37a50bec12194f93d36161616","modified":1652757539274},{"_id":"themes/next/docs/AGPL3.md","hash":"0d2b8c5fa8a614723be0767cc3bca39c49578036","modified":1652757539273},{"_id":"themes/next/docs/LEANCLOUD-COUNTER-SECURITY.md","hash":"94dc3404ccb0e5f663af2aa883c1af1d6eae553d","modified":1652757539274},{"_id":"themes/next/docs/LICENSE.txt","hash":"368bf2c29d70f27d8726dd914f1b3211cae4bbab","modified":1652757539274},{"_id":"themes/next/docs/UPDATE-FROM-5.1.X.md","hash":"8b6e4b2c9cfcb969833092bdeaed78534082e3e6","modified":1652757539274},{"_id":"themes/next/docs/MATH.md","hash":"d645b025ec7fb9fbf799b9bb76af33b9f5b9ed93","modified":1652757539274},{"_id":"themes/next/languages/ar.yml","hash":"9815e84e53d750c8bcbd9193c2d44d8d910e3444","modified":1652757539277},{"_id":"themes/next/languages/de.yml","hash":"74c59f2744217003b717b59d96e275b54635abf5","modified":1652757539277},{"_id":"themes/next/languages/default.yml","hash":"45bc5118828bdc72dcaa25282cd367c8622758cb","modified":1652757539278},{"_id":"themes/next/languages/fa.yml","hash":"3676b32fda37e122f3c1a655085a1868fb6ad66b","modified":1652757539278},{"_id":"themes/next/languages/en.yml","hash":"45bc5118828bdc72dcaa25282cd367c8622758cb","modified":1652757539278},{"_id":"themes/next/languages/es.yml","hash":"c64cf05f356096f1464b4b1439da3c6c9b941062","modified":1652757539278},{"_id":"themes/next/languages/fr.yml","hash":"752bf309f46a2cd43890b82300b342d7218d625f","modified":1652757539278},{"_id":"themes/next/languages/hu.yml","hash":"b1ebb77a5fd101195b79f94de293bcf9001d996f","modified":1652757539278},{"_id":"themes/next/languages/id.yml","hash":"572ed855d47aafe26f58c73b1394530754881ec2","modified":1652757539279},{"_id":"themes/next/languages/it.yml","hash":"44759f779ce9c260b895532de1d209ad4bd144bf","modified":1652757539279},{"_id":"themes/next/languages/ko.yml","hash":"0feea9e43cd399f3610b94d755a39fff1d371e97","modified":1652757539279},{"_id":"themes/next/languages/ja.yml","hash":"0cf0baa663d530f22ff380a051881216d6adcdd8","modified":1652757539279},{"_id":"themes/next/languages/pt-BR.yml","hash":"67555b1ba31a0242b12fc6ce3add28531160e35b","modified":1652757539280},{"_id":"themes/next/languages/nl.yml","hash":"5af3473d9f22897204afabc08bb984b247493330","modified":1652757539279},{"_id":"themes/next/languages/pt.yml","hash":"718d131f42f214842337776e1eaddd1e9a584054","modified":1652757539280},{"_id":"themes/next/languages/ru.yml","hash":"e993d5ca072f7f6887e30fc0c19b4da791ca7a88","modified":1652757539280},{"_id":"themes/next/languages/tr.yml","hash":"2b041eeb8bd096f549464f191cfc1ea0181daca4","modified":1652757539280},{"_id":"themes/next/languages/uk.yml","hash":"3a6d635b1035423b22fc86d9455dba9003724de9","modified":1652757539280},{"_id":"themes/next/languages/vi.yml","hash":"93393b01df148dcbf0863f6eee8e404e2d94ef9e","modified":1652757539281},{"_id":"themes/next/layout/_layout.swig","hash":"6a6e92a4664cdb981890a27ac11fd057f44de1d5","modified":1652757539282},{"_id":"themes/next/languages/zh-CN.yml","hash":"a1f15571ee7e1e84e3cc0985c3ec4ba1a113f6f8","modified":1652757539281},{"_id":"themes/next/languages/zh-HK.yml","hash":"3789f94010f948e9f23e21235ef422a191753c65","modified":1652757539281},{"_id":"themes/next/languages/zh-TW.yml","hash":"8c09da7c4ec3fca2c6ee897b2eea260596a2baa1","modified":1652757539281},{"_id":"themes/next/layout/archive.swig","hash":"e4e31317a8df68f23156cfc49e9b1aa9a12ad2ed","modified":1652757539298},{"_id":"themes/next/layout/category.swig","hash":"1bde61cf4d2d171647311a0ac2c5c7933f6a53b0","modified":1652757539298},{"_id":"themes/next/layout/index.swig","hash":"7f403a18a68e6d662ae3e154b2c1d3bbe0801a23","modified":1652757539299},{"_id":"themes/next/scripts/renderer.js","hash":"49a65df2028a1bc24814dc72fa50d52231ca4f05","modified":1652757539305},{"_id":"themes/next/layout/page.swig","hash":"db581bdeac5c75fabb0f17d7c5e746e47f2a9168","modified":1652757539299},{"_id":"themes/next/layout/post.swig","hash":"2f6d992ced7e067521fdce05ffe4fd75481f41c5","modified":1652757539299},{"_id":"themes/next/layout/tag.swig","hash":"0dfb653bd5de980426d55a0606d1ab122bd8c017","modified":1652757539299},{"_id":"themes/next/.github/ISSUE_TEMPLATE/bug-report.md","hash":"c3e6b8196c983c40fd140bdeca012d03e6e86967","modified":1652757539270},{"_id":"themes/next/.github/ISSUE_TEMPLATE/feature-request.md","hash":"12d99fb8b62bd9e34d9672f306c9ae4ace7e053e","modified":1652757539270},{"_id":"themes/next/.github/ISSUE_TEMPLATE/other.md","hash":"d3efc0df0275c98440e69476f733097916a2d579","modified":1652757539270},{"_id":"themes/next/.github/ISSUE_TEMPLATE/question.md","hash":"53df7d537e26aaf062d70d86835c5fd8f81412f3","modified":1652757539270},{"_id":"themes/next/docs/ru/DATA-FILES.md","hash":"0bd2d696f62a997a11a7d84fec0130122234174e","modified":1652757539274},{"_id":"themes/next/docs/ru/README.md","hash":"85dd68ed1250897a8e4a444a53a68c1d49eb7e11","modified":1652757539275},{"_id":"themes/next/docs/ru/INSTALLATION.md","hash":"9c4fe2873123bf9ceacab5c50d17d8a0f1baef27","modified":1652757539275},{"_id":"themes/next/docs/zh-CN/ALGOLIA-SEARCH.md","hash":"34b88784ec120dfdc20fa82aadeb5f64ef614d14","modified":1652757539275},{"_id":"themes/next/docs/zh-CN/CODE_OF_CONDUCT.md","hash":"fb23b85db6f7d8279d73ae1f41631f92f64fc864","modified":1652757539275},{"_id":"themes/next/docs/ru/UPDATE-FROM-5.1.X.md","hash":"5237a368ab99123749d724b6c379415f2c142a96","modified":1652757539275},{"_id":"themes/next/docs/zh-CN/DATA-FILES.md","hash":"ca1030efdfca5e20f9db2e7a428998e66a24c0d0","modified":1652757539276},{"_id":"themes/next/docs/zh-CN/CONTRIBUTING.md","hash":"d3f03be036b75dc71cf3c366cd75aee7c127c874","modified":1652757539275},{"_id":"themes/next/docs/zh-CN/INSTALLATION.md","hash":"579c7bd8341873fb8be4732476d412814f1a3df7","modified":1652757539276},{"_id":"themes/next/docs/zh-CN/LEANCLOUD-COUNTER-SECURITY.md","hash":"8b18f84503a361fc712b0fe4d4568e2f086ca97d","modified":1652757539276},{"_id":"themes/next/docs/zh-CN/MATH.md","hash":"b92585d251f1f9ebe401abb5d932cb920f9b8b10","modified":1652757539276},{"_id":"themes/next/layout/_macro/post-collapse.swig","hash":"9c8dc0b8170679cdc1ee9ee8dbcbaebf3f42897b","modified":1652757539282},{"_id":"themes/next/layout/_macro/sidebar.swig","hash":"71655ca21907e9061b6e8ac52d0d8fbf54d0062b","modified":1652757539283},{"_id":"themes/next/layout/_partials/comments.swig","hash":"db6ab5421b5f4b7cb32ac73ad0e053fdf065f83e","modified":1652757539283},{"_id":"themes/next/docs/zh-CN/README.md","hash":"c038629ff8f3f24e8593c4c8ecf0bef3a35c750d","modified":1652757539276},{"_id":"themes/next/docs/zh-CN/UPDATE-FROM-5.1.X.md","hash":"d9ce7331c1236bbe0a551d56cef2405e47e65325","modified":1652757539277},{"_id":"themes/next/layout/_partials/languages.swig","hash":"ba9e272f1065b8f0e8848648caa7dea3f02c6be1","modified":1652757539285},{"_id":"themes/next/layout/_partials/pagination.swig","hash":"9876dbfc15713c7a47d4bcaa301f4757bd978269","modified":1652757539286},{"_id":"themes/next/layout/_macro/post.swig","hash":"090b5a9b6fca8e968178004cbd6cff205b7eba57","modified":1652757539282},{"_id":"themes/next/layout/_partials/footer.swig","hash":"4369b313cbbeae742cb35f86d23d99d4285f7359","modified":1652757539283},{"_id":"themes/next/layout/_third-party/quicklink.swig","hash":"311e5eceec9e949f1ea8d623b083cec0b8700ff2","modified":1652757539295},{"_id":"themes/next/layout/_partials/widgets.swig","hash":"83a40ce83dfd5cada417444fb2d6f5470aae6bb0","modified":1652757539289},{"_id":"themes/next/layout/_third-party/baidu-push.swig","hash":"b782eb2e34c0c15440837040b5d65b093ab6ec04","modified":1652757539293},{"_id":"themes/next/layout/_third-party/index.swig","hash":"70c3c01dd181de81270c57f3d99b6d8f4c723404","modified":1652757539295},{"_id":"themes/next/layout/_third-party/rating.swig","hash":"2731e262a6b88eaee2a3ca61e6a3583a7f594702","modified":1652757539296},{"_id":"themes/next/layout/_scripts/noscript.swig","hash":"d1f2bfde6f1da51a2b35a7ab9e7e8eb6eefd1c6b","modified":1652757539289},{"_id":"themes/next/layout/_scripts/index.swig","hash":"cea942b450bcb0f352da78d76dc6d6f1d23d5029","modified":1652757539289},{"_id":"themes/next/scripts/events/index.js","hash":"5743cde07f3d2aa11532a168a652e52ec28514fd","modified":1652757539300},{"_id":"themes/next/scripts/filters/default-injects.js","hash":"aec50ed57b9d5d3faf2db3c88374f107203617e0","modified":1652757539303},{"_id":"themes/next/scripts/filters/front-matter.js","hash":"703bdd142a671b4b67d3d9dfb4a19d1dd7e7e8f7","modified":1652757539303},{"_id":"themes/next/scripts/filters/locals.js","hash":"b193a936ee63451f09f8886343dcfdca577c0141","modified":1652757539303},{"_id":"themes/next/scripts/filters/minify.js","hash":"19985723b9f677ff775f3b17dcebf314819a76ac","modified":1652757539303},{"_id":"themes/next/scripts/filters/post.js","hash":"44ba9b1c0bdda57590b53141306bb90adf0678db","modified":1652757539304},{"_id":"themes/next/scripts/helpers/engine.js","hash":"bdb424c3cc0d145bd0c6015bb1d2443c8a9c6cda","modified":1652757539304},{"_id":"themes/next/scripts/helpers/font.js","hash":"40cf00e9f2b7aa6e5f33d412e03ed10304b15fd7","modified":1652757539304},{"_id":"themes/next/scripts/helpers/next-config.js","hash":"5e11f30ddb5093a88a687446617a46b048fa02e5","modified":1652757539304},{"_id":"themes/next/scripts/helpers/next-url.js","hash":"958e86b2bd24e4fdfcbf9ce73e998efe3491a71f","modified":1652757539304},{"_id":"themes/next/scripts/tags/button.js","hash":"8c6b45f36e324820c919a822674703769e6da32c","modified":1652757539305},{"_id":"themes/next/scripts/tags/caniuse.js","hash":"94e0bbc7999b359baa42fa3731bdcf89c79ae2b3","modified":1652757539305},{"_id":"themes/next/scripts/tags/center-quote.js","hash":"f1826ade2d135e2f60e2d95cb035383685b3370c","modified":1652757539305},{"_id":"themes/next/scripts/tags/group-pictures.js","hash":"d902fd313e8d35c3cc36f237607c2a0536c9edf1","modified":1652757539305},{"_id":"themes/next/scripts/tags/label.js","hash":"fc5b267d903facb7a35001792db28b801cccb1f8","modified":1652757539306},{"_id":"themes/next/scripts/tags/mermaid.js","hash":"983c6c4adea86160ecc0ba2204bc312aa338121d","modified":1652757539306},{"_id":"themes/next/scripts/tags/note.js","hash":"0a02bb4c15aec41f6d5f1271cdb5c65889e265d9","modified":1652757539306},{"_id":"themes/next/scripts/tags/pdf.js","hash":"8c613b39e7bff735473e35244b5629d02ee20618","modified":1652757539306},{"_id":"themes/next/scripts/tags/tabs.js","hash":"93d8a734a3035c1d3f04933167b500517557ba3e","modified":1652757539306},{"_id":"themes/next/scripts/tags/video.js","hash":"e5ff4c44faee604dd3ea9db6b222828c4750c227","modified":1652757539307},{"_id":"themes/next/source/css/_colors.styl","hash":"a8442520f719d3d7a19811cb3b85bcfd4a596e1f","modified":1652757539307},{"_id":"themes/next/source/css/_mixins.styl","hash":"e31a557f8879c2f4d8d5567ee1800b3e03f91f6e","modified":1652757539323},{"_id":"themes/next/source/css/main.styl","hash":"a3a3bbb5a973052f0186b3523911cb2539ff7b88","modified":1652757539328},{"_id":"themes/next/layout/_scripts/pjax.swig","hash":"4d2c93c66e069852bb0e3ea2e268d213d07bfa3f","modified":1652757539290},{"_id":"themes/next/layout/_scripts/vendors.swig","hash":"ef38c213679e7b6d2a4116f56c9e55d678446069","modified":1652757539291},{"_id":"themes/next/layout/_scripts/three.swig","hash":"a4f42f2301866bd25a784a2281069d8b66836d0b","modified":1652757539291},{"_id":"themes/next/source/images/algolia_logo.svg","hash":"ec119560b382b2624e00144ae01c137186e91621","modified":1652757539329},{"_id":"themes/next/source/images/avatar.gif","hash":"18c53e15eb0c84b139995f9334ed8522b40aeaf6","modified":1652757539329},{"_id":"themes/next/source/images/apple-touch-icon-next.png","hash":"2959dbc97f31c80283e67104fe0854e2369e40aa","modified":1652757539329},{"_id":"themes/next/source/images/cc-by-nc-sa.svg","hash":"3031be41e8753c70508aa88e84ed8f4f653f157e","modified":1652757539330},{"_id":"themes/next/source/images/cc-by-nc.svg","hash":"8d39b39d88f8501c0d27f8df9aae47136ebc59b7","modified":1652757539330},{"_id":"themes/next/source/images/cc-by-nc-nd.svg","hash":"c6524ece3f8039a5f612feaf865d21ec8a794564","modified":1652757539330},{"_id":"themes/next/source/images/cc-by-nd.svg","hash":"c563508ce9ced1e66948024ba1153400ac0e0621","modified":1652757539330},{"_id":"themes/next/source/images/cc-by-sa.svg","hash":"aa4742d733c8af8d38d4c183b8adbdcab045872e","modified":1652757539330},{"_id":"themes/next/source/images/cc-by.svg","hash":"28a0a4fe355a974a5e42f68031652b76798d4f7e","modified":1652757539331},{"_id":"themes/next/source/images/favicon-32x32-next.png","hash":"0749d7b24b0d2fae1c8eb7f671ad4646ee1894b1","modified":1652757539331},{"_id":"themes/next/source/images/cc-zero.svg","hash":"87669bf8ac268a91d027a0a4802c92a1473e9030","modified":1652757539331},{"_id":"themes/next/source/images/favicon-16x16-next.png","hash":"943a0d67a9cdf8c198109b28f9dbd42f761d11c3","modified":1652757539331},{"_id":"themes/next/source/js/local-search.js","hash":"35ccf100d8f9c0fd6bfbb7fa88c2a76c42a69110","modified":1652757539332},{"_id":"themes/next/source/js/motion.js","hash":"72df86f6dfa29cce22abeff9d814c9dddfcf13a9","modified":1652757539332},{"_id":"themes/next/source/js/next-boot.js","hash":"a1b0636423009d4a4e4cea97bcbf1842bfab582c","modified":1652757539333},{"_id":"themes/next/source/js/utils.js","hash":"730cca7f164eaf258661a61ff3f769851ff1e5da","modified":1652757539333},{"_id":"themes/next/source/lib/anime.min.js","hash":"47cb482a8a488620a793d50ba8f6752324b46af3","modified":1652757539334},{"_id":"themes/next/layout/_partials/head/head-unique.swig","hash":"000bad572d76ee95d9c0a78f9ccdc8d97cc7d4b4","modified":1652757539283},{"_id":"themes/next/source/js/algolia-search.js","hash":"498d233eb5c7af6940baf94c1a1c36fdf1dd2636","modified":1652757539332},{"_id":"themes/next/source/images/logo.svg","hash":"d29cacbae1bdc4bbccb542107ee0524fe55ad6de","modified":1652757539332},{"_id":"themes/next/layout/_partials/header/index.swig","hash":"7dbe93b8297b746afb89700b4d29289556e85267","modified":1652757539284},{"_id":"themes/next/layout/_partials/header/menu-item.swig","hash":"9440d8a3a181698b80e1fa47f5104f4565d8cdf3","modified":1652757539284},{"_id":"themes/next/source/js/bookmark.js","hash":"9734ebcb9b83489686f5c2da67dc9e6157e988ad","modified":1652757539332},{"_id":"themes/next/layout/_partials/head/head.swig","hash":"810d544019e4a8651b756dd23e5592ee851eda71","modified":1652757539284},{"_id":"themes/next/layout/_partials/header/sub-menu.swig","hash":"ae2261bea836581918a1c2b0d1028a78718434e0","modified":1652757539285},{"_id":"themes/next/layout/_partials/header/brand.swig","hash":"c70f8e71e026e878a4e9d5ab3bbbf9b0b23c240c","modified":1652757539284},{"_id":"themes/next/layout/_partials/header/menu.swig","hash":"d31f896680a6c2f2c3f5128b4d4dd46c87ce2130","modified":1652757539285},{"_id":"themes/next/layout/_partials/post/post-copyright.swig","hash":"954ad71536b6eb08bd1f30ac6e2f5493b69d1c04","modified":1652757539286},{"_id":"themes/next/layout/_partials/page/page-header.swig","hash":"9b7a66791d7822c52117fe167612265356512477","modified":1652757539285},{"_id":"themes/next/layout/_partials/page/breadcrumb.swig","hash":"c851717497ca64789f2176c9ecd1dedab237b752","modified":1652757539285},{"_id":"themes/next/layout/_partials/post/post-followme.swig","hash":"ceba16b9bd3a0c5c8811af7e7e49d0f9dcb2f41e","modified":1652757539286},{"_id":"themes/next/layout/_partials/post/post-related.swig","hash":"f79c44692451db26efce704813f7a8872b7e63a0","modified":1652757539287},{"_id":"themes/next/layout/_partials/post/post-footer.swig","hash":"8f14f3f8a1b2998d5114cc56b680fb5c419a6b07","modified":1652757539287},{"_id":"themes/next/layout/_partials/search/algolia-search.swig","hash":"48430bd03b8f19c9b8cdb2642005ed67d56c6e0b","modified":1652757539288},{"_id":"themes/next/layout/_partials/post/post-reward.swig","hash":"2b1a73556595c37951e39574df5a3f20b2edeaef","modified":1652757539287},{"_id":"themes/next/layout/_partials/search/index.swig","hash":"2be50f9bfb1c56b85b3b6910a7df27f51143632c","modified":1652757539288},{"_id":"themes/next/layout/_partials/sidebar/site-overview.swig","hash":"c46849e0af8f8fb78baccd40d2af14df04a074af","modified":1652757539289},{"_id":"themes/next/layout/_partials/search/localsearch.swig","hash":"f48a6a8eba04eb962470ce76dd731e13074d4c45","modified":1652757539288},{"_id":"themes/next/layout/_third-party/analytics/google-analytics.swig","hash":"2fa2b51d56bfac6a1ea76d651c93b9c20b01c09b","modified":1652757539292},{"_id":"themes/next/layout/_third-party/analytics/growingio.swig","hash":"5adea065641e8c55994dd2328ddae53215604928","modified":1652757539292},{"_id":"themes/next/layout/_third-party/analytics/baidu-analytics.swig","hash":"4790058691b7d36cf6d2d6b4e93795a7b8d608ad","modified":1652757539292},{"_id":"themes/next/layout/_third-party/analytics/index.swig","hash":"1472cabb0181f60a6a0b7fec8899a4d03dfb2040","modified":1652757539292},{"_id":"themes/next/layout/_third-party/chat/chatra.swig","hash":"f910618292c63871ca2e6c6e66c491f344fa7b1f","modified":1652757539293},{"_id":"themes/next/layout/_third-party/chat/tidio.swig","hash":"cba0e6e0fad08568a9e74ba9a5bee5341cfc04c1","modified":1652757539293},{"_id":"themes/next/layout/_third-party/comments/changyan.swig","hash":"f39a5bf3ce9ee9adad282501235e0c588e4356ec","modified":1652757539293},{"_id":"themes/next/layout/_third-party/comments/disqusjs.swig","hash":"82f5b6822aa5ec958aa987b101ef860494c6cf1f","modified":1652757539294},{"_id":"themes/next/layout/_third-party/comments/disqus.swig","hash":"b14908644225d78c864cd0a9b60c52407de56183","modified":1652757539294},{"_id":"themes/next/layout/_third-party/comments/gitalk.swig","hash":"d6ceb70648555338a80ae5724b778c8c58d7060d","modified":1652757539294},{"_id":"themes/next/layout/_third-party/math/index.swig","hash":"6c5976621efd5db5f7c4c6b4f11bc79d6554885f","modified":1652757539295},{"_id":"themes/next/layout/_third-party/comments/livere.swig","hash":"f7a9eca599a682479e8ca863db59be7c9c7508c8","modified":1652757539294},{"_id":"themes/next/layout/_third-party/math/katex.swig","hash":"4791c977a730f29c846efcf6c9c15131b9400ead","modified":1652757539295},{"_id":"themes/next/layout/_third-party/comments/valine.swig","hash":"be0a8eccf1f6dc21154af297fc79555343031277","modified":1652757539294},{"_id":"themes/next/layout/_third-party/math/mathjax.swig","hash":"ecf751321e799f0fb3bf94d049e535130e2547aa","modified":1652757539295},{"_id":"themes/next/layout/_third-party/statistics/busuanzi-counter.swig","hash":"4b1986e43d6abce13450d2b41a736dd6a5620a10","modified":1652757539297},{"_id":"themes/next/layout/_third-party/statistics/cnzz-analytics.swig","hash":"a17ace37876822327a2f9306a472974442c9005d","modified":1652757539297},{"_id":"themes/next/layout/_third-party/statistics/firestore.swig","hash":"b26ac2bfbe91dd88267f8b96aee6bb222b265b7a","modified":1652757539297},{"_id":"themes/next/layout/_third-party/statistics/index.swig","hash":"5f6a966c509680dbfa70433f9d658cee59c304d7","modified":1652757539297},{"_id":"themes/next/layout/_third-party/statistics/lean-analytics.swig","hash":"d56d5af427cdfecc33a0f62ee62c056b4e33d095","modified":1652757539297},{"_id":"themes/next/layout/_third-party/search/algolia-search.swig","hash":"d35a999d67f4c302f76fdf13744ceef3c6506481","modified":1652757539296},{"_id":"themes/next/layout/_third-party/search/localsearch.swig","hash":"767b6c714c22588bcd26ba70b0fc19b6810cbacd","modified":1652757539296},{"_id":"themes/next/layout/_third-party/search/swiftype.swig","hash":"ba0dbc06b9d244073a1c681ff7a722dcbf920b51","modified":1652757539296},{"_id":"themes/next/layout/_third-party/tags/pdf.swig","hash":"d30b0e255a8092043bac46441243f943ed6fb09b","modified":1652757539298},{"_id":"themes/next/layout/_third-party/tags/mermaid.swig","hash":"f3c43664a071ff3c0b28bd7e59b5523446829576","modified":1652757539298},{"_id":"themes/next/layout/_scripts/pages/schedule.swig","hash":"077b5d66f6309f2e7dcf08645058ff2e03143e6c","modified":1652757539290},{"_id":"themes/next/layout/_scripts/schemes/gemini.swig","hash":"1c910fc066c06d5fbbe9f2b0c47447539e029af7","modified":1652757539290},{"_id":"themes/next/layout/_scripts/schemes/mist.swig","hash":"7f14ef43d9e82bc1efc204c5adf0b1dbfc919a9f","modified":1652757539291},{"_id":"themes/next/scripts/events/lib/config.js","hash":"d34c6040b13649714939f59be5175e137de65ede","modified":1652757539300},{"_id":"themes/next/scripts/events/lib/injects.js","hash":"f233d8d0103ae7f9b861344aa65c1a3c1de8a845","modified":1652757539301},{"_id":"themes/next/layout/_scripts/schemes/muse.swig","hash":"7f14ef43d9e82bc1efc204c5adf0b1dbfc919a9f","modified":1652757539291},{"_id":"themes/next/scripts/events/lib/injects-point.js","hash":"6661c1c91c7cbdefc6a5e6a034b443b8811235a1","modified":1652757539301},{"_id":"themes/next/scripts/filters/comment/changyan.js","hash":"a54708fd9309b4357c423a3730eb67f395344a5e","modified":1652757539301},{"_id":"themes/next/scripts/filters/comment/common.js","hash":"2486f3e0150c753e5f3af1a3665d074704b8ee2c","modified":1652757539301},{"_id":"themes/next/scripts/filters/comment/disqusjs.js","hash":"7f8b92913d21070b489457fa5ed996d2a55f2c32","modified":1652757539302},{"_id":"themes/next/scripts/filters/comment/disqus.js","hash":"4c0c99c7e0f00849003dfce02a131104fb671137","modified":1652757539302},{"_id":"themes/next/layout/_scripts/schemes/pisces.swig","hash":"1c910fc066c06d5fbbe9f2b0c47447539e029af7","modified":1652757539291},{"_id":"themes/next/scripts/filters/comment/default-config.js","hash":"7f2d93af012c1e14b8596fecbfc7febb43d9b7f5","modified":1652757539302},{"_id":"themes/next/scripts/filters/comment/gitalk.js","hash":"e51dc3072c1ba0ea3008f09ecae8b46242ec6021","modified":1652757539302},{"_id":"themes/next/scripts/filters/comment/valine.js","hash":"6cbd85f9433c06bae22225ccf75ac55e04f2d106","modified":1652757539303},{"_id":"themes/next/scripts/filters/comment/livere.js","hash":"d5fefc31fba4ab0188305b1af1feb61da49fdeb0","modified":1652757539302},{"_id":"themes/next/source/css/_variables/Pisces.styl","hash":"612ec843372dae709acb17112c1145a53450cc59","modified":1652757539328},{"_id":"themes/next/source/css/_variables/base.styl","hash":"818508748b7a62e02035e87fe58e75b603ed56dc","modified":1652757539328},{"_id":"themes/next/source/css/_variables/Gemini.styl","hash":"f4e694e5db81e57442c7e34505a416d818b3044a","modified":1652757539327},{"_id":"themes/next/source/css/_variables/Muse.styl","hash":"62df49459d552bbf73841753da8011a1f5e875c8","modified":1652757539328},{"_id":"themes/next/source/js/schemes/pisces.js","hash":"0ac5ce155bc58c972fe21c4c447f85e6f8755c62","modified":1652757539333},{"_id":"themes/next/source/lib/velocity/velocity.ui.min.js","hash":"ed5e534cd680a25d8d14429af824f38a2c7d9908","modified":1652757539337},{"_id":"themes/next/source/lib/velocity/velocity.min.js","hash":"2f1afadc12e4cf59ef3b405308d21baa97e739c6","modified":1652757539337},{"_id":"themes/next/source/css/_common/components/components.styl","hash":"8e7b57a72e757cf95278239641726bb2d5b869d1","modified":1652757539308},{"_id":"themes/next/source/css/_common/components/back-to-top.styl","hash":"a47725574e1bee3bc3b63b0ff2039cc982b17eff","modified":1652757539308},{"_id":"themes/next/source/css/_variables/Mist.styl","hash":"f70be8e229da7e1715c11dd0e975a2e71e453ac8","modified":1652757539328},{"_id":"themes/next/source/css/_common/components/back-to-top-sidebar.styl","hash":"ca5e70662dcfb261c25191cc5db5084dcf661c76","modified":1652757539307},{"_id":"themes/next/source/css/_common/outline/outline.styl","hash":"a1690e035b505d28bdef2b4424c13fc6312ab049","modified":1652757539315},{"_id":"themes/next/source/css/_common/scaffolding/base.styl","hash":"0b2c4b78eead410020d7c4ded59c75592a648df8","modified":1652757539318},{"_id":"themes/next/source/css/_common/scaffolding/buttons.styl","hash":"a2e9e00962e43e98ec2614d6d248ef1773bb9b78","modified":1652757539318},{"_id":"themes/next/source/css/_common/scaffolding/comments.styl","hash":"b1f0fab7344a20ed6748b04065b141ad423cf4d9","modified":1652757539318},{"_id":"themes/next/source/css/_common/scaffolding/normalize.styl","hash":"b56367ea676ea8e8783ea89cd4ab150c7da7a060","modified":1652757539320},{"_id":"themes/next/source/css/_common/scaffolding/pagination.styl","hash":"8f58570a1bbc34c4989a47a1b7d42a8030f38b06","modified":1652757539320},{"_id":"themes/next/source/css/_common/components/reading-progress.styl","hash":"2e3bf7baf383c9073ec5e67f157d3cb3823c0957","modified":1652757539312},{"_id":"themes/next/source/css/_common/scaffolding/scaffolding.styl","hash":"523fb7b653b87ae37fc91fc8813e4ffad87b0d7e","modified":1652757539320},{"_id":"themes/next/source/css/_common/scaffolding/tables.styl","hash":"18ce72d90459c9aa66910ac64eae115f2dde3767","modified":1652757539321},{"_id":"themes/next/source/js/schemes/muse.js","hash":"1eb9b88103ddcf8827b1a7cbc56471a9c5592d53","modified":1652757539333},{"_id":"themes/next/source/css/_common/scaffolding/toggles.styl","hash":"179e33b8ac7f4d8a8e76736a7e4f965fe9ab8b42","modified":1652757539323},{"_id":"themes/next/source/css/_schemes/Muse/_layout.styl","hash":"4d1c17345d2d39ef7698f7acf82dfc0f59308c34","modified":1652757539325},{"_id":"themes/next/source/css/_common/outline/mobile.styl","hash":"681d33e3bc85bdca407d93b134c089264837378c","modified":1652757539315},{"_id":"themes/next/source/css/_schemes/Muse/_header.styl","hash":"f0131db6275ceaecae7e1a6a3798b8f89f6c850d","modified":1652757539325},{"_id":"themes/next/source/css/_schemes/Gemini/index.styl","hash":"7785bd756e0c4acede3a47fec1ed7b55988385a5","modified":1652757539323},{"_id":"themes/next/source/css/_schemes/Muse/_menu.styl","hash":"93db5dafe9294542a6b5f647643cb9deaced8e06","modified":1652757539325},{"_id":"themes/next/source/css/_schemes/Muse/_sidebar.styl","hash":"2b2e7b5cea7783c9c8bb92655e26a67c266886f0","modified":1652757539325},{"_id":"themes/next/source/css/_schemes/Muse/_sub-menu.styl","hash":"c48ccd8d6651fe1a01faff8f01179456d39ba9b1","modified":1652757539326},{"_id":"themes/next/source/css/_schemes/Muse/index.styl","hash":"6ad168288b213cec357e9b5a97674ff2ef3a910c","modified":1652757539326},{"_id":"themes/next/source/css/_schemes/Mist/_header.styl","hash":"f6516d0f7d89dc7b6c6e143a5af54b926f585d82","modified":1652757539324},{"_id":"themes/next/source/css/_schemes/Mist/_menu.styl","hash":"7104b9cef90ca3b140d7a7afcf15540a250218fc","modified":1652757539324},{"_id":"themes/next/source/css/_schemes/Mist/_layout.styl","hash":"bb7ace23345364eb14983e860a7172e1683a4c94","modified":1652757539324},{"_id":"themes/next/source/css/_schemes/Mist/index.styl","hash":"a717969829fa6ef88225095737df3f8ee86c286b","modified":1652757539324},{"_id":"themes/next/source/css/_schemes/Pisces/_header.styl","hash":"e282df938bd029f391c466168d0e68389978f120","modified":1652757539326},{"_id":"themes/next/source/css/_schemes/Pisces/_sidebar.styl","hash":"44f47c88c06d89d06f220f102649057118715828","modified":1652757539327},{"_id":"themes/next/source/css/_schemes/Mist/_posts-expand.styl","hash":"6136da4bbb7e70cec99f5c7ae8c7e74f5e7c261a","modified":1652757539324},{"_id":"themes/next/source/css/_schemes/Pisces/_layout.styl","hash":"70a4324b70501132855b5e59029acfc5d3da1ebd","modified":1652757539326},{"_id":"themes/next/source/css/_schemes/Pisces/_sub-menu.styl","hash":"e740deadcfc4f29c5cb01e40f9df6277262ba4e3","modified":1652757539327},{"_id":"themes/next/source/css/_schemes/Pisces/index.styl","hash":"6ad168288b213cec357e9b5a97674ff2ef3a910c","modified":1652757539327},{"_id":"themes/next/source/lib/font-awesome/webfonts/fa-regular-400.woff2","hash":"260bb01acd44d88dcb7f501a238ab968f86bef9e","modified":1652757539336},{"_id":"themes/next/source/lib/font-awesome/css/all.min.css","hash":"0038dc97c79451578b7bd48af60ba62282b4082b","modified":1652757539335},{"_id":"themes/next/source/css/_common/components/pages/breadcrumb.styl","hash":"fafc96c86926b22afba8bb9418c05e6afbc05a57","modified":1652757539308},{"_id":"themes/next/source/css/_common/components/pages/categories.styl","hash":"2bd0eb1512415325653b26d62a4463e6de83c5ac","modified":1652757539308},{"_id":"themes/next/source/css/_common/components/pages/pages.styl","hash":"7504dbc5c70262b048143b2c37d2b5aa2809afa2","modified":1652757539308},{"_id":"themes/next/source/css/_common/components/pages/schedule.styl","hash":"e771dcb0b4673e063c0f3e2d73e7336ac05bcd57","modified":1652757539309},{"_id":"themes/next/source/css/_common/components/pages/tag-cloud.styl","hash":"d21d4ac1982c13d02f125a67c065412085a92ff2","modified":1652757539309},{"_id":"themes/next/source/css/_common/components/post/post-collapse.styl","hash":"e75693f33dbc92afc55489438267869ae2f3db54","modified":1652757539309},{"_id":"themes/next/source/css/_common/components/post/post-eof.styl","hash":"902569a9dea90548bec21a823dd3efd94ff7c133","modified":1652757539310},{"_id":"themes/next/source/css/_common/components/post/post-expand.styl","hash":"ded41fd9d20a5e8db66aaff7cc50f105f5ef2952","modified":1652757539310},{"_id":"themes/next/source/css/_schemes/Pisces/_menu.styl","hash":"85da2f3006f4bef9a2199416ecfab4d288f848c4","modified":1652757539326},{"_id":"themes/next/source/css/_common/components/post/post-gallery.styl","hash":"72d495a88f7d6515af425c12cbc67308a57d88ea","modified":1652757539310},{"_id":"themes/next/source/css/_common/components/post/post-header.styl","hash":"65cb6edb69e94e70e3291e9132408361148d41d5","modified":1652757539310},{"_id":"themes/next/source/css/_common/components/post/post-nav.styl","hash":"6a97bcfa635d637dc59005be3b931109e0d1ead5","modified":1652757539311},{"_id":"themes/next/source/css/_common/components/post/post-reward.styl","hash":"d114b2a531129e739a27ba6271cfe6857aa9a865","modified":1652757539311},{"_id":"themes/next/source/css/_common/components/post/post-rtl.styl","hash":"f5c2788a78790aca1a2f37f7149d6058afb539e0","modified":1652757539311},{"_id":"themes/next/source/css/_common/components/post/post-tags.styl","hash":"99e12c9ce3d14d4837e3d3f12fc867ba9c565317","modified":1652757539311},{"_id":"themes/next/source/css/_common/components/post/post-widgets.styl","hash":"5b5649b9749e3fd8b63aef22ceeece0a6e1df605","modified":1652757539311},{"_id":"themes/next/source/css/_common/components/post/post.styl","hash":"a760ee83ba6216871a9f14c5e56dc9bd0d9e2103","modified":1652757539312},{"_id":"themes/next/source/css/_common/components/third-party/gitalk.styl","hash":"8a7fc03a568b95be8d3337195e38bc7ec5ba2b23","modified":1652757539312},{"_id":"themes/next/source/css/_common/components/third-party/math.styl","hash":"b49e9fbd3c182b8fc066b8c2caf248e3eb748619","modified":1652757539312},{"_id":"themes/next/source/css/_common/components/third-party/search.styl","hash":"9f0b93d109c9aec79450c8a0cf4a4eab717d674d","modified":1652757539313},{"_id":"themes/next/source/css/_common/components/third-party/third-party.styl","hash":"9a878d0119785a2316f42aebcceaa05a120b9a7a","modified":1652757539313},{"_id":"themes/next/source/css/_common/outline/footer/footer.styl","hash":"454a4aebfabb4469b92a8cbb49f46c49ac9bf165","modified":1652757539313},{"_id":"themes/next/source/css/_common/components/post/post-followme.styl","hash":"1e4190c10c9e0c9ce92653b0dbcec21754b0b69d","modified":1652757539310},{"_id":"themes/next/source/css/_common/components/post/post-copyright.styl","hash":"f49ca072b5a800f735e8f01fc3518f885951dd8e","modified":1652757539309},{"_id":"themes/next/source/css/_common/outline/header/header.styl","hash":"a793cfff86ad4af818faef04c18013077873f8f0","modified":1652757539314},{"_id":"themes/next/source/css/_common/outline/header/headerband.styl","hash":"0caf32492692ba8e854da43697a2ec8a41612194","modified":1652757539314},{"_id":"themes/next/source/css/_common/outline/header/menu.styl","hash":"5f432a6ed9ca80a413c68b00e93d4a411abf280a","modified":1652757539314},{"_id":"themes/next/source/css/_common/outline/header/site-meta.styl","hash":"45a239edca44acecf971d99b04f30a1aafbf6906","modified":1652757539315},{"_id":"themes/next/source/css/_common/outline/header/site-nav.styl","hash":"b2fc519828fe89a1f8f03ff7b809ad68cd46f3d7","modified":1652757539315},{"_id":"themes/next/source/css/_common/outline/sidebar/sidebar-author-links.styl","hash":"2cb1876e9e0c9ac32160888af27b1178dbcb0616","modified":1652757539316},{"_id":"themes/next/source/css/_common/outline/sidebar/sidebar-author.styl","hash":"fa0222197b5eee47e18ac864cdc6eac75678b8fe","modified":1652757539316},{"_id":"themes/next/source/css/_common/outline/sidebar/sidebar-blogroll.styl","hash":"44487d9ab290dc97871fa8dd4487016deb56e123","modified":1652757539316},{"_id":"themes/next/source/css/_common/outline/sidebar/sidebar-button.styl","hash":"1f0e7fbe80956f47087c2458ea880acf7a83078b","modified":1652757539316},{"_id":"themes/next/source/css/_common/outline/sidebar/sidebar-dimmer.styl","hash":"9b479c2f9a9bfed77885e5093b8245cc5d768ec7","modified":1652757539317},{"_id":"themes/next/source/css/_common/outline/sidebar/sidebar-nav.styl","hash":"a960a2dd587b15d3b3fe1b59525d6fa971c6a6ec","modified":1652757539317},{"_id":"themes/next/source/css/_common/components/third-party/related-posts.styl","hash":"e2992846b39bf3857b5104675af02ba73e72eed5","modified":1652757539312},{"_id":"themes/next/source/css/_common/outline/sidebar/sidebar-toc.styl","hash":"a05a4031e799bc864a4536f9ef61fe643cd421af","modified":1652757539317},{"_id":"themes/next/source/css/_common/outline/sidebar/sidebar.styl","hash":"a9cd93c36bae5af9223e7804963096274e8a4f03","modified":1652757539317},{"_id":"themes/next/source/css/_common/outline/header/github-banner.styl","hash":"e7a9fdb6478b8674b1cdf94de4f8052843fb71d9","modified":1652757539314},{"_id":"themes/next/source/css/_common/outline/header/bookmark.styl","hash":"e2d606f1ac343e9be4f15dbbaf3464bc4df8bf81","modified":1652757539313},{"_id":"themes/next/source/css/_common/outline/sidebar/site-state.styl","hash":"2a47f8a6bb589c2fb635e6c1e4a2563c7f63c407","modified":1652757539318},{"_id":"themes/next/source/css/_common/scaffolding/highlight/diff.styl","hash":"d3f73688bb7423e3ab0de1efdf6db46db5e34f80","modified":1652757539319},{"_id":"themes/next/source/css/_common/outline/sidebar/sidebar-toggle.styl","hash":"b3220db827e1adbca7880c2bb23e78fa7cbe95cb","modified":1652757539317},{"_id":"themes/next/source/css/_common/scaffolding/highlight/highlight.styl","hash":"35c871a809afa8306c8cde13651010e282548bc6","modified":1652757539319},{"_id":"themes/next/source/css/_common/scaffolding/tags/blockquote-center.styl","hash":"1d2778ca5aeeeafaa690dc2766b01b352ab76a02","modified":1652757539321},{"_id":"themes/next/source/css/_common/scaffolding/highlight/theme.styl","hash":"3b3acc5caa0b95a2598bef4eeacb21bab21bea56","modified":1652757539320},{"_id":"themes/next/source/css/_common/scaffolding/tags/group-pictures.styl","hash":"709d10f763e357e1472d6471f8be384ec9e2d983","modified":1652757539321},{"_id":"themes/next/source/css/_common/scaffolding/tags/label.styl","hash":"d7fce4b51b5f4b7c31d93a9edb6c6ce740aa0d6b","modified":1652757539321},{"_id":"themes/next/source/css/_common/scaffolding/tags/note.styl","hash":"e4d9a77ffe98e851c1202676940097ba28253313","modified":1652757539321},{"_id":"themes/next/source/css/_common/scaffolding/tags/tabs.styl","hash":"f23670f1d8e749f3e83766d446790d8fd9620278","modified":1652757539322},{"_id":"themes/next/source/css/_common/scaffolding/tags/tags.styl","hash":"9e4c0653cfd3cc6908fa0d97581bcf80861fb1e7","modified":1652757539322},{"_id":"themes/next/source/css/_common/scaffolding/highlight/copy-code.styl","hash":"f71a3e86c05ea668b008cf05a81f67d92b6d65e4","modified":1652757539319},{"_id":"themes/next/source/css/_common/scaffolding/tags/pdf.styl","hash":"b49c64f8e9a6ca1c45c0ba98febf1974fdd03616","modified":1652757539322},{"_id":"themes/next/source/lib/font-awesome/webfonts/fa-solid-900.woff2","hash":"75a88815c47a249eadb5f0edc1675957f860cca7","modified":1652757539336},{"_id":"themes/next/source/lib/font-awesome/webfonts/fa-brands-400.woff2","hash":"509988477da79c146cb93fb728405f18e923c2de","modified":1652757539335},{"_id":"source/about/me.jpg","hash":"599b7afe77706437dc7c7e0cae47ed1c62cb9a9f","modified":1650721075605},{"_id":"public/atom.xml","hash":"8a424d5c83bc2738c66a840a7157a7d711a6b720","modified":1652758408441},{"_id":"public/search.xml","hash":"145e069ed647f7d3d4395dc471b64bdec0876282","modified":1652758408441},{"_id":"public/categories/index.html","hash":"f1d3017f0c3e0fe752ff4c6c324c5a09ba63c192","modified":1652758408441},{"_id":"public/about/index.html","hash":"71c59063a4b6315be9a86759cc2f75b51e955b5e","modified":1652758408441},{"_id":"public/tags/index.html","hash":"e6858f34972f0ccd6c5d6fb3853e53164cbe7961","modified":1652758408441},{"_id":"public/2020/12/29/BayesianOptimization/index.html","hash":"1a7d773886c353798e5f4a89ca2dc9b51fe7a324","modified":1652758408441},{"_id":"public/2020/12/08/PageRank/index.html","hash":"3cd8bef3b6955b7fe36857129a930f60f676eafb","modified":1652758408441},{"_id":"public/2020/09/07/MicroCause/index.html","hash":"e0bc683ef04df652e3e1a8077233c1e7892d28a3","modified":1652758408441},{"_id":"public/2020/08/28/GraphRCA/index.html","hash":"6705b280b7aa3520031bf9d6fd1f1548d70385f0","modified":1652758408441},{"_id":"public/2020/05/25/IsolationTree/index.html","hash":"2fce61c9c81f1ab9f7e187f4c173e6343cb2bb43","modified":1652758408441},{"_id":"public/2019/09/26/SVM/index.html","hash":"4c56f22fe7efe388308321a9cc31600f93a9e376","modified":1652758408441},{"_id":"public/2019/04/08/DTW/index.html","hash":"4bf861c2b07de75875e7e0f89ad567d431c7c0b1","modified":1652758408441},{"_id":"public/2018/09/25/Dbscan/index.html","hash":"f1375cc6d2fd5e39b0dbbeb6ce27de50bc19b657","modified":1652758408441},{"_id":"public/archives/index.html","hash":"40130aea40ac5aa23470f1d60e19ef60dc419743","modified":1652758408441},{"_id":"public/archives/2018/index.html","hash":"a49b5eca295a5c8b400c1ab94df5305b90b161d8","modified":1652758408441},{"_id":"public/archives/2018/09/index.html","hash":"ed26863770e4ee0357a00d342cf46b7a83970cd5","modified":1652758408441},{"_id":"public/archives/2019/index.html","hash":"fb2fd13d22de2406b7a68cab76ed8ea528ea7609","modified":1652758408441},{"_id":"public/archives/2019/04/index.html","hash":"5d6dc3e73ac9a42920f4557eff508652b64b50fa","modified":1652758408441},{"_id":"public/archives/2019/09/index.html","hash":"3e2db4ab90c11d1cb5d1f0da1552deed2f0a4ff0","modified":1652758408441},{"_id":"public/archives/2020/index.html","hash":"93979415c3953a340f3bd5e00527b88a614e7a02","modified":1652758408441},{"_id":"public/archives/2020/05/index.html","hash":"d657d5d1aa63cf9340ac93df54a261e34d1565ec","modified":1652758408441},{"_id":"public/archives/2020/08/index.html","hash":"c15a74817d4e42514e13764865020a854c21be65","modified":1652758408441},{"_id":"public/archives/2020/09/index.html","hash":"ecbab88a4e102ee4ce1e15774f2e376761799e70","modified":1652758408441},{"_id":"public/archives/2020/12/index.html","hash":"dd28685e13d8a159c6d8feff9dce38daacc9fd4f","modified":1652758408441},{"_id":"public/categories/时间序列分析/index.html","hash":"3556b831c4674bf98a3c94462c487dd08ddd99df","modified":1652758408441},{"_id":"public/categories/机器学习/index.html","hash":"091d22331c8c5e390f26c240769729c4951226fd","modified":1652758408441},{"_id":"public/categories/论文阅读笔记/index.html","hash":"9bfc9138969deccc4416cbe7ae8ef6a705701217","modified":1652758408441},{"_id":"public/categories/异常检测/index.html","hash":"8a8e53b2c8b79f39d001f02d32584c41c8d309d9","modified":1652758408441},{"_id":"public/categories/根因定位/index.html","hash":"56be73b64372366344b6d70a5b5729745825ec13","modified":1652758408441},{"_id":"public/index.html","hash":"57a5b81cac9fdb19240e4fbea5d2b6fae10dc7ed","modified":1652758408441},{"_id":"public/tags/时间序列分析/index.html","hash":"43e46120f98679c5f3bec84563966674842064c8","modified":1652758408441},{"_id":"public/tags/聚类/index.html","hash":"517080ec4b4a0a69d939a92b59ce72320c42dff1","modified":1652758408441},{"_id":"public/tags/根因定位/index.html","hash":"5743f3f20a70ca24205f2105a44a751de56ccf39","modified":1652758408441},{"_id":"public/tags/异常检测/index.html","hash":"a00295639f419a9769f474597d853cf720c92b76","modified":1652758408441},{"_id":"public/tags/分类/index.html","hash":"f3013b231989092e92f380856e51c05419b3b87b","modified":1652758408441},{"_id":"public/tags/AutoML/index.html","hash":"0900453d8e17f2d0b8628e3e3f91df8935f9e7d8","modified":1652758408441},{"_id":"public/images/apple-touch-icon-next.png","hash":"2959dbc97f31c80283e67104fe0854e2369e40aa","modified":1652758408441},{"_id":"public/images/avatar.gif","hash":"18c53e15eb0c84b139995f9334ed8522b40aeaf6","modified":1652758408441},{"_id":"public/images/cc-by-nc-sa.svg","hash":"3031be41e8753c70508aa88e84ed8f4f653f157e","modified":1652758408441},{"_id":"public/images/cc-by-nc-nd.svg","hash":"c6524ece3f8039a5f612feaf865d21ec8a794564","modified":1652758408441},{"_id":"public/images/cc-by-nc.svg","hash":"8d39b39d88f8501c0d27f8df9aae47136ebc59b7","modified":1652758408441},{"_id":"public/images/cc-by-nd.svg","hash":"c563508ce9ced1e66948024ba1153400ac0e0621","modified":1652758408441},{"_id":"public/images/cc-by-sa.svg","hash":"aa4742d733c8af8d38d4c183b8adbdcab045872e","modified":1652758408441},{"_id":"public/images/cc-by.svg","hash":"28a0a4fe355a974a5e42f68031652b76798d4f7e","modified":1652758408441},{"_id":"public/images/favicon-16x16-next.png","hash":"943a0d67a9cdf8c198109b28f9dbd42f761d11c3","modified":1652758408441},{"_id":"public/images/cc-zero.svg","hash":"87669bf8ac268a91d027a0a4802c92a1473e9030","modified":1652758408441},{"_id":"public/images/favicon-32x32-next.png","hash":"0749d7b24b0d2fae1c8eb7f671ad4646ee1894b1","modified":1652758408441},{"_id":"public/images/logo.svg","hash":"d29cacbae1bdc4bbccb542107ee0524fe55ad6de","modified":1652758408441},{"_id":"public/lib/font-awesome/webfonts/fa-regular-400.woff2","hash":"260bb01acd44d88dcb7f501a238ab968f86bef9e","modified":1652758408441},{"_id":"public/CNAME","hash":"215b8f9388cfd3e9643bd739a98e9821d0512101","modified":1652758408441},{"_id":"public/images/luffy.jpeg","hash":"662b7f753206d9c3d48698ee590da883e988d045","modified":1652758408441},{"_id":"public/images/algolia_logo.svg","hash":"ec119560b382b2624e00144ae01c137186e91621","modified":1652758408441},{"_id":"public/lib/font-awesome/webfonts/fa-brands-400.woff2","hash":"509988477da79c146cb93fb728405f18e923c2de","modified":1652758408441},{"_id":"public/lib/font-awesome/webfonts/fa-solid-900.woff2","hash":"75a88815c47a249eadb5f0edc1675957f860cca7","modified":1652758408441},{"_id":"public/css/main.css","hash":"b8b1151fcc35a4bb220cf44eb64a250a1eaccf55","modified":1652758408441},{"_id":"public/js/algolia-search.js","hash":"498d233eb5c7af6940baf94c1a1c36fdf1dd2636","modified":1652758408441},{"_id":"public/js/local-search.js","hash":"35ccf100d8f9c0fd6bfbb7fa88c2a76c42a69110","modified":1652758408441},{"_id":"public/js/bookmark.js","hash":"9734ebcb9b83489686f5c2da67dc9e6157e988ad","modified":1652758408441},{"_id":"public/js/motion.js","hash":"72df86f6dfa29cce22abeff9d814c9dddfcf13a9","modified":1652758408441},{"_id":"public/js/next-boot.js","hash":"a1b0636423009d4a4e4cea97bcbf1842bfab582c","modified":1652758408441},{"_id":"public/js/utils.js","hash":"730cca7f164eaf258661a61ff3f769851ff1e5da","modified":1652758408441},{"_id":"public/js/schemes/muse.js","hash":"1eb9b88103ddcf8827b1a7cbc56471a9c5592d53","modified":1652758408441},{"_id":"public/js/schemes/pisces.js","hash":"0ac5ce155bc58c972fe21c4c447f85e6f8755c62","modified":1652758408441},{"_id":"public/lib/velocity/velocity.ui.min.js","hash":"ed5e534cd680a25d8d14429af824f38a2c7d9908","modified":1652758408441},{"_id":"public/lib/anime.min.js","hash":"47cb482a8a488620a793d50ba8f6752324b46af3","modified":1652758408441},{"_id":"public/lib/velocity/velocity.min.js","hash":"2f1afadc12e4cf59ef3b405308d21baa97e739c6","modified":1652758408441},{"_id":"public/lib/font-awesome/css/all.min.css","hash":"0038dc97c79451578b7bd48af60ba62282b4082b","modified":1652758408441},{"_id":"public/images/tree.png","hash":"562e0918a8ed0f3f2138ef904ac0da888e85b233","modified":1652758408441},{"_id":"public/about/me.jpg","hash":"599b7afe77706437dc7c7e0cae47ed1c62cb9a9f","modified":1652758408441}],"Category":[{"name":"时间序列分析","_id":"cl39lom8n0004otudhtlzhtq6"},{"name":"机器学习","_id":"cl39lom8r000aotudaedb4kmu"},{"name":"论文阅读笔记","_id":"cl39lom8t000fotud9w95652t"},{"name":"异常检测","_id":"cl39lom8u000jotud62rchkn2"},{"name":"根因定位","_id":"cl39lom8w000qotudcbeh58vo"}],"Data":[],"Page":[{"title":"分类","date":"2017-10-25T14:00:00.000Z","layout":"categories","type":"categories","_content":"","source":"categories/index.md","raw":"---\ntitle: 分类\ndate: 2017-10-25 22:00:00\nlayout: \"categories\"\ntype: \"categories\"\n---","updated":"2022-04-26T10:08:41.443Z","path":"categories/index.html","comments":1,"_id":"cl39lom8h0000otudhkk12uzv","content":"\n","site":{"data":{}},"excerpt":"","more":"\n"},{"title":"about","date":"2017-02-23T02:00:00.000Z","layout":"about","_content":"#### 教育经历\n硕士:关山口男子技校\n\n本科:左家垅男子技校\n\n#### 工作领域\n`时序异常检测` `故障根因定位` `关联分析` `时序预测`\n\n#### 技能\n`Python` `Java` `机器学习`\n\n\n### 兴趣爱好\n`户外` `动漫` `推理爱好者`\n\n### 联系方式\nWeChat:AckermanAni\n\nEmail:[email protected]\n\nGithub:[wjd92](https://github.com/wjd92)\n\n### 工作相关\n\n- [AIOps在美团的实践--故障发现篇(美团2020年度最受欢迎技术博客)](https://tech.meituan.com/2020/10/15/mt-aiops-horae.html)","source":"about/index.md","raw":"---\ntitle: about\ndate: 2017-02-23 10:00:00\nlayout: about\n---\n#### 教育经历\n硕士:关山口男子技校\n\n本科:左家垅男子技校\n\n#### 工作领域\n`时序异常检测` `故障根因定位` `关联分析` `时序预测`\n\n#### 技能\n`Python` `Java` `机器学习`\n\n\n### 兴趣爱好\n`户外` `动漫` `推理爱好者`\n\n### 联系方式\nWeChat:AckermanAni\n\nEmail:[email protected]\n\nGithub:[wjd92](https://github.com/wjd92)\n\n### 工作相关\n\n- [AIOps在美团的实践--故障发现篇(美团2020年度最受欢迎技术博客)](https://tech.meituan.com/2020/10/15/mt-aiops-horae.html)","updated":"2022-05-07T17:14:29.854Z","path":"about/index.html","comments":1,"_id":"cl39lom8l0002otudbzevax8s","content":"<h4 id=\"教育经历\">教育经历</h4>\n<p>硕士:关山口男子技校</p>\n<p>本科:左家垅男子技校</p>\n<h4 id=\"工作领域\">工作领域</h4>\n<p><code>时序异常检测</code> <code>故障根因定位</code>\n<code>关联分析</code> <code>时序预测</code></p>\n<h4 id=\"技能\">技能</h4>\n<p><code>Python</code> <code>Java</code> <code>机器学习</code></p>\n<h3 id=\"兴趣爱好\">兴趣爱好</h3>\n<p><code>户外</code> <code>动漫</code> <code>推理爱好者</code></p>\n<h3 id=\"联系方式\">联系方式</h3>\n<p>WeChat:AckermanAni</p>\n<p>Email:[email protected]</p>\n<p>Github:<a href=\"https://github.com/wjd92\">wjd92</a></p>\n<h3 id=\"工作相关\">工作相关</h3>\n<ul>\n<li><a\nhref=\"https://tech.meituan.com/2020/10/15/mt-aiops-horae.html\">AIOps在美团的实践--故障发现篇(美团2020年度最受欢迎技术博客)</a></li>\n</ul>\n","site":{"data":{}},"excerpt":"","more":"<h4 id=\"教育经历\">教育经历</h4>\n<p>硕士:关山口男子技校</p>\n<p>本科:左家垅男子技校</p>\n<h4 id=\"工作领域\">工作领域</h4>\n<p><code>时序异常检测</code> <code>故障根因定位</code>\n<code>关联分析</code> <code>时序预测</code></p>\n<h4 id=\"技能\">技能</h4>\n<p><code>Python</code> <code>Java</code> <code>机器学习</code></p>\n<h3 id=\"兴趣爱好\">兴趣爱好</h3>\n<p><code>户外</code> <code>动漫</code> <code>推理爱好者</code></p>\n<h3 id=\"联系方式\">联系方式</h3>\n<p>WeChat:AckermanAni</p>\n<p>Email:[email protected]</p>\n<p>Github:<a href=\"https://github.com/wjd92\">wjd92</a></p>\n<h3 id=\"工作相关\">工作相关</h3>\n<ul>\n<li><a\nhref=\"https://tech.meituan.com/2020/10/15/mt-aiops-horae.html\">AIOps在美团的实践--故障发现篇(美团2020年度最受欢迎技术博客)</a></li>\n</ul>\n"},{"title":"标签","date":"2017-10-25T14:00:00.000Z","layout":"tags","type":"tags","_content":"","source":"tags/index.md","raw":"---\ntitle: 标签\ndate: 2017-10-25 22:00:00\nlayout: \"tags\"\ntype: \"tags\"\n---","updated":"2022-04-26T10:09:38.921Z","path":"tags/index.html","comments":1,"_id":"cl39lom8p0006otudb7zgeasq","content":"\n","site":{"data":{}},"excerpt":"","more":"\n"}],"Post":[{"title":"动态归整距离(DTW)详解","date":"2019-04-08T11:51:00.000Z","mathjax":true,"_content":"\nDTW(Dynamic Time Warping)动态归整距离是一种衡量两个时间序列相似度的方法\n\n<!--more-->\n\n## DTW原理\n\n在时间序列分析中,需要比较相似性的两段时间序列的长度可能并不相等,在语音识别领域表现为不同人的语速不同。而且同一个单词内的不同音素的发音速度也不同,比如有的人会把‘A’这个音拖得很长,或者把‘i’发的很短。另外,不同时间序列可能仅仅存在时间轴上的位移,亦即在还原位移的情况下,两个时间序列是一致的。在这些复杂情况下,使用传统的欧几里得距离无法有效地求的两个时间序列之间的距离(或者相似性)。\n\n__DTW通过把时间序列进行延伸和缩短,来计算两个时间序列性之间的相似性__\n\n<img src=\"https://images.bumpchicken.cn/img/20220509012950.png\" width=\"50%\" height=\"50%\">\n\n如上图所示,上下两条实线代表两个时间序列,它们之间的虚线代表两个时间序列之间相似的点。DTW使用所有这些相似点之间距离的和,称之为归整路径距离(Warp Path Distance)\n\n## DTW计算方法\n\n令要计算相似度的两个时间序列分别为$X$和$Y$,长度分别为$|X|$和$|Y|$\n\n### 归整路径(Warp Path)\n\n归整路径的形式为$W=w_{1},w_{2},...,w_{k}$,其中$Max(|x|,|Y|)<= K <= |X| + |Y|$\n$w_{k}$的形式为$(i,j)$,其中$i$表示的是$X$中的$i$坐标,$j$表示的是$Y$中的$j$坐标。\n归整路径$W$必须从$w_{1}=(1,1)$ 开始,到$w_{k}=(|X|,|Y|)$结尾,以保证$X$和$Y$中的每个坐标都在$W$中出现。\n另外,$w_{k}$中$(i,j)$必须是单调增加的,以保证上图中的虚线不会相交,所谓单调增加是指:\n\n$$w_{k}=(i,j), w_{k+1}=(i^{'},j^{'}) \\qquad i<=i^{'}<=i+1, j<=j^{'}<=j+1$$\n\n最后得到的归整路径是距离最短的一个路径:\n\n$$Dist(W)=\\sum^{k=K}_{k=1}Dist(w_{ki}, w_{kj})$$\n\n其中$Dist(w_{ki}, w_{kj}$为任意经典的距离计算方法,比如欧氏距离。$w_{ki}$是指$X$的第i个数据点,$w_{kj}$是指$Y$的第$j$个数据点\n\n### DTW实现\n\n在实现DTW时,我们采用动态规划的思想,令$D(i,j)$表示长度为$i$和$j$的两个时间序列之间的归整路径距离:\n\n$$D(i,j)=Dist(i,j)+min[D(i-1,j),D(i,j-1),D(i-1,j-1)]$$\n\n<img src=\"https://images.bumpchicken.cn/img/20220509015951.png\" width=\"80%\" height=\"30%\">\n\n代码如下:\n```python\nimport sys\n\ndef distance(x,y):\n \"\"\"定义你的距离函数,欧式距离,街区距离等等\"\"\"\n return abs(x-y)\n \ndef dtw(X,Y):\n M=[[distance(X[i],Y[j]) for i in range(len(X))] for j in range(len(Y))]\n l1=len(X)\n l2=len(Y) \n D=[[0 for i in range(l1+1)] for i in range(l2+1)]\n D[0][0]=0 \n for i in range(1,l1+1):\n D[i][0]=sys.maxint\n for j in range(1,l2+1):\n D[0][j]=sys.maxint\n for i in range(1,l1+1):\n for j in range(1,l2+1):\n D[i][j]=M[i-1][j-1]+min(D[i-1][j],D[i][j-1],D[i-1][j-1])\n return D[l1][l2]\n```\n\n__DTW采用动态规划实现,时间复杂度为$O(N^{2})$,有一些改进的快速DTW算法,如FastDTW[1], SparseDTW, LB_Keogh, LB_Imporved等__\n\n## 参考资料\n\n1. FastDTW: Toward Accurate Dynamic Time Warping in Linear Time and Space. Stan Salvador, Philip Chan.","source":"_posts/DTW.md","raw":"\ntitle: 动态归整距离(DTW)详解\n\ndate: 2019-04-08 19:51:00\n\ntags: \n - 时间序列分析\n\ncategories:\n - 时间序列分析\n\nmathjax: true\n\n---\n\nDTW(Dynamic Time Warping)动态归整距离是一种衡量两个时间序列相似度的方法\n\n<!--more-->\n\n## DTW原理\n\n在时间序列分析中,需要比较相似性的两段时间序列的长度可能并不相等,在语音识别领域表现为不同人的语速不同。而且同一个单词内的不同音素的发音速度也不同,比如有的人会把‘A’这个音拖得很长,或者把‘i’发的很短。另外,不同时间序列可能仅仅存在时间轴上的位移,亦即在还原位移的情况下,两个时间序列是一致的。在这些复杂情况下,使用传统的欧几里得距离无法有效地求的两个时间序列之间的距离(或者相似性)。\n\n__DTW通过把时间序列进行延伸和缩短,来计算两个时间序列性之间的相似性__\n\n<img src=\"https://images.bumpchicken.cn/img/20220509012950.png\" width=\"50%\" height=\"50%\">\n\n如上图所示,上下两条实线代表两个时间序列,它们之间的虚线代表两个时间序列之间相似的点。DTW使用所有这些相似点之间距离的和,称之为归整路径距离(Warp Path Distance)\n\n## DTW计算方法\n\n令要计算相似度的两个时间序列分别为$X$和$Y$,长度分别为$|X|$和$|Y|$\n\n### 归整路径(Warp Path)\n\n归整路径的形式为$W=w_{1},w_{2},...,w_{k}$,其中$Max(|x|,|Y|)<= K <= |X| + |Y|$\n$w_{k}$的形式为$(i,j)$,其中$i$表示的是$X$中的$i$坐标,$j$表示的是$Y$中的$j$坐标。\n归整路径$W$必须从$w_{1}=(1,1)$ 开始,到$w_{k}=(|X|,|Y|)$结尾,以保证$X$和$Y$中的每个坐标都在$W$中出现。\n另外,$w_{k}$中$(i,j)$必须是单调增加的,以保证上图中的虚线不会相交,所谓单调增加是指:\n\n$$w_{k}=(i,j), w_{k+1}=(i^{'},j^{'}) \\qquad i<=i^{'}<=i+1, j<=j^{'}<=j+1$$\n\n最后得到的归整路径是距离最短的一个路径:\n\n$$Dist(W)=\\sum^{k=K}_{k=1}Dist(w_{ki}, w_{kj})$$\n\n其中$Dist(w_{ki}, w_{kj}$为任意经典的距离计算方法,比如欧氏距离。$w_{ki}$是指$X$的第i个数据点,$w_{kj}$是指$Y$的第$j$个数据点\n\n### DTW实现\n\n在实现DTW时,我们采用动态规划的思想,令$D(i,j)$表示长度为$i$和$j$的两个时间序列之间的归整路径距离:\n\n$$D(i,j)=Dist(i,j)+min[D(i-1,j),D(i,j-1),D(i-1,j-1)]$$\n\n<img src=\"https://images.bumpchicken.cn/img/20220509015951.png\" width=\"80%\" height=\"30%\">\n\n代码如下:\n```python\nimport sys\n\ndef distance(x,y):\n \"\"\"定义你的距离函数,欧式距离,街区距离等等\"\"\"\n return abs(x-y)\n \ndef dtw(X,Y):\n M=[[distance(X[i],Y[j]) for i in range(len(X))] for j in range(len(Y))]\n l1=len(X)\n l2=len(Y) \n D=[[0 for i in range(l1+1)] for i in range(l2+1)]\n D[0][0]=0 \n for i in range(1,l1+1):\n D[i][0]=sys.maxint\n for j in range(1,l2+1):\n D[0][j]=sys.maxint\n for i in range(1,l1+1):\n for j in range(1,l2+1):\n D[i][j]=M[i-1][j-1]+min(D[i-1][j],D[i][j-1],D[i-1][j-1])\n return D[l1][l2]\n```\n\n__DTW采用动态规划实现,时间复杂度为$O(N^{2})$,有一些改进的快速DTW算法,如FastDTW[1], SparseDTW, LB_Keogh, LB_Imporved等__\n\n## 参考资料\n\n1. FastDTW: Toward Accurate Dynamic Time Warping in Linear Time and Space. Stan Salvador, Philip Chan.","slug":"DTW","published":1,"updated":"2022-05-09T06:16:42.630Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl39lom8j0001otud2qaac68v","content":"<p>DTW(Dynamic Time\nWarping)动态归整距离是一种衡量两个时间序列相似度的方法</p>\n<span id=\"more\"></span>\n<h2 id=\"dtw原理\">DTW原理</h2>\n<p>在时间序列分析中,需要比较相似性的两段时间序列的长度可能并不相等,在语音识别领域表现为不同人的语速不同。而且同一个单词内的不同音素的发音速度也不同,比如有的人会把‘A’这个音拖得很长,或者把‘i’发的很短。另外,不同时间序列可能仅仅存在时间轴上的位移,亦即在还原位移的情况下,两个时间序列是一致的。在这些复杂情况下,使用传统的欧几里得距离无法有效地求的两个时间序列之间的距离(或者相似性)。</p>\n<p><strong>DTW通过把时间序列进行延伸和缩短,来计算两个时间序列性之间的相似性</strong></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220509012950.png\" width=\"50%\" height=\"50%\"></p>\n<p>如上图所示,上下两条实线代表两个时间序列,它们之间的虚线代表两个时间序列之间相似的点。DTW使用所有这些相似点之间距离的和,称之为归整路径距离(Warp\nPath Distance)</p>\n<h2 id=\"dtw计算方法\">DTW计算方法</h2>\n<p>令要计算相似度的两个时间序列分别为<span\nclass=\"math inline\">\\(X\\)</span>和<span\nclass=\"math inline\">\\(Y\\)</span>,长度分别为<span\nclass=\"math inline\">\\(|X|\\)</span>和<span\nclass=\"math inline\">\\(|Y|\\)</span></p>\n<h3 id=\"归整路径warp-path\">归整路径(Warp Path)</h3>\n<p>归整路径的形式为<span\nclass=\"math inline\">\\(W=w_{1},w_{2},...,w_{k}\\)</span>,其中<span\nclass=\"math inline\">\\(Max(|x|,|Y|)<= K <= |X| + |Y|\\)</span> <span\nclass=\"math inline\">\\(w_{k}\\)</span>的形式为<span\nclass=\"math inline\">\\((i,j)\\)</span>,其中<span\nclass=\"math inline\">\\(i\\)</span>表示的是<span\nclass=\"math inline\">\\(X\\)</span>中的<span\nclass=\"math inline\">\\(i\\)</span>坐标,<span\nclass=\"math inline\">\\(j\\)</span>表示的是<span\nclass=\"math inline\">\\(Y\\)</span>中的<span\nclass=\"math inline\">\\(j\\)</span>坐标。 归整路径<span\nclass=\"math inline\">\\(W\\)</span>必须从<span\nclass=\"math inline\">\\(w_{1}=(1,1)\\)</span> 开始,到<span\nclass=\"math inline\">\\(w_{k}=(|X|,|Y|)\\)</span>结尾,以保证<span\nclass=\"math inline\">\\(X\\)</span>和<span\nclass=\"math inline\">\\(Y\\)</span>中的每个坐标都在<span\nclass=\"math inline\">\\(W\\)</span>中出现。 另外,<span\nclass=\"math inline\">\\(w_{k}\\)</span>中<span\nclass=\"math inline\">\\((i,j)\\)</span>必须是单调增加的,以保证上图中的虚线不会相交,所谓单调增加是指:</p>\n<p><span class=\"math display\">\\[w_{k}=(i,j),\nw_{k+1}=(i^{'},j^{'}) \\qquad i<=i^{'}<=i+1,\nj<=j^{'}<=j+1\\]</span></p>\n<p>最后得到的归整路径是距离最短的一个路径:</p>\n<p><span class=\"math display\">\\[Dist(W)=\\sum^{k=K}_{k=1}Dist(w_{ki},\nw_{kj})\\]</span></p>\n<p>其中<span class=\"math inline\">\\(Dist(w_{ki},\nw_{kj}\\)</span>为任意经典的距离计算方法,比如欧氏距离。<span\nclass=\"math inline\">\\(w_{ki}\\)</span>是指<span\nclass=\"math inline\">\\(X\\)</span>的第i个数据点,<span\nclass=\"math inline\">\\(w_{kj}\\)</span>是指<span\nclass=\"math inline\">\\(Y\\)</span>的第<span\nclass=\"math inline\">\\(j\\)</span>个数据点</p>\n<h3 id=\"dtw实现\">DTW实现</h3>\n<p>在实现DTW时,我们采用动态规划的思想,令<span\nclass=\"math inline\">\\(D(i,j)\\)</span>表示长度为<span\nclass=\"math inline\">\\(i\\)</span>和<span\nclass=\"math inline\">\\(j\\)</span>的两个时间序列之间的归整路径距离:</p>\n<p><span\nclass=\"math display\">\\[D(i,j)=Dist(i,j)+min[D(i-1,j),D(i,j-1),D(i-1,j-1)]\\]</span></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220509015951.png\" width=\"80%\" height=\"30%\"></p>\n<p>代码如下: <figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> sys</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">distance</span>(<span class=\"params\">x,y</span>):</span><br><span class=\"line\"> <span class=\"string\">"""定义你的距离函数,欧式距离,街区距离等等"""</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"built_in\">abs</span>(x-y)</span><br><span class=\"line\"> </span><br><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">dtw</span>(<span class=\"params\">X,Y</span>):</span><br><span class=\"line\"> M=[[distance(X[i],Y[j]) <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"built_in\">len</span>(X))] <span class=\"keyword\">for</span> j <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"built_in\">len</span>(Y))]</span><br><span class=\"line\"> l1=<span class=\"built_in\">len</span>(X)</span><br><span class=\"line\"> l2=<span class=\"built_in\">len</span>(Y) </span><br><span class=\"line\"> D=[[<span class=\"number\">0</span> <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(l1+<span class=\"number\">1</span>)] <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(l2+<span class=\"number\">1</span>)]</span><br><span class=\"line\"> D[<span class=\"number\">0</span>][<span class=\"number\">0</span>]=<span class=\"number\">0</span> </span><br><span class=\"line\"> <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">1</span>,l1+<span class=\"number\">1</span>):</span><br><span class=\"line\"> D[i][<span class=\"number\">0</span>]=sys.maxint</span><br><span class=\"line\"> <span class=\"keyword\">for</span> j <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">1</span>,l2+<span class=\"number\">1</span>):</span><br><span class=\"line\"> D[<span class=\"number\">0</span>][j]=sys.maxint</span><br><span class=\"line\"> <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">1</span>,l1+<span class=\"number\">1</span>):</span><br><span class=\"line\"> <span class=\"keyword\">for</span> j <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">1</span>,l2+<span class=\"number\">1</span>):</span><br><span class=\"line\"> D[i][j]=M[i-<span class=\"number\">1</span>][j-<span class=\"number\">1</span>]+<span class=\"built_in\">min</span>(D[i-<span class=\"number\">1</span>][j],D[i][j-<span class=\"number\">1</span>],D[i-<span class=\"number\">1</span>][j-<span class=\"number\">1</span>])</span><br><span class=\"line\"> <span class=\"keyword\">return</span> D[l1][l2]</span><br></pre></td></tr></table></figure></p>\n<p><strong>DTW采用动态规划实现,时间复杂度为<span\nclass=\"math inline\">\\(O(N^{2})\\)</span>,有一些改进的快速DTW算法,如FastDTW[1],\nSparseDTW, LB_Keogh, LB_Imporved等</strong></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<ol type=\"1\">\n<li>FastDTW: Toward Accurate Dynamic Time Warping in Linear Time and\nSpace. Stan Salvador, Philip Chan.</li>\n</ol>\n","site":{"data":{}},"excerpt":"<p>DTW(Dynamic Time\nWarping)动态归整距离是一种衡量两个时间序列相似度的方法</p>","more":"<h2 id=\"dtw原理\">DTW原理</h2>\n<p>在时间序列分析中,需要比较相似性的两段时间序列的长度可能并不相等,在语音识别领域表现为不同人的语速不同。而且同一个单词内的不同音素的发音速度也不同,比如有的人会把‘A’这个音拖得很长,或者把‘i’发的很短。另外,不同时间序列可能仅仅存在时间轴上的位移,亦即在还原位移的情况下,两个时间序列是一致的。在这些复杂情况下,使用传统的欧几里得距离无法有效地求的两个时间序列之间的距离(或者相似性)。</p>\n<p><strong>DTW通过把时间序列进行延伸和缩短,来计算两个时间序列性之间的相似性</strong></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220509012950.png\" width=\"50%\" height=\"50%\"></p>\n<p>如上图所示,上下两条实线代表两个时间序列,它们之间的虚线代表两个时间序列之间相似的点。DTW使用所有这些相似点之间距离的和,称之为归整路径距离(Warp\nPath Distance)</p>\n<h2 id=\"dtw计算方法\">DTW计算方法</h2>\n<p>令要计算相似度的两个时间序列分别为<span\nclass=\"math inline\">\\(X\\)</span>和<span\nclass=\"math inline\">\\(Y\\)</span>,长度分别为<span\nclass=\"math inline\">\\(|X|\\)</span>和<span\nclass=\"math inline\">\\(|Y|\\)</span></p>\n<h3 id=\"归整路径warp-path\">归整路径(Warp Path)</h3>\n<p>归整路径的形式为<span\nclass=\"math inline\">\\(W=w_{1},w_{2},...,w_{k}\\)</span>,其中<span\nclass=\"math inline\">\\(Max(|x|,|Y|)<= K <= |X| + |Y|\\)</span> <span\nclass=\"math inline\">\\(w_{k}\\)</span>的形式为<span\nclass=\"math inline\">\\((i,j)\\)</span>,其中<span\nclass=\"math inline\">\\(i\\)</span>表示的是<span\nclass=\"math inline\">\\(X\\)</span>中的<span\nclass=\"math inline\">\\(i\\)</span>坐标,<span\nclass=\"math inline\">\\(j\\)</span>表示的是<span\nclass=\"math inline\">\\(Y\\)</span>中的<span\nclass=\"math inline\">\\(j\\)</span>坐标。 归整路径<span\nclass=\"math inline\">\\(W\\)</span>必须从<span\nclass=\"math inline\">\\(w_{1}=(1,1)\\)</span> 开始,到<span\nclass=\"math inline\">\\(w_{k}=(|X|,|Y|)\\)</span>结尾,以保证<span\nclass=\"math inline\">\\(X\\)</span>和<span\nclass=\"math inline\">\\(Y\\)</span>中的每个坐标都在<span\nclass=\"math inline\">\\(W\\)</span>中出现。 另外,<span\nclass=\"math inline\">\\(w_{k}\\)</span>中<span\nclass=\"math inline\">\\((i,j)\\)</span>必须是单调增加的,以保证上图中的虚线不会相交,所谓单调增加是指:</p>\n<p><span class=\"math display\">\\[w_{k}=(i,j),\nw_{k+1}=(i^{'},j^{'}) \\qquad i<=i^{'}<=i+1,\nj<=j^{'}<=j+1\\]</span></p>\n<p>最后得到的归整路径是距离最短的一个路径:</p>\n<p><span class=\"math display\">\\[Dist(W)=\\sum^{k=K}_{k=1}Dist(w_{ki},\nw_{kj})\\]</span></p>\n<p>其中<span class=\"math inline\">\\(Dist(w_{ki},\nw_{kj}\\)</span>为任意经典的距离计算方法,比如欧氏距离。<span\nclass=\"math inline\">\\(w_{ki}\\)</span>是指<span\nclass=\"math inline\">\\(X\\)</span>的第i个数据点,<span\nclass=\"math inline\">\\(w_{kj}\\)</span>是指<span\nclass=\"math inline\">\\(Y\\)</span>的第<span\nclass=\"math inline\">\\(j\\)</span>个数据点</p>\n<h3 id=\"dtw实现\">DTW实现</h3>\n<p>在实现DTW时,我们采用动态规划的思想,令<span\nclass=\"math inline\">\\(D(i,j)\\)</span>表示长度为<span\nclass=\"math inline\">\\(i\\)</span>和<span\nclass=\"math inline\">\\(j\\)</span>的两个时间序列之间的归整路径距离:</p>\n<p><span\nclass=\"math display\">\\[D(i,j)=Dist(i,j)+min[D(i-1,j),D(i,j-1),D(i-1,j-1)]\\]</span></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220509015951.png\" width=\"80%\" height=\"30%\"></p>\n<p>代码如下: <figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> sys</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">distance</span>(<span class=\"params\">x,y</span>):</span><br><span class=\"line\"> <span class=\"string\">"""定义你的距离函数,欧式距离,街区距离等等"""</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> <span class=\"built_in\">abs</span>(x-y)</span><br><span class=\"line\"> </span><br><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">dtw</span>(<span class=\"params\">X,Y</span>):</span><br><span class=\"line\"> M=[[distance(X[i],Y[j]) <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"built_in\">len</span>(X))] <span class=\"keyword\">for</span> j <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"built_in\">len</span>(Y))]</span><br><span class=\"line\"> l1=<span class=\"built_in\">len</span>(X)</span><br><span class=\"line\"> l2=<span class=\"built_in\">len</span>(Y) </span><br><span class=\"line\"> D=[[<span class=\"number\">0</span> <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(l1+<span class=\"number\">1</span>)] <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(l2+<span class=\"number\">1</span>)]</span><br><span class=\"line\"> D[<span class=\"number\">0</span>][<span class=\"number\">0</span>]=<span class=\"number\">0</span> </span><br><span class=\"line\"> <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">1</span>,l1+<span class=\"number\">1</span>):</span><br><span class=\"line\"> D[i][<span class=\"number\">0</span>]=sys.maxint</span><br><span class=\"line\"> <span class=\"keyword\">for</span> j <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">1</span>,l2+<span class=\"number\">1</span>):</span><br><span class=\"line\"> D[<span class=\"number\">0</span>][j]=sys.maxint</span><br><span class=\"line\"> <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">1</span>,l1+<span class=\"number\">1</span>):</span><br><span class=\"line\"> <span class=\"keyword\">for</span> j <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">1</span>,l2+<span class=\"number\">1</span>):</span><br><span class=\"line\"> D[i][j]=M[i-<span class=\"number\">1</span>][j-<span class=\"number\">1</span>]+<span class=\"built_in\">min</span>(D[i-<span class=\"number\">1</span>][j],D[i][j-<span class=\"number\">1</span>],D[i-<span class=\"number\">1</span>][j-<span class=\"number\">1</span>])</span><br><span class=\"line\"> <span class=\"keyword\">return</span> D[l1][l2]</span><br></pre></td></tr></table></figure></p>\n<p><strong>DTW采用动态规划实现,时间复杂度为<span\nclass=\"math inline\">\\(O(N^{2})\\)</span>,有一些改进的快速DTW算法,如FastDTW[1],\nSparseDTW, LB_Keogh, LB_Imporved等</strong></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<ol type=\"1\">\n<li>FastDTW: Toward Accurate Dynamic Time Warping in Linear Time and\nSpace. Stan Salvador, Philip Chan.</li>\n</ol>"},{"title":"DBSCAN算法原理","date":"2018-09-25T08:00:00.000Z","mathjax":true,"top":2,"_content":"<img src=\"https://images.bumpchicken.cn/img/20220508173927.png\" width=\"60%\" height=\"20%\">\n\nDBSCAN(Density-Based Spatial Clustering of Application with Noise)是一种基于密度的空间聚类算法。该算法将具有足够密度的区域划分为簇,并在具有噪声的空间数据中发现任意形状的簇,它将簇定义为密度相连的点的最大集合。算法无需事先指定聚类中心数目,可以对大规模无规则形状的数据进行有效聚类\n\n<!--more-->\n\n## 相关定义\nDBSCAN有自己的一套符号体系,定义了许多新概念,数学体系十分严谨\n\n### 密度定义\n\n给定数据集$D$\n\n- $\\epsilon$: 邻域半径\n- $\\epsilon$-邻域: 邻域内点的集合\n\n$$N_{\\varepsilon}(p):=\\{\\text{q in dataset D} \\mid \\operatorname{dist}(p,q)<=\\varepsilon\\}$$\n\n【注】_距离度量$dist(p,q)$是聚类算法中一个值得探究的问题。此处的距离度量可以为欧氏距离、曼哈顿距离等多种距离度量方式,并且数据点的维度可为任意维度_\n\n- MinPts: 核心点邻域内数据点的最小数量\n\n<img src=\"https://images.bumpchicken.cn/img/20220508215638.png\" width=\"50%\" height=\"80%\">\n\n如上图,当MinPts = 4, p的密度相较于q大,p称为高密度点\n\n### 核心点、边界点和离群点定义\n\n<img src=\"https://images.bumpchicken.cn/img/20220508220041.png\" width=\"60%\" height=\"60%\">\n\n- 核心点(Core): 高密度点,其 $\\epsilon$-邻域数据点数量 >= MinPts\n- 边界点(Border): 低密度点,但在某个核心点的邻域内\n- 离群点(Outlier): 既不是核心点也不是边界点\n\n### 密度可达定义\n\n- 直接密度可达: 如果p是一个核心点,切q在p的$\\epsilon$-邻域内,那么称q直接密度可达p\n\n【注】_不能说p直接密度可达q,直接密度可达不具有对称性(symmetric)_\n\n<img src=\"https://images.bumpchicken.cn/img/20220508221408.png\" width=\"40%\" height=\"40%\">\n\n\n- 密度可达:如果存在一串这样的数据点: $p_{1},p_{2},...,p_{n}$,其中$p_{1}=q,p_{n}=p$,且$p_{i+1}$直接密度可达$p_{i}$,那么称p密度可达q\n\n【注】_不能说q密度可达p,密度可达同样不具有对称性_\n\n<img src=\"https://images.bumpchicken.cn/img/20220508222514.png\" width=\"50%\" height=\"50%\">\n\n### 密度连通\n\n如果p和q都密度可达点o,那么称p和q密度连通,如下所示\n\n<img src=\"https://images.bumpchicken.cn/img/20220508224900.png\" width=\"50%\" height=\"50%\">\n\n【注】_密度连通具有对称性,可以说q和p密度连通_\n\n## 聚类准则\n给定一个数据集D,参数$\\epsilon$和MinPts,那么聚类产生的子集C必须满足两个准则:\n\n1. Maximality(极大性):对于任意的p、q,如果$p\\in C$,且q密度可达p,那么同样$q\\in C$\n2. Connectivity(连通性):对于任意的p、q,p和q是密度相连的\n\n## 聚类流程\n\nDBSCAN聚类过程如下图所示\n\n<img src=\"https://images.bumpchicken.cn/img/20220508225615.png\" width=\"80%\" height=\"80%\">\n\n\n## 参数选择\n\n### 邻域大小$\\epsilon$\n\nDBSCAN采用全局$\\epsilon$和MinPts值,因此每个节点的邻域大小是一致的。当数据密度和聚簇间距离分布不均匀时,若选取较小的$\\epsilon$,则较稀疏的聚簇中的数据点密度会小于MintPts,会被认为是边界点而不被用于所在类的进一步扩展,可能导致较稀疏的聚簇被划分为多个性质相似的小聚簇。相反,若选取较大的$\\epsilon$,则离得较近而密度较大的那些聚簇可能被合并为同一个聚簇,他们之间的差异将被忽略。因此这种情况下,选取合适的邻域大小是较为困难的,当维度较高时,$\\epsilon$的选取更加困难\n\n### MinPts\n\n参数MinPts的选取有一个指导性原则,即 $MinPts >= dim+1$,这里$dim$表示聚类空间的维度大小\n\n## 优缺点\n\n### 优点\n1. 可以对任意形状的稠密数据集进行聚类(K-Means一般只适用于凸数据集)\n2. 可以在聚类时发现异常点,对数据集的异常点不敏感\n\n### 缺点\n1. 如果样本集的密度不均匀,聚类间距相距很大时,聚类效果较差\n2. 对于参数 $\\epsilon$ 和MinPts敏感,不同参数组合对聚类效果影响较大","source":"_posts/Dbscan.md","raw":"\ntitle: DBSCAN算法原理\n\ndate: 2018-09-25 16:00:00\n\ntags: \n - 聚类\n\ncategories:\n - 机器学习\n\nmathjax: true\n\ntop: 2\n\n---\n<img src=\"https://images.bumpchicken.cn/img/20220508173927.png\" width=\"60%\" height=\"20%\">\n\nDBSCAN(Density-Based Spatial Clustering of Application with Noise)是一种基于密度的空间聚类算法。该算法将具有足够密度的区域划分为簇,并在具有噪声的空间数据中发现任意形状的簇,它将簇定义为密度相连的点的最大集合。算法无需事先指定聚类中心数目,可以对大规模无规则形状的数据进行有效聚类\n\n<!--more-->\n\n## 相关定义\nDBSCAN有自己的一套符号体系,定义了许多新概念,数学体系十分严谨\n\n### 密度定义\n\n给定数据集$D$\n\n- $\\epsilon$: 邻域半径\n- $\\epsilon$-邻域: 邻域内点的集合\n\n$$N_{\\varepsilon}(p):=\\{\\text{q in dataset D} \\mid \\operatorname{dist}(p,q)<=\\varepsilon\\}$$\n\n【注】_距离度量$dist(p,q)$是聚类算法中一个值得探究的问题。此处的距离度量可以为欧氏距离、曼哈顿距离等多种距离度量方式,并且数据点的维度可为任意维度_\n\n- MinPts: 核心点邻域内数据点的最小数量\n\n<img src=\"https://images.bumpchicken.cn/img/20220508215638.png\" width=\"50%\" height=\"80%\">\n\n如上图,当MinPts = 4, p的密度相较于q大,p称为高密度点\n\n### 核心点、边界点和离群点定义\n\n<img src=\"https://images.bumpchicken.cn/img/20220508220041.png\" width=\"60%\" height=\"60%\">\n\n- 核心点(Core): 高密度点,其 $\\epsilon$-邻域数据点数量 >= MinPts\n- 边界点(Border): 低密度点,但在某个核心点的邻域内\n- 离群点(Outlier): 既不是核心点也不是边界点\n\n### 密度可达定义\n\n- 直接密度可达: 如果p是一个核心点,切q在p的$\\epsilon$-邻域内,那么称q直接密度可达p\n\n【注】_不能说p直接密度可达q,直接密度可达不具有对称性(symmetric)_\n\n<img src=\"https://images.bumpchicken.cn/img/20220508221408.png\" width=\"40%\" height=\"40%\">\n\n\n- 密度可达:如果存在一串这样的数据点: $p_{1},p_{2},...,p_{n}$,其中$p_{1}=q,p_{n}=p$,且$p_{i+1}$直接密度可达$p_{i}$,那么称p密度可达q\n\n【注】_不能说q密度可达p,密度可达同样不具有对称性_\n\n<img src=\"https://images.bumpchicken.cn/img/20220508222514.png\" width=\"50%\" height=\"50%\">\n\n### 密度连通\n\n如果p和q都密度可达点o,那么称p和q密度连通,如下所示\n\n<img src=\"https://images.bumpchicken.cn/img/20220508224900.png\" width=\"50%\" height=\"50%\">\n\n【注】_密度连通具有对称性,可以说q和p密度连通_\n\n## 聚类准则\n给定一个数据集D,参数$\\epsilon$和MinPts,那么聚类产生的子集C必须满足两个准则:\n\n1. Maximality(极大性):对于任意的p、q,如果$p\\in C$,且q密度可达p,那么同样$q\\in C$\n2. Connectivity(连通性):对于任意的p、q,p和q是密度相连的\n\n## 聚类流程\n\nDBSCAN聚类过程如下图所示\n\n<img src=\"https://images.bumpchicken.cn/img/20220508225615.png\" width=\"80%\" height=\"80%\">\n\n\n## 参数选择\n\n### 邻域大小$\\epsilon$\n\nDBSCAN采用全局$\\epsilon$和MinPts值,因此每个节点的邻域大小是一致的。当数据密度和聚簇间距离分布不均匀时,若选取较小的$\\epsilon$,则较稀疏的聚簇中的数据点密度会小于MintPts,会被认为是边界点而不被用于所在类的进一步扩展,可能导致较稀疏的聚簇被划分为多个性质相似的小聚簇。相反,若选取较大的$\\epsilon$,则离得较近而密度较大的那些聚簇可能被合并为同一个聚簇,他们之间的差异将被忽略。因此这种情况下,选取合适的邻域大小是较为困难的,当维度较高时,$\\epsilon$的选取更加困难\n\n### MinPts\n\n参数MinPts的选取有一个指导性原则,即 $MinPts >= dim+1$,这里$dim$表示聚类空间的维度大小\n\n## 优缺点\n\n### 优点\n1. 可以对任意形状的稠密数据集进行聚类(K-Means一般只适用于凸数据集)\n2. 可以在聚类时发现异常点,对数据集的异常点不敏感\n\n### 缺点\n1. 如果样本集的密度不均匀,聚类间距相距很大时,聚类效果较差\n2. 对于参数 $\\epsilon$ 和MinPts敏感,不同参数组合对聚类效果影响较大","slug":"Dbscan","published":1,"updated":"2022-05-08T16:53:34.484Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl39lom8m0003otudbl8r2wm1","content":"<p><img src=\"https://images.bumpchicken.cn/img/20220508173927.png\" width=\"60%\" height=\"20%\"></p>\n<p>DBSCAN(Density-Based Spatial Clustering of Application with\nNoise)是一种基于密度的空间聚类算法。该算法将具有足够密度的区域划分为簇,并在具有噪声的空间数据中发现任意形状的簇,它将簇定义为密度相连的点的最大集合。算法无需事先指定聚类中心数目,可以对大规模无规则形状的数据进行有效聚类</p>\n<span id=\"more\"></span>\n<h2 id=\"相关定义\">相关定义</h2>\n<p>DBSCAN有自己的一套符号体系,定义了许多新概念,数学体系十分严谨</p>\n<h3 id=\"密度定义\">密度定义</h3>\n<p>给定数据集<span class=\"math inline\">\\(D\\)</span></p>\n<ul>\n<li><span class=\"math inline\">\\(\\epsilon\\)</span>: 邻域半径</li>\n<li><span class=\"math inline\">\\(\\epsilon\\)</span>-邻域:\n邻域内点的集合</li>\n</ul>\n<p><span class=\"math display\">\\[N_{\\varepsilon}(p):=\\{\\text{q in dataset\nD} \\mid \\operatorname{dist}(p,q)<=\\varepsilon\\}\\]</span></p>\n<p>【注】<em>距离度量<span\nclass=\"math inline\">\\(dist(p,q)\\)</span>是聚类算法中一个值得探究的问题。此处的距离度量可以为欧氏距离、曼哈顿距离等多种距离度量方式,并且数据点的维度可为任意维度</em></p>\n<ul>\n<li>MinPts: 核心点邻域内数据点的最小数量</li>\n</ul>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508215638.png\" width=\"50%\" height=\"80%\"></p>\n<p>如上图,当MinPts = 4, p的密度相较于q大,p称为高密度点</p>\n<h3 id=\"核心点边界点和离群点定义\">核心点、边界点和离群点定义</h3>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508220041.png\" width=\"60%\" height=\"60%\"></p>\n<ul>\n<li>核心点(Core): 高密度点,其 <span\nclass=\"math inline\">\\(\\epsilon\\)</span>-邻域数据点数量 >= MinPts</li>\n<li>边界点(Border): 低密度点,但在某个核心点的邻域内</li>\n<li>离群点(Outlier): 既不是核心点也不是边界点</li>\n</ul>\n<h3 id=\"密度可达定义\">密度可达定义</h3>\n<ul>\n<li>直接密度可达: 如果p是一个核心点,切q在p的<span\nclass=\"math inline\">\\(\\epsilon\\)</span>-邻域内,那么称q直接密度可达p</li>\n</ul>\n<p>【注】<em>不能说p直接密度可达q,直接密度可达不具有对称性(symmetric)</em></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508221408.png\" width=\"40%\" height=\"40%\"></p>\n<ul>\n<li>密度可达:如果存在一串这样的数据点: <span\nclass=\"math inline\">\\(p_{1},p_{2},...,p_{n}\\)</span>,其中<span\nclass=\"math inline\">\\(p_{1}=q,p_{n}=p\\)</span>,且<span\nclass=\"math inline\">\\(p_{i+1}\\)</span>直接密度可达<span\nclass=\"math inline\">\\(p_{i}\\)</span>,那么称p密度可达q</li>\n</ul>\n<p>【注】<em>不能说q密度可达p,密度可达同样不具有对称性</em></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508222514.png\" width=\"50%\" height=\"50%\"></p>\n<h3 id=\"密度连通\">密度连通</h3>\n<p>如果p和q都密度可达点o,那么称p和q密度连通,如下所示</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508224900.png\" width=\"50%\" height=\"50%\"></p>\n<p>【注】<em>密度连通具有对称性,可以说q和p密度连通</em></p>\n<h2 id=\"聚类准则\">聚类准则</h2>\n<p>给定一个数据集D,参数<span\nclass=\"math inline\">\\(\\epsilon\\)</span>和MinPts,那么聚类产生的子集C必须满足两个准则:</p>\n<ol type=\"1\">\n<li>Maximality(极大性):对于任意的p、q,如果<span\nclass=\"math inline\">\\(p\\in C\\)</span>,且q密度可达p,那么同样<span\nclass=\"math inline\">\\(q\\in C\\)</span></li>\n<li>Connectivity(连通性):对于任意的p、q,p和q是密度相连的</li>\n</ol>\n<h2 id=\"聚类流程\">聚类流程</h2>\n<p>DBSCAN聚类过程如下图所示</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508225615.png\" width=\"80%\" height=\"80%\"></p>\n<h2 id=\"参数选择\">参数选择</h2>\n<h3 id=\"邻域大小epsilon\">邻域大小<span\nclass=\"math inline\">\\(\\epsilon\\)</span></h3>\n<p>DBSCAN采用全局<span\nclass=\"math inline\">\\(\\epsilon\\)</span>和MinPts值,因此每个节点的邻域大小是一致的。当数据密度和聚簇间距离分布不均匀时,若选取较小的<span\nclass=\"math inline\">\\(\\epsilon\\)</span>,则较稀疏的聚簇中的数据点密度会小于MintPts,会被认为是边界点而不被用于所在类的进一步扩展,可能导致较稀疏的聚簇被划分为多个性质相似的小聚簇。相反,若选取较大的<span\nclass=\"math inline\">\\(\\epsilon\\)</span>,则离得较近而密度较大的那些聚簇可能被合并为同一个聚簇,他们之间的差异将被忽略。因此这种情况下,选取合适的邻域大小是较为困难的,当维度较高时,<span\nclass=\"math inline\">\\(\\epsilon\\)</span>的选取更加困难</p>\n<h3 id=\"minpts\">MinPts</h3>\n<p>参数MinPts的选取有一个指导性原则,即 <span\nclass=\"math inline\">\\(MinPts >= dim+1\\)</span>,这里<span\nclass=\"math inline\">\\(dim\\)</span>表示聚类空间的维度大小</p>\n<h2 id=\"优缺点\">优缺点</h2>\n<h3 id=\"优点\">优点</h3>\n<ol type=\"1\">\n<li>可以对任意形状的稠密数据集进行聚类(K-Means一般只适用于凸数据集)</li>\n<li>可以在聚类时发现异常点,对数据集的异常点不敏感</li>\n</ol>\n<h3 id=\"缺点\">缺点</h3>\n<ol type=\"1\">\n<li>如果样本集的密度不均匀,聚类间距相距很大时,聚类效果较差</li>\n<li>对于参数 <span class=\"math inline\">\\(\\epsilon\\)</span>\n和MinPts敏感,不同参数组合对聚类效果影响较大</li>\n</ol>\n","site":{"data":{}},"excerpt":"<p><img src=\"https://images.bumpchicken.cn/img/20220508173927.png\" width=\"60%\" height=\"20%\"></p>\n<p>DBSCAN(Density-Based Spatial Clustering of Application with\nNoise)是一种基于密度的空间聚类算法。该算法将具有足够密度的区域划分为簇,并在具有噪声的空间数据中发现任意形状的簇,它将簇定义为密度相连的点的最大集合。算法无需事先指定聚类中心数目,可以对大规模无规则形状的数据进行有效聚类</p>","more":"<h2 id=\"相关定义\">相关定义</h2>\n<p>DBSCAN有自己的一套符号体系,定义了许多新概念,数学体系十分严谨</p>\n<h3 id=\"密度定义\">密度定义</h3>\n<p>给定数据集<span class=\"math inline\">\\(D\\)</span></p>\n<ul>\n<li><span class=\"math inline\">\\(\\epsilon\\)</span>: 邻域半径</li>\n<li><span class=\"math inline\">\\(\\epsilon\\)</span>-邻域:\n邻域内点的集合</li>\n</ul>\n<p><span class=\"math display\">\\[N_{\\varepsilon}(p):=\\{\\text{q in dataset\nD} \\mid \\operatorname{dist}(p,q)<=\\varepsilon\\}\\]</span></p>\n<p>【注】<em>距离度量<span\nclass=\"math inline\">\\(dist(p,q)\\)</span>是聚类算法中一个值得探究的问题。此处的距离度量可以为欧氏距离、曼哈顿距离等多种距离度量方式,并且数据点的维度可为任意维度</em></p>\n<ul>\n<li>MinPts: 核心点邻域内数据点的最小数量</li>\n</ul>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508215638.png\" width=\"50%\" height=\"80%\"></p>\n<p>如上图,当MinPts = 4, p的密度相较于q大,p称为高密度点</p>\n<h3 id=\"核心点边界点和离群点定义\">核心点、边界点和离群点定义</h3>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508220041.png\" width=\"60%\" height=\"60%\"></p>\n<ul>\n<li>核心点(Core): 高密度点,其 <span\nclass=\"math inline\">\\(\\epsilon\\)</span>-邻域数据点数量 >= MinPts</li>\n<li>边界点(Border): 低密度点,但在某个核心点的邻域内</li>\n<li>离群点(Outlier): 既不是核心点也不是边界点</li>\n</ul>\n<h3 id=\"密度可达定义\">密度可达定义</h3>\n<ul>\n<li>直接密度可达: 如果p是一个核心点,切q在p的<span\nclass=\"math inline\">\\(\\epsilon\\)</span>-邻域内,那么称q直接密度可达p</li>\n</ul>\n<p>【注】<em>不能说p直接密度可达q,直接密度可达不具有对称性(symmetric)</em></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508221408.png\" width=\"40%\" height=\"40%\"></p>\n<ul>\n<li>密度可达:如果存在一串这样的数据点: <span\nclass=\"math inline\">\\(p_{1},p_{2},...,p_{n}\\)</span>,其中<span\nclass=\"math inline\">\\(p_{1}=q,p_{n}=p\\)</span>,且<span\nclass=\"math inline\">\\(p_{i+1}\\)</span>直接密度可达<span\nclass=\"math inline\">\\(p_{i}\\)</span>,那么称p密度可达q</li>\n</ul>\n<p>【注】<em>不能说q密度可达p,密度可达同样不具有对称性</em></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508222514.png\" width=\"50%\" height=\"50%\"></p>\n<h3 id=\"密度连通\">密度连通</h3>\n<p>如果p和q都密度可达点o,那么称p和q密度连通,如下所示</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508224900.png\" width=\"50%\" height=\"50%\"></p>\n<p>【注】<em>密度连通具有对称性,可以说q和p密度连通</em></p>\n<h2 id=\"聚类准则\">聚类准则</h2>\n<p>给定一个数据集D,参数<span\nclass=\"math inline\">\\(\\epsilon\\)</span>和MinPts,那么聚类产生的子集C必须满足两个准则:</p>\n<ol type=\"1\">\n<li>Maximality(极大性):对于任意的p、q,如果<span\nclass=\"math inline\">\\(p\\in C\\)</span>,且q密度可达p,那么同样<span\nclass=\"math inline\">\\(q\\in C\\)</span></li>\n<li>Connectivity(连通性):对于任意的p、q,p和q是密度相连的</li>\n</ol>\n<h2 id=\"聚类流程\">聚类流程</h2>\n<p>DBSCAN聚类过程如下图所示</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508225615.png\" width=\"80%\" height=\"80%\"></p>\n<h2 id=\"参数选择\">参数选择</h2>\n<h3 id=\"邻域大小epsilon\">邻域大小<span\nclass=\"math inline\">\\(\\epsilon\\)</span></h3>\n<p>DBSCAN采用全局<span\nclass=\"math inline\">\\(\\epsilon\\)</span>和MinPts值,因此每个节点的邻域大小是一致的。当数据密度和聚簇间距离分布不均匀时,若选取较小的<span\nclass=\"math inline\">\\(\\epsilon\\)</span>,则较稀疏的聚簇中的数据点密度会小于MintPts,会被认为是边界点而不被用于所在类的进一步扩展,可能导致较稀疏的聚簇被划分为多个性质相似的小聚簇。相反,若选取较大的<span\nclass=\"math inline\">\\(\\epsilon\\)</span>,则离得较近而密度较大的那些聚簇可能被合并为同一个聚簇,他们之间的差异将被忽略。因此这种情况下,选取合适的邻域大小是较为困难的,当维度较高时,<span\nclass=\"math inline\">\\(\\epsilon\\)</span>的选取更加困难</p>\n<h3 id=\"minpts\">MinPts</h3>\n<p>参数MinPts的选取有一个指导性原则,即 <span\nclass=\"math inline\">\\(MinPts >= dim+1\\)</span>,这里<span\nclass=\"math inline\">\\(dim\\)</span>表示聚类空间的维度大小</p>\n<h2 id=\"优缺点\">优缺点</h2>\n<h3 id=\"优点\">优点</h3>\n<ol type=\"1\">\n<li>可以对任意形状的稠密数据集进行聚类(K-Means一般只适用于凸数据集)</li>\n<li>可以在聚类时发现异常点,对数据集的异常点不敏感</li>\n</ol>\n<h3 id=\"缺点\">缺点</h3>\n<ol type=\"1\">\n<li>如果样本集的密度不均匀,聚类间距相距很大时,聚类效果较差</li>\n<li>对于参数 <span class=\"math inline\">\\(\\epsilon\\)</span>\n和MinPts敏感,不同参数组合对聚类效果影响较大</li>\n</ol>"},{"title":"面向服务及微服务架构基于图论的根因分析","date":"2020-08-28T13:41:00.000Z","mathjax":true,"_content":"\n该论文是Universidad Politécnica de Madrid(马德里技术大学)和CA科技公司(现被思科收购)合作于2020年发表在Journal of Systems and Software上的一篇论文。题目是《Graph-based Root Cause Analysis for Service-Oriented and Microservice Architectures》/ 《面向服务及微服务架构基于图论的根因分析》\n\n<!--more-->\n\n## 整体架构\n\n论文首先定义一个普遍的网络服务场景,在这个场景中包含负载均衡(load bancer),比如HAproxy,包含数个web服务,比如WordPress,包含中间件存储,比如MySQL。每个element运行在各自集群独立的容器中,容器之间能够相互**通信**。每个element都是一个节点,节点会包含一些属性(attr),这些属性可能是一些数值型的指标信息,比如cpu usage, DiskIO,或者是一些类别信息,比如说是一个标签信息用来指示当前节点是容器还是物理机,当前服务是前端还是后端服务等等。如下图所示\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202347.png\" width=\"80%\">\n\n显然,上述拓扑图中,如果有某个节点发生异常会产生链式反应。比如若load balancer发生了异常,通常会导致某个服务的入口流量减少。论文提出了一种基于图论的根因定位系统,如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202451.png\" width=\"80%\">\n\n## 系统图生成模块(System graph module)\n\n该模块的职责是将系统中所有指标、网络活动,日志和异常情况等在给定的时间窗口(例如15s)用结构化的图形表示,相当于给某个系统的当前状态作了一个快照(snapshot)。这个时间窗口是可以调整的,可以根据系统的变更率、监控频率进行调整。时间窗口不宜设置得过大或过小,较大的时间窗口会导致指标信息粗略,无法检测到某些异常;窗口设置得过小则会导致收集到大量噪音数据,无法得到有用的信息。\n\n图的构建过程,论文在第五部分会详细介绍,这里先给出了图元素的组成部分:\n\n- 节点(Nodes)\n\n节点可以用来表示任意一个容器,主机或者和系统交互的任意元素。每个节点会包含多个属性。\n\n- 边(Edges)\n\n边表示系统中各个元素之间的网络通信,例如tcp连接或http请求。还可以用来表示逻辑连接,例如容器与托管该容器之间的关系。与节点相同,边具备不同类型的多个属性。\n\n- 属性(Attr)\n\n属性从收集的指标、日志和异常中获得,包含以下几种类型:\n\n1. 数值型:可以包含离散或者连续时间范围内的指标值,比如一段时间内的cpu usage。\n2. 类别型:特定类别的标签信息。比如特定容器内的Docker镜像(比如HAproxy v1.8,WordPress V4.9)\n3. 本体类型(Ontological): 此类型用来表示概念的层级结构,如下图所示,该类型的属性用来描述语义上相同但是涉及不同元素的问题。比如,在负载均衡场景中,Nginx实例和WordPress实例是等同的,因为他们都是前端服务器。\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202538.png\" width=\"50%\">\n\n- 异常等级:可以视为一种特殊的属性,表示给定的节点或者边正在发生异常。可以用任意的异常检测方法来识别是否异常并在图中作标记。之后,异常区域提取器将使用它在系统图之外构建异常子图。\n\n## 异常区域提取模块(Anomalous region module)\n\n该模块能够对2.1中输出的系统图进行异常区域提取,该模块由两部分组成。\n\n1. 异常检测系统:检测并标记系统图中的异常节点\n2. 异常区域提取器\n\n其中,1可以使用任何有效的异常检测技术,论文假设存在此模块,不关心这一块如何实现,因为论文专注于检测到的异常而非如何检测异常。\n\n论文基于异常发生时会影响其相关节点这样的直觉,用异常区域提取器对检测到并被标记的异常节点生成异常子图。考虑到计算资源有限以及出于可伸缩性目的,异常区域提取器仅对异常周边有限节点进行提取。这样做有一个明显的缺点是,可能根因节点不在该子图范围之内,但是,该提取器提取范围是可以根据需要设置。提取出的异常子图仍然包含节点、边以及各自的属性信息。异常区域中的节点、边和属性可以单独设置权重,以表示该异常区域中给定元素的重要性。例如,机器中cpu usage消耗过多产生的异常,cpu对应属性的权重可能会相应增加,后续图相似性模块会用到这些权重。\n\n提取到的异常区域会发送到相似性模块,和已经定位过的异常进行相似性匹配。\n\n下图是异常区域提取示意图:\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202638.png\" width=\"60%\">\n\n\n#### 异常模式库(The pattern library module)\n\n异常模式库是一系列经专家标注过的异常区域的集合,这些异常区域可以和系统中正在发生的异常情况进行匹配。如果新的异常区域与库中存在的异常区域图都无法匹配(相似度达不到阈值),则将该模式添加到库中。在系统设计之初,可以手动扩充这个库,使得系统拥有一组最常见的异常模式。随着系统运行,该库将越来越丰富,尽管这可能会增加搜索时间。用户能够访问到这个库中的内容(可视化的形式),浏览并进行如下操作:\n\n1. 权重调整:用户可以对异常区域中的节点、边的权重进行设置,对一些重要的节点设置更大的权重将有助于根因定位过程。\n2. 根因标注:对异常区域图进行根因节点的标注,主要由专家对异常结构图进行分析并确定根因节点。\n\n论文尚未实现上述提到的可视化操作,更多地关注于图相似比较方法上。\n\n## 图相似性度量模块\n\n图相似性度量模块依赖模糊匹配函数,该函数考虑了两个多属性加权图之间的相似度距离。该模块接收两个输入:由异常区域提取模块生成的异常区域和来自异常模式库标记过的异常区域图。输出由两个元素组成:相似性得分和节点从图A到图B的映射。前者从0到1,得分为1表示两个图是完全相同的,而0表示两个图完全不同。后者描述了来自第一个图的那些图元素与来自第二图的元素匹配以实现相似性得分。下图是输出的示意图:\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202727.png\" width=\"60%\">\n\n图相似性可以使用任何图匹配方法,在第四部分论文详细介绍了本文使用的方法。\n\n每当新的异常区域产生时,模块会尝试将其与异常模式库中的一个或多个图进行匹配。当两个图之间的相似度高于设定的阈值时,认为两个图存在匹配。如果没有任何匹配项,则将该新的异常区域添加到异常模式库中,等待专家进行根因节点标记。如果存在一个或多个,则可以将这些匹配的模式图以及可能的根因节点按相似度排名生成一个列表显示给用户。\n\n相似度阈值的设定需要在精确率和搜索时间之间进行权衡。降低阈值将导致产生多个可能的根因节点,需要进行更多次数的比较来提高准确性。论文认为将阈值设置在0.8到0.9之间会有比较好的精确率。论文指出,对图中的节点正确地设置权重能有效提升匹配的准确率。\n\n\n\n## 图相似性度量计算引擎\n\n图相似性度量是论文所提出方法中关键的一环,论文详细介绍了其实现。\n\n### 问题定义\n\n__DEF1__.一个图由一个5元组构成,用$G$表示一个图结构\n\n$$G=(V, E, a t t, C, w)$$\n\n其中,$V$是节点的集合,$E \\subseteq V \\space \\times \\space V$是边的集合,$att$是一个函数,$attr(V \\vee E) = A$,$A$是由一系列$a = \\{ val, weight, c \\subset C \\}$这样的元素组成的列表,其中$val$是具体值,$weight$是该节点或边的权重,$c$是上下文属性。$C$表示图形上下文,用来结石图形中包含的属性,并提供一个用于比较其值的上下文,后面会提到。$w$是一个函数,$w(x)$用来生成图内节点、边或属性的权重。\n\n__DEF2__.图相似性度量定义,给定两个图\n\n$$G_{1}=\\left(V_{1}, E_{1}, a t t, C, w\\right), G_{2}=\\left(V_{2}, E_{2}, a t t, C, w\\right)$$\n\n以及两个单射函数,$m_{n}: V_{1} \\rightarrow V_{2}$和$m_{e} = E_{1} \\rightarrow E_{2}$,前者返回$G_{1}$中和$G_{2}$匹配的节点,后者返回两个图相同的边,这里我们遇到一个优化问题:找到一个使得两个图之间相似性最大化的映射,公式如下:\n\n$$\\begin{aligned}\n&\\frac{\\sum_{v \\in V_{1}}\\left(w(v)+w\\left(m_{n}(v)\\right)\\right) \\cdot \\operatorname{sim}\\left(v, m_{n}(v)\\right)+\\sum_{e \\in E_{1}}\\left(w(e)+w\\left(m_{e}(e)\\right)\\right) \\cdot \\operatorname{sim}\\left(e, m_{e}(e)\\right)}{\\sum_{v \\in V_{1}} w(v)+\\sum_{v \\in V_{2}} w(v)+\\sum_{e \\in E_{1}} w(e)+\\sum_{e \\in E_{2}} w(e)}\n\\\\\n&\\underset{m_{n}, m_{e}}{\\arg \\max }\n\\end{aligned}$$\n\n其中,$sim(x_{1}, x_{2})$是一个能得到一个0到1之间的相似性度量函数。上面公式表示,相似性度量是通过 $m_{n}$和$m_{e}$两个函数确定的最佳匹配点和边决定的,匹配点和边的权重将会影响相似性得分。最小化问题可以用任何优化手段求解,论文使用的是Hill climbing (爬山算法),使用该算法能够减少搜索时间但是是以找到局部最优而不是全局最优为代价的。\n\n$sim(v, m_{n}(v))$和$sim(e, m_{e}(e))$后文会介绍其实现,下表列出了所有相关的定义:\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202838.png\" width=\"60%\" height=\"60%\">\n\n### 节点和边的相似性计算方法\n\n节点或边的相似性由如下公式给出:\n\n$$\\frac{\\sum_{a \\in a t t(v) \\cup a t t(u)}\\left(w\\left(a_{1}\\right)+w\\left(a_{2}\\right)\\right) \\cdot \\operatorname{sim}\\left(a_{1}, a_{2}\\right)}{\\sum_{a \\in a t t(v) \\cup a t t(u)}\\left(w\\left(a_{1}\\right)+w\\left(a_{2}\\right)\\right)}$$\n\n其中,$sim(a_{1}, a_{2})$用来衡量两个元素间属性的相似性。和**DEF 2** 中公式相似,这个公式表示,两个元素之间的相似性由他们之间共同属性的加权平均值确定。同样的,属性的权重会影响相似性计算结果。增加属性的权重意味着如果两个节点或两个边的属性值相似,则它们的相似性将进一步增加,下图是一个示例,对属性(如节点的形状)赋予更大的权重,则形状的相似度优先于结构相似度,可以看到右侧匹配的节点既没有两条边也没有连接到一个圆圈,但是仍将两个图视为匹配。\n\n<img src=\"https://images.bumpchicken.cn/img/20220512203041.png\" width=\"60%\">\n\n举个例子,假如某个异常是由磁盘异常产生的,那么该异常模式中,关于磁盘相关的属性值的权重应该适当增加。\n\n\n\n### 属性相似性计算\n\n本节介绍$sim(a_{1},a_{2})$实现方式。进行属性比较时,上下文可以帮我们衡量不同属性间的相似性。上下文$C$将用于不同的相似度计算中,如果属性没有有效的上下文,那么在相似度计算中将忽略该属性。论文考虑了三种上下文:\n\n1. 明确类别的上下文(Categorical Context):具备该上下文的属性一般有明确的标签值,比较此类属性时,直接用精确的相等函数,如下所示,仅当两个属性值相等,相似度为1,否则为0。\n\n$$\\operatorname{sim}\\left(a_{1}, a_{2}\\right)=\\left\\{\\begin{array}{ll}\n1 & \\text { if } a_{1}=a_{2} \\\\\n0 & \\text { otherwise }\n\\end{array}\\right.$$\n\n2. 数值类型的上下文(Numerical Context): 具备该上下文的属性有特定的范围,上下文可以表示为$c_{numerical} = \\{min, max\\}$,比如cpu usage 的范围应该设置为从0 到100。当比较具备此类型上下文的属性时,用如下公式:\n\n $$\\operatorname{sim}\\left(a_{1}, a_{2}\\right)=1-\\frac{\\left|a_{1}-a_{2}\\right|}{\\max -m i n}$$\n\n3. 本体上下文(Ontological Context): 此类上下文一般表示为树结构,在该上下文中,每个节点表示层级结构中的一种概念。这种类型的上下文目的是使本质相同的两种属性(比如负载均衡和分布式数据库主数据库:它们的作用比较相似,都是将请求重定向到后端执行单元池)。此上下文中,论文使用 Wu 和Palmer相似性度量的一种变体:令c1和c2作为本体中的两种概念,C作为它们共同的最近的祖先,那么相似性按下式进行计算\n\n $$\\operatorname{sim}(C 1, C 2)=\\frac{2 \\cdot d(C)}{d(C 1)+d(C 2)}$$\n\n 其中,$d(x)$是从根节点遍历到节点x的节点数,要求树结构中必须包含$x$,因此$d(x) >= 1$。\n\n## 图结构生成\n\n### 节点添加\n\n首先添加构成图的节点,即系统中存在的元素(容器和主机)。由于需要提取容器级别和主机级别两种不同的指标,论文在每台机器上运行了两个容器:[node exporter](https://github.com/prometheus/node exporter) 和[cadvisor](https://github.com/google/cadvisor)。前者会监控主机,后者监控容器。收集指标的后端使用了[Prometheus](https://github.com/prometheus/prometheus),它是一个监控和报警系统。\nnode export和cadvisor通过http请求将指标上报到prometheus,指标遵循多维时间序列数据模型进行存储,即每个指标都有一系列格式为(ts, id, value, labels) 的元组,其中,ts是毫秒级别的时间戳,id指示指标属于哪个容器或主机,value是指标具体值,标签是一个可选的键值对,用来指示指标特定的维度。\n举例说明,可能有一个数据点,如$(ts = 1518537794351, id = 10.136.1.9, value = 98.1, cpu = cpu3, mode = user)$,它表示 节点为10.136.1.9的cpu3在用户模式下时间戳为1518537794351时,值为98.1。\n每个不同的id将作为一个节点添加到图中,并且其信息(label包含的键值对,如mode=user)会作为节点的属性。\n\n### 边添加\n\n添加了各个节点之后,可以通过边对它们进行连接,为此,需要监控它们的内部通信。论文通过使用Sysdig捕获容器或主机间的任何TCP通信。对于每个TCP请求,会有一个元组\n\n$$(ts,id,cip,sip,io dir, bytes)$$\n\n其中,ts是该请求发生的时间戳,id是发生通信的容器或主机,cip是客户端ip,sip是服务器ip,io dir是正则读取或写入的TCP套接字,字节是发送或读取的信息量。对一段时间内的通信进行监控,将该时间内的所有通信信息按(cip,sip)分组,构建起所有节点的边节点。下面是一个例子示意图:\n\n\n\n## 参考资料\n\n1.Brandón Á, Solé M, Huélamo A, et al. Graph-based root cause analysis for service-oriented and microservice architectures[J]. Journal of Systems and Software, 2020, 159: 110432.\n\n","source":"_posts/GraphRCA.md","raw":"title: 面向服务及微服务架构基于图论的根因分析\n\ndate: 2020-08-28 21:41:00\n\ntags: \n - 根因定位\n\ncategories:\n - 论文阅读笔记\n\nmathjax: true\n\n---\n\n该论文是Universidad Politécnica de Madrid(马德里技术大学)和CA科技公司(现被思科收购)合作于2020年发表在Journal of Systems and Software上的一篇论文。题目是《Graph-based Root Cause Analysis for Service-Oriented and Microservice Architectures》/ 《面向服务及微服务架构基于图论的根因分析》\n\n<!--more-->\n\n## 整体架构\n\n论文首先定义一个普遍的网络服务场景,在这个场景中包含负载均衡(load bancer),比如HAproxy,包含数个web服务,比如WordPress,包含中间件存储,比如MySQL。每个element运行在各自集群独立的容器中,容器之间能够相互**通信**。每个element都是一个节点,节点会包含一些属性(attr),这些属性可能是一些数值型的指标信息,比如cpu usage, DiskIO,或者是一些类别信息,比如说是一个标签信息用来指示当前节点是容器还是物理机,当前服务是前端还是后端服务等等。如下图所示\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202347.png\" width=\"80%\">\n\n显然,上述拓扑图中,如果有某个节点发生异常会产生链式反应。比如若load balancer发生了异常,通常会导致某个服务的入口流量减少。论文提出了一种基于图论的根因定位系统,如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202451.png\" width=\"80%\">\n\n## 系统图生成模块(System graph module)\n\n该模块的职责是将系统中所有指标、网络活动,日志和异常情况等在给定的时间窗口(例如15s)用结构化的图形表示,相当于给某个系统的当前状态作了一个快照(snapshot)。这个时间窗口是可以调整的,可以根据系统的变更率、监控频率进行调整。时间窗口不宜设置得过大或过小,较大的时间窗口会导致指标信息粗略,无法检测到某些异常;窗口设置得过小则会导致收集到大量噪音数据,无法得到有用的信息。\n\n图的构建过程,论文在第五部分会详细介绍,这里先给出了图元素的组成部分:\n\n- 节点(Nodes)\n\n节点可以用来表示任意一个容器,主机或者和系统交互的任意元素。每个节点会包含多个属性。\n\n- 边(Edges)\n\n边表示系统中各个元素之间的网络通信,例如tcp连接或http请求。还可以用来表示逻辑连接,例如容器与托管该容器之间的关系。与节点相同,边具备不同类型的多个属性。\n\n- 属性(Attr)\n\n属性从收集的指标、日志和异常中获得,包含以下几种类型:\n\n1. 数值型:可以包含离散或者连续时间范围内的指标值,比如一段时间内的cpu usage。\n2. 类别型:特定类别的标签信息。比如特定容器内的Docker镜像(比如HAproxy v1.8,WordPress V4.9)\n3. 本体类型(Ontological): 此类型用来表示概念的层级结构,如下图所示,该类型的属性用来描述语义上相同但是涉及不同元素的问题。比如,在负载均衡场景中,Nginx实例和WordPress实例是等同的,因为他们都是前端服务器。\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202538.png\" width=\"50%\">\n\n- 异常等级:可以视为一种特殊的属性,表示给定的节点或者边正在发生异常。可以用任意的异常检测方法来识别是否异常并在图中作标记。之后,异常区域提取器将使用它在系统图之外构建异常子图。\n\n## 异常区域提取模块(Anomalous region module)\n\n该模块能够对2.1中输出的系统图进行异常区域提取,该模块由两部分组成。\n\n1. 异常检测系统:检测并标记系统图中的异常节点\n2. 异常区域提取器\n\n其中,1可以使用任何有效的异常检测技术,论文假设存在此模块,不关心这一块如何实现,因为论文专注于检测到的异常而非如何检测异常。\n\n论文基于异常发生时会影响其相关节点这样的直觉,用异常区域提取器对检测到并被标记的异常节点生成异常子图。考虑到计算资源有限以及出于可伸缩性目的,异常区域提取器仅对异常周边有限节点进行提取。这样做有一个明显的缺点是,可能根因节点不在该子图范围之内,但是,该提取器提取范围是可以根据需要设置。提取出的异常子图仍然包含节点、边以及各自的属性信息。异常区域中的节点、边和属性可以单独设置权重,以表示该异常区域中给定元素的重要性。例如,机器中cpu usage消耗过多产生的异常,cpu对应属性的权重可能会相应增加,后续图相似性模块会用到这些权重。\n\n提取到的异常区域会发送到相似性模块,和已经定位过的异常进行相似性匹配。\n\n下图是异常区域提取示意图:\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202638.png\" width=\"60%\">\n\n\n#### 异常模式库(The pattern library module)\n\n异常模式库是一系列经专家标注过的异常区域的集合,这些异常区域可以和系统中正在发生的异常情况进行匹配。如果新的异常区域与库中存在的异常区域图都无法匹配(相似度达不到阈值),则将该模式添加到库中。在系统设计之初,可以手动扩充这个库,使得系统拥有一组最常见的异常模式。随着系统运行,该库将越来越丰富,尽管这可能会增加搜索时间。用户能够访问到这个库中的内容(可视化的形式),浏览并进行如下操作:\n\n1. 权重调整:用户可以对异常区域中的节点、边的权重进行设置,对一些重要的节点设置更大的权重将有助于根因定位过程。\n2. 根因标注:对异常区域图进行根因节点的标注,主要由专家对异常结构图进行分析并确定根因节点。\n\n论文尚未实现上述提到的可视化操作,更多地关注于图相似比较方法上。\n\n## 图相似性度量模块\n\n图相似性度量模块依赖模糊匹配函数,该函数考虑了两个多属性加权图之间的相似度距离。该模块接收两个输入:由异常区域提取模块生成的异常区域和来自异常模式库标记过的异常区域图。输出由两个元素组成:相似性得分和节点从图A到图B的映射。前者从0到1,得分为1表示两个图是完全相同的,而0表示两个图完全不同。后者描述了来自第一个图的那些图元素与来自第二图的元素匹配以实现相似性得分。下图是输出的示意图:\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202727.png\" width=\"60%\">\n\n图相似性可以使用任何图匹配方法,在第四部分论文详细介绍了本文使用的方法。\n\n每当新的异常区域产生时,模块会尝试将其与异常模式库中的一个或多个图进行匹配。当两个图之间的相似度高于设定的阈值时,认为两个图存在匹配。如果没有任何匹配项,则将该新的异常区域添加到异常模式库中,等待专家进行根因节点标记。如果存在一个或多个,则可以将这些匹配的模式图以及可能的根因节点按相似度排名生成一个列表显示给用户。\n\n相似度阈值的设定需要在精确率和搜索时间之间进行权衡。降低阈值将导致产生多个可能的根因节点,需要进行更多次数的比较来提高准确性。论文认为将阈值设置在0.8到0.9之间会有比较好的精确率。论文指出,对图中的节点正确地设置权重能有效提升匹配的准确率。\n\n\n\n## 图相似性度量计算引擎\n\n图相似性度量是论文所提出方法中关键的一环,论文详细介绍了其实现。\n\n### 问题定义\n\n__DEF1__.一个图由一个5元组构成,用$G$表示一个图结构\n\n$$G=(V, E, a t t, C, w)$$\n\n其中,$V$是节点的集合,$E \\subseteq V \\space \\times \\space V$是边的集合,$att$是一个函数,$attr(V \\vee E) = A$,$A$是由一系列$a = \\{ val, weight, c \\subset C \\}$这样的元素组成的列表,其中$val$是具体值,$weight$是该节点或边的权重,$c$是上下文属性。$C$表示图形上下文,用来结石图形中包含的属性,并提供一个用于比较其值的上下文,后面会提到。$w$是一个函数,$w(x)$用来生成图内节点、边或属性的权重。\n\n__DEF2__.图相似性度量定义,给定两个图\n\n$$G_{1}=\\left(V_{1}, E_{1}, a t t, C, w\\right), G_{2}=\\left(V_{2}, E_{2}, a t t, C, w\\right)$$\n\n以及两个单射函数,$m_{n}: V_{1} \\rightarrow V_{2}$和$m_{e} = E_{1} \\rightarrow E_{2}$,前者返回$G_{1}$中和$G_{2}$匹配的节点,后者返回两个图相同的边,这里我们遇到一个优化问题:找到一个使得两个图之间相似性最大化的映射,公式如下:\n\n$$\\begin{aligned}\n&\\frac{\\sum_{v \\in V_{1}}\\left(w(v)+w\\left(m_{n}(v)\\right)\\right) \\cdot \\operatorname{sim}\\left(v, m_{n}(v)\\right)+\\sum_{e \\in E_{1}}\\left(w(e)+w\\left(m_{e}(e)\\right)\\right) \\cdot \\operatorname{sim}\\left(e, m_{e}(e)\\right)}{\\sum_{v \\in V_{1}} w(v)+\\sum_{v \\in V_{2}} w(v)+\\sum_{e \\in E_{1}} w(e)+\\sum_{e \\in E_{2}} w(e)}\n\\\\\n&\\underset{m_{n}, m_{e}}{\\arg \\max }\n\\end{aligned}$$\n\n其中,$sim(x_{1}, x_{2})$是一个能得到一个0到1之间的相似性度量函数。上面公式表示,相似性度量是通过 $m_{n}$和$m_{e}$两个函数确定的最佳匹配点和边决定的,匹配点和边的权重将会影响相似性得分。最小化问题可以用任何优化手段求解,论文使用的是Hill climbing (爬山算法),使用该算法能够减少搜索时间但是是以找到局部最优而不是全局最优为代价的。\n\n$sim(v, m_{n}(v))$和$sim(e, m_{e}(e))$后文会介绍其实现,下表列出了所有相关的定义:\n\n<img src=\"https://images.bumpchicken.cn/img/20220512202838.png\" width=\"60%\" height=\"60%\">\n\n### 节点和边的相似性计算方法\n\n节点或边的相似性由如下公式给出:\n\n$$\\frac{\\sum_{a \\in a t t(v) \\cup a t t(u)}\\left(w\\left(a_{1}\\right)+w\\left(a_{2}\\right)\\right) \\cdot \\operatorname{sim}\\left(a_{1}, a_{2}\\right)}{\\sum_{a \\in a t t(v) \\cup a t t(u)}\\left(w\\left(a_{1}\\right)+w\\left(a_{2}\\right)\\right)}$$\n\n其中,$sim(a_{1}, a_{2})$用来衡量两个元素间属性的相似性。和**DEF 2** 中公式相似,这个公式表示,两个元素之间的相似性由他们之间共同属性的加权平均值确定。同样的,属性的权重会影响相似性计算结果。增加属性的权重意味着如果两个节点或两个边的属性值相似,则它们的相似性将进一步增加,下图是一个示例,对属性(如节点的形状)赋予更大的权重,则形状的相似度优先于结构相似度,可以看到右侧匹配的节点既没有两条边也没有连接到一个圆圈,但是仍将两个图视为匹配。\n\n<img src=\"https://images.bumpchicken.cn/img/20220512203041.png\" width=\"60%\">\n\n举个例子,假如某个异常是由磁盘异常产生的,那么该异常模式中,关于磁盘相关的属性值的权重应该适当增加。\n\n\n\n### 属性相似性计算\n\n本节介绍$sim(a_{1},a_{2})$实现方式。进行属性比较时,上下文可以帮我们衡量不同属性间的相似性。上下文$C$将用于不同的相似度计算中,如果属性没有有效的上下文,那么在相似度计算中将忽略该属性。论文考虑了三种上下文:\n\n1. 明确类别的上下文(Categorical Context):具备该上下文的属性一般有明确的标签值,比较此类属性时,直接用精确的相等函数,如下所示,仅当两个属性值相等,相似度为1,否则为0。\n\n$$\\operatorname{sim}\\left(a_{1}, a_{2}\\right)=\\left\\{\\begin{array}{ll}\n1 & \\text { if } a_{1}=a_{2} \\\\\n0 & \\text { otherwise }\n\\end{array}\\right.$$\n\n2. 数值类型的上下文(Numerical Context): 具备该上下文的属性有特定的范围,上下文可以表示为$c_{numerical} = \\{min, max\\}$,比如cpu usage 的范围应该设置为从0 到100。当比较具备此类型上下文的属性时,用如下公式:\n\n $$\\operatorname{sim}\\left(a_{1}, a_{2}\\right)=1-\\frac{\\left|a_{1}-a_{2}\\right|}{\\max -m i n}$$\n\n3. 本体上下文(Ontological Context): 此类上下文一般表示为树结构,在该上下文中,每个节点表示层级结构中的一种概念。这种类型的上下文目的是使本质相同的两种属性(比如负载均衡和分布式数据库主数据库:它们的作用比较相似,都是将请求重定向到后端执行单元池)。此上下文中,论文使用 Wu 和Palmer相似性度量的一种变体:令c1和c2作为本体中的两种概念,C作为它们共同的最近的祖先,那么相似性按下式进行计算\n\n $$\\operatorname{sim}(C 1, C 2)=\\frac{2 \\cdot d(C)}{d(C 1)+d(C 2)}$$\n\n 其中,$d(x)$是从根节点遍历到节点x的节点数,要求树结构中必须包含$x$,因此$d(x) >= 1$。\n\n## 图结构生成\n\n### 节点添加\n\n首先添加构成图的节点,即系统中存在的元素(容器和主机)。由于需要提取容器级别和主机级别两种不同的指标,论文在每台机器上运行了两个容器:[node exporter](https://github.com/prometheus/node exporter) 和[cadvisor](https://github.com/google/cadvisor)。前者会监控主机,后者监控容器。收集指标的后端使用了[Prometheus](https://github.com/prometheus/prometheus),它是一个监控和报警系统。\nnode export和cadvisor通过http请求将指标上报到prometheus,指标遵循多维时间序列数据模型进行存储,即每个指标都有一系列格式为(ts, id, value, labels) 的元组,其中,ts是毫秒级别的时间戳,id指示指标属于哪个容器或主机,value是指标具体值,标签是一个可选的键值对,用来指示指标特定的维度。\n举例说明,可能有一个数据点,如$(ts = 1518537794351, id = 10.136.1.9, value = 98.1, cpu = cpu3, mode = user)$,它表示 节点为10.136.1.9的cpu3在用户模式下时间戳为1518537794351时,值为98.1。\n每个不同的id将作为一个节点添加到图中,并且其信息(label包含的键值对,如mode=user)会作为节点的属性。\n\n### 边添加\n\n添加了各个节点之后,可以通过边对它们进行连接,为此,需要监控它们的内部通信。论文通过使用Sysdig捕获容器或主机间的任何TCP通信。对于每个TCP请求,会有一个元组\n\n$$(ts,id,cip,sip,io dir, bytes)$$\n\n其中,ts是该请求发生的时间戳,id是发生通信的容器或主机,cip是客户端ip,sip是服务器ip,io dir是正则读取或写入的TCP套接字,字节是发送或读取的信息量。对一段时间内的通信进行监控,将该时间内的所有通信信息按(cip,sip)分组,构建起所有节点的边节点。下面是一个例子示意图:\n\n\n\n## 参考资料\n\n1.Brandón Á, Solé M, Huélamo A, et al. Graph-based root cause analysis for service-oriented and microservice architectures[J]. Journal of Systems and Software, 2020, 159: 110432.\n\n","slug":"GraphRCA","published":1,"updated":"2022-05-12T12:36:32.704Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl39lom8p0007otud53k4fp9e","content":"<p>该论文是Universidad Politécnica de\nMadrid(马德里技术大学)和CA科技公司(现被思科收购)合作于2020年发表在Journal\nof Systems and Software上的一篇论文。题目是《Graph-based Root Cause\nAnalysis for Service-Oriented and Microservice Architectures》/\n《面向服务及微服务架构基于图论的根因分析》</p>\n<span id=\"more\"></span>\n<h2 id=\"整体架构\">整体架构</h2>\n<p>论文首先定义一个普遍的网络服务场景,在这个场景中包含负载均衡(load\nbancer),比如HAproxy,包含数个web服务,比如WordPress,包含中间件存储,比如MySQL。每个element运行在各自集群独立的容器中,容器之间能够相互<strong>通信</strong>。每个element都是一个节点,节点会包含一些属性(attr),这些属性可能是一些数值型的指标信息,比如cpu\nusage,\nDiskIO,或者是一些类别信息,比如说是一个标签信息用来指示当前节点是容器还是物理机,当前服务是前端还是后端服务等等。如下图所示</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202347.png\" width=\"80%\"></p>\n<p>显然,上述拓扑图中,如果有某个节点发生异常会产生链式反应。比如若load\nbalancer发生了异常,通常会导致某个服务的入口流量减少。论文提出了一种基于图论的根因定位系统,如下图所示:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202451.png\" width=\"80%\"></p>\n<h2 id=\"系统图生成模块system-graph-module\">系统图生成模块(System graph\nmodule)</h2>\n<p>该模块的职责是将系统中所有指标、网络活动,日志和异常情况等在给定的时间窗口(例如15s)用结构化的图形表示,相当于给某个系统的当前状态作了一个快照(snapshot)。这个时间窗口是可以调整的,可以根据系统的变更率、监控频率进行调整。时间窗口不宜设置得过大或过小,较大的时间窗口会导致指标信息粗略,无法检测到某些异常;窗口设置得过小则会导致收集到大量噪音数据,无法得到有用的信息。</p>\n<p>图的构建过程,论文在第五部分会详细介绍,这里先给出了图元素的组成部分:</p>\n<ul>\n<li>节点(Nodes)</li>\n</ul>\n<p>节点可以用来表示任意一个容器,主机或者和系统交互的任意元素。每个节点会包含多个属性。</p>\n<ul>\n<li>边(Edges)</li>\n</ul>\n<p>边表示系统中各个元素之间的网络通信,例如tcp连接或http请求。还可以用来表示逻辑连接,例如容器与托管该容器之间的关系。与节点相同,边具备不同类型的多个属性。</p>\n<ul>\n<li>属性(Attr)</li>\n</ul>\n<p>属性从收集的指标、日志和异常中获得,包含以下几种类型:</p>\n<ol type=\"1\">\n<li>数值型:可以包含离散或者连续时间范围内的指标值,比如一段时间内的cpu\nusage。</li>\n<li>类别型:特定类别的标签信息。比如特定容器内的Docker镜像(比如HAproxy\nv1.8,WordPress V4.9)</li>\n<li>本体类型(Ontological):\n此类型用来表示概念的层级结构,如下图所示,该类型的属性用来描述语义上相同但是涉及不同元素的问题。比如,在负载均衡场景中,Nginx实例和WordPress实例是等同的,因为他们都是前端服务器。</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202538.png\" width=\"50%\"></p>\n<ul>\n<li>异常等级:可以视为一种特殊的属性,表示给定的节点或者边正在发生异常。可以用任意的异常检测方法来识别是否异常并在图中作标记。之后,异常区域提取器将使用它在系统图之外构建异常子图。</li>\n</ul>\n<h2\nid=\"异常区域提取模块anomalous-region-module\">异常区域提取模块(Anomalous\nregion module)</h2>\n<p>该模块能够对2.1中输出的系统图进行异常区域提取,该模块由两部分组成。</p>\n<ol type=\"1\">\n<li>异常检测系统:检测并标记系统图中的异常节点</li>\n<li>异常区域提取器</li>\n</ol>\n<p>其中,1可以使用任何有效的异常检测技术,论文假设存在此模块,不关心这一块如何实现,因为论文专注于检测到的异常而非如何检测异常。</p>\n<p>论文基于异常发生时会影响其相关节点这样的直觉,用异常区域提取器对检测到并被标记的异常节点生成异常子图。考虑到计算资源有限以及出于可伸缩性目的,异常区域提取器仅对异常周边有限节点进行提取。这样做有一个明显的缺点是,可能根因节点不在该子图范围之内,但是,该提取器提取范围是可以根据需要设置。提取出的异常子图仍然包含节点、边以及各自的属性信息。异常区域中的节点、边和属性可以单独设置权重,以表示该异常区域中给定元素的重要性。例如,机器中cpu\nusage消耗过多产生的异常,cpu对应属性的权重可能会相应增加,后续图相似性模块会用到这些权重。</p>\n<p>提取到的异常区域会发送到相似性模块,和已经定位过的异常进行相似性匹配。</p>\n<p>下图是异常区域提取示意图:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202638.png\" width=\"60%\"></p>\n<h4 id=\"异常模式库the-pattern-library-module\">异常模式库(The pattern\nlibrary module)</h4>\n<p>异常模式库是一系列经专家标注过的异常区域的集合,这些异常区域可以和系统中正在发生的异常情况进行匹配。如果新的异常区域与库中存在的异常区域图都无法匹配(相似度达不到阈值),则将该模式添加到库中。在系统设计之初,可以手动扩充这个库,使得系统拥有一组最常见的异常模式。随着系统运行,该库将越来越丰富,尽管这可能会增加搜索时间。用户能够访问到这个库中的内容(可视化的形式),浏览并进行如下操作:</p>\n<ol type=\"1\">\n<li>权重调整:用户可以对异常区域中的节点、边的权重进行设置,对一些重要的节点设置更大的权重将有助于根因定位过程。</li>\n<li>根因标注:对异常区域图进行根因节点的标注,主要由专家对异常结构图进行分析并确定根因节点。</li>\n</ol>\n<p>论文尚未实现上述提到的可视化操作,更多地关注于图相似比较方法上。</p>\n<h2 id=\"图相似性度量模块\">图相似性度量模块</h2>\n<p>图相似性度量模块依赖模糊匹配函数,该函数考虑了两个多属性加权图之间的相似度距离。该模块接收两个输入:由异常区域提取模块生成的异常区域和来自异常模式库标记过的异常区域图。输出由两个元素组成:相似性得分和节点从图A到图B的映射。前者从0到1,得分为1表示两个图是完全相同的,而0表示两个图完全不同。后者描述了来自第一个图的那些图元素与来自第二图的元素匹配以实现相似性得分。下图是输出的示意图:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202727.png\" width=\"60%\"></p>\n<p>图相似性可以使用任何图匹配方法,在第四部分论文详细介绍了本文使用的方法。</p>\n<p>每当新的异常区域产生时,模块会尝试将其与异常模式库中的一个或多个图进行匹配。当两个图之间的相似度高于设定的阈值时,认为两个图存在匹配。如果没有任何匹配项,则将该新的异常区域添加到异常模式库中,等待专家进行根因节点标记。如果存在一个或多个,则可以将这些匹配的模式图以及可能的根因节点按相似度排名生成一个列表显示给用户。</p>\n<p>相似度阈值的设定需要在精确率和搜索时间之间进行权衡。降低阈值将导致产生多个可能的根因节点,需要进行更多次数的比较来提高准确性。论文认为将阈值设置在0.8到0.9之间会有比较好的精确率。论文指出,对图中的节点正确地设置权重能有效提升匹配的准确率。</p>\n<h2 id=\"图相似性度量计算引擎\">图相似性度量计算引擎</h2>\n<p>图相似性度量是论文所提出方法中关键的一环,论文详细介绍了其实现。</p>\n<h3 id=\"问题定义\">问题定义</h3>\n<p><strong>DEF1</strong>.一个图由一个5元组构成,用<span\nclass=\"math inline\">\\(G\\)</span>表示一个图结构</p>\n<p><span class=\"math display\">\\[G=(V, E, a t t, C, w)\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(V\\)</span>是节点的集合,<span\nclass=\"math inline\">\\(E \\subseteq V \\space \\times \\space\nV\\)</span>是边的集合,<span\nclass=\"math inline\">\\(att\\)</span>是一个函数,<span\nclass=\"math inline\">\\(attr(V \\vee E) = A\\)</span>,<span\nclass=\"math inline\">\\(A\\)</span>是由一系列<span class=\"math inline\">\\(a\n= \\{ val, weight, c \\subset C \\}\\)</span>这样的元素组成的列表,其中<span\nclass=\"math inline\">\\(val\\)</span>是具体值,<span\nclass=\"math inline\">\\(weight\\)</span>是该节点或边的权重,<span\nclass=\"math inline\">\\(c\\)</span>是上下文属性。<span\nclass=\"math inline\">\\(C\\)</span>表示图形上下文,用来结石图形中包含的属性,并提供一个用于比较其值的上下文,后面会提到。<span\nclass=\"math inline\">\\(w\\)</span>是一个函数,<span\nclass=\"math inline\">\\(w(x)\\)</span>用来生成图内节点、边或属性的权重。</p>\n<p><strong>DEF2</strong>.图相似性度量定义,给定两个图</p>\n<p><span class=\"math display\">\\[G_{1}=\\left(V_{1}, E_{1}, a t t, C,\nw\\right), G_{2}=\\left(V_{2}, E_{2}, a t t, C, w\\right)\\]</span></p>\n<p>以及两个单射函数,<span class=\"math inline\">\\(m_{n}: V_{1}\n\\rightarrow V_{2}\\)</span>和<span class=\"math inline\">\\(m_{e} = E_{1}\n\\rightarrow E_{2}\\)</span>,前者返回<span\nclass=\"math inline\">\\(G_{1}\\)</span>中和<span\nclass=\"math inline\">\\(G_{2}\\)</span>匹配的节点,后者返回两个图相同的边,这里我们遇到一个优化问题:找到一个使得两个图之间相似性最大化的映射,公式如下:</p>\n<p><span class=\"math display\">\\[\\begin{aligned}\n&\\frac{\\sum_{v \\in V_{1}}\\left(w(v)+w\\left(m_{n}(v)\\right)\\right)\n\\cdot \\operatorname{sim}\\left(v, m_{n}(v)\\right)+\\sum_{e \\in\nE_{1}}\\left(w(e)+w\\left(m_{e}(e)\\right)\\right) \\cdot\n\\operatorname{sim}\\left(e, m_{e}(e)\\right)}{\\sum_{v \\in V_{1}}\nw(v)+\\sum_{v \\in V_{2}} w(v)+\\sum_{e \\in E_{1}} w(e)+\\sum_{e \\in E_{2}}\nw(e)}\n\\\\\n&\\underset{m_{n}, m_{e}}{\\arg \\max }\n\\end{aligned}\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(sim(x_{1},\nx_{2})\\)</span>是一个能得到一个0到1之间的相似性度量函数。上面公式表示,相似性度量是通过\n<span class=\"math inline\">\\(m_{n}\\)</span>和<span\nclass=\"math inline\">\\(m_{e}\\)</span>两个函数确定的最佳匹配点和边决定的,匹配点和边的权重将会影响相似性得分。最小化问题可以用任何优化手段求解,论文使用的是Hill\nclimbing\n(爬山算法),使用该算法能够减少搜索时间但是是以找到局部最优而不是全局最优为代价的。</p>\n<p><span class=\"math inline\">\\(sim(v, m_{n}(v))\\)</span>和<span\nclass=\"math inline\">\\(sim(e,\nm_{e}(e))\\)</span>后文会介绍其实现,下表列出了所有相关的定义:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202838.png\" width=\"60%\" height=\"60%\"></p>\n<h3 id=\"节点和边的相似性计算方法\">节点和边的相似性计算方法</h3>\n<p>节点或边的相似性由如下公式给出:</p>\n<p><span class=\"math display\">\\[\\frac{\\sum_{a \\in a t t(v) \\cup a t\nt(u)}\\left(w\\left(a_{1}\\right)+w\\left(a_{2}\\right)\\right) \\cdot\n\\operatorname{sim}\\left(a_{1}, a_{2}\\right)}{\\sum_{a \\in a t t(v) \\cup a\nt\nt(u)}\\left(w\\left(a_{1}\\right)+w\\left(a_{2}\\right)\\right)}\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(sim(a_{1},\na_{2})\\)</span>用来衡量两个元素间属性的相似性。和<strong>DEF 2</strong>\n中公式相似,这个公式表示,两个元素之间的相似性由他们之间共同属性的加权平均值确定。同样的,属性的权重会影响相似性计算结果。增加属性的权重意味着如果两个节点或两个边的属性值相似,则它们的相似性将进一步增加,下图是一个示例,对属性(如节点的形状)赋予更大的权重,则形状的相似度优先于结构相似度,可以看到右侧匹配的节点既没有两条边也没有连接到一个圆圈,但是仍将两个图视为匹配。</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512203041.png\" width=\"60%\"></p>\n<p>举个例子,假如某个异常是由磁盘异常产生的,那么该异常模式中,关于磁盘相关的属性值的权重应该适当增加。</p>\n<h3 id=\"属性相似性计算\">属性相似性计算</h3>\n<p>本节介绍<span\nclass=\"math inline\">\\(sim(a_{1},a_{2})\\)</span>实现方式。进行属性比较时,上下文可以帮我们衡量不同属性间的相似性。上下文<span\nclass=\"math inline\">\\(C\\)</span>将用于不同的相似度计算中,如果属性没有有效的上下文,那么在相似度计算中将忽略该属性。论文考虑了三种上下文:</p>\n<ol type=\"1\">\n<li>明确类别的上下文(Categorical\nContext):具备该上下文的属性一般有明确的标签值,比较此类属性时,直接用精确的相等函数,如下所示,仅当两个属性值相等,相似度为1,否则为0。</li>\n</ol>\n<p><span class=\"math display\">\\[\\operatorname{sim}\\left(a_{1},\na_{2}\\right)=\\left\\{\\begin{array}{ll}\n1 & \\text { if } a_{1}=a_{2} \\\\\n0 & \\text { otherwise }\n\\end{array}\\right.\\]</span></p>\n<ol start=\"2\" type=\"1\">\n<li><p>数值类型的上下文(Numerical Context):\n具备该上下文的属性有特定的范围,上下文可以表示为<span\nclass=\"math inline\">\\(c_{numerical} = \\{min, max\\}\\)</span>,比如cpu\nusage 的范围应该设置为从0\n到100。当比较具备此类型上下文的属性时,用如下公式:</p>\n<p><span class=\"math display\">\\[\\operatorname{sim}\\left(a_{1},\na_{2}\\right)=1-\\frac{\\left|a_{1}-a_{2}\\right|}{\\max -m i\nn}\\]</span></p></li>\n<li><p>本体上下文(Ontological Context):\n此类上下文一般表示为树结构,在该上下文中,每个节点表示层级结构中的一种概念。这种类型的上下文目的是使本质相同的两种属性(比如负载均衡和分布式数据库主数据库:它们的作用比较相似,都是将请求重定向到后端执行单元池)。此上下文中,论文使用\nWu\n和Palmer相似性度量的一种变体:令c1和c2作为本体中的两种概念,C作为它们共同的最近的祖先,那么相似性按下式进行计算</p>\n<p><span class=\"math display\">\\[\\operatorname{sim}(C 1, C 2)=\\frac{2\n\\cdot d(C)}{d(C 1)+d(C 2)}\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(d(x)\\)</span>是从根节点遍历到节点x的节点数,要求树结构中必须包含<span\nclass=\"math inline\">\\(x\\)</span>,因此<span class=\"math inline\">\\(d(x)\n>= 1\\)</span>。</p></li>\n</ol>\n<h2 id=\"图结构生成\">图结构生成</h2>\n<h3 id=\"节点添加\">节点添加</h3>\n<p>首先添加构成图的节点,即系统中存在的元素(容器和主机)。由于需要提取容器级别和主机级别两种不同的指标,论文在每台机器上运行了两个容器:<a\nhref=\"https://github.com/prometheus/node%20exporter\">node exporter</a>\n和<a\nhref=\"https://github.com/google/cadvisor\">cadvisor</a>。前者会监控主机,后者监控容器。收集指标的后端使用了<a\nhref=\"https://github.com/prometheus/prometheus\">Prometheus</a>,它是一个监控和报警系统。\nnode\nexport和cadvisor通过http请求将指标上报到prometheus,指标遵循多维时间序列数据模型进行存储,即每个指标都有一系列格式为(ts,\nid, value, labels)\n的元组,其中,ts是毫秒级别的时间戳,id指示指标属于哪个容器或主机,value是指标具体值,标签是一个可选的键值对,用来指示指标特定的维度。\n举例说明,可能有一个数据点,如<span class=\"math inline\">\\((ts =\n1518537794351, id = 10.136.1.9, value = 98.1, cpu = cpu3, mode =\nuser)\\)</span>,它表示\n节点为10.136.1.9的cpu3在用户模式下时间戳为1518537794351时,值为98.1。\n每个不同的id将作为一个节点添加到图中,并且其信息(label包含的键值对,如mode=user)会作为节点的属性。</p>\n<h3 id=\"边添加\">边添加</h3>\n<p>添加了各个节点之后,可以通过边对它们进行连接,为此,需要监控它们的内部通信。论文通过使用Sysdig捕获容器或主机间的任何TCP通信。对于每个TCP请求,会有一个元组</p>\n<p><span class=\"math display\">\\[(ts,id,cip,sip,io dir,\nbytes)\\]</span></p>\n<p>其中,ts是该请求发生的时间戳,id是发生通信的容器或主机,cip是客户端ip,sip是服务器ip,io\ndir是正则读取或写入的TCP套接字,字节是发送或读取的信息量。对一段时间内的通信进行监控,将该时间内的所有通信信息按(cip,sip)分组,构建起所有节点的边节点。下面是一个例子示意图:</p>\n<h2 id=\"参考资料\">参考资料</h2>\n<p>1.Brandón Á, Solé M, Huélamo A, et al. Graph-based root cause\nanalysis for service-oriented and microservice architectures[J]. Journal\nof Systems and Software, 2020, 159: 110432.</p>\n","site":{"data":{}},"excerpt":"<p>该论文是Universidad Politécnica de\nMadrid(马德里技术大学)和CA科技公司(现被思科收购)合作于2020年发表在Journal\nof Systems and Software上的一篇论文。题目是《Graph-based Root Cause\nAnalysis for Service-Oriented and Microservice Architectures》/\n《面向服务及微服务架构基于图论的根因分析》</p>","more":"<h2 id=\"整体架构\">整体架构</h2>\n<p>论文首先定义一个普遍的网络服务场景,在这个场景中包含负载均衡(load\nbancer),比如HAproxy,包含数个web服务,比如WordPress,包含中间件存储,比如MySQL。每个element运行在各自集群独立的容器中,容器之间能够相互<strong>通信</strong>。每个element都是一个节点,节点会包含一些属性(attr),这些属性可能是一些数值型的指标信息,比如cpu\nusage,\nDiskIO,或者是一些类别信息,比如说是一个标签信息用来指示当前节点是容器还是物理机,当前服务是前端还是后端服务等等。如下图所示</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202347.png\" width=\"80%\"></p>\n<p>显然,上述拓扑图中,如果有某个节点发生异常会产生链式反应。比如若load\nbalancer发生了异常,通常会导致某个服务的入口流量减少。论文提出了一种基于图论的根因定位系统,如下图所示:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202451.png\" width=\"80%\"></p>\n<h2 id=\"系统图生成模块system-graph-module\">系统图生成模块(System graph\nmodule)</h2>\n<p>该模块的职责是将系统中所有指标、网络活动,日志和异常情况等在给定的时间窗口(例如15s)用结构化的图形表示,相当于给某个系统的当前状态作了一个快照(snapshot)。这个时间窗口是可以调整的,可以根据系统的变更率、监控频率进行调整。时间窗口不宜设置得过大或过小,较大的时间窗口会导致指标信息粗略,无法检测到某些异常;窗口设置得过小则会导致收集到大量噪音数据,无法得到有用的信息。</p>\n<p>图的构建过程,论文在第五部分会详细介绍,这里先给出了图元素的组成部分:</p>\n<ul>\n<li>节点(Nodes)</li>\n</ul>\n<p>节点可以用来表示任意一个容器,主机或者和系统交互的任意元素。每个节点会包含多个属性。</p>\n<ul>\n<li>边(Edges)</li>\n</ul>\n<p>边表示系统中各个元素之间的网络通信,例如tcp连接或http请求。还可以用来表示逻辑连接,例如容器与托管该容器之间的关系。与节点相同,边具备不同类型的多个属性。</p>\n<ul>\n<li>属性(Attr)</li>\n</ul>\n<p>属性从收集的指标、日志和异常中获得,包含以下几种类型:</p>\n<ol type=\"1\">\n<li>数值型:可以包含离散或者连续时间范围内的指标值,比如一段时间内的cpu\nusage。</li>\n<li>类别型:特定类别的标签信息。比如特定容器内的Docker镜像(比如HAproxy\nv1.8,WordPress V4.9)</li>\n<li>本体类型(Ontological):\n此类型用来表示概念的层级结构,如下图所示,该类型的属性用来描述语义上相同但是涉及不同元素的问题。比如,在负载均衡场景中,Nginx实例和WordPress实例是等同的,因为他们都是前端服务器。</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202538.png\" width=\"50%\"></p>\n<ul>\n<li>异常等级:可以视为一种特殊的属性,表示给定的节点或者边正在发生异常。可以用任意的异常检测方法来识别是否异常并在图中作标记。之后,异常区域提取器将使用它在系统图之外构建异常子图。</li>\n</ul>\n<h2\nid=\"异常区域提取模块anomalous-region-module\">异常区域提取模块(Anomalous\nregion module)</h2>\n<p>该模块能够对2.1中输出的系统图进行异常区域提取,该模块由两部分组成。</p>\n<ol type=\"1\">\n<li>异常检测系统:检测并标记系统图中的异常节点</li>\n<li>异常区域提取器</li>\n</ol>\n<p>其中,1可以使用任何有效的异常检测技术,论文假设存在此模块,不关心这一块如何实现,因为论文专注于检测到的异常而非如何检测异常。</p>\n<p>论文基于异常发生时会影响其相关节点这样的直觉,用异常区域提取器对检测到并被标记的异常节点生成异常子图。考虑到计算资源有限以及出于可伸缩性目的,异常区域提取器仅对异常周边有限节点进行提取。这样做有一个明显的缺点是,可能根因节点不在该子图范围之内,但是,该提取器提取范围是可以根据需要设置。提取出的异常子图仍然包含节点、边以及各自的属性信息。异常区域中的节点、边和属性可以单独设置权重,以表示该异常区域中给定元素的重要性。例如,机器中cpu\nusage消耗过多产生的异常,cpu对应属性的权重可能会相应增加,后续图相似性模块会用到这些权重。</p>\n<p>提取到的异常区域会发送到相似性模块,和已经定位过的异常进行相似性匹配。</p>\n<p>下图是异常区域提取示意图:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202638.png\" width=\"60%\"></p>\n<h4 id=\"异常模式库the-pattern-library-module\">异常模式库(The pattern\nlibrary module)</h4>\n<p>异常模式库是一系列经专家标注过的异常区域的集合,这些异常区域可以和系统中正在发生的异常情况进行匹配。如果新的异常区域与库中存在的异常区域图都无法匹配(相似度达不到阈值),则将该模式添加到库中。在系统设计之初,可以手动扩充这个库,使得系统拥有一组最常见的异常模式。随着系统运行,该库将越来越丰富,尽管这可能会增加搜索时间。用户能够访问到这个库中的内容(可视化的形式),浏览并进行如下操作:</p>\n<ol type=\"1\">\n<li>权重调整:用户可以对异常区域中的节点、边的权重进行设置,对一些重要的节点设置更大的权重将有助于根因定位过程。</li>\n<li>根因标注:对异常区域图进行根因节点的标注,主要由专家对异常结构图进行分析并确定根因节点。</li>\n</ol>\n<p>论文尚未实现上述提到的可视化操作,更多地关注于图相似比较方法上。</p>\n<h2 id=\"图相似性度量模块\">图相似性度量模块</h2>\n<p>图相似性度量模块依赖模糊匹配函数,该函数考虑了两个多属性加权图之间的相似度距离。该模块接收两个输入:由异常区域提取模块生成的异常区域和来自异常模式库标记过的异常区域图。输出由两个元素组成:相似性得分和节点从图A到图B的映射。前者从0到1,得分为1表示两个图是完全相同的,而0表示两个图完全不同。后者描述了来自第一个图的那些图元素与来自第二图的元素匹配以实现相似性得分。下图是输出的示意图:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202727.png\" width=\"60%\"></p>\n<p>图相似性可以使用任何图匹配方法,在第四部分论文详细介绍了本文使用的方法。</p>\n<p>每当新的异常区域产生时,模块会尝试将其与异常模式库中的一个或多个图进行匹配。当两个图之间的相似度高于设定的阈值时,认为两个图存在匹配。如果没有任何匹配项,则将该新的异常区域添加到异常模式库中,等待专家进行根因节点标记。如果存在一个或多个,则可以将这些匹配的模式图以及可能的根因节点按相似度排名生成一个列表显示给用户。</p>\n<p>相似度阈值的设定需要在精确率和搜索时间之间进行权衡。降低阈值将导致产生多个可能的根因节点,需要进行更多次数的比较来提高准确性。论文认为将阈值设置在0.8到0.9之间会有比较好的精确率。论文指出,对图中的节点正确地设置权重能有效提升匹配的准确率。</p>\n<h2 id=\"图相似性度量计算引擎\">图相似性度量计算引擎</h2>\n<p>图相似性度量是论文所提出方法中关键的一环,论文详细介绍了其实现。</p>\n<h3 id=\"问题定义\">问题定义</h3>\n<p><strong>DEF1</strong>.一个图由一个5元组构成,用<span\nclass=\"math inline\">\\(G\\)</span>表示一个图结构</p>\n<p><span class=\"math display\">\\[G=(V, E, a t t, C, w)\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(V\\)</span>是节点的集合,<span\nclass=\"math inline\">\\(E \\subseteq V \\space \\times \\space\nV\\)</span>是边的集合,<span\nclass=\"math inline\">\\(att\\)</span>是一个函数,<span\nclass=\"math inline\">\\(attr(V \\vee E) = A\\)</span>,<span\nclass=\"math inline\">\\(A\\)</span>是由一系列<span class=\"math inline\">\\(a\n= \\{ val, weight, c \\subset C \\}\\)</span>这样的元素组成的列表,其中<span\nclass=\"math inline\">\\(val\\)</span>是具体值,<span\nclass=\"math inline\">\\(weight\\)</span>是该节点或边的权重,<span\nclass=\"math inline\">\\(c\\)</span>是上下文属性。<span\nclass=\"math inline\">\\(C\\)</span>表示图形上下文,用来结石图形中包含的属性,并提供一个用于比较其值的上下文,后面会提到。<span\nclass=\"math inline\">\\(w\\)</span>是一个函数,<span\nclass=\"math inline\">\\(w(x)\\)</span>用来生成图内节点、边或属性的权重。</p>\n<p><strong>DEF2</strong>.图相似性度量定义,给定两个图</p>\n<p><span class=\"math display\">\\[G_{1}=\\left(V_{1}, E_{1}, a t t, C,\nw\\right), G_{2}=\\left(V_{2}, E_{2}, a t t, C, w\\right)\\]</span></p>\n<p>以及两个单射函数,<span class=\"math inline\">\\(m_{n}: V_{1}\n\\rightarrow V_{2}\\)</span>和<span class=\"math inline\">\\(m_{e} = E_{1}\n\\rightarrow E_{2}\\)</span>,前者返回<span\nclass=\"math inline\">\\(G_{1}\\)</span>中和<span\nclass=\"math inline\">\\(G_{2}\\)</span>匹配的节点,后者返回两个图相同的边,这里我们遇到一个优化问题:找到一个使得两个图之间相似性最大化的映射,公式如下:</p>\n<p><span class=\"math display\">\\[\\begin{aligned}\n&\\frac{\\sum_{v \\in V_{1}}\\left(w(v)+w\\left(m_{n}(v)\\right)\\right)\n\\cdot \\operatorname{sim}\\left(v, m_{n}(v)\\right)+\\sum_{e \\in\nE_{1}}\\left(w(e)+w\\left(m_{e}(e)\\right)\\right) \\cdot\n\\operatorname{sim}\\left(e, m_{e}(e)\\right)}{\\sum_{v \\in V_{1}}\nw(v)+\\sum_{v \\in V_{2}} w(v)+\\sum_{e \\in E_{1}} w(e)+\\sum_{e \\in E_{2}}\nw(e)}\n\\\\\n&\\underset{m_{n}, m_{e}}{\\arg \\max }\n\\end{aligned}\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(sim(x_{1},\nx_{2})\\)</span>是一个能得到一个0到1之间的相似性度量函数。上面公式表示,相似性度量是通过\n<span class=\"math inline\">\\(m_{n}\\)</span>和<span\nclass=\"math inline\">\\(m_{e}\\)</span>两个函数确定的最佳匹配点和边决定的,匹配点和边的权重将会影响相似性得分。最小化问题可以用任何优化手段求解,论文使用的是Hill\nclimbing\n(爬山算法),使用该算法能够减少搜索时间但是是以找到局部最优而不是全局最优为代价的。</p>\n<p><span class=\"math inline\">\\(sim(v, m_{n}(v))\\)</span>和<span\nclass=\"math inline\">\\(sim(e,\nm_{e}(e))\\)</span>后文会介绍其实现,下表列出了所有相关的定义:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512202838.png\" width=\"60%\" height=\"60%\"></p>\n<h3 id=\"节点和边的相似性计算方法\">节点和边的相似性计算方法</h3>\n<p>节点或边的相似性由如下公式给出:</p>\n<p><span class=\"math display\">\\[\\frac{\\sum_{a \\in a t t(v) \\cup a t\nt(u)}\\left(w\\left(a_{1}\\right)+w\\left(a_{2}\\right)\\right) \\cdot\n\\operatorname{sim}\\left(a_{1}, a_{2}\\right)}{\\sum_{a \\in a t t(v) \\cup a\nt\nt(u)}\\left(w\\left(a_{1}\\right)+w\\left(a_{2}\\right)\\right)}\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(sim(a_{1},\na_{2})\\)</span>用来衡量两个元素间属性的相似性。和<strong>DEF 2</strong>\n中公式相似,这个公式表示,两个元素之间的相似性由他们之间共同属性的加权平均值确定。同样的,属性的权重会影响相似性计算结果。增加属性的权重意味着如果两个节点或两个边的属性值相似,则它们的相似性将进一步增加,下图是一个示例,对属性(如节点的形状)赋予更大的权重,则形状的相似度优先于结构相似度,可以看到右侧匹配的节点既没有两条边也没有连接到一个圆圈,但是仍将两个图视为匹配。</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220512203041.png\" width=\"60%\"></p>\n<p>举个例子,假如某个异常是由磁盘异常产生的,那么该异常模式中,关于磁盘相关的属性值的权重应该适当增加。</p>\n<h3 id=\"属性相似性计算\">属性相似性计算</h3>\n<p>本节介绍<span\nclass=\"math inline\">\\(sim(a_{1},a_{2})\\)</span>实现方式。进行属性比较时,上下文可以帮我们衡量不同属性间的相似性。上下文<span\nclass=\"math inline\">\\(C\\)</span>将用于不同的相似度计算中,如果属性没有有效的上下文,那么在相似度计算中将忽略该属性。论文考虑了三种上下文:</p>\n<ol type=\"1\">\n<li>明确类别的上下文(Categorical\nContext):具备该上下文的属性一般有明确的标签值,比较此类属性时,直接用精确的相等函数,如下所示,仅当两个属性值相等,相似度为1,否则为0。</li>\n</ol>\n<p><span class=\"math display\">\\[\\operatorname{sim}\\left(a_{1},\na_{2}\\right)=\\left\\{\\begin{array}{ll}\n1 & \\text { if } a_{1}=a_{2} \\\\\n0 & \\text { otherwise }\n\\end{array}\\right.\\]</span></p>\n<ol start=\"2\" type=\"1\">\n<li><p>数值类型的上下文(Numerical Context):\n具备该上下文的属性有特定的范围,上下文可以表示为<span\nclass=\"math inline\">\\(c_{numerical} = \\{min, max\\}\\)</span>,比如cpu\nusage 的范围应该设置为从0\n到100。当比较具备此类型上下文的属性时,用如下公式:</p>\n<p><span class=\"math display\">\\[\\operatorname{sim}\\left(a_{1},\na_{2}\\right)=1-\\frac{\\left|a_{1}-a_{2}\\right|}{\\max -m i\nn}\\]</span></p></li>\n<li><p>本体上下文(Ontological Context):\n此类上下文一般表示为树结构,在该上下文中,每个节点表示层级结构中的一种概念。这种类型的上下文目的是使本质相同的两种属性(比如负载均衡和分布式数据库主数据库:它们的作用比较相似,都是将请求重定向到后端执行单元池)。此上下文中,论文使用\nWu\n和Palmer相似性度量的一种变体:令c1和c2作为本体中的两种概念,C作为它们共同的最近的祖先,那么相似性按下式进行计算</p>\n<p><span class=\"math display\">\\[\\operatorname{sim}(C 1, C 2)=\\frac{2\n\\cdot d(C)}{d(C 1)+d(C 2)}\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(d(x)\\)</span>是从根节点遍历到节点x的节点数,要求树结构中必须包含<span\nclass=\"math inline\">\\(x\\)</span>,因此<span class=\"math inline\">\\(d(x)\n>= 1\\)</span>。</p></li>\n</ol>\n<h2 id=\"图结构生成\">图结构生成</h2>\n<h3 id=\"节点添加\">节点添加</h3>\n<p>首先添加构成图的节点,即系统中存在的元素(容器和主机)。由于需要提取容器级别和主机级别两种不同的指标,论文在每台机器上运行了两个容器:<a\nhref=\"https://github.com/prometheus/node%20exporter\">node exporter</a>\n和<a\nhref=\"https://github.com/google/cadvisor\">cadvisor</a>。前者会监控主机,后者监控容器。收集指标的后端使用了<a\nhref=\"https://github.com/prometheus/prometheus\">Prometheus</a>,它是一个监控和报警系统。\nnode\nexport和cadvisor通过http请求将指标上报到prometheus,指标遵循多维时间序列数据模型进行存储,即每个指标都有一系列格式为(ts,\nid, value, labels)\n的元组,其中,ts是毫秒级别的时间戳,id指示指标属于哪个容器或主机,value是指标具体值,标签是一个可选的键值对,用来指示指标特定的维度。\n举例说明,可能有一个数据点,如<span class=\"math inline\">\\((ts =\n1518537794351, id = 10.136.1.9, value = 98.1, cpu = cpu3, mode =\nuser)\\)</span>,它表示\n节点为10.136.1.9的cpu3在用户模式下时间戳为1518537794351时,值为98.1。\n每个不同的id将作为一个节点添加到图中,并且其信息(label包含的键值对,如mode=user)会作为节点的属性。</p>\n<h3 id=\"边添加\">边添加</h3>\n<p>添加了各个节点之后,可以通过边对它们进行连接,为此,需要监控它们的内部通信。论文通过使用Sysdig捕获容器或主机间的任何TCP通信。对于每个TCP请求,会有一个元组</p>\n<p><span class=\"math display\">\\[(ts,id,cip,sip,io dir,\nbytes)\\]</span></p>\n<p>其中,ts是该请求发生的时间戳,id是发生通信的容器或主机,cip是客户端ip,sip是服务器ip,io\ndir是正则读取或写入的TCP套接字,字节是发送或读取的信息量。对一段时间内的通信进行监控,将该时间内的所有通信信息按(cip,sip)分组,构建起所有节点的边节点。下面是一个例子示意图:</p>\n<h2 id=\"参考资料\">参考资料</h2>\n<p>1.Brandón Á, Solé M, Huélamo A, et al. Graph-based root cause\nanalysis for service-oriented and microservice architectures[J]. Journal\nof Systems and Software, 2020, 159: 110432.</p>"},{"title":"孤立森林原理详解","date":"2020-05-25T03:33:00.000Z","mathjax":true,"top":9999,"_content":" \n![](https://images.bumpchicken.cn/img/tree.png)\n\n孤立森林(Isolation Forest)是周志华团队于2008年提出的一种具有线性复杂度的异常检测算法,被工业界广泛应用于诸如异常流量检测,金融欺诈行为检测等场景。\n\n<!--more-->\n\n## 算法原理\n异常检测领域,通常是正常的样本占大多数,离群点占绝少数,因此大多数异常检测算法的基本思想都是对正常点构建模型,然后根据规则识别出不属于正常点模型的离群点,比较典型的算法有One Class SVM(OCSVM), Local Outlier Factor(LOF)。和多数异常检测算法不同,孤立森林采用了一种较为高效的异常发现算法,其思路很朴素,但也足够直观有效。\n\n考虑以下场景,一个二维平面上零零散散分布着一些点,随机使用分割线对其进行分割,直至所有但点都不可再划分(即被孤立了)。直观上来讲,可以发现那些密度很高的簇需要被切割很多次才会停止切割,但是密度很低的点很快就会停止切割到某个子空间了。\n\n<img src=\"https://images.bumpchicken.cn/img/20220424235501.png\" width=\"90%\" height=\"50%\">\n\n\n孤立森林分<b>训练</b>和<b>异常评估</b>两部分:\n\n- <b>训练:</b> 根据样本抽样构建多棵iTree,形成孤立森林\n- <b>异常评估:</b> 根据训练过程构建的孤立森林,计算待评估值的异常得分\n\n## 训练\n1. 给定训练数据集$X$,确定需要构建的孤立树(iTree)个数t,按$\\phi$采样大小随机取样作为子样本集$X^{'}$\n2. 在子样本集$X^{'}$上构建一棵孤立树(iTree),过程如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220508001729.png\" width=\"80%\" height=\"20%\">\n\na) 在$X$中随机选择一个属性(维度),在当前样本数据范围内,随机产生一个分割点$p$(介于当前维度最大和最小值之间)\n\nb) 此切割点即是一个超平面,将当前节点数据空间切分成2个子空间:将当前所选维度下小于p点的放在当前节点左分支,把大于p点的放在当前节点的右分支\n\nc) 在节点的左分支和右分支递归执行步骤a,b,不断构造新的叶子节点,直到叶子节点上只有一个数据或者树已经生长到了限制的高度\n\nd) 单棵iTree构建完成\n\n3. 按2的过程,依次构建t棵iTree,得到孤立森林\n\n<img src=\"https://images.bumpchicken.cn/img/20220508003153.png\" width=\"80%\" height=\"30%\">\n\n\n## 异常评估\n\n构建了孤立森林(IForest)后,可以评估某个点$x$的异常得分,用到如下公式:\n\n$$s(x,n)=2^{-\\frac{E(h(x))}{c(n}} $$\n\n其中,$h(x)$ 表示$x$在某棵孤立树中的路径长度,$E(h(x))$ 表示在所有孤立树中的期望路径长度。$c(n)$ 为样本数为n时的二叉排序树(BST)的平均搜索路径长度,用来对样本$x$的期望路径长度做归一化处理。$c(n)$公式如下:\n\n$$c(n)=2H(n-1)-(2(n-1)/n)$$\n\n其中,$H(i)$是一个调和数,约等于 $ln(i) + \\gamma$,$\\gamma$为欧拉常数,约等于0.5772156649\n\n\n论文对于异常得分分布有如下结论:\n\n1. 如果异常得分接近1,那么一定是异常点\n\n2. 如果异常得分远小于0.5, 那么一定不是异常点\n\n3. 如果样本点的异常得分均在0.5左右,那么样本中可能不存在异常点\n\n异常得分$s$和$E(h(x))$关系图如下所示\n\n<img src=\"https://images.bumpchicken.cn/img/20220508010609.png\" width=\"50%\" height=\"30%\">\n\n\n异常得分的等高线图如下所示,通常潜在的异常点$s>=0.6$\n<img src=\"http://images.bumpchicken.cn/img/20220508010909.png\" width=\"50%\" height=\"30%\">\n\n## 参考资料\n\n1.Liu F T, Ting K M, Zhou Z H. Isolation forest[C]//2008 Eighth IEEE International Conference on Data Mining. IEEE, 2008: 413-422.\n","source":"_posts/IsolationTree.md","raw":"\ntitle: 孤立森林原理详解\n\ndate: 2020-05-25 11:33:00\n\ntags: \n - 异常检测\n\ncategories:\n - 异常检测\n\nmathjax: true\n\ntop: 9999\n\n---\n \n![](https://images.bumpchicken.cn/img/tree.png)\n\n孤立森林(Isolation Forest)是周志华团队于2008年提出的一种具有线性复杂度的异常检测算法,被工业界广泛应用于诸如异常流量检测,金融欺诈行为检测等场景。\n\n<!--more-->\n\n## 算法原理\n异常检测领域,通常是正常的样本占大多数,离群点占绝少数,因此大多数异常检测算法的基本思想都是对正常点构建模型,然后根据规则识别出不属于正常点模型的离群点,比较典型的算法有One Class SVM(OCSVM), Local Outlier Factor(LOF)。和多数异常检测算法不同,孤立森林采用了一种较为高效的异常发现算法,其思路很朴素,但也足够直观有效。\n\n考虑以下场景,一个二维平面上零零散散分布着一些点,随机使用分割线对其进行分割,直至所有但点都不可再划分(即被孤立了)。直观上来讲,可以发现那些密度很高的簇需要被切割很多次才会停止切割,但是密度很低的点很快就会停止切割到某个子空间了。\n\n<img src=\"https://images.bumpchicken.cn/img/20220424235501.png\" width=\"90%\" height=\"50%\">\n\n\n孤立森林分<b>训练</b>和<b>异常评估</b>两部分:\n\n- <b>训练:</b> 根据样本抽样构建多棵iTree,形成孤立森林\n- <b>异常评估:</b> 根据训练过程构建的孤立森林,计算待评估值的异常得分\n\n## 训练\n1. 给定训练数据集$X$,确定需要构建的孤立树(iTree)个数t,按$\\phi$采样大小随机取样作为子样本集$X^{'}$\n2. 在子样本集$X^{'}$上构建一棵孤立树(iTree),过程如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220508001729.png\" width=\"80%\" height=\"20%\">\n\na) 在$X$中随机选择一个属性(维度),在当前样本数据范围内,随机产生一个分割点$p$(介于当前维度最大和最小值之间)\n\nb) 此切割点即是一个超平面,将当前节点数据空间切分成2个子空间:将当前所选维度下小于p点的放在当前节点左分支,把大于p点的放在当前节点的右分支\n\nc) 在节点的左分支和右分支递归执行步骤a,b,不断构造新的叶子节点,直到叶子节点上只有一个数据或者树已经生长到了限制的高度\n\nd) 单棵iTree构建完成\n\n3. 按2的过程,依次构建t棵iTree,得到孤立森林\n\n<img src=\"https://images.bumpchicken.cn/img/20220508003153.png\" width=\"80%\" height=\"30%\">\n\n\n## 异常评估\n\n构建了孤立森林(IForest)后,可以评估某个点$x$的异常得分,用到如下公式:\n\n$$s(x,n)=2^{-\\frac{E(h(x))}{c(n}} $$\n\n其中,$h(x)$ 表示$x$在某棵孤立树中的路径长度,$E(h(x))$ 表示在所有孤立树中的期望路径长度。$c(n)$ 为样本数为n时的二叉排序树(BST)的平均搜索路径长度,用来对样本$x$的期望路径长度做归一化处理。$c(n)$公式如下:\n\n$$c(n)=2H(n-1)-(2(n-1)/n)$$\n\n其中,$H(i)$是一个调和数,约等于 $ln(i) + \\gamma$,$\\gamma$为欧拉常数,约等于0.5772156649\n\n\n论文对于异常得分分布有如下结论:\n\n1. 如果异常得分接近1,那么一定是异常点\n\n2. 如果异常得分远小于0.5, 那么一定不是异常点\n\n3. 如果样本点的异常得分均在0.5左右,那么样本中可能不存在异常点\n\n异常得分$s$和$E(h(x))$关系图如下所示\n\n<img src=\"https://images.bumpchicken.cn/img/20220508010609.png\" width=\"50%\" height=\"30%\">\n\n\n异常得分的等高线图如下所示,通常潜在的异常点$s>=0.6$\n<img src=\"http://images.bumpchicken.cn/img/20220508010909.png\" width=\"50%\" height=\"30%\">\n\n## 参考资料\n\n1.Liu F T, Ting K M, Zhou Z H. Isolation forest[C]//2008 Eighth IEEE International Conference on Data Mining. IEEE, 2008: 413-422.\n","slug":"IsolationTree","published":1,"updated":"2022-05-08T15:39:32.109Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl39lom8q0008otudgvwt3cjw","content":"<p><img src=\"https://images.bumpchicken.cn/img/tree.png\" /></p>\n<p>孤立森林(Isolation\nForest)是周志华团队于2008年提出的一种具有线性复杂度的异常检测算法,被工业界广泛应用于诸如异常流量检测,金融欺诈行为检测等场景。</p>\n<span id=\"more\"></span>\n<h2 id=\"算法原理\">算法原理</h2>\n<p>异常检测领域,通常是正常的样本占大多数,离群点占绝少数,因此大多数异常检测算法的基本思想都是对正常点构建模型,然后根据规则识别出不属于正常点模型的离群点,比较典型的算法有One\nClass SVM(OCSVM), Local Outlier\nFactor(LOF)。和多数异常检测算法不同,孤立森林采用了一种较为高效的异常发现算法,其思路很朴素,但也足够直观有效。</p>\n<p>考虑以下场景,一个二维平面上零零散散分布着一些点,随机使用分割线对其进行分割,直至所有但点都不可再划分(即被孤立了)。直观上来讲,可以发现那些密度很高的簇需要被切割很多次才会停止切割,但是密度很低的点很快就会停止切割到某个子空间了。</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220424235501.png\" width=\"90%\" height=\"50%\"></p>\n<p>孤立森林分<b>训练</b>和<b>异常评估</b>两部分:</p>\n<ul>\n<li><b>训练:</b> 根据样本抽样构建多棵iTree,形成孤立森林</li>\n<li><b>异常评估:</b>\n根据训练过程构建的孤立森林,计算待评估值的异常得分</li>\n</ul>\n<h2 id=\"训练\">训练</h2>\n<ol type=\"1\">\n<li>给定训练数据集<span\nclass=\"math inline\">\\(X\\)</span>,确定需要构建的孤立树(iTree)个数t,按<span\nclass=\"math inline\">\\(\\phi\\)</span>采样大小随机取样作为子样本集<span\nclass=\"math inline\">\\(X^{'}\\)</span></li>\n<li>在子样本集<span\nclass=\"math inline\">\\(X^{'}\\)</span>上构建一棵孤立树(iTree),过程如下图所示:</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508001729.png\" width=\"80%\" height=\"20%\"></p>\n<ol type=\"a\">\n<li><p>在<span\nclass=\"math inline\">\\(X\\)</span>中随机选择一个属性(维度),在当前样本数据范围内,随机产生一个分割点<span\nclass=\"math inline\">\\(p\\)</span>(介于当前维度最大和最小值之间)</p></li>\n<li><p>此切割点即是一个超平面,将当前节点数据空间切分成2个子空间:将当前所选维度下小于p点的放在当前节点左分支,把大于p点的放在当前节点的右分支</p></li>\n<li><p>在节点的左分支和右分支递归执行步骤a,b,不断构造新的叶子节点,直到叶子节点上只有一个数据或者树已经生长到了限制的高度</p></li>\n<li><p>单棵iTree构建完成</p></li>\n</ol>\n<ol start=\"3\" type=\"1\">\n<li>按2的过程,依次构建t棵iTree,得到孤立森林</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508003153.png\" width=\"80%\" height=\"30%\"></p>\n<h2 id=\"异常评估\">异常评估</h2>\n<p>构建了孤立森林(IForest)后,可以评估某个点<span\nclass=\"math inline\">\\(x\\)</span>的异常得分,用到如下公式:</p>\n<p><span class=\"math display\">\\[s(x,n)=2^{-\\frac{E(h(x))}{c(n}}\n\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(h(x)\\)</span> 表示<span\nclass=\"math inline\">\\(x\\)</span>在某棵孤立树中的路径长度,<span\nclass=\"math inline\">\\(E(h(x))\\)</span>\n表示在所有孤立树中的期望路径长度。<span\nclass=\"math inline\">\\(c(n)\\)</span>\n为样本数为n时的二叉排序树(BST)的平均搜索路径长度,用来对样本<span\nclass=\"math inline\">\\(x\\)</span>的期望路径长度做归一化处理。<span\nclass=\"math inline\">\\(c(n)\\)</span>公式如下:</p>\n<p><span class=\"math display\">\\[c(n)=2H(n-1)-(2(n-1)/n)\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(H(i)\\)</span>是一个调和数,约等于\n<span class=\"math inline\">\\(ln(i) + \\gamma\\)</span>,<span\nclass=\"math inline\">\\(\\gamma\\)</span>为欧拉常数,约等于0.5772156649</p>\n<p>论文对于异常得分分布有如下结论:</p>\n<ol type=\"1\">\n<li><p>如果异常得分接近1,那么一定是异常点</p></li>\n<li><p>如果异常得分远小于0.5, 那么一定不是异常点</p></li>\n<li><p>如果样本点的异常得分均在0.5左右,那么样本中可能不存在异常点</p></li>\n</ol>\n<p>异常得分<span class=\"math inline\">\\(s\\)</span>和<span\nclass=\"math inline\">\\(E(h(x))\\)</span>关系图如下所示</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508010609.png\" width=\"50%\" height=\"30%\"></p>\n<p>异常得分的等高线图如下所示,通常潜在的异常点<span\nclass=\"math inline\">\\(s>=0.6\\)</span>\n<img src=\"http://images.bumpchicken.cn/img/20220508010909.png\" width=\"50%\" height=\"30%\"></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<p>1.Liu F T, Ting K M, Zhou Z H. Isolation forest[C]//2008 Eighth IEEE\nInternational Conference on Data Mining. IEEE, 2008: 413-422.</p>\n","site":{"data":{}},"excerpt":"<p><img src=\"https://images.bumpchicken.cn/img/tree.png\" /></p>\n<p>孤立森林(Isolation\nForest)是周志华团队于2008年提出的一种具有线性复杂度的异常检测算法,被工业界广泛应用于诸如异常流量检测,金融欺诈行为检测等场景。</p>","more":"<h2 id=\"算法原理\">算法原理</h2>\n<p>异常检测领域,通常是正常的样本占大多数,离群点占绝少数,因此大多数异常检测算法的基本思想都是对正常点构建模型,然后根据规则识别出不属于正常点模型的离群点,比较典型的算法有One\nClass SVM(OCSVM), Local Outlier\nFactor(LOF)。和多数异常检测算法不同,孤立森林采用了一种较为高效的异常发现算法,其思路很朴素,但也足够直观有效。</p>\n<p>考虑以下场景,一个二维平面上零零散散分布着一些点,随机使用分割线对其进行分割,直至所有但点都不可再划分(即被孤立了)。直观上来讲,可以发现那些密度很高的簇需要被切割很多次才会停止切割,但是密度很低的点很快就会停止切割到某个子空间了。</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220424235501.png\" width=\"90%\" height=\"50%\"></p>\n<p>孤立森林分<b>训练</b>和<b>异常评估</b>两部分:</p>\n<ul>\n<li><b>训练:</b> 根据样本抽样构建多棵iTree,形成孤立森林</li>\n<li><b>异常评估:</b>\n根据训练过程构建的孤立森林,计算待评估值的异常得分</li>\n</ul>\n<h2 id=\"训练\">训练</h2>\n<ol type=\"1\">\n<li>给定训练数据集<span\nclass=\"math inline\">\\(X\\)</span>,确定需要构建的孤立树(iTree)个数t,按<span\nclass=\"math inline\">\\(\\phi\\)</span>采样大小随机取样作为子样本集<span\nclass=\"math inline\">\\(X^{'}\\)</span></li>\n<li>在子样本集<span\nclass=\"math inline\">\\(X^{'}\\)</span>上构建一棵孤立树(iTree),过程如下图所示:</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508001729.png\" width=\"80%\" height=\"20%\"></p>\n<ol type=\"a\">\n<li><p>在<span\nclass=\"math inline\">\\(X\\)</span>中随机选择一个属性(维度),在当前样本数据范围内,随机产生一个分割点<span\nclass=\"math inline\">\\(p\\)</span>(介于当前维度最大和最小值之间)</p></li>\n<li><p>此切割点即是一个超平面,将当前节点数据空间切分成2个子空间:将当前所选维度下小于p点的放在当前节点左分支,把大于p点的放在当前节点的右分支</p></li>\n<li><p>在节点的左分支和右分支递归执行步骤a,b,不断构造新的叶子节点,直到叶子节点上只有一个数据或者树已经生长到了限制的高度</p></li>\n<li><p>单棵iTree构建完成</p></li>\n</ol>\n<ol start=\"3\" type=\"1\">\n<li>按2的过程,依次构建t棵iTree,得到孤立森林</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508003153.png\" width=\"80%\" height=\"30%\"></p>\n<h2 id=\"异常评估\">异常评估</h2>\n<p>构建了孤立森林(IForest)后,可以评估某个点<span\nclass=\"math inline\">\\(x\\)</span>的异常得分,用到如下公式:</p>\n<p><span class=\"math display\">\\[s(x,n)=2^{-\\frac{E(h(x))}{c(n}}\n\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(h(x)\\)</span> 表示<span\nclass=\"math inline\">\\(x\\)</span>在某棵孤立树中的路径长度,<span\nclass=\"math inline\">\\(E(h(x))\\)</span>\n表示在所有孤立树中的期望路径长度。<span\nclass=\"math inline\">\\(c(n)\\)</span>\n为样本数为n时的二叉排序树(BST)的平均搜索路径长度,用来对样本<span\nclass=\"math inline\">\\(x\\)</span>的期望路径长度做归一化处理。<span\nclass=\"math inline\">\\(c(n)\\)</span>公式如下:</p>\n<p><span class=\"math display\">\\[c(n)=2H(n-1)-(2(n-1)/n)\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(H(i)\\)</span>是一个调和数,约等于\n<span class=\"math inline\">\\(ln(i) + \\gamma\\)</span>,<span\nclass=\"math inline\">\\(\\gamma\\)</span>为欧拉常数,约等于0.5772156649</p>\n<p>论文对于异常得分分布有如下结论:</p>\n<ol type=\"1\">\n<li><p>如果异常得分接近1,那么一定是异常点</p></li>\n<li><p>如果异常得分远小于0.5, 那么一定不是异常点</p></li>\n<li><p>如果样本点的异常得分均在0.5左右,那么样本中可能不存在异常点</p></li>\n</ol>\n<p>异常得分<span class=\"math inline\">\\(s\\)</span>和<span\nclass=\"math inline\">\\(E(h(x))\\)</span>关系图如下所示</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508010609.png\" width=\"50%\" height=\"30%\"></p>\n<p>异常得分的等高线图如下所示,通常潜在的异常点<span\nclass=\"math inline\">\\(s>=0.6\\)</span>\n<img src=\"http://images.bumpchicken.cn/img/20220508010909.png\" width=\"50%\" height=\"30%\"></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<p>1.Liu F T, Ting K M, Zhou Z H. Isolation forest[C]//2008 Eighth IEEE\nInternational Conference on Data Mining. IEEE, 2008: 413-422.</p>"},{"title":"MicroCause论文阅读笔记","date":"2020-09-07T09:30:00.000Z","mathjax":true,"_content":"\n该论文是清华大学NetMan实验室2020年发表于IWQoS上的一篇关于微服务根因定位的论文。论文题目是《Localizing Failure Root Causes in a Microservice through Causality Inference》/ 通过根因推理对微服务进行根因定位。论文提出一种基于改进的PC算法和随机游走的根因定位算法\n\n<!--more-->\n\n## 问题定义\n\n### 典型的微服务架构\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004031.png\" width=\"60%\">\n\n如上是一个折扣券服务,该服务会有上游和下游的依赖中间件、服务等。运维人员会对每个微服务的状态进行监控,生成一些指标,如响应时间(RT),QPS,CPU利用率等。当某个故障发生时,体现在这些指标上就是一系列异常抖动\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004250.png\" width=\"60%\">\n\n如上是一个故障,W-QPS指标导致了这次case,为了找到如W-QPS这种根因指标,论文提出了一种根因定位方法:MicroCause\n\n### 面临的挑战与解决思路\n\n通常解决上述根因定位问题基本思路是:首先构建各个组件之间的依赖关系图然后通过随机游走(random walk)的方式寻找根因节点。此方法面临两个主要问题:\n\n1. __传统因果图构造方法适用于独立且均匀分布的数据,无法充分利用传播延迟__\n2. __当前随机游走方法基于这样的假设:与KPI异常相关性更高的指标更有可能是根因指标,但是实际场景中并不总是如此__\n\n为了解决上述问题,论文提供了以下解决方法:\n\n- 设计一种新的因果图构造方法:__PCTS(path condition time series)__。首先通过改进的PC算法对时序数据学习得到一个因果图,然后对节点进行边(edge)的生成\n\n- 设计一种新的随机游走方法:__TCORW(temporal cause oriented random walk)__。在TCORW中,主要利用了三类信息:\n\n 1. 指标间的因果关系\n\n 2. 指标当前是否异常以及异常程度\n 3. 指标的优先级(通过先验经验确定)\n\n## 因果图生成算法与随机游走算法\n\n### PC算法\n\n由Peter Spirtes 和Clark Glymour提出的**PC算法**是当前比较广泛使用的构造因果图的算法,PC算法旨在了解随机变量之间的因果关系。假设我们准备学习M个随机变量的因果图,输入是N个独立的均匀分布的样本,每个样本包含M个值,分别代表M个随机变量的观测值。PC算法将输出具有M个节点的有向无环图(DAG),其中每个节点代表一个随机变量(每个时间序列视为一个随机变量,并将每个时间点的数据视为样本)。PC算法基于以下假设:变量之间没有边(相互独立)。给定变量集S,A独立于B,表示为A⊥B,PC算法包含下列步骤:\n\n1. 构建一个M个随机变量的完全连接图。\n2. 对每个相邻变量在显著性水平α下进行条件独立性测试。如果存在条件独立性,则删除两个变量之间的边。在这个步骤中,条件变量集S的大小逐步增加,直到没有更多的变量可以添加到S为止。\n3. 根据v-structure确定某些边的方向\n4. 确定其余边的方向\n\n## 随机游走算法\n\n随机游走算法可以在各种类型的空间(图形、向量等)执行随机游走,通常包含下列步骤:\n\n1. 生成关系图G,其中V是节点集,E是边集\n\n2. 计算矩阵Q\n\n a)前向游走(从结果节点到根因节点):特别地,我们假定异常节点更可能是根因节点,因此\n\n $$Q_{ij} = R(V_{abnormal}, v_{j})$$\n\n 其中$R(v_{abnormal}, v{j})$是$v_{abnormal}$和$v_{j}$的相关系数\n\n b) 后向游走(从根因节点到结果节点):此步是为了避免算法陷入与异常节点相关性较低的节点\n\n $$Q_{ji} = \\rho R(v_{abnormal, v_{i}})$$\n\n 其中$\\rho \\in [0, 1]$\n\n c) 原地游走(从源节点到源节点):如果算法走到的节点都与异常节点相关性较低,则该点可能表示根本原因\n\n $$Q_{ii} = max[0, R(v_{abnormal}, v_{i}) - \\max _{k: e_{k i} \\in E} R\\left(v_{a b n o r m a l}, v_{k}\\right)]$$\n\n3. 归一化Q的每一行\n\n $$\\bar{Q}_{i j}=\\frac{Q_{i j}}{\\sum_{j} Q_{i j}}$$\n\n4. 在G上进行随机游走,从$v_{i}$到$v_{j}$的概率是$Q_{ij}$\n\n通过以上四个步骤,被访问最频繁的节点就是根因节点\n\n## MicroCause\n\n论文提出了一种MicroCause算法,下图是其整体结构:\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004449.png\">\n\n 当某个KPI检测到异常时,MicroCause会被启动进行根因分析。所有相关指标最近4h的数据将作为MicroCause的输入,通过PCTS生成因果图,以及检测相关指标是否异常和异常程度,然后通过TCORW进行根因分析,给出TOP N的根因节点\n\n### PCTS\n\n对于一个故障X,给定一个数据集$\\mathbf{I}_{t}^{i}, t=0, \\ldots, T, i=1, \\ldots, N$即N个时间序列,每个时间序列长度为T。定义最大的时间延迟$\\tau_{\\max }$即如果我们想找到$I_{t}^{i}$的根因时,$I_{t}$到$I_{t-\\tau _{max}}$的时间序列会被使用到,利用滑动窗口对样本进行独立性测试:\n\n1. 提取每个时间点最大延时时间内的数据, 生成一个父集合$\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)=\\left(\\mathbf{I}_{t-1}, \\ldots, \\mathbf{I}_{t-\\tau_{\\max }}\\right)$\n2. 和PC算法类似,对$\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)$中每个时间点进行独立性测试,如果某个点不满足置信度$\\alpha_{I P C}$,要求,则将其从$\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)$中移除\n3. 构建出以下结构的图G,每个指标每个时间点都是一个节点,下面的图还不能用来定位,需要将其转化为以指标为节点因果图的DAG(Fig 2),但是由于有了延迟传播的概念,因果图将更加符合实际情况。\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004554.png\" width=\"60%\">\n\n## TCORW\n\n包含三个步骤:\n\n**Step1: 面向根因的随机游走**\n\n传统随机游走过程中,通常使用相关性来量化指标与异常指标的关系,相关研究表明:相关并不等于因果关系。因为相关关系无法消除第三个变量的影响(在因果关系研究中称为混杂因素)。 在面向原因的随机游动中,论文通过偏相关来计算矩阵Q,它可以消除混杂因素的影响。我们在随机游动中计算矩阵Q如下:\n\n1)前向游走(从结果节点到根因节点):\n\n$$Q_{i j}=R_{p c}\\left(v_{a k}, v_{j} \\mid P a\\left(v_{a k}\\right) \\backslash v_{j}, P a\\left(v_{j}\\right)\\right)$$\n\n其中,$R_{pc}$代表偏相关,使用了和皮尔逊相关系数相关的算法。$P_{a}(v_{ak})$是$v_{ak}$的父节点集,$P a\\left(v_{a k}\\right) \\backslash v_{j}$表示$v_{j}$将从$v_{ak}$的父节点集中删除。我们将$P a\\left(v_{a k}\\right) \\backslash v_{j}$和$Pa(v_{j})$作为偏相关的混杂因素\n\n2)后向游走(从根因节点到结果节点)\n\n$$Q_{j i}=\\rho R_{p c}\\left(v_{a k}, v_{i} \\mid P a\\left(v_{a k}\\right) \\backslash v_{i}, P a\\left(v_{i}\\right)\\right)$$\n\n其中,$\\rho$是用来控制后向游走的权重系数,$\\rho \\in [0, 1]$\n\n3)原地游走\n\n$$\\begin{array}{l}\nQ_{i i}=\\max \\left[0, R_{p c}\\left(v_{a k}, v_{i} \\mid P a\\left(v_{a k}\\right) \\backslash v_{i}, P a\\left(v_{i}\\right)\\right)-P_{p c}^{m a x}\\right] \\\\\nP_{p c}^{\\max }=\\max _{k: e_{k i} \\in E} R_{p c}\\left(v_{a k}, v_{k} \\mid P a\\left(v_{a k}\\right) \\backslash v_{k}, P a\\left(v_{k}\\right)\\right)\n\\end{array}$$\n\n同样地,归一化矩阵Q:\n\n$$\\bar{Q}_{i j}=\\frac{Q_{i j}}{\\sum_{j} Q_{i j}}$$\n\n**Step 2 潜在根因节点打分**\n\n定义指标的根因节点得分公式如下:\n\n$$\\gamma_{i}=\\lambda \\bar{c}_{i}+(1-\\lambda) \\bar{\\eta}_{\\max }^{i}$$\n\n其中,$\\bar{c}_{i}$是在随机游走中被访问的次数(标准化过的),$\\bar{\\eta}_{\\max }^{i}$是指标的异常程度(经标准化过的),$\\lambda$是用来控制两者权重的系数,$\\lambda \\in [0,1]$\n\n**Step 3 根因节点排序**\n\n把指标分成了三类,如下表所示,通常高级别的指标会影响低级别的指标,因此,在确定根因节点时高级别指标会拥有更高的权重系数。\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004925.png\">\n\n论文考虑了指标的优先级、指标的异常时间,指标的异常根因得分,提出了一个根因节点排序算法,如下:\n\n<img src=\"https://images.bumpchicken.cn/img/20220515005008.png\" width=\"60%\">\n\n即级别更高的指标、更加异常的指标,更早发生异常的指标更有可能会被认为是根因节点\n\n## 算法结果验证\n\n与相关算法对比\n\n<img src=\"https://images.bumpchicken.cn/img/20220515005103.png\" width=\"80%\">\n\n## 参考资料\n\n1.Meng Y, Zhang S, Sun Y, et al. Localizing failure root causes in a microservice through causality inference[C]//2020 IEEE/ACM 28th International Symposium on Quality of Service (IWQoS). IEEE, 2020: 1-10.\n\n","source":"_posts/MicroCause.md","raw":"\ntitle: MicroCause论文阅读笔记\n\ndate: 2020-09-07 17:30:00\n\ntags: \n - 根因定位\n\ncategories:\n - 论文阅读笔记\n\nmathjax: true\n\n---\n\n该论文是清华大学NetMan实验室2020年发表于IWQoS上的一篇关于微服务根因定位的论文。论文题目是《Localizing Failure Root Causes in a Microservice through Causality Inference》/ 通过根因推理对微服务进行根因定位。论文提出一种基于改进的PC算法和随机游走的根因定位算法\n\n<!--more-->\n\n## 问题定义\n\n### 典型的微服务架构\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004031.png\" width=\"60%\">\n\n如上是一个折扣券服务,该服务会有上游和下游的依赖中间件、服务等。运维人员会对每个微服务的状态进行监控,生成一些指标,如响应时间(RT),QPS,CPU利用率等。当某个故障发生时,体现在这些指标上就是一系列异常抖动\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004250.png\" width=\"60%\">\n\n如上是一个故障,W-QPS指标导致了这次case,为了找到如W-QPS这种根因指标,论文提出了一种根因定位方法:MicroCause\n\n### 面临的挑战与解决思路\n\n通常解决上述根因定位问题基本思路是:首先构建各个组件之间的依赖关系图然后通过随机游走(random walk)的方式寻找根因节点。此方法面临两个主要问题:\n\n1. __传统因果图构造方法适用于独立且均匀分布的数据,无法充分利用传播延迟__\n2. __当前随机游走方法基于这样的假设:与KPI异常相关性更高的指标更有可能是根因指标,但是实际场景中并不总是如此__\n\n为了解决上述问题,论文提供了以下解决方法:\n\n- 设计一种新的因果图构造方法:__PCTS(path condition time series)__。首先通过改进的PC算法对时序数据学习得到一个因果图,然后对节点进行边(edge)的生成\n\n- 设计一种新的随机游走方法:__TCORW(temporal cause oriented random walk)__。在TCORW中,主要利用了三类信息:\n\n 1. 指标间的因果关系\n\n 2. 指标当前是否异常以及异常程度\n 3. 指标的优先级(通过先验经验确定)\n\n## 因果图生成算法与随机游走算法\n\n### PC算法\n\n由Peter Spirtes 和Clark Glymour提出的**PC算法**是当前比较广泛使用的构造因果图的算法,PC算法旨在了解随机变量之间的因果关系。假设我们准备学习M个随机变量的因果图,输入是N个独立的均匀分布的样本,每个样本包含M个值,分别代表M个随机变量的观测值。PC算法将输出具有M个节点的有向无环图(DAG),其中每个节点代表一个随机变量(每个时间序列视为一个随机变量,并将每个时间点的数据视为样本)。PC算法基于以下假设:变量之间没有边(相互独立)。给定变量集S,A独立于B,表示为A⊥B,PC算法包含下列步骤:\n\n1. 构建一个M个随机变量的完全连接图。\n2. 对每个相邻变量在显著性水平α下进行条件独立性测试。如果存在条件独立性,则删除两个变量之间的边。在这个步骤中,条件变量集S的大小逐步增加,直到没有更多的变量可以添加到S为止。\n3. 根据v-structure确定某些边的方向\n4. 确定其余边的方向\n\n## 随机游走算法\n\n随机游走算法可以在各种类型的空间(图形、向量等)执行随机游走,通常包含下列步骤:\n\n1. 生成关系图G,其中V是节点集,E是边集\n\n2. 计算矩阵Q\n\n a)前向游走(从结果节点到根因节点):特别地,我们假定异常节点更可能是根因节点,因此\n\n $$Q_{ij} = R(V_{abnormal}, v_{j})$$\n\n 其中$R(v_{abnormal}, v{j})$是$v_{abnormal}$和$v_{j}$的相关系数\n\n b) 后向游走(从根因节点到结果节点):此步是为了避免算法陷入与异常节点相关性较低的节点\n\n $$Q_{ji} = \\rho R(v_{abnormal, v_{i}})$$\n\n 其中$\\rho \\in [0, 1]$\n\n c) 原地游走(从源节点到源节点):如果算法走到的节点都与异常节点相关性较低,则该点可能表示根本原因\n\n $$Q_{ii} = max[0, R(v_{abnormal}, v_{i}) - \\max _{k: e_{k i} \\in E} R\\left(v_{a b n o r m a l}, v_{k}\\right)]$$\n\n3. 归一化Q的每一行\n\n $$\\bar{Q}_{i j}=\\frac{Q_{i j}}{\\sum_{j} Q_{i j}}$$\n\n4. 在G上进行随机游走,从$v_{i}$到$v_{j}$的概率是$Q_{ij}$\n\n通过以上四个步骤,被访问最频繁的节点就是根因节点\n\n## MicroCause\n\n论文提出了一种MicroCause算法,下图是其整体结构:\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004449.png\">\n\n 当某个KPI检测到异常时,MicroCause会被启动进行根因分析。所有相关指标最近4h的数据将作为MicroCause的输入,通过PCTS生成因果图,以及检测相关指标是否异常和异常程度,然后通过TCORW进行根因分析,给出TOP N的根因节点\n\n### PCTS\n\n对于一个故障X,给定一个数据集$\\mathbf{I}_{t}^{i}, t=0, \\ldots, T, i=1, \\ldots, N$即N个时间序列,每个时间序列长度为T。定义最大的时间延迟$\\tau_{\\max }$即如果我们想找到$I_{t}^{i}$的根因时,$I_{t}$到$I_{t-\\tau _{max}}$的时间序列会被使用到,利用滑动窗口对样本进行独立性测试:\n\n1. 提取每个时间点最大延时时间内的数据, 生成一个父集合$\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)=\\left(\\mathbf{I}_{t-1}, \\ldots, \\mathbf{I}_{t-\\tau_{\\max }}\\right)$\n2. 和PC算法类似,对$\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)$中每个时间点进行独立性测试,如果某个点不满足置信度$\\alpha_{I P C}$,要求,则将其从$\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)$中移除\n3. 构建出以下结构的图G,每个指标每个时间点都是一个节点,下面的图还不能用来定位,需要将其转化为以指标为节点因果图的DAG(Fig 2),但是由于有了延迟传播的概念,因果图将更加符合实际情况。\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004554.png\" width=\"60%\">\n\n## TCORW\n\n包含三个步骤:\n\n**Step1: 面向根因的随机游走**\n\n传统随机游走过程中,通常使用相关性来量化指标与异常指标的关系,相关研究表明:相关并不等于因果关系。因为相关关系无法消除第三个变量的影响(在因果关系研究中称为混杂因素)。 在面向原因的随机游动中,论文通过偏相关来计算矩阵Q,它可以消除混杂因素的影响。我们在随机游动中计算矩阵Q如下:\n\n1)前向游走(从结果节点到根因节点):\n\n$$Q_{i j}=R_{p c}\\left(v_{a k}, v_{j} \\mid P a\\left(v_{a k}\\right) \\backslash v_{j}, P a\\left(v_{j}\\right)\\right)$$\n\n其中,$R_{pc}$代表偏相关,使用了和皮尔逊相关系数相关的算法。$P_{a}(v_{ak})$是$v_{ak}$的父节点集,$P a\\left(v_{a k}\\right) \\backslash v_{j}$表示$v_{j}$将从$v_{ak}$的父节点集中删除。我们将$P a\\left(v_{a k}\\right) \\backslash v_{j}$和$Pa(v_{j})$作为偏相关的混杂因素\n\n2)后向游走(从根因节点到结果节点)\n\n$$Q_{j i}=\\rho R_{p c}\\left(v_{a k}, v_{i} \\mid P a\\left(v_{a k}\\right) \\backslash v_{i}, P a\\left(v_{i}\\right)\\right)$$\n\n其中,$\\rho$是用来控制后向游走的权重系数,$\\rho \\in [0, 1]$\n\n3)原地游走\n\n$$\\begin{array}{l}\nQ_{i i}=\\max \\left[0, R_{p c}\\left(v_{a k}, v_{i} \\mid P a\\left(v_{a k}\\right) \\backslash v_{i}, P a\\left(v_{i}\\right)\\right)-P_{p c}^{m a x}\\right] \\\\\nP_{p c}^{\\max }=\\max _{k: e_{k i} \\in E} R_{p c}\\left(v_{a k}, v_{k} \\mid P a\\left(v_{a k}\\right) \\backslash v_{k}, P a\\left(v_{k}\\right)\\right)\n\\end{array}$$\n\n同样地,归一化矩阵Q:\n\n$$\\bar{Q}_{i j}=\\frac{Q_{i j}}{\\sum_{j} Q_{i j}}$$\n\n**Step 2 潜在根因节点打分**\n\n定义指标的根因节点得分公式如下:\n\n$$\\gamma_{i}=\\lambda \\bar{c}_{i}+(1-\\lambda) \\bar{\\eta}_{\\max }^{i}$$\n\n其中,$\\bar{c}_{i}$是在随机游走中被访问的次数(标准化过的),$\\bar{\\eta}_{\\max }^{i}$是指标的异常程度(经标准化过的),$\\lambda$是用来控制两者权重的系数,$\\lambda \\in [0,1]$\n\n**Step 3 根因节点排序**\n\n把指标分成了三类,如下表所示,通常高级别的指标会影响低级别的指标,因此,在确定根因节点时高级别指标会拥有更高的权重系数。\n\n<img src=\"https://images.bumpchicken.cn/img/20220515004925.png\">\n\n论文考虑了指标的优先级、指标的异常时间,指标的异常根因得分,提出了一个根因节点排序算法,如下:\n\n<img src=\"https://images.bumpchicken.cn/img/20220515005008.png\" width=\"60%\">\n\n即级别更高的指标、更加异常的指标,更早发生异常的指标更有可能会被认为是根因节点\n\n## 算法结果验证\n\n与相关算法对比\n\n<img src=\"https://images.bumpchicken.cn/img/20220515005103.png\" width=\"80%\">\n\n## 参考资料\n\n1.Meng Y, Zhang S, Sun Y, et al. Localizing failure root causes in a microservice through causality inference[C]//2020 IEEE/ACM 28th International Symposium on Quality of Service (IWQoS). IEEE, 2020: 1-10.\n\n","slug":"MicroCause","published":1,"updated":"2022-05-17T03:22:43.041Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl39lom8r0009otudhi5y2ps5","content":"<p>该论文是清华大学NetMan实验室2020年发表于IWQoS上的一篇关于微服务根因定位的论文。论文题目是《Localizing\nFailure Root Causes in a Microservice through Causality Inference》/\n通过根因推理对微服务进行根因定位。论文提出一种基于改进的PC算法和随机游走的根因定位算法</p>\n<span id=\"more\"></span>\n<h2 id=\"问题定义\">问题定义</h2>\n<h3 id=\"典型的微服务架构\">典型的微服务架构</h3>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004031.png\" width=\"60%\"></p>\n<p>如上是一个折扣券服务,该服务会有上游和下游的依赖中间件、服务等。运维人员会对每个微服务的状态进行监控,生成一些指标,如响应时间(RT),QPS,CPU利用率等。当某个故障发生时,体现在这些指标上就是一系列异常抖动</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004250.png\" width=\"60%\"></p>\n<p>如上是一个故障,W-QPS指标导致了这次case,为了找到如W-QPS这种根因指标,论文提出了一种根因定位方法:MicroCause</p>\n<h3 id=\"面临的挑战与解决思路\">面临的挑战与解决思路</h3>\n<p>通常解决上述根因定位问题基本思路是:首先构建各个组件之间的依赖关系图然后通过随机游走(random\nwalk)的方式寻找根因节点。此方法面临两个主要问题:</p>\n<ol type=\"1\">\n<li><strong>传统因果图构造方法适用于独立且均匀分布的数据,无法充分利用传播延迟</strong></li>\n<li><strong>当前随机游走方法基于这样的假设:与KPI异常相关性更高的指标更有可能是根因指标,但是实际场景中并不总是如此</strong></li>\n</ol>\n<p>为了解决上述问题,论文提供了以下解决方法:</p>\n<ul>\n<li><p>设计一种新的因果图构造方法:<strong>PCTS(path condition time\nseries)</strong>。首先通过改进的PC算法对时序数据学习得到一个因果图,然后对节点进行边(edge)的生成</p></li>\n<li><p>设计一种新的随机游走方法:<strong>TCORW(temporal cause oriented\nrandom walk)</strong>。在TCORW中,主要利用了三类信息:</p>\n<ol type=\"1\">\n<li><p>指标间的因果关系</p></li>\n<li><p>指标当前是否异常以及异常程度</p></li>\n<li><p>指标的优先级(通过先验经验确定)</p></li>\n</ol></li>\n</ul>\n<h2 id=\"因果图生成算法与随机游走算法\">因果图生成算法与随机游走算法</h2>\n<h3 id=\"pc算法\">PC算法</h3>\n<p>由Peter Spirtes 和Clark\nGlymour提出的<strong>PC算法</strong>是当前比较广泛使用的构造因果图的算法,PC算法旨在了解随机变量之间的因果关系。假设我们准备学习M个随机变量的因果图,输入是N个独立的均匀分布的样本,每个样本包含M个值,分别代表M个随机变量的观测值。PC算法将输出具有M个节点的有向无环图(DAG),其中每个节点代表一个随机变量(每个时间序列视为一个随机变量,并将每个时间点的数据视为样本)。PC算法基于以下假设:变量之间没有边(相互独立)。给定变量集S,A独立于B,表示为A⊥B,PC算法包含下列步骤:</p>\n<ol type=\"1\">\n<li>构建一个M个随机变量的完全连接图。</li>\n<li>对每个相邻变量在显著性水平α下进行条件独立性测试。如果存在条件独立性,则删除两个变量之间的边。在这个步骤中,条件变量集S的大小逐步增加,直到没有更多的变量可以添加到S为止。</li>\n<li>根据v-structure确定某些边的方向</li>\n<li>确定其余边的方向</li>\n</ol>\n<h2 id=\"随机游走算法\">随机游走算法</h2>\n<p>随机游走算法可以在各种类型的空间(图形、向量等)执行随机游走,通常包含下列步骤:</p>\n<ol type=\"1\">\n<li><p>生成关系图G,其中V是节点集,E是边集</p></li>\n<li><p>计算矩阵Q</p>\n<p>a)前向游走(从结果节点到根因节点):特别地,我们假定异常节点更可能是根因节点,因此</p>\n<p><span class=\"math display\">\\[Q_{ij} = R(V_{abnormal},\nv_{j})\\]</span></p>\n<p>其中<span class=\"math inline\">\\(R(v_{abnormal}, v{j})\\)</span>是<span\nclass=\"math inline\">\\(v_{abnormal}\\)</span>和<span\nclass=\"math inline\">\\(v_{j}\\)</span>的相关系数</p>\n<ol start=\"2\" type=\"a\">\n<li>后向游走(从根因节点到结果节点):此步是为了避免算法陷入与异常节点相关性较低的节点</li>\n</ol>\n<p><span class=\"math display\">\\[Q_{ji} = \\rho R(v_{abnormal,\nv_{i}})\\]</span></p>\n<p>其中<span class=\"math inline\">\\(\\rho \\in [0, 1]\\)</span></p>\n<ol start=\"3\" type=\"a\">\n<li>原地游走(从源节点到源节点):如果算法走到的节点都与异常节点相关性较低,则该点可能表示根本原因</li>\n</ol>\n<p><span class=\"math display\">\\[Q_{ii} = max[0, R(v_{abnormal}, v_{i}) -\n\\max _{k: e_{k i} \\in E} R\\left(v_{a b n o r m a l},\nv_{k}\\right)]\\]</span></p></li>\n<li><p>归一化Q的每一行</p>\n<p><span class=\"math display\">\\[\\bar{Q}_{i j}=\\frac{Q_{i j}}{\\sum_{j}\nQ_{i j}}\\]</span></p></li>\n<li><p>在G上进行随机游走,从<span\nclass=\"math inline\">\\(v_{i}\\)</span>到<span\nclass=\"math inline\">\\(v_{j}\\)</span>的概率是<span\nclass=\"math inline\">\\(Q_{ij}\\)</span></p></li>\n</ol>\n<p>通过以上四个步骤,被访问最频繁的节点就是根因节点</p>\n<h2 id=\"microcause\">MicroCause</h2>\n<p>论文提出了一种MicroCause算法,下图是其整体结构:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004449.png\"></p>\n<p>当某个KPI检测到异常时,MicroCause会被启动进行根因分析。所有相关指标最近4h的数据将作为MicroCause的输入,通过PCTS生成因果图,以及检测相关指标是否异常和异常程度,然后通过TCORW进行根因分析,给出TOP\nN的根因节点</p>\n<h3 id=\"pcts\">PCTS</h3>\n<p>对于一个故障X,给定一个数据集<span\nclass=\"math inline\">\\(\\mathbf{I}_{t}^{i}, t=0, \\ldots, T, i=1, \\ldots,\nN\\)</span>即N个时间序列,每个时间序列长度为T。定义最大的时间延迟<span\nclass=\"math inline\">\\(\\tau_{\\max }\\)</span>即如果我们想找到<span\nclass=\"math inline\">\\(I_{t}^{i}\\)</span>的根因时,<span\nclass=\"math inline\">\\(I_{t}\\)</span>到<span\nclass=\"math inline\">\\(I_{t-\\tau\n_{max}}\\)</span>的时间序列会被使用到,利用滑动窗口对样本进行独立性测试:</p>\n<ol type=\"1\">\n<li>提取每个时间点最大延时时间内的数据, 生成一个父集合<span\nclass=\"math inline\">\\(\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)=\\left(\\mathbf{I}_{t-1},\n\\ldots, \\mathbf{I}_{t-\\tau_{\\max }}\\right)\\)</span></li>\n<li>和PC算法类似,对<span\nclass=\"math inline\">\\(\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)\\)</span>中每个时间点进行独立性测试,如果某个点不满足置信度<span\nclass=\"math inline\">\\(\\alpha_{I P C}\\)</span>,要求,则将其从<span\nclass=\"math inline\">\\(\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)\\)</span>中移除</li>\n<li>构建出以下结构的图G,每个指标每个时间点都是一个节点,下面的图还不能用来定位,需要将其转化为以指标为节点因果图的DAG(Fig\n2),但是由于有了延迟传播的概念,因果图将更加符合实际情况。</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004554.png\" width=\"60%\"></p>\n<h2 id=\"tcorw\">TCORW</h2>\n<p>包含三个步骤:</p>\n<p><strong>Step1: 面向根因的随机游走</strong></p>\n<p>传统随机游走过程中,通常使用相关性来量化指标与异常指标的关系,相关研究表明:相关并不等于因果关系。因为相关关系无法消除第三个变量的影响(在因果关系研究中称为混杂因素)。\n在面向原因的随机游动中,论文通过偏相关来计算矩阵Q,它可以消除混杂因素的影响。我们在随机游动中计算矩阵Q如下:</p>\n<p>1)前向游走(从结果节点到根因节点):</p>\n<p><span class=\"math display\">\\[Q_{i j}=R_{p c}\\left(v_{a k}, v_{j} \\mid\nP a\\left(v_{a k}\\right) \\backslash v_{j}, P\na\\left(v_{j}\\right)\\right)\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(R_{pc}\\)</span>代表偏相关,使用了和皮尔逊相关系数相关的算法。<span\nclass=\"math inline\">\\(P_{a}(v_{ak})\\)</span>是<span\nclass=\"math inline\">\\(v_{ak}\\)</span>的父节点集,<span\nclass=\"math inline\">\\(P a\\left(v_{a k}\\right) \\backslash\nv_{j}\\)</span>表示<span class=\"math inline\">\\(v_{j}\\)</span>将从<span\nclass=\"math inline\">\\(v_{ak}\\)</span>的父节点集中删除。我们将<span\nclass=\"math inline\">\\(P a\\left(v_{a k}\\right) \\backslash\nv_{j}\\)</span>和<span\nclass=\"math inline\">\\(Pa(v_{j})\\)</span>作为偏相关的混杂因素</p>\n<p>2)后向游走(从根因节点到结果节点)</p>\n<p><span class=\"math display\">\\[Q_{j i}=\\rho R_{p c}\\left(v_{a k}, v_{i}\n\\mid P a\\left(v_{a k}\\right) \\backslash v_{i}, P\na\\left(v_{i}\\right)\\right)\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(\\rho\\)</span>是用来控制后向游走的权重系数,<span\nclass=\"math inline\">\\(\\rho \\in [0, 1]\\)</span></p>\n<p>3)原地游走</p>\n<p><span class=\"math display\">\\[\\begin{array}{l}\nQ_{i i}=\\max \\left[0, R_{p c}\\left(v_{a k}, v_{i} \\mid P a\\left(v_{a\nk}\\right) \\backslash v_{i}, P a\\left(v_{i}\\right)\\right)-P_{p c}^{m a\nx}\\right] \\\\\nP_{p c}^{\\max }=\\max _{k: e_{k i} \\in E} R_{p c}\\left(v_{a k}, v_{k}\n\\mid P a\\left(v_{a k}\\right) \\backslash v_{k}, P\na\\left(v_{k}\\right)\\right)\n\\end{array}\\]</span></p>\n<p>同样地,归一化矩阵Q:</p>\n<p><span class=\"math display\">\\[\\bar{Q}_{i j}=\\frac{Q_{i j}}{\\sum_{j}\nQ_{i j}}\\]</span></p>\n<p><strong>Step 2 潜在根因节点打分</strong></p>\n<p>定义指标的根因节点得分公式如下:</p>\n<p><span class=\"math display\">\\[\\gamma_{i}=\\lambda\n\\bar{c}_{i}+(1-\\lambda) \\bar{\\eta}_{\\max }^{i}\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(\\bar{c}_{i}\\)</span>是在随机游走中被访问的次数(标准化过的),<span\nclass=\"math inline\">\\(\\bar{\\eta}_{\\max\n}^{i}\\)</span>是指标的异常程度(经标准化过的),<span\nclass=\"math inline\">\\(\\lambda\\)</span>是用来控制两者权重的系数,<span\nclass=\"math inline\">\\(\\lambda \\in [0,1]\\)</span></p>\n<p><strong>Step 3 根因节点排序</strong></p>\n<p>把指标分成了三类,如下表所示,通常高级别的指标会影响低级别的指标,因此,在确定根因节点时高级别指标会拥有更高的权重系数。</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004925.png\"></p>\n<p>论文考虑了指标的优先级、指标的异常时间,指标的异常根因得分,提出了一个根因节点排序算法,如下:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515005008.png\" width=\"60%\"></p>\n<p>即级别更高的指标、更加异常的指标,更早发生异常的指标更有可能会被认为是根因节点</p>\n<h2 id=\"算法结果验证\">算法结果验证</h2>\n<p>与相关算法对比</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515005103.png\" width=\"80%\"></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<p>1.Meng Y, Zhang S, Sun Y, et al. Localizing failure root causes in a\nmicroservice through causality inference[C]//2020 IEEE/ACM 28th\nInternational Symposium on Quality of Service (IWQoS). IEEE, 2020:\n1-10.</p>\n","site":{"data":{}},"excerpt":"<p>该论文是清华大学NetMan实验室2020年发表于IWQoS上的一篇关于微服务根因定位的论文。论文题目是《Localizing\nFailure Root Causes in a Microservice through Causality Inference》/\n通过根因推理对微服务进行根因定位。论文提出一种基于改进的PC算法和随机游走的根因定位算法</p>","more":"<h2 id=\"问题定义\">问题定义</h2>\n<h3 id=\"典型的微服务架构\">典型的微服务架构</h3>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004031.png\" width=\"60%\"></p>\n<p>如上是一个折扣券服务,该服务会有上游和下游的依赖中间件、服务等。运维人员会对每个微服务的状态进行监控,生成一些指标,如响应时间(RT),QPS,CPU利用率等。当某个故障发生时,体现在这些指标上就是一系列异常抖动</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004250.png\" width=\"60%\"></p>\n<p>如上是一个故障,W-QPS指标导致了这次case,为了找到如W-QPS这种根因指标,论文提出了一种根因定位方法:MicroCause</p>\n<h3 id=\"面临的挑战与解决思路\">面临的挑战与解决思路</h3>\n<p>通常解决上述根因定位问题基本思路是:首先构建各个组件之间的依赖关系图然后通过随机游走(random\nwalk)的方式寻找根因节点。此方法面临两个主要问题:</p>\n<ol type=\"1\">\n<li><strong>传统因果图构造方法适用于独立且均匀分布的数据,无法充分利用传播延迟</strong></li>\n<li><strong>当前随机游走方法基于这样的假设:与KPI异常相关性更高的指标更有可能是根因指标,但是实际场景中并不总是如此</strong></li>\n</ol>\n<p>为了解决上述问题,论文提供了以下解决方法:</p>\n<ul>\n<li><p>设计一种新的因果图构造方法:<strong>PCTS(path condition time\nseries)</strong>。首先通过改进的PC算法对时序数据学习得到一个因果图,然后对节点进行边(edge)的生成</p></li>\n<li><p>设计一种新的随机游走方法:<strong>TCORW(temporal cause oriented\nrandom walk)</strong>。在TCORW中,主要利用了三类信息:</p>\n<ol type=\"1\">\n<li><p>指标间的因果关系</p></li>\n<li><p>指标当前是否异常以及异常程度</p></li>\n<li><p>指标的优先级(通过先验经验确定)</p></li>\n</ol></li>\n</ul>\n<h2 id=\"因果图生成算法与随机游走算法\">因果图生成算法与随机游走算法</h2>\n<h3 id=\"pc算法\">PC算法</h3>\n<p>由Peter Spirtes 和Clark\nGlymour提出的<strong>PC算法</strong>是当前比较广泛使用的构造因果图的算法,PC算法旨在了解随机变量之间的因果关系。假设我们准备学习M个随机变量的因果图,输入是N个独立的均匀分布的样本,每个样本包含M个值,分别代表M个随机变量的观测值。PC算法将输出具有M个节点的有向无环图(DAG),其中每个节点代表一个随机变量(每个时间序列视为一个随机变量,并将每个时间点的数据视为样本)。PC算法基于以下假设:变量之间没有边(相互独立)。给定变量集S,A独立于B,表示为A⊥B,PC算法包含下列步骤:</p>\n<ol type=\"1\">\n<li>构建一个M个随机变量的完全连接图。</li>\n<li>对每个相邻变量在显著性水平α下进行条件独立性测试。如果存在条件独立性,则删除两个变量之间的边。在这个步骤中,条件变量集S的大小逐步增加,直到没有更多的变量可以添加到S为止。</li>\n<li>根据v-structure确定某些边的方向</li>\n<li>确定其余边的方向</li>\n</ol>\n<h2 id=\"随机游走算法\">随机游走算法</h2>\n<p>随机游走算法可以在各种类型的空间(图形、向量等)执行随机游走,通常包含下列步骤:</p>\n<ol type=\"1\">\n<li><p>生成关系图G,其中V是节点集,E是边集</p></li>\n<li><p>计算矩阵Q</p>\n<p>a)前向游走(从结果节点到根因节点):特别地,我们假定异常节点更可能是根因节点,因此</p>\n<p><span class=\"math display\">\\[Q_{ij} = R(V_{abnormal},\nv_{j})\\]</span></p>\n<p>其中<span class=\"math inline\">\\(R(v_{abnormal}, v{j})\\)</span>是<span\nclass=\"math inline\">\\(v_{abnormal}\\)</span>和<span\nclass=\"math inline\">\\(v_{j}\\)</span>的相关系数</p>\n<ol start=\"2\" type=\"a\">\n<li>后向游走(从根因节点到结果节点):此步是为了避免算法陷入与异常节点相关性较低的节点</li>\n</ol>\n<p><span class=\"math display\">\\[Q_{ji} = \\rho R(v_{abnormal,\nv_{i}})\\]</span></p>\n<p>其中<span class=\"math inline\">\\(\\rho \\in [0, 1]\\)</span></p>\n<ol start=\"3\" type=\"a\">\n<li>原地游走(从源节点到源节点):如果算法走到的节点都与异常节点相关性较低,则该点可能表示根本原因</li>\n</ol>\n<p><span class=\"math display\">\\[Q_{ii} = max[0, R(v_{abnormal}, v_{i}) -\n\\max _{k: e_{k i} \\in E} R\\left(v_{a b n o r m a l},\nv_{k}\\right)]\\]</span></p></li>\n<li><p>归一化Q的每一行</p>\n<p><span class=\"math display\">\\[\\bar{Q}_{i j}=\\frac{Q_{i j}}{\\sum_{j}\nQ_{i j}}\\]</span></p></li>\n<li><p>在G上进行随机游走,从<span\nclass=\"math inline\">\\(v_{i}\\)</span>到<span\nclass=\"math inline\">\\(v_{j}\\)</span>的概率是<span\nclass=\"math inline\">\\(Q_{ij}\\)</span></p></li>\n</ol>\n<p>通过以上四个步骤,被访问最频繁的节点就是根因节点</p>\n<h2 id=\"microcause\">MicroCause</h2>\n<p>论文提出了一种MicroCause算法,下图是其整体结构:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004449.png\"></p>\n<p>当某个KPI检测到异常时,MicroCause会被启动进行根因分析。所有相关指标最近4h的数据将作为MicroCause的输入,通过PCTS生成因果图,以及检测相关指标是否异常和异常程度,然后通过TCORW进行根因分析,给出TOP\nN的根因节点</p>\n<h3 id=\"pcts\">PCTS</h3>\n<p>对于一个故障X,给定一个数据集<span\nclass=\"math inline\">\\(\\mathbf{I}_{t}^{i}, t=0, \\ldots, T, i=1, \\ldots,\nN\\)</span>即N个时间序列,每个时间序列长度为T。定义最大的时间延迟<span\nclass=\"math inline\">\\(\\tau_{\\max }\\)</span>即如果我们想找到<span\nclass=\"math inline\">\\(I_{t}^{i}\\)</span>的根因时,<span\nclass=\"math inline\">\\(I_{t}\\)</span>到<span\nclass=\"math inline\">\\(I_{t-\\tau\n_{max}}\\)</span>的时间序列会被使用到,利用滑动窗口对样本进行独立性测试:</p>\n<ol type=\"1\">\n<li>提取每个时间点最大延时时间内的数据, 生成一个父集合<span\nclass=\"math inline\">\\(\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)=\\left(\\mathbf{I}_{t-1},\n\\ldots, \\mathbf{I}_{t-\\tau_{\\max }}\\right)\\)</span></li>\n<li>和PC算法类似,对<span\nclass=\"math inline\">\\(\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)\\)</span>中每个时间点进行独立性测试,如果某个点不满足置信度<span\nclass=\"math inline\">\\(\\alpha_{I P C}\\)</span>,要求,则将其从<span\nclass=\"math inline\">\\(\\widehat{\\mathcal{P}}\\left(I_{t}^{i}\\right)\\)</span>中移除</li>\n<li>构建出以下结构的图G,每个指标每个时间点都是一个节点,下面的图还不能用来定位,需要将其转化为以指标为节点因果图的DAG(Fig\n2),但是由于有了延迟传播的概念,因果图将更加符合实际情况。</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004554.png\" width=\"60%\"></p>\n<h2 id=\"tcorw\">TCORW</h2>\n<p>包含三个步骤:</p>\n<p><strong>Step1: 面向根因的随机游走</strong></p>\n<p>传统随机游走过程中,通常使用相关性来量化指标与异常指标的关系,相关研究表明:相关并不等于因果关系。因为相关关系无法消除第三个变量的影响(在因果关系研究中称为混杂因素)。\n在面向原因的随机游动中,论文通过偏相关来计算矩阵Q,它可以消除混杂因素的影响。我们在随机游动中计算矩阵Q如下:</p>\n<p>1)前向游走(从结果节点到根因节点):</p>\n<p><span class=\"math display\">\\[Q_{i j}=R_{p c}\\left(v_{a k}, v_{j} \\mid\nP a\\left(v_{a k}\\right) \\backslash v_{j}, P\na\\left(v_{j}\\right)\\right)\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(R_{pc}\\)</span>代表偏相关,使用了和皮尔逊相关系数相关的算法。<span\nclass=\"math inline\">\\(P_{a}(v_{ak})\\)</span>是<span\nclass=\"math inline\">\\(v_{ak}\\)</span>的父节点集,<span\nclass=\"math inline\">\\(P a\\left(v_{a k}\\right) \\backslash\nv_{j}\\)</span>表示<span class=\"math inline\">\\(v_{j}\\)</span>将从<span\nclass=\"math inline\">\\(v_{ak}\\)</span>的父节点集中删除。我们将<span\nclass=\"math inline\">\\(P a\\left(v_{a k}\\right) \\backslash\nv_{j}\\)</span>和<span\nclass=\"math inline\">\\(Pa(v_{j})\\)</span>作为偏相关的混杂因素</p>\n<p>2)后向游走(从根因节点到结果节点)</p>\n<p><span class=\"math display\">\\[Q_{j i}=\\rho R_{p c}\\left(v_{a k}, v_{i}\n\\mid P a\\left(v_{a k}\\right) \\backslash v_{i}, P\na\\left(v_{i}\\right)\\right)\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(\\rho\\)</span>是用来控制后向游走的权重系数,<span\nclass=\"math inline\">\\(\\rho \\in [0, 1]\\)</span></p>\n<p>3)原地游走</p>\n<p><span class=\"math display\">\\[\\begin{array}{l}\nQ_{i i}=\\max \\left[0, R_{p c}\\left(v_{a k}, v_{i} \\mid P a\\left(v_{a\nk}\\right) \\backslash v_{i}, P a\\left(v_{i}\\right)\\right)-P_{p c}^{m a\nx}\\right] \\\\\nP_{p c}^{\\max }=\\max _{k: e_{k i} \\in E} R_{p c}\\left(v_{a k}, v_{k}\n\\mid P a\\left(v_{a k}\\right) \\backslash v_{k}, P\na\\left(v_{k}\\right)\\right)\n\\end{array}\\]</span></p>\n<p>同样地,归一化矩阵Q:</p>\n<p><span class=\"math display\">\\[\\bar{Q}_{i j}=\\frac{Q_{i j}}{\\sum_{j}\nQ_{i j}}\\]</span></p>\n<p><strong>Step 2 潜在根因节点打分</strong></p>\n<p>定义指标的根因节点得分公式如下:</p>\n<p><span class=\"math display\">\\[\\gamma_{i}=\\lambda\n\\bar{c}_{i}+(1-\\lambda) \\bar{\\eta}_{\\max }^{i}\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(\\bar{c}_{i}\\)</span>是在随机游走中被访问的次数(标准化过的),<span\nclass=\"math inline\">\\(\\bar{\\eta}_{\\max\n}^{i}\\)</span>是指标的异常程度(经标准化过的),<span\nclass=\"math inline\">\\(\\lambda\\)</span>是用来控制两者权重的系数,<span\nclass=\"math inline\">\\(\\lambda \\in [0,1]\\)</span></p>\n<p><strong>Step 3 根因节点排序</strong></p>\n<p>把指标分成了三类,如下表所示,通常高级别的指标会影响低级别的指标,因此,在确定根因节点时高级别指标会拥有更高的权重系数。</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515004925.png\"></p>\n<p>论文考虑了指标的优先级、指标的异常时间,指标的异常根因得分,提出了一个根因节点排序算法,如下:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515005008.png\" width=\"60%\"></p>\n<p>即级别更高的指标、更加异常的指标,更早发生异常的指标更有可能会被认为是根因节点</p>\n<h2 id=\"算法结果验证\">算法结果验证</h2>\n<p>与相关算法对比</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220515005103.png\" width=\"80%\"></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<p>1.Meng Y, Zhang S, Sun Y, et al. Localizing failure root causes in a\nmicroservice through causality inference[C]//2020 IEEE/ACM 28th\nInternational Symposium on Quality of Service (IWQoS). IEEE, 2020:\n1-10.</p>"},{"title":"PageRank算法原理","date":"2020-12-08T10:05:00.000Z","mathjax":true,"_content":"PageRank, 网页排名,又称网页级别,佩奇排名等,是一种由搜索引擎根据网页之间相互的超链接计算的技术,作为网页排名的要素之一,以Google创办人拉里佩奇(Larry Page)命名。Google用它来体现网页的相关性和重要性,在搜索引擎优化操作中是经常被用来评估网页优化的成效因素之一。\n\n<!--more-->\n\n## PageRank简单形式\n\n### 基本思想\n\n- 如果一个网页被其他很多网页链接,则重要,权值高\n- 如果PageRank值高的网页链接某个网页,则该网页权值也会相应提高\n\n### 计算方式\n\n假设有如下四个网页A、B、C、D,链接信息如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220508234438.png\" width=\"50%\" height=\"50%\">\n\n上图是一个有向图,将网页看成节点,网页之间的链接关系用边表示,出链指的是链接出去的链接,入链指的是进来的链接,比如上图A有2个入链,3个出链\n\n__PageRank定义__: 一个网页的影响力 = 所有入链集合的页面加权影响力之和\n\n上图A节点的影响力可用如下公式计算:\n\n$$PR(A) = \\frac{PR(B)}{L(B)} + \\frac{PR(C)}{L(C)} + \\frac{PR(D)}{L(D)}$$\n\n其中,$PR(A)$表示网页A的影响力,$L(B)$表示B的出链数量,用通用的公式表示为:\n\n$$PR(u)=\\sum_{\\nu \\in B_{u}} \\frac{P R(v)}{L(v)}$$\n\nu为待评估的页面,$B_{u}$为页面u的入链集合。针对入链集合中的任意页面v,它能给u带来的影响力是其自身的影响力$PR(v)$除以v页面的出链数量,即页面v把影响力$PR(v$平均分配给了它的出链,这样统计所有能给u带来链接的页面v,得到的总和就是网页u的影响力,即为$PR(u)$\n\n因此,PageRank的简单形式定义如下:\n\n> 当含有若干个节点的有向图是强连通且非周期性的有向图时,在其基础上定义的随机游走模型,即一阶马尔科夫链具有平稳分布,平稳分布向量称为这个有向图的PageRank。若矩阵M是马尔科夫链的转移矩阵,则向量R满足:\n> $$ MR = R $$\n\n上图A、B、C、D四个网页的转移矩阵M如下:\n\n$$M=\\left[\\begin{array}{cccc}\n0 & 1 / 2 & 1 & 0 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 1 / 2 & 0 & 0\n\\end{array}\\right]$$\n\n假设A、B、C、D四个页面的初始影响力是相同的,即$w_{0}^{T} = [1/4\\space1/4\\space1/4\\space1/4]$\n\n第一次转移后,各页面影响力$w_{1}$变为:\n\n$$w_{1}=M w_{0}=\\left[\\begin{array}{cccc}\n0 & 1 / 2 & 1 & 0 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 1 / 2 & 0 & 0\n\\end{array}\\right]\\left[\\begin{array}{c}\n1 / 4 \\\\\n1 / 4 \\\\\n1 / 4 \\\\\n1 / 4\n\\end{array}\\right]=\\left[\\begin{array}{l}\n9 / 24 \\\\\n5 / 24 \\\\\n5 / 24 \\\\\n5 / 24\n\\end{array}\\right]$$\n\n之后再用转移矩阵乘以$w_{1}$得到$w_{2}$,直到第n次迭代后$w_{n}$收敛不再变化,上述例子,$w$会收敛至[0.3333 0.2222 0.2222 0.2222],对应A、B、C、D的影响力\n\n### 等级泄露和等级沉没\n\n1. 等级泄露(Rank Leak): 如果一个网页没有出链,就像是一个黑洞一样,吸收了其他网页的影响力而不释放,最终会导致其他网页的PR值为0,如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220509000856.png\" width=\"50%\" height=\"50%\">\n\n2. 等级沉没(Rank Sink): 如果一个网页只有出链没有入链,计算过程迭代下来,会导致这个网页的PR值为0,入下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220509001111.png\" width=\"50%\" height=\"50%\">\n\n## PageRank改进版\n\n为了解决简化模型中存在的等级泄露和等级沉没问题,拉里佩奇提出了PageRank的随机浏览模型。他假设了这样一个场景:\n\n> 用户并不都是按照跳转链接的方式来上网,还有一种可能是不论当前处于哪个页面,都有概率访问到其他任意页面,比如用户就是要直接输入网址访问其他页面,虽然这个概率比较小\n\n所以他定义了阻尼因子d,这个因子代表了用户按照跳转链接来上网的概率,通常可以取一个固定值0.85,而$1-d=0.15$则代表了用户不是通过跳转链接的方式来访问网页的概率\n\n下式是PageRank计算影响力的改进公式:\n\n$$PR(u)=\\frac{1-d}{N}+d \\sum_{\\nu=B_{u}} \\frac{P R(v)}{L(v)}$$\n\n其中,N为网页总数,这样我们有可以重新迭代网页的权重计算了,因为加入了阻尼因子d,一定程度上解决了等级泄露和等级沉没的问题\n\n同样地,定义概率转移矩阵M,则其一般公式如下:\n\n$$R=d M R+\\frac{1-d}{n}1$$\n\n其中,$d(0<=d<=1)$为阻尼因子,$1$是所有分量为1的n维向量\n\n## PageRank 代码实现\n\n```python\nimport numpy as np\nfrom scipy.sparse import csc_matrix\n\ndef pageRank(G, s=.85, maxerr=.0001):\n \"\"\"\n Computes the pagerank for each of the n states\n Parameters\n ----------\n G: matrix representing state transitions\n Gij is a binary value representing a transition from state i to j.\n s: probability of following a transition. 1-s probability of teleporting\n to another state.\n maxerr: if the sum of pageranks between iterations is bellow this we will\n have converged.\n \"\"\"\n n = G.shape[0]\n # transform G into markov matrix A\n A = csc_matrix(G, dtype=np.float)\n rsums = np.array(A.sum(1))[:, 0]\n ri, ci = A.nonzero()\n A.data /= rsums[ri]\n\n # bool array of sink states\n sink = rsums == 0\n\n # Compute pagerank r until we converge\n ro, r = np.zeros(n), np.ones(n)\n while np.sum(np.abs(r - ro)) > maxerr: # 迭代直至收敛\n ro = r.copy()\n # calculate each pagerank at a time\n for i in range(0, n):\n # inlinks of state i\n Ai = np.array(A[:, i].todense())[:, 0]\n # account for sink states\n Di = sink / float(n)\n # account for teleportation to state i\n Ei = np.ones(n) / float(n)\n r[i] = ro.dot(Ai * s + Di * s + Ei * (1 - s))\n\n # return normalized pagerank\n return r / float(sum(r))\n```\n\n使用示例:\n```python\nG = np.array([[0,0,1,0,0,0,0],\n [0,1,1,0,0,0,0],\n [1,0,1,1,0,0,0],\n [0,0,0,1,1,0,0],\n [0,0,0,0,0,0,1],\n [0,0,0,0,0,1,1],\n [0,0,0,1,1,0,1]])\nprint(pageRank(G,s=.86))\n--------------------\n[0.12727557 0.03616954 0.12221594 0.22608452 0.28934412 0.03616954 0.16274076]\n```\n\n## 参考资料\n\n1.https://www.cnblogs.com/jpcflyer/p/11180263.html\n\n2.[PageRank notebook](https://github.com/fengdu78/lihang-code/blob/master/%E7%AC%AC21%E7%AB%A0%20PageRank%E7%AE%97%E6%B3%95/21.PageRank.ipynb)\n\n## 其他\n\n一些相似算法或改进的算法\n\n1.LeaderRank\n\n2.Hilltop算法\n\n3.ExpertRank\n\n4.HITS\n\n5.TrustRank\n","source":"_posts/PageRank.md","raw":"\ntitle: PageRank算法原理\n\ndate: 2020-12-08 18:05:00\n\ntags: \n - 根因定位\n\ncategories:\n - 根因定位\n\nmathjax: true\n\n------\nPageRank, 网页排名,又称网页级别,佩奇排名等,是一种由搜索引擎根据网页之间相互的超链接计算的技术,作为网页排名的要素之一,以Google创办人拉里佩奇(Larry Page)命名。Google用它来体现网页的相关性和重要性,在搜索引擎优化操作中是经常被用来评估网页优化的成效因素之一。\n\n<!--more-->\n\n## PageRank简单形式\n\n### 基本思想\n\n- 如果一个网页被其他很多网页链接,则重要,权值高\n- 如果PageRank值高的网页链接某个网页,则该网页权值也会相应提高\n\n### 计算方式\n\n假设有如下四个网页A、B、C、D,链接信息如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220508234438.png\" width=\"50%\" height=\"50%\">\n\n上图是一个有向图,将网页看成节点,网页之间的链接关系用边表示,出链指的是链接出去的链接,入链指的是进来的链接,比如上图A有2个入链,3个出链\n\n__PageRank定义__: 一个网页的影响力 = 所有入链集合的页面加权影响力之和\n\n上图A节点的影响力可用如下公式计算:\n\n$$PR(A) = \\frac{PR(B)}{L(B)} + \\frac{PR(C)}{L(C)} + \\frac{PR(D)}{L(D)}$$\n\n其中,$PR(A)$表示网页A的影响力,$L(B)$表示B的出链数量,用通用的公式表示为:\n\n$$PR(u)=\\sum_{\\nu \\in B_{u}} \\frac{P R(v)}{L(v)}$$\n\nu为待评估的页面,$B_{u}$为页面u的入链集合。针对入链集合中的任意页面v,它能给u带来的影响力是其自身的影响力$PR(v)$除以v页面的出链数量,即页面v把影响力$PR(v$平均分配给了它的出链,这样统计所有能给u带来链接的页面v,得到的总和就是网页u的影响力,即为$PR(u)$\n\n因此,PageRank的简单形式定义如下:\n\n> 当含有若干个节点的有向图是强连通且非周期性的有向图时,在其基础上定义的随机游走模型,即一阶马尔科夫链具有平稳分布,平稳分布向量称为这个有向图的PageRank。若矩阵M是马尔科夫链的转移矩阵,则向量R满足:\n> $$ MR = R $$\n\n上图A、B、C、D四个网页的转移矩阵M如下:\n\n$$M=\\left[\\begin{array}{cccc}\n0 & 1 / 2 & 1 & 0 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 1 / 2 & 0 & 0\n\\end{array}\\right]$$\n\n假设A、B、C、D四个页面的初始影响力是相同的,即$w_{0}^{T} = [1/4\\space1/4\\space1/4\\space1/4]$\n\n第一次转移后,各页面影响力$w_{1}$变为:\n\n$$w_{1}=M w_{0}=\\left[\\begin{array}{cccc}\n0 & 1 / 2 & 1 & 0 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 1 / 2 & 0 & 0\n\\end{array}\\right]\\left[\\begin{array}{c}\n1 / 4 \\\\\n1 / 4 \\\\\n1 / 4 \\\\\n1 / 4\n\\end{array}\\right]=\\left[\\begin{array}{l}\n9 / 24 \\\\\n5 / 24 \\\\\n5 / 24 \\\\\n5 / 24\n\\end{array}\\right]$$\n\n之后再用转移矩阵乘以$w_{1}$得到$w_{2}$,直到第n次迭代后$w_{n}$收敛不再变化,上述例子,$w$会收敛至[0.3333 0.2222 0.2222 0.2222],对应A、B、C、D的影响力\n\n### 等级泄露和等级沉没\n\n1. 等级泄露(Rank Leak): 如果一个网页没有出链,就像是一个黑洞一样,吸收了其他网页的影响力而不释放,最终会导致其他网页的PR值为0,如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220509000856.png\" width=\"50%\" height=\"50%\">\n\n2. 等级沉没(Rank Sink): 如果一个网页只有出链没有入链,计算过程迭代下来,会导致这个网页的PR值为0,入下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220509001111.png\" width=\"50%\" height=\"50%\">\n\n## PageRank改进版\n\n为了解决简化模型中存在的等级泄露和等级沉没问题,拉里佩奇提出了PageRank的随机浏览模型。他假设了这样一个场景:\n\n> 用户并不都是按照跳转链接的方式来上网,还有一种可能是不论当前处于哪个页面,都有概率访问到其他任意页面,比如用户就是要直接输入网址访问其他页面,虽然这个概率比较小\n\n所以他定义了阻尼因子d,这个因子代表了用户按照跳转链接来上网的概率,通常可以取一个固定值0.85,而$1-d=0.15$则代表了用户不是通过跳转链接的方式来访问网页的概率\n\n下式是PageRank计算影响力的改进公式:\n\n$$PR(u)=\\frac{1-d}{N}+d \\sum_{\\nu=B_{u}} \\frac{P R(v)}{L(v)}$$\n\n其中,N为网页总数,这样我们有可以重新迭代网页的权重计算了,因为加入了阻尼因子d,一定程度上解决了等级泄露和等级沉没的问题\n\n同样地,定义概率转移矩阵M,则其一般公式如下:\n\n$$R=d M R+\\frac{1-d}{n}1$$\n\n其中,$d(0<=d<=1)$为阻尼因子,$1$是所有分量为1的n维向量\n\n## PageRank 代码实现\n\n```python\nimport numpy as np\nfrom scipy.sparse import csc_matrix\n\ndef pageRank(G, s=.85, maxerr=.0001):\n \"\"\"\n Computes the pagerank for each of the n states\n Parameters\n ----------\n G: matrix representing state transitions\n Gij is a binary value representing a transition from state i to j.\n s: probability of following a transition. 1-s probability of teleporting\n to another state.\n maxerr: if the sum of pageranks between iterations is bellow this we will\n have converged.\n \"\"\"\n n = G.shape[0]\n # transform G into markov matrix A\n A = csc_matrix(G, dtype=np.float)\n rsums = np.array(A.sum(1))[:, 0]\n ri, ci = A.nonzero()\n A.data /= rsums[ri]\n\n # bool array of sink states\n sink = rsums == 0\n\n # Compute pagerank r until we converge\n ro, r = np.zeros(n), np.ones(n)\n while np.sum(np.abs(r - ro)) > maxerr: # 迭代直至收敛\n ro = r.copy()\n # calculate each pagerank at a time\n for i in range(0, n):\n # inlinks of state i\n Ai = np.array(A[:, i].todense())[:, 0]\n # account for sink states\n Di = sink / float(n)\n # account for teleportation to state i\n Ei = np.ones(n) / float(n)\n r[i] = ro.dot(Ai * s + Di * s + Ei * (1 - s))\n\n # return normalized pagerank\n return r / float(sum(r))\n```\n\n使用示例:\n```python\nG = np.array([[0,0,1,0,0,0,0],\n [0,1,1,0,0,0,0],\n [1,0,1,1,0,0,0],\n [0,0,0,1,1,0,0],\n [0,0,0,0,0,0,1],\n [0,0,0,0,0,1,1],\n [0,0,0,1,1,0,1]])\nprint(pageRank(G,s=.86))\n--------------------\n[0.12727557 0.03616954 0.12221594 0.22608452 0.28934412 0.03616954 0.16274076]\n```\n\n## 参考资料\n\n1.https://www.cnblogs.com/jpcflyer/p/11180263.html\n\n2.[PageRank notebook](https://github.com/fengdu78/lihang-code/blob/master/%E7%AC%AC21%E7%AB%A0%20PageRank%E7%AE%97%E6%B3%95/21.PageRank.ipynb)\n\n## 其他\n\n一些相似算法或改进的算法\n\n1.LeaderRank\n\n2.Hilltop算法\n\n3.ExpertRank\n\n4.HITS\n\n5.TrustRank\n","slug":"PageRank","published":1,"updated":"2022-05-14T17:03:36.145Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl39lom8s000dotudhu169n9s","content":"<p>PageRank,\n网页排名,又称网页级别,佩奇排名等,是一种由搜索引擎根据网页之间相互的超链接计算的技术,作为网页排名的要素之一,以Google创办人拉里佩奇(Larry\nPage)命名。Google用它来体现网页的相关性和重要性,在搜索引擎优化操作中是经常被用来评估网页优化的成效因素之一。</p>\n<span id=\"more\"></span>\n<h2 id=\"pagerank简单形式\">PageRank简单形式</h2>\n<h3 id=\"基本思想\">基本思想</h3>\n<ul>\n<li>如果一个网页被其他很多网页链接,则重要,权值高</li>\n<li>如果PageRank值高的网页链接某个网页,则该网页权值也会相应提高</li>\n</ul>\n<h3 id=\"计算方式\">计算方式</h3>\n<p>假设有如下四个网页A、B、C、D,链接信息如下图所示:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508234438.png\" width=\"50%\" height=\"50%\"></p>\n<p>上图是一个有向图,将网页看成节点,网页之间的链接关系用边表示,出链指的是链接出去的链接,入链指的是进来的链接,比如上图A有2个入链,3个出链</p>\n<p><strong>PageRank定义</strong>: 一个网页的影响力 =\n所有入链集合的页面加权影响力之和</p>\n<p>上图A节点的影响力可用如下公式计算:</p>\n<p><span class=\"math display\">\\[PR(A) = \\frac{PR(B)}{L(B)} +\n\\frac{PR(C)}{L(C)} + \\frac{PR(D)}{L(D)}\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(PR(A)\\)</span>表示网页A的影响力,<span\nclass=\"math inline\">\\(L(B)\\)</span>表示B的出链数量,用通用的公式表示为:</p>\n<p><span class=\"math display\">\\[PR(u)=\\sum_{\\nu \\in B_{u}} \\frac{P\nR(v)}{L(v)}\\]</span></p>\n<p>u为待评估的页面,<span\nclass=\"math inline\">\\(B_{u}\\)</span>为页面u的入链集合。针对入链集合中的任意页面v,它能给u带来的影响力是其自身的影响力<span\nclass=\"math inline\">\\(PR(v)\\)</span>除以v页面的出链数量,即页面v把影响力<span\nclass=\"math inline\">\\(PR(v\\)</span>平均分配给了它的出链,这样统计所有能给u带来链接的页面v,得到的总和就是网页u的影响力,即为<span\nclass=\"math inline\">\\(PR(u)\\)</span></p>\n<p>因此,PageRank的简单形式定义如下:</p>\n<blockquote>\n<p>当含有若干个节点的有向图是强连通且非周期性的有向图时,在其基础上定义的随机游走模型,即一阶马尔科夫链具有平稳分布,平稳分布向量称为这个有向图的PageRank。若矩阵M是马尔科夫链的转移矩阵,则向量R满足:\n<span class=\"math display\">\\[ MR = R \\]</span></p>\n</blockquote>\n<p>上图A、B、C、D四个网页的转移矩阵M如下:</p>\n<p><span class=\"math display\">\\[M=\\left[\\begin{array}{cccc}\n0 & 1 / 2 & 1 & 0 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 1 / 2 & 0 & 0\n\\end{array}\\right]\\]</span></p>\n<p>假设A、B、C、D四个页面的初始影响力是相同的,即<span\nclass=\"math inline\">\\(w_{0}^{T} =\n[1/4\\space1/4\\space1/4\\space1/4]\\)</span></p>\n<p>第一次转移后,各页面影响力<span\nclass=\"math inline\">\\(w_{1}\\)</span>变为:</p>\n<p><span class=\"math display\">\\[w_{1}=M w_{0}=\\left[\\begin{array}{cccc}\n0 & 1 / 2 & 1 & 0 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 1 / 2 & 0 & 0\n\\end{array}\\right]\\left[\\begin{array}{c}\n1 / 4 \\\\\n1 / 4 \\\\\n1 / 4 \\\\\n1 / 4\n\\end{array}\\right]=\\left[\\begin{array}{l}\n9 / 24 \\\\\n5 / 24 \\\\\n5 / 24 \\\\\n5 / 24\n\\end{array}\\right]\\]</span></p>\n<p>之后再用转移矩阵乘以<span\nclass=\"math inline\">\\(w_{1}\\)</span>得到<span\nclass=\"math inline\">\\(w_{2}\\)</span>,直到第n次迭代后<span\nclass=\"math inline\">\\(w_{n}\\)</span>收敛不再变化,上述例子,<span\nclass=\"math inline\">\\(w\\)</span>会收敛至[0.3333 0.2222 0.2222\n0.2222],对应A、B、C、D的影响力</p>\n<h3 id=\"等级泄露和等级沉没\">等级泄露和等级沉没</h3>\n<ol type=\"1\">\n<li>等级泄露(Rank Leak):\n如果一个网页没有出链,就像是一个黑洞一样,吸收了其他网页的影响力而不释放,最终会导致其他网页的PR值为0,如下图所示:</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220509000856.png\" width=\"50%\" height=\"50%\"></p>\n<ol start=\"2\" type=\"1\">\n<li>等级沉没(Rank Sink):\n如果一个网页只有出链没有入链,计算过程迭代下来,会导致这个网页的PR值为0,入下图所示:</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220509001111.png\" width=\"50%\" height=\"50%\"></p>\n<h2 id=\"pagerank改进版\">PageRank改进版</h2>\n<p>为了解决简化模型中存在的等级泄露和等级沉没问题,拉里佩奇提出了PageRank的随机浏览模型。他假设了这样一个场景:</p>\n<blockquote>\n<p>用户并不都是按照跳转链接的方式来上网,还有一种可能是不论当前处于哪个页面,都有概率访问到其他任意页面,比如用户就是要直接输入网址访问其他页面,虽然这个概率比较小</p>\n</blockquote>\n<p>所以他定义了阻尼因子d,这个因子代表了用户按照跳转链接来上网的概率,通常可以取一个固定值0.85,而<span\nclass=\"math inline\">\\(1-d=0.15\\)</span>则代表了用户不是通过跳转链接的方式来访问网页的概率</p>\n<p>下式是PageRank计算影响力的改进公式:</p>\n<p><span class=\"math display\">\\[PR(u)=\\frac{1-d}{N}+d \\sum_{\\nu=B_{u}}\n\\frac{P R(v)}{L(v)}\\]</span></p>\n<p>其中,N为网页总数,这样我们有可以重新迭代网页的权重计算了,因为加入了阻尼因子d,一定程度上解决了等级泄露和等级沉没的问题</p>\n<p>同样地,定义概率转移矩阵M,则其一般公式如下:</p>\n<p><span class=\"math display\">\\[R=d M R+\\frac{1-d}{n}1\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(d(0<=d<=1)\\)</span>为阻尼因子,<span\nclass=\"math inline\">\\(1\\)</span>是所有分量为1的n维向量</p>\n<h2 id=\"pagerank-代码实现\">PageRank 代码实现</h2>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> numpy <span class=\"keyword\">as</span> np</span><br><span class=\"line\"><span class=\"keyword\">from</span> scipy.sparse <span class=\"keyword\">import</span> csc_matrix</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">pageRank</span>(<span class=\"params\">G, s=<span class=\"number\">.85</span>, maxerr=<span class=\"number\">.0001</span></span>):</span><br><span class=\"line\"> <span class=\"string\">"""</span></span><br><span class=\"line\"><span class=\"string\"> Computes the pagerank for each of the n states</span></span><br><span class=\"line\"><span class=\"string\"> Parameters</span></span><br><span class=\"line\"><span class=\"string\"> ----------</span></span><br><span class=\"line\"><span class=\"string\"> G: matrix representing state transitions</span></span><br><span class=\"line\"><span class=\"string\"> Gij is a binary value representing a transition from state i to j.</span></span><br><span class=\"line\"><span class=\"string\"> s: probability of following a transition. 1-s probability of teleporting</span></span><br><span class=\"line\"><span class=\"string\"> to another state.</span></span><br><span class=\"line\"><span class=\"string\"> maxerr: if the sum of pageranks between iterations is bellow this we will</span></span><br><span class=\"line\"><span class=\"string\"> have converged.</span></span><br><span class=\"line\"><span class=\"string\"> """</span></span><br><span class=\"line\"> n = G.shape[<span class=\"number\">0</span>]</span><br><span class=\"line\"> <span class=\"comment\"># transform G into markov matrix A</span></span><br><span class=\"line\"> A = csc_matrix(G, dtype=np.<span class=\"built_in\">float</span>)</span><br><span class=\"line\"> rsums = np.array(A.<span class=\"built_in\">sum</span>(<span class=\"number\">1</span>))[:, <span class=\"number\">0</span>]</span><br><span class=\"line\"> ri, ci = A.nonzero()</span><br><span class=\"line\"> A.data /= rsums[ri]</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\"># bool array of sink states</span></span><br><span class=\"line\"> sink = rsums == <span class=\"number\">0</span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\"># Compute pagerank r until we converge</span></span><br><span class=\"line\"> ro, r = np.zeros(n), np.ones(n)</span><br><span class=\"line\"> <span class=\"keyword\">while</span> np.<span class=\"built_in\">sum</span>(np.<span class=\"built_in\">abs</span>(r - ro)) > maxerr: <span class=\"comment\"># 迭代直至收敛</span></span><br><span class=\"line\"> ro = r.copy()</span><br><span class=\"line\"> <span class=\"comment\"># calculate each pagerank at a time</span></span><br><span class=\"line\"> <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">0</span>, n):</span><br><span class=\"line\"> <span class=\"comment\"># inlinks of state i</span></span><br><span class=\"line\"> Ai = np.array(A[:, i].todense())[:, <span class=\"number\">0</span>]</span><br><span class=\"line\"> <span class=\"comment\"># account for sink states</span></span><br><span class=\"line\"> Di = sink / <span class=\"built_in\">float</span>(n)</span><br><span class=\"line\"> <span class=\"comment\"># account for teleportation to state i</span></span><br><span class=\"line\"> Ei = np.ones(n) / <span class=\"built_in\">float</span>(n)</span><br><span class=\"line\"> r[i] = ro.dot(Ai * s + Di * s + Ei * (<span class=\"number\">1</span> - s))</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\"># return normalized pagerank</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> r / <span class=\"built_in\">float</span>(<span class=\"built_in\">sum</span>(r))</span><br></pre></td></tr></table></figure>\n<p>使用示例: <figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">G = np.array([[<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>],</span><br><span class=\"line\"> [<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>]])</span><br><span class=\"line\"><span class=\"built_in\">print</span>(pageRank(G,s=<span class=\"number\">.86</span>))</span><br><span class=\"line\">--------------------</span><br><span class=\"line\">[<span class=\"number\">0.12727557</span> <span class=\"number\">0.03616954</span> <span class=\"number\">0.12221594</span> <span class=\"number\">0.22608452</span> <span class=\"number\">0.28934412</span> <span class=\"number\">0.03616954</span> <span class=\"number\">0.16274076</span>]</span><br></pre></td></tr></table></figure></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<p>1.https://www.cnblogs.com/jpcflyer/p/11180263.html</p>\n<p>2.<a\nhref=\"https://github.com/fengdu78/lihang-code/blob/master/%E7%AC%AC21%E7%AB%A0%20PageRank%E7%AE%97%E6%B3%95/21.PageRank.ipynb\">PageRank\nnotebook</a></p>\n<h2 id=\"其他\">其他</h2>\n<p>一些相似算法或改进的算法</p>\n<p>1.LeaderRank</p>\n<p>2.Hilltop算法</p>\n<p>3.ExpertRank</p>\n<p>4.HITS</p>\n<p>5.TrustRank</p>\n","site":{"data":{}},"excerpt":"<p>PageRank,\n网页排名,又称网页级别,佩奇排名等,是一种由搜索引擎根据网页之间相互的超链接计算的技术,作为网页排名的要素之一,以Google创办人拉里佩奇(Larry\nPage)命名。Google用它来体现网页的相关性和重要性,在搜索引擎优化操作中是经常被用来评估网页优化的成效因素之一。</p>","more":"<h2 id=\"pagerank简单形式\">PageRank简单形式</h2>\n<h3 id=\"基本思想\">基本思想</h3>\n<ul>\n<li>如果一个网页被其他很多网页链接,则重要,权值高</li>\n<li>如果PageRank值高的网页链接某个网页,则该网页权值也会相应提高</li>\n</ul>\n<h3 id=\"计算方式\">计算方式</h3>\n<p>假设有如下四个网页A、B、C、D,链接信息如下图所示:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220508234438.png\" width=\"50%\" height=\"50%\"></p>\n<p>上图是一个有向图,将网页看成节点,网页之间的链接关系用边表示,出链指的是链接出去的链接,入链指的是进来的链接,比如上图A有2个入链,3个出链</p>\n<p><strong>PageRank定义</strong>: 一个网页的影响力 =\n所有入链集合的页面加权影响力之和</p>\n<p>上图A节点的影响力可用如下公式计算:</p>\n<p><span class=\"math display\">\\[PR(A) = \\frac{PR(B)}{L(B)} +\n\\frac{PR(C)}{L(C)} + \\frac{PR(D)}{L(D)}\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(PR(A)\\)</span>表示网页A的影响力,<span\nclass=\"math inline\">\\(L(B)\\)</span>表示B的出链数量,用通用的公式表示为:</p>\n<p><span class=\"math display\">\\[PR(u)=\\sum_{\\nu \\in B_{u}} \\frac{P\nR(v)}{L(v)}\\]</span></p>\n<p>u为待评估的页面,<span\nclass=\"math inline\">\\(B_{u}\\)</span>为页面u的入链集合。针对入链集合中的任意页面v,它能给u带来的影响力是其自身的影响力<span\nclass=\"math inline\">\\(PR(v)\\)</span>除以v页面的出链数量,即页面v把影响力<span\nclass=\"math inline\">\\(PR(v\\)</span>平均分配给了它的出链,这样统计所有能给u带来链接的页面v,得到的总和就是网页u的影响力,即为<span\nclass=\"math inline\">\\(PR(u)\\)</span></p>\n<p>因此,PageRank的简单形式定义如下:</p>\n<blockquote>\n<p>当含有若干个节点的有向图是强连通且非周期性的有向图时,在其基础上定义的随机游走模型,即一阶马尔科夫链具有平稳分布,平稳分布向量称为这个有向图的PageRank。若矩阵M是马尔科夫链的转移矩阵,则向量R满足:\n<span class=\"math display\">\\[ MR = R \\]</span></p>\n</blockquote>\n<p>上图A、B、C、D四个网页的转移矩阵M如下:</p>\n<p><span class=\"math display\">\\[M=\\left[\\begin{array}{cccc}\n0 & 1 / 2 & 1 & 0 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 1 / 2 & 0 & 0\n\\end{array}\\right]\\]</span></p>\n<p>假设A、B、C、D四个页面的初始影响力是相同的,即<span\nclass=\"math inline\">\\(w_{0}^{T} =\n[1/4\\space1/4\\space1/4\\space1/4]\\)</span></p>\n<p>第一次转移后,各页面影响力<span\nclass=\"math inline\">\\(w_{1}\\)</span>变为:</p>\n<p><span class=\"math display\">\\[w_{1}=M w_{0}=\\left[\\begin{array}{cccc}\n0 & 1 / 2 & 1 & 0 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 0 & 0 & 1 / 2 \\\\\n1 / 3 & 1 / 2 & 0 & 0\n\\end{array}\\right]\\left[\\begin{array}{c}\n1 / 4 \\\\\n1 / 4 \\\\\n1 / 4 \\\\\n1 / 4\n\\end{array}\\right]=\\left[\\begin{array}{l}\n9 / 24 \\\\\n5 / 24 \\\\\n5 / 24 \\\\\n5 / 24\n\\end{array}\\right]\\]</span></p>\n<p>之后再用转移矩阵乘以<span\nclass=\"math inline\">\\(w_{1}\\)</span>得到<span\nclass=\"math inline\">\\(w_{2}\\)</span>,直到第n次迭代后<span\nclass=\"math inline\">\\(w_{n}\\)</span>收敛不再变化,上述例子,<span\nclass=\"math inline\">\\(w\\)</span>会收敛至[0.3333 0.2222 0.2222\n0.2222],对应A、B、C、D的影响力</p>\n<h3 id=\"等级泄露和等级沉没\">等级泄露和等级沉没</h3>\n<ol type=\"1\">\n<li>等级泄露(Rank Leak):\n如果一个网页没有出链,就像是一个黑洞一样,吸收了其他网页的影响力而不释放,最终会导致其他网页的PR值为0,如下图所示:</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220509000856.png\" width=\"50%\" height=\"50%\"></p>\n<ol start=\"2\" type=\"1\">\n<li>等级沉没(Rank Sink):\n如果一个网页只有出链没有入链,计算过程迭代下来,会导致这个网页的PR值为0,入下图所示:</li>\n</ol>\n<p><img src=\"https://images.bumpchicken.cn/img/20220509001111.png\" width=\"50%\" height=\"50%\"></p>\n<h2 id=\"pagerank改进版\">PageRank改进版</h2>\n<p>为了解决简化模型中存在的等级泄露和等级沉没问题,拉里佩奇提出了PageRank的随机浏览模型。他假设了这样一个场景:</p>\n<blockquote>\n<p>用户并不都是按照跳转链接的方式来上网,还有一种可能是不论当前处于哪个页面,都有概率访问到其他任意页面,比如用户就是要直接输入网址访问其他页面,虽然这个概率比较小</p>\n</blockquote>\n<p>所以他定义了阻尼因子d,这个因子代表了用户按照跳转链接来上网的概率,通常可以取一个固定值0.85,而<span\nclass=\"math inline\">\\(1-d=0.15\\)</span>则代表了用户不是通过跳转链接的方式来访问网页的概率</p>\n<p>下式是PageRank计算影响力的改进公式:</p>\n<p><span class=\"math display\">\\[PR(u)=\\frac{1-d}{N}+d \\sum_{\\nu=B_{u}}\n\\frac{P R(v)}{L(v)}\\]</span></p>\n<p>其中,N为网页总数,这样我们有可以重新迭代网页的权重计算了,因为加入了阻尼因子d,一定程度上解决了等级泄露和等级沉没的问题</p>\n<p>同样地,定义概率转移矩阵M,则其一般公式如下:</p>\n<p><span class=\"math display\">\\[R=d M R+\\frac{1-d}{n}1\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(d(0<=d<=1)\\)</span>为阻尼因子,<span\nclass=\"math inline\">\\(1\\)</span>是所有分量为1的n维向量</p>\n<h2 id=\"pagerank-代码实现\">PageRank 代码实现</h2>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br><span class=\"line\">24</span><br><span class=\"line\">25</span><br><span class=\"line\">26</span><br><span class=\"line\">27</span><br><span class=\"line\">28</span><br><span class=\"line\">29</span><br><span class=\"line\">30</span><br><span class=\"line\">31</span><br><span class=\"line\">32</span><br><span class=\"line\">33</span><br><span class=\"line\">34</span><br><span class=\"line\">35</span><br><span class=\"line\">36</span><br><span class=\"line\">37</span><br><span class=\"line\">38</span><br><span class=\"line\">39</span><br><span class=\"line\">40</span><br><span class=\"line\">41</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">import</span> numpy <span class=\"keyword\">as</span> np</span><br><span class=\"line\"><span class=\"keyword\">from</span> scipy.sparse <span class=\"keyword\">import</span> csc_matrix</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">pageRank</span>(<span class=\"params\">G, s=<span class=\"number\">.85</span>, maxerr=<span class=\"number\">.0001</span></span>):</span><br><span class=\"line\"> <span class=\"string\">"""</span></span><br><span class=\"line\"><span class=\"string\"> Computes the pagerank for each of the n states</span></span><br><span class=\"line\"><span class=\"string\"> Parameters</span></span><br><span class=\"line\"><span class=\"string\"> ----------</span></span><br><span class=\"line\"><span class=\"string\"> G: matrix representing state transitions</span></span><br><span class=\"line\"><span class=\"string\"> Gij is a binary value representing a transition from state i to j.</span></span><br><span class=\"line\"><span class=\"string\"> s: probability of following a transition. 1-s probability of teleporting</span></span><br><span class=\"line\"><span class=\"string\"> to another state.</span></span><br><span class=\"line\"><span class=\"string\"> maxerr: if the sum of pageranks between iterations is bellow this we will</span></span><br><span class=\"line\"><span class=\"string\"> have converged.</span></span><br><span class=\"line\"><span class=\"string\"> """</span></span><br><span class=\"line\"> n = G.shape[<span class=\"number\">0</span>]</span><br><span class=\"line\"> <span class=\"comment\"># transform G into markov matrix A</span></span><br><span class=\"line\"> A = csc_matrix(G, dtype=np.<span class=\"built_in\">float</span>)</span><br><span class=\"line\"> rsums = np.array(A.<span class=\"built_in\">sum</span>(<span class=\"number\">1</span>))[:, <span class=\"number\">0</span>]</span><br><span class=\"line\"> ri, ci = A.nonzero()</span><br><span class=\"line\"> A.data /= rsums[ri]</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\"># bool array of sink states</span></span><br><span class=\"line\"> sink = rsums == <span class=\"number\">0</span></span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\"># Compute pagerank r until we converge</span></span><br><span class=\"line\"> ro, r = np.zeros(n), np.ones(n)</span><br><span class=\"line\"> <span class=\"keyword\">while</span> np.<span class=\"built_in\">sum</span>(np.<span class=\"built_in\">abs</span>(r - ro)) > maxerr: <span class=\"comment\"># 迭代直至收敛</span></span><br><span class=\"line\"> ro = r.copy()</span><br><span class=\"line\"> <span class=\"comment\"># calculate each pagerank at a time</span></span><br><span class=\"line\"> <span class=\"keyword\">for</span> i <span class=\"keyword\">in</span> <span class=\"built_in\">range</span>(<span class=\"number\">0</span>, n):</span><br><span class=\"line\"> <span class=\"comment\"># inlinks of state i</span></span><br><span class=\"line\"> Ai = np.array(A[:, i].todense())[:, <span class=\"number\">0</span>]</span><br><span class=\"line\"> <span class=\"comment\"># account for sink states</span></span><br><span class=\"line\"> Di = sink / <span class=\"built_in\">float</span>(n)</span><br><span class=\"line\"> <span class=\"comment\"># account for teleportation to state i</span></span><br><span class=\"line\"> Ei = np.ones(n) / <span class=\"built_in\">float</span>(n)</span><br><span class=\"line\"> r[i] = ro.dot(Ai * s + Di * s + Ei * (<span class=\"number\">1</span> - s))</span><br><span class=\"line\"></span><br><span class=\"line\"> <span class=\"comment\"># return normalized pagerank</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> r / <span class=\"built_in\">float</span>(<span class=\"built_in\">sum</span>(r))</span><br></pre></td></tr></table></figure>\n<p>使用示例: <figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">G = np.array([[<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>],</span><br><span class=\"line\"> [<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>],</span><br><span class=\"line\"> [<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>,<span class=\"number\">1</span>,<span class=\"number\">0</span>,<span class=\"number\">1</span>]])</span><br><span class=\"line\"><span class=\"built_in\">print</span>(pageRank(G,s=<span class=\"number\">.86</span>))</span><br><span class=\"line\">--------------------</span><br><span class=\"line\">[<span class=\"number\">0.12727557</span> <span class=\"number\">0.03616954</span> <span class=\"number\">0.12221594</span> <span class=\"number\">0.22608452</span> <span class=\"number\">0.28934412</span> <span class=\"number\">0.03616954</span> <span class=\"number\">0.16274076</span>]</span><br></pre></td></tr></table></figure></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<p>1.https://www.cnblogs.com/jpcflyer/p/11180263.html</p>\n<p>2.<a\nhref=\"https://github.com/fengdu78/lihang-code/blob/master/%E7%AC%AC21%E7%AB%A0%20PageRank%E7%AE%97%E6%B3%95/21.PageRank.ipynb\">PageRank\nnotebook</a></p>\n<h2 id=\"其他\">其他</h2>\n<p>一些相似算法或改进的算法</p>\n<p>1.LeaderRank</p>\n<p>2.Hilltop算法</p>\n<p>3.ExpertRank</p>\n<p>4.HITS</p>\n<p>5.TrustRank</p>"},{"title":"深入浅出SVM","date":"2019-09-25T16:09:00.000Z","mathjax":true,"top":8,"_content":"<img src=\"https://images.bumpchicken.cn/img/20220511220448.png\" width=\"60%\">\nSupport Vector Machine(支持向量机),是一种非常经典的机器学习分类方法。\n<!--more-->\n\n## SVM基本原理\n\nSupport Vector Machine(支持向量机),是一种非常经典的机器学习分类方法。它有严格的数学理论支持,可解释性强,不依靠统计方法,并且利用核函数技巧能有效解决一些线性不可分的场景。\n\n给定样本集:$D=\\left\\{\\left(\\boldsymbol{x}_{1}, y_{1}\\right),\\left(\\boldsymbol{x}_{2}, y_{2}\\right), \\ldots,\\left(\\boldsymbol{x}_{m}, y_{m}\\right)\\right\\}, y_{i} \\in\\{-1,+1\\}$,如下所示,假设左上方的点为正样本,右下方的点为负样本\n\n<img src=\"https://images.bumpchicken.cn/img/20220511220958.png\" width=\"60%\">\n\n\n 寻找一个最优的分类面,使得依赖这个分类面产生的分类结果最具鲁棒性,体现在图上就是分类样本离这个分类平面尽可能远,具有最大的间隔。将这个分类的平面称为**最大间隔超平面**,离这个最大间隔超平面最近的点称之为**支持向量(Support Vector),分类超平面的构建只与这些少数的点有关,这也是为什么它叫作支持向量机的原因。**\n\n<img src=\"https://images.bumpchicken.cn/img/20220511221109.png\" width=\"60%\">\n\n\n## SVM最优化问题\n\nSVM 目的是找到各类样本点到超平面的距离最远,也就是找到最大间隔超平面。任意超平面可以用下面这个线性方程来描述:\n\n$$\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b=0$$\n\n我们知道,二维空间点(x, y) 到直线 Ax+By+C=0的距离公式为:\n\n$$\\frac{|A x+B y+C|}{\\sqrt{A^{2}+B^{2}}}$$\n\n扩展到n维空间,点$x=\\left(x_{1}, x_{2} \\dots x_{n}\\right)$到$w^{T} x+b=0$距离为:\n\n$$\\frac{\\left|w^{T} x+b\\right|}{\\|w\\|}$$\n\n其中,$\\|w\\|=\\sqrt{w_{1}^{2}+\\ldots w_{n}^{2}}$\n\n假定分割超平面能将样本点 __准确__ 分为两类,设支持向量到最大间隔超平面的距离为d,则其他向量到最大间隔超平面的距离大于d。于是有:\n\n$$\\left\\{\\begin{array}{l}{\\frac{w^{T} x+b}{\\|w\\|} \\geq d \\quad y_{i}=1} \\\\ {\\frac{w^{T} x+b}{\\|w\\|} \\leq-d \\quad y_{i}=-1}\\end{array}\\right.$$\n\n因为$\\|w\\| d$ 是正数,其对目标函数优化无影响,这里令其为1,因此上式不等式组可简化为:\n\n$$\\left\\{\\begin{array}{l}{w^{T} x+b \\geq 1 \\quad y_{i}=1} \\\\ {w^{T} x+b \\leq-1 \\quad y_{i}=-1}\\end{array}\\right.$$\n\n合并两个不等式,得到:$y_{i}\\left(w^{T} x+b\\right) \\geq 1$\n\n这里,我们再来看关于超平面和支持向量的图解:\n\n<img src=\"https://images.bumpchicken.cn/img/20220511221210.png\" width=\"60%\">\n\n支持向量到最大间隔超平面的距离为:\n\n$$d=\\frac{\\left|w^{T} x+b\\right|}{\\|w\\|}$$\n\n最大化这个距离:\n\n$$\\max 2 * \\frac{\\left|w^{T} x+b\\right|}{\\|w\\|}$$\n\n对于确定的样本集来说,$\\left|w^{T} x+b\\right|$是个常量,因此目标函数变为:$\\max \\frac{2}{\\|w\\|}$,即$\\min \\frac{1}{2}\\|w\\|$\n\n为了方便计算,去除根号,目标函数转化为:$\\min \\frac{1}{2}\\|w\\|^{2}$\n\n因此得到SVM的优化问题:\n\n$$\\min \\frac{1}{2}\\|w\\|^{2} $$\n\n$${s.t.}\\quad y_{i}\\quad\\left(w^{T} x_{i}+b\\right) \\geq 1$$\n\n\n## KKT条件\n\n上述最优化问题约束条件是不等式。如果是等式约束,可以直接用Lagrange乘数法求解,即对于下述最优化问题\n\n$$\\begin{array}{c}{\\min f\\left(x_{1}, x_{2}, \\ldots, x_{n}\\right)} \\\\ {\\text { s.t. } \\quad h_{k}\\left(x_{1}, x_{2}, \\ldots, x_{n}\\right)=0}\\end{array}$$\n\n我们可以构造拉格朗日函数:$L(x, \\lambda)=f(x)+\\sum_{k=1}^{l} \\lambda_{k} h_{k}(x)$,然后分别对$x$,$\\lambda$求偏导,求得可能的极值点\n\n$$\\left\\{\\begin{array}{ll}{\\frac{\\partial L}{\\partial x_{i}}=0} & {i=1,2, \\ldots, n} \\\\ {\\frac{\\partial L}{\\partial \\lambda_{k}}=0} & {k=1,2, \\ldots, l}\\end{array}\\right.$$\n\n那么对于不等式约束条件,做法是引入一个松弛变量,然后将该松弛变量也视为待优化变量。以SVM优化问题为例:\n\n$$\\begin{aligned} \\min f(w) &=\\min \\frac{1}{2}\\|w\\|^{2} \\\\ \\text {s.t.} & g_{i}(w)=1-y_{i}\\left(w^{T} x_{i}+b\\right) \\leq 0 \\end{aligned}$$\n\n引入松弛变量 $a_{i}^{2}$,得到新的约束条件: $h_{i}\\left(w, a_{i}\\right)=g_{i}(w)+a_{i}^{2}=0$,将不等式约束变为等式约束,得到新的拉格朗日函数:\n\n$$\\begin{aligned} L(w, \\lambda, a) &=\\frac{1}{2} f(w)+\\sum_{i=1}^{n} \\lambda_{i} h_{i}(w) \\\\ &=\\frac{1}{2} f(w)+\\sum_{i=1}^{n} \\lambda_{i}\\left[g_{i}(w)+a_{i}^{2}\\right] \\quad \\lambda_{i} \\geq 0 \\end{aligned}$$\n\n(**注意到,这里有$\\lambda_{i}>=0$,在拉格朗日乘数法中,没有非负的要求,关于这里为何 $\\lambda_{i}>=0$ 可以通过几何性质来证明,有兴趣的可以查阅相关资料**。)\n\n根据等式约束条件,有:\n\n$$\\left\\{\\begin{array}{c}{\\frac{\\partial L}{\\partial w_{i}}=\\frac{\\partial f}{\\partial w_{i}}+\\sum_{i=1}^{n} \\lambda_{i} \\frac{\\partial g_{i}}{\\partial w_{i}}=0} \\\\ {\\frac{\\partial L}{\\partial a_{i}}=2 \\lambda_{i} a_{i}=0} \\\\ {\\frac{\\partial L}{\\partial \\lambda_{i}}=g_{i}(w)+a_{i}^{2}=0} \\\\ {\\lambda_{i} \\geq 0}\\end{array}\\right.$$\n\n第二个式子,$2\\lambda_{i} a_{i}=0$,有两种情况:\n\n1. $\\lambda_{i}$ 为0,$a_{i}$不为0:由于$\\lambda_{i}$为0,这时候约束$g_{i}(w)$不起作用,并且$g_{i}(w)<0$\n2. $\\lambda_{i}$不为0,$a_{i}$为0:这时$g_{i}(w)$起约束作用,并且$g_{i}(w)=0$\n\n因此,方程组可转换为:\n\n$$\\left\\{\\begin{aligned} \\frac{\\partial L}{\\partial w_{i}} &=\\frac{\\partial f}{\\partial w_{i}}+\\sum_{j=1}^{n} \\lambda_{j} \\frac{\\partial g_{j}}{\\partial w_{i}}=0 \\\\ \\lambda_{i} g_{i}(w) &=0 \\\\ g_{i}(w) & \\leq 0 \\\\ \\lambda_{i} & \\geq 0 \\end{aligned}\\right.$$\n\n以上便是不等式约束优化问题的__KKT(Karush-Kuhn-Tucker)条件__,$\\lambda_{i}$称为KKT乘子。从这个方程组可以得到以下讯息:\n\n1. 对于支持向量 $g_{i}(w)=0$,$\\lambda_{i}>0$即可\n2. 对于非支持向量 $g_{i}(w)<0$,但要求 $\\lambda_{i}=0$\n\n## 求解SVM最优化问题\n\n利用KKT条件,我们可以求解SVM最优化问题:\n\n$$\\min _{w} \\frac{1}{2}\\|w\\|^{2}$$\n\n$$\\text {s.t. } \\quad g_{i}(w, b)=1-y_{i} \\left(w^{T} x_{i}+b\\right) \\quad \\leq 0, \\quad i=1,2, \\ldots, n$$\n\n__Step 1__: 构造拉格朗日函数\n\n$$\\begin{aligned} L(w, b, \\lambda)=& \\frac{1}{2}\\|w\\|^{2}+\\sum_{i=1}^{n} \\lambda_{i}\\left[1-y_{i}\\left(w^{T} x_{i}+b\\right)\\right] \\\\ & \\text {s.t.} \\quad \\lambda_{i} \\geq 0 \\end{aligned}$$\n\n假设目标函数最小值为p, 即$\\frac{1}{2}\\|w\\|^{2} = p$,因为$\\sum_{i=1}^{n} \\lambda_{i}\\left[1-y_{i}\\left(w^{T} x_{i}+b\\right)\\right] <= 0$,即$L(w, b, \\lambda) <= p$,为了找到最优的参数$\\lambda$使得$L(w, b, \\lambda)$接近p,问题转换为:$\\max _{\\lambda} L(w, b, \\lambda)$,即:\n\n$$\\begin{array}{c}{\\min _{w} \\max _{\\lambda} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq 0}\\end{array}$$\n\n__Step 2__:利用对偶性转换求解问题:\n\n对偶问题其实就是将:\n\n$$\\begin{array}{c}{\\min _{w} \\max _{\\lambda} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq 0}\\end{array}$$\n\n转化为:$\\begin{array}{c}{\\max _{\\lambda} \\min _{w} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq 0}\\end{array}$\n\n假设有函数$f$,我们有:\n\n$$min \\space max f >= max \\space min f$$\n\n即最大的里面挑出来最小的也要比最小的里面挑出来最大的要大,这是一种 __弱对偶关系__,而 __强对偶关系__ 是当等号成立时,即:\n\n$$min \\space max f == max \\space min f$$\n\n当$f$是凸优化问题时,等号成立,而我们之前求的KKT条件是强对偶性的 __充要条件__\n\n因此,对$\\begin{array}{c}{\\max _{\\lambda} \\min _{w} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq 0}\\end{array}$进行求解:\n\n1)对参数$w$和$b$求偏导数:\n\n$$\\frac{\\partial L}{\\partial w}=w-\\sum_{i=1}^{n} \\lambda_{i} x_{i} y_{i}=0$$\n\n$$\\frac{\\partial L}{\\partial b}=\\sum_{i=1}^{n} \\lambda_{i} y_{i}=0$$\n\n得到:\n\n$$\\sum_{i=1}^{n} \\lambda_{i} x_{i} y_{i}=w$$\n\n$$\\sum_{i=1}^{n} \\lambda_{i} y_{i}=0$$\n\n2)将1)中求导结果代回 $L(w, b, \\lambda)$中,得到:\n\n$$\\begin{aligned} L(w, b, \\lambda) &=\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+\\sum_{i=1}^{n} \\lambda_{i}-\\sum_{i=1}^{n} \\lambda_{i} y_{i}\\left(\\sum_{j=1}^{n} \\lambda_{j} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+b\\right) \\\\ &=\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+\\sum_{i=1}^{n} \\lambda_{i}-\\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)-\\sum_{i=1}^{n} \\lambda_{i} y_{i} b \\\\ &=\\sum_{j=1}^{n} \\lambda_{i}-\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right) \\end{aligned}$$\n\n3)利用SMO(Sequential Minimal Optimization)求解\n\n$\\max _{\\lambda}\\left[\\sum_{j=1}^{n} \\lambda_{i}-\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)\\right]$ $\\text { s.t. } \\sum_{i=1}^{n} \\lambda_{i} y_{i}=0 \\quad \\lambda_{i} \\geq 0$\n\n这是一个二次规划问题,问题规模正比于训练样本数,我们常用 SMO算法求解。\n\nSMO的思路是:先固定除$\\lambda_{i}$之外的参数,然后求$\\lambda_{i}$上的极值。但是我们这里有约束条件$\\sum_{i=1}^{n} \\lambda_{i} y_{i}=0$,如果固定$\\lambda_{i}$之外的参数,$\\lambda_{i}$可直接由其他参数推导出来。因此这里SMO一次优化两个参数,具体步骤为:\n\n- 选择两个需要更新的参数$\\lambda_{i}$和$\\lambda_{j}$,固定其他参数,于是约束变为:\n\n $\\lambda_{i} y_{i}+\\lambda_{j} y_{j}=c \\quad \\lambda_{i} \\geq 0, \\lambda_{j} \\geq 0$,其中,$c=-\\sum_{k \\neq i, j} \\lambda_{k} y_{k}$\n\n 得到:$\\lambda_{j}=\\frac{c-\\lambda_{i} y_{i}}{y_{j}}$\n\n 也就是说我们可以用$\\lambda_{i}$的表达式代替$\\lambda_{j}$。这样就相当于把目标问题转化成了仅有一个约束条件的最优化问题,仅有的约束是$\\lambda_{i}>=0$\n\n- 对于仅有一个约束条件的最优化问题,我们完全可以在$\\lambda_{i}$上对优化目标求偏导,令导数为零,从而求出变量值$\\lambda_{i}$的极值$\\lambda_{i_{new}}$,然后通过$\\lambda_{i_{new}}$求出$\\lambda_{j_{new}}$\n\n- 多次迭代直至收敛\n\n4)根据 ${\\sum_{i=1}^{n} \\lambda_{i} x_{i} y_{i}=w}$求得$w$\n\n5)求偏移项$b$\n\n对于$\\lambda_{i}>0$,$g_{i}(w)=0$,满足这个条件的点均为支持向量,可以取任一支持向量,带入$y_{s}\\left(w x_{s}+b\\right)=1$,即可求得$b$\n\n或者采取更为鲁棒的做法,取所有支持向量各计算出一个$b$,然后取均值,即按下式求取:\n\n$$b=\\frac{1}{|S|} \\sum_{s \\in S}\\left(y_{s}-w x_{s}\\right)$$\n\n6)构造分类超平面:$w^{T} x+b=0$\n\n分类决策函数:$f(x)=\\operatorname{sign}\\left(w^{T} x+b\\right)$\n\n其中,$sign(x)$是阶跃函数:\n\n$$\\operatorname{sign}(x)=\\left\\{\\begin{array}{rl}{-1} & {x<0} \\\\ {0} & {x=0} \\\\ {1} & {x>0}\\end{array}\\right.$$\n\n\n## 线性不可分场景\n\n 以上我们讨论的都是线性可分的情况,实际场景中,通常遇到的数据分布都是线性不可分的。如以下场景,此场景下,求得的分类面损失将会超出我们的容忍范围。\n\n<img src=\"https://images.bumpchicken.cn/img/20220511221306.png\" width=\"60%\">\n\n\n**SVM的做法是将二维线性不可分样本映射到高维空间中,让样本点在高维空间线性可分**,比如下列动图演示的做法\n\n<img src=\"https://images.bumpchicken.cn/img/201485427.gif\" width=\"80%\">\n\n\n对于在有限维度向量空间中线性不可分的样本,我们将其映射到更高维度的向量空间里,再通过间隔最大化的方式,学习得到的支持向量机称之为**非线性 SVM。**\n\n我们用 x 表示原来的样本点,用 $\\kappa(x)$表示 $x$ 映射到特征新的特征空间后到新向量。那么优化问题可以表示为:\n\n$$\\max _{\\boldsymbol{\\lambda}} \\sum_{i=1}^{m} \\lambda{i}-\\frac{1}{2} \\sum_{i=1}^{m} \\sum_{j=1}^{m} \\lambda{i} \\lambda{j} y_{i} y_{j} \\kappa\\left(\\boldsymbol{x}_{i}, \\boldsymbol{x}_{j}\\right)$$\n\n$$\\text {s.t.} \\quad \\sum_{i=1}^{n} \\lambda_{i} y_{i}=0, \\quad \\lambda_{i} \\geq 0$$\n\n我们常用的核函数有:\n\n- 线性核函数\n\n $$k\\left(x_{i}, x_{j}\\right)=x_{i}^{T} x_{j}$$\n\n- 多项式核函数\n\n $$k\\left(x_{i}, x_{j}\\right)=\\left(x_{i}^{T} x_{j}\\right)^{d}$$\n\n- 高斯核函数\n\n $$k\\left(x_{i}, x_{j}\\right)=\\exp \\left(-\\frac{\\left\\|x_{i}-x_{j}\\right\\|}{2 \\delta^{2}}\\right)$$\n\n\n 理论上高斯核函数可以将数据映射到无限维。\n\n## 总结\n\nSVM作为一种经典的机器学习分类方法,具有以下优点:\n\n1. 采用核技巧之后,可以处理非线性分类/回归任务\n2. 能找出对任务至关重要的关键样本(即支持向量)\n3. 最终决策函数只由少数的支持向量所确定,计算的复杂性取决于支持向量的数目,而不是样本空间的维数,这在某种意义上避免了“维数灾难”\n\n同时,也具有以下缺点:\n\n1. 训练时间长。当采用 SMO 算法时,由于每次都需要挑选一对参数,因此时间复杂度为 O(N2),其中 N 为训练样本的数量\n2. 当采用核技巧时,如果需要存储核矩阵,则空间复杂度为 O(N2)\n3. 模型预测时,预测时间与支持向量的个数成正比。当支持向量的数量较大时,预测计算复杂度较高\n\n\n\n## 参考资料\n\n1. 《机器学习》 周志华\n\n2. [ 浅谈最优化问题中的KKT条件](https://zhuanlan.zhihu.com/p/26514613)","source":"_posts/SVM.md","raw":"\ntitle: 深入浅出SVM\n\ndate: 2019-09-26 00:09:00\n\ntags: \n - 分类\n\ncategories:\n - 机器学习\n\nmathjax: true\n\ntop: 8\n\n---\n<img src=\"https://images.bumpchicken.cn/img/20220511220448.png\" width=\"60%\">\nSupport Vector Machine(支持向量机),是一种非常经典的机器学习分类方法。\n<!--more-->\n\n## SVM基本原理\n\nSupport Vector Machine(支持向量机),是一种非常经典的机器学习分类方法。它有严格的数学理论支持,可解释性强,不依靠统计方法,并且利用核函数技巧能有效解决一些线性不可分的场景。\n\n给定样本集:$D=\\left\\{\\left(\\boldsymbol{x}_{1}, y_{1}\\right),\\left(\\boldsymbol{x}_{2}, y_{2}\\right), \\ldots,\\left(\\boldsymbol{x}_{m}, y_{m}\\right)\\right\\}, y_{i} \\in\\{-1,+1\\}$,如下所示,假设左上方的点为正样本,右下方的点为负样本\n\n<img src=\"https://images.bumpchicken.cn/img/20220511220958.png\" width=\"60%\">\n\n\n 寻找一个最优的分类面,使得依赖这个分类面产生的分类结果最具鲁棒性,体现在图上就是分类样本离这个分类平面尽可能远,具有最大的间隔。将这个分类的平面称为**最大间隔超平面**,离这个最大间隔超平面最近的点称之为**支持向量(Support Vector),分类超平面的构建只与这些少数的点有关,这也是为什么它叫作支持向量机的原因。**\n\n<img src=\"https://images.bumpchicken.cn/img/20220511221109.png\" width=\"60%\">\n\n\n## SVM最优化问题\n\nSVM 目的是找到各类样本点到超平面的距离最远,也就是找到最大间隔超平面。任意超平面可以用下面这个线性方程来描述:\n\n$$\\boldsymbol{w}^{\\mathrm{T}} \\boldsymbol{x}+b=0$$\n\n我们知道,二维空间点(x, y) 到直线 Ax+By+C=0的距离公式为:\n\n$$\\frac{|A x+B y+C|}{\\sqrt{A^{2}+B^{2}}}$$\n\n扩展到n维空间,点$x=\\left(x_{1}, x_{2} \\dots x_{n}\\right)$到$w^{T} x+b=0$距离为:\n\n$$\\frac{\\left|w^{T} x+b\\right|}{\\|w\\|}$$\n\n其中,$\\|w\\|=\\sqrt{w_{1}^{2}+\\ldots w_{n}^{2}}$\n\n假定分割超平面能将样本点 __准确__ 分为两类,设支持向量到最大间隔超平面的距离为d,则其他向量到最大间隔超平面的距离大于d。于是有:\n\n$$\\left\\{\\begin{array}{l}{\\frac{w^{T} x+b}{\\|w\\|} \\geq d \\quad y_{i}=1} \\\\ {\\frac{w^{T} x+b}{\\|w\\|} \\leq-d \\quad y_{i}=-1}\\end{array}\\right.$$\n\n因为$\\|w\\| d$ 是正数,其对目标函数优化无影响,这里令其为1,因此上式不等式组可简化为:\n\n$$\\left\\{\\begin{array}{l}{w^{T} x+b \\geq 1 \\quad y_{i}=1} \\\\ {w^{T} x+b \\leq-1 \\quad y_{i}=-1}\\end{array}\\right.$$\n\n合并两个不等式,得到:$y_{i}\\left(w^{T} x+b\\right) \\geq 1$\n\n这里,我们再来看关于超平面和支持向量的图解:\n\n<img src=\"https://images.bumpchicken.cn/img/20220511221210.png\" width=\"60%\">\n\n支持向量到最大间隔超平面的距离为:\n\n$$d=\\frac{\\left|w^{T} x+b\\right|}{\\|w\\|}$$\n\n最大化这个距离:\n\n$$\\max 2 * \\frac{\\left|w^{T} x+b\\right|}{\\|w\\|}$$\n\n对于确定的样本集来说,$\\left|w^{T} x+b\\right|$是个常量,因此目标函数变为:$\\max \\frac{2}{\\|w\\|}$,即$\\min \\frac{1}{2}\\|w\\|$\n\n为了方便计算,去除根号,目标函数转化为:$\\min \\frac{1}{2}\\|w\\|^{2}$\n\n因此得到SVM的优化问题:\n\n$$\\min \\frac{1}{2}\\|w\\|^{2} $$\n\n$${s.t.}\\quad y_{i}\\quad\\left(w^{T} x_{i}+b\\right) \\geq 1$$\n\n\n## KKT条件\n\n上述最优化问题约束条件是不等式。如果是等式约束,可以直接用Lagrange乘数法求解,即对于下述最优化问题\n\n$$\\begin{array}{c}{\\min f\\left(x_{1}, x_{2}, \\ldots, x_{n}\\right)} \\\\ {\\text { s.t. } \\quad h_{k}\\left(x_{1}, x_{2}, \\ldots, x_{n}\\right)=0}\\end{array}$$\n\n我们可以构造拉格朗日函数:$L(x, \\lambda)=f(x)+\\sum_{k=1}^{l} \\lambda_{k} h_{k}(x)$,然后分别对$x$,$\\lambda$求偏导,求得可能的极值点\n\n$$\\left\\{\\begin{array}{ll}{\\frac{\\partial L}{\\partial x_{i}}=0} & {i=1,2, \\ldots, n} \\\\ {\\frac{\\partial L}{\\partial \\lambda_{k}}=0} & {k=1,2, \\ldots, l}\\end{array}\\right.$$\n\n那么对于不等式约束条件,做法是引入一个松弛变量,然后将该松弛变量也视为待优化变量。以SVM优化问题为例:\n\n$$\\begin{aligned} \\min f(w) &=\\min \\frac{1}{2}\\|w\\|^{2} \\\\ \\text {s.t.} & g_{i}(w)=1-y_{i}\\left(w^{T} x_{i}+b\\right) \\leq 0 \\end{aligned}$$\n\n引入松弛变量 $a_{i}^{2}$,得到新的约束条件: $h_{i}\\left(w, a_{i}\\right)=g_{i}(w)+a_{i}^{2}=0$,将不等式约束变为等式约束,得到新的拉格朗日函数:\n\n$$\\begin{aligned} L(w, \\lambda, a) &=\\frac{1}{2} f(w)+\\sum_{i=1}^{n} \\lambda_{i} h_{i}(w) \\\\ &=\\frac{1}{2} f(w)+\\sum_{i=1}^{n} \\lambda_{i}\\left[g_{i}(w)+a_{i}^{2}\\right] \\quad \\lambda_{i} \\geq 0 \\end{aligned}$$\n\n(**注意到,这里有$\\lambda_{i}>=0$,在拉格朗日乘数法中,没有非负的要求,关于这里为何 $\\lambda_{i}>=0$ 可以通过几何性质来证明,有兴趣的可以查阅相关资料**。)\n\n根据等式约束条件,有:\n\n$$\\left\\{\\begin{array}{c}{\\frac{\\partial L}{\\partial w_{i}}=\\frac{\\partial f}{\\partial w_{i}}+\\sum_{i=1}^{n} \\lambda_{i} \\frac{\\partial g_{i}}{\\partial w_{i}}=0} \\\\ {\\frac{\\partial L}{\\partial a_{i}}=2 \\lambda_{i} a_{i}=0} \\\\ {\\frac{\\partial L}{\\partial \\lambda_{i}}=g_{i}(w)+a_{i}^{2}=0} \\\\ {\\lambda_{i} \\geq 0}\\end{array}\\right.$$\n\n第二个式子,$2\\lambda_{i} a_{i}=0$,有两种情况:\n\n1. $\\lambda_{i}$ 为0,$a_{i}$不为0:由于$\\lambda_{i}$为0,这时候约束$g_{i}(w)$不起作用,并且$g_{i}(w)<0$\n2. $\\lambda_{i}$不为0,$a_{i}$为0:这时$g_{i}(w)$起约束作用,并且$g_{i}(w)=0$\n\n因此,方程组可转换为:\n\n$$\\left\\{\\begin{aligned} \\frac{\\partial L}{\\partial w_{i}} &=\\frac{\\partial f}{\\partial w_{i}}+\\sum_{j=1}^{n} \\lambda_{j} \\frac{\\partial g_{j}}{\\partial w_{i}}=0 \\\\ \\lambda_{i} g_{i}(w) &=0 \\\\ g_{i}(w) & \\leq 0 \\\\ \\lambda_{i} & \\geq 0 \\end{aligned}\\right.$$\n\n以上便是不等式约束优化问题的__KKT(Karush-Kuhn-Tucker)条件__,$\\lambda_{i}$称为KKT乘子。从这个方程组可以得到以下讯息:\n\n1. 对于支持向量 $g_{i}(w)=0$,$\\lambda_{i}>0$即可\n2. 对于非支持向量 $g_{i}(w)<0$,但要求 $\\lambda_{i}=0$\n\n## 求解SVM最优化问题\n\n利用KKT条件,我们可以求解SVM最优化问题:\n\n$$\\min _{w} \\frac{1}{2}\\|w\\|^{2}$$\n\n$$\\text {s.t. } \\quad g_{i}(w, b)=1-y_{i} \\left(w^{T} x_{i}+b\\right) \\quad \\leq 0, \\quad i=1,2, \\ldots, n$$\n\n__Step 1__: 构造拉格朗日函数\n\n$$\\begin{aligned} L(w, b, \\lambda)=& \\frac{1}{2}\\|w\\|^{2}+\\sum_{i=1}^{n} \\lambda_{i}\\left[1-y_{i}\\left(w^{T} x_{i}+b\\right)\\right] \\\\ & \\text {s.t.} \\quad \\lambda_{i} \\geq 0 \\end{aligned}$$\n\n假设目标函数最小值为p, 即$\\frac{1}{2}\\|w\\|^{2} = p$,因为$\\sum_{i=1}^{n} \\lambda_{i}\\left[1-y_{i}\\left(w^{T} x_{i}+b\\right)\\right] <= 0$,即$L(w, b, \\lambda) <= p$,为了找到最优的参数$\\lambda$使得$L(w, b, \\lambda)$接近p,问题转换为:$\\max _{\\lambda} L(w, b, \\lambda)$,即:\n\n$$\\begin{array}{c}{\\min _{w} \\max _{\\lambda} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq 0}\\end{array}$$\n\n__Step 2__:利用对偶性转换求解问题:\n\n对偶问题其实就是将:\n\n$$\\begin{array}{c}{\\min _{w} \\max _{\\lambda} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq 0}\\end{array}$$\n\n转化为:$\\begin{array}{c}{\\max _{\\lambda} \\min _{w} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq 0}\\end{array}$\n\n假设有函数$f$,我们有:\n\n$$min \\space max f >= max \\space min f$$\n\n即最大的里面挑出来最小的也要比最小的里面挑出来最大的要大,这是一种 __弱对偶关系__,而 __强对偶关系__ 是当等号成立时,即:\n\n$$min \\space max f == max \\space min f$$\n\n当$f$是凸优化问题时,等号成立,而我们之前求的KKT条件是强对偶性的 __充要条件__\n\n因此,对$\\begin{array}{c}{\\max _{\\lambda} \\min _{w} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq 0}\\end{array}$进行求解:\n\n1)对参数$w$和$b$求偏导数:\n\n$$\\frac{\\partial L}{\\partial w}=w-\\sum_{i=1}^{n} \\lambda_{i} x_{i} y_{i}=0$$\n\n$$\\frac{\\partial L}{\\partial b}=\\sum_{i=1}^{n} \\lambda_{i} y_{i}=0$$\n\n得到:\n\n$$\\sum_{i=1}^{n} \\lambda_{i} x_{i} y_{i}=w$$\n\n$$\\sum_{i=1}^{n} \\lambda_{i} y_{i}=0$$\n\n2)将1)中求导结果代回 $L(w, b, \\lambda)$中,得到:\n\n$$\\begin{aligned} L(w, b, \\lambda) &=\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+\\sum_{i=1}^{n} \\lambda_{i}-\\sum_{i=1}^{n} \\lambda_{i} y_{i}\\left(\\sum_{j=1}^{n} \\lambda_{j} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+b\\right) \\\\ &=\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+\\sum_{i=1}^{n} \\lambda_{i}-\\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)-\\sum_{i=1}^{n} \\lambda_{i} y_{i} b \\\\ &=\\sum_{j=1}^{n} \\lambda_{i}-\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right) \\end{aligned}$$\n\n3)利用SMO(Sequential Minimal Optimization)求解\n\n$\\max _{\\lambda}\\left[\\sum_{j=1}^{n} \\lambda_{i}-\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)\\right]$ $\\text { s.t. } \\sum_{i=1}^{n} \\lambda_{i} y_{i}=0 \\quad \\lambda_{i} \\geq 0$\n\n这是一个二次规划问题,问题规模正比于训练样本数,我们常用 SMO算法求解。\n\nSMO的思路是:先固定除$\\lambda_{i}$之外的参数,然后求$\\lambda_{i}$上的极值。但是我们这里有约束条件$\\sum_{i=1}^{n} \\lambda_{i} y_{i}=0$,如果固定$\\lambda_{i}$之外的参数,$\\lambda_{i}$可直接由其他参数推导出来。因此这里SMO一次优化两个参数,具体步骤为:\n\n- 选择两个需要更新的参数$\\lambda_{i}$和$\\lambda_{j}$,固定其他参数,于是约束变为:\n\n $\\lambda_{i} y_{i}+\\lambda_{j} y_{j}=c \\quad \\lambda_{i} \\geq 0, \\lambda_{j} \\geq 0$,其中,$c=-\\sum_{k \\neq i, j} \\lambda_{k} y_{k}$\n\n 得到:$\\lambda_{j}=\\frac{c-\\lambda_{i} y_{i}}{y_{j}}$\n\n 也就是说我们可以用$\\lambda_{i}$的表达式代替$\\lambda_{j}$。这样就相当于把目标问题转化成了仅有一个约束条件的最优化问题,仅有的约束是$\\lambda_{i}>=0$\n\n- 对于仅有一个约束条件的最优化问题,我们完全可以在$\\lambda_{i}$上对优化目标求偏导,令导数为零,从而求出变量值$\\lambda_{i}$的极值$\\lambda_{i_{new}}$,然后通过$\\lambda_{i_{new}}$求出$\\lambda_{j_{new}}$\n\n- 多次迭代直至收敛\n\n4)根据 ${\\sum_{i=1}^{n} \\lambda_{i} x_{i} y_{i}=w}$求得$w$\n\n5)求偏移项$b$\n\n对于$\\lambda_{i}>0$,$g_{i}(w)=0$,满足这个条件的点均为支持向量,可以取任一支持向量,带入$y_{s}\\left(w x_{s}+b\\right)=1$,即可求得$b$\n\n或者采取更为鲁棒的做法,取所有支持向量各计算出一个$b$,然后取均值,即按下式求取:\n\n$$b=\\frac{1}{|S|} \\sum_{s \\in S}\\left(y_{s}-w x_{s}\\right)$$\n\n6)构造分类超平面:$w^{T} x+b=0$\n\n分类决策函数:$f(x)=\\operatorname{sign}\\left(w^{T} x+b\\right)$\n\n其中,$sign(x)$是阶跃函数:\n\n$$\\operatorname{sign}(x)=\\left\\{\\begin{array}{rl}{-1} & {x<0} \\\\ {0} & {x=0} \\\\ {1} & {x>0}\\end{array}\\right.$$\n\n\n## 线性不可分场景\n\n 以上我们讨论的都是线性可分的情况,实际场景中,通常遇到的数据分布都是线性不可分的。如以下场景,此场景下,求得的分类面损失将会超出我们的容忍范围。\n\n<img src=\"https://images.bumpchicken.cn/img/20220511221306.png\" width=\"60%\">\n\n\n**SVM的做法是将二维线性不可分样本映射到高维空间中,让样本点在高维空间线性可分**,比如下列动图演示的做法\n\n<img src=\"https://images.bumpchicken.cn/img/201485427.gif\" width=\"80%\">\n\n\n对于在有限维度向量空间中线性不可分的样本,我们将其映射到更高维度的向量空间里,再通过间隔最大化的方式,学习得到的支持向量机称之为**非线性 SVM。**\n\n我们用 x 表示原来的样本点,用 $\\kappa(x)$表示 $x$ 映射到特征新的特征空间后到新向量。那么优化问题可以表示为:\n\n$$\\max _{\\boldsymbol{\\lambda}} \\sum_{i=1}^{m} \\lambda{i}-\\frac{1}{2} \\sum_{i=1}^{m} \\sum_{j=1}^{m} \\lambda{i} \\lambda{j} y_{i} y_{j} \\kappa\\left(\\boldsymbol{x}_{i}, \\boldsymbol{x}_{j}\\right)$$\n\n$$\\text {s.t.} \\quad \\sum_{i=1}^{n} \\lambda_{i} y_{i}=0, \\quad \\lambda_{i} \\geq 0$$\n\n我们常用的核函数有:\n\n- 线性核函数\n\n $$k\\left(x_{i}, x_{j}\\right)=x_{i}^{T} x_{j}$$\n\n- 多项式核函数\n\n $$k\\left(x_{i}, x_{j}\\right)=\\left(x_{i}^{T} x_{j}\\right)^{d}$$\n\n- 高斯核函数\n\n $$k\\left(x_{i}, x_{j}\\right)=\\exp \\left(-\\frac{\\left\\|x_{i}-x_{j}\\right\\|}{2 \\delta^{2}}\\right)$$\n\n\n 理论上高斯核函数可以将数据映射到无限维。\n\n## 总结\n\nSVM作为一种经典的机器学习分类方法,具有以下优点:\n\n1. 采用核技巧之后,可以处理非线性分类/回归任务\n2. 能找出对任务至关重要的关键样本(即支持向量)\n3. 最终决策函数只由少数的支持向量所确定,计算的复杂性取决于支持向量的数目,而不是样本空间的维数,这在某种意义上避免了“维数灾难”\n\n同时,也具有以下缺点:\n\n1. 训练时间长。当采用 SMO 算法时,由于每次都需要挑选一对参数,因此时间复杂度为 O(N2),其中 N 为训练样本的数量\n2. 当采用核技巧时,如果需要存储核矩阵,则空间复杂度为 O(N2)\n3. 模型预测时,预测时间与支持向量的个数成正比。当支持向量的数量较大时,预测计算复杂度较高\n\n\n\n## 参考资料\n\n1. 《机器学习》 周志华\n\n2. [ 浅谈最优化问题中的KKT条件](https://zhuanlan.zhihu.com/p/26514613)","slug":"SVM","published":1,"updated":"2022-05-11T14:30:08.178Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl39lom8y000xotud1ven0tcf","content":"<p><img src=\"https://images.bumpchicken.cn/img/20220511220448.png\" width=\"60%\">\nSupport Vector Machine(支持向量机),是一种非常经典的机器学习分类方法。\n<span id=\"more\"></span></p>\n<h2 id=\"svm基本原理\">SVM基本原理</h2>\n<p>Support Vector\nMachine(支持向量机),是一种非常经典的机器学习分类方法。它有严格的数学理论支持,可解释性强,不依靠统计方法,并且利用核函数技巧能有效解决一些线性不可分的场景。</p>\n<p>给定样本集:<span\nclass=\"math inline\">\\(D=\\left\\{\\left(\\boldsymbol{x}_{1},\ny_{1}\\right),\\left(\\boldsymbol{x}_{2}, y_{2}\\right),\n\\ldots,\\left(\\boldsymbol{x}_{m}, y_{m}\\right)\\right\\}, y_{i}\n\\in\\{-1,+1\\}\\)</span>,如下所示,假设左上方的点为正样本,右下方的点为负样本</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511220958.png\" width=\"60%\"></p>\n<p>寻找一个最优的分类面,使得依赖这个分类面产生的分类结果最具鲁棒性,体现在图上就是分类样本离这个分类平面尽可能远,具有最大的间隔。将这个分类的平面称为<strong>最大间隔超平面</strong>,离这个最大间隔超平面最近的点称之为<strong>支持向量(Support\nVector),分类超平面的构建只与这些少数的点有关,这也是为什么它叫作支持向量机的原因。</strong></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511221109.png\" width=\"60%\"></p>\n<h2 id=\"svm最优化问题\">SVM最优化问题</h2>\n<p>SVM\n目的是找到各类样本点到超平面的距离最远,也就是找到最大间隔超平面。任意超平面可以用下面这个线性方程来描述:</p>\n<p><span class=\"math display\">\\[\\boldsymbol{w}^{\\mathrm{T}}\n\\boldsymbol{x}+b=0\\]</span></p>\n<p>我们知道,二维空间点(x, y) 到直线 Ax+By+C=0的距离公式为:</p>\n<p><span class=\"math display\">\\[\\frac{|A x+B\ny+C|}{\\sqrt{A^{2}+B^{2}}}\\]</span></p>\n<p>扩展到n维空间,点<span class=\"math inline\">\\(x=\\left(x_{1}, x_{2}\n\\dots x_{n}\\right)\\)</span>到<span class=\"math inline\">\\(w^{T}\nx+b=0\\)</span>距离为:</p>\n<p><span class=\"math display\">\\[\\frac{\\left|w^{T}\nx+b\\right|}{\\|w\\|}\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(\\|w\\|=\\sqrt{w_{1}^{2}+\\ldots\nw_{n}^{2}}\\)</span></p>\n<p>假定分割超平面能将样本点 <strong>准确</strong>\n分为两类,设支持向量到最大间隔超平面的距离为d,则其他向量到最大间隔超平面的距离大于d。于是有:</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{array}{l}{\\frac{w^{T}\nx+b}{\\|w\\|} \\geq d \\quad y_{i}=1} \\\\ {\\frac{w^{T} x+b}{\\|w\\|} \\leq-d\n\\quad y_{i}=-1}\\end{array}\\right.\\]</span></p>\n<p>因为<span class=\"math inline\">\\(\\|w\\| d\\)</span>\n是正数,其对目标函数优化无影响,这里令其为1,因此上式不等式组可简化为:</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{array}{l}{w^{T} x+b \\geq 1\n\\quad y_{i}=1} \\\\ {w^{T} x+b \\leq-1 \\quad\ny_{i}=-1}\\end{array}\\right.\\]</span></p>\n<p>合并两个不等式,得到:<span class=\"math inline\">\\(y_{i}\\left(w^{T}\nx+b\\right) \\geq 1\\)</span></p>\n<p>这里,我们再来看关于超平面和支持向量的图解:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511221210.png\" width=\"60%\"></p>\n<p>支持向量到最大间隔超平面的距离为:</p>\n<p><span class=\"math display\">\\[d=\\frac{\\left|w^{T}\nx+b\\right|}{\\|w\\|}\\]</span></p>\n<p>最大化这个距离:</p>\n<p><span class=\"math display\">\\[\\max 2 * \\frac{\\left|w^{T}\nx+b\\right|}{\\|w\\|}\\]</span></p>\n<p>对于确定的样本集来说,<span class=\"math inline\">\\(\\left|w^{T}\nx+b\\right|\\)</span>是个常量,因此目标函数变为:<span\nclass=\"math inline\">\\(\\max \\frac{2}{\\|w\\|}\\)</span>,即<span\nclass=\"math inline\">\\(\\min \\frac{1}{2}\\|w\\|\\)</span></p>\n<p>为了方便计算,去除根号,目标函数转化为:<span\nclass=\"math inline\">\\(\\min \\frac{1}{2}\\|w\\|^{2}\\)</span></p>\n<p>因此得到SVM的优化问题:</p>\n<p><span class=\"math display\">\\[\\min \\frac{1}{2}\\|w\\|^{2} \\]</span></p>\n<p><span class=\"math display\">\\[{s.t.}\\quad y_{i}\\quad\\left(w^{T}\nx_{i}+b\\right) \\geq 1\\]</span></p>\n<h2 id=\"kkt条件\">KKT条件</h2>\n<p>上述最优化问题约束条件是不等式。如果是等式约束,可以直接用Lagrange乘数法求解,即对于下述最优化问题</p>\n<p><span class=\"math display\">\\[\\begin{array}{c}{\\min f\\left(x_{1},\nx_{2}, \\ldots, x_{n}\\right)} \\\\ {\\text { s.t. } \\quad h_{k}\\left(x_{1},\nx_{2}, \\ldots, x_{n}\\right)=0}\\end{array}\\]</span></p>\n<p>我们可以构造拉格朗日函数:<span class=\"math inline\">\\(L(x,\n\\lambda)=f(x)+\\sum_{k=1}^{l} \\lambda_{k}\nh_{k}(x)\\)</span>,然后分别对<span\nclass=\"math inline\">\\(x\\)</span>,<span\nclass=\"math inline\">\\(\\lambda\\)</span>求偏导,求得可能的极值点</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{array}{ll}{\\frac{\\partial\nL}{\\partial x_{i}}=0} & {i=1,2, \\ldots, n} \\\\ {\\frac{\\partial\nL}{\\partial \\lambda_{k}}=0} & {k=1,2, \\ldots,\nl}\\end{array}\\right.\\]</span></p>\n<p>那么对于不等式约束条件,做法是引入一个松弛变量,然后将该松弛变量也视为待优化变量。以SVM优化问题为例:</p>\n<p><span class=\"math display\">\\[\\begin{aligned} \\min f(w) &=\\min\n\\frac{1}{2}\\|w\\|^{2} \\\\ \\text {s.t.} & g_{i}(w)=1-y_{i}\\left(w^{T}\nx_{i}+b\\right) \\leq 0 \\end{aligned}\\]</span></p>\n<p>引入松弛变量 <span\nclass=\"math inline\">\\(a_{i}^{2}\\)</span>,得到新的约束条件: <span\nclass=\"math inline\">\\(h_{i}\\left(w,\na_{i}\\right)=g_{i}(w)+a_{i}^{2}=0\\)</span>,将不等式约束变为等式约束,得到新的拉格朗日函数:</p>\n<p><span class=\"math display\">\\[\\begin{aligned} L(w, \\lambda, a)\n&=\\frac{1}{2} f(w)+\\sum_{i=1}^{n} \\lambda_{i} h_{i}(w) \\\\\n&=\\frac{1}{2} f(w)+\\sum_{i=1}^{n}\n\\lambda_{i}\\left[g_{i}(w)+a_{i}^{2}\\right] \\quad \\lambda_{i} \\geq 0\n\\end{aligned}\\]</span></p>\n<p>(<strong>注意到,这里有<span\nclass=\"math inline\">\\(\\lambda_{i}>=0\\)</span>,在拉格朗日乘数法中,没有非负的要求,关于这里为何\n<span class=\"math inline\">\\(\\lambda_{i}>=0\\)</span>\n可以通过几何性质来证明,有兴趣的可以查阅相关资料</strong>。)</p>\n<p>根据等式约束条件,有:</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{array}{c}{\\frac{\\partial\nL}{\\partial w_{i}}=\\frac{\\partial f}{\\partial w_{i}}+\\sum_{i=1}^{n}\n\\lambda_{i} \\frac{\\partial g_{i}}{\\partial w_{i}}=0} \\\\ {\\frac{\\partial\nL}{\\partial a_{i}}=2 \\lambda_{i} a_{i}=0} \\\\ {\\frac{\\partial L}{\\partial\n\\lambda_{i}}=g_{i}(w)+a_{i}^{2}=0} \\\\ {\\lambda_{i} \\geq\n0}\\end{array}\\right.\\]</span></p>\n<p>第二个式子,<span class=\"math inline\">\\(2\\lambda_{i}\na_{i}=0\\)</span>,有两种情况:</p>\n<ol type=\"1\">\n<li><span class=\"math inline\">\\(\\lambda_{i}\\)</span> 为0,<span\nclass=\"math inline\">\\(a_{i}\\)</span>不为0:由于<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>为0,这时候约束<span\nclass=\"math inline\">\\(g_{i}(w)\\)</span>不起作用,并且<span\nclass=\"math inline\">\\(g_{i}(w)<0\\)</span></li>\n<li><span class=\"math inline\">\\(\\lambda_{i}\\)</span>不为0,<span\nclass=\"math inline\">\\(a_{i}\\)</span>为0:这时<span\nclass=\"math inline\">\\(g_{i}(w)\\)</span>起约束作用,并且<span\nclass=\"math inline\">\\(g_{i}(w)=0\\)</span></li>\n</ol>\n<p>因此,方程组可转换为:</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{aligned} \\frac{\\partial\nL}{\\partial w_{i}} &=\\frac{\\partial f}{\\partial\nw_{i}}+\\sum_{j=1}^{n} \\lambda_{j} \\frac{\\partial g_{j}}{\\partial\nw_{i}}=0 \\\\ \\lambda_{i} g_{i}(w) &=0 \\\\ g_{i}(w) & \\leq 0 \\\\\n\\lambda_{i} & \\geq 0 \\end{aligned}\\right.\\]</span></p>\n<p>以上便是不等式约束优化问题的__KKT(Karush-Kuhn-Tucker)条件__,<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>称为KKT乘子。从这个方程组可以得到以下讯息:</p>\n<ol type=\"1\">\n<li>对于支持向量 <span class=\"math inline\">\\(g_{i}(w)=0\\)</span>,<span\nclass=\"math inline\">\\(\\lambda_{i}>0\\)</span>即可</li>\n<li>对于非支持向量 <span\nclass=\"math inline\">\\(g_{i}(w)<0\\)</span>,但要求 <span\nclass=\"math inline\">\\(\\lambda_{i}=0\\)</span></li>\n</ol>\n<h2 id=\"求解svm最优化问题\">求解SVM最优化问题</h2>\n<p>利用KKT条件,我们可以求解SVM最优化问题:</p>\n<p><span class=\"math display\">\\[\\min _{w}\n\\frac{1}{2}\\|w\\|^{2}\\]</span></p>\n<p><span class=\"math display\">\\[\\text {s.t. } \\quad g_{i}(w, b)=1-y_{i}\n\\left(w^{T} x_{i}+b\\right) \\quad \\leq 0, \\quad i=1,2, \\ldots,\nn\\]</span></p>\n<p><strong>Step 1</strong>: 构造拉格朗日函数</p>\n<p><span class=\"math display\">\\[\\begin{aligned} L(w, b, \\lambda)=&\n\\frac{1}{2}\\|w\\|^{2}+\\sum_{i=1}^{n} \\lambda_{i}\\left[1-y_{i}\\left(w^{T}\nx_{i}+b\\right)\\right] \\\\ & \\text {s.t.} \\quad \\lambda_{i} \\geq 0\n\\end{aligned}\\]</span></p>\n<p>假设目标函数最小值为p, 即<span\nclass=\"math inline\">\\(\\frac{1}{2}\\|w\\|^{2} = p\\)</span>,因为<span\nclass=\"math inline\">\\(\\sum_{i=1}^{n} \\lambda_{i}\\left[1-y_{i}\\left(w^{T}\nx_{i}+b\\right)\\right] <= 0\\)</span>,即<span\nclass=\"math inline\">\\(L(w, b, \\lambda) <=\np\\)</span>,为了找到最优的参数<span\nclass=\"math inline\">\\(\\lambda\\)</span>使得<span\nclass=\"math inline\">\\(L(w, b, \\lambda)\\)</span>接近p,问题转换为:<span\nclass=\"math inline\">\\(\\max _{\\lambda} L(w, b,\n\\lambda)\\)</span>,即:</p>\n<p><span class=\"math display\">\\[\\begin{array}{c}{\\min _{w} \\max\n_{\\lambda} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq\n0}\\end{array}\\]</span></p>\n<p><strong>Step 2</strong>:利用对偶性转换求解问题:</p>\n<p>对偶问题其实就是将:</p>\n<p><span class=\"math display\">\\[\\begin{array}{c}{\\min _{w} \\max\n_{\\lambda} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq\n0}\\end{array}\\]</span></p>\n<p>转化为:<span class=\"math inline\">\\(\\begin{array}{c}{\\max _{\\lambda}\n\\min _{w} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq\n0}\\end{array}\\)</span></p>\n<p>假设有函数<span class=\"math inline\">\\(f\\)</span>,我们有:</p>\n<p><span class=\"math display\">\\[min \\space max f >= max \\space min\nf\\]</span></p>\n<p>即最大的里面挑出来最小的也要比最小的里面挑出来最大的要大,这是一种\n<strong>弱对偶关系</strong>,而 <strong>强对偶关系</strong>\n是当等号成立时,即:</p>\n<p><span class=\"math display\">\\[min \\space max f == max \\space min\nf\\]</span></p>\n<p>当<span\nclass=\"math inline\">\\(f\\)</span>是凸优化问题时,等号成立,而我们之前求的KKT条件是强对偶性的\n<strong>充要条件</strong></p>\n<p>因此,对<span class=\"math inline\">\\(\\begin{array}{c}{\\max _{\\lambda}\n\\min _{w} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq\n0}\\end{array}\\)</span>进行求解:</p>\n<p>1)对参数<span class=\"math inline\">\\(w\\)</span>和<span\nclass=\"math inline\">\\(b\\)</span>求偏导数:</p>\n<p><span class=\"math display\">\\[\\frac{\\partial L}{\\partial\nw}=w-\\sum_{i=1}^{n} \\lambda_{i} x_{i} y_{i}=0\\]</span></p>\n<p><span class=\"math display\">\\[\\frac{\\partial L}{\\partial\nb}=\\sum_{i=1}^{n} \\lambda_{i} y_{i}=0\\]</span></p>\n<p>得到:</p>\n<p><span class=\"math display\">\\[\\sum_{i=1}^{n} \\lambda_{i} x_{i}\ny_{i}=w\\]</span></p>\n<p><span class=\"math display\">\\[\\sum_{i=1}^{n} \\lambda_{i}\ny_{i}=0\\]</span></p>\n<p>2)将1)中求导结果代回 <span class=\"math inline\">\\(L(w, b,\n\\lambda)\\)</span>中,得到:</p>\n<p><span class=\"math display\">\\[\\begin{aligned} L(w, b, \\lambda)\n&=\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j}\ny_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+\\sum_{i=1}^{n}\n\\lambda_{i}-\\sum_{i=1}^{n} \\lambda_{i} y_{i}\\left(\\sum_{j=1}^{n}\n\\lambda_{j} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+b\\right) \\\\\n&=\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j}\ny_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+\\sum_{i=1}^{n}\n\\lambda_{i}-\\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i}\ny_{j}\\left(x_{i} \\cdot x_{j}\\right)-\\sum_{i=1}^{n} \\lambda_{i} y_{i} b\n\\\\ &=\\sum_{j=1}^{n} \\lambda_{i}-\\frac{1}{2} \\sum_{i=1}^{n}\n\\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot\nx_{j}\\right) \\end{aligned}\\]</span></p>\n<p>3)利用SMO(Sequential Minimal Optimization)求解</p>\n<p><span class=\"math inline\">\\(\\max _{\\lambda}\\left[\\sum_{j=1}^{n}\n\\lambda_{i}-\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i}\n\\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)\\right]\\)</span>\n<span class=\"math inline\">\\(\\text { s.t. } \\sum_{i=1}^{n} \\lambda_{i}\ny_{i}=0 \\quad \\lambda_{i} \\geq 0\\)</span></p>\n<p>这是一个二次规划问题,问题规模正比于训练样本数,我们常用\nSMO算法求解。</p>\n<p>SMO的思路是:先固定除<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>之外的参数,然后求<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>上的极值。但是我们这里有约束条件<span\nclass=\"math inline\">\\(\\sum_{i=1}^{n} \\lambda_{i}\ny_{i}=0\\)</span>,如果固定<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>之外的参数,<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>可直接由其他参数推导出来。因此这里SMO一次优化两个参数,具体步骤为:</p>\n<ul>\n<li><p>选择两个需要更新的参数<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>和<span\nclass=\"math inline\">\\(\\lambda_{j}\\)</span>,固定其他参数,于是约束变为:</p>\n<p><span class=\"math inline\">\\(\\lambda_{i} y_{i}+\\lambda_{j} y_{j}=c\n\\quad \\lambda_{i} \\geq 0, \\lambda_{j} \\geq 0\\)</span>,其中,<span\nclass=\"math inline\">\\(c=-\\sum_{k \\neq i, j} \\lambda_{k}\ny_{k}\\)</span></p>\n<p>得到:<span class=\"math inline\">\\(\\lambda_{j}=\\frac{c-\\lambda_{i}\ny_{i}}{y_{j}}\\)</span></p>\n<p>也就是说我们可以用<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>的表达式代替<span\nclass=\"math inline\">\\(\\lambda_{j}\\)</span>。这样就相当于把目标问题转化成了仅有一个约束条件的最优化问题,仅有的约束是<span\nclass=\"math inline\">\\(\\lambda_{i}>=0\\)</span></p></li>\n<li><p>对于仅有一个约束条件的最优化问题,我们完全可以在<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>上对优化目标求偏导,令导数为零,从而求出变量值<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>的极值<span\nclass=\"math inline\">\\(\\lambda_{i_{new}}\\)</span>,然后通过<span\nclass=\"math inline\">\\(\\lambda_{i_{new}}\\)</span>求出<span\nclass=\"math inline\">\\(\\lambda_{j_{new}}\\)</span></p></li>\n<li><p>多次迭代直至收敛</p></li>\n</ul>\n<p>4)根据 <span class=\"math inline\">\\({\\sum_{i=1}^{n} \\lambda_{i} x_{i}\ny_{i}=w}\\)</span>求得<span class=\"math inline\">\\(w\\)</span></p>\n<p>5)求偏移项<span class=\"math inline\">\\(b\\)</span></p>\n<p>对于<span class=\"math inline\">\\(\\lambda_{i}>0\\)</span>,<span\nclass=\"math inline\">\\(g_{i}(w)=0\\)</span>,满足这个条件的点均为支持向量,可以取任一支持向量,带入<span\nclass=\"math inline\">\\(y_{s}\\left(w\nx_{s}+b\\right)=1\\)</span>,即可求得<span\nclass=\"math inline\">\\(b\\)</span></p>\n<p>或者采取更为鲁棒的做法,取所有支持向量各计算出一个<span\nclass=\"math inline\">\\(b\\)</span>,然后取均值,即按下式求取:</p>\n<p><span class=\"math display\">\\[b=\\frac{1}{|S|} \\sum_{s \\in\nS}\\left(y_{s}-w x_{s}\\right)\\]</span></p>\n<p>6)构造分类超平面:<span class=\"math inline\">\\(w^{T}\nx+b=0\\)</span></p>\n<p>分类决策函数:<span\nclass=\"math inline\">\\(f(x)=\\operatorname{sign}\\left(w^{T}\nx+b\\right)\\)</span></p>\n<p>其中,<span class=\"math inline\">\\(sign(x)\\)</span>是阶跃函数:</p>\n<p><span\nclass=\"math display\">\\[\\operatorname{sign}(x)=\\left\\{\\begin{array}{rl}{-1}\n& {x<0} \\\\ {0} & {x=0} \\\\ {1} &\n{x>0}\\end{array}\\right.\\]</span></p>\n<h2 id=\"线性不可分场景\">线性不可分场景</h2>\n<p>以上我们讨论的都是线性可分的情况,实际场景中,通常遇到的数据分布都是线性不可分的。如以下场景,此场景下,求得的分类面损失将会超出我们的容忍范围。</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511221306.png\" width=\"60%\"></p>\n<p><strong>SVM的做法是将二维线性不可分样本映射到高维空间中,让样本点在高维空间线性可分</strong>,比如下列动图演示的做法</p>\n<p><img src=\"https://images.bumpchicken.cn/img/201485427.gif\" width=\"80%\"></p>\n<p>对于在有限维度向量空间中线性不可分的样本,我们将其映射到更高维度的向量空间里,再通过间隔最大化的方式,学习得到的支持向量机称之为<strong>非线性\nSVM。</strong></p>\n<p>我们用 x 表示原来的样本点,用 <span\nclass=\"math inline\">\\(\\kappa(x)\\)</span>表示 <span\nclass=\"math inline\">\\(x\\)</span>\n映射到特征新的特征空间后到新向量。那么优化问题可以表示为:</p>\n<p><span class=\"math display\">\\[\\max _{\\boldsymbol{\\lambda}}\n\\sum_{i=1}^{m} \\lambda{i}-\\frac{1}{2} \\sum_{i=1}^{m} \\sum_{j=1}^{m}\n\\lambda{i} \\lambda{j} y_{i} y_{j} \\kappa\\left(\\boldsymbol{x}_{i},\n\\boldsymbol{x}_{j}\\right)\\]</span></p>\n<p><span class=\"math display\">\\[\\text {s.t.} \\quad \\sum_{i=1}^{n}\n\\lambda_{i} y_{i}=0, \\quad \\lambda_{i} \\geq 0\\]</span></p>\n<p>我们常用的核函数有:</p>\n<ul>\n<li><p>线性核函数</p>\n<p><span class=\"math display\">\\[k\\left(x_{i}, x_{j}\\right)=x_{i}^{T}\nx_{j}\\]</span></p></li>\n<li><p>多项式核函数</p>\n<p><span class=\"math display\">\\[k\\left(x_{i},\nx_{j}\\right)=\\left(x_{i}^{T} x_{j}\\right)^{d}\\]</span></p></li>\n<li><p>高斯核函数</p>\n<p><span class=\"math display\">\\[k\\left(x_{i}, x_{j}\\right)=\\exp\n\\left(-\\frac{\\left\\|x_{i}-x_{j}\\right\\|}{2\n\\delta^{2}}\\right)\\]</span></p></li>\n</ul>\n<p>理论上高斯核函数可以将数据映射到无限维。</p>\n<h2 id=\"总结\">总结</h2>\n<p>SVM作为一种经典的机器学习分类方法,具有以下优点:</p>\n<ol type=\"1\">\n<li>采用核技巧之后,可以处理非线性分类/回归任务</li>\n<li>能找出对任务至关重要的关键样本(即支持向量)</li>\n<li>最终决策函数只由少数的支持向量所确定,计算的复杂性取决于支持向量的数目,而不是样本空间的维数,这在某种意义上避免了“维数灾难”</li>\n</ol>\n<p>同时,也具有以下缺点:</p>\n<ol type=\"1\">\n<li>训练时间长。当采用 SMO\n算法时,由于每次都需要挑选一对参数,因此时间复杂度为 O(N2),其中 N\n为训练样本的数量</li>\n<li>当采用核技巧时,如果需要存储核矩阵,则空间复杂度为 O(N2)</li>\n<li>模型预测时,预测时间与支持向量的个数成正比。当支持向量的数量较大时,预测计算复杂度较高</li>\n</ol>\n<h2 id=\"参考资料\">参考资料</h2>\n<ol type=\"1\">\n<li><p>《机器学习》 周志华</p></li>\n<li><p><a\nhref=\"https://zhuanlan.zhihu.com/p/26514613\">浅谈最优化问题中的KKT条件</a></p></li>\n</ol>\n","site":{"data":{}},"excerpt":"<p><img src=\"https://images.bumpchicken.cn/img/20220511220448.png\" width=\"60%\">\nSupport Vector Machine(支持向量机),是一种非常经典的机器学习分类方法。","more":"</p>\n<h2 id=\"svm基本原理\">SVM基本原理</h2>\n<p>Support Vector\nMachine(支持向量机),是一种非常经典的机器学习分类方法。它有严格的数学理论支持,可解释性强,不依靠统计方法,并且利用核函数技巧能有效解决一些线性不可分的场景。</p>\n<p>给定样本集:<span\nclass=\"math inline\">\\(D=\\left\\{\\left(\\boldsymbol{x}_{1},\ny_{1}\\right),\\left(\\boldsymbol{x}_{2}, y_{2}\\right),\n\\ldots,\\left(\\boldsymbol{x}_{m}, y_{m}\\right)\\right\\}, y_{i}\n\\in\\{-1,+1\\}\\)</span>,如下所示,假设左上方的点为正样本,右下方的点为负样本</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511220958.png\" width=\"60%\"></p>\n<p>寻找一个最优的分类面,使得依赖这个分类面产生的分类结果最具鲁棒性,体现在图上就是分类样本离这个分类平面尽可能远,具有最大的间隔。将这个分类的平面称为<strong>最大间隔超平面</strong>,离这个最大间隔超平面最近的点称之为<strong>支持向量(Support\nVector),分类超平面的构建只与这些少数的点有关,这也是为什么它叫作支持向量机的原因。</strong></p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511221109.png\" width=\"60%\"></p>\n<h2 id=\"svm最优化问题\">SVM最优化问题</h2>\n<p>SVM\n目的是找到各类样本点到超平面的距离最远,也就是找到最大间隔超平面。任意超平面可以用下面这个线性方程来描述:</p>\n<p><span class=\"math display\">\\[\\boldsymbol{w}^{\\mathrm{T}}\n\\boldsymbol{x}+b=0\\]</span></p>\n<p>我们知道,二维空间点(x, y) 到直线 Ax+By+C=0的距离公式为:</p>\n<p><span class=\"math display\">\\[\\frac{|A x+B\ny+C|}{\\sqrt{A^{2}+B^{2}}}\\]</span></p>\n<p>扩展到n维空间,点<span class=\"math inline\">\\(x=\\left(x_{1}, x_{2}\n\\dots x_{n}\\right)\\)</span>到<span class=\"math inline\">\\(w^{T}\nx+b=0\\)</span>距离为:</p>\n<p><span class=\"math display\">\\[\\frac{\\left|w^{T}\nx+b\\right|}{\\|w\\|}\\]</span></p>\n<p>其中,<span class=\"math inline\">\\(\\|w\\|=\\sqrt{w_{1}^{2}+\\ldots\nw_{n}^{2}}\\)</span></p>\n<p>假定分割超平面能将样本点 <strong>准确</strong>\n分为两类,设支持向量到最大间隔超平面的距离为d,则其他向量到最大间隔超平面的距离大于d。于是有:</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{array}{l}{\\frac{w^{T}\nx+b}{\\|w\\|} \\geq d \\quad y_{i}=1} \\\\ {\\frac{w^{T} x+b}{\\|w\\|} \\leq-d\n\\quad y_{i}=-1}\\end{array}\\right.\\]</span></p>\n<p>因为<span class=\"math inline\">\\(\\|w\\| d\\)</span>\n是正数,其对目标函数优化无影响,这里令其为1,因此上式不等式组可简化为:</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{array}{l}{w^{T} x+b \\geq 1\n\\quad y_{i}=1} \\\\ {w^{T} x+b \\leq-1 \\quad\ny_{i}=-1}\\end{array}\\right.\\]</span></p>\n<p>合并两个不等式,得到:<span class=\"math inline\">\\(y_{i}\\left(w^{T}\nx+b\\right) \\geq 1\\)</span></p>\n<p>这里,我们再来看关于超平面和支持向量的图解:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511221210.png\" width=\"60%\"></p>\n<p>支持向量到最大间隔超平面的距离为:</p>\n<p><span class=\"math display\">\\[d=\\frac{\\left|w^{T}\nx+b\\right|}{\\|w\\|}\\]</span></p>\n<p>最大化这个距离:</p>\n<p><span class=\"math display\">\\[\\max 2 * \\frac{\\left|w^{T}\nx+b\\right|}{\\|w\\|}\\]</span></p>\n<p>对于确定的样本集来说,<span class=\"math inline\">\\(\\left|w^{T}\nx+b\\right|\\)</span>是个常量,因此目标函数变为:<span\nclass=\"math inline\">\\(\\max \\frac{2}{\\|w\\|}\\)</span>,即<span\nclass=\"math inline\">\\(\\min \\frac{1}{2}\\|w\\|\\)</span></p>\n<p>为了方便计算,去除根号,目标函数转化为:<span\nclass=\"math inline\">\\(\\min \\frac{1}{2}\\|w\\|^{2}\\)</span></p>\n<p>因此得到SVM的优化问题:</p>\n<p><span class=\"math display\">\\[\\min \\frac{1}{2}\\|w\\|^{2} \\]</span></p>\n<p><span class=\"math display\">\\[{s.t.}\\quad y_{i}\\quad\\left(w^{T}\nx_{i}+b\\right) \\geq 1\\]</span></p>\n<h2 id=\"kkt条件\">KKT条件</h2>\n<p>上述最优化问题约束条件是不等式。如果是等式约束,可以直接用Lagrange乘数法求解,即对于下述最优化问题</p>\n<p><span class=\"math display\">\\[\\begin{array}{c}{\\min f\\left(x_{1},\nx_{2}, \\ldots, x_{n}\\right)} \\\\ {\\text { s.t. } \\quad h_{k}\\left(x_{1},\nx_{2}, \\ldots, x_{n}\\right)=0}\\end{array}\\]</span></p>\n<p>我们可以构造拉格朗日函数:<span class=\"math inline\">\\(L(x,\n\\lambda)=f(x)+\\sum_{k=1}^{l} \\lambda_{k}\nh_{k}(x)\\)</span>,然后分别对<span\nclass=\"math inline\">\\(x\\)</span>,<span\nclass=\"math inline\">\\(\\lambda\\)</span>求偏导,求得可能的极值点</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{array}{ll}{\\frac{\\partial\nL}{\\partial x_{i}}=0} & {i=1,2, \\ldots, n} \\\\ {\\frac{\\partial\nL}{\\partial \\lambda_{k}}=0} & {k=1,2, \\ldots,\nl}\\end{array}\\right.\\]</span></p>\n<p>那么对于不等式约束条件,做法是引入一个松弛变量,然后将该松弛变量也视为待优化变量。以SVM优化问题为例:</p>\n<p><span class=\"math display\">\\[\\begin{aligned} \\min f(w) &=\\min\n\\frac{1}{2}\\|w\\|^{2} \\\\ \\text {s.t.} & g_{i}(w)=1-y_{i}\\left(w^{T}\nx_{i}+b\\right) \\leq 0 \\end{aligned}\\]</span></p>\n<p>引入松弛变量 <span\nclass=\"math inline\">\\(a_{i}^{2}\\)</span>,得到新的约束条件: <span\nclass=\"math inline\">\\(h_{i}\\left(w,\na_{i}\\right)=g_{i}(w)+a_{i}^{2}=0\\)</span>,将不等式约束变为等式约束,得到新的拉格朗日函数:</p>\n<p><span class=\"math display\">\\[\\begin{aligned} L(w, \\lambda, a)\n&=\\frac{1}{2} f(w)+\\sum_{i=1}^{n} \\lambda_{i} h_{i}(w) \\\\\n&=\\frac{1}{2} f(w)+\\sum_{i=1}^{n}\n\\lambda_{i}\\left[g_{i}(w)+a_{i}^{2}\\right] \\quad \\lambda_{i} \\geq 0\n\\end{aligned}\\]</span></p>\n<p>(<strong>注意到,这里有<span\nclass=\"math inline\">\\(\\lambda_{i}>=0\\)</span>,在拉格朗日乘数法中,没有非负的要求,关于这里为何\n<span class=\"math inline\">\\(\\lambda_{i}>=0\\)</span>\n可以通过几何性质来证明,有兴趣的可以查阅相关资料</strong>。)</p>\n<p>根据等式约束条件,有:</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{array}{c}{\\frac{\\partial\nL}{\\partial w_{i}}=\\frac{\\partial f}{\\partial w_{i}}+\\sum_{i=1}^{n}\n\\lambda_{i} \\frac{\\partial g_{i}}{\\partial w_{i}}=0} \\\\ {\\frac{\\partial\nL}{\\partial a_{i}}=2 \\lambda_{i} a_{i}=0} \\\\ {\\frac{\\partial L}{\\partial\n\\lambda_{i}}=g_{i}(w)+a_{i}^{2}=0} \\\\ {\\lambda_{i} \\geq\n0}\\end{array}\\right.\\]</span></p>\n<p>第二个式子,<span class=\"math inline\">\\(2\\lambda_{i}\na_{i}=0\\)</span>,有两种情况:</p>\n<ol type=\"1\">\n<li><span class=\"math inline\">\\(\\lambda_{i}\\)</span> 为0,<span\nclass=\"math inline\">\\(a_{i}\\)</span>不为0:由于<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>为0,这时候约束<span\nclass=\"math inline\">\\(g_{i}(w)\\)</span>不起作用,并且<span\nclass=\"math inline\">\\(g_{i}(w)<0\\)</span></li>\n<li><span class=\"math inline\">\\(\\lambda_{i}\\)</span>不为0,<span\nclass=\"math inline\">\\(a_{i}\\)</span>为0:这时<span\nclass=\"math inline\">\\(g_{i}(w)\\)</span>起约束作用,并且<span\nclass=\"math inline\">\\(g_{i}(w)=0\\)</span></li>\n</ol>\n<p>因此,方程组可转换为:</p>\n<p><span class=\"math display\">\\[\\left\\{\\begin{aligned} \\frac{\\partial\nL}{\\partial w_{i}} &=\\frac{\\partial f}{\\partial\nw_{i}}+\\sum_{j=1}^{n} \\lambda_{j} \\frac{\\partial g_{j}}{\\partial\nw_{i}}=0 \\\\ \\lambda_{i} g_{i}(w) &=0 \\\\ g_{i}(w) & \\leq 0 \\\\\n\\lambda_{i} & \\geq 0 \\end{aligned}\\right.\\]</span></p>\n<p>以上便是不等式约束优化问题的__KKT(Karush-Kuhn-Tucker)条件__,<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>称为KKT乘子。从这个方程组可以得到以下讯息:</p>\n<ol type=\"1\">\n<li>对于支持向量 <span class=\"math inline\">\\(g_{i}(w)=0\\)</span>,<span\nclass=\"math inline\">\\(\\lambda_{i}>0\\)</span>即可</li>\n<li>对于非支持向量 <span\nclass=\"math inline\">\\(g_{i}(w)<0\\)</span>,但要求 <span\nclass=\"math inline\">\\(\\lambda_{i}=0\\)</span></li>\n</ol>\n<h2 id=\"求解svm最优化问题\">求解SVM最优化问题</h2>\n<p>利用KKT条件,我们可以求解SVM最优化问题:</p>\n<p><span class=\"math display\">\\[\\min _{w}\n\\frac{1}{2}\\|w\\|^{2}\\]</span></p>\n<p><span class=\"math display\">\\[\\text {s.t. } \\quad g_{i}(w, b)=1-y_{i}\n\\left(w^{T} x_{i}+b\\right) \\quad \\leq 0, \\quad i=1,2, \\ldots,\nn\\]</span></p>\n<p><strong>Step 1</strong>: 构造拉格朗日函数</p>\n<p><span class=\"math display\">\\[\\begin{aligned} L(w, b, \\lambda)=&\n\\frac{1}{2}\\|w\\|^{2}+\\sum_{i=1}^{n} \\lambda_{i}\\left[1-y_{i}\\left(w^{T}\nx_{i}+b\\right)\\right] \\\\ & \\text {s.t.} \\quad \\lambda_{i} \\geq 0\n\\end{aligned}\\]</span></p>\n<p>假设目标函数最小值为p, 即<span\nclass=\"math inline\">\\(\\frac{1}{2}\\|w\\|^{2} = p\\)</span>,因为<span\nclass=\"math inline\">\\(\\sum_{i=1}^{n} \\lambda_{i}\\left[1-y_{i}\\left(w^{T}\nx_{i}+b\\right)\\right] <= 0\\)</span>,即<span\nclass=\"math inline\">\\(L(w, b, \\lambda) <=\np\\)</span>,为了找到最优的参数<span\nclass=\"math inline\">\\(\\lambda\\)</span>使得<span\nclass=\"math inline\">\\(L(w, b, \\lambda)\\)</span>接近p,问题转换为:<span\nclass=\"math inline\">\\(\\max _{\\lambda} L(w, b,\n\\lambda)\\)</span>,即:</p>\n<p><span class=\"math display\">\\[\\begin{array}{c}{\\min _{w} \\max\n_{\\lambda} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq\n0}\\end{array}\\]</span></p>\n<p><strong>Step 2</strong>:利用对偶性转换求解问题:</p>\n<p>对偶问题其实就是将:</p>\n<p><span class=\"math display\">\\[\\begin{array}{c}{\\min _{w} \\max\n_{\\lambda} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq\n0}\\end{array}\\]</span></p>\n<p>转化为:<span class=\"math inline\">\\(\\begin{array}{c}{\\max _{\\lambda}\n\\min _{w} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq\n0}\\end{array}\\)</span></p>\n<p>假设有函数<span class=\"math inline\">\\(f\\)</span>,我们有:</p>\n<p><span class=\"math display\">\\[min \\space max f >= max \\space min\nf\\]</span></p>\n<p>即最大的里面挑出来最小的也要比最小的里面挑出来最大的要大,这是一种\n<strong>弱对偶关系</strong>,而 <strong>强对偶关系</strong>\n是当等号成立时,即:</p>\n<p><span class=\"math display\">\\[min \\space max f == max \\space min\nf\\]</span></p>\n<p>当<span\nclass=\"math inline\">\\(f\\)</span>是凸优化问题时,等号成立,而我们之前求的KKT条件是强对偶性的\n<strong>充要条件</strong></p>\n<p>因此,对<span class=\"math inline\">\\(\\begin{array}{c}{\\max _{\\lambda}\n\\min _{w} L(w, b, \\lambda)} \\\\ {\\text { s.t. } \\quad \\lambda_{i} \\geq\n0}\\end{array}\\)</span>进行求解:</p>\n<p>1)对参数<span class=\"math inline\">\\(w\\)</span>和<span\nclass=\"math inline\">\\(b\\)</span>求偏导数:</p>\n<p><span class=\"math display\">\\[\\frac{\\partial L}{\\partial\nw}=w-\\sum_{i=1}^{n} \\lambda_{i} x_{i} y_{i}=0\\]</span></p>\n<p><span class=\"math display\">\\[\\frac{\\partial L}{\\partial\nb}=\\sum_{i=1}^{n} \\lambda_{i} y_{i}=0\\]</span></p>\n<p>得到:</p>\n<p><span class=\"math display\">\\[\\sum_{i=1}^{n} \\lambda_{i} x_{i}\ny_{i}=w\\]</span></p>\n<p><span class=\"math display\">\\[\\sum_{i=1}^{n} \\lambda_{i}\ny_{i}=0\\]</span></p>\n<p>2)将1)中求导结果代回 <span class=\"math inline\">\\(L(w, b,\n\\lambda)\\)</span>中,得到:</p>\n<p><span class=\"math display\">\\[\\begin{aligned} L(w, b, \\lambda)\n&=\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j}\ny_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+\\sum_{i=1}^{n}\n\\lambda_{i}-\\sum_{i=1}^{n} \\lambda_{i} y_{i}\\left(\\sum_{j=1}^{n}\n\\lambda_{j} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+b\\right) \\\\\n&=\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j}\ny_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)+\\sum_{i=1}^{n}\n\\lambda_{i}-\\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i}\ny_{j}\\left(x_{i} \\cdot x_{j}\\right)-\\sum_{i=1}^{n} \\lambda_{i} y_{i} b\n\\\\ &=\\sum_{j=1}^{n} \\lambda_{i}-\\frac{1}{2} \\sum_{i=1}^{n}\n\\sum_{j=1}^{n} \\lambda_{i} \\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot\nx_{j}\\right) \\end{aligned}\\]</span></p>\n<p>3)利用SMO(Sequential Minimal Optimization)求解</p>\n<p><span class=\"math inline\">\\(\\max _{\\lambda}\\left[\\sum_{j=1}^{n}\n\\lambda_{i}-\\frac{1}{2} \\sum_{i=1}^{n} \\sum_{j=1}^{n} \\lambda_{i}\n\\lambda_{j} y_{i} y_{j}\\left(x_{i} \\cdot x_{j}\\right)\\right]\\)</span>\n<span class=\"math inline\">\\(\\text { s.t. } \\sum_{i=1}^{n} \\lambda_{i}\ny_{i}=0 \\quad \\lambda_{i} \\geq 0\\)</span></p>\n<p>这是一个二次规划问题,问题规模正比于训练样本数,我们常用\nSMO算法求解。</p>\n<p>SMO的思路是:先固定除<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>之外的参数,然后求<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>上的极值。但是我们这里有约束条件<span\nclass=\"math inline\">\\(\\sum_{i=1}^{n} \\lambda_{i}\ny_{i}=0\\)</span>,如果固定<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>之外的参数,<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>可直接由其他参数推导出来。因此这里SMO一次优化两个参数,具体步骤为:</p>\n<ul>\n<li><p>选择两个需要更新的参数<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>和<span\nclass=\"math inline\">\\(\\lambda_{j}\\)</span>,固定其他参数,于是约束变为:</p>\n<p><span class=\"math inline\">\\(\\lambda_{i} y_{i}+\\lambda_{j} y_{j}=c\n\\quad \\lambda_{i} \\geq 0, \\lambda_{j} \\geq 0\\)</span>,其中,<span\nclass=\"math inline\">\\(c=-\\sum_{k \\neq i, j} \\lambda_{k}\ny_{k}\\)</span></p>\n<p>得到:<span class=\"math inline\">\\(\\lambda_{j}=\\frac{c-\\lambda_{i}\ny_{i}}{y_{j}}\\)</span></p>\n<p>也就是说我们可以用<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>的表达式代替<span\nclass=\"math inline\">\\(\\lambda_{j}\\)</span>。这样就相当于把目标问题转化成了仅有一个约束条件的最优化问题,仅有的约束是<span\nclass=\"math inline\">\\(\\lambda_{i}>=0\\)</span></p></li>\n<li><p>对于仅有一个约束条件的最优化问题,我们完全可以在<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>上对优化目标求偏导,令导数为零,从而求出变量值<span\nclass=\"math inline\">\\(\\lambda_{i}\\)</span>的极值<span\nclass=\"math inline\">\\(\\lambda_{i_{new}}\\)</span>,然后通过<span\nclass=\"math inline\">\\(\\lambda_{i_{new}}\\)</span>求出<span\nclass=\"math inline\">\\(\\lambda_{j_{new}}\\)</span></p></li>\n<li><p>多次迭代直至收敛</p></li>\n</ul>\n<p>4)根据 <span class=\"math inline\">\\({\\sum_{i=1}^{n} \\lambda_{i} x_{i}\ny_{i}=w}\\)</span>求得<span class=\"math inline\">\\(w\\)</span></p>\n<p>5)求偏移项<span class=\"math inline\">\\(b\\)</span></p>\n<p>对于<span class=\"math inline\">\\(\\lambda_{i}>0\\)</span>,<span\nclass=\"math inline\">\\(g_{i}(w)=0\\)</span>,满足这个条件的点均为支持向量,可以取任一支持向量,带入<span\nclass=\"math inline\">\\(y_{s}\\left(w\nx_{s}+b\\right)=1\\)</span>,即可求得<span\nclass=\"math inline\">\\(b\\)</span></p>\n<p>或者采取更为鲁棒的做法,取所有支持向量各计算出一个<span\nclass=\"math inline\">\\(b\\)</span>,然后取均值,即按下式求取:</p>\n<p><span class=\"math display\">\\[b=\\frac{1}{|S|} \\sum_{s \\in\nS}\\left(y_{s}-w x_{s}\\right)\\]</span></p>\n<p>6)构造分类超平面:<span class=\"math inline\">\\(w^{T}\nx+b=0\\)</span></p>\n<p>分类决策函数:<span\nclass=\"math inline\">\\(f(x)=\\operatorname{sign}\\left(w^{T}\nx+b\\right)\\)</span></p>\n<p>其中,<span class=\"math inline\">\\(sign(x)\\)</span>是阶跃函数:</p>\n<p><span\nclass=\"math display\">\\[\\operatorname{sign}(x)=\\left\\{\\begin{array}{rl}{-1}\n& {x<0} \\\\ {0} & {x=0} \\\\ {1} &\n{x>0}\\end{array}\\right.\\]</span></p>\n<h2 id=\"线性不可分场景\">线性不可分场景</h2>\n<p>以上我们讨论的都是线性可分的情况,实际场景中,通常遇到的数据分布都是线性不可分的。如以下场景,此场景下,求得的分类面损失将会超出我们的容忍范围。</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511221306.png\" width=\"60%\"></p>\n<p><strong>SVM的做法是将二维线性不可分样本映射到高维空间中,让样本点在高维空间线性可分</strong>,比如下列动图演示的做法</p>\n<p><img src=\"https://images.bumpchicken.cn/img/201485427.gif\" width=\"80%\"></p>\n<p>对于在有限维度向量空间中线性不可分的样本,我们将其映射到更高维度的向量空间里,再通过间隔最大化的方式,学习得到的支持向量机称之为<strong>非线性\nSVM。</strong></p>\n<p>我们用 x 表示原来的样本点,用 <span\nclass=\"math inline\">\\(\\kappa(x)\\)</span>表示 <span\nclass=\"math inline\">\\(x\\)</span>\n映射到特征新的特征空间后到新向量。那么优化问题可以表示为:</p>\n<p><span class=\"math display\">\\[\\max _{\\boldsymbol{\\lambda}}\n\\sum_{i=1}^{m} \\lambda{i}-\\frac{1}{2} \\sum_{i=1}^{m} \\sum_{j=1}^{m}\n\\lambda{i} \\lambda{j} y_{i} y_{j} \\kappa\\left(\\boldsymbol{x}_{i},\n\\boldsymbol{x}_{j}\\right)\\]</span></p>\n<p><span class=\"math display\">\\[\\text {s.t.} \\quad \\sum_{i=1}^{n}\n\\lambda_{i} y_{i}=0, \\quad \\lambda_{i} \\geq 0\\]</span></p>\n<p>我们常用的核函数有:</p>\n<ul>\n<li><p>线性核函数</p>\n<p><span class=\"math display\">\\[k\\left(x_{i}, x_{j}\\right)=x_{i}^{T}\nx_{j}\\]</span></p></li>\n<li><p>多项式核函数</p>\n<p><span class=\"math display\">\\[k\\left(x_{i},\nx_{j}\\right)=\\left(x_{i}^{T} x_{j}\\right)^{d}\\]</span></p></li>\n<li><p>高斯核函数</p>\n<p><span class=\"math display\">\\[k\\left(x_{i}, x_{j}\\right)=\\exp\n\\left(-\\frac{\\left\\|x_{i}-x_{j}\\right\\|}{2\n\\delta^{2}}\\right)\\]</span></p></li>\n</ul>\n<p>理论上高斯核函数可以将数据映射到无限维。</p>\n<h2 id=\"总结\">总结</h2>\n<p>SVM作为一种经典的机器学习分类方法,具有以下优点:</p>\n<ol type=\"1\">\n<li>采用核技巧之后,可以处理非线性分类/回归任务</li>\n<li>能找出对任务至关重要的关键样本(即支持向量)</li>\n<li>最终决策函数只由少数的支持向量所确定,计算的复杂性取决于支持向量的数目,而不是样本空间的维数,这在某种意义上避免了“维数灾难”</li>\n</ol>\n<p>同时,也具有以下缺点:</p>\n<ol type=\"1\">\n<li>训练时间长。当采用 SMO\n算法时,由于每次都需要挑选一对参数,因此时间复杂度为 O(N2),其中 N\n为训练样本的数量</li>\n<li>当采用核技巧时,如果需要存储核矩阵,则空间复杂度为 O(N2)</li>\n<li>模型预测时,预测时间与支持向量的个数成正比。当支持向量的数量较大时,预测计算复杂度较高</li>\n</ol>\n<h2 id=\"参考资料\">参考资料</h2>\n<ol type=\"1\">\n<li><p>《机器学习》 周志华</p></li>\n<li><p><a\nhref=\"https://zhuanlan.zhihu.com/p/26514613\">浅谈最优化问题中的KKT条件</a></p></li>\n</ol>"},{"title":"贝叶斯优化原理及应用","date":"2020-12-29T04:07:00.000Z","mathjax":true,"top":888,"_content":"<img src=\"https://images.bumpchicken.cn/img/20220509122455.png\">\n\n贝叶斯优化(Bayesian Optimization, BO)是一种黑盒优化算法,用于求解表达式未知的函数极值问题。算法使用高斯过程回归对一组采样点的函数值进行概率建模,预测出任意点处函数值的概率分布,然后构造采集函数(Acquistion Function),用于衡量每一个点值得探索(explore)的程度,求解采集函数的极值从而确定下一个采样点,最后返回这组采样点的极值作为函数的极值。\n\n<!--more-->\n\n## 黑盒优化问题\n训练机器学习模型过程中,会有很多模型参数之外的参数,如学习率,卷积核大小等,再比如训练xgboost时,树的最大深度、采样率等参数都会影响训练结果,这些参数我们将其称为超参数。假设一组超参数组合$X=x_{1},x_{2},...,x_{n}$,存在一个未知函数 $f:x \\rightarrow \\mathbb {R}$,我们需要在$x \\in X$找到一组最佳参数组合$x^{*}$使得:\n\n$$x^{*} = \\underset{x\\in X}{argmin} f(x) $$\n\n当$f$是凸函数并且定义域也是凸的时候,可以通过凸优化手段(梯度下降、L_BFGS等)来求解。但是超参数优化属于黑盒优化问题,$f$不一定是凸函数,并且是未知的,在优化过程中只能得到函数的输入和输出,不能获取目标函数的表达式和梯度信息,这里的$f$通常还是计算代价非常昂贵的函数,因此优化过程会比较困难,尤其是当超参数数量大的情况。常用的超参数优化方法有网格搜索(Grid Search),随机搜索(Random Search),遗传算法(粒子群优化、模拟退火等)以及本文要介绍的贝叶斯优化方法。\n\n下面介绍两种最基本的超参调优方法: 网格搜索法和随机搜索法\n\n- 网格搜索法\n\n 网格搜索法搜索一组离散的取值情况,得到最优参数值。如果是连续型的超参数,则需要对其定义域进行网格划分,然后选取典型值计算。网格搜索法本质上是一种穷举法,对待调优参数进行全排列组合,逐一计算$f$,然后选取最小的$f$时的参数组合,如下代码所示,给定参数候选项,我们可以列出所有的参数组合\n```python\nfrom itertools import product\ntuning_params = {'a':[1,2,3], 'b':[4,5]} # 待优化参数可选项\nfor conf in product(*tuning_params.values()):\n print({k:v for k,v in zip(tuning_params.keys(), conf)}) # 生成参数组合\n```\n输出:\n```python\n{'a': 1, 'b': 4}\n{'a': 1, 'b': 5}\n{'a': 2, 'b': 4}\n{'a': 2, 'b': 5}\n{'a': 3, 'b': 4}\n{'a': 3, 'b': 5}\n```\n随着待调优参数增加,生成的全排列组合数量将非常巨大,计算代价过于昂贵\n- 随机搜索法\n 相比于网格搜索法,随机搜索的做法是将超参数随机地取某些值,设置一个最大迭代次数,比较每次迭代中不同取值算法的输出,得到最优超参数组合。而随机取值的方法也有多种不同的做法,常用的做法是采用均匀分布的随机数进行搜索,或者采用一些启发式的搜索策略(粒子群优化算法),这里不展开赘述。随机搜索并不总能找到全局最优解,但是通常认为随机搜索比网格搜索更优,其可以花费更少的计算代价得到相近的结果。\n\n__无论是网格搜索法还是随机搜索法,每一次进行迭代计算的时候,都未曾考虑已经搜索过的空间,即搜索过的空间未对下一次搜索产生任何指导作用,因此可能存在很多无效搜索。不同于网格搜索和随机搜索法,贝叶斯优化则能够通过高斯过程回归有效利用先验的搜索空间进行下一次搜索参数的选择,能大大减少迭代次数__\n\n## 理论准备\n经典的贝叶斯优化利用高斯过程(Gaussian Process, GP)对$f$进行概率建模,在介绍贝叶斯优化之前,有必要了解一下高斯过程回归的相关知识\n\n### 高斯过程\n高斯过程用于对一组随着时间增长的随机向量进行建模,在任意时刻,某个向量的所有子向量均服从高斯分布。\n假设有连续型随机变量序列$x_{1},x_{2},...,x_{T}$,如果该序列中任意数量的随机变量构成的向量$X_{t_{1}, ... ,t_{k}} = [x_{t_{1}} \\space ... \\space x_{t_{k}}]^{T}$均服从多维正态分布,则称次随机变量序列为高斯过程。\n\n特别地,假设当前有k个随机变量$x_{1},...,x{k}$,它们服从k维正态分布$N( \\mu_{k}, \\sum _{k} )$,其中均值向量$N( \\mu_{k}, \\sum _{k} )$,协方差矩阵$\\sum _{k} \\in \\mathbb R^{k*k}$\n\n当加入一个新的随机变量$x_{k+1}$之后,随机向量$x_{1},x_{2},...,x_{k},x_{k+1}$服从k+1维正态分布$\\mu_{k+1} \\in \\mathbb{R}^{k+1}$,其中均值向量$\\mu_{k+1} \\in \\mathbb{R}^{k+1}$,协方差矩阵$\\sum _{k+1} \\in \\mathbb R^{(k+1)*(k+1)}$\n\n由于正态分布的积分能够得到解析解,因此可以方便地得到边缘概率于条件概率。\n\n### 高斯过程回归\n\n机器学习中,算法通常是根据输入值$x$预测出一个最佳输出值$y$,用于分类或回归。某些情况下我们需要的不是预测出一个函数值,而是给出这个函数值的后验概率分布$p(y|x)$。对于实际应用问题,一般是给定一组样本点$x_{i}, \\space i=1,…,l$,基于此拟合出一个假设函数,给定输入值$x$,预测其标签值或者后验概率$p(y|x)$,高斯过程回归对应后者。\n\n高斯过程回归(Gaussian Process Regression, GPR)对表达式未知的函数的一组函数值进行概率建模,给出函数值的概率分布。嘉定给定某些点$x_{i}, i= 1,…,t$,以及在这些点处的函数值$f(x_{i})$,GPR能够根据这些点,拟合该未知函数,那么对于任意给定的$x$,就可以预测出$f(x)$,并且能够给出预测结果的置信度。\n\nGPR假设黑盒函数在各个点处的函数值$f(x)$都是随机变量,它们构成的随机向量服从多维正态分布。假设有t个采样点$x_{1},…,x_{t}$,在这些点处的函数值构成向量:\n\n$$f(x_{1:t}) = [f(x_{1} \\space ... \\space f(x_{t})]$$\n\nGPR假设此向量服从t维正态分布:\n\n$$f(x_{1:t}) \\sim N(\\mu(x_{1:t}), \\sum(x_{1:t},x_{1:t}))$$\n\n其中,$\\mu(x_{1:t})=[\\mu(x_{1}),…,\\mu(x_{t})]$是高斯分布的均值向量,$\\sum(x_{1:t},x_{1:t})$是协方差矩阵\n\n$$\\left[\\begin{array}{ccc}\n\\operatorname{cov}\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots & \\operatorname{cov}\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\cdots & \\ldots & \\ldots \\\\\n\\operatorname{cov}\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots & \\operatorname{cov}\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots & k\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\ldots & \\ldots & \\ldots \\\\\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots & k\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]$$\n\n问题的关键是如何根据样本值计算出正态分布的均值向量和协方差矩阵,均值向量是通过均值函数$\\mu(x)$根据每个采样点x计算构造的,可简单令$\\mu(x)=c$,或者将均值设置为0,因为即使均值设置为常数,由于有方差的作用,依然能够对数据进行有效建模。\n\n协方差通过核函数$k(x,x^{'})$计算得到,也称为协方差函数,协方差函数需要满足以下要求:\n\n1. 距离相近的样本点$x$和$x^{'}$之间有更大的正协方差值,因为相近的两个点的函数值有更强的相关性\n2. 保证协方差矩阵是对称半正定矩阵\n\n常用的是高斯核和Matern核,高斯核定义为:\n\n$$k\\left(\\mathbf{x}_{1}, \\mathbf{x}_{2}\\right)=\\alpha_{0} \\exp \\left(-\\frac{1}{2 \\sigma^{2}}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|^{2}\\right)$$\n\nMatern核定义为:\n\n$$k\\left(\\mathbf{x}_{1}, \\mathbf{x}_{2}\\right)=\\frac{2^{1-v}}{\\Gamma(v)}\\left(\\sqrt{2 v}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|\\right)^{v} K_{v}\\left(\\sqrt{2 v}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|\\right)$$\n\n其中$\\Gamma$是伽马函数,$K_{v}$是贝塞尔函数(Bessel function),$v$是人工设定的正参数。用核函数计算任意两点之间的核函数值,得到核函数矩阵$K$作为协方差矩阵的估计值:\n\n$$\\mathbf{K}=\\left[\\begin{array}{ccc}\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots & k\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\ldots & \\ldots & \\ldots \\\\\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots & k\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]$$\n\n在计算出均值向量和协方差矩阵之后,可以根据此多维正态分布预测$f(x)$在任意点处的概率分布。假设已经得到了一组样本$X_{1:t}$,以及对应的函数值$f(x_{1:t})$,如果要预测新的点$x$的函数值$f(x)$的期望$\\mu(x)$和方差$\\sigma^{2}(x)$,令$x_{t+1}=x$,加入该点后,$f(x_{1:t+1})$服从$t+1$维正态分布,即:\n\n$$\\left[\\begin{array}{c}\nf\\left(\\mathbf{x}_{1: t}\\right) \\\\\nf\\left(\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right] \\sim N\\left(\\left[\\begin{array}{c}\n\\mu\\left(\\mathbf{x}_{1: t}\\right) \\\\\n\\mu\\left(\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right],\\left[\\begin{array}{cc}\n\\mathbf{K} & \\mathbf{k} \\\\\n\\mathbf{k}^{\\mathrm{T}} & k\\left(\\mathbf{x}_{t+1}, \\mathbf{x}_{t+1}\\right)\n\\end{array}\\right]\\right)$$\n\n在已知$f(x_{1:t})$的情况下,$f(x_{t+1})$服从一维正态分布,即:\n\n$$f\\left(\\mathbf{x}_{t+1}\\right) \\mid f\\left(\\mathbf{x}_{1: t}\\right) \\sim N\\left(\\mu, \\sigma^{2}\\right)$$\n\n可以计算出对应的均值和方差,公式如下:\n\n$$\\begin{array}{l}\n\\mu=\\mathbf{k}^{\\mathrm{T}} \\mathbf{K}^{-1}\\left(f\\left(\\mathbf{x}_{1: t}\\right)-\\mu\\left(\\mathbf{x}_{1: t}\\right)\\right)+\\mu\\left(\\mathbf{x}_{t+1}\\right) \\\\\n\\sigma^{2}=k\\left(\\mathbf{x}_{t+1}, \\mathbf{x}_{t+1}\\right)-\\mathbf{k}^{\\mathrm{T}} \\mathbf{K}^{-1} \\mathbf{k}\n\\end{array}$$\n\n计算均值利用了已有采样点处函数值$f(x_{1:t})$,方差只与协方差值有关,与$f(x_{1:t})$无关\n\n### GPR代码实现\n\n- 定义高斯核\n\n```python\ndef kernel(X1, X2, l=1.0, sigma_f=1.0):\n '''\n X1: Array of m points (m x d).\n X2: Array of n points (n x d).\n Returns:\n Covariance matrix (m x n).\n '''\n sqdist = np.sum(X1**2, 1).reshape(-1, 1) + np.sum(X2**2, 1) - 2 * np.dot(X1, X2.T)\n return sigma_f**2 * np.exp(-0.5 / l**2 * sqdist)\n```\n\n- 计算均值和协方差矩阵\n\n```python\ndef posterior_predictive(X_s, X_train, Y_train, l=1.0, sigma_f=1.0, sigma_y=1e-8):\n '''\n 根据先验数据点计算均值向量和协方差矩阵\n Args:\n X_s: New input locations (n x d).\n X_train: Training locations (m x d).\n Y_train: Training targets (m x 1).\n l: Kernel length parameter.\n sigma_f: Kernel vertical variation parameter.\n sigma_y: Noise parameter.\n Returns:\n Posterior mean vector (n x d) and covariance matrix (n x n).\n '''\n K = kernel(X_train, X_train, l, sigma_f) + sigma_y**2 * np.eye(len(X_train))\n K_s = kernel(X_train, X_s, l, sigma_f)\n K_ss = kernel(X_s, X_s, l, sigma_f) + 1e-8 * np.eye(len(X_s))\n K_inv = np.linalg.inv(K)\n mu_s = K_s.T.dot(K_inv).dot(Y_train) # 均值向量, 注意均值函数被设置为 0\n cov_s = K_ss - K_s.T.dot(K_inv).dot(K_s) # 协方差矩阵\n return mu_s, cov_s\n```\n\n- GPR拟合效果绘制\n\n```Python\ndef plot_gp(mu, cov, X, X_train=None, Y_train=None, samples=[]):\n # 定义gp绘图函数\n X = X.ravel()\n mu = mu.ravel()\n uncertainty = 1.96 * np.sqrt(np.diag(cov)) # 1.96倍标准差对应95%置信度区间\n\n plt.fill_between(X, mu + uncertainty, mu - uncertainty, alpha=0.1)\n plt.plot(X, mu, label='Mean')\n for i, sample in enumerate(samples):\n plt.plot(X, sample, lw=1, ls='--', label=f'Sample {i+1}')\n if X_train is not None:\n plt.plot(X_train, Y_train, 'rx')\n plt.legend()\n\nX = np.arange(-5, 5, 0.2).reshape(-1, 1)\nX_train = np.array([-4, -3, -2, -1, 1]).reshape(-1, 1)\nY_train = np.sin(X_train)\n\n# 计算均值向量和协方差矩阵\nmu_s, cov_s = posterior_predictive(X, X_train, Y_train)\n\nsamples = np.random.multivariate_normal(mu_s.ravel(), cov_s, 1)\nplot_gp(mu_s, cov_s, X, X_train=X_train, Y_train=Y_train, samples=samples)\n```\n\n结果如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220510004441.png\" width=\"50%\" height=\"50%\">\n\n其中,红色叉代表观测值,蓝色线代表均值,浅蓝色区域代表95%置信区间。\n\n## 贝叶斯优化原理\n\n#### 基本过程\n\n以下是贝叶斯优化的过程\n\n<img src=\"https://images.bumpchicken.cn/img/20220510004715.png\">\n\n有两个主要组成部分:\n\n1. GPR。根据观测点构建高斯过程回归模型,该模型能求取任意点处的函数值及后验概率。\n2. 构造采集函数(acquisition function),用于决定本次迭代在哪个点处进行采样。\n\n算法首先初始化$n_{0}$个点,设定最大迭代次数$N$,开始循环求解,每次增加一个点,寻找下一个点时根据已经找到的$n$个候选解建立高斯回归模型,通过这个模型能得到任意点处的函数值的后验概率。然后根据后验概率构造采集函数,寻找采集函数的极大值点作为下一个搜索点,以此循环,直到达到最大迭代次数,返回$N$个解中的极大值作为最优解。\n\n采集函数的选择有很多种,最常用的是期望改进(Expected Improvement,EI),下一节介绍下EI的原理。\n\n#### 采集函数\n\n假设已经搜索了n个点,这些点中的函数极大值记为\n\n$$f_{n}^{*} = max(f(x_{1},...,f(x_{n}))$$\n\n考虑下一个搜索点,计算该点处的函数值$f(x)$,如果$f(x)>=f_{n}^{*}$,则这$n+1$个点处的函数极大值为$f(x)$,否则为$f_{n}^{*}$\n\n加入这个新的点后,函数值的改进可以记为\n\n$$e^{+} = max(0, f(x) - f_{n}^{*})$$\n\n我们的目标是找到使得上面的改进值最大的$x$,但是该点的函数值在我们找到$x$是多少前又是未知的,幸运的是我们知道$f(x)$的概率分布,因此我们可以计算在所有x处的改进值的数学期望,然后选择期望最大的$x$作为下一个搜索点。定义期望改进(EI)函数如下:\n\n$$EI_{n}(x) = E_{n}[max(0, f(x) - f_{n}^{*})]$$\n\n令$z=f(x)$,则有:\n\n$$\\begin{aligned}\n\\mathrm{EI}_{n}(\\mathbf{x}) &=\\int_{-\\infty}^{+\\infty}\\left (max(0, z-f_{n}^{*})\\right) \\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z \\\\\n&=\\int_{f_{n}^{*}}^{+\\infty}\\left(z-f_{n}^{*}\\right) \\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z\n\\end{aligned}$$\n\n换元法,得到:\n\n$$\\\\\n\\begin{array}{c}\n\\int_{f_{n}^{*}}^{+\\infty}\\left(z-f_{n}^{*}\\right) \\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z\n \\\\\n=\\left(\\mu-f_{n}^{*}\\right)\\left(1-\\Phi\\left(\\left(f_{n}^{*}-\\mu\\right) / \\sigma\\right)\\right)+\\sigma \\varphi\\left(\\left(f_{n}^{*}-\\mu\\right) / \\sigma\\right)\n\\end{array}$$\n\n其中,$\\varphi(x)$是标准正态分布的概率密度函数,$\\phi(x)$是是标准正态分布的分布函数\n\n我们的目标是求EI的极值获取下一个采样点,即\n\n$$x_{n+1} = argmax EI_{n}(x)$$\n\n现在目标函数已知,且能得到目标函数的一阶导数和二阶导数,可以通过梯度下降法或L-BFGS求解极值,这里不再展开叙述\n\n\n\n## 贝叶斯优化应用\n\nBO有许多开源的实现,scikit-optimize 以及 参考资料4 [BayesianOptimization](https://github.com/fmfn/BayesianOptimization)都封装了BO,这里我们采用参考资料4中封装的BO进行演示。\n\n1. 直接用pip安装BayesianOptimization\n\n```shell\npip install bayesian-optimization\n```\n\n2. 定义黑盒函数\n\n```python\ndef black_box_function(x, y):\n \"\"\"\n x,y 均是待调优参数\n \"\"\"\n return -x ** 2 - (y - 1) ** 2 + 1 \n```\n\n3. 初始化BO\n\n```python\nfrom bayes_opt import BayesianOptimization\n\npbounds = {'x': (2, 4), 'y': (-3, 3)} # 设定x, y 调参范围\n\n# 初始化bo\noptimizer = BayesianOptimization(\n f=black_box_function,\n pbounds=pbounds,\n random_state=1,\n)\n```\n\n4. 进行迭代\n\n```python\noptimizer.maximize(\n init_points=2, # 初始解个数\n n_iter=20, # 迭代次数\n)\nprint(optimizer.max) # 输出最大值及对应的参数组合\n```\n\n输出:\n\n<img src=\"https://images.bumpchicken.cn/img/20220511172849.png\" width=\"80%\" height=\"80%\">\n\n函数$f(x,y) = -x^{2} - (y-1)^{2} + 1$,当$x\\in[2,4]$,$y\\in[-3,3]$时,很显然,当$x=2,y=1$时能取到最大值,BO给出的解已经相当接近最优解\n\n运行了多次,BO给出的解非常稳健,如下所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220511173250.png\">\n\n## 参考资料\n\n1. 《机器学习 原理、算法与应用》 雷明著\n\n2. Frazier P I. A tutorial on bayesian optimization[J]. arXiv preprint arXiv:1807.02811, 2018.\n\n3. https://github.com/krasserm/bayesian-machine-learning\n\n4. https://github.com/fmfn/BayesianOptimization\n\n5. https://github.com/scikit-optimize/scikit-optimize\n","source":"_posts/BayesianOptimization.md","raw":"\ntitle: 贝叶斯优化原理及应用\n\ndate: 2020-12-29 12:07:00\n\ntags: \n - AutoML\n\ncategories:\n - 机器学习\n\nmathjax: true\n\ntop: 888\n\n---\n<img src=\"https://images.bumpchicken.cn/img/20220509122455.png\">\n\n贝叶斯优化(Bayesian Optimization, BO)是一种黑盒优化算法,用于求解表达式未知的函数极值问题。算法使用高斯过程回归对一组采样点的函数值进行概率建模,预测出任意点处函数值的概率分布,然后构造采集函数(Acquistion Function),用于衡量每一个点值得探索(explore)的程度,求解采集函数的极值从而确定下一个采样点,最后返回这组采样点的极值作为函数的极值。\n\n<!--more-->\n\n## 黑盒优化问题\n训练机器学习模型过程中,会有很多模型参数之外的参数,如学习率,卷积核大小等,再比如训练xgboost时,树的最大深度、采样率等参数都会影响训练结果,这些参数我们将其称为超参数。假设一组超参数组合$X=x_{1},x_{2},...,x_{n}$,存在一个未知函数 $f:x \\rightarrow \\mathbb {R}$,我们需要在$x \\in X$找到一组最佳参数组合$x^{*}$使得:\n\n$$x^{*} = \\underset{x\\in X}{argmin} f(x) $$\n\n当$f$是凸函数并且定义域也是凸的时候,可以通过凸优化手段(梯度下降、L_BFGS等)来求解。但是超参数优化属于黑盒优化问题,$f$不一定是凸函数,并且是未知的,在优化过程中只能得到函数的输入和输出,不能获取目标函数的表达式和梯度信息,这里的$f$通常还是计算代价非常昂贵的函数,因此优化过程会比较困难,尤其是当超参数数量大的情况。常用的超参数优化方法有网格搜索(Grid Search),随机搜索(Random Search),遗传算法(粒子群优化、模拟退火等)以及本文要介绍的贝叶斯优化方法。\n\n下面介绍两种最基本的超参调优方法: 网格搜索法和随机搜索法\n\n- 网格搜索法\n\n 网格搜索法搜索一组离散的取值情况,得到最优参数值。如果是连续型的超参数,则需要对其定义域进行网格划分,然后选取典型值计算。网格搜索法本质上是一种穷举法,对待调优参数进行全排列组合,逐一计算$f$,然后选取最小的$f$时的参数组合,如下代码所示,给定参数候选项,我们可以列出所有的参数组合\n```python\nfrom itertools import product\ntuning_params = {'a':[1,2,3], 'b':[4,5]} # 待优化参数可选项\nfor conf in product(*tuning_params.values()):\n print({k:v for k,v in zip(tuning_params.keys(), conf)}) # 生成参数组合\n```\n输出:\n```python\n{'a': 1, 'b': 4}\n{'a': 1, 'b': 5}\n{'a': 2, 'b': 4}\n{'a': 2, 'b': 5}\n{'a': 3, 'b': 4}\n{'a': 3, 'b': 5}\n```\n随着待调优参数增加,生成的全排列组合数量将非常巨大,计算代价过于昂贵\n- 随机搜索法\n 相比于网格搜索法,随机搜索的做法是将超参数随机地取某些值,设置一个最大迭代次数,比较每次迭代中不同取值算法的输出,得到最优超参数组合。而随机取值的方法也有多种不同的做法,常用的做法是采用均匀分布的随机数进行搜索,或者采用一些启发式的搜索策略(粒子群优化算法),这里不展开赘述。随机搜索并不总能找到全局最优解,但是通常认为随机搜索比网格搜索更优,其可以花费更少的计算代价得到相近的结果。\n\n__无论是网格搜索法还是随机搜索法,每一次进行迭代计算的时候,都未曾考虑已经搜索过的空间,即搜索过的空间未对下一次搜索产生任何指导作用,因此可能存在很多无效搜索。不同于网格搜索和随机搜索法,贝叶斯优化则能够通过高斯过程回归有效利用先验的搜索空间进行下一次搜索参数的选择,能大大减少迭代次数__\n\n## 理论准备\n经典的贝叶斯优化利用高斯过程(Gaussian Process, GP)对$f$进行概率建模,在介绍贝叶斯优化之前,有必要了解一下高斯过程回归的相关知识\n\n### 高斯过程\n高斯过程用于对一组随着时间增长的随机向量进行建模,在任意时刻,某个向量的所有子向量均服从高斯分布。\n假设有连续型随机变量序列$x_{1},x_{2},...,x_{T}$,如果该序列中任意数量的随机变量构成的向量$X_{t_{1}, ... ,t_{k}} = [x_{t_{1}} \\space ... \\space x_{t_{k}}]^{T}$均服从多维正态分布,则称次随机变量序列为高斯过程。\n\n特别地,假设当前有k个随机变量$x_{1},...,x{k}$,它们服从k维正态分布$N( \\mu_{k}, \\sum _{k} )$,其中均值向量$N( \\mu_{k}, \\sum _{k} )$,协方差矩阵$\\sum _{k} \\in \\mathbb R^{k*k}$\n\n当加入一个新的随机变量$x_{k+1}$之后,随机向量$x_{1},x_{2},...,x_{k},x_{k+1}$服从k+1维正态分布$\\mu_{k+1} \\in \\mathbb{R}^{k+1}$,其中均值向量$\\mu_{k+1} \\in \\mathbb{R}^{k+1}$,协方差矩阵$\\sum _{k+1} \\in \\mathbb R^{(k+1)*(k+1)}$\n\n由于正态分布的积分能够得到解析解,因此可以方便地得到边缘概率于条件概率。\n\n### 高斯过程回归\n\n机器学习中,算法通常是根据输入值$x$预测出一个最佳输出值$y$,用于分类或回归。某些情况下我们需要的不是预测出一个函数值,而是给出这个函数值的后验概率分布$p(y|x)$。对于实际应用问题,一般是给定一组样本点$x_{i}, \\space i=1,…,l$,基于此拟合出一个假设函数,给定输入值$x$,预测其标签值或者后验概率$p(y|x)$,高斯过程回归对应后者。\n\n高斯过程回归(Gaussian Process Regression, GPR)对表达式未知的函数的一组函数值进行概率建模,给出函数值的概率分布。嘉定给定某些点$x_{i}, i= 1,…,t$,以及在这些点处的函数值$f(x_{i})$,GPR能够根据这些点,拟合该未知函数,那么对于任意给定的$x$,就可以预测出$f(x)$,并且能够给出预测结果的置信度。\n\nGPR假设黑盒函数在各个点处的函数值$f(x)$都是随机变量,它们构成的随机向量服从多维正态分布。假设有t个采样点$x_{1},…,x_{t}$,在这些点处的函数值构成向量:\n\n$$f(x_{1:t}) = [f(x_{1} \\space ... \\space f(x_{t})]$$\n\nGPR假设此向量服从t维正态分布:\n\n$$f(x_{1:t}) \\sim N(\\mu(x_{1:t}), \\sum(x_{1:t},x_{1:t}))$$\n\n其中,$\\mu(x_{1:t})=[\\mu(x_{1}),…,\\mu(x_{t})]$是高斯分布的均值向量,$\\sum(x_{1:t},x_{1:t})$是协方差矩阵\n\n$$\\left[\\begin{array}{ccc}\n\\operatorname{cov}\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots & \\operatorname{cov}\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\cdots & \\ldots & \\ldots \\\\\n\\operatorname{cov}\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots & \\operatorname{cov}\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots & k\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\ldots & \\ldots & \\ldots \\\\\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots & k\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]$$\n\n问题的关键是如何根据样本值计算出正态分布的均值向量和协方差矩阵,均值向量是通过均值函数$\\mu(x)$根据每个采样点x计算构造的,可简单令$\\mu(x)=c$,或者将均值设置为0,因为即使均值设置为常数,由于有方差的作用,依然能够对数据进行有效建模。\n\n协方差通过核函数$k(x,x^{'})$计算得到,也称为协方差函数,协方差函数需要满足以下要求:\n\n1. 距离相近的样本点$x$和$x^{'}$之间有更大的正协方差值,因为相近的两个点的函数值有更强的相关性\n2. 保证协方差矩阵是对称半正定矩阵\n\n常用的是高斯核和Matern核,高斯核定义为:\n\n$$k\\left(\\mathbf{x}_{1}, \\mathbf{x}_{2}\\right)=\\alpha_{0} \\exp \\left(-\\frac{1}{2 \\sigma^{2}}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|^{2}\\right)$$\n\nMatern核定义为:\n\n$$k\\left(\\mathbf{x}_{1}, \\mathbf{x}_{2}\\right)=\\frac{2^{1-v}}{\\Gamma(v)}\\left(\\sqrt{2 v}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|\\right)^{v} K_{v}\\left(\\sqrt{2 v}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|\\right)$$\n\n其中$\\Gamma$是伽马函数,$K_{v}$是贝塞尔函数(Bessel function),$v$是人工设定的正参数。用核函数计算任意两点之间的核函数值,得到核函数矩阵$K$作为协方差矩阵的估计值:\n\n$$\\mathbf{K}=\\left[\\begin{array}{ccc}\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots & k\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\ldots & \\ldots & \\ldots \\\\\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots & k\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]$$\n\n在计算出均值向量和协方差矩阵之后,可以根据此多维正态分布预测$f(x)$在任意点处的概率分布。假设已经得到了一组样本$X_{1:t}$,以及对应的函数值$f(x_{1:t})$,如果要预测新的点$x$的函数值$f(x)$的期望$\\mu(x)$和方差$\\sigma^{2}(x)$,令$x_{t+1}=x$,加入该点后,$f(x_{1:t+1})$服从$t+1$维正态分布,即:\n\n$$\\left[\\begin{array}{c}\nf\\left(\\mathbf{x}_{1: t}\\right) \\\\\nf\\left(\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right] \\sim N\\left(\\left[\\begin{array}{c}\n\\mu\\left(\\mathbf{x}_{1: t}\\right) \\\\\n\\mu\\left(\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right],\\left[\\begin{array}{cc}\n\\mathbf{K} & \\mathbf{k} \\\\\n\\mathbf{k}^{\\mathrm{T}} & k\\left(\\mathbf{x}_{t+1}, \\mathbf{x}_{t+1}\\right)\n\\end{array}\\right]\\right)$$\n\n在已知$f(x_{1:t})$的情况下,$f(x_{t+1})$服从一维正态分布,即:\n\n$$f\\left(\\mathbf{x}_{t+1}\\right) \\mid f\\left(\\mathbf{x}_{1: t}\\right) \\sim N\\left(\\mu, \\sigma^{2}\\right)$$\n\n可以计算出对应的均值和方差,公式如下:\n\n$$\\begin{array}{l}\n\\mu=\\mathbf{k}^{\\mathrm{T}} \\mathbf{K}^{-1}\\left(f\\left(\\mathbf{x}_{1: t}\\right)-\\mu\\left(\\mathbf{x}_{1: t}\\right)\\right)+\\mu\\left(\\mathbf{x}_{t+1}\\right) \\\\\n\\sigma^{2}=k\\left(\\mathbf{x}_{t+1}, \\mathbf{x}_{t+1}\\right)-\\mathbf{k}^{\\mathrm{T}} \\mathbf{K}^{-1} \\mathbf{k}\n\\end{array}$$\n\n计算均值利用了已有采样点处函数值$f(x_{1:t})$,方差只与协方差值有关,与$f(x_{1:t})$无关\n\n### GPR代码实现\n\n- 定义高斯核\n\n```python\ndef kernel(X1, X2, l=1.0, sigma_f=1.0):\n '''\n X1: Array of m points (m x d).\n X2: Array of n points (n x d).\n Returns:\n Covariance matrix (m x n).\n '''\n sqdist = np.sum(X1**2, 1).reshape(-1, 1) + np.sum(X2**2, 1) - 2 * np.dot(X1, X2.T)\n return sigma_f**2 * np.exp(-0.5 / l**2 * sqdist)\n```\n\n- 计算均值和协方差矩阵\n\n```python\ndef posterior_predictive(X_s, X_train, Y_train, l=1.0, sigma_f=1.0, sigma_y=1e-8):\n '''\n 根据先验数据点计算均值向量和协方差矩阵\n Args:\n X_s: New input locations (n x d).\n X_train: Training locations (m x d).\n Y_train: Training targets (m x 1).\n l: Kernel length parameter.\n sigma_f: Kernel vertical variation parameter.\n sigma_y: Noise parameter.\n Returns:\n Posterior mean vector (n x d) and covariance matrix (n x n).\n '''\n K = kernel(X_train, X_train, l, sigma_f) + sigma_y**2 * np.eye(len(X_train))\n K_s = kernel(X_train, X_s, l, sigma_f)\n K_ss = kernel(X_s, X_s, l, sigma_f) + 1e-8 * np.eye(len(X_s))\n K_inv = np.linalg.inv(K)\n mu_s = K_s.T.dot(K_inv).dot(Y_train) # 均值向量, 注意均值函数被设置为 0\n cov_s = K_ss - K_s.T.dot(K_inv).dot(K_s) # 协方差矩阵\n return mu_s, cov_s\n```\n\n- GPR拟合效果绘制\n\n```Python\ndef plot_gp(mu, cov, X, X_train=None, Y_train=None, samples=[]):\n # 定义gp绘图函数\n X = X.ravel()\n mu = mu.ravel()\n uncertainty = 1.96 * np.sqrt(np.diag(cov)) # 1.96倍标准差对应95%置信度区间\n\n plt.fill_between(X, mu + uncertainty, mu - uncertainty, alpha=0.1)\n plt.plot(X, mu, label='Mean')\n for i, sample in enumerate(samples):\n plt.plot(X, sample, lw=1, ls='--', label=f'Sample {i+1}')\n if X_train is not None:\n plt.plot(X_train, Y_train, 'rx')\n plt.legend()\n\nX = np.arange(-5, 5, 0.2).reshape(-1, 1)\nX_train = np.array([-4, -3, -2, -1, 1]).reshape(-1, 1)\nY_train = np.sin(X_train)\n\n# 计算均值向量和协方差矩阵\nmu_s, cov_s = posterior_predictive(X, X_train, Y_train)\n\nsamples = np.random.multivariate_normal(mu_s.ravel(), cov_s, 1)\nplot_gp(mu_s, cov_s, X, X_train=X_train, Y_train=Y_train, samples=samples)\n```\n\n结果如下图所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220510004441.png\" width=\"50%\" height=\"50%\">\n\n其中,红色叉代表观测值,蓝色线代表均值,浅蓝色区域代表95%置信区间。\n\n## 贝叶斯优化原理\n\n#### 基本过程\n\n以下是贝叶斯优化的过程\n\n<img src=\"https://images.bumpchicken.cn/img/20220510004715.png\">\n\n有两个主要组成部分:\n\n1. GPR。根据观测点构建高斯过程回归模型,该模型能求取任意点处的函数值及后验概率。\n2. 构造采集函数(acquisition function),用于决定本次迭代在哪个点处进行采样。\n\n算法首先初始化$n_{0}$个点,设定最大迭代次数$N$,开始循环求解,每次增加一个点,寻找下一个点时根据已经找到的$n$个候选解建立高斯回归模型,通过这个模型能得到任意点处的函数值的后验概率。然后根据后验概率构造采集函数,寻找采集函数的极大值点作为下一个搜索点,以此循环,直到达到最大迭代次数,返回$N$个解中的极大值作为最优解。\n\n采集函数的选择有很多种,最常用的是期望改进(Expected Improvement,EI),下一节介绍下EI的原理。\n\n#### 采集函数\n\n假设已经搜索了n个点,这些点中的函数极大值记为\n\n$$f_{n}^{*} = max(f(x_{1},...,f(x_{n}))$$\n\n考虑下一个搜索点,计算该点处的函数值$f(x)$,如果$f(x)>=f_{n}^{*}$,则这$n+1$个点处的函数极大值为$f(x)$,否则为$f_{n}^{*}$\n\n加入这个新的点后,函数值的改进可以记为\n\n$$e^{+} = max(0, f(x) - f_{n}^{*})$$\n\n我们的目标是找到使得上面的改进值最大的$x$,但是该点的函数值在我们找到$x$是多少前又是未知的,幸运的是我们知道$f(x)$的概率分布,因此我们可以计算在所有x处的改进值的数学期望,然后选择期望最大的$x$作为下一个搜索点。定义期望改进(EI)函数如下:\n\n$$EI_{n}(x) = E_{n}[max(0, f(x) - f_{n}^{*})]$$\n\n令$z=f(x)$,则有:\n\n$$\\begin{aligned}\n\\mathrm{EI}_{n}(\\mathbf{x}) &=\\int_{-\\infty}^{+\\infty}\\left (max(0, z-f_{n}^{*})\\right) \\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z \\\\\n&=\\int_{f_{n}^{*}}^{+\\infty}\\left(z-f_{n}^{*}\\right) \\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z\n\\end{aligned}$$\n\n换元法,得到:\n\n$$\\\\\n\\begin{array}{c}\n\\int_{f_{n}^{*}}^{+\\infty}\\left(z-f_{n}^{*}\\right) \\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z\n \\\\\n=\\left(\\mu-f_{n}^{*}\\right)\\left(1-\\Phi\\left(\\left(f_{n}^{*}-\\mu\\right) / \\sigma\\right)\\right)+\\sigma \\varphi\\left(\\left(f_{n}^{*}-\\mu\\right) / \\sigma\\right)\n\\end{array}$$\n\n其中,$\\varphi(x)$是标准正态分布的概率密度函数,$\\phi(x)$是是标准正态分布的分布函数\n\n我们的目标是求EI的极值获取下一个采样点,即\n\n$$x_{n+1} = argmax EI_{n}(x)$$\n\n现在目标函数已知,且能得到目标函数的一阶导数和二阶导数,可以通过梯度下降法或L-BFGS求解极值,这里不再展开叙述\n\n\n\n## 贝叶斯优化应用\n\nBO有许多开源的实现,scikit-optimize 以及 参考资料4 [BayesianOptimization](https://github.com/fmfn/BayesianOptimization)都封装了BO,这里我们采用参考资料4中封装的BO进行演示。\n\n1. 直接用pip安装BayesianOptimization\n\n```shell\npip install bayesian-optimization\n```\n\n2. 定义黑盒函数\n\n```python\ndef black_box_function(x, y):\n \"\"\"\n x,y 均是待调优参数\n \"\"\"\n return -x ** 2 - (y - 1) ** 2 + 1 \n```\n\n3. 初始化BO\n\n```python\nfrom bayes_opt import BayesianOptimization\n\npbounds = {'x': (2, 4), 'y': (-3, 3)} # 设定x, y 调参范围\n\n# 初始化bo\noptimizer = BayesianOptimization(\n f=black_box_function,\n pbounds=pbounds,\n random_state=1,\n)\n```\n\n4. 进行迭代\n\n```python\noptimizer.maximize(\n init_points=2, # 初始解个数\n n_iter=20, # 迭代次数\n)\nprint(optimizer.max) # 输出最大值及对应的参数组合\n```\n\n输出:\n\n<img src=\"https://images.bumpchicken.cn/img/20220511172849.png\" width=\"80%\" height=\"80%\">\n\n函数$f(x,y) = -x^{2} - (y-1)^{2} + 1$,当$x\\in[2,4]$,$y\\in[-3,3]$时,很显然,当$x=2,y=1$时能取到最大值,BO给出的解已经相当接近最优解\n\n运行了多次,BO给出的解非常稳健,如下所示:\n\n<img src=\"https://images.bumpchicken.cn/img/20220511173250.png\">\n\n## 参考资料\n\n1. 《机器学习 原理、算法与应用》 雷明著\n\n2. Frazier P I. A tutorial on bayesian optimization[J]. arXiv preprint arXiv:1807.02811, 2018.\n\n3. https://github.com/krasserm/bayesian-machine-learning\n\n4. https://github.com/fmfn/BayesianOptimization\n\n5. https://github.com/scikit-optimize/scikit-optimize\n","slug":"BayesianOptimization","published":1,"updated":"2022-05-11T09:37:53.447Z","comments":1,"layout":"post","photos":[],"link":"","_id":"cl39lom8y000yotud6sw262pm","content":"<p><img src=\"https://images.bumpchicken.cn/img/20220509122455.png\"></p>\n<p>贝叶斯优化(Bayesian Optimization,\nBO)是一种黑盒优化算法,用于求解表达式未知的函数极值问题。算法使用高斯过程回归对一组采样点的函数值进行概率建模,预测出任意点处函数值的概率分布,然后构造采集函数(Acquistion\nFunction),用于衡量每一个点值得探索(explore)的程度,求解采集函数的极值从而确定下一个采样点,最后返回这组采样点的极值作为函数的极值。</p>\n<span id=\"more\"></span>\n<h2 id=\"黑盒优化问题\">黑盒优化问题</h2>\n<p>训练机器学习模型过程中,会有很多模型参数之外的参数,如学习率,卷积核大小等,再比如训练xgboost时,树的最大深度、采样率等参数都会影响训练结果,这些参数我们将其称为超参数。假设一组超参数组合<span\nclass=\"math inline\">\\(X=x_{1},x_{2},...,x_{n}\\)</span>,存在一个未知函数\n<span class=\"math inline\">\\(f:x \\rightarrow \\mathbb\n{R}\\)</span>,我们需要在<span class=\"math inline\">\\(x \\in\nX\\)</span>找到一组最佳参数组合<span\nclass=\"math inline\">\\(x^{*}\\)</span>使得:</p>\n<p><span class=\"math display\">\\[x^{*} = \\underset{x\\in X}{argmin} f(x)\n\\]</span></p>\n<p>当<span\nclass=\"math inline\">\\(f\\)</span>是凸函数并且定义域也是凸的时候,可以通过凸优化手段(梯度下降、L_BFGS等)来求解。但是超参数优化属于黑盒优化问题,<span\nclass=\"math inline\">\\(f\\)</span>不一定是凸函数,并且是未知的,在优化过程中只能得到函数的输入和输出,不能获取目标函数的表达式和梯度信息,这里的<span\nclass=\"math inline\">\\(f\\)</span>通常还是计算代价非常昂贵的函数,因此优化过程会比较困难,尤其是当超参数数量大的情况。常用的超参数优化方法有网格搜索(Grid\nSearch),随机搜索(Random\nSearch),遗传算法(粒子群优化、模拟退火等)以及本文要介绍的贝叶斯优化方法。</p>\n<p>下面介绍两种最基本的超参调优方法: 网格搜索法和随机搜索法</p>\n<ul>\n<li><p>网格搜索法</p>\n<p>网格搜索法搜索一组离散的取值情况,得到最优参数值。如果是连续型的超参数,则需要对其定义域进行网格划分,然后选取典型值计算。网格搜索法本质上是一种穷举法,对待调优参数进行全排列组合,逐一计算<span\nclass=\"math inline\">\\(f\\)</span>,然后选取最小的<span\nclass=\"math inline\">\\(f\\)</span>时的参数组合,如下代码所示,给定参数候选项,我们可以列出所有的参数组合\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">from</span> itertools <span class=\"keyword\">import</span> product</span><br><span class=\"line\">tuning_params = {<span class=\"string\">'a'</span>:[<span class=\"number\">1</span>,<span class=\"number\">2</span>,<span class=\"number\">3</span>], <span class=\"string\">'b'</span>:[<span class=\"number\">4</span>,<span class=\"number\">5</span>]} <span class=\"comment\"># 待优化参数可选项</span></span><br><span class=\"line\"><span class=\"keyword\">for</span> conf <span class=\"keyword\">in</span> product(*tuning_params.values()):</span><br><span class=\"line\"> <span class=\"built_in\">print</span>({k:v <span class=\"keyword\">for</span> k,v <span class=\"keyword\">in</span> <span class=\"built_in\">zip</span>(tuning_params.keys(), conf)}) <span class=\"comment\"># 生成参数组合</span></span><br></pre></td></tr></table></figure> 输出: <figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">1</span>, <span class=\"string\">'b'</span>: <span class=\"number\">4</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">1</span>, <span class=\"string\">'b'</span>: <span class=\"number\">5</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">2</span>, <span class=\"string\">'b'</span>: <span class=\"number\">4</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">2</span>, <span class=\"string\">'b'</span>: <span class=\"number\">5</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">3</span>, <span class=\"string\">'b'</span>: <span class=\"number\">4</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">3</span>, <span class=\"string\">'b'</span>: <span class=\"number\">5</span>}</span><br></pre></td></tr></table></figure>\n随着待调优参数增加,生成的全排列组合数量将非常巨大,计算代价过于昂贵</p></li>\n<li><p>随机搜索法\n相比于网格搜索法,随机搜索的做法是将超参数随机地取某些值,设置一个最大迭代次数,比较每次迭代中不同取值算法的输出,得到最优超参数组合。而随机取值的方法也有多种不同的做法,常用的做法是采用均匀分布的随机数进行搜索,或者采用一些启发式的搜索策略(粒子群优化算法),这里不展开赘述。随机搜索并不总能找到全局最优解,但是通常认为随机搜索比网格搜索更优,其可以花费更少的计算代价得到相近的结果。</p></li>\n</ul>\n<p><strong>无论是网格搜索法还是随机搜索法,每一次进行迭代计算的时候,都未曾考虑已经搜索过的空间,即搜索过的空间未对下一次搜索产生任何指导作用,因此可能存在很多无效搜索。不同于网格搜索和随机搜索法,贝叶斯优化则能够通过高斯过程回归有效利用先验的搜索空间进行下一次搜索参数的选择,能大大减少迭代次数</strong></p>\n<h2 id=\"理论准备\">理论准备</h2>\n<p>经典的贝叶斯优化利用高斯过程(Gaussian Process, GP)对<span\nclass=\"math inline\">\\(f\\)</span>进行概率建模,在介绍贝叶斯优化之前,有必要了解一下高斯过程回归的相关知识</p>\n<h3 id=\"高斯过程\">高斯过程</h3>\n<p>高斯过程用于对一组随着时间增长的随机向量进行建模,在任意时刻,某个向量的所有子向量均服从高斯分布。\n假设有连续型随机变量序列<span\nclass=\"math inline\">\\(x_{1},x_{2},...,x_{T}\\)</span>,如果该序列中任意数量的随机变量构成的向量<span\nclass=\"math inline\">\\(X_{t_{1}, ... ,t_{k}} = [x_{t_{1}} \\space ...\n\\space\nx_{t_{k}}]^{T}\\)</span>均服从多维正态分布,则称次随机变量序列为高斯过程。</p>\n<p>特别地,假设当前有k个随机变量<span\nclass=\"math inline\">\\(x_{1},...,x{k}\\)</span>,它们服从k维正态分布<span\nclass=\"math inline\">\\(N( \\mu_{k}, \\sum _{k}\n)\\)</span>,其中均值向量<span class=\"math inline\">\\(N( \\mu_{k}, \\sum\n_{k} )\\)</span>,协方差矩阵<span class=\"math inline\">\\(\\sum _{k} \\in\n\\mathbb R^{k*k}\\)</span></p>\n<p>当加入一个新的随机变量<span\nclass=\"math inline\">\\(x_{k+1}\\)</span>之后,随机向量<span\nclass=\"math inline\">\\(x_{1},x_{2},...,x_{k},x_{k+1}\\)</span>服从k+1维正态分布<span\nclass=\"math inline\">\\(\\mu_{k+1} \\in\n\\mathbb{R}^{k+1}\\)</span>,其中均值向量<span\nclass=\"math inline\">\\(\\mu_{k+1} \\in\n\\mathbb{R}^{k+1}\\)</span>,协方差矩阵<span class=\"math inline\">\\(\\sum\n_{k+1} \\in \\mathbb R^{(k+1)*(k+1)}\\)</span></p>\n<p>由于正态分布的积分能够得到解析解,因此可以方便地得到边缘概率于条件概率。</p>\n<h3 id=\"高斯过程回归\">高斯过程回归</h3>\n<p>机器学习中,算法通常是根据输入值<span\nclass=\"math inline\">\\(x\\)</span>预测出一个最佳输出值<span\nclass=\"math inline\">\\(y\\)</span>,用于分类或回归。某些情况下我们需要的不是预测出一个函数值,而是给出这个函数值的后验概率分布<span\nclass=\"math inline\">\\(p(y|x)\\)</span>。对于实际应用问题,一般是给定一组样本点<span\nclass=\"math inline\">\\(x_{i}, \\space\ni=1,…,l\\)</span>,基于此拟合出一个假设函数,给定输入值<span\nclass=\"math inline\">\\(x\\)</span>,预测其标签值或者后验概率<span\nclass=\"math inline\">\\(p(y|x)\\)</span>,高斯过程回归对应后者。</p>\n<p>高斯过程回归(Gaussian Process Regression,\nGPR)对表达式未知的函数的一组函数值进行概率建模,给出函数值的概率分布。嘉定给定某些点<span\nclass=\"math inline\">\\(x_{i}, i=\n1,…,t\\)</span>,以及在这些点处的函数值<span\nclass=\"math inline\">\\(f(x_{i})\\)</span>,GPR能够根据这些点,拟合该未知函数,那么对于任意给定的<span\nclass=\"math inline\">\\(x\\)</span>,就可以预测出<span\nclass=\"math inline\">\\(f(x)\\)</span>,并且能够给出预测结果的置信度。</p>\n<p>GPR假设黑盒函数在各个点处的函数值<span\nclass=\"math inline\">\\(f(x)\\)</span>都是随机变量,它们构成的随机向量服从多维正态分布。假设有t个采样点<span\nclass=\"math inline\">\\(x_{1},…,x_{t}\\)</span>,在这些点处的函数值构成向量:</p>\n<p><span class=\"math display\">\\[f(x_{1:t}) = [f(x_{1} \\space ... \\space\nf(x_{t})]\\]</span></p>\n<p>GPR假设此向量服从t维正态分布:</p>\n<p><span class=\"math display\">\\[f(x_{1:t}) \\sim N(\\mu(x_{1:t}),\n\\sum(x_{1:t},x_{1:t}))\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(\\mu(x_{1:t})=[\\mu(x_{1}),…,\\mu(x_{t})]\\)</span>是高斯分布的均值向量,<span\nclass=\"math inline\">\\(\\sum(x_{1:t},x_{1:t})\\)</span>是协方差矩阵</p>\n<p><span class=\"math display\">\\[\\left[\\begin{array}{ccc}\n\\operatorname{cov}\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) &\n\\ldots & \\operatorname{cov}\\left(\\mathbf{x}_{1},\n\\mathbf{x}_{t}\\right) \\\\\n\\cdots & \\ldots & \\ldots \\\\\n\\operatorname{cov}\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) &\n\\ldots & \\operatorname{cov}\\left(\\mathbf{x}_{t},\n\\mathbf{x}_{t}\\right)\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots &\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\ldots & \\ldots & \\ldots \\\\\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots &\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]\\]</span></p>\n<p>问题的关键是如何根据样本值计算出正态分布的均值向量和协方差矩阵,均值向量是通过均值函数<span\nclass=\"math inline\">\\(\\mu(x)\\)</span>根据每个采样点x计算构造的,可简单令<span\nclass=\"math inline\">\\(\\mu(x)=c\\)</span>,或者将均值设置为0,因为即使均值设置为常数,由于有方差的作用,依然能够对数据进行有效建模。</p>\n<p>协方差通过核函数<span\nclass=\"math inline\">\\(k(x,x^{'})\\)</span>计算得到,也称为协方差函数,协方差函数需要满足以下要求:</p>\n<ol type=\"1\">\n<li>距离相近的样本点<span class=\"math inline\">\\(x\\)</span>和<span\nclass=\"math inline\">\\(x^{'}\\)</span>之间有更大的正协方差值,因为相近的两个点的函数值有更强的相关性</li>\n<li>保证协方差矩阵是对称半正定矩阵</li>\n</ol>\n<p>常用的是高斯核和Matern核,高斯核定义为:</p>\n<p><span class=\"math display\">\\[k\\left(\\mathbf{x}_{1},\n\\mathbf{x}_{2}\\right)=\\alpha_{0} \\exp \\left(-\\frac{1}{2\n\\sigma^{2}}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|^{2}\\right)\\]</span></p>\n<p>Matern核定义为:</p>\n<p><span class=\"math display\">\\[k\\left(\\mathbf{x}_{1},\n\\mathbf{x}_{2}\\right)=\\frac{2^{1-v}}{\\Gamma(v)}\\left(\\sqrt{2\nv}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|\\right)^{v}\nK_{v}\\left(\\sqrt{2\nv}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|\\right)\\]</span></p>\n<p>其中<span class=\"math inline\">\\(\\Gamma\\)</span>是伽马函数,<span\nclass=\"math inline\">\\(K_{v}\\)</span>是贝塞尔函数(Bessel function),<span\nclass=\"math inline\">\\(v\\)</span>是人工设定的正参数。用核函数计算任意两点之间的核函数值,得到核函数矩阵<span\nclass=\"math inline\">\\(K\\)</span>作为协方差矩阵的估计值:</p>\n<p><span class=\"math display\">\\[\\mathbf{K}=\\left[\\begin{array}{ccc}\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots &\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\ldots & \\ldots & \\ldots \\\\\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots &\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]\\]</span></p>\n<p>在计算出均值向量和协方差矩阵之后,可以根据此多维正态分布预测<span\nclass=\"math inline\">\\(f(x)\\)</span>在任意点处的概率分布。假设已经得到了一组样本<span\nclass=\"math inline\">\\(X_{1:t}\\)</span>,以及对应的函数值<span\nclass=\"math inline\">\\(f(x_{1:t})\\)</span>,如果要预测新的点<span\nclass=\"math inline\">\\(x\\)</span>的函数值<span\nclass=\"math inline\">\\(f(x)\\)</span>的期望<span\nclass=\"math inline\">\\(\\mu(x)\\)</span>和方差<span\nclass=\"math inline\">\\(\\sigma^{2}(x)\\)</span>,令<span\nclass=\"math inline\">\\(x_{t+1}=x\\)</span>,加入该点后,<span\nclass=\"math inline\">\\(f(x_{1:t+1})\\)</span>服从<span\nclass=\"math inline\">\\(t+1\\)</span>维正态分布,即:</p>\n<p><span class=\"math display\">\\[\\left[\\begin{array}{c}\nf\\left(\\mathbf{x}_{1: t}\\right) \\\\\nf\\left(\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right] \\sim N\\left(\\left[\\begin{array}{c}\n\\mu\\left(\\mathbf{x}_{1: t}\\right) \\\\\n\\mu\\left(\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right],\\left[\\begin{array}{cc}\n\\mathbf{K} & \\mathbf{k} \\\\\n\\mathbf{k}^{\\mathrm{T}} & k\\left(\\mathbf{x}_{t+1},\n\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right]\\right)\\]</span></p>\n<p>在已知<span class=\"math inline\">\\(f(x_{1:t})\\)</span>的情况下,<span\nclass=\"math inline\">\\(f(x_{t+1})\\)</span>服从一维正态分布,即:</p>\n<p><span class=\"math display\">\\[f\\left(\\mathbf{x}_{t+1}\\right) \\mid\nf\\left(\\mathbf{x}_{1: t}\\right) \\sim N\\left(\\mu,\n\\sigma^{2}\\right)\\]</span></p>\n<p>可以计算出对应的均值和方差,公式如下:</p>\n<p><span class=\"math display\">\\[\\begin{array}{l}\n\\mu=\\mathbf{k}^{\\mathrm{T}} \\mathbf{K}^{-1}\\left(f\\left(\\mathbf{x}_{1:\nt}\\right)-\\mu\\left(\\mathbf{x}_{1:\nt}\\right)\\right)+\\mu\\left(\\mathbf{x}_{t+1}\\right) \\\\\n\\sigma^{2}=k\\left(\\mathbf{x}_{t+1},\n\\mathbf{x}_{t+1}\\right)-\\mathbf{k}^{\\mathrm{T}} \\mathbf{K}^{-1}\n\\mathbf{k}\n\\end{array}\\]</span></p>\n<p>计算均值利用了已有采样点处函数值<span\nclass=\"math inline\">\\(f(x_{1:t})\\)</span>,方差只与协方差值有关,与<span\nclass=\"math inline\">\\(f(x_{1:t})\\)</span>无关</p>\n<h3 id=\"gpr代码实现\">GPR代码实现</h3>\n<ul>\n<li>定义高斯核</li>\n</ul>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">kernel</span>(<span class=\"params\">X1, X2, l=<span class=\"number\">1.0</span>, sigma_f=<span class=\"number\">1.0</span></span>):</span><br><span class=\"line\"> <span class=\"string\">'''</span></span><br><span class=\"line\"><span class=\"string\"> X1: Array of m points (m x d).</span></span><br><span class=\"line\"><span class=\"string\"> X2: Array of n points (n x d).</span></span><br><span class=\"line\"><span class=\"string\"> Returns:</span></span><br><span class=\"line\"><span class=\"string\"> Covariance matrix (m x n).</span></span><br><span class=\"line\"><span class=\"string\"> '''</span></span><br><span class=\"line\"> sqdist = np.<span class=\"built_in\">sum</span>(X1**<span class=\"number\">2</span>, <span class=\"number\">1</span>).reshape(-<span class=\"number\">1</span>, <span class=\"number\">1</span>) + np.<span class=\"built_in\">sum</span>(X2**<span class=\"number\">2</span>, <span class=\"number\">1</span>) - <span class=\"number\">2</span> * np.dot(X1, X2.T)</span><br><span class=\"line\"> <span class=\"keyword\">return</span> sigma_f**<span class=\"number\">2</span> * np.exp(-<span class=\"number\">0.5</span> / l**<span class=\"number\">2</span> * sqdist)</span><br></pre></td></tr></table></figure>\n<ul>\n<li>计算均值和协方差矩阵</li>\n</ul>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">posterior_predictive</span>(<span class=\"params\">X_s, X_train, Y_train, l=<span class=\"number\">1.0</span>, sigma_f=<span class=\"number\">1.0</span>, sigma_y=<span class=\"number\">1e-8</span></span>):</span><br><span class=\"line\"> <span class=\"string\">'''</span></span><br><span class=\"line\"><span class=\"string\"> 根据先验数据点计算均值向量和协方差矩阵</span></span><br><span class=\"line\"><span class=\"string\"> Args:</span></span><br><span class=\"line\"><span class=\"string\"> X_s: New input locations (n x d).</span></span><br><span class=\"line\"><span class=\"string\"> X_train: Training locations (m x d).</span></span><br><span class=\"line\"><span class=\"string\"> Y_train: Training targets (m x 1).</span></span><br><span class=\"line\"><span class=\"string\"> l: Kernel length parameter.</span></span><br><span class=\"line\"><span class=\"string\"> sigma_f: Kernel vertical variation parameter.</span></span><br><span class=\"line\"><span class=\"string\"> sigma_y: Noise parameter.</span></span><br><span class=\"line\"><span class=\"string\"> Returns:</span></span><br><span class=\"line\"><span class=\"string\"> Posterior mean vector (n x d) and covariance matrix (n x n).</span></span><br><span class=\"line\"><span class=\"string\"> '''</span></span><br><span class=\"line\"> K = kernel(X_train, X_train, l, sigma_f) + sigma_y**<span class=\"number\">2</span> * np.eye(<span class=\"built_in\">len</span>(X_train))</span><br><span class=\"line\"> K_s = kernel(X_train, X_s, l, sigma_f)</span><br><span class=\"line\"> K_ss = kernel(X_s, X_s, l, sigma_f) + <span class=\"number\">1e-8</span> * np.eye(<span class=\"built_in\">len</span>(X_s))</span><br><span class=\"line\"> K_inv = np.linalg.inv(K)</span><br><span class=\"line\"> mu_s = K_s.T.dot(K_inv).dot(Y_train) <span class=\"comment\"># 均值向量, 注意均值函数被设置为 0</span></span><br><span class=\"line\"> cov_s = K_ss - K_s.T.dot(K_inv).dot(K_s) <span class=\"comment\"># 协方差矩阵</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> mu_s, cov_s</span><br></pre></td></tr></table></figure>\n<ul>\n<li>GPR拟合效果绘制</li>\n</ul>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">plot_gp</span>(<span class=\"params\">mu, cov, X, X_train=<span class=\"literal\">None</span>, Y_train=<span class=\"literal\">None</span>, samples=[]</span>):</span><br><span class=\"line\"> <span class=\"comment\"># 定义gp绘图函数</span></span><br><span class=\"line\"> X = X.ravel()</span><br><span class=\"line\"> mu = mu.ravel()</span><br><span class=\"line\"> uncertainty = <span class=\"number\">1.96</span> * np.sqrt(np.diag(cov)) <span class=\"comment\"># 1.96倍标准差对应95%置信度区间</span></span><br><span class=\"line\"></span><br><span class=\"line\"> plt.fill_between(X, mu + uncertainty, mu - uncertainty, alpha=<span class=\"number\">0.1</span>)</span><br><span class=\"line\"> plt.plot(X, mu, label=<span class=\"string\">'Mean'</span>)</span><br><span class=\"line\"> <span class=\"keyword\">for</span> i, sample <span class=\"keyword\">in</span> <span class=\"built_in\">enumerate</span>(samples):</span><br><span class=\"line\"> plt.plot(X, sample, lw=<span class=\"number\">1</span>, ls=<span class=\"string\">'--'</span>, label=<span class=\"string\">f'Sample <span class=\"subst\">{i+<span class=\"number\">1</span>}</span>'</span>)</span><br><span class=\"line\"> <span class=\"keyword\">if</span> X_train <span class=\"keyword\">is</span> <span class=\"keyword\">not</span> <span class=\"literal\">None</span>:</span><br><span class=\"line\"> plt.plot(X_train, Y_train, <span class=\"string\">'rx'</span>)</span><br><span class=\"line\"> plt.legend()</span><br><span class=\"line\"></span><br><span class=\"line\">X = np.arange(-<span class=\"number\">5</span>, <span class=\"number\">5</span>, <span class=\"number\">0.2</span>).reshape(-<span class=\"number\">1</span>, <span class=\"number\">1</span>)</span><br><span class=\"line\">X_train = np.array([-<span class=\"number\">4</span>, -<span class=\"number\">3</span>, -<span class=\"number\">2</span>, -<span class=\"number\">1</span>, <span class=\"number\">1</span>]).reshape(-<span class=\"number\">1</span>, <span class=\"number\">1</span>)</span><br><span class=\"line\">Y_train = np.sin(X_train)</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 计算均值向量和协方差矩阵</span></span><br><span class=\"line\">mu_s, cov_s = posterior_predictive(X, X_train, Y_train)</span><br><span class=\"line\"></span><br><span class=\"line\">samples = np.random.multivariate_normal(mu_s.ravel(), cov_s, <span class=\"number\">1</span>)</span><br><span class=\"line\">plot_gp(mu_s, cov_s, X, X_train=X_train, Y_train=Y_train, samples=samples)</span><br></pre></td></tr></table></figure>\n<p>结果如下图所示:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220510004441.png\" width=\"50%\" height=\"50%\"></p>\n<p>其中,红色叉代表观测值,蓝色线代表均值,浅蓝色区域代表95%置信区间。</p>\n<h2 id=\"贝叶斯优化原理\">贝叶斯优化原理</h2>\n<h4 id=\"基本过程\">基本过程</h4>\n<p>以下是贝叶斯优化的过程</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220510004715.png\"></p>\n<p>有两个主要组成部分:</p>\n<ol type=\"1\">\n<li>GPR。根据观测点构建高斯过程回归模型,该模型能求取任意点处的函数值及后验概率。</li>\n<li>构造采集函数(acquisition\nfunction),用于决定本次迭代在哪个点处进行采样。</li>\n</ol>\n<p>算法首先初始化<span\nclass=\"math inline\">\\(n_{0}\\)</span>个点,设定最大迭代次数<span\nclass=\"math inline\">\\(N\\)</span>,开始循环求解,每次增加一个点,寻找下一个点时根据已经找到的<span\nclass=\"math inline\">\\(n\\)</span>个候选解建立高斯回归模型,通过这个模型能得到任意点处的函数值的后验概率。然后根据后验概率构造采集函数,寻找采集函数的极大值点作为下一个搜索点,以此循环,直到达到最大迭代次数,返回<span\nclass=\"math inline\">\\(N\\)</span>个解中的极大值作为最优解。</p>\n<p>采集函数的选择有很多种,最常用的是期望改进(Expected\nImprovement,EI),下一节介绍下EI的原理。</p>\n<h4 id=\"采集函数\">采集函数</h4>\n<p>假设已经搜索了n个点,这些点中的函数极大值记为</p>\n<p><span class=\"math display\">\\[f_{n}^{*} =\nmax(f(x_{1},...,f(x_{n}))\\]</span></p>\n<p>考虑下一个搜索点,计算该点处的函数值<span\nclass=\"math inline\">\\(f(x)\\)</span>,如果<span\nclass=\"math inline\">\\(f(x)>=f_{n}^{*}\\)</span>,则这<span\nclass=\"math inline\">\\(n+1\\)</span>个点处的函数极大值为<span\nclass=\"math inline\">\\(f(x)\\)</span>,否则为<span\nclass=\"math inline\">\\(f_{n}^{*}\\)</span></p>\n<p>加入这个新的点后,函数值的改进可以记为</p>\n<p><span class=\"math display\">\\[e^{+} = max(0, f(x) -\nf_{n}^{*})\\]</span></p>\n<p>我们的目标是找到使得上面的改进值最大的<span\nclass=\"math inline\">\\(x\\)</span>,但是该点的函数值在我们找到<span\nclass=\"math inline\">\\(x\\)</span>是多少前又是未知的,幸运的是我们知道<span\nclass=\"math inline\">\\(f(x)\\)</span>的概率分布,因此我们可以计算在所有x处的改进值的数学期望,然后选择期望最大的<span\nclass=\"math inline\">\\(x\\)</span>作为下一个搜索点。定义期望改进(EI)函数如下:</p>\n<p><span class=\"math display\">\\[EI_{n}(x) = E_{n}[max(0, f(x) -\nf_{n}^{*})]\\]</span></p>\n<p>令<span class=\"math inline\">\\(z=f(x)\\)</span>,则有:</p>\n<p><span class=\"math display\">\\[\\begin{aligned}\n\\mathrm{EI}_{n}(\\mathbf{x}) &=\\int_{-\\infty}^{+\\infty}\\left (max(0,\nz-f_{n}^{*})\\right) \\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp\n\\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z \\\\\n&=\\int_{f_{n}^{*}}^{+\\infty}\\left(z-f_{n}^{*}\\right)\n\\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2\n\\sigma^{2}}\\right) d z\n\\end{aligned}\\]</span></p>\n<p>换元法,得到:</p>\n<p><span class=\"math display\">\\[\\\\\n\\begin{array}{c}\n\\int_{f_{n}^{*}}^{+\\infty}\\left(z-f_{n}^{*}\\right) \\frac{1}{\\sqrt{2 \\pi}\n\\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z\n\\\\\n=\\left(\\mu-f_{n}^{*}\\right)\\left(1-\\Phi\\left(\\left(f_{n}^{*}-\\mu\\right)\n/ \\sigma\\right)\\right)+\\sigma \\varphi\\left(\\left(f_{n}^{*}-\\mu\\right) /\n\\sigma\\right)\n\\end{array}\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(\\varphi(x)\\)</span>是标准正态分布的概率密度函数,<span\nclass=\"math inline\">\\(\\phi(x)\\)</span>是是标准正态分布的分布函数</p>\n<p>我们的目标是求EI的极值获取下一个采样点,即</p>\n<p><span class=\"math display\">\\[x_{n+1} = argmax EI_{n}(x)\\]</span></p>\n<p>现在目标函数已知,且能得到目标函数的一阶导数和二阶导数,可以通过梯度下降法或L-BFGS求解极值,这里不再展开叙述</p>\n<h2 id=\"贝叶斯优化应用\">贝叶斯优化应用</h2>\n<p>BO有许多开源的实现,scikit-optimize 以及 参考资料4 <a\nhref=\"https://github.com/fmfn/BayesianOptimization\">BayesianOptimization</a>都封装了BO,这里我们采用参考资料4中封装的BO进行演示。</p>\n<ol type=\"1\">\n<li>直接用pip安装BayesianOptimization</li>\n</ol>\n<figure class=\"highlight shell\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">pip install bayesian-optimization</span><br></pre></td></tr></table></figure>\n<ol start=\"2\" type=\"1\">\n<li>定义黑盒函数</li>\n</ol>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">black_box_function</span>(<span class=\"params\">x, y</span>):</span><br><span class=\"line\"> <span class=\"string\">"""</span></span><br><span class=\"line\"><span class=\"string\"> x,y 均是待调优参数</span></span><br><span class=\"line\"><span class=\"string\"> """</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> -x ** <span class=\"number\">2</span> - (y - <span class=\"number\">1</span>) ** <span class=\"number\">2</span> + <span class=\"number\">1</span> </span><br></pre></td></tr></table></figure>\n<ol start=\"3\" type=\"1\">\n<li>初始化BO</li>\n</ol>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">from</span> bayes_opt <span class=\"keyword\">import</span> BayesianOptimization</span><br><span class=\"line\"></span><br><span class=\"line\">pbounds = {<span class=\"string\">'x'</span>: (<span class=\"number\">2</span>, <span class=\"number\">4</span>), <span class=\"string\">'y'</span>: (-<span class=\"number\">3</span>, <span class=\"number\">3</span>)} <span class=\"comment\"># 设定x, y 调参范围</span></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 初始化bo</span></span><br><span class=\"line\">optimizer = BayesianOptimization(</span><br><span class=\"line\"> f=black_box_function,</span><br><span class=\"line\"> pbounds=pbounds,</span><br><span class=\"line\"> random_state=<span class=\"number\">1</span>,</span><br><span class=\"line\">)</span><br></pre></td></tr></table></figure>\n<ol start=\"4\" type=\"1\">\n<li>进行迭代</li>\n</ol>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">optimizer.maximize(</span><br><span class=\"line\"> init_points=<span class=\"number\">2</span>, <span class=\"comment\"># 初始解个数</span></span><br><span class=\"line\"> n_iter=<span class=\"number\">20</span>, <span class=\"comment\"># 迭代次数</span></span><br><span class=\"line\">)</span><br><span class=\"line\"><span class=\"built_in\">print</span>(optimizer.<span class=\"built_in\">max</span>) <span class=\"comment\"># 输出最大值及对应的参数组合</span></span><br></pre></td></tr></table></figure>\n<p>输出:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511172849.png\" width=\"80%\" height=\"80%\"></p>\n<p>函数<span class=\"math inline\">\\(f(x,y) = -x^{2} - (y-1)^{2} +\n1\\)</span>,当<span class=\"math inline\">\\(x\\in[2,4]\\)</span>,<span\nclass=\"math inline\">\\(y\\in[-3,3]\\)</span>时,很显然,当<span\nclass=\"math inline\">\\(x=2,y=1\\)</span>时能取到最大值,BO给出的解已经相当接近最优解</p>\n<p>运行了多次,BO给出的解非常稳健,如下所示:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511173250.png\"></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<ol type=\"1\">\n<li><p>《机器学习 原理、算法与应用》 雷明著</p></li>\n<li><p>Frazier P I. A tutorial on bayesian optimization[J]. arXiv\npreprint arXiv:1807.02811, 2018.</p></li>\n<li><p>https://github.com/krasserm/bayesian-machine-learning</p></li>\n<li><p>https://github.com/fmfn/BayesianOptimization</p></li>\n<li><p>https://github.com/scikit-optimize/scikit-optimize</p></li>\n</ol>\n","site":{"data":{}},"excerpt":"<p><img src=\"https://images.bumpchicken.cn/img/20220509122455.png\"></p>\n<p>贝叶斯优化(Bayesian Optimization,\nBO)是一种黑盒优化算法,用于求解表达式未知的函数极值问题。算法使用高斯过程回归对一组采样点的函数值进行概率建模,预测出任意点处函数值的概率分布,然后构造采集函数(Acquistion\nFunction),用于衡量每一个点值得探索(explore)的程度,求解采集函数的极值从而确定下一个采样点,最后返回这组采样点的极值作为函数的极值。</p>","more":"<h2 id=\"黑盒优化问题\">黑盒优化问题</h2>\n<p>训练机器学习模型过程中,会有很多模型参数之外的参数,如学习率,卷积核大小等,再比如训练xgboost时,树的最大深度、采样率等参数都会影响训练结果,这些参数我们将其称为超参数。假设一组超参数组合<span\nclass=\"math inline\">\\(X=x_{1},x_{2},...,x_{n}\\)</span>,存在一个未知函数\n<span class=\"math inline\">\\(f:x \\rightarrow \\mathbb\n{R}\\)</span>,我们需要在<span class=\"math inline\">\\(x \\in\nX\\)</span>找到一组最佳参数组合<span\nclass=\"math inline\">\\(x^{*}\\)</span>使得:</p>\n<p><span class=\"math display\">\\[x^{*} = \\underset{x\\in X}{argmin} f(x)\n\\]</span></p>\n<p>当<span\nclass=\"math inline\">\\(f\\)</span>是凸函数并且定义域也是凸的时候,可以通过凸优化手段(梯度下降、L_BFGS等)来求解。但是超参数优化属于黑盒优化问题,<span\nclass=\"math inline\">\\(f\\)</span>不一定是凸函数,并且是未知的,在优化过程中只能得到函数的输入和输出,不能获取目标函数的表达式和梯度信息,这里的<span\nclass=\"math inline\">\\(f\\)</span>通常还是计算代价非常昂贵的函数,因此优化过程会比较困难,尤其是当超参数数量大的情况。常用的超参数优化方法有网格搜索(Grid\nSearch),随机搜索(Random\nSearch),遗传算法(粒子群优化、模拟退火等)以及本文要介绍的贝叶斯优化方法。</p>\n<p>下面介绍两种最基本的超参调优方法: 网格搜索法和随机搜索法</p>\n<ul>\n<li><p>网格搜索法</p>\n<p>网格搜索法搜索一组离散的取值情况,得到最优参数值。如果是连续型的超参数,则需要对其定义域进行网格划分,然后选取典型值计算。网格搜索法本质上是一种穷举法,对待调优参数进行全排列组合,逐一计算<span\nclass=\"math inline\">\\(f\\)</span>,然后选取最小的<span\nclass=\"math inline\">\\(f\\)</span>时的参数组合,如下代码所示,给定参数候选项,我们可以列出所有的参数组合\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">from</span> itertools <span class=\"keyword\">import</span> product</span><br><span class=\"line\">tuning_params = {<span class=\"string\">'a'</span>:[<span class=\"number\">1</span>,<span class=\"number\">2</span>,<span class=\"number\">3</span>], <span class=\"string\">'b'</span>:[<span class=\"number\">4</span>,<span class=\"number\">5</span>]} <span class=\"comment\"># 待优化参数可选项</span></span><br><span class=\"line\"><span class=\"keyword\">for</span> conf <span class=\"keyword\">in</span> product(*tuning_params.values()):</span><br><span class=\"line\"> <span class=\"built_in\">print</span>({k:v <span class=\"keyword\">for</span> k,v <span class=\"keyword\">in</span> <span class=\"built_in\">zip</span>(tuning_params.keys(), conf)}) <span class=\"comment\"># 生成参数组合</span></span><br></pre></td></tr></table></figure> 输出: <figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">1</span>, <span class=\"string\">'b'</span>: <span class=\"number\">4</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">1</span>, <span class=\"string\">'b'</span>: <span class=\"number\">5</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">2</span>, <span class=\"string\">'b'</span>: <span class=\"number\">4</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">2</span>, <span class=\"string\">'b'</span>: <span class=\"number\">5</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">3</span>, <span class=\"string\">'b'</span>: <span class=\"number\">4</span>}</span><br><span class=\"line\">{<span class=\"string\">'a'</span>: <span class=\"number\">3</span>, <span class=\"string\">'b'</span>: <span class=\"number\">5</span>}</span><br></pre></td></tr></table></figure>\n随着待调优参数增加,生成的全排列组合数量将非常巨大,计算代价过于昂贵</p></li>\n<li><p>随机搜索法\n相比于网格搜索法,随机搜索的做法是将超参数随机地取某些值,设置一个最大迭代次数,比较每次迭代中不同取值算法的输出,得到最优超参数组合。而随机取值的方法也有多种不同的做法,常用的做法是采用均匀分布的随机数进行搜索,或者采用一些启发式的搜索策略(粒子群优化算法),这里不展开赘述。随机搜索并不总能找到全局最优解,但是通常认为随机搜索比网格搜索更优,其可以花费更少的计算代价得到相近的结果。</p></li>\n</ul>\n<p><strong>无论是网格搜索法还是随机搜索法,每一次进行迭代计算的时候,都未曾考虑已经搜索过的空间,即搜索过的空间未对下一次搜索产生任何指导作用,因此可能存在很多无效搜索。不同于网格搜索和随机搜索法,贝叶斯优化则能够通过高斯过程回归有效利用先验的搜索空间进行下一次搜索参数的选择,能大大减少迭代次数</strong></p>\n<h2 id=\"理论准备\">理论准备</h2>\n<p>经典的贝叶斯优化利用高斯过程(Gaussian Process, GP)对<span\nclass=\"math inline\">\\(f\\)</span>进行概率建模,在介绍贝叶斯优化之前,有必要了解一下高斯过程回归的相关知识</p>\n<h3 id=\"高斯过程\">高斯过程</h3>\n<p>高斯过程用于对一组随着时间增长的随机向量进行建模,在任意时刻,某个向量的所有子向量均服从高斯分布。\n假设有连续型随机变量序列<span\nclass=\"math inline\">\\(x_{1},x_{2},...,x_{T}\\)</span>,如果该序列中任意数量的随机变量构成的向量<span\nclass=\"math inline\">\\(X_{t_{1}, ... ,t_{k}} = [x_{t_{1}} \\space ...\n\\space\nx_{t_{k}}]^{T}\\)</span>均服从多维正态分布,则称次随机变量序列为高斯过程。</p>\n<p>特别地,假设当前有k个随机变量<span\nclass=\"math inline\">\\(x_{1},...,x{k}\\)</span>,它们服从k维正态分布<span\nclass=\"math inline\">\\(N( \\mu_{k}, \\sum _{k}\n)\\)</span>,其中均值向量<span class=\"math inline\">\\(N( \\mu_{k}, \\sum\n_{k} )\\)</span>,协方差矩阵<span class=\"math inline\">\\(\\sum _{k} \\in\n\\mathbb R^{k*k}\\)</span></p>\n<p>当加入一个新的随机变量<span\nclass=\"math inline\">\\(x_{k+1}\\)</span>之后,随机向量<span\nclass=\"math inline\">\\(x_{1},x_{2},...,x_{k},x_{k+1}\\)</span>服从k+1维正态分布<span\nclass=\"math inline\">\\(\\mu_{k+1} \\in\n\\mathbb{R}^{k+1}\\)</span>,其中均值向量<span\nclass=\"math inline\">\\(\\mu_{k+1} \\in\n\\mathbb{R}^{k+1}\\)</span>,协方差矩阵<span class=\"math inline\">\\(\\sum\n_{k+1} \\in \\mathbb R^{(k+1)*(k+1)}\\)</span></p>\n<p>由于正态分布的积分能够得到解析解,因此可以方便地得到边缘概率于条件概率。</p>\n<h3 id=\"高斯过程回归\">高斯过程回归</h3>\n<p>机器学习中,算法通常是根据输入值<span\nclass=\"math inline\">\\(x\\)</span>预测出一个最佳输出值<span\nclass=\"math inline\">\\(y\\)</span>,用于分类或回归。某些情况下我们需要的不是预测出一个函数值,而是给出这个函数值的后验概率分布<span\nclass=\"math inline\">\\(p(y|x)\\)</span>。对于实际应用问题,一般是给定一组样本点<span\nclass=\"math inline\">\\(x_{i}, \\space\ni=1,…,l\\)</span>,基于此拟合出一个假设函数,给定输入值<span\nclass=\"math inline\">\\(x\\)</span>,预测其标签值或者后验概率<span\nclass=\"math inline\">\\(p(y|x)\\)</span>,高斯过程回归对应后者。</p>\n<p>高斯过程回归(Gaussian Process Regression,\nGPR)对表达式未知的函数的一组函数值进行概率建模,给出函数值的概率分布。嘉定给定某些点<span\nclass=\"math inline\">\\(x_{i}, i=\n1,…,t\\)</span>,以及在这些点处的函数值<span\nclass=\"math inline\">\\(f(x_{i})\\)</span>,GPR能够根据这些点,拟合该未知函数,那么对于任意给定的<span\nclass=\"math inline\">\\(x\\)</span>,就可以预测出<span\nclass=\"math inline\">\\(f(x)\\)</span>,并且能够给出预测结果的置信度。</p>\n<p>GPR假设黑盒函数在各个点处的函数值<span\nclass=\"math inline\">\\(f(x)\\)</span>都是随机变量,它们构成的随机向量服从多维正态分布。假设有t个采样点<span\nclass=\"math inline\">\\(x_{1},…,x_{t}\\)</span>,在这些点处的函数值构成向量:</p>\n<p><span class=\"math display\">\\[f(x_{1:t}) = [f(x_{1} \\space ... \\space\nf(x_{t})]\\]</span></p>\n<p>GPR假设此向量服从t维正态分布:</p>\n<p><span class=\"math display\">\\[f(x_{1:t}) \\sim N(\\mu(x_{1:t}),\n\\sum(x_{1:t},x_{1:t}))\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(\\mu(x_{1:t})=[\\mu(x_{1}),…,\\mu(x_{t})]\\)</span>是高斯分布的均值向量,<span\nclass=\"math inline\">\\(\\sum(x_{1:t},x_{1:t})\\)</span>是协方差矩阵</p>\n<p><span class=\"math display\">\\[\\left[\\begin{array}{ccc}\n\\operatorname{cov}\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) &\n\\ldots & \\operatorname{cov}\\left(\\mathbf{x}_{1},\n\\mathbf{x}_{t}\\right) \\\\\n\\cdots & \\ldots & \\ldots \\\\\n\\operatorname{cov}\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) &\n\\ldots & \\operatorname{cov}\\left(\\mathbf{x}_{t},\n\\mathbf{x}_{t}\\right)\n\\end{array}\\right]=\\left[\\begin{array}{ccc}\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots &\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\ldots & \\ldots & \\ldots \\\\\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots &\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]\\]</span></p>\n<p>问题的关键是如何根据样本值计算出正态分布的均值向量和协方差矩阵,均值向量是通过均值函数<span\nclass=\"math inline\">\\(\\mu(x)\\)</span>根据每个采样点x计算构造的,可简单令<span\nclass=\"math inline\">\\(\\mu(x)=c\\)</span>,或者将均值设置为0,因为即使均值设置为常数,由于有方差的作用,依然能够对数据进行有效建模。</p>\n<p>协方差通过核函数<span\nclass=\"math inline\">\\(k(x,x^{'})\\)</span>计算得到,也称为协方差函数,协方差函数需要满足以下要求:</p>\n<ol type=\"1\">\n<li>距离相近的样本点<span class=\"math inline\">\\(x\\)</span>和<span\nclass=\"math inline\">\\(x^{'}\\)</span>之间有更大的正协方差值,因为相近的两个点的函数值有更强的相关性</li>\n<li>保证协方差矩阵是对称半正定矩阵</li>\n</ol>\n<p>常用的是高斯核和Matern核,高斯核定义为:</p>\n<p><span class=\"math display\">\\[k\\left(\\mathbf{x}_{1},\n\\mathbf{x}_{2}\\right)=\\alpha_{0} \\exp \\left(-\\frac{1}{2\n\\sigma^{2}}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|^{2}\\right)\\]</span></p>\n<p>Matern核定义为:</p>\n<p><span class=\"math display\">\\[k\\left(\\mathbf{x}_{1},\n\\mathbf{x}_{2}\\right)=\\frac{2^{1-v}}{\\Gamma(v)}\\left(\\sqrt{2\nv}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|\\right)^{v}\nK_{v}\\left(\\sqrt{2\nv}\\left\\|\\mathbf{x}_{1}-\\mathbf{x}_{2}\\right\\|\\right)\\]</span></p>\n<p>其中<span class=\"math inline\">\\(\\Gamma\\)</span>是伽马函数,<span\nclass=\"math inline\">\\(K_{v}\\)</span>是贝塞尔函数(Bessel function),<span\nclass=\"math inline\">\\(v\\)</span>是人工设定的正参数。用核函数计算任意两点之间的核函数值,得到核函数矩阵<span\nclass=\"math inline\">\\(K\\)</span>作为协方差矩阵的估计值:</p>\n<p><span class=\"math display\">\\[\\mathbf{K}=\\left[\\begin{array}{ccc}\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{1}\\right) & \\ldots &\nk\\left(\\mathbf{x}_{1}, \\mathbf{x}_{t}\\right) \\\\\n\\ldots & \\ldots & \\ldots \\\\\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{1}\\right) & \\ldots &\nk\\left(\\mathbf{x}_{t}, \\mathbf{x}_{t}\\right)\n\\end{array}\\right]\\]</span></p>\n<p>在计算出均值向量和协方差矩阵之后,可以根据此多维正态分布预测<span\nclass=\"math inline\">\\(f(x)\\)</span>在任意点处的概率分布。假设已经得到了一组样本<span\nclass=\"math inline\">\\(X_{1:t}\\)</span>,以及对应的函数值<span\nclass=\"math inline\">\\(f(x_{1:t})\\)</span>,如果要预测新的点<span\nclass=\"math inline\">\\(x\\)</span>的函数值<span\nclass=\"math inline\">\\(f(x)\\)</span>的期望<span\nclass=\"math inline\">\\(\\mu(x)\\)</span>和方差<span\nclass=\"math inline\">\\(\\sigma^{2}(x)\\)</span>,令<span\nclass=\"math inline\">\\(x_{t+1}=x\\)</span>,加入该点后,<span\nclass=\"math inline\">\\(f(x_{1:t+1})\\)</span>服从<span\nclass=\"math inline\">\\(t+1\\)</span>维正态分布,即:</p>\n<p><span class=\"math display\">\\[\\left[\\begin{array}{c}\nf\\left(\\mathbf{x}_{1: t}\\right) \\\\\nf\\left(\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right] \\sim N\\left(\\left[\\begin{array}{c}\n\\mu\\left(\\mathbf{x}_{1: t}\\right) \\\\\n\\mu\\left(\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right],\\left[\\begin{array}{cc}\n\\mathbf{K} & \\mathbf{k} \\\\\n\\mathbf{k}^{\\mathrm{T}} & k\\left(\\mathbf{x}_{t+1},\n\\mathbf{x}_{t+1}\\right)\n\\end{array}\\right]\\right)\\]</span></p>\n<p>在已知<span class=\"math inline\">\\(f(x_{1:t})\\)</span>的情况下,<span\nclass=\"math inline\">\\(f(x_{t+1})\\)</span>服从一维正态分布,即:</p>\n<p><span class=\"math display\">\\[f\\left(\\mathbf{x}_{t+1}\\right) \\mid\nf\\left(\\mathbf{x}_{1: t}\\right) \\sim N\\left(\\mu,\n\\sigma^{2}\\right)\\]</span></p>\n<p>可以计算出对应的均值和方差,公式如下:</p>\n<p><span class=\"math display\">\\[\\begin{array}{l}\n\\mu=\\mathbf{k}^{\\mathrm{T}} \\mathbf{K}^{-1}\\left(f\\left(\\mathbf{x}_{1:\nt}\\right)-\\mu\\left(\\mathbf{x}_{1:\nt}\\right)\\right)+\\mu\\left(\\mathbf{x}_{t+1}\\right) \\\\\n\\sigma^{2}=k\\left(\\mathbf{x}_{t+1},\n\\mathbf{x}_{t+1}\\right)-\\mathbf{k}^{\\mathrm{T}} \\mathbf{K}^{-1}\n\\mathbf{k}\n\\end{array}\\]</span></p>\n<p>计算均值利用了已有采样点处函数值<span\nclass=\"math inline\">\\(f(x_{1:t})\\)</span>,方差只与协方差值有关,与<span\nclass=\"math inline\">\\(f(x_{1:t})\\)</span>无关</p>\n<h3 id=\"gpr代码实现\">GPR代码实现</h3>\n<ul>\n<li>定义高斯核</li>\n</ul>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">kernel</span>(<span class=\"params\">X1, X2, l=<span class=\"number\">1.0</span>, sigma_f=<span class=\"number\">1.0</span></span>):</span><br><span class=\"line\"> <span class=\"string\">'''</span></span><br><span class=\"line\"><span class=\"string\"> X1: Array of m points (m x d).</span></span><br><span class=\"line\"><span class=\"string\"> X2: Array of n points (n x d).</span></span><br><span class=\"line\"><span class=\"string\"> Returns:</span></span><br><span class=\"line\"><span class=\"string\"> Covariance matrix (m x n).</span></span><br><span class=\"line\"><span class=\"string\"> '''</span></span><br><span class=\"line\"> sqdist = np.<span class=\"built_in\">sum</span>(X1**<span class=\"number\">2</span>, <span class=\"number\">1</span>).reshape(-<span class=\"number\">1</span>, <span class=\"number\">1</span>) + np.<span class=\"built_in\">sum</span>(X2**<span class=\"number\">2</span>, <span class=\"number\">1</span>) - <span class=\"number\">2</span> * np.dot(X1, X2.T)</span><br><span class=\"line\"> <span class=\"keyword\">return</span> sigma_f**<span class=\"number\">2</span> * np.exp(-<span class=\"number\">0.5</span> / l**<span class=\"number\">2</span> * sqdist)</span><br></pre></td></tr></table></figure>\n<ul>\n<li>计算均值和协方差矩阵</li>\n</ul>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">posterior_predictive</span>(<span class=\"params\">X_s, X_train, Y_train, l=<span class=\"number\">1.0</span>, sigma_f=<span class=\"number\">1.0</span>, sigma_y=<span class=\"number\">1e-8</span></span>):</span><br><span class=\"line\"> <span class=\"string\">'''</span></span><br><span class=\"line\"><span class=\"string\"> 根据先验数据点计算均值向量和协方差矩阵</span></span><br><span class=\"line\"><span class=\"string\"> Args:</span></span><br><span class=\"line\"><span class=\"string\"> X_s: New input locations (n x d).</span></span><br><span class=\"line\"><span class=\"string\"> X_train: Training locations (m x d).</span></span><br><span class=\"line\"><span class=\"string\"> Y_train: Training targets (m x 1).</span></span><br><span class=\"line\"><span class=\"string\"> l: Kernel length parameter.</span></span><br><span class=\"line\"><span class=\"string\"> sigma_f: Kernel vertical variation parameter.</span></span><br><span class=\"line\"><span class=\"string\"> sigma_y: Noise parameter.</span></span><br><span class=\"line\"><span class=\"string\"> Returns:</span></span><br><span class=\"line\"><span class=\"string\"> Posterior mean vector (n x d) and covariance matrix (n x n).</span></span><br><span class=\"line\"><span class=\"string\"> '''</span></span><br><span class=\"line\"> K = kernel(X_train, X_train, l, sigma_f) + sigma_y**<span class=\"number\">2</span> * np.eye(<span class=\"built_in\">len</span>(X_train))</span><br><span class=\"line\"> K_s = kernel(X_train, X_s, l, sigma_f)</span><br><span class=\"line\"> K_ss = kernel(X_s, X_s, l, sigma_f) + <span class=\"number\">1e-8</span> * np.eye(<span class=\"built_in\">len</span>(X_s))</span><br><span class=\"line\"> K_inv = np.linalg.inv(K)</span><br><span class=\"line\"> mu_s = K_s.T.dot(K_inv).dot(Y_train) <span class=\"comment\"># 均值向量, 注意均值函数被设置为 0</span></span><br><span class=\"line\"> cov_s = K_ss - K_s.T.dot(K_inv).dot(K_s) <span class=\"comment\"># 协方差矩阵</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> mu_s, cov_s</span><br></pre></td></tr></table></figure>\n<ul>\n<li>GPR拟合效果绘制</li>\n</ul>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br><span class=\"line\">11</span><br><span class=\"line\">12</span><br><span class=\"line\">13</span><br><span class=\"line\">14</span><br><span class=\"line\">15</span><br><span class=\"line\">16</span><br><span class=\"line\">17</span><br><span class=\"line\">18</span><br><span class=\"line\">19</span><br><span class=\"line\">20</span><br><span class=\"line\">21</span><br><span class=\"line\">22</span><br><span class=\"line\">23</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">plot_gp</span>(<span class=\"params\">mu, cov, X, X_train=<span class=\"literal\">None</span>, Y_train=<span class=\"literal\">None</span>, samples=[]</span>):</span><br><span class=\"line\"> <span class=\"comment\"># 定义gp绘图函数</span></span><br><span class=\"line\"> X = X.ravel()</span><br><span class=\"line\"> mu = mu.ravel()</span><br><span class=\"line\"> uncertainty = <span class=\"number\">1.96</span> * np.sqrt(np.diag(cov)) <span class=\"comment\"># 1.96倍标准差对应95%置信度区间</span></span><br><span class=\"line\"></span><br><span class=\"line\"> plt.fill_between(X, mu + uncertainty, mu - uncertainty, alpha=<span class=\"number\">0.1</span>)</span><br><span class=\"line\"> plt.plot(X, mu, label=<span class=\"string\">'Mean'</span>)</span><br><span class=\"line\"> <span class=\"keyword\">for</span> i, sample <span class=\"keyword\">in</span> <span class=\"built_in\">enumerate</span>(samples):</span><br><span class=\"line\"> plt.plot(X, sample, lw=<span class=\"number\">1</span>, ls=<span class=\"string\">'--'</span>, label=<span class=\"string\">f'Sample <span class=\"subst\">{i+<span class=\"number\">1</span>}</span>'</span>)</span><br><span class=\"line\"> <span class=\"keyword\">if</span> X_train <span class=\"keyword\">is</span> <span class=\"keyword\">not</span> <span class=\"literal\">None</span>:</span><br><span class=\"line\"> plt.plot(X_train, Y_train, <span class=\"string\">'rx'</span>)</span><br><span class=\"line\"> plt.legend()</span><br><span class=\"line\"></span><br><span class=\"line\">X = np.arange(-<span class=\"number\">5</span>, <span class=\"number\">5</span>, <span class=\"number\">0.2</span>).reshape(-<span class=\"number\">1</span>, <span class=\"number\">1</span>)</span><br><span class=\"line\">X_train = np.array([-<span class=\"number\">4</span>, -<span class=\"number\">3</span>, -<span class=\"number\">2</span>, -<span class=\"number\">1</span>, <span class=\"number\">1</span>]).reshape(-<span class=\"number\">1</span>, <span class=\"number\">1</span>)</span><br><span class=\"line\">Y_train = np.sin(X_train)</span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 计算均值向量和协方差矩阵</span></span><br><span class=\"line\">mu_s, cov_s = posterior_predictive(X, X_train, Y_train)</span><br><span class=\"line\"></span><br><span class=\"line\">samples = np.random.multivariate_normal(mu_s.ravel(), cov_s, <span class=\"number\">1</span>)</span><br><span class=\"line\">plot_gp(mu_s, cov_s, X, X_train=X_train, Y_train=Y_train, samples=samples)</span><br></pre></td></tr></table></figure>\n<p>结果如下图所示:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220510004441.png\" width=\"50%\" height=\"50%\"></p>\n<p>其中,红色叉代表观测值,蓝色线代表均值,浅蓝色区域代表95%置信区间。</p>\n<h2 id=\"贝叶斯优化原理\">贝叶斯优化原理</h2>\n<h4 id=\"基本过程\">基本过程</h4>\n<p>以下是贝叶斯优化的过程</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220510004715.png\"></p>\n<p>有两个主要组成部分:</p>\n<ol type=\"1\">\n<li>GPR。根据观测点构建高斯过程回归模型,该模型能求取任意点处的函数值及后验概率。</li>\n<li>构造采集函数(acquisition\nfunction),用于决定本次迭代在哪个点处进行采样。</li>\n</ol>\n<p>算法首先初始化<span\nclass=\"math inline\">\\(n_{0}\\)</span>个点,设定最大迭代次数<span\nclass=\"math inline\">\\(N\\)</span>,开始循环求解,每次增加一个点,寻找下一个点时根据已经找到的<span\nclass=\"math inline\">\\(n\\)</span>个候选解建立高斯回归模型,通过这个模型能得到任意点处的函数值的后验概率。然后根据后验概率构造采集函数,寻找采集函数的极大值点作为下一个搜索点,以此循环,直到达到最大迭代次数,返回<span\nclass=\"math inline\">\\(N\\)</span>个解中的极大值作为最优解。</p>\n<p>采集函数的选择有很多种,最常用的是期望改进(Expected\nImprovement,EI),下一节介绍下EI的原理。</p>\n<h4 id=\"采集函数\">采集函数</h4>\n<p>假设已经搜索了n个点,这些点中的函数极大值记为</p>\n<p><span class=\"math display\">\\[f_{n}^{*} =\nmax(f(x_{1},...,f(x_{n}))\\]</span></p>\n<p>考虑下一个搜索点,计算该点处的函数值<span\nclass=\"math inline\">\\(f(x)\\)</span>,如果<span\nclass=\"math inline\">\\(f(x)>=f_{n}^{*}\\)</span>,则这<span\nclass=\"math inline\">\\(n+1\\)</span>个点处的函数极大值为<span\nclass=\"math inline\">\\(f(x)\\)</span>,否则为<span\nclass=\"math inline\">\\(f_{n}^{*}\\)</span></p>\n<p>加入这个新的点后,函数值的改进可以记为</p>\n<p><span class=\"math display\">\\[e^{+} = max(0, f(x) -\nf_{n}^{*})\\]</span></p>\n<p>我们的目标是找到使得上面的改进值最大的<span\nclass=\"math inline\">\\(x\\)</span>,但是该点的函数值在我们找到<span\nclass=\"math inline\">\\(x\\)</span>是多少前又是未知的,幸运的是我们知道<span\nclass=\"math inline\">\\(f(x)\\)</span>的概率分布,因此我们可以计算在所有x处的改进值的数学期望,然后选择期望最大的<span\nclass=\"math inline\">\\(x\\)</span>作为下一个搜索点。定义期望改进(EI)函数如下:</p>\n<p><span class=\"math display\">\\[EI_{n}(x) = E_{n}[max(0, f(x) -\nf_{n}^{*})]\\]</span></p>\n<p>令<span class=\"math inline\">\\(z=f(x)\\)</span>,则有:</p>\n<p><span class=\"math display\">\\[\\begin{aligned}\n\\mathrm{EI}_{n}(\\mathbf{x}) &=\\int_{-\\infty}^{+\\infty}\\left (max(0,\nz-f_{n}^{*})\\right) \\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp\n\\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z \\\\\n&=\\int_{f_{n}^{*}}^{+\\infty}\\left(z-f_{n}^{*}\\right)\n\\frac{1}{\\sqrt{2 \\pi} \\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2\n\\sigma^{2}}\\right) d z\n\\end{aligned}\\]</span></p>\n<p>换元法,得到:</p>\n<p><span class=\"math display\">\\[\\\\\n\\begin{array}{c}\n\\int_{f_{n}^{*}}^{+\\infty}\\left(z-f_{n}^{*}\\right) \\frac{1}{\\sqrt{2 \\pi}\n\\sigma} \\exp \\left(-\\frac{(z-\\mu)^{2}}{2 \\sigma^{2}}\\right) d z\n\\\\\n=\\left(\\mu-f_{n}^{*}\\right)\\left(1-\\Phi\\left(\\left(f_{n}^{*}-\\mu\\right)\n/ \\sigma\\right)\\right)+\\sigma \\varphi\\left(\\left(f_{n}^{*}-\\mu\\right) /\n\\sigma\\right)\n\\end{array}\\]</span></p>\n<p>其中,<span\nclass=\"math inline\">\\(\\varphi(x)\\)</span>是标准正态分布的概率密度函数,<span\nclass=\"math inline\">\\(\\phi(x)\\)</span>是是标准正态分布的分布函数</p>\n<p>我们的目标是求EI的极值获取下一个采样点,即</p>\n<p><span class=\"math display\">\\[x_{n+1} = argmax EI_{n}(x)\\]</span></p>\n<p>现在目标函数已知,且能得到目标函数的一阶导数和二阶导数,可以通过梯度下降法或L-BFGS求解极值,这里不再展开叙述</p>\n<h2 id=\"贝叶斯优化应用\">贝叶斯优化应用</h2>\n<p>BO有许多开源的实现,scikit-optimize 以及 参考资料4 <a\nhref=\"https://github.com/fmfn/BayesianOptimization\">BayesianOptimization</a>都封装了BO,这里我们采用参考资料4中封装的BO进行演示。</p>\n<ol type=\"1\">\n<li>直接用pip安装BayesianOptimization</li>\n</ol>\n<figure class=\"highlight shell\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">pip install bayesian-optimization</span><br></pre></td></tr></table></figure>\n<ol start=\"2\" type=\"1\">\n<li>定义黑盒函数</li>\n</ol>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">def</span> <span class=\"title function_\">black_box_function</span>(<span class=\"params\">x, y</span>):</span><br><span class=\"line\"> <span class=\"string\">"""</span></span><br><span class=\"line\"><span class=\"string\"> x,y 均是待调优参数</span></span><br><span class=\"line\"><span class=\"string\"> """</span></span><br><span class=\"line\"> <span class=\"keyword\">return</span> -x ** <span class=\"number\">2</span> - (y - <span class=\"number\">1</span>) ** <span class=\"number\">2</span> + <span class=\"number\">1</span> </span><br></pre></td></tr></table></figure>\n<ol start=\"3\" type=\"1\">\n<li>初始化BO</li>\n</ol>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br><span class=\"line\">6</span><br><span class=\"line\">7</span><br><span class=\"line\">8</span><br><span class=\"line\">9</span><br><span class=\"line\">10</span><br></pre></td><td class=\"code\"><pre><span class=\"line\"><span class=\"keyword\">from</span> bayes_opt <span class=\"keyword\">import</span> BayesianOptimization</span><br><span class=\"line\"></span><br><span class=\"line\">pbounds = {<span class=\"string\">'x'</span>: (<span class=\"number\">2</span>, <span class=\"number\">4</span>), <span class=\"string\">'y'</span>: (-<span class=\"number\">3</span>, <span class=\"number\">3</span>)} <span class=\"comment\"># 设定x, y 调参范围</span></span><br><span class=\"line\"></span><br><span class=\"line\"><span class=\"comment\"># 初始化bo</span></span><br><span class=\"line\">optimizer = BayesianOptimization(</span><br><span class=\"line\"> f=black_box_function,</span><br><span class=\"line\"> pbounds=pbounds,</span><br><span class=\"line\"> random_state=<span class=\"number\">1</span>,</span><br><span class=\"line\">)</span><br></pre></td></tr></table></figure>\n<ol start=\"4\" type=\"1\">\n<li>进行迭代</li>\n</ol>\n<figure class=\"highlight python\"><table><tr><td class=\"gutter\"><pre><span class=\"line\">1</span><br><span class=\"line\">2</span><br><span class=\"line\">3</span><br><span class=\"line\">4</span><br><span class=\"line\">5</span><br></pre></td><td class=\"code\"><pre><span class=\"line\">optimizer.maximize(</span><br><span class=\"line\"> init_points=<span class=\"number\">2</span>, <span class=\"comment\"># 初始解个数</span></span><br><span class=\"line\"> n_iter=<span class=\"number\">20</span>, <span class=\"comment\"># 迭代次数</span></span><br><span class=\"line\">)</span><br><span class=\"line\"><span class=\"built_in\">print</span>(optimizer.<span class=\"built_in\">max</span>) <span class=\"comment\"># 输出最大值及对应的参数组合</span></span><br></pre></td></tr></table></figure>\n<p>输出:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511172849.png\" width=\"80%\" height=\"80%\"></p>\n<p>函数<span class=\"math inline\">\\(f(x,y) = -x^{2} - (y-1)^{2} +\n1\\)</span>,当<span class=\"math inline\">\\(x\\in[2,4]\\)</span>,<span\nclass=\"math inline\">\\(y\\in[-3,3]\\)</span>时,很显然,当<span\nclass=\"math inline\">\\(x=2,y=1\\)</span>时能取到最大值,BO给出的解已经相当接近最优解</p>\n<p>运行了多次,BO给出的解非常稳健,如下所示:</p>\n<p><img src=\"https://images.bumpchicken.cn/img/20220511173250.png\"></p>\n<h2 id=\"参考资料\">参考资料</h2>\n<ol type=\"1\">\n<li><p>《机器学习 原理、算法与应用》 雷明著</p></li>\n<li><p>Frazier P I. A tutorial on bayesian optimization[J]. arXiv\npreprint arXiv:1807.02811, 2018.</p></li>\n<li><p>https://github.com/krasserm/bayesian-machine-learning</p></li>\n<li><p>https://github.com/fmfn/BayesianOptimization</p></li>\n<li><p>https://github.com/scikit-optimize/scikit-optimize</p></li>\n</ol>"}],"PostAsset":[],"PostCategory":[{"post_id":"cl39lom8j0001otud2qaac68v","category_id":"cl39lom8n0004otudhtlzhtq6","_id":"cl39lom8s000eotud2h7n9syq"},{"post_id":"cl39lom8m0003otudbl8r2wm1","category_id":"cl39lom8r000aotudaedb4kmu","_id":"cl39lom8u000iotud0yxl1iti"},{"post_id":"cl39lom8p0007otud53k4fp9e","category_id":"cl39lom8t000fotud9w95652t","_id":"cl39lom8u000motuddt98djbq"},{"post_id":"cl39lom8q0008otudgvwt3cjw","category_id":"cl39lom8u000jotud62rchkn2","_id":"cl39lom8w000rotud4bub6us2"},{"post_id":"cl39lom8r0009otudhi5y2ps5","category_id":"cl39lom8t000fotud9w95652t","_id":"cl39lom8x000uotudbord8w3j"},{"post_id":"cl39lom8s000dotudhu169n9s","category_id":"cl39lom8w000qotudcbeh58vo","_id":"cl39lom8x000wotud7jgqcyat"},{"post_id":"cl39lom8y000xotud1ven0tcf","category_id":"cl39lom8r000aotudaedb4kmu","_id":"cl39lom8z0010otud0jwp3mjz"},{"post_id":"cl39lom8y000yotud6sw262pm","category_id":"cl39lom8r000aotudaedb4kmu","_id":"cl39lom8z0011otudbe6c1mgi"}],"PostTag":[{"post_id":"cl39lom8j0001otud2qaac68v","tag_id":"cl39lom8o0005otud0n1e8aer","_id":"cl39lom8s000cotud8oejh4ij"},{"post_id":"cl39lom8m0003otudbl8r2wm1","tag_id":"cl39lom8r000botud1v9h1gsa","_id":"cl39lom8t000hotudckv0d3h6"},{"post_id":"cl39lom8p0007otud53k4fp9e","tag_id":"cl39lom8t000gotud93ii4ivf","_id":"cl39lom8u000lotud4yus5df1"},{"post_id":"cl39lom8q0008otudgvwt3cjw","tag_id":"cl39lom8u000kotudctm69426","_id":"cl39lom8w000potudaxh07l35"},{"post_id":"cl39lom8r0009otudhi5y2ps5","tag_id":"cl39lom8t000gotud93ii4ivf","_id":"cl39lom8x000totud445wagw1"},{"post_id":"cl39lom8s000dotudhu169n9s","tag_id":"cl39lom8t000gotud93ii4ivf","_id":"cl39lom8x000votud4fhf3pbz"},{"post_id":"cl39lom8y000xotud1ven0tcf","tag_id":"cl39lom8z000zotud54314igq","_id":"cl39lom900013otudb1cphrvn"},{"post_id":"cl39lom8y000yotud6sw262pm","tag_id":"cl39lom8z0012otudf1gqa83b","_id":"cl39lom900014otud1fso2yqv"}],"Tag":[{"name":"时间序列分析","_id":"cl39lom8o0005otud0n1e8aer"},{"name":"聚类","_id":"cl39lom8r000botud1v9h1gsa"},{"name":"根因定位","_id":"cl39lom8t000gotud93ii4ivf"},{"name":"异常检测","_id":"cl39lom8u000kotudctm69426"},{"name":"分类","_id":"cl39lom8z000zotud54314igq"},{"name":"AutoML","_id":"cl39lom8z0012otudf1gqa83b"}]}}