From 1f1116721ca51650b37a8b1166d6caef62485521 Mon Sep 17 00:00:00 2001 From: TingsongYu Date: Fri, 8 Nov 2024 10:41:47 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B01.4=E7=9A=84cuDNN?= =?UTF-8?q?=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/chapter-1/1.1-PyTorch-Introduction.html | 2 +- docs/chapter-1/1.2-Anaconda.html | 2 +- docs/chapter-1/1.3-Pycharm.html | 2 +- docs/chapter-1/1.4-CUDA&cuDNN.html | 30 +++++++++++++-- docs/chapter-1/1.4-CUDA&cuDNN.md | 37 ++++++++++++++++++- docs/chapter-1/1.5-PyTorch-install.html | 2 +- .../1.6-JupyterNotebook-install.html | 2 +- docs/chapter-1/1.7-PyTorch2.0.html | 2 +- docs/chapter-1/index.html | 2 +- docs/chapter-10/10.1-qwen.html | 2 +- docs/chapter-10/10.2-chatglm.html | 2 +- docs/chapter-10/10.3-baichuan.html | 2 +- docs/chapter-10/10.4-yi.html | 2 +- docs/chapter-10/10.5-gpt-academic.html | 2 +- docs/chapter-10/index.html | 2 +- docs/chapter-11/11.1-onnx-introduction.html | 2 +- docs/chapter-11/11.2-onnxruntime-intro.html | 2 +- .../chapter-11/11.3-onnxruntime-advanced.html | 2 +- docs/chapter-11/index.html | 2 +- docs/chapter-12/12.1-trt-install.html | 2 +- docs/chapter-12/12.2-trt-workflow.html | 2 +- docs/chapter-12/12.3-trt-trtexec.html | 2 +- docs/chapter-12/12.4-trt-tools.html | 2 +- docs/chapter-12/12.5-trt-python-api.html | 2 +- docs/chapter-12/12.6-quantization.html | 2 +- docs/chapter-12/12.7-ptq.html | 2 +- docs/chapter-12/12.8-qat.html | 2 +- docs/chapter-12/12.9-trt-deploy.html | 2 +- docs/chapter-12/index.html | 2 +- docs/chapter-2/2.1-module-tree.html | 2 +- docs/chapter-2/2.2-covid-19-cls.html | 2 +- docs/chapter-2/2.3-datastruct-tensor.html | 2 +- docs/chapter-2/2.4-method-tensor.html | 2 +- docs/chapter-2/2.5-computational-graphs.html | 2 +- docs/chapter-2/2.6-autograd.html | 2 +- docs/chapter-2/index.html | 2 +- docs/chapter-3/3.1-dataset.html | 2 +- docs/chapter-3/3.2-dataloader.html | 2 +- docs/chapter-3/3.3-dataset-useful-api.html | 2 +- docs/chapter-3/3.4-transforms.html | 2 +- docs/chapter-3/3.5-torchvision-dataset.html | 2 +- docs/chapter-3/index.html | 2 +- docs/chapter-4/4.1-module&Parameter.html | 2 +- docs/chapter-4/4.2-containers.html | 2 +- docs/chapter-4/4.3-common-module.html | 2 +- docs/chapter-4/4.4-module-api.html | 2 +- docs/chapter-4/4.5-hook-func.html | 2 +- docs/chapter-4/4.6-classic-model.html | 2 +- docs/chapter-4/4.7-weight-init.html | 2 +- docs/chapter-4/index.html | 2 +- docs/chapter-5/5.1-loss-function.html | 2 +- docs/chapter-5/5.2-Optimizer.html | 2 +- docs/chapter-5/5.3-lr-scheduler.html | 2 +- docs/chapter-5/index.html | 2 +- docs/chapter-6/6.1-tensorboard.html | 2 +- docs/chapter-6/6.2-cnn-visualization.html | 2 +- docs/chapter-6/6.3-conf_matrix.html | 2 +- docs/chapter-6/6.4-cam-vis.html | 2 +- docs/chapter-6/6.5-model-print.html | 2 +- docs/chapter-6/index.html | 2 +- docs/chapter-7/7.1-serialization.html | 2 +- docs/chapter-7/7.2-finetune.html | 2 +- docs/chapter-7/7.3-gpu.html | 2 +- docs/chapter-7/7.4-training-script.html | 2 +- docs/chapter-7/7.5-torchmetrics.html | 2 +- docs/chapter-7/7.6-albumentations.html | 2 +- docs/chapter-7/7.7-torchensemble.html | 2 +- docs/chapter-7/index.html | 2 +- docs/chapter-8/8.1-classification.html | 2 +- docs/chapter-8/8.2-segmentation.html | 2 +- docs/chapter-8/8.3-detection.html | 2 +- docs/chapter-8/8.4-tracking-1.html | 2 +- docs/chapter-8/8.4-tracking-2.html | 2 +- docs/chapter-8/8.5-cycleGAN.html | 2 +- docs/chapter-8/8.6-diffusion-model.html | 2 +- docs/chapter-8/8.7-image-captioning.html | 2 +- docs/chapter-8/8.8-image-retrieval-1.html | 2 +- docs/chapter-8/8.8-image-retrieval-2.html | 2 +- docs/chapter-8/index.html | 2 +- docs/chapter-9/9.1-nlp_introduction.html | 2 +- docs/chapter-9/9.2-rnn-lstm.html | 2 +- docs/chapter-9/9.3-seq2seq.html | 2 +- docs/chapter-9/9.4-transformer.html | 2 +- docs/chapter-9/9.5-bert.html | 2 +- docs/chapter-9/9.6-gpt.html | 2 +- docs/chapter-9/index.html | 2 +- docs/index.html | 2 +- docs/preface.html | 2 +- docs/search_plus_index.json | 2 +- 89 files changed, 148 insertions(+), 93 deletions(-) diff --git a/docs/chapter-1/1.1-PyTorch-Introduction.html b/docs/chapter-1/1.1-PyTorch-Introduction.html index 43e4ce1..1c42b69 100644 --- a/docs/chapter-1/1.1-PyTorch-Introduction.html +++ b/docs/chapter-1/1.1-PyTorch-Introduction.html @@ -1510,7 +1510,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"next":{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"path":"chapter-1/1.2-Anaconda.md","ref":"chapter-1/1.2-Anaconda.md","articles":[]},"previous":{"title":"第一章 PyTorch 简介与安装","level":"2.1","depth":1,"path":"chapter-1/README.md","ref":"chapter-1/README.md","articles":[{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"path":"chapter-1/1.1-PyTorch-Introduction.md","ref":"chapter-1/1.1-PyTorch-Introduction.md","articles":[]},{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"path":"chapter-1/1.2-Anaconda.md","ref":"chapter-1/1.2-Anaconda.md","articles":[]},{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"path":"chapter-1/1.3-Pycharm.md","ref":"chapter-1/1.3-Pycharm.md","articles":[]},{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"path":"chapter-1/1.4-CUDA&cuDNN.md","ref":"chapter-1/1.4-CUDA&cuDNN.md","articles":[]},{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"path":"chapter-1/1.5-PyTorch-install.md","ref":"chapter-1/1.5-PyTorch-install.md","articles":[]},{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"path":"chapter-1/1.6-JupyterNotebook-install.md","ref":"chapter-1/1.6-JupyterNotebook-install.md","articles":[]},{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"path":"chapter-1/1.7-PyTorch2.0.md","ref":"chapter-1/1.7-PyTorch2.0.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.1-PyTorch-Introduction.md","mtime":"2024-05-10T14:14:05.584Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"next":{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"path":"chapter-1/1.2-Anaconda.md","ref":"chapter-1/1.2-Anaconda.md","articles":[]},"previous":{"title":"第一章 PyTorch 简介与安装","level":"2.1","depth":1,"path":"chapter-1/README.md","ref":"chapter-1/README.md","articles":[{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"path":"chapter-1/1.1-PyTorch-Introduction.md","ref":"chapter-1/1.1-PyTorch-Introduction.md","articles":[]},{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"path":"chapter-1/1.2-Anaconda.md","ref":"chapter-1/1.2-Anaconda.md","articles":[]},{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"path":"chapter-1/1.3-Pycharm.md","ref":"chapter-1/1.3-Pycharm.md","articles":[]},{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"path":"chapter-1/1.4-CUDA&cuDNN.md","ref":"chapter-1/1.4-CUDA&cuDNN.md","articles":[]},{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"path":"chapter-1/1.5-PyTorch-install.md","ref":"chapter-1/1.5-PyTorch-install.md","articles":[]},{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"path":"chapter-1/1.6-JupyterNotebook-install.md","ref":"chapter-1/1.6-JupyterNotebook-install.md","articles":[]},{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"path":"chapter-1/1.7-PyTorch2.0.md","ref":"chapter-1/1.7-PyTorch2.0.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.1-PyTorch-Introduction.md","mtime":"2024-05-10T14:14:05.584Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-1/1.2-Anaconda.html b/docs/chapter-1/1.2-Anaconda.html index 16ea088..b56f510 100644 --- a/docs/chapter-1/1.2-Anaconda.html +++ b/docs/chapter-1/1.2-Anaconda.html @@ -1492,7 +1492,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"next":{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"path":"chapter-1/1.3-Pycharm.md","ref":"chapter-1/1.3-Pycharm.md","articles":[]},"previous":{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"path":"chapter-1/1.1-PyTorch-Introduction.md","ref":"chapter-1/1.1-PyTorch-Introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.2-Anaconda.md","mtime":"2024-05-10T14:21:02.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"next":{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"path":"chapter-1/1.3-Pycharm.md","ref":"chapter-1/1.3-Pycharm.md","articles":[]},"previous":{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"path":"chapter-1/1.1-PyTorch-Introduction.md","ref":"chapter-1/1.1-PyTorch-Introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.2-Anaconda.md","mtime":"2024-05-10T14:21:02.586Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-1/1.3-Pycharm.html b/docs/chapter-1/1.3-Pycharm.html index 6b5c368..c3f0000 100644 --- a/docs/chapter-1/1.3-Pycharm.html +++ b/docs/chapter-1/1.3-Pycharm.html @@ -1666,7 +1666,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"next":{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"path":"chapter-1/1.4-CUDA&cuDNN.md","ref":"chapter-1/1.4-CUDA&cuDNN.md","articles":[]},"previous":{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"path":"chapter-1/1.2-Anaconda.md","ref":"chapter-1/1.2-Anaconda.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.3-Pycharm.md","mtime":"2024-05-10T14:25:24.930Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"next":{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"path":"chapter-1/1.4-CUDA&cuDNN.md","ref":"chapter-1/1.4-CUDA&cuDNN.md","articles":[]},"previous":{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"path":"chapter-1/1.2-Anaconda.md","ref":"chapter-1/1.2-Anaconda.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.3-Pycharm.md","mtime":"2024-05-10T14:25:24.930Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-1/1.4-CUDA&cuDNN.html b/docs/chapter-1/1.4-CUDA&cuDNN.html index a356709..882326a 100644 --- a/docs/chapter-1/1.4-CUDA&cuDNN.html +++ b/docs/chapter-1/1.4-CUDA&cuDNN.html @@ -1599,7 +1599,7 @@

CUDA

nvcc -V

-

cuDNN

+

cuDNN

有了CUDA平台,还需要安装cuDNN,cuDNN全称为NVIDIA CUDA Deep Neural Network (cuDNN) 。它是一个深度神经网络的加速库,里边实现了神经网络常用的操作,并且是高度优化的,可以极大地榨干NVIDA显卡的性能,因此用N卡都会用cuDNN库。

cuDNN库的安装非常简单,与其说是安装,不如说是下载库文件,放到CUDA所在的目录下。具体步骤如下:

    @@ -1611,11 +1611,33 @@

    cuDNN

  1. 将下载好的压缩包cudnn-10.2-windows10-x64-v8.2.4.15.zip 解压

  2. 分别将bin、include、lib\x64下的文件分别对应拷贝到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2文件夹下的bin、include、lib\x64下

    -
  3. -
  4. 打开命令窗口,在C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\extras\demo_suite文件夹中分别执行bandwidthTest.exe和deviceQuery.exe。观察到Result=PASS 即表示安装成功。

    +

    到这里CuDNN安装完毕。

+

CUDA 安装验证

+

​ 接下来运行NVIDIA提供的官方案例,检查CUDA的正常是否正确,这里暂时未找到好的办法验证CuDNN的安装正确与否,CuDNN的验证可在后续pytorch安装好之后,采用代码验证。

+

​ 打开命令窗口,在C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\extras\demo_suite文件夹中分别执行bandwidthTest.exe和deviceQuery.exe。观察到Result=PASS 即表示CUDA安装成功。

1

+

cuDNN验证

+
import torch
+from torch.nn import Conv2d
+
+# 检查CUDA是否可用
+print("CUDA available:", torch.cuda.is_available())
+
+# 检查cuDNN是否启用
+print("cuDNN enabled:", torch.backends.cudnn.enabled)
+
+# 创建一个卷积层并移动到GPU
+conv = Conv2d(in_channels=3, out_channels=16, kernel_size=3).cuda()
+
+# 创建一个输入张量并移动到GPU
+input_tensor = torch.randn(1, 3, 32, 32).cuda()
+
+# 前向传播
+output = conv(input_tensor)
+print("Output shape:", output.shape)
+
Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10
@@ -1662,7 +1684,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"next":{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"path":"chapter-1/1.5-PyTorch-install.md","ref":"chapter-1/1.5-PyTorch-install.md","articles":[]},"previous":{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"path":"chapter-1/1.3-Pycharm.md","ref":"chapter-1/1.3-Pycharm.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.4-CUDA&cuDNN.md","mtime":"2024-05-10T14:30:19.596Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"next":{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"path":"chapter-1/1.5-PyTorch-install.md","ref":"chapter-1/1.5-PyTorch-install.md","articles":[]},"previous":{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"path":"chapter-1/1.3-Pycharm.md","ref":"chapter-1/1.3-Pycharm.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.4-CUDA&cuDNN.md","mtime":"2024-11-07T06:33:54.439Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-1/1.4-CUDA&cuDNN.md b/docs/chapter-1/1.4-CUDA&cuDNN.md index 0bb2f45..7024f5f 100644 --- a/docs/chapter-1/1.4-CUDA&cuDNN.md +++ b/docs/chapter-1/1.4-CUDA&cuDNN.md @@ -101,7 +101,7 @@ C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2 -### **cuDNN** +## cuDNN 有了CUDA平台,还需要安装cuDNN,cuDNN全称为NVIDIA CUDA Deep Neural Network (*cuDNN*) 。它是一个深度神经网络的加速库,里边实现了神经网络常用的操作,并且是高度优化的,可以极大地榨干NVIDA显卡的性能,因此用N卡都会用cuDNN库。 @@ -117,12 +117,45 @@ cuDNN库的安装非常简单,与其说是安装,不如说是下载库文件 4. 分别将bin、include、lib\x64下的**文件**分别对应拷贝到C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2文件夹下的bin、include、lib\x64下 -5. 打开命令窗口,在C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\extras\demo_suite文件夹中分别执行bandwidthTest.exe和deviceQuery.exe。观察到Result=PASS 即表示安装成功。 + 到这里CuDNN安装完毕。 + + + +## CUDA 安装验证 + +​ 接下来运行NVIDIA提供的官方案例,检查CUDA的正常是否正确,这里暂时未找到好的办法验证CuDNN的安装正确与否,CuDNN的验证可在后续pytorch安装好之后,采用代码验证。 + +​ 打开命令窗口,在C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.2\extras\demo_suite文件夹中分别执行bandwidthTest.exe和deviceQuery.exe。观察到Result=PASS 即表示CUDA安装成功。 1 +## cuDNN验证 + +```python +import torch +from torch.nn import Conv2d + +# 检查CUDA是否可用 +print("CUDA available:", torch.cuda.is_available()) + +# 检查cuDNN是否启用 +print("cuDNN enabled:", torch.backends.cudnn.enabled) + +# 创建一个卷积层并移动到GPU +conv = Conv2d(in_channels=3, out_channels=16, kernel_size=3).cuda() + +# 创建一个输入张量并移动到GPU +input_tensor = torch.randn(1, 3, 32, 32).cuda() + +# 前向传播 +output = conv(input_tensor) +print("Output shape:", output.shape) +``` + + + diff --git a/docs/chapter-1/1.5-PyTorch-install.html b/docs/chapter-1/1.5-PyTorch-install.html index 3b4263b..836ff99 100644 --- a/docs/chapter-1/1.5-PyTorch-install.html +++ b/docs/chapter-1/1.5-PyTorch-install.html @@ -1486,7 +1486,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"next":{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"path":"chapter-1/1.6-JupyterNotebook-install.md","ref":"chapter-1/1.6-JupyterNotebook-install.md","articles":[]},"previous":{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"path":"chapter-1/1.4-CUDA&cuDNN.md","ref":"chapter-1/1.4-CUDA&cuDNN.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.5-PyTorch-install.md","mtime":"2024-05-10T14:36:43.933Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"next":{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"path":"chapter-1/1.6-JupyterNotebook-install.md","ref":"chapter-1/1.6-JupyterNotebook-install.md","articles":[]},"previous":{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"path":"chapter-1/1.4-CUDA&cuDNN.md","ref":"chapter-1/1.4-CUDA&cuDNN.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.5-PyTorch-install.md","mtime":"2024-05-10T14:36:43.933Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-1/1.6-JupyterNotebook-install.html b/docs/chapter-1/1.6-JupyterNotebook-install.html index 63975db..67c1cde 100644 --- a/docs/chapter-1/1.6-JupyterNotebook-install.html +++ b/docs/chapter-1/1.6-JupyterNotebook-install.html @@ -1591,7 +1591,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"next":{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"path":"chapter-1/1.7-PyTorch2.0.md","ref":"chapter-1/1.7-PyTorch2.0.md","articles":[]},"previous":{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"path":"chapter-1/1.5-PyTorch-install.md","ref":"chapter-1/1.5-PyTorch-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.6-JupyterNotebook-install.md","mtime":"2024-05-10T14:42:58.325Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"next":{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"path":"chapter-1/1.7-PyTorch2.0.md","ref":"chapter-1/1.7-PyTorch2.0.md","articles":[]},"previous":{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"path":"chapter-1/1.5-PyTorch-install.md","ref":"chapter-1/1.5-PyTorch-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.6-JupyterNotebook-install.md","mtime":"2024-05-10T14:42:58.325Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-1/1.7-PyTorch2.0.html b/docs/chapter-1/1.7-PyTorch2.0.html index 8e45c3a..a516ec9 100644 --- a/docs/chapter-1/1.7-PyTorch2.0.html +++ b/docs/chapter-1/1.7-PyTorch2.0.html @@ -1682,7 +1682,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"next":{"title":"第二章 PyTorch 核心模块","level":"2.2","depth":1,"path":"chapter-2/README.md","ref":"chapter-2/README.md","articles":[{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"path":"chapter-2/2.1-module-tree.md","ref":"chapter-2/2.1-module-tree.md","articles":[]},{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"path":"chapter-2/2.2-covid-19-cls.md","ref":"chapter-2/2.2-covid-19-cls.md","articles":[]},{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"path":"chapter-2/2.3-datastruct-tensor.md","ref":"chapter-2/2.3-datastruct-tensor.md","articles":[]},{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"path":"chapter-2/2.4-method-tensor.md","ref":"chapter-2/2.4-method-tensor.md","articles":[]},{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"path":"chapter-2/2.5-computational-graphs.md","ref":"chapter-2/2.5-computational-graphs.md","articles":[]},{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"path":"chapter-2/2.6-autograd.md","ref":"chapter-2/2.6-autograd.md","articles":[]}]},"previous":{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"path":"chapter-1/1.6-JupyterNotebook-install.md","ref":"chapter-1/1.6-JupyterNotebook-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.7-PyTorch2.0.md","mtime":"2024-07-17T06:14:58.497Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"next":{"title":"第二章 PyTorch 核心模块","level":"2.2","depth":1,"path":"chapter-2/README.md","ref":"chapter-2/README.md","articles":[{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"path":"chapter-2/2.1-module-tree.md","ref":"chapter-2/2.1-module-tree.md","articles":[]},{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"path":"chapter-2/2.2-covid-19-cls.md","ref":"chapter-2/2.2-covid-19-cls.md","articles":[]},{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"path":"chapter-2/2.3-datastruct-tensor.md","ref":"chapter-2/2.3-datastruct-tensor.md","articles":[]},{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"path":"chapter-2/2.4-method-tensor.md","ref":"chapter-2/2.4-method-tensor.md","articles":[]},{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"path":"chapter-2/2.5-computational-graphs.md","ref":"chapter-2/2.5-computational-graphs.md","articles":[]},{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"path":"chapter-2/2.6-autograd.md","ref":"chapter-2/2.6-autograd.md","articles":[]}]},"previous":{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"path":"chapter-1/1.6-JupyterNotebook-install.md","ref":"chapter-1/1.6-JupyterNotebook-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/1.7-PyTorch2.0.md","mtime":"2024-07-17T06:14:58.497Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-1/index.html b/docs/chapter-1/index.html index e8a27e5..505332d 100644 --- a/docs/chapter-1/index.html +++ b/docs/chapter-1/index.html @@ -1487,7 +1487,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第一章 PyTorch 简介与安装","level":"2.1","depth":1,"next":{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"path":"chapter-1/1.1-PyTorch-Introduction.md","ref":"chapter-1/1.1-PyTorch-Introduction.md","articles":[]},"previous":{"title":"前言","level":"1.2","depth":1,"path":"preface.md","ref":"preface.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/README.md","mtime":"2024-07-17T06:01:55.434Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第一章 PyTorch 简介与安装","level":"2.1","depth":1,"next":{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"path":"chapter-1/1.1-PyTorch-Introduction.md","ref":"chapter-1/1.1-PyTorch-Introduction.md","articles":[]},"previous":{"title":"前言","level":"1.2","depth":1,"path":"preface.md","ref":"preface.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-1/README.md","mtime":"2024-07-17T06:01:55.434Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-10/10.1-qwen.html b/docs/chapter-10/10.1-qwen.html index 07bf9a6..3abf8b4 100644 --- a/docs/chapter-10/10.1-qwen.html +++ b/docs/chapter-10/10.1-qwen.html @@ -1793,7 +1793,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"next":{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"path":"chapter-10/10.2-chatglm.md","ref":"chapter-10/10.2-chatglm.md","articles":[]},"previous":{"title":"第十章 大语言模型安装与应用","level":"3.3","depth":1,"path":"chapter-10/README.md","ref":"chapter-10/README.md","articles":[{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"path":"chapter-10/10.1-qwen.md","ref":"chapter-10/10.1-qwen.md","articles":[]},{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"path":"chapter-10/10.2-chatglm.md","ref":"chapter-10/10.2-chatglm.md","articles":[]},{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"path":"chapter-10/10.3-baichuan.md","ref":"chapter-10/10.3-baichuan.md","articles":[]},{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"path":"chapter-10/10.4-yi.md","ref":"chapter-10/10.4-yi.md","articles":[]},{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"path":"chapter-10/10.5-gpt-academic.md","ref":"chapter-10/10.5-gpt-academic.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.1-qwen.md","mtime":"2024-04-19T15:14:24.485Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"next":{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"path":"chapter-10/10.2-chatglm.md","ref":"chapter-10/10.2-chatglm.md","articles":[]},"previous":{"title":"第十章 大语言模型安装与应用","level":"3.3","depth":1,"path":"chapter-10/README.md","ref":"chapter-10/README.md","articles":[{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"path":"chapter-10/10.1-qwen.md","ref":"chapter-10/10.1-qwen.md","articles":[]},{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"path":"chapter-10/10.2-chatglm.md","ref":"chapter-10/10.2-chatglm.md","articles":[]},{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"path":"chapter-10/10.3-baichuan.md","ref":"chapter-10/10.3-baichuan.md","articles":[]},{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"path":"chapter-10/10.4-yi.md","ref":"chapter-10/10.4-yi.md","articles":[]},{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"path":"chapter-10/10.5-gpt-academic.md","ref":"chapter-10/10.5-gpt-academic.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.1-qwen.md","mtime":"2024-04-19T15:14:24.485Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-10/10.2-chatglm.html b/docs/chapter-10/10.2-chatglm.html index 7a97b29..4157bc2 100644 --- a/docs/chapter-10/10.2-chatglm.html +++ b/docs/chapter-10/10.2-chatglm.html @@ -1678,7 +1678,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"next":{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"path":"chapter-10/10.3-baichuan.md","ref":"chapter-10/10.3-baichuan.md","articles":[]},"previous":{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"path":"chapter-10/10.1-qwen.md","ref":"chapter-10/10.1-qwen.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.2-chatglm.md","mtime":"2024-05-23T05:51:45.074Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"next":{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"path":"chapter-10/10.3-baichuan.md","ref":"chapter-10/10.3-baichuan.md","articles":[]},"previous":{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"path":"chapter-10/10.1-qwen.md","ref":"chapter-10/10.1-qwen.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.2-chatglm.md","mtime":"2024-05-23T05:51:45.074Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-10/10.3-baichuan.html b/docs/chapter-10/10.3-baichuan.html index 906ad87..deb40a2 100644 --- a/docs/chapter-10/10.3-baichuan.html +++ b/docs/chapter-10/10.3-baichuan.html @@ -1636,7 +1636,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"next":{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"path":"chapter-10/10.4-yi.md","ref":"chapter-10/10.4-yi.md","articles":[]},"previous":{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"path":"chapter-10/10.2-chatglm.md","ref":"chapter-10/10.2-chatglm.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.3-baichuan.md","mtime":"2024-04-19T15:17:52.455Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"next":{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"path":"chapter-10/10.4-yi.md","ref":"chapter-10/10.4-yi.md","articles":[]},"previous":{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"path":"chapter-10/10.2-chatglm.md","ref":"chapter-10/10.2-chatglm.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.3-baichuan.md","mtime":"2024-04-19T15:17:52.455Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-10/10.4-yi.html b/docs/chapter-10/10.4-yi.html index 2658228..a2cd933 100644 --- a/docs/chapter-10/10.4-yi.html +++ b/docs/chapter-10/10.4-yi.html @@ -1619,7 +1619,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"next":{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"path":"chapter-10/10.5-gpt-academic.md","ref":"chapter-10/10.5-gpt-academic.md","articles":[]},"previous":{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"path":"chapter-10/10.3-baichuan.md","ref":"chapter-10/10.3-baichuan.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.4-yi.md","mtime":"2024-04-19T15:18:35.218Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"next":{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"path":"chapter-10/10.5-gpt-academic.md","ref":"chapter-10/10.5-gpt-academic.md","articles":[]},"previous":{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"path":"chapter-10/10.3-baichuan.md","ref":"chapter-10/10.3-baichuan.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.4-yi.md","mtime":"2024-04-19T15:18:35.218Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-10/10.5-gpt-academic.html b/docs/chapter-10/10.5-gpt-academic.html index e6b88bd..61ff368 100644 --- a/docs/chapter-10/10.5-gpt-academic.html +++ b/docs/chapter-10/10.5-gpt-academic.html @@ -1634,7 +1634,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"next":{"title":"第十一章 ONNX 使用","level":"4.1","depth":1,"path":"chapter-11/README.md","ref":"chapter-11/README.md","articles":[{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"path":"chapter-11/11.1-onnx-introduction.md","ref":"chapter-11/11.1-onnx-introduction.md","articles":[]},{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"path":"chapter-11/11.2-onnxruntime-intro.md","ref":"chapter-11/11.2-onnxruntime-intro.md","articles":[]},{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"path":"chapter-11/11.3-onnxruntime-advanced.md","ref":"chapter-11/11.3-onnxruntime-advanced.md","articles":[]}]},"previous":{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"path":"chapter-10/10.4-yi.md","ref":"chapter-10/10.4-yi.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.5-gpt-academic.md","mtime":"2024-04-19T15:19:31.234Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"next":{"title":"第十一章 ONNX 使用","level":"4.1","depth":1,"path":"chapter-11/README.md","ref":"chapter-11/README.md","articles":[{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"path":"chapter-11/11.1-onnx-introduction.md","ref":"chapter-11/11.1-onnx-introduction.md","articles":[]},{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"path":"chapter-11/11.2-onnxruntime-intro.md","ref":"chapter-11/11.2-onnxruntime-intro.md","articles":[]},{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"path":"chapter-11/11.3-onnxruntime-advanced.md","ref":"chapter-11/11.3-onnxruntime-advanced.md","articles":[]}]},"previous":{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"path":"chapter-10/10.4-yi.md","ref":"chapter-10/10.4-yi.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/10.5-gpt-academic.md","mtime":"2024-04-19T15:19:31.234Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-10/index.html b/docs/chapter-10/index.html index 791f2fe..4305b86 100644 --- a/docs/chapter-10/index.html +++ b/docs/chapter-10/index.html @@ -1486,7 +1486,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第十章 大语言模型安装与应用","level":"3.3","depth":1,"next":{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"path":"chapter-10/10.1-qwen.md","ref":"chapter-10/10.1-qwen.md","articles":[]},"previous":{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"path":"chapter-9/9.6-gpt.md","ref":"chapter-9/9.6-gpt.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/README.md","mtime":"2024-04-19T06:09:17.714Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第十章 大语言模型安装与应用","level":"3.3","depth":1,"next":{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"path":"chapter-10/10.1-qwen.md","ref":"chapter-10/10.1-qwen.md","articles":[]},"previous":{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"path":"chapter-9/9.6-gpt.md","ref":"chapter-9/9.6-gpt.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-10/README.md","mtime":"2024-04-19T06:09:17.714Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-11/11.1-onnx-introduction.html b/docs/chapter-11/11.1-onnx-introduction.html index 4f6b1b6..699c213 100644 --- a/docs/chapter-11/11.1-onnx-introduction.html +++ b/docs/chapter-11/11.1-onnx-introduction.html @@ -1596,7 +1596,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"next":{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"path":"chapter-11/11.2-onnxruntime-intro.md","ref":"chapter-11/11.2-onnxruntime-intro.md","articles":[]},"previous":{"title":"第十一章 ONNX 使用","level":"4.1","depth":1,"path":"chapter-11/README.md","ref":"chapter-11/README.md","articles":[{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"path":"chapter-11/11.1-onnx-introduction.md","ref":"chapter-11/11.1-onnx-introduction.md","articles":[]},{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"path":"chapter-11/11.2-onnxruntime-intro.md","ref":"chapter-11/11.2-onnxruntime-intro.md","articles":[]},{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"path":"chapter-11/11.3-onnxruntime-advanced.md","ref":"chapter-11/11.3-onnxruntime-advanced.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-11/11.1-onnx-introduction.md","mtime":"2023-06-11T04:56:15.981Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"next":{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"path":"chapter-11/11.2-onnxruntime-intro.md","ref":"chapter-11/11.2-onnxruntime-intro.md","articles":[]},"previous":{"title":"第十一章 ONNX 使用","level":"4.1","depth":1,"path":"chapter-11/README.md","ref":"chapter-11/README.md","articles":[{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"path":"chapter-11/11.1-onnx-introduction.md","ref":"chapter-11/11.1-onnx-introduction.md","articles":[]},{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"path":"chapter-11/11.2-onnxruntime-intro.md","ref":"chapter-11/11.2-onnxruntime-intro.md","articles":[]},{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"path":"chapter-11/11.3-onnxruntime-advanced.md","ref":"chapter-11/11.3-onnxruntime-advanced.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-11/11.1-onnx-introduction.md","mtime":"2023-06-11T04:56:15.981Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-11/11.2-onnxruntime-intro.html b/docs/chapter-11/11.2-onnxruntime-intro.html index 7dccc1c..652e393 100644 --- a/docs/chapter-11/11.2-onnxruntime-intro.html +++ b/docs/chapter-11/11.2-onnxruntime-intro.html @@ -1590,7 +1590,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"next":{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"path":"chapter-11/11.3-onnxruntime-advanced.md","ref":"chapter-11/11.3-onnxruntime-advanced.md","articles":[]},"previous":{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"path":"chapter-11/11.1-onnx-introduction.md","ref":"chapter-11/11.1-onnx-introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-11/11.2-onnxruntime-intro.md","mtime":"2023-06-11T04:58:43.677Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"next":{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"path":"chapter-11/11.3-onnxruntime-advanced.md","ref":"chapter-11/11.3-onnxruntime-advanced.md","articles":[]},"previous":{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"path":"chapter-11/11.1-onnx-introduction.md","ref":"chapter-11/11.1-onnx-introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-11/11.2-onnxruntime-intro.md","mtime":"2023-06-11T04:58:43.677Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-11/11.3-onnxruntime-advanced.html b/docs/chapter-11/11.3-onnxruntime-advanced.html index 5c41954..fb3ec73 100644 --- a/docs/chapter-11/11.3-onnxruntime-advanced.html +++ b/docs/chapter-11/11.3-onnxruntime-advanced.html @@ -1587,7 +1587,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"next":{"title":"第十二章 TensorRT 使用","level":"4.2","depth":1,"path":"chapter-12/README.md","ref":"chapter-12/README.md","articles":[{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"path":"chapter-12/12.1-trt-install.md","ref":"chapter-12/12.1-trt-install.md","articles":[]},{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"path":"chapter-12/12.2-trt-workflow.md","ref":"chapter-12/12.2-trt-workflow.md","articles":[]},{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"path":"chapter-12/12.3-trt-trtexec.md","ref":"chapter-12/12.3-trt-trtexec.md","articles":[]},{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"path":"chapter-12/12.4-trt-tools.md","ref":"chapter-12/12.4-trt-tools.md","articles":[]},{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"path":"chapter-12/12.5-trt-python-api.md","ref":"chapter-12/12.5-trt-python-api.md","articles":[]},{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"path":"chapter-12/12.6-quantization.md","ref":"chapter-12/12.6-quantization.md","articles":[]},{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"path":"chapter-12/12.7-ptq.md","ref":"chapter-12/12.7-ptq.md","articles":[]},{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"path":"chapter-12/12.8-qat.md","ref":"chapter-12/12.8-qat.md","articles":[]},{"title":"12.9 TensorRT Python 工程化","level":"4.2.9","depth":2,"path":"chapter-12/12.9-trt-deploy.md","ref":"chapter-12/12.9-trt-deploy.md","articles":[]}]},"previous":{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"path":"chapter-11/11.2-onnxruntime-intro.md","ref":"chapter-11/11.2-onnxruntime-intro.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-11/11.3-onnxruntime-advanced.md","mtime":"2023-06-11T05:01:48.275Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"next":{"title":"第十二章 TensorRT 使用","level":"4.2","depth":1,"path":"chapter-12/README.md","ref":"chapter-12/README.md","articles":[{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"path":"chapter-12/12.1-trt-install.md","ref":"chapter-12/12.1-trt-install.md","articles":[]},{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"path":"chapter-12/12.2-trt-workflow.md","ref":"chapter-12/12.2-trt-workflow.md","articles":[]},{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"path":"chapter-12/12.3-trt-trtexec.md","ref":"chapter-12/12.3-trt-trtexec.md","articles":[]},{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"path":"chapter-12/12.4-trt-tools.md","ref":"chapter-12/12.4-trt-tools.md","articles":[]},{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"path":"chapter-12/12.5-trt-python-api.md","ref":"chapter-12/12.5-trt-python-api.md","articles":[]},{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"path":"chapter-12/12.6-quantization.md","ref":"chapter-12/12.6-quantization.md","articles":[]},{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"path":"chapter-12/12.7-ptq.md","ref":"chapter-12/12.7-ptq.md","articles":[]},{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"path":"chapter-12/12.8-qat.md","ref":"chapter-12/12.8-qat.md","articles":[]},{"title":"12.9 TensorRT Python 工程化","level":"4.2.9","depth":2,"path":"chapter-12/12.9-trt-deploy.md","ref":"chapter-12/12.9-trt-deploy.md","articles":[]}]},"previous":{"title":"11.2 ONNX Runtime 简介与使用","level":"4.1.2","depth":2,"path":"chapter-11/11.2-onnxruntime-intro.md","ref":"chapter-11/11.2-onnxruntime-intro.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-11/11.3-onnxruntime-advanced.md","mtime":"2023-06-11T05:01:48.275Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-11/index.html b/docs/chapter-11/index.html index 7d15b7b..af7acaf 100644 --- a/docs/chapter-11/index.html +++ b/docs/chapter-11/index.html @@ -1477,7 +1477,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第十一章 ONNX 使用","level":"4.1","depth":1,"next":{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"path":"chapter-11/11.1-onnx-introduction.md","ref":"chapter-11/11.1-onnx-introduction.md","articles":[]},"previous":{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"path":"chapter-10/10.5-gpt-academic.md","ref":"chapter-10/10.5-gpt-academic.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-11/README.md","mtime":"2023-06-11T05:02:22.101Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第十一章 ONNX 使用","level":"4.1","depth":1,"next":{"title":"11.1 ONNX 简介与安装","level":"4.1.1","depth":2,"path":"chapter-11/11.1-onnx-introduction.md","ref":"chapter-11/11.1-onnx-introduction.md","articles":[]},"previous":{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"path":"chapter-10/10.5-gpt-academic.md","ref":"chapter-10/10.5-gpt-academic.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-11/README.md","mtime":"2023-06-11T05:02:22.101Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/12.1-trt-install.html b/docs/chapter-12/12.1-trt-install.html index d5b678f..853bce3 100644 --- a/docs/chapter-12/12.1-trt-install.html +++ b/docs/chapter-12/12.1-trt-install.html @@ -1742,7 +1742,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"next":{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"path":"chapter-12/12.2-trt-workflow.md","ref":"chapter-12/12.2-trt-workflow.md","articles":[]},"previous":{"title":"第十二章 TensorRT 使用","level":"4.2","depth":1,"path":"chapter-12/README.md","ref":"chapter-12/README.md","articles":[{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"path":"chapter-12/12.1-trt-install.md","ref":"chapter-12/12.1-trt-install.md","articles":[]},{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"path":"chapter-12/12.2-trt-workflow.md","ref":"chapter-12/12.2-trt-workflow.md","articles":[]},{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"path":"chapter-12/12.3-trt-trtexec.md","ref":"chapter-12/12.3-trt-trtexec.md","articles":[]},{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"path":"chapter-12/12.4-trt-tools.md","ref":"chapter-12/12.4-trt-tools.md","articles":[]},{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"path":"chapter-12/12.5-trt-python-api.md","ref":"chapter-12/12.5-trt-python-api.md","articles":[]},{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"path":"chapter-12/12.6-quantization.md","ref":"chapter-12/12.6-quantization.md","articles":[]},{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"path":"chapter-12/12.7-ptq.md","ref":"chapter-12/12.7-ptq.md","articles":[]},{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"path":"chapter-12/12.8-qat.md","ref":"chapter-12/12.8-qat.md","articles":[]},{"title":"12.9 TensorRT Python 工程化","level":"4.2.9","depth":2,"path":"chapter-12/12.9-trt-deploy.md","ref":"chapter-12/12.9-trt-deploy.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.1-trt-install.md","mtime":"2024-03-28T05:44:04.131Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"next":{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"path":"chapter-12/12.2-trt-workflow.md","ref":"chapter-12/12.2-trt-workflow.md","articles":[]},"previous":{"title":"第十二章 TensorRT 使用","level":"4.2","depth":1,"path":"chapter-12/README.md","ref":"chapter-12/README.md","articles":[{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"path":"chapter-12/12.1-trt-install.md","ref":"chapter-12/12.1-trt-install.md","articles":[]},{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"path":"chapter-12/12.2-trt-workflow.md","ref":"chapter-12/12.2-trt-workflow.md","articles":[]},{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"path":"chapter-12/12.3-trt-trtexec.md","ref":"chapter-12/12.3-trt-trtexec.md","articles":[]},{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"path":"chapter-12/12.4-trt-tools.md","ref":"chapter-12/12.4-trt-tools.md","articles":[]},{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"path":"chapter-12/12.5-trt-python-api.md","ref":"chapter-12/12.5-trt-python-api.md","articles":[]},{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"path":"chapter-12/12.6-quantization.md","ref":"chapter-12/12.6-quantization.md","articles":[]},{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"path":"chapter-12/12.7-ptq.md","ref":"chapter-12/12.7-ptq.md","articles":[]},{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"path":"chapter-12/12.8-qat.md","ref":"chapter-12/12.8-qat.md","articles":[]},{"title":"12.9 TensorRT Python 工程化","level":"4.2.9","depth":2,"path":"chapter-12/12.9-trt-deploy.md","ref":"chapter-12/12.9-trt-deploy.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.1-trt-install.md","mtime":"2024-03-28T05:44:04.131Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/12.2-trt-workflow.html b/docs/chapter-12/12.2-trt-workflow.html index 35407b5..e92770f 100644 --- a/docs/chapter-12/12.2-trt-workflow.html +++ b/docs/chapter-12/12.2-trt-workflow.html @@ -1633,7 +1633,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"next":{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"path":"chapter-12/12.3-trt-trtexec.md","ref":"chapter-12/12.3-trt-trtexec.md","articles":[]},"previous":{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"path":"chapter-12/12.1-trt-install.md","ref":"chapter-12/12.1-trt-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.2-trt-workflow.md","mtime":"2024-03-28T07:35:19.397Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"next":{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"path":"chapter-12/12.3-trt-trtexec.md","ref":"chapter-12/12.3-trt-trtexec.md","articles":[]},"previous":{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"path":"chapter-12/12.1-trt-install.md","ref":"chapter-12/12.1-trt-install.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.2-trt-workflow.md","mtime":"2024-03-28T07:35:19.397Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/12.3-trt-trtexec.html b/docs/chapter-12/12.3-trt-trtexec.html index dd7605d..9851419 100644 --- a/docs/chapter-12/12.3-trt-trtexec.html +++ b/docs/chapter-12/12.3-trt-trtexec.html @@ -1837,7 +1837,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"next":{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"path":"chapter-12/12.4-trt-tools.md","ref":"chapter-12/12.4-trt-tools.md","articles":[]},"previous":{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"path":"chapter-12/12.2-trt-workflow.md","ref":"chapter-12/12.2-trt-workflow.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.3-trt-trtexec.md","mtime":"2024-03-28T07:33:03.800Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"next":{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"path":"chapter-12/12.4-trt-tools.md","ref":"chapter-12/12.4-trt-tools.md","articles":[]},"previous":{"title":"12.2 TensorRT 工作流及cuda-python","level":"4.2.2","depth":2,"path":"chapter-12/12.2-trt-workflow.md","ref":"chapter-12/12.2-trt-workflow.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.3-trt-trtexec.md","mtime":"2024-03-28T07:33:03.800Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/12.4-trt-tools.html b/docs/chapter-12/12.4-trt-tools.html index 9915ca4..b3f021e 100644 --- a/docs/chapter-12/12.4-trt-tools.html +++ b/docs/chapter-12/12.4-trt-tools.html @@ -1624,7 +1624,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"next":{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"path":"chapter-12/12.5-trt-python-api.md","ref":"chapter-12/12.5-trt-python-api.md","articles":[]},"previous":{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"path":"chapter-12/12.3-trt-trtexec.md","ref":"chapter-12/12.3-trt-trtexec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.4-trt-tools.md","mtime":"2024-04-01T01:41:57.595Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"next":{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"path":"chapter-12/12.5-trt-python-api.md","ref":"chapter-12/12.5-trt-python-api.md","articles":[]},"previous":{"title":"12.3 trtexec 工具使用","level":"4.2.3","depth":2,"path":"chapter-12/12.3-trt-trtexec.md","ref":"chapter-12/12.3-trt-trtexec.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.4-trt-tools.md","mtime":"2024-04-01T01:41:57.595Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/12.5-trt-python-api.html b/docs/chapter-12/12.5-trt-python-api.html index e8791af..e768a36 100644 --- a/docs/chapter-12/12.5-trt-python-api.html +++ b/docs/chapter-12/12.5-trt-python-api.html @@ -1695,7 +1695,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"next":{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"path":"chapter-12/12.6-quantization.md","ref":"chapter-12/12.6-quantization.md","articles":[]},"previous":{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"path":"chapter-12/12.4-trt-tools.md","ref":"chapter-12/12.4-trt-tools.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.5-trt-python-api.md","mtime":"2024-04-01T03:13:35.722Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"next":{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"path":"chapter-12/12.6-quantization.md","ref":"chapter-12/12.6-quantization.md","articles":[]},"previous":{"title":"12.4 TensorRT 实用工具","level":"4.2.4","depth":2,"path":"chapter-12/12.4-trt-tools.md","ref":"chapter-12/12.4-trt-tools.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.5-trt-python-api.md","mtime":"2024-04-01T03:13:35.722Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/12.6-quantization.html b/docs/chapter-12/12.6-quantization.html index 71b76cd..eeed5cb 100644 --- a/docs/chapter-12/12.6-quantization.html +++ b/docs/chapter-12/12.6-quantization.html @@ -1590,7 +1590,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"next":{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"path":"chapter-12/12.7-ptq.md","ref":"chapter-12/12.7-ptq.md","articles":[]},"previous":{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"path":"chapter-12/12.5-trt-python-api.md","ref":"chapter-12/12.5-trt-python-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.6-quantization.md","mtime":"2024-04-03T15:19:26.722Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"next":{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"path":"chapter-12/12.7-ptq.md","ref":"chapter-12/12.7-ptq.md","articles":[]},"previous":{"title":"12.5 TensorRT API 使用","level":"4.2.5","depth":2,"path":"chapter-12/12.5-trt-python-api.md","ref":"chapter-12/12.5-trt-python-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.6-quantization.md","mtime":"2024-04-03T15:19:26.722Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/12.7-ptq.html b/docs/chapter-12/12.7-ptq.html index df8b15b..2555082 100644 --- a/docs/chapter-12/12.7-ptq.html +++ b/docs/chapter-12/12.7-ptq.html @@ -1774,7 +1774,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"next":{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"path":"chapter-12/12.8-qat.md","ref":"chapter-12/12.8-qat.md","articles":[]},"previous":{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"path":"chapter-12/12.6-quantization.md","ref":"chapter-12/12.6-quantization.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.7-ptq.md","mtime":"2024-04-04T10:13:54.127Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"next":{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"path":"chapter-12/12.8-qat.md","ref":"chapter-12/12.8-qat.md","articles":[]},"previous":{"title":"12.6 模型量化基础概念","level":"4.2.6","depth":2,"path":"chapter-12/12.6-quantization.md","ref":"chapter-12/12.6-quantization.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.7-ptq.md","mtime":"2024-04-04T10:13:54.127Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/12.8-qat.html b/docs/chapter-12/12.8-qat.html index e8b294c..aea6e37 100644 --- a/docs/chapter-12/12.8-qat.html +++ b/docs/chapter-12/12.8-qat.html @@ -1627,7 +1627,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"next":{"title":"12.9 TensorRT Python 工程化","level":"4.2.9","depth":2,"path":"chapter-12/12.9-trt-deploy.md","ref":"chapter-12/12.9-trt-deploy.md","articles":[]},"previous":{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"path":"chapter-12/12.7-ptq.md","ref":"chapter-12/12.7-ptq.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.8-qat.md","mtime":"2024-04-04T13:09:38.149Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"next":{"title":"12.9 TensorRT Python 工程化","level":"4.2.9","depth":2,"path":"chapter-12/12.9-trt-deploy.md","ref":"chapter-12/12.9-trt-deploy.md","articles":[]},"previous":{"title":"12.7 PTQ 量化实践","level":"4.2.7","depth":2,"path":"chapter-12/12.7-ptq.md","ref":"chapter-12/12.7-ptq.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.8-qat.md","mtime":"2024-04-04T13:09:38.149Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/12.9-trt-deploy.html b/docs/chapter-12/12.9-trt-deploy.html index 43cbe4c..22f89ed 100644 --- a/docs/chapter-12/12.9-trt-deploy.html +++ b/docs/chapter-12/12.9-trt-deploy.html @@ -1588,7 +1588,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"12.9 TensorRT Python 工程化","level":"4.2.9","depth":2,"previous":{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"path":"chapter-12/12.8-qat.md","ref":"chapter-12/12.8-qat.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.9-trt-deploy.md","mtime":"2024-04-04T14:02:48.721Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"12.9 TensorRT Python 工程化","level":"4.2.9","depth":2,"previous":{"title":"12.8 QAT 量化实践","level":"4.2.8","depth":2,"path":"chapter-12/12.8-qat.md","ref":"chapter-12/12.8-qat.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/12.9-trt-deploy.md","mtime":"2024-04-04T14:02:48.721Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-12/index.html b/docs/chapter-12/index.html index d8ea4ed..db3f7f8 100644 --- a/docs/chapter-12/index.html +++ b/docs/chapter-12/index.html @@ -1483,7 +1483,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第十二章 TensorRT 使用","level":"4.2","depth":1,"next":{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"path":"chapter-12/12.1-trt-install.md","ref":"chapter-12/12.1-trt-install.md","articles":[]},"previous":{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"path":"chapter-11/11.3-onnxruntime-advanced.md","ref":"chapter-11/11.3-onnxruntime-advanced.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/README.md","mtime":"2024-04-04T14:06:01.075Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第十二章 TensorRT 使用","level":"4.2","depth":1,"next":{"title":"12.1 TensorRT 简介与安装","level":"4.2.1","depth":2,"path":"chapter-12/12.1-trt-install.md","ref":"chapter-12/12.1-trt-install.md","articles":[]},"previous":{"title":"11.3 ONNX Runtime 进阶使用","level":"4.1.3","depth":2,"path":"chapter-11/11.3-onnxruntime-advanced.md","ref":"chapter-11/11.3-onnxruntime-advanced.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-12/README.md","mtime":"2024-04-04T14:06:01.075Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-2/2.1-module-tree.html b/docs/chapter-2/2.1-module-tree.html index bb9f59b..bb8ccce 100644 --- a/docs/chapter-2/2.1-module-tree.html +++ b/docs/chapter-2/2.1-module-tree.html @@ -1515,7 +1515,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"next":{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"path":"chapter-2/2.2-covid-19-cls.md","ref":"chapter-2/2.2-covid-19-cls.md","articles":[]},"previous":{"title":"第二章 PyTorch 核心模块","level":"2.2","depth":1,"path":"chapter-2/README.md","ref":"chapter-2/README.md","articles":[{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"path":"chapter-2/2.1-module-tree.md","ref":"chapter-2/2.1-module-tree.md","articles":[]},{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"path":"chapter-2/2.2-covid-19-cls.md","ref":"chapter-2/2.2-covid-19-cls.md","articles":[]},{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"path":"chapter-2/2.3-datastruct-tensor.md","ref":"chapter-2/2.3-datastruct-tensor.md","articles":[]},{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"path":"chapter-2/2.4-method-tensor.md","ref":"chapter-2/2.4-method-tensor.md","articles":[]},{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"path":"chapter-2/2.5-computational-graphs.md","ref":"chapter-2/2.5-computational-graphs.md","articles":[]},{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"path":"chapter-2/2.6-autograd.md","ref":"chapter-2/2.6-autograd.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.1-module-tree.md","mtime":"2024-05-10T15:01:13.823Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"next":{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"path":"chapter-2/2.2-covid-19-cls.md","ref":"chapter-2/2.2-covid-19-cls.md","articles":[]},"previous":{"title":"第二章 PyTorch 核心模块","level":"2.2","depth":1,"path":"chapter-2/README.md","ref":"chapter-2/README.md","articles":[{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"path":"chapter-2/2.1-module-tree.md","ref":"chapter-2/2.1-module-tree.md","articles":[]},{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"path":"chapter-2/2.2-covid-19-cls.md","ref":"chapter-2/2.2-covid-19-cls.md","articles":[]},{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"path":"chapter-2/2.3-datastruct-tensor.md","ref":"chapter-2/2.3-datastruct-tensor.md","articles":[]},{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"path":"chapter-2/2.4-method-tensor.md","ref":"chapter-2/2.4-method-tensor.md","articles":[]},{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"path":"chapter-2/2.5-computational-graphs.md","ref":"chapter-2/2.5-computational-graphs.md","articles":[]},{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"path":"chapter-2/2.6-autograd.md","ref":"chapter-2/2.6-autograd.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.1-module-tree.md","mtime":"2024-05-10T15:01:13.823Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-2/2.2-covid-19-cls.html b/docs/chapter-2/2.2-covid-19-cls.html index cc5dad5..788813f 100644 --- a/docs/chapter-2/2.2-covid-19-cls.html +++ b/docs/chapter-2/2.2-covid-19-cls.html @@ -1522,7 +1522,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"next":{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"path":"chapter-2/2.3-datastruct-tensor.md","ref":"chapter-2/2.3-datastruct-tensor.md","articles":[]},"previous":{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"path":"chapter-2/2.1-module-tree.md","ref":"chapter-2/2.1-module-tree.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.2-covid-19-cls.md","mtime":"2024-05-10T15:30:45.616Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"next":{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"path":"chapter-2/2.3-datastruct-tensor.md","ref":"chapter-2/2.3-datastruct-tensor.md","articles":[]},"previous":{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"path":"chapter-2/2.1-module-tree.md","ref":"chapter-2/2.1-module-tree.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.2-covid-19-cls.md","mtime":"2024-05-10T15:30:45.616Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-2/2.3-datastruct-tensor.html b/docs/chapter-2/2.3-datastruct-tensor.html index de7d99a..dfbde73 100644 --- a/docs/chapter-2/2.3-datastruct-tensor.html +++ b/docs/chapter-2/2.3-datastruct-tensor.html @@ -1514,7 +1514,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"next":{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"path":"chapter-2/2.4-method-tensor.md","ref":"chapter-2/2.4-method-tensor.md","articles":[]},"previous":{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"path":"chapter-2/2.2-covid-19-cls.md","ref":"chapter-2/2.2-covid-19-cls.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.3-datastruct-tensor.md","mtime":"2024-05-11T06:14:24.580Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"next":{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"path":"chapter-2/2.4-method-tensor.md","ref":"chapter-2/2.4-method-tensor.md","articles":[]},"previous":{"title":"2.2 新冠肺炎分类","level":"2.2.2","depth":2,"path":"chapter-2/2.2-covid-19-cls.md","ref":"chapter-2/2.2-covid-19-cls.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.3-datastruct-tensor.md","mtime":"2024-05-11T06:14:24.580Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-2/2.4-method-tensor.html b/docs/chapter-2/2.4-method-tensor.html index cdef9c4..71edba1 100644 --- a/docs/chapter-2/2.4-method-tensor.html +++ b/docs/chapter-2/2.4-method-tensor.html @@ -2007,7 +2007,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"next":{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"path":"chapter-2/2.5-computational-graphs.md","ref":"chapter-2/2.5-computational-graphs.md","articles":[]},"previous":{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"path":"chapter-2/2.3-datastruct-tensor.md","ref":"chapter-2/2.3-datastruct-tensor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.4-method-tensor.md","mtime":"2024-05-11T06:18:30.233Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"next":{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"path":"chapter-2/2.5-computational-graphs.md","ref":"chapter-2/2.5-computational-graphs.md","articles":[]},"previous":{"title":"2.3 核心数据结构——Tensor","level":"2.2.3","depth":2,"path":"chapter-2/2.3-datastruct-tensor.md","ref":"chapter-2/2.3-datastruct-tensor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.4-method-tensor.md","mtime":"2024-05-11T06:18:30.233Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-2/2.5-computational-graphs.html b/docs/chapter-2/2.5-computational-graphs.html index ccebc52..00ecaf0 100644 --- a/docs/chapter-2/2.5-computational-graphs.html +++ b/docs/chapter-2/2.5-computational-graphs.html @@ -1573,7 +1573,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"next":{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"path":"chapter-2/2.6-autograd.md","ref":"chapter-2/2.6-autograd.md","articles":[]},"previous":{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"path":"chapter-2/2.4-method-tensor.md","ref":"chapter-2/2.4-method-tensor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.5-computational-graphs.md","mtime":"2024-06-17T16:18:54.819Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"next":{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"path":"chapter-2/2.6-autograd.md","ref":"chapter-2/2.6-autograd.md","articles":[]},"previous":{"title":"2.4 张量的相关函数","level":"2.2.4","depth":2,"path":"chapter-2/2.4-method-tensor.md","ref":"chapter-2/2.4-method-tensor.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.5-computational-graphs.md","mtime":"2024-06-17T16:18:54.819Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-2/2.6-autograd.html b/docs/chapter-2/2.6-autograd.html index 3c42a6c..a742797 100644 --- a/docs/chapter-2/2.6-autograd.html +++ b/docs/chapter-2/2.6-autograd.html @@ -1918,7 +1918,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"next":{"title":"第三章 PyTorch 数据模块","level":"2.3","depth":1,"path":"chapter-3/README.md","ref":"chapter-3/README.md","articles":[{"title":"3.1 Dataset","level":"2.3.1","depth":2,"path":"chapter-3/3.1-dataset.md","ref":"chapter-3/3.1-dataset.md","articles":[]},{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"path":"chapter-3/3.2-dataloader.md","ref":"chapter-3/3.2-dataloader.md","articles":[]},{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"path":"chapter-3/3.3-dataset-useful-api.md","ref":"chapter-3/3.3-dataset-useful-api.md","articles":[]},{"title":"3.4 transforms","level":"2.3.4","depth":2,"path":"chapter-3/3.4-transforms.md","ref":"chapter-3/3.4-transforms.md","articles":[]},{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"path":"chapter-3/3.5-torchvision-dataset.md","ref":"chapter-3/3.5-torchvision-dataset.md","articles":[]}]},"previous":{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"path":"chapter-2/2.5-computational-graphs.md","ref":"chapter-2/2.5-computational-graphs.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.6-autograd.md","mtime":"2024-08-19T14:59:39.654Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"next":{"title":"第三章 PyTorch 数据模块","level":"2.3","depth":1,"path":"chapter-3/README.md","ref":"chapter-3/README.md","articles":[{"title":"3.1 Dataset","level":"2.3.1","depth":2,"path":"chapter-3/3.1-dataset.md","ref":"chapter-3/3.1-dataset.md","articles":[]},{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"path":"chapter-3/3.2-dataloader.md","ref":"chapter-3/3.2-dataloader.md","articles":[]},{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"path":"chapter-3/3.3-dataset-useful-api.md","ref":"chapter-3/3.3-dataset-useful-api.md","articles":[]},{"title":"3.4 transforms","level":"2.3.4","depth":2,"path":"chapter-3/3.4-transforms.md","ref":"chapter-3/3.4-transforms.md","articles":[]},{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"path":"chapter-3/3.5-torchvision-dataset.md","ref":"chapter-3/3.5-torchvision-dataset.md","articles":[]}]},"previous":{"title":"2.5 自动求导核心——计算图","level":"2.2.5","depth":2,"path":"chapter-2/2.5-computational-graphs.md","ref":"chapter-2/2.5-computational-graphs.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/2.6-autograd.md","mtime":"2024-08-19T14:59:39.654Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-2/index.html b/docs/chapter-2/index.html index a987590..60bd7a6 100644 --- a/docs/chapter-2/index.html +++ b/docs/chapter-2/index.html @@ -1490,7 +1490,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第二章 PyTorch 核心模块","level":"2.2","depth":1,"next":{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"path":"chapter-2/2.1-module-tree.md","ref":"chapter-2/2.1-module-tree.md","articles":[]},"previous":{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"path":"chapter-1/1.7-PyTorch2.0.md","ref":"chapter-1/1.7-PyTorch2.0.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/README.md","mtime":"2024-05-11T06:39:54.757Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第二章 PyTorch 核心模块","level":"2.2","depth":1,"next":{"title":"2.1 PyTorch 模块结构","level":"2.2.1","depth":2,"path":"chapter-2/2.1-module-tree.md","ref":"chapter-2/2.1-module-tree.md","articles":[]},"previous":{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"path":"chapter-1/1.7-PyTorch2.0.md","ref":"chapter-1/1.7-PyTorch2.0.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-2/README.md","mtime":"2024-05-11T06:39:54.757Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-3/3.1-dataset.html b/docs/chapter-3/3.1-dataset.html index 93a78f7..92629f0 100644 --- a/docs/chapter-3/3.1-dataset.html +++ b/docs/chapter-3/3.1-dataset.html @@ -1520,7 +1520,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"3.1 Dataset","level":"2.3.1","depth":2,"next":{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"path":"chapter-3/3.2-dataloader.md","ref":"chapter-3/3.2-dataloader.md","articles":[]},"previous":{"title":"第三章 PyTorch 数据模块","level":"2.3","depth":1,"path":"chapter-3/README.md","ref":"chapter-3/README.md","articles":[{"title":"3.1 Dataset","level":"2.3.1","depth":2,"path":"chapter-3/3.1-dataset.md","ref":"chapter-3/3.1-dataset.md","articles":[]},{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"path":"chapter-3/3.2-dataloader.md","ref":"chapter-3/3.2-dataloader.md","articles":[]},{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"path":"chapter-3/3.3-dataset-useful-api.md","ref":"chapter-3/3.3-dataset-useful-api.md","articles":[]},{"title":"3.4 transforms","level":"2.3.4","depth":2,"path":"chapter-3/3.4-transforms.md","ref":"chapter-3/3.4-transforms.md","articles":[]},{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"path":"chapter-3/3.5-torchvision-dataset.md","ref":"chapter-3/3.5-torchvision-dataset.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.1-dataset.md","mtime":"2024-05-11T06:48:09.572Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"3.1 Dataset","level":"2.3.1","depth":2,"next":{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"path":"chapter-3/3.2-dataloader.md","ref":"chapter-3/3.2-dataloader.md","articles":[]},"previous":{"title":"第三章 PyTorch 数据模块","level":"2.3","depth":1,"path":"chapter-3/README.md","ref":"chapter-3/README.md","articles":[{"title":"3.1 Dataset","level":"2.3.1","depth":2,"path":"chapter-3/3.1-dataset.md","ref":"chapter-3/3.1-dataset.md","articles":[]},{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"path":"chapter-3/3.2-dataloader.md","ref":"chapter-3/3.2-dataloader.md","articles":[]},{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"path":"chapter-3/3.3-dataset-useful-api.md","ref":"chapter-3/3.3-dataset-useful-api.md","articles":[]},{"title":"3.4 transforms","level":"2.3.4","depth":2,"path":"chapter-3/3.4-transforms.md","ref":"chapter-3/3.4-transforms.md","articles":[]},{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"path":"chapter-3/3.5-torchvision-dataset.md","ref":"chapter-3/3.5-torchvision-dataset.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.1-dataset.md","mtime":"2024-05-11T06:48:09.572Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-3/3.2-dataloader.html b/docs/chapter-3/3.2-dataloader.html index 487df01..ead570c 100644 --- a/docs/chapter-3/3.2-dataloader.html +++ b/docs/chapter-3/3.2-dataloader.html @@ -1548,7 +1548,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"next":{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"path":"chapter-3/3.3-dataset-useful-api.md","ref":"chapter-3/3.3-dataset-useful-api.md","articles":[]},"previous":{"title":"3.1 Dataset","level":"2.3.1","depth":2,"path":"chapter-3/3.1-dataset.md","ref":"chapter-3/3.1-dataset.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.2-dataloader.md","mtime":"2024-05-11T06:52:53.685Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"next":{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"path":"chapter-3/3.3-dataset-useful-api.md","ref":"chapter-3/3.3-dataset-useful-api.md","articles":[]},"previous":{"title":"3.1 Dataset","level":"2.3.1","depth":2,"path":"chapter-3/3.1-dataset.md","ref":"chapter-3/3.1-dataset.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.2-dataloader.md","mtime":"2024-05-11T06:52:53.685Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-3/3.3-dataset-useful-api.html b/docs/chapter-3/3.3-dataset-useful-api.html index 21693ae..7e0621e 100644 --- a/docs/chapter-3/3.3-dataset-useful-api.html +++ b/docs/chapter-3/3.3-dataset-useful-api.html @@ -1724,7 +1724,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"next":{"title":"3.4 transforms","level":"2.3.4","depth":2,"path":"chapter-3/3.4-transforms.md","ref":"chapter-3/3.4-transforms.md","articles":[]},"previous":{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"path":"chapter-3/3.2-dataloader.md","ref":"chapter-3/3.2-dataloader.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.3-dataset-useful-api.md","mtime":"2024-05-11T07:02:11.237Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"next":{"title":"3.4 transforms","level":"2.3.4","depth":2,"path":"chapter-3/3.4-transforms.md","ref":"chapter-3/3.4-transforms.md","articles":[]},"previous":{"title":"3.2 DataLoader","level":"2.3.2","depth":2,"path":"chapter-3/3.2-dataloader.md","ref":"chapter-3/3.2-dataloader.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.3-dataset-useful-api.md","mtime":"2024-05-11T07:02:11.237Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-3/3.4-transforms.html b/docs/chapter-3/3.4-transforms.html index f3b3ccd..06d1109 100644 --- a/docs/chapter-3/3.4-transforms.html +++ b/docs/chapter-3/3.4-transforms.html @@ -1680,7 +1680,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"3.4 transforms","level":"2.3.4","depth":2,"next":{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"path":"chapter-3/3.5-torchvision-dataset.md","ref":"chapter-3/3.5-torchvision-dataset.md","articles":[]},"previous":{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"path":"chapter-3/3.3-dataset-useful-api.md","ref":"chapter-3/3.3-dataset-useful-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.4-transforms.md","mtime":"2024-05-11T07:10:12.169Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"3.4 transforms","level":"2.3.4","depth":2,"next":{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"path":"chapter-3/3.5-torchvision-dataset.md","ref":"chapter-3/3.5-torchvision-dataset.md","articles":[]},"previous":{"title":"3.3 Dataset及常用API","level":"2.3.3","depth":2,"path":"chapter-3/3.3-dataset-useful-api.md","ref":"chapter-3/3.3-dataset-useful-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.4-transforms.md","mtime":"2024-05-11T07:10:12.169Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-3/3.5-torchvision-dataset.html b/docs/chapter-3/3.5-torchvision-dataset.html index 3280e0e..d549684 100644 --- a/docs/chapter-3/3.5-torchvision-dataset.html +++ b/docs/chapter-3/3.5-torchvision-dataset.html @@ -1565,7 +1565,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"next":{"title":"第四章 PyTorch 模型模块","level":"2.4","depth":1,"path":"chapter-4/README.md","ref":"chapter-4/README.md","articles":[{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"path":"chapter-4/4.1-module&Parameter.md","ref":"chapter-4/4.1-module&Parameter.md","articles":[]},{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"path":"chapter-4/4.2-containers.md","ref":"chapter-4/4.2-containers.md","articles":[]},{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"path":"chapter-4/4.3-common-module.md","ref":"chapter-4/4.3-common-module.md","articles":[]},{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"path":"chapter-4/4.4-module-api.md","ref":"chapter-4/4.4-module-api.md","articles":[]},{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"path":"chapter-4/4.5-hook-func.md","ref":"chapter-4/4.5-hook-func.md","articles":[]},{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"path":"chapter-4/4.6-classic-model.md","ref":"chapter-4/4.6-classic-model.md","articles":[]},{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"path":"chapter-4/4.7-weight-init.md","ref":"chapter-4/4.7-weight-init.md","articles":[]}]},"previous":{"title":"3.4 transforms","level":"2.3.4","depth":2,"path":"chapter-3/3.4-transforms.md","ref":"chapter-3/3.4-transforms.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.5-torchvision-dataset.md","mtime":"2024-05-11T07:24:38.510Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"next":{"title":"第四章 PyTorch 模型模块","level":"2.4","depth":1,"path":"chapter-4/README.md","ref":"chapter-4/README.md","articles":[{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"path":"chapter-4/4.1-module&Parameter.md","ref":"chapter-4/4.1-module&Parameter.md","articles":[]},{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"path":"chapter-4/4.2-containers.md","ref":"chapter-4/4.2-containers.md","articles":[]},{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"path":"chapter-4/4.3-common-module.md","ref":"chapter-4/4.3-common-module.md","articles":[]},{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"path":"chapter-4/4.4-module-api.md","ref":"chapter-4/4.4-module-api.md","articles":[]},{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"path":"chapter-4/4.5-hook-func.md","ref":"chapter-4/4.5-hook-func.md","articles":[]},{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"path":"chapter-4/4.6-classic-model.md","ref":"chapter-4/4.6-classic-model.md","articles":[]},{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"path":"chapter-4/4.7-weight-init.md","ref":"chapter-4/4.7-weight-init.md","articles":[]}]},"previous":{"title":"3.4 transforms","level":"2.3.4","depth":2,"path":"chapter-3/3.4-transforms.md","ref":"chapter-3/3.4-transforms.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/3.5-torchvision-dataset.md","mtime":"2024-05-11T07:24:38.510Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-3/index.html b/docs/chapter-3/index.html index b5fbcab..5a5af70 100644 --- a/docs/chapter-3/index.html +++ b/docs/chapter-3/index.html @@ -1498,7 +1498,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第三章 PyTorch 数据模块","level":"2.3","depth":1,"next":{"title":"3.1 Dataset","level":"2.3.1","depth":2,"path":"chapter-3/3.1-dataset.md","ref":"chapter-3/3.1-dataset.md","articles":[]},"previous":{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"path":"chapter-2/2.6-autograd.md","ref":"chapter-2/2.6-autograd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/README.md","mtime":"2022-06-27T05:57:47.729Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第三章 PyTorch 数据模块","level":"2.3","depth":1,"next":{"title":"3.1 Dataset","level":"2.3.1","depth":2,"path":"chapter-3/3.1-dataset.md","ref":"chapter-3/3.1-dataset.md","articles":[]},"previous":{"title":"2.6 Autograd——自动微分","level":"2.2.6","depth":2,"path":"chapter-2/2.6-autograd.md","ref":"chapter-2/2.6-autograd.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-3/README.md","mtime":"2022-06-27T05:57:47.729Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-4/4.1-module&Parameter.html b/docs/chapter-4/4.1-module&Parameter.html index ef27182..f2d92f0 100644 --- a/docs/chapter-4/4.1-module&Parameter.html +++ b/docs/chapter-4/4.1-module&Parameter.html @@ -1615,7 +1615,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"next":{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"path":"chapter-4/4.2-containers.md","ref":"chapter-4/4.2-containers.md","articles":[]},"previous":{"title":"第四章 PyTorch 模型模块","level":"2.4","depth":1,"path":"chapter-4/README.md","ref":"chapter-4/README.md","articles":[{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"path":"chapter-4/4.1-module&Parameter.md","ref":"chapter-4/4.1-module&Parameter.md","articles":[]},{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"path":"chapter-4/4.2-containers.md","ref":"chapter-4/4.2-containers.md","articles":[]},{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"path":"chapter-4/4.3-common-module.md","ref":"chapter-4/4.3-common-module.md","articles":[]},{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"path":"chapter-4/4.4-module-api.md","ref":"chapter-4/4.4-module-api.md","articles":[]},{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"path":"chapter-4/4.5-hook-func.md","ref":"chapter-4/4.5-hook-func.md","articles":[]},{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"path":"chapter-4/4.6-classic-model.md","ref":"chapter-4/4.6-classic-model.md","articles":[]},{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"path":"chapter-4/4.7-weight-init.md","ref":"chapter-4/4.7-weight-init.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.1-module&Parameter.md","mtime":"2024-05-11T09:33:53.956Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"next":{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"path":"chapter-4/4.2-containers.md","ref":"chapter-4/4.2-containers.md","articles":[]},"previous":{"title":"第四章 PyTorch 模型模块","level":"2.4","depth":1,"path":"chapter-4/README.md","ref":"chapter-4/README.md","articles":[{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"path":"chapter-4/4.1-module&Parameter.md","ref":"chapter-4/4.1-module&Parameter.md","articles":[]},{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"path":"chapter-4/4.2-containers.md","ref":"chapter-4/4.2-containers.md","articles":[]},{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"path":"chapter-4/4.3-common-module.md","ref":"chapter-4/4.3-common-module.md","articles":[]},{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"path":"chapter-4/4.4-module-api.md","ref":"chapter-4/4.4-module-api.md","articles":[]},{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"path":"chapter-4/4.5-hook-func.md","ref":"chapter-4/4.5-hook-func.md","articles":[]},{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"path":"chapter-4/4.6-classic-model.md","ref":"chapter-4/4.6-classic-model.md","articles":[]},{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"path":"chapter-4/4.7-weight-init.md","ref":"chapter-4/4.7-weight-init.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.1-module&Parameter.md","mtime":"2024-05-11T09:33:53.956Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-4/4.2-containers.html b/docs/chapter-4/4.2-containers.html index 145cb9c..a86916a 100644 --- a/docs/chapter-4/4.2-containers.html +++ b/docs/chapter-4/4.2-containers.html @@ -1595,7 +1595,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"next":{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"path":"chapter-4/4.3-common-module.md","ref":"chapter-4/4.3-common-module.md","articles":[]},"previous":{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"path":"chapter-4/4.1-module&Parameter.md","ref":"chapter-4/4.1-module&Parameter.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.2-containers.md","mtime":"2024-05-11T09:39:55.396Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"next":{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"path":"chapter-4/4.3-common-module.md","ref":"chapter-4/4.3-common-module.md","articles":[]},"previous":{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"path":"chapter-4/4.1-module&Parameter.md","ref":"chapter-4/4.1-module&Parameter.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.2-containers.md","mtime":"2024-05-11T09:39:55.396Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-4/4.3-common-module.html b/docs/chapter-4/4.3-common-module.html index af7a78d..e63eed8 100644 --- a/docs/chapter-4/4.3-common-module.html +++ b/docs/chapter-4/4.3-common-module.html @@ -1614,7 +1614,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"next":{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"path":"chapter-4/4.4-module-api.md","ref":"chapter-4/4.4-module-api.md","articles":[]},"previous":{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"path":"chapter-4/4.2-containers.md","ref":"chapter-4/4.2-containers.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.3-common-module.md","mtime":"2024-05-11T09:45:17.881Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"next":{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"path":"chapter-4/4.4-module-api.md","ref":"chapter-4/4.4-module-api.md","articles":[]},"previous":{"title":"4.2 Module的容器","level":"2.4.2","depth":2,"path":"chapter-4/4.2-containers.md","ref":"chapter-4/4.2-containers.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.3-common-module.md","mtime":"2024-05-11T09:45:17.881Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-4/4.4-module-api.html b/docs/chapter-4/4.4-module-api.html index ada2be4..d794546 100644 --- a/docs/chapter-4/4.4-module-api.html +++ b/docs/chapter-4/4.4-module-api.html @@ -1582,7 +1582,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"next":{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"path":"chapter-4/4.5-hook-func.md","ref":"chapter-4/4.5-hook-func.md","articles":[]},"previous":{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"path":"chapter-4/4.3-common-module.md","ref":"chapter-4/4.3-common-module.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.4-module-api.md","mtime":"2024-05-11T09:49:40.527Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"next":{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"path":"chapter-4/4.5-hook-func.md","ref":"chapter-4/4.5-hook-func.md","articles":[]},"previous":{"title":"4.3 常用网络层","level":"2.4.3","depth":2,"path":"chapter-4/4.3-common-module.md","ref":"chapter-4/4.3-common-module.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.4-module-api.md","mtime":"2024-05-11T09:49:40.527Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-4/4.5-hook-func.html b/docs/chapter-4/4.5-hook-func.html index eeec02b..cf7f2f0 100644 --- a/docs/chapter-4/4.5-hook-func.html +++ b/docs/chapter-4/4.5-hook-func.html @@ -1694,7 +1694,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"next":{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"path":"chapter-4/4.6-classic-model.md","ref":"chapter-4/4.6-classic-model.md","articles":[]},"previous":{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"path":"chapter-4/4.4-module-api.md","ref":"chapter-4/4.4-module-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.5-hook-func.md","mtime":"2024-05-11T10:01:21.722Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"next":{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"path":"chapter-4/4.6-classic-model.md","ref":"chapter-4/4.6-classic-model.md","articles":[]},"previous":{"title":"4.4 Module常用API函数","level":"2.4.4","depth":2,"path":"chapter-4/4.4-module-api.md","ref":"chapter-4/4.4-module-api.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.5-hook-func.md","mtime":"2024-05-11T10:01:21.722Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-4/4.6-classic-model.html b/docs/chapter-4/4.6-classic-model.html index bbacfc5..3a8994c 100644 --- a/docs/chapter-4/4.6-classic-model.html +++ b/docs/chapter-4/4.6-classic-model.html @@ -1687,7 +1687,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"next":{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"path":"chapter-4/4.7-weight-init.md","ref":"chapter-4/4.7-weight-init.md","articles":[]},"previous":{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"path":"chapter-4/4.5-hook-func.md","ref":"chapter-4/4.5-hook-func.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.6-classic-model.md","mtime":"2024-05-11T09:59:54.561Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"next":{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"path":"chapter-4/4.7-weight-init.md","ref":"chapter-4/4.7-weight-init.md","articles":[]},"previous":{"title":"4.5 Hook函数及Grad-CAM","level":"2.4.5","depth":2,"path":"chapter-4/4.5-hook-func.md","ref":"chapter-4/4.5-hook-func.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.6-classic-model.md","mtime":"2024-05-11T09:59:54.561Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-4/4.7-weight-init.html b/docs/chapter-4/4.7-weight-init.html index 304d86b..d9b1406 100644 --- a/docs/chapter-4/4.7-weight-init.html +++ b/docs/chapter-4/4.7-weight-init.html @@ -1626,7 +1626,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"next":{"title":"第五章 PyTorch 优化模块","level":"2.5","depth":1,"path":"chapter-5/README.md","ref":"chapter-5/README.md","articles":[{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"path":"chapter-5/5.1-loss-function.md","ref":"chapter-5/5.1-loss-function.md","articles":[]},{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"path":"chapter-5/5.2-Optimizer.md","ref":"chapter-5/5.2-Optimizer.md","articles":[]},{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"path":"chapter-5/5.3-lr-scheduler.md","ref":"chapter-5/5.3-lr-scheduler.md","articles":[]}]},"previous":{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"path":"chapter-4/4.6-classic-model.md","ref":"chapter-4/4.6-classic-model.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.7-weight-init.md","mtime":"2024-05-13T02:43:48.630Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"next":{"title":"第五章 PyTorch 优化模块","level":"2.5","depth":1,"path":"chapter-5/README.md","ref":"chapter-5/README.md","articles":[{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"path":"chapter-5/5.1-loss-function.md","ref":"chapter-5/5.1-loss-function.md","articles":[]},{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"path":"chapter-5/5.2-Optimizer.md","ref":"chapter-5/5.2-Optimizer.md","articles":[]},{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"path":"chapter-5/5.3-lr-scheduler.md","ref":"chapter-5/5.3-lr-scheduler.md","articles":[]}]},"previous":{"title":"4.6 经典模型代码分析","level":"2.4.6","depth":2,"path":"chapter-4/4.6-classic-model.md","ref":"chapter-4/4.6-classic-model.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/4.7-weight-init.md","mtime":"2024-05-13T02:43:48.630Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-4/index.html b/docs/chapter-4/index.html index dbf3023..ace0760 100644 --- a/docs/chapter-4/index.html +++ b/docs/chapter-4/index.html @@ -1494,7 +1494,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第四章 PyTorch 模型模块","level":"2.4","depth":1,"next":{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"path":"chapter-4/4.1-module&Parameter.md","ref":"chapter-4/4.1-module&Parameter.md","articles":[]},"previous":{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"path":"chapter-3/3.5-torchvision-dataset.md","ref":"chapter-3/3.5-torchvision-dataset.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/README.md","mtime":"2022-05-18T09:08:28.895Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第四章 PyTorch 模型模块","level":"2.4","depth":1,"next":{"title":"4.1 Module&Parameter","level":"2.4.1","depth":2,"path":"chapter-4/4.1-module&Parameter.md","ref":"chapter-4/4.1-module&Parameter.md","articles":[]},"previous":{"title":"3.5 torchvision 经典dataset学习","level":"2.3.5","depth":2,"path":"chapter-3/3.5-torchvision-dataset.md","ref":"chapter-3/3.5-torchvision-dataset.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-4/README.md","mtime":"2022-05-18T09:08:28.895Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-5/5.1-loss-function.html b/docs/chapter-5/5.1-loss-function.html index 7a140a9..a5085dd 100644 --- a/docs/chapter-5/5.1-loss-function.html +++ b/docs/chapter-5/5.1-loss-function.html @@ -1733,7 +1733,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"next":{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"path":"chapter-5/5.2-Optimizer.md","ref":"chapter-5/5.2-Optimizer.md","articles":[]},"previous":{"title":"第五章 PyTorch 优化模块","level":"2.5","depth":1,"path":"chapter-5/README.md","ref":"chapter-5/README.md","articles":[{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"path":"chapter-5/5.1-loss-function.md","ref":"chapter-5/5.1-loss-function.md","articles":[]},{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"path":"chapter-5/5.2-Optimizer.md","ref":"chapter-5/5.2-Optimizer.md","articles":[]},{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"path":"chapter-5/5.3-lr-scheduler.md","ref":"chapter-5/5.3-lr-scheduler.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-5/5.1-loss-function.md","mtime":"2024-05-13T02:47:12.279Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"next":{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"path":"chapter-5/5.2-Optimizer.md","ref":"chapter-5/5.2-Optimizer.md","articles":[]},"previous":{"title":"第五章 PyTorch 优化模块","level":"2.5","depth":1,"path":"chapter-5/README.md","ref":"chapter-5/README.md","articles":[{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"path":"chapter-5/5.1-loss-function.md","ref":"chapter-5/5.1-loss-function.md","articles":[]},{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"path":"chapter-5/5.2-Optimizer.md","ref":"chapter-5/5.2-Optimizer.md","articles":[]},{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"path":"chapter-5/5.3-lr-scheduler.md","ref":"chapter-5/5.3-lr-scheduler.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-5/5.1-loss-function.md","mtime":"2024-05-13T02:47:12.279Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-5/5.2-Optimizer.html b/docs/chapter-5/5.2-Optimizer.html index 3525453..064890b 100644 --- a/docs/chapter-5/5.2-Optimizer.html +++ b/docs/chapter-5/5.2-Optimizer.html @@ -1647,7 +1647,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"next":{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"path":"chapter-5/5.3-lr-scheduler.md","ref":"chapter-5/5.3-lr-scheduler.md","articles":[]},"previous":{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"path":"chapter-5/5.1-loss-function.md","ref":"chapter-5/5.1-loss-function.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-5/5.2-Optimizer.md","mtime":"2024-05-13T02:52:54.861Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"next":{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"path":"chapter-5/5.3-lr-scheduler.md","ref":"chapter-5/5.3-lr-scheduler.md","articles":[]},"previous":{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"path":"chapter-5/5.1-loss-function.md","ref":"chapter-5/5.1-loss-function.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-5/5.2-Optimizer.md","mtime":"2024-05-13T02:52:54.861Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-5/5.3-lr-scheduler.html b/docs/chapter-5/5.3-lr-scheduler.html index dd2e1ce..8606ce4 100644 --- a/docs/chapter-5/5.3-lr-scheduler.html +++ b/docs/chapter-5/5.3-lr-scheduler.html @@ -1600,7 +1600,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"next":{"title":"第六章 PyTorch 可视化模块","level":"2.6","depth":1,"path":"chapter-6/README.md","ref":"chapter-6/README.md","articles":[{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"path":"chapter-6/6.1-tensorboard.md","ref":"chapter-6/6.1-tensorboard.md","articles":[]},{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"path":"chapter-6/6.2-cnn-visualization.md","ref":"chapter-6/6.2-cnn-visualization.md","articles":[]},{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"path":"chapter-6/6.3-conf_matrix.md","ref":"chapter-6/6.3-conf_matrix.md","articles":[]},{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"path":"chapter-6/6.4-cam-vis.md","ref":"chapter-6/6.4-cam-vis.md","articles":[]},{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"path":"chapter-6/6.5-model-print.md","ref":"chapter-6/6.5-model-print.md","articles":[]}]},"previous":{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"path":"chapter-5/5.2-Optimizer.md","ref":"chapter-5/5.2-Optimizer.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-5/5.3-lr-scheduler.md","mtime":"2022-06-02T09:36:01.963Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"next":{"title":"第六章 PyTorch 可视化模块","level":"2.6","depth":1,"path":"chapter-6/README.md","ref":"chapter-6/README.md","articles":[{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"path":"chapter-6/6.1-tensorboard.md","ref":"chapter-6/6.1-tensorboard.md","articles":[]},{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"path":"chapter-6/6.2-cnn-visualization.md","ref":"chapter-6/6.2-cnn-visualization.md","articles":[]},{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"path":"chapter-6/6.3-conf_matrix.md","ref":"chapter-6/6.3-conf_matrix.md","articles":[]},{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"path":"chapter-6/6.4-cam-vis.md","ref":"chapter-6/6.4-cam-vis.md","articles":[]},{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"path":"chapter-6/6.5-model-print.md","ref":"chapter-6/6.5-model-print.md","articles":[]}]},"previous":{"title":"5.2 十三个优化器","level":"2.5.2","depth":2,"path":"chapter-5/5.2-Optimizer.md","ref":"chapter-5/5.2-Optimizer.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-5/5.3-lr-scheduler.md","mtime":"2022-06-02T09:36:01.963Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-5/index.html b/docs/chapter-5/index.html index 504959b..62a3cba 100644 --- a/docs/chapter-5/index.html +++ b/docs/chapter-5/index.html @@ -1482,7 +1482,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第五章 PyTorch 优化模块","level":"2.5","depth":1,"next":{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"path":"chapter-5/5.1-loss-function.md","ref":"chapter-5/5.1-loss-function.md","articles":[]},"previous":{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"path":"chapter-4/4.7-weight-init.md","ref":"chapter-4/4.7-weight-init.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-5/README.md","mtime":"2022-06-02T09:33:25.262Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第五章 PyTorch 优化模块","level":"2.5","depth":1,"next":{"title":"5.1 二十一个损失函数","level":"2.5.1","depth":2,"path":"chapter-5/5.1-loss-function.md","ref":"chapter-5/5.1-loss-function.md","articles":[]},"previous":{"title":"4.7 权重初始化方法","level":"2.4.7","depth":2,"path":"chapter-4/4.7-weight-init.md","ref":"chapter-4/4.7-weight-init.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-5/README.md","mtime":"2022-06-02T09:33:25.262Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-6/6.1-tensorboard.html b/docs/chapter-6/6.1-tensorboard.html index 588167f..cfbbab4 100644 --- a/docs/chapter-6/6.1-tensorboard.html +++ b/docs/chapter-6/6.1-tensorboard.html @@ -1624,7 +1624,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"next":{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"path":"chapter-6/6.2-cnn-visualization.md","ref":"chapter-6/6.2-cnn-visualization.md","articles":[]},"previous":{"title":"第六章 PyTorch 可视化模块","level":"2.6","depth":1,"path":"chapter-6/README.md","ref":"chapter-6/README.md","articles":[{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"path":"chapter-6/6.1-tensorboard.md","ref":"chapter-6/6.1-tensorboard.md","articles":[]},{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"path":"chapter-6/6.2-cnn-visualization.md","ref":"chapter-6/6.2-cnn-visualization.md","articles":[]},{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"path":"chapter-6/6.3-conf_matrix.md","ref":"chapter-6/6.3-conf_matrix.md","articles":[]},{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"path":"chapter-6/6.4-cam-vis.md","ref":"chapter-6/6.4-cam-vis.md","articles":[]},{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"path":"chapter-6/6.5-model-print.md","ref":"chapter-6/6.5-model-print.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.1-tensorboard.md","mtime":"2022-06-09T03:43:45.178Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"next":{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"path":"chapter-6/6.2-cnn-visualization.md","ref":"chapter-6/6.2-cnn-visualization.md","articles":[]},"previous":{"title":"第六章 PyTorch 可视化模块","level":"2.6","depth":1,"path":"chapter-6/README.md","ref":"chapter-6/README.md","articles":[{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"path":"chapter-6/6.1-tensorboard.md","ref":"chapter-6/6.1-tensorboard.md","articles":[]},{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"path":"chapter-6/6.2-cnn-visualization.md","ref":"chapter-6/6.2-cnn-visualization.md","articles":[]},{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"path":"chapter-6/6.3-conf_matrix.md","ref":"chapter-6/6.3-conf_matrix.md","articles":[]},{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"path":"chapter-6/6.4-cam-vis.md","ref":"chapter-6/6.4-cam-vis.md","articles":[]},{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"path":"chapter-6/6.5-model-print.md","ref":"chapter-6/6.5-model-print.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.1-tensorboard.md","mtime":"2022-06-09T03:43:45.178Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-6/6.2-cnn-visualization.html b/docs/chapter-6/6.2-cnn-visualization.html index 8c4a076..7adb162 100644 --- a/docs/chapter-6/6.2-cnn-visualization.html +++ b/docs/chapter-6/6.2-cnn-visualization.html @@ -1537,7 +1537,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"next":{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"path":"chapter-6/6.3-conf_matrix.md","ref":"chapter-6/6.3-conf_matrix.md","articles":[]},"previous":{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"path":"chapter-6/6.1-tensorboard.md","ref":"chapter-6/6.1-tensorboard.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.2-cnn-visualization.md","mtime":"2022-06-10T07:39:48.591Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"next":{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"path":"chapter-6/6.3-conf_matrix.md","ref":"chapter-6/6.3-conf_matrix.md","articles":[]},"previous":{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"path":"chapter-6/6.1-tensorboard.md","ref":"chapter-6/6.1-tensorboard.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.2-cnn-visualization.md","mtime":"2022-06-10T07:39:48.591Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-6/6.3-conf_matrix.html b/docs/chapter-6/6.3-conf_matrix.html index e65106c..8cf1669 100644 --- a/docs/chapter-6/6.3-conf_matrix.html +++ b/docs/chapter-6/6.3-conf_matrix.html @@ -1558,7 +1558,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"next":{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"path":"chapter-6/6.4-cam-vis.md","ref":"chapter-6/6.4-cam-vis.md","articles":[]},"previous":{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"path":"chapter-6/6.2-cnn-visualization.md","ref":"chapter-6/6.2-cnn-visualization.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.3-conf_matrix.md","mtime":"2022-06-15T07:53:17.736Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"next":{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"path":"chapter-6/6.4-cam-vis.md","ref":"chapter-6/6.4-cam-vis.md","articles":[]},"previous":{"title":"6.2 CNN卷积核与特征图可视化","level":"2.6.2","depth":2,"path":"chapter-6/6.2-cnn-visualization.md","ref":"chapter-6/6.2-cnn-visualization.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.3-conf_matrix.md","mtime":"2022-06-15T07:53:17.736Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-6/6.4-cam-vis.html b/docs/chapter-6/6.4-cam-vis.html index 8f7f245..1dfcac5 100644 --- a/docs/chapter-6/6.4-cam-vis.html +++ b/docs/chapter-6/6.4-cam-vis.html @@ -1610,7 +1610,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"next":{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"path":"chapter-6/6.5-model-print.md","ref":"chapter-6/6.5-model-print.md","articles":[]},"previous":{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"path":"chapter-6/6.3-conf_matrix.md","ref":"chapter-6/6.3-conf_matrix.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.4-cam-vis.md","mtime":"2022-06-16T03:42:37.477Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"next":{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"path":"chapter-6/6.5-model-print.md","ref":"chapter-6/6.5-model-print.md","articles":[]},"previous":{"title":"6.3 混淆矩阵与训练曲线可视化","level":"2.6.3","depth":2,"path":"chapter-6/6.3-conf_matrix.md","ref":"chapter-6/6.3-conf_matrix.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.4-cam-vis.md","mtime":"2022-06-16T03:42:37.477Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-6/6.5-model-print.html b/docs/chapter-6/6.5-model-print.html index 94a23c9..b05e202 100644 --- a/docs/chapter-6/6.5-model-print.html +++ b/docs/chapter-6/6.5-model-print.html @@ -1554,7 +1554,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"next":{"title":"第七章 PyTorch 小技巧汇总","level":"2.7","depth":1,"path":"chapter-7/README.md","ref":"chapter-7/README.md","articles":[{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"path":"chapter-7/7.1-serialization.md","ref":"chapter-7/7.1-serialization.md","articles":[]},{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"path":"chapter-7/7.2-finetune.md","ref":"chapter-7/7.2-finetune.md","articles":[]},{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"path":"chapter-7/7.3-gpu.md","ref":"chapter-7/7.3-gpu.md","articles":[]},{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"path":"chapter-7/7.4-training-script.md","ref":"chapter-7/7.4-training-script.md","articles":[]},{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"path":"chapter-7/7.5-torchmetrics.md","ref":"chapter-7/7.5-torchmetrics.md","articles":[]},{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"path":"chapter-7/7.6-albumentations.md","ref":"chapter-7/7.6-albumentations.md","articles":[]},{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"path":"chapter-7/7.7-torchensemble.md","ref":"chapter-7/7.7-torchensemble.md","articles":[]}]},"previous":{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"path":"chapter-6/6.4-cam-vis.md","ref":"chapter-6/6.4-cam-vis.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.5-model-print.md","mtime":"2022-06-23T09:24:10.180Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"next":{"title":"第七章 PyTorch 小技巧汇总","level":"2.7","depth":1,"path":"chapter-7/README.md","ref":"chapter-7/README.md","articles":[{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"path":"chapter-7/7.1-serialization.md","ref":"chapter-7/7.1-serialization.md","articles":[]},{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"path":"chapter-7/7.2-finetune.md","ref":"chapter-7/7.2-finetune.md","articles":[]},{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"path":"chapter-7/7.3-gpu.md","ref":"chapter-7/7.3-gpu.md","articles":[]},{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"path":"chapter-7/7.4-training-script.md","ref":"chapter-7/7.4-training-script.md","articles":[]},{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"path":"chapter-7/7.5-torchmetrics.md","ref":"chapter-7/7.5-torchmetrics.md","articles":[]},{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"path":"chapter-7/7.6-albumentations.md","ref":"chapter-7/7.6-albumentations.md","articles":[]},{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"path":"chapter-7/7.7-torchensemble.md","ref":"chapter-7/7.7-torchensemble.md","articles":[]}]},"previous":{"title":"6.4 CAM可视化与hook函数使用","level":"2.6.4","depth":2,"path":"chapter-6/6.4-cam-vis.md","ref":"chapter-6/6.4-cam-vis.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/6.5-model-print.md","mtime":"2022-06-23T09:24:10.180Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-6/index.html b/docs/chapter-6/index.html index 646c1a3..d20debe 100644 --- a/docs/chapter-6/index.html +++ b/docs/chapter-6/index.html @@ -1487,7 +1487,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第六章 PyTorch 可视化模块","level":"2.6","depth":1,"next":{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"path":"chapter-6/6.1-tensorboard.md","ref":"chapter-6/6.1-tensorboard.md","articles":[]},"previous":{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"path":"chapter-5/5.3-lr-scheduler.md","ref":"chapter-5/5.3-lr-scheduler.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/README.md","mtime":"2022-06-21T07:40:23.771Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第六章 PyTorch 可视化模块","level":"2.6","depth":1,"next":{"title":"6.1 TensorBoard安装与使用","level":"2.6.1","depth":2,"path":"chapter-6/6.1-tensorboard.md","ref":"chapter-6/6.1-tensorboard.md","articles":[]},"previous":{"title":"5.3 十四个学习率调整器","level":"2.5.3","depth":2,"path":"chapter-5/5.3-lr-scheduler.md","ref":"chapter-5/5.3-lr-scheduler.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-6/README.md","mtime":"2022-06-21T07:40:23.771Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-7/7.1-serialization.html b/docs/chapter-7/7.1-serialization.html index 6181bad..bbba052 100644 --- a/docs/chapter-7/7.1-serialization.html +++ b/docs/chapter-7/7.1-serialization.html @@ -1547,7 +1547,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"next":{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"path":"chapter-7/7.2-finetune.md","ref":"chapter-7/7.2-finetune.md","articles":[]},"previous":{"title":"第七章 PyTorch 小技巧汇总","level":"2.7","depth":1,"path":"chapter-7/README.md","ref":"chapter-7/README.md","articles":[{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"path":"chapter-7/7.1-serialization.md","ref":"chapter-7/7.1-serialization.md","articles":[]},{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"path":"chapter-7/7.2-finetune.md","ref":"chapter-7/7.2-finetune.md","articles":[]},{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"path":"chapter-7/7.3-gpu.md","ref":"chapter-7/7.3-gpu.md","articles":[]},{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"path":"chapter-7/7.4-training-script.md","ref":"chapter-7/7.4-training-script.md","articles":[]},{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"path":"chapter-7/7.5-torchmetrics.md","ref":"chapter-7/7.5-torchmetrics.md","articles":[]},{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"path":"chapter-7/7.6-albumentations.md","ref":"chapter-7/7.6-albumentations.md","articles":[]},{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"path":"chapter-7/7.7-torchensemble.md","ref":"chapter-7/7.7-torchensemble.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.1-serialization.md","mtime":"2022-06-24T08:39:20.364Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"next":{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"path":"chapter-7/7.2-finetune.md","ref":"chapter-7/7.2-finetune.md","articles":[]},"previous":{"title":"第七章 PyTorch 小技巧汇总","level":"2.7","depth":1,"path":"chapter-7/README.md","ref":"chapter-7/README.md","articles":[{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"path":"chapter-7/7.1-serialization.md","ref":"chapter-7/7.1-serialization.md","articles":[]},{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"path":"chapter-7/7.2-finetune.md","ref":"chapter-7/7.2-finetune.md","articles":[]},{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"path":"chapter-7/7.3-gpu.md","ref":"chapter-7/7.3-gpu.md","articles":[]},{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"path":"chapter-7/7.4-training-script.md","ref":"chapter-7/7.4-training-script.md","articles":[]},{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"path":"chapter-7/7.5-torchmetrics.md","ref":"chapter-7/7.5-torchmetrics.md","articles":[]},{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"path":"chapter-7/7.6-albumentations.md","ref":"chapter-7/7.6-albumentations.md","articles":[]},{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"path":"chapter-7/7.7-torchensemble.md","ref":"chapter-7/7.7-torchensemble.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.1-serialization.md","mtime":"2022-06-24T08:39:20.364Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-7/7.2-finetune.html b/docs/chapter-7/7.2-finetune.html index 9213a1b..8193f92 100644 --- a/docs/chapter-7/7.2-finetune.html +++ b/docs/chapter-7/7.2-finetune.html @@ -1515,7 +1515,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"next":{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"path":"chapter-7/7.3-gpu.md","ref":"chapter-7/7.3-gpu.md","articles":[]},"previous":{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"path":"chapter-7/7.1-serialization.md","ref":"chapter-7/7.1-serialization.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.2-finetune.md","mtime":"2024-06-19T02:20:33.970Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"next":{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"path":"chapter-7/7.3-gpu.md","ref":"chapter-7/7.3-gpu.md","articles":[]},"previous":{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"path":"chapter-7/7.1-serialization.md","ref":"chapter-7/7.1-serialization.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.2-finetune.md","mtime":"2024-06-19T02:20:33.970Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-7/7.3-gpu.html b/docs/chapter-7/7.3-gpu.html index c5d33ba..15cd13b 100644 --- a/docs/chapter-7/7.3-gpu.html +++ b/docs/chapter-7/7.3-gpu.html @@ -1758,7 +1758,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"next":{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"path":"chapter-7/7.4-training-script.md","ref":"chapter-7/7.4-training-script.md","articles":[]},"previous":{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"path":"chapter-7/7.2-finetune.md","ref":"chapter-7/7.2-finetune.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.3-gpu.md","mtime":"2022-06-25T03:33:17.494Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"next":{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"path":"chapter-7/7.4-training-script.md","ref":"chapter-7/7.4-training-script.md","articles":[]},"previous":{"title":"7.2 Finetune 模型微调","level":"2.7.2","depth":2,"path":"chapter-7/7.2-finetune.md","ref":"chapter-7/7.2-finetune.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.3-gpu.md","mtime":"2022-06-25T03:33:17.494Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-7/7.4-training-script.html b/docs/chapter-7/7.4-training-script.html index 923bcee..3fcdbc6 100644 --- a/docs/chapter-7/7.4-training-script.html +++ b/docs/chapter-7/7.4-training-script.html @@ -1501,7 +1501,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"next":{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"path":"chapter-7/7.5-torchmetrics.md","ref":"chapter-7/7.5-torchmetrics.md","articles":[]},"previous":{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"path":"chapter-7/7.3-gpu.md","ref":"chapter-7/7.3-gpu.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.4-training-script.md","mtime":"2022-06-26T03:30:26.608Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"next":{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"path":"chapter-7/7.5-torchmetrics.md","ref":"chapter-7/7.5-torchmetrics.md","articles":[]},"previous":{"title":"7.3 GPU使用","level":"2.7.3","depth":2,"path":"chapter-7/7.3-gpu.md","ref":"chapter-7/7.3-gpu.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.4-training-script.md","mtime":"2022-06-26T03:30:26.608Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-7/7.5-torchmetrics.html b/docs/chapter-7/7.5-torchmetrics.html index 283d3af..e82f31c 100644 --- a/docs/chapter-7/7.5-torchmetrics.html +++ b/docs/chapter-7/7.5-torchmetrics.html @@ -1691,7 +1691,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"next":{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"path":"chapter-7/7.6-albumentations.md","ref":"chapter-7/7.6-albumentations.md","articles":[]},"previous":{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"path":"chapter-7/7.4-training-script.md","ref":"chapter-7/7.4-training-script.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.5-torchmetrics.md","mtime":"2022-06-26T08:52:28.877Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"next":{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"path":"chapter-7/7.6-albumentations.md","ref":"chapter-7/7.6-albumentations.md","articles":[]},"previous":{"title":"7.4 模型训练代码模板","level":"2.7.4","depth":2,"path":"chapter-7/7.4-training-script.md","ref":"chapter-7/7.4-training-script.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.5-torchmetrics.md","mtime":"2022-06-26T08:52:28.877Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-7/7.6-albumentations.html b/docs/chapter-7/7.6-albumentations.html index 30abcc6..8d5ddf9 100644 --- a/docs/chapter-7/7.6-albumentations.html +++ b/docs/chapter-7/7.6-albumentations.html @@ -1851,7 +1851,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"next":{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"path":"chapter-7/7.7-torchensemble.md","ref":"chapter-7/7.7-torchensemble.md","articles":[]},"previous":{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"path":"chapter-7/7.5-torchmetrics.md","ref":"chapter-7/7.5-torchmetrics.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.6-albumentations.md","mtime":"2022-06-29T03:10:44.453Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"next":{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"path":"chapter-7/7.7-torchensemble.md","ref":"chapter-7/7.7-torchensemble.md","articles":[]},"previous":{"title":"7.5 TorchMetrics 模型评估指标库","level":"2.7.5","depth":2,"path":"chapter-7/7.5-torchmetrics.md","ref":"chapter-7/7.5-torchmetrics.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.6-albumentations.md","mtime":"2022-06-29T03:10:44.453Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-7/7.7-torchensemble.html b/docs/chapter-7/7.7-torchensemble.html index 0b1b39f..a5d2392 100644 --- a/docs/chapter-7/7.7-torchensemble.html +++ b/docs/chapter-7/7.7-torchensemble.html @@ -1682,7 +1682,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"next":{"title":"第八章 图像项目案例","level":"3.1","depth":1,"path":"chapter-8/README.md","ref":"chapter-8/README.md","articles":[{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"path":"chapter-8/8.1-classification.md","ref":"chapter-8/8.1-classification.md","articles":[]},{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"path":"chapter-8/8.2-segmentation.md","ref":"chapter-8/8.2-segmentation.md","articles":[]},{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"path":"chapter-8/8.3-detection.md","ref":"chapter-8/8.3-detection.md","articles":[]},{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"path":"chapter-8/8.4-tracking-1.md","ref":"chapter-8/8.4-tracking-1.md","articles":[]},{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"path":"chapter-8/8.4-tracking-2.md","ref":"chapter-8/8.4-tracking-2.md","articles":[]},{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"path":"chapter-8/8.5-cycleGAN.md","ref":"chapter-8/8.5-cycleGAN.md","articles":[]},{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"path":"chapter-8/8.6-diffusion-model.md","ref":"chapter-8/8.6-diffusion-model.md","articles":[]},{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"path":"chapter-8/8.7-image-captioning.md","ref":"chapter-8/8.7-image-captioning.md","articles":[]},{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"path":"chapter-8/8.8-image-retrieval-1.md","ref":"chapter-8/8.8-image-retrieval-1.md","articles":[]},{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"path":"chapter-8/8.8-image-retrieval-2.md","ref":"chapter-8/8.8-image-retrieval-2.md","articles":[]}]},"previous":{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"path":"chapter-7/7.6-albumentations.md","ref":"chapter-7/7.6-albumentations.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.7-torchensemble.md","mtime":"2022-07-23T14:02:38.844Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"next":{"title":"第八章 图像项目案例","level":"3.1","depth":1,"path":"chapter-8/README.md","ref":"chapter-8/README.md","articles":[{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"path":"chapter-8/8.1-classification.md","ref":"chapter-8/8.1-classification.md","articles":[]},{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"path":"chapter-8/8.2-segmentation.md","ref":"chapter-8/8.2-segmentation.md","articles":[]},{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"path":"chapter-8/8.3-detection.md","ref":"chapter-8/8.3-detection.md","articles":[]},{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"path":"chapter-8/8.4-tracking-1.md","ref":"chapter-8/8.4-tracking-1.md","articles":[]},{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"path":"chapter-8/8.4-tracking-2.md","ref":"chapter-8/8.4-tracking-2.md","articles":[]},{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"path":"chapter-8/8.5-cycleGAN.md","ref":"chapter-8/8.5-cycleGAN.md","articles":[]},{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"path":"chapter-8/8.6-diffusion-model.md","ref":"chapter-8/8.6-diffusion-model.md","articles":[]},{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"path":"chapter-8/8.7-image-captioning.md","ref":"chapter-8/8.7-image-captioning.md","articles":[]},{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"path":"chapter-8/8.8-image-retrieval-1.md","ref":"chapter-8/8.8-image-retrieval-1.md","articles":[]},{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"path":"chapter-8/8.8-image-retrieval-2.md","ref":"chapter-8/8.8-image-retrieval-2.md","articles":[]}]},"previous":{"title":"7.6 Albumentations 数据增强库","level":"2.7.6","depth":2,"path":"chapter-7/7.6-albumentations.md","ref":"chapter-7/7.6-albumentations.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/7.7-torchensemble.md","mtime":"2022-07-23T14:02:38.844Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-7/index.html b/docs/chapter-7/index.html index 13c9c4f..df3723e 100644 --- a/docs/chapter-7/index.html +++ b/docs/chapter-7/index.html @@ -1480,7 +1480,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第七章 PyTorch 小技巧汇总","level":"2.7","depth":1,"next":{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"path":"chapter-7/7.1-serialization.md","ref":"chapter-7/7.1-serialization.md","articles":[]},"previous":{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"path":"chapter-6/6.5-model-print.md","ref":"chapter-6/6.5-model-print.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/README.md","mtime":"2022-07-05T10:00:39.718Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第七章 PyTorch 小技巧汇总","level":"2.7","depth":1,"next":{"title":"7.1 模型保存与加载","level":"2.7.1","depth":2,"path":"chapter-7/7.1-serialization.md","ref":"chapter-7/7.1-serialization.md","articles":[]},"previous":{"title":"6.5 模型参数打印","level":"2.6.5","depth":2,"path":"chapter-6/6.5-model-print.md","ref":"chapter-6/6.5-model-print.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-7/README.md","mtime":"2022-07-05T10:00:39.718Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.1-classification.html b/docs/chapter-8/8.1-classification.html index f1d4101..71c12dc 100644 --- a/docs/chapter-8/8.1-classification.html +++ b/docs/chapter-8/8.1-classification.html @@ -1609,7 +1609,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"next":{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"path":"chapter-8/8.2-segmentation.md","ref":"chapter-8/8.2-segmentation.md","articles":[]},"previous":{"title":"第八章 图像项目案例","level":"3.1","depth":1,"path":"chapter-8/README.md","ref":"chapter-8/README.md","articles":[{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"path":"chapter-8/8.1-classification.md","ref":"chapter-8/8.1-classification.md","articles":[]},{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"path":"chapter-8/8.2-segmentation.md","ref":"chapter-8/8.2-segmentation.md","articles":[]},{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"path":"chapter-8/8.3-detection.md","ref":"chapter-8/8.3-detection.md","articles":[]},{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"path":"chapter-8/8.4-tracking-1.md","ref":"chapter-8/8.4-tracking-1.md","articles":[]},{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"path":"chapter-8/8.4-tracking-2.md","ref":"chapter-8/8.4-tracking-2.md","articles":[]},{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"path":"chapter-8/8.5-cycleGAN.md","ref":"chapter-8/8.5-cycleGAN.md","articles":[]},{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"path":"chapter-8/8.6-diffusion-model.md","ref":"chapter-8/8.6-diffusion-model.md","articles":[]},{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"path":"chapter-8/8.7-image-captioning.md","ref":"chapter-8/8.7-image-captioning.md","articles":[]},{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"path":"chapter-8/8.8-image-retrieval-1.md","ref":"chapter-8/8.8-image-retrieval-1.md","articles":[]},{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"path":"chapter-8/8.8-image-retrieval-2.md","ref":"chapter-8/8.8-image-retrieval-2.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.1-classification.md","mtime":"2023-05-31T14:06:01.007Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"next":{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"path":"chapter-8/8.2-segmentation.md","ref":"chapter-8/8.2-segmentation.md","articles":[]},"previous":{"title":"第八章 图像项目案例","level":"3.1","depth":1,"path":"chapter-8/README.md","ref":"chapter-8/README.md","articles":[{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"path":"chapter-8/8.1-classification.md","ref":"chapter-8/8.1-classification.md","articles":[]},{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"path":"chapter-8/8.2-segmentation.md","ref":"chapter-8/8.2-segmentation.md","articles":[]},{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"path":"chapter-8/8.3-detection.md","ref":"chapter-8/8.3-detection.md","articles":[]},{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"path":"chapter-8/8.4-tracking-1.md","ref":"chapter-8/8.4-tracking-1.md","articles":[]},{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"path":"chapter-8/8.4-tracking-2.md","ref":"chapter-8/8.4-tracking-2.md","articles":[]},{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"path":"chapter-8/8.5-cycleGAN.md","ref":"chapter-8/8.5-cycleGAN.md","articles":[]},{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"path":"chapter-8/8.6-diffusion-model.md","ref":"chapter-8/8.6-diffusion-model.md","articles":[]},{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"path":"chapter-8/8.7-image-captioning.md","ref":"chapter-8/8.7-image-captioning.md","articles":[]},{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"path":"chapter-8/8.8-image-retrieval-1.md","ref":"chapter-8/8.8-image-retrieval-1.md","articles":[]},{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"path":"chapter-8/8.8-image-retrieval-2.md","ref":"chapter-8/8.8-image-retrieval-2.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.1-classification.md","mtime":"2023-05-31T14:06:01.007Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.2-segmentation.html b/docs/chapter-8/8.2-segmentation.html index 30a137e..74986be 100644 --- a/docs/chapter-8/8.2-segmentation.html +++ b/docs/chapter-8/8.2-segmentation.html @@ -1778,7 +1778,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"next":{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"path":"chapter-8/8.3-detection.md","ref":"chapter-8/8.3-detection.md","articles":[]},"previous":{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"path":"chapter-8/8.1-classification.md","ref":"chapter-8/8.1-classification.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.2-segmentation.md","mtime":"2023-04-15T08:50:15.779Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"next":{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"path":"chapter-8/8.3-detection.md","ref":"chapter-8/8.3-detection.md","articles":[]},"previous":{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"path":"chapter-8/8.1-classification.md","ref":"chapter-8/8.1-classification.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.2-segmentation.md","mtime":"2023-04-15T08:50:15.779Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.3-detection.html b/docs/chapter-8/8.3-detection.html index 527ff45..0e39f46 100644 --- a/docs/chapter-8/8.3-detection.html +++ b/docs/chapter-8/8.3-detection.html @@ -1833,7 +1833,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"next":{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"path":"chapter-8/8.4-tracking-1.md","ref":"chapter-8/8.4-tracking-1.md","articles":[]},"previous":{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"path":"chapter-8/8.2-segmentation.md","ref":"chapter-8/8.2-segmentation.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.3-detection.md","mtime":"2023-04-15T08:51:11.783Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"next":{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"path":"chapter-8/8.4-tracking-1.md","ref":"chapter-8/8.4-tracking-1.md","articles":[]},"previous":{"title":"8.2 图像分割——脑MRI胶质瘤分割","level":"3.1.2","depth":2,"path":"chapter-8/8.2-segmentation.md","ref":"chapter-8/8.2-segmentation.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.3-detection.md","mtime":"2023-04-15T08:51:11.783Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.4-tracking-1.html b/docs/chapter-8/8.4-tracking-1.html index b808ad9..5648d4e 100644 --- a/docs/chapter-8/8.4-tracking-1.html +++ b/docs/chapter-8/8.4-tracking-1.html @@ -1683,7 +1683,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"next":{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"path":"chapter-8/8.4-tracking-2.md","ref":"chapter-8/8.4-tracking-2.md","articles":[]},"previous":{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"path":"chapter-8/8.3-detection.md","ref":"chapter-8/8.3-detection.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.4-tracking-1.md","mtime":"2023-04-08T09:46:01.309Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"next":{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"path":"chapter-8/8.4-tracking-2.md","ref":"chapter-8/8.4-tracking-2.md","articles":[]},"previous":{"title":"8.3 目标检测——无人机检测","level":"3.1.3","depth":2,"path":"chapter-8/8.3-detection.md","ref":"chapter-8/8.3-detection.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.4-tracking-1.md","mtime":"2023-04-08T09:46:01.309Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.4-tracking-2.html b/docs/chapter-8/8.4-tracking-2.html index 801b772..b1a4296 100644 --- a/docs/chapter-8/8.4-tracking-2.html +++ b/docs/chapter-8/8.4-tracking-2.html @@ -1555,7 +1555,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"next":{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"path":"chapter-8/8.5-cycleGAN.md","ref":"chapter-8/8.5-cycleGAN.md","articles":[]},"previous":{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"path":"chapter-8/8.4-tracking-1.md","ref":"chapter-8/8.4-tracking-1.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.4-tracking-2.md","mtime":"2023-04-15T08:51:55.667Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"next":{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"path":"chapter-8/8.5-cycleGAN.md","ref":"chapter-8/8.5-cycleGAN.md","articles":[]},"previous":{"title":"8.4 目标跟踪(上)——DeepSORT原理","level":"3.1.4","depth":2,"path":"chapter-8/8.4-tracking-1.md","ref":"chapter-8/8.4-tracking-1.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.4-tracking-2.md","mtime":"2023-04-15T08:51:55.667Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.5-cycleGAN.html b/docs/chapter-8/8.5-cycleGAN.html index 50652b0..8491b03 100644 --- a/docs/chapter-8/8.5-cycleGAN.html +++ b/docs/chapter-8/8.5-cycleGAN.html @@ -1660,7 +1660,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"next":{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"path":"chapter-8/8.6-diffusion-model.md","ref":"chapter-8/8.6-diffusion-model.md","articles":[]},"previous":{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"path":"chapter-8/8.4-tracking-2.md","ref":"chapter-8/8.4-tracking-2.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.5-cycleGAN.md","mtime":"2023-04-15T08:46:11.987Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"next":{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"path":"chapter-8/8.6-diffusion-model.md","ref":"chapter-8/8.6-diffusion-model.md","articles":[]},"previous":{"title":"8.4 目标跟踪(下)——虎门大桥车流量统计","level":"3.1.5","depth":2,"path":"chapter-8/8.4-tracking-2.md","ref":"chapter-8/8.4-tracking-2.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.5-cycleGAN.md","mtime":"2023-04-15T08:46:11.987Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.6-diffusion-model.html b/docs/chapter-8/8.6-diffusion-model.html index 7491d23..f4d7495 100644 --- a/docs/chapter-8/8.6-diffusion-model.html +++ b/docs/chapter-8/8.6-diffusion-model.html @@ -1710,7 +1710,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"next":{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"path":"chapter-8/8.7-image-captioning.md","ref":"chapter-8/8.7-image-captioning.md","articles":[]},"previous":{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"path":"chapter-8/8.5-cycleGAN.md","ref":"chapter-8/8.5-cycleGAN.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.6-diffusion-model.md","mtime":"2023-05-05T01:52:42.947Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"next":{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"path":"chapter-8/8.7-image-captioning.md","ref":"chapter-8/8.7-image-captioning.md","articles":[]},"previous":{"title":"8.5 生成对抗网络——CycleGAN","level":"3.1.6","depth":2,"path":"chapter-8/8.5-cycleGAN.md","ref":"chapter-8/8.5-cycleGAN.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.6-diffusion-model.md","mtime":"2023-05-05T01:52:42.947Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.7-image-captioning.html b/docs/chapter-8/8.7-image-captioning.html index 002223a..bab7210 100644 --- a/docs/chapter-8/8.7-image-captioning.html +++ b/docs/chapter-8/8.7-image-captioning.html @@ -1736,7 +1736,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"next":{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"path":"chapter-8/8.8-image-retrieval-1.md","ref":"chapter-8/8.8-image-retrieval-1.md","articles":[]},"previous":{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"path":"chapter-8/8.6-diffusion-model.md","ref":"chapter-8/8.6-diffusion-model.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.7-image-captioning.md","mtime":"2023-05-21T13:10:53.939Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"next":{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"path":"chapter-8/8.8-image-retrieval-1.md","ref":"chapter-8/8.8-image-retrieval-1.md","articles":[]},"previous":{"title":"8.6 扩散模型——DDPM","level":"3.1.7","depth":2,"path":"chapter-8/8.6-diffusion-model.md","ref":"chapter-8/8.6-diffusion-model.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.7-image-captioning.md","mtime":"2023-05-21T13:10:53.939Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.8-image-retrieval-1.html b/docs/chapter-8/8.8-image-retrieval-1.html index 6cd31f5..e2cf9aa 100644 --- a/docs/chapter-8/8.8-image-retrieval-1.html +++ b/docs/chapter-8/8.8-image-retrieval-1.html @@ -1791,7 +1791,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"next":{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"path":"chapter-8/8.8-image-retrieval-2.md","ref":"chapter-8/8.8-image-retrieval-2.md","articles":[]},"previous":{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"path":"chapter-8/8.7-image-captioning.md","ref":"chapter-8/8.7-image-captioning.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.8-image-retrieval-1.md","mtime":"2023-05-25T01:46:09.079Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"next":{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"path":"chapter-8/8.8-image-retrieval-2.md","ref":"chapter-8/8.8-image-retrieval-2.md","articles":[]},"previous":{"title":"8.7 图像描述——Image Captioning","level":"3.1.8","depth":2,"path":"chapter-8/8.7-image-captioning.md","ref":"chapter-8/8.7-image-captioning.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.8-image-retrieval-1.md","mtime":"2023-05-25T01:46:09.079Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/8.8-image-retrieval-2.html b/docs/chapter-8/8.8-image-retrieval-2.html index c12fc84..ef3fd05 100644 --- a/docs/chapter-8/8.8-image-retrieval-2.html +++ b/docs/chapter-8/8.8-image-retrieval-2.html @@ -1873,7 +1873,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"next":{"title":"第九章 自然语言处理项目案例","level":"3.2","depth":1,"path":"chapter-9/README.md","ref":"chapter-9/README.md","articles":[{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"path":"chapter-9/9.1-nlp_introduction.md","ref":"chapter-9/9.1-nlp_introduction.md","articles":[]},{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"path":"chapter-9/9.2-rnn-lstm.md","ref":"chapter-9/9.2-rnn-lstm.md","articles":[]},{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"path":"chapter-9/9.3-seq2seq.md","ref":"chapter-9/9.3-seq2seq.md","articles":[]},{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"path":"chapter-9/9.4-transformer.md","ref":"chapter-9/9.4-transformer.md","articles":[]},{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"path":"chapter-9/9.5-bert.md","ref":"chapter-9/9.5-bert.md","articles":[]},{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"path":"chapter-9/9.6-gpt.md","ref":"chapter-9/9.6-gpt.md","articles":[]}]},"previous":{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"path":"chapter-8/8.8-image-retrieval-1.md","ref":"chapter-8/8.8-image-retrieval-1.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.8-image-retrieval-2.md","mtime":"2023-05-25T08:15:42.799Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"next":{"title":"第九章 自然语言处理项目案例","level":"3.2","depth":1,"path":"chapter-9/README.md","ref":"chapter-9/README.md","articles":[{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"path":"chapter-9/9.1-nlp_introduction.md","ref":"chapter-9/9.1-nlp_introduction.md","articles":[]},{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"path":"chapter-9/9.2-rnn-lstm.md","ref":"chapter-9/9.2-rnn-lstm.md","articles":[]},{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"path":"chapter-9/9.3-seq2seq.md","ref":"chapter-9/9.3-seq2seq.md","articles":[]},{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"path":"chapter-9/9.4-transformer.md","ref":"chapter-9/9.4-transformer.md","articles":[]},{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"path":"chapter-9/9.5-bert.md","ref":"chapter-9/9.5-bert.md","articles":[]},{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"path":"chapter-9/9.6-gpt.md","ref":"chapter-9/9.6-gpt.md","articles":[]}]},"previous":{"title":"8.8 图像检索(上)——理论基础","level":"3.1.9","depth":2,"path":"chapter-8/8.8-image-retrieval-1.md","ref":"chapter-8/8.8-image-retrieval-1.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/8.8-image-retrieval-2.md","mtime":"2023-05-25T08:15:42.799Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-8/index.html b/docs/chapter-8/index.html index e8c5424..842c4f4 100644 --- a/docs/chapter-8/index.html +++ b/docs/chapter-8/index.html @@ -1482,7 +1482,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第八章 图像项目案例","level":"3.1","depth":1,"next":{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"path":"chapter-8/8.1-classification.md","ref":"chapter-8/8.1-classification.md","articles":[]},"previous":{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"path":"chapter-7/7.7-torchensemble.md","ref":"chapter-7/7.7-torchensemble.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/README.md","mtime":"2023-05-25T15:11:14.778Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第八章 图像项目案例","level":"3.1","depth":1,"next":{"title":"8.1 图像分类——胸部X光肺炎分类","level":"3.1.1","depth":2,"path":"chapter-8/8.1-classification.md","ref":"chapter-8/8.1-classification.md","articles":[]},"previous":{"title":"7.7 TorchEnsemble 模型集成库","level":"2.7.7","depth":2,"path":"chapter-7/7.7-torchensemble.md","ref":"chapter-7/7.7-torchensemble.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-8/README.md","mtime":"2023-05-25T15:11:14.778Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-9/9.1-nlp_introduction.html b/docs/chapter-9/9.1-nlp_introduction.html index 24acd27..44b0f3e 100644 --- a/docs/chapter-9/9.1-nlp_introduction.html +++ b/docs/chapter-9/9.1-nlp_introduction.html @@ -1527,7 +1527,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"next":{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"path":"chapter-9/9.2-rnn-lstm.md","ref":"chapter-9/9.2-rnn-lstm.md","articles":[]},"previous":{"title":"第九章 自然语言处理项目案例","level":"3.2","depth":1,"path":"chapter-9/README.md","ref":"chapter-9/README.md","articles":[{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"path":"chapter-9/9.1-nlp_introduction.md","ref":"chapter-9/9.1-nlp_introduction.md","articles":[]},{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"path":"chapter-9/9.2-rnn-lstm.md","ref":"chapter-9/9.2-rnn-lstm.md","articles":[]},{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"path":"chapter-9/9.3-seq2seq.md","ref":"chapter-9/9.3-seq2seq.md","articles":[]},{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"path":"chapter-9/9.4-transformer.md","ref":"chapter-9/9.4-transformer.md","articles":[]},{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"path":"chapter-9/9.5-bert.md","ref":"chapter-9/9.5-bert.md","articles":[]},{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"path":"chapter-9/9.6-gpt.md","ref":"chapter-9/9.6-gpt.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.1-nlp_introduction.md","mtime":"2024-02-07T07:56:20.140Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"next":{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"path":"chapter-9/9.2-rnn-lstm.md","ref":"chapter-9/9.2-rnn-lstm.md","articles":[]},"previous":{"title":"第九章 自然语言处理项目案例","level":"3.2","depth":1,"path":"chapter-9/README.md","ref":"chapter-9/README.md","articles":[{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"path":"chapter-9/9.1-nlp_introduction.md","ref":"chapter-9/9.1-nlp_introduction.md","articles":[]},{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"path":"chapter-9/9.2-rnn-lstm.md","ref":"chapter-9/9.2-rnn-lstm.md","articles":[]},{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"path":"chapter-9/9.3-seq2seq.md","ref":"chapter-9/9.3-seq2seq.md","articles":[]},{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"path":"chapter-9/9.4-transformer.md","ref":"chapter-9/9.4-transformer.md","articles":[]},{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"path":"chapter-9/9.5-bert.md","ref":"chapter-9/9.5-bert.md","articles":[]},{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"path":"chapter-9/9.6-gpt.md","ref":"chapter-9/9.6-gpt.md","articles":[]}]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.1-nlp_introduction.md","mtime":"2024-02-07T07:56:20.140Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-9/9.2-rnn-lstm.html b/docs/chapter-9/9.2-rnn-lstm.html index 2e0b1ec..21b9bb7 100644 --- a/docs/chapter-9/9.2-rnn-lstm.html +++ b/docs/chapter-9/9.2-rnn-lstm.html @@ -1698,7 +1698,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"next":{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"path":"chapter-9/9.3-seq2seq.md","ref":"chapter-9/9.3-seq2seq.md","articles":[]},"previous":{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"path":"chapter-9/9.1-nlp_introduction.md","ref":"chapter-9/9.1-nlp_introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.2-rnn-lstm.md","mtime":"2024-02-07T08:11:45.075Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"next":{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"path":"chapter-9/9.3-seq2seq.md","ref":"chapter-9/9.3-seq2seq.md","articles":[]},"previous":{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"path":"chapter-9/9.1-nlp_introduction.md","ref":"chapter-9/9.1-nlp_introduction.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.2-rnn-lstm.md","mtime":"2024-02-07T08:11:45.075Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-9/9.3-seq2seq.html b/docs/chapter-9/9.3-seq2seq.html index 5f49b67..d58126f 100644 --- a/docs/chapter-9/9.3-seq2seq.html +++ b/docs/chapter-9/9.3-seq2seq.html @@ -1670,7 +1670,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"next":{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"path":"chapter-9/9.4-transformer.md","ref":"chapter-9/9.4-transformer.md","articles":[]},"previous":{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"path":"chapter-9/9.2-rnn-lstm.md","ref":"chapter-9/9.2-rnn-lstm.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.3-seq2seq.md","mtime":"2024-02-21T07:37:49.628Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"next":{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"path":"chapter-9/9.4-transformer.md","ref":"chapter-9/9.4-transformer.md","articles":[]},"previous":{"title":"9.2 文本分类-RNN-LSTM","level":"3.2.2","depth":2,"path":"chapter-9/9.2-rnn-lstm.md","ref":"chapter-9/9.2-rnn-lstm.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.3-seq2seq.md","mtime":"2024-02-21T07:37:49.628Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-9/9.4-transformer.html b/docs/chapter-9/9.4-transformer.html index 210f833..9e8678e 100644 --- a/docs/chapter-9/9.4-transformer.html +++ b/docs/chapter-9/9.4-transformer.html @@ -1701,7 +1701,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"next":{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"path":"chapter-9/9.5-bert.md","ref":"chapter-9/9.5-bert.md","articles":[]},"previous":{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"path":"chapter-9/9.3-seq2seq.md","ref":"chapter-9/9.3-seq2seq.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.4-transformer.md","mtime":"2024-02-21T07:37:19.778Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"next":{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"path":"chapter-9/9.5-bert.md","ref":"chapter-9/9.5-bert.md","articles":[]},"previous":{"title":"9.3 机器翻译-seq2seq","level":"3.2.3","depth":2,"path":"chapter-9/9.3-seq2seq.md","ref":"chapter-9/9.3-seq2seq.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.4-transformer.md","mtime":"2024-02-21T07:37:19.778Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-9/9.5-bert.html b/docs/chapter-9/9.5-bert.html index 77c88ab..a32db07 100644 --- a/docs/chapter-9/9.5-bert.html +++ b/docs/chapter-9/9.5-bert.html @@ -1673,7 +1673,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"next":{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"path":"chapter-9/9.6-gpt.md","ref":"chapter-9/9.6-gpt.md","articles":[]},"previous":{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"path":"chapter-9/9.4-transformer.md","ref":"chapter-9/9.4-transformer.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.5-bert.md","mtime":"2024-02-26T08:35:58.947Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"next":{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"path":"chapter-9/9.6-gpt.md","ref":"chapter-9/9.6-gpt.md","articles":[]},"previous":{"title":"9.4 机器翻译-Transformer","level":"3.2.4","depth":2,"path":"chapter-9/9.4-transformer.md","ref":"chapter-9/9.4-transformer.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.5-bert.md","mtime":"2024-02-26T08:35:58.947Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-9/9.6-gpt.html b/docs/chapter-9/9.6-gpt.html index b5ed4fa..540fe9d 100644 --- a/docs/chapter-9/9.6-gpt.html +++ b/docs/chapter-9/9.6-gpt.html @@ -1736,7 +1736,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"next":{"title":"第十章 大语言模型安装与应用","level":"3.3","depth":1,"path":"chapter-10/README.md","ref":"chapter-10/README.md","articles":[{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"path":"chapter-10/10.1-qwen.md","ref":"chapter-10/10.1-qwen.md","articles":[]},{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"path":"chapter-10/10.2-chatglm.md","ref":"chapter-10/10.2-chatglm.md","articles":[]},{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"path":"chapter-10/10.3-baichuan.md","ref":"chapter-10/10.3-baichuan.md","articles":[]},{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"path":"chapter-10/10.4-yi.md","ref":"chapter-10/10.4-yi.md","articles":[]},{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"path":"chapter-10/10.5-gpt-academic.md","ref":"chapter-10/10.5-gpt-academic.md","articles":[]}]},"previous":{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"path":"chapter-9/9.5-bert.md","ref":"chapter-9/9.5-bert.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.6-gpt.md","mtime":"2024-03-26T07:32:43.163Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"9.6 文章续写-问答对话-GPT","level":"3.2.6","depth":2,"next":{"title":"第十章 大语言模型安装与应用","level":"3.3","depth":1,"path":"chapter-10/README.md","ref":"chapter-10/README.md","articles":[{"title":"10.1 Qwen部署与分析","level":"3.3.1","depth":2,"path":"chapter-10/10.1-qwen.md","ref":"chapter-10/10.1-qwen.md","articles":[]},{"title":"10.2 ChatGLM3 部署与分析","level":"3.3.2","depth":2,"path":"chapter-10/10.2-chatglm.md","ref":"chapter-10/10.2-chatglm.md","articles":[]},{"title":"10.3 Baichuan2 部署与分析","level":"3.3.3","depth":2,"path":"chapter-10/10.3-baichuan.md","ref":"chapter-10/10.3-baichuan.md","articles":[]},{"title":"10.4 Yi 部署与分析","level":"3.3.4","depth":2,"path":"chapter-10/10.4-yi.md","ref":"chapter-10/10.4-yi.md","articles":[]},{"title":"10.5 GPT Academic 安装与使用","level":"3.3.5","depth":2,"path":"chapter-10/10.5-gpt-academic.md","ref":"chapter-10/10.5-gpt-academic.md","articles":[]}]},"previous":{"title":"9.5 命名实体识别-BERT","level":"3.2.5","depth":2,"path":"chapter-9/9.5-bert.md","ref":"chapter-9/9.5-bert.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/9.6-gpt.md","mtime":"2024-03-26T07:32:43.163Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/chapter-9/index.html b/docs/chapter-9/index.html index 69d766c..683cf64 100644 --- a/docs/chapter-9/index.html +++ b/docs/chapter-9/index.html @@ -1479,7 +1479,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"第九章 自然语言处理项目案例","level":"3.2","depth":1,"next":{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"path":"chapter-9/9.1-nlp_introduction.md","ref":"chapter-9/9.1-nlp_introduction.md","articles":[]},"previous":{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"path":"chapter-8/8.8-image-retrieval-2.md","ref":"chapter-8/8.8-image-retrieval-2.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/README.md","mtime":"2024-03-26T07:31:47.986Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":"..","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"第九章 自然语言处理项目案例","level":"3.2","depth":1,"next":{"title":"9.1 自然语言处理简介","level":"3.2.1","depth":2,"path":"chapter-9/9.1-nlp_introduction.md","ref":"chapter-9/9.1-nlp_introduction.md","articles":[]},"previous":{"title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","level":"3.1.10","depth":2,"path":"chapter-8/8.8-image-retrieval-2.md","ref":"chapter-8/8.8-image-retrieval-2.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"chapter-9/README.md","mtime":"2024-03-26T07:31:47.986Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":"..","book":{"language":""}}); }); diff --git a/docs/index.html b/docs/index.html index 073a20f..d0329dc 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1525,7 +1525,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"简介","level":"1.1","depth":1,"next":{"title":"前言","level":"1.2","depth":1,"path":"preface.md","ref":"preface.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"README.md","mtime":"2024-04-25T15:10:23.534Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":".","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"简介","level":"1.1","depth":1,"next":{"title":"前言","level":"1.2","depth":1,"path":"preface.md","ref":"preface.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"README.md","mtime":"2024-04-25T15:10:23.534Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":".","book":{"language":""}}); }); diff --git a/docs/preface.html b/docs/preface.html index fe214a0..923d472 100644 --- a/docs/preface.html +++ b/docs/preface.html @@ -1494,7 +1494,7 @@

No results matching " var gitbook = gitbook || []; gitbook.push(function() { - gitbook.page.hasChanged({"page":{"title":"前言","level":"1.2","depth":1,"next":{"title":"第一章 PyTorch 简介与安装","level":"2.1","depth":1,"path":"chapter-1/README.md","ref":"chapter-1/README.md","articles":[{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"path":"chapter-1/1.1-PyTorch-Introduction.md","ref":"chapter-1/1.1-PyTorch-Introduction.md","articles":[]},{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"path":"chapter-1/1.2-Anaconda.md","ref":"chapter-1/1.2-Anaconda.md","articles":[]},{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"path":"chapter-1/1.3-Pycharm.md","ref":"chapter-1/1.3-Pycharm.md","articles":[]},{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"path":"chapter-1/1.4-CUDA&cuDNN.md","ref":"chapter-1/1.4-CUDA&cuDNN.md","articles":[]},{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"path":"chapter-1/1.5-PyTorch-install.md","ref":"chapter-1/1.5-PyTorch-install.md","articles":[]},{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"path":"chapter-1/1.6-JupyterNotebook-install.md","ref":"chapter-1/1.6-JupyterNotebook-install.md","articles":[]},{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"path":"chapter-1/1.7-PyTorch2.0.md","ref":"chapter-1/1.7-PyTorch2.0.md","articles":[]}]},"previous":{"title":"简介","level":"1.1","depth":1,"path":"README.md","ref":"README.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"preface.md","mtime":"2024-04-24T15:50:08.131Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-08-19T15:00:20.896Z"},"basePath":".","book":{"language":""}}); + gitbook.page.hasChanged({"page":{"title":"前言","level":"1.2","depth":1,"next":{"title":"第一章 PyTorch 简介与安装","level":"2.1","depth":1,"path":"chapter-1/README.md","ref":"chapter-1/README.md","articles":[{"title":"1.1 PyTorch 初认识","level":"2.1.1","depth":2,"path":"chapter-1/1.1-PyTorch-Introduction.md","ref":"chapter-1/1.1-PyTorch-Introduction.md","articles":[]},{"title":"1.2 环境配置之Anaconda","level":"2.1.2","depth":2,"path":"chapter-1/1.2-Anaconda.md","ref":"chapter-1/1.2-Anaconda.md","articles":[]},{"title":"1.3 环境配置之-IDE——Pycharm & VS Code","level":"2.1.3","depth":2,"path":"chapter-1/1.3-Pycharm.md","ref":"chapter-1/1.3-Pycharm.md","articles":[]},{"title":"1.4 环境配置之CUDA&cuDNN","level":"2.1.4","depth":2,"path":"chapter-1/1.4-CUDA&cuDNN.md","ref":"chapter-1/1.4-CUDA&cuDNN.md","articles":[]},{"title":"1.5 环境配置之PyTorch系列包","level":"2.1.5","depth":2,"path":"chapter-1/1.5-PyTorch-install.md","ref":"chapter-1/1.5-PyTorch-install.md","articles":[]},{"title":"1.6 环境配置之Jupyter Notebook","level":"2.1.6","depth":2,"path":"chapter-1/1.6-JupyterNotebook-install.md","ref":"chapter-1/1.6-JupyterNotebook-install.md","articles":[]},{"title":"1.7 PyTorch 2.0 与 torch.compile","level":"2.1.7","depth":2,"path":"chapter-1/1.7-PyTorch2.0.md","ref":"chapter-1/1.7-PyTorch2.0.md","articles":[]}]},"previous":{"title":"简介","level":"1.1","depth":1,"path":"README.md","ref":"README.md","articles":[]},"dir":"ltr"},"config":{"plugins":["copy-code-button","back-to-top-button","expandable-chapters-small","chapter-fold","-lunr","-search","search-pro","github-buttons@2.1.0","github","splitter","sharing-plus","tbfed-pagefooter","intopic-toc","page-toc-button","klipse","pageview-count","donate","popup","3-ba","disqus","emphasize","katex","livereload"],"root":".","styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"pluginsConfig":{"tbfed-pagefooter":{"copyright":"Copyright © TingsongYu 2021","modify_label":"文件修订时间:","modify_format":"2024年04月26日21:48:10"},"chapter-fold":{},"disqus":{"useIdentifier":false,"shortName":"gitbookuse"},"emphasize":{},"github":{"url":"https://github.com/TingsongYu/PyTorch-Tutorial-2nd"},"intopic-toc":{"isCollapsed":true,"isScrollspyActive":true,"label":"In this article","maxDepth":6,"mode":"nested","selector":".markdown-section h1, .markdown-section h2, .markdown-section h3, .markdown-section h4, .markdown-section h5, .markdown-section h6","visible":true},"livereload":{},"splitter":{},"search-pro":{},"sharing-plus":{"qq":false,"all":["facebook","google","twitter","instapaper","linkedin","pocket","stumbleupon"],"douban":false,"facebook":true,"weibo":false,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":true,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"popup":{},"donate":{"alipay":"https://img.picgo.net/2024/05/18/alipayd96d6d0a325ef7e0.jpg","alipayText":" 支付宝 ↑ ","button":"赏","title":"原创不易,赏!","wechat":"https://img.picgo.net/2024/05/18/wechat2859fc4f155e9302.jpg","wechatText":" 微信 ↑ "},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"highlight":{},"page-toc-button":{"maxTocDepth":2,"minTocSize":2},"back-to-top-button":{},"pageview-count":{},"github-buttons":{"repo":"TingsongYu/PyTorch-Tutorial-2nd","types":["star","watch","fork"],"size":"small"},"3-ba":{"configuration":"auto","token":"d00d5236ccf6a1cabd370aa6366ff513"},"expandable-chapters-small":{},"copy-code-button":{},"klipse":{"myConfigKey":"it's the default value"},"sharing":{"qq":true,"all":["douban","facebook","google","hatenaBookmark","instapaper","linkedin","twitter","messenger","qq","qzone","viber","vk","weibo","pocket","stumbleupon","whatsapp"],"douban":false,"facebook":false,"weibo":true,"instapaper":false,"whatsapp":false,"hatenaBookmark":false,"twitter":true,"messenger":false,"line":false,"vk":false,"pocket":false,"google":false,"viber":false,"stumbleupon":false,"qzone":false,"linkedin":false},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"theme":"default","author":"余霆嵩","pdf":{"pageNumbers":true,"fontSize":20,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":36,"bottom":36}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"variables":{},"title":"PyTorch实用教程(第二版)","language":"zh-hans","output.name":"site","gitbook":"3.2.3","description":"PyTorch高质量学习资料"},"file":{"path":"preface.md","mtime":"2024-04-24T15:50:08.131Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2024-11-08T02:40:16.805Z"},"basePath":".","book":{"language":""}}); }); diff --git a/docs/search_plus_index.json b/docs/search_plus_index.json index 72b07ee..975220f 100644 --- a/docs/search_plus_index.json +++ b/docs/search_plus_index.json @@ -1 +1 @@ -{"./":{"url":"./","title":"简介","keywords":"","body":"简介 2018年12月《PyTorch 模型训练实用教程》面世距今5年多仍在发光发热,且备受欢迎已超7.2K stars。为了满足读者进一步需求和紧跟技术发展潮流,第二版应运而生。 时隔5年,历时4年,耗时2年的《Pytorch实用教程》第二版完成了。在第一版的精华之上,增加了丰富详实的深度学习应用案例和推理部署框架,使本书更系统性的涵盖深度学习工程师所涉及的知识面。如人工智能技术发展一浪接一浪,《Pytorch实用教程》第二版不是结束,而是开始,开启新的技术、新的领域、新的篇章,希望未来能继续与大家一起在人工智能技术里学习、进步。 本书以基础概念为基石,计算机视觉、自然语言处理和大语言模型为核心,推理部署框架为桥梁,皆在为读者提供面向项目落地的代码工程与理论讲解。本书整体分三部分,上篇:入门,中篇:应用,下篇:落地。 上篇 PyTorch基础。针对刚入门、非科班、本科生,提供PyTorch介绍,讲解开发环境的搭建,介绍PyTorch的数据、模型、优化、可视化等核心模块,最后利用所讲解的PyTorch知识点构建一套自己的代码结构,为后续的应用打下基础。 中篇 产业应用。经过上篇,磨了一把好刀,接下来就用它在各领域上大显身手。将会讲解三个主题,分别是计算机视觉(Computer Vision)、自然语言处理(Natural Language Processing)和大语言模型(Large Language Model)。 在CV章节,包括主流的任务,有图像分类、图像分割、目标检测、目标跟踪、GAN生成、Diffusion生成、图像描述和图像检索八大任务。 在NLP章节,包括RNN、LSTM、Transformer、BERT和GPT模型详解与应用,应用的任务有文本分类、机器翻译、命名体识别、QA问答和文章生成五大任务。 在LLM章节,包括4个LLM部署与代码分析和一个LLM行业应用——GPT Academic(GPT 学术优化),LLM包括国内开源的四大主流模型,Qwen、ChatGLM、Baichuan和Yi。 下篇 工业落地。有了工具,有了场景,接下来就要让它产生价值,变成可用的、好用的算法服务。因此,从pytorch这样一个训练框架、重框架中剥离出来进行部署、加速、量化是常见的方法。本章将介绍ONNX和TensorRT的原理与使用,同时借助TensorRT详细分析模型量化概念、PTQ和QAT量化实战与原理。 相信经过上、中、下篇的学习,可以帮助入门的同学少走很多弯路,快速掌握PyTorch,具备独当一面的能力,能依据实际场景选择算法模型,可以将模型部署应用,形成闭环,全流程打通。 本书亮点 结构清晰:全书分为三部分:上篇(入门)、中篇(应用)、下篇(落地),逐步引导读者深入学习。 理论与实践结合:不仅提供理论讲解,还通过丰富的项目案例,让读者能够将理论应用于实践。 实战案例丰富:提供了计算机视觉、自然语言处理和大语言模型等多个领域的实战案例。 系统性覆盖:涵盖Pytorch基础、计算机视觉基础任务、自然语言处理基础任务、大语言模型基础、推理部署框架。 适用性广:适合AI自学者、AI产品经理、在校学生以及跨领域人士阅读,满足不同背景和需求的读者。 本书内容及结构 本书包含十二章,分三篇。分别是pytorch基础、项目应用和工业落地。 第一章 PyTorch 简介与安装,详细介绍Pytorch环境安装,包括Anaconda、Pycharm、CUDA&cuDNN和Jupyter Notebook。 第二章 PyTorch 核心模块,介绍代码结构,Tensor与自动求导机制。 第三章 PyTorch 数据模块,介绍Dataset、DataLoader、transforms库和应用案例。 第四章 PyTorch 模型模块,介绍Module、Parameter、Module容器、Hook等。 第五章 PyTorch 优化模块,介绍二十一个损失函数,十三个优化器,十四个学习率调整方法。 第六章 PyTorch 可视化模块,介绍Tensorboard、混淆矩阵、CAM、Grad-CAM、Grad-CAM++等。 第七章 PyTorch 小技巧汇总,介绍模型保存、加载、GPT使用、TorchMetrics、Albumentation、TorchEnsemble等。 第八章 PyTorch 图像项目案例之图像分类,介绍肺炎X光片二分类,AutoAug、推理性能分析等。 第八章 PyTorch 图像项目案例之图像分割,介绍脑部MRI肿瘤分割,涉及smp工具库、分割评价指标分析等。 第八章 PyTorch 图像项目案例之目标检测,近1万字介绍YOLOv5在无人机场景的目标检测,包含yolov5框架代码剖析、训练机制剖析、消融实验等。 第八章 PyTorch 图像项目案例之目标跟踪,近1万字介绍DeepSORT算法流程、匈牙利算法、卡尔曼滤波、源代码深入剖析、代码UML设计、撞线机制等。 第八章 PyTorch 图像项目案例之CycleGAN,介绍GAN、cycleGAN的理论与风格迁移代码实现。 第八章 PyTorch 图像项目案例之DDPM,介绍DDPM、Guided Diffusion Model、classifier-base 、classifier-free、去噪代码流程等。 第八章 PyTorch 图像项目案例之图像描述,介绍CNN+RNN架构、Clip+GPT架构、clip原理、gpt原理、CLIPCap 代码等。 第八章 PyTorch 图像项目案例之图像检索,近1万字介绍图像检索概念、评价指标、向量检索、Faiss库安装使用、CLIP+Faiss+Flask工程部署等。 第九章 PyTorch NLP项目案例之文本分类,介绍NLP基础、RNN、LSTM、分词、词频、词表、词向量、影评数据分类项目等。 第九章 PyTorch NLP项目案例之机器翻译,介绍seq2seq、transformers、特殊token、强制学习、三种注意力机制、中英互译项目等。 第九章 PyTorch NLP项目案例之命名体识别,介绍BERT原理与结构、命名体识别的表示、cluener数据集及其BERT训练项目等。 第九章 PyTorch NLP项目案例之文章续写,介绍gpt1, gpt2, gpt3, Instruct GPT四篇论文及对比,介绍GPT2训练代码框架,数据准备等 第九章 PyTorch NLP项目案例之QA问答,介绍gpt1, gpt2, gpt3, Instruct GPT四篇论文及对比,介绍GPT2训练代码框架,数据准备等 第十章 大语言模型安装与应用,介绍Qwen、ChatGLM3、Baichuan2、Yi的安装与应用,介绍GPT Academic(GPT 学术优化工具)的安装与项目剖析等。 第十一章 ONNX使用,介绍ONNX、onnxruntime、ONNX量化、混合精度、计算图优化、线程管理、IO binding、耗时分析等。 第十二章 TensorRT使用,介绍TensorRT安装、工作流原理、cuda-python库、trtexec使用、nsight system工具、polygraphy工具、TRT API、模型量化理论、PTQ量化理论与实践、QAT量化与实践,python工程化等。 阅读建议 为了更好使用本书,读者需要具备python基础,了解人工智能、深度学习概念。 本着Talk is cheap, show me the code 的精神,本书配套了丰富详细的代码,建议先跑通代码,再看理论基础。 深度学习是一门工程性质极强的学科,建议基于本项目提供的代码,在自己的任务、其他数据集上进行实践。 项目代码 https://github.com/TingsongYu/PyTorch-Tutorial-2nd 有任何想法和建议,欢迎联系:yts3221@126.com Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"preface.html":{"url":"preface.html","title":"前言","keywords":"","body":"前言 本书背后的故事 《PyTorch实用教程》(第二版),动笔时间为2021年12月16日,什么时候能完结,并不知道(2024年4月19日完成了!)。为什么会不是写完再发布?因为担心再不迈出第一步,这本书就会难产。其实,想写这本书已经很久了,至少2年多了吧(见下图),总由于各种原因,迟迟没有开始动笔,索性,采用github在线更新的方式,和各位朋友提前见面吧,你们的反馈是我最大的动力。 为何历时4年? 早在2019年12月,就萌生编写第二版的想法,当时咨询过朋友,阅读过同类书籍,编写了大纲,但2020年的疫情、换工作、成家等事情给写书破了第一盆冷水。写书的想法就像一颗种子,一直埋在心里,2021年12月终于下定决心,通过在线书籍的方式,一砖一瓦的编著,而非一次性完工再发布。现如今回看当时的方式是正确的,很多事情不是一蹴而就,而是需要漫长的时光来打磨,因此踏出第一步显得尤为重要。正如一位博主所说,“一个人要怎样才能不断坚持干一件事?那就是基于“一点点哲学”,“一点点哲学”说的是,先一点点的干,不管是30分还是20分,千万不要有一次干到位的想法,千万不要想一次就干到90分,100分。慢即是快。”回顾本书编写历程,的确是这样,耗时2年多的时间里,中断了3次,包括工作任务重、家长里短和孩子出生。 但好在,写书这团火没有灭,经过多次重启,终于在2024年4月完成最后一节——GPT 学术优化工具使用,并在2024年4月19日晚导出PDF版,字数共计204842字,导出PDF时间虽然只有几秒钟,但是我感觉特别漫长且忐忑,像极了当时产房外等待孩子。 为什么写这本书? 这本书是对PyTorch模型训练实用教程的改进优化,是对第一版的内容进行丰富,增加更多的PyTorch基础,增加丰富的应用案例,同时包含模型部署上线这一关键的知识点。萌生第二版的想法大约在2019年11月,当时距离第一版发布一年,期间又对PyTorch有了一个深入的学习了解过程,于是想把这本书继续优化下去,帮助更多的朋友快速掌握PyTorch。可奈于当时精力有限,就迟迟没有动笔,2020年与2021年是忙碌的两年,生活的琐碎以及工作占据了大多数时间,2021年12月16日22:13:10终于有合适的条件动笔了,一刻不敢拖延,害怕这刚刚燃起的火苗又被琐碎的生活浇灭。 除了是对第一版的改进,更大的原因是一种责任。由于这几年的经历,让我感到需要出一本高质量的PyTorch书,它一定是大家爱不释手的资料,可以为大家的深度学习道路提供一点便捷,能给大家带来些许改变,这就足够了。第一版发布过去已经五年,仍旧能看到她在发光发热,这令我十分欣慰,但又非常愧疚。 并且,书籍是人类智慧的结晶,不同于普通的博客、公众号甚至课件,书籍的内容会更加完整和充实,这是受到一个视频的启发,视频说的是北大退休的韩茂莉教授将在B站上延续她的课堂生涯,将她毕生所学的历史地理知识传授给更多的朋友,在第一个视频里她提到:“但凡具有一种人类形成的,知识性的精华,其实都在书中 。” 看到这句话感触颇深。 第一版仍旧在发光发热 本书是一本面向深度学习领域的综合指南,可为不同背景、需求的读者提供从基础到实战的全面指导。适合阅读本书的读者类型有: AI自学者:本书包含入门知识、使用技巧和完整项目代码,可独立完成多个AI项目。 AI产品经理:本书包含CV、NLP、LLM方向的十多个应用案例、代码和原理分析,可提供全面的应用场景。 在校学生:本书包含理论分析、代码详解、结构设计,可与机器学习、面向对象设计、深度学习等课程形成体系。 跨领域人士:本书包含环境安装、基础介绍、项目应用,可提供轻松入门学习路径。 如何阅读这本书 为了更好使用本书,读者需要具备python基础,了解人工智能、深度学习概念。 本着Talk is cheap, show me the code 的精神,本书配套了丰富详细的代码,建议先跑通代码,再看理论基础。 深度学习是一门工程性质极强的学科,建议基于本项目提供的代码,在自己的任务、其他数据集上进行实践。 由于自身水平有限,书中不可避免的会出现错误,恳请大家指正。 欢迎联系:yts3221@126.com 项目代码 https://github.com/TingsongYu/PyTorch-Tutorial-2nd 致谢 本书的面世,首先感谢家人们的支持、理解和帮助,其中包括妻子的精神鼓励和行动上支持,包括四位父母的家庭生活上支持。其次,感谢所有热衷开源、分享、传播技术的伟大开发者们,本书更像是搬运工,穿梭于论文、技术报告、开源项目之中,筛选、消化、整合,再以深入浅出的方式呈现出来,因此本书也以开源的方式提供给广大的网友学习。最后,感谢一路上遇到的所有支持本项目的伙伴,是你们的热情鼓舞着我在这一次次中断后重启。再次感谢你们! Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/":{"url":"chapter-1/","title":"第一章 PyTorch 简介与安装","keywords":"","body":"第一章 PyTorch 简介与安装 第一章 PyTorch 简介与安装 1.1 PyTorch 初认识 1.2 环境配置之Anaconda 1.3 环境配置之-IDE——Pycharm & VS Code 1.4 环境配置之CUDA&cuDNN 1.5 环境配置之PyTorch系列包 1.6 环境配置之Jupyter Notebook 1.7 PyTorch 2.0 与 torch.compile 本章,将带领读者踏入PyTorch的环境配置之路,从Python虚拟环境的搭建到IDE的安装,CUDA与cuDNN的配置,再到PyTorch的精细安装,直至Jupyter Notebook的实践,每一步均图文并茂。 篇章概览: PyTorch初认识:开篇,将揭示PyTorch的起源,介绍它如何加速从科研原型至部署的路径,以及背后的历史与哲学。 Anaconda环境配置:深入Python虚拟环境的搭建,Anaconda的语法,如何借助它轻松管理Python环境与包,创建隔离的环境,为PyTorch铺路。 IDE选择:PyCharm & VS Code:对比两大IDE,PyCharm的社区版与专业版,VS Code。 CUDA与cuDNN:介绍CUDA与cuDNN的安装,如何让GPU驱动PyTorch加速,选对的CUDA版本匹配,是关键的一节。 PyTorch系列包安装:从torch、torchvision、torchaudio至torchtext,将详细介绍安装流程与版本匹配。 Jupyter Notebook实践:介绍Jupyter Notebook的概念及安装,并且配置Kernel。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.1-PyTorch-Introduction.html":{"url":"chapter-1/1.1-PyTorch-Introduction.html","title":"1.1 PyTorch 初认识","keywords":"","body":"1.1 PyTorch 初认识 一句话认识PyTorch “An open source machine learning framework that accelerates the path from research prototyping to production deployment.” ——选自 https://pytorch.org/ PyTorch 是一个开源机器学习框架,帮助用户加速从算法模型研究到产品部署的过程。 PyTorch历史 FAIR( Facebook AI Research,Facebook人工智能研究院 )于2017年初发布PyTorch,PyTorch 的命名由 Py(代表 Python)和 Torch 组成。Py就是python语言,Torch是一款早期的深度学习框架,所以PyTorch是在Torch基础上用python语言重新打造的一款深度学习框架。 那么什么是Torch呢?Torch是纽约大学在2002年开发的深度学习框架。Torch虽然很好用,但是它采用了一门较为小众的语言——Lua语言作为接口。这明显地增高了Torch的使用门槛,想要使用Torch必须学习一门新的编程语言,这让大家都望而却步。 好在Torch的幕后团队也意识到这一点,于是团队采用python语言作为接口对Torch进行了重构,于是就有了PyTorch。 PyTorch代码最早公开版本可追溯至2016年8月24日的v0.1.1(https://github.com/pytorch/pytorch/tags?after=v0.1.4) 随后 •2017年1月正式发布PyTorch •2018年4月更新0.4.0版,支持Windows系统 •2018年11月更新1.0稳定版,已成为GitHub上第二快的开源项目 ...... 对PyTorch版本的更新感兴趣的读者,可以关注PyTorch-Tags,这里是最权威版本更新信息来源,要比官方文档还要快。 PyTorch必备网站 要熟悉PyTorch,不得不熟悉PyTorch的一些官方网站,以及这些网站的使用。下面列举几个实用的PyTorch网站。 官网 https://pytorch.org/ 官网包含权威的PyTorch介绍、PyTorch官方文档、生态资源等信息。例如在Get Started中,可以获取权威的安装信息。例如,特定版本下,windows系统,所支持的CUDA版本是多少,这点非常关键,往往GPU用不起来,就是CUDA版本不匹配。 除了最新稳定版,还可以下载历史版本的whl文件,进行离线安装(网速不好的读者,建议手动下载whl,然后进行安装)。历史版本whl的在哪那里下载呢? 是需要挖掘的,网址在上图的windows系统、Pip安装、Python、CUDA条件下才会出现,它是:https://download.pytorch.org/whl,点击torch,就可以发现所有历史版本都在这里可以找到,并且命名都有良好的规范,这里不过多介绍,在安装部分再详细讲解。 除了Get Started栏目,其它栏目也是很好的学习资料。 Ecosystem:PyTorch生态系统资源库,里面收录生态系统内的资源,也欢迎大家加入并贡献资源,里边有CV数据增强开源库——albumentations、FB的目标检测和分割算法库——detectron2、优秀的部署平台——onnxruntime等等 Mobile:移动端PyTorch实现方案及资源。 Blog:PyTorch相关新闻。 Tutorials:案例教程,这里面都是个人提供的、针对某一个应用的demo级的教程。包含如下方向,对于新手来说,可以看一下,了解个大概,但里面的代码多是demo,无法开箱即用于项目应用,这也是本书第二部分将会弥补的地方,敬请期待。 Docs:PyTorch API官方文档, 这也是我一直首推的学习资料,PyTorch的文档非常友好,可以查阅不同版本,各个API都有详细介绍,大多都有代码案例,PyTorch的基础部分主要从这里开始进行讲解。Docs下还会分PyTorch、torchvision、torchaudio、torchtext等,大家需要针对性的检索。 Resource:这里包含各种资源,如社区、新闻报道、论坛、ModelHub资源等等。 PyTorch发展趋势 为什么要学PyTorch? 因为PyTorch发展迅猛,已经在多方面荣登深度学习框架第一的宝座,学术界绝大多数论文都有PyTorch实现,想要紧跟技术,利用新模型进行科学研究,进行项目开发的,不得不跟随学术界的趋势,所以可以看到PyTorch席卷各界。 PyTorch称王,TensorFlow答应么?一起来看看几个数据。 图1: 各大顶会期刊中,使用PyTorch的论文数量占PyTorch+TensorFlow的百分比。其实就是 p / (p+t),这里有一个分界点就是50%,当达到50%时,说明PyTorch与TensorFlow平分秋色,当大于50%时说明PyTorch已经超过TF,而当数据超过75%,表明PyTorch已经是TF的两倍。从这个趋势可以发现转折点在2018-2019年之间发生,现在已经2021年末了,哪个框架是学术界的带头大哥? 图2:这幅图对比的是PyTorch与TF的决定数量,可以看到TF的份额被PyTorch一步步蚕食,实线代表的PyTorch持续上扬,TF的虚线不断下探。 图片出自:https://horace.io/pytorch-vs-tensorflow/ 通过学术界的论文情况就可以说明PyTorch是未来的大势所趋,虽然说早期PyTorch在工业部署上并不如TensorFlow,但是如今PyTorch推出了libtorch,TorchServe,以及各类优秀的,适配性良好的部署框架层出不穷,如TensorRT、OpenVINO、ONNX等,都可以帮助PyTorch进行快速部署 感觉PyTorch是在学术界占据主导地位,让科研工作者感到满意,新模型都是PyTorch实现的,工业界的开发者总不能不用最新的算法、模型,只能纷纷转向PyTorch了。因此,相信大家选择使用PyTorch进行深度学习、机器学习模型开发,一定能加速大家的算法模型开发,也印证了PyTorch的主旨——An open source machine learning framework that accelerates the path from research prototyping to production deployment.” ​ Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.2-Anaconda.html":{"url":"chapter-1/1.2-Anaconda.html","title":"1.2 环境配置之Anaconda","keywords":"","body":"1.2 环境配置之Anaconda 工欲善其事必先利其器,想要使用PyTorch进行模型开发,必须要搭建好开发环境。听到环境安装,大家是否已经心生恐惧?是否都被某些不知名的报错卡住好几天,网上搜不到答案,各种文章的方案都不适用? 为了解决大家环境安装的苦恼,本章将从Python虚拟环境、Anaconda、Pycharm、CUDA、CuDNN的背景说起,给大家构建系统的认识,理清各软件之间的关系,为大家呈现一个清晰的、完整的开发环境配置。 1.1节中提到过,PyTorch是基于Python为接口提供给用户使用,因此Python语言是我们的核心,PyTorch对于Python只是一个工具包(library),通过import torch的方式使用而已。因此想要搭建完整的PyTorch开发环境,其实是搭建完整的Python开发环境,同时安装上PyTorch这个工具库。 虚拟环境 为了使用PyTorch,先搞定Python环境安装。提到Python环境,就不得不讲python虚拟环境(virtual environment)的概念了。python虚拟环境是为了解决众多的工具包之间版本冲突而设计的一个纯净开发环境,简单地可创建多个虚拟环境,不同的环境中使用不同的工具包,例如虚拟环境1中安装pytorch 1.6, 虚拟环境2中安装的是pytorch0.4,当需要用老版本pytorch时,切换到虚拟环境2,然后调用虚拟环境2中的解释器——python.exe 运行你的.py代码。当需要用pytorch1.6时,就切换到虚拟环境1,调用虚拟环境1中的python.exe运行代码。这样就很好的解决工具包版本冲突问题。 解释器 这里提到一个概念,解释器——python.exe。 解释器,就是人类与CPU之间的桥梁,人写的高级语言,如print(\"Hello World\"),CPU是读不懂的,CPU只能读0/1形式的二进制文件,这时就需要一个翻译官——python.exe, python.exe读取.py文件,解释成二进制文件,让CPU读懂,并运行。这就是解释器的作用,更直观的例子,python3的解释器是无法翻译python2语法的语句print \"Hello World\"的,因此不同的python.exe其实就对应了不同的环境。 Anaconda Anaconda是为方便使用Python而建立的一个软件包,包含常用的250多个工具包,多版本Python解释器和强大的虚拟环境管理工具,所以Anaconda被称为Python全家桶。Anaconda可以使安装、运行和升级环境变得更简单,因此使用它作为Python虚拟环境管理工具。 安装非常简单,首先进入 anaconda官网,点击“Get Started”, 点击”download anaconda installers“,看到如下图所示的信息,选择你对应的操作系统下载,安装即可。 安装完毕,可以尝试创建一个虚拟环境,这里需要注意创建环境的时候就要决定Python的版本,而pytorch的安装对python的版本有要求,所以大家先到pytorch官网看看要装的pytorch版本所支持的python版本,然后再回来创建虚拟环境。这里演示pytorch最新版本1.10的安装。由于1.10是支持python3.6/3.7/3.9的(通过1.1介绍过的神奇网站找到信息),在这里用python3.6作为python版本进行创建虚拟环境。 >>> conda create -n pytorch-1.10-gpu python=3.6 这里的pytorch-1.10-gpu就是虚拟环境的名称,激活(activate)时的标识,同时也会在anaconda安装目录下创建pytorch-1.10-gpu的文件夹,在该文件夹存放本环境所有工具包、解释器,如下图所示: 虚拟环境创建好之后,可以看看这个文件夹里都有些什么,先来看解释器: D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe ,大家可以双击它,就可以看到解释器的相关信息,后续用到pytorch_1.10_gpu这个环境的时候,也是用这个.exe进行运行.py文件,后面用pycharm运行代码的时候会给大家展示。 到此,一个纯净的虚拟环境就创建好了,接下来需要激活(activate)这个环境,然后再里面进行各种工具包的安装。这里先暂停一下,先去看另外一个工具——Pycharm的安装及使用。 anaconda常用命令 创建环境:conda create -n your_env_name python=X.X (X.X为python版本) eg: conda create -n pytorch_tutorial python=3.7 激活环境:source activate your_env_name eg: source activate pytorch_tutorial 退出环境:source deactivate 删除环境:conda remove -n your_env_name –all eg: conda remove -n pytorch_tutorial --all 查看已有虚拟环境:conda env list / conda info -e (推荐大家自行了解更多anaconda命令) 有了anaconda帮助我们管理虚拟环境以及python工具包,接下来就可以安装IDE,用来管理项目了。请看Pycharm安装 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.3-Pycharm.html":{"url":"chapter-1/1.3-Pycharm.html","title":"1.3 环境配置之-IDE——Pycharm & VS Code","keywords":"","body":"1.3 环境配置之IDE——PyCharm&VS Code Pycharm——强大的python IDE Pycharm——强大的python IDE,拥有调试、语法高亮、Project管理、代码跳转、智能提示、版本控制等功能。是 Pycharm有社区版和专业版区分,社区版为免费的,专业版需要付费。 Pycharm 社区版 vs 专业版 由于专业版需要一定的费用,对于普通学习者来说,若不需要特殊功能的话,社区版也是足够的。 专业版和社区版在功能上的一些主要区别如下: 功能分类 PyCharm专业版 PyCharm社区版 基础功能 - 代码编辑 - 代码编辑 - 语法高亮 - 语法高亮 - 代码提示 - 代码提示 - 代码格式化 - 代码格式化 版本控制 - Mercurial, Subversion, Git, Perforce等支持 - Mercurial, Subversion, Git支持 代码分析 - Python代码质量分析 - 基础代码分析 代码重构 - 重命名、提取方法/超类等重构功能 - 有限的重构功能 调试器 - 强大的调试器,支持断点、步进、多画面视图等 - 基本的调试功能 Web开发支持 - Django, Flask等Web框架支持 - 不支持 数据库与SQL支持 - 支持多种数据库与SQL查询 - 不支持 科学计算工具 - 支持NumPy, SciPy等科学计算库 - 不支持 远程开发 - 支持远程解释器、部署和调试 - 不支持 用户界面与自定义 - 高度可定制的用户界面 - 有限的自定义选项 插件支持 - 支持大量插件,扩展性强 - 支持有限插件 许可与费用 - 商业许可,需要付费 - 免费,但仅限于非商业用途 Pycharm 的安装 这里我采用的是pycharm.2019专业版,用于演示。 可以从官网下载安装包 https://www.jetbrains.com/pycharm/ 运行 pycharm-professional-2019.2.exe 选择路径,勾选Add launchers dir to the PATH,等待安装完成 激活部分:略。 这里主要讲如何创建项目,以及关联前面创建的虚拟环境pytorch_1.10_gpu。 打开pycharm,左上角的File可选择New,或者Open,如果已经有一个文件夹下有相关.py代码,那么就用Open对应的文件夹即可。这里假设已经存在pytorch-tutorial-2nd文件夹,找到它,Open即可。 我们找到pytorch-tutorial-2nd\\code\\chapter-1\\01-hello-pytorch.py,发现import torch下面有个红色波浪线,鼠标放上去会提示“No Module named torch\",表明当前环境里并没有torch这个工具包。可好像我们并没有为当前.py文件设定好用哪个一解释器不是?所以我们先将当前项目pytorch-tutorial-2nd的虚拟环境设置为刚刚创建好的pytorch_1.10_gpu,然后再在pytorch_1.10_gpu里安装上pytorch即可。 左上角File--> Settings-->Project:pytorch-tutorial-2nd-->Project Interpreter, 然后如下图找到对应的python.exe,之后选中,点击OK,再次点击OK。就完成了虚拟环境与项目的关联,接着就可以安装pytorch了。 到这里,大家可以尝试运行 pytorch-tutorial-2nd\\code\\chapter-1\\01-hello-pytorch.py,会提示 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe E:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py Traceback (most recent call last): File \"E:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py\", line 9, in import torch ModuleNotFoundError: No module named 'torch' Process finished with exit code 1 这里是完整的Pycharm控制台信息,我们可以看到第一行先是解释器(即对应了我们创建的虚拟环境),然后是执行的.py文件,接着是报错信息,提示没有torch这个module,下一小节我们来就来安装这个module。 Pycharm 拓展 pycharm是很好用的IDE,这里面提供很多快捷键,希望大家可以熟悉使用这些快捷键,例如常用的 批量注释:Ctrl + / 快速查看文档:Ctrl + q 搜索:Ctrl+f 运行:Shift + F10 Tab / Shift + Tab 缩进、不缩进当前行 Ctrl + D 复制选定的区域或行 Ctrl + Y 删除选定的行 更多功能推荐大家自行了解一下pycharm的基础使用,相信它一定是你的高效生产力。 推荐pycharm也有一些文档教程: pycharm官方文档 《pycharm 中文指南》 VS Code 简介 Visual Studio Code 是微软推出的独立源代码编辑器,可在 Windows、macOS 和 Linux 上运行,是 JavaScript 和 Web 开发人员的最佳选择,具有几乎可支持任何编程语言的扩展。其轻量的特点,受到广大开发者的喜爱,这里介绍如何在VS Code中配置python开发环境。 提到Visual Studio Code,大部分人都会联想到Visual Studio,但两者是完全不同的概念。 Visual Studio:适用于 Windows 上 .NET 和 C++ 开发人员的最全面 IDE。 完整打包了一系列丰富的工具和功能,可提升和增强软件开发的每个阶段。 Visual Studio Code:在 Windows、macOS 和 Linux 上运行的独立源代码编辑器。 JavaScript 和 Web 开发人员的最佳选择,具有几乎可支持任何编程语言的扩展。 从官网定义就知道,VS是IDE, VS Code是代码编辑器,下面进行VS Code安装、配置python开发环境。 VS Code下载安装 VS Code下载安装非常简单,通过官网下载安装包:https://visualstudio.microsoft.com/zh-hans/ 双击一路安装,在“选择附加任务”当中,建议把“添加到 PATH(重启后生效)”勾选上。 VS Code Python环境配置 插件1 - Python VS Code强大之处在于有许多插件,在这里python环境也需要安装一些插件,首先需要安装的插件是“Python”。 在IDE左侧的Extensions菜单中(Ctrl+Shift+X),输入python,搜索到Python插件,点击Install 安装好之后,点击右下角,选择解释器。 接着就可以右键对应的代码文件,Run Python -> Run Python File in Terminal,即可获得如下提示 第一行表示选择了解释器C:/Users/yts32/anaconda3/envs/pt112/python.exe,运行代码 f:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py。 PS F:\\pytorch-tutorial-2nd> & C:/Users/yts32/anaconda3/envs/pt112/python.exe f:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py Hello World, Hello PyTorch 1.12.1 CUDA is available:True, version is 11.3 device_name: NVIDIA GeForce RTX 4060 Laptop GPU 插件2 - Python Pylance Pylance是强大的Python静态类型检查器,提供更精确的自动补全和错误检查。 插件3 - autoDocstring autoDocstring是函数自动注释模板生成插件, 在函数下输入\"\"\"回车,即可得到函数的注释模板,例如: def test(a: str): \"\"\"_summary_ Args: a (str): _description_ Returns: _type_: _description_ \"\"\" print(a) 其他功能: 自动生成文档字符串:快速生成可以逐个Tab键完成的文档字符串片段。 支持多种文档字符串格式:用户可以选择不同格式的文档字符串,如Google、Sphinx、Numpy、docBlockr、one-line-sphinx和PEP257。 类型推断:根据PEP484类型提示、默认值和变量名推断参数类型。 支持参数和关键字参数:插件支持args、kwargs、装饰器、错误和参数类型的自动填充。 自定义文档字符串模板:支持自定义模板,使用mustache.js模板引擎。 插件4 - Python Indent Python Indent是一个改进Python自动缩进的工具。这个插件通过解析用户编写的Python代码,自动确定每一行代码应该缩进的程度,主要功能和特点: 自动缩进:按下Enter键时,插件会解析光标位置的Python文件并确定下一行(或两行,如果是悬挂缩进)应该缩进多少,以及附近的行应该如何取消缩进。 括号配对缩进:当光标位于一个开放的括号([({)和它的闭合括号对(相应的]}))之间时,插件会保持后续行的缩进正好在它被打开的位置的右侧。 悬挂缩进:当用户打开一个括号但尚未插入任何内容时,按下Enter键将创建一个悬挂缩进,与VS Code的基本行为相匹配。 关键字缩进:某些Python关键字(return, pass, break, continue, raise)暗示了特定的缩进行为。例如,如果有return语句,则下一行可以取消缩进,因为同一代码块中不能跟随任何语句。 注释扩展:如果用户在注释中间按下Enter键,那么下一行将自动成为注释。 VS Code 整体组件轻便,简洁,适合中高级开发者进行高自由度的插件选择,打造适合自己的IDE,对于要求不高且不需要高级自定义功能,pycharm是不错的选择。 小结 pycharm和VS Code都是强大的IDE,前者适合“懒人”用户,开箱即用,后者适合喜欢DIY的开发者。这里个人建议入门者使用pycharm的社区版,方便快捷无烦恼的用上IDE。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.4-CUDA&cuDNN.html":{"url":"chapter-1/1.4-CUDA&cuDNN.html","title":"1.4 环境配置之CUDA&cuDNN","keywords":"","body":"1.4 环境配置之CUDA&cuDNN 有了python环境和开发环境,马上到主角登场。PyTorch登场前,针对GPU版,还需要额外安装一些东西。 从1.1我们知道PyTorch的安装可根据设备类型分为GPU版或CPU版。 CPU 对于CPU版本直接通过pip或者anaconda命令安装即可,如: >>> pip3 install torch torchvision torchaudio 具体的命令可查阅:https://pytorch.org/get-started/locally/ 官网上给出的命令其实安装了3个包,分别是torch, torchvision,torchaudio,这命令会根据当前系统自动选择对应python版本的whl进行安装,不需要用户额外操作。但,如果网速不好,或者需要离线安装,这时可以考虑下载whl包然后自行安装,下载whl的链接:https://download.pytorch.org/whl/torch/ pytorch与torchvision版本匹配 若是手动下载的whl,需要注意pytorch与torchvision之间版本对应关系,这个可以到torchvision Github查看,这点非常重要,CV中一些报错就是因为torchvision与pytorch版本不匹配导致的。这里就copy过来,大家参考好了。 torch torchvision python main / nightly main / nightly >=3.6, 1.10.0 0.11.1 >=3.6, 1.9.1 0.10.1 >=3.6, 1.9.0 0.10.0 >=3.6, 1.8.2 0.9.2 >=3.6, 1.8.1 0.9.1 >=3.6, 1.8.0 0.9.0 >=3.6, 1.7.1 0.8.2 >=3.6, 1.7.0 0.8.1 >=3.6, 1.7.0 0.8.0 >=3.6, 1.6.0 0.7.0 >=3.6, 1.5.1 0.6.1 >=3.5, 1.5.0 0.6.0 >=3.5, 1.4.0 0.5.0 ==2.7, >=3.5, 1.3.1 0.4.2 ==2.7, >=3.5, 1.3.0 0.4.1 ==2.7, >=3.5, 1.2.0 0.4.0 ==2.7, >=3.5, 1.1.0 0.3.0 ==2.7, >=3.5, 0.2.2 ==2.7, >=3.5, 举一反三,torchaudio、torchtext同理。 GPU版本 深度学习之所以能火,是因为有了强大的GPU支撑,自然地,绝大多数情况下我们会安装GPU版本的pytorch。目前PyTorch不仅支持NVIDIA的GPU,还支持AMD的ROCm的GPU。不过我们还是以N卡为例,毕竟N卡还是主流,A卡仍需努力。 对于N卡,什么型号是pytorch支持的呢?首先,需要计算能力(compute capability)≥3.0的GPU。很多地方都会看到计算能力≥3.0,理论出自哪呢? 我在官方文档中找到了相关信息文档 It has a CUDA counterpart, that enables you to run your tensor computations on an NVIDIA GPU with compute capability >= 3.0 问题来了,怎么知道自己的GPU的compute capability呢?请看NVIDA文档,选择你对应的系列,找到对应型号。 举几个例子: GPU 计算能力 GeForce RTX 2080 7.5 GeForce RTX 2070 7.5 GeForce RTX 2060 7.5 GeForce GTX 1080 6.1 GeForce GTX 1070 6.1 GeForce GTX 1060 6.1 其实,只要是近几年购买的N卡都是没有问题的。确定了显卡是支持的,接下来就要决定一个非常重要事情,就是选中对应的CUDA版本进行安装。 CUDA ​ CUDA(ComputeUnified Device Architecture),是NVIDIA推出的运算平台。 CUDA是一种由NVIDIA推出的通用并行计算架构,该架构使GPU能够解决复杂的计算问题。 与之配套的是cuDNN, NVIDIA cuDNN是用于深度神经网络的GPU加速库。它强调性能、易用性和低内存开销。NVIDIA cuDNN可以集成到更高级别的机器学习框架中。 细心的朋友在PyTorch官网就能发现, Compute Platform中并不给出显卡型号,而是给出CUDA版本,这就要求我们安装特定版本的CUDA,才能使用特定版本的PyTorch。例如PyTorch 1.10 只支持CUDA 10.2, CUDA 11.3,以及CUDA 11.1。为什么这里用了以及呢? 因为在官网上并没有显示CUDA 11.1,但是在https://download.pytorch.org/whl/torch,搜索,可以看到11.1的whl。 在这里选择10.2版本进行安装,CUDA下载通过官网,官网通常只显示最新版本cuda,这里需要大家进入具体的版本下载界面,拖到底部,找到: Archive of Previous CUDA Releases 接着可以找到对应的CUDA版本,进入下载即可,这Installer Type 有 exe (network) 和 exe (local)两种选择,我们选local的方式,下载2.6G的cuda_10.2.89_441.22_win10.exe即可。 安装方式十分简单,一直下一步即可,只需要记住安装到了哪里,这里默认路径为 C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2 下面来测试一下CUDA安装是否成功,可以打开命令窗,进入C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2\\bin,然后输入 nvcc -V cuDNN 有了CUDA平台,还需要安装cuDNN,cuDNN全称为NVIDIA CUDA Deep Neural Network (cuDNN) 。它是一个深度神经网络的加速库,里边实现了神经网络常用的操作,并且是高度优化的,可以极大地榨干NVIDA显卡的性能,因此用N卡都会用cuDNN库。 cuDNN库的安装非常简单,与其说是安装,不如说是下载库文件,放到CUDA所在的目录下。具体步骤如下: 打开网址:https://developer.nvidia.com/cudnn,点击右上角,需要注册,再登录。 登录后,点击Download cuDNN,跳转到下载页面,选择好cudnn版本,操作系统版本,即可开始下载 将下载好的压缩包cudnn-10.2-windows10-x64-v8.2.4.15.zip 解压 分别将bin、include、lib\\x64下的文件分别对应拷贝到C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2文件夹下的bin、include、lib\\x64下 打开命令窗口,在C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2\\extras\\demo_suite文件夹中分别执行bandwidthTest.exe和deviceQuery.exe。观察到Result=PASS 即表示安装成功。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.5-PyTorch-install.html":{"url":"chapter-1/1.5-PyTorch-install.html","title":"1.5 环境配置之PyTorch系列包","keywords":"","body":"1.5 环境配置之PyTorch系列包 虚拟环境,Pycharm,CUDA,cuDNN均已准备好,现在终于可以安装PyTorch了。 现在,通过命令窗口,进入虚拟环境 E:\\pytorch-tutorial-2nd>conda activate pytorch_1.10_gpu (pytorch_1.10_gpu) E:\\pytorch-tutorial-2nd> 可使用以下命令安装 pip3 install torch==1.10.1+cu102 torchvision==0.11.2+cu102 torchaudio===0.10.1+cu102 -f https://download.pytorch.org/whl/cu102/torch_stable.html 可以看到通过pip安装,也是下载我们提到的神奇网站里的whl文件,大家可以根据自己的网速决定是采用pip还是自行下载的方法。 如果网速不好的话,推荐通过神奇的网站——https://download.pytorch.org/whl/torch 搜索对应的whl进行下载。然后pip install *.whl。 在使用pip时,建议大家添加镜像源。例如,清华镜像源或者中科大镜像源,这样安装python工具包的下载速度会快很多,请自行搜索如何添加清华镜像源。 安装完毕,再回到pycharm,运行 pytorch-tutorial-2nd\\code\\chapter-1\\01-hello-pytorch.py,可以看到 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe E:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py Hello World, Hello PyTorch 1.10.1+cu102 CUDA is available:True, version is 10.2 device_name: NVIDIA GeForce GTX 1660 Ti with Max-Q Design Process finished with exit code 0 表示pytorch环境安装完毕,此时我们也可以再次打开pycharm的解释器配置,可以看到当前的解释器(虚拟环境)下,拥有的相关工具包,这个界面也是后续检查当前环境工具包版本常用的工具,请收藏。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.6-JupyterNotebook-install.html":{"url":"chapter-1/1.6-JupyterNotebook-install.html","title":"1.6 环境配置之Jupyter Notebook","keywords":"","body":"1.6 环境配置之Jupyter Notebook 什么是 jupyter notebook 经过前几个章节的准备,目前已经具备了PyTorch开发环境,但本教程需要照顾初学者使用代码,为初学者提供更佳的代码学习体验。因此,在上篇,主要采用Jupyter Notebook进行代码演示。 注意:Jupyter Notebook 建议仅用于学习目的,不推荐用于项目开发。 因为notebook自身定位决定的,先来看看jupyter notebook 的定义“The Jupyter Notebook is a web application for creating and sharing documents that contain code, visualizations, and text. It can be used for data science, statistical modeling, machine learning, and much more.”——官网 Jupyter notebook 是一个网页应用,在这个网页上可以编写代码、做可视化、写文本,这就是非常好的教学展示平台。可以在上面进行概念描述、配上代码、运行结果,并且可以按小段进行代码运行,给用户更多的交互体验,便于用户理解代码细节。 基于此,本书上篇主要采用notebook进行代码讲解,到了中篇,基于完整的项目代码框架进行应用开发。 关于jupyter与jupyter notebook的关系,请大家查看官网(以下开始,notebook 指代 jupyter notebook) notebook 运行逻辑 Jupyter Notebook不仅仅是一个简单的web应用程序,它还需要关联指定的kernel,用于执行我们的代码。相信刚接触notebook的朋友大多都被notebook, kernel的概念搞的一头雾水。如果上述概念理不清楚,就更不清楚如何配置kernel,选择指定的虚拟环境了。 下面,我们先来看看notebook的结构 图片来自官网 图中左边是用户写的代码,传输到中间的Jupyter server, server本身不能执行代码,server把代码传给Kernel,Kernel才是实际执行代码的组件,执行代码的地方。Kernel执行完代码,把结果返回给server,再返回到用户的网页。 从图中可以看出Kernel不仅可以是python.exe,也可以是其他语言的解释器,如Julia, R等,更多kernel可以看支持的kernel列表. 通过上述示意图我们就知道了,在pytorch开发中,kernel其实就是某一个python解释器——python.exe,我们需要为当前的notebook配置相应的kernel,来进入相应的虚拟环境,这样才能运行代码。 notebook 安装 理清概念,下面进行notebook安装,目标是正确调用pytorch_1.10_gpu这个虚拟环境来执行notebook上的代码。 安装过程分三步: 进入虚拟环境:conda activate pytorch_1.10_gpu 安装ipykernel工具包(安装jupyter): pip install jupyter 添加kernel到notebook: python -m ipykernel install --user --name pytorch_1.10_gpu (意思是,将python这个kernel添加到jupyter的kernel中,由于当前已经在虚拟环境中,所以第一个python表示的含义是:D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe;而pytorch_1.10_gpu是kernel的别名,用于区分不同的kernel,这里建议与虚拟环境名称保持一致就好) 启动 在命令行中执行jupyter notebook就可以打开web应用了,网址为:http://localhost:8888; 这里默认端口为8888,如果再次启动一个jupyter notebook,可以看到端口号变为了8889,即它是另外一个web服务。 进入之后,我们可以看到有一个根目录,我们需要找到我们的notebook文件进行打开,这里有一个小技巧,就是进入到指定文件夹后,再运行notebook,这样notebook的路径就进入了想要的文件夹。 配置kernel 我们进入 chapter-1/02-notebook-demo.ipynb,点击run,可能会出现以下错误 --------------------------------------------------------------------------- ModuleNotFoundError Traceback (most recent call last) in 7 \"\"\" 8 ----> 9 import torch 10 11 print(\"Hello World, Hello PyTorch {}\".format(torch.__version__)) ModuleNotFoundError: No module named 'torch' 告诉我们找不到torch这个包,这很明显,使用的kernel不是我们的D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe。 下面我们来设置一下,方法很简单: 重新运行,将看到以下信息,表明notebook的环境就配置好了。 Hello World, Hello PyTorch 1.10.1+cu102 CUDA is available:True, version is 10.2 device_name: NVIDIA GeForce GTX 1660 Ti with Max-Q Design 实用插件——jupyter_contrib_nbextensions 原生的notebook还是缺点意思,这里推荐大家安装jupyter_contrib_nbextensions插件,jupyter_contrib_nbextensions提供了非常丰富的功能,例如代码折叠、分段折叠、代码自动补全、字体大小、行号显示、目录索引等等,详见下图 插件安装十分简单,打开命令窗,进入虚拟环境,分别依次执行 : pip install jupyter_contrib_nbextensions jupyter contrib nbextension install --user 然后重启notebook,就可以看到导航栏里有Nbextensions,大家可以根据自己的喜好进行调整,更多内容请查看Github Notebook 快速上手 notebook所使用的文件格式为.ipynb,jupyter会将.ipynb转为json进行保存,这样便于版本记录以及分享。 例如下图是用sublime打开的 02-notebook-demo.ipynb 下面,我们来研究notebook界面和常用的操作。 界面中需要认识的几个模块分别是:菜单栏、工具栏、单元格(cell) 菜单栏:用得最多的是Kernel,用于中断程序、重启解释器环境、切换解释器等;其它按键顾名思义。 工具栏:一些功能的按钮,高手都是用快捷键的。 单元格:这就是承载信息的地方,cell可分为code cells, markdown cells, raw cells。用得最多的是code cells和markdown cells。 右上角有一个小圆圈,用于观察当前kernel运行状态,如果是实心的,表明kernel正在运行某个cell,被运行的cell以及等待运行的cell的左边会有一个* notebook 的两种模式 Notebook中的单元,有两种模式:命令模式(Command Mode)与编辑模式(Edit Mode),在不同模式下我们可以进行不同的操作。 命令模式:cell的边框为蓝色,此时可对cell进行操作。在编辑模式下,按esc键进入命令模式。 编辑模式:cell的边框为绿色,此时可在单元格内编辑代码或文档。在命令模式下,按enter或return键进入编辑模式。 常用快捷键 在命令模式下,按下“h”键,就会弹出快捷键的介绍,但是太多了,不方便初学者使用,这里总结一些常用的,实用的快捷键供大家参考。 命令模式: 插入单元格: A 键上方插入,B 键在下方插入 合并单元格:选中多个单元格,Shift + M 显示行号:L 删除单元格:连续按两次D 剪切单元格:X。 通常我用X代替删除,毕竟只用按一个键,哈哈。 复制粘贴单元格: C/V 撤销删除的单元格:要撤消已删除的单元格,请按 Z 键 编辑模式: 运行单元格:Ctrl + Enter 运行并创建新单元格:Alt + Enter 分割单元格:光标放到想要分割的地方,Ctrl + Shift + - 函数详情:Shift+Tab (注意,要把模块导入才会提示函数详情!) 请大家将以上快捷键都试用一遍,这些是高频快捷键,下面给大家列举所有快捷键,请收藏。 下面再介绍两个神奇操作,分别是在单元格中执行shell命令以及magic操作。 请自行尝试!+shell命令进行体会。 magic commands Magic关键字是 IPython 的高级用法,如%matplotlib将matplolib设置为交互式 %和%%分别代表 行Magic命令 和 单元格Magic命令 演示一个魔法命令 %%timeit %%timeit a = [] for i in range(10): a.append(i) 858 ns ± 50.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 表示将代码段运行了100万次,并统计运行时间。 推荐阅读 pycharm中使用jupyter notebook:Jupyter notebook support 更多的magic commands请看这里Jupyter 魔术命令(magic commands) 更多技巧推荐大家看看Jupyter Notebook 有哪些技巧? 更多官方信息请查看Jupyter Notebook docs Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.7-PyTorch2.0.html":{"url":"chapter-1/1.7-PyTorch2.0.html","title":"1.7 PyTorch 2.0 与 torch.compile","keywords":"","body":"1.7 PyTorch 2.0 与torch.compile 前言 由于本书开始撰写是2021年,当时pytorch 2.0还未发布。因此主要基于pytorch 1.12进行实践,近期随着LLM等新技术的发展,pytorch2.x应用越来越广泛,为此新增一篇pytorch 2.0的介绍, 并对torch.compile进行代码实验。 pytorch 2.0是2022年12月初在PyTorch Conference 2022 上正式宣布,距离上一个版本pytorch 1.13 的发布仅一个月。虽然早在2022年12月就宣布2.0,但稳定版式2023年3月才正式上线,截止2024年7月,pytorch版本已经发展到2.3。 为什么叫2.0?而不是延续1.13命名为1.14?因为本次更新有众多特性,并且更新内容繁多,相对于1.13.1,2.0有428位开发者贡献了4541个代码提交,可见2.0的更新内容庞大。 2.0 特性 参考官方发布文档,这里总结一下2.0带来的新特性。虽然发布了很多特性,但是对于入门/普通开发者而言,关系不大。pytorch从2017年发展到2022年已经过去五年,从最初的用户易用逐步向工程化、高性能方向迭代,因此后续的版本更多是针对规模化落地、大型AI基建、平台兼容性等方面。 所有的新功能点如下表所示: 其中最大的特点是torch.compile的发布,它是pytorch走向编译范式的重要模块,也是pytorch2.0发布会重点介绍的部分。 下面围绕torch.compile展开梳理几个与编译相关的核心特性。 特性一:torch.compile torch.complie目的是提高计算速度。通常使用只需要一行代码即可完成,例如:model = torch.compile(model)。 之所以一行能让整个pytorch运算提速,是因为complie是一个高级接口,它背后使用了 TorchDynamo、AOTAutograd 和TorchInductor 等工具链来对模型的计算图进行分析、优化和编译。 torch.complie是与开发者关系最大的、最有别于1.x的特性,它背后对于计算图的分析、优化和编译是本次更新的核心构成,但对于普通用户而言,了解好torch.compile的接口,了解其可提高模型计算速度就可以。 特性二:TorchDynamo TorchDynamo是支撑torch.compile的工具,它可进行快速地捕获计算图(Graph),计算图在深度学习中至关重要,它描述了数据在网络中的流动形式。在早期,pytorch团队已经对计算图的捕获进行了一些列工具开发,例如TorchScript。但TorchDynamo相较于之前的工具,在速度上有了更大提升,并且在99%的情况下都能正确、安全地获取计算图。 特性三:AOTAutograd AOTAutograd 的目的是希望在计算运行之前,捕获计算的反向传播过程,即“ahead of time Autograd”。AOTAutograd 通过重用和扩展PyTorch的现有自动微分系统,实现提高训练速度。 特性四:TorchInductor TorchInductor 是一个新的编译器后端,可以为多个硬件平台进行生成优化的代码,例如针对NVIDIA和AMD的GPU,使用OpenAI 的Triton语言(一门GPU编程语言,不是NVIDIA的推理框架)作为目标语言,针对CPU,可生成C++代码。由此可见,TorchInductor 能够为多种加速器和后端生成快速的代码。 特性五:PrimTorch PrimTorch 是将PyTorch底层操作符(operators)进行归约、精简,使下游编译器开发更容易和高效。PyTorch包含1200+操作符,算上重载,有2000+,操作符过多,对于后端和编译器开发式不友好的。为了简化后端开发,提高效率,PrimTorch项目整理了两大类基础操作符,包括: Prim操作符:相对底层的约250个操作符 ATen操作符:约750个操作符,适合直接导出 小结:TorchDynamo、AOTAutograd、TorchInductor和PrimTorch 都在为PyTorch的计算效率服务,让PyTorch计算速度更快、更pythonic。 对于普通用户,重点关注torch.compile的接口使用,接下来将对torch.compile的概念和使用展开说明。 torch.compile 效果 得益于多个模块的优点组合,compile模式对大部分模型均有加速效果,下面看看官方给出的效果对比。 实验配置如下 采用A100 GPU, 采用46个HuggingFace Transformers模型、61个TIMM模型、56个TorchBench模型 采用AMP和float32两种数据精度,最终数据采用加权平均计算,0.75 AMP + 0.25 float32 程序仅采用compile进行包装,不修改任何代码 结果表明,93%的模型都可以采用torch.compile, 并且有43%的训练速度提升,其中AMP速度有51%提升,float32有21%的提升。 在三个模型库中,加速情况如下图所示: torch.compile 接口 根据官方文档定义,\"Optimizes given model/function using TorchDynamo and specified backend.\" torch.compile是采用TorchDynamo和指定的后端对模型/计算进行优化,期望使模型/函数在未来应用时,计算速度更快。 使用上,torch.compile接收一个可调用对象(Callable), 返回一个可调用对象(Callable),对于用户,只需要一行代码,调用compile进行优化。 参数: - model (Callable) : Module或者是Function, 这个Function可以是pytorch的函数,也可以是numpy语句,当前compile还支持numpy的加速优化 - mode:优化模式的选择,目前(2024年7月16日)提供了四种模式,区别在于不同的存储消耗、时间消耗、性能之间的权衡。 default : 默认模式, 在性能和开销之间有不错的平衡 reduce-overhead:这个模式旨在减少使用CUDA图时的Python开销。该模式会增加内存占用,提高速度,并且不保证总是有效。目前,这种方法只适用于那些不改变输入的CUDA图。 max-autotune:基于Triton的矩阵乘法和卷积来提高性能 max-autotune-no-cudagraphs: 与max-autotune一样,但是不会使用CUDA计算图 - fullgraph (bool) : 是否将整个对象构建为单个图(a single graph),否认是False,即根据compile的机制拆分为多个子图。 - dynamic (bool or None) : 是否采用动态形状追踪,默认为None,对于输入形状是变化的,compile会尝试生成对应的kernel来适应动态形状,从而减少重复编译,但并不是所有动态形状都能这样操作,随缘吧,这个过程可以设置TORCH_LOGS=dynamic来观察日志信息。 - backend (str or Callable) : 选择所用的后端,默认是\"inductor\",可以较好平衡性能和开销,可用的后端可以通过torch._dynamo.list_backends()查看,注册自定义后端库,可参考 https://pytorch.org/docs/main/compile/custom-backends.html - options (dict): 用于向后端传入额外数据信息,key-value可以自定义,只要后端可读取即可,这个参数预留了较好的接口。 - disable (bool): Turn torch.compile() into a no-op for testing torch.compile 实验 为了充分观察compile带来的速度变化,以及不同mode之间的影响,下面针对四种情况分别进行速度的观察。 四种情况包括:简单的pytorch运算, Resnet18, BERT, numpy计算。 并在三种型号GPU进行了测试,分别是RTX 4060 Laptop GPU 、L20、H20。 配套代码位于github,注意,torch.compile目前仅支持与Linux系统,并且不支持python≥3.12。 结论: 常见模型均有10-30%的耗时降低 numpy也可以用torch.compile加速,并且耗时降低高达90% 简单的pytorch计算并无法带来速度提升(应该是没有复杂的计算图,无优化空间了 RTX 4060 (8GB) Sin+Cos Resnet18 BERT numpy baseline 1.33 0.92 0.59 94.31 default 3.26(-144.37%) 0.70(23.56%) 0.23(61.04%) 6.52(93.09%) reduce-overhead 5.02(-272.92%) 0.83(20.99%) 0.42(18.11%) 6.26(93.17%) max-autotune 5.16(-269.47%) 0.84(20.07%) 0.44(19.62%) 6.55(92.93%) default 编译耗时 0.45 0 0 0 reduce-overhead 编译耗时 0 0 0 0 max-autotune 编译耗时 0 0 0 0 由于L20与H20服务器无法联网,于是提出BERT模型的测试 L20 (48GB) Sin+Cos Resnet18 numpy baseline 1.34 0.23 1.02 default 2.27 0.21 0.05 reduce-overhead 2.27 0.21 0.05 max-autotune 2.28 0.21 0.05 H20(96GB) Sin+Cos Resnet18 numpy baseline 1.03 0.25 0.79 default 1.74 0.18 0.04 reduce-overhead 1.75 0.18 0.04 max-autotune 1.75 0.18 0.04 小结 pytorch 2.0 进行了多个功能的更新,与普通用户关系最大的是compile编译功能,本小节对compile接口的入参进行分析,并且针对四种不同的modle/function进行了编译对比,对于常用的模型,compile还是可以得到一定计算速度的提升,值得大家尝试。 其中numpy可以降低90%的耗时,效果十分惊艳,这块似乎通过FAQs中发现的功能,建议可以进一步了解: https://pytorch.org/docs/stable/torch.compiler_faq.html# https://pytorch.org/docs/stable/torch.compiler.html#torch-compiler Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/":{"url":"chapter-2/","title":"第二章 PyTorch 核心模块","keywords":"","body":"第二章 PyTorch 核心模块 第二章 PyTorch 核心模块 2.1 PyTorch 模块结构 2.2 新冠肺炎分类 2.3 核心数据结构——Tensor 2.4 张量的相关函数 2.5 自动求导核心——计算图 2.6 Autograd——自动微分 在第一章中,对PyTorch框架进行了全面的介绍,从它的起源、特性到安装和基本使用。现在,我们已经为深入探索PyTorch的核心模块做好了准备。第二章将带领读者深入了解PyTorch核心——它的数据结构、自动求导机制以及如何构建和训练神经网络模型。 本章内容预告 2.1 PyTorch 模块结构 首先,我们将探索PyTorch的模块结构,了解其代码的组织方式和各个模块的功能。这不仅有助于我们更好地理解PyTorch的工作原理,还能帮助我们在后续的开发中快速定位和使用所需的功能。 2.2 新冠肺炎X光分类 通过一个与现实世界紧密相关的案例——新冠肺炎X光分类,我们将介绍PyTorch在实际深度学习项目中的应用。本小节将通过一个具体的项目流程,展示如何使用PyTorch构建数据加载、模型训练和评估的完整工作流。 2.3 核心数据结构——Tensor Tensor作为PyTorch中的核心数据结构,是理解和使用PyTorch的关键。本小节将详细介绍Tensor的概念、属性和操作。 2.4 张量的相关函数 在对Tensor有了基本了解之后,我们将学习张量的各种相关函数,包括它们的数学操作、随机采样、序列化等。这些函数是构建和操作神经网络模型的必备工具。 2.5 自动求导核心——计算图 自动求导是深度学习框架的精髓。本小节将介绍计算图的概念,它是自动求导系统的基石。通过计算图,我们将能够理解如何自动地计算神经网络中的梯度。 2.6 Autograd——自动微分 最后,我们将深入探讨Autograd模块,它是PyTorch自动求导机制的实现。通过本小节的学习,我们将掌握如何利用Autograd进行复杂的梯度计算,从而优化神经网络模型。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.1-module-tree.html":{"url":"chapter-2/2.1-module-tree.html","title":"2.1 PyTorch 模块结构","keywords":"","body":"2.1 PyTorch 模块结构 上一章安装好的PyTorch是一个庞大的python库,其中包含几十个模块,这一小节就来了解模块的构成,模块代码的位置,对应文档的位置。从而帮助读者具象化PyTorch,清楚地知道所用的PyTorch函数、模块都在哪里,是如何调用的。 您的代码位于何处? 很多开发者都用过pip/conda install 进行一键安装,但可能不清楚工具库代码安装在了哪里。使用的时候只知道import *,但具体引用的功能函数又是如何实现的,是模糊的。 为了让大家知道调用的是什么,先来看安装的pytorch在哪里。上一章案例中,我们装的pytorch在:D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch 如果pycharm中配置好了虚拟环境,大家也可以通过pycharm的快捷键,快速定位到这个文件夹。方法是,找到import torch这一行代码,按住Ctrl键,鼠标左键单击torch,就可以跳转到D:\\Anacondadata\\envs\\pytorch1.10_gpu\\Lib\\site-packages\\torch__init.py 文件。 可以看到torch文件夹中有一系列子文件夹,我们平时常用的函数都包含在这些子文件夹中,下面将重点介绍一些。 _pycache_ 该文件夹存放python解释器生成的字节码,后缀通常为pyc/pyo。其目的是通过牺牲一定的存储空间来提高加载速度,对应的模块直接读取pyc文件,而不需再次将.py语言转换为字节码的过程,从此节省了时间。 从文件夹名称可知,它是一个缓存,如果需要,我们当然可以删掉它。更多关于pycache的内容,建议额外阅读:https://www.python.org/dev/peps/pep-3147/#proposal _C 从文件夹名称就知道它和C语言有关,其实它是辅助C语言代码调用的一个模块,该文件夹里存放了一系列pyi文件,pyi文件是python用来校验数据类型的,如果调用数据类型不规范,会报错。更多pyi知识,请查阅PEP 8 -->.pyi files that are read by the type checker in preference of the corresponding .py files. PyTorch的底层计算代码采用的是C++语言编写,并封装成库,供pytorch的python语言进行调用。这一点非常重要,后续我们会发现一些pytorch函数无法跳转到具体实现,这是因为具体的实现通过C++语言,我们无法在Pycharm中跳转查看。 include 上面讲到pytorch许多底层运算用的是C++代码,那么C++代码在哪里呢? 它们在这里,在torch/csrc文件夹下可以看到各个.h/.hpp文件,而在python库中,只包含头文件,这些头文件就在include文件夹下。 lib torch文件夹中最重要的一个模块,torch文件夹占3.2GB的空间,98%的内容都在lib中,占了3.16GB空间。pytorch的内容几乎都在lib里面,让我们看看里面是什么。 lib文件夹下包含大量的.lib .dll文件(分别是静态链接库和动态链接库),例如大名鼎鼎的cudnn64_7.dll(占435MB), torch_cuda.dll(940MB)。这些底层库都会被各类顶层python api调用。这里推荐大家自行了解什么是静态链接库和动态链接库。 autograd 该模块是pytorch的核心模块与概念,它实现了梯度的自动求导,极大地简化了深度学习研究者开发的工作量,开发人员只需编写前向传播代码,反向传播部分由autograd自动实现,再也不用手动去推导数学公式,然后编写代码了(很多朋友可能不知道,在早期的深度学习框架中是没有这个功能的,例如caffe,它需要手动编写反向传播的公式代码) nn 相信这个模块是99%pytorch开发者使用频率最高的模块,搭建网络的网络层就在nn.modules里边。nn.modules也将作为一章独立展开。我们可以到D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch\\nn\\modules里面看看是否有你熟悉的网络层? onnx pytorch模型转换到onnx模型表示的核心模块,进入文件夹可以看到大量的opset**.py, 这里留下一个问题,各版本opset是什么意思?有什么区别? optim 优化模块,深度学习的学习过程,就是不断的优化,而优化使用的方法函数,都暗藏在了optim文件夹中,进入该文件夹,可以看到熟悉的优化方法:“Adam”、“SGD”、“ASGD”等。以及非常重要的学习率调整模块,lr_scheduler.py。该模块也将采用独立一章进行详细剖析。 utils utils是各种软件工程中常见的文件夹,其中包含了各类常用工具,其中比较关键的是data文件夹,tensorboard文件夹,这些工具都将在后续章节详细展开。第三章将展开data里的dataloader与dataset等数据读取相关的模块。 其他文件夹不再逐一介绍,读者可以到官方文档查看。 以上是torch库,针对不同的应用方向,pytorch还提供了torchvision\\torchtext\\torchaudio等模块,本书重点对torchvision进行剖析,其它两个模块类似。 torchvision 类似地,我们来到D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision文件夹下看看有什么模块。 datasets 这里是官方为常用的数据集写的数据读取函数,例如常见的cifar, coco, mnist,svhn,voc都是有对应的函数支持,可以方便地使用轮子,同时也可以学习大牛们是如何写dataset的。 models 这里是宝藏库,里边存放了经典的、可复现的、有训练权重参数可下载的视觉模型,例如分类的alexnet、densenet、efficientnet、mobilenet-v1/2/3、resnet等,分割模型、检测模型、视频任务模型、量化模型。这个库中的模型实现,也是大家可以借鉴学习的好资料,可以模仿它们的代码结构、函数、类的组织。 ops 视觉任务特殊的功能函数,例如检测中用到的 roi_align, roi_pool,boxes的生成,以及focal_loss实现,都在这里边有实现。 transforms 数据增强库,相信99%的初学者用到的第一个视觉数据增强库就是transforms了,transforms是pytorch自带的图像预处理、增强、转换工具,可以满足日常的需求。但无法满足各类复杂场景,因此后续会介绍更强大的、更通用的、使用人数更多的数据增强库——Albumentations。 通过torchvision\\transforms\\transforms.py , 可以看到 torchvision包含了这些功能 __all__ = [\"Compose\", \"ToTensor\", \"PILToTensor\", \"ConvertImageDtype\", \"ToPILImage\", \"Normalize\", \"Resize\", \"Scale\", \"CenterCrop\", \"Pad\", \"Lambda\", \"RandomApply\", \"RandomChoice\", \"RandomOrder\", \"RandomCrop\", \"RandomHorizontalFlip\", \"RandomVerticalFlip\", \"RandomResizedCrop\", \"RandomSizedCrop\", \"FiveCrop\", \"TenCrop\", \"LinearTransformation\", \"ColorJitter\", \"RandomRotation\", \"RandomAffine\", \"Grayscale\", \"RandomGrayscale\", \"RandomPerspective\", \"RandomErasing\", \"GaussianBlur\", \"InterpolationMode\", \"RandomInvert\", \"RandomPosterize\", \"RandomSolarize\", \"RandomAdjustSharpness\", \"RandomAutocontrast\", \"RandomEqualize\"] 通过上面的内容,相信大家对所安装的代码结构有了清晰认识,也知道自己将调用的代码函数都在哪里,已经为下一步工作打好基础,下一节我们极简的代码,完成第一个深度学习任务—— 新冠肺炎X光分类 。其目的在于搭建模型训练框架,加深对各模块的理解,为后续对核心模块的讲解打下基础。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.2-covid-19-cls.html":{"url":"chapter-2/2.2-covid-19-cls.html","title":"2.2 新冠肺炎分类","keywords":"","body":"2.2 新冠肺炎X光分类 上一节,我们学习了pytorch python API的结构,本节将以一个具体的案例介绍pytorch模型训练流程,并提出一系列问题,供大家思考。当然,这些问题也是本书后续章节一一解答的内容。 相信绝大多数朋友接触过或者看到的第一个Hello World级图像分类都是Mnist,思来想去觉得还是换点东西,于是选择了当下与所有人都息息相关的案例——新型冠状病毒肺炎(Corona Virus Disease 2019,COVID-19),简称“新冠肺炎”。关于新冠肺炎的背景,大家很熟悉了,口罩、绿码、核酸检测已经融入了我们的生活。因此,想让大家更进一步的了解COVID-19,所以选用此案例。当然,最重要的目的是要了解pytorch如何完成模型训练。 案例背景 2020年1月底2月初的时候,新冠在国内/外大流行。而确定一个人是否患有新冠肺炎,是尤为重要的事情。新冠肺炎的确诊需要通过核酸检测完成,但是核酸检测并不是那么容易完成,需要医护人员采样、送检、在PCR仪器上进行检测、出结果、发报告等一系列复杂工序,核酸检测产能完全达不到当时的检测需求。在疫情初期,就有医生提出,是否可以采用特殊方法进行诊断,例如通过CT、X光的方法,给病人进行X光摄影,几分钟就能看出结果,比核酸检测快了不少。于是,新冠肺炎患者的胸片X光数据就不断的被收集,并发布到网上供全球科学家使用,共同抗击新冠疫情。这里就采用了https://github.com/ieee8023/covid-chestxray-dataset上的数据,同时采用了正常人的X光片,来自于:https://github.com/zoogzog/chexnet。 由于本案例目的是pytorch流程学习,为了简化学习过程,数据仅选择了4张图片,分为2类,正常与新冠,训练集2张,验证集2张。标签信息存储于TXT文件中。具体目录结构如下: ├─imgs │ ├─covid-19 │ │ auntminnie-a-2020_01_28_23_51_6665_2020_01_28_Vietnam_coronavirus.jpeg │ │ ryct.2020200028.fig1a.jpeg │ │ │ └─no-finding │ 00001215_000.png │ 00001215_001.png │ └─labels train.txt valid.txt 建模思路 这是一个典型的图像分类任务,此处采用面向过程的思路给大家介绍如何进行代码编写。 step 1 数据 需要编写代码完成数据的读取,转换成模型能够读取的格式。这里涉及pytorch的dataset,dataloader,transforms等模块。以及需要清楚地知道pytorch的模型需要怎样的格式。 数据模块需要完整的工作大体如下图所示: 首先,需要将数据在硬盘上的信息,如路径,标签读取并存储起来,然后被使用,这一步骤主要是通过COVID19Dataset这个类。类里有四个函数,除了Dataset类必须要实现的三个外,我们通过get_img_info()函数实现读取硬盘中的路径、标签等信息,并存储到一个列表中。后续大家可以根据不同的任务情况在这个函数中修改,只要能获取到数据的信息,供\\_getitem__函数进行读取。 接着,使用dataloader进行封装,dataloader是一个数据加载器,提供诸多方法进行数据的获取,如设置一个batch获取几个样本,采用几个进程进行数据读取,是否对数据进行随机化等功能。 下一步,还需要设置对图像进行预处理(Preprocess)的操作,这里为了演示,仅采用resize 和 totensor两个方法,并且图片只需要缩放到8x8的大小,并不需要224,256,448,512,1024等大尺寸。(totensor与下一小节内容强相关) step 2 模型 数据模块构建完毕,需要输入到模型中,所以我们需要构建神经网络模型,模型接收数据并前向传播处理,输出二分类概率向量。此处需要用到nn.Module模块和nn下的各个网络层进行搭建模型,模型的搭建就像搭积木,一层一层地摞起来。模型完成的任务就如下图所示:下图示意图是一张分辨率为4x4的图像输入到模型中,模型经过运算,输出二分类概率。中间的“?\"是什么内容呢? 这里,“?”是构建一个极其简单的卷积神经网络,仅仅包含两个网络层,第一层是包含1个3*3卷积核的2d卷积,第二层是两个神经元的全连接层(pytorch也叫linear层)。模型的输入被限制在了8x8,原因在于linear层设置了输入神经元个数为36, 8x8与36之间是息息相关的,他们之间的关系是什么?这需要大家对卷积层有一定了解。(大家可以改一下36,改为35,或者transforms_func()中的resize改为9x9,看看会报什么错误,这个错误是经常会遇到的报错) step3 优化 模型可以完成前向传播之后,根据什么规则对模型的参数进行更新学习呢?这就需要损失函数和优化器的搭配了,损失函数用于衡量模型输出与标签之间的差异,并通过反向传播获得每个参数的梯度,有了梯度,就可以用优化器对权重进行更新。这里就要涉及各种LossFunction和optim中的优化器,以及学习率调整模块optim.lr_scheduler。 这里,采用的都是常用的方法:交叉熵损失函数(CrossEntropyLoss)、随机梯度下降法(SGD)和按固定步长下降学习率策略(StepLR)。 step4 迭代 有了模型参数更新的必备组件,接下来需要一遍又一遍地给模型喂数据,监控模型训练状态,这时候就需要for循环,不断地从dataloader里取出数据进行前向传播,反向传播,参数更新,观察loss、acc,周而复始。当达到条件,如最大迭代次数、某指标达到某个值时,进行模型保存,并break循环,停止训练。 以上就是一个经典的面向过程式的代码编写,先考虑数据怎么读进来,读进来之后喂给的模型如何搭建,模型如何更新,模型如何迭代训练到满意。请大家结合代码一步一步的观察整体过程。 在经过几十个epoch的训练之后达到了100%,模型可以成功区分从未见过的两张图片:auntminnie-a-2020_01_28_23_51_6665_2020_01_28_Vietnam_coronavirus.jpeg,00001215_000.png。 由于数据量少,随机性非常大,大家多运行几次,观察结果。不过本案例结果完全不重要!),可以看到模型的准确率(Accuracy)变化。 一系列问题 通过上述步骤及代码,虽然完成了一个图像分类任务,但其中很多细节想必大家还是弄不清楚,例如: 图像数据是哪用一行代码读取进来的? transforms.Compose是如何工作对图像数据进行转换的? ToTensor又有哪些操作? 自己如何编写Dataset? DataLoader有什么功能?如何使用?有什么需要注意的? 模型如何按自己的数据流程搭建? nn有哪些网络层可以调用? 损失函数有哪些? 优化器是如何更新model参数的? 学习率调整有哪些方法?如何设置它们的参数? model.train()与model.eval()作用是什么? optimizer.zero_grad()是做什么?为什么要梯度清零? scheduler.step() 作用是什么?应该放在哪个for循环里? 等等 如果大家能有以上的问题提出,本小节的目的就达到了。大家有了模型训练的思路,对过程有了解,但是使用细节还需进一步学习,更多pytorch基础内容将会在后续章节一一解答。 下一小节我们将介绍流动在pytorch各个模块中的基础数据结构——Tensor(张量)。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.3-datastruct-tensor.html":{"url":"chapter-2/2.3-datastruct-tensor.html","title":"2.3 核心数据结构——Tensor","keywords":"","body":"2.3 核心数据结构——Tensor 张量初认识 经过前两小节的铺垫,大家一定对pytorch有了初步认识,本小节就展开讲pytorch的核心数据结构——Tensor(张量)。Tensor 中文被翻译为张量。张量在不同学科中有不同的意义,在深度学习中张量表示的是一个多维数组,它是标量、向量、矩阵的拓展。标量是零维张量,向量是一维张量,矩阵是二维张量,一个RGB图像的数组就是一个三维张量,第一维是图像的高,第二维是图像的宽,第三维是图像的颜色通道。 在pytorch中,有两个张量的相关概念极其容易混淆,分别是torch.Tensor和torch.tensor。其实,通过命名规范,可知道torch.Tensor是Python的一个类, torch.tensor是Python的一个函数。通常我们调用torch.tensor进行创建张量,而不直接调用torch.Tensor类进行创建。为了进一步区分两者,我们来看看它们代码实现。 torch.Tensor:类定义与torch/_tensor.py#L80,它继承torch._C._TensorBase,这里看到_C就知道要接触C++代码了。 跳转到torch/C/\\_init__.pyi #L839 可以看到: # Defined in torch/csrc/autograd/python_variable.cpp class _TensorBase(metaclass=_TensorMeta): requires_grad: _bool shape: Size 张量的定义和底层C++实现是在python_variable.cpp代码中,感兴趣的朋友可以进一步探究。 torch.tensor:pytorch的一个函数,用于将数据变为张量形式的数据,例如list, tuple, NumPy ndarray, scalar等。 同样,torch.tensor的底层实现也是C++代码,具体实现位于torch_C_VariableFunctions.pyi文件。如2.1节所述,.pyi文件用于Python类型检查,而其底层实现在对应的C++代码中。 后续将不再区分Tensor和tensor,主要用小写tensor表示张量这个数据类型(数据结构)。 张量的作用 tensor之于pytorch等同于ndarray之于numpy,它是pytorch中最核心的数据结构,用于表达各类数据,如输入数据、模型的参数、模型的特征图、模型的输出等。这里边有一个很重要的数据,就是模型的参数。对于模型的参数,我们需要更新它们,而更新操作需要记录梯度,梯度的记录功能正是被张量所实现的(求梯度是autograd实现的)。 张量的历史演变 讲tensor结构之前,还需要介绍一小段历史,那就是Variable与Tensor。在0.4.0版本之前,Tensor需要经过Variable的包装才能实现自动求导。从0.4.0版本开始,torch.Tensor与torch.autograd.Variable合并,torch.Tensor拥有了跟踪历史操作的功能。虽然Variable仍可用,但Variable返回值已经是一个Tensor(原来返回值是Variable),所以今后无需再用Variable包装Tensor。 虽然Variable的概念已经被摒弃,但是了解其数据结构对理解Tensor还是有帮助的。Variable不仅能对Tensor包装,而且能记录生成Tensor的运算(这是自动求导的关键)。在Variable类中包含5个属性:data,grad,grad_fn,is_leaf,requires_grad data: 保存的是具体数据,即被包装的Tensor; grad: 对应于data的梯度,形状与data一致; grad_fn: 记录创建该Tensor时用到的Function,该Function在反向传播计算中使用,因此是自动求导的关键; requires_grad: 指示是否计算梯度; is_leaf: 指示节点是否为叶子节点,为叶子结点时,反向传播结束,其梯度仍会保存,非叶子结点的梯度被释放,以节省内存。 从Variable的主要属性中可以发现,除了data外,grad,grad_fn,is_leaf和requires_grad都是为计算梯度服务,所以Variable在torch.autogard包中自然不难理解。 但是我们的数据载体是tensor,每次需要自动求导,都要用Variable包装,这明显太过繁琐,于是PyTorch从0.4.0版将torch.Tensor与torch.autograd.Variable合并。 张量的结构 tensor是一个类,我们先来认识它有哪些属性,再去观察它有哪些方法函数可使用。 Tensor主要有以下八个主要属性,data,dtype,shape,device,grad,grad_fn,is_leaf,requires_grad。 data:多维数组,最核心的属性,其他属性都是为其服务的; dtype:多维数组的数据类型,tensor数据类型如下,常用到的三种已经用红框标注出来; shape:多维数组的形状; device: tensor所在的设备,cpu或cuda; grad,grad_fn,is_leaf和requires_grad就与Variable一样,都是梯度计算中所用到的。 张量的属性还有很多,大家可以通过Pycharm的debug功能进行查看 更多关于张量的概念背景,请查看官方文档,下一小节,我们进行张量的操作介绍。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.4-method-tensor.html":{"url":"chapter-2/2.4-method-tensor.html","title":"2.4 张量的相关函数","keywords":"","body":"2.4 张量的相关函数 接下来开始学习各类张量的api,主要参考官方文档,通过右边目录栏可以看出有以下几个部分。 torchTensors Generators Random sampling Serialization Parallelism Locally disabling gradient computation Math operations Utilities 里面有上百个函数,这里只挑高频使用的进行讲解,建议大家自行浏览一遍官方文档,看看都有哪些功能,便于今后使用到的时候不必重复造轮子。 张量的创建 直接创建 torch.tensor torch.tensor(data, dtype=None, device=None, requires_grad=False, pin_memory=False) data(array_like) - tensor的初始数据,可以是list, tuple, numpy array, scalar或其他类型。 dtype(torch.dtype, optional) - tensor的数据类型,如torch.uint8, torch.float, torch.long等 device (torch.device, optional) – 决定tensor位于cpu还是gpu。如果为None,将会采用默认值,默认值在torch.set_default_tensor_type()中设置,默认为 cpu。 requires_grad (bool, optional) – 决定是否需要计算梯度。 pin_memory (bool, optional) – 是否将tensor存于锁页内存。这与内存的存储方式有关,通常为False。 import torch import numpy as np l = [[1., -1.], [1., -1.]] t_from_list = torch.tensor(l) arr = np.array([[1, 2, 3], [4, 5, 6]]) t_from_array = torch.tensor(arr) print(t_from_list, t_from_list.dtype) print(t_from_array, t_from_array.dtype) tensor([[ 1., -1.], ​ [ 1., -1.]]) torch.float32 tensor([[1, 2, 3], ​ [4, 5, 6]]) torch.int64 可以看到t_from_list是float32类型,而t_from_array是int64类型。如果想让tensor是其他数据类型,可以在创建tensor时使用dtype参数确定数据类型。 import torch import numpy as np arr = np.array([[1, 2, 3], [4, 5, 6]]) t_from_array = torch.tensor(arr, dtype=torch.uint8) print(t_from_array) tensor([[1, 2, 3], ​ [4, 5, 6]], dtype=torch.uint8) torch.from_numpy 还有一种常用的通过numpy创建tensor方法是torch.from_numpy()。这里需要特别注意的是,创建的tensor和原array共享同一块内存(The returned tensor and ndarray share the same memory. ),即当改变array里的数值,tensor中的数值也会被改变。 import torch import numpy as np arr = np.array([[1, 2, 3], [4, 5, 6]]) t_from_numpy = torch.from_numpy(arr) print(\"numpy array: \", arr) print(\"tensor : \", t_from_numpy) print(\"\\n修改arr\") arr[0, 0] = 0 print(\"numpy array: \", arr) print(\"tensor : \", t_from_numpy) print(\"\\n修改tensor\") t_from_numpy[0, 0] = -1 print(\"numpy array: \", arr) print(\"tensor : \", t_from_numpy) > > numpy array: [[1 2 3] [4 5 6]] tensor : tensor([[1, 2, 3], ​ [4, 5, 6]]) 修改arr numpy array: [[0 2 3] [4 5 6]] tensor : tensor([[0, 2, 3], ​ [4, 5, 6]]) 修改tensor numpy array: [[-1 2 3] [ 4 5 6]] tensor : tensor([[-1, 2, 3], ​ [ 4, 5, 6]]) 可以看到虽然只改变了arr的值,但是tensor中的data也被改变了,这一点在使用过程中需要注意。 依数值创建 torch.zeros torch.zeros(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:依给定的size创建一个全0的tensor,默认数据类型为torch.float32(也称为torch.float)。 主要参数: layout(torch.layout, optional) - 参数表明张量在内存中采用何种布局方式。常用的有torch.strided, torch.sparse_coo等。 out(tensor, optional) - 输出的tensor,即该函数返回的tensor可以通过out进行赋值,请看例子。 example: import torch o_t = torch.tensor([1]) t = torch.zeros((3, 3), out=o_t) print(t, '\\n', o_t) print(id(t), id(o_t)) > > tensor([[0, 0, 0], ​ [0, 0, 0], ​ [0, 0, 0]]) tensor([[0, 0, 0], ​ [0, 0, 0], ​ [0, 0, 0]]) 4925603056 4925603056 可以看到,通过torch.zeros创建的张量不仅赋给了t,同时赋给了o_t,并且这两个张量是共享同一块内存,只是变量名不同。 torch.zeros_like torch.zeros_like(input, dtype=None, layout=None, device=None, requires_grad=False) 功能:依input的size创建全0的tensor。 主要参数: input(Tensor) - 创建的tensor与intput具有相同的形状。 example: import torch t1 = torch.tensor([[1., -1.], [1., -1.]]) t2 = torch.zeros_like(t1) print(t2) tensor([[0., 0.], ​ [0., 0.]]) 除了创建全0还有创建全1的tensor,使用方法是一样的,这里就不赘述。 torch.ones(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:依给定的size创建一个全1的tensor。 torch.ones_like(input, dtype=None, layout=None, device=None, requires_grad=False) 功能:依input的size创建全1的tensor。 torch.full(size, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:依给定的size创建一个值全为fill_value的tensor。 主要参数: siz (int...) - tensor的形状。 fill_value - 所创建tensor的值 out(tensor, optional) - 输出的tensor,即该函数返回的tensor可以通过out进行赋值。 example: import torch print(torch.full((2, 3), 3.141592)) torch.full_like(input, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) torch.full_like之于torch.full等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.arange(start=0, end, step=1, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:创建等差的1维张量,长度为 (end-start)/step,需要注意数值区间为[start, end)。 主要参数: start (Number) – 数列起始值,默认值为0。the starting value for the set of points. Default: 0. end (Number) – 数列的结束值。 step (Number) – 数列的等差值,默认值为1。 out (Tensor, optional) – 输出的tensor,即该函数返回的tensor可以通过out进行赋值。 example: import torch print(torch.arange(1, 2.51, 0.5)) torch.range()函数就不推荐了,因为官网说了“This function is deprecated in favor of torch.arange().” torch.linspace(start, end, steps=100, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:创建均分的1维张量,长度为steps,区间为[start, end]。 主要参数: start (float) – 数列起始值。 end (float) – 数列结束值。 steps (int) – 数列长度。 example: print(torch.linspace(3, 10, steps=5)) print(torch.linspace(1, 5, steps=3)) torch.logspace(start, end, steps=100, base=10.0, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:创建对数均分的1维张量,长度为steps, 底为base。 主要参数: start (float) – 确定数列起始值为base^start end (float) – 确定数列结束值为base^end steps (int) – 数列长度。 base (float) - 对数函数的底,默认值为10,此参数是在pytorch 1.0.1版本之后加入的。 example: torch.logspace(start=0.1, end=1.0, steps=5) torch.logspace(start=2, end=2, steps=1, base=2) torch.eye(n, m=None, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)** 功能:创建单位对角矩阵。 主要参数: n (int) - 矩阵的行数 m (int, optional) - 矩阵的列数,默认值为n,即默认创建一个方阵 example: import torch print(torch.eye(3)) print(torch.eye(3, 4)) torch.empty(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False, pin_memory=False) 功能:依size创建“空”张量,这里的“空”指的是不会进行初始化赋值操作。 主要参数: size (int...) - 张量维度 pin_memory (bool, optional) - pinned memory 又称page locked memory,即锁页内存,该参数用来指示是否将tensor存于锁页内存,通常为False,若内存足够大,建议设置为True,这样在转到GPU时会快一些。 torch.empty_like(input, dtype=None, layout=None, device=None, requires_grad=False) 功能:torch.empty_like之于torch.empty等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.empty_strided(size, stride, dtype=None, layout=None, device=None, requires_grad=False, pin_memory=False) 功能:依size创建“空”张量,这里的“空”指的是不会进行初始化赋值操作。 主要参数: stride (tuple of python:ints) - 张量存储在内存中的步长,是设置在内存中的存储方式。 size (int...) - 张量维度 pin_memory (bool, optional) - 是否存于锁页内存。 依概率分布创建 torch.normal(mean, std, out=None) 功能:为每一个元素以给定的mean和std用高斯分布生成随机数 主要参数: mean (Tensor or Float) - 高斯分布的均值, std (Tensor or Float) - 高斯分布的标准差 特别注意事项: mean和std的取值分别有2种,共4种组合,不同组合产生的效果也不同,需要注意 mean为张量,std为张量,torch.normal(mean, std, out=None),每个元素从不同的高斯分布采样,分布的均值和标准差由mean和std对应位置元素的值确定; mean为张量,std为标量,torch.normal(mean, std=1.0, out=None),每个元素采用相同的标准差,不同的均值; mean为标量,std为张量,torch.normal(mean=0.0, std, out=None), 每个元素采用相同均值,不同标准差; mean为标量,std为标量,torch.normal(mean, std, size, *, out=None) ,从一个高斯分布中生成大小为size的张量; 案例1 import mean = torch.arange(1, 11.) std = torch.arange(1, 0, -0.1) normal = torch.normal(mean=mean, std=std) print(\"mean: {}, \\nstd: {}, \\nnormal: {}\".format(mean, std, normal)) mean: tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]), std: tensor([1.0000, 0.9000, 0.8000, 0.7000, 0.6000, 0.5000, 0.4000, 0.3000, 0.2000, ​ 0.1000]), normal: tensor([ 1.3530, -1.3498, 3.0021, 5.1200, 3.9818, 5.0163, 6.9272, 8.1171, ​ 9.0623, 10.0621]) 1.3530是通过均值为1,标准差为1的高斯分布采样得来, -1.3498是通过均值为2,标准差为0.9的高斯分布采样得来,以此类推 torch.rand(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:在区间[0, 1)上,生成均匀分布。 主要参数: size (int...) - 创建的张量的形状 torch.rand_like(input, dtype=None, layout=None, device=None, requires_grad=False) torch.rand_like之于torch.rand等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.randint(low=0, high, size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:在区间[low, high)上,生成整数的均匀分布。 主要参数: low (int, optional) - 下限。 high (int) – 上限,主要是开区间。 size (tuple) – 张量的形状。 example print(torch.randint(3, 10, (2, 2))) torch.randint_like(input, low=0, high, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:torch.randint_like之于torch.randint等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.randn(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:生成形状为size的标准正态分布张量。 主要参数: size (int...) - 张量的形状 torch.randn_like(input, dtype=None, layout=None, device=None, requires_grad=False) 功能:torch.rafndn_like之于torch_randn等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.randperm(n, out=None, dtype=torch.int64, layout=torch.strided, device=None, requires_grad=False) 功能:生成从0到n-1的随机排列。perm == permutation torch.bernoulli(input, *, generator=None, out=None) 功能:以input的值为概率,生成伯努力分布(0-1分布,两点分布)。 主要参数: input (Tensor) - 分布的概率值,该张量中的每个值的值域为[0-1] example: import torch p = torch.empty(3, 3).uniform_(0, 1) b = torch.bernoulli(p) print(\"probability: \\n{}, \\nbernoulli_tensor:\\n{}\".format(p, b)) probability: tensor([[0.7566, 0.2899, 0.4688], ​ [0.1662, 0.8341, 0.9572], ​ [0.6060, 0.4685, 0.6366]]), bernoulli_tensor: tensor([[0., 0., 1.], ​ [1., 1., 1.], ​ [1., 1., 1.]]) 张量的操作 熟悉numpy的朋友应该知道,Tensor与numpy的数据结构很类似,不仅数据结构类似,操作也是类似的,接下来介绍Tensor的常用操作。由于操作函数很多,这里就不一一举例,仅通过表格说明各个函数作用,详细介绍可查看官方文档 cat 将多个张量拼接在一起,例如多个特征图的融合可用。 concat 同cat, 是cat()的别名。 conj 返回共轭复数。 chunk 将tensor在某个维度上分成n份。 dsplit 类似numpy.dsplit()., 将张量按索引或指定的份数进行切分。 column_stack 水平堆叠张量。即第二个维度上增加,等同于torch.hstack。 dstack 沿第三个轴进行逐像素(depthwise)拼接。 gather 高级索引方法,目标检测中常用于索引bbox。在指定的轴上,根据给定的index进行索引。强烈推荐看example。 hsplit 类似numpy.hsplit(),将张量按列进行切分。若传入整数,则按等分划分。若传入list,则按list中元素进行索引。例如:[2, 3] and dim=0 would result in the tensors input[:2], input[2:3], and input[3:]. hstack 水平堆叠张量。即第二个维度上增加,等同于torch.column_stack。 index_select 在指定的维度上,按索引进行选择数据,然后拼接成新张量。可知道,新张量的指定维度上长度是index的长度。 masked_select 根据mask(0/1, False/True 形式的mask)索引数据,返回1-D张量。 movedim 移动轴。如0,1轴交换:torch.movedim(t, 1, 0) . moveaxis 同movedim。Alias for torch.movedim().(这里发现pytorch很多地方会将dim和axis混用,概念都是一样的。) narrow 变窄的张量?从功能看还是索引。在指定轴上,设置起始和长度进行索引。例如:torch.narrow(x, 0, 0, 2), 从第0个轴上的第0元素开始,索引2个元素。x[0:0+2, ...] nonzero 返回非零元素的index。torch.nonzero(torch.tensor([1, 1, 1, 0, 1])) 返回tensor([[ 0], [ 1], [ 2], [ 4]])。建议看example,一看就明白,尤其是对角线矩阵的那个例子,太清晰了。 permute 交换轴。 reshape 变换形状。 row_stack 按行堆叠张量。即第一个维度上增加,等同于torch.vstack。Alias of torch.vstack(). scatter scatter_(dim, index, src, reduce=None) → Tensor。将src中数据根据index中的索引按照dim的方向填进input中。这是一个十分难理解的函数,其中index是告诉你哪些位置需要变,src是告诉你要变的值是什么。这个就必须配合例子讲解,请跳转到本节底部进行学习。 scatter_add 同scatter一样,对input进行元素修改,这里是 +=, 而scatter是直接替换。 split 按给定的大小切分出多个张量。例如:torch.split(a, [1,4]); torch.split(a, 2) squeeze 移除张量为1的轴。如t.shape=[1, 3, 224, 224]. t.squeeze().shape -> [3, 224, 224] stack 在新的轴上拼接张量。与hstack\\vstack不同,它是新增一个轴。默认从第0个轴插入新轴。 swapaxes Alias for torch.transpose().交换轴。 swapdims Alias for torch.transpose().交换轴。 t 转置。 take 取张量中的某些元素,返回的是1D张量。torch.take(src, torch.tensor([0, 2, 5]))表示取第0,2,5个元素。 take_along_dim 取张量中的某些元素,返回的张量与index维度保持一致。可搭配torch.argmax(t)和torch.argsort使用,用于对最大概率所在位置取值,或进行排序,详见官方文档的example。 tensor_split 切分张量,核心看indices_or_sections变量如何设置。 tile 将张量重复X遍,X遍表示可按多个维度进行重复。例如:torch.tile(y, (2, 2)) transpose 交换轴。 unbind 移除张量的某个轴,并返回一串张量。如[[1], [2], [3]] --> [1], [2], [3] 。把行这个轴拆了。 unsqueeze 增加一个轴,常用于匹配数据维度。 vsplit 垂直切分。 vstack 垂直堆叠。 where 根据一个是非条件,选择x的元素还是y的元素,拼接成新张量。看案例可瞬间明白。 scater_ scater是将input张量中的部分值进行替换。公式如下: self[index[i][j][k]][j][k] = src[i][j][k] # if dim == 0 self[i][index[i][j][k]][k] = src[i][j][k] # if dim == 1 self[i][j][index[i][j][k]] = src[i][j][k] # if dim == 2 设计两个核心问题: input哪个位置需要替换? 替换成什么? 答: 从公式可知道,依次从index中找到元素放到dim的位置,就是input需要变的地方。 变成什么呢? 从src中找,src中与index一样位置的那个元素值放到input中。 案例1: >>> src = torch.arange(1, 11).reshape((2, 5)) >>> src tensor([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10]]) >>> index = torch.tensor([[0, 1, 2, 0]]) >>> torch.zeros(3, 5, dtype=src.dtype).scatter_(0, index, src) tensor([[1, 0, 0, 4, 0], [0, 2, 0, 0, 0], [0, 0, 3, 0, 0]]) dim=0, 所以行号跟着index的元素走。其它跟index的索引走。 第一步:找到index的第一个元素index[0, 0]是0, 那么把src[0, 0](是1)放到input[0, 0]第二步:找到index的第二个元素index[0, 1]是1, 那么把src[0, 1](是2)放到input[1, 1]第三步:找到index的第三个元素index[0, 2]是2, 那么把src[0, 2](是3)放到input[2, 2]第四步:找到index的第四个元素index[0, 3]是0, 那么把src[0, 3](是4)放到input[0, 3] 案例2: >>> src = torch.arange(1, 11).reshape((2, 5)) >>> src tensor([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10]]) >>> index = torch.tensor([[0, 2, 4], [1, 2, 3]]) >>> index tensor([[0, 2, 4], [1, 2, 3]]) >>> torch.zeros(3, 5, dtype=src.dtype).scatter_(1, index, src) tensor([[1, 0, 2, 0, 3], [0, 6, 7, 8, 0], [0, 0, 0, 0, 0]]) dim=1:告诉input(零矩阵)的索引,沿着列进行索引,行根据index走。 index:2*3,告诉input(零矩阵),你的哪些行是要被替换的。 src:input要替换成什么呢?从src里找,怎么找?通过index的索引对应的找。 第一步:找到index的第一个元素index[0, 0]是0, 那么把src[0, 0](是1)放到input[0, 0]第二步:找到index的第二个元素index[0, 1]是2, 那么把src[0, 1](是2)放到input[0, 2]第三步:找到index的第三个元素index[0, 2]是4, 那么把src[0, 2](是3)放到input[0, 4]第四步:找到index的第四个元素index[1, 0]是1, 那么把src[1, 0](是6)放到input[1, 1]第五步:找到index的第五个元素index[1, 1]是2, 那么把src[1, 1](是7)放到input[1, 2]第六步:找到index的第六个元素index[1, 2]是3, 那么把src[1, 2](是8)放到input[1, 3] 这里可以看到 index的元素是决定input的哪个位置要变 变的值是从src上对应于index的索引上找。可以看到src的索引与index的索引保持一致的 案例3:one-hot的生成 >>> label = torch.arange(3).view(-1, 1) >>> label tensor([[0], [1], [2]]) >>> torch.zeros(3, 3).scatter_(1, label, 1) tensor([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]) 第一步:找到index的第一个元素index[0, 0]是0, 那么把src[0, 0](是1)放到input[0, 0] 第二步:找到index的第二个元素index[1, 0]是1, 那么把src[1, 0](是1)放到input[1, 1] 第三步:找到index的第三个元素index[2, 0]是2, 那么把src[2, 0](是1)放到input[2, 2] (one-hot的案例不利于理解scater函数,因为它的行和列是一样的。。。其实input[x, y] 中的x,y是有区别的,x是根据index走,y是根据index的元素值走的,而具体的值是根据src的值。) 张量的随机种子 随机种子(random seed)是编程语言中基础的概念,大多数编程语言都有随机种子的概念,它主要用于实验的复现。针对随机种子pytorch也有一些设置函数。 seed 获取一个随机的随机种子。Returns a 64 bit number used to seed the RNG. manual_seed 手动设置随机种子,建议设置为42,这是近期一个玄学研究。说42有效的提高模型精度。当然大家可以设置为你喜欢的,只要保持一致即可。 initial_seed 返回初始种子。 get_rng_state 获取随机数生成器状态。Returns the random number generator state as a torch.ByteTensor. set_rng_state 设定随机数生成器状态。这两怎么用暂时未知。Sets the random number generator state. 以上均是设置cpu上的张量随机种子,在cuda上是另外一套随机种子,如torch.cuda.manual_seed_all(seed), 这些到cuda模块再进行介绍,这里只需要知道cpu和cuda上需要分别设置随机种子。 张量的数学操作 张量还提供大量数学操作,估计了一下,有快一百个函数,这里就不再一一分析,只需要知道有哪几大类,用到的时候来查吧。 Pointwise Ops: 逐元素的操作,如abs, cos, sin, floor, floor_divide, pow等 Reduction Ops: 减少元素的操作,如argmax, argmin, all, any, mean, norm, var等 Comparison Ops:对比操作, 如ge, gt, le, lt, eq, argsort, isnan, topk, Spectral Ops: 谱操作,如短时傅里叶变换等各类信号处理的函数。 Other Operations:其它, clone, diag,flip等 BLAS and LAPACK Operations:BLAS(Basic Linear Algebra Subprograms)基础线性代数)操作。如, addmm, dot, inner, svd等。 小结 本节介绍了张量主要的操作函数,并归类到各个小结,这些仅是张量的部分操作,更多操作还请大家多多看官方文档。对于张量,主要是要理解2.3小节中张量的结构以及作用,对于它的操作就像numpy一样简单易用。 下一节就开始讲解pytorch的核心——autograd,autograd也是现代深度学习框架的核心,是实现自动微分的具体实现。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.5-computational-graphs.html":{"url":"chapter-2/2.5-computational-graphs.html","title":"2.5 自动求导核心——计算图","keywords":"","body":"2.5 计算图 前两小节对tensor进行了详细介绍,知道了tensor是pytorch的核心数据结构,各类数据均以tensor来表示,并且tensor类中有许多属性与求导/梯度有关,接下来我们将深入学习pytorch的自动求导模块——autograd。在autograd正式开始之前,需要了解一个重要概念——计算图(Computational Graphs)。 在学习自动求导系统之前,需要了解计算图的概念。计算图(Computational Graphs)是一种描述运算的“语言”,它由节点(Node)和边(Edge)构成。 根据官网介绍,节点表示数据和计算操作,边仅表示数据流向 Each node in this graph represents a particular computation or operation, and edges of this graph consist of references between nodes. A Node represents a particular computation or operation and is represented in Python using the torch.fx.Node class. Edges between nodes are represented as direct references to other nodes via the args property of the Node class. 更多关于计算图、节点和边的概念,可以阅读: https://medium.com/tebs-lab/deep-neural-networks-as-computational-graphs-867fcaa56c9 https://simple-english-machine-learning.readthedocs.io/en/latest/neural-networks/computational-graphs.html https://www.cs.cornell.edu/courses/cs5740/2017sp/lectures/04-nn-compgraph.pdf 节点表示数据,如标量,向量,矩阵,张量等 边表示运算,如加、减、乘、除、卷积、relu等; 记录所有节点和边的信息,可以方便地完成自动求导,假设有这么一个计算: y = (x+ w) * (w+1) 将每一步细化为: a = x + w b = w + 1 y = a * b 得到计算图如下: 有了计算图,我们可以尝试进行forward,带入x,w的输入数据,就得到结果y。 同样的,如果需要获取各参数的导数,也可以方便地获得。 计算图求导 假设我们要算y对w的导数,在计算图中要怎么做呢? 先来看w和y之间的关系,w会通过左边这条路走到y,也会通过右边这条路走到y,因此梯度也是一样的,会经过这两条路反馈回来。 所以y对w的偏导有两条路径,可以写成以下形式, ∂y/∂w = ∂y/∂a ∂a/∂w + ∂y/∂b ∂b/∂w,然后可以通过计算图依次求出。 如图所示: 这样我们得到 y对w的导数是5,我们可以拿纸和笔推一下,是否是一样的。 我们发现,所有的偏微分计算所需要用到的数据都是基于w和x的,这里,w和x就称为叶子结点。 叶子结点是最基础结点,其数据不是由运算生成的,因此是整个计算图的基石,是不可轻易”修改“的。而最终计算得到的y就是根节点,就像一棵树一样,叶子在上面,根在下面。 叶子结点 叶子结点是最基础的结点,其数据不是由运算生成的,因此是整个计算图的基石,是不可轻易”修改“的。而最终计算得到的y就是根节点,就像一棵树一样,叶子在上面,根在下面。 张量有一个属性是is_leaf, 就是用来指示一个张量是否为叶子结点的属性。 我们通过代码,实现以上运算,并查看该计算图的叶子结点和梯度。 import torch w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) # retain_grad() y = torch.mul(a, b) y.backward() print(w.grad) # 查看叶子结点 print(\"is_leaf:\\n\", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf) # 查看梯度 print(\"gradient:\\n\", w.grad, x.grad, a.grad, b.grad, y.grad) # 查看 grad_fn print(\"grad_fn:\\n\", w.grad_fn, x.grad_fn, a.grad_fn, b.grad_fn, y.grad_fn) tensor([5.]) is_leaf: True True False False False gradient: tensor([5.]) tensor([2.]) None None None grad_fn: None None 我们发现y就不是叶子结点了,因为它是由结点w和结点x通过乘法运算得到的。 补充知识点1:非叶子结点在梯度反向传播结束后释放 只有叶子节点的梯度得到保留,中间变量的梯度默认不保留;在pytorch中,非叶子结点的梯度在反向传播结束之后就会被释放掉,如果需要保留的话可以对该结点设置retain_grad() 补充知识点2:grad_fn是用来记录创建张量时所用到的运算,在链式求导法则中会使用到。 思考一下y对w求导的过程,我们知道只要记录下计算图中的结点(数据)和边(运算),就可以通过链式法则轻易的求取梯度。 所以在pytorch中,自动微分的关键就是记录数据和该结点的运算。回想一下张量的结构当中其实就记录了这两个重要的东西。 在张量中,数据对应着data,结点的运算对应着grad_fn,大家现在应该明白为什么结点的运算叫grad_fn而不叫fn了吧,因为这个运算是在求梯度的时候使用的。 静态图与动态图 以上就是计算图的简单介绍。计算图根据计算图的搭建方式可以划分为静态图和动态图。 pytorch是典型的动态图,TensorFlow是静态图(TF 2.x 也支持动态图模式)。 动态图和静态图的搭建方式有何不同,如何判断和区分? 第一种判断:这就要看运算,是在计算图搭建之后,还是两者同步进行 先搭建计算图,再运算,这就是静态图机制。 而在运算的同时去搭建计算图,这就是动态图机制。 第二种判断:也可以通过判断运算过程中,计算图是否可变动来区分静态图与动态图。 在运算过程中,计算图可变动的是动态图;计算图不可变,是静止的,就是静态图。 下面来看两个示意图。 图1为pytorch的动态图示意,图2为TensorFlow的静态图示意。 动态图优点: 易理解:程序按照编写命令的顺序进行执行 灵活性:可依据模型运算结果来决定计算图 静态图优点: 高效性:优化计算图,提高运算效率(但在gpu时代,这一点对于初学者而言可忽略不计) 缺点: 晦涩性:需要学习 seesion, placeholder等概念,调试困难 以上是关于计算图概念的介绍,下一小节将详细剖析autograd机制及其常用的功能函数,请注意,下一节内容也非常丰富,可能需要多次阅读以充分理解。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.6-autograd.html":{"url":"chapter-2/2.6-autograd.html","title":"2.6 Autograd——自动微分","keywords":"","body":"2.6 Autograd 了解计算图后,我们可以开始学习autograd。这里再次回顾pytorch官网的一张示意图 在进行h2h、i2h、next_h、loss的计算过程中,逐步搭建计算图,同时针对每一个变量(tensor)都存储计算梯度所必备的grad_fn,便于自动求导系统使用。当计算到根节点后,在根节点调用.backward()函数,即可自动反向传播计算计算图中所有节点的梯度。这就是pytorch自动求导机制,其中涉及张量类、计算图、grad_fn、链式求导法则等基础概念,大家可以自行补充学习。 autograd 官方定义 来看看官方文档中对autograd的解释: Conceptually, autograd keeps a record of data (tensors) and all executed operations (along with the resulting new tensors) in a directed acyclic graph (DAG) consisting of Function objects. In this DAG, leaves are the input tensors, roots are the output tensors. By tracing this graph from roots to leaves, you can automatically compute the gradients using the chain rule. In a forward pass, autograd does two things simultaneously: run the requested operation to compute a resulting tensor maintain the operation’s gradient function in the DAG. The backward pass kicks off when .backward() is called on the DAG root. autograd then: computes the gradients from each .grad_fn, accumulates them in the respective tensor’s .grad attribute using the chain rule, propagates all the way to the leaf tensors. from: https://pytorch.org/tutorials/beginner/basics/autogradqs_tutorial.html#more-on-computational-graphs 划重点: 自动求导机制通过有向无环图(directed acyclic graph ,DAG)实现 在DAG中,记录数据(对应tensor.data)以及操作(对应tensor.grad_fn) 操作在pytorch中统称为Function,如加法、减法、乘法、ReLU、conv、Pooling等,统统是Function autograd 的使用 autograd的使用有很多方法,这里重点讲解一下三个,并在最后汇总一些知识点。更多API推荐阅读官方文档 torch.autograd.backward torch.autograd.grad torch.autograd.Function torch.autograd.backward backward函数是使用频率最高的自动求导函数,没有之一。99%的训练代码中都会用它进行梯度求导,然后更新权重。 使用方法可以参考第二章第二节-新冠肺炎分类的代码,loss.backward()就可以完成计算图中所有张量的梯度求解。 虽然绝大多数都是直接使用,但是backward()里边还有一些高级参数,值得了解。 torch.autograd.backward(tensors, grad_tensors=None, retain_graph=None, create_graph=False, grad_variables=None, inputs=None) tensors (Sequence[Tensor] or Tensor) – 用于求导的张量。如上例的loss。 grad_tensors (Sequence[Tensor or None] or Tensor, optional) – 雅克比向量积中使用,详细作用请看代码演示。 retain_graph (bool, optional) – 是否需要保留计算图。pytorch的机制是在方向传播结束时,计算图释放以节省内存。大家可以尝试连续使用loss.backward(),就会报错。如果需要多次求导,则在执行backward()时,retain_graph=True。 create_graph (bool, optional) – 是否创建计算图,用于高阶求导。 inputs (Sequence[Tensor] or Tensor, optional) – Inputs w.r.t. which the gradient be will accumulated into .grad. All other Tensors will be ignored. If not provided, the gradient is accumulated into all the leaf Tensors that were used to compute the attr::tensors. 补充说明:我们使用时候都是在张量上直接调用.backward()函数,但这里却是torch.autograd.backward,为什么不一样呢? 其实Tensor.backward()接口内部调用了autograd.backward。 请看使用示例 retain_grad参数使用 对比两个代码段,仔细阅读pytorch报错信息。 ##### retain_graph=True import torch w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) y.backward(retain_graph=True) print(w.grad) y.backward() print(w.grad) tensor([5.]) tensor([10.]) 运行上面代码段可以看到是正常的,下面这个代码段就会报错,报错信息提示非常明确:Trying to backward through the graph a second time。并且还给出了解决方法: Specify retain_graph=True if you need to backward through the graph a second time 。这也是pytorch代码写得好的地方,出现错误不要慌,仔细看看报错信息,里边可能会有解决问题的方法。 ##### retain_graph=False import torch w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) y.backward() print(w.grad) y.backward() print(w.grad) tensor([5.]) --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) in 10 y.backward() 11 print(w.grad) ---> 12 y.backward() 13 print(w.grad) D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\lib\\site-packages\\torch\\_tensor.py in backward(self, gradient, retain_graph, create_graph, inputs) 305 create_graph=create_graph, 306 inputs=inputs) --> 307 torch.autograd.backward(self, gradient, retain_graph, create_graph, inputs=inputs) 308 309 def register_hook(self, hook): D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\lib\\site-packages\\torch\\autograd\\__init__.py in backward(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs) 154 Variable._execution_engine.run_backward( 155 tensors, grad_tensors_, retain_graph, create_graph, inputs, --> 156 allow_unreachable=True, accumulate_grad=True) # allow_unreachable flag 157 158 RuntimeError: Trying to backward through the graph a second time (or directly access saved tensors after they have already been freed). Saved intermediate values of the graph are freed when you call .backward() or autograd.grad(). Specify retain_graph=True if you need to backward through the graph a second time or if you need to access saved tensors after calling backward. grad_tensors使用 w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y0 = torch.mul(a, b) # y0 = (x+w) * (w+1) dy0/dw = 2w + x + 1 y1 = torch.add(a, b) # y1 = (x+w) + (w+1) dy1/dw = 2 loss = torch.cat([y0, y1], dim=0) # [y0, y1] grad_tensors = torch.tensor([1., 2.]) loss.backward(gradient=grad_tensors) # Tensor.backward中的 gradient 传入 torch.autograd.backward()中的grad_tensors # w = 1* (dy0/dw) + 2*(dy1/dw) # w = 1* (2w + x + 1) + 2*(w) # w = 1* (5) + 2*(2) # w = 9 print(w.grad) tensor([9.]) torch.autograd.grad torch.autograd.grad(outputs, inputs, grad_outputs=None, retain_graph=None, create_graph=False, only_inputs=True, allow_unused=False) 功能:计算outputs对inputs的导数 主要参数: outputs (sequence of Tensor) – 用于求导的张量,如loss inputs (sequence of Tensor) – 所要计算导数的张量 grad_outputs (sequence of Tensor) – 雅克比向量积中使用。 retain_graph (bool, optional) – 是否需要保留计算图。pytorch的机制是在方向传播结束时,计算图释放以节省内存。大家可以尝试连续使用loss.backward(),就会报错。如果需要多次求导,则在执行backward()时,retain_graph=True。 create_graph (bool, optional) – 是否创建计算图,用于高阶求导。 allow_unused (bool, optional) – 是否需要指示,计算梯度时未使用的张量是错误的。 此函数使用上比较简单,请看案例: import torch x = torch.tensor([3.], requires_grad=True) y = torch.pow(x, 2) # y = x**2 # 一阶导数 grad_1 = torch.autograd.grad(y, x, create_graph=True) # grad_1 = dy/dx = 2x = 2 * 3 = 6 print(grad_1) # 二阶导数 grad_2 = torch.autograd.grad(grad_1[0], x) # grad_2 = d(dy/dx)/dx = d(2x)/dx = 2 print(grad_2) (tensor([6.], grad_fn=),) (tensor([2.]),) torch.autograd.Function 有的时候,想要实现自己的一些操作(op),如特殊的数学函数、pytorch的module中没有的网络层,那就需要自己写一个Function,在Function中定义好forward的计算公式、backward的计算公式,然后将这些op组合到模型中,模型就可以用autograd完成梯度求取。 这个概念还是很抽象,平时用得不多,但是自己想要自定义网络时,常常需要自己写op,那么它就很好用了,为了让大家掌握自定义op——Function的写法,特地从多处收集了四个案例,大家多运行代码体会Function如何写。 案例1: exp 案例1:来自 https://pytorch.org/docs/stable/autograd.html#function 假设需要一个计算指数的功能,并且能组合到模型中,实现autograd,那么可以这样实现 第一步:继承Function第二步:实现forward第三步:实现backward 注意事项: forward和backward函数第一个参数为ctx,它的作用类似于类函数的self一样,更详细解释可参考如下: In the forward pass we receive a Tensor containing the input and return a Tensor containing the output. ctx is a context object that can be used to stash information for backward computation. You can cache arbitrary objects for use in the backward pass using the ctx.save_for_backward method. backward函数返回的参数个数与forward的输入参数个数相同, 即,传入该op的参数,都需要给它们计算对应的梯度。 import torch from torch.autograd.function import Function class Exp(Function): @staticmethod def forward(ctx, i): # ============== step1: 函数功能实现 ============== result = i.exp() # ============== step1: 函数功能实现 ============== # ============== step2: 结果保存,用于反向传播 ============== ctx.save_for_backward(result) # ============== step2: 结果保存,用于反向传播 ============== return result @staticmethod def backward(ctx, grad_output): # ============== step1: 取出结果,用于反向传播 ============== result, = ctx.saved_tensors # ============== step1: 取出结果,用于反向传播 ============== # ============== step2: 反向传播公式实现 ============== grad_results = grad_output * result # ============== step2: 反向传播公式实现 ============== return grad_results x = torch.tensor([1.], requires_grad=True) y = Exp.apply(x) # 需要使用apply方法调用自定义autograd function print(y) # y = e^x = e^1 = 2.7183 y.backward() print(x.grad) # 反传梯度, x.grad = dy/dx = e^x = e^1 = 2.7183 # 关于本例子更详细解释,推荐阅读 https://zhuanlan.zhihu.com/p/321449610 tensor([2.7183], grad_fn=) tensor([2.7183]) 从代码里可以看到,y这个张量的 grad_fn 是 ExpBackward,正是我们自己实现的函数,这表明当y求梯度时,会调用ExpBackward这个函数进行计算这也是张量的grad_fn的作用所在 案例2:为梯度乘以一定系数 Gradcoeff 案例2来自: https://zhuanlan.zhihu.com/p/321449610 功能是反向传梯度时乘以一个自定义系数 class GradCoeff(Function): @staticmethod def forward(ctx, x, coeff): # ============== step1: 函数功能实现 ============== ctx.coeff = coeff # 将coeff存为ctx的成员变量 x.view_as(x) # ============== step1: 函数功能实现 ============== return x @staticmethod def backward(ctx, grad_output): return ctx.coeff * grad_output, None # backward的输出个数,应与forward的输入个数相同,此处coeff不需要梯度,因此返回None # 尝试使用 x = torch.tensor([2.], requires_grad=True) ret = GradCoeff.apply(x, -0.1) # 前向需要同时提供x及coeff,设置coeff为-0.1 ret = ret ** 2 print(ret) # 注意看: ret.grad_fn ret.backward() print(x.grad) tensor([4.], grad_fn=) tensor([-0.4000]) 在这里需要注意 backward函数返回的参数个数与forward的输入参数个数相同即,传入该op的参数,都需要给它们计算对应的梯度。 案例3:勒让德多项式 案例来自:https://github.com/excelkks/blog 假设多项式为: y=a+bx+cx2+dx3 y = a+bx+cx^2+dx^3 y=a+bx+cx​2​​+dx​3​​ 时,用两步替代该过程 y=a+b×P3(c+dx),P3(x)=12(5x3−3x) y= a+b\\times P_3(c+dx), P_3(x) = \\frac{1}{2}(5x^3-3x) y=a+b×P​3​​(c+dx),P​3​​(x)=​2​​1​​(5x​3​​−3x) import torch import math from torch.autograd.function import Function class LegendrePolynomial3(Function): @staticmethod def forward(ctx, x): \"\"\" In the forward pass we receive a Tensor containing the input and return a Tensor containing the output. ctx is a context object that can be used to stash information for backward computation. You can cache arbitrary objects for use in the backward pass using the ctx.save_for_backward method. \"\"\" y = 0.5 * (5 * x ** 3 - 3 * x) ctx.save_for_backward(x) return y @staticmethod def backward(ctx, grad_output): \"\"\" In the backward pass we receive a Tensor containing the gradient of the loss with respect to the output, and we need to compute the gradient of the loss with respect to the input. \"\"\" ret, = ctx.saved_tensors return grad_output * 1.5 * (5 * ret ** 2 - 1) a, b, c, d = 1, 2, 1, 2 x = 1 P3 = LegendrePolynomial3.apply y_pred = a + b * P3(c + d * x) print(y_pred) 127.0 案例4:手动实现2D卷积 案例来自:https://pytorch.org/tutorials/intermediate/custom_function_conv_bn_tutorial.html案例本是卷积与BN的融合实现,此处仅观察Function的使用,更详细的内容,十分推荐阅读原文章下面看如何实现conv_2d的 import torch from torch.autograd.function import once_differentiable import torch.nn.functional as F def convolution_backward(grad_out, X, weight): \"\"\" 将反向传播功能用函数包装起来,返回的参数个数与forward接收的参数个数保持一致,为2个 \"\"\" grad_input = F.conv2d(X.transpose(0, 1), grad_out.transpose(0, 1)).transpose(0, 1) grad_X = F.conv_transpose2d(grad_out, weight) return grad_X, grad_input class MyConv2D(torch.autograd.Function): @staticmethod def forward(ctx, X, weight): ctx.save_for_backward(X, weight) # ============== step1: 函数功能实现 ============== ret = F.conv2d(X, weight) # ============== step1: 函数功能实现 ============== return ret @staticmethod def backward(ctx, grad_out): X, weight = ctx.saved_tensors return convolution_backward(grad_out, X, weight) weight = torch.rand(5, 3, 3, 3, requires_grad=True, dtype=torch.double) X = torch.rand(10, 3, 7, 7, requires_grad=True, dtype=torch.double) # gradcheck 会检查你实现的自定义操作的前向传播 (forward) 和反向传播 (backward) 方法是否正确计算了梯度。 # 如果返回 True,则表示梯度检查通过,即自定义操作的梯度计算与数值近似梯度之间的一致性在允许的误差范围内; # 如果返回 False,则说明存在不匹配,需要检查和修正自定义操作的反向传播逻辑。 print(\"梯度检查: \", torch.autograd.gradcheck(MyConv2D.apply, (X, weight))) # gradcheck 功能请自行了解,通常写完Function会用它检查一下 y = MyConv2D.apply(X, weight) label = torch.randn_like(y) loss = F.mse_loss(y, label) print(\"反向传播前,weight.grad: \", weight.grad) loss.backward() print(\"反向传播后,weight.grad: \", weight.grad) 梯度检查: True 反向传播前,weight.grad: None 反向传播后,weight.grad: tensor([[[[1.3423, 1.3445, 1.3271], [1.3008, 1.3262, 1.2493], [1.2969, 1.3269, 1.2369]], ...... [[1.2302, 1.2134, 1.2619], [1.2397, 1.2048, 1.2447], [1.2419, 1.2548, 1.2647]]]], dtype=torch.float64) ​ autograd相关的知识点 autograd使用过程中还有很多需要注意的地方,在这里做个小汇总。 知识点一:梯度不会自动清零 知识点二: 依赖于叶子结点的结点,requires_grad默认为True 知识点三: 叶子结点不可执行in-place 知识点四: detach 的作用 知识点五: with torch.no_grad()的作用 知识点一:梯度不会自动清零 import torch w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) for i in range(4): a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) y.backward() print(w.grad) # 梯度不会自动清零,数据会累加, 通常需要采用 optimizer.zero_grad() 完成对参数的梯度清零 # w.grad.zero_() tensor([5.]) tensor([10.]) tensor([15.]) tensor([20.]) 知识点二:依赖于叶子结点的结点,requires_grad默认为True 结点的运算依赖于叶子结点的话,它一定是要计算梯度的,因为叶子结点梯度的计算是从后向前传播的,因此与其相关的结点均需要计算梯度,这点还是很好理解的。 import torch w = torch.tensor([1.], requires_grad=True) # x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) print(a.requires_grad, b.requires_grad, y.requires_grad) print(a.is_leaf, b.is_leaf, y.is_leaf) True True True False False False 知识点三:叶子张量不可以执行in-place操作 叶子结点不可执行in-place,因为计算图的backward过程都依赖于叶子结点的计算,可以回顾计算图当中的例子,所有的偏微分计算所需要用到的数据都是基于w和x(叶子结点),因此叶子结点不允许in-place操作。 a = torch.ones((1, )) print(id(a), a) a = a + torch.ones((1, )) print(id(a), a) a += torch.ones((1, )) print(id(a), a) 2361561191752 tensor([1.]) 2362180999432 tensor([2.]) 2362180999432 tensor([3.]) w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) w.add_(1) y.backward() --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) in 6 y = torch.mul(a, b) 7 ----> 8 w.add_(1) 9 10 y.backward() RuntimeError: a leaf Variable that requires grad is being used in an in-place operation. 知识点四:detach 的作用 通过以上知识,我们知道计算图中的张量是不能随便修改的,否则会造成计算图的backward计算错误,那有没有其他方法能修改呢?当然有,那就是detach() detach的作用是:从计算图中剥离出“数据”,并以一个新张量的形式返回,并且新张量与旧张量共享数据,简单的可理解为做了一个别名。 请看下例的w,detach后对w_detach修改数据,w同步地被改为了999 w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) y.backward() w_detach = w.detach() w_detach.data[0] = 999 print(w) tensor([999.], requires_grad=True) 知识点五:with torch.no_grad()的作用 autograd自动构建计算图过程中会保存一系列中间变量,以便于backward的计算,这就必然需要花费额外的内存和时间。而并不是所有情况下都需要backward,例如推理的时候,因此可以采用上下文管理器——torch.no_grad()来管理上下文,让pytorch不记录相应的变量,以加快速度和节省空间。详见:https://pytorch.org/docs/stable/generated/torch.no_grad.html?highlight=no_grad#torch.no_grad 小结 本章终于结束,本章目的是为大家介绍pytorch的核心模块,包括pytorch代码库结构,以便于今后阅读源码,知道从哪里找代码;包括第一个分类模型训练,便于大家理解模型训练过程;包括核心数据结构——张量,便于理解整个pytorch的数据;包括计算图与autograd,便于大家熟悉自动微分的过程及自定义op的方法。 下一章将通过借助covid-19任务,详细介绍pytorch的数据读取机制,以及各种数据形式的读取,包括csv形式、txt形式、杂乱文件夹形式等一切关于数据读取、加载、操作的模块都将涉及。 小记:动笔一个多月,才写了两章,尤其autograd和tensor写了大半个月,希望后面能有更多时间精力早日完成,加油!2022年1月18日 ​ ​ Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/":{"url":"chapter-3/","title":"第三章 PyTorch 数据模块","keywords":"","body":"第三章 PyTorch 数据模块 第三章 PyTorch 数据模块 3.1 Dataset 3.2 DataLoader 3.3 Dataset及常用API 3.4 transforms 3.5 torchvision 经典dataset学习 第三章简介 经过前两章的铺垫,本章终于可以讲讲项目代码中重要的模块——数据模块。 数据模块包括哪些内容呢?相信大家多少会有一些感觉,不过最好结合具体任务来剖析数据模块。 我们回顾2.2中的COVID-19分类任务,观察一下数据是如何从硬盘到模型输入的。 我们倒着推, 模型接收的训练数据是 data:outputs = model(data) data来自train_loader: for data, labels in train_loader: train_loader 来自 DataLoader与train_data:train_loader = DataLoader(dataset=train_data, batch_size=2) train_data 来自 COVID19Dataset:train_data = COVID19Dataset(root_dir=img_dir, txt_path=path_txt_train, transform=transforms_func) COVID19Dataset继承于Dataset:COVID19Dataset(Dataset) 至此,知道整个数据处理过程会涉及pytorch的两个核心——Dataset, DataLoader。 Dataset是一个抽象基类,提供给用户定义自己的数据读取方式,最核心在于getitem中间对数据的处理。 DataLoader是pytorch数据加载的核心,其中包括多个功能,如打乱数据,采样机制(实现均衡1:1采样),多进程数据加载,组装成Batch形式等丰富的功能。 本章将围绕着它们两个展开介绍pytorch的数据读取、预处理、加载等功能。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.1-dataset.html":{"url":"chapter-3/3.1-dataset.html","title":"3.1 Dataset","keywords":"","body":"3.1 torch.utils.data.Dataset 数据交互模块——Dataset 虽然说pytorch数据模块的核心是DataLoader,但是对于使用者而言,改动最多的、与源数据最接近的是Dataset, 本小节就详细分析Dataset的作用,并通过三个案例学习如何编写自定义Dataset来读取自己的数据集。 Dataset的功能 pytorch提供的torch.utils.data.Dataset类是一个抽象基类An abstract class representing a Dataset,供用户继承,编写自己的dataset,实现对数据的读取。在Dataset类的编写中必须要实现的两个函数是__getitem__和__len__(由于markdown语法问题,后续双下划线就省略了)。 getitem:需要实现读取一个样本的功能。通常是传入索引(index,可以是序号或key),然后实现从磁盘中读取数据,并进行预处理(包括online的数据增强),然后返回一个样本的数据。数据可以是包括模型需要的输入、标签,也可以是其他元信息,例如图片的路径。getitem返回的数据会在dataloader中组装成一个batch。即,通常情况下是在dataloader中调用Dataset的getitem函数获取一个样本。 len:返回数据集的大小,数据集的大小也是个最要的信息,它在dataloader中也会用到。如果这个函数返回的是0,dataloader会报错:\"ValueError: num_samples should be a positive integer value, but got num_samples=0\" 这个报错相信大家经常会遇到,这通常是文件路径没写对,导致你的dataset找不到数据,数据个数为0。 了解Dataset类的概念,下面通过一幅示意图,来理解Dataset与DataLoader的关系。 dataset负责与磁盘打交道,将磁盘上的数据读取并预处理好,提供给DataLoader,而DataLoader只需要关心如何组装成批数据,以及如何采样。采样的体现是出现在传入getitem函数的索引,这里采样的规则可以通过sampler由用户自定义,可以方便地实现均衡采样、随机采样、有偏采样、渐进式采样等,这个留在DataLoader中会详细展开。 此处,先分析Dataset如何与磁盘构建联系。 从2.2节的例子中可以看到,我们为COVID19Dataset定义了一个_get_img_info函数,该函数就是用来建立磁盘关系的,在这个函数中收集并处理样本的路径信息、标签信息,存储到一个list中,供getitem函数使用。getitem函数只需要拿到序号,就可获得图片的路径信息、标签信息,接着进行图片预处理,最后返回一个样本信息。 希望大家体会_get_img_info函数的作用,对于各种不同的数据形式,都可以用这个模板实现Dataset的构建,只需将数据信息(路径、标签等)收集并存储至列表中,供__getitem__函数使用”。 三个Dataset案例 相信大家在做自己的任务时,遇到的第一个问题就是,怎么把自己的数据放到github的模型上跑起来。很多朋友通常会把自己的数据调整为与现成项目数据一模一样的数据形式,然后执行相关代码。这样虽然快捷,但缺少灵活性。 为了让大家能掌握各类数据形式的读取,这里构建三个不同的数据形式进行编写Dataset。 第一个:2.2中的类型。数据的划分及标签在txt中。 第二个:数据的划分及标签在文件夹中体现 第三个:数据的划分及标签在csv中 详情请结合 配套代码,深刻理解_get_img_info及Dataset做了什么。 代码输出主要有两部分, 第一部分是两种dataset的getitem输出。 第二部分是结合DataLoader进行数据加载。 先看第一部分,输出的是 PIL对象及图像标签,这里可以进入getitem函数看到采用了 img = Image.open(path_img).convert('L') 对图片进行了读取,得到了PIL对象,由于transform为None,不对图像进行任何预处理,因此getitem函数返回的图像是PIL对象。 2 (, 1) 2 (, 1) 第二部分是结合DataLoader的使用,这种形式更贴近真实场景,在这里为Dataset设置了一些transform,有图像的缩放,ToTensor, normalize三个方法。因此,getitem返回的图像变为了张量的形式,并且在DataLoader中组装成了batchsize的形式。大家可以尝试修改缩放的大小来观察输出,也可以注释normalize来观察它们的作用。 0 torch.Size([2, 1, 4, 4]) tensor([[[[-0.0431, -0.1216, -0.0980, -0.1373], [-0.0667, -0.2000, -0.0824, -0.2392], [-0.1137, 0.0353, 0.1843, -0.2078], [ 0.0510, 0.3255, 0.3490, -0.0510]]], [[[-0.3569, -0.2863, -0.3333, -0.4118], [ 0.0196, -0.3098, -0.2941, 0.1059], [-0.2392, -0.1294, 0.0510, -0.2314], [-0.1059, 0.4118, 0.4667, 0.0275]]]]) torch.Size([2]) tensor([1, 0]) 关于transform的系列方法以及工作原理,将在本章后半部分讲解数据增强部分再详细展开。 小结 本小结介绍了torch.utils.data.Dataset类的结构及工作原理,并通过三个案例实践,加深大家对自行编写Dataset的认识,关于Dataset的编写,torchvision也有很多常用公开数据集的Dataset模板,建议大家学习,本章后半部分也会挑选几个Dataset进行分析。下一小节将介绍DataLoader类的使用。 补充学习建议 IDE的debug: 下一小节的代码将采用debug模式进行逐步分析,建议大家提前熟悉pycharm等IDE的debug功能。 python的迭代器:相信很多初学者对代码中的“next(iter(train_set))”不太了解,这里建议大家了解iter概念、next概念、迭代器概念、以及双下划线函数概念。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.2-dataloader.html":{"url":"chapter-3/3.2-dataloader.html","title":"3.2 DataLoader","keywords":"","body":"3.2 DataLoader dataloader简介 按照上图的顺序,本小节就来到pytorch数据加载最核心模块——DataLoader。 torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0, collate_fn=None, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None, multiprocessing_context=None, generator=None, *, prefetch_factor=2, persistent_workers=False) 从以上可以看到,DataLoader类有14个变量,因此成为最核心模块,一点不为过。 DataLoader功能繁多,这里根据官方文档可总结为以下五大功能: 支持两种形式数据集读取:map-style and iterable-style datasets, 自定义采样策略:customizing data loading order, 自动组装成批数据:automatic batching, 多进程数据加载:single- and multi-process data loading, 自动实现锁页内存(Pinning Memory):automatic memory pinning. 支持两种形式数据集读取 两种形式的数据集分别是映射式(Map-style)与迭代式(Iterable-style),在3.1小结中讲解的Dataset类就是映射式,因为它(getitem)提供了序号到数据的映射。迭代式则是编写一个可迭代对象,从中依次获取数据,此处不详细展开,感兴趣可以了解IterableDataset 注:往后不做特别说明,Dataset均表示映射式Dataset。 自定义采样策略 DataLoader可借助Sampler自定义采样策略,包括为每个类别设置采样权重以实现1:1的均衡采样,或者是自定义采样策略,关于Sampler会在后面小结详细展开,它是一个涨点神奇。 自动组装成批数据 mini-batch形式的训练成为了深度学习的标配,如何把数据组装成一个batch数据?DataLoader内部自动实现了该功能,并且可以通过batch_sampler、collate_fn来自定义组装的策略,十分灵活。 多进程数据加载 通常GPU运算消耗数据会比CPU读取加载数据要快,CPU“生产”跟不上GPU“消费”,因此需要多进程进行加载数据,以满足GPU的消费需求。通常指要设置num_workers 为CPU核心数,如16核的CPU就设置为16。 自动实现锁页内存(Pinning Memory) 锁页内存的概念通常在操作系统课程里才会涉及,非CS的同学可能不太了解,感兴趣的可以去了解一下。Pinning Memory是空间换时间的做法,将指定的数据“锁”住,不会被系统移动(交换)到磁盘中的虚拟内存,因此可以加快数据的读取速率。简单的可以理解为常用的衣服就“锁”在你的衣柜里,某些时候(如夏天),暂时不用的衣服——冬季大衣,则会移动到收纳柜里,以腾出空间放其它常用的衣服,等到冬天来临,需要用到大衣的时候,再从收纳柜里把大衣放到衣柜中。但是冬天拿大衣的时候就会慢一些,如果把它“锁”在你的衣柜,那么冬天获取它的时候自然快了,但占用了你的空间。这就是空间换时间的一个例子。这里的“锁”就是固定的意思,大家可补充学习一下OS的内容。 DataLoader API DataLoader提供了丰富的功能,下面介绍常用的功能,高阶功能等到具体项目中再进行分析。 dataset:它是一个Dataset实例,要能实现从索引(indices/keys)到样本的映射。(即getitem函数) batch_size:每个batch的样本量 shuffle:是否对打乱样本顺序。训练集通常要打乱它!验证集和测试集无所谓。 sampler:设置采样策略。后面会详细展开。 batch_sampler:设置采样策略, batch_sampler与sampler二选一,具体选中规则后面代码会体现。 num_workers: 设置多少个子进程进行数据加载(data loading) collate_fn:组装数据的规则, 决定如何将一批数据组装起来。 pin_memory:是否使用锁页内存,具体行为是“the data loader will copy Tensors into CUDA pinned memory before returning them” drop_last:每个epoch是否放弃最后一批不足batchsize大小的数据,即无法被batchsize整除时,最后会有一小批数据,是否进行训练,如果数据量足够多,通常设置为True。这样使模型训练更为稳定,大家千万不要理解为某些数据被舍弃了,因为每个epoch,dataloader的采样都会重新shuffle,因此不会存在某些数据被真正的丢弃。 下面通过配套代码加深dataloader的理解,并且观察DataLoader 与 Dataset是如何配合使用的。 运行代码,可看到输出如下信息: 0 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([1, 0]) 1 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([0, 1]) 2 torch.Size([1, 3, 224, 224]) torch.Size([1]) tensor([0]) 0 torch.Size([3, 3, 224, 224]) torch.Size([3]) tensor([0, 0, 1]) 1 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([1, 0]) 0 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([0, 0]) 1 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([0, 1]) 这里主要观察batch_size和drop_last的作用,以及图片组装成batch之后的shape。 这里构建一个数据量为5的dataset,这样可以采用batchsize=2和3来观察drop_last的作用。 dataloader内部代码 下一步,我们将采用debug模式,深入dataloader内部,观察它是如何进行采样的,如何调用dataset的getitem获取数据,如何组装一个batch的。这里我们仅观察单进程模式,因此大家的num_workers注意设置为0。 首先在for i, (inputs, target) in enumerate(train_loader_bs2) 设置一个断点,然后debug模式运行代码,接着持续采用 Step Into方式运行代码,下面就列出依次会进入的代码: 第一步:初始化dataloader迭代器 for i, (inputs, target) in enumerate(train_loader_bs2) DataLoader的iter() DataLoader的_get_iterator() SingleProcessDataLoaderIter的_init。 注:至此,仅完成了DataLoader的初始化,需要再一次进入dataloader才开始读取数据。 第二步:依次循环该迭代器 来到 BaseDataLoaderIter的_next:进入521行:data = self._next_data() 来到 _SingleProcessDataLoaderIter的_next_data:此函数调用了两个重要功能,第一个获取一个batch的索引,第二个获取此batch的数据。下面一个一个来看。 进入 _SingleProcessDataLoaderIter的_next_data:进入560行, index = self._next_index() 来到 _BaseDataLoaderIter的_next_index(): 这里是对sampler的包装,调用sampler获取一批索引,进入512行 来到BatchSampler的iter():函数中有yield,这是一个迭代器,从这里可以看到sampler是如何工作的。默认情况下,这里用的是RandomSampler, 它会实现采样的顺序及频率。在本函数中,对self.sampler依次迭代,拿到足够一个batchsize的索引时,就yield。 回到 _SingleProcessDataLoaderIter的_next_data:第561行,经过index = self._next_index() ,已经获得一个batch所对应的index,接着进入self._dataset_fetcher.fetch(index) 来到 _MapDatasetFetcher的fetch:mapdataset就是前面讲到的map-style dataset。看到第49行,是一个列表生成式,在这里,调用了我们自己写的dataset,继续进入。 来到 AntsBeesDataset的getitem:进入到这里,大家就豁然开朗了吧,知道dataset是如何被dataloader使用的。下面,直接跳出去,回到 fetch看看如何组装的。 来到 _MapDatasetFetcher的fetch:第52行self.collate_fn(data), 这里采用collate_fn对数据进行组装,继续进入。 来到 collate.py的default_collate():这是pytorch默认的组装函数,值得大家认真学习。这个函数通常是一个递归函数,第一次进入时可以发现会来到第84行:return [default_collate(samples) for samples in transposed]。会依次再进入一次default_collate()。 这里的逻辑是这样的: 首先将dataset返回的一系列数据解包再zip,为的是将相同数据放到一起。即getitem的return返回有img和label,这里就是为了将多个img放到一起,多个label放到一起,当然大家可以在getitem返回其它有用信息(例如图片的路径)。 接着再次进入default_collate函数时,会对实际的数据进行组装。例如img的数据会进入if isinstance(elem, torch.Tensor),然后会看到img数据是这样被组装起来的:torch.stack(batch, 0, out=out),因此可知一个batch的图像数据第一个维度是B,整体是BCH*W。 至此,一个batch数据已经读取、加载完毕,依次跳出函数,可回到for i, (inputs, target) in enumerate(train_loader_bs2)。 这个时候,再观察inputs, target,一定会更清晰了,知道它们是如何从硬盘到模型需要的形式。并且通过上述debug过程,我们可以知道sampler的机制、collate_fn的机制,方便今后进行高级的改造。希望大家一定要debug几遍上述过程,并且记录。 小结 以上就是关于DataLoader的概念的介绍,通过两个小节相信大家对数据读取有了初步认识,可pytorch的数据处理远不止于此,它还提供了很多使用的方法,例如数据集的拼接,数据集的截取,数据的划分等,想了解怎么使用,请接着往下看。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.3-dataset-useful-api.html":{"url":"chapter-3/3.3-dataset-useful-api.html","title":"3.3 Dataset及常用API","keywords":"","body":"3.3 系列APIs API*022-1月至4月完全没有更新,这三个月发生的事情太多了,无论如何,过去的终将过去,继续学习PyTorch与人工智能吧,加油! 前几个小节已经把pytorch的数据读取、加载、预处理机制和逻辑关系理清楚了,下面讲一下实用的APIs,包括数据集的拼接、截取、划分,以及十分重要的采样策略——sampler。 concat 在实际项目中,数据的来源往往是多源的,可能是多个中心收集的,也可能来自多个时间段的收集,很难将可用数据统一到一个数据形式。通常有两种做法,一种是固定一个数据形式,所有获取到的数据经过整理,变为统一格式,然后用一个Dataset即可读取。还有一种更为灵活的方式是为每批数据编写一个Dataset,然后使用torch.utils.data.ConcatDataset类将他们拼接起来,这种方法可以灵活的处理多源数据,也可以很好的使用别人的数据及Dataset。 下面还是来看COVID-19的例子,大家知道想要获取大量的COVID-19数据,肯定是多源的,不同国家、不同机构、不同时间的X光片收集过来之后,如何把他们整理起来供模型训练呢?先看这个github仓库covid-chestxray-dataset,他们采取了以下方法,将采集到的数据统一整理,并生成metadata(元信息)。基于现成的Dataset,我们可通过拼接的方法将所有数据拼接成一个大的dataset进行使用。 请结合代码阅读,在2.2与3.2中分别实现了COVID19Dataset、COVID19Dataset2、COVID19Dataset3,假设在项目开始时拿到了COVID19Dataset,做了一段时间来了新数据2和3,那么像把他们放到一起充当训练集,可以用concat完成。可以看到代码将3个数据集拼接得到总的数据集,数据量为2+2+2=6。这里的concatdataset其实还是一个dataset类,它内部还是有len和getitem,里面的getitem代码思路值得学习。concatdataset通过给数据集编号、所有样本编号,然后在__getitem函数中将dataloader传进来的整体样本序号进行计算,得到匹配的数据集序号,以及在该数据集内的样本编号。 可能有点绕,请看图:假设dataloader想要第5个样本,传入index=4, 这时getitem会计算第五个样本在第三个数据集的第1个位置。然后通过self.datasets[datasetidx][sampleidx]来获取数据。这样对外进行一层封装,内部实现仍旧调用各个dataset的__getitem,这样是不是很巧妙呢? def __getitem__(self, idx): if idx len(self): raise ValueError(\"absolute value of index should not exceed dataset length\") idx = len(self) + idx dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) if dataset_idx == 0: sample_idx = idx else: sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] return self.datasets[dataset_idx][sample_idx] Subset subset可根据指定的索引获取子数据集,Subset也是Dataset类,同样包含_len_和__getitem\\,其代码编写风格也可以学习一下. CLASStorch.utils.data.Subset(dataset, indices)[SOURCE] Subset of a dataset at specified indices. Parameters dataset (Dataset) – The whole Dataset indices (sequence) – Indices in the whole set selected for subset def __init__(self, dataset: Dataset[T_co], indices: Sequence[int]) -> None: self.dataset = dataset self.indices = indices def __getitem__(self, idx): if isinstance(idx, list): return self.dataset[[self.indices[i] for i in idx]] return self.dataset[self.indices[idx]] def __len__(self): return len(self.indices) 使用上非常简单,代码一目了然,不再赘述。 random_split 该函数的功能是随机的将dataset划分为多个不重叠的子集,适合用来划分训练、验证集(不过不建议通过它进行,因为对用户而言,其划分不可见,不利于分析)。 使用也非常简单,只需要设置每个子集的数据量,传给lengths即可。 torch.utils.data.random_split(dataset, lengths, generator=)[SOURCE] Randomly split a dataset into non-overlapping new datasets of given lengths. Optionally fix the generator for reproducible results, e.g.: Parameters dataset (Dataset) – Dataset to be split lengths (sequence) – lengths of splits to be produced generator (Generator) – Generator used for the random permutation ---------------------------------------------------------------------- 分割线 ------------------------------------------------------------------ sampler 下面进入另外一个主题——sampler, sampler是在dataloader中起到挑选数据的功能,主要是设置挑选策略,如按顺序挑选、随机挑选、按类别分概率挑选等等,这些都可以通过自定义sampler实现。 在上一节我们已经用过了一个sampler,那就是batch_sampler,我们先学习一下它的用法,然后再去了解 RandomSampler, SequentialSampler, 以及SubsetRandomSampler和WeightedRandomSampler。 sampler的概念比较复杂,建议大家将BatchSampler、RandomSampler和SequentialSampler放在一起学习。 sampler 与 batch_sampler 首先讲一下dataloader类的sampler变量与batch_sampler变量的区别,在dataloader里会有这两个变量,第一次碰到时候很懵,怎么还有两个采样器,dataloader到底用的哪一个?还是两个都用?经过一番调试,终于搞清楚了。 本质上它们两个都是采样器,当采用auto_collation时,采用batch_sampler。依据如下:dataloader.py 365行 @property def _index_sampler(self): ​ if self._auto_collation: ​ return self.batch_sampler ​ else: ​ return self.sampler 来看一下两者定义: sampler (Sampler or Iterable, optional) – defines the strategy to draw samples from the dataset. Can be any Iterable with len implemented. If specified, shuffle must not be specified. batch_sampler (Sampler or Iterable, optional) – like sampler, but returns a batch of indices at a time. Mutually exclusive with batch_size, shuffle, sampler, and drop_last. 从定义可知道batch_sampler是一次返回一个batch的索引。通常我们用的都是batch_sampler,其对应的是BatchSampler类。 BatchSampler 下面先学习BatchSampler类。回顾3.3的dataloader获取一个样本的机制,会在一个self.nextindex()中调用实际的sampler迭代器,继续进入会来到BatchSampler类的__iter函数,dataloader初始化的时候根据参数配置,自动设置了采样策略为BatchSampler, 依据如下:dataloader.py 第272行代码 if batch_size is not None and batch_sampler is None: # auto_collation without custom batch_sampler batch_sampler = BatchSampler(sampler, batch_size, drop_last) dataloader.py 365行 @property def _index_sampler(self): if self._auto_collation: return self.batch_sampler else: return self.sampler 定位到了BatchSampler,下面来看看类的定义以及传进去的参数是什么。 torch.utils.data.BatchSampler(sampler, batch_size, drop_last) 后两个参数好理解,第一个参数传入的是一个sampler采样器,在这里会有两种情况,如果需要shuffle,则传入RandomSampler,不需要打乱,则传入SequentialSampler。 依据如下, dataloader.py 267行。 if shuffle: sampler = RandomSampler(dataset, generator=generator) else: sampler = SequentialSampler(dataset) 到这里,BatchSampler、RandomSampler和SequentialSampler三者之间的关系逐渐清晰. BatchSampler是在其它两者之上封装了一个批抽取的功能,一次yield一个batch的index,而样本采样的顺序取决于RandomSampler和SequentialSample。 来学习一下BatchSampler如何产生一个batch的序号,并且支持drop_last的功能。 def __iter__(self) -> Iterator[List[int]]: batch = [] for idx in self.sampler: batch.append(idx) if len(batch) == self.batch_size: yield batch batch = [] # 当for循环结束,且batch的数量又不满足batchsize时,则进入以下代码 # 其实就是drop_last的逻辑代码 if len(batch) > 0 and not self.drop_last: yield batch 理解了三者的关系(BatchSampler、RandomSampler和SequentialSampler),RandomSampler和SequentialSampler就很容易理解,来看它们的核心iter函数,学习一下如何编写顺序迭代器以及随机迭代器。 SequentialSampler 顺序迭代器相对简单,是得到一个按顺序的迭代器。这个顺序就来自 range()函数。 def __iter__(self) -> Iterator[int]: return iter(range(len(self.data_source))) RandomSampler RandomSampler的iter函数核心在于设置一个随机策略,随机策略委托给generator实现,在使用的时候非常简单,默认情况下会使用这行代码实现:yield from torch.randperm(n, generator=generator).tolist(), 利用torch的随机方法生成一个随机整数序列,对于generator默认采用的是随机一个随机种子进行设置。更多的随机概念可以自行了解torch.Generator()、torch.randperm()。 def __iter__(self) -> Iterator[int]: n = len(self.data_source) if self.generator is None: seed = int(torch.empty((), dtype=torch.int64).random_().item()) generator = torch.Generator() generator.manual_seed(seed) else: generator = self.generator if self.replacement: for _ in range(self.num_samples // 32): yield from torch.randint(high=n, size=(32,), dtype=torch.int64, generator=generator).tolist() yield from torch.randint(high=n, size=(self.num_samples % 32,), dtype=torch.int64, generator=generator).tolist() else: yield from torch.randperm(n, generator=generator).tolist() 接下来介绍另外两个实用的采样器:SubsetRandomSampler和WeightedRandomSampler。 SubsetRandomSampler 顾名思义,可以通过索引定义一个子集的随机采样器,直接看代码 ``` ​ def iter(self) -> Iterator[int]: ​ for i in torch.randperm(len(self.indices), generator=self.generator): ​ yield self.indices[i] 从代码可知道,这个采样器返回的样本总数是传入的索引的长度,这里体现了subset,而随机则是每次会随机的从子集里挑选1个数据返回。 ---------------------------------------------------------------------- 分割线 ------------------------------------------------------------------ WeightedRandomSampler 不知大家是否自行处理过数据均衡采样?最简单粗暴的方法是否是把数据少的样本复制n份,直到所有类别样本数量一致,这是一种“笨”办法,其实可以通过采样器进行加权的采样,下面来看看WeightedRandomSampler。 先来看它的原型: torch.utils.data.WeightedRandomSampler(weights, num_samples, replacement=True, generator=None) Samples elements from [0,..,len(weights)-1] with given probabilities (weights). weights (sequence) – 每个样本的采样权重,权重之和不必为1,只需要关心各样本之间的比例即可。 num_samples (int) – 采样数量,一般设为样本总量。 replacement (bool) –是否有放回采样。 True,表示有放回。 generator (Generator) – 自定义生成器,通常用默认的。 在pytorch的机制里,sampler为每个sample设置权重,因此在设置的时候不仅要指定每个类的采样概率,还要把各类采样概率分发到每个样本上,再传给WeightedRandomSampler。这个机制与常识有一点点不一样,直观的理解应该是为每个类别设置采样概率就好,但这却是为每个样本设置权重,因此需要额外操作两行代码。 通过以下两个案例学习如何使用WeightedRandomSampler。 案例1: sampler初认识 # 第一步:计算每个类的采样概率 weights = torch.tensor([1, 5], dtype=torch.float) # 第二步:生成每个样本的采样概率 train_targets = [sample[1] for sample in train_data.img_info] samples_weights = weights[train_targets] # 第三步:实例化WeightedRandomSampler sampler_w = WeightedRandomSampler( ​ weights=samples_weights, ​ num_samples=len(samples_weights), ​ replacement=True) sampler的构建分三步: 计算各类的采样概率:这里手动设置,是为了让大家可以调整不同的比率,观察dataloader采出样本的变化。下一个例子中采用样本数量进行计算,来达到均衡采样。 生成每个样本的概率:从pytorch机制了解到,需要为每个样本设置采样概率,这里采用的方法是按类别分发即可。在这里有一点需要注意,就是样本标签的顺序需要与dataset中的getitem中的索引顺序保持一致!由于这里采用了dataset.img_info来维护这个顺序,因此可以轻松获得样本顺序。 实例化WeightedRandomSampler 通过运行配套代码可以看到 torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 0]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) 这里发现出现了很多次[1, 1]。这是因为有放回采样,并且样本1的采样概率比0高很多。 通过这个例子,希望大家能了解 WeightedRandomSampler的使用流程 WeightedRandomSampler采样机制可以为有放回的 有的样本在整个loader中可能不会选中 案例2:不均衡数据集进行均衡采样 点击进入配套代码 下面利用WeightedRandomSampler实现一个10类别的不均衡数据集采样,使它变为1:1的采样。 下面制作了一个虚拟的不均衡数据集,每个类别数量分别是 10, 20,..., 100。总共550张样本,下面希望通过WeightedRandomSampler实现一个dataloader,每次采样550张样本,各类别的数量大约为55。 代码的核心在于统计各类样本的数量,可仔细阅读 # 第一步:计算各类别的采样权重 # 计算每个类的样本数量 train_targets = [sample[1] for sample in train_data.img_info] label_counter = collections.Counter(train_targets) class_sample_counts = [label_counter[k] for k in sorted(label_counter)] # 需要特别注意,此list的顺序! # 计算权重,利用倒数即可 weights = 1. / torch.tensor(class_sample_counts, dtype=torch.float) 最后可以看到每个epoch采样到的数据几乎实现1:1,可以很好的实现按照设置的权重比例采样。 Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) 接下来运用sampler Counter({0: 62, 4: 62, 8: 61, 9: 58, 6: 57, 3: 54, 1: 51, 7: 50, 5: 48, 2: 47}) Counter({5: 72, 7: 59, 6: 59, 8: 57, 1: 57, 0: 55, 4: 53, 2: 49, 9: 48, 3: 41}) Counter({0: 71, 3: 64, 5: 60, 9: 57, 4: 56, 2: 54, 1: 54, 6: 51, 8: 43, 7: 40}) Counter({4: 64, 7: 62, 3: 60, 8: 58, 1: 54, 5: 54, 0: 53, 6: 51, 2: 50, 9: 44}) Counter({8: 68, 0: 62, 7: 60, 6: 58, 2: 55, 3: 51, 9: 50, 5: 50, 1: 50, 4: 46}) Counter({5: 66, 4: 59, 9: 57, 0: 56, 1: 55, 3: 54, 7: 53, 2: 51, 8: 51, 6: 48}) Counter({3: 72, 9: 68, 5: 65, 6: 58, 4: 56, 8: 49, 1: 47, 2: 47, 0: 45, 7: 43}) Counter({4: 63, 2: 62, 7: 60, 9: 59, 3: 58, 8: 57, 6: 52, 0: 50, 5: 45, 1: 44}) Counter({8: 73, 3: 62, 6: 55, 0: 55, 2: 54, 4: 53, 7: 51, 1: 50, 9: 49, 5: 48}) Counter({5: 61, 3: 61, 2: 60, 9: 57, 1: 57, 7: 55, 6: 55, 4: 53, 8: 47, 0: 44}) 进一步地,为了便于大家理解“weights (sequence) – a sequence of weights, not necessary summing up to one”这句话,在代码中增加了 weights = 12345. / torch.tensor(class_sample_counts, dtype=torch.float) 大家可以随机修改weight的尺度,观察采样结果 关于采样策略有很多的研究,也有多种现成工具库可以使用,推荐大家看看这个repo 小结 本小结将常用的dataset、dataloader配套方法进行了讲解,包括数据集的拼接、子集挑选、子集划分和sampler。其中sampler是涨点神器,推荐掌握。在sampler中,先通过代码单步调试了解RandomSampler,然后顺藤摸瓜找到SequentialSampler和SubsetRandomSampler, 最后通过两个案例详细介绍涨点神器——WeightedRandomSampler的代码编写。 同时推荐大家拓展阅读关于数据采样策略对模型精度的论文,典型的主题是——长尾分布(Long Tail) 下一小节将介绍另外一个涨点首选神器——数据增强模块。先从torchvision的transform模块讲起,然后拓展到更强大的Albumentations Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.4-transforms.html":{"url":"chapter-3/3.4-transforms.html","title":"3.4 transforms","keywords":"","body":"3.4 transforms 本节分为两部分,首先介绍pytorch的图像数据增强函数库——transforms,深入分析它的工作机制,并阐述常用的方法。 transforms简介 数据增强(Data augmentation)已经成为深度学习时代的常规做法,数据增强目的是为了增加训练数据的丰富度,让模型接触多样性的数据以增加模型的泛化能力。更多关于数据增强的概念,推荐大家阅读《动手学》的image-augmentation章节 通常,数据增强可分为在线(online)与离线(offline)两种方式,离线方式指的是在训练开始之前将数据进行变换,变换后的图片保存到硬盘当中,在线方式则是在训练过程中,每一次加载训练数据时对数据进行变换,以实现让模型看到的图片都是增强之后的。实际上,这两种方法理论上是等价的,一般的框架都采用在线方式的数据增强,pytorch的transforms就是在线方式。后续不做特别说明,数据增强专指在线数据增强。 transforms是广泛使用的图像变换库,包含二十多种基础方法以及多种组合功能,通常可以用Compose把各方法串联在一起使用。大多数的transforms类都有对应的 functional transforms ,可供用户自定义调整。transforms提供的主要是PIL格式和Tensor的变换,并且对于图像的通道也做了规定,默认情况下一个batch的数据是(B, C, H, W) 形状的张量。 在transforms库中包含二十多种变换方法,那么多的方法里应该如何挑选,以及如何设置参数呢? 这是值得大家仔细思考的地方,数据增强的方向一定是测试数据集中可能存在的情况。 举个例子,做人脸检测可以用水平翻转(如前置相机的镜像就是水平翻转),但不宜采用垂直翻转(这里指一般业务场景,特殊业务场景有垂直翻转的人脸就另说)。因为真实应用场景不存在倒转(垂直翻转)的人脸,因此在训练过程选择数据增强时就不应包含垂直翻转。 运行机制 在正式介绍transforms的系列方法前,先来了解pytorch对数据增强的运行机制,我们继续通过debug模式在dataloader部分进行调试,观察一张图片是如何进行数据增强的。 同样的,我们回顾2.2小结的COVID-19代码,在dataloader中设置断点,进行debug。这里有一个小技巧,我们可以到dataset的getitem函数里设置一个断点,因为我们前面知道了图像的读取及处理是在dataset的getitem里,因此可以直接进入dataset,不必在dataloader里绕圈。当然,前提是需要大家熟悉dataloader的运行机制。 在第48行img = self.transform(img)设置断点,可以看到self.transform是一个Compose对象,继续进入self.transform(img) 来到 transforms.py 的Compose类的 __call__函数:这个函数的逻辑是依次调用compose对象里的变换方法,从此处也可看出数据是串联的,上一个方法的输出是下一个方法输入,这就要求各个方法之间传输的数据对象要一致。继续单步运行,进入第一个t(img), 第一个t是Resize。 来到D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch\\nn\\modules\\module.py的Module类的_call_impl函数:Module类是pytorch模型、网络层的核心,这个类有1854行代码,下一章将详细介绍模型模块以及Module。在这里我们只需要了解Resize这个变换方法是一个Module类,它实际的调用在1102行,进入1102行会来到Resize类的forward方法。 来到 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\transforms.py的Resize类的forward函数:可以看到此函数仅一行代码F.resize(img, self.size, self.interpolation, self.max_size, self.antialias),继续进入它。 来到D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch\\nn\\functional.py 的resize函数:functional模块是对一系列操作的封装,这里看到419行,resize功能的实现。继续进入419行。 来到 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\functional_pil.py的resize函数:这里终于进入到最核心的Resize方法实现了,这个函数里需要时间缩放的w,h,这里的计算代码非常值得大家学习,同时函数进来之后对参数的一系列判断,也值得借鉴。从此函数可以看到它利用了PIL库的resize函数对PIL图像进行resize。最终对图像resize是这265行代码:return img.resize(size[::-1], interpolation) 然后依次返回,回到transforms.py的Compose类的call函数,此时 img = t(img)完成了1次对图像的变换。接着继续执行for循环,把compose中的变换执行完毕,就对图像做完了变换、增强。 总结一下,一开始采用transforms.Compose把变换的方法包装起来,放到dataset中;在dataloader依次读数据时,调用dataset的getitem,每个sample读取时,会根据compose里的方法依次地对数据进行变换,以此完成在线数据增强。而具体的transforms方法通常包装成一个Module类,具体实现会在各functional中。 熟悉此运行机制,有助于大家今后自己编写数据增强方法,嵌入到自己的工程中。 系列API 通过单步debug,了解了transforms运行机制,下面看看transforms库提供的一系列方法及使用。更全面的方法介绍请直接看官方文档,官方文档配备了一个图解transforms的教程 这里不再一一展开各方法介绍,只挑选几个代表性的方法展开讲解,其余方法可以到第一版中阅读transforms的二十二个方法 在这里,结合COVID-2019 X光分类场景进行系列API的使用介绍。主要内容包括: 具体变换方法使用:resize、Normalize、totensor、FiveCrop、TenCrop 特殊方法使用:RandomChoice、RandomOrder、Lambda 自动数据增强:AutoAugmentPolicy、AutoAugment、RandAugment 具体变换方法使用 Compose 此类用于包装一系列的transforms方法,在其内部会通过for循环依次调用各个方法。这个在上面的代码调试过程中已经分析清楚了。 Resize Resize(size, interpolation=, max_size=None, antialias=None) 功能:支持对PIL或Tensor对象的缩放,关于size的设置有些讲究,请结合代码尝试int方式与tuple方式的差异。int方式是会根据长宽比等比例的缩放图像,这个在AlexNet论文中提到先等比例缩放再裁剪出224*224的正方形区域。 ToTensor 功能:将PIL对象或nd.array对象转换成tensor,并且对数值缩放到[0, 1]之间,并且对通道进行右移。具体地,来看源代码 ...\\Lib\\site-packages\\torchvision\\transforms\\functional.py 下的to_tensor函数 ···python img = img.permute((2, 0, 1)).contiguous() if isinstance(img, torch.ByteTensor): ​ return img.to(dtype=default_float_dtype).div(255) 对PIL对象的通道进行右移,由原来的(H x W x C)变为了(C x H x W) , 接着对数值进行除以255,若是正常的图像像素,那么数值被缩放到了[0, 1]之间。 Normalize Normalize(mean, std, inplace=False) 功能:对tensor对象进行逐通道的标准化,具体操作为减均值再除以标准差,一般使用imagenet的128万数据R\\G\\B三通道统计得到的mean和std,mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]。相信大家今后再看到这一组数据就明白它们到底怎么来的了。 FiveCrop&TenCrop 这两个方法是AlexNet论文中提及,是一个涨点神器,具体使用方式是一张图片经过多区域裁剪得到5/10张图片,同时放到模型进行推理,得到5/10个概率向量,然后取它们的平均/最大/最小得到这一张图片的概率。 FiveCrop表示对图片进行上下左右以及中心裁剪,获得 5 张图片,并返回一个list,这导致我们需要额外处理它们,使得他们符合其它transforms方法的形式——3D-tensor。 TenCrop同理,在FiveCrop的基础上增加水平镜像,获得 10 张图片,并返回一个 list。 它们的使用与普通的transforms有一点区别,需要代码层面的一些改变,下面就通过具体例子讲解它们的注意事项。 代码 授人以渔:其余的二十多个不在一一介绍,只需要到官方文档上查看,并到配套代码中运行,观察效果即可。 特殊方法使用 PyTorch 不仅可设置对数据的操作,还可以对这些操作进行随机选择、组合,让数据增强更加灵活。 具体有以下4个方法: Lambda RandomChoice RandomOrder RandomApply Lambda 功能:可进行自定义的操作,例如上文的FiveCrop中利用lambda很好的处理了上下游transforms数据维度不一致的问题。transforms.Lambda(lambda crops: torch.stack([ToTensor()(crop) for crop in crops])) RandomChoice 功能:以一定的概率从中选择一个变换方法执行。 RandomOrder 功能:随机打乱一串变换方法。 RandomApply 功能:以一定的概率执行这一串变换方法。这与RandomChoice的区别仅在于它将一组变换看成一个选择单位,RandomChoice是一次选一个,RandomApply是一次选一组(list) 具体使用可配合配套代码 自动数据增强 从transforms丰富的变换方法以及灵活的组合函数可以知道,数据增强的策略可以千变万化,怎样的策略会更好?Google Brain团队就针对这个问题,利用它们的钞能力进行研究,采用RNN网络自动搜索组合策略,寻找较好的数据增强策略,详细可以看这篇文章AutoAugment: Learning Augmentation Strategies from Data。文章中利用RNN搜索出来的策略,可以在Imagenet、Cifar-10和SVHN三个数据集上达到当时的SOTA,pytorch中也提供了基于AutoAugment论文的三个数据集的自动数据增强策略,下面一起来学习它们。 AutoAugmentPolicy 通过论文AutoAugment: Learning Augmentation Strategies from Data我们知道它研究出了针对三个数据集的数据增强策略,在pytorch中同样的提供对应的策略,并设计了AutoAugmentPolicy来指示,直接看源代码,一目了然envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\autoaugment.py: class AutoAugmentPolicy(Enum): \"\"\"AutoAugment policies learned on different datasets. Available policies are IMAGENET, CIFAR10 and SVHN. \"\"\" IMAGENET = \"imagenet\" CIFAR10 = \"cifar10\" SVHN = \"svhn\" AutoAugment torchvision.transforms.AutoAugment(policy: torchvision.transforms.autoaugment.AutoAugmentPolicy = , interpolation: torchvision.transforms.functional.InterpolationMode = , fill: Optional[List[float]] = None) 功能:自动数据增强方法的封装,支持三种数据增强策略,分别是IMAGENET、CIFAR10 和SVHN 参数: policy :需要是AutoAugmentPolicy类 interpolation:设置插值方法 fill :设置填充像素的像素值,默认为0,黑色。 AutoAugment也是一个Module类,具体的变换操作在forward()函数中体现,建议大家看看源代码,pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\autoaugment.py 里面有详细的三组数据增强策略的顺序与参数 例如ImageNet的数据增强策略总共有25组变换,共50个变换: return [ ((\"Posterize\", 0.4, 8), (\"Rotate\", 0.6, 9)), ((\"Solarize\", 0.6, 5), (\"AutoContrast\", 0.6, None)), ((\"Equalize\", 0.8, None), (\"Equalize\", 0.6, None)), ((\"Posterize\", 0.6, 7), (\"Posterize\", 0.6, 6)), ((\"Equalize\", 0.4, None), (\"Solarize\", 0.2, 4)), ((\"Equalize\", 0.4, None), (\"Rotate\", 0.8, 8)), ((\"Solarize\", 0.6, 3), (\"Equalize\", 0.6, None)), ((\"Posterize\", 0.8, 5), (\"Equalize\", 1.0, None)), ((\"Rotate\", 0.2, 3), (\"Solarize\", 0.6, 8)), ((\"Equalize\", 0.6, None), (\"Posterize\", 0.4, 6)), ((\"Rotate\", 0.8, 8), (\"Color\", 0.4, 0)), ((\"Rotate\", 0.4, 9), (\"Equalize\", 0.6, None)), ((\"Equalize\", 0.0, None), (\"Equalize\", 0.8, None)), ((\"Invert\", 0.6, None), (\"Equalize\", 1.0, None)), ((\"Color\", 0.6, 4), (\"Contrast\", 1.0, 8)), ((\"Rotate\", 0.8, 8), (\"Color\", 1.0, 2)), ((\"Color\", 0.8, 8), (\"Solarize\", 0.8, 7)), ((\"Sharpness\", 0.4, 7), (\"Invert\", 0.6, None)), ((\"ShearX\", 0.6, 5), (\"Equalize\", 1.0, None)), ((\"Color\", 0.4, 0), (\"Equalize\", 0.6, None)), ((\"Equalize\", 0.4, None), (\"Solarize\", 0.2, 4)), ((\"Solarize\", 0.6, 5), (\"AutoContrast\", 0.6, None)), ((\"Invert\", 0.6, None), (\"Equalize\", 1.0, None)), ((\"Color\", 0.6, 4), (\"Contrast\", 1.0, 8)), ((\"Equalize\", 0.8, None), (\"Equalize\", 0.6, None)), ] 特别说明:这里反复提到的自动数据增强在实际应用中它们是固定的一组变换策略,这是获得这一组策略的过程是通过强化学习自动搜素的,所以称之为自动数据增强策略。 RandAugment RandAugment是进行N次(num_ops )变换,变换方法从策略池中随机挑选。pytorch官方文档对于RandAugment给了较高的评价——“RandAugment is a simple high-performing Data Augmentation technique which improves the accuracy of Image Classification models.” 参数: num_ops :执行多少次变换 magnitude :每个变换的强度, num_magnitude_bins:与变化强度的采样分布有关 如果对autoaugmentation不熟悉的话,理解RandAugment的参数可能有点困难,这里结合代码看一看就知道了。 RandAugment仍旧是一个Module类,来看它的forward(), def forward(self, img: Tensor) -> Tensor: \"\"\" img (PIL Image or Tensor): Image to be transformed. Returns: PIL Image or Tensor: Transformed image. \"\"\" fill = self.fill if isinstance(img, Tensor): if isinstance(fill, (int, float)): fill = [float(fill)] * F.get_image_num_channels(img) elif fill is not None: fill = [float(f) for f in fill] for _ in range(self.num_ops): op_meta = self._augmentation_space(self.num_magnitude_bins, F.get_image_size(img)) op_index = int(torch.randint(len(op_meta), (1,)).item()) op_name = list(op_meta.keys())[op_index] magnitudes, signed = op_meta[op_name] magnitude = float(magnitudes[self.magnitude].item()) if magnitudes.ndim > 0 else 0.0 if signed and torch.randint(2, (1,)): magnitude *= -1.0 img = _apply_op(img, op_name, magnitude, interpolation=self.interpolation, fill=fill) return img 前面的代码段主要是根据规则获取需要进行的变换方法名称:op_name;变换的强度:magnitude,从 op_index = int(torch.randint(len(op_meta), (1,)).item()) op_name = list(op_meta.keys())[op_index] 这两行代码可以看到,每次采用的变换是随机的选择。 而变换强度magnitude则是根据一个区间里选择,不同变换方法的强度区间在这里: def _augmentation_space(self, num_bins: int, image_size: List[int]) -> Dict[str, Tuple[Tensor, bool]]: return { # op_name: (magnitudes, signed) \"Identity\": (torch.tensor(0.0), False), \"ShearX\": (torch.linspace(0.0, 0.3, num_bins), True), \"ShearY\": (torch.linspace(0.0, 0.3, num_bins), True), \"TranslateX\": (torch.linspace(0.0, 150.0 / 331.0 * image_size[0], num_bins), True), \"TranslateY\": (torch.linspace(0.0, 150.0 / 331.0 * image_size[1], num_bins), True), \"Rotate\": (torch.linspace(0.0, 30.0, num_bins), True), \"Brightness\": (torch.linspace(0.0, 0.9, num_bins), True), \"Color\": (torch.linspace(0.0, 0.9, num_bins), True), \"Contrast\": (torch.linspace(0.0, 0.9, num_bins), True), \"Sharpness\": (torch.linspace(0.0, 0.9, num_bins), True), \"Posterize\": (8 - (torch.arange(num_bins) / ((num_bins - 1) / 4)).round().int(), False), \"Solarize\": (torch.linspace(255.0, 0.0, num_bins), False), \"AutoContrast\": (torch.tensor(0.0), False), \"Equalize\": (torch.tensor(0.0), False), } TrivialAugmentWide TrivialAugment是采用NAS技术搜索得到的一组数据增强策略,推荐阅读原文TrivialAugment: Tuning-free Yet State-of-the-Art Data Augmentation 使用方法也非常简单,直接看代码即可。 想了解细节,请查看D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\autoaugment.py TrivialAugment核心 def _augmentation_space(self, num_bins: int) -> Dict[str, Tuple[Tensor, bool]]: return { # op_name: (magnitudes, signed) \"Identity\": (torch.tensor(0.0), False), \"ShearX\": (torch.linspace(0.0, 0.99, num_bins), True), \"ShearY\": (torch.linspace(0.0, 0.99, num_bins), True), \"TranslateX\": (torch.linspace(0.0, 32.0, num_bins), True), \"TranslateY\": (torch.linspace(0.0, 32.0, num_bins), True), \"Rotate\": (torch.linspace(0.0, 135.0, num_bins), True), \"Brightness\": (torch.linspace(0.0, 0.99, num_bins), True), \"Color\": (torch.linspace(0.0, 0.99, num_bins), True), \"Contrast\": (torch.linspace(0.0, 0.99, num_bins), True), \"Sharpness\": (torch.linspace(0.0, 0.99, num_bins), True), \"Posterize\": (8 - (torch.arange(num_bins) / ((num_bins - 1) / 6)).round().int(), False), \"Solarize\": (torch.linspace(255.0, 0.0, num_bins), False), \"AutoContrast\": (torch.tensor(0.0), False), \"Equalize\": (torch.tensor(0.0), False), } 小结 本小节详细剖析transforms运行机制,熟悉内部工作原理,大家可自行编写变换方法嵌入模型训练中。同时教授大家学习使用transforms的二十多种方法的方法——授人以渔,最后探讨了自动数据增强策略的原理及代码实践。 希望大家利用好数据增强,给自己的模型提升性能,切记数据增强的方向是朝着测试集(真实应用场景情况下)的数据分布、数据情况去变换,切勿盲目应用。 本章节介绍albumentations,但由于本章未涉及图像分割、目标检测,以及本章内容也不少了,因此将albumentations放到后续章节,适时进行讲解。 预告:原计划在本章节介绍albumentations,但由于本章未涉及图像分割、目标检测,以及本章内容也不少了,因此将albumentations放到后续章节,适时进行讲解。 为什么要用albumentations,pytorch的transforms有什么不足呢? 当然有不足的, pytorch的transforms在处理图像分割与目标检测这一类需要图像与标签同时变换的时候不太方便。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.5-torchvision-dataset.html":{"url":"chapter-3/3.5-torchvision-dataset.html","title":"3.5 torchvision 经典dataset学习","keywords":"","body":"3.5 torchvision 经典dataset学习 前面已经学习了Dataset,DataLoader,以及常用的函数,通常足以应对大多数需求,但距离熟练编写自己的Dataset可能还有一段距离。 为了让大家能轻松掌握各种情况下的dataset编写,本小节对torchvision中提供的几个常见dataset进行分析,观察它们的代码共性,总结编写dataset的经验。 X-MNIST 由于MNIST数据使用广泛,在多领域均可基于这个小数据集进行初步的研发与验证,因此基于MNIST数据格式的各类X-MNIST数据层出不穷,在mnist.py文件中也提供了多个X-MNIST的编写,这里需要大家体会类继承。 示例表明FashionMNIST、KMNIST两个dataset仅需要修改数据url(mirrors、resources)和类别名称(classes),其余的函数均可复用MNIST中写好的功能,这一点体现了面向对象编程的优点。 来看dataset的 getitem,十分简洁,因为已经把图片和标签处理好,存在self.data和self.targets中使用了: def __getitem__(self, index: int) -> Tuple[Any, Any]: img, target = self.data[index], int(self.targets[index]) img = Image.fromarray(img.numpy(), mode='L') if self.transform is not None: img = self.transform(img) if self.target_transform is not None: target = self.target_transform(target) return img, target 代码参阅:D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\datasets\\mnist.py cifar-10 cifar-10是除MNIST之外使用最多的公开数据集,同样,让我们直接关注其 Dataset 实现的关键部分 def __getitem__(self, index: int) -> Tuple[Any, Any]: img, target = self.data[index], self.targets[index] # doing this so that it is consistent with all other datasets # to return a PIL Image img = Image.fromarray(img) if self.transform is not None: img = self.transform(img) if self.target_transform is not None: target = self.target_transform(target) return img, target 核心代码还是这一行: img, target = self.data[index], self.targets[index] 接下来,去分析data和self.targets是如何从磁盘上获取的?通过代码搜索可以看到它们来自这里(D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\datasets\\cifar.py CIFAR10 类的 init函数): # now load the picked numpy arrays for file_name, checksum in downloaded_list: file_path = os.path.join(self.root, self.base_folder, file_name) with open(file_path, 'rb') as f: entry = pickle.load(f, encoding='latin1') self.data.append(entry['data']) if 'labels' in entry: self.targets.extend(entry['labels']) else: self.targets.extend(entry['fine_labels']) self.data = np.vstack(self.data).reshape(-1, 3, 32, 32) self.data = self.data.transpose((0, 2, 3, 1)) # convert to HWC 这一段的作用于MNIST的_load_data(), 我们的_get_img_info()一样,就是读取数据信息。 总结: getitem函数中十分简洁,逻辑简单 初始化时需完成数据信息的采集,存储到变量中,供getitem使用 VOC 之前讨论的数据集主要用于教学目的,比较复杂的目标检测数据是否具有较高的编写难度?答案是,一点也不,仍旧可以用我们分析出来的逻辑进行编写。 下面来看第一个大规模应用的目标检测数据集——PASCAL VOC, D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\datasets\\voc.py的 VOCDetection类的getitem函数: def __getitem__(self, index: int) -> Tuple[Any, Any]: \"\"\" Args: index (int): Index Returns: tuple: (image, target) where target is a dictionary of the XML tree. \"\"\" img = Image.open(self.images[index]).convert(\"RGB\") target = self.parse_voc_xml(ET_parse(self.annotations[index]).getroot()) if self.transforms is not None: img, target = self.transforms(img, target) return img, target 更简洁了,与我们的案例中的getitem一样一样的,那么images和annotations从哪里来?相信大家已经知道答案了,那就是初始化的时候根据数据格式、数据组织结构,从磁盘中读取。 COCO 说到目标检测就不得不提COCO数据集,COCO数据集是微软提出的大规模视觉数据集,主要用于目标检测,它从数据量、类别量都远超VOC,对于深度学习模型的落地应用起到了推动作用。 对于CV那么重要的COCO,它的dataset难吗?答案是,不难。反而更简单了,整个类仅40多行。 getitem函数连注释都显得是多余的: def __getitem__(self, index: int) -> Tuple[Any, Any]: id = self.ids[index] image = self._load_image(id) target = self._load_target(id) if self.transforms is not None: image, target = self.transforms(image, target) return image, target 其实,这一切得益于COCO的应用过于广泛,因此有了针对COCO数据集的轮子——pycocotools,它非常好用,建议使用COCO数据集的话,一定要花几天时间熟悉pycocotools。pycocotools把getitem需要的东西都准备好了,因此这个类只需要40多行代码。 小结 本章从数据模块中两个核心——Dataset&Dataloader出发,剖析pytorch是如何从硬盘中读取数据、组装数据和处理数据的。在数据处理流程中深入介绍数据预处理、数据增强模块transforms,并通过notebook的形式展示了常用的transforms方法使用,最后归纳总结torchvision中常见的dataset,为大家将来应对五花八门的任务时都能写出dataset代码。 下一章将介绍模型模块。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/":{"url":"chapter-4/","title":"第四章 PyTorch 模型模块","keywords":"","body":"第四章 PyTorch 模型模块 第四章 PyTorch 模型模块 4.1 Module&Parameter 4.2 Module的容器 4.3 常用网络层 4.4 Module常用API函数 4.5 Hook函数及Grad-CAM 4.6 经典模型代码分析 4.7 权重初始化方法 第四章简介 上一章介绍了数据相关的Dataset、DataLoader、transforms,已经能把数据从磁盘中有序的读取并处理以及加载成batch形式。接下来就需要一个强大的模型来处理它,本章就针对模型部分进行展开,这也是深度学习最核心的地方,其中包括一个模型如何创建各网络层、各网络层如何搭建、参数如何管理与初始化、如何截取某些层的特征图等一系列问题。 首先介绍核心类——Module 再介绍常用的模块容器——Containers 接着讲解常用网络层的使用 再学习module常用函数与hook函数应用 最后介绍权重初始化方法——nn.init Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.1-module&Parameter.html":{"url":"chapter-4/4.1-module&Parameter.html","title":"4.1 Module&Parameter","keywords":"","body":"4.1 Module & parameter Module初认识 深度学习通常通过深度神经网络实现,这些网络由多个层组成,我们通常称之为模型(Model),一个模型包含很多个网络层,多个网络层拼接构建成一个模型。在pytorch中模型是一个Module,各网络层、模块也是Module,本小节就介绍模型/模块的抽象——Module。后续不加以说明的话,模型、模块、网络层都可指代Module。 Module是所有神经网络的基类,所有的模型都必须继承于Module类,并且它可以嵌套,一个Module里可以包含另外一个Module。要想理解清楚这句话就必须清楚了解一个Module是如何工作的。在第二章我们就构建了一个Module——TinnyCNN,第三章讲解transform的时候也用到了Module,并且知道它的前向传播具体执行是在forward()函数当中,其实Module定义了一些列属性来管理模块的功能,分别用8个有序字典进行管理,分别是: self._modules = OrderedDict() self._parameters = OrderedDict() self._buffers = OrderedDict() self._backward_hooks = OrderedDict() self._forward_hooks = OrderedDict() self._forward_pre_hooks = OrderedDict() self._state_dict_hooks = OrderedDict() self._load_state_dict_pre_hooks = OrderedDict() 它们的作用分别是: modules : 存储管理nn.Module类 parameters: 存储管理nn.Parameter类 buffers:存储管理缓冲属性,如BN层中的running_mean *_hooks:存储管理钩子函数 讲到这,大家估计很懵,因为与前面接触到的内容完全搭不上边。但是这些又是Module的核心知识点,为了降低大家的学习曲线斜率,在这里暂且只需要知道一个Module有这些关键属性用于管理Module,以及在哪里找到它们——debug模式下的Protected Attributes看到它们的详情。 forward函数 除了八大核心属性之外,还有一个函数不得不了解,那就是forward函数,forward之于Module等价于getitem之于Dataset。forward函数是模型每次调用的具体实现,所有的模型必须实现forward函数,否则调用时会报错 Traceback (most recent call last): File \"E:/pytorch-tutorial-2nd/code/chapter-2/02_COVID_19_cls.py\", line 150, in main() File \"E:/pytorch-tutorial-2nd/code/chapter-2/02_COVID_19_cls.py\", line 111, in main outputs = model(data) File \"D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\lib\\site-packages\\torch\\nn\\modules\\module.py\", line 1102, in _call_impl return forward_call(*input, **kwargs) File \"D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\lib\\site-packages\\torch\\nn\\modules\\module.py\", line 201, in _forward_unimplemented raise NotImplementedError NotImplementedError Process finished with exit code 1 到这里,总结一下Module: Module是所有模型的基类 每个module有8个字典管理它的核心属性 一个module可以包含多个子module 一个module相当于一个运算,必须实现forward函数 一个模型的创建 下面通过简单的代码慢慢去熟悉Module,回顾TinnyCNN的创建与使用,可以总结出一个模型的创建需要考虑两个要素: 构建子模块:构建网络所需要的网络层,如卷积层,池化层,全联接层等等 拼接子模块:在forward函数中定义需要执行的功能,即将子模块以一定的方式拼接起来,完成对数据的前向传播 模型的创建就像搭积木,首先收集到需要的基础部件,是三角形、正方形还是六边形,然后以一定的方式拼接起来,如果要一个屋子就先放正方形,然后放三角形。如果需要一个汽车就先放两个圆形,再放一个长方形。 同理,模型搭建也是,先知道有哪些网络层是需要的,那么再init函数里进行初始化,先让此类获得这些网络层可用。具体如何用,需要在forward函数中写清楚。就像下面这个图一样。 知道了Module有哪些关键属性,以及一个模型如何创建,下面回顾2.2小结的COVID-19分类代码,采用debug方式观察TinnyCNN的创建——model = TinnyCNN(2), 以及它的推理: outputs = model(data) TinnyCNN的创建 代码在:code/chapter-2/02_COVID_19_cls.py 模型实例化的代码是这行: model = TinnyCNN(2) 我们打下断点,采用debug运行,进行分析如下: 进入 TinnyCNN 类的init函数:这里进行初始化,可以看到第一行就是调用父类的init函数,父类是Module,因此我们继续step into进去看看; 来到 Module类的init函数:这里会初始化那8个有序字典,以及一些关键属性,如training等。我们跳出去; 回到TinnyCNN 类的init函数:父类init函数结束,就来到自定义的组件定义部分,这里我们需要一个卷积层、一个全连接层供搭积木使用。这里的nn.Conv2d也是一个module,大家可以自行step into进去观察它的创建,这里暂且知道它是一个module即可。同理,nn.Linear也是。init函数里收集了需要搭积木的组件,下面跳出去。 回到主代码:model = TinnyCNN(2),这样一个模型就创建好了,我们可以看到model下面就有了这些属性: 重点看红框的三个内容,分别是convolution_layer、fc和_modules。前两个没啥好说的,是init函数中自定义的类属性名称,而第三个_modules什么时候“悄悄”地记录了我们自己定义的convolution_layer和fc呢? 这就需要大家了解一下python的基础了,请看这行代码: self.convolution_layer = nn.Conv2d(1, 1, kernel_size=(3, 3)) 在类属性赋值的时候,即这行代码中的“=”号,会调用类的__setattr__方法,在module.py的1180行代码是setatrr的实现,里面会将“=”号右边的值放到相应的地方去,如module会放到_modules里,parameter会放到_parameters里。 至此,对于模型的创建流程有了解后,下面看看模型的推理是如何进行的,它可不是简单的进入forward函数就完事了,中间还有复杂的辅助功能,一起往下看。 TinnyCNN的推理 继续采用debug,往下看。 先来到模型调用的地方:outputs = model(data),采用step into进入; 来到Module类的call_impl函数:熟悉python的朋友就疑惑了,为什么进入的是它而不是\\_call__函数?(python规定可被调用的对象,其实现是在__call__\\函数里)其实并没有错,只Module类对call函数重命名了罢了,可以看到1148行 __call__ : Callable[..., Any] = _call_impl 在早期版本的pytorch中还没有这一层包装,请各位专家指导一下为什么采用这种方式? 在_call_impl函数当中才会调用forward函数来实现数据的前向传播,但module除了forward调用之外,还有一些辅助功能,那就是一系列的hook函数的使用,这里暂且放下,后续会展开hook函数的作用。这里只需要关心怎么进入forward的。如果没有设置任何hook函数,则直接进行forward函数的调用 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks or _global_forward_hooks or _global_forward_pre_hooks): return forward_call(*input, **kwargs) step into 进入 return forward_call(input, *kwargs),就会发现来到了自定义的forward函数。 来到TinnyCNN类的forward函数:这里就是我们如何拼接网络层,组装积木的地方了。 通常会在这里调用其他module来完成数据的处理,例如使用nn.Conv2d来进行卷及操作,除了使用module对象,其它的数学运算、功能函数(如torch.nn.functionals里的系列函数)、for循环等都是可以使用的。 值得说的一点是,一些激活函数没有可训练参数,也不是module类,因此会在forward函数中直接调用,而不需要在init中初始化。比如 :out = F.relu(self.conv1(x)) 中的F.relu。 最后要强调一点是:forward函数中需要注意前后层数据的格式,类似transforms的实现一样,上一层的输出一定要对得上下一层的输入,否则会报错,常见的报错是Linear层接收到了不合适的数据。建议大家把TinnyCNN的forward函数的第二行注释掉:# x = x.view(x.size(0),-1),运行代码并观察错误,这个错误是大部分朋友都会遇到的:RuntimeError: mat1 and mat2 shapes cannot be multiplied (12x6 and 36x2)。 到这里一个模型的搭建以及前向推理就很清晰了,构建自己的网络只需要三步: 写一个类继承于Module init函数中把需要的网络层创建好 forward函数中把模型如何搭建的规则写好 Parameter 在Module中有一个重要的对象——Parameter,参数。它继承于Tensor,与Tensor差别不太大,主要作用是用来区分可训练的参数与常规的Tensor。 在这里要做一下说明,权重、参数和超参数,它们的含义。一般情况下模型的权重就表示模型的参数,它们是可训练的,通过反向传播算法不断的更新;而超参数如卷积核大小、学习率、迭代次数是不能通过反向传播算法去更新的。很明显Parameter就指模型的参数,如卷积层的卷积核权重和偏置,Linear层的权重和偏置,BN层的α和β等等。 Module中对于参数是采用_parameters 进行管理的,并且提供相应的api可以对module内所有参数进行调用与读取。回顾2.2 COVID-19的优化器实例化这行代码: optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4) 代码中表示把model.parameters()返回的内容给优化器,让优化器更新model.parameters(),从这里可进一步理解parameter类的作用,以及各网络层它们的参数都会初始化为parameter类。 可以看看 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch\\nn\\modules\\conv.py的131行代码 self.weight = Parameter(torch.empty( (out_channels, in_channels // groups, *kernel_size), **factory_kwargs)) 对默认的卷积核采用empty初始化数值,然后包装成Parameter类。 小结 到这里,一个简单模型是如何创建、如何工作的我们就已经讲解完了。但随着深度神经网络的拓扑结构越来越复杂,层数越来越多,只靠上面的方法无法很好的构建神经网络,还需要借助一些容器把固定的模块封装起来,循环地进行调用。下一节将介绍Module的容器,包括以下5个 - - Sequential A sequential container. ModuleList Holds submodules in a list. ModuleDict Holds submodules in a dictionary. ParameterList Holds parameters in a list. Parameter DictHolds parameters in a dictionary. Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.2-containers.html":{"url":"chapter-4/4.2-containers.html","title":"4.2 Module的容器","keywords":"","body":"4.2 Module容器——Containers 容器的概念出现在日常生活的方方面面,每天喝水的杯子用来装一些水,书包用来装一些办公用品,衣柜用来装一些衣服。因此,我们可以很容易地抽象出容器的概念,它用于将一些物品放置在某个地方,并进行有效的管理和使用。 在深度学习模型里面,有一些网络层需要放在一起使用,如 conv + bn + relu 的组合。Module的容器是将一组操作捆绑在一起的工具,在pytorch官方文档中把Module也定义为Containers,或许是因为“Modules can also contain other Modules”。 对于Module类可查看4.1小结,这里详细介绍两个常用的容器Sequential与ModuleList,同时介绍ModuleDict,ParameterList,ParameterDict。 Sequential sequential是pytorch里使用最广泛的容器,它的作用是将一系列网络层按固定的先后顺序串起来,当成一个整体,调用时数据从第一个层按顺序执行到最后一个层。回顾一下transforms的Compose就可以体会到按顺序的含义了。 sequential可以直接传module,也可以传OrderedDict,OrderedDict可以让容器里的每个module都有名字,方便调用。 请看两段官方代码: model = nn.Sequential( nn.Conv2d(1,20,5), nn.ReLU(), nn.Conv2d(20,64,5), nn.ReLU() ) # Using Sequential with OrderedDict. This is functionally the # same as the above code model = nn.Sequential(OrderedDict([ ('conv1', nn.Conv2d(1,20,5)), ('relu1', nn.ReLU()), ('conv2', nn.Conv2d(20,64,5)), ('relu2', nn.ReLU()) ])) 来看一个实际案例: AlexNet是新一代CNN的开山之作,也是这一轮深度学习潮流里,计算机视觉任务的开山之作。对于现代CNN,通常会把前面的卷积层、池化层称为特征提取部分,最后的全连接层当作分类器,这一点在代码编写上将有所体现。 例如,在D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\alexnet.py 下的AlexNet,它把前面的卷积和池化都放到Sequential这个容器当中,并且命名为self.features。在最后还有一个名为self.classifier的Sequential容器,它包含3个Linear层及激活函数、Dropout。这里正如下图所示,把模型大卸两块。 def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.features(x) x = self.avgpool(x) x = torch.flatten(x, 1) x = self.classifier(x) return x ​ Sequential的调用 Sequential容器把一堆module包起来之后,它在forward中是如何使用的呢? 下面请看配套代码,主要采用debug模式,观察Alexnet的forward中,两个Sequential容器是如何forward的,同时也要看看Alexnet这个模型的属性。 在 output = model(fake_input) 设置断点,step into,然后进入熟悉的_call_impl,关于module内部的代码这里直接略过,不熟悉的朋友请回到4.1小节阅读。 这里直接跳到Alexnet类的forward函数,第一行就是执行x = self.features(x),继续step into观察这个sequential容器是如何工作的,进入它 来到了Module类下的_call_impl函数:没错,又来到了_call_impl,因为sequential它也是一个module,因此在调用self.features的时候,会进入_call_impl, 下面需要大家有耐心的进入self.features的forward函数,其实就是1102行进去; 来到Sequential类的forward函数,它十分简洁,如下所示: def forward(self, input): ​ for module in self: ​ input = module(input) ​ return input 这段代码是不是十分的熟悉呢? transforms当中也是这样去实现一个Compose里的变换的。 从此处可知道,Sequential类的功能是将一系列网络层按固定的先后顺序串起来,当成一个整体,调用时数据从第一个层按顺序执行到最后一个层,各层之间的数据必须能对接起来。 接着回到 Alexnet的forward函数下,观察一下Alexnet这个module的属性 重点看_modules属性,它有3个key-value,其中有2个是Sequential类,因为Sequential属于module类,继续展开一个Sequential来看看 可以看到该容器下的一系列网络层,并且是排了序的,这些对于后续理解网络结构、理解网络权重加载的key是十分重要的 ModuleList ModuleList 是将各个网络层放到一个“列表”中,便于迭代的形式调用。 ModuleList与python List的区别 这里注意是“列表”而不是列表,因为ModuleList管理的module与python的List管理的module是有不同的,大家是否还记得module的setattr函数?在那里会对类属性进行判断管理,只有ModuleList里的网络层才会被管理,如果是List里的网络层则不会被管理,也就不能迭代更新了。 ModuleList 使用示例 假设要构建一个10层的全连接网络,如果用Sequential,那就要手写10行nn.Linear,而用ModuleList是这样的: class MyModule(nn.Module): def __init__(self): super(MyModule, self).__init__() self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)]) # self.linears = [nn.Linear(10, 10) for i in range(10)] # 观察model._modules,将会是空的 def forward(self, x): for sub_layer in self.linears: x = sub_layer(x) return x 需要对python的 list进行一个ModuleList封装,这样才可以在model的_modules属性下看到创建的10个Linear层。 推荐大家看看class ModuleList(Module)的实现,里边并不会像Sequential那样提供forward,即管理的网络层由用户自行调用,可以for循环全用,也可以通过if判断,有条件的选择部分网络层使用。同时ModuleList也提供了类似List的方法,insert\\append\\extend等。 ModuleDict ModuleList可以像python的List一样管理各个module,但对于索引而言有一些不方便,因为它没有名字,需要记住是第几个元素才能定位到指定的层,这在深度神经网络中有一点不方便。 而ModuleDict就是可以像python的Dict一样为每个层赋予名字,可以根据网络层的名字进行选择性的调用网络层。 请看代码 class MyModule2(nn.Module): def __init__(self): super(MyModule2, self).__init__() self.choices = nn.ModuleDict({ 'conv': nn.Conv2d(3, 16, 5), 'pool': nn.MaxPool2d(3) }) self.activations = nn.ModuleDict({ 'lrelu': nn.LeakyReLU(), 'prelu': nn.PReLU() }) def forward(self, x, choice, act): x = self.choices[choice](x) x = self.activations[act](x) return x ParameterList & ParameterDict 除了Module有容器,Parameter也有容器。与ModuleList和ModuleDict类似的,Paramter也有List和Dict,使用方法一样,这里就不详细展开,可以参考Module的容器。 可以看两段官方文档代码感受一下 class MyModule(nn.Module): def __init__(self): super(MyModule, self).__init__() self.params = nn.ParameterDict({ 'left': nn.Parameter(torch.randn(5, 10)), 'right': nn.Parameter(torch.randn(5, 10)) }) def forward(self, x, choice): x = self.params[choice].mm(x) return x # ParameterList class MyModule(nn.Module): def __init__(self): super(MyModule, self).__init__() self.params = nn.ParameterList([nn.Parameter(torch.randn(10, 10)) for i in range(10)]) def forward(self, x): # ParameterList can act as an iterable, or be indexed using ints for i, p in enumerate(self.params): x = self.params[i // 2].mm(x) + p.mm(x) return x 小结 随着深度神经网络拓扑结构越来越复杂,网络模块多,杂,乱,因此需要Module容器来管理、组织各个网络层,便于forward函数中调用。 使用频率最高的是Sequential,其次是ModuleList,其余的均为进阶用法,在各类魔改网络中才会涉及。 这里深刻理解Sequential的机制、理解一个module是如何把Sequential里的module管理到自己的_modules属性中,对于后续使用模型是非常重要的。 熟悉了Module类,各种容器封装,下一小节将介绍一些常用的网络层,如卷积、池化、全连接、激活函数等。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.3-common-module.html":{"url":"chapter-4/4.3-common-module.html","title":"4.3 常用网络层","keywords":"","body":"4.3 常用网络层 本小节将介绍几个常用的layers,核心目的是传授如何学习各layers的方法,今后更多layers也可自行从官方文档中学习。 Convolutional Layers 卷积整体分两大类,正常卷积与转置卷积(Transpose Convolution),除此之外还有Lazy系列的正常卷积与转置卷积,Lazy系列就是懒惰系列,为那些懒得计算输入特征图的通道数的使用者而设计,经过第一次forward之后,该网络层的in_channels将被确定。 下面通过官方文档详细学习Conv2d这个卷积层,在文档中会详细介绍该层的功能,各参数含义,计算公式,以及示例代码。 torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros') 主要功能:对多个二维平面组成的信号进行二维卷积 主要参数: in_channels (int) – Number of channels in the input image。输入这个网络层的图像的通道数是多少。 out_channels (int) – Number of channels produced by the convolution。此网络层输出的特征图的通道数是多少,等价于卷积核数量是多少。 kernel_size (int or tuple) – Size of the convolving kernel。卷积核大小。 stride (int or tuple, optional) – Stride of the convolution. Default: 1。卷积核卷积过程的步长。 padding (int, tuple or str, optional) – Padding added to all four sides of the input. Default: 0。对于输入图像的四周进行填充的数量进行控制,可指定填充像素数量,也可以指定填充模式,如\"same\", \"valid\",具体含义参见文档,这是从TF中借鉴过来的。 padding_mode (string, optional) – 'zeros', 'reflect', 'replicate' or 'circular'. Default: 'zeros'。填充的像素值如何确定。默认填充0。 dilation (int or tuple, optional) – Spacing between kernel elements. Default: 1。孔洞卷积的孔洞大小。 groups (int, optional) – Number of blocked connections from input channels to output channels. Default: 1。分组卷积的分组。 bias (bool, optional) – If True, adds a learnable bias to the output. Default: True。是否采用偏置。 nn.Conv2d是图像领域里99%模型都用到的,它的计算公式及细节需要大家了如指掌,具体公式如下:` 这里建议大家结合各种动图进行学习,推荐这个repo, Pooling Layers Pooling layer叫池化层,池化是形象词,就像下雨天篮球场上低洼的地方会聚集周围的雨水一样,由大变小的过程。 自然它的作用是将特征图分辨率变小,通常减小一半。如下图所示,相同颜色的区域”池化“为1个像素,4x4的图像变为了2x2的图像。 图片来源:https://www.geeksforgeeks.org/cnn-introduction-to-pooling-layer/ 1个像素代替4个像素,那应该用什么值呢?针对这个问题,可对池化层进行划分,分为最大值池化、平均值池化、分数阶池化、基于范数的池化。分别对应torch.nn中的Maxpool, Avgpool, FractionalMaxPool, LPPool。 由于它们只是在计算像素时才用的方法不同,下面就以Maxpool为例讲解池化层。 torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False) 功能:2D最大池化 参数: kernel_size – 池化窗口大小 stride – 滑窗步长 padding – 原图填充大小 dilation – 孔洞大小 return_indices – 是否返回最大值所在位置,主要在 torch.nn.MaxUnpool2d 中使用,是上采样的一种策略 ceil_mode – 当无法整除时,是向下取整还是向上取整,默认为向下取整。 池化层输出特征图的大小计算公式如下,细心的朋友会发现它与卷积层是一样的。 针对最大池化还有一个特殊的地方是它可以记录最大值所在的位置,供上采样时(MaxUnpool2d)所用,这个在图像分割任务中会有涉及。MaxUnpool2d的使用非常简单,参数设置很容易。原型如下: torch.nn.MaxUnpool2d(kernel_size, stride=None, padding=0),具体使用可看配套代码。 自适应池化层 上面针对池化像素如何取值进行划分,其实针对窗口大小的选择也可划分,还有另外一种特殊的池化方法,那就是AdaptiveXpool, 它的作用是自适应窗口大小,保证经过池化层之后的图像尺寸是固定的,这个在接入全连接层之前经常会见到。 使用也很方便,只需要设置想要的输出大小即可,详细可见配套代码。torch.nn.AdaptiveMaxPool2d(output_size, return_indices=False) Padding Layers Padding layer在许多魔改网络中常用到,功能是给特征图周围填充一定的像素,调整特征图分辨率的一种方法。既然是填充就涉及两个问题,填充多少个像素?像素应该如何确定? 针对第二个问题,可将padding layer划分为三类,镜像填充、边界重复填充,指定值填充、零值填充,分别对应nn的三大类,nn.ReflectionPad2d, nn.ReplicationPad2d, nn.ZeroPad2d, nn.ConstantPad2d,使用非常简单,详细可见配套代码。 Linear Layers Linear Layers包含4个层分别是nn.Identity,nn.Linear, nn.Bilinear, nn.LazyLinear nn.Identity 是恒等映射,不对输入做任何变换,它通常用于占位。 nn.Linear 就是大家熟悉的全连接层(Fully Connection Layer),可实现 y= Wx + b nn.Bilinear 是双线性层,它有两个输入,实现公式 y = x1Wx2 +b nn.LazyLinear 是nn.Linear的lazy版本,也就是懒惰的Linear层,它在第一次推理时自动根据输入特征图的尺寸来设定in_features,免去了手动计算in_features的麻烦。 Linear层十分简单,就不用代码演示了。 Normaliation Layers Normaliation Layers 里包含主流的标准化网络层,分别有BN、LN、IN、GN以及早期的LRN。这一些列的层已经成为现在深度学习模型的标配,它们充当一种正则,对数据的分布进行变换,使数据分布变到0均值,1标准差的形式。实验结果发现这样做可以加速模型训练,让模型更稳定,精度更高。 其中最出名的当属2015年提出的BatchNorm, 来自于Google团队的Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift,关于BN的介绍网上有很多文章,大家可自行学习,在代码实现上我们需要熟悉网络层内部的参数,以及训练与推理过程中的差异。 BatchNorm 会对输入进行减均值、除以标准差、乘以γ、加β的操作。如下图所示: 其中γ与β是Parameter,是可训练的参数,与卷积层的卷积核、FC层的权重一样,容易理解。 均值与标准差就没那么简单了,在训练过程,它们是通过指数移动平均统计得来,在测试时则是用固定的、不会再变化的均值和方差。 从此也可知道,当模型设置在训练状态(model.train() )与推理状态(model.eval() )时,BN层的操作输出是会不一样的。 方法原型如下: torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None) num_features – 输入的通道数,该参数将决定BN层有多少个γ和β eps – 分母增加的一个小数,防止除以0,默认值为1e-5 momentum – 指数滑动平均的动量值,Default: 0.1 affine – 是否执行乘以γ、加β的操作,要理解为什么叫affine需要去看论文。Default: True track_running_stats – 是否需要执行记录统计均值、统计方差。默认是开启的,如果不开启,则计算时的均值方差只来自当前batch的计算值。 Default: True 具体使用方法详细可见配套代码,请尝试调整各个参数,观察输出的变化以及网络层本身参数的变化。 BN提出之后几乎成了深度学习模型的标配,但在一些任务中BN的均值、方差计算方式就不太适用了,针对均值、方差的统计方式不同,就有了GN、LN、IN。 GN是针对batch size小(一些任务占显存大,只能用小batch跑),统计的均值方差存在较大差异而提出的分组进行统计,详细参见:Group Normalization LN是针对RNN这样的序列网络设计的,以层为单位进行统计均值、方差,详细参见:Layer Normalization IN是针对风格迁移这类GAN任务中,不同风格实例的差异较大,以实例为单位进行统计均值、方差,详细参见:Instance Normalization: The Missing Ingredient for Fast Stylization LRN是2012年深度学习图像领域开山之作——AlexNet中采用的正则化方法,现在很少采用,详细参见:ImageNet Classifification with Deep Convolutional Neural Networks Dropout Layers Dropout——随机失活和LRN一样在Alexnet论文中所采用,以防止模型过拟合,针对它的正则化作用探讨可参见由Hinton一作发表的论文Improving neural networks by preventing co-adaptation of feature detectors。 Dropout 的操作非常简单,以概率p随机的让部分神经元暂时失活,失活表示它不与任何神经元连接,如下图所示: 图片出自:《Dropout: A Simple Way to Prevent Neural Networks from Overfitting》 训练过程的每一次forward,都会重新进行随机失活。在测试(推理)过程,所有神经元都参与工作,不再采用随机失活。更详细的操作及理论分析,推荐阅读《Dropout: A Simple Way to Prevent Neural Networks from Overfitting》。 Dropout使用注意事项: Dropout通常用于nn.Linear层之前 Dropout执行后,神经元个数会减少,导致数据尺度发生变化 论文中给出的方法是在测试时,需要将神经元数据尺度缩放 1/p倍,因为在训练时候减少了p倍。(p为随机失活的概率)。但在工程应用的时候,最好是减少推理的步骤,于是pytorch把数据尺度的缩放弄到了训练中,在训练时,对数据进行1/(1-p)的放大。(Furthermore, the outputs are scaled by a factor of 1/(1-p) during training. ) 关于数据尺度缩放,这里设计了验证实验,可到配套代码中运行并查看。 Alpha Dropout Dropout的随机失活会导致数据分布的变化,而数据分布对于模型训练的稳定是非常关键的,因此有针对这个问题提出了一种保持输入均值和方差不变的Dropout——Alpha Dropout。理论分析建议阅读论文Self-Normalization Neural Networks FeatureAlphaDropout是基于通道维度进行的,并且不同于Dropout的置零,它是将神经元设置为SELU激活函数的负饱和值,通常 Alpha Dropout都是搭配SELU激活函数的,具体推导还是要看论文Self-Normalization Neural Networks,一篇102页的论文。 Non-linear Layers 非线性激活函数是深度学习的命根子,倘若没有非线性变换函数,那么1亿层的Linear层堆叠,也只能等价于1层网络(通过矩阵乘法法则可推导)。因此非线性激活函数是深度学习之所以能称之为深度的重要因素。 对于非线性激活函数,pytorch划分为了两大类,这是非常合理的!分别是Non-linear Activations (weighted sum, nonlinearity) 和Non-linear Activations (other)。 其实可以作用进行划分 为了对神经元进行非线性变换的称为非线性激活函数 为了对输出神经元进行Softmax的、变为概率分布形式的称为特殊非线性激活函数 更通俗的划分是: 非softmx的; softmax系列; 对于非softmax,大家肯定不陌生,如sigmoid、tanh、ReLU、PReLU等,这些就不过多介绍,请大家自行查阅文档 对于softmax需要简单讲一讲,softmax的作用是将一个向量转换为一个概率分布的形式,以便于实现loss的计算,计算过程如下图所示: 计算公式如下: 看着一头雾水,其实很好理解。一个概率向量它的要求至少有这两个 非负 求和等于1 对于非负,用上幂函数,就可以实现了; 对于求和对于1,那就所有元素除以一个求和项,所有元素再加起来的时候分子就等于分母,自然求和等于1了,Softmax的设计思路真巧妙! 对于Softmax系列的激活函数,可参考文档 小结 本文对pytorch常用的网络层接口进行了介绍与代码分析,由于深度学习模型发展迅速,难以详尽介绍每一个网络层的使用,但pytorch都有详细的文档可以学习,希望大家可以通过本节内容学习如何学习pytorch的系列函数、类方法使用。 本小节配套代码中有这些网络层的演示: 更多更详细的网络层使用介绍,可查看文档中的目录,这里简介每个主题的内容 Containers: 模型容器 Convolution Layers:卷积层 Pooling layers:池化层 Padding Layers:填充层 Non-linear Activations (weighted sum, nonlinearity):非线性激活函数 Non-linear Activations (other):Softmax系列激活函数 Normalization Layers:标准化层 Recurrent Layers:RNN 网络层 Transformer Layers: Transformer 网络层 Linear Layers:线性层 Dropout Layers: 随机失活层 Sparse Layers:稀疏网络层 Distance Functions:计算距离函数 Loss Functions:计算损失函数 Vision Layers:CV任务网络层 Shuffle Layers:随机打乱功能层 DataParallel Layers (multi-GPU, distributed):多GPU网络层,多gpu需要用层的概念进行包装 Utilities:各功能函数层 Quantized Functions:量化功能函数 Lazy Modules Initialization:“懒惰”初始化功能模块 到这里,Module的类型就介绍完毕,下一小节将学习Module内部有哪些api,如何使用它们对一个Module进行管理,如模型的网络层查看、管理,模型的参数查看、管理,以及Hook函数的用法。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.4-module-api.html":{"url":"chapter-4/4.4-module-api.html","title":"4.4 Module常用API函数","keywords":"","body":"4.4 Module常用函数 本小节汇总介绍Module常用的方法,由于文档中是按首字母排序展示所有方法,未按用途进行归类,不便于理解各函数之间的关系。在这里,特别将具有相似功能的相关函数归纳整理,供大家参考学习。 常用方法包括: 设置模型训练、评估模式 eval train 设置模型存放在cpu/gpu/xpu cpu cuda to xpu 获取模型参数、加载权重参数 load_state_dict state_dict 管理模型的modules, parameters, sub_module parameters children modules named_children named_modules named_parameters get_parameter get_submodule add_module 设置模型的参数精度,可选半精度、单精度、双精度等 bfloat16 half float double 对子模块执行特定功能 apply zero_grad 以上是不完全的列举,有些非高频使用的函数请到文档中查阅。下面通过简介和配套代码的形式学习上述函数的使用。 设置模型训练、评估模式 eval:设置模型为评估模式,这一点与上一小节介绍的BN,Dropout息息相关,评估模式下模型的某些层执行的操作与训练状态下是不同的。 train:设置模型为训练模式,如BN层需要统计running_var这些统计数据,Dropout层需要执行随机失活等。 使用方法过于简单,无需代码展示。 设置模型存放在cpu/gpu 对于gpu的使用会在后面设置单独小节详细介绍,由于这里是基础学习,暂时可不考虑运算速度问题。这里既然遇到了相关的概念,就简单说一下。 pytorch可以利用gpu进行加速运算,早期只支持NVIDIA公司的GPU,现在也逐步开始支持AMD的GPU。使用gpu进行运算的方法很简单,就是把需要运算的数据放到gpu即可。方法就是 xxx.cuda(),若想回到cpu运算,那就需要xxx.cpu()即可。但有一个更好的方法是to(),to方法可将对象放到指定的设备中去,如to.(\"cpu\") 、 to.(\"cuda)、to(\"cuda:0\")等。 cpu:将Module放到cpu上。 cuda:将Module放到cuda上。为什么是cuda不是gpu呢?因为CUDA(Compute Unified Device Architecture)是NVIDIA推出的运算平台,数据是放到那上面进行运算,而gpu可以有很多个品牌,因此用cuda更合理一些。 to:将Module放到指定的设备上。 关于to通常会配备torch.cuda.is_available()使用,请看配套代码学习。 获取模型参数、加载权重参数 模型训练完毕后,我们需要保存的核心内容是模型参数,这样可以供下次使用,或者是给别人进行finetune。相信大家都用ImageNet上的预训练模型,而使用方法就是官方训练完毕后保存模型的参数,供我们下载,然后加载到自己的模型中。在这里就涉及两个重要操作:保存模型参数与加载模型参数,分别要用到以下两个函数。 state_dict:返回参数字典。key是告诉你这个权重参数是放到哪个网络层。 load_state_dict:将参数字典中的参数复制到当前模型中。这里的复制要求key要一一对应,若key对不上,自然模型不知道要把这个参数放到哪里去。绝大多数开发者都会在load_state_dict这里遇到过报错,如 RuntimeError: Error(s) in loading state_dict for ResNet: Missing key(s) in state_dict: xxxxxxxx   Unexpected key(s) in state_dict: xxxxxxxxxx 这通常是拿到的参数字典与模型当前的结构不匹配。 对于load_state_dict函数,还有两个参数可以设置,请看原型: 参数: state_dict (dict) – a dict containing parameters and persistent buffers. strict (bool, optional) – whether to strictly enforce that the keys in state_dict match the keys returned by this module’s state_dict() function. Default: True 返回项 missing_keys is a list of str containing the missing keys unexpected_keys is a list of str containing the unexpected keys 上述两个方法具体的使用请看配套代码。 管理模型的modules, parameters, sub_module 模型中需要管理的主要是parameter与module,每个对象都有两种方式读取,分别是带名字和不带名字的。针对module还有一个称为children的方法,它与modules方法最大的不同在于modules会返回module本身。具体差异通过配套代码一看便明了。 parameters:返回一个迭代器,迭代器可抛出Module的所有parameter对象 named_parameters:作用同上,不仅可得到parameter对象,还会给出它的名称 modules:返回一个迭代器,迭代器可以抛出Module的所有Module对象,注意:模型本身也是module,所以也会获得自己。 named_modules:作用同上,不仅可得到Module对象,还会给出它的名称 children:作用同modules,但不会返回Module自己。 named_children:作用同named_modules,但不会返回Module自己。 获取某个参数或submodule 当想查看某个部分数据时,可以通过get_xxx方法获取模型特定位置的数据,可获取parameter、submodule,使用方法也很简单,只需要传入对应的name即可。 get_parameter get_submodule 设置模型的参数精度,可选半精度、单精度、双精度等 为了调整模型占存储空间的大小,可以设置参数的数据类型,默认情况是float32位(单精度),在一些场景可采用半精度、双精度等,以此改变模型的大小或精度。Module提供了几个转换权重参数精度的方法,分别如下: half:半精度 float:单精度 double:双精度 bfloat16:Brain Floating Point 是Google开发的一种数据格式,详细参见wikipedia 对子模块执行特定功能 zero_grad:将所有参数的梯度设置为0,或者None apply:对所有子Module执行指定fn(函数),常见于参数初始化。这个可以参见配套代码。 小结 本节对Module的常用API函数进行了介绍,包括模型两种状态,模型存储于何种设备,模型获取参数,加载参数,管理模型的modules,设置模型参数的精度,对模型子模块执行特定功能。 由于Module是核心模块,其涉及的API非常多,短时间不好消化,建议大家结合代码用例,把这些方法都过一遍,留个印象,待日后项目开发需要的时候知道有这些函数可以使用即可。 下一小节将介绍Module中的Hook函数。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.5-hook-func.html":{"url":"chapter-4/4.5-hook-func.html","title":"4.5 Hook函数及Grad-CAM","keywords":"","body":"4.5 hook函数 注:本小节主要参考《PyTorch模型训练实用教程》(第一版),主要更新了PyTorch新版本的函数——torch.nn.Module.register_full_backward_hook。 -------------------------------------------------分割线--------------------------------------------------------------- 本小节将介绍Module中的三个Hook函数以及Tensor的一个Hook函数 torch.Tensor.register_hook torch.nn.Module.register_forward_hook torch.nn.Module.register_forward_pre_hook torch.nn.Module.register_full_backward_hook 同时使用hook函数优雅地实现Grad-CAM,效果如下图所示: ​ Grad-CAM是CAM(class activation map,类激活图)的改进,可对任意结构的CNN进行类激活可视化,不需要修改网络结构或者重新训练,详细理论请参见Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization 什么是hook? Hook函数在多门编程语言中均有出现,是一个经典的编程方式。hook意为钩、挂钩、鱼钩。 引用知乎用户“马索萌”对hook的解释:“(hook)相当于插件。可以实现一些额外的功能,而又不用修改主体代码。把这些额外功能实现了挂在主代码上,所以叫钩子,很形象。” 简单讲,就是不修改主体,而实现额外功能。对应到在pytorch中,主体就是forward和backward,而额外的功能就是对模型的变量进行操作,如“提取”特征图,“提取”非叶子张量的梯度,修改张量梯度等等。 hook的出现与pytorch运算机制有关,pytorch在每一次运算结束后,会将中间变量释放,以节省内存空间,这些会被释放的变量包括非叶子张量的梯度,中间层的特征图等。 但有时候,想可视化中间层的特征图,又不能改动模型主体代码,该怎么办呢?这时候就要用到hook了。 举个例子演示hook提取非叶子张量的梯度: import torch def grad_hook(grad): y_grad.append(grad) y_grad = list() x = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True) y = x+1 y.register_hook(grad_hook) z = torch.mean(y*y) z.backward() print(\"type(y): \", type(y)) print(\"y.grad: \", y.grad) print(\"y_grad[0]: \", y_grad[0]) >>> ('type(y): ', ) >>> ('y.grad: ', None) >>> ('y_grad[0]: ', tensor([[1.0000, 1.5000], [2.0000, 2.5000]])) 可以看到y.grad的值为None,这是因为y是非叶子结点张量,在z.backward()完成之后,y的梯度被释放掉以节省内存,但可以通过torch.Tensor的类方法register_hook将y的梯度提取出来。 torch.Tensor.register_hook torch.Tensor.register_hook (Python method, in torch.Tensor.register_hook) 功能:注册一个反向传播hook函数,这个函数是Tensor类里的,当计算tensor的梯度时自动执行。 为什么是backward?因为这个hook是针对tensor的,tensor中的什么东西会在计算结束后释放? 那就是gradient,所以是backward hook. 形式: hook(grad) -> Tensor or None ,其中grad就是这个tensor的梯度。 返回值:a handle that can be used to remove the added hook by calling handle.remove() 应用场景举例:在hook函数中可对梯度grad进行in-place操作,即可修改tensor的grad值。 这是一个很酷的功能,例如当浅层的梯度消失时,可以对浅层的梯度乘以一定的倍数,用来增大梯度; 还可以对梯度做截断,限制梯度在某一区间,防止过大的梯度对权值参数进行修改。 下面举两个例子,例1是如何获取中间变量y的梯度,例2是利用hook函数将变量x的梯度扩大2倍。 例1: import torch y_grad = list() def grad_hook(grad): y_grad.append(grad) x = torch.tensor([2., 2., 2., 2.], requires_grad=True) y = torch.pow(x, 2) z = torch.mean(y) h = y.register_hook(grad_hook) z.backward() print(\"y.grad: \", y.grad) print(\"y_grad[0]: \", y_grad[0]) h.remove() # removes the hook >>> ('y.grad: ', None) >>> ('y_grad[0]: ', tensor([0.2500, 0.2500, 0.2500, 0.2500])) 可以看到当z.backward()结束后,张量y中的grad为None,因为y是非叶子节点张量,在梯度反传结束之后,被释放。 在对张量y的hook函数(grad_hook)中,将y的梯度保存到了y_grad列表中,因此可以在z.backward()结束后,仍旧可以在y_grad[0]中读到y的梯度为tensor([0.2500, 0.2500, 0.2500, 0.2500]) 例2: import torch def grad_hook(grad): grad *= 2 x = torch.tensor([2., 2., 2., 2.], requires_grad=True) y = torch.pow(x, 2) z = torch.mean(y) h = x.register_hook(grad_hook) z.backward() print(x.grad) h.remove() # removes the hook >>> tensor([2., 2., 2., 2.]) 原x的梯度为tensor([1., 1., 1., 1.]),经grad_hook操作后,梯度为tensor([2., 2., 2., 2.])。 torch.nn.Module.register_forward_hook 功能:Module前向传播中的hook,module在前向传播后,自动调用hook函数。 形式:hook(module, input, output) -> None or modified output 。注意不能修改input和output 返回值:a handle that can be used to remove the added hook by calling handle.remove() 举例:假设网络由卷积层conv1和池化层pool1构成,输入一张4*4的图片,现采用forward_hook获取module——conv1之后的feature maps,示意图如下: ​ import torch import torch.nn as nn class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 2, 3) self.pool1 = nn.MaxPool2d(2, 2) def forward(self, x): x = self.conv1(x) x = self.pool1(x) return x def farward_hook(module, data_input, data_output): fmap_block.append(data_output) input_block.append(data_input) if __name__ == \"__main__\": # 初始化网络 net = Net() net.conv1.weight[0].fill_(1) net.conv1.weight[1].fill_(2) net.conv1.bias.data.zero_() # 注册hook fmap_block = list() input_block = list() net.conv1.register_forward_hook(farward_hook) # inference fake_img = torch.ones((1, 1, 4, 4)) # batch size * channel * H * W output = net(fake_img) # 观察 print(\"output shape: {}\\noutput value: {}\\n\".format(output.shape, output)) print(\"feature maps shape: {}\\noutput value: {}\\n\".format(fmap_block[0].shape, fmap_block[0])) print(\"input shape: {}\\ninput value: {}\".format(input_block[0][0].shape, input_block[0])) 首先初始化一个网络,卷积层有两个卷积核,权重分别为全1和全2,bias设置为0,池化层采用2*2的最大池化。 在进行forward之前对module——conv1注册了forward_hook函数,然后执行前向传播(output=net(fake_img)),当前向传播完成后, fmap_block列表中的第一个元素就是conv1层输出的特征图了。 这里注意观察forward_hook函数有data_input和data_output两个变量,特征图是data_output这个变量,而data_input是conv1层的输入数据, conv1层的输入是一个tuple的形式。 hook函数调用逻辑 下面剖析一下module是怎么样调用hook函数的呢? output = net(fakeimg) net是一个module类,对module执行 module(input)是会调用module._call module.call :会进入_call_impl,回顾Module那一小节,call_impl是有很多其它代码,这就是对hook函数的处理,可以看到,让注册了hook函数,模型的forward不再是4.1小节里分析的1102行代码进行,而是分别执行对应的hook函数。1109行是执行每个forward_pre_hook的,1120行是执行forward的,1123行是执行forward_hook的, 1144行是执行full_backward_hook的。 def _call_impl(self, *input, **kwargs): forward_call = (self._slow_forward if torch._C._get_tracing_state() else self.forward) # If we don't have any hooks, we want to skip the rest of the logic in # this function, and just call forward. if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks or _global_forward_hooks or _global_forward_pre_hooks): return forward_call(*input, **kwargs) # Do not call functions when jit is used full_backward_hooks, non_full_backward_hooks = [], [] if self._backward_hooks or _global_backward_hooks: full_backward_hooks, non_full_backward_hooks = self._get_backward_hooks() if _global_forward_pre_hooks or self._forward_pre_hooks: for hook in (*_global_forward_pre_hooks.values(), *self._forward_pre_hooks.values()): result = hook(self, input) if result is not None: if not isinstance(result, tuple): result = (result,) input = result bw_hook = None if full_backward_hooks: bw_hook = hooks.BackwardHook(self, full_backward_hooks) input = bw_hook.setup_input_hook(input) result = forward_call(*input, **kwargs) if _global_forward_hooks or self._forward_hooks: for hook in (*_global_forward_hooks.values(), *self._forward_hooks.values()): hook_result = hook(self, input, result) if hook_result is not None: result = hook_result if bw_hook: result = bw_hook.setup_output_hook(result) # Handle the non-full backward hooks if non_full_backward_hooks: var = result while not isinstance(var, torch.Tensor): if isinstance(var, dict): var = next((v for v in var.values() if isinstance(v, torch.Tensor))) else: var = var[0] grad_fn = var.grad_fn if grad_fn is not None: for hook in non_full_backward_hooks: wrapper = functools.partial(hook, self) functools.update_wrapper(wrapper, hook) grad_fn.register_hook(wrapper) self._maybe_warn_non_full_backward_hook(input, result, grad_fn) return result 这里需要注意两点: hook_result = hook(self, input, result)中的input和result不可以修改。这里的input对应forward_hook函数中的data_input,result对应forward_hook函数中的data_output,在conv1中,input就是该层的输入数据,result就是经过conv1层操作之后的输出特征图。虽然可以通过hook来对这些数据操作,但是不能修改这些值,否则会破坏模型的计算。 注册的hook函数是不能带返回值的,否则抛出异常,这个可以从代码中看到 if hook_result is not None: raise RuntimeError 总结一下调用流程: net(fake_img) --> net.call : result = self.forward(input, *kwargs) --> net.forward: x = self.conv1(x) --> conv1.call:hook_result = hook(self, input, result) hook就是注册了的forward_hook函数。 torch.nn.Module.register_forward_pre_hook 功能:执行forward()之前调用hook函数。 形式:hook(module, input) -> None or modified input 应用场景:register_forward_pre_hook与forward_hook一样,是在module.call中注册的,与forward_hook不同的是,其在module执行forward之前就运行了,具体可看module.call中的代码。 torch.nn.Module.register_full_backward_hook 功能:Module反向传播中的hook,每次计算module的梯度后,自动调用hook函数。 形式:hook(module, grad_input, grad_output) -> tuple(Tensor) or None 注意事项: 当module有多个输入或输出时,grad_input和grad_output是一个tuple。 register_full_backward_hook 是修改过的版本,旧版本为register_backward_hook,不过官方已经建议弃用,不需要再了解。 返回值:a handle that can be used to remove the added hook by calling handle.remove() 应用场景举例:提取特征图的梯度 Grad-CAM 实现 采用register_full_backward_hook实现特征图梯度的提取,并结合Grad-CAM(基于类梯度的类激活图可视化)方法对卷积神经网络的学习模式进行可视化。 关于Grad-CAM请看论文:《Grad-CAM Visual Explanations from Deep Networks via Gradient-based Localization》 简单介绍Grad-CAM的操作,Grad-CAM通过对最后一层特征图进行加权求和得到heatmap,整个CAM系列的主要研究就在于这个加权求和中的权值从那里来。 Grad-CAM是对特征图求梯度,将每一张特征图的梯度求平均得到权值(特征图的梯度是element-wise的)。求梯度时并不采用网络的输出,而是采用类向量,即one-hot向量。 下图是ResNet的Grad-CAM示意图,上图类向量采用的是猫的标签,下图采用的是狗的标签,可以看到在上图模型更关注猫(红色部分),下图判别为狗的主要依据是狗的头部。 ​ 下面采用一个LeNet-5演示backward_hook在Grad-CAM中的应用。 简述代码过程: 创建网络net; 注册forward_hook函数用于提取最后一层特征图; 注册backward_hook函数用于提取类向量(one-hot)关于特征图的梯度 对特征图的梯度进行求均值,并对特征图进行加权; 可视化heatmap。 注意:需要注意的是在backward_hook函数中,grad_out是一个tuple类型的,要取得特征图的梯度需要这样grad_block.append(grad_out[0].detach()) 思考 这里对3张飞机的图片进行观察heatmap,如下图所示,第一行是原图,第二行是叠加了heatmap的图片。 这里发现一个有意思的现象,模型将图片判为飞机的依据是蓝天,而不是飞机(图1-3)。 那么我们喂给模型一张纯天蓝色的图片,模型会判为什么呢?如图4所示,发现模型判为了飞机 从这里发现,虽然能将飞机正确分类,但是它学到的却不是飞机的特征! 这导致模型的泛化性能大打折扣,从这里我们可以考虑采用trick让模型强制的学习到飞机而不是常与飞机一同出现的蓝天,或者是调整数据。 ​ 对于图4疑问:heatmap蓝色区域是否对图像完全不起作用呢?是否仅仅通过红色区域就可以对图像进行判别呢? 接下来将一辆正确分类的汽车图片(图5)叠加到图4蓝色响应区域(即模型并不关注的区域),结果如图6所示,汽车部分的响应值很小,模型仍通过天蓝色区域将图片判为了飞机。 接着又将汽车叠加到图4红色响应区域(图的右下角),结果如图7所示,仍将图片判为了飞机。 有意思的是将汽车叠加到图7的红色响应区域,模型把图片判为了船,而且红色响应区域是蓝色区域的下部分,这个与船在大海中的位置很接近。 ​ 通过以上代码学习full_backward_hook的使用及其在Grad-CAM中的应用,并通过Grad-CAM能诊断模型是否学习到关键特征。 关于CAM( class activation maping,类激活响应图)是一个很有趣的研究,有兴趣的朋友可以对CAM、Grad-CAM和Grad-CAM++进行研究。 小结 本小节介绍了编程语言中经典的思想——Hook函数,并讲解了pytorch中如何使用它们,最后还采用full_backward_hook实现有趣的Grad-CAM可视化,本节代码较多,建议对着配套代码单步调试进行学习,掌握hook函数的妙用,在今后使用pytorch进行模型分析、魔改的时候更游刃有余。 下一小结会把本章所学的Module相关容器、网络层的知识点串起来使用,通过剖析torchvision中经典模型的源代码,了解所学习的知识点是如何使用的。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.6-classic-model.html":{"url":"chapter-4/4.6-classic-model.html","title":"4.6 经典模型代码分析","keywords":"","body":"4.6 经典Model代码分析 torchvision中提供了一些经典的卷积神经网络模型实现,本小节将挑选部分进行分析,学习torchvision是如何构建复杂的网络模型,学习它们的代码风格、代码规范。 AlexNet 出自:ImageNet Classification with Deep Convolutional Neural Networks 模型结构图如下图所示:整体可分为前半部分的特征提取与后半部分的分类。 ​ 代码分析: D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\alexnet.py 代码中定义了一个AlexNet类与一个alexnet函数,这样的封装形式贯穿整个torchvision的模型定义。 AlexNet类是nn.Module,其中定义了AlexNet模型的具体结构,而alexnet函数则是对Alexnet类的包装,并且实现加载预训练参数的功能,即以下代码: model = AlexNet(**kwargs) if pretrained: state_dict = load_state_dict_from_url(model_urls[\"alexnet\"], progress=progress) model.load_state_dict(state_dict) return model 从此也知道,torchvision中定义模型所采用的预训练模型均是通过指定的url下载,并存储于本地磁盘供下一次使用。 由于“网络问题”,通常建议大家通过代码中给出的url自行下载权重文件,然后在自己的代码中使用load_state_dict方法加载预训练参数。 分析了alexnet.py整体结构,下面回到AlexNet类本身,看看具体模型如何写的。 class AlexNet(nn.Module): def __init__(self, num_classes: int = 1000) -> None: def forward(self, x: torch.Tensor) -> torch.Tensor: AlexNet采用熟悉的方式定义了两个函数,熟悉4.1小结中的知识点的话,这里不比多说。 forward函数第48行代码值得注意,二维特征图要输入到Linear层,通常通过flatten函数对特征图进行变换。 def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.features(x) x = self.avgpool(x) x = torch.flatten(x, 1) # Line 48 x = self.classifier(x) return x 总结: 采用函数形式封装模型类,额外提供预训练权重加载功能; Linear层之前可通过torch.flatten将数据变为一维向量; VGG VGG出自:Very Deep Convolutional Networks For Large-Scale Image Recognition 其共有4种深度,分别是11, 13, 16, 19层,用得比较多的VGG16、19。VGG的代码就比AlexNet的代码复杂了,因为它涉及8个具体的网络模型定义,因此不能再使用面向过程的方式进行编写,需要将共性的部分抽象出来,这一份代码值得新手仔细、认真学习。 首先是大体了解VGG整体结构,网络结构示意图如下图所示: ​ VGG最大特点是2个3x3、3个3x3卷积层的堆叠,并且堆叠总共分5次,最后接入三个FC层。从此可知,核心是如何将特征提取部分进行抽象,请大家带着这个问题观察代码:D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\vgg.py vgg.py中定义了VGG类、_vgg函数、make_layers函数、cfgs字典,以及一系列具体网络模型封装的函数,如vgg11,vgg13, vgg16等。 看过alexnet.py,这里能猜出VGG类是一个nn.module。 _vgg函数:vgg函数接收具体的网络参数,以此决定返回哪一个vgg模型; vggxxx:定义了具体VGG所需要的参数,并调用_vgg函数得到具体模型; make_layers函数:创建可抽象出来、共性的网络层函数,即网络结构图中的5次堆叠部分。 cfgs字典:配置各具体vgg模型所需要的参数,主要在make_layers中使用。 下面以vgg16为例,观察vgg.py是如何实现它的。 看到153行代码: def vgg16(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> VGG: return _vgg(\"vgg16\", \"D\", False, pretrained, progress, **kwargs) 可知道,vgg16是对_vgg的封装,并且固定了两个参数\"vgg16\" 和 \"D\"。 跳到94行代码: def _vgg(arch: str, cfg: str, batch_norm: bool, pretrained: bool, progress: bool, **kwargs: Any) -> VGG: if pretrained: kwargs[\"init_weights\"] = False model = VGG(make_layers(cfgs[cfg], batch_norm=batch_norm), **kwargs) if pretrained: state_dict = load_state_dict_from_url(model_urls[arch], progress=progress) model.load_state_dict(state_dict) return model 可知道_vgg调用了VGG类得到最终的模型,并且给VGG传入了make_layers函数创建的网络层; 通过这行代码可知道,需要进入make_layers去观察如何创建网络层的。进入make_layers前,需要知道cfgs[cfg]当前传入的是: 'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'], 跳到69行代码: def make_layers(cfg: List[Union[str, int]], batch_norm: bool = False) -> nn.Sequential: layers: List[nn.Module] = [] in_channels = 3 for v in cfg: if v == \"M\": layers += [nn.MaxPool2d(kernel_size=2, stride=2)] else: v = cast(int, v) conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1) if batch_norm: layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)] else: layers += [conv2d, nn.ReLU(inplace=True)] in_channels = v return nn.Sequential(*layers) 从这里可知道是对cfg中进行for循环,不断的构建网络层,并且添加到list中,最后组装成一个Sequential的形式。这里的代码逻辑就是网络结构图中的抽象,把四种模型的共性地方抽象出来,然后通过不同的配置参数可生成vgg11, vgg13, vgg16, vgg19。这里的代码值得学习。 弄清楚make_layers是生成前面一系列卷积层的堆叠Sequential之后,继续进入VGG类观察。 跳到25行代码,看一个Module,可以先看forward函数,再看forward中的属性是怎么来的。 def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.features(x) x = self.avgpool(x) x = torch.flatten(x, 1) x = self.classifier(x) return x 可以发现它的forward十分简洁,因为vgg模型就是以简洁出名的,像一个糖葫芦一样串起来即可。接着去看看self.features是什么,怎么来的,这个需要到init函数中寻找。 跳到34行代码:self.features = features 由此可知道,VGG特征提取部分的网络层均是通过make_layers函数定义的那个Sequential。 接着36行代码的classifier就没啥好说的。 接着的第45行代码出现了新内容,权重初始化。调用了_initialize_weights函数对VGG模型进行权重初始化。众所周知,良好的权重初始化对模型训练是至关重要的,早期对于权重初始化有许多的研究,比较著名的有Xavier方法、MSRA(Kaiming)方法。 预告:具体的权重初始化方法将在下一小节详细介绍。 下面观察如何编写函数对VGG进行权重初始化:跳转55行 def _initialize_weights(self) -> None: for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode=\"fan_out\", nonlinearity=\"relu\") if m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.BatchNorm2d): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0) elif isinstance(m, nn.Linear): nn.init.normal_(m.weight, 0, 0.01) nn.init.constant_(m.bias, 0) 此函数的逻辑遍历所有Module,并判断Module类型,根据不同的Module类型设置不同的初始化方法,如卷积层则用kaiming方法设置weight,bias全部设置为0;BN层的weight设置为1,bias设置为0;全连接层的weight用正态分布进行随机初始化,bias设置为0。 到这里一个具体的VGG模型定义就讲完了,下面总结一下它们的调用关系与逻辑。 vgg16() --> _vgg() --> make_layers --> VGG:最核心在于如何构建一个模块(函数也好、类也好)可以接收不同的参数(cfgs)就能生成对应VGG的特征提取部分的网络层(一个大的Sequential)。 GoogLeNet GoogLeNet-V1 出自 Going deeper with convolutions,后续也有V2,V3,V4,这里不进行介绍。 V1的提出最大的特点在于提出Inception模块,它是一个多分支的特征提取模块,如下图所示: ​ 网络结构如下图所示: ​ 代码并不复杂,但其中的Inception模块的编写,是之前没有遇到的,可以借鉴学习。 观察D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\googlenet.py 可以看到熟悉的定义了具体的Module——GoogLeNet类,模型的封装调用函数——googlenet,以及从GoogLeNet模型抽象出来的、反复需要使用的模块——Inception、InceptionAux、BasicConv2d。 这里面的代码并不复杂,这里不逐行分析,只把GoogLeNet类的逻辑关系理一理。 首先,将反复使用的模块抽象成一个类,这样在使用的时候只需要一行代码即可定义好,如BasicConv2d:包含了卷积层+BN层; Inception:包含四个分支的处理并合并最终特征图; InceptionAux:辅助分类层输出。 然后在init函数中像搭积木一样,把需要用到的模块逐一定义 最后在forward函数中调用定义好的网络层即可。 总结: 反复使用的模块抽象为一个Moudle类,并作为参数进行调用。好处在于当想修改这些基础元素模块的时候,仅需要重新写一个Module类替换即可,并不需要改动GoogLeNet类当中的任何代码(在resnet中会有体现)。要想理解好这一点,请仔细体会这几行代码 blocks = [BasicConv2d, Inception, InceptionAux] conv_block = blocks[0] inception_block = blocks[1] inception_aux_block = blocks[2] Resnet ResNet出自何恺明的《Deep Residual Learning for Image Recognition》,是目前工业界应用最广泛的卷积神经网络。 网络结构如下图所示,有ResNet-18, 34, 50, 101, 152,使用较多的为ResNet-50。其结构特点也是模块的堆叠,如表格中看到的x2, x3,x4, x6表示的是一个模块堆叠2次、3次、4次、6次。 ​ 在resnet模型中,最大的特点在于采用了残差结构的模块,如下图所示: ​ 这里有两种形式,一种是BasicBlock,另外一种是resnet50/101/152用的Bottleneck。 下面就来看看D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\resnet.py 是如何实现这一系列复杂的resnet模型。 提示:pycharm中按住Ctrl+Shift+ \"-\" ,可以把代码块收起来,可以快速浏览resnet.py下的主要内容,可以发现,还是熟悉的结构,分别有 ResNet类 _resnet函数 resnet18\\34\\50...一系列具体模型函数 抽象出来的基础模块:BasicBlock、Bottleneck、conv1x1和conv3x3。 这其中最为特色的是BasicBlock和Bottleneck,分别对应论文图5中的两个结构,它们将在不同的模型中使用。 下面就看看BasicBlock和Bottleneck到底是如何使用的。 跳到144行代码:class ResNet(nn.Module),观察init函数里是如何使用block的。 跳到第178行代码:self.layer1 = self._make_layer(block, 64, layers[0]),在make_layer函数中使用了block进行网络层的构建。这点与VGG中的make_layers类似。 跳到205行代码: def _make_layer( self, block: Type[Union[BasicBlock, Bottleneck]], planes: int, blocks: int, stride: int = 1, dilate: bool = False, ) -> nn.Sequential: norm_layer = self._norm_layer downsample = None previous_dilation = self.dilation if dilate: self.dilation *= stride stride = 1 if stride != 1 or self.inplanes != planes * block.expansion: downsample = nn.Sequential( conv1x1(self.inplanes, planes * block.expansion, stride), norm_layer(planes * block.expansion), ) layers = [] layers.append( block( self.inplanes, planes, stride, downsample, self.groups, self.base_width, previous_dilation, norm_layer ) ) self.inplanes = planes * block.expansion for _ in range(1, blocks): layers.append( block( self.inplanes, planes, groups=self.groups, base_width=self.base_width, dilation=self.dilation, norm_layer=norm_layer, ) ) return nn.Sequential(*layers) 在此函数中使用block(是一个基础模块的类,是BasicBlock或Bottleneck)定义网络层,然后堆叠起来,最后使用Sequential进行包装,构成一个整体。 回到init函数可知道,178-184行代码所构建的模块对应了网络结构的四个部分,对应关系如下图所示: self.layer1 = self._make_layer(block, 64, layers[0]) self.layer2 = self._make_layer(block, 128, layers[1], stride=2, dilate=replace_stride_with_dilation[0]) self.layer3 = self._make_layer(block, 256, layers[2], stride=2, dilate=replace_stride_with_dilation[1]) self.layer4 = self._make_layer(block, 512, layers[3], stride=2, dilate=replace_stride_with_dilation[2]) ​ 在这里,可以发现resnet18和34用的是BasicBlock, resnet50/101/152用的是Bottleneck def resnet18(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet: return _resnet(\"resnet18\", BasicBlock, [2, 2, 2, 2], pretrained, progress, **kwargs) def resnet50(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet: return _resnet(\"resnet50\", Bottleneck, [3, 4, 6, 3], pretrained, progress, **kwargs) BasicBlock和Bottleneck的使用与googlenet中的blocks呼应上了,请大家仔细对比。 resnet总结 resnet的搭建是将block抽象出来提供接口,由用户自行传入,并且设定堆叠次数,如resnet18就是BasicBlock, [2, 2, 2, 2], resnet50就是 Bottleneck, [3, 4, 6, 3],处处体现了面向对象的编程思维,值得学习。 总结 本小节从简单的AlexNet到复杂的ResNet进行了代码分析,剖析了pytorch的代码结构,编写逻辑以及思想,其中面向对象的思维值得认真学习借鉴。 VGG中的make_layers():通过参数配置形式搭建一个大的Sequential; GoogLeNet的BasicConv2d, Inception, InceptionAux、ResNet的BasicBlock、Bottleneck、conv1x1、conv3x3都是抽象的基础模块。 本小节在多出看到了权重初始化方法,好的权重初始化是模型训练的第一步,下一小节将介绍pytorch提供的系列权重初始化方法及其应用。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.7-weight-init.html":{"url":"chapter-4/4.7-weight-init.html","title":"4.7 权重初始化方法","keywords":"","body":"4.7 权重初始化方法 良好的模型权重初始化,有利于模型的训练,在torch.nn.init中提供了数十个初始化方法,本小节对它们进行介绍。 回顾上一小节中VGG的初始化代码,先总结出权重初始化的流程与步骤。 for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode=\"fan_out\", nonlinearity=\"relu\") if m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.BatchNorm2d): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0) elif isinstance(m, nn.Linear): nn.init.normal_(m.weight, 0, 0.01) nn.init.constant_(m.bias, 0) 首先对每一个子module进行遍历,对不同的网络层进行设置对应的权重初始化方法,初始化方法采用的是nn.init.xxx函数。 接下来nn.init.xxx就是本节的主角,下面依次介绍nn.init.xxx函数。 主要分为三部分: Xavier 系列 kaiming 系列 常数方法 Xavier 系列 torch.nn.init.xavieruniform(tensor, gain=1.0) xavier 初始化方法中服从均匀分布 U(-a,a) ,分布的参数 a = gain * sqrt(6/fan_in+fan_out), 这里有一个 gain,表示增益,其大小取决于激活函数的类型。本方法也称为 Glorot initialization。 torch.nn.init.xaviernormal(tensor, gain=1.0) xavier初始化方法中服从正态分布, mean=0,std = gain * sqrt(2/fan_in + fan_out) Xavier初始化方法的理论分析可见《Understanding the difficulty of training deep feedforward neural networks - Glorot, X. & Bengio, Y. (2010),》 Kaiming系列 ​ 3.### torch.nn.init.kaiminguniform(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu') 此为均匀分布,U~(-bound, bound), bound = sqrt(6/(1+a^2)*fan_in) 其中,a为激活函数的负半轴的斜率,relu是0 mode- 可选为fan_in 或 fan_out, fan_in使正向传播时,方差一致; fan_out使反向传播时,方差一致 nonlinearity- 可选 relu 和 leaky_relu ,默认值为 :leaky_relu torch.nn.init.kaimingnormal(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu') 此为0均值的正态分布,N~ (0,std),其中std = sqrt(2/(1+a^2)*fan_in) 其中,a为激活函数的负半轴的斜率,relu是0 mode- 可选为fan_in 或 fan_out, fan_in使正向传播时,方差一致;fan_out使反向传播时,方差一致 nonlinearity- 可选 relu 和 leaky_relu ,默认值为: leaky_relu Kaiming系列的初始化方法理论分析可参阅《Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification - He, K. et al. (2015》 其它方法 均匀分布初始化 torch.nn.init.uniform_(tensor, a=0, b=1) 使值服从均匀分布U(a,b) 正态分布初始化 torch.nn.init.normal_(tensor, mean=0, std=1) 使值服从正态分布N(mean, std),默认值为0,1 常数初始化 torch.nn.init.constant_(tensor, val) 使值为常数val nn.init.constant_(w, 0.3) 单位矩阵初始化 torch.nn.init.eye_(tensor) 将二维tensor初始化为单位矩阵(the identity matrix) 正交初始化 torch.nn.init.orthogonal_(tensor, gain=1) 使得tensor是正交的,论文:Exact solutions to the nonlinear dynamics of learning in deep linear neural networks” - Saxe, A. et al. (2013) 稀疏初始化 torch.nn.init.sparse_(tensor, sparsity, std=0.01) 从正态分布N~(0. std)中进行稀疏化,使每一个column有一部分为0 sparsity- 每一个column稀疏的比例,即为0的比例 nn.init.sparse_(w, sparsity=0.1) 全零初始化 torch.nn.init.zeros_(tensor) 所有参数置零。 全1初始化 torch.nn.init.ones_(tensor) 所有参数置一。 狄拉克初始化 torch.nn.init.dirac_(tensor, groups=1) 采用狄拉克函数进行权重初始化, 增益计算 torch.nn.init.calculate_gain(nonlinearity, param=None) 返回各激活函数对应的增益值,该值用于调整权重初始化的方差。 nonlinearity gain Linear / Identity 1 Conv{1,2,3}D 1 Sigmoid 1 Tanh 35 ReLU 2 Leaky Relu 1+negative_slope22 SELU 43 小结 本小节将torch.nn.init中包含的初始化方法进行了分类介绍,主要分为Xavier和Kaiming方法与其它常数方法,并且回顾了torchvision中如何对一个模型所有层进行权重初始化的流程,希望对大家有所帮助。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-5/":{"url":"chapter-5/","title":"第五章 PyTorch 优化模块","keywords":"","body":"第五章 PyTorch 优化模块 第五章 PyTorch 优化模块 5.1 二十一个损失函数 5.2 十三个优化器 5.3 十四个学习率调整器 第五章简介 本章开始介绍模型优化过程中涉及的三大概念:损失函数、优化器和学习率调整。 由于损失函数、优化器、学习率调整的方法有非常多,仅pytorch官方实现(V1.10)的就有二十一个损失函数,十三个优化器,十四个学习率调整方法。 这几十种方法不会对每一个进行详细介绍,主要通过几个核心的方法为案例,进行剖析各模块的机制,如损失函数的Module如何编写、pytorch是如何构建loss.py体系、优化器如何更新模型中的参数、优化器常用函数、学习率调整机制等内容。 相信了解上述机制,便可举一反三,掌握更为复杂的方法函数。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-5/5.1-loss-function.html":{"url":"chapter-5/5.1-loss-function.html","title":"5.1 二十一个损失函数","keywords":"","body":"5.1 二十一个损失函数 本节重点为pytorch损失函数实现方式及逻辑,而非具体某个损失函数的公式计算,核心为下图: 损失函数——Loss Function 损失函数(loss function)是用来衡量模型输出与真实标签之间的差异,当模型输出越接近标签,认为模型越好,反之亦然。因此,可以得到一个近乎等价的概念,loss越小,模型越好。这样就可以用数值优化的方法不断的让loss变小,即模型的训练。 针对不同的任务有不同的损失函数,例如回归任务常用MSE(Mean Square Error),分类任务常用CE(Cross Entropy),这是根据标签的特征来决定的。而不同的任务还可以对基础损失函数进行各式各样的改进,如Focal Loss针对困难样本的设计,GIoU新增相交尺度的衡量方式,DIoU新增重叠面积与中心点距离衡量等等。 在pytorch中提供了二十一个损失函数,如下所示 nn.L1Loss nn.MSELoss nn.CrossEntropyLoss nn.CTCLoss nn.NLLLoss nn.PoissonNLLLoss nn.GaussianNLLLoss nn.KLDivLoss nn.BCELoss nn.BCEWithLogitsLoss nn.MarginRankingLoss nn.HingeEmbeddingLoss nn.MultiLabelMarginLoss nn.HuberLoss nn.SmoothL1Loss nn.SoftMarginLoss nn.MultiLabelSoftMarginLoss nn.CosineEmbeddingLoss nn.MultiMarginLoss nn.TripletMarginLoss nn.TripletMarginWithDistanceLoss 本小节讲解仅剖析nn.L1Loss和nn.CrossEntropyLoss这两个损失函数及其衍生函数。其余损失函数可以触类旁通。 核心知识在于损失函数的实现流程,不同的损失函数仅在于计算公式的不同,每个损失函数处理公式可在官方文档查阅。 以最简单的L1Loss出发,观察pytorch的损失函数是如何实现的 1. L1loss CLASS torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean') 功能: 计算output和target之差的绝对值,可选返回同维度的tensor(reduction=none)或一个标量(reduction=mean/sum)。 计算公式: 参数: size_average (bool, optional) – 已舍弃使用的变量,功能已经由reduction代替实现,仍旧保留是为了旧版本代码可以正常运行。reduce (bool, optional) – 已舍弃使用的变量,功能已经由reduction代替实现,仍旧保留是为了旧版本代码可以正常运行。reduction (string, optional) – 是否需要对loss进行“降维”,这里的reduction指是否将loss值进行取平均(mean)、求和(sum)或是保持原尺寸(none),这一变量在pytorch绝大多数损失函数中都有在使用,需要重点理解。 示例:代码 流程剖析 通过示例代码可知,loss_func是一个类实例,使用方式是loss_func(output, target)。 而nn.L1Loss是一个什么类?提供怎么样的接口来实现loss_func(output, target)的? 可跳转进入nn.L1Loss类定义,可以发现它继承_Loss,继续观察_Loss类,发现它继承nn.Module,既然是一个nn.Module,只需要在其内部实现一个forward()函数,就可以使类实例可以像函数一样被调用。 请看L1Loss类的实现: class L1Loss(_Loss): __constants__ = ['reduction'] def __init__(self, size_average=None, reduce=None, reduction: str = 'mean') -> None: super(L1Loss, self).__init__(size_average, reduce, reduction) def forward(self, input: Tensor, target: Tensor) -> Tensor: return F.l1_loss(input, target, reduction=self.reduction) L1Loss的forward函数接收两个变量,然后计算它们之差的绝对值,具体的实现委托给F.l1_loss函数 继续进入F.l1_loss一探究竟: def l1_loss( input: Tensor, target: Tensor, size_average: Optional[bool] = None, reduce: Optional[bool] = None, reduction: str = \"mean\", ) -> Tensor: if has_torch_function_variadic(input, target): return handle_torch_function( l1_loss, (input, target), input, target, size_average=size_average, reduce=reduce, reduction=reduction ) if not (target.size() == input.size()): warnings.warn( \"Using a target size ({}) that is different to the input size ({}). \" \"This will likely lead to incorrect results due to broadcasting. \" \"Please ensure they have the same size.\".format(target.size(), input.size()), stacklevel=2, ) if size_average is not None or reduce is not None: reduction = _Reduction.legacy_get_string(size_average, reduce) expanded_input, expanded_target = torch.broadcast_tensors(input, target) return torch._C._nn.l1_loss(expanded_input, expanded_target, _Reduction.get_enum(reduction)) F.l1_loss函数对输入参数相应的判断,例如传入的两个变量的维度必须一致,否则无法计算l1 loss。而具体公式的数值计算又委托给了torch._C._nn.l1_loss,torch._C._nn.l1_loss 就已经调用了python的C++拓展,底层代码是用C++语言编写,在python中就无法观察到,从这里大家可以知道pytorch大量的数值运算是借助了C++语言,毕竟python的底层运算比较慢。 关于C++底层代码,可依次观察: https://github.com/pytorch/pytorch/blob/master/torch/csrc/api/src/nn/modules/loss.cpp #include namespace F = torch::nn::functional; namespace torch { namespace nn { L1LossImpl::L1LossImpl(const L1LossOptions& options_) : options(options_) {} void L1LossImpl::reset() {} void L1LossImpl::pretty_print(std::ostream& stream) const { stream https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/native/Loss.cpp Tensor& l1_loss_out(const Tensor& input, const Tensor& target, int64_t reduction, Tensor& result) { if (reduction != Reduction::None) { auto diff = at::sub(input, target); auto loss = diff.is_complex() ? diff.abs() : diff.abs_(); if (reduction == Reduction::Mean) { return at::mean_out(result, loss, IntArrayRef{}); } else { return at::sum_out(result, loss, IntArrayRef{}); } } else { auto diff = input.is_complex() ? at::sub(input, target) : at::sub_out(result, input, target); return at::abs_out(result, diff); } } 从上述代码中可以看到,实际的L1Loss公式的实现是 auto diff = at::sub(input, target); auto loss = diff.is_complex() ? diff.abs() : diff.abs_(); 总结一下,Loss的实现流程如下图所示: 首先,损失函数继承Module,并实现forward函数,forward函数中完成具体公式计算;其次,具体的公式运算委托给nn.functional下函数实现;最后,pytorch大多的数值运算借助C++代码实现,具体在ATen/native/Loss.cpp 2. CrossEntropyLoss CLASS torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=- 100, reduce=None, reduction='mean', label_smoothing=0.0) 功能:先将输入经过softmax激活函数之后,再计算交叉熵损失。 在早期的pytorch中,是利用nn.LogSoftmax()和 nn.NLLLoss()实现的,现已经通过nn.CrossEntropyLoss()实现,不过官方文档中仍旧有提示:V1.11.0: \"Note that this case is equivalent to the combination of LogSoftmax and NLLLoss.\"V1.6.0: \"This criterion combines nn.LogSoftmax() and nn.NLLLoss() in one single class.\"\" 补充:小谈交叉熵损失函数交叉熵损失(cross-entropy Loss) 又称为对数似然损失(Log-likelihood Loss)、对数损失;二分类时还可称之为逻辑斯谛回归损失(Logistic Loss)。交叉熵损失函数表达式为 L = - sigama(y_i * log(x_i))。pytroch这里不是严格意义上的交叉熵损失函数,而是先将input经过softmax激活函数,将向量“归一化”成概率形式,然后再与target计算严格意义上交叉熵损失。 在多分类任务中,经常采用softmax激活函数+交叉熵损失函数,因为交叉熵描述了两个概率分布的差异,然而神经网络输出的是向量,并不是概率分布的形式。所以需要softmax激活函数将一个向量进行“归一化”成概率分布的形式,再采用交叉熵损失函数计算loss。 参数: weight (Tensor, optional) – 类别权重,用于调整各类别的损失重要程度,常用于类别不均衡的情况。 If given, has to be a Tensor of size Cignore_index (int, optional) – 忽略某些类别不进行loss计算。size_average (bool, optional) – 已舍弃使用的变量,功能已经由reduction代替实现,仍旧保留是为了旧版本代码可以正常运行。reduce (bool, optional) – 已舍弃使用的变量,功能已经由reduction代替实现,仍旧保留是为了旧版本代码可以正常运行。reduction (string, optional) – 是否需要对loss进行“降维”,这里的reduction指是否将loss值进行取平均(mean)、求和(sum)或是保持原尺寸(none),这一变量在pytorch绝大多数损失函数中都有在使用,需要重点理解。 label_smoothing (float, optional) – 标签平滑参数,一个用于减少方差,防止过拟合的技巧。详细请看论文《 Rethinking the Inception Architecture for Computer Vision》。通常设置为0.01-0.1之间,虽然理论值域为:A float in [0.0, 1.0]. 计算公式: 补图:https://pytorch.org/docs/1.11/generated/torch.nn.CrossEntropyLoss.html?highlight=crossentropyloss#torch.nn.CrossEntropyLoss C++底层代码实现: https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/native/LossNLL.cpp Tensor cross_entropy_loss( const Tensor& self, const Tensor& target, const c10::optional& weight, int64_t reduction, int64_t ignore_index, double label_smoothing) { Tensor ret; if (self.sizes() == target.sizes()) { // Assume soft targets when input and target shapes are the same TORCH_CHECK(at::isFloatingType(target.scalar_type()), \"Expected floating point type for target with class probabilities, got \", target.scalar_type()); TORCH_CHECK(ignore_index weight_maybe_owned = at::borrow_from_optional_tensor(weight); const Tensor& weight_ = *weight_maybe_owned; ret = cross_entropy_loss_prob_target(self, target, weight_, reduction, label_smoothing); } else if (label_smoothing > 0.0) { TORCH_CHECK(label_smoothing weight_maybe_owned = at::borrow_from_optional_tensor(weight); const Tensor& weight_ = *weight_maybe_owned; ret = cross_entropy_loss_label_smoothing(self, target, weight_, reduction, ignore_index, label_smoothing); } else { auto class_dim = self.dim() == 1 ? 0 : 1; ret = at::nll_loss_nd( at::log_softmax(self, class_dim, self.scalar_type()), target, weight, reduction, ignore_index); } return ret; } Tensor & nll_loss_out(const Tensor & self, const Tensor & target, const c10::optional& weight_opt, int64_t reduction, int64_t ignore_index, Tensor & output) { // See [Note: hacky wrapper removal for optional tensor] c10::MaybeOwned weight_maybe_owned = at::borrow_from_optional_tensor(weight_opt); const Tensor& weight = *weight_maybe_owned; Tensor total_weight = at::empty({0}, self.options()); return std::get(at::nll_loss_forward_out(output, total_weight, self, target, weight, reduction, ignore_index)); } Tensor nll_loss(const Tensor & self, const Tensor & target, const c10::optional& weight_opt, int64_t reduction, int64_t ignore_index) { // See [Note: hacky wrapper removal for optional tensor] c10::MaybeOwned weight_maybe_owned = at::borrow_from_optional_tensor(weight_opt); const Tensor& weight = *weight_maybe_owned; return std::get(at::nll_loss_forward(self, target, weight, reduction, ignore_index)); } Tensor nll_loss_nd( const Tensor& self, const Tensor& target, const c10::optional& weight, int64_t reduction, int64_t ignore_index) { if (self.dim() 4 auto n = input_.sizes()[0]; auto c = input_.sizes()[1]; auto out_size = input_.sizes().slice(2).vec(); out_size.insert(out_size.begin(), n); if (target_.sizes().slice(1) != input_.sizes().slice(2)) { TORCH_CHECK( false, \"Expected target size \", IntArrayRef(out_size), \", got \", target_.sizes()); } input_ = input_.contiguous(); target_ = target_.contiguous(); // support empty batches, see #15870 if (input_.numel() > 0) { input_ = input_.view({n, c, 1, -1}); } else { input_ = input_.view({n, c, 0, 0}); } if (target_.numel() > 0) { target_ = target_.view({n, 1, -1}); } else { target_ = target_.view({n, 0, 0}); } if (reduction != Reduction::None) { ret = at::nll_loss2d(input_, target_, weight, reduction, ignore_index); } else { auto out = at::nll_loss2d(input_, target_, weight, reduction, ignore_index); ret = out.view(out_size); } } return ret; } 示例:代码 CrossEntropyLoss使用注意事项: target需要的是int类型,不需要one-hot向量形式; 类别需要从0开始计数,即10分类任务,类别index应当为0,1,2,3,4,5,6,7,8,9 小结 本小节重点剖析两个损失函数,学习pytorch损失函数的实现逻辑,请详细观察以下关系图,对后续编写其它千奇百怪的损失函数很有帮助。在深度学习中,损失函数还有很多,这里无法一一列举,感兴趣可以了解一下:https://github.com/JunMa11/SegLoss 以及目标检测中的IoU、GIoU、DIoU、CIoU等。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-5/5.2-Optimizer.html":{"url":"chapter-5/5.2-Optimizer.html","title":"5.2 十三个优化器","keywords":"","body":"5.2 十三个优化器 Optimizer 简介 有了数据、模型和损失函数,就要选择一个合适的优化器(Optimizer)来优化该模型,使loss不断降低,直到模型收敛。本节将介绍pytorch中优化器——Optimizer。 优化器的实现在torch.optim中,torch.optim is a package implementing various optimization algorithms. 在其中有一个核心类是Optimizer,Optimizer在pytorch提供的功能是所有具体优化器的基类,它对优化器进行了抽象与定义,约定了一个优化器应有的功能,Optimizer与十三个优化器的关系如下图所示: 通过上图可知道Optimizer定义了优化器应当具备的基础功能,如获取状态数据,加载状态数据,梯度清零,执行一步优化和添加参数组。 不同的优化方法会继承Optimizer,同时只需要实现不同的step()即可。这一点与损失函数类似,不同的损失函数只需要在forward()函数中定义好不同的公式计算即可。 本小节将以SGD为例,深入讲解优化器的以下函数,并具体地观察SGD算法的实现过程。 state_dict(self) load_state_dict(self, state_dict) zero_grad(self, set_to_none: bool = False) step(self, closure) add_param_group(self, param_group) 优化器工作方式 优化器如何工作,使得模型精度逐渐提高的?在此就不详细讲解,请大家自行补充机器学习基础概念。 众所周知,优化器是根据权重的梯度作为指导,定义权重更新的力度,对权重进行更新。 过程很简单,实现起来却是复杂的,上述过程涉及几个问题: 梯度哪里来? 更新哪些权重? 怎么执行权重更新? 依次回答上述问题,便可熟悉优化器工作方式。 梯度哪里来? 梯度通过loss的反向传播,得到每个权重的梯度值,其中利用pytorch的autograd机制自动求导获得各权重的梯度。 (如果对autograd机制不熟悉,请查看第二章第六节) 更新哪些权重?通过loss的反向传播,模型(nn.Module)的权重(Parameter)上有了梯度(.grad)值,但是优化器对哪些权重进行操作呢?实际上优化器会对需要操作的权重进行管理,只有被管理的权重,优化器才会对其进行操作。在Optimizer基类中就定义了add_param_group()函数来实现参数的管理。通常在实例化的时候,第一个参数就是需要被管理的参数。 怎么执行权重更新?通过上述UML类图不难发现,step()函数是进行优化操作的,step()函数中实现了对所管理的参数进行更新的步骤。 总结一下:优化器在实例化时告诉它,需要对哪些参数进行管理,然后再每个iteration迭代时,借助loss.backward()得到梯度,接着优化器干活 optimizer.step()完成一步参数更新。 (此过程可以回顾第二章第二节的模型训练代码) 优化器基类 Optimizer Optimizer类是所有具体优化器的基类(Base class for all optimizers.) 下面分别介绍Optimizer的基础属性及方法。 属性: 参数组(param_groups): 在finetune、某层定制学习率,某层学习率置零操作中,都会设计参数组的概念,因此首先了解参数组的概念非常有必要。 参数组是用于管理需要进行优化的那些参数,例如权值weight,偏置bias,BN的alpha/beta等。 注意,这里是参数组不是参数,表明可以将所有参数进行分组,区别对待。 例如在finetune过程中,通常让前面层的网络采用较小的学习率,后面几层全连接层采用较大的学习率, 这是我们就要把网络的参数划分为两组,每一组有它对应的学习率。正是因为这种针对不同参数需要不同的更新策略的需求,才有了参数组的概念。 参数组是一个list,其元素是一个dict,dict中包含,所管理的参数,对应的超参,例如学习率,momentum,weight_decay等等。 案例:chapter-5/02_optimizer.py state: 用于存储优化策略中需要保存的一些缓存值,例如在用momentum时,需要保存之前的梯度,这些数据保存在state中。 defaults: 优化方法默认的超参数; 方法: zero_grad() 功能:清零所管理参数的梯度。由于pytorch不会自动清零梯度,因此需要再optimizer中手动清零,然后再执行反向传播,得出当前iteration的loss对权值的梯度。 step() 功能:执行一步更新,依据当前的梯度进行更新参数 add_param_group(param_group) 功能:给optimizer管理的参数组中增加一组参数,可为该组参数定制lr,momentum,weight_decay等,在finetune中常用。 例如:optimizer_1.add_param_group({'params': w3, 'lr': 0.001, 'momentum': 0.8}) state_dict() 功能:获取当前state属性。 通常在保存模型时同时保存优化器状态,用于断点保存,下次继续从当前状态训练; load_state_dict(state_dict) 功能:加载所保存的state属性,恢复训练状态。 对优化器工作方式熟悉后,再看Optimizer的属性和方法就简单了,下面通过一个具体的优化算法来熟悉完整的优化器使用。 SGD概念 torch.optim.SGD详情请参照[官方文档]( https://pytorch.org/docs/stable/generated/torch.optim.SGD.html#torch.optim.SGD) SGD(stochastic gradient descent,随机梯度下降)是深度学习模型优化过程中最基础、最受欢迎、最稳定的一个,即使优化算法层出不穷,仅pytorch就提供了十三个,但目前绝大多数论文中仍旧采用SGD进行训练,因此SGD必须掌握。 SGD核心理论知识是梯度下降( gradient descent),即沿着梯度的负方向,是变化最快的方向。 而随机则指的是一次更新中,采用了一部分样本进行计算,即一个batch的数据可以看作是整个训练样本的随机采样。更多关于SGD的理论知识请自行学习机器学习基础。 SGD更新公式可简化为 w新 = w旧 - w_grad,即参数减去梯度(学习率为1) 不过通常会加入学习率来调整更新的步伐:w_新 = w_旧 - (lr * w_grad) 对于加入L2正则(weight decay)时,变为:w_新 = w_旧 - (lr (w_grad + weight_decay w_旧)) L2正则称之为weight decay的原因,对比公式1发现W需要乘以一个小于1的系数,因此是衰减的:w_新 = w_旧 - (lr (w_grad + weight_decay w_旧)) = w_旧(1 - lrweight_decay) - (lr w_grad) 对于其它momentum 、dampening 和nesterov 的加入就不详细展开,可通过官方文档查阅: SGD使用 请结合代码观察SGD的使用 第一步:实例化:optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4) 第二步:loss.backward()之前进行梯度清零:optimizer.zero_grad() 第三步:loss.backward()之后执行一步更新:optimizer.step() 在代码中还有一处用到了optimizer,那就是学习率调整模块: scheduler = optim.lr_scheduler.StepLR(optimizer, gamma=0.1, step_size=50) 原理是将optimizer放到lr_scheduler进行管理,lr_scheduler会修改optimizer中的学习率 SGD代码实现 SGD类继承于Optimizer类,并重写step函数实现核心功能。 SGD类中step函数首先对各参数组、参数进行超参数的获取确定:for group in self.param_groups: 然后借助functional.sgd函数实现公式计算: F.sgd(params_with_grad, d_p_list, momentum_buffer_list, weight_decay=weight_decay, momentum=momentum, lr=lr, dampening=dampening, nesterov=nesterov, maximize=maximize,) 下面重点进入/torch/optim/_functional.py 的sgd()观察:这里为了讲解过程,省略了momentum的代码: d_p:是梯度 weight_decay:是权重衰减系数,如 0.0001 param:是具体的权重参数 lr:是学习率 依194行代码: param = param + (alpha)* d_p 依193行代码: param = param + (-lr) d_p = **param - lr \\ d_p** 依177行代码: param = param - lr (d_p + weight_decay param) = param(1 - lr * weight_decay) - lr*d_p 到这里,就可以与上述理论部分对应上了。 小结 其余十二个优化器可通过官方文档查阅,相信大家熟悉SGD以及优化器使用逻辑,其它的优化器都可轻松掌握。 Adadelta Implements Adadelta algorithm. Adagrad Implements Adagrad algorithm. Adam Implements Adam algorithm. AdamW Implements AdamW algorithm. SparseAdam Implements lazy version of Adam algorithm suitable for sparse tensors. Adamax Implements Adamax algorithm (a variant of Adam based on infinity norm). ASGD Implements Averaged Stochastic Gradient Descent. LBFGS Implements L-BFGS algorithm, heavily inspired by minFunc. NAdam Implements NAdam algorithm. RAdam Implements RAdam algorithm. RMSprop Implements RMSprop algorithm. Rprop Implements the resilient backpropagation algorithm. SGD Implements stochastic gradient descent (optionally with momentum). 下面梳理pytorch整个优化器实现的逻辑关系: Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-5/5.3-lr-scheduler.html":{"url":"chapter-5/5.3-lr-scheduler.html","title":"5.3 十四个学习率调整器","keywords":"","body":"5.3 学习率调整策略 深度学习模型训练中调整最频繁的就属学习率了,好的学习率可以使模型逐渐收敛并获得更好的精度。 本小节将介绍pytorch提供的十四种学习率调整方法,首先了解lr_scheduler整体结构设计,然后以StepLR为例,深度剖析一个学习率调整器的源代码实现细节,最后总结十四个学习率调整策略。 lr_scheduler 设计 lr_scheduler模块的设计与优化器一样,定义了一个核心基类_LRScheduler,在_LRScheduler中设计好学习率调整器的属性与方法。 核心属性有optimizer、base_lrs和last_epoch。 optimizer是调整器所管理的优化器,优化器中所管理的参数组有对应的学习率,调整器要调整的内容就在那里。 base_lrs是基础学习率,来自于optimizer一开始设定的那个值。self.base_lrs = [group['initial_lr'] for group in optimizer.param_groups] last_epoch是记录迭代次数,通常用于计算下一轮学习率。注意,默认初始值是-1,因为last_epoch的管理逻辑是执行一次,自加1。 核心方法有state_dict()、load_state_dict()、get_last_lr()、get_lr()、print_lr()、step()。 state_dict()和load_state_dict()分别是获取调整器的状态数据与加载状态数据。 get_last_lr()、get_lr() 分别为获取上一次和当前的学习率。 print_lr()是打印学习率。 step()为更新学习率的接口函数,使用者调用 scheduler.step()即完成一次更新。 lr_scheduler 使用流程 第一步:实例化。scheduler = optim.lr_scheduler.StepLR(optimizer, gamma=0.1, step_size=50) 第二步:合适的位置执行step()。大家要注意,不同的调整器的更新策略是不一样的,有的是基于epoch维度,有的是基于iteration维度,这个需要注意。 StepLR 源代码解读 StepLR是依据设定的step_size,以固定的间隔进行调整,调整规则为 lr_new = lr_old * gamma, 通常gamma设置为0.1,即一定次数训练后,学习率下降10倍。 下面观察代码,在代码104行与146行打断点进行调试,依次观察初始化过程、step更新过程。 初始化过程: StepLR类__init__()函数: self.step_size = step_size self.gamma = gamma super(StepLR, self).__init__(optimizer, last_epoch, verbose) 跳转到_LRScheduler类__init__()函数: 第一步:获取初始学习率:group.setdefault('initial_lr', group['lr']) 第二步:记录基础学习率:self.base_lrs = [group['initial_lr'] for group in optimizer.param_groups] self.last_epoch = last_epoch 第三步:执行首次step(),也就是在初始化的时候会自动执行一次step(),这也是为什么last_epoch的默认值是-1的原因。 step中会进行第0个epoch的学习率获取,具体过程如代码所示: torch/optim/lr_scheduler.py 151至168行 with _enable_get_lr_call(self): if epoch is None: self.last_epoch += 1 values = self.get_lr() else: warnings.warn(EPOCH_DEPRECATION_WARNING, UserWarning) self.last_epoch = epoch if hasattr(self, \"_get_closed_form_lr\"): values = self._get_closed_form_lr() else: values = self.get_lr() for i, data in enumerate(zip(self.optimizer.param_groups, values)): param_group, lr = data param_group['lr'] = lr self.print_lr(self.verbose, i, lr, epoch) self._last_lr = [group['lr'] for group in self.optimizer.param_groups] step更新过程 在初始化过程已经进行过一次step,这里再仔细分析一遍。 首先通过154行代码,获取新一轮学习率 values = self.get_lr() 然后通过165行代码,对优化器中的学习率进行更新param_group['lr'] = lr 可以发现step()函数由基类_LRScheduler实现,即所有学习率调整器均采用这个流程,具体的一步更新策略则委托给子类的.get_lr()函数,这样的架构设计值得学习。 此处,关注的是StepLR,下面深入 StepLR.get_lr()函数观察。 if (self.last_epoch == 0) or (self.last_epoch % self.step_size != 0): return [group['lr'] for group in self.optimizer.param_groups] return [group['lr'] * self.gamma for group in self.optimizer.param_groups] 可以看到,核心在两个return行。 第一个return表示未达到step_size,新学习率保持不变 第二个return表示达到step_size,学习率均需要乘以 gamma。 至此,整个学习率更新过程及逻辑讲解完毕,其余十三个学习率调整整体逻辑一样,均是step()与get_lr()的配合来实现学习率调整,请大家举一反三,自行深入研究其它调整器。 十四个调整器汇总 lr_scheduler.LambdaLR Sets the learning rate of each parameter group to the initial lr times a given function. lr_scheduler.MultiplicativeLR Multiply the learning rate of each parameter group by the factor given in the specified function. lr_scheduler.StepLR Decays the learning rate of each parameter group by gamma every step_size epochs. lr_scheduler.MultiStepLR Decays the learning rate of each parameter group by gamma once the number of epoch reaches one of the milestones. lr_scheduler.ConstantLR Decays the learning rate of each parameter group by a small constant factor until the number of epoch reaches a pre-defined milestone: total_iters. lr_scheduler.LinearLR Decays the learning rate of each parameter group by linearly changing small multiplicative factor until the number of epoch reaches a pre-defined milestone: total_iters. lr_scheduler.ExponentialLR Decays the learning rate of each parameter group by gamma every epoch. lr_scheduler.CosineAnnealingLR Set the learning rate of each parameter group using a cosine annealing schedule, where \\eta{max}ηmax is set to the initial lr and T{cur}Tcu**r is the number of epochs since the last restart in SGDR: lr_scheduler.ChainedScheduler Chains list of learning rate schedulers. lr_scheduler.SequentialLR Receives the list of schedulers that is expected to be called sequentially during optimization process and milestone points that provides exact intervals to reflect which scheduler is supposed to be called at a given epoch. lr_scheduler.ReduceLROnPlateau Reduce learning rate when a metric has stopped improving. lr_scheduler.CyclicLR Sets the learning rate of each parameter group according to cyclical learning rate policy (CLR). lr_scheduler.OneCycleLR Sets the learning rate of each parameter group according to the 1cycle learning rate policy. lr_scheduler.CosineAnnealingWarmRestarts Set the learning rate of each parameter group using a cosine annealing schedule, where \\eta{max}ηmax is set to the initial lr, T{cur}Tcu**r is the number of epochs since the last restart and T_{i}T**i is the number of epochs between two warm restarts in SGDR: 小结 本小节通过lr_scheduler结构设计出发,分析十四个优化器是如何组织实现的,并通过_LRScheduler基类与StepLR的代码讲解学习率调整的机制。 总结下来即:初始化时建立scheduler与optimizer的联系,scheduler在每次step()时修改optimizer中的lr,step()获取新lr的功能委托给get_lr()函数实现。 最后对六个学习率调整器进行了测试并绘图,绘图代码位于配套代码 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/":{"url":"chapter-6/","title":"第六章 PyTorch 可视化模块","keywords":"","body":"第六章 PyTorch 可视化模块 第六章 PyTorch 可视化模块 6.1 TensorBoard安装与使用 6.2 CNN卷积核与特征图可视化 6.3 混淆矩阵与训练曲线可视化 6.4 CAM可视化与hook函数使用 6.5 模型参数打印 第六章简介 本章介绍可视化工具,包括TensorBoard可视化工具,混淆矩阵,CNN卷积核与特征图可视化,分类模型注意力算法——Grad CAM,模型参数量可视化。 首先对强大的可视化工具TensorBoard进行讲解,介绍其提供的十多个数据可视化API。 然后借助Tensorboard的绘图功能,观察CNN卷积核与特征图的变化过程,同时对分类混淆矩阵进行分析。 接着介绍一个实用的分类模型注意力机制可视化算法——Grad CAM。 最后介绍一系列对模型参数量分析的工具。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.1-tensorboard.html":{"url":"chapter-6/6.1-tensorboard.html","title":"6.1 TensorBoard安装与使用","keywords":"","body":"6.1 Tensorboard 基础与使用 Tensorboard是TensorFlow中提供的可视化工具,它能可视化数据曲线、模型拓扑图、图像、统计分布曲线等。 在PyTorch中,早期是不支持Tensorboard,采用了TensorboardX作为替身,现在PyTorch已经支持Tensorboard的使用,本节就介绍Tensorboard工具的概念、原理以及使用方法。 tensorboard 安装 首先运行以下代码,观察报错,通过报错信息指引我们安装tensorboard。 import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) from torch.utils.tensorboard import SummaryWriter log_dir = BASE_DIR # 即test_tensorboard.py文件所在目录 writer = SummaryWriter(log_dir=log_dir, filename_suffix=\"_test_tensorboard\") # writer = SummaryWriter(comment=\"test01\", filename_suffix=\"_test_tensorboard\") x = range(100) for i in x: writer.add_scalar('y=2x', i * 2, i) writer.add_scalar('y=pow(2, x)', 2 ** i, i) writer.close() 直接运行代码,提示: import tensorboard ModuleNotFoundError: No module named 'tensorboard' 只需要在命令窗口中执行: pip install tensorboard 重新运行代码,获得event file文件,《events.out.tfevents.1654508983.dream.5756.0》 tensorboard 初体验 通过以上步骤得到了一个event file 文件,下面需要启动tensorboard软件对event file文件进行可视化。 tensorboard基本原理是这样的 python代码中将可视化的数据记录到event file中,保存至硬盘 采用tensorboard对event file文件进行读取,并在web端进行可视化 启动步骤如下: tensorboard --logdir=your path dir 在terminal中执行以下命令即可,注意必须是文件夹,不能是文件名,tensorboard会将文件夹下所有event file都可视化 得到TensorBoard 2.0.0 at http://localhost:6006/ (Press CTRL+C to quit)之类的提示 然后复制网址http://localhost:6006/ 到浏览器中进行打开,就得到如下界面 在tensorboard启动的过程中,可能发生的问题: 1. 6006端口被占用: port 6006 was already in use E0117 15:58:38.631224 MainThread program.py:260] TensorBoard attempted to bind to port 6006, but it was already in use TensorBoard attempted to bind to port 6006, but it was already in use 解决方法:修改端口为6007 tensorboard --logdir=your_dir --port=6007 到这里可以知道,tensorboard软件是一个web应用,它对指定目录下的event file进行可视化,可视化页面拥有一系列功能按钮,供开发者使用。 通过demo代码,我们绘制了两条曲线,除了绘制曲线,tensorboard还又许多强大可视化功能,下面就介绍如何进行其它数据类型的可视化。 SummaryWriter类介绍 tensorboard环境配置好了,下面要重点学习在python代码中如何把各类数据合理的写入event file,然后用tensorboard软件进行可视化。 在pytorch代码中,提供了SummaryWriter类来实现数据的写入,请阅读官方文档对SummaryWriter的描述: The SummaryWriter class provides a high-level API to create an event file in a given directory and add summaries and events to it. The class updates the file contents asynchronously. This allows a training program to call methods to add data to the file directly from the training loop, without slowing down training. 首先来学习SummaryWriter的参数设置,然后学习它提供的一些列写入方法,如add_scalars、add_histogram和add_image等等。 CLASS torch.utils.tensorboard.writer.SummaryWriter(log_dir=None, comment='', purge_step=None, max_queue=10, flush_secs=120, filename_suffix='') 属性: log_dir (string) – 文件保存目录设置,默认为 runs/current_datetime_hostname comment (string) – 当log_dir采用默认值时,comment字符串作为子目录 purge_step (int) – ? max_queue (int) –? flush_secs (int) – 磁盘刷新时间,默认值为120秒 filename_suffix (string) –文件名后缀 方法: add_scalar add_scalar(tag, scalar_value, global_step=None, walltime=None, new_style=False, double_precision=False) 功能:添加标量;tag的设置可以有个技巧是在同一栏下绘制多个图,如'Loss/train', 'Loss/Valid', 这就类似于matplotlib的subplot(121), subplot(122) tag (string) – Data identifier scalar_value (float or string/blobname) – Value to save global_step (int) – Global step value to record 代码实现: add_scalars add_scalars(main_tag, tag_scalar_dict, global_step=None, walltime=None) 功能:在一个坐标轴中绘制多条曲线。常用于曲线对比。 main_tag (string) – The parent name for the tags tag_scalar_dict (dict) – Key-value pair storing the tag and corresponding values global_step (int) – Global step value to record 核心在于tag_scalar_dict 字典中存放多条曲线的数值。 代码实现: add_histogram add_histogram(tag, values, global_step=None, bins='tensorflow', walltime=None, max_bins=None) 功能:绘制直方图。这里的global_step表明会得到多个直方图,详情请看图理解。 在tensorboard界面,需要进入HISTOGRAM中才能看到直方图可视化。 tag (string) – Data identifier values (torch.Tensor, numpy.array, or string/blobname) – Values to build histogram global_step (int) – Global step value to record bins (string) – One of {‘tensorflow’,’auto’, ‘fd’, …}. This determines how the bins are made. 代码实现: add_image add_image(tag, img_tensor, global_step=None, walltime=None, dataformats='CHW') 功能:绘制图像。 tag (string) – Data identifier img_tensor (torch.Tensor, numpy.array, or string/blobname) – Image data global_step (int) – Global step value to record dataformats- 数据通道顺序物理意义。默认为 CHW 代码实现: add_images add_images(tag, img_tensor, global_step=None, walltime=None, dataformats='NCHW') 功能:绘制图像序列,常用于数据清洗,卷积核,特征图的可视化。 Add batched image data to summary.Note that this requires the pillow package. tag (string) – Data identifier img_tensor (torch.Tensor, numpy.array, or string/blobname) – Image data global_step (int) – Global step value to record walltime (float) – Optional override default walltime (time.time()) seconds after epoch of event dataformats (string) – Image data format specification of the form NCHW, NHWC, CHW, HWC, HW, WH, etc. 代码实现: add_figure add_figure(tag, figure, global_step=None, close=True, walltime=None) 功能:将matplotlib的figure绘制到tensorboard中。 Render matplotlib figure into an image and add it to summary.Note that this requires the matplotlib package. tag (string) – Data identifier figure (matplotlib.pyplot.figure) – Figure or a list of figures global_step (int) – Global step value to record close (bool) – Flag to automatically close the figure walltime (float) – Optional override default walltime (time.time()) seconds after epoch of event 剩下一些高级函数,就不一一讲解,用法雷同,再次仅汇总,供需使用。 add_video:绘制视频 add_audio:绘制音频,可进行音频播放。 add_text:绘制文本 add_graph:绘制pytorch模型拓扑结构图。 add_embedding:绘制高维数据在低维的投影 add_pr_curve:绘制PR曲线,二分类任务中很实用。 add_mesh:绘制网格、3D点云图。 add_hparams:记录超参数组,可用于记录本次曲线所对应的超参数。 小结 pytorch中使用tensorboard非常简单,只需要将想要可视化的数据采用SummaryWriter类进行记录,存在硬盘中永久保存,然后借助tensorboard软件对event file进行可视化。 tensorboard是非常强大的可视化工具,可以很好帮助开发者分析模型开发过程中的各个状态,如监控loss曲线观察模型训练情况,绘制梯度分布直方图观察是否有梯度消失,绘制网络拓扑图观察网络结构等,请大家多留意SummaryWriter官方文档的介绍,了解最新SummaryWriter有什么方法可用。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.2-cnn-visualization.html":{"url":"chapter-6/6.2-cnn-visualization.html","title":"6.2 CNN卷积核与特征图可视化","keywords":"","body":"6.2 CNN卷积核与特征图可视化 众所周知,深度学习仍是一个黑盒子,模型内部的逻辑含义仍旧无法解释,越是未知的东西,越能激起人们的好奇心。 在卷积神经网络中,有时会对卷积核以及特征图进行可视化,以此观察卷积神经网络学习到了何种模式。 作为深度卷积神经网络的开山之作,AlexNet(2012年)就已经对卷积核的模式进行了分析,论文中发现卷积核的学习具有偏向性,一部分学习颜色特征,一部分学习边缘特征,详见下图: 紧接着AlexNet之后的2013年,ZFNet对CNN的特征图进行了可视化,进一步的探究卷积神经网络的奥秘。 AlexNet:《ImageNet Classification with Deep Convolutional Neural Networks》 ZFNet:《Visualizing and understanding convolutional networks》 本节就利用tensorboard以及pytorch的函数对AlexNet的卷积核与特征图进行可视化。 make_grid 函数 在图像任务中,往往需要人眼审核、观察大批量图像数据,如果一张一张的观察,效率会非常低。 通常会将一批数据绘制成网格图片,类似大排档的菜单一样,这样便于观察。 在pytorch的torchvision库中,提供了make_grid函数帮助大家完成网格图片制作。下面先学习make_grid函数,再用它绘制卷积核与特征图。 `torchvision.utils.make_grid(tensor: Union[torch.Tensor, List[torch.Tensor]], nrow: int = 8, padding: int = 2, normalize: bool = False, value_range: Optional[Tuple[int, int]] = None, scale_each: bool = False, pad_value: float = 0.0, **kwargs) 功能: 将一组图片拼接成一张网格图片,便于可视化。 参数: tensor(Tensor or list)- 需可视化的数据,shape:(B x C x H x W) ,B表示batch数,即几张图片 nrow(int)- 一行显示几张图,默认值为8。 padding(int)- 每张图片之间的间隔,默认值为2。 normalize(bool)- 是否进行归一化至(0,1)。 value_range(tuple)- 设置归一化的min和max,若不设置,默认从tensor中找min和max。 scale_each(bool)- 每张图片是否单独进行归一化,还是min和max的一个选择。 pad_value(float)- 填充部分的像素值,默认为0,即黑色。 关于输入:make_grid的输入可以分为两种: 一种是4D张量,函数自动将第一维度作为图片数量进行拆解。 一种是list,元素必须是张量形式,并且张量的shape必须一致。 这两种输入分别对应两种常用场景: 4D张量:卷积核大小与特征图张量都适合用这种形式。 list:对普通图片进行可视化观察,一次加载一张图片时使用。 另外,对于像素的转换也需要注意相应策略,对于float类型的数据,需要设置归一化的策略,策略由value_range和scale_each构成,请自行调整观察变化。 请看代码使用效果: Alexnet卷积核可视化 要对卷积核进行可视化,就需要对pytorch的nn.Module类非常熟悉,要了解卷积核以怎样的形式?存储在哪里? 2D卷积的卷积核权重是一个4D张量,包含输入通道,输出通道,高,宽。 注意:除了第一层可以将 输入通道 *高*宽作为 RGB图像进行可视化之外,其余网络层只能将高*宽作为灰度图像(2D)进行可视化。 卷积核存储在nn.Conv2D的weight变量中,下面就可以通过如下代码获得。 for sub_module in alexnet.modules(): # 非卷积层则跳过 if isinstance(sub_module, nn.Conv2d): # 获取conv2d层的权重,即卷积核权重 kernels = sub_module.weight 有了4D张量,剩下就按部就班的绘制到grid中可视化即可,完整代码生成的可视化图像如下所示: 可以看到,alexnet模型的第一层的卷积核确实学习到了不同模式,有边缘模式,有色彩模式。 Alexnet特征图可视化 特征图可视化与卷积核可视化类似,需要知道特征图以怎样的形式?从哪里获得? 常规任务中,特征图是4D张量(BCHW)。 但是获得就没有那么简单,因为特征图是中间数据,通常不会保留,在前向运算过程中,不再使用的特征图会被舍弃。 因此需要特殊方法获得特征图,本节介绍一种笨办法,但容易理解,就是将对应层(仍旧是一个nn.Module)拿出来,然后把图片仍给网络层(仍旧是一个nn.Module),其输出的就是特征图了。 更高级的方法是利用hook函数机制完成中间特征图的获取,这个在本章的后半部分会介绍。 请看核心代码 alexnet = models.alexnet(pretrained=True) # forward convlayer1 = alexnet.features[0] fmap_1 = convlayer1(img_tensor) 这样就获得了(1, 64, 55, 55)的4D张量(fmap_1),然后将其转换成能可视化的形式,再利用tensorboard绘制。 完整代码生成的可视化图像如下所示: 小结 本节介绍了tensorboard对卷积核与特征图的绘制,其中涉及非常实用的函数make_grid,make_grid可以帮助开发人员高效的审核图片,人眼看图是算法工程师最重要的一步,因为模型是没办法知道哪张图片的标签搞错了! 下一小节将介绍在模型训练过程中,如何基于tensorboard进行监控模型状态。 最后留一个思考题,将卷积核与特征图放到一起去比较,大家能否找到什么规律? 边缘模式的卷积核似乎能过滤掉大部分细节,仅留下边缘;还能看到清晰原图信息的特征图所对应的卷积核,都是颜色卷积核。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.3-conf_matrix.html":{"url":"chapter-6/6.3-conf_matrix.html","title":"6.3 混淆矩阵与训练曲线可视化","keywords":"","body":"6.3 混淆矩阵与训练曲线可视化 在分类任务中,通过混淆矩阵可以看出模型的偏好,而且对每一个类别的分类情况都了如指掌,为模型的优化提供很大帮助。本节将介绍混淆矩阵概念及其可视化。 为了演示混淆矩阵与训练曲线,本节代码采用cifar10数据集进行训练,模型采用resnet系列。 数据cifar-10-python.tar.gz 可从 \"https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz\" 下载,放到指定文件夹节课,无需解压,代码会自动解压。 混淆矩阵概念 混淆矩阵(Confusion Matrix)常用来观察分类结果,其是一个N*N的方阵,N表示类别数。 混淆矩阵的行表示真实类别,列表示预测类别。例如,猫狗的二分类问题,有猫的图像10张,狗的图像30张,模型对这40张图片进行预测,得到的混淆矩阵为 阿猫 阿狗 阿猫 7 3 阿狗 10 20 从第一行中可知道,10张猫的图像中,7张预测为猫,3张预测为狗,猫的召回率(Recall)为7/10 = 70%, 从第二行中可知道,30张狗的图像中,8张预测为猫,22张预测为狗,狗的召回率为20/30 = 66.7%, 从第一列中可知道,预测为猫的17张图像中,有7张是真正的猫,猫的精确度(Precision)为7 / 17 = 41.17% 从第二列中可知道,预测为狗的23张图像中,有20张是真正的狗,狗的精确度(Precision)为20 / 23 = 86.96% 模型的准确率(Accuracy)为 7+20 / 40 = 67.5% 可以发现通过混淆矩阵可以清晰的看出网络模型的分类情况,若再结合上颜色可视化,可方便的看出模型的分类偏好。 本小节将介绍,混淆矩阵的统计及其可视化。 混淆矩阵的统计 混淆矩阵的绘制将借助matplotlib的imshow功能,在imshow中可对矩阵进行上色,colorbar可自行调整,如本例中采用的黑白调,也可以选择其他的colorbar。 在模型训练中,通常以一个epoch为单位,进行混淆矩阵的统计,然后绘制,代码思路如下: 第一步:创建混淆矩阵 获取类别数,创建N*N的零矩阵 conf_mat = np.zeros([cls_num, cls_num]) 第二步:获取真实标签和预测标签 labels 为真实标签,通常为一个batch的标签 predicted为预测类别,与labels同长度 第三步:依据标签为混淆矩阵计数 for j in range(len(labels)): cate_i = labels[j].cpu().numpy() pre_i = predicted[j].cpu().numpy() conf_mat[cate_i, pre_i] += 1. 混淆矩阵可视化 混淆矩阵可视化已经封装成一个函数show_conf_mat,函数位于 配套代码 show_conf_mat(confusion_mat, classes, set_name, out_dir, epoch=999, verbose=False, perc=False) 参数: \"\"\" 混淆矩阵绘制并保存图片 :param confusion_mat: nd.array :param classes: list or tuple, 类别名称 :param set_name: str, 数据集名称 train or valid or test? :param out_dir: str, 图片要保存的文件夹 :param epoch: int, 第几个epoch :param verbose: bool, 是否打印精度信息 :param perc: bool, 是否采用百分比,图像分割时用,因分类数目过大 :return: \"\"\" show_conf_mat函数内部原理就不再详细展开,都是matplotlib的基础知识。下图为最终效果图: show_conf_mat函数提供png的保存,不便于观察整个训练过程的变化,这里借助tensorboard的add_figure功能,将每个epoch的混淆矩阵保存到tensorboard中,然后可拖拽的形式观察模型精度的变化情况。 效果如下图: 从上述变化可以发现模型在迭代过程中的偏好,前后对比图可很好的帮助工程师分析模型的偏好。 当global_step比较多的时候,toolbar无法展示每一个step,这需要在启动tensorboard的时候设置一下参数即可 tensorboard --logdir=./Result --samples_per_plugin images=200 除了手动绘制之外,sklearn库也提供了混淆矩阵绘制(from sklearn.metrics import confusion_matrix),这里不再拓展。 训练曲线绘制 除了混淆矩阵,在模型训练过程中最重要的是观察loss曲线的变化,loss曲线变化趋势直接决定训练是否需要停止,并指引我们进行参数的调整。 loss曲线是需要将训练与验证放在一起看的,单独看一条曲线是不够的,这一点需要大家了解模型评估中的方差与偏差的概念。 通过训练loss看偏差,通过训练loss与验证loss看方差。 偏差看的是模型拟合能力是否足够,方差是看模型泛化性能是否足够,是否存在过拟合。 将两条曲线绘制到一个坐标系里,可以借助tensorboard的add_scalars函数,具体请看代码 在训练集的迭代之后记录训练集的loss writer.add_scalars('Loss_group', {'train_loss': loss_avg}, epoch) 在验证集的迭代之后记录训练集的loss writer.add_scalars('Loss_group', {'valid_loss': loss_avg}, epoch) 在这里可以发现,SummaryWriter类的函数是以tag变量进行区分不同的坐标系,以上例子看出,虽然在两个地方执行代码,但是通过tag=\"Loss_group\",仍旧可以把它们绘制在一个坐标系里。 小结 以上就是在训练过程中记录必要的训练信息,用于监控模型训练状态。 下一节将介绍有趣的CAM可视化实现,以及nn.Module模块中的系列hook函数使用。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.4-cam-vis.html":{"url":"chapter-6/6.4-cam-vis.html","title":"6.4 CAM可视化与hook函数使用","keywords":"","body":"6.4 CAM可视化与hook函数使用 前文说到,在本章第二节介绍CNN的可视化时我们知道,深度学习模型仍是一个黑箱,大家想尽办法对其进行可视化,本节就介绍一个实用的分析方法CAM(Class activation mapping,类激活图),如下图所示: 以上图为例,CAM方法是探究模型将图片分为“Dog”的依据是什么,即图片中哪些区域支撑了模型将图片判别为\"Dog\"。 其背后的原理是将网络中间的特征图进行加权并可视化,而权重的得来就有多种方法,不同的方法就有了不同的CAM算法,如CAM、Grad CAM和Grad CAM++。 CAM:《2016-CAM-Learning Deep Features for Discriminative Localization》 Grad-CAM:《2017-Grad-CAM Visual Explanations from Deep Networks via Gradient-based Localization》 Grad-CAM++:《2018-Grad-CAM++ Generalized Gradient-based Visual Explanations for Deep Convolutional Networks》 本文将介绍其演变过程,并用手写代码实现Grad CAM,同时借助强大的github仓库,实现多种热力图对比,如下图所示: CAM 论文名称:《Learning Deep Features for Discriminative Localization》 原理解释:CAM方法需要将CNN模型修改后重新训练,修改的目的就是为了获得加权权重,具体方式如下图所示: 将最后一层特征图之后的层移除,并接入一个全连接层用于分类,全连接层的输入是特征图的全局池化后的特征向量。 最终热力图通过分类类别神经元所对应的权重w,乘以最后一层特征图,再求和即可。 背后逻辑有两个: 1、最后一层特征图是原图经过一系列卷积后保留下来的特征,其与原图的空间关系一一对应,空间关系指左上角对应左上角,右下角对应原图右下角,等等。 2、特征图对于分类类别(如上图的AustralianTerrier)的贡献程度与全连接层权重密切相关 因此,只需要利用全连接层的权重乘以对应通道,即可得到热力图。 Grad CAM 论文名称:《Grad-CAM Visual Explanations from Deep Networks via Gradient-based Localization》 CAM的思路非常巧妙,但缺点很明显,它需要对模型进行修改,并且重新训练,不适用于大多数场景。 为此,研究者提出额Grad CAM,可以对模型直接进行观察,无需改动模型。 Grad CAM的思路也非常巧妙,在CAM思路中有两个要素: 1、特征图 2、特征图对应指定类别的权重 特征图很容易获得,但特征图的重要性(加权权重)应该如何寻找? Grad CAM给出了不一样的答案,Grad CAM利用梯度求导获得特征图的重要性权重。 原理分析: 假设最后一层feature maps有10个,那么如何寻找10个权值用来指示这些feature maps对某一类别的重要性呢? CAM是通过对feature maps进行GAP,然后采用该类别与这个10个GAP后的神经元的连接权值作为权值; 而Grad-CAM采用的则是梯度,是该类别对于这10个feature maps的求取梯度。 注意:最终求取的梯度是一个110的向量,即每个feature map对应一个标量,而对feature maps求取梯度是一个矩阵,作者是通过对*矩阵求均值得到的标量。 对于类别c,特征图的权值 a_k^c 计算公式如下: c表示第c类,k表示第k个特征图,Z表示特征图像素点总数,i表示行,j表示列,A表示特征图。 公式可以分两部分看,最右边 gradients via backprop,即对类别c求得特征图的梯度,是一个二维的矩阵; 再通过左边的global average pooling,对二维的梯度矩阵求平均值,得到第k个特征图对于第c类的权值。 示意图如下: 最终的热力图通过以下公式进行求取: 与CAM不同的是,Grad-CAM在加权求和后还加上了ReLU函数,计算公式如下: 之所以加上ReLU,是因为在这里只关注正的梯度值,而不关注负的。 最后将Discriminative Localization Map直接resize至原图大小即可,如最后一层feature maps是14*14的,原图是224*224,则将14*14的图缩放到224*224即可。 Grad-CAM++ 论文名称:《Grad-CAM++ Generalized Gradient-based Visual Explanations for Deep Convolutional Networks》 事物总是在不断的发展,Grad CAM还存在以下缺点: 当图片出现多个同类物体时,无法定位;(1-3列) 单物体图片中,定位错误。(4-6列) 并且, Grad-CAM中一个特征图对应的权重是对特征图的梯度求平均,即认为特征图的梯度(2维数据)上的每个元素同等重要。 而Grad-CAM++则认为特征图的梯度上的每个元素重要程度应当不一样,因此对Grad CAM进行了改进。 Grad-CAM++ 热力图的权重计算通过以下公式: CAM系列对比 CAM、Grad-CAM和Grad-CAM++的区别如下图所示: 关于CAM还有很多迭代改进,可参考这个repo repo中对数十个CAM进行了实现,建议使用。 本节将用代码手动的实现Grad CAM,并通过算法的实现来学习nn.module中的hook函数使用。 nn.module中的hook函数 在CAM系列算法中知道,需要利用中间层的特征图,可nn.module并不会返回、保留中间层的特征图。这时,就要用到nn.module中的hook函数,把中间层特征图给拿出来。 什么是hook函数? pytorch中的hook是一个非常有意思的概念,hook意为钩、挂钩、鱼钩。 引用知乎用户“马索萌”对hook的解释:“(hook)相当于插件。可以实现一些额外的功能,而又不用修改主体代码。把这些额外功能实现了挂在主代码上,所以叫钩子,很形象。” 简单讲,就是不修改主体,而实现额外功能。对应到在pytorch中,主体就是forward和backward,而额外的功能就是对模型的变量进行操作 pytorch提供的hook 1. torch.Tensor.register_hook 功能:注册一个反向传播hook函数,这个函数是Tensor类里的,当计算tensor的梯度时自动执行。 形式: hook(grad) -> Tensor or None ,其中grad就是这个tensor的梯度。 返回值:a handle that can be used to remove the added hook by calling handle.remove() 案例请看配套代码 2. torch.nn.Module.register_forward_hook 功能:Module前向传播中的hook,module在前向传播后,自动调用hook函数。 形式:hook(module, input, output) -> None。注意不能修改input和output 返回值 其中,module是当前网络层,input是网络层的输入数据, output是网络层的输出数据 应用场景:如用于提取特征图 案例请看配套代码 3. torch.nn.Module.register_forward_pre_hook 功能:执行forward()之前调用hook函数。 形式:hook(module, input) -> None or modified input 应用场景举例:暂时没碰到过,希望读者朋友补充register_forward_pre_hook相关应用场景。 registerforwardprehook与forwardhook一样,是在module.__call中注册的,与forward_hook不同的是,其在module执行forward之前就运行了,具体可看module.__call中的代码,第一行就是执行forward_pre_hook的相关操作。 4. torch.nn.Module.register_full_backward_hook 功能:Module反向传播中的hook,每次计算module的梯度后,自动调用hook函数。 形式:hook(module, grad_input, grad_output) -> tuple(Tensor) or None 注意事项:当module有多个输入或输出时,grad_input和grad_output是一个tuple。 返回值:a handle that can be used to remove the added hook by calling handle.remove() 应用场景举例:例如提取特征图的梯度 Grad CAM 手动实现 下面就利用 register_forward_hook 和 register_full_backward_hook 来实现Grad CAM 详情请看配套代码 整体思路如下: 对模型最后一个卷积层进行hook函数注册,两个hook分别记录特征图于梯度 def backward_hook(module, grad_in, grad_out): grad_block.append(grad_out[0].detach()) def farward_hook(module, input, output): fmap_block.append(output) ------------------------------------------------------------ # 注册hook resnet_50.layer4[-1].register_forward_hook(farward_hook) resnet_50.layer4[-1].register_full_backward_hook(backward_hook) 获取类别loss,类别loss为分类类别最大的那个神经元的值,具体由comp_class_vec函数实现 if not index: index = np.argmax(ouput_vec.cpu().data.numpy()) else: index = np.array(index) index = index[np.newaxis, np.newaxis] index = torch.from_numpy(index) one_hot = torch.zeros(1, 1000).scatter_(1, index, 1) one_hot.requires_grad = True class_vec = torch.sum(one_hot * ouput_vec) # one_hot = 11.8605 执行backward,得到梯度 通过gen_cam()函数得到CAM图 将CAM与原图进行融合可视化,如下图所示 CAM 系列算法统一实现 CAM自2016年提出以来,已经有多种改进,并可运用于图像分割和目标检测,详细的CAM算法参见仓库。 pytorch-grad-cam提供了丰富的算法及简单的接口应用,下面就以resnet50为例,绘制6种CAM算法的热力图,效果如下图所示。 代码就不剖析了,grad-cam的接口已经非常清晰。请运行代码,查看结果如下图所示: 小结 CAM系列算法对理解深度卷积神经网络非常有帮助,建议仔细学习本节内容并进行拓展。 通过CAM分析: 可诊断模型是否学到真正特征 可通过热力图信息做对应的数据增强(如对非激活区域进行随机擦除和Cutout处理),类似YOLOv4中的CutMix数据增强方法。 还可以将热力图作为语义分割的弱监督标签进行训练分割模型,可参考《Tell Me Where to Look: Guided Attention Inference Network》 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.5-model-print.html":{"url":"chapter-6/6.5-model-print.html","title":"6.5 模型参数打印","keywords":"","body":"6.5 模型参数可视化 随着神经网络越来越深,越来越复杂,手动计算模型中间的数据的shape变得困难。 本节将介绍torchinfo,可用一键实现模型参数量计算、各层特征图形状计算和计算量计算等功能。 torchinfo的功能最早来自于TensorFlow和Kearas的summary()函数,torchinfo是学习借鉴而来。而在torchinfo之前还有torchsummary工具,不过torchsummary已经停止更新,并且推荐使用torchinfo。 torchsummay:https://github.com/sksq96/pytorch-summary torchinfo:https://github.com/TylerYep/torchinfo torchinfo 主要提供了一个函数,即 def summary( model: nn.Module, input_size: Optional[INPUT_SIZE_TYPE] = None, input_data: Optional[INPUT_DATA_TYPE] = None, batch_dim: Optional[int] = None, cache_forward_pass: Optional[bool] = None, col_names: Optional[Iterable[str]] = None, col_width: int = 25, depth: int = 3, device: Optional[torch.device] = None, dtypes: Optional[List[torch.dtype]] = None, mode: str | None = None, row_settings: Optional[Iterable[str]] = None, verbose: int = 1, **kwargs: Any, ) -> ModelStatistics: torchinfo 演示 运行代码 resnet_50 = models.resnet50(pretrained=False) batch_size = 1 summary(resnet_50, input_size=(batch_size, 3, 224, 224)) 可看到resnet50的以下信息 ========================================================================================== Layer (type:depth-idx) Output Shape Param # ========================================================================================== ResNet [1, 1000] -- ├─Conv2d: 1-1 [1, 64, 112, 112] 9,408 ├─BatchNorm2d: 1-2 [1, 64, 112, 112] 128 ├─ReLU: 1-3 [1, 64, 112, 112] -- ├─MaxPool2d: 1-4 [1, 64, 56, 56] -- ├─Sequential: 1-5 [1, 256, 56, 56] -- │ └─Bottleneck: 2-1 [1, 256, 56, 56] -- │ │ └─Conv2d: 3-1 [1, 64, 56, 56] 4,096 │ │ └─BatchNorm2d: 3-2 [1, 64, 56, 56] 128 │ │ └─ReLU: 3-3 [1, 64, 56, 56] -- │ │ └─Conv2d: 3-4 [1, 64, 56, 56] 36,864 │ │ └─BatchNorm2d: 3-5 [1, 64, 56, 56] 128 │ │ └─ReLU: 3-6 [1, 64, 56, 56] -- │ │ └─Conv2d: 3-7 [1, 256, 56, 56] 16,384 │ │ └─BatchNorm2d: 3-8 [1, 256, 56, 56] 512 │ │ └─Sequential: 3-9 [1, 256, 56, 56] 16,896 │ │ └─ReLU: 3-10 [1, 256, 56, 56] -- ...... │ └─Bottleneck: 2-16 [1, 2048, 7, 7] -- │ │ └─Conv2d: 3-140 [1, 512, 7, 7] 1,048,576 │ │ └─BatchNorm2d: 3-141 [1, 512, 7, 7] 1,024 │ │ └─ReLU: 3-142 [1, 512, 7, 7] -- │ │ └─Conv2d: 3-143 [1, 512, 7, 7] 2,359,296 │ │ └─BatchNorm2d: 3-144 [1, 512, 7, 7] 1,024 │ │ └─ReLU: 3-145 [1, 512, 7, 7] -- │ │ └─Conv2d: 3-146 [1, 2048, 7, 7] 1,048,576 │ │ └─BatchNorm2d: 3-147 [1, 2048, 7, 7] 4,096 │ │ └─ReLU: 3-148 [1, 2048, 7, 7] -- ├─AdaptiveAvgPool2d: 1-9 [1, 2048, 1, 1] -- ├─Linear: 1-10 [1, 1000] 2,049,000 ========================================================================================== Total params: 25,557,032 Trainable params: 25,557,032 Non-trainable params: 0 Total mult-adds (G): 4.09 ========================================================================================== Input size (MB): 0.60 Forward/backward pass size (MB): 177.83 Params size (MB): 102.23 Estimated Total Size (MB): 280.66 ========================================================================================== 其中包括各网络层名称,以及层级关系,各网络层输出形状以及参数量。在最后还有模型的总结,包括总的参数量有25,557,032个,总的乘加(Mult-Adds)操作有4.09G(4.09*10^9次方 浮点运算),输入大小为0.60MB,参数占102.23MB。 计算量:1G表示10^9 次浮点运算 (Giga Floating-point Operations Per Second),关于乘加运算,可参考知乎问题 存储量:这里的Input size (MB): 0.60,是通过数据精度计算得到,默认情况下采用float32位存储一个数,因此输入为:3*224*224*32b = 4816896b = 602112B = 602.112 KB = 0.6 MB 同理,Params size (MB): 25557032 * 32b = 817,825,024 b = 102,228,128 B = 102.23 MB 接口详解 summary提供了很多参数可以配置打印信息,这里介绍几个常用参数。 col_names:可选择打印的信息内容,如 (\"input_size\",\"output_size\",\"num_params\",\"kernel_size\",\"mult_adds\",\"trainable\",) dtypes:可以设置数据类型,默认的为float32,单精度。 mode:可设置模型在训练还是测试状态。 verbose: 可设置打印信息的详细程度。0是不打印,1是默认,2是将weight和bias也打出来。 小结 本节介绍torchinfo的使用,并分析其参数的计算过程,这里需要了解训练参数数量、特征图参数数量和计算量。其中计算量还有一个好用的工具库进行计算,这里作为额外资料供大家学习——PyTorch-OpCounter Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/":{"url":"chapter-7/","title":"第七章 PyTorch 小技巧汇总","keywords":"","body":"第七章 PyTorch 小技巧汇总 第七章 PyTorch 小技巧汇总 7.1 模型保存与加载 7.2 Finetune 模型微调 7.3 GPU使用 7.4 模型训练代码模板 7.5 TorchMetrics 模型评估指标库 7.6 Albumentations 数据增强库 7.7 TorchEnsemble 模型集成库 第七章简介 本章介绍开发过程中常用的代码段、工具模块和技巧等,初步设计有模型保存与加载、模型Finetune技巧、GPU使用技巧、训练代码框架等。 本章小结会续更新,将工作中遇到的小技巧分享出来。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.1-serialization.html":{"url":"chapter-7/7.1-serialization.html","title":"7.1 模型保存与加载","keywords":"","body":"7.1 模型保存与加载 保存与加载的概念(序列化与反序列化) 模型训练完毕之后,肯定想要把它保存下来,供以后使用,不需要再次去训练。 那么在pytorch中如何把训练好的模型,保存,保存之后又如何加载呢? 这就用需要序列化与反序列化,序列化与反序列化的概念如下图所示: 因为在内存中的数据,运行结束会进行释放,所以我们需要将数据保存到硬盘中,以二进制序列的形式进行长久存储,便于日后使用。 序列化即把对象转换为字节序列的过程,反序列化则把字节序列恢复为对象。 在pytorch中,对象就是模型,所以我们常常听到序列化和反序列化,就是将训练好的模型从内存中保存到硬盘里,当要使用的时候,再从硬盘中加载。 torch.save / torch.load pytorch提供的序列化与反序列化函数分别是 1. torch.save(obj, f, pickle_module=pickle, pickle_protocol=DEFAULT_PROTOCOL, _use_new_zipfile_serialization=True) 功能:保存对象到硬盘中 主要参数: obj- 对象 f - 文件路径 2. torch.load(f, map_location=None, pickle_module=pickle, **pickle_load_args) 功能:加载硬盘中对象 主要参数: f - 文件路径 map_location - 指定存储位置,如map_location='cpu', map_location={'cuda:1':'cuda:0'} 这里的map_location大有文章,经常需要手动设置,否者会报错。具体可参考以下形式: GPU->CPU:torch.load(model_path, map_location='cpu') CPU->GPU:torch.load(model_path, map_location=lambda storage, loc: storage) 两种保存方式 pytorch保存模型有两种方式 保存整个模型 保存模型参数 我们通过示意图来区分两者之间的差异 从上图左边知道法1保存整个nn.Module, 而法2只保存模型的参数信息。 我们知道一个module当中包含了很多信息,不仅仅是模型的参数 parameters,还包含了buffers, hooks和modules等一系列信息。 对于模型应用,最重要的是模型的parameters,其余的信息是可以通过model 类再去构建的,所以模型保存就有两种方式 所有内容都保存; 仅保存模型的parameters。 通常,我们只需要保存模型的参数,在使用的时候再通过load_state_dict方法加载参数。 由于第一种方法不常用,并且在加载过程中还需要指定的类方法,因此不做演示也不推荐。 对于第二种方法的代码十分简单,请看示例: net_state_dict = net.state_dict() torch.save(net_state_dict, \"my_model.pth\") 常用的代码段 在模型开发过程中,往往不是一次就能训练好模型,经常需要反复训练,因此需要保存训练的“状态信息”,以便于基于某个状态继续训练,这就是常说的resume,可以理解为断点续训练。 在整个训练阶段,除了模型参数需要保存,还有优化器的参数、学习率调整器的参数和迭代次数等信息也需要保存,因此推荐在训练时,采用以下代码段进行模型保存。以下代码来自torchvision的训练脚本。 checkpoint = { \"model\": model_without_ddp.state_dict(), \"optimizer\": optimizer.state_dict(), \"lr_scheduler\": lr_scheduler.state_dict(), \"epoch\": epoch, } path_save = \"model_{}.pth\".format(epoch) torch.save(checkpoint, path_save # =================== resume =============== # resume checkpoint = torch.load(path_save, map_location=\"cpu\") model.load_state_dict(checkpoint[\"model\"]) optimizer.load_state_dict(checkpoint[\"optimizer\"]) lr_scheduler.load_state_dict(checkpoint[\"lr_scheduler\"]) start_epoch = checkpoint[\"epoch\"] + 1 小结 模型保存与加载比较简单,需要注意的有两点: torch.load的时候注意map_location的设置; 理解checkpoint resume的概念,以及训练过程是需要模型、优化器、学习率调整器和已迭代次数的共同配合。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.2-finetune.html":{"url":"chapter-7/7.2-finetune.html","title":"7.2 Finetune 模型微调","keywords":"","body":"7.2 Finetune 模型微调 Finetune(微调)是深度学习模型训练中常用的方法。Finetune的理论可从迁移学习(Transfer Learning)中学习。 迁移学习 Transfer Learning是机器学习的分支,主要研究源域(source domain)所学到的知识,如何迁移到目标域(target domain)中 如《A Survey on Transfer Learning》中的图所示: 为什么要这样做呢?这是因为在target domain中,数据量较少,可学习提取的知识不足以完成任务,所以需要进行迁移学习,这样在target domain中可以学得快,学得好。 例如一个人学会了骑自行车,再去学骑电动车就很快,又比如一个人学会了C语言,再去学python就会比较快。 transfer learning是一个较大的领域,这里只讨论神经网络的finetune,如何理解模型的finetune它也是迁移学习呢? 我们知道,神经网络中最重要的东西就是权值参数,训练模型就是在更新参数,这些参数就是模型学习到知识。 之前对alexnet的卷积核进行了可视化,可以理解卷积核学习到的图像边缘,色彩信息就是alexnet所学习到的知识。 在图像任务中,这些知识是可以共享,可以迁移到其它任务中去,因此,大家常常采用在imagenet上训练好的模型进行finetune,进行transfer learning。 Finetune常用的两种方法 通常,会将模型划分为两个部分 feature extractor: 将fc层之前的部分认为是一个feature extractor classifier: fc层认为是classifier 基于此,finetune大体有两种方法: 将 feature extractor部分的参数固定,冻结,不进行训练,仅训练classifier 将 feature extractor设置较小的学习率,classifier设置较大的学习率 下面通过一个实例讲解两种方法 方法一:冻结 feature extractor。 原理是通过设置paramters的requires_grad为False,让它们不进行权重更新即可。 for param in resnet18_ft.parameters(): param.requires_grad = False 在完整代码中,每个epoch都打印了第一个卷积层权重,观察它们是否有变化。 经过25个epoch训练,性能指标如下图所示。 方法二:不同层不同学习率 原理是通过优化器的参数组管理,不同参数组可以设置不同的学习率。 因此第一步需要将不同的参数从模型中识别、提取出来,分别定义为不同参数组,这里通过内存地址进行区分。 # 返回的是该层所有参数的内存地址 fc_params_id = list(map(id, resnet18_ft.fc.parameters())) #遍历model的参数,只要不是需要ignore的,就保留,返回filter对象,在optimizer.py中的add_param_group中有 base_params = filter(lambda p: id(p) not in fc_params_id, resnet18_ft.parameters()) optimizer = optim.SGD([ {'params': base_params, 'lr': LR}, # 0 {'params': resnet18_ft.fc.parameters(), 'lr': LR*2}], momentum=0.9) 通过代码看到最后的全连接层学习率比前面的特征提取部分大10倍,如果对optimizer的参数组概念不了解,请回看第五章 小结 Finetune的代码实现非常简单,不过需要大家对nn.Module和optimizer的基础概念熟悉,建议回顾第四章与第五章的基础知识。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.3-gpu.html":{"url":"chapter-7/7.3-gpu.html","title":"7.3 GPU使用","keywords":"","body":"7.3 GPU使用 深度学习之所以可以发展迅猛,得益于强大的计算力。在PyTorch中,自然加持GPU加速运算,本节将介绍PyTorch中GPU的使用原理与多GPU使用的DataParallel原理,还有一些针对GPU的实用代码段。 gpu与cpu 在处理器家族中,有两大阵营,分别是CPU和GPU,它们分工协作,共同完成计算机复杂功能。 但它们两者主要差别在哪里?下面一探究竟。 CPU(central processing unit, 中央处理器)cpu主要包括两个部分,即控制器、运算器,除此之外还包括高速缓存等 GPU(Graphics Processing Unit, 图形处理器)是为处理类型统一并且相互无依赖的大规模数据运算,以及不需要被打断的纯净的计算环境为设计的处理器,因早期仅有图形图像任务中设计大规模统一无依赖的运算,因此该处理器称为图像处理器,俗称显卡。 那么它们之间主要区别在哪里呢,来看一张示意图 绿色的是计算单元,橙红色的是存储单元,橙黄色的是控制单元,从示意图中看出,gpu的重点在计算,cpu的重点在控制,这就是两者之间的主要差异。 在pytorch中,可以将训练数据以及模型参数迁移到gpu上,这样就可以加速模型的运算 在这里,需要了解的是,在pytorch中,两个数据运算必须在同一个设备上。 PyTorch的设备——torch.device 前面提到,PyTorch的运算需要将运算数据放到同一个设备上,因此,需要了解PyTorch中设备有哪些? 目前,PyTorch支持两种设备,cpu与cuda,为什么是cuda而不是gpu?因为早期,只有Nvidia的GPU能用于模型训练加速,因此称之为cuda。 即使现在支持了AMD显卡进行加速,仍旧使用cuda来代替gpu。 PyTorch中表示设备通常用torch.device)这个函数进行设置,例如: >>> torch.device('cuda:0') device(type='cuda', index=0) >>> torch.device('cpu') device(type='cpu') >>> torch.device('cuda') # current cuda device device(type='cuda') 补充资料: ROCm平台及HIP介绍 https://pytorch.org/docs/stable/notes/hip.html 把数据放到GPU——to函数 在pytorch中,只需要将要进行运算的数据放到gpu上,即可使用gpu加速运算 在模型运算过程中,需要放到GPU的主要是两个: 输入数据——形式为tensor 网络模型——形式为module pytorch中针对这两种数据都有相应的函数把它们放到gpu上,我们来认识一下这个函数,就是to函数 tensor的to函数: to(*args, \\kwargs) → Tensor** 功能:转换张量的数据类型或者设备 注意事项:to函数不是inplace操作,所以需要重新赋值,这与module的to函数不同 使用: 转换数据类型 x = torch.ones((3, 3)) x = x.to(torch.float64) 转换设备 x = torch.ones((3, 3)) x = x.to(\"cuda\") module的to函数:to(*args, \\kwargs)** 功能:move and/or cast the parameters and buffers,转换模型中的参数和缓存 注意事项:实行的是inplace操作 使用: 转换数据类型 linear = nn.Linear(2, 2) print(linear.weight) linear.to(torch.double) print(linear.weight) 迁移至gpu gpu1 = torch.device(\"cuda:1\") linear.to(gpu1) print(linear.weight) 将torch.device 与 to函数联合使用,就是第六章混淆矩阵代码中使用过的方式 device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") model.to(device) inputs, labels = inputs.to(device), labels.to(device) 通常,会采用torch.cuda.is_available()函数来自适应当前设备,若没有gpu可用,自动设置device为cpu,不会影响代码的运行。 除了torch.cuda.is_available(),torch库中还有一些关于cuda的实用函数,下面一起看看。 torch.cuda 在torch.cuda中有几十个关于guda的函数,详细请查阅官方文档) 下面介绍几个常用的函数。 torch.cuda.device_count(): 查看可用GPU数量 torch.cuda.current_device():查看当前使用的设备的序号 torch.cuda.get_device_name():获取设备的名称 torch.cuda.get_device_capability(device=None):查看设备的计算力 torch.cuda.is_available():查看cuda是否可用 torch.cuda.get_device_properties():查看GPU属性 torch.cuda.set_device(device):设置可用设备,已不推荐使用,建议通过CUDA_VISIBLE_DEVICES来设置,下文会讲解CUDA_VISIBLE_DEVICES的使用。 torch.cuda.mem_get_info(device=None):查询gpu空余显存以及总显存。 torch.cuda.memory_summary(device=None, abbreviated=False):类似模型的summary,它将GPU的详细信息进行输出。 torch.cuda.empty_cache():清空缓存,释放显存碎片。 torch.backends.cudnn.benchmark = True : 提升运行效率,仅适用于输入数据较固定的,如卷积 会让程序在开始时花费一点额外时间,为整个网络的每个卷积层搜索最适合它的卷积实现算法,进而实现网络的加速让内置的 cuDNN 的 auto-tuner 自动寻找最适合当前配置的高效算法,来达到优化运行效率的问题 torch.backends.cudnn.deterministic: 用以保证实验的可重复性. 由于cnDNN 每次都会去寻找一遍最优配置,会产生随机性,为了模型可复现,可设置torch.backends.cudnn.deterministic = True 运行代码,可看到如下信息: device_count: 1 current_device: 0 (8, 6) NVIDIA GeForce RTX 3060 Laptop GPU True ['sm_37', 'sm_50', 'sm_60', 'sm_61', 'sm_70', 'sm_75', 'sm_80', 'sm_86', 'compute_37'] _CudaDeviceProperties(name='NVIDIA GeForce RTX 3060 Laptop GPU', major=8, minor=6, total_memory=6144MB, multi_processor_count=30) (5407899648, 6442450944) |===========================================================================| | PyTorch CUDA memory summary, device ID 0 | |---------------------------------------------------------------------------| | CUDA OOMs: 0 | cudaMalloc retries: 0 | |===========================================================================| | Metric | Cur Usage | Peak Usage | Tot Alloc | Tot Freed | |---------------------------------------------------------------------------| | Allocated memory | 0 B | 0 B | 0 B | 0 B | | from large pool | 0 B | 0 B | 0 B | 0 B | | from small pool | 0 B | 0 B | 0 B | 0 B | |---------------------------------------------------------------------------| | Active memory | 0 B | 0 B | 0 B | 0 B | | from large pool | 0 B | 0 B | 0 B | 0 B | | from small pool | 0 B | 0 B | 0 B | 0 B | |---------------------------------------------------------------------------| | GPU reserved memory | 0 B | 0 B | 0 B | 0 B | | from large pool | 0 B | 0 B | 0 B | 0 B | | from small pool | 0 B | 0 B | 0 B | 0 B | |---------------------------------------------------------------------------| | Non-releasable memory | 0 B | 0 B | 0 B | 0 B | | from large pool | 0 B | 0 B | 0 B | 0 B | | from small pool | 0 B | 0 B | 0 B | 0 B | |---------------------------------------------------------------------------| | Allocations | 0 | 0 | 0 | 0 | | from large pool | 0 | 0 | 0 | 0 | | from small pool | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | Active allocs | 0 | 0 | 0 | 0 | | from large pool | 0 | 0 | 0 | 0 | | from small pool | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | GPU reserved segments | 0 | 0 | 0 | 0 | | from large pool | 0 | 0 | 0 | 0 | | from small pool | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | Non-releasable allocs | 0 | 0 | 0 | 0 | | from large pool | 0 | 0 | 0 | 0 | | from small pool | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | Oversize allocations | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | Oversize GPU segments | 0 | 0 | 0 | 0 | |===========================================================================| None 多gpu训练——nn.DataParallel 人多力量大的道理在PyTorch的训练中也是适用的,PyTorch支持多个GPU共同训练,加快训练速度。 多GPU可分为单机多卡和多机多卡,这里仅介绍单机多卡的方式。 单机多卡的实现非常简单,只需要增加一行代码: net = nn.DataParallel(net) 代码的意思是将一个nn.Module变为一个特殊的nn.Module,这个Module的forward函数实现多GPU调用。 如果对nn.Module的概念以及forward函数不理解的话,请回到第四章进行学习。 首先看一幅示意图,理解多GPU是如何工作的 整体有四个步骤: 数据平均划为N份 模型参数复制N份 在N个GPU上同时运算 回收N个GPU上的运算结果 了解了多gpu运行机制,下面看看DataParallel是如何实现的。 torch.nn.DataParallel(module, device_ids=None, output_device=None, dim=0) 功能:实现模型的数据并行运算 主要参数: module - 需要并行的module device_ids: (list of python:int or torch.device) – CUDA devices (default: all devices), eg: [2, 3] 默认采用所有可见gpu,这里强调了可见gpu,就是说可以设置部分gpu对当前python脚本不可见,这个可以通过系统环境变量设置 output_device: int or torch.device , 设置输出结果所在设备,默认为 device_ids[0],通常以第1个逻辑gpu为主gpu 源代码分析: DataParallel仍旧是一个nn.Module类,所以首要关注它的forward函数。 来到/torch/nn/parallel/data_parallel.py的147行: def forward(self, *inputs, **kwargs): with torch.autograd.profiler.record_function(\"DataParallel.forward\"): if not self.device_ids: return self.module(*inputs, **kwargs) for t in chain(self.module.parameters(), self.module.buffers()): if t.device != self.src_device_obj: raise RuntimeError(\"module must have its parameters and buffers \" \"on device {} (device_ids[0]) but found one of \" \"them on device: {}\".format(self.src_device_obj, t.device)) inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) # for forward function without any inputs, empty list and dict will be created # so the module can be executed on one device which is the first one in device_ids if not inputs and not kwargs: inputs = ((),) kwargs = ({},) if len(self.device_ids) == 1: return self.module(*inputs[0], **kwargs[0]) replicas = self.replicate(self.module, self.device_ids[:len(inputs)]) outputs = self.parallel_apply(replicas, inputs, kwargs) return self.gather(outputs, self.output_device) 核心点有4行代码,分别是: inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) replicas = self.replicate(self.module, self.device_ids[:len(inputs)]) outputs = self.parallel_apply(replicas, inputs, kwargs) return self.gather(outputs, self.output_device) 一、数据切分 inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids):利用scatter函数,将数据切分为多块,为各GPU需要的数据做准备。 scatter函数在torch\\nn\\parallel\\scatter_gather.py第11行。 def scatter(inputs, target_gpus, dim=0): r\"\"\" Slices tensors into approximately equal chunks and distributes them across given GPUs. Duplicates references to objects that are not tensors. \"\"\" 二、模型分发至GPU replicas = self.replicate(self.module, self.device_ids[:len(inputs)]):利用replicate函数将模型复制N份,用于多GPU上。 replicate函数在 torch\\nn\\parallel\\replicate.py第78行。 三、执行并行推理 outputs = self.parallel_apply(replicas, inputs, kwargs):多GPU同时进行运算。 四、结果回收 return self.gather(outputs, self.output_device) 为了理解分发过程,请使用具备多GPU的环境,运行配套代码,可看到如下信息: batch size in forward: 4 batch size in forward: 4 batch size in forward: 4 batch size in forward: 4 model outputs.size: torch.Size([16, 3]) CUDA_VISIBLE_DEVICES :0,1,3,2 device_count :4 batchsize设置为16,将16个样本平均分发给4个GPU,因此在forward函数当中,看到的数据是4个样本。 多GPU训练模型的保存与加载 当模型变为了Dataparallel时,其参数名称会多一个module.字段,这导致在保存的时候state_dict也会多了module.字段。 从而,在加载的时候经常出现以下报错。 RuntimeError: Error(s) in loading state_dict for FooNet: ​ Missing key(s) in state_dict: \"linears.0.weight\", \"linears.1.weight\", \"linears.2.weight\". ​ Unexpected key(s) in state_dict: \"module.linears.0.weight\", \"module.linears.1.weight\", \"module.linears.2.weight\". 解决方法是,移除key中的module.: from collections import OrderedDict new_state_dict = OrderedDict() for k, v in state_dict_load.items(): namekey = k[7:] if k.startswith('module.') else k new_state_dict[namekey] = v 请结合代码运行,观察其使用,并看到如下结果: state_dict_load: OrderedDict([('module.linears.0.weight', tensor([[ 0.3337, 0.0317, -0.1331], [ 0.0431, 0.0454, 0.1235], [ 0.0575, -0.2903, -0.2634]])), ('module.linears.1.weight', tensor([[ 0.1235, 0.1520, -0.1611], [ 0.4511, -0.1460, -0.1098], [ 0.0653, -0.5025, -0.1693]])), ('module.linears.2.weight', tensor([[ 0.3657, -0.1107, -0.2341], [ 0.0657, -0.0194, -0.3119], [-0.0477, -0.1008, 0.2462]]))]) new_state_dict: OrderedDict([('linears.0.weight', tensor([[ 0.3337, 0.0317, -0.1331], [ 0.0431, 0.0454, 0.1235], [ 0.0575, -0.2903, -0.2634]])), ('linears.1.weight', tensor([[ 0.1235, 0.1520, -0.1611], [ 0.4511, -0.1460, -0.1098], [ 0.0653, -0.5025, -0.1693]])), ('linears.2.weight', tensor([[ 0.3657, -0.1107, -0.2341], [ 0.0657, -0.0194, -0.3119], [-0.0477, -0.1008, 0.2462]]))]) Process finished with exit code 0 使用指定编号的gpu 通常,一台服务器上有多个用户,或者是会进行多个任务,此时,对gpu合理的安排使用就尤为重要 在实践中,通常会设置当前python脚本可见的gpu,然后直接使用nn.dataparallel使用所有gpu即可,不需要手动去设置使用哪些gpu 设置python脚本可见gpu的方法为设置系统环境变量中的CUDA_VISIBLE_DEVICES 设置方法为: os.environ.setdefault(\"CUDA_VISIBLE_DEVICES\", \"2, 3\") 当时之后,即物理设备的2,3号GPU,在程序中分别是0号、1号GPU,这里需要理解逻辑编号与物理编号的对应关系。 注意事项: CUDA_VISIBLE_DEVICES的设置一定要在 device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") 之前! 否则已经调用cuda,python脚本已经获取当前可见gpu了,再设置就无效了 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.4-training-script.html":{"url":"chapter-7/7.4-training-script.html","title":"7.4 模型训练代码模板","keywords":"","body":"7.4 模型训练代码模板 一个良好的训练代码,可以有助于分析和超参调优,本节将以torchvision提供的分类模型训练代码为基础,编写适合自己的训练代码框架。 torchvision还提供了分割、检测、相似性学习和视频分类的 训练脚本,可以参考https://pytorch.org/vision/stable/training_references.html。 在分类的train.py中,共计501行代码,下面我们提炼出核心内容,在cifar10数据集上完成resnet-8的训练。 提炼后,代码核心内容包括: 参数设置部分采用argparse模块进行配置,便于服务器上训练,以及超参数记录; 日志模块,包括logging模块记录文本信息.log文件,以及tensorboard部分的可视化内容; 训练模块封装为通用类——ModelTrainer 模型保存部分 一、参数设置 在服务器上进行训练时,通常采用命令行启动,或时采用sh脚本批量训练,这时候就需要从命令行传入一些参数,用来调整模型超参。 例如学习率想从0.1改为0.01,按以往代码,需要进入.py文件,修改代码,保存代码,运行代码。 这样操作明显欠妥,因此通常会采用argparse模块,将经常需要调整的参数,可以从命令行中接收。 在代码中,采用了函数get_args_parser()实现,有了args,还可以将它记录到日志中,便于复现以及查看模型的超参数设置,便于跟踪。 二、日志模块 模型训练的日志很重要,它用于指导下一次实验的超参数如何调整。 代码中采用借助logging模块构建一个logger,并且以时间戳(年月日-时分秒)的形式创建文件夹,便于日志管理。 在logger中使用logger.info函数代替print函数,可以实现在终端展示信息,还可以将其保存到日志文件夹下的log.log文件,便于溯源。 三、训练模块 训练过程比较固定,因此会将其封装成 train_one_epoch和evaluate的两个函数,从这两个函数中需要返回我们关心的指标,如loss,accuracy,混淆矩阵等。 四、指标统计模块 之前的代码中,loss和accuracy需要手动记录每个值,然后取平均,除了它们两个,深度学习训练中还有许多指标都需要类似的操作。 因此,可以抽象出一个AverageMeter类,用于记录需要求取平均值的那些指标。 AverageMeter类的使用,使得代码更简洁,下面一同分析一下。 运行代码当训练完成后,可在输出目录下得到以时间戳为文件夹的日志目录,里面包括loss、accuracy、混淆矩阵可视化图,最优模型checkpoint。 小结 训练模型的代码结构可以千变万化,每个人结合自己的风格进行编写,本节代码也是吸取了多个代码的精华,当然还有不足之处,后续会慢慢补上,这里提供一个整体思路,知道代码中需要什么。 建议参考以下训练代码结构: PyTorch ImageNet example (https://github.com/pytorch/examples/tree/master/imagenet) NVIDIA CUDA specific speedups adopted from NVIDIA Apex examples (https://github.com/NVIDIA/apex/tree/master/examples/imagenet) TIMM: https://github.com/rwightman/pytorch-image-models/blob/master/train.py Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.5-torchmetrics.html":{"url":"chapter-7/7.5-torchmetrics.html","title":"7.5 TorchMetrics 模型评估指标库","keywords":"","body":"7.5 torchmetrics 模型评估指标库 模型训练时是通过loss进行好坏的评估,因为我们采用的是loss进行方向传播。对于人类评判好坏,往往不是通过loss值,而是采用某种评判指标。 在图像分类任务中常用的有Accuracy(准确率)、Recall(召回率)和Precision(精确度),图像分割中常用mIoU和Dice系数,目标检测中常用mAP,由此可见不同任务的评价指标大多不一样。 常用的指标多达几十种,本节将介绍torchmetrics工具,它目前提供超过80种评价指标的函数,并且使用起来非常方便,值得学习。 TorchMetrics简介与安装 TorchMetrics: Github TorchMetrics is a collection of 80+ PyTorch metrics implementations and an easy-to-use API to create custom metrics. It offers: A standardized interface to increase reproducibility Reduces Boilerplate Distributed-training compatible Rigorously tested Automatic accumulation over batches Automatic synchronization between multiple devices 安装: pip install torchmetrics conda install -c conda-forge torchmetrics TorchMetrics 快速上手 torchmetrics 的使用与本章第四节课中介绍的AverageMeter类似,它能够记录每一次的信息,并通过.compute()函数进行汇总计算。 下面通过一个accuracy的例子,剖析torchmetrics的体系结构。 from my_utils import setup_seed setup_seed(40) import torch import torchmetrics metric = torchmetrics.Accuracy() n_batches = 3 for i in range(n_batches): preds = torch.randn(10, 5).softmax(dim=-1) target = torch.randint(5, (10,)) acc = metric(preds, target) # 单次计算,并记录本次信息。通过维护tp, tn, fp, fn来记录所有数据 print(f\"Accuracy on batch {i}: {acc}\") acc_avg = metric.compute() print(f\"Accuracy on all data: {acc_avg}\") tp, tn, fp, fn = metric.tp, metric.tn, metric.fp, metric.fn print(tp, tn, fp, fn, sum([tp, tn, fp, fn])) metric.reset() Accuracy on batch 0: 0.30000001192092896 Accuracy on batch 1: 0.10000000149011612 Accuracy on batch 2: 0.20000000298023224 Accuracy on all data: 0.20000000298023224 tensor(6) tensor(96) tensor(24) tensor(24) tensor(150) torchmetrics的使用可以分以下三步: ​ 1.创建指标评价器 ​ 2.迭代中进行\"update\"或forward,update和forward均可记录每次数据信息 ​ 3.计算所有数据指标 TorchMetrics代码结构 这里提到forward,正是第四章中nn.Module的forward。 TorchMetrics所有指标均继承了nn.Module,因此可以看到这样的用法。 acc = metric(preds, target) 下面进入 torchmetrics\\classification\\accuracy.py 中观察 Accuracy到底是什么。 可以看到Accuracy类只有3个函数,分别是__init__, update, compute,其作用就如上文所述。 再看继承关系,Accuracy --> StatScores --> Metric --> nn.Module + ABC。 Metric类正如文档所说“The base Metric class is an abstract base class that are used as the building block for all other Module metrics.”,是torchmetrics所有类的基类,它实现forward函数,因此才有像这样的调用: acc = metric(preds, target) Accuracy 更新逻辑 torchmetrics的使用与上一节课中的AverageMeter+Accuracy函数类似,不过在数据更新维护方面略有不同,并且torchmetrics还有点难理解。 AverageMeter+Accuracy时,是通过self.val, self.sum, self.count, self.avg进行维护。 在torchmetrics.Accuracy中,并没有这些属性,而是通过tp, tn, fp, fn进行维护。 但是有个问题来了,请仔细观察代码,iteration循环是3次,每一次batch的数量是10,按道理tp+tn+fp+fn= 30,总共30个样本,为什么会是150? 因为,这是多类别分类的统计,不是二分类。因此需要为每一个类,单独计算tp, tn, fp, fn。又因为有5个类别,因此是30*5=150。 关于多类别的tp, tn, fp, fn,可参考stackoverflow 还有个好例子,请看混淆矩阵: 真实\\预测 0 1 2 0 2 0 0 1 1 0 1 2 0 2 0 对于类别0的 FP=1 TP=2 FN=0 TN=3 对于类别1的 FP=2 TP=0 FN=2 TN=2 对于类别2的 FP=1 TP=0 FN=2 TN=3 自定义metrics 了解了Accuracy使用逻辑,就可以触类旁通,使用其它80多个Metrics。 但总有不满足业务需求的时候,这时候就需要自定义metrics。 自定义metrics非常简单,它就像自定义Module一样,提供必备的函数即可。 自定义metrics只需要继承Metric,然后实现以下三个函数即可: init(): Each state variable should be called using self.add_state(...). update(): Any code needed to update the state given any inputs to the metric. compute(): Computes a final value from the state of the metric. 举例: class MyAccuracy(Metric): full_state_update: bool = False def __init__(self): super().__init__() self.add_state(\"correct\", default=torch.tensor(0), dist_reduce_fx=\"sum\") self.add_state(\"total\", default=torch.tensor(0), dist_reduce_fx=\"sum\") def update(self, preds: torch.Tensor, target: torch.Tensor): batch_size = target.size(0) _, pred = preds.topk(1, 1, True, True) pred = pred.t() correct = pred.eq(target.reshape(1, -1).expand_as(pred)) self.correct += torch.sum(correct) self.total += batch_size def compute(self): return self.correct.float() / self.total 这里需要注意的是: 在init函数中需要通过add_state进行属性初始化; 在update中需要处理接收的数据,并可自定义管理机制,如这里采用correct与total来管理总的数据 在compute中需清晰知道返回的是总数据的Accuracy 小结 torchmetrics是一个简单易用的指标评估库,里面提供了80多种指标,建议采用torchmetrics进行指标评估,避免重复造轮子。 下面请看支持的指标: Auido 任务指标 Perceptual Evaluation of Speech Quality (PESQ) Permutation Invariant Training (PIT) Scale-Invariant Signal-to-Distortion Ratio (SI-SDR) Scale-Invariant Signal-to-Noise Ratio (SI-SNR) Short-Time Objective Intelligibility (STOI) Signal to Distortion Ratio (SDR) Signal-to-Noise Ratio (SNR) 分类 任务指标 Accuracy AUC AUROC Average Precision Binned Average Precision Binned Precision Recall Curve Binned Recall At Fixed Precision Calibration Error Cohen Kappa Confusion Matrix Coverage Error Dice Score F1 Score FBeta Score Hamming Distance Hinge Loss Jaccard Index KL Divergence Label Ranking Average Precision Label Ranking Loss Matthews Corr. Coef. Precision Precision Recall Precision Recall Curve Recall ROC Specificity Stat Scores 图像 任务指标 Error Relative Global Dim. Synthesis (ERGAS) Frechet Inception Distance (FID) Image Gradients Inception Score Kernel Inception Distance Learned Perceptual Image Patch Similarity (LPIPS) Multi-Scale SSIM Peak Signal-to-Noise Ratio (PSNR) Spectral Angle Mapper Spectral Distortion Index Structural Similarity Index Measure (SSIM) Universal Image Quality Index 检测 任务指标 Mean-Average-Precision (mAP) Pairwise 任务指标 Cosine Similarity Euclidean Distance Linear Similarity Manhattan Distance Regression 任务指标 Cosine Similarity Explained Variance Mean Absolute Error (MAE) Mean Absolute Percentage Error (MAPE) Mean Squared Error (MSE) Mean Squared Log Error (MSLE) Pearson Corr. Coef. R2 Score Spearman Corr. Coef. Symmetric Mean Absolute Percentage Error (SMAPE) Tweedie Deviance Score Weighted MAPE Retrieval 任务指标 Retrieval Fall-Out Retrieval Hit Rate Retrieval Mean Average Precision (MAP) Retrieval Mean Reciprocal Rank (MRR) Retrieval Normalized DCG Retrieval Precision Retrieval R-Precision Retrieval Recall Text 任务指标 BERT Score BLEU Score Char Error Rate ChrF Score Extended Edit Distance Match Error Rate ROUGE Score Sacre BLEU Score SQuAD Translation Edit Rate (TER) Word Error Rate Word Info. LostWord Info. Preserved Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.6-albumentations.html":{"url":"chapter-7/7.6-albumentations.html","title":"7.6 Albumentations 数据增强库","keywords":"","body":"7.6 albumentations 数据增强库 本节介绍albumentations库,albumentations是强大的数据增强库,原计划在第三章中进行介绍,后因篇幅过大,放到了本章作为进阶技巧。 为什么要用albumentations? pytorch的transforms有什么不足么? 当然有不足了, pytorch的transforms在处理图像分割与目标检测这一类需要图像与标签同时变换的时候不方便,而albumentations提供了图像分割、目标检测等复杂任务的数据增强方法。 为什么要用albumentations? 从github中译过来: 支持多种任务:支持分类、语义分割、实例分割、目标检测和姿态估计等; 提供简洁的API:针对分割、边界框回归、关键点任务及多种数据形态(RBG-images, grayscale images, multispectral images)均可采用统一的函数完成数据增强。 数据增强方法多:提供超70种变换方法。 速度快效率高:相比其它常见数据增强库,多数方法速度都为最优 支持主流框架:albumentations已是pytorch生态系统的一员,可很好适配pytorch,同时支持TensorFlow中使用。 专家背书:albumentations的作者大多数来自工业界大牛 市场验证:albumentations已经被广泛应用于工业界和学术界,以及各种竞赛,并且获得了优异成绩。 albumentations有那么多优点,还不赶快来学习它。 安装 pip install -U albumentations 上手demo demo代码请查看配套代码,通过代码可以看到,使用步骤有: 定义一个Compose,内部包括多个变换方法(同transforms一样) 将compose放到dataset中,在getitem函数中实现调用 getitem中注意各变换方法的输入,由于albumentations支持多种数据同时处理,因此输入时需要指定变量。如image、mask、bboxes和keypoints之类。 经过Compose返回的数据是一个字典形式,需要根据key获取对应信息,如image、mask、bboxes和keypoints。 可以任意调整以下变换方法,以及参数设置,观察图像变化。 train_transform = A.Compose( [ A.Resize(512, 512), A.ShiftScaleRotate(shift_limit=0.05, scale_limit=0.05, rotate_limit=55, p=0.5), ] ) 图像-标签对的数据增强 图像分割、目标检测和关键点任务中的数据增强,需要同时对图像和标签一起变换,这也是albumentations与pytorch的transforms最大的区别。 请运行代码观察结果,这里介绍使用步骤 dataset的getitem中将image与mask同时传入self.transfoms()中; 在DataLoader返回的数据中,采用key-value的形式获取image与mask/bboes/keypoints; data_augmented = data_transform(image=image_rgb, mask=mask) albumentations 代码结构 albumentations采用BasicTransform作为基类,再根据是否需要对标签进行变换,划分为nlyTransform 和 DualTransform 顾名思义,ImageOnlyTransform就是仅针对原图进行操作,DualTransform就是同时对原图和标签进行操作。 两种类型的变换常见的方如下: ImageOnlyTransform: Blur,RGBShift,RandomBrightnessContrast等 DualTransform: Resize, Flip, Crop, Rotate,ElasticTransform等 为了分析albumentations代码结构,特地绘制了一副简略版的UML类图,再结合Resize与Blur的函数,分析albumentations代码运行结构。 在代码中通过Resize类的调试,可看到实现图像模糊的功能在apply()函数中,接下来就需要理清代码结构,寻找BasicTransform是如何调用apply()的。 通过代码单步调试,发现依次进行以下步骤: BasicTransform的__call__():在89行,return时调用self.apply_with_params(params, **kwargs) BasicTransform的self.apply_with_params():在100行,通过target_function = self._get_target_function(key)获得具体需要变换的函数 BasicTransform的self._get_target_function():在123行,通过self.targets.get()获得具体的target_function DualTransform的targets:是一个字典,其中定义了key与具体变换函数的映射,其中self.apply即上文提到的Resize类下的self.apply。 到这里一个具体的变换实现过程才算走完,其中BasicTransform定义了基础逻辑,例如概率选择、参数获取等,DualTransform则是定义mask,masks,bboxes,keypoints的变换函数接口,最终的实现由Resize类来完成。同理,ImageOnlyTransform 一样。 @property def targets(self): return { \"image\": self.apply, \"mask\": self.apply_to_mask, \"masks\": self.apply_to_masks, \"bboxes\": self.apply_to_bboxes, \"keypoints\": self.apply_to_keypoints, } 小结 本节介绍了albumentations的优点,基本使用以及代码结构,关于albumentations的70多种方法请查看附录,或者阅读文档查看API,使用方法非常简单,也可到代码中查看编写的批量数据增强代码段,实现了68个数据增强可视化 附录 albumentations提供70多种API,这里将不再一一介绍各API的参数,请查看文档即可。 整体分为两种,Pixel-Level和Spatial-Level: AdvancedBlur Blur CLAHE ChannelDropout ChannelShuffle ColorJitter Downscale Emboss Equalize FDA FancyPCA FromFloat GaussNoise GaussianBlur GlassBlur HistogramMatching HueSaturationValue ISONoise ImageCompression InvertImg MedianBlur MotionBlur MultiplicativeNoise Normalize PixelDistributionAdaptation Posterize RGBShift RandomBrightnessContrast RandomFog RandomGamma RandomRain RandomShadow RandomSnow RandomSunFlare RandomToneCurve RingingOvershoot Sharpen Solarize Superpixels TemplateTransform ToFloat ToGray ToSepia UnsharpMask Transform Image Masks BBoxes Keypoints Affine ✓ ✓ ✓ ✓ CenterCrop ✓ ✓ ✓ ✓ CoarseDropout ✓ ✓ ✓ Crop ✓ ✓ ✓ ✓ CropAndPad ✓ ✓ ✓ ✓ CropNonEmptyMaskIfExists ✓ ✓ ✓ ✓ ElasticTransform ✓ ✓ Flip ✓ ✓ ✓ ✓ GridDistortion ✓ ✓ GridDropout ✓ ✓ HorizontalFlip ✓ ✓ ✓ ✓ Lambda ✓ ✓ ✓ ✓ LongestMaxSize ✓ ✓ ✓ ✓ MaskDropout ✓ ✓ NoOp ✓ ✓ ✓ ✓ OpticalDistortion ✓ ✓ PadIfNeeded ✓ ✓ ✓ ✓ Perspective ✓ ✓ ✓ ✓ PiecewiseAffine ✓ ✓ ✓ ✓ PixelDropout ✓ ✓ ✓ ✓ RandomCrop ✓ ✓ ✓ ✓ RandomCropNearBBox ✓ ✓ ✓ ✓ RandomGridShuffle ✓ ✓ ✓ RandomResizedCrop ✓ ✓ ✓ ✓ RandomRotate90 ✓ ✓ ✓ ✓ RandomScale ✓ ✓ ✓ ✓ RandomSizedBBoxSafeCrop ✓ ✓ ✓ RandomSizedCrop ✓ ✓ ✓ ✓ Resize ✓ ✓ ✓ ✓ Rotate ✓ ✓ ✓ ✓ SafeRotate ✓ ✓ ✓ ✓ ShiftScaleRotate ✓ ✓ ✓ ✓ SmallestMaxSize ✓ ✓ ✓ ✓ Transpose ✓ ✓ ✓ ✓ VerticalFlip ✓ ✓ ✓ ✓ Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.7-torchensemble.html":{"url":"chapter-7/7.7-torchensemble.html","title":"7.7 TorchEnsemble 模型集成库","keywords":"","body":"7.7 TorchEnsemble 模型集成库 俗话说,三个臭皮匠顶个诸葛亮,机器学习模型亦是如此。Model Ensemble(模型集成)是机器学习领域重要的研究方向,在传统机器学习以及各种数据科学竞赛中,Model Ensemble成了标配, 因此,本节就介绍工业生产中实用的模型集成技术。 pytorch的生态系统中已经有ensemble的库,本节将介绍torchensemble的使用,各种集成方法的逻辑,torchensemble的代码结构以及如何改造自己的代码。 ensemble-pytorch 简介 TorchEnsemble是pytorch生态的一员,提供了统一的集成模块供pytorch使用。目前支持的方法有 Fusion Mixed fusion.py Classification / Regression Voting[1] Parallel voting.py Classification / Regression Neural Forest Parallel voting.py Classification / Regression Bagging[2] Parallel bagging.py Classification / Regression Gradient Boosting[3] Sequential gradient_boosting.py Classification / Regression Snapshot Ensemble[4] Sequential snapshot_ensemble.py Classification / Regression Adversarial Training[5] Parallel adversarial_training.py Classification / Regression Fast Geometric Ensemble[6] Sequential fast_geometric.py Classification / Regression Soft Gradient Boosting[7] Parallel soft_gradient_boosting.py Classification / Regression 各方法的理论简介可查阅文档 (关于模型集成的理论概念,请自行通过机器学习基础了解) 原理分析介绍 torchensemble提供了训练示例,本节对示例进行了整理及注释,可参见配套代码。 通过代码段可知道,torchensemble提供了集成类,集成类中的基学习器都是同一个结构(如LeNet-5),然后通过集成类的fit()函数完成训练,通过evaluate()来评估。 本部分最核心的问题在于理解不同的集成方法,它是如何使用多个基学习器的输出结果,下面通过源代码中的forward函数观察不同方法的集成方式。 fusion 原理:先平均,后概率。 通过FusionClassifier类的 forward函数可看到,它是对基学习器的输出进行逐元素的平均,然后再进行softmax进行输出分类概率向量。 class FusionClassifier(BaseClassifier): def _forward(self, *x): \"\"\" Implementation on the internal data forwarding in FusionClassifier. \"\"\" # Average outputs = [estimator(*x) for estimator in self.estimators_] output = op.average(outputs) return output @torchensemble_model_doc( \"\"\"Implementation on the data forwarding in FusionClassifier.\"\"\", \"classifier_forward\", ) def forward(self, *x): output = self._forward(*x) proba = F.softmax(output, dim=1) return proba voting voting:先概率,后平均。 voting先对基学习器进行softmax,然后把多个概率向量进行平均。 关于投票法,在sklearn中还有多种选择,vote模式有soft与hard,在torchensemble采用的是soft方式,及返回的是各分类器分类概率的平均。 If voting='soft' and flatten_transform=True: returns ndarray of shape (n_samples, n_classifiers * n_classes), being class probabilities calculated by each classifier. If voting='soft' and `flatten_transform=False: ndarray of shape (n_classifiers, n_samples, n_classes) If voting='hard': ndarray of shape (n_samples, n_classifiers), being class labels predicted by each classifier. class VotingClassifier(BaseClassifier): @torchensemble_model_doc( \"\"\"Implementation on the data forwarding in VotingClassifier.\"\"\", \"classifier_forward\", ) def forward(self, *x): # Average over class distributions from all base estimators. outputs = [ F.softmax(estimator(*x), dim=1) for estimator in self.estimators_ ] proba = op.average(outputs) return proba bagging bagging:先概率,后平均。这与voting一样。 bagging的主要原理在于基模型的训练数据不一样,因此可得到不同的基模型,而torchensemble文档里提到,深度学习中数据越多,模型越好,因此就没有采用K-Flod的方法划分数据了。 \"bagging further uses sampling with replacement on each batch of data. Notice that sub-sampling is not typically used when training neural networks, because the neural networks typically achieve better performance with more training data.\" class BaggingClassifier(BaseClassifier): @torchensemble_model_doc( \"\"\"Implementation on the data forwarding in BaggingClassifier.\"\"\", \"classifier_forward\", ) def forward(self, *x): # Average over class distributions from all base estimators. outputs = [F.softmax(estimator(*x), dim=1) for estimator in self.estimators_] proba = op.average(outputs) return proba GradientBoostingClassifier GradientBoostingClassifier:先求和,再概率。 这里先求和是因为Gradient Boosting算法原理就是“加法模型”,最终的结果是利用N个学习器的结果之和得到。为什么呢?因为第二个学习器学习的是第一个学习器与目标检测的差距,第三个学习器学习的是第一个+第二个学习器结果之和与结果之间的差距,以此类推。因此才有了sum_with_multiplicative这个函数中的代码逻辑。 如果不了解集成学习的基础概念,是无法理解上面这段话的,因为上面这段话是对梯度提升(GradientBoosting)算法的简述,而梯度提升(GradientBoosting)算法相对于bagging方法而言,不是那么容易理解,还请自行补足机器学习基础概念。 def forward(self, *x): output = [estimator(*x) for estimator in self.estimators_] output = op.sum_with_multiplicative(output, self.shrinkage_rate) proba = F.softmax(output, dim=1) return proba def sum_with_multiplicative(outputs, factor): \"\"\" Compuate the summation on a list of tensors, and the result is multiplied by a multiplicative factor. \"\"\" return factor * sum(outputs) SnapshotEnsembleClassifier SnapshotEnsembleClassifier:先平均,后概率。 SnapshotEnsembleClassifier是深度学习模型提出后才发明的集成方法,这与深度学习模型训练过程有关。其思路是保存多个局部最后的模型,然后将它们的结果进行集成输出。 这个思路非常新奇,集成学习的核心点之一是如何寻找多个基学习器,通常方法是从数据、参数、模型类型出发,获得多个性能不同的基学习器。而SnapShotEnsemble是通过一次训练过程中,保存多个局部最优的状态为基学习器,这样做的好处是高效,且各基学习器的错误样本通常不会重复,因此模型是基于上一次错误样本进行训练的。 [Huang Gao, Sharon Yixuan Li, Geoff Pleisset, et al., “Snapshot Ensembles: Train 1, Get M for Free.” ICLR, 2017.] def _forward(self, *x): \"\"\" Implementation on the internal data forwarding in snapshot ensemble. \"\"\" # Average results = [estimator(*x) for estimator in self.estimators_] output = op.average(results) return output Average results = [estimator(*x) for estimator in self.estimators_] output = op.average(results) 更多方法请关注官方文档的介绍。 官方demo提供了一些对比实验,这里进行汇总,供参考 代码结构 torchensemble库将模型训练、推理、集成过程都进行了高度封装,并且提供统一的接口,如何将复杂的、多种多样的模型编写为统一的API接口? 这里就绘制简略版的UML类图,梳理代码结构。 通过类图,可以看到所有的继承类都是nn.Module类,由此可见第四章的nn.Module有多重要。 从代码结构可以学习了解到,一个复杂的模块都可以提炼、抽象出最基础的BaseClass,BaseClass中定义最核心的、最通用的属性和方法,如这里的基学习器、基学习器数量、学习器容器,forward(), fit(), predict()等。 手动实现模型集成 虽然torchensemble提供了丰富的集成方法,但是有的时候并不适用于手动训练的多个模型,下面记录两个手动实现模型集成(投票法)的代码方案。 一、借助nn.ModuleList() class Ensemble(nn.Module): def __init__(self, device): super(Ensemble,self).__init__() # you should use nn.ModuleList. Optimizer doesn't detect python list as parameters self.models = nn.ModuleList(models) def forward(self, x): # it is super simple. just forward num_ models and concat it. output = torch.zeros([x.size(0), len(training.classes)]).to(device) for model in self.models: output += model(x) return output 原文链接:https://www.kaggle.com/code/georgiisirotenko/pytorch-fruits-transferlearing-ensemble-test99-18/notebook 二、借助list,并Counter for img, label in tqdm(testloader): img, label = img.to(device), label.to(device) for i, mlp in enumerate(mlps): mlp.eval() out = mlp(img) _, prediction = torch.max(out, 1) # 按行取最大值 pre_num = prediction.cpu().numpy() mlps_correct[i] += (pre_num == label.cpu().numpy()).sum() pre.append(pre_num) arr = np.array(pre) pre.clear() result = [Counter(arr[:, i]).most_common(1)[0][0] for i in range(BATCHSIZE)] vote_correct += (result == label.cpu().numpy()).sum() print(\"epoch:\" + str(ep) + \"集成模型的正确率\" + str(vote_correct / valid_data_size)) 原文链接:https://blog.csdn.net/weixin_42237113/article/details/108970920 ResNet在Cifar-10上的实验效果 为了观察模型融合的效果,特地编写了代码,实现ResNet在Cifar10上的训练,并对每个基学习器的性能进行对比,直观的看出模型集成的作用是立竿见影的,请看效果图。 本实验采用3个学习器进行投票式集成,因此绘制了7条曲线,其中各学习器在训练和验证各有2条曲线,集成模型的结果通过 valid_acc输出(蓝色),通过上图可发现,集成模型与三个基学习器相比,分类准确率都能提高3个多百分点左右,是非常高的提升了。 为了能绘制出这幅图,特地构思了代码,代码主要是自定义了class MyEnsemble(VotingClassifier),并重写fit函数,使得训练过程的信息可以被记录下来。 小结 本节简单介绍了pytorch中使用模型集成的方法库——torchensemble,详细内容还请查看官方文档,同时可以关注kaggle的方案,集成学习是竞赛的必备方案,也是工业项目中常用的方法,请重点学习。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/":{"url":"chapter-8/","title":"第八章 图像项目案例","keywords":"","body":"第八章 图像项目案例 第八章 图像项目案例 8.1 图像分类——胸部X光肺炎分类 8.2 图像分割——脑MRI胶质瘤分割 8.3 目标检测——无人机检测 8.4 目标跟踪(上)——DeepSORT原理 8.4 目标跟踪(下)——虎门大桥车流量统计 8.5 生成对抗网络——CycleGAN 8.6 扩散模型——DDPM 8.7 图像描述——Image Captioning 8.8 图像检索(上)——理论基础 8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统 第八章简介 本章介绍图像领域的相关案例,包括图像分类、图像分割、目标检测、目标跟踪、GAN、扩散模型等相关任务,这里将以一个完整的项目为一节,代码也将是独立的,希望通过一个个完整的案例,梳理在实际任务中所涉及的各个知识点、环节。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.1-classification.html":{"url":"chapter-8/8.1-classification.html","title":"8.1 图像分类——胸部X光肺炎分类","keywords":"","body":"8.1 图像分类案例——胸部X光肺炎分类 前言 前言:时隔7个月,终于能有时间继续撰写本书,在这期间,生活发生了重大变化、工作内容与岗位也有不小调整,整体而言还是未来可期,接下来一段时间将开始撰写中篇,案例应用。 本案例以胸部X光片二分类任务为案例,完整的介绍图像分类任务的训练过程。其中,涉及的知识点有: 图片的读取与dataset的编写 数据增强策略消融实验:手动设置数据增强,AutoAug实验 基于torchvision的预训练模型使用与替换:调用resnet、convnext模型 完整的训练日志分析 模型推理代码及推理速度、吞吐量计算对比 案例的讲解不再拘泥于代码细节,而是从任务分解出发,将该案例任务划分为多个模块,并对每个模块涉及的知识点及核心代码进行讲解。 图像分类的主代码采用第七章第四节中的训练脚本实现。 数据模块 首先来看数据部分,数据通过mendeley下载,得到的是ChestXRay2017.zip压缩包,图片已经划分好训练集与验证集。 获取数据后,需要对数据基础情况进行了解,首先看目录组织形式,便于dataset的编写,目录组织形式如下: ├─test │ ├─NORMAL │ └─PNEUMONIA └─train ​ ├─NORMAL ​ └─PNEUMONIA 数据为jpeg格式,类型为灰度图像,长宽在600-1000像素左右。 接下来即可编写dataset,这里仍旧需要dataset的基础知识,可以快速回顾第三章 PyTorch 数据模块。 dataset配套代码。 数据加载完成后,需要做数据增强,这里可以手动根据任务背景知识进行手动设计,或者使用AutoAug中的数据增强策略。 手动设计 对于手动设计,这里给出了基于torchvision的案例与albumentations的案例,分别位于train_main.py与train_aug.py 这里要重点介绍的是albumentations的使用,需要注意的是数据normalize与totensor的地方有些不同,详情看 A.Normalize(normMean, normStd, max_pixel_value=255.), # mean, std, 基于0-1,像素值要求0-255,并通过max_pixel_value=255,来实现整体数据变换为0-1 ToTensorV2(), # 仅数据转换,不会除以255 对于pytorch,totensor是会除以255的,而albumentations是在normalize中实现除以255的操作。 AutoAug 一些任务可以套用AutoAug的策略,关于自动数据增强可以回顾第三章,在代码实现时候也需要注意将变换后的数据进行衔接,这里主要是把数据的size变换到模型接收的大小。从代码上看,autoaug可以理解为一个打包好的transform.compose,插入自定义的compose中即可。 auto_aug_list = torchvision.transforms.AutoAugment(transforms.AutoAugmentPolicy.IMAGENET) train_transform = transforms.Compose([ auto_aug_list, transforms.Resize(256), transforms.RandomCrop(input_size, padding=4), transforms.ToTensor(), transforms.Normalize(normMean, normStd), ]) 模型模块 关于模型的创建就很简单了,基于CNN的模型,可以通过torchvision直接创建,并且加载imagenet的预训练参数。 主要注意如何修改模型最后的FC层,来适应自定义任务的分类类别数。 对于提供的模型(如resnet, convnext),需要加载进来之后,debug形式的去观察模型的结构,看看最后一个FC层的定义是什么,然后针对性的修改。 例如:resnet50的是model.fc, convnext的是model.classifier[2],这里就需要大家对第四章module的概念有较深入的理解。 训练指令 resnet50 nohup python train_aug.py --data-path ./data/chest_xray --batch-size 64 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --model resnet50 > ./log.log 2>&1 & nohup python train_main.py --data-path ./data/chest_xray --batch-size 64--workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --model resnet50 > ./log.log 2>&1 & nohup python train_aug.py --data-path ./data/chest_xray --batch-size 64 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --model resnet50 --autoaug > ./log.log 2>&1 & convnext nohup python train_aug.py --data-path ./data/chest_xray --batch-size 32 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --print-freq 20 --model convnext > ./log.log 2>&1 & nohup python train_main.py --data-path ./data/chest_xray --batch-size 32 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --print-freq 20 --model convnext > ./log.log 2>&1 & convnext-tiny nohup python train_aug.py --data-path ./data/chest_xray --batch-size 32 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --print-freq 20 --model convnext-tiny > ./log.log 2>&1 & nohup python train_main.py --data-path ./data/chest_xray --batch-size 64 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --print-freq 20 --model convnext-tiny > ./log.log 2>&1 & 训练实验记录 其它模块代码都是先前脚本中有的,就不再赘述,在本案例中,做了一些对比实验。 对比实验一:resnet50与convnext-base/tiny的比较 对比实验二:手动设计数据增强与AutoAug数据增强的比较 结论一:简单场景下,resnet50适用性更好;convnext-base与convnext-tiny在本任务差别不大; 结论二:AutoAug数据增强方法稍差些,非自然图像场景,手动设计数据增强策略效果更好; 模型名称 日志文件夹 acc convnext-tiny 2023-02-09_09-50-07 92.5 convnext-base 2023-02-08_20-28-44 92.3 resnet50 2023-02-08_16-37-24 93.8 resnet50 + albumentation 2023-02-08_17-45-29 94.87 resnet50 + autoaug 2023-02-09_13-45-08 93.3 模型推理与时间测试 模型训练好之后是以将模型的权重参数存储为字典形式,放在了pt文件中,在未来使用时候需要创建模型(nn.module),然后把参数加载进去,同时需要注意在推理阶段的数据前处理。 在配套代码中,实现了一张图片的推理,同时对resnet50的推理速度进行了评估,推理速度效率如下: 设备 推理速度(bs=128) 吞吐量(bs=128) GTX 1080 Ti 5.23 ms/例0.67s ÷ 128 = 5.23 ms 191 帧 / 秒 1 ÷ 5.23 * 1000 = 191 RTX 3060 Laptop 2.18 ms/例0.28s ÷ 128 = 2.18 ms 459 帧 / 秒 1 ÷ 2.18 * 1000 = 459 Inte i7-11800H @ 2.30GHz 八核 41.2 ms/例 5.27s ÷ 128 = 41.2 ms 24.3 帧 / 秒1 ÷ 41.2 * 1000 = 24.3 i7-7700 @ 3.60GHz 101.8 ms/例 13.03s ÷ 128 = 101.8 ms 9.8 帧 / 秒1 ÷ 101.8 * 1000 = 9.8 综合来看,一般的cpu可以实现实时推理,gpu推理速度是cpu的10-50倍。 PS:本次推理时间测试并不充分,不可代表实际生产下的推理效率,因此需要额外注意几个点: 时间测算需考虑数据前处理、后处理时间; 数据组batch后,可充分利用gpu资源,单位时间内推理的图片数更多,但也需要考虑组batch的时间消耗 小结 本小节通过胸部X光片的二分类场景,详细描述了从数据集读取、数据增强策略、模型finetune修改、实验对比、模型推理的各个步骤,包含代码与理论介绍。 通过本小节,应能独立实现图像分类任务。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.2-segmentation.html":{"url":"chapter-8/8.2-segmentation.html","title":"8.2 图像分割——脑MRI胶质瘤分割","keywords":"","body":"8.2 图像分割案例——脑MRI胶质瘤分割 前言 本案例以脑部MRI肿瘤数据为例,介绍图像分割(本节特指语义分割)的训练、推理过程。其中,涉及的知识点有: 基于csv的数据集维护管理,及其dataset编写; smp库的介绍与使用:segmentation_models_pytorch库,是语义分割的高级API库,提供9种分割架构、数百个encoder的backbone及预训练权重,以及分割的loss和衡量指标计算函数,是语义分割的好帮手,使用它可以快速实现各语义分割功能; 对smp中9中网络架构对比实验,网络架构分别是:'Unet', 'UnetPlusPlus', 'MAnet', 'Linknet', 'FPN', 'PSPNet', 'DeepLabV3', 'DeepLabV3Plus', 'PAN'; 语义分割iou计算时,image-wise与整体计算的差异,当纯阴性片时,iou统计应当采用image-wise更合理。 探究不同backbone对于语义分割的效果差异; 探究不同loss对语义分割的效果差异; 探究encoder采用较小学习率时,模型的精度变化。 本案例将从数据介绍、训练代码、smp库使用、对比实验和模型推理,五个模块进行讲解。 数据模块 数据集来自Kaggle,包含110位脑胶质瘤患者的MRI数据,总共3929张图片,其中有肿瘤区域的图片为2556张,阴性图片1373张。 下图为每个患者图片数量,以及阴、阳图片的比例汇总,从图中可知,一个MRI序列包含20-40张图片,其中出现肿瘤的图片有60%左右。(不过这里需要注意,肿瘤区域的像素点还还是远小于阴性像素点区域的) 下图为数据集示意,第一行为MRI图像,第二行为人工标注的脑胶质瘤mask。 数据集划分 首先下载数据集,解压得到kaggle_3m文件夹,然后设置数据根目录data_dir = args.data_path,运行下面代码,即可得到对应csv python 01_parse_data.py --data-path /mnt/chapter-8/data/kaggle_3m 数据集为110个文件夹形式,这里需要进行数据集划分,本案例采用csv对数据集进行维护,这里将会通过01_parse_data.py对数据集进行解析,获得以下三个csv data_info.csv:包含文件夹id、图片路径、标签路径; data_train.csv:根据文件夹id分组划分的训练集,比例为80%,有88位患者的3151张图片; data_val.csv:根据文件夹id分组划分的验证集,比例为20%, 有22位患者的778张图片; 知识点:数据划分需要按患者维度划分,不能基于图片维度随机划分,基于图片维度随机划分会使得模型存在作弊行为,导致模型在真实应用场景下效果很差。 为什么不能基于图片维度划分数据?因为这样做的话,以为患者的40张图片,有32张用于训练,另外8张用于测试,这8张与那32张是非常接近的,因为是同一个人的连续影像。这样划分的数据集是带有偏差的,理所当然的效果很好,模型不容易出现过拟合。后续的实验也证明了这一点,基于图片维度划分的精度要高出10个百分点。 Dataset编写 dataset编写就相当容易了,因为数据的路径信息已经获得,因此只需要注意数据读取进来之后,如何转换称为标签的格式即可。 这里要注意,对于语义分割,若采用的是交叉熵损失函数,Dice损失函数,它们要求标签是一个long类型数据,不需要手动转为one-hot向量,因此对于本实验,mask要变成一个[256,256]的矩阵,其中每个元素是0或者1。对应的实现代码如下: mask = cv_imread(self.df.iloc[idx, 2]) mask[mask == 255] = 1 # 转换为0, 1 二分类标签 mask.long() 训练代码 训练代码整体结构仍旧沿用第七章第四节中的训练脚本实现。 在此处需要做的修改主要是,语义分割模型的创建、分割模型的Loss创建、分割模型指标评价,以下四行代码分别是对应的实现 model = smp.Unet(encoder_name=args.encoder, encoder_weights=\"imagenet\", in_channels=3, classes=1) criterion = smp.losses.DiceLoss(mode='binary') tp, fp, fn, tn = smp.metrics.get_stats(outputs.long(), labels, mode=\"binary\") iou_score = smp.metrics.iou_score(tp, fp, fn, tn, reduction=\"macro\") 可以发现,这里面无一例外都用到了smp库,下面简单介绍smp库的优点,以及使用方法。 smp库介绍 segmentation-models-pytorch是pytorch的语义分割工具库,提供9个分割框架,数百个encoder,常用的loss,指标计算函数,非常方便开发者进行语义分割开发。 这是smp库的github链接与官方文档 github: https://github.com/qubvel/segmentation_models.pytorch docs:https://smp.readthedocs.io/en/latest/ 它安装很方便,只需要pip即可, pip install segmentation-models-pytorch。 掌握pytorch基础知识的话,smp库只需要10分钟即可掌握上手,更系统应用建议配合smp库的两个案例进行学习。 下面将从模型创建、loss创建、指标计算三个部分介绍smp使用。 模型创建 语义分割模型发展至今,主要还是采用encoder-decoder的形式,通常会采用主流的CNN作为encoder,decoder部分则进行随机初始化去训练。 而encoder与decoder之间如何信息交互、以及decoder由哪些组件构成等等一系列问题,就引出了不同的语义分割架构。 在smp中,提供了9种常用的语义分割模型架构,分别是'Unet', 'UnetPlusPlus', 'MAnet', 'Linknet', 'FPN', 'PSPNet', 'DeepLabV3', 'DeepLabV3Plus', 'PAN'。 在语义分割中,除了架构、encoder,输入和输出的维度也非常重要,这关系到可接收的数据形式是什么,以及可以预测的类别有多少个。 因此,一个语义分割模型的创建,需要确定架构、选择encoder、再确定输入通道数、输出通道数。 下面介绍unet的创建 import segmentation_models_pytorch as smp model = smp.Unet( encoder_name=\"resnet34\", # choose encoder, e.g. mobilenet_v2 or efficientnet-b7 encoder_weights=\"imagenet\", # use `imagenet` pre-trained weights for encoder initialization in_channels=1, # model input channels (1 for gray-scale images, 3 for RGB, etc.) classes=3, # model output channels (number of classes in your dataset) ) 对于初学者来说,那么多模型或许是个头痛的问题,后续也进行了对比实验,采用相同的encoder(resnet-18),分别训练9个语义分割架构,观察精度变化。 从经验来说,凡是有系列的模型都是应用广泛、相对可靠的模型,例如net系列,deeplab系列,yolo系列等等。 如何用代码来实现类属性的调用,这里是一个值得学习的代码段,这里主要通过getattr()方法获取module的属性,然后对其进行实例化即可。 archs = ['Unet', 'UnetPlusPlus', 'MAnet', 'Linknet', 'FPN', 'PSPNet', 'DeepLabV3', 'DeepLabV3Plus', 'PAN'] for arch_str in archs: model_class = getattr(smp, arch_str) model = model_class(encoder_name=args.encoder, encoder_weights=\"imagenet\", in_channels=3, classes=1) 更多关于分割模型的介绍,可以参见:https://smp.readthedocs.io/en/latest/ 损失函数创建 smp提供了8个损失函数,分别是JaccardLoss、DiceLoss、TverskyLoss、FocalLoss、LovaszLoss、SoftBCEWithLogitsLoss、SoftCrossEntropyLoss、MCCLoss。具体差异参见官方文档,这里要讲讲损失函数创建时,需要设置的模式。 损失函数创建时需要设置mode,smp库提供了3种mode,分别是二分类、多分类、多标签分类。 二分类:'binary', 用于一个类的分割(阴性不算一个类,例如本案例),对于二分类,标签需要是(N, H, W)的形式,元素为0或1,,它不需要通道维度,而模型输出是跟着pytorch走的,因此仍旧是4D张量,形状是(N, 1, H, W). 多分类:'multiclass',多分类是更为常见的场景,例如VOC、COCO数据集,这时标签元素为0, 1, ..., C-1,类似交叉熵损失函数(可以把语义分割看成是逐像素分割),模型的输出自然是(N, C, H, W)了,因为一个像素点,需要用C维的分类概率向量来做分类。 多标签:'multilabel',多标签语义分割指一个像素即是A类又是B类,因此它的标签需要借助C个通道来标注,对应类别设置为1,其它设置为0。所以标签形式为(N, C, H, W),模型输出仍旧是(N, C, H, W),多标签需要注意模型输出时就不再做softmax,而是对每个神经元做sigmoid,以此判断该类是否存在。 对于loss的选择,一般是交叉熵损失函数、Dice这两个系列,其它的可以自行选择,它就像模型架构一样,学术界有几十上百个选择,但在工程领域,仁者见仁智者见智,更多的语义分割损失函数可参考SegLoss 指标计算 语义分割可以理解为逐像素的图像分类,因此图像分类的各指标也可用于衡量分割模型,但分割与分类不同的是,它注重空间结构信息,关注集合与集合之间的关系,因此更常用的是IoU或Dice系数来评估模型。 IoU(Intersection over Union,交并比)是用来衡量两个集合重叠的情况,公式计算为:交集/并集,而dice系数(Dice similarity coefficient,又名dsc)也用于评估两个集合重叠情况,但是计算公式不一样,而且根据文章阐述, \"Dice倾向于衡量平均性能,而 IoU 倾向于衡量最坏的表现。\" 具体计算时,通常先获得tn, tp, fn, fp,然后计算指标。蓝色部分为TP(True Positives),红色部分为FN(False Negatives),黄色部分为(False Positives) smp工具库中也是这么做的,首先根据smp.metrics.get_stats函数,获得tp, fp, fn, tn。随后通过各指标计算函数获得相应指标, 可计算的指标有fbeta_score、f1_score、iou_score、accuracy、precision、recal1、sensitivity、specificity、balanced_accuracy、positive_predictive_value、negative_predictive_value、false_negative_rate、false_positive_rate、false_discovery_rate、false_omission_rate、positive_likelihood_ratio、negative_likelihood_ratio。 来看一段使用示例: import segmentation_models_pytorch as smp # lets assume we have multilabel prediction for 3 classes output = torch.rand([10, 3, 256, 256]) target = torch.rand([10, 3, 256, 256]).round().long() # first compute statistics for true positives, false positives, false negative and # true negative \"pixels\" tp, fp, fn, tn = smp.metrics.get_stats(output, target, mode='multilabel', threshold=0.5) # then compute metrics with required reduction (see metric docs) iou_score = smp.metrics.iou_score(tp, fp, fn, tn, reduction=\"micro\") f1_score = smp.metrics.f1_score(tp, fp, fn, tn, reduction=\"micro\") f2_score = smp.metrics.fbeta_score(tp, fp, fn, tn, beta=2, reduction=\"micro\") accuracy = smp.metrics.accuracy(tp, fp, fn, tn, reduction=\"macro\") recall = smp.metrics.recall(tp, fp, fn, tn, reduction=\"micro-imagewise\") 在使用时,需要注意的有2个地方, 计算tp, fp, fn, tn时,模型输出要转换为类别标签,而不是概率向量。 对batch数据统计时,是否需要考虑样本不平衡问题,进行加权平均,是否需要基于图像维度进行计算后再平均。 对于问题1,get_stats函数提供了二分类、多标签时的处理方法,只需要在model下设置'binary' 或 'multiclass'之后,设置threshold即可。从此可看出需要手动进行sigmoid(); 对于问题2,较为复杂一些,smp提供了6个模式,这些在sklearn中有的,分别是,'micro' , 'macro' , 'weighted' , 'micro-imagewise' , 'macro-imagewise' , 'weighted-imagewise'。 'micro’ 用于计算总体的指标,不对每个类别进行计算, 'macro'计算每个类别的指标,然后求和平均,不加权。 ’weighted’ 计算每个类别的指标,然后根据每类样本数,进行加权求平均 可参考(https://blog.csdn.net/qq_27668313/article/details/125570210) 对于x-imagewise,表示根据图片维度进行计算,然后将指标求取平均。 因此,可知道,前缀micro、macro、weighted是决定如何对类别进行求取平均,后缀-imagewise表示如何对图片之间进行求平均。 所以,对于binary来说,'micro' = 'macro' = 'weighted' ,并且 'micro-imagewise' = 'macro-imagewise' = 'weighted-imagewise'。 在这里重点讲一下,本案例采用的是macro来统计,因此iou比较低,如果是手写iou统计,一般会基于图片维度计算,然后再平均,也就是macro-imagewise。 本案例中,由于大量阴性图片的存在,所以若不采用-imagewise的话,阴性片虽然预测正确,但实际上是tn很大,而tn缺不计入iou计算中。若采用imagewise,阴性预测正确时,iou为1,从而可以大幅度提高iou值。 下面可以通过代码观察,对于阴性片,模型预测完全正确,tp, fp, fn值都是0的情况下,iou也是会被计算为1的。 tp, fp, fn, tn = smp.metrics.get_stats(c, b, mode=\"binary\") tp Out[12]: tensor([[0]], device='cuda:0') fp Out[13]: tensor([[0]], device='cuda:0') fn Out[14]: tensor([[0]], device='cuda:0') tn Out[15]: tensor([[65536]], device='cuda:0') smp.metrics.iou_score(tp, fp, fn, tn, reduction=\"macro\") Out[16]: tensor(1., device='cuda:0') 对比实验 此部分实验没有进过严格微调,只用于横向比对,主要观察不同架构、不同backbone、不同学习率策略之间的差异,可以大体上观察出一定的趋势,供大家参考,以便后续选择模型。 在这里主要做了4次实验,分别是: 实验一:不同backbone的实验,猜想为越复杂的backbone,精度越高,这里采用uent-resnet18/34/50来观察。 实验二:不同网络架构之间的实验,猜想是有系列的模型,鲁棒性更高,适用性更广,如unet、deeplab系列。这里backbone均采用resnet-18,训练了九个模型。 实验三:encoder采用小10倍学习率,让decoder学习率高一些,猜想是要比相同学习率的效果更好,这里就需要与实验二中的九个模型精度对比。 实验四:根据图片随机划分与病人维度划分的差异,猜想是根据图片随机划分,精度要比病人高的,毕竟用到了极其类似的图片进行训练。 实验完整代码在github 实验一:不同backbone的差异 python 02_train_seg.py --batch-size 16 --workers 8 --lr 0.01 --epochs 100 --useplateau --model unet --encoder resnet18 python 02_train_seg.py --batch-size 16 --workers 8 --lr 0.01 --epochs 100 --useplateau --model unet --encoder resnet34 python 02_train_seg.py --batch-size 16 --workers 8 --lr 0.01 --epochs 100 --useplateau --model unet --encoder resnet50 unet-resnet18 unet-resnet34 unet-resnet50 miou 0.82 0.79 0.84 结论:可证实猜想基本正确,越复杂的backbone,精度越高。但resnet34的精度反而比resnet18要差,这个需要仔细研究,在此不做讨论。 实验二:对比不同模型架构差异 python 03_train_architecture.py --batch-size 16 --workers 4 --lr 0.01 --epochs 100 --useplateau --encoder resnet18 这里可以看到unet++和deeplabv3+精度较高,过它们的训练速度也是最慢的,侧面反映出它的模型复杂,理所应当获得最高精度。 'Unet' 'UnetPlusPlus' 'MAnet' 'Linknet' 'FPN' 'PSPNet' 'DeepLabV3' 'DeepLabV3Plus' PAN miou 0.81 0.85 0.73 0.83 0.83 0.78 0.81 0.84 0.62 实验三:encoder采用更小学习率差异 # encoder采用小10倍学习率 python 03_train_architecture.py --batch-size 16 --workers 4 --lr 0.01 --epochs 100 --useplateau --encoder resnet18 --lowlr 要想获得好的精度,往往需要各种trick来微调,对于语义分割模型中encoder部分,可以采用较小学习率,因为encoder提取的是共性的语义特征,对于decoder才需要特有的特征,因此可以对它们两个进行差异化设置学习率。为此,对encoder学习率乘以0.1,观察模型精度变化。 从实验结果来看,简单粗暴的调整大都未获得精度提升,反而都存在3-4个点的掉点,deeplabv3, MAnet, PAN缺有提升,由此可见训练的trick还是难以适应各个场景的。 综合实验二、实验三,可以观察到unet系列和deeplab系列都是比较稳定的,这也是它们一直被工业界认可、使用至今的原因,因此推荐首选Unet系列或deepalabv3+进行任务的初步验证。 'Unet' 'UnetPlusPlus' 'MAnet' 'Linknet' 'FPN' 'PSPNet' 'DeepLabV3' 'DeepLabV3Plus' PAN lowlr 0.79 0.81 0.82 0.81 0.81 0.76 0.83 0.80 0.80 base 0.81 0.85 0.73 0.83 0.83 0.78 0.81 0.84 0.62 实验四:根据图片随机划分数据集 此实验需要在01_parse_data.py 代码中的data_split()函数下修改groupby的对象,需要改为 grouped = dff.groupby('image_path') # bad method 并且将csv文件名做相应修改。 miou 细节 unet-resnet18 0.82 基于patient维度划分 unet-resent18 0.88 基于imgs维度划分 很明显,基于imgs维度划分存在很明显的性能提升,约6个点,但是这个提升是假性的,会迷惑工程师,误以为模型很好。 这是实际业务场景中常碰到的问题,一定要注意业务数据的维度划分问题。 模型推理 模型推理与图像分类类似,没有什么特殊的地方,在这里想重点讲一下模型输出的数据如何转换为要用的类别mask。 由于是二分类,并且输出是一通道的矩阵,因此会采用sigmoid将它变为分类概率的形式,然后再通过阈值(一般设为0.5)转换为0/1的mask矩阵。如下代码所示: outputs = model(img_tensor_batch) outputs_prob = (outputs.sigmoid() > 0.5).float() outputs_prob = outputs_prob.squeeze().cpu().numpy().astype('uint8') 接着通过opencv寻找轮廓函数可以得到边界,最后进行可视化。 最后通过imageio实现gif图的生成,可参见05-gen-gif.py。 即可得到下图 小结 通过本案例,可以掌握: 语义分割模型的训练与推理流程 smp工具库中的模型创建、损失函数创建、评价指标计算使用 评价指标中,micro、macro、weighted, x-imagewise的差别 dice与iou评价指标的定义与差异 9个语义分割架构实验比对,为后续选择模型提供参考 医学数据处理划分维度,需要基于患者维度,不可基于图片维度 基于本案例需要进一步学习的内容: 采用多类分割任务进行实验,了解softmax与sigmoid的处理差异; 进一步了解unet系列、deeplab系列适合的场景 语义分割后处理:图像处理的形态学操作 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.3-detection.html":{"url":"chapter-8/8.3-detection.html","title":"8.3 目标检测——无人机检测","keywords":"","body":"8.3 目标检测——无人机检测 前言 目标检测在工业界应用广泛,例如工业生产线上的质量检测、监控工业场景中的安全问题、机器人的自主导航等,可以提高生产效率和产品质量,降低生产成本,也可以提高工作环境的安全性,减少事故发生,由此可见目标检测意义重大。 本小节,将介绍工业界中的热宠YOLOv5,并通过无人机场景检测任务进行实现。 本小节内容丰富,包括: VisDrone数据集介绍 目标检测常见数据格式介绍:VoC, COCO, YOLO YOLOv1-YOLOv8 概述:了解YOLO发展历史,各版本模型优点,缺点 YOLOv5 源代码结构剖析及使用步骤:了解优秀的项目代码结构,设计 YOLOv5在VisDrone数据集上的多组实验,了解不同容量的YOLOv5能力 数据模块 VisDrone数据集介绍 VisDrone数据集是一个大规模的用于视觉目标检测、跟踪和计数等任务的数据集,由天津大学整理于2018年发布,论文为Vision Meets Drones: A Challenge,可通过github下载。 其中包含多个任务,这里采用VisDrone-DET数据集,进行目标检测案例学习,VisDrone-DET也就是VisDrone2018。 VisDrone-DET中,总共包含10209张,划分为了4个数据集,额外给出了一个1610张模拟的测试集-dev,训练集6471张,验证集548张,测试集-dev1610张,测试集-challenge 1580张。 数据特点:图片分辨率较大、目标小、目标存在一定程度遮挡,部分是严重遮挡(例如汽车)。 下面介绍数据集目录组织形式与标签含义。 数据包含annotations和images两个文件夹,分别存储标注信息txt文件,图片jpg文件,文件名保持一致。 标签txt文件中,一行表示一个标注框,一行有8个信息,例如 495,473,120,46,1,4,0,1 507,447,107,38,1,4,0,1 分别表示: ,,,,,,, bbox:前四个为bbox信息; score:表示标注框的置信度,取值为0或1,0表示忽略,1表示可用。 object_category: 目标类别,0-11,分别是,gnored regions(0), pedestrian(1), people(2), bicycle(3), car(4), van(5), truck(6), tricycle(7), awning-tricycle(8), bus(9), motor(10), others(11) truncation:截断程度,取值为0,1。0表示无截断,1表示目标有1%~50%的区域再当前帧(图像)之外。 occlusion:遮挡程度,0-2。0表示无遮挡,1表示1%-50%遮挡,2表示50%-100%遮挡。 对于本案例使用,仅需要剔除score=0的bbox。 目标检测数据集格式 了解数据之后,需要把数据集转换为代码可以接收的形式。与分类分割任务不同,目标检测领域的数据格式相对固定,一般不需要自己重写数据加载模块,而是跟随代码框架的要求来适配。 目标检测目前主流的有三种格式,VOC格式、COCO格式、YOLO格式,下面分别介绍。 VOC数据集格式如下: 图像位于JPEGImages文件夹。 标注文件位于Annotations文件夹中,标注文件与图像文件文件名应当相同,并且XML格式描述目标的位置和类别等信息,bbox形式为xmin、xmax、ymin、ymax。 COCO数据集格式如下: 图像位于images文件夹中。 标注文件位于annotations文件夹下的instances_train2017.json,所有标注均存储在一个json中,并通过特定字段获取图片对应的标签信息。bbox形式为xmin, ymin, w, h。 YOLO数据格式如下: 图像位于images文件夹下。 标注文件位于labels文件夹下,标注文件与图像文件文件名应当相同,并且以txt文件存储标签信息。txt中一行是一个标注框,一行的格式为: class x_center y_center width height,其中bbox的数值需要除以长/宽,使得值为0-1之间。 在这里侧面证明了YOLO模型的强大,以往算法开发要适应数据集的格式,而现在yolo系列模型被广泛使用,使得大家愿意接受YOLO数据格式。 标签数据转换代码 在目标检测任务开发过程中,无法避免数据集之间的转换,这里推荐repo,可以实现三种数据格式之间的互相转换。 在本案例中,适合自行编写脚本进行转换,这里yolov5官方也给出了代码,因此直接复用即可。 推荐yolov5中使用的几个小函数: 文件夹的创建, Path对象可直接.mkdir(),并且可设置递归及判断存在与否。 from pathlib import Path (dir / 'labels').mkdir(parents=True, exist_ok=True) # make labels directory pbar显示进度, 将可迭代对象用tqdm包一下,并且设置打印的字符串信息格式desc pbar = tqdm((dir / 'annotations').glob('*.txt'), desc=f'Converting {dir}') 使用配套代码,运行即可得到与images文件夹在同级目录下的labels文件夹 python visdrone2yolo.py --data-path /mnt/chapter-8/data/visdrone 到这里yolo格式数据集已经准备好,如何在代码中使用及配置,这部分放到模型模块中的yolo框架讲解部分。 模型模块 在数据模块,讲解了目标检测中数据集常见的格式,并且已经转换称为yolo格式,等待使用。 若是在分类或分割中,我们会手写代码进行实验,然而在目标检测,绝大多数是不需要的,原因有 目标检测中涉及多个模块,重新造轮子的时间成本高,容易出bug; 目标检测可用成熟的“框架”(可用的repo)很多,如ultralytics的yolov3/yolov5/yolov8,mmdetection,paddledetction等,感谢前人的工作! 成熟的\"框架\"指,功能齐全,满足绝大多数场景,受众多,经得起时间的考验,可以放心使用。 在图像分割的实验中就指出,凡是有系列的,优先考虑,因此,这里选择ultralytics的yolov5,它是目前为止(2023年3月10日07:07:25)star最高的目标检测开源代码仓库,已经36K,足以证明yolov5的稳定性与可靠性。 yolo系列简介 YOLOv1 YOLOv2 YOLOv3 YOLOv4 YOLOv5 YOLOv6 YOLOv7 YOLOv8 时间 2015.6.8 2016.12.25 2018.4.8 2020.4.23 2020.6.10 2022.6.23 2022.7.7 2023.1 作者 Joseph Redmon Joseph Redmon Joseph Redmon Alexey Bochkovskiy Ultralytics 美团 Alexey Bochkovskiy Ultralytics 深度学习目标检测框架发展快10年时间,只有yolo(You Only Look Once)系列久经不衰,截至目前公认的版本已经到了v8,很难有人能将v1-v8的细节全部吃透。 在校有时间的同学十分建议从v1-v8认真学习,这样可以理解目标检测的发展,锻炼科研意识,掌握代码能力。 对于工作繁忙的工程师来说,它们只是解决问题的工具,了解工具的优点与缺点,有的放矢的应用在各个项目场景也是不错的选择。 为此,特地总结yolov1-v8的特点,为后续使用工具打下基础。 参考deephub的文章,可以看到yolo系列主流的模型发展历程。 yolov1:2015年提出的one-stage目标检测算法,与当时的Fater RCNN(two-stage)共同称为当时最受欢迎的检测模型。特点为anchor-free:没有anchor的概念,每个cell直接输出bbox。每个cell仅输出2个bbox,每个cell输出向量为(20+ (4+1)*2),20为20个类,1为bbox概率,4为bbox信息,一张图片最终变为7×7的特征图,一个cell只能预测1个类,因此定位粗糙,小目标不友好,对重叠物体检测能力差。 yolov2:针对yolov1定位不精准问题,借鉴faster rcnn的anchor-base的概念,并且引入k-means实现anchor的自动配置。 yolov3:划时代意义的目标检测算法,也奠定了目标检测之后的范式,backone+neck+多尺度。yolov3网路结构简单,并且采用多尺度特征图实现不同尺寸目标的检测,速度与精度在当时都是优于其他模型。yolov3采用的是手动配置的33=9种anchor,anchor的参数设置是通过k-means对标签进行聚类发现的,*3种尺寸,3种长宽比。 yolov4:yolov4发布前有个小插曲,那就是YOLO之父Jeseph Redmon,由于“无法忽视工作带来的负面影响”,公开宣布隐退。好在有大神接了他的大旗,在yolov3推出快2年的时间,yolov3的改进版v4终于在2020年来了,yolov4开始,可以认为是一个分割点,更准确地说yolov3是后续模型的分割点。借助paperswithcode的一个精度图,可以看到yolov3在coco的map是30-40之间,而往后v4-v8已经来到50-60的区间,已经不在一个档次。 对于yolov4,它对当时深度学习的多种tricks进行了实验,集成到yolov3上进行改进,精度和速度都得到大幅度提升。它使用了大量tricks,包括WRC、CSP、CmBN、SAT、 Mish activation、Mosaic data augmentation、CutMix、CmBN、DropBlock regularization 和 CIoU loss、GIoU loss。 yolov5:在yolov4发布后短短2个月,yolov5横空出世,并且带来了多种大小的模型, nano/s/m/l/x等尺寸,可适用于多种场景,同时配备高质量的开源代码仓库,短时间内就受到了广泛关注。yolov5数据增强上使用了Mosaic数据增强、自适应锚框计算、自适应图片缩放(推理时采用最小填充原则,加速推理)、融合新网络模块Focus、CSP结、FPN+PAN,GIOU_Loss,以及预测框筛选的DIOU_nms、 yolov6:2022年由美团提出的速度更快的检测模型,主打是速度,因此模型特点是backbone与neck的设计都为了适应硬件的运算,使用了Rep-Pan和EfficientRep块,head部分用了解耦的形式,在训练策略方面采用了anchor-free、SimOTA标记策略、SIoU盒回归的损失。 yolov7:在yolov6推出不到半个月,yolov7也发布了,yolov7团队与yolov4团队一致,属于官方YOLO团队(yolov4团队接过yolo之父Jeseph Redmon的大旗)。yolov7同样从速度方面做了许多优化,例如内存访问成本、I / O比率、element-wise、激活函数等,以及模型重参数化(re-parameterization)。 yolov8:yolov5的团队——ultralytics打造的集成图像分类、图像分割、目标检测于一体的结构,目前github地址并为采用yolov8而是采用ultralytics。发布2个多月后,论文仍旧未发布,具体优化内容请关注官方github,从代码中观察吧。 yolov5 代码结构讲解 根据广大工程师“用脚投票”的结果,本案例采用ultralytics的yolov5来实现目标检测,并学习代码中优秀的设计思想,同时剖析用户如何使用yolov5仓库代码。 后续建议跟进yolov8! 学习yolov5之前,推荐阅读yolov5的readme,其中包含了许多学习资料。 下图为yolov5(2023.03版)的代码结构,逻辑相当清晰,可分三个模块,三个模块是图像分类、图像分割和目标检测。 目标检测分为data、models、utils和运行脚本部分。 data:存放的主要是数据超参数配置yaml文件。 models:存放的是各模型的yaml配置文件,即模型创建依赖于yaml文件。 utils:存放各功能模块,如,数据增强augmentations.py, 自动计算anchor功能autoanchor.py,激活函数activations.py, fastapi接口等等。 检测模型的训练、验证、推理分别在:train.py, val.py, detect.py中。下面将重点讲解train.py的运行机制。 yolov5 训练机制讲解 train.py有600多行,并且调用了许多函数,一开始接触会感到困难。 不过不用担心,它还是pytorch框架,仍旧逃离不了基础核心模块, dataloader, module,loss, scheduler,迭代训练。 下面依次简要的说明train.py中是如何训练的。 参数配置模块 使用parse_opt()进行参数包装,训练时可指定模型、数据集配置yaml路径,超参数配置yaml路径等内容。 数据模块 man() --> train()-->create_dataloader(): 第188行,调用了create_dataloader()函数,并且传入了数据集配置yaml文件中的训练集路径。 utils/dataloders.py-->LoadImagesAndLabels(): 在函数内部采用LoadImagesAndLabels()类实现pytorch的dataset的生成,class LoadImagesAndLabels(Dataset), 代码有483行,里面实现了许多数据检测、数据加载功能,但是核心与pytorch的dataset是一致的,重点关注init中做了哪些初始化,以及getitem如何从磁盘加载图片并且做数据预处理、数据增强的即可。 模型模块 第125行实现模型创建,可以看到是通过配置信息进行创建的,这里的配置信息来自参数配置模块中--cfg或者指定预训练模型--weights, model = Model(cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create 再看往里看 DetectionModel继承自BaseModel,BaseModel集成nn.Module,里边细节就可通过nn.Module的基础知识一步一步剖析。 迭代训练模块 核心功能在,262行 262行:主循环 for epoch in range(start_epoch, epochs): # epoch ------------------------------------------------------------------ 284行:batch循环 for i, (imgs, targets, paths, _) in pbar: # batch ------------------------------------------------------------- 310行:前向推理,计算loss,反向传播 pred = model(imgs) # forward loss, loss_items = compute_loss(pred, targets.to(device)) # loss scaled by batch_size scaler.scale(loss).backward() 344行:epoch维度变更学习率,因此在batch的循环之外 lr = [x['lr'] for x in optimizer.param_groups] # for loggers scheduler.step() 日志输出模块 在runs文件夹下会有train文件夹,每一次实验会以exp{实验次序}创建文件夹,在下面会保存训练过程中的一系列有价值内容。 如下图所示,会有这些文件 weights:训练好的模型权重,包括last.pt, best.pt hyp.yaml:训练时的超参数,便于复现 results.png:训练曲线,便于分析训练情况,调整超参数 results.csv:训练指标记录表格 train_batch2.jpg:训练数据集,bbox绘制情况,十分有用,可用于检测数据标签是否处理正确! val_batch0_labels.jpg:验证数据集,bbox验证情况 val_batch2_pred.jpg:模型预测出的bbox在验证集上的情况。 混淆矩阵:针对标签的预测的情况进行混淆矩阵观察,这个混淆矩阵是转置了的,行是预测,列才是真实标签,以下图为例,汽车这个类别中,有78%的汽车框被预测为了汽车,有19%的汽车框没有被检测到,剩下2%的汽车框被检测出来了,但是分类时分为了van(箱式货柜车)。 PR_curve:PR曲线是各类别的AP情况 P_curve和R_curve:是观察模型对各类别分类情况,可用于挑选分类概率阈值, 横轴表示选择的阈值,纵轴是对应的值。可以看到阈值越小召回率越高,反之,精确度越低。 labels_correlogram.jpg:是借助seaborn的pairplot,绘制的多变量的二维分析相关性统计分析图。以列为基础,第一列是x与其它数据的关系,第一列,第一行,表示x的整体分布,可以看到是相对于中心点0.5对称的,表明矩形框在宽这个维度,左边有的数量与右边有的数量是一致的,并且呈现中间多,两边少的情况。 第一列,第二行,表示x与y的分布情况,同labels.jpg中第三幅图一样,观察矩形框整体情况是宽度偏中间,高度偏中间与中下部。 第一列,第三行,表示x与w的分布情况,呈现一个梯形,这个很合理,因为当x靠近图片的最左边的时候,即物体在图像的边界时,这个物体一般不会很大,否则根据拍照的基础原理,肯定会将镜头朝向主要物体,并放置在镜头中央,不至于在边界处。 第一列,第四行,表示x与h的分布情况。 第二列,第二行,是y的整体分布,可以看到是偏向0-0.5之间。 第三列,第三行,是w的整体分布。 第四列,第四行,是h的整体分布。 yolov5 训练VisDrone步骤 第一步:设置数据集配置yaml文件,首先到detection\\yolov5-master\\data\\下复制一份yaml,命名为mydrone.yaml,设置好路径即可,这里yolo数据格式只需要images路径就能通过相对路径寻找到labels。同时设置好检测类别数量与名称 path: G:\\deep_learning_data\\VisDrone # dataset root dir train: VisDrone2019-DET-train\\\\images # train images (relative to 'path') 128 images val: VisDrone2019-DET-val\\\\images # val images (relative to 'path') 128 images test: # test images (optional) nc: 10 # number of classes names: ['pedestrian', 'people', 'bicycle', 'car', 'van', 'truck', 'tricycle', 'awning-tricycle', 'bus', 'motor'] 第二步:将预训练模型下载到code/chapter-8/03_detection/yolov5-master下,下载方式为github。 第三步:在终端,运行训练指令,即可在runs/train下面看到对应日志. python train.py --imgsz 640 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 对比实验 实验一:visdrone数据集特点是分辨率大,一般的640,1000的尺寸无法满足要求,为此,进行了5种尺寸的训练,用于观察不同分辨率对精度的影响 实验二:yolov5提供多种尺寸的模型,这里观察s/m/l三种尺寸的模型对精度的影响。 实验三:同时观察yolov5自带的三种不同强度的数据增强带来怎样的精度变化。 更新:所有实验权重、实验文件已经上传云盘:链接:https://pan.baidu.com/s/11kQJcCka2VyR5ToF-N0BOQ 提取码:op4x 实验一/二:不同输入尺寸对模型精度的变化 python train.py --imgsz 640 --batch 24 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 960 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 1280 --batch 12 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 1600 --batch 8 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 1920 --batch 6 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml map50/map50:95 640 960 1280 1600 1920 yolov5s 0.33/0.18 0.44/0.26 0.50/0.30 0.54/0.33 0.55/0.34 exp0-4 yolov5m 0.38/0.21 0.48/0.30 0.53/0.33 0.57/0.36 0.59/0.38 exp11-15 yolov5l 0.40/0.23 0.50/0.31 0.55/0.35 0.57/0.37 0.60/0.39 exp16-20 从上图可以看出: 随着尺寸增大,精度得到提高,且1920仍未达到瓶颈,可继续增加图片尺寸来获得精度提高。 随着模型容量增大,精度得到提高;可根据任务难以程度选择合适容量的模型。 在size和模型容量两者间可以选择更适合的方式来涨点,即size也可以涨点,换大模型也可以涨点,如果不能同时采用,则根据上下游条件进行取舍。 实验三:不同数据增强方法的变化 这里套用yolov5提供的三种强度的数据增强方法,观察精度变化。 python train.py --imgsz 960 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 960 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-med.yaml python train.py --imgsz 960 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-high.yaml scratch-low scratch-med scratch-high map50/map50:95 0.44/0.26 0.44/0.26 0.43/0.26 exp5-7 从结果可知,yolov5中自带的low, med, high在本案例中效果都一样,并无差别。 模型推理 训练好模型后,可通过detect.py进行推理并观察结果,detect.py提供了多个参数接口 weights:训练好的.pt文件,.pt文件中存储了模型结构,因此无需额外指定模型结构的yaml文件 source:需要检测的数据来源,支持图片、视频、摄像头、网络视频url等 data:数据集yaml文件,关联检测的类别名称 imgsz:图像输入大小 conf-thres:检测框置信度阈值 iou-thres:非极大值抑制时的iou阈值设置 half:采用半精度(Float 16)进行推理,可提升推理速度,但有一定精度损失 其它配置参数可看代码注释,这面介绍detect.py中的核心代码结构,观察其是如何实现推理的。 第一部分,数据加载读取,对于数据的加载与读取,采用utils/dataloders.py中实现的3个类来实现包装,并进行迭代。如LoadStreams、LoadScreenshots、LoadImages,对于三个类的实例,在使用时,采用for循环进行依次取出数据 dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride) dataset = LoadScreenshots(source, img_size=imgsz, stride=stride, auto=pt) dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride) for path, im, im0s, vid_cap, s in dataset: 第二部分,模型加载,使用models/common.py中的DetectMultiBackend类实现,该类支持多种计算后端如pytorch\\onnx\\tensorrt\\jit\\dnn等等。其中,pytorch模型是通过models/experimental.py中的attempt_load()函数实现加载。attempt_load()需要的一个核心参数就是.pt文件路径。然后根据.pt内信息完成模型创建、权重加载等工作。 model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) model = attempt_load(weights if isinstance(weights, list) else w, device=device, inplace=True, fuse=fuse) 第三部分,推理与保存,推理主要两个步骤,模型前向传播,经过NMS后得到最终输出矩形框。对于结果可视化,这里采用Annotator类实现绘制,首先将图像传入Annottor,进行实例化,后续通过annotator.box_label()进行bbox与labels的绘制。 pred = model(im, augment=augment, visualize=visualize) pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det) # ------------------------------------------------------------------------------------------------ annotator = Annotator(im0, line_width=line_thickness, example=str(names)) annotator.box_label(xyxy, label, color=colors(c, True)) 运行以下推理指令,即可在.runs/detect/exp*下获得结果,下图为航拍视频推理示例 python detect.py --weights ./runs/train/exp2/best.pt --source G:\\DJI_0690.MP4 --data data/mydrone.yaml --imgsz 1280 --half python detect.py --weights best.pt --source G:\\DJI_0690.MP4 --data data/mydrone.yaml --imgsz 1280 到这里,yolov5代码就讲解完毕,yolov5代码库还有许多值得学习的地方,这里由于篇幅关系,作为拓展阅读推荐给大家: 模型导出为TFLite, ONNX, CoreML, TensorRT:https://github.com/ultralytics/yolov5/issues/251 TTA(test time augmentation): https://github.com/ultralytics/yolov5/issues/303 模型剪枝:https://github.com/ultralytics/yolov5/issues/304 yolov5训练技巧总结:https://github.com/ultralytics/yolov5/wiki/Tips-for-Best-Training-Results yolov5模型集成:https://github.com/ultralytics/yolov5/issues/318 小结 本案例介绍了yolov5实现无人机视角的目标检测,主要涉及以下知识点: Visdrone数据集介绍与标签含义解析,会有模糊程度与遮挡程度的两个额外标注信息。 目标检测常见数据形式:voc,coco,yolo形式,三者的bbox形式均不一样,使用时需要注意转换。xmin,ymin,xmax,ymax; xmin, ymin, w, h; x_center, y_center, w, h yolov1-v8模型简介:简要介绍v1-v8的模型特点,便于后续有的放矢的选择使用。 yolov5代码结构介绍:剖析yolov5项目代码结构,并分析如何进行训练、推理。 自定义数据集训练过程:详细介绍自己的数据集要如何使用yolov5进行训练的过程,核心在于了解yolov5的数据加载形式与模型加载形式都通过yaml文件进行管理。 对比实验:分析分辨率、模型容量、数据增强方法带来的精度变化,对后续重要超参数设置具有指导性意义。 本案例已近万字,可以快速用代码实现目标检测,但是对于目标检测的学习来说,还远不够,案例初衷还是通过具体的项目,来巩固pytorch基础知识。 最后,可以发现,即使是采用yaml来管理数据和模型,在实现的时候还会继承dataset和dataloader,以及nn.Module,由此可见第三章和第四章的概念有多么重要。 对于想要深入了解目标检测的朋友,推荐学习: 非深度学习目标检测时期的检测方法; faster rcnn + yolov3的详细理论过程与代码实现 yolov3后时代下的,anchor-free, one-stage的检测模型 特定问题目标检测:小目标检测, 旋转目标检测,密集场景目标检测,超大分辨率图像目标检测、遮挡场景目标检测等等 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.4-tracking-1.html":{"url":"chapter-8/8.4-tracking-1.html","title":"8.4 目标跟踪(上)——DeepSORT原理","keywords":"","body":"8.4 目标跟踪(上)——DeepSORT原理 前言 目标跟踪技术可以用于人流量计数和车流量计数等,能够帮助人们更好地了解和掌握一个地区的交通状况和人流状况。这些计数功能有以下几个价值和意义: 交通规划:通过了解车流量,可以更好地规划交通路线和疏导车流,提高交通效率,减少拥堵,从而减少交通事故的发生。 商业决策:通过了解人流量,可以更好地了解商业活动的热点区域,从而制定更加有效的营销策略和经营计划,提高商业效益。 目标跟踪(object tracking)定义是,在视频序列中识别目标并赋予唯一标识,同时跟踪目标在视频序列中的位置。在自动驾驶、监控、人机交互等领域都有应用。 目标跟踪常用的策略是TBD(Tracking-by-Detecton),又称DBT(Detection-Based-Tracking)。即在每一帧进行目标检测,再利用目标检测的结果来进行目标跟踪,这一步称为数据关联(Data Assoiation)。与之相对的,是DFT(Detection-Free Tracking), DFT使用较少。 根据目标的数量,目标跟踪可分为单目标跟踪(Sing-Object Tracking)与多目标跟踪(Multi-Object Tracking),目前MOT研究较多,并且MOT可覆盖SOT。 根据处理时效性,又可分为在线跟踪(Online)与离线跟踪(Offline),离线跟踪是指可以使用后续帧的信息来预测当前帧,在视频分析中可用。在线跟踪是只能采用前序帧信息与当前帧信息,这是目前主流方案。 本案例中的目标跟踪属于多目标跟踪、在线跟踪、TBD。 通过简单定义,可以知道,目标跟踪分两步 检测:找出当前帧中的目标,即目标检测 关联匹配:将当前目标与历史帧中的目标进行关联与匹配 检测可以采用各类目标检测算法,关联匹配可以采用deepsort算法。 本案例将详细介绍DeepSORT算法原理,并基于yolov5实现车流量统计代码。 DeepSORT算法流程 DeepSORT算法发表于2017年,其是SORT的改进版。SORT(Simple Online and Realtime Tracking)于2016年发表,主要基于卡尔曼滤波和匈牙利算法实现。 DeepSORT算法则是对SORT加入了Deep Association Metric进行特征提取与匹配,是目前精度与速度都不错的跟踪算法。 SORT论文速读:提出了基于卡尔曼滤波和匈牙利算法的目标跟踪策略,同时发现好的目标检测器,可以大幅度提升MOT精度,高达18.9个百分点。SORT实现分为4个步骤,分别对应3.1-3.4,目标检测模型得到目标框;采用卡尔曼滤波进行轨迹框的预测;采用匈牙利算法对目标框与轨迹框进行匹配;最后基于匹配结果,删除旧轨迹框,添加新轨迹框。(论文只有5页,核心内容第三章仅半页纸,但不妨碍它是优秀的工作) DeepSORT论文速读:基于SORT,DeepSORT最大特点是引入了deep association metric,即采用CNN提取目标框中图像特征,来进行匹配。同时,涉及了级联匹配策略,有了更好的准入、准出机制,对目标的跟踪更精细、合理。 目标跟踪的过程相当复杂,为了能了解全过程,这里通过具体案例,一步一步发现问题,然后学习DeepSORT的解决方案,最后汇总。 为了将复杂的问题描述清楚,有必要对名词进行一些解释。 检测框(dets):由目标检测模型输出的框,包含框的位置信息,物体类别信息,是该物体的概率信息 跟踪框(tracks):跟踪模块认为是有价值的检测框。跟踪框中有两种,一个是正式框,一个是预备框。论文中称为confirmed, unconfirmed, 这里借鉴正式党员、预备党员的叫法,应该好理解一些。 预备框(unconfirmed):潜在的跟踪框,只在算法内部记录,当达到一定条件,转为正式跟踪框,才能被算法输出,在屏幕上绘制出来。 正式框(confirmed):目标跟踪算法的输出,回顾定义,目标跟踪需要在视频序列中识别目标并赋予唯一标识,即输出框应当包含检测框信息、唯一标识。 假设世界上没有目标跟踪算法,需要我们自己构思,需求是在在连续帧中将检测到的物体关联起来,实现目标跟踪。 现在有个行人跟踪任务,如图所示 第一帧:检测器只有一个检测框,因此赋予它唯一标识,再采用卡尔曼滤波算法进行跟踪框坐标的输出。 第二帧:检测器输出两个框,如何将2个检测框与1个跟踪框进行匹配,获得行人1在第二帧当中的跟踪框。这时可以借助匈牙利算法,它是求解任务分配问题的组合优化算法。 匈牙利算法可以很好的将检测框1与前序跟踪框1匹配上,然后对前序跟踪框1进行更新(采用卡尔曼滤波),获得当前跟踪框1。 对于检测框2,没有找到与其匹配的前序跟踪框,所以认为它是新进入的,给它创建一个新跟踪框即可。因此,当前跟踪框应有两个。 第三帧:又来了一个人,检测到了3个框,因此重复第二帧的任务,采用检测框更新采用卡尔曼滤波)跟踪框的信息,同时为王五注册新的身份ID——行人3。 第四帧:张三离开了图像,检测器只检测到2个框,2个检测框去匹配3个跟踪框,自然会有一个跟踪框匹配不上,这里显然是行人1,因此没有匹配上的跟踪框需要被删除,最终输出两个跟踪框。 以此类推,新来检测框匹配已有跟踪框,匹配不上,则增加跟踪框,同理,已有跟踪框没有匹配到新的检测框,认为它离开了,需要删除跟踪框。 到这里,一个基础的目标跟踪框架出来了,有了新增跟踪框机制、删除跟踪框机制。这就是大名鼎鼎的SORT算法的流程,对于匹配细节和跟踪框的坐标更新 SORT很好的解决检测框如何与跟踪框对上号,同时有了新增、删除跟踪框机制,但是对于常见的问题没有得到很好的解决,例如: 检测器漏检:检测器在某一帧漏检是很常见的现象,假设第二帧中,张三漏检了,第二帧会将张三的身份ID——行人1给删除。第三帧中的张三将会被认为是新来的,无法匹配到他是行人1。 检测器误检:检测器在某一帧错误的将背景检测为了行人,根据SORT算法,会被背景赋予一个跟踪框,这是很不合理的。 为了让目标跟踪算法输出的跟踪框更稳定,DeepSORT引入了预备框、正式框机制,可以很好的解决漏检、误检带来的不稳定。 对于新增,要考察一下框是否是真的,通常用3帧的时间来考察,当发现框连续3帧都存在,那么认为它是一个好的框,算法批准框称为正式框。这样可以很好的过滤掉一些”没有耐心“的框。这样对于某一帧,某两帧的误检,是很好的过滤方法。 对于删除,要考察一下框是否真的离开,毕竟框也是经过了准入审查的,通常不会一瞬间就离开,此时给它连续30次机会,连续30帧里边发现它都不在了,将它永久开除。 综合上述理解,DeepSORT流程解释如下: DeepSORT核心——匹配过程 匹配过程指的是,如何将检测框与跟踪框匹配上,让每一个检测框都能找到与之对应的跟踪框。若没有找到,则认为是新进入的物体,会创建新跟踪框。 deepsort的匹配过程分两部分。 首先,基于外观特征和马氏距离,为正式框进行匹配,方法是级联匹配(matching cascade),用到的优化方法是匈牙利算法。 然后,基于bbox坐标和IoU,为预备框进行匹配,采用剩余检测框与剩余跟踪框(未匹配和预备框)匹配,用到的优化方法是匈牙利算法。 外观特征与bbox坐标对应:表示的是对于一个物体,要用什么特征表示TA,是1128的向量?还是14的向量? 马氏距离与IoU对应:表示两个特征之间\"相近\"程度的衡量,只有衡量了两个特征之间的距离,后续才能用优化算法优化距离最短的匹配方案 匈牙利算法作用:将N个A与M个B,采用特征向量描述以及距离度量方法,可以得到N*M的距离代价矩阵,即A中每一个元素与B中每一个元素之间的距离。随后用匈牙利算法找到最优匹配。 级联匹配 级联匹配的思想是分70级进行匹配,级的概念指距离当前帧的远近,第一级(level)采用所有检测框, 和仅被记录了一次的正式框(if tracks[k].time_since_update == 1 + level),以此循环70次。 因为越新的目标,越有可能与检测框匹配上,存在太久的目标可能离开了。级联匹配可以解决一部分身份交换问题。 级联匹配中,传入了: distance_metric:基于外观特征(CNN提取出来的512维特征向量)的举例度量函数 max_distance:当距离大于max_distance时,认为是不匹配的 tracks:跟踪框 detections:检测框 track_indices_l:本轮需要匹配的跟踪框的index unmatched_detections:本轮需要匹配的检测框的index # code/chapter-8/tracking/deep_sort/deep_sort/sort/linear_assignment.py 的matching_cascade函数 for level in range(cascade_depth): if len(unmatched_detections) == 0: # No detections left break track_indices_l = [ k for k in track_indices if tracks[k].time_since_update == 1 + level # 为每个跟踪框记录它被更新的次数,优先选择新跟踪框进行匹配, 1+0 ] if len(track_indices_l) == 0: # Nothing to match at this level continue # ============================ 核心部分:匹配 ================================ matches_l, _, unmatched_detections = \\ min_cost_matching( distance_metric, max_distance, tracks, detections, track_indices_l, unmatched_detections) matches += matches_l 级联匹配中采用的是跟踪框的历史特征列表与检测框进行匹配,如跟踪框已经检测到了18次,会得到18个特征向量,新的检测框有30个,则会得到18*30的矩阵。 然后在第0维选择最小值,得到1*30的距离矩阵,最终判断是否有匹配上的检测框。 Tracker --> _match() --> gated_metric() 下的: cost_matrix = self.metric.distance(features, targets) 跳转到:deep_sort/sort/nn_matching.py 的 NearestNeighborDistanceMetric.distance() cost_matrix = np.zeros((len(targets), len(features))) for i, target in enumerate(targets): cost_matrix[i, :] = self._metric(self.samples[target], features) 跳转到: def _nn_cosine_distance(): distances = _cosine_distance(x, y) # 18*30的矩阵 return distances.min(axis=0) # 选择距离最小的特征; 如18*1,选择18个跟踪框中与第一个检测框距离最近的;以此类推得到1*30. # 由此可见,检测框与目标的所有历史特征向量进行距离计算,挑选最近那个特征的距离作为评判距离。 级联匹配之后,会有未匹配的检测框,未匹配的正式框(如果被记录70次以上,是无法进行匹配的),以及预备框。 接下来用IoU测量检测框与跟踪框之间的相似性,很好理解,IoU越大,它俩越有可能是一个物体。 IoU匹配 IoU匹配的代码位于:code/chapter-8/tracking/deep_sort/deep_sort/sort/tracker.py 的_match()函数, 同理采用的min_cost_matching进行匹配,传入的有iou_cost度量函数,max_iou_distance用于过滤,跟踪框,检测框,需要匹配的跟踪框的index,需要匹配的检测框的index。 # Associate remaining tracks together with unconfirmed tracks using IOU. iou_track_candidates = unconfirmed_tracks + [ k for k in unmatched_tracks_a if self.tracks[k].time_since_update == 1] unmatched_tracks_a = [ k for k in unmatched_tracks_a if self.tracks[k].time_since_update != 1] matches_b, unmatched_tracks_b, unmatched_detections = \\ linear_assignment.min_cost_matching( iou_matching.iou_cost, self.max_iou_distance, self.tracks, detections, iou_track_candidates, unmatched_detections) 匈牙利算法 无论是级联匹配还是IoU匹配,最后都会用到min_cost_matching函数,其中匹配的核心代码是: # code/chapter-8/tracking/deep_sort/deep_sort/sort/linear_assignment.py min_cost_matching() row_indices, col_indices = linear_assignment(cost_matrix) # 匈牙利算法求解,得到配对的(raw, col) 这里使用了scipy库的linear_sum_assignment实现,可返回最优匹配的坐标,到底匈牙利算法是如何解决分配问题,下面进行介绍。 匈牙利算法是1955年美国数学家哈罗德·库恩((W.W.Kuhn)),基于匈牙利数学家康尼格(D.Kőnig)提出的康尼格定理,提出求解二分图最大匹配的一种方法。 二分图( Bipartite graph,二部图)是图论中一种模型,指的是有A,B两个节点集合,存在一系列边,边的两端不能再同一个集合,简单说就是A只能和B相连,反之亦然。 为了求解分配问题,需要对二分图中每种可能进行代价描述,称之为代价矩阵(系数矩阵、变换矩阵等等)。 下面借鉴视频中的内容,简要介绍匈牙利解决二分图最大匹配问题。 假设有一本说明书,需要翻译成4种语言,现在有4个人,他们对每个语言的熟悉程度不同,因此如何分配任务,就是一个典型的二分图最大匹配问题。 首先,可以根据任务进行量化,得到目标函数min Z。 然后,设置约束条件,一个人选一个语言,一个语言只能被一个人选择。 最后,得到右下角的方程式。 匈牙利算法实现步骤是: 画圈,划0:对代价矩阵每行减去最小值,使得其出现0;然后对列进行同样操作,使得其出现0; 试指派寻找最优解:0表示最优解,一行只有一个0的话,肯定优先考虑分配。 因此按行找仅有一个0的行,并且分配,分配之后,行已经被分配,因此对应的行需要删除。 同理对列操作。 若还存在没有标记的0元素,且找不到独立0元素的行(列),从剩余0元素最少的行(列)开始,比较这行0元素所在列中0元素的数目,选择0元素最少的那列的这个0元素画圈,同时划去该行该列其余0元素。(如绕口令一般,这里推荐看视频) 打勾,画圈:没有画圈的行打√,打勾行含划0元素的列打√,打√列含画圈0元素的行打√,未打√的行画横线,打√的列画竖线。 增加0元素:寻找未被直线覆盖部分的最小元素,打 √的行减最小元素,打 √ 的列加最小元素。 重复执行2-4,直到找到n个位于不同行不同列的0元素。 其核心思想是用增广路求最大匹配,这里的行列操作实际是将增广路的思想转换为矩阵的表达,因此单纯观察匈牙利算法的矩阵解法,是很难理解其原因,建议通过图论、运筹学的基础知识去了解匈牙利算法求解过程。 更多目标跟踪中的匈牙利算法讲解推荐: 匈牙利算法&KM算法 目标跟踪初探(DeepSORT) 代码细节: 对于代价矩阵,行是tracks, 列是dets,匹配上的框才会有index返回。 DeepSORT核心——更新输出过程 卡尔曼滤波 由于目标检测算法的不稳定,直接用目标检测输出的检测框来表示目标位置的精度不佳,常常会看到框的抖动。 为了让框更稳定的描述物体的位置,deepsort中采用了卡尔曼滤波算法(Kalman filtering)来对目标位置进行输出。 卡尔曼滤波算法是斯坦利·施密特(Stanley Schmidt)在1958年提出的,当时要解决的是阿波罗飞船的导航问题,可以用于估计飞船的位置,是一个很好的运动估计。 随后,卡尔曼滤波广泛应用在天文,宇航,气象等领域。 卡尔曼滤波可以解决的核心问题是,在一个线性动态系统中,可以基于历史信息与当前输入信息,很好的估计当前最优信息,当前最优信息就是卡尔曼滤波的输出,它可以很好的过滤掉噪声(必须是高斯噪声)。 这里的历史信息,可以理解为跟踪框(tracks)(上一帧),当前输入信息是目标检测算法输出的检测框(dets),而当前时刻deepsort要输出的目标的位置,是dets+tracks经过卡尔曼滤波算法的输出,即一个当前最优信息,是一个预测的、估计的值。 为了对卡尔曼滤波有进一步认识,这里简要介绍卡尔曼滤波思想和概念。对于细节,推荐阅读图说卡尔曼滤波,, 从放弃到精通!卡尔曼滤波从理论到实践~ 这里借用视频中的公式进行讲解过程,公式中更细节内容,可以参考卡尔曼滤波的五大公式 x:观测对象,例如卫星的坐标,图像中目标的坐标,水壶中的温度等。 x:观测对象,例如卫星的坐标,图像中目标的坐标,水壶中的温度等。 t:表示时刻 -:表示估计值 ^:表示估计,由于x都是带着^的,这里可以不加以区分 F:状态转移矩阵 P:协方差矩阵 K:卡尔曼增益,用于权衡,历史信息与当前输入信息的重要程度。 对于目标跟踪算法的输出,是公式4,公式4也是最核心的内容,其余公式都在为公式4服务的。 为了理解公式4,借鉴文章如何通俗直白地理解卡尔曼滤波算法的讲解。 假设,有两台电子秤,分别进行测量一瓶水,得到的结果如图所示。 由图可知,电子秤不是绝对精准的,存在一定误差,不过当前观测值分别是160和170,那么如何融合两个数据? 最简单的是 M = (A+B)/2 = 165。 求平均的设想里,有一个重要前提是,认为A和B的贡献是一样的,重要程度是一样的,因此各占50%的权重。 如果,A的精度更高,B精度差一些,即A的方差小一些,B方差大。这时,平均就不合适了,应该让精度高的观测值的权重更高。 权重的计算,需要考虑谁更重要,即方差更小,所以可以通过方差的比较获得权重分配。 A 测量结果 为 160 +- 3, B 测量结果 为 170 +- 9,可知A 的测量结果精度是 B 测量结果精度的 3倍。 这个公式是理解上述公式4的关键,通过将A提取出来,变为单独一项,就可以很好的衡量,基于A,要如何变更,得到最优估计值。 这里的变更加号的右边,B-A乘以一个权重,这个权重就是卡尔曼滤波中的卡尔曼增益K。其中B就是目标检测算法输出的dets,A是tracks。 而卡尔曼增益K的计算,需要依靠协方差矩阵P。 DeepSORT 小结 到此,总结一下卡尔曼滤波过程,当前帧跟踪框的信息由卡尔曼滤波器在更新阶段输出。 更新阶段需要使用到:当前帧检测框, 基于上一帧跟踪框的预测值,并且加权得到。 其中,上一帧跟踪框的预测值来自公式1。代码中是: self.tracker.predict()。 有了基于上一帧跟踪框的预测值,再输入dets,就可以得到当前帧跟踪框信息,代码中是: self.tracker.update(detections)。 在代码中,卡尔曼滤波器维护mean和covariance,分别表示公式中的预测值x,协方差矩阵P。 self.mean, self.covariance = kf.predict(self.mean, self.covariance) # mean即bbox的坐标数据 self.mean, self.covariance = kf.update(self.mean, self.covariance, detection.to_xyah()) 到这里,deepsort原理有了大概的描述,更多细节仍需要到代码中观察,这里做一个简要回顾。 跟踪框的输出: 为了更稳定,采用了卡尔曼滤波算法,将当前帧检测框信息,结合卡尔曼滤波对当前帧的预测,两者共同作用,得到输出。 目标的匹配: 为了让检测框找到对应的,合适的跟踪框,把它转化为二分图最大匹配问题,可以用匈牙利算法很好的求解。 同时,为了匹配更精准,减少身份交换,deepsort先进行新目标框的匹配(仅限前70级,级表示被跟踪的次数),然后再进行基于IoU的匹配。 跟踪框准入准出: 为了避免漏检、误检等短暂的不稳定因素,设计了预备框和正式框的概念。经过3次考验,可转正,经过30次机会仍不靠谱(未检测到),开除X籍。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.4-tracking-2.html":{"url":"chapter-8/8.4-tracking-2.html","title":"8.4 目标跟踪(下)——虎门大桥车流量统计","keywords":"","body":"8.4 目标跟踪(下)——虎门大桥车流量统计 上一小节,对deepsort的实现步骤进行了详细分析,本小节将使用deepsort代码以及yolov5来实现车流量统计。 本节,首先对deepsort源代码的设计进行简要的结构剖析,来学习代码设计,然后将其结合yolov5,实现车流量统计。 注意,代码所需的模型文件,通过网盘下载:链接:https://pan.baidu.com/s/1Ys_v1Tqta4wJMHC8NKeTTg 提取码:ucf4 注意,ckpt.t7为deepsort的模型文件,一定要放到:F:\\pytorch-tutorial-2nd\\code\\chapter-8\\tracking\\deep_sort\\deep_sort\\deep\\checkpoint下面 deepsort源码——结构分析 deepsort的代码不算复杂,设计了几个核心类,然后为各个核心功能编写了实现函数。这里绘制简要的UML图,对代码设计的思路进行学习。 对于一个业务场景,首先识别实体,并分析实体的属性和功能,下面自下而上进行分析目标跟踪当中存在的实体。 Track目标物体:目标跟踪的核心元素是目标物体,这里称为Track类,对于一个目标,需要有坐标信息,id, 特征向量列表,状态等信息。 Tracker目标跟踪器:管理所有目标,并可实现目标的更新,因此需要卡尔曼滤波器,管理目标集合tracks等信息。 KalmanFilter卡尔曼滤波器:维护卡尔曼增益矩阵,并实现预测、更新两大功能。 DeepSort类:进行统一封装,对外提供update函数,返回跟踪框。 对于级联匹配,会在Tracker类中_match()实现,其中设计了一系列模块,包括 matching_cascade:级联匹配实现,循环70次进行匹配。 min_cost_matching:实现一次最小代价匹配。 linear_assignment:匈牙利算法实现。 NearestNeighborDistanceMetric:级联匹配中距离度量功能实现,其中维护了各目标的所有特征向量,每一帧的特征向量都会被保存。最大保存100个特征向量。 if self.budget is not None: self.samples[target] = self.samples[target][-self.budget:] # 相当巧妙的实现最多记录最新的100个特征向量 到这里,deepsort大体框架以及搭建完毕,使用方法非常简单,只需要实例化DeepSort类,调用.update()即可获得跟踪框信息。 deepsort+yolov5——车流量统计 目标跟踪可以获得目标的位置及唯一标识,它只是方法,并不是目的。 基于目标跟踪方法,可以实现许多有价值的事情,例如卡口人流量计数,交通道路车流量计数与统计,警戒区域预警等。 进行行人计数或车流量计数时,需要判断目标是否经过特定区域,这时需要撞线机制来实现,撞线机制可以有两种方法实现。 一种是基于区域判断,另外一种是基于线段相交判断。 另外一种是基于线段相交判断,则是基于物体的历史轨迹曲线,判断是否与界线相交。 计数中的撞线机制 本案例采用基于区域的撞线机制,对边界设置两个区域,一般称inner和outter区域,当物体先到达inner,再进入outter,则可判断物体是离开,反之亦然。 对于inner和outter区域,每个区域需要记录曾经到达区域里的目标id,仅当两个区域同时存在过目标id,可以计数,并且删除目标id。 例如图中的ID-60,进入了inner区域(蓝色),inner区域需要观察ID-60是否存在outer区域中(黄色),当前是不存在的,因此添加到inner区域的历史列表中。 下一帧,ID-60到达黄色区域,黄色区域同样地,先判断ID-60是否来自蓝色区域,它在蓝色区域的历史记录中找到了ID-60,因此可以判断ID-60是从inner到达outer,所以outer进行加一。 反之inter加一。 在代码实现上有一些抽象,这里做简单的讲解。 如何判断物体到达inner和outter区域? 采用mask矩阵,inner区域像素是1, outer像素是2,mask矩阵大小与图片大小一致,采用目标的位置坐标对mask矩阵索引,通过索引值==1? 还是==2?来判断当前物体位于什么区域。 如何判断物体先、后顺序? 区域中发现新物体时,首先判断是否存在对向区域,若不存在,才可以加入区域的物体容器中进行管理。若存在,即可删除,并且计数。 为了实现撞线机制,这里设计了三个类,分别是BoundaryType、CountBoundary和BaseCounter 处理逻辑在BaseCounter的counting(),边界区域抽象成CountBoundary,实现了必要的函数来完成计数。 下面简单介绍counting函数中,如何判断物体是从outer来,到达inter,实现inter计数+1的(反之亦然) 第1行:通过物体的x,y坐标,对索引矩阵进行索引,得到索引值。例如:[1, 2, 0, 0, 0, ...]。通过索引值可知在何区域 第4行:通过索引值列表,判断当前在inner区域的目标,并且返回它们的id 第5行:获取,当前到过outer的目标id 第8行:判断是否有交集,有交集表明,该id从outer来,已经抵达inner。可以计数。 第9行:判断是否存在差集,inner有,outer没有,表明物体可以加入inner的id_container中进行管理 第10行:由于目标完成了计数,outer_boundary中需要删除它。 第11行:由于目标第一次到来,所以注册到inner_boundary中,后续供outer_boundary查询。 bbox_area_list = self.area_mask[index_xy] # 获取bbox在图像中区域的索引,1,2分别表示在边界区域. [int,] # ======================== 先处理inner区域 ==================================== inner_tracks_currently_ids = self.get_currently_ids_by_area(tracks, bbox_area_list, BoundaryType.inner) outer_tracks_history_ids = list(self.outer_boundary.id_container.keys()) # 获取历史帧经过outer区域的目标的id # 当前与历史的交集,认为是目标从outer已经到达inner,可以计数,并且删除。 outer_2_inner_tracks_id = self.intersection(inner_tracks_currently_ids, outer_tracks_history_ids) only_at_inner_tracks_id = self.difference(inner_tracks_currently_ids, outer_tracks_history_ids) self.outer_boundary.remove_tracks(outer_2_inner_tracks_id) # 删除outer中已计数的id self.inner_boundary.register_tracks(only_at_inner_tracks_id) # 注册仅inner有的id 注意事项: 在第1行中 self.area_mask的制作中,由于采用的是像素1和2,在resize时,导致2的边界有一系列1的存在,导致了误检! 按设计,1在2的下面,这里1反而出现在了2的上面,导致实际是“出”的,计算为了“入” 把上述代码组装起来得到01-main.py,做好模型文件、视频文件、边界点的配置,运行即可得到以下结果。 模型权重文件,视频文件可通过网盘下载: 注意,ckpt.t7为deepsort的模型文件,一定要放到:F:\\pytorch-tutorial-2nd\\code\\chapter-8\\tracking\\deep_sort\\deep_sort\\deep\\checkpoint下面 完整视频可见B站 这里为了实现边界区域点集的获取,编写了鼠标点选边界区域的代码00-draw-border.py。 运行后,鼠标双击实现选点,选点顺序必须从左上角开始,顺时针,选择完毕,terminal中打印的点集list,复制下来使用即可。 小结 目标跟踪案例中,内容比较多,这里总结一些关键知识点: SORT与DeepSORT算法步骤:本案介绍目标跟踪中出现的问题,一步步引出DeepSORT设计的复杂逻辑,由问题出发,以解决问题的方式,观察DeepSORT的步骤。 DeepSORT中的核心算法:卡尔曼滤波器与匈牙利算法,卡尔曼滤波常用于带有高斯噪声的线性运动系统,可以很好的预测运动状态。匈牙利算法可以解决二分图匹配问题,今后也可以借鉴两个算法解决实际业务问题。 DeepSORT代码结构剖析:通过UML图,分析DeepSORT代码是如何抽象、设计的,巩固面向对象编程的思想。 计数的撞线机制:介绍基于区域的撞线机制,并通过面向对象编程来实现计数器。 DeepSORT+YOLOv5的联合使用:将目标检测+目标跟踪+计数机制联合使用,构建实际应用,在主代码中可以发现,各功能模块抽象独立出去,主代码的核心代码仅两行:bboxes = detector.detect(im); counter.counting(list_bboxs); 目标跟踪仍是一个较大研究方向,DeepSORT仅是其中一种方法,要深入掌握目标跟踪还需学习其他方法。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.5-cycleGAN.html":{"url":"chapter-8/8.5-cycleGAN.html","title":"8.5 生成对抗网络——CycleGAN","keywords":"","body":"8.5 生成对抗网络——CycleGAN 简介 本小节将介绍GAN模型中有趣的模型CycleGAN。 CycleGAN是一种双向循环的GAN模型,可实现X域与Y域之间的相互转换,并且是基于unpaired data(即不需要标注,只需要收集图片)。相较于此前的pix2pix,cyclegan适用性更广,毕竟unpaired data比paired data更容易获取。 例如论文中展示的,照片与莫奈风格画之间的互相转换,斑马与马之间的转换,夏天与冬天之间的转换。 本节先介绍GAN与CycleGAN的结构,再通过代码详细介绍CycleGAN的训练、推理。 GAN简介 GAN(Generative Adversarial Nets,生成对抗网络)由 Ian J Goodfellow在2014发表于《Generative Adversarial Nets》,可谓是推开了生成模型的一扇大门。 GAN是一种从随机噪声生成特定分布数据的模型,例如生成人脸数据,手写体数据,自定义数据集等。 GAN当中有Generator与Discriminator两个模型,G负责学习从噪声到数据的映射,D负责充当损失函数,判断G生成得是否足够好,G和D交替训练,形成对抗,同步提升,最终使得G生成的数据越来越像人脸。 根据模型的结构,GAN模型延伸出一系列变体,如本文要介绍的CycleGAN,还有DCGAN,Conditional GANs,Pix2Pix,SRGAN等。 GAN的设计十分巧妙,从神经网络训练的角度考虑,GAN是将损失函数替换为,神经网络的输出,具体如下图所示: 传统模型训练,需要用loss_fun(output, label)得到loss值,然后求梯度优化。 在GAN中,巧妙了利用一个判别器模型,D_net, D_net(output) 趋向0, D_net(training_data)趋向1,依次获得loss值。 CycleGAN简介 CycleGAN是一种无监督学习方法,由Jun-Yan Zhu等人于2017年提出。 它的主要思想是通过两个生成器和两个判别器来实现两个不同域之间的图像转换。 与其他的GAN模型不同的是,CycleGAN不需要成对的图像进行训练,而是只需要两个域中的任意数量的图像即可。 下面介绍CycleGAN的模型结构与损失函数。 CycleGAN模型结构 CycleGAN模型由两个生成器,两个判别器构成。 生成器G,将X域图像变换到Y域 生成器F,将Y域图像变换到X域 判别器Dx,判别图像来自X则为1, 图像来自Y则为0 判别器Dy,判别图像来自X则为0, 图像来自Y则为1 生成器采用3层卷积+一系列残差块构成; 判别器采用PatchGANs,其特点在于对一张图片不是输出一个向量,而是输出NxNxC的张量,NxN分别对应原图中的70x70区域,即对一张图,划分多个70x70的patch,每个patch来判别它是0类,还是1类。 CycleGAN损失 cyclegan最大的特点在于损失函数的设计,除了GAN的常规两个损失函数之外,论文中增加了cycle consistency loss(循环一致性损失),用来避免模式坍塌,以及更好的让GAN模型生成合理的图像,同时,在官方代码中还增加了一项identity loss,用来增加GAN模型对于本域图像信息的学习。 因此,整体loss有8项,分别是'D_A', 'G_A', 'cycle_A', 'idt_A', 'D_B', 'G_B', 'cycle_B', 'idt_B' 生成器的损失 loss1 :判别器的输出接近1 对于G,目标是让对应的判别器D,认为假图像是真图像,即输入是假图像,标签是1,目标是欺骗D,G就是训练好了。 self.loss_G_A = self.criterionGAN(self.netD_A(self.fake_B), True) target_tensor = self.get_target_tensor(prediction, target_is_real) # 根据self.fake_B,生成对应的标签,即N*N的标签,为patchGAN的输出匹配 loss = self.loss(prediction, target_tensor) # MSELoss() 而非BCE loss2:F(G(x)) 与 x一致 除了常规Loss,还有cycle consistency loss(循环一致性损失),目的是经过G得到的图片,返回去再经过F,应当是可以恢复得到X域的图像x_hut,并且x与x_hat应当是逐像素一模一样的。 这样的G和F才是合理的。 self.criterionCycle = torch.nn.L1Loss() self.loss_cycle_A = self.criterionCycle(self.rec_A, self.real_A) * lambda_A # lambda为缩放系数 loss3:恒等映射损失 该损失在代码中才出现,论文中并没有提到。恒等映射损失的思想是,生成器G_A接收A域图像,生成B域图像;若接收B域图像,应该生成恒等的B域图像,即B域图像一模一样,不能变。 G_A should be identity if real_B is fed: ||G_A(B) - B|| self.idt_A = self.netG_A(self.real_B) self.loss_idt_A = self.criterionIdt(self.idt_A, self.real_B) * lambda_B * lambda_idt # self.criterionIdt = torch.nn.L1Loss() 因此,对于生成器G而言,要求它: 生成的假图像,要让判别器预测为1, D(G(x)) 逼近1; G生成的图像,再经过F生成的图像,应当等于原图,此为循环一致性损失 已经是B域的图像,经过G_A,应当得到B域的原图。 判别器的loss 判别器损失较为简单,对于真图像,需要预测为1,对于假图像,需要预测为0。 其中,假图像不是基于当前batch的,而是记录过往的一批假图像,从假图像池中抽取。 fake_B = self.fake_B_pool.query(self.fake_B) self.loss_D_A = self.backward_D_basic(self.netD_A, self.real_B, fake_B) def backward_D_basic(self, netD, real, fake): # Real pred_real = netD(real) loss_D_real = self.criterionGAN(pred_real, True) # Fake pred_fake = netD(fake.detach()) loss_D_fake = self.criterionGAN(pred_fake, False) # Combined loss and calculate gradients loss_D = (loss_D_real + loss_D_fake) * 0.5 loss_D.backward() return loss_D 训练注意事项 论文中超参数:batch size =1;epoch =200;lr:前100epoch,固定0.0002,后100epoch,线性下降至0 其它注意事项: 两个域图像是否有一致性: 举个例子: 苹果 橘子: 都是球形, OK! 苹果 香蕉: Mode Collapse! 训练CycleGAN要有耐心 学习率别太高 对抗损失权重不要太高,循环一致性损失权重为1的时候,对抗损失一般设置为0.1 判别器优化频率高于生成器 使用最小二乘损失(MSE) cycleGAN的loss不能准确反应训练的好坏,不代表着训练进度,甚至不能代表结果优劣。所以还是要输出样张看效果,或许可以借鉴WGAN的思想 由于 minimax 优化的性质,许多 GAN 损失不会收敛(例外:WGAN、WGAN-GP 等)。对于 DCGAN 和 LSGAN 目标,G 和 D 损失上下波动是很正常的。只要不爆炸应该没问题。 CycleGAN代码实现 接下来,通过pytorch训练一个可以将图片转换为莫奈风格图像的CycleGAN,github已经19.5K star了,可见深受大家喜爱。 数据集准备 由于不需要标签,仅需要准备图像,所以在根目录下,存放trainA, trainB, testA, testB即可,分别存放A域的图像,B域的图像。 这里下载官方提供的monet2photo数据集,可以通过sh脚本下载,也可以手动下载(推荐) # 方法一:bash bash ./datasets/download_cyclegan_dataset.sh monet2photo # 方法二:手动下载 # apple2orange, summer2winter_yosemite, horse2zebra, monet2photo, cezanne2photo, ukiyoe2photo, vangogh2photo http://efrosgans.eecs.berkeley.edu/cyclegan/datasets/$FILE.zip # 例如莫奈数据下载 http://efrosgans.eecs.berkeley.edu/cyclegan/datasets/monet2photo.zip 数据加载 整个数据模块代码设计如下图所示: 该项目适配pix2pix, cyclegan,因此提供了多种dataset,所有的dataset都继承于BaseDataset,针对cyclegan的是unaligned_dataset.py中的UnalignedDataset。 对于dataloader,提供了一个类 CustomDatasetDataLoader,并且实现了迭代协议iter,因此\"dataloader\"是自定义的一个可迭代对象。 在主代码01_train.py中,通过33行代码:dataloader = create_dataset(opt) ,实现dataloader的创建,所有的配置信息存放在opt中。 接下来关注UnalignedDataset,它内部实现了transform,transform由opt的参数决定 第一步:缩放变换,有resize,或者基于width缩放的方式;默认基于resize_and_crop。 resize的尺寸是opt.load_size 第二步:crop的尺寸是 opt.crop_size 第三步:Normalize 这份代码中有一个值得借鉴的是,通过参数配置,来选择调用具体的类。实现方法是通过,importlib.import_module实现通过字符串形式import工具库。 def find_dataset_using_name(dataset_name): \"\"\"Import the module \"data/[dataset_name]_dataset.py\". In the file, the class called DatasetNameDataset() will be instantiated. It has to be a subclass of BaseDataset, and it is case-insensitive. \"\"\" dataset_filename = \"data.\" + dataset_name + \"_dataset\" datasetlib = importlib.import_module(dataset_filename) # 这里的dataetlib,等同于一个库,例如。import cv2的cv2, import torch的torch dataset = None target_dataset_name = dataset_name.replace('_', '') + 'dataset' for name, cls in datasetlib.__dict__.items(): if name.lower() == target_dataset_name.lower() \\ and issubclass(cls, BaseDataset): dataset = cls if dataset is None: raise NotImplementedError(\"In %s.py, there should be a subclass of BaseDataset with class name that matches %s in lowercase.\" % (dataset_filename, target_dataset_name)) return dataset 模型构建 源代码中将模型(nn.Module), 损失函数,优化器一并放到了CycleGANModel类当中,对外提供set_input()与optimize_parameters(),实现前向传播、损失计算、反向传播,这样可以让主代码更简洁。 模型部分代码设计如下图所示 对于 netG_A/B,cyclegan中是resnetblock构成的生成器,详细可见models/networks.py的ResnetGenerator类, 主要由resnetblock的下采样和TransConv的上采样构成,最后加入tanh()激活函数。 对于netD_A/B, 是一个patchGAN,全部由卷积层构成的全卷积网络,详见 models/networks.py的NLayerDiscriminator。 整个模型构建与优化核心代码如下: model = create_model(opt) # create a model given opt.model and other options model.setup(opt) # regular setup: load and print networks; create schedulers ---------------------------------- model.set_input(data) # unpack data from dataset and apply preprocessing model.optimize_parameters() # calculate loss functions, get gradients, update network weights 模型训练 数据、模型、损失函数与优化器准备完毕,可以进行迭代训练。 如果在windows下训练,需要开启 visdom python -m visdom.server 由于此处使用了visdom进行可视化,需要先行开启visdom,否则会报错: requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8097): Max retries exceeded with url: /env/main (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接。')) [WinError 10061] 由于目标计算机积极拒绝,无法连接。 训练指令: python 01_train.py --n_epochs 200 --dataroot path/to/your/datasets/monet2photo --name monet2photo_cyclegan --model cycle_gan 日志信息、模型信息将存储于checkpoints\\monet2photo_cyclegan中 训练结果 cycleGAN的loss不能准确反应训练的好坏,不代表着训练进度,甚至不能代表结果优劣,整体趋势是,cycle loss逐渐下降,如下图所示 推理测试 预训练模型可以从这里下载:链接:https://pan.baidu.com/s/1bEPNBbAeqMumpM2pqKwb4w 提取码:q159 python 02_inference.py --dataroot G:\\deep_learning_data\\cyclegan\\monet2photo\\testB --name monet2photo_cyclegan --model test --no_dropout --model_suffix _B180 model_suffix 格式说明:模型模型文件名保存为latest_net_G_A.pth、latest_net_G_B.pth。在代码中会自动拼接: \"latest_net_G{}.pth\".format(model_suffix) 最后在F:\\pytorch-tutorial-2nd\\code\\chapter-8\\cyclegan\\results\\monet2photo_cyclegan\\test_latest下就有对应的结果图片 这里展示了120, 180, 200个epoch时的展示效果,感觉180时的效果最好。 小结 本小结先介绍了GAN与cycleGAN的模型结构,GAN是一个巧妙的利用神经网络进行损失计算的设计,CycleGAN是巧妙的利用了两个GAN相互转换,并提出循环一致性loss,最终CycleGAN的损失共8个,分别是'D_A', 'G_A', 'cycle_A', 'idt_A', 'D_B', 'G_B', 'cycle_B', 'idt_B'。 然后介绍CycleGAN源代码使用及设计,其将Dataset, DataLoader, model, loss, optim进行了高度封装,使主代码很简洁。 从此可见,无论多复杂、难理解的pytorch模型训练代码,都离不开Dataset, DataLoader, nn.Module,loss, optim,只要了解训练的步骤,这些复杂的代码都可以梳理出来。 自2014年GAN提出以来,往后的5年间提出了各式各样的GAN变体,也有了非常多有趣的应用,感兴趣的朋友可以进一步了解。 2020年之前,在图像生成领域,GAN是当之无愧的主流,但2020年《Denoising Diffusion Probabilistic Models》(Diffusion)提出后,基于扩散模型(diffusion model)的图像生成称为了学术界的宠儿,包括OpenAI提出的DALL-E系列,stability.ai提出的Stable-Diffusion。 下一节将介绍扩散模型(diffusion model)及代码实现 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.6-diffusion-model.html":{"url":"chapter-8/8.6-diffusion-model.html","title":"8.6 扩散模型——DDPM","keywords":"","body":"8.6 Diffusion Model——DDPM 前言 2020年,DDPM横空出世,将扩散模型概念迅速扩散到深度学习各个领域,并在2022年随着Stable Diffusion的提出及开源,该技术实现了破圈,走入了大众的视野,而不再是科技工作者才了解的概念。 为此,对扩散模型原理进行简要介绍,并通过代码实现DDPM模型,同时介绍Stable Diffusion 模型背后的原理。 本文主要内容包括 Diffusion Model 概念介绍 DDPM 模型原理及代码实现,训练、推理 Guided Diffusion:引导条件的扩散模型介绍,包括classifier-base 和 classifier-free 两大主流模型 Stable Diffusion:让技术出圈的模型 Latent Diffusion Model(LDM):Stable Diffusion背后的核心技术 Diffusion Model 简介 扩散模型(Diffusion Model)发展至今已成为一个大的概念、思想,扩散是借鉴物理学中的扩散过程(Diffusion Process)概念。 物理学中扩散是一种物质分子在非均匀环境中的运动,物质从高浓度区域向低浓度区域传输,最终实现浓度均衡。 在深度学习中,则是将噪声加入到原始图像中进行扩散,最终使图片变为噪声,然后利用深度学习模型学习从噪声变到图像的过程,最后可以随机生成噪声,并利用模型将噪声生成图片的过程。 深度学习中扩散的概念自2015就有了,并在2019年发表于论文《Generative Modeling by Estimating Gradients of the Data Distribution》, 最终在2020年的《Denoising Diffusion Probabilistic Models》中被大众熟知,随后就开启了扩散模型的学术界扩散,如DALL-E 2,imagen, Stable Diffusion等强大应用。 DDPM 实现噪声到图片步骤 此处借鉴李宏毅教授2023年春季期ML课程中课件进行讲解。 DDPM模型推理过程是将一个标准正态分布中采样的噪声图片(与原图同尺寸),经过T步(1000步)的去噪(Denoising),生成高质量图像的过程。 DDPM模型推理过程,可以看似将噪声逐步的去除,先获得图像大体轮廓,逐步精雕细琢,获得清晰的图像。 这就像雕像制作过程,工匠常说:“雕像本身就在石头里,我只是把多余的部分剔除掉”,雕刻雕像的过程就像噪声变到高质量图像的过程,一开始它们都是可以生成“万物”的本源。 如何对Denoise模块进行数学建模,使得噪声逐步变清晰? 可以这么做,设计一个神经网络,它接收噪声图以及当前步数,输出一个噪声,然后与原图相减,获得更清晰的图片。 如何训练这样的神经网络模型?训练数据如何构建? 前面提到噪声如何变图像,现在反过来看看,图片如何变噪声的过程。 对于原图,经过T步的逐渐加高斯噪声,使图像逐步模糊,最终趋近于标准高斯分布。 这其中就可以构建Noise Predicter的训练数据,例如蓝色框中为输入,红色框噪声则是反向过程时期望预测的标签。 对于具体模型,DDPM中采用了Unet架构的神经网络实现数据预测。 到这里,DDPM实现从噪声生成图像的步骤就清晰了: 前向过程:将原图逐步添加噪声, 共1000步 反向过程:利用神经网络学习加噪图像到噪声的变换,使得模型可以去噪 推理使用:随机采样,得到高斯噪声,然后逐步去噪,经过1000步去噪,得到清晰图像。 DDPM 公式理解 根据上述步骤,可以将DDPM训练、推理过程采用数学形式表达,如下图所示 训练过程: q(x0) 表示原始图像数据集(分布),x0表示一张原始图像 t 看成是1到1000的均匀分布采样 ε 表示从标准正态分布中采样得到的噪声图像 εθ 表示模型需要学习到的噪声图像,该图像是利用unet生成的,unet接收上一步去噪图与当前步数t,预测出一个噪声图像,并且期望它与高斯噪声越接近越好。即ε - εθ 趋于0。 αt_bar:均值系数,可由重参数方法训练而来,或是固定值。固定值如0.0001 到0.02线性插值。 推理过程: xT:从正态分布中随机采样的噪声 z:从正态分布中随机采样的噪声 xt-1:主要是由Xt减去模型生成的噪声图像,并且以一定的权重加权后,加上标准差乘以随机噪声。至于原因需要看原文及进行公式推导理解了 更多公式推导,推荐阅读 What are Diffusion Models?) 由浅入深了解Diffusion Model DDPM 模型结构 下面通过代码进行DDPM模型结构的剖析,官方代码为TF版,在这里采用非官方的PyTorch版。 论文采用TPU v3-8(相当于8张V100 GPU),在cifar10上花了10.6小时,由此可见,要想在256x256的图片上训练,会非常耗时。 为了快速使用DDPM,这里采用cifar10进行学习。 通过代码分析,DDPM模型结构如下图所示,是在unet结构上进行了一些改进,包括加入时间步t的embedding,卷积中采用了ResBlock,并且采用了Self-Attention机制。 如图例所示,模型整体有7个组件,彩色箭头是一组操作,包含2-3个网络层的堆叠,通常最后一个网络层才会改变图像分辨率。 第一个,时间步的embedding,它会输入到除了head,tail的其它网络层当中,并且是add的形式添加的(h += self.temb_proj(temb)[:, :, None, None]) 第二个,head模块,是一个3x3卷积,主要是为了改变通道,没有特殊的地方。 第三个,down block,是下采样的核心,一个block由2个ResBlock与一个下采样层构成。ResBlock内部如图所示: 第四个,middle block,由两个ResBlock构成 self.middleblocks = nn.ModuleList([ ResBlock(now_ch, now_ch, tdim, dropout, attn=True), ResBlock(now_ch, now_ch, tdim, dropout, attn=False),]) 第五个,Up block,由3个ResBlock+1个上采样层, 第六个,tail,由GN + swish + conv构成,输出最终图像 self.tail = nn.Sequential( nn.GroupNorm(32, now_ch), Swish(), nn.Conv2d(now_ch, 3, 3, stride=1, padding=1)) 第七个,concat,是unet的low-level特征融合到high-level特征当中 总的来说,特色在于时间步t的embedding是加入了每一个ResBlock中进行使用,并且ResBlock采用了self-attention机制。 DDPM——训练Cifar-10 接下来使用配套代码中的Main.py进行训练,并且使用Main.py进行推理,训练和推理需要调整\"state\": \"eval\"。 数据准备:在Main.py同级目录下创建cifar文件夹,并且将cifar-10-python.tar.gz放到里边。 运行训练:python Main.py,1080ti上训练,约16小时,训练完毕,在Checkpoints文件下有ckpt_last_.pt。 下面观察cifar-10的训练细节,在配套代码中可以看到,数据采用torchvision提供的cifar10 dataset接口,模型为Unet,优化器为AdamW,学习率为warmup+consine。 在主循环中,只用了images,labels是没有使用到的。 模型的迭代,封装在了GaussianDiffusionTrainer类的forward函数,这也是核心代码之一,下面详细看看forward函数。 第5行:进行时间步的采样,即为每一个样本配一个时间步,并不需要为每个样本采样1000个时间步进行训练,这是因为公式推导的时候,xt可以用x0直接表示的,不需要依赖xt-1。 第6行:对需要加入的噪声进行采样,这里为标准正态分布 第11行:根据公式计算得到x_t,x_t由x0与noise加权得到,细节可看公式理解部分,这里的两个权重之和不等于一,但是接近1。 第14行:模型接收x_t与t,去预测噪声图像,并且通过mse_loss进行损失计算。 def forward(self, x_0): \"\"\" Algorithm 1. \"\"\" t = torch.randint(self.T, size=(x_0.shape[0], ), device=x_0.device) # 不需要1000步都训练,随机batchsize个 noise = torch.randn_like(x_0) # 标准正态分布 # 基于x0,获得xt, 随后得到训练数据[(xt, t, noise), ] # x_t.shape [bs, 3, 32, 32] # noise.shape [bs, 3, 32, 32] # t.shape (bs,) x_t = (extract(self.sqrt_alphas_bar, t, x_0.shape) * x_0 + extract(self.sqrt_one_minus_alphas_bar, t, x_0.shape) * noise) loss = F.mse_loss(self.model(x_t, t), noise, reduction='none') return loss 代码与上文的原理介绍是一致的,时间步step2与加噪后的图像是输入数据,标签ground truth是noise。 DDPM——推理Cifar-10 训练完毕后,先看看推理效果。 首先在Main.py中修改 \"state\": \"eval\", # train or eval,然后运行python Main.py,即可在\"sampled_dir\": \"./SampledImgs/ 文件夹下获得如下图片,看上去还像个样子,毕竟数据量、算力、时间摆在这里。 这里提供一个训练好的模型参数,ckptlast.pt,下载后放到Checkpoints文件夹。链接:https://pan.baidu.com/s/17X_L9oH4lmrGwnD-V9D5HQ 提取码:w4ki 推理过程的代码理解相对有一点绕,主要还是参照论文中的sampling过程,如红框所示,首先获得均值(为什么叫均值?可能要去推一下公式了),然后加上时间步对应的标准差乘以随机噪声。 其中,均值主要是由Xt减去模型生成的噪声图像,并且以一定的权重加权后得到。 核心代码涉及3个函数, forward()为主函数。 p_mean_variance()为调用Unet模型,获得mean和var。 predict_xt_prev_mean_from_eps()是进行上图中红色框运算的过程。 第26行,获得模型预测的噪声图片 第27行,获得mean,即上图中的红色框 第15行,加上标准差乘以随机噪声,获得t时刻的输出,反复迭代1000次,得到最终输出图像。 def forward(self, x_T): \"\"\" Algorithm 2. \"\"\" x_t = x_T for time_step in reversed(range(self.T)): print(time_step) t = x_t.new_ones([x_T.shape[0], ], dtype=torch.long) * time_step mean, var = self.p_mean_variance(x_t=x_t, t=t) # mean是 xt图 减去 噪声图 # no noise when t == 0 if time_step > 0: noise = torch.randn_like(x_t) else: noise = 0 x_t = mean + torch.sqrt(var) * noise assert torch.isnan(x_t).int().sum() == 0, \"nan in tensor.\" x_0 = x_t return torch.clip(x_0, -1, 1) def p_mean_variance(self, x_t, t): # below: only log_variance is used in the KL computations # posterior_var: 由 betas计算得到,betas=[0.0001 to 0.02] var = torch.cat([self.posterior_var[1:2], self.betas[1:]]) # betas=[0.0001 to 0.02] var = extract(var, t, x_t.shape) eps = self.model(x_t, t) # eps是unet输出的图像 xt_prev_mean = self.predict_xt_prev_mean_from_eps(x_t, t, eps=eps) # 加权减法,xt图 减去 噪声图 return xt_prev_mean, var def predict_xt_prev_mean_from_eps(self, x_t, t, eps): assert x_t.shape == eps.shape return ( extract(self.coeff1, t, x_t.shape) * x_t - extract(self.coeff2, t, x_t.shape) * eps ) Diffusion Model 拓展 —— Guided Diffusion guided diffusion是加入了引导信息,让生成的图像变为我们想要的形式,而不是随机的图片。 引导式的扩散模型从有无分类器,可以分为两种,classifier-base和classifier-free,classifier-free由于不需要分类器,引导信息直接embedding到模型中,所以应用更为广泛。 classifier-base ——《Diffusion Models Beat GANs on Image Synthesis》 DDPM提出后,其实效果并未惊艳大家,在DDPM发表后的几个月,《Diffusion Models Beat GANs on Image Synthesis》的发表, 其github,把扩散模型带入了高潮,因为它效果比GAN更好,并且针对DDPM,引入了classifier-guidance思想,可以在生成时加入条件约束,可控制生成特定类别的图像。 具体公式详见原文。在使用时,采用unet估计mean时,需要额外加上分类器的分类结果的梯度,详见openai的github:https://github.com/openai/guided-diffusion 第4行:均值除了unet的,还需要加入分类器得到的梯度 第7行:分类器推理,计算梯度过程,这里有个重要参数是args.classifier_scale # guided_diffusion/gaussian_diffusion.py def condition_mean(self, cond_fn, p_mean_var, x, t, model_kwargs=None): gradient = cond_fn(x, self._scale_timesteps(t), **model_kwargs) new_mean = (p_mean_var[\"mean\"].float() + p_mean_var[\"variance\"] * gradient.float()) return new_mean # scripts/classifier_sample.py def cond_fn(x, t, y=None): assert y is not None with th.enable_grad(): x_in = x.detach().requires_grad_(True) logits = classifier(x_in, t) log_probs = F.log_softmax(logits, dim=-1) selected = log_probs[range(len(logits)), y.view(-1)] return th.autograd.grad(selected.sum(), x_in)[0] * args.classifier_scale classifier-free —— 《classifier free diffusion guidance》 由于classifier-base需要训练分类器,并且在推理时有超参数args.classifier_scale的影响,以及引导条件的加入过于单一,没有办法通用性的加入各类条件。 为此,谷歌大脑的两位工程师提出了classifier free的方式,文中将分类信息通过embedding的方式加入到模型中训练,这里类似时间步t的embedding。 训练时会结合有条件与无条件进行训练,无条件则将分类标签embedding全部设置为0,具体细节可参见论文。 由于论文中没有提供代码,所以找到的代码是这个DDPM,其中的condition模式就是classifier-free。 第2行:训练时,有10%的是无条件的,90%是有条件的 第9行:标签信息与时间步一样,通过embedding汇入模型中,称为引导信息。 # DiffusionFreeGuidence/TrainCondition.py if np.random.rand() classifier free diffusion是打开了一扇大门,既然类别标签可以embedding,那么文本信息也可以通过该方式注入模型中进行引导,火爆的Stable Diffusion就是这么做的。 Diffusion Model 拓展 —— Stable Diffusion Stable Diffusion 是2022年火爆全球的文图生成(text-to-image)扩散模型,由于它开源,并且效果相当炸裂,因此已经被大多数人使用。 Stable Diffusion 背后的技术是LDM(latent diffusion model),之所以叫Stable Diffusion,或许与其背后的公司由Stability AI有关。 Stable Diffusion 是由CompVis、Stability AI和LAION三家公司共同创建,CompVis提供的技术LDM(latent diffusion model)源自论文《High-Resolution Image Synthesis with Latent Diffusion Models》,对应的github。LAION公司是一家致力于推动人工智能和数据科学发展的科技公司,其从互联网上抓取的 58 亿「图像-文本」数据,并开源了 LAION-5B数据集。而Stability AI的贡献,或许是出钱出力出人吧。 Stable Diffusion 的开源代码: https://github.com/CompVis/stable-diffusion 与 LDM(latent diffusion model)的开源代码:https://github.com/CompVis/latent-diffusion都在CompVis下,代码几乎一样。 下面简要介绍Stable Diffusion用到的latent diffusion model技术。 LDM之前,扩散模型在像素域进行扩散与去噪,这样的计算量过大。因此,考虑将扩散过程放到隐空间(latent space),即将数据经过encoder,来到特征空间,在特征空间上进行扩散和去噪。 这样一来,有以下好处: 计算量减小,训练和推理速度变快 可以加入更多引导信息,例如文本信息。 LDM论文中有一幅图很好的解释了LDM的思想:首先在pixel space,需要有encoder和decoder,在latent space采用了多头注意力机制,并且除了时间步信息,加入了conditioning模块,其中的引导信息可以是文本、图片、表征向量等等一切内容,然后为引导信息配一个embedding模块,就可以将引导信息加入模型中。 这里配上李宏毅老师的结构示意图,可知道LDM的核心在于2当中,处理的不再是像素空间,而是一个特征空间 stable diffusion 的使用与安装,网上有太多教程,这里不进行介绍,主要了解LDM的架构。推荐阅读:文生图模型之Stable Diffusion 与Stable Diffusion同一时期,叫得上名称的文图生成模型还有Midjourney、DALL-E 2,不过它们都是不开源的。 小结 本案例借助DDPM的代码剖析,了解扩散模型实现去噪,从而生成图像的过程和原理,并且对Guided Diffusion Model进行介绍,模型要能根据我们的“指示”生成特定的图像,这样的模型才有更大的应用价值。 在Guided Diffusion Model中,包含classifier-base 和 classifier-free,classifier-free是后来的主流。 classifier-free的代表即出圈的Stable Diffusion,Stable Diffusion是完全开源的,因此得到了全球的使用与关注。 在扩散模型中,LDM(latent diffusion model)思想值得仔细研究,它将一切信息都放到隐空间(特征空间)进行处理,使得图片处理起来更小,还可以进行多模态处理。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.7-image-captioning.html":{"url":"chapter-8/8.7-image-captioning.html","title":"8.7 图像描述——Image Captioning","keywords":"","body":"8.7 Image Captioning 图像描述 前言 图像描述是CV与NLP结合的一个典型任务,也是CV与NLP桥梁,本节将介绍图像描述模型的训练及使用,包括经典的CNN+RNN,以及现在流行的多模态模型。 本节内容将包括: 图像描述的概念,发展历史,常用数据集,BLUE评价指标 基于CNN+RNN+attention机制的图像描述模型训练 基于Clip+GPT2的图像描述模型训练 图像描述简介 Image Captioning (图像描述)是对图像采用文字进行描述的过程,Image Captioning又称图像图像字幕生成、图像标注、图像说明等,目前应用在医疗诊断、智能客服、人机交互等领域。 图像描述是一个交叉领域,将图片的视觉特征和自然语言处理结合起来,实现自动化的图片描述。 2014年之前,主流方法是采用数字图像处理进行特征提取,然后通过对特征的描述,实现image captioning。 2014年,Google发表了《Show and Tell: Lessons Learned from the 2015 MSCOCO Image Captioning Challenge?,首次采用CNN+RNN的形式进行端到端的深度学习模型训练,并且获得2015 COCO 图像描述的冠军。 2015年2月,Bengio领衔的团队针对Show and Tell改进,加入了attention机制,发表了《Show, Attend and Tell》, 该方法在文本生成模块,加入注意力机制,挖掘单词与图像区域的关联关系。 2019年,得益于Transformer广泛应用,图像描述开启了Transformer一统天下的时代,先后有Attention on Attention for Image Captioning、Image Captioning: Transforming Objects into Words和Entangled Transformer for Image Captioning等论文采用了Transformer进行图像描述,简要看了论文,发现模型结构图略显负责,不够优雅,这里就不贴图了,因为后续的超大预训练模型将会开启图像描述新范式。 2021年,随着图文、文本任务的预训练模型(pre-training model)的成功,学者迅速将其应用于图像描述,2021年有ClipCap,2022年BLIP, 2023年1月BLIPv2,目前BLIP系列已经达到较好效果,也将是本案例的重点。 简单来说,图像描述主流方法的发展,先经过CNN+RNN,及其变体,再到Transformer,再到VLP(visual language pre-training)模式,随着ChatGPT等预训练模型的成功,多模态任务也将有更多应用。 图像描述数据集 图像描述常用的数据集有Flickr8K、Flickr30K、Conceptual Captions (CC),COCO2014,数据集中语料库的高频词云如下图所示: 图片来源:《2021-07-From Show to Tell A Survey on Deep Learning-Based Image Captioning》 本案例将采用COCO2014,该数据集train有82783张,val有40504张,test有40775张,每张图片对应有5~7句的caption。为了线下比较模型的性能,会把train和val经过karpathy分割后,train变成113287张,val变成5000张,test变成5000张,而在线测试的test不变,仍为40775张。 标注规则为: 描述这个场景的所有重要部分; 不描述不重要的细节。 不要描述在未来或过去可能发生的事情。 不描述一个人可能会说什么。 不提供专有的人名。 这些句子应该至少包含8个单词。 更多数据集介绍可参考Image Caption 2021最新整理:数据集 / 文献 / 代码 图像描述评价指标 图像描述的评价,可以参考机器翻译的评价,都是比较两个句子之间的相似度。 机器翻译中常用的评价指标有,BLEU1-4, METEOR, ROUGE-L, and CIDEr等,这里介绍最常见的BLEU1-4。 BLEU是IBM在2002年提出的,用于机器翻译任务的评价,发表在ACL,引用次数10000+,原文题目是“BLEU: a Method for Automatic Evaluation of Machine Translation”。 它的总体思想就是准确率,假如给定标准译文reference,模型生成的句子是candidate,句子长度为n,candidate中有m个单词出现在reference,m/n就是bleu的1-gram的计算公式。 当统计不再是一个单词,而是连续的N个单词时,就有了n-gram的概念,词组的概念称为n-gram,词组长度通常选择1, 2, 3, 4 举一个例子来看看实际的计算: candinate: the cat sat on the mat reference: the cat is on the mat BLEU-1: 5/6 = 0.83 BLEU-2: 3/5 = 0.6 BLEU-3: 1/4 = 0.25 BLEU-4: 0/3 = 0 分子表示candidate中预测到了的词组的次数,如BLEU-1中,5分别表示, the, cat, on, the, mat预测中了。BLEU-2中,3分别表示, the cat, on the, the mat预测中了。以此类推。 针对BLEU还有些改进计算方法,可参考BLEU详解 BLEU的优点在于它考虑的是n-gram级别的匹配,而不是词级别的匹配,因此可以考虑更长的匹配信息,从而更好地评估翻译的质量。 但是,BLEU的缺点在于无论哪种n-gram被匹配上了,都会被同等对待,这可能会导致一些问题。例如,动词的匹配在翻译中可能比冠词更重要,但是在BLEU中,它们被同等地看待,这可能不太合适。 CNN+RNN 代码实现 接下来采用CNN+RNN结构,并配合attention机制,实现图像描述模型训练, 在coco2014上可实现23.1的BlEU-4 。 代码来自github,论文可参考《Show, Attend and Tel》 数据采用github上推荐的下载链接,coco2014,数据集划分采用 Andrej Karpathy划分好的json文件。 先看效果图,这是一张测试图片,模型可以输出 a brown teddy bear sitting on top of a pair of shoes,能对图中的泰迪、鞋子进行表述。 数据模块 数据下载与转换 首先下载数据,并转换数据为pytorch的dataset读取的形式 下载图像数据,val2014, train2014 文件夹,并将其放置到xxx/coco2014/images下 下载标注数据,caption_datasets.zip,其中包含coco, flickr8k, flick30k的标签,这里只使用dataset_coco.json 在配套代码00_create_input_files.py中设置以下路径,运行后获得相应的数据 create_input_files(dataset='coco', karpathy_json_path=r'G:\\deep_learning_data\\coco_2014\\image-caption-json\\dataset_coco.json', image_folder=r'G:\\deep_learning_data\\coco_2014\\images', captions_per_image=5, min_word_freq=5, output_folder=r'G:\\deep_learning_data\\coco_2014\\dataset-created', max_len=50) 获得的数据是经过预处理转换的,下面介绍对数据是如何处理的。 数据预处理 文本数据需要进行一系列的预处理,例如,将一张图片对应的5句不等长度的描述,整理为可以batch输入数据,这里涉及一些NLP常见的操作,下面通过代码进行剖析。 对描述的开头和结尾,加入起始、停止词, a man holds a football 将句子填充至等长,如100个词, a man holds a football .... 创建词映射,将词映射为编号, 如 [9488, 1, 20, 64, 3, 60, 57, 69, 35, 66, 14, 67, 17, 1, 68, 9489, 0,.., 0],其中9488和9489,0分别表示 上述信息,通过00_create_input_files.py获得,数据处理后,分别获得: HDF5文件,包含了所有图片,数据形状为 N, 3, 256, 256,但hdf5在pytorch的dataloader中无法启用多进程,因此本案例改用保存图片路径的方式,在dataset中再读取图片 CATIONS*.json,包含每个描述句子添加起始词、填充、映射后的索引,为 N_c * I 形式, N_c表示所有描述句子的梳理,I表示图像的数量。由于coco固定了一张图片有5个描述,因此N_c == 5. CAPLENS*.json,包含每句描述的长度,N_c * I , N_c表示所有描述句子的梳理,I表示图像的数量。 WORDMAP*.json,所以一个字典,包含了单词到索引的映射关系。 xxx_paths.pkl:包含每张图片的路径,用于在dataset中进行图片读取 原代码采用HDF5进行图片读取,这样无法采用num_worker>1,因此在这里我将代码改为基于图片路径形式进行读取,可以充分利用cpu加载数据。详细内容参见dataset的编写。 模型模块 模型部分主要有encoder, decoder, attention三个模块。 encoder为resnet101将图片变为14x14x2048的特征图,并且经linear层变换到向量形式,便于与文本特征拼接 attention模块由多个linear层构成,注意力权重最终经sigmoid函数得到0-1区间的注意力权重,并且是1x196的向量,对应着14x14的图像区域。 decoder为标准的LSTM,其输入由词嵌入向量1x512 + attention的特征1x2048构成 output模块采用LSTM的hiddent feature,经过linear层输出1x9490的分类概率向量,9490表示单词库中总共有9490个单词。 模型结构如下图所示,本图对github原图进行了详细补充,对每一个数据维度及流向进行了标记: 训练与推理 在配套代码01_train.py代码中仅需要配置数据所在文件夹data_folder,执行 python 01_train.py即可。 在epoch=14左右会得到最优BLEU-4, 22.7。 关于超参数,num_worker可以设置大于1,batchsize设为了256,采用的是1080ti 11GB,显存占用8G+,耗时大约1.5小时一个epoch。 训练代码比较常规,只是文本任务在数据处理上有一个比较特殊的操作就是组batch时,先对文本长度进行排序,然后依次取batch送入LSTM。组batch的操作,具体如github所示: 推理观察 训练到14个epoch时可以将模型拿来试试了,将 BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth.tar的路径配置到02_inference.py args.img目前支持图片以及文件夹形式的推理 args.model 是ckpt的路径 args.word_map是单词库,模型预测出来的9490个类别需要对应到具体的单词,用的就是这个字典。 out_dir是输出图片的文件夹 args.img = r'G:\\deep_learning_data\\coco_2014\\images\\val2014' #img path or dir args.model = 'BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth.tar' # model checkpoint args.word_map = r'G:\\deep_learning_data\\coco_2014\\dataset-created\\WORDMAP_coco_5_cap_per_img_5_min_word_freq.json' out_dir = './output_img' 效果如下图所示 训练好的模型权重下载:链接:https://pan.baidu.com/s/1fLS0_EPqfj0x_PX3JLN1Eg 提取码:ta3v 到这里,快速地实现了一个效果还不错的图像描述模型,里边有一些新知识值得学习: 图像可经过模型提取特征,变为特征向量与文本特征向量融合,实现图文多模态的处理 LSTM训练时,将句子长度排序,便可获得batch size依次递减的训练样本 coco数据训练时,一个样本为 (一张图片,一个句描述,句子长度),因此共5x113287=566435个训练样本 随着Transformer不断的落地应用,以及多模态模型langueage-visual模型的成功,基Transformer体系的图像描述模型成为主流。 下面介绍一款”亲民“的模型,CLIPCap,亲民指它在1080上1天就可以训练,并且仍旧使用了强大的Transformer模型,论文idea值得学习。 CLIPCap 代码实现 接下来,借助强大的多模态模型的特征提取能力实现图像描述。 这里采用CLIP对图像的理解能力,获取图像编码特征embedding向量,再经过一个生成器模型,实现图像描述。这个工作就是2021年11月发表的ClipCap。 ClipCap提出一种轻量化的方法,可以结合 CLIP的image encoder 和 GPT-2 ,实现图像描述。 ClipCap有三个部分,分别是image encoder, mapping network, gpt2。其中image encoder和gpt2都是在超大规模数据集上预训练过的,可以直接用。 在学习ClipCap前,先来了解什么是CLIP,什么是GPT2。 CLIP简介 CLIP(Contrastive Language-Image Pre-training),基于对比学习的文图预训练模型,该模型可实现zero-shot的图像分类、检测等下游任务,也可以作为图像检索、图像生成、图像描述任务的backbone,是图文多模态模型领域中划时代意义的一个作品。 CLIP于2021年2月由openAI发表,并开源了模型,模型由4亿的图文数据,采用对比学习方式进行训练得到,由于对比学习与超大规模的数据集加持,使CLIP模型很好的理解了自然图像,在众多数据集上表现出了优异的zero-shot性能,同时在表征学习(representation learning)中也很好。 CLIP模型由text encoder和image encoder组成,分别对文本和图像进行特征提取,获得特征向量,随后进行对比学习,即图像1与文本1是一对数据,I1向量要与T1越接近越好,I1与其它的T向量越不接近越好,对于一个batch的数据来说,可以构成一个方阵,对角线上是正样本,非对角线是负样本。 训练伪代码如下: # 分别提取图像特征和文本特征 I_f = image_encoder(I) #[n, d_i] T_f = text_encoder(T) #[n, d_t] # 对两个特征进行线性投射,得到相同维度的特征,并进行l2归一化 I_e = l2_normalize(np.dot(I_f, W_i), axis=1) T_e = l2_normalize(np.dot(T_f, W_t), axis=1) # 计算缩放的余弦相似度:[n, n] logits = np.dot(I_e, T_e.T) * np.exp(t) # 对称的对比学习损失:等价于N个类别的cross_entropy_loss labels = np.arange(n) # 对角线元素的labels loss_i = cross_entropy_loss(logits, labels, axis=0) loss_t = cross_entropy_loss(logits, labels, axis=1) loss = (loss_i + loss_t)/2 text encoder采用的是标准的text transformer。 image encoder则有多个模型,主要是ResNet系列,包含5个不同大小的模型:ResNet50,ResNet101,RN50x4,RN50x16和RNx64(后面三个模型是按照EfficientNet缩放规则对ResNet分别增大4x,16x和64x得到),ViT系列,3个不同大小的模型:ViT-B/32,ViT-B/16和ViT-L/14。所有的模型都训练32个epochs,采用AdamW优化器,而且训练过程采用了一个较大的batch size:32768。由于数据量较大,最大的ResNet模型RN50x64需要在592个V100卡上训练18天,而最大ViT模型ViT-L/14需要在256张V100卡上训练12天,都需要几千个V100天。 模型训练好之后,神奇之处在于其可以zero-shot的进行图像分类,这个方式很具有创新性。具体步骤是 人为设定一批候选类别的文本描述,例如:A photo of {label}, 然后label分别填入候选类别的单词,假设有N个类别,则得到N个句子 送入text encoder,得到N个文本特征向量 图像送入image encoder,得到图像特征向量 图像特征向量与N个文本特征向量进行比较,找到最近的那个特征向量,即可得到类别输出 使用CLIP进行zero-shot分类,另外一个比较重要的地方是文本描述的生成,上面的例子我们采用A photo of {label},但其实也有其它选择,比如我们直接用类别标签,这其实属于最近NLP领域比较火的一个研究:prompt learning或者prompt engineering,具体可以见这篇综述论文:Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing,这里就不再进行阐述。 感兴趣可参考官方的ImageNet分类的Prompt engineering,采用80个不同的prompt来进行集成,发现在ImageNet数据集上能带来3.5%的提升,具体见CLIP公开的notebook。 到这里大体了解CLIP中有一个对自然图像理解能力很强的image encoder,可以获得很好的图像特征向量,接下来需要一个能接收embedding向量,输出文本描述的强大模型,GPT当之无愧作为首选。 GPT2简介 GPT2(Generative Pre-trained 2),是由OpenAI开发的生成式自然语言模型,鉴于chatGPT的火爆,这里不过多介绍GPT1,2,3,3.5,4的差别。 在这里需要了解gpt2是一个生成式模型,根据输入的文本信息,可以生成一系列文本,如输入一个问题句子,gpt将句子变为text embedding,输入到模型中,然后一个一个单词的输出,最终输出一句回答。其中,人类输入的问题句子,可以看成是prefix embedding,gpt根据前缀信息,依次生成内容。 Prefix embeddings是指在GPT模型中,为每一个输入词添加一个前缀,然后将添加前缀后的词转化为向量表示。这个前缀是指输入词前面的所有词,它可以为模型提供更多的上下文信息,帮助模型更好地理解输入文本的含义。 举个例子,假设输入文本是“我喜欢吃苹果”,对于“苹果”这个词,它的前缀是“我喜欢吃”,添加前缀后的词就是“我喜欢吃苹果”。这个添加前缀后的词可以被转化为向量表示,然后作为GPT模型的输入。 在CLIPCap中,正式利用了gpt2强大的文本生成能力进行图像描述,但图像信息如何输入到gpt呢?接下来就看看CLIPCap的创新。 CLIP Captioning 2021年11月,ClipCap提出一种轻量化的方法,可以结合 CLIP的image encoder 和 GPT-2 ,实现图像描述。 ClipCap有三个部分,分别是image encoder, mapping network, gpt2。其中image encoder和gpt2都是在超大规模数据集上预训练过的,可以直接用。 由于CLIP和GPT2不好训练,所以设计一个mapping network,图像embedding特征向文本embedding特征的转换,从而巧妙的衔接了CLIP与GPT2,并且可以仅训练mapping nework,这一点与当前BLIP v2中的QFormer是一样的。 结合上图,来看看模型到底是如何实现图像描述的。 第一步,一张图片及其对应的描述文本,会被输入到CLIP中,得到image embedding向量:512维,文本则通过gpt2的字典转换为tokens,gpt2字典有50257个词。 第二步:图像特征经过maping network,获得40x768的特征,可以理解为将图像翻译为了40个768的特征向量 第三步:文本tokens经过word2emb获得 text embedding向量40x768维度,这里的40表示句子最长有40个单词,如果补足40,填充即可。 第四步:图像与文本特征拼接,输入到gpt2进行训练,gpt2输出80个50257维的分类概率向量,其中取后40个向量进行输出分类的单词,最终形成句子。 上述为训练过程,其中CLIP和GPT2是可以冻结的,详情可看代码。 在推理的时候,GPT2 word2emb的这一分支是没有的,gpt2仅拿到图像的40x768向量进行推理,逐步生成每一个单词,最终形成句子。 CLIP Captioning 训练代码实现 第一步,数据集下载及准备 首先准备数据集,这里采用coco 2014,需要下载的文件有 预处理过的标签文件:train_caption.json 原始图像文件夹:train2014,val2014 组织为以下目录形式 ROOT_PROJECT / data / annotations / train_caption.json ROOT_PROJECT / data / train2014 ROOT_PROJECT / data / val2014 第二步,CLIP模型特征提取,运行配套代码00_parse_coco.py python 00_parse_coco.py --clip_model_type ViT-B/32 由于不涉及CLIP的训练,因此CLIP对每一张图片的输出是固定的,所以可以把训练集中的566757个样本对进行特征提取及文本tokens的映射。 执行以下代码,预计需要3小时完成56万多张样本对的处理。 结果将保存在 ROOT_PROJECDT / data / oscar_split_ViT-B_32_train.pkl ROOT_PROJECDT / data / oscar_split_ViT-B_32_train_tokens.pkl 第三步,模型训练,运行配套代码01-train.py python 01-train.py --data ./data/coco/oscar_split_ViT-B_32_train.pkl --out_dir ./coco_train/ 到这里就可以开始训练了,在1080ti上训练10个epoch,耗时16小时,模型将保存在 ROOT_PROJECDT / coco_train 文件夹下 提供一个预训练权重,模型权重下载链接提取码:mqri CLIP Captioning 推理代码实现 推理示例,运行配套代码02-inference.py,需要配置下面三个路径即可。 ckpt_path = r'coco_prefix-009-2023-0411.pt' path_img = r'G:\\deep_learning_data\\coco_2014\\images\\val2014' out_dir = './inference_output' 在推理代码中,配置好模型路径、测试图片/文件夹路径,输出路径,运行即可得到结果 推理仅需要0.2s即可获得一例输出,速度还是可以接受的。 下图为val2014中的一些推理结果示意图 到这里Clip Cap就结束了,简单回顾一下Clip Cap整体内容。 借力:Clip Cap 站在了两个巨人的肩膀上,分别是CLIP和GPT2 磨合:为了让两个模块可以更好的融合使用,提出mapping network模块将CLIP的输出转换为GPT2能接收的特征向量形式, 40x768的40个\"单词\"的特征向量形式。 亲民:在1080ti上一天时间就可以训练coco数据集,并且还能用上大模型,这个motivation不得不说很巧妙 如果想将Clip Cap运用于中文,也推荐大家阅读ClipCap-Chinese 小结 Image Captioning 是一个CV+NLP的典型应用,是从一幅图输出一句话的过程,并且具有较高的商业价值,如字幕生成、图像标注、图像说明等。 本文介绍了图像描述的概念,发展历史,常用数据集,BLUE评价指标,并通过代码实现两种主流的图像描述算法。 CNN+RNN架构 CNN负责提取特征图,并变为特征向量1x512作为h0输入到RNN中 RNN逐步输出单词 Clip Cap 借助大模型——CLIP和GPT2,效果比CNN+RNN好很多,这也是基于Transformer的深度学习模型广泛应用的原因之一 巧妙的将图像特征与NLP模型嫁接起来,后续更为强大的BLIP v2同样采用了此操作 关于图像描述、文图、图文等多模态任务,十分推荐大家学习以下内容 https://github.com/salesforce/BLIP https://github.com/salesforce/LAVIS Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.8-image-retrieval-1.html":{"url":"chapter-8/8.8-image-retrieval-1.html","title":"8.8 图像检索(上)——理论基础","keywords":"","body":"8.8 Image Retrieval 图像检索 (上) 前言 本节介绍图像检索的概念及相关优化算法,为后续代码实践打下基础。 本节主要内容包括: 常用数据集 评价指标:R@K、mAP的计算 常用的loss 向量检索框架简介:Faiss、Milvus、Jina、Proxima和vearch都是工业界常用的向量检索框架 向量检索优化算法:LSH(局部敏感哈希)、HNSW(基于图的近似最近邻搜索)、PQ(乘积量化)、IVF(倒排检索) 检索中的排序:粗排、精排、重排的概念 图像检索简介 图像检索(Image Retrieval)指根据用户输入的信息进行图像查询,在数据库中查找出与之相似/相关的图像。 图像检索在电商、安防、医学领域有广泛应用,例如微信扫一扫、淘宝的拍立淘、搜索引擎的图片搜索功能等。 图像检索可根据输入的内容不同,可分为以文搜图和以图搜图。 以文搜图又称TBIR (Text Based Image Retrieval),是通过图片名称、关键词等信息进行检索,是早期图像检索的方法,依赖于图像的描述信息。 以图搜图又称CBIR (Content Based Image Retrieval),也叫基于内容的图像检索,也是目前主流的方法。CBIR的方式更符合人类视觉感觉,基于图像内容、图像相似度进行检索相似的图像。本节后续内容均以CBIR为例。 下图是图像检索系统的基础组件,通常分为offline和online两部分,offline主要构建图像数据库及特征数据库,online则是完成query图像特征提取,并从特征数据库中找到相似的特征,最终由推荐策略模块输出相似图像。 在构建图像检索系统时,需要关注: 特征提取器:传统方法中会基于SIFT、HoG等特征描述子,2012年后,主要是基于神经网络进行特征提取,获得特征向量,如1x768维,本文后续也会基于CLIP来进行特征提取。对特征提取器一个简单的要求是,相似图像的特征向量的欧氏距离要相近,差异大的图像的欧氏距离要大。特征提取模型可分为无监督与有监督的,无监督的主要采用视觉预训练模型进行特征提取,如CLIP, BLIP之类的,有监督的训练则可以采用CNN-base或者ViT-base的模型进行训练。 向量检索:如何快速的从亿万级的特征数据库中找到相似的那一批特征向量,这是向量检索及推荐策略需要考虑的,后续会介绍一系列快速的向量检索策略。 推荐策略:由于特征检索阶段采用了快速检索方法,势必损失一定精度,因此在后续需要做一些排序策略,如粗排、精排、重排等,最终输出相似的检索结果。 图像检索数据集 图像检索常用数据集并不多,常用的有Oxford-5k, UKBench, Holidays, Paris-6k Oxford-5k 由牛津的11个建筑物的5062张图片构成,每个建筑物提供了5张query image。标签从官网下载,图片从paddle下载 UKBench 包含2550组,每组4张图,总共10200张图片,图片由日常生活中常见的物体构成,如物体、场景和CD封面等。下载链接:https://pan.baidu.com/s/1LAURtLwq8UYPW3cnetDCyg 提取码:yrsy Holidays 是个人假日相册中收集的1491张图像,有500张query images, 991张相关图像。 Paris-6k 包含来自Flickr上爬取的6412张图片,由12组巴黎的建筑构成,总共提供了500张 query images进行评估。 数据从kaggle下载 图像检索评价指标 以图搜图中,常用的评价指标有mAP和R@1, R@5, R@10。 R@K:top-k个检索结果中的召回率(recall),对于检索结果中存在目标结果的即判为召回,记1分, 总得分÷样本总数 = R@K。 假设我们有一个包含100张图片的数据集,进行一次图片检索,对于查询图片Query-img来说,100张图片里只有1张是相似的,GT为图片编号5,其它的都是负样本,假设算法返回的Top 10检索结果如下: 排名 图片编号 是否为真实目标 1 56 否 2 12 否 3 5 是 4 88 否 5 1 否 6 90 否 7 3 否 8 21 否 9 6 否 10 47 否 R@1,Top 1的图片编号为56,不是真实目标,因此R@1的值为0。 R@5,Top 5的图片编号为56、12、5、88、1,其中图片5是真实目标,因此R@5的值为1。 R@10,Top 10的图片中,其中图片5是真实目标,因此R@10的值为1。 假设有20张查询图片,统计20张查询图片的R@1,得到20个R@1,求平均,即可得到最终R@1。 由此可知,R@1≤ R@5≤ R@10。 mAP mAP(mean average precision)是AP的平均值,对于每一张查询图片计算一个AP,求取平均。 AP值是PR(Precision-Recall)曲线下的面积,具体定义参考机器学习基础知识。 这里借助一个视频的示意图来讲解具体的计算 上图有两个查询,分别是query 1和query 2,其中黑色的样本表示正样本,是期望所检索到的样本,白色是负样本。 对于查询图像1,有5个相关结果,检索的排序结果如图所示,分别排在了1,3,6,9,10。 AP是将5个结果的Precision求平均,每个结果的Precision为 “标签次序÷结果次序”。 因此查询图像1的AP = (1/1 + 2/3 + 3/6 + 4/9 + 5/10)/ 5 = 0.62 mAP则是将所有查询图像的AP求取平均,如上例,为 (0.62+0.44) / 2 = 0.53 图像检索常用loss 图像检索选择特征提取器时,如果需要自行训练一个特征提取器,通常会选择一个适合的loss来约束模型输出的特征向量。 这里可能不好理解,先从图像分类模型思考,分类模型输出层前一层通常认为是对图像的特征描述,这个特征向量被认为是具备图像语义信息的描述,它只需要经过一个分类层,就能实现图像分类。 Triplet Loss 同理,也可把该特征向量认为是图像的描述,用于图像检索,但是图像检索里,还希望相同类别的向量的欧氏距离要近,不同类别的向量要远。 这时就可以在此处增加一个Triplet Loss(三元组损失函数)进行训练,triplet loss训练时有3个样本,分别称为anchor, negative, positive。 A表示目标图片,N表示负样本,要与目标图片远离的, P表示正样本,与A是同类型图片,要接近的。 更详细的triplet loss请阅读原论文,提出triplet loss是为了解决人脸比对问题的,后来拓展到各类对比任务中。在图像检索任务中,Triplet Loss同样具有较好的表现。通过将同一类别的图片的嵌入向量尽可能地接近,而将不同类别的嵌入向量尽可能地远离,可以实现高效的图像检索。 (N+1)-tuplet loss 三元损失函数考虑的 negative 样本太少,以至于收敛慢,为此,研究者提出了(N+1)-tuplet loss,一次考虑N-1个负样本,N=2时,等价于 triplet loss。 (N+1)-tuplet loss 如下图所示 N-pair Loss (N+1)-tuplet loss 有个缺点是计算量大,需要计算N-1个负样本的距离,推理成本过高。 为此,提出了N-pair Loss,巧妙的实现样本的重复利用,减少了负样本的多次运算。N-pair loss 思想是把其他样本的正样本作为当前样本的负样本,这样就不用重复计算不同样本的负样本,只需要计算 N 次即可得出。 除了 triplet loss 思路外,还可以借鉴其它类似任务的loss,例如: 人脸:ArcFace,sub-ArcFace 类内损失:center loss, Island loss 类间损失:cosface 度量学习任务:Multi-Similarity Loss, Contrastive Loss / Pairwise Ranking Loss,SimCSE loss, Smooth AP 更多loss参考论文2021年的Deep_Image_Retrieval_A_Survey 检索核心技术——向量检索 向量检索概念 图像检索的核心技术之一是向量检索,也称向量索引、相似度匹配,是将query向量与数据库中万亿级的向量进行相似度匹配,要求又快又准的找出相似向量。 向量相似度可以通过欧氏距离、余弦距离、汉明距离等距离度量方法来计算,通常float类型采用欧氏距离,二值哈希采用汉明距离。 对于欧氏距离、余弦距离来说,时间复杂度为O(nd),其中n是数据集中向量的数量,d是向量的维度,这对于亿万级的检索是不可接受的。 为了实现快速的向量检索,针对暴力检索(精确检索)有一系列的优化算法,例如基于树的KD-Tree,基于图的NSW、HNSW, 倒排索引、PQ(乘积量化,Product-Quantization)等。 由于采用了快速索引,必然导致精度损失,因此在向量检索之后,通常有排序算法的介入,一般有粗排、精排和重排,这个在下一小节讲解。 向量检索框架 向量检索不仅可用于图像检索,在搜索引擎、短视频推荐、商品推荐等领域均是基础模块,有较多开发者及对应的产品,因此,可以选择开源的向量检索引擎,而无需造轮子。 下面简单总结几款常用的框架 Faiss:由 Facebook 开发的适用于稠密向量匹配的开源库。 支持相似度检索和聚类 支持多种索引方式 支持CPU和GPU计算 支持Python和C++调用 常见的人脸比对,指纹比对,基因比对等 缺点是单机部署,无法分布式 Milvus:是一个开源的分布式向量搜索引擎。集成了成熟的向量相似度搜索技术,包括Faiss、Annoy、NMSLIB等。 Jina :Jina AI公司开发,是一家专注基于深度学习模型搭建搜索引擎技术的开源商业公司 Proxima:阿里巴巴达摩院自研的向量检索内核,通用化的向量检索工程引擎,实现了对大数据的高性能相似性搜索,支持 ARM64、x86、GPU 等多种硬件平台 vearch:是京东开源一个分布式向量搜索系统,可用来存储、计算海量的特征向量,在 faiss 的基础上研发了 vearch,提供了类似 ElasticSearch 的灵活易用的 RESTFul API,可以方便地对表结构及数据进行管理查询。 更多框架介绍推荐阅读几款多模态向量检索引擎 Faiss简介 对于80%的场景,基于faiss足以满足需求,并且faiss开源早,性能强大,功能多,且易上手,这里就介绍Faiss的使用及常用算法的原理。 Faiss由 Facebook AI开发及开源,适用于亿级的稠密向量检索,并且部分算法支持GPU加速。 faiss中常用的算法有,精确检索FlatL2、FlatIP,局部敏感哈希LSH(Locality-Sensitive Hashing),基于图的近似最近邻搜索 HNSW,倒排检索 IFS(Inverted File System,倒排)和 乘积量化PQ(Product Quantization)。 更详细介绍,参见faiss wiki 最常用的算法是倒排、乘积量化,接下来会对上述4个算法进行介绍,并详细介绍倒排与乘积量化。 LSH——局部敏感哈希 LSH是哈希算法中的一种,是将向量映射为标量,并进行分桶,距离较近的向量在哈希后映射到同一个桶的概率较高,反之概率较低,这个性质称之为局部敏感。 下面借助一位Up主的讲解及代码,解释LSH实现过程: 选择一系列hash functions,对原始数据进行投影,一个样本变为一个标量 选择分桶间隔w,标量除以间隔w,并向下取整,获得样本的hash编码 如上图,6个数据经过一次投影后得到的hash编码分别是 0, 1, 2, 3, 5, 5,选择多个hash functions即可得到一个样本的多个编码,即可构成一个编码向量。 hash function到底要选择怎样的投影方式,才能使得距离较近的向量在哈希后映射到同一个桶的概率较高,反之概率较低呢? 其实这里可以直接采用高斯分布采样得到的向量,与样本x进行点乘即可,这个可以通过P稳定分布定义进行证明,详情可参见第八讲 图像检索 下面看一个具体案例,包含8个向量,采用4个hash functions进行编码,得到的编码如下。 可以看到,样本7和样本8的4个编码都相同,表明它们很类似。 同理,样本1和样本2的hash编码也很类似,因它们的样本数据比较类似。 原始数据: [[8, 7, 6, 4, 8, 9], [7, 8, 5, 8, 9, 7], [3, 2, 0, 1, 2, 3], [3, 3, 2, 3, 3, 3], [21, 21, 22, 99, 2, 12], [1, 1, 1, 0, 1, 0], [1, 1, 1, 1, 1, 0]] 编码向量: [3.0, 3.0, 0.0, 1.0, 8.0, 0.0, 0.0] [5.0, 5.0, 1.0, 2.0, 32.0, 1.0, 1.0] [4.0, 4.0, 1.0, 2.0, 22.0, 0.0, 0.0] [7.0, 6.0, 2.0, 3.0, 21.0, 1.0, 1.0] import numpy as np import random def getHash(v, x, b, w): return (v.dot(x) + b) // w def dealOneBuket(dataSet): k = dataSet.shape[1] b = random.uniform(0, w) x = np.random.random(k) buket = [] for data in dataSet: h = getHash(data, x, b, w) buket.append(h) return buket if __name__ == \"__main__\": dataSet = [[8, 7, 6, 4, 8, 9], [7, 8, 5, 8, 9, 7], [3, 2, 0, 1, 2, 3], [3, 3, 2, 3, 3, 3], [21, 21, 22, 99, 2, 12], [1, 1, 1, 0, 1, 0], [1, 1, 1, 1, 1, 0]] dataSet = np.array(dataSet) w = 4 hash_funcs_num = 4 for _ in range(hash_funcs_num): print(dealOneBuket(dataSet)) HNSW——基于图的近似最近邻搜索 HNSW(Hierarchical Navigable Small World)是一种以空间换时间的基于图的最近邻搜索,属于基于层级结构的ANN(Approximate Nearest Neighbor)算法,通过建立多层图结构实现高效的最近邻搜索。其核心思想是在每一层图中使用稠密连接的方式建立向量之间的联系,同时保持局部连通性,从而形成“小世界”网络。 分层机制类似高速公路,顶层是省份间的连接,往下则是城市之间,城市内部之间的连接,由大到小的缩小检索范围。 更详细理解,推荐阅读Faiss的手册 倒排索引的优点是可以快速地定位包含某个特征的向量,适用于高维稠密向量。但是在处理高维稀疏向量时,倒排索引的效果会受到影响。 总之,倒排索引是向量检索算法中重要的一种索引方式,可以大幅提高检索效率和准确性。 PQ —— 乘积量化 PQ(product quantizaition)是2011年,Herve Jegou等学者在PAMI上发表的论文《Product quantization for nearest neighbor search》,提出的一个快速向量检索算法。 PQ方法可以减少几十倍的RAM消耗,并且获得数倍的速度提升,并且精度损失不是很大,非常适合高维向量检索任务的优化。 PQ算法的思想是将原始向量分解为多个子向量,并对每个子向量进行量化,从而将高维空间中的向量转换为一个小的离散码本,编码代表子向量所处的聚类中心的编号。查询向量需要与子向量的聚类中心进行距离计算,获得距离表,随后数据库向量根据编码进行查询,即可获得查询向量与被索引向量的距离,最后对子段距离求和、排序。 PQ的思想可以总结为,将高维向量划分子段,并且利用子段的聚类中心来代替原始数据,查询向量只需要与聚类中心计算距离,即可实现所有向量距离的近似计算。 下面以一个1000个128维向量数据库为例,讲解PQ过程。 第一步,pre-train,获得聚类中心。 第二步,对数据库向量进行编码。 第三步,查询向量与聚类中心计算距离表。 第四步,查询所有向量与查询向量的距距离求和、排序,得到结果。 第一步:pre-train,对128维向量划分为M份,这里划分8个子段,每个字段长度为16,针对每个字段进行K-means聚类,聚为256类(256是作者自定义的)。 打不严谨的比方,1000个向量分为256类,平均4个向量为一簇,由1个聚类中心向量代表,后续查询向量只需要与聚类中心比较,就可知道查询向量与4个向量的距离了。简单理解就是聚类簇中的向量统一由聚类中心作为代表,查询向量与中心近,那么就与类成员近,以此减少计算量。 第二步,对数据库向量进行编码,得到数据库的PQ code,这个code的作用是用于查表的,即每个向量它被谁代表了,后续只需要去找“代表”获取距离,而不是与查询向量计算距离。 1000个128维向量,最终的PQ code为 1000x8bit的码表,码表上的值表示它归属于哪个聚类中心,如第一个向量的第一个字段为2,则说明它属于第二类,它后续与查询向量的距离只需要查看,查询向量与第二个聚类中心的距离即可。 经过PQ编码,内存的效果显著下降,下降了64倍,64来自128维下降到了8维,这里减少了16倍;数据精度由32b减少到了8b,这里减少了4倍,总共64倍。 第三步,查询向量与聚类中心计算距离,获得查询向量与聚类中心的距离表(256x8)。 例如查询向量为[1, 2, ..., 128],首先对第一子段进行256个聚类中心的距离计算,采用[1, 2, 3, 4, 5, 6, 7, 8]与256个聚类中心计算距离,填入Distance Table的第一列, 以此类推,获得256x8的距离表。 距离表的作用是供PQ code查询的,前面提到,数据库向量已经被代表了,被聚类中心代表,查询向量不与数据库向量直接计算距离,而是与聚类中心计算距离。 数据库向量只需要找到它的代表与查询向量的距离即可,因此PQ code是 Distance Table的索引,只需要查表即可获得数据库向量与查询向量的距离。 第四步,查询所有向量与查询向量的距距离求和、排序,得到结果。 PQ算法巧妙的采用了代表(聚类中心),以此减少运算复杂度,同时得益于代表(聚类中心),数据库无需存储所有数据,只需要存储所处聚类的编号,从存储上优化了算法。 IVF —— 倒排检索 IVF(Inverted File Index),倒排文件索引,用于快速地定位包含某个特征的向量。其中使用了聚类分组、倒排索引的方法来加速索引速度。 这里的Inverted Index 是著名的倒排索引算法,最早来自文本搜索领域,关于Inverted Index的命名及翻译,让人多少有些困惑,倒排?怎么倒?怎么排? 其实这里和“倒”没啥关系,更好的中文意译是,分词索引或反向索引。详见知乎问题 Inveted Index是从文档检索中提出的,以往检索文档是通过一个一个文档中从头到尾比对,是否存在关键词,是文档-->关键词的方式;倒排则是将文档中文字进行分词,并且构建词到文档编号的一个字典索引,查询时只需要根据字典的key索引,可得到哪些文档中存在这些关键词。 这里借助 喔喔牛的回答 - 知乎 假设有一个文档数据库,共5分文档,现在需要检索“wave”关键词在哪份文档中,正向索引则是挨个文档一次检索匹配“wave”,到了第4个文档的中间才能找到,相当耗时。 倒排索引的思路是将文档中的词先拆分,需要用分词工具把文档打散称为一系列word,然后构建关键词到文档的关系字典。 如下图,通过倒排索引字典,直接获取文档3是包含\"wave\"的,效率提升显而易见。 回到Faiss中的IVF,IVF是将向量先分为nlist个组,每组有一个聚类中心及类别id,通过构建聚类中心+类别id 到 向量的索引字典,可以快速的找到查询向量与哪一些数据库向量是相近的,与上文的PQ量化中的聚类类似,一群被检索向量采用聚类中心作为代表。 具体地,IVF算法流程如下: 将数据集进行聚类,得到若干个簇(nlist个),每个簇有一个代表它的中心点; 为每个簇建立一个倒排文件,key是聚类中心,value是簇内所有向量; 对于一个查询向量,计算其与每个簇中心点的距离,确定其所属的簇; 在所属簇的倒排文件中搜索与查询向量最相似的数据点。 在实际实现时,还会针对边缘问题,提出所个类簇的比对,如下图所示,对聚类中心最新的8个类簇内所有向量均进行比对,以此提高召回。详情可见Faiss中的IVF IVF+PQ Faiss中用得更多的是将IVFPQ,主要是用IVF中的方法来减少搜索向量范围,然后利用PQ的索引。product-quantization中对FlatL2(精确检索)、PQ和IVFPQ进行了对比,可见IVFPQ速度快,并且召回与PQ相当。 这里需要注意的是IVF中的nlist和nprobe是超参,需要仔细选择,尤其是nprobe,文中实验采用了nprobe=48才达到52%的召回,nrpobe越高,召回越高,但是时间越慢。 检索核心技术——粗排、精排、重排 上文提到的一系列向量检索方法仅仅是检索的第一步——召回,通常一个检索系统是级联式的,多层级一步一步的由粗到精,得到最终输出结果。 如下图所所示 召回,是处理亿万级别数据量,要求的是速度快,精度可以不高,但别漏,去粗取精还有粗排、精排和重排来完成,但是漏了的话,后续是没办法“生成”、找回的。 粗排,是召回和精排之间的过渡,速度比精排快,但精确度不需要那么高。 精排,核心层,直接决定检索推荐的效果; 重排,根据业务逻辑,决定哪些item的顺序需要调整。 总结下来,推荐算法的这一套级联策略——召回、粗排、精排、重排,与其电商业务、互联网搜索业务息息相关,每一个模块为了处理特定了业务逻辑而设计的一个操作。 例如第一步召回是从亿万级数据捞数据的过程; 粗排、精排是根据特定的业务背景设计算法、模型,详见知乎文章; 重排也是根据业务逻辑,例如为了提升用户的多样性体验,扶持业务产品,这时在重排层则需要写一些固定的逻辑判断来重排。 在图像检索领域,较少涉及粗排、精排,通常只需要做一个重排,图像检索的重排技术非常细分,这里不进行介绍了,感兴趣自行阅读一下论文: 扩展查询(Query Expansion) Ondrej Chum, James Philbin, Josef Sivic, Michael Isard, and Andrew Zisserman. Total recall: Automatic query expansion with a generative feature model for object retrieval. In Proceedings of the IEEE International Conference on Computer Vision and Pattern Recognition, pages 1–8, 2007. 1, 2, 3, 9 Ondˇ rej Chum, Andrej Mikulik, Michal Perdoch, and Jiˇ rí Matas. Total recall ii: query expansion revisited. In Proceedings of the IEEE International Conference on Computer Vision and Pattern Recognition, pages 889–896, 2011 几何信息法(Geometric Context Verification (GCV)) Yannis Avrithis and Giorgos Tolias. Hough pyramid matching: Speeded-up geometry re-rankingfor large scale image retrieval. International Journal of Computer Vision, 107(1):1–19, 2014 行人重识别中假阳过滤:Box Re-Ranking_ Unsupervised False Positive Suppression for Domain Adaptive Pedestrian Detection 基于Transformer的重排:Contextual Similarity Aggregation with Self-attentionfor Visual Re-ranking 小结 本小节介绍了图像检索技术的概念、数据集、损失函数和常用的评价指标,然后介绍了检索的两大核心技术,向量检索与排序。 向量检索中最常用的是PQ+IVF,这也将在后续代码实现中进行使用以及讲解,LSH和HNSW也有自己的特点,可根据是否需要用空间换时间来决定选择。 检索任务的排序环节包括一系列的操作,有粗排、精排、重排等操作,就像一个漏斗,逐步去粗取精,每一步都是与业务场景息息相关,在图像任务中往往只需要一个重排就ok了。 本节整体介绍图像检索概念及实现技术,下一节将介绍Faiss库的使用、构建基于CLIP模型的图像检索算法、基于Flask部署图像检索系统等。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.8-image-retrieval-2.html":{"url":"chapter-8/8.8-image-retrieval-2.html","title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","keywords":"","body":"8.8 Image Retrieval 图像检索 (下) 前言 上一小节,对图像检索的基础概念进行了介绍,本节将通过代码实践,实现以文搜图、以图搜图的功能。 本节代码核心包括: Faiss 框架介绍及常用算法评估,并基于Faiss实现COCO 2017的11万数据的图像检索 CLIP实现image/text的特征提取 集成Faiss+CLIP构建无需训练的图像检索系统 基于Flask将图像检索系统部署为web服务 Faiss安装及使用 简介 Faiss是MetaAI开源的高性能向量检索库,它基于C++编写,提供python接口,核心模块还可以用GPU加速,在亿级数据可在xxx秒级实现结果返回,目前应用较为广泛。中小型项目及个人,非常适合采用Faiss。 Faiss目前最好的学习资源有两个,一个是官方wiki,一个是www.pinecone.io的Faiss: The Missing Manual faiss提供的优化算法主要分为PQ量化和倒排索引,下图为faiss中算法体系结构图,可以看到核心在蓝色区域和黄色区域。 安装 可以通过pip或者conda安装,这里推荐conda,这里需要注意镜像源最好conda源,若是清华镜像是行不通的。 # cpu版本安装 conda install -c pytorch faiss-cpu # gpu版本安装 conda install -c pytorch faiss-gpu 切换回到conda官方源方法: # 依次输入 conda config --remove-key channels conda config --add channels defaults conda config --add channels conda-forge 在这里安装了gpu版本,便于测试,在gpu版本安装中,会默认装上cudatoolkit, 600多M,总共需要900多M The following packages will be downloaded: package | build ---------------------------|----------------- ca-certificates-2022.12.7 | h5b45459_0 143 KB conda-forge cudatoolkit-11.8.0 | h09e9e62_11 638.9 MB conda-forge faiss-1.7.2 |py38cuda112h7f1466e_3_cuda 885 KB conda-forge faiss-gpu-1.7.2 | h949689a_3 15 KB conda-forge intel-openmp-2023.1.0 | h57928b3_46319 2.5 MB conda-forge libblas-3.9.0 | 16_win64_mkl 5.6 MB conda-forge libcblas-3.9.0 | 16_win64_mkl 5.6 MB conda-forge libfaiss-1.7.2 |cuda112h33bf9e0_3_cuda 51.0 MB conda-forge libfaiss-avx2-1.7.2 |cuda112h1234567_3_cuda 51.1 MB conda-forge libhwloc-2.9.1 | h51c2c0f_0 2.4 MB conda-forge libiconv-1.17 | h8ffe710_0 698 KB conda-forge liblapack-3.9.0 | 16_win64_mkl 5.6 MB conda-forge libxml2-2.10.4 | hc3477c8_0 1.7 MB conda-forge libzlib-1.2.13 | hcfcfb64_4 70 KB conda-forge mkl-2022.1.0 | h6a75c08_874 182.7 MB conda-forge numpy-1.24.2 | py38h7ec9225_0 5.6 MB conda-forge openssl-1.1.1t | hcfcfb64_0 5.0 MB conda-forge pthreads-win32-2.9.1 | hfa6e2cd_3 141 KB conda-forge python_abi-3.8 | 2_cp38 4 KB conda-forge setuptools-67.6.1 | pyhd8ed1ab_0 567 KB conda-forge tbb-2021.9.0 | h91493d7_0 151 KB conda-forge ucrt-10.0.22621.0 | h57928b3_0 1.2 MB conda-forge vs2015_runtime-14.34.31931 | h4c5c07a_10 708 KB conda-forge ------------------------------------------------------------ Total: 962.3 MB 安装验证——\"Hello Faiss\" 接下来采用faiss进行精确查找,实现10000条查询向量的top-4向量检索。 faiss使用步骤主要分三步: 创建索引器,索引器有FlatL2、LSH、PQ、HNSWFlat等 初始化索引器,将数据库向量添加到索引器中,并进行预训练(如果需要) 使用索引器进行检索。 以下为配套代码运行结果 import time import numpy as np import faiss # ============================ step 0: 数据构建 ============================ np.random.seed(1234) d = 64 # dimension nb = 100000 # database size nq = 10000 # nb of queries xb = np.random.random((nb, d)).astype('float32') xq = np.random.random((nq, d)).astype('float32') xb[:, 0] += np.arange(nb) / 1000. xq[:, 0] += np.arange(nq) / 1000. # ============================ step 1: 构建索引器 ============================ index = faiss.IndexFlatL2(d) index.add(xb) # ============================ step 2: 索引 ============================ k = 4 # top_k number for i in range(5): s = time.time() D, I = index.search(xq, k) print(\"{}*{}量级的精确检索,耗时:{:.3f}s\".format(nb, nq, time.time()-s)) # ============================ step 3: 检查索引结果 ============================ print('D.shape: {}, D[0, ...]: {}'.format(D.shape, D[0])) print('I.shape: {}, I[0, ...]: {}'.format(I.shape, I[0])) # D是查询向量与topk向量的距离,distance # I是与查询向量最近的向量的id,此处有10万数据,index在0-99999之间。 输出的D表示距离, 6.815表示第一个查询向量的top-1向量的距离是6.815 输出的I表示向量id, 381表示第一个查询向量的top-1向量的id是381 D.shape: (10000, 4), D[0, ...]: [6.815506 6.8894653 7.3956795 7.4290257] I.shape: (10000, 4), I[0, ...]: [381 207 210 477] faiss提供的方法汇总表如下: Method Class name index_factory Main parameters Bytes/vector Exhaustive Comments Exact Search for L2 IndexFlatL2 \"Flat\" d 4*d yes brute-force Exact Search for Inner Product IndexFlatIP \"Flat\" d 4*d yes also for cosine (normalize vectors beforehand) Hierarchical Navigable Small World graph exploration IndexHNSWFlat 'HNSWx,Flat` d,M 4d + x M 2 4 no Inverted file with exact post-verification IndexIVFFlat \"IVFx,Flat\" quantizer,d,nlists,metric 4*d + 8 no Takes another index to assign vectors to inverted lists. The 8 additional bytes are the vector id that needs to be stored. Locality-Sensitive Hashing (binary flat index) IndexLSH - d,nbits ceil(nbits/8) yes optimized by using random rotation instead of random projections Scalar quantizer (SQ) in flat mode IndexScalarQuantizer \"SQ8\" d d yes 4 and 6 bits per component are also implemented. Product quantizer (PQ) in flat mode IndexPQ \"PQx\",\"PQ\"x\"x\"nbits d,M,nbits ceil(M * nbit / 8) yes IVF and scalar quantizer IndexIVFScalarQuantizer \"IVFx,SQ4\" \"IVFx,SQ8\" quantizer,d,nlists,qtype SQfp16: 2 *d+ 8, SQ8:d+ 8 or SQ4:d/2+ 8 no Same as theIndexScalarQuantizer IVFADC (coarse quantizer+PQ on residuals) IndexIVFPQ \"IVFx,PQ\"y\"x\"nbits quantizer,d,nlists,M,nbits ceil(M * nbits/8)+8 no IVFADC+R (same as IVFADC with re-ranking based on codes) IndexIVFPQR \"IVFx,PQy+z\" quantizer,d,nlists,M,nbits,M_refine,nbits_refine M+M_refine+8 no faiss 基础 相似性评价指标 faiss的评价指标主要有L2 和 inner product,L2是平方后的L2,这是为了减少计算量,只是比较大小,就没必要开平方了,需要真正意义的L2,要自行开平方。 除了L2和inner product,还有METRIC_L1, METRIC_Linf and METRIC_Lp ,但不常用,需要时查看官方wiki文档。 faiss的数据预处理 faiss提供了高性能的 k-means clustering, PCA, PQ encoding/decoding,需要用时查看wiki 索引器太多,如何选? RAM充足:选HNSW,IVF1024,PQNx4fs,RFlat RAM一般:OPQ M _D ,...,PQ M x 4fsr RAM不足:OPQM _D ,...,PQM 数据量不大,且要求精确:选 IndexFlatL2 或 IndexFlatIP 数据量大:IVF K, K可以选择256,2^16=65536, 2^18=262144, 2^20=1048576,根据数据量在1M, 10M, 100M, 1B之间选择。 综上:内存问题用量化,速度问题用倒排,一个普适的方法是 IVF + PQ。 faiss预处理及后处理 预处理方法: random rotation, RandomRotationMatrix, useful to re-balance components of a vector before indexing in an IndexPQ or IndexLSH remapping of dimensions, RemapDimensionsTransform, to reduce or increase the size of a vector because the index has a preferred dimension, or to apply a random permutation on dimensions. PCA, PCAMatrix, for dimensionality reduction , OPQ rotation, OPQMatrix, OPQ applies a rotation to the input vectors to make them more amenable to PQ coding. See Optimized product quantization, Ge et al., CVPR'13 for more details. 后处理方法: re-ranking:IndexRefineFlat,基于距离的重排,利用近似检索获得的结果精度可能较低,可通过距离进行重排。 IndexShards:组合多个索引器的结果,或是多gpu并行结果的融合。 index factory faiss提供了基于字符串创建索引器的工厂函数,字符串以逗号分隔,可以一次性创建复合的索引器,包括预处理、索引、后处理等。 举例: index = index_factory(128, \"PCA80,Flat\") :为 128维 向量生成一个索引,通过 PCA 将它们减少到 80维,然后进行精确索引。 index = index_factory(128, \"OPQ16_64,IMI2x8,PQ8+16\"):输入是128维度 向量,再将 OPQ 变换应用于 64D 中的 16 个块,再使用 2x8 位的反向多索引(= 65536 个反向列表),最后使用大小为 8 的 PQ 进行量化16 字节。 索引数据的I/O 数据库的索引信息通常需要存于磁盘,再读入内存,这时候需要读取I/O方法。 write_index(index, \"large.index\"): writes the given index to file large.index index = read_index(\"large.index\"): reads a file GPU 使用 faiss提供核心索引器的gpu加速,可以将数据放到gpu上进行加速运算,使用比较方便,只需要以下三步:先获取gpu资源、创建cpu的索引器、cpu索引器搬到gpu上。 注意:PQ不支持gpu加速,IVFPQ才支持。GPU: only pq.nbits == 8 is supported res = faiss.StandardGpuResources() # 1. 获取gpu资源 index_flat = faiss.IndexFlatL2(d) # 2. 创建cpu索引器 gpu_index_flat = faiss.index_cpu_to_gpu(res, 0, index_flat) # 3. 迁移至gpu 索引速度比cpu快5-10倍,笔记本测试是快了10倍,详情可运行配套代码。 faiss还提供多gpu并行运算,多gpu使用如下代码所示 import numpy as np import faiss d = 64 # dimension nb = 100000 # database size nq = 10000 # nb of queries np.random.seed(1234) # make reproducible xb = np.random.random((nb, d)).astype('float32') xb[:, 0] += np.arange(nb) / 1000. xq = np.random.random((nq, d)).astype('float32') xq[:, 0] += np.arange(nq) / 1000. ngpus = faiss.get_num_gpus() print(\"number of GPUs:\", ngpus) cpu_index = faiss.IndexFlatL2(d) gpu_index = faiss.index_cpu_to_all_gpus(cpu_index) gpu_index.add(xb) # add vectors to the index print(gpu_index.ntotal) k = 4 # we want to see 4 nearest neighbors D, I = gpu_index.search(xq, k) # actual search print('D.shape: {}, D[0, ...]: {}'.format(D.shape, D[0])) print('I.shape: {}, I[0, ...]: {}'.format(I.shape, I[0])) 除了cpu迁移至gpu和多gpu的函数,还有gpu迁移至cpu的函数:faiss.index_gpu_to_cpu。 使用gpu需要注意: k and nprobe must be For GpuIndexIVFPQ, code sizes per encoded vector allowed are 1, 2, 3, 4, 8, 12, 16, 20, 24, 28, 32, 48, 56, 64 and 96 bytes. 其它:详见wiki cpu与gpu有相同的精度,但是会发生cpu与gpu的检索结果不一致的情况!这可能是floating point reduction order、equivalent element k-selection order、float16 opt-in导致的 faiss 代码结构设计 faiss 底层代码为CUDA、BLAS、numpy,其中CUDA+BAL是核心,构建了所有底层计算(浅绿色方框),再往上就是对python、Rust/C#、C++的代码封装,用户可直接使用python等语言调用faiss的功能。 这里主要看python部分,对于底层计算库c++/cuda,经过SWIG将numpy与底层计算库封装为python类,变为python可调用的接口;向上再经过Adaptor layer封装,以便于python对象可转换为C++对象;随后采用contrib library进行封装,提供python代码接口。 SWIG (Simplified Wrapper and Interface Generator) 是一个用于将 C/C++ 代码封装为其他编程语言接口的开源工具。 Adaptor layer 是指用 Python 编写的桥接层,其作用是将 Python 对象与 C++ 对象进行转换,通过定义 Python 类或函数的方式,将 C++ 类或函数封装为 Python 对象或函数 Python contrib library 是一个开源的 Python 库,它包含了许多常用的机器学习和深度学习模型、工具和数据集。 faiss有一大特点,可以用pytorch的tensor可直接传入索引器的.add() 和 .serarch()。 faiss进阶知识点 线程与异步:cpu是线程安全的,gpu不是线程安全的,详情参见wiki 底层API介绍:InvertedLists 和 InvertedListsScannerwiki 超大数据的存储方案:分布式存储于多机器、存储于磁盘、分布式键值存储wiki 二进制量化编码:三个核心API及使用demowiki 暴力检索的直接调用:暴力检索可以不需要索引器,直接调用函数计算就好。wiki 低比特高速索引:借鉴Google的SCANN 4-bit PQ fast scan,实现高速索引。wiki) 实战笔记系列:k-means算法、IVFPQ的预计算表、PCA矩阵计算、非穷举搜索的统计信息、重排refine的作用及参数设置。wiki 超参数自动搜索:例如nprobe,有的向量用10,有的向量用20,这里提供了SearchParameter实现自动搜索。wiki 加速优化策略:大内存页、选择适当的CPU资源、选择适当的MKL线程数等。wiki Faiss 的benchmark 接下来对常用的PQ与IVFPQ进行benchmark测试,观察不同参数下,耗时与recall的情况,为后续实际应用挑选参数做准备。 数据集下载:采用sift1M数据(http://corpus-texmex.irisa.fr/),提供100万的图像特征向量,向量长度为128,在个人电脑上可运行。 首先观察Product Quantization算法的参数,PQ中子段的数量会影响计算速度与召回,子段越多召回越高,同时量化bit越小,内存占用越小,但召回降低。 下面就观察子段分别是4,8,16,32时,量化bit分别是6,7,8,9时,耗时与召回的情况。 运行配套代码,可得到如下两张图,分别是召回的对比、耗时的对比。 通过实验结果图可知道: 16个子段大约是个分水岭,小于16个子段时,召回会快速提升。一般用16个子段、32个子段 耗时方面,8bit的耗时出现异常,这可能是由于代码针对8bit做了特殊优化,并且论文中推荐的也是8bit量化 后续使用,直接用PQ16x8或者PQ32x8就好。 PQ + IVF 在faiss中PQ通常不会单独使用,而是结合IVF,IVF中聚类中心的数量会影响速度和召回,检索时probe的数量也会影响速度和召回。 由于PQ参数已经选好了,这里采用PQ32x8,IVF的聚类中心分别有128,256,512,1024,2048,4096;probe分别有1, 2, 4, 8, 16, 32, 64。 运行配套代码,可得到如下两张图,分别是召回的对比、耗时的对比。 通过实验结果图可知道: 相同probe时,聚类中心越少,召回越高。可能是数据集的问题,聚类中心的差异没有很明显,需要把128和4096进行对比,精度差别不大。 聚类中心越多,耗时越少,因为一个类里边的向量个数少了,需要匹配的向量就少了,速度就快 probe数量增大,耗时增加,在32之前增加较小,因此32是个不错的选择,当精度不足时可适当增加到48。PQ的wiki中也提到IVFPQ的对比,probe=48时,可以得到与PQ相同水平的recall,并且耗时大幅度较少。 聚类中心选4096,nprobe选48,下调最多到36,否则精度降低,并且速度没啥提升。 经过两个实验,可以得到一个通用的检索器,\"IVF4096,PQ32x8\",并且推理检索时,设置index.nprobe=48。 更多benchmark及应用案例可以看wiki中的Case-studies, benchs 基于CLIP+Faiss+Flask的图像检索系统 接下来,采用CLIP模型充当特征提取器,采用Faiss实现向量检索,采用Flask进行web服务部署,最终可实现以图搜图和以文搜图。 回顾图像检索简介中介绍的,一个图像检索系统如下图所示, 在构建图像检索系统时,需要关注: 特征提取器:采用CLIP(Contrastive Language-Image Pre-training)模型进行图像特征提取,是一个文图预训练模型,CLIP于2021年2月由openAI发表,并开源了模型,模型由4亿的图文数据,采用对比学习方式进行训练得到,由于对比学习与超大规模的数据集加持,使CLIP模型很好的理解了自然图像,在众多数据集上表现出了优异的zero-shot性能,同时在表征学习(representation learning)中也很好。CLIP简介可以回顾图像描述,上一节采用了CLIP进行图像描述。 向量检索:基于faiss的IVF+PQ方法 推荐策略:目前缺少业务背景及需求,这里不涉及。 代码结构 整套代码位于:pytorch-tutorial-2nd\\code\\chapter-8\\08_image_retrieval,代码结构如下: ├── config ├── data ├── flask_app.py ├── image_feature_extract.py ├── my_utils ├── retrieval_by_faiss.py ├── static └── templates 需要运行的为三份.py脚本分别是 image_feature_extract.py:特征向量库构建,基于CLIP将数据图片进行特征提取,并存储为特征向量形式 retrieval_by_faiss.py:基于faiss+clip的图像检索算法实现,作为被调用的代码块 flask_app.py:web服务部署,将调用retrieval_by_faiss中的ImageRetrievalModule实现检索 其它文件夹功能如下: config:整个项目的配置文件,需要设置图片数据根目录、faiss检索算法超参、相似topk等 data:存放clip模型提取的特征,即特征数据库。该路径可以在配置文件中修改 my_utils:功能函数 static:flask框架静态文件的目录,很重要的一点是需要在里面构建软链接,让flask能访问到coco2017的图片,否则无法在web前端页面展示 templates:flask的模板目录,存放html文件,前端页面设计就在index.htm中 整个图像检索运行逻辑如下图所示: 准备阶段,用clip对数据库进行数据编码,得到图像特征字典库,将特征向量传递给faiss,用于构建检索器;图片id及路径关系用于id到路径的映射 推理阶段,图片或文本经clip编码,输入faiss进行向量检索,得到向量id,再由{id:path}字典,获得图片路径,最后进行可视化。 PS:这里有一个需要注意的是,文本特征向量与图像特征向量进行匹配时,是需要进行norm操作的!在这里被坑了一天,最好对照论文才发现少了norm这个步骤。 代码UML设计简图如下: 将特征提取与向量检索分别设计一个类来实现,最后将它们这到图像检索模块中,对外提供图像检索服务。 代码使用 第一步,配置路径:修改config文件,设置图片文件路径等信息 第二步,进行特征提取:运行image_feature_extract.py,进行特征提取,在data文件夹下获得两个pkl文件 第三步,启动web服务,运行flask_app.py,可到http://localhost:5000/,使用图像检索系统 图像检索系统web使用说明 PS:由于前端知识的欠缺,有意美化该界面的,欢迎提issue! 小结 本小节通过代码介绍了faiss库的基础使用,并对常见的PQ, IVF+PQ的性能进行了评测,对未来使用向量检索框架做准备; 同时还涉及了基于CLIP+Faiss+Flask的图像检索系统部署,其中使用了优秀的多模态模型——CLIP进行特征提取,通过这一套系统可以巩固图像检索系统构建时的几个要点:预处理建特征向量库;检索器初始化;在线检索服务。 图像检索领域涵盖的知识点太多了,本案例仅能作为入门教程,各细分方向则需要大家自行修炼,祝好! Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/":{"url":"chapter-9/","title":"第九章 自然语言处理项目案例","keywords":"","body":"第九章 自然语言处理项目案例 第九章 自然语言处理项目案例 9.1 自然语言处理简介 9.2 文本分类-RNN-LSTM 9.3 机器翻译-Seq2Seq 9.4 机器翻译-Transformer 9.5 命名实体识别-BERT 9.6 文章续写-问答对话-GPT 第九章简介 本章介绍NLP中常用的模型及任务实现,模型主要包括RNN、LSTM、Transformer、BERT和GPT,任务方面包括情感分析、文本分类、机器翻译、命名体识别、QA问答、文章续写。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.1-nlp_introduction.html":{"url":"chapter-9/9.1-nlp_introduction.html","title":"9.1 自然语言处理简介","keywords":"","body":"9.1 NLP基础概念 NLP任务不同于CV任务,它存在更多难点,例如 数据表示:NLP任务处理的是文本数据,需要将自然语言文本转化为计算机可处理的形式,即如何把字符串变为数值数据,常用的有词嵌入(Word Embedding),而CV任务处理的图像,天然是数值形数据 上下文依赖:文本具有丰富的上下文依赖性,单个词或短语的含义可以依赖于其周围的上下文,这使得NLP任务在理解和处理上更具挑战性。 长期依赖:文本序列可能非常长,并且存在长期依赖性,需要模型能够捕捉长距离的上下文信息 多义性和歧义性:语言中存在多义词和歧义性,需要根据上下文来进行准确理解和推断。 由于NLP任务存在诸多难点,因此在正式开始前,需要对NLP基础概念进行说明。 首先介绍NLP数据处理基础流程,然后介绍各式各样的NLP任务,最后总结NLP基础概念名词 NLP任务处理流程 以电影影评文本分类为例,流程可分为以下几个步骤: 数据清洗和预处理:去除无关字符、标点符号、HTML标签、去除停用词等,目的是得到具备真实语义的文本序列。 分词:将文本序列划分为“具备独立语义“的词元(token),每个token是基本单位。英文中通常用空格可区分,而中文常需要特定的分词方法。例如,“这电影不错。”可划分为[\"这\", \"电影\", \"不\", \"错\", \"。\"],或者将每个字符划分,[\"这\", \"电\", \"影\", \"不\", \"错\", \"。\"]。具体要如何分词,这需要根据任务以及模型的能力决定,对于BERT这样的模型,或许以及学会了分词、组词的能力,只需要逐个字输入即可。 构建词表:词表(Vocabulary)是个字典,作用是将词元(token)映射到整数索引,例如[('', 0), ('the', 1), ('i', 2), ('and', 3)...]。词表将文本数据中的词元(token)映射为对应的标识符(通常是正整数),然后使用词嵌入模型将这些标识符转化为对应的词向量表示。 词嵌入:模型运算需要的是数值型数据,因此需要一个映射,将字符映射到向量,这个向量可理解为特征向量/词向量,这个映射过程称为词嵌入(word embedding)。词嵌入是个复杂问题,需要的约束较多。例如,语义相近的词元(token)的特征向量要接近,即余弦距离要小。目前常采用已经预训练好的词嵌入模型,如word2vec, GloVe, FastText等。 算法模型:NLP模型通常是序列模型,即可处理多个词向量输入,然后根据任务类型进行输出,如文本分类则最终输出一个分类概率向量,翻译任务则输出一些列分类概率向量,向量个数是文本单词/词元个数,概率向量的长度是词表大小,即把单词的输出转换为单词分类,只能在词表中选一个最有可能词进行输出。 NLP常见任务 与CV任务不同,NLP任务繁多,需要归纳总结,逐个分析。 下面借助台大李宏毅老师2021年NLP课程的内容,梳理NLP主要内容。 详细内容参见:http://speech.ee.ntu.edu.tw/~tlkagk/courses_DLHLP20.html NLP任务细分有十多种大类,小类有几十种,但整体归纳下来可概括为两种类型,分别是seq2cls和seq2seq。 seq2cls:输入是序列,输出是类别,根据输出数量又可分序列级分类和token级分类。文本分类就是序列级,词性标注就是token级。 seq2seq:输入是序列,输出也序列,例如机器翻译、文章摘要、问答系统、阅读理解等任务都是seq2seq的。 下面对常见的NLP任务进行一句话概括。 词性标记,Part-of-Speech(POS) Tagging:seq2cls, 为每个token输出一个类别,在早期NLP模型中,作为预处理,将词性和文本输入到下游模型中,期望通过词性信息提升模型能力。 分词,word segmentation:seq2cls,将文本序列切分开,英文有空格进行切分,中文则需要分词,文本序列切分为多个token。 指代消解,coreference resolution:将同一个东西(entity)的不同表达识别出来,给下游任务额外的提示信息。 情感分析,sentiment classification:seq2cls,将整个文本进行输出一个类别,常用于影评、社交媒体评论的分类。 舆情分析(立场检测),Stance Detection:seq+seq 2 cls,常分为4类:Many systems use the Support, Denying, Querying, andCommenting (SDOC) labels for classifying replies。 机器翻译, machine translation:seq2seq,很好理解,两种语言之间的转换,典型的seq2seq任务。 文本摘要,summarization:抽取式(Extraction-based),seq2cls。为原文中逐个token进行二分类,保留/不保留,最终输出保留的token(单词/句子)。 文本摘要,summarization:生成式(Abstraction-based),seq2seq。将原文进行理解、编码,再经解码器输出总结的内容。类似机器翻译,输出的是NxM的概率向量矩阵。N是token数量,M是词表中词的数量。 问答系统:seq2seq,输入问题,输出答案,进阶版问答系统还具备“记忆”能力,能根据上下文进行回答,这就变为对话系统。 命名实体识别,NER(Name Entity Recognition):seq2cls,将文本中的实体(如人名、地名、组织名)进行标注和分类,将每个token分类为实体类型或非实体类型。 关系抽取,Relation Extraction:seq2seq,旨在从文本中提取出实体之间的关系。输入是包含两个实体的句子以及两个实体,输出是描述这两个实体关系的标签或文本。 自然语言推理 Natural Language Inference (NLI):seq2cls,推理模型的文本输入:premise(前提) + hypothesis(假设) ,模型输出:对假设是否成立的判断结果,矛盾/包含(可推得)/中立(contradiction/entailment/neutral) NLP基础概念 以上是对一个样本推理过程涉及的几个关键步骤进行了介绍,在训练过程中组batch可能还会涉及连接词、填充词的预处理,这里暂不作说明。 下面总结一些NLP中常用的概念名词,便于理解任务。 词表(Vocabulary):文本数据集中出现的所有单词的集合。 语料库(Corpus):用于NLP任务的文本数据集合,可以是大规模的书籍、文章、网页等。 词嵌入(Word Embedding):将单词映射到低维连续向量空间的技术,用于捕捉单词的语义和语法信息。 停用词(Stop Words):在文本处理中被忽略的常见单词,如\"a\"、\"the\"、\"is\"等,它们通常对文本的意义贡献较小。 分词(Tokenization):将文本分割成一个个单词或标记的过程,为后续处理提供基本的单位。 词频(Term Frequency):在给定文档中,某个单词出现的次数。 逆文档频率(Inverse Document Frequency):用于衡量一个单词在整个语料库中的重要性,是将词频取倒数并取对数的值。 TF-IDF(Term Frequency-Inverse Document Frequency):一种常用的文本特征表示方法,综合考虑了词频和逆文档频率。 词袋模型(Bag of Words):将文本表示为一个单词的集合,忽略了单词的顺序和语法结构。 N-gram:连续的N个单词构成的序列,用于捕捉文本中的局部特征和上下文信息。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.2-rnn-lstm.html":{"url":"chapter-9/9.2-rnn-lstm.html","title":"9.2 文本分类-RNN-LSTM","keywords":"","body":"9.2 文本分类-RNN-LSTM 前言 本节介绍RNN和LSTM,并采用它们在电影评论数据集上实现文本分类,本小节会涉及以下几个知识点。 词表构建:包括数据清洗,词频统计,词频截断,词表构建 预训练词向量应用:下载并加载Glove的预训练embedding进行训练,主要是如何把词向量放到nn.embedding层中的权重。 RNN及LSTM构建:涉及nn.RNN和nn.LSTM的使用 任务介绍 本节采用的数据集是斯坦福大学的大型电影评论数据集(large movie review dataset) https://ai.stanford.edu/~amaas/data/sentiment/ 包含25000个训练样本,25000个测试样本,下载解压后得到aclImdb文件夹,aclImdb下有train和test,neg和pos下分别有txt文件,txt中为电影评论文本。 来看看一条具体的样本,train/pos/3_10.txt: \"All the world's a stage and its people actors in it\"--or something like that. Who the hell said that theatre stopped at the orchestra pit--or even at the theatre door? Why is not the audience participants in the theatrical experience, including the story itself?This film was a grand experiment that said: \"Hey! the story is you and it needs more than your attention, it needs your active participation\". \"Sometimes we bring the story to you, sometimes you have to go to the story.\"Alas no one listened, but that does not mean it should not have been said. 本节任务就是对这样的一条文本进行处理,输出积极/消极的二分类概率向量。 数据模块 文本任务与图像任务不同,输入不再是像素这样的数值,而是字符串,因此需要将字符串转为矩阵运算可接受的向量形式。 为此需要在数据处理模块完成以下步骤: 分词:将一长串文本切分为一个个独立语义的词,英文可用空格来切分。 词嵌入:词嵌入通常分两步。首先将词字符串转为索引序号,然后索引序号根据词嵌入矩阵(embedding层)取对应的向量。其中词与索引之间的映射关系需要提前构建,这就是词表构建的过程。 因此,代码开发整体流程: 编写分词功能函数 构建词表:对训练数据进行分词,统计词频,并构建词表。例如{'UNK': 0, 'PAD': 1, 'the': 2, '.': 3, 'and': 4, 'a': 5, 'of': 6, 'to': 7, ...} 编写PyTorch的Dataset,实现分词、词转序号、长度填充/截断 序号转词向量的过程由模型的nn.Embedding层实现,因此数据模块只需将词变为索引序号即可,接下来一一解析各环节核心功能代码实现。 词表构建 参考配套代码a_gen_vocabulary.py,首先编写分词功能函数,分词前做一些简单的数据清洗,例如在标点符号前加入空格、去除掉不是大小写字母及 .!? 符号的数据。 def text_split(content: str) -> List[str]: content = re.sub(r\"([.!?])\", r\" \\1\", content) # 在 .!? 之前添加一个空格 content = re.sub(r\"[^a-zA-Z.!?]+\", r\" \", content) # 去除掉不是大小写字母及 .!? 符号的数据 token = [i.strip().lower() for i in content.split()] # 全部转换为小写,然后去除两边空格,将字符串转换成list, return token 接着,写一个词表统计类实现词频统计,和词表字典的创建,代码注释非常详细,这里不赘述。 运行代码,即可完成词频统计,词表的构建,并保存到本地npy文件,在训练及推理过程中使用。 class Vocabulary: UNK_TAG = \"UNK\" # 遇到未知字符,用UNK表示 PAD_TAG = \"PAD\" # 用PAD补全句子长度 UNK = 0 # UNK字符对应的数字 PAD = 1 # PAD字符对应的数字 def __init__(self): self.inverse_vocab = None self.vocabulary = {self.UNK_TAG: self.UNK, self.PAD_TAG: self.PAD} self.count = {} # 统计词频、 def fit(self, sentence_: List[str]): def build_vocab(self, min=0, max=None, max_vocab_size=None) -> Tuple[dict, dict] 在词表构建过程中有一个截断数量的超参数需要设置,这里设置为20000,即最多有20000个词的表示,不在字典中的词被归为UNK这个词。 在这个数据集中,原始词表长度为74952,即通过split切分后,有7万多个不一样的字符串,通常可以通过降序排列,取前面一部分即可。 代码会输出词频统计图,也可以观察出词频下降的速度以及高频词是哪些。 Dataset编写 参考配套代码aclImdb_dataset.py,getitem中主要做两件事,首先获取label,然后获取文本预处理后的列表,列表中元素是词所对应的索引序号。 def __getitem__(self, item): # 读取文件路径 file_path = self.total_file_path[item] # 获取 label label = 0 if os.path.basename(os.path.dirname(file_path)) == \"neg\" else 1 # neg -> 0; pos -> 1 # tokenize & encode to index token_list = text_split(open(file_path, encoding='utf-8').read()) # 切分 token_idx_list = self.word2index.encode(token_list, self.vocab, self.max_len) return np.array(token_idx_list), label 在self.word2index.encode中需要注意设置文本最大长度self.max_len,这是由于需要将所有文本处理到相同长度,长度不足的用词填充,长度超出则截断。 模型模块——RNN 模型的构建相对简单,理论知识在这里不介绍,需要了解和温习的推荐看看《动手学》。这里借助动手学的RNN图片讲解代码的实现。 在构建的模型RNNTextClassifier中,需要三个子module,分别是: nn.Embedding:将词序号变为词向量,用于后续矩阵运算 nn.RNN:循环神经网络的实现 nn.Linear:最终分类输出层的实现 在forward时,流程如下: 获取词向量 构建初始化隐藏层,默认为全0 rnn推理获得输出层和隐藏层 fc层输出分类概率:fc层的输入是rnn最后一个隐藏层 def forward(self, x): x_embed = self.embedding(x) # [batch_size, max_len] -> [batch_size, text_len, embed_len] bs_, text_len, embed_len = x_embed.shape hidden_init = self.init_hidden(bs_) outputs, hidden = self.rnn(x_embed, hidden_init) # Extract the last hidden state last_hidden = hidden[-1].squeeze(0) # [num_layers, bs, hidden_size] -> [bs, hidden_size] fc_output = self.fc(last_hidden) return fc_output 更多关于nn.RNN的参数设置,可以参考官方文档:torch.nn.RNN(self, input_size, hidden_size, num_layers=1, nonlinearity='tanh', bias=True, batch_first=False, dropout=0.0, bidirectional=False, device=None, dtype=None) 模型模块——LSTM RNN是神经网络中处理时序任务最为经典的设计,但是其也存在一些缺点,例如梯度消失和梯度爆炸,以及长期依赖问题。 当序列很长时,RNN模型很难捕捉到远距离的依赖关系,导致模型预测不准确。 为此,带门控机制的RNN涌现,包括GRU(Gated Recurrent Unit,门控循环单元)和LSTM(Long Short-Term Memory,长短期记忆网络),其中LSTM应用最广,这里直接跳过GRU。 LSTM模型引入了三个门(input gate、forget gate和output gate),用于控制输入、输出和遗忘的流动,允许模型有选择性地忘记或记住一些信息。 input gate用于控制输入的流动 forget gate用于控制遗忘的流动 output gate用于控制输出的流动 相较于RNN,除了输出隐藏层向量h,还输出记忆层向量c,不过对于下游使用,不需要关心向量c的存在。 同样地,借助《动手学》中的LSTM示意图来理解代码。 在这里,借鉴《动手学》的代码,采用的LSTM为双向LSTM,这里简单介绍双向循环神经网络的概念。 双向循环神经网络(Bidirectional Recurrent Neural Network,Bi-RNN)同时考虑前向和后向的上下文信息,前向层和后向层的输出在每个时间步骤上都被连接起来,形成了一个综合的输出,这样可以更好地捕捉序列中的上下文信息。 在pytorch代码中,只需要将bidirectional设置为True即可,nn.LSTM(embed_size, num_hiddens, num_layers=num_layers, bidirectional=True)。 当采用双向时,需要注意output矩阵的shape为 [ sequence length , batch size ,2×hidden size] 更多关于nn.LSTM的参数设置,可以参考官方文档:torch.nn.LSTM(self, input_size, hidden_size, num_layers=1, bias=True, batch_first=False, dropout=0.0, bidirectional=False, proj_size=0, device=None, dtype=None) 详细参考:https://pytorch.org/docs/stable/generated/torch.nn.LSTM.html#torch.nn.LSTM embedding预训练加载 模型构建好之后,词向量的embedding层是随机初始化的,要从头训练具备一定逻辑关系的词向量表示是费时费力的,通常可以采用在大规模预料上训练好的词向量矩阵。 这里可以参考斯坦福大学的GloVe(Global Vectors for Word Representation)预训练词向量。 GloVe是一种无监督学习算法,用于获取单词的向量表示,GloVe预训练词向量可以有效地捕捉单词之间的语义关系,被广泛应用于自然语言处理领域的各种任务,例如文本分类、命名实体识别和机器翻译等。 Glove有四大类,根据数据量不同进行区分,相同数据下又根据向量长度分 Wikipedia 2014 + Gigaword 5 (6B tokens, 400K vocab, uncased, 50d, 100d, 200d, & 300d vectors, 822 MB download): glove.6B.zip Common Crawl (42B tokens, 1.9M vocab, uncased, 300d vectors, 1.75 GB download): glove.42B.300d.zip Common Crawl (840B tokens, 2.2M vocab, cased, 300d vectors, 2.03 GB download): glove.840B.300d.zip Twitter (2B tweets, 27B tokens, 1.2M vocab, uncased, 25d, 50d, 100d, & 200d vectors, 1.42 GB download): glove.twitter.27B.zip 在这里,采用Wikipedia 2014 + Gigaword 5 中的100d,即词向量长度为100,向量的token数量有6B。 下载好的GloVe词向量矩阵是一个txt文件,一行是一个词和词向量,中间用空格隔开,因此加载该预训练词向量矩阵可以这样。 def load_glove_vectors(glove_file_path, word2idx): \"\"\" 加载预训练词向量权重 :param glove_file_path: :param word2idx: :return: \"\"\" with open(glove_file_path, 'r', encoding='utf-8') as f: vectors = {} for line in f: split = line.split() word = split[0] vector = torch.FloatTensor([float(num) for num in split[1:]]) vectors[word] = vector return vectors 原始GloVe预训练词向量有40万个词,在这里只关心词表中有的词,因此可以在加载字典时加一行过滤,即在词表中的词,才去获取它的词向量。 if word in word2idx: vector = torch.FloatTensor([float(num) for num in split[1:]]) vectors[word] = vector 在本案例中,词表大小是2万,根据匹配,只有19720个词在GloVe中找到了词向量,其余的词向量就需要随机初始化。 获取GloVe预训练词向量字典后,需要把词向量放到embedding层中的矩阵,对弈embedding层来说,一行是一个词的词向量,因此通过词表的序号找到对应的行,然后把预训练词向量放进去即可,代码如下: word2idx = np.load(vocab_path, allow_pickle=True).item() # 词表顺序仍旧根据训练集统计得到的词表顺序 for word, idx in word2idx.items(): if word in glove_vectors: model.embedding.weight.data[idx] = glove_vectors[word] if args.is_freeze: model.embedding.weight.requires_grad = False # embedding层是否还需要更新 训练及实验记录 准备好了数据和模型,接下来按照常规模型训练即可。 这里将会做一些对比实验,包括模型对比: RNN vs LSTM 有预训练词向量 vs 无预训练词向量 冻结预训练词向量 vs 放开预训练词向量 具体指令如下,推荐放到bash文件中,一次性跑 python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode rnn --glove-file-path \"\" python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode rnn --glove-file-path /workspace/data/glove_6B/glove.6B.100d.txt --is-freeze python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode rnn --glove-file-path /workspace/data/glove_6B/glove.6B.100d.txt python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode lstm --glove-file-path \"\" python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode lstm --glove-file-path /workspace/data/glove_6B/glove.6B.100d.txt --is-freeze python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode lstm --glove-file-path /workspace/data/glove_6B/glove.6B.100d.txt 实验结果如下所示: RNN整体不work,经过分析发现设置的文本token长度太长,导致RNN梯度消失,以至于无法训练。调整text_max_len为50后,train acc=0.8+, val=0.62,整体效果较差。 有了预训练词向量要比没有预训练词向量高出10多个点 放开词向量训练,效果会好一些,但是不明显 RNN(text_len=500) LSTM RNN(text_len=50) random init embedding 0.50 0.53, 0.70 0.58 FREEZE-glove 6B 100d 0.50 0.85 0.67 TRAIN-glove 6B 100d 0.50 0.88 0.67 补充实验:将RNN模型的文本最长token数量设置为50,其余保持不变,得到的三种embedding方式的结果如下: 结论: LSTM较RNN在长文本处理上效果更好 预训练词向量在小样本数据集上很关键,有10多个点的提升 放开与冻结embedding层训练,效果差不多 小结 本小节通过电影影评数据集实现文本分类任务,通过该任务可以了解: 文本预处理机制:包括清洗、分词、词频统计、词表构建、词表截断、UNK与PAD特殊词设定等 预训练词向量使用:包括GloVe的下载及加载、nn.embedding层的设置 RNN系列网络模型使用:大致了解循环神经网络的输入/输出是如何构建,如何配合fc层实现文本分类 RNN可接收的文本长度有限:文本过长,导致梯度消失,文本过短,导致无法捕获更多文本信息,因此推荐采用LSTM等门控机制的模型 这一小节是经典的seq2cls的任务,下一小节,将对seq2seq进行介绍。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.3-seq2seq.html":{"url":"chapter-9/9.3-seq2seq.html","title":"9.3 机器翻译-seq2seq","keywords":"","body":"9.3 机器翻译-Seq2Seq 前言 上一节通过影评数据分类任务介绍了seq2cls的场景,本节介绍seq2seq的任务——机器翻译。 机器翻译是典型的seq2seq任务,本节将采用传统基于RNN的seq2seq结构模型进行,seq2seq任务有一些特点,并且需要重点理解,包括 特殊token:, , , 。分别表示起始、结束、未知和填充。尤其是“起始”和\"结束\",它们是序列生成任务中重要的概念。 序列最大长度:序列最大长度直接影响模型性能,过长导致模型难以学习,过短导致无法完成任务,本案例选择长度为20。 Teacher forcing 教师强制学习:本概念也是序列生成任务中,在训练阶段所涉及的重要概念,表示decoder的输入采用标签,而非自回归式。 推理代码逻辑:模型推理输出序列时,采用的是自回归方式,因而代码与训练时要做修改。 任务介绍 机器翻译是经典的seq2seq任务,日常生活中也常用到翻译工具,因此任务比较好理解。例如以下几个句子就是本节要处理的任务。 That mountain is easy to climb. 那座山很容易爬。 It's too soon. 太早了。 Japan is smaller than Canada. 日本比加拿大小。 现在需要构建模型,接收英文句子序列,进行综合理解,然后输出中文句子序列,在神经网络中,通常采用encoder-decoder架构,例如《动手学》中的示意图。 编码器,对输入进行处理,获得对输入句子的综合理解信息——状态。 解码器,根据状态,以及解码器的输入,逐个token的生成输出,直到输出特殊token——,模型停止输出。 解码器的输入,采用自回归方式(推理时),含义是当前时刻的输入,来自上一时刻的输出,特别地,第0个时刻,是没有上一个时刻,所以采用特殊token——作为输入。 数据模块 数据下载 本案例数据集Tatoeba下载自https://www.manythings.org/anki/,该项目是帮助不同语言的人学习英语,因此是英语与其它几十种语言的翻译文本。 其中就包括本案例使用的英中文本,共计29668条(Mandarin Chinese - English cmn-eng.zip (29668)) 数据以txt形式存储,一行是一对翻译文本,例如长这样: 1.That mountain is easy to climb. 那座山很容易爬。 2.It's too soon. 太早了。 3.Japan is smaller than Canada. 日本比加拿大小。 数据集划分 对于29668条数据进行8:2划分为训练、验证,这里采用配套代码a_data_split.py进行划分,即可在统计目录下获得train.txt和text.txt。 词表构建 文本任务首要任务是为文本构建词表,这里采用与上节一样的方法,首先对文本进行分词,然后统计语料库中所有的词,最后根据最大上限、最小词频等约束,构建词表。本部分配套代码是b_gen_vocabulary.py 词表的构建过程中,涉及两个知识点:中文分词和特殊token。 1. 中文分词 对于英文,分词可以直接采用空格。而对于中文,就需要用特定的分词方法,这里采用的是jieba分词工具,以下是英文和中文的分词代码。 source.append(parts[0].split(' ')) target.append(list(jieba.cut(parts[1]))) # 分词 2. 特殊token 由于seq2seq任务的特殊性,在解码器部分,通常需要一个token告诉模型,现在是开始,同时还需要有个token让模型输出,以此告诉人类,模型输出完毕,不要再继续生成了。 因此相较于文本分类,还多了,, 两个特殊token,有的时候,开始token也会用表示。 PAD_TAG = \"\" # 用PAD补全句子长度 BOS_TAG = \"\" # 用BOS表示开始 EOS_TAG = \"\" # 用EOS表示结束 UNK_TAG = \"\" # 用EOS表示结束 PAD = 0 # PAD字符对应的数字 BOS = 1 # BOS字符对应的数字 EOS = 2 # EOS字符对应的数字 UNK = 3 # UNK字符对应的数字 运行代码后,词表字典保存到了result目录下,并得到如下输出,表明英文中有2518个词,中文有3365,但经过最大长度3000的截断后,只剩下2996,另外4个是特殊token。 100%|██████████| 23635/23635 [00:00 Dataset编写 NMTDataset的编写逻辑与上一小节的Dataset类似,首先在类初始化的时候加载原始数据,并进行分词;在getitem迭代时,再进行token转index操作,这里会涉及增加结束符、填充符、未知符。 核心代码如下: def __init__(self, path_txt, vocab_path_en, vocab_path_fra, max_len=32): self.path_txt = path_txt self.vocab_path_en = vocab_path_en self.vocab_path_fra = vocab_path_fra self.max_len = max_len self.word2index = WordToIndex() self._init_vocab() self._get_file_info() def __getitem__(self, item): # 获取切分好的句子list,一个元素是一个词 sentence_src, sentence_trg = self.source_list[item], self.target_list[item] # 进行填充, 增加结束符,索引转换 token_idx_src = self.word2index.encode(sentence_src, self.vocab_en, self.max_len) token_idx_trg = self.word2index.encode(sentence_trg, self.vocab_fra, self.max_len) str_len, trg_len = len(sentence_src) + 1, len(sentence_trg) + 1 # 有效长度, +1是填充的结束符 . return np.array(token_idx_src, dtype=np.int64), str_len, np.array(token_idx_trg, dtype=np.int64), trg_len def _get_file_info(self): text_raw = read_data_nmt(self.path_txt) text_clean = text_preprocess(text_raw) self.source_list, self.target_list = text_split(text_clean) 模型模块 seq2seq模型,由编码器和解码器两部分构成。 对于编码器,需要的是其对输入句子的全文理解,因此可采用RNN中输出的hidden state特征来表示,在这里均采用LSTM作为基础模型。 对于解码器,同样是一个LSTM,它接收3个数据,一个是输入,另外两个是hidden state和cell state。解码器的输入就是自回归式的,上一时刻输出的单词传到当前时刻。 这里借助《动手学》的示意图,理解seq2seq模型的样子。 首先构建一个EncoderLSTM,这个比较简单,只看它的forward,对于输入的句子x,输出hidden_state, cell_state。 def forward(self, x): # Shape -----------> (26, 32, 300) [Sequence_length , batch_size , embedding dims] embedding = self.dropout(self.embedding(x)) # Shape --> outputs (26, 32, 1024) [Sequence_length , batch_size , hidden_size] # Shape --> (hs, cs) (2, 32, 1024) , (2, 32, 1024) [num_layers, batch_size, hidden_size] outputs, (hidden_state, cell_state) = self.LSTM(embedding) return hidden_state, cell_state 然后构建一个DecoderLSTM,解码器除了需要对数据进行提特征,获得hidden_state, cell_state,还需要进行当前时刻,单词的输出,即token级的分类任务。 所以,它的forward返回有三个信息,包括输出的token预测向量,LSTM的hidden_state, cell_state,这里需要注意,在代码实现时,解码器输出的隐状态默认包括了来自编码器的,因此后续时间步不再需要从编码器拿隐状态特征了。更直观的就是,解码器返回的hidden_state, cell_state,会是下一次forward输入的hidden_state, cell_state。 def forward(self, x, hidden_state, cell_state): x = x.unsqueeze(0) # x.shape == [1, batch_size] embedding = self.dropout(self.embedding(x)) outputs, (hidden_state, cell_state) = self.LSTM(embedding, (hidden_state, cell_state)) predictions = self.fc(outputs) predictions = predictions.squeeze(0) return predictions, hidden_state, cell_state 最后,编写一个Seq2Seq类,将编码器和解码器有序的组合起来,在这里,核心任务是解码器中,如何有序的进行每个时刻的数据处理。 该Seq2Seq类用于训练阶段,会设计teacher forcing(强制学习)的概念,它表示在训练阶段,解码器的输入时自回归,还是根据标签进行输入,采用标签进行输入的方法称为teacher forcing。 这里仔细研究一下forward的后半部分——解码器部分,前半部分就是编码器进行一次性的编码,获得隐状态特征。 对于解码器,采用for循环,依次进行token输出,最终存储在outputs中,for循环需要设置最大步数,一般根据任务的数据长度而定,这里设置为32。 接着,解码器工作,解码器会输出:预测的token分类向量,隐状态信息,其中隐状态信息会在下一个for循环时,输入到解码器中。 再往下看,解码器的输入x,则是根据一个条件判断,以一定的概率采用标签,一定的概率采用自回归方式。这里概率通常设置为0.5,如果设置为1,表明采用强制学习,永远采用标签作为输入,也就是强制学习机制(teacher forcing)。 # Shape of x (32 elements) x = target[0] # token for i in range(1, target_len): # output.shape == (bs, vocab_length) # hidden_state.shape == [num_layers, batch_size size, hidden_size] output, hidden_state, cell_state = self.Decoder_LSTM(x, hidden_state, cell_state) outputs[i] = output best_guess = output.argmax(1) # 0th dimension is batch size, 1st dimension is word embedding x = target[i] if random.random() outputs (14, 32, 5766) return outputs 模型训练 数据和模型准备好之后,可以运行train_seq2seq.py进行训练,seq2seq的训练还有些许不同,主要包括: 采用blue进行指标评价; 损失函数加入ignore_index BLEU是IBM在2002提出的,用于机器翻译任务的评价,发表在ACL,引用次数10000+,原文题目是“BLEU: a Method for Automatic Evaluation of Machine Translation”。 它的总体思想就是准确率,假如给定标准译文reference,模型生成的句子是candidate,句子长度为n,candidate中有m个单词出现在reference,m/n就是bleu的1-gram的计算公式。 当统计不再是一个单词,而是连续的N个单词时,就有了n-gram的概念,词组的概念称为n-gram,词组长度通常选择1, 2, 3, 4 举一个例子来看看实际的计算: candinate: the cat sat on the mat reference: the cat is on the mat BLEU-1: 5/6 = 0.83 BLEU-2: 3/5 = 0.6 BLEU-3: 1/4 = 0.25 BLEU-4: 0/3 = 0 分子表示candidate中预测到了的词组的次数,如BLEU-1中,5分别表示, the, cat, on, the, mat预测中了。BLEU-2中,3分别表示, the cat, on the, the mat预测中了。以此类推。 针对BLEU还有些改进计算方法,可参考BLEU详解 由于句子长度不一致,而训练又需要将数据构造成统一长度的句子来组batch,因此会加入很多特殊token——,对应的index是0,所以会在计算损失函数的时候,这部分的loss是不计算的,可以巧妙的通过设置ignore_index来实现。 nn.CrossEntropyLoss(ignore_index=0) 由于数据量少,以及模型参数未精心设计,模型存在过拟合,这里仅作为seq2seq任务的学习和理解,不对模型性能做进一步提升,因为后续有更强大的解决方案——基于Transformer。 模型推理 运行配套代码c_inference.py,可以看到模型的表现如下所示,整体上像一个模型翻译的样子,但效果远不如意,这里包含多方面原因。 数据量过少,训练数据仅2万多条,而中英文的词表就3000多 模型过于简单,传统RNN构建的seq2seq在上下文理解能力上表现欠佳,后续可加入注意力机制,或是基于Transformer架构来提升。 输入: he didn't answer the phone , so i sent him an email . 标签:他 没有 , 所以 我 给 他 发 了 。 翻译:我 不 , 所以 我 他 他 他 。 输入: just as he was going out , there was a great earthquake . 标签:就 在 他 要 出門 的 時候 , 發生 了 。 翻译:他 他 的 , 但 他 。 输入: tom hugged mary . 标签:汤姆 拥抱 了 玛丽 。 翻译: 了 玛丽 。 输入: there are many americans who can speak japanese . 标签:有 很多 美国 说 日语 。 翻译:有 能 非常 。 输入: i'm not good at . 标签:我 不 擅長 。 翻译:我 不 太 擅长 运动 。 小结 本节通过机器翻译了解seq2seq任务,主要涉及一些特殊的、新的知识点,包括: 特殊token:解码器需要两个特殊的token,起始token和结束token,用于控制生成序列的起始和结束。 强制学习:训练时,解码器的输入采用标签 编码器-解码器的自回归推理逻辑:需要for循环的形式,串行的依次生成句子中的每个单词 中文分词工具jieba Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.4-transformer.html":{"url":"chapter-9/9.4-transformer.html","title":"9.4 机器翻译-Transformer","keywords":"","body":"9.4 机器翻译—Transformer 前言 本节将介绍当下人工智能领域的基石与核心结构模型——Transformer,为什么说它是基石,因为以ChatGPT为代表的聊天机器人以及各种有望通向AGI(通用人工智能)的道路上均在采用的Transformer。 Transformer也是当下NLP任务的底座,包括后续的BERT和GPT,都是Transformer架构,BERT主要由Transformer的encoder构成,GPT则主要是decoder构成。 本节将会通读Transformer原论文《Attention is all you need》,总结Transformer结构以及文章要点,然后采用pytorch代码进行机器翻译实验,通过代码进一步了解Transformer在应用过程中的步骤。 论文阅读笔记 Transformer的论文是《Attention is all you need》(https://arxiv.org/abs/1706.03762),由Google团队在2017提出的一种针对机器翻译的模型结构,后续在各类NLP任务上均获取SOTA。 Motivation:针对RNN存在的计算复杂、无法串行的问题,提出仅通过attention机制的简洁结构——Transformer,在多个序列任务、机器翻译中获得SOTA,并有运用到其他数据模态的潜力。 模型结构 模型由encoder和decoder两部分构成,分别采用了6个block堆叠, encoder的block有两层,multi-head attention和FC层 decoder的block有三层,处理自回归的输入masked multi-head attention,处理encoder的attention,FC层。 注意力机制:scale dot-production attention,采用QK矩阵乘法缩放后softmax充当权重,再与value进行乘法。 多头注意力机制:实验发现多个头效果好,block的输出,把多个头向量进行concat,然后加一个FC层。因此每个头的向量长度是总长度/头数,例:512/8=64,每个头是64维向量。 Transformer的三种注意力层: encoder:输入来自上一层的全部输出 decoder-输入:为避免模型未卜先知,只允许看见第i步之前的信息,需要做mask操作,确保在生成序列的每个元素时,只考虑该元素之前的元素。这里通过softmax位置设置负无穷来控制无效的连接。 decoder-第二个attention:q来自上一步输出,k和v来自encoer的输出。这样解码器可以看到输入的所有序列。 FFN层:attention之后接入两个FC层,第一个FC层采用2048,第二个是512,第一个FC层采用max(0, x)作为激活函数。 embedding层的缩放:在embedding层进行尺度缩放,乘以根号d_model, 位置编码:采用正余弦函数构建位置向量,然后采用加法,融入embedding中。 实验:两个任务,450万句子(英-德),3600万句子(英-法),8*16GB显卡,分别训练12小时,84小时。10万step时,采用4kstep预热;采用标签平滑0.1. 重点句子摘录 In the Transformer this is reduced to a constant number of operations, albeit at the cost of reduced effective resolution due to averaging attention-weighted positions, an effect we counteract with Multi-Head Attention as described in section 3.2. 由于注意力机制最后的加权平均可能会序列中各位置的细粒度捕捉不足,因此引入多头注意力机制。 这里是官方对多头注意力机制引入的解释。 \\2. We suspect that for large values of dk, the dot products grow large in magnitude, pushing the softmax function into regions where it has extremely small gradients 4. To counteract this effect, we scale the dot products by 根号dk 在Q和K做点积时,若向量维度过长,会导致点积结果过大,再经softmax映射后,梯度会变小,不利于模型学习,因此需要进行缩放。缩放因子为除以根号dk。 多头注意力机制的三种情况: encoder:输入来自上一层的全部输出 decoder-输入:为避免模型未卜先知,只允许看见第i步之前的信息,需要做mask操作,确保在生成序列的每个元素时,只考虑该元素之前的元素。这里通过softmax位置设置负无穷来控制无效的连接。 decoder-第二个attention:q来自上一步输出,k和v来自encoer的输出。这样解码器可以看到输入的所有序列。 首次阅读遗留问题 位置编码是否重新学习? ​ 不重新学习,详见 PositionalEncoding的 self.register_buffer('pos_table', self._get_sinusoid_encoding_table(n_position, d_hid)) qkv具体实现过程 通过3个w获得3个特征向量: attn = torch.matmul(q / self.temperature, k.transpose(2, 3)) # q.shape == (bs, n_head, len_seq, d_k/n_head) ,每个token用 一个向量表示,总向量长度是头数*每个头的向量长度。 attn = self.dropout(F.softmax(attn, dim=-1)) output = torch.matmul(attn, v) decoder输入的处理细节 训练阶段,无特殊处理,一个样本可以直接输入,根据下三角mask避免未卜先知 推理阶段,首先手动执行token的输入,然后for循环直至最大长度,期间输出的token拼接到输出列表中,并作为下一步decoder的输入。选择输出token时还采用了beam search来有效地平衡广度和深度。 数据集构建 数据下载 本案例数据集Tatoeba下载自这里。该项目是帮助不同语言的人学习英语,因此是英语与其它几十种语言的翻译文本。 其中就包括本案例使用的英中文本,共计29668条(Mandarin Chinese - English cmn-eng.zip (29668)) 数据以txt形式存储,一行是一对翻译文本,例如长这样: 1.That mountain is easy to climb. 那座山很容易爬。 2.It's too soon. 太早了。 3.Japan is smaller than Canada. 日本比加拿大小。 数据集划分 对于29668条数据进行8:2划分为训练、验证,这里采用配套代码a_data_split.py进行划分,即可在统计目录下获得train.txt和text.txt。 词表构建 文本任务首要任务是为文本构建词表,这里采用与上节一样的方法,首先对文本进行分词,然后统计语料库中所有的词,最后根据最大上限、最小词频等约束,构建词表。本部分配套代码是b_gen_vocabulary.py 词表的构建过程中,涉及两个知识点:中文分词和特殊token。 1. 中文分词 对于英文,分词可以直接采用空格。而对于中文,就需要用特定的分词方法,这里采用的是jieba分词工具,以下是英文和中文的分词代码。 source.append(parts[0].split(' ')) target.append(list(jieba.cut(parts[1]))) # 分词 2. 特殊token 由于seq2seq任务的特殊性,在解码器部分,通常需要一个token告诉模型,现在是开始,同时还需要有个token让模型输出,以此告诉人类,模型输出完毕,不要再继续生成了。 因此相较于文本分类,还多了bos, eos,两个特殊token,有的时候,开始token也会用start表示。 PAD_TAG = \"\" # 用PAD补全句子长度 BOS_TAG = \"\" # 用BOS表示开始 EOS_TAG = \"\" # 用EOS表示结束 UNK_TAG = \"\" # 用EOS表示结束 PAD = 0 # PAD字符对应的数字 BOS = 1 # BOS字符对应的数字 EOS = 2 # EOS字符对应的数字 UNK = 3 # UNK字符对应的数字 运行代码后,词表字典保存到了result目录下,并得到如下输出,表明英文中有2518个词,中文有3365,但经过最大长度3000的截断后,只剩下2996,另外4个是特殊token。 100%|██████████| 23635/23635 [00:00Dataset编写 NMTDataset的编写逻辑与上一小节的Dataset类似,首先在类初始化的时候加载原始数据,并进行分词;在getitem迭代时,再进行token转index操作,这里会涉及增加结束符、填充符、未知符。 核心代码如下: def __init__(self, path_txt, vocab_path_en, vocab_path_fra, max_len=32): self.path_txt = path_txt self.vocab_path_en = vocab_path_en self.vocab_path_fra = vocab_path_fra self.max_len = max_len self.word2index = WordToIndex() self._init_vocab() self._get_file_info() def __getitem__(self, item): # 获取切分好的句子list,一个元素是一个词 sentence_src, sentence_trg = self.source_list[item], self.target_list[item] # 进行填充, 增加结束符,索引转换 token_idx_src = self.word2index.encode(sentence_src, self.vocab_en, self.max_len) token_idx_trg = self.word2index.encode(sentence_trg, self.vocab_fra, self.max_len) str_len, trg_len = len(sentence_src) + 1, len(sentence_trg) + 1 # 有效长度, +1是填充的结束符 . return np.array(token_idx_src, dtype=np.int64), str_len, np.array(token_idx_trg, dtype=np.int64), trg_len def _get_file_info(self): text_raw = read_data_nmt(self.path_txt) text_clean = text_preprocess(text_raw) self.source_list, self.target_list = text_split(text_clean) 模型构建 Transformer代码梳理如下图所示,大体可分为三个层级 Transformer的构建,包含encoder、decoder两个模块,以及两个mask构建函数 两个coder内部实现,包括位置编码、堆叠的block block实现,包含多头注意力、FFN,其中多头注意力将softmax(QK)*V拆分为ScaledDotProductAttention类。 代码整体与论文中保持一致,总结几个不同之处: layernorm使用时机前置到attention层和FFN层之前 position embedding 的序列长度默认采用了200,如果需要更长的序列,则要注意配置。 具体代码实现不再赘述,把论文中图2的结果熟悉,并掌握上面的代码结构,可以快速理解各模块、组件的运算和操作步骤,如有疑问的点,再打开代码观察具体运算过程即可。 模型训练 原论文进行两个数据集的机器翻译任务,采用的数据和超参数列举如下,供参考。 英语-德语,450万句子对,英语-法语,3600万句子对。均进行base/big两种尺寸训练,分别进行10万step和30万step训练,耗时12小时/84小时。10万step时,采用4千step进行warmup。正则化方面采用了dropout=0.1的残差连接,0.1的标签平滑。 本实验中有2.3万句子对训练,只能作为Transformer的学习,性能指标仅为参考,具体任务后续由BERT、GPT、T5来实现更为具体的项目。 采用配套代码train_transformer.py,执行训练即可: python train_transformer.py -embs_share_weight -proj_share_weight -label_smoothing -b 256 -warmup 128000 -epoch 400 训练完成后,在result文件夹下会得到日志与模型,接下来采用配套代码c_train_curve_plot.py对日志数据进行绘图可视化,Loss和Accuracy分别如下,可以看出模型拟合能力非常强,性能还在提高,但受限于数据量过少,模型在200个epoch之后就已经出现了过拟合。 这里面的评估指标用的acc,具体是直接复用github项目,也能作为模型性能的评估指标,就没有去修改为BLUE。 模型推理 Transformer的推理过程与训练时不同,值得仔细学习。 Transformer的推理输出是典型的自回归(Auto regressive),并且需要根据多个条件综合判断何时停止,因此推理部分的逻辑值得认真学习,具体步骤如下: 第一步:输入序列经encoder,获得特征,每个token输出一个向量,这个特征会在decoder的每个step都用到,即decoder中各block的第二个multi-head attention。需要enc_output去计算k和v,用decoder上一层输出特征向量去计算q,以此进行decoder的第二个attention。 enc_output, *_ = self.model.encoder(src_seq, src_mask) 第二步:手动执行decoder第一步,输入是这个token,输出的是一个概率向量,由分类概率向量再决定第一个输出token。 self.register_buffer('init_seq', torch.LongTensor([[trg_bos_idx]])) dec_output = self._model_decode(self.init_seq, enc_output, src_mask) 第三步:循环max_length次执行decoder剩余步。第i步时,将前序所有步输出的token,组装为一个序列,输入到decoder。 在代码中用一个gen_seq维护模型输出的token,输入给模型时,只需要gen_seq[:, :step]即可,很巧妙。 在每一步输出时,都会判断是否需要停止输出字符。 for step in range(2, max_seq_len): dec_output = self._model_decode(gen_seq[:, :step], enc_output, src_mask) 略 if (eos_locs.sum(1) > 0).sum(0).item() == beam_size: _, ans_idx = scores.div(seq_lens.float() ** alpha).max(0) ans_idx = ans_idx.item() break 借助李宏毅老师2021年课件中一幅图,配合代码,可以很好的理解Transformer在推理时的流程。 输入序列经encoder,获得特征(绿色、蓝色、蓝色、橙色) decoder输入第一个序列(序列长度为1,token是),输出第一个概率向量,并通过max得到“机”。 decoder输入第二个序列(序列长度为2,token是[BOS, 机]),输出得到“器” decoder输入第三个序列(序列长度为3,token是[BOS, 机,器]),输出得到“学” decoder输入第四个序列(序列长度为4,token是[BOS, 机,器,学]),输出得到“习” ...以此类推 在推理过程中,通常还会使用Beam Search 来最终确定当前步应该输出哪个token,此处不做展开。 运行配套代码inference_transformer.py可以看到10条训练集的推理结果。 从结果上看,基本像回事儿了。 src: tom's door is open . trg: 湯姆 的 門開 著 。 pred: 汤姆 的 了 。 src: is a country . trg: 是 一個 的 國家 。 pred: 是 一個 的 城市 。 src: i can come at three . trg: 可以 來 。 pred: 我 可以 在 那裡 。 小结 本节通过Transformer论文的学习,了解Transformer基础架构,并通过机器翻译案例,从代码实现的角度深入剖析Transformer训练和推理的过程。由于Transformer是目前人工智能的核心与基石,因此需要认真、仔细的掌握其中细节。 本节内容值得注意的知识点: 多头注意力机制的引入:官方解释为,由于注意力机制最后的加权平均可能会序列中各位置的细粒度捕捉不足,因此引入多头注意力机制 注意力计算时的缩放因子:QK乘法后需要缩放,是因为若向量维度过长,会导致点积结果过大,再经softmax映射后,梯度会变小,不利于模型学习,因此需要进行缩放。缩放因子为除以根号dk。 多头注意力机制的三种情况: encoder:输入来自上一层的全部输出 decoder-输入:为避免模型未卜先知,只允许看见第i步之前的信息,需要做mask操作,确保在生成序列的每个元素时,只考虑该元素之前的元素。这里通过softmax位置设置负无穷来控制无效的连接。 decoder-第二个attention:q来自上一步输出,k和v来自encoer的输出。这样解码器可以看到输入的所有序列。 输入序列的msk:代码实现时,由于输入数据是通过添加来组batch的,并且为了在运算时做并行运算,因此需要对src中是pad的token做mask,这一点在论文是不会提及的。 decoder的mask:根据下三角mask避免未卜先知。 推理时会采用beam search进行搜索,确定输出的token。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.5-bert.html":{"url":"chapter-9/9.5-bert.html","title":"9.5 命名实体识别-BERT","keywords":"","body":"9.5 命名实体识别—BERT 前言 本节介绍一个NLP领域划时代意义的模型——BERT(Bidirectional Encoder Representations from Transformers),BERT论文目前的引用超9万次!(2024年2月),BERT模型架构是的NLP中各类任务也可以使用预训练+微调范式(类似CV领域)。 本节将简牍BERT论文,然后介绍采用BERT进行NER(Named Entity Recognition,命名实体识别)任务的微调,通过论文了解BERT的发展,同时通过具体下游任务,了解如何对预训练好的BERT模型进行微调,来适应下游任务。本节不涉及BERT的预训练。 BERT论文略读 《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》(https://arxiv.org/abs/1810.04805) 摘要:前人优秀工作仅用了单向信息且不能很好的应用到各类下游任务,本文提出一种基于Transformer的双向处理预训练模型——BERT,在预训练完成后,采用统一的结构进行多个NLP下游任务微调,均达到SOTA。 BERT模型关键词:预训练;双向信息;MLM(Masked Language Model)预训练任务;NSP(Next Sentence Predict)预训练任务 预训练相关工作 BERT之前的ELMo和GPT都是预训练机制,并且取得不错成绩,但他们都是基于单向的,存在缺点。 BERT为了解决单向预训练带来的不足,引入了MLM和NSP两个预训练任务,让模型能够从双向来理解语言。 BERT模型结构 BERT的构建与使用分为两个阶段,预训练和微调。所有下游任务微调时,均采用预训练好的参数进行全局初始化、全局训练。 BERT模型结构很简单,完全基于Transformer的encoder,并且有base和large两个版本,attention block、hidden size 和 head分别为(L=12, H=768, A=12, Total Parameters=110M) (L=24, H=1024,A=16, Total Parameters=340M)。 BERT的输入设计很巧妙,使得一个结构适应了多个NLP任务。输入设计为序列形式,将一个句子、两个句子都组装成为一个序列,输入到模型中。输入上,设计了两个特殊的token,cls和sep。 cls:可以理解为序列的全局特征,用于文本分类、情感分析这类的seq2cls的任务。 sep:用于将句子1和句子2进行拼接的token 在embedding处理上,设计了额外的segment embedding来标记句子是第一句、还是第二句。具体的输入embedding由三部分组成,如下图所示: BERT的预训练——MLM BERT的第一种预训练任务是MLM(masked language model),是对一句话中的一些单词进行隐藏,然后让模型根据上下文内容,在该mask的token位置上要求预测该单词。例如:“白切鸡” 经过MLM处理变为 “白mask鸡”,输入到BERT模型,BERT模型的输出标签是“白切鸡“。 在进行mask是需要一定概率的,文章中对全文的15%的token进行遮罩,然后这15%里,80%真正变为mask,10%为随机token,10%为原始token。这么做的原因是,下游任务中并没有mask这个特殊token,为了保障微调时的性能,这里做了这样的设置。( a downside is that we are creating a mismatch between pre-training and fine-tuning, since the [MASK] token does not appear during fine-tuning. ) BERT的预训练——NSP BERT的第一种预训练任务是NSP(Next Sentence Prediction),由于NLP任务中有一些是需要理解两个句子之间的关系,例如QA和NLI任务。为了让BERT掌握句子之间的理解能力,设计了NSP。 NSP是一个二分类任务。输入的是两个句子组成的序列,输出的是IsText or Not Text。含义是这两个句子是否是前后两句。论文举的例子: Input = [CLS] the man went to [MASK] store [SEP] he bought a gallon [MASK] milk [SEP] Label = IsNext Input = [CLS] the man [MASK] to the store [SEP] penguin [MASK] are flight ##less birds [SEP] Label = NotNext 预训练实验 预训练采用了2个数据集:BooksCorpus (800M words) (Zhu et al.,2015) and English Wikipedia (2,500M words)。 预训练参数及耗时如下:bs=256, epoch=40, 100万step, 1万step预热, lr=1e-4, base:16个TPU训4天, large:64个TPU训4天。 BERT的微调——下游任务 有了预训练好的BERT模型,可以快速方便的应用到各类NLP的下游任务,直接看下图列举的四种典型的任务: (1)seq2cls:输入是多个句子,用sep拼接,输出用cls的特征向量接softmax实现分类。 (2)seq2cls:输入是单个句子,处理同上。 (3)seq2seq:输入是两段话构成的序列,输出是第二段话中要求各token输出3个类别,用于标记哪些是答案的开始、答案的结束和无关内容。可以理解为词表为3的序列生成任务。 (4)seq2seq:输入是一句话,输出是每个token的分类类别,类别数根据任务而定,例如NER任务中,类别数是(实体种类*2 + 1),一个实体需要两个类别,实体开始和实体结束两个标记,1表示无关类别。 下游任务微调实验 超参数基本固定,可以套用大部分任务: Learning rate (Adam): 5e-5, 3e-5, 2e-5 Number of epochs: 2, 3, 4 Batch size: 16, 32 且采用的计算资源也很少,单个TPU一小时,单GPU几个小时即可,真实亲民的好模型。 论文小结 BERT是在ELMo和GPT之后提出来的一种基于Transformer的Encoder实现双向信息交互的预训练架构,并且可在一个模型结构上实现多种下游任务的微调,具有统一结构。BERT对于NLP的预训练-微调,算得上开创性的作品,为NLP微调范式打开了大门,后续的NLP任务大多基于BERT范式。 本论文需重点学习的几点如下: BERT最大亮点是双向信息交互和MLM+NSP的预训练-微调范式; MLM执行时,15%选中为mask候选token,再根据8:1:1比例进行真mask、随机token、原始token的设置 NSP任务采用sep特殊的token来拼接句子,实现一个序列输入,包含多个句子,sep的引入为模型统一多个任务提供了可能 预训练耗时4天,微调仅需数小时,此方案非常亲民。 NER任务简介 命名实体识别(Named Entity Recognition,简称 NER)是自然语言处理(NLP)中的一个基础任务,旨在识别文本中具有特定意义的实体,如人名、地名、组织名、时间、金额等。NER 通常用于信息提取、知识图谱构建、文本分类等任务的前置任务,属于基础任务。 NER数据的标注通常采用BIO体系来对文本中的每个token进行逐token的打标签(三分类),Begin表示实体开头,Inside表示实体中间/结尾,Outside表示非实体。简而言之,NER是逐token的多分类问题。分类类别等于(实体种类*2 + 1),一个实体需要B和I两个类别标记,所有实体共用一个无关类别O。 具体地,看一个文本: “苹果公司计划在2023年在中国深圳建立一家新工厂”,假设有三种实体类别,分别是组织、地名、时间。那么对应的NER训练标签为B-ORG, I-ORG, O, O, B-TIME, O, B-LOC, I-LOC, O, O, O, O。对应关系是 苹果 公司 计划 在 2023年 在 中国 深圳 建立 一家 新 工厂,由此可知“苹果公司”是组织, “2023年”是时间, “中国深圳”是地名。 接下来将会采用CLUENER 细粒度命名实体识别数据进行实验。 数据集构建 数据集下载 根据BERT论文的微调示意图和上文对NER的介绍,已经知道NER的数据制作目标是做一个token级别的多分类任务,下面就来观察数据。 首先下载CLUENER2020(https://github.com/CLUEbenchmark/CLUENER2020), 得到cluener_public.zip,解压后得到3个主要的json,分别是训练集、验证集和测试集。其中测试集没有标注标签。 以一条具体的数据介绍,一条数据包括原文本text,标签label,label中存在多个实体的标注,以字典形式,key是实体类别,value是具体内容以及字符的开始与结束字符。 {\"text\": \"虚幻引擎3动作游戏《黑光》新作公布\", \"label\": {\"game\": {\"《黑光》\": [[9, 12]]}}} 数据分为10个标签类别,分别为: 地址(address),书名(book),公司(company),游戏(game),政府(goverment),电影(movie),姓名(name),组织机构(organization),职位(position),景点(scene)。 在代码中对应的模型标签为(B表示Begin, I表示inside, O表示outside): [\"X\", \"B-address\", \"B-book\", \"B-company\", 'B-game', 'B-government', 'B-movie', 'B-name', 'B-organization', 'B-position','B-scene',\"I-address\", \"I-book\", \"I-company\", 'I-game', 'I-government', 'I-movie', 'I-name', 'I-organization', 'I-position','I-scene', \"S-address\", \"S-book\", \"S-company\", 'S-game', 'S-government', 'S-movie', 'S-name', 'S-organization', 'S-position', 'S-scene','O',\"[START]\", \"[END]\"] 整套代码借鉴https://github.com/lonePatient/BERT-NER-Pytorch,因此直接分析数据集编写部分,是如何将原始json解析为模型需要的形式。 数据集解析 根据utils_ner.py中的read_json函数,可以看出对json中的数据是如何做处理的。 最终数据被解析为: words = ['万', '通', '地', '产', '设', '计', '总', '监', '刘', '克', '峰', ';'] labels = ['B-company', 'I-company', 'I-company', 'I-company', 'B-position', 'I-position', 'I-position', 'I-position', 'B-name', 'I-name', 'I-name', 'O'] def _read_json(self,input_file): lines = [] with open(input_file, 'r', encoding='utf-8') as f: for line in f: line = json.loads(line.strip()) text = line['text'] label_entities = line.get('label',None) words = list(text) labels = ['O'] * len(words) if label_entities is not None: for key,value in label_entities.items(): for sub_name,sub_index in value.items(): for start_index,end_index in sub_index: assert ''.join(words[start_index:end_index+1]) == sub_name if start_index == end_index: labels[start_index] = 'S-'+key else: labels[start_index] = 'B-'+key labels[start_index+1:end_index+1] = ['I-'+key]*(len(sub_name)-1) lines.append({\"words\": words, \"labels\": labels}) return lines DataSet构建 对于BERT模型输入需要有3个内容,分别是token的index, mask的index, segment的index,下面是一条具体数据的情况。 02/22/2024 23:05:43 - INFO - processors.ner_seq - tokens: [CLS] 浙 商 银 行 企 业 信 贷 部 叶 老 桂 博 士 则 从 另 一 个 角 度 对 五 道 门 槛 进 行 了 解 读 。 叶 老 桂 认 为 , 对 目 前 国 内 商 业 银 行 而 言 , [SEP] 02/22/2024 23:05:43 - INFO - processors.ner_seq - input_ids: 101 3851 1555 7213 6121 821 689 928 6587 6956 1383 5439 3424 1300 1894 1156 794 1369 671 702 6235 2428 2190 758 6887 7305 3546 6822 6121 749 6237 6438 511 1383 5439 3424 6371 711 8024 2190 4680 1184 1744 1079 1555 689 7213 6121 5445 6241 8024 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 02/22/2024 23:05:44 - INFO - processors.ner_seq - input_mask: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 02/22/2024 23:05:44 - INFO - processors.ner_seq - segment_ids: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 02/22/2024 23:05:44 - INFO - processors.ner_seq - label_ids: 31 3 13 13 13 31 31 31 31 31 7 17 17 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 在这里会用到预训练模型的字典来做tokens到input_ids的映射,例如[CLS]就是101类,[PAD]是0类,浙是3851类。 以上功能在以下两个地方体现。 run_ner_softmax.py 的 train_dataset = load_and_cache_examples(args, args.task_name,tokenizer, data_type='train') processors/ner_seq.py 的 features = convert_examples_to_features(examples=examples, 在此份代码中,采用了 dataset = TensorDataset(all_input_ids, all_input_mask, all_segment_ids, all_lens,all_label_ids) 的方式构建dataset。 模型构建 BERT模型已成为NLP任务中常用的模型,因此已得到很好的集成,目前大多采用HuggingFace的transformers库进行构建BERT。 构建BERT模型有三要素,分别是配置文件,模型本体,tokenizer。 配置文件BertConfig 配置文件用class transformers.BertConfig封装,包含以下主要内容,从参数大概就明白,BERT模型的大小、向量大小、层数等都在这里面配置。 通常这个配置内容是跟着预训练模型的信息走的,需要从磁盘中加载config.json文件,例如本案例使用的预训练模型是从https://huggingface.co/google-bert/bert-base-chinese/tree/main下载,里边就包含了配置文件。 def __init__( self, vocab_size=30522, hidden_size=768, num_hidden_layers=12, num_attention_heads=12, intermediate_size=3072, hidden_act=\"gelu\", hidden_dropout_prob=0.1, attention_probs_dropout_prob=0.1, max_position_embeddings=512, type_vocab_size=2, initializer_range=0.02, layer_norm_eps=1e-12, pad_token_id=0, position_embedding_type=\"absolute\", use_cache=True, classifier_dropout=None, **kwargs ): 模型本体 BERT本身这个nn.Module就复杂一些,除了实现BERT运算功能,还涉及huggingface的协议、功能,所以最终应用的模型类BertSoftmaxForNer是继承了官方BertPreTrainedModel,并且内部定义了官方BertModel模型,用于BERT的运算;同时在BertSoftmaxForNer内部,进行适当的代码修改,用于实现下游任务以处理逻辑。 类代码具体的定义与关系可参考如下UML示意图:BertSoftmaxForNer是核心,往左边是BertModel模型的核心,BertModel与上节Transformer一样,拆分了多个子模块来构建,因此第一列整体是BERT的构建,值得注意的是三个embedding的构建。 捋清楚模型类定义后,再看模型初始化就会清楚真正用的模型是怎么来的: model = model_class.from_pretrained(args.model_name_or_path, config=config) 根据config配置,模型权重文件,借助PreTrainedModel类的from_pretrained()函数,实现模型定义、权重加载。 模型训练 模型训练采用run_ner_softmax.py,主要修改预训练文件路径、数据集路径、输出路径即可,具体的运行参数如下所示 --model_type=bert --model_name_or_path=./bert-base-pretrained --task_name=cluener --do_lower_case --loss_type=ce --do_train --do_eval --data_dir=G:\\deep_learning_data\\cluener_public --train_max_seq_length=128 --eval_max_seq_length=512 --per_gpu_train_batch_size=64 --per_gpu_eval_batch_size=64 --learning_rate=3e-5 --num_train_epochs=4.0 --logging_steps=20 --save_steps=224 --output_dir=D:\\github_desktop\\BERT-NER-Pytorch\\outputs --overwrite_output_dir --seed=42 BERT的微调大多是4epoch,很快就见效,笔记本上训练大约10分即可完成,接下来观察一下训练效果。 首先来看loss曲线,运行01_loss_curve_plot.py,可以查看loss曲线变化,如下图所示: 从曲线看,loss下降很快,并且迅速到达了平台期,F1快速达到了0.79,训练效果较好。 补充说明:本节的训练代码写得不是很优雅,就不过多讨论,后续应该不会再遇到类似结构的代码。 模型推理 模型训练好之后,来看看在测试集上的推理效果。 首先,run_ner_softmax.py代码已经实现推理预测功能,把--do_train --do_eval去掉,保留 --do_predict即可得到test_prediction.json。 然后,运行02_predict_parse.py,可以看到解析结果。例如: 从文本\"四川敦煌学”。近年来,丹棱县等地一些不知名的石窟迎来了海内外的游客,他们随身携带着胡文和的著作。\"中,提取到实体 organization \"四川敦煌学\", address \"丹棱县\", name \"胡文和\", 从文本\"尼日利亚海军发言人当天在阿布贾向尼日利亚通讯社证实了这一消息。\"中,提取到实体 government \"尼日利亚海军\", address \"阿布贾\", company \"尼日利亚通讯社\", 从文本\"销售冠军:辐射3-Bethesda\"中,提取到实体 game \"辐射3\", 从文本\"所以大多数人都是从巴厘岛南部开始环岛之旅。\"中,提取到实体 scene \"巴厘岛\", 从文本\"备受瞩目的动作及冒险类大作《迷失》在其英文版上市之初就受到了全球玩家的大力追捧。\"中,提取到实体 game \"《迷失》\", 从文本\"filippagowski:14岁时我感觉自己像梵高\"中,提取到实体 name \"filippagowski\", name \"梵高\", 小结 本节通过论文+代码的形式来介绍BERT模型的应用,通过实验发现BERT的出现,为NLP各类任务提供了方便。为此,有必要再次回顾一下BERT模型的特性: (1)基于Transformer的encoder,实现双向信息的编码融合,提高上下文理解能力,并设计预训练+微调范式,实现多个NLP任务的统一架构——BERT; (2)BERT采用的无监督预训练方法为MLM(masked language model)和NSP(next sentence predict),可以实现token级和句子级的信息学习; (3)BERT的各任务微调,通常指需要4epoch,学习率1e-5级别,gpu几个小时即可,非常亲民; (4)BERT的输入embedding新增了一个segment embedding来标记句子当前token属于第几个句子; (5)BERT的统一架构实现多个任务,得益于输入-输出的设计,有必要再次回顾这幅图 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.6-gpt.html":{"url":"chapter-9/9.6-gpt.html","title":"9.6 文章续写-问答对话-GPT","keywords":"","body":"9.6 文章续写&问答对话-GPT 前言 本节介绍生成式对话模型的基座——GPT(Generative Pre-Training),将会从OpenAI的四篇论文入门,了解GPT的概念及发展历程。随后通过GPT2的训练及推理,加深对GPT的理解。最后对本节进行小结。 GPT系列论文简读 生成式对话模型(以ChatGPT为代表)的发展历史可以关注OpenAI在GPT系列上的研究,这里通过GPT1, 2, 3和InstructGPT来了解GPT的发展及变化,有助于理解大语言模型。 GPT: Improving Language Understanding by Generative Pre-Training https://www.mikecaptain.com/resources/pdf/GPT-1.pdf GPT2:Language Models are Unsupervised Multitask Learners https://insightcivic.s3.us-east-1.amazonaws.com/language-models.pdf GPT3:Language Models are Few-Shot Learners https://arxiv.org/abs/2005.14165 Instruct GPT:Training language models to follow instructions with human feedback https://arxiv.org/pdf/2203.02155.pdf GPT1 GPT1《Improving Language Understanding by Generative Pre-Training》由OpenAI团队在2018年6月公开,首次将Transformer的decoder用于大规模预训练,从而实现从无标签数据从训练具备语言理解的基础模型,为NLP下游任务提供强大底座。 GPT关键词:预训练+微调(全文似乎就没有其它关键点了...) GPT模型结构及下游应用范式,可以根据文中图1来理解。 GPT模型结构:12层Transformer的decoder,再根据不同任务接不同的输出头。 下游任务微调范式:论文介绍了GPT应用于四种下游任务,分别是文本分类、蕴含、句子相似性、阅读理解(多项选择题) 文本分类:输出特征向量接入一个linear层做分类; 蕴含:蕴含是涉及两个句子的二分类问题,这里为了保证基础模型架构不变动,把两个句子通过特殊的token Delim来拼接,然后接linear做二分类。 句子相似性:两个句子拼接为一个序列,根据句子前后顺序,可以获得两个序列,分别输入模型,将两个特征向量相加,再接linear做二分类。 多项选择题:多选题有3部分信息,原始文本、问题、答案,将不同的选项拼接起来得到多个序列,每个序列最终输出一个概率,最后把多个选项通过softmax得到最终的概率。 训练情况: 预训练7000本书,具体数据量未说明,100个epoch,bs=64, lr=1e-4级别 微调,通常3个epoch,bs=32, lr=1e-5级别。 小结:GPT采用Transformer的Decoder作为组件,采用MLM进行预训练获取基础模型,基于预训练好的模型,实现一个模型架构四种NLP任务微调,获取9个任务的SOTA,表明了预训练+微调范式有效。 GPT2 GPT2:《Language Models are Unsupervised Multitask Learners》 在BERT提出后的4个月,GPT2出世,继续坚定采用decoder架构(BERT是encoder模块堆叠),在更大尺寸、更多数据上进行预训练,并在多个NLP任务(GPT提及4个任务,BERT提及4类任务)上达到SOTA。 GPT2的文章没有过多介绍算法,这里简单记录几个关键点: 爬取4500万链接,清洗出800万文档,共计40GB。 词表大小50257,上下文从512变为1024. 词表策略采用优化后的BPE,可将词表大小缩减2-3倍(13万-->5万) 探索zero-shot learning:gpt中在微调时采用了特殊token来让模型理解人类的意思,需要执行特定任务;GPT2希望做到在无需微调,即预训练阶段就让模型具备根据输入信息来执行特定任务的能力,这就是零样本学习,预训练好之后,模型就可以完成特定任务,无需样本。如何让模型能完成特定任务呢?举个机器翻译的例子,只要将输入给模型的文本构造成 translate english to chinese, [english text], [chinese text] 就好了。比如:translate english to chinese, [machine learning]。这就是prompt的概念,预训练模型可以根据用户的输入来理解任务。这需要预训练模型具备大量类似的训练数据(这也是后续大模型有base和chat的区别,chat模型加入更多的对话数据,实现对话能力,base模型更多的是知识能力)。 GPT3 GPT3:《Language Model are Few-Shot Learners》 随着数据与算力的发展,同时预训练的具体潜力被认可,OpenAI在GPT2的基础上进行了多方面的投入,数据增长1000倍(40GB->45TB),模型扩大100倍(1.5B -> 175B),论文作者增大7倍(4人->31人)。由此可见,GPT3是一个大力出奇迹的作品,耗费大约1200 万美金训练。 GPT3的论文有75页之多, 这里简要梳理整体内容。 本文主旨:利用大模型、大数据来无监督训练GPT,探索它在少样本学习上的能力,希望让GPT模型更接近人类的思考方式。 本文关键词:Few-Shot learning,任务无关 (Task-agnostic) 数据量:来自互联网、高质量书籍文章等的45TB数据,总token达到260B(0.26T;2024年大模型训练数据一般在2-5T的tokens)。 数据处理:互联网爬取数据中过滤低质量数据;文档级别的数据去重;加入高质量数据; 数据处理的bug:为了让模型在评估数据集上公平的测评,需要把训练集中的数据污染过滤掉,虽然做了,但最后发现还是存在数据污染。遗憾的是,因模型训练过于昂贵,无法重新训练了。 模型设置:做了8个不同尺寸的模型,最大的1750亿参数,命名为GPT3,结构与GPT2没有过多差别 模型训练:batchsize也做了预热,逐步增大batchsize;学习率预热 375 M tokens,总共训练260 B tokens。样本全部组装为长度是2048的序列。 模型评估:在各类NLP任务上评估了少样本学习的能力。 模型缺点:会出现重复、缺乏连贯、以及单向结构的弊端(BERT那样的是双向结构) 模型的潜在影响:模型可能存在滥用,例如垃圾邮件、欺诈性学术论文写作等;性别、种族、宗教的偏见问题;能源问题; 一句话总结GPT3:采用大数据大模型训练GPT3,可实现少样本的学习能力,可以完成多种NLP任务,其模式更接近人类的1-shot learning。 ChatGPT(Instruct GPT) Instruct GPT:Training language models to follow instructions with human feedback 于2022年3月发表。 关键词Language Model Alignment;Following Instructions, Reinforcement Learning from Human Feedback 摘要 GPT3已经能进行各类的文本生成任务,但TA不太能够遵循人类的意图,为了让GPT更好的遵循人类意图,回答人类的问题,本文采用RLHF的强化学习方式,来微调GPT,结果表明微调后的GPT,即使仅1.3B参数,也比175B参数模型,更受人们青睐。 指令跟随的理解 简单回顾GPT机制。GPT是Language Model,根据左边的序列预测下一个token,在GPT-123系列中,已经看到在各类文本生成任务中表现不错,但是要利用GPT来完成具体的文本分类、句子相似性判断、多项选择题时,需要在推理时加入特殊的token,例如GPT1中的start、delim、extra这样的“指令”来组装序列。 这是由于GPT并不能直接根据人类语言来完成这些任务,例如希望实现机器翻译时,期望输入的应该是:translate english to chinese, [english text], [chinese text] 。这里的translate english to chinese就是人类的指令,但在GPT1、2和3的预训练中,采用大多是书籍、互联网文本数据,缺乏这样的指令对话,因而GPT3也不能很好的满足人类的美好需求。 为了让GPT模型能更好的理解人类意图,需要进行模型对齐、指令跟随的微调训练,本文训练出来的GPT就称为Instruct GPT,这也是ChatGPT背后强大的技术支撑——满足人类意图。 训练过程 训练的目标是让GPT输出的内容,让人类更满意,本文巧妙的利用模型输出多个答案,让人类进行排序的方式进行衡量,实现对模型输出结果好坏的评估。 第一步,训练基础模型,让GPT3能够回答人类问题。由人工撰写问题,以及希望模型回答的内容作为示例,对GPT-3进行监督学习微调,得到一个基线监督模型。 第二步,训练奖励模型(Reward Model)。又人类对模型的输出进行排序,然后训练一个能打分的RM模型。这个模型会在强化学习中评估GPT模型输出的好坏。 第三步,训练Instruct GPT。奖励模型的输出作为奖励信号,通过proximal policy optimization (PPO)算法,进一步微调第一步得到的基线模型。微调目标是最大化奖励模型预测的偏好分数,从而使模型输出逐步符合人类意图。最终得到nstructGPT。 更多细节: 第一步监督微调所用数据集大约有28,000个示例,主要来自标注员编写和OpenAI API用户提交的提示。 第二步奖励模型训练数据集包含约13,000个标注员对不同模型输出的偏好排序实例。 第三步强化学习阶段除了基于奖励模型的PPO目标外,还混合了与原始GPT-3预训练数据的似然目标(PPO-ptx),以缓解在下游任务上的性能下降。 下面通过表格对比GPT系列模型的变化 GPT GPT2-s GPT2-m GPT2-l GPT2-xl GPT3 参数量 ~117 M 117 M 345 M 762 M 1.5 B 175 B 训练数据 7000本书 40GB 40GB 40GB 40GB 45TB Block 12 12 24 36 48 96 d_model 768 1024 1024 1280 1600 12888 论文小结 ChatGPT的出圈不是一蹴而就,自2017年微软发布Transformer,再到OpenAI从2019年基于Transformer的decoer提出GPT,经过3年多的发展,从预训练-微调范式,不断加大模型尺寸,探索模型能力边界,再到基于强化学习的微调让GPT模型具备说人话的能力,最终有了ChatGPT这样的作品。 GPT训练 本小节采用中文预料,预训练一个GPT2-small的模型,基于wiki百科训练的模型可以实现一定程度的文章续写,基于百科QA的数据可以实现类似的问答效果。 通过GPT论文的学习,可以知道GPT要实现与人顺畅的交流,需要特定对话数据进行微调,在这里暂时不具备此能力。 在这里希望通过代码,强化对GPT运行机制的了解,包括训练数据如何组装、拼接、特殊token的处理、推理时的处理细节,为后续LLM的研发打下基础。 这里整体训练代码参考自:https://github.com/Morizeyao/GPT2-Chinese 环境配置说明: transformers==2.1.1 (高版本不适配) GPT训练数据准备 由于是预训练预料,因此可以采用互利网上的文本信息,这个repo收集了5个数据集,有上千万的句子可用。分别有: 1.维基百科(wiki2019zh),100万个结构良好的中文词条 2.新闻语料(news2016zh),250万篇新闻,含关键词、描述 3.百科问答(baike2018qa),150万个带问题类型的问答 4.社区问答json版(webtext2019zh),410万个高质量社区问答,适合训练超大模型 5.翻译语料(translation2019zh),520万个中英文句子对 在这里,受限于计算资源,只采用了维基百科(wiki2019zh)和百科问答(baike2018qa)两个数据集,分别训练了两次GPT2,用于观察不同数据下模型的差异。 GPT预训练的数据主要是文本序列,不同文章、段落的文本可以拼接到一起输入给模型。 因此,数据处理就比较简单,将所有文本拼接,然后在文章开头、结束加入特殊token来标识,最后划分为batch的形式输入到模型即可。 这部分逻辑参考train.py中的build_files函数。build_files大致逻辑如下: 读取文件夹中的原始文件,所有字符串以list为单位,存储于lines_total中 根据分片的数量,对lines_total进行切割,过滤单条文本长度小于min_length的文本 不同文本之间加入特殊token进行分隔,并用tokenizer转为index 写入txt文件 PS:代码中没有采用pytorch的dataset和dataloader的概念,直接对txt文件进行读取,在训练代码中处理。 GPT模型构建 基于transformers库中的modeling_gpt2的GPT2LMHeadModel实现,代码也很简单,没有特殊的,需要提一点就是在GPT2LMHeadModel中的forward实现了计算loss功能,只需要在forward的时候吧labels传入,由于是masked langurage model的任务,标签就是输入右移一位,具体看如下代码: # ... GPT2LMHeadModel中的forward()) if labels is not None: shift_logits = lm_logits[..., :-1, :].contiguous() # 取输入n-1个 shift_labels = labels[..., 1:].contiguous() # 取n-1个标签 loss_fct = CrossEntropyLoss(ignore_index=-1) loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1)) outputs = (loss,) + outputs GPT模型训练 模型训练采用了两个数据集,通过不同的数据类型,观察GPT模型对于人类意图的跟随能力。 这里采用了wiki2019和baike2018qa两个数据集,wiki2019中的文本主要是陈述句,baike2018qa里则会有对话和问答。 首先看wiki2019的训练,wiki2019,总token数5亿(508478004),在1080ti(12GB)上训练需要5天左右,5个epoch后loss基本稳定在2。 python train.py --raw --raw_data_path ./wiki_raw/wiki_zh --epochs 10 --batch_size 4 --log_step 100 --num_pieces 1000 接着看baike2018qa的训练,总token数也是5亿(505564900),在1080ti(12GB)上训练需要5天左右,5个epoch后loss基本稳定在2+。 python train.py --raw --raw_data_path ../data/baike2018qa --epochs 5 --batch_size 4 --log_step 100 --num_pieces 1000 GPT推理 训练完成,在model文件下获取模型权重,采用generate.py进行推理,推理时需要设置prefix,模型根据前缀进行后续文字生成。 推理示例: python generate.py --model_path ./model/model_epoch5_baikeqa --prefix 我肚子痛了,要怎么办? python generate.py --model_path ./model/model_epoch5_wiki_alldata --prefix 秦始皇,名嬴政,是中国历史上著名的君主,也是中国历史上第一个使用“皇帝”称号的君主。他出生于公元前259年,卒于 这里暂时没有做停止符处理,先让模型输出到最大长度,正常情况应该判断是否输出了停止符。(停止符是两个回车\\n\\n) 具体的推理、采样逻辑如下代码所示: def sample_sequence(model, context, length, n_ctx, tokenizer, temperature=1.0, top_k=30, top_p=0.0, repitition_penalty=1.0, device='cpu'): context = torch.tensor(context, dtype=torch.long, device=device) context = context.unsqueeze(0) generated = context with torch.no_grad(): for _ in trange(length): inputs = {'input_ids': generated[0][-(n_ctx - 1):].unsqueeze(0)} # 模型推理 outputs = model(**inputs) # Note: we could also use 'past' with GPT-2/Transfo-XL/XLNet (cached hidden-states) next_token_logits = outputs[0][0, -1, :] # 输出l个logits,但只需要取最后一个 # 加入温度惩罚 for id in set(generated): next_token_logits[id] /= repitition_penalty next_token_logits = next_token_logits / temperature next_token_logits[tokenizer.convert_tokens_to_ids('[UNK]')] = -float('Inf') # 采样 filtered_logits = top_k_top_p_filtering(next_token_logits, top_k=top_k, top_p=top_p) next_token = torch.multinomial(F.softmax(filtered_logits, dim=-1), num_samples=1) # 拼接 generated = torch.cat((generated, next_token.unsqueeze(0)), dim=1) return generated.tolist()[0] ------------------------------------------------------------------ 看第一个推理例子 ------------------------------------------------------------------ (模型权重可以从这里下载提取码:lwzr) python generate.py --model_path ./model/model_epoch5_wiki_alldata --prefix 秦始皇,名嬴政,是中国历史上著名的君主,也是中国历史上第一个使用“皇帝”称号的君主。他出生于公元前259年,卒于 模型输出: 秦始皇,名嬴政,是中国历史上著名的君主,也是中国历史上第一个使用“皇帝”称号的君主。他出生于公元前259年,卒于154年,享年68岁。\\n\\n\"} {\"id\":\"3704970\",\"url\":\"https://zh.wikipedia.org/wiki?curid=3704970\",\"title\":\"中国人民解放军海军航空兵\",\"text\":\"中国人民解放军海军航空兵\\n\\n中国人民解放军海军航空 兵(,缩写为)是中国海军的一支现役军事部队,由现役部队改编而成,是中国人民解放军陆军中的最新式军事部队。\\n\\n1962年,在海军的改编和建立之初,海军航空兵的基础上开展 以航空兵为主体的军事训练。随着1985年7月20日成立了第一届海军大学校,海军航空兵开始进行部队建设,并且在1990年12月28日举行了首届海军大学体育训练班。1996年3月18日,, 军航空兵正式开始招收军事战斗部队,这一年开始实施大学体育运动。1999年,海军航空兵正式进入国防部直属的军训,并且在2003年7月19日起正式开始招收军事战斗部队,同时开 始招收专业的军事战斗部队。目前中国人民解放军的各级军事战斗部队包括陆军航空兵第二十二集团军,第六十七集团军,海军军事学校航空兵第四十五集团军。\\n\\n2005年,陆军航空 开始进行全军装备升级,并且在2006年1月1日起开始进行了大规模的现场演练,并且在一天的时间里将部队装备的现役军事战斗部队改为现役部队。现役部队装备的现役部队装备已经经 全转型到现役部队,并且陆续进驻陆军航空兵、海军军事战斗部队及海军军事战斗部队等。陆军航空兵的现役部队现役部队包括陆军航空兵第二十二集团军、海军军事战斗部队、海军军 事战斗部队、海军航空兵第四十五集团军、海军航空兵第一十一集团军和空军航空兵第二十六集团军。\\n\\n陆军航空兵现役的部队包括陆军航空兵、空军航空兵、空军航空兵、海军军军 战斗部队、海军航空兵、海军航空兵、海军航空兵等,部队装备的现役部队还包括海军航空兵、海军航空兵、海军航空兵和空军军事战斗部队。\\n\\n现役部队装备的现役部队包括陆军军 空兵第九十一集团军、海军航空兵第四十五集团军、空军航空兵第九十六集团军、海军军事战斗部队、海军航空兵第四十五集团军、空军航空兵第一十六集团军、空军航空兵第二十六六 团军、海军航空兵第十六集团军、海军航空兵第十八集团军、空军航空兵第二十九集团军、空军航空兵第二十九集团军、空军航空兵第九十四集团军和空军航空兵第九十五集团军。\\n n\\n现役部队装备的现役部队包括陆军航空兵第九十八集团军、海军航空兵第九十七集团军、空军航空兵第九十七集团军及空军航空兵第十八集团军。 ...略......略......略......略... 从输出来看,文字读起来都是人话,但是深究逻辑就有明显错误,例如秦始皇的逝世年份是不对的。并且在后续的文本中,输出了一个wiki不存在的词条,同时文本里面还出现了大量的重复,这些都是GPT3论文中提到的问题——重复。 ------------------------------------------------------------------ 看第二个例子 ------------------------------------------------------------------ python generate.py --model_path ./model/model_epoch5_wiki_alldata --prefix 我感冒了怎么办? 模型输出: 我感冒了怎么办?」()这句话在电影界中引起了一片好的关注,在电影中也常出现一些不好的场景。在2002年,电影的剧照已经被删除过一次,并且由于其他原因,它在电影中被删除。\\n\\n\"} {\"id\":\"3543288\",\"url\":\"https://zh.wikipedia.org/wiki?curid=3543288\",\"title\":\"阿尔及尔航空330号班机空难\",\"text\":\"阿尔及尔航空330号班机空难\\n\\n阿尔及尔航空330号班机 阿尔及尔国内首班国内航班,机型为波音747-2er,于1983年7月22日起执飞,是一班从利比亚布尔加斯-首都贝鲁特经利比亚首都贝鲁特、贝鲁特抵达巴林国际机场的定期航班,机上8 87人当中,50人是来自利比亚的阿尔及尔航空公司。\\n\\n事发时阿尔及尔国内有16名乘客和53名机师的生还。\\n\\n机上16名乘客和12名机师生还。\\n\\n\"} ...略......略......略......略... 很明显,模型完全没能理解用户输入信息,也就是无法进行对话功能。这与训练数据有关,属于Instruct GPT里提到的指令跟随内容,wiki预料中都是陈述句,没有这些问句以及人类提出的需求文本,因此模型无法跟随人类指令。如果需要让模型能够进行指令跟随,应当用一系列问答、对话、命令式的文本来训练。 ------------------------------------------------------------------ 看第三个例子 ------------------------------------------------------------------ (模型权重可以从这里下载提取码:4hua python generate.py --model_path ./model/model_epoch5_baikeqa --prefix 我肚子痛了,要怎么办? 模型输出: 我肚子痛了,要怎么办?是有什么问题 如果是肠炎,就应该是肠梗阻了,建议用中药治疗一下,平时要多喝水,饮食要清淡,多运动,要注意饮食,忌烟酒。 我是新手,刚玩。请问怎样才能玩到15级啊!!还有我现在的号是52级的,怎样升级才能升到13级啊?我是新手,请高手指点一下,我现在是17级。谢啦 怎样才能玩到15级!!!我是新手,刚玩。请问怎样才能玩到15级啊! 你的号已经是新手了,你的级别是在40级前的,那你可以到新手村,那里会有很多的怪,你可以从40级以上的怪物身上找到一些相关怪物,你会获得一定经验,还可以去找别人成一定数量的装备和你需要的金钱。 ...略......略......略......略... 可以看到模型会续写用户的输入,并且换行之后进行“回答”,看似模型已经会回答问题了,这是因为baike2018qa数据集就是大量问答对的数据,从以上三个案例也可知道,生成式模型的对话能力,需要用对话数据、人类指令数据进行微调。这也是当下大模型中有base版和chat版的区别。base版模型是在大量的预料数据库上训练的,那些都是陈述句,属于信息的描述。要想让模型理解人类意图,还需要训练chat版。 推理速度分析 通过以上案例,可以发现token推理速度随着输出长度增加而增加,这个很好理解,因为是自回归式的输出,预测token越多,下一个token推理时需要处理的QKV计算相应增加,从而速度变慢,并且显存占用是逐渐增加的。 在这里也是Transformer架构可优化的地方,每个token的推理时,其实有一大部分K,V是重复运算了的,因此可以将这部分K,V缓存下来,减少运算量,这就是KV-Cache的由来。 小结 本节通过梳理GPT1,2,3到chatGPT的技术发展历程,对GPT模型有了详细的了解,并通过GPT2-small模型的训练,进一步了解GPT的运行机制,为后续大语言模型的学习打下基础。 本节值得重点关注的点有: GPT1,2,3模型从GPT1中采用特殊token来让模型完成特定任务,到Instruct GPT通过自然语言让模型完成特定任务,是ChatGPT得以破圈,被人类所接受的重要改变,这样的交互方式,也是大家耳熟能详的Prompt Engineering。 GPT系列是一个模型、数据、计算基础设施的复杂系统性工程,三者缺一不可,从GPT3论文中提及数据污染的bug无法重新训练模型可知,训练175B模型太贵了。 GPT训练时,句子之间用特殊token进行拼接,变为一个长序列输入给到模型训练。一般loss在2上下是一个还过得去的区间。 指令跟随的理解,可以用GPT1如何应用到下游任务思考,结合Instruct GPT的内容,即可理解指令跟随本意就是预训练数据需要包含人类指令、人类命令,这样预训练模型才能在推理时,理解人类意图。 GPT的推理是自回归的,模型一次推理,对于人类而言有价值意义的是最后一个概率向量,对于循环多少次,要输出多少个token,取决于人类的代码逻辑。 KV-cache的理解,在最原始的代码推理中,可以发现输出的token越长时,越靠后的token耗时约长,这是因为单次推理时,输入的token变长了,需要计算的注意力变多,因而变慢,但大部分注意力的计算在前序推理时已经计算过的,因此可以通过缓存存储那些计算值,以此加快推理速度。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/":{"url":"chapter-10/","title":"第十章 大语言模型安装与应用","keywords":"","body":"第十章 大语言模型安装与应用 第十章 大语言模型安装与应用 10.1 Qwen 部署与分析 10.2 ChatGLM3 部署与分析 10.3 Baichuan2 部署与分析 10.4 Yi 部署与分析 10.5 GPT Academic 安装与使用 第十章简介 本章介绍大语言模型(Large Language Model)的本地安装、推理分析,包括国内主流开源大模型,Qwen系列、ChatGLM系列、Baichuan系列和Yi。同时为了辅助大家了解LLM的能力边界和应用,分析一个54K stars的LLM应用工具——GPT Academic(GPT 学术优化)。 通过本章可以了解到四个国产开源大模型的: 安装部署环境准备 模型代码结构设计方案 LLM推理流程步骤 多轮对话prompt组装方式 显存占用与上下文长度关系趋势 LLM内置角色system、user、assistant、observation的作用与区别 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.1-qwen.html":{"url":"chapter-10/10.1-qwen.html","title":"10.1 Qwen部署与分析","keywords":"","body":"10.1 Qwen部署与分析 前言 随着大语言模型的出圈及广泛应用落地,LLM技术已成为新一代人工智能、深度学习的必备技能,本节将介绍当下较为热门的开源大语言模型Qwen的安装及代码、流程、显存消耗分析。 本节内容有3个内容,有助于理解LLM模型,包括: Qwen模型代码结构组织与接口分析:可了解LLM模型的代码是如何设计的 LLM模型多轮对话机制:逐步剖析LLM的推理机制,多轮对话时如何组装历史聊天记录的 LLM模型显存占用:了解显存随着上下文长度的增加的变化趋势 Qwen简介 通义千问是阿里巴巴集团Qwen小组研发的大语言模型,自2023年4月开放测试8月开源7B模型,12月开源72B;到2024年3月开放了Qwen1.5-32B,可最大限度兼顾性能、效率和内存占用的平衡,Qwen系列不断更新,为开源社区做出了贡献。 Qwen系列产品 通义千问仅是Qwen团队产品之一,属于对话模型,能理解人类语言、生成内容,作为用户生活和工作的智能助手。 通义,取自《汉书》中的“天地之常经,古今之通义也”,有“普遍适用的道理与法则”之意。官网展示,通义是致力于实现类人智慧的通用智能 千问,千万次的问,千万的学问。 除了通义千问,Qwen团队还有其他大模型,共计10个(截止至2024年4月16日),分别是: 通义千问、通义万相、通义听悟、通义点金、通义灵码、通义法睿、通义星尘、通义仁心、通义晓蜜和通义智文。 Qwen目前开源Qwen1.5系列,属于2.0的pre版,相较于1.0在更方面指标均有所提升,并且开放了0.5B, 1.8B, 4B, 7B, 14B, 32B, 72B多个尺寸,同时有base和chat版,也有多种精度fp16, int8, int4。开发者可根据不同的场景、硬件平台、任务复杂度来选择模型。 为了更全面了解Qwen系列产品,推荐以下链接: 官方产品主页:https://tongyi.aliyun.com/ Qwen团队github主页:https://github.com/QwenLM Qwen开源文档:https://qwen.readthedocs.io/en/latest/ Qwen技术报告:https://arxiv.org/abs/2309.16609 Qwen的HuggingFace主页:https://huggingface.co/Qwen 本地部署安装 Qwen代码提供了命令交互案例,运行成功后,如下所示: User: 你是谁 Qwen-Chat: 我是来自阿里云的大规模语言模型,我叫通义千问。我可以回答各种问题、创作文字,还能表达观点、撰写代码。 User: 你还有什么能力? Qwen-Chat: 我还可以将文本从一种语言翻译成另一种语言,总结文本,生成文本,写故事,分析情绪,提供建议,开发算法,编写代码以及任何其他基于语言的任务。 User> 为了成功安装Qwen,建议严格阅读github的readme,下面说明本次部署的环境、版本配置。 Qwen官方github建议的版本要求如下: python 3.8 and above pytorch 1.12 and above, 2.0 and above are recommended transformers 4.32 and above CUDA 11.4 and above are recommended (this is for GPU users, flash-attention users, etc.) 代码和模型,采用的事Qwen,不是Qwen1.5,这两个是不同的代码仓库,一定要注意。 笔者的安装环境如下: win11 python 3.10.14 pytorch 2.2.0 transformers 4.39.0 CUDA 12.1 RTX 4060 Laptop 8GB RAM 32GB 安装步骤 第一步,下载Qwen代码 git clone https://github.com/QwenLM/Qwen.git 第二步,安装基础python包 pip install -r requirements.txt pip install -r requirements_web_demo.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 如果需要量化版,则需要安装对应的autogptq等; pip install auto-gptq optimum 第三步 下载模型权重 根据显存大小,选择合适的模型,这里选择1.8B进行使用,通过github信息,可大约知道不同尺寸模型显存需求。 Model Release Date Max Length System Prompt Enhancement # of Pretrained Tokens Minimum GPU Memory Usage of Finetuning (Q-Lora) Minimum GPU Usage of Generating 2048 Tokens (Int4) Tool Usage Qwen-1.8B 23.11.30 32K ✅ 2.2T 5.8GB 2.9GB ✅ Qwen-7B 23.08.03 32K ❎ 2.4T 11.5GB 8.2GB ✅ Qwen-14B 23.09.25 8K ❎ 3.0T 18.7GB 13.0GB ✅ Qwen-72B 23.11.30 32K ✅ 3.0T 61.4GB 48.9GB ✅ Qwen-1.8B-chat下载 下载有两个地方,分别是HF和国内的ModelScope社区 HuggingFace下载:https://huggingface.co/Qwen/Qwen-1_8B-Chat/tree/main ModelScope: https://www.modelscope.cn/models/qwen/Qwen-1_8B-Chat/files 模型除了权重文件,还有一系列配置文件等信息,因此需要完整的文件下载才可使用,鉴于网络问题,这里介绍两种下载方式: 方式一:纯命令行(看网速) git clone https://www.modelscope.cn/qwen/Qwen-1_8B-Chat.git 方式二:命令行 + 手动下载权重文件 git clone https://www.modelscope.cn/qwen/Qwen-1_8B-Chat.git 查看文件夹下配置文件是否下载完毕,下载完毕后,ctrl + c停止。 可手动下载两个.safetensors文件,放到文件夹中,手动下载.safetensors文件,速度高达10+MB/s 第四步:配置模型路径 修改 Qwen/cli_demo.py 中权重路径 DEFAULT_CKPT_PATH = r\"G:\\04-model-weights\\qwen\\Qwen-1_8B-Chat\" 第五步,运行cli_demo.py,看到客户端运行成功(通过日志看出,这里采用流式输出): C:\\Users\\yts32\\anaconda3\\envs\\pt220\\python.exe D:\\github_desktop\\Qwen\\cli_demo.py The model is automatically converting to bf16 for faster inference. If you want to disable the automatic precision, please manually add bf16/fp16/fp32=True to \"AutoModelForCausalLM.from_pretrained\". Try importing flash-attention for faster inference... Warning: import flash_attn rotary fail, please install FlashAttention rotary to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/rotary Warning: import flash_attn rms_norm fail, please install FlashAttention layer_norm to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/layer_norm Warning: import flash_attn fail, please install FlashAttention to get higher efficiency https://github.com/Dao-AILab/flash-attention Loading checkpoint shards: 100%|██████████| 2/2 [00:02 你好,你是谁? C:\\Users\\yts32\\.cache\\huggingface\\modules\\transformers_modules\\Qwen-1_8B-Chat\\modeling_qwen.py:528: UserWarning: 1Torch was not compiled with flash attention. (Triggered internally at C:\\cb\\pytorch_1000000000000\\work\\aten\\src\\ATen\\native\\transformers\\cuda\\sdp_utils.cpp:263.) attn_output = F.scaled_dot_product_attention( User: 你好,你是谁? Qwen-Chat: 我是 User: 你好,你是谁? Qwen-Chat: 我是来自 User: 你好,你是谁? 需要web ui界面,也可以修改web_demo.py的路径,然后运行代码即可 DEFAULT_CKPT_PATH = r\"G:\\04-model-weights\\qwen\\Qwen-1_8B-Chat\" 模型代码结构分析 为了详细了解Qwen模型代码的设计结构,了解大语言模型推理逻辑,在这里对Qwen的代码结构进行剖析。 首先关心的是,模型在哪里创建的,是什么形式,这个可从cli_demo.py的55行代码看到 model = AutoModelForCausalLM.from_pretrained( args.checkpoint_path, device_map=device_map, trust_remote_code=True, resume_download=True, ).eval() 其中AutoModelForCausalLM是transformers库的类,那么它和Qwen的关系又是怎么样的呢?下面通过UML类图来分析两者关系。 UML类图分两部分. 左边的_BaseAutoModelClass和AutoModelForCausalLM是transformers库的标准设计,基于transformers推理的LLM需要遵循这套规则。 右边则是Qwen的代码结构设计,包括: QWenLMHeadModel:最外层,用户调用的模型,通过HeadModel就知道这个模型是加入下游任务的head,可被用户使用的。 QWenPreTrainedModel:定义为所有预训练模型类 QWenModel:定义为基模型,可以看到在QWenLMHeadModel中被初始化为它的一个属性,用于基座推理,然后加上lm_head: nn.Linear进行具体的token分类任务。 再往上,就是transformers库的基础类了,包括PreTrainedModel、nn.Module, ModuleUtilsMixin, GenerationMixin, PushToHubMixin, PeftAdapterMixin。这些都是基础的必备模块。可以看到熟悉的pytorch的nn.Module,以及一系列Mixin类。 补充知识:Mixin类是一种设计模式,表示一些通用的功能放到这里,其他需要此功能的模块,通过继承的方式将这些共同行为混入到其他类中。 流式推理流程分析 通过cli_demo.py代码可看到,用户输入的字符串是通过这个chat_stream函数实现返回的,下面分析该函数实现的推理流程 def chat_stream( self, tokenizer: PreTrainedTokenizer, query: str, history: Optional[HistoryType], system: str = \"You are a helpful assistant.\", stop_words_ids: Optional[List[List[int]]] = None, logits_processor: Optional[LogitsProcessorList] = None, generation_config: Optional[GenerationConfig] = None, **kwargs, ) -> Generator[str, Any, None]: chat_stream源代码位于C:\\Users\\yts32.cache\\huggingface\\modules\\transformers_modules\\Qwen-7B-Chat-Int4\\modeling_qwen.py: 它不在本地仓库,也不在transformers库,这是因为HuggingFace提供的通用规则,允许用户自定义上传模型代码进行推理,因此基于本规则,Qwen的代码会变到.cache文件夹下进行运行。 核心参数说明: query:用户本轮对话所输入的信息 history: 历史对话信息,以list存储,包括用户输入的信息及用户收到的信息。(用户收到的信息不等价于模型输出信息) system:用于模型角色设定 generation_config: 是transformers库中一个模块,该类中存储大量与生成相关的配置,例如停止词、最大输出长度等 函数核心步骤: 第一步:生成本轮输入给模型的信息及tokens。 通过make_context函数,实现需要输入给模型的上下文信息组装,其核心功能包括: 设定system角色信息:例如,system \\n You are a helpful assistant. 拼接历史对话信息:例如,user 你是谁 assistant 我是来自阿里云的大规模语言模型,我叫通义千问。我可以回答问题、创作文字,还能表达观点、撰写代码。有什么我可以帮助你的吗? user 1+1= assistant 1+1=2。这是数学中的基本算术运算之一。如果您有任何其他问题或需要任何帮助,请随时告诉我。 将string转token 说明:模型输入的信息中,包含多个段落结构,主要包括三个角色的内容描述。各角色内容之间采用特殊分隔符来区分。Qwen采用的是、。 raw_text, context_tokens = make_context( tokenizer, query, history=history, system=system, max_window_size=max_window_size, chat_format=generation_config.chat_format, ) raw_text = 'system You are a helpful assistant. user 你好 assistant ' context_tokens = [151644, 8948, 198, 2610, 525, 264, 10950, 17847, 13, 151645, 198, 151644, 872, 198, 108386, 151645, 198, 151644, 77091, 198] 第二步,获取停止词。 LLM的生成是自回归的,通常设定一些停止词,当模型输出停止词时,程序会停止推理,返回本轮结果。 stop_words_ids = [[151645], [151644]] 第三步,进入生成器 stream_generator,开始逐token输出。 for token in self.generate_stream( input_ids, return_dict_in_generate=False, generation_config=stream_config, logits_processor=logits_processor, seed=-1, **kwargs): outputs.append(token.item()) yield tokenizer.decode(outputs, skip_special_tokens=True, errors='ignore') self.generate_stream方法会进入NewGenerationMixin(GenerationMixin)类中的方法进行生成,实际生成token函数是NewGenerationMixin.sample_stream(),这部分属于transformers_stream_generator库中功能。 注:NewGenerationMixin.sample_stream()位于site-packages\\transformers_stream_generator\\main.py 第四步:停止判断 通过判断上一个词是否为停止词,决定是否跳出while循环。 代码位于transformers_stream_generator\\main.py 中NewGenerationMixin.sample_stream() # if eos_token was found in one sentence, set sentence to finished if eos_token_id is not None: unfinished_sequences = unfinished_sequences.mul( (sum(next_tokens != i for i in eos_token_id)).long() ) # stop when each sentence is finished, or if we exceed the maximum length if unfinished_sequences.max() == 0 or stopping_criteria(input_ids, scores): if not synced_gpus: break else: this_peer_finished = True 总结一下,chat_stream()内部主要的步骤包括: 第一步:生成本轮输入给模型的信息及tokens。 第二步,获取停止词。 第三步,进入生成器 stream_generator,开始逐token输出。 第四步:停止条件判断。 多轮对话Prompt分析 通过代码分析,可了解到LLM模型每次推理都需要把上文信息全部输入,计算开销相当大,并且随着上下文长度增加,计算量呈平方级增长。 为了更好理解LLM实际的推理过程,下面分析模型的多轮对话过程。 第一轮对话 第一轮对话,用户输入“你是谁”,对于LLM而言,实际的输入信息包括角色描述、特殊分隔token、换行符。如下图所示: LLM模型执行第一次推理,获得“我”,然后将“我”拼接到第一次推理的输入,继续让LLM输出下一个token,直到第N次推理时,模型输出的是停止符号token,第一轮对话结束。 第二轮对话 第二轮对话,用户输入“1+1=”,对于LLM而言,它是无状态的,因此需要把前面对话信息一并带入,作为输入给模型的信息,如下图所示: 第一次推理,给模型输入的信息需要包括前序对话轮次的历史信息,加上本轮用户输入信息,经过N次LLM模型推理后,模型输出停止token,本轮对话结束。 通过多轮对话流程分析,可以知道: LLM是无状态的,多轮对话完全依赖于输入信息的拼接; LLM输入信息通常有三个部分。第一个部分,所有推理的开头是system的描述;第二个部分,用户的输入;第三个部分LLM回答的信息,称之为assistant。多轮对话时,把历史对话,循环的通过user、assistant进行拼接。 显存使用与文本长度分析 为了了解模型显存使用与文本长度的关系,这里做了文本长度与显存的统计,观察在transformers库进行LLM模型推理部署时,显存使用情况。(修改了的cli_demo.py在github上) 这里需要特别说明:LLM的推理部署针对显存使用有大量的优化策略和现成框架,这里的分析是针对transformers库,针对其他后端推理框架的适用性需要仔细考量。 LLM模型推理显存占用可分两个部分,模型参数和中间变量。模型参数加载后就占用固定空间,中间变量则会随着输入文本的长度增加和增加。(transformers是没有KV-Cache机制的,因此没有额外的显存消耗) 这里手动进行对话,并且记录每一轮对话结束后,输入+输出的字符串长度,显存使用情况,绘制折线图如下所示: 通过上图分析,可发现: 1.8B - fp16模型加载需要3.6GB显存; 3千字符之后,显存需求呈指数级增长,3千之前还可呈现线性增长趋势; Qwen测试代码有截断功能,即将超出显存上限8GB时,上下文被截断,以保障模型能运行。 为了进一步了解显存与上下文长度的关系,还进行了7B-int4模型的分析,结果如下图: 同样可得出上述的2和3两点结论。 在此处用的是文本长度,而不是tokens长度,是因为代码接口返回的只有文本,没有tokens_ids,但不影响分析结果的趋势。 详细代码参见 小结 本小节对Qwen-1.8B-chat-fp16模型进行了本地安装部署,以及Qwen系列LLM模型代码结构的剖析,同时还分析了LLM模型进行多轮对话时的处理逻辑。通过本节的学习,可以了解到: 通义千问是阿里Qwen团队的产品之一,还有其余9款基于大模型的产品。 Qwen最新的1.5版支持多尺寸、多精度版模型。 Qwen系列LLM模型支持三种角色,system, user, assistant。 LLM模型的多轮对话机制是将历史信息拼接起来,类似于微信的聊天记录。 LLM一次推理只输出一个token。 Qwen模型推理显存占用与上下文长度关系。 下一小节,将进行ChatGLM模型的部署及分析。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.2-chatglm.html":{"url":"chapter-10/10.2-chatglm.html","title":"10.2 ChatGLM3 部署与分析","keywords":"","body":"10.2 ChatGLM3 部署与分析 前言 本小节介绍国内大模型开源界的先驱,ChatGLM,其提供了多种开源大模型,以及工具调用功能。 本节将介绍ChatGLM发展历史,模型结构,prompt分析,显存分析等内容,帮助大家理解ChatGLM。 ChatGLM 简介 ChatGLM是由北京智谱华章科技有限公司开发的基于GLM(General Language Model )的对话模型。 ChatGLM目前(2024年4月16日)已发展到v4(未开源),其中v3版已开源,并且获得相当高的关注。 ChatGLM开源方面主打性价比,目前只有一个尺寸6B,但支持32K,128K不同上下文长度的版本。 智谱华章(智谱AI)公司2019年胎于清华大学的知识工程实验室(KEG),其技术和产品的发展与清华大学的研究成果紧密相关。清华大学的背景为智谱AI提供了强大的学术和技术支持。 除了ChatGLM,智谱AI还有CogVLM 、 CogAgent和CharacterGLM的开源,为人工智能开源社区的发展提供了帮助。产品方面,推出三大系列,大模型、AMiner学术情报收集、数字人产品。 智谱华章的价值观也非常好——让机器像人一样思考。 智谱的技术背景历史相对其他开源模型是比较长的,早在ChatGPT爆火之前,他们就已经发表了相关的文章,如2021年3月发表的《GLM: General Language Model Pretraining with Autoregressive Blank Infilling》(https://arxiv.org/pdf/2103.10360.pdf)、2022年10月发表的《Glm-130b: An open bilingual pre-trained model》(https://arxiv.org/pdf/2210.02414.pdf) 更多智谱AI信息可关注: github:https://github.com/THUDM/ChatGLM3 HF主页:https://huggingface.co/THUDM/ 官方文档:https://zhipu-ai.feishu.cn/wiki/WvQbwIJ9tiPAxGk8ywDck6yfnof 官方博客:https://zhipuai.cn/devday 公司首页:https://www.zhipuai.cn/ 本地部署安装 接下来就来本地化部署ChatGLM3-6B模型,试试效果 版本说明 ChatGLM3本地部署安装不复杂,首先看官方建议的环境要求: huggingface_hub>=0.19.4 pillow>=10.1.0 pyyaml>=6.0.1 requests>=2.31.0 ipykernel>=6.26.0 ipython>=8.18.1 jupyter_client>=8.6.0 本案例环境详情: Win11 、 RAM 32GB、RTX 4060 Laptop 8GB Python 3.10.14 transformers 4.38.2 pytorch 2.2.0 CUDA 12.1 操作步骤 第一步,下载代码仓库 git clone https://github.com/THUDM/ChatGLM3.git 第二步,下载模型 模型除了权重文件,还有一系列配置文件等信息,因此需要完整的文件下载才可使用,鉴于网络问题,这里介绍两种下载方式: 方式一:纯命令行(拼网速) git clone https://www.modelscope.cn/ZhipuAI/chatglm3-6b.git or git clone https://huggingface.co/THUDM/chatglm3-6b 方式二:命令行+ 手动下载权重文件 git clone https://www.modelscope.cn/ZhipuAI/chatglm3-6b.git 首先,查看文件夹下配置文件是否下载完毕,下载完毕后,ctrl + c停止。 然后,可手动下载.safetensors文件,放到文件夹中,手动下载.safetensors文件,速度高达10+MB/s 第三步,环境安装 pip install -r requirments.txt 第四步,设置模型路径 ChatGLM3\\basic_demo\\cli_demo.py中,修改 MODEL_PATH = r\"G:\\04-model-weights\\chatglm\\chatglm3-6b\" TOKENIZER_PATH = r\"G:\\04-model-weights\\chatglm\\chatglm3-6b\" 如果需要int4量化,则修改以下代码(不量化,需要7.9GB加载,int4量化,需要4.7GB加载) model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True).quantize(bits=4, device=\"cuda\").cuda().eval() # add .quantize(bits=4, device=\"cuda\").cuda() before .eval() to use int4 model 第五步,运行代码 进入虚拟环境,运行basic_demo\\cli_demo.py, terminal中显示如下信息表明加载成功,加载完成后,显存占用4678MB。 C:\\Users\\yts32\\anaconda3\\envs\\chatglm\\python.exe D:\\github_desktop\\ChatGLM3\\basic_demo\\cli_demo.py Setting eos_token is not supported, use the default one. Setting pad_token is not supported, use the default one. Setting unk_token is not supported, use the default one. Loading checkpoint shards: 100%|██████████| 7/7 [00:16 对于6B模型,笔记本级显卡就非常慢,已经到达用户无法接受的程度,解码时延约1.5s/token,通常LLM服务的解码时延在50ms。 这或许与代码有关,在baichuan2的7B-int4模型中,速度还是合理的。 模型结构分析 为了详细了解ChatGLM3模型代码的设计结构,接下来分析模型UML类图结构。 首先关心的是,模型在哪里创建的,是什么形式,这个可从ChatGLM3\\basic_demo\\cli_demo.py的12行代码看到 model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True).quantize(bits=4, device=\"cuda\").cuda().eval() 这里基于transformers库的规则采用AutoModel类的接口进行模型初始化,内部实际调用为用户自定义的模型类。 最终调用的是C:\\Users\\yts32.cache\\huggingface\\modules\\transformers_modules\\chatglm3-6b\\modeling_chatglm.py当中的ChatGLMForConditionalGeneration。 其内部关系如下图所示: UML类图分两部分 左边的_BaseAutoModelClass和AutoModelForCausalLM是transformers库的标准设计,基于transformers推理的LLM需要遵循这套规则。 右边则是ChatGLM的代码结构设计,包括: ChatGLMForConditionalGeneration:提供用户使用的类,有stream_chat、chat这两个对话接口 ChatGLMPreTrainedModel:所有预训练模型基类,提供通用接口,如get_position_ids、get_masks ChatGLMModel:LLM基模型,在ChatGLMForConditionalGeneration中被实例化为transformer属性,可理解为一个神经网络模型。 再往上,就是transformers库的基础类了,包括PreTrainedModel、nn.Module, ModuleUtilsMixin, GenerationMixin, PushToHubMixin, PeftAdapterMixin。这些都是基础的必备模块。可以看到熟悉的pytorch的nn.Module,以及一系列Mixin类。 补充知识:Mixin类是一种设计模式,表示一些通用的功能放到这里,其他需要此功能的模块,通过继承的方式将这些共同行为混入到其他类中。 流式推理流程分析 cli_demo.py采用的是流式返回,推理流程在.cache\\huggingface\\modules\\transformers_modules\\chatglm3-6b\\modeling_chatglm.py 中ChatGLMForConditionalGeneration.stream_generate() LLM输出的过程采用while循环进行控制,当达到停止条件时,break跳出循环。 下面分析stream_generate()中while循环里的代码,可以分为四个步骤 获取LLM的输入,执行推理,即outputs = self(xxx) 采样,获取本次推理得到的tokens yield 抛出结果 判断是否已停止,对于生成器而言,下一次进入循环会到yield之后的代码。 while True: # ================================== step1 ================================== # 获取LLM模型需要的输入,例如,input_ids, position_ids, att_mask等 model_inputs = self.prepare_inputs_for_generation(input_ids, **model_kwargs) # forward pass to get next token # LLM 模型的一次推理,输出1个向量,在此处为 1*65024维的向量,表明词表大小为65024。 outputs = self( **model_inputs, return_dict=True, output_attentions=False, output_hidden_states=False, ) # ================================== step2 ================================== next_token_logits = outputs.logits[:, -1, :] # pre-process distribution next_token_scores = logits_processor(input_ids, next_token_logits) next_token_scores = logits_warper(input_ids, next_token_scores) # sample probs = nn.functional.softmax(next_token_scores, dim=-1) # 未采用温度惩罚 if generation_config.do_sample: next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1) # 这里采用top-p=1 else: next_tokens = torch.argmax(probs, dim=-1) # update generated ids, model inputs, and length for next step input_ids = torch.cat([input_ids, next_tokens[:, None]], dim=-1) model_kwargs = self._update_model_kwargs_for_generation( outputs, model_kwargs, is_encoder_decoder=self.config.is_encoder_decoder ) unfinished_sequences = unfinished_sequences.mul( next_tokens.tile(eos_token_id_tensor.shape[0], 1).ne(eos_token_id_tensor.unsqueeze(1)).prod(dim=0) ) # ================================== step3 ================================== if return_past_key_values: yield input_ids, outputs.past_key_values else: yield input_ids # ================================== step0 ================================== # stop when each sentence is finished, or if we exceed the maximum length if unfinished_sequences.max() == 0 or stopping_criteria(input_ids, scores): break prompt结构分析 ChatGLM3 prompt结构由对话头和内容组成,一个典型的多轮对话结构如官方文档所示: You are ChatGLM3, a large language model trained by Zhipu.AI. Follow the user's instructions carefully. Hello Hello, I'm ChatGLM3. What can I assist you today? 对比Qwen的结构如下: system You are a helpful assistant. user 你是谁 assistant ChatGLM3 为了支持Code Interpreter,Tool & Agent 等任务的输入,还增加了一个角色,总共支持四种角色,具体是: :系统信息,设计上可穿插于对话中,但目前规定仅可以出现在开头 :用户 不会连续出现多个来自 的信息 :AI 助手 在出现之前必须有一个来自 的信息 :外部的返回结果 必须在 的信息之后 的加入,使得ChatGLM3具备更丰富的功能,可以让LLM加上“四肢“,让大模型学会使用工具。 工具功能是一种扩展机制,它允许大模型通过调用外部工具来增强自身的能力,解决一些由于训练数据限制或专业领域知识缺乏而无法独立完成的任务。 常用的工具有计算器、搜索引擎、天气查询等,这个部分设计Agent的内容,此处暂不展开。 显存使用与上下文长度分析 本案例中并未能正确分析出上下文长度与显存占用的情况,出现了一些奇怪现象,这里总结如下: int4 启动需要4.6GB显存 上下文增加到3000时,显存仅增加了100MB 上下文超3500之后,显存不再增加,应该与上下文截断有关 此处,未进一步查看源代码寻找原因,毕竟这里是demo,生产部署会采用其他推理框架。 在cli_demo.py代码后加入如下代码,可进行统计,完整代码位于github # 统计文本长度 conversation_length = sum([len(content['content']) for content in history]) import subprocess import json result = subprocess.run(['gpustat', '--json'], stdout=subprocess.PIPE) output = result.stdout.decode() data = json.loads(output) used_memory = data['gpus'][0]['memory.used'] f.writelines(\"{}, {}\\n\".format(conversation_length, used_memory)) f.flush() 在此处用的是文本长度,而不是tokens长度,是因为代码接口返回的只有文本,没有tokens_ids,但不影响分析结果的趋势。 小结 本节详细介绍了ChatGLM的背景与本地部署实战,包括以下核心内容: ChatGLM3模型介绍,是基于GLM(General Language Model)的对话模型,目前发展到v4版本,v3版本已开源,获得相当高的关注 本地部署ChatGLM3-6B模型详细步骤,包括环境配置介绍 ChatGLM3-6B模型结构分析:对ChatGLM3的模型结构进行了详细分析,包括其类图结构、流式推理流程以及prompt结构等 显存分析:分析了显存使用与上下文长度的关系 下一节将介绍百川2的本地部署 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.3-baichuan.html":{"url":"chapter-10/10.3-baichuan.html","title":"10.3 Baichuan2 部署与分析","keywords":"","body":"10.3 Baichuan2 部署与分析 baichuan作为首批开源的国产大语言模型,具备7B和13B两个尺寸,在多个子任务上有出色表现,本节就来了解baichuan系列大模型。 Baichuan 简介 Baichuan开源大模型由百川智能研发,目前最新开源版本为Baichuan2,闭源版本为Baichuan3。 百川智能成立于2023年4月10日,由前搜狗公司CEO王小川创立。公司以帮助大众轻松、吾普惠地获取世界知识和专业服务为使命,致力于通过语言AI的突破,构建中国最优秀的大模型底座。 Baichuan2提供7B,13B两个尺寸,具体如下 基座模型 对齐模型 对齐模型 4bits 量化 7B Baichuan2-7B-Base Baichuan2-7B-Chat Baichuan2-7B-Chat-4bits 13B Baichuan2-13B-Base Baichuan2-13B-Chat Baichuan2-13B-Chat-4bits 更多关于Baichuan的信息,可查阅: 公司首页:https://www.baichuan-ai.com/home github: https://github.com/baichuan-inc 技术报告:https://arxiv.org/abs/2309.10305 本地部署安装 第一步,下载下载baichuan2代码 git clone https://github.com/baichuan-inc/Baichuan2 第二步,下载7B-in4模型权重 git clone https://huggingface.co/baichuan-inc/Baichuan2-7B-Chat-4bits (也可以通过github desktop下载) 第三步,环境配置 根据Baichuan2中的 requirements.txt进行安装,其中pytorch环境自行配置,要求 pytorch ≥ 2.x pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 第四步,报错处理 根据官方的教程,安装了requirements.txt后报错,通常会报错: init model ... A matching Triton is not available, some optimizations will not be enabled Traceback (most recent call last): File \"C:\\Users\\yts32\\anaconda3\\envs\\pt220\\lib\\site-packages\\xformers\\__init__.py\", line 55, in _is_triton_available from xformers.triton.softmax import softmax as triton_softmax # noqa File \"C:\\Users\\yts32\\anaconda3\\envs\\pt220\\lib\\site-packages\\xformers\\triton\\softmax.py\", line 11, in import triton ModuleNotFoundError: No module named 'triton' C:\\Users\\yts32\\anaconda3\\envs\\pt220\\lib\\site-packages\\bitsandbytes\\cuda_setup\\main.py:166: UserWarning: Welcome to bitsandbytes. For bug reports, please run python -m bitsandbytes 如果是linux,可以尝试 pip install bitsandbytes==0.41.1 -q pip install accelerate==0.25.0 -q 参考自:https://github.com/baichuan-inc/Baichuan2/issues/52 如果是windows,可以尝试,先下载bitsandbytes-windows版的0.41.1的安装包,再手动安装。原因是通过pip install bitsandbytes,只能获得linux的,而windows的目前最高版本时0.37.x,因此需要手动下载安装。 https://github.com/jllllll/bitsandbytes-windows-webui/releases/download/wheels/bitsandbytes-0.41.1-py3-none-win_amd64.whl pip install bitsandbytes-0.41.1-py3-none-win_amd64.whl 参考自:https://github.com/baichuan-inc/Baichuan2/issues/35 如果报错:TypeError: 'NoneType' object is not subscriptable pip install accelerate==0.25.0 -q 如果报错:auto-gptq 0.7.1 requires accelerate>=0.26.0, but you have accelerate 0.25.0 which is incompatible. https://github.com/AutoGPTQ/AutoGPTQ pip install auto-gptq==0.6 第五步,配置路径 model, model.generation_config, tokenizer三个的路径需要配置 def init_model(): print(\"init model ...\") model = AutoModelForCausalLM.from_pretrained( r\"G:\\04-model-weights\\Baichuan2-7B-Chat-4bits\", torch_dtype=torch.float16, device_map=\"auto\", trust_remote_code=True ) model.generation_config = GenerationConfig.from_pretrained( r\"G:\\04-model-weights\\Baichuan2-7B-Chat-4bits\" ) tokenizer = AutoTokenizer.from_pretrained( r\"G:\\04-model-weights\\Baichuan2-7B-Chat-4bits\", use_fast=False, trust_remote_code=True ) return model, tokenizer 第六步,运行cli_demo.py C:\\Users\\yts32\\anaconda3\\envs\\chatglm\\python.exe D:\\github_desktop\\Baichuan2\\cli_demo.py init model ... bin C:\\Users\\yts32\\anaconda3\\envs\\chatglm\\lib\\site-packages\\bitsandbytes\\libbitsandbytes_cuda121.dll 欢迎使用百川大模型,输入进行对话,vim 多行输入,clear 清空历史,CTRL+C 中断生成,stream 开关流式生成,exit 结束。 用户:你好 Baichuan 2: 你好今天我能为您提供什么帮助? 用户:你是谁 Baichuan 2:我是百川大模型,是由百川智能的工程师们创造的大语言模型,我可以和人类进行自然交流、解答问题、协助创作,帮助大众轻松、普惠的获得世界知识和专业服务。如果你有任何问题,可以随时向我提问 模型结构分析 Baichuan2的模型结构可通过如下UML类图了解,其他更多模型结构可以参考前两节Qwen和ChatGLM的结构分析。 Prompt 结构分析 baichuan2的Prompt结构是经典的三角色设计,包括system, user, assistant。 在示例代码中,并没有给出system的预设,需要分析源代码后才看到system可通过messages来维护。bichuan2中的messages等同于history的作用,用于记录历史对话信息。 一个真实的messages如下: [{'content': '你好', 'role': 'user'}, {'content': '你好今天我能为您提供什么帮助?', 'role': 'assistant'}, {'content': '今天天气如何', 'role': 'user'}] 特殊token处理 不同的角色之间,通常用特殊token标记,在baichun2代码中,可通过generation_config中看到特殊token的index,但对应的text没有显示给出。 \\.cache\\huggingface\\modules\\transformers_modules\\Baichuan2-7B-Chat-4bits\\generation_utils.py # 以下代码是组装历史对话的代码段,首先判断当前角色,然后获取角色分隔token for message in round: if message[\"role\"] == \"user\": round_tokens.append(model.generation_config.user_token_id) else: round_tokens.append(model.generation_config.assistant_token_id) 单轮推理长度限制 模型支持的上下文是4K,这里包括输入+输出=4K,在单轮对话时,会对输入长度做限制。 首先,预留2K是用于本轮对话的输出,因此输入的最大长度为4K-2K=2K。详细代码如下: max_input_tokens = model.config.model_max_length - max_new_tokens input_tokens = input_tokens[-max_input_tokens:] # truncate left 其中: model.config.model_max_lengt = 4096 max_new_tokens = 2048 参考自:C:\\Users\\yts32\\.cache\\huggingface\\modules\\transformers_modules\\Baichuan2-7B-Chat-4bits\\generation_utils.py 对于输入超过2K的情况,是会被向左截断。 显存与上下文长度分析 百川官方给出了字符串长度与token之间的换算的比例,一般情况下Baichuan2大模型1个token约等于1.5个中文汉字。详见产品定价:https://cq6qe6bvfr6.feishu.cn/wiki/DOxNw9t97iwL3hkPB41ctfsMnMI 通过分析发现: 在未进行对话时,显存仅占用5.3GB 第一次对话时,显存立即飙升到6.4GB 前2000字符显存消耗不高,2000之后显存消耗激增 超过3500字符后,同样出现了截断(参考Qwen、ChatGLM的分析) 统计代码如下,完整代码cli_demo.py位于github conversation_length = sum([len(content['content']) for content in messages]) import subprocess import json result = subprocess.run(['gpustat', '--json'], stdout=subprocess.PIPE) output = result.stdout.decode() data = json.loads(output) used_memory = data['gpus'][0]['memory.used'] f.writelines(\"{}, {}\\n\".format(conversation_length, used_memory)) f.flush() 小结 本节对Baichuan2模型进行了本地部署安装,并分析模型结构、prompt结构、推理上限机制、显存分析等内容,有助于进一步理解LLM原理。 下一小节,分析Yi。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.4-yi.html":{"url":"chapter-10/10.4-yi.html","title":"10.4 Yi 部署与分析","keywords":"","body":"10.4 Yi 部署与分析 Yi 简介 Yi是由零一万物开源的大语言模型,目前(2024年4月16日)包括6B和34B-chat版本,base版本有6B、9B和34B。 零一万物是2023年7月,李开复筹组新公司,部注册于北京,集中在大模型技术、人工智能算法、自然语言处理、系统架构、算力架构、数据安全、产品研发等领域。 更多信息: 公司官网:https://www.lingyiwanwu.com/ HF: https://huggingface.co/01-ai github: https://github.com/01-ai LLM-github:https://github.com/01-ai/Yi 技术报告:https://arxiv.org/abs/2403.04652 部署安装 第一步,代码下载 git clone https://github.com/01-ai/Yi.git 第二步,权重下载 git clone https://www.modelscope.cn/01ai/Yi-6B-Chat-4bits.git 第三步,量化环境安装 int4和int8分别需要AWQ和GPTQ环境 pip install autoawq https://github.com/casper-hansen/AutoAWQ?tab=readme-ov-file#install-from-pypi https://github.com/AutoGPTQ/AutoGPTQ?tab=readme-ov-file#quick-installation 第四步,配置代码中模型路径 parser.add_argument( \"-c\", \"--checkpoint-path\", type=str, default=r\"G:\\04-model-weights\\Yi-6B-Chat-4bits\", help=\"Checkpoint name or path, default to %(default)r\", ) default=r\"G:\\04-model-weights\\Yi-6B-Chat-4bits\", 第五步,运行代码 Yi没有提供命令行的交互demo,提供的事web ui版,运行Yi\\demo\\web_demo.py 即可跳出基于gradio的交互界面。 修改后的代码可参考github 模型UML分析 Yi的github仓库及模型权重仓库均未找到类似Qwen、ChatGLM、Baichuan那样的模型文件,因此无法深入探究模型结构。 为了探究Yi模型的推理步骤,debug观察到以下信息,供流程分析所用。 model = AutoModelForCausalLM.from_pretrained 获得的模型是LlamaForCausalLM类,其中的核心model是LlamaModel LlamaForCausalLM( (model): LlamaModel( (embed_tokens): Embedding(64000, 4096) (layers): ModuleList( (0-31): 32 x LlamaDecoderLayer( (self_attn): LlamaSdpaAttention( (q_proj): WQLinear_GEMM(in_features=4096, out_features=4096, bias=False, w_bit=4, group_size=128) (k_proj): WQLinear_GEMM(in_features=4096, out_features=512, bias=False, w_bit=4, group_size=128) (v_proj): WQLinear_GEMM(in_features=4096, out_features=512, bias=False, w_bit=4, group_size=128) (o_proj): WQLinear_GEMM(in_features=4096, out_features=4096, bias=False, w_bit=4, group_size=128) (rotary_emb): LlamaRotaryEmbedding() ) (mlp): LlamaMLP( (gate_proj): WQLinear_GEMM(in_features=4096, out_features=11008, bias=False, w_bit=4, group_size=128) (up_proj): WQLinear_GEMM(in_features=4096, out_features=11008, bias=False, w_bit=4, group_size=128) (down_proj): WQLinear_GEMM(in_features=11008, out_features=4096, bias=False, w_bit=4, group_size=128) (act_fn): SiLU() ) (input_layernorm): LlamaRMSNorm() (post_attention_layernorm): LlamaRMSNorm() ) ) (norm): LlamaRMSNorm() ) (lm_head): Linear(in_features=4096, out_features=64000, bias=False) ) 在模型权重的config.json中,体现模型架构为LlamaForCausalLM { \"architectures\": [ \"LlamaForCausalLM\" ], \"attention_bias\": false, \"bos_token_id\": 1, \"eos_token_id\": 2, \"hidden_act\": \"silu\", \"hidden_size\": 4096, \"initializer_range\": 0.02, \"intermediate_size\": 11008, \"max_position_embeddings\": 4096, \"model_type\": \"llama\", \"num_attention_heads\": 32, \"num_hidden_layers\": 32, \"num_key_value_heads\": 4, \"pretraining_tp\": 1, \"quantization_config\": { \"bits\": 4, \"group_size\": 128, \"quant_method\": \"awq\", \"version\": \"gemm\", \"zero_point\": true }, \"rms_norm_eps\": 1e-05, \"rope_scaling\": null, \"rope_theta\": 5000000.0, \"tie_word_embeddings\": false, \"torch_dtype\": \"float16\", \"transformers_version\": \"4.35.0\", \"use_cache\": true, \"vocab_size\": 64000 } Prompt 结构分析 web_demo.py代码结构整体基于transformers库的工具来实现,推理采用流处理,基于transformers的TextIteratorStreamer实现,模型单次推理由TextIteratorStreamer代理,这里不深入。 这里看看Yi源代码中的predict函数,该函数对历史对话进行了处理,实现多轮对话的Prompt处理。大体可分为4步: 第一步:将历史信息转为模型输入的tokens_ids, 这一步调用transformers的apply_chat_template接口功能实现; 第二步:创建流处理器TextIteratorStreamer 第三步:组装本轮对话所需信息,generate_kwargs 第四步:启动线程执行model.generate, 从流处理器streamer中拿单次推理的结果 由于大部分是transformers库的代码,此处就不深入展开了 def predict(history, max_length, top_p, temperature): stop = StopOnTokens() messages = [] for idx, (user_msg, model_msg) in enumerate(history): if idx == len(history) - 1 and not model_msg: messages.append({\"role\": \"user\", \"content\": user_msg}) break if user_msg: messages.append({\"role\": \"user\", \"content\": user_msg}) if model_msg: messages.append({\"role\": \"assistant\", \"content\": model_msg}) print(\"\\n\\n====conversation====\\n\", messages) model_inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_tensors=\"pt\" ).to(next(model.parameters()).device) streamer = TextIteratorStreamer( tokenizer, timeout=60, skip_prompt=True, skip_special_tokens=True ) generate_kwargs = { \"input_ids\": model_inputs, \"streamer\": streamer, \"max_new_tokens\": max_length, \"do_sample\": True, \"top_p\": top_p, \"temperature\": temperature, \"stopping_criteria\": StoppingCriteriaList([stop]), \"repetition_penalty\": 1.2, } t = Thread(target=model.generate, kwargs=generate_kwargs) t.start() for new_token in streamer: if new_token != \"\": history[-1][1] += new_token yield history 小结 通过Yi的代码,可以了解如何快速基于transformers构建一个LLM推理部署代码。 并且可以了解GPTQ和AWQ的部署需要单独安装对应的python库。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.5-gpt-academic.html":{"url":"chapter-10/10.5-gpt-academic.html","title":"10.5 GPT Academic 安装与使用","keywords":"","body":"10.5 GPT Academic 安装与使用 前言 本节介绍一个LLM的具体应用场景,也是热门、火爆的应用和场景,目前(2024年4月18日)github已经有54K stars。 希望通过这个应用案例,介绍LLM的能力、LLM的作用,帮助大家找到LLM用武之地。 本节将介绍本地部署 gpt academic工具,它是为GPT/GLM等LLM大语言模型提供实用化交互接口,特别优化论文阅读/润色/写作体验,模块化设计,支持自定义快捷按钮&函数插件,支持Python和C++等项目剖析&自译解功能,PDF/LaTex论文翻译&总结功能,支持并行问询多种LLM模型,支持chatglm3等本地模型。接入通义千问, deepseekcoder, 讯飞星火, 文心一言, llama2, rwkv, claude2, moss等。 更多信息查阅github:https://github.com/binary-husky/gpt_academic 本地安装 第一步,下载代码 git clone https://github.com/binary-husky/gpt_academic 第二步,安装环境 第一步:python -m pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ 第二步:python -m pip install -r request_llms/requirements_qwen_local.txt 第三步,修改配置文件 这里采用本地Qwen模型,当然大家有API_KEY,那用在线模型就更方便了。(本地qwen模型版本的config文件放在github了) 3.1 修改config.py,设置本地模型列表,将qwen添加进去 # [step 3]>> 模型选择是 (注意: LLM_MODEL是默认选中的模型, 它*必须*被包含在AVAIL_LLM_MODELS列表中 ) LLM_MODEL = \"gpt-3.5-turbo-16k\" # 可选 ↓↓↓ AVAIL_LLM_MODELS = [\"gpt-4-1106-preview\", \"gpt-4-turbo-preview\", \"gpt-4-vision-preview\", \"gpt-3.5-turbo-1106\", \"gpt-3.5-turbo-16k\", \"gpt-3.5-turbo\", \"azure-gpt-3.5\", \"gpt-4\", \"gpt-4-32k\", \"azure-gpt-4\", \"glm-4\", \"glm-3-turbo\", \"gemini-pro\", \"chatglm3\", \"qwen-local\" ] 3.2. 配置本地模型的路径,此路径是模型权重路径 QWEN_LOCAL_MODEL_SELECTION = r\"G:\\04-model-weights\\qwen\\Qwen-1_8B-Chat\" 第四步,运行程序 运行后跳出主页面,左上角选择qwen-local,即可进行对话。 python main.py 选用在线模型的配置步骤如下: 第一步:api key配置 config.py配置api key到API_KEY变量里面 第二步:修改重定向URL API_URL_REDIRECT = {\"https://api.openai.com/v1/chat/completions\": \"https://api.chatanywhere.com.cn/v1/chat/completions\"} 第三步:修改代理配置 USE_PROXY = True if USE_PROXY: proxies = { # [协议]:// [地址] :[端口] # \"http\": \"socks5h://localhost:11284\", # 再例如 \"http\": \"http://127.0.0.1:7890\", # \"https\": \"socks5h://localhost:11284\", # 再例如 \"https\": \"http://127.0.0.1:7890\", \"http\": \"http://127.0.0.1:7890\", \"https\": \"http://127.0.0.1:7890\", } 界面使用介绍 gpt academic的页面功能按钮比较多,需要了解各功能模块,方便使用。大体可以分为4个区域,如下图的红色、黄色、蓝色和绿色区域所示: 红色:菜单栏,可上传文档、压缩包,批量处理的文档、代码,通过这里上传;更换大语言模型;修改界面外观 黄色:对话区,所有的对话交互在这里显示 蓝色:用户文本输入区,主要是一些对话、问题的输入,文档和代码通过模块进行传入 绿色:核心功能区域,本项目的核心在此处,提供了多种辅助功能。这些功能主要内容是帮用户撰写好了提示词,便于用户一键发送prompt。 gpt academic项目主要作用有两个: 内置了一系列提示词模板,帮助用户快速完成学术方面的对话任务。 实现各类批量处理操作,提高用户使用LLM效率,例如批量总结PDF文档、解析代码文件夹等,感受下来是将一些列python处理的自动化工具集成好,变为按钮方式提供用户使用,这部分才是该项目真正的生产力体现。 下面先看基础功能的使用: 学术语料润色 上文提到,基础功能主要是内置了prompt,让我们只需要点击按钮,即可发送响应指令,快速使用LLM功能。 这里的学术语料润色按钮,点击之后,会将以下提示词+输入区的文本,一并发送给LLM,以此完成润色功能。 内置prompt: 作为一名中文学术论文写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性,同时分解长句,减少重复,并提供改进建议。请先提供文本的更正版本,然后在markdown表格中列出修改的内容,并给出修改的理由: 页面效果如下图所示: 第一步,在输入区输入希望润色的文字内容; 第二步,点击学术语料润色按钮; 第三步,对话框会输出结果。 通过一个案例,大体可基础功能的使用,不同的功能,内置了不同的提示词模板,加快用户与LLM对话的过程,提高工作效率。 查找语法错误 Help me ensure that the grammar and the spelling is correct. Do not try to polish the text, if no mistake is found, tell me that this paragraph is good. If you find grammar or spelling mistakes, please list mistakes you find in a two-column markdown table, put the original text the first column, put the corrected text in the second column and highlight the key words you fixed. Finally, please provide the proofreaded text. Example: Paragraph: How is you? Do you knows what is it? | Original sentence | Corrected sentence | | :--- | :--- | | How is you? | How are you? | | Do you knows what is it? | Do you know what it is ? | Below is a paragraph from an academic paper. You need to report all grammar and spelling mistakes as the example before. 中译英 Please translate following sentence to English: 学术英中互译 I want you to act as a scientific English-Chinese translator, I will provide you with some paragraphs in one language and your task is to accurately and academically translate the paragraphs only into the other language. Do not repeat the original provided paragraphs after translation. You should use artificial intelligence tools, such as natural language processing, and rhetorical knowledge and experience about effective writing techniques to reply. I'll give you my paragraphs as follows, tell me what language it is written in, and then translate: 解释代码 请解释以下代码: 进阶功能主要包括一系列批量操作的功能,例如批量总结Word文档、批量Markdown中译英、解析代码项目文档等。 处理批量操作外,还有一系列通过代码实现的实用功能,如一键下载arXiv论文、全文润色、绘制脑图等。 下面先了解批量处理的功能 批量总结Word文档 该功能会对上传的所有word文档逐一总结,每个word文档会根据LLM的上下文长度配置进行阶段,过长的文档被切分为多个片段。 使用过程第一步,左上角上传文件中,上传所有word文档 第二步,点击 第三步,等待处理结束,在右下角找到“文件下载区”,即可下载总结好的文本内容。 该功能提供的prompt为: i_say = f'请对下面的文章片段用中文做概述,文件名是{os.path.relpath(fp, project_folder)},文章内容是 ```{paper_frag}```' 如果文章被切分,还会对所有概述内容进行一次文章总结。 i_say = f\"根据以上的对话,总结文章{os.path.abspath(fp)}的主要内容。\" 运行完毕,结果会以markdown格式生成文件,在右下角可下载。 解析整个Python项目 除了对文档(word、pdf)的批量总结,gpt academic还支持对代码项目的解析总结,目前支持python, Matlab, c++, Go, Rust, Java, js, ts, css, Lua, CSharp。 使用步骤: 第一步,将项目代码压缩为zip,然后在上传文件区域上传 第二步,点击,则自动开始解析。 这里采用gpt academic项目代码为例,看看解析效果: 逐个文件总结: [6/7] 请对下面的程序文件做一个概述: private_upload\\default_user\\2024-04-18-10-26-51\\gpt_academic.zip.extract\\gpt_academic\\crazy_functions__init__.py 这是一个Python脚本,它使用OpenAI的GPT-3.5-Turbo模型进行微调,并将生成的JSON数据保存到指定的目录中。 定义了一个fetch_items函数,该函数接受一组列表作为输入,每个列表代表一个待处理的文件。对于每一个文件,该函数会读取其内容并将其转换为字符串格式。 ...略... 这个脚本应该可以作为一个简化的示例来了解如何使用OpenAI的预训练语言模型来进行微调,并将其部署到生产环境中。 所有py汇总 逐个文件分析已完成。D:\\github_desktop\\gpt_academic\\gpt_log\\default_user\\shared\\GPT-Academic-2024-04-18-10-33-40.md 正在开始汇总。 这个Python程序是一个网络爬虫,用于抓取HTML内容并从中提取有用的信息。这个程序使用了requests库来发送HTTP请求,并使用BeautifulSoup库来解析这些请求的响应。此外,这个程序还使用了pandas库来处理大量的CSV文件,并将它们转换为字典,以便进行进一步的数据挖掘。 ...略... 最后,当一切顺利时,我们停止爬取流程,并从服务器上解压tar.gz文件,得到原始的CSV文件内容。我们可以将这些内容存储在一个数据库中,以便后续使用。 最后还有一个基于mermaid软件的脑图展示,非常直观。(脑图只是一部分) Prompt逻辑分析 首先会设定system role sys_prompt_array.append(\"你是一个程序架构分析师,正在分析一个源代码项目。你的回答必须简单明了。\") 用户prompt,针对单个文件 prefix = \"接下来请你逐文件分析下面的工程\" i_say = prefix + f'请对下面的程序文件做一个概述文件名是{os.path.relpath(fp, project_folder)},文件代码是 ```{file_content}```' 用户prompt,针对项目维度 i_say = f'用一张Markdown表格简要描述以下文件的功能:{focus}。根据以上分析,用一句话概括程序的整体功能。' summary = \"请用一句话概括这些文件的整体功能。\\n\\n\" + diagram_code 通过源代码的分析,可知道处理逻辑是,先对每个代码文件进行概述,然后汇总所有代码文件的概述内容,再让LLM进行概括和markdown表格的输出。 下载arXiv论文并翻译摘要 可下载arxiv论文pdf文件,还可以将摘要翻译为中文,加快研究者下载文章、挑选文章的速度。 使用非常简单,第一步,在输入区输入编号,第二步,插件中找到一键下载arxiv论文并翻译摘要(先在input输入编号如1812.10695,点击启动。 内置prompt如下 sys_prompt=\"Your job is to collect information from materials and translate to Chinese。\", f\"请你阅读以下学术论文相关的材料,提取摘要,翻译为中文。材料如下:{str(info)}\" 基于知识库问答 下面介绍一个高级功能,本地知识库的功能,常用的场景是,基于论文PDF进行问答、基于内部知识文档进行问答。 这部分是借助了langchain的功能,实现了一个简易版本的RAG,从而让用户可以基于上传的文档进行对话。 整个过程分两步,第一步构建知识库,第二步知识库文件注入。这两步需要两个不同的插件功能。 由于issue上有大量不成功案例和问题未解决,同时此处尝试解决4个error后仍不能成功,在这里不进行此处的演示,其背后逻辑与langchain那套类似的,想要体验基于文档的问答,可关注fastgpt、chachat这两个项目。 项目工程结构分析 gpt academic项目提供的功能有好几十个,这些都是开源的力量,众人拾柴火焰高。 为了能让大家贡献插件,项目提供了功能设计的模板,并且统一存放在crazy_functions文件夹,其中配套使用的一些功能函数存放在toolbox.py,LLM模型适配存放在request_llms文件夹。 代码结构相对清晰,模块化程度高,值得学习,但是代码格式风格上可以再优雅一些(笑哭)。 这里采用功能,对gpt academic项目进行了解读,耗时数小时,花费上万token,内容较多,这里就放到云盘,大家下载html来查看吧。 提取码:epfq 小结 本小节介绍了LLM模型的具体应用——学术生产力工具(gpt academic),通过本小节不仅了解学术研究的先进生产力,更了解了LLM是如何辅助人们提高生产效率。这也是当下LLM发展的关键,找到关键场景,关键痛点,然后利用LLM的能力进行改造、改进,从而实现LLM的落地。 如果没有痛点场景,关键问题的发现,那么LLM仅是一个简单的对话机器人,无法发挥TA的价值。通过本节、本章的内容,希望大家能了解LLM的概念,能力边界,清晰的认识到场景定义LLM,场景结合LLM,场景+LLM,一切场景先行,而不是拿着LLM去找场景。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-11/":{"url":"chapter-11/","title":"第十一章 ONNX 使用","keywords":"","body":"第十一章 ONNX 使用 第十一章 ONNX 使用 11.1 ONNX 简介与安装 11.2 ONNX Runtime 简介与使用 11.3 ONNX Runtime 进阶使用 第十一章简介 本章介绍模型部署的第一个工具——ONNX。 ONNX (Open Neural Network Exchange,开放神经网络交换格式)是一种开放的、跨平台的深度学习模型交换格式,可以方便地将模型从一个框架转移到另一个框架,可谓是模型部署必须要了解的一个工具。 本章将从ONNX的概念及原理介绍开始,再介绍ONNX配套的推理引擎——ONNXRuntime, 最后介绍ONNXRuntime中常用的优化方法(float16量化、int8量化、混合精度量化、计算图优化、线程管理和IO binding)。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-11/11.1-onnx-introduction.html":{"url":"chapter-11/11.1-onnx-introduction.html","title":"11.1 ONNX 简介与安装","keywords":"","body":"11.1 ONNX 简介与安装 前言 在深度学习算法开发过程中,模型训练与部署是两个环节,pytorch通常只用于训练,获得模型权重文件,而最终部署还有专门的部署平台,例如TensorRT、NCNN、OpenVINO等几十种部署推理平台。 如何将pytorch模型文件让几十种部署推理平台能接收与读取是个大问题。即使各推理平台都适配pytorch,那还有其他训练框架也要适配,是非常麻烦的。 假设有N个训练框架,M个推理框架,互相要适配,那就是O(NM)的复杂度。如果能有一种中间格式作为一个标注,能被所有框架所适配,那复杂度顺便降低为O(N+M)。 onnx就是为了降低深度学习模型从训练到部署的复杂度,由微软和meta在2017年提出的一种开放神经网络交换格式,目的在于方便的将模型从一个框架转移到另一个框架。 本小结就介绍onnx基础概念、pytorch模型导出onnx模型。 ONNX 简介 ONNX (Open Neural Network Exchange,开放神经网络交换格式)是一种开放的、跨平台的深度学习模型交换格式,可以方便地将模型从一个框架转移到另一个框架。 onnx最初由微软和meta在2017年联合发布,后来亚马逊也加入进来,目前已经成为行业共识,目前已经有50多个机构的产品支持onnx。 onnx最大的优点是简化了模型部署之间因框架的不同带来的繁琐事,这就像普通话。在中国129种方言之间要互相通信是很困难的,解决办法就是设计一种可以与129种语言进行转换的语言——普通话。onnx就是一个支持绝大多数主流机器学习模型格式之间转换的格式。 采用pytorch进行模型开发时,部署环节通常将pytorch模型转换为onnx模型,然后再进行其他格式转换,或者直接采用onnx文件进行推理,在本章节就介绍采用onnx文件进行推理的方法。 ONNX 基础概念 onnx文件是一种计算图,用于描述数据要进行何种计算,它就像是数学计算的语言,可以进行计算的操作称之为操作符——operator,一系列operator构成一个计算图。 计算图中包含了各节点、输入、输出、属性的详细信息,有助于开发者观察模型结构。 下面通过一个线性回归模型的计算图来了解onnx的计算图 可以采用python代码构建onnx计算图,运行配套代码,构建了一个线性回归模型 from onnx import TensorProto from onnx.helper import ( make_model, make_node, make_graph, make_tensor_value_info) # 'X' is the name, TensorProto.FLOAT the type, [None, None] the shape X = make_tensor_value_info('X', TensorProto.FLOAT, [None, None]) A = make_tensor_value_info('A', TensorProto.FLOAT, [None, None]) B = make_tensor_value_info('B', TensorProto.FLOAT, [None, None]) Y = make_tensor_value_info('Y', TensorProto.FLOAT, [None]) node1 = make_node('MatMul', ['X', 'A'], ['XA']) node2 = make_node('Add', ['XA', 'B'], ['Y']) graph = make_graph([node1, node2], # nodes 'lr', # a name [X, A, B], # inputs [Y]) # outputs onnx_model = make_model(graph) with open(\"linear_regression.onnx\", \"wb\") as f: f.write(onnx_model.SerializeToString()) 运行以上代码会获得linear_regression.onnx文件,可通过https://netron.app/ 进行可视化 图中 A, B, X, Y表示输入输出数据 黑色的MatMul和Add是Node,表示具体的操作 format:表示生成该onnx文件的onnx版本 imports:operator的版本;算子是onnx中最重要的一个概念,大多数模型不成功是因为没有对应的算子,因此算子集的版本选择很重要; inputs和outputs:是输入和输出,其中type是数据类型以及shape。 为了进一步了解onnx文件,下面导出一个resnet50进行观察,onnx文件可通过以下代码获得: import torchvision import torch model = torchvision.models.resnet50(pretrained=False) dummy_data = torch.randn((1, 3, 224, 224)) with torch.no_grad(): torch.onnx.export(model, (dummy_data), \"resnet50.onnx\", opset_version=11, input_names=['input_name_edit_by_tingsongyu'], output_names=['output_name_edit_by_tingsongyu']) 下面再看一个resnet50的onnx文件,观察更多算子的描述。 更多onnx基础概念参见官网:https://onnx.ai/onnx/intro/concepts.html ONNX 的operator 上面介绍了onnx文件主要定义了计算图,计算图中的每个操作称为算子,算子库的丰富程度,直接决定了onnx可以表示模型的种类。 关于onnx支持哪些算子,一定要上官网看一看。 对于普通用户,需要关注使用时的opset是哪个版本,目前最新版本是20。算子库可通过以下函数查看。 import onnx print(onnx.version, \" opset=\", onnx.defs.onnx_opset_version()) 关于算子的理解,以及不适配问题,推荐OpenMMLab的三篇博文 https://zhuanlan.zhihu.com/p/479290520:讲解了pytorch转onnx时,每一个操作是如何转换到onnx算子的;介绍了算子映射关系 https://zhuanlan.zhihu.com/p/498425043:讲解了pytorch转onnx时,trace和script两种模式下的区别;以及torch.onnx.export()函数的使用; https://zhuanlan.zhihu.com/p/513387413:讲解了三种添加算子的方法 其中有一张图对于理解pytorch转onnx很有帮助,这里引用一下: ONNX 安装 onnx的安装很简单:pip install onnx 在这里,提前说一下,onnx是onnx,与onnxruntime不是同一个东西,它们要分开安装,也要分开理解。 pytorch导出onnx pytorch模型导出为onnx调用torch.onnx.export函数即可,该函数包含很多参数,这里只介绍几个常用的,更多的参考官方文档 torch.onnx.export(model, args, f, export_params=True, verbose=False, training=, input_names=None, output_names=None, operator_export_type=, opset_version=None, do_constant_folding=True, dynamic_axes=None, keep_initializers_as_inputs=None, custom_opsets=None, export_modules_as_functions=False) model: 需要被转换的模型,可以有三种类型, torch.nn.Module, torch.jit.ScriptModule or torch.jit.ScriptFunction args:model输入时所需要的参数,这里要传参时因为构建计算图过程中,需要采用数据对模型进行一遍推理,然后记录推理过程需要的操作,然后生成计算图。args要求是tuple或者是Tensor的形式。一般只有一个输入时,直接传入Tensor,多个输入时要用tuple包起来。 export_params: 是否需要保存参数。默认为True,通常用于模型结构迁移到其它框架时,可以用False。 input_names:输入数据的名字, (list of str, default empty list) ,在使用onnx文件时,数据的传输和使用,都是通过name: value的形式。 output_names:同上。 opset_version:使用的算子集版本。 dynamic_axes:动态维度的指定,例如batchsize在使用时随时会变,则需要把该维度指定为动态的。默认情况下计算图的数据维度是固定的,这有利于效率提升,但缺乏灵活性。用法是,对于动态维度的输入、输出,需要设置它哪个轴是动态的,并且为这个轴设定名称。这里有3个要素,数据名称,轴序号,轴名称。因此是通过dict来设置的。例如dynamic_axes={ \"x\": {0: \"my_custom_axis_name\"} }, 表示名称为x的数据,第0个轴是动态的,动态轴的名字叫my_custom_axis_name。通常用于batchsize或者是对于h,w是不固定的模型要设置动态轴。 接下来以resnet50为例,导出一个在ImageNet上训练好的分类模型,再通过netron观察区别。 下面使用配套代码导出三个模型,分别是bs=1, bs=128, bs为动态的,下一节将对比两者效率。 import torchvision import torch model = torchvision.models.resnet50(weights=torchvision.models.ResNet50_Weights.IMAGENET1K_V1) if __name__ == '__main__': op_set = 13 dummy_data = torch.randn((1, 3, 224, 224)) dummdy_data_128 = torch.randn((128, 3, 224, 224)) # 固定 batch = 1 torch.onnx.export(model, (dummy_data), \"resnet50_bs_1.onnx\", opset_version=op_set, input_names=['input'], output_names=['output']) # 固定 batch = 128 torch.onnx.export(model, (dummdy_data_128), \"resnet50_bs_128.onnx\", opset_version=op_set, input_names=['input'], output_names=['output']) # 动态 batch torch.onnx.export(model, (dummy_data), \"resnet50_bs_dynamic.onnx\", opset_version=op_set, input_names=['input'], output_names=['output'], dynamic_axes={\"input\": {0: \"batch_axes\"}, \"output\": {0: \"batch_axes\"}}) 对比如下图所示,input的type中,shape一个是1,一个是batch_axes,其中batch_axes这个就是自定义的命名。 小结 本小节介绍了onnx提出的目的与意义,还有基础概念,onnx可以作为一个中间格式,被绝大多数框架所适配,方便开发人员从训练框架转到开发框架。 onnx文件核心是记录模型的计算图,包括输入数据、各操作节点、输出数据等信息。 最后介绍了pytorch导出onnx的方法,其中需要主要的是op_set版本,以及动态维度的设置。 下一小节,将利用本节导出的onnx模型文件,在onnx的推理库——onnxruntime上进行推理以及性能效率评估。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-11/11.2-onnxruntime-intro.html":{"url":"chapter-11/11.2-onnxruntime-intro.html","title":"11.2 ONNX Runtime 简介与使用","keywords":"","body":"11.2 ONNXRuntime 简介与使用 前言 onnx是一个开放式的格式,它还需要放到推理框架(推理引擎)上运行才可以,支持运行onnx文件的框架有ONNX Rruntime、TensorRT、pytorch、TensorFlow等等。 在这里就介绍onnx自带的推理框架onnxruntime。 onnxruntime是onnx官方的推理框架,它与onnx库是完全两个东西,安装了onnx库并没有安装上onnxruntime,它需要额外安装。 onnxruntime分为cpu版和gpu版,两个版本的安装又分别是两个库,分别是 onnxruntime, onnxruntime-gpu onnxruntime-gpu的安装,又要求cuda、cudnn版本的严格匹配,否则会无法运行!这里被坑了挺长时间,下面讲讲onnxruntime的安装。 onnxruntime 安装 对于cpu安装,可以直接pip install onnxruntime,对于gpu版本的安装,通常不能直接pip install onnxruntime-gpu,而是要设置指定版本,因为cuda和cudnn版本会限制onnxruntime的版本。 版本的对应关系如官网所示:https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html#requirements 例如,cuda版本是11.4,且cudnn是8.2.2.26,则可以pip install onnxruntime-gpu==1.10.0 。如果不是,那就需要配置对应版本的cuda、cudnn了。 通常来说,系统上cuda和cudnn的安装比较麻烦,并且更换版本也不方便。这里推荐直接在python虚拟环境中安装指定版本的cuda, cudnn,这样不会与系统的cuda、cudnn冲突。 例如: conda install cudatoolkit=11.3 -c pytorch -c conda-forge conda install cudnn==8.2.1 pip install onnxruntime-gpu==1.14.1 PS:需要注意的是,onnxruntime和onnxruntime-gpu不可并存,装了onnxruntime-gpu,也是可以调用cpu的,这里建议把onnxruntime卸载,只保留onnxruntime-gpu即可。 onnxruntime 使用 onnxruntime 中使用onnx文件,只需要将其加载到InferenceSession中,然后调用InferenceSession.run()就可以完成推理。 相比于pytorch,不需要在代码中保留如何定义模型的的class,也不用加载权重了,这一切都存储在onnx的计算图中。 InferenceSession 的初始化细节如下所示 class InferenceSession(Session): \"\"\" This is the main class used to run a model. \"\"\" def __init__(self, path_or_bytes, sess_options=None, providers=None, provider_options=None, **kwargs): \"\"\" :param path_or_bytes: filename or serialized ONNX or ORT format model in a byte string :param sess_options: session options :param providers: Optional sequence of providers in order of decreasing precedence. Values can either be provider names or tuples of (provider name, options dict). If not provided, then all available providers are used with the default precedence. :param provider_options: Optional sequence of options dicts corresponding to the providers listed in 'providers'. 在这里,需要关注的是providers,它的作用是指定可用的设备,如[\"CUDAExecutionProvider\", \"CPUExecutionProvider\", \"ROCMExecutionProvider\"]。 ort_session_bs1 = ort.InferenceSession('resnet50_bs_1.onnx', providers=['CUDAExecutionProvider']) inp = np.random.randn(1, 3, 224, 224).astype(np.float32) output = model.run(['output'], {'input': inp}) 完整的resnet50实现图像分类推理,参见配套代码,需要注意的是要与模型训练时的前处理、后处理保持一致。 onnxruntime 推理速度评估 为了观察batchsize对推理效率的影响,这里设计了三个模型的对比实验,分别是bs=1, bs=128, bs为动态时,从1到256的推理时延与吞吐量的对比。 通常说推理速度,只看一次推理的耗时是不足以反应模型在生产时的效率的,因为推理并行的存在,因此可以采用大的batchsize来提高单位时间内,处理样本的数量。 通常评估模型的推理的时间效率会将时延(latency)和吞吐量(throughout)一起观察。 这里简单介绍时延(latency)和吞吐量(throughout)的意义。 时延(latency):通常用于评估用户需要等待多长时间,根据业务场景,需要针对性保障时延,约莫等于平时说的耗时。 吞吐量(throughout):用于评估服务器一定时间内能处理的量,通常是为了提高单位时间内,能处理更多的用户请求。 时延和吞吐量通常是矛盾的,即想要高吞吐的时候,时延就会提高。 这个就像深夜的大排档,你到店里点一份炒河粉,需要等待多久?这取决于老板的策略是低延时,还是高吞吐。 低延时策略:来一个处理一个,尽快把你的一份河粉炒出来,需要3分钟。 高吞吐策略:稍微等等,等到3个炒河粉订单,一次性炒出来,等了3分钟,炒粉3分钟,总共6分钟,算下来,每分钟可以炒0.5份。而低时延策略的吞吐量显然低了,每分钟可以炒0.33份。 计算机的运行也是一样的,可以通过batchsize来权衡时延与吞吐量。 配套代码 首先来看bs是动态的模型,将bs从1到256的效率变化,数据如表所示: bs=1 2 4 8 16 32 64 128 256 时延ms 3.7 5.2 7.7 12.9 45.4 39.1 75.9 150.3 7285.6 吞吐量 frame/s 270 386 521 620 353 818 843 852 35 将吞吐量绘图如下图所示: 结论: 随着batchsize的增加,吞吐量逐步提高,在bs=128时,吞吐量增长平缓; cpu上推理,batchsize的增加,吞吐量差别不大,这也符合逻辑,毕竟cpu不是计算型处理器,无法批量处理大规模矩阵运算; 不定batchsize的模型与动态batchsize的模型,在相同batchsize下,效率并没有什么变化(注:由于变化没差别,表格中没有展示); 在onnruntime有一些奇怪的bs,当bs=16,bs=256时,运行效率出现异常,详情看表格; 建议:模型上线前,实际评测一下模型不同输入时的效率,选择合适的batchsize,可以最大化服务器利用率。 在这里,补充一个pytorch下resnet50的推理评估数据,左图为float32, 右图为半精度float16。配套代码 可以看到: 半精度的吞吐量可以提高50%左右,时延能降低30%左右。 同样的,随着batchsize的增加,吞吐量逐步提高,在bs=128时,吞吐量几乎不变。 小结 本节介绍了onnx自带推理框架——onnxruntime的安装及使用,同时评估了resnet50模型在固定batch和动态batch下,以及不同batchsize时,推理的效率。 通过推理效率的评估,可以知道,batchsize到达一定的量后,吞吐量饱和,因此无需追求过大的batchsize,毕竟大的batchsize,时延会增加。 这里就有一个疑问,如何充分利用gpu资源?例如有一个计算密集型的场景,需要resnet50在24小时不间断的推理,根据上面得出来的理论,batchsize 128就可以了,gpu的显存只需要3GB左右,对于一张16G的T4而言,是否存在浪费呢?虽然gpu的利用率非常高。不知大家对此问题有什么看法?欢迎留言评论,一起探讨。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-11/11.3-onnxruntime-advanced.html":{"url":"chapter-11/11.3-onnxruntime-advanced.html","title":"11.3 ONNX Runtime 进阶使用","keywords":"","body":"11.3 ONNXRuntime 进阶使用 前言 模型部署推理,除了换了一个框架平台外,最重要的是推理框架平台支持各种优化方法,使得模型推理效率更高。 在onnxruntime中也提供了一些优化方法,例如float16量化、int8量化、计算图优化等操作。 本小节就介绍在onnxruntime中实现float16量化、int8量化、混合精度量化、计算图优化、线程管理和IO binding。 float16量化、int8量化、混合精度量化:都是从数据存储及运算角度出发,提高运行效率 计算图优化:是从计算逻辑上优化计算过程 线程管理:是从设备资源分配及调度的角度,充分利用cpu资源以应对并行、串行的任务 IO binding:是从数据在设备之间迁移的问题考虑,将数据提前绑定到gpu上,减少cpu与gpu之间的通信 float16量化 官方文档 float16量化是常用的,精度损失较小的量化策略,通常模型的精度是float32,即一个浮点数用32位来表示,它占用4字节。 但大多数情况下,用16位表示浮点数,已经能满足要求。因此,在推理时可以将模型从float32转为float16。 float16的量化需要用到 onnxconverter-common库,先提前安装:pip install onnx onnxconverter-common。 然后只需要调用convert_float_to_float16,就可以把onnx模型转换为float16的模型。 import onnx from onnxconverter_common import float16 model = onnx.load(\"path/to/model.onnx\") model_fp16 = float16.convert_float_to_float16(model) onnx.save(model_fp16, \"path/to/model_fp16.onnx\") 通过配套代码,进行性能测试,可以看到相比于float32的效率,float16在吞吐量上18.8%((1023-861)/861)的提升。 除了性能效率提升,float16的onnx模型只有48.7MB, float32占97.4MB,磁盘存储占用少了50%。 int8量化 官方文档 ; 官方案例 既然能用低比特表示模型,能否再进一步简化,采用8bit来表示float32呢?当然可以,8bit量化是一种常用于移动端、边缘端的优化策略。 量化又分动态量化(dynamic quantization)和静态量化(static quantization),这里采用动态量化演示,动态量化直接将数据量化到固定的8bit,而静态量化需要采用校准数据集进行一段时间的推理后,得到量化结果。 int8/uint8动态量化只需要采用两行代码,即可将量化后的模型保存到本地。 from onnxruntime.quantization import quantize_dynamic, QuantType _ = quantize_dynamic(path_model_float32, path_model_int8, weight_type=QuantType.QInt8) int8的onnx模型只有24.4MB, float32占97.4MB,float16占48.7MB,比float32少了75%,比float16少了50%。 通过配套代码,进行性能测试,发现int8的推理速度非常慢,连cpu的比不上,其中的原因可能是硬件的不匹配? 关于int8量化,也需要支持int8计算的显卡,例如T4和A100显卡。 “Hardware support is required to achieve better performance with quantization on GPUs. You need a device that supports Tensor Core int8 computation, like T4 or A100. Older hardware will not benefit from quantization.” 混合精度 官方文档 当float16精度不够的时候,可以考虑混合精度,在有必要的地方变为float16,其它地方保持float32,这样可以实现精度和速度的权衡。 混合精度的实现与float16导出类似,需要额外注意的是要给模型一些测试数据,用于评估哪些是float16,哪些是float32。 from onnxconverter_common import auto_mixed_precision import onnx model = onnx.load(\"path/to/model.onnx\") model_fp16 = auto_convert_mixed_precision(model, test_data, rtol=0.01, atol=0.001, keep_io_types=True) onnx.save(model_fp16, \"path/to/model_fp16.onnx\") 计算图优化 官方文档 除了对数据的量化,计算图层面的优化也可以提高推理效率,例如移除不必要的层、网络层的融合: Identity Elimination Slice Elimination Unsqueeze Elimination Dropout Elimination Conv Add Fusion Conv Mul Fusion Conv BatchNorm Fusion Relu Clip Fusion Reshape Fusion 整体上,计算图优化分为三个level:Basic、Extended和Layout Optimizations。 代码实现上比较简单,是在InferenceSession实例化的时候,添加sess_options,就可实现计算图优化。 sess_options需要设置优化的类型,以及优化后模型保存路径。 优化类型有4个level可选: GraphOptimizationLevel::ORT_DISABLE_ALL -> Disables all optimizations GraphOptimizationLevel::ORT_ENABLE_BASIC -> Enables basic optimizations GraphOptimizationLevel::ORT_ENABLE_EXTENDED -> Enables basic and extended optimizations GraphOptimizationLevel::ORT_ENABLE_ALL -> Enables all available optimizations including layout optimizations import onnxruntime as rt sess_options = rt.SessionOptions() # Set graph optimization level sess_options.graph_optimization_level = rt.GraphOptimizationLevel.ORT_ENABLE_EXTENDED # To enable model serialization after graph optimization set this sess_options.optimized_model_filepath = \"\" session = rt.InferenceSession(\"\", sess_options) 通过配套代码,可以观察吞吐量,发现没有得到任何变化,这可能与设备有关,在这里暂时不深究,后续生产真用到了,再回来看。 在这里需要注意的是,由于onnx的显存不能自动释放,一次性跑两个模型的效率评估的话,第二个模型会受到资源问题,导致效率评估不准确,这里代码中需要手动切换两个模型,分两次跑。上图中vgg的float32的bs=16时,性能突然降低,或许就是因为资源问题。 线程管理 onnxruntime提供线程管理功能,可以通过调整不同的参数来控制模型的运行方式和性能。该功能的主要特点包括: 控制线程数量:使用sess_options.intra_op_num_threads参数可以控制模型运行时所使用的线程数 顺序执行或并行执行:使用sess_options.execution_mode参数可以控制运算符在图中是顺序执行还是并行执行。当模型具有较多的分支时,将该参数设置为ORT_PARALLEL可以提供更好的性能。 控制并行执行的线程数量:在sess_options.execution_mode = rt.ExecutionMode.ORT_PARALLEL的情况下,可以使用sess_options.inter_op_num_threads参数来控制在不同节点上并行执行图时使用的线程数。 具体使用参见配套代码 和官方文档。 由于配套代码中运行的结果并没有得到提升,这里猜测与硬件设备有关,因此就不讨论线程参数设置的问题了。 I/O binding IO Binding 用于在运行计算图之前将输入和输出张量绑定到设备上,以提高运行效率。 IO Binding 可以避免在运行计算图时将输入和输出数据从 CPU 复制到设备上,从而减少数据复制操作所需的时间。 具体操作可参考:配套代码和io bind 同样地,配套代码中的案例没有得到效率提升,也无法进一步探讨了,这里可以作为参考代码。 当输入和输出张量比较大时,使用 IO Binding 功能可以显著提高计算图的执行效率,因此在后续的任务中尝试使用 IO binding。 运行耗时分析工具 onnxruntime还提供了一个运行耗时分析工具,在sess_options中设置sess_options.enable_profiling = True,就可以在当前目录输出一份json文件,根据json文件中详细记录了运行时间和性能数据。每个事件条目包括以下信息: cat:事件的分类,可以是Session(会话)或Node(节点); pid:进程ID; tid:线程ID; dur:事件持续时间,以微秒为单位; ts:事件的时间戳,以微秒为单位; ph:事件的类型,可以是X(完整事件)或B(事件开始)/E(事件结束); name:事件的名称; args:事件的参数,包括输入和输出张量的类型、形状和大小,以及线程池的名称、线程ID和运行时间。 通过配套代码,获得json文件,还可以通过网站:https://www.ui.perfetto.dev/, open trace file打开json进行可视化观察。 小结 本小节介绍了onnxruntime在性能优化上的一些技巧,包括float16量化、int8量化、混合精度量化、计算图优化、线程管理和IO binding。 但由于本机设备原因,并没有看到有多大的性能优化,大家也可以在自己设备上尝试一下运行效率的变化,按道理这些技巧是能提速的。 这些优化技巧是模型部署过程中常见的加速技巧,在其它框架中也会有实现,这些在TensorRT中会再详细展开。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/":{"url":"chapter-12/","title":"第十二章 TensorRT 使用","keywords":"","body":"第十二章 TensorRT 使用 第十二章 TensorRT 使用 12.1 TensorRT 简介与安装 12.2 TensorRT 工作流及cuda-python 12.3 trtexec 工具使用 12.4 TensorRT 实用工具 12.5 TensorRT API 使用 12.6 模型量化基础概念 12.7 PTQ 量化实践 12.8 QAT 量化实践 12.9 TensorRT Python 工程化 第十二章简介 本章介绍模型部署的实用工具——TensorRT。 TensorRT是Nvidia公司针对N卡推出的高性能深度学习推理框架,TRT采用c++编写底层库,并提供c++/python应用接口,实现了高吞吐、低时延的优点。TRT 应用量化、图优化、层融合等优化技术,同时利用高度优化的内核找到该模型的最快实现。 TRT涉及的内容非常多,本章将从基础概念与安装开始介绍,然后通过一个Resnet的推理来了解TRT的工作流程,接着介绍TRT实用工具集trtexec、Night system, polygraph,接着介绍基于API的模型搭建方法,最后介绍基于TRT的量化方法及理论原理,还包括PTQ和QAT的实现,最后通过YOLOv5的TRT推理部署来熟悉工程化时,python代码的编写。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.1-trt-install.html":{"url":"chapter-12/12.1-trt-install.html","title":"12.1 TensorRT 简介与安装","keywords":"","body":"12.1 TensorRT 简介与安装 前言 本节将介绍TensorRT的基础概念,以及在windows和linux下进行安装及验证的方法,为后续正式学习TensorRT做好理论和环境的基础。 github:https://github.com/NVIDIA/TensorRT 主页:https://developer.nvidia.com/tensorrt python接口文档:https://docs.nvidia.com/deeplearning/tensorrt/api/python_api/ 安装文档: https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html 开发者文档: https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html 各版本trt介绍:https://docs.nvidia.com/deeplearning/tensorrt/archives/index.html TRT简介 TRT是Nvidia公司针对N卡推出的高性能深度学习推理框架,TRT采用c++编写底层库,并提供c++/python应用接口,实现了高吞吐、低时延的优点。TRT 应用量化、图优化、层融合等优化技术,同时利用高度优化的内核找到该模型的最快实现。 TRT是官方推理引擎,优化效果自然靠谱,因此使用TRT进行工程化部署已经成为主流方案。 下面介绍TRT工作原理,它与pytorch这样的训练框架之间存在什么差异?为什么它是弥补pytorch走上高性能工程化必不可少的工具?下面通过一幅图来说明。 TRT主要包含优化器和运行时库两部分: optimizer是进行模型表示的优化,例如,层融合、图优化、量化、kernel选择等操作,目的是为了找到模型在GPU上,运行时最优的表示形式以及方案(kernel选择),这里的kernel是具体某个算子的实现方法,例如卷积的实现就有很多,可以根据实现方式、算法、数据布局等进行挑选,优化器会根据网络结构、硬件配置和精度需求等因素,选择最适合当前场景的 kernel 进行推理计算,以获得最佳的性能。optimizer是在生产前做准备工作,获得最优方案,供runtime使用。 runtime是运行时库,可以实现推理引擎创建、推理计算、引擎销毁等内容,是生产服务中使用的。到这里,就知道TRT会根据GPU特性、任务特性、用户自定义操作等,将pytorch模型转换为TRT 运行时库能读取的形式,简单粗暴理解就是pytorch模型转为TRT模型。 TRT模型文件(.plan或者.engine)是高度优化的模型表示形式,在真正使用时被Runtime所加载并使用,可以实现高性能推理。 由此可知,TRT可以高性能推理的重要环节是采用优化器优化,那么优化通常有哪些操作呢? 可以通过上图看到会包括6个主要部分: Weight & Activation Precision Calibration:权重和激活精度校准,目的是通过量化模型(float16, int8等)实现高吞吐量,以及低存储。 Layer & Tensor Fusion:层和张量融合,这一步是优化 GPU 内存和带宽的使用,通过将多个节点融合成一个节点,从而减少计算和通信的开销。例如,卷积、BN、ReLU融合为一个操作算子,可以减少数据在 GPU 内存中的复制次数,从而提高推理性能。 Kernel Auto-Tuning,内核自动调优,的是根据目标 GPU 平台的特性和硬件配置,选择最佳的计算内核和数据布局方式,以获得最佳的性能和精度。TensorRT 会根据模型的结构和运行时环境,自动选择最佳的计算内核和数据布局方式,从而最大化推理性能。 从这里表明,优化时的GPU型号要与生产时的GPU型号保持一致,尤其采用.engine来存储TRT模型时,不同设备之间不能使用。 Dynamic Tensor Memory,动态张量内存,可以最小化模型的内存占用,通过动态地分配和释放张量内存,以最大程度地利用有限的 GPU 内存资源。 Multi-Stream Execution,多流执行是通过并行执行多个输入流,提高模型的推理性能。TensorRT 可以将多个输入流分配到不同的 CUDA 流中,并行执行多个输入流,从而提高模型的并发性和吞吐量。 CUDA流可以理解为 Time Fusion(时间融合) 这一步的主要目的是通过动态生成内核,优化循环神经网络(RNN)等模型在多个时间步骤中的计算过程。通过将多个时间步骤的计算合并成一个内核,可以减少数据在 GPU 内存中的复制次数,从而提高推理性能。 使用场景:需要优化循环神经网络等模型在多个时间步骤中的计算过程,适用于序列模型等有时间序列特征的深度学习模型。 PS:CUDA流的理解 CUDA流是CUDA编程中用于并行计算的一种抽象概念,是一组在GPU上并行执行的操作序列。CUDA流用于管理和调度GPU上的计算资源。每个CUDA流都有自己的计算资源,包括寄存器、共享内存、全局内存等,这些资源是独立的,不同流之间不会相互干扰。在执行CUDA程序时,GPU上的计算资源被分割成了若干个流,每个流可以独立地执行一组操作序列。CUDA流可以看作是一条命令流水线,其中的每个操作都是在GPU上并行执行的。 可以将CUDA流看作是一个GPU上的工厂生产线,流中的每个操作就像是生产线上的一个工人,每个工人都有自己的工作任务,按照一定的顺序进行工作。这样,GPU上的计算资源就可以同时处理多个任务,从而实现并行计算。 为什么叫流(stream),是可以将其理解为数据的流动。在CUDA编程中,数据可以从CPU到GPU,也可以从GPU到GPU,甚至可以在GPU内部进行数据流动。通过合理地利用CUDA流,可以将数据的流动和计算的流程进行有效地整合和优化,以获得更高的性能和效率。 Windows安装 接下来进行TRT的安装,将分windows和linux下,所有安装步骤参照官方文档进行:https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html 建议采用docker镜像安装使用,可避免很多不必要的坑,若不采用docker的话,建议严格按照官方文档步骤进行。 额外需要注意的是cuda、cudnn版本要与TRT版本匹配,同时主要环境变量的设置,中间可能会存在各种报错,请自行网上搜索解决方案。 TensorRT可以通过多种方法安装,例如Debian or RPM packages, a Python wheel file, a tar file, or a zip file. 在这里先介绍Windows的安装,采用zip文件的形式。 第一步:在官网下载zip文件,TensorRT-8.6.0.12.Windows10.x86_64.cuda-11.8.zip 第二步:把zip解压到CUDA同级目录(位置无所谓,建议与CUDA放在一起,例如C:\\Program Files\\NVIDIA GPU Computing Toolkit\\TensorRT-8.6.0.12),将TensorRT-8.6.0.12\\lib下文件复制到CUDA安装目录/bin下面(C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v11.3\\bin) 第三步:安装python whl,在TensorRT-8.6.0.12\\python下找到对应版本的whl,主要是看python版本。然后安装以下三个库 python.exe -m pip install tensorrt-*-cp3x-none-win_amd64.whl python.exe -m pip install tensorrt_lean-*-cp3x-none-win_amd64.whl python.exe -m pip install tensorrt_dispatch-*-cp3x-none-win_amd64.whl 第四步:pytorch与TensorFlow需要额外安装3个python包 python3 -m pip install graphsurgeon\\graphsurgeon-0.4.6-py2.py3-none-any.whl python3 -m pip install uff\\uff-0.6.9-py2.py3-none-any.whl python3 -m pip install onnx_graphsurgeon\\onnx_graphsurgeon-0.3.12-py2.py3-none-any.whl 第五步:建议安装 cuda-python & pycuda 根据官方安装文档指引:https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html 如果需要使用python-api,则需要安装cuda-python,安装方法:https://nvidia.github.io/cuda-python/install.html pip install pycuda pip install cuda-python 安装pycuda可能会遇到:提示缺少 c++构建工具 error: Microsoft Visual C++ 14.0 or greater is required. Get it with \"Microsoft C++ Build Tools\": https://visualstudio.microsoft.com/visual-cpp-build-tools/ 参考微软的回答:https://learn.microsoft.com/en-us/answers/questions/136595/error-microsoft-visual-c-14-0-or-greater-is-requir 下载一个vs_BuildTools.exe,然后安装生成工具即可。 Windows TRT验证 trtexec 运行模型 trtexec是tensorrt自带的命令行工具,可以进行TRT模型生成,以及推理耗时评估。 在windows下,首先需要将trtexec.exe所在文件夹添加到系统环境变量,这样在cmd中,trtexec指令才会被识别到。 接着打开cmd,运行以下命令,将onnx文件转换为tensorrt的engine文件,并且自动进行推理耗时评估。 其中,onnx文件可从chatper-11文件夹下获取。 trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 经过30s左右详细日志输出,最底部有推理性能日志: [06/13/2023-14:56:23] [I] === Performance summary === [06/13/2023-14:56:23] [I] Throughput: 503.749 qps [06/13/2023-14:56:23] [I] Latency: min = 1.89886 ms, max = 2.85559 ms, mean = 1.92952 ms, median = 1.92126 ms, percentile(90%) = 1.93677 ms, percentile(95%) = 1.95227 ms, percentile(99%) = 2.20648 ms [06/13/2023-14:56:23] [I] Enqueue Time: min = 0.288818 ms, max = 1.23926 ms, mean = 0.570478 ms, median = 0.669189 ms, percentile(90%) = 0.782898 ms, percentile(95%) = 0.83667 ms, percentile(99%) = 1.03223 ms [06/13/2023-14:56:23] [I] H2D Latency: min = 0.0751953 ms, max = 0.138062 ms, mean = 0.0788889 ms, median = 0.0770264 ms, percentile(90%) = 0.0814819 ms, percentile(95%) = 0.0932617 ms, percentile(99%) = 0.111938 ms [06/13/2023-14:56:23] [I] GPU Compute Time: min = 1.81738 ms, max = 2.7533 ms, mean = 1.8471 ms, median = 1.84009 ms, percentile(90%) = 1.85034 ms, percentile(95%) = 1.8606 ms, percentile(99%) = 2.11353 ms [06/13/2023-14:56:23] [I] D2H Latency: min = 0.00317383 ms, max = 0.0183716 ms, mean = 0.0035316 ms, median = 0.00341797 ms, percentile(90%) = 0.00390625 ms, percentile(95%) = 0.00390625 ms, percentile(99%) = 0.00415039 ms [06/13/2023-14:56:23] [I] Total Host Walltime: 3.00348 s [06/13/2023-14:56:23] [I] Total GPU Compute Time: 2.79466 s [06/13/2023-14:56:23] [W] * GPU compute time is unstable, with coefficient of variance = 3.29947%. [06/13/2023-14:56:23] [W] If not already in use, locking GPU clock frequency or adding --useSpinWait may improve the stability. [06/13/2023-14:56:23] [I] Explanations of the performance metrics are printed in the verbose logs. [06/13/2023-14:56:23] [I] &&&& PASSED TensorRT.trtexec [TensorRT v8600] # trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 可看到,基于TRT引擎的resnet50,batchsize=1的模型,在推理效率上,吞吐量为503,平均时延为1.92ms。 可以对比onnxruntime的吞吐量,提高了86%的吞吐(11.2小节做了评估,bs=1时,吞吐量为270),时延减少了48%(3.7 --> 1.92 ms) 为了进一步对比,还可以执行batchsize=128时的resnet50,观察吞吐量与时延情况,具体如下表所示: trtexec --onnx=resnet50_bs_128.onnx --saveEngine=resnet50_bs_128.engine [06/13/2023-15:05:08] [I] === Performance summary === [06/13/2023-15:05:08] [I] Throughput: 9.05117 qps [06/13/2023-15:05:08] [I] Latency: min = 109.778 ms, max = 111.595 ms, mean = 110.354 ms, median = 110.348 ms, percentile(90%) = 110.654 ms, percentile(95%) = 110.674 ms, percentile(99%) = 111.595 ms [06/13/2023-15:05:08] [I] Enqueue Time: min = 0.542603 ms, max = 1.39661 ms, mean = 0.860929 ms, median = 0.815994 ms, percentile(90%) = 1.23169 ms, percentile(95%) = 1.28577 ms, percentile(99%) = 1.39661 ms [06/13/2023-15:05:08] [I] H2D Latency: min = 9.49048 ms, max = 10.332 ms, mean = 9.63266 ms, median = 9.57568 ms, percentile(90%) = 9.7522 ms, percentile(95%) = 10.2017 ms, percentile(99%) = 10.332 ms [06/13/2023-15:05:08] [I] GPU Compute Time: min = 100.16 ms, max = 101.197 ms, mean = 100.652 ms, median = 100.593 ms, percentile(90%) = 101.011 ms, percentile(95%) = 101.036 ms, percentile(99%) = 101.197 ms [06/13/2023-15:05:08] [I] D2H Latency: min = 0.064209 ms, max = 0.114136 ms, mean = 0.0695243 ms, median = 0.0656738 ms, percentile(90%) = 0.083374 ms, percentile(95%) = 0.0861816 ms, percentile(99%) = 0.114136 ms [06/13/2023-15:05:08] [I] Total Host Walltime: 3.204 s [06/13/2023-15:05:08] [I] Total GPU Compute Time: 2.9189 s [06/13/2023-15:05:08] [I] Explanations of the performance metrics are printed in the verbose logs. [06/13/2023-15:05:08] [I] &&&& PASSED TensorRT.trtexec [TensorRT v8600] # trtexec --onnx=resnet50_bs_128.onnx --saveEngine=resnet50_bs_128.engine onnx和TRT的吞吐量、时延对比如下表所示,可以看到吞吐量有30-80%的提升,时延有20-50%左右的降低。 吞吐量 吞吐量 时延 时延 onnxruntime trt onnxruntime trt bs = 1 270 503(↑86%) 3.7 1.9(↓49%) bs = 128 852 1158(↑36%) 150.3 110(↓27%) 日志信息中有很多意义内容,包括: Throughput:模型的推理吞吐量,以每秒推理数量(QPS)为单位。 Latency:模型一次推理的延迟时间统计信息,包括最小值、最大值、平均值、中位数和百分位数(90%、95%和99%)。 Enqueue Time:将数据传输到GPU的时间统计信息, H2D Latency:将主机数据传输到GPU的延迟时间统计信息, GPU Compute Time:模型在GPU上运行的计算时间统计信息 D2H Latency:从GPU将数据传输回主机的延迟时间统计信息 Total Host Walltime:模型推理的总时间,包括传输数据、计算和传输数据回主机的时间。 Total GPU Compute Time:模型在GPU上的总计算时间。 Bug记录 安装TRT时,切记关注自己的cuda版本,要找到适配你cuda版本的TRT版本! 由于刚更换电脑,cuda安装了最新的v12.4, 一开始想安装TRT v8.6.1,发现报错: [03/27/2024-22:34:24] [I] Loading standard plugins [03/27/2024-22:34:24] [E] Uncaught exception detected: Unable to open library: nvinfer_plugin.dll &&&& FAILED TensorRT.trtexec [TensorRT v8601] # trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine --tacticSources=-CUDNN 根本原因是 cuda-v12.4需要更高的TRT版本,最终安装的是TensorRT-10.0.0.6。 Linux 安装 linux的安装有多种途径,如果有docker环境,强烈建议使用docker拉取官方镜像,直接可以使用。 如果不用docker,可以采用deb(ubuntu)、tar(linux都可以)、rpm(Redhat/Centos),笔者在centos和ubuntu上尝试过以上三种方法,由于服务器环境不纯净,导致各种问题装上。因此建议大家有docker就用docker,没有docker的话可完全参照官方文档的步骤就可以了。 安装前准备-系统版本查看 Linux系统下,注意系统的发行版本是什么,Linux、Ubuntu、Centos/RedHat有着不同的安装包,这里需要一开始查看自己系统版本。 cat /etc/*-release 同时查看cuda版本以及cudnn版本 nvcc -V # cudnn 8.0以前版本查看 cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2 # cudnn 8.0以后版本查看 cat /usr/local/cuda/include/cudnn_version.h | grep CUDNN_MAJOR -A 2 这里安装的是ubuntu18.04, cuda11.6, cudnn8.9.2; 通过docker安装 https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tensorrt https://docs.nvidia.com/deeplearning/tensorrt/container-release-notes/index.html https://catalog.ngc.nvidia.com/orgs/nvidia/containers/pytorch/tags(pytorch + trt的容器) 有了docker可以直接拉取镜像,在环境中使用对应的TRT 第一步:拉取镜像 docker pull nvcr.io/nvidia/tensorrt:23.04-py3 TRT的版本可以通过文档查找 23.04-py3表示2023年4月发布的镜像,该镜像环境是: Ubuntu 20.04 Python3.8 NVIDIA CUDA® 12.1.0 NVIDIA cuBLAS 12.1.3 NVIDIA cuDNN 8.9.0 NVIDIA NCCL 2.17.1 第二步:启动镜像 docker run -it -d --name trt --gpus all -v /home/docker_volume:/mnt nvcr.io/nvidia/tensorrt:23.04-py3 /bin/bash 第三步:进入容器,查看trt版本 docker exec -it trt /bin/bash dpkg -l | grep TensorRT 得到如下显示,表明安装成功,可以尝试使用trtexec和python进行TRT模型推理。 root@c3d8bfb1d917:/opt/tensorrt/bin# dpkg -l | grep TensorRT ii libnvinfer-bin 8.5.1-1+cuda11.8 amd64 TensorRT binaries ii libnvinfer-dev 8.5.1-1+cuda11.8 amd64 TensorRT development libraries and headers ii libnvinfer-plugin-dev 8.5.1-1+cuda11.8 amd64 TensorRT plugin libraries and headers ii libnvinfer-plugin8 8.5.1-1+cuda11.8 amd64 TensorRT plugin library ii libnvinfer8 8.5.1-1+cuda11.8 amd64 TensorRT runtime libraries ii libnvonnxparsers-dev 8.5.1-1+cuda11.8 amd64 TensorRT ONNX libraries ii libnvonnxparsers8 8.5.1-1+cuda11.8 amd64 TensorRT ONNX libraries ii libnvparsers-dev 8.5.1-1+cuda11.8 amd64 TensorRT parsers libraries ii libnvparsers8 8.5.1-1+cuda11.8 amd64 TensorRT parsers libraries ii tensorrt-dev 8.5.1.7-1+cuda11.8 amd64 Meta package for TensorRT development libraries 第四步:验证TRT 基于trtexec 运行模型。首先,编译trtexec: cd /workspace/tensorrt/samples/trtexec make 然后,添加环境变量: export PATH=$PATH:/path/to/trtexec export PATH=$PATH:/workspace/tensorrt/samples/trtexec 最后,将resnet50_bs_1.onnx文件放到服务器,执行命令: trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 最后得到如下输出: [06/14/2023-07:18:37] [I] === Performance summary === [06/14/2023-07:18:37] [I] Throughput: 443.756 qps [06/14/2023-07:18:37] [I] Latency: min = 2.27521 ms, max = 2.51807 ms, mean = 2.30483 ms, median = 2.3042 ms, percentile(90%) = 2.32056 ms, percentile(95%) = 2.32422 ms, percentile(99%) = 2.33069 ms [06/14/2023-07:18:37] [I] Enqueue Time: min = 0.792969 ms, max = 1.42358 ms, mean = 0.966213 ms, median = 0.89917 ms, percentile(90%) = 1.28101 ms, percentile(95%) = 1.29688 ms, percentile(99%) = 1.34668 ms [06/14/2023-07:18:37] [I] H2D Latency: min = 0.0510864 ms, max = 0.09021 ms, mean = 0.0603899 ms, median = 0.0601807 ms, percentile(90%) = 0.0654297 ms, percentile(95%) = 0.06604 ms, percentile(99%) = 0.0751953 ms [06/14/2023-07:18:37] [I] GPU Compute Time: min = 2.22003 ms, max = 2.45557 ms, mean = 2.24015 ms, median = 2.24048 ms, percentile(90%) = 2.25073 ms, percentile(95%) = 2.25391 ms, percentile(99%) = 2.26099 ms [06/14/2023-07:18:37] [I] D2H Latency: min = 0.00244141 ms, max = 0.0136719 ms, mean = 0.00428888 ms, median = 0.00305176 ms, percentile(90%) = 0.0114746 ms, percentile(95%) = 0.0126953 ms, percentile(99%) = 0.0133057 ms [06/14/2023-07:18:37] [I] Total Host Walltime: 3.00616 s [06/14/2023-07:18:37] [I] Total GPU Compute Time: 2.98836 s [06/14/2023-07:18:37] [I] Explanations of the performance metrics are printed in the verbose logs. [06/14/2023-07:18:37] [I] &&&& PASSED TensorRT.trtexec [TensorRT v8601] # trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 通过Deb包安装 Deb参考官网吧,这里安装最后失败了,所以推荐大家用docker安装,下面是作为记录。 第一步:在官网下载deb包。并安装 sudo dpkg -i nv-tensorrt-local-repo-${os}-${tag}_1.0-1_amd64.deb sudo cp /var/nv-tensorrt-local-repo-${os}-${tag}/*-keyring.gpg /usr/share/keyrings/ sudo apt-get update 第二步:安装 tensorrt。 需要等待3-5分钟。 sudo apt-get install tensorrt 第三步:安装其它工具 sudo apt-get install python3-libnvinfer-lean sudo apt-get install python3-libnvinfer-dispatch python3 -m pip install numpy sudo apt-get install python3-libnvinfer-dev python3 -m pip install numpy onnx sudo apt-get install onnx-graphsurgeon 第四步:验证安装 安装好后,可到/usr/src/tensorrt 查看安装目录。 dpkg-query -W tensorrt 第五步:编译trtexec cd /usr/src/tensorrt/samples/trtexec make 根据官方文档只需要make就可以获得trtexec,但是我的服务器的配置出了些问题,make时出现如下报错: /usr/local/cuda/include/cuda_runtime_api.h:4219:65: error: 'cudaLaunchConfig_t' does not name a type; did you mean 'cudaFunction_t'? extern __host__ cudaError_t CUDARTAPI cudaLaunchKernelExC(const cudaLaunchConfig_t *config, const void *func, void **args); 多种尝试未解决,后续将采用docker镜像来使用TRT,这里暂时放弃。 小结 本小节介绍了TensorRT的基础概念,知道了TRT中需要做优化和推理两个主要内容,其中的优化可以在API中或者是trtexec中进行,推理则提供了python和c++两种接口。在安装上,介绍了windows下和linux下的安装及验证,可以看到在吞吐和时延上,TRT比ONNX都有优势。 下一小节将详细介绍TRT的工作流程。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.2-trt-workflow.html":{"url":"chapter-12/12.2-trt-workflow.html","title":"12.2 TensorRT 工作流及cuda-python","keywords":"","body":"12.2 TensorRT 工作流及cuda-python 前言 本节将在python中采用TensorRT进行resnet50模型推理,通过一个案例了解TensorRT的工作步骤流程,为后续各模块深入研究打下基础。 本节核心内容包括TensorRT中各模块概念,python中进行推理步骤,python中的cuda库使用。 本节用到的resnet50_bs_1.engine文件,需要提前通过trtexec来生成(resnet50_bs_1.onnx通过11章内容生成,或者从网盘下载-提取码:24uq trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine workflow基础概念 在python中使用TensorRT进行推理,需要了解cuda编程的概念,在这里会出现一些新名词,这里进行了总结,帮助大家理解python中使用TRT的代码。 借鉴nvidia官方教程中的一幅图来说明TRT从构建模型到使用模型推理,需要涉及的概念。 Logger:用于在TensorRT日志中记录消息,可以来实现自定义日志记录器,以便更好地了解TensorRT运行时发生的事情。 Builder:用于构建一个优化的TensorRT引擎,可以使用Builder来定义和优化网络 BuilderConfig:用于配置Builder的行为,例如最大批处理大小、优化级别等 Network:用于描述一个计算图,它由一组层组成,用来定义和构建深度学习模型,通常有三种方式获取network,包括TensorRT API、parser、训练框架中trt工具。 SerializeNetwork:用于将网络序列化为二进制格式,可以将网络模型保存到磁盘上的.plan文件中,以便稍后加载和使用。 .plan:.plan是TensorRT引擎的序列化文件格式,有的地方用.engine,.plan文件和.engine都是序列化的TensorRT引擎文件,但是它们有一些区别。 .plan文件是通用序列化文件格式,它包含了优化后的网络和权重参数。而.engine文件是Nvidia TensorRT引擎的专用二进制格式,它包含了优化后的网络,权重参数和硬件相关信息。 可 移植性方面,由于.plan文件是通用格式,所以可以在不同的硬件平台上使用。而.engine文件是特定于硬件的,需要在具有相同GPU架构的系统上使用。 加载速度上:.plan文件的加载速度通常比.engine文件快,因为它不包含硬件相关信息,而.engine文件必须在运行时进行硬件特定的编译和优化。 Engine:Engine是TensorRT中的主要对象,它包含了优化后的网络,可以用于进行推理。可以使用Engine类加载.plan文件,创建Engine对象,并使用它来进行推理。 Context:Context是Engine的一个实例,它提供了对引擎计算的访问。可以使用Context来执行推理,并在执行过程中管理输入和输出缓冲区。 Buffer:用于管理内存缓冲区。可以使用Buffer类来分配和释放内存,并将其用作输入和输出缓冲区。 Execute:Execute是Context类的一个方法,用于执行推理。您可以使用Execute方法来执行推理,并通过传递输入和输出缓冲区来管理数据流。 在这里面需要重点了解的是Network的构建有三种方式,包括TensorRT API、parser、训练框架中trt工具。 TensorRT API是手写网络结构,一层一层的搭建 parser 是采用解析器对常见的模型进行解析、转换,常用的有ONNX Parser。本小节那里中采用parser进行onnx模型解析,实现trt模型创建。 直接采用pytorch/tensorflow框架中的trt工具导出模型 此处先采用parser形式获取trt模型,后续章节再讲解API形式构建trt模型。 TensorRT resnet50推理 到这里环境有了,基础概念了解了,下面通过python代码实现resnet50的推理,通过本案例代码梳理在代码层面的workflow。 pycuda库与cuda库 正式看代码前,有必要简单介绍pycuda库与cuda库的差别。在早期版本中,代码多以pycuda进行buffer的管理,在python3.7后,官方推荐采用cuda库进行buffer的管理。 cuda库是NVIDIA提供的用于CUDA GPU编程的python接口,包含在cuda toolkit中。主要作用是: 直接调用cuda runtime API,如内存管理、执行kernel等。 pycuda库是一个第三方库,提供了另一个python绑定到CUDA runtime API。主要作用是: 封装cuda runtime API到python调用,管理GPU Context、Stream等。 cuda库更基础,pycuda库更全面。前者集成在CUDA toolkit中,后者更灵活。 但在最新的trt版本(v8.6.1)中,官方推荐采用cuda库,两者使用上略有不同,在配套代码中会实现两种buffer管理的代码。 python workflow 正式跑代码前,了解一下代码层面的workflow: 初始化模型,获得context 创建logger:logger = trt.Logger(trt.Logger.WARNING) 创建engine:engine = runtime.deserialize_cuda_engine(ff.read()) 创建context: context = engine.create_execution_context() 内存申请: 申请host(cpu)内存:进行变数据量的赋值,即完成变量内存分配。 申请device(gpu)内存: 采用cudart函数,获得内存地址。 d_input = cudart.cudaMalloc(h_input.nbytes)[1] 告知context gpu地址:context.set_tensor_address(l_tensor_name[0], d_input) 推理 数据拷贝 host 2 device: cudart.cudaMemcpy(d_input, h_input.ctypes.data, h_input.nbytes, cudart.cudaMemcpyKind.cudaMemcpyHostToDevice) 推理: context.execute_async_v3(0) 数据拷贝 device 2 host: cudart.cudaMemcpy(h_output.ctypes.data, d_output, h_output.nbytes, cudart.cudaMemcpyKind.cudaMemcpyDeviceToHost) 内存释放 cudart.cudaFree(d_input) 取结果 在host的变量上即可拿到模型的输出结果。 这里采用配套代码实现ResNet图像分类,可以得到与上一章ONNX中一样的分类效果,并且吞吐量与trtexec中差别不大,大约在470 it/s。 100%|██████████| 3000/3000 [00:06 cuda库的buffer管理 采用cuda库进行buffer管理,可分3个部分,内存和显存的申请、数据拷贝、显存释放。 这里推荐官方教程以及官方教程代码 在教程配套代码的 model_infer()函数是对上述两份资料进行了结合,下面详细介绍cuda部分的代码含义。 def model_infer(context, engine, img_chw_array): n_io = engine.num_io_tensors # since TensorRT 8.5, the concept of Binding is replaced by I/O Tensor, all the APIs with \"binding\" in their name are deprecated l_tensor_name = [engine.get_tensor_name(ii) for ii in range(n_io)] # get a list of I/O tensor names of the engine, because all I/O tensor in Engine and Excution Context are indexed by name, not binding number like TensorRT 8.4 or before # 内存、显存的申请 h_input = np.ascontiguousarray(img_chw_array) h_output = np.empty(context.get_tensor_shape(l_tensor_name[1]), dtype=trt.nptype(engine.get_tensor_dtype(l_tensor_name[1]))) d_input = cudart.cudaMalloc(h_input.nbytes)[1] d_output = cudart.cudaMalloc(h_output.nbytes)[1] # 分配地址 context.set_tensor_address(l_tensor_name[0], d_input) # 'input' context.set_tensor_address(l_tensor_name[1], d_output) # 'output' # 数据拷贝 cudart.cudaMemcpy(d_input, h_input.ctypes.data, h_input.nbytes, cudart.cudaMemcpyKind.cudaMemcpyHostToDevice) # 推理 context.execute_async_v3(0) # do inference computation # 数据拷贝 cudart.cudaMemcpy(h_output.ctypes.data, d_output, h_output.nbytes, cudart.cudaMemcpyKind.cudaMemcpyDeviceToHost) # 释放显存 cudart.cudaFree(d_input) cudart.cudaFree(d_output) return h_output 第3行:获取模型输入、输出变量的数量,在本例中是2。 第4行:获取模型输入、输出变量的名字,存储在list中。本案例中是['input', 'output'],这两个名字是在onnx导入时候设定的。 第7/8行:申请host端的内存,可看出只需要进行两个numpy的赋值,即可开辟内存空间存储变量。 第9/10行:调用cudart进行显存空间申请,变量获取的是内存地址。例如”47348061184 “ 第13/14行:将显存地址告知context,context在推理的时候才能找到它们。 第17行:将内存中数据拷贝到显存中 第19行:context执行推理,此时运算结果已经到了显存 第21行:将显存中数据拷贝到内存中 第24/25行:释放显存中的变量。 pycuda库的buffer管理 注意:代码在v8.6.1上运行通过,在v10.0.0.6上未能正确使用context.execute_async_v3,因此请注意版本。 更多接口参考:https://docs.nvidia.com/deeplearning/tensorrt/api/python_api/infer/Core/ExecutionContext.html 在配套代码中pycuda进行buffer的申请及管理代码如下: h_input = cuda.pagelocked_empty(trt.volume(context.get_binding_shape(0)), dtype=np.float32) h_output = cuda.pagelocked_empty(trt.volume(context.get_binding_shape(1)), dtype=np.float32) d_input = cuda.mem_alloc(h_input.nbytes) d_output = cuda.mem_alloc(h_output.nbytes) def model_infer(context, h_input, h_output, d_input, d_output, stream, img_chw_array): # 图像数据迁到 input buffer np.copyto(h_input, img_chw_array.ravel()) # 数据迁移, H2D cuda.memcpy_htod_async(d_input, h_input, stream) # 推理 context.execute_async_v2(bindings=[int(d_input), int(d_output)], stream_handle=stream.handle) # 数据迁移,D2H cuda.memcpy_dtoh_async(h_output, d_output, stream) stream.synchronize() return h_output 第1,2行:通过cuda.pagelocked_empty创建一个数组,该数组位于GPU中的锁页内存,锁页内存(pinned Memory/ page locked memory)的概念可以到操作系统中了解,它是为了提高数据读取、传输速率,用空间换时间,在内存中开辟一块固定的区域进行独享。申请的大小及数据类型,通过trt.volume(context.get_binding_shape(0)和np.float32进行设置。其中trt.volume(context.get_binding_shape(0)是获取输入数据的元素个数,此案例,输入是[1, 3, 224, 224],因此得到的数是1x3x224x224 = 150528 第3,4行:通过cuda.mem_alloc函数在GPU上分配了一段内存,返回一个指向这段内存的指针 第8行:将图像数据迁移到input buffer中 第10行,将输入数据从CPU内存异步复制到GPU内存。其中,cuda.memcpy_htod_async函数异步将数据从CPU内存复制到GPU内存,d_input是GPU上的内存指针,h_input是CPU上的numpy数组,stream是CUDA流 第14行,同理,从GPU中把数据复制回到CPU端的h_output,模型最终使用h_output来表示模型预测结果 小结 本节介绍了TensorRT的工作流程,其中涉及10个主要模块,各模块的概念刚开始不好理解,可以先跳过,在后续实践中去理解。 随后基于onnx parser实现TensorRT模型的创建,engine文件的生成通过trtexec工具生成,并进行图片推理,获得了与trtexec中类似的吞吐量。 最后介绍了pycuda库和cuda库进行buffer管理的详细步骤,两者在代码效率上是一样的,吞吐量几乎一致。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.3-trt-trtexec.html":{"url":"chapter-12/12.3-trt-trtexec.html","title":"12.3 trtexec 工具使用","keywords":"","body":"12.3 trtexec 工具使用 本小节介绍trtexec工具的使用,trtexec可以实现onnx模型导出trt模型、耗时分析和模型优化分析等功能,本节将对trtexec的运用进行介绍。 trtexec trtexec是官方提供的命令行工具,主要用于一下三个方面 生成模型序列化文件:由ONNX文件生成 TensorRT 引擎并序列化为 Plan文件/engine文件 查看模型文件信息:查看 ONNX文件或 Plan 文件的网络逐层信息 模型性能测试:测试 TensorRT 引擎基于随机输入或给定输入下的性能 trtexec提供了大量参数,整体可分为构建和运行两个阶段。 构建阶段常用参数 --onnx=: onnx文件路径 --minShapes=, --optShapes=, and --maxShapes=: 当是onnx模型时,可指定batchsize的动态范围。 –-memPoolSize=: 优化过程可使用的最大内存 --saveEngine=: 保存的文件输出路径 --fp16, --int8, --noTF32, and --best: 指定数据精度 --verbose: 是否需要打印详细信息。默认是不打印详细信息。 --skipInference: 创建并保存引擎文件,不执行推理过程。 --timingCacheFile=: 记录每个tensor的最小最大值、运行时间等,可以用来分析量化效果。 --dumpLayerInfo, --exportLayerInfo=: 打印及保存每一层详细信息 更多高级用法,参考官方文档:https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#trtexec 运行阶段常用参数 --loadEngine=: 要加载的模型文件 --shapes=:指定输入张量的形状 --loadInputs=: Load input values from files. Default is to generate random inputs. --warmUp=, 热身阶段最短运行时间,单位ms --duration=, 测试阶段最短运行时间,单位s --iterations=: 测试阶段最小迭代次数 --useCudaGraph: 采用 CUDA graph 捕获和执行推理过程 --noDataTransfers: 关闭host与device之间的数据传输 --dumpProfile, --exportProfile=: 打印及保存每一层性能信息 --dumpLayerInfo, --exportLayerInfo=: 打印及保存每一层详细信息 更多高级用法,参考官方文档:https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#trtexec 案例0:固定batchsize 输出固定batchsize的engine文件,这里需要注意,batchsize的状态需要与ONNX匹配,因此在生成onnx时需要设置好。 trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 案例1: 动态batchsize使用 resnet50_bs_dynamic.onnx 可通过第十一章章生成,或通过百度网盘下载-提取码:whai trtexec --onnx=resnet50_bs_dynamic.onnx --saveEngine=resnet50_bs_dynamic_1-32-64.engine --timingCacheFile=dynamic-1-32-64.cache --minShapes=input:1x3x224x224 --maxShapes=input:64x3x224x224 --optShapes=input:16x3x224x224 通过下表可知,fp32时,大batchsize带来吞吐量增加不明显,因此可考虑时延的平衡,选择batchsize=8。 FP32 1-8-64 1-16-64 1-32-64 1-64-64 吞吐量( FPS) 952 1060 1126 1139 时延(ms) 8.3 14.9 28.2 112 案例2:fp32、fp16、int8 性能比较 运行配套代码中的run.bat/run.sh,可以查看log,观察吞吐量、时延的变化。 如下图所示,吞吐量方面 fp16相较于fp32有约2~3倍提升,int8相较于fp16约2倍提升 相同精度时,吞吐量随batchsize增加,但在32后增速不明显。int8随着batchsize增速潜力更大。 时延方面 时延随着batchsize是线性增长 fp32, fp16, int8的时延依次递减一半 案例3:查看层详细信息 通过参数--dumpLayerInfo --exportLayerInfo,可以输出各层详细信息,以及融合情况,还有输入输出张量的名字(Bindings) trtexec --onnx=resnet50_bs_dynamic.onnx --saveEngine=demo.engine --skipInference --dumpLayerInfo --exportLayerInfo=\"exportLayerInfo.log\" 在exportLayerInfo.log文件中可以看到如下信息,主要包括 各网络层内容,以及融合情况“Reformatting CopyNode for Input Tensor 0 to Conv_0 + Relu_1” Reformatting CopyNode 表示 TensorRT 将输入tensor 0 复制(Copy)到 Conv_0 和 Relu_1 两个层进行了融合(Reformatting)。这里的 Reformatting 指的是 TensorRT 在优化网络结构时,会将一些层进行融合,以减少内存拷贝和提高计算效率。CopyNode 则表示插入了一个拷贝层,用于将输入数据复制到融合后新的层中。 这种层的融合可以减少内存访问,优化数据流,从而提升推理性能。 Bindings:包括输入输出张量的名称,这个在onnx导出时设定的,在下游python推理代码中也会用到。 {\"Layers\": [\"Reformatting CopyNode for Input Tensor 0 to Conv_0 + Relu_1\" ,\"Conv_0 + Relu_1\" ,\"MaxPool_2\" ,\"Conv_3 + Relu_4\" ,\"Conv_5 + Relu_6\" ... ,\"Reformatting CopyNode for Input Tensor 0 to Gemm_121\" ,\"Gemm_121\" ,\"reshape_after_Gemm_121\" ], \"Bindings\": [\"input\" ,\"output\" ]} 案例4:verbose中的日志内容 打开verbose开关后,trtexec将输出详细内容,包括以下六大模块: 导入模型情况:模型格式、名称 参数配置情况:设置了哪些参数进行优化,例如 --fp16等 设备情况:当前GPU device具体信息 计算图优化细节:详细描述网络层融合情况,计算图优化结果 网络层实现方式选择(几千行):打印每个网络层选择的kernel的过程,挑选耗时最低的方法 耗时统计:统计推理耗时时间,包括数据拷贝、推理等耗时的统计值 trtexec --onnx=resnet50_bs_dynamic.onnx --saveEngine=demo.engine --verbose > verbose.log 执行以下命令,可获得日志文件,下面对主要内容进行介绍。 Model Options :包含导入的模型内容 [08/20/2023-11:59:45] [I] === Model Options === [08/20/2023-11:59:45] [I] Format: ONNX [08/20/2023-11:59:45] [I] Model: resnet50_bs_dynamic.onnx [08/20/2023-11:59:45] [I] Output: Build Options:创建trt模型的参数设置 [08/20/2023-11:59:45] [I] === Build Options === [08/20/2023-11:59:45] [I] Max batch: explicit batch [08/20/2023-11:59:45] [I] Memory Pools: workspace: default, dlaSRAM: default, dlaLocalDRAM: default, dlaGlobalDRAM: default [08/20/2023-11:59:45] [I] minTiming: 1 [08/20/2023-11:59:45] [I] avgTiming: 8 [08/20/2023-11:59:45] [I] Precision: FP32 ... 推理设置 [08/20/2023-11:59:45] [I] === Inference Options=== [08/20/2023-11:59:45] [I] Batch: Explicit [08/20/2023-11:59:45] [I] Input inference shapes: model [08/20/2023-11:59:45] [I] Iterations: 10 [08/20/2023-11:59:45] [I] Duration: 3s (+ 200ms warm up) [08/20/2023-11:59:45] [I] Sleep time: 0ms [08/20/2023-11:59:45] [I] Idle time: 0ms [08/20/2023-11:59:45] [I] Inference Streams: 1 日志输出设置 [08/20/2023-11:59:45] [I] === Reporting Options === [08/20/2023-11:59:45] [I] Verbose: Enabled [08/20/2023-11:59:45] [I] Averages: 10 inferences [08/20/2023-11:59:45] [I] Percentiles: 90,95,99 [08/20/2023-11:59:45] [I] Dump refittable layers:Disabled [08/20/2023-11:59:45] [I] Dump output: Disabled [08/20/2023-11:59:45] [I] Profile: Disabled [08/20/2023-11:59:45] [I] Export timing to JSON file: [08/20/2023-11:59:45] [I] Export output to JSON file: [08/20/2023-11:59:45] [I] Export profile to JSON file: 设备信息 [08/20/2023-11:59:46] [I] === Device Information === [08/20/2023-11:59:46] [I] Selected Device: NVIDIA GeForce RTX 3060 Laptop GPU [08/20/2023-11:59:46] [I] Compute Capability: 8.6 [08/20/2023-11:59:46] [I] SMs: 30 [08/20/2023-11:59:46] [I] Device Global Memory: 6143 MiB [08/20/2023-11:59:46] [I] Shared Memory per SM: 100 KiB [08/20/2023-11:59:46] [I] Memory Bus Width: 192 bits (ECC disabled) [08/20/2023-11:59:46] [I] Application Compute Clock Rate: 1.702 GHz [08/20/2023-11:59:46] [I] Application Memory Clock Rate: 7.001 GHz [03/28/2024-15:01:18] [I] === Device Information === [03/28/2024-15:01:20] [I] Available Devices: [03/28/2024-15:01:20] [I] Device 0: \"NVIDIA GeForce RTX 4060 Laptop GPU [03/28/2024-15:01:20] [I] Selected Device: NVIDIA GeForce RTX 4060 Laptop GPU [03/28/2024-15:01:20] [I] Selected Device ID: 0 [03/28/2024-15:01:20] [I] Compute Capability: 8.9 [03/28/2024-15:01:20] [I] SMs: 24 [03/28/2024-15:01:20] [I] Device Global Memory: 8187 MiB [03/28/2024-15:01:20] [I] Shared Memory per SM: 100 KiB [03/28/2024-15:01:20] [I] Memory Bus Width: 128 bits (ECC disabled) [03/28/2024-15:01:20] [I] Application Compute Clock Rate: 1.89 GHz [03/28/2024-15:01:20] [I] Application Memory Clock Rate: 8.001 GHz 补充一个4060的显卡信息,可以看到SMs是少于3060的,这个与基本厂商的刀法有关。虽然是4060的设备,但是计算性能比不上3060设备。因为里边的核心——SMs是少于3060的30个SM的。“SMs” 代表 “Streaming Multiprocessors”(流处理器),流处理器是执行 CUDA 核心的基本单元,SM越大算力越大。 对于RTX 4060 Laptop,官方显示有3072个CUDA核心,对应24个SM,即一个SM有128个CUDA核心。 对于RTX 3060 Laptop,官方显示有3840个CUDA核心,对应30个SM,也是符合一个SM有128个CUDA核心的。 4060不仅流处理器少,带宽也低,128 bits VS 192 bits,唯一的优点就是8GB VS 6GB了。 ONNX模型加载及创建 解析模型耗时0.14秒,总共126层,后续trt会针对该模型进行优化。 [08/20/2023-11:59:52] [I] [TRT] ---------------------------------------------------------------- [08/20/2023-11:59:52] [I] [TRT] Input filename: resnet50_bs_dynamic.onnx [08/20/2023-11:59:52] [I] [TRT] ONNX IR version: 0.0.7 [08/20/2023-11:59:52] [I] [TRT] Opset version: 13 [08/20/2023-11:59:52] [I] [TRT] Producer name: pytorch [08/20/2023-11:59:52] [I] [TRT] Producer version: 1.12.0 [08/20/2023-11:59:52] [I] [TRT] Domain: [08/20/2023-11:59:52] [I] [TRT] Model version: 0 [08/20/2023-11:59:52] [I] [TRT] Doc string: [08/20/2023-11:59:52] [I] [TRT] ---------------------------------------------------------------- [08/20/2023-11:59:52] [V] [TRT] Plugin creator already registered - ::BatchedNMSDynamic_TRT version 1 [08/20/2023-11:59:52] [V] [TRT] Plugin creator already registered - ::BatchedNMS_TRT version 1 [08/20/2023-11:59:52] [V] [TRT] Plugin creator already registered - ::BatchTilePlugin_TRT version 1 ... [08/20/2023-11:59:52] [V] [TRT] Adding network input: input with dtype: float32, dimensions: (-1, 3, 224, 224) [08/20/2023-11:59:52] [V] [TRT] Registering tensor: input for ONNX tensor: input [08/20/2023-11:59:52] [V] [TRT] Importing initializer: fc.weight [08/20/2023-11:59:52] [V] [TRT] Importing initializer: fc.bias [08/20/2023-11:59:52] [V] [TRT] Importing initializer: onnx::Conv_497 [08/20/2023-11:59:52] [V] [TRT] Importing initializer: onnx::Conv_498 [08/20/2023-11:59:52] [V] [TRT] Importing initializer: onnx::Conv_500 ... [08/20/2023-11:59:52] [V] [TRT] Searching for input: onnx::Conv_497 [08/20/2023-11:59:52] [V] [TRT] Searching for input: onnx::Conv_498 [08/20/2023-11:59:52] [V] [TRT] Conv_0 [Conv] inputs: [input -> (-1, 3, 224, 224)[FLOAT]], [onnx::Conv_497 -> (64, 3, 7, 7)[FLOAT]], [onnx::Conv_498 -> (64)[FLOAT]], [08/20/2023-11:59:52] [V] [TRT] Convolution input dimensions: (-1, 3, 224, 224) [08/20/2023-11:59:52] [V] [TRT] Registering layer: Conv_0 for ONNX node: Conv_0 ... [08/20/2023-11:59:52] [V] [TRT] Marking output_1 as output: output [08/20/2023-11:59:52] [I] Finished parsing network model. Parse time: 0.141545 [08/20/2023-11:59:52] [V] [TRT] After dead-layer removal: 126 layers [08/20/2023-11:59:52] [V] [TRT] Graph construction completed in 0.0015515 seconds. 计算图优化 优化计算图,可以使得推理速度更快,在本案例中,将模型从126层优化到57层 [08/20/2023-11:59:52] [I] [TRT] Graph optimization time: 0.0150853 seconds. 计算图优化中采用了大量的层融合,融合的原理是尽可能地合并不同层之间相关的计算,避免不必要的中间tensor生成, 减少内存读写, 降低计算消耗,最终提高推理效率 常见的优化方法如下: ConstShuffleFusion: 在fc层的bias中使用,可以将常量shuffle到bias数据中,减少冗余计算。 ShuffleShuffleFusion: 在flatten层中使用,可以减少shuffle的计算次数。 ConvReshapeBiasAddFusion: 将conv层的输出reshape,然后进行bias add的计算融合到一起,减少运算耗时。 ConvReluFusion: 将conv层和后续的Relu激活函数层融合,可以减少一次Relu的计算。 ConvEltwiseSumFusion: 将conv层和element-wise add层融合,避免重复计算。 ReduceToPoolingFusion: 将reduce层修改为pooling层,减少运算消耗。 ConcatReluFusion: 将concat层和relu层融合,减少relu计算次数。 BiasSoftmaxFusion: 融合bias层和softmax层,减少冗余计算。 [08/20/2023-11:59:52] [V] [TRT] Running: ConstShuffleFusion on fc.bias [08/20/2023-11:59:52] [V] [TRT] ConstShuffleFusion: Fusing fc.bias with (Unnamed Layer* 129) [Shuffle] [08/20/2023-11:59:52] [V] [TRT] After Myelin optimization: 125 layers ... [08/20/2023-11:59:52] [V] [TRT] After dupe layer removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After final dead-layer removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After tensor merging: 57 layers [08/20/2023-11:59:52] [V] [TRT] After vertical fusions: 57 layers [08/20/2023-11:59:52] [V] [TRT] After dupe layer removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After final dead-layer removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After tensor merging: 57 layers [08/20/2023-11:59:52] [V] [TRT] After slice removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After concat removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] Trying to split Reshape and strided tensor [08/20/2023-11:59:52] [I] [TRT] Graph optimization time: 0.0150853 seconds. 各网络层实现方式选择 网络层具体的实现有多种方式,例如不同的底层库、不同的实现算法、不同的算法策略,在TensorRT中会把所有的实现方式跑一遍,挑选速度最优的实现方式。 在实现网络层的过程中,runner和tactic是TensorRT中用于实现layer的关键组件。 runner代表着一种实现layer的算法或代码路径。例如,卷积层可以通过cudnn、cublas或者TensorRT自身的cask实现。runner封装了具体的实现算法。 tactic代表具体的实现方案。每个runner下面可以有多个tactic,对应不同的优化方法。例如cask convolution runner下面可以有基于tensor core的tactic,或是各种tile size的tactic等等。tactic包含了针对特定layer进行各种优化的代码实现。 所以TensorRT通过组合不同的runner和tactic,就可以得到层的多种实现方式。然后通过Auto Tuner来测试不同组合的性能,选择出最优的实现。 例如,对于一个卷积层: runner可以是cudnn、cublas、cask convolution等 cask convolution下面可以有基于tensor core的tactic,tile size为32x32或64x64的tactic等等 最终会选择出cask convolution + 64x64 tile size这个tactic组合作为最优实现 在本日志中,第一个runner跑的是conv_0 + Relu_1,最终选择的Tactic Name是 0x9cb304e2edbc1221,耗时0.040秒。 [08/20/2023-11:59:52] [V] [TRT] =============== Computing costs for [08/20/2023-11:59:52] [V] [TRT] *************** Autotuning format combination: Float(150528,50176,224,1) -> Float(802816,12544,112,1) *************** [08/20/2023-11:59:52] [V] [TRT] --------------- Timing Runner: Conv_0 + Relu_1 (CaskConvolution[0x80000009]) [08/20/2023-11:59:52] [V] [TRT] Tactic Name: sm50_xmma_conv_fprop_fused_conv_act_fp32_NCHW_fp32_NCHW_KCRS_fp32_fp32_fp32_Accfloat_1_1_cC1_dC1_srcVec1_fltVec1_1_TP3_TQ4_C1_R7_S7_U2_V2 Tactic: 0x0a617a3531b5b6dc Time: 0.102619 [08/20/2023-11:59:52] [V] [TRT] Tactic Name: sm50_xmma_conv_fprop_fused_conv_act_fp32_NCHW_fp32_NCHW_KCRS_fp32_fp32_fp32_Accfloat_1_1_cC1_dC1_srcVec1_fltVec2_2_TP3_TQ4_C1_R7_S7_U2_V2 Tactic: 0x520e893be7313ed2 Time: 0.0999131 ... [08/20/2023-11:59:52] [V] [TRT] Conv_0 + Relu_1 (CaskConvolution[0x80000009]) profiling completed in 0.0647644 seconds. Fastest Tactic: 0x9cb304e2edbc1221 Time: 0.0402286 最终trt将57个层都进行了Computing costs,得到各网络层的最优实现方案。 除了网络层,还需要reformat layer,它的作用是改变tensor的格式,将前一层的输出重新排布成后一层所需的格式。这样就可以使得两层之间的tensor兼容,然后进行融合。 例如:Conv_0 + Relu_1层需要[50176,1:4,224,1]格式的tensor作为输入,而输入层输出的是[150528,50176,224,1]格式,所以在输入层和Conv_0层之间加入了reformat layer,将tensor重新排布成Conv层需要的格式。 最终添加了25个reformat layer,模型变为了82层。 ... [08/20/2023-12:00:07] [V] [TRT] Adding reformat layer: Reformatted Input Tensor 0 to Gemm_121 (onnx::Flatten_493) from Float(512,1:4,512,512) to Float(2048,1,1,1) [08/20/2023-12:00:07] [V] [TRT] Formats and tactics selection completed in 15.1664 seconds. [08/20/2023-12:00:07] [V] [TRT] After reformat layers: 82 layers [08/20/2023-12:00:07] [V] [TRT] Total number of blocks in pre-optimized block assignment: 82 [08/20/2023-12:00:07] [I] [TRT] Detected 1 inputs and 1 output network tensors. 存储空间占用情况 介绍各网络层存储占用情况,以及汇总,例如本案例,engine的GPU占用是107MB ... [08/20/2023-12:00:07] [V] [TRT] Layer: Conv_116 + Add_117 + Relu_118 Host Persistent: 7200 Device Persistent: 0 Scratch Memory: 0 [08/20/2023-12:00:07] [V] [TRT] Layer: GlobalAveragePool_119 Host Persistent: 4176 Device Persistent: 0 Scratch Memory: 0 [08/20/2023-12:00:07] [V] [TRT] Layer: Gemm_121 Host Persistent: 6944 Device Persistent: 0 Scratch Memory: 0 [08/20/2023-12:00:07] [V] [TRT] Skipped printing memory information for 26 layers with 0 memory size i.e. Host Persistent + Device Persistent + Scratch Memory == 0. [08/20/2023-12:00:07] [I] [TRT] Total Host Persistent Memory: 331696 [08/20/2023-12:00:07] [I] [TRT] Total Device Persistent Memory: 22016 [08/20/2023-12:00:07] [I] [TRT] Total Scratch Memory: 4608 [08/20/2023-12:00:07] [I] [TRT] [MemUsageStats] Peak memory usage of TRT CPU/GPU memory allocators: CPU 18 MiB, GPU 107 MiB engine构建情况 对完成好的engine各网络层、网络层对应的kernel选择情况进行打印。 可以看到engine的构建耗时15.3秒 [08/20/2023-12:00:07] [V] [TRT] Engine generation completed in 15.3167 seconds. [08/20/2023-12:00:07] [V] [TRT] Deleting timing cache: 214 entries, served 528 hits since creation. [08/20/2023-12:00:07] [V] [TRT] Engine Layer Information: Layer(Reformat): Reformatting CopyNode for Input Tensor 0 to Conv_0 + Relu_1, Tactic: 0x00000000000003e8, input (Float[1,3,224,224]) -> Reformatted Input Tensor 0 to Conv_0 + Relu_1 (Float[1,3:4,224,224]) Layer(CaskConvolution): Conv_0 + Relu_1, Tactic: 0x9cb304e2edbc1221, Reformatted Input Tensor 0 to Conv_0 + Relu_1 (Float[1,3:4,224,224]) -> onnx::MaxPool_323 (Float[1,64:4,112,112]) 推理耗时统计 进行10次推理,依次得到以下信息,同时相应的统计值。 Throughput:模型的推理吞吐量,以每秒推理数量(QPS)为单位。实际图片量需要乘以batchsize。 Latency:模型一次推理的延迟时间统计信息,包括最小值、最大值、平均值、中位数和百分位数(90%、95%和99%)。 Enqueue Time:将数据传输到GPU的时间统计信息, H2D Latency:将主机数据传输到GPU的延迟时间统计信息, GPU Compute Time:模型在GPU上运行的计算时间统计信息 D2H Latency:从GPU将数据传输回主机的延迟时间统计信息 Total Host Walltime:模型推理的总时间,包括传输数据、计算和传输数据回主机的时间。 Total GPU Compute Time:模型在GPU上的总计算时间。 [08/20/2023-12:00:11] [I] === Performance summary === [08/20/2023-12:00:11] [I] Throughput: 502.107 qps [08/20/2023-12:00:11] [I] Latency: min = 1.88583 ms, max = 2.96844 ms, mean = 1.93245 ms, median = 1.91833 ms, percentile(90%) = 1.9592 ms, percentile(95%) = 1.98364 ms, percentile(99%) = 2.34845 ms [08/20/2023-12:00:11] [I] Enqueue Time: min = 0.312988 ms, max = 1.77197 ms, mean = 0.46439 ms, median = 0.390869 ms, percentile(90%) = 0.748291 ms, percentile(95%) = 0.836853 ms, percentile(99%) = 1.10229 ms [08/20/2023-12:00:11] [I] H2D Latency: min = 0.0714111 ms, max = 0.225464 ms, mean = 0.0769845 ms, median = 0.0737305 ms, percentile(90%) = 0.088623 ms, percentile(95%) = 0.0947266 ms, percentile(99%) = 0.112671 ms [08/20/2023-12:00:11] [I] GPU Compute Time: min = 1.80939 ms, max = 2.86005 ms, mean = 1.8518 ms, median = 1.84009 ms, percentile(90%) = 1.87183 ms, percentile(95%) = 1.89734 ms, percentile(99%) = 2.22314 ms [08/20/2023-12:00:11] [I] D2H Latency: min = 0.00317383 ms, max = 0.0220947 ms, mean = 0.00366304 ms, median = 0.00341797 ms, percentile(90%) = 0.00390625 ms, percentile(95%) = 0.00402832 ms, percentile(99%) = 0.00488281 ms [08/20/2023-12:00:11] [I] Total Host Walltime: 3.00334 s [08/20/2023-12:00:11] [I] Total GPU Compute Time: 2.79252 s [08/20/2023-12:00:11] [I] Explanations of the performance metrics are printed in the verbose logs. [08/20/2023-12:00:11] [V] [08/20/2023-12:00:11] [V] === Explanations of the performance metrics === [08/20/2023-12:00:11] [V] Total Host Walltime: the host walltime from when the first query (after warmups) is enqueued to when the last query is completed. [08/20/2023-12:00:11] [V] GPU Compute Time: the GPU latency to execute the kernels for a query. [08/20/2023-12:00:11] [V] Total GPU Compute Time: the summation of the GPU Compute Time of all the queries. If this is significantly shorter than Total Host Walltime, the GPU may be under-utilized because of host-side overheads or data transfers. [08/20/2023-12:00:11] [V] Throughput: the observed throughput computed by dividing the number of queries by the Total Host Walltime. If this is significantly lower than the reciprocal of GPU Compute Time, the GPU may be under-utilized because of host-side overheads or data transfers. [08/20/2023-12:00:11] [V] Enqueue Time: the host latency to enqueue a query. If this is longer than GPU Compute Time, the GPU may be under-utilized. [08/20/2023-12:00:11] [V] H2D Latency: the latency for host-to-device data transfers for input tensors of a single query. [08/20/2023-12:00:11] [V] D2H Latency: the latency for device-to-host data transfers for output tensors of a single query. [08/20/2023-12:00:11] [V] Latency: the summation of H2D Latency, GPU Compute Time, and D2H Latency. This is the latency to infer a single query. [08/20/2023-12:00:11] [I] 案例5:trt模型推理 通过推理trt模型,可以查看网络层信息、网络层推理耗时情况 trtexec --loadEngine=resnet50_bs_128_fp32.engine --batch=128 --useCudaGraph --dumpProfile --dumpLayerInfo > inference.log 可以看到,卷积层耗时较大 8/20/2023-17:51:29] [I] === Profile (32 iterations ) === [08/20/2023-17:51:29] [I] Layer Time (ms) Avg. Time (ms) Median Time (ms) Time % [08/20/2023-17:51:29] [I] Reformatting CopyNode for Input Tensor 0 to Conv_0 + Relu_1 18.53 0.5790 0.5765 0.6 [08/20/2023-17:51:29] [I] Conv_0 + Relu_1 116.65 3.6453 3.6336 3.6 [08/20/2023-17:51:29] [I] MaxPool_2 51.21 1.6004 1.6005 1.6 小节 本节介绍了trtexec基础用法,可以通过trtexec实现onnx模型转trt模型,并且可以进行动态batchsize设置、半精度量化的选择。 更全面用法推荐查看帮助文档以及官方文档。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.4-trt-tools.html":{"url":"chapter-12/12.4-trt-tools.html","title":"12.4 TensorRT 实用工具","keywords":"","body":"12.4 TensorRT 实用工具 前言 工程化部署是一个复杂的任务,涉及的环节众多,因此需要有足够好的工具来检测、分析,NVIDIA也提供了一系列工具用于分析、调试、优化部署环节。本节就介绍两个实用工具,nsight system 和 polygraphy。 nsight system可分析cpu和gpu的性能,可找出应用程序的瓶颈。 polygraphy可在各种框架中运行和调试深度学习模型,用于分析模型转换间的瓶颈。 nsight system NVIDIA Nsight Systems是一个系统分析工具,它可以分析CPU和GPU的利用率、内存占用、数据输送量等各种性能指标,找出应用程序的瓶颈所在。用户文档 安装 打开官网,选择对应的操作系统、版本进行下载 。 NsightSystems-2023.3.1.92-3314722.msi,双击安装,一路默认 将目录添加到环境变量:C:\\Program Files\\NVIDIA Corporation\\Nsight Systems 2023.3.1\\target-windows-x64 将gui工作目录页添加到环境变量:C:\\Program Files\\NVIDIA Corporation\\Nsight Systems 2023.3.1\\host-windows-x64 运行 nsys包括命令行工具与UI界面,这里采用UI界面演示。 命令行工具是C:\\Program Files\\NVIDIA Corporation\\Nsight Systems 2023.3.1\\target-windows-x64\\nsys.exe UI界面是C:\\Program Files\\NVIDIA Corporation\\Nsight Systems 2023.3.1\\host-windows-x64\\nsys-ui.exe nsys运行逻辑是从nsys端启动任务,nsys会自动监控任务的性能。 第一步:启动nsys。在cmd中输入nsys-ui,或者到安装目录下双击nsys-ui.exe。 第二步:创建project,配置要运行的程序。在这里运行本章配套代码01_trt_resnet50_cuda.py。具体操作如下图所示 第三步:查看统计信息 nsight system是一个强大的软件,但具体如何有效使用,以及如何更细粒度、更接近底层的去分析耗时,请大家参照官方文档以及需求来学习。 polygraphy polygraphy是TensorRT生态中重要的debug调试工具,它可以 使用多种后端运行推理计算,包括 TensorRT, onnxruntime, TensorFlow; 比较不同后端的逐层计算结果; 由模型文件生成 TensorRT 引擎并序列化为.plan; 查看模型网络的逐层信息; 修改 Onnx 模型,如提取子图,计算图化简; 分析 Onnx 转 TensorRT 失败原因,将原计算图中可以 / 不可以转 TensorRT 的子图分割保存; 隔离 TensorRT 中错误的tactic; 常用的几个功能是: 检验 TensorRT 上计算结果正确性 /精度 找出计算错误 / 精度不足的层 进行简单的计算图优化 安装 pip install nvidia-pyindex pip install polygraphy 验证 polygraphy依托于虚拟环境运行,因此需要激活相应的虚拟环境,然后执行 polygraphy -h polygraphy有七种模式,分别是 {run,convert,inspect,surgeon,template,debug,data},具体含义参见文档 (pt112) C:\\Users\\yts32>polygraphy -h usage: polygraphy [-h] [-v] {run,convert,inspect,check,surgeon,template,debug,data} ... Polygraphy: A Deep Learning Debugging Toolkit optional arguments: -h, --help show this help message and exit -v, --version show program's version number and exit Tools: {run,convert,inspect,check,surgeon,template,debug,data} run Run inference and compare results across backends. convert Convert models to other formats. inspect View information about various types of files. check Check and validate various aspects of a model surgeon Modify ONNX models. template [EXPERIMENTAL] Generate template files. debug [EXPERIMENTAL] Debug a wide variety of model issues. data Manipulate input and output data generated by other Polygraphy subtools. 案例1:运行onnx及trt模型 polygraphy run resnet50_bs_1.onnx --onnxrt polygraphy run resnet50_bs_1.engine --trt --input-shapes 'input:[1,3,224,224]' --verbose 得到如下运行日志,表明两个框架推理运行成功: ...... | Completed 1 iteration(s) in 1958 ms | Average inference time: 1958 ms. ...... | Completed 1 iteration(s) in 120.1 ms | Average inference time: 120.1 ms. 案例2:对比onnx与trt输出结果(常用) polygraphy还可以充当trtexec的功能,可以实现onnx导出trt模型,并且进行逐层结果对比。 其中atol表示绝对误差,rtol表示相对误差。 polygraphy run resnet50_bs_1.onnx --onnxrt --trt ^ --save-engine=resnet50_bs_1_fp32_polygraphy.engine ^ --onnx-outputs mark all --trt-outputs mark all ^ --input-shapes \"input:[1,3,224,224]\" ^ --atol 1e-3 --rtol 1e-3 --verbose > onnx-trt-compare.log 输出的日志如下: 对于每一个网络层会输出onnx、trt的直方图,绝对误差直方图,相对误差直方图 最后会统计所有网络层符合设置的超参数atol, rtol的百分比,本案例中 Pass Rate: 100.0%。 [I] Comparing Output: 'input.4' (dtype=float32, shape=(1, 64, 112, 112)) with 'input.4' (dtype=float32, shape=(1, 64, 112, 112)) [I] Tolerance: [abs=0.001, rel=0.001] | Checking elemwise error [I] onnxrt-runner-N0-08/20/23-23:08:36: input.4 | Stats: mean=0.20451, std-dev=0.31748, var=0.10079, median=0.19194, min=-1.3369 at (0, 33, 13, 104), max=2.0691 at (0, 33, 64, 77), avg-magnitude=0.29563 [V] ---- Histogram ---- Bin Range | Num Elems | Visualization (-1.34 , -0.996) | 29 | (-0.996, -0.656) | 11765 | # (-0.656, -0.315) | 31237 | ### (-0.315, 0.0255) | 140832 | ############# (0.0255, 0.366 ) | 411249 | ######################################## (0.366 , 0.707 ) | 166138 | ################ (0.707 , 1.05 ) | 40956 | ### (1.05 , 1.39 ) | 551 | (1.39 , 1.73 ) | 54 | (1.73 , 2.07 ) | 5 | [I] trt-runner-N0-08/20/23-23:08:36: input.4 | Stats: mean=0.20451, std-dev=0.31748, var=0.10079, median=0.19194, min=-1.3369 at (0, 33, 13, 104), max=2.0691 at (0, 33, 64, 77), avg-magnitude=0.29563 [V] ---- Histogram ---- Bin Range | Num Elems | Visualization (-1.34 , -0.996) | 29 | (-0.996, -0.656) | 11765 | # (-0.656, -0.315) | 31237 | ### (-0.315, 0.0255) | 140832 | ############# (0.0255, 0.366 ) | 411249 | ######################################## (0.366 , 0.707 ) | 166138 | ################ (0.707 , 1.05 ) | 40956 | ### (1.05 , 1.39 ) | 551 | (1.39 , 1.73 ) | 54 | (1.73 , 2.07 ) | 5 | [I] Error Metrics: input.4 [I] Minimum Required Tolerance: elemwise error | [abs=8.3447e-07] OR [rel=0.037037] (requirements may be lower if both abs/rel tolerances are set) [I] Absolute Difference | Stats: mean=2.6075e-08, std-dev=3.2558e-08, var=1.06e-15, median=1.4901e-08, min=0 at (0, 0, 0, 3), max=8.3447e-07 at (0, 33, 86, 43), avg-magnitude=2.6075e-08 [V] ---- Histogram ---- Bin Range | Num Elems | Visualization (0 , 8.34e-08) | 757931 | ######################################## (8.34e-08, 1.67e-07) | 40058 | ## (1.67e-07, 2.5e-07 ) | 4334 | (2.5e-07 , 3.34e-07) | 249 | (3.34e-07, 4.17e-07) | 181 | (4.17e-07, 5.01e-07) | 53 | (5.01e-07, 5.84e-07) | 0 | (5.84e-07, 6.68e-07) | 8 | (6.68e-07, 7.51e-07) | 1 | (7.51e-07, 8.34e-07) | 1 | [I] Relative Difference | Stats: mean=6.039e-07, std-dev=5.4597e-05, var=2.9809e-09, median=8.7838e-08, min=0 at (0, 0, 0, 3), max=0.037037 at (0, 4, 15, 12), avg-magnitude=6.039e-07 [V] ---- Histogram ---- Bin Range | Num Elems | Visualization (0 , 0.0037 ) | 802806 | ######################################## (0.0037 , 0.00741) | 7 | (0.00741, 0.0111 ) | 1 | (0.0111 , 0.0148 ) | 0 | (0.0148 , 0.0185 ) | 0 | (0.0185 , 0.0222 ) | 0 | (0.0222 , 0.0259 ) | 1 | (0.0259 , 0.0296 ) | 0 | (0.0296 , 0.0333 ) | 0 | (0.0333 , 0.037 ) | 1 | [I] PASSED | Output: 'input.4' | Difference is within tolerance (rel=0.001, abs=0.001) [I] PASSED | All outputs matched | Outputs: ['input.4', 'onnx::MaxPool_323', 'input.8', 'input.16', 'onnx::Conv_327', 'input.24', 'onnx::Conv_330', 'onnx::Add_505', 'onnx::Add_508', 'onnx::Relu_335', 'input.36', 'input.44', 'onnx::Conv_339', 'input.52', 'onnx::Conv_342', 'onnx::Add_517', 'onnx::Relu_345', 'input.60', 'input.68', 'onnx::Conv_349', 'input.76', 'onnx::Conv_352', 'onnx::Add_526', 'onnx::Relu_355', 'input.84', 'input.92', 'onnx::Conv_359', 'input.100', 'onnx::Conv_362', 'onnx::Add_535', 'onnx::Add_538', 'onnx::Relu_367', 'input.112', 'input.120', 'onnx::Conv_371', 'input.128', 'onnx::Conv_374', 'onnx::Add_547', 'onnx::Relu_377', 'input.136', 'input.144', 'onnx::Conv_381', 'input.152', 'onnx::Conv_384', 'onnx::Add_556', 'onnx::Relu_387', 'input.160', 'input.168', 'onnx::Conv_391', 'input.176', 'onnx::Conv_394', 'onnx::Add_565', 'onnx::Relu_397', 'input.184', 'input.192', 'onnx::Conv_401', 'input.200', 'onnx::Conv_404', 'onnx::Add_574', 'onnx::Add_577', 'onnx::Relu_409', 'input.212', 'input.220', 'onnx::Conv_413', 'input.228', 'onnx::Conv_416', 'onnx::Add_586', 'onnx::Relu_419', 'input.236', 'input.244', 'onnx::Conv_423', 'input.252', 'onnx::Conv_426', 'onnx::Add_595', 'onnx::Relu_429', 'input.260', 'input.268', 'onnx::Conv_433', 'input.276', 'onnx::Conv_436', 'onnx::Add_604', 'onnx::Relu_439', 'input.284', 'input.292', 'onnx::Conv_443', 'input.300', 'onnx::Conv_446', 'onnx::Add_613', 'onnx::Relu_449', 'input.308', 'input.316', 'onnx::Conv_453', 'input.324', 'onnx::Conv_456', 'onnx::Add_622', 'onnx::Relu_459', 'input.332', 'input.340', 'onnx::Conv_463', 'input.348', 'onnx::Conv_466', 'onnx::Add_631', 'onnx::Add_634', 'onnx::Relu_471', 'input.360', 'input.368', 'onnx::Conv_475', 'input.376', 'onnx::Conv_478', 'onnx::Add_643', 'onnx::Relu_481', 'input.384', 'input.392', 'onnx::Conv_485', 'input.400', 'onnx::Conv_488', 'onnx::Add_652', 'onnx::Relu_491', 'input.408', 'onnx::Flatten_493', 'onnx::Gemm_494', 'output'] [I] Accuracy Summary | onnxrt-runner-N0-08/20/23-23:08:36 vs. trt-runner-N0-08/20/23-23:08:36 | Passed: 1/1 iterations | Pass Rate: 100.0% 更多使用案例推荐阅读github cookbook 小结 本节介绍了nsight system和polygraphy的应用,在模型部署全流程中,可以深入挖掘的还有很多,推荐查看TensorRT的GitHub下的tools目录 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.5-trt-python-api.html":{"url":"chapter-12/12.5-trt-python-api.html","title":"12.5 TensorRT API 使用","keywords":"","body":"12.5 TensorRT API 使用 前言 在TensorRT的第二小节中,介绍了TensorRT模型计算图的构建通常有三种方式,分别是TensorRT API、parser、训练框架中trt工具。 三种方式的便捷程度与灵活度是依次递增和递减的,TensorRT API最灵活,可自由设定网络每一层,任意编辑模型的任何层,本节就介绍 TensorRT API的使用。 TensorRT API 简介 TensorRT API是构建Network的一种方法,整个TensorRT的使用流程还是与第二节中介绍的一样,整体流程如下图所示 这里不再赘述workflow内容,直接看TensorRT是如何构建network的,整体可分三个步骤: 创建network:network = builder.create_network() 逐层搭建网络:搭建网络层,并且赋予权重值 创建engine:engine = builder.build_engine(network, config) 由此可见,核心部分在于network提供了一系列网络层的搭建,这类似于pytorch中的nn.Module可以搭建各种网络层。 TensorRT官方提供的网络层可参考文档 下面介绍采用TensorRT API搭建常见网络层的方法。 TensorRT API 网络层创建 卷积层 add_convolution(self: tensorrt.tensorrt.INetworkDefinition, input: tensorrt.tensorrt.ITensor, num_output_maps: int, kernel_shape: tensorrt.tensorrt.DimsHW, kernel: tensorrt.tensorrt.Weights, bias: tensorrt.tensorrt.Weights = None)→ tensorrt.tensorrt.IConvolutionLayer Parameters: input – The input tensor to the convolution. num_output_maps – The number of output feature maps for the convolution. kernel_shape – The dimensions of the convolution kernel. kernel – The kernel weights for the convolution. bias – The optional bias weights for the convolution. Returns:The new convolution layer, or None if it could not be created. 具体的创建案例代码解释如下: 设置输入input:这个input如果是第一层的话,需要自行构建data;如果是中间层,则需要设置为上一层的输出ITensor,例如pool2.get_output(0)返回的是一个ITensor对象。 设置卷积权重:在创建网络时,直接赋予卷积层的权重,这个权重通常通过wst文件获取,是一个字典,key是自行标识的名称,value是np.ndarray。 data = network.add_input('input', trt.float32, (3, 224, 224)) conv1 = network.add_convolution(input=data, num_output_maps=64, kernel_shape=(7, 7), kernel=weight_map[\"conv1.weight\"], bias=trt.Weights()) fc1 = network.add_fully_connected(input=pool2.get_output(0), num_outputs=OUTPUT_SIZE, kernel=weight_map['fc.weight'], bias=weight_map['fc.bias']) BN层 在TensorRT API中没有BN的实现,BN层需要通过add_scale层来实现,即通过减法、乘法的操作等价于实现BN层。 下面代码是实现过程,核心是手动计算出需要shift和scale的数值,然后创建add_scale层。 def addBatchNorm2d(network, weight_map, input, layer_name, eps): gamma = weight_map[layer_name + \".weight\"] beta = weight_map[layer_name + \".bias\"] mean = weight_map[layer_name + \".running_mean\"] var = weight_map[layer_name + \".running_var\"] var = np.sqrt(var + eps) scale = gamma / var shift = -mean / var * gamma + beta return network.add_scale(input=input, mode=trt.ScaleMode.CHANNEL, shift=shift, scale=scale) 激活函数层 激活函数层通过add_activation,其中提供了type属性来设置不同的激活函数。 relu3 = network.add_activation(ew1.get_output(0), type=trt.ActivationType.RELU) type中可设置的类型有十多种,具体参考IActivationLayer的文档 RELU : Rectified Linear activation SIGMOID : Sigmoid activation TANH : Hyperbolic Tangent activation LEAKY_RELU : Leaky Relu activation: f(x) = x if x >= 0, f(x) = alpha * x if x = 0, f(x) = alpha * (exp(x) - 1) if x 0, f(x) = beta * (alpha * exp(x) - alpha) if x alpha, f(x) = 0 if x 池化层 pool1 = network.add_pooling(input=relu1.get_output(0), window_size=trt.DimsHW(3, 3), type=trt.PoolingType.MAX) 全连接层 全连接层在v8.6.1之后进行了删除,不再支持直接创建,需要用add_matrix_multiply、add_elementwise间接实现。 这个问题在官方文档没有指引,是通过多种方法找到的替代方案,这里简单记录debug过程。 运行报错:AttributeError: 'tensorrt.tensorrt.INetworkDefinition' object has no attribute 'add_fully_connected' 各种文档找'add_fully_connected',无果; 官方文档查阅 ‘INetworkDefinition' 支持的方法,的确没了add_fully_connected,猜测肯定有对应方法替换,梳理所有支持的方法,找到最相关的方法是:add_matrix_multiply TRT github仓库搜索add_matrix_multiply, sample.py,发现了FC层实现方法: https://github.com/NVIDIA/TensorRT/blob/c0c633cc629cc0705f0f69359f531a192e524c0f/samples/python/network_api_pytorch_mnist/sample.py 具体方法如下: def add_matmul_as_fc(net, input, outputs, w, b): assert len(input.shape) >= 3 m = 1 if len(input.shape) == 3 else input.shape[0] k = int(np.prod(input.shape) / m) # 输入大小: 2048 assert np.prod(input.shape) == m * k n = int(w.size / k) # 输出大小: 1000 assert w.size == n * k assert b.size == n input_reshape = net.add_shuffle(input) input_reshape.reshape_dims = trt.Dims2(m, k) filter_const = net.add_constant(trt.Dims2(n, k), w) mm = net.add_matrix_multiply( input_reshape.get_output(0), trt.MatrixOperation.NONE, filter_const.get_output(0), trt.MatrixOperation.TRANSPOSE, ) bias_const = net.add_constant(trt.Dims2(1, n), b) bias_add = net.add_elementwise(mm.get_output(0), bias_const.get_output(0), trt.ElementWiseOperation.SUM) output_reshape = net.add_shuffle(bias_add.get_output(0)) output_reshape.reshape_dims = trt.Dims4(m, n, 1, 1) return output_reshape V 8.6.1版本可用的方法如下: fc1 = network.add_fully_connected(input=pool2.get_output(0), num_outputs=OUTPUT_SIZE, kernel=weight_map['fc.weight'], bias=weight_map['fc.bias']) 以上是常见的网络层介绍,更多网络层参见文档 tensorrtx 学习TensorRT API搭建网络模型,十分推荐根据tensorrtx的代码资料学习。 tensorrtx是一个基于TensorRT API构建常用的网络模型库,它完全基于TensorRT API搭建模型,这使得模型的构建具备高可调节性,也对理解、学习网络模型内部细节提供了很好的资料。 tensorrtx为模型提供了python和c++的代码,使用tensorrtx的工作流程如下: 获取训练好的模型权重文件,例如pytorch的pt文件,tensorflow的ckpt文件,MXNet的.params+.json等 将权重文件导出为.wts文件,wts文件TensorRT定义的一种模型参数文件格式 创建network,逐网络层搭建模型,并基于wts文件赋予网络层权重值 搭建engine,进行推理或序列化保存到磁盘。 wts 文件 这里遇到一个新的文件格式wts,wts是Weights的缩写,是由Nvidia定义的一种模型参数文件格式。 wts文件只包含模型的参数数据,不包含网络结构等其他信息。网络结构需要另外定义,在这里就需要用TensorRT API逐层定义。 通过使用wts文件分离参数和网络结构,可以方便地进行模型压缩、量化等优化,也可以跨框架部署模型,同时还可保护模型的结构不被泄露。 ResNet50 推理 接下来演示如何采用TensorRT API搭建ResNet50,并完成推理,代码来自tensorrx和pytorchx。 整体分两步: 第一步,通过pytorchx的代码获得resnet.wts。在本教程,resnet.wts的生成可通过配套代码实现 第二步,通过tensorrtx的代码搭建TensorRT的network,并且创建engine进行推理。在本教程,推理代码可通过配套代码实现 wts文件生成 上述代码已集成到配套章节代码中,先看生成wts的核心代码: wts文件第一行表明整个文件有多少个权重; 之后的每一行是一个权重名称及权重值的二进制形式。 f = open(path_wts, 'w') f.write(\"{}\\n\".format(len(net.state_dict().keys()))) for k, v in net.state_dict().items(): print('key: ', k) print('value: ', v.shape) vr = v.reshape(-1).cpu().numpy() f.write(\"{} {}\".format(k, len(vr))) for vv in vr: f.write(\" \") f.write(struct.pack(\">f\", float(vv)).hex()) f.write(\"\\n\") 以下是一个wts案例: 10 conv1.weight 150 be40ee1b bd20bab8 bdc4bc53 ....... conv1.bias 6 bd327058 ....... conv2.weight 2400 3c6f2220 3c693090 ...... network创建 接下来参照第一份配套代码,将engine的构建从直接加载文件改为基于network创建的形式 回顾之前直接读取磁盘上的engine文件,可以直接得到engine。 with open(model_path, 'rb') as ff, trt.Runtime(logger) as runtime: engine = runtime.deserialize_cuda_engine(ff.read()) 接下来看network逐层创建的过程,主workflow是一样的,需要创建各模块,这里有两个重点。 trt推荐采用显式batch,因此create_network的时候需要设置1 resnet50的创建,在函数build_model_by_trt_api中实现 def init_model(model_path): logger = trt.Logger(trt.Logger.WARNING) builder = trt.Builder(logger) network = builder.create_network(1 build_model_by_trt_api函数中就是上文提到的TensorRT API的各网络层创建接口,这里就不一一赘述,到这里,整体的流程梳理完成。 def build_model_by_trt_api(network, model_path): weight_map = load_wts_file(model_path) # 权重字典 data = network.add_input(\"data\", trt.float32, (1, 3, 224, 224)) assert data conv1 = network.add_convolution(input=data, num_output_maps=64, kernel_shape=(7, 7), kernel=weight_map[\"conv1.weight\"], bias=trt.Weights()) 最终能得到如下图片,表明基于TensorRT API创建网络模型并推理是成功的。 tensorrtx还提供了非常多常见的网络模型,如果有需要使用 TensorRT API创建网络模型,建议优选tensorrtx中寻找参考案例。 小结 本小节介绍TensorRT API进行trt模型创建的流程及案例。首先介绍了TensorRT API创建网络层的方法及常用接口,随后介绍学习TensorRT API 非常好的代码库——tensorrtx,最后以一个resnet50的推理案例介绍基于TensorRT API 搭建模型的全流程。 通过本小节,可以了解TensorRT API 创建模型的过程与概念,更多案例及用法,请阅读: python api文档:https://docs.nvidia.com/deeplearning/tensorrt/api/python_api/index.htm tensorrtx:https://github.com/wang-xinyu/tensorrtx Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.6-quantization.html":{"url":"chapter-12/12.6-quantization.html","title":"12.6 模型量化基础概念","keywords":"","body":"12.6 模型量化基础概念 前言 量化是模型压缩和加速常用的方法,因其效果相较于蒸馏、剪枝都要好,因此被大量的应用。 TensorRT也提供了模型量化的功能,本节开始来学习如何利用TensorRT进行模型量化,实现模型存储的减小,时延的降低,吞吐量的提升。 由于量化是非常大的概念,知识点和技巧非常多,因此量化将分为三个小节,第一个小节介绍量化基础概念,第二小节介绍TensorRT的PTQ(Post-Trainning Quantization,训练后量化)的方法,第三节介绍QAT(Quantization-aware training,量化感知训练) 。 量化概念 模型量化(model quantization)是通过降低权重和激活值的数据精度,以此减少模型计算时间和能耗的方法。 通常情况下,模型权重和激活值以FP32/FP16进行训练和存储,当精度降低到Int8时,存储数据的内存开销减少了4倍(相对于FP32),矩阵乘法的计算成本减少了16倍。 大量研究及工程实践表明,模型量化有一定的鲁棒性,在降低存储、提高推理效率的同时,模型精度损失相对较小,如Nvidia文章《integer quantization for deep learning inference principles and empirical evaluation》中给出的精度损失统计表格,通常掉点在1个点上下,但可带来几十倍的效率提升。 量化优点 量化最直观的优点是带来速度提升和存储的降低,但其好处远不止这些,它还可以为环境保护,抵御全球变暖提供帮助,因为低比特模型可以带来更少的热量挥发。 根据文章《A Survey of Quantization Methods for Efficient》的分析,FP32的乘/加操作消耗的能量是Int8的30倍/18.5倍,由此可见低比特可以显著降低能量消耗与热量挥发。 由此可知,量化可以带来: 存储降低:通过将权重和激活值从高位精度(如32位)降低到低位精度(如8位),模型所需的存储空间大大减少。 访问减少:低位精度的量化模型需要较少的内存带宽和存储带宽来读取和传输权重和激活值 速度提升:由于低精度模型执行矩阵乘法等计算操作时的计算量减少,模型的推理速度可以显著提高 电费减少:量化模型的计算操作需要更少的能量,功耗降低,从而减少了电费开支 热量减少:由于量化模型对处理器的负载较低,因此产生的热量也相对较少,这对于移动设备和嵌入式系统等散热受限的环境中的长时间性能非常重要。 量化的数学公式 在数字信号处理领域,量化是指将信号的连续取值近似为有限多个离散值的过程,在模型量化中也是类似的,用较少的数(例如:Int8)表示较多的数(例如:FP32)。 用较少的数表示较多的数,实际上是一个线性映射,求一个缩放值scale和一个偏移量offset,将FP32中的数缩放到int8表示的256个数中的某一个数。 可以参考下图,最上面是FP32可表达的数据范围,经过缩放和偏移以及四舍五入,将数据划分到[-128, 127]之间,这类似于直方图统计。 将浮点型数据转换为整型数据有多种形式,根据整型数据的0点是否为中间位置,可将量化分为对称量化(Symmetric Quantization)和非对称量化(Asymmetric Quantization)。参考MIT TinyML 第六节的两页PPT来学习对称量化和非对称量化。(PS:此处讲的均是线性量化) 浮点型数据与整型数据转换公式如下: r = S(q - Z) q = r / S + Z r:浮点型 q:整型 S:缩放因子scale Z:零点数值Zero 对称量化 对称量化是零点在0处,为此需要取绝对值的最大值作为上限、下限,这样就可得到零点在0处。 然后根据公式计算缩放值scale,S = 2.12 / 1 = 2.12,更一般的可以写为 S = 2.12 - (-2.12) / 1 - (-1) = (22.12) / (21) = 2.12 / 1 = 2.12 由于Z=0,不需要计算Z值 有了S和Z就可以对浮点型数据和整型数据进行互相转换.如下图所示,图片来源: https://efficentml.ai 非对称量化 非对称量化的上下限就根据真实分布选择,这样通常Z就不是0。 根据公式计算Scale,S = 2.12 - (-1.08) / 1 - (-2) = 1.07; 根据公式计算Zero, Z = round(-2 - -1.08/1.07) = -1; 尝试量化 r=0,观察q对应什么值: q = r / S + Z = 0/1.07 + -1 = -1 可以发现,非对称量化时,浮点数的0值在量化后变为了Z值。 动态范围计算方法 在计算scale时,如何选择数据的上限、下限将会影响最终量化效果。 例如上文的对称量化是有数据损失的,是不饱和量化。因为直接取绝对值的最大值作为上限,取负作为下限,通常会存在一个数据区间是没有浮点数据的,但会占据量化后的一部分数据表示,这些数据表示则会被浪费掉,导致量化后是不饱和的。 Min-Max 除了上文中的Min-Max方法、通常还采用Histgram和Entropy进行动态范围的选择。 这里推荐B站博主手写AI的视频教程这里对其视频做笔记分享。 Histogram 为了选择合适的上限、下限,可以通过直方图进行数据统计,然后根据数据分布情况,选择恰当的上下限,以此解决离群点带来的不饱和问题。 通常Histogram需要设置一个超参数,用来确定上下限收缩的范围,称为limit。 通常limit=0.9999, 0.99999之类。表示从上限、下限进行收缩,收缩之后的数据频次占总数据量的99%。 可采用双指针实现上述过程: 首先定左指针、右指针分别指向左边界和右边界 计算当前双指针之间的直方图覆盖率,如果小于等于设定的覆盖率阈值,则返回此刻的左指针指向的直方图值,如果不满足,则需要调整双指针的值,向中间靠拢 如果当前左指针所指向的直方图值大于右指针所指向的直方图值,则右指针左移,否则左指针右移 循环,直到双指针覆盖的区域满足要求 得到上限、下限后,再根据量化公式计算S和Z。 Entropy(K-L散度) Entropy方法是一种基于概率分布的动态范围计算方法,通过对原始数据进行直方图统计,得到真实数据分布p,然后通过KL散度(相对熵,用于描述两个概率分布之间的相似性)来衡量分布q与p之间的相似性,最终找到一个与p很相似的q分布,q的分布上限、下限就可以作为动态范围。 下图是计算q分布的伪代码,具体过程包含较多细节,推荐大家观察博主的视频即可,在此了解采用的Entropy是通过概率分布的方式寻找动态范围,此方法在TensorRT中也有实现和运用。 weight、bias和activation的量化 上文对量化数值的变化及选择进行了介绍,但对于模型具体运算过程是如何对FP32数据进行量化为int8,最终由反量化回FP32进行输出,还需要进一步分析。 在模型中,需要量化的内容主要有三个,分别是weight、bias和activation。 weight和bias好理解,当模型训练好之后,就可以选定动态范围,然后计算scale和Z值,就可得到量化方法及量化后的weight和bias。 而activation也需要量化,这个一开始不好理解,这个要从运算溢出讲起。 例如两个int8计算时,如100 + 100 = 200,这是采用int8(可表示[-128, 127])就无法表示200,因此对于运算后的值(激活值)通常需要更高精度来表示,如int32。 但对于激活值输出又需要采用int8来表示,这时候就需要将int32量化为int8的过程,这就是激活值(activation)量化。 可通过下图观察一个网络层在量化后是如何计算的,同时可知道一个网络层需要在哪几个地方进行量化(计算scale和Z值)。 图中对于weights、bias和网络层输出都需要经过quantize,即量化。量化的scale和Z值则是在部署前进行确定,这些值的确定也是模型量化核心的内容。 了解了需要量化的对象后,下面来学习校准(Calibration)的概念。 对于weight和bias,当模型训练结束后,数值分布就已确定,因此无需额外操作获取数据分布,即可进行scale和Z值的计算。 对于activation就不行,它依赖于具体数据的输入。因此,对于activation的量化,往往需要采用真实数据的输入(前向传播),然后统计激活值的数据分布,用于量化。 对于activation,采用真实数据获取数据分布再量化计算scale和Z值,这个过程叫做校准(Calibration)。 PTQ与QAT 量化也好,校准也好,都是将高精度数据进行统计,然后计算scale和Z值,在部署的推理过程中进行低比特量化。 根据计算scale和Z值的阶段不同,模型量化可分为PTQ和QAT两大类,两者各有优缺点。 PTQ(Post-training quantization,训练后量化),是不需要训练数据(输入数据和标签对数据),通常采用小部分数据(不需要标签)进行模型激活值校准,统计激活值的分布,然后选择量化scale和Z值,对于weight和bias直接根据模型数据进行量化。 QAT(Quantization-aware training,量化感知训练) ,是需要训练数据,并需要在训练阶段插入伪量化层,训练得到带QDQ节点的数据,然后在量化时采用QDQ节点进行量化,可以得到比PTQ更高的精度。 QDQ(quantize节点、dequantize节点)节点是QAT的灵魂,是一个量化节点和反量化节点,可在训练时进行迭代优化,思想是在训练的时候获得一个好的Q节点,它的scale和Z值是比较好的scale和Z值,为什么说它得到的值比较好呢?因为这些值可通过DQ节点恢复出更接近真实值的数据,因此认为训练阶段获得的Q节点中的scale和Z值是较好的选择。 根据TensorRT官方介绍pdf,QDQ(也称FQ, fake quantize,伪量化节点)的插入如下图所示,最终部署时可直接采用Q节点中的scale和Z值。 PTQ和QAT的对比,可参考B站博主ZOMI酱的视频 通过综述《A Survey of Quantization Methods for Efficient》图4,也可以很好的理解PTQ和QAT之间的差别。 那么PTQ和QAT如何选择? 通常根据精度损失的多少来选,优先选择方便的PTQ进行验证,通常PTQ掉2个点以内,如果PTQ掉点过大,可采用QAT。 下图是Nvidia对模型量化前后精度损失的统计,绝大多数模型采用PTQ掉点0-2个点,甚至还有精度提高的模型。 特别需要注意的是efficientnet及其他轻量化设计的模型结构对于PTQ是不适用的,需要采用QAT进行量化。 小结 本节详细介绍了量化的基础知识和概念,对于初入门量化概念的朋友来说,信息量会有一些大,这里做一下要点总结。 量化概念:高精度用低精度数据表示,可减少存储,提高推理速度 线性量化:分为对称和非对称量化,公式是r = S(q - Z), q = r / S + Z , r是高精度数据,q是量化后数据 S和Z的求取:可通过Min-Max、Histogram、Entropy三种方法确定数据上限和下限,然后根据通用公式求取 量化对象:通常量化对象有weight、bias、activation,前两者不需要数据推理,后者需要真实数据推理才可进行统计,并计算S和Z值 量化方法:根据是否需要训练,量化分为PTQ和QAT PTQ:不需要训练数据,需要小批量真实数据(校准数据)进行推理,对activation进行量化。 QAT:需要训练数据,在训练阶段插入QDQ节点,在部署推理时,Q节点可实现高精度量化。 下一小节将介绍在TensorRT中实现PTQ。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.7-ptq.html":{"url":"chapter-12/12.7-ptq.html","title":"12.7 PTQ 量化实践","keywords":"","body":"12.7 PTQ 量化实践 前言 上一节介绍了模型量化的概念,本节开始进行PTQ的实践,并对比不同动态范围计算方法的差别(max, entropy, mse, pertentile)。 由于量化是对已经训练好的模型进行量化并评估在测试集上的精度,因此量化(PTQ和QAT)需要依托一个完成的模型训练项目开展,这里采用第八章第一节的分类任务。 PTQ代码实践将从以下几个部分进行:pytorch-quantization库介绍、推理统计、校准、量化、评估模型准确率和TensorRT推理效率。 pytorch_quantization 工具库 安装: pip install pytorch-quantization --extra-index-url https://pypi.ngc.nvidia.com 或者(pytorch 1.12版本时推荐用 pytorch-quantization==2.1.2 ) pip install pytorch-quantization==2.1.2 pytorch_quantization是由NVIDIA官方提供的PyTorch量化库,用于将PyTorch模型量化为低比特形式(如Int8)。 pytorch_quantization 库使用非常方便,其PTQ步骤分四步: 第一步:构建具备量化模块的nn.Module,在模型定义之前替换pytorch官方nn.Module的网络层。如nn.conv2替换为quant_nn.QuantConv2d。 Quantxxx这样的网络层中会在原功能基础上,添加一些组件,这些组件将在校准时记录数据、并存储scale和Z值。 第二步:正常构建模型,并执行前向推理。 第三步:执行动态范围计算,并计算scale和Z值。 第四步:导出ONNX模型 通过以上流程可知道,仅第一步中pytorch_quantization 库中自定义的量化功能模块需要深入了解,其余都是常规流程。 因此,下面简单介绍pytorch_quantization中的核心组件。 量化函数——Quantization function 提供两个量化功能函数 tensor_quant 和 fake_tensor_quant。 tensor_quant(inputs, amax, num_bits=8, output_dtype=torch.float, unsigned=False) 功能:对输入张量进行量化,输出量化后的张量 fake_tensor_quant(inputs, amax, num_bits=8, output_dtype=torch.float, unsigned=False) 输入张量进行伪量化,先量化,后反量化,伪量化输出的张量与原张量存在量化误差,可用于理解量化误差的产生。 from pytorch_quantization import tensor_quant # Generate random input. With fixed seed 12345, x should be # tensor([0.9817, 0.8796, 0.9921, 0.4611, 0.0832, 0.1784, 0.3674, 0.5676, 0.3376, 0.2119]) torch.manual_seed(12345) x = torch.rand(10) # fake quantize tensor x. fake_quant_x will be # tensor([0.9843, 0.8828, 0.9921, 0.4609, 0.0859, 0.1797, 0.3672, 0.5703, 0.3359, 0.2109]) fake_quant_x = tensor_quant.fake_tensor_quant(x, x.abs().max()) # quantize tensor x. quant_x will be # tensor([126., 113., 127., 59., 11., 23., 47., 73., 43., 27.]) # with scale=128.0057 quant_x, scale = tensor_quant.tensor_quant(x, x.abs().max()) 量化描述符和量化器——Descriptor and quantizer QuantDescriptor 用于描述该采用何种量化方式,例如 QUANT_DESC_8BIT_PER_TENSOR、 QUANT_DESC_8BIT_CONV2D_WEIGHT_PER_CHANNEL。 这里的pre tensor , per channel是一个新知识点,对于activation通常用per tensor, 对于卷积核通常用per channel。细节不展开,感兴趣可以阅读《A White Paper on Neural Network Quantization》的2.4.2 Per-tensor and per-channel quantization 描述符是用于初始化量化器quantizer的,量化器可接收张量,输出量化后的张量。完成量化后,在量化器内部会记录相应的scale和Z值。 from pytorch_quantization.tensor_quant import QuantDescriptor from pytorch_quantization.nn.modules.tensor_quantizer import TensorQuantizer quant_desc = QuantDescriptor(num_bits=4, fake_quant=False, axis=(0), unsigned=True) quantizer = TensorQuantizer(quant_desc) torch.manual_seed(12345) x = torch.rand(3, 4, 2) quant_x = quantizer(x) print(x) print(quant_x) print(quantizer.scale) # 可以得到3个scale,可知道是按照第一个维度切分张量。因为描述符设置了 axis=(0) 量化模块——Quantized module 前面提到PTQ量化第一步是将pq库的Quantxxx模块替换掉pytorch的nn.Module,例如nn.conv2d变为quant_nn.Conv2d。 在实际量化中主要有Linear和Conv两大类(nn.Module可以回顾chapter-4),下面对比pytorch和pq库的网络层创建区别。 from torch import nn from pytorch_quantization import tensor_quant import pytorch_quantization.nn as quant_nn # pytorch's module fc1 = nn.Linear(in_features, out_features, bias=True) conv1 = nn.Conv2d(in_channels, out_channels, kernel_size) # quantized version quant_fc1 = quant_nn.Linear( in_features, out_features, bias=True, quant_desc_input=tensor_quant.QUANT_DESC_8BIT_PER_TENSOR, quant_desc_weight=tensor_quant.QUANT_DESC_8BIT_LINEAR_WEIGHT_PER_ROW) quant_conv1 = quant_nn.Conv2d( in_channels, out_channels, kernel_size, quant_desc_input=tensor_quant.QUANT_DESC_8BIT_PER_TENSOR, quant_desc_weight=tensor_quant.QUANT_DESC_8BIT_CONV2D_WEIGHT_PER_CHANNEL) 可以发现,只是多了量化描述符的配置,根据量化对象(上一节提到,主要是weight, bias, activation),通常需要设定不同的量化方式。 例如,对于activation用per tensor, 对于linear的权重用per row,对于conv的权重用pre channel。 ResNet50 PTQ 模型权重传入百度云盘,供下载 下面基于chapter-8/01_classification中的项目进行量化,首先训练一个resnet50,得到的accuracy是94.39。模型权重下载-提取码:jhkm # 也可以重新训练 resnet50 nohup python train_main.py --data-path ../data/chest_xray --batch-size 64 --workers 8 --lr 0.01 --lr-step-size 20 --epochs 50 --model resnet50 > ./log.log 2>&1 & 接下来对它进行量化,并对比accuracy掉点情况以及推理加速情况。 配套代码位于这里 流程分析 PTQ整体流程在前言以及介绍,这里从代码角度介绍核心流程 # 第一步,初始化pq,将pytorch的模块替换掉 quant_modules.initialize() # 替换torch.nn的常用层,变为可量化的层 # 第二步,创建模型、加载训练权重 model = get_model(args, logger, device) # 第三步,前向推理,统计activation激活值分布 collect_stats(model, train_loader, num_batches=args.num_data) # 设置量化模块开关,并推理,同时统计激活值 # 第四步,根据动态范围方法计算scale、Z值,获得量化后的模型 compute_amax(model, method=args.ptq_method) # 计算上限、下限,并计算scale 、Z值 # 第五步,采用量化后的模型进行accuracy评估 loss_m_valid, acc_m_valid, mat_valid = \\ utils.ModelTrainer.evaluate(valid_loader, model, criterion, device, classes) # 第六步,保存成ONNX模型和pth模型 第一步中,可以进入pytorch_quantization\\quant_modules.py查看当前支持的量化层,量化层中会插入量化器,量化器中存储量化用的数据——scale和Z # Global member of the file that contains the mapping of quantized modules _DEFAULT_QUANT_MAP = [_quant_entry(torch.nn, \"Conv1d\", quant_nn.QuantConv1d), _quant_entry(torch.nn, \"Conv2d\", quant_nn.QuantConv2d), _quant_entry(torch.nn, \"Conv3d\", quant_nn.QuantConv3d), _quant_entry(torch.nn, \"ConvTranspose1d\", quant_nn.QuantConvTranspose1d), _quant_entry(torch.nn, \"ConvTranspose2d\", quant_nn.QuantConvTranspose2d), _quant_entry(torch.nn, \"ConvTranspose3d\", quant_nn.QuantConvTranspose3d), _quant_entry(torch.nn, \"Linear\", quant_nn.QuantLinear), _quant_entry(torch.nn, \"LSTM\", quant_nn.QuantLSTM), _quant_entry(torch.nn, \"LSTMCell\", quant_nn.QuantLSTMCell), _quant_entry(torch.nn, \"AvgPool1d\", quant_nn.QuantAvgPool1d), _quant_entry(torch.nn, \"AvgPool2d\", quant_nn.QuantAvgPool2d), _quant_entry(torch.nn, \"AvgPool3d\", quant_nn.QuantAvgPool3d), _quant_entry(torch.nn, \"AdaptiveAvgPool1d\", quant_nn.QuantAdaptiveAvgPool1d), _quant_entry(torch.nn, \"AdaptiveAvgPool2d\", quant_nn.QuantAdaptiveAvgPool2d), _quant_entry(torch.nn, \"AdaptiveAvgPool3d\", quant_nn.QuantAdaptiveAvgPool3d),] 第二步,加载模型。 第三步,进行前向推理,但在实际推理时需要对两个开关分别进行控制。 量化开关:若打开,会进行量化,这个在校准的时候是不需要的。 校准开关:若打开,会在模型前向传播时,统计数据,这个在校准时是需要的。 因此在推理前有这样的代码段(同理,推理后需要把两个开关反过来): # Enable calibrators for name, module in model.named_modules(): if isinstance(module, quant_nn.TensorQuantizer): if module._calibrator is not None: module.disable_quant() module.enable_calib() else: module.disable() 第四步中,调用module.load_calib_amax(**kwargs),实现scale和Z值的计算。scale和Z值会存在量化器中。 例如resnet50中的conv1层是一个QuantConv2d,其中包含了input_quantizer和weight_quantizer两个量化器,量化器中存储了amax和step_size(scale值) 第五步中,量化后resnet50模型推理时,量化层会对输入数据、权重进行量化,然后再进行运算。 这里debug进入到layer1的第一个卷积层,观察Quantconv2d的forward(pytorch_quantization\\nn\\modules\\quant_conv.py) 可以看到,进行运算前,对输入数据和权重进行量化,量化用到的就是上述提到的量化器,量化器中有scale和Z值。 def forward(self, input): quant_input, quant_weight = self._quant(input) output = F.conv2d(quant_input, quant_weight, self.bias, self.stride, self.padding, self.dilation, self.groups) return output def _quant(self, input): quant_input = self._input_quantizer(input) quant_weight = self._weight_quantizer(self.weight) return (quant_input, quant_weight) 具体量化过程,可debug追溯得到如下过程: quant_weight = self._weight_quantizer(self.weight) 1 ---> 进入量化器中的forward。pytorch_quantization\\nn\\modules\\tensor_quantizer.py 1 ---> 量化器中可实现校准功能、截断功能、量化功能,是根据3个属性开关来判断,这里也发现了校准数据收集的代码。 def forward(self, inputs): if self._disabled: return inputs outputs = inputs if self._if_calib: if self._calibrator is None: raise RuntimeError(\"Calibrator was not created.\") # Shape is only know when it sees the first tensor self._calibrator.collect(inputs) if self._if_clip: if not self._learn_amax: raise RuntimeError(\"Clip without learning amax is not implemented.\") outputs = self.clip(inputs) if self._if_quant: outputs = self._quant_forward(inputs) return outputs 2 ---> 进入量化器中的_quant_forward(), 2 ---> _quant_forward中可以实现伪量化、量化两种操作,这里是需要进行量化的,因此会执行 2 ---> outputs, self._scale = tensor_quant(inputs, amax, self._num_bits, self._unsigned) 2 ---> tensor_quant就是开篇介绍pytorch_quantization库的第一个功能,对张量进行量化的函数。 def _quant_forward(self, inputs): \"\"\"Quantized forward pass.\"\"\" if self._learn_amax: inputs = self.clip(inputs) amax = torch.max(-self.clip.clip_value_min, self.clip.clip_value_max).detach() else: amax = self._get_amax(inputs) if self._fake_quant: if not TensorQuantizer.use_fb_fake_quant: outputs = fake_tensor_quant(inputs, amax, self._num_bits, self._unsigned, self._narrow_range) else: if inputs.dtype == torch.half or amax.dtype == torch.half: raise Exception(\"Exporting to ONNX in fp16 is not supported. Please export in fp32, i.e. disable AMP.\") outputs = self._fb_fake_quant(inputs, amax) else: outputs, self._scale = tensor_quant(inputs, amax, self._num_bits, self._unsigned) return outputs 完成量化模型评估后,可进行模型保存为ONNX格式,再用netron查看。 可以看到量化的层会插入QDQ节点,这些节点在TensorRT中将会被使用 PTQ 量化实现 执行命令后,可在chapter-8\\01_classification\\Result\\2023-09-26_01-47-40 文件夹下获得对应的onnx输出,并可观察不同方法得到的模型精度 python resnet_ptq.py --mode quantize --num-data 512 动态范围计算方法对比 对于resnet_ptq.py,这里简单介绍使用方法 根据args.ptq_method的有无决定单个动态范围方法量化, 还是四种量化方法均量化。 根据args.mode == 'quantize' 还是 evaluate,决定代码进行量化,还是进行单纯的预训练模型accuracy评估。(用于对比) 其他参数参考get_args_parser() # 进行四种方法对比。跑512个batch python resnet_ptq.py --mode quantize --num-data 512 # 单个方法量化 python resnet_ptq.py --mode quantize --ptq-method max --num-data 512 python resnet_ptq.py --mode quantize --ptq-method entropy --num-data 512 python resnet_ptq.py --mode quantize --ptq-method mse --num-data 512 python resnet_ptq.py --mode quantize --ptq-method percentile --num-data 512 通过五次实验,发现PTQ后的Acc存在较大方差,具体数据分布如下箱线图所示: {'entropy': [85.74, 89.26, 91.67, 92.79, 93.27], 'max': [83.17, 90.54, 92.15, 92.79, 94.07], 'mse': [89.42, 91.67, 92.63, 92.79, 93.59], 'percentile': [85.58, 87.34, 91.67, 92.63, 92.79]} 效果最好的是max,除了一次83%的acc之外,其余效果比较好,并且最高达到94.07%。 建议:方法都试试,实在不稳定,考虑QAT。 PTQ 效率对比 为了观察PTQ带来的效率提升,下面进行未量化、int8量化,分别在bs=1,bs=32时的对比。 首先获得fp32的onnx: python resnet_ptq.py --mode onnxexport 此时在Result\\2023-09-26_01-47-40下应当拥有所有onnx模型,可以采用trtexec进行效率测试 trtexec --onnx=resnet_50_fp32_bs1.onnx trtexec --onnx=resnet_50_fp32_bs32.onnx trtexec --onnx=resnet_50_ptq_bs1_data-num512_percentile_91.03%.onnx --int8 trtexec --onnx=resnet_50_ptq_bs32_data-num512_percentile_91.03%.onnx --int8 --saveEngine=resnet_ptq_int8.engine 时延(中位数) ms fp32 int8 吞吐量 fps fp32 int8 bs=1 1.84 1.01(↓46%) 524 860(↑64%) bs=32 26.79 15.4(↓43%) 37.1*32=1187 64.5*32=2064(↑73.9%) 通过实验,可知道,时延可下降40%, 吞吐量提高70%左右。 小结 本节首先介绍pytorch-quantization库中三个核心概念,然后梳理PTQ量化步骤,接着实现了resnet50的PTQ量化实验,最后对量化后的模型在trtexec上进行效率对比。 通过本节实践可知: int8 可有效提高推理速度和吞吐量,具体提高比例需结合具体模型、GPU型号而定。本案例时延降低40%左右,吞吐量提高70%左右 PTQ量化掉点不稳定,需多次试验,或进行逐层分析,或逐层设定动态范围方法 采用pytorch-quantization量化,大体分6步:初始化quanti_modules,加载模型,推理统计,量化获得s和z,精度评估,模型导出 下一小节,介绍QAT的实现。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.8-qat.html":{"url":"chapter-12/12.8-qat.html","title":"12.8 QAT 量化实践","keywords":"","body":"12.8 QAT 量化实践 前言 上一小节介绍了PTQ量化,本节介绍另外一种重要量化方法——QAT(quantization aware training,感知量化训练)。 本节将从QDQ节点出发,深入了解QAT的量化过程,然后基于QAT进行ResNet50的量化及性能、效率评估。 QAT基础概念 再回顾上上小结QAT与PTQ的区别。 PTQ(Post-training quantization,训练后量化),是不需要训练数据(带标签的数据),通常采用小部分数据(不需要标签)进行模型激活值校准,统计激活值的分布,然后计算动态范围,再计算量化scale和Z值;对于weight和bias直接根据模型权重数据进行量化。 QAT(Quantization-aware training,量化感知训练) ,是需要训练数据,并需要在训练阶段插入伪量化层,训练得到带QDQ节点的模型,然后在量化时采用QDQ节点进行量化,通常可以得到比PTQ更高的精度。 QDQ(quantize节点、dequantize节点)节点是QAT的灵魂,是一个量化节点和反量化节点,可在训练时进行迭代优化,思想是在训练的时候获得一个好的Q节点,它的scale和Z值是比较好的scale和Z值,为什么说它得到的值比较好呢?因为这些值可通过DQ节点恢复出更接近真实值的数据,因此认为训练阶段获得的Q节点中的scale和Z值是较好的选择。 伪量化节点 刚接触量化时,伪量化是较难理解的,伪量化的思想是在训练时模拟量化带来的误差,通过反向传播来对抗误差带来的精度下降,从而得到一个较好的scale和Z值。 具体可参考Nvidia的PPT《toward-int8-inference-deploying-quantization-aware-trained-networks-using-tensorrt》 下图是一个常规fp32的运算过程,接下来将逐步讲解QAT过程。 第一步,插入QDQ(fake quant ops)节点。 插入QDQ的地方即需要量化的地方,回顾上上小结,需要量化的通常是weight、bias和activation,因此有以下4个节点需要量化: X:可看成第一个activation,因此是需要量化的 Wx:conv1的权重 Relu:之所以在Relu而不是conv1之后,是因为Relu是线性的,可以合并它两,在Relu后做FQ即可。 Wy:conv2的权重 第二步,训练导出ONNX,最终得到scale和Z值。 第三步,在TensorRT中对计算图进行优化 常量的折叠:如权重的Q节点可与权重合并,无需在真实推理中由fp32的权重经过scale和Z变为int8的权重 op融合:将DQ信息融合到算子(如图中conv)中,通过op融合,模型计算将变为真实的int8输入、int8输出 于TensorRT是如何做op融合的,暂时找不到相关资料,暂且略过。 思想大概能理解,QConvRelu是吸收了DQ节点的信息,改变weight的数值,这样能更好的计算int8与int8的卷积。 通过以上案例可知conv2输出的是fp32,因为它没有插入QDQ,而conv1输出的是int8,因为插入了QDQ。 这点可以让我们在量化时根据需要,自定义插入QDQ,使得需要高精度输出的地方保持fp32。大意如下图所示 总结一下,QAT的过程: 插入QDQ 训练,得到scale和Z值,导出onnx模型 TensorRT图优化、算子融合,导出engine,算子融合中将DQ信息注入op中,使得op接收int8,输出int8,从而提升了运算速度。 ResNet50 QAT 实践 接下来进行QAT实践,初识量化时,一直认为QAT比PTQ繁琐,因为QAT需要训练,而PTQ直接校准就好了。但是,在代码实现上,QAT反而比PTQ简单许多。 流程分析 首先,分析QAT的实现流程步骤: 第一步,初始化pytorch_quantization,将pytorch的模块替换掉,这一步pq库已经封装好了,只需要一行代码quant_modules.initialize() 第二步,创建模型、加载训练权重 第三步,常规训练步骤 第四步,保存成ONNX模型和pth模型 由此可知,只是在常规训练流程的代码中,插入quant_modules.initialize() 即可。 当然,在QAT训练和常规训练不一样,学习率通常要小100倍,同时可以用余弦下降法,逐步下降学习率。 TensorRT文档中给了一些QAT的建议: Usually, it doesn’t need to fine tune very long. We usually use around **10% of the original training schedule**, starting at **1% of the initial training learning rate**, and a cosine annealing learning rate schedule that follows the decreasing half of a cosine period, down to 1% of the initial fine tuning learning rate (0.01% of the initial training learning rate). 代码说明 同样的,代码位于chapter-8/01_classification中,代码整体与训练代码train_main.py保持一致,其中删减了一些不必要的日志记录代码段。 同时修改了学习率调整方法,以及增加ONNX模型导出。 这里采用4epoch,lr=0.001*0.01进行训练,代码直接运行即可:python resnet50_qat.py 日志如下: Epoch: [000/005] Train Loss avg: 0.1086 Valid Loss avg: 0.1770 Train Acc@1 avg: 96.1009 Valid Acc@1 avg: 93.4295 Epoch: [001/005] Train Loss avg: 0.0786 Valid Loss avg: 0.1819 Train Acc@1 avg: 97.2477 Valid Acc@1 avg: 93.4295 Epoch: [002/005] Train Loss avg: 0.0773 Valid Loss avg: 0.1742 Train Acc@1 avg: 97.3624 Valid Acc@1 avg: 92.9487 Epoch: [003/005] Train Loss avg: 0.0735 Valid Loss avg: 0.1771 Train Acc@1 avg: 97.0183 Valid Acc@1 avg: 93.5897 Epoch: [004/005] Train Loss avg: 0.0633 Valid Loss avg: 0.1704 Train Acc@1 avg: 97.9740 Valid Acc@1 avg: 93.4295 可以看到,Accuracy基本稳定在93.5上下,最优为93.58,相较于原始的94.3,掉了不到1个百分点,比PTQ稳定以及性能更优。 效率对比 得到onnx模型,下面比较fp32、PTQ量化和QAT量化,三者的时延和吞吐量。 可以看到PTQ和QAT在效率上是一样的。 时延(中位数) ms: fp32 PTQ int8 QAT int8 bs=1 1.84 1.01(↓46%) 1.07(42%) bs=32 26.79 15.4(↓43%) 15.4(↓43%) 吞吐量(FPS) fp32 PTQ int8 QAT int8 524 860(↑64%) 817 37.1*32=1187 64.5*32=2064(↑73.9%) 64.4*32 = 2060(↑73.8%) FP32\\ptq\\qat 三种模型的对比指令如下: trtexec --onnx=resnet_50_fp32_bs1.onnx trtexec --onnx=resnet_50_fp32_bs32.onnx trtexec --onnx=resnet_50_ptq_bs1_data-num512_percentile_91.03%.onnx --int8 trtexec --onnx=resnet_50_ptq_bs32_data-num512_percentile_91.03%.onnx --int8 --saveEngine=resnet_ptq_int8.engine trtexec --onnx=resnet_50_qat_bs1_93.43%.onnx --int8 trtexec --onnx=resnet_50_qat_bs32_93.43%.onnx --int8 注1:在使用trtexec时,看到了这样的日志,随后尝试了加上 --fp16,整体吞吐量又提升了~40%(相较于fp32提高140%),这或许是将fp32变为fp16带来的效率提升? [09/29/2023-16:20:37] [I] FP32 and INT8 precisions have been specified - more performance might be enabled by additionally specifying --fp16 or --best trtexec --onnx=resnet_50_qat_bs32_93.43%.onnx --int8 --best trtexec --onnx=resnet_50_qat_bs32_93.43%.onnx --int8 --fp16 注2:在新电脑里又看到了这样的warning,尝试使用--useCudaGraph ,可以提高吞吐。尝试之后,发现吞吐从1200 提升到了1600,看来trtexec里的提示信息非常有帮助! [04/04/2024-20:55:04] [W] Throughput may be bound by Enqueue Time rather than GPU Compute and the GPU may be under-utilized. [04/04/2024-20:55:04] [W] If not already in use, --useCudaGraph (utilize CUDA graphs where possible) may increase the throughput.* 小结 本节回顾了QAT和PTQ的区别,同时介绍了QDQ(伪量化算子:量化和反量化)在训练、推理时的变化,最后通过代码实现QAT,并对比了性能和效率变化。 到这里,量化的基础就结束了,模型量化的内容远不止这些,模型量化还有更高级的内容,例如逐层精度损失分析、逐层插入/取消量化节点、自定义量化算法。 想要深入研究模型加速/量化的朋友,可以进一步学习其它资料,这里推荐一些学习量化用到的资料: TRT int8量化文档:https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#working-with-int8 TRT范例代码:https://github.com/NVIDIA/TensorRT/blob/main/quickstart/quantization_tutorial/qat-ptq-workflow.ipynb pytorch-quantization:https://github.com/NVIDIA/TensorRT/tree/main/tools/pytorch-quantization nvidia int8 QAT量化介绍:https://developer.nvidia.com/blog/achieving-fp32-accuracy-for-int8-inference-using-quantization-aware-training-with-tensorrt/ yolov6 量化工程范例:https://tech.meituan.com/2022/09/22/yolov6-quantization-in-meituan.html 老潘的TRTv8量化入门博客:https://zhuanlan.zhihu.com/p/479101029 B站量化系列教程-ZOMI酱:https://www.bilibili.com/video/BV1VD4y1n7AR/?spm_id_from=333.788&vd_source=19b783e279d4d4ceff5b927f27ea6aa3 B站量化系列教程-手写AI:https://www.bilibili.com/video/BV18L41197Uz/?spm_id_from=333.337.search-card.all.click&vd_source=19b783e279d4d4ceff5b927f27ea6aa3 MIT的TinyML-第六节:MIT-TinyML-Lec06-Quantization-II 下一小节,将利用本章学习的TensorRT知识,进行YOLOv8的量化实践,学习优秀开源代码的工程化过程。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.9-trt-deploy.html":{"url":"chapter-12/12.9-trt-deploy.html","title":"12.9 TensorRT Python 工程化","keywords":"","body":"12.9 TRT python 工程化 前言 在生产应用中,需要将trt的engine封装为类,内部实现engine初始化、推理的内存申请和释放,host与device的数据迁移,对外提供推理接口。 这样,算法推理功能才能优雅地嵌入到整个生产流程的代码中,供上下游调用。 本节将参考多个代码案例,总结了基于context.execute_v3的trt模型类编写。 两种推理方式——context 在前面介绍trt的python代码推理时,执行推理采用的是execute_async_v3,在不少案例教程中使用的是execute_v2,这将导致代码无法复用。 为了了解execute_v2和execute_async_v3的区别,下面将会介绍: 两者差异 基于yolov5代码,以及Nvidia官方代码,分析execute_v2的机制 两者差异 def execute_v2(self, bindings, p_int=None): # real signature unknown; restored from __doc__ \"\"\" execute_v2(self: tensorrt.tensorrt.IExecutionContext, bindings: List[int]) -> bool Synchronously execute inference on a batch. This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine.get_binding_index()` . This method only works for execution contexts built from networks with no implicit batch dimension. :arg bindings: A list of integers representing input and output buffer addresses for the network. :returns: True if execution succeeded. \"\"\" def execute_async_v3(self, stream_handle): # real signature unknown; restored from __doc__ \"\"\" execute_async_v3(self: tensorrt.tensorrt.IExecutionContext, stream_handle: int) -> bool Asynchronously execute inference. Modifying or releasing memory that has been registered for the tensors before stream synchronization or the event passed to :func:`set_input_consumed_event` has been triggered results in undefined behavior. Input tensors can be released after the :func:`set_input_consumed_event` whereas output tensors require stream synchronization. :arg stream_handle: The cuda stream on which the inference kernels will be enqueued. \"\"\" 根据函数注释,可知execute_v2接收的是device地址,即要求数据已经变到gpu上,执行execute_v2时,仅仅实现gpu的显存数据读取与运算。 根据函数注释,可知execute_async_v3接收的是cuda流的编号,即默认通过该cuda流进行运算(具体含义并不熟,需要深入了解cuda编程,这里猜想是默认用的0),这就要求context知道从哪里取输入数据进行推理,会有额外的context.set_tensor_address(l_tensor_name[0], d_input) , d_input是显存地址。 YOLOv5 TRT推理 关于execute_async_v3,可回顾本章第二节的工作流程梳理。 为了解基于execute_v2进行推理时,需要进行的操作流程,这里分析YOLOv5官方代码。 yolov5支持多种backend的推理,方式在链接中也给出了详细步骤,这里不再重复。 # 1. 导出trt模型 python export.py --weights best.pt --data data/mydrone.yaml --include engine --device 0 # 2. 推理 python detect.py --weights best.engine --data data/mydrone.yaml --source G:\\虎门大桥车流\\DJI_0049.MP4 核心代码在detect.py和common.py中,其中实现了基于TRT的engine推理代码,这里主要关注模型初始化和模型推理两部分。 模型初始化核心代码: yolov5-master/models/common.py 中的DetectMultiBackend的elif engine部分: 通过bindings字典管理输入输出数据,包括数据的'name', 'dtype', 'shape', 'data', 'ptr'。 数据的地址ptr指在device(GPU)上的地址!这里基于torch库对数据做操作,并实现数据搬迁到GPU。这点与我们手动写推理不一样! execute_v2需要输入的地址,恰恰是GPU地址,因此可借助torch的to(device)方法得到GPU上数据的地址。 Binding = namedtuple('Binding', ('name', 'dtype', 'shape', 'data', 'ptr')) logger = trt.Logger(trt.Logger.INFO) with open(w, 'rb') as f, trt.Runtime(logger) as runtime: model = runtime.deserialize_cuda_engine(f.read()) context = model.create_execution_context() bindings = OrderedDict() output_names = [] for i in range(model.num_bindings): name = model.get_binding_name(i) dtype = trt.nptype(model.get_binding_dtype(i)) if model.binding_is_input(i): pass # 略 else: output_names.append(name) shape = tuple(context.get_binding_shape(i)) im = torch.from_numpy(np.empty(shape, dtype=dtype)).to(device) bindings[name] = Binding(name, dtype, shape, im, int(im.data_ptr())) binding_addrs = OrderedDict((n, d.ptr) for n, d in bindings.items()) batch_size = bindings['images'].shape[0] # if dynamic, this is instead max batch size 模型推理核心代码 yolov5-master/models/common.py 中的DetectMultiBackend的forward函数的elif self.engine部分: self.binding_addrs['images'] = int(im.data_ptr()) # 设置输入数据的GPU地址,im已经是在GPU上的tensor了 self.context.execute_v2(list(self.binding_addrs.values())) # 传入数据的GPU地址,执行推理 y = [self.bindings[x].data for x in sorted(self.output_names)] # 完成推理后,到输出张量(已在GPU上)取推理结果 小结:由此可知道yolov5的trt推理,借助了torch库将cpu上的数据与gpu上的数据进行关联 这种方式仍旧依赖pytorch,为此需要进一步探究手动管理gpu显存时,如何基于execute_v2进行推理,难点在于如何获得gpu上显存的地址。 Nvidia官方案例代码 代码位于:https://github.com/NVIDIA/TensorRT/blob/78245b0ac2af9a208ed02e5257bfd3ee7ae8a88d/samples/python/detectron2/infer.py 这里仅观察输入给execute_v2的数据如何得来,为此,代码倒着来看。 1. 在infer函数中: self.context.execute_v2(self.allocations) 2. 观察init中 self.allocations的定义 shape = self.engine.get_binding_shape(i) size = np.dtype(trt.nptype(dtype)).itemsize for s in shape: size *= s allocation = common.cuda_call(cudart.cudaMalloc(size)) self.allocations.append(allocation) 由此可知,传入execute_v2的地址是通过cudart.cudaMalloc(size)获取的,这个在第二节中也采用了这个方式获取GPU上数据的地址。 size则是通过shape和单个数据大小乘积得到。 通过两个开源代码分析,发现execute_v2还是需要手动管理显存,为此接下来还是基于execute_async_v3进行推理类的编写。 原因有两个: 第一,本章第二节就是基于execute_async_v3的流程介绍数据在host和device之间是如何传输,流转的。 第二,Nvidia官方教程中基于TRT 8.5给出了一个较好的host、device数据管理方法,代码简洁易理解。- 推理类的面向对象设计 在工程化时,算法模块对外提供推理函数,供主流程调用,因此需要将算法模块封装为类,并将相关属性和方法在类中实现,便于管理。 在推理类中,为了实现推理,首先需要构建context,而构建context所需要的过程步骤,均放到init函数中实现,同时配置模型相关的参数,如类别名称,阈值,mean/std等。 根据ResNet50模型的特点,TensorRTInfer的UML类图设计如下: 主要包括对外提供推理接口inference,围绕推理过程,实现预处理,TRT模型推理,后处理,可视化,TRT模型初始化等函数功能。 配套完整代码在这里,代码中实现的推理类已可以完成独立功能,但仍有更进一步优化点,包括: 组batch推理模式,实现更高吞吐量 预处理模块以batch形式处理,提高数据预处理效率 预处理模块放置GPU进行处理,均衡CPU和GPU的负载 采用分布式队列机制,解耦任务调用,任务处理,可参考celery库 对于C/S架构的设计,需要用web服务包装算法,常用的有flask, fastapi, django,对于一般工程,可用flask, fastapi,复杂工程用django。 更多TRT模型类参考: TRT官方-simpledemo TRT官方-detectron2-infer YOLOv6官方 engine常用方法/属性 在上述TRT模型构建中,使用了engine中一系列方法和属性,这里简单总结一下。 engine.num_io_tensors: 获取输入、输出数据个数 engine.get_tensor_name(index):根据数据的顺序,获取名称,这个名称是onnx导出时设置的 engine.get_tensor_mode(tensor_name):根据数据名称,获取是Input还是output类型。 小结 本结整理TRT在python下推理的代码编写,包括对比execute_v2与v3的差异,并实现了推理类的设计与实现。 对于TensorRT的学习及使用,暂时告一段落,通过本章内容,可以将pytorch模型转换为trt模型,并在python中实现高效推理,同时了解TensorRT常用工具的使用。 对于一般场景,本章的TensorRT工具及量化内容可以满足,但对于边缘端、高性能部署,仍需精进模型加速知识。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "}} \ No newline at end of file +{"./":{"url":"./","title":"简介","keywords":"","body":"简介 2018年12月《PyTorch 模型训练实用教程》面世距今5年多仍在发光发热,且备受欢迎已超7.2K stars。为了满足读者进一步需求和紧跟技术发展潮流,第二版应运而生。 时隔5年,历时4年,耗时2年的《Pytorch实用教程》第二版完成了。在第一版的精华之上,增加了丰富详实的深度学习应用案例和推理部署框架,使本书更系统性的涵盖深度学习工程师所涉及的知识面。如人工智能技术发展一浪接一浪,《Pytorch实用教程》第二版不是结束,而是开始,开启新的技术、新的领域、新的篇章,希望未来能继续与大家一起在人工智能技术里学习、进步。 本书以基础概念为基石,计算机视觉、自然语言处理和大语言模型为核心,推理部署框架为桥梁,皆在为读者提供面向项目落地的代码工程与理论讲解。本书整体分三部分,上篇:入门,中篇:应用,下篇:落地。 上篇 PyTorch基础。针对刚入门、非科班、本科生,提供PyTorch介绍,讲解开发环境的搭建,介绍PyTorch的数据、模型、优化、可视化等核心模块,最后利用所讲解的PyTorch知识点构建一套自己的代码结构,为后续的应用打下基础。 中篇 产业应用。经过上篇,磨了一把好刀,接下来就用它在各领域上大显身手。将会讲解三个主题,分别是计算机视觉(Computer Vision)、自然语言处理(Natural Language Processing)和大语言模型(Large Language Model)。 在CV章节,包括主流的任务,有图像分类、图像分割、目标检测、目标跟踪、GAN生成、Diffusion生成、图像描述和图像检索八大任务。 在NLP章节,包括RNN、LSTM、Transformer、BERT和GPT模型详解与应用,应用的任务有文本分类、机器翻译、命名体识别、QA问答和文章生成五大任务。 在LLM章节,包括4个LLM部署与代码分析和一个LLM行业应用——GPT Academic(GPT 学术优化),LLM包括国内开源的四大主流模型,Qwen、ChatGLM、Baichuan和Yi。 下篇 工业落地。有了工具,有了场景,接下来就要让它产生价值,变成可用的、好用的算法服务。因此,从pytorch这样一个训练框架、重框架中剥离出来进行部署、加速、量化是常见的方法。本章将介绍ONNX和TensorRT的原理与使用,同时借助TensorRT详细分析模型量化概念、PTQ和QAT量化实战与原理。 相信经过上、中、下篇的学习,可以帮助入门的同学少走很多弯路,快速掌握PyTorch,具备独当一面的能力,能依据实际场景选择算法模型,可以将模型部署应用,形成闭环,全流程打通。 本书亮点 结构清晰:全书分为三部分:上篇(入门)、中篇(应用)、下篇(落地),逐步引导读者深入学习。 理论与实践结合:不仅提供理论讲解,还通过丰富的项目案例,让读者能够将理论应用于实践。 实战案例丰富:提供了计算机视觉、自然语言处理和大语言模型等多个领域的实战案例。 系统性覆盖:涵盖Pytorch基础、计算机视觉基础任务、自然语言处理基础任务、大语言模型基础、推理部署框架。 适用性广:适合AI自学者、AI产品经理、在校学生以及跨领域人士阅读,满足不同背景和需求的读者。 本书内容及结构 本书包含十二章,分三篇。分别是pytorch基础、项目应用和工业落地。 第一章 PyTorch 简介与安装,详细介绍Pytorch环境安装,包括Anaconda、Pycharm、CUDA&cuDNN和Jupyter Notebook。 第二章 PyTorch 核心模块,介绍代码结构,Tensor与自动求导机制。 第三章 PyTorch 数据模块,介绍Dataset、DataLoader、transforms库和应用案例。 第四章 PyTorch 模型模块,介绍Module、Parameter、Module容器、Hook等。 第五章 PyTorch 优化模块,介绍二十一个损失函数,十三个优化器,十四个学习率调整方法。 第六章 PyTorch 可视化模块,介绍Tensorboard、混淆矩阵、CAM、Grad-CAM、Grad-CAM++等。 第七章 PyTorch 小技巧汇总,介绍模型保存、加载、GPT使用、TorchMetrics、Albumentation、TorchEnsemble等。 第八章 PyTorch 图像项目案例之图像分类,介绍肺炎X光片二分类,AutoAug、推理性能分析等。 第八章 PyTorch 图像项目案例之图像分割,介绍脑部MRI肿瘤分割,涉及smp工具库、分割评价指标分析等。 第八章 PyTorch 图像项目案例之目标检测,近1万字介绍YOLOv5在无人机场景的目标检测,包含yolov5框架代码剖析、训练机制剖析、消融实验等。 第八章 PyTorch 图像项目案例之目标跟踪,近1万字介绍DeepSORT算法流程、匈牙利算法、卡尔曼滤波、源代码深入剖析、代码UML设计、撞线机制等。 第八章 PyTorch 图像项目案例之CycleGAN,介绍GAN、cycleGAN的理论与风格迁移代码实现。 第八章 PyTorch 图像项目案例之DDPM,介绍DDPM、Guided Diffusion Model、classifier-base 、classifier-free、去噪代码流程等。 第八章 PyTorch 图像项目案例之图像描述,介绍CNN+RNN架构、Clip+GPT架构、clip原理、gpt原理、CLIPCap 代码等。 第八章 PyTorch 图像项目案例之图像检索,近1万字介绍图像检索概念、评价指标、向量检索、Faiss库安装使用、CLIP+Faiss+Flask工程部署等。 第九章 PyTorch NLP项目案例之文本分类,介绍NLP基础、RNN、LSTM、分词、词频、词表、词向量、影评数据分类项目等。 第九章 PyTorch NLP项目案例之机器翻译,介绍seq2seq、transformers、特殊token、强制学习、三种注意力机制、中英互译项目等。 第九章 PyTorch NLP项目案例之命名体识别,介绍BERT原理与结构、命名体识别的表示、cluener数据集及其BERT训练项目等。 第九章 PyTorch NLP项目案例之文章续写,介绍gpt1, gpt2, gpt3, Instruct GPT四篇论文及对比,介绍GPT2训练代码框架,数据准备等 第九章 PyTorch NLP项目案例之QA问答,介绍gpt1, gpt2, gpt3, Instruct GPT四篇论文及对比,介绍GPT2训练代码框架,数据准备等 第十章 大语言模型安装与应用,介绍Qwen、ChatGLM3、Baichuan2、Yi的安装与应用,介绍GPT Academic(GPT 学术优化工具)的安装与项目剖析等。 第十一章 ONNX使用,介绍ONNX、onnxruntime、ONNX量化、混合精度、计算图优化、线程管理、IO binding、耗时分析等。 第十二章 TensorRT使用,介绍TensorRT安装、工作流原理、cuda-python库、trtexec使用、nsight system工具、polygraphy工具、TRT API、模型量化理论、PTQ量化理论与实践、QAT量化与实践,python工程化等。 阅读建议 为了更好使用本书,读者需要具备python基础,了解人工智能、深度学习概念。 本着Talk is cheap, show me the code 的精神,本书配套了丰富详细的代码,建议先跑通代码,再看理论基础。 深度学习是一门工程性质极强的学科,建议基于本项目提供的代码,在自己的任务、其他数据集上进行实践。 项目代码 https://github.com/TingsongYu/PyTorch-Tutorial-2nd 有任何想法和建议,欢迎联系:yts3221@126.com Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"preface.html":{"url":"preface.html","title":"前言","keywords":"","body":"前言 本书背后的故事 《PyTorch实用教程》(第二版),动笔时间为2021年12月16日,什么时候能完结,并不知道(2024年4月19日完成了!)。为什么会不是写完再发布?因为担心再不迈出第一步,这本书就会难产。其实,想写这本书已经很久了,至少2年多了吧(见下图),总由于各种原因,迟迟没有开始动笔,索性,采用github在线更新的方式,和各位朋友提前见面吧,你们的反馈是我最大的动力。 为何历时4年? 早在2019年12月,就萌生编写第二版的想法,当时咨询过朋友,阅读过同类书籍,编写了大纲,但2020年的疫情、换工作、成家等事情给写书破了第一盆冷水。写书的想法就像一颗种子,一直埋在心里,2021年12月终于下定决心,通过在线书籍的方式,一砖一瓦的编著,而非一次性完工再发布。现如今回看当时的方式是正确的,很多事情不是一蹴而就,而是需要漫长的时光来打磨,因此踏出第一步显得尤为重要。正如一位博主所说,“一个人要怎样才能不断坚持干一件事?那就是基于“一点点哲学”,“一点点哲学”说的是,先一点点的干,不管是30分还是20分,千万不要有一次干到位的想法,千万不要想一次就干到90分,100分。慢即是快。”回顾本书编写历程,的确是这样,耗时2年多的时间里,中断了3次,包括工作任务重、家长里短和孩子出生。 但好在,写书这团火没有灭,经过多次重启,终于在2024年4月完成最后一节——GPT 学术优化工具使用,并在2024年4月19日晚导出PDF版,字数共计204842字,导出PDF时间虽然只有几秒钟,但是我感觉特别漫长且忐忑,像极了当时产房外等待孩子。 为什么写这本书? 这本书是对PyTorch模型训练实用教程的改进优化,是对第一版的内容进行丰富,增加更多的PyTorch基础,增加丰富的应用案例,同时包含模型部署上线这一关键的知识点。萌生第二版的想法大约在2019年11月,当时距离第一版发布一年,期间又对PyTorch有了一个深入的学习了解过程,于是想把这本书继续优化下去,帮助更多的朋友快速掌握PyTorch。可奈于当时精力有限,就迟迟没有动笔,2020年与2021年是忙碌的两年,生活的琐碎以及工作占据了大多数时间,2021年12月16日22:13:10终于有合适的条件动笔了,一刻不敢拖延,害怕这刚刚燃起的火苗又被琐碎的生活浇灭。 除了是对第一版的改进,更大的原因是一种责任。由于这几年的经历,让我感到需要出一本高质量的PyTorch书,它一定是大家爱不释手的资料,可以为大家的深度学习道路提供一点便捷,能给大家带来些许改变,这就足够了。第一版发布过去已经五年,仍旧能看到她在发光发热,这令我十分欣慰,但又非常愧疚。 并且,书籍是人类智慧的结晶,不同于普通的博客、公众号甚至课件,书籍的内容会更加完整和充实,这是受到一个视频的启发,视频说的是北大退休的韩茂莉教授将在B站上延续她的课堂生涯,将她毕生所学的历史地理知识传授给更多的朋友,在第一个视频里她提到:“但凡具有一种人类形成的,知识性的精华,其实都在书中 。” 看到这句话感触颇深。 第一版仍旧在发光发热 本书是一本面向深度学习领域的综合指南,可为不同背景、需求的读者提供从基础到实战的全面指导。适合阅读本书的读者类型有: AI自学者:本书包含入门知识、使用技巧和完整项目代码,可独立完成多个AI项目。 AI产品经理:本书包含CV、NLP、LLM方向的十多个应用案例、代码和原理分析,可提供全面的应用场景。 在校学生:本书包含理论分析、代码详解、结构设计,可与机器学习、面向对象设计、深度学习等课程形成体系。 跨领域人士:本书包含环境安装、基础介绍、项目应用,可提供轻松入门学习路径。 如何阅读这本书 为了更好使用本书,读者需要具备python基础,了解人工智能、深度学习概念。 本着Talk is cheap, show me the code 的精神,本书配套了丰富详细的代码,建议先跑通代码,再看理论基础。 深度学习是一门工程性质极强的学科,建议基于本项目提供的代码,在自己的任务、其他数据集上进行实践。 由于自身水平有限,书中不可避免的会出现错误,恳请大家指正。 欢迎联系:yts3221@126.com 项目代码 https://github.com/TingsongYu/PyTorch-Tutorial-2nd 致谢 本书的面世,首先感谢家人们的支持、理解和帮助,其中包括妻子的精神鼓励和行动上支持,包括四位父母的家庭生活上支持。其次,感谢所有热衷开源、分享、传播技术的伟大开发者们,本书更像是搬运工,穿梭于论文、技术报告、开源项目之中,筛选、消化、整合,再以深入浅出的方式呈现出来,因此本书也以开源的方式提供给广大的网友学习。最后,感谢一路上遇到的所有支持本项目的伙伴,是你们的热情鼓舞着我在这一次次中断后重启。再次感谢你们! Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/":{"url":"chapter-1/","title":"第一章 PyTorch 简介与安装","keywords":"","body":"第一章 PyTorch 简介与安装 第一章 PyTorch 简介与安装 1.1 PyTorch 初认识 1.2 环境配置之Anaconda 1.3 环境配置之-IDE——Pycharm & VS Code 1.4 环境配置之CUDA&cuDNN 1.5 环境配置之PyTorch系列包 1.6 环境配置之Jupyter Notebook 1.7 PyTorch 2.0 与 torch.compile 本章,将带领读者踏入PyTorch的环境配置之路,从Python虚拟环境的搭建到IDE的安装,CUDA与cuDNN的配置,再到PyTorch的精细安装,直至Jupyter Notebook的实践,每一步均图文并茂。 篇章概览: PyTorch初认识:开篇,将揭示PyTorch的起源,介绍它如何加速从科研原型至部署的路径,以及背后的历史与哲学。 Anaconda环境配置:深入Python虚拟环境的搭建,Anaconda的语法,如何借助它轻松管理Python环境与包,创建隔离的环境,为PyTorch铺路。 IDE选择:PyCharm & VS Code:对比两大IDE,PyCharm的社区版与专业版,VS Code。 CUDA与cuDNN:介绍CUDA与cuDNN的安装,如何让GPU驱动PyTorch加速,选对的CUDA版本匹配,是关键的一节。 PyTorch系列包安装:从torch、torchvision、torchaudio至torchtext,将详细介绍安装流程与版本匹配。 Jupyter Notebook实践:介绍Jupyter Notebook的概念及安装,并且配置Kernel。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.1-PyTorch-Introduction.html":{"url":"chapter-1/1.1-PyTorch-Introduction.html","title":"1.1 PyTorch 初认识","keywords":"","body":"1.1 PyTorch 初认识 一句话认识PyTorch “An open source machine learning framework that accelerates the path from research prototyping to production deployment.” ——选自 https://pytorch.org/ PyTorch 是一个开源机器学习框架,帮助用户加速从算法模型研究到产品部署的过程。 PyTorch历史 FAIR( Facebook AI Research,Facebook人工智能研究院 )于2017年初发布PyTorch,PyTorch 的命名由 Py(代表 Python)和 Torch 组成。Py就是python语言,Torch是一款早期的深度学习框架,所以PyTorch是在Torch基础上用python语言重新打造的一款深度学习框架。 那么什么是Torch呢?Torch是纽约大学在2002年开发的深度学习框架。Torch虽然很好用,但是它采用了一门较为小众的语言——Lua语言作为接口。这明显地增高了Torch的使用门槛,想要使用Torch必须学习一门新的编程语言,这让大家都望而却步。 好在Torch的幕后团队也意识到这一点,于是团队采用python语言作为接口对Torch进行了重构,于是就有了PyTorch。 PyTorch代码最早公开版本可追溯至2016年8月24日的v0.1.1(https://github.com/pytorch/pytorch/tags?after=v0.1.4) 随后 •2017年1月正式发布PyTorch •2018年4月更新0.4.0版,支持Windows系统 •2018年11月更新1.0稳定版,已成为GitHub上第二快的开源项目 ...... 对PyTorch版本的更新感兴趣的读者,可以关注PyTorch-Tags,这里是最权威版本更新信息来源,要比官方文档还要快。 PyTorch必备网站 要熟悉PyTorch,不得不熟悉PyTorch的一些官方网站,以及这些网站的使用。下面列举几个实用的PyTorch网站。 官网 https://pytorch.org/ 官网包含权威的PyTorch介绍、PyTorch官方文档、生态资源等信息。例如在Get Started中,可以获取权威的安装信息。例如,特定版本下,windows系统,所支持的CUDA版本是多少,这点非常关键,往往GPU用不起来,就是CUDA版本不匹配。 除了最新稳定版,还可以下载历史版本的whl文件,进行离线安装(网速不好的读者,建议手动下载whl,然后进行安装)。历史版本whl的在哪那里下载呢? 是需要挖掘的,网址在上图的windows系统、Pip安装、Python、CUDA条件下才会出现,它是:https://download.pytorch.org/whl,点击torch,就可以发现所有历史版本都在这里可以找到,并且命名都有良好的规范,这里不过多介绍,在安装部分再详细讲解。 除了Get Started栏目,其它栏目也是很好的学习资料。 Ecosystem:PyTorch生态系统资源库,里面收录生态系统内的资源,也欢迎大家加入并贡献资源,里边有CV数据增强开源库——albumentations、FB的目标检测和分割算法库——detectron2、优秀的部署平台——onnxruntime等等 Mobile:移动端PyTorch实现方案及资源。 Blog:PyTorch相关新闻。 Tutorials:案例教程,这里面都是个人提供的、针对某一个应用的demo级的教程。包含如下方向,对于新手来说,可以看一下,了解个大概,但里面的代码多是demo,无法开箱即用于项目应用,这也是本书第二部分将会弥补的地方,敬请期待。 Docs:PyTorch API官方文档, 这也是我一直首推的学习资料,PyTorch的文档非常友好,可以查阅不同版本,各个API都有详细介绍,大多都有代码案例,PyTorch的基础部分主要从这里开始进行讲解。Docs下还会分PyTorch、torchvision、torchaudio、torchtext等,大家需要针对性的检索。 Resource:这里包含各种资源,如社区、新闻报道、论坛、ModelHub资源等等。 PyTorch发展趋势 为什么要学PyTorch? 因为PyTorch发展迅猛,已经在多方面荣登深度学习框架第一的宝座,学术界绝大多数论文都有PyTorch实现,想要紧跟技术,利用新模型进行科学研究,进行项目开发的,不得不跟随学术界的趋势,所以可以看到PyTorch席卷各界。 PyTorch称王,TensorFlow答应么?一起来看看几个数据。 图1: 各大顶会期刊中,使用PyTorch的论文数量占PyTorch+TensorFlow的百分比。其实就是 p / (p+t),这里有一个分界点就是50%,当达到50%时,说明PyTorch与TensorFlow平分秋色,当大于50%时说明PyTorch已经超过TF,而当数据超过75%,表明PyTorch已经是TF的两倍。从这个趋势可以发现转折点在2018-2019年之间发生,现在已经2021年末了,哪个框架是学术界的带头大哥? 图2:这幅图对比的是PyTorch与TF的决定数量,可以看到TF的份额被PyTorch一步步蚕食,实线代表的PyTorch持续上扬,TF的虚线不断下探。 图片出自:https://horace.io/pytorch-vs-tensorflow/ 通过学术界的论文情况就可以说明PyTorch是未来的大势所趋,虽然说早期PyTorch在工业部署上并不如TensorFlow,但是如今PyTorch推出了libtorch,TorchServe,以及各类优秀的,适配性良好的部署框架层出不穷,如TensorRT、OpenVINO、ONNX等,都可以帮助PyTorch进行快速部署 感觉PyTorch是在学术界占据主导地位,让科研工作者感到满意,新模型都是PyTorch实现的,工业界的开发者总不能不用最新的算法、模型,只能纷纷转向PyTorch了。因此,相信大家选择使用PyTorch进行深度学习、机器学习模型开发,一定能加速大家的算法模型开发,也印证了PyTorch的主旨——An open source machine learning framework that accelerates the path from research prototyping to production deployment.” ​ Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.2-Anaconda.html":{"url":"chapter-1/1.2-Anaconda.html","title":"1.2 环境配置之Anaconda","keywords":"","body":"1.2 环境配置之Anaconda 工欲善其事必先利其器,想要使用PyTorch进行模型开发,必须要搭建好开发环境。听到环境安装,大家是否已经心生恐惧?是否都被某些不知名的报错卡住好几天,网上搜不到答案,各种文章的方案都不适用? 为了解决大家环境安装的苦恼,本章将从Python虚拟环境、Anaconda、Pycharm、CUDA、CuDNN的背景说起,给大家构建系统的认识,理清各软件之间的关系,为大家呈现一个清晰的、完整的开发环境配置。 1.1节中提到过,PyTorch是基于Python为接口提供给用户使用,因此Python语言是我们的核心,PyTorch对于Python只是一个工具包(library),通过import torch的方式使用而已。因此想要搭建完整的PyTorch开发环境,其实是搭建完整的Python开发环境,同时安装上PyTorch这个工具库。 虚拟环境 为了使用PyTorch,先搞定Python环境安装。提到Python环境,就不得不讲python虚拟环境(virtual environment)的概念了。python虚拟环境是为了解决众多的工具包之间版本冲突而设计的一个纯净开发环境,简单地可创建多个虚拟环境,不同的环境中使用不同的工具包,例如虚拟环境1中安装pytorch 1.6, 虚拟环境2中安装的是pytorch0.4,当需要用老版本pytorch时,切换到虚拟环境2,然后调用虚拟环境2中的解释器——python.exe 运行你的.py代码。当需要用pytorch1.6时,就切换到虚拟环境1,调用虚拟环境1中的python.exe运行代码。这样就很好的解决工具包版本冲突问题。 解释器 这里提到一个概念,解释器——python.exe。 解释器,就是人类与CPU之间的桥梁,人写的高级语言,如print(\"Hello World\"),CPU是读不懂的,CPU只能读0/1形式的二进制文件,这时就需要一个翻译官——python.exe, python.exe读取.py文件,解释成二进制文件,让CPU读懂,并运行。这就是解释器的作用,更直观的例子,python3的解释器是无法翻译python2语法的语句print \"Hello World\"的,因此不同的python.exe其实就对应了不同的环境。 Anaconda Anaconda是为方便使用Python而建立的一个软件包,包含常用的250多个工具包,多版本Python解释器和强大的虚拟环境管理工具,所以Anaconda被称为Python全家桶。Anaconda可以使安装、运行和升级环境变得更简单,因此使用它作为Python虚拟环境管理工具。 安装非常简单,首先进入 anaconda官网,点击“Get Started”, 点击”download anaconda installers“,看到如下图所示的信息,选择你对应的操作系统下载,安装即可。 安装完毕,可以尝试创建一个虚拟环境,这里需要注意创建环境的时候就要决定Python的版本,而pytorch的安装对python的版本有要求,所以大家先到pytorch官网看看要装的pytorch版本所支持的python版本,然后再回来创建虚拟环境。这里演示pytorch最新版本1.10的安装。由于1.10是支持python3.6/3.7/3.9的(通过1.1介绍过的神奇网站找到信息),在这里用python3.6作为python版本进行创建虚拟环境。 >>> conda create -n pytorch-1.10-gpu python=3.6 这里的pytorch-1.10-gpu就是虚拟环境的名称,激活(activate)时的标识,同时也会在anaconda安装目录下创建pytorch-1.10-gpu的文件夹,在该文件夹存放本环境所有工具包、解释器,如下图所示: 虚拟环境创建好之后,可以看看这个文件夹里都有些什么,先来看解释器: D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe ,大家可以双击它,就可以看到解释器的相关信息,后续用到pytorch_1.10_gpu这个环境的时候,也是用这个.exe进行运行.py文件,后面用pycharm运行代码的时候会给大家展示。 到此,一个纯净的虚拟环境就创建好了,接下来需要激活(activate)这个环境,然后再里面进行各种工具包的安装。这里先暂停一下,先去看另外一个工具——Pycharm的安装及使用。 anaconda常用命令 创建环境:conda create -n your_env_name python=X.X (X.X为python版本) eg: conda create -n pytorch_tutorial python=3.7 激活环境:source activate your_env_name eg: source activate pytorch_tutorial 退出环境:source deactivate 删除环境:conda remove -n your_env_name –all eg: conda remove -n pytorch_tutorial --all 查看已有虚拟环境:conda env list / conda info -e (推荐大家自行了解更多anaconda命令) 有了anaconda帮助我们管理虚拟环境以及python工具包,接下来就可以安装IDE,用来管理项目了。请看Pycharm安装 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.3-Pycharm.html":{"url":"chapter-1/1.3-Pycharm.html","title":"1.3 环境配置之-IDE——Pycharm & VS Code","keywords":"","body":"1.3 环境配置之IDE——PyCharm&VS Code Pycharm——强大的python IDE Pycharm——强大的python IDE,拥有调试、语法高亮、Project管理、代码跳转、智能提示、版本控制等功能。是 Pycharm有社区版和专业版区分,社区版为免费的,专业版需要付费。 Pycharm 社区版 vs 专业版 由于专业版需要一定的费用,对于普通学习者来说,若不需要特殊功能的话,社区版也是足够的。 专业版和社区版在功能上的一些主要区别如下: 功能分类 PyCharm专业版 PyCharm社区版 基础功能 - 代码编辑 - 代码编辑 - 语法高亮 - 语法高亮 - 代码提示 - 代码提示 - 代码格式化 - 代码格式化 版本控制 - Mercurial, Subversion, Git, Perforce等支持 - Mercurial, Subversion, Git支持 代码分析 - Python代码质量分析 - 基础代码分析 代码重构 - 重命名、提取方法/超类等重构功能 - 有限的重构功能 调试器 - 强大的调试器,支持断点、步进、多画面视图等 - 基本的调试功能 Web开发支持 - Django, Flask等Web框架支持 - 不支持 数据库与SQL支持 - 支持多种数据库与SQL查询 - 不支持 科学计算工具 - 支持NumPy, SciPy等科学计算库 - 不支持 远程开发 - 支持远程解释器、部署和调试 - 不支持 用户界面与自定义 - 高度可定制的用户界面 - 有限的自定义选项 插件支持 - 支持大量插件,扩展性强 - 支持有限插件 许可与费用 - 商业许可,需要付费 - 免费,但仅限于非商业用途 Pycharm 的安装 这里我采用的是pycharm.2019专业版,用于演示。 可以从官网下载安装包 https://www.jetbrains.com/pycharm/ 运行 pycharm-professional-2019.2.exe 选择路径,勾选Add launchers dir to the PATH,等待安装完成 激活部分:略。 这里主要讲如何创建项目,以及关联前面创建的虚拟环境pytorch_1.10_gpu。 打开pycharm,左上角的File可选择New,或者Open,如果已经有一个文件夹下有相关.py代码,那么就用Open对应的文件夹即可。这里假设已经存在pytorch-tutorial-2nd文件夹,找到它,Open即可。 我们找到pytorch-tutorial-2nd\\code\\chapter-1\\01-hello-pytorch.py,发现import torch下面有个红色波浪线,鼠标放上去会提示“No Module named torch\",表明当前环境里并没有torch这个工具包。可好像我们并没有为当前.py文件设定好用哪个一解释器不是?所以我们先将当前项目pytorch-tutorial-2nd的虚拟环境设置为刚刚创建好的pytorch_1.10_gpu,然后再在pytorch_1.10_gpu里安装上pytorch即可。 左上角File--> Settings-->Project:pytorch-tutorial-2nd-->Project Interpreter, 然后如下图找到对应的python.exe,之后选中,点击OK,再次点击OK。就完成了虚拟环境与项目的关联,接着就可以安装pytorch了。 到这里,大家可以尝试运行 pytorch-tutorial-2nd\\code\\chapter-1\\01-hello-pytorch.py,会提示 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe E:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py Traceback (most recent call last): File \"E:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py\", line 9, in import torch ModuleNotFoundError: No module named 'torch' Process finished with exit code 1 这里是完整的Pycharm控制台信息,我们可以看到第一行先是解释器(即对应了我们创建的虚拟环境),然后是执行的.py文件,接着是报错信息,提示没有torch这个module,下一小节我们来就来安装这个module。 Pycharm 拓展 pycharm是很好用的IDE,这里面提供很多快捷键,希望大家可以熟悉使用这些快捷键,例如常用的 批量注释:Ctrl + / 快速查看文档:Ctrl + q 搜索:Ctrl+f 运行:Shift + F10 Tab / Shift + Tab 缩进、不缩进当前行 Ctrl + D 复制选定的区域或行 Ctrl + Y 删除选定的行 更多功能推荐大家自行了解一下pycharm的基础使用,相信它一定是你的高效生产力。 推荐pycharm也有一些文档教程: pycharm官方文档 《pycharm 中文指南》 VS Code 简介 Visual Studio Code 是微软推出的独立源代码编辑器,可在 Windows、macOS 和 Linux 上运行,是 JavaScript 和 Web 开发人员的最佳选择,具有几乎可支持任何编程语言的扩展。其轻量的特点,受到广大开发者的喜爱,这里介绍如何在VS Code中配置python开发环境。 提到Visual Studio Code,大部分人都会联想到Visual Studio,但两者是完全不同的概念。 Visual Studio:适用于 Windows 上 .NET 和 C++ 开发人员的最全面 IDE。 完整打包了一系列丰富的工具和功能,可提升和增强软件开发的每个阶段。 Visual Studio Code:在 Windows、macOS 和 Linux 上运行的独立源代码编辑器。 JavaScript 和 Web 开发人员的最佳选择,具有几乎可支持任何编程语言的扩展。 从官网定义就知道,VS是IDE, VS Code是代码编辑器,下面进行VS Code安装、配置python开发环境。 VS Code下载安装 VS Code下载安装非常简单,通过官网下载安装包:https://visualstudio.microsoft.com/zh-hans/ 双击一路安装,在“选择附加任务”当中,建议把“添加到 PATH(重启后生效)”勾选上。 VS Code Python环境配置 插件1 - Python VS Code强大之处在于有许多插件,在这里python环境也需要安装一些插件,首先需要安装的插件是“Python”。 在IDE左侧的Extensions菜单中(Ctrl+Shift+X),输入python,搜索到Python插件,点击Install 安装好之后,点击右下角,选择解释器。 接着就可以右键对应的代码文件,Run Python -> Run Python File in Terminal,即可获得如下提示 第一行表示选择了解释器C:/Users/yts32/anaconda3/envs/pt112/python.exe,运行代码 f:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py。 PS F:\\pytorch-tutorial-2nd> & C:/Users/yts32/anaconda3/envs/pt112/python.exe f:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py Hello World, Hello PyTorch 1.12.1 CUDA is available:True, version is 11.3 device_name: NVIDIA GeForce RTX 4060 Laptop GPU 插件2 - Python Pylance Pylance是强大的Python静态类型检查器,提供更精确的自动补全和错误检查。 插件3 - autoDocstring autoDocstring是函数自动注释模板生成插件, 在函数下输入\"\"\"回车,即可得到函数的注释模板,例如: def test(a: str): \"\"\"_summary_ Args: a (str): _description_ Returns: _type_: _description_ \"\"\" print(a) 其他功能: 自动生成文档字符串:快速生成可以逐个Tab键完成的文档字符串片段。 支持多种文档字符串格式:用户可以选择不同格式的文档字符串,如Google、Sphinx、Numpy、docBlockr、one-line-sphinx和PEP257。 类型推断:根据PEP484类型提示、默认值和变量名推断参数类型。 支持参数和关键字参数:插件支持args、kwargs、装饰器、错误和参数类型的自动填充。 自定义文档字符串模板:支持自定义模板,使用mustache.js模板引擎。 插件4 - Python Indent Python Indent是一个改进Python自动缩进的工具。这个插件通过解析用户编写的Python代码,自动确定每一行代码应该缩进的程度,主要功能和特点: 自动缩进:按下Enter键时,插件会解析光标位置的Python文件并确定下一行(或两行,如果是悬挂缩进)应该缩进多少,以及附近的行应该如何取消缩进。 括号配对缩进:当光标位于一个开放的括号([({)和它的闭合括号对(相应的]}))之间时,插件会保持后续行的缩进正好在它被打开的位置的右侧。 悬挂缩进:当用户打开一个括号但尚未插入任何内容时,按下Enter键将创建一个悬挂缩进,与VS Code的基本行为相匹配。 关键字缩进:某些Python关键字(return, pass, break, continue, raise)暗示了特定的缩进行为。例如,如果有return语句,则下一行可以取消缩进,因为同一代码块中不能跟随任何语句。 注释扩展:如果用户在注释中间按下Enter键,那么下一行将自动成为注释。 VS Code 整体组件轻便,简洁,适合中高级开发者进行高自由度的插件选择,打造适合自己的IDE,对于要求不高且不需要高级自定义功能,pycharm是不错的选择。 小结 pycharm和VS Code都是强大的IDE,前者适合“懒人”用户,开箱即用,后者适合喜欢DIY的开发者。这里个人建议入门者使用pycharm的社区版,方便快捷无烦恼的用上IDE。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.4-CUDA&cuDNN.html":{"url":"chapter-1/1.4-CUDA&cuDNN.html","title":"1.4 环境配置之CUDA&cuDNN","keywords":"","body":"1.4 环境配置之CUDA&cuDNN 有了python环境和开发环境,马上到主角登场。PyTorch登场前,针对GPU版,还需要额外安装一些东西。 从1.1我们知道PyTorch的安装可根据设备类型分为GPU版或CPU版。 CPU 对于CPU版本直接通过pip或者anaconda命令安装即可,如: >>> pip3 install torch torchvision torchaudio 具体的命令可查阅:https://pytorch.org/get-started/locally/ 官网上给出的命令其实安装了3个包,分别是torch, torchvision,torchaudio,这命令会根据当前系统自动选择对应python版本的whl进行安装,不需要用户额外操作。但,如果网速不好,或者需要离线安装,这时可以考虑下载whl包然后自行安装,下载whl的链接:https://download.pytorch.org/whl/torch/ pytorch与torchvision版本匹配 若是手动下载的whl,需要注意pytorch与torchvision之间版本对应关系,这个可以到torchvision Github查看,这点非常重要,CV中一些报错就是因为torchvision与pytorch版本不匹配导致的。这里就copy过来,大家参考好了。 torch torchvision python main / nightly main / nightly >=3.6, 1.10.0 0.11.1 >=3.6, 1.9.1 0.10.1 >=3.6, 1.9.0 0.10.0 >=3.6, 1.8.2 0.9.2 >=3.6, 1.8.1 0.9.1 >=3.6, 1.8.0 0.9.0 >=3.6, 1.7.1 0.8.2 >=3.6, 1.7.0 0.8.1 >=3.6, 1.7.0 0.8.0 >=3.6, 1.6.0 0.7.0 >=3.6, 1.5.1 0.6.1 >=3.5, 1.5.0 0.6.0 >=3.5, 1.4.0 0.5.0 ==2.7, >=3.5, 1.3.1 0.4.2 ==2.7, >=3.5, 1.3.0 0.4.1 ==2.7, >=3.5, 1.2.0 0.4.0 ==2.7, >=3.5, 1.1.0 0.3.0 ==2.7, >=3.5, 0.2.2 ==2.7, >=3.5, 举一反三,torchaudio、torchtext同理。 GPU版本 深度学习之所以能火,是因为有了强大的GPU支撑,自然地,绝大多数情况下我们会安装GPU版本的pytorch。目前PyTorch不仅支持NVIDIA的GPU,还支持AMD的ROCm的GPU。不过我们还是以N卡为例,毕竟N卡还是主流,A卡仍需努力。 对于N卡,什么型号是pytorch支持的呢?首先,需要计算能力(compute capability)≥3.0的GPU。很多地方都会看到计算能力≥3.0,理论出自哪呢? 我在官方文档中找到了相关信息文档 It has a CUDA counterpart, that enables you to run your tensor computations on an NVIDIA GPU with compute capability >= 3.0 问题来了,怎么知道自己的GPU的compute capability呢?请看NVIDA文档,选择你对应的系列,找到对应型号。 举几个例子: GPU 计算能力 GeForce RTX 2080 7.5 GeForce RTX 2070 7.5 GeForce RTX 2060 7.5 GeForce GTX 1080 6.1 GeForce GTX 1070 6.1 GeForce GTX 1060 6.1 其实,只要是近几年购买的N卡都是没有问题的。确定了显卡是支持的,接下来就要决定一个非常重要事情,就是选中对应的CUDA版本进行安装。 CUDA ​ CUDA(ComputeUnified Device Architecture),是NVIDIA推出的运算平台。 CUDA是一种由NVIDIA推出的通用并行计算架构,该架构使GPU能够解决复杂的计算问题。 与之配套的是cuDNN, NVIDIA cuDNN是用于深度神经网络的GPU加速库。它强调性能、易用性和低内存开销。NVIDIA cuDNN可以集成到更高级别的机器学习框架中。 细心的朋友在PyTorch官网就能发现, Compute Platform中并不给出显卡型号,而是给出CUDA版本,这就要求我们安装特定版本的CUDA,才能使用特定版本的PyTorch。例如PyTorch 1.10 只支持CUDA 10.2, CUDA 11.3,以及CUDA 11.1。为什么这里用了以及呢? 因为在官网上并没有显示CUDA 11.1,但是在https://download.pytorch.org/whl/torch,搜索,可以看到11.1的whl。 在这里选择10.2版本进行安装,CUDA下载通过官网,官网通常只显示最新版本cuda,这里需要大家进入具体的版本下载界面,拖到底部,找到: Archive of Previous CUDA Releases 接着可以找到对应的CUDA版本,进入下载即可,这Installer Type 有 exe (network) 和 exe (local)两种选择,我们选local的方式,下载2.6G的cuda_10.2.89_441.22_win10.exe即可。 安装方式十分简单,一直下一步即可,只需要记住安装到了哪里,这里默认路径为 C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2 下面来测试一下CUDA安装是否成功,可以打开命令窗,进入C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2\\bin,然后输入 nvcc -V cuDNN 有了CUDA平台,还需要安装cuDNN,cuDNN全称为NVIDIA CUDA Deep Neural Network (cuDNN) 。它是一个深度神经网络的加速库,里边实现了神经网络常用的操作,并且是高度优化的,可以极大地榨干NVIDA显卡的性能,因此用N卡都会用cuDNN库。 cuDNN库的安装非常简单,与其说是安装,不如说是下载库文件,放到CUDA所在的目录下。具体步骤如下: 打开网址:https://developer.nvidia.com/cudnn,点击右上角,需要注册,再登录。 登录后,点击Download cuDNN,跳转到下载页面,选择好cudnn版本,操作系统版本,即可开始下载 将下载好的压缩包cudnn-10.2-windows10-x64-v8.2.4.15.zip 解压 分别将bin、include、lib\\x64下的文件分别对应拷贝到C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2文件夹下的bin、include、lib\\x64下 到这里CuDNN安装完毕。 CUDA 安装验证 ​ 接下来运行NVIDIA提供的官方案例,检查CUDA的正常是否正确,这里暂时未找到好的办法验证CuDNN的安装正确与否,CuDNN的验证可在后续pytorch安装好之后,采用代码验证。 ​ 打开命令窗口,在C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v10.2\\extras\\demo_suite文件夹中分别执行bandwidthTest.exe和deviceQuery.exe。观察到Result=PASS 即表示CUDA安装成功。 cuDNN验证 import torch from torch.nn import Conv2d # 检查CUDA是否可用 print(\"CUDA available:\", torch.cuda.is_available()) # 检查cuDNN是否启用 print(\"cuDNN enabled:\", torch.backends.cudnn.enabled) # 创建一个卷积层并移动到GPU conv = Conv2d(in_channels=3, out_channels=16, kernel_size=3).cuda() # 创建一个输入张量并移动到GPU input_tensor = torch.randn(1, 3, 32, 32).cuda() # 前向传播 output = conv(input_tensor) print(\"Output shape:\", output.shape) Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.5-PyTorch-install.html":{"url":"chapter-1/1.5-PyTorch-install.html","title":"1.5 环境配置之PyTorch系列包","keywords":"","body":"1.5 环境配置之PyTorch系列包 虚拟环境,Pycharm,CUDA,cuDNN均已准备好,现在终于可以安装PyTorch了。 现在,通过命令窗口,进入虚拟环境 E:\\pytorch-tutorial-2nd>conda activate pytorch_1.10_gpu (pytorch_1.10_gpu) E:\\pytorch-tutorial-2nd> 可使用以下命令安装 pip3 install torch==1.10.1+cu102 torchvision==0.11.2+cu102 torchaudio===0.10.1+cu102 -f https://download.pytorch.org/whl/cu102/torch_stable.html 可以看到通过pip安装,也是下载我们提到的神奇网站里的whl文件,大家可以根据自己的网速决定是采用pip还是自行下载的方法。 如果网速不好的话,推荐通过神奇的网站——https://download.pytorch.org/whl/torch 搜索对应的whl进行下载。然后pip install *.whl。 在使用pip时,建议大家添加镜像源。例如,清华镜像源或者中科大镜像源,这样安装python工具包的下载速度会快很多,请自行搜索如何添加清华镜像源。 安装完毕,再回到pycharm,运行 pytorch-tutorial-2nd\\code\\chapter-1\\01-hello-pytorch.py,可以看到 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe E:/pytorch-tutorial-2nd/code/chapter-1/01-hello-pytorch.py Hello World, Hello PyTorch 1.10.1+cu102 CUDA is available:True, version is 10.2 device_name: NVIDIA GeForce GTX 1660 Ti with Max-Q Design Process finished with exit code 0 表示pytorch环境安装完毕,此时我们也可以再次打开pycharm的解释器配置,可以看到当前的解释器(虚拟环境)下,拥有的相关工具包,这个界面也是后续检查当前环境工具包版本常用的工具,请收藏。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.6-JupyterNotebook-install.html":{"url":"chapter-1/1.6-JupyterNotebook-install.html","title":"1.6 环境配置之Jupyter Notebook","keywords":"","body":"1.6 环境配置之Jupyter Notebook 什么是 jupyter notebook 经过前几个章节的准备,目前已经具备了PyTorch开发环境,但本教程需要照顾初学者使用代码,为初学者提供更佳的代码学习体验。因此,在上篇,主要采用Jupyter Notebook进行代码演示。 注意:Jupyter Notebook 建议仅用于学习目的,不推荐用于项目开发。 因为notebook自身定位决定的,先来看看jupyter notebook 的定义“The Jupyter Notebook is a web application for creating and sharing documents that contain code, visualizations, and text. It can be used for data science, statistical modeling, machine learning, and much more.”——官网 Jupyter notebook 是一个网页应用,在这个网页上可以编写代码、做可视化、写文本,这就是非常好的教学展示平台。可以在上面进行概念描述、配上代码、运行结果,并且可以按小段进行代码运行,给用户更多的交互体验,便于用户理解代码细节。 基于此,本书上篇主要采用notebook进行代码讲解,到了中篇,基于完整的项目代码框架进行应用开发。 关于jupyter与jupyter notebook的关系,请大家查看官网(以下开始,notebook 指代 jupyter notebook) notebook 运行逻辑 Jupyter Notebook不仅仅是一个简单的web应用程序,它还需要关联指定的kernel,用于执行我们的代码。相信刚接触notebook的朋友大多都被notebook, kernel的概念搞的一头雾水。如果上述概念理不清楚,就更不清楚如何配置kernel,选择指定的虚拟环境了。 下面,我们先来看看notebook的结构 图片来自官网 图中左边是用户写的代码,传输到中间的Jupyter server, server本身不能执行代码,server把代码传给Kernel,Kernel才是实际执行代码的组件,执行代码的地方。Kernel执行完代码,把结果返回给server,再返回到用户的网页。 从图中可以看出Kernel不仅可以是python.exe,也可以是其他语言的解释器,如Julia, R等,更多kernel可以看支持的kernel列表. 通过上述示意图我们就知道了,在pytorch开发中,kernel其实就是某一个python解释器——python.exe,我们需要为当前的notebook配置相应的kernel,来进入相应的虚拟环境,这样才能运行代码。 notebook 安装 理清概念,下面进行notebook安装,目标是正确调用pytorch_1.10_gpu这个虚拟环境来执行notebook上的代码。 安装过程分三步: 进入虚拟环境:conda activate pytorch_1.10_gpu 安装ipykernel工具包(安装jupyter): pip install jupyter 添加kernel到notebook: python -m ipykernel install --user --name pytorch_1.10_gpu (意思是,将python这个kernel添加到jupyter的kernel中,由于当前已经在虚拟环境中,所以第一个python表示的含义是:D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe;而pytorch_1.10_gpu是kernel的别名,用于区分不同的kernel,这里建议与虚拟环境名称保持一致就好) 启动 在命令行中执行jupyter notebook就可以打开web应用了,网址为:http://localhost:8888; 这里默认端口为8888,如果再次启动一个jupyter notebook,可以看到端口号变为了8889,即它是另外一个web服务。 进入之后,我们可以看到有一个根目录,我们需要找到我们的notebook文件进行打开,这里有一个小技巧,就是进入到指定文件夹后,再运行notebook,这样notebook的路径就进入了想要的文件夹。 配置kernel 我们进入 chapter-1/02-notebook-demo.ipynb,点击run,可能会出现以下错误 --------------------------------------------------------------------------- ModuleNotFoundError Traceback (most recent call last) in 7 \"\"\" 8 ----> 9 import torch 10 11 print(\"Hello World, Hello PyTorch {}\".format(torch.__version__)) ModuleNotFoundError: No module named 'torch' 告诉我们找不到torch这个包,这很明显,使用的kernel不是我们的D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\python.exe。 下面我们来设置一下,方法很简单: 重新运行,将看到以下信息,表明notebook的环境就配置好了。 Hello World, Hello PyTorch 1.10.1+cu102 CUDA is available:True, version is 10.2 device_name: NVIDIA GeForce GTX 1660 Ti with Max-Q Design 实用插件——jupyter_contrib_nbextensions 原生的notebook还是缺点意思,这里推荐大家安装jupyter_contrib_nbextensions插件,jupyter_contrib_nbextensions提供了非常丰富的功能,例如代码折叠、分段折叠、代码自动补全、字体大小、行号显示、目录索引等等,详见下图 插件安装十分简单,打开命令窗,进入虚拟环境,分别依次执行 : pip install jupyter_contrib_nbextensions jupyter contrib nbextension install --user 然后重启notebook,就可以看到导航栏里有Nbextensions,大家可以根据自己的喜好进行调整,更多内容请查看Github Notebook 快速上手 notebook所使用的文件格式为.ipynb,jupyter会将.ipynb转为json进行保存,这样便于版本记录以及分享。 例如下图是用sublime打开的 02-notebook-demo.ipynb 下面,我们来研究notebook界面和常用的操作。 界面中需要认识的几个模块分别是:菜单栏、工具栏、单元格(cell) 菜单栏:用得最多的是Kernel,用于中断程序、重启解释器环境、切换解释器等;其它按键顾名思义。 工具栏:一些功能的按钮,高手都是用快捷键的。 单元格:这就是承载信息的地方,cell可分为code cells, markdown cells, raw cells。用得最多的是code cells和markdown cells。 右上角有一个小圆圈,用于观察当前kernel运行状态,如果是实心的,表明kernel正在运行某个cell,被运行的cell以及等待运行的cell的左边会有一个* notebook 的两种模式 Notebook中的单元,有两种模式:命令模式(Command Mode)与编辑模式(Edit Mode),在不同模式下我们可以进行不同的操作。 命令模式:cell的边框为蓝色,此时可对cell进行操作。在编辑模式下,按esc键进入命令模式。 编辑模式:cell的边框为绿色,此时可在单元格内编辑代码或文档。在命令模式下,按enter或return键进入编辑模式。 常用快捷键 在命令模式下,按下“h”键,就会弹出快捷键的介绍,但是太多了,不方便初学者使用,这里总结一些常用的,实用的快捷键供大家参考。 命令模式: 插入单元格: A 键上方插入,B 键在下方插入 合并单元格:选中多个单元格,Shift + M 显示行号:L 删除单元格:连续按两次D 剪切单元格:X。 通常我用X代替删除,毕竟只用按一个键,哈哈。 复制粘贴单元格: C/V 撤销删除的单元格:要撤消已删除的单元格,请按 Z 键 编辑模式: 运行单元格:Ctrl + Enter 运行并创建新单元格:Alt + Enter 分割单元格:光标放到想要分割的地方,Ctrl + Shift + - 函数详情:Shift+Tab (注意,要把模块导入才会提示函数详情!) 请大家将以上快捷键都试用一遍,这些是高频快捷键,下面给大家列举所有快捷键,请收藏。 下面再介绍两个神奇操作,分别是在单元格中执行shell命令以及magic操作。 请自行尝试!+shell命令进行体会。 magic commands Magic关键字是 IPython 的高级用法,如%matplotlib将matplolib设置为交互式 %和%%分别代表 行Magic命令 和 单元格Magic命令 演示一个魔法命令 %%timeit %%timeit a = [] for i in range(10): a.append(i) 858 ns ± 50.3 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) 表示将代码段运行了100万次,并统计运行时间。 推荐阅读 pycharm中使用jupyter notebook:Jupyter notebook support 更多的magic commands请看这里Jupyter 魔术命令(magic commands) 更多技巧推荐大家看看Jupyter Notebook 有哪些技巧? 更多官方信息请查看Jupyter Notebook docs Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-1/1.7-PyTorch2.0.html":{"url":"chapter-1/1.7-PyTorch2.0.html","title":"1.7 PyTorch 2.0 与 torch.compile","keywords":"","body":"1.7 PyTorch 2.0 与torch.compile 前言 由于本书开始撰写是2021年,当时pytorch 2.0还未发布。因此主要基于pytorch 1.12进行实践,近期随着LLM等新技术的发展,pytorch2.x应用越来越广泛,为此新增一篇pytorch 2.0的介绍, 并对torch.compile进行代码实验。 pytorch 2.0是2022年12月初在PyTorch Conference 2022 上正式宣布,距离上一个版本pytorch 1.13 的发布仅一个月。虽然早在2022年12月就宣布2.0,但稳定版式2023年3月才正式上线,截止2024年7月,pytorch版本已经发展到2.3。 为什么叫2.0?而不是延续1.13命名为1.14?因为本次更新有众多特性,并且更新内容繁多,相对于1.13.1,2.0有428位开发者贡献了4541个代码提交,可见2.0的更新内容庞大。 2.0 特性 参考官方发布文档,这里总结一下2.0带来的新特性。虽然发布了很多特性,但是对于入门/普通开发者而言,关系不大。pytorch从2017年发展到2022年已经过去五年,从最初的用户易用逐步向工程化、高性能方向迭代,因此后续的版本更多是针对规模化落地、大型AI基建、平台兼容性等方面。 所有的新功能点如下表所示: 其中最大的特点是torch.compile的发布,它是pytorch走向编译范式的重要模块,也是pytorch2.0发布会重点介绍的部分。 下面围绕torch.compile展开梳理几个与编译相关的核心特性。 特性一:torch.compile torch.complie目的是提高计算速度。通常使用只需要一行代码即可完成,例如:model = torch.compile(model)。 之所以一行能让整个pytorch运算提速,是因为complie是一个高级接口,它背后使用了 TorchDynamo、AOTAutograd 和TorchInductor 等工具链来对模型的计算图进行分析、优化和编译。 torch.complie是与开发者关系最大的、最有别于1.x的特性,它背后对于计算图的分析、优化和编译是本次更新的核心构成,但对于普通用户而言,了解好torch.compile的接口,了解其可提高模型计算速度就可以。 特性二:TorchDynamo TorchDynamo是支撑torch.compile的工具,它可进行快速地捕获计算图(Graph),计算图在深度学习中至关重要,它描述了数据在网络中的流动形式。在早期,pytorch团队已经对计算图的捕获进行了一些列工具开发,例如TorchScript。但TorchDynamo相较于之前的工具,在速度上有了更大提升,并且在99%的情况下都能正确、安全地获取计算图。 特性三:AOTAutograd AOTAutograd 的目的是希望在计算运行之前,捕获计算的反向传播过程,即“ahead of time Autograd”。AOTAutograd 通过重用和扩展PyTorch的现有自动微分系统,实现提高训练速度。 特性四:TorchInductor TorchInductor 是一个新的编译器后端,可以为多个硬件平台进行生成优化的代码,例如针对NVIDIA和AMD的GPU,使用OpenAI 的Triton语言(一门GPU编程语言,不是NVIDIA的推理框架)作为目标语言,针对CPU,可生成C++代码。由此可见,TorchInductor 能够为多种加速器和后端生成快速的代码。 特性五:PrimTorch PrimTorch 是将PyTorch底层操作符(operators)进行归约、精简,使下游编译器开发更容易和高效。PyTorch包含1200+操作符,算上重载,有2000+,操作符过多,对于后端和编译器开发式不友好的。为了简化后端开发,提高效率,PrimTorch项目整理了两大类基础操作符,包括: Prim操作符:相对底层的约250个操作符 ATen操作符:约750个操作符,适合直接导出 小结:TorchDynamo、AOTAutograd、TorchInductor和PrimTorch 都在为PyTorch的计算效率服务,让PyTorch计算速度更快、更pythonic。 对于普通用户,重点关注torch.compile的接口使用,接下来将对torch.compile的概念和使用展开说明。 torch.compile 效果 得益于多个模块的优点组合,compile模式对大部分模型均有加速效果,下面看看官方给出的效果对比。 实验配置如下 采用A100 GPU, 采用46个HuggingFace Transformers模型、61个TIMM模型、56个TorchBench模型 采用AMP和float32两种数据精度,最终数据采用加权平均计算,0.75 AMP + 0.25 float32 程序仅采用compile进行包装,不修改任何代码 结果表明,93%的模型都可以采用torch.compile, 并且有43%的训练速度提升,其中AMP速度有51%提升,float32有21%的提升。 在三个模型库中,加速情况如下图所示: torch.compile 接口 根据官方文档定义,\"Optimizes given model/function using TorchDynamo and specified backend.\" torch.compile是采用TorchDynamo和指定的后端对模型/计算进行优化,期望使模型/函数在未来应用时,计算速度更快。 使用上,torch.compile接收一个可调用对象(Callable), 返回一个可调用对象(Callable),对于用户,只需要一行代码,调用compile进行优化。 参数: - model (Callable) : Module或者是Function, 这个Function可以是pytorch的函数,也可以是numpy语句,当前compile还支持numpy的加速优化 - mode:优化模式的选择,目前(2024年7月16日)提供了四种模式,区别在于不同的存储消耗、时间消耗、性能之间的权衡。 default : 默认模式, 在性能和开销之间有不错的平衡 reduce-overhead:这个模式旨在减少使用CUDA图时的Python开销。该模式会增加内存占用,提高速度,并且不保证总是有效。目前,这种方法只适用于那些不改变输入的CUDA图。 max-autotune:基于Triton的矩阵乘法和卷积来提高性能 max-autotune-no-cudagraphs: 与max-autotune一样,但是不会使用CUDA计算图 - fullgraph (bool) : 是否将整个对象构建为单个图(a single graph),否认是False,即根据compile的机制拆分为多个子图。 - dynamic (bool or None) : 是否采用动态形状追踪,默认为None,对于输入形状是变化的,compile会尝试生成对应的kernel来适应动态形状,从而减少重复编译,但并不是所有动态形状都能这样操作,随缘吧,这个过程可以设置TORCH_LOGS=dynamic来观察日志信息。 - backend (str or Callable) : 选择所用的后端,默认是\"inductor\",可以较好平衡性能和开销,可用的后端可以通过torch._dynamo.list_backends()查看,注册自定义后端库,可参考 https://pytorch.org/docs/main/compile/custom-backends.html - options (dict): 用于向后端传入额外数据信息,key-value可以自定义,只要后端可读取即可,这个参数预留了较好的接口。 - disable (bool): Turn torch.compile() into a no-op for testing torch.compile 实验 为了充分观察compile带来的速度变化,以及不同mode之间的影响,下面针对四种情况分别进行速度的观察。 四种情况包括:简单的pytorch运算, Resnet18, BERT, numpy计算。 并在三种型号GPU进行了测试,分别是RTX 4060 Laptop GPU 、L20、H20。 配套代码位于github,注意,torch.compile目前仅支持与Linux系统,并且不支持python≥3.12。 结论: 常见模型均有10-30%的耗时降低 numpy也可以用torch.compile加速,并且耗时降低高达90% 简单的pytorch计算并无法带来速度提升(应该是没有复杂的计算图,无优化空间了 RTX 4060 (8GB) Sin+Cos Resnet18 BERT numpy baseline 1.33 0.92 0.59 94.31 default 3.26(-144.37%) 0.70(23.56%) 0.23(61.04%) 6.52(93.09%) reduce-overhead 5.02(-272.92%) 0.83(20.99%) 0.42(18.11%) 6.26(93.17%) max-autotune 5.16(-269.47%) 0.84(20.07%) 0.44(19.62%) 6.55(92.93%) default 编译耗时 0.45 0 0 0 reduce-overhead 编译耗时 0 0 0 0 max-autotune 编译耗时 0 0 0 0 由于L20与H20服务器无法联网,于是提出BERT模型的测试 L20 (48GB) Sin+Cos Resnet18 numpy baseline 1.34 0.23 1.02 default 2.27 0.21 0.05 reduce-overhead 2.27 0.21 0.05 max-autotune 2.28 0.21 0.05 H20(96GB) Sin+Cos Resnet18 numpy baseline 1.03 0.25 0.79 default 1.74 0.18 0.04 reduce-overhead 1.75 0.18 0.04 max-autotune 1.75 0.18 0.04 小结 pytorch 2.0 进行了多个功能的更新,与普通用户关系最大的是compile编译功能,本小节对compile接口的入参进行分析,并且针对四种不同的modle/function进行了编译对比,对于常用的模型,compile还是可以得到一定计算速度的提升,值得大家尝试。 其中numpy可以降低90%的耗时,效果十分惊艳,这块似乎通过FAQs中发现的功能,建议可以进一步了解: https://pytorch.org/docs/stable/torch.compiler_faq.html# https://pytorch.org/docs/stable/torch.compiler.html#torch-compiler Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/":{"url":"chapter-2/","title":"第二章 PyTorch 核心模块","keywords":"","body":"第二章 PyTorch 核心模块 第二章 PyTorch 核心模块 2.1 PyTorch 模块结构 2.2 新冠肺炎分类 2.3 核心数据结构——Tensor 2.4 张量的相关函数 2.5 自动求导核心——计算图 2.6 Autograd——自动微分 在第一章中,对PyTorch框架进行了全面的介绍,从它的起源、特性到安装和基本使用。现在,我们已经为深入探索PyTorch的核心模块做好了准备。第二章将带领读者深入了解PyTorch核心——它的数据结构、自动求导机制以及如何构建和训练神经网络模型。 本章内容预告 2.1 PyTorch 模块结构 首先,我们将探索PyTorch的模块结构,了解其代码的组织方式和各个模块的功能。这不仅有助于我们更好地理解PyTorch的工作原理,还能帮助我们在后续的开发中快速定位和使用所需的功能。 2.2 新冠肺炎X光分类 通过一个与现实世界紧密相关的案例——新冠肺炎X光分类,我们将介绍PyTorch在实际深度学习项目中的应用。本小节将通过一个具体的项目流程,展示如何使用PyTorch构建数据加载、模型训练和评估的完整工作流。 2.3 核心数据结构——Tensor Tensor作为PyTorch中的核心数据结构,是理解和使用PyTorch的关键。本小节将详细介绍Tensor的概念、属性和操作。 2.4 张量的相关函数 在对Tensor有了基本了解之后,我们将学习张量的各种相关函数,包括它们的数学操作、随机采样、序列化等。这些函数是构建和操作神经网络模型的必备工具。 2.5 自动求导核心——计算图 自动求导是深度学习框架的精髓。本小节将介绍计算图的概念,它是自动求导系统的基石。通过计算图,我们将能够理解如何自动地计算神经网络中的梯度。 2.6 Autograd——自动微分 最后,我们将深入探讨Autograd模块,它是PyTorch自动求导机制的实现。通过本小节的学习,我们将掌握如何利用Autograd进行复杂的梯度计算,从而优化神经网络模型。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.1-module-tree.html":{"url":"chapter-2/2.1-module-tree.html","title":"2.1 PyTorch 模块结构","keywords":"","body":"2.1 PyTorch 模块结构 上一章安装好的PyTorch是一个庞大的python库,其中包含几十个模块,这一小节就来了解模块的构成,模块代码的位置,对应文档的位置。从而帮助读者具象化PyTorch,清楚地知道所用的PyTorch函数、模块都在哪里,是如何调用的。 您的代码位于何处? 很多开发者都用过pip/conda install 进行一键安装,但可能不清楚工具库代码安装在了哪里。使用的时候只知道import *,但具体引用的功能函数又是如何实现的,是模糊的。 为了让大家知道调用的是什么,先来看安装的pytorch在哪里。上一章案例中,我们装的pytorch在:D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch 如果pycharm中配置好了虚拟环境,大家也可以通过pycharm的快捷键,快速定位到这个文件夹。方法是,找到import torch这一行代码,按住Ctrl键,鼠标左键单击torch,就可以跳转到D:\\Anacondadata\\envs\\pytorch1.10_gpu\\Lib\\site-packages\\torch__init.py 文件。 可以看到torch文件夹中有一系列子文件夹,我们平时常用的函数都包含在这些子文件夹中,下面将重点介绍一些。 _pycache_ 该文件夹存放python解释器生成的字节码,后缀通常为pyc/pyo。其目的是通过牺牲一定的存储空间来提高加载速度,对应的模块直接读取pyc文件,而不需再次将.py语言转换为字节码的过程,从此节省了时间。 从文件夹名称可知,它是一个缓存,如果需要,我们当然可以删掉它。更多关于pycache的内容,建议额外阅读:https://www.python.org/dev/peps/pep-3147/#proposal _C 从文件夹名称就知道它和C语言有关,其实它是辅助C语言代码调用的一个模块,该文件夹里存放了一系列pyi文件,pyi文件是python用来校验数据类型的,如果调用数据类型不规范,会报错。更多pyi知识,请查阅PEP 8 -->.pyi files that are read by the type checker in preference of the corresponding .py files. PyTorch的底层计算代码采用的是C++语言编写,并封装成库,供pytorch的python语言进行调用。这一点非常重要,后续我们会发现一些pytorch函数无法跳转到具体实现,这是因为具体的实现通过C++语言,我们无法在Pycharm中跳转查看。 include 上面讲到pytorch许多底层运算用的是C++代码,那么C++代码在哪里呢? 它们在这里,在torch/csrc文件夹下可以看到各个.h/.hpp文件,而在python库中,只包含头文件,这些头文件就在include文件夹下。 lib torch文件夹中最重要的一个模块,torch文件夹占3.2GB的空间,98%的内容都在lib中,占了3.16GB空间。pytorch的内容几乎都在lib里面,让我们看看里面是什么。 lib文件夹下包含大量的.lib .dll文件(分别是静态链接库和动态链接库),例如大名鼎鼎的cudnn64_7.dll(占435MB), torch_cuda.dll(940MB)。这些底层库都会被各类顶层python api调用。这里推荐大家自行了解什么是静态链接库和动态链接库。 autograd 该模块是pytorch的核心模块与概念,它实现了梯度的自动求导,极大地简化了深度学习研究者开发的工作量,开发人员只需编写前向传播代码,反向传播部分由autograd自动实现,再也不用手动去推导数学公式,然后编写代码了(很多朋友可能不知道,在早期的深度学习框架中是没有这个功能的,例如caffe,它需要手动编写反向传播的公式代码) nn 相信这个模块是99%pytorch开发者使用频率最高的模块,搭建网络的网络层就在nn.modules里边。nn.modules也将作为一章独立展开。我们可以到D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch\\nn\\modules里面看看是否有你熟悉的网络层? onnx pytorch模型转换到onnx模型表示的核心模块,进入文件夹可以看到大量的opset**.py, 这里留下一个问题,各版本opset是什么意思?有什么区别? optim 优化模块,深度学习的学习过程,就是不断的优化,而优化使用的方法函数,都暗藏在了optim文件夹中,进入该文件夹,可以看到熟悉的优化方法:“Adam”、“SGD”、“ASGD”等。以及非常重要的学习率调整模块,lr_scheduler.py。该模块也将采用独立一章进行详细剖析。 utils utils是各种软件工程中常见的文件夹,其中包含了各类常用工具,其中比较关键的是data文件夹,tensorboard文件夹,这些工具都将在后续章节详细展开。第三章将展开data里的dataloader与dataset等数据读取相关的模块。 其他文件夹不再逐一介绍,读者可以到官方文档查看。 以上是torch库,针对不同的应用方向,pytorch还提供了torchvision\\torchtext\\torchaudio等模块,本书重点对torchvision进行剖析,其它两个模块类似。 torchvision 类似地,我们来到D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision文件夹下看看有什么模块。 datasets 这里是官方为常用的数据集写的数据读取函数,例如常见的cifar, coco, mnist,svhn,voc都是有对应的函数支持,可以方便地使用轮子,同时也可以学习大牛们是如何写dataset的。 models 这里是宝藏库,里边存放了经典的、可复现的、有训练权重参数可下载的视觉模型,例如分类的alexnet、densenet、efficientnet、mobilenet-v1/2/3、resnet等,分割模型、检测模型、视频任务模型、量化模型。这个库中的模型实现,也是大家可以借鉴学习的好资料,可以模仿它们的代码结构、函数、类的组织。 ops 视觉任务特殊的功能函数,例如检测中用到的 roi_align, roi_pool,boxes的生成,以及focal_loss实现,都在这里边有实现。 transforms 数据增强库,相信99%的初学者用到的第一个视觉数据增强库就是transforms了,transforms是pytorch自带的图像预处理、增强、转换工具,可以满足日常的需求。但无法满足各类复杂场景,因此后续会介绍更强大的、更通用的、使用人数更多的数据增强库——Albumentations。 通过torchvision\\transforms\\transforms.py , 可以看到 torchvision包含了这些功能 __all__ = [\"Compose\", \"ToTensor\", \"PILToTensor\", \"ConvertImageDtype\", \"ToPILImage\", \"Normalize\", \"Resize\", \"Scale\", \"CenterCrop\", \"Pad\", \"Lambda\", \"RandomApply\", \"RandomChoice\", \"RandomOrder\", \"RandomCrop\", \"RandomHorizontalFlip\", \"RandomVerticalFlip\", \"RandomResizedCrop\", \"RandomSizedCrop\", \"FiveCrop\", \"TenCrop\", \"LinearTransformation\", \"ColorJitter\", \"RandomRotation\", \"RandomAffine\", \"Grayscale\", \"RandomGrayscale\", \"RandomPerspective\", \"RandomErasing\", \"GaussianBlur\", \"InterpolationMode\", \"RandomInvert\", \"RandomPosterize\", \"RandomSolarize\", \"RandomAdjustSharpness\", \"RandomAutocontrast\", \"RandomEqualize\"] 通过上面的内容,相信大家对所安装的代码结构有了清晰认识,也知道自己将调用的代码函数都在哪里,已经为下一步工作打好基础,下一节我们极简的代码,完成第一个深度学习任务—— 新冠肺炎X光分类 。其目的在于搭建模型训练框架,加深对各模块的理解,为后续对核心模块的讲解打下基础。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.2-covid-19-cls.html":{"url":"chapter-2/2.2-covid-19-cls.html","title":"2.2 新冠肺炎分类","keywords":"","body":"2.2 新冠肺炎X光分类 上一节,我们学习了pytorch python API的结构,本节将以一个具体的案例介绍pytorch模型训练流程,并提出一系列问题,供大家思考。当然,这些问题也是本书后续章节一一解答的内容。 相信绝大多数朋友接触过或者看到的第一个Hello World级图像分类都是Mnist,思来想去觉得还是换点东西,于是选择了当下与所有人都息息相关的案例——新型冠状病毒肺炎(Corona Virus Disease 2019,COVID-19),简称“新冠肺炎”。关于新冠肺炎的背景,大家很熟悉了,口罩、绿码、核酸检测已经融入了我们的生活。因此,想让大家更进一步的了解COVID-19,所以选用此案例。当然,最重要的目的是要了解pytorch如何完成模型训练。 案例背景 2020年1月底2月初的时候,新冠在国内/外大流行。而确定一个人是否患有新冠肺炎,是尤为重要的事情。新冠肺炎的确诊需要通过核酸检测完成,但是核酸检测并不是那么容易完成,需要医护人员采样、送检、在PCR仪器上进行检测、出结果、发报告等一系列复杂工序,核酸检测产能完全达不到当时的检测需求。在疫情初期,就有医生提出,是否可以采用特殊方法进行诊断,例如通过CT、X光的方法,给病人进行X光摄影,几分钟就能看出结果,比核酸检测快了不少。于是,新冠肺炎患者的胸片X光数据就不断的被收集,并发布到网上供全球科学家使用,共同抗击新冠疫情。这里就采用了https://github.com/ieee8023/covid-chestxray-dataset上的数据,同时采用了正常人的X光片,来自于:https://github.com/zoogzog/chexnet。 由于本案例目的是pytorch流程学习,为了简化学习过程,数据仅选择了4张图片,分为2类,正常与新冠,训练集2张,验证集2张。标签信息存储于TXT文件中。具体目录结构如下: ├─imgs │ ├─covid-19 │ │ auntminnie-a-2020_01_28_23_51_6665_2020_01_28_Vietnam_coronavirus.jpeg │ │ ryct.2020200028.fig1a.jpeg │ │ │ └─no-finding │ 00001215_000.png │ 00001215_001.png │ └─labels train.txt valid.txt 建模思路 这是一个典型的图像分类任务,此处采用面向过程的思路给大家介绍如何进行代码编写。 step 1 数据 需要编写代码完成数据的读取,转换成模型能够读取的格式。这里涉及pytorch的dataset,dataloader,transforms等模块。以及需要清楚地知道pytorch的模型需要怎样的格式。 数据模块需要完整的工作大体如下图所示: 首先,需要将数据在硬盘上的信息,如路径,标签读取并存储起来,然后被使用,这一步骤主要是通过COVID19Dataset这个类。类里有四个函数,除了Dataset类必须要实现的三个外,我们通过get_img_info()函数实现读取硬盘中的路径、标签等信息,并存储到一个列表中。后续大家可以根据不同的任务情况在这个函数中修改,只要能获取到数据的信息,供\\_getitem__函数进行读取。 接着,使用dataloader进行封装,dataloader是一个数据加载器,提供诸多方法进行数据的获取,如设置一个batch获取几个样本,采用几个进程进行数据读取,是否对数据进行随机化等功能。 下一步,还需要设置对图像进行预处理(Preprocess)的操作,这里为了演示,仅采用resize 和 totensor两个方法,并且图片只需要缩放到8x8的大小,并不需要224,256,448,512,1024等大尺寸。(totensor与下一小节内容强相关) step 2 模型 数据模块构建完毕,需要输入到模型中,所以我们需要构建神经网络模型,模型接收数据并前向传播处理,输出二分类概率向量。此处需要用到nn.Module模块和nn下的各个网络层进行搭建模型,模型的搭建就像搭积木,一层一层地摞起来。模型完成的任务就如下图所示:下图示意图是一张分辨率为4x4的图像输入到模型中,模型经过运算,输出二分类概率。中间的“?\"是什么内容呢? 这里,“?”是构建一个极其简单的卷积神经网络,仅仅包含两个网络层,第一层是包含1个3*3卷积核的2d卷积,第二层是两个神经元的全连接层(pytorch也叫linear层)。模型的输入被限制在了8x8,原因在于linear层设置了输入神经元个数为36, 8x8与36之间是息息相关的,他们之间的关系是什么?这需要大家对卷积层有一定了解。(大家可以改一下36,改为35,或者transforms_func()中的resize改为9x9,看看会报什么错误,这个错误是经常会遇到的报错) step3 优化 模型可以完成前向传播之后,根据什么规则对模型的参数进行更新学习呢?这就需要损失函数和优化器的搭配了,损失函数用于衡量模型输出与标签之间的差异,并通过反向传播获得每个参数的梯度,有了梯度,就可以用优化器对权重进行更新。这里就要涉及各种LossFunction和optim中的优化器,以及学习率调整模块optim.lr_scheduler。 这里,采用的都是常用的方法:交叉熵损失函数(CrossEntropyLoss)、随机梯度下降法(SGD)和按固定步长下降学习率策略(StepLR)。 step4 迭代 有了模型参数更新的必备组件,接下来需要一遍又一遍地给模型喂数据,监控模型训练状态,这时候就需要for循环,不断地从dataloader里取出数据进行前向传播,反向传播,参数更新,观察loss、acc,周而复始。当达到条件,如最大迭代次数、某指标达到某个值时,进行模型保存,并break循环,停止训练。 以上就是一个经典的面向过程式的代码编写,先考虑数据怎么读进来,读进来之后喂给的模型如何搭建,模型如何更新,模型如何迭代训练到满意。请大家结合代码一步一步的观察整体过程。 在经过几十个epoch的训练之后达到了100%,模型可以成功区分从未见过的两张图片:auntminnie-a-2020_01_28_23_51_6665_2020_01_28_Vietnam_coronavirus.jpeg,00001215_000.png。 由于数据量少,随机性非常大,大家多运行几次,观察结果。不过本案例结果完全不重要!),可以看到模型的准确率(Accuracy)变化。 一系列问题 通过上述步骤及代码,虽然完成了一个图像分类任务,但其中很多细节想必大家还是弄不清楚,例如: 图像数据是哪用一行代码读取进来的? transforms.Compose是如何工作对图像数据进行转换的? ToTensor又有哪些操作? 自己如何编写Dataset? DataLoader有什么功能?如何使用?有什么需要注意的? 模型如何按自己的数据流程搭建? nn有哪些网络层可以调用? 损失函数有哪些? 优化器是如何更新model参数的? 学习率调整有哪些方法?如何设置它们的参数? model.train()与model.eval()作用是什么? optimizer.zero_grad()是做什么?为什么要梯度清零? scheduler.step() 作用是什么?应该放在哪个for循环里? 等等 如果大家能有以上的问题提出,本小节的目的就达到了。大家有了模型训练的思路,对过程有了解,但是使用细节还需进一步学习,更多pytorch基础内容将会在后续章节一一解答。 下一小节我们将介绍流动在pytorch各个模块中的基础数据结构——Tensor(张量)。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.3-datastruct-tensor.html":{"url":"chapter-2/2.3-datastruct-tensor.html","title":"2.3 核心数据结构——Tensor","keywords":"","body":"2.3 核心数据结构——Tensor 张量初认识 经过前两小节的铺垫,大家一定对pytorch有了初步认识,本小节就展开讲pytorch的核心数据结构——Tensor(张量)。Tensor 中文被翻译为张量。张量在不同学科中有不同的意义,在深度学习中张量表示的是一个多维数组,它是标量、向量、矩阵的拓展。标量是零维张量,向量是一维张量,矩阵是二维张量,一个RGB图像的数组就是一个三维张量,第一维是图像的高,第二维是图像的宽,第三维是图像的颜色通道。 在pytorch中,有两个张量的相关概念极其容易混淆,分别是torch.Tensor和torch.tensor。其实,通过命名规范,可知道torch.Tensor是Python的一个类, torch.tensor是Python的一个函数。通常我们调用torch.tensor进行创建张量,而不直接调用torch.Tensor类进行创建。为了进一步区分两者,我们来看看它们代码实现。 torch.Tensor:类定义与torch/_tensor.py#L80,它继承torch._C._TensorBase,这里看到_C就知道要接触C++代码了。 跳转到torch/C/\\_init__.pyi #L839 可以看到: # Defined in torch/csrc/autograd/python_variable.cpp class _TensorBase(metaclass=_TensorMeta): requires_grad: _bool shape: Size 张量的定义和底层C++实现是在python_variable.cpp代码中,感兴趣的朋友可以进一步探究。 torch.tensor:pytorch的一个函数,用于将数据变为张量形式的数据,例如list, tuple, NumPy ndarray, scalar等。 同样,torch.tensor的底层实现也是C++代码,具体实现位于torch_C_VariableFunctions.pyi文件。如2.1节所述,.pyi文件用于Python类型检查,而其底层实现在对应的C++代码中。 后续将不再区分Tensor和tensor,主要用小写tensor表示张量这个数据类型(数据结构)。 张量的作用 tensor之于pytorch等同于ndarray之于numpy,它是pytorch中最核心的数据结构,用于表达各类数据,如输入数据、模型的参数、模型的特征图、模型的输出等。这里边有一个很重要的数据,就是模型的参数。对于模型的参数,我们需要更新它们,而更新操作需要记录梯度,梯度的记录功能正是被张量所实现的(求梯度是autograd实现的)。 张量的历史演变 讲tensor结构之前,还需要介绍一小段历史,那就是Variable与Tensor。在0.4.0版本之前,Tensor需要经过Variable的包装才能实现自动求导。从0.4.0版本开始,torch.Tensor与torch.autograd.Variable合并,torch.Tensor拥有了跟踪历史操作的功能。虽然Variable仍可用,但Variable返回值已经是一个Tensor(原来返回值是Variable),所以今后无需再用Variable包装Tensor。 虽然Variable的概念已经被摒弃,但是了解其数据结构对理解Tensor还是有帮助的。Variable不仅能对Tensor包装,而且能记录生成Tensor的运算(这是自动求导的关键)。在Variable类中包含5个属性:data,grad,grad_fn,is_leaf,requires_grad data: 保存的是具体数据,即被包装的Tensor; grad: 对应于data的梯度,形状与data一致; grad_fn: 记录创建该Tensor时用到的Function,该Function在反向传播计算中使用,因此是自动求导的关键; requires_grad: 指示是否计算梯度; is_leaf: 指示节点是否为叶子节点,为叶子结点时,反向传播结束,其梯度仍会保存,非叶子结点的梯度被释放,以节省内存。 从Variable的主要属性中可以发现,除了data外,grad,grad_fn,is_leaf和requires_grad都是为计算梯度服务,所以Variable在torch.autogard包中自然不难理解。 但是我们的数据载体是tensor,每次需要自动求导,都要用Variable包装,这明显太过繁琐,于是PyTorch从0.4.0版将torch.Tensor与torch.autograd.Variable合并。 张量的结构 tensor是一个类,我们先来认识它有哪些属性,再去观察它有哪些方法函数可使用。 Tensor主要有以下八个主要属性,data,dtype,shape,device,grad,grad_fn,is_leaf,requires_grad。 data:多维数组,最核心的属性,其他属性都是为其服务的; dtype:多维数组的数据类型,tensor数据类型如下,常用到的三种已经用红框标注出来; shape:多维数组的形状; device: tensor所在的设备,cpu或cuda; grad,grad_fn,is_leaf和requires_grad就与Variable一样,都是梯度计算中所用到的。 张量的属性还有很多,大家可以通过Pycharm的debug功能进行查看 更多关于张量的概念背景,请查看官方文档,下一小节,我们进行张量的操作介绍。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.4-method-tensor.html":{"url":"chapter-2/2.4-method-tensor.html","title":"2.4 张量的相关函数","keywords":"","body":"2.4 张量的相关函数 接下来开始学习各类张量的api,主要参考官方文档,通过右边目录栏可以看出有以下几个部分。 torchTensors Generators Random sampling Serialization Parallelism Locally disabling gradient computation Math operations Utilities 里面有上百个函数,这里只挑高频使用的进行讲解,建议大家自行浏览一遍官方文档,看看都有哪些功能,便于今后使用到的时候不必重复造轮子。 张量的创建 直接创建 torch.tensor torch.tensor(data, dtype=None, device=None, requires_grad=False, pin_memory=False) data(array_like) - tensor的初始数据,可以是list, tuple, numpy array, scalar或其他类型。 dtype(torch.dtype, optional) - tensor的数据类型,如torch.uint8, torch.float, torch.long等 device (torch.device, optional) – 决定tensor位于cpu还是gpu。如果为None,将会采用默认值,默认值在torch.set_default_tensor_type()中设置,默认为 cpu。 requires_grad (bool, optional) – 决定是否需要计算梯度。 pin_memory (bool, optional) – 是否将tensor存于锁页内存。这与内存的存储方式有关,通常为False。 import torch import numpy as np l = [[1., -1.], [1., -1.]] t_from_list = torch.tensor(l) arr = np.array([[1, 2, 3], [4, 5, 6]]) t_from_array = torch.tensor(arr) print(t_from_list, t_from_list.dtype) print(t_from_array, t_from_array.dtype) tensor([[ 1., -1.], ​ [ 1., -1.]]) torch.float32 tensor([[1, 2, 3], ​ [4, 5, 6]]) torch.int64 可以看到t_from_list是float32类型,而t_from_array是int64类型。如果想让tensor是其他数据类型,可以在创建tensor时使用dtype参数确定数据类型。 import torch import numpy as np arr = np.array([[1, 2, 3], [4, 5, 6]]) t_from_array = torch.tensor(arr, dtype=torch.uint8) print(t_from_array) tensor([[1, 2, 3], ​ [4, 5, 6]], dtype=torch.uint8) torch.from_numpy 还有一种常用的通过numpy创建tensor方法是torch.from_numpy()。这里需要特别注意的是,创建的tensor和原array共享同一块内存(The returned tensor and ndarray share the same memory. ),即当改变array里的数值,tensor中的数值也会被改变。 import torch import numpy as np arr = np.array([[1, 2, 3], [4, 5, 6]]) t_from_numpy = torch.from_numpy(arr) print(\"numpy array: \", arr) print(\"tensor : \", t_from_numpy) print(\"\\n修改arr\") arr[0, 0] = 0 print(\"numpy array: \", arr) print(\"tensor : \", t_from_numpy) print(\"\\n修改tensor\") t_from_numpy[0, 0] = -1 print(\"numpy array: \", arr) print(\"tensor : \", t_from_numpy) > > numpy array: [[1 2 3] [4 5 6]] tensor : tensor([[1, 2, 3], ​ [4, 5, 6]]) 修改arr numpy array: [[0 2 3] [4 5 6]] tensor : tensor([[0, 2, 3], ​ [4, 5, 6]]) 修改tensor numpy array: [[-1 2 3] [ 4 5 6]] tensor : tensor([[-1, 2, 3], ​ [ 4, 5, 6]]) 可以看到虽然只改变了arr的值,但是tensor中的data也被改变了,这一点在使用过程中需要注意。 依数值创建 torch.zeros torch.zeros(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:依给定的size创建一个全0的tensor,默认数据类型为torch.float32(也称为torch.float)。 主要参数: layout(torch.layout, optional) - 参数表明张量在内存中采用何种布局方式。常用的有torch.strided, torch.sparse_coo等。 out(tensor, optional) - 输出的tensor,即该函数返回的tensor可以通过out进行赋值,请看例子。 example: import torch o_t = torch.tensor([1]) t = torch.zeros((3, 3), out=o_t) print(t, '\\n', o_t) print(id(t), id(o_t)) > > tensor([[0, 0, 0], ​ [0, 0, 0], ​ [0, 0, 0]]) tensor([[0, 0, 0], ​ [0, 0, 0], ​ [0, 0, 0]]) 4925603056 4925603056 可以看到,通过torch.zeros创建的张量不仅赋给了t,同时赋给了o_t,并且这两个张量是共享同一块内存,只是变量名不同。 torch.zeros_like torch.zeros_like(input, dtype=None, layout=None, device=None, requires_grad=False) 功能:依input的size创建全0的tensor。 主要参数: input(Tensor) - 创建的tensor与intput具有相同的形状。 example: import torch t1 = torch.tensor([[1., -1.], [1., -1.]]) t2 = torch.zeros_like(t1) print(t2) tensor([[0., 0.], ​ [0., 0.]]) 除了创建全0还有创建全1的tensor,使用方法是一样的,这里就不赘述。 torch.ones(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:依给定的size创建一个全1的tensor。 torch.ones_like(input, dtype=None, layout=None, device=None, requires_grad=False) 功能:依input的size创建全1的tensor。 torch.full(size, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:依给定的size创建一个值全为fill_value的tensor。 主要参数: siz (int...) - tensor的形状。 fill_value - 所创建tensor的值 out(tensor, optional) - 输出的tensor,即该函数返回的tensor可以通过out进行赋值。 example: import torch print(torch.full((2, 3), 3.141592)) torch.full_like(input, fill_value, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) torch.full_like之于torch.full等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.arange(start=0, end, step=1, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:创建等差的1维张量,长度为 (end-start)/step,需要注意数值区间为[start, end)。 主要参数: start (Number) – 数列起始值,默认值为0。the starting value for the set of points. Default: 0. end (Number) – 数列的结束值。 step (Number) – 数列的等差值,默认值为1。 out (Tensor, optional) – 输出的tensor,即该函数返回的tensor可以通过out进行赋值。 example: import torch print(torch.arange(1, 2.51, 0.5)) torch.range()函数就不推荐了,因为官网说了“This function is deprecated in favor of torch.arange().” torch.linspace(start, end, steps=100, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:创建均分的1维张量,长度为steps,区间为[start, end]。 主要参数: start (float) – 数列起始值。 end (float) – 数列结束值。 steps (int) – 数列长度。 example: print(torch.linspace(3, 10, steps=5)) print(torch.linspace(1, 5, steps=3)) torch.logspace(start, end, steps=100, base=10.0, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:创建对数均分的1维张量,长度为steps, 底为base。 主要参数: start (float) – 确定数列起始值为base^start end (float) – 确定数列结束值为base^end steps (int) – 数列长度。 base (float) - 对数函数的底,默认值为10,此参数是在pytorch 1.0.1版本之后加入的。 example: torch.logspace(start=0.1, end=1.0, steps=5) torch.logspace(start=2, end=2, steps=1, base=2) torch.eye(n, m=None, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False)** 功能:创建单位对角矩阵。 主要参数: n (int) - 矩阵的行数 m (int, optional) - 矩阵的列数,默认值为n,即默认创建一个方阵 example: import torch print(torch.eye(3)) print(torch.eye(3, 4)) torch.empty(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False, pin_memory=False) 功能:依size创建“空”张量,这里的“空”指的是不会进行初始化赋值操作。 主要参数: size (int...) - 张量维度 pin_memory (bool, optional) - pinned memory 又称page locked memory,即锁页内存,该参数用来指示是否将tensor存于锁页内存,通常为False,若内存足够大,建议设置为True,这样在转到GPU时会快一些。 torch.empty_like(input, dtype=None, layout=None, device=None, requires_grad=False) 功能:torch.empty_like之于torch.empty等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.empty_strided(size, stride, dtype=None, layout=None, device=None, requires_grad=False, pin_memory=False) 功能:依size创建“空”张量,这里的“空”指的是不会进行初始化赋值操作。 主要参数: stride (tuple of python:ints) - 张量存储在内存中的步长,是设置在内存中的存储方式。 size (int...) - 张量维度 pin_memory (bool, optional) - 是否存于锁页内存。 依概率分布创建 torch.normal(mean, std, out=None) 功能:为每一个元素以给定的mean和std用高斯分布生成随机数 主要参数: mean (Tensor or Float) - 高斯分布的均值, std (Tensor or Float) - 高斯分布的标准差 特别注意事项: mean和std的取值分别有2种,共4种组合,不同组合产生的效果也不同,需要注意 mean为张量,std为张量,torch.normal(mean, std, out=None),每个元素从不同的高斯分布采样,分布的均值和标准差由mean和std对应位置元素的值确定; mean为张量,std为标量,torch.normal(mean, std=1.0, out=None),每个元素采用相同的标准差,不同的均值; mean为标量,std为张量,torch.normal(mean=0.0, std, out=None), 每个元素采用相同均值,不同标准差; mean为标量,std为标量,torch.normal(mean, std, size, *, out=None) ,从一个高斯分布中生成大小为size的张量; 案例1 import mean = torch.arange(1, 11.) std = torch.arange(1, 0, -0.1) normal = torch.normal(mean=mean, std=std) print(\"mean: {}, \\nstd: {}, \\nnormal: {}\".format(mean, std, normal)) mean: tensor([ 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]), std: tensor([1.0000, 0.9000, 0.8000, 0.7000, 0.6000, 0.5000, 0.4000, 0.3000, 0.2000, ​ 0.1000]), normal: tensor([ 1.3530, -1.3498, 3.0021, 5.1200, 3.9818, 5.0163, 6.9272, 8.1171, ​ 9.0623, 10.0621]) 1.3530是通过均值为1,标准差为1的高斯分布采样得来, -1.3498是通过均值为2,标准差为0.9的高斯分布采样得来,以此类推 torch.rand(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:在区间[0, 1)上,生成均匀分布。 主要参数: size (int...) - 创建的张量的形状 torch.rand_like(input, dtype=None, layout=None, device=None, requires_grad=False) torch.rand_like之于torch.rand等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.randint(low=0, high, size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:在区间[low, high)上,生成整数的均匀分布。 主要参数: low (int, optional) - 下限。 high (int) – 上限,主要是开区间。 size (tuple) – 张量的形状。 example print(torch.randint(3, 10, (2, 2))) torch.randint_like(input, low=0, high, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:torch.randint_like之于torch.randint等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.randn(*size, out=None, dtype=None, layout=torch.strided, device=None, requires_grad=False) 功能:生成形状为size的标准正态分布张量。 主要参数: size (int...) - 张量的形状 torch.randn_like(input, dtype=None, layout=None, device=None, requires_grad=False) 功能:torch.rafndn_like之于torch_randn等同于torch.zeros_like之于torch.zeros,因此不再赘述。 torch.randperm(n, out=None, dtype=torch.int64, layout=torch.strided, device=None, requires_grad=False) 功能:生成从0到n-1的随机排列。perm == permutation torch.bernoulli(input, *, generator=None, out=None) 功能:以input的值为概率,生成伯努力分布(0-1分布,两点分布)。 主要参数: input (Tensor) - 分布的概率值,该张量中的每个值的值域为[0-1] example: import torch p = torch.empty(3, 3).uniform_(0, 1) b = torch.bernoulli(p) print(\"probability: \\n{}, \\nbernoulli_tensor:\\n{}\".format(p, b)) probability: tensor([[0.7566, 0.2899, 0.4688], ​ [0.1662, 0.8341, 0.9572], ​ [0.6060, 0.4685, 0.6366]]), bernoulli_tensor: tensor([[0., 0., 1.], ​ [1., 1., 1.], ​ [1., 1., 1.]]) 张量的操作 熟悉numpy的朋友应该知道,Tensor与numpy的数据结构很类似,不仅数据结构类似,操作也是类似的,接下来介绍Tensor的常用操作。由于操作函数很多,这里就不一一举例,仅通过表格说明各个函数作用,详细介绍可查看官方文档 cat 将多个张量拼接在一起,例如多个特征图的融合可用。 concat 同cat, 是cat()的别名。 conj 返回共轭复数。 chunk 将tensor在某个维度上分成n份。 dsplit 类似numpy.dsplit()., 将张量按索引或指定的份数进行切分。 column_stack 水平堆叠张量。即第二个维度上增加,等同于torch.hstack。 dstack 沿第三个轴进行逐像素(depthwise)拼接。 gather 高级索引方法,目标检测中常用于索引bbox。在指定的轴上,根据给定的index进行索引。强烈推荐看example。 hsplit 类似numpy.hsplit(),将张量按列进行切分。若传入整数,则按等分划分。若传入list,则按list中元素进行索引。例如:[2, 3] and dim=0 would result in the tensors input[:2], input[2:3], and input[3:]. hstack 水平堆叠张量。即第二个维度上增加,等同于torch.column_stack。 index_select 在指定的维度上,按索引进行选择数据,然后拼接成新张量。可知道,新张量的指定维度上长度是index的长度。 masked_select 根据mask(0/1, False/True 形式的mask)索引数据,返回1-D张量。 movedim 移动轴。如0,1轴交换:torch.movedim(t, 1, 0) . moveaxis 同movedim。Alias for torch.movedim().(这里发现pytorch很多地方会将dim和axis混用,概念都是一样的。) narrow 变窄的张量?从功能看还是索引。在指定轴上,设置起始和长度进行索引。例如:torch.narrow(x, 0, 0, 2), 从第0个轴上的第0元素开始,索引2个元素。x[0:0+2, ...] nonzero 返回非零元素的index。torch.nonzero(torch.tensor([1, 1, 1, 0, 1])) 返回tensor([[ 0], [ 1], [ 2], [ 4]])。建议看example,一看就明白,尤其是对角线矩阵的那个例子,太清晰了。 permute 交换轴。 reshape 变换形状。 row_stack 按行堆叠张量。即第一个维度上增加,等同于torch.vstack。Alias of torch.vstack(). scatter scatter_(dim, index, src, reduce=None) → Tensor。将src中数据根据index中的索引按照dim的方向填进input中。这是一个十分难理解的函数,其中index是告诉你哪些位置需要变,src是告诉你要变的值是什么。这个就必须配合例子讲解,请跳转到本节底部进行学习。 scatter_add 同scatter一样,对input进行元素修改,这里是 +=, 而scatter是直接替换。 split 按给定的大小切分出多个张量。例如:torch.split(a, [1,4]); torch.split(a, 2) squeeze 移除张量为1的轴。如t.shape=[1, 3, 224, 224]. t.squeeze().shape -> [3, 224, 224] stack 在新的轴上拼接张量。与hstack\\vstack不同,它是新增一个轴。默认从第0个轴插入新轴。 swapaxes Alias for torch.transpose().交换轴。 swapdims Alias for torch.transpose().交换轴。 t 转置。 take 取张量中的某些元素,返回的是1D张量。torch.take(src, torch.tensor([0, 2, 5]))表示取第0,2,5个元素。 take_along_dim 取张量中的某些元素,返回的张量与index维度保持一致。可搭配torch.argmax(t)和torch.argsort使用,用于对最大概率所在位置取值,或进行排序,详见官方文档的example。 tensor_split 切分张量,核心看indices_or_sections变量如何设置。 tile 将张量重复X遍,X遍表示可按多个维度进行重复。例如:torch.tile(y, (2, 2)) transpose 交换轴。 unbind 移除张量的某个轴,并返回一串张量。如[[1], [2], [3]] --> [1], [2], [3] 。把行这个轴拆了。 unsqueeze 增加一个轴,常用于匹配数据维度。 vsplit 垂直切分。 vstack 垂直堆叠。 where 根据一个是非条件,选择x的元素还是y的元素,拼接成新张量。看案例可瞬间明白。 scater_ scater是将input张量中的部分值进行替换。公式如下: self[index[i][j][k]][j][k] = src[i][j][k] # if dim == 0 self[i][index[i][j][k]][k] = src[i][j][k] # if dim == 1 self[i][j][index[i][j][k]] = src[i][j][k] # if dim == 2 设计两个核心问题: input哪个位置需要替换? 替换成什么? 答: 从公式可知道,依次从index中找到元素放到dim的位置,就是input需要变的地方。 变成什么呢? 从src中找,src中与index一样位置的那个元素值放到input中。 案例1: >>> src = torch.arange(1, 11).reshape((2, 5)) >>> src tensor([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10]]) >>> index = torch.tensor([[0, 1, 2, 0]]) >>> torch.zeros(3, 5, dtype=src.dtype).scatter_(0, index, src) tensor([[1, 0, 0, 4, 0], [0, 2, 0, 0, 0], [0, 0, 3, 0, 0]]) dim=0, 所以行号跟着index的元素走。其它跟index的索引走。 第一步:找到index的第一个元素index[0, 0]是0, 那么把src[0, 0](是1)放到input[0, 0]第二步:找到index的第二个元素index[0, 1]是1, 那么把src[0, 1](是2)放到input[1, 1]第三步:找到index的第三个元素index[0, 2]是2, 那么把src[0, 2](是3)放到input[2, 2]第四步:找到index的第四个元素index[0, 3]是0, 那么把src[0, 3](是4)放到input[0, 3] 案例2: >>> src = torch.arange(1, 11).reshape((2, 5)) >>> src tensor([[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9, 10]]) >>> index = torch.tensor([[0, 2, 4], [1, 2, 3]]) >>> index tensor([[0, 2, 4], [1, 2, 3]]) >>> torch.zeros(3, 5, dtype=src.dtype).scatter_(1, index, src) tensor([[1, 0, 2, 0, 3], [0, 6, 7, 8, 0], [0, 0, 0, 0, 0]]) dim=1:告诉input(零矩阵)的索引,沿着列进行索引,行根据index走。 index:2*3,告诉input(零矩阵),你的哪些行是要被替换的。 src:input要替换成什么呢?从src里找,怎么找?通过index的索引对应的找。 第一步:找到index的第一个元素index[0, 0]是0, 那么把src[0, 0](是1)放到input[0, 0]第二步:找到index的第二个元素index[0, 1]是2, 那么把src[0, 1](是2)放到input[0, 2]第三步:找到index的第三个元素index[0, 2]是4, 那么把src[0, 2](是3)放到input[0, 4]第四步:找到index的第四个元素index[1, 0]是1, 那么把src[1, 0](是6)放到input[1, 1]第五步:找到index的第五个元素index[1, 1]是2, 那么把src[1, 1](是7)放到input[1, 2]第六步:找到index的第六个元素index[1, 2]是3, 那么把src[1, 2](是8)放到input[1, 3] 这里可以看到 index的元素是决定input的哪个位置要变 变的值是从src上对应于index的索引上找。可以看到src的索引与index的索引保持一致的 案例3:one-hot的生成 >>> label = torch.arange(3).view(-1, 1) >>> label tensor([[0], [1], [2]]) >>> torch.zeros(3, 3).scatter_(1, label, 1) tensor([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]) 第一步:找到index的第一个元素index[0, 0]是0, 那么把src[0, 0](是1)放到input[0, 0] 第二步:找到index的第二个元素index[1, 0]是1, 那么把src[1, 0](是1)放到input[1, 1] 第三步:找到index的第三个元素index[2, 0]是2, 那么把src[2, 0](是1)放到input[2, 2] (one-hot的案例不利于理解scater函数,因为它的行和列是一样的。。。其实input[x, y] 中的x,y是有区别的,x是根据index走,y是根据index的元素值走的,而具体的值是根据src的值。) 张量的随机种子 随机种子(random seed)是编程语言中基础的概念,大多数编程语言都有随机种子的概念,它主要用于实验的复现。针对随机种子pytorch也有一些设置函数。 seed 获取一个随机的随机种子。Returns a 64 bit number used to seed the RNG. manual_seed 手动设置随机种子,建议设置为42,这是近期一个玄学研究。说42有效的提高模型精度。当然大家可以设置为你喜欢的,只要保持一致即可。 initial_seed 返回初始种子。 get_rng_state 获取随机数生成器状态。Returns the random number generator state as a torch.ByteTensor. set_rng_state 设定随机数生成器状态。这两怎么用暂时未知。Sets the random number generator state. 以上均是设置cpu上的张量随机种子,在cuda上是另外一套随机种子,如torch.cuda.manual_seed_all(seed), 这些到cuda模块再进行介绍,这里只需要知道cpu和cuda上需要分别设置随机种子。 张量的数学操作 张量还提供大量数学操作,估计了一下,有快一百个函数,这里就不再一一分析,只需要知道有哪几大类,用到的时候来查吧。 Pointwise Ops: 逐元素的操作,如abs, cos, sin, floor, floor_divide, pow等 Reduction Ops: 减少元素的操作,如argmax, argmin, all, any, mean, norm, var等 Comparison Ops:对比操作, 如ge, gt, le, lt, eq, argsort, isnan, topk, Spectral Ops: 谱操作,如短时傅里叶变换等各类信号处理的函数。 Other Operations:其它, clone, diag,flip等 BLAS and LAPACK Operations:BLAS(Basic Linear Algebra Subprograms)基础线性代数)操作。如, addmm, dot, inner, svd等。 小结 本节介绍了张量主要的操作函数,并归类到各个小结,这些仅是张量的部分操作,更多操作还请大家多多看官方文档。对于张量,主要是要理解2.3小节中张量的结构以及作用,对于它的操作就像numpy一样简单易用。 下一节就开始讲解pytorch的核心——autograd,autograd也是现代深度学习框架的核心,是实现自动微分的具体实现。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.5-computational-graphs.html":{"url":"chapter-2/2.5-computational-graphs.html","title":"2.5 自动求导核心——计算图","keywords":"","body":"2.5 计算图 前两小节对tensor进行了详细介绍,知道了tensor是pytorch的核心数据结构,各类数据均以tensor来表示,并且tensor类中有许多属性与求导/梯度有关,接下来我们将深入学习pytorch的自动求导模块——autograd。在autograd正式开始之前,需要了解一个重要概念——计算图(Computational Graphs)。 在学习自动求导系统之前,需要了解计算图的概念。计算图(Computational Graphs)是一种描述运算的“语言”,它由节点(Node)和边(Edge)构成。 根据官网介绍,节点表示数据和计算操作,边仅表示数据流向 Each node in this graph represents a particular computation or operation, and edges of this graph consist of references between nodes. A Node represents a particular computation or operation and is represented in Python using the torch.fx.Node class. Edges between nodes are represented as direct references to other nodes via the args property of the Node class. 更多关于计算图、节点和边的概念,可以阅读: https://medium.com/tebs-lab/deep-neural-networks-as-computational-graphs-867fcaa56c9 https://simple-english-machine-learning.readthedocs.io/en/latest/neural-networks/computational-graphs.html https://www.cs.cornell.edu/courses/cs5740/2017sp/lectures/04-nn-compgraph.pdf 节点表示数据,如标量,向量,矩阵,张量等 边表示运算,如加、减、乘、除、卷积、relu等; 记录所有节点和边的信息,可以方便地完成自动求导,假设有这么一个计算: y = (x+ w) * (w+1) 将每一步细化为: a = x + w b = w + 1 y = a * b 得到计算图如下: 有了计算图,我们可以尝试进行forward,带入x,w的输入数据,就得到结果y。 同样的,如果需要获取各参数的导数,也可以方便地获得。 计算图求导 假设我们要算y对w的导数,在计算图中要怎么做呢? 先来看w和y之间的关系,w会通过左边这条路走到y,也会通过右边这条路走到y,因此梯度也是一样的,会经过这两条路反馈回来。 所以y对w的偏导有两条路径,可以写成以下形式, ∂y/∂w = ∂y/∂a ∂a/∂w + ∂y/∂b ∂b/∂w,然后可以通过计算图依次求出。 如图所示: 这样我们得到 y对w的导数是5,我们可以拿纸和笔推一下,是否是一样的。 我们发现,所有的偏微分计算所需要用到的数据都是基于w和x的,这里,w和x就称为叶子结点。 叶子结点是最基础结点,其数据不是由运算生成的,因此是整个计算图的基石,是不可轻易”修改“的。而最终计算得到的y就是根节点,就像一棵树一样,叶子在上面,根在下面。 叶子结点 叶子结点是最基础的结点,其数据不是由运算生成的,因此是整个计算图的基石,是不可轻易”修改“的。而最终计算得到的y就是根节点,就像一棵树一样,叶子在上面,根在下面。 张量有一个属性是is_leaf, 就是用来指示一个张量是否为叶子结点的属性。 我们通过代码,实现以上运算,并查看该计算图的叶子结点和梯度。 import torch w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) # retain_grad() y = torch.mul(a, b) y.backward() print(w.grad) # 查看叶子结点 print(\"is_leaf:\\n\", w.is_leaf, x.is_leaf, a.is_leaf, b.is_leaf, y.is_leaf) # 查看梯度 print(\"gradient:\\n\", w.grad, x.grad, a.grad, b.grad, y.grad) # 查看 grad_fn print(\"grad_fn:\\n\", w.grad_fn, x.grad_fn, a.grad_fn, b.grad_fn, y.grad_fn) tensor([5.]) is_leaf: True True False False False gradient: tensor([5.]) tensor([2.]) None None None grad_fn: None None 我们发现y就不是叶子结点了,因为它是由结点w和结点x通过乘法运算得到的。 补充知识点1:非叶子结点在梯度反向传播结束后释放 只有叶子节点的梯度得到保留,中间变量的梯度默认不保留;在pytorch中,非叶子结点的梯度在反向传播结束之后就会被释放掉,如果需要保留的话可以对该结点设置retain_grad() 补充知识点2:grad_fn是用来记录创建张量时所用到的运算,在链式求导法则中会使用到。 思考一下y对w求导的过程,我们知道只要记录下计算图中的结点(数据)和边(运算),就可以通过链式法则轻易的求取梯度。 所以在pytorch中,自动微分的关键就是记录数据和该结点的运算。回想一下张量的结构当中其实就记录了这两个重要的东西。 在张量中,数据对应着data,结点的运算对应着grad_fn,大家现在应该明白为什么结点的运算叫grad_fn而不叫fn了吧,因为这个运算是在求梯度的时候使用的。 静态图与动态图 以上就是计算图的简单介绍。计算图根据计算图的搭建方式可以划分为静态图和动态图。 pytorch是典型的动态图,TensorFlow是静态图(TF 2.x 也支持动态图模式)。 动态图和静态图的搭建方式有何不同,如何判断和区分? 第一种判断:这就要看运算,是在计算图搭建之后,还是两者同步进行 先搭建计算图,再运算,这就是静态图机制。 而在运算的同时去搭建计算图,这就是动态图机制。 第二种判断:也可以通过判断运算过程中,计算图是否可变动来区分静态图与动态图。 在运算过程中,计算图可变动的是动态图;计算图不可变,是静止的,就是静态图。 下面来看两个示意图。 图1为pytorch的动态图示意,图2为TensorFlow的静态图示意。 动态图优点: 易理解:程序按照编写命令的顺序进行执行 灵活性:可依据模型运算结果来决定计算图 静态图优点: 高效性:优化计算图,提高运算效率(但在gpu时代,这一点对于初学者而言可忽略不计) 缺点: 晦涩性:需要学习 seesion, placeholder等概念,调试困难 以上是关于计算图概念的介绍,下一小节将详细剖析autograd机制及其常用的功能函数,请注意,下一节内容也非常丰富,可能需要多次阅读以充分理解。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-2/2.6-autograd.html":{"url":"chapter-2/2.6-autograd.html","title":"2.6 Autograd——自动微分","keywords":"","body":"2.6 Autograd 了解计算图后,我们可以开始学习autograd。这里再次回顾pytorch官网的一张示意图 在进行h2h、i2h、next_h、loss的计算过程中,逐步搭建计算图,同时针对每一个变量(tensor)都存储计算梯度所必备的grad_fn,便于自动求导系统使用。当计算到根节点后,在根节点调用.backward()函数,即可自动反向传播计算计算图中所有节点的梯度。这就是pytorch自动求导机制,其中涉及张量类、计算图、grad_fn、链式求导法则等基础概念,大家可以自行补充学习。 autograd 官方定义 来看看官方文档中对autograd的解释: Conceptually, autograd keeps a record of data (tensors) and all executed operations (along with the resulting new tensors) in a directed acyclic graph (DAG) consisting of Function objects. In this DAG, leaves are the input tensors, roots are the output tensors. By tracing this graph from roots to leaves, you can automatically compute the gradients using the chain rule. In a forward pass, autograd does two things simultaneously: run the requested operation to compute a resulting tensor maintain the operation’s gradient function in the DAG. The backward pass kicks off when .backward() is called on the DAG root. autograd then: computes the gradients from each .grad_fn, accumulates them in the respective tensor’s .grad attribute using the chain rule, propagates all the way to the leaf tensors. from: https://pytorch.org/tutorials/beginner/basics/autogradqs_tutorial.html#more-on-computational-graphs 划重点: 自动求导机制通过有向无环图(directed acyclic graph ,DAG)实现 在DAG中,记录数据(对应tensor.data)以及操作(对应tensor.grad_fn) 操作在pytorch中统称为Function,如加法、减法、乘法、ReLU、conv、Pooling等,统统是Function autograd 的使用 autograd的使用有很多方法,这里重点讲解一下三个,并在最后汇总一些知识点。更多API推荐阅读官方文档 torch.autograd.backward torch.autograd.grad torch.autograd.Function torch.autograd.backward backward函数是使用频率最高的自动求导函数,没有之一。99%的训练代码中都会用它进行梯度求导,然后更新权重。 使用方法可以参考第二章第二节-新冠肺炎分类的代码,loss.backward()就可以完成计算图中所有张量的梯度求解。 虽然绝大多数都是直接使用,但是backward()里边还有一些高级参数,值得了解。 torch.autograd.backward(tensors, grad_tensors=None, retain_graph=None, create_graph=False, grad_variables=None, inputs=None) tensors (Sequence[Tensor] or Tensor) – 用于求导的张量。如上例的loss。 grad_tensors (Sequence[Tensor or None] or Tensor, optional) – 雅克比向量积中使用,详细作用请看代码演示。 retain_graph (bool, optional) – 是否需要保留计算图。pytorch的机制是在方向传播结束时,计算图释放以节省内存。大家可以尝试连续使用loss.backward(),就会报错。如果需要多次求导,则在执行backward()时,retain_graph=True。 create_graph (bool, optional) – 是否创建计算图,用于高阶求导。 inputs (Sequence[Tensor] or Tensor, optional) – Inputs w.r.t. which the gradient be will accumulated into .grad. All other Tensors will be ignored. If not provided, the gradient is accumulated into all the leaf Tensors that were used to compute the attr::tensors. 补充说明:我们使用时候都是在张量上直接调用.backward()函数,但这里却是torch.autograd.backward,为什么不一样呢? 其实Tensor.backward()接口内部调用了autograd.backward。 请看使用示例 retain_grad参数使用 对比两个代码段,仔细阅读pytorch报错信息。 ##### retain_graph=True import torch w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) y.backward(retain_graph=True) print(w.grad) y.backward() print(w.grad) tensor([5.]) tensor([10.]) 运行上面代码段可以看到是正常的,下面这个代码段就会报错,报错信息提示非常明确:Trying to backward through the graph a second time。并且还给出了解决方法: Specify retain_graph=True if you need to backward through the graph a second time 。这也是pytorch代码写得好的地方,出现错误不要慌,仔细看看报错信息,里边可能会有解决问题的方法。 ##### retain_graph=False import torch w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) y.backward() print(w.grad) y.backward() print(w.grad) tensor([5.]) --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) in 10 y.backward() 11 print(w.grad) ---> 12 y.backward() 13 print(w.grad) D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\lib\\site-packages\\torch\\_tensor.py in backward(self, gradient, retain_graph, create_graph, inputs) 305 create_graph=create_graph, 306 inputs=inputs) --> 307 torch.autograd.backward(self, gradient, retain_graph, create_graph, inputs=inputs) 308 309 def register_hook(self, hook): D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\lib\\site-packages\\torch\\autograd\\__init__.py in backward(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs) 154 Variable._execution_engine.run_backward( 155 tensors, grad_tensors_, retain_graph, create_graph, inputs, --> 156 allow_unreachable=True, accumulate_grad=True) # allow_unreachable flag 157 158 RuntimeError: Trying to backward through the graph a second time (or directly access saved tensors after they have already been freed). Saved intermediate values of the graph are freed when you call .backward() or autograd.grad(). Specify retain_graph=True if you need to backward through the graph a second time or if you need to access saved tensors after calling backward. grad_tensors使用 w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y0 = torch.mul(a, b) # y0 = (x+w) * (w+1) dy0/dw = 2w + x + 1 y1 = torch.add(a, b) # y1 = (x+w) + (w+1) dy1/dw = 2 loss = torch.cat([y0, y1], dim=0) # [y0, y1] grad_tensors = torch.tensor([1., 2.]) loss.backward(gradient=grad_tensors) # Tensor.backward中的 gradient 传入 torch.autograd.backward()中的grad_tensors # w = 1* (dy0/dw) + 2*(dy1/dw) # w = 1* (2w + x + 1) + 2*(w) # w = 1* (5) + 2*(2) # w = 9 print(w.grad) tensor([9.]) torch.autograd.grad torch.autograd.grad(outputs, inputs, grad_outputs=None, retain_graph=None, create_graph=False, only_inputs=True, allow_unused=False) 功能:计算outputs对inputs的导数 主要参数: outputs (sequence of Tensor) – 用于求导的张量,如loss inputs (sequence of Tensor) – 所要计算导数的张量 grad_outputs (sequence of Tensor) – 雅克比向量积中使用。 retain_graph (bool, optional) – 是否需要保留计算图。pytorch的机制是在方向传播结束时,计算图释放以节省内存。大家可以尝试连续使用loss.backward(),就会报错。如果需要多次求导,则在执行backward()时,retain_graph=True。 create_graph (bool, optional) – 是否创建计算图,用于高阶求导。 allow_unused (bool, optional) – 是否需要指示,计算梯度时未使用的张量是错误的。 此函数使用上比较简单,请看案例: import torch x = torch.tensor([3.], requires_grad=True) y = torch.pow(x, 2) # y = x**2 # 一阶导数 grad_1 = torch.autograd.grad(y, x, create_graph=True) # grad_1 = dy/dx = 2x = 2 * 3 = 6 print(grad_1) # 二阶导数 grad_2 = torch.autograd.grad(grad_1[0], x) # grad_2 = d(dy/dx)/dx = d(2x)/dx = 2 print(grad_2) (tensor([6.], grad_fn=),) (tensor([2.]),) torch.autograd.Function 有的时候,想要实现自己的一些操作(op),如特殊的数学函数、pytorch的module中没有的网络层,那就需要自己写一个Function,在Function中定义好forward的计算公式、backward的计算公式,然后将这些op组合到模型中,模型就可以用autograd完成梯度求取。 这个概念还是很抽象,平时用得不多,但是自己想要自定义网络时,常常需要自己写op,那么它就很好用了,为了让大家掌握自定义op——Function的写法,特地从多处收集了四个案例,大家多运行代码体会Function如何写。 案例1: exp 案例1:来自 https://pytorch.org/docs/stable/autograd.html#function 假设需要一个计算指数的功能,并且能组合到模型中,实现autograd,那么可以这样实现 第一步:继承Function第二步:实现forward第三步:实现backward 注意事项: forward和backward函数第一个参数为ctx,它的作用类似于类函数的self一样,更详细解释可参考如下: In the forward pass we receive a Tensor containing the input and return a Tensor containing the output. ctx is a context object that can be used to stash information for backward computation. You can cache arbitrary objects for use in the backward pass using the ctx.save_for_backward method. backward函数返回的参数个数与forward的输入参数个数相同, 即,传入该op的参数,都需要给它们计算对应的梯度。 import torch from torch.autograd.function import Function class Exp(Function): @staticmethod def forward(ctx, i): # ============== step1: 函数功能实现 ============== result = i.exp() # ============== step1: 函数功能实现 ============== # ============== step2: 结果保存,用于反向传播 ============== ctx.save_for_backward(result) # ============== step2: 结果保存,用于反向传播 ============== return result @staticmethod def backward(ctx, grad_output): # ============== step1: 取出结果,用于反向传播 ============== result, = ctx.saved_tensors # ============== step1: 取出结果,用于反向传播 ============== # ============== step2: 反向传播公式实现 ============== grad_results = grad_output * result # ============== step2: 反向传播公式实现 ============== return grad_results x = torch.tensor([1.], requires_grad=True) y = Exp.apply(x) # 需要使用apply方法调用自定义autograd function print(y) # y = e^x = e^1 = 2.7183 y.backward() print(x.grad) # 反传梯度, x.grad = dy/dx = e^x = e^1 = 2.7183 # 关于本例子更详细解释,推荐阅读 https://zhuanlan.zhihu.com/p/321449610 tensor([2.7183], grad_fn=) tensor([2.7183]) 从代码里可以看到,y这个张量的 grad_fn 是 ExpBackward,正是我们自己实现的函数,这表明当y求梯度时,会调用ExpBackward这个函数进行计算这也是张量的grad_fn的作用所在 案例2:为梯度乘以一定系数 Gradcoeff 案例2来自: https://zhuanlan.zhihu.com/p/321449610 功能是反向传梯度时乘以一个自定义系数 class GradCoeff(Function): @staticmethod def forward(ctx, x, coeff): # ============== step1: 函数功能实现 ============== ctx.coeff = coeff # 将coeff存为ctx的成员变量 x.view_as(x) # ============== step1: 函数功能实现 ============== return x @staticmethod def backward(ctx, grad_output): return ctx.coeff * grad_output, None # backward的输出个数,应与forward的输入个数相同,此处coeff不需要梯度,因此返回None # 尝试使用 x = torch.tensor([2.], requires_grad=True) ret = GradCoeff.apply(x, -0.1) # 前向需要同时提供x及coeff,设置coeff为-0.1 ret = ret ** 2 print(ret) # 注意看: ret.grad_fn ret.backward() print(x.grad) tensor([4.], grad_fn=) tensor([-0.4000]) 在这里需要注意 backward函数返回的参数个数与forward的输入参数个数相同即,传入该op的参数,都需要给它们计算对应的梯度。 案例3:勒让德多项式 案例来自:https://github.com/excelkks/blog 假设多项式为: y=a+bx+cx2+dx3 y = a+bx+cx^2+dx^3 y=a+bx+cx​2​​+dx​3​​ 时,用两步替代该过程 y=a+b×P3(c+dx),P3(x)=12(5x3−3x) y= a+b\\times P_3(c+dx), P_3(x) = \\frac{1}{2}(5x^3-3x) y=a+b×P​3​​(c+dx),P​3​​(x)=​2​​1​​(5x​3​​−3x) import torch import math from torch.autograd.function import Function class LegendrePolynomial3(Function): @staticmethod def forward(ctx, x): \"\"\" In the forward pass we receive a Tensor containing the input and return a Tensor containing the output. ctx is a context object that can be used to stash information for backward computation. You can cache arbitrary objects for use in the backward pass using the ctx.save_for_backward method. \"\"\" y = 0.5 * (5 * x ** 3 - 3 * x) ctx.save_for_backward(x) return y @staticmethod def backward(ctx, grad_output): \"\"\" In the backward pass we receive a Tensor containing the gradient of the loss with respect to the output, and we need to compute the gradient of the loss with respect to the input. \"\"\" ret, = ctx.saved_tensors return grad_output * 1.5 * (5 * ret ** 2 - 1) a, b, c, d = 1, 2, 1, 2 x = 1 P3 = LegendrePolynomial3.apply y_pred = a + b * P3(c + d * x) print(y_pred) 127.0 案例4:手动实现2D卷积 案例来自:https://pytorch.org/tutorials/intermediate/custom_function_conv_bn_tutorial.html案例本是卷积与BN的融合实现,此处仅观察Function的使用,更详细的内容,十分推荐阅读原文章下面看如何实现conv_2d的 import torch from torch.autograd.function import once_differentiable import torch.nn.functional as F def convolution_backward(grad_out, X, weight): \"\"\" 将反向传播功能用函数包装起来,返回的参数个数与forward接收的参数个数保持一致,为2个 \"\"\" grad_input = F.conv2d(X.transpose(0, 1), grad_out.transpose(0, 1)).transpose(0, 1) grad_X = F.conv_transpose2d(grad_out, weight) return grad_X, grad_input class MyConv2D(torch.autograd.Function): @staticmethod def forward(ctx, X, weight): ctx.save_for_backward(X, weight) # ============== step1: 函数功能实现 ============== ret = F.conv2d(X, weight) # ============== step1: 函数功能实现 ============== return ret @staticmethod def backward(ctx, grad_out): X, weight = ctx.saved_tensors return convolution_backward(grad_out, X, weight) weight = torch.rand(5, 3, 3, 3, requires_grad=True, dtype=torch.double) X = torch.rand(10, 3, 7, 7, requires_grad=True, dtype=torch.double) # gradcheck 会检查你实现的自定义操作的前向传播 (forward) 和反向传播 (backward) 方法是否正确计算了梯度。 # 如果返回 True,则表示梯度检查通过,即自定义操作的梯度计算与数值近似梯度之间的一致性在允许的误差范围内; # 如果返回 False,则说明存在不匹配,需要检查和修正自定义操作的反向传播逻辑。 print(\"梯度检查: \", torch.autograd.gradcheck(MyConv2D.apply, (X, weight))) # gradcheck 功能请自行了解,通常写完Function会用它检查一下 y = MyConv2D.apply(X, weight) label = torch.randn_like(y) loss = F.mse_loss(y, label) print(\"反向传播前,weight.grad: \", weight.grad) loss.backward() print(\"反向传播后,weight.grad: \", weight.grad) 梯度检查: True 反向传播前,weight.grad: None 反向传播后,weight.grad: tensor([[[[1.3423, 1.3445, 1.3271], [1.3008, 1.3262, 1.2493], [1.2969, 1.3269, 1.2369]], ...... [[1.2302, 1.2134, 1.2619], [1.2397, 1.2048, 1.2447], [1.2419, 1.2548, 1.2647]]]], dtype=torch.float64) ​ autograd相关的知识点 autograd使用过程中还有很多需要注意的地方,在这里做个小汇总。 知识点一:梯度不会自动清零 知识点二: 依赖于叶子结点的结点,requires_grad默认为True 知识点三: 叶子结点不可执行in-place 知识点四: detach 的作用 知识点五: with torch.no_grad()的作用 知识点一:梯度不会自动清零 import torch w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) for i in range(4): a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) y.backward() print(w.grad) # 梯度不会自动清零,数据会累加, 通常需要采用 optimizer.zero_grad() 完成对参数的梯度清零 # w.grad.zero_() tensor([5.]) tensor([10.]) tensor([15.]) tensor([20.]) 知识点二:依赖于叶子结点的结点,requires_grad默认为True 结点的运算依赖于叶子结点的话,它一定是要计算梯度的,因为叶子结点梯度的计算是从后向前传播的,因此与其相关的结点均需要计算梯度,这点还是很好理解的。 import torch w = torch.tensor([1.], requires_grad=True) # x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) print(a.requires_grad, b.requires_grad, y.requires_grad) print(a.is_leaf, b.is_leaf, y.is_leaf) True True True False False False 知识点三:叶子张量不可以执行in-place操作 叶子结点不可执行in-place,因为计算图的backward过程都依赖于叶子结点的计算,可以回顾计算图当中的例子,所有的偏微分计算所需要用到的数据都是基于w和x(叶子结点),因此叶子结点不允许in-place操作。 a = torch.ones((1, )) print(id(a), a) a = a + torch.ones((1, )) print(id(a), a) a += torch.ones((1, )) print(id(a), a) 2361561191752 tensor([1.]) 2362180999432 tensor([2.]) 2362180999432 tensor([3.]) w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) w.add_(1) y.backward() --------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) in 6 y = torch.mul(a, b) 7 ----> 8 w.add_(1) 9 10 y.backward() RuntimeError: a leaf Variable that requires grad is being used in an in-place operation. 知识点四:detach 的作用 通过以上知识,我们知道计算图中的张量是不能随便修改的,否则会造成计算图的backward计算错误,那有没有其他方法能修改呢?当然有,那就是detach() detach的作用是:从计算图中剥离出“数据”,并以一个新张量的形式返回,并且新张量与旧张量共享数据,简单的可理解为做了一个别名。 请看下例的w,detach后对w_detach修改数据,w同步地被改为了999 w = torch.tensor([1.], requires_grad=True) x = torch.tensor([2.], requires_grad=True) a = torch.add(w, x) b = torch.add(w, 1) y = torch.mul(a, b) y.backward() w_detach = w.detach() w_detach.data[0] = 999 print(w) tensor([999.], requires_grad=True) 知识点五:with torch.no_grad()的作用 autograd自动构建计算图过程中会保存一系列中间变量,以便于backward的计算,这就必然需要花费额外的内存和时间。而并不是所有情况下都需要backward,例如推理的时候,因此可以采用上下文管理器——torch.no_grad()来管理上下文,让pytorch不记录相应的变量,以加快速度和节省空间。详见:https://pytorch.org/docs/stable/generated/torch.no_grad.html?highlight=no_grad#torch.no_grad 小结 本章终于结束,本章目的是为大家介绍pytorch的核心模块,包括pytorch代码库结构,以便于今后阅读源码,知道从哪里找代码;包括第一个分类模型训练,便于大家理解模型训练过程;包括核心数据结构——张量,便于理解整个pytorch的数据;包括计算图与autograd,便于大家熟悉自动微分的过程及自定义op的方法。 下一章将通过借助covid-19任务,详细介绍pytorch的数据读取机制,以及各种数据形式的读取,包括csv形式、txt形式、杂乱文件夹形式等一切关于数据读取、加载、操作的模块都将涉及。 小记:动笔一个多月,才写了两章,尤其autograd和tensor写了大半个月,希望后面能有更多时间精力早日完成,加油!2022年1月18日 ​ ​ Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/":{"url":"chapter-3/","title":"第三章 PyTorch 数据模块","keywords":"","body":"第三章 PyTorch 数据模块 第三章 PyTorch 数据模块 3.1 Dataset 3.2 DataLoader 3.3 Dataset及常用API 3.4 transforms 3.5 torchvision 经典dataset学习 第三章简介 经过前两章的铺垫,本章终于可以讲讲项目代码中重要的模块——数据模块。 数据模块包括哪些内容呢?相信大家多少会有一些感觉,不过最好结合具体任务来剖析数据模块。 我们回顾2.2中的COVID-19分类任务,观察一下数据是如何从硬盘到模型输入的。 我们倒着推, 模型接收的训练数据是 data:outputs = model(data) data来自train_loader: for data, labels in train_loader: train_loader 来自 DataLoader与train_data:train_loader = DataLoader(dataset=train_data, batch_size=2) train_data 来自 COVID19Dataset:train_data = COVID19Dataset(root_dir=img_dir, txt_path=path_txt_train, transform=transforms_func) COVID19Dataset继承于Dataset:COVID19Dataset(Dataset) 至此,知道整个数据处理过程会涉及pytorch的两个核心——Dataset, DataLoader。 Dataset是一个抽象基类,提供给用户定义自己的数据读取方式,最核心在于getitem中间对数据的处理。 DataLoader是pytorch数据加载的核心,其中包括多个功能,如打乱数据,采样机制(实现均衡1:1采样),多进程数据加载,组装成Batch形式等丰富的功能。 本章将围绕着它们两个展开介绍pytorch的数据读取、预处理、加载等功能。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.1-dataset.html":{"url":"chapter-3/3.1-dataset.html","title":"3.1 Dataset","keywords":"","body":"3.1 torch.utils.data.Dataset 数据交互模块——Dataset 虽然说pytorch数据模块的核心是DataLoader,但是对于使用者而言,改动最多的、与源数据最接近的是Dataset, 本小节就详细分析Dataset的作用,并通过三个案例学习如何编写自定义Dataset来读取自己的数据集。 Dataset的功能 pytorch提供的torch.utils.data.Dataset类是一个抽象基类An abstract class representing a Dataset,供用户继承,编写自己的dataset,实现对数据的读取。在Dataset类的编写中必须要实现的两个函数是__getitem__和__len__(由于markdown语法问题,后续双下划线就省略了)。 getitem:需要实现读取一个样本的功能。通常是传入索引(index,可以是序号或key),然后实现从磁盘中读取数据,并进行预处理(包括online的数据增强),然后返回一个样本的数据。数据可以是包括模型需要的输入、标签,也可以是其他元信息,例如图片的路径。getitem返回的数据会在dataloader中组装成一个batch。即,通常情况下是在dataloader中调用Dataset的getitem函数获取一个样本。 len:返回数据集的大小,数据集的大小也是个最要的信息,它在dataloader中也会用到。如果这个函数返回的是0,dataloader会报错:\"ValueError: num_samples should be a positive integer value, but got num_samples=0\" 这个报错相信大家经常会遇到,这通常是文件路径没写对,导致你的dataset找不到数据,数据个数为0。 了解Dataset类的概念,下面通过一幅示意图,来理解Dataset与DataLoader的关系。 dataset负责与磁盘打交道,将磁盘上的数据读取并预处理好,提供给DataLoader,而DataLoader只需要关心如何组装成批数据,以及如何采样。采样的体现是出现在传入getitem函数的索引,这里采样的规则可以通过sampler由用户自定义,可以方便地实现均衡采样、随机采样、有偏采样、渐进式采样等,这个留在DataLoader中会详细展开。 此处,先分析Dataset如何与磁盘构建联系。 从2.2节的例子中可以看到,我们为COVID19Dataset定义了一个_get_img_info函数,该函数就是用来建立磁盘关系的,在这个函数中收集并处理样本的路径信息、标签信息,存储到一个list中,供getitem函数使用。getitem函数只需要拿到序号,就可获得图片的路径信息、标签信息,接着进行图片预处理,最后返回一个样本信息。 希望大家体会_get_img_info函数的作用,对于各种不同的数据形式,都可以用这个模板实现Dataset的构建,只需将数据信息(路径、标签等)收集并存储至列表中,供__getitem__函数使用”。 三个Dataset案例 相信大家在做自己的任务时,遇到的第一个问题就是,怎么把自己的数据放到github的模型上跑起来。很多朋友通常会把自己的数据调整为与现成项目数据一模一样的数据形式,然后执行相关代码。这样虽然快捷,但缺少灵活性。 为了让大家能掌握各类数据形式的读取,这里构建三个不同的数据形式进行编写Dataset。 第一个:2.2中的类型。数据的划分及标签在txt中。 第二个:数据的划分及标签在文件夹中体现 第三个:数据的划分及标签在csv中 详情请结合 配套代码,深刻理解_get_img_info及Dataset做了什么。 代码输出主要有两部分, 第一部分是两种dataset的getitem输出。 第二部分是结合DataLoader进行数据加载。 先看第一部分,输出的是 PIL对象及图像标签,这里可以进入getitem函数看到采用了 img = Image.open(path_img).convert('L') 对图片进行了读取,得到了PIL对象,由于transform为None,不对图像进行任何预处理,因此getitem函数返回的图像是PIL对象。 2 (, 1) 2 (, 1) 第二部分是结合DataLoader的使用,这种形式更贴近真实场景,在这里为Dataset设置了一些transform,有图像的缩放,ToTensor, normalize三个方法。因此,getitem返回的图像变为了张量的形式,并且在DataLoader中组装成了batchsize的形式。大家可以尝试修改缩放的大小来观察输出,也可以注释normalize来观察它们的作用。 0 torch.Size([2, 1, 4, 4]) tensor([[[[-0.0431, -0.1216, -0.0980, -0.1373], [-0.0667, -0.2000, -0.0824, -0.2392], [-0.1137, 0.0353, 0.1843, -0.2078], [ 0.0510, 0.3255, 0.3490, -0.0510]]], [[[-0.3569, -0.2863, -0.3333, -0.4118], [ 0.0196, -0.3098, -0.2941, 0.1059], [-0.2392, -0.1294, 0.0510, -0.2314], [-0.1059, 0.4118, 0.4667, 0.0275]]]]) torch.Size([2]) tensor([1, 0]) 关于transform的系列方法以及工作原理,将在本章后半部分讲解数据增强部分再详细展开。 小结 本小结介绍了torch.utils.data.Dataset类的结构及工作原理,并通过三个案例实践,加深大家对自行编写Dataset的认识,关于Dataset的编写,torchvision也有很多常用公开数据集的Dataset模板,建议大家学习,本章后半部分也会挑选几个Dataset进行分析。下一小节将介绍DataLoader类的使用。 补充学习建议 IDE的debug: 下一小节的代码将采用debug模式进行逐步分析,建议大家提前熟悉pycharm等IDE的debug功能。 python的迭代器:相信很多初学者对代码中的“next(iter(train_set))”不太了解,这里建议大家了解iter概念、next概念、迭代器概念、以及双下划线函数概念。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.2-dataloader.html":{"url":"chapter-3/3.2-dataloader.html","title":"3.2 DataLoader","keywords":"","body":"3.2 DataLoader dataloader简介 按照上图的顺序,本小节就来到pytorch数据加载最核心模块——DataLoader。 torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False, sampler=None, batch_sampler=None, num_workers=0, collate_fn=None, pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None, multiprocessing_context=None, generator=None, *, prefetch_factor=2, persistent_workers=False) 从以上可以看到,DataLoader类有14个变量,因此成为最核心模块,一点不为过。 DataLoader功能繁多,这里根据官方文档可总结为以下五大功能: 支持两种形式数据集读取:map-style and iterable-style datasets, 自定义采样策略:customizing data loading order, 自动组装成批数据:automatic batching, 多进程数据加载:single- and multi-process data loading, 自动实现锁页内存(Pinning Memory):automatic memory pinning. 支持两种形式数据集读取 两种形式的数据集分别是映射式(Map-style)与迭代式(Iterable-style),在3.1小结中讲解的Dataset类就是映射式,因为它(getitem)提供了序号到数据的映射。迭代式则是编写一个可迭代对象,从中依次获取数据,此处不详细展开,感兴趣可以了解IterableDataset 注:往后不做特别说明,Dataset均表示映射式Dataset。 自定义采样策略 DataLoader可借助Sampler自定义采样策略,包括为每个类别设置采样权重以实现1:1的均衡采样,或者是自定义采样策略,关于Sampler会在后面小结详细展开,它是一个涨点神奇。 自动组装成批数据 mini-batch形式的训练成为了深度学习的标配,如何把数据组装成一个batch数据?DataLoader内部自动实现了该功能,并且可以通过batch_sampler、collate_fn来自定义组装的策略,十分灵活。 多进程数据加载 通常GPU运算消耗数据会比CPU读取加载数据要快,CPU“生产”跟不上GPU“消费”,因此需要多进程进行加载数据,以满足GPU的消费需求。通常指要设置num_workers 为CPU核心数,如16核的CPU就设置为16。 自动实现锁页内存(Pinning Memory) 锁页内存的概念通常在操作系统课程里才会涉及,非CS的同学可能不太了解,感兴趣的可以去了解一下。Pinning Memory是空间换时间的做法,将指定的数据“锁”住,不会被系统移动(交换)到磁盘中的虚拟内存,因此可以加快数据的读取速率。简单的可以理解为常用的衣服就“锁”在你的衣柜里,某些时候(如夏天),暂时不用的衣服——冬季大衣,则会移动到收纳柜里,以腾出空间放其它常用的衣服,等到冬天来临,需要用到大衣的时候,再从收纳柜里把大衣放到衣柜中。但是冬天拿大衣的时候就会慢一些,如果把它“锁”在你的衣柜,那么冬天获取它的时候自然快了,但占用了你的空间。这就是空间换时间的一个例子。这里的“锁”就是固定的意思,大家可补充学习一下OS的内容。 DataLoader API DataLoader提供了丰富的功能,下面介绍常用的功能,高阶功能等到具体项目中再进行分析。 dataset:它是一个Dataset实例,要能实现从索引(indices/keys)到样本的映射。(即getitem函数) batch_size:每个batch的样本量 shuffle:是否对打乱样本顺序。训练集通常要打乱它!验证集和测试集无所谓。 sampler:设置采样策略。后面会详细展开。 batch_sampler:设置采样策略, batch_sampler与sampler二选一,具体选中规则后面代码会体现。 num_workers: 设置多少个子进程进行数据加载(data loading) collate_fn:组装数据的规则, 决定如何将一批数据组装起来。 pin_memory:是否使用锁页内存,具体行为是“the data loader will copy Tensors into CUDA pinned memory before returning them” drop_last:每个epoch是否放弃最后一批不足batchsize大小的数据,即无法被batchsize整除时,最后会有一小批数据,是否进行训练,如果数据量足够多,通常设置为True。这样使模型训练更为稳定,大家千万不要理解为某些数据被舍弃了,因为每个epoch,dataloader的采样都会重新shuffle,因此不会存在某些数据被真正的丢弃。 下面通过配套代码加深dataloader的理解,并且观察DataLoader 与 Dataset是如何配合使用的。 运行代码,可看到输出如下信息: 0 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([1, 0]) 1 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([0, 1]) 2 torch.Size([1, 3, 224, 224]) torch.Size([1]) tensor([0]) 0 torch.Size([3, 3, 224, 224]) torch.Size([3]) tensor([0, 0, 1]) 1 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([1, 0]) 0 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([0, 0]) 1 torch.Size([2, 3, 224, 224]) torch.Size([2]) tensor([0, 1]) 这里主要观察batch_size和drop_last的作用,以及图片组装成batch之后的shape。 这里构建一个数据量为5的dataset,这样可以采用batchsize=2和3来观察drop_last的作用。 dataloader内部代码 下一步,我们将采用debug模式,深入dataloader内部,观察它是如何进行采样的,如何调用dataset的getitem获取数据,如何组装一个batch的。这里我们仅观察单进程模式,因此大家的num_workers注意设置为0。 首先在for i, (inputs, target) in enumerate(train_loader_bs2) 设置一个断点,然后debug模式运行代码,接着持续采用 Step Into方式运行代码,下面就列出依次会进入的代码: 第一步:初始化dataloader迭代器 for i, (inputs, target) in enumerate(train_loader_bs2) DataLoader的iter() DataLoader的_get_iterator() SingleProcessDataLoaderIter的_init。 注:至此,仅完成了DataLoader的初始化,需要再一次进入dataloader才开始读取数据。 第二步:依次循环该迭代器 来到 BaseDataLoaderIter的_next:进入521行:data = self._next_data() 来到 _SingleProcessDataLoaderIter的_next_data:此函数调用了两个重要功能,第一个获取一个batch的索引,第二个获取此batch的数据。下面一个一个来看。 进入 _SingleProcessDataLoaderIter的_next_data:进入560行, index = self._next_index() 来到 _BaseDataLoaderIter的_next_index(): 这里是对sampler的包装,调用sampler获取一批索引,进入512行 来到BatchSampler的iter():函数中有yield,这是一个迭代器,从这里可以看到sampler是如何工作的。默认情况下,这里用的是RandomSampler, 它会实现采样的顺序及频率。在本函数中,对self.sampler依次迭代,拿到足够一个batchsize的索引时,就yield。 回到 _SingleProcessDataLoaderIter的_next_data:第561行,经过index = self._next_index() ,已经获得一个batch所对应的index,接着进入self._dataset_fetcher.fetch(index) 来到 _MapDatasetFetcher的fetch:mapdataset就是前面讲到的map-style dataset。看到第49行,是一个列表生成式,在这里,调用了我们自己写的dataset,继续进入。 来到 AntsBeesDataset的getitem:进入到这里,大家就豁然开朗了吧,知道dataset是如何被dataloader使用的。下面,直接跳出去,回到 fetch看看如何组装的。 来到 _MapDatasetFetcher的fetch:第52行self.collate_fn(data), 这里采用collate_fn对数据进行组装,继续进入。 来到 collate.py的default_collate():这是pytorch默认的组装函数,值得大家认真学习。这个函数通常是一个递归函数,第一次进入时可以发现会来到第84行:return [default_collate(samples) for samples in transposed]。会依次再进入一次default_collate()。 这里的逻辑是这样的: 首先将dataset返回的一系列数据解包再zip,为的是将相同数据放到一起。即getitem的return返回有img和label,这里就是为了将多个img放到一起,多个label放到一起,当然大家可以在getitem返回其它有用信息(例如图片的路径)。 接着再次进入default_collate函数时,会对实际的数据进行组装。例如img的数据会进入if isinstance(elem, torch.Tensor),然后会看到img数据是这样被组装起来的:torch.stack(batch, 0, out=out),因此可知一个batch的图像数据第一个维度是B,整体是BCH*W。 至此,一个batch数据已经读取、加载完毕,依次跳出函数,可回到for i, (inputs, target) in enumerate(train_loader_bs2)。 这个时候,再观察inputs, target,一定会更清晰了,知道它们是如何从硬盘到模型需要的形式。并且通过上述debug过程,我们可以知道sampler的机制、collate_fn的机制,方便今后进行高级的改造。希望大家一定要debug几遍上述过程,并且记录。 小结 以上就是关于DataLoader的概念的介绍,通过两个小节相信大家对数据读取有了初步认识,可pytorch的数据处理远不止于此,它还提供了很多使用的方法,例如数据集的拼接,数据集的截取,数据的划分等,想了解怎么使用,请接着往下看。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.3-dataset-useful-api.html":{"url":"chapter-3/3.3-dataset-useful-api.html","title":"3.3 Dataset及常用API","keywords":"","body":"3.3 系列APIs API*022-1月至4月完全没有更新,这三个月发生的事情太多了,无论如何,过去的终将过去,继续学习PyTorch与人工智能吧,加油! 前几个小节已经把pytorch的数据读取、加载、预处理机制和逻辑关系理清楚了,下面讲一下实用的APIs,包括数据集的拼接、截取、划分,以及十分重要的采样策略——sampler。 concat 在实际项目中,数据的来源往往是多源的,可能是多个中心收集的,也可能来自多个时间段的收集,很难将可用数据统一到一个数据形式。通常有两种做法,一种是固定一个数据形式,所有获取到的数据经过整理,变为统一格式,然后用一个Dataset即可读取。还有一种更为灵活的方式是为每批数据编写一个Dataset,然后使用torch.utils.data.ConcatDataset类将他们拼接起来,这种方法可以灵活的处理多源数据,也可以很好的使用别人的数据及Dataset。 下面还是来看COVID-19的例子,大家知道想要获取大量的COVID-19数据,肯定是多源的,不同国家、不同机构、不同时间的X光片收集过来之后,如何把他们整理起来供模型训练呢?先看这个github仓库covid-chestxray-dataset,他们采取了以下方法,将采集到的数据统一整理,并生成metadata(元信息)。基于现成的Dataset,我们可通过拼接的方法将所有数据拼接成一个大的dataset进行使用。 请结合代码阅读,在2.2与3.2中分别实现了COVID19Dataset、COVID19Dataset2、COVID19Dataset3,假设在项目开始时拿到了COVID19Dataset,做了一段时间来了新数据2和3,那么像把他们放到一起充当训练集,可以用concat完成。可以看到代码将3个数据集拼接得到总的数据集,数据量为2+2+2=6。这里的concatdataset其实还是一个dataset类,它内部还是有len和getitem,里面的getitem代码思路值得学习。concatdataset通过给数据集编号、所有样本编号,然后在__getitem函数中将dataloader传进来的整体样本序号进行计算,得到匹配的数据集序号,以及在该数据集内的样本编号。 可能有点绕,请看图:假设dataloader想要第5个样本,传入index=4, 这时getitem会计算第五个样本在第三个数据集的第1个位置。然后通过self.datasets[datasetidx][sampleidx]来获取数据。这样对外进行一层封装,内部实现仍旧调用各个dataset的__getitem,这样是不是很巧妙呢? def __getitem__(self, idx): if idx len(self): raise ValueError(\"absolute value of index should not exceed dataset length\") idx = len(self) + idx dataset_idx = bisect.bisect_right(self.cumulative_sizes, idx) if dataset_idx == 0: sample_idx = idx else: sample_idx = idx - self.cumulative_sizes[dataset_idx - 1] return self.datasets[dataset_idx][sample_idx] Subset subset可根据指定的索引获取子数据集,Subset也是Dataset类,同样包含_len_和__getitem\\,其代码编写风格也可以学习一下. CLASStorch.utils.data.Subset(dataset, indices)[SOURCE] Subset of a dataset at specified indices. Parameters dataset (Dataset) – The whole Dataset indices (sequence) – Indices in the whole set selected for subset def __init__(self, dataset: Dataset[T_co], indices: Sequence[int]) -> None: self.dataset = dataset self.indices = indices def __getitem__(self, idx): if isinstance(idx, list): return self.dataset[[self.indices[i] for i in idx]] return self.dataset[self.indices[idx]] def __len__(self): return len(self.indices) 使用上非常简单,代码一目了然,不再赘述。 random_split 该函数的功能是随机的将dataset划分为多个不重叠的子集,适合用来划分训练、验证集(不过不建议通过它进行,因为对用户而言,其划分不可见,不利于分析)。 使用也非常简单,只需要设置每个子集的数据量,传给lengths即可。 torch.utils.data.random_split(dataset, lengths, generator=)[SOURCE] Randomly split a dataset into non-overlapping new datasets of given lengths. Optionally fix the generator for reproducible results, e.g.: Parameters dataset (Dataset) – Dataset to be split lengths (sequence) – lengths of splits to be produced generator (Generator) – Generator used for the random permutation ---------------------------------------------------------------------- 分割线 ------------------------------------------------------------------ sampler 下面进入另外一个主题——sampler, sampler是在dataloader中起到挑选数据的功能,主要是设置挑选策略,如按顺序挑选、随机挑选、按类别分概率挑选等等,这些都可以通过自定义sampler实现。 在上一节我们已经用过了一个sampler,那就是batch_sampler,我们先学习一下它的用法,然后再去了解 RandomSampler, SequentialSampler, 以及SubsetRandomSampler和WeightedRandomSampler。 sampler的概念比较复杂,建议大家将BatchSampler、RandomSampler和SequentialSampler放在一起学习。 sampler 与 batch_sampler 首先讲一下dataloader类的sampler变量与batch_sampler变量的区别,在dataloader里会有这两个变量,第一次碰到时候很懵,怎么还有两个采样器,dataloader到底用的哪一个?还是两个都用?经过一番调试,终于搞清楚了。 本质上它们两个都是采样器,当采用auto_collation时,采用batch_sampler。依据如下:dataloader.py 365行 @property def _index_sampler(self): ​ if self._auto_collation: ​ return self.batch_sampler ​ else: ​ return self.sampler 来看一下两者定义: sampler (Sampler or Iterable, optional) – defines the strategy to draw samples from the dataset. Can be any Iterable with len implemented. If specified, shuffle must not be specified. batch_sampler (Sampler or Iterable, optional) – like sampler, but returns a batch of indices at a time. Mutually exclusive with batch_size, shuffle, sampler, and drop_last. 从定义可知道batch_sampler是一次返回一个batch的索引。通常我们用的都是batch_sampler,其对应的是BatchSampler类。 BatchSampler 下面先学习BatchSampler类。回顾3.3的dataloader获取一个样本的机制,会在一个self.nextindex()中调用实际的sampler迭代器,继续进入会来到BatchSampler类的__iter函数,dataloader初始化的时候根据参数配置,自动设置了采样策略为BatchSampler, 依据如下:dataloader.py 第272行代码 if batch_size is not None and batch_sampler is None: # auto_collation without custom batch_sampler batch_sampler = BatchSampler(sampler, batch_size, drop_last) dataloader.py 365行 @property def _index_sampler(self): if self._auto_collation: return self.batch_sampler else: return self.sampler 定位到了BatchSampler,下面来看看类的定义以及传进去的参数是什么。 torch.utils.data.BatchSampler(sampler, batch_size, drop_last) 后两个参数好理解,第一个参数传入的是一个sampler采样器,在这里会有两种情况,如果需要shuffle,则传入RandomSampler,不需要打乱,则传入SequentialSampler。 依据如下, dataloader.py 267行。 if shuffle: sampler = RandomSampler(dataset, generator=generator) else: sampler = SequentialSampler(dataset) 到这里,BatchSampler、RandomSampler和SequentialSampler三者之间的关系逐渐清晰. BatchSampler是在其它两者之上封装了一个批抽取的功能,一次yield一个batch的index,而样本采样的顺序取决于RandomSampler和SequentialSample。 来学习一下BatchSampler如何产生一个batch的序号,并且支持drop_last的功能。 def __iter__(self) -> Iterator[List[int]]: batch = [] for idx in self.sampler: batch.append(idx) if len(batch) == self.batch_size: yield batch batch = [] # 当for循环结束,且batch的数量又不满足batchsize时,则进入以下代码 # 其实就是drop_last的逻辑代码 if len(batch) > 0 and not self.drop_last: yield batch 理解了三者的关系(BatchSampler、RandomSampler和SequentialSampler),RandomSampler和SequentialSampler就很容易理解,来看它们的核心iter函数,学习一下如何编写顺序迭代器以及随机迭代器。 SequentialSampler 顺序迭代器相对简单,是得到一个按顺序的迭代器。这个顺序就来自 range()函数。 def __iter__(self) -> Iterator[int]: return iter(range(len(self.data_source))) RandomSampler RandomSampler的iter函数核心在于设置一个随机策略,随机策略委托给generator实现,在使用的时候非常简单,默认情况下会使用这行代码实现:yield from torch.randperm(n, generator=generator).tolist(), 利用torch的随机方法生成一个随机整数序列,对于generator默认采用的是随机一个随机种子进行设置。更多的随机概念可以自行了解torch.Generator()、torch.randperm()。 def __iter__(self) -> Iterator[int]: n = len(self.data_source) if self.generator is None: seed = int(torch.empty((), dtype=torch.int64).random_().item()) generator = torch.Generator() generator.manual_seed(seed) else: generator = self.generator if self.replacement: for _ in range(self.num_samples // 32): yield from torch.randint(high=n, size=(32,), dtype=torch.int64, generator=generator).tolist() yield from torch.randint(high=n, size=(self.num_samples % 32,), dtype=torch.int64, generator=generator).tolist() else: yield from torch.randperm(n, generator=generator).tolist() 接下来介绍另外两个实用的采样器:SubsetRandomSampler和WeightedRandomSampler。 SubsetRandomSampler 顾名思义,可以通过索引定义一个子集的随机采样器,直接看代码 ``` ​ def iter(self) -> Iterator[int]: ​ for i in torch.randperm(len(self.indices), generator=self.generator): ​ yield self.indices[i] 从代码可知道,这个采样器返回的样本总数是传入的索引的长度,这里体现了subset,而随机则是每次会随机的从子集里挑选1个数据返回。 ---------------------------------------------------------------------- 分割线 ------------------------------------------------------------------ WeightedRandomSampler 不知大家是否自行处理过数据均衡采样?最简单粗暴的方法是否是把数据少的样本复制n份,直到所有类别样本数量一致,这是一种“笨”办法,其实可以通过采样器进行加权的采样,下面来看看WeightedRandomSampler。 先来看它的原型: torch.utils.data.WeightedRandomSampler(weights, num_samples, replacement=True, generator=None) Samples elements from [0,..,len(weights)-1] with given probabilities (weights). weights (sequence) – 每个样本的采样权重,权重之和不必为1,只需要关心各样本之间的比例即可。 num_samples (int) – 采样数量,一般设为样本总量。 replacement (bool) –是否有放回采样。 True,表示有放回。 generator (Generator) – 自定义生成器,通常用默认的。 在pytorch的机制里,sampler为每个sample设置权重,因此在设置的时候不仅要指定每个类的采样概率,还要把各类采样概率分发到每个样本上,再传给WeightedRandomSampler。这个机制与常识有一点点不一样,直观的理解应该是为每个类别设置采样概率就好,但这却是为每个样本设置权重,因此需要额外操作两行代码。 通过以下两个案例学习如何使用WeightedRandomSampler。 案例1: sampler初认识 # 第一步:计算每个类的采样概率 weights = torch.tensor([1, 5], dtype=torch.float) # 第二步:生成每个样本的采样概率 train_targets = [sample[1] for sample in train_data.img_info] samples_weights = weights[train_targets] # 第三步:实例化WeightedRandomSampler sampler_w = WeightedRandomSampler( ​ weights=samples_weights, ​ num_samples=len(samples_weights), ​ replacement=True) sampler的构建分三步: 计算各类的采样概率:这里手动设置,是为了让大家可以调整不同的比率,观察dataloader采出样本的变化。下一个例子中采用样本数量进行计算,来达到均衡采样。 生成每个样本的概率:从pytorch机制了解到,需要为每个样本设置采样概率,这里采用的方法是按类别分发即可。在这里有一点需要注意,就是样本标签的顺序需要与dataset中的getitem中的索引顺序保持一致!由于这里采用了dataset.img_info来维护这个顺序,因此可以轻松获得样本顺序。 实例化WeightedRandomSampler 通过运行配套代码可以看到 torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 0]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) torch.Size([2]) tensor([1, 1]) 这里发现出现了很多次[1, 1]。这是因为有放回采样,并且样本1的采样概率比0高很多。 通过这个例子,希望大家能了解 WeightedRandomSampler的使用流程 WeightedRandomSampler采样机制可以为有放回的 有的样本在整个loader中可能不会选中 案例2:不均衡数据集进行均衡采样 点击进入配套代码 下面利用WeightedRandomSampler实现一个10类别的不均衡数据集采样,使它变为1:1的采样。 下面制作了一个虚拟的不均衡数据集,每个类别数量分别是 10, 20,..., 100。总共550张样本,下面希望通过WeightedRandomSampler实现一个dataloader,每次采样550张样本,各类别的数量大约为55。 代码的核心在于统计各类样本的数量,可仔细阅读 # 第一步:计算各类别的采样权重 # 计算每个类的样本数量 train_targets = [sample[1] for sample in train_data.img_info] label_counter = collections.Counter(train_targets) class_sample_counts = [label_counter[k] for k in sorted(label_counter)] # 需要特别注意,此list的顺序! # 计算权重,利用倒数即可 weights = 1. / torch.tensor(class_sample_counts, dtype=torch.float) 最后可以看到每个epoch采样到的数据几乎实现1:1,可以很好的实现按照设置的权重比例采样。 Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) Counter({9: 100, 8: 90, 7: 80, 6: 70, 5: 60, 4: 50, 3: 40, 2: 30, 1: 20, 0: 10}) 接下来运用sampler Counter({0: 62, 4: 62, 8: 61, 9: 58, 6: 57, 3: 54, 1: 51, 7: 50, 5: 48, 2: 47}) Counter({5: 72, 7: 59, 6: 59, 8: 57, 1: 57, 0: 55, 4: 53, 2: 49, 9: 48, 3: 41}) Counter({0: 71, 3: 64, 5: 60, 9: 57, 4: 56, 2: 54, 1: 54, 6: 51, 8: 43, 7: 40}) Counter({4: 64, 7: 62, 3: 60, 8: 58, 1: 54, 5: 54, 0: 53, 6: 51, 2: 50, 9: 44}) Counter({8: 68, 0: 62, 7: 60, 6: 58, 2: 55, 3: 51, 9: 50, 5: 50, 1: 50, 4: 46}) Counter({5: 66, 4: 59, 9: 57, 0: 56, 1: 55, 3: 54, 7: 53, 2: 51, 8: 51, 6: 48}) Counter({3: 72, 9: 68, 5: 65, 6: 58, 4: 56, 8: 49, 1: 47, 2: 47, 0: 45, 7: 43}) Counter({4: 63, 2: 62, 7: 60, 9: 59, 3: 58, 8: 57, 6: 52, 0: 50, 5: 45, 1: 44}) Counter({8: 73, 3: 62, 6: 55, 0: 55, 2: 54, 4: 53, 7: 51, 1: 50, 9: 49, 5: 48}) Counter({5: 61, 3: 61, 2: 60, 9: 57, 1: 57, 7: 55, 6: 55, 4: 53, 8: 47, 0: 44}) 进一步地,为了便于大家理解“weights (sequence) – a sequence of weights, not necessary summing up to one”这句话,在代码中增加了 weights = 12345. / torch.tensor(class_sample_counts, dtype=torch.float) 大家可以随机修改weight的尺度,观察采样结果 关于采样策略有很多的研究,也有多种现成工具库可以使用,推荐大家看看这个repo 小结 本小结将常用的dataset、dataloader配套方法进行了讲解,包括数据集的拼接、子集挑选、子集划分和sampler。其中sampler是涨点神器,推荐掌握。在sampler中,先通过代码单步调试了解RandomSampler,然后顺藤摸瓜找到SequentialSampler和SubsetRandomSampler, 最后通过两个案例详细介绍涨点神器——WeightedRandomSampler的代码编写。 同时推荐大家拓展阅读关于数据采样策略对模型精度的论文,典型的主题是——长尾分布(Long Tail) 下一小节将介绍另外一个涨点首选神器——数据增强模块。先从torchvision的transform模块讲起,然后拓展到更强大的Albumentations Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.4-transforms.html":{"url":"chapter-3/3.4-transforms.html","title":"3.4 transforms","keywords":"","body":"3.4 transforms 本节分为两部分,首先介绍pytorch的图像数据增强函数库——transforms,深入分析它的工作机制,并阐述常用的方法。 transforms简介 数据增强(Data augmentation)已经成为深度学习时代的常规做法,数据增强目的是为了增加训练数据的丰富度,让模型接触多样性的数据以增加模型的泛化能力。更多关于数据增强的概念,推荐大家阅读《动手学》的image-augmentation章节 通常,数据增强可分为在线(online)与离线(offline)两种方式,离线方式指的是在训练开始之前将数据进行变换,变换后的图片保存到硬盘当中,在线方式则是在训练过程中,每一次加载训练数据时对数据进行变换,以实现让模型看到的图片都是增强之后的。实际上,这两种方法理论上是等价的,一般的框架都采用在线方式的数据增强,pytorch的transforms就是在线方式。后续不做特别说明,数据增强专指在线数据增强。 transforms是广泛使用的图像变换库,包含二十多种基础方法以及多种组合功能,通常可以用Compose把各方法串联在一起使用。大多数的transforms类都有对应的 functional transforms ,可供用户自定义调整。transforms提供的主要是PIL格式和Tensor的变换,并且对于图像的通道也做了规定,默认情况下一个batch的数据是(B, C, H, W) 形状的张量。 在transforms库中包含二十多种变换方法,那么多的方法里应该如何挑选,以及如何设置参数呢? 这是值得大家仔细思考的地方,数据增强的方向一定是测试数据集中可能存在的情况。 举个例子,做人脸检测可以用水平翻转(如前置相机的镜像就是水平翻转),但不宜采用垂直翻转(这里指一般业务场景,特殊业务场景有垂直翻转的人脸就另说)。因为真实应用场景不存在倒转(垂直翻转)的人脸,因此在训练过程选择数据增强时就不应包含垂直翻转。 运行机制 在正式介绍transforms的系列方法前,先来了解pytorch对数据增强的运行机制,我们继续通过debug模式在dataloader部分进行调试,观察一张图片是如何进行数据增强的。 同样的,我们回顾2.2小结的COVID-19代码,在dataloader中设置断点,进行debug。这里有一个小技巧,我们可以到dataset的getitem函数里设置一个断点,因为我们前面知道了图像的读取及处理是在dataset的getitem里,因此可以直接进入dataset,不必在dataloader里绕圈。当然,前提是需要大家熟悉dataloader的运行机制。 在第48行img = self.transform(img)设置断点,可以看到self.transform是一个Compose对象,继续进入self.transform(img) 来到 transforms.py 的Compose类的 __call__函数:这个函数的逻辑是依次调用compose对象里的变换方法,从此处也可看出数据是串联的,上一个方法的输出是下一个方法输入,这就要求各个方法之间传输的数据对象要一致。继续单步运行,进入第一个t(img), 第一个t是Resize。 来到D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch\\nn\\modules\\module.py的Module类的_call_impl函数:Module类是pytorch模型、网络层的核心,这个类有1854行代码,下一章将详细介绍模型模块以及Module。在这里我们只需要了解Resize这个变换方法是一个Module类,它实际的调用在1102行,进入1102行会来到Resize类的forward方法。 来到 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\transforms.py的Resize类的forward函数:可以看到此函数仅一行代码F.resize(img, self.size, self.interpolation, self.max_size, self.antialias),继续进入它。 来到D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch\\nn\\functional.py 的resize函数:functional模块是对一系列操作的封装,这里看到419行,resize功能的实现。继续进入419行。 来到 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\functional_pil.py的resize函数:这里终于进入到最核心的Resize方法实现了,这个函数里需要时间缩放的w,h,这里的计算代码非常值得大家学习,同时函数进来之后对参数的一系列判断,也值得借鉴。从此函数可以看到它利用了PIL库的resize函数对PIL图像进行resize。最终对图像resize是这265行代码:return img.resize(size[::-1], interpolation) 然后依次返回,回到transforms.py的Compose类的call函数,此时 img = t(img)完成了1次对图像的变换。接着继续执行for循环,把compose中的变换执行完毕,就对图像做完了变换、增强。 总结一下,一开始采用transforms.Compose把变换的方法包装起来,放到dataset中;在dataloader依次读数据时,调用dataset的getitem,每个sample读取时,会根据compose里的方法依次地对数据进行变换,以此完成在线数据增强。而具体的transforms方法通常包装成一个Module类,具体实现会在各functional中。 熟悉此运行机制,有助于大家今后自己编写数据增强方法,嵌入到自己的工程中。 系列API 通过单步debug,了解了transforms运行机制,下面看看transforms库提供的一系列方法及使用。更全面的方法介绍请直接看官方文档,官方文档配备了一个图解transforms的教程 这里不再一一展开各方法介绍,只挑选几个代表性的方法展开讲解,其余方法可以到第一版中阅读transforms的二十二个方法 在这里,结合COVID-2019 X光分类场景进行系列API的使用介绍。主要内容包括: 具体变换方法使用:resize、Normalize、totensor、FiveCrop、TenCrop 特殊方法使用:RandomChoice、RandomOrder、Lambda 自动数据增强:AutoAugmentPolicy、AutoAugment、RandAugment 具体变换方法使用 Compose 此类用于包装一系列的transforms方法,在其内部会通过for循环依次调用各个方法。这个在上面的代码调试过程中已经分析清楚了。 Resize Resize(size, interpolation=, max_size=None, antialias=None) 功能:支持对PIL或Tensor对象的缩放,关于size的设置有些讲究,请结合代码尝试int方式与tuple方式的差异。int方式是会根据长宽比等比例的缩放图像,这个在AlexNet论文中提到先等比例缩放再裁剪出224*224的正方形区域。 ToTensor 功能:将PIL对象或nd.array对象转换成tensor,并且对数值缩放到[0, 1]之间,并且对通道进行右移。具体地,来看源代码 ...\\Lib\\site-packages\\torchvision\\transforms\\functional.py 下的to_tensor函数 ···python img = img.permute((2, 0, 1)).contiguous() if isinstance(img, torch.ByteTensor): ​ return img.to(dtype=default_float_dtype).div(255) 对PIL对象的通道进行右移,由原来的(H x W x C)变为了(C x H x W) , 接着对数值进行除以255,若是正常的图像像素,那么数值被缩放到了[0, 1]之间。 Normalize Normalize(mean, std, inplace=False) 功能:对tensor对象进行逐通道的标准化,具体操作为减均值再除以标准差,一般使用imagenet的128万数据R\\G\\B三通道统计得到的mean和std,mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]。相信大家今后再看到这一组数据就明白它们到底怎么来的了。 FiveCrop&TenCrop 这两个方法是AlexNet论文中提及,是一个涨点神器,具体使用方式是一张图片经过多区域裁剪得到5/10张图片,同时放到模型进行推理,得到5/10个概率向量,然后取它们的平均/最大/最小得到这一张图片的概率。 FiveCrop表示对图片进行上下左右以及中心裁剪,获得 5 张图片,并返回一个list,这导致我们需要额外处理它们,使得他们符合其它transforms方法的形式——3D-tensor。 TenCrop同理,在FiveCrop的基础上增加水平镜像,获得 10 张图片,并返回一个 list。 它们的使用与普通的transforms有一点区别,需要代码层面的一些改变,下面就通过具体例子讲解它们的注意事项。 代码 授人以渔:其余的二十多个不在一一介绍,只需要到官方文档上查看,并到配套代码中运行,观察效果即可。 特殊方法使用 PyTorch 不仅可设置对数据的操作,还可以对这些操作进行随机选择、组合,让数据增强更加灵活。 具体有以下4个方法: Lambda RandomChoice RandomOrder RandomApply Lambda 功能:可进行自定义的操作,例如上文的FiveCrop中利用lambda很好的处理了上下游transforms数据维度不一致的问题。transforms.Lambda(lambda crops: torch.stack([ToTensor()(crop) for crop in crops])) RandomChoice 功能:以一定的概率从中选择一个变换方法执行。 RandomOrder 功能:随机打乱一串变换方法。 RandomApply 功能:以一定的概率执行这一串变换方法。这与RandomChoice的区别仅在于它将一组变换看成一个选择单位,RandomChoice是一次选一个,RandomApply是一次选一组(list) 具体使用可配合配套代码 自动数据增强 从transforms丰富的变换方法以及灵活的组合函数可以知道,数据增强的策略可以千变万化,怎样的策略会更好?Google Brain团队就针对这个问题,利用它们的钞能力进行研究,采用RNN网络自动搜索组合策略,寻找较好的数据增强策略,详细可以看这篇文章AutoAugment: Learning Augmentation Strategies from Data。文章中利用RNN搜索出来的策略,可以在Imagenet、Cifar-10和SVHN三个数据集上达到当时的SOTA,pytorch中也提供了基于AutoAugment论文的三个数据集的自动数据增强策略,下面一起来学习它们。 AutoAugmentPolicy 通过论文AutoAugment: Learning Augmentation Strategies from Data我们知道它研究出了针对三个数据集的数据增强策略,在pytorch中同样的提供对应的策略,并设计了AutoAugmentPolicy来指示,直接看源代码,一目了然envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\autoaugment.py: class AutoAugmentPolicy(Enum): \"\"\"AutoAugment policies learned on different datasets. Available policies are IMAGENET, CIFAR10 and SVHN. \"\"\" IMAGENET = \"imagenet\" CIFAR10 = \"cifar10\" SVHN = \"svhn\" AutoAugment torchvision.transforms.AutoAugment(policy: torchvision.transforms.autoaugment.AutoAugmentPolicy = , interpolation: torchvision.transforms.functional.InterpolationMode = , fill: Optional[List[float]] = None) 功能:自动数据增强方法的封装,支持三种数据增强策略,分别是IMAGENET、CIFAR10 和SVHN 参数: policy :需要是AutoAugmentPolicy类 interpolation:设置插值方法 fill :设置填充像素的像素值,默认为0,黑色。 AutoAugment也是一个Module类,具体的变换操作在forward()函数中体现,建议大家看看源代码,pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\autoaugment.py 里面有详细的三组数据增强策略的顺序与参数 例如ImageNet的数据增强策略总共有25组变换,共50个变换: return [ ((\"Posterize\", 0.4, 8), (\"Rotate\", 0.6, 9)), ((\"Solarize\", 0.6, 5), (\"AutoContrast\", 0.6, None)), ((\"Equalize\", 0.8, None), (\"Equalize\", 0.6, None)), ((\"Posterize\", 0.6, 7), (\"Posterize\", 0.6, 6)), ((\"Equalize\", 0.4, None), (\"Solarize\", 0.2, 4)), ((\"Equalize\", 0.4, None), (\"Rotate\", 0.8, 8)), ((\"Solarize\", 0.6, 3), (\"Equalize\", 0.6, None)), ((\"Posterize\", 0.8, 5), (\"Equalize\", 1.0, None)), ((\"Rotate\", 0.2, 3), (\"Solarize\", 0.6, 8)), ((\"Equalize\", 0.6, None), (\"Posterize\", 0.4, 6)), ((\"Rotate\", 0.8, 8), (\"Color\", 0.4, 0)), ((\"Rotate\", 0.4, 9), (\"Equalize\", 0.6, None)), ((\"Equalize\", 0.0, None), (\"Equalize\", 0.8, None)), ((\"Invert\", 0.6, None), (\"Equalize\", 1.0, None)), ((\"Color\", 0.6, 4), (\"Contrast\", 1.0, 8)), ((\"Rotate\", 0.8, 8), (\"Color\", 1.0, 2)), ((\"Color\", 0.8, 8), (\"Solarize\", 0.8, 7)), ((\"Sharpness\", 0.4, 7), (\"Invert\", 0.6, None)), ((\"ShearX\", 0.6, 5), (\"Equalize\", 1.0, None)), ((\"Color\", 0.4, 0), (\"Equalize\", 0.6, None)), ((\"Equalize\", 0.4, None), (\"Solarize\", 0.2, 4)), ((\"Solarize\", 0.6, 5), (\"AutoContrast\", 0.6, None)), ((\"Invert\", 0.6, None), (\"Equalize\", 1.0, None)), ((\"Color\", 0.6, 4), (\"Contrast\", 1.0, 8)), ((\"Equalize\", 0.8, None), (\"Equalize\", 0.6, None)), ] 特别说明:这里反复提到的自动数据增强在实际应用中它们是固定的一组变换策略,这是获得这一组策略的过程是通过强化学习自动搜素的,所以称之为自动数据增强策略。 RandAugment RandAugment是进行N次(num_ops )变换,变换方法从策略池中随机挑选。pytorch官方文档对于RandAugment给了较高的评价——“RandAugment is a simple high-performing Data Augmentation technique which improves the accuracy of Image Classification models.” 参数: num_ops :执行多少次变换 magnitude :每个变换的强度, num_magnitude_bins:与变化强度的采样分布有关 如果对autoaugmentation不熟悉的话,理解RandAugment的参数可能有点困难,这里结合代码看一看就知道了。 RandAugment仍旧是一个Module类,来看它的forward(), def forward(self, img: Tensor) -> Tensor: \"\"\" img (PIL Image or Tensor): Image to be transformed. Returns: PIL Image or Tensor: Transformed image. \"\"\" fill = self.fill if isinstance(img, Tensor): if isinstance(fill, (int, float)): fill = [float(fill)] * F.get_image_num_channels(img) elif fill is not None: fill = [float(f) for f in fill] for _ in range(self.num_ops): op_meta = self._augmentation_space(self.num_magnitude_bins, F.get_image_size(img)) op_index = int(torch.randint(len(op_meta), (1,)).item()) op_name = list(op_meta.keys())[op_index] magnitudes, signed = op_meta[op_name] magnitude = float(magnitudes[self.magnitude].item()) if magnitudes.ndim > 0 else 0.0 if signed and torch.randint(2, (1,)): magnitude *= -1.0 img = _apply_op(img, op_name, magnitude, interpolation=self.interpolation, fill=fill) return img 前面的代码段主要是根据规则获取需要进行的变换方法名称:op_name;变换的强度:magnitude,从 op_index = int(torch.randint(len(op_meta), (1,)).item()) op_name = list(op_meta.keys())[op_index] 这两行代码可以看到,每次采用的变换是随机的选择。 而变换强度magnitude则是根据一个区间里选择,不同变换方法的强度区间在这里: def _augmentation_space(self, num_bins: int, image_size: List[int]) -> Dict[str, Tuple[Tensor, bool]]: return { # op_name: (magnitudes, signed) \"Identity\": (torch.tensor(0.0), False), \"ShearX\": (torch.linspace(0.0, 0.3, num_bins), True), \"ShearY\": (torch.linspace(0.0, 0.3, num_bins), True), \"TranslateX\": (torch.linspace(0.0, 150.0 / 331.0 * image_size[0], num_bins), True), \"TranslateY\": (torch.linspace(0.0, 150.0 / 331.0 * image_size[1], num_bins), True), \"Rotate\": (torch.linspace(0.0, 30.0, num_bins), True), \"Brightness\": (torch.linspace(0.0, 0.9, num_bins), True), \"Color\": (torch.linspace(0.0, 0.9, num_bins), True), \"Contrast\": (torch.linspace(0.0, 0.9, num_bins), True), \"Sharpness\": (torch.linspace(0.0, 0.9, num_bins), True), \"Posterize\": (8 - (torch.arange(num_bins) / ((num_bins - 1) / 4)).round().int(), False), \"Solarize\": (torch.linspace(255.0, 0.0, num_bins), False), \"AutoContrast\": (torch.tensor(0.0), False), \"Equalize\": (torch.tensor(0.0), False), } TrivialAugmentWide TrivialAugment是采用NAS技术搜索得到的一组数据增强策略,推荐阅读原文TrivialAugment: Tuning-free Yet State-of-the-Art Data Augmentation 使用方法也非常简单,直接看代码即可。 想了解细节,请查看D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\transforms\\autoaugment.py TrivialAugment核心 def _augmentation_space(self, num_bins: int) -> Dict[str, Tuple[Tensor, bool]]: return { # op_name: (magnitudes, signed) \"Identity\": (torch.tensor(0.0), False), \"ShearX\": (torch.linspace(0.0, 0.99, num_bins), True), \"ShearY\": (torch.linspace(0.0, 0.99, num_bins), True), \"TranslateX\": (torch.linspace(0.0, 32.0, num_bins), True), \"TranslateY\": (torch.linspace(0.0, 32.0, num_bins), True), \"Rotate\": (torch.linspace(0.0, 135.0, num_bins), True), \"Brightness\": (torch.linspace(0.0, 0.99, num_bins), True), \"Color\": (torch.linspace(0.0, 0.99, num_bins), True), \"Contrast\": (torch.linspace(0.0, 0.99, num_bins), True), \"Sharpness\": (torch.linspace(0.0, 0.99, num_bins), True), \"Posterize\": (8 - (torch.arange(num_bins) / ((num_bins - 1) / 6)).round().int(), False), \"Solarize\": (torch.linspace(255.0, 0.0, num_bins), False), \"AutoContrast\": (torch.tensor(0.0), False), \"Equalize\": (torch.tensor(0.0), False), } 小结 本小节详细剖析transforms运行机制,熟悉内部工作原理,大家可自行编写变换方法嵌入模型训练中。同时教授大家学习使用transforms的二十多种方法的方法——授人以渔,最后探讨了自动数据增强策略的原理及代码实践。 希望大家利用好数据增强,给自己的模型提升性能,切记数据增强的方向是朝着测试集(真实应用场景情况下)的数据分布、数据情况去变换,切勿盲目应用。 本章节介绍albumentations,但由于本章未涉及图像分割、目标检测,以及本章内容也不少了,因此将albumentations放到后续章节,适时进行讲解。 预告:原计划在本章节介绍albumentations,但由于本章未涉及图像分割、目标检测,以及本章内容也不少了,因此将albumentations放到后续章节,适时进行讲解。 为什么要用albumentations,pytorch的transforms有什么不足呢? 当然有不足的, pytorch的transforms在处理图像分割与目标检测这一类需要图像与标签同时变换的时候不太方便。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-3/3.5-torchvision-dataset.html":{"url":"chapter-3/3.5-torchvision-dataset.html","title":"3.5 torchvision 经典dataset学习","keywords":"","body":"3.5 torchvision 经典dataset学习 前面已经学习了Dataset,DataLoader,以及常用的函数,通常足以应对大多数需求,但距离熟练编写自己的Dataset可能还有一段距离。 为了让大家能轻松掌握各种情况下的dataset编写,本小节对torchvision中提供的几个常见dataset进行分析,观察它们的代码共性,总结编写dataset的经验。 X-MNIST 由于MNIST数据使用广泛,在多领域均可基于这个小数据集进行初步的研发与验证,因此基于MNIST数据格式的各类X-MNIST数据层出不穷,在mnist.py文件中也提供了多个X-MNIST的编写,这里需要大家体会类继承。 示例表明FashionMNIST、KMNIST两个dataset仅需要修改数据url(mirrors、resources)和类别名称(classes),其余的函数均可复用MNIST中写好的功能,这一点体现了面向对象编程的优点。 来看dataset的 getitem,十分简洁,因为已经把图片和标签处理好,存在self.data和self.targets中使用了: def __getitem__(self, index: int) -> Tuple[Any, Any]: img, target = self.data[index], int(self.targets[index]) img = Image.fromarray(img.numpy(), mode='L') if self.transform is not None: img = self.transform(img) if self.target_transform is not None: target = self.target_transform(target) return img, target 代码参阅:D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\datasets\\mnist.py cifar-10 cifar-10是除MNIST之外使用最多的公开数据集,同样,让我们直接关注其 Dataset 实现的关键部分 def __getitem__(self, index: int) -> Tuple[Any, Any]: img, target = self.data[index], self.targets[index] # doing this so that it is consistent with all other datasets # to return a PIL Image img = Image.fromarray(img) if self.transform is not None: img = self.transform(img) if self.target_transform is not None: target = self.target_transform(target) return img, target 核心代码还是这一行: img, target = self.data[index], self.targets[index] 接下来,去分析data和self.targets是如何从磁盘上获取的?通过代码搜索可以看到它们来自这里(D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\datasets\\cifar.py CIFAR10 类的 init函数): # now load the picked numpy arrays for file_name, checksum in downloaded_list: file_path = os.path.join(self.root, self.base_folder, file_name) with open(file_path, 'rb') as f: entry = pickle.load(f, encoding='latin1') self.data.append(entry['data']) if 'labels' in entry: self.targets.extend(entry['labels']) else: self.targets.extend(entry['fine_labels']) self.data = np.vstack(self.data).reshape(-1, 3, 32, 32) self.data = self.data.transpose((0, 2, 3, 1)) # convert to HWC 这一段的作用于MNIST的_load_data(), 我们的_get_img_info()一样,就是读取数据信息。 总结: getitem函数中十分简洁,逻辑简单 初始化时需完成数据信息的采集,存储到变量中,供getitem使用 VOC 之前讨论的数据集主要用于教学目的,比较复杂的目标检测数据是否具有较高的编写难度?答案是,一点也不,仍旧可以用我们分析出来的逻辑进行编写。 下面来看第一个大规模应用的目标检测数据集——PASCAL VOC, D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\datasets\\voc.py的 VOCDetection类的getitem函数: def __getitem__(self, index: int) -> Tuple[Any, Any]: \"\"\" Args: index (int): Index Returns: tuple: (image, target) where target is a dictionary of the XML tree. \"\"\" img = Image.open(self.images[index]).convert(\"RGB\") target = self.parse_voc_xml(ET_parse(self.annotations[index]).getroot()) if self.transforms is not None: img, target = self.transforms(img, target) return img, target 更简洁了,与我们的案例中的getitem一样一样的,那么images和annotations从哪里来?相信大家已经知道答案了,那就是初始化的时候根据数据格式、数据组织结构,从磁盘中读取。 COCO 说到目标检测就不得不提COCO数据集,COCO数据集是微软提出的大规模视觉数据集,主要用于目标检测,它从数据量、类别量都远超VOC,对于深度学习模型的落地应用起到了推动作用。 对于CV那么重要的COCO,它的dataset难吗?答案是,不难。反而更简单了,整个类仅40多行。 getitem函数连注释都显得是多余的: def __getitem__(self, index: int) -> Tuple[Any, Any]: id = self.ids[index] image = self._load_image(id) target = self._load_target(id) if self.transforms is not None: image, target = self.transforms(image, target) return image, target 其实,这一切得益于COCO的应用过于广泛,因此有了针对COCO数据集的轮子——pycocotools,它非常好用,建议使用COCO数据集的话,一定要花几天时间熟悉pycocotools。pycocotools把getitem需要的东西都准备好了,因此这个类只需要40多行代码。 小结 本章从数据模块中两个核心——Dataset&Dataloader出发,剖析pytorch是如何从硬盘中读取数据、组装数据和处理数据的。在数据处理流程中深入介绍数据预处理、数据增强模块transforms,并通过notebook的形式展示了常用的transforms方法使用,最后归纳总结torchvision中常见的dataset,为大家将来应对五花八门的任务时都能写出dataset代码。 下一章将介绍模型模块。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/":{"url":"chapter-4/","title":"第四章 PyTorch 模型模块","keywords":"","body":"第四章 PyTorch 模型模块 第四章 PyTorch 模型模块 4.1 Module&Parameter 4.2 Module的容器 4.3 常用网络层 4.4 Module常用API函数 4.5 Hook函数及Grad-CAM 4.6 经典模型代码分析 4.7 权重初始化方法 第四章简介 上一章介绍了数据相关的Dataset、DataLoader、transforms,已经能把数据从磁盘中有序的读取并处理以及加载成batch形式。接下来就需要一个强大的模型来处理它,本章就针对模型部分进行展开,这也是深度学习最核心的地方,其中包括一个模型如何创建各网络层、各网络层如何搭建、参数如何管理与初始化、如何截取某些层的特征图等一系列问题。 首先介绍核心类——Module 再介绍常用的模块容器——Containers 接着讲解常用网络层的使用 再学习module常用函数与hook函数应用 最后介绍权重初始化方法——nn.init Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.1-module&Parameter.html":{"url":"chapter-4/4.1-module&Parameter.html","title":"4.1 Module&Parameter","keywords":"","body":"4.1 Module & parameter Module初认识 深度学习通常通过深度神经网络实现,这些网络由多个层组成,我们通常称之为模型(Model),一个模型包含很多个网络层,多个网络层拼接构建成一个模型。在pytorch中模型是一个Module,各网络层、模块也是Module,本小节就介绍模型/模块的抽象——Module。后续不加以说明的话,模型、模块、网络层都可指代Module。 Module是所有神经网络的基类,所有的模型都必须继承于Module类,并且它可以嵌套,一个Module里可以包含另外一个Module。要想理解清楚这句话就必须清楚了解一个Module是如何工作的。在第二章我们就构建了一个Module——TinnyCNN,第三章讲解transform的时候也用到了Module,并且知道它的前向传播具体执行是在forward()函数当中,其实Module定义了一些列属性来管理模块的功能,分别用8个有序字典进行管理,分别是: self._modules = OrderedDict() self._parameters = OrderedDict() self._buffers = OrderedDict() self._backward_hooks = OrderedDict() self._forward_hooks = OrderedDict() self._forward_pre_hooks = OrderedDict() self._state_dict_hooks = OrderedDict() self._load_state_dict_pre_hooks = OrderedDict() 它们的作用分别是: modules : 存储管理nn.Module类 parameters: 存储管理nn.Parameter类 buffers:存储管理缓冲属性,如BN层中的running_mean *_hooks:存储管理钩子函数 讲到这,大家估计很懵,因为与前面接触到的内容完全搭不上边。但是这些又是Module的核心知识点,为了降低大家的学习曲线斜率,在这里暂且只需要知道一个Module有这些关键属性用于管理Module,以及在哪里找到它们——debug模式下的Protected Attributes看到它们的详情。 forward函数 除了八大核心属性之外,还有一个函数不得不了解,那就是forward函数,forward之于Module等价于getitem之于Dataset。forward函数是模型每次调用的具体实现,所有的模型必须实现forward函数,否则调用时会报错 Traceback (most recent call last): File \"E:/pytorch-tutorial-2nd/code/chapter-2/02_COVID_19_cls.py\", line 150, in main() File \"E:/pytorch-tutorial-2nd/code/chapter-2/02_COVID_19_cls.py\", line 111, in main outputs = model(data) File \"D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\lib\\site-packages\\torch\\nn\\modules\\module.py\", line 1102, in _call_impl return forward_call(*input, **kwargs) File \"D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\lib\\site-packages\\torch\\nn\\modules\\module.py\", line 201, in _forward_unimplemented raise NotImplementedError NotImplementedError Process finished with exit code 1 到这里,总结一下Module: Module是所有模型的基类 每个module有8个字典管理它的核心属性 一个module可以包含多个子module 一个module相当于一个运算,必须实现forward函数 一个模型的创建 下面通过简单的代码慢慢去熟悉Module,回顾TinnyCNN的创建与使用,可以总结出一个模型的创建需要考虑两个要素: 构建子模块:构建网络所需要的网络层,如卷积层,池化层,全联接层等等 拼接子模块:在forward函数中定义需要执行的功能,即将子模块以一定的方式拼接起来,完成对数据的前向传播 模型的创建就像搭积木,首先收集到需要的基础部件,是三角形、正方形还是六边形,然后以一定的方式拼接起来,如果要一个屋子就先放正方形,然后放三角形。如果需要一个汽车就先放两个圆形,再放一个长方形。 同理,模型搭建也是,先知道有哪些网络层是需要的,那么再init函数里进行初始化,先让此类获得这些网络层可用。具体如何用,需要在forward函数中写清楚。就像下面这个图一样。 知道了Module有哪些关键属性,以及一个模型如何创建,下面回顾2.2小结的COVID-19分类代码,采用debug方式观察TinnyCNN的创建——model = TinnyCNN(2), 以及它的推理: outputs = model(data) TinnyCNN的创建 代码在:code/chapter-2/02_COVID_19_cls.py 模型实例化的代码是这行: model = TinnyCNN(2) 我们打下断点,采用debug运行,进行分析如下: 进入 TinnyCNN 类的init函数:这里进行初始化,可以看到第一行就是调用父类的init函数,父类是Module,因此我们继续step into进去看看; 来到 Module类的init函数:这里会初始化那8个有序字典,以及一些关键属性,如training等。我们跳出去; 回到TinnyCNN 类的init函数:父类init函数结束,就来到自定义的组件定义部分,这里我们需要一个卷积层、一个全连接层供搭积木使用。这里的nn.Conv2d也是一个module,大家可以自行step into进去观察它的创建,这里暂且知道它是一个module即可。同理,nn.Linear也是。init函数里收集了需要搭积木的组件,下面跳出去。 回到主代码:model = TinnyCNN(2),这样一个模型就创建好了,我们可以看到model下面就有了这些属性: 重点看红框的三个内容,分别是convolution_layer、fc和_modules。前两个没啥好说的,是init函数中自定义的类属性名称,而第三个_modules什么时候“悄悄”地记录了我们自己定义的convolution_layer和fc呢? 这就需要大家了解一下python的基础了,请看这行代码: self.convolution_layer = nn.Conv2d(1, 1, kernel_size=(3, 3)) 在类属性赋值的时候,即这行代码中的“=”号,会调用类的__setattr__方法,在module.py的1180行代码是setatrr的实现,里面会将“=”号右边的值放到相应的地方去,如module会放到_modules里,parameter会放到_parameters里。 至此,对于模型的创建流程有了解后,下面看看模型的推理是如何进行的,它可不是简单的进入forward函数就完事了,中间还有复杂的辅助功能,一起往下看。 TinnyCNN的推理 继续采用debug,往下看。 先来到模型调用的地方:outputs = model(data),采用step into进入; 来到Module类的call_impl函数:熟悉python的朋友就疑惑了,为什么进入的是它而不是\\_call__函数?(python规定可被调用的对象,其实现是在__call__\\函数里)其实并没有错,只Module类对call函数重命名了罢了,可以看到1148行 __call__ : Callable[..., Any] = _call_impl 在早期版本的pytorch中还没有这一层包装,请各位专家指导一下为什么采用这种方式? 在_call_impl函数当中才会调用forward函数来实现数据的前向传播,但module除了forward调用之外,还有一些辅助功能,那就是一系列的hook函数的使用,这里暂且放下,后续会展开hook函数的作用。这里只需要关心怎么进入forward的。如果没有设置任何hook函数,则直接进行forward函数的调用 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks or _global_forward_hooks or _global_forward_pre_hooks): return forward_call(*input, **kwargs) step into 进入 return forward_call(input, *kwargs),就会发现来到了自定义的forward函数。 来到TinnyCNN类的forward函数:这里就是我们如何拼接网络层,组装积木的地方了。 通常会在这里调用其他module来完成数据的处理,例如使用nn.Conv2d来进行卷及操作,除了使用module对象,其它的数学运算、功能函数(如torch.nn.functionals里的系列函数)、for循环等都是可以使用的。 值得说的一点是,一些激活函数没有可训练参数,也不是module类,因此会在forward函数中直接调用,而不需要在init中初始化。比如 :out = F.relu(self.conv1(x)) 中的F.relu。 最后要强调一点是:forward函数中需要注意前后层数据的格式,类似transforms的实现一样,上一层的输出一定要对得上下一层的输入,否则会报错,常见的报错是Linear层接收到了不合适的数据。建议大家把TinnyCNN的forward函数的第二行注释掉:# x = x.view(x.size(0),-1),运行代码并观察错误,这个错误是大部分朋友都会遇到的:RuntimeError: mat1 and mat2 shapes cannot be multiplied (12x6 and 36x2)。 到这里一个模型的搭建以及前向推理就很清晰了,构建自己的网络只需要三步: 写一个类继承于Module init函数中把需要的网络层创建好 forward函数中把模型如何搭建的规则写好 Parameter 在Module中有一个重要的对象——Parameter,参数。它继承于Tensor,与Tensor差别不太大,主要作用是用来区分可训练的参数与常规的Tensor。 在这里要做一下说明,权重、参数和超参数,它们的含义。一般情况下模型的权重就表示模型的参数,它们是可训练的,通过反向传播算法不断的更新;而超参数如卷积核大小、学习率、迭代次数是不能通过反向传播算法去更新的。很明显Parameter就指模型的参数,如卷积层的卷积核权重和偏置,Linear层的权重和偏置,BN层的α和β等等。 Module中对于参数是采用_parameters 进行管理的,并且提供相应的api可以对module内所有参数进行调用与读取。回顾2.2 COVID-19的优化器实例化这行代码: optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4) 代码中表示把model.parameters()返回的内容给优化器,让优化器更新model.parameters(),从这里可进一步理解parameter类的作用,以及各网络层它们的参数都会初始化为parameter类。 可以看看 D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torch\\nn\\modules\\conv.py的131行代码 self.weight = Parameter(torch.empty( (out_channels, in_channels // groups, *kernel_size), **factory_kwargs)) 对默认的卷积核采用empty初始化数值,然后包装成Parameter类。 小结 到这里,一个简单模型是如何创建、如何工作的我们就已经讲解完了。但随着深度神经网络的拓扑结构越来越复杂,层数越来越多,只靠上面的方法无法很好的构建神经网络,还需要借助一些容器把固定的模块封装起来,循环地进行调用。下一节将介绍Module的容器,包括以下5个 - - Sequential A sequential container. ModuleList Holds submodules in a list. ModuleDict Holds submodules in a dictionary. ParameterList Holds parameters in a list. Parameter DictHolds parameters in a dictionary. Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.2-containers.html":{"url":"chapter-4/4.2-containers.html","title":"4.2 Module的容器","keywords":"","body":"4.2 Module容器——Containers 容器的概念出现在日常生活的方方面面,每天喝水的杯子用来装一些水,书包用来装一些办公用品,衣柜用来装一些衣服。因此,我们可以很容易地抽象出容器的概念,它用于将一些物品放置在某个地方,并进行有效的管理和使用。 在深度学习模型里面,有一些网络层需要放在一起使用,如 conv + bn + relu 的组合。Module的容器是将一组操作捆绑在一起的工具,在pytorch官方文档中把Module也定义为Containers,或许是因为“Modules can also contain other Modules”。 对于Module类可查看4.1小结,这里详细介绍两个常用的容器Sequential与ModuleList,同时介绍ModuleDict,ParameterList,ParameterDict。 Sequential sequential是pytorch里使用最广泛的容器,它的作用是将一系列网络层按固定的先后顺序串起来,当成一个整体,调用时数据从第一个层按顺序执行到最后一个层。回顾一下transforms的Compose就可以体会到按顺序的含义了。 sequential可以直接传module,也可以传OrderedDict,OrderedDict可以让容器里的每个module都有名字,方便调用。 请看两段官方代码: model = nn.Sequential( nn.Conv2d(1,20,5), nn.ReLU(), nn.Conv2d(20,64,5), nn.ReLU() ) # Using Sequential with OrderedDict. This is functionally the # same as the above code model = nn.Sequential(OrderedDict([ ('conv1', nn.Conv2d(1,20,5)), ('relu1', nn.ReLU()), ('conv2', nn.Conv2d(20,64,5)), ('relu2', nn.ReLU()) ])) 来看一个实际案例: AlexNet是新一代CNN的开山之作,也是这一轮深度学习潮流里,计算机视觉任务的开山之作。对于现代CNN,通常会把前面的卷积层、池化层称为特征提取部分,最后的全连接层当作分类器,这一点在代码编写上将有所体现。 例如,在D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\alexnet.py 下的AlexNet,它把前面的卷积和池化都放到Sequential这个容器当中,并且命名为self.features。在最后还有一个名为self.classifier的Sequential容器,它包含3个Linear层及激活函数、Dropout。这里正如下图所示,把模型大卸两块。 def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.features(x) x = self.avgpool(x) x = torch.flatten(x, 1) x = self.classifier(x) return x ​ Sequential的调用 Sequential容器把一堆module包起来之后,它在forward中是如何使用的呢? 下面请看配套代码,主要采用debug模式,观察Alexnet的forward中,两个Sequential容器是如何forward的,同时也要看看Alexnet这个模型的属性。 在 output = model(fake_input) 设置断点,step into,然后进入熟悉的_call_impl,关于module内部的代码这里直接略过,不熟悉的朋友请回到4.1小节阅读。 这里直接跳到Alexnet类的forward函数,第一行就是执行x = self.features(x),继续step into观察这个sequential容器是如何工作的,进入它 来到了Module类下的_call_impl函数:没错,又来到了_call_impl,因为sequential它也是一个module,因此在调用self.features的时候,会进入_call_impl, 下面需要大家有耐心的进入self.features的forward函数,其实就是1102行进去; 来到Sequential类的forward函数,它十分简洁,如下所示: def forward(self, input): ​ for module in self: ​ input = module(input) ​ return input 这段代码是不是十分的熟悉呢? transforms当中也是这样去实现一个Compose里的变换的。 从此处可知道,Sequential类的功能是将一系列网络层按固定的先后顺序串起来,当成一个整体,调用时数据从第一个层按顺序执行到最后一个层,各层之间的数据必须能对接起来。 接着回到 Alexnet的forward函数下,观察一下Alexnet这个module的属性 重点看_modules属性,它有3个key-value,其中有2个是Sequential类,因为Sequential属于module类,继续展开一个Sequential来看看 可以看到该容器下的一系列网络层,并且是排了序的,这些对于后续理解网络结构、理解网络权重加载的key是十分重要的 ModuleList ModuleList 是将各个网络层放到一个“列表”中,便于迭代的形式调用。 ModuleList与python List的区别 这里注意是“列表”而不是列表,因为ModuleList管理的module与python的List管理的module是有不同的,大家是否还记得module的setattr函数?在那里会对类属性进行判断管理,只有ModuleList里的网络层才会被管理,如果是List里的网络层则不会被管理,也就不能迭代更新了。 ModuleList 使用示例 假设要构建一个10层的全连接网络,如果用Sequential,那就要手写10行nn.Linear,而用ModuleList是这样的: class MyModule(nn.Module): def __init__(self): super(MyModule, self).__init__() self.linears = nn.ModuleList([nn.Linear(10, 10) for i in range(10)]) # self.linears = [nn.Linear(10, 10) for i in range(10)] # 观察model._modules,将会是空的 def forward(self, x): for sub_layer in self.linears: x = sub_layer(x) return x 需要对python的 list进行一个ModuleList封装,这样才可以在model的_modules属性下看到创建的10个Linear层。 推荐大家看看class ModuleList(Module)的实现,里边并不会像Sequential那样提供forward,即管理的网络层由用户自行调用,可以for循环全用,也可以通过if判断,有条件的选择部分网络层使用。同时ModuleList也提供了类似List的方法,insert\\append\\extend等。 ModuleDict ModuleList可以像python的List一样管理各个module,但对于索引而言有一些不方便,因为它没有名字,需要记住是第几个元素才能定位到指定的层,这在深度神经网络中有一点不方便。 而ModuleDict就是可以像python的Dict一样为每个层赋予名字,可以根据网络层的名字进行选择性的调用网络层。 请看代码 class MyModule2(nn.Module): def __init__(self): super(MyModule2, self).__init__() self.choices = nn.ModuleDict({ 'conv': nn.Conv2d(3, 16, 5), 'pool': nn.MaxPool2d(3) }) self.activations = nn.ModuleDict({ 'lrelu': nn.LeakyReLU(), 'prelu': nn.PReLU() }) def forward(self, x, choice, act): x = self.choices[choice](x) x = self.activations[act](x) return x ParameterList & ParameterDict 除了Module有容器,Parameter也有容器。与ModuleList和ModuleDict类似的,Paramter也有List和Dict,使用方法一样,这里就不详细展开,可以参考Module的容器。 可以看两段官方文档代码感受一下 class MyModule(nn.Module): def __init__(self): super(MyModule, self).__init__() self.params = nn.ParameterDict({ 'left': nn.Parameter(torch.randn(5, 10)), 'right': nn.Parameter(torch.randn(5, 10)) }) def forward(self, x, choice): x = self.params[choice].mm(x) return x # ParameterList class MyModule(nn.Module): def __init__(self): super(MyModule, self).__init__() self.params = nn.ParameterList([nn.Parameter(torch.randn(10, 10)) for i in range(10)]) def forward(self, x): # ParameterList can act as an iterable, or be indexed using ints for i, p in enumerate(self.params): x = self.params[i // 2].mm(x) + p.mm(x) return x 小结 随着深度神经网络拓扑结构越来越复杂,网络模块多,杂,乱,因此需要Module容器来管理、组织各个网络层,便于forward函数中调用。 使用频率最高的是Sequential,其次是ModuleList,其余的均为进阶用法,在各类魔改网络中才会涉及。 这里深刻理解Sequential的机制、理解一个module是如何把Sequential里的module管理到自己的_modules属性中,对于后续使用模型是非常重要的。 熟悉了Module类,各种容器封装,下一小节将介绍一些常用的网络层,如卷积、池化、全连接、激活函数等。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.3-common-module.html":{"url":"chapter-4/4.3-common-module.html","title":"4.3 常用网络层","keywords":"","body":"4.3 常用网络层 本小节将介绍几个常用的layers,核心目的是传授如何学习各layers的方法,今后更多layers也可自行从官方文档中学习。 Convolutional Layers 卷积整体分两大类,正常卷积与转置卷积(Transpose Convolution),除此之外还有Lazy系列的正常卷积与转置卷积,Lazy系列就是懒惰系列,为那些懒得计算输入特征图的通道数的使用者而设计,经过第一次forward之后,该网络层的in_channels将被确定。 下面通过官方文档详细学习Conv2d这个卷积层,在文档中会详细介绍该层的功能,各参数含义,计算公式,以及示例代码。 torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros') 主要功能:对多个二维平面组成的信号进行二维卷积 主要参数: in_channels (int) – Number of channels in the input image。输入这个网络层的图像的通道数是多少。 out_channels (int) – Number of channels produced by the convolution。此网络层输出的特征图的通道数是多少,等价于卷积核数量是多少。 kernel_size (int or tuple) – Size of the convolving kernel。卷积核大小。 stride (int or tuple, optional) – Stride of the convolution. Default: 1。卷积核卷积过程的步长。 padding (int, tuple or str, optional) – Padding added to all four sides of the input. Default: 0。对于输入图像的四周进行填充的数量进行控制,可指定填充像素数量,也可以指定填充模式,如\"same\", \"valid\",具体含义参见文档,这是从TF中借鉴过来的。 padding_mode (string, optional) – 'zeros', 'reflect', 'replicate' or 'circular'. Default: 'zeros'。填充的像素值如何确定。默认填充0。 dilation (int or tuple, optional) – Spacing between kernel elements. Default: 1。孔洞卷积的孔洞大小。 groups (int, optional) – Number of blocked connections from input channels to output channels. Default: 1。分组卷积的分组。 bias (bool, optional) – If True, adds a learnable bias to the output. Default: True。是否采用偏置。 nn.Conv2d是图像领域里99%模型都用到的,它的计算公式及细节需要大家了如指掌,具体公式如下:` 这里建议大家结合各种动图进行学习,推荐这个repo, Pooling Layers Pooling layer叫池化层,池化是形象词,就像下雨天篮球场上低洼的地方会聚集周围的雨水一样,由大变小的过程。 自然它的作用是将特征图分辨率变小,通常减小一半。如下图所示,相同颜色的区域”池化“为1个像素,4x4的图像变为了2x2的图像。 图片来源:https://www.geeksforgeeks.org/cnn-introduction-to-pooling-layer/ 1个像素代替4个像素,那应该用什么值呢?针对这个问题,可对池化层进行划分,分为最大值池化、平均值池化、分数阶池化、基于范数的池化。分别对应torch.nn中的Maxpool, Avgpool, FractionalMaxPool, LPPool。 由于它们只是在计算像素时才用的方法不同,下面就以Maxpool为例讲解池化层。 torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, dilation=1, return_indices=False, ceil_mode=False) 功能:2D最大池化 参数: kernel_size – 池化窗口大小 stride – 滑窗步长 padding – 原图填充大小 dilation – 孔洞大小 return_indices – 是否返回最大值所在位置,主要在 torch.nn.MaxUnpool2d 中使用,是上采样的一种策略 ceil_mode – 当无法整除时,是向下取整还是向上取整,默认为向下取整。 池化层输出特征图的大小计算公式如下,细心的朋友会发现它与卷积层是一样的。 针对最大池化还有一个特殊的地方是它可以记录最大值所在的位置,供上采样时(MaxUnpool2d)所用,这个在图像分割任务中会有涉及。MaxUnpool2d的使用非常简单,参数设置很容易。原型如下: torch.nn.MaxUnpool2d(kernel_size, stride=None, padding=0),具体使用可看配套代码。 自适应池化层 上面针对池化像素如何取值进行划分,其实针对窗口大小的选择也可划分,还有另外一种特殊的池化方法,那就是AdaptiveXpool, 它的作用是自适应窗口大小,保证经过池化层之后的图像尺寸是固定的,这个在接入全连接层之前经常会见到。 使用也很方便,只需要设置想要的输出大小即可,详细可见配套代码。torch.nn.AdaptiveMaxPool2d(output_size, return_indices=False) Padding Layers Padding layer在许多魔改网络中常用到,功能是给特征图周围填充一定的像素,调整特征图分辨率的一种方法。既然是填充就涉及两个问题,填充多少个像素?像素应该如何确定? 针对第二个问题,可将padding layer划分为三类,镜像填充、边界重复填充,指定值填充、零值填充,分别对应nn的三大类,nn.ReflectionPad2d, nn.ReplicationPad2d, nn.ZeroPad2d, nn.ConstantPad2d,使用非常简单,详细可见配套代码。 Linear Layers Linear Layers包含4个层分别是nn.Identity,nn.Linear, nn.Bilinear, nn.LazyLinear nn.Identity 是恒等映射,不对输入做任何变换,它通常用于占位。 nn.Linear 就是大家熟悉的全连接层(Fully Connection Layer),可实现 y= Wx + b nn.Bilinear 是双线性层,它有两个输入,实现公式 y = x1Wx2 +b nn.LazyLinear 是nn.Linear的lazy版本,也就是懒惰的Linear层,它在第一次推理时自动根据输入特征图的尺寸来设定in_features,免去了手动计算in_features的麻烦。 Linear层十分简单,就不用代码演示了。 Normaliation Layers Normaliation Layers 里包含主流的标准化网络层,分别有BN、LN、IN、GN以及早期的LRN。这一些列的层已经成为现在深度学习模型的标配,它们充当一种正则,对数据的分布进行变换,使数据分布变到0均值,1标准差的形式。实验结果发现这样做可以加速模型训练,让模型更稳定,精度更高。 其中最出名的当属2015年提出的BatchNorm, 来自于Google团队的Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift,关于BN的介绍网上有很多文章,大家可自行学习,在代码实现上我们需要熟悉网络层内部的参数,以及训练与推理过程中的差异。 BatchNorm 会对输入进行减均值、除以标准差、乘以γ、加β的操作。如下图所示: 其中γ与β是Parameter,是可训练的参数,与卷积层的卷积核、FC层的权重一样,容易理解。 均值与标准差就没那么简单了,在训练过程,它们是通过指数移动平均统计得来,在测试时则是用固定的、不会再变化的均值和方差。 从此也可知道,当模型设置在训练状态(model.train() )与推理状态(model.eval() )时,BN层的操作输出是会不一样的。 方法原型如下: torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True, device=None, dtype=None) num_features – 输入的通道数,该参数将决定BN层有多少个γ和β eps – 分母增加的一个小数,防止除以0,默认值为1e-5 momentum – 指数滑动平均的动量值,Default: 0.1 affine – 是否执行乘以γ、加β的操作,要理解为什么叫affine需要去看论文。Default: True track_running_stats – 是否需要执行记录统计均值、统计方差。默认是开启的,如果不开启,则计算时的均值方差只来自当前batch的计算值。 Default: True 具体使用方法详细可见配套代码,请尝试调整各个参数,观察输出的变化以及网络层本身参数的变化。 BN提出之后几乎成了深度学习模型的标配,但在一些任务中BN的均值、方差计算方式就不太适用了,针对均值、方差的统计方式不同,就有了GN、LN、IN。 GN是针对batch size小(一些任务占显存大,只能用小batch跑),统计的均值方差存在较大差异而提出的分组进行统计,详细参见:Group Normalization LN是针对RNN这样的序列网络设计的,以层为单位进行统计均值、方差,详细参见:Layer Normalization IN是针对风格迁移这类GAN任务中,不同风格实例的差异较大,以实例为单位进行统计均值、方差,详细参见:Instance Normalization: The Missing Ingredient for Fast Stylization LRN是2012年深度学习图像领域开山之作——AlexNet中采用的正则化方法,现在很少采用,详细参见:ImageNet Classifification with Deep Convolutional Neural Networks Dropout Layers Dropout——随机失活和LRN一样在Alexnet论文中所采用,以防止模型过拟合,针对它的正则化作用探讨可参见由Hinton一作发表的论文Improving neural networks by preventing co-adaptation of feature detectors。 Dropout 的操作非常简单,以概率p随机的让部分神经元暂时失活,失活表示它不与任何神经元连接,如下图所示: 图片出自:《Dropout: A Simple Way to Prevent Neural Networks from Overfitting》 训练过程的每一次forward,都会重新进行随机失活。在测试(推理)过程,所有神经元都参与工作,不再采用随机失活。更详细的操作及理论分析,推荐阅读《Dropout: A Simple Way to Prevent Neural Networks from Overfitting》。 Dropout使用注意事项: Dropout通常用于nn.Linear层之前 Dropout执行后,神经元个数会减少,导致数据尺度发生变化 论文中给出的方法是在测试时,需要将神经元数据尺度缩放 1/p倍,因为在训练时候减少了p倍。(p为随机失活的概率)。但在工程应用的时候,最好是减少推理的步骤,于是pytorch把数据尺度的缩放弄到了训练中,在训练时,对数据进行1/(1-p)的放大。(Furthermore, the outputs are scaled by a factor of 1/(1-p) during training. ) 关于数据尺度缩放,这里设计了验证实验,可到配套代码中运行并查看。 Alpha Dropout Dropout的随机失活会导致数据分布的变化,而数据分布对于模型训练的稳定是非常关键的,因此有针对这个问题提出了一种保持输入均值和方差不变的Dropout——Alpha Dropout。理论分析建议阅读论文Self-Normalization Neural Networks FeatureAlphaDropout是基于通道维度进行的,并且不同于Dropout的置零,它是将神经元设置为SELU激活函数的负饱和值,通常 Alpha Dropout都是搭配SELU激活函数的,具体推导还是要看论文Self-Normalization Neural Networks,一篇102页的论文。 Non-linear Layers 非线性激活函数是深度学习的命根子,倘若没有非线性变换函数,那么1亿层的Linear层堆叠,也只能等价于1层网络(通过矩阵乘法法则可推导)。因此非线性激活函数是深度学习之所以能称之为深度的重要因素。 对于非线性激活函数,pytorch划分为了两大类,这是非常合理的!分别是Non-linear Activations (weighted sum, nonlinearity) 和Non-linear Activations (other)。 其实可以作用进行划分 为了对神经元进行非线性变换的称为非线性激活函数 为了对输出神经元进行Softmax的、变为概率分布形式的称为特殊非线性激活函数 更通俗的划分是: 非softmx的; softmax系列; 对于非softmax,大家肯定不陌生,如sigmoid、tanh、ReLU、PReLU等,这些就不过多介绍,请大家自行查阅文档 对于softmax需要简单讲一讲,softmax的作用是将一个向量转换为一个概率分布的形式,以便于实现loss的计算,计算过程如下图所示: 计算公式如下: 看着一头雾水,其实很好理解。一个概率向量它的要求至少有这两个 非负 求和等于1 对于非负,用上幂函数,就可以实现了; 对于求和对于1,那就所有元素除以一个求和项,所有元素再加起来的时候分子就等于分母,自然求和等于1了,Softmax的设计思路真巧妙! 对于Softmax系列的激活函数,可参考文档 小结 本文对pytorch常用的网络层接口进行了介绍与代码分析,由于深度学习模型发展迅速,难以详尽介绍每一个网络层的使用,但pytorch都有详细的文档可以学习,希望大家可以通过本节内容学习如何学习pytorch的系列函数、类方法使用。 本小节配套代码中有这些网络层的演示: 更多更详细的网络层使用介绍,可查看文档中的目录,这里简介每个主题的内容 Containers: 模型容器 Convolution Layers:卷积层 Pooling layers:池化层 Padding Layers:填充层 Non-linear Activations (weighted sum, nonlinearity):非线性激活函数 Non-linear Activations (other):Softmax系列激活函数 Normalization Layers:标准化层 Recurrent Layers:RNN 网络层 Transformer Layers: Transformer 网络层 Linear Layers:线性层 Dropout Layers: 随机失活层 Sparse Layers:稀疏网络层 Distance Functions:计算距离函数 Loss Functions:计算损失函数 Vision Layers:CV任务网络层 Shuffle Layers:随机打乱功能层 DataParallel Layers (multi-GPU, distributed):多GPU网络层,多gpu需要用层的概念进行包装 Utilities:各功能函数层 Quantized Functions:量化功能函数 Lazy Modules Initialization:“懒惰”初始化功能模块 到这里,Module的类型就介绍完毕,下一小节将学习Module内部有哪些api,如何使用它们对一个Module进行管理,如模型的网络层查看、管理,模型的参数查看、管理,以及Hook函数的用法。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.4-module-api.html":{"url":"chapter-4/4.4-module-api.html","title":"4.4 Module常用API函数","keywords":"","body":"4.4 Module常用函数 本小节汇总介绍Module常用的方法,由于文档中是按首字母排序展示所有方法,未按用途进行归类,不便于理解各函数之间的关系。在这里,特别将具有相似功能的相关函数归纳整理,供大家参考学习。 常用方法包括: 设置模型训练、评估模式 eval train 设置模型存放在cpu/gpu/xpu cpu cuda to xpu 获取模型参数、加载权重参数 load_state_dict state_dict 管理模型的modules, parameters, sub_module parameters children modules named_children named_modules named_parameters get_parameter get_submodule add_module 设置模型的参数精度,可选半精度、单精度、双精度等 bfloat16 half float double 对子模块执行特定功能 apply zero_grad 以上是不完全的列举,有些非高频使用的函数请到文档中查阅。下面通过简介和配套代码的形式学习上述函数的使用。 设置模型训练、评估模式 eval:设置模型为评估模式,这一点与上一小节介绍的BN,Dropout息息相关,评估模式下模型的某些层执行的操作与训练状态下是不同的。 train:设置模型为训练模式,如BN层需要统计running_var这些统计数据,Dropout层需要执行随机失活等。 使用方法过于简单,无需代码展示。 设置模型存放在cpu/gpu 对于gpu的使用会在后面设置单独小节详细介绍,由于这里是基础学习,暂时可不考虑运算速度问题。这里既然遇到了相关的概念,就简单说一下。 pytorch可以利用gpu进行加速运算,早期只支持NVIDIA公司的GPU,现在也逐步开始支持AMD的GPU。使用gpu进行运算的方法很简单,就是把需要运算的数据放到gpu即可。方法就是 xxx.cuda(),若想回到cpu运算,那就需要xxx.cpu()即可。但有一个更好的方法是to(),to方法可将对象放到指定的设备中去,如to.(\"cpu\") 、 to.(\"cuda)、to(\"cuda:0\")等。 cpu:将Module放到cpu上。 cuda:将Module放到cuda上。为什么是cuda不是gpu呢?因为CUDA(Compute Unified Device Architecture)是NVIDIA推出的运算平台,数据是放到那上面进行运算,而gpu可以有很多个品牌,因此用cuda更合理一些。 to:将Module放到指定的设备上。 关于to通常会配备torch.cuda.is_available()使用,请看配套代码学习。 获取模型参数、加载权重参数 模型训练完毕后,我们需要保存的核心内容是模型参数,这样可以供下次使用,或者是给别人进行finetune。相信大家都用ImageNet上的预训练模型,而使用方法就是官方训练完毕后保存模型的参数,供我们下载,然后加载到自己的模型中。在这里就涉及两个重要操作:保存模型参数与加载模型参数,分别要用到以下两个函数。 state_dict:返回参数字典。key是告诉你这个权重参数是放到哪个网络层。 load_state_dict:将参数字典中的参数复制到当前模型中。这里的复制要求key要一一对应,若key对不上,自然模型不知道要把这个参数放到哪里去。绝大多数开发者都会在load_state_dict这里遇到过报错,如 RuntimeError: Error(s) in loading state_dict for ResNet: Missing key(s) in state_dict: xxxxxxxx   Unexpected key(s) in state_dict: xxxxxxxxxx 这通常是拿到的参数字典与模型当前的结构不匹配。 对于load_state_dict函数,还有两个参数可以设置,请看原型: 参数: state_dict (dict) – a dict containing parameters and persistent buffers. strict (bool, optional) – whether to strictly enforce that the keys in state_dict match the keys returned by this module’s state_dict() function. Default: True 返回项 missing_keys is a list of str containing the missing keys unexpected_keys is a list of str containing the unexpected keys 上述两个方法具体的使用请看配套代码。 管理模型的modules, parameters, sub_module 模型中需要管理的主要是parameter与module,每个对象都有两种方式读取,分别是带名字和不带名字的。针对module还有一个称为children的方法,它与modules方法最大的不同在于modules会返回module本身。具体差异通过配套代码一看便明了。 parameters:返回一个迭代器,迭代器可抛出Module的所有parameter对象 named_parameters:作用同上,不仅可得到parameter对象,还会给出它的名称 modules:返回一个迭代器,迭代器可以抛出Module的所有Module对象,注意:模型本身也是module,所以也会获得自己。 named_modules:作用同上,不仅可得到Module对象,还会给出它的名称 children:作用同modules,但不会返回Module自己。 named_children:作用同named_modules,但不会返回Module自己。 获取某个参数或submodule 当想查看某个部分数据时,可以通过get_xxx方法获取模型特定位置的数据,可获取parameter、submodule,使用方法也很简单,只需要传入对应的name即可。 get_parameter get_submodule 设置模型的参数精度,可选半精度、单精度、双精度等 为了调整模型占存储空间的大小,可以设置参数的数据类型,默认情况是float32位(单精度),在一些场景可采用半精度、双精度等,以此改变模型的大小或精度。Module提供了几个转换权重参数精度的方法,分别如下: half:半精度 float:单精度 double:双精度 bfloat16:Brain Floating Point 是Google开发的一种数据格式,详细参见wikipedia 对子模块执行特定功能 zero_grad:将所有参数的梯度设置为0,或者None apply:对所有子Module执行指定fn(函数),常见于参数初始化。这个可以参见配套代码。 小结 本节对Module的常用API函数进行了介绍,包括模型两种状态,模型存储于何种设备,模型获取参数,加载参数,管理模型的modules,设置模型参数的精度,对模型子模块执行特定功能。 由于Module是核心模块,其涉及的API非常多,短时间不好消化,建议大家结合代码用例,把这些方法都过一遍,留个印象,待日后项目开发需要的时候知道有这些函数可以使用即可。 下一小节将介绍Module中的Hook函数。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.5-hook-func.html":{"url":"chapter-4/4.5-hook-func.html","title":"4.5 Hook函数及Grad-CAM","keywords":"","body":"4.5 hook函数 注:本小节主要参考《PyTorch模型训练实用教程》(第一版),主要更新了PyTorch新版本的函数——torch.nn.Module.register_full_backward_hook。 -------------------------------------------------分割线--------------------------------------------------------------- 本小节将介绍Module中的三个Hook函数以及Tensor的一个Hook函数 torch.Tensor.register_hook torch.nn.Module.register_forward_hook torch.nn.Module.register_forward_pre_hook torch.nn.Module.register_full_backward_hook 同时使用hook函数优雅地实现Grad-CAM,效果如下图所示: ​ Grad-CAM是CAM(class activation map,类激活图)的改进,可对任意结构的CNN进行类激活可视化,不需要修改网络结构或者重新训练,详细理论请参见Grad-CAM: Visual Explanations from Deep Networks via Gradient-based Localization 什么是hook? Hook函数在多门编程语言中均有出现,是一个经典的编程方式。hook意为钩、挂钩、鱼钩。 引用知乎用户“马索萌”对hook的解释:“(hook)相当于插件。可以实现一些额外的功能,而又不用修改主体代码。把这些额外功能实现了挂在主代码上,所以叫钩子,很形象。” 简单讲,就是不修改主体,而实现额外功能。对应到在pytorch中,主体就是forward和backward,而额外的功能就是对模型的变量进行操作,如“提取”特征图,“提取”非叶子张量的梯度,修改张量梯度等等。 hook的出现与pytorch运算机制有关,pytorch在每一次运算结束后,会将中间变量释放,以节省内存空间,这些会被释放的变量包括非叶子张量的梯度,中间层的特征图等。 但有时候,想可视化中间层的特征图,又不能改动模型主体代码,该怎么办呢?这时候就要用到hook了。 举个例子演示hook提取非叶子张量的梯度: import torch def grad_hook(grad): y_grad.append(grad) y_grad = list() x = torch.tensor([[1., 2.], [3., 4.]], requires_grad=True) y = x+1 y.register_hook(grad_hook) z = torch.mean(y*y) z.backward() print(\"type(y): \", type(y)) print(\"y.grad: \", y.grad) print(\"y_grad[0]: \", y_grad[0]) >>> ('type(y): ', ) >>> ('y.grad: ', None) >>> ('y_grad[0]: ', tensor([[1.0000, 1.5000], [2.0000, 2.5000]])) 可以看到y.grad的值为None,这是因为y是非叶子结点张量,在z.backward()完成之后,y的梯度被释放掉以节省内存,但可以通过torch.Tensor的类方法register_hook将y的梯度提取出来。 torch.Tensor.register_hook torch.Tensor.register_hook (Python method, in torch.Tensor.register_hook) 功能:注册一个反向传播hook函数,这个函数是Tensor类里的,当计算tensor的梯度时自动执行。 为什么是backward?因为这个hook是针对tensor的,tensor中的什么东西会在计算结束后释放? 那就是gradient,所以是backward hook. 形式: hook(grad) -> Tensor or None ,其中grad就是这个tensor的梯度。 返回值:a handle that can be used to remove the added hook by calling handle.remove() 应用场景举例:在hook函数中可对梯度grad进行in-place操作,即可修改tensor的grad值。 这是一个很酷的功能,例如当浅层的梯度消失时,可以对浅层的梯度乘以一定的倍数,用来增大梯度; 还可以对梯度做截断,限制梯度在某一区间,防止过大的梯度对权值参数进行修改。 下面举两个例子,例1是如何获取中间变量y的梯度,例2是利用hook函数将变量x的梯度扩大2倍。 例1: import torch y_grad = list() def grad_hook(grad): y_grad.append(grad) x = torch.tensor([2., 2., 2., 2.], requires_grad=True) y = torch.pow(x, 2) z = torch.mean(y) h = y.register_hook(grad_hook) z.backward() print(\"y.grad: \", y.grad) print(\"y_grad[0]: \", y_grad[0]) h.remove() # removes the hook >>> ('y.grad: ', None) >>> ('y_grad[0]: ', tensor([0.2500, 0.2500, 0.2500, 0.2500])) 可以看到当z.backward()结束后,张量y中的grad为None,因为y是非叶子节点张量,在梯度反传结束之后,被释放。 在对张量y的hook函数(grad_hook)中,将y的梯度保存到了y_grad列表中,因此可以在z.backward()结束后,仍旧可以在y_grad[0]中读到y的梯度为tensor([0.2500, 0.2500, 0.2500, 0.2500]) 例2: import torch def grad_hook(grad): grad *= 2 x = torch.tensor([2., 2., 2., 2.], requires_grad=True) y = torch.pow(x, 2) z = torch.mean(y) h = x.register_hook(grad_hook) z.backward() print(x.grad) h.remove() # removes the hook >>> tensor([2., 2., 2., 2.]) 原x的梯度为tensor([1., 1., 1., 1.]),经grad_hook操作后,梯度为tensor([2., 2., 2., 2.])。 torch.nn.Module.register_forward_hook 功能:Module前向传播中的hook,module在前向传播后,自动调用hook函数。 形式:hook(module, input, output) -> None or modified output 。注意不能修改input和output 返回值:a handle that can be used to remove the added hook by calling handle.remove() 举例:假设网络由卷积层conv1和池化层pool1构成,输入一张4*4的图片,现采用forward_hook获取module——conv1之后的feature maps,示意图如下: ​ import torch import torch.nn as nn class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 2, 3) self.pool1 = nn.MaxPool2d(2, 2) def forward(self, x): x = self.conv1(x) x = self.pool1(x) return x def farward_hook(module, data_input, data_output): fmap_block.append(data_output) input_block.append(data_input) if __name__ == \"__main__\": # 初始化网络 net = Net() net.conv1.weight[0].fill_(1) net.conv1.weight[1].fill_(2) net.conv1.bias.data.zero_() # 注册hook fmap_block = list() input_block = list() net.conv1.register_forward_hook(farward_hook) # inference fake_img = torch.ones((1, 1, 4, 4)) # batch size * channel * H * W output = net(fake_img) # 观察 print(\"output shape: {}\\noutput value: {}\\n\".format(output.shape, output)) print(\"feature maps shape: {}\\noutput value: {}\\n\".format(fmap_block[0].shape, fmap_block[0])) print(\"input shape: {}\\ninput value: {}\".format(input_block[0][0].shape, input_block[0])) 首先初始化一个网络,卷积层有两个卷积核,权重分别为全1和全2,bias设置为0,池化层采用2*2的最大池化。 在进行forward之前对module——conv1注册了forward_hook函数,然后执行前向传播(output=net(fake_img)),当前向传播完成后, fmap_block列表中的第一个元素就是conv1层输出的特征图了。 这里注意观察forward_hook函数有data_input和data_output两个变量,特征图是data_output这个变量,而data_input是conv1层的输入数据, conv1层的输入是一个tuple的形式。 hook函数调用逻辑 下面剖析一下module是怎么样调用hook函数的呢? output = net(fakeimg) net是一个module类,对module执行 module(input)是会调用module._call module.call :会进入_call_impl,回顾Module那一小节,call_impl是有很多其它代码,这就是对hook函数的处理,可以看到,让注册了hook函数,模型的forward不再是4.1小节里分析的1102行代码进行,而是分别执行对应的hook函数。1109行是执行每个forward_pre_hook的,1120行是执行forward的,1123行是执行forward_hook的, 1144行是执行full_backward_hook的。 def _call_impl(self, *input, **kwargs): forward_call = (self._slow_forward if torch._C._get_tracing_state() else self.forward) # If we don't have any hooks, we want to skip the rest of the logic in # this function, and just call forward. if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks or _global_forward_hooks or _global_forward_pre_hooks): return forward_call(*input, **kwargs) # Do not call functions when jit is used full_backward_hooks, non_full_backward_hooks = [], [] if self._backward_hooks or _global_backward_hooks: full_backward_hooks, non_full_backward_hooks = self._get_backward_hooks() if _global_forward_pre_hooks or self._forward_pre_hooks: for hook in (*_global_forward_pre_hooks.values(), *self._forward_pre_hooks.values()): result = hook(self, input) if result is not None: if not isinstance(result, tuple): result = (result,) input = result bw_hook = None if full_backward_hooks: bw_hook = hooks.BackwardHook(self, full_backward_hooks) input = bw_hook.setup_input_hook(input) result = forward_call(*input, **kwargs) if _global_forward_hooks or self._forward_hooks: for hook in (*_global_forward_hooks.values(), *self._forward_hooks.values()): hook_result = hook(self, input, result) if hook_result is not None: result = hook_result if bw_hook: result = bw_hook.setup_output_hook(result) # Handle the non-full backward hooks if non_full_backward_hooks: var = result while not isinstance(var, torch.Tensor): if isinstance(var, dict): var = next((v for v in var.values() if isinstance(v, torch.Tensor))) else: var = var[0] grad_fn = var.grad_fn if grad_fn is not None: for hook in non_full_backward_hooks: wrapper = functools.partial(hook, self) functools.update_wrapper(wrapper, hook) grad_fn.register_hook(wrapper) self._maybe_warn_non_full_backward_hook(input, result, grad_fn) return result 这里需要注意两点: hook_result = hook(self, input, result)中的input和result不可以修改。这里的input对应forward_hook函数中的data_input,result对应forward_hook函数中的data_output,在conv1中,input就是该层的输入数据,result就是经过conv1层操作之后的输出特征图。虽然可以通过hook来对这些数据操作,但是不能修改这些值,否则会破坏模型的计算。 注册的hook函数是不能带返回值的,否则抛出异常,这个可以从代码中看到 if hook_result is not None: raise RuntimeError 总结一下调用流程: net(fake_img) --> net.call : result = self.forward(input, *kwargs) --> net.forward: x = self.conv1(x) --> conv1.call:hook_result = hook(self, input, result) hook就是注册了的forward_hook函数。 torch.nn.Module.register_forward_pre_hook 功能:执行forward()之前调用hook函数。 形式:hook(module, input) -> None or modified input 应用场景:register_forward_pre_hook与forward_hook一样,是在module.call中注册的,与forward_hook不同的是,其在module执行forward之前就运行了,具体可看module.call中的代码。 torch.nn.Module.register_full_backward_hook 功能:Module反向传播中的hook,每次计算module的梯度后,自动调用hook函数。 形式:hook(module, grad_input, grad_output) -> tuple(Tensor) or None 注意事项: 当module有多个输入或输出时,grad_input和grad_output是一个tuple。 register_full_backward_hook 是修改过的版本,旧版本为register_backward_hook,不过官方已经建议弃用,不需要再了解。 返回值:a handle that can be used to remove the added hook by calling handle.remove() 应用场景举例:提取特征图的梯度 Grad-CAM 实现 采用register_full_backward_hook实现特征图梯度的提取,并结合Grad-CAM(基于类梯度的类激活图可视化)方法对卷积神经网络的学习模式进行可视化。 关于Grad-CAM请看论文:《Grad-CAM Visual Explanations from Deep Networks via Gradient-based Localization》 简单介绍Grad-CAM的操作,Grad-CAM通过对最后一层特征图进行加权求和得到heatmap,整个CAM系列的主要研究就在于这个加权求和中的权值从那里来。 Grad-CAM是对特征图求梯度,将每一张特征图的梯度求平均得到权值(特征图的梯度是element-wise的)。求梯度时并不采用网络的输出,而是采用类向量,即one-hot向量。 下图是ResNet的Grad-CAM示意图,上图类向量采用的是猫的标签,下图采用的是狗的标签,可以看到在上图模型更关注猫(红色部分),下图判别为狗的主要依据是狗的头部。 ​ 下面采用一个LeNet-5演示backward_hook在Grad-CAM中的应用。 简述代码过程: 创建网络net; 注册forward_hook函数用于提取最后一层特征图; 注册backward_hook函数用于提取类向量(one-hot)关于特征图的梯度 对特征图的梯度进行求均值,并对特征图进行加权; 可视化heatmap。 注意:需要注意的是在backward_hook函数中,grad_out是一个tuple类型的,要取得特征图的梯度需要这样grad_block.append(grad_out[0].detach()) 思考 这里对3张飞机的图片进行观察heatmap,如下图所示,第一行是原图,第二行是叠加了heatmap的图片。 这里发现一个有意思的现象,模型将图片判为飞机的依据是蓝天,而不是飞机(图1-3)。 那么我们喂给模型一张纯天蓝色的图片,模型会判为什么呢?如图4所示,发现模型判为了飞机 从这里发现,虽然能将飞机正确分类,但是它学到的却不是飞机的特征! 这导致模型的泛化性能大打折扣,从这里我们可以考虑采用trick让模型强制的学习到飞机而不是常与飞机一同出现的蓝天,或者是调整数据。 ​ 对于图4疑问:heatmap蓝色区域是否对图像完全不起作用呢?是否仅仅通过红色区域就可以对图像进行判别呢? 接下来将一辆正确分类的汽车图片(图5)叠加到图4蓝色响应区域(即模型并不关注的区域),结果如图6所示,汽车部分的响应值很小,模型仍通过天蓝色区域将图片判为了飞机。 接着又将汽车叠加到图4红色响应区域(图的右下角),结果如图7所示,仍将图片判为了飞机。 有意思的是将汽车叠加到图7的红色响应区域,模型把图片判为了船,而且红色响应区域是蓝色区域的下部分,这个与船在大海中的位置很接近。 ​ 通过以上代码学习full_backward_hook的使用及其在Grad-CAM中的应用,并通过Grad-CAM能诊断模型是否学习到关键特征。 关于CAM( class activation maping,类激活响应图)是一个很有趣的研究,有兴趣的朋友可以对CAM、Grad-CAM和Grad-CAM++进行研究。 小结 本小节介绍了编程语言中经典的思想——Hook函数,并讲解了pytorch中如何使用它们,最后还采用full_backward_hook实现有趣的Grad-CAM可视化,本节代码较多,建议对着配套代码单步调试进行学习,掌握hook函数的妙用,在今后使用pytorch进行模型分析、魔改的时候更游刃有余。 下一小结会把本章所学的Module相关容器、网络层的知识点串起来使用,通过剖析torchvision中经典模型的源代码,了解所学习的知识点是如何使用的。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.6-classic-model.html":{"url":"chapter-4/4.6-classic-model.html","title":"4.6 经典模型代码分析","keywords":"","body":"4.6 经典Model代码分析 torchvision中提供了一些经典的卷积神经网络模型实现,本小节将挑选部分进行分析,学习torchvision是如何构建复杂的网络模型,学习它们的代码风格、代码规范。 AlexNet 出自:ImageNet Classification with Deep Convolutional Neural Networks 模型结构图如下图所示:整体可分为前半部分的特征提取与后半部分的分类。 ​ 代码分析: D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\alexnet.py 代码中定义了一个AlexNet类与一个alexnet函数,这样的封装形式贯穿整个torchvision的模型定义。 AlexNet类是nn.Module,其中定义了AlexNet模型的具体结构,而alexnet函数则是对Alexnet类的包装,并且实现加载预训练参数的功能,即以下代码: model = AlexNet(**kwargs) if pretrained: state_dict = load_state_dict_from_url(model_urls[\"alexnet\"], progress=progress) model.load_state_dict(state_dict) return model 从此也知道,torchvision中定义模型所采用的预训练模型均是通过指定的url下载,并存储于本地磁盘供下一次使用。 由于“网络问题”,通常建议大家通过代码中给出的url自行下载权重文件,然后在自己的代码中使用load_state_dict方法加载预训练参数。 分析了alexnet.py整体结构,下面回到AlexNet类本身,看看具体模型如何写的。 class AlexNet(nn.Module): def __init__(self, num_classes: int = 1000) -> None: def forward(self, x: torch.Tensor) -> torch.Tensor: AlexNet采用熟悉的方式定义了两个函数,熟悉4.1小结中的知识点的话,这里不比多说。 forward函数第48行代码值得注意,二维特征图要输入到Linear层,通常通过flatten函数对特征图进行变换。 def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.features(x) x = self.avgpool(x) x = torch.flatten(x, 1) # Line 48 x = self.classifier(x) return x 总结: 采用函数形式封装模型类,额外提供预训练权重加载功能; Linear层之前可通过torch.flatten将数据变为一维向量; VGG VGG出自:Very Deep Convolutional Networks For Large-Scale Image Recognition 其共有4种深度,分别是11, 13, 16, 19层,用得比较多的VGG16、19。VGG的代码就比AlexNet的代码复杂了,因为它涉及8个具体的网络模型定义,因此不能再使用面向过程的方式进行编写,需要将共性的部分抽象出来,这一份代码值得新手仔细、认真学习。 首先是大体了解VGG整体结构,网络结构示意图如下图所示: ​ VGG最大特点是2个3x3、3个3x3卷积层的堆叠,并且堆叠总共分5次,最后接入三个FC层。从此可知,核心是如何将特征提取部分进行抽象,请大家带着这个问题观察代码:D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\vgg.py vgg.py中定义了VGG类、_vgg函数、make_layers函数、cfgs字典,以及一系列具体网络模型封装的函数,如vgg11,vgg13, vgg16等。 看过alexnet.py,这里能猜出VGG类是一个nn.module。 _vgg函数:vgg函数接收具体的网络参数,以此决定返回哪一个vgg模型; vggxxx:定义了具体VGG所需要的参数,并调用_vgg函数得到具体模型; make_layers函数:创建可抽象出来、共性的网络层函数,即网络结构图中的5次堆叠部分。 cfgs字典:配置各具体vgg模型所需要的参数,主要在make_layers中使用。 下面以vgg16为例,观察vgg.py是如何实现它的。 看到153行代码: def vgg16(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> VGG: return _vgg(\"vgg16\", \"D\", False, pretrained, progress, **kwargs) 可知道,vgg16是对_vgg的封装,并且固定了两个参数\"vgg16\" 和 \"D\"。 跳到94行代码: def _vgg(arch: str, cfg: str, batch_norm: bool, pretrained: bool, progress: bool, **kwargs: Any) -> VGG: if pretrained: kwargs[\"init_weights\"] = False model = VGG(make_layers(cfgs[cfg], batch_norm=batch_norm), **kwargs) if pretrained: state_dict = load_state_dict_from_url(model_urls[arch], progress=progress) model.load_state_dict(state_dict) return model 可知道_vgg调用了VGG类得到最终的模型,并且给VGG传入了make_layers函数创建的网络层; 通过这行代码可知道,需要进入make_layers去观察如何创建网络层的。进入make_layers前,需要知道cfgs[cfg]当前传入的是: 'D': [64, 64, 'M', 128, 128, 'M', 256, 256, 256, 'M', 512, 512, 512, 'M', 512, 512, 512, 'M'], 跳到69行代码: def make_layers(cfg: List[Union[str, int]], batch_norm: bool = False) -> nn.Sequential: layers: List[nn.Module] = [] in_channels = 3 for v in cfg: if v == \"M\": layers += [nn.MaxPool2d(kernel_size=2, stride=2)] else: v = cast(int, v) conv2d = nn.Conv2d(in_channels, v, kernel_size=3, padding=1) if batch_norm: layers += [conv2d, nn.BatchNorm2d(v), nn.ReLU(inplace=True)] else: layers += [conv2d, nn.ReLU(inplace=True)] in_channels = v return nn.Sequential(*layers) 从这里可知道是对cfg中进行for循环,不断的构建网络层,并且添加到list中,最后组装成一个Sequential的形式。这里的代码逻辑就是网络结构图中的抽象,把四种模型的共性地方抽象出来,然后通过不同的配置参数可生成vgg11, vgg13, vgg16, vgg19。这里的代码值得学习。 弄清楚make_layers是生成前面一系列卷积层的堆叠Sequential之后,继续进入VGG类观察。 跳到25行代码,看一个Module,可以先看forward函数,再看forward中的属性是怎么来的。 def forward(self, x: torch.Tensor) -> torch.Tensor: x = self.features(x) x = self.avgpool(x) x = torch.flatten(x, 1) x = self.classifier(x) return x 可以发现它的forward十分简洁,因为vgg模型就是以简洁出名的,像一个糖葫芦一样串起来即可。接着去看看self.features是什么,怎么来的,这个需要到init函数中寻找。 跳到34行代码:self.features = features 由此可知道,VGG特征提取部分的网络层均是通过make_layers函数定义的那个Sequential。 接着36行代码的classifier就没啥好说的。 接着的第45行代码出现了新内容,权重初始化。调用了_initialize_weights函数对VGG模型进行权重初始化。众所周知,良好的权重初始化对模型训练是至关重要的,早期对于权重初始化有许多的研究,比较著名的有Xavier方法、MSRA(Kaiming)方法。 预告:具体的权重初始化方法将在下一小节详细介绍。 下面观察如何编写函数对VGG进行权重初始化:跳转55行 def _initialize_weights(self) -> None: for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode=\"fan_out\", nonlinearity=\"relu\") if m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.BatchNorm2d): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0) elif isinstance(m, nn.Linear): nn.init.normal_(m.weight, 0, 0.01) nn.init.constant_(m.bias, 0) 此函数的逻辑遍历所有Module,并判断Module类型,根据不同的Module类型设置不同的初始化方法,如卷积层则用kaiming方法设置weight,bias全部设置为0;BN层的weight设置为1,bias设置为0;全连接层的weight用正态分布进行随机初始化,bias设置为0。 到这里一个具体的VGG模型定义就讲完了,下面总结一下它们的调用关系与逻辑。 vgg16() --> _vgg() --> make_layers --> VGG:最核心在于如何构建一个模块(函数也好、类也好)可以接收不同的参数(cfgs)就能生成对应VGG的特征提取部分的网络层(一个大的Sequential)。 GoogLeNet GoogLeNet-V1 出自 Going deeper with convolutions,后续也有V2,V3,V4,这里不进行介绍。 V1的提出最大的特点在于提出Inception模块,它是一个多分支的特征提取模块,如下图所示: ​ 网络结构如下图所示: ​ 代码并不复杂,但其中的Inception模块的编写,是之前没有遇到的,可以借鉴学习。 观察D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\googlenet.py 可以看到熟悉的定义了具体的Module——GoogLeNet类,模型的封装调用函数——googlenet,以及从GoogLeNet模型抽象出来的、反复需要使用的模块——Inception、InceptionAux、BasicConv2d。 这里面的代码并不复杂,这里不逐行分析,只把GoogLeNet类的逻辑关系理一理。 首先,将反复使用的模块抽象成一个类,这样在使用的时候只需要一行代码即可定义好,如BasicConv2d:包含了卷积层+BN层; Inception:包含四个分支的处理并合并最终特征图; InceptionAux:辅助分类层输出。 然后在init函数中像搭积木一样,把需要用到的模块逐一定义 最后在forward函数中调用定义好的网络层即可。 总结: 反复使用的模块抽象为一个Moudle类,并作为参数进行调用。好处在于当想修改这些基础元素模块的时候,仅需要重新写一个Module类替换即可,并不需要改动GoogLeNet类当中的任何代码(在resnet中会有体现)。要想理解好这一点,请仔细体会这几行代码 blocks = [BasicConv2d, Inception, InceptionAux] conv_block = blocks[0] inception_block = blocks[1] inception_aux_block = blocks[2] Resnet ResNet出自何恺明的《Deep Residual Learning for Image Recognition》,是目前工业界应用最广泛的卷积神经网络。 网络结构如下图所示,有ResNet-18, 34, 50, 101, 152,使用较多的为ResNet-50。其结构特点也是模块的堆叠,如表格中看到的x2, x3,x4, x6表示的是一个模块堆叠2次、3次、4次、6次。 ​ 在resnet模型中,最大的特点在于采用了残差结构的模块,如下图所示: ​ 这里有两种形式,一种是BasicBlock,另外一种是resnet50/101/152用的Bottleneck。 下面就来看看D:\\Anaconda_data\\envs\\pytorch_1.10_gpu\\Lib\\site-packages\\torchvision\\models\\resnet.py 是如何实现这一系列复杂的resnet模型。 提示:pycharm中按住Ctrl+Shift+ \"-\" ,可以把代码块收起来,可以快速浏览resnet.py下的主要内容,可以发现,还是熟悉的结构,分别有 ResNet类 _resnet函数 resnet18\\34\\50...一系列具体模型函数 抽象出来的基础模块:BasicBlock、Bottleneck、conv1x1和conv3x3。 这其中最为特色的是BasicBlock和Bottleneck,分别对应论文图5中的两个结构,它们将在不同的模型中使用。 下面就看看BasicBlock和Bottleneck到底是如何使用的。 跳到144行代码:class ResNet(nn.Module),观察init函数里是如何使用block的。 跳到第178行代码:self.layer1 = self._make_layer(block, 64, layers[0]),在make_layer函数中使用了block进行网络层的构建。这点与VGG中的make_layers类似。 跳到205行代码: def _make_layer( self, block: Type[Union[BasicBlock, Bottleneck]], planes: int, blocks: int, stride: int = 1, dilate: bool = False, ) -> nn.Sequential: norm_layer = self._norm_layer downsample = None previous_dilation = self.dilation if dilate: self.dilation *= stride stride = 1 if stride != 1 or self.inplanes != planes * block.expansion: downsample = nn.Sequential( conv1x1(self.inplanes, planes * block.expansion, stride), norm_layer(planes * block.expansion), ) layers = [] layers.append( block( self.inplanes, planes, stride, downsample, self.groups, self.base_width, previous_dilation, norm_layer ) ) self.inplanes = planes * block.expansion for _ in range(1, blocks): layers.append( block( self.inplanes, planes, groups=self.groups, base_width=self.base_width, dilation=self.dilation, norm_layer=norm_layer, ) ) return nn.Sequential(*layers) 在此函数中使用block(是一个基础模块的类,是BasicBlock或Bottleneck)定义网络层,然后堆叠起来,最后使用Sequential进行包装,构成一个整体。 回到init函数可知道,178-184行代码所构建的模块对应了网络结构的四个部分,对应关系如下图所示: self.layer1 = self._make_layer(block, 64, layers[0]) self.layer2 = self._make_layer(block, 128, layers[1], stride=2, dilate=replace_stride_with_dilation[0]) self.layer3 = self._make_layer(block, 256, layers[2], stride=2, dilate=replace_stride_with_dilation[1]) self.layer4 = self._make_layer(block, 512, layers[3], stride=2, dilate=replace_stride_with_dilation[2]) ​ 在这里,可以发现resnet18和34用的是BasicBlock, resnet50/101/152用的是Bottleneck def resnet18(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet: return _resnet(\"resnet18\", BasicBlock, [2, 2, 2, 2], pretrained, progress, **kwargs) def resnet50(pretrained: bool = False, progress: bool = True, **kwargs: Any) -> ResNet: return _resnet(\"resnet50\", Bottleneck, [3, 4, 6, 3], pretrained, progress, **kwargs) BasicBlock和Bottleneck的使用与googlenet中的blocks呼应上了,请大家仔细对比。 resnet总结 resnet的搭建是将block抽象出来提供接口,由用户自行传入,并且设定堆叠次数,如resnet18就是BasicBlock, [2, 2, 2, 2], resnet50就是 Bottleneck, [3, 4, 6, 3],处处体现了面向对象的编程思维,值得学习。 总结 本小节从简单的AlexNet到复杂的ResNet进行了代码分析,剖析了pytorch的代码结构,编写逻辑以及思想,其中面向对象的思维值得认真学习借鉴。 VGG中的make_layers():通过参数配置形式搭建一个大的Sequential; GoogLeNet的BasicConv2d, Inception, InceptionAux、ResNet的BasicBlock、Bottleneck、conv1x1、conv3x3都是抽象的基础模块。 本小节在多出看到了权重初始化方法,好的权重初始化是模型训练的第一步,下一小节将介绍pytorch提供的系列权重初始化方法及其应用。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-4/4.7-weight-init.html":{"url":"chapter-4/4.7-weight-init.html","title":"4.7 权重初始化方法","keywords":"","body":"4.7 权重初始化方法 良好的模型权重初始化,有利于模型的训练,在torch.nn.init中提供了数十个初始化方法,本小节对它们进行介绍。 回顾上一小节中VGG的初始化代码,先总结出权重初始化的流程与步骤。 for m in self.modules(): if isinstance(m, nn.Conv2d): nn.init.kaiming_normal_(m.weight, mode=\"fan_out\", nonlinearity=\"relu\") if m.bias is not None: nn.init.constant_(m.bias, 0) elif isinstance(m, nn.BatchNorm2d): nn.init.constant_(m.weight, 1) nn.init.constant_(m.bias, 0) elif isinstance(m, nn.Linear): nn.init.normal_(m.weight, 0, 0.01) nn.init.constant_(m.bias, 0) 首先对每一个子module进行遍历,对不同的网络层进行设置对应的权重初始化方法,初始化方法采用的是nn.init.xxx函数。 接下来nn.init.xxx就是本节的主角,下面依次介绍nn.init.xxx函数。 主要分为三部分: Xavier 系列 kaiming 系列 常数方法 Xavier 系列 torch.nn.init.xavieruniform(tensor, gain=1.0) xavier 初始化方法中服从均匀分布 U(-a,a) ,分布的参数 a = gain * sqrt(6/fan_in+fan_out), 这里有一个 gain,表示增益,其大小取决于激活函数的类型。本方法也称为 Glorot initialization。 torch.nn.init.xaviernormal(tensor, gain=1.0) xavier初始化方法中服从正态分布, mean=0,std = gain * sqrt(2/fan_in + fan_out) Xavier初始化方法的理论分析可见《Understanding the difficulty of training deep feedforward neural networks - Glorot, X. & Bengio, Y. (2010),》 Kaiming系列 ​ 3.### torch.nn.init.kaiminguniform(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu') 此为均匀分布,U~(-bound, bound), bound = sqrt(6/(1+a^2)*fan_in) 其中,a为激活函数的负半轴的斜率,relu是0 mode- 可选为fan_in 或 fan_out, fan_in使正向传播时,方差一致; fan_out使反向传播时,方差一致 nonlinearity- 可选 relu 和 leaky_relu ,默认值为 :leaky_relu torch.nn.init.kaimingnormal(tensor, a=0, mode='fan_in', nonlinearity='leaky_relu') 此为0均值的正态分布,N~ (0,std),其中std = sqrt(2/(1+a^2)*fan_in) 其中,a为激活函数的负半轴的斜率,relu是0 mode- 可选为fan_in 或 fan_out, fan_in使正向传播时,方差一致;fan_out使反向传播时,方差一致 nonlinearity- 可选 relu 和 leaky_relu ,默认值为: leaky_relu Kaiming系列的初始化方法理论分析可参阅《Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification - He, K. et al. (2015》 其它方法 均匀分布初始化 torch.nn.init.uniform_(tensor, a=0, b=1) 使值服从均匀分布U(a,b) 正态分布初始化 torch.nn.init.normal_(tensor, mean=0, std=1) 使值服从正态分布N(mean, std),默认值为0,1 常数初始化 torch.nn.init.constant_(tensor, val) 使值为常数val nn.init.constant_(w, 0.3) 单位矩阵初始化 torch.nn.init.eye_(tensor) 将二维tensor初始化为单位矩阵(the identity matrix) 正交初始化 torch.nn.init.orthogonal_(tensor, gain=1) 使得tensor是正交的,论文:Exact solutions to the nonlinear dynamics of learning in deep linear neural networks” - Saxe, A. et al. (2013) 稀疏初始化 torch.nn.init.sparse_(tensor, sparsity, std=0.01) 从正态分布N~(0. std)中进行稀疏化,使每一个column有一部分为0 sparsity- 每一个column稀疏的比例,即为0的比例 nn.init.sparse_(w, sparsity=0.1) 全零初始化 torch.nn.init.zeros_(tensor) 所有参数置零。 全1初始化 torch.nn.init.ones_(tensor) 所有参数置一。 狄拉克初始化 torch.nn.init.dirac_(tensor, groups=1) 采用狄拉克函数进行权重初始化, 增益计算 torch.nn.init.calculate_gain(nonlinearity, param=None) 返回各激活函数对应的增益值,该值用于调整权重初始化的方差。 nonlinearity gain Linear / Identity 1 Conv{1,2,3}D 1 Sigmoid 1 Tanh 35 ReLU 2 Leaky Relu 1+negative_slope22 SELU 43 小结 本小节将torch.nn.init中包含的初始化方法进行了分类介绍,主要分为Xavier和Kaiming方法与其它常数方法,并且回顾了torchvision中如何对一个模型所有层进行权重初始化的流程,希望对大家有所帮助。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-5/":{"url":"chapter-5/","title":"第五章 PyTorch 优化模块","keywords":"","body":"第五章 PyTorch 优化模块 第五章 PyTorch 优化模块 5.1 二十一个损失函数 5.2 十三个优化器 5.3 十四个学习率调整器 第五章简介 本章开始介绍模型优化过程中涉及的三大概念:损失函数、优化器和学习率调整。 由于损失函数、优化器、学习率调整的方法有非常多,仅pytorch官方实现(V1.10)的就有二十一个损失函数,十三个优化器,十四个学习率调整方法。 这几十种方法不会对每一个进行详细介绍,主要通过几个核心的方法为案例,进行剖析各模块的机制,如损失函数的Module如何编写、pytorch是如何构建loss.py体系、优化器如何更新模型中的参数、优化器常用函数、学习率调整机制等内容。 相信了解上述机制,便可举一反三,掌握更为复杂的方法函数。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-5/5.1-loss-function.html":{"url":"chapter-5/5.1-loss-function.html","title":"5.1 二十一个损失函数","keywords":"","body":"5.1 二十一个损失函数 本节重点为pytorch损失函数实现方式及逻辑,而非具体某个损失函数的公式计算,核心为下图: 损失函数——Loss Function 损失函数(loss function)是用来衡量模型输出与真实标签之间的差异,当模型输出越接近标签,认为模型越好,反之亦然。因此,可以得到一个近乎等价的概念,loss越小,模型越好。这样就可以用数值优化的方法不断的让loss变小,即模型的训练。 针对不同的任务有不同的损失函数,例如回归任务常用MSE(Mean Square Error),分类任务常用CE(Cross Entropy),这是根据标签的特征来决定的。而不同的任务还可以对基础损失函数进行各式各样的改进,如Focal Loss针对困难样本的设计,GIoU新增相交尺度的衡量方式,DIoU新增重叠面积与中心点距离衡量等等。 在pytorch中提供了二十一个损失函数,如下所示 nn.L1Loss nn.MSELoss nn.CrossEntropyLoss nn.CTCLoss nn.NLLLoss nn.PoissonNLLLoss nn.GaussianNLLLoss nn.KLDivLoss nn.BCELoss nn.BCEWithLogitsLoss nn.MarginRankingLoss nn.HingeEmbeddingLoss nn.MultiLabelMarginLoss nn.HuberLoss nn.SmoothL1Loss nn.SoftMarginLoss nn.MultiLabelSoftMarginLoss nn.CosineEmbeddingLoss nn.MultiMarginLoss nn.TripletMarginLoss nn.TripletMarginWithDistanceLoss 本小节讲解仅剖析nn.L1Loss和nn.CrossEntropyLoss这两个损失函数及其衍生函数。其余损失函数可以触类旁通。 核心知识在于损失函数的实现流程,不同的损失函数仅在于计算公式的不同,每个损失函数处理公式可在官方文档查阅。 以最简单的L1Loss出发,观察pytorch的损失函数是如何实现的 1. L1loss CLASS torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean') 功能: 计算output和target之差的绝对值,可选返回同维度的tensor(reduction=none)或一个标量(reduction=mean/sum)。 计算公式: 参数: size_average (bool, optional) – 已舍弃使用的变量,功能已经由reduction代替实现,仍旧保留是为了旧版本代码可以正常运行。reduce (bool, optional) – 已舍弃使用的变量,功能已经由reduction代替实现,仍旧保留是为了旧版本代码可以正常运行。reduction (string, optional) – 是否需要对loss进行“降维”,这里的reduction指是否将loss值进行取平均(mean)、求和(sum)或是保持原尺寸(none),这一变量在pytorch绝大多数损失函数中都有在使用,需要重点理解。 示例:代码 流程剖析 通过示例代码可知,loss_func是一个类实例,使用方式是loss_func(output, target)。 而nn.L1Loss是一个什么类?提供怎么样的接口来实现loss_func(output, target)的? 可跳转进入nn.L1Loss类定义,可以发现它继承_Loss,继续观察_Loss类,发现它继承nn.Module,既然是一个nn.Module,只需要在其内部实现一个forward()函数,就可以使类实例可以像函数一样被调用。 请看L1Loss类的实现: class L1Loss(_Loss): __constants__ = ['reduction'] def __init__(self, size_average=None, reduce=None, reduction: str = 'mean') -> None: super(L1Loss, self).__init__(size_average, reduce, reduction) def forward(self, input: Tensor, target: Tensor) -> Tensor: return F.l1_loss(input, target, reduction=self.reduction) L1Loss的forward函数接收两个变量,然后计算它们之差的绝对值,具体的实现委托给F.l1_loss函数 继续进入F.l1_loss一探究竟: def l1_loss( input: Tensor, target: Tensor, size_average: Optional[bool] = None, reduce: Optional[bool] = None, reduction: str = \"mean\", ) -> Tensor: if has_torch_function_variadic(input, target): return handle_torch_function( l1_loss, (input, target), input, target, size_average=size_average, reduce=reduce, reduction=reduction ) if not (target.size() == input.size()): warnings.warn( \"Using a target size ({}) that is different to the input size ({}). \" \"This will likely lead to incorrect results due to broadcasting. \" \"Please ensure they have the same size.\".format(target.size(), input.size()), stacklevel=2, ) if size_average is not None or reduce is not None: reduction = _Reduction.legacy_get_string(size_average, reduce) expanded_input, expanded_target = torch.broadcast_tensors(input, target) return torch._C._nn.l1_loss(expanded_input, expanded_target, _Reduction.get_enum(reduction)) F.l1_loss函数对输入参数相应的判断,例如传入的两个变量的维度必须一致,否则无法计算l1 loss。而具体公式的数值计算又委托给了torch._C._nn.l1_loss,torch._C._nn.l1_loss 就已经调用了python的C++拓展,底层代码是用C++语言编写,在python中就无法观察到,从这里大家可以知道pytorch大量的数值运算是借助了C++语言,毕竟python的底层运算比较慢。 关于C++底层代码,可依次观察: https://github.com/pytorch/pytorch/blob/master/torch/csrc/api/src/nn/modules/loss.cpp #include namespace F = torch::nn::functional; namespace torch { namespace nn { L1LossImpl::L1LossImpl(const L1LossOptions& options_) : options(options_) {} void L1LossImpl::reset() {} void L1LossImpl::pretty_print(std::ostream& stream) const { stream https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/native/Loss.cpp Tensor& l1_loss_out(const Tensor& input, const Tensor& target, int64_t reduction, Tensor& result) { if (reduction != Reduction::None) { auto diff = at::sub(input, target); auto loss = diff.is_complex() ? diff.abs() : diff.abs_(); if (reduction == Reduction::Mean) { return at::mean_out(result, loss, IntArrayRef{}); } else { return at::sum_out(result, loss, IntArrayRef{}); } } else { auto diff = input.is_complex() ? at::sub(input, target) : at::sub_out(result, input, target); return at::abs_out(result, diff); } } 从上述代码中可以看到,实际的L1Loss公式的实现是 auto diff = at::sub(input, target); auto loss = diff.is_complex() ? diff.abs() : diff.abs_(); 总结一下,Loss的实现流程如下图所示: 首先,损失函数继承Module,并实现forward函数,forward函数中完成具体公式计算;其次,具体的公式运算委托给nn.functional下函数实现;最后,pytorch大多的数值运算借助C++代码实现,具体在ATen/native/Loss.cpp 2. CrossEntropyLoss CLASS torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=- 100, reduce=None, reduction='mean', label_smoothing=0.0) 功能:先将输入经过softmax激活函数之后,再计算交叉熵损失。 在早期的pytorch中,是利用nn.LogSoftmax()和 nn.NLLLoss()实现的,现已经通过nn.CrossEntropyLoss()实现,不过官方文档中仍旧有提示:V1.11.0: \"Note that this case is equivalent to the combination of LogSoftmax and NLLLoss.\"V1.6.0: \"This criterion combines nn.LogSoftmax() and nn.NLLLoss() in one single class.\"\" 补充:小谈交叉熵损失函数交叉熵损失(cross-entropy Loss) 又称为对数似然损失(Log-likelihood Loss)、对数损失;二分类时还可称之为逻辑斯谛回归损失(Logistic Loss)。交叉熵损失函数表达式为 L = - sigama(y_i * log(x_i))。pytroch这里不是严格意义上的交叉熵损失函数,而是先将input经过softmax激活函数,将向量“归一化”成概率形式,然后再与target计算严格意义上交叉熵损失。 在多分类任务中,经常采用softmax激活函数+交叉熵损失函数,因为交叉熵描述了两个概率分布的差异,然而神经网络输出的是向量,并不是概率分布的形式。所以需要softmax激活函数将一个向量进行“归一化”成概率分布的形式,再采用交叉熵损失函数计算loss。 参数: weight (Tensor, optional) – 类别权重,用于调整各类别的损失重要程度,常用于类别不均衡的情况。 If given, has to be a Tensor of size Cignore_index (int, optional) – 忽略某些类别不进行loss计算。size_average (bool, optional) – 已舍弃使用的变量,功能已经由reduction代替实现,仍旧保留是为了旧版本代码可以正常运行。reduce (bool, optional) – 已舍弃使用的变量,功能已经由reduction代替实现,仍旧保留是为了旧版本代码可以正常运行。reduction (string, optional) – 是否需要对loss进行“降维”,这里的reduction指是否将loss值进行取平均(mean)、求和(sum)或是保持原尺寸(none),这一变量在pytorch绝大多数损失函数中都有在使用,需要重点理解。 label_smoothing (float, optional) – 标签平滑参数,一个用于减少方差,防止过拟合的技巧。详细请看论文《 Rethinking the Inception Architecture for Computer Vision》。通常设置为0.01-0.1之间,虽然理论值域为:A float in [0.0, 1.0]. 计算公式: 补图:https://pytorch.org/docs/1.11/generated/torch.nn.CrossEntropyLoss.html?highlight=crossentropyloss#torch.nn.CrossEntropyLoss C++底层代码实现: https://github.com/pytorch/pytorch/blob/master/aten/src/ATen/native/LossNLL.cpp Tensor cross_entropy_loss( const Tensor& self, const Tensor& target, const c10::optional& weight, int64_t reduction, int64_t ignore_index, double label_smoothing) { Tensor ret; if (self.sizes() == target.sizes()) { // Assume soft targets when input and target shapes are the same TORCH_CHECK(at::isFloatingType(target.scalar_type()), \"Expected floating point type for target with class probabilities, got \", target.scalar_type()); TORCH_CHECK(ignore_index weight_maybe_owned = at::borrow_from_optional_tensor(weight); const Tensor& weight_ = *weight_maybe_owned; ret = cross_entropy_loss_prob_target(self, target, weight_, reduction, label_smoothing); } else if (label_smoothing > 0.0) { TORCH_CHECK(label_smoothing weight_maybe_owned = at::borrow_from_optional_tensor(weight); const Tensor& weight_ = *weight_maybe_owned; ret = cross_entropy_loss_label_smoothing(self, target, weight_, reduction, ignore_index, label_smoothing); } else { auto class_dim = self.dim() == 1 ? 0 : 1; ret = at::nll_loss_nd( at::log_softmax(self, class_dim, self.scalar_type()), target, weight, reduction, ignore_index); } return ret; } Tensor & nll_loss_out(const Tensor & self, const Tensor & target, const c10::optional& weight_opt, int64_t reduction, int64_t ignore_index, Tensor & output) { // See [Note: hacky wrapper removal for optional tensor] c10::MaybeOwned weight_maybe_owned = at::borrow_from_optional_tensor(weight_opt); const Tensor& weight = *weight_maybe_owned; Tensor total_weight = at::empty({0}, self.options()); return std::get(at::nll_loss_forward_out(output, total_weight, self, target, weight, reduction, ignore_index)); } Tensor nll_loss(const Tensor & self, const Tensor & target, const c10::optional& weight_opt, int64_t reduction, int64_t ignore_index) { // See [Note: hacky wrapper removal for optional tensor] c10::MaybeOwned weight_maybe_owned = at::borrow_from_optional_tensor(weight_opt); const Tensor& weight = *weight_maybe_owned; return std::get(at::nll_loss_forward(self, target, weight, reduction, ignore_index)); } Tensor nll_loss_nd( const Tensor& self, const Tensor& target, const c10::optional& weight, int64_t reduction, int64_t ignore_index) { if (self.dim() 4 auto n = input_.sizes()[0]; auto c = input_.sizes()[1]; auto out_size = input_.sizes().slice(2).vec(); out_size.insert(out_size.begin(), n); if (target_.sizes().slice(1) != input_.sizes().slice(2)) { TORCH_CHECK( false, \"Expected target size \", IntArrayRef(out_size), \", got \", target_.sizes()); } input_ = input_.contiguous(); target_ = target_.contiguous(); // support empty batches, see #15870 if (input_.numel() > 0) { input_ = input_.view({n, c, 1, -1}); } else { input_ = input_.view({n, c, 0, 0}); } if (target_.numel() > 0) { target_ = target_.view({n, 1, -1}); } else { target_ = target_.view({n, 0, 0}); } if (reduction != Reduction::None) { ret = at::nll_loss2d(input_, target_, weight, reduction, ignore_index); } else { auto out = at::nll_loss2d(input_, target_, weight, reduction, ignore_index); ret = out.view(out_size); } } return ret; } 示例:代码 CrossEntropyLoss使用注意事项: target需要的是int类型,不需要one-hot向量形式; 类别需要从0开始计数,即10分类任务,类别index应当为0,1,2,3,4,5,6,7,8,9 小结 本小节重点剖析两个损失函数,学习pytorch损失函数的实现逻辑,请详细观察以下关系图,对后续编写其它千奇百怪的损失函数很有帮助。在深度学习中,损失函数还有很多,这里无法一一列举,感兴趣可以了解一下:https://github.com/JunMa11/SegLoss 以及目标检测中的IoU、GIoU、DIoU、CIoU等。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-5/5.2-Optimizer.html":{"url":"chapter-5/5.2-Optimizer.html","title":"5.2 十三个优化器","keywords":"","body":"5.2 十三个优化器 Optimizer 简介 有了数据、模型和损失函数,就要选择一个合适的优化器(Optimizer)来优化该模型,使loss不断降低,直到模型收敛。本节将介绍pytorch中优化器——Optimizer。 优化器的实现在torch.optim中,torch.optim is a package implementing various optimization algorithms. 在其中有一个核心类是Optimizer,Optimizer在pytorch提供的功能是所有具体优化器的基类,它对优化器进行了抽象与定义,约定了一个优化器应有的功能,Optimizer与十三个优化器的关系如下图所示: 通过上图可知道Optimizer定义了优化器应当具备的基础功能,如获取状态数据,加载状态数据,梯度清零,执行一步优化和添加参数组。 不同的优化方法会继承Optimizer,同时只需要实现不同的step()即可。这一点与损失函数类似,不同的损失函数只需要在forward()函数中定义好不同的公式计算即可。 本小节将以SGD为例,深入讲解优化器的以下函数,并具体地观察SGD算法的实现过程。 state_dict(self) load_state_dict(self, state_dict) zero_grad(self, set_to_none: bool = False) step(self, closure) add_param_group(self, param_group) 优化器工作方式 优化器如何工作,使得模型精度逐渐提高的?在此就不详细讲解,请大家自行补充机器学习基础概念。 众所周知,优化器是根据权重的梯度作为指导,定义权重更新的力度,对权重进行更新。 过程很简单,实现起来却是复杂的,上述过程涉及几个问题: 梯度哪里来? 更新哪些权重? 怎么执行权重更新? 依次回答上述问题,便可熟悉优化器工作方式。 梯度哪里来? 梯度通过loss的反向传播,得到每个权重的梯度值,其中利用pytorch的autograd机制自动求导获得各权重的梯度。 (如果对autograd机制不熟悉,请查看第二章第六节) 更新哪些权重?通过loss的反向传播,模型(nn.Module)的权重(Parameter)上有了梯度(.grad)值,但是优化器对哪些权重进行操作呢?实际上优化器会对需要操作的权重进行管理,只有被管理的权重,优化器才会对其进行操作。在Optimizer基类中就定义了add_param_group()函数来实现参数的管理。通常在实例化的时候,第一个参数就是需要被管理的参数。 怎么执行权重更新?通过上述UML类图不难发现,step()函数是进行优化操作的,step()函数中实现了对所管理的参数进行更新的步骤。 总结一下:优化器在实例化时告诉它,需要对哪些参数进行管理,然后再每个iteration迭代时,借助loss.backward()得到梯度,接着优化器干活 optimizer.step()完成一步参数更新。 (此过程可以回顾第二章第二节的模型训练代码) 优化器基类 Optimizer Optimizer类是所有具体优化器的基类(Base class for all optimizers.) 下面分别介绍Optimizer的基础属性及方法。 属性: 参数组(param_groups): 在finetune、某层定制学习率,某层学习率置零操作中,都会设计参数组的概念,因此首先了解参数组的概念非常有必要。 参数组是用于管理需要进行优化的那些参数,例如权值weight,偏置bias,BN的alpha/beta等。 注意,这里是参数组不是参数,表明可以将所有参数进行分组,区别对待。 例如在finetune过程中,通常让前面层的网络采用较小的学习率,后面几层全连接层采用较大的学习率, 这是我们就要把网络的参数划分为两组,每一组有它对应的学习率。正是因为这种针对不同参数需要不同的更新策略的需求,才有了参数组的概念。 参数组是一个list,其元素是一个dict,dict中包含,所管理的参数,对应的超参,例如学习率,momentum,weight_decay等等。 案例:chapter-5/02_optimizer.py state: 用于存储优化策略中需要保存的一些缓存值,例如在用momentum时,需要保存之前的梯度,这些数据保存在state中。 defaults: 优化方法默认的超参数; 方法: zero_grad() 功能:清零所管理参数的梯度。由于pytorch不会自动清零梯度,因此需要再optimizer中手动清零,然后再执行反向传播,得出当前iteration的loss对权值的梯度。 step() 功能:执行一步更新,依据当前的梯度进行更新参数 add_param_group(param_group) 功能:给optimizer管理的参数组中增加一组参数,可为该组参数定制lr,momentum,weight_decay等,在finetune中常用。 例如:optimizer_1.add_param_group({'params': w3, 'lr': 0.001, 'momentum': 0.8}) state_dict() 功能:获取当前state属性。 通常在保存模型时同时保存优化器状态,用于断点保存,下次继续从当前状态训练; load_state_dict(state_dict) 功能:加载所保存的state属性,恢复训练状态。 对优化器工作方式熟悉后,再看Optimizer的属性和方法就简单了,下面通过一个具体的优化算法来熟悉完整的优化器使用。 SGD概念 torch.optim.SGD详情请参照[官方文档]( https://pytorch.org/docs/stable/generated/torch.optim.SGD.html#torch.optim.SGD) SGD(stochastic gradient descent,随机梯度下降)是深度学习模型优化过程中最基础、最受欢迎、最稳定的一个,即使优化算法层出不穷,仅pytorch就提供了十三个,但目前绝大多数论文中仍旧采用SGD进行训练,因此SGD必须掌握。 SGD核心理论知识是梯度下降( gradient descent),即沿着梯度的负方向,是变化最快的方向。 而随机则指的是一次更新中,采用了一部分样本进行计算,即一个batch的数据可以看作是整个训练样本的随机采样。更多关于SGD的理论知识请自行学习机器学习基础。 SGD更新公式可简化为 w新 = w旧 - w_grad,即参数减去梯度(学习率为1) 不过通常会加入学习率来调整更新的步伐:w_新 = w_旧 - (lr * w_grad) 对于加入L2正则(weight decay)时,变为:w_新 = w_旧 - (lr (w_grad + weight_decay w_旧)) L2正则称之为weight decay的原因,对比公式1发现W需要乘以一个小于1的系数,因此是衰减的:w_新 = w_旧 - (lr (w_grad + weight_decay w_旧)) = w_旧(1 - lrweight_decay) - (lr w_grad) 对于其它momentum 、dampening 和nesterov 的加入就不详细展开,可通过官方文档查阅: SGD使用 请结合代码观察SGD的使用 第一步:实例化:optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9, weight_decay=5e-4) 第二步:loss.backward()之前进行梯度清零:optimizer.zero_grad() 第三步:loss.backward()之后执行一步更新:optimizer.step() 在代码中还有一处用到了optimizer,那就是学习率调整模块: scheduler = optim.lr_scheduler.StepLR(optimizer, gamma=0.1, step_size=50) 原理是将optimizer放到lr_scheduler进行管理,lr_scheduler会修改optimizer中的学习率 SGD代码实现 SGD类继承于Optimizer类,并重写step函数实现核心功能。 SGD类中step函数首先对各参数组、参数进行超参数的获取确定:for group in self.param_groups: 然后借助functional.sgd函数实现公式计算: F.sgd(params_with_grad, d_p_list, momentum_buffer_list, weight_decay=weight_decay, momentum=momentum, lr=lr, dampening=dampening, nesterov=nesterov, maximize=maximize,) 下面重点进入/torch/optim/_functional.py 的sgd()观察:这里为了讲解过程,省略了momentum的代码: d_p:是梯度 weight_decay:是权重衰减系数,如 0.0001 param:是具体的权重参数 lr:是学习率 依194行代码: param = param + (alpha)* d_p 依193行代码: param = param + (-lr) d_p = **param - lr \\ d_p** 依177行代码: param = param - lr (d_p + weight_decay param) = param(1 - lr * weight_decay) - lr*d_p 到这里,就可以与上述理论部分对应上了。 小结 其余十二个优化器可通过官方文档查阅,相信大家熟悉SGD以及优化器使用逻辑,其它的优化器都可轻松掌握。 Adadelta Implements Adadelta algorithm. Adagrad Implements Adagrad algorithm. Adam Implements Adam algorithm. AdamW Implements AdamW algorithm. SparseAdam Implements lazy version of Adam algorithm suitable for sparse tensors. Adamax Implements Adamax algorithm (a variant of Adam based on infinity norm). ASGD Implements Averaged Stochastic Gradient Descent. LBFGS Implements L-BFGS algorithm, heavily inspired by minFunc. NAdam Implements NAdam algorithm. RAdam Implements RAdam algorithm. RMSprop Implements RMSprop algorithm. Rprop Implements the resilient backpropagation algorithm. SGD Implements stochastic gradient descent (optionally with momentum). 下面梳理pytorch整个优化器实现的逻辑关系: Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-5/5.3-lr-scheduler.html":{"url":"chapter-5/5.3-lr-scheduler.html","title":"5.3 十四个学习率调整器","keywords":"","body":"5.3 学习率调整策略 深度学习模型训练中调整最频繁的就属学习率了,好的学习率可以使模型逐渐收敛并获得更好的精度。 本小节将介绍pytorch提供的十四种学习率调整方法,首先了解lr_scheduler整体结构设计,然后以StepLR为例,深度剖析一个学习率调整器的源代码实现细节,最后总结十四个学习率调整策略。 lr_scheduler 设计 lr_scheduler模块的设计与优化器一样,定义了一个核心基类_LRScheduler,在_LRScheduler中设计好学习率调整器的属性与方法。 核心属性有optimizer、base_lrs和last_epoch。 optimizer是调整器所管理的优化器,优化器中所管理的参数组有对应的学习率,调整器要调整的内容就在那里。 base_lrs是基础学习率,来自于optimizer一开始设定的那个值。self.base_lrs = [group['initial_lr'] for group in optimizer.param_groups] last_epoch是记录迭代次数,通常用于计算下一轮学习率。注意,默认初始值是-1,因为last_epoch的管理逻辑是执行一次,自加1。 核心方法有state_dict()、load_state_dict()、get_last_lr()、get_lr()、print_lr()、step()。 state_dict()和load_state_dict()分别是获取调整器的状态数据与加载状态数据。 get_last_lr()、get_lr() 分别为获取上一次和当前的学习率。 print_lr()是打印学习率。 step()为更新学习率的接口函数,使用者调用 scheduler.step()即完成一次更新。 lr_scheduler 使用流程 第一步:实例化。scheduler = optim.lr_scheduler.StepLR(optimizer, gamma=0.1, step_size=50) 第二步:合适的位置执行step()。大家要注意,不同的调整器的更新策略是不一样的,有的是基于epoch维度,有的是基于iteration维度,这个需要注意。 StepLR 源代码解读 StepLR是依据设定的step_size,以固定的间隔进行调整,调整规则为 lr_new = lr_old * gamma, 通常gamma设置为0.1,即一定次数训练后,学习率下降10倍。 下面观察代码,在代码104行与146行打断点进行调试,依次观察初始化过程、step更新过程。 初始化过程: StepLR类__init__()函数: self.step_size = step_size self.gamma = gamma super(StepLR, self).__init__(optimizer, last_epoch, verbose) 跳转到_LRScheduler类__init__()函数: 第一步:获取初始学习率:group.setdefault('initial_lr', group['lr']) 第二步:记录基础学习率:self.base_lrs = [group['initial_lr'] for group in optimizer.param_groups] self.last_epoch = last_epoch 第三步:执行首次step(),也就是在初始化的时候会自动执行一次step(),这也是为什么last_epoch的默认值是-1的原因。 step中会进行第0个epoch的学习率获取,具体过程如代码所示: torch/optim/lr_scheduler.py 151至168行 with _enable_get_lr_call(self): if epoch is None: self.last_epoch += 1 values = self.get_lr() else: warnings.warn(EPOCH_DEPRECATION_WARNING, UserWarning) self.last_epoch = epoch if hasattr(self, \"_get_closed_form_lr\"): values = self._get_closed_form_lr() else: values = self.get_lr() for i, data in enumerate(zip(self.optimizer.param_groups, values)): param_group, lr = data param_group['lr'] = lr self.print_lr(self.verbose, i, lr, epoch) self._last_lr = [group['lr'] for group in self.optimizer.param_groups] step更新过程 在初始化过程已经进行过一次step,这里再仔细分析一遍。 首先通过154行代码,获取新一轮学习率 values = self.get_lr() 然后通过165行代码,对优化器中的学习率进行更新param_group['lr'] = lr 可以发现step()函数由基类_LRScheduler实现,即所有学习率调整器均采用这个流程,具体的一步更新策略则委托给子类的.get_lr()函数,这样的架构设计值得学习。 此处,关注的是StepLR,下面深入 StepLR.get_lr()函数观察。 if (self.last_epoch == 0) or (self.last_epoch % self.step_size != 0): return [group['lr'] for group in self.optimizer.param_groups] return [group['lr'] * self.gamma for group in self.optimizer.param_groups] 可以看到,核心在两个return行。 第一个return表示未达到step_size,新学习率保持不变 第二个return表示达到step_size,学习率均需要乘以 gamma。 至此,整个学习率更新过程及逻辑讲解完毕,其余十三个学习率调整整体逻辑一样,均是step()与get_lr()的配合来实现学习率调整,请大家举一反三,自行深入研究其它调整器。 十四个调整器汇总 lr_scheduler.LambdaLR Sets the learning rate of each parameter group to the initial lr times a given function. lr_scheduler.MultiplicativeLR Multiply the learning rate of each parameter group by the factor given in the specified function. lr_scheduler.StepLR Decays the learning rate of each parameter group by gamma every step_size epochs. lr_scheduler.MultiStepLR Decays the learning rate of each parameter group by gamma once the number of epoch reaches one of the milestones. lr_scheduler.ConstantLR Decays the learning rate of each parameter group by a small constant factor until the number of epoch reaches a pre-defined milestone: total_iters. lr_scheduler.LinearLR Decays the learning rate of each parameter group by linearly changing small multiplicative factor until the number of epoch reaches a pre-defined milestone: total_iters. lr_scheduler.ExponentialLR Decays the learning rate of each parameter group by gamma every epoch. lr_scheduler.CosineAnnealingLR Set the learning rate of each parameter group using a cosine annealing schedule, where \\eta{max}ηmax is set to the initial lr and T{cur}Tcu**r is the number of epochs since the last restart in SGDR: lr_scheduler.ChainedScheduler Chains list of learning rate schedulers. lr_scheduler.SequentialLR Receives the list of schedulers that is expected to be called sequentially during optimization process and milestone points that provides exact intervals to reflect which scheduler is supposed to be called at a given epoch. lr_scheduler.ReduceLROnPlateau Reduce learning rate when a metric has stopped improving. lr_scheduler.CyclicLR Sets the learning rate of each parameter group according to cyclical learning rate policy (CLR). lr_scheduler.OneCycleLR Sets the learning rate of each parameter group according to the 1cycle learning rate policy. lr_scheduler.CosineAnnealingWarmRestarts Set the learning rate of each parameter group using a cosine annealing schedule, where \\eta{max}ηmax is set to the initial lr, T{cur}Tcu**r is the number of epochs since the last restart and T_{i}T**i is the number of epochs between two warm restarts in SGDR: 小结 本小节通过lr_scheduler结构设计出发,分析十四个优化器是如何组织实现的,并通过_LRScheduler基类与StepLR的代码讲解学习率调整的机制。 总结下来即:初始化时建立scheduler与optimizer的联系,scheduler在每次step()时修改optimizer中的lr,step()获取新lr的功能委托给get_lr()函数实现。 最后对六个学习率调整器进行了测试并绘图,绘图代码位于配套代码 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/":{"url":"chapter-6/","title":"第六章 PyTorch 可视化模块","keywords":"","body":"第六章 PyTorch 可视化模块 第六章 PyTorch 可视化模块 6.1 TensorBoard安装与使用 6.2 CNN卷积核与特征图可视化 6.3 混淆矩阵与训练曲线可视化 6.4 CAM可视化与hook函数使用 6.5 模型参数打印 第六章简介 本章介绍可视化工具,包括TensorBoard可视化工具,混淆矩阵,CNN卷积核与特征图可视化,分类模型注意力算法——Grad CAM,模型参数量可视化。 首先对强大的可视化工具TensorBoard进行讲解,介绍其提供的十多个数据可视化API。 然后借助Tensorboard的绘图功能,观察CNN卷积核与特征图的变化过程,同时对分类混淆矩阵进行分析。 接着介绍一个实用的分类模型注意力机制可视化算法——Grad CAM。 最后介绍一系列对模型参数量分析的工具。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.1-tensorboard.html":{"url":"chapter-6/6.1-tensorboard.html","title":"6.1 TensorBoard安装与使用","keywords":"","body":"6.1 Tensorboard 基础与使用 Tensorboard是TensorFlow中提供的可视化工具,它能可视化数据曲线、模型拓扑图、图像、统计分布曲线等。 在PyTorch中,早期是不支持Tensorboard,采用了TensorboardX作为替身,现在PyTorch已经支持Tensorboard的使用,本节就介绍Tensorboard工具的概念、原理以及使用方法。 tensorboard 安装 首先运行以下代码,观察报错,通过报错信息指引我们安装tensorboard。 import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) from torch.utils.tensorboard import SummaryWriter log_dir = BASE_DIR # 即test_tensorboard.py文件所在目录 writer = SummaryWriter(log_dir=log_dir, filename_suffix=\"_test_tensorboard\") # writer = SummaryWriter(comment=\"test01\", filename_suffix=\"_test_tensorboard\") x = range(100) for i in x: writer.add_scalar('y=2x', i * 2, i) writer.add_scalar('y=pow(2, x)', 2 ** i, i) writer.close() 直接运行代码,提示: import tensorboard ModuleNotFoundError: No module named 'tensorboard' 只需要在命令窗口中执行: pip install tensorboard 重新运行代码,获得event file文件,《events.out.tfevents.1654508983.dream.5756.0》 tensorboard 初体验 通过以上步骤得到了一个event file 文件,下面需要启动tensorboard软件对event file文件进行可视化。 tensorboard基本原理是这样的 python代码中将可视化的数据记录到event file中,保存至硬盘 采用tensorboard对event file文件进行读取,并在web端进行可视化 启动步骤如下: tensorboard --logdir=your path dir 在terminal中执行以下命令即可,注意必须是文件夹,不能是文件名,tensorboard会将文件夹下所有event file都可视化 得到TensorBoard 2.0.0 at http://localhost:6006/ (Press CTRL+C to quit)之类的提示 然后复制网址http://localhost:6006/ 到浏览器中进行打开,就得到如下界面 在tensorboard启动的过程中,可能发生的问题: 1. 6006端口被占用: port 6006 was already in use E0117 15:58:38.631224 MainThread program.py:260] TensorBoard attempted to bind to port 6006, but it was already in use TensorBoard attempted to bind to port 6006, but it was already in use 解决方法:修改端口为6007 tensorboard --logdir=your_dir --port=6007 到这里可以知道,tensorboard软件是一个web应用,它对指定目录下的event file进行可视化,可视化页面拥有一系列功能按钮,供开发者使用。 通过demo代码,我们绘制了两条曲线,除了绘制曲线,tensorboard还又许多强大可视化功能,下面就介绍如何进行其它数据类型的可视化。 SummaryWriter类介绍 tensorboard环境配置好了,下面要重点学习在python代码中如何把各类数据合理的写入event file,然后用tensorboard软件进行可视化。 在pytorch代码中,提供了SummaryWriter类来实现数据的写入,请阅读官方文档对SummaryWriter的描述: The SummaryWriter class provides a high-level API to create an event file in a given directory and add summaries and events to it. The class updates the file contents asynchronously. This allows a training program to call methods to add data to the file directly from the training loop, without slowing down training. 首先来学习SummaryWriter的参数设置,然后学习它提供的一些列写入方法,如add_scalars、add_histogram和add_image等等。 CLASS torch.utils.tensorboard.writer.SummaryWriter(log_dir=None, comment='', purge_step=None, max_queue=10, flush_secs=120, filename_suffix='') 属性: log_dir (string) – 文件保存目录设置,默认为 runs/current_datetime_hostname comment (string) – 当log_dir采用默认值时,comment字符串作为子目录 purge_step (int) – ? max_queue (int) –? flush_secs (int) – 磁盘刷新时间,默认值为120秒 filename_suffix (string) –文件名后缀 方法: add_scalar add_scalar(tag, scalar_value, global_step=None, walltime=None, new_style=False, double_precision=False) 功能:添加标量;tag的设置可以有个技巧是在同一栏下绘制多个图,如'Loss/train', 'Loss/Valid', 这就类似于matplotlib的subplot(121), subplot(122) tag (string) – Data identifier scalar_value (float or string/blobname) – Value to save global_step (int) – Global step value to record 代码实现: add_scalars add_scalars(main_tag, tag_scalar_dict, global_step=None, walltime=None) 功能:在一个坐标轴中绘制多条曲线。常用于曲线对比。 main_tag (string) – The parent name for the tags tag_scalar_dict (dict) – Key-value pair storing the tag and corresponding values global_step (int) – Global step value to record 核心在于tag_scalar_dict 字典中存放多条曲线的数值。 代码实现: add_histogram add_histogram(tag, values, global_step=None, bins='tensorflow', walltime=None, max_bins=None) 功能:绘制直方图。这里的global_step表明会得到多个直方图,详情请看图理解。 在tensorboard界面,需要进入HISTOGRAM中才能看到直方图可视化。 tag (string) – Data identifier values (torch.Tensor, numpy.array, or string/blobname) – Values to build histogram global_step (int) – Global step value to record bins (string) – One of {‘tensorflow’,’auto’, ‘fd’, …}. This determines how the bins are made. 代码实现: add_image add_image(tag, img_tensor, global_step=None, walltime=None, dataformats='CHW') 功能:绘制图像。 tag (string) – Data identifier img_tensor (torch.Tensor, numpy.array, or string/blobname) – Image data global_step (int) – Global step value to record dataformats- 数据通道顺序物理意义。默认为 CHW 代码实现: add_images add_images(tag, img_tensor, global_step=None, walltime=None, dataformats='NCHW') 功能:绘制图像序列,常用于数据清洗,卷积核,特征图的可视化。 Add batched image data to summary.Note that this requires the pillow package. tag (string) – Data identifier img_tensor (torch.Tensor, numpy.array, or string/blobname) – Image data global_step (int) – Global step value to record walltime (float) – Optional override default walltime (time.time()) seconds after epoch of event dataformats (string) – Image data format specification of the form NCHW, NHWC, CHW, HWC, HW, WH, etc. 代码实现: add_figure add_figure(tag, figure, global_step=None, close=True, walltime=None) 功能:将matplotlib的figure绘制到tensorboard中。 Render matplotlib figure into an image and add it to summary.Note that this requires the matplotlib package. tag (string) – Data identifier figure (matplotlib.pyplot.figure) – Figure or a list of figures global_step (int) – Global step value to record close (bool) – Flag to automatically close the figure walltime (float) – Optional override default walltime (time.time()) seconds after epoch of event 剩下一些高级函数,就不一一讲解,用法雷同,再次仅汇总,供需使用。 add_video:绘制视频 add_audio:绘制音频,可进行音频播放。 add_text:绘制文本 add_graph:绘制pytorch模型拓扑结构图。 add_embedding:绘制高维数据在低维的投影 add_pr_curve:绘制PR曲线,二分类任务中很实用。 add_mesh:绘制网格、3D点云图。 add_hparams:记录超参数组,可用于记录本次曲线所对应的超参数。 小结 pytorch中使用tensorboard非常简单,只需要将想要可视化的数据采用SummaryWriter类进行记录,存在硬盘中永久保存,然后借助tensorboard软件对event file进行可视化。 tensorboard是非常强大的可视化工具,可以很好帮助开发者分析模型开发过程中的各个状态,如监控loss曲线观察模型训练情况,绘制梯度分布直方图观察是否有梯度消失,绘制网络拓扑图观察网络结构等,请大家多留意SummaryWriter官方文档的介绍,了解最新SummaryWriter有什么方法可用。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.2-cnn-visualization.html":{"url":"chapter-6/6.2-cnn-visualization.html","title":"6.2 CNN卷积核与特征图可视化","keywords":"","body":"6.2 CNN卷积核与特征图可视化 众所周知,深度学习仍是一个黑盒子,模型内部的逻辑含义仍旧无法解释,越是未知的东西,越能激起人们的好奇心。 在卷积神经网络中,有时会对卷积核以及特征图进行可视化,以此观察卷积神经网络学习到了何种模式。 作为深度卷积神经网络的开山之作,AlexNet(2012年)就已经对卷积核的模式进行了分析,论文中发现卷积核的学习具有偏向性,一部分学习颜色特征,一部分学习边缘特征,详见下图: 紧接着AlexNet之后的2013年,ZFNet对CNN的特征图进行了可视化,进一步的探究卷积神经网络的奥秘。 AlexNet:《ImageNet Classification with Deep Convolutional Neural Networks》 ZFNet:《Visualizing and understanding convolutional networks》 本节就利用tensorboard以及pytorch的函数对AlexNet的卷积核与特征图进行可视化。 make_grid 函数 在图像任务中,往往需要人眼审核、观察大批量图像数据,如果一张一张的观察,效率会非常低。 通常会将一批数据绘制成网格图片,类似大排档的菜单一样,这样便于观察。 在pytorch的torchvision库中,提供了make_grid函数帮助大家完成网格图片制作。下面先学习make_grid函数,再用它绘制卷积核与特征图。 `torchvision.utils.make_grid(tensor: Union[torch.Tensor, List[torch.Tensor]], nrow: int = 8, padding: int = 2, normalize: bool = False, value_range: Optional[Tuple[int, int]] = None, scale_each: bool = False, pad_value: float = 0.0, **kwargs) 功能: 将一组图片拼接成一张网格图片,便于可视化。 参数: tensor(Tensor or list)- 需可视化的数据,shape:(B x C x H x W) ,B表示batch数,即几张图片 nrow(int)- 一行显示几张图,默认值为8。 padding(int)- 每张图片之间的间隔,默认值为2。 normalize(bool)- 是否进行归一化至(0,1)。 value_range(tuple)- 设置归一化的min和max,若不设置,默认从tensor中找min和max。 scale_each(bool)- 每张图片是否单独进行归一化,还是min和max的一个选择。 pad_value(float)- 填充部分的像素值,默认为0,即黑色。 关于输入:make_grid的输入可以分为两种: 一种是4D张量,函数自动将第一维度作为图片数量进行拆解。 一种是list,元素必须是张量形式,并且张量的shape必须一致。 这两种输入分别对应两种常用场景: 4D张量:卷积核大小与特征图张量都适合用这种形式。 list:对普通图片进行可视化观察,一次加载一张图片时使用。 另外,对于像素的转换也需要注意相应策略,对于float类型的数据,需要设置归一化的策略,策略由value_range和scale_each构成,请自行调整观察变化。 请看代码使用效果: Alexnet卷积核可视化 要对卷积核进行可视化,就需要对pytorch的nn.Module类非常熟悉,要了解卷积核以怎样的形式?存储在哪里? 2D卷积的卷积核权重是一个4D张量,包含输入通道,输出通道,高,宽。 注意:除了第一层可以将 输入通道 *高*宽作为 RGB图像进行可视化之外,其余网络层只能将高*宽作为灰度图像(2D)进行可视化。 卷积核存储在nn.Conv2D的weight变量中,下面就可以通过如下代码获得。 for sub_module in alexnet.modules(): # 非卷积层则跳过 if isinstance(sub_module, nn.Conv2d): # 获取conv2d层的权重,即卷积核权重 kernels = sub_module.weight 有了4D张量,剩下就按部就班的绘制到grid中可视化即可,完整代码生成的可视化图像如下所示: 可以看到,alexnet模型的第一层的卷积核确实学习到了不同模式,有边缘模式,有色彩模式。 Alexnet特征图可视化 特征图可视化与卷积核可视化类似,需要知道特征图以怎样的形式?从哪里获得? 常规任务中,特征图是4D张量(BCHW)。 但是获得就没有那么简单,因为特征图是中间数据,通常不会保留,在前向运算过程中,不再使用的特征图会被舍弃。 因此需要特殊方法获得特征图,本节介绍一种笨办法,但容易理解,就是将对应层(仍旧是一个nn.Module)拿出来,然后把图片仍给网络层(仍旧是一个nn.Module),其输出的就是特征图了。 更高级的方法是利用hook函数机制完成中间特征图的获取,这个在本章的后半部分会介绍。 请看核心代码 alexnet = models.alexnet(pretrained=True) # forward convlayer1 = alexnet.features[0] fmap_1 = convlayer1(img_tensor) 这样就获得了(1, 64, 55, 55)的4D张量(fmap_1),然后将其转换成能可视化的形式,再利用tensorboard绘制。 完整代码生成的可视化图像如下所示: 小结 本节介绍了tensorboard对卷积核与特征图的绘制,其中涉及非常实用的函数make_grid,make_grid可以帮助开发人员高效的审核图片,人眼看图是算法工程师最重要的一步,因为模型是没办法知道哪张图片的标签搞错了! 下一小节将介绍在模型训练过程中,如何基于tensorboard进行监控模型状态。 最后留一个思考题,将卷积核与特征图放到一起去比较,大家能否找到什么规律? 边缘模式的卷积核似乎能过滤掉大部分细节,仅留下边缘;还能看到清晰原图信息的特征图所对应的卷积核,都是颜色卷积核。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.3-conf_matrix.html":{"url":"chapter-6/6.3-conf_matrix.html","title":"6.3 混淆矩阵与训练曲线可视化","keywords":"","body":"6.3 混淆矩阵与训练曲线可视化 在分类任务中,通过混淆矩阵可以看出模型的偏好,而且对每一个类别的分类情况都了如指掌,为模型的优化提供很大帮助。本节将介绍混淆矩阵概念及其可视化。 为了演示混淆矩阵与训练曲线,本节代码采用cifar10数据集进行训练,模型采用resnet系列。 数据cifar-10-python.tar.gz 可从 \"https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz\" 下载,放到指定文件夹节课,无需解压,代码会自动解压。 混淆矩阵概念 混淆矩阵(Confusion Matrix)常用来观察分类结果,其是一个N*N的方阵,N表示类别数。 混淆矩阵的行表示真实类别,列表示预测类别。例如,猫狗的二分类问题,有猫的图像10张,狗的图像30张,模型对这40张图片进行预测,得到的混淆矩阵为 阿猫 阿狗 阿猫 7 3 阿狗 10 20 从第一行中可知道,10张猫的图像中,7张预测为猫,3张预测为狗,猫的召回率(Recall)为7/10 = 70%, 从第二行中可知道,30张狗的图像中,8张预测为猫,22张预测为狗,狗的召回率为20/30 = 66.7%, 从第一列中可知道,预测为猫的17张图像中,有7张是真正的猫,猫的精确度(Precision)为7 / 17 = 41.17% 从第二列中可知道,预测为狗的23张图像中,有20张是真正的狗,狗的精确度(Precision)为20 / 23 = 86.96% 模型的准确率(Accuracy)为 7+20 / 40 = 67.5% 可以发现通过混淆矩阵可以清晰的看出网络模型的分类情况,若再结合上颜色可视化,可方便的看出模型的分类偏好。 本小节将介绍,混淆矩阵的统计及其可视化。 混淆矩阵的统计 混淆矩阵的绘制将借助matplotlib的imshow功能,在imshow中可对矩阵进行上色,colorbar可自行调整,如本例中采用的黑白调,也可以选择其他的colorbar。 在模型训练中,通常以一个epoch为单位,进行混淆矩阵的统计,然后绘制,代码思路如下: 第一步:创建混淆矩阵 获取类别数,创建N*N的零矩阵 conf_mat = np.zeros([cls_num, cls_num]) 第二步:获取真实标签和预测标签 labels 为真实标签,通常为一个batch的标签 predicted为预测类别,与labels同长度 第三步:依据标签为混淆矩阵计数 for j in range(len(labels)): cate_i = labels[j].cpu().numpy() pre_i = predicted[j].cpu().numpy() conf_mat[cate_i, pre_i] += 1. 混淆矩阵可视化 混淆矩阵可视化已经封装成一个函数show_conf_mat,函数位于 配套代码 show_conf_mat(confusion_mat, classes, set_name, out_dir, epoch=999, verbose=False, perc=False) 参数: \"\"\" 混淆矩阵绘制并保存图片 :param confusion_mat: nd.array :param classes: list or tuple, 类别名称 :param set_name: str, 数据集名称 train or valid or test? :param out_dir: str, 图片要保存的文件夹 :param epoch: int, 第几个epoch :param verbose: bool, 是否打印精度信息 :param perc: bool, 是否采用百分比,图像分割时用,因分类数目过大 :return: \"\"\" show_conf_mat函数内部原理就不再详细展开,都是matplotlib的基础知识。下图为最终效果图: show_conf_mat函数提供png的保存,不便于观察整个训练过程的变化,这里借助tensorboard的add_figure功能,将每个epoch的混淆矩阵保存到tensorboard中,然后可拖拽的形式观察模型精度的变化情况。 效果如下图: 从上述变化可以发现模型在迭代过程中的偏好,前后对比图可很好的帮助工程师分析模型的偏好。 当global_step比较多的时候,toolbar无法展示每一个step,这需要在启动tensorboard的时候设置一下参数即可 tensorboard --logdir=./Result --samples_per_plugin images=200 除了手动绘制之外,sklearn库也提供了混淆矩阵绘制(from sklearn.metrics import confusion_matrix),这里不再拓展。 训练曲线绘制 除了混淆矩阵,在模型训练过程中最重要的是观察loss曲线的变化,loss曲线变化趋势直接决定训练是否需要停止,并指引我们进行参数的调整。 loss曲线是需要将训练与验证放在一起看的,单独看一条曲线是不够的,这一点需要大家了解模型评估中的方差与偏差的概念。 通过训练loss看偏差,通过训练loss与验证loss看方差。 偏差看的是模型拟合能力是否足够,方差是看模型泛化性能是否足够,是否存在过拟合。 将两条曲线绘制到一个坐标系里,可以借助tensorboard的add_scalars函数,具体请看代码 在训练集的迭代之后记录训练集的loss writer.add_scalars('Loss_group', {'train_loss': loss_avg}, epoch) 在验证集的迭代之后记录训练集的loss writer.add_scalars('Loss_group', {'valid_loss': loss_avg}, epoch) 在这里可以发现,SummaryWriter类的函数是以tag变量进行区分不同的坐标系,以上例子看出,虽然在两个地方执行代码,但是通过tag=\"Loss_group\",仍旧可以把它们绘制在一个坐标系里。 小结 以上就是在训练过程中记录必要的训练信息,用于监控模型训练状态。 下一节将介绍有趣的CAM可视化实现,以及nn.Module模块中的系列hook函数使用。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.4-cam-vis.html":{"url":"chapter-6/6.4-cam-vis.html","title":"6.4 CAM可视化与hook函数使用","keywords":"","body":"6.4 CAM可视化与hook函数使用 前文说到,在本章第二节介绍CNN的可视化时我们知道,深度学习模型仍是一个黑箱,大家想尽办法对其进行可视化,本节就介绍一个实用的分析方法CAM(Class activation mapping,类激活图),如下图所示: 以上图为例,CAM方法是探究模型将图片分为“Dog”的依据是什么,即图片中哪些区域支撑了模型将图片判别为\"Dog\"。 其背后的原理是将网络中间的特征图进行加权并可视化,而权重的得来就有多种方法,不同的方法就有了不同的CAM算法,如CAM、Grad CAM和Grad CAM++。 CAM:《2016-CAM-Learning Deep Features for Discriminative Localization》 Grad-CAM:《2017-Grad-CAM Visual Explanations from Deep Networks via Gradient-based Localization》 Grad-CAM++:《2018-Grad-CAM++ Generalized Gradient-based Visual Explanations for Deep Convolutional Networks》 本文将介绍其演变过程,并用手写代码实现Grad CAM,同时借助强大的github仓库,实现多种热力图对比,如下图所示: CAM 论文名称:《Learning Deep Features for Discriminative Localization》 原理解释:CAM方法需要将CNN模型修改后重新训练,修改的目的就是为了获得加权权重,具体方式如下图所示: 将最后一层特征图之后的层移除,并接入一个全连接层用于分类,全连接层的输入是特征图的全局池化后的特征向量。 最终热力图通过分类类别神经元所对应的权重w,乘以最后一层特征图,再求和即可。 背后逻辑有两个: 1、最后一层特征图是原图经过一系列卷积后保留下来的特征,其与原图的空间关系一一对应,空间关系指左上角对应左上角,右下角对应原图右下角,等等。 2、特征图对于分类类别(如上图的AustralianTerrier)的贡献程度与全连接层权重密切相关 因此,只需要利用全连接层的权重乘以对应通道,即可得到热力图。 Grad CAM 论文名称:《Grad-CAM Visual Explanations from Deep Networks via Gradient-based Localization》 CAM的思路非常巧妙,但缺点很明显,它需要对模型进行修改,并且重新训练,不适用于大多数场景。 为此,研究者提出额Grad CAM,可以对模型直接进行观察,无需改动模型。 Grad CAM的思路也非常巧妙,在CAM思路中有两个要素: 1、特征图 2、特征图对应指定类别的权重 特征图很容易获得,但特征图的重要性(加权权重)应该如何寻找? Grad CAM给出了不一样的答案,Grad CAM利用梯度求导获得特征图的重要性权重。 原理分析: 假设最后一层feature maps有10个,那么如何寻找10个权值用来指示这些feature maps对某一类别的重要性呢? CAM是通过对feature maps进行GAP,然后采用该类别与这个10个GAP后的神经元的连接权值作为权值; 而Grad-CAM采用的则是梯度,是该类别对于这10个feature maps的求取梯度。 注意:最终求取的梯度是一个110的向量,即每个feature map对应一个标量,而对feature maps求取梯度是一个矩阵,作者是通过对*矩阵求均值得到的标量。 对于类别c,特征图的权值 a_k^c 计算公式如下: c表示第c类,k表示第k个特征图,Z表示特征图像素点总数,i表示行,j表示列,A表示特征图。 公式可以分两部分看,最右边 gradients via backprop,即对类别c求得特征图的梯度,是一个二维的矩阵; 再通过左边的global average pooling,对二维的梯度矩阵求平均值,得到第k个特征图对于第c类的权值。 示意图如下: 最终的热力图通过以下公式进行求取: 与CAM不同的是,Grad-CAM在加权求和后还加上了ReLU函数,计算公式如下: 之所以加上ReLU,是因为在这里只关注正的梯度值,而不关注负的。 最后将Discriminative Localization Map直接resize至原图大小即可,如最后一层feature maps是14*14的,原图是224*224,则将14*14的图缩放到224*224即可。 Grad-CAM++ 论文名称:《Grad-CAM++ Generalized Gradient-based Visual Explanations for Deep Convolutional Networks》 事物总是在不断的发展,Grad CAM还存在以下缺点: 当图片出现多个同类物体时,无法定位;(1-3列) 单物体图片中,定位错误。(4-6列) 并且, Grad-CAM中一个特征图对应的权重是对特征图的梯度求平均,即认为特征图的梯度(2维数据)上的每个元素同等重要。 而Grad-CAM++则认为特征图的梯度上的每个元素重要程度应当不一样,因此对Grad CAM进行了改进。 Grad-CAM++ 热力图的权重计算通过以下公式: CAM系列对比 CAM、Grad-CAM和Grad-CAM++的区别如下图所示: 关于CAM还有很多迭代改进,可参考这个repo repo中对数十个CAM进行了实现,建议使用。 本节将用代码手动的实现Grad CAM,并通过算法的实现来学习nn.module中的hook函数使用。 nn.module中的hook函数 在CAM系列算法中知道,需要利用中间层的特征图,可nn.module并不会返回、保留中间层的特征图。这时,就要用到nn.module中的hook函数,把中间层特征图给拿出来。 什么是hook函数? pytorch中的hook是一个非常有意思的概念,hook意为钩、挂钩、鱼钩。 引用知乎用户“马索萌”对hook的解释:“(hook)相当于插件。可以实现一些额外的功能,而又不用修改主体代码。把这些额外功能实现了挂在主代码上,所以叫钩子,很形象。” 简单讲,就是不修改主体,而实现额外功能。对应到在pytorch中,主体就是forward和backward,而额外的功能就是对模型的变量进行操作 pytorch提供的hook 1. torch.Tensor.register_hook 功能:注册一个反向传播hook函数,这个函数是Tensor类里的,当计算tensor的梯度时自动执行。 形式: hook(grad) -> Tensor or None ,其中grad就是这个tensor的梯度。 返回值:a handle that can be used to remove the added hook by calling handle.remove() 案例请看配套代码 2. torch.nn.Module.register_forward_hook 功能:Module前向传播中的hook,module在前向传播后,自动调用hook函数。 形式:hook(module, input, output) -> None。注意不能修改input和output 返回值 其中,module是当前网络层,input是网络层的输入数据, output是网络层的输出数据 应用场景:如用于提取特征图 案例请看配套代码 3. torch.nn.Module.register_forward_pre_hook 功能:执行forward()之前调用hook函数。 形式:hook(module, input) -> None or modified input 应用场景举例:暂时没碰到过,希望读者朋友补充register_forward_pre_hook相关应用场景。 registerforwardprehook与forwardhook一样,是在module.__call中注册的,与forward_hook不同的是,其在module执行forward之前就运行了,具体可看module.__call中的代码,第一行就是执行forward_pre_hook的相关操作。 4. torch.nn.Module.register_full_backward_hook 功能:Module反向传播中的hook,每次计算module的梯度后,自动调用hook函数。 形式:hook(module, grad_input, grad_output) -> tuple(Tensor) or None 注意事项:当module有多个输入或输出时,grad_input和grad_output是一个tuple。 返回值:a handle that can be used to remove the added hook by calling handle.remove() 应用场景举例:例如提取特征图的梯度 Grad CAM 手动实现 下面就利用 register_forward_hook 和 register_full_backward_hook 来实现Grad CAM 详情请看配套代码 整体思路如下: 对模型最后一个卷积层进行hook函数注册,两个hook分别记录特征图于梯度 def backward_hook(module, grad_in, grad_out): grad_block.append(grad_out[0].detach()) def farward_hook(module, input, output): fmap_block.append(output) ------------------------------------------------------------ # 注册hook resnet_50.layer4[-1].register_forward_hook(farward_hook) resnet_50.layer4[-1].register_full_backward_hook(backward_hook) 获取类别loss,类别loss为分类类别最大的那个神经元的值,具体由comp_class_vec函数实现 if not index: index = np.argmax(ouput_vec.cpu().data.numpy()) else: index = np.array(index) index = index[np.newaxis, np.newaxis] index = torch.from_numpy(index) one_hot = torch.zeros(1, 1000).scatter_(1, index, 1) one_hot.requires_grad = True class_vec = torch.sum(one_hot * ouput_vec) # one_hot = 11.8605 执行backward,得到梯度 通过gen_cam()函数得到CAM图 将CAM与原图进行融合可视化,如下图所示 CAM 系列算法统一实现 CAM自2016年提出以来,已经有多种改进,并可运用于图像分割和目标检测,详细的CAM算法参见仓库。 pytorch-grad-cam提供了丰富的算法及简单的接口应用,下面就以resnet50为例,绘制6种CAM算法的热力图,效果如下图所示。 代码就不剖析了,grad-cam的接口已经非常清晰。请运行代码,查看结果如下图所示: 小结 CAM系列算法对理解深度卷积神经网络非常有帮助,建议仔细学习本节内容并进行拓展。 通过CAM分析: 可诊断模型是否学到真正特征 可通过热力图信息做对应的数据增强(如对非激活区域进行随机擦除和Cutout处理),类似YOLOv4中的CutMix数据增强方法。 还可以将热力图作为语义分割的弱监督标签进行训练分割模型,可参考《Tell Me Where to Look: Guided Attention Inference Network》 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-6/6.5-model-print.html":{"url":"chapter-6/6.5-model-print.html","title":"6.5 模型参数打印","keywords":"","body":"6.5 模型参数可视化 随着神经网络越来越深,越来越复杂,手动计算模型中间的数据的shape变得困难。 本节将介绍torchinfo,可用一键实现模型参数量计算、各层特征图形状计算和计算量计算等功能。 torchinfo的功能最早来自于TensorFlow和Kearas的summary()函数,torchinfo是学习借鉴而来。而在torchinfo之前还有torchsummary工具,不过torchsummary已经停止更新,并且推荐使用torchinfo。 torchsummay:https://github.com/sksq96/pytorch-summary torchinfo:https://github.com/TylerYep/torchinfo torchinfo 主要提供了一个函数,即 def summary( model: nn.Module, input_size: Optional[INPUT_SIZE_TYPE] = None, input_data: Optional[INPUT_DATA_TYPE] = None, batch_dim: Optional[int] = None, cache_forward_pass: Optional[bool] = None, col_names: Optional[Iterable[str]] = None, col_width: int = 25, depth: int = 3, device: Optional[torch.device] = None, dtypes: Optional[List[torch.dtype]] = None, mode: str | None = None, row_settings: Optional[Iterable[str]] = None, verbose: int = 1, **kwargs: Any, ) -> ModelStatistics: torchinfo 演示 运行代码 resnet_50 = models.resnet50(pretrained=False) batch_size = 1 summary(resnet_50, input_size=(batch_size, 3, 224, 224)) 可看到resnet50的以下信息 ========================================================================================== Layer (type:depth-idx) Output Shape Param # ========================================================================================== ResNet [1, 1000] -- ├─Conv2d: 1-1 [1, 64, 112, 112] 9,408 ├─BatchNorm2d: 1-2 [1, 64, 112, 112] 128 ├─ReLU: 1-3 [1, 64, 112, 112] -- ├─MaxPool2d: 1-4 [1, 64, 56, 56] -- ├─Sequential: 1-5 [1, 256, 56, 56] -- │ └─Bottleneck: 2-1 [1, 256, 56, 56] -- │ │ └─Conv2d: 3-1 [1, 64, 56, 56] 4,096 │ │ └─BatchNorm2d: 3-2 [1, 64, 56, 56] 128 │ │ └─ReLU: 3-3 [1, 64, 56, 56] -- │ │ └─Conv2d: 3-4 [1, 64, 56, 56] 36,864 │ │ └─BatchNorm2d: 3-5 [1, 64, 56, 56] 128 │ │ └─ReLU: 3-6 [1, 64, 56, 56] -- │ │ └─Conv2d: 3-7 [1, 256, 56, 56] 16,384 │ │ └─BatchNorm2d: 3-8 [1, 256, 56, 56] 512 │ │ └─Sequential: 3-9 [1, 256, 56, 56] 16,896 │ │ └─ReLU: 3-10 [1, 256, 56, 56] -- ...... │ └─Bottleneck: 2-16 [1, 2048, 7, 7] -- │ │ └─Conv2d: 3-140 [1, 512, 7, 7] 1,048,576 │ │ └─BatchNorm2d: 3-141 [1, 512, 7, 7] 1,024 │ │ └─ReLU: 3-142 [1, 512, 7, 7] -- │ │ └─Conv2d: 3-143 [1, 512, 7, 7] 2,359,296 │ │ └─BatchNorm2d: 3-144 [1, 512, 7, 7] 1,024 │ │ └─ReLU: 3-145 [1, 512, 7, 7] -- │ │ └─Conv2d: 3-146 [1, 2048, 7, 7] 1,048,576 │ │ └─BatchNorm2d: 3-147 [1, 2048, 7, 7] 4,096 │ │ └─ReLU: 3-148 [1, 2048, 7, 7] -- ├─AdaptiveAvgPool2d: 1-9 [1, 2048, 1, 1] -- ├─Linear: 1-10 [1, 1000] 2,049,000 ========================================================================================== Total params: 25,557,032 Trainable params: 25,557,032 Non-trainable params: 0 Total mult-adds (G): 4.09 ========================================================================================== Input size (MB): 0.60 Forward/backward pass size (MB): 177.83 Params size (MB): 102.23 Estimated Total Size (MB): 280.66 ========================================================================================== 其中包括各网络层名称,以及层级关系,各网络层输出形状以及参数量。在最后还有模型的总结,包括总的参数量有25,557,032个,总的乘加(Mult-Adds)操作有4.09G(4.09*10^9次方 浮点运算),输入大小为0.60MB,参数占102.23MB。 计算量:1G表示10^9 次浮点运算 (Giga Floating-point Operations Per Second),关于乘加运算,可参考知乎问题 存储量:这里的Input size (MB): 0.60,是通过数据精度计算得到,默认情况下采用float32位存储一个数,因此输入为:3*224*224*32b = 4816896b = 602112B = 602.112 KB = 0.6 MB 同理,Params size (MB): 25557032 * 32b = 817,825,024 b = 102,228,128 B = 102.23 MB 接口详解 summary提供了很多参数可以配置打印信息,这里介绍几个常用参数。 col_names:可选择打印的信息内容,如 (\"input_size\",\"output_size\",\"num_params\",\"kernel_size\",\"mult_adds\",\"trainable\",) dtypes:可以设置数据类型,默认的为float32,单精度。 mode:可设置模型在训练还是测试状态。 verbose: 可设置打印信息的详细程度。0是不打印,1是默认,2是将weight和bias也打出来。 小结 本节介绍torchinfo的使用,并分析其参数的计算过程,这里需要了解训练参数数量、特征图参数数量和计算量。其中计算量还有一个好用的工具库进行计算,这里作为额外资料供大家学习——PyTorch-OpCounter Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/":{"url":"chapter-7/","title":"第七章 PyTorch 小技巧汇总","keywords":"","body":"第七章 PyTorch 小技巧汇总 第七章 PyTorch 小技巧汇总 7.1 模型保存与加载 7.2 Finetune 模型微调 7.3 GPU使用 7.4 模型训练代码模板 7.5 TorchMetrics 模型评估指标库 7.6 Albumentations 数据增强库 7.7 TorchEnsemble 模型集成库 第七章简介 本章介绍开发过程中常用的代码段、工具模块和技巧等,初步设计有模型保存与加载、模型Finetune技巧、GPU使用技巧、训练代码框架等。 本章小结会续更新,将工作中遇到的小技巧分享出来。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.1-serialization.html":{"url":"chapter-7/7.1-serialization.html","title":"7.1 模型保存与加载","keywords":"","body":"7.1 模型保存与加载 保存与加载的概念(序列化与反序列化) 模型训练完毕之后,肯定想要把它保存下来,供以后使用,不需要再次去训练。 那么在pytorch中如何把训练好的模型,保存,保存之后又如何加载呢? 这就用需要序列化与反序列化,序列化与反序列化的概念如下图所示: 因为在内存中的数据,运行结束会进行释放,所以我们需要将数据保存到硬盘中,以二进制序列的形式进行长久存储,便于日后使用。 序列化即把对象转换为字节序列的过程,反序列化则把字节序列恢复为对象。 在pytorch中,对象就是模型,所以我们常常听到序列化和反序列化,就是将训练好的模型从内存中保存到硬盘里,当要使用的时候,再从硬盘中加载。 torch.save / torch.load pytorch提供的序列化与反序列化函数分别是 1. torch.save(obj, f, pickle_module=pickle, pickle_protocol=DEFAULT_PROTOCOL, _use_new_zipfile_serialization=True) 功能:保存对象到硬盘中 主要参数: obj- 对象 f - 文件路径 2. torch.load(f, map_location=None, pickle_module=pickle, **pickle_load_args) 功能:加载硬盘中对象 主要参数: f - 文件路径 map_location - 指定存储位置,如map_location='cpu', map_location={'cuda:1':'cuda:0'} 这里的map_location大有文章,经常需要手动设置,否者会报错。具体可参考以下形式: GPU->CPU:torch.load(model_path, map_location='cpu') CPU->GPU:torch.load(model_path, map_location=lambda storage, loc: storage) 两种保存方式 pytorch保存模型有两种方式 保存整个模型 保存模型参数 我们通过示意图来区分两者之间的差异 从上图左边知道法1保存整个nn.Module, 而法2只保存模型的参数信息。 我们知道一个module当中包含了很多信息,不仅仅是模型的参数 parameters,还包含了buffers, hooks和modules等一系列信息。 对于模型应用,最重要的是模型的parameters,其余的信息是可以通过model 类再去构建的,所以模型保存就有两种方式 所有内容都保存; 仅保存模型的parameters。 通常,我们只需要保存模型的参数,在使用的时候再通过load_state_dict方法加载参数。 由于第一种方法不常用,并且在加载过程中还需要指定的类方法,因此不做演示也不推荐。 对于第二种方法的代码十分简单,请看示例: net_state_dict = net.state_dict() torch.save(net_state_dict, \"my_model.pth\") 常用的代码段 在模型开发过程中,往往不是一次就能训练好模型,经常需要反复训练,因此需要保存训练的“状态信息”,以便于基于某个状态继续训练,这就是常说的resume,可以理解为断点续训练。 在整个训练阶段,除了模型参数需要保存,还有优化器的参数、学习率调整器的参数和迭代次数等信息也需要保存,因此推荐在训练时,采用以下代码段进行模型保存。以下代码来自torchvision的训练脚本。 checkpoint = { \"model\": model_without_ddp.state_dict(), \"optimizer\": optimizer.state_dict(), \"lr_scheduler\": lr_scheduler.state_dict(), \"epoch\": epoch, } path_save = \"model_{}.pth\".format(epoch) torch.save(checkpoint, path_save # =================== resume =============== # resume checkpoint = torch.load(path_save, map_location=\"cpu\") model.load_state_dict(checkpoint[\"model\"]) optimizer.load_state_dict(checkpoint[\"optimizer\"]) lr_scheduler.load_state_dict(checkpoint[\"lr_scheduler\"]) start_epoch = checkpoint[\"epoch\"] + 1 小结 模型保存与加载比较简单,需要注意的有两点: torch.load的时候注意map_location的设置; 理解checkpoint resume的概念,以及训练过程是需要模型、优化器、学习率调整器和已迭代次数的共同配合。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.2-finetune.html":{"url":"chapter-7/7.2-finetune.html","title":"7.2 Finetune 模型微调","keywords":"","body":"7.2 Finetune 模型微调 Finetune(微调)是深度学习模型训练中常用的方法。Finetune的理论可从迁移学习(Transfer Learning)中学习。 迁移学习 Transfer Learning是机器学习的分支,主要研究源域(source domain)所学到的知识,如何迁移到目标域(target domain)中 如《A Survey on Transfer Learning》中的图所示: 为什么要这样做呢?这是因为在target domain中,数据量较少,可学习提取的知识不足以完成任务,所以需要进行迁移学习,这样在target domain中可以学得快,学得好。 例如一个人学会了骑自行车,再去学骑电动车就很快,又比如一个人学会了C语言,再去学python就会比较快。 transfer learning是一个较大的领域,这里只讨论神经网络的finetune,如何理解模型的finetune它也是迁移学习呢? 我们知道,神经网络中最重要的东西就是权值参数,训练模型就是在更新参数,这些参数就是模型学习到知识。 之前对alexnet的卷积核进行了可视化,可以理解卷积核学习到的图像边缘,色彩信息就是alexnet所学习到的知识。 在图像任务中,这些知识是可以共享,可以迁移到其它任务中去,因此,大家常常采用在imagenet上训练好的模型进行finetune,进行transfer learning。 Finetune常用的两种方法 通常,会将模型划分为两个部分 feature extractor: 将fc层之前的部分认为是一个feature extractor classifier: fc层认为是classifier 基于此,finetune大体有两种方法: 将 feature extractor部分的参数固定,冻结,不进行训练,仅训练classifier 将 feature extractor设置较小的学习率,classifier设置较大的学习率 下面通过一个实例讲解两种方法 方法一:冻结 feature extractor。 原理是通过设置paramters的requires_grad为False,让它们不进行权重更新即可。 for param in resnet18_ft.parameters(): param.requires_grad = False 在完整代码中,每个epoch都打印了第一个卷积层权重,观察它们是否有变化。 经过25个epoch训练,性能指标如下图所示。 方法二:不同层不同学习率 原理是通过优化器的参数组管理,不同参数组可以设置不同的学习率。 因此第一步需要将不同的参数从模型中识别、提取出来,分别定义为不同参数组,这里通过内存地址进行区分。 # 返回的是该层所有参数的内存地址 fc_params_id = list(map(id, resnet18_ft.fc.parameters())) #遍历model的参数,只要不是需要ignore的,就保留,返回filter对象,在optimizer.py中的add_param_group中有 base_params = filter(lambda p: id(p) not in fc_params_id, resnet18_ft.parameters()) optimizer = optim.SGD([ {'params': base_params, 'lr': LR}, # 0 {'params': resnet18_ft.fc.parameters(), 'lr': LR*2}], momentum=0.9) 通过代码看到最后的全连接层学习率比前面的特征提取部分大10倍,如果对optimizer的参数组概念不了解,请回看第五章 小结 Finetune的代码实现非常简单,不过需要大家对nn.Module和optimizer的基础概念熟悉,建议回顾第四章与第五章的基础知识。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.3-gpu.html":{"url":"chapter-7/7.3-gpu.html","title":"7.3 GPU使用","keywords":"","body":"7.3 GPU使用 深度学习之所以可以发展迅猛,得益于强大的计算力。在PyTorch中,自然加持GPU加速运算,本节将介绍PyTorch中GPU的使用原理与多GPU使用的DataParallel原理,还有一些针对GPU的实用代码段。 gpu与cpu 在处理器家族中,有两大阵营,分别是CPU和GPU,它们分工协作,共同完成计算机复杂功能。 但它们两者主要差别在哪里?下面一探究竟。 CPU(central processing unit, 中央处理器)cpu主要包括两个部分,即控制器、运算器,除此之外还包括高速缓存等 GPU(Graphics Processing Unit, 图形处理器)是为处理类型统一并且相互无依赖的大规模数据运算,以及不需要被打断的纯净的计算环境为设计的处理器,因早期仅有图形图像任务中设计大规模统一无依赖的运算,因此该处理器称为图像处理器,俗称显卡。 那么它们之间主要区别在哪里呢,来看一张示意图 绿色的是计算单元,橙红色的是存储单元,橙黄色的是控制单元,从示意图中看出,gpu的重点在计算,cpu的重点在控制,这就是两者之间的主要差异。 在pytorch中,可以将训练数据以及模型参数迁移到gpu上,这样就可以加速模型的运算 在这里,需要了解的是,在pytorch中,两个数据运算必须在同一个设备上。 PyTorch的设备——torch.device 前面提到,PyTorch的运算需要将运算数据放到同一个设备上,因此,需要了解PyTorch中设备有哪些? 目前,PyTorch支持两种设备,cpu与cuda,为什么是cuda而不是gpu?因为早期,只有Nvidia的GPU能用于模型训练加速,因此称之为cuda。 即使现在支持了AMD显卡进行加速,仍旧使用cuda来代替gpu。 PyTorch中表示设备通常用torch.device)这个函数进行设置,例如: >>> torch.device('cuda:0') device(type='cuda', index=0) >>> torch.device('cpu') device(type='cpu') >>> torch.device('cuda') # current cuda device device(type='cuda') 补充资料: ROCm平台及HIP介绍 https://pytorch.org/docs/stable/notes/hip.html 把数据放到GPU——to函数 在pytorch中,只需要将要进行运算的数据放到gpu上,即可使用gpu加速运算 在模型运算过程中,需要放到GPU的主要是两个: 输入数据——形式为tensor 网络模型——形式为module pytorch中针对这两种数据都有相应的函数把它们放到gpu上,我们来认识一下这个函数,就是to函数 tensor的to函数: to(*args, \\kwargs) → Tensor** 功能:转换张量的数据类型或者设备 注意事项:to函数不是inplace操作,所以需要重新赋值,这与module的to函数不同 使用: 转换数据类型 x = torch.ones((3, 3)) x = x.to(torch.float64) 转换设备 x = torch.ones((3, 3)) x = x.to(\"cuda\") module的to函数:to(*args, \\kwargs)** 功能:move and/or cast the parameters and buffers,转换模型中的参数和缓存 注意事项:实行的是inplace操作 使用: 转换数据类型 linear = nn.Linear(2, 2) print(linear.weight) linear.to(torch.double) print(linear.weight) 迁移至gpu gpu1 = torch.device(\"cuda:1\") linear.to(gpu1) print(linear.weight) 将torch.device 与 to函数联合使用,就是第六章混淆矩阵代码中使用过的方式 device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") model.to(device) inputs, labels = inputs.to(device), labels.to(device) 通常,会采用torch.cuda.is_available()函数来自适应当前设备,若没有gpu可用,自动设置device为cpu,不会影响代码的运行。 除了torch.cuda.is_available(),torch库中还有一些关于cuda的实用函数,下面一起看看。 torch.cuda 在torch.cuda中有几十个关于guda的函数,详细请查阅官方文档) 下面介绍几个常用的函数。 torch.cuda.device_count(): 查看可用GPU数量 torch.cuda.current_device():查看当前使用的设备的序号 torch.cuda.get_device_name():获取设备的名称 torch.cuda.get_device_capability(device=None):查看设备的计算力 torch.cuda.is_available():查看cuda是否可用 torch.cuda.get_device_properties():查看GPU属性 torch.cuda.set_device(device):设置可用设备,已不推荐使用,建议通过CUDA_VISIBLE_DEVICES来设置,下文会讲解CUDA_VISIBLE_DEVICES的使用。 torch.cuda.mem_get_info(device=None):查询gpu空余显存以及总显存。 torch.cuda.memory_summary(device=None, abbreviated=False):类似模型的summary,它将GPU的详细信息进行输出。 torch.cuda.empty_cache():清空缓存,释放显存碎片。 torch.backends.cudnn.benchmark = True : 提升运行效率,仅适用于输入数据较固定的,如卷积 会让程序在开始时花费一点额外时间,为整个网络的每个卷积层搜索最适合它的卷积实现算法,进而实现网络的加速让内置的 cuDNN 的 auto-tuner 自动寻找最适合当前配置的高效算法,来达到优化运行效率的问题 torch.backends.cudnn.deterministic: 用以保证实验的可重复性. 由于cnDNN 每次都会去寻找一遍最优配置,会产生随机性,为了模型可复现,可设置torch.backends.cudnn.deterministic = True 运行代码,可看到如下信息: device_count: 1 current_device: 0 (8, 6) NVIDIA GeForce RTX 3060 Laptop GPU True ['sm_37', 'sm_50', 'sm_60', 'sm_61', 'sm_70', 'sm_75', 'sm_80', 'sm_86', 'compute_37'] _CudaDeviceProperties(name='NVIDIA GeForce RTX 3060 Laptop GPU', major=8, minor=6, total_memory=6144MB, multi_processor_count=30) (5407899648, 6442450944) |===========================================================================| | PyTorch CUDA memory summary, device ID 0 | |---------------------------------------------------------------------------| | CUDA OOMs: 0 | cudaMalloc retries: 0 | |===========================================================================| | Metric | Cur Usage | Peak Usage | Tot Alloc | Tot Freed | |---------------------------------------------------------------------------| | Allocated memory | 0 B | 0 B | 0 B | 0 B | | from large pool | 0 B | 0 B | 0 B | 0 B | | from small pool | 0 B | 0 B | 0 B | 0 B | |---------------------------------------------------------------------------| | Active memory | 0 B | 0 B | 0 B | 0 B | | from large pool | 0 B | 0 B | 0 B | 0 B | | from small pool | 0 B | 0 B | 0 B | 0 B | |---------------------------------------------------------------------------| | GPU reserved memory | 0 B | 0 B | 0 B | 0 B | | from large pool | 0 B | 0 B | 0 B | 0 B | | from small pool | 0 B | 0 B | 0 B | 0 B | |---------------------------------------------------------------------------| | Non-releasable memory | 0 B | 0 B | 0 B | 0 B | | from large pool | 0 B | 0 B | 0 B | 0 B | | from small pool | 0 B | 0 B | 0 B | 0 B | |---------------------------------------------------------------------------| | Allocations | 0 | 0 | 0 | 0 | | from large pool | 0 | 0 | 0 | 0 | | from small pool | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | Active allocs | 0 | 0 | 0 | 0 | | from large pool | 0 | 0 | 0 | 0 | | from small pool | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | GPU reserved segments | 0 | 0 | 0 | 0 | | from large pool | 0 | 0 | 0 | 0 | | from small pool | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | Non-releasable allocs | 0 | 0 | 0 | 0 | | from large pool | 0 | 0 | 0 | 0 | | from small pool | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | Oversize allocations | 0 | 0 | 0 | 0 | |---------------------------------------------------------------------------| | Oversize GPU segments | 0 | 0 | 0 | 0 | |===========================================================================| None 多gpu训练——nn.DataParallel 人多力量大的道理在PyTorch的训练中也是适用的,PyTorch支持多个GPU共同训练,加快训练速度。 多GPU可分为单机多卡和多机多卡,这里仅介绍单机多卡的方式。 单机多卡的实现非常简单,只需要增加一行代码: net = nn.DataParallel(net) 代码的意思是将一个nn.Module变为一个特殊的nn.Module,这个Module的forward函数实现多GPU调用。 如果对nn.Module的概念以及forward函数不理解的话,请回到第四章进行学习。 首先看一幅示意图,理解多GPU是如何工作的 整体有四个步骤: 数据平均划为N份 模型参数复制N份 在N个GPU上同时运算 回收N个GPU上的运算结果 了解了多gpu运行机制,下面看看DataParallel是如何实现的。 torch.nn.DataParallel(module, device_ids=None, output_device=None, dim=0) 功能:实现模型的数据并行运算 主要参数: module - 需要并行的module device_ids: (list of python:int or torch.device) – CUDA devices (default: all devices), eg: [2, 3] 默认采用所有可见gpu,这里强调了可见gpu,就是说可以设置部分gpu对当前python脚本不可见,这个可以通过系统环境变量设置 output_device: int or torch.device , 设置输出结果所在设备,默认为 device_ids[0],通常以第1个逻辑gpu为主gpu 源代码分析: DataParallel仍旧是一个nn.Module类,所以首要关注它的forward函数。 来到/torch/nn/parallel/data_parallel.py的147行: def forward(self, *inputs, **kwargs): with torch.autograd.profiler.record_function(\"DataParallel.forward\"): if not self.device_ids: return self.module(*inputs, **kwargs) for t in chain(self.module.parameters(), self.module.buffers()): if t.device != self.src_device_obj: raise RuntimeError(\"module must have its parameters and buffers \" \"on device {} (device_ids[0]) but found one of \" \"them on device: {}\".format(self.src_device_obj, t.device)) inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) # for forward function without any inputs, empty list and dict will be created # so the module can be executed on one device which is the first one in device_ids if not inputs and not kwargs: inputs = ((),) kwargs = ({},) if len(self.device_ids) == 1: return self.module(*inputs[0], **kwargs[0]) replicas = self.replicate(self.module, self.device_ids[:len(inputs)]) outputs = self.parallel_apply(replicas, inputs, kwargs) return self.gather(outputs, self.output_device) 核心点有4行代码,分别是: inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids) replicas = self.replicate(self.module, self.device_ids[:len(inputs)]) outputs = self.parallel_apply(replicas, inputs, kwargs) return self.gather(outputs, self.output_device) 一、数据切分 inputs, kwargs = self.scatter(inputs, kwargs, self.device_ids):利用scatter函数,将数据切分为多块,为各GPU需要的数据做准备。 scatter函数在torch\\nn\\parallel\\scatter_gather.py第11行。 def scatter(inputs, target_gpus, dim=0): r\"\"\" Slices tensors into approximately equal chunks and distributes them across given GPUs. Duplicates references to objects that are not tensors. \"\"\" 二、模型分发至GPU replicas = self.replicate(self.module, self.device_ids[:len(inputs)]):利用replicate函数将模型复制N份,用于多GPU上。 replicate函数在 torch\\nn\\parallel\\replicate.py第78行。 三、执行并行推理 outputs = self.parallel_apply(replicas, inputs, kwargs):多GPU同时进行运算。 四、结果回收 return self.gather(outputs, self.output_device) 为了理解分发过程,请使用具备多GPU的环境,运行配套代码,可看到如下信息: batch size in forward: 4 batch size in forward: 4 batch size in forward: 4 batch size in forward: 4 model outputs.size: torch.Size([16, 3]) CUDA_VISIBLE_DEVICES :0,1,3,2 device_count :4 batchsize设置为16,将16个样本平均分发给4个GPU,因此在forward函数当中,看到的数据是4个样本。 多GPU训练模型的保存与加载 当模型变为了Dataparallel时,其参数名称会多一个module.字段,这导致在保存的时候state_dict也会多了module.字段。 从而,在加载的时候经常出现以下报错。 RuntimeError: Error(s) in loading state_dict for FooNet: ​ Missing key(s) in state_dict: \"linears.0.weight\", \"linears.1.weight\", \"linears.2.weight\". ​ Unexpected key(s) in state_dict: \"module.linears.0.weight\", \"module.linears.1.weight\", \"module.linears.2.weight\". 解决方法是,移除key中的module.: from collections import OrderedDict new_state_dict = OrderedDict() for k, v in state_dict_load.items(): namekey = k[7:] if k.startswith('module.') else k new_state_dict[namekey] = v 请结合代码运行,观察其使用,并看到如下结果: state_dict_load: OrderedDict([('module.linears.0.weight', tensor([[ 0.3337, 0.0317, -0.1331], [ 0.0431, 0.0454, 0.1235], [ 0.0575, -0.2903, -0.2634]])), ('module.linears.1.weight', tensor([[ 0.1235, 0.1520, -0.1611], [ 0.4511, -0.1460, -0.1098], [ 0.0653, -0.5025, -0.1693]])), ('module.linears.2.weight', tensor([[ 0.3657, -0.1107, -0.2341], [ 0.0657, -0.0194, -0.3119], [-0.0477, -0.1008, 0.2462]]))]) new_state_dict: OrderedDict([('linears.0.weight', tensor([[ 0.3337, 0.0317, -0.1331], [ 0.0431, 0.0454, 0.1235], [ 0.0575, -0.2903, -0.2634]])), ('linears.1.weight', tensor([[ 0.1235, 0.1520, -0.1611], [ 0.4511, -0.1460, -0.1098], [ 0.0653, -0.5025, -0.1693]])), ('linears.2.weight', tensor([[ 0.3657, -0.1107, -0.2341], [ 0.0657, -0.0194, -0.3119], [-0.0477, -0.1008, 0.2462]]))]) Process finished with exit code 0 使用指定编号的gpu 通常,一台服务器上有多个用户,或者是会进行多个任务,此时,对gpu合理的安排使用就尤为重要 在实践中,通常会设置当前python脚本可见的gpu,然后直接使用nn.dataparallel使用所有gpu即可,不需要手动去设置使用哪些gpu 设置python脚本可见gpu的方法为设置系统环境变量中的CUDA_VISIBLE_DEVICES 设置方法为: os.environ.setdefault(\"CUDA_VISIBLE_DEVICES\", \"2, 3\") 当时之后,即物理设备的2,3号GPU,在程序中分别是0号、1号GPU,这里需要理解逻辑编号与物理编号的对应关系。 注意事项: CUDA_VISIBLE_DEVICES的设置一定要在 device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\") 之前! 否则已经调用cuda,python脚本已经获取当前可见gpu了,再设置就无效了 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.4-training-script.html":{"url":"chapter-7/7.4-training-script.html","title":"7.4 模型训练代码模板","keywords":"","body":"7.4 模型训练代码模板 一个良好的训练代码,可以有助于分析和超参调优,本节将以torchvision提供的分类模型训练代码为基础,编写适合自己的训练代码框架。 torchvision还提供了分割、检测、相似性学习和视频分类的 训练脚本,可以参考https://pytorch.org/vision/stable/training_references.html。 在分类的train.py中,共计501行代码,下面我们提炼出核心内容,在cifar10数据集上完成resnet-8的训练。 提炼后,代码核心内容包括: 参数设置部分采用argparse模块进行配置,便于服务器上训练,以及超参数记录; 日志模块,包括logging模块记录文本信息.log文件,以及tensorboard部分的可视化内容; 训练模块封装为通用类——ModelTrainer 模型保存部分 一、参数设置 在服务器上进行训练时,通常采用命令行启动,或时采用sh脚本批量训练,这时候就需要从命令行传入一些参数,用来调整模型超参。 例如学习率想从0.1改为0.01,按以往代码,需要进入.py文件,修改代码,保存代码,运行代码。 这样操作明显欠妥,因此通常会采用argparse模块,将经常需要调整的参数,可以从命令行中接收。 在代码中,采用了函数get_args_parser()实现,有了args,还可以将它记录到日志中,便于复现以及查看模型的超参数设置,便于跟踪。 二、日志模块 模型训练的日志很重要,它用于指导下一次实验的超参数如何调整。 代码中采用借助logging模块构建一个logger,并且以时间戳(年月日-时分秒)的形式创建文件夹,便于日志管理。 在logger中使用logger.info函数代替print函数,可以实现在终端展示信息,还可以将其保存到日志文件夹下的log.log文件,便于溯源。 三、训练模块 训练过程比较固定,因此会将其封装成 train_one_epoch和evaluate的两个函数,从这两个函数中需要返回我们关心的指标,如loss,accuracy,混淆矩阵等。 四、指标统计模块 之前的代码中,loss和accuracy需要手动记录每个值,然后取平均,除了它们两个,深度学习训练中还有许多指标都需要类似的操作。 因此,可以抽象出一个AverageMeter类,用于记录需要求取平均值的那些指标。 AverageMeter类的使用,使得代码更简洁,下面一同分析一下。 运行代码当训练完成后,可在输出目录下得到以时间戳为文件夹的日志目录,里面包括loss、accuracy、混淆矩阵可视化图,最优模型checkpoint。 小结 训练模型的代码结构可以千变万化,每个人结合自己的风格进行编写,本节代码也是吸取了多个代码的精华,当然还有不足之处,后续会慢慢补上,这里提供一个整体思路,知道代码中需要什么。 建议参考以下训练代码结构: PyTorch ImageNet example (https://github.com/pytorch/examples/tree/master/imagenet) NVIDIA CUDA specific speedups adopted from NVIDIA Apex examples (https://github.com/NVIDIA/apex/tree/master/examples/imagenet) TIMM: https://github.com/rwightman/pytorch-image-models/blob/master/train.py Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.5-torchmetrics.html":{"url":"chapter-7/7.5-torchmetrics.html","title":"7.5 TorchMetrics 模型评估指标库","keywords":"","body":"7.5 torchmetrics 模型评估指标库 模型训练时是通过loss进行好坏的评估,因为我们采用的是loss进行方向传播。对于人类评判好坏,往往不是通过loss值,而是采用某种评判指标。 在图像分类任务中常用的有Accuracy(准确率)、Recall(召回率)和Precision(精确度),图像分割中常用mIoU和Dice系数,目标检测中常用mAP,由此可见不同任务的评价指标大多不一样。 常用的指标多达几十种,本节将介绍torchmetrics工具,它目前提供超过80种评价指标的函数,并且使用起来非常方便,值得学习。 TorchMetrics简介与安装 TorchMetrics: Github TorchMetrics is a collection of 80+ PyTorch metrics implementations and an easy-to-use API to create custom metrics. It offers: A standardized interface to increase reproducibility Reduces Boilerplate Distributed-training compatible Rigorously tested Automatic accumulation over batches Automatic synchronization between multiple devices 安装: pip install torchmetrics conda install -c conda-forge torchmetrics TorchMetrics 快速上手 torchmetrics 的使用与本章第四节课中介绍的AverageMeter类似,它能够记录每一次的信息,并通过.compute()函数进行汇总计算。 下面通过一个accuracy的例子,剖析torchmetrics的体系结构。 from my_utils import setup_seed setup_seed(40) import torch import torchmetrics metric = torchmetrics.Accuracy() n_batches = 3 for i in range(n_batches): preds = torch.randn(10, 5).softmax(dim=-1) target = torch.randint(5, (10,)) acc = metric(preds, target) # 单次计算,并记录本次信息。通过维护tp, tn, fp, fn来记录所有数据 print(f\"Accuracy on batch {i}: {acc}\") acc_avg = metric.compute() print(f\"Accuracy on all data: {acc_avg}\") tp, tn, fp, fn = metric.tp, metric.tn, metric.fp, metric.fn print(tp, tn, fp, fn, sum([tp, tn, fp, fn])) metric.reset() Accuracy on batch 0: 0.30000001192092896 Accuracy on batch 1: 0.10000000149011612 Accuracy on batch 2: 0.20000000298023224 Accuracy on all data: 0.20000000298023224 tensor(6) tensor(96) tensor(24) tensor(24) tensor(150) torchmetrics的使用可以分以下三步: ​ 1.创建指标评价器 ​ 2.迭代中进行\"update\"或forward,update和forward均可记录每次数据信息 ​ 3.计算所有数据指标 TorchMetrics代码结构 这里提到forward,正是第四章中nn.Module的forward。 TorchMetrics所有指标均继承了nn.Module,因此可以看到这样的用法。 acc = metric(preds, target) 下面进入 torchmetrics\\classification\\accuracy.py 中观察 Accuracy到底是什么。 可以看到Accuracy类只有3个函数,分别是__init__, update, compute,其作用就如上文所述。 再看继承关系,Accuracy --> StatScores --> Metric --> nn.Module + ABC。 Metric类正如文档所说“The base Metric class is an abstract base class that are used as the building block for all other Module metrics.”,是torchmetrics所有类的基类,它实现forward函数,因此才有像这样的调用: acc = metric(preds, target) Accuracy 更新逻辑 torchmetrics的使用与上一节课中的AverageMeter+Accuracy函数类似,不过在数据更新维护方面略有不同,并且torchmetrics还有点难理解。 AverageMeter+Accuracy时,是通过self.val, self.sum, self.count, self.avg进行维护。 在torchmetrics.Accuracy中,并没有这些属性,而是通过tp, tn, fp, fn进行维护。 但是有个问题来了,请仔细观察代码,iteration循环是3次,每一次batch的数量是10,按道理tp+tn+fp+fn= 30,总共30个样本,为什么会是150? 因为,这是多类别分类的统计,不是二分类。因此需要为每一个类,单独计算tp, tn, fp, fn。又因为有5个类别,因此是30*5=150。 关于多类别的tp, tn, fp, fn,可参考stackoverflow 还有个好例子,请看混淆矩阵: 真实\\预测 0 1 2 0 2 0 0 1 1 0 1 2 0 2 0 对于类别0的 FP=1 TP=2 FN=0 TN=3 对于类别1的 FP=2 TP=0 FN=2 TN=2 对于类别2的 FP=1 TP=0 FN=2 TN=3 自定义metrics 了解了Accuracy使用逻辑,就可以触类旁通,使用其它80多个Metrics。 但总有不满足业务需求的时候,这时候就需要自定义metrics。 自定义metrics非常简单,它就像自定义Module一样,提供必备的函数即可。 自定义metrics只需要继承Metric,然后实现以下三个函数即可: init(): Each state variable should be called using self.add_state(...). update(): Any code needed to update the state given any inputs to the metric. compute(): Computes a final value from the state of the metric. 举例: class MyAccuracy(Metric): full_state_update: bool = False def __init__(self): super().__init__() self.add_state(\"correct\", default=torch.tensor(0), dist_reduce_fx=\"sum\") self.add_state(\"total\", default=torch.tensor(0), dist_reduce_fx=\"sum\") def update(self, preds: torch.Tensor, target: torch.Tensor): batch_size = target.size(0) _, pred = preds.topk(1, 1, True, True) pred = pred.t() correct = pred.eq(target.reshape(1, -1).expand_as(pred)) self.correct += torch.sum(correct) self.total += batch_size def compute(self): return self.correct.float() / self.total 这里需要注意的是: 在init函数中需要通过add_state进行属性初始化; 在update中需要处理接收的数据,并可自定义管理机制,如这里采用correct与total来管理总的数据 在compute中需清晰知道返回的是总数据的Accuracy 小结 torchmetrics是一个简单易用的指标评估库,里面提供了80多种指标,建议采用torchmetrics进行指标评估,避免重复造轮子。 下面请看支持的指标: Auido 任务指标 Perceptual Evaluation of Speech Quality (PESQ) Permutation Invariant Training (PIT) Scale-Invariant Signal-to-Distortion Ratio (SI-SDR) Scale-Invariant Signal-to-Noise Ratio (SI-SNR) Short-Time Objective Intelligibility (STOI) Signal to Distortion Ratio (SDR) Signal-to-Noise Ratio (SNR) 分类 任务指标 Accuracy AUC AUROC Average Precision Binned Average Precision Binned Precision Recall Curve Binned Recall At Fixed Precision Calibration Error Cohen Kappa Confusion Matrix Coverage Error Dice Score F1 Score FBeta Score Hamming Distance Hinge Loss Jaccard Index KL Divergence Label Ranking Average Precision Label Ranking Loss Matthews Corr. Coef. Precision Precision Recall Precision Recall Curve Recall ROC Specificity Stat Scores 图像 任务指标 Error Relative Global Dim. Synthesis (ERGAS) Frechet Inception Distance (FID) Image Gradients Inception Score Kernel Inception Distance Learned Perceptual Image Patch Similarity (LPIPS) Multi-Scale SSIM Peak Signal-to-Noise Ratio (PSNR) Spectral Angle Mapper Spectral Distortion Index Structural Similarity Index Measure (SSIM) Universal Image Quality Index 检测 任务指标 Mean-Average-Precision (mAP) Pairwise 任务指标 Cosine Similarity Euclidean Distance Linear Similarity Manhattan Distance Regression 任务指标 Cosine Similarity Explained Variance Mean Absolute Error (MAE) Mean Absolute Percentage Error (MAPE) Mean Squared Error (MSE) Mean Squared Log Error (MSLE) Pearson Corr. Coef. R2 Score Spearman Corr. Coef. Symmetric Mean Absolute Percentage Error (SMAPE) Tweedie Deviance Score Weighted MAPE Retrieval 任务指标 Retrieval Fall-Out Retrieval Hit Rate Retrieval Mean Average Precision (MAP) Retrieval Mean Reciprocal Rank (MRR) Retrieval Normalized DCG Retrieval Precision Retrieval R-Precision Retrieval Recall Text 任务指标 BERT Score BLEU Score Char Error Rate ChrF Score Extended Edit Distance Match Error Rate ROUGE Score Sacre BLEU Score SQuAD Translation Edit Rate (TER) Word Error Rate Word Info. LostWord Info. Preserved Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.6-albumentations.html":{"url":"chapter-7/7.6-albumentations.html","title":"7.6 Albumentations 数据增强库","keywords":"","body":"7.6 albumentations 数据增强库 本节介绍albumentations库,albumentations是强大的数据增强库,原计划在第三章中进行介绍,后因篇幅过大,放到了本章作为进阶技巧。 为什么要用albumentations? pytorch的transforms有什么不足么? 当然有不足了, pytorch的transforms在处理图像分割与目标检测这一类需要图像与标签同时变换的时候不方便,而albumentations提供了图像分割、目标检测等复杂任务的数据增强方法。 为什么要用albumentations? 从github中译过来: 支持多种任务:支持分类、语义分割、实例分割、目标检测和姿态估计等; 提供简洁的API:针对分割、边界框回归、关键点任务及多种数据形态(RBG-images, grayscale images, multispectral images)均可采用统一的函数完成数据增强。 数据增强方法多:提供超70种变换方法。 速度快效率高:相比其它常见数据增强库,多数方法速度都为最优 支持主流框架:albumentations已是pytorch生态系统的一员,可很好适配pytorch,同时支持TensorFlow中使用。 专家背书:albumentations的作者大多数来自工业界大牛 市场验证:albumentations已经被广泛应用于工业界和学术界,以及各种竞赛,并且获得了优异成绩。 albumentations有那么多优点,还不赶快来学习它。 安装 pip install -U albumentations 上手demo demo代码请查看配套代码,通过代码可以看到,使用步骤有: 定义一个Compose,内部包括多个变换方法(同transforms一样) 将compose放到dataset中,在getitem函数中实现调用 getitem中注意各变换方法的输入,由于albumentations支持多种数据同时处理,因此输入时需要指定变量。如image、mask、bboxes和keypoints之类。 经过Compose返回的数据是一个字典形式,需要根据key获取对应信息,如image、mask、bboxes和keypoints。 可以任意调整以下变换方法,以及参数设置,观察图像变化。 train_transform = A.Compose( [ A.Resize(512, 512), A.ShiftScaleRotate(shift_limit=0.05, scale_limit=0.05, rotate_limit=55, p=0.5), ] ) 图像-标签对的数据增强 图像分割、目标检测和关键点任务中的数据增强,需要同时对图像和标签一起变换,这也是albumentations与pytorch的transforms最大的区别。 请运行代码观察结果,这里介绍使用步骤 dataset的getitem中将image与mask同时传入self.transfoms()中; 在DataLoader返回的数据中,采用key-value的形式获取image与mask/bboes/keypoints; data_augmented = data_transform(image=image_rgb, mask=mask) albumentations 代码结构 albumentations采用BasicTransform作为基类,再根据是否需要对标签进行变换,划分为nlyTransform 和 DualTransform 顾名思义,ImageOnlyTransform就是仅针对原图进行操作,DualTransform就是同时对原图和标签进行操作。 两种类型的变换常见的方如下: ImageOnlyTransform: Blur,RGBShift,RandomBrightnessContrast等 DualTransform: Resize, Flip, Crop, Rotate,ElasticTransform等 为了分析albumentations代码结构,特地绘制了一副简略版的UML类图,再结合Resize与Blur的函数,分析albumentations代码运行结构。 在代码中通过Resize类的调试,可看到实现图像模糊的功能在apply()函数中,接下来就需要理清代码结构,寻找BasicTransform是如何调用apply()的。 通过代码单步调试,发现依次进行以下步骤: BasicTransform的__call__():在89行,return时调用self.apply_with_params(params, **kwargs) BasicTransform的self.apply_with_params():在100行,通过target_function = self._get_target_function(key)获得具体需要变换的函数 BasicTransform的self._get_target_function():在123行,通过self.targets.get()获得具体的target_function DualTransform的targets:是一个字典,其中定义了key与具体变换函数的映射,其中self.apply即上文提到的Resize类下的self.apply。 到这里一个具体的变换实现过程才算走完,其中BasicTransform定义了基础逻辑,例如概率选择、参数获取等,DualTransform则是定义mask,masks,bboxes,keypoints的变换函数接口,最终的实现由Resize类来完成。同理,ImageOnlyTransform 一样。 @property def targets(self): return { \"image\": self.apply, \"mask\": self.apply_to_mask, \"masks\": self.apply_to_masks, \"bboxes\": self.apply_to_bboxes, \"keypoints\": self.apply_to_keypoints, } 小结 本节介绍了albumentations的优点,基本使用以及代码结构,关于albumentations的70多种方法请查看附录,或者阅读文档查看API,使用方法非常简单,也可到代码中查看编写的批量数据增强代码段,实现了68个数据增强可视化 附录 albumentations提供70多种API,这里将不再一一介绍各API的参数,请查看文档即可。 整体分为两种,Pixel-Level和Spatial-Level: AdvancedBlur Blur CLAHE ChannelDropout ChannelShuffle ColorJitter Downscale Emboss Equalize FDA FancyPCA FromFloat GaussNoise GaussianBlur GlassBlur HistogramMatching HueSaturationValue ISONoise ImageCompression InvertImg MedianBlur MotionBlur MultiplicativeNoise Normalize PixelDistributionAdaptation Posterize RGBShift RandomBrightnessContrast RandomFog RandomGamma RandomRain RandomShadow RandomSnow RandomSunFlare RandomToneCurve RingingOvershoot Sharpen Solarize Superpixels TemplateTransform ToFloat ToGray ToSepia UnsharpMask Transform Image Masks BBoxes Keypoints Affine ✓ ✓ ✓ ✓ CenterCrop ✓ ✓ ✓ ✓ CoarseDropout ✓ ✓ ✓ Crop ✓ ✓ ✓ ✓ CropAndPad ✓ ✓ ✓ ✓ CropNonEmptyMaskIfExists ✓ ✓ ✓ ✓ ElasticTransform ✓ ✓ Flip ✓ ✓ ✓ ✓ GridDistortion ✓ ✓ GridDropout ✓ ✓ HorizontalFlip ✓ ✓ ✓ ✓ Lambda ✓ ✓ ✓ ✓ LongestMaxSize ✓ ✓ ✓ ✓ MaskDropout ✓ ✓ NoOp ✓ ✓ ✓ ✓ OpticalDistortion ✓ ✓ PadIfNeeded ✓ ✓ ✓ ✓ Perspective ✓ ✓ ✓ ✓ PiecewiseAffine ✓ ✓ ✓ ✓ PixelDropout ✓ ✓ ✓ ✓ RandomCrop ✓ ✓ ✓ ✓ RandomCropNearBBox ✓ ✓ ✓ ✓ RandomGridShuffle ✓ ✓ ✓ RandomResizedCrop ✓ ✓ ✓ ✓ RandomRotate90 ✓ ✓ ✓ ✓ RandomScale ✓ ✓ ✓ ✓ RandomSizedBBoxSafeCrop ✓ ✓ ✓ RandomSizedCrop ✓ ✓ ✓ ✓ Resize ✓ ✓ ✓ ✓ Rotate ✓ ✓ ✓ ✓ SafeRotate ✓ ✓ ✓ ✓ ShiftScaleRotate ✓ ✓ ✓ ✓ SmallestMaxSize ✓ ✓ ✓ ✓ Transpose ✓ ✓ ✓ ✓ VerticalFlip ✓ ✓ ✓ ✓ Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-7/7.7-torchensemble.html":{"url":"chapter-7/7.7-torchensemble.html","title":"7.7 TorchEnsemble 模型集成库","keywords":"","body":"7.7 TorchEnsemble 模型集成库 俗话说,三个臭皮匠顶个诸葛亮,机器学习模型亦是如此。Model Ensemble(模型集成)是机器学习领域重要的研究方向,在传统机器学习以及各种数据科学竞赛中,Model Ensemble成了标配, 因此,本节就介绍工业生产中实用的模型集成技术。 pytorch的生态系统中已经有ensemble的库,本节将介绍torchensemble的使用,各种集成方法的逻辑,torchensemble的代码结构以及如何改造自己的代码。 ensemble-pytorch 简介 TorchEnsemble是pytorch生态的一员,提供了统一的集成模块供pytorch使用。目前支持的方法有 Fusion Mixed fusion.py Classification / Regression Voting[1] Parallel voting.py Classification / Regression Neural Forest Parallel voting.py Classification / Regression Bagging[2] Parallel bagging.py Classification / Regression Gradient Boosting[3] Sequential gradient_boosting.py Classification / Regression Snapshot Ensemble[4] Sequential snapshot_ensemble.py Classification / Regression Adversarial Training[5] Parallel adversarial_training.py Classification / Regression Fast Geometric Ensemble[6] Sequential fast_geometric.py Classification / Regression Soft Gradient Boosting[7] Parallel soft_gradient_boosting.py Classification / Regression 各方法的理论简介可查阅文档 (关于模型集成的理论概念,请自行通过机器学习基础了解) 原理分析介绍 torchensemble提供了训练示例,本节对示例进行了整理及注释,可参见配套代码。 通过代码段可知道,torchensemble提供了集成类,集成类中的基学习器都是同一个结构(如LeNet-5),然后通过集成类的fit()函数完成训练,通过evaluate()来评估。 本部分最核心的问题在于理解不同的集成方法,它是如何使用多个基学习器的输出结果,下面通过源代码中的forward函数观察不同方法的集成方式。 fusion 原理:先平均,后概率。 通过FusionClassifier类的 forward函数可看到,它是对基学习器的输出进行逐元素的平均,然后再进行softmax进行输出分类概率向量。 class FusionClassifier(BaseClassifier): def _forward(self, *x): \"\"\" Implementation on the internal data forwarding in FusionClassifier. \"\"\" # Average outputs = [estimator(*x) for estimator in self.estimators_] output = op.average(outputs) return output @torchensemble_model_doc( \"\"\"Implementation on the data forwarding in FusionClassifier.\"\"\", \"classifier_forward\", ) def forward(self, *x): output = self._forward(*x) proba = F.softmax(output, dim=1) return proba voting voting:先概率,后平均。 voting先对基学习器进行softmax,然后把多个概率向量进行平均。 关于投票法,在sklearn中还有多种选择,vote模式有soft与hard,在torchensemble采用的是soft方式,及返回的是各分类器分类概率的平均。 If voting='soft' and flatten_transform=True: returns ndarray of shape (n_samples, n_classifiers * n_classes), being class probabilities calculated by each classifier. If voting='soft' and `flatten_transform=False: ndarray of shape (n_classifiers, n_samples, n_classes) If voting='hard': ndarray of shape (n_samples, n_classifiers), being class labels predicted by each classifier. class VotingClassifier(BaseClassifier): @torchensemble_model_doc( \"\"\"Implementation on the data forwarding in VotingClassifier.\"\"\", \"classifier_forward\", ) def forward(self, *x): # Average over class distributions from all base estimators. outputs = [ F.softmax(estimator(*x), dim=1) for estimator in self.estimators_ ] proba = op.average(outputs) return proba bagging bagging:先概率,后平均。这与voting一样。 bagging的主要原理在于基模型的训练数据不一样,因此可得到不同的基模型,而torchensemble文档里提到,深度学习中数据越多,模型越好,因此就没有采用K-Flod的方法划分数据了。 \"bagging further uses sampling with replacement on each batch of data. Notice that sub-sampling is not typically used when training neural networks, because the neural networks typically achieve better performance with more training data.\" class BaggingClassifier(BaseClassifier): @torchensemble_model_doc( \"\"\"Implementation on the data forwarding in BaggingClassifier.\"\"\", \"classifier_forward\", ) def forward(self, *x): # Average over class distributions from all base estimators. outputs = [F.softmax(estimator(*x), dim=1) for estimator in self.estimators_] proba = op.average(outputs) return proba GradientBoostingClassifier GradientBoostingClassifier:先求和,再概率。 这里先求和是因为Gradient Boosting算法原理就是“加法模型”,最终的结果是利用N个学习器的结果之和得到。为什么呢?因为第二个学习器学习的是第一个学习器与目标检测的差距,第三个学习器学习的是第一个+第二个学习器结果之和与结果之间的差距,以此类推。因此才有了sum_with_multiplicative这个函数中的代码逻辑。 如果不了解集成学习的基础概念,是无法理解上面这段话的,因为上面这段话是对梯度提升(GradientBoosting)算法的简述,而梯度提升(GradientBoosting)算法相对于bagging方法而言,不是那么容易理解,还请自行补足机器学习基础概念。 def forward(self, *x): output = [estimator(*x) for estimator in self.estimators_] output = op.sum_with_multiplicative(output, self.shrinkage_rate) proba = F.softmax(output, dim=1) return proba def sum_with_multiplicative(outputs, factor): \"\"\" Compuate the summation on a list of tensors, and the result is multiplied by a multiplicative factor. \"\"\" return factor * sum(outputs) SnapshotEnsembleClassifier SnapshotEnsembleClassifier:先平均,后概率。 SnapshotEnsembleClassifier是深度学习模型提出后才发明的集成方法,这与深度学习模型训练过程有关。其思路是保存多个局部最后的模型,然后将它们的结果进行集成输出。 这个思路非常新奇,集成学习的核心点之一是如何寻找多个基学习器,通常方法是从数据、参数、模型类型出发,获得多个性能不同的基学习器。而SnapShotEnsemble是通过一次训练过程中,保存多个局部最优的状态为基学习器,这样做的好处是高效,且各基学习器的错误样本通常不会重复,因此模型是基于上一次错误样本进行训练的。 [Huang Gao, Sharon Yixuan Li, Geoff Pleisset, et al., “Snapshot Ensembles: Train 1, Get M for Free.” ICLR, 2017.] def _forward(self, *x): \"\"\" Implementation on the internal data forwarding in snapshot ensemble. \"\"\" # Average results = [estimator(*x) for estimator in self.estimators_] output = op.average(results) return output Average results = [estimator(*x) for estimator in self.estimators_] output = op.average(results) 更多方法请关注官方文档的介绍。 官方demo提供了一些对比实验,这里进行汇总,供参考 代码结构 torchensemble库将模型训练、推理、集成过程都进行了高度封装,并且提供统一的接口,如何将复杂的、多种多样的模型编写为统一的API接口? 这里就绘制简略版的UML类图,梳理代码结构。 通过类图,可以看到所有的继承类都是nn.Module类,由此可见第四章的nn.Module有多重要。 从代码结构可以学习了解到,一个复杂的模块都可以提炼、抽象出最基础的BaseClass,BaseClass中定义最核心的、最通用的属性和方法,如这里的基学习器、基学习器数量、学习器容器,forward(), fit(), predict()等。 手动实现模型集成 虽然torchensemble提供了丰富的集成方法,但是有的时候并不适用于手动训练的多个模型,下面记录两个手动实现模型集成(投票法)的代码方案。 一、借助nn.ModuleList() class Ensemble(nn.Module): def __init__(self, device): super(Ensemble,self).__init__() # you should use nn.ModuleList. Optimizer doesn't detect python list as parameters self.models = nn.ModuleList(models) def forward(self, x): # it is super simple. just forward num_ models and concat it. output = torch.zeros([x.size(0), len(training.classes)]).to(device) for model in self.models: output += model(x) return output 原文链接:https://www.kaggle.com/code/georgiisirotenko/pytorch-fruits-transferlearing-ensemble-test99-18/notebook 二、借助list,并Counter for img, label in tqdm(testloader): img, label = img.to(device), label.to(device) for i, mlp in enumerate(mlps): mlp.eval() out = mlp(img) _, prediction = torch.max(out, 1) # 按行取最大值 pre_num = prediction.cpu().numpy() mlps_correct[i] += (pre_num == label.cpu().numpy()).sum() pre.append(pre_num) arr = np.array(pre) pre.clear() result = [Counter(arr[:, i]).most_common(1)[0][0] for i in range(BATCHSIZE)] vote_correct += (result == label.cpu().numpy()).sum() print(\"epoch:\" + str(ep) + \"集成模型的正确率\" + str(vote_correct / valid_data_size)) 原文链接:https://blog.csdn.net/weixin_42237113/article/details/108970920 ResNet在Cifar-10上的实验效果 为了观察模型融合的效果,特地编写了代码,实现ResNet在Cifar10上的训练,并对每个基学习器的性能进行对比,直观的看出模型集成的作用是立竿见影的,请看效果图。 本实验采用3个学习器进行投票式集成,因此绘制了7条曲线,其中各学习器在训练和验证各有2条曲线,集成模型的结果通过 valid_acc输出(蓝色),通过上图可发现,集成模型与三个基学习器相比,分类准确率都能提高3个多百分点左右,是非常高的提升了。 为了能绘制出这幅图,特地构思了代码,代码主要是自定义了class MyEnsemble(VotingClassifier),并重写fit函数,使得训练过程的信息可以被记录下来。 小结 本节简单介绍了pytorch中使用模型集成的方法库——torchensemble,详细内容还请查看官方文档,同时可以关注kaggle的方案,集成学习是竞赛的必备方案,也是工业项目中常用的方法,请重点学习。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/":{"url":"chapter-8/","title":"第八章 图像项目案例","keywords":"","body":"第八章 图像项目案例 第八章 图像项目案例 8.1 图像分类——胸部X光肺炎分类 8.2 图像分割——脑MRI胶质瘤分割 8.3 目标检测——无人机检测 8.4 目标跟踪(上)——DeepSORT原理 8.4 目标跟踪(下)——虎门大桥车流量统计 8.5 生成对抗网络——CycleGAN 8.6 扩散模型——DDPM 8.7 图像描述——Image Captioning 8.8 图像检索(上)——理论基础 8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统 第八章简介 本章介绍图像领域的相关案例,包括图像分类、图像分割、目标检测、目标跟踪、GAN、扩散模型等相关任务,这里将以一个完整的项目为一节,代码也将是独立的,希望通过一个个完整的案例,梳理在实际任务中所涉及的各个知识点、环节。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.1-classification.html":{"url":"chapter-8/8.1-classification.html","title":"8.1 图像分类——胸部X光肺炎分类","keywords":"","body":"8.1 图像分类案例——胸部X光肺炎分类 前言 前言:时隔7个月,终于能有时间继续撰写本书,在这期间,生活发生了重大变化、工作内容与岗位也有不小调整,整体而言还是未来可期,接下来一段时间将开始撰写中篇,案例应用。 本案例以胸部X光片二分类任务为案例,完整的介绍图像分类任务的训练过程。其中,涉及的知识点有: 图片的读取与dataset的编写 数据增强策略消融实验:手动设置数据增强,AutoAug实验 基于torchvision的预训练模型使用与替换:调用resnet、convnext模型 完整的训练日志分析 模型推理代码及推理速度、吞吐量计算对比 案例的讲解不再拘泥于代码细节,而是从任务分解出发,将该案例任务划分为多个模块,并对每个模块涉及的知识点及核心代码进行讲解。 图像分类的主代码采用第七章第四节中的训练脚本实现。 数据模块 首先来看数据部分,数据通过mendeley下载,得到的是ChestXRay2017.zip压缩包,图片已经划分好训练集与验证集。 获取数据后,需要对数据基础情况进行了解,首先看目录组织形式,便于dataset的编写,目录组织形式如下: ├─test │ ├─NORMAL │ └─PNEUMONIA └─train ​ ├─NORMAL ​ └─PNEUMONIA 数据为jpeg格式,类型为灰度图像,长宽在600-1000像素左右。 接下来即可编写dataset,这里仍旧需要dataset的基础知识,可以快速回顾第三章 PyTorch 数据模块。 dataset配套代码。 数据加载完成后,需要做数据增强,这里可以手动根据任务背景知识进行手动设计,或者使用AutoAug中的数据增强策略。 手动设计 对于手动设计,这里给出了基于torchvision的案例与albumentations的案例,分别位于train_main.py与train_aug.py 这里要重点介绍的是albumentations的使用,需要注意的是数据normalize与totensor的地方有些不同,详情看 A.Normalize(normMean, normStd, max_pixel_value=255.), # mean, std, 基于0-1,像素值要求0-255,并通过max_pixel_value=255,来实现整体数据变换为0-1 ToTensorV2(), # 仅数据转换,不会除以255 对于pytorch,totensor是会除以255的,而albumentations是在normalize中实现除以255的操作。 AutoAug 一些任务可以套用AutoAug的策略,关于自动数据增强可以回顾第三章,在代码实现时候也需要注意将变换后的数据进行衔接,这里主要是把数据的size变换到模型接收的大小。从代码上看,autoaug可以理解为一个打包好的transform.compose,插入自定义的compose中即可。 auto_aug_list = torchvision.transforms.AutoAugment(transforms.AutoAugmentPolicy.IMAGENET) train_transform = transforms.Compose([ auto_aug_list, transforms.Resize(256), transforms.RandomCrop(input_size, padding=4), transforms.ToTensor(), transforms.Normalize(normMean, normStd), ]) 模型模块 关于模型的创建就很简单了,基于CNN的模型,可以通过torchvision直接创建,并且加载imagenet的预训练参数。 主要注意如何修改模型最后的FC层,来适应自定义任务的分类类别数。 对于提供的模型(如resnet, convnext),需要加载进来之后,debug形式的去观察模型的结构,看看最后一个FC层的定义是什么,然后针对性的修改。 例如:resnet50的是model.fc, convnext的是model.classifier[2],这里就需要大家对第四章module的概念有较深入的理解。 训练指令 resnet50 nohup python train_aug.py --data-path ./data/chest_xray --batch-size 64 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --model resnet50 > ./log.log 2>&1 & nohup python train_main.py --data-path ./data/chest_xray --batch-size 64--workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --model resnet50 > ./log.log 2>&1 & nohup python train_aug.py --data-path ./data/chest_xray --batch-size 64 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --model resnet50 --autoaug > ./log.log 2>&1 & convnext nohup python train_aug.py --data-path ./data/chest_xray --batch-size 32 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --print-freq 20 --model convnext > ./log.log 2>&1 & nohup python train_main.py --data-path ./data/chest_xray --batch-size 32 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --print-freq 20 --model convnext > ./log.log 2>&1 & convnext-tiny nohup python train_aug.py --data-path ./data/chest_xray --batch-size 32 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --print-freq 20 --model convnext-tiny > ./log.log 2>&1 & nohup python train_main.py --data-path ./data/chest_xray --batch-size 64 --workers 16 --lr 0.01 --lr-step-size 20 --epochs 50 --print-freq 20 --model convnext-tiny > ./log.log 2>&1 & 训练实验记录 其它模块代码都是先前脚本中有的,就不再赘述,在本案例中,做了一些对比实验。 对比实验一:resnet50与convnext-base/tiny的比较 对比实验二:手动设计数据增强与AutoAug数据增强的比较 结论一:简单场景下,resnet50适用性更好;convnext-base与convnext-tiny在本任务差别不大; 结论二:AutoAug数据增强方法稍差些,非自然图像场景,手动设计数据增强策略效果更好; 模型名称 日志文件夹 acc convnext-tiny 2023-02-09_09-50-07 92.5 convnext-base 2023-02-08_20-28-44 92.3 resnet50 2023-02-08_16-37-24 93.8 resnet50 + albumentation 2023-02-08_17-45-29 94.87 resnet50 + autoaug 2023-02-09_13-45-08 93.3 模型推理与时间测试 模型训练好之后是以将模型的权重参数存储为字典形式,放在了pt文件中,在未来使用时候需要创建模型(nn.module),然后把参数加载进去,同时需要注意在推理阶段的数据前处理。 在配套代码中,实现了一张图片的推理,同时对resnet50的推理速度进行了评估,推理速度效率如下: 设备 推理速度(bs=128) 吞吐量(bs=128) GTX 1080 Ti 5.23 ms/例0.67s ÷ 128 = 5.23 ms 191 帧 / 秒 1 ÷ 5.23 * 1000 = 191 RTX 3060 Laptop 2.18 ms/例0.28s ÷ 128 = 2.18 ms 459 帧 / 秒 1 ÷ 2.18 * 1000 = 459 Inte i7-11800H @ 2.30GHz 八核 41.2 ms/例 5.27s ÷ 128 = 41.2 ms 24.3 帧 / 秒1 ÷ 41.2 * 1000 = 24.3 i7-7700 @ 3.60GHz 101.8 ms/例 13.03s ÷ 128 = 101.8 ms 9.8 帧 / 秒1 ÷ 101.8 * 1000 = 9.8 综合来看,一般的cpu可以实现实时推理,gpu推理速度是cpu的10-50倍。 PS:本次推理时间测试并不充分,不可代表实际生产下的推理效率,因此需要额外注意几个点: 时间测算需考虑数据前处理、后处理时间; 数据组batch后,可充分利用gpu资源,单位时间内推理的图片数更多,但也需要考虑组batch的时间消耗 小结 本小节通过胸部X光片的二分类场景,详细描述了从数据集读取、数据增强策略、模型finetune修改、实验对比、模型推理的各个步骤,包含代码与理论介绍。 通过本小节,应能独立实现图像分类任务。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.2-segmentation.html":{"url":"chapter-8/8.2-segmentation.html","title":"8.2 图像分割——脑MRI胶质瘤分割","keywords":"","body":"8.2 图像分割案例——脑MRI胶质瘤分割 前言 本案例以脑部MRI肿瘤数据为例,介绍图像分割(本节特指语义分割)的训练、推理过程。其中,涉及的知识点有: 基于csv的数据集维护管理,及其dataset编写; smp库的介绍与使用:segmentation_models_pytorch库,是语义分割的高级API库,提供9种分割架构、数百个encoder的backbone及预训练权重,以及分割的loss和衡量指标计算函数,是语义分割的好帮手,使用它可以快速实现各语义分割功能; 对smp中9中网络架构对比实验,网络架构分别是:'Unet', 'UnetPlusPlus', 'MAnet', 'Linknet', 'FPN', 'PSPNet', 'DeepLabV3', 'DeepLabV3Plus', 'PAN'; 语义分割iou计算时,image-wise与整体计算的差异,当纯阴性片时,iou统计应当采用image-wise更合理。 探究不同backbone对于语义分割的效果差异; 探究不同loss对语义分割的效果差异; 探究encoder采用较小学习率时,模型的精度变化。 本案例将从数据介绍、训练代码、smp库使用、对比实验和模型推理,五个模块进行讲解。 数据模块 数据集来自Kaggle,包含110位脑胶质瘤患者的MRI数据,总共3929张图片,其中有肿瘤区域的图片为2556张,阴性图片1373张。 下图为每个患者图片数量,以及阴、阳图片的比例汇总,从图中可知,一个MRI序列包含20-40张图片,其中出现肿瘤的图片有60%左右。(不过这里需要注意,肿瘤区域的像素点还还是远小于阴性像素点区域的) 下图为数据集示意,第一行为MRI图像,第二行为人工标注的脑胶质瘤mask。 数据集划分 首先下载数据集,解压得到kaggle_3m文件夹,然后设置数据根目录data_dir = args.data_path,运行下面代码,即可得到对应csv python 01_parse_data.py --data-path /mnt/chapter-8/data/kaggle_3m 数据集为110个文件夹形式,这里需要进行数据集划分,本案例采用csv对数据集进行维护,这里将会通过01_parse_data.py对数据集进行解析,获得以下三个csv data_info.csv:包含文件夹id、图片路径、标签路径; data_train.csv:根据文件夹id分组划分的训练集,比例为80%,有88位患者的3151张图片; data_val.csv:根据文件夹id分组划分的验证集,比例为20%, 有22位患者的778张图片; 知识点:数据划分需要按患者维度划分,不能基于图片维度随机划分,基于图片维度随机划分会使得模型存在作弊行为,导致模型在真实应用场景下效果很差。 为什么不能基于图片维度划分数据?因为这样做的话,以为患者的40张图片,有32张用于训练,另外8张用于测试,这8张与那32张是非常接近的,因为是同一个人的连续影像。这样划分的数据集是带有偏差的,理所当然的效果很好,模型不容易出现过拟合。后续的实验也证明了这一点,基于图片维度划分的精度要高出10个百分点。 Dataset编写 dataset编写就相当容易了,因为数据的路径信息已经获得,因此只需要注意数据读取进来之后,如何转换称为标签的格式即可。 这里要注意,对于语义分割,若采用的是交叉熵损失函数,Dice损失函数,它们要求标签是一个long类型数据,不需要手动转为one-hot向量,因此对于本实验,mask要变成一个[256,256]的矩阵,其中每个元素是0或者1。对应的实现代码如下: mask = cv_imread(self.df.iloc[idx, 2]) mask[mask == 255] = 1 # 转换为0, 1 二分类标签 mask.long() 训练代码 训练代码整体结构仍旧沿用第七章第四节中的训练脚本实现。 在此处需要做的修改主要是,语义分割模型的创建、分割模型的Loss创建、分割模型指标评价,以下四行代码分别是对应的实现 model = smp.Unet(encoder_name=args.encoder, encoder_weights=\"imagenet\", in_channels=3, classes=1) criterion = smp.losses.DiceLoss(mode='binary') tp, fp, fn, tn = smp.metrics.get_stats(outputs.long(), labels, mode=\"binary\") iou_score = smp.metrics.iou_score(tp, fp, fn, tn, reduction=\"macro\") 可以发现,这里面无一例外都用到了smp库,下面简单介绍smp库的优点,以及使用方法。 smp库介绍 segmentation-models-pytorch是pytorch的语义分割工具库,提供9个分割框架,数百个encoder,常用的loss,指标计算函数,非常方便开发者进行语义分割开发。 这是smp库的github链接与官方文档 github: https://github.com/qubvel/segmentation_models.pytorch docs:https://smp.readthedocs.io/en/latest/ 它安装很方便,只需要pip即可, pip install segmentation-models-pytorch。 掌握pytorch基础知识的话,smp库只需要10分钟即可掌握上手,更系统应用建议配合smp库的两个案例进行学习。 下面将从模型创建、loss创建、指标计算三个部分介绍smp使用。 模型创建 语义分割模型发展至今,主要还是采用encoder-decoder的形式,通常会采用主流的CNN作为encoder,decoder部分则进行随机初始化去训练。 而encoder与decoder之间如何信息交互、以及decoder由哪些组件构成等等一系列问题,就引出了不同的语义分割架构。 在smp中,提供了9种常用的语义分割模型架构,分别是'Unet', 'UnetPlusPlus', 'MAnet', 'Linknet', 'FPN', 'PSPNet', 'DeepLabV3', 'DeepLabV3Plus', 'PAN'。 在语义分割中,除了架构、encoder,输入和输出的维度也非常重要,这关系到可接收的数据形式是什么,以及可以预测的类别有多少个。 因此,一个语义分割模型的创建,需要确定架构、选择encoder、再确定输入通道数、输出通道数。 下面介绍unet的创建 import segmentation_models_pytorch as smp model = smp.Unet( encoder_name=\"resnet34\", # choose encoder, e.g. mobilenet_v2 or efficientnet-b7 encoder_weights=\"imagenet\", # use `imagenet` pre-trained weights for encoder initialization in_channels=1, # model input channels (1 for gray-scale images, 3 for RGB, etc.) classes=3, # model output channels (number of classes in your dataset) ) 对于初学者来说,那么多模型或许是个头痛的问题,后续也进行了对比实验,采用相同的encoder(resnet-18),分别训练9个语义分割架构,观察精度变化。 从经验来说,凡是有系列的模型都是应用广泛、相对可靠的模型,例如net系列,deeplab系列,yolo系列等等。 如何用代码来实现类属性的调用,这里是一个值得学习的代码段,这里主要通过getattr()方法获取module的属性,然后对其进行实例化即可。 archs = ['Unet', 'UnetPlusPlus', 'MAnet', 'Linknet', 'FPN', 'PSPNet', 'DeepLabV3', 'DeepLabV3Plus', 'PAN'] for arch_str in archs: model_class = getattr(smp, arch_str) model = model_class(encoder_name=args.encoder, encoder_weights=\"imagenet\", in_channels=3, classes=1) 更多关于分割模型的介绍,可以参见:https://smp.readthedocs.io/en/latest/ 损失函数创建 smp提供了8个损失函数,分别是JaccardLoss、DiceLoss、TverskyLoss、FocalLoss、LovaszLoss、SoftBCEWithLogitsLoss、SoftCrossEntropyLoss、MCCLoss。具体差异参见官方文档,这里要讲讲损失函数创建时,需要设置的模式。 损失函数创建时需要设置mode,smp库提供了3种mode,分别是二分类、多分类、多标签分类。 二分类:'binary', 用于一个类的分割(阴性不算一个类,例如本案例),对于二分类,标签需要是(N, H, W)的形式,元素为0或1,,它不需要通道维度,而模型输出是跟着pytorch走的,因此仍旧是4D张量,形状是(N, 1, H, W). 多分类:'multiclass',多分类是更为常见的场景,例如VOC、COCO数据集,这时标签元素为0, 1, ..., C-1,类似交叉熵损失函数(可以把语义分割看成是逐像素分割),模型的输出自然是(N, C, H, W)了,因为一个像素点,需要用C维的分类概率向量来做分类。 多标签:'multilabel',多标签语义分割指一个像素即是A类又是B类,因此它的标签需要借助C个通道来标注,对应类别设置为1,其它设置为0。所以标签形式为(N, C, H, W),模型输出仍旧是(N, C, H, W),多标签需要注意模型输出时就不再做softmax,而是对每个神经元做sigmoid,以此判断该类是否存在。 对于loss的选择,一般是交叉熵损失函数、Dice这两个系列,其它的可以自行选择,它就像模型架构一样,学术界有几十上百个选择,但在工程领域,仁者见仁智者见智,更多的语义分割损失函数可参考SegLoss 指标计算 语义分割可以理解为逐像素的图像分类,因此图像分类的各指标也可用于衡量分割模型,但分割与分类不同的是,它注重空间结构信息,关注集合与集合之间的关系,因此更常用的是IoU或Dice系数来评估模型。 IoU(Intersection over Union,交并比)是用来衡量两个集合重叠的情况,公式计算为:交集/并集,而dice系数(Dice similarity coefficient,又名dsc)也用于评估两个集合重叠情况,但是计算公式不一样,而且根据文章阐述, \"Dice倾向于衡量平均性能,而 IoU 倾向于衡量最坏的表现。\" 具体计算时,通常先获得tn, tp, fn, fp,然后计算指标。蓝色部分为TP(True Positives),红色部分为FN(False Negatives),黄色部分为(False Positives) smp工具库中也是这么做的,首先根据smp.metrics.get_stats函数,获得tp, fp, fn, tn。随后通过各指标计算函数获得相应指标, 可计算的指标有fbeta_score、f1_score、iou_score、accuracy、precision、recal1、sensitivity、specificity、balanced_accuracy、positive_predictive_value、negative_predictive_value、false_negative_rate、false_positive_rate、false_discovery_rate、false_omission_rate、positive_likelihood_ratio、negative_likelihood_ratio。 来看一段使用示例: import segmentation_models_pytorch as smp # lets assume we have multilabel prediction for 3 classes output = torch.rand([10, 3, 256, 256]) target = torch.rand([10, 3, 256, 256]).round().long() # first compute statistics for true positives, false positives, false negative and # true negative \"pixels\" tp, fp, fn, tn = smp.metrics.get_stats(output, target, mode='multilabel', threshold=0.5) # then compute metrics with required reduction (see metric docs) iou_score = smp.metrics.iou_score(tp, fp, fn, tn, reduction=\"micro\") f1_score = smp.metrics.f1_score(tp, fp, fn, tn, reduction=\"micro\") f2_score = smp.metrics.fbeta_score(tp, fp, fn, tn, beta=2, reduction=\"micro\") accuracy = smp.metrics.accuracy(tp, fp, fn, tn, reduction=\"macro\") recall = smp.metrics.recall(tp, fp, fn, tn, reduction=\"micro-imagewise\") 在使用时,需要注意的有2个地方, 计算tp, fp, fn, tn时,模型输出要转换为类别标签,而不是概率向量。 对batch数据统计时,是否需要考虑样本不平衡问题,进行加权平均,是否需要基于图像维度进行计算后再平均。 对于问题1,get_stats函数提供了二分类、多标签时的处理方法,只需要在model下设置'binary' 或 'multiclass'之后,设置threshold即可。从此可看出需要手动进行sigmoid(); 对于问题2,较为复杂一些,smp提供了6个模式,这些在sklearn中有的,分别是,'micro' , 'macro' , 'weighted' , 'micro-imagewise' , 'macro-imagewise' , 'weighted-imagewise'。 'micro’ 用于计算总体的指标,不对每个类别进行计算, 'macro'计算每个类别的指标,然后求和平均,不加权。 ’weighted’ 计算每个类别的指标,然后根据每类样本数,进行加权求平均 可参考(https://blog.csdn.net/qq_27668313/article/details/125570210) 对于x-imagewise,表示根据图片维度进行计算,然后将指标求取平均。 因此,可知道,前缀micro、macro、weighted是决定如何对类别进行求取平均,后缀-imagewise表示如何对图片之间进行求平均。 所以,对于binary来说,'micro' = 'macro' = 'weighted' ,并且 'micro-imagewise' = 'macro-imagewise' = 'weighted-imagewise'。 在这里重点讲一下,本案例采用的是macro来统计,因此iou比较低,如果是手写iou统计,一般会基于图片维度计算,然后再平均,也就是macro-imagewise。 本案例中,由于大量阴性图片的存在,所以若不采用-imagewise的话,阴性片虽然预测正确,但实际上是tn很大,而tn缺不计入iou计算中。若采用imagewise,阴性预测正确时,iou为1,从而可以大幅度提高iou值。 下面可以通过代码观察,对于阴性片,模型预测完全正确,tp, fp, fn值都是0的情况下,iou也是会被计算为1的。 tp, fp, fn, tn = smp.metrics.get_stats(c, b, mode=\"binary\") tp Out[12]: tensor([[0]], device='cuda:0') fp Out[13]: tensor([[0]], device='cuda:0') fn Out[14]: tensor([[0]], device='cuda:0') tn Out[15]: tensor([[65536]], device='cuda:0') smp.metrics.iou_score(tp, fp, fn, tn, reduction=\"macro\") Out[16]: tensor(1., device='cuda:0') 对比实验 此部分实验没有进过严格微调,只用于横向比对,主要观察不同架构、不同backbone、不同学习率策略之间的差异,可以大体上观察出一定的趋势,供大家参考,以便后续选择模型。 在这里主要做了4次实验,分别是: 实验一:不同backbone的实验,猜想为越复杂的backbone,精度越高,这里采用uent-resnet18/34/50来观察。 实验二:不同网络架构之间的实验,猜想是有系列的模型,鲁棒性更高,适用性更广,如unet、deeplab系列。这里backbone均采用resnet-18,训练了九个模型。 实验三:encoder采用小10倍学习率,让decoder学习率高一些,猜想是要比相同学习率的效果更好,这里就需要与实验二中的九个模型精度对比。 实验四:根据图片随机划分与病人维度划分的差异,猜想是根据图片随机划分,精度要比病人高的,毕竟用到了极其类似的图片进行训练。 实验完整代码在github 实验一:不同backbone的差异 python 02_train_seg.py --batch-size 16 --workers 8 --lr 0.01 --epochs 100 --useplateau --model unet --encoder resnet18 python 02_train_seg.py --batch-size 16 --workers 8 --lr 0.01 --epochs 100 --useplateau --model unet --encoder resnet34 python 02_train_seg.py --batch-size 16 --workers 8 --lr 0.01 --epochs 100 --useplateau --model unet --encoder resnet50 unet-resnet18 unet-resnet34 unet-resnet50 miou 0.82 0.79 0.84 结论:可证实猜想基本正确,越复杂的backbone,精度越高。但resnet34的精度反而比resnet18要差,这个需要仔细研究,在此不做讨论。 实验二:对比不同模型架构差异 python 03_train_architecture.py --batch-size 16 --workers 4 --lr 0.01 --epochs 100 --useplateau --encoder resnet18 这里可以看到unet++和deeplabv3+精度较高,过它们的训练速度也是最慢的,侧面反映出它的模型复杂,理所应当获得最高精度。 'Unet' 'UnetPlusPlus' 'MAnet' 'Linknet' 'FPN' 'PSPNet' 'DeepLabV3' 'DeepLabV3Plus' PAN miou 0.81 0.85 0.73 0.83 0.83 0.78 0.81 0.84 0.62 实验三:encoder采用更小学习率差异 # encoder采用小10倍学习率 python 03_train_architecture.py --batch-size 16 --workers 4 --lr 0.01 --epochs 100 --useplateau --encoder resnet18 --lowlr 要想获得好的精度,往往需要各种trick来微调,对于语义分割模型中encoder部分,可以采用较小学习率,因为encoder提取的是共性的语义特征,对于decoder才需要特有的特征,因此可以对它们两个进行差异化设置学习率。为此,对encoder学习率乘以0.1,观察模型精度变化。 从实验结果来看,简单粗暴的调整大都未获得精度提升,反而都存在3-4个点的掉点,deeplabv3, MAnet, PAN缺有提升,由此可见训练的trick还是难以适应各个场景的。 综合实验二、实验三,可以观察到unet系列和deeplab系列都是比较稳定的,这也是它们一直被工业界认可、使用至今的原因,因此推荐首选Unet系列或deepalabv3+进行任务的初步验证。 'Unet' 'UnetPlusPlus' 'MAnet' 'Linknet' 'FPN' 'PSPNet' 'DeepLabV3' 'DeepLabV3Plus' PAN lowlr 0.79 0.81 0.82 0.81 0.81 0.76 0.83 0.80 0.80 base 0.81 0.85 0.73 0.83 0.83 0.78 0.81 0.84 0.62 实验四:根据图片随机划分数据集 此实验需要在01_parse_data.py 代码中的data_split()函数下修改groupby的对象,需要改为 grouped = dff.groupby('image_path') # bad method 并且将csv文件名做相应修改。 miou 细节 unet-resnet18 0.82 基于patient维度划分 unet-resent18 0.88 基于imgs维度划分 很明显,基于imgs维度划分存在很明显的性能提升,约6个点,但是这个提升是假性的,会迷惑工程师,误以为模型很好。 这是实际业务场景中常碰到的问题,一定要注意业务数据的维度划分问题。 模型推理 模型推理与图像分类类似,没有什么特殊的地方,在这里想重点讲一下模型输出的数据如何转换为要用的类别mask。 由于是二分类,并且输出是一通道的矩阵,因此会采用sigmoid将它变为分类概率的形式,然后再通过阈值(一般设为0.5)转换为0/1的mask矩阵。如下代码所示: outputs = model(img_tensor_batch) outputs_prob = (outputs.sigmoid() > 0.5).float() outputs_prob = outputs_prob.squeeze().cpu().numpy().astype('uint8') 接着通过opencv寻找轮廓函数可以得到边界,最后进行可视化。 最后通过imageio实现gif图的生成,可参见05-gen-gif.py。 即可得到下图 小结 通过本案例,可以掌握: 语义分割模型的训练与推理流程 smp工具库中的模型创建、损失函数创建、评价指标计算使用 评价指标中,micro、macro、weighted, x-imagewise的差别 dice与iou评价指标的定义与差异 9个语义分割架构实验比对,为后续选择模型提供参考 医学数据处理划分维度,需要基于患者维度,不可基于图片维度 基于本案例需要进一步学习的内容: 采用多类分割任务进行实验,了解softmax与sigmoid的处理差异; 进一步了解unet系列、deeplab系列适合的场景 语义分割后处理:图像处理的形态学操作 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.3-detection.html":{"url":"chapter-8/8.3-detection.html","title":"8.3 目标检测——无人机检测","keywords":"","body":"8.3 目标检测——无人机检测 前言 目标检测在工业界应用广泛,例如工业生产线上的质量检测、监控工业场景中的安全问题、机器人的自主导航等,可以提高生产效率和产品质量,降低生产成本,也可以提高工作环境的安全性,减少事故发生,由此可见目标检测意义重大。 本小节,将介绍工业界中的热宠YOLOv5,并通过无人机场景检测任务进行实现。 本小节内容丰富,包括: VisDrone数据集介绍 目标检测常见数据格式介绍:VoC, COCO, YOLO YOLOv1-YOLOv8 概述:了解YOLO发展历史,各版本模型优点,缺点 YOLOv5 源代码结构剖析及使用步骤:了解优秀的项目代码结构,设计 YOLOv5在VisDrone数据集上的多组实验,了解不同容量的YOLOv5能力 数据模块 VisDrone数据集介绍 VisDrone数据集是一个大规模的用于视觉目标检测、跟踪和计数等任务的数据集,由天津大学整理于2018年发布,论文为Vision Meets Drones: A Challenge,可通过github下载。 其中包含多个任务,这里采用VisDrone-DET数据集,进行目标检测案例学习,VisDrone-DET也就是VisDrone2018。 VisDrone-DET中,总共包含10209张,划分为了4个数据集,额外给出了一个1610张模拟的测试集-dev,训练集6471张,验证集548张,测试集-dev1610张,测试集-challenge 1580张。 数据特点:图片分辨率较大、目标小、目标存在一定程度遮挡,部分是严重遮挡(例如汽车)。 下面介绍数据集目录组织形式与标签含义。 数据包含annotations和images两个文件夹,分别存储标注信息txt文件,图片jpg文件,文件名保持一致。 标签txt文件中,一行表示一个标注框,一行有8个信息,例如 495,473,120,46,1,4,0,1 507,447,107,38,1,4,0,1 分别表示: ,,,,,,, bbox:前四个为bbox信息; score:表示标注框的置信度,取值为0或1,0表示忽略,1表示可用。 object_category: 目标类别,0-11,分别是,gnored regions(0), pedestrian(1), people(2), bicycle(3), car(4), van(5), truck(6), tricycle(7), awning-tricycle(8), bus(9), motor(10), others(11) truncation:截断程度,取值为0,1。0表示无截断,1表示目标有1%~50%的区域再当前帧(图像)之外。 occlusion:遮挡程度,0-2。0表示无遮挡,1表示1%-50%遮挡,2表示50%-100%遮挡。 对于本案例使用,仅需要剔除score=0的bbox。 目标检测数据集格式 了解数据之后,需要把数据集转换为代码可以接收的形式。与分类分割任务不同,目标检测领域的数据格式相对固定,一般不需要自己重写数据加载模块,而是跟随代码框架的要求来适配。 目标检测目前主流的有三种格式,VOC格式、COCO格式、YOLO格式,下面分别介绍。 VOC数据集格式如下: 图像位于JPEGImages文件夹。 标注文件位于Annotations文件夹中,标注文件与图像文件文件名应当相同,并且XML格式描述目标的位置和类别等信息,bbox形式为xmin、xmax、ymin、ymax。 COCO数据集格式如下: 图像位于images文件夹中。 标注文件位于annotations文件夹下的instances_train2017.json,所有标注均存储在一个json中,并通过特定字段获取图片对应的标签信息。bbox形式为xmin, ymin, w, h。 YOLO数据格式如下: 图像位于images文件夹下。 标注文件位于labels文件夹下,标注文件与图像文件文件名应当相同,并且以txt文件存储标签信息。txt中一行是一个标注框,一行的格式为: class x_center y_center width height,其中bbox的数值需要除以长/宽,使得值为0-1之间。 在这里侧面证明了YOLO模型的强大,以往算法开发要适应数据集的格式,而现在yolo系列模型被广泛使用,使得大家愿意接受YOLO数据格式。 标签数据转换代码 在目标检测任务开发过程中,无法避免数据集之间的转换,这里推荐repo,可以实现三种数据格式之间的互相转换。 在本案例中,适合自行编写脚本进行转换,这里yolov5官方也给出了代码,因此直接复用即可。 推荐yolov5中使用的几个小函数: 文件夹的创建, Path对象可直接.mkdir(),并且可设置递归及判断存在与否。 from pathlib import Path (dir / 'labels').mkdir(parents=True, exist_ok=True) # make labels directory pbar显示进度, 将可迭代对象用tqdm包一下,并且设置打印的字符串信息格式desc pbar = tqdm((dir / 'annotations').glob('*.txt'), desc=f'Converting {dir}') 使用配套代码,运行即可得到与images文件夹在同级目录下的labels文件夹 python visdrone2yolo.py --data-path /mnt/chapter-8/data/visdrone 到这里yolo格式数据集已经准备好,如何在代码中使用及配置,这部分放到模型模块中的yolo框架讲解部分。 模型模块 在数据模块,讲解了目标检测中数据集常见的格式,并且已经转换称为yolo格式,等待使用。 若是在分类或分割中,我们会手写代码进行实验,然而在目标检测,绝大多数是不需要的,原因有 目标检测中涉及多个模块,重新造轮子的时间成本高,容易出bug; 目标检测可用成熟的“框架”(可用的repo)很多,如ultralytics的yolov3/yolov5/yolov8,mmdetection,paddledetction等,感谢前人的工作! 成熟的\"框架\"指,功能齐全,满足绝大多数场景,受众多,经得起时间的考验,可以放心使用。 在图像分割的实验中就指出,凡是有系列的,优先考虑,因此,这里选择ultralytics的yolov5,它是目前为止(2023年3月10日07:07:25)star最高的目标检测开源代码仓库,已经36K,足以证明yolov5的稳定性与可靠性。 yolo系列简介 YOLOv1 YOLOv2 YOLOv3 YOLOv4 YOLOv5 YOLOv6 YOLOv7 YOLOv8 时间 2015.6.8 2016.12.25 2018.4.8 2020.4.23 2020.6.10 2022.6.23 2022.7.7 2023.1 作者 Joseph Redmon Joseph Redmon Joseph Redmon Alexey Bochkovskiy Ultralytics 美团 Alexey Bochkovskiy Ultralytics 深度学习目标检测框架发展快10年时间,只有yolo(You Only Look Once)系列久经不衰,截至目前公认的版本已经到了v8,很难有人能将v1-v8的细节全部吃透。 在校有时间的同学十分建议从v1-v8认真学习,这样可以理解目标检测的发展,锻炼科研意识,掌握代码能力。 对于工作繁忙的工程师来说,它们只是解决问题的工具,了解工具的优点与缺点,有的放矢的应用在各个项目场景也是不错的选择。 为此,特地总结yolov1-v8的特点,为后续使用工具打下基础。 参考deephub的文章,可以看到yolo系列主流的模型发展历程。 yolov1:2015年提出的one-stage目标检测算法,与当时的Fater RCNN(two-stage)共同称为当时最受欢迎的检测模型。特点为anchor-free:没有anchor的概念,每个cell直接输出bbox。每个cell仅输出2个bbox,每个cell输出向量为(20+ (4+1)*2),20为20个类,1为bbox概率,4为bbox信息,一张图片最终变为7×7的特征图,一个cell只能预测1个类,因此定位粗糙,小目标不友好,对重叠物体检测能力差。 yolov2:针对yolov1定位不精准问题,借鉴faster rcnn的anchor-base的概念,并且引入k-means实现anchor的自动配置。 yolov3:划时代意义的目标检测算法,也奠定了目标检测之后的范式,backone+neck+多尺度。yolov3网路结构简单,并且采用多尺度特征图实现不同尺寸目标的检测,速度与精度在当时都是优于其他模型。yolov3采用的是手动配置的33=9种anchor,anchor的参数设置是通过k-means对标签进行聚类发现的,*3种尺寸,3种长宽比。 yolov4:yolov4发布前有个小插曲,那就是YOLO之父Jeseph Redmon,由于“无法忽视工作带来的负面影响”,公开宣布隐退。好在有大神接了他的大旗,在yolov3推出快2年的时间,yolov3的改进版v4终于在2020年来了,yolov4开始,可以认为是一个分割点,更准确地说yolov3是后续模型的分割点。借助paperswithcode的一个精度图,可以看到yolov3在coco的map是30-40之间,而往后v4-v8已经来到50-60的区间,已经不在一个档次。 对于yolov4,它对当时深度学习的多种tricks进行了实验,集成到yolov3上进行改进,精度和速度都得到大幅度提升。它使用了大量tricks,包括WRC、CSP、CmBN、SAT、 Mish activation、Mosaic data augmentation、CutMix、CmBN、DropBlock regularization 和 CIoU loss、GIoU loss。 yolov5:在yolov4发布后短短2个月,yolov5横空出世,并且带来了多种大小的模型, nano/s/m/l/x等尺寸,可适用于多种场景,同时配备高质量的开源代码仓库,短时间内就受到了广泛关注。yolov5数据增强上使用了Mosaic数据增强、自适应锚框计算、自适应图片缩放(推理时采用最小填充原则,加速推理)、融合新网络模块Focus、CSP结、FPN+PAN,GIOU_Loss,以及预测框筛选的DIOU_nms、 yolov6:2022年由美团提出的速度更快的检测模型,主打是速度,因此模型特点是backbone与neck的设计都为了适应硬件的运算,使用了Rep-Pan和EfficientRep块,head部分用了解耦的形式,在训练策略方面采用了anchor-free、SimOTA标记策略、SIoU盒回归的损失。 yolov7:在yolov6推出不到半个月,yolov7也发布了,yolov7团队与yolov4团队一致,属于官方YOLO团队(yolov4团队接过yolo之父Jeseph Redmon的大旗)。yolov7同样从速度方面做了许多优化,例如内存访问成本、I / O比率、element-wise、激活函数等,以及模型重参数化(re-parameterization)。 yolov8:yolov5的团队——ultralytics打造的集成图像分类、图像分割、目标检测于一体的结构,目前github地址并为采用yolov8而是采用ultralytics。发布2个多月后,论文仍旧未发布,具体优化内容请关注官方github,从代码中观察吧。 yolov5 代码结构讲解 根据广大工程师“用脚投票”的结果,本案例采用ultralytics的yolov5来实现目标检测,并学习代码中优秀的设计思想,同时剖析用户如何使用yolov5仓库代码。 后续建议跟进yolov8! 学习yolov5之前,推荐阅读yolov5的readme,其中包含了许多学习资料。 下图为yolov5(2023.03版)的代码结构,逻辑相当清晰,可分三个模块,三个模块是图像分类、图像分割和目标检测。 目标检测分为data、models、utils和运行脚本部分。 data:存放的主要是数据超参数配置yaml文件。 models:存放的是各模型的yaml配置文件,即模型创建依赖于yaml文件。 utils:存放各功能模块,如,数据增强augmentations.py, 自动计算anchor功能autoanchor.py,激活函数activations.py, fastapi接口等等。 检测模型的训练、验证、推理分别在:train.py, val.py, detect.py中。下面将重点讲解train.py的运行机制。 yolov5 训练机制讲解 train.py有600多行,并且调用了许多函数,一开始接触会感到困难。 不过不用担心,它还是pytorch框架,仍旧逃离不了基础核心模块, dataloader, module,loss, scheduler,迭代训练。 下面依次简要的说明train.py中是如何训练的。 参数配置模块 使用parse_opt()进行参数包装,训练时可指定模型、数据集配置yaml路径,超参数配置yaml路径等内容。 数据模块 man() --> train()-->create_dataloader(): 第188行,调用了create_dataloader()函数,并且传入了数据集配置yaml文件中的训练集路径。 utils/dataloders.py-->LoadImagesAndLabels(): 在函数内部采用LoadImagesAndLabels()类实现pytorch的dataset的生成,class LoadImagesAndLabels(Dataset), 代码有483行,里面实现了许多数据检测、数据加载功能,但是核心与pytorch的dataset是一致的,重点关注init中做了哪些初始化,以及getitem如何从磁盘加载图片并且做数据预处理、数据增强的即可。 模型模块 第125行实现模型创建,可以看到是通过配置信息进行创建的,这里的配置信息来自参数配置模块中--cfg或者指定预训练模型--weights, model = Model(cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=hyp.get('anchors')).to(device) # create 再看往里看 DetectionModel继承自BaseModel,BaseModel集成nn.Module,里边细节就可通过nn.Module的基础知识一步一步剖析。 迭代训练模块 核心功能在,262行 262行:主循环 for epoch in range(start_epoch, epochs): # epoch ------------------------------------------------------------------ 284行:batch循环 for i, (imgs, targets, paths, _) in pbar: # batch ------------------------------------------------------------- 310行:前向推理,计算loss,反向传播 pred = model(imgs) # forward loss, loss_items = compute_loss(pred, targets.to(device)) # loss scaled by batch_size scaler.scale(loss).backward() 344行:epoch维度变更学习率,因此在batch的循环之外 lr = [x['lr'] for x in optimizer.param_groups] # for loggers scheduler.step() 日志输出模块 在runs文件夹下会有train文件夹,每一次实验会以exp{实验次序}创建文件夹,在下面会保存训练过程中的一系列有价值内容。 如下图所示,会有这些文件 weights:训练好的模型权重,包括last.pt, best.pt hyp.yaml:训练时的超参数,便于复现 results.png:训练曲线,便于分析训练情况,调整超参数 results.csv:训练指标记录表格 train_batch2.jpg:训练数据集,bbox绘制情况,十分有用,可用于检测数据标签是否处理正确! val_batch0_labels.jpg:验证数据集,bbox验证情况 val_batch2_pred.jpg:模型预测出的bbox在验证集上的情况。 混淆矩阵:针对标签的预测的情况进行混淆矩阵观察,这个混淆矩阵是转置了的,行是预测,列才是真实标签,以下图为例,汽车这个类别中,有78%的汽车框被预测为了汽车,有19%的汽车框没有被检测到,剩下2%的汽车框被检测出来了,但是分类时分为了van(箱式货柜车)。 PR_curve:PR曲线是各类别的AP情况 P_curve和R_curve:是观察模型对各类别分类情况,可用于挑选分类概率阈值, 横轴表示选择的阈值,纵轴是对应的值。可以看到阈值越小召回率越高,反之,精确度越低。 labels_correlogram.jpg:是借助seaborn的pairplot,绘制的多变量的二维分析相关性统计分析图。以列为基础,第一列是x与其它数据的关系,第一列,第一行,表示x的整体分布,可以看到是相对于中心点0.5对称的,表明矩形框在宽这个维度,左边有的数量与右边有的数量是一致的,并且呈现中间多,两边少的情况。 第一列,第二行,表示x与y的分布情况,同labels.jpg中第三幅图一样,观察矩形框整体情况是宽度偏中间,高度偏中间与中下部。 第一列,第三行,表示x与w的分布情况,呈现一个梯形,这个很合理,因为当x靠近图片的最左边的时候,即物体在图像的边界时,这个物体一般不会很大,否则根据拍照的基础原理,肯定会将镜头朝向主要物体,并放置在镜头中央,不至于在边界处。 第一列,第四行,表示x与h的分布情况。 第二列,第二行,是y的整体分布,可以看到是偏向0-0.5之间。 第三列,第三行,是w的整体分布。 第四列,第四行,是h的整体分布。 yolov5 训练VisDrone步骤 第一步:设置数据集配置yaml文件,首先到detection\\yolov5-master\\data\\下复制一份yaml,命名为mydrone.yaml,设置好路径即可,这里yolo数据格式只需要images路径就能通过相对路径寻找到labels。同时设置好检测类别数量与名称 path: G:\\deep_learning_data\\VisDrone # dataset root dir train: VisDrone2019-DET-train\\\\images # train images (relative to 'path') 128 images val: VisDrone2019-DET-val\\\\images # val images (relative to 'path') 128 images test: # test images (optional) nc: 10 # number of classes names: ['pedestrian', 'people', 'bicycle', 'car', 'van', 'truck', 'tricycle', 'awning-tricycle', 'bus', 'motor'] 第二步:将预训练模型下载到code/chapter-8/03_detection/yolov5-master下,下载方式为github。 第三步:在终端,运行训练指令,即可在runs/train下面看到对应日志. python train.py --imgsz 640 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 对比实验 实验一:visdrone数据集特点是分辨率大,一般的640,1000的尺寸无法满足要求,为此,进行了5种尺寸的训练,用于观察不同分辨率对精度的影响 实验二:yolov5提供多种尺寸的模型,这里观察s/m/l三种尺寸的模型对精度的影响。 实验三:同时观察yolov5自带的三种不同强度的数据增强带来怎样的精度变化。 更新:所有实验权重、实验文件已经上传云盘:链接:https://pan.baidu.com/s/11kQJcCka2VyR5ToF-N0BOQ 提取码:op4x 实验一/二:不同输入尺寸对模型精度的变化 python train.py --imgsz 640 --batch 24 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 960 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 1280 --batch 12 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 1600 --batch 8 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 1920 --batch 6 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml map50/map50:95 640 960 1280 1600 1920 yolov5s 0.33/0.18 0.44/0.26 0.50/0.30 0.54/0.33 0.55/0.34 exp0-4 yolov5m 0.38/0.21 0.48/0.30 0.53/0.33 0.57/0.36 0.59/0.38 exp11-15 yolov5l 0.40/0.23 0.50/0.31 0.55/0.35 0.57/0.37 0.60/0.39 exp16-20 从上图可以看出: 随着尺寸增大,精度得到提高,且1920仍未达到瓶颈,可继续增加图片尺寸来获得精度提高。 随着模型容量增大,精度得到提高;可根据任务难以程度选择合适容量的模型。 在size和模型容量两者间可以选择更适合的方式来涨点,即size也可以涨点,换大模型也可以涨点,如果不能同时采用,则根据上下游条件进行取舍。 实验三:不同数据增强方法的变化 这里套用yolov5提供的三种强度的数据增强方法,观察精度变化。 python train.py --imgsz 960 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-low.yaml python train.py --imgsz 960 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-med.yaml python train.py --imgsz 960 --batch 16 --epochs 100 --data mydrone.yaml --weights yolov5s.pt --workers 8 --hyp data/hyps/hyp.scratch-high.yaml scratch-low scratch-med scratch-high map50/map50:95 0.44/0.26 0.44/0.26 0.43/0.26 exp5-7 从结果可知,yolov5中自带的low, med, high在本案例中效果都一样,并无差别。 模型推理 训练好模型后,可通过detect.py进行推理并观察结果,detect.py提供了多个参数接口 weights:训练好的.pt文件,.pt文件中存储了模型结构,因此无需额外指定模型结构的yaml文件 source:需要检测的数据来源,支持图片、视频、摄像头、网络视频url等 data:数据集yaml文件,关联检测的类别名称 imgsz:图像输入大小 conf-thres:检测框置信度阈值 iou-thres:非极大值抑制时的iou阈值设置 half:采用半精度(Float 16)进行推理,可提升推理速度,但有一定精度损失 其它配置参数可看代码注释,这面介绍detect.py中的核心代码结构,观察其是如何实现推理的。 第一部分,数据加载读取,对于数据的加载与读取,采用utils/dataloders.py中实现的3个类来实现包装,并进行迭代。如LoadStreams、LoadScreenshots、LoadImages,对于三个类的实例,在使用时,采用for循环进行依次取出数据 dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride) dataset = LoadScreenshots(source, img_size=imgsz, stride=stride, auto=pt) dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt, vid_stride=vid_stride) for path, im, im0s, vid_cap, s in dataset: 第二部分,模型加载,使用models/common.py中的DetectMultiBackend类实现,该类支持多种计算后端如pytorch\\onnx\\tensorrt\\jit\\dnn等等。其中,pytorch模型是通过models/experimental.py中的attempt_load()函数实现加载。attempt_load()需要的一个核心参数就是.pt文件路径。然后根据.pt内信息完成模型创建、权重加载等工作。 model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half) model = attempt_load(weights if isinstance(weights, list) else w, device=device, inplace=True, fuse=fuse) 第三部分,推理与保存,推理主要两个步骤,模型前向传播,经过NMS后得到最终输出矩形框。对于结果可视化,这里采用Annotator类实现绘制,首先将图像传入Annottor,进行实例化,后续通过annotator.box_label()进行bbox与labels的绘制。 pred = model(im, augment=augment, visualize=visualize) pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det) # ------------------------------------------------------------------------------------------------ annotator = Annotator(im0, line_width=line_thickness, example=str(names)) annotator.box_label(xyxy, label, color=colors(c, True)) 运行以下推理指令,即可在.runs/detect/exp*下获得结果,下图为航拍视频推理示例 python detect.py --weights ./runs/train/exp2/best.pt --source G:\\DJI_0690.MP4 --data data/mydrone.yaml --imgsz 1280 --half python detect.py --weights best.pt --source G:\\DJI_0690.MP4 --data data/mydrone.yaml --imgsz 1280 到这里,yolov5代码就讲解完毕,yolov5代码库还有许多值得学习的地方,这里由于篇幅关系,作为拓展阅读推荐给大家: 模型导出为TFLite, ONNX, CoreML, TensorRT:https://github.com/ultralytics/yolov5/issues/251 TTA(test time augmentation): https://github.com/ultralytics/yolov5/issues/303 模型剪枝:https://github.com/ultralytics/yolov5/issues/304 yolov5训练技巧总结:https://github.com/ultralytics/yolov5/wiki/Tips-for-Best-Training-Results yolov5模型集成:https://github.com/ultralytics/yolov5/issues/318 小结 本案例介绍了yolov5实现无人机视角的目标检测,主要涉及以下知识点: Visdrone数据集介绍与标签含义解析,会有模糊程度与遮挡程度的两个额外标注信息。 目标检测常见数据形式:voc,coco,yolo形式,三者的bbox形式均不一样,使用时需要注意转换。xmin,ymin,xmax,ymax; xmin, ymin, w, h; x_center, y_center, w, h yolov1-v8模型简介:简要介绍v1-v8的模型特点,便于后续有的放矢的选择使用。 yolov5代码结构介绍:剖析yolov5项目代码结构,并分析如何进行训练、推理。 自定义数据集训练过程:详细介绍自己的数据集要如何使用yolov5进行训练的过程,核心在于了解yolov5的数据加载形式与模型加载形式都通过yaml文件进行管理。 对比实验:分析分辨率、模型容量、数据增强方法带来的精度变化,对后续重要超参数设置具有指导性意义。 本案例已近万字,可以快速用代码实现目标检测,但是对于目标检测的学习来说,还远不够,案例初衷还是通过具体的项目,来巩固pytorch基础知识。 最后,可以发现,即使是采用yaml来管理数据和模型,在实现的时候还会继承dataset和dataloader,以及nn.Module,由此可见第三章和第四章的概念有多么重要。 对于想要深入了解目标检测的朋友,推荐学习: 非深度学习目标检测时期的检测方法; faster rcnn + yolov3的详细理论过程与代码实现 yolov3后时代下的,anchor-free, one-stage的检测模型 特定问题目标检测:小目标检测, 旋转目标检测,密集场景目标检测,超大分辨率图像目标检测、遮挡场景目标检测等等 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.4-tracking-1.html":{"url":"chapter-8/8.4-tracking-1.html","title":"8.4 目标跟踪(上)——DeepSORT原理","keywords":"","body":"8.4 目标跟踪(上)——DeepSORT原理 前言 目标跟踪技术可以用于人流量计数和车流量计数等,能够帮助人们更好地了解和掌握一个地区的交通状况和人流状况。这些计数功能有以下几个价值和意义: 交通规划:通过了解车流量,可以更好地规划交通路线和疏导车流,提高交通效率,减少拥堵,从而减少交通事故的发生。 商业决策:通过了解人流量,可以更好地了解商业活动的热点区域,从而制定更加有效的营销策略和经营计划,提高商业效益。 目标跟踪(object tracking)定义是,在视频序列中识别目标并赋予唯一标识,同时跟踪目标在视频序列中的位置。在自动驾驶、监控、人机交互等领域都有应用。 目标跟踪常用的策略是TBD(Tracking-by-Detecton),又称DBT(Detection-Based-Tracking)。即在每一帧进行目标检测,再利用目标检测的结果来进行目标跟踪,这一步称为数据关联(Data Assoiation)。与之相对的,是DFT(Detection-Free Tracking), DFT使用较少。 根据目标的数量,目标跟踪可分为单目标跟踪(Sing-Object Tracking)与多目标跟踪(Multi-Object Tracking),目前MOT研究较多,并且MOT可覆盖SOT。 根据处理时效性,又可分为在线跟踪(Online)与离线跟踪(Offline),离线跟踪是指可以使用后续帧的信息来预测当前帧,在视频分析中可用。在线跟踪是只能采用前序帧信息与当前帧信息,这是目前主流方案。 本案例中的目标跟踪属于多目标跟踪、在线跟踪、TBD。 通过简单定义,可以知道,目标跟踪分两步 检测:找出当前帧中的目标,即目标检测 关联匹配:将当前目标与历史帧中的目标进行关联与匹配 检测可以采用各类目标检测算法,关联匹配可以采用deepsort算法。 本案例将详细介绍DeepSORT算法原理,并基于yolov5实现车流量统计代码。 DeepSORT算法流程 DeepSORT算法发表于2017年,其是SORT的改进版。SORT(Simple Online and Realtime Tracking)于2016年发表,主要基于卡尔曼滤波和匈牙利算法实现。 DeepSORT算法则是对SORT加入了Deep Association Metric进行特征提取与匹配,是目前精度与速度都不错的跟踪算法。 SORT论文速读:提出了基于卡尔曼滤波和匈牙利算法的目标跟踪策略,同时发现好的目标检测器,可以大幅度提升MOT精度,高达18.9个百分点。SORT实现分为4个步骤,分别对应3.1-3.4,目标检测模型得到目标框;采用卡尔曼滤波进行轨迹框的预测;采用匈牙利算法对目标框与轨迹框进行匹配;最后基于匹配结果,删除旧轨迹框,添加新轨迹框。(论文只有5页,核心内容第三章仅半页纸,但不妨碍它是优秀的工作) DeepSORT论文速读:基于SORT,DeepSORT最大特点是引入了deep association metric,即采用CNN提取目标框中图像特征,来进行匹配。同时,涉及了级联匹配策略,有了更好的准入、准出机制,对目标的跟踪更精细、合理。 目标跟踪的过程相当复杂,为了能了解全过程,这里通过具体案例,一步一步发现问题,然后学习DeepSORT的解决方案,最后汇总。 为了将复杂的问题描述清楚,有必要对名词进行一些解释。 检测框(dets):由目标检测模型输出的框,包含框的位置信息,物体类别信息,是该物体的概率信息 跟踪框(tracks):跟踪模块认为是有价值的检测框。跟踪框中有两种,一个是正式框,一个是预备框。论文中称为confirmed, unconfirmed, 这里借鉴正式党员、预备党员的叫法,应该好理解一些。 预备框(unconfirmed):潜在的跟踪框,只在算法内部记录,当达到一定条件,转为正式跟踪框,才能被算法输出,在屏幕上绘制出来。 正式框(confirmed):目标跟踪算法的输出,回顾定义,目标跟踪需要在视频序列中识别目标并赋予唯一标识,即输出框应当包含检测框信息、唯一标识。 假设世界上没有目标跟踪算法,需要我们自己构思,需求是在在连续帧中将检测到的物体关联起来,实现目标跟踪。 现在有个行人跟踪任务,如图所示 第一帧:检测器只有一个检测框,因此赋予它唯一标识,再采用卡尔曼滤波算法进行跟踪框坐标的输出。 第二帧:检测器输出两个框,如何将2个检测框与1个跟踪框进行匹配,获得行人1在第二帧当中的跟踪框。这时可以借助匈牙利算法,它是求解任务分配问题的组合优化算法。 匈牙利算法可以很好的将检测框1与前序跟踪框1匹配上,然后对前序跟踪框1进行更新(采用卡尔曼滤波),获得当前跟踪框1。 对于检测框2,没有找到与其匹配的前序跟踪框,所以认为它是新进入的,给它创建一个新跟踪框即可。因此,当前跟踪框应有两个。 第三帧:又来了一个人,检测到了3个框,因此重复第二帧的任务,采用检测框更新采用卡尔曼滤波)跟踪框的信息,同时为王五注册新的身份ID——行人3。 第四帧:张三离开了图像,检测器只检测到2个框,2个检测框去匹配3个跟踪框,自然会有一个跟踪框匹配不上,这里显然是行人1,因此没有匹配上的跟踪框需要被删除,最终输出两个跟踪框。 以此类推,新来检测框匹配已有跟踪框,匹配不上,则增加跟踪框,同理,已有跟踪框没有匹配到新的检测框,认为它离开了,需要删除跟踪框。 到这里,一个基础的目标跟踪框架出来了,有了新增跟踪框机制、删除跟踪框机制。这就是大名鼎鼎的SORT算法的流程,对于匹配细节和跟踪框的坐标更新 SORT很好的解决检测框如何与跟踪框对上号,同时有了新增、删除跟踪框机制,但是对于常见的问题没有得到很好的解决,例如: 检测器漏检:检测器在某一帧漏检是很常见的现象,假设第二帧中,张三漏检了,第二帧会将张三的身份ID——行人1给删除。第三帧中的张三将会被认为是新来的,无法匹配到他是行人1。 检测器误检:检测器在某一帧错误的将背景检测为了行人,根据SORT算法,会被背景赋予一个跟踪框,这是很不合理的。 为了让目标跟踪算法输出的跟踪框更稳定,DeepSORT引入了预备框、正式框机制,可以很好的解决漏检、误检带来的不稳定。 对于新增,要考察一下框是否是真的,通常用3帧的时间来考察,当发现框连续3帧都存在,那么认为它是一个好的框,算法批准框称为正式框。这样可以很好的过滤掉一些”没有耐心“的框。这样对于某一帧,某两帧的误检,是很好的过滤方法。 对于删除,要考察一下框是否真的离开,毕竟框也是经过了准入审查的,通常不会一瞬间就离开,此时给它连续30次机会,连续30帧里边发现它都不在了,将它永久开除。 综合上述理解,DeepSORT流程解释如下: DeepSORT核心——匹配过程 匹配过程指的是,如何将检测框与跟踪框匹配上,让每一个检测框都能找到与之对应的跟踪框。若没有找到,则认为是新进入的物体,会创建新跟踪框。 deepsort的匹配过程分两部分。 首先,基于外观特征和马氏距离,为正式框进行匹配,方法是级联匹配(matching cascade),用到的优化方法是匈牙利算法。 然后,基于bbox坐标和IoU,为预备框进行匹配,采用剩余检测框与剩余跟踪框(未匹配和预备框)匹配,用到的优化方法是匈牙利算法。 外观特征与bbox坐标对应:表示的是对于一个物体,要用什么特征表示TA,是1128的向量?还是14的向量? 马氏距离与IoU对应:表示两个特征之间\"相近\"程度的衡量,只有衡量了两个特征之间的距离,后续才能用优化算法优化距离最短的匹配方案 匈牙利算法作用:将N个A与M个B,采用特征向量描述以及距离度量方法,可以得到N*M的距离代价矩阵,即A中每一个元素与B中每一个元素之间的距离。随后用匈牙利算法找到最优匹配。 级联匹配 级联匹配的思想是分70级进行匹配,级的概念指距离当前帧的远近,第一级(level)采用所有检测框, 和仅被记录了一次的正式框(if tracks[k].time_since_update == 1 + level),以此循环70次。 因为越新的目标,越有可能与检测框匹配上,存在太久的目标可能离开了。级联匹配可以解决一部分身份交换问题。 级联匹配中,传入了: distance_metric:基于外观特征(CNN提取出来的512维特征向量)的举例度量函数 max_distance:当距离大于max_distance时,认为是不匹配的 tracks:跟踪框 detections:检测框 track_indices_l:本轮需要匹配的跟踪框的index unmatched_detections:本轮需要匹配的检测框的index # code/chapter-8/tracking/deep_sort/deep_sort/sort/linear_assignment.py 的matching_cascade函数 for level in range(cascade_depth): if len(unmatched_detections) == 0: # No detections left break track_indices_l = [ k for k in track_indices if tracks[k].time_since_update == 1 + level # 为每个跟踪框记录它被更新的次数,优先选择新跟踪框进行匹配, 1+0 ] if len(track_indices_l) == 0: # Nothing to match at this level continue # ============================ 核心部分:匹配 ================================ matches_l, _, unmatched_detections = \\ min_cost_matching( distance_metric, max_distance, tracks, detections, track_indices_l, unmatched_detections) matches += matches_l 级联匹配中采用的是跟踪框的历史特征列表与检测框进行匹配,如跟踪框已经检测到了18次,会得到18个特征向量,新的检测框有30个,则会得到18*30的矩阵。 然后在第0维选择最小值,得到1*30的距离矩阵,最终判断是否有匹配上的检测框。 Tracker --> _match() --> gated_metric() 下的: cost_matrix = self.metric.distance(features, targets) 跳转到:deep_sort/sort/nn_matching.py 的 NearestNeighborDistanceMetric.distance() cost_matrix = np.zeros((len(targets), len(features))) for i, target in enumerate(targets): cost_matrix[i, :] = self._metric(self.samples[target], features) 跳转到: def _nn_cosine_distance(): distances = _cosine_distance(x, y) # 18*30的矩阵 return distances.min(axis=0) # 选择距离最小的特征; 如18*1,选择18个跟踪框中与第一个检测框距离最近的;以此类推得到1*30. # 由此可见,检测框与目标的所有历史特征向量进行距离计算,挑选最近那个特征的距离作为评判距离。 级联匹配之后,会有未匹配的检测框,未匹配的正式框(如果被记录70次以上,是无法进行匹配的),以及预备框。 接下来用IoU测量检测框与跟踪框之间的相似性,很好理解,IoU越大,它俩越有可能是一个物体。 IoU匹配 IoU匹配的代码位于:code/chapter-8/tracking/deep_sort/deep_sort/sort/tracker.py 的_match()函数, 同理采用的min_cost_matching进行匹配,传入的有iou_cost度量函数,max_iou_distance用于过滤,跟踪框,检测框,需要匹配的跟踪框的index,需要匹配的检测框的index。 # Associate remaining tracks together with unconfirmed tracks using IOU. iou_track_candidates = unconfirmed_tracks + [ k for k in unmatched_tracks_a if self.tracks[k].time_since_update == 1] unmatched_tracks_a = [ k for k in unmatched_tracks_a if self.tracks[k].time_since_update != 1] matches_b, unmatched_tracks_b, unmatched_detections = \\ linear_assignment.min_cost_matching( iou_matching.iou_cost, self.max_iou_distance, self.tracks, detections, iou_track_candidates, unmatched_detections) 匈牙利算法 无论是级联匹配还是IoU匹配,最后都会用到min_cost_matching函数,其中匹配的核心代码是: # code/chapter-8/tracking/deep_sort/deep_sort/sort/linear_assignment.py min_cost_matching() row_indices, col_indices = linear_assignment(cost_matrix) # 匈牙利算法求解,得到配对的(raw, col) 这里使用了scipy库的linear_sum_assignment实现,可返回最优匹配的坐标,到底匈牙利算法是如何解决分配问题,下面进行介绍。 匈牙利算法是1955年美国数学家哈罗德·库恩((W.W.Kuhn)),基于匈牙利数学家康尼格(D.Kőnig)提出的康尼格定理,提出求解二分图最大匹配的一种方法。 二分图( Bipartite graph,二部图)是图论中一种模型,指的是有A,B两个节点集合,存在一系列边,边的两端不能再同一个集合,简单说就是A只能和B相连,反之亦然。 为了求解分配问题,需要对二分图中每种可能进行代价描述,称之为代价矩阵(系数矩阵、变换矩阵等等)。 下面借鉴视频中的内容,简要介绍匈牙利解决二分图最大匹配问题。 假设有一本说明书,需要翻译成4种语言,现在有4个人,他们对每个语言的熟悉程度不同,因此如何分配任务,就是一个典型的二分图最大匹配问题。 首先,可以根据任务进行量化,得到目标函数min Z。 然后,设置约束条件,一个人选一个语言,一个语言只能被一个人选择。 最后,得到右下角的方程式。 匈牙利算法实现步骤是: 画圈,划0:对代价矩阵每行减去最小值,使得其出现0;然后对列进行同样操作,使得其出现0; 试指派寻找最优解:0表示最优解,一行只有一个0的话,肯定优先考虑分配。 因此按行找仅有一个0的行,并且分配,分配之后,行已经被分配,因此对应的行需要删除。 同理对列操作。 若还存在没有标记的0元素,且找不到独立0元素的行(列),从剩余0元素最少的行(列)开始,比较这行0元素所在列中0元素的数目,选择0元素最少的那列的这个0元素画圈,同时划去该行该列其余0元素。(如绕口令一般,这里推荐看视频) 打勾,画圈:没有画圈的行打√,打勾行含划0元素的列打√,打√列含画圈0元素的行打√,未打√的行画横线,打√的列画竖线。 增加0元素:寻找未被直线覆盖部分的最小元素,打 √的行减最小元素,打 √ 的列加最小元素。 重复执行2-4,直到找到n个位于不同行不同列的0元素。 其核心思想是用增广路求最大匹配,这里的行列操作实际是将增广路的思想转换为矩阵的表达,因此单纯观察匈牙利算法的矩阵解法,是很难理解其原因,建议通过图论、运筹学的基础知识去了解匈牙利算法求解过程。 更多目标跟踪中的匈牙利算法讲解推荐: 匈牙利算法&KM算法 目标跟踪初探(DeepSORT) 代码细节: 对于代价矩阵,行是tracks, 列是dets,匹配上的框才会有index返回。 DeepSORT核心——更新输出过程 卡尔曼滤波 由于目标检测算法的不稳定,直接用目标检测输出的检测框来表示目标位置的精度不佳,常常会看到框的抖动。 为了让框更稳定的描述物体的位置,deepsort中采用了卡尔曼滤波算法(Kalman filtering)来对目标位置进行输出。 卡尔曼滤波算法是斯坦利·施密特(Stanley Schmidt)在1958年提出的,当时要解决的是阿波罗飞船的导航问题,可以用于估计飞船的位置,是一个很好的运动估计。 随后,卡尔曼滤波广泛应用在天文,宇航,气象等领域。 卡尔曼滤波可以解决的核心问题是,在一个线性动态系统中,可以基于历史信息与当前输入信息,很好的估计当前最优信息,当前最优信息就是卡尔曼滤波的输出,它可以很好的过滤掉噪声(必须是高斯噪声)。 这里的历史信息,可以理解为跟踪框(tracks)(上一帧),当前输入信息是目标检测算法输出的检测框(dets),而当前时刻deepsort要输出的目标的位置,是dets+tracks经过卡尔曼滤波算法的输出,即一个当前最优信息,是一个预测的、估计的值。 为了对卡尔曼滤波有进一步认识,这里简要介绍卡尔曼滤波思想和概念。对于细节,推荐阅读图说卡尔曼滤波,, 从放弃到精通!卡尔曼滤波从理论到实践~ 这里借用视频中的公式进行讲解过程,公式中更细节内容,可以参考卡尔曼滤波的五大公式 x:观测对象,例如卫星的坐标,图像中目标的坐标,水壶中的温度等。 x:观测对象,例如卫星的坐标,图像中目标的坐标,水壶中的温度等。 t:表示时刻 -:表示估计值 ^:表示估计,由于x都是带着^的,这里可以不加以区分 F:状态转移矩阵 P:协方差矩阵 K:卡尔曼增益,用于权衡,历史信息与当前输入信息的重要程度。 对于目标跟踪算法的输出,是公式4,公式4也是最核心的内容,其余公式都在为公式4服务的。 为了理解公式4,借鉴文章如何通俗直白地理解卡尔曼滤波算法的讲解。 假设,有两台电子秤,分别进行测量一瓶水,得到的结果如图所示。 由图可知,电子秤不是绝对精准的,存在一定误差,不过当前观测值分别是160和170,那么如何融合两个数据? 最简单的是 M = (A+B)/2 = 165。 求平均的设想里,有一个重要前提是,认为A和B的贡献是一样的,重要程度是一样的,因此各占50%的权重。 如果,A的精度更高,B精度差一些,即A的方差小一些,B方差大。这时,平均就不合适了,应该让精度高的观测值的权重更高。 权重的计算,需要考虑谁更重要,即方差更小,所以可以通过方差的比较获得权重分配。 A 测量结果 为 160 +- 3, B 测量结果 为 170 +- 9,可知A 的测量结果精度是 B 测量结果精度的 3倍。 这个公式是理解上述公式4的关键,通过将A提取出来,变为单独一项,就可以很好的衡量,基于A,要如何变更,得到最优估计值。 这里的变更加号的右边,B-A乘以一个权重,这个权重就是卡尔曼滤波中的卡尔曼增益K。其中B就是目标检测算法输出的dets,A是tracks。 而卡尔曼增益K的计算,需要依靠协方差矩阵P。 DeepSORT 小结 到此,总结一下卡尔曼滤波过程,当前帧跟踪框的信息由卡尔曼滤波器在更新阶段输出。 更新阶段需要使用到:当前帧检测框, 基于上一帧跟踪框的预测值,并且加权得到。 其中,上一帧跟踪框的预测值来自公式1。代码中是: self.tracker.predict()。 有了基于上一帧跟踪框的预测值,再输入dets,就可以得到当前帧跟踪框信息,代码中是: self.tracker.update(detections)。 在代码中,卡尔曼滤波器维护mean和covariance,分别表示公式中的预测值x,协方差矩阵P。 self.mean, self.covariance = kf.predict(self.mean, self.covariance) # mean即bbox的坐标数据 self.mean, self.covariance = kf.update(self.mean, self.covariance, detection.to_xyah()) 到这里,deepsort原理有了大概的描述,更多细节仍需要到代码中观察,这里做一个简要回顾。 跟踪框的输出: 为了更稳定,采用了卡尔曼滤波算法,将当前帧检测框信息,结合卡尔曼滤波对当前帧的预测,两者共同作用,得到输出。 目标的匹配: 为了让检测框找到对应的,合适的跟踪框,把它转化为二分图最大匹配问题,可以用匈牙利算法很好的求解。 同时,为了匹配更精准,减少身份交换,deepsort先进行新目标框的匹配(仅限前70级,级表示被跟踪的次数),然后再进行基于IoU的匹配。 跟踪框准入准出: 为了避免漏检、误检等短暂的不稳定因素,设计了预备框和正式框的概念。经过3次考验,可转正,经过30次机会仍不靠谱(未检测到),开除X籍。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.4-tracking-2.html":{"url":"chapter-8/8.4-tracking-2.html","title":"8.4 目标跟踪(下)——虎门大桥车流量统计","keywords":"","body":"8.4 目标跟踪(下)——虎门大桥车流量统计 上一小节,对deepsort的实现步骤进行了详细分析,本小节将使用deepsort代码以及yolov5来实现车流量统计。 本节,首先对deepsort源代码的设计进行简要的结构剖析,来学习代码设计,然后将其结合yolov5,实现车流量统计。 注意,代码所需的模型文件,通过网盘下载:链接:https://pan.baidu.com/s/1Ys_v1Tqta4wJMHC8NKeTTg 提取码:ucf4 注意,ckpt.t7为deepsort的模型文件,一定要放到:F:\\pytorch-tutorial-2nd\\code\\chapter-8\\tracking\\deep_sort\\deep_sort\\deep\\checkpoint下面 deepsort源码——结构分析 deepsort的代码不算复杂,设计了几个核心类,然后为各个核心功能编写了实现函数。这里绘制简要的UML图,对代码设计的思路进行学习。 对于一个业务场景,首先识别实体,并分析实体的属性和功能,下面自下而上进行分析目标跟踪当中存在的实体。 Track目标物体:目标跟踪的核心元素是目标物体,这里称为Track类,对于一个目标,需要有坐标信息,id, 特征向量列表,状态等信息。 Tracker目标跟踪器:管理所有目标,并可实现目标的更新,因此需要卡尔曼滤波器,管理目标集合tracks等信息。 KalmanFilter卡尔曼滤波器:维护卡尔曼增益矩阵,并实现预测、更新两大功能。 DeepSort类:进行统一封装,对外提供update函数,返回跟踪框。 对于级联匹配,会在Tracker类中_match()实现,其中设计了一系列模块,包括 matching_cascade:级联匹配实现,循环70次进行匹配。 min_cost_matching:实现一次最小代价匹配。 linear_assignment:匈牙利算法实现。 NearestNeighborDistanceMetric:级联匹配中距离度量功能实现,其中维护了各目标的所有特征向量,每一帧的特征向量都会被保存。最大保存100个特征向量。 if self.budget is not None: self.samples[target] = self.samples[target][-self.budget:] # 相当巧妙的实现最多记录最新的100个特征向量 到这里,deepsort大体框架以及搭建完毕,使用方法非常简单,只需要实例化DeepSort类,调用.update()即可获得跟踪框信息。 deepsort+yolov5——车流量统计 目标跟踪可以获得目标的位置及唯一标识,它只是方法,并不是目的。 基于目标跟踪方法,可以实现许多有价值的事情,例如卡口人流量计数,交通道路车流量计数与统计,警戒区域预警等。 进行行人计数或车流量计数时,需要判断目标是否经过特定区域,这时需要撞线机制来实现,撞线机制可以有两种方法实现。 一种是基于区域判断,另外一种是基于线段相交判断。 另外一种是基于线段相交判断,则是基于物体的历史轨迹曲线,判断是否与界线相交。 计数中的撞线机制 本案例采用基于区域的撞线机制,对边界设置两个区域,一般称inner和outter区域,当物体先到达inner,再进入outter,则可判断物体是离开,反之亦然。 对于inner和outter区域,每个区域需要记录曾经到达区域里的目标id,仅当两个区域同时存在过目标id,可以计数,并且删除目标id。 例如图中的ID-60,进入了inner区域(蓝色),inner区域需要观察ID-60是否存在outer区域中(黄色),当前是不存在的,因此添加到inner区域的历史列表中。 下一帧,ID-60到达黄色区域,黄色区域同样地,先判断ID-60是否来自蓝色区域,它在蓝色区域的历史记录中找到了ID-60,因此可以判断ID-60是从inner到达outer,所以outer进行加一。 反之inter加一。 在代码实现上有一些抽象,这里做简单的讲解。 如何判断物体到达inner和outter区域? 采用mask矩阵,inner区域像素是1, outer像素是2,mask矩阵大小与图片大小一致,采用目标的位置坐标对mask矩阵索引,通过索引值==1? 还是==2?来判断当前物体位于什么区域。 如何判断物体先、后顺序? 区域中发现新物体时,首先判断是否存在对向区域,若不存在,才可以加入区域的物体容器中进行管理。若存在,即可删除,并且计数。 为了实现撞线机制,这里设计了三个类,分别是BoundaryType、CountBoundary和BaseCounter 处理逻辑在BaseCounter的counting(),边界区域抽象成CountBoundary,实现了必要的函数来完成计数。 下面简单介绍counting函数中,如何判断物体是从outer来,到达inter,实现inter计数+1的(反之亦然) 第1行:通过物体的x,y坐标,对索引矩阵进行索引,得到索引值。例如:[1, 2, 0, 0, 0, ...]。通过索引值可知在何区域 第4行:通过索引值列表,判断当前在inner区域的目标,并且返回它们的id 第5行:获取,当前到过outer的目标id 第8行:判断是否有交集,有交集表明,该id从outer来,已经抵达inner。可以计数。 第9行:判断是否存在差集,inner有,outer没有,表明物体可以加入inner的id_container中进行管理 第10行:由于目标完成了计数,outer_boundary中需要删除它。 第11行:由于目标第一次到来,所以注册到inner_boundary中,后续供outer_boundary查询。 bbox_area_list = self.area_mask[index_xy] # 获取bbox在图像中区域的索引,1,2分别表示在边界区域. [int,] # ======================== 先处理inner区域 ==================================== inner_tracks_currently_ids = self.get_currently_ids_by_area(tracks, bbox_area_list, BoundaryType.inner) outer_tracks_history_ids = list(self.outer_boundary.id_container.keys()) # 获取历史帧经过outer区域的目标的id # 当前与历史的交集,认为是目标从outer已经到达inner,可以计数,并且删除。 outer_2_inner_tracks_id = self.intersection(inner_tracks_currently_ids, outer_tracks_history_ids) only_at_inner_tracks_id = self.difference(inner_tracks_currently_ids, outer_tracks_history_ids) self.outer_boundary.remove_tracks(outer_2_inner_tracks_id) # 删除outer中已计数的id self.inner_boundary.register_tracks(only_at_inner_tracks_id) # 注册仅inner有的id 注意事项: 在第1行中 self.area_mask的制作中,由于采用的是像素1和2,在resize时,导致2的边界有一系列1的存在,导致了误检! 按设计,1在2的下面,这里1反而出现在了2的上面,导致实际是“出”的,计算为了“入” 把上述代码组装起来得到01-main.py,做好模型文件、视频文件、边界点的配置,运行即可得到以下结果。 模型权重文件,视频文件可通过网盘下载: 注意,ckpt.t7为deepsort的模型文件,一定要放到:F:\\pytorch-tutorial-2nd\\code\\chapter-8\\tracking\\deep_sort\\deep_sort\\deep\\checkpoint下面 完整视频可见B站 这里为了实现边界区域点集的获取,编写了鼠标点选边界区域的代码00-draw-border.py。 运行后,鼠标双击实现选点,选点顺序必须从左上角开始,顺时针,选择完毕,terminal中打印的点集list,复制下来使用即可。 小结 目标跟踪案例中,内容比较多,这里总结一些关键知识点: SORT与DeepSORT算法步骤:本案介绍目标跟踪中出现的问题,一步步引出DeepSORT设计的复杂逻辑,由问题出发,以解决问题的方式,观察DeepSORT的步骤。 DeepSORT中的核心算法:卡尔曼滤波器与匈牙利算法,卡尔曼滤波常用于带有高斯噪声的线性运动系统,可以很好的预测运动状态。匈牙利算法可以解决二分图匹配问题,今后也可以借鉴两个算法解决实际业务问题。 DeepSORT代码结构剖析:通过UML图,分析DeepSORT代码是如何抽象、设计的,巩固面向对象编程的思想。 计数的撞线机制:介绍基于区域的撞线机制,并通过面向对象编程来实现计数器。 DeepSORT+YOLOv5的联合使用:将目标检测+目标跟踪+计数机制联合使用,构建实际应用,在主代码中可以发现,各功能模块抽象独立出去,主代码的核心代码仅两行:bboxes = detector.detect(im); counter.counting(list_bboxs); 目标跟踪仍是一个较大研究方向,DeepSORT仅是其中一种方法,要深入掌握目标跟踪还需学习其他方法。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.5-cycleGAN.html":{"url":"chapter-8/8.5-cycleGAN.html","title":"8.5 生成对抗网络——CycleGAN","keywords":"","body":"8.5 生成对抗网络——CycleGAN 简介 本小节将介绍GAN模型中有趣的模型CycleGAN。 CycleGAN是一种双向循环的GAN模型,可实现X域与Y域之间的相互转换,并且是基于unpaired data(即不需要标注,只需要收集图片)。相较于此前的pix2pix,cyclegan适用性更广,毕竟unpaired data比paired data更容易获取。 例如论文中展示的,照片与莫奈风格画之间的互相转换,斑马与马之间的转换,夏天与冬天之间的转换。 本节先介绍GAN与CycleGAN的结构,再通过代码详细介绍CycleGAN的训练、推理。 GAN简介 GAN(Generative Adversarial Nets,生成对抗网络)由 Ian J Goodfellow在2014发表于《Generative Adversarial Nets》,可谓是推开了生成模型的一扇大门。 GAN是一种从随机噪声生成特定分布数据的模型,例如生成人脸数据,手写体数据,自定义数据集等。 GAN当中有Generator与Discriminator两个模型,G负责学习从噪声到数据的映射,D负责充当损失函数,判断G生成得是否足够好,G和D交替训练,形成对抗,同步提升,最终使得G生成的数据越来越像人脸。 根据模型的结构,GAN模型延伸出一系列变体,如本文要介绍的CycleGAN,还有DCGAN,Conditional GANs,Pix2Pix,SRGAN等。 GAN的设计十分巧妙,从神经网络训练的角度考虑,GAN是将损失函数替换为,神经网络的输出,具体如下图所示: 传统模型训练,需要用loss_fun(output, label)得到loss值,然后求梯度优化。 在GAN中,巧妙了利用一个判别器模型,D_net, D_net(output) 趋向0, D_net(training_data)趋向1,依次获得loss值。 CycleGAN简介 CycleGAN是一种无监督学习方法,由Jun-Yan Zhu等人于2017年提出。 它的主要思想是通过两个生成器和两个判别器来实现两个不同域之间的图像转换。 与其他的GAN模型不同的是,CycleGAN不需要成对的图像进行训练,而是只需要两个域中的任意数量的图像即可。 下面介绍CycleGAN的模型结构与损失函数。 CycleGAN模型结构 CycleGAN模型由两个生成器,两个判别器构成。 生成器G,将X域图像变换到Y域 生成器F,将Y域图像变换到X域 判别器Dx,判别图像来自X则为1, 图像来自Y则为0 判别器Dy,判别图像来自X则为0, 图像来自Y则为1 生成器采用3层卷积+一系列残差块构成; 判别器采用PatchGANs,其特点在于对一张图片不是输出一个向量,而是输出NxNxC的张量,NxN分别对应原图中的70x70区域,即对一张图,划分多个70x70的patch,每个patch来判别它是0类,还是1类。 CycleGAN损失 cyclegan最大的特点在于损失函数的设计,除了GAN的常规两个损失函数之外,论文中增加了cycle consistency loss(循环一致性损失),用来避免模式坍塌,以及更好的让GAN模型生成合理的图像,同时,在官方代码中还增加了一项identity loss,用来增加GAN模型对于本域图像信息的学习。 因此,整体loss有8项,分别是'D_A', 'G_A', 'cycle_A', 'idt_A', 'D_B', 'G_B', 'cycle_B', 'idt_B' 生成器的损失 loss1 :判别器的输出接近1 对于G,目标是让对应的判别器D,认为假图像是真图像,即输入是假图像,标签是1,目标是欺骗D,G就是训练好了。 self.loss_G_A = self.criterionGAN(self.netD_A(self.fake_B), True) target_tensor = self.get_target_tensor(prediction, target_is_real) # 根据self.fake_B,生成对应的标签,即N*N的标签,为patchGAN的输出匹配 loss = self.loss(prediction, target_tensor) # MSELoss() 而非BCE loss2:F(G(x)) 与 x一致 除了常规Loss,还有cycle consistency loss(循环一致性损失),目的是经过G得到的图片,返回去再经过F,应当是可以恢复得到X域的图像x_hut,并且x与x_hat应当是逐像素一模一样的。 这样的G和F才是合理的。 self.criterionCycle = torch.nn.L1Loss() self.loss_cycle_A = self.criterionCycle(self.rec_A, self.real_A) * lambda_A # lambda为缩放系数 loss3:恒等映射损失 该损失在代码中才出现,论文中并没有提到。恒等映射损失的思想是,生成器G_A接收A域图像,生成B域图像;若接收B域图像,应该生成恒等的B域图像,即B域图像一模一样,不能变。 G_A should be identity if real_B is fed: ||G_A(B) - B|| self.idt_A = self.netG_A(self.real_B) self.loss_idt_A = self.criterionIdt(self.idt_A, self.real_B) * lambda_B * lambda_idt # self.criterionIdt = torch.nn.L1Loss() 因此,对于生成器G而言,要求它: 生成的假图像,要让判别器预测为1, D(G(x)) 逼近1; G生成的图像,再经过F生成的图像,应当等于原图,此为循环一致性损失 已经是B域的图像,经过G_A,应当得到B域的原图。 判别器的loss 判别器损失较为简单,对于真图像,需要预测为1,对于假图像,需要预测为0。 其中,假图像不是基于当前batch的,而是记录过往的一批假图像,从假图像池中抽取。 fake_B = self.fake_B_pool.query(self.fake_B) self.loss_D_A = self.backward_D_basic(self.netD_A, self.real_B, fake_B) def backward_D_basic(self, netD, real, fake): # Real pred_real = netD(real) loss_D_real = self.criterionGAN(pred_real, True) # Fake pred_fake = netD(fake.detach()) loss_D_fake = self.criterionGAN(pred_fake, False) # Combined loss and calculate gradients loss_D = (loss_D_real + loss_D_fake) * 0.5 loss_D.backward() return loss_D 训练注意事项 论文中超参数:batch size =1;epoch =200;lr:前100epoch,固定0.0002,后100epoch,线性下降至0 其它注意事项: 两个域图像是否有一致性: 举个例子: 苹果 橘子: 都是球形, OK! 苹果 香蕉: Mode Collapse! 训练CycleGAN要有耐心 学习率别太高 对抗损失权重不要太高,循环一致性损失权重为1的时候,对抗损失一般设置为0.1 判别器优化频率高于生成器 使用最小二乘损失(MSE) cycleGAN的loss不能准确反应训练的好坏,不代表着训练进度,甚至不能代表结果优劣。所以还是要输出样张看效果,或许可以借鉴WGAN的思想 由于 minimax 优化的性质,许多 GAN 损失不会收敛(例外:WGAN、WGAN-GP 等)。对于 DCGAN 和 LSGAN 目标,G 和 D 损失上下波动是很正常的。只要不爆炸应该没问题。 CycleGAN代码实现 接下来,通过pytorch训练一个可以将图片转换为莫奈风格图像的CycleGAN,github已经19.5K star了,可见深受大家喜爱。 数据集准备 由于不需要标签,仅需要准备图像,所以在根目录下,存放trainA, trainB, testA, testB即可,分别存放A域的图像,B域的图像。 这里下载官方提供的monet2photo数据集,可以通过sh脚本下载,也可以手动下载(推荐) # 方法一:bash bash ./datasets/download_cyclegan_dataset.sh monet2photo # 方法二:手动下载 # apple2orange, summer2winter_yosemite, horse2zebra, monet2photo, cezanne2photo, ukiyoe2photo, vangogh2photo http://efrosgans.eecs.berkeley.edu/cyclegan/datasets/$FILE.zip # 例如莫奈数据下载 http://efrosgans.eecs.berkeley.edu/cyclegan/datasets/monet2photo.zip 数据加载 整个数据模块代码设计如下图所示: 该项目适配pix2pix, cyclegan,因此提供了多种dataset,所有的dataset都继承于BaseDataset,针对cyclegan的是unaligned_dataset.py中的UnalignedDataset。 对于dataloader,提供了一个类 CustomDatasetDataLoader,并且实现了迭代协议iter,因此\"dataloader\"是自定义的一个可迭代对象。 在主代码01_train.py中,通过33行代码:dataloader = create_dataset(opt) ,实现dataloader的创建,所有的配置信息存放在opt中。 接下来关注UnalignedDataset,它内部实现了transform,transform由opt的参数决定 第一步:缩放变换,有resize,或者基于width缩放的方式;默认基于resize_and_crop。 resize的尺寸是opt.load_size 第二步:crop的尺寸是 opt.crop_size 第三步:Normalize 这份代码中有一个值得借鉴的是,通过参数配置,来选择调用具体的类。实现方法是通过,importlib.import_module实现通过字符串形式import工具库。 def find_dataset_using_name(dataset_name): \"\"\"Import the module \"data/[dataset_name]_dataset.py\". In the file, the class called DatasetNameDataset() will be instantiated. It has to be a subclass of BaseDataset, and it is case-insensitive. \"\"\" dataset_filename = \"data.\" + dataset_name + \"_dataset\" datasetlib = importlib.import_module(dataset_filename) # 这里的dataetlib,等同于一个库,例如。import cv2的cv2, import torch的torch dataset = None target_dataset_name = dataset_name.replace('_', '') + 'dataset' for name, cls in datasetlib.__dict__.items(): if name.lower() == target_dataset_name.lower() \\ and issubclass(cls, BaseDataset): dataset = cls if dataset is None: raise NotImplementedError(\"In %s.py, there should be a subclass of BaseDataset with class name that matches %s in lowercase.\" % (dataset_filename, target_dataset_name)) return dataset 模型构建 源代码中将模型(nn.Module), 损失函数,优化器一并放到了CycleGANModel类当中,对外提供set_input()与optimize_parameters(),实现前向传播、损失计算、反向传播,这样可以让主代码更简洁。 模型部分代码设计如下图所示 对于 netG_A/B,cyclegan中是resnetblock构成的生成器,详细可见models/networks.py的ResnetGenerator类, 主要由resnetblock的下采样和TransConv的上采样构成,最后加入tanh()激活函数。 对于netD_A/B, 是一个patchGAN,全部由卷积层构成的全卷积网络,详见 models/networks.py的NLayerDiscriminator。 整个模型构建与优化核心代码如下: model = create_model(opt) # create a model given opt.model and other options model.setup(opt) # regular setup: load and print networks; create schedulers ---------------------------------- model.set_input(data) # unpack data from dataset and apply preprocessing model.optimize_parameters() # calculate loss functions, get gradients, update network weights 模型训练 数据、模型、损失函数与优化器准备完毕,可以进行迭代训练。 如果在windows下训练,需要开启 visdom python -m visdom.server 由于此处使用了visdom进行可视化,需要先行开启visdom,否则会报错: requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8097): Max retries exceeded with url: /env/main (Caused by NewConnectionError(': Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接。')) [WinError 10061] 由于目标计算机积极拒绝,无法连接。 训练指令: python 01_train.py --n_epochs 200 --dataroot path/to/your/datasets/monet2photo --name monet2photo_cyclegan --model cycle_gan 日志信息、模型信息将存储于checkpoints\\monet2photo_cyclegan中 训练结果 cycleGAN的loss不能准确反应训练的好坏,不代表着训练进度,甚至不能代表结果优劣,整体趋势是,cycle loss逐渐下降,如下图所示 推理测试 预训练模型可以从这里下载:链接:https://pan.baidu.com/s/1bEPNBbAeqMumpM2pqKwb4w 提取码:q159 python 02_inference.py --dataroot G:\\deep_learning_data\\cyclegan\\monet2photo\\testB --name monet2photo_cyclegan --model test --no_dropout --model_suffix _B180 model_suffix 格式说明:模型模型文件名保存为latest_net_G_A.pth、latest_net_G_B.pth。在代码中会自动拼接: \"latest_net_G{}.pth\".format(model_suffix) 最后在F:\\pytorch-tutorial-2nd\\code\\chapter-8\\cyclegan\\results\\monet2photo_cyclegan\\test_latest下就有对应的结果图片 这里展示了120, 180, 200个epoch时的展示效果,感觉180时的效果最好。 小结 本小结先介绍了GAN与cycleGAN的模型结构,GAN是一个巧妙的利用神经网络进行损失计算的设计,CycleGAN是巧妙的利用了两个GAN相互转换,并提出循环一致性loss,最终CycleGAN的损失共8个,分别是'D_A', 'G_A', 'cycle_A', 'idt_A', 'D_B', 'G_B', 'cycle_B', 'idt_B'。 然后介绍CycleGAN源代码使用及设计,其将Dataset, DataLoader, model, loss, optim进行了高度封装,使主代码很简洁。 从此可见,无论多复杂、难理解的pytorch模型训练代码,都离不开Dataset, DataLoader, nn.Module,loss, optim,只要了解训练的步骤,这些复杂的代码都可以梳理出来。 自2014年GAN提出以来,往后的5年间提出了各式各样的GAN变体,也有了非常多有趣的应用,感兴趣的朋友可以进一步了解。 2020年之前,在图像生成领域,GAN是当之无愧的主流,但2020年《Denoising Diffusion Probabilistic Models》(Diffusion)提出后,基于扩散模型(diffusion model)的图像生成称为了学术界的宠儿,包括OpenAI提出的DALL-E系列,stability.ai提出的Stable-Diffusion。 下一节将介绍扩散模型(diffusion model)及代码实现 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.6-diffusion-model.html":{"url":"chapter-8/8.6-diffusion-model.html","title":"8.6 扩散模型——DDPM","keywords":"","body":"8.6 Diffusion Model——DDPM 前言 2020年,DDPM横空出世,将扩散模型概念迅速扩散到深度学习各个领域,并在2022年随着Stable Diffusion的提出及开源,该技术实现了破圈,走入了大众的视野,而不再是科技工作者才了解的概念。 为此,对扩散模型原理进行简要介绍,并通过代码实现DDPM模型,同时介绍Stable Diffusion 模型背后的原理。 本文主要内容包括 Diffusion Model 概念介绍 DDPM 模型原理及代码实现,训练、推理 Guided Diffusion:引导条件的扩散模型介绍,包括classifier-base 和 classifier-free 两大主流模型 Stable Diffusion:让技术出圈的模型 Latent Diffusion Model(LDM):Stable Diffusion背后的核心技术 Diffusion Model 简介 扩散模型(Diffusion Model)发展至今已成为一个大的概念、思想,扩散是借鉴物理学中的扩散过程(Diffusion Process)概念。 物理学中扩散是一种物质分子在非均匀环境中的运动,物质从高浓度区域向低浓度区域传输,最终实现浓度均衡。 在深度学习中,则是将噪声加入到原始图像中进行扩散,最终使图片变为噪声,然后利用深度学习模型学习从噪声变到图像的过程,最后可以随机生成噪声,并利用模型将噪声生成图片的过程。 深度学习中扩散的概念自2015就有了,并在2019年发表于论文《Generative Modeling by Estimating Gradients of the Data Distribution》, 最终在2020年的《Denoising Diffusion Probabilistic Models》中被大众熟知,随后就开启了扩散模型的学术界扩散,如DALL-E 2,imagen, Stable Diffusion等强大应用。 DDPM 实现噪声到图片步骤 此处借鉴李宏毅教授2023年春季期ML课程中课件进行讲解。 DDPM模型推理过程是将一个标准正态分布中采样的噪声图片(与原图同尺寸),经过T步(1000步)的去噪(Denoising),生成高质量图像的过程。 DDPM模型推理过程,可以看似将噪声逐步的去除,先获得图像大体轮廓,逐步精雕细琢,获得清晰的图像。 这就像雕像制作过程,工匠常说:“雕像本身就在石头里,我只是把多余的部分剔除掉”,雕刻雕像的过程就像噪声变到高质量图像的过程,一开始它们都是可以生成“万物”的本源。 如何对Denoise模块进行数学建模,使得噪声逐步变清晰? 可以这么做,设计一个神经网络,它接收噪声图以及当前步数,输出一个噪声,然后与原图相减,获得更清晰的图片。 如何训练这样的神经网络模型?训练数据如何构建? 前面提到噪声如何变图像,现在反过来看看,图片如何变噪声的过程。 对于原图,经过T步的逐渐加高斯噪声,使图像逐步模糊,最终趋近于标准高斯分布。 这其中就可以构建Noise Predicter的训练数据,例如蓝色框中为输入,红色框噪声则是反向过程时期望预测的标签。 对于具体模型,DDPM中采用了Unet架构的神经网络实现数据预测。 到这里,DDPM实现从噪声生成图像的步骤就清晰了: 前向过程:将原图逐步添加噪声, 共1000步 反向过程:利用神经网络学习加噪图像到噪声的变换,使得模型可以去噪 推理使用:随机采样,得到高斯噪声,然后逐步去噪,经过1000步去噪,得到清晰图像。 DDPM 公式理解 根据上述步骤,可以将DDPM训练、推理过程采用数学形式表达,如下图所示 训练过程: q(x0) 表示原始图像数据集(分布),x0表示一张原始图像 t 看成是1到1000的均匀分布采样 ε 表示从标准正态分布中采样得到的噪声图像 εθ 表示模型需要学习到的噪声图像,该图像是利用unet生成的,unet接收上一步去噪图与当前步数t,预测出一个噪声图像,并且期望它与高斯噪声越接近越好。即ε - εθ 趋于0。 αt_bar:均值系数,可由重参数方法训练而来,或是固定值。固定值如0.0001 到0.02线性插值。 推理过程: xT:从正态分布中随机采样的噪声 z:从正态分布中随机采样的噪声 xt-1:主要是由Xt减去模型生成的噪声图像,并且以一定的权重加权后,加上标准差乘以随机噪声。至于原因需要看原文及进行公式推导理解了 更多公式推导,推荐阅读 What are Diffusion Models?) 由浅入深了解Diffusion Model DDPM 模型结构 下面通过代码进行DDPM模型结构的剖析,官方代码为TF版,在这里采用非官方的PyTorch版。 论文采用TPU v3-8(相当于8张V100 GPU),在cifar10上花了10.6小时,由此可见,要想在256x256的图片上训练,会非常耗时。 为了快速使用DDPM,这里采用cifar10进行学习。 通过代码分析,DDPM模型结构如下图所示,是在unet结构上进行了一些改进,包括加入时间步t的embedding,卷积中采用了ResBlock,并且采用了Self-Attention机制。 如图例所示,模型整体有7个组件,彩色箭头是一组操作,包含2-3个网络层的堆叠,通常最后一个网络层才会改变图像分辨率。 第一个,时间步的embedding,它会输入到除了head,tail的其它网络层当中,并且是add的形式添加的(h += self.temb_proj(temb)[:, :, None, None]) 第二个,head模块,是一个3x3卷积,主要是为了改变通道,没有特殊的地方。 第三个,down block,是下采样的核心,一个block由2个ResBlock与一个下采样层构成。ResBlock内部如图所示: 第四个,middle block,由两个ResBlock构成 self.middleblocks = nn.ModuleList([ ResBlock(now_ch, now_ch, tdim, dropout, attn=True), ResBlock(now_ch, now_ch, tdim, dropout, attn=False),]) 第五个,Up block,由3个ResBlock+1个上采样层, 第六个,tail,由GN + swish + conv构成,输出最终图像 self.tail = nn.Sequential( nn.GroupNorm(32, now_ch), Swish(), nn.Conv2d(now_ch, 3, 3, stride=1, padding=1)) 第七个,concat,是unet的low-level特征融合到high-level特征当中 总的来说,特色在于时间步t的embedding是加入了每一个ResBlock中进行使用,并且ResBlock采用了self-attention机制。 DDPM——训练Cifar-10 接下来使用配套代码中的Main.py进行训练,并且使用Main.py进行推理,训练和推理需要调整\"state\": \"eval\"。 数据准备:在Main.py同级目录下创建cifar文件夹,并且将cifar-10-python.tar.gz放到里边。 运行训练:python Main.py,1080ti上训练,约16小时,训练完毕,在Checkpoints文件下有ckpt_last_.pt。 下面观察cifar-10的训练细节,在配套代码中可以看到,数据采用torchvision提供的cifar10 dataset接口,模型为Unet,优化器为AdamW,学习率为warmup+consine。 在主循环中,只用了images,labels是没有使用到的。 模型的迭代,封装在了GaussianDiffusionTrainer类的forward函数,这也是核心代码之一,下面详细看看forward函数。 第5行:进行时间步的采样,即为每一个样本配一个时间步,并不需要为每个样本采样1000个时间步进行训练,这是因为公式推导的时候,xt可以用x0直接表示的,不需要依赖xt-1。 第6行:对需要加入的噪声进行采样,这里为标准正态分布 第11行:根据公式计算得到x_t,x_t由x0与noise加权得到,细节可看公式理解部分,这里的两个权重之和不等于一,但是接近1。 第14行:模型接收x_t与t,去预测噪声图像,并且通过mse_loss进行损失计算。 def forward(self, x_0): \"\"\" Algorithm 1. \"\"\" t = torch.randint(self.T, size=(x_0.shape[0], ), device=x_0.device) # 不需要1000步都训练,随机batchsize个 noise = torch.randn_like(x_0) # 标准正态分布 # 基于x0,获得xt, 随后得到训练数据[(xt, t, noise), ] # x_t.shape [bs, 3, 32, 32] # noise.shape [bs, 3, 32, 32] # t.shape (bs,) x_t = (extract(self.sqrt_alphas_bar, t, x_0.shape) * x_0 + extract(self.sqrt_one_minus_alphas_bar, t, x_0.shape) * noise) loss = F.mse_loss(self.model(x_t, t), noise, reduction='none') return loss 代码与上文的原理介绍是一致的,时间步step2与加噪后的图像是输入数据,标签ground truth是noise。 DDPM——推理Cifar-10 训练完毕后,先看看推理效果。 首先在Main.py中修改 \"state\": \"eval\", # train or eval,然后运行python Main.py,即可在\"sampled_dir\": \"./SampledImgs/ 文件夹下获得如下图片,看上去还像个样子,毕竟数据量、算力、时间摆在这里。 这里提供一个训练好的模型参数,ckptlast.pt,下载后放到Checkpoints文件夹。链接:https://pan.baidu.com/s/17X_L9oH4lmrGwnD-V9D5HQ 提取码:w4ki 推理过程的代码理解相对有一点绕,主要还是参照论文中的sampling过程,如红框所示,首先获得均值(为什么叫均值?可能要去推一下公式了),然后加上时间步对应的标准差乘以随机噪声。 其中,均值主要是由Xt减去模型生成的噪声图像,并且以一定的权重加权后得到。 核心代码涉及3个函数, forward()为主函数。 p_mean_variance()为调用Unet模型,获得mean和var。 predict_xt_prev_mean_from_eps()是进行上图中红色框运算的过程。 第26行,获得模型预测的噪声图片 第27行,获得mean,即上图中的红色框 第15行,加上标准差乘以随机噪声,获得t时刻的输出,反复迭代1000次,得到最终输出图像。 def forward(self, x_T): \"\"\" Algorithm 2. \"\"\" x_t = x_T for time_step in reversed(range(self.T)): print(time_step) t = x_t.new_ones([x_T.shape[0], ], dtype=torch.long) * time_step mean, var = self.p_mean_variance(x_t=x_t, t=t) # mean是 xt图 减去 噪声图 # no noise when t == 0 if time_step > 0: noise = torch.randn_like(x_t) else: noise = 0 x_t = mean + torch.sqrt(var) * noise assert torch.isnan(x_t).int().sum() == 0, \"nan in tensor.\" x_0 = x_t return torch.clip(x_0, -1, 1) def p_mean_variance(self, x_t, t): # below: only log_variance is used in the KL computations # posterior_var: 由 betas计算得到,betas=[0.0001 to 0.02] var = torch.cat([self.posterior_var[1:2], self.betas[1:]]) # betas=[0.0001 to 0.02] var = extract(var, t, x_t.shape) eps = self.model(x_t, t) # eps是unet输出的图像 xt_prev_mean = self.predict_xt_prev_mean_from_eps(x_t, t, eps=eps) # 加权减法,xt图 减去 噪声图 return xt_prev_mean, var def predict_xt_prev_mean_from_eps(self, x_t, t, eps): assert x_t.shape == eps.shape return ( extract(self.coeff1, t, x_t.shape) * x_t - extract(self.coeff2, t, x_t.shape) * eps ) Diffusion Model 拓展 —— Guided Diffusion guided diffusion是加入了引导信息,让生成的图像变为我们想要的形式,而不是随机的图片。 引导式的扩散模型从有无分类器,可以分为两种,classifier-base和classifier-free,classifier-free由于不需要分类器,引导信息直接embedding到模型中,所以应用更为广泛。 classifier-base ——《Diffusion Models Beat GANs on Image Synthesis》 DDPM提出后,其实效果并未惊艳大家,在DDPM发表后的几个月,《Diffusion Models Beat GANs on Image Synthesis》的发表, 其github,把扩散模型带入了高潮,因为它效果比GAN更好,并且针对DDPM,引入了classifier-guidance思想,可以在生成时加入条件约束,可控制生成特定类别的图像。 具体公式详见原文。在使用时,采用unet估计mean时,需要额外加上分类器的分类结果的梯度,详见openai的github:https://github.com/openai/guided-diffusion 第4行:均值除了unet的,还需要加入分类器得到的梯度 第7行:分类器推理,计算梯度过程,这里有个重要参数是args.classifier_scale # guided_diffusion/gaussian_diffusion.py def condition_mean(self, cond_fn, p_mean_var, x, t, model_kwargs=None): gradient = cond_fn(x, self._scale_timesteps(t), **model_kwargs) new_mean = (p_mean_var[\"mean\"].float() + p_mean_var[\"variance\"] * gradient.float()) return new_mean # scripts/classifier_sample.py def cond_fn(x, t, y=None): assert y is not None with th.enable_grad(): x_in = x.detach().requires_grad_(True) logits = classifier(x_in, t) log_probs = F.log_softmax(logits, dim=-1) selected = log_probs[range(len(logits)), y.view(-1)] return th.autograd.grad(selected.sum(), x_in)[0] * args.classifier_scale classifier-free —— 《classifier free diffusion guidance》 由于classifier-base需要训练分类器,并且在推理时有超参数args.classifier_scale的影响,以及引导条件的加入过于单一,没有办法通用性的加入各类条件。 为此,谷歌大脑的两位工程师提出了classifier free的方式,文中将分类信息通过embedding的方式加入到模型中训练,这里类似时间步t的embedding。 训练时会结合有条件与无条件进行训练,无条件则将分类标签embedding全部设置为0,具体细节可参见论文。 由于论文中没有提供代码,所以找到的代码是这个DDPM,其中的condition模式就是classifier-free。 第2行:训练时,有10%的是无条件的,90%是有条件的 第9行:标签信息与时间步一样,通过embedding汇入模型中,称为引导信息。 # DiffusionFreeGuidence/TrainCondition.py if np.random.rand() classifier free diffusion是打开了一扇大门,既然类别标签可以embedding,那么文本信息也可以通过该方式注入模型中进行引导,火爆的Stable Diffusion就是这么做的。 Diffusion Model 拓展 —— Stable Diffusion Stable Diffusion 是2022年火爆全球的文图生成(text-to-image)扩散模型,由于它开源,并且效果相当炸裂,因此已经被大多数人使用。 Stable Diffusion 背后的技术是LDM(latent diffusion model),之所以叫Stable Diffusion,或许与其背后的公司由Stability AI有关。 Stable Diffusion 是由CompVis、Stability AI和LAION三家公司共同创建,CompVis提供的技术LDM(latent diffusion model)源自论文《High-Resolution Image Synthesis with Latent Diffusion Models》,对应的github。LAION公司是一家致力于推动人工智能和数据科学发展的科技公司,其从互联网上抓取的 58 亿「图像-文本」数据,并开源了 LAION-5B数据集。而Stability AI的贡献,或许是出钱出力出人吧。 Stable Diffusion 的开源代码: https://github.com/CompVis/stable-diffusion 与 LDM(latent diffusion model)的开源代码:https://github.com/CompVis/latent-diffusion都在CompVis下,代码几乎一样。 下面简要介绍Stable Diffusion用到的latent diffusion model技术。 LDM之前,扩散模型在像素域进行扩散与去噪,这样的计算量过大。因此,考虑将扩散过程放到隐空间(latent space),即将数据经过encoder,来到特征空间,在特征空间上进行扩散和去噪。 这样一来,有以下好处: 计算量减小,训练和推理速度变快 可以加入更多引导信息,例如文本信息。 LDM论文中有一幅图很好的解释了LDM的思想:首先在pixel space,需要有encoder和decoder,在latent space采用了多头注意力机制,并且除了时间步信息,加入了conditioning模块,其中的引导信息可以是文本、图片、表征向量等等一切内容,然后为引导信息配一个embedding模块,就可以将引导信息加入模型中。 这里配上李宏毅老师的结构示意图,可知道LDM的核心在于2当中,处理的不再是像素空间,而是一个特征空间 stable diffusion 的使用与安装,网上有太多教程,这里不进行介绍,主要了解LDM的架构。推荐阅读:文生图模型之Stable Diffusion 与Stable Diffusion同一时期,叫得上名称的文图生成模型还有Midjourney、DALL-E 2,不过它们都是不开源的。 小结 本案例借助DDPM的代码剖析,了解扩散模型实现去噪,从而生成图像的过程和原理,并且对Guided Diffusion Model进行介绍,模型要能根据我们的“指示”生成特定的图像,这样的模型才有更大的应用价值。 在Guided Diffusion Model中,包含classifier-base 和 classifier-free,classifier-free是后来的主流。 classifier-free的代表即出圈的Stable Diffusion,Stable Diffusion是完全开源的,因此得到了全球的使用与关注。 在扩散模型中,LDM(latent diffusion model)思想值得仔细研究,它将一切信息都放到隐空间(特征空间)进行处理,使得图片处理起来更小,还可以进行多模态处理。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.7-image-captioning.html":{"url":"chapter-8/8.7-image-captioning.html","title":"8.7 图像描述——Image Captioning","keywords":"","body":"8.7 Image Captioning 图像描述 前言 图像描述是CV与NLP结合的一个典型任务,也是CV与NLP桥梁,本节将介绍图像描述模型的训练及使用,包括经典的CNN+RNN,以及现在流行的多模态模型。 本节内容将包括: 图像描述的概念,发展历史,常用数据集,BLUE评价指标 基于CNN+RNN+attention机制的图像描述模型训练 基于Clip+GPT2的图像描述模型训练 图像描述简介 Image Captioning (图像描述)是对图像采用文字进行描述的过程,Image Captioning又称图像图像字幕生成、图像标注、图像说明等,目前应用在医疗诊断、智能客服、人机交互等领域。 图像描述是一个交叉领域,将图片的视觉特征和自然语言处理结合起来,实现自动化的图片描述。 2014年之前,主流方法是采用数字图像处理进行特征提取,然后通过对特征的描述,实现image captioning。 2014年,Google发表了《Show and Tell: Lessons Learned from the 2015 MSCOCO Image Captioning Challenge?,首次采用CNN+RNN的形式进行端到端的深度学习模型训练,并且获得2015 COCO 图像描述的冠军。 2015年2月,Bengio领衔的团队针对Show and Tell改进,加入了attention机制,发表了《Show, Attend and Tell》, 该方法在文本生成模块,加入注意力机制,挖掘单词与图像区域的关联关系。 2019年,得益于Transformer广泛应用,图像描述开启了Transformer一统天下的时代,先后有Attention on Attention for Image Captioning、Image Captioning: Transforming Objects into Words和Entangled Transformer for Image Captioning等论文采用了Transformer进行图像描述,简要看了论文,发现模型结构图略显负责,不够优雅,这里就不贴图了,因为后续的超大预训练模型将会开启图像描述新范式。 2021年,随着图文、文本任务的预训练模型(pre-training model)的成功,学者迅速将其应用于图像描述,2021年有ClipCap,2022年BLIP, 2023年1月BLIPv2,目前BLIP系列已经达到较好效果,也将是本案例的重点。 简单来说,图像描述主流方法的发展,先经过CNN+RNN,及其变体,再到Transformer,再到VLP(visual language pre-training)模式,随着ChatGPT等预训练模型的成功,多模态任务也将有更多应用。 图像描述数据集 图像描述常用的数据集有Flickr8K、Flickr30K、Conceptual Captions (CC),COCO2014,数据集中语料库的高频词云如下图所示: 图片来源:《2021-07-From Show to Tell A Survey on Deep Learning-Based Image Captioning》 本案例将采用COCO2014,该数据集train有82783张,val有40504张,test有40775张,每张图片对应有5~7句的caption。为了线下比较模型的性能,会把train和val经过karpathy分割后,train变成113287张,val变成5000张,test变成5000张,而在线测试的test不变,仍为40775张。 标注规则为: 描述这个场景的所有重要部分; 不描述不重要的细节。 不要描述在未来或过去可能发生的事情。 不描述一个人可能会说什么。 不提供专有的人名。 这些句子应该至少包含8个单词。 更多数据集介绍可参考Image Caption 2021最新整理:数据集 / 文献 / 代码 图像描述评价指标 图像描述的评价,可以参考机器翻译的评价,都是比较两个句子之间的相似度。 机器翻译中常用的评价指标有,BLEU1-4, METEOR, ROUGE-L, and CIDEr等,这里介绍最常见的BLEU1-4。 BLEU是IBM在2002年提出的,用于机器翻译任务的评价,发表在ACL,引用次数10000+,原文题目是“BLEU: a Method for Automatic Evaluation of Machine Translation”。 它的总体思想就是准确率,假如给定标准译文reference,模型生成的句子是candidate,句子长度为n,candidate中有m个单词出现在reference,m/n就是bleu的1-gram的计算公式。 当统计不再是一个单词,而是连续的N个单词时,就有了n-gram的概念,词组的概念称为n-gram,词组长度通常选择1, 2, 3, 4 举一个例子来看看实际的计算: candinate: the cat sat on the mat reference: the cat is on the mat BLEU-1: 5/6 = 0.83 BLEU-2: 3/5 = 0.6 BLEU-3: 1/4 = 0.25 BLEU-4: 0/3 = 0 分子表示candidate中预测到了的词组的次数,如BLEU-1中,5分别表示, the, cat, on, the, mat预测中了。BLEU-2中,3分别表示, the cat, on the, the mat预测中了。以此类推。 针对BLEU还有些改进计算方法,可参考BLEU详解 BLEU的优点在于它考虑的是n-gram级别的匹配,而不是词级别的匹配,因此可以考虑更长的匹配信息,从而更好地评估翻译的质量。 但是,BLEU的缺点在于无论哪种n-gram被匹配上了,都会被同等对待,这可能会导致一些问题。例如,动词的匹配在翻译中可能比冠词更重要,但是在BLEU中,它们被同等地看待,这可能不太合适。 CNN+RNN 代码实现 接下来采用CNN+RNN结构,并配合attention机制,实现图像描述模型训练, 在coco2014上可实现23.1的BlEU-4 。 代码来自github,论文可参考《Show, Attend and Tel》 数据采用github上推荐的下载链接,coco2014,数据集划分采用 Andrej Karpathy划分好的json文件。 先看效果图,这是一张测试图片,模型可以输出 a brown teddy bear sitting on top of a pair of shoes,能对图中的泰迪、鞋子进行表述。 数据模块 数据下载与转换 首先下载数据,并转换数据为pytorch的dataset读取的形式 下载图像数据,val2014, train2014 文件夹,并将其放置到xxx/coco2014/images下 下载标注数据,caption_datasets.zip,其中包含coco, flickr8k, flick30k的标签,这里只使用dataset_coco.json 在配套代码00_create_input_files.py中设置以下路径,运行后获得相应的数据 create_input_files(dataset='coco', karpathy_json_path=r'G:\\deep_learning_data\\coco_2014\\image-caption-json\\dataset_coco.json', image_folder=r'G:\\deep_learning_data\\coco_2014\\images', captions_per_image=5, min_word_freq=5, output_folder=r'G:\\deep_learning_data\\coco_2014\\dataset-created', max_len=50) 获得的数据是经过预处理转换的,下面介绍对数据是如何处理的。 数据预处理 文本数据需要进行一系列的预处理,例如,将一张图片对应的5句不等长度的描述,整理为可以batch输入数据,这里涉及一些NLP常见的操作,下面通过代码进行剖析。 对描述的开头和结尾,加入起始、停止词, a man holds a football 将句子填充至等长,如100个词, a man holds a football .... 创建词映射,将词映射为编号, 如 [9488, 1, 20, 64, 3, 60, 57, 69, 35, 66, 14, 67, 17, 1, 68, 9489, 0,.., 0],其中9488和9489,0分别表示 上述信息,通过00_create_input_files.py获得,数据处理后,分别获得: HDF5文件,包含了所有图片,数据形状为 N, 3, 256, 256,但hdf5在pytorch的dataloader中无法启用多进程,因此本案例改用保存图片路径的方式,在dataset中再读取图片 CATIONS*.json,包含每个描述句子添加起始词、填充、映射后的索引,为 N_c * I 形式, N_c表示所有描述句子的梳理,I表示图像的数量。由于coco固定了一张图片有5个描述,因此N_c == 5. CAPLENS*.json,包含每句描述的长度,N_c * I , N_c表示所有描述句子的梳理,I表示图像的数量。 WORDMAP*.json,所以一个字典,包含了单词到索引的映射关系。 xxx_paths.pkl:包含每张图片的路径,用于在dataset中进行图片读取 原代码采用HDF5进行图片读取,这样无法采用num_worker>1,因此在这里我将代码改为基于图片路径形式进行读取,可以充分利用cpu加载数据。详细内容参见dataset的编写。 模型模块 模型部分主要有encoder, decoder, attention三个模块。 encoder为resnet101将图片变为14x14x2048的特征图,并且经linear层变换到向量形式,便于与文本特征拼接 attention模块由多个linear层构成,注意力权重最终经sigmoid函数得到0-1区间的注意力权重,并且是1x196的向量,对应着14x14的图像区域。 decoder为标准的LSTM,其输入由词嵌入向量1x512 + attention的特征1x2048构成 output模块采用LSTM的hiddent feature,经过linear层输出1x9490的分类概率向量,9490表示单词库中总共有9490个单词。 模型结构如下图所示,本图对github原图进行了详细补充,对每一个数据维度及流向进行了标记: 训练与推理 在配套代码01_train.py代码中仅需要配置数据所在文件夹data_folder,执行 python 01_train.py即可。 在epoch=14左右会得到最优BLEU-4, 22.7。 关于超参数,num_worker可以设置大于1,batchsize设为了256,采用的是1080ti 11GB,显存占用8G+,耗时大约1.5小时一个epoch。 训练代码比较常规,只是文本任务在数据处理上有一个比较特殊的操作就是组batch时,先对文本长度进行排序,然后依次取batch送入LSTM。组batch的操作,具体如github所示: 推理观察 训练到14个epoch时可以将模型拿来试试了,将 BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth.tar的路径配置到02_inference.py args.img目前支持图片以及文件夹形式的推理 args.model 是ckpt的路径 args.word_map是单词库,模型预测出来的9490个类别需要对应到具体的单词,用的就是这个字典。 out_dir是输出图片的文件夹 args.img = r'G:\\deep_learning_data\\coco_2014\\images\\val2014' #img path or dir args.model = 'BEST_checkpoint_coco_5_cap_per_img_5_min_word_freq.pth.tar' # model checkpoint args.word_map = r'G:\\deep_learning_data\\coco_2014\\dataset-created\\WORDMAP_coco_5_cap_per_img_5_min_word_freq.json' out_dir = './output_img' 效果如下图所示 训练好的模型权重下载:链接:https://pan.baidu.com/s/1fLS0_EPqfj0x_PX3JLN1Eg 提取码:ta3v 到这里,快速地实现了一个效果还不错的图像描述模型,里边有一些新知识值得学习: 图像可经过模型提取特征,变为特征向量与文本特征向量融合,实现图文多模态的处理 LSTM训练时,将句子长度排序,便可获得batch size依次递减的训练样本 coco数据训练时,一个样本为 (一张图片,一个句描述,句子长度),因此共5x113287=566435个训练样本 随着Transformer不断的落地应用,以及多模态模型langueage-visual模型的成功,基Transformer体系的图像描述模型成为主流。 下面介绍一款”亲民“的模型,CLIPCap,亲民指它在1080上1天就可以训练,并且仍旧使用了强大的Transformer模型,论文idea值得学习。 CLIPCap 代码实现 接下来,借助强大的多模态模型的特征提取能力实现图像描述。 这里采用CLIP对图像的理解能力,获取图像编码特征embedding向量,再经过一个生成器模型,实现图像描述。这个工作就是2021年11月发表的ClipCap。 ClipCap提出一种轻量化的方法,可以结合 CLIP的image encoder 和 GPT-2 ,实现图像描述。 ClipCap有三个部分,分别是image encoder, mapping network, gpt2。其中image encoder和gpt2都是在超大规模数据集上预训练过的,可以直接用。 在学习ClipCap前,先来了解什么是CLIP,什么是GPT2。 CLIP简介 CLIP(Contrastive Language-Image Pre-training),基于对比学习的文图预训练模型,该模型可实现zero-shot的图像分类、检测等下游任务,也可以作为图像检索、图像生成、图像描述任务的backbone,是图文多模态模型领域中划时代意义的一个作品。 CLIP于2021年2月由openAI发表,并开源了模型,模型由4亿的图文数据,采用对比学习方式进行训练得到,由于对比学习与超大规模的数据集加持,使CLIP模型很好的理解了自然图像,在众多数据集上表现出了优异的zero-shot性能,同时在表征学习(representation learning)中也很好。 CLIP模型由text encoder和image encoder组成,分别对文本和图像进行特征提取,获得特征向量,随后进行对比学习,即图像1与文本1是一对数据,I1向量要与T1越接近越好,I1与其它的T向量越不接近越好,对于一个batch的数据来说,可以构成一个方阵,对角线上是正样本,非对角线是负样本。 训练伪代码如下: # 分别提取图像特征和文本特征 I_f = image_encoder(I) #[n, d_i] T_f = text_encoder(T) #[n, d_t] # 对两个特征进行线性投射,得到相同维度的特征,并进行l2归一化 I_e = l2_normalize(np.dot(I_f, W_i), axis=1) T_e = l2_normalize(np.dot(T_f, W_t), axis=1) # 计算缩放的余弦相似度:[n, n] logits = np.dot(I_e, T_e.T) * np.exp(t) # 对称的对比学习损失:等价于N个类别的cross_entropy_loss labels = np.arange(n) # 对角线元素的labels loss_i = cross_entropy_loss(logits, labels, axis=0) loss_t = cross_entropy_loss(logits, labels, axis=1) loss = (loss_i + loss_t)/2 text encoder采用的是标准的text transformer。 image encoder则有多个模型,主要是ResNet系列,包含5个不同大小的模型:ResNet50,ResNet101,RN50x4,RN50x16和RNx64(后面三个模型是按照EfficientNet缩放规则对ResNet分别增大4x,16x和64x得到),ViT系列,3个不同大小的模型:ViT-B/32,ViT-B/16和ViT-L/14。所有的模型都训练32个epochs,采用AdamW优化器,而且训练过程采用了一个较大的batch size:32768。由于数据量较大,最大的ResNet模型RN50x64需要在592个V100卡上训练18天,而最大ViT模型ViT-L/14需要在256张V100卡上训练12天,都需要几千个V100天。 模型训练好之后,神奇之处在于其可以zero-shot的进行图像分类,这个方式很具有创新性。具体步骤是 人为设定一批候选类别的文本描述,例如:A photo of {label}, 然后label分别填入候选类别的单词,假设有N个类别,则得到N个句子 送入text encoder,得到N个文本特征向量 图像送入image encoder,得到图像特征向量 图像特征向量与N个文本特征向量进行比较,找到最近的那个特征向量,即可得到类别输出 使用CLIP进行zero-shot分类,另外一个比较重要的地方是文本描述的生成,上面的例子我们采用A photo of {label},但其实也有其它选择,比如我们直接用类别标签,这其实属于最近NLP领域比较火的一个研究:prompt learning或者prompt engineering,具体可以见这篇综述论文:Pre-train, Prompt, and Predict: A Systematic Survey of Prompting Methods in Natural Language Processing,这里就不再进行阐述。 感兴趣可参考官方的ImageNet分类的Prompt engineering,采用80个不同的prompt来进行集成,发现在ImageNet数据集上能带来3.5%的提升,具体见CLIP公开的notebook。 到这里大体了解CLIP中有一个对自然图像理解能力很强的image encoder,可以获得很好的图像特征向量,接下来需要一个能接收embedding向量,输出文本描述的强大模型,GPT当之无愧作为首选。 GPT2简介 GPT2(Generative Pre-trained 2),是由OpenAI开发的生成式自然语言模型,鉴于chatGPT的火爆,这里不过多介绍GPT1,2,3,3.5,4的差别。 在这里需要了解gpt2是一个生成式模型,根据输入的文本信息,可以生成一系列文本,如输入一个问题句子,gpt将句子变为text embedding,输入到模型中,然后一个一个单词的输出,最终输出一句回答。其中,人类输入的问题句子,可以看成是prefix embedding,gpt根据前缀信息,依次生成内容。 Prefix embeddings是指在GPT模型中,为每一个输入词添加一个前缀,然后将添加前缀后的词转化为向量表示。这个前缀是指输入词前面的所有词,它可以为模型提供更多的上下文信息,帮助模型更好地理解输入文本的含义。 举个例子,假设输入文本是“我喜欢吃苹果”,对于“苹果”这个词,它的前缀是“我喜欢吃”,添加前缀后的词就是“我喜欢吃苹果”。这个添加前缀后的词可以被转化为向量表示,然后作为GPT模型的输入。 在CLIPCap中,正式利用了gpt2强大的文本生成能力进行图像描述,但图像信息如何输入到gpt呢?接下来就看看CLIPCap的创新。 CLIP Captioning 2021年11月,ClipCap提出一种轻量化的方法,可以结合 CLIP的image encoder 和 GPT-2 ,实现图像描述。 ClipCap有三个部分,分别是image encoder, mapping network, gpt2。其中image encoder和gpt2都是在超大规模数据集上预训练过的,可以直接用。 由于CLIP和GPT2不好训练,所以设计一个mapping network,图像embedding特征向文本embedding特征的转换,从而巧妙的衔接了CLIP与GPT2,并且可以仅训练mapping nework,这一点与当前BLIP v2中的QFormer是一样的。 结合上图,来看看模型到底是如何实现图像描述的。 第一步,一张图片及其对应的描述文本,会被输入到CLIP中,得到image embedding向量:512维,文本则通过gpt2的字典转换为tokens,gpt2字典有50257个词。 第二步:图像特征经过maping network,获得40x768的特征,可以理解为将图像翻译为了40个768的特征向量 第三步:文本tokens经过word2emb获得 text embedding向量40x768维度,这里的40表示句子最长有40个单词,如果补足40,填充即可。 第四步:图像与文本特征拼接,输入到gpt2进行训练,gpt2输出80个50257维的分类概率向量,其中取后40个向量进行输出分类的单词,最终形成句子。 上述为训练过程,其中CLIP和GPT2是可以冻结的,详情可看代码。 在推理的时候,GPT2 word2emb的这一分支是没有的,gpt2仅拿到图像的40x768向量进行推理,逐步生成每一个单词,最终形成句子。 CLIP Captioning 训练代码实现 第一步,数据集下载及准备 首先准备数据集,这里采用coco 2014,需要下载的文件有 预处理过的标签文件:train_caption.json 原始图像文件夹:train2014,val2014 组织为以下目录形式 ROOT_PROJECT / data / annotations / train_caption.json ROOT_PROJECT / data / train2014 ROOT_PROJECT / data / val2014 第二步,CLIP模型特征提取,运行配套代码00_parse_coco.py python 00_parse_coco.py --clip_model_type ViT-B/32 由于不涉及CLIP的训练,因此CLIP对每一张图片的输出是固定的,所以可以把训练集中的566757个样本对进行特征提取及文本tokens的映射。 执行以下代码,预计需要3小时完成56万多张样本对的处理。 结果将保存在 ROOT_PROJECDT / data / oscar_split_ViT-B_32_train.pkl ROOT_PROJECDT / data / oscar_split_ViT-B_32_train_tokens.pkl 第三步,模型训练,运行配套代码01-train.py python 01-train.py --data ./data/coco/oscar_split_ViT-B_32_train.pkl --out_dir ./coco_train/ 到这里就可以开始训练了,在1080ti上训练10个epoch,耗时16小时,模型将保存在 ROOT_PROJECDT / coco_train 文件夹下 提供一个预训练权重,模型权重下载链接提取码:mqri CLIP Captioning 推理代码实现 推理示例,运行配套代码02-inference.py,需要配置下面三个路径即可。 ckpt_path = r'coco_prefix-009-2023-0411.pt' path_img = r'G:\\deep_learning_data\\coco_2014\\images\\val2014' out_dir = './inference_output' 在推理代码中,配置好模型路径、测试图片/文件夹路径,输出路径,运行即可得到结果 推理仅需要0.2s即可获得一例输出,速度还是可以接受的。 下图为val2014中的一些推理结果示意图 到这里Clip Cap就结束了,简单回顾一下Clip Cap整体内容。 借力:Clip Cap 站在了两个巨人的肩膀上,分别是CLIP和GPT2 磨合:为了让两个模块可以更好的融合使用,提出mapping network模块将CLIP的输出转换为GPT2能接收的特征向量形式, 40x768的40个\"单词\"的特征向量形式。 亲民:在1080ti上一天时间就可以训练coco数据集,并且还能用上大模型,这个motivation不得不说很巧妙 如果想将Clip Cap运用于中文,也推荐大家阅读ClipCap-Chinese 小结 Image Captioning 是一个CV+NLP的典型应用,是从一幅图输出一句话的过程,并且具有较高的商业价值,如字幕生成、图像标注、图像说明等。 本文介绍了图像描述的概念,发展历史,常用数据集,BLUE评价指标,并通过代码实现两种主流的图像描述算法。 CNN+RNN架构 CNN负责提取特征图,并变为特征向量1x512作为h0输入到RNN中 RNN逐步输出单词 Clip Cap 借助大模型——CLIP和GPT2,效果比CNN+RNN好很多,这也是基于Transformer的深度学习模型广泛应用的原因之一 巧妙的将图像特征与NLP模型嫁接起来,后续更为强大的BLIP v2同样采用了此操作 关于图像描述、文图、图文等多模态任务,十分推荐大家学习以下内容 https://github.com/salesforce/BLIP https://github.com/salesforce/LAVIS Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.8-image-retrieval-1.html":{"url":"chapter-8/8.8-image-retrieval-1.html","title":"8.8 图像检索(上)——理论基础","keywords":"","body":"8.8 Image Retrieval 图像检索 (上) 前言 本节介绍图像检索的概念及相关优化算法,为后续代码实践打下基础。 本节主要内容包括: 常用数据集 评价指标:R@K、mAP的计算 常用的loss 向量检索框架简介:Faiss、Milvus、Jina、Proxima和vearch都是工业界常用的向量检索框架 向量检索优化算法:LSH(局部敏感哈希)、HNSW(基于图的近似最近邻搜索)、PQ(乘积量化)、IVF(倒排检索) 检索中的排序:粗排、精排、重排的概念 图像检索简介 图像检索(Image Retrieval)指根据用户输入的信息进行图像查询,在数据库中查找出与之相似/相关的图像。 图像检索在电商、安防、医学领域有广泛应用,例如微信扫一扫、淘宝的拍立淘、搜索引擎的图片搜索功能等。 图像检索可根据输入的内容不同,可分为以文搜图和以图搜图。 以文搜图又称TBIR (Text Based Image Retrieval),是通过图片名称、关键词等信息进行检索,是早期图像检索的方法,依赖于图像的描述信息。 以图搜图又称CBIR (Content Based Image Retrieval),也叫基于内容的图像检索,也是目前主流的方法。CBIR的方式更符合人类视觉感觉,基于图像内容、图像相似度进行检索相似的图像。本节后续内容均以CBIR为例。 下图是图像检索系统的基础组件,通常分为offline和online两部分,offline主要构建图像数据库及特征数据库,online则是完成query图像特征提取,并从特征数据库中找到相似的特征,最终由推荐策略模块输出相似图像。 在构建图像检索系统时,需要关注: 特征提取器:传统方法中会基于SIFT、HoG等特征描述子,2012年后,主要是基于神经网络进行特征提取,获得特征向量,如1x768维,本文后续也会基于CLIP来进行特征提取。对特征提取器一个简单的要求是,相似图像的特征向量的欧氏距离要相近,差异大的图像的欧氏距离要大。特征提取模型可分为无监督与有监督的,无监督的主要采用视觉预训练模型进行特征提取,如CLIP, BLIP之类的,有监督的训练则可以采用CNN-base或者ViT-base的模型进行训练。 向量检索:如何快速的从亿万级的特征数据库中找到相似的那一批特征向量,这是向量检索及推荐策略需要考虑的,后续会介绍一系列快速的向量检索策略。 推荐策略:由于特征检索阶段采用了快速检索方法,势必损失一定精度,因此在后续需要做一些排序策略,如粗排、精排、重排等,最终输出相似的检索结果。 图像检索数据集 图像检索常用数据集并不多,常用的有Oxford-5k, UKBench, Holidays, Paris-6k Oxford-5k 由牛津的11个建筑物的5062张图片构成,每个建筑物提供了5张query image。标签从官网下载,图片从paddle下载 UKBench 包含2550组,每组4张图,总共10200张图片,图片由日常生活中常见的物体构成,如物体、场景和CD封面等。下载链接:https://pan.baidu.com/s/1LAURtLwq8UYPW3cnetDCyg 提取码:yrsy Holidays 是个人假日相册中收集的1491张图像,有500张query images, 991张相关图像。 Paris-6k 包含来自Flickr上爬取的6412张图片,由12组巴黎的建筑构成,总共提供了500张 query images进行评估。 数据从kaggle下载 图像检索评价指标 以图搜图中,常用的评价指标有mAP和R@1, R@5, R@10。 R@K:top-k个检索结果中的召回率(recall),对于检索结果中存在目标结果的即判为召回,记1分, 总得分÷样本总数 = R@K。 假设我们有一个包含100张图片的数据集,进行一次图片检索,对于查询图片Query-img来说,100张图片里只有1张是相似的,GT为图片编号5,其它的都是负样本,假设算法返回的Top 10检索结果如下: 排名 图片编号 是否为真实目标 1 56 否 2 12 否 3 5 是 4 88 否 5 1 否 6 90 否 7 3 否 8 21 否 9 6 否 10 47 否 R@1,Top 1的图片编号为56,不是真实目标,因此R@1的值为0。 R@5,Top 5的图片编号为56、12、5、88、1,其中图片5是真实目标,因此R@5的值为1。 R@10,Top 10的图片中,其中图片5是真实目标,因此R@10的值为1。 假设有20张查询图片,统计20张查询图片的R@1,得到20个R@1,求平均,即可得到最终R@1。 由此可知,R@1≤ R@5≤ R@10。 mAP mAP(mean average precision)是AP的平均值,对于每一张查询图片计算一个AP,求取平均。 AP值是PR(Precision-Recall)曲线下的面积,具体定义参考机器学习基础知识。 这里借助一个视频的示意图来讲解具体的计算 上图有两个查询,分别是query 1和query 2,其中黑色的样本表示正样本,是期望所检索到的样本,白色是负样本。 对于查询图像1,有5个相关结果,检索的排序结果如图所示,分别排在了1,3,6,9,10。 AP是将5个结果的Precision求平均,每个结果的Precision为 “标签次序÷结果次序”。 因此查询图像1的AP = (1/1 + 2/3 + 3/6 + 4/9 + 5/10)/ 5 = 0.62 mAP则是将所有查询图像的AP求取平均,如上例,为 (0.62+0.44) / 2 = 0.53 图像检索常用loss 图像检索选择特征提取器时,如果需要自行训练一个特征提取器,通常会选择一个适合的loss来约束模型输出的特征向量。 这里可能不好理解,先从图像分类模型思考,分类模型输出层前一层通常认为是对图像的特征描述,这个特征向量被认为是具备图像语义信息的描述,它只需要经过一个分类层,就能实现图像分类。 Triplet Loss 同理,也可把该特征向量认为是图像的描述,用于图像检索,但是图像检索里,还希望相同类别的向量的欧氏距离要近,不同类别的向量要远。 这时就可以在此处增加一个Triplet Loss(三元组损失函数)进行训练,triplet loss训练时有3个样本,分别称为anchor, negative, positive。 A表示目标图片,N表示负样本,要与目标图片远离的, P表示正样本,与A是同类型图片,要接近的。 更详细的triplet loss请阅读原论文,提出triplet loss是为了解决人脸比对问题的,后来拓展到各类对比任务中。在图像检索任务中,Triplet Loss同样具有较好的表现。通过将同一类别的图片的嵌入向量尽可能地接近,而将不同类别的嵌入向量尽可能地远离,可以实现高效的图像检索。 (N+1)-tuplet loss 三元损失函数考虑的 negative 样本太少,以至于收敛慢,为此,研究者提出了(N+1)-tuplet loss,一次考虑N-1个负样本,N=2时,等价于 triplet loss。 (N+1)-tuplet loss 如下图所示 N-pair Loss (N+1)-tuplet loss 有个缺点是计算量大,需要计算N-1个负样本的距离,推理成本过高。 为此,提出了N-pair Loss,巧妙的实现样本的重复利用,减少了负样本的多次运算。N-pair loss 思想是把其他样本的正样本作为当前样本的负样本,这样就不用重复计算不同样本的负样本,只需要计算 N 次即可得出。 除了 triplet loss 思路外,还可以借鉴其它类似任务的loss,例如: 人脸:ArcFace,sub-ArcFace 类内损失:center loss, Island loss 类间损失:cosface 度量学习任务:Multi-Similarity Loss, Contrastive Loss / Pairwise Ranking Loss,SimCSE loss, Smooth AP 更多loss参考论文2021年的Deep_Image_Retrieval_A_Survey 检索核心技术——向量检索 向量检索概念 图像检索的核心技术之一是向量检索,也称向量索引、相似度匹配,是将query向量与数据库中万亿级的向量进行相似度匹配,要求又快又准的找出相似向量。 向量相似度可以通过欧氏距离、余弦距离、汉明距离等距离度量方法来计算,通常float类型采用欧氏距离,二值哈希采用汉明距离。 对于欧氏距离、余弦距离来说,时间复杂度为O(nd),其中n是数据集中向量的数量,d是向量的维度,这对于亿万级的检索是不可接受的。 为了实现快速的向量检索,针对暴力检索(精确检索)有一系列的优化算法,例如基于树的KD-Tree,基于图的NSW、HNSW, 倒排索引、PQ(乘积量化,Product-Quantization)等。 由于采用了快速索引,必然导致精度损失,因此在向量检索之后,通常有排序算法的介入,一般有粗排、精排和重排,这个在下一小节讲解。 向量检索框架 向量检索不仅可用于图像检索,在搜索引擎、短视频推荐、商品推荐等领域均是基础模块,有较多开发者及对应的产品,因此,可以选择开源的向量检索引擎,而无需造轮子。 下面简单总结几款常用的框架 Faiss:由 Facebook 开发的适用于稠密向量匹配的开源库。 支持相似度检索和聚类 支持多种索引方式 支持CPU和GPU计算 支持Python和C++调用 常见的人脸比对,指纹比对,基因比对等 缺点是单机部署,无法分布式 Milvus:是一个开源的分布式向量搜索引擎。集成了成熟的向量相似度搜索技术,包括Faiss、Annoy、NMSLIB等。 Jina :Jina AI公司开发,是一家专注基于深度学习模型搭建搜索引擎技术的开源商业公司 Proxima:阿里巴巴达摩院自研的向量检索内核,通用化的向量检索工程引擎,实现了对大数据的高性能相似性搜索,支持 ARM64、x86、GPU 等多种硬件平台 vearch:是京东开源一个分布式向量搜索系统,可用来存储、计算海量的特征向量,在 faiss 的基础上研发了 vearch,提供了类似 ElasticSearch 的灵活易用的 RESTFul API,可以方便地对表结构及数据进行管理查询。 更多框架介绍推荐阅读几款多模态向量检索引擎 Faiss简介 对于80%的场景,基于faiss足以满足需求,并且faiss开源早,性能强大,功能多,且易上手,这里就介绍Faiss的使用及常用算法的原理。 Faiss由 Facebook AI开发及开源,适用于亿级的稠密向量检索,并且部分算法支持GPU加速。 faiss中常用的算法有,精确检索FlatL2、FlatIP,局部敏感哈希LSH(Locality-Sensitive Hashing),基于图的近似最近邻搜索 HNSW,倒排检索 IFS(Inverted File System,倒排)和 乘积量化PQ(Product Quantization)。 更详细介绍,参见faiss wiki 最常用的算法是倒排、乘积量化,接下来会对上述4个算法进行介绍,并详细介绍倒排与乘积量化。 LSH——局部敏感哈希 LSH是哈希算法中的一种,是将向量映射为标量,并进行分桶,距离较近的向量在哈希后映射到同一个桶的概率较高,反之概率较低,这个性质称之为局部敏感。 下面借助一位Up主的讲解及代码,解释LSH实现过程: 选择一系列hash functions,对原始数据进行投影,一个样本变为一个标量 选择分桶间隔w,标量除以间隔w,并向下取整,获得样本的hash编码 如上图,6个数据经过一次投影后得到的hash编码分别是 0, 1, 2, 3, 5, 5,选择多个hash functions即可得到一个样本的多个编码,即可构成一个编码向量。 hash function到底要选择怎样的投影方式,才能使得距离较近的向量在哈希后映射到同一个桶的概率较高,反之概率较低呢? 其实这里可以直接采用高斯分布采样得到的向量,与样本x进行点乘即可,这个可以通过P稳定分布定义进行证明,详情可参见第八讲 图像检索 下面看一个具体案例,包含8个向量,采用4个hash functions进行编码,得到的编码如下。 可以看到,样本7和样本8的4个编码都相同,表明它们很类似。 同理,样本1和样本2的hash编码也很类似,因它们的样本数据比较类似。 原始数据: [[8, 7, 6, 4, 8, 9], [7, 8, 5, 8, 9, 7], [3, 2, 0, 1, 2, 3], [3, 3, 2, 3, 3, 3], [21, 21, 22, 99, 2, 12], [1, 1, 1, 0, 1, 0], [1, 1, 1, 1, 1, 0]] 编码向量: [3.0, 3.0, 0.0, 1.0, 8.0, 0.0, 0.0] [5.0, 5.0, 1.0, 2.0, 32.0, 1.0, 1.0] [4.0, 4.0, 1.0, 2.0, 22.0, 0.0, 0.0] [7.0, 6.0, 2.0, 3.0, 21.0, 1.0, 1.0] import numpy as np import random def getHash(v, x, b, w): return (v.dot(x) + b) // w def dealOneBuket(dataSet): k = dataSet.shape[1] b = random.uniform(0, w) x = np.random.random(k) buket = [] for data in dataSet: h = getHash(data, x, b, w) buket.append(h) return buket if __name__ == \"__main__\": dataSet = [[8, 7, 6, 4, 8, 9], [7, 8, 5, 8, 9, 7], [3, 2, 0, 1, 2, 3], [3, 3, 2, 3, 3, 3], [21, 21, 22, 99, 2, 12], [1, 1, 1, 0, 1, 0], [1, 1, 1, 1, 1, 0]] dataSet = np.array(dataSet) w = 4 hash_funcs_num = 4 for _ in range(hash_funcs_num): print(dealOneBuket(dataSet)) HNSW——基于图的近似最近邻搜索 HNSW(Hierarchical Navigable Small World)是一种以空间换时间的基于图的最近邻搜索,属于基于层级结构的ANN(Approximate Nearest Neighbor)算法,通过建立多层图结构实现高效的最近邻搜索。其核心思想是在每一层图中使用稠密连接的方式建立向量之间的联系,同时保持局部连通性,从而形成“小世界”网络。 分层机制类似高速公路,顶层是省份间的连接,往下则是城市之间,城市内部之间的连接,由大到小的缩小检索范围。 更详细理解,推荐阅读Faiss的手册 倒排索引的优点是可以快速地定位包含某个特征的向量,适用于高维稠密向量。但是在处理高维稀疏向量时,倒排索引的效果会受到影响。 总之,倒排索引是向量检索算法中重要的一种索引方式,可以大幅提高检索效率和准确性。 PQ —— 乘积量化 PQ(product quantizaition)是2011年,Herve Jegou等学者在PAMI上发表的论文《Product quantization for nearest neighbor search》,提出的一个快速向量检索算法。 PQ方法可以减少几十倍的RAM消耗,并且获得数倍的速度提升,并且精度损失不是很大,非常适合高维向量检索任务的优化。 PQ算法的思想是将原始向量分解为多个子向量,并对每个子向量进行量化,从而将高维空间中的向量转换为一个小的离散码本,编码代表子向量所处的聚类中心的编号。查询向量需要与子向量的聚类中心进行距离计算,获得距离表,随后数据库向量根据编码进行查询,即可获得查询向量与被索引向量的距离,最后对子段距离求和、排序。 PQ的思想可以总结为,将高维向量划分子段,并且利用子段的聚类中心来代替原始数据,查询向量只需要与聚类中心计算距离,即可实现所有向量距离的近似计算。 下面以一个1000个128维向量数据库为例,讲解PQ过程。 第一步,pre-train,获得聚类中心。 第二步,对数据库向量进行编码。 第三步,查询向量与聚类中心计算距离表。 第四步,查询所有向量与查询向量的距距离求和、排序,得到结果。 第一步:pre-train,对128维向量划分为M份,这里划分8个子段,每个字段长度为16,针对每个字段进行K-means聚类,聚为256类(256是作者自定义的)。 打不严谨的比方,1000个向量分为256类,平均4个向量为一簇,由1个聚类中心向量代表,后续查询向量只需要与聚类中心比较,就可知道查询向量与4个向量的距离了。简单理解就是聚类簇中的向量统一由聚类中心作为代表,查询向量与中心近,那么就与类成员近,以此减少计算量。 第二步,对数据库向量进行编码,得到数据库的PQ code,这个code的作用是用于查表的,即每个向量它被谁代表了,后续只需要去找“代表”获取距离,而不是与查询向量计算距离。 1000个128维向量,最终的PQ code为 1000x8bit的码表,码表上的值表示它归属于哪个聚类中心,如第一个向量的第一个字段为2,则说明它属于第二类,它后续与查询向量的距离只需要查看,查询向量与第二个聚类中心的距离即可。 经过PQ编码,内存的效果显著下降,下降了64倍,64来自128维下降到了8维,这里减少了16倍;数据精度由32b减少到了8b,这里减少了4倍,总共64倍。 第三步,查询向量与聚类中心计算距离,获得查询向量与聚类中心的距离表(256x8)。 例如查询向量为[1, 2, ..., 128],首先对第一子段进行256个聚类中心的距离计算,采用[1, 2, 3, 4, 5, 6, 7, 8]与256个聚类中心计算距离,填入Distance Table的第一列, 以此类推,获得256x8的距离表。 距离表的作用是供PQ code查询的,前面提到,数据库向量已经被代表了,被聚类中心代表,查询向量不与数据库向量直接计算距离,而是与聚类中心计算距离。 数据库向量只需要找到它的代表与查询向量的距离即可,因此PQ code是 Distance Table的索引,只需要查表即可获得数据库向量与查询向量的距离。 第四步,查询所有向量与查询向量的距距离求和、排序,得到结果。 PQ算法巧妙的采用了代表(聚类中心),以此减少运算复杂度,同时得益于代表(聚类中心),数据库无需存储所有数据,只需要存储所处聚类的编号,从存储上优化了算法。 IVF —— 倒排检索 IVF(Inverted File Index),倒排文件索引,用于快速地定位包含某个特征的向量。其中使用了聚类分组、倒排索引的方法来加速索引速度。 这里的Inverted Index 是著名的倒排索引算法,最早来自文本搜索领域,关于Inverted Index的命名及翻译,让人多少有些困惑,倒排?怎么倒?怎么排? 其实这里和“倒”没啥关系,更好的中文意译是,分词索引或反向索引。详见知乎问题 Inveted Index是从文档检索中提出的,以往检索文档是通过一个一个文档中从头到尾比对,是否存在关键词,是文档-->关键词的方式;倒排则是将文档中文字进行分词,并且构建词到文档编号的一个字典索引,查询时只需要根据字典的key索引,可得到哪些文档中存在这些关键词。 这里借助 喔喔牛的回答 - 知乎 假设有一个文档数据库,共5分文档,现在需要检索“wave”关键词在哪份文档中,正向索引则是挨个文档一次检索匹配“wave”,到了第4个文档的中间才能找到,相当耗时。 倒排索引的思路是将文档中的词先拆分,需要用分词工具把文档打散称为一系列word,然后构建关键词到文档的关系字典。 如下图,通过倒排索引字典,直接获取文档3是包含\"wave\"的,效率提升显而易见。 回到Faiss中的IVF,IVF是将向量先分为nlist个组,每组有一个聚类中心及类别id,通过构建聚类中心+类别id 到 向量的索引字典,可以快速的找到查询向量与哪一些数据库向量是相近的,与上文的PQ量化中的聚类类似,一群被检索向量采用聚类中心作为代表。 具体地,IVF算法流程如下: 将数据集进行聚类,得到若干个簇(nlist个),每个簇有一个代表它的中心点; 为每个簇建立一个倒排文件,key是聚类中心,value是簇内所有向量; 对于一个查询向量,计算其与每个簇中心点的距离,确定其所属的簇; 在所属簇的倒排文件中搜索与查询向量最相似的数据点。 在实际实现时,还会针对边缘问题,提出所个类簇的比对,如下图所示,对聚类中心最新的8个类簇内所有向量均进行比对,以此提高召回。详情可见Faiss中的IVF IVF+PQ Faiss中用得更多的是将IVFPQ,主要是用IVF中的方法来减少搜索向量范围,然后利用PQ的索引。product-quantization中对FlatL2(精确检索)、PQ和IVFPQ进行了对比,可见IVFPQ速度快,并且召回与PQ相当。 这里需要注意的是IVF中的nlist和nprobe是超参,需要仔细选择,尤其是nprobe,文中实验采用了nprobe=48才达到52%的召回,nrpobe越高,召回越高,但是时间越慢。 检索核心技术——粗排、精排、重排 上文提到的一系列向量检索方法仅仅是检索的第一步——召回,通常一个检索系统是级联式的,多层级一步一步的由粗到精,得到最终输出结果。 如下图所所示 召回,是处理亿万级别数据量,要求的是速度快,精度可以不高,但别漏,去粗取精还有粗排、精排和重排来完成,但是漏了的话,后续是没办法“生成”、找回的。 粗排,是召回和精排之间的过渡,速度比精排快,但精确度不需要那么高。 精排,核心层,直接决定检索推荐的效果; 重排,根据业务逻辑,决定哪些item的顺序需要调整。 总结下来,推荐算法的这一套级联策略——召回、粗排、精排、重排,与其电商业务、互联网搜索业务息息相关,每一个模块为了处理特定了业务逻辑而设计的一个操作。 例如第一步召回是从亿万级数据捞数据的过程; 粗排、精排是根据特定的业务背景设计算法、模型,详见知乎文章; 重排也是根据业务逻辑,例如为了提升用户的多样性体验,扶持业务产品,这时在重排层则需要写一些固定的逻辑判断来重排。 在图像检索领域,较少涉及粗排、精排,通常只需要做一个重排,图像检索的重排技术非常细分,这里不进行介绍了,感兴趣自行阅读一下论文: 扩展查询(Query Expansion) Ondrej Chum, James Philbin, Josef Sivic, Michael Isard, and Andrew Zisserman. Total recall: Automatic query expansion with a generative feature model for object retrieval. In Proceedings of the IEEE International Conference on Computer Vision and Pattern Recognition, pages 1–8, 2007. 1, 2, 3, 9 Ondˇ rej Chum, Andrej Mikulik, Michal Perdoch, and Jiˇ rí Matas. Total recall ii: query expansion revisited. In Proceedings of the IEEE International Conference on Computer Vision and Pattern Recognition, pages 889–896, 2011 几何信息法(Geometric Context Verification (GCV)) Yannis Avrithis and Giorgos Tolias. Hough pyramid matching: Speeded-up geometry re-rankingfor large scale image retrieval. International Journal of Computer Vision, 107(1):1–19, 2014 行人重识别中假阳过滤:Box Re-Ranking_ Unsupervised False Positive Suppression for Domain Adaptive Pedestrian Detection 基于Transformer的重排:Contextual Similarity Aggregation with Self-attentionfor Visual Re-ranking 小结 本小节介绍了图像检索技术的概念、数据集、损失函数和常用的评价指标,然后介绍了检索的两大核心技术,向量检索与排序。 向量检索中最常用的是PQ+IVF,这也将在后续代码实现中进行使用以及讲解,LSH和HNSW也有自己的特点,可根据是否需要用空间换时间来决定选择。 检索任务的排序环节包括一系列的操作,有粗排、精排、重排等操作,就像一个漏斗,逐步去粗取精,每一步都是与业务场景息息相关,在图像任务中往往只需要一个重排就ok了。 本节整体介绍图像检索概念及实现技术,下一节将介绍Faiss库的使用、构建基于CLIP模型的图像检索算法、基于Flask部署图像检索系统等。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-8/8.8-image-retrieval-2.html":{"url":"chapter-8/8.8-image-retrieval-2.html","title":"8.8 图像检索(下)——CLIP+Faiss+Flask的图像检索系统","keywords":"","body":"8.8 Image Retrieval 图像检索 (下) 前言 上一小节,对图像检索的基础概念进行了介绍,本节将通过代码实践,实现以文搜图、以图搜图的功能。 本节代码核心包括: Faiss 框架介绍及常用算法评估,并基于Faiss实现COCO 2017的11万数据的图像检索 CLIP实现image/text的特征提取 集成Faiss+CLIP构建无需训练的图像检索系统 基于Flask将图像检索系统部署为web服务 Faiss安装及使用 简介 Faiss是MetaAI开源的高性能向量检索库,它基于C++编写,提供python接口,核心模块还可以用GPU加速,在亿级数据可在xxx秒级实现结果返回,目前应用较为广泛。中小型项目及个人,非常适合采用Faiss。 Faiss目前最好的学习资源有两个,一个是官方wiki,一个是www.pinecone.io的Faiss: The Missing Manual faiss提供的优化算法主要分为PQ量化和倒排索引,下图为faiss中算法体系结构图,可以看到核心在蓝色区域和黄色区域。 安装 可以通过pip或者conda安装,这里推荐conda,这里需要注意镜像源最好conda源,若是清华镜像是行不通的。 # cpu版本安装 conda install -c pytorch faiss-cpu # gpu版本安装 conda install -c pytorch faiss-gpu 切换回到conda官方源方法: # 依次输入 conda config --remove-key channels conda config --add channels defaults conda config --add channels conda-forge 在这里安装了gpu版本,便于测试,在gpu版本安装中,会默认装上cudatoolkit, 600多M,总共需要900多M The following packages will be downloaded: package | build ---------------------------|----------------- ca-certificates-2022.12.7 | h5b45459_0 143 KB conda-forge cudatoolkit-11.8.0 | h09e9e62_11 638.9 MB conda-forge faiss-1.7.2 |py38cuda112h7f1466e_3_cuda 885 KB conda-forge faiss-gpu-1.7.2 | h949689a_3 15 KB conda-forge intel-openmp-2023.1.0 | h57928b3_46319 2.5 MB conda-forge libblas-3.9.0 | 16_win64_mkl 5.6 MB conda-forge libcblas-3.9.0 | 16_win64_mkl 5.6 MB conda-forge libfaiss-1.7.2 |cuda112h33bf9e0_3_cuda 51.0 MB conda-forge libfaiss-avx2-1.7.2 |cuda112h1234567_3_cuda 51.1 MB conda-forge libhwloc-2.9.1 | h51c2c0f_0 2.4 MB conda-forge libiconv-1.17 | h8ffe710_0 698 KB conda-forge liblapack-3.9.0 | 16_win64_mkl 5.6 MB conda-forge libxml2-2.10.4 | hc3477c8_0 1.7 MB conda-forge libzlib-1.2.13 | hcfcfb64_4 70 KB conda-forge mkl-2022.1.0 | h6a75c08_874 182.7 MB conda-forge numpy-1.24.2 | py38h7ec9225_0 5.6 MB conda-forge openssl-1.1.1t | hcfcfb64_0 5.0 MB conda-forge pthreads-win32-2.9.1 | hfa6e2cd_3 141 KB conda-forge python_abi-3.8 | 2_cp38 4 KB conda-forge setuptools-67.6.1 | pyhd8ed1ab_0 567 KB conda-forge tbb-2021.9.0 | h91493d7_0 151 KB conda-forge ucrt-10.0.22621.0 | h57928b3_0 1.2 MB conda-forge vs2015_runtime-14.34.31931 | h4c5c07a_10 708 KB conda-forge ------------------------------------------------------------ Total: 962.3 MB 安装验证——\"Hello Faiss\" 接下来采用faiss进行精确查找,实现10000条查询向量的top-4向量检索。 faiss使用步骤主要分三步: 创建索引器,索引器有FlatL2、LSH、PQ、HNSWFlat等 初始化索引器,将数据库向量添加到索引器中,并进行预训练(如果需要) 使用索引器进行检索。 以下为配套代码运行结果 import time import numpy as np import faiss # ============================ step 0: 数据构建 ============================ np.random.seed(1234) d = 64 # dimension nb = 100000 # database size nq = 10000 # nb of queries xb = np.random.random((nb, d)).astype('float32') xq = np.random.random((nq, d)).astype('float32') xb[:, 0] += np.arange(nb) / 1000. xq[:, 0] += np.arange(nq) / 1000. # ============================ step 1: 构建索引器 ============================ index = faiss.IndexFlatL2(d) index.add(xb) # ============================ step 2: 索引 ============================ k = 4 # top_k number for i in range(5): s = time.time() D, I = index.search(xq, k) print(\"{}*{}量级的精确检索,耗时:{:.3f}s\".format(nb, nq, time.time()-s)) # ============================ step 3: 检查索引结果 ============================ print('D.shape: {}, D[0, ...]: {}'.format(D.shape, D[0])) print('I.shape: {}, I[0, ...]: {}'.format(I.shape, I[0])) # D是查询向量与topk向量的距离,distance # I是与查询向量最近的向量的id,此处有10万数据,index在0-99999之间。 输出的D表示距离, 6.815表示第一个查询向量的top-1向量的距离是6.815 输出的I表示向量id, 381表示第一个查询向量的top-1向量的id是381 D.shape: (10000, 4), D[0, ...]: [6.815506 6.8894653 7.3956795 7.4290257] I.shape: (10000, 4), I[0, ...]: [381 207 210 477] faiss提供的方法汇总表如下: Method Class name index_factory Main parameters Bytes/vector Exhaustive Comments Exact Search for L2 IndexFlatL2 \"Flat\" d 4*d yes brute-force Exact Search for Inner Product IndexFlatIP \"Flat\" d 4*d yes also for cosine (normalize vectors beforehand) Hierarchical Navigable Small World graph exploration IndexHNSWFlat 'HNSWx,Flat` d,M 4d + x M 2 4 no Inverted file with exact post-verification IndexIVFFlat \"IVFx,Flat\" quantizer,d,nlists,metric 4*d + 8 no Takes another index to assign vectors to inverted lists. The 8 additional bytes are the vector id that needs to be stored. Locality-Sensitive Hashing (binary flat index) IndexLSH - d,nbits ceil(nbits/8) yes optimized by using random rotation instead of random projections Scalar quantizer (SQ) in flat mode IndexScalarQuantizer \"SQ8\" d d yes 4 and 6 bits per component are also implemented. Product quantizer (PQ) in flat mode IndexPQ \"PQx\",\"PQ\"x\"x\"nbits d,M,nbits ceil(M * nbit / 8) yes IVF and scalar quantizer IndexIVFScalarQuantizer \"IVFx,SQ4\" \"IVFx,SQ8\" quantizer,d,nlists,qtype SQfp16: 2 *d+ 8, SQ8:d+ 8 or SQ4:d/2+ 8 no Same as theIndexScalarQuantizer IVFADC (coarse quantizer+PQ on residuals) IndexIVFPQ \"IVFx,PQ\"y\"x\"nbits quantizer,d,nlists,M,nbits ceil(M * nbits/8)+8 no IVFADC+R (same as IVFADC with re-ranking based on codes) IndexIVFPQR \"IVFx,PQy+z\" quantizer,d,nlists,M,nbits,M_refine,nbits_refine M+M_refine+8 no faiss 基础 相似性评价指标 faiss的评价指标主要有L2 和 inner product,L2是平方后的L2,这是为了减少计算量,只是比较大小,就没必要开平方了,需要真正意义的L2,要自行开平方。 除了L2和inner product,还有METRIC_L1, METRIC_Linf and METRIC_Lp ,但不常用,需要时查看官方wiki文档。 faiss的数据预处理 faiss提供了高性能的 k-means clustering, PCA, PQ encoding/decoding,需要用时查看wiki 索引器太多,如何选? RAM充足:选HNSW,IVF1024,PQNx4fs,RFlat RAM一般:OPQ M _D ,...,PQ M x 4fsr RAM不足:OPQM _D ,...,PQM 数据量不大,且要求精确:选 IndexFlatL2 或 IndexFlatIP 数据量大:IVF K, K可以选择256,2^16=65536, 2^18=262144, 2^20=1048576,根据数据量在1M, 10M, 100M, 1B之间选择。 综上:内存问题用量化,速度问题用倒排,一个普适的方法是 IVF + PQ。 faiss预处理及后处理 预处理方法: random rotation, RandomRotationMatrix, useful to re-balance components of a vector before indexing in an IndexPQ or IndexLSH remapping of dimensions, RemapDimensionsTransform, to reduce or increase the size of a vector because the index has a preferred dimension, or to apply a random permutation on dimensions. PCA, PCAMatrix, for dimensionality reduction , OPQ rotation, OPQMatrix, OPQ applies a rotation to the input vectors to make them more amenable to PQ coding. See Optimized product quantization, Ge et al., CVPR'13 for more details. 后处理方法: re-ranking:IndexRefineFlat,基于距离的重排,利用近似检索获得的结果精度可能较低,可通过距离进行重排。 IndexShards:组合多个索引器的结果,或是多gpu并行结果的融合。 index factory faiss提供了基于字符串创建索引器的工厂函数,字符串以逗号分隔,可以一次性创建复合的索引器,包括预处理、索引、后处理等。 举例: index = index_factory(128, \"PCA80,Flat\") :为 128维 向量生成一个索引,通过 PCA 将它们减少到 80维,然后进行精确索引。 index = index_factory(128, \"OPQ16_64,IMI2x8,PQ8+16\"):输入是128维度 向量,再将 OPQ 变换应用于 64D 中的 16 个块,再使用 2x8 位的反向多索引(= 65536 个反向列表),最后使用大小为 8 的 PQ 进行量化16 字节。 索引数据的I/O 数据库的索引信息通常需要存于磁盘,再读入内存,这时候需要读取I/O方法。 write_index(index, \"large.index\"): writes the given index to file large.index index = read_index(\"large.index\"): reads a file GPU 使用 faiss提供核心索引器的gpu加速,可以将数据放到gpu上进行加速运算,使用比较方便,只需要以下三步:先获取gpu资源、创建cpu的索引器、cpu索引器搬到gpu上。 注意:PQ不支持gpu加速,IVFPQ才支持。GPU: only pq.nbits == 8 is supported res = faiss.StandardGpuResources() # 1. 获取gpu资源 index_flat = faiss.IndexFlatL2(d) # 2. 创建cpu索引器 gpu_index_flat = faiss.index_cpu_to_gpu(res, 0, index_flat) # 3. 迁移至gpu 索引速度比cpu快5-10倍,笔记本测试是快了10倍,详情可运行配套代码。 faiss还提供多gpu并行运算,多gpu使用如下代码所示 import numpy as np import faiss d = 64 # dimension nb = 100000 # database size nq = 10000 # nb of queries np.random.seed(1234) # make reproducible xb = np.random.random((nb, d)).astype('float32') xb[:, 0] += np.arange(nb) / 1000. xq = np.random.random((nq, d)).astype('float32') xq[:, 0] += np.arange(nq) / 1000. ngpus = faiss.get_num_gpus() print(\"number of GPUs:\", ngpus) cpu_index = faiss.IndexFlatL2(d) gpu_index = faiss.index_cpu_to_all_gpus(cpu_index) gpu_index.add(xb) # add vectors to the index print(gpu_index.ntotal) k = 4 # we want to see 4 nearest neighbors D, I = gpu_index.search(xq, k) # actual search print('D.shape: {}, D[0, ...]: {}'.format(D.shape, D[0])) print('I.shape: {}, I[0, ...]: {}'.format(I.shape, I[0])) 除了cpu迁移至gpu和多gpu的函数,还有gpu迁移至cpu的函数:faiss.index_gpu_to_cpu。 使用gpu需要注意: k and nprobe must be For GpuIndexIVFPQ, code sizes per encoded vector allowed are 1, 2, 3, 4, 8, 12, 16, 20, 24, 28, 32, 48, 56, 64 and 96 bytes. 其它:详见wiki cpu与gpu有相同的精度,但是会发生cpu与gpu的检索结果不一致的情况!这可能是floating point reduction order、equivalent element k-selection order、float16 opt-in导致的 faiss 代码结构设计 faiss 底层代码为CUDA、BLAS、numpy,其中CUDA+BAL是核心,构建了所有底层计算(浅绿色方框),再往上就是对python、Rust/C#、C++的代码封装,用户可直接使用python等语言调用faiss的功能。 这里主要看python部分,对于底层计算库c++/cuda,经过SWIG将numpy与底层计算库封装为python类,变为python可调用的接口;向上再经过Adaptor layer封装,以便于python对象可转换为C++对象;随后采用contrib library进行封装,提供python代码接口。 SWIG (Simplified Wrapper and Interface Generator) 是一个用于将 C/C++ 代码封装为其他编程语言接口的开源工具。 Adaptor layer 是指用 Python 编写的桥接层,其作用是将 Python 对象与 C++ 对象进行转换,通过定义 Python 类或函数的方式,将 C++ 类或函数封装为 Python 对象或函数 Python contrib library 是一个开源的 Python 库,它包含了许多常用的机器学习和深度学习模型、工具和数据集。 faiss有一大特点,可以用pytorch的tensor可直接传入索引器的.add() 和 .serarch()。 faiss进阶知识点 线程与异步:cpu是线程安全的,gpu不是线程安全的,详情参见wiki 底层API介绍:InvertedLists 和 InvertedListsScannerwiki 超大数据的存储方案:分布式存储于多机器、存储于磁盘、分布式键值存储wiki 二进制量化编码:三个核心API及使用demowiki 暴力检索的直接调用:暴力检索可以不需要索引器,直接调用函数计算就好。wiki 低比特高速索引:借鉴Google的SCANN 4-bit PQ fast scan,实现高速索引。wiki) 实战笔记系列:k-means算法、IVFPQ的预计算表、PCA矩阵计算、非穷举搜索的统计信息、重排refine的作用及参数设置。wiki 超参数自动搜索:例如nprobe,有的向量用10,有的向量用20,这里提供了SearchParameter实现自动搜索。wiki 加速优化策略:大内存页、选择适当的CPU资源、选择适当的MKL线程数等。wiki Faiss 的benchmark 接下来对常用的PQ与IVFPQ进行benchmark测试,观察不同参数下,耗时与recall的情况,为后续实际应用挑选参数做准备。 数据集下载:采用sift1M数据(http://corpus-texmex.irisa.fr/),提供100万的图像特征向量,向量长度为128,在个人电脑上可运行。 首先观察Product Quantization算法的参数,PQ中子段的数量会影响计算速度与召回,子段越多召回越高,同时量化bit越小,内存占用越小,但召回降低。 下面就观察子段分别是4,8,16,32时,量化bit分别是6,7,8,9时,耗时与召回的情况。 运行配套代码,可得到如下两张图,分别是召回的对比、耗时的对比。 通过实验结果图可知道: 16个子段大约是个分水岭,小于16个子段时,召回会快速提升。一般用16个子段、32个子段 耗时方面,8bit的耗时出现异常,这可能是由于代码针对8bit做了特殊优化,并且论文中推荐的也是8bit量化 后续使用,直接用PQ16x8或者PQ32x8就好。 PQ + IVF 在faiss中PQ通常不会单独使用,而是结合IVF,IVF中聚类中心的数量会影响速度和召回,检索时probe的数量也会影响速度和召回。 由于PQ参数已经选好了,这里采用PQ32x8,IVF的聚类中心分别有128,256,512,1024,2048,4096;probe分别有1, 2, 4, 8, 16, 32, 64。 运行配套代码,可得到如下两张图,分别是召回的对比、耗时的对比。 通过实验结果图可知道: 相同probe时,聚类中心越少,召回越高。可能是数据集的问题,聚类中心的差异没有很明显,需要把128和4096进行对比,精度差别不大。 聚类中心越多,耗时越少,因为一个类里边的向量个数少了,需要匹配的向量就少了,速度就快 probe数量增大,耗时增加,在32之前增加较小,因此32是个不错的选择,当精度不足时可适当增加到48。PQ的wiki中也提到IVFPQ的对比,probe=48时,可以得到与PQ相同水平的recall,并且耗时大幅度较少。 聚类中心选4096,nprobe选48,下调最多到36,否则精度降低,并且速度没啥提升。 经过两个实验,可以得到一个通用的检索器,\"IVF4096,PQ32x8\",并且推理检索时,设置index.nprobe=48。 更多benchmark及应用案例可以看wiki中的Case-studies, benchs 基于CLIP+Faiss+Flask的图像检索系统 接下来,采用CLIP模型充当特征提取器,采用Faiss实现向量检索,采用Flask进行web服务部署,最终可实现以图搜图和以文搜图。 回顾图像检索简介中介绍的,一个图像检索系统如下图所示, 在构建图像检索系统时,需要关注: 特征提取器:采用CLIP(Contrastive Language-Image Pre-training)模型进行图像特征提取,是一个文图预训练模型,CLIP于2021年2月由openAI发表,并开源了模型,模型由4亿的图文数据,采用对比学习方式进行训练得到,由于对比学习与超大规模的数据集加持,使CLIP模型很好的理解了自然图像,在众多数据集上表现出了优异的zero-shot性能,同时在表征学习(representation learning)中也很好。CLIP简介可以回顾图像描述,上一节采用了CLIP进行图像描述。 向量检索:基于faiss的IVF+PQ方法 推荐策略:目前缺少业务背景及需求,这里不涉及。 代码结构 整套代码位于:pytorch-tutorial-2nd\\code\\chapter-8\\08_image_retrieval,代码结构如下: ├── config ├── data ├── flask_app.py ├── image_feature_extract.py ├── my_utils ├── retrieval_by_faiss.py ├── static └── templates 需要运行的为三份.py脚本分别是 image_feature_extract.py:特征向量库构建,基于CLIP将数据图片进行特征提取,并存储为特征向量形式 retrieval_by_faiss.py:基于faiss+clip的图像检索算法实现,作为被调用的代码块 flask_app.py:web服务部署,将调用retrieval_by_faiss中的ImageRetrievalModule实现检索 其它文件夹功能如下: config:整个项目的配置文件,需要设置图片数据根目录、faiss检索算法超参、相似topk等 data:存放clip模型提取的特征,即特征数据库。该路径可以在配置文件中修改 my_utils:功能函数 static:flask框架静态文件的目录,很重要的一点是需要在里面构建软链接,让flask能访问到coco2017的图片,否则无法在web前端页面展示 templates:flask的模板目录,存放html文件,前端页面设计就在index.htm中 整个图像检索运行逻辑如下图所示: 准备阶段,用clip对数据库进行数据编码,得到图像特征字典库,将特征向量传递给faiss,用于构建检索器;图片id及路径关系用于id到路径的映射 推理阶段,图片或文本经clip编码,输入faiss进行向量检索,得到向量id,再由{id:path}字典,获得图片路径,最后进行可视化。 PS:这里有一个需要注意的是,文本特征向量与图像特征向量进行匹配时,是需要进行norm操作的!在这里被坑了一天,最好对照论文才发现少了norm这个步骤。 代码UML设计简图如下: 将特征提取与向量检索分别设计一个类来实现,最后将它们这到图像检索模块中,对外提供图像检索服务。 代码使用 第一步,配置路径:修改config文件,设置图片文件路径等信息 第二步,进行特征提取:运行image_feature_extract.py,进行特征提取,在data文件夹下获得两个pkl文件 第三步,启动web服务,运行flask_app.py,可到http://localhost:5000/,使用图像检索系统 图像检索系统web使用说明 PS:由于前端知识的欠缺,有意美化该界面的,欢迎提issue! 小结 本小节通过代码介绍了faiss库的基础使用,并对常见的PQ, IVF+PQ的性能进行了评测,对未来使用向量检索框架做准备; 同时还涉及了基于CLIP+Faiss+Flask的图像检索系统部署,其中使用了优秀的多模态模型——CLIP进行特征提取,通过这一套系统可以巩固图像检索系统构建时的几个要点:预处理建特征向量库;检索器初始化;在线检索服务。 图像检索领域涵盖的知识点太多了,本案例仅能作为入门教程,各细分方向则需要大家自行修炼,祝好! Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/":{"url":"chapter-9/","title":"第九章 自然语言处理项目案例","keywords":"","body":"第九章 自然语言处理项目案例 第九章 自然语言处理项目案例 9.1 自然语言处理简介 9.2 文本分类-RNN-LSTM 9.3 机器翻译-Seq2Seq 9.4 机器翻译-Transformer 9.5 命名实体识别-BERT 9.6 文章续写-问答对话-GPT 第九章简介 本章介绍NLP中常用的模型及任务实现,模型主要包括RNN、LSTM、Transformer、BERT和GPT,任务方面包括情感分析、文本分类、机器翻译、命名体识别、QA问答、文章续写。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.1-nlp_introduction.html":{"url":"chapter-9/9.1-nlp_introduction.html","title":"9.1 自然语言处理简介","keywords":"","body":"9.1 NLP基础概念 NLP任务不同于CV任务,它存在更多难点,例如 数据表示:NLP任务处理的是文本数据,需要将自然语言文本转化为计算机可处理的形式,即如何把字符串变为数值数据,常用的有词嵌入(Word Embedding),而CV任务处理的图像,天然是数值形数据 上下文依赖:文本具有丰富的上下文依赖性,单个词或短语的含义可以依赖于其周围的上下文,这使得NLP任务在理解和处理上更具挑战性。 长期依赖:文本序列可能非常长,并且存在长期依赖性,需要模型能够捕捉长距离的上下文信息 多义性和歧义性:语言中存在多义词和歧义性,需要根据上下文来进行准确理解和推断。 由于NLP任务存在诸多难点,因此在正式开始前,需要对NLP基础概念进行说明。 首先介绍NLP数据处理基础流程,然后介绍各式各样的NLP任务,最后总结NLP基础概念名词 NLP任务处理流程 以电影影评文本分类为例,流程可分为以下几个步骤: 数据清洗和预处理:去除无关字符、标点符号、HTML标签、去除停用词等,目的是得到具备真实语义的文本序列。 分词:将文本序列划分为“具备独立语义“的词元(token),每个token是基本单位。英文中通常用空格可区分,而中文常需要特定的分词方法。例如,“这电影不错。”可划分为[\"这\", \"电影\", \"不\", \"错\", \"。\"],或者将每个字符划分,[\"这\", \"电\", \"影\", \"不\", \"错\", \"。\"]。具体要如何分词,这需要根据任务以及模型的能力决定,对于BERT这样的模型,或许以及学会了分词、组词的能力,只需要逐个字输入即可。 构建词表:词表(Vocabulary)是个字典,作用是将词元(token)映射到整数索引,例如[('', 0), ('the', 1), ('i', 2), ('and', 3)...]。词表将文本数据中的词元(token)映射为对应的标识符(通常是正整数),然后使用词嵌入模型将这些标识符转化为对应的词向量表示。 词嵌入:模型运算需要的是数值型数据,因此需要一个映射,将字符映射到向量,这个向量可理解为特征向量/词向量,这个映射过程称为词嵌入(word embedding)。词嵌入是个复杂问题,需要的约束较多。例如,语义相近的词元(token)的特征向量要接近,即余弦距离要小。目前常采用已经预训练好的词嵌入模型,如word2vec, GloVe, FastText等。 算法模型:NLP模型通常是序列模型,即可处理多个词向量输入,然后根据任务类型进行输出,如文本分类则最终输出一个分类概率向量,翻译任务则输出一些列分类概率向量,向量个数是文本单词/词元个数,概率向量的长度是词表大小,即把单词的输出转换为单词分类,只能在词表中选一个最有可能词进行输出。 NLP常见任务 与CV任务不同,NLP任务繁多,需要归纳总结,逐个分析。 下面借助台大李宏毅老师2021年NLP课程的内容,梳理NLP主要内容。 详细内容参见:http://speech.ee.ntu.edu.tw/~tlkagk/courses_DLHLP20.html NLP任务细分有十多种大类,小类有几十种,但整体归纳下来可概括为两种类型,分别是seq2cls和seq2seq。 seq2cls:输入是序列,输出是类别,根据输出数量又可分序列级分类和token级分类。文本分类就是序列级,词性标注就是token级。 seq2seq:输入是序列,输出也序列,例如机器翻译、文章摘要、问答系统、阅读理解等任务都是seq2seq的。 下面对常见的NLP任务进行一句话概括。 词性标记,Part-of-Speech(POS) Tagging:seq2cls, 为每个token输出一个类别,在早期NLP模型中,作为预处理,将词性和文本输入到下游模型中,期望通过词性信息提升模型能力。 分词,word segmentation:seq2cls,将文本序列切分开,英文有空格进行切分,中文则需要分词,文本序列切分为多个token。 指代消解,coreference resolution:将同一个东西(entity)的不同表达识别出来,给下游任务额外的提示信息。 情感分析,sentiment classification:seq2cls,将整个文本进行输出一个类别,常用于影评、社交媒体评论的分类。 舆情分析(立场检测),Stance Detection:seq+seq 2 cls,常分为4类:Many systems use the Support, Denying, Querying, andCommenting (SDOC) labels for classifying replies。 机器翻译, machine translation:seq2seq,很好理解,两种语言之间的转换,典型的seq2seq任务。 文本摘要,summarization:抽取式(Extraction-based),seq2cls。为原文中逐个token进行二分类,保留/不保留,最终输出保留的token(单词/句子)。 文本摘要,summarization:生成式(Abstraction-based),seq2seq。将原文进行理解、编码,再经解码器输出总结的内容。类似机器翻译,输出的是NxM的概率向量矩阵。N是token数量,M是词表中词的数量。 问答系统:seq2seq,输入问题,输出答案,进阶版问答系统还具备“记忆”能力,能根据上下文进行回答,这就变为对话系统。 命名实体识别,NER(Name Entity Recognition):seq2cls,将文本中的实体(如人名、地名、组织名)进行标注和分类,将每个token分类为实体类型或非实体类型。 关系抽取,Relation Extraction:seq2seq,旨在从文本中提取出实体之间的关系。输入是包含两个实体的句子以及两个实体,输出是描述这两个实体关系的标签或文本。 自然语言推理 Natural Language Inference (NLI):seq2cls,推理模型的文本输入:premise(前提) + hypothesis(假设) ,模型输出:对假设是否成立的判断结果,矛盾/包含(可推得)/中立(contradiction/entailment/neutral) NLP基础概念 以上是对一个样本推理过程涉及的几个关键步骤进行了介绍,在训练过程中组batch可能还会涉及连接词、填充词的预处理,这里暂不作说明。 下面总结一些NLP中常用的概念名词,便于理解任务。 词表(Vocabulary):文本数据集中出现的所有单词的集合。 语料库(Corpus):用于NLP任务的文本数据集合,可以是大规模的书籍、文章、网页等。 词嵌入(Word Embedding):将单词映射到低维连续向量空间的技术,用于捕捉单词的语义和语法信息。 停用词(Stop Words):在文本处理中被忽略的常见单词,如\"a\"、\"the\"、\"is\"等,它们通常对文本的意义贡献较小。 分词(Tokenization):将文本分割成一个个单词或标记的过程,为后续处理提供基本的单位。 词频(Term Frequency):在给定文档中,某个单词出现的次数。 逆文档频率(Inverse Document Frequency):用于衡量一个单词在整个语料库中的重要性,是将词频取倒数并取对数的值。 TF-IDF(Term Frequency-Inverse Document Frequency):一种常用的文本特征表示方法,综合考虑了词频和逆文档频率。 词袋模型(Bag of Words):将文本表示为一个单词的集合,忽略了单词的顺序和语法结构。 N-gram:连续的N个单词构成的序列,用于捕捉文本中的局部特征和上下文信息。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.2-rnn-lstm.html":{"url":"chapter-9/9.2-rnn-lstm.html","title":"9.2 文本分类-RNN-LSTM","keywords":"","body":"9.2 文本分类-RNN-LSTM 前言 本节介绍RNN和LSTM,并采用它们在电影评论数据集上实现文本分类,本小节会涉及以下几个知识点。 词表构建:包括数据清洗,词频统计,词频截断,词表构建 预训练词向量应用:下载并加载Glove的预训练embedding进行训练,主要是如何把词向量放到nn.embedding层中的权重。 RNN及LSTM构建:涉及nn.RNN和nn.LSTM的使用 任务介绍 本节采用的数据集是斯坦福大学的大型电影评论数据集(large movie review dataset) https://ai.stanford.edu/~amaas/data/sentiment/ 包含25000个训练样本,25000个测试样本,下载解压后得到aclImdb文件夹,aclImdb下有train和test,neg和pos下分别有txt文件,txt中为电影评论文本。 来看看一条具体的样本,train/pos/3_10.txt: \"All the world's a stage and its people actors in it\"--or something like that. Who the hell said that theatre stopped at the orchestra pit--or even at the theatre door? Why is not the audience participants in the theatrical experience, including the story itself?This film was a grand experiment that said: \"Hey! the story is you and it needs more than your attention, it needs your active participation\". \"Sometimes we bring the story to you, sometimes you have to go to the story.\"Alas no one listened, but that does not mean it should not have been said. 本节任务就是对这样的一条文本进行处理,输出积极/消极的二分类概率向量。 数据模块 文本任务与图像任务不同,输入不再是像素这样的数值,而是字符串,因此需要将字符串转为矩阵运算可接受的向量形式。 为此需要在数据处理模块完成以下步骤: 分词:将一长串文本切分为一个个独立语义的词,英文可用空格来切分。 词嵌入:词嵌入通常分两步。首先将词字符串转为索引序号,然后索引序号根据词嵌入矩阵(embedding层)取对应的向量。其中词与索引之间的映射关系需要提前构建,这就是词表构建的过程。 因此,代码开发整体流程: 编写分词功能函数 构建词表:对训练数据进行分词,统计词频,并构建词表。例如{'UNK': 0, 'PAD': 1, 'the': 2, '.': 3, 'and': 4, 'a': 5, 'of': 6, 'to': 7, ...} 编写PyTorch的Dataset,实现分词、词转序号、长度填充/截断 序号转词向量的过程由模型的nn.Embedding层实现,因此数据模块只需将词变为索引序号即可,接下来一一解析各环节核心功能代码实现。 词表构建 参考配套代码a_gen_vocabulary.py,首先编写分词功能函数,分词前做一些简单的数据清洗,例如在标点符号前加入空格、去除掉不是大小写字母及 .!? 符号的数据。 def text_split(content: str) -> List[str]: content = re.sub(r\"([.!?])\", r\" \\1\", content) # 在 .!? 之前添加一个空格 content = re.sub(r\"[^a-zA-Z.!?]+\", r\" \", content) # 去除掉不是大小写字母及 .!? 符号的数据 token = [i.strip().lower() for i in content.split()] # 全部转换为小写,然后去除两边空格,将字符串转换成list, return token 接着,写一个词表统计类实现词频统计,和词表字典的创建,代码注释非常详细,这里不赘述。 运行代码,即可完成词频统计,词表的构建,并保存到本地npy文件,在训练及推理过程中使用。 class Vocabulary: UNK_TAG = \"UNK\" # 遇到未知字符,用UNK表示 PAD_TAG = \"PAD\" # 用PAD补全句子长度 UNK = 0 # UNK字符对应的数字 PAD = 1 # PAD字符对应的数字 def __init__(self): self.inverse_vocab = None self.vocabulary = {self.UNK_TAG: self.UNK, self.PAD_TAG: self.PAD} self.count = {} # 统计词频、 def fit(self, sentence_: List[str]): def build_vocab(self, min=0, max=None, max_vocab_size=None) -> Tuple[dict, dict] 在词表构建过程中有一个截断数量的超参数需要设置,这里设置为20000,即最多有20000个词的表示,不在字典中的词被归为UNK这个词。 在这个数据集中,原始词表长度为74952,即通过split切分后,有7万多个不一样的字符串,通常可以通过降序排列,取前面一部分即可。 代码会输出词频统计图,也可以观察出词频下降的速度以及高频词是哪些。 Dataset编写 参考配套代码aclImdb_dataset.py,getitem中主要做两件事,首先获取label,然后获取文本预处理后的列表,列表中元素是词所对应的索引序号。 def __getitem__(self, item): # 读取文件路径 file_path = self.total_file_path[item] # 获取 label label = 0 if os.path.basename(os.path.dirname(file_path)) == \"neg\" else 1 # neg -> 0; pos -> 1 # tokenize & encode to index token_list = text_split(open(file_path, encoding='utf-8').read()) # 切分 token_idx_list = self.word2index.encode(token_list, self.vocab, self.max_len) return np.array(token_idx_list), label 在self.word2index.encode中需要注意设置文本最大长度self.max_len,这是由于需要将所有文本处理到相同长度,长度不足的用词填充,长度超出则截断。 模型模块——RNN 模型的构建相对简单,理论知识在这里不介绍,需要了解和温习的推荐看看《动手学》。这里借助动手学的RNN图片讲解代码的实现。 在构建的模型RNNTextClassifier中,需要三个子module,分别是: nn.Embedding:将词序号变为词向量,用于后续矩阵运算 nn.RNN:循环神经网络的实现 nn.Linear:最终分类输出层的实现 在forward时,流程如下: 获取词向量 构建初始化隐藏层,默认为全0 rnn推理获得输出层和隐藏层 fc层输出分类概率:fc层的输入是rnn最后一个隐藏层 def forward(self, x): x_embed = self.embedding(x) # [batch_size, max_len] -> [batch_size, text_len, embed_len] bs_, text_len, embed_len = x_embed.shape hidden_init = self.init_hidden(bs_) outputs, hidden = self.rnn(x_embed, hidden_init) # Extract the last hidden state last_hidden = hidden[-1].squeeze(0) # [num_layers, bs, hidden_size] -> [bs, hidden_size] fc_output = self.fc(last_hidden) return fc_output 更多关于nn.RNN的参数设置,可以参考官方文档:torch.nn.RNN(self, input_size, hidden_size, num_layers=1, nonlinearity='tanh', bias=True, batch_first=False, dropout=0.0, bidirectional=False, device=None, dtype=None) 模型模块——LSTM RNN是神经网络中处理时序任务最为经典的设计,但是其也存在一些缺点,例如梯度消失和梯度爆炸,以及长期依赖问题。 当序列很长时,RNN模型很难捕捉到远距离的依赖关系,导致模型预测不准确。 为此,带门控机制的RNN涌现,包括GRU(Gated Recurrent Unit,门控循环单元)和LSTM(Long Short-Term Memory,长短期记忆网络),其中LSTM应用最广,这里直接跳过GRU。 LSTM模型引入了三个门(input gate、forget gate和output gate),用于控制输入、输出和遗忘的流动,允许模型有选择性地忘记或记住一些信息。 input gate用于控制输入的流动 forget gate用于控制遗忘的流动 output gate用于控制输出的流动 相较于RNN,除了输出隐藏层向量h,还输出记忆层向量c,不过对于下游使用,不需要关心向量c的存在。 同样地,借助《动手学》中的LSTM示意图来理解代码。 在这里,借鉴《动手学》的代码,采用的LSTM为双向LSTM,这里简单介绍双向循环神经网络的概念。 双向循环神经网络(Bidirectional Recurrent Neural Network,Bi-RNN)同时考虑前向和后向的上下文信息,前向层和后向层的输出在每个时间步骤上都被连接起来,形成了一个综合的输出,这样可以更好地捕捉序列中的上下文信息。 在pytorch代码中,只需要将bidirectional设置为True即可,nn.LSTM(embed_size, num_hiddens, num_layers=num_layers, bidirectional=True)。 当采用双向时,需要注意output矩阵的shape为 [ sequence length , batch size ,2×hidden size] 更多关于nn.LSTM的参数设置,可以参考官方文档:torch.nn.LSTM(self, input_size, hidden_size, num_layers=1, bias=True, batch_first=False, dropout=0.0, bidirectional=False, proj_size=0, device=None, dtype=None) 详细参考:https://pytorch.org/docs/stable/generated/torch.nn.LSTM.html#torch.nn.LSTM embedding预训练加载 模型构建好之后,词向量的embedding层是随机初始化的,要从头训练具备一定逻辑关系的词向量表示是费时费力的,通常可以采用在大规模预料上训练好的词向量矩阵。 这里可以参考斯坦福大学的GloVe(Global Vectors for Word Representation)预训练词向量。 GloVe是一种无监督学习算法,用于获取单词的向量表示,GloVe预训练词向量可以有效地捕捉单词之间的语义关系,被广泛应用于自然语言处理领域的各种任务,例如文本分类、命名实体识别和机器翻译等。 Glove有四大类,根据数据量不同进行区分,相同数据下又根据向量长度分 Wikipedia 2014 + Gigaword 5 (6B tokens, 400K vocab, uncased, 50d, 100d, 200d, & 300d vectors, 822 MB download): glove.6B.zip Common Crawl (42B tokens, 1.9M vocab, uncased, 300d vectors, 1.75 GB download): glove.42B.300d.zip Common Crawl (840B tokens, 2.2M vocab, cased, 300d vectors, 2.03 GB download): glove.840B.300d.zip Twitter (2B tweets, 27B tokens, 1.2M vocab, uncased, 25d, 50d, 100d, & 200d vectors, 1.42 GB download): glove.twitter.27B.zip 在这里,采用Wikipedia 2014 + Gigaword 5 中的100d,即词向量长度为100,向量的token数量有6B。 下载好的GloVe词向量矩阵是一个txt文件,一行是一个词和词向量,中间用空格隔开,因此加载该预训练词向量矩阵可以这样。 def load_glove_vectors(glove_file_path, word2idx): \"\"\" 加载预训练词向量权重 :param glove_file_path: :param word2idx: :return: \"\"\" with open(glove_file_path, 'r', encoding='utf-8') as f: vectors = {} for line in f: split = line.split() word = split[0] vector = torch.FloatTensor([float(num) for num in split[1:]]) vectors[word] = vector return vectors 原始GloVe预训练词向量有40万个词,在这里只关心词表中有的词,因此可以在加载字典时加一行过滤,即在词表中的词,才去获取它的词向量。 if word in word2idx: vector = torch.FloatTensor([float(num) for num in split[1:]]) vectors[word] = vector 在本案例中,词表大小是2万,根据匹配,只有19720个词在GloVe中找到了词向量,其余的词向量就需要随机初始化。 获取GloVe预训练词向量字典后,需要把词向量放到embedding层中的矩阵,对弈embedding层来说,一行是一个词的词向量,因此通过词表的序号找到对应的行,然后把预训练词向量放进去即可,代码如下: word2idx = np.load(vocab_path, allow_pickle=True).item() # 词表顺序仍旧根据训练集统计得到的词表顺序 for word, idx in word2idx.items(): if word in glove_vectors: model.embedding.weight.data[idx] = glove_vectors[word] if args.is_freeze: model.embedding.weight.requires_grad = False # embedding层是否还需要更新 训练及实验记录 准备好了数据和模型,接下来按照常规模型训练即可。 这里将会做一些对比实验,包括模型对比: RNN vs LSTM 有预训练词向量 vs 无预训练词向量 冻结预训练词向量 vs 放开预训练词向量 具体指令如下,推荐放到bash文件中,一次性跑 python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode rnn --glove-file-path \"\" python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode rnn --glove-file-path /workspace/data/glove_6B/glove.6B.100d.txt --is-freeze python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode rnn --glove-file-path /workspace/data/glove_6B/glove.6B.100d.txt python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode lstm --glove-file-path \"\" python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode lstm --glove-file-path /workspace/data/glove_6B/glove.6B.100d.txt --is-freeze python train_main.py --data-path /workspace/data/aclImdb --batch-size 64 --epochs 50 --lr 0.01 --model-mode lstm --glove-file-path /workspace/data/glove_6B/glove.6B.100d.txt 实验结果如下所示: RNN整体不work,经过分析发现设置的文本token长度太长,导致RNN梯度消失,以至于无法训练。调整text_max_len为50后,train acc=0.8+, val=0.62,整体效果较差。 有了预训练词向量要比没有预训练词向量高出10多个点 放开词向量训练,效果会好一些,但是不明显 RNN(text_len=500) LSTM RNN(text_len=50) random init embedding 0.50 0.53, 0.70 0.58 FREEZE-glove 6B 100d 0.50 0.85 0.67 TRAIN-glove 6B 100d 0.50 0.88 0.67 补充实验:将RNN模型的文本最长token数量设置为50,其余保持不变,得到的三种embedding方式的结果如下: 结论: LSTM较RNN在长文本处理上效果更好 预训练词向量在小样本数据集上很关键,有10多个点的提升 放开与冻结embedding层训练,效果差不多 小结 本小节通过电影影评数据集实现文本分类任务,通过该任务可以了解: 文本预处理机制:包括清洗、分词、词频统计、词表构建、词表截断、UNK与PAD特殊词设定等 预训练词向量使用:包括GloVe的下载及加载、nn.embedding层的设置 RNN系列网络模型使用:大致了解循环神经网络的输入/输出是如何构建,如何配合fc层实现文本分类 RNN可接收的文本长度有限:文本过长,导致梯度消失,文本过短,导致无法捕获更多文本信息,因此推荐采用LSTM等门控机制的模型 这一小节是经典的seq2cls的任务,下一小节,将对seq2seq进行介绍。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.3-seq2seq.html":{"url":"chapter-9/9.3-seq2seq.html","title":"9.3 机器翻译-seq2seq","keywords":"","body":"9.3 机器翻译-Seq2Seq 前言 上一节通过影评数据分类任务介绍了seq2cls的场景,本节介绍seq2seq的任务——机器翻译。 机器翻译是典型的seq2seq任务,本节将采用传统基于RNN的seq2seq结构模型进行,seq2seq任务有一些特点,并且需要重点理解,包括 特殊token:, , , 。分别表示起始、结束、未知和填充。尤其是“起始”和\"结束\",它们是序列生成任务中重要的概念。 序列最大长度:序列最大长度直接影响模型性能,过长导致模型难以学习,过短导致无法完成任务,本案例选择长度为20。 Teacher forcing 教师强制学习:本概念也是序列生成任务中,在训练阶段所涉及的重要概念,表示decoder的输入采用标签,而非自回归式。 推理代码逻辑:模型推理输出序列时,采用的是自回归方式,因而代码与训练时要做修改。 任务介绍 机器翻译是经典的seq2seq任务,日常生活中也常用到翻译工具,因此任务比较好理解。例如以下几个句子就是本节要处理的任务。 That mountain is easy to climb. 那座山很容易爬。 It's too soon. 太早了。 Japan is smaller than Canada. 日本比加拿大小。 现在需要构建模型,接收英文句子序列,进行综合理解,然后输出中文句子序列,在神经网络中,通常采用encoder-decoder架构,例如《动手学》中的示意图。 编码器,对输入进行处理,获得对输入句子的综合理解信息——状态。 解码器,根据状态,以及解码器的输入,逐个token的生成输出,直到输出特殊token——,模型停止输出。 解码器的输入,采用自回归方式(推理时),含义是当前时刻的输入,来自上一时刻的输出,特别地,第0个时刻,是没有上一个时刻,所以采用特殊token——作为输入。 数据模块 数据下载 本案例数据集Tatoeba下载自https://www.manythings.org/anki/,该项目是帮助不同语言的人学习英语,因此是英语与其它几十种语言的翻译文本。 其中就包括本案例使用的英中文本,共计29668条(Mandarin Chinese - English cmn-eng.zip (29668)) 数据以txt形式存储,一行是一对翻译文本,例如长这样: 1.That mountain is easy to climb. 那座山很容易爬。 2.It's too soon. 太早了。 3.Japan is smaller than Canada. 日本比加拿大小。 数据集划分 对于29668条数据进行8:2划分为训练、验证,这里采用配套代码a_data_split.py进行划分,即可在统计目录下获得train.txt和text.txt。 词表构建 文本任务首要任务是为文本构建词表,这里采用与上节一样的方法,首先对文本进行分词,然后统计语料库中所有的词,最后根据最大上限、最小词频等约束,构建词表。本部分配套代码是b_gen_vocabulary.py 词表的构建过程中,涉及两个知识点:中文分词和特殊token。 1. 中文分词 对于英文,分词可以直接采用空格。而对于中文,就需要用特定的分词方法,这里采用的是jieba分词工具,以下是英文和中文的分词代码。 source.append(parts[0].split(' ')) target.append(list(jieba.cut(parts[1]))) # 分词 2. 特殊token 由于seq2seq任务的特殊性,在解码器部分,通常需要一个token告诉模型,现在是开始,同时还需要有个token让模型输出,以此告诉人类,模型输出完毕,不要再继续生成了。 因此相较于文本分类,还多了,, 两个特殊token,有的时候,开始token也会用表示。 PAD_TAG = \"\" # 用PAD补全句子长度 BOS_TAG = \"\" # 用BOS表示开始 EOS_TAG = \"\" # 用EOS表示结束 UNK_TAG = \"\" # 用EOS表示结束 PAD = 0 # PAD字符对应的数字 BOS = 1 # BOS字符对应的数字 EOS = 2 # EOS字符对应的数字 UNK = 3 # UNK字符对应的数字 运行代码后,词表字典保存到了result目录下,并得到如下输出,表明英文中有2518个词,中文有3365,但经过最大长度3000的截断后,只剩下2996,另外4个是特殊token。 100%|██████████| 23635/23635 [00:00 Dataset编写 NMTDataset的编写逻辑与上一小节的Dataset类似,首先在类初始化的时候加载原始数据,并进行分词;在getitem迭代时,再进行token转index操作,这里会涉及增加结束符、填充符、未知符。 核心代码如下: def __init__(self, path_txt, vocab_path_en, vocab_path_fra, max_len=32): self.path_txt = path_txt self.vocab_path_en = vocab_path_en self.vocab_path_fra = vocab_path_fra self.max_len = max_len self.word2index = WordToIndex() self._init_vocab() self._get_file_info() def __getitem__(self, item): # 获取切分好的句子list,一个元素是一个词 sentence_src, sentence_trg = self.source_list[item], self.target_list[item] # 进行填充, 增加结束符,索引转换 token_idx_src = self.word2index.encode(sentence_src, self.vocab_en, self.max_len) token_idx_trg = self.word2index.encode(sentence_trg, self.vocab_fra, self.max_len) str_len, trg_len = len(sentence_src) + 1, len(sentence_trg) + 1 # 有效长度, +1是填充的结束符 . return np.array(token_idx_src, dtype=np.int64), str_len, np.array(token_idx_trg, dtype=np.int64), trg_len def _get_file_info(self): text_raw = read_data_nmt(self.path_txt) text_clean = text_preprocess(text_raw) self.source_list, self.target_list = text_split(text_clean) 模型模块 seq2seq模型,由编码器和解码器两部分构成。 对于编码器,需要的是其对输入句子的全文理解,因此可采用RNN中输出的hidden state特征来表示,在这里均采用LSTM作为基础模型。 对于解码器,同样是一个LSTM,它接收3个数据,一个是输入,另外两个是hidden state和cell state。解码器的输入就是自回归式的,上一时刻输出的单词传到当前时刻。 这里借助《动手学》的示意图,理解seq2seq模型的样子。 首先构建一个EncoderLSTM,这个比较简单,只看它的forward,对于输入的句子x,输出hidden_state, cell_state。 def forward(self, x): # Shape -----------> (26, 32, 300) [Sequence_length , batch_size , embedding dims] embedding = self.dropout(self.embedding(x)) # Shape --> outputs (26, 32, 1024) [Sequence_length , batch_size , hidden_size] # Shape --> (hs, cs) (2, 32, 1024) , (2, 32, 1024) [num_layers, batch_size, hidden_size] outputs, (hidden_state, cell_state) = self.LSTM(embedding) return hidden_state, cell_state 然后构建一个DecoderLSTM,解码器除了需要对数据进行提特征,获得hidden_state, cell_state,还需要进行当前时刻,单词的输出,即token级的分类任务。 所以,它的forward返回有三个信息,包括输出的token预测向量,LSTM的hidden_state, cell_state,这里需要注意,在代码实现时,解码器输出的隐状态默认包括了来自编码器的,因此后续时间步不再需要从编码器拿隐状态特征了。更直观的就是,解码器返回的hidden_state, cell_state,会是下一次forward输入的hidden_state, cell_state。 def forward(self, x, hidden_state, cell_state): x = x.unsqueeze(0) # x.shape == [1, batch_size] embedding = self.dropout(self.embedding(x)) outputs, (hidden_state, cell_state) = self.LSTM(embedding, (hidden_state, cell_state)) predictions = self.fc(outputs) predictions = predictions.squeeze(0) return predictions, hidden_state, cell_state 最后,编写一个Seq2Seq类,将编码器和解码器有序的组合起来,在这里,核心任务是解码器中,如何有序的进行每个时刻的数据处理。 该Seq2Seq类用于训练阶段,会设计teacher forcing(强制学习)的概念,它表示在训练阶段,解码器的输入时自回归,还是根据标签进行输入,采用标签进行输入的方法称为teacher forcing。 这里仔细研究一下forward的后半部分——解码器部分,前半部分就是编码器进行一次性的编码,获得隐状态特征。 对于解码器,采用for循环,依次进行token输出,最终存储在outputs中,for循环需要设置最大步数,一般根据任务的数据长度而定,这里设置为32。 接着,解码器工作,解码器会输出:预测的token分类向量,隐状态信息,其中隐状态信息会在下一个for循环时,输入到解码器中。 再往下看,解码器的输入x,则是根据一个条件判断,以一定的概率采用标签,一定的概率采用自回归方式。这里概率通常设置为0.5,如果设置为1,表明采用强制学习,永远采用标签作为输入,也就是强制学习机制(teacher forcing)。 # Shape of x (32 elements) x = target[0] # token for i in range(1, target_len): # output.shape == (bs, vocab_length) # hidden_state.shape == [num_layers, batch_size size, hidden_size] output, hidden_state, cell_state = self.Decoder_LSTM(x, hidden_state, cell_state) outputs[i] = output best_guess = output.argmax(1) # 0th dimension is batch size, 1st dimension is word embedding x = target[i] if random.random() outputs (14, 32, 5766) return outputs 模型训练 数据和模型准备好之后,可以运行train_seq2seq.py进行训练,seq2seq的训练还有些许不同,主要包括: 采用blue进行指标评价; 损失函数加入ignore_index BLEU是IBM在2002提出的,用于机器翻译任务的评价,发表在ACL,引用次数10000+,原文题目是“BLEU: a Method for Automatic Evaluation of Machine Translation”。 它的总体思想就是准确率,假如给定标准译文reference,模型生成的句子是candidate,句子长度为n,candidate中有m个单词出现在reference,m/n就是bleu的1-gram的计算公式。 当统计不再是一个单词,而是连续的N个单词时,就有了n-gram的概念,词组的概念称为n-gram,词组长度通常选择1, 2, 3, 4 举一个例子来看看实际的计算: candinate: the cat sat on the mat reference: the cat is on the mat BLEU-1: 5/6 = 0.83 BLEU-2: 3/5 = 0.6 BLEU-3: 1/4 = 0.25 BLEU-4: 0/3 = 0 分子表示candidate中预测到了的词组的次数,如BLEU-1中,5分别表示, the, cat, on, the, mat预测中了。BLEU-2中,3分别表示, the cat, on the, the mat预测中了。以此类推。 针对BLEU还有些改进计算方法,可参考BLEU详解 由于句子长度不一致,而训练又需要将数据构造成统一长度的句子来组batch,因此会加入很多特殊token——,对应的index是0,所以会在计算损失函数的时候,这部分的loss是不计算的,可以巧妙的通过设置ignore_index来实现。 nn.CrossEntropyLoss(ignore_index=0) 由于数据量少,以及模型参数未精心设计,模型存在过拟合,这里仅作为seq2seq任务的学习和理解,不对模型性能做进一步提升,因为后续有更强大的解决方案——基于Transformer。 模型推理 运行配套代码c_inference.py,可以看到模型的表现如下所示,整体上像一个模型翻译的样子,但效果远不如意,这里包含多方面原因。 数据量过少,训练数据仅2万多条,而中英文的词表就3000多 模型过于简单,传统RNN构建的seq2seq在上下文理解能力上表现欠佳,后续可加入注意力机制,或是基于Transformer架构来提升。 输入: he didn't answer the phone , so i sent him an email . 标签:他 没有 , 所以 我 给 他 发 了 。 翻译:我 不 , 所以 我 他 他 他 。 输入: just as he was going out , there was a great earthquake . 标签:就 在 他 要 出門 的 時候 , 發生 了 。 翻译:他 他 的 , 但 他 。 输入: tom hugged mary . 标签:汤姆 拥抱 了 玛丽 。 翻译: 了 玛丽 。 输入: there are many americans who can speak japanese . 标签:有 很多 美国 说 日语 。 翻译:有 能 非常 。 输入: i'm not good at . 标签:我 不 擅長 。 翻译:我 不 太 擅长 运动 。 小结 本节通过机器翻译了解seq2seq任务,主要涉及一些特殊的、新的知识点,包括: 特殊token:解码器需要两个特殊的token,起始token和结束token,用于控制生成序列的起始和结束。 强制学习:训练时,解码器的输入采用标签 编码器-解码器的自回归推理逻辑:需要for循环的形式,串行的依次生成句子中的每个单词 中文分词工具jieba Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.4-transformer.html":{"url":"chapter-9/9.4-transformer.html","title":"9.4 机器翻译-Transformer","keywords":"","body":"9.4 机器翻译—Transformer 前言 本节将介绍当下人工智能领域的基石与核心结构模型——Transformer,为什么说它是基石,因为以ChatGPT为代表的聊天机器人以及各种有望通向AGI(通用人工智能)的道路上均在采用的Transformer。 Transformer也是当下NLP任务的底座,包括后续的BERT和GPT,都是Transformer架构,BERT主要由Transformer的encoder构成,GPT则主要是decoder构成。 本节将会通读Transformer原论文《Attention is all you need》,总结Transformer结构以及文章要点,然后采用pytorch代码进行机器翻译实验,通过代码进一步了解Transformer在应用过程中的步骤。 论文阅读笔记 Transformer的论文是《Attention is all you need》(https://arxiv.org/abs/1706.03762),由Google团队在2017提出的一种针对机器翻译的模型结构,后续在各类NLP任务上均获取SOTA。 Motivation:针对RNN存在的计算复杂、无法串行的问题,提出仅通过attention机制的简洁结构——Transformer,在多个序列任务、机器翻译中获得SOTA,并有运用到其他数据模态的潜力。 模型结构 模型由encoder和decoder两部分构成,分别采用了6个block堆叠, encoder的block有两层,multi-head attention和FC层 decoder的block有三层,处理自回归的输入masked multi-head attention,处理encoder的attention,FC层。 注意力机制:scale dot-production attention,采用QK矩阵乘法缩放后softmax充当权重,再与value进行乘法。 多头注意力机制:实验发现多个头效果好,block的输出,把多个头向量进行concat,然后加一个FC层。因此每个头的向量长度是总长度/头数,例:512/8=64,每个头是64维向量。 Transformer的三种注意力层: encoder:输入来自上一层的全部输出 decoder-输入:为避免模型未卜先知,只允许看见第i步之前的信息,需要做mask操作,确保在生成序列的每个元素时,只考虑该元素之前的元素。这里通过softmax位置设置负无穷来控制无效的连接。 decoder-第二个attention:q来自上一步输出,k和v来自encoer的输出。这样解码器可以看到输入的所有序列。 FFN层:attention之后接入两个FC层,第一个FC层采用2048,第二个是512,第一个FC层采用max(0, x)作为激活函数。 embedding层的缩放:在embedding层进行尺度缩放,乘以根号d_model, 位置编码:采用正余弦函数构建位置向量,然后采用加法,融入embedding中。 实验:两个任务,450万句子(英-德),3600万句子(英-法),8*16GB显卡,分别训练12小时,84小时。10万step时,采用4kstep预热;采用标签平滑0.1. 重点句子摘录 In the Transformer this is reduced to a constant number of operations, albeit at the cost of reduced effective resolution due to averaging attention-weighted positions, an effect we counteract with Multi-Head Attention as described in section 3.2. 由于注意力机制最后的加权平均可能会序列中各位置的细粒度捕捉不足,因此引入多头注意力机制。 这里是官方对多头注意力机制引入的解释。 \\2. We suspect that for large values of dk, the dot products grow large in magnitude, pushing the softmax function into regions where it has extremely small gradients 4. To counteract this effect, we scale the dot products by 根号dk 在Q和K做点积时,若向量维度过长,会导致点积结果过大,再经softmax映射后,梯度会变小,不利于模型学习,因此需要进行缩放。缩放因子为除以根号dk。 多头注意力机制的三种情况: encoder:输入来自上一层的全部输出 decoder-输入:为避免模型未卜先知,只允许看见第i步之前的信息,需要做mask操作,确保在生成序列的每个元素时,只考虑该元素之前的元素。这里通过softmax位置设置负无穷来控制无效的连接。 decoder-第二个attention:q来自上一步输出,k和v来自encoer的输出。这样解码器可以看到输入的所有序列。 首次阅读遗留问题 位置编码是否重新学习? ​ 不重新学习,详见 PositionalEncoding的 self.register_buffer('pos_table', self._get_sinusoid_encoding_table(n_position, d_hid)) qkv具体实现过程 通过3个w获得3个特征向量: attn = torch.matmul(q / self.temperature, k.transpose(2, 3)) # q.shape == (bs, n_head, len_seq, d_k/n_head) ,每个token用 一个向量表示,总向量长度是头数*每个头的向量长度。 attn = self.dropout(F.softmax(attn, dim=-1)) output = torch.matmul(attn, v) decoder输入的处理细节 训练阶段,无特殊处理,一个样本可以直接输入,根据下三角mask避免未卜先知 推理阶段,首先手动执行token的输入,然后for循环直至最大长度,期间输出的token拼接到输出列表中,并作为下一步decoder的输入。选择输出token时还采用了beam search来有效地平衡广度和深度。 数据集构建 数据下载 本案例数据集Tatoeba下载自这里。该项目是帮助不同语言的人学习英语,因此是英语与其它几十种语言的翻译文本。 其中就包括本案例使用的英中文本,共计29668条(Mandarin Chinese - English cmn-eng.zip (29668)) 数据以txt形式存储,一行是一对翻译文本,例如长这样: 1.That mountain is easy to climb. 那座山很容易爬。 2.It's too soon. 太早了。 3.Japan is smaller than Canada. 日本比加拿大小。 数据集划分 对于29668条数据进行8:2划分为训练、验证,这里采用配套代码a_data_split.py进行划分,即可在统计目录下获得train.txt和text.txt。 词表构建 文本任务首要任务是为文本构建词表,这里采用与上节一样的方法,首先对文本进行分词,然后统计语料库中所有的词,最后根据最大上限、最小词频等约束,构建词表。本部分配套代码是b_gen_vocabulary.py 词表的构建过程中,涉及两个知识点:中文分词和特殊token。 1. 中文分词 对于英文,分词可以直接采用空格。而对于中文,就需要用特定的分词方法,这里采用的是jieba分词工具,以下是英文和中文的分词代码。 source.append(parts[0].split(' ')) target.append(list(jieba.cut(parts[1]))) # 分词 2. 特殊token 由于seq2seq任务的特殊性,在解码器部分,通常需要一个token告诉模型,现在是开始,同时还需要有个token让模型输出,以此告诉人类,模型输出完毕,不要再继续生成了。 因此相较于文本分类,还多了bos, eos,两个特殊token,有的时候,开始token也会用start表示。 PAD_TAG = \"\" # 用PAD补全句子长度 BOS_TAG = \"\" # 用BOS表示开始 EOS_TAG = \"\" # 用EOS表示结束 UNK_TAG = \"\" # 用EOS表示结束 PAD = 0 # PAD字符对应的数字 BOS = 1 # BOS字符对应的数字 EOS = 2 # EOS字符对应的数字 UNK = 3 # UNK字符对应的数字 运行代码后,词表字典保存到了result目录下,并得到如下输出,表明英文中有2518个词,中文有3365,但经过最大长度3000的截断后,只剩下2996,另外4个是特殊token。 100%|██████████| 23635/23635 [00:00Dataset编写 NMTDataset的编写逻辑与上一小节的Dataset类似,首先在类初始化的时候加载原始数据,并进行分词;在getitem迭代时,再进行token转index操作,这里会涉及增加结束符、填充符、未知符。 核心代码如下: def __init__(self, path_txt, vocab_path_en, vocab_path_fra, max_len=32): self.path_txt = path_txt self.vocab_path_en = vocab_path_en self.vocab_path_fra = vocab_path_fra self.max_len = max_len self.word2index = WordToIndex() self._init_vocab() self._get_file_info() def __getitem__(self, item): # 获取切分好的句子list,一个元素是一个词 sentence_src, sentence_trg = self.source_list[item], self.target_list[item] # 进行填充, 增加结束符,索引转换 token_idx_src = self.word2index.encode(sentence_src, self.vocab_en, self.max_len) token_idx_trg = self.word2index.encode(sentence_trg, self.vocab_fra, self.max_len) str_len, trg_len = len(sentence_src) + 1, len(sentence_trg) + 1 # 有效长度, +1是填充的结束符 . return np.array(token_idx_src, dtype=np.int64), str_len, np.array(token_idx_trg, dtype=np.int64), trg_len def _get_file_info(self): text_raw = read_data_nmt(self.path_txt) text_clean = text_preprocess(text_raw) self.source_list, self.target_list = text_split(text_clean) 模型构建 Transformer代码梳理如下图所示,大体可分为三个层级 Transformer的构建,包含encoder、decoder两个模块,以及两个mask构建函数 两个coder内部实现,包括位置编码、堆叠的block block实现,包含多头注意力、FFN,其中多头注意力将softmax(QK)*V拆分为ScaledDotProductAttention类。 代码整体与论文中保持一致,总结几个不同之处: layernorm使用时机前置到attention层和FFN层之前 position embedding 的序列长度默认采用了200,如果需要更长的序列,则要注意配置。 具体代码实现不再赘述,把论文中图2的结果熟悉,并掌握上面的代码结构,可以快速理解各模块、组件的运算和操作步骤,如有疑问的点,再打开代码观察具体运算过程即可。 模型训练 原论文进行两个数据集的机器翻译任务,采用的数据和超参数列举如下,供参考。 英语-德语,450万句子对,英语-法语,3600万句子对。均进行base/big两种尺寸训练,分别进行10万step和30万step训练,耗时12小时/84小时。10万step时,采用4千step进行warmup。正则化方面采用了dropout=0.1的残差连接,0.1的标签平滑。 本实验中有2.3万句子对训练,只能作为Transformer的学习,性能指标仅为参考,具体任务后续由BERT、GPT、T5来实现更为具体的项目。 采用配套代码train_transformer.py,执行训练即可: python train_transformer.py -embs_share_weight -proj_share_weight -label_smoothing -b 256 -warmup 128000 -epoch 400 训练完成后,在result文件夹下会得到日志与模型,接下来采用配套代码c_train_curve_plot.py对日志数据进行绘图可视化,Loss和Accuracy分别如下,可以看出模型拟合能力非常强,性能还在提高,但受限于数据量过少,模型在200个epoch之后就已经出现了过拟合。 这里面的评估指标用的acc,具体是直接复用github项目,也能作为模型性能的评估指标,就没有去修改为BLUE。 模型推理 Transformer的推理过程与训练时不同,值得仔细学习。 Transformer的推理输出是典型的自回归(Auto regressive),并且需要根据多个条件综合判断何时停止,因此推理部分的逻辑值得认真学习,具体步骤如下: 第一步:输入序列经encoder,获得特征,每个token输出一个向量,这个特征会在decoder的每个step都用到,即decoder中各block的第二个multi-head attention。需要enc_output去计算k和v,用decoder上一层输出特征向量去计算q,以此进行decoder的第二个attention。 enc_output, *_ = self.model.encoder(src_seq, src_mask) 第二步:手动执行decoder第一步,输入是这个token,输出的是一个概率向量,由分类概率向量再决定第一个输出token。 self.register_buffer('init_seq', torch.LongTensor([[trg_bos_idx]])) dec_output = self._model_decode(self.init_seq, enc_output, src_mask) 第三步:循环max_length次执行decoder剩余步。第i步时,将前序所有步输出的token,组装为一个序列,输入到decoder。 在代码中用一个gen_seq维护模型输出的token,输入给模型时,只需要gen_seq[:, :step]即可,很巧妙。 在每一步输出时,都会判断是否需要停止输出字符。 for step in range(2, max_seq_len): dec_output = self._model_decode(gen_seq[:, :step], enc_output, src_mask) 略 if (eos_locs.sum(1) > 0).sum(0).item() == beam_size: _, ans_idx = scores.div(seq_lens.float() ** alpha).max(0) ans_idx = ans_idx.item() break 借助李宏毅老师2021年课件中一幅图,配合代码,可以很好的理解Transformer在推理时的流程。 输入序列经encoder,获得特征(绿色、蓝色、蓝色、橙色) decoder输入第一个序列(序列长度为1,token是),输出第一个概率向量,并通过max得到“机”。 decoder输入第二个序列(序列长度为2,token是[BOS, 机]),输出得到“器” decoder输入第三个序列(序列长度为3,token是[BOS, 机,器]),输出得到“学” decoder输入第四个序列(序列长度为4,token是[BOS, 机,器,学]),输出得到“习” ...以此类推 在推理过程中,通常还会使用Beam Search 来最终确定当前步应该输出哪个token,此处不做展开。 运行配套代码inference_transformer.py可以看到10条训练集的推理结果。 从结果上看,基本像回事儿了。 src: tom's door is open . trg: 湯姆 的 門開 著 。 pred: 汤姆 的 了 。 src: is a country . trg: 是 一個 的 國家 。 pred: 是 一個 的 城市 。 src: i can come at three . trg: 可以 來 。 pred: 我 可以 在 那裡 。 小结 本节通过Transformer论文的学习,了解Transformer基础架构,并通过机器翻译案例,从代码实现的角度深入剖析Transformer训练和推理的过程。由于Transformer是目前人工智能的核心与基石,因此需要认真、仔细的掌握其中细节。 本节内容值得注意的知识点: 多头注意力机制的引入:官方解释为,由于注意力机制最后的加权平均可能会序列中各位置的细粒度捕捉不足,因此引入多头注意力机制 注意力计算时的缩放因子:QK乘法后需要缩放,是因为若向量维度过长,会导致点积结果过大,再经softmax映射后,梯度会变小,不利于模型学习,因此需要进行缩放。缩放因子为除以根号dk。 多头注意力机制的三种情况: encoder:输入来自上一层的全部输出 decoder-输入:为避免模型未卜先知,只允许看见第i步之前的信息,需要做mask操作,确保在生成序列的每个元素时,只考虑该元素之前的元素。这里通过softmax位置设置负无穷来控制无效的连接。 decoder-第二个attention:q来自上一步输出,k和v来自encoer的输出。这样解码器可以看到输入的所有序列。 输入序列的msk:代码实现时,由于输入数据是通过添加来组batch的,并且为了在运算时做并行运算,因此需要对src中是pad的token做mask,这一点在论文是不会提及的。 decoder的mask:根据下三角mask避免未卜先知。 推理时会采用beam search进行搜索,确定输出的token。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.5-bert.html":{"url":"chapter-9/9.5-bert.html","title":"9.5 命名实体识别-BERT","keywords":"","body":"9.5 命名实体识别—BERT 前言 本节介绍一个NLP领域划时代意义的模型——BERT(Bidirectional Encoder Representations from Transformers),BERT论文目前的引用超9万次!(2024年2月),BERT模型架构是的NLP中各类任务也可以使用预训练+微调范式(类似CV领域)。 本节将简牍BERT论文,然后介绍采用BERT进行NER(Named Entity Recognition,命名实体识别)任务的微调,通过论文了解BERT的发展,同时通过具体下游任务,了解如何对预训练好的BERT模型进行微调,来适应下游任务。本节不涉及BERT的预训练。 BERT论文略读 《BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding》(https://arxiv.org/abs/1810.04805) 摘要:前人优秀工作仅用了单向信息且不能很好的应用到各类下游任务,本文提出一种基于Transformer的双向处理预训练模型——BERT,在预训练完成后,采用统一的结构进行多个NLP下游任务微调,均达到SOTA。 BERT模型关键词:预训练;双向信息;MLM(Masked Language Model)预训练任务;NSP(Next Sentence Predict)预训练任务 预训练相关工作 BERT之前的ELMo和GPT都是预训练机制,并且取得不错成绩,但他们都是基于单向的,存在缺点。 BERT为了解决单向预训练带来的不足,引入了MLM和NSP两个预训练任务,让模型能够从双向来理解语言。 BERT模型结构 BERT的构建与使用分为两个阶段,预训练和微调。所有下游任务微调时,均采用预训练好的参数进行全局初始化、全局训练。 BERT模型结构很简单,完全基于Transformer的encoder,并且有base和large两个版本,attention block、hidden size 和 head分别为(L=12, H=768, A=12, Total Parameters=110M) (L=24, H=1024,A=16, Total Parameters=340M)。 BERT的输入设计很巧妙,使得一个结构适应了多个NLP任务。输入设计为序列形式,将一个句子、两个句子都组装成为一个序列,输入到模型中。输入上,设计了两个特殊的token,cls和sep。 cls:可以理解为序列的全局特征,用于文本分类、情感分析这类的seq2cls的任务。 sep:用于将句子1和句子2进行拼接的token 在embedding处理上,设计了额外的segment embedding来标记句子是第一句、还是第二句。具体的输入embedding由三部分组成,如下图所示: BERT的预训练——MLM BERT的第一种预训练任务是MLM(masked language model),是对一句话中的一些单词进行隐藏,然后让模型根据上下文内容,在该mask的token位置上要求预测该单词。例如:“白切鸡” 经过MLM处理变为 “白mask鸡”,输入到BERT模型,BERT模型的输出标签是“白切鸡“。 在进行mask是需要一定概率的,文章中对全文的15%的token进行遮罩,然后这15%里,80%真正变为mask,10%为随机token,10%为原始token。这么做的原因是,下游任务中并没有mask这个特殊token,为了保障微调时的性能,这里做了这样的设置。( a downside is that we are creating a mismatch between pre-training and fine-tuning, since the [MASK] token does not appear during fine-tuning. ) BERT的预训练——NSP BERT的第一种预训练任务是NSP(Next Sentence Prediction),由于NLP任务中有一些是需要理解两个句子之间的关系,例如QA和NLI任务。为了让BERT掌握句子之间的理解能力,设计了NSP。 NSP是一个二分类任务。输入的是两个句子组成的序列,输出的是IsText or Not Text。含义是这两个句子是否是前后两句。论文举的例子: Input = [CLS] the man went to [MASK] store [SEP] he bought a gallon [MASK] milk [SEP] Label = IsNext Input = [CLS] the man [MASK] to the store [SEP] penguin [MASK] are flight ##less birds [SEP] Label = NotNext 预训练实验 预训练采用了2个数据集:BooksCorpus (800M words) (Zhu et al.,2015) and English Wikipedia (2,500M words)。 预训练参数及耗时如下:bs=256, epoch=40, 100万step, 1万step预热, lr=1e-4, base:16个TPU训4天, large:64个TPU训4天。 BERT的微调——下游任务 有了预训练好的BERT模型,可以快速方便的应用到各类NLP的下游任务,直接看下图列举的四种典型的任务: (1)seq2cls:输入是多个句子,用sep拼接,输出用cls的特征向量接softmax实现分类。 (2)seq2cls:输入是单个句子,处理同上。 (3)seq2seq:输入是两段话构成的序列,输出是第二段话中要求各token输出3个类别,用于标记哪些是答案的开始、答案的结束和无关内容。可以理解为词表为3的序列生成任务。 (4)seq2seq:输入是一句话,输出是每个token的分类类别,类别数根据任务而定,例如NER任务中,类别数是(实体种类*2 + 1),一个实体需要两个类别,实体开始和实体结束两个标记,1表示无关类别。 下游任务微调实验 超参数基本固定,可以套用大部分任务: Learning rate (Adam): 5e-5, 3e-5, 2e-5 Number of epochs: 2, 3, 4 Batch size: 16, 32 且采用的计算资源也很少,单个TPU一小时,单GPU几个小时即可,真实亲民的好模型。 论文小结 BERT是在ELMo和GPT之后提出来的一种基于Transformer的Encoder实现双向信息交互的预训练架构,并且可在一个模型结构上实现多种下游任务的微调,具有统一结构。BERT对于NLP的预训练-微调,算得上开创性的作品,为NLP微调范式打开了大门,后续的NLP任务大多基于BERT范式。 本论文需重点学习的几点如下: BERT最大亮点是双向信息交互和MLM+NSP的预训练-微调范式; MLM执行时,15%选中为mask候选token,再根据8:1:1比例进行真mask、随机token、原始token的设置 NSP任务采用sep特殊的token来拼接句子,实现一个序列输入,包含多个句子,sep的引入为模型统一多个任务提供了可能 预训练耗时4天,微调仅需数小时,此方案非常亲民。 NER任务简介 命名实体识别(Named Entity Recognition,简称 NER)是自然语言处理(NLP)中的一个基础任务,旨在识别文本中具有特定意义的实体,如人名、地名、组织名、时间、金额等。NER 通常用于信息提取、知识图谱构建、文本分类等任务的前置任务,属于基础任务。 NER数据的标注通常采用BIO体系来对文本中的每个token进行逐token的打标签(三分类),Begin表示实体开头,Inside表示实体中间/结尾,Outside表示非实体。简而言之,NER是逐token的多分类问题。分类类别等于(实体种类*2 + 1),一个实体需要B和I两个类别标记,所有实体共用一个无关类别O。 具体地,看一个文本: “苹果公司计划在2023年在中国深圳建立一家新工厂”,假设有三种实体类别,分别是组织、地名、时间。那么对应的NER训练标签为B-ORG, I-ORG, O, O, B-TIME, O, B-LOC, I-LOC, O, O, O, O。对应关系是 苹果 公司 计划 在 2023年 在 中国 深圳 建立 一家 新 工厂,由此可知“苹果公司”是组织, “2023年”是时间, “中国深圳”是地名。 接下来将会采用CLUENER 细粒度命名实体识别数据进行实验。 数据集构建 数据集下载 根据BERT论文的微调示意图和上文对NER的介绍,已经知道NER的数据制作目标是做一个token级别的多分类任务,下面就来观察数据。 首先下载CLUENER2020(https://github.com/CLUEbenchmark/CLUENER2020), 得到cluener_public.zip,解压后得到3个主要的json,分别是训练集、验证集和测试集。其中测试集没有标注标签。 以一条具体的数据介绍,一条数据包括原文本text,标签label,label中存在多个实体的标注,以字典形式,key是实体类别,value是具体内容以及字符的开始与结束字符。 {\"text\": \"虚幻引擎3动作游戏《黑光》新作公布\", \"label\": {\"game\": {\"《黑光》\": [[9, 12]]}}} 数据分为10个标签类别,分别为: 地址(address),书名(book),公司(company),游戏(game),政府(goverment),电影(movie),姓名(name),组织机构(organization),职位(position),景点(scene)。 在代码中对应的模型标签为(B表示Begin, I表示inside, O表示outside): [\"X\", \"B-address\", \"B-book\", \"B-company\", 'B-game', 'B-government', 'B-movie', 'B-name', 'B-organization', 'B-position','B-scene',\"I-address\", \"I-book\", \"I-company\", 'I-game', 'I-government', 'I-movie', 'I-name', 'I-organization', 'I-position','I-scene', \"S-address\", \"S-book\", \"S-company\", 'S-game', 'S-government', 'S-movie', 'S-name', 'S-organization', 'S-position', 'S-scene','O',\"[START]\", \"[END]\"] 整套代码借鉴https://github.com/lonePatient/BERT-NER-Pytorch,因此直接分析数据集编写部分,是如何将原始json解析为模型需要的形式。 数据集解析 根据utils_ner.py中的read_json函数,可以看出对json中的数据是如何做处理的。 最终数据被解析为: words = ['万', '通', '地', '产', '设', '计', '总', '监', '刘', '克', '峰', ';'] labels = ['B-company', 'I-company', 'I-company', 'I-company', 'B-position', 'I-position', 'I-position', 'I-position', 'B-name', 'I-name', 'I-name', 'O'] def _read_json(self,input_file): lines = [] with open(input_file, 'r', encoding='utf-8') as f: for line in f: line = json.loads(line.strip()) text = line['text'] label_entities = line.get('label',None) words = list(text) labels = ['O'] * len(words) if label_entities is not None: for key,value in label_entities.items(): for sub_name,sub_index in value.items(): for start_index,end_index in sub_index: assert ''.join(words[start_index:end_index+1]) == sub_name if start_index == end_index: labels[start_index] = 'S-'+key else: labels[start_index] = 'B-'+key labels[start_index+1:end_index+1] = ['I-'+key]*(len(sub_name)-1) lines.append({\"words\": words, \"labels\": labels}) return lines DataSet构建 对于BERT模型输入需要有3个内容,分别是token的index, mask的index, segment的index,下面是一条具体数据的情况。 02/22/2024 23:05:43 - INFO - processors.ner_seq - tokens: [CLS] 浙 商 银 行 企 业 信 贷 部 叶 老 桂 博 士 则 从 另 一 个 角 度 对 五 道 门 槛 进 行 了 解 读 。 叶 老 桂 认 为 , 对 目 前 国 内 商 业 银 行 而 言 , [SEP] 02/22/2024 23:05:43 - INFO - processors.ner_seq - input_ids: 101 3851 1555 7213 6121 821 689 928 6587 6956 1383 5439 3424 1300 1894 1156 794 1369 671 702 6235 2428 2190 758 6887 7305 3546 6822 6121 749 6237 6438 511 1383 5439 3424 6371 711 8024 2190 4680 1184 1744 1079 1555 689 7213 6121 5445 6241 8024 102 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 02/22/2024 23:05:44 - INFO - processors.ner_seq - input_mask: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 02/22/2024 23:05:44 - INFO - processors.ner_seq - segment_ids: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 02/22/2024 23:05:44 - INFO - processors.ner_seq - label_ids: 31 3 13 13 13 31 31 31 31 31 7 17 17 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 在这里会用到预训练模型的字典来做tokens到input_ids的映射,例如[CLS]就是101类,[PAD]是0类,浙是3851类。 以上功能在以下两个地方体现。 run_ner_softmax.py 的 train_dataset = load_and_cache_examples(args, args.task_name,tokenizer, data_type='train') processors/ner_seq.py 的 features = convert_examples_to_features(examples=examples, 在此份代码中,采用了 dataset = TensorDataset(all_input_ids, all_input_mask, all_segment_ids, all_lens,all_label_ids) 的方式构建dataset。 模型构建 BERT模型已成为NLP任务中常用的模型,因此已得到很好的集成,目前大多采用HuggingFace的transformers库进行构建BERT。 构建BERT模型有三要素,分别是配置文件,模型本体,tokenizer。 配置文件BertConfig 配置文件用class transformers.BertConfig封装,包含以下主要内容,从参数大概就明白,BERT模型的大小、向量大小、层数等都在这里面配置。 通常这个配置内容是跟着预训练模型的信息走的,需要从磁盘中加载config.json文件,例如本案例使用的预训练模型是从https://huggingface.co/google-bert/bert-base-chinese/tree/main下载,里边就包含了配置文件。 def __init__( self, vocab_size=30522, hidden_size=768, num_hidden_layers=12, num_attention_heads=12, intermediate_size=3072, hidden_act=\"gelu\", hidden_dropout_prob=0.1, attention_probs_dropout_prob=0.1, max_position_embeddings=512, type_vocab_size=2, initializer_range=0.02, layer_norm_eps=1e-12, pad_token_id=0, position_embedding_type=\"absolute\", use_cache=True, classifier_dropout=None, **kwargs ): 模型本体 BERT本身这个nn.Module就复杂一些,除了实现BERT运算功能,还涉及huggingface的协议、功能,所以最终应用的模型类BertSoftmaxForNer是继承了官方BertPreTrainedModel,并且内部定义了官方BertModel模型,用于BERT的运算;同时在BertSoftmaxForNer内部,进行适当的代码修改,用于实现下游任务以处理逻辑。 类代码具体的定义与关系可参考如下UML示意图:BertSoftmaxForNer是核心,往左边是BertModel模型的核心,BertModel与上节Transformer一样,拆分了多个子模块来构建,因此第一列整体是BERT的构建,值得注意的是三个embedding的构建。 捋清楚模型类定义后,再看模型初始化就会清楚真正用的模型是怎么来的: model = model_class.from_pretrained(args.model_name_or_path, config=config) 根据config配置,模型权重文件,借助PreTrainedModel类的from_pretrained()函数,实现模型定义、权重加载。 模型训练 模型训练采用run_ner_softmax.py,主要修改预训练文件路径、数据集路径、输出路径即可,具体的运行参数如下所示 --model_type=bert --model_name_or_path=./bert-base-pretrained --task_name=cluener --do_lower_case --loss_type=ce --do_train --do_eval --data_dir=G:\\deep_learning_data\\cluener_public --train_max_seq_length=128 --eval_max_seq_length=512 --per_gpu_train_batch_size=64 --per_gpu_eval_batch_size=64 --learning_rate=3e-5 --num_train_epochs=4.0 --logging_steps=20 --save_steps=224 --output_dir=D:\\github_desktop\\BERT-NER-Pytorch\\outputs --overwrite_output_dir --seed=42 BERT的微调大多是4epoch,很快就见效,笔记本上训练大约10分即可完成,接下来观察一下训练效果。 首先来看loss曲线,运行01_loss_curve_plot.py,可以查看loss曲线变化,如下图所示: 从曲线看,loss下降很快,并且迅速到达了平台期,F1快速达到了0.79,训练效果较好。 补充说明:本节的训练代码写得不是很优雅,就不过多讨论,后续应该不会再遇到类似结构的代码。 模型推理 模型训练好之后,来看看在测试集上的推理效果。 首先,run_ner_softmax.py代码已经实现推理预测功能,把--do_train --do_eval去掉,保留 --do_predict即可得到test_prediction.json。 然后,运行02_predict_parse.py,可以看到解析结果。例如: 从文本\"四川敦煌学”。近年来,丹棱县等地一些不知名的石窟迎来了海内外的游客,他们随身携带着胡文和的著作。\"中,提取到实体 organization \"四川敦煌学\", address \"丹棱县\", name \"胡文和\", 从文本\"尼日利亚海军发言人当天在阿布贾向尼日利亚通讯社证实了这一消息。\"中,提取到实体 government \"尼日利亚海军\", address \"阿布贾\", company \"尼日利亚通讯社\", 从文本\"销售冠军:辐射3-Bethesda\"中,提取到实体 game \"辐射3\", 从文本\"所以大多数人都是从巴厘岛南部开始环岛之旅。\"中,提取到实体 scene \"巴厘岛\", 从文本\"备受瞩目的动作及冒险类大作《迷失》在其英文版上市之初就受到了全球玩家的大力追捧。\"中,提取到实体 game \"《迷失》\", 从文本\"filippagowski:14岁时我感觉自己像梵高\"中,提取到实体 name \"filippagowski\", name \"梵高\", 小结 本节通过论文+代码的形式来介绍BERT模型的应用,通过实验发现BERT的出现,为NLP各类任务提供了方便。为此,有必要再次回顾一下BERT模型的特性: (1)基于Transformer的encoder,实现双向信息的编码融合,提高上下文理解能力,并设计预训练+微调范式,实现多个NLP任务的统一架构——BERT; (2)BERT采用的无监督预训练方法为MLM(masked language model)和NSP(next sentence predict),可以实现token级和句子级的信息学习; (3)BERT的各任务微调,通常指需要4epoch,学习率1e-5级别,gpu几个小时即可,非常亲民; (4)BERT的输入embedding新增了一个segment embedding来标记句子当前token属于第几个句子; (5)BERT的统一架构实现多个任务,得益于输入-输出的设计,有必要再次回顾这幅图 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-9/9.6-gpt.html":{"url":"chapter-9/9.6-gpt.html","title":"9.6 文章续写-问答对话-GPT","keywords":"","body":"9.6 文章续写&问答对话-GPT 前言 本节介绍生成式对话模型的基座——GPT(Generative Pre-Training),将会从OpenAI的四篇论文入门,了解GPT的概念及发展历程。随后通过GPT2的训练及推理,加深对GPT的理解。最后对本节进行小结。 GPT系列论文简读 生成式对话模型(以ChatGPT为代表)的发展历史可以关注OpenAI在GPT系列上的研究,这里通过GPT1, 2, 3和InstructGPT来了解GPT的发展及变化,有助于理解大语言模型。 GPT: Improving Language Understanding by Generative Pre-Training https://www.mikecaptain.com/resources/pdf/GPT-1.pdf GPT2:Language Models are Unsupervised Multitask Learners https://insightcivic.s3.us-east-1.amazonaws.com/language-models.pdf GPT3:Language Models are Few-Shot Learners https://arxiv.org/abs/2005.14165 Instruct GPT:Training language models to follow instructions with human feedback https://arxiv.org/pdf/2203.02155.pdf GPT1 GPT1《Improving Language Understanding by Generative Pre-Training》由OpenAI团队在2018年6月公开,首次将Transformer的decoder用于大规模预训练,从而实现从无标签数据从训练具备语言理解的基础模型,为NLP下游任务提供强大底座。 GPT关键词:预训练+微调(全文似乎就没有其它关键点了...) GPT模型结构及下游应用范式,可以根据文中图1来理解。 GPT模型结构:12层Transformer的decoder,再根据不同任务接不同的输出头。 下游任务微调范式:论文介绍了GPT应用于四种下游任务,分别是文本分类、蕴含、句子相似性、阅读理解(多项选择题) 文本分类:输出特征向量接入一个linear层做分类; 蕴含:蕴含是涉及两个句子的二分类问题,这里为了保证基础模型架构不变动,把两个句子通过特殊的token Delim来拼接,然后接linear做二分类。 句子相似性:两个句子拼接为一个序列,根据句子前后顺序,可以获得两个序列,分别输入模型,将两个特征向量相加,再接linear做二分类。 多项选择题:多选题有3部分信息,原始文本、问题、答案,将不同的选项拼接起来得到多个序列,每个序列最终输出一个概率,最后把多个选项通过softmax得到最终的概率。 训练情况: 预训练7000本书,具体数据量未说明,100个epoch,bs=64, lr=1e-4级别 微调,通常3个epoch,bs=32, lr=1e-5级别。 小结:GPT采用Transformer的Decoder作为组件,采用MLM进行预训练获取基础模型,基于预训练好的模型,实现一个模型架构四种NLP任务微调,获取9个任务的SOTA,表明了预训练+微调范式有效。 GPT2 GPT2:《Language Models are Unsupervised Multitask Learners》 在BERT提出后的4个月,GPT2出世,继续坚定采用decoder架构(BERT是encoder模块堆叠),在更大尺寸、更多数据上进行预训练,并在多个NLP任务(GPT提及4个任务,BERT提及4类任务)上达到SOTA。 GPT2的文章没有过多介绍算法,这里简单记录几个关键点: 爬取4500万链接,清洗出800万文档,共计40GB。 词表大小50257,上下文从512变为1024. 词表策略采用优化后的BPE,可将词表大小缩减2-3倍(13万-->5万) 探索zero-shot learning:gpt中在微调时采用了特殊token来让模型理解人类的意思,需要执行特定任务;GPT2希望做到在无需微调,即预训练阶段就让模型具备根据输入信息来执行特定任务的能力,这就是零样本学习,预训练好之后,模型就可以完成特定任务,无需样本。如何让模型能完成特定任务呢?举个机器翻译的例子,只要将输入给模型的文本构造成 translate english to chinese, [english text], [chinese text] 就好了。比如:translate english to chinese, [machine learning]。这就是prompt的概念,预训练模型可以根据用户的输入来理解任务。这需要预训练模型具备大量类似的训练数据(这也是后续大模型有base和chat的区别,chat模型加入更多的对话数据,实现对话能力,base模型更多的是知识能力)。 GPT3 GPT3:《Language Model are Few-Shot Learners》 随着数据与算力的发展,同时预训练的具体潜力被认可,OpenAI在GPT2的基础上进行了多方面的投入,数据增长1000倍(40GB->45TB),模型扩大100倍(1.5B -> 175B),论文作者增大7倍(4人->31人)。由此可见,GPT3是一个大力出奇迹的作品,耗费大约1200 万美金训练。 GPT3的论文有75页之多, 这里简要梳理整体内容。 本文主旨:利用大模型、大数据来无监督训练GPT,探索它在少样本学习上的能力,希望让GPT模型更接近人类的思考方式。 本文关键词:Few-Shot learning,任务无关 (Task-agnostic) 数据量:来自互联网、高质量书籍文章等的45TB数据,总token达到260B(0.26T;2024年大模型训练数据一般在2-5T的tokens)。 数据处理:互联网爬取数据中过滤低质量数据;文档级别的数据去重;加入高质量数据; 数据处理的bug:为了让模型在评估数据集上公平的测评,需要把训练集中的数据污染过滤掉,虽然做了,但最后发现还是存在数据污染。遗憾的是,因模型训练过于昂贵,无法重新训练了。 模型设置:做了8个不同尺寸的模型,最大的1750亿参数,命名为GPT3,结构与GPT2没有过多差别 模型训练:batchsize也做了预热,逐步增大batchsize;学习率预热 375 M tokens,总共训练260 B tokens。样本全部组装为长度是2048的序列。 模型评估:在各类NLP任务上评估了少样本学习的能力。 模型缺点:会出现重复、缺乏连贯、以及单向结构的弊端(BERT那样的是双向结构) 模型的潜在影响:模型可能存在滥用,例如垃圾邮件、欺诈性学术论文写作等;性别、种族、宗教的偏见问题;能源问题; 一句话总结GPT3:采用大数据大模型训练GPT3,可实现少样本的学习能力,可以完成多种NLP任务,其模式更接近人类的1-shot learning。 ChatGPT(Instruct GPT) Instruct GPT:Training language models to follow instructions with human feedback 于2022年3月发表。 关键词Language Model Alignment;Following Instructions, Reinforcement Learning from Human Feedback 摘要 GPT3已经能进行各类的文本生成任务,但TA不太能够遵循人类的意图,为了让GPT更好的遵循人类意图,回答人类的问题,本文采用RLHF的强化学习方式,来微调GPT,结果表明微调后的GPT,即使仅1.3B参数,也比175B参数模型,更受人们青睐。 指令跟随的理解 简单回顾GPT机制。GPT是Language Model,根据左边的序列预测下一个token,在GPT-123系列中,已经看到在各类文本生成任务中表现不错,但是要利用GPT来完成具体的文本分类、句子相似性判断、多项选择题时,需要在推理时加入特殊的token,例如GPT1中的start、delim、extra这样的“指令”来组装序列。 这是由于GPT并不能直接根据人类语言来完成这些任务,例如希望实现机器翻译时,期望输入的应该是:translate english to chinese, [english text], [chinese text] 。这里的translate english to chinese就是人类的指令,但在GPT1、2和3的预训练中,采用大多是书籍、互联网文本数据,缺乏这样的指令对话,因而GPT3也不能很好的满足人类的美好需求。 为了让GPT模型能更好的理解人类意图,需要进行模型对齐、指令跟随的微调训练,本文训练出来的GPT就称为Instruct GPT,这也是ChatGPT背后强大的技术支撑——满足人类意图。 训练过程 训练的目标是让GPT输出的内容,让人类更满意,本文巧妙的利用模型输出多个答案,让人类进行排序的方式进行衡量,实现对模型输出结果好坏的评估。 第一步,训练基础模型,让GPT3能够回答人类问题。由人工撰写问题,以及希望模型回答的内容作为示例,对GPT-3进行监督学习微调,得到一个基线监督模型。 第二步,训练奖励模型(Reward Model)。又人类对模型的输出进行排序,然后训练一个能打分的RM模型。这个模型会在强化学习中评估GPT模型输出的好坏。 第三步,训练Instruct GPT。奖励模型的输出作为奖励信号,通过proximal policy optimization (PPO)算法,进一步微调第一步得到的基线模型。微调目标是最大化奖励模型预测的偏好分数,从而使模型输出逐步符合人类意图。最终得到nstructGPT。 更多细节: 第一步监督微调所用数据集大约有28,000个示例,主要来自标注员编写和OpenAI API用户提交的提示。 第二步奖励模型训练数据集包含约13,000个标注员对不同模型输出的偏好排序实例。 第三步强化学习阶段除了基于奖励模型的PPO目标外,还混合了与原始GPT-3预训练数据的似然目标(PPO-ptx),以缓解在下游任务上的性能下降。 下面通过表格对比GPT系列模型的变化 GPT GPT2-s GPT2-m GPT2-l GPT2-xl GPT3 参数量 ~117 M 117 M 345 M 762 M 1.5 B 175 B 训练数据 7000本书 40GB 40GB 40GB 40GB 45TB Block 12 12 24 36 48 96 d_model 768 1024 1024 1280 1600 12888 论文小结 ChatGPT的出圈不是一蹴而就,自2017年微软发布Transformer,再到OpenAI从2019年基于Transformer的decoer提出GPT,经过3年多的发展,从预训练-微调范式,不断加大模型尺寸,探索模型能力边界,再到基于强化学习的微调让GPT模型具备说人话的能力,最终有了ChatGPT这样的作品。 GPT训练 本小节采用中文预料,预训练一个GPT2-small的模型,基于wiki百科训练的模型可以实现一定程度的文章续写,基于百科QA的数据可以实现类似的问答效果。 通过GPT论文的学习,可以知道GPT要实现与人顺畅的交流,需要特定对话数据进行微调,在这里暂时不具备此能力。 在这里希望通过代码,强化对GPT运行机制的了解,包括训练数据如何组装、拼接、特殊token的处理、推理时的处理细节,为后续LLM的研发打下基础。 这里整体训练代码参考自:https://github.com/Morizeyao/GPT2-Chinese 环境配置说明: transformers==2.1.1 (高版本不适配) GPT训练数据准备 由于是预训练预料,因此可以采用互利网上的文本信息,这个repo收集了5个数据集,有上千万的句子可用。分别有: 1.维基百科(wiki2019zh),100万个结构良好的中文词条 2.新闻语料(news2016zh),250万篇新闻,含关键词、描述 3.百科问答(baike2018qa),150万个带问题类型的问答 4.社区问答json版(webtext2019zh),410万个高质量社区问答,适合训练超大模型 5.翻译语料(translation2019zh),520万个中英文句子对 在这里,受限于计算资源,只采用了维基百科(wiki2019zh)和百科问答(baike2018qa)两个数据集,分别训练了两次GPT2,用于观察不同数据下模型的差异。 GPT预训练的数据主要是文本序列,不同文章、段落的文本可以拼接到一起输入给模型。 因此,数据处理就比较简单,将所有文本拼接,然后在文章开头、结束加入特殊token来标识,最后划分为batch的形式输入到模型即可。 这部分逻辑参考train.py中的build_files函数。build_files大致逻辑如下: 读取文件夹中的原始文件,所有字符串以list为单位,存储于lines_total中 根据分片的数量,对lines_total进行切割,过滤单条文本长度小于min_length的文本 不同文本之间加入特殊token进行分隔,并用tokenizer转为index 写入txt文件 PS:代码中没有采用pytorch的dataset和dataloader的概念,直接对txt文件进行读取,在训练代码中处理。 GPT模型构建 基于transformers库中的modeling_gpt2的GPT2LMHeadModel实现,代码也很简单,没有特殊的,需要提一点就是在GPT2LMHeadModel中的forward实现了计算loss功能,只需要在forward的时候吧labels传入,由于是masked langurage model的任务,标签就是输入右移一位,具体看如下代码: # ... GPT2LMHeadModel中的forward()) if labels is not None: shift_logits = lm_logits[..., :-1, :].contiguous() # 取输入n-1个 shift_labels = labels[..., 1:].contiguous() # 取n-1个标签 loss_fct = CrossEntropyLoss(ignore_index=-1) loss = loss_fct(shift_logits.view(-1, shift_logits.size(-1)), shift_labels.view(-1)) outputs = (loss,) + outputs GPT模型训练 模型训练采用了两个数据集,通过不同的数据类型,观察GPT模型对于人类意图的跟随能力。 这里采用了wiki2019和baike2018qa两个数据集,wiki2019中的文本主要是陈述句,baike2018qa里则会有对话和问答。 首先看wiki2019的训练,wiki2019,总token数5亿(508478004),在1080ti(12GB)上训练需要5天左右,5个epoch后loss基本稳定在2。 python train.py --raw --raw_data_path ./wiki_raw/wiki_zh --epochs 10 --batch_size 4 --log_step 100 --num_pieces 1000 接着看baike2018qa的训练,总token数也是5亿(505564900),在1080ti(12GB)上训练需要5天左右,5个epoch后loss基本稳定在2+。 python train.py --raw --raw_data_path ../data/baike2018qa --epochs 5 --batch_size 4 --log_step 100 --num_pieces 1000 GPT推理 训练完成,在model文件下获取模型权重,采用generate.py进行推理,推理时需要设置prefix,模型根据前缀进行后续文字生成。 推理示例: python generate.py --model_path ./model/model_epoch5_baikeqa --prefix 我肚子痛了,要怎么办? python generate.py --model_path ./model/model_epoch5_wiki_alldata --prefix 秦始皇,名嬴政,是中国历史上著名的君主,也是中国历史上第一个使用“皇帝”称号的君主。他出生于公元前259年,卒于 这里暂时没有做停止符处理,先让模型输出到最大长度,正常情况应该判断是否输出了停止符。(停止符是两个回车\\n\\n) 具体的推理、采样逻辑如下代码所示: def sample_sequence(model, context, length, n_ctx, tokenizer, temperature=1.0, top_k=30, top_p=0.0, repitition_penalty=1.0, device='cpu'): context = torch.tensor(context, dtype=torch.long, device=device) context = context.unsqueeze(0) generated = context with torch.no_grad(): for _ in trange(length): inputs = {'input_ids': generated[0][-(n_ctx - 1):].unsqueeze(0)} # 模型推理 outputs = model(**inputs) # Note: we could also use 'past' with GPT-2/Transfo-XL/XLNet (cached hidden-states) next_token_logits = outputs[0][0, -1, :] # 输出l个logits,但只需要取最后一个 # 加入温度惩罚 for id in set(generated): next_token_logits[id] /= repitition_penalty next_token_logits = next_token_logits / temperature next_token_logits[tokenizer.convert_tokens_to_ids('[UNK]')] = -float('Inf') # 采样 filtered_logits = top_k_top_p_filtering(next_token_logits, top_k=top_k, top_p=top_p) next_token = torch.multinomial(F.softmax(filtered_logits, dim=-1), num_samples=1) # 拼接 generated = torch.cat((generated, next_token.unsqueeze(0)), dim=1) return generated.tolist()[0] ------------------------------------------------------------------ 看第一个推理例子 ------------------------------------------------------------------ (模型权重可以从这里下载提取码:lwzr) python generate.py --model_path ./model/model_epoch5_wiki_alldata --prefix 秦始皇,名嬴政,是中国历史上著名的君主,也是中国历史上第一个使用“皇帝”称号的君主。他出生于公元前259年,卒于 模型输出: 秦始皇,名嬴政,是中国历史上著名的君主,也是中国历史上第一个使用“皇帝”称号的君主。他出生于公元前259年,卒于154年,享年68岁。\\n\\n\"} {\"id\":\"3704970\",\"url\":\"https://zh.wikipedia.org/wiki?curid=3704970\",\"title\":\"中国人民解放军海军航空兵\",\"text\":\"中国人民解放军海军航空兵\\n\\n中国人民解放军海军航空 兵(,缩写为)是中国海军的一支现役军事部队,由现役部队改编而成,是中国人民解放军陆军中的最新式军事部队。\\n\\n1962年,在海军的改编和建立之初,海军航空兵的基础上开展 以航空兵为主体的军事训练。随着1985年7月20日成立了第一届海军大学校,海军航空兵开始进行部队建设,并且在1990年12月28日举行了首届海军大学体育训练班。1996年3月18日,, 军航空兵正式开始招收军事战斗部队,这一年开始实施大学体育运动。1999年,海军航空兵正式进入国防部直属的军训,并且在2003年7月19日起正式开始招收军事战斗部队,同时开 始招收专业的军事战斗部队。目前中国人民解放军的各级军事战斗部队包括陆军航空兵第二十二集团军,第六十七集团军,海军军事学校航空兵第四十五集团军。\\n\\n2005年,陆军航空 开始进行全军装备升级,并且在2006年1月1日起开始进行了大规模的现场演练,并且在一天的时间里将部队装备的现役军事战斗部队改为现役部队。现役部队装备的现役部队装备已经经 全转型到现役部队,并且陆续进驻陆军航空兵、海军军事战斗部队及海军军事战斗部队等。陆军航空兵的现役部队现役部队包括陆军航空兵第二十二集团军、海军军事战斗部队、海军军 事战斗部队、海军航空兵第四十五集团军、海军航空兵第一十一集团军和空军航空兵第二十六集团军。\\n\\n陆军航空兵现役的部队包括陆军航空兵、空军航空兵、空军航空兵、海军军军 战斗部队、海军航空兵、海军航空兵、海军航空兵等,部队装备的现役部队还包括海军航空兵、海军航空兵、海军航空兵和空军军事战斗部队。\\n\\n现役部队装备的现役部队包括陆军军 空兵第九十一集团军、海军航空兵第四十五集团军、空军航空兵第九十六集团军、海军军事战斗部队、海军航空兵第四十五集团军、空军航空兵第一十六集团军、空军航空兵第二十六六 团军、海军航空兵第十六集团军、海军航空兵第十八集团军、空军航空兵第二十九集团军、空军航空兵第二十九集团军、空军航空兵第九十四集团军和空军航空兵第九十五集团军。\\n n\\n现役部队装备的现役部队包括陆军航空兵第九十八集团军、海军航空兵第九十七集团军、空军航空兵第九十七集团军及空军航空兵第十八集团军。 ...略......略......略......略... 从输出来看,文字读起来都是人话,但是深究逻辑就有明显错误,例如秦始皇的逝世年份是不对的。并且在后续的文本中,输出了一个wiki不存在的词条,同时文本里面还出现了大量的重复,这些都是GPT3论文中提到的问题——重复。 ------------------------------------------------------------------ 看第二个例子 ------------------------------------------------------------------ python generate.py --model_path ./model/model_epoch5_wiki_alldata --prefix 我感冒了怎么办? 模型输出: 我感冒了怎么办?」()这句话在电影界中引起了一片好的关注,在电影中也常出现一些不好的场景。在2002年,电影的剧照已经被删除过一次,并且由于其他原因,它在电影中被删除。\\n\\n\"} {\"id\":\"3543288\",\"url\":\"https://zh.wikipedia.org/wiki?curid=3543288\",\"title\":\"阿尔及尔航空330号班机空难\",\"text\":\"阿尔及尔航空330号班机空难\\n\\n阿尔及尔航空330号班机 阿尔及尔国内首班国内航班,机型为波音747-2er,于1983年7月22日起执飞,是一班从利比亚布尔加斯-首都贝鲁特经利比亚首都贝鲁特、贝鲁特抵达巴林国际机场的定期航班,机上8 87人当中,50人是来自利比亚的阿尔及尔航空公司。\\n\\n事发时阿尔及尔国内有16名乘客和53名机师的生还。\\n\\n机上16名乘客和12名机师生还。\\n\\n\"} ...略......略......略......略... 很明显,模型完全没能理解用户输入信息,也就是无法进行对话功能。这与训练数据有关,属于Instruct GPT里提到的指令跟随内容,wiki预料中都是陈述句,没有这些问句以及人类提出的需求文本,因此模型无法跟随人类指令。如果需要让模型能够进行指令跟随,应当用一系列问答、对话、命令式的文本来训练。 ------------------------------------------------------------------ 看第三个例子 ------------------------------------------------------------------ (模型权重可以从这里下载提取码:4hua python generate.py --model_path ./model/model_epoch5_baikeqa --prefix 我肚子痛了,要怎么办? 模型输出: 我肚子痛了,要怎么办?是有什么问题 如果是肠炎,就应该是肠梗阻了,建议用中药治疗一下,平时要多喝水,饮食要清淡,多运动,要注意饮食,忌烟酒。 我是新手,刚玩。请问怎样才能玩到15级啊!!还有我现在的号是52级的,怎样升级才能升到13级啊?我是新手,请高手指点一下,我现在是17级。谢啦 怎样才能玩到15级!!!我是新手,刚玩。请问怎样才能玩到15级啊! 你的号已经是新手了,你的级别是在40级前的,那你可以到新手村,那里会有很多的怪,你可以从40级以上的怪物身上找到一些相关怪物,你会获得一定经验,还可以去找别人成一定数量的装备和你需要的金钱。 ...略......略......略......略... 可以看到模型会续写用户的输入,并且换行之后进行“回答”,看似模型已经会回答问题了,这是因为baike2018qa数据集就是大量问答对的数据,从以上三个案例也可知道,生成式模型的对话能力,需要用对话数据、人类指令数据进行微调。这也是当下大模型中有base版和chat版的区别。base版模型是在大量的预料数据库上训练的,那些都是陈述句,属于信息的描述。要想让模型理解人类意图,还需要训练chat版。 推理速度分析 通过以上案例,可以发现token推理速度随着输出长度增加而增加,这个很好理解,因为是自回归式的输出,预测token越多,下一个token推理时需要处理的QKV计算相应增加,从而速度变慢,并且显存占用是逐渐增加的。 在这里也是Transformer架构可优化的地方,每个token的推理时,其实有一大部分K,V是重复运算了的,因此可以将这部分K,V缓存下来,减少运算量,这就是KV-Cache的由来。 小结 本节通过梳理GPT1,2,3到chatGPT的技术发展历程,对GPT模型有了详细的了解,并通过GPT2-small模型的训练,进一步了解GPT的运行机制,为后续大语言模型的学习打下基础。 本节值得重点关注的点有: GPT1,2,3模型从GPT1中采用特殊token来让模型完成特定任务,到Instruct GPT通过自然语言让模型完成特定任务,是ChatGPT得以破圈,被人类所接受的重要改变,这样的交互方式,也是大家耳熟能详的Prompt Engineering。 GPT系列是一个模型、数据、计算基础设施的复杂系统性工程,三者缺一不可,从GPT3论文中提及数据污染的bug无法重新训练模型可知,训练175B模型太贵了。 GPT训练时,句子之间用特殊token进行拼接,变为一个长序列输入给到模型训练。一般loss在2上下是一个还过得去的区间。 指令跟随的理解,可以用GPT1如何应用到下游任务思考,结合Instruct GPT的内容,即可理解指令跟随本意就是预训练数据需要包含人类指令、人类命令,这样预训练模型才能在推理时,理解人类意图。 GPT的推理是自回归的,模型一次推理,对于人类而言有价值意义的是最后一个概率向量,对于循环多少次,要输出多少个token,取决于人类的代码逻辑。 KV-cache的理解,在最原始的代码推理中,可以发现输出的token越长时,越靠后的token耗时约长,这是因为单次推理时,输入的token变长了,需要计算的注意力变多,因而变慢,但大部分注意力的计算在前序推理时已经计算过的,因此可以通过缓存存储那些计算值,以此加快推理速度。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/":{"url":"chapter-10/","title":"第十章 大语言模型安装与应用","keywords":"","body":"第十章 大语言模型安装与应用 第十章 大语言模型安装与应用 10.1 Qwen 部署与分析 10.2 ChatGLM3 部署与分析 10.3 Baichuan2 部署与分析 10.4 Yi 部署与分析 10.5 GPT Academic 安装与使用 第十章简介 本章介绍大语言模型(Large Language Model)的本地安装、推理分析,包括国内主流开源大模型,Qwen系列、ChatGLM系列、Baichuan系列和Yi。同时为了辅助大家了解LLM的能力边界和应用,分析一个54K stars的LLM应用工具——GPT Academic(GPT 学术优化)。 通过本章可以了解到四个国产开源大模型的: 安装部署环境准备 模型代码结构设计方案 LLM推理流程步骤 多轮对话prompt组装方式 显存占用与上下文长度关系趋势 LLM内置角色system、user、assistant、observation的作用与区别 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.1-qwen.html":{"url":"chapter-10/10.1-qwen.html","title":"10.1 Qwen部署与分析","keywords":"","body":"10.1 Qwen部署与分析 前言 随着大语言模型的出圈及广泛应用落地,LLM技术已成为新一代人工智能、深度学习的必备技能,本节将介绍当下较为热门的开源大语言模型Qwen的安装及代码、流程、显存消耗分析。 本节内容有3个内容,有助于理解LLM模型,包括: Qwen模型代码结构组织与接口分析:可了解LLM模型的代码是如何设计的 LLM模型多轮对话机制:逐步剖析LLM的推理机制,多轮对话时如何组装历史聊天记录的 LLM模型显存占用:了解显存随着上下文长度的增加的变化趋势 Qwen简介 通义千问是阿里巴巴集团Qwen小组研发的大语言模型,自2023年4月开放测试8月开源7B模型,12月开源72B;到2024年3月开放了Qwen1.5-32B,可最大限度兼顾性能、效率和内存占用的平衡,Qwen系列不断更新,为开源社区做出了贡献。 Qwen系列产品 通义千问仅是Qwen团队产品之一,属于对话模型,能理解人类语言、生成内容,作为用户生活和工作的智能助手。 通义,取自《汉书》中的“天地之常经,古今之通义也”,有“普遍适用的道理与法则”之意。官网展示,通义是致力于实现类人智慧的通用智能 千问,千万次的问,千万的学问。 除了通义千问,Qwen团队还有其他大模型,共计10个(截止至2024年4月16日),分别是: 通义千问、通义万相、通义听悟、通义点金、通义灵码、通义法睿、通义星尘、通义仁心、通义晓蜜和通义智文。 Qwen目前开源Qwen1.5系列,属于2.0的pre版,相较于1.0在更方面指标均有所提升,并且开放了0.5B, 1.8B, 4B, 7B, 14B, 32B, 72B多个尺寸,同时有base和chat版,也有多种精度fp16, int8, int4。开发者可根据不同的场景、硬件平台、任务复杂度来选择模型。 为了更全面了解Qwen系列产品,推荐以下链接: 官方产品主页:https://tongyi.aliyun.com/ Qwen团队github主页:https://github.com/QwenLM Qwen开源文档:https://qwen.readthedocs.io/en/latest/ Qwen技术报告:https://arxiv.org/abs/2309.16609 Qwen的HuggingFace主页:https://huggingface.co/Qwen 本地部署安装 Qwen代码提供了命令交互案例,运行成功后,如下所示: User: 你是谁 Qwen-Chat: 我是来自阿里云的大规模语言模型,我叫通义千问。我可以回答各种问题、创作文字,还能表达观点、撰写代码。 User: 你还有什么能力? Qwen-Chat: 我还可以将文本从一种语言翻译成另一种语言,总结文本,生成文本,写故事,分析情绪,提供建议,开发算法,编写代码以及任何其他基于语言的任务。 User> 为了成功安装Qwen,建议严格阅读github的readme,下面说明本次部署的环境、版本配置。 Qwen官方github建议的版本要求如下: python 3.8 and above pytorch 1.12 and above, 2.0 and above are recommended transformers 4.32 and above CUDA 11.4 and above are recommended (this is for GPU users, flash-attention users, etc.) 代码和模型,采用的事Qwen,不是Qwen1.5,这两个是不同的代码仓库,一定要注意。 笔者的安装环境如下: win11 python 3.10.14 pytorch 2.2.0 transformers 4.39.0 CUDA 12.1 RTX 4060 Laptop 8GB RAM 32GB 安装步骤 第一步,下载Qwen代码 git clone https://github.com/QwenLM/Qwen.git 第二步,安装基础python包 pip install -r requirements.txt pip install -r requirements_web_demo.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 如果需要量化版,则需要安装对应的autogptq等; pip install auto-gptq optimum 第三步 下载模型权重 根据显存大小,选择合适的模型,这里选择1.8B进行使用,通过github信息,可大约知道不同尺寸模型显存需求。 Model Release Date Max Length System Prompt Enhancement # of Pretrained Tokens Minimum GPU Memory Usage of Finetuning (Q-Lora) Minimum GPU Usage of Generating 2048 Tokens (Int4) Tool Usage Qwen-1.8B 23.11.30 32K ✅ 2.2T 5.8GB 2.9GB ✅ Qwen-7B 23.08.03 32K ❎ 2.4T 11.5GB 8.2GB ✅ Qwen-14B 23.09.25 8K ❎ 3.0T 18.7GB 13.0GB ✅ Qwen-72B 23.11.30 32K ✅ 3.0T 61.4GB 48.9GB ✅ Qwen-1.8B-chat下载 下载有两个地方,分别是HF和国内的ModelScope社区 HuggingFace下载:https://huggingface.co/Qwen/Qwen-1_8B-Chat/tree/main ModelScope: https://www.modelscope.cn/models/qwen/Qwen-1_8B-Chat/files 模型除了权重文件,还有一系列配置文件等信息,因此需要完整的文件下载才可使用,鉴于网络问题,这里介绍两种下载方式: 方式一:纯命令行(看网速) git clone https://www.modelscope.cn/qwen/Qwen-1_8B-Chat.git 方式二:命令行 + 手动下载权重文件 git clone https://www.modelscope.cn/qwen/Qwen-1_8B-Chat.git 查看文件夹下配置文件是否下载完毕,下载完毕后,ctrl + c停止。 可手动下载两个.safetensors文件,放到文件夹中,手动下载.safetensors文件,速度高达10+MB/s 第四步:配置模型路径 修改 Qwen/cli_demo.py 中权重路径 DEFAULT_CKPT_PATH = r\"G:\\04-model-weights\\qwen\\Qwen-1_8B-Chat\" 第五步,运行cli_demo.py,看到客户端运行成功(通过日志看出,这里采用流式输出): C:\\Users\\yts32\\anaconda3\\envs\\pt220\\python.exe D:\\github_desktop\\Qwen\\cli_demo.py The model is automatically converting to bf16 for faster inference. If you want to disable the automatic precision, please manually add bf16/fp16/fp32=True to \"AutoModelForCausalLM.from_pretrained\". Try importing flash-attention for faster inference... Warning: import flash_attn rotary fail, please install FlashAttention rotary to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/rotary Warning: import flash_attn rms_norm fail, please install FlashAttention layer_norm to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/layer_norm Warning: import flash_attn fail, please install FlashAttention to get higher efficiency https://github.com/Dao-AILab/flash-attention Loading checkpoint shards: 100%|██████████| 2/2 [00:02 你好,你是谁? C:\\Users\\yts32\\.cache\\huggingface\\modules\\transformers_modules\\Qwen-1_8B-Chat\\modeling_qwen.py:528: UserWarning: 1Torch was not compiled with flash attention. (Triggered internally at C:\\cb\\pytorch_1000000000000\\work\\aten\\src\\ATen\\native\\transformers\\cuda\\sdp_utils.cpp:263.) attn_output = F.scaled_dot_product_attention( User: 你好,你是谁? Qwen-Chat: 我是 User: 你好,你是谁? Qwen-Chat: 我是来自 User: 你好,你是谁? 需要web ui界面,也可以修改web_demo.py的路径,然后运行代码即可 DEFAULT_CKPT_PATH = r\"G:\\04-model-weights\\qwen\\Qwen-1_8B-Chat\" 模型代码结构分析 为了详细了解Qwen模型代码的设计结构,了解大语言模型推理逻辑,在这里对Qwen的代码结构进行剖析。 首先关心的是,模型在哪里创建的,是什么形式,这个可从cli_demo.py的55行代码看到 model = AutoModelForCausalLM.from_pretrained( args.checkpoint_path, device_map=device_map, trust_remote_code=True, resume_download=True, ).eval() 其中AutoModelForCausalLM是transformers库的类,那么它和Qwen的关系又是怎么样的呢?下面通过UML类图来分析两者关系。 UML类图分两部分. 左边的_BaseAutoModelClass和AutoModelForCausalLM是transformers库的标准设计,基于transformers推理的LLM需要遵循这套规则。 右边则是Qwen的代码结构设计,包括: QWenLMHeadModel:最外层,用户调用的模型,通过HeadModel就知道这个模型是加入下游任务的head,可被用户使用的。 QWenPreTrainedModel:定义为所有预训练模型类 QWenModel:定义为基模型,可以看到在QWenLMHeadModel中被初始化为它的一个属性,用于基座推理,然后加上lm_head: nn.Linear进行具体的token分类任务。 再往上,就是transformers库的基础类了,包括PreTrainedModel、nn.Module, ModuleUtilsMixin, GenerationMixin, PushToHubMixin, PeftAdapterMixin。这些都是基础的必备模块。可以看到熟悉的pytorch的nn.Module,以及一系列Mixin类。 补充知识:Mixin类是一种设计模式,表示一些通用的功能放到这里,其他需要此功能的模块,通过继承的方式将这些共同行为混入到其他类中。 流式推理流程分析 通过cli_demo.py代码可看到,用户输入的字符串是通过这个chat_stream函数实现返回的,下面分析该函数实现的推理流程 def chat_stream( self, tokenizer: PreTrainedTokenizer, query: str, history: Optional[HistoryType], system: str = \"You are a helpful assistant.\", stop_words_ids: Optional[List[List[int]]] = None, logits_processor: Optional[LogitsProcessorList] = None, generation_config: Optional[GenerationConfig] = None, **kwargs, ) -> Generator[str, Any, None]: chat_stream源代码位于C:\\Users\\yts32.cache\\huggingface\\modules\\transformers_modules\\Qwen-7B-Chat-Int4\\modeling_qwen.py: 它不在本地仓库,也不在transformers库,这是因为HuggingFace提供的通用规则,允许用户自定义上传模型代码进行推理,因此基于本规则,Qwen的代码会变到.cache文件夹下进行运行。 核心参数说明: query:用户本轮对话所输入的信息 history: 历史对话信息,以list存储,包括用户输入的信息及用户收到的信息。(用户收到的信息不等价于模型输出信息) system:用于模型角色设定 generation_config: 是transformers库中一个模块,该类中存储大量与生成相关的配置,例如停止词、最大输出长度等 函数核心步骤: 第一步:生成本轮输入给模型的信息及tokens。 通过make_context函数,实现需要输入给模型的上下文信息组装,其核心功能包括: 设定system角色信息:例如,system \\n You are a helpful assistant. 拼接历史对话信息:例如,user 你是谁 assistant 我是来自阿里云的大规模语言模型,我叫通义千问。我可以回答问题、创作文字,还能表达观点、撰写代码。有什么我可以帮助你的吗? user 1+1= assistant 1+1=2。这是数学中的基本算术运算之一。如果您有任何其他问题或需要任何帮助,请随时告诉我。 将string转token 说明:模型输入的信息中,包含多个段落结构,主要包括三个角色的内容描述。各角色内容之间采用特殊分隔符来区分。Qwen采用的是、。 raw_text, context_tokens = make_context( tokenizer, query, history=history, system=system, max_window_size=max_window_size, chat_format=generation_config.chat_format, ) raw_text = 'system You are a helpful assistant. user 你好 assistant ' context_tokens = [151644, 8948, 198, 2610, 525, 264, 10950, 17847, 13, 151645, 198, 151644, 872, 198, 108386, 151645, 198, 151644, 77091, 198] 第二步,获取停止词。 LLM的生成是自回归的,通常设定一些停止词,当模型输出停止词时,程序会停止推理,返回本轮结果。 stop_words_ids = [[151645], [151644]] 第三步,进入生成器 stream_generator,开始逐token输出。 for token in self.generate_stream( input_ids, return_dict_in_generate=False, generation_config=stream_config, logits_processor=logits_processor, seed=-1, **kwargs): outputs.append(token.item()) yield tokenizer.decode(outputs, skip_special_tokens=True, errors='ignore') self.generate_stream方法会进入NewGenerationMixin(GenerationMixin)类中的方法进行生成,实际生成token函数是NewGenerationMixin.sample_stream(),这部分属于transformers_stream_generator库中功能。 注:NewGenerationMixin.sample_stream()位于site-packages\\transformers_stream_generator\\main.py 第四步:停止判断 通过判断上一个词是否为停止词,决定是否跳出while循环。 代码位于transformers_stream_generator\\main.py 中NewGenerationMixin.sample_stream() # if eos_token was found in one sentence, set sentence to finished if eos_token_id is not None: unfinished_sequences = unfinished_sequences.mul( (sum(next_tokens != i for i in eos_token_id)).long() ) # stop when each sentence is finished, or if we exceed the maximum length if unfinished_sequences.max() == 0 or stopping_criteria(input_ids, scores): if not synced_gpus: break else: this_peer_finished = True 总结一下,chat_stream()内部主要的步骤包括: 第一步:生成本轮输入给模型的信息及tokens。 第二步,获取停止词。 第三步,进入生成器 stream_generator,开始逐token输出。 第四步:停止条件判断。 多轮对话Prompt分析 通过代码分析,可了解到LLM模型每次推理都需要把上文信息全部输入,计算开销相当大,并且随着上下文长度增加,计算量呈平方级增长。 为了更好理解LLM实际的推理过程,下面分析模型的多轮对话过程。 第一轮对话 第一轮对话,用户输入“你是谁”,对于LLM而言,实际的输入信息包括角色描述、特殊分隔token、换行符。如下图所示: LLM模型执行第一次推理,获得“我”,然后将“我”拼接到第一次推理的输入,继续让LLM输出下一个token,直到第N次推理时,模型输出的是停止符号token,第一轮对话结束。 第二轮对话 第二轮对话,用户输入“1+1=”,对于LLM而言,它是无状态的,因此需要把前面对话信息一并带入,作为输入给模型的信息,如下图所示: 第一次推理,给模型输入的信息需要包括前序对话轮次的历史信息,加上本轮用户输入信息,经过N次LLM模型推理后,模型输出停止token,本轮对话结束。 通过多轮对话流程分析,可以知道: LLM是无状态的,多轮对话完全依赖于输入信息的拼接; LLM输入信息通常有三个部分。第一个部分,所有推理的开头是system的描述;第二个部分,用户的输入;第三个部分LLM回答的信息,称之为assistant。多轮对话时,把历史对话,循环的通过user、assistant进行拼接。 显存使用与文本长度分析 为了了解模型显存使用与文本长度的关系,这里做了文本长度与显存的统计,观察在transformers库进行LLM模型推理部署时,显存使用情况。(修改了的cli_demo.py在github上) 这里需要特别说明:LLM的推理部署针对显存使用有大量的优化策略和现成框架,这里的分析是针对transformers库,针对其他后端推理框架的适用性需要仔细考量。 LLM模型推理显存占用可分两个部分,模型参数和中间变量。模型参数加载后就占用固定空间,中间变量则会随着输入文本的长度增加和增加。(transformers是没有KV-Cache机制的,因此没有额外的显存消耗) 这里手动进行对话,并且记录每一轮对话结束后,输入+输出的字符串长度,显存使用情况,绘制折线图如下所示: 通过上图分析,可发现: 1.8B - fp16模型加载需要3.6GB显存; 3千字符之后,显存需求呈指数级增长,3千之前还可呈现线性增长趋势; Qwen测试代码有截断功能,即将超出显存上限8GB时,上下文被截断,以保障模型能运行。 为了进一步了解显存与上下文长度的关系,还进行了7B-int4模型的分析,结果如下图: 同样可得出上述的2和3两点结论。 在此处用的是文本长度,而不是tokens长度,是因为代码接口返回的只有文本,没有tokens_ids,但不影响分析结果的趋势。 详细代码参见 小结 本小节对Qwen-1.8B-chat-fp16模型进行了本地安装部署,以及Qwen系列LLM模型代码结构的剖析,同时还分析了LLM模型进行多轮对话时的处理逻辑。通过本节的学习,可以了解到: 通义千问是阿里Qwen团队的产品之一,还有其余9款基于大模型的产品。 Qwen最新的1.5版支持多尺寸、多精度版模型。 Qwen系列LLM模型支持三种角色,system, user, assistant。 LLM模型的多轮对话机制是将历史信息拼接起来,类似于微信的聊天记录。 LLM一次推理只输出一个token。 Qwen模型推理显存占用与上下文长度关系。 下一小节,将进行ChatGLM模型的部署及分析。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.2-chatglm.html":{"url":"chapter-10/10.2-chatglm.html","title":"10.2 ChatGLM3 部署与分析","keywords":"","body":"10.2 ChatGLM3 部署与分析 前言 本小节介绍国内大模型开源界的先驱,ChatGLM,其提供了多种开源大模型,以及工具调用功能。 本节将介绍ChatGLM发展历史,模型结构,prompt分析,显存分析等内容,帮助大家理解ChatGLM。 ChatGLM 简介 ChatGLM是由北京智谱华章科技有限公司开发的基于GLM(General Language Model )的对话模型。 ChatGLM目前(2024年4月16日)已发展到v4(未开源),其中v3版已开源,并且获得相当高的关注。 ChatGLM开源方面主打性价比,目前只有一个尺寸6B,但支持32K,128K不同上下文长度的版本。 智谱华章(智谱AI)公司2019年胎于清华大学的知识工程实验室(KEG),其技术和产品的发展与清华大学的研究成果紧密相关。清华大学的背景为智谱AI提供了强大的学术和技术支持。 除了ChatGLM,智谱AI还有CogVLM 、 CogAgent和CharacterGLM的开源,为人工智能开源社区的发展提供了帮助。产品方面,推出三大系列,大模型、AMiner学术情报收集、数字人产品。 智谱华章的价值观也非常好——让机器像人一样思考。 智谱的技术背景历史相对其他开源模型是比较长的,早在ChatGPT爆火之前,他们就已经发表了相关的文章,如2021年3月发表的《GLM: General Language Model Pretraining with Autoregressive Blank Infilling》(https://arxiv.org/pdf/2103.10360.pdf)、2022年10月发表的《Glm-130b: An open bilingual pre-trained model》(https://arxiv.org/pdf/2210.02414.pdf) 更多智谱AI信息可关注: github:https://github.com/THUDM/ChatGLM3 HF主页:https://huggingface.co/THUDM/ 官方文档:https://zhipu-ai.feishu.cn/wiki/WvQbwIJ9tiPAxGk8ywDck6yfnof 官方博客:https://zhipuai.cn/devday 公司首页:https://www.zhipuai.cn/ 本地部署安装 接下来就来本地化部署ChatGLM3-6B模型,试试效果 版本说明 ChatGLM3本地部署安装不复杂,首先看官方建议的环境要求: huggingface_hub>=0.19.4 pillow>=10.1.0 pyyaml>=6.0.1 requests>=2.31.0 ipykernel>=6.26.0 ipython>=8.18.1 jupyter_client>=8.6.0 本案例环境详情: Win11 、 RAM 32GB、RTX 4060 Laptop 8GB Python 3.10.14 transformers 4.38.2 pytorch 2.2.0 CUDA 12.1 操作步骤 第一步,下载代码仓库 git clone https://github.com/THUDM/ChatGLM3.git 第二步,下载模型 模型除了权重文件,还有一系列配置文件等信息,因此需要完整的文件下载才可使用,鉴于网络问题,这里介绍两种下载方式: 方式一:纯命令行(拼网速) git clone https://www.modelscope.cn/ZhipuAI/chatglm3-6b.git or git clone https://huggingface.co/THUDM/chatglm3-6b 方式二:命令行+ 手动下载权重文件 git clone https://www.modelscope.cn/ZhipuAI/chatglm3-6b.git 首先,查看文件夹下配置文件是否下载完毕,下载完毕后,ctrl + c停止。 然后,可手动下载.safetensors文件,放到文件夹中,手动下载.safetensors文件,速度高达10+MB/s 第三步,环境安装 pip install -r requirments.txt 第四步,设置模型路径 ChatGLM3\\basic_demo\\cli_demo.py中,修改 MODEL_PATH = r\"G:\\04-model-weights\\chatglm\\chatglm3-6b\" TOKENIZER_PATH = r\"G:\\04-model-weights\\chatglm\\chatglm3-6b\" 如果需要int4量化,则修改以下代码(不量化,需要7.9GB加载,int4量化,需要4.7GB加载) model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True).quantize(bits=4, device=\"cuda\").cuda().eval() # add .quantize(bits=4, device=\"cuda\").cuda() before .eval() to use int4 model 第五步,运行代码 进入虚拟环境,运行basic_demo\\cli_demo.py, terminal中显示如下信息表明加载成功,加载完成后,显存占用4678MB。 C:\\Users\\yts32\\anaconda3\\envs\\chatglm\\python.exe D:\\github_desktop\\ChatGLM3\\basic_demo\\cli_demo.py Setting eos_token is not supported, use the default one. Setting pad_token is not supported, use the default one. Setting unk_token is not supported, use the default one. Loading checkpoint shards: 100%|██████████| 7/7 [00:16 对于6B模型,笔记本级显卡就非常慢,已经到达用户无法接受的程度,解码时延约1.5s/token,通常LLM服务的解码时延在50ms。 这或许与代码有关,在baichuan2的7B-int4模型中,速度还是合理的。 模型结构分析 为了详细了解ChatGLM3模型代码的设计结构,接下来分析模型UML类图结构。 首先关心的是,模型在哪里创建的,是什么形式,这个可从ChatGLM3\\basic_demo\\cli_demo.py的12行代码看到 model = AutoModel.from_pretrained(MODEL_PATH, trust_remote_code=True).quantize(bits=4, device=\"cuda\").cuda().eval() 这里基于transformers库的规则采用AutoModel类的接口进行模型初始化,内部实际调用为用户自定义的模型类。 最终调用的是C:\\Users\\yts32.cache\\huggingface\\modules\\transformers_modules\\chatglm3-6b\\modeling_chatglm.py当中的ChatGLMForConditionalGeneration。 其内部关系如下图所示: UML类图分两部分 左边的_BaseAutoModelClass和AutoModelForCausalLM是transformers库的标准设计,基于transformers推理的LLM需要遵循这套规则。 右边则是ChatGLM的代码结构设计,包括: ChatGLMForConditionalGeneration:提供用户使用的类,有stream_chat、chat这两个对话接口 ChatGLMPreTrainedModel:所有预训练模型基类,提供通用接口,如get_position_ids、get_masks ChatGLMModel:LLM基模型,在ChatGLMForConditionalGeneration中被实例化为transformer属性,可理解为一个神经网络模型。 再往上,就是transformers库的基础类了,包括PreTrainedModel、nn.Module, ModuleUtilsMixin, GenerationMixin, PushToHubMixin, PeftAdapterMixin。这些都是基础的必备模块。可以看到熟悉的pytorch的nn.Module,以及一系列Mixin类。 补充知识:Mixin类是一种设计模式,表示一些通用的功能放到这里,其他需要此功能的模块,通过继承的方式将这些共同行为混入到其他类中。 流式推理流程分析 cli_demo.py采用的是流式返回,推理流程在.cache\\huggingface\\modules\\transformers_modules\\chatglm3-6b\\modeling_chatglm.py 中ChatGLMForConditionalGeneration.stream_generate() LLM输出的过程采用while循环进行控制,当达到停止条件时,break跳出循环。 下面分析stream_generate()中while循环里的代码,可以分为四个步骤 获取LLM的输入,执行推理,即outputs = self(xxx) 采样,获取本次推理得到的tokens yield 抛出结果 判断是否已停止,对于生成器而言,下一次进入循环会到yield之后的代码。 while True: # ================================== step1 ================================== # 获取LLM模型需要的输入,例如,input_ids, position_ids, att_mask等 model_inputs = self.prepare_inputs_for_generation(input_ids, **model_kwargs) # forward pass to get next token # LLM 模型的一次推理,输出1个向量,在此处为 1*65024维的向量,表明词表大小为65024。 outputs = self( **model_inputs, return_dict=True, output_attentions=False, output_hidden_states=False, ) # ================================== step2 ================================== next_token_logits = outputs.logits[:, -1, :] # pre-process distribution next_token_scores = logits_processor(input_ids, next_token_logits) next_token_scores = logits_warper(input_ids, next_token_scores) # sample probs = nn.functional.softmax(next_token_scores, dim=-1) # 未采用温度惩罚 if generation_config.do_sample: next_tokens = torch.multinomial(probs, num_samples=1).squeeze(1) # 这里采用top-p=1 else: next_tokens = torch.argmax(probs, dim=-1) # update generated ids, model inputs, and length for next step input_ids = torch.cat([input_ids, next_tokens[:, None]], dim=-1) model_kwargs = self._update_model_kwargs_for_generation( outputs, model_kwargs, is_encoder_decoder=self.config.is_encoder_decoder ) unfinished_sequences = unfinished_sequences.mul( next_tokens.tile(eos_token_id_tensor.shape[0], 1).ne(eos_token_id_tensor.unsqueeze(1)).prod(dim=0) ) # ================================== step3 ================================== if return_past_key_values: yield input_ids, outputs.past_key_values else: yield input_ids # ================================== step0 ================================== # stop when each sentence is finished, or if we exceed the maximum length if unfinished_sequences.max() == 0 or stopping_criteria(input_ids, scores): break prompt结构分析 ChatGLM3 prompt结构由对话头和内容组成,一个典型的多轮对话结构如官方文档所示: You are ChatGLM3, a large language model trained by Zhipu.AI. Follow the user's instructions carefully. Hello Hello, I'm ChatGLM3. What can I assist you today? 对比Qwen的结构如下: system You are a helpful assistant. user 你是谁 assistant ChatGLM3 为了支持Code Interpreter,Tool & Agent 等任务的输入,还增加了一个角色,总共支持四种角色,具体是: :系统信息,设计上可穿插于对话中,但目前规定仅可以出现在开头 :用户 不会连续出现多个来自 的信息 :AI 助手 在出现之前必须有一个来自 的信息 :外部的返回结果 必须在 的信息之后 的加入,使得ChatGLM3具备更丰富的功能,可以让LLM加上“四肢“,让大模型学会使用工具。 工具功能是一种扩展机制,它允许大模型通过调用外部工具来增强自身的能力,解决一些由于训练数据限制或专业领域知识缺乏而无法独立完成的任务。 常用的工具有计算器、搜索引擎、天气查询等,这个部分设计Agent的内容,此处暂不展开。 显存使用与上下文长度分析 本案例中并未能正确分析出上下文长度与显存占用的情况,出现了一些奇怪现象,这里总结如下: int4 启动需要4.6GB显存 上下文增加到3000时,显存仅增加了100MB 上下文超3500之后,显存不再增加,应该与上下文截断有关 此处,未进一步查看源代码寻找原因,毕竟这里是demo,生产部署会采用其他推理框架。 在cli_demo.py代码后加入如下代码,可进行统计,完整代码位于github # 统计文本长度 conversation_length = sum([len(content['content']) for content in history]) import subprocess import json result = subprocess.run(['gpustat', '--json'], stdout=subprocess.PIPE) output = result.stdout.decode() data = json.loads(output) used_memory = data['gpus'][0]['memory.used'] f.writelines(\"{}, {}\\n\".format(conversation_length, used_memory)) f.flush() 在此处用的是文本长度,而不是tokens长度,是因为代码接口返回的只有文本,没有tokens_ids,但不影响分析结果的趋势。 小结 本节详细介绍了ChatGLM的背景与本地部署实战,包括以下核心内容: ChatGLM3模型介绍,是基于GLM(General Language Model)的对话模型,目前发展到v4版本,v3版本已开源,获得相当高的关注 本地部署ChatGLM3-6B模型详细步骤,包括环境配置介绍 ChatGLM3-6B模型结构分析:对ChatGLM3的模型结构进行了详细分析,包括其类图结构、流式推理流程以及prompt结构等 显存分析:分析了显存使用与上下文长度的关系 下一节将介绍百川2的本地部署 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.3-baichuan.html":{"url":"chapter-10/10.3-baichuan.html","title":"10.3 Baichuan2 部署与分析","keywords":"","body":"10.3 Baichuan2 部署与分析 baichuan作为首批开源的国产大语言模型,具备7B和13B两个尺寸,在多个子任务上有出色表现,本节就来了解baichuan系列大模型。 Baichuan 简介 Baichuan开源大模型由百川智能研发,目前最新开源版本为Baichuan2,闭源版本为Baichuan3。 百川智能成立于2023年4月10日,由前搜狗公司CEO王小川创立。公司以帮助大众轻松、吾普惠地获取世界知识和专业服务为使命,致力于通过语言AI的突破,构建中国最优秀的大模型底座。 Baichuan2提供7B,13B两个尺寸,具体如下 基座模型 对齐模型 对齐模型 4bits 量化 7B Baichuan2-7B-Base Baichuan2-7B-Chat Baichuan2-7B-Chat-4bits 13B Baichuan2-13B-Base Baichuan2-13B-Chat Baichuan2-13B-Chat-4bits 更多关于Baichuan的信息,可查阅: 公司首页:https://www.baichuan-ai.com/home github: https://github.com/baichuan-inc 技术报告:https://arxiv.org/abs/2309.10305 本地部署安装 第一步,下载下载baichuan2代码 git clone https://github.com/baichuan-inc/Baichuan2 第二步,下载7B-in4模型权重 git clone https://huggingface.co/baichuan-inc/Baichuan2-7B-Chat-4bits (也可以通过github desktop下载) 第三步,环境配置 根据Baichuan2中的 requirements.txt进行安装,其中pytorch环境自行配置,要求 pytorch ≥ 2.x pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 第四步,报错处理 根据官方的教程,安装了requirements.txt后报错,通常会报错: init model ... A matching Triton is not available, some optimizations will not be enabled Traceback (most recent call last): File \"C:\\Users\\yts32\\anaconda3\\envs\\pt220\\lib\\site-packages\\xformers\\__init__.py\", line 55, in _is_triton_available from xformers.triton.softmax import softmax as triton_softmax # noqa File \"C:\\Users\\yts32\\anaconda3\\envs\\pt220\\lib\\site-packages\\xformers\\triton\\softmax.py\", line 11, in import triton ModuleNotFoundError: No module named 'triton' C:\\Users\\yts32\\anaconda3\\envs\\pt220\\lib\\site-packages\\bitsandbytes\\cuda_setup\\main.py:166: UserWarning: Welcome to bitsandbytes. For bug reports, please run python -m bitsandbytes 如果是linux,可以尝试 pip install bitsandbytes==0.41.1 -q pip install accelerate==0.25.0 -q 参考自:https://github.com/baichuan-inc/Baichuan2/issues/52 如果是windows,可以尝试,先下载bitsandbytes-windows版的0.41.1的安装包,再手动安装。原因是通过pip install bitsandbytes,只能获得linux的,而windows的目前最高版本时0.37.x,因此需要手动下载安装。 https://github.com/jllllll/bitsandbytes-windows-webui/releases/download/wheels/bitsandbytes-0.41.1-py3-none-win_amd64.whl pip install bitsandbytes-0.41.1-py3-none-win_amd64.whl 参考自:https://github.com/baichuan-inc/Baichuan2/issues/35 如果报错:TypeError: 'NoneType' object is not subscriptable pip install accelerate==0.25.0 -q 如果报错:auto-gptq 0.7.1 requires accelerate>=0.26.0, but you have accelerate 0.25.0 which is incompatible. https://github.com/AutoGPTQ/AutoGPTQ pip install auto-gptq==0.6 第五步,配置路径 model, model.generation_config, tokenizer三个的路径需要配置 def init_model(): print(\"init model ...\") model = AutoModelForCausalLM.from_pretrained( r\"G:\\04-model-weights\\Baichuan2-7B-Chat-4bits\", torch_dtype=torch.float16, device_map=\"auto\", trust_remote_code=True ) model.generation_config = GenerationConfig.from_pretrained( r\"G:\\04-model-weights\\Baichuan2-7B-Chat-4bits\" ) tokenizer = AutoTokenizer.from_pretrained( r\"G:\\04-model-weights\\Baichuan2-7B-Chat-4bits\", use_fast=False, trust_remote_code=True ) return model, tokenizer 第六步,运行cli_demo.py C:\\Users\\yts32\\anaconda3\\envs\\chatglm\\python.exe D:\\github_desktop\\Baichuan2\\cli_demo.py init model ... bin C:\\Users\\yts32\\anaconda3\\envs\\chatglm\\lib\\site-packages\\bitsandbytes\\libbitsandbytes_cuda121.dll 欢迎使用百川大模型,输入进行对话,vim 多行输入,clear 清空历史,CTRL+C 中断生成,stream 开关流式生成,exit 结束。 用户:你好 Baichuan 2: 你好今天我能为您提供什么帮助? 用户:你是谁 Baichuan 2:我是百川大模型,是由百川智能的工程师们创造的大语言模型,我可以和人类进行自然交流、解答问题、协助创作,帮助大众轻松、普惠的获得世界知识和专业服务。如果你有任何问题,可以随时向我提问 模型结构分析 Baichuan2的模型结构可通过如下UML类图了解,其他更多模型结构可以参考前两节Qwen和ChatGLM的结构分析。 Prompt 结构分析 baichuan2的Prompt结构是经典的三角色设计,包括system, user, assistant。 在示例代码中,并没有给出system的预设,需要分析源代码后才看到system可通过messages来维护。bichuan2中的messages等同于history的作用,用于记录历史对话信息。 一个真实的messages如下: [{'content': '你好', 'role': 'user'}, {'content': '你好今天我能为您提供什么帮助?', 'role': 'assistant'}, {'content': '今天天气如何', 'role': 'user'}] 特殊token处理 不同的角色之间,通常用特殊token标记,在baichun2代码中,可通过generation_config中看到特殊token的index,但对应的text没有显示给出。 \\.cache\\huggingface\\modules\\transformers_modules\\Baichuan2-7B-Chat-4bits\\generation_utils.py # 以下代码是组装历史对话的代码段,首先判断当前角色,然后获取角色分隔token for message in round: if message[\"role\"] == \"user\": round_tokens.append(model.generation_config.user_token_id) else: round_tokens.append(model.generation_config.assistant_token_id) 单轮推理长度限制 模型支持的上下文是4K,这里包括输入+输出=4K,在单轮对话时,会对输入长度做限制。 首先,预留2K是用于本轮对话的输出,因此输入的最大长度为4K-2K=2K。详细代码如下: max_input_tokens = model.config.model_max_length - max_new_tokens input_tokens = input_tokens[-max_input_tokens:] # truncate left 其中: model.config.model_max_lengt = 4096 max_new_tokens = 2048 参考自:C:\\Users\\yts32\\.cache\\huggingface\\modules\\transformers_modules\\Baichuan2-7B-Chat-4bits\\generation_utils.py 对于输入超过2K的情况,是会被向左截断。 显存与上下文长度分析 百川官方给出了字符串长度与token之间的换算的比例,一般情况下Baichuan2大模型1个token约等于1.5个中文汉字。详见产品定价:https://cq6qe6bvfr6.feishu.cn/wiki/DOxNw9t97iwL3hkPB41ctfsMnMI 通过分析发现: 在未进行对话时,显存仅占用5.3GB 第一次对话时,显存立即飙升到6.4GB 前2000字符显存消耗不高,2000之后显存消耗激增 超过3500字符后,同样出现了截断(参考Qwen、ChatGLM的分析) 统计代码如下,完整代码cli_demo.py位于github conversation_length = sum([len(content['content']) for content in messages]) import subprocess import json result = subprocess.run(['gpustat', '--json'], stdout=subprocess.PIPE) output = result.stdout.decode() data = json.loads(output) used_memory = data['gpus'][0]['memory.used'] f.writelines(\"{}, {}\\n\".format(conversation_length, used_memory)) f.flush() 小结 本节对Baichuan2模型进行了本地部署安装,并分析模型结构、prompt结构、推理上限机制、显存分析等内容,有助于进一步理解LLM原理。 下一小节,分析Yi。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.4-yi.html":{"url":"chapter-10/10.4-yi.html","title":"10.4 Yi 部署与分析","keywords":"","body":"10.4 Yi 部署与分析 Yi 简介 Yi是由零一万物开源的大语言模型,目前(2024年4月16日)包括6B和34B-chat版本,base版本有6B、9B和34B。 零一万物是2023年7月,李开复筹组新公司,部注册于北京,集中在大模型技术、人工智能算法、自然语言处理、系统架构、算力架构、数据安全、产品研发等领域。 更多信息: 公司官网:https://www.lingyiwanwu.com/ HF: https://huggingface.co/01-ai github: https://github.com/01-ai LLM-github:https://github.com/01-ai/Yi 技术报告:https://arxiv.org/abs/2403.04652 部署安装 第一步,代码下载 git clone https://github.com/01-ai/Yi.git 第二步,权重下载 git clone https://www.modelscope.cn/01ai/Yi-6B-Chat-4bits.git 第三步,量化环境安装 int4和int8分别需要AWQ和GPTQ环境 pip install autoawq https://github.com/casper-hansen/AutoAWQ?tab=readme-ov-file#install-from-pypi https://github.com/AutoGPTQ/AutoGPTQ?tab=readme-ov-file#quick-installation 第四步,配置代码中模型路径 parser.add_argument( \"-c\", \"--checkpoint-path\", type=str, default=r\"G:\\04-model-weights\\Yi-6B-Chat-4bits\", help=\"Checkpoint name or path, default to %(default)r\", ) default=r\"G:\\04-model-weights\\Yi-6B-Chat-4bits\", 第五步,运行代码 Yi没有提供命令行的交互demo,提供的事web ui版,运行Yi\\demo\\web_demo.py 即可跳出基于gradio的交互界面。 修改后的代码可参考github 模型UML分析 Yi的github仓库及模型权重仓库均未找到类似Qwen、ChatGLM、Baichuan那样的模型文件,因此无法深入探究模型结构。 为了探究Yi模型的推理步骤,debug观察到以下信息,供流程分析所用。 model = AutoModelForCausalLM.from_pretrained 获得的模型是LlamaForCausalLM类,其中的核心model是LlamaModel LlamaForCausalLM( (model): LlamaModel( (embed_tokens): Embedding(64000, 4096) (layers): ModuleList( (0-31): 32 x LlamaDecoderLayer( (self_attn): LlamaSdpaAttention( (q_proj): WQLinear_GEMM(in_features=4096, out_features=4096, bias=False, w_bit=4, group_size=128) (k_proj): WQLinear_GEMM(in_features=4096, out_features=512, bias=False, w_bit=4, group_size=128) (v_proj): WQLinear_GEMM(in_features=4096, out_features=512, bias=False, w_bit=4, group_size=128) (o_proj): WQLinear_GEMM(in_features=4096, out_features=4096, bias=False, w_bit=4, group_size=128) (rotary_emb): LlamaRotaryEmbedding() ) (mlp): LlamaMLP( (gate_proj): WQLinear_GEMM(in_features=4096, out_features=11008, bias=False, w_bit=4, group_size=128) (up_proj): WQLinear_GEMM(in_features=4096, out_features=11008, bias=False, w_bit=4, group_size=128) (down_proj): WQLinear_GEMM(in_features=11008, out_features=4096, bias=False, w_bit=4, group_size=128) (act_fn): SiLU() ) (input_layernorm): LlamaRMSNorm() (post_attention_layernorm): LlamaRMSNorm() ) ) (norm): LlamaRMSNorm() ) (lm_head): Linear(in_features=4096, out_features=64000, bias=False) ) 在模型权重的config.json中,体现模型架构为LlamaForCausalLM { \"architectures\": [ \"LlamaForCausalLM\" ], \"attention_bias\": false, \"bos_token_id\": 1, \"eos_token_id\": 2, \"hidden_act\": \"silu\", \"hidden_size\": 4096, \"initializer_range\": 0.02, \"intermediate_size\": 11008, \"max_position_embeddings\": 4096, \"model_type\": \"llama\", \"num_attention_heads\": 32, \"num_hidden_layers\": 32, \"num_key_value_heads\": 4, \"pretraining_tp\": 1, \"quantization_config\": { \"bits\": 4, \"group_size\": 128, \"quant_method\": \"awq\", \"version\": \"gemm\", \"zero_point\": true }, \"rms_norm_eps\": 1e-05, \"rope_scaling\": null, \"rope_theta\": 5000000.0, \"tie_word_embeddings\": false, \"torch_dtype\": \"float16\", \"transformers_version\": \"4.35.0\", \"use_cache\": true, \"vocab_size\": 64000 } Prompt 结构分析 web_demo.py代码结构整体基于transformers库的工具来实现,推理采用流处理,基于transformers的TextIteratorStreamer实现,模型单次推理由TextIteratorStreamer代理,这里不深入。 这里看看Yi源代码中的predict函数,该函数对历史对话进行了处理,实现多轮对话的Prompt处理。大体可分为4步: 第一步:将历史信息转为模型输入的tokens_ids, 这一步调用transformers的apply_chat_template接口功能实现; 第二步:创建流处理器TextIteratorStreamer 第三步:组装本轮对话所需信息,generate_kwargs 第四步:启动线程执行model.generate, 从流处理器streamer中拿单次推理的结果 由于大部分是transformers库的代码,此处就不深入展开了 def predict(history, max_length, top_p, temperature): stop = StopOnTokens() messages = [] for idx, (user_msg, model_msg) in enumerate(history): if idx == len(history) - 1 and not model_msg: messages.append({\"role\": \"user\", \"content\": user_msg}) break if user_msg: messages.append({\"role\": \"user\", \"content\": user_msg}) if model_msg: messages.append({\"role\": \"assistant\", \"content\": model_msg}) print(\"\\n\\n====conversation====\\n\", messages) model_inputs = tokenizer.apply_chat_template( messages, add_generation_prompt=True, tokenize=True, return_tensors=\"pt\" ).to(next(model.parameters()).device) streamer = TextIteratorStreamer( tokenizer, timeout=60, skip_prompt=True, skip_special_tokens=True ) generate_kwargs = { \"input_ids\": model_inputs, \"streamer\": streamer, \"max_new_tokens\": max_length, \"do_sample\": True, \"top_p\": top_p, \"temperature\": temperature, \"stopping_criteria\": StoppingCriteriaList([stop]), \"repetition_penalty\": 1.2, } t = Thread(target=model.generate, kwargs=generate_kwargs) t.start() for new_token in streamer: if new_token != \"\": history[-1][1] += new_token yield history 小结 通过Yi的代码,可以了解如何快速基于transformers构建一个LLM推理部署代码。 并且可以了解GPTQ和AWQ的部署需要单独安装对应的python库。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-10/10.5-gpt-academic.html":{"url":"chapter-10/10.5-gpt-academic.html","title":"10.5 GPT Academic 安装与使用","keywords":"","body":"10.5 GPT Academic 安装与使用 前言 本节介绍一个LLM的具体应用场景,也是热门、火爆的应用和场景,目前(2024年4月18日)github已经有54K stars。 希望通过这个应用案例,介绍LLM的能力、LLM的作用,帮助大家找到LLM用武之地。 本节将介绍本地部署 gpt academic工具,它是为GPT/GLM等LLM大语言模型提供实用化交互接口,特别优化论文阅读/润色/写作体验,模块化设计,支持自定义快捷按钮&函数插件,支持Python和C++等项目剖析&自译解功能,PDF/LaTex论文翻译&总结功能,支持并行问询多种LLM模型,支持chatglm3等本地模型。接入通义千问, deepseekcoder, 讯飞星火, 文心一言, llama2, rwkv, claude2, moss等。 更多信息查阅github:https://github.com/binary-husky/gpt_academic 本地安装 第一步,下载代码 git clone https://github.com/binary-husky/gpt_academic 第二步,安装环境 第一步:python -m pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ 第二步:python -m pip install -r request_llms/requirements_qwen_local.txt 第三步,修改配置文件 这里采用本地Qwen模型,当然大家有API_KEY,那用在线模型就更方便了。(本地qwen模型版本的config文件放在github了) 3.1 修改config.py,设置本地模型列表,将qwen添加进去 # [step 3]>> 模型选择是 (注意: LLM_MODEL是默认选中的模型, 它*必须*被包含在AVAIL_LLM_MODELS列表中 ) LLM_MODEL = \"gpt-3.5-turbo-16k\" # 可选 ↓↓↓ AVAIL_LLM_MODELS = [\"gpt-4-1106-preview\", \"gpt-4-turbo-preview\", \"gpt-4-vision-preview\", \"gpt-3.5-turbo-1106\", \"gpt-3.5-turbo-16k\", \"gpt-3.5-turbo\", \"azure-gpt-3.5\", \"gpt-4\", \"gpt-4-32k\", \"azure-gpt-4\", \"glm-4\", \"glm-3-turbo\", \"gemini-pro\", \"chatglm3\", \"qwen-local\" ] 3.2. 配置本地模型的路径,此路径是模型权重路径 QWEN_LOCAL_MODEL_SELECTION = r\"G:\\04-model-weights\\qwen\\Qwen-1_8B-Chat\" 第四步,运行程序 运行后跳出主页面,左上角选择qwen-local,即可进行对话。 python main.py 选用在线模型的配置步骤如下: 第一步:api key配置 config.py配置api key到API_KEY变量里面 第二步:修改重定向URL API_URL_REDIRECT = {\"https://api.openai.com/v1/chat/completions\": \"https://api.chatanywhere.com.cn/v1/chat/completions\"} 第三步:修改代理配置 USE_PROXY = True if USE_PROXY: proxies = { # [协议]:// [地址] :[端口] # \"http\": \"socks5h://localhost:11284\", # 再例如 \"http\": \"http://127.0.0.1:7890\", # \"https\": \"socks5h://localhost:11284\", # 再例如 \"https\": \"http://127.0.0.1:7890\", \"http\": \"http://127.0.0.1:7890\", \"https\": \"http://127.0.0.1:7890\", } 界面使用介绍 gpt academic的页面功能按钮比较多,需要了解各功能模块,方便使用。大体可以分为4个区域,如下图的红色、黄色、蓝色和绿色区域所示: 红色:菜单栏,可上传文档、压缩包,批量处理的文档、代码,通过这里上传;更换大语言模型;修改界面外观 黄色:对话区,所有的对话交互在这里显示 蓝色:用户文本输入区,主要是一些对话、问题的输入,文档和代码通过模块进行传入 绿色:核心功能区域,本项目的核心在此处,提供了多种辅助功能。这些功能主要内容是帮用户撰写好了提示词,便于用户一键发送prompt。 gpt academic项目主要作用有两个: 内置了一系列提示词模板,帮助用户快速完成学术方面的对话任务。 实现各类批量处理操作,提高用户使用LLM效率,例如批量总结PDF文档、解析代码文件夹等,感受下来是将一些列python处理的自动化工具集成好,变为按钮方式提供用户使用,这部分才是该项目真正的生产力体现。 下面先看基础功能的使用: 学术语料润色 上文提到,基础功能主要是内置了prompt,让我们只需要点击按钮,即可发送响应指令,快速使用LLM功能。 这里的学术语料润色按钮,点击之后,会将以下提示词+输入区的文本,一并发送给LLM,以此完成润色功能。 内置prompt: 作为一名中文学术论文写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性,同时分解长句,减少重复,并提供改进建议。请先提供文本的更正版本,然后在markdown表格中列出修改的内容,并给出修改的理由: 页面效果如下图所示: 第一步,在输入区输入希望润色的文字内容; 第二步,点击学术语料润色按钮; 第三步,对话框会输出结果。 通过一个案例,大体可基础功能的使用,不同的功能,内置了不同的提示词模板,加快用户与LLM对话的过程,提高工作效率。 查找语法错误 Help me ensure that the grammar and the spelling is correct. Do not try to polish the text, if no mistake is found, tell me that this paragraph is good. If you find grammar or spelling mistakes, please list mistakes you find in a two-column markdown table, put the original text the first column, put the corrected text in the second column and highlight the key words you fixed. Finally, please provide the proofreaded text. Example: Paragraph: How is you? Do you knows what is it? | Original sentence | Corrected sentence | | :--- | :--- | | How is you? | How are you? | | Do you knows what is it? | Do you know what it is ? | Below is a paragraph from an academic paper. You need to report all grammar and spelling mistakes as the example before. 中译英 Please translate following sentence to English: 学术英中互译 I want you to act as a scientific English-Chinese translator, I will provide you with some paragraphs in one language and your task is to accurately and academically translate the paragraphs only into the other language. Do not repeat the original provided paragraphs after translation. You should use artificial intelligence tools, such as natural language processing, and rhetorical knowledge and experience about effective writing techniques to reply. I'll give you my paragraphs as follows, tell me what language it is written in, and then translate: 解释代码 请解释以下代码: 进阶功能主要包括一系列批量操作的功能,例如批量总结Word文档、批量Markdown中译英、解析代码项目文档等。 处理批量操作外,还有一系列通过代码实现的实用功能,如一键下载arXiv论文、全文润色、绘制脑图等。 下面先了解批量处理的功能 批量总结Word文档 该功能会对上传的所有word文档逐一总结,每个word文档会根据LLM的上下文长度配置进行阶段,过长的文档被切分为多个片段。 使用过程第一步,左上角上传文件中,上传所有word文档 第二步,点击 第三步,等待处理结束,在右下角找到“文件下载区”,即可下载总结好的文本内容。 该功能提供的prompt为: i_say = f'请对下面的文章片段用中文做概述,文件名是{os.path.relpath(fp, project_folder)},文章内容是 ```{paper_frag}```' 如果文章被切分,还会对所有概述内容进行一次文章总结。 i_say = f\"根据以上的对话,总结文章{os.path.abspath(fp)}的主要内容。\" 运行完毕,结果会以markdown格式生成文件,在右下角可下载。 解析整个Python项目 除了对文档(word、pdf)的批量总结,gpt academic还支持对代码项目的解析总结,目前支持python, Matlab, c++, Go, Rust, Java, js, ts, css, Lua, CSharp。 使用步骤: 第一步,将项目代码压缩为zip,然后在上传文件区域上传 第二步,点击,则自动开始解析。 这里采用gpt academic项目代码为例,看看解析效果: 逐个文件总结: [6/7] 请对下面的程序文件做一个概述: private_upload\\default_user\\2024-04-18-10-26-51\\gpt_academic.zip.extract\\gpt_academic\\crazy_functions__init__.py 这是一个Python脚本,它使用OpenAI的GPT-3.5-Turbo模型进行微调,并将生成的JSON数据保存到指定的目录中。 定义了一个fetch_items函数,该函数接受一组列表作为输入,每个列表代表一个待处理的文件。对于每一个文件,该函数会读取其内容并将其转换为字符串格式。 ...略... 这个脚本应该可以作为一个简化的示例来了解如何使用OpenAI的预训练语言模型来进行微调,并将其部署到生产环境中。 所有py汇总 逐个文件分析已完成。D:\\github_desktop\\gpt_academic\\gpt_log\\default_user\\shared\\GPT-Academic-2024-04-18-10-33-40.md 正在开始汇总。 这个Python程序是一个网络爬虫,用于抓取HTML内容并从中提取有用的信息。这个程序使用了requests库来发送HTTP请求,并使用BeautifulSoup库来解析这些请求的响应。此外,这个程序还使用了pandas库来处理大量的CSV文件,并将它们转换为字典,以便进行进一步的数据挖掘。 ...略... 最后,当一切顺利时,我们停止爬取流程,并从服务器上解压tar.gz文件,得到原始的CSV文件内容。我们可以将这些内容存储在一个数据库中,以便后续使用。 最后还有一个基于mermaid软件的脑图展示,非常直观。(脑图只是一部分) Prompt逻辑分析 首先会设定system role sys_prompt_array.append(\"你是一个程序架构分析师,正在分析一个源代码项目。你的回答必须简单明了。\") 用户prompt,针对单个文件 prefix = \"接下来请你逐文件分析下面的工程\" i_say = prefix + f'请对下面的程序文件做一个概述文件名是{os.path.relpath(fp, project_folder)},文件代码是 ```{file_content}```' 用户prompt,针对项目维度 i_say = f'用一张Markdown表格简要描述以下文件的功能:{focus}。根据以上分析,用一句话概括程序的整体功能。' summary = \"请用一句话概括这些文件的整体功能。\\n\\n\" + diagram_code 通过源代码的分析,可知道处理逻辑是,先对每个代码文件进行概述,然后汇总所有代码文件的概述内容,再让LLM进行概括和markdown表格的输出。 下载arXiv论文并翻译摘要 可下载arxiv论文pdf文件,还可以将摘要翻译为中文,加快研究者下载文章、挑选文章的速度。 使用非常简单,第一步,在输入区输入编号,第二步,插件中找到一键下载arxiv论文并翻译摘要(先在input输入编号如1812.10695,点击启动。 内置prompt如下 sys_prompt=\"Your job is to collect information from materials and translate to Chinese。\", f\"请你阅读以下学术论文相关的材料,提取摘要,翻译为中文。材料如下:{str(info)}\" 基于知识库问答 下面介绍一个高级功能,本地知识库的功能,常用的场景是,基于论文PDF进行问答、基于内部知识文档进行问答。 这部分是借助了langchain的功能,实现了一个简易版本的RAG,从而让用户可以基于上传的文档进行对话。 整个过程分两步,第一步构建知识库,第二步知识库文件注入。这两步需要两个不同的插件功能。 由于issue上有大量不成功案例和问题未解决,同时此处尝试解决4个error后仍不能成功,在这里不进行此处的演示,其背后逻辑与langchain那套类似的,想要体验基于文档的问答,可关注fastgpt、chachat这两个项目。 项目工程结构分析 gpt academic项目提供的功能有好几十个,这些都是开源的力量,众人拾柴火焰高。 为了能让大家贡献插件,项目提供了功能设计的模板,并且统一存放在crazy_functions文件夹,其中配套使用的一些功能函数存放在toolbox.py,LLM模型适配存放在request_llms文件夹。 代码结构相对清晰,模块化程度高,值得学习,但是代码格式风格上可以再优雅一些(笑哭)。 这里采用功能,对gpt academic项目进行了解读,耗时数小时,花费上万token,内容较多,这里就放到云盘,大家下载html来查看吧。 提取码:epfq 小结 本小节介绍了LLM模型的具体应用——学术生产力工具(gpt academic),通过本小节不仅了解学术研究的先进生产力,更了解了LLM是如何辅助人们提高生产效率。这也是当下LLM发展的关键,找到关键场景,关键痛点,然后利用LLM的能力进行改造、改进,从而实现LLM的落地。 如果没有痛点场景,关键问题的发现,那么LLM仅是一个简单的对话机器人,无法发挥TA的价值。通过本节、本章的内容,希望大家能了解LLM的概念,能力边界,清晰的认识到场景定义LLM,场景结合LLM,场景+LLM,一切场景先行,而不是拿着LLM去找场景。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-11/":{"url":"chapter-11/","title":"第十一章 ONNX 使用","keywords":"","body":"第十一章 ONNX 使用 第十一章 ONNX 使用 11.1 ONNX 简介与安装 11.2 ONNX Runtime 简介与使用 11.3 ONNX Runtime 进阶使用 第十一章简介 本章介绍模型部署的第一个工具——ONNX。 ONNX (Open Neural Network Exchange,开放神经网络交换格式)是一种开放的、跨平台的深度学习模型交换格式,可以方便地将模型从一个框架转移到另一个框架,可谓是模型部署必须要了解的一个工具。 本章将从ONNX的概念及原理介绍开始,再介绍ONNX配套的推理引擎——ONNXRuntime, 最后介绍ONNXRuntime中常用的优化方法(float16量化、int8量化、混合精度量化、计算图优化、线程管理和IO binding)。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-11/11.1-onnx-introduction.html":{"url":"chapter-11/11.1-onnx-introduction.html","title":"11.1 ONNX 简介与安装","keywords":"","body":"11.1 ONNX 简介与安装 前言 在深度学习算法开发过程中,模型训练与部署是两个环节,pytorch通常只用于训练,获得模型权重文件,而最终部署还有专门的部署平台,例如TensorRT、NCNN、OpenVINO等几十种部署推理平台。 如何将pytorch模型文件让几十种部署推理平台能接收与读取是个大问题。即使各推理平台都适配pytorch,那还有其他训练框架也要适配,是非常麻烦的。 假设有N个训练框架,M个推理框架,互相要适配,那就是O(NM)的复杂度。如果能有一种中间格式作为一个标注,能被所有框架所适配,那复杂度顺便降低为O(N+M)。 onnx就是为了降低深度学习模型从训练到部署的复杂度,由微软和meta在2017年提出的一种开放神经网络交换格式,目的在于方便的将模型从一个框架转移到另一个框架。 本小结就介绍onnx基础概念、pytorch模型导出onnx模型。 ONNX 简介 ONNX (Open Neural Network Exchange,开放神经网络交换格式)是一种开放的、跨平台的深度学习模型交换格式,可以方便地将模型从一个框架转移到另一个框架。 onnx最初由微软和meta在2017年联合发布,后来亚马逊也加入进来,目前已经成为行业共识,目前已经有50多个机构的产品支持onnx。 onnx最大的优点是简化了模型部署之间因框架的不同带来的繁琐事,这就像普通话。在中国129种方言之间要互相通信是很困难的,解决办法就是设计一种可以与129种语言进行转换的语言——普通话。onnx就是一个支持绝大多数主流机器学习模型格式之间转换的格式。 采用pytorch进行模型开发时,部署环节通常将pytorch模型转换为onnx模型,然后再进行其他格式转换,或者直接采用onnx文件进行推理,在本章节就介绍采用onnx文件进行推理的方法。 ONNX 基础概念 onnx文件是一种计算图,用于描述数据要进行何种计算,它就像是数学计算的语言,可以进行计算的操作称之为操作符——operator,一系列operator构成一个计算图。 计算图中包含了各节点、输入、输出、属性的详细信息,有助于开发者观察模型结构。 下面通过一个线性回归模型的计算图来了解onnx的计算图 可以采用python代码构建onnx计算图,运行配套代码,构建了一个线性回归模型 from onnx import TensorProto from onnx.helper import ( make_model, make_node, make_graph, make_tensor_value_info) # 'X' is the name, TensorProto.FLOAT the type, [None, None] the shape X = make_tensor_value_info('X', TensorProto.FLOAT, [None, None]) A = make_tensor_value_info('A', TensorProto.FLOAT, [None, None]) B = make_tensor_value_info('B', TensorProto.FLOAT, [None, None]) Y = make_tensor_value_info('Y', TensorProto.FLOAT, [None]) node1 = make_node('MatMul', ['X', 'A'], ['XA']) node2 = make_node('Add', ['XA', 'B'], ['Y']) graph = make_graph([node1, node2], # nodes 'lr', # a name [X, A, B], # inputs [Y]) # outputs onnx_model = make_model(graph) with open(\"linear_regression.onnx\", \"wb\") as f: f.write(onnx_model.SerializeToString()) 运行以上代码会获得linear_regression.onnx文件,可通过https://netron.app/ 进行可视化 图中 A, B, X, Y表示输入输出数据 黑色的MatMul和Add是Node,表示具体的操作 format:表示生成该onnx文件的onnx版本 imports:operator的版本;算子是onnx中最重要的一个概念,大多数模型不成功是因为没有对应的算子,因此算子集的版本选择很重要; inputs和outputs:是输入和输出,其中type是数据类型以及shape。 为了进一步了解onnx文件,下面导出一个resnet50进行观察,onnx文件可通过以下代码获得: import torchvision import torch model = torchvision.models.resnet50(pretrained=False) dummy_data = torch.randn((1, 3, 224, 224)) with torch.no_grad(): torch.onnx.export(model, (dummy_data), \"resnet50.onnx\", opset_version=11, input_names=['input_name_edit_by_tingsongyu'], output_names=['output_name_edit_by_tingsongyu']) 下面再看一个resnet50的onnx文件,观察更多算子的描述。 更多onnx基础概念参见官网:https://onnx.ai/onnx/intro/concepts.html ONNX 的operator 上面介绍了onnx文件主要定义了计算图,计算图中的每个操作称为算子,算子库的丰富程度,直接决定了onnx可以表示模型的种类。 关于onnx支持哪些算子,一定要上官网看一看。 对于普通用户,需要关注使用时的opset是哪个版本,目前最新版本是20。算子库可通过以下函数查看。 import onnx print(onnx.version, \" opset=\", onnx.defs.onnx_opset_version()) 关于算子的理解,以及不适配问题,推荐OpenMMLab的三篇博文 https://zhuanlan.zhihu.com/p/479290520:讲解了pytorch转onnx时,每一个操作是如何转换到onnx算子的;介绍了算子映射关系 https://zhuanlan.zhihu.com/p/498425043:讲解了pytorch转onnx时,trace和script两种模式下的区别;以及torch.onnx.export()函数的使用; https://zhuanlan.zhihu.com/p/513387413:讲解了三种添加算子的方法 其中有一张图对于理解pytorch转onnx很有帮助,这里引用一下: ONNX 安装 onnx的安装很简单:pip install onnx 在这里,提前说一下,onnx是onnx,与onnxruntime不是同一个东西,它们要分开安装,也要分开理解。 pytorch导出onnx pytorch模型导出为onnx调用torch.onnx.export函数即可,该函数包含很多参数,这里只介绍几个常用的,更多的参考官方文档 torch.onnx.export(model, args, f, export_params=True, verbose=False, training=, input_names=None, output_names=None, operator_export_type=, opset_version=None, do_constant_folding=True, dynamic_axes=None, keep_initializers_as_inputs=None, custom_opsets=None, export_modules_as_functions=False) model: 需要被转换的模型,可以有三种类型, torch.nn.Module, torch.jit.ScriptModule or torch.jit.ScriptFunction args:model输入时所需要的参数,这里要传参时因为构建计算图过程中,需要采用数据对模型进行一遍推理,然后记录推理过程需要的操作,然后生成计算图。args要求是tuple或者是Tensor的形式。一般只有一个输入时,直接传入Tensor,多个输入时要用tuple包起来。 export_params: 是否需要保存参数。默认为True,通常用于模型结构迁移到其它框架时,可以用False。 input_names:输入数据的名字, (list of str, default empty list) ,在使用onnx文件时,数据的传输和使用,都是通过name: value的形式。 output_names:同上。 opset_version:使用的算子集版本。 dynamic_axes:动态维度的指定,例如batchsize在使用时随时会变,则需要把该维度指定为动态的。默认情况下计算图的数据维度是固定的,这有利于效率提升,但缺乏灵活性。用法是,对于动态维度的输入、输出,需要设置它哪个轴是动态的,并且为这个轴设定名称。这里有3个要素,数据名称,轴序号,轴名称。因此是通过dict来设置的。例如dynamic_axes={ \"x\": {0: \"my_custom_axis_name\"} }, 表示名称为x的数据,第0个轴是动态的,动态轴的名字叫my_custom_axis_name。通常用于batchsize或者是对于h,w是不固定的模型要设置动态轴。 接下来以resnet50为例,导出一个在ImageNet上训练好的分类模型,再通过netron观察区别。 下面使用配套代码导出三个模型,分别是bs=1, bs=128, bs为动态的,下一节将对比两者效率。 import torchvision import torch model = torchvision.models.resnet50(weights=torchvision.models.ResNet50_Weights.IMAGENET1K_V1) if __name__ == '__main__': op_set = 13 dummy_data = torch.randn((1, 3, 224, 224)) dummdy_data_128 = torch.randn((128, 3, 224, 224)) # 固定 batch = 1 torch.onnx.export(model, (dummy_data), \"resnet50_bs_1.onnx\", opset_version=op_set, input_names=['input'], output_names=['output']) # 固定 batch = 128 torch.onnx.export(model, (dummdy_data_128), \"resnet50_bs_128.onnx\", opset_version=op_set, input_names=['input'], output_names=['output']) # 动态 batch torch.onnx.export(model, (dummy_data), \"resnet50_bs_dynamic.onnx\", opset_version=op_set, input_names=['input'], output_names=['output'], dynamic_axes={\"input\": {0: \"batch_axes\"}, \"output\": {0: \"batch_axes\"}}) 对比如下图所示,input的type中,shape一个是1,一个是batch_axes,其中batch_axes这个就是自定义的命名。 小结 本小节介绍了onnx提出的目的与意义,还有基础概念,onnx可以作为一个中间格式,被绝大多数框架所适配,方便开发人员从训练框架转到开发框架。 onnx文件核心是记录模型的计算图,包括输入数据、各操作节点、输出数据等信息。 最后介绍了pytorch导出onnx的方法,其中需要主要的是op_set版本,以及动态维度的设置。 下一小节,将利用本节导出的onnx模型文件,在onnx的推理库——onnxruntime上进行推理以及性能效率评估。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-11/11.2-onnxruntime-intro.html":{"url":"chapter-11/11.2-onnxruntime-intro.html","title":"11.2 ONNX Runtime 简介与使用","keywords":"","body":"11.2 ONNXRuntime 简介与使用 前言 onnx是一个开放式的格式,它还需要放到推理框架(推理引擎)上运行才可以,支持运行onnx文件的框架有ONNX Rruntime、TensorRT、pytorch、TensorFlow等等。 在这里就介绍onnx自带的推理框架onnxruntime。 onnxruntime是onnx官方的推理框架,它与onnx库是完全两个东西,安装了onnx库并没有安装上onnxruntime,它需要额外安装。 onnxruntime分为cpu版和gpu版,两个版本的安装又分别是两个库,分别是 onnxruntime, onnxruntime-gpu onnxruntime-gpu的安装,又要求cuda、cudnn版本的严格匹配,否则会无法运行!这里被坑了挺长时间,下面讲讲onnxruntime的安装。 onnxruntime 安装 对于cpu安装,可以直接pip install onnxruntime,对于gpu版本的安装,通常不能直接pip install onnxruntime-gpu,而是要设置指定版本,因为cuda和cudnn版本会限制onnxruntime的版本。 版本的对应关系如官网所示:https://onnxruntime.ai/docs/execution-providers/CUDA-ExecutionProvider.html#requirements 例如,cuda版本是11.4,且cudnn是8.2.2.26,则可以pip install onnxruntime-gpu==1.10.0 。如果不是,那就需要配置对应版本的cuda、cudnn了。 通常来说,系统上cuda和cudnn的安装比较麻烦,并且更换版本也不方便。这里推荐直接在python虚拟环境中安装指定版本的cuda, cudnn,这样不会与系统的cuda、cudnn冲突。 例如: conda install cudatoolkit=11.3 -c pytorch -c conda-forge conda install cudnn==8.2.1 pip install onnxruntime-gpu==1.14.1 PS:需要注意的是,onnxruntime和onnxruntime-gpu不可并存,装了onnxruntime-gpu,也是可以调用cpu的,这里建议把onnxruntime卸载,只保留onnxruntime-gpu即可。 onnxruntime 使用 onnxruntime 中使用onnx文件,只需要将其加载到InferenceSession中,然后调用InferenceSession.run()就可以完成推理。 相比于pytorch,不需要在代码中保留如何定义模型的的class,也不用加载权重了,这一切都存储在onnx的计算图中。 InferenceSession 的初始化细节如下所示 class InferenceSession(Session): \"\"\" This is the main class used to run a model. \"\"\" def __init__(self, path_or_bytes, sess_options=None, providers=None, provider_options=None, **kwargs): \"\"\" :param path_or_bytes: filename or serialized ONNX or ORT format model in a byte string :param sess_options: session options :param providers: Optional sequence of providers in order of decreasing precedence. Values can either be provider names or tuples of (provider name, options dict). If not provided, then all available providers are used with the default precedence. :param provider_options: Optional sequence of options dicts corresponding to the providers listed in 'providers'. 在这里,需要关注的是providers,它的作用是指定可用的设备,如[\"CUDAExecutionProvider\", \"CPUExecutionProvider\", \"ROCMExecutionProvider\"]。 ort_session_bs1 = ort.InferenceSession('resnet50_bs_1.onnx', providers=['CUDAExecutionProvider']) inp = np.random.randn(1, 3, 224, 224).astype(np.float32) output = model.run(['output'], {'input': inp}) 完整的resnet50实现图像分类推理,参见配套代码,需要注意的是要与模型训练时的前处理、后处理保持一致。 onnxruntime 推理速度评估 为了观察batchsize对推理效率的影响,这里设计了三个模型的对比实验,分别是bs=1, bs=128, bs为动态时,从1到256的推理时延与吞吐量的对比。 通常说推理速度,只看一次推理的耗时是不足以反应模型在生产时的效率的,因为推理并行的存在,因此可以采用大的batchsize来提高单位时间内,处理样本的数量。 通常评估模型的推理的时间效率会将时延(latency)和吞吐量(throughout)一起观察。 这里简单介绍时延(latency)和吞吐量(throughout)的意义。 时延(latency):通常用于评估用户需要等待多长时间,根据业务场景,需要针对性保障时延,约莫等于平时说的耗时。 吞吐量(throughout):用于评估服务器一定时间内能处理的量,通常是为了提高单位时间内,能处理更多的用户请求。 时延和吞吐量通常是矛盾的,即想要高吞吐的时候,时延就会提高。 这个就像深夜的大排档,你到店里点一份炒河粉,需要等待多久?这取决于老板的策略是低延时,还是高吞吐。 低延时策略:来一个处理一个,尽快把你的一份河粉炒出来,需要3分钟。 高吞吐策略:稍微等等,等到3个炒河粉订单,一次性炒出来,等了3分钟,炒粉3分钟,总共6分钟,算下来,每分钟可以炒0.5份。而低时延策略的吞吐量显然低了,每分钟可以炒0.33份。 计算机的运行也是一样的,可以通过batchsize来权衡时延与吞吐量。 配套代码 首先来看bs是动态的模型,将bs从1到256的效率变化,数据如表所示: bs=1 2 4 8 16 32 64 128 256 时延ms 3.7 5.2 7.7 12.9 45.4 39.1 75.9 150.3 7285.6 吞吐量 frame/s 270 386 521 620 353 818 843 852 35 将吞吐量绘图如下图所示: 结论: 随着batchsize的增加,吞吐量逐步提高,在bs=128时,吞吐量增长平缓; cpu上推理,batchsize的增加,吞吐量差别不大,这也符合逻辑,毕竟cpu不是计算型处理器,无法批量处理大规模矩阵运算; 不定batchsize的模型与动态batchsize的模型,在相同batchsize下,效率并没有什么变化(注:由于变化没差别,表格中没有展示); 在onnruntime有一些奇怪的bs,当bs=16,bs=256时,运行效率出现异常,详情看表格; 建议:模型上线前,实际评测一下模型不同输入时的效率,选择合适的batchsize,可以最大化服务器利用率。 在这里,补充一个pytorch下resnet50的推理评估数据,左图为float32, 右图为半精度float16。配套代码 可以看到: 半精度的吞吐量可以提高50%左右,时延能降低30%左右。 同样的,随着batchsize的增加,吞吐量逐步提高,在bs=128时,吞吐量几乎不变。 小结 本节介绍了onnx自带推理框架——onnxruntime的安装及使用,同时评估了resnet50模型在固定batch和动态batch下,以及不同batchsize时,推理的效率。 通过推理效率的评估,可以知道,batchsize到达一定的量后,吞吐量饱和,因此无需追求过大的batchsize,毕竟大的batchsize,时延会增加。 这里就有一个疑问,如何充分利用gpu资源?例如有一个计算密集型的场景,需要resnet50在24小时不间断的推理,根据上面得出来的理论,batchsize 128就可以了,gpu的显存只需要3GB左右,对于一张16G的T4而言,是否存在浪费呢?虽然gpu的利用率非常高。不知大家对此问题有什么看法?欢迎留言评论,一起探讨。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-11/11.3-onnxruntime-advanced.html":{"url":"chapter-11/11.3-onnxruntime-advanced.html","title":"11.3 ONNX Runtime 进阶使用","keywords":"","body":"11.3 ONNXRuntime 进阶使用 前言 模型部署推理,除了换了一个框架平台外,最重要的是推理框架平台支持各种优化方法,使得模型推理效率更高。 在onnxruntime中也提供了一些优化方法,例如float16量化、int8量化、计算图优化等操作。 本小节就介绍在onnxruntime中实现float16量化、int8量化、混合精度量化、计算图优化、线程管理和IO binding。 float16量化、int8量化、混合精度量化:都是从数据存储及运算角度出发,提高运行效率 计算图优化:是从计算逻辑上优化计算过程 线程管理:是从设备资源分配及调度的角度,充分利用cpu资源以应对并行、串行的任务 IO binding:是从数据在设备之间迁移的问题考虑,将数据提前绑定到gpu上,减少cpu与gpu之间的通信 float16量化 官方文档 float16量化是常用的,精度损失较小的量化策略,通常模型的精度是float32,即一个浮点数用32位来表示,它占用4字节。 但大多数情况下,用16位表示浮点数,已经能满足要求。因此,在推理时可以将模型从float32转为float16。 float16的量化需要用到 onnxconverter-common库,先提前安装:pip install onnx onnxconverter-common。 然后只需要调用convert_float_to_float16,就可以把onnx模型转换为float16的模型。 import onnx from onnxconverter_common import float16 model = onnx.load(\"path/to/model.onnx\") model_fp16 = float16.convert_float_to_float16(model) onnx.save(model_fp16, \"path/to/model_fp16.onnx\") 通过配套代码,进行性能测试,可以看到相比于float32的效率,float16在吞吐量上18.8%((1023-861)/861)的提升。 除了性能效率提升,float16的onnx模型只有48.7MB, float32占97.4MB,磁盘存储占用少了50%。 int8量化 官方文档 ; 官方案例 既然能用低比特表示模型,能否再进一步简化,采用8bit来表示float32呢?当然可以,8bit量化是一种常用于移动端、边缘端的优化策略。 量化又分动态量化(dynamic quantization)和静态量化(static quantization),这里采用动态量化演示,动态量化直接将数据量化到固定的8bit,而静态量化需要采用校准数据集进行一段时间的推理后,得到量化结果。 int8/uint8动态量化只需要采用两行代码,即可将量化后的模型保存到本地。 from onnxruntime.quantization import quantize_dynamic, QuantType _ = quantize_dynamic(path_model_float32, path_model_int8, weight_type=QuantType.QInt8) int8的onnx模型只有24.4MB, float32占97.4MB,float16占48.7MB,比float32少了75%,比float16少了50%。 通过配套代码,进行性能测试,发现int8的推理速度非常慢,连cpu的比不上,其中的原因可能是硬件的不匹配? 关于int8量化,也需要支持int8计算的显卡,例如T4和A100显卡。 “Hardware support is required to achieve better performance with quantization on GPUs. You need a device that supports Tensor Core int8 computation, like T4 or A100. Older hardware will not benefit from quantization.” 混合精度 官方文档 当float16精度不够的时候,可以考虑混合精度,在有必要的地方变为float16,其它地方保持float32,这样可以实现精度和速度的权衡。 混合精度的实现与float16导出类似,需要额外注意的是要给模型一些测试数据,用于评估哪些是float16,哪些是float32。 from onnxconverter_common import auto_mixed_precision import onnx model = onnx.load(\"path/to/model.onnx\") model_fp16 = auto_convert_mixed_precision(model, test_data, rtol=0.01, atol=0.001, keep_io_types=True) onnx.save(model_fp16, \"path/to/model_fp16.onnx\") 计算图优化 官方文档 除了对数据的量化,计算图层面的优化也可以提高推理效率,例如移除不必要的层、网络层的融合: Identity Elimination Slice Elimination Unsqueeze Elimination Dropout Elimination Conv Add Fusion Conv Mul Fusion Conv BatchNorm Fusion Relu Clip Fusion Reshape Fusion 整体上,计算图优化分为三个level:Basic、Extended和Layout Optimizations。 代码实现上比较简单,是在InferenceSession实例化的时候,添加sess_options,就可实现计算图优化。 sess_options需要设置优化的类型,以及优化后模型保存路径。 优化类型有4个level可选: GraphOptimizationLevel::ORT_DISABLE_ALL -> Disables all optimizations GraphOptimizationLevel::ORT_ENABLE_BASIC -> Enables basic optimizations GraphOptimizationLevel::ORT_ENABLE_EXTENDED -> Enables basic and extended optimizations GraphOptimizationLevel::ORT_ENABLE_ALL -> Enables all available optimizations including layout optimizations import onnxruntime as rt sess_options = rt.SessionOptions() # Set graph optimization level sess_options.graph_optimization_level = rt.GraphOptimizationLevel.ORT_ENABLE_EXTENDED # To enable model serialization after graph optimization set this sess_options.optimized_model_filepath = \"\" session = rt.InferenceSession(\"\", sess_options) 通过配套代码,可以观察吞吐量,发现没有得到任何变化,这可能与设备有关,在这里暂时不深究,后续生产真用到了,再回来看。 在这里需要注意的是,由于onnx的显存不能自动释放,一次性跑两个模型的效率评估的话,第二个模型会受到资源问题,导致效率评估不准确,这里代码中需要手动切换两个模型,分两次跑。上图中vgg的float32的bs=16时,性能突然降低,或许就是因为资源问题。 线程管理 onnxruntime提供线程管理功能,可以通过调整不同的参数来控制模型的运行方式和性能。该功能的主要特点包括: 控制线程数量:使用sess_options.intra_op_num_threads参数可以控制模型运行时所使用的线程数 顺序执行或并行执行:使用sess_options.execution_mode参数可以控制运算符在图中是顺序执行还是并行执行。当模型具有较多的分支时,将该参数设置为ORT_PARALLEL可以提供更好的性能。 控制并行执行的线程数量:在sess_options.execution_mode = rt.ExecutionMode.ORT_PARALLEL的情况下,可以使用sess_options.inter_op_num_threads参数来控制在不同节点上并行执行图时使用的线程数。 具体使用参见配套代码 和官方文档。 由于配套代码中运行的结果并没有得到提升,这里猜测与硬件设备有关,因此就不讨论线程参数设置的问题了。 I/O binding IO Binding 用于在运行计算图之前将输入和输出张量绑定到设备上,以提高运行效率。 IO Binding 可以避免在运行计算图时将输入和输出数据从 CPU 复制到设备上,从而减少数据复制操作所需的时间。 具体操作可参考:配套代码和io bind 同样地,配套代码中的案例没有得到效率提升,也无法进一步探讨了,这里可以作为参考代码。 当输入和输出张量比较大时,使用 IO Binding 功能可以显著提高计算图的执行效率,因此在后续的任务中尝试使用 IO binding。 运行耗时分析工具 onnxruntime还提供了一个运行耗时分析工具,在sess_options中设置sess_options.enable_profiling = True,就可以在当前目录输出一份json文件,根据json文件中详细记录了运行时间和性能数据。每个事件条目包括以下信息: cat:事件的分类,可以是Session(会话)或Node(节点); pid:进程ID; tid:线程ID; dur:事件持续时间,以微秒为单位; ts:事件的时间戳,以微秒为单位; ph:事件的类型,可以是X(完整事件)或B(事件开始)/E(事件结束); name:事件的名称; args:事件的参数,包括输入和输出张量的类型、形状和大小,以及线程池的名称、线程ID和运行时间。 通过配套代码,获得json文件,还可以通过网站:https://www.ui.perfetto.dev/, open trace file打开json进行可视化观察。 小结 本小节介绍了onnxruntime在性能优化上的一些技巧,包括float16量化、int8量化、混合精度量化、计算图优化、线程管理和IO binding。 但由于本机设备原因,并没有看到有多大的性能优化,大家也可以在自己设备上尝试一下运行效率的变化,按道理这些技巧是能提速的。 这些优化技巧是模型部署过程中常见的加速技巧,在其它框架中也会有实现,这些在TensorRT中会再详细展开。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/":{"url":"chapter-12/","title":"第十二章 TensorRT 使用","keywords":"","body":"第十二章 TensorRT 使用 第十二章 TensorRT 使用 12.1 TensorRT 简介与安装 12.2 TensorRT 工作流及cuda-python 12.3 trtexec 工具使用 12.4 TensorRT 实用工具 12.5 TensorRT API 使用 12.6 模型量化基础概念 12.7 PTQ 量化实践 12.8 QAT 量化实践 12.9 TensorRT Python 工程化 第十二章简介 本章介绍模型部署的实用工具——TensorRT。 TensorRT是Nvidia公司针对N卡推出的高性能深度学习推理框架,TRT采用c++编写底层库,并提供c++/python应用接口,实现了高吞吐、低时延的优点。TRT 应用量化、图优化、层融合等优化技术,同时利用高度优化的内核找到该模型的最快实现。 TRT涉及的内容非常多,本章将从基础概念与安装开始介绍,然后通过一个Resnet的推理来了解TRT的工作流程,接着介绍TRT实用工具集trtexec、Night system, polygraph,接着介绍基于API的模型搭建方法,最后介绍基于TRT的量化方法及理论原理,还包括PTQ和QAT的实现,最后通过YOLOv5的TRT推理部署来熟悉工程化时,python代码的编写。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.1-trt-install.html":{"url":"chapter-12/12.1-trt-install.html","title":"12.1 TensorRT 简介与安装","keywords":"","body":"12.1 TensorRT 简介与安装 前言 本节将介绍TensorRT的基础概念,以及在windows和linux下进行安装及验证的方法,为后续正式学习TensorRT做好理论和环境的基础。 github:https://github.com/NVIDIA/TensorRT 主页:https://developer.nvidia.com/tensorrt python接口文档:https://docs.nvidia.com/deeplearning/tensorrt/api/python_api/ 安装文档: https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html 开发者文档: https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html 各版本trt介绍:https://docs.nvidia.com/deeplearning/tensorrt/archives/index.html TRT简介 TRT是Nvidia公司针对N卡推出的高性能深度学习推理框架,TRT采用c++编写底层库,并提供c++/python应用接口,实现了高吞吐、低时延的优点。TRT 应用量化、图优化、层融合等优化技术,同时利用高度优化的内核找到该模型的最快实现。 TRT是官方推理引擎,优化效果自然靠谱,因此使用TRT进行工程化部署已经成为主流方案。 下面介绍TRT工作原理,它与pytorch这样的训练框架之间存在什么差异?为什么它是弥补pytorch走上高性能工程化必不可少的工具?下面通过一幅图来说明。 TRT主要包含优化器和运行时库两部分: optimizer是进行模型表示的优化,例如,层融合、图优化、量化、kernel选择等操作,目的是为了找到模型在GPU上,运行时最优的表示形式以及方案(kernel选择),这里的kernel是具体某个算子的实现方法,例如卷积的实现就有很多,可以根据实现方式、算法、数据布局等进行挑选,优化器会根据网络结构、硬件配置和精度需求等因素,选择最适合当前场景的 kernel 进行推理计算,以获得最佳的性能。optimizer是在生产前做准备工作,获得最优方案,供runtime使用。 runtime是运行时库,可以实现推理引擎创建、推理计算、引擎销毁等内容,是生产服务中使用的。到这里,就知道TRT会根据GPU特性、任务特性、用户自定义操作等,将pytorch模型转换为TRT 运行时库能读取的形式,简单粗暴理解就是pytorch模型转为TRT模型。 TRT模型文件(.plan或者.engine)是高度优化的模型表示形式,在真正使用时被Runtime所加载并使用,可以实现高性能推理。 由此可知,TRT可以高性能推理的重要环节是采用优化器优化,那么优化通常有哪些操作呢? 可以通过上图看到会包括6个主要部分: Weight & Activation Precision Calibration:权重和激活精度校准,目的是通过量化模型(float16, int8等)实现高吞吐量,以及低存储。 Layer & Tensor Fusion:层和张量融合,这一步是优化 GPU 内存和带宽的使用,通过将多个节点融合成一个节点,从而减少计算和通信的开销。例如,卷积、BN、ReLU融合为一个操作算子,可以减少数据在 GPU 内存中的复制次数,从而提高推理性能。 Kernel Auto-Tuning,内核自动调优,的是根据目标 GPU 平台的特性和硬件配置,选择最佳的计算内核和数据布局方式,以获得最佳的性能和精度。TensorRT 会根据模型的结构和运行时环境,自动选择最佳的计算内核和数据布局方式,从而最大化推理性能。 从这里表明,优化时的GPU型号要与生产时的GPU型号保持一致,尤其采用.engine来存储TRT模型时,不同设备之间不能使用。 Dynamic Tensor Memory,动态张量内存,可以最小化模型的内存占用,通过动态地分配和释放张量内存,以最大程度地利用有限的 GPU 内存资源。 Multi-Stream Execution,多流执行是通过并行执行多个输入流,提高模型的推理性能。TensorRT 可以将多个输入流分配到不同的 CUDA 流中,并行执行多个输入流,从而提高模型的并发性和吞吐量。 CUDA流可以理解为 Time Fusion(时间融合) 这一步的主要目的是通过动态生成内核,优化循环神经网络(RNN)等模型在多个时间步骤中的计算过程。通过将多个时间步骤的计算合并成一个内核,可以减少数据在 GPU 内存中的复制次数,从而提高推理性能。 使用场景:需要优化循环神经网络等模型在多个时间步骤中的计算过程,适用于序列模型等有时间序列特征的深度学习模型。 PS:CUDA流的理解 CUDA流是CUDA编程中用于并行计算的一种抽象概念,是一组在GPU上并行执行的操作序列。CUDA流用于管理和调度GPU上的计算资源。每个CUDA流都有自己的计算资源,包括寄存器、共享内存、全局内存等,这些资源是独立的,不同流之间不会相互干扰。在执行CUDA程序时,GPU上的计算资源被分割成了若干个流,每个流可以独立地执行一组操作序列。CUDA流可以看作是一条命令流水线,其中的每个操作都是在GPU上并行执行的。 可以将CUDA流看作是一个GPU上的工厂生产线,流中的每个操作就像是生产线上的一个工人,每个工人都有自己的工作任务,按照一定的顺序进行工作。这样,GPU上的计算资源就可以同时处理多个任务,从而实现并行计算。 为什么叫流(stream),是可以将其理解为数据的流动。在CUDA编程中,数据可以从CPU到GPU,也可以从GPU到GPU,甚至可以在GPU内部进行数据流动。通过合理地利用CUDA流,可以将数据的流动和计算的流程进行有效地整合和优化,以获得更高的性能和效率。 Windows安装 接下来进行TRT的安装,将分windows和linux下,所有安装步骤参照官方文档进行:https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html 建议采用docker镜像安装使用,可避免很多不必要的坑,若不采用docker的话,建议严格按照官方文档步骤进行。 额外需要注意的是cuda、cudnn版本要与TRT版本匹配,同时主要环境变量的设置,中间可能会存在各种报错,请自行网上搜索解决方案。 TensorRT可以通过多种方法安装,例如Debian or RPM packages, a Python wheel file, a tar file, or a zip file. 在这里先介绍Windows的安装,采用zip文件的形式。 第一步:在官网下载zip文件,TensorRT-8.6.0.12.Windows10.x86_64.cuda-11.8.zip 第二步:把zip解压到CUDA同级目录(位置无所谓,建议与CUDA放在一起,例如C:\\Program Files\\NVIDIA GPU Computing Toolkit\\TensorRT-8.6.0.12),将TensorRT-8.6.0.12\\lib下文件复制到CUDA安装目录/bin下面(C:\\Program Files\\NVIDIA GPU Computing Toolkit\\CUDA\\v11.3\\bin) 第三步:安装python whl,在TensorRT-8.6.0.12\\python下找到对应版本的whl,主要是看python版本。然后安装以下三个库 python.exe -m pip install tensorrt-*-cp3x-none-win_amd64.whl python.exe -m pip install tensorrt_lean-*-cp3x-none-win_amd64.whl python.exe -m pip install tensorrt_dispatch-*-cp3x-none-win_amd64.whl 第四步:pytorch与TensorFlow需要额外安装3个python包 python3 -m pip install graphsurgeon\\graphsurgeon-0.4.6-py2.py3-none-any.whl python3 -m pip install uff\\uff-0.6.9-py2.py3-none-any.whl python3 -m pip install onnx_graphsurgeon\\onnx_graphsurgeon-0.3.12-py2.py3-none-any.whl 第五步:建议安装 cuda-python & pycuda 根据官方安装文档指引:https://docs.nvidia.com/deeplearning/tensorrt/install-guide/index.html 如果需要使用python-api,则需要安装cuda-python,安装方法:https://nvidia.github.io/cuda-python/install.html pip install pycuda pip install cuda-python 安装pycuda可能会遇到:提示缺少 c++构建工具 error: Microsoft Visual C++ 14.0 or greater is required. Get it with \"Microsoft C++ Build Tools\": https://visualstudio.microsoft.com/visual-cpp-build-tools/ 参考微软的回答:https://learn.microsoft.com/en-us/answers/questions/136595/error-microsoft-visual-c-14-0-or-greater-is-requir 下载一个vs_BuildTools.exe,然后安装生成工具即可。 Windows TRT验证 trtexec 运行模型 trtexec是tensorrt自带的命令行工具,可以进行TRT模型生成,以及推理耗时评估。 在windows下,首先需要将trtexec.exe所在文件夹添加到系统环境变量,这样在cmd中,trtexec指令才会被识别到。 接着打开cmd,运行以下命令,将onnx文件转换为tensorrt的engine文件,并且自动进行推理耗时评估。 其中,onnx文件可从chatper-11文件夹下获取。 trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 经过30s左右详细日志输出,最底部有推理性能日志: [06/13/2023-14:56:23] [I] === Performance summary === [06/13/2023-14:56:23] [I] Throughput: 503.749 qps [06/13/2023-14:56:23] [I] Latency: min = 1.89886 ms, max = 2.85559 ms, mean = 1.92952 ms, median = 1.92126 ms, percentile(90%) = 1.93677 ms, percentile(95%) = 1.95227 ms, percentile(99%) = 2.20648 ms [06/13/2023-14:56:23] [I] Enqueue Time: min = 0.288818 ms, max = 1.23926 ms, mean = 0.570478 ms, median = 0.669189 ms, percentile(90%) = 0.782898 ms, percentile(95%) = 0.83667 ms, percentile(99%) = 1.03223 ms [06/13/2023-14:56:23] [I] H2D Latency: min = 0.0751953 ms, max = 0.138062 ms, mean = 0.0788889 ms, median = 0.0770264 ms, percentile(90%) = 0.0814819 ms, percentile(95%) = 0.0932617 ms, percentile(99%) = 0.111938 ms [06/13/2023-14:56:23] [I] GPU Compute Time: min = 1.81738 ms, max = 2.7533 ms, mean = 1.8471 ms, median = 1.84009 ms, percentile(90%) = 1.85034 ms, percentile(95%) = 1.8606 ms, percentile(99%) = 2.11353 ms [06/13/2023-14:56:23] [I] D2H Latency: min = 0.00317383 ms, max = 0.0183716 ms, mean = 0.0035316 ms, median = 0.00341797 ms, percentile(90%) = 0.00390625 ms, percentile(95%) = 0.00390625 ms, percentile(99%) = 0.00415039 ms [06/13/2023-14:56:23] [I] Total Host Walltime: 3.00348 s [06/13/2023-14:56:23] [I] Total GPU Compute Time: 2.79466 s [06/13/2023-14:56:23] [W] * GPU compute time is unstable, with coefficient of variance = 3.29947%. [06/13/2023-14:56:23] [W] If not already in use, locking GPU clock frequency or adding --useSpinWait may improve the stability. [06/13/2023-14:56:23] [I] Explanations of the performance metrics are printed in the verbose logs. [06/13/2023-14:56:23] [I] &&&& PASSED TensorRT.trtexec [TensorRT v8600] # trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 可看到,基于TRT引擎的resnet50,batchsize=1的模型,在推理效率上,吞吐量为503,平均时延为1.92ms。 可以对比onnxruntime的吞吐量,提高了86%的吞吐(11.2小节做了评估,bs=1时,吞吐量为270),时延减少了48%(3.7 --> 1.92 ms) 为了进一步对比,还可以执行batchsize=128时的resnet50,观察吞吐量与时延情况,具体如下表所示: trtexec --onnx=resnet50_bs_128.onnx --saveEngine=resnet50_bs_128.engine [06/13/2023-15:05:08] [I] === Performance summary === [06/13/2023-15:05:08] [I] Throughput: 9.05117 qps [06/13/2023-15:05:08] [I] Latency: min = 109.778 ms, max = 111.595 ms, mean = 110.354 ms, median = 110.348 ms, percentile(90%) = 110.654 ms, percentile(95%) = 110.674 ms, percentile(99%) = 111.595 ms [06/13/2023-15:05:08] [I] Enqueue Time: min = 0.542603 ms, max = 1.39661 ms, mean = 0.860929 ms, median = 0.815994 ms, percentile(90%) = 1.23169 ms, percentile(95%) = 1.28577 ms, percentile(99%) = 1.39661 ms [06/13/2023-15:05:08] [I] H2D Latency: min = 9.49048 ms, max = 10.332 ms, mean = 9.63266 ms, median = 9.57568 ms, percentile(90%) = 9.7522 ms, percentile(95%) = 10.2017 ms, percentile(99%) = 10.332 ms [06/13/2023-15:05:08] [I] GPU Compute Time: min = 100.16 ms, max = 101.197 ms, mean = 100.652 ms, median = 100.593 ms, percentile(90%) = 101.011 ms, percentile(95%) = 101.036 ms, percentile(99%) = 101.197 ms [06/13/2023-15:05:08] [I] D2H Latency: min = 0.064209 ms, max = 0.114136 ms, mean = 0.0695243 ms, median = 0.0656738 ms, percentile(90%) = 0.083374 ms, percentile(95%) = 0.0861816 ms, percentile(99%) = 0.114136 ms [06/13/2023-15:05:08] [I] Total Host Walltime: 3.204 s [06/13/2023-15:05:08] [I] Total GPU Compute Time: 2.9189 s [06/13/2023-15:05:08] [I] Explanations of the performance metrics are printed in the verbose logs. [06/13/2023-15:05:08] [I] &&&& PASSED TensorRT.trtexec [TensorRT v8600] # trtexec --onnx=resnet50_bs_128.onnx --saveEngine=resnet50_bs_128.engine onnx和TRT的吞吐量、时延对比如下表所示,可以看到吞吐量有30-80%的提升,时延有20-50%左右的降低。 吞吐量 吞吐量 时延 时延 onnxruntime trt onnxruntime trt bs = 1 270 503(↑86%) 3.7 1.9(↓49%) bs = 128 852 1158(↑36%) 150.3 110(↓27%) 日志信息中有很多意义内容,包括: Throughput:模型的推理吞吐量,以每秒推理数量(QPS)为单位。 Latency:模型一次推理的延迟时间统计信息,包括最小值、最大值、平均值、中位数和百分位数(90%、95%和99%)。 Enqueue Time:将数据传输到GPU的时间统计信息, H2D Latency:将主机数据传输到GPU的延迟时间统计信息, GPU Compute Time:模型在GPU上运行的计算时间统计信息 D2H Latency:从GPU将数据传输回主机的延迟时间统计信息 Total Host Walltime:模型推理的总时间,包括传输数据、计算和传输数据回主机的时间。 Total GPU Compute Time:模型在GPU上的总计算时间。 Bug记录 安装TRT时,切记关注自己的cuda版本,要找到适配你cuda版本的TRT版本! 由于刚更换电脑,cuda安装了最新的v12.4, 一开始想安装TRT v8.6.1,发现报错: [03/27/2024-22:34:24] [I] Loading standard plugins [03/27/2024-22:34:24] [E] Uncaught exception detected: Unable to open library: nvinfer_plugin.dll &&&& FAILED TensorRT.trtexec [TensorRT v8601] # trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine --tacticSources=-CUDNN 根本原因是 cuda-v12.4需要更高的TRT版本,最终安装的是TensorRT-10.0.0.6。 Linux 安装 linux的安装有多种途径,如果有docker环境,强烈建议使用docker拉取官方镜像,直接可以使用。 如果不用docker,可以采用deb(ubuntu)、tar(linux都可以)、rpm(Redhat/Centos),笔者在centos和ubuntu上尝试过以上三种方法,由于服务器环境不纯净,导致各种问题装上。因此建议大家有docker就用docker,没有docker的话可完全参照官方文档的步骤就可以了。 安装前准备-系统版本查看 Linux系统下,注意系统的发行版本是什么,Linux、Ubuntu、Centos/RedHat有着不同的安装包,这里需要一开始查看自己系统版本。 cat /etc/*-release 同时查看cuda版本以及cudnn版本 nvcc -V # cudnn 8.0以前版本查看 cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 2 # cudnn 8.0以后版本查看 cat /usr/local/cuda/include/cudnn_version.h | grep CUDNN_MAJOR -A 2 这里安装的是ubuntu18.04, cuda11.6, cudnn8.9.2; 通过docker安装 https://catalog.ngc.nvidia.com/orgs/nvidia/containers/tensorrt https://docs.nvidia.com/deeplearning/tensorrt/container-release-notes/index.html https://catalog.ngc.nvidia.com/orgs/nvidia/containers/pytorch/tags(pytorch + trt的容器) 有了docker可以直接拉取镜像,在环境中使用对应的TRT 第一步:拉取镜像 docker pull nvcr.io/nvidia/tensorrt:23.04-py3 TRT的版本可以通过文档查找 23.04-py3表示2023年4月发布的镜像,该镜像环境是: Ubuntu 20.04 Python3.8 NVIDIA CUDA® 12.1.0 NVIDIA cuBLAS 12.1.3 NVIDIA cuDNN 8.9.0 NVIDIA NCCL 2.17.1 第二步:启动镜像 docker run -it -d --name trt --gpus all -v /home/docker_volume:/mnt nvcr.io/nvidia/tensorrt:23.04-py3 /bin/bash 第三步:进入容器,查看trt版本 docker exec -it trt /bin/bash dpkg -l | grep TensorRT 得到如下显示,表明安装成功,可以尝试使用trtexec和python进行TRT模型推理。 root@c3d8bfb1d917:/opt/tensorrt/bin# dpkg -l | grep TensorRT ii libnvinfer-bin 8.5.1-1+cuda11.8 amd64 TensorRT binaries ii libnvinfer-dev 8.5.1-1+cuda11.8 amd64 TensorRT development libraries and headers ii libnvinfer-plugin-dev 8.5.1-1+cuda11.8 amd64 TensorRT plugin libraries and headers ii libnvinfer-plugin8 8.5.1-1+cuda11.8 amd64 TensorRT plugin library ii libnvinfer8 8.5.1-1+cuda11.8 amd64 TensorRT runtime libraries ii libnvonnxparsers-dev 8.5.1-1+cuda11.8 amd64 TensorRT ONNX libraries ii libnvonnxparsers8 8.5.1-1+cuda11.8 amd64 TensorRT ONNX libraries ii libnvparsers-dev 8.5.1-1+cuda11.8 amd64 TensorRT parsers libraries ii libnvparsers8 8.5.1-1+cuda11.8 amd64 TensorRT parsers libraries ii tensorrt-dev 8.5.1.7-1+cuda11.8 amd64 Meta package for TensorRT development libraries 第四步:验证TRT 基于trtexec 运行模型。首先,编译trtexec: cd /workspace/tensorrt/samples/trtexec make 然后,添加环境变量: export PATH=$PATH:/path/to/trtexec export PATH=$PATH:/workspace/tensorrt/samples/trtexec 最后,将resnet50_bs_1.onnx文件放到服务器,执行命令: trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 最后得到如下输出: [06/14/2023-07:18:37] [I] === Performance summary === [06/14/2023-07:18:37] [I] Throughput: 443.756 qps [06/14/2023-07:18:37] [I] Latency: min = 2.27521 ms, max = 2.51807 ms, mean = 2.30483 ms, median = 2.3042 ms, percentile(90%) = 2.32056 ms, percentile(95%) = 2.32422 ms, percentile(99%) = 2.33069 ms [06/14/2023-07:18:37] [I] Enqueue Time: min = 0.792969 ms, max = 1.42358 ms, mean = 0.966213 ms, median = 0.89917 ms, percentile(90%) = 1.28101 ms, percentile(95%) = 1.29688 ms, percentile(99%) = 1.34668 ms [06/14/2023-07:18:37] [I] H2D Latency: min = 0.0510864 ms, max = 0.09021 ms, mean = 0.0603899 ms, median = 0.0601807 ms, percentile(90%) = 0.0654297 ms, percentile(95%) = 0.06604 ms, percentile(99%) = 0.0751953 ms [06/14/2023-07:18:37] [I] GPU Compute Time: min = 2.22003 ms, max = 2.45557 ms, mean = 2.24015 ms, median = 2.24048 ms, percentile(90%) = 2.25073 ms, percentile(95%) = 2.25391 ms, percentile(99%) = 2.26099 ms [06/14/2023-07:18:37] [I] D2H Latency: min = 0.00244141 ms, max = 0.0136719 ms, mean = 0.00428888 ms, median = 0.00305176 ms, percentile(90%) = 0.0114746 ms, percentile(95%) = 0.0126953 ms, percentile(99%) = 0.0133057 ms [06/14/2023-07:18:37] [I] Total Host Walltime: 3.00616 s [06/14/2023-07:18:37] [I] Total GPU Compute Time: 2.98836 s [06/14/2023-07:18:37] [I] Explanations of the performance metrics are printed in the verbose logs. [06/14/2023-07:18:37] [I] &&&& PASSED TensorRT.trtexec [TensorRT v8601] # trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 通过Deb包安装 Deb参考官网吧,这里安装最后失败了,所以推荐大家用docker安装,下面是作为记录。 第一步:在官网下载deb包。并安装 sudo dpkg -i nv-tensorrt-local-repo-${os}-${tag}_1.0-1_amd64.deb sudo cp /var/nv-tensorrt-local-repo-${os}-${tag}/*-keyring.gpg /usr/share/keyrings/ sudo apt-get update 第二步:安装 tensorrt。 需要等待3-5分钟。 sudo apt-get install tensorrt 第三步:安装其它工具 sudo apt-get install python3-libnvinfer-lean sudo apt-get install python3-libnvinfer-dispatch python3 -m pip install numpy sudo apt-get install python3-libnvinfer-dev python3 -m pip install numpy onnx sudo apt-get install onnx-graphsurgeon 第四步:验证安装 安装好后,可到/usr/src/tensorrt 查看安装目录。 dpkg-query -W tensorrt 第五步:编译trtexec cd /usr/src/tensorrt/samples/trtexec make 根据官方文档只需要make就可以获得trtexec,但是我的服务器的配置出了些问题,make时出现如下报错: /usr/local/cuda/include/cuda_runtime_api.h:4219:65: error: 'cudaLaunchConfig_t' does not name a type; did you mean 'cudaFunction_t'? extern __host__ cudaError_t CUDARTAPI cudaLaunchKernelExC(const cudaLaunchConfig_t *config, const void *func, void **args); 多种尝试未解决,后续将采用docker镜像来使用TRT,这里暂时放弃。 小结 本小节介绍了TensorRT的基础概念,知道了TRT中需要做优化和推理两个主要内容,其中的优化可以在API中或者是trtexec中进行,推理则提供了python和c++两种接口。在安装上,介绍了windows下和linux下的安装及验证,可以看到在吞吐和时延上,TRT比ONNX都有优势。 下一小节将详细介绍TRT的工作流程。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.2-trt-workflow.html":{"url":"chapter-12/12.2-trt-workflow.html","title":"12.2 TensorRT 工作流及cuda-python","keywords":"","body":"12.2 TensorRT 工作流及cuda-python 前言 本节将在python中采用TensorRT进行resnet50模型推理,通过一个案例了解TensorRT的工作步骤流程,为后续各模块深入研究打下基础。 本节核心内容包括TensorRT中各模块概念,python中进行推理步骤,python中的cuda库使用。 本节用到的resnet50_bs_1.engine文件,需要提前通过trtexec来生成(resnet50_bs_1.onnx通过11章内容生成,或者从网盘下载-提取码:24uq trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine workflow基础概念 在python中使用TensorRT进行推理,需要了解cuda编程的概念,在这里会出现一些新名词,这里进行了总结,帮助大家理解python中使用TRT的代码。 借鉴nvidia官方教程中的一幅图来说明TRT从构建模型到使用模型推理,需要涉及的概念。 Logger:用于在TensorRT日志中记录消息,可以来实现自定义日志记录器,以便更好地了解TensorRT运行时发生的事情。 Builder:用于构建一个优化的TensorRT引擎,可以使用Builder来定义和优化网络 BuilderConfig:用于配置Builder的行为,例如最大批处理大小、优化级别等 Network:用于描述一个计算图,它由一组层组成,用来定义和构建深度学习模型,通常有三种方式获取network,包括TensorRT API、parser、训练框架中trt工具。 SerializeNetwork:用于将网络序列化为二进制格式,可以将网络模型保存到磁盘上的.plan文件中,以便稍后加载和使用。 .plan:.plan是TensorRT引擎的序列化文件格式,有的地方用.engine,.plan文件和.engine都是序列化的TensorRT引擎文件,但是它们有一些区别。 .plan文件是通用序列化文件格式,它包含了优化后的网络和权重参数。而.engine文件是Nvidia TensorRT引擎的专用二进制格式,它包含了优化后的网络,权重参数和硬件相关信息。 可 移植性方面,由于.plan文件是通用格式,所以可以在不同的硬件平台上使用。而.engine文件是特定于硬件的,需要在具有相同GPU架构的系统上使用。 加载速度上:.plan文件的加载速度通常比.engine文件快,因为它不包含硬件相关信息,而.engine文件必须在运行时进行硬件特定的编译和优化。 Engine:Engine是TensorRT中的主要对象,它包含了优化后的网络,可以用于进行推理。可以使用Engine类加载.plan文件,创建Engine对象,并使用它来进行推理。 Context:Context是Engine的一个实例,它提供了对引擎计算的访问。可以使用Context来执行推理,并在执行过程中管理输入和输出缓冲区。 Buffer:用于管理内存缓冲区。可以使用Buffer类来分配和释放内存,并将其用作输入和输出缓冲区。 Execute:Execute是Context类的一个方法,用于执行推理。您可以使用Execute方法来执行推理,并通过传递输入和输出缓冲区来管理数据流。 在这里面需要重点了解的是Network的构建有三种方式,包括TensorRT API、parser、训练框架中trt工具。 TensorRT API是手写网络结构,一层一层的搭建 parser 是采用解析器对常见的模型进行解析、转换,常用的有ONNX Parser。本小节那里中采用parser进行onnx模型解析,实现trt模型创建。 直接采用pytorch/tensorflow框架中的trt工具导出模型 此处先采用parser形式获取trt模型,后续章节再讲解API形式构建trt模型。 TensorRT resnet50推理 到这里环境有了,基础概念了解了,下面通过python代码实现resnet50的推理,通过本案例代码梳理在代码层面的workflow。 pycuda库与cuda库 正式看代码前,有必要简单介绍pycuda库与cuda库的差别。在早期版本中,代码多以pycuda进行buffer的管理,在python3.7后,官方推荐采用cuda库进行buffer的管理。 cuda库是NVIDIA提供的用于CUDA GPU编程的python接口,包含在cuda toolkit中。主要作用是: 直接调用cuda runtime API,如内存管理、执行kernel等。 pycuda库是一个第三方库,提供了另一个python绑定到CUDA runtime API。主要作用是: 封装cuda runtime API到python调用,管理GPU Context、Stream等。 cuda库更基础,pycuda库更全面。前者集成在CUDA toolkit中,后者更灵活。 但在最新的trt版本(v8.6.1)中,官方推荐采用cuda库,两者使用上略有不同,在配套代码中会实现两种buffer管理的代码。 python workflow 正式跑代码前,了解一下代码层面的workflow: 初始化模型,获得context 创建logger:logger = trt.Logger(trt.Logger.WARNING) 创建engine:engine = runtime.deserialize_cuda_engine(ff.read()) 创建context: context = engine.create_execution_context() 内存申请: 申请host(cpu)内存:进行变数据量的赋值,即完成变量内存分配。 申请device(gpu)内存: 采用cudart函数,获得内存地址。 d_input = cudart.cudaMalloc(h_input.nbytes)[1] 告知context gpu地址:context.set_tensor_address(l_tensor_name[0], d_input) 推理 数据拷贝 host 2 device: cudart.cudaMemcpy(d_input, h_input.ctypes.data, h_input.nbytes, cudart.cudaMemcpyKind.cudaMemcpyHostToDevice) 推理: context.execute_async_v3(0) 数据拷贝 device 2 host: cudart.cudaMemcpy(h_output.ctypes.data, d_output, h_output.nbytes, cudart.cudaMemcpyKind.cudaMemcpyDeviceToHost) 内存释放 cudart.cudaFree(d_input) 取结果 在host的变量上即可拿到模型的输出结果。 这里采用配套代码实现ResNet图像分类,可以得到与上一章ONNX中一样的分类效果,并且吞吐量与trtexec中差别不大,大约在470 it/s。 100%|██████████| 3000/3000 [00:06 cuda库的buffer管理 采用cuda库进行buffer管理,可分3个部分,内存和显存的申请、数据拷贝、显存释放。 这里推荐官方教程以及官方教程代码 在教程配套代码的 model_infer()函数是对上述两份资料进行了结合,下面详细介绍cuda部分的代码含义。 def model_infer(context, engine, img_chw_array): n_io = engine.num_io_tensors # since TensorRT 8.5, the concept of Binding is replaced by I/O Tensor, all the APIs with \"binding\" in their name are deprecated l_tensor_name = [engine.get_tensor_name(ii) for ii in range(n_io)] # get a list of I/O tensor names of the engine, because all I/O tensor in Engine and Excution Context are indexed by name, not binding number like TensorRT 8.4 or before # 内存、显存的申请 h_input = np.ascontiguousarray(img_chw_array) h_output = np.empty(context.get_tensor_shape(l_tensor_name[1]), dtype=trt.nptype(engine.get_tensor_dtype(l_tensor_name[1]))) d_input = cudart.cudaMalloc(h_input.nbytes)[1] d_output = cudart.cudaMalloc(h_output.nbytes)[1] # 分配地址 context.set_tensor_address(l_tensor_name[0], d_input) # 'input' context.set_tensor_address(l_tensor_name[1], d_output) # 'output' # 数据拷贝 cudart.cudaMemcpy(d_input, h_input.ctypes.data, h_input.nbytes, cudart.cudaMemcpyKind.cudaMemcpyHostToDevice) # 推理 context.execute_async_v3(0) # do inference computation # 数据拷贝 cudart.cudaMemcpy(h_output.ctypes.data, d_output, h_output.nbytes, cudart.cudaMemcpyKind.cudaMemcpyDeviceToHost) # 释放显存 cudart.cudaFree(d_input) cudart.cudaFree(d_output) return h_output 第3行:获取模型输入、输出变量的数量,在本例中是2。 第4行:获取模型输入、输出变量的名字,存储在list中。本案例中是['input', 'output'],这两个名字是在onnx导入时候设定的。 第7/8行:申请host端的内存,可看出只需要进行两个numpy的赋值,即可开辟内存空间存储变量。 第9/10行:调用cudart进行显存空间申请,变量获取的是内存地址。例如”47348061184 “ 第13/14行:将显存地址告知context,context在推理的时候才能找到它们。 第17行:将内存中数据拷贝到显存中 第19行:context执行推理,此时运算结果已经到了显存 第21行:将显存中数据拷贝到内存中 第24/25行:释放显存中的变量。 pycuda库的buffer管理 注意:代码在v8.6.1上运行通过,在v10.0.0.6上未能正确使用context.execute_async_v3,因此请注意版本。 更多接口参考:https://docs.nvidia.com/deeplearning/tensorrt/api/python_api/infer/Core/ExecutionContext.html 在配套代码中pycuda进行buffer的申请及管理代码如下: h_input = cuda.pagelocked_empty(trt.volume(context.get_binding_shape(0)), dtype=np.float32) h_output = cuda.pagelocked_empty(trt.volume(context.get_binding_shape(1)), dtype=np.float32) d_input = cuda.mem_alloc(h_input.nbytes) d_output = cuda.mem_alloc(h_output.nbytes) def model_infer(context, h_input, h_output, d_input, d_output, stream, img_chw_array): # 图像数据迁到 input buffer np.copyto(h_input, img_chw_array.ravel()) # 数据迁移, H2D cuda.memcpy_htod_async(d_input, h_input, stream) # 推理 context.execute_async_v2(bindings=[int(d_input), int(d_output)], stream_handle=stream.handle) # 数据迁移,D2H cuda.memcpy_dtoh_async(h_output, d_output, stream) stream.synchronize() return h_output 第1,2行:通过cuda.pagelocked_empty创建一个数组,该数组位于GPU中的锁页内存,锁页内存(pinned Memory/ page locked memory)的概念可以到操作系统中了解,它是为了提高数据读取、传输速率,用空间换时间,在内存中开辟一块固定的区域进行独享。申请的大小及数据类型,通过trt.volume(context.get_binding_shape(0)和np.float32进行设置。其中trt.volume(context.get_binding_shape(0)是获取输入数据的元素个数,此案例,输入是[1, 3, 224, 224],因此得到的数是1x3x224x224 = 150528 第3,4行:通过cuda.mem_alloc函数在GPU上分配了一段内存,返回一个指向这段内存的指针 第8行:将图像数据迁移到input buffer中 第10行,将输入数据从CPU内存异步复制到GPU内存。其中,cuda.memcpy_htod_async函数异步将数据从CPU内存复制到GPU内存,d_input是GPU上的内存指针,h_input是CPU上的numpy数组,stream是CUDA流 第14行,同理,从GPU中把数据复制回到CPU端的h_output,模型最终使用h_output来表示模型预测结果 小结 本节介绍了TensorRT的工作流程,其中涉及10个主要模块,各模块的概念刚开始不好理解,可以先跳过,在后续实践中去理解。 随后基于onnx parser实现TensorRT模型的创建,engine文件的生成通过trtexec工具生成,并进行图片推理,获得了与trtexec中类似的吞吐量。 最后介绍了pycuda库和cuda库进行buffer管理的详细步骤,两者在代码效率上是一样的,吞吐量几乎一致。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.3-trt-trtexec.html":{"url":"chapter-12/12.3-trt-trtexec.html","title":"12.3 trtexec 工具使用","keywords":"","body":"12.3 trtexec 工具使用 本小节介绍trtexec工具的使用,trtexec可以实现onnx模型导出trt模型、耗时分析和模型优化分析等功能,本节将对trtexec的运用进行介绍。 trtexec trtexec是官方提供的命令行工具,主要用于一下三个方面 生成模型序列化文件:由ONNX文件生成 TensorRT 引擎并序列化为 Plan文件/engine文件 查看模型文件信息:查看 ONNX文件或 Plan 文件的网络逐层信息 模型性能测试:测试 TensorRT 引擎基于随机输入或给定输入下的性能 trtexec提供了大量参数,整体可分为构建和运行两个阶段。 构建阶段常用参数 --onnx=: onnx文件路径 --minShapes=, --optShapes=, and --maxShapes=: 当是onnx模型时,可指定batchsize的动态范围。 –-memPoolSize=: 优化过程可使用的最大内存 --saveEngine=: 保存的文件输出路径 --fp16, --int8, --noTF32, and --best: 指定数据精度 --verbose: 是否需要打印详细信息。默认是不打印详细信息。 --skipInference: 创建并保存引擎文件,不执行推理过程。 --timingCacheFile=: 记录每个tensor的最小最大值、运行时间等,可以用来分析量化效果。 --dumpLayerInfo, --exportLayerInfo=: 打印及保存每一层详细信息 更多高级用法,参考官方文档:https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#trtexec 运行阶段常用参数 --loadEngine=: 要加载的模型文件 --shapes=:指定输入张量的形状 --loadInputs=: Load input values from files. Default is to generate random inputs. --warmUp=, 热身阶段最短运行时间,单位ms --duration=, 测试阶段最短运行时间,单位s --iterations=: 测试阶段最小迭代次数 --useCudaGraph: 采用 CUDA graph 捕获和执行推理过程 --noDataTransfers: 关闭host与device之间的数据传输 --dumpProfile, --exportProfile=: 打印及保存每一层性能信息 --dumpLayerInfo, --exportLayerInfo=: 打印及保存每一层详细信息 更多高级用法,参考官方文档:https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#trtexec 案例0:固定batchsize 输出固定batchsize的engine文件,这里需要注意,batchsize的状态需要与ONNX匹配,因此在生成onnx时需要设置好。 trtexec --onnx=resnet50_bs_1.onnx --saveEngine=resnet50_bs_1.engine 案例1: 动态batchsize使用 resnet50_bs_dynamic.onnx 可通过第十一章章生成,或通过百度网盘下载-提取码:whai trtexec --onnx=resnet50_bs_dynamic.onnx --saveEngine=resnet50_bs_dynamic_1-32-64.engine --timingCacheFile=dynamic-1-32-64.cache --minShapes=input:1x3x224x224 --maxShapes=input:64x3x224x224 --optShapes=input:16x3x224x224 通过下表可知,fp32时,大batchsize带来吞吐量增加不明显,因此可考虑时延的平衡,选择batchsize=8。 FP32 1-8-64 1-16-64 1-32-64 1-64-64 吞吐量( FPS) 952 1060 1126 1139 时延(ms) 8.3 14.9 28.2 112 案例2:fp32、fp16、int8 性能比较 运行配套代码中的run.bat/run.sh,可以查看log,观察吞吐量、时延的变化。 如下图所示,吞吐量方面 fp16相较于fp32有约2~3倍提升,int8相较于fp16约2倍提升 相同精度时,吞吐量随batchsize增加,但在32后增速不明显。int8随着batchsize增速潜力更大。 时延方面 时延随着batchsize是线性增长 fp32, fp16, int8的时延依次递减一半 案例3:查看层详细信息 通过参数--dumpLayerInfo --exportLayerInfo,可以输出各层详细信息,以及融合情况,还有输入输出张量的名字(Bindings) trtexec --onnx=resnet50_bs_dynamic.onnx --saveEngine=demo.engine --skipInference --dumpLayerInfo --exportLayerInfo=\"exportLayerInfo.log\" 在exportLayerInfo.log文件中可以看到如下信息,主要包括 各网络层内容,以及融合情况“Reformatting CopyNode for Input Tensor 0 to Conv_0 + Relu_1” Reformatting CopyNode 表示 TensorRT 将输入tensor 0 复制(Copy)到 Conv_0 和 Relu_1 两个层进行了融合(Reformatting)。这里的 Reformatting 指的是 TensorRT 在优化网络结构时,会将一些层进行融合,以减少内存拷贝和提高计算效率。CopyNode 则表示插入了一个拷贝层,用于将输入数据复制到融合后新的层中。 这种层的融合可以减少内存访问,优化数据流,从而提升推理性能。 Bindings:包括输入输出张量的名称,这个在onnx导出时设定的,在下游python推理代码中也会用到。 {\"Layers\": [\"Reformatting CopyNode for Input Tensor 0 to Conv_0 + Relu_1\" ,\"Conv_0 + Relu_1\" ,\"MaxPool_2\" ,\"Conv_3 + Relu_4\" ,\"Conv_5 + Relu_6\" ... ,\"Reformatting CopyNode for Input Tensor 0 to Gemm_121\" ,\"Gemm_121\" ,\"reshape_after_Gemm_121\" ], \"Bindings\": [\"input\" ,\"output\" ]} 案例4:verbose中的日志内容 打开verbose开关后,trtexec将输出详细内容,包括以下六大模块: 导入模型情况:模型格式、名称 参数配置情况:设置了哪些参数进行优化,例如 --fp16等 设备情况:当前GPU device具体信息 计算图优化细节:详细描述网络层融合情况,计算图优化结果 网络层实现方式选择(几千行):打印每个网络层选择的kernel的过程,挑选耗时最低的方法 耗时统计:统计推理耗时时间,包括数据拷贝、推理等耗时的统计值 trtexec --onnx=resnet50_bs_dynamic.onnx --saveEngine=demo.engine --verbose > verbose.log 执行以下命令,可获得日志文件,下面对主要内容进行介绍。 Model Options :包含导入的模型内容 [08/20/2023-11:59:45] [I] === Model Options === [08/20/2023-11:59:45] [I] Format: ONNX [08/20/2023-11:59:45] [I] Model: resnet50_bs_dynamic.onnx [08/20/2023-11:59:45] [I] Output: Build Options:创建trt模型的参数设置 [08/20/2023-11:59:45] [I] === Build Options === [08/20/2023-11:59:45] [I] Max batch: explicit batch [08/20/2023-11:59:45] [I] Memory Pools: workspace: default, dlaSRAM: default, dlaLocalDRAM: default, dlaGlobalDRAM: default [08/20/2023-11:59:45] [I] minTiming: 1 [08/20/2023-11:59:45] [I] avgTiming: 8 [08/20/2023-11:59:45] [I] Precision: FP32 ... 推理设置 [08/20/2023-11:59:45] [I] === Inference Options=== [08/20/2023-11:59:45] [I] Batch: Explicit [08/20/2023-11:59:45] [I] Input inference shapes: model [08/20/2023-11:59:45] [I] Iterations: 10 [08/20/2023-11:59:45] [I] Duration: 3s (+ 200ms warm up) [08/20/2023-11:59:45] [I] Sleep time: 0ms [08/20/2023-11:59:45] [I] Idle time: 0ms [08/20/2023-11:59:45] [I] Inference Streams: 1 日志输出设置 [08/20/2023-11:59:45] [I] === Reporting Options === [08/20/2023-11:59:45] [I] Verbose: Enabled [08/20/2023-11:59:45] [I] Averages: 10 inferences [08/20/2023-11:59:45] [I] Percentiles: 90,95,99 [08/20/2023-11:59:45] [I] Dump refittable layers:Disabled [08/20/2023-11:59:45] [I] Dump output: Disabled [08/20/2023-11:59:45] [I] Profile: Disabled [08/20/2023-11:59:45] [I] Export timing to JSON file: [08/20/2023-11:59:45] [I] Export output to JSON file: [08/20/2023-11:59:45] [I] Export profile to JSON file: 设备信息 [08/20/2023-11:59:46] [I] === Device Information === [08/20/2023-11:59:46] [I] Selected Device: NVIDIA GeForce RTX 3060 Laptop GPU [08/20/2023-11:59:46] [I] Compute Capability: 8.6 [08/20/2023-11:59:46] [I] SMs: 30 [08/20/2023-11:59:46] [I] Device Global Memory: 6143 MiB [08/20/2023-11:59:46] [I] Shared Memory per SM: 100 KiB [08/20/2023-11:59:46] [I] Memory Bus Width: 192 bits (ECC disabled) [08/20/2023-11:59:46] [I] Application Compute Clock Rate: 1.702 GHz [08/20/2023-11:59:46] [I] Application Memory Clock Rate: 7.001 GHz [03/28/2024-15:01:18] [I] === Device Information === [03/28/2024-15:01:20] [I] Available Devices: [03/28/2024-15:01:20] [I] Device 0: \"NVIDIA GeForce RTX 4060 Laptop GPU [03/28/2024-15:01:20] [I] Selected Device: NVIDIA GeForce RTX 4060 Laptop GPU [03/28/2024-15:01:20] [I] Selected Device ID: 0 [03/28/2024-15:01:20] [I] Compute Capability: 8.9 [03/28/2024-15:01:20] [I] SMs: 24 [03/28/2024-15:01:20] [I] Device Global Memory: 8187 MiB [03/28/2024-15:01:20] [I] Shared Memory per SM: 100 KiB [03/28/2024-15:01:20] [I] Memory Bus Width: 128 bits (ECC disabled) [03/28/2024-15:01:20] [I] Application Compute Clock Rate: 1.89 GHz [03/28/2024-15:01:20] [I] Application Memory Clock Rate: 8.001 GHz 补充一个4060的显卡信息,可以看到SMs是少于3060的,这个与基本厂商的刀法有关。虽然是4060的设备,但是计算性能比不上3060设备。因为里边的核心——SMs是少于3060的30个SM的。“SMs” 代表 “Streaming Multiprocessors”(流处理器),流处理器是执行 CUDA 核心的基本单元,SM越大算力越大。 对于RTX 4060 Laptop,官方显示有3072个CUDA核心,对应24个SM,即一个SM有128个CUDA核心。 对于RTX 3060 Laptop,官方显示有3840个CUDA核心,对应30个SM,也是符合一个SM有128个CUDA核心的。 4060不仅流处理器少,带宽也低,128 bits VS 192 bits,唯一的优点就是8GB VS 6GB了。 ONNX模型加载及创建 解析模型耗时0.14秒,总共126层,后续trt会针对该模型进行优化。 [08/20/2023-11:59:52] [I] [TRT] ---------------------------------------------------------------- [08/20/2023-11:59:52] [I] [TRT] Input filename: resnet50_bs_dynamic.onnx [08/20/2023-11:59:52] [I] [TRT] ONNX IR version: 0.0.7 [08/20/2023-11:59:52] [I] [TRT] Opset version: 13 [08/20/2023-11:59:52] [I] [TRT] Producer name: pytorch [08/20/2023-11:59:52] [I] [TRT] Producer version: 1.12.0 [08/20/2023-11:59:52] [I] [TRT] Domain: [08/20/2023-11:59:52] [I] [TRT] Model version: 0 [08/20/2023-11:59:52] [I] [TRT] Doc string: [08/20/2023-11:59:52] [I] [TRT] ---------------------------------------------------------------- [08/20/2023-11:59:52] [V] [TRT] Plugin creator already registered - ::BatchedNMSDynamic_TRT version 1 [08/20/2023-11:59:52] [V] [TRT] Plugin creator already registered - ::BatchedNMS_TRT version 1 [08/20/2023-11:59:52] [V] [TRT] Plugin creator already registered - ::BatchTilePlugin_TRT version 1 ... [08/20/2023-11:59:52] [V] [TRT] Adding network input: input with dtype: float32, dimensions: (-1, 3, 224, 224) [08/20/2023-11:59:52] [V] [TRT] Registering tensor: input for ONNX tensor: input [08/20/2023-11:59:52] [V] [TRT] Importing initializer: fc.weight [08/20/2023-11:59:52] [V] [TRT] Importing initializer: fc.bias [08/20/2023-11:59:52] [V] [TRT] Importing initializer: onnx::Conv_497 [08/20/2023-11:59:52] [V] [TRT] Importing initializer: onnx::Conv_498 [08/20/2023-11:59:52] [V] [TRT] Importing initializer: onnx::Conv_500 ... [08/20/2023-11:59:52] [V] [TRT] Searching for input: onnx::Conv_497 [08/20/2023-11:59:52] [V] [TRT] Searching for input: onnx::Conv_498 [08/20/2023-11:59:52] [V] [TRT] Conv_0 [Conv] inputs: [input -> (-1, 3, 224, 224)[FLOAT]], [onnx::Conv_497 -> (64, 3, 7, 7)[FLOAT]], [onnx::Conv_498 -> (64)[FLOAT]], [08/20/2023-11:59:52] [V] [TRT] Convolution input dimensions: (-1, 3, 224, 224) [08/20/2023-11:59:52] [V] [TRT] Registering layer: Conv_0 for ONNX node: Conv_0 ... [08/20/2023-11:59:52] [V] [TRT] Marking output_1 as output: output [08/20/2023-11:59:52] [I] Finished parsing network model. Parse time: 0.141545 [08/20/2023-11:59:52] [V] [TRT] After dead-layer removal: 126 layers [08/20/2023-11:59:52] [V] [TRT] Graph construction completed in 0.0015515 seconds. 计算图优化 优化计算图,可以使得推理速度更快,在本案例中,将模型从126层优化到57层 [08/20/2023-11:59:52] [I] [TRT] Graph optimization time: 0.0150853 seconds. 计算图优化中采用了大量的层融合,融合的原理是尽可能地合并不同层之间相关的计算,避免不必要的中间tensor生成, 减少内存读写, 降低计算消耗,最终提高推理效率 常见的优化方法如下: ConstShuffleFusion: 在fc层的bias中使用,可以将常量shuffle到bias数据中,减少冗余计算。 ShuffleShuffleFusion: 在flatten层中使用,可以减少shuffle的计算次数。 ConvReshapeBiasAddFusion: 将conv层的输出reshape,然后进行bias add的计算融合到一起,减少运算耗时。 ConvReluFusion: 将conv层和后续的Relu激活函数层融合,可以减少一次Relu的计算。 ConvEltwiseSumFusion: 将conv层和element-wise add层融合,避免重复计算。 ReduceToPoolingFusion: 将reduce层修改为pooling层,减少运算消耗。 ConcatReluFusion: 将concat层和relu层融合,减少relu计算次数。 BiasSoftmaxFusion: 融合bias层和softmax层,减少冗余计算。 [08/20/2023-11:59:52] [V] [TRT] Running: ConstShuffleFusion on fc.bias [08/20/2023-11:59:52] [V] [TRT] ConstShuffleFusion: Fusing fc.bias with (Unnamed Layer* 129) [Shuffle] [08/20/2023-11:59:52] [V] [TRT] After Myelin optimization: 125 layers ... [08/20/2023-11:59:52] [V] [TRT] After dupe layer removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After final dead-layer removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After tensor merging: 57 layers [08/20/2023-11:59:52] [V] [TRT] After vertical fusions: 57 layers [08/20/2023-11:59:52] [V] [TRT] After dupe layer removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After final dead-layer removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After tensor merging: 57 layers [08/20/2023-11:59:52] [V] [TRT] After slice removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] After concat removal: 57 layers [08/20/2023-11:59:52] [V] [TRT] Trying to split Reshape and strided tensor [08/20/2023-11:59:52] [I] [TRT] Graph optimization time: 0.0150853 seconds. 各网络层实现方式选择 网络层具体的实现有多种方式,例如不同的底层库、不同的实现算法、不同的算法策略,在TensorRT中会把所有的实现方式跑一遍,挑选速度最优的实现方式。 在实现网络层的过程中,runner和tactic是TensorRT中用于实现layer的关键组件。 runner代表着一种实现layer的算法或代码路径。例如,卷积层可以通过cudnn、cublas或者TensorRT自身的cask实现。runner封装了具体的实现算法。 tactic代表具体的实现方案。每个runner下面可以有多个tactic,对应不同的优化方法。例如cask convolution runner下面可以有基于tensor core的tactic,或是各种tile size的tactic等等。tactic包含了针对特定layer进行各种优化的代码实现。 所以TensorRT通过组合不同的runner和tactic,就可以得到层的多种实现方式。然后通过Auto Tuner来测试不同组合的性能,选择出最优的实现。 例如,对于一个卷积层: runner可以是cudnn、cublas、cask convolution等 cask convolution下面可以有基于tensor core的tactic,tile size为32x32或64x64的tactic等等 最终会选择出cask convolution + 64x64 tile size这个tactic组合作为最优实现 在本日志中,第一个runner跑的是conv_0 + Relu_1,最终选择的Tactic Name是 0x9cb304e2edbc1221,耗时0.040秒。 [08/20/2023-11:59:52] [V] [TRT] =============== Computing costs for [08/20/2023-11:59:52] [V] [TRT] *************** Autotuning format combination: Float(150528,50176,224,1) -> Float(802816,12544,112,1) *************** [08/20/2023-11:59:52] [V] [TRT] --------------- Timing Runner: Conv_0 + Relu_1 (CaskConvolution[0x80000009]) [08/20/2023-11:59:52] [V] [TRT] Tactic Name: sm50_xmma_conv_fprop_fused_conv_act_fp32_NCHW_fp32_NCHW_KCRS_fp32_fp32_fp32_Accfloat_1_1_cC1_dC1_srcVec1_fltVec1_1_TP3_TQ4_C1_R7_S7_U2_V2 Tactic: 0x0a617a3531b5b6dc Time: 0.102619 [08/20/2023-11:59:52] [V] [TRT] Tactic Name: sm50_xmma_conv_fprop_fused_conv_act_fp32_NCHW_fp32_NCHW_KCRS_fp32_fp32_fp32_Accfloat_1_1_cC1_dC1_srcVec1_fltVec2_2_TP3_TQ4_C1_R7_S7_U2_V2 Tactic: 0x520e893be7313ed2 Time: 0.0999131 ... [08/20/2023-11:59:52] [V] [TRT] Conv_0 + Relu_1 (CaskConvolution[0x80000009]) profiling completed in 0.0647644 seconds. Fastest Tactic: 0x9cb304e2edbc1221 Time: 0.0402286 最终trt将57个层都进行了Computing costs,得到各网络层的最优实现方案。 除了网络层,还需要reformat layer,它的作用是改变tensor的格式,将前一层的输出重新排布成后一层所需的格式。这样就可以使得两层之间的tensor兼容,然后进行融合。 例如:Conv_0 + Relu_1层需要[50176,1:4,224,1]格式的tensor作为输入,而输入层输出的是[150528,50176,224,1]格式,所以在输入层和Conv_0层之间加入了reformat layer,将tensor重新排布成Conv层需要的格式。 最终添加了25个reformat layer,模型变为了82层。 ... [08/20/2023-12:00:07] [V] [TRT] Adding reformat layer: Reformatted Input Tensor 0 to Gemm_121 (onnx::Flatten_493) from Float(512,1:4,512,512) to Float(2048,1,1,1) [08/20/2023-12:00:07] [V] [TRT] Formats and tactics selection completed in 15.1664 seconds. [08/20/2023-12:00:07] [V] [TRT] After reformat layers: 82 layers [08/20/2023-12:00:07] [V] [TRT] Total number of blocks in pre-optimized block assignment: 82 [08/20/2023-12:00:07] [I] [TRT] Detected 1 inputs and 1 output network tensors. 存储空间占用情况 介绍各网络层存储占用情况,以及汇总,例如本案例,engine的GPU占用是107MB ... [08/20/2023-12:00:07] [V] [TRT] Layer: Conv_116 + Add_117 + Relu_118 Host Persistent: 7200 Device Persistent: 0 Scratch Memory: 0 [08/20/2023-12:00:07] [V] [TRT] Layer: GlobalAveragePool_119 Host Persistent: 4176 Device Persistent: 0 Scratch Memory: 0 [08/20/2023-12:00:07] [V] [TRT] Layer: Gemm_121 Host Persistent: 6944 Device Persistent: 0 Scratch Memory: 0 [08/20/2023-12:00:07] [V] [TRT] Skipped printing memory information for 26 layers with 0 memory size i.e. Host Persistent + Device Persistent + Scratch Memory == 0. [08/20/2023-12:00:07] [I] [TRT] Total Host Persistent Memory: 331696 [08/20/2023-12:00:07] [I] [TRT] Total Device Persistent Memory: 22016 [08/20/2023-12:00:07] [I] [TRT] Total Scratch Memory: 4608 [08/20/2023-12:00:07] [I] [TRT] [MemUsageStats] Peak memory usage of TRT CPU/GPU memory allocators: CPU 18 MiB, GPU 107 MiB engine构建情况 对完成好的engine各网络层、网络层对应的kernel选择情况进行打印。 可以看到engine的构建耗时15.3秒 [08/20/2023-12:00:07] [V] [TRT] Engine generation completed in 15.3167 seconds. [08/20/2023-12:00:07] [V] [TRT] Deleting timing cache: 214 entries, served 528 hits since creation. [08/20/2023-12:00:07] [V] [TRT] Engine Layer Information: Layer(Reformat): Reformatting CopyNode for Input Tensor 0 to Conv_0 + Relu_1, Tactic: 0x00000000000003e8, input (Float[1,3,224,224]) -> Reformatted Input Tensor 0 to Conv_0 + Relu_1 (Float[1,3:4,224,224]) Layer(CaskConvolution): Conv_0 + Relu_1, Tactic: 0x9cb304e2edbc1221, Reformatted Input Tensor 0 to Conv_0 + Relu_1 (Float[1,3:4,224,224]) -> onnx::MaxPool_323 (Float[1,64:4,112,112]) 推理耗时统计 进行10次推理,依次得到以下信息,同时相应的统计值。 Throughput:模型的推理吞吐量,以每秒推理数量(QPS)为单位。实际图片量需要乘以batchsize。 Latency:模型一次推理的延迟时间统计信息,包括最小值、最大值、平均值、中位数和百分位数(90%、95%和99%)。 Enqueue Time:将数据传输到GPU的时间统计信息, H2D Latency:将主机数据传输到GPU的延迟时间统计信息, GPU Compute Time:模型在GPU上运行的计算时间统计信息 D2H Latency:从GPU将数据传输回主机的延迟时间统计信息 Total Host Walltime:模型推理的总时间,包括传输数据、计算和传输数据回主机的时间。 Total GPU Compute Time:模型在GPU上的总计算时间。 [08/20/2023-12:00:11] [I] === Performance summary === [08/20/2023-12:00:11] [I] Throughput: 502.107 qps [08/20/2023-12:00:11] [I] Latency: min = 1.88583 ms, max = 2.96844 ms, mean = 1.93245 ms, median = 1.91833 ms, percentile(90%) = 1.9592 ms, percentile(95%) = 1.98364 ms, percentile(99%) = 2.34845 ms [08/20/2023-12:00:11] [I] Enqueue Time: min = 0.312988 ms, max = 1.77197 ms, mean = 0.46439 ms, median = 0.390869 ms, percentile(90%) = 0.748291 ms, percentile(95%) = 0.836853 ms, percentile(99%) = 1.10229 ms [08/20/2023-12:00:11] [I] H2D Latency: min = 0.0714111 ms, max = 0.225464 ms, mean = 0.0769845 ms, median = 0.0737305 ms, percentile(90%) = 0.088623 ms, percentile(95%) = 0.0947266 ms, percentile(99%) = 0.112671 ms [08/20/2023-12:00:11] [I] GPU Compute Time: min = 1.80939 ms, max = 2.86005 ms, mean = 1.8518 ms, median = 1.84009 ms, percentile(90%) = 1.87183 ms, percentile(95%) = 1.89734 ms, percentile(99%) = 2.22314 ms [08/20/2023-12:00:11] [I] D2H Latency: min = 0.00317383 ms, max = 0.0220947 ms, mean = 0.00366304 ms, median = 0.00341797 ms, percentile(90%) = 0.00390625 ms, percentile(95%) = 0.00402832 ms, percentile(99%) = 0.00488281 ms [08/20/2023-12:00:11] [I] Total Host Walltime: 3.00334 s [08/20/2023-12:00:11] [I] Total GPU Compute Time: 2.79252 s [08/20/2023-12:00:11] [I] Explanations of the performance metrics are printed in the verbose logs. [08/20/2023-12:00:11] [V] [08/20/2023-12:00:11] [V] === Explanations of the performance metrics === [08/20/2023-12:00:11] [V] Total Host Walltime: the host walltime from when the first query (after warmups) is enqueued to when the last query is completed. [08/20/2023-12:00:11] [V] GPU Compute Time: the GPU latency to execute the kernels for a query. [08/20/2023-12:00:11] [V] Total GPU Compute Time: the summation of the GPU Compute Time of all the queries. If this is significantly shorter than Total Host Walltime, the GPU may be under-utilized because of host-side overheads or data transfers. [08/20/2023-12:00:11] [V] Throughput: the observed throughput computed by dividing the number of queries by the Total Host Walltime. If this is significantly lower than the reciprocal of GPU Compute Time, the GPU may be under-utilized because of host-side overheads or data transfers. [08/20/2023-12:00:11] [V] Enqueue Time: the host latency to enqueue a query. If this is longer than GPU Compute Time, the GPU may be under-utilized. [08/20/2023-12:00:11] [V] H2D Latency: the latency for host-to-device data transfers for input tensors of a single query. [08/20/2023-12:00:11] [V] D2H Latency: the latency for device-to-host data transfers for output tensors of a single query. [08/20/2023-12:00:11] [V] Latency: the summation of H2D Latency, GPU Compute Time, and D2H Latency. This is the latency to infer a single query. [08/20/2023-12:00:11] [I] 案例5:trt模型推理 通过推理trt模型,可以查看网络层信息、网络层推理耗时情况 trtexec --loadEngine=resnet50_bs_128_fp32.engine --batch=128 --useCudaGraph --dumpProfile --dumpLayerInfo > inference.log 可以看到,卷积层耗时较大 8/20/2023-17:51:29] [I] === Profile (32 iterations ) === [08/20/2023-17:51:29] [I] Layer Time (ms) Avg. Time (ms) Median Time (ms) Time % [08/20/2023-17:51:29] [I] Reformatting CopyNode for Input Tensor 0 to Conv_0 + Relu_1 18.53 0.5790 0.5765 0.6 [08/20/2023-17:51:29] [I] Conv_0 + Relu_1 116.65 3.6453 3.6336 3.6 [08/20/2023-17:51:29] [I] MaxPool_2 51.21 1.6004 1.6005 1.6 小节 本节介绍了trtexec基础用法,可以通过trtexec实现onnx模型转trt模型,并且可以进行动态batchsize设置、半精度量化的选择。 更全面用法推荐查看帮助文档以及官方文档。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.4-trt-tools.html":{"url":"chapter-12/12.4-trt-tools.html","title":"12.4 TensorRT 实用工具","keywords":"","body":"12.4 TensorRT 实用工具 前言 工程化部署是一个复杂的任务,涉及的环节众多,因此需要有足够好的工具来检测、分析,NVIDIA也提供了一系列工具用于分析、调试、优化部署环节。本节就介绍两个实用工具,nsight system 和 polygraphy。 nsight system可分析cpu和gpu的性能,可找出应用程序的瓶颈。 polygraphy可在各种框架中运行和调试深度学习模型,用于分析模型转换间的瓶颈。 nsight system NVIDIA Nsight Systems是一个系统分析工具,它可以分析CPU和GPU的利用率、内存占用、数据输送量等各种性能指标,找出应用程序的瓶颈所在。用户文档 安装 打开官网,选择对应的操作系统、版本进行下载 。 NsightSystems-2023.3.1.92-3314722.msi,双击安装,一路默认 将目录添加到环境变量:C:\\Program Files\\NVIDIA Corporation\\Nsight Systems 2023.3.1\\target-windows-x64 将gui工作目录页添加到环境变量:C:\\Program Files\\NVIDIA Corporation\\Nsight Systems 2023.3.1\\host-windows-x64 运行 nsys包括命令行工具与UI界面,这里采用UI界面演示。 命令行工具是C:\\Program Files\\NVIDIA Corporation\\Nsight Systems 2023.3.1\\target-windows-x64\\nsys.exe UI界面是C:\\Program Files\\NVIDIA Corporation\\Nsight Systems 2023.3.1\\host-windows-x64\\nsys-ui.exe nsys运行逻辑是从nsys端启动任务,nsys会自动监控任务的性能。 第一步:启动nsys。在cmd中输入nsys-ui,或者到安装目录下双击nsys-ui.exe。 第二步:创建project,配置要运行的程序。在这里运行本章配套代码01_trt_resnet50_cuda.py。具体操作如下图所示 第三步:查看统计信息 nsight system是一个强大的软件,但具体如何有效使用,以及如何更细粒度、更接近底层的去分析耗时,请大家参照官方文档以及需求来学习。 polygraphy polygraphy是TensorRT生态中重要的debug调试工具,它可以 使用多种后端运行推理计算,包括 TensorRT, onnxruntime, TensorFlow; 比较不同后端的逐层计算结果; 由模型文件生成 TensorRT 引擎并序列化为.plan; 查看模型网络的逐层信息; 修改 Onnx 模型,如提取子图,计算图化简; 分析 Onnx 转 TensorRT 失败原因,将原计算图中可以 / 不可以转 TensorRT 的子图分割保存; 隔离 TensorRT 中错误的tactic; 常用的几个功能是: 检验 TensorRT 上计算结果正确性 /精度 找出计算错误 / 精度不足的层 进行简单的计算图优化 安装 pip install nvidia-pyindex pip install polygraphy 验证 polygraphy依托于虚拟环境运行,因此需要激活相应的虚拟环境,然后执行 polygraphy -h polygraphy有七种模式,分别是 {run,convert,inspect,surgeon,template,debug,data},具体含义参见文档 (pt112) C:\\Users\\yts32>polygraphy -h usage: polygraphy [-h] [-v] {run,convert,inspect,check,surgeon,template,debug,data} ... Polygraphy: A Deep Learning Debugging Toolkit optional arguments: -h, --help show this help message and exit -v, --version show program's version number and exit Tools: {run,convert,inspect,check,surgeon,template,debug,data} run Run inference and compare results across backends. convert Convert models to other formats. inspect View information about various types of files. check Check and validate various aspects of a model surgeon Modify ONNX models. template [EXPERIMENTAL] Generate template files. debug [EXPERIMENTAL] Debug a wide variety of model issues. data Manipulate input and output data generated by other Polygraphy subtools. 案例1:运行onnx及trt模型 polygraphy run resnet50_bs_1.onnx --onnxrt polygraphy run resnet50_bs_1.engine --trt --input-shapes 'input:[1,3,224,224]' --verbose 得到如下运行日志,表明两个框架推理运行成功: ...... | Completed 1 iteration(s) in 1958 ms | Average inference time: 1958 ms. ...... | Completed 1 iteration(s) in 120.1 ms | Average inference time: 120.1 ms. 案例2:对比onnx与trt输出结果(常用) polygraphy还可以充当trtexec的功能,可以实现onnx导出trt模型,并且进行逐层结果对比。 其中atol表示绝对误差,rtol表示相对误差。 polygraphy run resnet50_bs_1.onnx --onnxrt --trt ^ --save-engine=resnet50_bs_1_fp32_polygraphy.engine ^ --onnx-outputs mark all --trt-outputs mark all ^ --input-shapes \"input:[1,3,224,224]\" ^ --atol 1e-3 --rtol 1e-3 --verbose > onnx-trt-compare.log 输出的日志如下: 对于每一个网络层会输出onnx、trt的直方图,绝对误差直方图,相对误差直方图 最后会统计所有网络层符合设置的超参数atol, rtol的百分比,本案例中 Pass Rate: 100.0%。 [I] Comparing Output: 'input.4' (dtype=float32, shape=(1, 64, 112, 112)) with 'input.4' (dtype=float32, shape=(1, 64, 112, 112)) [I] Tolerance: [abs=0.001, rel=0.001] | Checking elemwise error [I] onnxrt-runner-N0-08/20/23-23:08:36: input.4 | Stats: mean=0.20451, std-dev=0.31748, var=0.10079, median=0.19194, min=-1.3369 at (0, 33, 13, 104), max=2.0691 at (0, 33, 64, 77), avg-magnitude=0.29563 [V] ---- Histogram ---- Bin Range | Num Elems | Visualization (-1.34 , -0.996) | 29 | (-0.996, -0.656) | 11765 | # (-0.656, -0.315) | 31237 | ### (-0.315, 0.0255) | 140832 | ############# (0.0255, 0.366 ) | 411249 | ######################################## (0.366 , 0.707 ) | 166138 | ################ (0.707 , 1.05 ) | 40956 | ### (1.05 , 1.39 ) | 551 | (1.39 , 1.73 ) | 54 | (1.73 , 2.07 ) | 5 | [I] trt-runner-N0-08/20/23-23:08:36: input.4 | Stats: mean=0.20451, std-dev=0.31748, var=0.10079, median=0.19194, min=-1.3369 at (0, 33, 13, 104), max=2.0691 at (0, 33, 64, 77), avg-magnitude=0.29563 [V] ---- Histogram ---- Bin Range | Num Elems | Visualization (-1.34 , -0.996) | 29 | (-0.996, -0.656) | 11765 | # (-0.656, -0.315) | 31237 | ### (-0.315, 0.0255) | 140832 | ############# (0.0255, 0.366 ) | 411249 | ######################################## (0.366 , 0.707 ) | 166138 | ################ (0.707 , 1.05 ) | 40956 | ### (1.05 , 1.39 ) | 551 | (1.39 , 1.73 ) | 54 | (1.73 , 2.07 ) | 5 | [I] Error Metrics: input.4 [I] Minimum Required Tolerance: elemwise error | [abs=8.3447e-07] OR [rel=0.037037] (requirements may be lower if both abs/rel tolerances are set) [I] Absolute Difference | Stats: mean=2.6075e-08, std-dev=3.2558e-08, var=1.06e-15, median=1.4901e-08, min=0 at (0, 0, 0, 3), max=8.3447e-07 at (0, 33, 86, 43), avg-magnitude=2.6075e-08 [V] ---- Histogram ---- Bin Range | Num Elems | Visualization (0 , 8.34e-08) | 757931 | ######################################## (8.34e-08, 1.67e-07) | 40058 | ## (1.67e-07, 2.5e-07 ) | 4334 | (2.5e-07 , 3.34e-07) | 249 | (3.34e-07, 4.17e-07) | 181 | (4.17e-07, 5.01e-07) | 53 | (5.01e-07, 5.84e-07) | 0 | (5.84e-07, 6.68e-07) | 8 | (6.68e-07, 7.51e-07) | 1 | (7.51e-07, 8.34e-07) | 1 | [I] Relative Difference | Stats: mean=6.039e-07, std-dev=5.4597e-05, var=2.9809e-09, median=8.7838e-08, min=0 at (0, 0, 0, 3), max=0.037037 at (0, 4, 15, 12), avg-magnitude=6.039e-07 [V] ---- Histogram ---- Bin Range | Num Elems | Visualization (0 , 0.0037 ) | 802806 | ######################################## (0.0037 , 0.00741) | 7 | (0.00741, 0.0111 ) | 1 | (0.0111 , 0.0148 ) | 0 | (0.0148 , 0.0185 ) | 0 | (0.0185 , 0.0222 ) | 0 | (0.0222 , 0.0259 ) | 1 | (0.0259 , 0.0296 ) | 0 | (0.0296 , 0.0333 ) | 0 | (0.0333 , 0.037 ) | 1 | [I] PASSED | Output: 'input.4' | Difference is within tolerance (rel=0.001, abs=0.001) [I] PASSED | All outputs matched | Outputs: ['input.4', 'onnx::MaxPool_323', 'input.8', 'input.16', 'onnx::Conv_327', 'input.24', 'onnx::Conv_330', 'onnx::Add_505', 'onnx::Add_508', 'onnx::Relu_335', 'input.36', 'input.44', 'onnx::Conv_339', 'input.52', 'onnx::Conv_342', 'onnx::Add_517', 'onnx::Relu_345', 'input.60', 'input.68', 'onnx::Conv_349', 'input.76', 'onnx::Conv_352', 'onnx::Add_526', 'onnx::Relu_355', 'input.84', 'input.92', 'onnx::Conv_359', 'input.100', 'onnx::Conv_362', 'onnx::Add_535', 'onnx::Add_538', 'onnx::Relu_367', 'input.112', 'input.120', 'onnx::Conv_371', 'input.128', 'onnx::Conv_374', 'onnx::Add_547', 'onnx::Relu_377', 'input.136', 'input.144', 'onnx::Conv_381', 'input.152', 'onnx::Conv_384', 'onnx::Add_556', 'onnx::Relu_387', 'input.160', 'input.168', 'onnx::Conv_391', 'input.176', 'onnx::Conv_394', 'onnx::Add_565', 'onnx::Relu_397', 'input.184', 'input.192', 'onnx::Conv_401', 'input.200', 'onnx::Conv_404', 'onnx::Add_574', 'onnx::Add_577', 'onnx::Relu_409', 'input.212', 'input.220', 'onnx::Conv_413', 'input.228', 'onnx::Conv_416', 'onnx::Add_586', 'onnx::Relu_419', 'input.236', 'input.244', 'onnx::Conv_423', 'input.252', 'onnx::Conv_426', 'onnx::Add_595', 'onnx::Relu_429', 'input.260', 'input.268', 'onnx::Conv_433', 'input.276', 'onnx::Conv_436', 'onnx::Add_604', 'onnx::Relu_439', 'input.284', 'input.292', 'onnx::Conv_443', 'input.300', 'onnx::Conv_446', 'onnx::Add_613', 'onnx::Relu_449', 'input.308', 'input.316', 'onnx::Conv_453', 'input.324', 'onnx::Conv_456', 'onnx::Add_622', 'onnx::Relu_459', 'input.332', 'input.340', 'onnx::Conv_463', 'input.348', 'onnx::Conv_466', 'onnx::Add_631', 'onnx::Add_634', 'onnx::Relu_471', 'input.360', 'input.368', 'onnx::Conv_475', 'input.376', 'onnx::Conv_478', 'onnx::Add_643', 'onnx::Relu_481', 'input.384', 'input.392', 'onnx::Conv_485', 'input.400', 'onnx::Conv_488', 'onnx::Add_652', 'onnx::Relu_491', 'input.408', 'onnx::Flatten_493', 'onnx::Gemm_494', 'output'] [I] Accuracy Summary | onnxrt-runner-N0-08/20/23-23:08:36 vs. trt-runner-N0-08/20/23-23:08:36 | Passed: 1/1 iterations | Pass Rate: 100.0% 更多使用案例推荐阅读github cookbook 小结 本节介绍了nsight system和polygraphy的应用,在模型部署全流程中,可以深入挖掘的还有很多,推荐查看TensorRT的GitHub下的tools目录 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.5-trt-python-api.html":{"url":"chapter-12/12.5-trt-python-api.html","title":"12.5 TensorRT API 使用","keywords":"","body":"12.5 TensorRT API 使用 前言 在TensorRT的第二小节中,介绍了TensorRT模型计算图的构建通常有三种方式,分别是TensorRT API、parser、训练框架中trt工具。 三种方式的便捷程度与灵活度是依次递增和递减的,TensorRT API最灵活,可自由设定网络每一层,任意编辑模型的任何层,本节就介绍 TensorRT API的使用。 TensorRT API 简介 TensorRT API是构建Network的一种方法,整个TensorRT的使用流程还是与第二节中介绍的一样,整体流程如下图所示 这里不再赘述workflow内容,直接看TensorRT是如何构建network的,整体可分三个步骤: 创建network:network = builder.create_network() 逐层搭建网络:搭建网络层,并且赋予权重值 创建engine:engine = builder.build_engine(network, config) 由此可见,核心部分在于network提供了一系列网络层的搭建,这类似于pytorch中的nn.Module可以搭建各种网络层。 TensorRT官方提供的网络层可参考文档 下面介绍采用TensorRT API搭建常见网络层的方法。 TensorRT API 网络层创建 卷积层 add_convolution(self: tensorrt.tensorrt.INetworkDefinition, input: tensorrt.tensorrt.ITensor, num_output_maps: int, kernel_shape: tensorrt.tensorrt.DimsHW, kernel: tensorrt.tensorrt.Weights, bias: tensorrt.tensorrt.Weights = None)→ tensorrt.tensorrt.IConvolutionLayer Parameters: input – The input tensor to the convolution. num_output_maps – The number of output feature maps for the convolution. kernel_shape – The dimensions of the convolution kernel. kernel – The kernel weights for the convolution. bias – The optional bias weights for the convolution. Returns:The new convolution layer, or None if it could not be created. 具体的创建案例代码解释如下: 设置输入input:这个input如果是第一层的话,需要自行构建data;如果是中间层,则需要设置为上一层的输出ITensor,例如pool2.get_output(0)返回的是一个ITensor对象。 设置卷积权重:在创建网络时,直接赋予卷积层的权重,这个权重通常通过wst文件获取,是一个字典,key是自行标识的名称,value是np.ndarray。 data = network.add_input('input', trt.float32, (3, 224, 224)) conv1 = network.add_convolution(input=data, num_output_maps=64, kernel_shape=(7, 7), kernel=weight_map[\"conv1.weight\"], bias=trt.Weights()) fc1 = network.add_fully_connected(input=pool2.get_output(0), num_outputs=OUTPUT_SIZE, kernel=weight_map['fc.weight'], bias=weight_map['fc.bias']) BN层 在TensorRT API中没有BN的实现,BN层需要通过add_scale层来实现,即通过减法、乘法的操作等价于实现BN层。 下面代码是实现过程,核心是手动计算出需要shift和scale的数值,然后创建add_scale层。 def addBatchNorm2d(network, weight_map, input, layer_name, eps): gamma = weight_map[layer_name + \".weight\"] beta = weight_map[layer_name + \".bias\"] mean = weight_map[layer_name + \".running_mean\"] var = weight_map[layer_name + \".running_var\"] var = np.sqrt(var + eps) scale = gamma / var shift = -mean / var * gamma + beta return network.add_scale(input=input, mode=trt.ScaleMode.CHANNEL, shift=shift, scale=scale) 激活函数层 激活函数层通过add_activation,其中提供了type属性来设置不同的激活函数。 relu3 = network.add_activation(ew1.get_output(0), type=trt.ActivationType.RELU) type中可设置的类型有十多种,具体参考IActivationLayer的文档 RELU : Rectified Linear activation SIGMOID : Sigmoid activation TANH : Hyperbolic Tangent activation LEAKY_RELU : Leaky Relu activation: f(x) = x if x >= 0, f(x) = alpha * x if x = 0, f(x) = alpha * (exp(x) - 1) if x 0, f(x) = beta * (alpha * exp(x) - alpha) if x alpha, f(x) = 0 if x 池化层 pool1 = network.add_pooling(input=relu1.get_output(0), window_size=trt.DimsHW(3, 3), type=trt.PoolingType.MAX) 全连接层 全连接层在v8.6.1之后进行了删除,不再支持直接创建,需要用add_matrix_multiply、add_elementwise间接实现。 这个问题在官方文档没有指引,是通过多种方法找到的替代方案,这里简单记录debug过程。 运行报错:AttributeError: 'tensorrt.tensorrt.INetworkDefinition' object has no attribute 'add_fully_connected' 各种文档找'add_fully_connected',无果; 官方文档查阅 ‘INetworkDefinition' 支持的方法,的确没了add_fully_connected,猜测肯定有对应方法替换,梳理所有支持的方法,找到最相关的方法是:add_matrix_multiply TRT github仓库搜索add_matrix_multiply, sample.py,发现了FC层实现方法: https://github.com/NVIDIA/TensorRT/blob/c0c633cc629cc0705f0f69359f531a192e524c0f/samples/python/network_api_pytorch_mnist/sample.py 具体方法如下: def add_matmul_as_fc(net, input, outputs, w, b): assert len(input.shape) >= 3 m = 1 if len(input.shape) == 3 else input.shape[0] k = int(np.prod(input.shape) / m) # 输入大小: 2048 assert np.prod(input.shape) == m * k n = int(w.size / k) # 输出大小: 1000 assert w.size == n * k assert b.size == n input_reshape = net.add_shuffle(input) input_reshape.reshape_dims = trt.Dims2(m, k) filter_const = net.add_constant(trt.Dims2(n, k), w) mm = net.add_matrix_multiply( input_reshape.get_output(0), trt.MatrixOperation.NONE, filter_const.get_output(0), trt.MatrixOperation.TRANSPOSE, ) bias_const = net.add_constant(trt.Dims2(1, n), b) bias_add = net.add_elementwise(mm.get_output(0), bias_const.get_output(0), trt.ElementWiseOperation.SUM) output_reshape = net.add_shuffle(bias_add.get_output(0)) output_reshape.reshape_dims = trt.Dims4(m, n, 1, 1) return output_reshape V 8.6.1版本可用的方法如下: fc1 = network.add_fully_connected(input=pool2.get_output(0), num_outputs=OUTPUT_SIZE, kernel=weight_map['fc.weight'], bias=weight_map['fc.bias']) 以上是常见的网络层介绍,更多网络层参见文档 tensorrtx 学习TensorRT API搭建网络模型,十分推荐根据tensorrtx的代码资料学习。 tensorrtx是一个基于TensorRT API构建常用的网络模型库,它完全基于TensorRT API搭建模型,这使得模型的构建具备高可调节性,也对理解、学习网络模型内部细节提供了很好的资料。 tensorrtx为模型提供了python和c++的代码,使用tensorrtx的工作流程如下: 获取训练好的模型权重文件,例如pytorch的pt文件,tensorflow的ckpt文件,MXNet的.params+.json等 将权重文件导出为.wts文件,wts文件TensorRT定义的一种模型参数文件格式 创建network,逐网络层搭建模型,并基于wts文件赋予网络层权重值 搭建engine,进行推理或序列化保存到磁盘。 wts 文件 这里遇到一个新的文件格式wts,wts是Weights的缩写,是由Nvidia定义的一种模型参数文件格式。 wts文件只包含模型的参数数据,不包含网络结构等其他信息。网络结构需要另外定义,在这里就需要用TensorRT API逐层定义。 通过使用wts文件分离参数和网络结构,可以方便地进行模型压缩、量化等优化,也可以跨框架部署模型,同时还可保护模型的结构不被泄露。 ResNet50 推理 接下来演示如何采用TensorRT API搭建ResNet50,并完成推理,代码来自tensorrx和pytorchx。 整体分两步: 第一步,通过pytorchx的代码获得resnet.wts。在本教程,resnet.wts的生成可通过配套代码实现 第二步,通过tensorrtx的代码搭建TensorRT的network,并且创建engine进行推理。在本教程,推理代码可通过配套代码实现 wts文件生成 上述代码已集成到配套章节代码中,先看生成wts的核心代码: wts文件第一行表明整个文件有多少个权重; 之后的每一行是一个权重名称及权重值的二进制形式。 f = open(path_wts, 'w') f.write(\"{}\\n\".format(len(net.state_dict().keys()))) for k, v in net.state_dict().items(): print('key: ', k) print('value: ', v.shape) vr = v.reshape(-1).cpu().numpy() f.write(\"{} {}\".format(k, len(vr))) for vv in vr: f.write(\" \") f.write(struct.pack(\">f\", float(vv)).hex()) f.write(\"\\n\") 以下是一个wts案例: 10 conv1.weight 150 be40ee1b bd20bab8 bdc4bc53 ....... conv1.bias 6 bd327058 ....... conv2.weight 2400 3c6f2220 3c693090 ...... network创建 接下来参照第一份配套代码,将engine的构建从直接加载文件改为基于network创建的形式 回顾之前直接读取磁盘上的engine文件,可以直接得到engine。 with open(model_path, 'rb') as ff, trt.Runtime(logger) as runtime: engine = runtime.deserialize_cuda_engine(ff.read()) 接下来看network逐层创建的过程,主workflow是一样的,需要创建各模块,这里有两个重点。 trt推荐采用显式batch,因此create_network的时候需要设置1 resnet50的创建,在函数build_model_by_trt_api中实现 def init_model(model_path): logger = trt.Logger(trt.Logger.WARNING) builder = trt.Builder(logger) network = builder.create_network(1 build_model_by_trt_api函数中就是上文提到的TensorRT API的各网络层创建接口,这里就不一一赘述,到这里,整体的流程梳理完成。 def build_model_by_trt_api(network, model_path): weight_map = load_wts_file(model_path) # 权重字典 data = network.add_input(\"data\", trt.float32, (1, 3, 224, 224)) assert data conv1 = network.add_convolution(input=data, num_output_maps=64, kernel_shape=(7, 7), kernel=weight_map[\"conv1.weight\"], bias=trt.Weights()) 最终能得到如下图片,表明基于TensorRT API创建网络模型并推理是成功的。 tensorrtx还提供了非常多常见的网络模型,如果有需要使用 TensorRT API创建网络模型,建议优选tensorrtx中寻找参考案例。 小结 本小节介绍TensorRT API进行trt模型创建的流程及案例。首先介绍了TensorRT API创建网络层的方法及常用接口,随后介绍学习TensorRT API 非常好的代码库——tensorrtx,最后以一个resnet50的推理案例介绍基于TensorRT API 搭建模型的全流程。 通过本小节,可以了解TensorRT API 创建模型的过程与概念,更多案例及用法,请阅读: python api文档:https://docs.nvidia.com/deeplearning/tensorrt/api/python_api/index.htm tensorrtx:https://github.com/wang-xinyu/tensorrtx Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.6-quantization.html":{"url":"chapter-12/12.6-quantization.html","title":"12.6 模型量化基础概念","keywords":"","body":"12.6 模型量化基础概念 前言 量化是模型压缩和加速常用的方法,因其效果相较于蒸馏、剪枝都要好,因此被大量的应用。 TensorRT也提供了模型量化的功能,本节开始来学习如何利用TensorRT进行模型量化,实现模型存储的减小,时延的降低,吞吐量的提升。 由于量化是非常大的概念,知识点和技巧非常多,因此量化将分为三个小节,第一个小节介绍量化基础概念,第二小节介绍TensorRT的PTQ(Post-Trainning Quantization,训练后量化)的方法,第三节介绍QAT(Quantization-aware training,量化感知训练) 。 量化概念 模型量化(model quantization)是通过降低权重和激活值的数据精度,以此减少模型计算时间和能耗的方法。 通常情况下,模型权重和激活值以FP32/FP16进行训练和存储,当精度降低到Int8时,存储数据的内存开销减少了4倍(相对于FP32),矩阵乘法的计算成本减少了16倍。 大量研究及工程实践表明,模型量化有一定的鲁棒性,在降低存储、提高推理效率的同时,模型精度损失相对较小,如Nvidia文章《integer quantization for deep learning inference principles and empirical evaluation》中给出的精度损失统计表格,通常掉点在1个点上下,但可带来几十倍的效率提升。 量化优点 量化最直观的优点是带来速度提升和存储的降低,但其好处远不止这些,它还可以为环境保护,抵御全球变暖提供帮助,因为低比特模型可以带来更少的热量挥发。 根据文章《A Survey of Quantization Methods for Efficient》的分析,FP32的乘/加操作消耗的能量是Int8的30倍/18.5倍,由此可见低比特可以显著降低能量消耗与热量挥发。 由此可知,量化可以带来: 存储降低:通过将权重和激活值从高位精度(如32位)降低到低位精度(如8位),模型所需的存储空间大大减少。 访问减少:低位精度的量化模型需要较少的内存带宽和存储带宽来读取和传输权重和激活值 速度提升:由于低精度模型执行矩阵乘法等计算操作时的计算量减少,模型的推理速度可以显著提高 电费减少:量化模型的计算操作需要更少的能量,功耗降低,从而减少了电费开支 热量减少:由于量化模型对处理器的负载较低,因此产生的热量也相对较少,这对于移动设备和嵌入式系统等散热受限的环境中的长时间性能非常重要。 量化的数学公式 在数字信号处理领域,量化是指将信号的连续取值近似为有限多个离散值的过程,在模型量化中也是类似的,用较少的数(例如:Int8)表示较多的数(例如:FP32)。 用较少的数表示较多的数,实际上是一个线性映射,求一个缩放值scale和一个偏移量offset,将FP32中的数缩放到int8表示的256个数中的某一个数。 可以参考下图,最上面是FP32可表达的数据范围,经过缩放和偏移以及四舍五入,将数据划分到[-128, 127]之间,这类似于直方图统计。 将浮点型数据转换为整型数据有多种形式,根据整型数据的0点是否为中间位置,可将量化分为对称量化(Symmetric Quantization)和非对称量化(Asymmetric Quantization)。参考MIT TinyML 第六节的两页PPT来学习对称量化和非对称量化。(PS:此处讲的均是线性量化) 浮点型数据与整型数据转换公式如下: r = S(q - Z) q = r / S + Z r:浮点型 q:整型 S:缩放因子scale Z:零点数值Zero 对称量化 对称量化是零点在0处,为此需要取绝对值的最大值作为上限、下限,这样就可得到零点在0处。 然后根据公式计算缩放值scale,S = 2.12 / 1 = 2.12,更一般的可以写为 S = 2.12 - (-2.12) / 1 - (-1) = (22.12) / (21) = 2.12 / 1 = 2.12 由于Z=0,不需要计算Z值 有了S和Z就可以对浮点型数据和整型数据进行互相转换.如下图所示,图片来源: https://efficentml.ai 非对称量化 非对称量化的上下限就根据真实分布选择,这样通常Z就不是0。 根据公式计算Scale,S = 2.12 - (-1.08) / 1 - (-2) = 1.07; 根据公式计算Zero, Z = round(-2 - -1.08/1.07) = -1; 尝试量化 r=0,观察q对应什么值: q = r / S + Z = 0/1.07 + -1 = -1 可以发现,非对称量化时,浮点数的0值在量化后变为了Z值。 动态范围计算方法 在计算scale时,如何选择数据的上限、下限将会影响最终量化效果。 例如上文的对称量化是有数据损失的,是不饱和量化。因为直接取绝对值的最大值作为上限,取负作为下限,通常会存在一个数据区间是没有浮点数据的,但会占据量化后的一部分数据表示,这些数据表示则会被浪费掉,导致量化后是不饱和的。 Min-Max 除了上文中的Min-Max方法、通常还采用Histgram和Entropy进行动态范围的选择。 这里推荐B站博主手写AI的视频教程这里对其视频做笔记分享。 Histogram 为了选择合适的上限、下限,可以通过直方图进行数据统计,然后根据数据分布情况,选择恰当的上下限,以此解决离群点带来的不饱和问题。 通常Histogram需要设置一个超参数,用来确定上下限收缩的范围,称为limit。 通常limit=0.9999, 0.99999之类。表示从上限、下限进行收缩,收缩之后的数据频次占总数据量的99%。 可采用双指针实现上述过程: 首先定左指针、右指针分别指向左边界和右边界 计算当前双指针之间的直方图覆盖率,如果小于等于设定的覆盖率阈值,则返回此刻的左指针指向的直方图值,如果不满足,则需要调整双指针的值,向中间靠拢 如果当前左指针所指向的直方图值大于右指针所指向的直方图值,则右指针左移,否则左指针右移 循环,直到双指针覆盖的区域满足要求 得到上限、下限后,再根据量化公式计算S和Z。 Entropy(K-L散度) Entropy方法是一种基于概率分布的动态范围计算方法,通过对原始数据进行直方图统计,得到真实数据分布p,然后通过KL散度(相对熵,用于描述两个概率分布之间的相似性)来衡量分布q与p之间的相似性,最终找到一个与p很相似的q分布,q的分布上限、下限就可以作为动态范围。 下图是计算q分布的伪代码,具体过程包含较多细节,推荐大家观察博主的视频即可,在此了解采用的Entropy是通过概率分布的方式寻找动态范围,此方法在TensorRT中也有实现和运用。 weight、bias和activation的量化 上文对量化数值的变化及选择进行了介绍,但对于模型具体运算过程是如何对FP32数据进行量化为int8,最终由反量化回FP32进行输出,还需要进一步分析。 在模型中,需要量化的内容主要有三个,分别是weight、bias和activation。 weight和bias好理解,当模型训练好之后,就可以选定动态范围,然后计算scale和Z值,就可得到量化方法及量化后的weight和bias。 而activation也需要量化,这个一开始不好理解,这个要从运算溢出讲起。 例如两个int8计算时,如100 + 100 = 200,这是采用int8(可表示[-128, 127])就无法表示200,因此对于运算后的值(激活值)通常需要更高精度来表示,如int32。 但对于激活值输出又需要采用int8来表示,这时候就需要将int32量化为int8的过程,这就是激活值(activation)量化。 可通过下图观察一个网络层在量化后是如何计算的,同时可知道一个网络层需要在哪几个地方进行量化(计算scale和Z值)。 图中对于weights、bias和网络层输出都需要经过quantize,即量化。量化的scale和Z值则是在部署前进行确定,这些值的确定也是模型量化核心的内容。 了解了需要量化的对象后,下面来学习校准(Calibration)的概念。 对于weight和bias,当模型训练结束后,数值分布就已确定,因此无需额外操作获取数据分布,即可进行scale和Z值的计算。 对于activation就不行,它依赖于具体数据的输入。因此,对于activation的量化,往往需要采用真实数据的输入(前向传播),然后统计激活值的数据分布,用于量化。 对于activation,采用真实数据获取数据分布再量化计算scale和Z值,这个过程叫做校准(Calibration)。 PTQ与QAT 量化也好,校准也好,都是将高精度数据进行统计,然后计算scale和Z值,在部署的推理过程中进行低比特量化。 根据计算scale和Z值的阶段不同,模型量化可分为PTQ和QAT两大类,两者各有优缺点。 PTQ(Post-training quantization,训练后量化),是不需要训练数据(输入数据和标签对数据),通常采用小部分数据(不需要标签)进行模型激活值校准,统计激活值的分布,然后选择量化scale和Z值,对于weight和bias直接根据模型数据进行量化。 QAT(Quantization-aware training,量化感知训练) ,是需要训练数据,并需要在训练阶段插入伪量化层,训练得到带QDQ节点的数据,然后在量化时采用QDQ节点进行量化,可以得到比PTQ更高的精度。 QDQ(quantize节点、dequantize节点)节点是QAT的灵魂,是一个量化节点和反量化节点,可在训练时进行迭代优化,思想是在训练的时候获得一个好的Q节点,它的scale和Z值是比较好的scale和Z值,为什么说它得到的值比较好呢?因为这些值可通过DQ节点恢复出更接近真实值的数据,因此认为训练阶段获得的Q节点中的scale和Z值是较好的选择。 根据TensorRT官方介绍pdf,QDQ(也称FQ, fake quantize,伪量化节点)的插入如下图所示,最终部署时可直接采用Q节点中的scale和Z值。 PTQ和QAT的对比,可参考B站博主ZOMI酱的视频 通过综述《A Survey of Quantization Methods for Efficient》图4,也可以很好的理解PTQ和QAT之间的差别。 那么PTQ和QAT如何选择? 通常根据精度损失的多少来选,优先选择方便的PTQ进行验证,通常PTQ掉2个点以内,如果PTQ掉点过大,可采用QAT。 下图是Nvidia对模型量化前后精度损失的统计,绝大多数模型采用PTQ掉点0-2个点,甚至还有精度提高的模型。 特别需要注意的是efficientnet及其他轻量化设计的模型结构对于PTQ是不适用的,需要采用QAT进行量化。 小结 本节详细介绍了量化的基础知识和概念,对于初入门量化概念的朋友来说,信息量会有一些大,这里做一下要点总结。 量化概念:高精度用低精度数据表示,可减少存储,提高推理速度 线性量化:分为对称和非对称量化,公式是r = S(q - Z), q = r / S + Z , r是高精度数据,q是量化后数据 S和Z的求取:可通过Min-Max、Histogram、Entropy三种方法确定数据上限和下限,然后根据通用公式求取 量化对象:通常量化对象有weight、bias、activation,前两者不需要数据推理,后者需要真实数据推理才可进行统计,并计算S和Z值 量化方法:根据是否需要训练,量化分为PTQ和QAT PTQ:不需要训练数据,需要小批量真实数据(校准数据)进行推理,对activation进行量化。 QAT:需要训练数据,在训练阶段插入QDQ节点,在部署推理时,Q节点可实现高精度量化。 下一小节将介绍在TensorRT中实现PTQ。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.7-ptq.html":{"url":"chapter-12/12.7-ptq.html","title":"12.7 PTQ 量化实践","keywords":"","body":"12.7 PTQ 量化实践 前言 上一节介绍了模型量化的概念,本节开始进行PTQ的实践,并对比不同动态范围计算方法的差别(max, entropy, mse, pertentile)。 由于量化是对已经训练好的模型进行量化并评估在测试集上的精度,因此量化(PTQ和QAT)需要依托一个完成的模型训练项目开展,这里采用第八章第一节的分类任务。 PTQ代码实践将从以下几个部分进行:pytorch-quantization库介绍、推理统计、校准、量化、评估模型准确率和TensorRT推理效率。 pytorch_quantization 工具库 安装: pip install pytorch-quantization --extra-index-url https://pypi.ngc.nvidia.com 或者(pytorch 1.12版本时推荐用 pytorch-quantization==2.1.2 ) pip install pytorch-quantization==2.1.2 pytorch_quantization是由NVIDIA官方提供的PyTorch量化库,用于将PyTorch模型量化为低比特形式(如Int8)。 pytorch_quantization 库使用非常方便,其PTQ步骤分四步: 第一步:构建具备量化模块的nn.Module,在模型定义之前替换pytorch官方nn.Module的网络层。如nn.conv2替换为quant_nn.QuantConv2d。 Quantxxx这样的网络层中会在原功能基础上,添加一些组件,这些组件将在校准时记录数据、并存储scale和Z值。 第二步:正常构建模型,并执行前向推理。 第三步:执行动态范围计算,并计算scale和Z值。 第四步:导出ONNX模型 通过以上流程可知道,仅第一步中pytorch_quantization 库中自定义的量化功能模块需要深入了解,其余都是常规流程。 因此,下面简单介绍pytorch_quantization中的核心组件。 量化函数——Quantization function 提供两个量化功能函数 tensor_quant 和 fake_tensor_quant。 tensor_quant(inputs, amax, num_bits=8, output_dtype=torch.float, unsigned=False) 功能:对输入张量进行量化,输出量化后的张量 fake_tensor_quant(inputs, amax, num_bits=8, output_dtype=torch.float, unsigned=False) 输入张量进行伪量化,先量化,后反量化,伪量化输出的张量与原张量存在量化误差,可用于理解量化误差的产生。 from pytorch_quantization import tensor_quant # Generate random input. With fixed seed 12345, x should be # tensor([0.9817, 0.8796, 0.9921, 0.4611, 0.0832, 0.1784, 0.3674, 0.5676, 0.3376, 0.2119]) torch.manual_seed(12345) x = torch.rand(10) # fake quantize tensor x. fake_quant_x will be # tensor([0.9843, 0.8828, 0.9921, 0.4609, 0.0859, 0.1797, 0.3672, 0.5703, 0.3359, 0.2109]) fake_quant_x = tensor_quant.fake_tensor_quant(x, x.abs().max()) # quantize tensor x. quant_x will be # tensor([126., 113., 127., 59., 11., 23., 47., 73., 43., 27.]) # with scale=128.0057 quant_x, scale = tensor_quant.tensor_quant(x, x.abs().max()) 量化描述符和量化器——Descriptor and quantizer QuantDescriptor 用于描述该采用何种量化方式,例如 QUANT_DESC_8BIT_PER_TENSOR、 QUANT_DESC_8BIT_CONV2D_WEIGHT_PER_CHANNEL。 这里的pre tensor , per channel是一个新知识点,对于activation通常用per tensor, 对于卷积核通常用per channel。细节不展开,感兴趣可以阅读《A White Paper on Neural Network Quantization》的2.4.2 Per-tensor and per-channel quantization 描述符是用于初始化量化器quantizer的,量化器可接收张量,输出量化后的张量。完成量化后,在量化器内部会记录相应的scale和Z值。 from pytorch_quantization.tensor_quant import QuantDescriptor from pytorch_quantization.nn.modules.tensor_quantizer import TensorQuantizer quant_desc = QuantDescriptor(num_bits=4, fake_quant=False, axis=(0), unsigned=True) quantizer = TensorQuantizer(quant_desc) torch.manual_seed(12345) x = torch.rand(3, 4, 2) quant_x = quantizer(x) print(x) print(quant_x) print(quantizer.scale) # 可以得到3个scale,可知道是按照第一个维度切分张量。因为描述符设置了 axis=(0) 量化模块——Quantized module 前面提到PTQ量化第一步是将pq库的Quantxxx模块替换掉pytorch的nn.Module,例如nn.conv2d变为quant_nn.Conv2d。 在实际量化中主要有Linear和Conv两大类(nn.Module可以回顾chapter-4),下面对比pytorch和pq库的网络层创建区别。 from torch import nn from pytorch_quantization import tensor_quant import pytorch_quantization.nn as quant_nn # pytorch's module fc1 = nn.Linear(in_features, out_features, bias=True) conv1 = nn.Conv2d(in_channels, out_channels, kernel_size) # quantized version quant_fc1 = quant_nn.Linear( in_features, out_features, bias=True, quant_desc_input=tensor_quant.QUANT_DESC_8BIT_PER_TENSOR, quant_desc_weight=tensor_quant.QUANT_DESC_8BIT_LINEAR_WEIGHT_PER_ROW) quant_conv1 = quant_nn.Conv2d( in_channels, out_channels, kernel_size, quant_desc_input=tensor_quant.QUANT_DESC_8BIT_PER_TENSOR, quant_desc_weight=tensor_quant.QUANT_DESC_8BIT_CONV2D_WEIGHT_PER_CHANNEL) 可以发现,只是多了量化描述符的配置,根据量化对象(上一节提到,主要是weight, bias, activation),通常需要设定不同的量化方式。 例如,对于activation用per tensor, 对于linear的权重用per row,对于conv的权重用pre channel。 ResNet50 PTQ 模型权重传入百度云盘,供下载 下面基于chapter-8/01_classification中的项目进行量化,首先训练一个resnet50,得到的accuracy是94.39。模型权重下载-提取码:jhkm # 也可以重新训练 resnet50 nohup python train_main.py --data-path ../data/chest_xray --batch-size 64 --workers 8 --lr 0.01 --lr-step-size 20 --epochs 50 --model resnet50 > ./log.log 2>&1 & 接下来对它进行量化,并对比accuracy掉点情况以及推理加速情况。 配套代码位于这里 流程分析 PTQ整体流程在前言以及介绍,这里从代码角度介绍核心流程 # 第一步,初始化pq,将pytorch的模块替换掉 quant_modules.initialize() # 替换torch.nn的常用层,变为可量化的层 # 第二步,创建模型、加载训练权重 model = get_model(args, logger, device) # 第三步,前向推理,统计activation激活值分布 collect_stats(model, train_loader, num_batches=args.num_data) # 设置量化模块开关,并推理,同时统计激活值 # 第四步,根据动态范围方法计算scale、Z值,获得量化后的模型 compute_amax(model, method=args.ptq_method) # 计算上限、下限,并计算scale 、Z值 # 第五步,采用量化后的模型进行accuracy评估 loss_m_valid, acc_m_valid, mat_valid = \\ utils.ModelTrainer.evaluate(valid_loader, model, criterion, device, classes) # 第六步,保存成ONNX模型和pth模型 第一步中,可以进入pytorch_quantization\\quant_modules.py查看当前支持的量化层,量化层中会插入量化器,量化器中存储量化用的数据——scale和Z # Global member of the file that contains the mapping of quantized modules _DEFAULT_QUANT_MAP = [_quant_entry(torch.nn, \"Conv1d\", quant_nn.QuantConv1d), _quant_entry(torch.nn, \"Conv2d\", quant_nn.QuantConv2d), _quant_entry(torch.nn, \"Conv3d\", quant_nn.QuantConv3d), _quant_entry(torch.nn, \"ConvTranspose1d\", quant_nn.QuantConvTranspose1d), _quant_entry(torch.nn, \"ConvTranspose2d\", quant_nn.QuantConvTranspose2d), _quant_entry(torch.nn, \"ConvTranspose3d\", quant_nn.QuantConvTranspose3d), _quant_entry(torch.nn, \"Linear\", quant_nn.QuantLinear), _quant_entry(torch.nn, \"LSTM\", quant_nn.QuantLSTM), _quant_entry(torch.nn, \"LSTMCell\", quant_nn.QuantLSTMCell), _quant_entry(torch.nn, \"AvgPool1d\", quant_nn.QuantAvgPool1d), _quant_entry(torch.nn, \"AvgPool2d\", quant_nn.QuantAvgPool2d), _quant_entry(torch.nn, \"AvgPool3d\", quant_nn.QuantAvgPool3d), _quant_entry(torch.nn, \"AdaptiveAvgPool1d\", quant_nn.QuantAdaptiveAvgPool1d), _quant_entry(torch.nn, \"AdaptiveAvgPool2d\", quant_nn.QuantAdaptiveAvgPool2d), _quant_entry(torch.nn, \"AdaptiveAvgPool3d\", quant_nn.QuantAdaptiveAvgPool3d),] 第二步,加载模型。 第三步,进行前向推理,但在实际推理时需要对两个开关分别进行控制。 量化开关:若打开,会进行量化,这个在校准的时候是不需要的。 校准开关:若打开,会在模型前向传播时,统计数据,这个在校准时是需要的。 因此在推理前有这样的代码段(同理,推理后需要把两个开关反过来): # Enable calibrators for name, module in model.named_modules(): if isinstance(module, quant_nn.TensorQuantizer): if module._calibrator is not None: module.disable_quant() module.enable_calib() else: module.disable() 第四步中,调用module.load_calib_amax(**kwargs),实现scale和Z值的计算。scale和Z值会存在量化器中。 例如resnet50中的conv1层是一个QuantConv2d,其中包含了input_quantizer和weight_quantizer两个量化器,量化器中存储了amax和step_size(scale值) 第五步中,量化后resnet50模型推理时,量化层会对输入数据、权重进行量化,然后再进行运算。 这里debug进入到layer1的第一个卷积层,观察Quantconv2d的forward(pytorch_quantization\\nn\\modules\\quant_conv.py) 可以看到,进行运算前,对输入数据和权重进行量化,量化用到的就是上述提到的量化器,量化器中有scale和Z值。 def forward(self, input): quant_input, quant_weight = self._quant(input) output = F.conv2d(quant_input, quant_weight, self.bias, self.stride, self.padding, self.dilation, self.groups) return output def _quant(self, input): quant_input = self._input_quantizer(input) quant_weight = self._weight_quantizer(self.weight) return (quant_input, quant_weight) 具体量化过程,可debug追溯得到如下过程: quant_weight = self._weight_quantizer(self.weight) 1 ---> 进入量化器中的forward。pytorch_quantization\\nn\\modules\\tensor_quantizer.py 1 ---> 量化器中可实现校准功能、截断功能、量化功能,是根据3个属性开关来判断,这里也发现了校准数据收集的代码。 def forward(self, inputs): if self._disabled: return inputs outputs = inputs if self._if_calib: if self._calibrator is None: raise RuntimeError(\"Calibrator was not created.\") # Shape is only know when it sees the first tensor self._calibrator.collect(inputs) if self._if_clip: if not self._learn_amax: raise RuntimeError(\"Clip without learning amax is not implemented.\") outputs = self.clip(inputs) if self._if_quant: outputs = self._quant_forward(inputs) return outputs 2 ---> 进入量化器中的_quant_forward(), 2 ---> _quant_forward中可以实现伪量化、量化两种操作,这里是需要进行量化的,因此会执行 2 ---> outputs, self._scale = tensor_quant(inputs, amax, self._num_bits, self._unsigned) 2 ---> tensor_quant就是开篇介绍pytorch_quantization库的第一个功能,对张量进行量化的函数。 def _quant_forward(self, inputs): \"\"\"Quantized forward pass.\"\"\" if self._learn_amax: inputs = self.clip(inputs) amax = torch.max(-self.clip.clip_value_min, self.clip.clip_value_max).detach() else: amax = self._get_amax(inputs) if self._fake_quant: if not TensorQuantizer.use_fb_fake_quant: outputs = fake_tensor_quant(inputs, amax, self._num_bits, self._unsigned, self._narrow_range) else: if inputs.dtype == torch.half or amax.dtype == torch.half: raise Exception(\"Exporting to ONNX in fp16 is not supported. Please export in fp32, i.e. disable AMP.\") outputs = self._fb_fake_quant(inputs, amax) else: outputs, self._scale = tensor_quant(inputs, amax, self._num_bits, self._unsigned) return outputs 完成量化模型评估后,可进行模型保存为ONNX格式,再用netron查看。 可以看到量化的层会插入QDQ节点,这些节点在TensorRT中将会被使用 PTQ 量化实现 执行命令后,可在chapter-8\\01_classification\\Result\\2023-09-26_01-47-40 文件夹下获得对应的onnx输出,并可观察不同方法得到的模型精度 python resnet_ptq.py --mode quantize --num-data 512 动态范围计算方法对比 对于resnet_ptq.py,这里简单介绍使用方法 根据args.ptq_method的有无决定单个动态范围方法量化, 还是四种量化方法均量化。 根据args.mode == 'quantize' 还是 evaluate,决定代码进行量化,还是进行单纯的预训练模型accuracy评估。(用于对比) 其他参数参考get_args_parser() # 进行四种方法对比。跑512个batch python resnet_ptq.py --mode quantize --num-data 512 # 单个方法量化 python resnet_ptq.py --mode quantize --ptq-method max --num-data 512 python resnet_ptq.py --mode quantize --ptq-method entropy --num-data 512 python resnet_ptq.py --mode quantize --ptq-method mse --num-data 512 python resnet_ptq.py --mode quantize --ptq-method percentile --num-data 512 通过五次实验,发现PTQ后的Acc存在较大方差,具体数据分布如下箱线图所示: {'entropy': [85.74, 89.26, 91.67, 92.79, 93.27], 'max': [83.17, 90.54, 92.15, 92.79, 94.07], 'mse': [89.42, 91.67, 92.63, 92.79, 93.59], 'percentile': [85.58, 87.34, 91.67, 92.63, 92.79]} 效果最好的是max,除了一次83%的acc之外,其余效果比较好,并且最高达到94.07%。 建议:方法都试试,实在不稳定,考虑QAT。 PTQ 效率对比 为了观察PTQ带来的效率提升,下面进行未量化、int8量化,分别在bs=1,bs=32时的对比。 首先获得fp32的onnx: python resnet_ptq.py --mode onnxexport 此时在Result\\2023-09-26_01-47-40下应当拥有所有onnx模型,可以采用trtexec进行效率测试 trtexec --onnx=resnet_50_fp32_bs1.onnx trtexec --onnx=resnet_50_fp32_bs32.onnx trtexec --onnx=resnet_50_ptq_bs1_data-num512_percentile_91.03%.onnx --int8 trtexec --onnx=resnet_50_ptq_bs32_data-num512_percentile_91.03%.onnx --int8 --saveEngine=resnet_ptq_int8.engine 时延(中位数) ms fp32 int8 吞吐量 fps fp32 int8 bs=1 1.84 1.01(↓46%) 524 860(↑64%) bs=32 26.79 15.4(↓43%) 37.1*32=1187 64.5*32=2064(↑73.9%) 通过实验,可知道,时延可下降40%, 吞吐量提高70%左右。 小结 本节首先介绍pytorch-quantization库中三个核心概念,然后梳理PTQ量化步骤,接着实现了resnet50的PTQ量化实验,最后对量化后的模型在trtexec上进行效率对比。 通过本节实践可知: int8 可有效提高推理速度和吞吐量,具体提高比例需结合具体模型、GPU型号而定。本案例时延降低40%左右,吞吐量提高70%左右 PTQ量化掉点不稳定,需多次试验,或进行逐层分析,或逐层设定动态范围方法 采用pytorch-quantization量化,大体分6步:初始化quanti_modules,加载模型,推理统计,量化获得s和z,精度评估,模型导出 下一小节,介绍QAT的实现。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.8-qat.html":{"url":"chapter-12/12.8-qat.html","title":"12.8 QAT 量化实践","keywords":"","body":"12.8 QAT 量化实践 前言 上一小节介绍了PTQ量化,本节介绍另外一种重要量化方法——QAT(quantization aware training,感知量化训练)。 本节将从QDQ节点出发,深入了解QAT的量化过程,然后基于QAT进行ResNet50的量化及性能、效率评估。 QAT基础概念 再回顾上上小结QAT与PTQ的区别。 PTQ(Post-training quantization,训练后量化),是不需要训练数据(带标签的数据),通常采用小部分数据(不需要标签)进行模型激活值校准,统计激活值的分布,然后计算动态范围,再计算量化scale和Z值;对于weight和bias直接根据模型权重数据进行量化。 QAT(Quantization-aware training,量化感知训练) ,是需要训练数据,并需要在训练阶段插入伪量化层,训练得到带QDQ节点的模型,然后在量化时采用QDQ节点进行量化,通常可以得到比PTQ更高的精度。 QDQ(quantize节点、dequantize节点)节点是QAT的灵魂,是一个量化节点和反量化节点,可在训练时进行迭代优化,思想是在训练的时候获得一个好的Q节点,它的scale和Z值是比较好的scale和Z值,为什么说它得到的值比较好呢?因为这些值可通过DQ节点恢复出更接近真实值的数据,因此认为训练阶段获得的Q节点中的scale和Z值是较好的选择。 伪量化节点 刚接触量化时,伪量化是较难理解的,伪量化的思想是在训练时模拟量化带来的误差,通过反向传播来对抗误差带来的精度下降,从而得到一个较好的scale和Z值。 具体可参考Nvidia的PPT《toward-int8-inference-deploying-quantization-aware-trained-networks-using-tensorrt》 下图是一个常规fp32的运算过程,接下来将逐步讲解QAT过程。 第一步,插入QDQ(fake quant ops)节点。 插入QDQ的地方即需要量化的地方,回顾上上小结,需要量化的通常是weight、bias和activation,因此有以下4个节点需要量化: X:可看成第一个activation,因此是需要量化的 Wx:conv1的权重 Relu:之所以在Relu而不是conv1之后,是因为Relu是线性的,可以合并它两,在Relu后做FQ即可。 Wy:conv2的权重 第二步,训练导出ONNX,最终得到scale和Z值。 第三步,在TensorRT中对计算图进行优化 常量的折叠:如权重的Q节点可与权重合并,无需在真实推理中由fp32的权重经过scale和Z变为int8的权重 op融合:将DQ信息融合到算子(如图中conv)中,通过op融合,模型计算将变为真实的int8输入、int8输出 于TensorRT是如何做op融合的,暂时找不到相关资料,暂且略过。 思想大概能理解,QConvRelu是吸收了DQ节点的信息,改变weight的数值,这样能更好的计算int8与int8的卷积。 通过以上案例可知conv2输出的是fp32,因为它没有插入QDQ,而conv1输出的是int8,因为插入了QDQ。 这点可以让我们在量化时根据需要,自定义插入QDQ,使得需要高精度输出的地方保持fp32。大意如下图所示 总结一下,QAT的过程: 插入QDQ 训练,得到scale和Z值,导出onnx模型 TensorRT图优化、算子融合,导出engine,算子融合中将DQ信息注入op中,使得op接收int8,输出int8,从而提升了运算速度。 ResNet50 QAT 实践 接下来进行QAT实践,初识量化时,一直认为QAT比PTQ繁琐,因为QAT需要训练,而PTQ直接校准就好了。但是,在代码实现上,QAT反而比PTQ简单许多。 流程分析 首先,分析QAT的实现流程步骤: 第一步,初始化pytorch_quantization,将pytorch的模块替换掉,这一步pq库已经封装好了,只需要一行代码quant_modules.initialize() 第二步,创建模型、加载训练权重 第三步,常规训练步骤 第四步,保存成ONNX模型和pth模型 由此可知,只是在常规训练流程的代码中,插入quant_modules.initialize() 即可。 当然,在QAT训练和常规训练不一样,学习率通常要小100倍,同时可以用余弦下降法,逐步下降学习率。 TensorRT文档中给了一些QAT的建议: Usually, it doesn’t need to fine tune very long. We usually use around **10% of the original training schedule**, starting at **1% of the initial training learning rate**, and a cosine annealing learning rate schedule that follows the decreasing half of a cosine period, down to 1% of the initial fine tuning learning rate (0.01% of the initial training learning rate). 代码说明 同样的,代码位于chapter-8/01_classification中,代码整体与训练代码train_main.py保持一致,其中删减了一些不必要的日志记录代码段。 同时修改了学习率调整方法,以及增加ONNX模型导出。 这里采用4epoch,lr=0.001*0.01进行训练,代码直接运行即可:python resnet50_qat.py 日志如下: Epoch: [000/005] Train Loss avg: 0.1086 Valid Loss avg: 0.1770 Train Acc@1 avg: 96.1009 Valid Acc@1 avg: 93.4295 Epoch: [001/005] Train Loss avg: 0.0786 Valid Loss avg: 0.1819 Train Acc@1 avg: 97.2477 Valid Acc@1 avg: 93.4295 Epoch: [002/005] Train Loss avg: 0.0773 Valid Loss avg: 0.1742 Train Acc@1 avg: 97.3624 Valid Acc@1 avg: 92.9487 Epoch: [003/005] Train Loss avg: 0.0735 Valid Loss avg: 0.1771 Train Acc@1 avg: 97.0183 Valid Acc@1 avg: 93.5897 Epoch: [004/005] Train Loss avg: 0.0633 Valid Loss avg: 0.1704 Train Acc@1 avg: 97.9740 Valid Acc@1 avg: 93.4295 可以看到,Accuracy基本稳定在93.5上下,最优为93.58,相较于原始的94.3,掉了不到1个百分点,比PTQ稳定以及性能更优。 效率对比 得到onnx模型,下面比较fp32、PTQ量化和QAT量化,三者的时延和吞吐量。 可以看到PTQ和QAT在效率上是一样的。 时延(中位数) ms: fp32 PTQ int8 QAT int8 bs=1 1.84 1.01(↓46%) 1.07(42%) bs=32 26.79 15.4(↓43%) 15.4(↓43%) 吞吐量(FPS) fp32 PTQ int8 QAT int8 524 860(↑64%) 817 37.1*32=1187 64.5*32=2064(↑73.9%) 64.4*32 = 2060(↑73.8%) FP32\\ptq\\qat 三种模型的对比指令如下: trtexec --onnx=resnet_50_fp32_bs1.onnx trtexec --onnx=resnet_50_fp32_bs32.onnx trtexec --onnx=resnet_50_ptq_bs1_data-num512_percentile_91.03%.onnx --int8 trtexec --onnx=resnet_50_ptq_bs32_data-num512_percentile_91.03%.onnx --int8 --saveEngine=resnet_ptq_int8.engine trtexec --onnx=resnet_50_qat_bs1_93.43%.onnx --int8 trtexec --onnx=resnet_50_qat_bs32_93.43%.onnx --int8 注1:在使用trtexec时,看到了这样的日志,随后尝试了加上 --fp16,整体吞吐量又提升了~40%(相较于fp32提高140%),这或许是将fp32变为fp16带来的效率提升? [09/29/2023-16:20:37] [I] FP32 and INT8 precisions have been specified - more performance might be enabled by additionally specifying --fp16 or --best trtexec --onnx=resnet_50_qat_bs32_93.43%.onnx --int8 --best trtexec --onnx=resnet_50_qat_bs32_93.43%.onnx --int8 --fp16 注2:在新电脑里又看到了这样的warning,尝试使用--useCudaGraph ,可以提高吞吐。尝试之后,发现吞吐从1200 提升到了1600,看来trtexec里的提示信息非常有帮助! [04/04/2024-20:55:04] [W] Throughput may be bound by Enqueue Time rather than GPU Compute and the GPU may be under-utilized. [04/04/2024-20:55:04] [W] If not already in use, --useCudaGraph (utilize CUDA graphs where possible) may increase the throughput.* 小结 本节回顾了QAT和PTQ的区别,同时介绍了QDQ(伪量化算子:量化和反量化)在训练、推理时的变化,最后通过代码实现QAT,并对比了性能和效率变化。 到这里,量化的基础就结束了,模型量化的内容远不止这些,模型量化还有更高级的内容,例如逐层精度损失分析、逐层插入/取消量化节点、自定义量化算法。 想要深入研究模型加速/量化的朋友,可以进一步学习其它资料,这里推荐一些学习量化用到的资料: TRT int8量化文档:https://docs.nvidia.com/deeplearning/tensorrt/developer-guide/index.html#working-with-int8 TRT范例代码:https://github.com/NVIDIA/TensorRT/blob/main/quickstart/quantization_tutorial/qat-ptq-workflow.ipynb pytorch-quantization:https://github.com/NVIDIA/TensorRT/tree/main/tools/pytorch-quantization nvidia int8 QAT量化介绍:https://developer.nvidia.com/blog/achieving-fp32-accuracy-for-int8-inference-using-quantization-aware-training-with-tensorrt/ yolov6 量化工程范例:https://tech.meituan.com/2022/09/22/yolov6-quantization-in-meituan.html 老潘的TRTv8量化入门博客:https://zhuanlan.zhihu.com/p/479101029 B站量化系列教程-ZOMI酱:https://www.bilibili.com/video/BV1VD4y1n7AR/?spm_id_from=333.788&vd_source=19b783e279d4d4ceff5b927f27ea6aa3 B站量化系列教程-手写AI:https://www.bilibili.com/video/BV18L41197Uz/?spm_id_from=333.337.search-card.all.click&vd_source=19b783e279d4d4ceff5b927f27ea6aa3 MIT的TinyML-第六节:MIT-TinyML-Lec06-Quantization-II 下一小节,将利用本章学习的TensorRT知识,进行YOLOv8的量化实践,学习优秀开源代码的工程化过程。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "},"chapter-12/12.9-trt-deploy.html":{"url":"chapter-12/12.9-trt-deploy.html","title":"12.9 TensorRT Python 工程化","keywords":"","body":"12.9 TRT python 工程化 前言 在生产应用中,需要将trt的engine封装为类,内部实现engine初始化、推理的内存申请和释放,host与device的数据迁移,对外提供推理接口。 这样,算法推理功能才能优雅地嵌入到整个生产流程的代码中,供上下游调用。 本节将参考多个代码案例,总结了基于context.execute_v3的trt模型类编写。 两种推理方式——context 在前面介绍trt的python代码推理时,执行推理采用的是execute_async_v3,在不少案例教程中使用的是execute_v2,这将导致代码无法复用。 为了了解execute_v2和execute_async_v3的区别,下面将会介绍: 两者差异 基于yolov5代码,以及Nvidia官方代码,分析execute_v2的机制 两者差异 def execute_v2(self, bindings, p_int=None): # real signature unknown; restored from __doc__ \"\"\" execute_v2(self: tensorrt.tensorrt.IExecutionContext, bindings: List[int]) -> bool Synchronously execute inference on a batch. This method requires a array of input and output buffers. The mapping from tensor names to indices can be queried using :func:`ICudaEngine.get_binding_index()` . This method only works for execution contexts built from networks with no implicit batch dimension. :arg bindings: A list of integers representing input and output buffer addresses for the network. :returns: True if execution succeeded. \"\"\" def execute_async_v3(self, stream_handle): # real signature unknown; restored from __doc__ \"\"\" execute_async_v3(self: tensorrt.tensorrt.IExecutionContext, stream_handle: int) -> bool Asynchronously execute inference. Modifying or releasing memory that has been registered for the tensors before stream synchronization or the event passed to :func:`set_input_consumed_event` has been triggered results in undefined behavior. Input tensors can be released after the :func:`set_input_consumed_event` whereas output tensors require stream synchronization. :arg stream_handle: The cuda stream on which the inference kernels will be enqueued. \"\"\" 根据函数注释,可知execute_v2接收的是device地址,即要求数据已经变到gpu上,执行execute_v2时,仅仅实现gpu的显存数据读取与运算。 根据函数注释,可知execute_async_v3接收的是cuda流的编号,即默认通过该cuda流进行运算(具体含义并不熟,需要深入了解cuda编程,这里猜想是默认用的0),这就要求context知道从哪里取输入数据进行推理,会有额外的context.set_tensor_address(l_tensor_name[0], d_input) , d_input是显存地址。 YOLOv5 TRT推理 关于execute_async_v3,可回顾本章第二节的工作流程梳理。 为了解基于execute_v2进行推理时,需要进行的操作流程,这里分析YOLOv5官方代码。 yolov5支持多种backend的推理,方式在链接中也给出了详细步骤,这里不再重复。 # 1. 导出trt模型 python export.py --weights best.pt --data data/mydrone.yaml --include engine --device 0 # 2. 推理 python detect.py --weights best.engine --data data/mydrone.yaml --source G:\\虎门大桥车流\\DJI_0049.MP4 核心代码在detect.py和common.py中,其中实现了基于TRT的engine推理代码,这里主要关注模型初始化和模型推理两部分。 模型初始化核心代码: yolov5-master/models/common.py 中的DetectMultiBackend的elif engine部分: 通过bindings字典管理输入输出数据,包括数据的'name', 'dtype', 'shape', 'data', 'ptr'。 数据的地址ptr指在device(GPU)上的地址!这里基于torch库对数据做操作,并实现数据搬迁到GPU。这点与我们手动写推理不一样! execute_v2需要输入的地址,恰恰是GPU地址,因此可借助torch的to(device)方法得到GPU上数据的地址。 Binding = namedtuple('Binding', ('name', 'dtype', 'shape', 'data', 'ptr')) logger = trt.Logger(trt.Logger.INFO) with open(w, 'rb') as f, trt.Runtime(logger) as runtime: model = runtime.deserialize_cuda_engine(f.read()) context = model.create_execution_context() bindings = OrderedDict() output_names = [] for i in range(model.num_bindings): name = model.get_binding_name(i) dtype = trt.nptype(model.get_binding_dtype(i)) if model.binding_is_input(i): pass # 略 else: output_names.append(name) shape = tuple(context.get_binding_shape(i)) im = torch.from_numpy(np.empty(shape, dtype=dtype)).to(device) bindings[name] = Binding(name, dtype, shape, im, int(im.data_ptr())) binding_addrs = OrderedDict((n, d.ptr) for n, d in bindings.items()) batch_size = bindings['images'].shape[0] # if dynamic, this is instead max batch size 模型推理核心代码 yolov5-master/models/common.py 中的DetectMultiBackend的forward函数的elif self.engine部分: self.binding_addrs['images'] = int(im.data_ptr()) # 设置输入数据的GPU地址,im已经是在GPU上的tensor了 self.context.execute_v2(list(self.binding_addrs.values())) # 传入数据的GPU地址,执行推理 y = [self.bindings[x].data for x in sorted(self.output_names)] # 完成推理后,到输出张量(已在GPU上)取推理结果 小结:由此可知道yolov5的trt推理,借助了torch库将cpu上的数据与gpu上的数据进行关联 这种方式仍旧依赖pytorch,为此需要进一步探究手动管理gpu显存时,如何基于execute_v2进行推理,难点在于如何获得gpu上显存的地址。 Nvidia官方案例代码 代码位于:https://github.com/NVIDIA/TensorRT/blob/78245b0ac2af9a208ed02e5257bfd3ee7ae8a88d/samples/python/detectron2/infer.py 这里仅观察输入给execute_v2的数据如何得来,为此,代码倒着来看。 1. 在infer函数中: self.context.execute_v2(self.allocations) 2. 观察init中 self.allocations的定义 shape = self.engine.get_binding_shape(i) size = np.dtype(trt.nptype(dtype)).itemsize for s in shape: size *= s allocation = common.cuda_call(cudart.cudaMalloc(size)) self.allocations.append(allocation) 由此可知,传入execute_v2的地址是通过cudart.cudaMalloc(size)获取的,这个在第二节中也采用了这个方式获取GPU上数据的地址。 size则是通过shape和单个数据大小乘积得到。 通过两个开源代码分析,发现execute_v2还是需要手动管理显存,为此接下来还是基于execute_async_v3进行推理类的编写。 原因有两个: 第一,本章第二节就是基于execute_async_v3的流程介绍数据在host和device之间是如何传输,流转的。 第二,Nvidia官方教程中基于TRT 8.5给出了一个较好的host、device数据管理方法,代码简洁易理解。- 推理类的面向对象设计 在工程化时,算法模块对外提供推理函数,供主流程调用,因此需要将算法模块封装为类,并将相关属性和方法在类中实现,便于管理。 在推理类中,为了实现推理,首先需要构建context,而构建context所需要的过程步骤,均放到init函数中实现,同时配置模型相关的参数,如类别名称,阈值,mean/std等。 根据ResNet50模型的特点,TensorRTInfer的UML类图设计如下: 主要包括对外提供推理接口inference,围绕推理过程,实现预处理,TRT模型推理,后处理,可视化,TRT模型初始化等函数功能。 配套完整代码在这里,代码中实现的推理类已可以完成独立功能,但仍有更进一步优化点,包括: 组batch推理模式,实现更高吞吐量 预处理模块以batch形式处理,提高数据预处理效率 预处理模块放置GPU进行处理,均衡CPU和GPU的负载 采用分布式队列机制,解耦任务调用,任务处理,可参考celery库 对于C/S架构的设计,需要用web服务包装算法,常用的有flask, fastapi, django,对于一般工程,可用flask, fastapi,复杂工程用django。 更多TRT模型类参考: TRT官方-simpledemo TRT官方-detectron2-infer YOLOv6官方 engine常用方法/属性 在上述TRT模型构建中,使用了engine中一系列方法和属性,这里简单总结一下。 engine.num_io_tensors: 获取输入、输出数据个数 engine.get_tensor_name(index):根据数据的顺序,获取名称,这个名称是onnx导出时设置的 engine.get_tensor_mode(tensor_name):根据数据名称,获取是Input还是output类型。 小结 本结整理TRT在python下推理的代码编写,包括对比execute_v2与v3的差异,并实现了推理类的设计与实现。 对于TensorRT的学习及使用,暂时告一段落,通过本章内容,可以将pytorch模型转换为trt模型,并在python中实现高效推理,同时了解TensorRT常用工具的使用。 对于一般场景,本章的TensorRT工具及量化内容可以满足,但对于边缘端、高性能部署,仍需精进模型加速知识。 Copyright © TingsongYu 2021 all right reserved,powered by Gitbook文件修订时间: 2024年04月26日21:48:10 "}} \ No newline at end of file