DIYgod / RSSHub

🧡 Everything is RSSible
https://docs.rsshub.app
MIT License
31.02k stars 6.92k forks source link

抓取知乎存在pubdate错误 #15400

Closed domonnss closed 2 months ago

domonnss commented 2 months ago

路由地址

/zhihu/posts/:usertype/:id

完整路由地址

http://{selfhost}/zhihu/posts/people/pang-zi-qi-40

相关文档

https://docs.rsshub.app/social-media.html#zhi-hu-yong-hu-wen-zhang

预期是什么?

文章发表时间正确

实际发生了什么?

文章时间显示错误均为1970年1月20日

部署

自建

部署相关信息

OS:ubuntu20.04,node:v20.9.0,Docker:25.0.0-beta.1

额外信息

时间显示错误,用docker-compose部署,将镜像版本更换为diygod/rsshub:2024-03-23,问题解决

这不是重复的 issue

github-actions[bot] commented 2 months ago
Searching for maintainers:

To maintainers: if you are not willing to be disturbed, list your username in scripts/workflow/test-issue/call-maintainer.js. In this way, your username will be wrapped in an inline code block when tagged so you will not be notified.

If all routes can not be found, the issue will be closed automatically. Please use NOROUTE for a route-irrelevant issue or leave a comment if it is a mistake. 如果所有路由都无法匹配,issue 将会被自动关闭。如果 issue 和路由无关,请使用 NOROUTE 关键词,或者留下评论。我们会重新审核。

TonyRL commented 2 months ago

/test

/zhihu/posts/people/pang-zi-qi-40
github-actions[bot] commented 2 months ago

Successfully generated as following:

http://localhost:1200/zhihu/posts/people/pang-zi-qi-40 - Success ✔️ ```rss 庞子奇 的知乎文章 https://www.zhihu.com/people/pang-zi-qi-40/posts UIUC CS PhD, PKU CS - Made with love by RSSHub(https://github.com/DIYgod/RSSHub) RSSHub i@diygod.me (DIYgod) en Sat, 04 May 2024 06:41:27 GMT 5 [ICLR 2024 (Spotlight)] LLM里的Transformer还可以这么用? <p data-pid="8QgFV3iF">宣传一下最近的新工作,个人感觉是读博以来做得最难最累但是成就感也最大的一个项目。它起源自一个很简单的问题——自LLM诞生以来,我们见到了很多把LLM接到Vision Backbone后面的算法,那么有两个自然的问题:</p><ul><li data-pid="HUz5DMvl"><b>LLM的Transformer是否可以直接处理视觉Token</b>?</li><li data-pid="TEUE4kyi"><b>LLM的Transformer是否可以提升处理视觉Token的Performance</b>?</li></ul><p data-pid="qyhbgEzX">我们的工作回答了这两个问题 (答案是Yes) 而且解释了其中的原因:<i><b>在语言模型中Pretrain的Transformer可以用作视觉任务的Encoder Layer</b></i>。代码已经开源,欢迎大家点赞关注我们的Paper和GitHub。</p><figure data-size="normal"><img src="https://pic4.zhimg.com/v2-1bef71c8b028e2f6f7d34366634b8433_r.jpg" data-caption="" data-size="normal" data-rawwidth="846" data-rawheight="206" class="origin_image zh-lightbox-thumb lazy" width="846" data-original-token="v2-c9ac193e0dd91babbe5b0c2569282ee6" referrerpolicy="no-referrer"></figure><a href="https://link.zhihu.com/?target=https%3A//arxiv.org/abs/2310.12973" data-draft-node="block" data-draft-type="link-card" data-image="https://pic4.zhimg.com/v2-4baaae2386ede0213c693947a141a747_qhd.jpg" data-image-width="1200" data-image-height="700" class=" wrap external" target="_blank" rel="nofollow noreferrer">Frozen Transformers in Language Models Are Effective Visual Encoder Layers</a><a href="https://link.zhihu.com/?target=https%3A//github.com/ziqipang/LM4VisualEncoding" data-draft-node="block" data-draft-type="link-card" class=" external" target="_blank" rel="nofollow noreferrer"><span class="invisible">https://</span><span class="visible">github.com/ziqipang/LM4</span><span class="invisible">VisualEncoding</span><span class="ellipsis"></span></a><h2>1. LLM的Transformer可以处理视觉Token吗?</h2><p data-pid="xnzPPCYW">在LLM的加持下,很多Vision-language Model 会直接把来自图像的Embedding输入给LLM,并让LLM作为Decoder输出文字、类别、检测框等。但是在这些模型中,LLM并不会直接处理来自图像的Token,它们更多地是 (1) <i>处理提前设计好的语义Token</i>,例如CLIP中的cls token;(2) <i>处理被压缩过的Token</i>,例如BLIP里面经过information bottleneck的token。<b>那么LLM是否可以直接作用于其它模态的Token呢,即LLM是否可以用作Encoder,而不只是Decoder呢?</b></p><h3><b>1.1 实验方法</b></h3><p data-pid="3Moigc_i">验证这个事情非常简单,以ViT为例,我们只需要:</p><ul><li data-pid="fZqJ0OY6">取出某一个LLM的Transformer Layer (例如LLaMA的最后一个Transformer),<b>请注意这里只需要一个Transformer Block而不是整个LLM</b>;</li><li data-pid="Jfo5F2qT">把它加入到最后一个Encoder Block后面,只需要额外两个Linear Layers把Feature Dimensions拉齐;</li><li data-pid="UMc9AiwT">冻结LLM的Transformer,但是正常训练其它部分。</li></ul><figure data-size="normal"><img src="https://pic2.zhimg.com/v2-965d5f4abcbbbc99934e41d55a90d685_r.jpg" data-size="normal" data-rawwidth="3765" data-rawheight="1388" class="origin_image zh-lightbox-thumb lazy" width="3765" data-original-token="v2-04562aad59ba6a67fbe556f4965951df" referrerpolicy="no-referrer"><figcaption>以ViT为例,我们的模型结构非常简单,只需要额外两个线性层</figcaption></figure><h3>1.2 和现在的Vision-language Model的异同</h3><ul><li data-pid="omnyT3Dx"><b>是否需要Pretraining</b>?我们的方法重在提升Encoding能力,所以我<i>们<b>既支持Train-from-scratch</b>,<b>也支持Finetune</b>,</i>而不是必须要依赖预训练好的Backbones。</li><li data-pid="SXi_70pB"><b>是否需要Language</b>?虽然我们用的是LLM的Transformer,但是<i>我们的Framework<b>独立于使用Language</b></i> (比如Prompts或者Alignment),而不是像Vision-language Models一样必须要Language。</li><li data-pid="PdG3Zlwj"><b>可以处理多少模态</b>?我们的<i>Framework<b>可以泛化到多个模态和任务</b></i>,而不是只能处理图像。</li><li data-pid="8-AHutUk"><b>Encoder和Decoder有什么区别</b>?Encoder需要<b><i>直接和Visual tokens打交道</i></b>,比如和<img src="https://www.zhihu.com/equation?tex=H%5Ctimes+W" alt="H\times W" eeimg="1" referrerpolicy="no-referrer"> 个图像token的信息做Cross-attention去改变cls token。</li><li data-pid="oK1yo1zu"><b>现在已经有这么多Vision-language Models了,你们的研究有什么用</b>?首先,我们的研究和现在的vision-language Models不矛盾而且互相补充——现在vision-language model研究如何把视觉embedding输入给LLM,而<b><i>我们的研究聚焦如何提供更好的embedding</i></b>。</li></ul><h3>1.3 一个预训练的LLaMA Transformer在<i>许多不同模态、任务</i>的Encoder上都有用</h3><p data-pid="3YdUz3Ef">在论文中,我们发现把LLM的Transformer用作视觉Encoder可以泛化到极其多样的场景。</p><ul><li data-pid="7Ra6YFHl">2D语义:图像分类 (<b>image classification</b>)</li><li data-pid="A571tNNZ">点云:点云分类 (<b>point cloud classification</b>)</li><li data-pid="VROJ45cm">视频:动作识别 (<b>action recognition</b>)</li><li data-pid="aZL7Aj0P">无语义,回归任务:轨迹预测 (<b>motion forecasting</b>)</li><li data-pid="C_FB8JOy">2D多模态:2D VQA和图像搜索 (<b>2D VQA and Retrieval</b>)</li><li data-pid="ULcIkEJI">3D多模态:<b>3D VQA</b></li></ul><p data-pid="lQEM5rDR">在这些任务中,我们的模型不只要处理<i>图像上像patch一样的Token</i>,还要处理</p><ul><li data-pid="shI69dD3">点云中无规则的3D点</li><li data-pid="0H4k7i6j">视频中形状是 <img src="https://www.zhihu.com/equation?tex=T%5Ctimes+H%5Ctimes+W" alt="T\times H\times W" eeimg="1" referrerpolicy="no-referrer"> 的长方体形状的token</li><li data-pid="7UJqzQOk">轨迹预测里面来自Agent和高精地图的Polylines</li><li data-pid="HEbCJ5SC">多模态任务中混合了图像和语言的Token</li></ul><figure data-size="normal"><img src="https://pic2.zhimg.com/v2-2d35530e82d1f5e243401660c50014b5_r.jpg" data-size="normal" data-rawwidth="544" data-rawheight="119" class="origin_image zh-lightbox-thumb lazy" width="544" data-original-token="v2-2bb84d5659fb0c7903c304d6761a92ae" referrerpolicy="no-referrer"><figcaption>ImageNet, 图像分类</figcaption></figure><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-e994001b52e3dec34796a2834f3638b8_r.jpg" data-size="normal" data-rawwidth="707" data-rawheight="119" class="origin_image zh-lightbox-thumb lazy" width="707" data-original-token="v2-1493a75a4e098782940788e649a580d0" referrerpolicy="no-referrer"><figcaption>2D/3D 语言多模态任务</figcaption></figure><figure data-size="normal"><img src="https://pic4.zhimg.com/v2-bf3b9ada9d8f543ed95f4c061c2fdf97_r.jpg" data-size="normal" data-rawwidth="1125" data-rawheight="186" class="origin_image zh-lightbox-thumb lazy" width="1125" data-original-token="v2-bf3b9ada9d8f543ed95f4c061c2fdf97" referrerpolicy="no-referrer"><figcaption>自动驾驶,轨迹预测</figcaption></figure><h2>2. 为什么预训练的LLM Transformer有用:Information Filtering Hypothesis</h2><p data-pid="q6iXoYGm">虽然我们在许多任务和模态上都看到了性能的提升,但是如何解释这一点呢?我们在研究的过程中感觉如果把加了LLM的提升都归结于"LLM包含了可以泛化的知识",其实比较偷懒而且不一定正确。所以我们研究了Token在加LLM transformer前后的变化提出了Information Filtering假设:</p><p data-pid="N5ypxBJh"><i><b>LLM Transformer模块能够在训练的过程中筛选和目标任务相关的Visual Tokens并且放大他们的贡献。</b></i></p><p data-pid="xoQft8N9"><i>这个结论是我们paper里面可能最重要的发现。</i></p><h3>2.1 在ViT上的观察 —— LLM Transformer筛选出了前景</h3><p data-pid="J1D3q5qW">为什么可以这么说呢?我们看下图中我们对ViT的Token Activation的可视化:为了体现不同Token的贡献,我们从本身<i>Activation的大小</i>(L2-norm)和<i>频率大小</i>进行了可视化(做傅里叶变换后算角度的L2-norm)。</p><p data-pid="FIeyHH9-">可以看到:<b>在有了LLM Transformer之后,ViT的Activation能更干净地集中到前景区域</b>,而这个性质只有在无监督学习的ViT中(e.g. DINO)中可以见到,在监督学习的ViT中很少见。</p><p data-pid="EeLsdJfL">另一方面,我们对比了有/没有LLM transformer对于Attention weight的影响:普通的ViT的Attention Weight几乎是完全Noisy的 (和DINO的观察吻合),在加了LLMTransformer之后 (1) 有极少的Attention Head体现出了干净的前景分割的样子,但是 (2) 它们的数量较少不足以解释Token Activation更显著地好。</p><p data-pid="oW7UEK3V">因此,我们观察到的提升来自有用的Feature被放大了,这也是为什么我们称之为<i><b>information filtering hypothesis</b></i>。</p><figure data-size="normal"><img src="https://pic2.zhimg.com/v2-398f58e03a3a7d0a0469d0025a3ad865_r.jpg" data-caption="" data-size="normal" data-rawwidth="1499" data-rawheight="683" class="origin_image zh-lightbox-thumb lazy" width="1499" data-original-token="v2-c60373554afd5a426c3c98ce7dd7df10" referrerpolicy="no-referrer"></figure><h3>2.2 在其它任务的也可以筛选有用的Token</h3><p data-pid="riNRp8b9">类似的“<b><i>information filtering</i></b>”现象不只在ViT和图像分类上有,<b>在其它任务上,LLM Transformer也有效地提升了对目标任务最有用的Token</b>。这里我们举两个例子:</p><ul><li data-pid="-f8Foxhz">在<b>动作识别</b>中,加了LLaMA的Transformer可以更好地集中到前景的手和物体(low threshold),也更多地筛选出了手和物体有实际动作的帧(high threshold)。</li></ul><figure data-size="normal"><img src="https://pic4.zhimg.com/v2-802573b02995ff1afcd5302754f39a3b_r.jpg" data-caption="" data-size="normal" data-rawwidth="1186" data-rawheight="548" class="origin_image zh-lightbox-thumb lazy" width="1186" data-original-token="v2-706e7c113a3a342676539af13f65c297" referrerpolicy="no-referrer"></figure><ul><li data-pid="wg8qUmrT">在<b>3D VQA</b>中,我们可视化了点云Token的大小。可以看到,那些真正和预测目标、或者问题相关的点得到了更大的关注:比如在左图中,"behind me"的点云显著得到了更大的Activation (颜色更亮了)。</li></ul><figure data-size="normal"><img src="https://pic4.zhimg.com/v2-4bfa5088ef4b999ccf83cf41cb56142f_r.jpg" data-caption="" data-size="normal" data-rawwidth="1535" data-rawheight="469" class="origin_image zh-lightbox-thumb lazy" width="1535" data-original-token="v2-0ad055633b71ccd460c2c46818c7bc10" referrerpolicy="no-referrer"></figure><h2>3. 一点Ablation Study</h2><p data-pid="xHctGh6-">那么我们观察到的现象,即LLM的Transformer可以提升Visual Encoding,是否和不同的层、LLM有关呢?</p><ul><li data-pid="jqyFr203"><b>多种LLM Transformer都可以提升Visual Encoding</b>。例如用LLaMA和OPT的不同Transformer层都会有提升,而且不同层之间也会体现不同的规律。</li></ul><figure data-size="normal"><img src="https://pic4.zhimg.com/v2-ffcc774126732385cc1ff2122d0ce4af_r.jpg" data-caption="" data-size="normal" data-rawwidth="1489" data-rawheight="433" class="origin_image zh-lightbox-thumb lazy" width="1489" data-original-token="v2-5c57f930aa4270378200061a4b9b2a3c" referrerpolicy="no-referrer"></figure><ul><li data-pid="UUPJ-B4k"><b>只有足够大的LLM才有提升Visual Encoding的效果</b>。例如只有足够大的OPT才会提升Visual Encoding的效果。</li></ul><figure data-size="normal"><img src="https://pic3.zhimg.com/v2-89ba4169a625aa55ec41ce9fb881a116_r.jpg" data-caption="" data-size="normal" data-rawwidth="943" data-rawheight="220" class="origin_image zh-lightbox-thumb lazy" width="943" data-original-token="v2-256395ca6503f029cfa57acd51effcfe" referrerpolicy="no-referrer"></figure><h2>4. 后记</h2><p data-pid="CfTEJy6P">最后写一些没有写在Paper里面的自己的感受和思考:</p><ul><li data-pid="Uxw4ZZsH">在论文中最让我感到兴奋的不是结合了LLM在很多Task上都有提升,而是在我们Information filtering假设的分析中看到了质变:<b>神经网络能够更好地学习到那些和任务最相关的Token。</b></li><li data-pid="rWqOapGF">那么为什么会有这样的效果?<b>我猜测是LLM的Transformer的参数矩阵,例如FFN的矩阵,有一些很好的性质,例如在某些情况下是一个高通滤波器</b>。我们可以从反面思考,如果一个参数矩阵是随机初始化(低通滤波器),或者干脆就是一个单位矩阵,那么必然不可能去筛选出来有用的Token,并且放大他们的贡献。</li><li data-pid="qjpSQtkJ">在尝试解释这个现象的时候,我们发现用transfer learning的工具来分析会非常有难度,因为我们不能保证vision和language确实在一层transformer之后就align了。最终,一个比较合理的直觉是受到了我本科同学许逸伦"<a href="https://link.zhihu.com/?target=https%3A//arxiv.org/abs/2002.10689" class=" wrap external" target="_blank" rel="nofollow noreferrer">A Theory of Usable Information Under Computational Constraints</a>"这篇Paper的启发:我们可以<b><i>把LLM Transformer看作一种Decipher,它提升了Feature的有用性,使得一层MLP或者Decoder的有限计算资源可以把Feature映射到和真实结果Mutual Information更高的空间中</i></b>。事实上,这也契合我们Information filtering的观察。</li></ul><p data-pid="eSqpqzms">最后感谢我的Co-authors <a class="member_mention" href="https://www.zhihu.com/people/bfd3d0449bff1c4af0738317804445d2" data-hash="bfd3d0449bff1c4af0738317804445d2" data-hovercard="p$b$bfd3d0449bff1c4af0738317804445d2">@Ziyang Xie</a> <a class="member_mention" href="https://www.zhihu.com/people/474330a9a69bc70b086425219fa2cea4" data-hash="474330a9a69bc70b086425219fa2cea4" data-hovercard="p$b$474330a9a69bc70b086425219fa2cea4">@Yunze MAN</a> 。感谢 <a class="member_mention" href="https://www.zhihu.com/people/2c278d1efa03911629c0c4d391acabe3" data-hash="2c278d1efa03911629c0c4d391acabe3" data-hovercard="p$b$2c278d1efa03911629c0c4d391acabe3">@Baifeng</a> 提供了很多技术支持,他的Paper <a href="https://zhuanlan.zhihu.com/p/612475689" class="internal">AbsViT</a> 非常有insight启发了我们的分析,也感谢Shoufa Chen同学提供了很多模型训练的指导。</p><p data-pid="zDuajfTw">最后再放一个GitHub链接</p><a href="https://link.zhihu.com/?target=https%3A//github.com/ziqipang/LM4VisualEncoding" data-draft-node="block" data-draft-type="link-card" class=" external" target="_blank" rel="nofollow noreferrer"><span class="invisible">https://</span><span class="visible">github.com/ziqipang/LM4</span><span class="invisible">VisualEncoding</span><span class="ellipsis"></span></a><p></p> http://zhuanlan.zhihu.com/p/663023589 http://zhuanlan.zhihu.com/p/663023589 Wed, 01 Nov 2023 02:49:35 GMT 毕业之后的一年探险 <p data-pid="F1zYM1-Q">今天(按照美国时间)是我的生日,正好简单讲讲过去的一年的经历,特别是我自己体会最深的几点。去年概括起来主要就是三件事情:1. 在TuSimple做了和自动驾驶算法相关的实习;2. 在此期间申请到了UIUC的CS PhD;3. 闲暇的时候参加了“登月工坊”认识了一些很有水平、有志于创业的同学。这一年其实我的变化很大,感觉自己现在的心态又回到了高三的时候——虽然未来充满了不确定性,但是感觉自己走在正确的路上;虽然清楚地知道未来有各种各样的困难,但是也有充分的信心自己可以把问题都逐渐解决掉。</p><p data-pid="bPFpaI1y">因为最近时间特别紧张,所以肯定有语句不通顺的地方,还望海涵~</p><h2>1. 经历的流水账</h2><p data-pid="i160FAWv">先说第一件事情。我在2020年年初还没有想好是否要读PhD,所以申请了一水的Research Oriented Master项目,拿到了CMU的Offer,同时计划着Gap一年到Industry实际体验一下。我当时的计划其实很简单:1. 争取去一个中小公司,能看到研发到落地的整个过程;2. 在公司里可以看到Cutting-edge的R&amp;D。于是顺理成章地投了TuSimple的自动驾驶算法实习生,并且幸运地被Naiyan Wang <a class="member_mention" href="http://www.zhihu.com/people/f5911fddc7fa5fd74a80d5ce2c12e1a2" data-hash="f5911fddc7fa5fd74a80d5ce2c12e1a2" data-hovercard="p$b$f5911fddc7fa5fd74a80d5ce2c12e1a2">@Naiyan Wang</a> 认可加入了算法团队。站在现在的角度来看,我可以说加入TuSimple是我上大学五年来做过Top-2正确的决定。在这一年里,我发表了人生第一篇顶会论文,身边的Senior Engineers也是我迄今见过的最勤奋踏实的一群人,我也通过技术团队每天解决问题的过程、公司上市的契机对于“创业”和“做技术”有了真正的实感。</p><p data-pid="-WlsvTiO">因为在TuSimple做得过于开心,从自己科研--落地的过程中体会到了“研究如何转化成为真正有意义的工程产品,”所以在2020年的申请季全部申请了PhD,最后以来到了UIUC读CS PhD。在此期间,也发生了很多有意思的事情,比如我的托福口语考了满分30分。(详见这篇文章)</p><a href="https://zhuanlan.zhihu.com/p/364021736" data-draft-node="block" data-draft-type="link-card" data-image="https://pica.zhimg.com/v2-d87a2800b769bc69b7f38902ef76e577_qhd.jpg?source=d16d100b" data-image-width="697" data-image-height="432" class="internal">庞子奇:托福从Fair到Good——口语满分的经验之谈</a><p data-pid="hfLOMgHU">虽然在读CS PhD,但是我暂时的长远的目标还是放在工业界上面——希望以后能够做出来有价值的产品——于是就报名参加了一些美本同学们组织的青年人创业类活动“登月工坊。”登月工坊的形式类似于Hackerthon,每个组的同学需要在3天的时间里开发出一个Potentially可以创业的产品Prototype。这个活动首先通过任务驱动的方式让我在三天之内学会了写网页和前端(笑哭),这也是我最开始参加这个活动的目的之一。最主要的,还是凭借着活动组织者和同组同学的很多努力,我能够跳出了自己所在的技术圈子,看到了许多优秀的做创业的年轻人,对于我未来可能想在PhD和Industry做的东西产生了很大的影响。这里要引用一句“原则”里的话:努力工作是为了和更优秀的、你更喜欢的人成为朋友。</p><h2>2. Lesson 1: 自顶向下和自底向上 Bottom-Up and Top-Down</h2><p data-pid="gweCiraB">在和做金融、咨询的同学讨论的时候,他们经常会提到一个词——“框架,”在公司和Mentor讨论问题的时候,他们经常有一针见血指出问题关键的能力,里面的关键其实也是框架。框架就是自顶向下,像正午的太阳照金字塔一样能把每一块露在外面的砖都给弄得明明白白。合理地使用框架,可以快速全面地解决问题,所以学到的第一点就是:拿到一件事情不要急着解决,先想想它的Big Picture或者问题的全貌,自己的脑子里是不是已经有了一个全面的解决方案。</p><p data-pid="dyhxnhlZ">但是不可能每件事情都有框架,特别我们自己在科研、做新东西的时候更是如此。此时就有了自底向上——也就是怎么通过一些离散的数据点总结出一套框架出来、或者用之前的框架改一套框架出来。这里举两个例子。在登月工坊的时候,需要在三天的时间里写出来一个Javascript做数据可视化的Demo,但是我之前并不会前端相关的技术,所以就要通过把之前自己学习编程语言和计算机知识建立的知识框架迁移到Javascript上。在做研究或者做工程问题的时候,一般都是会先注意到一个反常的数据点,那么这个数据点Indicate了怎样的事情呢?就需要去仔细地设计更多的实验,把一个数据点扩充成一个框架了。所以学到的第二点是:你是一个Engineer,拿到一些表面现象的数据点一定不要懵逼,要学会设计实验、找合适的人提问题把整个框架摸清楚。</p><p data-pid="p7CIJFnX">最后一点是在Bottom-Up和Top-Down之间做好的平衡,要不断地切换这两种思维的方式。如果习惯了直接输入框架、直接用框架思考,很容易就失去了“Bottom-Up”这种做侦探的能力,遇到Novel一些的问题也很难处置,遇到原本知识范围之外的问题也会Overconfident;如果习惯Bottom-Up,一方面效率会不高,而且往往囿于细节,没有了建筑师胸有楼宇的视野。这也是我现在还在学习的。</p><h2>3. Lesson 2: 把事情想清楚</h2><p data-pid="XC3JPVfM">“把事情想清楚”是我看“详谈:左晖”这本书想到的一点。这里的“想清楚”指的是:能用简短的话在当下的语境中把一件事最要命的东西点出来。现在很多人模仿毛邓的语言风格其实就是在模仿这一点。那么“想清楚”的反面是什么呢?我们经常会和别人讨论问题,问:“xxx是为什么 / xxx是什么?”如果对面没有想清楚,那么你会听到连续的“比如说”“比如说”“比如说。”</p><p data-pid="we4E2olc">诚然,有很多事情其实不太好想清楚,我要是能想清楚我的科研Idea为啥不Work不也就没有这个问题了吗。所以这里评判自己想没想清楚一个事情的原则就是:自己表达一遍,查看是否有模糊的地方。在这段表达中,你可以讲自己没有解决的问题,但是一定要尝试思考——这个没有解决的问题可能是由于xxx等几种原因导致的,通过xxx实验、事实的调查也许可以验证。</p><p data-pid="S5fvYEJ9">在哲学、科学、工程里面,都非常讲究总结出能够泛化的经验和原则,这里的“原则”就是需要想清楚的事情。我感觉最恰切的还是让我当时想到这一点的那句话。左晖被问到链家的愿景是什么,他说:“做有尊严的服务者。”他可以有无数的例子,例如中介不要内部恶性竞争、中介不应该下跪寻求客户息事宁人、每一位工作者应该把做中介当成一件长期的事情——但是最后放在当时的语境下他总结为“做有尊严的服务者。”</p><h2>4. Lesson 3: Have Fun!!!</h2><p data-pid="nxbio3Es">过去的一年其实还挺累的,一定要在生活中找乐趣。我自己的乐趣一方面是每周末都和同学出去吃吃喝喝聊天玩耍。社交生活成为了我快乐的重要来源。但是更主要的还是要有一个好的心态。作为一位东北人,我一直相信每个人其实都有做Talk Show的天分。如果大家仔细品一品赵本山的小品,往往都是调侃着生活的悲剧,于是伤心的事情也就快乐起来了——这也是我自认为的东北喜剧的精神内核,也是我身边的老乡们都有的一种生活态度。</p><p data-pid="7AZneKQf">我在公司第一次投稿ICRA被拒稿了,当时已经是我连续第5次被顶会拒稿,而且我之前也从没有过Publication。于是我说:“我第一次、第二次被拒稿的时候想的是,努努力下回就有了;等到第五次的时候想的是,我以后要是混出来了这件事儿铁定能写到我的自传里。”</p><h2>5. Some Books / Medias / Apps Recommendation</h2><p data-pid="e_oVx-pS">过去的一年看了不少书/剧,也用了一些新的App,推荐一些好玩儿的。</p><p data-pid="WyPZ9g21">Books:</p><ul><li data-pid="kMpzG1NZ">凤凰项目(用小说介绍IT运维和公司管理,很多工具很有启发)</li><li data-pid="6SCrjlSP">The Ph.D. Grind (Ph.D. is just like a start up)</li><li data-pid="iyjjwkNG">详谈:左晖</li><li data-pid="WRPEY8nb">莫失莫忘(文学作品就是有这样的一种隐隐悲伤感觉)</li><li data-pid="ixoCA4tT">一生的旅程(作者很坦诚)</li><li data-pid="ofq-7U8P">大师和玛格丽特(布尔加科夫的想象非常瑰奇,值得一看)</li><li data-pid="_5-IwHER">大问题(哲学导论,把我的生活幸福等级拉高了一级)</li><li data-pid="yN6GhKtI">启示录:做用户喜欢的产品</li><li data-pid="ioypQVGT">信息简史(从信息论的数学本质入手,讲解了信息技术的发展史)</li><li data-pid="KjOyPTdu">视觉SLAM十四讲(解决了我好多技术问题)</li></ul><p data-pid="EVL_AiZQ">Dramas:</p><ul><li data-pid="EZL3bn8o">大明王朝1566</li><li data-pid="7x9nejpb">北平无战事</li><li data-pid="Afr54QqJ">善地</li><li data-pid="pVtaSc4i">Hamilton</li><li data-pid="kKLz6S2H">大鱼</li><li data-pid="NBzv1bbA">雷曼兄弟三部曲(NT Live)</li><li data-pid="XzXZ8-mn">伦敦生活(哈哈哈哈哈哈哈)</li><li data-pid="e5nqrzxC">Eliza Shlesinger(我爱死她的表演和段子了)</li></ul><p data-pid="elVY4vnk">APP:Flomo(随记)、Trello(看板)</p> http://zhuanlan.zhihu.com/p/410202879 http://zhuanlan.zhihu.com/p/410202879 Tue, 14 Sep 2021 03:51:07 GMT 托福从Fair到Good——口语满分的经验之谈 <p data-pid="xb7Vnj8D">最近申请PhD,遇到了Committee紧急对口语有超过24分的要求,所以在很短的时间里考了两次托福——大概就是“这周五报名,一周时间准备,下周末考试”的节奏。因为我之前的托福考试都没有特殊准备过,口语也一直都是24分,所以<b>如何在一周之内实现水平的突破</b>其实是一件很有压力的事情。<b>所幸我在一周的时间里面取得了一定的进步,甚至还运气不错地拿到了一次口语满分</b>。(成绩见下面两张图)</p><p data-pid="VqeAndOJ">我写作这篇文章的目的就是希望传授一些有益的经验,特别它们具有某种程度的稀缺性:</p><ul><li data-pid="IiOpsjJm">新东方等教育机构的学习中,因为需要以大部分学员为主要对象,所以往往只能实现“如何达到Fair的水平(托福里面的普通)?”,在大班课中很难把更高的层次作为主要目标。</li><li data-pid="QYygprSt">很多同学其实需要在口语和写作上需要达到更高的水平,也就是“<b>从Fair达到Good</b>”,但是因为上面所述的原因,一般很难找到人帮助自己实现进化。</li><li data-pid="C-Au2X6T">想要实现这种进阶的同学,<b>一方面需要合适的指导原则,另一方面也需要合适的训练</b>。而考试的结果证明,知道并且可以验证这两方面的人在考生中必定是少数。</li></ul><p data-pid="N22XoKPu">闲话少叙,我们在本篇文章中关注如下两点:</p><ul><li data-pid="v5FMflgF"><b>实现从Fair到Good,有哪些提升能力的方法?</b></li><li data-pid="ec80LJyn"><b>在备考之前有什么可以使用的技巧?</b></li></ul><figure data-size="normal"><img src="https://picx.zhimg.com/v2-ce25c28c71f730912643d544147d6909_720w.jpg" data-caption="" data-size="normal" data-rawwidth="768" data-rawheight="312" class="origin_image zh-lightbox-thumb lazy" width="768" referrerpolicy="no-referrer"></figure><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-882f1d4c4f47f2e3681e2c39c206b0a2_720w.jpg" data-size="normal" data-rawwidth="783" data-rawheight="320" class="origin_image zh-lightbox-thumb lazy" width="783" referrerpolicy="no-referrer"><figcaption>两次考试的分数(左侧为单次考试,右侧为最好分数拼合)</figcaption></figure><h2><b>1 概述</b></h2><p data-pid="znZHRQH9">对于非母语者,也就是要参加托福考试的你我而言,想要拿到“Good”的口语分数往往需要满足如下图的依赖关系:英语基础例如语法和词汇是听力、阅读的基础,在此基础上锻炼出好的输出能力就能拿到满意的写作分数,进一步这个过程如果变得自然流畅,才可以得到很好的口语分数。</p><p data-pid="EVN0bgo0">因此,因为<b>这篇文章的核心在于“口语和写作如何从Fair到Good”</b>,因此不会赘述关于阅读、听力提升的内容,而是重点关注——<b>当我已经有了较好的阅读、听力基础,如何提升自己的写作和口语?</b>因此,如果后面所讲述的经验可能对已经有很好阅读听力的人参考意义更好。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-87efc2e010ecee1010cd5d4548748dbe_720w.jpg" data-size="normal" data-rawwidth="697" data-rawheight="432" class="origin_image zh-lightbox-thumb lazy" width="697" referrerpolicy="no-referrer"><figcaption>学习路径</figcaption></figure><h2><b>2 我有阅读、听力、写作的基础,如何提升口语</b></h2><p data-pid="OjWRtke8">托福口语的要求不只是说话,而是【<b>根据一个主题,进行时长为一分钟左右的有逻辑的论述</b>】。因此,<b>托福口语只是形式上和“说话”相同,但是它考察的本质更接近于“写作”,只是在实时性上要求更高、内容要求更低。</b>基于这样的原则,我认为当写作本身就有了较好的基础时,<b>利用写作辅助口语的提升是最有效的方法</b>。(懂AI的人士可以联想一下Knowledge Distillation)</p><h3><b>2.1 提升口语水平</b></h3><p data-pid="APjohSUt">托福口语做得好的基础就是能够写出合适的书面表达,想要在考试时间内拿出好的口语表现需要在实时性、反应上强过写作,所以提升托福口语水平主要有如下几步,可以根据自己的水平选择从其中的某一步开始训练:</p><ol><li data-pid="4mKoZjns">通过写作练习<b>快速组织语言</b>的能力:针对TPO的口语题目,是否可以在<b>3-5min内一步到位</b>写出一个口语化的草稿或者提纲;针对一篇文章,是否可以在<b>3-5min内一步到位</b>写出一个口语化的Summary。<br>--&gt;解释:<i>很多同学往往可以写出很好的文章,但是需要很长的时间构思、修改语言。如上训练的目标就是缩短组织语言所需要的时间,尽可能达到:当我知道要写什么时,可以一步到位地写出合适的句子。</i></li><li data-pid="MN6JGtE-">练习<b>根据关键点和已有内容输出</b>的能力:在<b>1-2min</b>内,没有时间写完整的文字稿,那么<b>是否可以写出“半成品”的草稿</b>?<br>--&gt;解释:<i>在这里,我们希望达到两个目的:A. 巩固步骤1的成果,能够在短时间内写出来合适的内容;B. 熟悉自己的语言习惯,练习“<b>自动补全</b>”的能力,即看到一个句子的一部分、缺少连缀的句子,可以自己熟练地补全内容,脱离对完整文字稿的依赖。</i></li><li data-pid="LRc28QS8">练习<b>使用逻辑框架表达</b>的能力:托福题目往往都有固定的逻辑和模式,比如“介绍一个东西的道理”、“xxx理由是xxx概念的例子”,那么针对一个来自汉语的问题和它的两点主要内容,是否可以<b>熟练地组装成一段非常有逻辑的语言</b>。<br>--&gt;解释:<i>这部分要求用“说话”完成。此训练<b>首先要求能在汉语里面很快地想清楚表达逻辑</b>——很多同学往往认为是自己的英语水平不足导致口语有问题,殊不知汉语逻辑想不清楚才是首要的问题。当自己在汉语中可以熟练地<b>掌握了常用的逻辑结构之后,就可以迁移到直接用英语进行表述,贴近考试的形式</b>。</i></li><li data-pid="56BJmVjy">把2、3两点结合起来——在得到一道口语题目之后,<b>首先很快地得到一个关键点的列表(步骤2)和它们之间逻辑关联的提纲,之后借助常用的逻辑结构用口语将其增加合适的逻辑连接表达出来(步骤3)</b>。<br>--&gt;解释:<i>在练习的过程中,可以不断地迭代、压缩准备的时间和程度(在我自己最后练习时,可以完全不借助草稿进行表达),同时思考自己的问题是来自如下的哪一点,并进行针对性练习:A. 是否可以熟练地使用语言产生一个文字稿;B. 自己对于逻辑结构的掌握是否熟练、是否可以根据关键点快速地补全句子。</i></li></ol><p data-pid="Le0y0XOv">如上四步最主要的目的就是提升“【<b>流畅】【输出】</b>”的能力。具体有这样几点需要关注:</p><ul><li data-pid="ZAf3wVOj"><b>如何保持适当流畅</b>:因为口语有对实时性的要求,所以势必达不到写作的水平——<b>那么牺牲哪部分质量是合理的</b>?在我看来,<b>表达的逻辑和内容仍然是交流的重中之重</b>,所以在练习口语的过程中:</li><ul><li data-pid="dgBtHb9H">可以<b>适当降低每个单句的复杂性、质量</b>,例如用更大比例的简单句。</li><li data-pid="i4lCcosZ">着重<b>保持整段话的逻辑和清晰</b>。</li></ul><li data-pid="xlYwFLcg"><b>如何保证输出质量</b>:较好的写作是口语能够输出高质量内容的保证,为了贴近口语考试的形式,在练习写作的时候就需要思考<b>如何总结、利用自己的逻辑框架</b>。</li></ul><h3><b>2.2 备考技巧</b></h3><ul><li data-pid="8UTRaX_-">知己知彼,百战不殆:<b>在托福考试之前有规律地刷ERater</b>。我自己是在考试之前借助了考满分网上的评分系统,感觉不是特别准,但是有一定的参考价值,特别是 A. 它确实会反映出很多自己口语表达的问题;B. 上面有很多其它高分考生的录音,经过对比可以知道自己的问题在哪里。</li><li data-pid="Cv-VwqUH">丰富词汇多样性:<b>针对常用词汇准备多种表达方式</b>可以有效提升口语考试中“语言质量”的分数。例如在口语表达中,经常需要用到表达观点的动词“反对”,它的表达方式可以为:Disagree、Undermine、Attack、Disapprove、Oppose、Express the disappointment...事实上,这个训练对于提升写作也是很有帮助的。</li><li data-pid="jhTEgDUV"><b>准备常用的文章段落结构</b>:因为托福口语的题目基本形式非常固定,所以可以自己准备一套模板,它的好处就是不需要花费过多的脑力想句子之间的逻辑连接。例如针对第二道口语题目,论述学校里的一件事情,可以按照如下的结构做:The university has proposed to xxx. This decision is largely based on the following two reasons. They are ... The student in the dialogue disagrees with the proposal, and he mainly attacks it by undermining the two claims made by the school officials. From the aspect of convenience, ... Moreover, from the aspect of... Therefore.</li></ul><h3><b>2.3 FAQ</b></h3><ul><li data-pid="IVpxO-Gh">有同学认为自己的语音语调不好,所以把大量的精力放在了练习语音上,其实这个不一定是最划算的。口语表达重在交流,所以“<b>清晰和逻辑最重要!语音语调在其次!</b>”其实很多来自印度、日本、韩国的同学语音语调也不标准 (比如印度同学的口音实际是经常被吐槽的),但是并不影响他们拿到很高的分数,说明口语拿到高分的本质在于表达清晰、内容充实、逻辑顺畅。</li><li data-pid="rKjaQrad">另外的问题就是,平时如何训练口语?大家在平时和人相处的过程中,是可以听出来哪些人表达能力强、哪些人表达能力弱的,所以听自己的录音一般就足以判断说得哪里好、哪里不好了。同时,在专业运动员训练的过程中,看自己的录像回放是提升技术水平的重要一环,因此学会听自己的录音是练好口语重要而且可行的手段。</li></ul><hr><h2><b>3 我有阅读、听力的基础,如何提升写作</b></h2><p data-pid="mMz34uPX">我在本科 (2~3年前)裸考过两次托福,写作都是24分——也就是刚刚可以达到Good的水平。<b>在准备过GRE、尝试写Paper、写申请文书</b>的过程中逐渐抓住了写作的关键点,由此提升了写作,能够稳定地拿到27分。在这一过程中,我认为主要的提升不在于我的语言能力本身有了显著的提升,而是我的写作、表达能力有了很大的进步。具体的体现就是,我的汉语表达也变得更清晰、有逻辑了。在这个过程中,我发现写作提升其实就是如下几个关键点:<b>层次清晰、论证清楚、内容充实</b>,在这三点中,我个人最大的进步就是知道了<b>如何落实好层次和逻辑</b>。</p><p data-pid="XGoQlEaQ">同学们在准备托福、GRE写作的过程中,会发现它的要求和高考写作非常不同。托福、GRE写作往往是要<b>写文章明确地阐述自己对一个问题的观点</b>(托福写作第二题),甚至在有的情况下内容也都是被限定好的(托福写作第一题)。因此托福、GRE写作的目标就非常清晰——<b>怎么最简单直接地把一件事情讲清楚?</b></p><h3><b>3.1 层次清晰</b></h3><p data-pid="zKiqMw_A">在撰写论文、写申请文书的过程中,最大的挑战就是“<b>如何在有限的篇幅里,在文字线性的限制下,把逻辑关系是一张网的事情讲清楚</b>。”比如在论文的Introduction,往往会有多个层层递进的Motivation,它们自己之间就会有依赖关系;同时,论文中也需要讲清楚这些Motivation和已有方法的联系、和实验结果的联系,而这些进一步加大了文章的复杂性。因此,那段时间主要锻炼了我的两个方面:</p><ol><li data-pid="HERsPuHV"><b>如何把复杂的依赖关系拆解成几个简单的链条,实现文章内容上的层次清晰</b>。</li><li data-pid="5pe-jdH1"><b>如何通过语言技巧(例如对总起段、连接词、连接句的使用)让读者在阅读过程中能够保持对文章结构的清晰认知,实现形式上的层次清晰</b>。</li></ol><p data-pid="v4G4TQq8">层次清晰包含两方面,一方面是形式上的,一方面是内容上的。形式上的层次清晰主要关注文章的结构和连接词的使用。内容的层次清晰主要关注支持主要观点的子观点设计是否符合逻辑关系。</p><p data-pid="OwNdcAGv">在形式上,主要有如下几点:</p><ul><li data-pid="qG7JEhTk">文章<b>开篇讲清楚核心观点</b>,同时<b>简要介绍论证逻辑</b>。</li><li data-pid="lE2TRhOp">在每个<b>段落的开篇和结尾概括本段的核心观点</b>。</li><li data-pid="iIteZajY">在每个段落的开头(或者结尾),<b>能够通过合适的过渡使读者了解到文章相邻段落间的逻辑关系</b>。例如:K+1段反对了K段的假设,或者K+1段在第K段的基础上从另一个角度佐证了核心观点。</li></ul><p data-pid="d3HeeYXj">在内容上,主要有如下几点:</p><ul><li data-pid="U1RZTeo6">安排文章内容的时候首先<b>想清楚子观点之间的逻辑关系</b></li><li data-pid="mSqrwOZy"><b>几个子观点之间的论证最好可以构成无环图</b>(托福考场上基本想不出来特别复杂的观点,所以如果最后逻辑混乱,基本就是自己组织的问题)</li></ul><h3><b>3.2 论证清楚</b></h3><p data-pid="svrNDgpK">在确定了整体的层次之后,<b>论证清楚主要关心局部的小逻辑</b>。这里具有关键作用的有两点:</p><ol><li data-pid="bnQ7Q65l">在撰写申请文书的过程中,需要介绍自己的科研项目,在这里需要通过5~6句话涵盖如下方面:Project的主要内容、Motivation、方法、实验效果、自己的贡献。绝大多数的人其实都可以覆盖到这几点,但是在我给其他同学修改的时候发现:<b>段内的论证小逻辑往往很差</b>。比如从论证逻辑上,很多同学没有讲清楚“自己的方法<b>在哪一点</b>上解决了开头提出的问题”、“<b>为什么这样做是合理的</b>”;从语言上,很多同学也会出现如下问题:“在<b>某段话的中间出现了一个新的对象</b>作为主语”、“在很多句话之间<b>多次使用了带有转折含义的连</b>词,让整段话的逻辑变得费解”等等。修改这些部分有效地提升了我对于段内逻辑的敏感。</li><li data-pid="Sb4w7qDv">在学习GRE的过程中,英语培训机构会给出一些<b>有效的论证结</b>构。通过学习这些结构其实可以有效地简化、标准化对于论据的组织。例如</li></ol><ul><li data-pid="x-2Wh80s">举例论证需要注意全面性,往往需要从多个方面展开;比如讲到某一种品质对于人生发展的作用,可以举出来三个例子覆盖不同国家、不同时间、不同领域——古希腊的哲学家(比如柏拉图),近代欧洲的政治家(比如拿破仑),现代美国的工程师(比如福特)。</li><li data-pid="L_5SfuXt">直接通过逻辑论证可以利用反证法,“如果xxx是正确的,那么会有xxx后果,它带来的好处无法抵消xxx负面效果,因此xxx是不对的”</li><li data-pid="JLd1Udjz">最经典的论证方法就是三段论,首先有xxx前提,同时又有xxx,所以xxx;通过这样的结构写作,可以清晰地论证自己的观点。</li></ul><p data-pid="iidKknR9"><b>3.3 其它</b></p><p data-pid="iSYzuoYx">在我的写作提升过程中,还有如下几点比较关键,值得一说:</p><ul><li data-pid="e5jUPYcX">在写作考试中,语言质量非常重要,其中一方面就是表达的规范性,这里特别需要关注语法错误和表达方式的误用。我推荐各位<b>使用Grammarly</b>,它可以有效地帮助自己改掉很多不好、不对的表达习惯 (例如很多国内同学非常非常非常喜欢用被动语态,但是这是非常非常非常不好的),同时也能很快地检查出自己的语法错误。</li><li data-pid="nb5070Q4">语言质量的另一个方面就是多样性。学会如何结合长短句需要一定的技巧,但是<b>学会多样性地用词和表达</b>并不是一件困难的事情。例如在讲解口语的过程中,我们就提到了“如何多样地表达反对”。</li><li data-pid="4Ntishz2">内容来自平时的积累,很多同学的困难是:汉语有很多例子,但是英语写不出来。所以大家在平时学习的时候,记得<b>有机会就查一查看到的电影、书里的故事人物的英文怎么写</b>,要不然往往在需要举例的时候知道中文怎么说但是写不出来。比如说,历史上的著名人物一定要知道怎么写,有名的城市要知道怎么写...</li><li data-pid="7cp3wuHD"><b>进步来自实践</b>:如果平时有汉语、英语写作的机会,一定要好好抓住,不断地Refine——能够把一篇文章怎么写好想清楚,胜过于写十篇没达到标准的文章。</li></ul><h2><b>4 考试建议</b></h2><p data-pid="Rf9oJKmw">最后分享一些我自己考试的经验:</p><ul><li data-pid="8P6M--13">做阅读的时候,我会首先阅读整个文章一遍,再去边看文章边做题。这样可以有效地提升速度,比如我基本9~12分钟可以完成一篇托福阅读。</li><li data-pid="cu8_2E9a">听力部分我不喜欢做笔记,而且也不需要做笔记,特别做笔记的时候经常会漏听一些内容,所以尽量训练可以不需要笔记就可以做听力。</li></ul><h2>5. 广告</h2><p data-pid="LeY-GcIz"><b>最近需要办签证,可能需要一些资金支持,所以有兴趣的同学可以私信找我付费辅导</b>:</p><ul><li data-pid="dU-3SAou"><b>托福口语</b>:通过1-3个小时的辅导,我希望可以“授之以渔”,帮助你搭<b>好练习口语表达突破24分的架子</b></li><li data-pid="i2jjPVO2"><b>文书写作</b>:<b>以申请文书为范例</b>,通过1-3小时的辅导,帮助你<b>精细修改一篇文章</b>,<b>弄清楚如何写出好的英语文章</b></li></ul><p data-pid="4g1sRYjG">因为已经有大量性价比较好的辅导机构存在,而且我希望能确信【<b>我可以帮到你,不会让你花冤枉钱</b>】,所以<b>希望你在私信我的时候告诉我你的如下信息</b>,这样我可以判断我们是否合适:</p><ul><li data-pid="RbgciLTQ"><b>托福分数</b></li><li data-pid="Y05A4y5u"><b>一段200词的介绍你的经历的Writing Sample,最好来自你的申请文书</b></li><li data-pid="NgeYkXpO"><b>暂时的年级和学英语的目的</b></li></ul><p></p> http://zhuanlan.zhihu.com/p/364021736 http://zhuanlan.zhihu.com/p/364021736 Tue, 20 Apr 2021 14:30:48 GMT 2021的生活心态——来自贝叶斯公式视角的解读 <h2>1 引子</h2><h2>1.1 引子1</h2><p data-pid="mgDT3ES2">某天和社科同学Z吃饭时发生了如下对话:</p><p data-pid="OgbNuxHX">我——“别管咱是文科生理科生,最好都得知道贝叶斯公式,而且它确实有用!”</p><p data-pid="pOwZAL6C">Z ——“Well…那你讲讲吧。”</p><p data-pid="8og6-XO0">我——“条件概率你知道不…贝叶斯公式讲的是啥呢,就是你有两个事件A、B,在A的条件下发生B的概率,等于B的概率乘以B的条件下发生A的概率…”(我知道我成功给她绕迷糊了)</p><p data-pid="kZdevptG">Z ——“要不你举个例子,或者直观地解释一下?”</p><p data-pid="Q1h0fuqL">我——“就是在A的条件下发生B的概率,不仅跟B本身有关系,而且跟B的条件下发生A的概率有关系…”</p><p data-pid="B6ZRDJnJ">Z ——“你看,你这还是不懂贝叶斯公式”</p><h2>1.2 引子2</h2><p data-pid="8dq449G7">根据21世纪的网络空间习俗,写篇小作文是年终岁尾不得不干的一件事情,只不过不同地方的风格不太一样——朋友圈里的是民主生活会风格,气氛严肃活泼,朋友们分享各自分享过去一年的成长和进步;知乎大V要走述职大会风格,气氛高端上档次,主要关心过去一年又实现了什么目标,毕竟他们得告诉关注自己的人——“你看,关注我不亏!”</p><p data-pid="wPfmsTgI">可惜我的2020实在没什么大书特书的东西,仍旧是在普通人的范畴里勤勉地努力。少数能拿出来可喜可贺的东西就是:离开了学校、进入了社会,多了些更生动的人生经验。虽然我拼命想总结出什么来,却发现“从实践到理论”这件事情难得很,更何况自己感情生活毫无进展、学术研究照旧死磕,实在是没有什么当人生导师的资本。</p><p data-pid="MeKkk27n">尽管提不出什么醍醐灌顶的“新道理”,但是这段离开校园的经历足够让我对书本上学到的“旧道理”有了更深的体会——这里的“旧道理”之一就是上面所述的贝叶斯公式。我不仅对它有了更深的体悟,而且也逐渐摸索出要怎样把它用到生活的实践里。</p><p data-pid="6jb-zHNf">所以,2020年的12月31日,我不写回顾2020的小作文——我们介绍一下贝叶斯公式,讲讲过去一年我在它身上的体会,展望一下2021的新生活。</p><hr><h2>2 什么是贝叶斯公式</h2><p data-pid="awtU1mSv">作为一篇意在科普的文章,我们终归是要写一下贝叶斯公式的数学形式和它的数学理解的:</p><p data-pid="20IeapJY"><img src="https://www.zhihu.com/equation?tex=P%28B%7CA%29%3DP%28A%7CB%29P%28B%29%2FP%28A%29+%5C%5C" alt="P(B|A)=P(A|B)P(B)/P(A) \\" eeimg="1" referrerpolicy="no-referrer"> </p><p data-pid="mqS5vYM8">在下图中,我们展示了一个对贝叶斯公式的简单展示。我们用圆角矩形代表世界上所有可能出现的情况,圆形A和矩形B代表了发生A、B对应的概率。那么P(B|A)对应的就是【<b>A、B重叠部分的面积</b>】和【<b>圆形A面积</b>】的比值。同时【<b>A、B重叠部分的面积</b>】也可以从相对【<b>矩形B面积</b>】的角度写成:矩形B中A的比例,乘以B的面积,也就是: <img src="https://www.zhihu.com/equation?tex=P%28A%7CB%29P%28B%29" alt="P(A|B)P(B)" eeimg="1" referrerpolicy="no-referrer"> 。由此,我们直观地理解了贝叶斯公式的正确性。</p><figure data-size="normal"><img src="https://picx.zhimg.com/v2-6960270c75e4845b2fa35a6458d4d242_720w.jpg" data-caption="" data-size="normal" data-rawwidth="674" data-rawheight="452" class="origin_image zh-lightbox-thumb lazy" width="674" referrerpolicy="no-referrer"></figure><hr><h2>3 <b>从贝叶斯公式看生活中的遗憾</b></h2><p data-pid="Hf4U0wFA">生活中有三件事儿不可避免:遗憾、失败和暂时迈不过去的槛儿,2020年如是,2021年亦如是。“失败是成功之母”无疑是鸡汤文和爽文永恒的主题,但是反败为胜不在本文的关注范围,我们的核心是:<i><b>通过贝叶斯公式的视角给予合适的精神慰藉——“坦然地接受并面对失败,不会以苛责自己的方式克服痛苦。</b>”</i>这样的心态去接受生活的不如意不仅会大幅度降低您患心脑血管疾病的风险(Absolutely),而且根据心理学研究,失败之后不去苛责自己一般会带来更好的长远自控和表现。(详见 “凯利•麦格尼格尔,自控力”)</p><p data-pid="-YKSzlwX">安然的心态实际上很难保持,特别是当我们自己非常上进,或者付出了巨大的努力却一败涂地,心态不平衡、感到沮丧是自然而且正常的反应,而且只有对生活的不如意有所体悟,才能进一步知道如何做得更好。因此,我们这里的“坦然接受不如意”,并不是为了把问题开脱掉,而是让自己的理智接受【<b><i>存在失败、存在不如意</i></b>】,而且明白【<b><i>它们的发生是合理的(reasonable)</i></b>】。我们实现这一点的核心工具就是贝叶斯公式。</p><p data-pid="n4sGRcNB">我们举一个有趣的“生活中的不如意”的例子。小明试图与一位女公民G交往,但是失败了,他非常郁闷。小明的基本情况是:“日常工作学习忙碌,感情经历匮乏所以不太会和女公民们交往”;女公民G的基本情况是“日常工作学习忙碌,但是有过几段感情经历”。每当我们想要劝小明“维纳斯不相信眼泪”的时候,小明就会说:“你们这些年轻人根本不懂爱情,我身边有好几对儿这样的都成了。”<b>实际上,我们要做的不是断了小明的念想,而是(1)让他从理智上接受暂时情场失意的现实;(2)能够想清楚这件事情的原因。</b></p><p data-pid="C7lNIXZ9">如下图,<b>我们两人交往这件事情建模为P(B|A):代表在A“小明和女公民G的基本情况情况”下,发生B“男女嘉宾成功牵手”的概率</b>。在贝叶斯公式的等式右侧中,我们一般假设P(A)、P(B)是定值,因为它们分别是“小明和女公民G的背景情况在人群中的概率”、“人们一般能够找到爱情的概率”。因此,<b>决定P(B|A)的主要就是P(A|B)</b>了。为了判定P(A|B)到底有多大,大家只需要想一想——身边的情侣里有多少是(a)没有足够精力培养熟悉度,(b)对于感情的经验和认知有一定的差距。从这个角度看,小明没能成功和女公民G交往是一件完全合理的事情,而且说不定因此还不至于糟蹋了两位公民的友谊。</p><figure data-size="normal"><img src="https://pic1.zhimg.com/v2-41daa8f28e9478b9d3de357403b1e8ab_720w.jpg" data-caption="" data-size="normal" data-rawwidth="641" data-rawheight="316" class="origin_image zh-lightbox-thumb lazy" width="641" referrerpolicy="no-referrer"></figure><p data-pid="dNZstirD">毫无疑问,在上面的过程中,我们没有把一些正面因素考虑进去,因为<b>我们再次重申这里的要义:【<i>接受生活不如意、失败的合理性</i>】、【<i>找到这种不如意发生的原因</i>】</b>。而在考虑这些负面因素的情况下,一方面我们理智上可以想“<b><i>我抓了一手烂牌这次打坏了确实也没有办法</i></b>”,另一方面也让我们思考“<b><i>手里的牌究竟烂在哪里了</i></b>?”</p><hr><hr><h2>4 <b>从贝叶斯公式看积极生活的意义</b></h2><p data-pid="wsHJ08fX">在上一部分中,我们主要讲述了“如何用贝叶斯公式的观点看待生活中的不如意”,好像生活整体总是带着一种“灰暗”的基调。但是从另一个方面讲,我们同样也能找到积极生活的意义。回顾之前的分析,<b>我们一直在把代表个人背景的P(A)、代表好好生活的目标P(B)做成一个定值处理——这样的假设是从静态的角度分析问题,而不是从动态、发展的角度分析事物</b>,自然给生活打上了灰暗的色调。<b>从动态的角度来看,人是有主观能动性而且在发展的,当我积极地改变自己的条件、改变自己的心态,那么P(A)也必然就发生了变化</b>。 </p><p data-pid="8Mnsb9A4">比如说,我们考虑新的P(B|A):B代表是否可以学好数学,A代表个人的知识储备。根据贝叶斯公式,它等于P(A|B)P(B)/P(A)。那么,随着我们积极努力地提升自己的知识储备,我们的P(A)实际上在减小——因为它在分母上,所以实际上会提升我们能够学好数学的概率。尽管在分子上的P(A|B)也会受到这个影响而减小,但是我们从直观上理解:能学好数学的人普遍需要足够的知识储备,有P(A|B)&gt;P(A),所以我们提升了自己的知识储备会让P(A)的减少比P(A|B)更加显著,因此P(A|B)/P(A)确实是在增加的。这个过程中,其实我们展示了两个重要的事情:</p><ul><li data-pid="pW2FLgWq"><b>积极努力可以提升实现目标的概率;</b></li><li data-pid="Ldwa-78F"><b>选对方向很重要,只有选对了方向才可以让你的P(A)跑赢P(A|B)</b></li></ul><figure data-size="normal"><img src="https://picx.zhimg.com/v2-fe7cf4f48d1b5e99dbe13722d8a43d79_720w.jpg" data-caption="" data-size="normal" data-rawwidth="620" data-rawheight="412" class="origin_image zh-lightbox-thumb lazy" width="620" referrerpolicy="no-referrer"></figure><p data-pid="lJ89tCf8">利用贝叶斯公式,我们想另外辩护一点:<b>很多时候一件事情会比较难做,受到很多客观条件或者额外人员的掣肘,此时有一种态度是“<i>做不做是你的事情,成不成是它的事情</i>”</b>。事实上,这种想法是非常值得提倡的。我们再次回到P(B|A),只不过这次我们把A换成了“是否行动?”很容易就可以看出,<b>如果我们不行动,那么P(A|B)将永远是0,毕竟没有人能够不做一件事情就先成功了(</b>要不美国抗疫就成功了,我就能去留学了hhh<b>)</b>。如果失败怎么办呢?<b>如果失败的代价可以承担,或者干脆只是一些时间成本,那么“多试多失败只会让你更皮实”</b>(对“纳西姆•尼古拉斯•塔勒布,反脆弱 ”的精简版理解)。</p><p data-pid="H_M-9_oD">因此,基于人是有主观能动性的、而且在不断发展的,无疑是不断积极地改变能够更好地生活。</p><hr><h2>5 结语</h2><p data-pid="4YFO2wPI">过去的一年,我离开校园走入社会真真正正地新学到了很多。根据我个人的观察到,很多事情最终的落脚点都是共通的——<b>生活和事业是一个长期的过程,生活中遇见的很多人其实都是人生路上的“回头客”,所以勤勉、诚实、多思、善良最终总会是更好的选择。</b>要不就把用贝叶斯公式解释这个弄成课后题算了?</p><p data-pid="cRlzi_f8"><b>最后祝大家元旦快乐!</b></p> http://zhuanlan.zhihu.com/p/341144043 http://zhuanlan.zhihu.com/p/341144043 Thu, 31 Dec 2020 14:41:38 GMT Detectron2代码学习3 -- 数据加载 <ol><li data-pid="O1U__XwN"><a href="https://zhuanlan.zhihu.com/p/161986997" class="internal">庞子奇:Detectron2 代码学习 1 -- 整体结构</a></li></ol><p data-pid="otdqXEi5">2. <a href="https://zhuanlan.zhihu.com/p/170612519" class="internal">Detectron2 代码学习 2 -- 检测模型实现</a></p><p data-pid="GWoxPvjO">3. 数据加载与实现 (本篇)</p><p data-pid="mt7SRIMO">4. (计划中) 模型细节与其它设计部分</p><h2><b>3. 数据加载与数据接口</b></h2><h3><b>3.1 整体框架</b></h3><p data-pid="CZ5RDNb7">从何开始阅读数据加载部分的代码呢?我们来到之前分析过的<code>engine</code>部分,在<code>defaults.py</code>里面的<code>import</code>可以发现构建数据集的部分是</p><div class="highlight"><pre><code class="language-python"><span></span> <span class="kn">from</span> <span class="nn">detectron2.data</span> <span class="kn">import</span> <span class="p">(</span> <span class="n">MetadataCatalog</span><span class="p">,</span> <span class="n">build_detection_test_loader</span><span class="p">,</span> <span class="n">build_detection_train_loader</span><span class="p">,</span> <span class="p">)</span> </code></pre></div><p data-pid="pjythNYv">因此我们需要到<code>data/</code>中从这几个函数入手分析<code>detectron2</code>是如何处理数据加载的。</p><p data-pid="4BC8H1om"><code>build_detection_train_loader</code>和<code>build_detection_test_loader</code>很自然地都在<code>data/build.py</code>中。我们以<code>build_detection_train_loader</code>为例进行分析。</p><p data-pid="o8xzg3dT">在<code>build</code>函数中,主要分为以下几步:</p><ul><li data-pid="uO6wz9fA">通过<code>get_detection_dataset_dicts</code>得到数据集的基本信息。通过<code>get...</code>函数的注释可以看到,它最终返回了一个列表,列表中的每一项都是标准的数据集字典格式。我们可以把它理解为一个列表,列表中的每一项都是数据集的一个元素,而每个元素都是用字典格式描述的。</li><li data-pid="5VltiWBb">通过<code>DatasetFromList</code>将之前得到的列表转化为<code>pytorch</code>格式的<code>dataset</code>,也就是<code>torch.dataset</code>。在<code>pytorch</code>中开发者提供了统一的<code>dataset</code>父类,通过继承这一统一的父类会对后续数据加载等提供更加方便良好的支持。</li><li data-pid="khVaLlQt">数据集提供的数据格式不一定能够满足<code>detectron2</code>的模型对于接口的要求。但是<code>detectron2</code>希望能够方便地支持多种任务、多个数据集上的训练、测试,所以在数据集和模型之间需要“胶水”来完成这个任务,这里的胶水就是<code>mapper</code>和<code>MapDataset</code>,它们将数据集中的数据变换成<code>detectron2</code>能够使用的格式。</li><li data-pid="ZBl1eNFw">为了构建一个<code>dataloader</code>,我们还需要指明从数据集中采样数据遵循的原则。例如最简单的方式就是从所有样本中平均采样。实现这一任务,即指明如何采样的,就是<code>Sampler</code>,我们需要在其中定义采样数据遵循的规范。</li><li data-pid="hTDRJAOx">最后,<code>dataset</code>提供了数据的来源,<code>sampler</code>提供了采样数据的规则,<code>cfg</code>中包含了其它运行过程的定义,我们通过<code>build_batch_data_loader</code>就创建了一个dataloader。在访问过程中,我们只要有一个迭代器,就可以从dataloader中方便地加载数据了。</li></ul><p data-pid="yzVVTw96">至此,我们完成了对于<code>detectron2</code>数据加载框架的分析。从这里可以看到几个深度学习,特别是pytorch中数据加载模块的重要层次:</p><ul><li data-pid="fNynA_xt"><b>Dataset</b>:定义数据的来源,其中需要包含<b>如何将原始数据变成模型接受的格式</b></li><li data-pid="gc5SUJxw"><b>Sampler</b>:从数据集中采样数据的顺序需要遵循的规则</li><li data-pid="JhNcsxmS"><b>DataLoader</b>:在前两者的基础上,由Pytorch封装的加载数据的接口。</li></ul><h3><b>3.2 <code>get_detection_dataset_dicts</code></b></h3><p data-pid="H6I56nqI">在此函数中,最终返回值是<code>dataset_dicts</code>,也就是最终描述dataset的列表。在函数中获得<code>dataset_dicts</code>的语句是<code>dataset_dicts = [DatasetCatalog.get(dataset_name) for dataset_name in dataset_names]</code>,也就是说,针对每一个dataset_name,通过<code>DatasetCatalog</code>获得对应的数据集的数据信息。</p><h3><b>3.2.1 <code>DatasetCatalog</code></b></h3><p data-pid="3fYe07TT"><code>DatasetCatalog</code>的实现在<code>data/catalog.py</code>中,<b>catalog</b>的中文释义是“目录”,而这个类的作用也正是存储数据集的信息,并且指定了访问数据集的方法。在<code>DatasetCatalog</code>主要起到的就是一个索引的作用,在<code>register</code>函数中,我们将能够“返回数据集信息的函数func”和“数据集的名字name”绑定,这样在<code>get</code>方法中通过数据集的名字就可以调用提取数据的函数了。那么下面的问题就是:提取数据的函数从哪里来呢?在<code>data/datasets</code>中,我们看到了一个文件<code>register_coco.py</code>。其中实现了方法<code>register_coco_instance</code>,我们可以看到它调用了<code>DatasetCatalog.register</code>,将<code>COCO</code>数据集和<code>load_coco_json</code>方法绑定到了一起。因此,我们下一步以<code>COCO</code>数据集为例,分析<code>load_coco_json</code>的实现和它返回的结果。</p><h3><b>3.2.2 加载COCO数据集</b></h3><p data-pid="plr0CJII">在<code>load_coco_json</code>中,通过coco标注文件的路径<code>json_file</code>、图片文件夹所在位置<code>image_root</code>,最终构建出了需要访问的coco数据集的信息。<b>在这里,对于深度学习初学者需要提到很重要的一点:在构建数据集的函数中,往往不会去读取图片,因为计算机的内存不足以承载所有的图片。因此普遍的解决办法,是在构建数据集的阶段记录下图片的路径,并在训练过程中OnLine地对图片进行读取。</b>所以,在这个函数中,我们也同样不会对图片进行读取。</p><p data-pid="lq2vQQWe">在这个函数的实现中大量地使用了COCO数据集本身提供的接口,我们不会关心这些接口的实现,仅仅把它们看作是使用者在自己的数据中能够自行定义的方法函数。在整个函数的思路中主要包含两个部分:</p><ul><li data-pid="R7OUHQi1">第一个部分是从COCO数据中读取所有的信息</li><li data-pid="aA7cWapw">第二个部分是将上面读取的信息变成合乎形式的<code>dataset_dicts</code></li></ul><p data-pid="e6UIFX5G">针对第一部分,我们首先在<code>COCO(json_file)</code>中用标注信息初始化了COCO数据集。在这里额外扩展一下,<code>json</code>文件用作储存数据集的标注信息是非常常用而且好用的一种手段。之后,<code>detectron2</code>分别通过<code>coco_api.loadCats</code>和<code>coco_api.loadImgs</code>获得了数据集中的类别(Category)和图片(Images)。更进一步,为了将图片和对应的标注对应起来,<code>anns = [coco_api.imgToAnns[img_id] for img_id in img_ids]</code>按照图片的ID得到了每张图片上的标注。</p><p data-pid="w0vfnNwF">针对第二部分,其实就是一个非常简单的遍历算法:遍历列表中每张图片和它对应的标注<code>anno</code>,把它们组织成本图片的信息<code>record</code>。<code>record</code>中包含了图片的基本信息,例如图片的长、宽和ID,也包含了图片中每个物体的信息,例如它的类别与2D Bbox的标注。在最后,把所有的Record组织成一个列表就实现了对dataset_dict的构建。</p><h3><b>3.3 <code>DatsetFromList</code></b></h3><p data-pid="uWvYJ7Zm">在Pytorch中有针对数据集的统一接口,它可以方便地转化成统一的<code>DataLoader</code>接口,方便在训练过程中读取数据。因此,我们需要关心如何将上面拿到的<code>dataset_dict</code>转化成一个pytorch的统一<code>dataset</code>。</p><p data-pid="MHoyCK8K"><code>DatasetFromList</code>的实现在<code>data/common.py</code>中,它继承了Pytorch对数据集的统一接口<code>torch.utils.data.Dataset</code>,将输入的<code>dataset_dicts</code>转化成可以Pytorch读取的数据集。它的主要组成部分有如下几个:</p><ul><li data-pid="6SI2pFsL">通过<code>__init__</code>读取数据</li><li data-pid="4uFMCXiF">通过<code>__getitem__</code>定义读取数据的规范</li></ul><p data-pid="_HssagXI">在读取数据的部分其实非常简单,只需要把之前准备好的<code>dataset_dicts</code>存储到类中即可。</p><p data-pid="FBNw5nzo"><code>__getitem__</code>是<code>torch.utils.data.Dataset</code>中定义的必须自己实现的函数,它的功能是根据给定的index读取数据集中的数据,因此我们可以把它看作是整个实现的核心。在<code>self._lst[idx]</code>中就体现了它的逻辑:把整个数据集想象成装载着数据的列表,我们只需要根据序号访问列表中的内容即可。</p><h3><b>3.4 MapDataset</b></h3><p data-pid="udSD3fnT">在上一部分中,我们将<code>dataset_dicts</code>变成了一个pytorch dataset,但是我们仍然没有解决如何读取数据,特别是图片数据的问题。在<code>MapDataset</code>中我们就需要处理从数据集索引到真正数据的过程。一个示例的实现可看<code>data/common.py</code>中的<code>MapDataset</code>,其初始化接受一个Pytorch Dataset,也就是在<code>DatasetFromList</code>中得到的结果,利用新的函数读取数据。这个函数区别于<code>DatasetFromList</code>的核心在于它的映射函数,一个示例实现在<code>data/dataset_mapper.py</code>中的DatasetMapper,它的主要目标就是实现<code>self.map_func</code>中映射的作用。</p><p data-pid="uCf2eGot"><code>DatasetMapper</code>的核心在<code>__call__</code>函数,它的主要功能是把类变成一个函数一样可以直接调用。在<code>__call__</code>中依次进行了如下步骤:</p><ol><li data-pid="C67qcq8f"><code>image = utils.read_image(dataset_dict["file_name"], format=self.image_format)</code>,通过文件名称读取图片,并由<code>utils.check_image_size</code>进行一些检查工作</li><li data-pid="qVLmkcnx">对图片进行数据增强,同时把数据增强的信息存储到<code>transforms</code>中<br> aug_input = T.StandardAugInput(image, sem_seg=sem_seg_gt)<br> transforms = aug_input.apply_augmentations(self.augmentations)</li><li data-pid="BU4OnmeQ">将图片转化成tensor,作为'image'的Key存储到图片数据中<br> dataset_dict["image"] = torch.as_tensor(np.ascontiguousarray(image.transpose(2, 0, 1)))</li><li data-pid="QRB86IhF">针对之前做过的图片变换transforms,我们可能需要对一些标注进行修改。例如我们如果对图片进行了裁剪(Crop)操作,可能BBox的坐标位置也会相对发生改变。这部分都是通过一些工具函数实现的,例如<code>utils.transform_instance_annotations</code>,不再进行详细分析</li><li data-pid="oLlbfynb">通过以上步骤,我们实现了一个读取数据的映射函数。</li></ol><h3><b>3.5 <code>Sampler</code></b></h3><p data-pid="kCg4JvS3">在Pytorch数据加载的过程中,需要指定数据采样的规范。例如最简单的方式就是把这个过程看成是均匀采样,每次随机从所有实例中挑选一个;也有复杂一些的需求,例如需要对不同的类别做数量上的平衡,那么就需要对不同的实例赋予不同的权重。因此,我们需要研究一下<code>detectron2</code>中<code>Sampler</code>的写法,具体研究<code>data/samplers/distributed_sampler.py</code>中的<code>RepeatFactorTrainingSampler</code>。在这个Sampler中,它会对不同的样本赋予不一样的权重,目标就是对抗不同类别之间的不平衡现象。</p><p data-pid="ZbnvPNnR">pytorch的Sampler中,最主要的部分在于<code>__iter__</code>函数。它的作用就是返回所在训练/测试的Batch需要使用的数据的序号。在<code>sampler</code>的实现中,就是从所有序号的列表<code>self._infinite_indices()</code>种得到想要的序号。在这里,我们需要额外解释一下<code>yield</code>的作用。按照我的理解,<code>yield</code>包含对内、对外两部分功能,对外的部分<code>yield</code>的作用相当于<code>return</code>,把结果返回;对内的作用<code>yield</code>相当于一个生成器,它存储了这一次函数执行的上下文,在下次函数被调用的时候,就从上一次结束的位置开始。因此,<code>yield</code>相当于帮助我们实现了一个迭代器的功能。</p><p data-pid="wnr_CS9I">在看完了<code>__iter__</code>之后,我们还需要进一步查看<code>sampler</code>是如何找到的所有序号的的indices,包括如何实现的RepeatFactor,所以我们下面会研究<code>self._infinite_indices()</code>的实现方式。在查看<code>RepeatFactorTrainingSampler</code>之前,我们先参考一下最普通的<code>TraningSampler</code>的实现。普通<code>TrainingSampler</code>的实现方式就是随机产生了一个固定<code>self._size</code>的列表作为<code>indices</code>的列表,因此实现了对于数据集中对象的随机采样。那么在<code>RepeatFactorTrainingSampler</code>中,它的做法就是按照一定的比例对权重更大的index进行重复,之后随机从这个新的列表中采样,由此实现了按照权重采样的经过。</p><p data-pid="0tRDLTGs">综上,我们探清楚了<code>Sampler</code>的设计方式和写法。</p><h3><b>3.6 小结</b></h3><p data-pid="eyWNRGhd">在定义了dataset、sampler之后,我们就已经基本完成了对数据加载过程的定义。在<code>build_batch_dataloader</code>中<code>detectron2</code>展示了如何通过调用Pytorch的统一接口实现对<code>dataloader</code>的创建。在pytorch中,dataloader的创建逻辑是接受传入的dataset——数据的来源、sampler——采样的方式,在需要的时候online地获得数据,而获得数据的函数就是dataset的<code>__getitem__</code>方法。在上面的分析中,我们就对于如下的几个层次进行分析,自底向上地分析了如下几个层次:</p><ul><li data-pid="aZ28bQz5">通过json文件保存数据集中每个实例的信息,并且如何从json文件中恢复出数据集</li><li data-pid="ozAtrEOV">通过映射函数实现从json文件内容指示的地址读取图片或者其它信息,并将其融入到Dataset的<code>__getitem__</code>方法中</li><li data-pid="cmhNqgoo">在Sampler中规定数据采样的规则</li></ul><p data-pid="1A5ipiSE">由此,我们一方面清楚了<code>detectron2</code>的数据加载是怎样实现的,另一方面也清楚了自己如果想定义新的数据加载方式要怎样做。</p><p></p> http://zhuanlan.zhihu.com/p/186051171 http://zhuanlan.zhihu.com/p/186051171 Sun, 16 Aug 2020 15:09:27 GMT Detectron2代码学习2 -- 检测模型实现 <p data-pid="jwvyM8Eh">合集目录:</p><ol><li data-pid="wJV3vVbx"><a href="https://zhuanlan.zhihu.com/p/161986997" class="internal">庞子奇:Detectron2 代码学习 1 -- 整体结构</a></li></ol><p data-pid="LTn-ICIc">2. Detectron2 代码学习 2 -- 检测模型实现 (本篇)</p><p data-pid="4HHuGd_q">3. <a href="https://zhuanlan.zhihu.com/p/186051171" class="internal">庞子奇:Detectron2代码学习3 -- 数据加载</a></p><p data-pid="5uKz62CQ">4. (计划中) 模型实现细节与其它设计</p><h2><b>2. <code>Detectron2</code>的<code>Model</code>部分</b></h2><p data-pid="IXYL877-">在这部分中我们将沿着刚才分析的训练结构,尝试分析如何构建<code>detectron2</code>的模型。为了实现这一点,我们会首先介绍<code>detectron2</code>中使用的<code>registry</code>机制,之后进一步分析</p><h3><b>2.1 <code>Registry</code>机制与<code>build_model</code></b></h3><p data-pid="lwBAnD4y"><code>Trainer</code>中初始化模型调用的接口是<code>build_model</code>函数,通过<code>modeling/__init__.py</code>可以知道它是在<code>modeling/meta_arch/build.py</code>中定义的。但是在阅读<code>build.py</code>的过程中,我们发现它使用了一个叫做<code>Registry</code>的东西——那么什么是<code>Registry</code>呢?</p><p data-pid="XkYYSj6D"><code>Registry</code>机制来自于FaceBook计算机视觉研究组的常用函数库<code><a href="http://link.zhihu.com/?target=https%3A//github.com/facebookresearch/fvcore" class=" wrap external" target="_blank" rel="nofollow noreferrer">fvcore</a></code>,其中<code>Registry</code>的源代码和解读可见<a href="http://link.zhihu.com/?target=https%3A//detectron2.readthedocs.io/_modules/fvcore/common/registry.html" class=" wrap external" target="_blank" rel="nofollow noreferrer">registry</a>。它的主要作用是提供了用字符串调用类方法的接口,具体的函数是<code>registry</code>中的<code>get</code>方法。个人感觉<code>registry.get()</code>非常像一个<code>getattr</code>方法,它能够通过字符串访问类的方法。</p><p data-pid="G8PI0TTa">接下来我们通过<code>build_model</code>的具体的用例来体会一下<code>Registry</code>机制的使用。例如我们有一个具体的网络结构,定义在<code>modeling/meta_arch/rcnn.py</code>中的<code>GeneralizedRCNN</code>。注意在它的定义上方有一个修饰器<code>@META_ARCH_REGISTRY.register()</code>,意思就是把<code>GeneralizedRCNN</code>注册到<code>META_ARCH_REGISTRY</code>中。那么在<code>modeling/meta_arch/build.py</code>中的<code>model = META_ARCH_REGISTRY.get(meta_arch)(cfg)</code>中,只要我们设置<code>meta_arch</code>为<code>GeneralizedRCNN</code>,那么<code>META_ARCH_REGISTRY.get(meta_arch)</code>就调用了<code>GeneralizedRCNN</code>方法,也就因此初始化了模型。</p><p data-pid="xSo4nZ3D">最后我们回顾和总结一下<code>registry</code>的几个要点</p><ul><li data-pid="e3LwsF_o"><b>创建一个<code>registry</code>,设置可能的调用的类的名字</b></li><li data-pid="SlvoZMVU"><b>对于想加入到<code>registry</code>中的类,在定义的时候通过修饰器指定</b></li><li data-pid="J2Zbxul5"><b>调用类方法的时候通过<code>get(name)</code></b></li><li data-pid="X3Uh-AHU"><b>通过<code>fvcore</code>中的源代码,我们可以知道如何写<code>python</code>修饰器。</b></li></ul><p data-pid="torAjx3v">由此,我们弄清楚了<code>build_model</code>的实现和<code>registry</code>的机制。</p><h3><b>2.2 <code>RCNN</code>结构模型</b></h3><p data-pid="o50gbXu3">在分析模型建构的过程中,我们主要看经典模型<code>Faster-RCNN</code>,在<code>modeling/meta_arch/rcnn.py</code>。<code>Faster-RCNN</code>的结构主要包含如下几个部分,而这几个部分也是<code>GeneralizedRCNN</code>在初始化部分的输入:</p><ul><li data-pid="CMWU4W6r"><code>backbone</code>:从图片提取特征表示的卷积神经网络结构,比如说ResNet。</li><li data-pid="Q0y7AUiD"><code>proposal_generator</code>:从图片的特征预测“哪里可能有物体”</li><li data-pid="t10k61Ik"><code>roi_head</code>:以<code>proposal_generator</code>部分预测的有物体区域为基础,预测物体的类别和检测框坐标</li></ul><p data-pid="1Hq8dhz6">与<code>__init__</code>功能相似的是<code>from_config</code>函数,它用config文件初始化模型,通过这个函数我们知道了实际构造backbone、proposal_generator和roi_head的函数分别都是来自对应文件夹的<code>build_xxx</code>函数。</p><p data-pid="b0tfnem_">在这之后我们继续考察<code>RCNN</code>模型处理函数的过程,我们主要关注<code>forward</code>函数,主要包括如下步骤:</p><ul><li data-pid="-FDVhdEr">利用<code>preprocess_image</code>对输入的图片做初始化,特别是其中会对图片做归一化(normalization),并将图片放到指定的设备(device,也就是gpu)上。</li><li data-pid="xN4M3u4o">在<code>self.backbone(images.tensor)</code>一句,我们可以得到这个图片经过卷积神经网络处理得到的<code>feature</code>。</li><li data-pid="AK6SeOzk">如果我们没有告诉<code>rcnn</code>已经被发现的物体有哪些(<code>detected_instances is None</code>),它就需要自己寻找哪些位置可能有物体出现,并通过<code>self.proposal_generator(images, features)</code>得到可能出现物体的位置<code>proposals</code>。</li><li data-pid="Ik-WUW16">得到了可能出现物体的区域,我们就用<code>self.roi_heads(images, features, proposals)</code>,结合feature的信息对roi区域进行处理,得到每个区域的结果。值得注意的是,如果我们提供了目标结果(<code>gt_instances</code>),那么<code>roi_head</code>计算的就是<code>Loss</code>了。</li></ul><p data-pid="eZUEVxTb">综上,我们知道了<code>RCNN</code>的架构和运行方式,接下来我们会针对它的组件,backbone、proposal_generator和roi_head分别进行研究。</p><h3><b>2.3 <code>backbone</code></b></h3><p data-pid="KF9bzDDO"><code>backbone</code>的类仍然是采用<code>registry</code>的模式,这样方便后继的人对于库进行修改。对于<code>backbone</code>的分析其实不用非常具体,因为BackBone就是最普通的卷积神经网络。但是在<code>detectron2</code>的具体实现中,有下面几点可以简单提一下:</p><ul><li data-pid="kZ7Zlf6T"><code>detectron2</code>的backbone均基于<code>modeling/backbone/backbone.py</code>中的<code>Backbone</code>基类,它在<code>nn.Module</code>上做了简单的包装,主要是针对物体检测和实例分割的特殊需求,比如说:<br></li><ul><li data-pid="_QWRujDg"><code>size_divisibility</code>:因为卷积神经网络涉及到降采样,所以会对输入数据产生要求。例如降采样8倍的网络,其输入的长宽也必须是8的倍数。</li><li data-pid="KjD15wNS"><code>output_shape</code>:返回输出的Feature Map的形状是怎样的,方便后续的proposal_generator和roi_head进行处理。</li></ul></ul><p><br></p><ul><li data-pid="EaCzFc0h">以<code>ResNet</code>的实现为例,它的实现方法和<code><a href="http://link.zhihu.com/?target=https%3A//github.com/pytorch/vision/tree/master/torchvision/models" class=" wrap external" target="_blank" rel="nofollow noreferrer">torchvision</a></code>中对<code>ResNet</code>的实现方式非常像(或者说基本是一样的),只是在<code>build_resnet</code>中制作了生成<code>resnet</code>模型的统一接口。</li></ul><h3><b>2.4 <code>proposal_generator</code></b></h3><p data-pid="AURYRoNI">与<code>backbone</code>相同,<code>proposal_generator</code>也是通过<code>build_proposal_generator</code>的接口创建“从图片的特征图提出可能产生物体的区域”的网络。暂时<code>detectron2</code>实现了两种<code>proposal_generator</code>,一种是<b>Region Proposal Generator</b>,也就是代码库中的<code>rpn</code>;另一种是<b>Rotated Region Proposal Network</b>,也就是<code>rrpn</code>。在下面,我们主要分析<code>RPN</code>的实现。</p><h3><b>2.4.1 <code>RPN Head</code></b></h3><p data-pid="VkUJ734v"><code>RPN</code>作为<code>proposal_generator</code>的一种并不是最细分的抽象层次,最细分的抽象层次是<b>RPN Head</b>,也就是RPN用怎样的结构处理输入的特征图。这也就是在<code>modeling/proposal_generator/rpn.py</code>中<code>RPN_HEAD_REGISTRY</code>的由来。</p><p data-pid="QIafMoyW">在这里,<code>detectron2</code>为我们实现了一个典型的RPN Head——<code>StandardRPNHead</code>。它首先用一个3x3的卷积处理输入的特征图,之后分别用两个1x1的卷积处理得到:<code>objectness_logits</code>,有多大可能是一个物体;<code>anchor_deltas</code>:如何生成一个合理的锚定框(Anchor Box)。在它的输出中,<code>objectness_logits</code>是一个的张量,其中是每个Batch的图片个数,是在每个空间位置上设定的锚定框个数;<code>anchor_deltas</code>和<code>objectness_logits</code>是一一对应的,只不过它的输出变成了,增加了对锚定框位置的描述。</p><h3><b>2.4.2 <code>RPN</code>的<code>forward</code>过程</b></h3><p data-pid="UGffrmnQ">尽管在上一段中我们清楚了RPN Head是如何构成的、它的工作原理是怎样的,但是我们对于RPN最关键的部分:它是如何预测锚定框的、它又是如何训练的仍然一无所知。为了实现这个目的,我们需要仔细研究<code>RPN</code>的实现,最主要的就是它<code>forward</code>的过程。</p><p data-pid="YoxPWkAo"><code>RPN</code>的输入是图片的特征,它通过如下步骤进行处理:</p><ul><li data-pid="FWqAV7qN">首先用<code>anchor_generator</code>生成了<code>anchor</code>的所有可能位置。我们会在稍后详细分析<code>anchor_generator</code>的实现,在这里我们可以把它的输出暂时理解为“一个列表,列表包含了几何意义上的所有可能锚定框”。</li><li data-pid="xCyFSiK6">其次通过刚刚分析过的<code>rpn_head</code>为每个anchor的位置预测了存在物体的可能性和框的可能位置。</li><li data-pid="TvPrJbwY">如果在输入中提供了真实的锚定框信息,也就是<code>gt_instances</code>不是<code>None</code>,那么会通过<code>self.label_and_sample_anchors</code>制作属于“锚定框”的数据集,并由<code>self.losses</code>计算<code>RPN</code>部分的损失函数,以训练<code>RPN Head</code>。</li><li data-pid="lT8bw4oi" ```
TonyRL commented 2 months ago

All articles listed have proper date.