<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Yuxin&#39;s Blog</title>
  <icon>https://www.gravatar.com/avatar/0c9f23b0750710ed47c897702096e1f5</icon>
  
  <link href="https://ppwwyyxx.com/blog/atom.xml" rel="self"/>
  
  <link href="https://ppwwyyxx.com/blog/"/>
  <updated>2025-11-20T08:00:00.000Z</updated>
  <id>https://ppwwyyxx.com/blog/</id>
  
  <author>
    <name>Yuxin Wu</name>
    <email>ppwwyyxxc@gmail.com</email>
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Common Python Reference Cycle Patterns</title>
    <link href="https://ppwwyyxx.com/blog/2025/Ref-Cycle-Patterns/"/>
    <id>https://ppwwyyxx.com/blog/2025/Ref-Cycle-Patterns/</id>
    <published>2025-11-20T08:00:00.000Z</published>
    <updated>2025-11-20T08:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>In Python, when a set of objects constructs a reference cycle, none of them would reach a zero refcount.In this case, even if these objects all go out-of-scope and are no longer accessible, they will not be immediately released.</p><p>The Python ecosystem typically accepts reference cycles as an inevitable issue, and relies on garbage collection (GC) to avoid leaks.A GC is triggered by the Python interpreter from time to time;it will detect all non-reachable objects, and release them regardless of their refcount.</p><p>However, in high performance deep learning systems, GC is not always a good choice.</p><span id="more"></span><ol><li><p>The GPU memory is so precious that we care a lot about its allocations and deallocations.We want a GPU tensor to be freed as soon as possible,and cannot risk leaving them non-reachable and wait for GC to kick in at a later time.</p></li><li><p>GC causes a random, significant pause of the interpreter.This is not acceptable for large-scale synchronous jobs, because once any worker is running GC, all otherswill have to wait for it in order to finish a collective communication.</p><p>Instead, high performance deep learning systems often disable the automatic GC, and run them manually at specific sync point,rather infrequently.See the relevant code in<a href="https://github.com/pytorch/torchtitan/blob/028a455c1e3c0ea5aeb34e04f6ae25401b900bfc/torchtitan/tools/utils.py#L37" aria-label="torchtitan/tools/utils.py &#xB7; pytorch/torchtitan" class="hint--top hint--rounded hint--no-animate hint--no-arrow">torchtitan</a>and <a href="https://github.com/NVIDIA/Megatron-LM/blob/41eecc45ec7b38f7dea237cce6c0df9b8eaaa710/megatron/training/training.py#L1875-L1878" aria-label="megatron/training/training.py &#xB7; NVIDIA/Megatron-LM" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Megatron-LM</a>for example.</p></li></ol><p>Therefore a deep learning system engineer has to <strong>be aware of refcycles and avoid them</strong>.This post shares a few patterns of refcycles that are worth knowing.We&apos;ll also use <a href="https://objgraph.readthedocs.io/" aria-label="Python Object Graphs &#x2014; objgraph 3.6.0 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">objgraph</a> to visualize the refcycle.</p><p>In this post, we&apos;ll assume GC is disabled, so that any object involved in a refcycle is considered &quot;leaked&quot;.</p><h2 id="Refcount-and-objgraph">Refcount and objgraph<a class="markdown-anchor" href="#Refcount-and-objgraph">&#xB6;</a></h2><p>Let&apos;s see a simple circular reference:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> gc<br><span class="hljs-keyword">import</span> sys<br><span class="hljs-keyword">import</span> objgraph<br><span class="hljs-keyword">from</span> types <span class="hljs-keyword">import</span> SimpleNamespace<br><br>obj = <span class="hljs-built_in">object</span>()<br>a, b = SimpleNamespace(obj=obj), SimpleNamespace()<br><span class="hljs-built_in">print</span>(sys.getrefcount(a))  <span class="hljs-comment"># 2</span><br>a.b = b; b.a = a<br><span class="hljs-built_in">print</span>(sys.getrefcount(a))  <span class="hljs-comment"># 3</span><br><span class="hljs-keyword">del</span> a, b<br><br>gc.collect = <span class="hljs-keyword">lambda</span> *_, **__: <span class="hljs-literal">None</span><br>objgraph.show_backrefs([obj], refcounts=<span class="hljs-literal">True</span>, filename=<span class="hljs-string">&apos;backrefs.png&apos;</span>, shortnames=<span class="hljs-literal">False</span>, max_depth=<span class="hljs-number">10</span>)<br></code></pre></td></tr></table></figure><ol><li>The refcount starts at 2, instead of 1. This is normal as <code>getrefcount</code> itself will also temporarily refer to the object.</li><li>Then we create a refcycle, the refcount becomes 3.</li><li>We disable GC with a monkey-patch, because <code>objgraph</code> internally would call <code>gc.collect</code> and remove our refcycle.</li><li>A reference graph is plotted which shows the refcycle:</li></ol><img src="/blog/2025/Ref-Cycle-Patterns/naive-refcycle.png" class="center" width="600"><h2 id="Implicit-references-to-self">Implicit references to <code>self</code><a class="markdown-anchor" href="#Implicit-references-to-self">&#xB6;</a></h2><p>In code reviews, I often frown upon any closure that captures <code>self</code>: when a closurerefers to <code>self</code>, we have to make sure <code>self</code> does not directly or indirectly refer to the closure.Below is a simple example of a refcycle that may arise:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> sys<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span>:<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, obj</span>):<br>        <span class="hljs-variable language_">self</span>.obj = obj<br>        <span class="hljs-variable language_">self</span>.print_obj = <span class="hljs-keyword">lambda</span>: <span class="hljs-built_in">print</span>(<span class="hljs-variable language_">self</span>.obj)<br>obj = <span class="hljs-built_in">object</span>()<br><span class="hljs-keyword">for</span> k <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">10</span>):<br>    a = A(obj)<br>    <span class="hljs-built_in">print</span>(sys.getrefcount(obj))  <span class="hljs-comment"># Refcount keep increasing!</span><br></code></pre></td></tr></table></figure><img src="/blog/2025/Ref-Cycle-Patterns/self-refcycle.png" class="center" width="600"><p>Any bound method also has a reference to <code>self</code>. So the following creates a refcycle as well:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> sys<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span>:<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, obj</span>):<br>        <span class="hljs-variable language_">self</span>.obj = obj<br>        <span class="hljs-variable language_">self</span>.method_alias = <span class="hljs-variable language_">self</span>.method<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">method</span>(<span class="hljs-params">self</span>):<br>        <span class="hljs-keyword">pass</span><br>obj = <span class="hljs-built_in">object</span>()<br><span class="hljs-keyword">for</span> k <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">10</span>):<br>    a = A(obj)<br>    <span class="hljs-built_in">print</span>(sys.getrefcount(obj))  <span class="hljs-comment"># Refcount keep increasing!</span><br></code></pre></td></tr></table></figure><p>The lesson learned from both examples is to prefer defining methods using the normal <code>def</code> syntax, instead of assignments.</p><p>Similarly, code below is an alarming pattern, because the active thread keeps a reference to <code>self</code>,despite <code>self</code> owning the thread.</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):<br>    <span class="hljs-variable language_">self</span>._thread = threading.Thread(target=<span class="hljs-variable language_">self</span>._worker)<br>    <span class="hljs-variable language_">self</span>._thread.start()<br></code></pre></td></tr></table></figure><h2 id="Recursive-closure-functions">Recursive closure functions<a class="markdown-anchor" href="#Recursive-closure-functions">&#xB6;</a></h2><p>In addition to capturing <code>self</code>, a closure that&apos;s a recursive function is also a refcycle by itself,because it captures the closure definition.</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>(<span class="hljs-params">obj</span>):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">foo</span>(<span class="hljs-params">n</span>):<br>        <span class="hljs-keyword">if</span> n == <span class="hljs-number">0</span>: <span class="hljs-keyword">return</span> <span class="hljs-number">0</span><br>        <span class="hljs-keyword">return</span> foo(n-<span class="hljs-number">1</span>) + obj<br>    result = foo(<span class="hljs-number">3</span>)<br><br>obj = <span class="hljs-number">12313123123</span><br><span class="hljs-keyword">for</span> k <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">10</span>):<br>    func(obj)<br>    <span class="hljs-built_in">print</span>(sys.getrefcount(obj))  <span class="hljs-comment"># Refcount keep increasing!</span><br></code></pre></td></tr></table></figure><p>In the example above, the function <code>foo</code> will leak because it refers to itself.This is sometimes OK because a function definition is cheap.But leaking <code>foo</code> causes <code>obj</code> to leak as well because <code>foo</code> refers to <code>obj</code>.This could be a severe problem when <code>obj</code> is a resource-heavy object.</p><p>A simple fix to this example is to remove the function object from the local scope after using it:</p><figure class="highlight diff"><table><tr><td class="code"><pre><code class="hljs diff"><span class="hljs-deletion">- result = foo(3)</span><br><span class="hljs-addition">+ try:</span><br><span class="hljs-addition">+     result = foo(3)</span><br><span class="hljs-addition">+ finally:</span><br><span class="hljs-addition">+     foo = None</span><br></code></pre></td></tr></table></figure><p>PyTorch has seen a few similar bugs in the past where tensors get leaked due to recursive local functions.For example,<a href="https://github.com/pytorch/pytorch/commit/885c8741677c" aria-label="Fix refcycles in DataParallel scatter and gather (#4988) &#xB7; pytorch/pytorch@885c874" class="hint--top hint--rounded hint--no-animate hint--no-arrow">[1]</a>,<a href="https://github.com/pytorch/pytorch/blob/512dd79ff030f17be22199daca84a0bbde2f3175/torch/distributed/utils.py#L150" aria-label="torch/distributed/utils.py &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">[2]</a>and a more complicated one involving functions calling each other:<a href="https://github.com/pytorch/pytorch/pull/136507" aria-label="[pipelining] fix py ref cycle in stage_backward by wconstab &#xB7; Pull Request #136507 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">[3]</a>.@dzhulgakov recently posted <a href="https://x.com/dzhulgakov/status/1985513916915712110">another example</a> on X.</p><h2 id="Class-definition-is-self-referential">Class definition is self-referential<a class="markdown-anchor" href="#Class-definition-is-self-referential">&#xB6;</a></h2><p>Keep in mind that <strong>ANY</strong> Python class definition is self-referential -- i.e. <strong>any class definition will be leaked</strong>.This has been <a href="https://bugs.python.org/issue17950" aria-label="Issue 17950: Dynamic classes contain non-breakable reference cycles - Python tracker" class="hint--top hint--rounded hint--no-animate hint--no-arrow">discussed</a> in the CPython tracker a long time ago and hasn&apos;t been fixed.It is typically fine, as most class definitions are cheap, and they are createdat import time and expected to survive the entire lifetime of the program anyway.</p><p>However, similar to recursive functions, it is a giant footgun to define a class that is a closure on local objects --the local objects will be leaked. The following example demonstrates this:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> sys<br><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>(<span class="hljs-params">obj</span>):<br>    <span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span>:<br>        <span class="hljs-keyword">def</span> <span class="hljs-title function_">meth</span>(<span class="hljs-params">self, x</span>):<br>            <span class="hljs-keyword">return</span> obj[x]<br><br>x = <span class="hljs-built_in">object</span>()<br><span class="hljs-keyword">for</span> k <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">10</span>):<br>    func(x)<br>    <span class="hljs-built_in">print</span>(sys.getrefcount(x))  <span class="hljs-comment"># Refcount keep increasing!</span><br></code></pre></td></tr></table></figure><p>This pattern has caused <code>torch.save</code> to <a href="https://github.com/pytorch/pytorch/pull/165204" aria-label="Quick fix of torch.save memory leak by ppwwyyxx &#xB7; Pull Request #165204 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">leak memory</a>.The fix is also to set the class definition to <code>None</code> after use.</p><h2 id="Exception-refers-to-its-stack">Exception refers to its stack<a class="markdown-anchor" href="#Exception-refers-to-its-stack">&#xB6;</a></h2><p>An exception stores its stack trace with all stack frames.Storing an exception object to a variable will create a refcycle&quot;frame &#x2192; variables of the frame &#x2192; exception object &#x2192; stack &#x2192; frame&quot;.This leaks the exception object, together with the <strong>entire stack</strong>!</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> sys<br><span class="hljs-keyword">def</span> <span class="hljs-title function_">f</span>(<span class="hljs-params">obj</span>):<br>    <span class="hljs-keyword">try</span>:<br>        <span class="hljs-keyword">assert</span> <span class="hljs-literal">False</span><br>    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:<br>        err = e<br>obj = <span class="hljs-built_in">object</span>()<br><span class="hljs-keyword">for</span> k <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">10</span>):<br>    f(obj)<br>    <span class="hljs-built_in">print</span>(sys.getrefcount(obj))  <span class="hljs-comment"># Refcount keep increasing!</span><br></code></pre></td></tr></table></figure><img src="/blog/2025/Ref-Cycle-Patterns/exc-refcycle.png" class="center" width="600"><p>This is a big problem, because any library we depend on may have a seemingly innocent <code>err = e</code> statement,and it will cause the <strong>entire stack</strong> that calls into this library to leak in a very hard-to-debug way.<a href="https://github.com/paramiko/paramiko/pull/2525" aria-label="Do not keep ImportError as global var by blahgeek &#xB7; Pull Request #2525 &#xB7; paramiko/paramiko" class="hint--top hint--rounded hint--no-animate hint--no-arrow">This PR</a> is an example where a 3rd-party library storingthe exception costs 100+GB of memory leak.</p><p>To properly store an exception, PyTorch provides a nice <code>ExceptionWrapper</code> that avoids refcycles.See <a href="https://github.com/pytorch/pytorch/blob/d01a7b0241ed1c4cded7e7ca097249feb343f072/torch/_utils.py#L720-L775" aria-label="torch/_utils.py &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">its comments</a>for details.</p><h2 id="PyTorch-autograd-related-refcycle">PyTorch <code>autograd</code>-related refcycle<a class="markdown-anchor" href="#PyTorch-autograd-related-refcycle">&#xB6;</a></h2><p>In autograd, any tensor in the graph has a <code>grad_fn</code> that refers to a node in the autograd graph,which may, in cases of custom <code>autograd.Function</code> or custom <code>saved_tensor_hook</code>, refer backto the tensor itself.<a href="https://github.com/pytorch/pytorch/issues/25340" aria-label="Memory leak: autograd contexts not being garbage collected when assigned output Tensors as attributes &#xB7; Issue #25340 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">[1]</a><a href="https://github.com/pytorch/pytorch/issues/115255" aria-label="lambda saved_tensor_hook memory leak? &#xB7; Issue #115255 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">[2]</a><a href="https://github.com/pytorch/pytorch/issues/94990" aria-label="interactions between views + autograd.Function + AOTAutograd causes memory leak &#xB7; Issue #94990 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">[3]</a><a href="https://gist.github.com/ppwwyyxx/f6f089f60882c309ffd1740ed2655fc3" aria-label="weird autograd leak" class="hint--top hint--rounded hint--no-animate hint--no-arrow">[4]</a>are some bugs of this family.What&apos;s worse is that the cycles involve C++ objects, therefore <code>gc.collect()</code> would not help.</p><p>The fix is to always apply a <code>detach()</code> whenever we need to manually save a tensor for backward(even if the tensor does not require grad).</p><h2 id="MagicMock-and-DictConfig-are-self-referential"><code>MagicMock</code> and <code>DictConfig</code> are self-referential<a class="markdown-anchor" href="#MagicMock-and-DictConfig-are-self-referential">&#xB6;</a></h2><p>A <code>unittest.mock.MagicMock</code> has some &quot;magic&quot; that allows mocked methods and sub-mocks (mocks created from a mock) toaccess their parents. This introduces refcycles:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> sys<br><span class="hljs-keyword">from</span> unittest.mock <span class="hljs-keyword">import</span> MagicMock<br><span class="hljs-keyword">def</span> <span class="hljs-title function_">f</span>(<span class="hljs-params">x</span>):<br>    mock = MagicMock();<br>    mock(x)<br><br>x = <span class="hljs-built_in">object</span>()<br><span class="hljs-keyword">for</span> k <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">10</span>):<br>   f(x)<br>   <span class="hljs-built_in">print</span>(sys.getrefcount(x))  <span class="hljs-comment"># Refcount keep increasing!</span><br></code></pre></td></tr></table></figure><p>I attempted to <a href="https://github.com/python/cpython/pull/139937" aria-label="gh-133423: Avoid ref cycle among MagicMock and its magic methods by ppwwyyxx &#xB7; Pull Request #139937 &#xB7; python/cpython" class="hint--top hint--rounded hint--no-animate hint--no-arrow">fix it</a> in CPython but it wasn&apos;t accepted.</p><p>Due to this issue, we should be careful when using <code>mock</code> in resource-heavy unittests (e.g. GPU tests) --some intermediate results may get leaked.</p><p><a href="https://github.com/omry/omegaconf" aria-label="omry/omegaconf: Flexible Python configuration system. The last one you will ever need." class="hint--top hint--rounded hint--no-animate hint--no-arrow">omegaconf</a> is a config library. Its config object is self-referential:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> omegaconf <span class="hljs-keyword">import</span> DictConfig<br><span class="hljs-keyword">import</span> sys<br><span class="hljs-keyword">def</span> <span class="hljs-title function_">f</span>(<span class="hljs-params">obj</span>):<br>    x = DictConfig({<span class="hljs-string">&apos;a&apos;</span>: obj}, flags={<span class="hljs-string">&apos;allow_objects&apos;</span>: <span class="hljs-literal">True</span>})<br>obj = <span class="hljs-built_in">object</span>()<br><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(<span class="hljs-number">5</span>):<br>    f(obj)<br>    <span class="hljs-built_in">print</span>(sys.getrefcount(obj))  <span class="hljs-comment"># Refcount keep increasing!</span><br></code></pre></td></tr></table></figure><p>This is because it allows a sub-config node to access its parent.We should keep this in mind when storing any resource-expensive objects in the config.</p><p>More generally,when any object has access to its conceptual parent/owner, i.e. if <code>A</code> owns <code>B</code> but <code>B</code> has access to <code>A</code>,care has to be taken to avoid refcycle.Ideally, we would structure software such that an owner has access to its children objects, but not the opposite.But it is often not the reality.Bidirectional access is helpful when flexibility is preferred, as we have seen in <code>MagicMock</code> and <code>DictConfig</code>.</p><p>As another example, detectron2&apos;s trainer owns several hooks -- which are functions to execute during training.These hooks themselves may need access to the trainer -- in order to access information about the training.A <a href="https://github.com/facebookresearch/detectron2/blob/fd27788985af0f4ca800bca563acdb700bb890e2/detectron2/engine/train_loop.py#L132-L133" aria-label="detectron2/engine/train_loop.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">weakref is used</a>there to avoid refcycle.</p><h2 id="Summary">Summary<a class="markdown-anchor" href="#Summary">&#xB6;</a></h2><p>Eliminating refcycles is important in deep learning systems.This post lists a few patterns of refcycle that I have seen frequently when debugging memory leaks.When needed, <code>objgraph</code> can be helpful to identify refcycles.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;In Python, when a set of objects constructs a reference cycle, none of them would reach a zero refcount.
In this case, even if these objects all go out-of-scope and are no longer accessible, they will not be immediately released.&lt;/p&gt;
&lt;p&gt;The Python ecosystem typically accepts reference cycles as an inevitable issue, and relies on garbage collection (GC) to avoid leaks.
A GC is triggered by the Python interpreter from time to time;
it will detect all non-reachable objects, and release them regardless of their refcount.&lt;/p&gt;
&lt;p&gt;However, in high performance deep learning systems, GC is not always a good choice.&lt;/p&gt;</summary>
    
    
    
    
    <category term="PyTorch" scheme="https://ppwwyyxx.com/blog/tags/PyTorch/"/>
    
    <category term="Python" scheme="https://ppwwyyxx.com/blog/tags/Python/"/>
    
    <category term="Programming" scheme="https://ppwwyyxx.com/blog/tags/Programming/"/>
    
  </entry>
  
  <entry>
    <title>写在 wechat-dump 项目的第十年</title>
    <link href="https://ppwwyyxx.com/blog/2025/wechat-dump-10-years/"/>
    <id>https://ppwwyyxx.com/blog/2025/wechat-dump-10-years/</id>
    <published>2025-01-31T08:00:00.000Z</published>
    <updated>2025-01-31T08:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>&#x5728;&#x8FC7;&#x5E74;&#x7684;&#x8FD9;&#x51E0;&#x5929;, &#x4E3A;&#x4E86;&#x4ECE;&#x7126;&#x8651;&#x7684;&#x5DE5;&#x4F5C;&#x4E2D;&#x6362;&#x4E00;&#x4E2A;&#x5FC3;&#x60C5;, &#x6211;&#x7ED9;&#x6211;&#x7684;<a href="https://github.com/ppwwyyxx/wechat-dump" aria-label="ppwwyyxx/wechat-dump: Cracking encrypted wechat message history from android" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> wechat-dump</a> &#x9879;&#x76EE;&#x6DFB;&#x52A0;&#x4E86;&#x51E0;&#x4E2A;&#x5F53;&#x5E74;&#x6CA1;&#x505A;&#x51FA;&#x6765;&#x7684;&#x529F;&#x80FD;, &#x89E3;&#x51B3;&#x4E86;&#x4E00;&#x4E9B;&#x9057;&#x7559;&#x95EE;&#x9898;. &#x610F;&#x5916;&#x7684;&#x53D1;&#x73B0;&#x8FD9;&#x4E2A;&#x9879;&#x76EE;&#x59CB;&#x4E8E; 2014 &#x5E74;&#x672B;, &#x5230;&#x4ECA;&#x5929;&#x5DF2;&#x7ECF;&#x8D85;&#x8FC7;&#x5341;&#x5E74;&#x4E86;. &#x6709;&#x591A;&#x5C11;&#x4EBA;&#x4F1A;&#x6709;&#x7ED9;&#x81EA;&#x5DF1;&#x5341;&#x5E74;&#x524D;&#x7684;&#x4EE3;&#x7801;&#x8865;&#x5145;&#x65B0; feature &#x7684;&#x7ECF;&#x5386;&#x5462;? &#x7A81;&#x7136;&#x6709;&#x4E86;&#x4E00;&#x4E9B;&#x611F;&#x89E6;&#x60F3;&#x8981;&#x5199;&#x4E0B;&#x6765;.</p><span id="more"></span><p>&#x7B80;&#x5355;&#x4ECB;&#x7ECD;&#x4E00;&#x4E0B;, wechat-dump &#x9879;&#x76EE;&#x5C31;&#x662F;&#x4ECE;&#x4E00;&#x4E2A;&#x8D8A;&#x72F1;&#x5B89;&#x5353;&#x624B;&#x673A;&#x4E0A;,  &#x628A;&#x5FAE;&#x4FE1;&#x804A;&#x5929;&#x8BB0;&#x5F55;&#x5C3D;&#x53EF;&#x80FD;&#x5B8C;&#x6574;&#x7684;&#x7ED3;&#x6784;&#x5316;&#x5BFC;&#x51FA; - &#x5305;&#x62EC;&#x8BED;&#x97F3; / &#x56FE;&#x50CF; / &#x89C6;&#x9891; / &#x8868;&#x60C5;&#x5305;&#x7B49; (&#x7528;&#x65F6;&#x9AE6;&#x8BDD;&#x8BF4;&#x53EB;&#x591A;&#x6A21;&#x6001;?). &#x4E3B;&#x8981;&#x7684;&#x6280;&#x672F;&#x96BE;&#x70B9;&#x662F;&#x9006;&#x5411;: &#x641E;&#x6E05;&#x695A;&#x5982;&#x4F55;&#x83B7;&#x53D6;, &#x89E3;&#x6790;&#x5FAE;&#x4FE1;&#x7684;&#x5404;&#x79CD;&#x7C7B;&#x578B;&#x7684;&#x804A;&#x5929;&#x8BB0;&#x5F55;.</p><p>&#x8FD9;&#x4E2A;&#x9879;&#x76EE;&#x672C;&#x8EAB;&#x5012;&#x6CA1;&#x4EC0;&#x4E48;&#x503C;&#x5F97;&#x9A84;&#x50B2;&#x7684;: &#x5B83;&#x4E0D;&#x662F;&#x4E00;&#x4E2A;&#x591A;&#x4E48;&#x6709;&#x7528;&#x7684;&#x4E1C;&#x897F; -- &#x6709;&#x8FD9;&#x79CD;&#x9700;&#x6C42;&#x548C;&#x8BBE;&#x5907;&#x7684;&#x4EBA;&#x672C;&#x6765;&#x5C31;&#x4E0D;&#x591A;;  &#x5B83;&#x7684;&#x4EE3;&#x7801;&#x4E5F;&#x6CA1;&#x6709;&#x5199;&#x7684;&#x591A;&#x597D; -- &#x5F88;&#x591A;&#x5730;&#x65B9;&#x662F; &quot;&#x80FD;&#x7528;&#x5C31;&#x884C;&quot; &#x7684;, &#x6BD5;&#x7ADF;&#x4E5F;&#x7528;&#x4E0D;&#x4E86;&#x51E0;&#x6B21;;  &#x6211;&#x5E76;&#x4E0D;&#x64C5;&#x957F;&#x9006;&#x5411;, &#x4E3A;&#x5B83;&#x82B1;&#x8D39;&#x7684;&#x7CBE;&#x529B;&#x4E5F;&#x8FDC;&#x8FDC;&#x4E0D;&#x5982;&#x5176;&#x5B83;&#x6211;&#x7528;&#x5FC3;&#x505A;&#x7684;&#x5F00;&#x6E90;&#x9879;&#x76EE;. &#x7136;&#x800C;&#x5B83;&#x662F;&#x552F;&#x4E00;&#x4E00;&#x4E2A;&#x6211;&#x5F00;&#x53D1;&#x4E86;&#x8D85;&#x8FC7;&#x5341;&#x5E74;&#x7684;&#x9879;&#x76EE; -- &#x8FD9;&#x91CC;&#x7684;&#x5F00;&#x53D1;&#x4E0D;&#x4EC5;&#x4EC5;&#x6307;&#x7EF4;&#x62A4;, &#x800C;&#x662F;&#x786E;&#x5B9E;&#x6709;&#x6DFB;&#x52A0;&#x65B0;&#x529F;&#x80FD;, &#x751A;&#x81F3;&#x89E3;&#x51B3;&#x81EA;&#x5DF1;&#x66FE;&#x7ECF;&#x6CA1;&#x6709;&#x89E3;&#x51B3;&#x7684;&#x6280;&#x672F;&#x95EE;&#x9898;.</p><h2 id="&#x4E09;&#x4EF6;&#x5C0F;&#x4E8B;">&#x4E09;&#x4EF6;&#x5C0F;&#x4E8B;<a class="markdown-anchor" href="#&#x4E09;&#x4EF6;&#x5C0F;&#x4E8B;"> &#xB6;</a></h2><p>&#x8FD9;&#x4E2A;&#x9879;&#x76EE;&#x7684;&#x6700;&#x521D;&#x4E24;&#x5E74;, &#x4E3B;&#x8981;&#x5185;&#x5BB9;&#x8FD8;&#x662F;&#x8D81;&#x7740;&#x6709;&#x7A7A;&#x7684;&#x65F6;&#x95F4;&#x6DFB;&#x52A0;&#x5BF9;&#x5404;&#x7C7B;&#x6D88;&#x606F;&#x7684;&#x652F;&#x6301;, &#x5305;&#x62EC;:</p><ul><li>&#x89E3;&#x6790;&#x5404;&#x79CD;&#x7C7B;&#x578B;&#x7684;&#x6D88;&#x606F; (&#x6587;&#x5B57;, &#x94FE;&#x63A5;, &#x6587;&#x4EF6;, &#x591A;&#x5A92;&#x4F53;, &#x7EA2;&#x5305;, &#x540D;&#x7247;, &#x7B49;&#x7B49;..) &#x5E76;&#x505A;&#x524D;&#x7AEF;&#x6E32;&#x67D3;</li><li>&#x89E3;&#x7801;&#x97F3;&#x9891; (&#x5FAE;&#x4FE1;&#x5148;&#x540E;&#x4F7F;&#x7528;&#x4E86; AMR &#x548C; SILK &#x4E24;&#x79CD;&#x683C;&#x5F0F;), &#x5934;&#x50CF; (&#x5B58;&#x50A8;&#x5728;&#x4E00;&#x4E2A;&#x5947;&#x602A;&#x7684;&#x683C;&#x5F0F;&#x91CC;), &#x56FE;&#x7247;&#x7B49;.</li></ul><p>&#x8FD9;&#x91CC;&#x5927;&#x90E8;&#x5206;&#x529F;&#x80FD;&#x7684;&#x5B9E;&#x73B0;&#x6BD4;&#x8F83; straightforward. &#x89E3;&#x7801;&#x97F3;&#x9891;&#x7528;&#x5230;&#x4E86;&#x4E24;&#x4E2A;&#x7B2C;&#x4E09;&#x65B9;&#x97F3;&#x9891;&#x5E93;; &#x5934;&#x50CF;&#x7684;&#x5B58;&#x50A8;&#x683C;&#x5F0F;&#x6211;&#x4E00;&#x5F00;&#x59CB;&#x6CA1;&#x505A;, &#x662F;&#x7531;&#x793E;&#x533A;&#x7528;&#x6237;&#x8D21;&#x732E;&#x7684;. &#x5B9E;&#x73B0;&#x4E86;&#x8FD9;&#x4E9B;&#x4E4B;&#x540E;, &#x9879;&#x76EE;&#x5C31;&#x8FDB;&#x5165;&#x4E86;&#x6BD4;&#x8F83;&#x957F;&#x671F;&#x7684;&#x7EF4;&#x62A4;&#x72B6;&#x6001;. &#x540E;&#x9762;&#x5341;&#x5E74;&#x91CC;&#x53EA;&#x505A;&#x8FC7;&#x4E24;&#x6B21;&#x5927;&#x7684;&#x66F4;&#x65B0;, &#x5176;&#x4E2D;&#x6709;&#x4E00;&#x4E9B;&#x6709;&#x8DA3;&#x7684;&#x6280;&#x672F;&#x8FDB;&#x6B65;.</p><h3 id="SQLCipher-&#x53C2;&#x6570;">SQLCipher &#x53C2;&#x6570;<a class="markdown-anchor" href="#SQLCipher-&#x53C2;&#x6570;"> &#xB6;</a></h3><p>&#x5FAE;&#x4FE1;&#x4F7F;&#x7528;&#x5E26;&#x52A0;&#x5BC6;&#x7684; sqlite database, &#x4E5F;&#x5C31;&#x662F; <a href="https://github.com/sqlcipher/sqlcipher/" aria-label="sqlcipher/sqlcipher: SQLCipher is a standalone fork of SQLite that adds 256 bit AES encryption of database files and other security features." class="hint--top hint--rounded hint--no-animate hint--no-arrow">sqlcipher</a>. &#x89E3;&#x5BC6;&#x7684;&#x79D8;&#x94A5;&#x83B7;&#x53D6;&#x65B9;&#x5F0F;&#x65E9;&#x5DF2;&#x88AB;&#x7834;&#x89E3;,  &#x4F46;&#x7F51;&#x4E0A;&#x5BF9;&#x4E8E;&#x89E3;&#x5BC6;&#x4F7F;&#x7528;&#x7684;&#x5177;&#x4F53;&#x53C2;&#x6570;&#x4E00;&#x76F4;&#x8BF4;&#x6CD5;&#x4E0D;&#x4E00;. &#x6211;&#x5728;&#x9879;&#x76EE;&#x4E2D;&#x8D77;&#x521D;&#x4F7F;&#x7528;&#x8FD9;&#x6837;&#x4E00;&#x7EC4;&#x53C2;&#x6570;:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">PRAGMA cipher_use_hmac = OFF;<br>PRAGMA cipher_page_size = 1024;<br>PRAGMA kdf_iter = 4000;<br></code></pre></td></tr></table></figure><p>&#x4F46;&#x51E0;&#x5E74;&#x4E2D;&#x65F6;&#x4E0D;&#x65F6;&#x770B;&#x5230;&#x6709;&#x4EBA;&#x62A5;&#x544A;&#x8BF4;&#x89E3;&#x5BC6;&#x4E0D;&#x4E86;&#x6216;&#x8005;&#x52A0;&#x4E86;&#x5176;&#x4ED6;&#x53C2;&#x6570;&#x624D;&#x80FD;&#x6210;&#x529F;&#x89E3;&#x5BC6;.</p><p>2020 &#x5E74;&#x6211;&#x60F3;&#x89E3;&#x51B3;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;, &#x4E8E;&#x662F;&#x53BB;&#x7FFB;&#x770B;&#x4E86; sqlcipher &#x7684;&#x6E90;&#x7801;. &#x7814;&#x7A76;&#x4E00;&#x756A;&#x4E4B;&#x540E;&#x4E0D;&#x96BE;&#x53D1;&#x73B0;&#x5B83;&#x5904;&#x7406;&#x517C;&#x5BB9;&#x6027;&#x7684;<a href="https://github.com/sqlcipher/sqlcipher/commit/e4b66d6cc8a2b7547a32ff2c3ac52f148eba3516" aria-label="adds PRAGMA cipher_compatibility and cipher_default_compatibility &#xB7; sqlcipher/sqlcipher@e4b66d6" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x76F8;&#x5173;&#x539F;&#x7406;</a>: &#x539F;&#x6765; sqlcipher &#x4E0D;&#x540C;&#x7684;&#x7248;&#x672C;&#x6709;&#x4E0D;&#x540C;&#x7684;&#x9ED8;&#x8BA4;&#x53C2;&#x6570; (&#x4E0D;&#x6B62;&#x4E0A;&#x9762;&#x7684;&#x8FD9;&#x4E09;&#x4E2A;), &#x5FAE;&#x4FE1;&#x4F7F;&#x7528;&#x7684;&#x662F;&#x6700;&#x65E9;&#x7684; &quot;&#x7248;&#x672C; 1&quot;. &#x5728;&#x6709;&#x4E9B;&#x540E;&#x7EED;&#x7248;&#x672C;&#x91CC;, &#x66F4;&#x6539;&#x4E0A;&#x9762;&#x4E09;&#x4E2A;&#x53C2;&#x6570;&#x5C31;&#x80FD;&#x56DE;&#x5230;&#x7248;&#x672C; 1 &#x7684;&#x884C;&#x4E3A;;  &#x4F46;&#x5728;&#x6709;&#x4E9B;&#x7248;&#x672C;&#x7684;&#x9ED8;&#x8BA4;&#x53C2;&#x6570;&#x4E0B;, &#x66F4;&#x6539;&#x4E0A;&#x9762;&#x4E09;&#x4E2A;&#x53C2;&#x6570;&#x5E76;&#x4E0D;&#x80FD;&#x56DE;&#x5230;&#x7248;&#x672C; 1 &#x7684;&#x884C;&#x4E3A;.</p><p>&#x7814;&#x7A76;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#x5E76;&#x6CA1;&#x6709;&#x82B1;&#x591A;&#x4E45;, &#x4F46;&#x6211;&#x89C9;&#x5F97;&#x4ECE;&#x67D0;&#x79CD;&#x7A0B;&#x5EA6;&#x4E0A;&#x4F53;&#x73B0;&#x4E86;<strong>&#x89E3;&#x51B3;&#x95EE;&#x9898;&#x7684;&#x4E60;&#x60EF;</strong>&#x7684;&#x53D8;&#x5316;:</p><ul><li><p>&#x5728;&#x5341;&#x5E74;&#x524D;, &#x6211;&#x770B;&#x5230;&#x522B;&#x4EBA;&#x5728;&#x7F51;&#x4E0A;&#x7ED9;&#x51FA;&#x7684;&#x8FD9;&#x4E9B;&#x53C2;&#x6570;&#x5C31;&#x4F1A;&#x53BB;&#x7528;, &#x5982;&#x679C;&#x9047;&#x5230;&#x95EE;&#x9898;&#x5C31;&#x6362;&#x53C2;&#x6570;&#x8BD5;&#x8BD5; -- &#x8FD9;&#x4E9B;&#x90FD;&#x662F;<strong>&#x4E0D;&#x6C42;&#x751A;&#x89E3;</strong>&#x7684;&#x505A;&#x4E8B;&#x65B9;&#x5F0F;.</p></li><li><p>&#x800C;&#x5230;&#x4E86; 2020 &#x5E74;, &#x90A3;&#x65F6;&#x5019;&#x7684;&#x6211;&#x5DF2;&#x7ECF;&#x4E60;&#x60EF;&#x9047;&#x4E8B;&#x4E0D;&#x51B3;&#x770B;&#x6E90;&#x7801;, &#x770B;&#x770B;&#x8FD9;&#x4E9B;&#x53C2;&#x6570;&#x7684;&#x8BBE;&#x7F6E;&#x6D41;&#x7A0B;&#x5230;&#x5E95;&#x662F;&#x600E;&#x4E48;&#x56DE;&#x4E8B;. &#x5DF2;&#x7ECF;&#x4E60;&#x60EF;&#x4E86; &quot;&#x81EA;&#x5DF1;&#x9047;&#x5230;&#x7684;&#x95EE;&#x9898;&#x5F88;&#x53EF;&#x80FD;&#x6CA1;&#x4EBA;&#x9047;&#x5230;&#x8FC7;&quot;, &#x4ECE;&#x5E95;&#x5C42;&#x548C;&#x7B2C;&#x4E00;&#x6027;&#x539F;&#x7406;&#x53BB;&#x5206;&#x6790;&#x6280;&#x672F;&#x95EE;&#x9898;: <strong>&#x5982;&#x679C;&#x89E3;&#x51B3;&#x4E0D;&#x4E86;, &#x5C31;&#x8D70;&#x5230;&#x66F4;&#x5E95;&#x5C42;</strong>. &#x8FD9;&#x79CD;&#x4E60;&#x60EF;&#x5176;&#x5B9E;&#x4E5F;&#x6765;&#x6E90;&#x4E8E;&#x5BF9;&#x81EA;&#x5DF1;&#x80FD;&#x529B;&#x7684;&#x8D8A;&#x6765;&#x8D8A;&#x81EA;&#x4FE1;: &#x76F8;&#x4FE1;&#x80FD;&#x5F88;&#x5FEB;&#x770B;&#x61C2;&#x4E00;&#x4E2A;&#x964C;&#x751F;&#x7684;&#x9879;&#x76EE;, &#x4ECE;&#x6E90;&#x7801;&#x4E2D;&#x627E;&#x5230;&#x4E00;&#x4E2A;&#x95EE;&#x9898;&#x6700;&#x672C;&#x8D28;&#x7684;&#x539F;&#x56E0;, &#x5E76;&#x4E14;&#x81EA;&#x5DF1;&#x627E;&#x5230;&#x7684;&#x7B54;&#x6848;&#x5927;&#x6982;&#x7387;&#x4F1A;&#x6BD4;&#x7F51;&#x4E0A;&#x7684;&#x66F4;&#x597D;</p><p>&#x540C;&#x6837;&#x662F; 2020 &#x5E74;, &#x6211;&#x4ECE;&#x4E00;&#x4E2A;<a href="https://github.com/opencv/opencv-python/issues/381" aria-label="segfault in docker when used with torch and matplotlib &#xB7; Issue #381 &#xB7; opencv/opencv-python" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> OpenCV &#x7684;&#x5D29;&#x6E83;</a> &#x4E00;&#x8DEF;&#x67E5;&#x5230;&#x4E86; glibc &#x91CC;&#x7684; bug; &#x4ECE; PyTorch, TensorFlow &#x91CC;&#x53D1;&#x73B0; / &#x89E3;&#x51B3;&#x4E86;<a href="/blog/2020/Fight-Against-Silent-Bugs-in-Deep-Learning-Libraries/" aria-label="Fight Against Silent Bugs in Deep Learning Libraries" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x597D;&#x51E0;&#x4E2A; silent correctness bug</a>. &#x4ECE;&#x90A3;&#x4E4B;&#x540E;, &#x89C9;&#x5F97;&#x597D;&#x50CF;&#x5DF2;&#x7ECF;&#x6CA1;&#x6709;&#x4EC0;&#x4E48; bug &#x80FD;&#x96BE;&#x5012;&#x81EA;&#x5DF1;&#x4E86;.</p></li><li><p>&#x81EA;&#x5DF1;&#x7684;&#x8FD9;&#x79CD;&#x6210;&#x957F;, &#x5F88;&#x5927;&#x7A0B;&#x5EA6;&#x4E0A;&#x5F52;&#x529F;&#x4E8E;&#x5728;&#x5927;&#x5382;&#x7684; monorepo &#x91CC;&#x5F00;&#x53D1;&#x9879;&#x76EE;: monorepo &#x63D0;&#x4F9B;&#x4E86;&#x4E00;&#x4E2A;&#x65B9;&#x4FBF;&#x7684;&#x73AF;&#x5883;,  &#x8BA9;&#x5DE5;&#x7A0B;&#x5E08;&#x53EF;&#x4EE5;&#x81EA;&#x7531;&#x7684; navigate &#x6240;&#x6709;&#x7684;&#x4EE3;&#x7801;&#x5E76;&#x4E14;&#x8C03;&#x8BD5;&#x6240;&#x6709;&#x7684;&#x4F9D;&#x8D56;, &#x975E;&#x5E38;&#x9002;&#x5408;&#x953B;&#x70BC; &quot;&#x5FEB;&#x901F;&#x8C03;&#x8BD5;&#x964C;&#x751F;&#x4EE3;&#x7801;&quot; &#x7684;&#x80FD;&#x529B;. &#x5728; Google &#x65F6;&#x8FD9;&#x79CD;&#x611F;&#x89C9;&#x5C24;&#x4E3A;&#x660E;&#x663E;, &#x56E0;&#x4E3A;: (1) Google &#x7684;&#x5DE5;&#x7A0B;&#x89C4;&#x8303;&#x5728;&#x516C;&#x53F8;&#x8303;&#x56F4;&#x5185;&#x4E00;&#x81F4;&#x6027;&#x66F4;&#x597D;, &#x964C;&#x751F;&#x4EE3;&#x7801;&#x66F4;&#x5BB9;&#x6613;&#x7406;&#x89E3;. &#x7528;&#x6211;&#x4E00;&#x4E2A; manager &#x7684;&#x8BDD;&#x8BF4;, &quot;Others&apos; code feels like yours&quot;;(2) Google &#x5DE5;&#x7A0B;&#x5E08;&#x6587;&#x5316;&#x4E2D;&#x7684;&#x9886;&#x5730;&#x610F;&#x8BC6;&#x5F31;&#x4E00;&#x4E9B;, &#x5BF9;&#x4E8E;&#x964C;&#x751F;&#x4EBA;&#x7684;&#x5408;&#x7406;&#x6539;&#x52A8;&#x662F;&#x6B22;&#x8FCE;&#x7684;. &#x6211;&#x4F30;&#x8BA1;&#x5728;&#x81EA;&#x5DF1;&#x6240;&#x6709;&#x7684;&#x4EE3;&#x7801;&#x63D0;&#x4EA4;&#x91CC;, &#x6709; 1/3 &#x7684; reviewer &#x90FD;&#x4E0D;&#x662F;&#x81EA;&#x5DF1;&#x9879;&#x76EE;&#x7684;&#x4EBA;.</p></li></ul><p>&#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#x6700;&#x540E;&#x7684;<a href="https://github.com/ppwwyyxx/wechat-dump/commit/ce84066d85eebb50ae6b0909cdf609b096856f84" aria-label="sqlcipher compatibilty &#xB7; ppwwyyxx/wechat-dump@ce84066" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x89E3;&#x6CD5;&#x5F88;&#x7B80;&#x5355;</a>,  &#x76F4;&#x63A5;&#x4F7F;&#x7528;&#x65B0;&#x7248; sqlcipher &#x7684;&#x517C;&#x5BB9;&#x8BBE;&#x7F6E;<code>PRAGMA cipher_compatibility = 1;</code> &#x5C31;&#x80FD;&#x5F7B;&#x5E95;&#x89E3;&#x51B3;&#x95EE;&#x9898;. &#x4F46;&#x662F;, &#x8FD9;&#x91CC;&#x591A;&#x8BF4;&#x4E00;&#x53E5;. &#x5982;&#x679C; sqlcipher &#x4E0D;&#x540C;&#x7248;&#x672C;&#x6709;&#x4E0D;&#x540C;&#x7684;&#x9ED8;&#x8BA4;&#x884C;&#x4E3A;, &#x4F46; sqlcipher &#x81EA;&#x5DF1;&#x6CA1;&#x6709;&#x63D0;&#x4F9B; &quot;&#x517C;&#x5BB9;&#x65E7;&#x7248;&quot; &#x7684;&#x529F;&#x80FD;,  &#x90A3;&#x6211;&#x4F1A;&#x53BB;&#x63D0;&#x51FA;&#x8FD9;&#x4E2A; feature request. &#x56E0;&#x4E3A;&#x8981;&#x8BA4;&#x8BC6;&#x5230;, &#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#x5728;&#x7528;&#x6237;&#x4FA7;&#x662F;&#x51E0;&#x4E4E;&#x4E0D;&#x53EF;&#x80FD;&#x672C;&#x8D28;&#x89E3;&#x51B3;&#x7684;: &#x5373;&#x4F7F;&#x7528;&#x6237;&#x53EF;&#x4EE5;&#x624B;&#x52A8;&#x8BBE;&#x7F6E;&#x6240;&#x6709;&#x5DF2;&#x77E5;&#x53C2;&#x6570;&#x5230;&#x65E7;&#x7248;&#x7684;&#x503C;, &#x4E5F;&#x65E0;&#x6CD5;&#x907F;&#x514D;&#x672A;&#x6765;&#x51FA;&#x73B0;&#x65B0;&#x7684;&#x53C2;&#x6570;&#x5F71;&#x54CD;&#x7A0B;&#x5E8F;&#x884C;&#x4E3A;.</p><p>&#x8981;&#x672C;&#x8D28;&#x7684; (&#x800C;&#x4E0D;&#x662F;&#x7CCA;&#x5F04;&#x7684;) &#x89E3;&#x51B3;&#x4E00;&#x4E2A;&#x6280;&#x672F;&#x95EE;&#x9898;, &#x5F80;&#x5F80;&#x4F1A;&#x7275;&#x626F;&#x5230;&#x4E0A;&#x4E0B;&#x6E38;&#x7684;&#x4F9D;&#x8D56;: &#x4E00;&#x4E2A;&#x8C03;&#x7528;&#x6CA1;&#x6709;&#x5F97;&#x5230;&#x9884;&#x671F;&#x7684;&#x7ED3;&#x679C;,  &#x53EF;&#x80FD;&#x9700;&#x8981;&#x4E0A;&#x4E0B;&#x6E38;&#x5171;&#x540C;&#x628A;&#x8FD9;&#x4E2A;&#x8C03;&#x7528;&#x7684;&#x8BED;&#x4E49;&#x548C;&#x7528;&#x6CD5;&#x5B8C;&#x5584;. &#x5982;&#x679C;&#x53EA;&#x5728;&#x4E00;&#x4FA7;&#x505A;&#x6539;&#x52A8;, &#x53EF;&#x80FD;&#x53EA;&#x80FD;&#x4E34;&#x65F6;&#x6027;&#x7684;&#x89E3;&#x51B3;&#x95EE;&#x9898;.<strong> &#x597D;&#x7684;&#x5DE5;&#x7A0B;&#x5E08;&#x5E94;&#x8BE5;&#x80FD;&#x591F;&#x6253;&#x7A7F;&#x4E0A;&#x4E0B;&#x6E38;</strong>. &#x4E60;&#x60EF;&#x4E8E;&#x7A7F;&#x68AD;&#x5728;&#x4E0A;&#x4E0B;&#x6E38;&#x7684;&#x6E90;&#x7801;&#x4E2D;, &#x5C31;&#x662F;&#x4E00;&#x4E2A;&#x5FC5;&#x8981;&#x6761;&#x4EF6;.</p><!--   -* 最近几年, 自己越来越习惯从源码解决问题. [我的开源贡献](https://ppwwyyxx.com/blog/misc/github-contrib/)   -  中开始出现更多 pytorch 的 commits - 因为遇到问题就自己修了.   -  同时也能看到, 我给我使用的vim插件, zsh插件, 终端模拟器(kitty)等工具都做了一些贡献.   --><h3 id="Emoji-&#x89E3;&#x5BC6;">Emoji &#x89E3;&#x5BC6;<a class="markdown-anchor" href="#Emoji-&#x89E3;&#x5BC6;"> &#xB6;</a></h3><p>2016 &#x5E74;&#x65F6;&#x6211;&#x53D1;&#x73B0;&#x65B0;&#x7248;&#x5FAE;&#x4FE1;&#x5C06;&#x6240;&#x6709;&#x5B58;&#x50A8;&#x7684; emoji &#x7684;&#x524D; 1KB &#x505A;&#x4E86;&#x52A0;&#x5BC6;,  &#x5C31;&#x5728; README &#x91CC;&#x5199;&#x4E86;&#x4E00;&#x4E2A;<a href="https://github.com/ppwwyyxx/wechat-dump/commit/d0e428f2accd4a94ab796920527f4063a7008d6b" aria-label="add help &#xB7; ppwwyyxx/wechat-dump@d0e428f" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x6C42;&#x52A9;&#x6027;&#x8D28;&#x7684; TODO</a>. &#x4E00;&#x90E8;&#x5206; emoji &#x5728;&#x6570;&#x636E;&#x5E93;&#x4E2D;&#x5B58;&#x4E86;&#x4E0B;&#x8F7D;&#x94FE;&#x63A5;, &#x53EF;&#x4EE5;&#x7ED5;&#x8FC7;&#x52A0;&#x5BC6;, &#x4F46;&#x662F;&#x8FD9;&#x4E0D;&#x80FD;&#x5904;&#x7406;&#x6240;&#x6709;&#x60C5;&#x51B5;. &#x6211;&#x8FD8;&#x8BBE;&#x60F3;&#x8FC7;&#x5728;&#x4E0D;&#x770B;&#x524D; 1KB &#x7684;&#x60C5;&#x51B5;&#x4E0B;&#x5F3A;&#x884C;&#x89E3;&#x7801;&#x56FE;&#x7247; (&#x56E0;&#x4E3A;&#x957F;&#x5BBD;&#x5728;&#x6570;&#x636E;&#x5E93;&#x4E2D;&#x5DF2;&#x77E5;), &#x5E76;&#x4E14;&#x81EA;&#x5DF1;&#x786E;&#x5B9E;&#x6210;&#x529F;&#x7684;&#x624B;&#x52A8;&#x89E3;&#x7801;&#x4E86;&#x4E00;&#x5F20;&#x56FE;,  &#x4F46;&#x662F;&#x663E;&#x7136;&#x4E5F;&#x4E0D;&#x662F;&#x4E00;&#x4E2A;&#x672C;&#x8D28;&#x7684;&#x65B9;&#x6848;.</p><p>2020 &#x5E74;&#x6211;&#x9006;&#x5411;&#x4E86;&#x5FAE;&#x4FE1;&#x8FD9;&#x90E8;&#x5206;&#x903B;&#x8F91;, &#x5F97;&#x5230;&#x4E86;<a href="https://github.com/ppwwyyxx/wechat-dump/commit/641af7a3cd362d6f6b2fa3b39dd82efc9887ed04#diff-44002a8e54decd4daa1a11572838d0f1476795531402649f6e0b34b90dafa071" aria-label="decrypt local emoji &#xB7; ppwwyyxx/wechat-dump@641af7a" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x89E3;&#x5BC6;&#x65B9;&#x5F0F;</a>:</p><ul><li>&#x5148;&#x4ECE;&#x6570;&#x636E;&#x5E93;&#x91CC;&#x7528; magic number query &#x4E00;&#x4E2A;&#x7279;&#x6B8A;&#x7684; md5: <code>SELECT md5 FROM EmojiInfo where catalog == 153</code></li><li>&#x867D;&#x7136;&#x4E00;&#x4E2A; md5 &#x7684;&#x5927;&#x5C0F;&#x662F; 128 bit, &#x4F46;&#x662F;&#x8FD9;&#x91CC;&#x6709;&#x4E2A;&#x5947;&#x5999; / &#x5947;&#x602A;&#x7684;&#x903B;&#x8F91;: &#x6211;&#x4EEC;&#x8981;&#x628A;&#x5B83;&#x7684;&#x524D;&#x4E00;&#x534A;&#x62FF;&#x51FA;&#x6765;, &#x7528; hex &#x89E3;&#x7801;&#x518D;&#x7528; ascii &#x7F16;&#x7801;, &#x8FD9;&#x6837;&#x4ECD;&#x7136;&#x662F; 128bit.</li><li>&#x7528;&#x8FD9; 128bit &#x7684;&#x79D8;&#x94A5;&#x5BF9;&#x6587;&#x4EF6;&#x7684;&#x524D; 1KB &#x505A; ECB &#x89E3;&#x7801;</li></ul><p>&#x8FD9;&#x4E9B;&#x5947;&#x5999;&#x7684;&#x903B;&#x8F91;&#x663E;&#x7136;&#x662F;&#x731C;&#x4E0D;&#x51FA;&#x6765;&#x7684;, &#x53EA;&#x80FD;&#x9760;&#x9006;&#x5411;&#x5F97;&#x5230;. &#x5728;&#x9879;&#x76EE;&#x6700;&#x65E9;&#x671F;, &#x6211;&#x770B;&#x8FC7;&#x4E00;&#x6B21;&#x522B;&#x4EBA;&#x53CD;&#x6C47;&#x7F16;&#x7684;&#x5FAE;&#x4FE1;&#x4EE3;&#x7801;&#x5C31;&#x653E;&#x5F03;&#x4E86;, &#x7136;&#x800C;&#x8FD9;&#x6B21;&#x5374;&#x80FD;&#x8010;&#x5FC3;&#x770B;&#x4E0B;&#x53BB;&#x5E76;&#x89E3;&#x51B3;&#x95EE;&#x9898;. &#x6211;&#x4ECD;&#x7136;&#x662F;&#x4E2A;&#x9006;&#x5411;&#x65B0;&#x624B;, &#x8FD9;&#x671F;&#x95F4;&#x6CA1;&#x6709;&#x4EFB;&#x4F55;&#x9006;&#x5411;&#x6280;&#x672F;&#x7684;&#x63D0;&#x5347;, &#x4F46;&#x662F;&#x6709;&#x4E00;&#x4E9B;&#x5FC3;&#x6001;&#x4E0A;&#x7684;&#x53D8;&#x5316;:</p><ul><li>&#x5F00;&#x59CB;&#x5B66;&#x4E60; jadx, ghidra &#x7B49;&#x9006;&#x5411;&#x5DE5;&#x5177;, &#x6709;&#x4E00;&#x79CD; &quot;&#x5373;&#x4F7F;&#x505A;&#x4E0D;&#x51FA;&#x6765;&#x4E5F;&#x5B66;&#x5230;&#x4E86;&#x4E1C;&#x897F;&quot; &#x7684;&#x611F;&#x89C9;, &#x80FD;&#x63A8;&#x52A8;&#x81EA;&#x5DF1;&#x505A;&#x4E0B;&#x53BB;. &#x867D;&#x7136;&#x73B0;&#x5728;&#x5DF2;&#x7ECF;&#x5FD8;&#x4E86; ghidra &#x600E;&#x4E48;&#x7528;..</li><li>&#x6709;&#x4E00;&#x4E2A;&#x660E;&#x786E;&#x7684;&#x95EE;&#x9898;&#x8981;&#x89E3;&#x51B3;, &#x5C31;&#x53EF;&#x4EE5;&#x671D;&#x7740;&#x5B83;&#x9006;&#x5411;, &#x4E00;&#x6B65;&#x6B65;&#x628A;&#x76F8;&#x5173;&#x7684;&#x53D8;&#x91CF;&#x540D;, &#x51FD;&#x6570;&#x540D;&#x8BC6;&#x522B;&#x51FA;&#x6765;. &#x4E00;&#x76F4;&#x80FD;&#x6709; progress &#x4E0D;&#x5361;&#x4F4F;, &#x4E5F;&#x662F;&#x4E00;&#x4E2A;&#x63A8;&#x52A8;&#x529B;.</li><li>&#x9006;&#x5411;&#x5176;&#x5B9E;&#x5F88;&#x5927;&#x7A0B;&#x5EA6;&#x662F;&#x4E2A;&#x6A21;&#x5F0F;&#x8BC6;&#x522B;&#x7684;&#x8FC7;&#x7A0B;: &#x8981;&#x9762;&#x5BF9;&#x4E22;&#x5931;&#x4E86;&#x53D8;&#x91CF;&#x540D;&#x751A;&#x81F3;&#x62BD;&#x8C61;&#x7ED3;&#x6784;&#x7684;&#x53CD;&#x6C47;&#x7F16;&#x4EE3;&#x7801;, &#x8BC6;&#x522B;&#x51FA;&#x5B83;&#x539F;&#x59CB;&#x7684;&#x529F;&#x80FD;. &#x53EF;&#x4EE5;&#x611F;&#x89C9;&#x7684;&#x5230;, &#x5728;&#x81EA;&#x5DF1;&#x505A;&#x4E86;&#x51E0;&#x5E74;&#x5DE5;&#x7A0B;&#x540E;, &#x6A21;&#x5F0F;&#x8BC6;&#x522B;&#x7684;&#x80FD;&#x529B;&#x662F;&#x66F4;&#x5F3A;&#x7684;: &#x5373;&#x4F7F;&#x6CA1;&#x6709;&#x53D8;&#x91CF;&#x540D;, &#x770B;&#x5230;&#x4E00;&#x4E9B;&#x719F;&#x6089;&#x7684;&#x7ED3;&#x6784;&#x8FD8;&#x662F;&#x80FD;&#x591F;&#x731C;&#x5230;&#x539F;&#x59CB;&#x4EE3;&#x7801;&#x5927;&#x6982;&#x662F;&#x600E;&#x4E48;&#x8BBE;&#x8BA1;&#x7684;. &#x5F53;&#x65F6;&#x6211;&#x60F3;&#x5230;, &#x673A;&#x5668;&#x5B66;&#x4E60;&#x672A;&#x6765;&#x53EF;&#x80FD;&#x4F1A;&#x5F88;&#x9002;&#x5408;&#x505A;&#x9006;&#x5411;.</li></ul><h3 id="WXGF-&#x89E3;&#x7801;">WXGF &#x89E3;&#x7801;<a class="markdown-anchor" href="#WXGF-&#x89E3;&#x7801;"> &#xB6;</a></h3><p>2020 &#x5E74;&#x65F6;, &#x5B8C;&#x6210;&#x4E86;&#x89E3;&#x5BC6;&#x4E4B;&#x540E;&#x8FD8;&#x53D1;&#x73B0;, &#x6709;&#x90E8;&#x5206; emoji &#x7684;&#x683C;&#x5F0F;&#x5E76;&#x4E0D;&#x662F;&#x6807;&#x51C6;&#x56FE;&#x7247;, &#x800C;&#x662F;&#x5FAE;&#x4FE1;&#x79C1;&#x6709;&#x7684; &quot;WXGF&quot; &#x683C;&#x5F0F;,  &#x4F53;&#x73B0;&#x4E3A;&#x89E3;&#x5BC6;&#x540E;&#x6587;&#x4EF6;&#x524D;&#x56DB;&#x4E2A;&#x5B57;&#x8282;&#x662F;<code>wxgf</code>. &#x5F53;&#x65F6;&#x7684;&#x6211;&#x89E3;&#x51B3;&#x4E0D;&#x4E86;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;, &#x4E00;&#x4E2A;&#x56FE;&#x7247;&#x683C;&#x5F0F; (ARM &#x6C47;&#x7F16;) &#x7684;&#x9006;&#x5411;&#x8D85;&#x51FA;&#x4E86;&#x6211;&#x8FD9;&#x4E2A;&#x65B0;&#x624B;&#x7684;&#x80FD;&#x529B;,  &#x4E8E;&#x662F;<a href="https://github.com/ppwwyyxx/wechat-dump/commit/8e8b1d3534d5049868f6495cf71966b05dbd70cf#diff-96bf48df560745983009d595070f548c0c70831a36f48758976da1139da304f4R137" aria-label="better error message &#xB7; ppwwyyxx/wechat-dump@8e8b1d3" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x7559;&#x4E86;&#x4E00;&#x4E2A;&#x62A5;&#x9519;</a>. &#x9879;&#x76EE;&#x53C8;&#x505C;&#x6EDE;&#x4E86;&#x51E0;&#x5E74;&#x540E;, &#x5230;&#x4E86; 2025 &#x5E74;&#x6211;&#x53D1;&#x73B0;&#x5FAE;&#x4FE1;&#x8FDE;&#x804A;&#x5929;&#x4E2D;&#x7684;&#x56FE;&#x7247;&#x4E5F;&#x7528; WXGF &#x683C;&#x5F0F;&#x5B58;&#x50A8;&#x4E86;, &#x56E0;&#x6B64;&#x89E3;&#x51B3;&#x5B83;&#x7684;&#x4F18;&#x5148;&#x7EA7;&#x66F4;&#x9AD8;&#x4E86;.</p><p>&#x7531;&#x4E8E;&#x8FD9;&#x90E8;&#x5206;&#x5DE5;&#x4F5C;&#x521A;&#x521A;&#x5B8C;&#x6210;, &#x6240;&#x4EE5;&#x8BB0;&#x5F55;&#x4E00;&#x4E0B;&#x65B0;&#x9C9C;&#x7684;&#x4F53;&#x9A8C;:</p><ul><li>&#x901A;&#x8FC7; Java &#x90E8;&#x5206;&#x7684;&#x9006;&#x5411;, &#x5F88;&#x5BB9;&#x6613;&#x627E;&#x5230;&#x5173;&#x952E;&#x5165;&#x53E3;&#x51FD;&#x6570;<code>com.tencent.mm.plugin.gif.MMWXGFJNI.nativeWxam2PicBuf</code>, &#x5C06; WXGF &#x683C;&#x5F0F;&#x8F6C;&#x4E3A;&#x666E;&#x901A;&#x56FE;&#x7247;&#x683C;&#x5F0F;. &#x4F46;&#x5B83;&#x662F;&#x4E2A; JNI &#x8C03;&#x7528;, &#x5177;&#x4F53;&#x5B9E;&#x73B0;&#x4E0D;&#x5728; Java &#x91CC;.</li><li>&#x6211;&#x4ECE;&#x6765;&#x6CA1;&#x6709;&#x63A5;&#x89E6;&#x8FC7; JNI, &#x4E8E;&#x662F;&#x628A;&#x53CD;&#x6C47;&#x7F16;&#x7684;&#x63A5;&#x53E3;&#x6587;&#x4EF6;&#x9001;&#x7ED9; ChatGPT, &#x5B66;&#x4E60;&#x4E86;&#x4E00;&#x4E0B; JNI &#x76F8;&#x5173;&#x77E5;&#x8BC6;. <a href="https://chatgpt.com/share/679cb418-50b8-8010-83b4-547aa9e47fcc" aria-label="ChatGPT - JNI Symbol Naming Convention" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5BF9;&#x8BDD;&#x94FE;&#x63A5;</a>.<ul><li>ChatGPT &#x544A;&#x8BC9;&#x4E86;&#x6211;&#x7B26;&#x53F7;&#x7684;&#x540D;&#x5B57;; &#x6211;&#x636E;&#x6B64;&#x627E;&#x5230;&#x4E86;&#x6B63;&#x786E;&#x7684; <code>.so</code> &#x6587;&#x4EF6;: <code>libwechatcommon.so</code>.</li><li>ChatGPT &#x8BA9;&#x6211;&#x610F;&#x8BC6;&#x5230;&#x60F3;&#x4ECE; C++ &#x8C03;&#x7528;&#x89E3;&#x7801;&#x51FD;&#x6570;&#x7684;&#x98CE;&#x9669;&#x4E0D;&#x5C0F;, &#x56E0;&#x4E3A;&#x5E76;&#x4E0D;&#x786E;&#x5B9A;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#x662F;&#x5426;&#x4F9D;&#x8D56; Java &#x73AF;&#x5883;.</li></ul></li><li>&#x4ECE;<code>.so</code> &#x4E2D;&#x62FF;&#x51FA;&#x4E86;&#x5BF9;&#x5E94;&#x51FD;&#x6570;&#x7684;&#x6C47;&#x7F16;, &#x8BA9; ChatGPT &#x9006;&#x5411;&#x4E86;&#x4E00;&#x4E0B;. <a href="https://chatgpt.com/share/679cb56b-9bd4-8010-a773-22e0143b18ec" aria-label="ChatGPT - ARM Function Analysis" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5BF9;&#x8BDD;&#x94FE;&#x63A5;</a>.<ul><li>&#x9006;&#x5411;&#x7ED3;&#x679C;&#x8868;&#x660E;&#x89E3;&#x7801;&#x6838;&#x5FC3;&#x903B;&#x8F91;&#x5728;&#x522B;&#x7684;&#x51FD;&#x6570;&#x91CC;, &#x5E76;&#x4E14;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#x5730;&#x5740;&#x662F;&#x4ECE;&#x4E00;&#x4E2A; global pointer &#x62FF;&#x7684;. &#x8FD9;&#x8BF4;&#x660E;&#x6709;&#x53E6;&#x5916;&#x7684;&#x521D;&#x59CB;&#x5316;&#x4EE3;&#x7801;&#x6765;&#x8BBE;&#x7F6E;&#x8FD9;&#x4E9B; global pointer. &#x8FD9;&#x4E00;&#x70B9;&#x5F88;&#x91CD;&#x8981;.</li><li>&#x4ECE;&#x8FD9;&#x91CC;&#x4ECD;&#x7136;&#x4E0D;&#x597D;&#x5224;&#x65AD;&#x662F;&#x5426;&#x53EF;&#x4EE5;&#x4E0D;&#x4F9D;&#x8D56; Java. &#x56E0;&#x6B64;&#x6211;&#x5224;&#x65AD;&#x8FD8;&#x662F;&#x5199;&#x4E00;&#x4E2A; android app &#x8C03;&#x7528;&#x8FD9;&#x4E2A; JNI &#x6210;&#x529F;&#x7387;&#x6700;&#x9AD8;. &#x4E8E;&#x662F;&#x6211;&#x5F00;&#x59CB;&#x5199;&#x4EBA;&#x751F;&#x4E2D;&#x7B2C;&#x4E00;&#x4E2A; android app.</li></ul></li><li>&#x5B66;&#x4E60;&#x4E86; android studio, &#x521B;&#x5EFA;&#x4E86;&#x4E00;&#x4E2A;&#x57FA;&#x672C; app &#x6A21;&#x677F;. &#x5168;&#x8FC7;&#x7A0B;&#x4E3B;&#x8981;&#x4F9D;&#x8D56; android studio &#x5185;&#x7684; Gemini &#x6765;&#x505A;&#x5404;&#x79CD;&#x65E0;&#x804A;&#x7684; setup, &#x6D4B;&#x8BD5;&#x56FE;&#x7247;&#x8BFB;&#x53D6;, &#x548C; UI &#x5DE5;&#x4F5C;.Gemini &#x867D;&#x7136;&#x6709;&#x5E2E;&#x52A9;, &#x4F46;&#x662F;&#x8FD8;&#x662F;&#x4F1A;&#x72AF;&#x4E0D;&#x5C11;&#x9519;&#x8BEF;, &#x800C;&#x4E14;&#x548C; IDE &#x6574;&#x5408;&#x51E0;&#x4E4E;&#x6CA1;&#x6709;, &#x56DE;&#x7B54;&#x95EE;&#x9898;&#x4F3C;&#x4E4E;&#x4E5F;&#x53EA;&#x770B;&#x5F97;&#x89C1;&#x5355;&#x6587;&#x4EF6;&#x7684; context.</li><li>&#x8BD5;&#x56FE;<code>System.LoadLibrary(libwechatcommon.so)</code> &#x5E76;&#x4E14;&#x628A;&#x4F9D;&#x8D56;&#x7684;&#x5176;&#x5B83; so &#x6587;&#x4EF6;&#x90FD;&#x52A0;&#x5165;&#x4E86; app, &#x5728;&#x6700;&#x540E;&#x9636;&#x6BB5;&#x62A5;&#x9519;:<figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">Native registration unable to find class &apos;com.tencent.mm.plugin.fts.jni.FTSJNIUtils&apos;<br></code></pre></td></tr></table></figure>&#x770B;&#x6765;&#x662F;&#x8FD9;&#x4E2A; so &#x91CC;&#x8FD8;&#x4F1A;&#x53CD;&#x5411;&#x4F9D;&#x8D56; Java &#x4EE3;&#x7801;. &#x548C; ChatGPT <a href="https://chatgpt.com/share/679cb829-40e8-8010-9953-a88c82b19e38" aria-label="ChatGPT - Native registration error fix" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5B66;&#x4E60;&#x4E86;&#x4E00;&#x4E0B;</a>,  &#x5F97;&#x5230;&#x4E86;<a href="https://github.com/ppwwyyxx/wechat-dump/blob/master/WXGFDecoder/app/src/main/java/com/tencent/mm/plugin/fts/jni/FTSJNIUtils.java" aria-label="WXGFDecoder/app/src/main/java/com/tencent/mm/plugin/fts/jni/FTSJNIUtils.java &#xB7; ppwwyyxx/wechat-dump" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x4F7F;&#x7528;&#x4E00;&#x4E2A; stub file</a> &#x7684;&#x65B9;&#x6848;. &#x6700;&#x540E;&#x8FD9;&#x6837;&#x786E;&#x5B9E;&#x89E3;&#x51B3;&#x4E86;&#x95EE;&#x9898;.</li><li>&#x6210;&#x529F; load so &#x4E4B;&#x540E;, &#x7ED9; app &#x6DFB;&#x52A0;&#x4E86;&#x6D4B;&#x8BD5;&#x7528;&#x7684; WXGF &#x56FE;&#x7247;, &#x5C1D;&#x8BD5;&#x8C03;&#x7528;<code>nativeWxam2PicBuf</code> &#x51FD;&#x6570;, &#x53D1;&#x73B0;&#x8FD4;&#x56DE; null. &#x56DE;&#x60F3;&#x524D;&#x9762; ChatGPT &#x5BF9;&#x6C47;&#x7F16;&#x7684;&#x5206;&#x6790;, &#x5F88;&#x53EF;&#x80FD;&#x662F;&#x7F3A;&#x5C11;&#x4E86;&#x5FC5;&#x8981;&#x7684;&#x521D;&#x59CB;&#x5316;.<ul><li>Java &#x53CD;&#x6C47;&#x7F16;&#x51FA;&#x7684;<a href="https://github.com/ppwwyyxx/wechat-dump/blob/c09323fd444257031b280941beacd67a76155f0c/WXGFDecoder/app/src/main/java/com/tencent/mm/plugin/gif/MMWXGFJNI.java#L78-L95" aria-label="WXGFDecoder/app/src/main/java/com/tencent/mm/plugin/gif/MMWXGFJNI.java &#xB7; ppwwyyxx/wechat-dump" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x4E00;&#x6BB5;&#x4EE3;&#x7801;</a>&#x770B;&#x4F3C;&#x4F1A;&#x6BD4;&#x8F83;&#x91CD;&#x8981;: &#x5F88;&#x53EF;&#x80FD;&#x5173;&#x952E;&#x89E3;&#x7801;&#x51FD;&#x6570;&#x7684;&#x5730;&#x5740;&#x5728;&#x8FD9;&#x4E2A;<code>libvoipCodec.so</code> &#x4E2D;, &#x9700;&#x8981;&#x521D;&#x59CB;&#x5316;. &#x4F46;&#x662F;&#x6211;&#x8C03;&#x7528;<code>nativeInit(&quot;path/to/libvoipCodec.so&quot;)</code> &#x4E4B;&#x540E;, &#x4ECD;&#x7136;&#x65E0;&#x6CD5;&#x89E3;&#x7801;&#x56FE;&#x7247;.</li><li>&#x53D1;&#x73B0;<code>nativeInit</code> &#x8FD4;&#x56DE; <code>-1</code>. &#x76F4;&#x63A5;&#x628A;&#x8FD9;&#x4E2A;&#x51FD;&#x6570;&#x7684;&#x6C47;&#x7F16;<a href="https://chatgpt.com/share/679cbafc-92d8-8010-b878-5b568050624b" aria-label="ChatGPT - ARM64 Function Decompilation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x4E22;&#x7ED9; ChatGPT</a>, &#x8BA9;&#x5B83;&#x5206;&#x6790;&#x884C;&#x4E3A;, &#x544A;&#x8BC9;&#x6211;&#x4E3A;&#x4EC0;&#x4E48;&#x4F1A;&#x8FD4;&#x56DE;<code>-1</code>. &#x5B83;&#x4E00;&#x5F00;&#x59CB;&#x8BF4;&#x56E0;&#x4E3A;&#x8F93;&#x5165;&#x662F;&#x65E0;&#x6548;&#x5B57;&#x7B26;&#x4E32;, &#x4F46;&#x8FD9;&#x5E76;&#x4E0D;&#x662F;&#x6211;&#x7684;&#x60C5;&#x51B5;, &#x518D;&#x95EE;&#x4E00;&#x6B21;&#x4E4B;&#x540E;&#x7ED9;&#x51FA;&#x4E86;&#x6B63;&#x786E;&#x7B54;&#x6848;: dlopen &#x5931;&#x8D25;, &#x53EF;&#x80FD;&#x662F;&#x4F9D;&#x8D56;&#x5176;&#x4ED6;<code>.so</code>.</li><li>&#x901A;&#x8FC7;<code>System.LoadLibrary(libvoipCodec.so)</code> &#x7684;&#x62A5;&#x9519;&#x53D1;&#x73B0;&#x5B83;&#x786E;&#x5B9E;&#x4F9D;&#x8D56;&#x5176;&#x4ED6;<code>.so</code>. &#x4F9D;&#x8D56;&#x6DFB;&#x52A0;&#x5B8C;&#x6574;&#x4E4B;&#x540E;<code>nativeInit</code> &#x6210;&#x529F;,  &#x89E3;&#x7801;&#x51FD;&#x6570;&#x80FD;&#x8FD4;&#x56DE;&#x4E00;&#x4E32;&#x6570;&#x636E;&#x4E86;.</li><li>&#x89E3;&#x7801;&#x7684;&#x6570;&#x636E;&#x6253;&#x5728;&#x5C4F;&#x5E55;&#x4E0A;&#x6211;&#x7B2C;&#x4E00;&#x65F6;&#x95F4;&#x4E5F;&#x4E0D;&#x77E5;&#x9053;&#x662F;&#x4E0D;&#x662F;&#x5783;&#x573E;, &#x4E8E;&#x662F;<a href="https://chatgpt.com/share/679cbc19-f054-8010-97ac-8abb9dad5662" aria-label="ChatGPT - Decimal to ASCII and Hex" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x95EE; ChatGPT</a> &#x5B83;&#x76F4;&#x63A5;&#x544A;&#x8BC9;&#x6211;&#x662F; GIF header. &#x8BF4;&#x660E;&#x89E3;&#x7801;&#x6210;&#x529F;&#x4E86;.</li></ul></li><li>&#x6700;&#x540E;&#x6DFB;&#x52A0; UI &#x548C;&#x7F51;&#x7EDC;&#x4EA4;&#x4E92;, &#x628A;&#x5B83;&#x53D8;&#x6210;&#x4E00;&#x4E2A;&#x53EF;&#x4EE5;&#x4ECE;&#x7535;&#x8111;&#x4E0A;&#x8C03;&#x7528;&#x7684; android service. &#x4E00;&#x5F00;&#x59CB;&#x60F3;&#x5F00;&#x4E00;&#x4E2A; http server, &#x4F46;&#x662F; Gemini &#x5199;&#x4E0D;&#x51FA;&#x80FD;&#x7F16;&#x8BD1;&#x901A;&#x8FC7;&#x7684;&#x4EE3;&#x7801;. &#x8BA9;&#x5B83;&#x6539;&#x7528; websocket &#x4E4B;&#x540E;&#x80FD;&#x8DD1;&#x4E86;. &#x8FD9;&#x4E2A; android app &#x6700;&#x540E;&#x7684;&#x4EE3;&#x7801;&#x5728;<a href="https://github.com/ppwwyyxx/wechat-dump/tree/master/WXGFDecoder" aria-label="WXGFDecoder &#xB7; ppwwyyxx/wechat-dump" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x91CC;</a>.</li></ul><p>&#x53EF;&#x4EE5;&#x770B;&#x5230;, LLM &#x8FD8;&#x662F;&#x53D1;&#x6325;&#x4E86;&#x975E;&#x5E38;&#x5927;&#x7684;&#x4F5C;&#x7528;. &#x5C3D;&#x7BA1;&#x6211;&#x4ECE;&#x6765;&#x6CA1;&#x5199;&#x8FC7; android app, &#x5728; Gemini &#x7684;&#x5E2E;&#x52A9;&#x4E0B;&#x53EF;&#x4EE5;&#x5FEB;&#x901F;&#x628A;&#x4E00;&#x4E9B;&#x4F4E;&#x667A;&#x80FD;&#x7684; boilerplate &#x586B;&#x4E0A;, &#x5F97;&#x5230;&#x80FD;&#x8DD1;&#x7684; app.ChatGPT &#x7684;&#x9006;&#x5411;&#x548C;&#x5206;&#x6790;&#x66F4;&#x662F;&#x8282;&#x7701;&#x4E86;&#x6211;&#x5927;&#x91CF;&#x7684;&#x65F6;&#x95F4;: &#x5982;&#x679C;&#x6CA1;&#x6709; ChatGPT &#x6211;&#x4E0D;&#x4F1A;&#x6709;&#x65F6;&#x95F4;&#x548C;&#x8010;&#x5FC3;&#x53BB;&#x81EA;&#x5DF1;&#x89E3;&#x51B3;&#x8FD9;&#x4E9B;&#x95EE;&#x9898;.</p><h2 id="&#x611F;&#x60F3;">&#x611F;&#x60F3;<a class="markdown-anchor" href="#&#x611F;&#x60F3;"> &#xB6;</a></h2><p>&#x8BF4;&#x6765;&#x60ED;&#x6127;, WXGF &#x89E3;&#x7801;&#x662F;&#x6211;&#x7B2C;&#x4E00;&#x6B21;&#x5728;&#x5B9E;&#x9645;&#x9879;&#x76EE;&#x4E2D;, &#x611F;&#x53D7;&#x5230; LLM &#x5BF9;&#x6211;&#x7684;&#x5DE5;&#x4F5C;&#x6548;&#x7387;&#x7684;<strong>&#x6210;&#x500D;&#x6570;</strong>&#x7684;&#x63D0;&#x5347;:</p><ul><li>&#x65E5;&#x5E38;&#x5DE5;&#x4F5C;&#x4E2D;, &#x7B80;&#x5355;&#x7684; boilerplate &#x4EE3;&#x7801;&#x6211;&#x4E5F;&#x4F1A;&#x8BA9; LLM &#x5199;. &#x4F46;&#x662F;&#x7531;&#x4E8E;&#x5B83;&#x4EEC;&#x8FD8;&#x662F;&#x5728;&#x6211;&#x719F;&#x6089;&#x7684;&#x73AF;&#x5883;&#x548C;&#x8BED;&#x8A00;&#x4E0B;, LLM &#x5BF9;&#x6211;&#x7684;&#x6548;&#x7387;&#x63D0;&#x5347;&#x6709;&#x9650;.</li><li>&#x6211;&#x53C2;&#x4E0E;&#x7684;&#x590D;&#x6742;&#x6280;&#x672F;&#x95EE;&#x9898;, &#x5927;&#x90E8;&#x5206;&#x662F;&#x7CFB;&#x7EDF;&#x8BBE;&#x8BA1; / &#x7F8E;&#x5B66;&#x95EE;&#x9898;, &#x8DE8;&#x7CFB;&#x7EDF;&#x7684;&#x6574;&#x4F53;&#x4F18;&#x5316;&#x95EE;&#x9898;&#x7B49;, LLM &#x8FD8;&#x4E0D;&#x80FD;&#x63D0;&#x4F9B;&#x5F88;&#x597D;&#x7684;&#x5E2E;&#x52A9;. &#x800C;&#x4E14;&#x89E3;&#x51B3;&#x8FD9;&#x4E9B;&#x95EE;&#x9898;&#x7684;&#x76EE;&#x7684;&#x4E4B;&#x4E00;&#x5C31;&#x662F;&#x51CF;&#x5C11; boilerplate &#x4EE3;&#x7801;.</li></ul><p>&#x800C;&#x8FD9;&#x4E00;&#x6B21;&#x4F7F;&#x7528; LLM, &#x5197;&#x957F;&#x4E14;&#x6211;&#x4E0D;&#x719F;&#x6089;&#x7684; Java boilerplate &#x53EF;&#x4EE5;&#x8FC5;&#x901F;&#x5B8C;&#x6210;;  &#x590D;&#x6742;&#x7684;&#x6C47;&#x7F16;&#x5206;&#x6790;, &#x8DE8;&#x7CFB;&#x7EDF;&#x8C03;&#x7528;, LLM &#x4E5F;&#x80FD;&#x5728;&#x601D;&#x8003;&#x540E;&#x7ED9;&#x51FA;&#x6709;&#x4EF7;&#x503C;&#x7684;&#x7B54;&#x6848; -- &#x8FD9;&#x79CD;&#x5BF9;&#x5E95;&#x5C42;&#x903B;&#x8F91;&#x7684;&#x6267;&#x7740;&#x548C;&#x7075;&#x5149;&#x4E00;&#x95EA;, &#x672C;&#x6765;&#x662F;&#x9006;&#x5411;&#x5DE5;&#x7A0B;&#x5E08;&#x7279;&#x6709;&#x7684;&#x6D6A;&#x6F2B;, &#x4F46; LLM &#x4F3C;&#x4E4E;&#x5C06;&#x4F1A;&#x6BD4;&#x4EBA;&#x7C7B;&#x66F4;&#x64C5;&#x957F;&#x53CD;&#x6C47;&#x7F16;.</p><p>&#x5728;&#x8FD9;&#x4E2A;&#x5341;&#x5E74;&#x7684;&#x9879;&#x76EE;&#x91CC;, &#x6211;&#x611F;&#x53D7;&#x5230;&#x4E86;&#x81EA;&#x5DF1;&#x7684;&#x6280;&#x672F;&#x8FDB;&#x6B65;, &#x638C;&#x63E1;&#x4E86;&#x66F4;&#x591A;&#x7684;&#x5DE5;&#x5177;&#x548C;&#x5DE5;&#x7A0B;&#x601D;&#x60F3;&#x5E76;&#x4E3A;&#x6B64;&#x5F97;&#x610F;&#x7684;&#x65F6;&#x5019;, AI &#x4E00;&#x7728;&#x773C;&#x4FBF;&#x5DF2;&#x7ECF;&#x4ECE;&#x65C1;&#x8FB9;&#x8FFD;&#x4E0A;. &#x5B83;&#x8FDB;&#x6B65;&#x7684;&#x901F;&#x5EA6;&#x65E0;&#x4EBA;&#x53EF;&#x53CA;, &#x800C;&#x6211;&#x4EEC;&#x5E2E;&#x52A9;&#x5B83;&#x8FDB;&#x6B65;&#x7684;&#x66F4;&#x5FEB;, &#x4F3C;&#x4E4E;&#x662F;&#x5F53;&#x4E0B;&#x6700;&#x503C;&#x5F97;&#x505A;&#x7684;&#x4E8B;&#x60C5;.</p><p>&#x719F;&#x6089;&#x6211;&#x5DE5;&#x4F5C;&#x7684;&#x670B;&#x53CB;&#x77E5;&#x9053;, &#x6211;&#x5341;&#x5206;&#x770B;&#x91CD;&#x7CFB;&#x7EDF;&#x7684;&#x8BBE;&#x8BA1;&#x548C;&#x7F8E;&#x5B66;, &#x6267;&#x7740;&#x4E8E;&#x7528;&#x6B63;&#x786E;&#x7684;&#x62BD;&#x8C61;&#x548C;&#x7CBE;&#x786E;&#x7684;&#x8BED;&#x4E49;, &#x5728;&#x7CFB;&#x7EDF;&#x4E2D;&#x6700;&#x5408;&#x9002;&#x7684;&#x4F4D;&#x7F6E;&#x89E3;&#x51B3;&#x95EE;&#x9898;,  &#x5C31;&#x50CF;&#x4E0A;&#x6587;&#x63D0;&#x5230;&#x7684; sqlcipher &#x517C;&#x5BB9;&#x6027;&#x7684;&#x8BBE;&#x8BA1;&#x667A;&#x6167;&#x4E00;&#x6837;. &#x7136;&#x800C;&#x6709;&#x4EBA;&#x8BF4;, &#x7CFB;&#x7EDF;&#x8BBE;&#x8BA1;&#x7684;&#x5DEE;&#x4E00;&#x70B9;, &#x8BA9;&#x7528;&#x6237;&#x7528;&#x8D77;&#x6765;&#x96BE;&#x53D7;&#x4E00;&#x70B9;, &#x5199;&#x5199;&#x4E11;&#x964B;&#x7684; boilerplate &#x6CA1;&#x5173;&#x7CFB;,  &#x56E0;&#x4E3A; LLM &#x6765;&#x5199;&#x5C31;&#x597D;&#x4E86;. &#x8FD9;&#x53E5;&#x8BDD;&#x80AF;&#x5B9A;&#x6709;&#x90E8;&#x5206;&#x662F;&#x5BF9;&#x7684;, &#x4F46;&#x6211;&#x5BF9;&#x6B64;&#x8FD8;&#x4E0D;&#x80FD;&#x5341;&#x5206;&#x8BA4;&#x53EF;: &#x4E00;&#x4E2A;&#x8BBE;&#x8BA1;&#x4E11;&#x964B;&#x7E41;&#x7410;&#x7684;&#x7CFB;&#x7EDF;, LLM &#x7406;&#x89E3;&#x8D77;&#x6765;&#x4E5F;&#x4F1A;&#x66F4;&#x56F0;&#x96BE;, &#x66F4;&#x5BB9;&#x6613;&#x5199;&#x9519;. &#x63D0;&#x4F9B;&#x6765;&#x81EA;&#x66F4;&#x62BD;&#x8C61;&#x7684;&#x5C42;&#x6B21;&#x7684;&#x89C6;&#x89D2;, &#x8FD9;&#x53EF;&#x80FD;&#x662F;&#x5F53; AI &#x53D6;&#x4EE3;&#x4E86;&#x7B80;&#x5355;&#x7684;&#x7F16;&#x7A0B;&#x4EFB;&#x52A1;&#x4E4B;&#x540E;&#x6211;&#x8FD8;&#x80FD;&#x505A;&#x7684;&#x5427;.</p><p>&#x53EF;&#x662F;, &#x5F53;&#x4EE3;&#x7801;&#x7684;&#x827A;&#x672F;&#x6210;&#x4E3A;&#x88AB;&#x6279;&#x91CF;&#x590D;&#x5236;&#x7684;&#x79EF;&#x6728;, &#x7F8E;&#x5B66;&#x7684;&#x4EF7;&#x503C;&#x662F;&#x4F1A;&#x88AB;&#x4E00;&#x540C;&#x590D;&#x5236;, &#x8FD8;&#x662F;&#x6CA6;&#x4E3A;&#x6570;&#x636E;&#x4E2D;&#x7684;&#x7EDF;&#x8BA1;&#x566A;&#x58F0;?  &#x4E0B;&#x4E00;&#x4EE3;&#x7684;&#x5DE5;&#x7A0B;&#x5E08;, &#x4ED6;&#x4EEC;&#x5C06;&#x5982;&#x4F55;&#x67B6;&#x6784;&#x4E00;&#x4E2A;&#x5DE5;&#x7A0B;? &#x5BF9;&#x8BBE;&#x8BA1;&#x7684;&#x575A;&#x6301;, &#x662F;&#x5426;&#x50CF;&#x662F;&#x624B;&#x5DE5;&#x827A;&#x4EBA;&#x9762;&#x5BF9;&#x5DE5;&#x4E1A;&#x6D41;&#x6C34;&#x7EBF;&#x65F6;&#x6700;&#x540E;&#x7684;&#x6297;&#x4E89;? </p><p>&#x5728; AGI &#x6765;&#x4E34;&#x524D;, &#x6211;&#x4EEC;&#x80FD;&#x4E0D;&#x80FD;&#x628A;&#x4EBA;&#x7C7B;&#x6700;&#x95EA;&#x8000;&#x7684;&#x601D;&#x60F3;&#x7559;&#x5728;&#x5B83;&#x4EEC;&#x8EAB;&#x4E0A;? </p><p>&#x4EBA;&#x7C7B;&#x4F1A;&#x5728;&#x4E0B;&#x4E00;&#x4E2A;&#x5341;&#x5E74;&#x91CC;&#x7ED9;&#x51FA;&#x7B54;&#x6848;.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&amp;#x5728;&amp;#x8FC7;&amp;#x5E74;&amp;#x7684;&amp;#x8FD9;&amp;#x51E0;&amp;#x5929;, &amp;#x4E3A;&amp;#x4E86;&amp;#x4ECE;&amp;#x7126;&amp;#x8651;&amp;#x7684;&amp;#x5DE5;&amp;#x4F5C;&amp;#x4E2D;&amp;#x6362;&amp;#x4E00;&amp;#x4E2A;&amp;#x5FC3;&amp;#x60C5;, &amp;#x6211;&amp;#x7ED9;&amp;#x6211;&amp;#x7684;
&lt;a href=&quot;https://github.com/ppwwyyxx/wechat-dump&quot; aria-label=&quot;ppwwyyxx/wechat-dump: Cracking encrypted wechat message history from android&quot; class=&quot;hint--top hint--rounded hint--no-animate hint--no-arrow&quot;&gt; wechat-dump&lt;/a&gt;
 &amp;#x9879;&amp;#x76EE;&amp;#x6DFB;&amp;#x52A0;&amp;#x4E86;&amp;#x51E0;&amp;#x4E2A;&amp;#x5F53;&amp;#x5E74;&amp;#x6CA1;&amp;#x505A;&amp;#x51FA;&amp;#x6765;&amp;#x7684;&amp;#x529F;&amp;#x80FD;, &amp;#x89E3;&amp;#x51B3;&amp;#x4E86;&amp;#x4E00;&amp;#x4E9B;&amp;#x9057;&amp;#x7559;&amp;#x95EE;&amp;#x9898;. &amp;#x610F;&amp;#x5916;&amp;#x7684;&amp;#x53D1;&amp;#x73B0;&amp;#x8FD9;&amp;#x4E2A;&amp;#x9879;&amp;#x76EE;&amp;#x59CB;&amp;#x4E8E; 2014 &amp;#x5E74;&amp;#x672B;, &amp;#x5230;&amp;#x4ECA;&amp;#x5929;&amp;#x5DF2;&amp;#x7ECF;&amp;#x8D85;&amp;#x8FC7;&amp;#x5341;&amp;#x5E74;&amp;#x4E86;.
 &amp;#x6709;&amp;#x591A;&amp;#x5C11;&amp;#x4EBA;&amp;#x4F1A;&amp;#x6709;&amp;#x7ED9;&amp;#x81EA;&amp;#x5DF1;&amp;#x5341;&amp;#x5E74;&amp;#x524D;&amp;#x7684;&amp;#x4EE3;&amp;#x7801;&amp;#x8865;&amp;#x5145;&amp;#x65B0; feature &amp;#x7684;&amp;#x7ECF;&amp;#x5386;&amp;#x5462;? &amp;#x7A81;&amp;#x7136;&amp;#x6709;&amp;#x4E86;&amp;#x4E00;&amp;#x4E9B;&amp;#x611F;&amp;#x89E6;&amp;#x60F3;&amp;#x8981;&amp;#x5199;&amp;#x4E0B;&amp;#x6765;.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Programming" scheme="https://ppwwyyxx.com/blog/tags/Programming/"/>
    
    <category term="Open Source" scheme="https://ppwwyyxx.com/blog/tags/Open-Source/"/>
    
    <category term="AGI" scheme="https://ppwwyyxx.com/blog/tags/AGI/"/>
    
  </entry>
  
  <entry>
    <title>为什么应该使用 Stacked Diffs / Stacked PRs</title>
    <link href="https://ppwwyyxx.com/blog/2023/Stacked-Diffs-Pull-Requests/"/>
    <id>https://ppwwyyxx.com/blog/2023/Stacked-Diffs-Pull-Requests/</id>
    <published>2023-08-14T07:00:00.000Z</published>
    <updated>2023-08-14T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>Meta &#x4E0E; Google &#x5185;&#x90E8;&#x7684;&#x4EE3;&#x7801;&#x7BA1;&#x7406;&#x5DE5;&#x5177;&#x90FD;&#x652F;&#x6301;&#x4E00;&#x79CD;&#x88AB;&#x79F0;&#x4F5C; &quot;stacked diffs / stacked PRs&quot; &#x7684; workflow. &#x7136;&#x800C;, &#x57FA;&#x4E8E; git &#x7684;&#x4E3B;&#x6D41;&#x5E73;&#x53F0; (github, gitlab) &#x90FD;&#x4E0D;&#x652F;&#x6301;&#x8FD9;&#x79CD; workflow. &#x8BB8;&#x591A;&#x79BB;&#x5F00; Meta &#x540E;&#x4E0D;&#x5F97;&#x4E0D;&#x4F7F;&#x7528; github &#x7684;&#x670B;&#x53CB;&#x8868;&#x793A;, stacked diffs &#x5BF9;&#x4E8E;&#x5DE5;&#x7A0B;&#x5E08;&#x662F;&#x4E00;&#x4E2A; &quot;ultimateproductivity tool&quot;, &#x6211;&#x4E5F;&#x6DF1;&#x6709;&#x540C;&#x611F;. &#x8FD9;&#x7BC7;&#x6587;&#x7AE0;&#x4ECB;&#x7ECD;&#x4E00;&#x4E0B;&#x4EC0;&#x4E48;&#x662F; stacked diffs workflow, &#x4EE5;&#x53CA;&#x4E3A;&#x4EC0;&#x4E48;&#x5B83;&#x80FD;&#x591F;&#x6781;&#x5927;&#x7684;&#x63D0;&#x5347;&#x56E2;&#x961F;&#x5F00;&#x53D1;&#x6548;&#x7387;.</p><span id="more"></span><h2 id="&#x573A;&#x666F;">&#x573A;&#x666F;<a class="markdown-anchor" href="#&#x573A;&#x666F;"> &#xB6;</a></h2><p>&#x8FD9;&#x91CC;&#x6211;&#x4EEC;&#x628A;&#x8BA8;&#x8BBA;&#x9650;&#x5236;&#x5728;&#x5982;&#x4E0B;&#x7684;&#x4F7F;&#x7528;&#x573A;&#x666F;&#x4E2D;:</p><ul><li>&#x5047;&#x8BBE; 1: &#x4E00;&#x4E2A;&#x590D;&#x6742;&#x7684;&#x4EE3;&#x7801;&#x4ED3;&#x5E93;&#x6709;&#x4E00;&#x4E2A;&#x552F;&#x4E00;&#x7684;&#x6838;&#x5FC3;&#x4E3B;&#x7EBF;&#x5206;&#x652F;.</li><li>&#x5047;&#x8BBE; 2: &#x6709;&#x591A;&#x4E2A;&#x5F00;&#x53D1;&#x8005;&#x8BD5;&#x56FE;&#x8FDB;&#x884C; non-trivial &#x7684;&#x6539;&#x52A8;.</li><li>&#x5047;&#x8BBE; 3: &#x9879;&#x76EE;&#x5BF9;&#x4EE3;&#x7801;&#x8D28;&#x91CF;&#x6709;&#x8981;&#x6C42;: &#x6BCF;&#x4E2A;&#x8981;&#x5408;&#x5E76;&#x7684;&#x6539;&#x52A8;&#x90FD;&#x5FC5;&#x987B;&#x7ECF;&#x8FC7;&#x5145;&#x5206; review &#x548C;&#x6D4B;&#x8BD5;.</li><li>&#x5047;&#x8BBE; 4: &#x9002;&#x7528;&#x5BF9;&#x8C61;&#x4E0D;&#x662F;&#x5F00;&#x6E90;&#x9879;&#x76EE;&#x91CC;&#x7684;&#x8DEF;&#x4EBA;&#x968F;&#x624B; PR, &#x4E5F;&#x4E0D;&#x662F;&#x5B9E;&#x9A8C;&#x6027;&#x8D28;&#x7684; prototype, &#x800C;&#x662F;&#x8F83;&#x4E3A;&#x786E;&#x5B9A;&#x6027;&#x7684;&#x4E25;&#x8083;&#x7684;&#x56E2;&#x961F;&#x5408;&#x4F5C;.</li></ul><p>&#x7531;&#x4E8E;&#x5047;&#x8BBE; 2, <strong>&#x4E00;&#x4E2A;&#x65B0; feature &#x5F80;&#x5F80;&#x8981;&#x5BF9;&#x4ED3;&#x5E93;&#x91CC;&#x7684;&#x591A;&#x4E2A;&#x90E8;&#x5206;&#x8FDB;&#x884C;&#x76F8;&#x5BF9;&#x72EC;&#x7ACB;&#x7684;&#x6539;&#x52A8;</strong>, &#x4F8B;&#x5982;&#x5728;&#x5B9E;&#x73B0; feature X &#x7684;&#x8FC7;&#x7A0B;&#x4E2D;, &#x6211;&#x4EEC;&#x53EF;&#x80FD;&#x4F1A;&#x6709;&#x5982;&#x4E0B;&#x6B65;&#x9AA4;:</p><ol><li>&#x4E3A;&#x6A21;&#x5757; A &#x6DFB;&#x52A0;&#x4E00;&#x4E2A; API &#x53CA;&#x6D4B;&#x8BD5;</li><li>&#x987A;&#x4FBF;&#x53D1;&#x73B0;&#x4E86;&#x6A21;&#x5757; B &#x7684;&#x4E00;&#x4E2A; bug, &#x4FEE;&#x590D;&#x5B83;&#x5E76;&#x6DFB;&#x52A0;&#x6D4B;&#x8BD5;</li><li>&#x5BF9;&#x6A21;&#x5757; C &#x7684;&#x4E00;&#x4E2A; API &#x8FDB;&#x884C;&#x4E86;&#x4E0D;&#x517C;&#x5BB9;&#x7684;&#x6539;&#x52A8;&#x5E76;&#x4FEE;&#x6539;&#x4E86;&#x5B83;&#x6240;&#x6709;&#x7684; callsite</li><li>&#x5B9E;&#x73B0; feature X &#x53CA;&#x6DFB;&#x52A0;&#x6D4B;&#x8BD5;</li><li>&#x5B8C;&#x5584;&#x5173;&#x4E8E; feature X &#x7684;&#x6587;&#x6863;&#x548C; release note</li></ol><p>&#x7B49;&#x7B49;.</p><p>&#x7531;&#x4E8E;&#x5047;&#x8BBE; 4, &#x5F00;&#x53D1;&#x8005;&#x77E5;&#x9053;&#x81EA;&#x5DF1;&#x7684;&#x65B9;&#x5411;&#x662F;&#x5927;&#x6982;&#x7387;&#x6B63;&#x786E;, (&#x505A;&#x5C11;&#x91CF;&#x4FEE;&#x6539;&#x540E;) &#x4F1A;&#x88AB; reviewer &#x901A;&#x8FC7;&#x7684;. &#x56E0;&#x6B64;&#x4E3A;&#x4E86;&#x63D0;&#x9AD8;&#x6548;&#x7387;, &#x5728;&#x5B9E;&#x73B0;&#x4E86;&#x90E8;&#x5206;&#x6539;&#x52A8; (&#x4F8B;&#x5982;&#x6B65;&#x9AA4; 1-3) &#x540E;, <strong> &#x540E;&#x7EED; (&#x4F8B;&#x5982;&#x6B65;&#x9AA4; 4-5) &#x7684;&#x5F00;&#x53D1;&#x4E0D;&#x5E94;&#x88AB; 1-3 &#x7684; code review block &#x4F4F;</strong>. &#x4EE3;&#x7801;&#x7BA1;&#x7406;&#x7CFB;&#x7EDF;&#x5E94;&#x5F53;&#x5F88;&#x597D;&#x7684;&#x652F;&#x6301;&#x8FD9;&#x79CD; <strong>&#x65E0;&#x963B;&#x585E;&#x7684;&#x5F00;&#x53D1;&#x6A21;&#x5F0F;</strong>,  &#x624D;&#x80FD;&#x6700;&#x5927;&#x5316;&#x56E2;&#x961F;&#x7684;&#x6548;&#x7387;. &#x7136;&#x800C;&#x6211;&#x4EEC;&#x5C06;&#x53D1;&#x73B0;, git + github &#x7684;&#x8BBE;&#x8BA1;&#x5E76;&#x4E0D;&#x9F13;&#x52B1;&#x8FD9;&#x79CD;&#x5F00;&#x53D1;&#x6A21;&#x5F0F;.</p><h2 id="Code-Review&#x7684;&#x57FA;&#x672C;&#x5355;&#x5143;-branch-vs-commit">Code Review &#x7684;&#x57FA;&#x672C;&#x5355;&#x5143;: branch vs. commit<a class="markdown-anchor" href="#Code-Review&#x7684;&#x57FA;&#x672C;&#x5355;&#x5143;-branch-vs-commit">&#xB6;</a></h2><p>&#x4F20;&#x7EDF;&#x7684;&#x57FA;&#x4E8E; github/gitlab &#x7684; workflow &#x5177;&#x6709;&#x8FD9;&#x6837;&#x7684;&#x7279;&#x6027;:</p><ul><li><strong>Code review &#x7684;&#x57FA;&#x672C;&#x5355;&#x5143;&#x662F; PR</strong> (&#x5728; gitlab &#x4E0A;&#x53EB; MR). &#x8FD9;&#x662F;&#x7531; github &#x5E73;&#x53F0;&#x7684; UI &#x8BBE;&#x5B9A;&#x7684;: &#x5B83;&#x7684;&#x8BBE;&#x8BA1;&#x6781;&#x5927;&#x7A0B;&#x5EA6;&#x4E0A;&#x9F13;&#x52B1;&#x7528;&#x6237;&#x5BF9; PR &#x6574;&#x4F53;&#x8FDB;&#x884C; review, &#x800C;&#x4E0D;&#x662F;&#x53BB; review PR &#x91CC;&#x72EC;&#x7ACB;&#x7684; commit.(PR &#x91CC;&#x7684;&#x6BCF;&#x4E2A; commit &#x867D;&#x7136;&#x53EF;&#x4EE5;&#x5206;&#x5F00;&#x770B;, &#x4F46;&#x662F;&#x4F53;&#x9A8C;&#x4E0A;&#x96BE;&#x4EE5;&#x5206;&#x5F00; review. <a href="https://github.com/llvm/llvm-project/issues/56636" aria-label="Write a guide for doing &quot;Stacked Reviews&quot; with GitHub Pull Requests &#xB7; Issue #56636 &#xB7; llvm/llvm-project" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x91CC;</a> &#x6709;&#x5173;&#x4E8E;&#x8FD9;&#x4E00;&#x70B9;&#x7684;&#x8BA8;&#x8BBA;.)</li><li><strong>Code review &#x7684;&#x57FA;&#x672C;&#x5355;&#x5143;&#x4E5F;&#x662F; branch</strong>, &#x56E0;&#x4E3A; PR &#x4E0E; branch &#x4E00;&#x4E00;&#x5BF9;&#x5E94;: &#x6BCF;&#x4E2A;&#x672A;&#x88AB;&#x5408;&#x5E76;&#x7684; PR &#x5FC5;&#x987B;&#x5B58;&#x5728;&#x4E8E;&#x4E00;&#x4E2A; branch &#x4E0A;, &#x4E14;&#x6BCF;&#x4E2A;&#x8FD9;&#x6837;&#x7684; branch &#x4E0A;&#x53EA;&#x80FD;&#x6709;&#x4E00;&#x4E2A;&#x672A;&#x88AB;&#x5408;&#x5E76;&#x7684; PR.</li></ul><p>&#x800C; stacked diffs workflow &#x7684;&#x6700;&#x91CD;&#x8981;&#x7684;&#x7279;&#x6027;&#x662F;: <strong>code review &#x7684;&#x57FA;&#x672C;&#x5355;&#x5143;&#x5BF9;&#x5E94;&#x4ED3;&#x5E93;&#x91CC;&#x7684;&#x5355;&#x4E2A; commit.</strong> &#x5728; phabricator (Meta &#x4F7F;&#x7528;&#x7684; code review &#x7CFB;&#x7EDF;, &#x4E5F;&#x88AB;&#x7528;&#x4E8E; llvm &#x7B49;&#x5F00;&#x6E90;&#x9879;&#x76EE;) &#x4E2D;&#x8FD9;&#x4E2A;&#x5355;&#x5143;&#x53EB;&#x505A; &quot;diff&quot;.</p><p>Code review &#x7684;&#x57FA;&#x672C;&#x5355;&#x5143;&#x662F; branch &#x8FD8;&#x662F; commit, &#x7A76;&#x7ADF;&#x6709;&#x4EC0;&#x4E48;&#x533A;&#x522B;? </p><p>&#x8BFB;&#x8005;&#x53EF;&#x80FD;&#x4F1A;&#x8BA4;&#x4E3A;, &#x5728; github &#x4E0A; review PR &#x7684;&#x65F6;&#x5019;, &#x4E5F;&#x662F;&#x5728; review commit. &#x4F46;&#x662F;, PR &#x91CC;&#x63D0;&#x4F9B;&#x7ED9; reviewer &#x7684;&#x5185;&#x5BB9;, &#x5176;&#x5B9E;&#x662F;&#x901A;&#x8FC7; branch &#x7684;&#x72B6;&#x6001;&#x8BA1;&#x7B97;&#x5F97;&#x6765;&#x7684;: &#x6BCF;&#x4E2A; PR &#x6709;&#x4E00;&#x4E2A; target branch (&#x4F8B;&#x5982;&#x4E0B;&#x56FE;&#x4E2D;&#x7684;<code>pytorch:main</code>) &#x548C;&#x4E00;&#x4E2A; featurebranch (&#x4F8B;&#x5982;<code>ppwwyyxx:logging</code>). Code review &#x7684;&#x5185;&#x5BB9;&#x662F;&#x5B83;&#x4EEC;&#x4E4B;&#x95F4;&#x7684;&#x5DEE;&#x522B;. &#x4E5F;&#x5C31;&#x662F;&#x8BF4;, &#x5982;&#x679C; feature branch &#x7684;&#x5185;&#x5BB9;&#x53D1;&#x751F;&#x53D8;&#x5316; (&#x4F8B;&#x5982;&#x6709;&#x4E86;&#x65B0;&#x7684; commit), PR &#x5C31;&#x4F1A;&#x53D1;&#x751F;&#x53D8;&#x5316;.</p><img src="/blog/2023/Stacked-Diffs-Pull-Requests/target-branch.png" class="center"><p>&#x800C;&#x5728;&#x57FA;&#x4E8E; commit &#x7684; workflow &#x4E2D;, &#x751A;&#x81F3;&#x6839;&#x672C;&#x4E0D;&#x9700;&#x8981; branch &#x8FD9;&#x4E2A;&#x6982;&#x5FF5;: &#x6211;&#x6240;&#x6709;&#x7684;&#x5DE5;&#x4F5C;&#x90FD;&#x4F1A;&#x662F;&#x672C;&#x5730;&#x7684;&#x4E00;&#x7CFB;&#x5217; commits, &#x5B83;&#x4EEC;&#x88AB;&#x540C;&#x6B65;&#x5230; code review &#x7CFB;&#x7EDF;&#x91CC;&#x6210;&#x4E3A; diffs. &#x7531;&#x4E8E; diff &#x4E0E;&#x5355;&#x4E2A; commit &#x5BF9;&#x5E94;,  &#x6DFB;&#x52A0;&#x65B0;&#x7684; commit &#x5E76;&#x4E0D;&#x4F1A;&#x5F71;&#x54CD; code review &#x7CFB;&#x7EDF;&#x91CC;&#x73B0;&#x5B58;&#x7684;&#x5185;&#x5BB9;, &#x800C;&#x4F1A;&#x521B;&#x5EFA;&#x65B0;&#x7684; diff. &#x5982;&#x679C;&#x8981;&#x4FEE;&#x6539;&#x67D0;&#x4E2A; diff &#x7684;&#x5185;&#x5BB9;, &#x6211;&#x4EEC;&#x53EF;&#x4EE5;&#x628A;&#x4FEE;&#x6539; amend &#x8FDB;&#x8FD9;&#x4E2A; diff &#x5BF9;&#x5E94;&#x7684; commit.</p><p>&#x8BF4;&#x4E86;&#x8FD9;&#x4E9B;&#x57FA;&#x7840;&#x6982;&#x5FF5;, &#x63A5;&#x4E0B;&#x6765;&#x6211;&#x4EEC;&#x89E3;&#x91CA;&#x4E3A;&#x4EC0;&#x4E48; git + github PR &#x7684; workflow &#x5E76;&#x4E0D;&#x597D;&#x7528;. &#x6574;&#x4E2A;&#x903B;&#x8F91;&#x603B;&#x7ED3;&#x4E0B;&#x6765;&#x662F;&#x8FD9;&#x6837;&#x7684;:</p><ol><li>git+github &#x7684; workflow &#x91CC;, &quot;code review &#x5355;&#x5143; == PR == branch&quot; (<a href="#Code-Review%E7%9A%84%E5%9F%BA%E6%9C%AC%E5%8D%95%E5%85%83-branch-vs-commit">&#x8FD9;&#x4E00;&#x8282;</a>)</li><li>&#x590D;&#x6742;&#x7684;&#x5F00;&#x53D1;&#x5E94;&#x5C3D;&#x53EF;&#x80FD;&#x62C6;&#x6210;&#x591A;&#x4E2A;&#x72EC;&#x7ACB;&#x7684; code review &#x5355;&#x5143; (&#x4E5F;&#x5373; PR) (<a href="#Code-Review%E5%8D%95%E5%85%83%E5%BA%94%E5%B0%BD%E5%8F%AF%E8%83%BD%E6%8B%86%E5%B0%8F">&#x4E0B;&#x4E00;&#x8282;</a>)</li><li>Code review &#x4E0D;&#x5E94;&#x963B;&#x585E;&#x5F00;&#x53D1;: &#x6211;&#x4EEC;&#x9700;&#x8981;&#x80FD;&#x591F;&#x5728;&#x8F83;&#x65E9;&#x7684; PR &#x8FD8;&#x6CA1;&#x5B8C;&#x6210; review (&#x751A;&#x81F3;&#x6CA1;&#x5B8C;&#x6210;&#x5F00;&#x53D1;) &#x65F6;, &#x5C31;&#x80FD;&#x591F;&#x5728;&#x5176;&#x57FA;&#x7840;&#x4E0A;&#x5F00;&#x53D1;&#x540E;&#x7EED;&#x7684;&#x4E00;&#x7CFB;&#x5217; PR (<a href="#%E5%9C%BA%E6%99%AF">&#x4E0A;&#x4E00;&#x8282;</a>). &#x8FD9;&#x4E9B; PR &#x95F4;&#x81EA;&#x7136;&#x5C31;&#x6709;&#x4E86;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;</li><li>&#x5F53;&#x8F83;&#x65E9;&#x7684; PR &#x53D1;&#x751F;&#x6539;&#x52A8;&#x65F6;, &#x6211;&#x4EEC;&#x9700;&#x8981;&#x5BF9;&#x4F9D;&#x8D56;&#x5B83;&#x7684;&#x540E;&#x7EED; PR &#x505A;&#x64CD;&#x4F5C;, &#x4EE5;&#x7EF4;&#x62A4; PR &#x95F4;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;. &#x800C; git + github <strong>&#x96BE;&#x4EE5;&#x7EF4;&#x62A4; branch &#x6216; PR &#x95F4;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;</strong>. (<a href="#%E5%A6%82%E4%BD%95%E7%AE%A1%E7%90%86code-review%E9%97%B4%E7%9A%84%E4%BE%9D%E8%B5%96%E5%85%B3%E7%B3%BB">&#x4E0B;&#x4E0B;&#x8282;</a>)</li></ol><h2 id="Code-Review&#x5355;&#x5143;&#x5E94;&#x5C3D;&#x53EF;&#x80FD;&#x62C6;&#x5C0F;">Code Review &#x5355;&#x5143;&#x5E94;&#x5C3D;&#x53EF;&#x80FD;&#x62C6;&#x5C0F;<a class="markdown-anchor" href="#Code-Review&#x5355;&#x5143;&#x5E94;&#x5C3D;&#x53EF;&#x80FD;&#x62C6;&#x5C0F;"> &#xB6;</a></h2><p>&#x8FD9;&#x4E00;&#x8282;&#x4ECB;&#x7ECD;&#x4E00;&#x4E2A;&#x666E;&#x904D;&#x7684;&#x5DE5;&#x7A0B;&#x5B9E;&#x8DF5;: code review &#x5355;&#x5143;&#x5E94;&#x5C3D;&#x53EF;&#x80FD;&#x7684;&#x5C0F;. &#x4E00;&#x4E2A;&#x590D;&#x6742;&#x7684;&#x5F00;&#x53D1;&#x4EFB;&#x52A1; <strong>&#x5E94;&#x5C3D;&#x53EF;&#x80FD;&#x62C6;&#x6210;&#x4E0D;&#x540C;&#x7684; code review &#x5355;&#x5143;</strong> &#x800C;&#x4E0D;&#x662F;&#x5408;&#x5728;&#x4E00;&#x8D77; review. &#x8FD9;&#x662F;&#x56E0;&#x4E3A;:</p><ol><li><p>Code review &#x6240;&#x9700;&#x8981;&#x7684;&#x7CBE;&#x529B;&#x4E0E; PR &#x957F;&#x5EA6;&#x5E76;&#x4E0D;&#x6210;&#x6BD4;&#x4F8B;: <strong>&#x5927;&#x7684; PR &#x8981;&#x6BD4;&#x7B49;&#x91CF;&#x7684;&#x5C0F; PR &#x66F4;&#x96BE; review</strong>.</p></li><li><p>&#x96BE;&#x4EE5; review &#x5E26;&#x6765;&#x7684;&#x7ED3;&#x679C;&#x662F;, <strong>&#x5927;&#x7684; PR review &#x65F6;&#x95F4;&#x66F4;&#x957F;, &#x6536;&#x5230;&#x7684; review &#x8D28;&#x91CF;&#x66F4;&#x4F4E;</strong>. &#x4EE5;&#x4E0A;&#x4E24;&#x70B9;&#x90FD;&#x662F;&#x6709;&#x5F88;&#x591A;&#x7814;&#x7A76;&#x4F50;&#x8BC1;&#x7684;, &#x4F8B;&#x5982; &quot;<a href="https://research.google/pubs/pub47025/" aria-label="Modern Code Review: A Case Study at Google &#x2013; Google Research" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Modern Code Review: A Case Study at Google</a>&quot; &#x8FD9;&#x7BC7; paper.</p></li><li><p>Review &#x662F;&#x6709;&#x5EF6;&#x8FDF;&#x7684;. &#x5206;&#x5F00;&#x72EC;&#x7ACB;&#x7684; code review &#x4F7F;&#x5F97; <strong>&#x8F83;&#x65E9;&#x7684;&#x6539;&#x52A8;&#x5728;&#x88AB; accept &#x540E;&#x53EF;&#x4EE5;&#x5C3D;&#x65E9;&#x5408;&#x5E76;</strong>, &#x8FD9;&#x6837;&#x80FD; <strong>(i) &#x51CF;&#x5C11;&#x51B2;&#x7A81;; (ii) &#x5C3D;&#x65E9;&#x88AB;&#x522B;&#x4EBA;&#x4F7F;&#x7528;, &#x89E6;&#x53D1;&#x53EF;&#x80FD;&#x7684;&#x95EE;&#x9898;</strong>.</p><p>&#x4E3E;&#x4F8B;&#x6765;&#x8BF4;, &#x5047;&#x5982;&#x4E00;&#x4E2A;&#x5DE5;&#x4F5C;&#x7684;&#x7B2C;&#x4E00;&#x90E8;&#x5206;&#x53EF;&#x4EE5;&#x5F88;&#x5FEB;&#x88AB; accept, &#x800C;&#x5176;&#x4ED6;&#x90E8;&#x5206;&#x8FD8;&#x9700;&#x8981;&#x8BA8;&#x8BBA; &amp; review &#x4E00;&#x5468;. &#x5982;&#x679C;&#x6211;&#x4EEC;&#x7B49;&#x6574;&#x4F53;&#x88AB; accept &#x4E86;&#x518D;&#x5168;&#x90E8;&#x4E00;&#x8D77;&#x5408;&#x5E76;, &#x5219;&#x7B2C;&#x4E00;&#x90E8;&#x5206;&#x53EF;&#x80FD;&#x4F1A;&#x4E0E;&#x8FD9;&#x4E00;&#x5468;&#x91CC;&#x5176;&#x4ED6;&#x6539;&#x52A8;&#x4EA7;&#x751F;&#x5408;&#x5E76;&#x51B2;&#x7A81;, &#x800C;&#x8FD9;&#x79CD;&#x51B2;&#x7A81;&#x672C;&#x53EF;&#x4EE5;&#x5B8C;&#x5168;&#x907F;&#x514D;. &#x5982;&#x679C;&#x7B2C;&#x4E00;&#x90E8;&#x5206;&#x6709; bug, &#x8FD9;&#x4E2A; bug &#x65E9;&#x70B9;&#x5408;&#x5165;&#x4E5F;&#x66F4;&#x5BB9;&#x6613;&#x65E9;&#x70B9;&#x88AB;&#x4EBA;&#x53D1;&#x73B0;, &#x65E9;&#x70B9;&#x4FEE;&#x590D;.</p></li><li><p>&#x5C3D;&#x65E9;&#x5C06;&#x5DF2;&#x5B8C;&#x6210;&#x7684;&#x5C0F;&#x90E8;&#x5206;&#x5DE5;&#x4F5C;&#x62FF;&#x51FA;&#x6765; review, &#x8FD9;&#x6837;&#x88AB; reviewer &#x53D1;&#x73B0;&#x95EE;&#x9898;&#x53EF;&#x4EE5;&#x53CA;&#x65F6;&#x8C03;&#x6574;&#x540E;&#x7EED;&#x8DEF;&#x7EBF;. &#x5426;&#x5219;&#x7684;&#x8BDD;, &#x5982;&#x679C;&#x618B;&#x4E86;&#x4E2A;&#x5927;&#x62DB;&#x4E00;&#x8D77; review, &#x518D;&#x53D1;&#x73B0;&#x95EE;&#x9898;&#x8C03;&#x6574;&#x8D77;&#x6765;&#x5C31;&#x4F1A;&#x989D;&#x5916;&#x82B1;&#x5F88;&#x591A;&#x5DE5;&#x4F5C;&#x91CF;.</p></li><li><p>&#x4E0D;&#x540C;&#x6A21;&#x5757;&#x7684;&#x6539;&#x52A8;&#x53EF;&#x80FD;&#x9700;&#x8981;&#x7531;&#x4E0D;&#x540C;&#x7684;&#x4EBA;&#x6765; review. &#x5408;&#x5728;&#x4E00;&#x8D77; review &#x4F1A;&#x7ED9;&#x6BCF;&#x4E2A; reviewer &#x589E;&#x52A0;&#x989D;&#x5916;&#x7684;&#x5FC3;&#x667A;&#x8D1F;&#x62C5;: review &#x65F6;&#x8981;&#x627E; &quot;&#x54EA;&#x4E9B;&#x662F;&#x6211;&#x8BE5;&#x770B;&#x7684;?&quot;. &#x4E0D;&#x65AD;&#x6536;&#x5230; code review &#x5E73;&#x53F0;&#x53D1;&#x9001;&#x7684;&#x65B0;&#x7684;&#x901A;&#x77E5;&#x8981;&#x60F3; &quot;&#x8DDF;&#x6211;&#x6709;&#x6CA1;&#x6709;&#x5173;&#x7CFB;?&quot;</p></li><li><p>&#x5408;&#x5E76;&#x8FDB;&#x4ED3;&#x5E93;&#x7684; commit &#x5386;&#x53F2;&#x5E94;&#x8BE5;&#x4E0E; code review &#x5355;&#x5143;&#x4E00;&#x4E00;&#x5BF9;&#x5E94; (&#x800C;&#x4E0D;&#x5E94;&#x591A;&#x5BF9;&#x4E00;). &#x5F53;&#x56DE;&#x770B; commit &#x5386;&#x53F2;&#x65F6;, &#x5C0F;&#x7684;, &#x4E00;&#x6B21;&#x53EA;&#x89E3;&#x51B3;&#x4E00;&#x4E2A;&#x72EC;&#x7ACB;&#x95EE;&#x9898;&#x7684; commit &#x4F1A;&#x770B;&#x8D77;&#x6765;&#x66F4;&#x6E05;&#x6670;, &#x627E;&#x95EE;&#x9898;&#x4E5F;&#x4F1A;&#x66F4;&#x5BB9;&#x6613;.</p></li></ol><p>&#x56E0;&#x4E3A;&#x8FD9;&#x4E9B;&#x539F;&#x56E0;, &#x597D;&#x7684;&#x5DE5;&#x7A0B;&#x5B9E;&#x8DF5;&#x662F; <strong>&#x9F13;&#x52B1;&#x5C06;&#x5927;&#x7684;&#x5355;&#x4E2A;&#x6539;&#x52A8;&#x62C6;&#x6210;&#x591A;&#x4E2A;&#x5C0F;&#x7684;&#x90E8;&#x5206;</strong>, &#x5206;&#x5F00;&#x8FDB;&#x884C; review &#x548C;&#x63D0;&#x4EA4;. &#x6BCF;&#x4E2A;&#x90E8;&#x5206;&#x5404;&#x81EA;&#x9700;&#x8981;&#x903B;&#x8F91;&#x4E0A;&#x662F;&#x4E00;&#x4E2A;&#x5B8C;&#x6574;&#x7684;, &#x6B63;&#x786E;&#x7684;&#x5C0F;&#x5355;&#x5143;. &#x4E00;&#x4E2A;&#x6539;&#x52A8;&#x901A;&#x5E38;&#x4E0D;&#x8D85;&#x8FC7; 100 &#x884C;, &#x539F;&#x5219;&#x4E0A;&#x4E0D;&#x8D85;&#x8FC7; 300 &#x884C;.</p><p>Google &#x7684; &quot;Modern Code Review&quot; &#x8BBA;&#x6587;&#x4E2D;&#x4E5F;&#x8BF4;:</p><blockquote><p>Developers are strongly encouraged to make small, incremental changes.</p></blockquote><p><a href="https://abseil.io/resources/swe-book" aria-label="abseil / Software Engineering at Google" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Software Engineering at Google</a> &#x8FD9;&#x672C;&#x4E66;&#x4E2D;&#x6709;&#x4E00;&#x8282;&#x53EB;&#x505A; <a href="https://abseil.io/resources/swe-book/html/ch09.html#write_small_changes" aria-label="Software Engineering at Google" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&quot;Write Small Changes&quot;</a>. &#x597D;&#x5904;&#x4E0A;&#x9762;&#x5DF2;&#x7ECF;&#x5206;&#x6790;&#x8FC7;&#x4E86;&#x5C31;&#x4E0D;&#x518D;&#x91CD;&#x590D;, &#x8FD9;&#x91CC;&#x6458;&#x5F55;&#x4E00;&#x4E9B;&#x5176;&#x4E2D;&#x7684;&#x6570;&#x636E;:</p><blockquote><p>&#x201C;Small&#x201D; changes should generally be limited to about 200 lines of code. ... Most changes at Google are expected to be reviewed within about a day.</p><p>... About 35% of the changes at Google are to a single file.</p></blockquote><p>Software Enginering at Google &#x4E00;&#x4E66;&#x5199;&#x6210;&#x65F6;, Google &#x5185;&#x90E8;&#x7684; stacked diff &#x5DE5;&#x5177;&#x8FD8;&#x4E0D;&#x6210;&#x719F;, &#x56E0;&#x6B64;&#x5B9E;&#x64CD;&#x7684;&#x4FBF;&#x6377;&#x6027;&#x88AB;&#x4E66;&#x4E2D;&#x5217;&#x4E3A;&#x4E00;&#x4E2A;&#x7F3A;&#x70B9;. &#x672C;&#x7BC7;&#x6587;&#x7AE0;&#x6B63;&#x8981;&#x4ECB;&#x7ECD;&#x5982;&#x4F55;&#x7528;&#x66F4;&#x597D;&#x7684;&#x5DE5;&#x5177;&#x89E3;&#x51B3;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;. &#x9664;&#x6B64;&#x4E4B;&#x5916;, &#x6709;&#x65F6;&#x540E;, &#x62C6;&#x5206;&#x4F1A;&#x5BFC;&#x81F4;&#x5404;&#x90E8;&#x5206;&#x7684;&#x603B;&#x548C;&#x7565;&#x5927;&#x4E8E;&#x5355;&#x4E2A;&#x6539;&#x52A8;; &#x6709;&#x65F6;, &#x4E3A;&#x4E86;&#x5C06;&#x4E00;&#x4E2A;&#x5927;&#x89C4;&#x6A21;&#x6539;&#x52A8; (&#x4F8B;&#x5982;&#x91CD;&#x6784;) &#x53D8;&#x5F97; &quot;&#x53EF;&#x4EE5;&#x62C6;&#x5206;&quot;, &#x751A;&#x81F3;&#x9700;&#x8981;&#x989D;&#x5916;&#x505A;&#x4E00;&#x4E9B;&#x5DE5;&#x4F5C; (&#x4F8B;&#x5982;&#x589E;&#x52A0;&#x517C;&#x5BB9;&#x5C42;). &#x4F46; &quot;small incremental change&quot; &#x7684;&#x4F18;&#x70B9;&#x503C;&#x5F97;&#x8FD9;&#x4E9B;&#x989D;&#x5916;&#x7684;&#x4ED8;&#x51FA;.</p><h2 id="&#x5982;&#x4F55;&#x7BA1;&#x7406;code-review&#x95F4;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;">&#x5982;&#x4F55;&#x7BA1;&#x7406; code review &#x95F4;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;<a class="markdown-anchor" href="#&#x5982;&#x4F55;&#x7BA1;&#x7406;code-review&#x95F4;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;"> &#xB6;</a></h2><p>&#x5F53;&#x6709;&#x591A;&#x4E2A;&#x4E92;&#x76F8;&#x4F9D;&#x8D56;&#x7684;&#x5C0F;&#x7684; code review &#x540E;, &#x9700;&#x8981;&#x5DE5;&#x5177;&#x6765;&#x81EA;&#x52A8;&#x5316;&#x7684;&#x7BA1;&#x7406;&#x5B83;&#x4EEC;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;. &#x7B14;&#x8005;&#x5728; Meta &#x548C; Google &#x90FD;&#x4F7F;&#x7528;&#x672C;&#x5730;&#x7684; mercurial &#x4ED3;&#x5E93;&#x914D;&#x5408; Meta/Google &#x5185;&#x90E8;&#x7684; code review &#x5DE5;&#x5177;. &#x8FD9;&#x5957; workflow &#x53EF;&#x4EE5;&#x975E;&#x5E38;&#x65B9;&#x4FBF;&#x7684;&#x7BA1;&#x7406; code review &#x95F4;&#x7684;&#x4F9D;&#x8D56;.</p><p>&#x4E0B;&#x9762;&#x4EE5;&#x51E0;&#x4E2A;&#x4F8B;&#x5B50;&#x8BF4;&#x660E; Meta &#x7684;&#x57FA;&#x4E8E; mercurial &#x4ED3;&#x5E93; + Phabricator Diff &#x7684; workflow &#x4E3A;&#x4EC0;&#x4E48;&#x4F18;&#x4E8E; git &#x4ED3;&#x5E93; + github PR &#x7684; workflow. &#x5728;&#x6BCF;&#x4E2A;&#x4F8B;&#x5B50;&#x4E2D;, &#x7528;&#x1F61E;&#x6765;&#x8868;&#x793A;&#x4F53;&#x9A8C;&#x7CDF;&#x7CD5;&#x7684;&#x90E8;&#x5206;.</p><p><u>Example 1</u>: &#x6211;&#x4EEC;&#x4EE5;&#x8FD9;&#x6837;&#x4E24;&#x4E2A;&#x6539;&#x52A8;&#x5F00;&#x59CB;:</p><ul><li>&#x6539;&#x52A8; A: &#x4E3A;&#x6A21;&#x5757; A &#x6DFB;&#x52A0;&#x4E00;&#x4E2A; API &#x53CA;&#x6D4B;&#x8BD5;</li><li>&#x6539;&#x52A8; X: &#x57FA;&#x4E8E; A &#x6765;&#x5B9E;&#x73B0; feature X &#x53CA;&#x6DFB;&#x52A0;&#x6D4B;&#x8BD5;</li></ul><p>&#x5B83;&#x4EEC;&#x6709;&#x4F9D;&#x8D56;&#x5173;&#x7CFB; <code>A &lt;- X</code> . &#x5728; Meta, &#x6211;&#x4F1A;&#x8FD9;&#x4E48;&#x505A;:</p><ul><li>&#x4E24;&#x4E2A;&#x6539;&#x52A8;&#x5C31;&#x662F;&#x672C;&#x5730;&#x4ED3;&#x5E93; main branch &#x7684;&#x4E24;&#x4E2A; commit. &#x4E0D;&#x7BA1;&#x6211;&#x5F53;&#x524D;&#x7684; checkout &#x662F;&#x54EA;&#x4E2A; commit, &#x4ECE;<code>hg log</code> &#x91CC;&#x90FD;&#x80FD;&#x770B;&#x5230;&#x5168;&#x90E8;&#x4E24;&#x4E2A;.</li><li>&#x901A;&#x8FC7;&#x4E00;&#x4E2A;&#x547D;&#x4EE4;&#x53EF;&#x4EE5;&#x5C06;&#x4E24;&#x4E2A; commits &#x4E00;&#x8D77;&#x53D1;&#x9001;&#x5230; phabricator &#x4E0A;&#x6210;&#x4E3A;&#x4E24;&#x4E2A; diff.</li><li>&#x5B83;&#x4EEC;&#x53EF;&#x4EE5;&#x88AB;&#x72EC;&#x7ACB; review, UI &#x4F1A;&#x663E;&#x793A;&#x5B83;&#x4EEC;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;.</li></ul><p>&#x5982;&#x679C;&#x4F7F;&#x7528; github, &#x6211;&#x5C06;&#x4E0D;&#x5F97;&#x4E0D;&#x8FD9;&#x4E48;&#x505A;:</p><ul><li>&#x1F61E;&#x53D6;&#x4E24;&#x4E2A;&#x65B0; branch &#x7684;&#x540D;&#x5B57; (&#x8FD9;&#x91CC;&#x6682;&#x4E14;&#x53EB; branchA &#x4E0E; branchX).</li><li>&#x5728; branchA &#x5B9E;&#x73B0;&#x6539;&#x52A8; A &#x540E;, &#x5728;&#x53E6;&#x4E00;&#x4E2A; branchX &#x5B9E;&#x73B0;&#x6539;&#x52A8; X. &#x1F61E;&#x53EA;&#x6709; branchX &#x7684;<code>git log</code> &#x80FD;&#x770B;&#x5230;&#x4E24;&#x4E2A;&#x6539;&#x52A8;, branchA &#x53EA;&#x80FD;&#x770B;&#x5230;&#x6539;&#x52A8; A.</li><li>&#x1F61E;&#x7528; <strong>&#x81F3;&#x5C11;&#x4E24;&#x4E2A;&#x547D;&#x4EE4;</strong> &#x5C06;&#x4E24;&#x4E2A; branch &#x5206;&#x522B; push. &#x4E00;&#x4E2A;&#x547D;&#x4EE4;&#x505A;&#x4E0D;&#x5230;, &#x56E0;&#x4E3A; git &#x4ED3;&#x5E93;&#x5E76;&#x4E0D;&#x77E5;&#x9053; branch &#x4E4B;&#x95F4;&#x6709;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;.</li><li>&#x4ECE;&#x8FD9;&#x4E24;&#x4E2A; branch &#x521B;&#x5EFA;&#x4E24;&#x4E2A; PR. &#x6CE8;&#x610F;&#x8FD9;&#x91CC; PR A &#x7684; merge target &#x662F;&#x9879;&#x76EE;&#x7684; main branch, &#x800C; PR X &#x7684; merge target &#x9700;&#x8981;&#x662F; branchA (&#x5426;&#x5219;&#x8FD9;&#x4E2A; PR &#x91CC;&#x5C31;&#x4F1A;&#x6709;&#x4E24;&#x4E2A;&#x6539;&#x52A8;, &#x6CA1;&#x6CD5;&#x72EC;&#x7ACB; review &#x4E86;). &#x1F61E;merge target &#x9700;&#x8981;&#x4E00;&#x4E9B;&#x624B;&#x5DE5;&#x64CD;&#x4F5C;&#x6765;&#x8BBE;&#x7F6E;, &#x56E0;&#x4E3A; github &#x5E76;&#x4E0D;&#x77E5;&#x9053;&#x4E24;&#x4E2A; branch &#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;.</li><li>&#x1F61E;PR X &#x7684; UI &#x4E0A;&#x80FD;&#x770B;&#x5230; merge target &#x662F; branchA, &#x4F46;&#x662F;&#x770B;&#x4E0D;&#x5230; PR A &#x7684;&#x94FE;&#x63A5;. &#x8981;&#x65B9;&#x4FBF;&#x7684;&#x94FE;&#x63A5;&#x5230; PR A &#x8FD8;&#x5F97;&#x7528;&#x5176;&#x5B83;&#x5DE5;&#x5177; (&#x6216;&#x624B;&#x52A8;&#x63CF;&#x8FF0;). &#x8FD9;&#x4E3B;&#x8981;&#x662F;&#x4E2A; UI &#x95EE;&#x9898;.</li></ul><p><u>Example 2</u>: &#x7EE7;&#x7EED;&#x4E0A;&#x4E00;&#x4E2A; example, &#x5728;&#x7ECF;&#x8FC7;&#x4E00;&#x4E9B; review &#x540E;, &#x6211;&#x4EEC;&#x9700;&#x8981;&#x5BF9;&#x6539;&#x52A8; A &#x7684;&#x5185;&#x5BB9;&#x8FDB;&#x884C;&#x4FEE;&#x6539;. &#x5728; Meta &#x6211;&#x4F1A;&#x8FD9;&#x4E48;&#x505A;:</p><img src="/blog/2023/Stacked-Diffs-Pull-Requests/example2-diff.png" class="center"><ul><li>&#x672C;&#x5730; amend commitA. &#x9ED8;&#x8BA4;&#x81EA;&#x52A8;&#x89E6;&#x53D1; rebase commitX. &#x8FD9;&#x6837;&#x672C;&#x5730;&#x4ECD;&#x7136;&#x662F;&#x4E24;&#x4E2A; commits. &#x901A;&#x8FC7;&#x4E00;&#x4E2A;&#x547D;&#x4EE4;&#x5C06; phabricator &#x4E0A;&#x7684;&#x4E24;&#x4E2A; diff &#x90FD;&#x540C;&#x6B65;&#x5230;&#x6700;&#x65B0;.</li><li>phabricator &#x4F1A;&#x4FDD;&#x5B58;&#x65E7;&#x7248;&#x672C;&#x7684; commitA, &#x5982;&#x679C;&#x8981;&#x770B;&#x65E7;&#x7248;&#x672C;&#x4E5F;&#x6709;&#x529E;&#x6CD5;.</li><li>reviewer &#x53EF;&#x4EE5;&#x9009;&#x62E9; review &#x5B8C;&#x6574;&#x7684; commitA, &#x4E5F;&#x53EF;&#x4EE5;&#x9009;&#x62E9; review commitA &#x7684;&#x589E;&#x91CF;.</li></ul><p>&#x5982;&#x679C;&#x4F7F;&#x7528; github, &#x6211;&#x9700;&#x8981;:</p><img src="/blog/2023/Stacked-Diffs-Pull-Requests/example2-PR.png" class="center"><ul><li>&#x5728; branchA &#x4E0A;&#x52A0;&#x5165;&#x989D;&#x5916;&#x7684; commitA-fix &#x518D; push. &#x6216;&#x8005;&#x5728; branchA &#x4E0A; amend commitA &#x518D; force push. &#x1F61E;&#x5B83;&#x4EEC;&#x5404;&#x6709;&#x5404;&#x7684;&#x95EE;&#x9898;:<ul><li>amend &#x6CA1;&#x6CD5;&#x5F88;&#x597D;&#x7684;&#x4FDD;&#x7559;&#x65E7;&#x7684; commitA (&#x8FD9;&#x5355;&#x7EAF;&#x662F;&#x4E2A; github UI &#x7684;&#x95EE;&#x9898;, &#x800C;&#x4E0D;&#x662F; git &#x7684;&#x95EE;&#x9898;).</li><li>amend &#x4F1A;&#x5BFC;&#x81F4; rebase branchX on branchA &#x7684;&#x65F6;&#x5019;&#x4EA7;&#x751F; conflict. (<a href="#Commit-Identifier">&#x4E0B;&#x4E00;&#x8282;</a>&#x89E3;&#x91CA;)</li><li>&#x5982;&#x679C;&#x5728; branchA &#x91CC;&#x52A0;&#x5165;&#x4E00;&#x4E2A;&#x4E0D;&#x5E72;&#x51C0;&#x7684; commitA-fix (&#x5373;&#x5E76;&#x4E0D;&#x9700;&#x8981;&#x6700;&#x7EC8;&#x4FDD;&#x7559;&#x5728; main branch &#x91CC;&#x7684; commit), &#x5728; rebase branchA on main &#x65F6;&#x4F1A;&#x5E26;&#x6765;&#x5F88;&#x591A;&#x4E0D;&#x5FC5;&#x8981;&#x7684;&#x75DB;&#x82E6;. &#x4F8B;&#x5982;: &#x5047;&#x8BBE; commitA &#x548C; commitA-fix &#x90FD;&#x5BF9;&#x51FD;&#x6570; func &#x8FDB;&#x884C;&#x4E86;&#x4FEE;&#x6539;. &#x800C;&#x4E0E;&#x6B64;&#x540C;&#x65F6; main branch &#x7684; func &#x4E5F;&#x6709;&#x4E86;&#x53D8;&#x5316;. &#x8FD9;&#x65F6;&#x5019; rebase branchA on main &#x65F6;&#x9700;&#x8981; resolve conflict &#x4E24;&#x6B21;.</li></ul></li><li>&#x1F61E;&#x65E0;&#x8BBA;&#x600E;&#x6837;, &#x90FD;&#x9700;&#x8981;&#x518D;&#x624B;&#x52A8;&#x5207;&#x6362;&#x5230; branchX, rebase on branchA, &#x518D; force push branchX, &#x624D;&#x80FD;&#x7EF4;&#x62A4;&#x597D;&#x5B83;&#x4EEC;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;.</li></ul><p><u>Example 3</u>: &#x63A5;&#x7740;&#x4E0A;&#x4E00;&#x4E2A; example, &#x5728;&#x7ECF;&#x8FC7;&#x4E00;&#x4E9B; review &#x540E;, &#x6211;&#x4EEC;&#x53D1;&#x73B0;&#x9700;&#x8981;&#x989D;&#x5916;&#x5BF9;&#x51FD;&#x6570; S &#x8FDB;&#x884C;&#x4FEE;&#x6539;&#x624D;&#x80FD;&#x66F4;&#x597D;&#x7684;&#x5B9E;&#x73B0; feature A. &#x4E5F;&#x5373;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;&#x4E3A;<code>S &lt;- A &lt;- X</code>. &#x5728; Meta &#x6211;&#x4F1A;&#x8FD9;&#x4E48;&#x505A;:</p><img src="/blog/2023/Stacked-Diffs-Pull-Requests/example3-diff.png" class="center"><ul><li>&#x76F4;&#x63A5;&#x5728; base &#x7684;&#x57FA;&#x7840;&#x4E0A;&#x6DFB;&#x52A0; commitS.</li><li>&#x4E00;&#x4E2A;&#x547D;&#x4EE4;&#x5C06; commitA &#x548C; commitX rebase &#x5230; commitS &#x4E0A;. &#x5982;&#x6709;&#x9700;&#x8981;, amend commitA &#x548C; commitX &#x7684;&#x5185;&#x5BB9;</li><li>&#x4E00;&#x4E2A;&#x547D;&#x4EE4;&#x5C06; phabricator &#x7684;&#x72B6;&#x6001;&#x4E0E;&#x672C;&#x5730;&#x540C;&#x6B65;, &#x5373;: &#x521B;&#x5EFA;&#x65B0;&#x7684; diffS, &#x66F4;&#x65B0;&#x5DF2;&#x6709;&#x7684; diffA, diffX, &#x5E76;&#x66F4;&#x65B0;&#x4E09;&#x4E2A; diff &#x95F4;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;.</li></ul><p>&#x5982;&#x679C;&#x4F7F;&#x7528; github, &#x6211;&#x9700;&#x8981;:</p><img src="/blog/2023/Stacked-Diffs-Pull-Requests/example3-PR.png" class="center"><ul><li>&#x4ECE; main branch &#x5F00;&#x8F9F;&#x65B0; branchS, &#x6DFB;&#x52A0; commitS</li><li>rebase branchA on branchS, &#x5982;&#x6709;&#x9700;&#x8981;, &#x4FEE;&#x6539; branchA &#x7684;&#x5185;&#x5BB9;.</li><li>&#x1F61E;&#x518D;&#x6B21; rebase branchX on branchA.</li><li>&#x1F61E;&#x7C7B;&#x4F3C; Example1 &#x4E2D;&#x7684;&#x7F3A;&#x70B9;, &#x6211;&#x4EEC;&#x8FD9;&#x91CC;&#x9700;&#x8981;&#x5206;&#x522B; push branchS, branchA &#x548C; branchX, &#x5E76;&#x4E14;&#x624B;&#x52A8;&#x5C06; PR A &#x7684; merge target &#x6539;&#x4E3A; branchS.</li><li>&#x1F61E;UI &#x95EE;&#x9898;: &#x5728; PR X &#x91CC;&#x80FD;&#x770B;&#x5230;&#x5B83;&#x5BF9; branchA &#x7684;&#x4F9D;&#x8D56;, &#x4F46;&#x770B;&#x4E0D;&#x5230;&#x5B83;&#x5BF9; branchS / PR S &#x7684;&#x4F9D;&#x8D56;.</li></ul><p><u>Example 4</u>: &#x63A5;&#x7740;&#x4E0A;&#x4E00;&#x4E2A; example, &#x5047;&#x5982;&#x6211;&#x4EEC;&#x6709;<code>S &lt;- A &lt;- X &lt;- Y</code> &#x7684;&#x4F9D;&#x8D56;&#x94FE;, &#x6B64;&#x65F6; S &#x548C; A &#x90FD;&#x88AB; accept, &#x6211;&#x4EEC;&#x60F3;&#x8981;&#x5C3D;&#x5FEB;&#x5C06;&#x5176;&#x5408;&#x5E76;, &#x5E76;&#x5728;&#x5408;&#x5E76;&#x540E;&#x7684;&#x6700;&#x65B0;&#x4E3B;&#x7EBF;&#x4E0A;&#x7EE7;&#x7EED;&#x5F00;&#x53D1; X &#x548C; Y. &#x5728; Meta &#x6211;&#x4F1A;:</p><ul><li>&#x5728; phabricator &#x4E0A;&#x7528;&#x6309;&#x94AE;&#x5C06;&#x5176;&#x5408;&#x5E76;.</li><li>&#x5728;&#x672C;&#x5730;&#x4ED3;&#x5E93;&#x91CC; rebase on main. rebase &#x5B8C;&#x6210;&#x540E;&#x672C;&#x5730;&#x53EA;&#x5269;&#x4E0B; commitX &#x548C; commitY. &#x4E00;&#x4E2A;&#x547D;&#x4EE4;&#x540C;&#x6B65;&#x8FD9;&#x4E24;&#x4E2A; diff &#x7684;&#x72B6;&#x6001;.</li></ul><p>&#x800C;&#x4F7F;&#x7528; github &#x65F6;, &#x6211;&#x9700;&#x8981;:</p><ul><li>&#x5728; github &#x4E0A;&#x64CD;&#x4F5C;&#x5C06; S &#x5408;&#x5E76;</li><li>&#x1F61E;&#x5728; github &#x4E0A;&#x518D;&#x6B21;&#x64CD;&#x4F5C;, &#x5C06; PR A &#x7684; merge target &#x8BBE;&#x4E3A; main branch, &#x518D;&#x5C06; A &#x5408;&#x5E76;</li><li>&#x672C;&#x5730;&#x5C06; branchX rebase on main. &#x1F61E;&#x5C06; branchX push &#x5230; PR X &#x5E76;&#x624B;&#x52A8;&#x628A; merge target &#x8BBE;&#x4E3A; main branch.</li><li>&#x1F61E;&#x518D;&#x5C06; branchY rebase on branchX. push branchY.</li></ul><p>&#x4ECE;&#x8FD9;&#x51E0;&#x4E2A;&#x4F8B;&#x5B50;&#x53EF;&#x4EE5;&#x770B;&#x51FA;, github workflow &#x7684;&#x672C;&#x8D28;&#x7F3A;&#x70B9;&#x5728;&#x4E8E;: &#x65E0;&#x8BBA;&#x662F; git &#x8FD8;&#x662F; github &#x90FD; <strong>&#x6CA1;&#x6709;&#x5145;&#x5206;&#x7684;&#x5173;&#x4E8E; branch &#x4E4B;&#x95F4;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB; (&#x4E5F;&#x5373; PR &#x4E4B;&#x95F4;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;) &#x7684;&#x4FE1;&#x606F;</strong>. &#x8FD9;&#x5E26;&#x6765;&#x7684;&#x4E3B;&#x8981;&#x95EE;&#x9898;&#x662F;:</p><blockquote><p>&#x5F53;&#x591A;&#x4E2A; PR &#x7684;&#x4F9D;&#x8D56;&#x94FE;&#x6761;&#x8F83;&#x957F;&#x65F6;, &#x6BCF;&#x6B21;&#x6539;&#x53D8;&#x4E2D;&#x95F4; PR &#x7684;&#x5185;&#x5BB9;, &#x6216;&#x5408;&#x5E76; / &#x5220;&#x9664;&#x4E86;&#x67D0;&#x4E2A;&#x4E2D;&#x95F4; PR &#x540E;, &#x90FD;&#x9700;&#x8981;&#x4E00;&#x4E2A;&#x4E2A;&#x624B;&#x52A8; rebase &#x6240;&#x6709;&#x4F9D;&#x8D56;&#x5B83;&#x7684;&#x540E;&#x7EED; branch, &#x5E76;&#x624B;&#x52A8; push github. &#x6709;&#x65F6;&#x5019;&#x8FD8;&#x9700;&#x8981;&#x624B;&#x52A8;&#x6539; github merge target.</p></blockquote><p>&#x800C;&#x5F53; commit &#x4F5C;&#x4E3A;&#x5DE5;&#x4F5C;&#x5355;&#x5143;&#x65F6;, &#x4EE5;&#x4E0A;&#x8FD9;&#x4E9B;&#x5DE5;&#x4F5C;&#x90FD;&#x53EF;&#x4EE5;&#x81EA;&#x52A8;&#x5B8C;&#x6210;: &#x5F53;&#x4E2D;&#x95F4; commit &#x88AB;&#x6539;&#x52A8;&#x65F6;, &#x6240;&#x6709;&#x9700;&#x8981;&#x88AB; rebase/push &#x7684; commit &#x90FD;&#x53EF;&#x4EE5;&#x901A;&#x8FC7;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;&#x81EA;&#x52A8;&#x627E;&#x5230;.</p><h2 id="Commit-Identifier">Commit Identifier<a class="markdown-anchor" href="#Commit-Identifier">&#xB6;</a></h2><p>&#x9664;&#x4E86;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;&#x7684;&#x7F3A;&#x5931;&#x4E4B;&#x5916;, &#x53E6;&#x4E00;&#x4E2A; git/github &#x7684;&#x7F3A;&#x70B9;&#x662F;, <strong>branch &#x4E4B;&#x95F4; rebase &#x6709;&#x66F4;&#x5927;&#x7684;&#x6982;&#x7387;&#x4EA7;&#x751F; conflict</strong>. &#x8FD9;&#x662F;&#x7531;&#x4E8E;&#x7F3A;&#x5C11;&#x4E00;&#x79CD; commit identifier &#x673A;&#x5236;.</p><p>&#x4EC0;&#x4E48;&#x662F; commit identifier? &#x5728;&#x57FA;&#x4E8E; branch &#x7684; workflow &#x91CC;, &#x672C;&#x5730; branch &#x4E0E;&#x8FDC;&#x7AEF; PR &#x901A;&#x8FC7; &quot;branch &#x7684;&#x540D;&#x5B57;&quot; &#x8FD9;&#x4E2A; identifier &#x6765;&#x5339;&#x914D;. &#x5728;&#x57FA;&#x4E8E; commit &#x7684; workflow &#x91CC;, commit &#x4E0E;&#x8FDC;&#x7AEF; diff &#x4E5F;&#x9700;&#x8981;&#x4E00;&#x79CD;&#x5339;&#x914D;&#x673A;&#x5236;, &#x5DE5;&#x5177;&#x624D;&#x77E5;&#x9053;&#x6BCF;&#x4E2A; commit &#x5E94;&#x8BE5;&#x66F4;&#x65B0;&#x54EA;&#x4E2A; diff. &#x5B83;&#x7684;&#x5B9E;&#x73B0;&#x65B9;&#x5F0F;&#x4E00;&#x822C;&#x901A;&#x8FC7;&#x672C;&#x5730;&#x5DE5;&#x5177; (&#x5982; hg) &#x5728; commit metadata &#x91CC;&#x6DFB;&#x52A0;&#x4E00;&#x4E2A;&#x968F;&#x673A; unique identifier &#x6765;&#x5B9E;&#x73B0;. &#x540C;&#x65F6;, &#x672C;&#x5730;&#x5DE5;&#x5177;&#x9700;&#x8981;&#x7EF4;&#x62A4;&#x8FD9;&#x4E2A; identifier, &#x786E;&#x4FDD;&#x4E00;&#x4E2A; commit &#x5728;&#x7ECF;&#x5386;&#x4E86; rebase, reorder, amend &#x7B49;&#x64CD;&#x4F5C;&#x540E; identifier &#x4E0D;&#x53D8;, &#x4E14;&#x5728; squash &#x64CD;&#x4F5C;&#x65F6;&#x8BE2;&#x95EE;&#x7528;&#x6237;&#x4FDD;&#x7559;&#x54EA;&#x4E2A; identifier. &#x8FD9;&#x4E2A; commit identifier &#x66FF;&#x4EE3;&#x4E86; &quot;branch &#x540D;&#x5B57;&quot; &#x7684;&#x529F;&#x80FD;.</p><p>&#x4E0D;&#x4EC5;&#x5982;&#x6B64;, commit identifier &#x80FD;&#x4F7F;&#x5F97; rebase &#x7684;&#x4F53;&#x9A8C;&#x66F4;&#x52A0;&#x7684;&#x4E1D;&#x6ED1;. &#x4F8B;&#x5982;, &#x5728;&#x4E0A;&#x4E00;&#x8282;&#x7684; Example 2 &#x4E2D;, &#x6211;&#x4EEC;&#x8981;&#x5C06; branchX rebase &#x5230;&#x4FEE;&#x6539;&#x540E;&#x7684; branchA &#x4E0A;:</p><img src="/blog/2023/Stacked-Diffs-Pull-Requests/rebase-problem.png" class="center"><p>&#x56FE;&#x4E2D;&#x7684; rebase &#x5E76;&#x6CA1;&#x6709;&#x60F3;&#x8C61;&#x4E2D;&#x90A3;&#x4E48;&#x7B80;&#x5355;: &#x7531;&#x4E8E; git &#x5E76;&#x4E0D;&#x77E5;&#x9053; commitA &#x4E0E; new commitA &#x4E4B;&#x95F4;&#x6709;&#x4EFB;&#x4F55;&#x5173;&#x7CFB;, git &#x4F1A;&#x5C1D;&#x8BD5;<strong>&#x5C06; commitA, commitX &#x5206;&#x522B;&#x5E94;&#x7528;&#x5230; new commitA &#x4E0A;</strong>. &#x800C;&#x5C06; commitA &#x5E94;&#x7528;&#x5230; new commitA &#x4E0A;&#x51E0;&#x4E4E;&#x4E00;&#x5B9A;&#x4F1A;&#x4EA7;&#x751F; conflict. &#x7136;&#x800C;, &#x5F53;&#x6709;&#x4E86; commit identifier &#x540E;, rebase &#x5DE5;&#x5177;&#x901A;&#x8FC7; identifier &#x548C; commit &#x65F6;&#x95F4;&#x77E5;&#x9053; &quot;new commitA&quot; &#x662F;&#x6700;&#x65B0;&#x7248;&#x7684; &quot;commitA&quot;, &#x5C31;&#x53EF;&#x4EE5;&#x76F4;&#x63A5;&#x907F;&#x514D;&#x8FD9;&#x4E2A; conflict.</p><p>&#x53E6;&#x5916;, &#x4E00;&#x4E2A;&#x5E38;&#x89C1;&#x7684;&#x5C0F;&#x95EE;&#x9898;&#x662F; github PR &#x7684; inline comment &#x7ECF;&#x5E38;&#x4F1A;&#x5728; force-push &#x4E4B;&#x540E;&#x4E22;&#x5931;, &#x8FD9;&#x540C;&#x6837;&#x662F;&#x56E0;&#x4E3A; github &#x4E0D;&#x77E5;&#x9053;&#x65B0;&#x7684; commit &#x4E0E;&#x65E7;&#x7684;&#x7684;&#x5BF9;&#x5E94;&#x5173;&#x7CFB;.</p><h2 id="From-Stack-to-DAG">From Stack to DAG<a class="markdown-anchor" href="#From-Stack-to-DAG">&#xB6;</a></h2><p>&#x4E0D;&#x96BE;&#x60F3;&#x5230;, &#x4E0D;&#x540C;&#x7684; PR/diff &#x4E4B;&#x95F4;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;&#x672A;&#x5FC5;&#x662F;&#x4E00;&#x6761;&#x5355;&#x94FE;&#x8868;, &#x800C;&#x53EF;&#x4EE5;&#x662F;&#x4E00;&#x4E2A;&#x6709;&#x5411;&#x65E0;&#x73AF;&#x56FE; (DAG). &#x8FD9;&#x79CD;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;&#x5C31;&#x66F4;&#x96BE;&#x5728; git &#x4E2D;&#x5904;&#x7406;&#x4E86;, &#x8FD9;&#x662F; git &#x7684;&#x53E6;&#x4E00;&#x4E2A;&#x5C0F;&#x7F3A;&#x70B9;.</p><p>&#x76F8;&#x8F83;&#x4E8E; git branch &#x5185;&#x7684;&#x6240;&#x6709; commits &#x5FC5;&#x987B;&#x662F;&#x4E00;&#x6761;&#x76F4;&#x7EBF;, &#x4E00;&#x4E2A; mercurial &#x4ED3;&#x5E93;&#x7684;&#x672C;&#x5730; workspace &#x53EF;&#x4EE5;&#x5305;&#x542B;&#x5206;&#x652F;, &#x4F8B;&#x5982;, &#x6211;&#x53EF;&#x4EE5;&#x5728;&#x672C;&#x5730;&#x521B;&#x5EFA;&#x8FD9;&#x6837; 5 &#x4E2A; WIP &#x7684; commits, &#x5B83;&#x4EEC;&#x53EF;&#x4EE5;&#x6709; DAG &#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;, &#x4F53;&#x73B0;&#x5728;&#x5DE6;&#x8FB9;&#x7684; ASCII &#x7EBF;&#x6761;&#x4E2D;:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ hg log --graph<br>@  changeset:   30:abcdef123456<br>|  user:        John Doe<br>|  summary:     Fix a small bug.<br>|<br>| o  changeset:   29:789012345678<br>| |  user:        John Doe<br>| |  summary:     Implemented feature X<br>| |<br>| | o  changeset:   27:abcdef012345<br>| |/   user:        John Doe<br>| |    summary:     Updated documentation for module B<br>| |<br>| o  changeset:   26:456789abcdef<br>| |  user:        John Doe<br>| |  summary:     Fix module B<br>| |<br>| o  changeset:   25:abcdef012345<br>|/   user:        John Doe<br>|    summary:     Change module A<br>|<br></code></pre></td></tr></table></figure><p>&#x7531;&#x4E8E; code review &#x4E0E; commits &#x5BF9;&#x5E94;, &#x8FD9; 5 &#x4E2A; commits &#x5C06;&#x6210;&#x4E3A; 5 &#x4E2A; &quot;diff&quot; &#x4EE5;&#x4F9B; review. Phabricator &#x7684; UI &#x4E0A;&#x4E5F;&#x53EF;&#x4EE5;&#x663E;&#x793A; diff &#x95F4;&#x7684; DAG &#x5173;&#x7CFB;, &#x4F8B;&#x5982;:</p><img src="/blog/2023/Stacked-Diffs-Pull-Requests/phabricator.png" class="center"><h2 id="&#x8BA9;-git-github-&#x66F4;&#x597D;">&#x8BA9; git + github &#x66F4;&#x597D;<a class="markdown-anchor" href="#&#x8BA9;-git-github-&#x66F4;&#x597D;"> &#xB6;</a></h2><p>&#x5728; Meta/Google &#x5DE5;&#x4F5C;&#x65F6;, &#x6211;&#x7684; mercurial workspace &#x91CC;&#x901A;&#x5E38;&#x6709;&#x6570;&#x5341;&#x4E2A;&#x5F00;&#x53D1;&#x4E2D;&#x7684; commits, &#x5BF9;&#x5E94;&#x7740; code review &#x5E73;&#x53F0;&#x4E0A;&#x7684; diffs (&#x5728; Google &#x53C8;&#x53EB; CL). &#x5B83;&#x4EEC;&#x53EF;&#x80FD;&#x6709;&#x590D;&#x6742;&#x7684; DAG &#x4F9D;&#x8D56;, &#x4E5F;&#x53EF;&#x80FD;&#x662F;&#x72EC;&#x7ACB;&#x7684;. &#x5B83;&#x4EEC;&#x6709;&#x7684;&#x662F;&#x4E25;&#x8083;&#x7684;&#x5F00;&#x53D1;, &#x6709;&#x7684;&#x662F; prototype, &#x6709;&#x7684;&#x53EA;&#x7528;&#x6765;&#x4E34;&#x65F6; debug, &#x4F46;&#x662F;&#x6CA1;&#x5173;&#x7CFB;, &#x56E0;&#x4E3A;&#x6211;&#x53EF;&#x4EE5;&#x9009;&#x62E9;&#x54EA;&#x4E9B; commits &#x8981;&#x7ED9;&#x4EBA; review, &#x4E0D;&#x4F1A;&#x53D7;&#x5230;&#x65B0;&#x589E; commits &#x7684;&#x5F71;&#x54CD;. &#x6211;&#x4E5F;&#x53EF;&#x4EE5;&#x65B9;&#x4FBF;&#x7684;&#x901A;&#x8FC7; amend/rebase &#x4FEE;&#x6539; commits &#x6216;&#x5B83;&#x4EEC;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;, &#x5E76;&#x4E14;&#x6240;&#x6709;&#x4FEE;&#x6539;&#x90FD;&#x53EF;&#x4EE5;&#x4E00;&#x952E;&#x4E0E; code review &#x5E73;&#x53F0;&#x540C;&#x6B65;. &#x5728; git &#x4E0A;&#x5982;&#x4F55;&#x590D;&#x523B;&#x8FD9;&#x79CD;&#x4F53;&#x9A8C;, &#x4ECD;&#x7136;&#x662F;&#x4E2A;&#x96BE;&#x9898;.</p><p>&#x5982;&#x679C;&#x8981;&#x5728;&#x4E0D;&#x6539;&#x53D8; git / github &#x7684;&#x60C5;&#x51B5;&#x4E0B;, &#x5B9E;&#x73B0;&#x63A5;&#x8FD1; stacked diff &#x7684; workflow, &#x5C31;&#x9700;&#x8981;&#x5B9E;&#x73B0;&#x4E00;&#x4E2A;&#x65B0;&#x7684; git &#x4ED3;&#x5E93;&#x7BA1;&#x7406;&#x5DE5;&#x5177;, &#x8D1F;&#x8D23;:</p><ul><li>&#x4E3A;&#x6BCF;&#x4E2A; commit &#x521B;&#x5EFA; branch, &#x5E76;&#x8BB0;&#x5F55;&#x4ED6;&#x4EEC;&#x7684;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;</li><li>&#x901A;&#x8FC7;&#x4F9D;&#x8D56;&#x5173;&#x7CFB;, &#x8FDB;&#x884C;&#x81EA;&#x52A8;&#x7684; rebase &#x7B49;&#x7EF4;&#x62A4;&#x5DE5;&#x4F5C;</li><li>&#x4E3A;&#x6BCF;&#x4E2A; commit &#x521B;&#x5EFA;&#x4E00;&#x4E2A; identifier, &#x636E;&#x6B64;&#x7EF4;&#x62A4; commit &#x4E0E; PR &#x7684;&#x5BF9;&#x5E94;&#x5173;&#x7CFB;</li><li>&#x5728;&#x672C;&#x5730;&#x663E;&#x793A; commits &#x4E4B;&#x95F4; DAG &#x5F62;&#x6001;&#x7684;&#x4F9D;&#x8D56;, &#x7528;&#x4E8E;&#x66FF;&#x4EE3;<code>git log</code>.</li><li>&#x5C06;&#x5FC5;&#x8981;&#x7684;&#x4F9D;&#x8D56;&#x4FE1;&#x606F;&#x901A;&#x8FC7; github API &#x66F4;&#x65B0;&#x5230; PR &#x4E0A;</li></ul><p>&#x6709;&#x4E00;&#x4E9B;&#x5DE5;&#x5177;&#x5DF2;&#x7ECF;&#x90E8;&#x5206;&#x5B9E;&#x73B0;&#x4E86;&#x8FD9;&#x4E9B;&#x529F;&#x80FD;, &#x4F8B;&#x5982;:</p><ul><li><a href="https://github.com/ezyang/ghstack" aria-label="ezyang/ghstack: Submit stacked diffs to GitHub on the command line" class="hint--top hint--rounded hint--no-animate hint--no-arrow">ghstack</a>: PyTorch &#x56E2;&#x961F;&#x5F00;&#x53D1;&#x7684;&#x7684; stacked PR &#x5DE5;&#x5177;. &#x9664;&#x4E86; readme &#x63D0;&#x5230;&#x7684;&#x4E00;&#x4E9B;&#x5C0F;&#x95EE;&#x9898;&#x5916;&#x4F53;&#x9A8C;&#x8FD8;&#x4E0D;&#x9519;.</li><li><a href="https://github.com/arxanas/git-branchless" aria-label="arxanas/git-branchless: High-velocity, monorepo-scale workflow for Git" class="hint--top hint--rounded hint--no-animate hint--no-arrow">git-branchless</a>: &#x6709; DAG &#x53CA;&#x81EA;&#x52A8; rebase &#x7B49;&#x7684;&#x529F;&#x80FD;. &#x672C;&#x5730;&#x4F53;&#x9A8C;&#x8FD8;&#x4E0D;&#x9519;, &#x4F46;&#x5E76;&#x6CA1;&#x6709;&#x548C; github &#x6574;&#x5408;.<ul><li>(Update: &#x5728; Moonshot AI, &#x6211;&#x4EEC;&#x57FA;&#x4E8E; git-branchless &#x5B9E;&#x73B0;&#x4E86;&#x4E00;&#x4E2A;&#x6BD4;&#x8F83;&#x597D;&#x7528;&#x7684;&#x5DE5;&#x5177;.)</li></ul></li><li><a href="https://github.com/ejoffe/spr" aria-label="ejoffe/spr: Stacked Pull Requests on GitHub" class="hint--top hint--rounded hint--no-animate hint--no-arrow">git-spr</a>: &#x8BD5;&#x8FC7;&#x4E00;&#x6B21;, &#x5F53;&#x65F6;&#x4F53;&#x9A8C;&#x5E76;&#x4E0D;&#x597D;.</li><li><a href="https://github.com/martinvonz/jj" aria-label="martinvonz/jj: A Git-compatible DVCS that is both simple and powerful" class="hint--top hint--rounded hint--no-animate hint--no-arrow">jj</a>: &#x6709; DAG &#x53CA;&#x81EA;&#x52A8; rebase &#x7B49;&#x529F;&#x80FD;, &#x4F46;&#x5E76;&#x6CA1;&#x6709;&#x548C; github &#x6574;&#x5408;. &#x4F5C;&#x8005;&#x662F; google &#x5185;&#x90E8; stacked diff &#x5DE5;&#x5177; (fig) &#x56E2;&#x961F;&#x6210;&#x5458;. &#x8BD5;&#x4E86;&#x4E00;&#x4E0B;, &#x64CD;&#x4F5C;&#x548C;&#x4F20;&#x7EDF; git &#x7684;&#x5DEE;&#x8DDD;&#x6709;&#x70B9;&#x5927;, &#x4E0D;&#x592A;&#x4E60;&#x60EF;.</li><li><a href="https://github.com/uptech/git-ps-rs" aria-label="uptech/git-ps-rs: Official git-ps Rust implementation - the future of git-ps" class="hint--top hint--rounded hint--no-animate hint--no-arrow">git-patchstack</a>: &#x6CA1;&#x7528;&#x8FC7;.</li><li><a href="https://graphite.dev/" aria-label="Fast, simple code review | Graphite" class="hint--top hint--rounded hint--no-animate hint--no-arrow">graphite</a>: &#x4E00;&#x4E2A;&#x4E13;&#x6CE8;&#x505A; stacked PR &#x5DE5;&#x5177;&#x7684; startup. &#x6CA1;&#x7528;&#x8FC7;&#x4F46;&#x662F;&#x770B;&#x4E0A;&#x53BB;&#x505A;&#x7684;&#x5F88;&#x8BA4;&#x771F;.</li><li><a href="https://github.com/aviator-co/av" aria-label="aviator-co/av: A command line tool to manage stacked PRs with Aviator" class="hint--top hint--rounded hint--no-animate hint--no-arrow">aviator</a>: &#x4E5F;&#x662F;&#x4E00;&#x4E2A;&#x4E13;&#x6CE8;&#x505A; stacked PR &#x5DE5;&#x5177;&#x7684; startup.</li></ul><p>&#x6700;&#x540E;, &#x5173;&#x4E8E; stacked diffs &#x7684;&#x8BDD;&#x9898;, &#x8FD9;&#x91CC;&#x63D0;&#x4F9B;&#x4E00;&#x4E9B;&#x5176;&#x4ED6;&#x53C2;&#x8003;:</p><ul><li><a href="https://gregoryszorc.com/blog/2020/01/07/problems-with-pull-requests-and-how-to-fix-them/" aria-label="Gregory Szorc&apos;s Digital Home  | Problems with Pull Requests and How to Fix Them" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Problems with pull requests and how to fix them</a></li><li><a href="https://jg.gg/2018/09/29/stacked-diffs-versus-pull-requests/" aria-label="Stacked Diffs Versus Pull Requests | Jackson Gabbard&apos;s Blog" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Stacked Diffs Versus Pull Requests</a></li></ul><p>&#x4E0A;&#x9762;&#x4E24;&#x7BC7;&#x6587;&#x7AE0;&#x5199;&#x7684;&#x6700;&#x8BE6;&#x7EC6;, &#x672C;&#x6587;&#x4E5F;&#x53C2;&#x8003;&#x4E86;&#x5176;&#x4E2D;&#x7684;&#x4E00;&#x4E9B;&#x89C2;&#x70B9;. &#x9664;&#x6B64;&#x4E4B;&#x5916;, &#x8FD8;&#x6709;:</p><ul><li><a href="https://pytorch-dev-podcast.simplecast.com/episodes/stacked-diffs-and-ghstack">Stacked diffs and ghstack</a>: ghstack &#x4F5C;&#x8005;&#x7684; podcast.</li><li>git-patchstack &#x5F00;&#x53D1;&#x8005;&#x7684;&#x6587;&#x7AE0;: <a href="https://engineering.uptechstudio.com/blog/how-we-should-be-using-git/" aria-label="How we should be using Git | Uptech Studio - Engineering" class="hint--top hint--rounded hint--no-animate hint--no-arrow">How we should be using Git</a>, &#x548C;&#x5B83;&#x4EEC;&#x7684;<a href="https://book.git-ps.sh/conceptual-model" aria-label="Conceptual Model - Git Patch Stack Book" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x6587;&#x6863;</a></li><li>graphite &#x5F00;&#x53D1;&#x8005;&#x7684;&#x6587;&#x7AE0;: <a href="https://graphite.dev/stacking" aria-label="Stacking | Graphite" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Stacking</a></li><li>aviator &#x5F00;&#x53D1;&#x8005;&#x7684;&#x6587;&#x7AE0;: <a href="https://www.aviator.co/blog/rethinking-code-reviews-with-stacked-prs/" aria-label="Rethinking code reviews with stacked PRs - Aviator Blog" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Rethinking code reviews with stacked PRs</a></li><li><a href="https://benjamincongdon.me/blog/2022/07/17/In-Praise-of-Stacked-PRs/" aria-label="In Praise of Stacked PRs | Ben Congdon" class="hint--top hint--rounded hint--no-animate hint--no-arrow">In-Praise-of-Stacked-PRs</a></li><li>Taichi &#x5F00;&#x53D1;&#x56E2;&#x961F;&#x4E5F;&#x4F7F;&#x7528; ghstack, &#x5728;<a href="https://www.bilibili.com/video/BV1124y1g7yv/" aria-label="&#x597D;&#x7528;&#x4E0D;&#x706B;&#x7684; ghstack&#xFF0C;&#x4E3A;&#x4EC0;&#x4E48;&#x662F; GitHub Pull Request &#x5229;&#x5668;&#xFF1F;&#xFF08;&#x5185;&#x542B;&#x64CD;&#x4F5C;&#x8BE6;&#x89E3;&#x6559;&#x5B66;&#xFF09;_&#x54D4;&#x54E9;&#x54D4;&#x54E9;_bilibili" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> B &#x7AD9;&#x6709;&#x4E2A;&#x4E2D;&#x6587;&#x6559;&#x7A0B;</a></li></ul><p>&#x6CE8;: &#x672C;&#x6587;&#x5927;&#x90E8;&#x5206;&#x5199;&#x4E8E;&#x79BB;&#x5F00; Meta &#x7684; Stacked Diff &#x540E;, &#x5728; Cruise &#x5DE5;&#x4F5C;&#x671F;&#x95F4;. &#x5F53;&#x65F6;&#x82E6;&#x4E8E; Cruise &#x7528; github &#x6CA1;&#x6709; Stacked Diff. &#x7136;&#x800C;&#x6587;&#x7AE0;&#x8FD8;&#x6CA1;&#x5199;&#x5B8C;&#x6211;&#x5C31;&#x53BB;&#x4E86; Google, &#x53C8;&#x6709;&#x4E86; Stacked Diff. &#x5982;&#x4ECA;&#x518D;&#x6B21;&#x56DE;&#x5230; git &#x7684;&#x4E16;&#x754C;, &#x6240;&#x4EE5;&#x53C8;&#x5F00;&#x59CB;&#x7814;&#x7A76;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Meta &amp;#x4E0E; Google &amp;#x5185;&amp;#x90E8;&amp;#x7684;&amp;#x4EE3;&amp;#x7801;&amp;#x7BA1;&amp;#x7406;&amp;#x5DE5;&amp;#x5177;&amp;#x90FD;&amp;#x652F;&amp;#x6301;&amp;#x4E00;&amp;#x79CD;&amp;#x88AB;&amp;#x79F0;&amp;#x4F5C; &amp;quot;stacked diffs / stacked PRs&amp;quot;
 &amp;#x7684; workflow. &amp;#x7136;&amp;#x800C;, &amp;#x57FA;&amp;#x4E8E; git &amp;#x7684;&amp;#x4E3B;&amp;#x6D41;&amp;#x5E73;&amp;#x53F0; (github, gitlab) &amp;#x90FD;&amp;#x4E0D;&amp;#x652F;&amp;#x6301;&amp;#x8FD9;&amp;#x79CD; workflow.
 &amp;#x8BB8;&amp;#x591A;&amp;#x79BB;&amp;#x5F00; Meta &amp;#x540E;&amp;#x4E0D;&amp;#x5F97;&amp;#x4E0D;&amp;#x4F7F;&amp;#x7528; github &amp;#x7684;&amp;#x670B;&amp;#x53CB;&amp;#x8868;&amp;#x793A;, stacked diffs &amp;#x5BF9;&amp;#x4E8E;&amp;#x5DE5;&amp;#x7A0B;&amp;#x5E08;&amp;#x662F;&amp;#x4E00;&amp;#x4E2A; &amp;quot;ultimate
productivity tool&amp;quot;, &amp;#x6211;&amp;#x4E5F;&amp;#x6DF1;&amp;#x6709;&amp;#x540C;&amp;#x611F;. &amp;#x8FD9;&amp;#x7BC7;&amp;#x6587;&amp;#x7AE0;&amp;#x4ECB;&amp;#x7ECD;&amp;#x4E00;&amp;#x4E0B;&amp;#x4EC0;&amp;#x4E48;&amp;#x662F; stacked diffs workflow,
 &amp;#x4EE5;&amp;#x53CA;&amp;#x4E3A;&amp;#x4EC0;&amp;#x4E48;&amp;#x5B83;&amp;#x80FD;&amp;#x591F;&amp;#x6781;&amp;#x5927;&amp;#x7684;&amp;#x63D0;&amp;#x5347;&amp;#x56E2;&amp;#x961F;&amp;#x5F00;&amp;#x53D1;&amp;#x6548;&amp;#x7387;.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Programming" scheme="https://ppwwyyxx.com/blog/tags/Programming/"/>
    
    <category term="Open Source" scheme="https://ppwwyyxx.com/blog/tags/Open-Source/"/>
    
  </entry>
  
  <entry>
    <title>Registration Does Not Scale Well</title>
    <link href="https://ppwwyyxx.com/blog/2023/Registration-Does-Not-Scale-Well/"/>
    <id>https://ppwwyyxx.com/blog/2023/Registration-Does-Not-Scale-Well/</id>
    <published>2023-04-23T07:00:00.000Z</published>
    <updated>2023-04-23T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>People have many different opinions about config systems. Having worked withvarious styles of configs, I also want to write about what a greatconfig subsystem in a large-scale (in terms of system complexity, number ofusers, etc.) system should look like.</p><p>The design space is complex, so in this article I&apos;ll start with a smaller topic:<strong>registration</strong> in config systems. I&apos;ll show why this common pattern, though works fine forsmall-scale projects, does not scale well in the long term. I&apos;ll also discussan alternative.</p><span id="more"></span><h2 id="Registration-for-configs">Registration for configs<a class="markdown-anchor" href="#Registration-for-configs">&#xB6;</a></h2><p>Configs are often constrained to include only primitive types (numbers andstrings), and there are a lot of good reasons to keep this property.But users often want to refer to more complex objects.</p><p>A global registry in a config system is typically a <code>Mapping[str, Any]</code>.Its purpose is to allow users to refer to complex objects through simple strings in a config,overcoming the constraint of primitive types.</p><p>Objects can be added to a registry like this:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># Maps string to a class/type.</span><br><span class="hljs-meta">@ModelRegistry.register</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyModel</span>():<br>  ...<br><br><span class="hljs-comment"># Maps string to an object.</span><br>DatasetRegistry.register(<span class="hljs-string">&quot;my_dataset&quot;</span>, MyDataset(...))<br></code></pre></td></tr></table></figure><p>This allows users to select which model / data to use by setting<code>cfg.model.name = &quot;MyModel&quot;</code> or <code>cfg.dataset.train = &quot;my_dataset&quot;</code>.</p><p>Registration has a clear benefit, but at a larger scale, some of its downside are serious.</p><h2 id="Pay-for-what-you-use">Pay for what you use<a class="markdown-anchor" href="#Pay-for-what-you-use">&#xB6;</a></h2><p><strong>Users should only pay (compute cost and mental cost) for what they use</strong> is a general design philosophy I found pretty important in almost all aspects of software design.</p><p>The registration pattern breaks this philosophy by running unnecessaryregistration code: users will only provide one (or very few) string in theirconfig, but they have to pay the overhead of registering many candidate stringsthat users might need.</p><p>To make matter worse, the overhead has to happen very early in a program, typically at import time. Import speed is crucial for developer ergonomics: unlike other code that may run async with development, <code>import</code>blocks developers, slowing down their day-to-day work.</p><p>The registration overhead includes:</p><ol><li>Cost to import extra Python modules that contain registration code, and most importantly, all their dependencies.</li><li>For registries that map strings to non-trivial objects (not just types/functions), the cost to create these objects.<ul><li>A better practice is to avoid such registries: don&apos;t store objectsin the registry, but store functions that create these objects if possible. Howeverthis does not always solve the problem: the function may have to be aclosure that close on non-trivial objects, in which case the objects stillhave to be created at registration time.</li></ul></li></ol><p>These costs are negligible for small-scale projects, but they can become quitebad when there are hundreds or more objects to register.If a design permits bad patterns, <strong>bad patterns are guaranteed to appear at larger scale</strong>:There will be users doingnon-trivial registration (e.g. register objects in a for loop) that&apos;s slow oreven has unintended side effects. I had to work with many projects that take &gt;10s to import and the most common reason of slow import is registration.</p><p>The import overhead is also greatly magnified by Python&apos;s <code>multiprocessing</code> module: all subprocesses will have to spend the time and RAM to rerun the imports.</p><h2 id="Global-states">Global states<a class="markdown-anchor" href="#Global-states">&#xB6;</a></h2><p>Registries are typically defined as a global dictionary, so they share manyinherent problems of using global states.</p><h3 id="Name-conflicts">Name conflicts<a class="markdown-anchor" href="#Name-conflicts">&#xB6;</a></h3><p>It&apos;s not uncommon that different users register different objects under thesame name -- at a large scale that&apos;s guaranteed to happen.</p><p>Such conflicts can live in two users&apos; own code for a long time, unnoticed,until one day someone needs to depend on both.The only viable solution is usually to rename one, hence break all its users.</p><h3 id="Overwrites">Overwrites<a class="markdown-anchor" href="#Overwrites">&#xB6;</a></h3><p>To complicate the issue even more, people sometimes decide to resolve nameconflicts by overwriting what&apos;s already registered. For example, an &quot;overwrite&quot;option is provided in<a href="https://github.com/facebookresearch/iopath/blob/0f11f7a05dd30ac77e3617b683e04d952c00e9d8/iopath/common/file_io.py#L1426-L1428" aria-label="file_io.py &#xB7; facebookresearch/iopath" class="hint--top hint--rounded hint--no-animate hint--no-arrow">the registry of iopath</a>,<a href="https://github.com/facebookresearch/mobile-vision/blob/17bc45f5d982615c714161d386c9a2682504bacd/mobile_cv/common/misc/registry.py#L61" aria-label="registry.py &#xB7; facebookresearch/mobile-vision" class="hint--top hint--rounded hint--no-animate hint--no-arrow">mobile_cv</a>,and <a href="https://github.com/google/paxml/blob/033eb2421a6fc3e24f76bb19dd260c6776c5933b/paxml/experiment_registry.py#L55" aria-label="experiment_registry.py &#xB7; google/paxml" class="hint--top hint--rounded hint--no-animate hint--no-arrow">paxml</a>.Using this option may introduce hard-to-debug problems, because now aninnocent <code>import</code> statement may silently change the behavior of user code.</p><p>Despite of this, note that overwriting is actually necessary when working in notebooks, where it&apos;s common to &quot;reload&quot; code (therefore re-register objects) on the fly. <a href="https://github.com/google/paxml/commit/57b8fe64ba3451f1dc474b03b9ad77cbd53e7c3e" aria-label="Always allow experiment overwrite when reloading modules. &#xB7; google/paxml@57b8fe6" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Here</a> is some code I use to always enable overwrite during reload.</p><h3 id="Pickle-multiprocessing">Pickle &amp; multiprocessing<a class="markdown-anchor" href="#Pickle-multiprocessing">&#xB6;</a></h3><p>When running a function using a <code>multiprocessing.Process</code> created with a safe <code>start_method</code> like &quot;spawn&quot;, the child process receives a pickled closure from its parent, so it knows what to run. However, this pickle does not include any global states. This implies that if a function access global states, it may behave differently depending on if it runs in the subprocess or the parent process. Python&apos;s documentation has <a href="https://docs.python.org/3/library/multiprocessing.html#the-spawn-and-forkserver-start-methods" aria-label="multiprocessing &#x2014; Process-based parallelism &#x2014; Python 3.11.3 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">a clear warning</a> about this:</p><blockquote><p>if code run in a child process tries to access a global variable, then the value it sees (if any) may not be the same as the value in the parent process at the time that&#xA0;<a href="https://docs.python.org/3/library/multiprocessing.html#multiprocessing.Process.start" aria-label="multiprocessing &#x2014; Process-based parallelism &#x2014; Python 3.11.3 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>Process.start</code></a>&#xA0;was called.</p></blockquote><p>The <a href="https://github.com/ray-project/ray" aria-label="ray-project/ray: Ray is a unified framework for scaling AI and Python applications. Ray consists of a core distributed runtime and a toolkit of libraries (Ray AIR) for accelerating ML workloads." class="hint--top hint--rounded hint--no-animate hint--no-arrow">ray</a> framework can run a pickledPython function remotely, and therefore it <a href="https://github.com/ray-project/ray/issues/15946" aria-label="[core] modifications to global variable has no effect &#xB7; Issue #15946 &#xB7; ray-project/ray" class="hint--top hint--rounded hint--no-animate hint--no-arrow">has similar (and even more counter-intuitive) issues</a>.</p><p>As a result, objects dynamically registered in parent process may not appear in child processes. I&apos;ve seen a number of bugs about this.</p><h3 id="Obscure-Provenance">Obscure Provenance<a class="markdown-anchor" href="#Obscure-Provenance">&#xB6;</a></h3><p>Since the registration is globally accessible, it&apos;s not easy to find where inthe code an object is registered (or modified, if overwrite is allowed) just byreading the code. When a user sees <code>cfg.dataset.name = &apos;dataset_X&apos;</code> and iscurious what is &quot;dataset_X&quot;, a global string search is almost the only way tofind it out without running the code. And the search does not always work: ifthe name is programmatically generated, then the string cannot be founddirectly in source code, e.g.:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">for</span> dataset <span class="hljs-keyword">in</span> [<span class="hljs-string">&quot;ds1&quot;</span>, <span class="hljs-string">&quot;ds2&quot;</span>]:<br>  <span class="hljs-keyword">for</span> r <span class="hljs-keyword">in</span> [<span class="hljs-number">0.1</span>, <span class="hljs-number">0.5</span>]:<br>    DatasetRegistry.register(<span class="hljs-string">f&apos;my_<span class="hljs-subst">{dataset}</span>_ratio<span class="hljs-subst">{r}</span>&apos;</span>, MyDataset(dataset, r))<br></code></pre></td></tr></table></figure><p>In this case, users will have to be more creative about what strings to search.</p><hr><p>In C++, registries cause even more trouble becauseconstruction and destruction of global objects are verytricky. In <a href="/blog/2023/Safe-Static-Initialization-No-Destruction/" aria-label="Safe Static Initialization, No Destruction" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Safe Static Initialization, No Destruction</a>I talked about a few PyTorch&apos;s C++ bugs related to this. Luckily,in Python, there are better alternatives.</p><h2 id="Alternative-module-name-variable-name">Alternative: module name + variable name<a class="markdown-anchor" href="#Alternative-module-name-variable-name">&#xB6;</a></h2><p>If the only goal of registration is to provide a name &#x2192; object mapping, then asimple alternative in Python is to use <code>obj.__module__ + &apos;.&apos; + {variable name}</code> as the name,which may look like <code>some_library.some_module.MyClass</code>.&quot;Variable name&quot; can be <code>obj.__qualname__</code> for classes &amp; functions.</p><p>Given this string, one can then call a simple function such as the builtin<code>pydoc.locate</code> to obtain the object it refers to: modules will be importedon-demand by <code>importlib</code>.</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Use registration:</th><th>Use full qualname:</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># my_lib/my_module.py:</span><br><span class="hljs-meta">@ModelRegistry.register()</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyModel</span>(...):<br>  ...<br><br><span class="hljs-comment"># main.py --name=MyModel:</span><br><span class="hljs-keyword">from</span> my_lib <span class="hljs-keyword">import</span> ModelRegistry<br><span class="hljs-keyword">import</span> my_lib.my_module  <span class="hljs-comment"># import to register</span><br>model = ModelRegistry.get(args.name)<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># my_lib/my_module.py:</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyModel</span>(...):<br>  ...<br><br><span class="hljs-comment"># main.py --name=my_lib.my_module.MyModel</span><br>model = pydoc.locate(args.name)<br></code></pre></td></tr></table></figure></td></tr></table></div><p>This pattern has some obvious advantages over registration:</p><ol><li>No need to import any unused modules. Modules are imported on-demand.</li><li>No global states.</li></ol><p>There are some common concerns of this pattern, but they are not hard to address.</p><ol><li><p>It&apos;s slightly harder to dynamically create candidates: there is no &quot;registry&quot; to add objects to,and the only equivalence is to edit the <code>globals()</code> dictionary directly.</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">for</span> dataset <span class="hljs-keyword">in</span> [<span class="hljs-string">&quot;ds1&quot;</span>, <span class="hljs-string">&quot;ds2&quot;</span>]:<br>  <span class="hljs-keyword">for</span> r <span class="hljs-keyword">in</span> [<span class="hljs-number">0.1</span>, <span class="hljs-number">0.5</span>]:<br>    <span class="hljs-built_in">globals</span>()[<span class="hljs-string">f&quot;my_<span class="hljs-subst">{dataset}</span>_<span class="hljs-subst">{r}</span>&quot;</span>] = create_dataset(dataset, r)<br></code></pre></td></tr></table></figure><p>I don&apos;t consider this an issue, because it&apos;s actually discouraging bad practice:in a proper config system (e.g. one that&apos;s based on recursive instantiation)there should be no need to dynamically generate candidates like above.I hope to get to this in a future article.</p></li><li><p>The names in config have to match the names of classes/functions in code.</p><p>This has the benefit of clarity on one hand. But on the other hand, code owners have more responsibilityto maintain backward compatibility, especially after renaming their classes and files.The standard good practice suffices to address this: distinguish private vs. public symbols;keep an alias from the deprecated name to the new name; etc.</p><p>Overall, I think the benefit of matching config and code outweighs the concern that config may be broken by code.</p></li><li><p>The names are too long.</p><p>This is a real problem. Here are some ways to address it:</p><ul><li><p>A $PATH-like mechanism can be used to specify which modules to search for names.The search path can include common prefixes like &quot;my_project&quot; so that users only have to provide &quot;submodule.MyModel&quot;.</p></li><li><p>There can be a registry-like <code>Mapping[str, str]</code> that maps from &quot;MyModel&quot; to&quot;my_lib.my_module.MyModel&quot; so that users don&apos;t have to write long strings.This mapping doesn&apos;t have to be global and doesn&apos;t introduce import overhead.This can help with problem (2) as well.</p></li><li><p>This is just a UI-level issue.Having <strong>a better config frontend</strong>, e.g. using Python code as the config language,can make this issue disappear! Let me save this for a future article.</p></li></ul></li></ol>]]></content>
    
    
    <summary type="html">&lt;p&gt;People have many different opinions about config systems. Having worked with
various styles of configs, I also want to write about what a great
config subsystem in a large-scale (in terms of system complexity, number of
users, etc.) system should look like.&lt;/p&gt;
&lt;p&gt;The design space is complex, so in this article I&amp;apos;ll start with a smaller topic:
&lt;strong&gt;registration&lt;/strong&gt; in config systems. I&amp;apos;ll show why this common pattern, though works fine for
small-scale projects, does not scale well in the long term. I&amp;apos;ll also discuss
an alternative.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Python" scheme="https://ppwwyyxx.com/blog/tags/Python/"/>
    
    <category term="Design" scheme="https://ppwwyyxx.com/blog/tags/Design/"/>
    
  </entry>
  
  <entry>
    <title>Safe Static Initialization, No Destruction</title>
    <link href="https://ppwwyyxx.com/blog/2023/Safe-Static-Initialization-No-Destruction/"/>
    <id>https://ppwwyyxx.com/blog/2023/Safe-Static-Initialization-No-Destruction/</id>
    <published>2023-04-06T07:00:00.000Z</published>
    <updated>2023-04-06T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>Since I joined Google Brain, I brought PyTorch to Google&apos;s internal infra andowned its maintenance. Being a &quot;tech island&quot;, it&apos;s well known that almosteverything in Google works differently from the outside world, and thatcreates many challenges when building a massive library like PyTorch.</p><p>Among those challenges, there are a few tricky bugs related to <a href="https://en.cppreference.com/w/cpp/language/siof" aria-label="Static Initialization Order Fiasco - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">staticinitialization order fiasco</a>(SIOF) and their destructions. This time I was forced to learn a lot more detailsthan I&apos;d like to know about these topics, so it&apos;s good to write them down before I forget.</p><span id="more"></span><h2 id="Terminology">Terminology<a class="markdown-anchor" href="#Terminology">&#xB6;</a></h2><p>&quot;<strong>Static initialization</strong>&quot; is an ambiguous term because &quot;static&quot; is very overloaded in C++.In our context, it is supposed to mean &quot;initialization of objects that have <a href="https://en.cppreference.com/w/c/language/static_storage_duration" aria-label="Static storage duration - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">static storage duration</a>&quot;,i.e. objects that live through the lifetime of a program.The word &quot;static&quot; actually talks about the <strong>object lifetime</strong>, not about initialization.</p><p>Meanwhile, initialization of such objects can have two steps:</p><ul><li><strong>Zero/Constant initialization</strong> (it&apos;s also confusingly<a href="https://en.cppreference.com/w/cpp/language/initialization#Non-local_variables" aria-label="Initialization - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">called &quot;static initialization&quot;</a> &#x1F631;):allocate memory and fill in<a href="https://en.cppreference.com/w/cpp/language/zero_initialization" aria-label="Zero-initialization - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">zero</a> or <a href="https://en.cppreference.com/w/cpp/language/constant_initialization" aria-label="Constant initialization - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">constant</a>bytes. For objects with static storage duration, this step is done during compile time.</li><li><strong>Dynamic initialization</strong>: call constructor (if non-trivial, and if not constexpr) of the object.</li></ul><p>Objects with static storage duration can be categorized into following two types, based on when their &quot;dynamic initialization&quot; happen:</p><ul><li><strong>Global</strong>: these objects are dynamic-initialized at program launch (before <code>main()</code>).  <figure class="highlight cpp"><table><tr><td class="code"><pre><code class="hljs cpp">Object a1;<br><span class="hljs-type">static</span> Object a2;<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span> {<br>    <span class="hljs-type">static</span> Object a3;<br>};<br></code></pre></td></tr></table></figure></li><li><strong>Function-local static</strong>:  <figure class="highlight cpp"><table><tr><td class="code"><pre><code class="hljs cpp"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> </span>{<br>    <span class="hljs-type">static</span> Object a4;<br>}<br></code></pre></td></tr></table></figure>They are dynamic-initialized the first time it&apos;s reached. Since C++11, they are <a href="https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables" aria-label="Storage class specifiers - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">guaranteed to initialize once and only once</a> even with multiple threads.</li></ul><h2 id="Static-Initialization-Order-Fiasco">Static Initialization Order Fiasco<a class="markdown-anchor" href="#Static-Initialization-Order-Fiasco">&#xB6;</a></h2><p><a href="https://en.cppreference.com/w/cpp/language/siof" aria-label="Static Initialization Order Fiasco - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">SIOF</a> typically refers to the problem that the dynamic initialization order of objects from different translation units is undefined, e.g.:</p><figure class="highlight cpp"><table><tr><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// a.cpp:</span><br>Object a;<br><span class="hljs-comment">// b.cpp:</span><br>AnotherObject b;<br></code></pre></td></tr></table></figure><p>If <code>a</code> and <code>b</code> have non-trivial constructors, and the constructor of <code>b</code> somehow needs to access <code>a</code>, the program may crash or behave unexpectedly because <code>a</code> may be initialized after <code>b</code>.</p><p>PyTorch heavily uses registrations, which all have static storage duration. A few SIOF bugs were found when Itried to build PyTorch in Google. As an example, when an ATen operator has many overloads, <a href="https://github.com/pytorch/pytorch/issues/90608" aria-label="Operator overload priority should not rely on static initialization order &#xB7; Issue #90608 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">initialization order affects which overload is called</a>, because an overload that&apos;s initialized earlier will be preferred over those initialized later.</p><p>Standard ways to avoid SIOF problems are:</p><ul><li><p><strong>Avoid dynamic initialization</strong>: change object type to something that can be zero/const-initialized. <a href="https://abseil.io/tips/140" aria-label="abseil / Tip of the Week #140: Constants: Safe Idioms" class="hint--top hint--rounded hint--no-animate hint--no-arrow">totw/140</a> shows a few examples on how to replace <code>std::string</code> with non-dynamic counterparts.</p></li><li><p><strong>Use well-defined initialization order</strong>: <a href="https://en.cppreference.com/w/cpp/language/siof" aria-label="Static Initialization Order Fiasco - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">there is a guarantee</a> that objects within the same translation unit are dynamically initialized according to the well-defined program order. So we can sometimes just move code into the same translation unit. In <a href="https://github.com/pytorch/pytorch/pull/90149" aria-label="Fix a static initialization order fiasco in c10d by ppwwyyxx &#xB7; Pull Request #90149 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">another PyTorch bug</a> where one global depends on another,I simply merged two files so that their constructors are properly sequenced.</p></li><li><p><strong>Construct on first use</strong>: it&apos;s often not practical to merge files. A better solution is the &quot;construct on first use&quot; idiom:</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>&#x274C; Don&apos;t use globals</th><th>&#x2705; Use function-local static:</th></tr> <tr><td><figure class="highlight cpp"><table><tr><td class="code"><pre><code class="hljs cpp">Object a;<br></code></pre></td></tr></table></figure></td><td><figure class="highlight cpp"><table><tr><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">Object&amp; <span class="hljs-title">get_a</span><span class="hljs-params">()</span> </span>{<br>    <span class="hljs-type">static</span> Object* a = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Object</span>();<br>    <span class="hljs-keyword">return</span> *a;<br>}<br><span class="hljs-comment">// or:</span><br><span class="hljs-function">Object&amp; <span class="hljs-title">get_a</span><span class="hljs-params">()</span> </span>{<br>    <span class="hljs-type">static</span> Object a;<br>    <span class="hljs-keyword">return</span> a;<br>}<br></code></pre></td></tr></table></figure></td></tr></table></div><p>By doing this, anyone that needs to access <code>a</code> will have to call <code>get_a()</code>. Because function-local static is guaranteed to initialize on first use, we can rest assured that <code>a</code> will not be used before initialization.</p><p>The &quot;construct on first use&quot; idiom may look differently, because sometimes we don&apos;t need to use <code>a</code> directly but do need to observe the side effects of its constructor. In such cases we just manually call <code>get_a</code> to make sure <code>a</code> is constructed. I used this to fix <a href="https://github.com/pytorch/pytorch/pull/90133" aria-label="Fix static initialization issue for static build by ppwwyyxx &#xB7; Pull Request #90133 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">another PyTorch bug</a> .</p></li></ul><h2 id="Safe-Destructions">Safe Destructions<a class="markdown-anchor" href="#Safe-Destructions">&#xB6;</a></h2><p>There are more ways things can go wrong in the destruction of objects with static storage duration.</p><p>In general, we have to carefully avoid use-after-free, i.e. access a global/function-local variable after it&apos;s destructed. This is typically protected by <a href="https://en.cppreference.com/w/cpp/utility/program/exit" aria-label="std::exit - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">this rule</a>:</p><blockquote><p>Non-local objects with static storage duration are destroyed in the reverse order of the completion of their constructor.</p></blockquote><p>Given this rule, we can deduce that:<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -4.977ex;" xmlns="http://www.w3.org/2000/svg" width="50.885ex" height="11.086ex" role="img" focusable="false" viewbox="0 -2700 22491 4900"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtable"><g data-mml-node="mtr" transform="translate(0,1950)"><g data-mml-node="mtd" transform="translate(1000,0)"/><g data-mml-node="mtd" transform="translate(1000,0)"><g data-mml-node="mrow"><g data-mml-node="mtext"><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z"/><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z" transform="translate(278,0)"/><path data-c="A0" d="" transform="translate(584,0)"/></g><g data-mml-node="mtext" transform="translate(834,0)"><path data-c="1D68A" d="M126 306Q105 306 90 321T74 359Q74 439 211 439Q268 439 276 438Q343 426 383 390T430 306Q431 301 431 190V81Q446 79 465 78T492 76T509 72T521 60T524 38Q524 11 506 3Q502 1 466 1Q426 1 406 5T379 14T355 36L345 30Q284 -6 205 -6Q135 -6 92 39T48 141Q48 182 79 212T158 256T252 278T342 285H347V290Q347 315 325 335T267 362Q258 363 224 363Q189 363 185 362H179L178 358Q178 353 178 352T176 345T174 337T170 330T165 322T158 316T150 311T139 308T126 306ZM132 140Q132 115 157 93T224 70Q269 70 302 87T344 133Q346 139 347 175V211H339Q256 209 194 190T132 140Z"/></g><g data-mml-node="mtext" transform="translate(1359,0)"><path data-c="2019" d="M78 634Q78 659 95 676T138 694Q166 694 189 668T212 579Q212 525 190 476T146 403T118 379Q114 379 105 388T95 401Q95 404 107 417T133 448T161 500T176 572Q176 584 175 584T170 581T157 576T139 573Q114 573 96 590T78 634Z"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(278,0)"/><path data-c="20" d="" transform="translate(672,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(922,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(1366,0)"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(1866,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(2422,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(2816,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(3205,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(3597,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(4153,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(4597,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(4986,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(5486,0)"/><path data-c="20" d="" transform="translate(5878,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(6128,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(6522,0)"/><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z" transform="translate(7022,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(7328,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(7772,0)"/><path data-c="79" d="M69 -66Q91 -66 104 -80T118 -116Q118 -134 109 -145T91 -160Q84 -163 97 -166Q104 -168 111 -168Q131 -168 148 -159T175 -138T197 -106T213 -75T225 -43L242 0L170 183Q150 233 125 297Q101 358 96 368T80 381Q79 382 78 382Q66 385 34 385H19V431H26L46 430Q65 430 88 429T122 428Q129 428 142 428T171 429T200 430T224 430L233 431H241V385H232Q183 385 185 366L286 112Q286 113 332 227L376 341V350Q376 365 366 373T348 383T334 385H331V431H337H344Q351 431 361 431T382 430T405 429T422 429Q477 429 503 431H508V385H497Q441 380 422 345Q420 343 378 235T289 9T227 -131Q180 -204 113 -204Q69 -204 44 -177T19 -116Q19 -89 35 -78T69 -66Z" transform="translate(8050,0)"/><path data-c="20" d="" transform="translate(8578,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(8828,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(9384,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(9778,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(10222,0)"/><path data-c="A0" d="" transform="translate(10616,0)"/></g><g data-mml-node="mtext" transform="translate(12225,0)"><path data-c="1D68B" d="M4 573Q4 596 15 603T52 611H90H124Q146 611 155 608T171 591Q173 586 173 491V396L182 402Q217 424 256 431Q280 437 309 437Q376 437 434 379T492 217Q492 162 473 118T422 47T358 8T293 -6Q229 -6 174 38Q171 13 163 7T135 1H131H122Q99 1 90 23L89 279V535H58L27 536Q4 543 4 573ZM409 215Q409 269 377 315T283 361Q255 361 224 344T177 297L173 290V167Q189 124 213 97T278 70Q330 70 369 111T409 215Z"/></g></g></g></g><g data-mml-node="mtr" transform="translate(0,650)"><g data-mml-node="mtd"><g data-mml-node="mo"><path data-c="21D2" d="M580 514Q580 525 596 525Q601 525 604 525T609 525T613 524T615 523T617 520T619 517T622 512Q659 438 720 381T831 300T927 263Q944 258 944 250T935 239T898 228T840 204Q696 134 622 -12Q618 -21 615 -22T600 -24Q580 -24 580 -17Q580 -13 585 0Q620 69 671 123L681 133H70Q56 140 56 153Q56 168 72 173H725L735 181Q774 211 852 250Q851 251 834 259T789 283T735 319L725 327H72Q56 332 56 347Q56 360 70 367H681L671 377Q638 412 609 458T580 514Z"/></g></g><g data-mml-node="mtd" transform="translate(1000,0)"><g data-mml-node="mrow"><g data-mml-node="mtext"><path data-c="1D68B" d="M4 573Q4 596 15 603T52 611H90H124Q146 611 155 608T171 591Q173 586 173 491V396L182 402Q217 424 256 431Q280 437 309 437Q376 437 434 379T492 217Q492 162 473 118T422 47T358 8T293 -6Q229 -6 174 38Q171 13 163 7T135 1H131H122Q99 1 90 23L89 279V535H58L27 536Q4 543 4 573ZM409 215Q409 269 377 315T283 361Q255 361 224 344T177 297L173 290V167Q189 124 213 97T278 70Q330 70 369 111T409 215Z"/></g><g data-mml-node="mtext" transform="translate(525,0)"><path data-c="2019" d="M78 634Q78 659 95 676T138 694Q166 694 189 668T212 579Q212 525 190 476T146 403T118 379Q114 379 105 388T95 401Q95 404 107 417T133 448T161 500T176 572Q176 584 175 584T170 581T157 576T139 573Q114 573 96 590T78 634Z"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(278,0)"/><path data-c="20" d="" transform="translate(672,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(922,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(1366,0)"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(1866,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(2422,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(2816,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(3205,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(3597,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(4153,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(4597,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(4986,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(5486,0)"/><path data-c="20" d="" transform="translate(5878,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(6128,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(6572,0)"/><path data-c="6D" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q351 442 364 440T387 434T406 426T421 417T432 406T441 395T448 384T452 374T455 366L457 361L460 365Q463 369 466 373T475 384T488 397T503 410T523 422T546 432T572 439T603 442Q729 442 740 329Q741 322 741 190V104Q741 66 743 59T754 49Q775 46 803 46H819V0H811L788 1Q764 2 737 2T699 3Q596 3 587 0H579V46H595Q656 46 656 62Q657 64 657 200Q656 335 655 343Q649 371 635 385T611 402T585 404Q540 404 506 370Q479 343 472 315T464 232V168V108Q464 78 465 68T468 55T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(7072,0)"/><path data-c="70" d="M36 -148H50Q89 -148 97 -134V-126Q97 -119 97 -107T97 -77T98 -38T98 6T98 55T98 106Q98 140 98 177T98 243T98 296T97 335T97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 61 434T98 436Q115 437 135 438T165 441T176 442H179V416L180 390L188 397Q247 441 326 441Q407 441 464 377T522 216Q522 115 457 52T310 -11Q242 -11 190 33L182 40V-45V-101Q182 -128 184 -134T195 -145Q216 -148 244 -148H260V-194H252L228 -193Q205 -192 178 -192T140 -191Q37 -191 28 -194H20V-148H36ZM424 218Q424 292 390 347T305 402Q234 402 182 337V98Q222 26 294 26Q345 26 384 80T424 218Z" transform="translate(7905,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(8461,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(8739,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(9183,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(9572,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(10016,0)"/><path data-c="20" d="" transform="translate(10410,0)"/><path data-c="62" d="M307 -11Q234 -11 168 55L158 37Q156 34 153 28T147 17T143 10L138 1L118 0H98V298Q98 599 97 603Q94 622 83 628T38 637H20V660Q20 683 22 683L32 684Q42 685 61 686T98 688Q115 689 135 690T165 693T176 694H179V543Q179 391 180 391L183 394Q186 397 192 401T207 411T228 421T254 431T286 439T323 442Q401 442 461 379T522 216Q522 115 458 52T307 -11ZM182 98Q182 97 187 90T196 79T206 67T218 55T233 44T250 35T271 29T295 26Q330 26 363 46T412 113Q424 148 424 212Q424 287 412 323Q385 405 300 405Q270 405 239 390T188 347L182 339V98Z" transform="translate(10660,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(11216,0)"/><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z" transform="translate(11660,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(11966,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(12466,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(12858,0)"/><path data-c="A0" d="" transform="translate(13302,0)"/></g><g data-mml-node="mtext" transform="translate(14077,0)"><path data-c="1D68A" d="M126 306Q105 306 90 321T74 359Q74 439 211 439Q268 439 276 438Q343 426 383 390T430 306Q431 301 431 190V81Q446 79 465 78T492 76T509 72T521 60T524 38Q524 11 506 3Q502 1 466 1Q426 1 406 5T379 14T355 36L345 30Q284 -6 205 -6Q135 -6 92 39T48 141Q48 182 79 212T158 256T252 278T342 285H347V290Q347 315 325 335T267 362Q258 363 224 363Q189 363 185 362H179L178 358Q178 353 178 352T176 345T174 337T170 330T165 322T158 316T150 311T139 308T126 306ZM132 140Q132 115 157 93T224 70Q269 70 302 87T344 133Q346 139 347 175V211H339Q256 209 194 190T132 140Z"/></g><g data-mml-node="mtext" transform="translate(14602,0)"><path data-c="2019" d="M78 634Q78 659 95 676T138 694Q166 694 189 668T212 579Q212 525 190 476T146 403T118 379Q114 379 105 388T95 401Q95 404 107 417T133 448T161 500T176 572Q176 584 175 584T170 581T157 576T139 573Q114 573 96 590T78 634Z"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(278,0)"/></g></g></g></g><g data-mml-node="mtr" transform="translate(0,-650)"><g data-mml-node="mtd"><g data-mml-node="mo"><path data-c="21D2" d="M580 514Q580 525 596 525Q601 525 604 525T609 525T613 524T615 523T617 520T619 517T622 512Q659 438 720 381T831 300T927 263Q944 258 944 250T935 239T898 228T840 204Q696 134 622 -12Q618 -21 615 -22T600 -24Q580 -24 580 -17Q580 -13 585 0Q620 69 671 123L681 133H70Q56 140 56 153Q56 168 72 173H725L735 181Q774 211 852 250Q851 251 834 259T789 283T735 319L725 327H72Q56 332 56 347Q56 360 70 367H681L671 377Q638 412 609 458T580 514Z"/></g></g><g data-mml-node="mtd" transform="translate(1000,0)"><g data-mml-node="mrow"><g data-mml-node="mtext"><path data-c="1D68B" d="M4 573Q4 596 15 603T52 611H90H124Q146 611 155 608T171 591Q173 586 173 491V396L182 402Q217 424 256 431Q280 437 309 437Q376 437 434 379T492 217Q492 162 473 118T422 47T358 8T293 -6Q229 -6 174 38Q171 13 163 7T135 1H131H122Q99 1 90 23L89 279V535H58L27 536Q4 543 4 573ZM409 215Q409 269 377 315T283 361Q255 361 224 344T177 297L173 290V167Q189 124 213 97T278 70Q330 70 369 111T409 215Z"/></g><g data-mml-node="mtext" transform="translate(525,0)"><path data-c="A0" d=""/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(250,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(528,0)"/><path data-c="20" d="" transform="translate(922,0)"/><path data-c="64" d="M376 495Q376 511 376 535T377 568Q377 613 367 624T316 637H298V660Q298 683 300 683L310 684Q320 685 339 686T376 688Q393 689 413 690T443 693T454 694H457V390Q457 84 458 81Q461 61 472 55T517 46H535V0Q533 0 459 -5T380 -11H373V44L365 37Q307 -11 235 -11Q158 -11 96 50T34 215Q34 315 97 378T244 442Q319 442 376 393V495ZM373 342Q328 405 260 405Q211 405 173 369Q146 341 139 305T131 211Q131 155 138 120T173 59Q203 26 251 26Q322 26 373 103V342Z" transform="translate(1172,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(1728,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(2172,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(2566,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(2955,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(3347,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(3903,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(4347,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(4736,0)"/><path data-c="64" d="M376 495Q376 511 376 535T377 568Q377 613 367 624T316 637H298V660Q298 683 300 683L310 684Q320 685 339 686T376 688Q393 689 413 690T443 693T454 694H457V390Q457 84 458 81Q461 61 472 55T517 46H535V0Q533 0 459 -5T380 -11H373V44L365 37Q307 -11 235 -11Q158 -11 96 50T34 215Q34 315 97 378T244 442Q319 442 376 393V495ZM373 342Q328 405 260 405Q211 405 173 369Q146 341 139 305T131 211Q131 155 138 120T173 59Q203 26 251 26Q322 26 373 103V342Z" transform="translate(5180,0)"/><path data-c="20" d="" transform="translate(5736,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(5986,0)"/><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z" transform="translate(6486,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(6792,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(7181,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(7625,0)"/><path data-c="A0" d="" transform="translate(8017,0)"/></g><g data-mml-node="mtext" transform="translate(8792,0)"><path data-c="1D68A" d="M126 306Q105 306 90 321T74 359Q74 439 211 439Q268 439 276 438Q343 426 383 390T430 306Q431 301 431 190V81Q446 79 465 78T492 76T509 72T521 60T524 38Q524 11 506 3Q502 1 466 1Q426 1 406 5T379 14T355 36L345 30Q284 -6 205 -6Q135 -6 92 39T48 141Q48 182 79 212T158 256T252 278T342 285H347V290Q347 315 325 335T267 362Q258 363 224 363Q189 363 185 362H179L178 358Q178 353 178 352T176 345T174 337T170 330T165 322T158 316T150 311T139 308T126 306ZM132 140Q132 115 157 93T224 70Q269 70 302 87T344 133Q346 139 347 175V211H339Q256 209 194 190T132 140Z"/></g><g data-mml-node="mtext" transform="translate(9317,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/><path data-c="20" d="" transform="translate(278,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(528,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(1028,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(1472,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(1916,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(2416,0)"/><path data-c="64" d="M376 495Q376 511 376 535T377 568Q377 613 367 624T316 637H298V660Q298 683 300 683L310 684Q320 685 339 686T376 688Q393 689 413 690T443 693T454 694H457V390Q457 84 458 81Q461 61 472 55T517 46H535V0Q533 0 459 -5T380 -11H373V44L365 37Q307 -11 235 -11Q158 -11 96 50T34 215Q34 315 97 378T244 442Q319 442 376 393V495ZM373 342Q328 405 260 405Q211 405 173 369Q146 341 139 305T131 211Q131 155 138 120T173 59Q203 26 251 26Q322 26 373 103V342Z" transform="translate(2808,0)"/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(3364,0)"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(3642,0)"/><path data-c="67" d="M329 409Q373 453 429 453Q459 453 472 434T485 396Q485 382 476 371T449 360Q416 360 412 390Q410 404 415 411Q415 412 416 414V415Q388 412 363 393Q355 388 355 386Q355 385 359 381T368 369T379 351T388 325T392 292Q392 230 343 187T222 143Q172 143 123 171Q112 153 112 133Q112 98 138 81Q147 75 155 75T227 73Q311 72 335 67Q396 58 431 26Q470 -13 470 -72Q470 -139 392 -175Q332 -206 250 -206Q167 -206 107 -175Q29 -140 29 -75Q29 -39 50 -15T92 18L103 24Q67 55 67 108Q67 155 96 193Q52 237 52 292Q52 355 102 398T223 442Q274 442 318 416L329 409ZM299 343Q294 371 273 387T221 404Q192 404 171 388T145 343Q142 326 142 292Q142 248 149 227T179 192Q196 182 222 182Q244 182 260 189T283 207T294 227T299 242Q302 258 302 292T299 343ZM403 -75Q403 -50 389 -34T348 -11T299 -2T245 0H218Q151 0 138 -6Q118 -15 107 -34T95 -74Q95 -84 101 -97T122 -127T170 -155T250 -167Q319 -167 361 -139T403 -75Z" transform="translate(4198,0)"/><path data-c="20" d="" transform="translate(4698,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(4948,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(5337,0)"/><path data-c="20" d="" transform="translate(5837,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(6087,0)"/><path data-c="68" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 124T102 167T103 217T103 272T103 329Q103 366 103 407T103 482T102 542T102 586T102 603Q99 622 88 628T43 637H25V660Q25 683 27 683L37 684Q47 685 66 686T103 688Q120 689 140 690T170 693T181 694H184V367Q244 442 328 442Q451 442 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(6476,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(7032,0)"/><path data-c="20" d="" transform="translate(7476,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(7726,0)"/><path data-c="62" d="M307 -11Q234 -11 168 55L158 37Q156 34 153 28T147 17T143 10L138 1L118 0H98V298Q98 599 97 603Q94 622 83 628T38 637H20V660Q20 683 22 683L32 684Q42 685 61 686T98 688Q115 689 135 690T165 693T176 694H179V543Q179 391 180 391L183 394Q186 397 192 401T207 411T228 421T254 431T286 439T323 442Q401 442 461 379T522 216Q522 115 458 52T307 -11ZM182 98Q182 97 187 90T196 79T206 67T218 55T233 44T250 35T271 29T295 26Q330 26 363 46T412 113Q424 148 424 212Q424 287 412 323Q385 405 300 405Q270 405 239 390T188 347L182 339V98Z" transform="translate(8226,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(8782,0)"/><path data-c="76" d="M338 431Q344 429 422 429Q479 429 503 431H508V385H497Q439 381 423 345Q421 341 356 172T288 -2Q283 -11 263 -11Q244 -11 239 -2Q99 359 98 364Q93 378 82 381T43 385H19V431H25L33 430Q41 430 53 430T79 430T104 429T122 428Q217 428 232 431H240V385H226Q187 384 184 370Q184 366 235 234L286 102L377 341V349Q377 363 367 372T349 383T335 385H331V431H338Z" transform="translate(9282,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(9810,0)"/><path data-c="20" d="" transform="translate(10254,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(10504,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(10896,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(11452,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(11730,0)"/></g></g></g></g><g data-mml-node="mtr" transform="translate(0,-1950)"><g data-mml-node="mtd"><g data-mml-node="mo"><path data-c="21D2" d="M580 514Q580 525 596 525Q601 525 604 525T609 525T613 524T615 523T617 520T619 517T622 512Q659 438 720 381T831 300T927 263Q944 258 944 250T935 239T898 228T840 204Q696 134 622 -12Q618 -21 615 -22T600 -24Q580 -24 580 -17Q580 -13 585 0Q620 69 671 123L681 133H70Q56 140 56 153Q56 168 72 173H725L735 181Q774 211 852 250Q851 251 834 259T789 283T735 319L725 327H72Q56 332 56 347Q56 360 70 367H681L671 377Q638 412 609 458T580 514Z"/></g></g><g data-mml-node="mtd" transform="translate(1000,0)"><g data-mml-node="mrow"><g data-mml-node="mtext"><path data-c="1D68A" d="M126 306Q105 306 90 321T74 359Q74 439 211 439Q268 439 276 438Q343 426 383 390T430 306Q431 301 431 190V81Q446 79 465 78T492 76T509 72T521 60T524 38Q524 11 506 3Q502 1 466 1Q426 1 406 5T379 14T355 36L345 30Q284 -6 205 -6Q135 -6 92 39T48 141Q48 182 79 212T158 256T252 278T342 285H347V290Q347 315 325 335T267 362Q258 363 224 363Q189 363 185 362H179L178 358Q178 353 178 352T176 345T174 337T170 330T165 322T158 316T150 311T139 308T126 306ZM132 140Q132 115 157 93T224 70Q269 70 302 87T344 133Q346 139 347 175V211H339Q256 209 194 190T132 140Z"/></g><g data-mml-node="mtext" transform="translate(525,0)"><path data-c="2019" d="M78 634Q78 659 95 676T138 694Q166 694 189 668T212 579Q212 525 190 476T146 403T118 379Q114 379 105 388T95 401Q95 404 107 417T133 448T161 500T176 572Q176 584 175 584T170 581T157 576T139 573Q114 573 96 590T78 634Z"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(278,0)"/><path data-c="20" d="" transform="translate(672,0)"/><path data-c="64" d="M376 495Q376 511 376 535T377 568Q377 613 367 624T316 637H298V660Q298 683 300 683L310 684Q320 685 339 686T376 688Q393 689 413 690T443 693T454 694H457V390Q457 84 458 81Q461 61 472 55T517 46H535V0Q533 0 459 -5T380 -11H373V44L365 37Q307 -11 235 -11Q158 -11 96 50T34 215Q34 315 97 378T244 442Q319 442 376 393V495ZM373 342Q328 405 260 405Q211 405 173 369Q146 341 139 305T131 211Q131 155 138 120T173 59Q203 26 251 26Q322 26 373 103V342Z" transform="translate(922,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(1478,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(1922,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(2316,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(2705,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(3097,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(3653,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(4097,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(4486,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(4986,0)"/><path data-c="20" d="" transform="translate(5378,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(5628,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(6072,0)"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(6572,0)"/><path data-c="20" d="" transform="translate(7128,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(7378,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(7772,0)"/><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z" transform="translate(8272,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(8578,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(9022,0)"/><path data-c="79" d="M69 -66Q91 -66 104 -80T118 -116Q118 -134 109 -145T91 -160Q84 -163 97 -166Q104 -168 111 -168Q131 -168 148 -159T175 -138T197 -106T213 -75T225 -43L242 0L170 183Q150 233 125 297Q101 358 96 368T80 381Q79 382 78 382Q66 385 34 385H19V431H26L46 430Q65 430 88 429T122 428Q129 428 142 428T171 429T200 430T224 430L233 431H241V385H232Q183 385 185 366L286 112Q286 113 332 227L376 341V350Q376 365 366 373T348 383T334 385H331V431H337H344Q351 431 361 431T382 430T405 429T422 429Q477 429 503 431H508V385H497Q441 380 422 345Q420 343 378 235T289 9T227 -131Q180 -204 113 -204Q69 -204 44 -177T19 -116Q19 -89 35 -78T69 -66Z" transform="translate(9300,0)"/><path data-c="20" d="" transform="translate(9828,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(10078,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(10634,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(11028,0)"/><path data-c="A0" d="" transform="translate(11472,0)"/></g><g data-mml-node="mtext" transform="translate(12247,0)"><path data-c="1D68B" d="M4 573Q4 596 15 603T52 611H90H124Q146 611 155 608T171 591Q173 586 173 491V396L182 402Q217 424 256 431Q280 437 309 437Q376 437 434 379T492 217Q492 162 473 118T422 47T358 8T293 -6Q229 -6 174 38Q171 13 163 7T135 1H131H122Q99 1 90 23L89 279V535H58L27 536Q4 543 4 573ZM409 215Q409 269 377 315T283 361Q255 361 224 344T177 297L173 290V167Q189 124 213 97T278 70Q330 70 369 111T409 215Z"/></g></g></g></g></g></g></g></svg></mjx-container>The above result sounds nice and is often enough protection, but people tend to overlook a few ways things can still go wrong:</p><ul><li>The program can, in theory, pass around the address of <code>b</code>. This should be discouraged, but it means that technically ANY object could access <code>b</code> in their destructor. If any of these objects are destructed after <code>b</code>, we&apos;re doomed.</li><li>When one thread crashes and destructs objects, there is a possibility that at this point another thread is still running and have access to the object. We&apos;re doomed again.</li><li>The above &quot;reverse-ordering&quot; rule, though written in cppreference.com, is <strong>not always true</strong> under certain build options!<figure class="highlight cpp"><table><tr><td class="code"><pre><code class="hljs cpp"><span class="hljs-comment">// bar.cpp:</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span><br><span class="hljs-function"><span class="hljs-keyword">extern</span> <span class="hljs-type">void</span> <span class="hljs-title">register_B</span><span class="hljs-params">()</span></span>;<br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">A</span> {<br>  <span class="hljs-built_in">A</span>() { <span class="hljs-built_in">register_B</span>(); <span class="hljs-built_in">puts</span>(<span class="hljs-string">&quot;Finishing A()&quot;</span>); }<br>  ~<span class="hljs-built_in">A</span>() { <span class="hljs-built_in">puts</span>(<span class="hljs-string">&quot;~A()&quot;</span>); }<br>};<br>A a;<br><span class="hljs-comment">// main.cpp:</span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdio.h&gt;</span></span><br><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;stdlib.h&gt;</span></span><br><span class="hljs-keyword">struct</span> <span class="hljs-title class_">B</span> {<br>  <span class="hljs-built_in">B</span>() { <span class="hljs-built_in">puts</span>(<span class="hljs-string">&quot;Finishing B()&quot;</span>); }<br>  ~<span class="hljs-built_in">B</span>() { <span class="hljs-built_in">puts</span>(<span class="hljs-string">&quot;~B()&quot;</span>); }<br>};<br><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">register_B</span><span class="hljs-params">()</span> </span>{ <span class="hljs-type">static</span> B b; }<br><span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">(<span class="hljs-type">void</span>)</span> </span>{ <span class="hljs-built_in">puts</span>(<span class="hljs-string">&quot;main&quot;</span>);}<br></code></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash">$ g++ -fPIC -shared bar.cpp -o bar.so<br>$ g++ -pie -Wl,--no-as-needed main.cpp ./bar.so -o main-pie &amp;&amp; ./main-pie<br>Finishing B()<br>Finishing A()<br>main<br>~B()<br>~A()<br>$ g++ -no-pie -Wl,--no-as-needed main.cpp ./bar.so -o main-no-pie &amp;&amp; ./main-no-pie<br>Finishing B()<br>Finishing A()<br>main<br>~A()<br>~B()<br></code></pre></td></tr></table></figure>I discovered this the hard way when debugging another <a href="https://github.com/pytorch/pytorch/issues/79236" aria-label="[bazel] quantization::_Bfloat16QuantizedToFloat kernel de-registration fails &#xB7; Issue #79236 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">PyTorch issue</a>:Still trying to understand if this is considered a compiler bug.</li></ul><p>Given the above issues, the Google C++ style guide <a href="https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables" aria-label="Google C++ Style Guide" class="hint--top hint--rounded hint--no-animate hint--no-arrow">bluntly forbids such destructions</a>:</p><blockquote><p>Objects with&#xA0;static storage duration&#xA0;are forbidden unless they are&#xA0;<a href="https://en.cppreference.com/w/cpp/language/destructor#Trivial_destructor" aria-label="Destructors - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">trivially destructible</a>.</p></blockquote><p>This &quot;no destruction&quot; rule implies that the following code is illegal</p><figure class="highlight cpp"><table><tr><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">Object&amp; <span class="hljs-title">get_a</span><span class="hljs-params">()</span> </span>{ <span class="hljs-type">static</span> Object a; <span class="hljs-keyword">return</span> a;}<br></code></pre></td></tr></table></figure><p>if <code>Object</code> is not trivially destructible. <a href="https://isocpp.org/wiki/faq/ctors#construct-on-first-use-v2" aria-label="Standard C++" class="hint--top hint--rounded hint--no-animate hint--no-arrow">C++ FAQ</a> advises the same.</p><p>Writing <code>static Object* a = new Object; return *a;</code> is safe as long as we never call <code>delete</code>, but this introduces a heap-allocation overhead.The last trick is to use a <a href="https://chromium.googlesource.com/chromium/src/base/+/40343e3fbc1e4835145f125adedcf15a2eb4542f/no_destructor.h" aria-label="no_destructor.h - chromium/src/base - Git at Google" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>NoDestructor</code></a>wrapper class to bypass RAII(the trick is <a href="https://en.cppreference.com/w/cpp/language/new#Placement_new" aria-label="new expression - cppreference.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">placement new operator</a>):</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Safe, but has heap allocation overhead</th><th>Safe and low overhead</th></tr> <tr><td><figure class="highlight cpp"><table><tr><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">Object&amp; <span class="hljs-title">get_a</span><span class="hljs-params">()</span> </span>{<br>    <span class="hljs-type">static</span> Object* a = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Object</span>();<br>    <span class="hljs-keyword">return</span> *a;<br>}<br></code></pre></td></tr></table></figure></td><td><figure class="highlight cpp"><table><tr><td class="code"><pre><code class="hljs cpp"><span class="hljs-function">Object&amp; <span class="hljs-title">get_a</span><span class="hljs-params">()</span> </span>{<br>    <span class="hljs-type">static</span> NoDestructor&lt;Object&gt; a;<br>    <span class="hljs-keyword">return</span> *a;<br>}<br></code></pre></td></tr></table></figure></td></tr></table></div><p>Finally, as an alternative to &quot;no destruction&quot;,another way to safely run destructors is to<a href="https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter" aria-label="More C++ Idioms/Nifty Counter - Wikibooks, open books for an open world" class="hint--top hint--rounded hint--no-animate hint--no-arrow">ref-counting all such objects</a>,but it&apos;s perhaps not worth the complexity. &quot;No destruction&quot; is usually a good enough solution.</p><h2 id="Summary">Summary<a class="markdown-anchor" href="#Summary">&#xB6;</a></h2><p>In conclusion, to safely construct and destruct objects with static storage duration + dynamic initialization, follow these rules of thumb:</p><ul><li><strong>Safe Initialization</strong>: use &quot;construct on first use&quot; idiom</li><li><strong>No Destruction</strong>: don&apos;t run any non-trivial destructors</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;Since I joined Google Brain, I brought PyTorch to Google&amp;apos;s internal infra and
owned its maintenance. Being a &amp;quot;tech island&amp;quot;, it&amp;apos;s well known that almost
everything in Google works differently from the outside world, and that
creates many challenges when building a massive library like PyTorch.&lt;/p&gt;
&lt;p&gt;Among those challenges, there are a few tricky bugs related to &lt;a href=&quot;https://en.cppreference.com/w/cpp/language/siof&quot; aria-label=&quot;Static Initialization Order Fiasco - cppreference.com&quot; class=&quot;hint--top hint--rounded hint--no-animate hint--no-arrow&quot;&gt;static
initialization order fiasco&lt;/a&gt;
(SIOF) and their destructions. This time I was forced to learn a lot more details
than I&amp;apos;d like to know about these topics, so it&amp;apos;s good to write them down before I forget.&lt;/p&gt;</summary>
    
    
    
    
    <category term="PyTorch" scheme="https://ppwwyyxx.com/blog/tags/PyTorch/"/>
    
    <category term="C++" scheme="https://ppwwyyxx.com/blog/tags/C/"/>
    
  </entry>
  
  <entry>
    <title>Some Useful Terminal Escape Sequences</title>
    <link href="https://ppwwyyxx.com/blog/2023/Terminal-Escape-Sequences/"/>
    <id>https://ppwwyyxx.com/blog/2023/Terminal-Escape-Sequences/</id>
    <published>2023-01-15T08:00:00.000Z</published>
    <updated>2023-01-15T08:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>&#x6700;&#x8FD1;&#x5B66;&#x4E60;&#x5230;&#x4E86;&#x4E00;&#x4E9B; Terminal Escape Sequences, &#x5176;&#x4E2D;&#x5C24;&#x5176;&#x5BF9; OSC52 &#x76F8;&#x89C1;&#x6068;&#x665A;. &#x8FD9;&#x91CC;&#x7A0D;&#x5FAE;&#x8BB0;&#x5F55;&#x4E00;&#x4E0B;&#x5404;&#x79CD; Sequences.</p><p>Terminal Escape Sequences &#x662F;&#x7EC8;&#x7AEF;&#x5E94;&#x7528;&#x5411; stdout &#x6253;&#x51FA;&#x7684;&#x4E00;&#x4E9B;&#x5177;&#x6709;&#x7279;&#x6B8A;&#x542B;&#x4E49;&#x7684;&#x5B57;&#x7B26;&#x4E32;. &#x7EC8;&#x7AEF;&#x770B;&#x5230;&#x8FD9;&#x4E9B;&#x4E32;&#x4E4B;&#x540E;&#x4E0D;&#x4F1A;&#x663E;&#x793A;&#x5B83;&#x4EEC;, &#x800C;&#x662F;&#x6267;&#x884C;&#x8FD9;&#x4E9B;&#x4E32;&#x6240;&#x5BF9;&#x5E94;&#x7684;&#x7EC8;&#x7AEF;&#x9AD8;&#x7EA7;&#x529F;&#x80FD;.</p><span id="more"></span><h2 id="Color-Rendering">Color &amp; Rendering<a class="markdown-anchor" href="#Color-Rendering">&#xB6;</a></h2><p>&#x6700;&#x5E38;&#x89C1;&#x7684; escape sequence &#x5C31;&#x662F;&#x6539;&#x53D8;&#x5B57;&#x7684;&#x989C;&#x8272;. &#x4F8B;&#x5982;, &#x8FD9;&#x4E2A;&#x547D;&#x4EE4;&#x4F1A;&#x6253;&#x5370;&#x51FA;&#x7EA2;&#x8272;&#x7684; &quot;<span style="color:red"> Hello World </span>&quot;:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">printf</span> <span class="hljs-string">&quot;\e[31mHello World\e[0m\n&quot;</span><br></code></pre></td></tr></table></figure><p>&#x7EC8;&#x7AEF;&#x989C;&#x8272;&#x6700;&#x521D;&#x53EA;&#x6709; 8 &#x79CD;, &#x800C;&#x5982;&#x4ECA;&#x591A;&#x6570;&#x7EC8;&#x7AEF;&#x5DF2;&#x7ECF;&#x652F;&#x6301; <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.05ex;" xmlns="http://www.w3.org/2000/svg" width="4.381ex" height="2.003ex" role="img" focusable="false" viewbox="0 -863.3 1936.6 885.3"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msup"><g data-mml-node="mn"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(500,0)"/><path data-c="36" d="M42 313Q42 476 123 571T303 666Q372 666 402 630T432 550Q432 525 418 510T379 495Q356 495 341 509T326 548Q326 592 373 601Q351 623 311 626Q240 626 194 566Q147 500 147 364L148 360Q153 366 156 373Q197 433 263 433H267Q313 433 348 414Q372 400 396 374T435 317Q456 268 456 210V192Q456 169 451 149Q440 90 387 34T253 -22Q225 -22 199 -14T143 16T92 75T56 172T42 313ZM257 397Q227 397 205 380T171 335T154 278T148 216Q148 133 160 97T198 39Q222 21 251 21Q302 21 329 59Q342 77 347 104T352 209Q352 289 347 316T329 361Q302 397 257 397Z" transform="translate(1000,0)"/></g><g data-mml-node="mn" transform="translate(1533,393.1) scale(0.707)"><path data-c="33" d="M127 463Q100 463 85 480T69 524Q69 579 117 622T233 665Q268 665 277 664Q351 652 390 611T430 522Q430 470 396 421T302 350L299 348Q299 347 308 345T337 336T375 315Q457 262 457 175Q457 96 395 37T238 -22Q158 -22 100 21T42 130Q42 158 60 175T105 193Q133 193 151 175T169 130Q169 119 166 110T159 94T148 82T136 74T126 70T118 67L114 66Q165 21 238 21Q293 21 321 74Q338 107 338 175V195Q338 290 274 322Q259 328 213 329L171 330L168 332Q166 335 166 348Q166 366 174 366Q202 366 232 371Q266 376 294 413T322 525V533Q322 590 287 612Q265 626 240 626Q208 626 181 615T143 592T132 580H135Q138 579 143 578T153 573T165 566T175 555T183 540T186 520Q186 498 172 481T127 463Z"/></g></g></g></g></svg></mjx-container> &#x79CD;&#x7684; truecolor &#x4E86;. &#x8FD9;&#x4E2A;&#x547D;&#x4EE4;&#x4F1A;&#x6253;&#x5370;&#x51FA; RGB &#x4E3A; (255, 100, 0) &#x7684; &quot;<span style="color:#ff6400"> Hello World </span>&quot;:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">printf</span> <span class="hljs-string">&quot;\e[38;2;255;100;0mHello World\e[0m\n&quot;</span><br></code></pre></td></tr></table></figure><p>&#x4E0D;&#x8FC7;&#x5927;&#x591A;&#x6570;&#x5E94;&#x7528;&#x8FD8;&#x662F;&#x53EA;&#x4F7F;&#x7528; 8 &#x79CD;&#x989C;&#x8272;. &#x4E30;&#x5BCC;&#x7684;&#x989C;&#x8272;&#x4E3B;&#x8981;&#x5728;&#x4EE3;&#x7801;&#x9AD8;&#x4EAE;&#x91CC;&#x6BD4;&#x8F83;&#x6709;&#x7528;: vim &#x4E2D;&#x4F7F;&#x7528;<code>set termguicolors</code> &#x6765;&#x6253;&#x5F00; true color &#x652F;&#x6301;,  &#x4E4B;&#x540E;&#x4FBF;&#x53EF;&#x4EE5;&#x7528; truecolor &#x6765;&#x914D;&#x7F6E;&#x5404;&#x4E2A; highlight group &#x7684; guifg &#x548C; guibg.</p><p><a href="https://gist.github.com/lilydjwg/fdeaf79e921c2f413f44b6f613f6ad53" aria-label="colors.py: show all kinds of terminal colors at a glance" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x4E2A;&#x6709;&#x7528;&#x7684;&#x811A;&#x672C;</a>&#x53EF;&#x4EE5;&#x6253;&#x51FA;&#x7EC8;&#x7AEF;&#x652F;&#x6301;&#x7684;&#x5404;&#x79CD;&#x989C;&#x8272;, &#x4EE5;&#x53CA;&#x5176;&#x4ED6;&#x6E32;&#x67D3;&#x7279;&#x6027;, &#x53EF;&#x60DC;&#x5927;&#x591A;&#x6570;&#x90FD;&#x6CA1;&#x6709;&#x4EC0;&#x4E48;&#x5E94;&#x7528;&#x5728;&#x7528;. Kitty &#x7EC8;&#x7AEF;&#x4E0B;&#x7684;&#x8F93;&#x51FA;&#x662F;&#x8FD9;&#x6837;&#x7684;:</p><!-- TODO: -https://askubuntu.com/a/985386/1580503 --><img src="/blog/2023/Terminal-Escape-Sequences/attributes.jpg" class="center" width="800"><h2 id="Clipboard-OSC52">Clipboard (OSC52)<a class="markdown-anchor" href="#Clipboard-OSC52">&#xB6;</a></h2><p>&#x5728;&#x652F;&#x6301;&#x7684;&#x7EC8;&#x7AEF;&#x91CC;&#x6267;&#x884C;&#x4EE5;&#x4E0B;&#x547D;&#x4EE4;&#x4F1A;&#x5C06; &quot;Hello World&quot; &#x590D;&#x5236;&#x5230;&#x526A;&#x8D34;&#x677F;.</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">printf</span> <span class="hljs-string">&quot;\e]52;c;<span class="hljs-subst">$(echo <span class="hljs-string">&quot;Hello World&quot;</span> | base64)</span>\a&quot;</span><br></code></pre></td></tr></table></figure><p>&#x8FD9;&#x4E2A; escape sequence &#x4E00;&#x822C;&#x79F0;&#x4F5C; &quot;OSC52&quot;, &#x5176;&#x4E2D; OSC &#x662F; &quot;operating system command&quot; &#x7684;&#x610F;&#x601D;.OSC52 &#x79D1;&#x5B66;&#x7684;&#x89E3;&#x51B3;&#x4E86;&#x4E00;&#x4E2A;&#x56F0;&#x6270;&#x6211;&#x5341;&#x591A;&#x5E74;&#x7684;&#x95EE;&#x9898;: <strong>&#x600E;&#x4E48;&#x5728; ssh + vim/tmux &#x7684;&#x65F6;&#x5019;&#x590D;&#x5236;&#x7EC8;&#x7AEF;&#x4E0A;&#x7684;&#x6587;&#x5B57;&#x5230;&#x672C;&#x5730;&#x526A;&#x8D34;&#x677F;</strong>? </p><p>&#x7EC8;&#x7AEF;&#x81EA;&#x5E26;&#x7684;&#x9009;&#x4E2D; + &#x590D;&#x5236;&#x7684;&#x529F;&#x80FD;&#x5E76;&#x4E0D;&#x80FD;&#x5F88;&#x597D;&#x7684;&#x4E0E; vim/tmux &#x8FD9;&#x7C7B;&#x6709; &quot;&#x7A97;&#x53E3;&quot; &#x7684;&#x7EC8;&#x7AEF;&#x5E94;&#x7528;&#x4E00;&#x8D77;&#x5DE5;&#x4F5C;, &#x56E0;&#x4E3A;:</p><ol><li>&#x65E0;&#x6CD5;&#x9009;&#x4E2D;&#x8D85;&#x8FC7;&#x4E00;&#x5C4F;&#x7684;&#x6587;&#x5B57;. &#x56E0;&#x4E3A;&#x7EC8;&#x7AEF;&#x7684;&#x9009;&#x4E2D;&#x529F;&#x80FD;&#x65E0;&#x6CD5;&#x5BF9; vim/tmux &#x91CC;&#x7684;&#x7A97;&#x53E3;&#x8FDB;&#x884C;&#x7FFB;&#x9875;.</li><li>&#x7ECF;&#x5E38;&#x4F1A;&#x88AB;&#x8FEB;&#x9009;&#x4E2D;&#x7EC8;&#x7AEF;&#x5E94;&#x7528;&#x7684; UI &#x5B57;&#x7B26;, &#x5C24;&#x5176;&#x662F;&#x5F53;&#x5E94;&#x7528;&#x6709;&#x591A;&#x4E2A;&#x7A97;&#x53E3;&#x7684;&#x65F6;&#x5019;. &#x4F8B;&#x5982;&#x5F53;&#x6211;&#x60F3;&#x8981;&#x9009;&#x4E2D;&#x53F3;&#x8FB9;&#x4E24;&#x884C;&#x65F6;:<img src="/blog/2023/Terminal-Escape-Sequences/selection-ui.jpg" class="center" width="500"></li></ol><p>&#x5982;&#x679C; vim/tmux &#x8FD0;&#x884C;&#x5728;&#x672C;&#x5730;, &#x8FD9;&#x4E9B;&#x95EE;&#x9898;&#x90FD;&#x5F88;&#x5BB9;&#x6613;&#x89E3;&#x51B3;: vim/tmux &#x5404;&#x81EA;&#x63D0;&#x4F9B;&#x4E86;&#x81EA;&#x5DF1;&#x7684;&#x9009;&#x4E2D; + &#x590D;&#x5236;&#x529F;&#x80FD;,  &#x5E76;&#x4E14;&#x90FD;&#x53EF;&#x4EE5;&#x8BFB;&#x5199;&#x672C;&#x5730;&#x7684;&#x7CFB;&#x7EDF;&#x526A;&#x8D34;&#x677F;. &#x4F46;&#x662F;&#x5F53;&#x5B83;&#x4EEC;&#x8DD1;&#x5728; ssh &#x91CC;&#x7684;&#x65F6;&#x5019;, &#x6211;&#x5C31;&#x53EA;&#x80FD;&#x4F9D;&#x8D56; hack:</p><ul><li>&#x5982;&#x679C;&#x8981;&#x9009;&#x4E2D;&#x8D85;&#x8FC7;&#x4E00;&#x5C4F;&#x7684;&#x6587;&#x5B57;, &#x5C31;&#x628A;&#x5B57;&#x4F53;&#x7F29;&#x5C0F;&#x8BD5;&#x8BD5;&#x80FD;&#x4E0D;&#x80FD;&#x4E00;&#x5C4F;&#x88C5;&#x4E0B;..</li><li>&#x8BA9; vim/tmux &#x5404;&#x81EA;&#x628A; UI &#x5C3D;&#x91CF;&#x5173;&#x6389; (&#x4F8B;&#x5982;&#x628A;&#x7A97;&#x53E3;&#x72EC;&#x7ACB;&#x51FA;&#x6765;, &#x8BA9; vim &#x4E0D;&#x663E;&#x793A;&#x884C;&#x53F7;&#x7B49;&#x7B49;).</li><li>&#x4EE5;&#x4E0A;&#x90FD;&#x4E0D; work &#x7684;&#x65F6;&#x5019;&#x5C31;&#x6CA1;&#x529E;&#x6CD5;&#x4E86;. &#x66FE;&#x7ECF;&#x5C1D;&#x8BD5;&#x8FC7;<a href="https://github.com/jedisct1/piknik" aria-label="jedisct1/piknik: Copy/paste anything over the network." class="hint--top hint--rounded hint--no-animate hint--no-arrow"> piknik</a>, &#x4F46;&#x662F;&#x4F7F;&#x7528;&#x592A;&#x590D;&#x6742;&#x4E86;.</li></ul><p>&#x6709;&#x4E86; OSC52 &#x4E4B;&#x540E;&#x518D;&#x4E5F;&#x4E0D;&#x4F1A;&#x6709;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#x4E86;: ssh &#x91CC;&#x7684;&#x5E94;&#x7528;&#x53EA;&#x8981;&#x8F93;&#x51FA;&#x4E86; OSC52 &#x7684;&#x63A7;&#x5236;&#x5B57;&#x7B26;, &#x88AB;&#x672C;&#x5730;&#x7684;&#x7EC8;&#x7AEF;&#x770B;&#x5230;&#x4E86;, &#x5C31;&#x53EF;&#x4EE5;&#x5199;&#x5165;&#x672C;&#x5730;&#x7684;&#x526A;&#x8D34;&#x677F;. &#x5177;&#x4F53;&#x65B9;&#x6848;&#x53EF;&#x4EE5;&#x8FD9;&#x6837;:</p><ol><li>vim &#x91CC;&#x4F7F;&#x7528;<a href="https://github.com/ojroques/vim-oscyank" aria-label="ojroques/vim-oscyank: A Vim plugin to copy text through SSH with OSC52" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> vim-oscyank</a> &#x63D2;&#x4EF6;&#x5728;&#x590D;&#x5236;&#x65F6;&#x8F93;&#x51FA; OSC52 &#x5B57;&#x7B26;.</li><li>tmux &#x91CC;&#x4F7F;&#x7528;<code>set-clipboard on</code> &#x9009;&#x9879;. &#x8FD9;&#x4E2A;&#x9009;&#x9879;&#x540C;&#x65F6;&#x505A;&#x4E86;&#x4E24;&#x4EF6;&#x4E8B; (&#x611F;&#x89C9;<a href="https://github.com/tmux/tmux/wiki/Clipboard" aria-label="Clipboard &#xB7; tmux/tmux Wiki" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5B98;&#x65B9; wiki</a> &#x89E3;&#x91CA;&#x7684;&#x5E76;&#x4E0D;&#x6E05;&#x695A;):<ul><li>tmux &#x4F1A;&#x5C06; copy-mode &#x91CC;&#x9009;&#x4E2D;&#x5E76;&#x590D;&#x5236;&#x7684;&#x5185;&#x5BB9;&#x7ECF;&#x7531; OSC52 &#x8F93;&#x51FA;.</li><li>tmux &#x5185;&#x8FD0;&#x884C;&#x7684;&#x5E94;&#x7528;&#x8F93;&#x51FA;&#x7684; OSC52 &#x63A7;&#x5236;&#x5B57;&#x7B26;&#x4F1A;&#x88AB; tmux &#x6B63;&#x786E;&#x7684;&#x8F6C;&#x53D1;&#x5230;&#x5916;&#x9762;. &#x8FD9;&#x6837;&#x5982;&#x679C; tmux &#x91CC;&#x8FD0;&#x884C;&#x4E86; vim, &#x4E5F;&#x80FD;&#x6B63;&#x786E;&#x5DE5;&#x4F5C;.</li></ul></li><li>&#x53E6;&#x5916;&#x641E;&#x4E86;&#x4E2A;&#x7B80;&#x5355;&#x7684;<a href="https://github.com/ppwwyyxx/dotfiles/blob/06c31ae166ae768baf0d8b5e20d5fe6e94665ae9/scripts/yank" aria-label="yank &#xB7; ppwwyyxx/dotfiles" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>yank</code> &#x811A;&#x672C;</a>&#x7528;&#x4E8E;&#x547D;&#x4EE4;&#x884C;:<code>$ run_some_command | yank</code>.</li></ol><h2 id="Hyperlink-OSC8">Hyperlink (OSC8)<a class="markdown-anchor" href="#Hyperlink-OSC8">&#xB6;</a></h2><p>&#x5728;&#x652F;&#x6301;&#x7684;&#x7EC8;&#x7AEF;&#x91CC;, &#x8FD9;&#x4E2A;&#x547D;&#x4EE4;&#x4F1A;&#x8F93;&#x51FA; &quot;This is a link&quot;, &#x9F20;&#x6807;&#x70B9;&#x51FB;&#x8F93;&#x51FA;&#x7684;&#x6587;&#x5B57;&#x4F1A;&#x6253;&#x5F00; &quot;example.com&quot;:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">printf</span> <span class="hljs-string">&apos;\e]8;;http://example.com\e\\This is a link\e]8;;\e\\\n&apos;</span><br></code></pre></td></tr></table></figure><img src="/blog/2023/Terminal-Escape-Sequences/hyperlink.jpg" class="center" width="600"><p>&#x6211;&#x4E0E;&#x5176;&#x4ED6;&#x4EBA;&#x5171;&#x540C;&#x7EF4;&#x62A4;&#x4E86;&#x4E00;&#x4E2A;<a href="https://github.com/Alhadis/OSC8-Adoption" aria-label="Alhadis/OSC8-Adoption: List of terminal emulators that support hyperlinks (OSC 8 escape sequences)." class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x6587;&#x6863;</a>,  &#x8BB0;&#x5F55;&#x4E86;&#x652F;&#x6301; OSC8 hyperlink &#x7684;&#x7EC8;&#x7AEF;&#x548C;&#x4F1A;&#x4F7F;&#x7528; OSC8 hyperlink &#x7684;&#x5E94;&#x7528;.</p><p>&#x7531;&#x4E8E;&#x5927;&#x90E8;&#x5206;&#x7EC8;&#x7AEF;&#x5DF2;&#x7ECF;&#x6709;&#x4E86;&#x57FA;&#x4E8E; regex &#x5339;&#x914D;&#x6587;&#x5B57;&#x4E2D;&#x7684; URL &#x7684;&#x529F;&#x80FD;, &#x56E0;&#x6B64; hyperlink &#x7684;&#x529F;&#x80FD;&#x5E76;&#x4E0D;&#x662F;&#x5341;&#x5206;&#x521A;&#x9700;, &#x53EF;&#x80FD;&#x8FD8;&#x9700;&#x8981;&#x5E94;&#x7528;&#x5F00;&#x53D1;&#x8005;&#x53D1;&#x6325;&#x66F4;&#x591A;&#x60F3;&#x8C61;&#x529B;. &#x76EE;&#x524D;&#x6211;&#x7528;&#x5230;&#x7684;&#x573A;&#x666F;&#x4EC5;&#x6709;:</p><ol><li><code>ls --hyperlink=auto</code>. &#x4F7F;&#x7528;&#x4E86;&#x8FD9;&#x4E2A; alias &#x4E4B;&#x540E;&#x53EF;&#x4EE5;&#x5728;&#x7EC8;&#x7AEF;&#x91CC;&#x70B9;&#x51FB;&#x6587;&#x4EF6;&#x540D;&#x6253;&#x5F00;&#x6587;&#x4EF6;.</li><li>&#x5728; source control &#x5DE5;&#x5177;&#x91CC;, &#x70B9;&#x51FB; commit &#x6253;&#x5F00;&#x5BF9;&#x5E94;&#x7684;&#x7F51;&#x9875; (&#x4F8B;&#x5982; github, bitbucket).<ul><li>&#x7C7B;&#x4F3C;&#x7684;, &#x5E0C;&#x671B;&#x5728; git prompt &#x91CC;&#x70B9;&#x51FB;&#x4E5F;&#x53EF;&#x4EE5;&#x6253;&#x5F00;&#x5BF9;&#x5E94; repo &#x6216; branch &#x7684;&#x7F51;&#x9875;.</li><li>Google &#x5185;&#x90E8;&#x7684; source control &#x6709;&#x8FD9;&#x4E9B;&#x529F;&#x80FD;. &#x5E0C;&#x671B;&#x6709;&#x4EBA;&#x80FD;&#x7ED9;<code>git log</code> &#x505A;&#x4E00;&#x4E2A;&#x7C7B;&#x4F3C;&#x7684;.<ul><li>UPDATE: git &#x53EF;&#x4EE5;&#x4F7F;&#x7528; <a href="https://github.com/dandavison/delta" aria-label="dandavison/delta: A syntax-highlighting pager for git, diff, and grep output" class="hint--top hint--rounded hint--no-animate hint--no-arrow">delta</a></li></ul></li></ul></li><li><a href="https://github.com/swsnr/mdcat" aria-label="swsnr/mdcat: cat for markdown" class="hint--top hint--rounded hint--no-animate hint--no-arrow">mdcat</a>, &#x4F46;&#x662F;&#x5E76;&#x4E0D;&#x5E38;&#x7528;.</li></ol><h2 id="Kitty-Graphics-Protocol">Kitty Graphics Protocol<a class="markdown-anchor" href="#Kitty-Graphics-Protocol">&#xB6;</a></h2><p>Kitty &#x7EC8;&#x7AEF;&#x81EA;&#x5DF1;&#x53D1;&#x660E;&#x4E86;&#x4E00;&#x5957; escape sequence &#x7528;&#x4E8E;&#x5728;&#x7EC8;&#x7AEF;&#x4E2D;&#x663E;&#x793A;&#x56FE;&#x7247;. &#x8FD9;&#x6837;&#x663E;&#x793A;&#x7684;&#x56FE;&#x7247;&#x4E0D;&#x662F;&#x7528;&#x5F69;&#x8272;&#x7684; unicode &#x5B57;&#x7B26;&#x62FC;&#x51FA;&#x7684;&#x9AD8;&#x7CCA;&#x56FE;, &#x800C;&#x662F;&#x6B63;&#x5E38;&#x7684;&#x9AD8;&#x6E05;&#x56FE;.<a href="https://github.com/hzeller/timg" aria-label="hzeller/timg: A terminal image and video viewer." class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>timg</code></a> &#x662F;&#x4E00;&#x4E2A;&#x652F;&#x6301; Kitty protocol &#x7684;&#x770B;&#x56FE;&#x5DE5;&#x5177;. &#x6709;&#x4E86;&#x5B83;&#x5C31;&#x53EF;&#x4EE5;&#x5728; ssh &#x7684;&#x65F6;&#x5019;&#x770B;&#x8FDC;&#x7AEF;&#x7684;&#x56FE;&#x7247;&#x4E86;.</p><img src="/blog/2023/Terminal-Escape-Sequences/timg.jpg" class="center" width="400"><p>&#x8981;&#x6CE8;&#x610F;&#x7684;&#x662F;, tmux &#x5E76;&#x4E0D;&#x652F;&#x6301;&#x8FD9;&#x4E2A;&#x975E;&#x6807;&#x51C6;&#x7684; protocol, &#x4F1A;&#x628A;&#x76F8;&#x5E94;&#x7684; escape sequence &#x541E;&#x6389;. &#x6240;&#x5E78;, tmux &#x63D0;&#x4F9B;&#x4E86; &quot;passthrough&quot; &#x529F;&#x80FD;: &#x5728;&#x6253;&#x5F00;&#x4E86;<code>allow-passthrough on</code> &#x4E4B;&#x540E;, &#x4F7F;&#x7528;&#x7279;&#x6B8A;&#x7684;<a href="https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it" aria-label="FAQ &#xB7; tmux/tmux Wiki" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> passthrough escape sequence</a> &#x53EF;&#x4EE5;&#x8BA9; tmux &#x628A;&#x5E94;&#x7528;&#x6253;&#x51FA;&#x7684; escape sequence &#x8F6C;&#x53D1;&#x5230;&#x5916;&#x5C42;. &#x7531;&#x4E8E; tmux &#x4E0D;&#x652F;&#x6301;, &#x5728; tmux &#x4E0B;&#x770B;&#x56FE;&#x8FD8;&#x4F1A;&#x6709;&#x4F4D;&#x7F6E;&#x9519;&#x4E71;&#x7684;&#x95EE;&#x9898;. &#x6211;&#x641E;&#x4E86;<a href="https://github.com/ppwwyyxx/dotfiles/blob/06c31ae166ae768baf0d8b5e20d5fe6e94665ae9/scripts/tmux-escape" aria-label="tmux-escape &#xB7; ppwwyyxx/dotfiles" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x4E00;&#x4E9B; hack</a> &#x52C9;&#x5F3A;&#x89E3;&#x51B3;&#x4E86;,  &#x5C31;&#x4E0D;&#x8FC7;&#x591A;&#x89E3;&#x91CA;&#x4E86;.</p><h2 id="Desktop-Notification-OSC9-OSC99">Desktop Notification (OSC9 / OSC99)<a class="markdown-anchor" href="#Desktop-Notification-OSC9-OSC99">&#xB6;</a></h2><p>&#x5728;&#x652F;&#x6301;&#x7684;&#x7EC8;&#x7AEF;&#x91CC;, &#x8FD9;&#x4E24;&#x4E2A;&#x547D;&#x4EE4;&#x4F1A;&#x5F39;&#x51FA; &quot;Hello World&quot; &#x7684;&#x901A;&#x77E5;:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">printf</span> <span class="hljs-string">&apos;\e]9;Hello World\e\\&apos;</span><br><span class="hljs-built_in">printf</span> <span class="hljs-string">&apos;\e]99;;Hello World\e\\&apos;</span>   <span class="hljs-comment"># Only in kitty</span><br></code></pre></td></tr></table></figure><p>&#x4E3B;&#x8981;&#x7684;&#x7528;&#x9014;&#x662F;&#x8BA9; ssh &#x8FDC;&#x7AEF;&#x7684;&#x7A0B;&#x5E8F;&#x7ED9;&#x672C;&#x5730;&#x53D1;&#x901A;&#x77E5;.tmux &#x540C;&#x6837;&#x4E0D;&#x652F;&#x6301;&#x8FD9;&#x4E2A; sequence, &#x9700;&#x8981;&#x914D;&#x5408; passthrough &#x4F7F;&#x7528;.</p><p>OSC9 &#x7684;&#x51FA;&#x73B0;&#x6BD4;&#x8F83;&#x65E9;, &#x517C;&#x5BB9;&#x6027;&#x4F1A;&#x66F4;&#x597D;. OSC99 &#x662F; kitty &#x81EA;&#x5DF1;&#x53D1;&#x660E;&#x7684;&#x7248;&#x672C;, &#x652F;&#x6301;&#x66F4;&#x4E30;&#x5BCC;&#x7684;&#x901A;&#x77E5;&#x683C;&#x5F0F;.</p><h2 id="Window-Title-OSC0">Window Title (OSC0)<a class="markdown-anchor" href="#Window-Title-OSC0">&#xB6;</a></h2><p>&#x8FD9;&#x4E2A;&#x547D;&#x4EE4;&#x8BA9;&#x4E0A;&#x5C42;&#x8BBE;&#x7F6E;&#x5F53;&#x524D;&#x7684;&#x7A97;&#x53E3;&#x6807;&#x9898;. &#x5177;&#x4F53;&#x505A;&#x4EC0;&#x4E48;&#x7531;&#x4E0A;&#x5C42; (tmux &#x6216;&#x7EC8;&#x7AEF;) &#x5B9E;&#x73B0;&#x51B3;&#x5B9A;:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">printf</span> <span class="hljs-string">&apos;\e]0;Hello World\a&apos;</span><br></code></pre></td></tr></table></figure><p>&#x7528;&#x5904;&#x4E0D;&#x5927;. &#x4E3B;&#x8981;&#x662F;&#x53EF;&#x4EE5;&#x8BA9; shell &#x81EA;&#x52A8;&#x8BBE;&#x7F6E;&#x6807;&#x9898;&#x4E3A; PWD &#x6216;&#x5F53;&#x524D;&#x5728;&#x6267;&#x884C;&#x7684;&#x547D;&#x4EE4;, &#x8FD9;&#x6837;&#x5F53;&#x5B58;&#x5728;&#x591A;&#x4E2A; tmux tab &#x6216;&#x7EC8;&#x7AEF; tab &#x7684;&#x65F6;&#x5019;&#x53EF;&#x4EE5;&#x65B9;&#x4FBF;&#x533A;&#x5206;.zsh &#x53EF;&#x4EE5;&#x8FD9;&#x6837;:</p><figure class="highlight bash"><table><tr><td class="code"><pre><code class="hljs bash"><span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">_my_update_title_cmd</span></span>() { <span class="hljs-built_in">echo</span> -ne <span class="hljs-string">&quot;\e]0;<span class="hljs-variable">${1%% *}</span>\a&quot;</span> }<br><span class="hljs-keyword">function</span> <span class="hljs-function"><span class="hljs-title">_my_update_title_pwd</span></span>() { <span class="hljs-built_in">echo</span> -ne <span class="hljs-string">&quot;\e]0;<span class="hljs-variable">${(%):-&quot;%3~&quot;}</span>\a&quot;</span> }<br><span class="hljs-built_in">autoload</span> -Uz add-zsh-hook<br>add-zsh-hook preexec _my_update_title_cmd<br>add-zsh-hook precmd _my_update_title_pwd<br></code></pre></td></tr></table></figure><hr><p>&#x6700;&#x540E;&#x5410;&#x70B9;&#x69FD;.</p><p>&#x6709;&#x4E0D;&#x5C11;&#x6709;&#x7528;&#x7684;&#x7EC8;&#x7AEF; feature &#x4EC5;&#x5B58;&#x5728;&#x4E8E;&#x4E00;&#x4E24;&#x4E2A;&#x7EC8;&#x7AEF;&#x91CC;: &#x8BFB;&#x526A;&#x8D34;&#x677F;, &#x4F20;&#x8F93;&#x6587;&#x4EF6;, &#x770B;&#x56FE;&#x770B;&#x89C6;&#x9891;, &#x8FDB;&#x5EA6;&#x6761;, &#x9F20;&#x6807;&#x60AC;&#x6D6E;&#x65F6;&#x663E;&#x793A; tooltip...</p><p>&#x7EC8;&#x7AEF; feature &#x4E00;&#x76F4;&#x7F3A;&#x4E4F;&#x6807;&#x51C6;&#x5316;: &#x5F88;&#x591A; escape code &#x5E76;&#x6CA1;&#x6709;&#x8BE6;&#x7EC6;&#x7684; spec -- &#x4E00;&#x4E2A;&#x65B0;&#x7684;&#x7EC8;&#x7AEF;&#x5F00;&#x53D1;&#x8005;&#x57FA;&#x672C;&#x4E0A;&#x4E3B;&#x8981;&#x9760;&#x770B;&#x5176;&#x4ED6;&#x7EC8;&#x7AEF;&#x7684;&#x4EE3;&#x7801;&#x6765;&#x7406;&#x89E3;&#x5B83;&#x4EEC;&#x7684;&#x884C;&#x4E3A;. &#x53E6;&#x5916;, &#x4E00;&#x4E9B;&#x7EC8;&#x7AEF;&#x81EA;&#x5DF1;&#x53D1;&#x660E;&#x7684; escape code &#x751A;&#x81F3;&#x4F1A;&#x4E92;&#x76F8;&#x51B2;&#x7A81; -- &#x7528;&#x4E86;&#x522B;&#x4EBA;&#x5DF2;&#x7ECF;&#x7528;&#x8FC7;&#x7684;&#x5B57;&#x7B26;.</p><p>&#x6BCF;&#x4E2A;&#x7EC8;&#x7AEF;&#x53EA;&#x4F1A;&#x5B9E;&#x73B0;&#x4E00;&#x90E8;&#x5206;&#x5B83;&#x8BA4;&#x4E3A;&#x6709;&#x4EF7;&#x503C;&#x7684;&#x7EC8;&#x7AEF; feature. &#x8FD9;&#x5C31;&#x4F7F;&#x5F97;&#x5927;&#x90E8;&#x5206;&#x5E94;&#x7528;&#x4E3A;&#x4E86;&#x517C;&#x5BB9;&#x6027;&#x90FD;&#x4E0D;&#x4F1A;&#x53BB;&#x4F7F;&#x7528;&#x9AD8;&#x7EA7;&#x7684; feature.</p><p>&#x5373;&#x4F7F;&#x4E00;&#x4E2A;&#x5E94;&#x7528;&#x60F3;&#x8981;&#x4F7F;&#x7528;&#x9AD8;&#x7EA7;&#x7684; feature, &#x5B83;&#x4E5F;&#x6CA1;&#x6709;&#x4E00;&#x4E2A;&#x597D;&#x7684;&#x65B9;&#x6CD5;&#x5224;&#x65AD;&#x7EC8;&#x7AEF;&#x662F;&#x5426;&#x652F;&#x6301;&#x4E00;&#x4E2A; feature. &#x5728;&#x8FD9;&#x91CC;&#x6211;&#x53D1;&#x73B0;&#x4E86;&#x4E00;&#x4E2A;&#x7C7B;&#x4F3C;&#x4E8E;<a href="https://www.zhihu.com/question/19553117/answer/1970834375" aria-label="&#x4E3A;&#x4EC0;&#x4E48;&#x6240;&#x6709;&#x4E3B;&#x6D41;&#x6D4F;&#x89C8;&#x5668;&#x7684; User-Agent &#x90FD;&#x662F; Mozilla/x.0 &#x5F00;&#x5934;&#xFF1F; - &#x77E5;&#x4E4E;" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x6D4F;&#x89C8;&#x5668;&#x7684; User-Agent &#x7684;&#x6545;&#x4E8B;</a>:</p><ul><li>&#x6700;&#x521D;, xterm &#x975E;&#x5E38;&#x6D41;&#x884C;&#x5E76;&#x4E14;&#x5B9E;&#x73B0;&#x4E86;&#x5404;&#x79CD;&#x9AD8;&#x7EA7; feature. &#x90A3;&#x4E2A;&#x65F6;&#x5019;, &#x5E94;&#x7528;&#x5224;&#x65AD;<code>$TERM</code> &#x73AF;&#x5883;&#x53D8;&#x91CF;&#x91CC;&#x6709;&#x6CA1;&#x6709; xterm &#x6765;&#x51B3;&#x5B9A;&#x8981;&#x4E0D;&#x8981;&#x4F7F;&#x7528;&#x8FD9;&#x4E9B; feature, &#x5224;&#x65AD;<code>$TERM</code> &#x91CC;&#x6709;&#x6CA1;&#x6709; &quot;256color&quot; &#x6765;&#x51B3;&#x5B9A;&#x8981;&#x4E0D;&#x8981;&#x4F7F;&#x7528; 256 &#x8272;&#x8F93;&#x51FA;.</li><li>&#x540E;&#x6765;, &#x66F4;&#x591A;&#x7684;&#x7EC8;&#x7AEF;&#x652F;&#x6301;&#x4E86;&#x8FD9;&#x4E9B; feature, &#x4F46;&#x662F;&#x7531;&#x4E8E;&#x7EC8;&#x7AEF;&#x540D;&#x5B57;&#x91CC;&#x6CA1;&#x6709; xterm, &#x5E94;&#x7528;&#x4E0D;&#x4F1A;&#x4F7F;&#x7528;&#x8FD9;&#x4E9B; feature, &#x6240;&#x4EE5;&#x5404;&#x4E2A;&#x7EC8;&#x7AEF;&#x90FD;&#x628A; &quot;xterm&quot; &#x52A0;&#x5165;&#x81EA;&#x5DF1;&#x7684;&#x540D;&#x5B57;.</li><li>&#x76F4;&#x5230;&#x4ECA;&#x5929;, gnome-terminal, konsole, iTerm, sakura &#x7B49;&#x5927;&#x591A;&#x6570;&#x7EC8;&#x7AEF;&#x9ED8;&#x8BA4;&#x7684;<code>$TERM</code> &#x540D;&#x79F0;&#x8FD8;&#x662F; &quot;xterm&quot; &#x6216; &quot;xterm-256color&quot;. kitty &#x7EC8;&#x7AEF;&#x7684;&#x540D;&#x5B57;&#x662F; &quot;xterm-kitty&quot;.</li></ul><p><a href="https://invisible-island.net/ncurses/man/terminfo.5.html" aria-label="terminfo 5 File Formats" class="hint--top hint--rounded hint--no-animate hint--no-arrow">terminfo</a> &#x5141;&#x8BB8;&#x5E94;&#x7528;&#x67E5;&#x8BE2;&#x7EC8;&#x7AEF;&#x662F;&#x5426;&#x652F;&#x6301;&#x7279;&#x5B9A; feature, &#x4F46;&#x7531;&#x4E8E; feature &#x7F3A;&#x4E4F;&#x6807;&#x51C6;&#x5316;, terminfo &#x4E5F;&#x5E76;&#x6CA1;&#x6709;&#x5F88;&#x597D;&#x7684;&#x89E3;&#x51B3;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;.</p><p>&#x5728;&#x8FD9;&#x4E2A;&#x6DF7;&#x4E71;&#x7684;&#x60C5;&#x5F62;&#x4E0B;, &#x7EC8;&#x7AEF;&#x5F00;&#x53D1;&#x8005;&#x4E5F;&#x96BE;&#x4EE5;&#x8FBE;&#x6210;&#x5171;&#x8BC6;. &#x66FE;&#x7ECF;&#x51E0;&#x4E2A;&#x7EC8;&#x7AEF;&#x5F00;&#x53D1;&#x8005;&#x7EC4;&#x7EC7;&#x4E86;&#x4E2A;<a href="https://gitlab.freedesktop.org/terminal-wg" aria-label="terminal-wg &#xB7; GitLab" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> terminal working group</a> &#x6765;&#x8BA8;&#x8BBA;&#x5404;&#x79CD; feature &#x7684;&#x63D0;&#x6848;, &#x4F46;&#x6700;&#x540E;&#x5927;&#x5BB6;&#x4E0D;&#x6B22;&#x800C;&#x6563;. <a href="https://gitlab.freedesktop.org/terminal-wg/specifications/-/issues/24" aria-label="Terminal WG Operational Doubts (#24) &#xB7; Issues &#xB7; terminal-wg / specifications &#xB7; GitLab" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x4E2A;&#x5E16;&#x5B50;</a>&#x8BB0;&#x5F55;&#x4E86;&#x7EC4;&#x7EC7;&#x8005;&#x7684;&#x5410;&#x69FD;.</p><p>&#x7531;&#x4E8E;&#x8FD9;&#x4E9B;&#x539F;&#x56E0;, &#x7EC8;&#x7AEF; feature &#x7684;&#x6F14;&#x5316;&#x5DF2;&#x7ECF;&#x57FA;&#x672C;&#x9677;&#x5165;&#x505C;&#x6EDE;. &#x53EA;&#x6709;&#x5C11;&#x6570;&#x51E0;&#x4E2A;&#x7EC8;&#x7AEF;&#x5728;&#x81EA;&#x5DF1;&#x53D1;&#x660E;&#x65B0; feature: &#x4F8B;&#x5982;<a href="https://iterm2.com/documentation-escape-codes.html" aria-label="Proprietary Escape Codes - Documentation - iTerm2 - macOS Terminal Replacement" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> iTerm2 &#x7684;&#x81EA;&#x917F; feature</a> &#x548C;<a href="https://sw.kovidgoyal.net/kitty/protocol-extensions/" aria-label="Terminal protocol extensions - kitty" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> kitty &#x7684;&#x81EA;&#x917F; feature</a>. &#x4F46;&#x7531;&#x4E8E;&#x7F3A;&#x4E4F;&#x6807;&#x51C6;&#x5316;, &#x6CA1;&#x6709;&#x5BF9;&#x793E;&#x533A;&#x4EA7;&#x751F;&#x592A;&#x591A;&#x5F71;&#x54CD;.</p><p>&#x7EC8;&#x7AEF;&#x662F;&#x6211;&#x5DE5;&#x4F5C;&#x7684;&#x4E3B;&#x529B;&#x5DE5;&#x5177;, &#x5E0C;&#x671B;&#x8FD9;&#x4E9B;&#x95EE;&#x9898;&#x80FD;&#x5F97;&#x5230;&#x89E3;&#x51B3;.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&amp;#x6700;&amp;#x8FD1;&amp;#x5B66;&amp;#x4E60;&amp;#x5230;&amp;#x4E86;&amp;#x4E00;&amp;#x4E9B; Terminal Escape Sequences, &amp;#x5176;&amp;#x4E2D;&amp;#x5C24;&amp;#x5176;&amp;#x5BF9; OSC52 &amp;#x76F8;&amp;#x89C1;&amp;#x6068;&amp;#x665A;.
 &amp;#x8FD9;&amp;#x91CC;&amp;#x7A0D;&amp;#x5FAE;&amp;#x8BB0;&amp;#x5F55;&amp;#x4E00;&amp;#x4E0B;&amp;#x5404;&amp;#x79CD; Sequences.&lt;/p&gt;
&lt;p&gt;Terminal Escape Sequences &amp;#x662F;&amp;#x7EC8;&amp;#x7AEF;&amp;#x5E94;&amp;#x7528;&amp;#x5411; stdout &amp;#x6253;&amp;#x51FA;&amp;#x7684;&amp;#x4E00;&amp;#x4E9B;&amp;#x5177;&amp;#x6709;&amp;#x7279;&amp;#x6B8A;&amp;#x542B;&amp;#x4E49;&amp;#x7684;&amp;#x5B57;&amp;#x7B26;&amp;#x4E32;.
 &amp;#x7EC8;&amp;#x7AEF;&amp;#x770B;&amp;#x5230;&amp;#x8FD9;&amp;#x4E9B;&amp;#x4E32;&amp;#x4E4B;&amp;#x540E;&amp;#x4E0D;&amp;#x4F1A;&amp;#x663E;&amp;#x793A;&amp;#x5B83;&amp;#x4EEC;, &amp;#x800C;&amp;#x662F;&amp;#x6267;&amp;#x884C;&amp;#x8FD9;&amp;#x4E9B;&amp;#x4E32;&amp;#x6240;&amp;#x5BF9;&amp;#x5E94;&amp;#x7684;&amp;#x7EC8;&amp;#x7AEF;&amp;#x9AD8;&amp;#x7EA7;&amp;#x529F;&amp;#x80FD;.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Linux" scheme="https://ppwwyyxx.com/blog/tags/Linux/"/>
    
  </entry>
  
  <entry>
    <title>Demystify RAM Usage in Multi-Process Data Loaders</title>
    <link href="https://ppwwyyxx.com/blog/2022/Demystify-RAM-Usage-in-Multiprocess-DataLoader/"/>
    <id>https://ppwwyyxx.com/blog/2022/Demystify-RAM-Usage-in-Multiprocess-DataLoader/</id>
    <published>2022-12-24T08:00:00.000Z</published>
    <updated>2022-12-24T08:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>A typical PyTorch training program on 8 GPUs with 4 dataloaderworkers per GPU would create at least <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="15.965ex" height="2.262ex" role="img" focusable="false" viewbox="0 -750 7056.4 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mn"><path data-c="38" d="M70 417T70 494T124 618T248 666Q319 666 374 624T429 515Q429 485 418 459T392 417T361 389T335 371T324 363L338 354Q352 344 366 334T382 323Q457 264 457 174Q457 95 399 37T249 -22Q159 -22 101 29T43 155Q43 263 172 335L154 348Q133 361 127 368Q70 417 70 494ZM286 386L292 390Q298 394 301 396T311 403T323 413T334 425T345 438T355 454T364 471T369 491T371 513Q371 556 342 586T275 624Q268 625 242 625Q201 625 165 599T128 534Q128 511 141 492T167 463T217 431Q224 426 228 424L286 386ZM250 21Q308 21 350 55T392 137Q392 154 387 169T375 194T353 216T330 234T301 253T274 270Q260 279 244 289T218 306L210 311Q204 311 181 294T133 239T107 157Q107 98 150 60T250 21Z"/></g><g data-mml-node="mo" transform="translate(722.2,0)"><path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"/></g><g data-mml-node="mo" transform="translate(1722.4,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mn" transform="translate(2111.4,0)"><path data-c="34" d="M462 0Q444 3 333 3Q217 3 199 0H190V46H221Q241 46 248 46T265 48T279 53T286 61Q287 63 287 115V165H28V211L179 442Q332 674 334 675Q336 677 355 677H373L379 671V211H471V165H379V114Q379 73 379 66T385 54Q393 47 442 46H471V0H462ZM293 211V545L74 212L183 211H293Z"/></g><g data-mml-node="mo" transform="translate(2833.7,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"/></g><g data-mml-node="mn" transform="translate(3833.9,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g><g data-mml-node="mo" transform="translate(4333.9,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(5000.7,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"/></g><g data-mml-node="mn" transform="translate(6056.4,0)"><path data-c="34" d="M462 0Q444 3 333 3Q217 3 199 0H190V46H221Q241 46 248 46T265 48T279 53T286 61Q287 63 287 115V165H28V211L179 442Q332 674 334 675Q336 677 355 677H373L379 671V211H471V165H379V114Q379 73 379 66T385 54Q393 47 442 46H471V0H462ZM293 211V545L74 212L183 211H293Z"/><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z" transform="translate(500,0)"/></g></g></g></svg></mjx-container> processes.A naive use of PyTorch dataset and dataloader can easily<strong>replicate your dataset&apos;s RAM usage by 40 times</strong>. This issue has probably affected everyone who has done anything nontrivial with PyTorch.In this post, we will explain why it happens, and how to <strong>avoid the 40x RAM usage</strong>.</p><span id="more"></span><p>All code examples and experiment results are available on github at <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader" aria-label="ppwwyyxx/RAM-multiprocess-dataloader: Demystify RAM Usage in Multi-process Dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">ppwwyyxx/RAM-multiprocess-dataloader</a>.The content is not specific to PyTorch: it applies to any user of Python&apos;s multiprocessing library on Linux.</p><h2 id="Motivation-for-In-RAM-Data">Motivation for In-RAM Data<a class="markdown-anchor" href="#Motivation-for-In-RAM-Data">&#xB6;</a></h2><p>Datasets for machine learning are usually not stored in RAM. But it&apos;s common to store their &quot;metadata&quot; in RAM, and this may still cause nontrivial RAM usage. The metadata could be:</p><ul><li>For ImageNet dataset: A million file names and their labels.</li><li>For COCO dataset: 100k file names and their bounding boxes, segmentations, etc.</li></ul><p>As a concrete case, loading the metadata of COCO training set into Python takes ~2.4G of RAM:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># Download from https://huggingface.co/datasets/merve/coco/resolve/main/annotations/instances_train2017.json</span><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">create_coco</span>() -&gt; <span class="hljs-built_in">list</span>[<span class="hljs-type">Any</span>]:<br>  <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(<span class="hljs-string">&quot;instances_train2017.json&quot;</span>) <span class="hljs-keyword">as</span> f:<br>    obj = json.load(f)<br>    <span class="hljs-keyword">return</span> obj[<span class="hljs-string">&quot;annotations&quot;</span>]<br></code></pre></td></tr></table></figure><p>We obviously don&apos;t want to replicate this 2.4G of RAM across all processes.</p><h3 id="In-RAM-metadata-is-needed-for-flexibility">In-RAM metadata is needed for flexibility<a class="markdown-anchor" href="#In-RAM-metadata-is-needed-for-flexibility">&#xB6;</a></h3><p>We acknowledge that there are ways to offload these metadata to disk. For example, people sometimes do:</p><ul><li>Store all the metadata together with raw data on disk, so metadata are not stored in RAM.</li><li>Read sequentially from a combined single-file dataset, so that file names or indices are not stored in RAM.</li></ul><p>By doing these, the RAM usage of a dataset becomes negligible. However, these methods will sacrifice flexibility and capabilities, such as random-access, perfect shuffle, merging datasets arbitrarily, custom subsampling support, etc.Notably, PyTorch&apos;s commonly used <a href="https://pytorch.org/docs/stable/data.html#map-style-datasets" aria-label="torch.utils.data &#x2014; PyTorch 1.13 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">map-style datasets</a> supportrandom access &amp; sampling.All of these capabilities require certain metadata in RAM.</p><p>This article ignores any of these offloading methods. Instead, we&apos;ll discuss how to reduce the RAM usage without moving these data out of RAM. The idea is simple: we&apos;ll try to let all processes <strong>share a single copy of the dataset</strong>.</p><h2 id="Measure-RAM-Usage">Measure RAM Usage<a class="markdown-anchor" href="#Measure-RAM-Usage">&#xB6;</a></h2><p>First let&apos;s build tools to measure RAM usage - which is not as easy as it sounds.</p><p>Common tools like <code>top -p PID</code> or <code>psutil.Process(PID).memory_info()</code> obtains memory statistics from <code>/proc/{PID}/statm</code> or <code>/proc/{PID}/status</code>, but they are insufficient for our analysis. Instead, we&apos;ll use the information provided in</p><ul><li><code>/proc/{PID}/smaps</code>: per-memory-mapping RAM usage information, documented inthis <a href="https://man7.org/linux/man-pages/man5/proc.5.html#:~:text=user_namespaces(7).-,/proc/%5Bpid%5D/smaps,-(since%20Linux%202.6.14" aria-label="proc(5) - Linux manual page" class="hint--top hint--rounded hint--no-animate hint--no-arrow">man page</a></li><li><code>/proc/{PID}/smaps_rollup</code>: aggregation of data from <code>smaps</code></li></ul><p>We&apos;ll derive the following important measurements from it:</p><ul><li><a href="https://en.wikipedia.org/wiki/Unique_set_size" aria-label="Unique set size - Wikipedia" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><strong>USS</strong> (Unique Set Size)</a>: RAM that&apos;s unique/private to this process, i.e. not shared with any other process. This is obtained by the sum of &quot;private_*&quot; entries in <code>smaps</code>.</li><li><strong>Shared</strong>: RAM in this process that&apos;s also shared with other processes. This is obtained by the sum of &quot;shared_*&quot; entries in <code>smaps</code>.<ul><li>Note that this is not equal to the &quot;shared&quot; column in <code>/proc/{PID}/statm</code> or <code>top</code>, which uses different accounting.</li></ul></li><li><strong>Shared_File</strong>: RAM that&apos;s shared with other processes through files. It is no larger than &quot;Shared&quot;.</li><li><a href="https://en.wikipedia.org/wiki/Resident_set_size" aria-label="Resident set size - Wikipedia" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><strong>RSS</strong> (Resident Set Size)</a>: All memory that this process holds in RAM. <strong>RSS = USS + Shared</strong>.</li><li><a href="https://en.wikipedia.org/wiki/Proportional_set_size" aria-label="Proportional set size - Wikipedia" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><strong>PSS</strong> (Proportional Set Size)</a>: Like RSS, but it avoids overcounting shared memory multiple times across all processes that are sharing it. It&apos;s basically &quot;USS + Shared / (number of processes sharing it)&quot;. By definition, we should <strong>use total PSS to count the total RAM usage</strong> of N processes.</li></ul><p>To obtain these measurements, we use <code>psutil.Process(PID).memory_maps()</code> which parses <code>smaps</code> under the hood:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">get_mem_info</span>(<span class="hljs-params">pid: <span class="hljs-built_in">int</span></span>) -&gt; <span class="hljs-built_in">dict</span>[<span class="hljs-built_in">str</span>, <span class="hljs-built_in">int</span>]:<br>  res = defaultdict(<span class="hljs-built_in">int</span>)<br>  <span class="hljs-keyword">for</span> mmap <span class="hljs-keyword">in</span> psutil.Process(pid).memory_maps():<br>    res[<span class="hljs-string">&apos;rss&apos;</span>] += mmap.rss<br>    res[<span class="hljs-string">&apos;pss&apos;</span>] += mmap.pss<br>    res[<span class="hljs-string">&apos;uss&apos;</span>] += mmap.private_clean + mmap.private_dirty<br>    res[<span class="hljs-string">&apos;shared&apos;</span>] += mmap.shared_clean + mmap.shared_dirty<br>    <span class="hljs-keyword">if</span> mmap.path.startswith(<span class="hljs-string">&apos;/&apos;</span>):  <span class="hljs-comment"># looks like a file path</span><br>      res[<span class="hljs-string">&apos;shared_file&apos;</span>] += mmap.shared_clean + mmap.shared_dirty<br>  <span class="hljs-keyword">return</span> res<br></code></pre></td></tr></table></figure><p>Then we create a <code>MemoryMonitor</code> utility to measure and print the results for a list of PIDs.The code is straightforward and can be found <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/common.py" aria-label="common.py &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>.</p><h2 id="Copy-on-read-Overhead-and-Memory-Leak">Copy-on-read Overhead and &quot;Memory Leak&quot;<a class="markdown-anchor" href="#Copy-on-read-Overhead-and-Memory-Leak">&#xB6;</a></h2><p>We start with a naive implementation of a dataset that produces itemsfrom a list:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">NaiveDatasetFromList</span>(torch.utils.data.Dataset):<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, lst</span>):<br>    <span class="hljs-variable language_">self</span>.lst = lst<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__len__</span>(<span class="hljs-params">self</span>):<br>    <span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(<span class="hljs-variable language_">self</span>.lst)<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__getitem__</span>(<span class="hljs-params">self, idx: <span class="hljs-built_in">int</span></span>):<br>    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>.lst[idx]<br></code></pre></td></tr></table></figure><p>Then we launch subprocesses to read from this dataset with the list of COCO data. To make a cleaner demo, we don&apos;t use PyTorch&apos;s dataloader, but just launch 4 subprocesses by ourselves:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">worker</span>(<span class="hljs-params">_, dataset: torch.utils.data.Dataset</span>):<br>  <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:<br>    <span class="hljs-keyword">for</span> sample <span class="hljs-keyword">in</span> dataset:<br>      <span class="hljs-comment"># read the data, with a fake latency</span><br>      time.sleep(<span class="hljs-number">0.000001</span>)<br>      result = pickle.dumps(sample)<br><br><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">&quot;__main__&quot;</span>:<br>  ds = NaiveDatasetFromList(create_coco())<br>  ctx = torch.multiprocessing.start_processes(<br>      worker, (ds, ), nprocs=<span class="hljs-number">4</span>, join=<span class="hljs-literal">False</span>, daemon=<span class="hljs-literal">True</span>, start_method=<span class="hljs-string">&apos;fork&apos;</span>)<br></code></pre></td></tr></table></figure><p>We then added our <code>MemoryMonitor</code> to it. The full <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/main-naive.py" aria-label="main-naive.py &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">code</a> and its <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/main-naive-log.txt" aria-label="main-naive-log.txt &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">output logs</a> are available on github. Each segment in the log contains memory measurements for the main process + 4 workers:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ ./main-naive.py<br>  time     PID  rss    pss    uss    shared    shared_file<br>------  ------  -----  -----  -----  --------  -------------<br> 34724  791339  2.7G   2.0G   1.8G   993.8M    163.5M<br> 34724  791625  2.6G   1.9G   1.8G   848.6M    16.4M<br> 34724  791626  2.6G   1.9G   1.8G   848.6M    16.4M<br> 34724  791627  2.6G   1.9G   1.8G   848.6M    16.4M<br> 34724  791628  2.6G   1.9G   1.8G   848.8M    16.5M<br></code></pre></td></tr></table></figure><p>The code above looks completely innocent, don&apos;t you think? However, if we plot the memoryusage of any dataloader worker over time, we seem to find a memory leak!This is the notorious &quot;dataloader leaks memory&quot; issue that is discussed at multiple places, e.g. <a href="https://github.com/pytorch/pytorch/issues/13246" aria-label="DataLoader num_workers &gt; 0 causes CPU memory from parent process to be replicated in all worker processes &#xB7; Issue #13246 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">this PyTorch issue</a> and <a href="https://pytorch-dev-podcast.simplecast.com/episodes/dataloader-with-multiple-workers-leaks-memory">Edward&apos;s podcast</a>.</p><img src="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/raw/main/main-naive.jpg" class="center" width="700"><p>In fact, the growth of RAM usage does stop in the end, so this issue is not a memory leak. But in reality, users often do not see the end before the system OOMs, and they may wrongly conclude this as a &quot;memory leak&quot;.</p><p>The root cause of this issue is &quot;copy-on-read&quot; of forked CPython objects.</p><h3 id="Copy-on-read-of-forked-CPython-objects">Copy-on-read of forked CPython objects<a class="markdown-anchor" href="#Copy-on-read-of-forked-CPython-objects">&#xB6;</a></h3><p>Linux has a <a href="https://en.wikipedia.org/wiki/Copy-on-write" aria-label="Copy-on-write - Wikipedia" class="hint--top hint--rounded hint--no-animate hint--no-arrow">copy-on-write</a> mechanism: when a process forks, the child process will share its entire memory space with the parent, and only copy the relevant pages when necessary, i.e. when the child process needs to write to the page. This mechanism allows read-only pages to be shared to reduce total memory usage.</p><p>The copy-on-write behavior can be clearly observed in the above figure:at time=0, the worker has 2.6G of shared RAM, 0 USS (memory unique to the worker), and <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.816ex;" xmlns="http://www.w3.org/2000/svg" width="4.296ex" height="2.835ex" role="img" focusable="false" viewbox="0 -892.5 1898.8 1253.1"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mrow" transform="translate(220,394) scale(0.707)"><g data-mml-node="mn"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="36" d="M42 313Q42 476 123 571T303 666Q372 666 402 630T432 550Q432 525 418 510T379 495Q356 495 341 509T326 548Q326 592 373 601Q351 623 311 626Q240 626 194 566Q147 500 147 364L148 360Q153 366 156 373Q197 433 263 433H267Q313 433 348 414Q372 400 396 374T435 317Q456 268 456 210V192Q456 169 451 149Q440 90 387 34T253 -22Q225 -22 199 -14T143 16T92 75T56 172T42 313ZM257 397Q227 397 205 380T171 335T154 278T148 216Q148 133 160 97T198 39Q222 21 251 21Q302 21 329 59Q342 77 347 104T352 209Q352 289 347 316T329 361Q302 397 257 397Z" transform="translate(778,0)"/></g><g data-mml-node="mtext" transform="translate(1278,0)"><path data-c="47" d="M56 342Q56 428 89 500T174 615T283 681T391 705Q394 705 400 705T408 704Q499 704 569 636L582 624L612 663Q639 700 643 704Q644 704 647 704T653 705H657Q660 705 666 699V419L660 413H626Q620 419 619 430Q610 512 571 572T476 651Q457 658 426 658Q401 658 376 654T316 633T254 592T205 519T177 411Q173 369 173 335Q173 259 192 201T238 111T302 58T370 31T431 24Q478 24 513 45T559 100Q562 110 562 160V212Q561 213 557 216T551 220T542 223T526 225T502 226T463 227H437V273H449L609 270Q715 270 727 273H735V227H721Q674 227 668 215Q666 211 666 108V6Q660 0 657 0Q653 0 639 10Q617 25 600 42L587 54Q571 27 524 3T406 -22Q317 -22 238 22T108 151T56 342Z"/></g></g><g data-mml-node="mn" transform="translate(772.6,-345) scale(0.707)"><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z"/></g><rect width="1658.8" height="60" x="120" y="220"/></g></g></g></svg></mjx-container> of PSS because the RAM is shared among 5 processes (4 workers + 1 main).</p><p>However, this mechanism did not help us when we read our dataset. The problemis that our dataset is a large nested data structure that contains many smallPython objects. Even though the dataset is &quot;read-only&quot; in theory, <strong>accessingany Python object will increment its refcount</strong> - causing a lot of memorywrites. With these writes, memory can no longer be shared among parent andchild processes. In other words, objects are not only copy-on-write, but also copy-on-read.Therefore, in the figure we see that the &quot;Shared&quot; RAM decreases and &quot;USS&quot; increases,since many pages are copied from shared memory into each process.</p><p>The end game is that each child process has to replicate all the pages that contain object refcounts in the dataset. For a dataset with many objects, this is almost the size of the dataset itself. In the output log, we see that this program uses<a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/795868a37446d61412b9a58dbb1b7c76e75d39c4/main-naive-log.txt#L578-L584" aria-label="main-naive-log.txt &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">10G total PSS in the end</a>,where each child process replicates 1.8G of USS.</p><h2 id="Serialize-to-a-Numpy-Array">Serialize to a Numpy Array<a class="markdown-anchor" href="#Serialize-to-a-Numpy-Array">&#xB6;</a></h2><p>The copy-on-read issue is due to CPython&apos;s reference counting.There are ways to change CPython&apos;s behavior, e.g. by <a href="https://docs.python.org/3/library/gc.html#gc.freeze" aria-label="gc &#x2014; Garbage Collector interface &#x2014; Python 3.11.1 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>gc.freeze</code></a>, but it has far-reaching consequences and I failed to make it work for the example here. However, there is a simple and transparent way to solve the issue: <strong>store the dataset with very few number of Python objects</strong>, so there are very few refcounts!Below is a minimal implementation that stores a listusing 2 numpy arrays:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">NumpySerializedList</span>:<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, lst: <span class="hljs-built_in">list</span>[<span class="hljs-type">Any</span>]</span>):<br>    lst = [np.frombuffer(pickle.dumps(x), dtype=np.uint8) <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> lst]<br>    <span class="hljs-variable language_">self</span>._addr = np.cumsum([<span class="hljs-built_in">len</span>(x) <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> lst])<br>    <span class="hljs-variable language_">self</span>._lst = np.concatenate(lst)<br><br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__len__</span>(<span class="hljs-params">self</span>):<br>    <span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(<span class="hljs-variable language_">self</span>._addr)<br><br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__getitem__</span>(<span class="hljs-params">self, idx: <span class="hljs-built_in">int</span></span>):<br>    start = <span class="hljs-number">0</span> <span class="hljs-keyword">if</span> idx == <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> <span class="hljs-variable language_">self</span>._addr[idx - <span class="hljs-number">1</span>]<br>    end = <span class="hljs-variable language_">self</span>._addr[idx]<br>    <span class="hljs-keyword">return</span> pickle.loads(<span class="hljs-built_in">memoryview</span>(<span class="hljs-variable language_">self</span>._lst[start:end]))<br></code></pre></td></tr></table></figure><p>Detectron2 enables this type of serialization by default (since <a href="https://github.com/facebookresearch/detectron2/commit/0cd0e7253f64fc40e9c2ee4617df6e4760d07974" aria-label="share RAM usage for data loader workers &#xB7; facebookresearch/detectron2@0cd0e72" class="hint--top hint--rounded hint--no-animate hint--no-arrow">this commit</a> by Yanghan). To compare different serialization mechanisms,we borrow its code into <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/795868a37446d61412b9a58dbb1b7c76e75d39c4/serialize.py#L19-L43" aria-label="serialize.py &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">a serialization util</a>, and use it here:</p><figure class="highlight diff"><table><tr><td class="code"><pre><code class="hljs diff"><span class="hljs-deletion">- ds = NaiveDatasetFromList(create_coco())</span><br><span class="hljs-addition">+ from serialize import NumpySerializedList</span><br><span class="hljs-addition">+ ds = NaiveDatasetFromList(NumpySerializedList(create_coco())</span><br></code></pre></td></tr></table></figure><p>Just by this simple one-line change, the RAM usage greatly reduces. The end of the <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/main-fork-numpyserialize-log.txt" aria-label="main-fork-numpyserialize-log.txt &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">output log file</a> is shown below.</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ ./main-numpyserialize.py<br>    PID  rss    pss     uss    shared    shared_file<br> ------  -----  ------  -----  --------  -------------<br> 877767  1.6G   396.3M  20.2M  1.6G      184.8M<br> 877901  1.5G   306.5M  3.8M   1.5G      22.3M<br> 877902  1.5G   306.5M  3.7M   1.5G      22.3M<br> 877903  1.5G   306.6M  3.9M   1.5G      22.3M<br> 877904  1.5G   306.4M  3.6M   1.5G      22.3M<br></code></pre></td></tr></table></figure><p>We can see that:</p><ul><li>The total PSS usage is only 1.6G -- a 6x RAM reduction.</li><li>All processes have almost 0 USS, which means everything is shared! In fact, from the logs we can see that 1.6G is exactly the memory usage of the main process before starting subprocesses. Subprocesses add no extra memory usage.</li><li>The 6x reduction factor is better than <code>#processes</code> because <code>pickle.dumps</code> notonly serializes but also compresses the data. We benefit from <strong>both sharingand compression</strong> by applying this optimization, at the cost of a tiny<code>pickle.loads</code> overhead in each access.</li></ul><h3 id="More-on-compression-not-important">More on compression (not important)<a class="markdown-anchor" href="#More-on-compression-not-important">&#xB6;</a></h3><p>Actually, after compression, the dataset only takes ~500M (printed at the beginning of log). So a question arises: why does the main process use 1.6G RAM before starting subprocesses?</p><p>This is in fact just an artifact of modern memory allocators: it does not always release memory back to the OS. In fact, if we run this simple serialization/compression code:</p>  <figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">monitor = MemoryMonitor()<br><span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Initial&quot;</span>, monitor.<span class="hljs-built_in">str</span>())<br>lst = create_coco()<br><span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;JSON&quot;</span>, monitor.<span class="hljs-built_in">str</span>())<br>lst = NumpySerializedList(lst)<br><span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Serialized&quot;</span>, monitor.<span class="hljs-built_in">str</span>())<br><span class="hljs-keyword">del</span> lst; <span class="hljs-keyword">import</span> gc; gc.collect()<br><span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;End&quot;</span>, monitor.<span class="hljs-built_in">str</span>())<br></code></pre></td></tr></table></figure><p>We see that we seem to &quot;lose&quot; ~700MB of RAM even after we&apos;ve deleted everything:</p>  <figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">Initial PID=1156792, rss=328.7M, pss=238.7M, uss=161.4M, shared=167.3M, shared_file=167.3M<br>JSON PID=1156792, rss=2.8G, pss=2.7G, uss=2.6G, shared=167.3M, shared_file=167.3M<br>Serialized PID=1156792, rss=1.6G, pss=1.5G, uss=1.5G, shared=167.3M, shared_file=167.3M<br>End PID=1156792, rss=1.1G, pss=1.0G, uss=986.2M, shared=167.3M, shared_file=167.3M<br></code></pre></td></tr></table></figure><p>Using a better allocator, e.g. by <code>export LD_PRELOAD=libjemalloc.so</code>, can make this issue largely disappear.</p><p>This artifact is typically not a big concern, since allocators will find opportunities to reuse these free buffers.<small>(Well, they may be concerning in <code>start_method=&quot;fork&quot;</code> because reusing these free buffers may trigger copy-on-write!But I&apos;m not going to talk more about that.) </small></p><h2 id="Pickle-Overhead-in-Spawn-Forkserver">Pickle Overhead in Spawn / Forkserver<a class="markdown-anchor" href="#Pickle-Overhead-in-Spawn-Forkserver">&#xB6;</a></h2><p>In our code above, we launched subprocesses using a <code>start_method=&quot;fork&quot;</code> argument.&quot;fork, spawn, forkserver&quot; are the 3 &quot;start methods&quot; of Python&apos;s multiprocessing library. <a href="https://bnikolic.co.uk/blog/python/parallelism/2019/11/13/python-forkserver-preload.html" aria-label="Python forkserver and set_forkserver_preload() | B. Nikolic Software and Computing Blog" class="hint--top hint--rounded hint--no-animate hint--no-arrow">This article</a> is a good reference that explains their differences.</p><p>Since <code>start_method=&quot;fork&quot;</code> is unsafe (in practice, it causes various crashes &amp; deadlocks) and might <a href="https://discuss.python.org/t/switching-default-multiprocessing-context-to-spawn-on-posix-as-well/" aria-label="Switching default multiprocessing context to &quot;spawn&quot; on POSIX as well - Core Development - Discussions on Python.org" class="hint--top hint--rounded hint--no-animate hint--no-arrow">no longer be the default</a> in the future, we want to rerun our code above with <code>start_method=&quot;spawn&quot;</code> or <code>&quot;forkserver&quot;</code>. Sadly, the serialized array is no longer shared among workers. Each worker has a large USS:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ ./main-numpyserialize.py spawn<br>     PID  rss     pss     uss     shared    shared_file<br> -------  ------  ------  ------  --------  -------------<br> 1177291  1.6G    1.5G    1.5G    168.7M    168.7M<br> 1177405  840.8M  698.3M  672.1M  168.7M    168.7M<br> 1177419  840.9M  698.3M  672.1M  168.8M    168.8M<br> 1177443  840.7M  698.3M  672.1M  168.6M    168.6M<br> 1177456  840.6M  698.5M  672.2M  168.4M    168.4M<br></code></pre></td></tr></table></figure><p>The reason why our trick no longer works is that &quot;spawn&quot; and &quot;forkserver&quot; don&apos;t benefit from the copy-on-write mechanism. They will start a &quot;fresh&quot; subprocess with fresh memory space, instead of sharing with the parent. Everything the child process needs to access is pickled in the parent process and sent to the child. This ensures safe behavior, but is bad for start-up speed and memory usage.</p><p>In our case, the entire dataset will be pickled and sent to child processes. This is why each child process consumes a large USS.</p><h3 id="Serialize-to-a-torch-Tensor">Serialize to a <code>torch.Tensor</code><a class="markdown-anchor" href="#Serialize-to-a-torch-Tensor">&#xB6;</a></h3><p>It turns out there is a simple fix to this problem: just <strong>store the serialized dataset in a <code>torch.Tensor</code> instead of a numpy array</strong>. The reason why it works, is that multiprocessing uses a customizable pickle implementation called <a href="https://github.com/python/cpython/blob/3c033a2e6fbde56f904aeca138141be6564341cf/Lib/multiprocessing/reduction.py#L33" aria-label="reduction.py &#xB7; python/cpython" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>ForkingPickler</code></a>, and PyTorch <a href="https://github.com/pytorch/pytorch/blob/fd3a7264ae80332ba5ec8f60446e6d7a2c2276c1/torch/multiprocessing/reductions.py#L379-L395" aria-label="reductions.py &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">customizes</a> how <code>torch.Tensor</code> should be pickled by it: the tensor data will not be serialized to bytes. Instead, during pickling the tensor will be moved to shared memory files (typically under <code>/dev/shm</code>) to be accessed by other processes directly.</p><p>To test tensor-based serialization, we run <code>./main-torchserialize.py spawn</code> using the code <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/main-torchserialize.py" aria-label="main-torchserialize.py &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>, and observes the following memory usage in workers (raw log is <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/main-spawn-torchserialize-log.txt" aria-label="main-spawn-torchserialize-log.txt &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>):</p><img src="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/raw/main/main-spawn-torchserialize.jpg" class="center" width="700"><ul><li>&quot;Shared_File&quot; grows because workers will load from the shared <code>torch.Tensor</code> as needed. This is different from <code>start_method=&quot;fork&quot;</code> where the entire memory space is shared at the beginning.</li><li>&quot;Shared_File&quot; stops growing when the worker has accessed the entire shared tensor.</li><li>The size of &quot;Shared_File&quot; in the end is roughly 500M(size of serialized dataset) + 170M, where 170M is thesize of all the binary files that <code>import torch</code> needs to load such as <code>libtorch.so</code>.This can be easily verified by printing the measurements after <code>import torch</code>.</li></ul><p>After applying tensor-based serialization,the total PSS usage <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/795868a37446d61412b9a58dbb1b7c76e75d39c4/main-spawn-torchserialize-log.txt#L594-L600" aria-label="main-spawn-torchserialize-log.txt &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">in the end is 2.2G</a>-- still worse than our earlier number using <code>start_method=&quot;fork&quot;</code>.Next section will optimize it further.</p><h2 id="Per-Process-Import-Overhead">Per-Process Import Overhead<a class="markdown-anchor" href="#Per-Process-Import-Overhead">&#xB6;</a></h2><p>The last culprit in the above experiment is the 160MBper-worker USS in the above figure: this is just the memory footprint of <code>import torch</code>,mainly for PyTorch&apos;s global variables, etc. Since every child process launched by &quot;spawn / forkserver&quot; is a &quot;fresh&quot; one, they all need to <code>import torch</code> independently, hence each has 160MB of USS.</p><p>Luckily, &quot;forkserver&quot; provides a way to share the <code>import torch</code> RAM usage through copy-on-write. By calling the undocumented Python API <code>multiprocessing.set_forkserver_preload([&quot;torch&quot;])</code> before launching processes, each child process will be &quot;less fresh&quot;: the torch library is preloaded (and shared), and don&apos;t need to be imported by each process independently.</p><p>Below are the experiment results. Code and <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/main-forkserver-torchserialize-log.txt" aria-label="main-forkserver-torchserialize-log.txt &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">full logs</a> are on github:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ ./main-torchserialize.py forkserver<br>     PID  rss     pss     uss     shared    shared_file<br> -------  ------  ------  ------  --------  -------------<br> 1204121  1.6G    1.1G    988.6M  681.5M    681.5M<br> 1204230  707.7M  152.1M  16.9M   690.9M    559.5M<br> 1204231  707.7M  152.2M  16.9M   690.9M    559.5M<br> 1204232  707.7M  152.1M  16.8M   690.9M    559.5M<br> 1204233  707.7M  152.1M  16.8M   691.0M    559.5M<br></code></pre></td></tr></table></figure><ul><li>The total PSS is only 1.7G, which is roughly the same as our best number using <code>start_method=&quot;fork&quot;</code>.</li><li>The USS of each worker is negligible, which means we&apos;ve successfully shared everything. There is <strong>no per-worker memory overhead</strong> anymore.</li></ul><p>(Note that this optimization may be unsafe if <code>import torch</code> creates any threads.My observation is that threads are indeed created due to <code>import numpy</code> inside torch, but they can be disabled with environment variables.)</p><h2 id="Share-Datasets-among-Multiple-GPU-Processes">Share Datasets among Multiple GPU Processes<a class="markdown-anchor" href="#Share-Datasets-among-Multiple-GPU-Processes">&#xB6;</a></h2><p>So far we&apos;ve only looked at a single dataloader (with 4 workers). In reality, the only scalable way to use PyTorch on multiple GPUs is to use one process per GPU, each will have its own dataloader and dataloader workers. This gives a total of <code>#GPUs x (#DL workers + 1)</code> processes organized like below:</p><img src="/blog/2022/Demystify-RAM-Usage-in-Multiprocess-DataLoader/workers.svg" class="center" width="500"><p>We modified the previous experiment slightly into <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/main-multigpu-naive.py" aria-label="main-multigpu-naive.py &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">this code</a> to run on 2 GPUs. The memory usage looks like this:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ ./main-multigpu-naive.py<br>    PID  rss     pss     uss      shared    shared_file<br>-------  ------  ------  -------  --------  -------------<br>1495766  1.7G    1.1G    1017.0M  694.2M    694.2M        # GPU worker 0<br>1495938  757.7M  198.5M  67.8M    689.9M    580.7M<br>1495939  757.6M  198.5M  67.8M    689.8M    580.6M<br>1495940  757.7M  198.5M  67.8M    689.9M    580.6M<br>1495941  757.7M  198.5M  67.8M    689.9M    580.7M<br>1495767  1.7G    1.1G    1015.9M  693.9M    693.9M        # GPU worker 1<br>1495934  757.9M  198.5M  67.7M    690.1M    580.8M<br>1495935  757.7M  198.4M  67.7M    690.0M    580.6M<br>1495936  757.7M  198.4M  67.7M    690.0M    580.6M<br>1495937  757.9M  198.4M  67.7M    690.2M    580.8M<br></code></pre></td></tr></table></figure><p>Our previous optimization on dataloader workers is still effective - dataloader workers have a tiny USS. However, RAM usage is now replicated by #GPUs times because we let each GPU worker read the dataset independently.</p><p>An inconvenient solution to this problem is to load and serialize the dataset <em>before</em> launching GPU workers. By doing this, all GPU workers share the dataset just like what dataloader workers do. However, this limits flexibility and often requires significant refactoring, due to reasons such as:</p><ul><li>Dataset would have to be made ready much earlier than usual</li><li>Per-GPU data loading logic (e.g. sharding) may need to be modified</li><li>Most launchers (e.g. <a href="https://pytorch.org/docs/stable/elastic/run.html" aria-label="torchrun (Elastic Launch) &#x2014; PyTorch 1.13 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">torchrun</a>, <a href="https://github.com/huggingface/accelerate" aria-label="huggingface/accelerate: &#x1F680; A simple way to train and use PyTorch models with multi-GPU, TPU, mixed-precision" class="hint--top hint--rounded hint--no-animate hint--no-arrow">accelerate</a>) don&apos;t support this at all</li></ul><p>Another simple solution to this problem is again to use <code>torch.Tensor</code> and <code>ForkingPickler</code> to share the dataset among GPU workers, except that now we need tomanage the sharing explicitly like this:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">if</span> comm.get_local_rank() == <span class="hljs-number">0</span>:  <span class="hljs-comment"># GPU0 reads data and moves it to shared memory.</span><br>    <span class="hljs-comment"># Move data to shared memory, obtain a handle to send to each local worker.</span><br>    handles = [<span class="hljs-literal">None</span>] + [<br>      <span class="hljs-built_in">bytes</span>(mp.reduction.ForkingPickler.dumps(tensor_dataset))<br>      <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-built_in">range</span>(comm.get_local_size() - <span class="hljs-number">1</span>)]<br><span class="hljs-keyword">else</span>:<br>    handles = <span class="hljs-literal">None</span><br><span class="hljs-comment"># Each GPU receives its handle from GPU0.</span><br>handle = local_scatter(handles)<br><br><span class="hljs-keyword">if</span> comm.get_local_rank() &gt; <span class="hljs-number">0</span>:<br>    <span class="hljs-comment"># Materialize a tensor from shared memory.</span><br>    tensor_dataset = ForkingPickler.loads(handle)<br></code></pre></td></tr></table></figure><p>This logic is implemented as another serialization util<a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/795868a37446d61412b9a58dbb1b7c76e75d39c4/serialize.py#L82-L101" aria-label="serialize.py &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>.When using it as a drop-in replacement (<a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/main-multigpu-sharedmem.py" aria-label="main-multigpu-sharedmem.py &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">full code here</a>),the dataset is no longer replicated by GPU workers:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">$ ./main-multigpu-sharedmem.py<br>    PID  rss     pss     uss      shared    shared_file<br>-------  ------  ------  -------  --------  -------------<br>1533910  1.7G    1.1G    1015.4M  693.4M    693.4M     # GPU worker 0<br>1534032  757.9M  152.9M  67.9M    690.0M    580.8M<br>1534033  757.9M  152.9M  67.9M    690.0M    580.8M<br>1534034  757.9M  152.9M  67.9M    690.0M    580.8M<br>1534035  757.9M  152.9M  67.9M    690.0M    580.8M<br>1533911  374.2M  220.0M  192.6M   181.6M    181.6M     # GPU worker 1<br>1534036  757.8M  152.7M  67.7M    690.1M    580.7M<br>1534037  757.8M  152.7M  67.6M    690.1M    580.7M<br>1534038  757.8M  152.7M  67.6M    690.2M    580.7M<br>1534039  757.8M  152.7M  67.6M    690.2M    580.7M<br></code></pre></td></tr></table></figure><p>GPU worker 1 still has a small amount of extra USS, and that&apos;s just the footprint of <code>import torch</code> that we saw earlier, and can be avoided using <code>set_forkserver_preload</code>.</p><p>Note that the <code>multiprocessing</code> library itself also provides shared memory support.<a href="https://github.com/facebookresearch/mobile-vision/pull/120" aria-label="support creating in-memory shared list from worker process by wat3rBro &#xB7; Pull Request #120 &#xB7; facebookresearch/mobile-vision" class="hint--top hint--rounded hint--no-animate hint--no-arrow">This PR</a> contains an implementation of our serialization util without using PyTorch.</p><h2 id="Summary">Summary<a class="markdown-anchor" href="#Summary">&#xB6;</a></h2><p>We&apos;ve successfully reduced the total RAM usage by (approximately) a factor of</p><p><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="63.091ex" height="2.262ex" role="img" focusable="false" viewbox="0 -750 27886.3 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mtext" transform="translate(389,0)"><path data-c="23" d="M56 347Q56 360 70 367H313L355 524Q394 676 401 686Q406 694 416 694Q434 694 436 676Q436 672 396 522Q355 374 355 369L354 367H543L585 524Q626 679 630 685Q636 694 646 694Q653 694 659 689T665 678Q665 668 626 522Q585 374 585 369L584 367H762Q777 359 777 347Q777 334 767 331T722 327H667H572L552 251L531 174Q531 173 647 173H720Q756 173 766 170T777 153T762 133H519L477 -24Q436 -179 432 -185Q426 -194 416 -194Q409 -194 403 -189T397 -177Q397 -167 436 -21Q477 125 477 131L478 133H289L247 -24Q206 -179 202 -185Q196 -194 186 -194Q179 -194 173 -189T167 -177Q167 -167 206 -21Q247 125 247 131L248 133H70Q56 140 56 153Q56 168 72 173H260L280 249L301 326Q301 327 186 327H72Q56 332 56 347ZM531 326Q531 327 437 327H342L322 251L301 174Q301 173 395 173H490L510 249L531 326Z"/><path data-c="64" d="M376 495Q376 511 376 535T377 568Q377 613 367 624T316 637H298V660Q298 683 300 683L310 684Q320 685 339 686T376 688Q393 689 413 690T443 693T454 694H457V390Q457 84 458 81Q461 61 472 55T517 46H535V0Q533 0 459 -5T380 -11H373V44L365 37Q307 -11 235 -11Q158 -11 96 50T34 215Q34 315 97 378T244 442Q319 442 376 393V495ZM373 342Q328 405 260 405Q211 405 173 369Q146 341 139 305T131 211Q131 155 138 120T173 59Q203 26 251 26Q322 26 373 103V342Z" transform="translate(833,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(1389,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(1889,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(2278,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(2778,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(3056,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(3556,0)"/><path data-c="64" d="M376 495Q376 511 376 535T377 568Q377 613 367 624T316 637H298V660Q298 683 300 683L310 684Q320 685 339 686T376 688Q393 689 413 690T443 693T454 694H457V390Q457 84 458 81Q461 61 472 55T517 46H535V0Q533 0 459 -5T380 -11H373V44L365 37Q307 -11 235 -11Q158 -11 96 50T34 215Q34 315 97 378T244 442Q319 442 376 393V495ZM373 342Q328 405 260 405Q211 405 173 369Q146 341 139 305T131 211Q131 155 138 120T173 59Q203 26 251 26Q322 26 373 103V342Z" transform="translate(4056,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(4612,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(5056,0)"/><path data-c="20" d="" transform="translate(5448,0)"/><path data-c="77" d="M90 368Q84 378 76 380T40 385H18V431H24L43 430Q62 430 84 429T116 428Q206 428 221 431H229V385H215Q177 383 177 368Q177 367 221 239L265 113L339 328L333 345Q323 374 316 379Q308 384 278 385H258V431H264Q270 428 348 428Q439 428 454 431H461V385H452Q404 385 404 369Q404 366 418 324T449 234T481 143L496 100L537 219Q579 341 579 347Q579 363 564 373T530 385H522V431H529Q541 428 624 428Q692 428 698 431H703V385H697Q696 385 691 385T682 384Q635 377 619 334L559 161Q546 124 528 71Q508 12 503 1T487 -11H479Q460 -11 456 -4Q455 -3 407 133L361 267Q359 263 266 -4Q261 -11 243 -11H238Q225 -11 220 -3L90 368Z" transform="translate(5698,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(6420,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(6920,0)"/><path data-c="6B" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T97 124T98 167T98 217T98 272T98 329Q98 366 98 407T98 482T98 542T97 586T97 603Q94 622 83 628T38 637H20V660Q20 683 22 683L32 684Q42 685 61 686T98 688Q115 689 135 690T165 693T176 694H179V463L180 233L240 287Q300 341 304 347Q310 356 310 364Q310 383 289 385H284V431H293Q308 428 412 428Q475 428 484 431H489V385H476Q407 380 360 341Q286 278 286 274Q286 273 349 181T420 79Q434 60 451 53T500 46H511V0H505Q496 3 418 3Q322 3 307 0H299V46H306Q330 48 330 65Q330 72 326 79Q323 84 276 153T228 222L176 176V120V84Q176 65 178 59T189 49Q210 46 238 46H254V0H246Q231 3 137 3T28 0H20V46H36Z" transform="translate(7312,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(7840,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(8284,0)"/></g><g data-mml-node="mo" transform="translate(9287.2,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"/></g><g data-mml-node="mn" transform="translate(10287.4,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g><g data-mml-node="mo" transform="translate(10787.4,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(11398.7,0)"><path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"/></g><g data-mml-node="mtext" transform="translate(12398.9,0)"><path data-c="23" d="M56 347Q56 360 70 367H313L355 524Q394 676 401 686Q406 694 416 694Q434 694 436 676Q436 672 396 522Q355 374 355 369L354 367H543L585 524Q626 679 630 685Q636 694 646 694Q653 694 659 689T665 678Q665 668 626 522Q585 374 585 369L584 367H762Q777 359 777 347Q777 334 767 331T722 327H667H572L552 251L531 174Q531 173 647 173H720Q756 173 766 170T777 153T762 133H519L477 -24Q436 -179 432 -185Q426 -194 416 -194Q409 -194 403 -189T397 -177Q397 -167 436 -21Q477 125 477 131L478 133H289L247 -24Q206 -179 202 -185Q196 -194 186 -194Q179 -194 173 -189T167 -177Q167 -167 206 -21Q247 125 247 131L248 133H70Q56 140 56 153Q56 168 72 173H260L280 249L301 326Q301 327 186 327H72Q56 332 56 347ZM531 326Q531 327 437 327H342L322 251L301 174Q301 173 395 173H490L510 249L531 326Z"/><path data-c="47" d="M56 342Q56 428 89 500T174 615T283 681T391 705Q394 705 400 705T408 704Q499 704 569 636L582 624L612 663Q639 700 643 704Q644 704 647 704T653 705H657Q660 705 666 699V419L660 413H626Q620 419 619 430Q610 512 571 572T476 651Q457 658 426 658Q401 658 376 654T316 633T254 592T205 519T177 411Q173 369 173 335Q173 259 192 201T238 111T302 58T370 31T431 24Q478 24 513 45T559 100Q562 110 562 160V212Q561 213 557 216T551 220T542 223T526 225T502 226T463 227H437V273H449L609 270Q715 270 727 273H735V227H721Q674 227 668 215Q666 211 666 108V6Q660 0 657 0Q653 0 639 10Q617 25 600 42L587 54Q571 27 524 3T406 -22Q317 -22 238 22T108 151T56 342Z" transform="translate(833,0)"/><path data-c="50" d="M130 622Q123 629 119 631T103 634T60 637H27V683H214Q237 683 276 683T331 684Q419 684 471 671T567 616Q624 563 624 489Q624 421 573 372T451 307Q429 302 328 301H234V181Q234 62 237 58Q245 47 304 46H337V0H326Q305 3 182 3Q47 3 38 0H27V46H60Q102 47 111 49T130 61V622ZM507 488Q507 514 506 528T500 564T483 597T450 620T397 635Q385 637 307 637H286Q237 637 234 628Q231 624 231 483V342H302H339Q390 342 423 349T481 382Q507 411 507 488Z" transform="translate(1618,0)"/><path data-c="55" d="M128 622Q121 629 117 631T101 634T58 637H25V683H36Q57 680 180 680Q315 680 324 683H335V637H302Q262 636 251 634T233 622L232 418V291Q232 189 240 145T280 67Q325 24 389 24Q454 24 506 64T571 183Q575 206 575 410V598Q569 608 565 613T541 627T489 637H472V683H481Q496 680 598 680T715 683H724V637H707Q634 633 622 598L621 399Q620 194 617 180Q617 179 615 171Q595 83 531 31T389 -22Q304 -22 226 33T130 192Q129 201 128 412V622Z" transform="translate(2299,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(3049,0)"/></g><g data-mml-node="mo" transform="translate(16064.1,0)"><path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"/></g><g data-mml-node="mo" transform="translate(17064.3,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mtext" transform="translate(17453.3,0)"><path data-c="70" d="M36 -148H50Q89 -148 97 -134V-126Q97 -119 97 -107T97 -77T98 -38T98 6T98 55T98 106Q98 140 98 177T98 243T98 296T97 335T97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 61 434T98 436Q115 437 135 438T165 441T176 442H179V416L180 390L188 397Q247 441 326 441Q407 441 464 377T522 216Q522 115 457 52T310 -11Q242 -11 190 33L182 40V-45V-101Q182 -128 184 -134T195 -145Q216 -148 244 -148H260V-194H252L228 -193Q205 -192 178 -192T140 -191Q37 -191 28 -194H20V-148H36ZM424 218Q424 292 390 347T305 402Q234 402 182 337V98Q222 26 294 26Q345 26 384 80T424 218Z"/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(556,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(834,0)"/><path data-c="6B" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T97 124T98 167T98 217T98 272T98 329Q98 366 98 407T98 482T98 542T97 586T97 603Q94 622 83 628T38 637H20V660Q20 683 22 683L32 684Q42 685 61 686T98 688Q115 689 135 690T165 693T176 694H179V463L180 233L240 287Q300 341 304 347Q310 356 310 364Q310 383 289 385H284V431H293Q308 428 412 428Q475 428 484 431H489V385H476Q407 380 360 341Q286 278 286 274Q286 273 349 181T420 79Q434 60 451 53T500 46H511V0H505Q496 3 418 3Q322 3 307 0H299V46H306Q330 48 330 65Q330 72 326 79Q323 84 276 153T228 222L176 176V120V84Q176 65 178 59T189 49Q210 46 238 46H254V0H246Q231 3 137 3T28 0H20V46H36Z" transform="translate(1278,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1806,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(2084,0)"/><path data-c="20" d="" transform="translate(2528,0)"/><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z" transform="translate(2778,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(3222,0)"/><path data-c="6D" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q351 442 364 440T387 434T406 426T421 417T432 406T441 395T448 384T452 374T455 366L457 361L460 365Q463 369 466 373T475 384T488 397T503 410T523 422T546 432T572 439T603 442Q729 442 740 329Q741 322 741 190V104Q741 66 743 59T754 49Q775 46 803 46H819V0H811L788 1Q764 2 737 2T699 3Q596 3 587 0H579V46H595Q656 46 656 62Q657 64 657 200Q656 335 655 343Q649 371 635 385T611 402T585 404Q540 404 506 370Q479 343 472 315T464 232V168V108Q464 78 465 68T468 55T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(3722,0)"/><path data-c="70" d="M36 -148H50Q89 -148 97 -134V-126Q97 -119 97 -107T97 -77T98 -38T98 6T98 55T98 106Q98 140 98 177T98 243T98 296T97 335T97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 61 434T98 436Q115 437 135 438T165 441T176 442H179V416L180 390L188 397Q247 441 326 441Q407 441 464 377T522 216Q522 115 457 52T310 -11Q242 -11 190 33L182 40V-45V-101Q182 -128 184 -134T195 -145Q216 -148 244 -148H260V-194H252L228 -193Q205 -192 178 -192T140 -191Q37 -191 28 -194H20V-148H36ZM424 218Q424 292 390 347T305 402Q234 402 182 337V98Q222 26 294 26Q345 26 384 80T424 218Z" transform="translate(4555,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(5111,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(5503,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(5947,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(6341,0)"/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(6735,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(7013,0)"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(7513,0)"/><path data-c="20" d="" transform="translate(8069,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(8319,0)"/><path data-c="61" d="M137 305T115 305T78 320T63 359Q63 394 97 421T218 448Q291 448 336 416T396 340Q401 326 401 309T402 194V124Q402 76 407 58T428 40Q443 40 448 56T453 109V145H493V106Q492 66 490 59Q481 29 455 12T400 -6T353 12T329 54V58L327 55Q325 52 322 49T314 40T302 29T287 17T269 6T247 -2T221 -8T190 -11Q130 -11 82 20T34 107Q34 128 41 147T68 188T116 225T194 253T304 268H318V290Q318 324 312 340Q290 411 215 411Q197 411 181 410T156 406T148 403Q170 388 170 359Q170 334 154 320ZM126 106Q126 75 150 51T209 26Q247 26 276 49T315 109Q317 116 318 175Q318 233 317 233Q309 233 296 232T251 223T193 203T147 166T126 106Z" transform="translate(8711,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(9211,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(9600,0)"/></g><g data-mml-node="mo" transform="translate(27497.3,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g></g></g></svg></mjx-container></p><p>The essence of the solution is to let all processes share memory through a single <code>torch.Tensor</code> object, which needs to be moved to Linux shared memory by PyTorch&apos;s custom pickling routine. The TLDR on how to achieve sharing is:</p><blockquote><ol><li>Don&apos;t let dataloader workers access many Python objects in their parent. Serialize all objects into a single <code>torch.Tensor</code> (but not numpy array) for workers to access.</li><li>Don&apos;t let all GPU workers load data independently. Load in one GPU worker, and share with others through a <code>torch.Tensor</code>.</li></ol></blockquote><p>For list-like data, all of these can be implemented transparently using the <a href="https://github.com/ppwwyyxx/RAM-multiprocess-dataloader/blob/main/serialize.py" aria-label="serialize.py &#xB7; ppwwyyxx/RAM-multiprocess-dataloader" class="hint--top hint--rounded hint--no-animate hint--no-arrow">serialization routines</a> developed in this article.</p><p>Multi-processing is often the only way to achieve trueparallelism in Python(until <a href="https://peps.python.org/pep-0703/" aria-label="PEP 703 &#x2013; Making the Global Interpreter Lock Optional in CPython | peps.python.org" class="hint--top hint--rounded hint--no-animate hint--no-arrow">PEP703</a>),but it comes with many tricky problems.This article hopefully provides an in-depth view of the problem of RAM usage.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;A typical PyTorch training program on 8 GPUs with 4 dataloader
workers per GPU would create at least &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;SVG&quot;&gt;&lt;svg style=&quot;vertical-align: -0.566ex;&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;15.965ex&quot; height=&quot;2.262ex&quot; role=&quot;img&quot; focusable=&quot;false&quot; viewbox=&quot;0 -750 7056.4 1000&quot;&gt;&lt;g stroke=&quot;currentColor&quot; fill=&quot;currentColor&quot; stroke-width=&quot;0&quot; transform=&quot;scale(1,-1)&quot;&gt;&lt;g data-mml-node=&quot;math&quot;&gt;&lt;g data-mml-node=&quot;mn&quot;&gt;&lt;path data-c=&quot;38&quot; d=&quot;M70 417T70 494T124 618T248 666Q319 666 374 624T429 515Q429 485 418 459T392 417T361 389T335 371T324 363L338 354Q352 344 366 334T382 323Q457 264 457 174Q457 95 399 37T249 -22Q159 -22 101 29T43 155Q43 263 172 335L154 348Q133 361 127 368Q70 417 70 494ZM286 386L292 390Q298 394 301 396T311 403T323 413T334 425T345 438T355 454T364 471T369 491T371 513Q371 556 342 586T275 624Q268 625 242 625Q201 625 165 599T128 534Q128 511 141 492T167 463T217 431Q224 426 228 424L286 386ZM250 21Q308 21 350 55T392 137Q392 154 387 169T375 194T353 216T330 234T301 253T274 270Q260 279 244 289T218 306L210 311Q204 311 181 294T133 239T107 157Q107 98 150 60T250 21Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(722.2,0)&quot;&gt;&lt;path data-c=&quot;D7&quot; d=&quot;M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(1722.4,0)&quot;&gt;&lt;path data-c=&quot;28&quot; d=&quot;M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mn&quot; transform=&quot;translate(2111.4,0)&quot;&gt;&lt;path data-c=&quot;34&quot; d=&quot;M462 0Q444 3 333 3Q217 3 199 0H190V46H221Q241 46 248 46T265 48T279 53T286 61Q287 63 287 115V165H28V211L179 442Q332 674 334 675Q336 677 355 677H373L379 671V211H471V165H379V114Q379 73 379 66T385 54Q393 47 442 46H471V0H462ZM293 211V545L74 212L183 211H293Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(2833.7,0)&quot;&gt;&lt;path data-c=&quot;2B&quot; d=&quot;M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mn&quot; transform=&quot;translate(3833.9,0)&quot;&gt;&lt;path data-c=&quot;31&quot; d=&quot;M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(4333.9,0)&quot;&gt;&lt;path data-c=&quot;29&quot; d=&quot;M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(5000.7,0)&quot;&gt;&lt;path data-c=&quot;3D&quot; d=&quot;M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mn&quot; transform=&quot;translate(6056.4,0)&quot;&gt;&lt;path data-c=&quot;34&quot; d=&quot;M462 0Q444 3 333 3Q217 3 199 0H190V46H221Q241 46 248 46T265 48T279 53T286 61Q287 63 287 115V165H28V211L179 442Q332 674 334 675Q336 677 355 677H373L379 671V211H471V165H379V114Q379 73 379 66T385 54Q393 47 442 46H471V0H462ZM293 211V545L74 212L183 211H293Z&quot;/&gt;&lt;path data-c=&quot;30&quot; d=&quot;M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z&quot; transform=&quot;translate(500,0)&quot;/&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/mjx-container&gt; processes.
A naive use of PyTorch dataset and dataloader can easily
&lt;strong&gt;replicate your dataset&amp;apos;s RAM usage by 40 times&lt;/strong&gt;. This issue has probably affected everyone who has done anything nontrivial with PyTorch.
In this post, we will explain why it happens, and how to &lt;strong&gt;avoid the 40x RAM usage&lt;/strong&gt;.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Deep Learning" scheme="https://ppwwyyxx.com/blog/tags/Deep-Learning/"/>
    
    <category term="PyTorch" scheme="https://ppwwyyxx.com/blog/tags/PyTorch/"/>
    
    <category term="Python" scheme="https://ppwwyyxx.com/blog/tags/Python/"/>
    
  </entry>
  
  <entry>
    <title>Not Every Model Has a Separate &quot;Loss Function&quot;</title>
    <link href="https://ppwwyyxx.com/blog/2022/Loss-Function-Separation/"/>
    <id>https://ppwwyyxx.com/blog/2022/Loss-Function-Separation/</id>
    <published>2022-10-08T07:00:00.000Z</published>
    <updated>2022-10-08T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>&quot;Loss function&quot; is one of the most basic concepts today in deep learning.Despite that,it is actually not necessarily a good programming abstraction whendesigning general-purpose systems. A system should not assume thata model always comes together with a separate &quot;loss function&quot;.</p><span id="more"></span><h1 id="Loss-Function-Separation-of-Logic">&quot;Loss Function&quot;: Separation of Logic<a class="markdown-anchor" href="#Loss-Function-Separation-of-Logic">&#xB6;</a></h1><p>&quot;Loss function&quot; may mean different things in different systems.The version I&apos;m going to criticize is the most common one that looks like below:</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Bad</th><th>Worse</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">trainer_bad</span>(<span class="hljs-params">data, model, loss_func</span>):<br>  <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:<br>    inputs = <span class="hljs-built_in">next</span>(data)<br>    predictions = model(inputs)<br>    loss = loss_func(predictions, inputs)<br>    <span class="hljs-comment"># compute gradients and update model</span><br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">trainer_worse</span>(<span class="hljs-params">data, model, loss_func</span>):<br>  <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:<br>    inputs, labels = <span class="hljs-built_in">next</span>(data)<br>    predictions = model(inputs)<br>    loss = loss_func(predictions, labels)<br>    <span class="hljs-comment"># compute gradients and update model</span><br></code></pre></td></tr></table></figure></td></tr></table></div><p>The key property of a bad &quot;loss function&quot; abstraction is:Users are asked to provide a &quot;loss function&quot; that&apos;s executed separately after the &quot;model / forward logic&quot;.Such abstraction appears in a few open source systems: Keras <a href="https://keras.io/api/models/model_training_apis/#compile-method" aria-label="Model training APIs" class="hint--top hint--rounded hint--no-animate hint--no-arrow">model.compile(loss=)</a>,fast.ai <a href="https://docs.fast.ai/learner.html#learner" aria-label="fastai - Learner, Metrics, Callbacks" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Learner(loss_func=)</a>, Lingvo <a href="https://github.com/tensorflow/lingvo/blob/master/lingvo/core/base_model.py#L476" aria-label="base_model.py &#xB7; tensorflow/lingvo" class="hint--top hint--rounded hint--no-animate hint--no-arrow">BaseModel.ComputeLoss</a>.</p><p>The main problem is not with the function itself, but that the users&apos; algorithm logic is <strong>forced to separate into two parts</strong>: <code>model</code> and <code>loss_func</code>.</p><p>As an alternative, <code>trainer_good</code> below no longer separates &quot;loss_func&quot; from the model, and has equal functionalitieswith <code>trainer_bad</code>.</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">trainer_good</span>(<span class="hljs-params">data, model</span>):<br>  <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:<br>    inputs: <span class="hljs-type">Any</span> = <span class="hljs-built_in">next</span>(data)<br>    loss: Scalar = model(inputs)  <span class="hljs-comment"># or losses: dict[str, Scalar]</span><br>    <span class="hljs-comment"># compute gradients and update model</span><br></code></pre></td></tr></table></figure><p>In this article, I want to argue that this is a better design because:</p><ol><li>Separating out &quot;loss function&quot; causes many trouble.</li><li>With <code>trainer_good</code>, users can still separate their <code>model</code> into two parts if they like, but they don&apos;t have to.</li><li>Although <code>trainer_good</code> is no longer aware of the separation, it does not matter.</li></ol><p>(Apparently, <code>trainer_good == partial(trainer_bad, loss_func=lambda x, y: x)</code>.So <code>trainer_bad</code> can still be used - we just set <code>loss_func</code> to a no-op if we don&apos;t like it.But <code>trainer_good</code> is cleaner.)</p><h1 id="Problems-of-a-Forced-Separation">Problems of a Forced Separation<a class="markdown-anchor" href="#Problems-of-a-Forced-Separation">&#xB6;</a></h1><p>It&apos;s true that the separation can be useful to certain types of models.But it&apos;s not always the case, and enforcing it can be harmful instead.</p><h2 id="Duplication-between-Model-and-Loss">Duplication between &quot;Model&quot; and &quot;Loss&quot;<a class="markdown-anchor" href="#Duplication-between-Model-and-Loss">&#xB6;</a></h2><p>The separation is not convenient for a model with many optional losses.Take a multi-task model for example:</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Separation</th><th>No Separation</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Module</span>(nn.Module):<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">forward</span>(<span class="hljs-params">self, inputs</span>):<br>    out = {}<br>    <span class="hljs-keyword">if</span> has_task1:<br>      out[<span class="hljs-string">&quot;out1&quot;</span>] = <span class="hljs-comment"># get outputs for task 1</span><br>    <span class="hljs-keyword">if</span> has_task2:<br>      out[<span class="hljs-string">&quot;out2&quot;</span>] = <span class="hljs-comment"># get outputs for task 2</span><br>    <span class="hljs-comment"># ...</span><br>    <span class="hljs-keyword">return</span> out<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">loss_func</span>(<span class="hljs-params">predictions, inputs</span>):<br>  losses = {}<br>  <span class="hljs-keyword">if</span> has_task1:<br>  <span class="hljs-comment"># Or: if &quot;out1&quot; in predictions:</span><br>    losses[<span class="hljs-string">&quot;loss1&quot;</span>] = <span class="hljs-comment"># get loss for task 1</span><br>  <span class="hljs-keyword">if</span> has_task2:<br>    losses[<span class="hljs-string">&quot;loss2&quot;</span>] = <span class="hljs-comment"># get loss for task 2</span><br>  <span class="hljs-comment"># ...</span><br>  <span class="hljs-keyword">return</span> losses<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Module</span>(nn.Module):<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">forward</span>(<span class="hljs-params">self, inputs</span>):<br>    losses = {}<br>    <span class="hljs-keyword">if</span> has_task1:<br>      out =             <span class="hljs-comment"># get outputs for task 1</span><br>      losses[<span class="hljs-string">&quot;task1&quot;</span>] = <span class="hljs-comment"># get loss for task 1</span><br>    <span class="hljs-keyword">if</span> has_task2:<br>      out =             <span class="hljs-comment"># get outputs for task 2</span><br>      losses[<span class="hljs-string">&quot;task2&quot;</span>] = <span class="hljs-comment"># get loss for task 2</span><br>    <span class="hljs-comment"># ...</span><br>    <span class="hljs-keyword">return</span> losses<br></code></pre></td></tr></table></figure></td></tr></table></div><p>The right one is simpler in that it does not duplicate thebranches that enable different tasks/losses.In reality, these conditions are often more complex than a simple <code>if</code>,and branching is generally less straightforward to maintain.So it&apos;s beneficial to not have to repeat the logic.</p><small><p>Note: If you think a wrapper like<code>multi_loss_func({&quot;task1&quot;: loss_func1, &quot;task2&quot;: loss_func2})</code>will help (like what Keras supports), it is not going to work wellbecause it doesn&apos;t know how to route the inputs/outputs to loss functions.</p></small><h2 id="Loss-is-not-Independent-of-Model">&quot;Loss&quot; is not Independent of &quot;Model&quot;<a class="markdown-anchor" href="#Loss-is-not-Independent-of-Model">&#xB6;</a></h2><p>One may argue that separating &quot;loss&quot; from &quot;model&quot; is nice becausethen we can easily switch different loss functions independent of &quot;model&quot;.That is indeed useful in many cases.However, in many algorithms, loss computation is simply not independent ofthe model and should not be switched arbitrarily.This could be due to:</p><ol><li><p>Loss computation depends on internal states computed during <code>model.forward</code>, e.g.:</p><ul><li>Loss needs to know which part of training data is sampled during <code>forward</code>.</li><li>Some predicted auxiliary attributes control whether a sample in a batch should participate in losses.</li><li>Losses such as activation regularization should naturally happen during <code>forward</code>.</li></ul><p>In these cases, forcing a separation of &quot;loss&quot; and &quot;model&quot; will require &quot;model&quot; to return its internal states, causing an abstraction leak.</p></li><li><p>Different loss functions expect different representations of model&apos;s predictions. For example, these representations could be:</p><ul><li>Discrete vs. one-hot encoding of class labels.</li><li>Boxes represented as absolute coordinates, or as reference anchors plus offsets.</li><li>Segmentation masks represented as polygons, binary bitmasks, or many other formats.</li></ul><p>Since conversion between representations may be expensive or lossy, we&apos;d like the model toproduce the exact representation needed by loss computation.Therefore, a separation would not make model independent of losses.On the contrary, it&apos;s even worse because loss-relatedlogic will be unnaturally split like this:</p></li></ol><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Separation</th><th>No Separation</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Model</span>(nn.Module):<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__call__</span>(<span class="hljs-params">self, inputs</span>):<br>    hidden_representation = layers(inputs)<br>    <span class="hljs-keyword">if</span> use_loss1:<br>      <span class="hljs-comment"># Return proper representation for loss1</span><br>      <span class="hljs-keyword">return</span> predict1(hidden_representation)<br>    <span class="hljs-keyword">if</span> use_loss2:<br>      <span class="hljs-keyword">return</span> predict2(hidden_representation)<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">loss_func</span>(<span class="hljs-params">pred, inputs</span>):<br>  <span class="hljs-comment"># pred could have different representations</span><br>  <span class="hljs-comment"># depending on the loss function used</span><br>  <span class="hljs-keyword">if</span> use_loss1:<br>    <span class="hljs-keyword">return</span> loss1(pred, inputs, some_options)<br>  <span class="hljs-keyword">if</span> use_loss2:<br>    <span class="hljs-keyword">return</span> loss2(pred, inputs, other_options)<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Model</span>(nn.Module):<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__call__</span>(<span class="hljs-params">self, inputs</span>):<br>    hidden_representation = layers(inputs)<br>    <span class="hljs-keyword">if</span> use_loss1:<br>      pred_fmt1 = predict1(hidden_representation)<br>      <span class="hljs-keyword">return</span> loss1(pred_fmt1, inputs, some_options)<br>    <span class="hljs-keyword">if</span> use_loss2:<br>      pred_fmt2 = predict2(hidden_representation)<br>      <span class="hljs-keyword">return</span> loss2(pred_fmt2, inputs, other_options)<br></code></pre></td></tr></table></figure></td></tr></table></div><p>We can see in the above snippet that the model is in factnot independent of losses.It also makes <code>loss_func</code> a bad abstraction because the semanticsof its <code>prediction</code> argument is complex: it should bein different formats depending on which of <code>loss{1,2}</code> is used.In the version with no separation, it&apos;s very clearthat the losses are computed using the right representation.</p><h2 id="No-Clean-Separation">No Clean Separation<a class="markdown-anchor" href="#No-Clean-Separation">&#xB6;</a></h2><p>One may argue that the separation is helpful because it&apos;s nice to let the &quot;model&quot;return the same data in training and inference.This makes sense for simple models where training and inference share most of the logic.For example, in a standard classification model shown below,we can let the &quot;model&quot; object return logits, which will be useful in bothtraining and inference.</p><img src="/blog/2022/Loss-Function-Separation/loss-function-cls.png" class="center" width="500"><p>But many models don&apos;t have a clean separation like this.In theory, <strong>training and inference only have to share (some) trained weights</strong>,but <strong>don&apos;t necessarily have to share any logic</strong>.Many object detection models, for example, do not compute &quot;predictions&quot; in trainingand do not compute losses in inference.A simplified diagram of Region-Proposal Network (RPN)of a two-stage detector looks like this during training:</p><img src="/blog/2022/Loss-Function-Separation/loss-rpn.png" class="center" width="700"><p>Any attempt to split a complicated algorithm like this into &quot;model&quot; and &quot;loss function&quot; will:</p><ul><li>Force &quot;model&quot; to return its internal algorithmic details that are not useful anywhere,except in a corresponding &quot;loss function&quot;. This is an abstraction leak.</li><li>Make code harder to read, because closely related logic is separated in an unnatural way.</li><li>Lead to higher memory usage (if executing eagerly), because some internal stateshave to be kept alive until &quot;loss function&quot; is called <strong>after</strong> the &quot;model&quot;.</li></ul><p>Therefore, it&apos;s unrealistic to expect that there is a nice separation, or that &quot;model&quot; can producea consistent format in both training and inference.A better design is to include loss computation in the model&apos;s training-mode <code>forward</code>, i.e., let model outputlosses in training, but predictions in inference.</p><h1 id="Trainer-Does-Not-Need-to-Know-about-the-Separation">Trainer Does Not Need to Know about the Separation<a class="markdown-anchor" href="#Trainer-Does-Not-Need-to-Know-about-the-Separation">&#xB6;</a></h1><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Separation</th><th>No Separation</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">trainer</span>(<span class="hljs-params">model, loss_func, ...</span>):<br>  <span class="hljs-comment"># model: data -&gt; outputs</span><br>  <span class="hljs-comment"># loss_func: outputs -&gt; losses</span><br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">trainer</span>(<span class="hljs-params">model, ...</span>):<br>  <span class="hljs-comment"># model: data -&gt; losses</span><br></code></pre></td></tr></table></figure></td></tr></table></div><p>In the &quot;no separation&quot; design, users provide a &quot;model&quot; that returns losses.This model internally can still use separation of &quot;loss function&quot; and &quot;forward logic&quot;as long as it makes sense for this specific model.However, trainer is no longer aware of the separation,and the trainer can no longer obtain the &quot;outputs&quot;.</p><p>Will this become a limitation of the &quot;no separation&quot; design? What if we&apos;d like to do something with&quot;outputs&quot;? My answer is:</p><ul><li>For 99% of the use cases where the &quot;outputs&quot; don&apos;t directly affect the training loop structure,trainer doesn&apos;t need to know about &quot;outputs&quot;.<ul><li>For use cases where the trainer does something non-critical (not affecting loop structure) with &quot;outputs&quot;,a proper design would move such responsibility away from trainer.</li><li>For example, writing &quot;outputs&quot; to tensorboard shouldn&apos;t be a responsibility of trainer. A commonapproach is to use a context-based systemthat allows users to simply call <code>write_summary(outputs)</code> inside their model.</li></ul></li><li>For other obscure use cases, they should have custom trainers anyway.</li></ul><h1 id="Summary">Summary<a class="markdown-anchor" href="#Summary">&#xB6;</a></h1><p>Design is always a trade-off.Adding assumptions to a system might result in some benefits, but at the same time can cause trouble when the assumption isn&apos;t true.Finding a balance in between is difficult and often subjective.</p><p>The assumption that models have to come together with a <strong>separate</strong> &quot;loss function&quot;, in my opinion, brings more trouble than it&apos;s worth.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&amp;quot;Loss function&amp;quot; is one of the most basic concepts today in deep learning.
Despite that,
it is actually not necessarily a good programming abstraction when
designing general-purpose systems. A system should not assume that
a model always comes together with a separate &amp;quot;loss function&amp;quot;.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Deep Learning" scheme="https://ppwwyyxx.com/blog/tags/Deep-Learning/"/>
    
    <category term="Design" scheme="https://ppwwyyxx.com/blog/tags/Design/"/>
    
  </entry>
  
  <entry>
    <title>How to Maintain Clean Core APIs for Research</title>
    <link href="https://ppwwyyxx.com/blog/2022/Maintain-Clean-Core-APIs-for-Research/"/>
    <id>https://ppwwyyxx.com/blog/2022/Maintain-Clean-Core-APIs-for-Research/</id>
    <published>2022-09-19T07:00:00.000Z</published>
    <updated>2022-09-19T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>Building a library for research and experiments is quite different from building other types of software.A key challenge is that, in research, abstractions and APIs are rarely set in stone:users may want to propose a slight variant or modification to literally <strong>ANYWHERE</strong> in the whole program,just because they have a new idea.</p><span id="more"></span><p>In deep learning libraries, these variants can be a different implementation of a layer,a change in optimization algorithm, or a small modification to the training logic, etc.</p><p>Designing and maintaining these <strong>&quot;research APIs&quot;</strong> is difficultdue to how frequently users want to change their behaviors.Such changes are often implemented by simply adding featuresto the target API they want to modify, e.g. by adding a new flag to the API,or by adding a new abstraction that generalizes the target API towards the users&#x2019; use case.</p><p>However, when maintaining a <strong>generic, core library meant to be adopted by diverse use cases for a long term</strong>,the above approach does not scale and poses many problems (discussed more<a href="#Concern-of-New-Arguments">below</a>).</p><p>This note lists a few principles when working with &quot;research APIs&quot; that should help answer:</p><ul><li>How to maintain a clean set of core APIs in research libraries.</li><li>How library maintainers &amp; users collaborate to achieveusers&#x2019; diverse needs without complicating the core APIs.</li></ul><h1 id="Core-does-not-aim-to-implement-all-use-cases">Core does not aim to implement all use cases<a class="markdown-anchor" href="#Core-does-not-aim-to-implement-all-use-cases">&#xB6;</a></h1><p>Researchers&apos; job is about doing things in new ways.Hence their needs are so diverse that a core library should not aim to include or implementfeatures for all possible use cases. The library should aim to only include the most popularand standardized features (more on the criteria later).</p><h1 id="Core-should-allow-most-features-to-be-implemented-out-of-core">Core should allow most features to be implemented out-of-core<a class="markdown-anchor" href="#Core-should-allow-most-features-to-be-implemented-out-of-core">&#xB6;</a></h1><p>For features not included in the core, there should be a way for users to implementthem in a non-intrusive way, often out-of-core as extensions,without too much overhead / repetition.</p><p>This requires a continuous design evolution to make the core more <strong>modular and composable</strong>,so that core code can be reused in users&#x2019; new implementation.</p><p>A good sanity check for library maintainers is to ask the following question:</p><blockquote><p>For <strong>any</strong> feature currently in the core library, suppose we remove it today, how much effort would it takefor users to reimplement it out-of-core?</p></blockquote><p>A well-designed library should be decoupled such that most of its features are just extensions of itself, and they can beimplemented out-of-core the same way as it is in the core.</p><h1 id="Criteria-for-feature-inclusion">Criteria for feature inclusion<a class="markdown-anchor" href="#Criteria-for-feature-inclusion">&#xB6;</a></h1><p>There are 3 criteria for feature inclusion in core, ordered by their importance.</p><ul><li>Popularity: Whether the feature is used by many users</li><li>Standardization: Whether the feature&#x2019;s API is standardized/agreed among its users</li><li>Simplicity: Whether the feature is simple to implement</li></ul><p>To understand the criteria more, let&#x2019;s ask: what if the feature is &#x2014;</p><p><u>Popular but not standardized</u>: sometimes a feature is popular, but its users don&#x2019;t yet align on the properparameterization, its API, or the subtle implementation details. Including such features is risky, as it may create unclearsemantics orimpede its standardization in the future. It&#x2019;s still OK to include it if it&#x2019;s very popular (popularity is the #1 most important criteria),but try to do it in a composable way and with warning signs.</p><p>As a negative example, &quot;Transformer&quot; is a popular but not standarized feature.It&apos;s included in Pytorch, but received many <a href="https://github.com/pytorch/pytorch/issues/10459" aria-label="[feature request] nn.Transformer &#xB7; Issue #10459 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">complaints</a>,and many projects (e.g. <a href="https://github.com/facebookresearch/fairseq" aria-label="facebookresearch/fairseq: Facebook AI Research Sequence-to-Sequence Toolkit written in Python." class="hint--top hint--rounded hint--no-animate hint--no-arrow">fairseq</a>, <a href="https://github.com/facebookresearch/detr" aria-label="facebookresearch/detr: End-to-End Object Detection with Transformers" class="hint--top hint--rounded hint--no-animate hint--no-arrow">detr</a>)eventually have to fork and reimplement their own Transformer.</p><p><u>Simple but not popular/standardized</u>: Simplicity alone is not sufficient for inclusion, no matter how simple it is.Because if everyone adds a simple feature they need, together it becomes complex.</p><p><u>Popular, standardized but not simple</u>: Simplicity is the #3 important factors.If something is complex but very popular &amp; standardized (e.g. BatchNorm being a headachefor DL library developers), it should be included. In fact this is where a library couldprovide a lot of value to users.</p><h1 id="Concern-of-New-Arguments">Concern of New Arguments<a class="markdown-anchor" href="#Concern-of-New-Arguments">&#xB6;</a></h1><p>When a user wants to change the behavior of a &quot;research API&quot; <code>def func()</code> defined in core,adding new arguments is often the quickest way to get things done. But it may introduce a number of maintenance problems.</p><h2 id="Simple-Flags-Options">Simple Flags / Options<a class="markdown-anchor" href="#Simple-Flags-Options">&#xB6;</a></h2><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>New flag</th><th>New argument</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>(<span class="hljs-params">flag=<span class="hljs-literal">False</span></span>):<br>   ...<br>   <span class="hljs-keyword">if</span> flag:<br>       Variant here<br>   ...<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>(<span class="hljs-params">multiplier_of_x=<span class="hljs-number">1.0</span></span>):<br>   ...<br>   x = x * multiplier_of_x<br>   ..<br></code></pre></td></tr></table></figure></td></tr></table></div><p>Adding a simple argument to control the behavior like above is OK,if we think that the new option is very clear and popular.But as a &quot;research API&quot;, many users will want to add their own customizations.This could lead to the following problems:</p><ul><li><strong>Poor Code health</strong>: The library may gradually accumulates too many features that are:<ul><li>Hard to read due to branching (as there are too many flags).Ideally, readers should not pay too much extra mental overhead for logic they don&#x2019;t care about</li><li>Hard to maintain because the contextual knowledge about them is distributed among different developers</li></ul></li><li><strong>Confusing behaviors</strong>: More and more features added over time may not interact with each other in a clear way,causing confusing or silent wrong behaviors<ul><li>E.g. featureA becomes a no-op when featureB is enabled</li><li>E.g. featureA and featureB are conflicting / overlapping in semantics</li><li>E.g. featureA&#x2019;s semantics becomes undefined when featureB is enabled</li></ul></li></ul><img src="/blog/2022/Maintain-Clean-Core-APIs-for-Research/if-then-else.jpg" class="center" width="600"><center><figcaption class="image-caption">Good characterization of the abovementioned problems</figcaption></center><ul><li><p><strong>&quot;More general&quot; may become &quot;less general&quot;</strong>:A common argument for adding options like this, is thatit doesn&apos;t change existing behavior and&quot;makes the function more general&quot;.</p><p>However, keep in mind that <strong>when a function becomes more general in one aspect,it&apos;s often less general in other aspects</strong>.Generalizing towards one direction may not be a net win, because<strong>research code has too many possible directions to generalize towards</strong>, andpicking one direction may affect its eligibility to pick others in the future.We will show what this means shortly.</p></li></ul><h2 id="Callbacks">Callbacks<a class="markdown-anchor" href="#Callbacks">&#xB6;</a></h2><p>To add a new behavior to <code>func</code>, one can also add a callback argument:</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Inject custom behaviors through callbacks:</th><th>Use object.method as callbacks:</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>(<span class="hljs-params">callback</span>):<br>  ...<br>  callback(x)<br>  ...<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>(<span class="hljs-params">obj</span>):<br>  ...<br>  obj.do(x)<br>  ...<br></code></pre></td></tr></table></figure></td></tr></table></div><p>This appears useful, since the custom logic is not implemented in core,but in a user-provided callback.For example, given the original code below (left), a researcher who wants to compute <code>y</code> differently may proposea <code>compute_y_fn</code> argument like below (right).</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Original:</th><th>With callbacks:</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>():<br> a = something ...<br> x = something ...<br> y = x.norm()<br> z = ...<br> ...<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>(<span class="hljs-params">compute_y_fn=<span class="hljs-keyword">lambda</span> x: x.norm(<span class="hljs-params"></span>)</span>):<br> a = something ...<br> x = something ...<br> y = compute_y_fn(x)<br> z = ...<br> ...<br></code></pre></td></tr></table></figure></td></tr></table></div><p>However, this design often turns out to be more problematic:</p><ul><li><p><strong>Premature abstractions</strong>: Assumptions/constraints are implicitly created about where the callback is triggered,what arguments it needs and what it returns. These assumptions may be bad.</p><p>In the example above, a 2nd researcher may want to compute<code>y</code> using both <code>x</code> and <code>a</code>; a 3rd researcher may want to compute <code>y, z</code>in one function <code>compute_y_z_fn</code> because it&apos;s more efficient. These variants conflict with the 1st researcher&apos;s design.</p><p>In the future, after seeing enough use cases, we might realize that a <code>xyz = compute_xyz(a)</code> is a truly good abstraction.However, at that time the premature abstraction of <code>compute_y_fn</code> will get in our way implementing <code>compute_xyz</code>.In other words, although the current design makes the computation of <code>y</code> &quot;more general&quot;, the abstraction limits our abilityto generalize the function in other ways. That&apos;s why we said earlier that &quot;more general means less general&quot;.</p></li><li><p><strong>Obscure logic</strong>: readers can&apos;t easily figure out what this function does by reading its code: they needto look at the caller of this function to see which callback is supplied, and thenlook at the implementation of the callback function. The aforementioned issue of &quot;confusing behaviors&quot; also applies here.</p></li></ul><p>Sometimes callbacks are good and useful abstractions. But because it is too powerful, I saw it frequently abused to altera behavior into something that&apos;s strongly overfitted to a small number of use cases.This happens especially often in research code.In code reviews, I usually frown upon APIs that require callbacks/user-defined functions.</p><h1 id="Prefer-forks-over-new-arguments">Prefer forks over new arguments<a class="markdown-anchor" href="#Prefer-forks-over-new-arguments">&#xB6;</a></h1><p>To customize a &quot;research API&quot; <code>def func()</code> defined in core, we have the following options:</p><ol><li>Out-of-core, e.g. a <code>def func_v2()</code> in user code.(Or a <code>class ClassV2</code> for classes).</li><li>In-core, but keep existing APIs unaffected, e.g. a <code>def func_v2()</code> in core.</li><li>In-core, and change existing APIs, e.g. a new option in <code>def func(option)</code>.</li></ol><p>The best choice is heavily subjective and should be evaluated case-by-case.Due to the concern of new arguments,in general we recommend methods (1) and (2), i.e. <strong>prefer forking <code>func()</code> over changing <code>func()</code></strong>.</p><ul><li>If a fork will create significant code duplication, choose (2) and try to reduce duplication with private abstractions (see next section).</li><li>Adding flags / simple args is acceptable for simple, clean, popular additions.</li><li>Adding callbacks / new abstraction requires scrutiny, and should come with more than a handful of use cases in mind.</li></ul><p>This also echoes<a href="https://flax.readthedocs.io/en/latest/philosophy.html" aria-label="The Flax philosophy" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Flax design philosophy</a> thatsays &quot;prefer duplication over adding options / bad abstractions&quot;.</p><h1 id="Accept-duplication-but-aim-to-reduce-them-later">Accept duplication, but aim to reduce them later<a class="markdown-anchor" href="#Accept-duplication-but-aim-to-reduce-them-later">&#xB6;</a></h1><p>Users/developers may find that the core design is not good enough yet, and recreating a variantof <code>func()</code>  without touching it may lead to too much code duplication.For example, <code>...</code> is duplicated between the two functions below.</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Existing API in core</th><th>New variant</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>():<br>  ...<br>  ...<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func_v2</span>():<br>  ...<br>  New custom logic <span class="hljs-keyword">in</span> between.<br>  ...<br></code></pre></td></tr></table></figure></td></tr></table></div><p>Such duplication is <strong>acceptable for a short term</strong>.We do NOT mean to encourage users to heavily fork core code.Instead, users and core developers should engage and aim to evolve the core design to reduce duplication&#x2014; but design change takes time to happen, and duplication is preferred before a good design is found.</p><h2 id="How-to-reduce-duplication">How to reduce duplication<a class="markdown-anchor" href="#How-to-reduce-duplication">&#xB6;</a></h2><p>The most risk-free way to reduce duplications is by moving them into shared reusable code:</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Existing API in core</th><th>New variant</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>():<br>    ... <span class="hljs-comment"># fewer duplications than before</span><br>    _reusable_parts()<br>    ...<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func_v2</span>():<br>    ... <span class="hljs-comment"># fewer duplications than before</span><br>    _reusable_parts()<br>    Variant logic inserted here<br>    ...<br></code></pre></td></tr></table></figure></td></tr></table></div><p>This should be the preferred way to reduce duplications. The benefits are:</p><ul><li>No change to the API of <code>func()</code>, hence little risk.</li><li>Create reusable sub-routines that may be useful to new use cases.</li></ul><p>However, there are also challenges:</p><ul><li>This adds a new API (<code>_reusable_parts()</code>) to maintain.</li><li>Sometimes it&apos;s difficult to identify a clean &amp; reusable subset that can be easily split from the duplicated code.It may require small refactoring to expose a clean subset.Also, remember that <strong>the approach that reduces the most duplications might not be the one with the best abstraction</strong>.</li></ul><p>The above challenges are less significant if <code>_reusable_parts()</code> is private. Therefore:</p><ul><li>If <code>func_v2()</code> is in core, make <code>_reusable_parts()</code> private.</li><li>If <code>func_v2()</code> must be out-of-core, consider <code>_reusable_parts()</code> as &quot;internal/experimental APIs&quot;.</li></ul><p>Inheritance, e.g. <code>class ModuleV2(ModuleCore)</code> may also reduce duplication between two variants.However, this is generally less preferable than composition like above. The reason is similar towhy callbacks are not preferred: overriding methods is like passing callbacks - they are both user-definedfunctions and suffer from the same limitations: users are constrained by the assumption ofwhen/where/how the methods/callbacks are triggered.</p><h1 id="Prefer-branching-at-shallower-code-path">Prefer branching at shallower code path<a class="markdown-anchor" href="#Prefer-branching-at-shallower-code-path">&#xB6;</a></h1><p>It is discussed that we prefer &quot;adding a new implementation&quot; over&quot;adding new conditional branches to the existing implementation&quot;,but sometimes branching has to happen somewhere anyway -to choose between the two implementations.</p><p>If branching has to happen, we prefer it at earlier, shallower code path:</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Branch earlier</th><th>Branch later</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Module</span>():<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__call__</span>(<span class="hljs-params">self, flag</span>):<br>      ...<br>      <span class="hljs-keyword">if</span> flag:<br>          func()<br>      <span class="hljs-keyword">else</span>:<br>          func_v2()<br>      ...<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">func</span>(<span class="hljs-params">flag</span>):<br>    ...<br>    <span class="hljs-keyword">if</span> flag:<br>        ...<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Module</span>():<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__call__</span>(<span class="hljs-params">self, flag</span>):<br>      ...<br>      func(flag)<br>      ...<br></code></pre></td></tr></table></figure></td></tr></table></div><p>By branching earlier, we keep a clean <code>func()</code> unaffected by the new variant.This recommendation is consistent with the preference to fork <code>func_v2()</code>, not to add <code>flag</code> to <code>func()</code>.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Building a library for research and experiments is quite different from building other types of software.
A key challenge is that, in research, abstractions and APIs are rarely set in stone:
users may want to propose a slight variant or modification to literally &lt;strong&gt;ANYWHERE&lt;/strong&gt; in the whole program,
just because they have a new idea.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Deep Learning" scheme="https://ppwwyyxx.com/blog/tags/Deep-Learning/"/>
    
    <category term="Design" scheme="https://ppwwyyxx.com/blog/tags/Design/"/>
    
  </entry>
  
  <entry>
    <title>Automatically Flatten &amp; Unflatten Nested Containers</title>
    <link href="https://ppwwyyxx.com/blog/2022/Automatically-Flatten-Unflatten-Nested-Containers/"/>
    <id>https://ppwwyyxx.com/blog/2022/Automatically-Flatten-Unflatten-Nested-Containers/</id>
    <published>2022-06-16T07:00:00.000Z</published>
    <updated>2022-06-16T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>This post is about a small functionality that is found useful in TensorFlow / JAX / PyTorch.</p><p>Low-level components of these systems often use a plain list of values/tensorsas inputs &amp; outputs.However, end-users that develop models often want to work with morecomplicated data structures:<code>Dict[str, Any]</code>, <code>List[Any]</code>, custom classes, and their nested combinations.Therefore, we need bidirectional conversion between nested structures and a plain list of tensors.I found that different libraries invent similar approaches to solve this problem, and it&apos;s interesting to list them here.</p><span id="more"></span><h2 id="Nested-Containers-Are-Useful-Abstractions">Nested Containers Are Useful Abstractions<a class="markdown-anchor" href="#Nested-Containers-Are-Useful-Abstractions">&#xB6;</a></h2><p>Though many simple deep learning models just needs a few inputs/outputs tensors,nested containers are useful abstractions in advanced models.This is because many concepts are naturally represented by &gt;1 tensors, e.g.:</p><ul><li>A sparse tensor consists of values + indices</li><li>A <a href="https://github.com/pytorch/maskedtensor" aria-label="pytorch/maskedtensor: MaskedTensors for PyTorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">masked tensor</a> (common in transformers) is represented by a tensor + its binary mask</li><li>A segmentation mask can be represented in different ways:<ul><li>single whole-image bitmask tensor</li><li>shape + bounding box + mask within the box (aka &quot;RoIMask&quot;)</li><li>shape + list of polygons</li><li>shape + run-length encoding</li></ul></li><li>Detected objects in an image are represented by boxes + scores + labels + many possible attributes</li><li>A list of variable-length vectors may be represented by a concatenated vector + a length vector, i.e.:<figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">[[1, 2, 3], [42], [6, 6]]  --&gt;  [1, 2, 3, 42, 6, 6], [3, 1, 2]<br></code></pre></td></tr></table></figure></li></ul><p>When a frequently-used concept has natural complexity like above, representing itin a flat structure consisting of only regular tensors (e.g. <code>Dict[str, Tensor]</code>) may result in ugly code.A multi-level nested structure is a better alternative.Take sparse tensor as a simple example:</p><table><thead><tr><th></th><th>Use nested containers</th><th>Use a flat <code>Dict[str, Tensor]</code></th></tr></thead><tbody><tr><td>Representation</td><td><pre>{&quot;a&quot;: SparseTensor,<br> &quot;b&quot;: Tensor}</pre><code>SparseTensor</code> can be a namedtuple/dataclass, or a new class.</td><td><pre>{&quot;a_values&quot;: Tensor,<br> &quot;a_indices&quot;: Tensor,<br> &quot;b&quot;: Tensor}</pre></td></tr><tr><td>Sanity check</td><td><code>SparseTensor</code> class can guarantee both tensors exist and follow certain contracts (e.g. their shapes match)</td><td>Need to check <code>a_{values,indices}</code> co-exist in the dict</td></tr><tr><td>Pass to another function</td><td>Pass <code>x[&quot;a&quot;]</code> directly</td><td>Extract <code>x[&quot;a_values&quot;], x[&quot;a_indices&quot;]</code> and pass both</td></tr><tr><td>Operations</td><td><code>SparseTensor</code> class can have methods that work like regular tensors, e.g. <br> <code>y = x[&quot;a&quot;] + 1</code></td><td>Need to implement many new functions, e.g. <br> <code>y = add_sparse(x[&quot;a_values&quot;], x[&quot;a_indices&quot;], 1)</code></td></tr></tbody></table><h2 id="Bidirectional-Conversion">Bidirectional Conversion<a class="markdown-anchor" href="#Bidirectional-Conversion">&#xB6;</a></h2><p>Despite the benefits, lower-level stacks often ignore these abstractionsand choose to use a &quot;flat&quot; interface: their inputs &amp; outputs are a flat list of values / Tensors.This is because:(i) the abstraction may no longer be useful in lower level;(ii) a simple structure simplifies their implementation;(iii) a flat list is a data structure available even in lower-level languages &amp; systems.</p><p>Therefore, conversion from a nested structure to a plain list of values is important.This is often referred to as &quot;flatten&quot;.It is pretty straightforward to flatten a container recursively -- like the following <code>flatten</code> function:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">flatten</span>(<span class="hljs-params">container</span>):<br>  <span class="hljs-keyword">if</span> <span class="hljs-built_in">isinstance</span>(container, <span class="hljs-type">Sequence</span>):<br>    <span class="hljs-keyword">return</span> <span class="hljs-built_in">list</span>(itertools.chain.from_iterable(flatten(x) <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> container))<br>  <span class="hljs-keyword">elif</span> <span class="hljs-built_in">isinstance</span>(container, Mapping):<br>    <span class="hljs-keyword">return</span> flatten(<span class="hljs-built_in">list</span>(container.values()))<br>  <span class="hljs-keyword">elif</span> <span class="hljs-built_in">isinstance</span>(container, Tensor):<br>    <span class="hljs-keyword">return</span> [container]<br>  <span class="hljs-comment"># ... handle other containers</span><br><br>x, y, z = torch.rand(<span class="hljs-number">3</span>)<br>obj = [{<span class="hljs-string">&quot;a&quot;</span>: x, <span class="hljs-string">&quot;b&quot;</span>: [y, {<span class="hljs-string">&quot;c&quot;</span>: z}]}]<br>flatten(obj)  <span class="hljs-comment"># ==&gt; [x, y, z]</span><br></code></pre></td></tr></table></figure><p>The inverse of <code>flatten</code> is also important: given new values <code>[x2, y2, z2]</code>,we want the <code>unflatten</code> function below to construct <code>obj2</code> that has the samestructure as <code>obj</code>.</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">x2, y2, z2 = torch.rand(<span class="hljs-number">3</span>)<br>obj2 = unflatten([x2, y2, z2], ???)  <span class="hljs-comment"># ==&gt; [{&quot;a&quot;: x2, &quot;b&quot;: [y2, {&quot;c&quot;: z2}]}]</span><br></code></pre></td></tr></table></figure><p><code>unflatten</code> is a very handy utility. For example, to create a clone of <code>obj</code>on a different device, we simply do this:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">values = flatten(obj)<br>new_values = [x.to(device) <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> values]<br>new_obj = unflatten(new_values, ???)<br></code></pre></td></tr></table></figure><p>Without <code>unflatten</code>, every such functionality needs to be reimplemented as a recursivefunction, like PyTorch&apos;s <a href="https://github.com/pytorch/pytorch/blob/c54d18dbc7bb2f9fdd83c5de529702e5a02295c3/torch/utils/data/_utils/pin_memory.py#L48-L72" aria-label="pin_memory.py &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>pin_memory</code></a>.</p><h2 id="Implementation-of-unflatten">Implementation of <code>unflatten</code><a class="markdown-anchor" href="#Implementation-of-unflatten">&#xB6;</a></h2><p>How do we implement <code>unflatten</code>?Apparently, we need to give it a representation of structure (noted as a placeholder <code>???</code> in the above code).There are two high-level approaches to solve this problem:</p><ul><li><p>Schema-based: when flattening a container, explicitly record its structure/schema to be used for unflatten.Its API may look like this:</p><figure class="highlight python-repl"><table><tr><td class="code"><pre><code class="hljs python-repl"><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-keyword">from</span> jax.tree_util <span class="hljs-keyword">import</span> tree_flatten, tree_unflatten</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">obj = [<span class="hljs-number">3</span>, ([<span class="hljs-number">5</span>, <span class="hljs-number">6</span>], {<span class="hljs-string">&quot;name&quot;</span>: [<span class="hljs-number">7</span>, <span class="hljs-number">9</span>], <span class="hljs-string">&quot;name2&quot;</span>: <span class="hljs-number">3</span>})]</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">res, schema = tree_flatten(obj)</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">res     <span class="hljs-comment"># Flattened results:</span></span><br>[3, 5, 6, 7, 9, 3]<br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">schema  <span class="hljs-comment"># An explicit representation of the container&apos;s structure</span></span><br>PyTreeDef([*, ([*, *], {&apos;name&apos;: [*, *], &apos;name2&apos;: *})])<br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-comment"># Construct a nested container using the given values and the structure/schema:</span></span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">tree_unflatten(schema, [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>])</span><br>[1, ([2, 3], {&apos;name&apos;: [4, 5], &apos;name2&apos;: 6})]<br></code></pre></td></tr></table></figure><p>Examples: Detectron2&apos;s <code>flatten_to_tuple</code>, TensorFlow&apos;s <code>FetchMapper</code>, JAX&apos;s <code>pytree</code>.</p></li><li><p>Schema-less: use the entire nested container as an implicit representation of structure. Its interface looks like this:</p><figure class="highlight python-repl"><table><tr><td class="code"><pre><code class="hljs python-repl"><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-keyword">import</span> tensorflow <span class="hljs-keyword">as</span> tf</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">obj = [<span class="hljs-number">3</span>, ([<span class="hljs-number">5</span>, <span class="hljs-number">6</span>], {<span class="hljs-string">&quot;name&quot;</span>: [<span class="hljs-number">7</span>, <span class="hljs-number">9</span>], <span class="hljs-string">&quot;name2&quot;</span>: <span class="hljs-number">3</span>})]</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">tf.nest.flatten(obj)  <span class="hljs-comment"># Flattened results:</span></span><br>[3, 5, 6, 7, 9, 3]<br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-comment"># Construct a nested container that has same structure as obj, using the given list of values:</span></span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">tf.nest.pack_sequence_as(obj, [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>])</span><br>[1, ([2, 3], {&apos;name&apos;: [4, 5], &apos;name2&apos;: 6})]<br></code></pre></td></tr></table></figure><p>Examples: TensorFlow&apos;s <code>tf.nest</code>. DeepMind&apos;s <code>dm-tree</code>.</p></li></ul><p>The two approaches have some pros and cons:</p><ul><li>The schema-less approach has simpler API and implementation.</li><li>The schema-based approach likely has a more memory-efficient representation of schema,compared to using an entire container as schema.</li><li>An explicit schema representation allows more functionalities to be added by customizing the representation.</li></ul><h2 id="Applications">Applications<a class="markdown-anchor" href="#Applications">&#xB6;</a></h2><h3 id="JAX-Pytree">JAX Pytree<a class="markdown-anchor" href="#JAX-Pytree">&#xB6;</a></h3><p>JAX&apos;s low level components accept/return flat tensors, so functions can be transformed and optimized more easily.Since end-users need nested containers, JAX transformations supports <a href="https://jax.readthedocs.io/en/latest/pytrees.html" aria-label="Pytrees &#x2014; JAX  documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">pytree</a> containers,which by default includes flattening &amp; unflattening for common Python containers.It further allows users to register custom classes by<a href="https://jax.readthedocs.io/en/latest/_autosummary/jax.tree_util.register_pytree_node.html#jax.tree_util.register_pytree_node" aria-label="jax.tree_util.register_pytree_node &#x2014; JAX  documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>register_pytree_node</code></a>.</p><p>Pytree uses a schema-based implementation that we already show-cased above.</p><p>When we need to independently process each leaf of the container, JAX provides another handyfunction <a href="https://jax.readthedocs.io/en/latest/_autosummary/jax.tree_util.tree_map.html" aria-label="jax.tree_util.tree_map &#x2014; JAX  documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>tree_map</code></a>:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">tree_map</span>(<span class="hljs-params">f, obj</span>):<br>  values, schema = flatten(obj)<br>  new_values = [f(x) <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> values]<br>  <span class="hljs-keyword">return</span> unflatten(schema, new_values)<br></code></pre></td></tr></table></figure><p>PyTorch also adds a similar implementation of pytree at <a href="https://github.com/pytorch/pytorch/blob/0f32d71b40e78c068231421b51a5335c7f7b1dc5/torch/utils/_pytree.py" aria-label="_pytree.py &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>that is used in its FX tracing.</p><h3 id="Detectron2-TracingAdapter">Detectron2 <code>TracingAdapter</code><a class="markdown-anchor" href="#Detectron2-TracingAdapter">&#xB6;</a></h3><p><code>torch.jit.trace(model, inputs)</code> executes the model with given inputs, and returns a graph representationof the model&apos;s execution.This is one of the most common methods (and <a href="/blog/2022/TorchScript-Tracing-vs-Scripting/" aria-label="TorchScript: Tracing vs. Scripting" class="hint--top hint--rounded hint--no-animate hint--no-arrow">the best IMO</a>) how PyTorch models are exported today.However, it limits model&apos;s input &amp; output format.</p><p>In order to trace models with more complicated inputs &amp; outputs,I created the <a href="https://detectron2.readthedocs.io/en/latest/modules/export.html#detectron2.export.TracingAdapter" aria-label="detectron2.export &#x2014; detectron2 0.6 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>TracingAdapter</code></a>tool in detectron2, that flattens/unflattens a model&apos;s inputs and outputs into simple <code>Tuple[Tensor]</code> to make it traceable.A minimal implementation of it may look like this:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">TracingAdapter</span>(nn.Module):<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self, model, inputs</span>):<br>    <span class="hljs-built_in">super</span>().__init__()<br>    <span class="hljs-variable language_">self</span>.model = model<br>    <span class="hljs-variable language_">self</span>.flattened_inputs, <span class="hljs-variable language_">self</span>.inputs_schema = flatten(inputs)<br><br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">forward</span>(<span class="hljs-params">self, *flattened_inputs</span>):<br>    inputs = <span class="hljs-variable language_">self</span>.inputs_schema(flattened_inputs)  <span class="hljs-comment"># This does unflatten</span><br>    outputs = <span class="hljs-variable language_">self</span>.model(inputs)<br>    flattened_outputs, <span class="hljs-variable language_">self</span>.outputs_schema = flatten(outputs)<br>    <span class="hljs-keyword">return</span> flattened_outputs<br><span class="hljs-comment"># torch.jit.trace(model, inputs)   # Fails because inputs is a nested container</span><br>flattened_inputs, _ = flatten(inputs)<br>torch.jit.trace(TracingAdapter(model, inputs), flattened_inputs)  <span class="hljs-comment"># Succeeds</span><br></code></pre></td></tr></table></figure><p>where <code>flatten</code> uses a schema-based implementation that can be found in <a href="https://github.com/facebookresearch/detectron2/blob/9921a2caa585d4fa66c4b534b6fab6e74d89b582/detectron2/export/flatten.py" aria-label="flatten.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">this file</a>.Coincidentally, its interface looks like JAX&apos;s pytree:</p><figure class="highlight python-repl"><table><tr><td class="code"><pre><code class="hljs python-repl"><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-keyword">from</span> detectron2.export.flatten <span class="hljs-keyword">import</span> flatten_to_tuple <span class="hljs-keyword">as</span> flatten</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">obj = [<span class="hljs-number">3</span>, ([<span class="hljs-number">5</span>, <span class="hljs-number">6</span>], {<span class="hljs-string">&quot;name&quot;</span>: [<span class="hljs-number">7</span>, <span class="hljs-number">9</span>], <span class="hljs-string">&quot;name2&quot;</span>: <span class="hljs-number">3</span>})]</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">res, schema = flatten(obj)</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">res     <span class="hljs-comment"># Flattened results:</span></span><br>(3, 5, 6, 7, 9, 3)<br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">schema  <span class="hljs-comment"># An explicit representation of the container&apos;s structure</span></span><br>ListSchema(schemas=[IdentitySchema(), TupleSchema(schemas=[ListSchema(schemas=[IdentitySchema(), IdentitySchema()], sizes=[1, 1]), DictSchema(schemas=[ListSchema(schemas=[IdentitySchema(), IdentitySchema()], sizes=[1, 1]), IdentitySchema()], sizes=[2, 1], keys=[&apos;name&apos;, &apos;name2&apos;])], sizes=[2, 3])], sizes=[1, 5])<br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-comment"># Construct a nested container using the given values and the structure/schema:</span></span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">schema((<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>))</span><br>[1, ([2, 3], {&apos;name&apos;: [4, 5], &apos;name2&apos;: 6})]<br></code></pre></td></tr></table></figure><p>Perception models in Meta accept a wide range of inputs/outputs formats:they may take any number of images plus auxiliary data as inputs, andpredict boxes, masks, keypoints or any other interesting attributes as outputs.But deployment prefers a flat interface for optimizability and interoperability.<code>TracingAdapter</code>&apos;s automatic flattening and unflattening mechanism has freed engineers fromwriting format conversion glue code when deploying these models.</p><p>In addition to deployment, <code>TracingAdapter</code> is also useful in a few other places to smooththe experience of <code>torch.jit.trace</code>:</p><ul><li>Flop counting: <a href="https://github.com/facebookresearch/fvcore/blob/main/docs/flop_count.md" aria-label="flop_count.md &#xB7; facebookresearch/fvcore" class="hint--top hint--rounded hint--no-animate hint--no-arrow">fvcore&apos;s flop counter</a>uses tracing to obtain a graph of operators.To let it support counting of complex models,<a href="https://github.com/facebookresearch/detectron2/blob/fb36bdd5641754e1cf379fab2b68ca17e4764273/detectron2/utils/analysis.py#L55-L67" aria-label="analysis.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">wrapping the model with <code>TracingAdapter</code></a> is the easiest way.</li><li>Tensorboard graph visualization: PyTorch&apos;s tensorboard writer has a <a href="https://pytorch.org/docs/stable/tensorboard.html#torch.utils.tensorboard.writer.SummaryWriter.add_graph" aria-label="torch.utils.tensorboard &#x2014; PyTorch 1.13 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>add_graph</code></a>method that visualizes the graph structure in tensorboard.The method requires flattened inputs,therefore <code>TracingAdapter</code> can be <a href="https://github.com/facebookresearch/detectron2/issues/1607#issuecomment-808828813" aria-label="Can I show model graph in tensorboard? &#xB7; Issue #1607 &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">used like this</a>.</li><li>PyTorch&apos;s ONNX export is also based on tracing. So <code>TracingAdapter</code> is useful as well, e.g. <a href="https://github.com/facebookresearch/detectron2/blob/5aeb252b194b93dc2879b4ac34bc51a31b5aee13/tools/deploy/export_model.py#L123-L132" aria-label="export_model.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>.</li></ul><h3 id="TensorFlow-tf-nest">TensorFlow <code>tf.nest</code><a class="markdown-anchor" href="#TensorFlow-tf-nest">&#xB6;</a></h3><p><a href="https://www.tensorflow.org/versions/r2.9/api_docs/python/tf/nest/flatten" aria-label="tf.nest.flatten &#xA0;|&#xA0; TensorFlow v2.9.2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>tf.nest.flatten</code></a> and <a href="https://www.tensorflow.org/versions/r2.9/api_docs/python/tf/nest/pack_sequence_as" aria-label="tf.nest.pack_sequence_as &#xA0;|&#xA0; TensorFlow v2.9.2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>tf.nest.pack_sequence_as</code></a>implement schema-less flattening and unflattening.</p><p>The unflatten function requires a container, and it will flatten this container on-the-fly whilesimultaneously &quot;pack&quot; flat values into the structure of this container. Here is an official example (note that dict values are ordered by keys):</p><figure class="highlight python-repl"><table><tr><td class="code"><pre><code class="hljs python-repl"><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">container = { <span class="hljs-string">&quot;key3&quot;</span>: {<span class="hljs-string">&quot;c&quot;</span>: (<span class="hljs-string">&apos;alpha&apos;</span>, <span class="hljs-string">&apos;beta&apos;</span>), <span class="hljs-string">&quot;a&quot;</span>: (<span class="hljs-string">&apos;gamma&apos;</span>)},</span><br><span class="hljs-meta prompt_">...</span> <span class="language-python">              <span class="hljs-string">&quot;key1&quot;</span>: {<span class="hljs-string">&quot;e&quot;</span>: <span class="hljs-string">&quot;val1&quot;</span>, <span class="hljs-string">&quot;d&quot;</span>: <span class="hljs-string">&quot;val2&quot;</span>} }</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">flat_sequence = [<span class="hljs-string">&apos;val2&apos;</span>, <span class="hljs-string">&apos;val1&apos;</span>, <span class="hljs-number">3.0</span>, <span class="hljs-number">1.0</span>, <span class="hljs-number">2.0</span>]</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">tf.nest.pack_sequence_as(container, flat_sequence)</span><br>{&apos;key3&apos;: {&apos;c&apos;: (1.0, 2.0), &apos;a&apos;: 3.0}, &apos;key1&apos;: {&apos;e&apos;: &apos;val1&apos;, &apos;d&apos;: &apos;val2&apos;}}<br></code></pre></td></tr></table></figure><p><code>tf.nest.{flatten,pack_sequence_as}</code> are widely used in TensorFlow because many low-level components have a flat interface, especially forinterop with C APIs.</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">~/tensorflow/tensorflow/python$ ag pack_sequence_as | wc -l<br>243<br></code></pre></td></tr></table></figure><p><a href="https://www.tensorflow.org/api_docs/python/tf/nest/map_structure" aria-label="tf.nest.map_structure &#xA0;|&#xA0; TensorFlow v2.11.0" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>tf.nest.map_structure</code></a>has the same functionality as JAX&apos;s <code>tree_map</code>.</p><h3 id="TensorFlow-FetchMapper">TensorFlow <code>FetchMapper</code><a class="markdown-anchor" href="#TensorFlow-FetchMapper">&#xB6;</a></h3><p>TFv1&apos;s <code>session.run(fetches)</code> supports fetching nested containers.This is demonstrated in an example from the<a href="https://www.tensorflow.org/versions/r1.15/api_docs/python/tf/Session#run" aria-label="tf.Session &#xA0;|&#xA0; TensorFlow v1.15.0" class="hint--top hint--rounded hint--no-animate hint--no-arrow">official documentation</a>:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">a = tf.constant([<span class="hljs-number">10</span>, <span class="hljs-number">20</span>])<br>b = tf.constant([<span class="hljs-number">1.0</span>, <span class="hljs-number">2.0</span>])<br>MyData = collections.namedtuple(<span class="hljs-string">&apos;MyData&apos;</span>, [<span class="hljs-string">&apos;a&apos;</span>, <span class="hljs-string">&apos;b&apos;</span>])<br><br>v = session.run({<span class="hljs-string">&apos;k1&apos;</span>: MyData(a, b), <span class="hljs-string">&apos;k2&apos;</span>: [b, a]})<br><span class="hljs-comment"># v is a dict with:</span><br><span class="hljs-comment"># v[&apos;k1&apos;] is a MyData namedtuple with &apos;a&apos; (the numpy array [10, 20]) and</span><br><span class="hljs-comment">#                                     &apos;b&apos; (the numpy array [1.0, 2.0])</span><br><span class="hljs-comment"># v[&apos;k2&apos;] is a list with the numpy array [1.0, 2.0] and the numpy array # [10, 20].</span><br></code></pre></td></tr></table></figure><p>This powerful interface exists in TF&apos;s Python client only.The client interacts with the C API&apos;s <a href="https://github.com/tensorflow/tensorflow/blob/a424c0b1d580a35768d75502ed68c6ef4d09f4e7/tensorflow/c/c_api.h#L1308-L1321" aria-label="c_api.h &#xB7; tensorflow/tensorflow" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>TF_SessionRun</code></a>which only accepts a plain array of inputs/outputs.Therefore, the client needs to:</p><ol><li>Flatten the container to a plain array of tensors</li><li>Send this array to the C API to obtain an array of results</li><li>Unflatten / reconstruct the container using the results</li></ol><p>The flatten/unflatten logic uses a schema-based implementation in the client&apos;s <a href="https://github.com/tensorflow/tensorflow/blob/d8ce9f9c301d021a69953134185ab728c1c248d3/tensorflow/python/client/session.py#L209" aria-label="session.py &#xB7; tensorflow/tensorflow" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>FetchMapper</code></a>.This implementation is a bit more complicated due toan extra guarantee thatthe flattened tensors are unique. (This is to ensure the client won&apos;t fetch the same tensor twice in one call;this cannot be done by using <code>tf.nest</code>.)</p><p>In addition to builtin Python containers, <code>FetchMapper</code> supports a few other TF containers(such as <code>SparseTensor</code>) and can be extended to new containers by <a href="https://github.com/tensorflow/tensorflow/blob/d8ce9f9c301d021a69953134185ab728c1c248d3/tensorflow/python/client/session.py#L143" aria-label="session.py &#xB7; tensorflow/tensorflow" class="hint--top hint--rounded hint--no-animate hint--no-arrow">registering conversion functions</a>.</p><h3 id="DeepMind-tree-library">DeepMind <code>tree</code> library<a class="markdown-anchor" href="#DeepMind-tree-library">&#xB6;</a></h3><p>DeepMind has a <a href="https://github.com/deepmind/tree" aria-label="deepmind/tree: tree is a library for working with nested data structures" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>tree</code> library</a> as a standalone alternative to <code>tf.nest</code>:</p><table><thead><tr><th><code>deepmind/tree</code></th><th><code>tf.nest</code></th><th><code>jax.tree_util</code></th></tr></thead><tbody><tr><td><a href="https://tree.readthedocs.io/en/latest/api.html#tree.flatten" aria-label="API Reference &#x2014; Tree  documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>tree.flatten</code></a></td><td><code>tf.nest.flatten</code></td><td><code>jax.tree_util.tree_flatten</code></td></tr><tr><td><a href="https://tree.readthedocs.io/en/latest/api.html#tree.unflatten_as" aria-label="API Reference &#x2014; Tree  documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>tree.unflatten_as</code></a></td><td><code>tf.nest.pack_sequence_as</code></td><td><code>jax.tree_util.tree_unflatten</code></td></tr><tr><td><a href="https://tree.readthedocs.io/en/latest/api.html#tree.map_structure" aria-label="API Reference &#x2014; Tree  documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>tree.map_structure</code></a></td><td><code>tf.nest.map_structure</code></td><td><code>jax.tree_util.tree_map</code></td></tr></tbody></table>]]></content>
    
    
    <summary type="html">&lt;p&gt;This post is about a small functionality that is found useful in TensorFlow / JAX / PyTorch.&lt;/p&gt;
&lt;p&gt;Low-level components of these systems often use a plain list of values/tensors
as inputs &amp;amp; outputs.
However, end-users that develop models often want to work with more
complicated data structures:
&lt;code&gt;Dict[str, Any]&lt;/code&gt;, &lt;code&gt;List[Any]&lt;/code&gt;, custom classes, and their nested combinations.
Therefore, we need bidirectional conversion between nested structures and a plain list of tensors.
I found that different libraries invent similar approaches to solve this problem, and it&amp;apos;s interesting to list them here.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Deep Learning" scheme="https://ppwwyyxx.com/blog/tags/Deep-Learning/"/>
    
    <category term="PyTorch" scheme="https://ppwwyyxx.com/blog/tags/PyTorch/"/>
    
  </entry>
  
  <entry>
    <title>TorchScript: Tracing vs. Scripting</title>
    <link href="https://ppwwyyxx.com/blog/2022/TorchScript-Tracing-vs-Scripting/"/>
    <id>https://ppwwyyxx.com/blog/2022/TorchScript-Tracing-vs-Scripting/</id>
    <published>2022-05-23T06:59:00.000Z</published>
    <updated>2022-05-23T06:59:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>PyTorch provides two methods to turn an <code>nn.Module</code> into agraph represented in TorchScript format: tracing and scripting.This article will:</p><ol><li>Compare their pros and cons, with a focus on useful tips for tracing.</li><li>Try to convince you that <strong><code>torch.jit.trace</code> should be preferred over <code>torch.jit.script</code></strong>for deployment of non-trivial models.</li></ol><span id="more"></span><p>The second point might be an uncommon opinion:If I Google &quot;tracing vs scripting&quot;, the <a href="https://paulbridger.com/posts/mastering-torchscript/" aria-label="Mastering TorchScript: Tracing vs Scripting, Device Pinning, Direct Graph Modification | paulbridger.com" class="hint--top hint--rounded hint--no-animate hint--no-arrow">first article</a>recommends scripting as default.But tracing has many advantages.In fact, by the time I left, <strong>&quot;tracing as default, scripting only when necessary&quot;</strong> is thestrategy all detection &amp; segmentation models in Facebook/Meta products are deployed.</p><p>Why tracing is better? TL;DR: (i) it will not damage the code quality; (ii) its main limitations can beaddressed by mixing with scripting.</p><h2 id="Terminology">Terminology<a class="markdown-anchor" href="#Terminology">&#xB6;</a></h2><p>We start by disambiguate some common terminologies:</p><ul><li><p><strong>Export</strong>: refers to the process that turns a model written in eager-mode Pythoncode into a graph that describes the computation.</p></li><li><p><strong>Tracing</strong>: An export method. It runs a model with certain inputs, and &quot;traces / records&quot; all the operationsthat are executed into a graph.</p><p><code>torch.jit.trace</code> is an export API that uses tracing, used like <code>torch.jit.trace(model, input)</code>.See its <a href="https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html#tracing-modules" aria-label="Introduction to TorchScript &#x2014; PyTorch Tutorials 1.13.1+cu117 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">tutorial</a>and <a href="https://pytorch.org/docs/stable/generated/torch.jit.trace.html" aria-label="torch.jit.trace &#x2014; PyTorch 1.13 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">API</a>.</p></li><li><p><strong>Scripting</strong>: Another export method. It parses the Python source code of the model, and compiles the code into agraph.</p><p><code>torch.jit.script</code> is an export API that uses scripting, used like <code>torch.jit.script(model)</code>.See its <a href="https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html#using-scripting-to-convert-modules" aria-label="Introduction to TorchScript &#x2014; PyTorch Tutorials 1.13.1+cu117 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">tutorial</a>and <a href="https://pytorch.org/docs/stable/generated/torch.jit.script.html" aria-label="torch.jit.script &#x2014; PyTorch 1.13 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">API</a>.</p></li><li><p><strong>TorchScript</strong>: This is an <strong>overloaded term</strong></p><ul><li>It often refers to the <strong>representation / format</strong> of the exported graph.</li><li>But sometimes it refers to the scripting <strong>export method</strong>.</li></ul><p>To avoid confusion, I&apos;ll never use &quot;TorchScript&quot; alone in this article.I&apos;ll use &quot;TS-format&quot; to refer to the format, and &quot;scripting&quot; to refer to the export method.</p><p>Because this term is used with ambiguity, it may have caused the impression that &quot;scripting&quot; is the&quot;official / preferred&quot; way to create a TS-format model. But that&apos;s not necessarily true.</p></li><li><p><strong>(Torch)Scriptable</strong>: A model is &quot;scriptable&quot; if <code>torch.jit.script(model)</code> succeeds, i.e. it canbe exported by scripting.</p></li><li><p><strong>Traceable</strong>: A model is &quot;traceable&quot; if <code>torch.jit.trace(model, input)</code> succeeds for atypical input.</p></li><li><p><strong>Generalize</strong>: A traced model (returned object of <code>trace()</code>) &quot;generalizes&quot; to other inputs(different from the inputs given during tracing), if it can inference correctly when given other inputs.Scripted models always generalize.</p></li><li><p><strong>Dynamic control flow</strong> or data-dependent control flow: control flow where the operatorsto be executed depend on the input data, e.g. for a <code>Tensor</code> x:</p><ul><li><code>if x[0] == 4: x += 1</code> is a dynamic control flow.</li><li><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">model: nn.Sequential = ...<br><span class="hljs-keyword">for</span> m <span class="hljs-keyword">in</span> model:<br>  x = m(x)<br></code></pre></td></tr></table></figure>is NOT a dynamic control flow.<figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span>(nn.Module):<br>  backbone: nn.Module<br>  head: Optiona[nn.Module]<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">forward</span>(<span class="hljs-params">self, x</span>):<br>    x = <span class="hljs-variable language_">self</span>.backbone(x)<br>    <span class="hljs-keyword">if</span> <span class="hljs-variable language_">self</span>.head <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:<br>        x = <span class="hljs-variable language_">self</span>.head(x)<br>    <span class="hljs-keyword">return</span> x<br></code></pre></td></tr></table></figure>is NOT a dynamic control flow.</li></ul></li></ul><h2 id="The-Cost-of-Scriptability">The Cost of Scriptability<a class="markdown-anchor" href="#The-Cost-of-Scriptability">&#xB6;</a></h2><p>If anyone says &quot;we&apos;ll make Python better by writing a compiler for it&quot;, you should immediatelybe alarmed and know that this is extremely difficult.Python is too big and too dynamic. A compiler can only support a subset of its syntax features and builtins, at best --the scripting compiler in PyTorch is no exception.</p><p>What subset of Python does this compiler support?A rough answer is: the compiler has<strong>good support for the most basic syntax, but medium to no support for anything more complicated</strong> (classes, builtins like <code>range</code> and <code>zip</code>, dynamic types, etc.).But there is no clear answer: even the developers of the compiler usually need to run the code to see if it can be compiled or not.</p><p>The incomplete Python compiler limits how users can write code.Though there isn&apos;t a clear list of constraints,I can tell from my experience what impact they have had on large projects:<strong>code quality is the cost of scriptability</strong>.</p><h3 id="Impact-on-Most-Projects">Impact on Most Projects<a class="markdown-anchor" href="#Impact-on-Most-Projects">&#xB6;</a></h3><p>To make their code scriptable / compilable by the scripting compiler,most projects choose to stay on the &quot;safe side&quot; to <strong>only use basic syntax of Python</strong>:no/few custom structures, no builtins, no inheritance, no <code>Union</code>, no <code>**kwargs</code>, no lambda, no dynamic types, etc.</p><p>This is because these &quot;advanced&quot; compiler features are either not supported at all, or with &quot;partial support&quot;which is not robust enough: they may work in some cases but fail in others.And because there is no clear spec of what is supported,users are unable to reason about or workaround the failures.Therefore, eventually users move to and stay on the safe side.</p><p>The terrible consequence is that:<strong>developers stop making abstractions / exploring useful language features</strong>due to concerns in scriptability.</p><p>A related hack that many projects do is to rewrite part of the code for scripting:create a separate, inference-only forward codepath that makes the compiler happy.This also makes the project harder to maintain.</p><h3 id="Impact-on-Detectron2">Impact on Detectron2<a class="markdown-anchor" href="#Impact-on-Detectron2">&#xB6;</a></h3><p>Detectron2 supports scripting, but the story was a bit different: it did not go downhill in code quality which we value a lot in research.Instead, with some creativity and direct support from PyTorch team (and some volunteered help from Alibaba engineers), we managed to make most modelsscriptable without removing any abstractions.</p><p>However, it is not an easy task:we had to add dozens of syntax fixes to the compiler, find creative workarounds,and develop some hacky patches in detectron2 that are in<a href="https://github.com/facebookresearch/detectron2/blob/e091a07ef573915056f8c2191b774aad0e38d09c/detectron2/export/torchscript_patch.py" aria-label="torchscript_patch.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">this file</a>(which honestly could affect maintainability in the long term).I would not recommend other large projects to aim for &quot;scriptability without losing abstractions&quot; unlessthey are also closely supported by PyTorch team.</p><h3 id="Recommendation">Recommendation<a class="markdown-anchor" href="#Recommendation">&#xB6;</a></h3><p>If you think &quot;scripting seems to work for my project&quot;so let&apos;s embrace it, I might advise against it for the following reasons,based on my past experiences with a few projects that support scripting:</p><ul><li><p><strong>What &quot;works&quot; might be more brittle than you think</strong> (unless you limit yourself to the basic syntax):Your code might happen to compile now, but one day you&apos;ll add a few innocent changes to your modeland find that the compiler refuses it.</p></li><li><p><strong>Basic syntax is not enough</strong>:Even if more complex abstractions don&apos;t appear necessary to your project at the moment,if the project is expected to grow, it will require more language features in the future.</p><p>Take a multi-task detector for example:</p><ol><li>There could be 10s of inputs, so it&apos;s preferable to use some structures/classes.</li><li>The same data can have different representations (e.g. different ways to represent a segmentation mask),which demands <code>Union</code> or more dynamic types.</li><li>There are many architectural choices of a detector, which makes inheritance useful.</li></ol><p>Large, growing projects definitely need evolving abstractions to stay healthy.</p></li><li><p><strong>Code quality could severely deteriorate</strong>:Ugly code starts to accumulate, because clean code sometimes just doesn&apos;t compile.Also, due to syntax limitations of the compiler,abstractions cannot be easily made to clean up the ugliness.The health of the project gradually goes downhill.</p></li></ul><p>Below is a complaint in PyTorch issues.The issue itself is just one small papercut of scripting,but similar complaints were heard many times.The status-quo is: scripting forces you to write ugly code, so only use it when necessary.</p><img src="/blog/2022/TorchScript-Tracing-vs-Scripting/ugly.jpg" class="center" width="500"><h2 id="Make-a-Model-Trace-and-Generalize">Make a Model Trace and Generalize<a class="markdown-anchor" href="#Make-a-Model-Trace-and-Generalize">&#xB6;</a></h2><h3 id="The-Cost-of-Traceability">The Cost of  Traceability<a class="markdown-anchor" href="#The-Cost-of-Traceability">&#xB6;</a></h3><p>What it takes to make a model traceable is very clear, and has a much smaller impact on code health.</p><ol><li><p>First, neither scripting nor tracing works if the model is not even a proper single-device, connected graph representable in TS-format.For example, if the model has <code>DataParallel</code> submodules, or if the modelconverts tensors to numpy arrays and calls OpenCV functions, etc, you&apos;ll have to refactor it.</p><p>Apart from this obvious constraint, there are only two extra requirements for traceability.</p></li><li><p><strong>Input/output format</strong></p><p>Model&apos;s inputs/outputs have to be <code>Union[Tensor, Tuple[Tensor], Dict[str, Tensor]]</code>or their nested combinations. Note that values in a dict have to belong to the same type.</p><p>Similar constraints exist for scripting as well.However, in tracing the constraint does not apply to submodules:submodules can use <strong>any</strong> input/output format: dicts of Any, classes, kwargs, anything that Python supports.Only the top-level model is required to use the constraint format.</p><p>This makes the constraint very easy to satisfy.If the model uses richer formats, just create a simple wrapper around it that converts to/from<code>Tuple[Tensor]</code>.Detectron2 even automates this for all its models by a <a href="https://github.com/facebookresearch/detectron2/blob/e091a07ef573915056f8c2191b774aad0e38d09c/detectron2/export/flatten.py#L186-L208" aria-label="flatten.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">universal wrapper</a>like this:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">outputs = model(inputs)   <span class="hljs-comment"># inputs/outputs are rich structure, e.g. dicts or classes</span><br><span class="hljs-comment"># torch.jit.trace(model, inputs)  # FAIL! unsupported format</span><br>adapter = TracingAdapter(model, inputs)<br>traced = torch.jit.trace(adapter, adapter.flattened_inputs)  <span class="hljs-comment"># Can now trace the model</span><br><br><span class="hljs-comment"># Traced model can only produce flattened outputs (tuple of tensors):</span><br>flattened_outputs = traced(*adapter.flattened_inputs)<br><span class="hljs-comment"># Adapter knows how to convert it back to the rich structure (new_outputs == outputs):</span><br>new_outputs = adapter.outputs_schema(flattened_outputs)<br></code></pre></td></tr></table></figure><a href="/blog/2022/Automatically-Flatten-Unflatten-Nested-Containers/" aria-label="Automatically Flatten &amp; Unflatten Nested Containers" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Automatically Flatten &amp; Unflatten Nested Containers</a> has more details on how this adapter is implemented.</li><li><p><strong>Symbolic shapes</strong>:</p><p>Expressions like <code>tensor.size(0)</code>, <code>tensor.size()[1]</code>, <code>tensor.shape[2]</code>are integers in eager mode, but <code>Tensor</code>s in tracing mode.Such difference is necessary so that during tracing, shape computation can becaptured as symbolic operations in the graph.An example is given in the next section about generalization.</p><p>Due to different return types,a model may be untraceable if parts of it assume shapes are integers.This usually can be fixed quite easily by handling both types in the code.A helpful function is <a href="https://pytorch.org/docs/stable/jit_language_reference.html#torch.jit.is_tracing" aria-label="TorchScript Language Reference &#x2014; PyTorch 1.13 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>torch.jit.is_tracing</code></a>which checks if the code is executed in tracing mode.</p></li></ol><p>That&apos;s all it takes for traceability - most importantly, <strong>any Python syntax</strong> is allowed in model implementation, because tracing does not careabout syntax at all.</p><h3 id="Generalization-Problem">Generalization Problem<a class="markdown-anchor" href="#Generalization-Problem">&#xB6;</a></h3><p>Just being &quot;traceable&quot; is not sufficient.The biggest problem with tracing, is that it may not generalize to other inputs.This problem happens in the following cases:</p><ol><li><p><strong>Dynamic control flow</strong>:</p><figure class="highlight python-repl"><table><tr><td class="code"><pre><code class="hljs python-repl"><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">f</span>(<span class="hljs-params">x</span>):</span><br><span class="hljs-meta prompt_">...</span> <span class="language-python">  <span class="hljs-keyword">return</span> torch.sqrt(x) <span class="hljs-keyword">if</span> x.<span class="hljs-built_in">sum</span>() &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> torch.square(x)</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">m = torch.jit.trace(f, torch.tensor(<span class="hljs-number">3</span>))</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-built_in">print</span>(m.code)</span><br>def f(x: Tensor) -&gt; Tensor:<br>  return torch.sqrt(x)<br></code></pre></td></tr></table></figure><p>In this example, due to dynamic control flow,the trace only keeps one branch of the condition, and will not generalize to certain (negative) inputs.</p></li><li><p><strong>Capture variables as constants</strong>:</p><figure class="highlight python-repl"><table><tr><td class="code"><pre><code class="hljs python-repl"><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">a, b = torch.rand(<span class="hljs-number">1</span>), torch.rand(<span class="hljs-number">2</span>)</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">f1</span>(<span class="hljs-params">x</span>): <span class="hljs-keyword">return</span> torch.arange(x.shape[<span class="hljs-number">0</span>])</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">f2</span>(<span class="hljs-params">x</span>): <span class="hljs-keyword">return</span> torch.arange(<span class="hljs-built_in">len</span>(x))</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-comment"># See if the two traces generalize from a to b:</span></span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">torch.jit.trace(f1, a)(b)</span><br>tensor([0, 1])<br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">torch.jit.trace(f2, a)(b)</span><br>tensor([0])  # WRONG!<br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-comment"># Why f2 does not generalize? Let&apos;s compare their code:</span></span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-built_in">print</span>(torch.jit.trace(f1, a).code, torch.jit.trace(f2, a).code)</span><br>def f1(x: Tensor) -&gt; Tensor:<br>  _0 = ops.prim.NumToTensor(torch.size(x, 0))<br>  _1 = torch.arange(annotate(number, _0), dtype=None, layout=0, device=torch.device(&quot;cpu&quot;), pin_memory=False)<br>  return _1<br>def f2(x: Tensor) -&gt; Tensor:<br>  _0 = torch.arange(1, dtype=None, layout=0, device=torch.device(&quot;cpu&quot;), pin_memory=False)<br>  return _0<br></code></pre></td></tr></table></figure><p>Intermediate computation results of a non-Tensor type (in this case, an int type) may be captured as constants, using thevalue observed during tracing. This causes the trace to not generalize.</p><p>In addition to <code>len()</code>, this issue can also appear in:</p><ul><li><code>.item()</code> which converts tensors to int/float.</li><li>Any other code that converts torch types to numpy/python primitives.</li><li>A few problematic operators, e.g. <a href="https://github.com/pytorch/pytorch/issues/49852" aria-label="Advanced Indexing does not trace correctly for tensor shape that has leading 1s &#xB7; Issue #49852 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">advanced indexing</a>.</li></ul></li><li><p><strong>Capture device</strong>:</p><figure class="highlight python-repl"><table><tr><td class="code"><pre><code class="hljs python-repl"><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">f</span>(<span class="hljs-params">x</span>):</span><br><span class="hljs-meta prompt_">...</span> <span class="language-python">  <span class="hljs-keyword">return</span> torch.arange(x.shape[<span class="hljs-number">0</span>], device=x.device)</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">m = torch.jit.trace(f, torch.tensor([<span class="hljs-number">3</span>]))</span><br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python"><span class="hljs-built_in">print</span>(m.code)</span><br>def f(x: Tensor) -&gt; Tensor:<br>  _0 = ops.prim.NumToTensor(torch.size(x, 0))<br>  _1 = torch.arange(annotate(number, _0), dtype=None, layout=0, device=torch.device(&quot;cpu&quot;), pin_memory=False)<br>  return _1<br><span class="hljs-meta prompt_">&gt;&gt;&gt;</span> <span class="language-python">m(torch.tensor([<span class="hljs-number">3</span>]).cuda()).device</span><br>device(type=&apos;cpu&apos;)  # WRONG!<br></code></pre></td></tr></table></figure><p>Similarly, operators that accept a <code>device</code> argument will remember the device used during tracing (this canbe seen in <code>m.code</code>).So the trace may not generalize to inputs on a different device.Such generalization is almost never needed, because deployment usually has a target device.</p></li></ol><h3 id="Let-Tracing-Generalize">Let Tracing Generalize<a class="markdown-anchor" href="#Let-Tracing-Generalize">&#xB6;</a></h3><p>The above problems are annoying and often silent (warnings, but no errors),but they can be successfully addressed by good practice and tools:</p><ul><li><p><strong>Pay attention to <code>TracerWarning</code></strong>: In the first two examples above, <code>torch.jit.trace</code> actually emits warnings.The first example prints:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">a.py:3: TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect.<br>We can&apos;t record the data flow of Python values, so this value will be treated as a constant in the future.<br>This means that the trace might not generalize to other inputs!<br>if x.sum() &gt; 0:<br></code></pre></td></tr></table></figure><p>Paying attention to these warnings (or even better, <a href="https://docs.python.org/3/library/warnings.html#warnings.catch_warnings" aria-label="warnings &#x2014; Warning control &#x2014; Python 3.11.1 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">catch them</a>)will expose most generalization problems of tracing.</p><p>Note that the &quot;capture device&quot; case does not print warnings because tracing was not designed to support such generalization at all.</p></li><li><p><strong>Unittests for parity</strong>: Unittests should be done after export and before deployment, to verify thatthe exported model produces the same outputs as the original eager-mode model, i.e.</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><code class="hljs plaintext">assert allclose(torch.jit.trace(model, input1)(input2), model(input2))<br></code></pre></td></tr></table></figure><p>If generalization across shapes is needed (not always needed), <code>input2</code> should have differentshapes from <code>input1</code>.</p><p>Detectron2 has many generalization tests, e.g. <a href="https://github.com/facebookresearch/detectron2/blob/e091a07ef573915056f8c2191b774aad0e38d09c/tests/test_export_torchscript.py#L166" aria-label="test_export_torchscript.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">this</a>and <a href="https://github.com/facebookresearch/detectron2/blob/e091a07ef573915056f8c2191b774aad0e38d09c/tests/modeling/test_roi_heads.py#L278" aria-label="test_roi_heads.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">this</a>.Once a gap is found, inspecting the code of the exported TS-format model can uncover the place whereit fails to generalize.</p></li><li><p><strong>Avoid unnecessary &quot;special case&quot; conditions</strong>:Avoid conditions like</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">if</span> x.numel() &gt; <span class="hljs-number">0</span>:<br>  output = <span class="hljs-variable language_">self</span>.layers(x)<br><span class="hljs-keyword">else</span>:<br>  output = torch.zeros((<span class="hljs-number">0</span>, C, H, W))  <span class="hljs-comment"># Create empty outputs</span><br></code></pre></td></tr></table></figure><p>that handles special cases such as empty inputs.Instead, improve <code>self.layers</code> or its underlying kernel so it supports empty inputs.This would result in cleaner code and also improve tracing.This is why I&apos;m involved in many PyTorch issues that improve support for emptyinputs, such as<a href="https://github.com/pytorch/pytorch/issues/12013" aria-label="[Feature Request] Make nn layers accept empty batch size &#xB7; Issue #12013 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">#12013</a>,<a href="https://github.com/pytorch/pytorch/issues/36530" aria-label="Empty batch support for SyncBatchNorm &#xB7; Issue #36530 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">#36530</a>,<a href="https://github.com/pytorch/pytorch/issues/56998" aria-label="Empty channel support in convolution &#xB7; Issue #56998 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">#56998</a>.Most PyTorch operations work perfectly with empty inputs,so such branching is hardly needed.</p></li><li><p><strong>Use symbolic shapes</strong>: As mentioned earlier, <code>tensor.size()</code> returns <code>Tensor</code> during tracing, sothat shape computations are captured in the graph.Users should avoid accidentally turning tensor shapes into constants:</p><ul><li>Use <code>tensor.size(0)</code> instead of <code>len(tensor)</code> because the latter is an int.For custom classes, implement a <code>.size</code> method or use <code>.__len__()</code> instead of <code>len()</code>, e.g. <a href="https://github.com/facebookresearch/detectron2/blob/e091a07ef573915056f8c2191b774aad0e38d09c/detectron2/structures/instances.py#L142-L146" aria-label="instances.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">like here</a>.</li><li>Do not convert sizes by <code>int()</code> or <code>torch.as_tensor</code> because they will capture constants.<a href="https://github.com/facebookresearch/detectron2/blob/e091a07ef573915056f8c2191b774aad0e38d09c/detectron2/layers/wrappers.py#L16-L35" aria-label="wrappers.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">This helper function</a>is useful to convert sizes into a tensor, in a way that works in both tracing and eager mode.</li></ul></li><li><p><strong>Mix tracing and scripting</strong>: they can be mixed together, so you can use scriptingon the small portion of code that tracing does not work correctly.This can <strong>fix almost all problems of tracing</strong>. More on this below.</p></li></ul><h2 id="Mix-Tracing-and-Scripting">Mix Tracing and Scripting<a class="markdown-anchor" href="#Mix-Tracing-and-Scripting">&#xB6;</a></h2><p>Tracing and scripting both have their own problems, and thebest solution is usually to mix them together.This gives us <strong>the best of both worlds</strong>.</p><p>To minimize the negative impact on code quality,we should use tracing for the majority of logic, and <strong>use scripting only when necessary</strong>.</p><ol><li><p><strong>Use <code>@script_if_tracing</code></strong>: Inside <code>torch.jit.trace</code>, the <a href="https://pytorch.org/docs/stable/generated/torch.jit.script_if_tracing.html" aria-label="torch.jit.script_if_tracing &#x2014; PyTorch 1.13 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>@script_if_tracing</code></a>decorator can compile functions by scripting.Typically, this only requires a small refactor of the forward logic to separate the parts that need tobe compiled (the parts with control flow):</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">forward</span>(<span class="hljs-params">self, ...</span>):<br>  <span class="hljs-comment"># ... some forward logic</span><br><span class="hljs-meta">  @torch.jit.script_if_tracing</span><br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">_inner_impl</span>(<span class="hljs-params">x, y, z, flag: <span class="hljs-built_in">bool</span></span>):<br>      <span class="hljs-comment"># use control flow, etc.</span><br>      <span class="hljs-keyword">return</span> ...<br>  output = _inner_impl(x, y, z, flag)<br>  <span class="hljs-comment"># ... other forward logic</span><br></code></pre></td></tr></table></figure><p>By scripting only the parts that need it,the code quality damage is strictly smaller than making the entire model scriptable,and it does not affect the module&apos;s forward interface at all.</p><p>The function decorated by <code>@script_if_tracing</code> has to be a pure function that does not contain modules.Therefore, sometimes a bit more refactoring is needed:</p><div class="code-grid-wrapper"><table class="code-grid"><colgroup><col span="1" style="width: 50%;"><col span="1" style="width: 50%;"> </colgroup>    <tr><th>Before Refactoring</th><th>After Refactoring</th></tr> <tr><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># This branch cannot be compiled by</span><br><span class="hljs-comment"># @script_if_tracing, because it</span><br><span class="hljs-comment"># refers to `self.layers`</span><br><span class="hljs-keyword">if</span> x.numel() &gt; <span class="hljs-number">0</span>:<br>  x = preprocess(x)<br>  output = <span class="hljs-variable language_">self</span>.layers(x)<br><span class="hljs-keyword">else</span>:<br>  <span class="hljs-comment"># Create empty outputs</span><br>  output = torch.zeros(...)<br></code></pre></td></tr></table></figure></td><td><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-comment"># This branch can be compiled by @script_if_tracing</span><br><span class="hljs-keyword">if</span> x.numel() &gt; <span class="hljs-number">0</span>:<br>  x = preprocess(x)<br><span class="hljs-keyword">else</span>:<br>  <span class="hljs-comment"># Create empty inputs</span><br>  x = torch.zeros(...)<br><span class="hljs-comment"># Needs to make sure self.layers accept empty</span><br><span class="hljs-comment"># inputs. If necessary, add such condition branch</span><br><span class="hljs-comment"># into self.layers as well.</span><br>output = <span class="hljs-variable language_">self</span>.layers(x)<br></code></pre></td></tr></table></figure></td></tr></table></div><p>In fact, for most vision models, dynamic control flow is needed only in a few submodules whereit&apos;s easy to be scriptable.To show how rare it is needed, the entire detectron2 only has two functions decorated with <code>@script_if_tracing</code> due to control flows:<a href="https://github.com/facebookresearch/detectron2/blob/e091a07ef573915056f8c2191b774aad0e38d09c/detectron2/layers/mask_ops.py#L73-L76" aria-label="mask_ops.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">paste_masks</a>and <a href="https://github.com/facebookresearch/detectron2/blob/e091a07ef573915056f8c2191b774aad0e38d09c/detectron2/structures/keypoints.py#L164-L165" aria-label="keypoints.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">heatmaps_to_keypoints</a>,both for post-processing only.A few other functions are also decorated to generalize across devices (a very rare requirement).</p></li><li><p><strong>Use scripted / traced submodules</strong>:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">model.submodule = torch.jit.script(model.submodule)<br>torch.jit.trace(model, inputs)<br></code></pre></td></tr></table></figure><p>In this example, suppose <code>submodule</code> cannot be traced correctly, we can script it before tracing.However I do not recommend it.If possible, I will suggest using <code>@script_if_tracing</code>inside <code>submodule.forward</code> instead,so that scripting is limited to the internals of the submodule,without affecting the module&apos;s interface.</p><p>And similarly,</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">model.submodule = torch.jit.trace(model.submodule, submodule_inputs)<br>torch.jit.script(model)<br></code></pre></td></tr></table></figure><p>this uses a traced submodule during scripting.This looks nice, but is not so useful in practice: it will affect the interfaceof <code>submodule</code>, requiring it to only accept/return <code>Tuple[Tensor]</code> -- this is abig constraint that might hurt code quality even more than scripting.</p><p>A rare scenario where &quot;tracing a submodule&quot; is useful, is this:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span>(nn.Module):<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">forward</span>(<span class="hljs-params">self, x</span>):<br>    <span class="hljs-comment"># Dispatch to different submodules based on a dynamic, data-dependent condition:</span><br>    <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>.submodule1(x) <span class="hljs-keyword">if</span> x.<span class="hljs-built_in">sum</span>() &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> <span class="hljs-variable language_">self</span>.submodule2(x)<br></code></pre></td></tr></table></figure><p><code>@script_if_tracing</code> cannot compile such control flow because it only supports pure functions.If <code>submodule{1,2}</code> are complex and cannot be scripted,using traced submodules in a scripted parent <code>A</code> is the best option.</p></li><li><p><strong>Merge multiple traces</strong>:</p><p>Scripted models support two more features that traced models don&apos;t:</p><ul><li><strong>Control flow conditioned on attributes</strong>: a scripted module can have mutable attributes (e.g. a boolean flag)that affect control flows. Traced modules do not have control flows.</li><li><strong>Multiple methods</strong>: a traced module only supports <code>forward()</code>, but a scripted module can havemultiple methods.</li></ul><p>Actually, both features above are doing the same thing: they allow an exported model to be used indifferent ways, i.e. execute different sequences of operators as requested by the caller.</p><p>Below is an example scenario where such feature is useful: if <code>Detector</code> is scripted, the caller can mutate its<code>do_keypoint</code> attribute to control its behavior, or call <code>predict_keypoint</code> methoddirectly if needed.</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">Detector</span>(nn.Module):<br>  do_keypoint: <span class="hljs-built_in">bool</span><br><br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">forward</span>(<span class="hljs-params">self, img</span>):<br>      box = <span class="hljs-variable language_">self</span>.predict_boxes(img)<br>      <span class="hljs-keyword">if</span> <span class="hljs-variable language_">self</span>.do_keypoint:<br>          kpts = <span class="hljs-variable language_">self</span>.predict_keypoint(img, box)<br><br><span class="hljs-meta">  @torch.jit.export</span><br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">predict_boxes</span>(<span class="hljs-params">self, img</span>): <span class="hljs-keyword">pass</span><br><br><span class="hljs-meta">  @torch.jit.export</span><br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">predict_keypoint</span>(<span class="hljs-params">self, img, box</span>): <span class="hljs-keyword">pass</span><br></code></pre></td></tr></table></figure><p>This requirement is not seen very often. But if needed, how to achieve this in tracing?I have a solution that&apos;s not very clean:</p><p>Tracing can only capture one sequence of operators, so the natural way is to trace the model twice:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">det1 = torch.jit.trace(Detector(do_keypoint=<span class="hljs-literal">True</span>), inputs)<br>det2 = torch.jit.trace(Detector(do_keypoint=<span class="hljs-literal">False</span>), inputs)<br></code></pre></td></tr></table></figure><p>We can then alias their weights (to not duplicate the storage), and merge thetwo traces into one module to script.</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">det2.submodule.weight = det1.submodule.weight<br><span class="hljs-keyword">class</span> <span class="hljs-title class_">Wrapper</span>(nn.ModuleList):<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">forward</span>(<span class="hljs-params">self, img, do_keypoint: <span class="hljs-built_in">bool</span></span>):<br>    <span class="hljs-keyword">if</span> do_keypoint:<br>        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>[<span class="hljs-number">0</span>](img)<br>    <span class="hljs-keyword">else</span>:<br>        <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>[<span class="hljs-number">1</span>](img)<br>exported = torch.jit.script(Wrapper([det1, det2]))<br></code></pre></td></tr></table></figure></li></ol><h2 id="Performance">Performance<a class="markdown-anchor" href="#Performance">&#xB6;</a></h2><p>If a model is both traceable and scriptable,<strong>tracing always generates same or simpler graph</strong> (therefore likely faster).</p><p>Why?Because scripting tries to faithfully representyour Python code, even some of it are unnecessary. For example:it is not always smart enough to realize that someloops or data structures in the Python code are actually static and can be removed:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">class</span> <span class="hljs-title class_">A</span>(nn.Module):<br>  <span class="hljs-keyword">def</span> <span class="hljs-title function_">forward</span>(<span class="hljs-params">self, x1, x2, x3</span>):<br>    z = [<span class="hljs-number">0</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>]<br>    xs = [x1, x2, x3]<br>    <span class="hljs-keyword">for</span> k <span class="hljs-keyword">in</span> z: x1 += xs[k]<br>    <span class="hljs-keyword">return</span> x1<br>model = A()<br><span class="hljs-built_in">print</span>(torch.jit.script(model).code)<br><span class="hljs-comment"># def forward(self, x1: Tensor, x2: Tensor, x3: Tensor) -&gt; Tensor:</span><br><span class="hljs-comment">#   z = [0, 1, 2]</span><br><span class="hljs-comment">#   xs = [x1, x2, x3]</span><br><span class="hljs-comment">#   x10 = x1</span><br><span class="hljs-comment">#   for _0 in range(torch.len(z)):</span><br><span class="hljs-comment">#     k = z[_0]</span><br><span class="hljs-comment">#     x10 = torch.add_(x10, xs[k])</span><br><span class="hljs-comment">#   return x10</span><br><span class="hljs-built_in">print</span>(torch.jit.trace(model, [torch.tensor(<span class="hljs-number">1</span>)] * <span class="hljs-number">3</span>).code)<br><span class="hljs-comment"># def forward(self, x1: Tensor, x2: Tensor, x3: Tensor) -&gt; Tensor:</span><br><span class="hljs-comment">#   x10 = torch.add_(x1, x1)</span><br><span class="hljs-comment">#   x11 = torch.add_(x10, x2)</span><br><span class="hljs-comment">#   return torch.add_(x11, x3)</span><br></code></pre></td></tr></table></figure><p>This example is very simple, so it actually has workarounds for scripting (use tuple instead of list),or the loop might get optimized in a later optimization pass.But the point is: the graph compiler is not always smart enough. For complicated models, scripting mightgenerate a graph with unnecessary complexity that&apos;s hard to optimize.</p><h2 id="Concluding-Thoughts">Concluding Thoughts<a class="markdown-anchor" href="#Concluding-Thoughts">&#xB6;</a></h2><p><strong>Tracing has clear limitations</strong>:I spent most of this article talking about the limitations of tracing and how to fix them.I actually think this is the advantage of tracing: it has <strong>clear</strong> limitations (and solutions),so you can reason about whether it works.</p><p>On the contrary, scripting is more like a black box:no one knows if it works before trying.I didn&apos;t mention a single trick about how to fix scripting:there are many of them, but it&apos;s not worth your time to probe and fix a black box.</p><p><strong>Tracing has small blast radius</strong>:Both tracing and scripting affect how code can be written, but tracing has a much smaller blastradius, and causes much less damage:</p><ul><li>It limits the input/output format, but on the outer-most module only. (And this issue can be automaticallysolved as discussed above.)</li><li>It needs some code changes to generalize (e.g. to mix scripting in tracing), but these changes only go into theinternal implementation of the affected modules, not their interfaces.</li></ul><p>On the other hand, scripting has an impact on:</p><ul><li>The interface of every module &amp; submodule involved.<ul><li>IMO, this is the biggest damage:Advanced syntax features are needed in interfaces, and I&apos;m not willing to compromise on interface design.</li><li>This may end up affecting training as well because interface is often shared between training and inference.</li></ul></li><li>Pretty much every line of code in the inference forward path.</li></ul><p>Having a large blast radius is why scripting can do great harm to code quality.</p><p><strong>Control flow vs. other Python syntax</strong>:PyTorch is loved by its users because they can &quot;just write Python&quot;, and most importantly writePython control flows. But other syntax of Python are important as well.If being able to write Python control flow (scripting) means losing other great syntax,I&apos;d rather give up on the ability to write Python control flow.</p><p>In fact, if PyTorch is less obsessed with Python control flow, and offers mesymbolic control flows such as <code>torch.cond</code> like this (similar to the API of <code>tf.cond</code>):</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">def</span> <span class="hljs-title function_">f</span>(<span class="hljs-params">x</span>):<br>  <span class="hljs-keyword">return</span> torch.cond(x.<span class="hljs-built_in">sum</span>() &gt; <span class="hljs-number">0</span>, <span class="hljs-keyword">lambda</span>: torch.sqrt(x), <span class="hljs-keyword">lambda</span>: torch.square(x))<br></code></pre></td></tr></table></figure><p>Then <code>f</code> could be traced correctly and I would be happy to use this, no longer having to worryabout scripting.<a href="https://blog.tensorflow.org/2018/07/autograph-converts-python-into-tensorflow-graphs.html" aria-label="AutoGraph converts Python into TensorFlow graphs &#x2014; The TensorFlow Blog" class="hint--top hint--rounded hint--no-animate hint--no-arrow">TensorFlow AutoGraph</a>is a great example that automates this idea.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;PyTorch provides two methods to turn an &lt;code&gt;nn.Module&lt;/code&gt; into a
graph represented in TorchScript format: tracing and scripting.
This article will:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compare their pros and cons, with a focus on useful tips for tracing.&lt;/li&gt;
&lt;li&gt;Try to convince you that &lt;strong&gt;&lt;code&gt;torch.jit.trace&lt;/code&gt; should be preferred over &lt;code&gt;torch.jit.script&lt;/code&gt;&lt;/strong&gt;
for deployment of non-trivial models.&lt;/li&gt;
&lt;/ol&gt;</summary>
    
    
    
    
    <category term="Deep Learning" scheme="https://ppwwyyxx.com/blog/tags/Deep-Learning/"/>
    
    <category term="PyTorch" scheme="https://ppwwyyxx.com/blog/tags/PyTorch/"/>
    
  </entry>
  
  <entry>
    <title>谈谈Github上如何交流(4): {Feature,Pull} Request</title>
    <link href="https://ppwwyyxx.com/blog/2022/Github-Communications-4/"/>
    <id>https://ppwwyyxx.com/blog/2022/Github-Communications-4/</id>
    <published>2022-05-18T09:00:00.000Z</published>
    <updated>2022-05-18T09:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<div class="message is-info my-custom-sidebar"><div class="message-header">     <i class="fas fa-info-circle mr-2"></i><p>&#x672C;&#x7CFB;&#x5217;&#x6587;&#x7AE0;</p></div><div class="message-body"><ol><li><a href="/blog/2022/Github-Communications-1/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(1)" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8C08;&#x8C08; Github &#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41; (1)</a></li><li><a href="/blog/2022/Github-Communications-2/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(2): &#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5;bug" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5; bug</a></li><li><a href="/blog/2022/Github-Communications-3/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(3): &#x5982;&#x4F55;&#x7BA1;&#x7406;issue" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5982;&#x4F55;&#x7BA1;&#x7406; issue</a></li><li><a href="/blog/2022/Github-Communications-4/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(4): {Feature,Pull} Request" class="hint--top hint--rounded hint--no-animate hint--no-arrow">{Feature,Pull} Request</a></li></ol></div></div><p>&#x8FD9;&#x7BC7;&#x6587;&#x7AE0;&#x8BF4;&#x8BF4;&#x7528;&#x6237;&#x600E;&#x4E48;&#x63D0;&#x51FA;&#x597D;&#x7684; feature request / pull request, &#x4EE5;&#x53CA;&#x7EF4;&#x62A4;&#x8005;&#x5982;&#x4F55;&#x5BF9;&#x5F85;&#x5B83;&#x4EEC;.</p><span id="more"></span><p>&#x8FD9;&#x91CC;, &#x6211;&#x4EEC;&#x5FFD;&#x7565;&#x90A3;&#x79CD;&#x7279;&#x522B;&#x7B80;&#x5355;&#x7684; (&#x4F8B;&#x5982; 10 &#x884C;&#x4EE3;&#x7801;&#x4EE5;&#x5185;&#x53EF;&#x4EE5;&#x5B9E;&#x73B0;&#x7684;) request, &#x53EA;&#x8003;&#x8651; non-trivial &#x7684; feature request &#x548C; pull request.</p><h2 id="&#x597D;&#x7684;feature-request">&#x597D;&#x7684; feature request<a class="markdown-anchor" href="#&#x597D;&#x7684;feature-request">&#xB6;</a></h2><p>&#x9996;&#x5148;, &#x4E00;&#x4E2A;&#x6B8B;&#x5FCD;&#x7684;&#x4E8B;&#x5B9E;&#x662F;, &#x5F00;&#x6E90;&#x9879;&#x76EE;&#x4E2D;&#x5927;&#x591A;&#x6570;&#x7684; feature requests &#x4E0D;&#x4F1A;&#x5F97;&#x5230; maintainer &#x7684;&#x56DE;&#x5E94;. &#x7406;&#x7531;&#x4E5F;&#x5F88;&#x7B80;&#x5355;: &#x9879;&#x76EE;&#x7684;&#x8D44;&#x6E90;&#x662F;&#x6709;&#x9650;&#x7684;, &#x800C;&#x4FEE; bug, &#x7EF4;&#x62A4;&#x73B0;&#x6709; feature &#x7684;&#x4F18;&#x5148;&#x7EA7;&#x81EA;&#x7136;&#x4F1A;&#x66F4;&#x9AD8;. &#x5F53;&#x9879;&#x76EE;&#x6709;&#x989D;&#x5916;&#x7684;&#x5F00;&#x53D1;&#x8D44;&#x6E90;&#x65F6;,  &#x4E00;&#x822C;&#x4E5F;&#x4F1A;&#x4F18;&#x5148;&#x63A8;&#x8FDB;&#x56E2;&#x961F;&#x81EA;&#x5DF1;&#x539F;&#x6709;&#x7684;&#x5F00;&#x53D1;&#x8BA1;&#x5212; / roadmap, &#x6216;&#x4F18;&#x5148;&#x4E3A;&#x9879;&#x76EE;&#x7684;&#x8D5E;&#x52A9;&#x65B9; (&#x5982;&#x80CC;&#x540E;&#x7684;&#x516C;&#x53F8;) &#x5B9E;&#x73B0; feature. &#x8DEF;&#x4EBA;&#x7684; feature request &#x4F18;&#x5148;&#x7EA7;&#x53EF;&#x4EE5;&#x8BF4;&#x662F;&#x6700;&#x4F4E;&#x7684;, &#x6392;&#x5728;&#x6240;&#x6709;&#x8FD9;&#x4E9B;&#x4E4B;&#x540E;.</p><p>&#x4E0B;&#x56FE;&#x662F; vscode &#x793E;&#x533A;&#x5904;&#x7406; feature request &#x7684;&#x6D41;&#x7A0B;: (<a href="https://github.com/microsoft/vscode/wiki/Issues-Triaging#managing-feature-requests" aria-label="Issues Triaging &#xB7; microsoft/vscode Wiki" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x6765;&#x6E90;</a>)</p><img src="/blog/2022/Github-Communications-4/vscode.jpg" class="center" width="400"><p>Vscode &#x662F;&#x4E00;&#x4E2A;&#x975E;&#x5E38;&#x6CE8;&#x91CD;&#x793E;&#x533A;&#x7684;&#x9879;&#x76EE;, &#x56E0;&#x4E3A;&#x7F16;&#x8F91;&#x5668;&#x5FC5;&#x987B;&#x8981;&#x6709;&#x597D;&#x7684;&#x751F;&#x6001;&#x624D;&#x80FD;&#x6210;&#x529F;. &#x56E0;&#x6B64;&#x6211;&#x4EEC;&#x624D;&#x80FD;&#x770B;&#x5230; vscode &#x628A;&#x7528;&#x6237;&#x7684; &quot;upvote&quot; &#x4E5F;&#x8003;&#x8651;&#x5728;&#x5185;. &#x7EDD;&#x5927;&#x591A;&#x6570;&#x9879;&#x76EE;&#x5E76;&#x6CA1;&#x6709;&#x8FD9;&#x6700;&#x540E;&#x4E00;&#x6B65;: &#x548C;&#x9879;&#x76EE; roadmap &#x4E0D; align &#x7684; feature request, &#x4E00;&#x822C;&#x5C31;&#x76F4;&#x63A5;&#x8FDB;&#x5165; backlog &#x4E86;.</p><p>&#x5728;&#x8FD9;&#x79CD;&#x60C5;&#x51B5;&#x4E0B;, &#x8981;&#x60F3;&#x63D0;&#x51FA;&#x4E00;&#x4E2A; &quot;&#x597D;&#x7684; feature request&quot;, &#x5E76;&#x5F97;&#x5230; maintainer &#x7684;&#x91CD;&#x89C6;, &#x5F53;&#x7136;&#x4E0D;&#x662F;&#x90A3;&#x4E48;&#x5BB9;&#x6613;. &#x4E00;&#x4E2A;&#x597D;&#x7684; feature request &#x4E00;&#x822C;&#x81F3;&#x5C11;&#x8981;&#x5728;&#x4EE5;&#x4E0B;&#x67D0;&#x4E00;&#x70B9;&#x4E2D;&#x6BD4;&#x8F83;&#x7A81;&#x51FA;:</p><ul><li>&#x7279;&#x522B;&#x597D;&#x7684; idea: &#x63D0;&#x4F9B; maintainer &#x53EF;&#x80FD;&#x8FD8;&#x4E0D;&#x4E86;&#x89E3;, &#x6216;&#x6CA1;&#x60F3;&#x5230;&#x7684;&#x4FE1;&#x606F;, &#x8BA9; maintainer &#x770B;&#x5230;&#x5C1A;&#x672A;&#x6CE8;&#x610F;&#x5230;&#x7684;&#x91CD;&#x8981; feature<ul><li>&#x4F8B;&#x5982;: &quot;&#x6211;&#x53D1;&#x73B0;&#x5F88;&#x591A;&#x7528;&#x6237;&#x9700;&#x8981;&#x5BF9;&#x73B0;&#x6709;&#x7684; feature A &#x52A0;&#x4E00;&#x4E2A; workaround, &#x5982;&#x679C;&#x6211;&#x4EEC;&#x80FD;&#x591F;&#x505A; X &#x7684;&#x8BDD;, &#x5C31;&#x80FD;&#x591F;&#x63D0;&#x5347;&#x7528;&#x6237;&#x4F53;&#x9A8C;&quot;</li><li>&#x4F8B;&#x5982;: &quot;&#x73B0;&#x5728;&#x7684; feature A &#x505A;&#x4E86;&#x67D0;&#x4E2A;&#x5047;&#x8BBE;, &#x4F46;&#x662F;&#x5BF9;&#x4E8E;&#x5F88;&#x591A;&#x7528;&#x6237;&#x8FD9;&#x4E2A;&#x5047;&#x8BBE;&#x5176;&#x5B9E;&#x4E0D;&#x6210;&#x7ACB;. &#x6211;&#x4EEC;&#x5E0C;&#x671B;&#x901A;&#x8FC7;&#x5B9E;&#x73B0; X &#x6765;&#x653E;&#x677E;&#x8FD9;&#x4E2A;&#x5047;&#x8BBE;&quot;</li></ul></li><li>&#x63CF;&#x8FF0;&#x8FD9;&#x4E2A; feature &#x7684;&#x8BBE;&#x8BA1;&#x548C;&#x5B9E;&#x73B0;: &#x66F4;&#x591A;&#x7684;&#x7EC6;&#x8282;&#x80FD;&#x8282;&#x7701; maintainer &#x7684;&#x65F6;&#x95F4;, &#x8BA9;&#x4EBA;&#x66F4;&#x5BB9;&#x6613;&#x5224;&#x65AD;&#x8FD9;&#x4E2A; feature &#x662F;&#x5426;&#x503C;&#x5F97;&#x6295;&#x5165;<ul><li>&#x4F8B;&#x5982;, &#x7528; pseudo-code &#x6765;&#x63CF;&#x8FF0;&#x7528;&#x6237;&#x60F3;&#x8981;&#x7684;&#x65B0;&#x63A5;&#x53E3; / &#x529F;&#x80FD; / &#x8F93;&#x5165;&#x8F93;&#x51FA;; &#x63CF;&#x8FF0;&#x63A5;&#x53E3;&#x5185;&#x90E8;&#x7684;&#x53EF;&#x80FD;&#x5B9E;&#x73B0;; &#x751A;&#x81F3;&#x7ED9;&#x51FA;&#x591A;&#x79CD;&#x5907;&#x9009;&#x65B9;&#x6848;, &#x7B49;&#x7B49;</li></ul></li><li>&#x613F;&#x610F;&#x8D21;&#x732E;: &#x5982;&#x679C;&#x7528;&#x6237;&#x8868;&#x793A;&#x81EA;&#x5DF1;&#x613F;&#x610F;&#x5728; maintainer &#x5E2E;&#x52A9;&#x4E0B;&#x5B8C;&#x6210;&#x8FD9;&#x4E2A; feature, &#x90A3;&#x4E48; maintainer &#x53EF;&#x80FD;&#x5C31;&#x66F4;&#x613F;&#x610F;&#x6295;&#x5165;&#x65F6;&#x95F4;&#x6765;&#x5229;&#x7528;&#x597D;&#x793E;&#x533A;&#x8D44;&#x6E90;</li></ul><p>&#x8981;&#x505A;&#x5230;&#x8FD9;&#x4E9B;, &#x6709;&#x65F6;&#x5019;&#x786E;&#x5B9E;&#x9700;&#x8981;&#x7528;&#x6237;&#x5BF9;&#x9879;&#x76EE;&#x6709;&#x4E00;&#x5B9A;&#x7684;&#x6DF1;&#x5165;&#x4E86;&#x89E3;, &#x80FD;&#x591F;&#x628A;&#x63E1;&#x4F4F;&#x9879;&#x76EE;&#x7684; direction. &#x6BD5;&#x7ADF;&#x60F3;&#x8981;&#x9879;&#x76EE;&#x7684; developer &#x6539;&#x53D8;&#x539F;&#x5B9A;&#x7684;&#x8BA1;&#x5212;, &#x81EA;&#x5DF1;&#x6CA1;&#x4E24;&#x628A;&#x5237;&#x5B50;&#x662F;&#x4E0D;&#x884C;&#x7684;.</p><p>&#x53CD;&#x8FC7;&#x6765;, &#x4E00;&#x4E2A; &quot;&#x5E73;&#x51E1;&#x7684; / &#x4E0D;&#x597D;&#x7684; feature request&quot; &#x53EF;&#x80FD;&#x4F1A;&#x6709;&#x5982;&#x4E0B;&#x7279;&#x5F81;:</p><ul><li>&#x5E73;&#x51E1;&#x7684; idea: nice to have, &#x4F46;&#x662F;&#x53EA;&#x5BF9;&#x5C11;&#x6570;&#x7528;&#x6237;&#x6709;&#x7528;</li><li>&#x5DF2;&#x7ECF;&#x80FD;&#x591F;&#x88AB;&#x7528;&#x6237;&#x4EE5;&#x6269;&#x5C55;&#x7684;&#x65B9;&#x5F0F;&#x81EA;&#x5DF1;&#x5B9E;&#x73B0;, &#x4E0D;&#x4E00;&#x5B9A;&#x9700;&#x8981;&#x52A0;&#x5230;&#x9879;&#x76EE;&#x4E2D;</li><li>&#x76EE;&#x6807;&#x5F88;&#x597D;, &#x4F46;&#x4E0D;&#x77E5;&#x9053;&#x600E;&#x4E48;&#x5B9E;&#x73B0;<ul><li>&#x4F8B;&#x5982;: &quot;&#x8BF7;&#x628A;&#x8FD9;&#x4E2A;&#x7A0B;&#x5E8F;&#x53D8;&#x5FEB;&quot; &#x5C31;&#x4E0D;&#x662F;&#x4E00;&#x4E2A;&#x597D;&#x7684; feature request. &#x8FD9;&#x751A;&#x81F3;&#x4E0D;&#x7B97;&#x662F;&#x4E00;&#x4E2A;&#x6709;&#x6548;&#x7684; feature request, &#x56E0;&#x4E3A;&#x4F18;&#x5316;&#x662F;&#x65E0;&#x6B62;&#x5883;&#x7684;, &#x8FD9;&#x4E2A; request &#x4EC0;&#x4E48;&#x65F6;&#x5019;&#x7B97;&#x5B8C;&#x6210;? </li><li>&quot;&#x7A0B;&#x5E8F;&#x7684; X &#x6A21;&#x5757;&#x53EF;&#x4EE5;&#x901A;&#x8FC7; ABC &#x7684;&#x65B9;&#x5F0F;&#x53D8;&#x5FEB;&quot; &#x5C31;&#x662F;&#x4E00;&#x4E2A;&#x597D;&#x7684; request.&quot;&#x7A0B;&#x5E8F;&#x6BD4;&#x4E0A;&#x4E2A;&#x7248;&#x672C;&#x6162;&quot; &#x53EF;&#x4EE5;&#x662F;&#x4E00;&#x4E2A; bug report.</li></ul></li><li>&#x63CF;&#x8FF0;&#x4E0D;&#x6E05;&#x695A;: &#x4F7F;&#x7528;&#x8FC7;&#x591A;&#x6709;&#x6B67;&#x4E49;&#x7684;&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;, &#x4E0D;&#x77E5;&#x9053; feature &#x5230;&#x5E95;&#x662F;&#x4EC0;&#x4E48;. &#x5982;&#x679C;&#x53EF;&#x4EE5;, &#x7528;&#x4EE3;&#x7801;&#x6765;&#x63CF;&#x8FF0;&#x4F1A;&#x6709;&#x66F4;&#x5C11;&#x6B67;&#x4E49;.</li><li>Out of scope: feature &#x79BB;&#x9879;&#x76EE;&#x7684;&#x6838;&#x5FC3;&#x529F;&#x80FD;&#x592A;&#x8FDC;</li></ul><p>&#x5F53;&#x7136;, &#x4E00;&#x4E2A;&#x5E73;&#x51E1;&#x7684; feature request &#x7167;&#x6837;&#x503C;&#x5F97;&#x63D0;&#x51FA;, &#x867D;&#x7136;&#x5B83;&#x53EF;&#x80FD;&#x4F1A;&#x8FDB;&#x5165; backlog &#x6682;&#x65F6;&#x65E0;&#x4EBA;&#x95EE;&#x6D25;,  &#x4F46;&#x662F;&#x4E5F;&#x8BB8;&#x5728;&#x6C89;&#x5BC2;&#x4E00;&#x6BB5;&#x65F6;&#x95F4;&#x4E4B;&#x540E;&#x4F1A;&#x5F15;&#x53D1;&#x66F4;&#x6709;&#x4EF7;&#x503C;&#x7684;&#x8BA8;&#x8BBA;&#x548C;&#x5B9E;&#x73B0;.</p><h2 id="&#x597D;&#x7684;pull-request">&#x597D;&#x7684; pull request<a class="markdown-anchor" href="#&#x597D;&#x7684;pull-request">&#xB6;</a></h2><p>Pull request &#x662F;&#x793E;&#x533A;&#x5411;&#x9879;&#x76EE;&#x8D21;&#x732E;&#x4EE3;&#x7801;, &#x56E0;&#x6B64;&#x4E00;&#x822C;&#x66F4;&#x53D7; maintainer &#x6B22;&#x8FCE;, &#x4F46;&#x4E5F;&#x4E0D;&#x5168;&#x662F;. &#x56F4;&#x7ED5; pull request &#x7684;&#x4E3B;&#x8981;&#x77DB;&#x76FE;&#x662F; <strong>&#x53EF;&#x7EF4;&#x62A4;&#x6027;</strong> : &#x5F53; maintainer &#x540C;&#x610F;&#x63A5;&#x53D7;&#x4E00;&#x4E2A; PR &#x65F6;, &#x5C31;&#x610F;&#x5473;&#x7740; maintainer &#x540C;&#x610F;&#x8D1F;&#x8D23;&#x7EF4;&#x62A4;&#x8FD9;&#x6BB5;&#x522B;&#x4EBA;&#x5199;&#x7684;&#x4EE3;&#x7801;,  &#x8FD9;&#x5BF9;&#x4EE3;&#x7801;&#x7684;&#x53EF;&#x7EF4;&#x62A4;&#x6027;&#x662F;&#x6709;&#x8981;&#x6C42;&#x7684;.</p><p>&#x56E0;&#x6B64;, &#x7528;&#x6237;&#x5E94;&#x8BE5;&#x8BA4;&#x8BC6;&#x5230;, maintainer &#x5173;&#x6CE8;&#x7684;&#x7EDD;&#x4E0D;&#x4EC5;&#x4EC5;&#x662F;&#x4E00;&#x4E2A; PR &#x662F;&#x5426; &quot;work&quot;, &#x800C;&#x662F;&#x4F1A;&#x8003;&#x8651;&#x66F4;&#x591A;&#x7684;&#x56E0;&#x7D20;:</p><ul><li>PR &#x7684;&#x8BBE;&#x8BA1;&#x548C;&#x5B9E;&#x73B0;&#x65B9;&#x6848;&#x662F;&#x5426;&#x662F;&#x6700;&#x5408;&#x9002;&#x7684;? <ul><li>&#x6700;&#x9002;&#x5408;&#x7EF4;&#x62A4;&#x7684;&#x65B9;&#x6848;&#x5E76;&#x4E0D;&#x4E00;&#x5B9A;&#x662F;&#x6700;&#x5BB9;&#x6613;&#x5B9E;&#x73B0;&#x7684;&#x65B9;&#x6848;: &#x4F8B;&#x5982;&#x79D1;&#x5B66;&#x7684;&#x5B9E;&#x73B0;&#x4E00;&#x4E2A;&#x65B0; feature &#x53EF;&#x80FD;&#x4F9D;&#x8D56;&#x67D0;&#x4E9B;&#x5C0F;&#x7684;&#x91CD;&#x6784;. &#x8FD9;&#x65F6;, maintainer &#x7684;&#x9AD8;&#x6807;&#x51C6;&#x5C31;&#x4F1A;&#x4E0E;&#x7528;&#x6237; &quot;&#x5FEB;&#x901F;&#x89E3;&#x51B3;&#x81EA;&#x8EAB;&#x9700;&#x6C42;&quot; &#x7684;&#x76EE;&#x6807;&#x4E0D;&#x4E00;&#x81F4;.</li></ul></li><li>PR &#x662F;&#x5426;&#x6709;&#x8DB3;&#x591F;&#x7684; test coverage?<ul><li>Maintainer &#x5BF9; PR &#x7684;&#x6D4B;&#x8BD5;&#x8981;&#x6C42;&#x751A;&#x81F3;&#x53EF;&#x80FD;&#x4F1A;&#x6BD4;&#x5BF9;&#x81EA;&#x5DF1;&#x7684;&#x4EE3;&#x7801;&#x66F4;&#x4E25;&#x683C;, &#x56E0;&#x4E3A;&#x7EF4;&#x62A4;&#x522B;&#x4EBA;&#x7684;&#x4EE3;&#x7801;, &#x96BE;&#x5EA6;&#x672C;&#x6765;&#x5C31;&#x66F4;&#x5927;.</li></ul></li><li>PR &#x662F;&#x5426;&#x5F15;&#x5165;&#x4E86;&#x65B0;&#x7684; dependency?<ul><li>&#x6BCF;&#x4E2A; dependency &#x90FD;&#x662F;&#x4E00;&#x4E2A;&#x989D;&#x5916;&#x7684;&#x7EF4;&#x62A4;&#x8D1F;&#x62C5;. &#x5728;&#x6211;&#x66FE;&#x7ECF;&#x505A;&#x7684; House3D &#x8FD9;&#x4E2A;&#x5C0F;&#x5C0F;&#x7684;&#x9879;&#x76EE;&#x91CC;, &#x5C31;&#x53D1;&#x73B0;&#x4E86;&#x81F3;&#x5C11;<a href="/blog/2018/House3D-Renderer/" aria-label="&#x5199;House3D&#x6E32;&#x67D3;&#x7684;&#x65F6;&#x5019;&#x8E29;&#x8FC7;&#x7684;&#x5751;" class="hint--top hint--rounded hint--no-animate hint--no-arrow">5&#x4E2A;&#x4E0D;&#x540C;dependency&#x4E2D;&#x7684;bug</a>.Maintainer &#x4F1A;&#x907F;&#x514D;&#x6DFB;&#x52A0; dependency.</li></ul></li><li>Lint / documentation &#x7B49;</li></ul><p>Jeff Geerling &#x7684;<a href="https://www.jeffgeerling.com/blog/2016/why-i-close-prs-oss-project-maintainer-notes" aria-label="Why I close PRs (OSS project maintainer notes) | Jeff Geerling" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> Why I Close PRs</a> &#x548C; <a href="https://www.jeffgeerling.com/blog/2022/burden-open-source-maintainer" aria-label="The burden of an Open Source maintainer | Jeff Geerling" class="hint--top hint--rounded hint--no-animate hint--no-arrow">The Burden of an Open Source Maintainer</a> &#x4E5F;&#x4ECB;&#x7ECD;&#x4E86;&#x4EC0;&#x4E48;&#x6837;&#x7684; PR &#x662F; maintainer &#x66F4;&#x4E50;&#x4E8E;&#x89C1;&#x5230;&#x7684;. &#x6587;&#x7AE0;&#x5199;&#x7684;&#x5F88;&#x597D;, &#x4E14;&#x53E6;&#x5916;&#x63D0;&#x5230;&#x4E86;&#x4E00;&#x6761;&#x91CD;&#x8981;&#x7684;&#x6C9F;&#x901A;&#x539F;&#x5219;:</p><ul><li>&#x5BF9;&#x4E8E;&#x5927;&#x7684;&#x6539;&#x52A8;, &#x5148;&#x5728; issue &#x91CC;&#x8BA8;&#x8BBA;&#x518D; PR.<ul><li>&#x8FD9;&#x4E2A; feature &#x662F;&#x5426;&#x503C;&#x5F97;&#x505A;? &#x662F;&#x5426;&#x4F1A;&#x88AB;&#x63A5;&#x53D7;? &#x5982;&#x4F55;&#x8BBE;&#x8BA1;? &#x8FD9;&#x4E9B;&#x95EE;&#x9898;&#x90FD;&#x6CA1;&#x5F97;&#x5230;&#x8BA4;&#x53EF;&#x5C31;&#x53D1;&#x4E2A;&#x5927; PR, &#x53EF;&#x80FD;&#x4F1A;&#x767D;&#x767D;&#x6D6A;&#x8D39;&#x4F5C;&#x8005;&#x7684;&#x65F6;&#x95F4;.</li><li>&#x5173;&#x4E8E;&#x8FD9;&#x4E00;&#x70B9;, &#x80E1;&#x6E0A;&#x9E23;&#x7684;<a href="https://zhuanlan.zhihu.com/p/204492805#h_204492805_5" aria-label="&#x5982;&#x4F55;&#x4F18;&#x96C5;&#x5730;&#x53C2;&#x4E0E;&#x5F00;&#x6E90;&#x5F00;&#x53D1;&#xFF1F; - &#x77E5;&#x4E4E;" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5982;&#x4F55;&#x4F18;&#x96C5;&#x5730;&#x53C2;&#x4E0E;&#x5F00;&#x6E90;&#x5F00;&#x53D1;</a>&#x4E5F;&#x6709;&#x66F4;&#x8BE6;&#x7EC6;&#x7684;&#x89E3;&#x91CA;.</li></ul></li></ul><p>Maintainer &#x5E94;&#x5728;<code>CONTRIBUTING.md</code> &#x6216; <code>.github/pull_request_template.md</code> &#x91CC;&#x4E3A; contributor &#x63D0;&#x4F9B;&#x5F15;&#x5BFC;,  &#x5305;&#x62EC;&#x4ECB;&#x7ECD;&#x63D0;&#x4EA4; PR &#x7684;&#x6CE8;&#x610F;&#x4E8B;&#x9879;, PR &#x88AB;&#x63A5;&#x6536;&#x7684;&#x539F;&#x5219;, &#x9879;&#x76EE;&#x7684; coding style, &#x5982;&#x4F55;&#x4F7F;&#x7528; linter, &#x5982;&#x4F55;&#x6D4B;&#x8BD5;, &#x5982;&#x4F55;&#x66F4;&#x65B0; documentation, &#x7B49;&#x7B49;. &#x4F8B;&#x5982; detectron2 &#x7684;<a href="https://detectron2.readthedocs.io/en/latest/notes/contributing.html#pull-requests" aria-label="Contributing to detectron2 &#x2014; detectron2 0.6 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> contributing.md</a> &#x548C;<a href="https://github.com/facebookresearch/detectron2/blob/main/.github/pull_request_template.md" aria-label="pull_request_template.md &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> pull_request_template.md</a>.</p><h1 id="Extensions">Extensions<a class="markdown-anchor" href="#Extensions">&#xB6;</a></h1><p>&#x5F00;&#x6E90;&#x793E;&#x533A;&#x4E2D;, &#x7528;&#x6237;&#x4F1A;&#x6709;&#x65E0;&#x6570;&#x4E0D;&#x540C;&#x7684;&#x9700;&#x6C42;. &#x5373;&#x4F7F; maintainer &#x6709;&#x65F6;&#x95F4; (&#x5927;&#x90E8;&#x5206; maintainer &#x6CA1;&#x6709;) &#x53BB;&#x5904;&#x7406; feature request / pull request, &#x4E5F;&#x4F1A;&#x6709;&#x5F88;&#x591A;&#x4EBA;&#x7684;&#x9700;&#x6C42;&#x65E0;&#x6CD5;&#x6EE1;&#x8DB3;.</p><p>&#x5728;&#x8FD9;&#x79CD;&#x73B0;&#x5B9E;&#x4E0B;, &#x9762;&#x5BF9;&#x6CA1;&#x6709;&#x7CBE;&#x529B;&#x5B9E;&#x73B0; / &#x7EF4;&#x62A4;&#x7684; {feature, pull} request, maintainer / contributor &#x53EF;&#x4EE5;&#x91C7;&#x53D6;&#x7684;&#x4E00;&#x4E2A;&#x597D;&#x7684;&#x7B56;&#x7565;&#x662F;: &#x901A;&#x8FC7;&#x4E00;&#x4E9B;&#x6539;&#x52A8;&#x8BA9;&#x9879;&#x76EE;&#x53D8;&#x5F97;&#x66F4; extensible, &#x4F7F;&#x5F97; feature &#x53EF;&#x4EE5;&#x88AB;&#x7528;&#x6237;&#x4EE5;&#x6269;&#x5C55; / extensions &#x7684;&#x65B9;&#x5F0F;&#x72EC;&#x7ACB;&#x5B9E;&#x73B0;, &#x800C;&#x4E0D;&#x662F;&#x5728;&#x9879;&#x76EE;&#x4E2D;&#x5B9E;&#x73B0;.</p><p>&#x5177;&#x4F53;&#x8981;&#x600E;&#x4E48;&#x505A;&#x5230;&#x8FD9;&#x4E00;&#x70B9;, &#x662F;&#x4E00;&#x4E2A;&#x7CFB;&#x7EDF;&#x8BBE;&#x8BA1;&#x95EE;&#x9898;, &#x8FD9;&#x7BC7;&#x6587;&#x7AE0;&#x5C31;&#x4E0D;&#x8DD1;&#x9898;&#x591A;&#x8BF4;&#x4E86;. &#x91C7;&#x7528;&#x8FD9;&#x79CD;&#x65B9;&#x5F0F;&#x7684;&#x597D;&#x5904;&#x662F;:</p><ul><li>&#x9F13;&#x52B1; contributor &#x5C06;&#x4E00;&#x4E9B; pull request &#x53D8;&#x4E3A;&#x5B83;&#x4EEC;&#x81EA;&#x5DF1;&#x7684;&#x9879;&#x76EE;, &#x7531; contributor &#x81EA;&#x5DF1;&#x8D1F;&#x8D23;&#x7EF4;&#x62A4;</li><li>maintainer &#x53EF;&#x4EE5;&#x4E13;&#x5FC3;&#x8D1F;&#x8D23;&#x6838;&#x5FC3;&#x7CFB;&#x7EDF;&#x7684;&#x6838;&#x5FC3;&#x529F;&#x80FD;, &#x51CF;&#x8F7B;&#x8D1F;&#x62C5;. &#x8FD9;&#x4E5F;&#x66F4;&#x7B26;&#x5408; &quot;&#x53EA;&#x505A;&#x597D;&#x4E00;&#x4EF6;&#x4E8B;&quot; &#x7684; UNIX &#x54F2;&#x5B66;</li><li>&#x5C06;&#x5927;&#x91CF;&#x4E0D;&#x4F1A;&#x88AB; maintainer &#x63A5;&#x53D7;&#x7684; feature request &#x770B;&#x4F5C; &quot;out of scope&quot;: &#x5B83;&#x4EEC;&#x53EF;&#x4EE5;&#x770B;&#x4F5C;&#x8FD9;&#x4E2A;&#x9879;&#x76EE;&#x7684; &quot;applications&quot;, &#x56E0;&#x4E3A;&#x7528;&#x6237;&#x53EF;&#x4EE5;&#x5728;&#x9879;&#x76EE;&#x7684;&#x57FA;&#x7840;&#x4E0A;&#x5B9E;&#x73B0;&#x5B83;&#x4EEC;</li><li>&#x597D;&#x7684; extensible design &#x662F; future proof &#x7684;: &#x5B83;&#x4E0D;&#x4EC5;&#x80FD;&#x89E3;&#x51B3;&#x773C;&#x524D;&#x7684;&#x4E00;&#x4E2A; feature, &#x8FD8;&#x80FD;&#x591F;&#x652F;&#x6301;&#x4E00;&#x5927;&#x7C7B;&#x672A;&#x6765;&#x53EF;&#x80FD;&#x4F1A;&#x51FA;&#x73B0;&#x7684;&#x9700;&#x6C42;</li><li>&#x5BF9;&#x4E8E;&#x4E00;&#x4E9B;&#x4E0D;&#x6210;&#x719F;&#x7684;&#x529F;&#x80FD;, &#x53EF;&#x4EE5;&#x8BA9;&#x5B83;&#x4EEC;&#x5728;&#x9879;&#x76EE;&#x4E4B;&#x5916;&#x7ECF;&#x8FC7;&#x8FED;&#x4EE3;&#x540E;, &#x518D;&#x5438;&#x6536;&#x56DE;&#x9879;&#x76EE;&#x4E2D;&#x7531;&#x5B98;&#x65B9;&#x8D1F;&#x8D23;&#x7EF4;&#x62A4;</li><li>&#x5EFA;&#x7ACB;&#x4E00;&#x4E2A;&#x66F4;&#x4E30;&#x5BCC;&#x7684;&#x751F;&#x6001;, &#x6BCF;&#x4E2A;&#x4EBA;&#x90FD;&#x53EF;&#x4EE5;&#x53D1;&#x6325;&#x81EA;&#x5DF1;&#x7684;&#x521B;&#x9020;&#x529B;, &#x4E0D;&#x88AB; core maintainer &#x7684;&#x7CBE;&#x529B;&#x9650;&#x5236;</li></ul><p>&#x5F88;&#x591A;&#x6210;&#x529F;&#x7684;&#x5F00;&#x6E90;&#x9879;&#x76EE;&#x90FD;&#x662F;&#x9760;&#x7740;&#x53EF;&#x6269;&#x5C55;&#x6027;&#x521B;&#x5EFA;&#x4E86;&#x4F18;&#x79C0;&#x7684;&#x751F;&#x6001;.</p><ul><li>Vim, Emacs &#x7B49;&#x7F16;&#x8F91;&#x5668;&#x7684;&#x6838;&#x5FC3;&#x529F;&#x80FD;&#x90FD;&#x4E0D;&#x591A;, &#x8981;&#x9760;&#x6D77;&#x91CF;&#x7684;&#x6269;&#x5C55;&#x5B9E;&#x73B0;&#x5404;&#x7C7B;&#x529F;&#x80FD;.</li><li>Vscode &#x5BF9;&#x5F88;&#x591A; feature request &#x4F1A;&#x5173;&#x95ED;&#x5E76;&#x52A0;&#x4E0A;&#x4E00;&#x4E2A;<a href="https://github.com/microsoft/vscode/labels/%2Aextension-candidate" aria-label="Issues &#xB7; microsoft/vscode" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> extension candidate</a> &#x7684; label, &#x610F;&#x601D;&#x662F;&#x8FD9;&#x4E2A; feature request &#x9002;&#x5408;&#x4F5C;&#x4E3A;&#x6269;&#x5C55;&#x6765;&#x72EC;&#x7ACB;&#x5B9E;&#x73B0;.</li><li>PyTorch &#x4E5F;&#x5341;&#x5206;&#x6CE8;&#x91CD;&#x53EF;&#x6269;&#x5C55;&#x6027;, &#x9664;&#x4E86; module/operator &#x4E0A;&#x7684;&#x6269;&#x5C55;&#x4E4B;&#x5916;, &#x751A;&#x81F3;&#x6709;&#x5141;&#x8BB8;&#x7528;&#x6237;&#x5B9E;&#x73B0;&#x81EA;&#x5DF1;&#x7684;<code>Tensor</code> subclass, &#x81EA;&#x5DF1;&#x7684; device &#x7B49;&#x975E;&#x5E38;&#x5938;&#x5F20;&#x7684;&#x6269;&#x5C55;. &#x6700;&#x8FD1;&#x7684;<code>torch.fx</code> &#x4E5F;&#x662F;&#x5728;&#x7ED9;&#x7528;&#x6237;&#x5B9E;&#x73B0; graph transformation &#x6269;&#x5C55;&#x7684;&#x673A;&#x4F1A;.PyTorch &#x56E2;&#x961F;&#x4F1A;&#x4F7F;&#x7528; &quot;extension points&quot; &#x8FD9;&#x4E2A;&#x8BCD;, &#x6307;&#x7CFB;&#x7EDF;&#x4E2D;&#x53EF;&#x4EE5;&#x7531;&#x7528;&#x6237;&#x5B9E;&#x73B0;&#x6269;&#x5C55;&#x7684;&#x90E8;&#x4F4D;.</li></ul><p>Detectron2 &#x4E5F;&#x4ECE;&#x6700;&#x521D;&#x5C31;&#x5C3D;&#x91CF;&#x8D70;&#x8FD9;&#x6761;&#x8DEF;, &#x628A; &quot;&#x5C3D;&#x91CF;&#x8BA9;&#x6240;&#x6709;&#x6A21;&#x5757;&#x90FD;&#x53EF;&#x6269;&#x5C55; / &#x53EF;&#x66FF;&#x6362;&quot; &#x4F5C;&#x4E3A;&#x4E00;&#x4E2A;&#x8BBE;&#x8BA1;&#x76EE;&#x6807;.Facebook &#x4E0E;&#x4E4B;&#x76F8;&#x5173;&#x7684; research project &#x5C31;&#x90FD;&#x4EE5;<a href="https://github.com/facebookresearch/detectron2/tree/main/projects/#projects-by-facebook" aria-label="projects &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> detectron2 &#x6269;&#x5C55;&#x7684;&#x5F62;&#x5F0F;&#x5F00;&#x6E90;</a>. &#x9664;&#x6B64;&#x4E4B;&#x5916;&#x4E5F;&#x6709;&#x4E0D;&#x5C11;&#x6765;&#x81EA;&#x793E;&#x533A;&#x7684;&#x4F18;&#x79C0;&#x6269;&#x5C55;, &#x4F8B;&#x5982;<a href="https://github.com/aim-uofa/adet" aria-label="aim-uofa/AdelaiDet: AdelaiDet is an open source toolbox for multiple instance-level detection and recognition tasks." class="hint--top hint--rounded hint--no-animate hint--no-arrow"> AdelaiDet</a>, <a href="https://github.com/jinfagang/yolov7" aria-label="jinfagang/yolov7: &#x1F525;&#x1F525;&#x1F525;&#x1F525; YOLO with Transformers and Instance Segmentation, with TensorRT acceleration! &#x1F525;&#x1F525;&#x1F525;" class="hint--top hint--rounded hint--no-animate hint--no-arrow">YOLOv7</a> &#x7B49;.</p><h1 id="&#x7EF4;&#x62A4;&#x7684;&#x8D1F;&#x62C5;">&#x7EF4;&#x62A4;&#x7684;&#x8D1F;&#x62C5;<a class="markdown-anchor" href="#&#x7EF4;&#x62A4;&#x7684;&#x8D1F;&#x62C5;"> &#xB6;</a></h1><p>&#x5982;&#x679C; pull request &#x5E76;&#x4E0D;&#x5BB9;&#x6613;&#x88AB;&#x63A5;&#x53D7;, &#x90A3;&#x4E48;&#x5F00;&#x53D1;&#x8005;&#x662F;&#x4E0D;&#x662F;&#x5E94;&#x8BE5;&#x5E72;&#x8106;&#x81EA;&#x5DF1; fork &#x9879;&#x76EE;, &#x6765;&#x5B9E;&#x73B0;&#x81EA;&#x5DF1;&#x60F3;&#x8981;&#x7684;&#x6539;&#x52A8;&#x5462;?  &#x8981;&#x56DE;&#x7B54;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;, &#x8981;&#x5148;&#x60F3;&#x6E05;&#x695A;&#x5C06;&#x8FD9;&#x4E9B;&#x6539;&#x52A8;&#x5F00;&#x6E90;&#x7684;&#x76EE;&#x7684;&#x662F;&#x4EC0;&#x4E48;:</p><p>&#x5982;&#x679C;&#x53EA;&#x662F;&#x4E00;&#x4E2A; proof-of-concept, &#x4E3A;&#x4E86;&#x516C;&#x5F00;&#x7684;&#x5C55;&#x793A;&#x8FD9;&#x4E2A;&#x6539;&#x52A8;&#x7684;&#x5185;&#x5BB9;, &#x90A3;&#x4E48; fork &#x662F;&#x6CA1;&#x95EE;&#x9898;&#x751A;&#x81F3;&#x66F4;&#x5408;&#x9002;&#x7684;:</p><ul><li>&#x4F8B;&#x5982;&#x4E00;&#x4E9B;&#x5B9E;&#x9A8C;&#x6027;&#x7684;&#x5927;&#x578B;&#x6539;&#x52A8;&#x53EF;&#x4EE5;&#x4F5C;&#x4E3A; fork &#x6765;&#x5411;&#x522B;&#x4EBA;&#x5C55;&#x793A;</li><li>&#x4F8B;&#x5982;&#x8BBA;&#x6587;&#x4F5C;&#x8005;&#x53D1;&#x8868;&#x53EF;&#x4EE5;&#x590D;&#x73B0;&#x8BBA;&#x6587;&#x7ED3;&#x679C;&#x7684;&#x4EE3;&#x7801;, &#x53EF;&#x4EE5;&#x7528; fork &#x7684;&#x5F62;&#x5F0F;,  &#x751A;&#x81F3;&#x53EF;&#x4EE5;&#x628A;&#x6240;&#x6709; dependency &#x7684;&#x7248;&#x672C;&#x90FD;&#x56FA;&#x5B9A;&#x4F4F;</li></ul><p>&#x5F00;&#x53D1;&#x8005;&#x4E5F;&#x8981;&#x610F;&#x8BC6;&#x5230;, &#x5982;&#x679C;&#x8BA4;&#x4E3A;&#x81EA;&#x5DF1;&#x7684;&#x5DE5;&#x4F5C;&#x4E0D;&#x53EA;&#x662F;&#x4E00;&#x4E2A; proof-of-concept/toy, &#x60F3;&#x8981;&#x8BA9;&#x81EA;&#x5DF1;&#x7684; fork &#x771F;&#x7684;&#x88AB;&#x4EBA;&#x4E25;&#x8083;&#x7684;&#x4F7F;&#x7528;&#x7684;&#x8BDD;,  &#x5C31;&#x4E0D;&#x5F97;&#x4E0D;&#x81EA;&#x5DF1;&#x627F;&#x62C5;&#x7EF4;&#x62A4;&#x7684;&#x8D23;&#x4EFB;. &#x800C;&#x7EF4;&#x62A4;&#x7684;&#x8D1F;&#x62C5;&#x662F;&#x5F88;&#x91CD;&#x7684;, &#x6311;&#x51E0;&#x4E2A;&#x70B9;&#x6765;&#x8BF4;:</p><ul><li>&#x5728;&#x5F00;&#x6E90;&#x4E16;&#x754C;&#x4E2D;, &#x5373;&#x4F7F;&#x4EE3;&#x7801;&#x4E0D;&#x53D8;, &#x73AF;&#x5883;&#x4E5F;&#x5728;&#x53D8;, bug &#x4E5F;&#x4F1A;&#x81EA;&#x5DF1;&#x627E;&#x4E0A;&#x95E8;&#x6765;</li><li>&#x53EA;&#x8981;&#x6709;&#x4EBA;&#x7528;, &#x5C31;&#x4F1A;&#x6709;&#x4EBA;&#x62A5;&#x544A;&#x95EE;&#x9898;</li><li>&#x7ECF;&#x9A8C;: &#x9879;&#x76EE;&#x4E2D;&#x5F88;&#x591A;&#x770B;&#x4F3C;&#x5947;&#x602A;&#x7684;&#x51B3;&#x5B9A;, &#x6216;&#x5B9E;&#x73B0;&#x7EC6;&#x8282;, &#x5F88;&#x53EF;&#x80FD;&#x662F;&#x6709;&#x5176;&#x5386;&#x53F2;&#x539F;&#x56E0;,  &#x6216;&#x662F;&#x5728;&#x8E29;&#x4E86;&#x5751;&#x4E4B;&#x540E;&#x603B;&#x7ED3;&#x51FA;&#x6765;&#x7684;. &#x5728;&#x81EA;&#x5DF1;&#x7684; fork &#x4E2D;&#x9B54;&#x6539;, &#x6CA1;&#x6709;&#x4E0E;&#x5B98;&#x65B9;&#x7EF4;&#x62A4;&#x8005;&#x5408;&#x4F5C;&#x4EA4;&#x6D41;, &#x5F88;&#x591A;&#x5751;&#x53EF;&#x80FD;&#x8981;&#x518D;&#x8E29;&#x4E00;&#x6B21;. &#x6709;&#x4E2A;&#x4E0E;&#x6B64;&#x76F8;&#x5173;&#x7684; Chesterton&apos;s Fence &#x539F;&#x5219;:<blockquote><p>Do not remove a fence until you know why it was put up in the first place</p><footer><strong>-</strong><cite><a href="https://fs.blog/chestertons-fence/" aria-label="Chesterton&#x2019;s Fence: A Lesson in Second Order Thinking - Farnam Street" class="hint--top hint--rounded hint--no-animate hint--no-arrow">fs.blog/chestertons-fence</a></cite></footer></blockquote></li><li>&#x6D4B;&#x8BD5;: &#x4F8B;&#x5982; PyTorch &#x8FD9;&#x6837;&#x7684;&#x9879;&#x76EE;, &#x6BCF;&#x5929;&#x82B1;&#x5728;&#x8DD1;&#x6D4B;&#x8BD5;&#x4E0A;&#x7684;&#x94B1;&#x90FD;&#x662F;&#x4E2A;&#x5929;&#x6587;&#x6570;&#x5B57;. &#x6BCF;&#x4E2A;&#x7528;&#x6237;&#x4E5F;&#x662F;&#x5929;&#x7136;&#x7684;&#x5728;&#x5E2E;&#x52A9;&#x9879;&#x76EE;&#x505A;&#x6D4B;&#x8BD5;.fork &#x6CA1;&#x6709;&#x8FD9;&#x6837;&#x7684;&#x6D4B;&#x8BD5;&#x8D44;&#x6E90;&#x548C;&#x7528;&#x6237;&#x91CF;, &#x5982;&#x4F55;&#x4FDD;&#x8BC1;&#x81EA;&#x5DF1;&#x7684;&#x7248;&#x672C;&#x4ECD;&#x7136;&#x6B63;&#x786E;? </li><li>&#x6211;&#x5C31;&#x9047;&#x5230;&#x4E0D;&#x6B62;&#x4E00;&#x6B21;&#x8FD9;&#x6837;&#x7684;&#x60C5;&#x5F62;: &#x6211;&#x7684;&#x5F00;&#x6E90;&#x9879;&#x76EE;&#x4E2D;&#x7684;&#x4E00;&#x4E9B;&#x4EE3;&#x7801;,  &#x65E9;&#x671F;&#x6709;&#x95EE;&#x9898;&#x7684;&#x7248;&#x672C;&#x88AB;&#x522B;&#x4EBA;&#x590D;&#x5236;&#x8F6C;&#x624B;&#x4E86;&#x591A;&#x6B21;,  &#x8F6C;&#x4E86;&#x4E00;&#x5708;&#x5C45;&#x7136;&#x8FD8;&#x56DE;&#x5230;&#x4E86;&#x6211;&#x53C2;&#x4E0E;&#x7684;&#x5176;&#x5B83;&#x9879;&#x76EE;&#x91CC;,  &#x8BA9;&#x6211;&#x88AB;&#x81EA;&#x5DF1;&#x591A;&#x5E74;&#x524D;&#x5DF2;&#x7ECF;&#x4FEE;&#x597D;&#x7684; bug &#x7ED9;&#x5751;&#x4E86;. &#x8FD9;&#x5C31;&#x662F;&#x7531;&#x4E8E;&#x4E00;&#x4E9B;&#x6CA1;&#x6709;&#x627F;&#x62C5;&#x7EF4;&#x62A4;&#x8D23;&#x4EFB;&#x7684; fork</li></ul><p>&#x56E0;&#x6B64;, &#x867D;&#x7136;&#x4E00;&#x4E2A;&#x6210;&#x529F;&#x7684; pull request &#x8981;&#x4ED8;&#x51FA;&#x989D;&#x5916;&#x7684;&#x4EA4;&#x6D41;,  &#x4F46;&#x5B83;&#x6362;&#x6765;&#x7684;&#x662F;&#x9879;&#x76EE;&#x7EF4;&#x62A4;&#x8005;&#x7684;&#x7EF4;&#x62A4;&#x5DE5;&#x4F5C;. &#x5982;&#x679C;&#x5F00;&#x53D1;&#x8005;&#x60F3;&#x52A0;&#x5165;&#x65B0; feature, &#x53C8;&#x6CA1;&#x6709;&#x81EA;&#x4FE1;&#x80FD;&#x80DC;&#x4EFB;&#x6574;&#x4E2A;&#x9879;&#x76EE;&#x7684;&#x7EF4;&#x62A4;,  &#x4E0E;&#x5176;&#x53E6;&#x8D77;&#x7089;&#x7076;,  &#x4E0D;&#x5982;&#x591A;&#x53C2;&#x4E0E;&#x4EA4;&#x6D41;, &#x4E0E;&#x7EF4;&#x62A4;&#x8005;&#x8BA8;&#x8BBA;&#x4E00;&#x4E2A;&#x66F4;&#x53EF;&#x7EF4;&#x62A4;&#x7684;&#x65B9;&#x6848; (pull request &#x6216; extension).</p>]]></content>
    
    
    <summary type="html">
&lt;p&gt;&amp;#x8FD9;&amp;#x7BC7;&amp;#x6587;&amp;#x7AE0;&amp;#x8BF4;&amp;#x8BF4;&amp;#x7528;&amp;#x6237;&amp;#x600E;&amp;#x4E48;&amp;#x63D0;&amp;#x51FA;&amp;#x597D;&amp;#x7684; feature request / pull request,
 &amp;#x4EE5;&amp;#x53CA;&amp;#x7EF4;&amp;#x62A4;&amp;#x8005;&amp;#x5982;&amp;#x4F55;&amp;#x5BF9;&amp;#x5F85;&amp;#x5B83;&amp;#x4EEC;.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Open Source" scheme="https://ppwwyyxx.com/blog/tags/Open-Source/"/>
    
  </entry>
  
  <entry>
    <title>谈谈Github上如何交流(3): 如何管理issue</title>
    <link href="https://ppwwyyxx.com/blog/2022/Github-Communications-3/"/>
    <id>https://ppwwyyxx.com/blog/2022/Github-Communications-3/</id>
    <published>2022-05-11T06:59:00.000Z</published>
    <updated>2022-05-11T06:59:00.000Z</updated>
    
    <content type="html"><![CDATA[<div class="message is-info my-custom-sidebar"><div class="message-header">     <i class="fas fa-info-circle mr-2"></i><p>&#x672C;&#x7CFB;&#x5217;&#x6587;&#x7AE0;</p></div><div class="message-body"><ol><li><a href="/blog/2022/Github-Communications-1/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(1)" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8C08;&#x8C08; Github &#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41; (1)</a></li><li><a href="/blog/2022/Github-Communications-2/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(2): &#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5;bug" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5; bug</a></li><li><a href="/blog/2022/Github-Communications-3/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(3): &#x5982;&#x4F55;&#x7BA1;&#x7406;issue" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5982;&#x4F55;&#x7BA1;&#x7406; issue</a></li><li><a href="/blog/2022/Github-Communications-4/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(4): {Feature,Pull} Request" class="hint--top hint--rounded hint--no-animate hint--no-arrow">{Feature,Pull} Request</a></li></ol></div></div><p>&#x6211;&#x542C;&#x8FC7;&#x4E0D;&#x5C11;&#x4EBA;&#x51ED;&#x501F;&#x7231;&#x597D;&#x5F00;&#x6E90;&#x4E86;&#x81EA;&#x5DF1;&#x7684;&#x9879;&#x76EE;&#x540E;, &#x5374;&#x5BF9; issue &#x592A;&#x4E71;&#x611F;&#x5230;&#x56F0;&#x6270;, &#x751A;&#x81F3;&#x60F3;&#x5E72;&#x8106;&#x76F4;&#x63A5;&#x7981;&#x7528; issue. &#x5176;&#x5B9E;, &#x4EFB;&#x4F55;&#x9879;&#x76EE;&#x8FBE;&#x5230;&#x4E00;&#x5B9A;&#x89C4;&#x6A21;&#x540E;, &#x5982;&#x679C;&#x4E0D;&#x5BF9; issue &#x8FDB;&#x884C;&#x9002;&#x5F53;&#x7BA1;&#x7406;, &#x90FD;&#x4F1A;&#x4F7F; issue &#x4FE1;&#x566A;&#x6BD4;&#x8FC7;&#x4F4E;, &#x5931;&#x53BB;&#x539F;&#x672C;&#x7684;&#x529F;&#x80FD;.</p><p>&#x8FD9;&#x7BC7;&#x6587;&#x7AE0;&#x4E3B;&#x8981;&#x4ECE; maintainer &#x7684;&#x89D2;&#x5EA6;&#x8BF4;&#x8BF4;, &#x5728;&#x5177;&#x5907;&#x89C4;&#x6A21;&#x7684;&#x9879;&#x76EE;&#x4E2D;&#x7BA1;&#x7406; issue &#x7684;&#x4E00;&#x4E9B;&#x65B9;&#x6CD5;&#x548C;&#x539F;&#x5219;.</p><span id="more"></span><h2 id="Issue-Template">Issue Template<a class="markdown-anchor" href="#Issue-Template">&#xB6;</a></h2><p>&#x4EFB;&#x4F55;&#x5177;&#x5907;&#x4E00;&#x5B9A;&#x89C4;&#x6A21;&#x7684;&#x9879;&#x76EE;&#x90FD;&#x5E94;&#x8BE5;&#x4F7F;&#x7528; issue template.Issue template &#x4F4D;&#x4E8E;&#x9879;&#x76EE;&#x7684;<code>.github/ISSUE_TEMPLATE</code> &#x76EE;&#x5F55;, &#x5305;&#x542B;&#x4E24;&#x79CD;&#x6587;&#x4EF6;:</p><ol><li><p>&#x6BCF;&#x4E2A; template &#x6709;&#x4E00;&#x4E2A; markdown &#x6587;&#x4EF6;, &#x5BF9;&#x5E94;&#x4E00;&#x7C7B; issue. &#x5176;&#x4E2D;&#x63CF;&#x8FF0;&#x9700;&#x8981;&#x7528;&#x6237;&#x63D0;&#x4F9B;&#x7684;&#x4FE1;&#x606F;.</p><p>&#x8FD8;&#x53EF;&#x4EE5;&#x4E3A;&#x8FD9;&#x4E2A; issue template &#x81EA;&#x52A8;&#x914D;&#x7F6E; issue label. &#x7136;&#x800C;&#x7531;&#x4E8E; template &#x662F;&#x7528;&#x6237;&#x9009;&#x62E9;&#x7684;, &#x8FD9;&#x79CD;&#x65B9;&#x5F0F;&#x5F97;&#x5230;&#x7684; issue label &#x566A;&#x97F3;&#x8F83;&#x5927;, &#x53EF;&#x80FD;&#x8FD8;&#x9700;&#x8981; maintainer &#x7EA0;&#x6B63;. (&#x6211;&#x7684;&#x7B56;&#x7565;&#x662F;&#x4EC5;&#x5BF9; &quot;feature request&quot; &#x548C; &quot;documentation issue&quot; &#x81EA;&#x52A8; label)</p></li><li><p>&#x53EF;&#x9009;&#x7684;<code>config.yml</code> &#x5168;&#x5C40;&#x914D;&#x7F6E;&#x6587;&#x4EF6;. &#x6709;&#x7528;&#x7684;&#x914D;&#x7F6E;&#x5305;&#x62EC;:</p><ul><li><code>blank_issues_enabled</code>: &#x662F;&#x5426;&#x5141;&#x8BB8;&#x7528;&#x6237;&#x4E0D;&#x4F7F;&#x7528; template &#x81EA;&#x5DF1;&#x5199; issue.</li><li><code>contact_links</code>: maintainer &#x7528;&#x5B83;&#x5C06;&#x7528;&#x6237;&#x5F15;&#x5BFC;&#x5230;&#x5176;&#x4ED6;&#x5730;&#x65B9; (&#x8BBA;&#x575B;, discussions &#x7B49;).</li></ul></li></ol><p>Github &#x8FD1;&#x671F;&#x5728;&#x6D4B;&#x8BD5;<a href="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#creating-issue-forms" aria-label="Configuring issue templates for your repository - GitHub Docs" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> issue form</a>, &#x662F; issue template &#x7684;&#x5347;&#x7EA7;&#x7248;, &#x6709;&#x4E86;&#x66F4;&#x597D;&#x770B;&#x7684; UI &#x548C;&#x4E30;&#x5BCC;&#x7684;&#x8F93;&#x5165;&#x7C7B;&#x578B;. &#x53EF;&#x60DC;&#x6211;&#x4E00;&#x76F4;&#x8FD8;&#x6CA1;&#x6709;&#x6D4B;&#x8BD5;&#x673A;&#x4F1A;.</p><h2 id="Issues-vs-Questions">Issues vs. Questions<a class="markdown-anchor" href="#Issues-vs-Questions">&#xB6;</a></h2><p>&#x5E38;&#x89C1;&#x7684; issue &#x6709;&#x5982;&#x4E0B;&#x4E24;&#x5927;&#x7C7B;:</p><ol><li>Unexpected behavior / bug: &#x62A5;&#x544A; bug &#x4EE5;&#x53CA;&#x6709;&#x53EF;&#x80FD;&#x662F; bug &#x7684; unexpected behavior.</li><li>Feature request / enhancement: &#x5BF9;&#x9879;&#x76EE;&#x7684;&#x4EE3;&#x7801;, &#x6587;&#x6863;, &#x6CE8;&#x91CA;&#x7B49;&#x7684;&#x5404;&#x7C7B; improvement/enhancement.</li></ol><p>&#x9664;&#x4E86;&#x8FD9;&#x4E9B;&#x4E4B;&#x5916;, &#x7528;&#x6237;&#x5E38;&#x5E38;&#x8FD8;&#x60F3;&#x95EE;&#x5404;&#x79CD;&#x5176;&#x4ED6;&#x95EE;&#x9898;,  &#x8B6C;&#x5982; &quot;&#x600E;&#x4E48;&#x7528; XXX&quot;, &quot;&#x6211;&#x8FD9;&#x6837;&#x505A;&#x5BF9;&#x4E0D;&#x5BF9;&quot;, &quot;&#x9879;&#x76EE;&#x91CC;&#x8FD9;&#x6BB5; code &#x662F;&#x5E72;&#x561B;&#x7528;&#x7684;&quot; &#x7B49;&#x7B49;. &#x6682;&#x4E14;&#x5C06;&#x5B83;&#x4EEC;&#x79F0;&#x4E3A; &quot;question&quot;. &#x6211;&#x8BA4;&#x4E3A;, &#x5927;&#x7684; (issue &#x5F88;&#x591A;&#x7684;) &#x5F00;&#x6E90;&#x9879;&#x76EE;&#x4E2D; <strong>issue &#x91CC;&#x4E0D;&#x5E94;&#x5305;&#x542B;&#x8FD9;&#x4E9B; &quot;question&quot;</strong>, issue &#x5E94;&#x5F53; <strong>&#x4E0D;&#x8D85;&#x8FC7;&#x4E0A;&#x9762;&#x7684;&#x4E24;&#x7C7B;</strong>.</p><p>&#x4E3A;&#x4EC0;&#x4E48;? &#x5F53; issue &#x5F88;&#x591A;&#x7684;&#x65F6;&#x5019;, &quot;question&quot; &#x4E0E;&#x4E24;&#x5927;&#x7C7B; &quot;issue&quot; &#x6709;&#x4E9B;&#x672C;&#x8D28;&#x7684;&#x4E0D;&#x540C;, &#x4F1A;&#x5BFC;&#x81F4; issue &#x96BE;&#x4EE5;&#x7BA1;&#x7406;:</p><ol><li><strong>&#x65E0;&#x6CD5;&#x5B9A;&#x4E49; close</strong>: issue &#x7684;&#x8BBE;&#x8BA1;&#x662F;&#x6709; open/close &#x7684;&#x72B6;&#x6001;&#x7684;, &#x4F46; question &#x5F80;&#x5F80;&#x662F; open-ended, &#x53EA;&#x80FD;&#x4EE5;&#x63D0;&#x95EE;&#x8005;&#x662F;&#x5426;&#x6EE1;&#x610F;&#x6765;&#x5B9A;&#x4E49; close. &#x7136;&#x800C;&#x63D0;&#x95EE;&#x8005;&#x662F;&#x5426;&#x6EE1;&#x610F;&#x662F;&#x5F88;&#x4E3B;&#x89C2;, &#x4E0D;&#x53EF;&#x63A7;&#x7684;: &#x63D0;&#x95EE;&#x8005;&#x53EF;&#x80FD;&#x6C34;&#x5E73;&#x4E0D;&#x9AD8;&#x4E0D;&#x7406;&#x89E3;&#x7B54;&#x6848;,  &#x53EF;&#x80FD;&#x4F1A;&#x6709; follow up &#x7684;&#x8DD1;&#x9898;&#x7684;&#x95EE;&#x9898;,  &#x751A;&#x81F3;&#x53EF;&#x80FD;&#x95EE;&#x9898;&#x90FD;&#x4E0D;&#x6210;&#x7ACB;&#x8BA9;&#x4EBA;&#x65E0;&#x4ECE;&#x56DE;&#x7B54;. &#x5904;&#x7406;&#x8FD9;&#x4E9B;&#x95EE;&#x9898;, &#x4F1A;&#x5E26;&#x6765;&#x5927;&#x91CF;&#x65E0;&#x6CD5;&#x6807;&#x8BB0; open/close &#x72B6;&#x6001;&#x7684; issue, &#x5F71;&#x54CD;&#x9879;&#x76EE;&#x7684;&#x7BA1;&#x7406;. &#x76F8;&#x53CD;, &#x4E0A;&#x6587;&#x63D0;&#x5230;&#x7684;&#x4E24;&#x5927;&#x7C7B; issue &#x662F;&#x53EF;&#x4EE5;&#x8F83;&#x4E3A;&#x5BA2;&#x89C2;&#x7684;&#x5B9A;&#x4E49; close &#x7684;: &#x4E00;&#x65E6;&#x4E00;&#x4E2A; bug / feature request &#x88AB;&#x89E3;&#x51B3;, maintainer &#x4E00;&#x822C;&#x90FD;&#x53EF;&#x4EE5;&#x81EA;&#x4E3B;&#x51B3;&#x5B9A;&#x662F;&#x5426;&#x5C06; issue &#x5173;&#x95ED;.</li><li><strong>Not Actionable</strong>: Maintainer &#x5E0C;&#x671B;&#x5B9E;&#x73B0; &quot;&#x6BCF;&#x4E2A; open issue &#x90FD;&#x662F;&#x4E00;&#x4E2A; todo item&quot;, &#x4EE5;&#x65B9;&#x4FBF;&#x5BF9;&#x9879;&#x76EE;&#x7684;&#x7BA1;&#x7406;. &#x7136;&#x800C;, question &#x4EE5;&#x63D0;&#x95EE;&#x8005;&#x4E3A;&#x4E2D;&#x5FC3;. &#x4ECE;&#x5F00;&#x53D1;&#x8005;&#x7684;&#x89D2;&#x5EA6;&#x6765;&#x770B;, &#x5E76;&#x6CA1;&#x6709;&#x660E;&#x786E;&#x7684; action items.</li><li><strong>&#x592A;&#x5BBD;&#x6CDB;</strong>: &#x4E24;&#x5927;&#x7C7B; issue &#x90FD;&#x53EF;&#x4EE5;&#x8BBE;&#x5B9A;&#x5F88;&#x6E05;&#x6670;&#x7684;&#x683C;&#x5F0F; / &#x6A21;&#x677F;,  &#x56E0;&#x6B64; maintainer &#x53EF;&#x4EE5;&#x901A;&#x8FC7; issue &#x7684;&#x7C7B;&#x522B;&#x548C;&#x6A21;&#x677F;&#x6765;&#x58F0;&#x660E;&#x81EA;&#x5DF1;&#x7684;&#x4E49;&#x52A1;&#x8303;&#x56F4; (&#x540E;&#x6587;&#x4F1A;&#x8BE6;&#x7EC6;&#x4ECB;&#x7ECD;). &#x7136;&#x800C;, question &#x7684;&#x8303;&#x56F4;&#x592A;&#x5E7F;&#x4E86;, &#x7528;&#x6237;&#x7684;&#x95EE;&#x6CD5;&#x5E38;&#x5E38;&#x4F1A;&#x8BA9;&#x4EBA;&#x4E0D;&#x660E;&#x767D;&#x5230;&#x5E95;&#x60F3;&#x95EE;&#x4EC0;&#x4E48;, &#x6216;&#x8005;&#x4E0D;&#x660E;&#x767D;&#x8BE5;&#x600E;&#x4E48;&#x5904;&#x7406;.<br>&#x4F8B;&#x5982;, &#x6709;&#x4E00;&#x7C7B;&#x6211;&#x5F88;&#x5934;&#x75BC;&#x7684;&#x95EE;&#x9898;&#x662F; &quot;&#x8FD9;&#x4E2A;&#x9879;&#x76EE;&#x80FD;&#x4E0D;&#x80FD;&#x505A; xxx?&quot;. &#x8FD9;&#x79CD;&#x8FC7;&#x4E8E;&#x81EA;&#x7531;&#x7684;&#x95EE;&#x6CD5;&#x8BA9; maintainer &#x4E0D;&#x660E;&#x767D;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#x5230;&#x5E95;&#x662F;:<ul><li>feature request: &#x5EFA;&#x8BAE;&#x672A;&#x6765;&#x5728;&#x9879;&#x76EE;&#x91CC;&#x5B9E;&#x73B0; xxx, <em>&#x8FD8;&#x662F;</em></li><li>&#x5BFB;&#x6C42;&#x5199;&#x4EE3;&#x7801;&#x6307;&#x5BFC;: &#x5E0C;&#x671B;&#x4F7F;&#x7528;&#x8FD9;&#x4E2A;&#x9879;&#x76EE;&#x81EA;&#x5DF1;&#x5B9E;&#x73B0; xxx &#x4F46;&#x4E0D;&#x77E5;&#x9053;&#x600E;&#x4E48;&#x505A; --- &#x8FD9;&#x5F80;&#x5F80;&#x4E0D;&#x662F; maintainer &#x7684;&#x4E49;&#x52A1;&#x8303;&#x56F4;.</li></ul></li></ol><p>&#x603B;&#x800C;&#x8A00;&#x4E4B;, question &#x5927;&#x591A;&#x4EE5;&#x7528;&#x6237;&#x4E3A;&#x4E2D;&#x5FC3;, &#x5904;&#x7406;&#x5B83;&#x4EEC;&#x7684;&#x6C9F;&#x901A;&#x6210;&#x672C;&#x66F4;&#x9AD8;,  &#x800C;&#x5BF9;&#x9879;&#x76EE;&#x7684; contribution &#x5374;&#x66F4;&#x4F4E;. &#x6DF7;&#x6742;&#x5728;&#x4EE5;&#x9879;&#x76EE;&#x4E3A;&#x4E2D;&#x5FC3;&#x7684;&#x53E6;&#x4E24;&#x7C7B;&#x66F4;&#x91CD;&#x8981;&#x7684; issue &#x4E2D;&#x4F1A;&#x5206;&#x6563; maintainer &#x7684;&#x7CBE;&#x529B;. &#x56E0;&#x6B64;&#x5F88;&#x591A;&#x5927;&#x7684;&#x9879;&#x76EE;&#x90FD;&#x5E0C;&#x671B;&#x5C06; question &#x5265;&#x79BB;&#x51FA; issue.</p><p>&#x7136;&#x800C;, &#x7528;&#x6237;&#x786E;&#x5B9E;&#x6709;&#x95EE;&#x95EE;&#x9898;&#x6216;&#x8FDB;&#x884C;&#x5176;&#x4ED6;&#x4EA4;&#x6D41;&#x7684;&#x9700;&#x6C42;, &#x8FD9;&#x6837;&#x7684;&#x9700;&#x6C42;&#x53EF;&#x4EE5;&#x7528; github discussions / &#x8BBA;&#x575B;&#x6765;&#x6EE1;&#x8DB3;.</p><h2 id="Github-Discussions-&#x8BBA;&#x575B;">Github Discussions / &#x8BBA;&#x575B;<a class="markdown-anchor" href="#Github-Discussions-&#x8BBA;&#x575B;"> &#xB6;</a></h2><p>Github &#x8FD1;&#x4E24;&#x5E74;&#x63A8;&#x51FA;&#x4E86; <a href="https://docs.github.com/en/discussions/collaborating-with-your-community-using-discussions/about-discussions" aria-label="About discussions - GitHub Docs" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&quot;discussions&quot;</a> &#x7248;&#x5757;.Discussions &#x5728;&#x529F;&#x80FD; / UI &#x4E0A;&#x4E0E; issues &#x6709;&#x6240;&#x533A;&#x522B;, &#x5404;&#x65B9;&#x9762;&#x90FD;&#x66F4;&#x50CF;&#x4F20;&#x7EDF;&#x7684;&#x8BBA;&#x575B;: &#x4F8B;&#x5982;&#x6CA1;&#x6709; open/close/assign &#x7684;&#x72B6;&#x6001;,  &#x53EF;&#x4EE5; &quot;&#x9876;&#x5E16;&quot;, &#x53EF;&#x4EE5; &quot;mark as answer&quot;, &#x7B49;&#x7B49;. &#x7B80;&#x5355;&#x6765;&#x8BF4;, github discussions &#x5C31;&#x662F;&#x63D0;&#x4F9B;&#x4E00;&#x4E2A; <strong>&#x7B80;&#x5316;&#x7248;&#x7684;&#x8BBA;&#x575B;</strong>.</p><p>&#x5728;&#x5185;&#x5BB9;&#x4E0A;, github &#x5E76;&#x6CA1;&#x6709;&#x7ED9; discussions &#x548C; issues &#x5B9A;&#x4E49;&#x660E;&#x786E;&#x7684;&#x8FB9;&#x754C;,  &#x8FD9;&#x4E2A;&#x8FB9;&#x754C;&#x7531;&#x6BCF;&#x4E2A;&#x9879;&#x76EE;&#x81EA;&#x5DF1;&#x5B9A;&#x4E49;:Maintainer &#x5E94;&#x901A;&#x8FC7; issue category &#x548C; issue template &#x6765; <strong>&#x58F0;&#x660E;&#x81EA;&#x5DF1;&#x613F;&#x610F;&#x652F;&#x6301;&#x89E3;&#x51B3;&#x7684; issue</strong> &#x6709;&#x54EA;&#x4E9B; (&#x4F8B;&#x5982; bug report, feature request), &#x5E76;&#x544A;&#x77E5;&#x7528;&#x6237; &quot;&#x5176;&#x4ED6;&quot; &#x8BA8;&#x8BBA; / Question &#x53EF;&#x4EE5;&#x53D1;&#x5230; discussions &#x4E2D;. &#x5982;&#x679C;&#x53D1;&#x9519;&#x4E86;&#x5730;&#x65B9;, maintainer &#x53EF;&#x4EE5;&#x901A;&#x8FC7; github &#x63D0;&#x4F9B;&#x7684;&#x6309;&#x94AE;&#x4E00;&#x952E;&#x5728; issue/discussion &#x4E4B;&#x95F4;&#x8F6C;&#x6362;.</p><p>&#x6211;&#x4EEC;&#x4EE5; PyTorch &#x4E3A;&#x4F8B;. &#x5728; PyTorch &#x7684; issue &#x5217;&#x8868;&#x70B9;&#x51FB; &quot;new issue&quot; &#x540E;,  &#x8FDB;&#x5165; PyTorch &#x7684; <a href="https://github.com/pytorch/pytorch/issues/new/choose">issue &#x7C7B;&#x522B;</a> &#x9875;&#x9762;.</p><img src="/blog/2022/Github-Communications-3/pytorch-github.jpg" class="center"><p>&#x53EF;&#x4EE5;&#x770B;&#x5230;:</p><ol><li><p>PyTorch issue &#x5C31;&#x53EA;&#x5305;&#x542B;&#x4E0A;&#x6587;&#x63D0;&#x5230;&#x7684;&#x4E24;&#x5927;&#x7C7B;: bug &#x4E0E; feature (&#x53EA;&#x662F;&#x7EC6;&#x5206;&#x6210;&#x4E86;&#x66F4;&#x591A;&#x7C7B;).</p><p>&#x5B9E;&#x8DF5;&#x4E0A;&#x628A; documentation &#x7EC6;&#x5206;&#x51FA;&#x4E00;&#x7C7B;&#x662F;&#x5F88;&#x6709;&#x7528;&#x7684;. &#x56E0;&#x4E3A; documentation &#x7684;&#x52D8;&#x8BEF;&#x5230;&#x5E95;&#x662F;&#x5C5E;&#x4E8E; &quot;bug&quot; &#x8FD8;&#x662F; &quot;enhancement&quot; &#x53EF;&#x80FD;&#x4F1A;&#x6709;&#x6B67;&#x4E49;.Documentation &#x88AB;&#x7EC6;&#x5206;&#x540E;, maintainer &#x5C31;&#x53EF;&#x4EE5;&#x5C06; &quot;bug&quot; &#x5B9A;&#x4E49;&#x4E3A;&#x72ED;&#x4E49;&#x7684;&#x4EE3;&#x7801; bug, &#x5C06; &quot;enhancement&quot; &#x5B9A;&#x4E49;&#x4E3A; &quot;feature request&quot;, &#x4F7F;&#x5F97;&#x7C7B;&#x522B;&#x7684;&#x5B9A;&#x4E49;&#x66F4;&#x6E05;&#x6670;.</p></li><li><p>&#x6240;&#x6709; &quot;&#x5176;&#x4ED6;&#x8BA8;&#x8BBA;&quot; &#x90FD;&#x901A;&#x8FC7;&#x6700;&#x540E;&#x4E00;&#x884C;&#x7684;&#x6309;&#x94AE;&#x88AB;&#x5F15;&#x5BFC;&#x5230; PyTorch &#x7684;<a href="https://discuss.pytorch.org/" aria-label="PyTorch Forums" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5B98;&#x65B9; Discourse &#x8BBA;&#x575B;</a>&#x4E0A;. &#x66FE;&#x7ECF;, PyTorch &#x751A;&#x81F3;&#x4E13;&#x95E8;&#x6709;&#x4E00;&#x4E2A;<a href="https://github.com/pytorch/pytorch/blob/v1.9.1/.github/ISSUE_TEMPLATE/questions-help-support.md" aria-label="questions-help-support.md at v1.9.1 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &quot;question&quot; issue template</a> &#x7684;&#x5185;&#x5BB9;&#x5C31;&#x662F; &quot;&#x4E0D;&#x8981;&#x53D1; question, &#x8BF7;&#x7528;&#x8BBA;&#x575B;&quot;. &#x7531;&#x4E8E;&#x907F;&#x514D;&#x4E86; question, PyTorch issue &#x59CB;&#x7EC8;&#x7EF4;&#x6301;&#x4E86;&#x9AD8;&#x8D28;&#x91CF;&#x7684;&#x6280;&#x672F;&#x8BA8;&#x8BBA;, &#x4E5F;&#x8FBE;&#x5230;&#x4E86;&#x7BA1;&#x7406;&#x5F00;&#x53D1;&#x4EFB;&#x52A1;&#x7684; &quot;tracker&quot; &#x529F;&#x80FD;.</p><p>Github discussions &#x7684;&#x5B9A;&#x4F4D;&#x5C31;&#x662F;&#x4E00;&#x4E2A;&#x9879;&#x76EE;&#x81EA;&#x5E26;&#x7684;&#x7B80;&#x6613;&#x8BBA;&#x575B;,  &#x6BD5;&#x7ADF;&#x4E0D;&#x662F;&#x6240;&#x6709;&#x9879;&#x76EE;&#x90FD;&#x6709;&#x8D44;&#x6E90;&#x81EA;&#x5DF1;&#x642D;&#x5EFA;&#x4E00;&#x4E2A;&#x8BBA;&#x575B;.</p></li></ol><p>&#x518D;&#x4EE5; TensorFlow &#x505A;&#x4E2A;&#x53CD;&#x9762;&#x6559;&#x6750;: &#x6211;&#x7531;&#x4E8E;&#x66FE;&#x7ECF;&#x662F;&#x6DF1;&#x5EA6; TF1 &#x7528;&#x6237;, &#x5728;&#x65E9;&#x671F;&#x8FD8;&#x662F;&#x5F88;&#x559C;&#x6B22;&#x770B;&#x5B83;&#x7684; github. &#x7136;&#x800C; TensorFlow &#x957F;&#x671F;&#x6CA1;&#x6709;&#x5BF9; issue &#x8FDB;&#x884C;&#x5206;&#x6D41;. &#x53EF;&#x4EE5;&#x89C2;&#x5BDF;&#x5230;&#x5927;&#x7EA6;&#x5728; 18 &#x5E74;&#x524D;&#x540E;, &#x4F30;&#x8BA1;&#x7531;&#x4E8E; issue &#x7684;&#x566A;&#x58F0;&#x592A;&#x5927;, &#x6027;&#x4EF7;&#x6BD4;&#x592A;&#x4F4E;, TensorFlow issues &#x91CC;&#x5DF2;&#x7ECF;&#x5F88;&#x5C11;&#x518D;&#x6709; core developer &#x56DE;&#x590D;,  &#x5BFC;&#x81F4;&#x771F;&#x6B63;&#x6709;&#x4EF7;&#x503C;&#x7684; issue &#x4E5F;&#x66F4;&#x96BE;&#x4EE5;&#x5F97;&#x5230;&#x91CD;&#x89C6;&#x4E86;. &#x6211;&#x5C31;&#x591A;&#x6B21;&#x9700;&#x8981;&#x9760;&#x624B;&#x52A8; at &#x5BF9;&#x5E94;&#x9886;&#x57DF;&#x6211;&#x8BA4;&#x8BC6;&#x7684; developer &#x624D;&#x80FD;&#x6709;&#x4EBA;&#x56DE;&#x5E94;&#x6211;&#x62A5;&#x7684; bug. &#x76F4;&#x5230; 2021 &#x5E74;, TensorFlow &#x624D;&#x7EC8;&#x4E8E;&#x5F00;&#x59CB;&#x5728; issue template &#x91CC;&#x628A;&#x7528;&#x6237;&#x5F15;&#x5BFC;&#x81F3;&#x81EA;&#x5EFA; Discourse &#x8BBA;&#x575B;.</p><p>&#x6700;&#x540E;&#x8FD8;&#x662F;&#x8981;&#x63D0;&#x9192;: discussions / &#x8BBA;&#x575B;&#x4EC5;&#x9002;&#x7528;&#x4E8E;&#x89C4;&#x6A21;&#x8F83;&#x5927;, &#x95EE;&#x9898;&#x8F83;&#x591A;&#x7684;&#x9879;&#x76EE;. &#x5BF9;&#x5C0F;&#x9879;&#x76EE;, &#x989D;&#x5916;&#x4E00;&#x4E2A;&#x8BA8;&#x8BBA;&#x5E73;&#x53F0;&#x5F15;&#x5165;&#x7684; overhead &#x53EF;&#x80FD;&#x5F97;&#x4E0D;&#x507F;&#x5931;.</p><h2 id="Maintainer&#x7684;&#x4E49;&#x52A1;&#x8303;&#x56F4;">Maintainer &#x7684;&#x4E49;&#x52A1;&#x8303;&#x56F4;<a class="markdown-anchor" href="#Maintainer&#x7684;&#x4E49;&#x52A1;&#x8303;&#x56F4;"> &#xB6;</a></h2><p>&#x5728;<a href="/blog/2022/Github-Communications-1/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(1)" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x7B2C;&#x4E00;&#x7BC7;&#x6587;&#x7AE0;&#x4E2D;&#x8BF4;&#x5230;</a>,maintainer &#x81EA;&#x5DF1;&#x51B3;&#x5B9A;&#x81EA;&#x5DF1;&#x6709;&#x54EA;&#x4E9B;&#x4E49;&#x52A1;, &#x51B3;&#x5B9A;&#x81EA;&#x5DF1;&#x7684; commitment, &#x4E5F;&#x5373;&#x81EA;&#x5DF1;&#x613F;&#x610F;&#x5BF9;&#x7528;&#x6237;&#x63D0;&#x4F9B;&#x54EA;&#x4E9B; &quot;support&quot;. &#x5F88;&#x591A; maintainer &#x4E0E;&#x7528;&#x6237;&#x6C9F;&#x901A;&#x4E0A;&#x7684;&#x95EE;&#x9898;, &#x6E90;&#x4E8E;&#x6CA1;&#x6709;&#x5212;&#x6E05;&#x81EA;&#x5DF1;&#x7684;&#x4E49;&#x52A1;&#x8303;&#x56F4;. &#x4E00;&#x65E6;&#x8FD9;&#x6761;&#x7EBF;&#x5212;&#x6E05;&#x4E86;, maintainer &#x5C31;&#x65E0;&#x9700;&#x4E3A;&#x4E71;&#x4E03;&#x516B;&#x7CDF;&#x7684; issue &#x5934;&#x75BC;: &#x9879;&#x76EE;&#x4E0D; support &#x7684;&#x95EE;&#x9898;&#x4E0D;&#x5FC5;&#x64CD;&#x5FC3;, &#x5173;&#x95ED;&#x6216;&#x8005;&#x79FB;&#x81F3; discussions &#x90FD;&#x53EF;&#x4EE5;.</p><ol><li><p>Maintainer &#x5E94;&#x8BE5;&#x901A;&#x8FC7; issue template &#x7684;&#x9009;&#x9879;&#x8868;&#x660E;&#x54EA;&#x4E9B;&#x7C7B; issue &#x662F;&#x5141;&#x8BB8;&#x7684;. &#x53EF;&#x4EE5;&#x901A;&#x8FC7;<a href="https://github.com/facebookresearch/detectron2/blob/0ad20f13e23f2f4454be6196c1cd38e2171b294c/.github/ISSUE_TEMPLATE/config.yml#L2" aria-label="config.yml &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>blank_issues_enabled: false</code></a> &#x6765;&#x7981;&#x7528; &quot;&#x65E0; template&quot; &#x7684; issue. &#x53EF;&#x4EE5;&#x901A;&#x8FC7;<a href="https://github.com/facebookresearch/detectron2/blob/0ad20f13e23f2f4454be6196c1cd38e2171b294c/.github/ISSUE_TEMPLATE/config.yml#L4-L10" aria-label="config.yml &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>contact_links</code></a> &#x5F15;&#x5BFC; &quot;&#x5176;&#x4ED6;&#x95EE;&#x9898;&quot; &#x5230;&#x522B;&#x7684;&#x5730;&#x65B9;. &#x5982;&#x679C;&#x7528;&#x6237;&#x4F9D;&#x7136;&#x53D1;&#x4E86;&#x4E0D;&#x652F;&#x6301;&#x7684; issue, &#x53EF;&#x4EE5;&#x4EE5; &quot;&#x4E0D;&#x652F;&#x6301;&quot; &#x4E3A;&#x7531;&#x5173;&#x95ED; / &#x79FB;&#x81F3; discussions.</p></li><li><p>Issue template &#x7684;&#x5185;&#x5BB9;&#x91CC;&#x53EF;&#x4EE5;&#x66F4;&#x6E05;&#x695A;&#x7684;&#x58F0;&#x660E;&#x54EA;&#x4E9B;&#x5E38;&#x89C1;&#x60C5;&#x5F62;&#x662F;&#x4E0D;&#x652F;&#x6301;&#x7684;, &#x4F8B;&#x5982;:</p><ul><li>Bug report template &#x53EF;&#x4EE5;&#x58F0;&#x660E; &quot;bug report &#x5FC5;&#x987B;&#x8981;&#x5305;&#x542B;&#x590D;&#x73B0;&#x6B65;&#x9AA4;&quot;.</li><li>Detectron2 &#x7684; issue template<a href="https://github.com/facebookresearch/detectron2/blob/main/.github/ISSUE_TEMPLATE/unexpected-problems-bugs.md#expected-behavior" aria-label="unexpected-problems-bugs.md &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &#x58F0;&#x660E;&#x4E86;</a> &quot;&#x4F60;&#x81EA;&#x5DF1;&#x7684;&#x6A21;&#x578B; train &#x4E0D;&#x597D;&#x6211;&#x4EEC;&#x4E0D;&#x7BA1;&quot;. &#x4F46;&#x51E1;&#x6709;&#x4EBA;&#x62A5;&#x544A;&#x81EA;&#x5DF1;&#x7684;&#x6A21;&#x578B;&#x6027;&#x80FD;&#x4E0D;&#x597D; / &#x4E0D;&#x6536;&#x655B;, &#x6211;&#x5C31;&#x76F4;&#x63A5;&#x5F15;&#x7528;&#x8FD9;&#x53E5;&#x8BDD;&#x7136;&#x540E;&#x5173;&#x95ED; (&#x4E3A;&#x4E86;&#x5F15;&#x7528;&#x65B9;&#x4FBF;, &#x6211;&#x4F9D;&#x7136;&#x4F7F;&#x7528;&#x4E86;<a href="https://docs.github.com/en/get-started/writing-on-github/working-with-saved-replies/using-saved-replies" aria-label="Using saved replies - GitHub Docs" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> saved replies</a>).</li></ul></li><li><p>&#x7528;&#x6237;&#x5E94;&#x8BE5;&#x8BA4;&#x8BC6;&#x5230; &quot;&#x652F;&#x6301; / support&quot; &#x5230;&#x5E95;&#x662F;&#x4EC0;&#x4E48;&#x610F;&#x601D;:</p><ul><li>&quot;We don&apos;t support X&quot; &#x66F4;&#x591A;&#x662F;&#x5173;&#x4E8E;&#x670D;&#x52A1;&#x8303;&#x56F4;&#x7684;&#x58F0;&#x660E;, &#x800C;&#x4E0D;&#x662F;&#x5173;&#x4E8E;&#x9879;&#x76EE;&#x529F;&#x80FD;&#x7684;&#x58F0;&#x660E;: &quot;We don&apos;t support X&quot; &#x4E0D;&#x4EE3;&#x8868; &quot;X doesn&apos;t work&quot;. &quot;We don&apos;t support X&quot; &#x7684;&#x610F;&#x601D;&#x662F; &quot;We won&apos;t help you about X&quot;, &#x4E5F;&#x5373; &quot;&#x6211;&#x4EEC;&#x4E0D;&#x7BA1; X &#x80FD;&#x4E0D;&#x80FD;&#x7528;&quot;.<ul><li>&#x4F8B;&#x5982; detectron2 &#x4E00;&#x76F4;&#x4EE5;&#x6765;&#x90FD;&#x5728; windows &#x4E0A;&#x53EF;&#x7528;&#x751A;&#x81F3;&#x8FD8;&#x6709; CI &#x6D4B;&#x8BD5;, &#x4F46;&#x662F;&#x4ECE;&#x4E0D; &quot;support windows&quot;. &#x5BF9;&#x4E8E;&#x4E0E; windows &#x6709;&#x5173;&#x7684; issue &#x6211;&#x4EEC;&#x4E5F;&#x5C31;&#x4E0D;&#x63D0;&#x4F9B;&#x5E2E;&#x52A9;.</li></ul></li><li>&#x5F00;&#x6E90;&#x793E;&#x533A;&#x4E2D;&#x7684; &quot;support&quot; &#x4E00;&#x8BCD;&#x5927;&#x591A;&#x6570;&#x65F6;&#x5019;&#x90FD;&#x662F;&#x8FD9;&#x4E2A;&#x542B;&#x4E49;.<ul><li>&#x4F8B;&#x5982; Ubuntu &#x7684; LTS (Long-term support) &#x4E2D; &quot;support&quot; &#x7684;&#x610F;&#x601D;, &#x5B98;&#x65B9;&#x662F;&#x8FD9;&#x4E48;<a href="https://ubuntu.com/blog/what-is-an-ubuntu-lts-release#:~:text=LTS%20stands%20for%20long%20term,the%20body%20of%20the%20release." aria-label="What is an Ubuntu LTS release? | Ubuntu" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x89E3;&#x91CA;</a>&#x7684;:<em>&quot;commitment to update, patch and maintain the software&quot;</em>.</li></ul></li></ul></li><li><p>&#x5BF9;&#x4E8E; maintainer &#x804C;&#x8D23;&#x4E4B;&#x5916;&#x7684; issue, &#x5373;&#x4F7F; maintainer &#x4E2A;&#x4EBA;&#x613F;&#x610F;&#x5E2E;&#x52A9;, &#x4E5F;&#x53EF;&#x4EE5;&#x7ACB;&#x523B;&#x5173;&#x95ED; / &#x79FB;&#x81F3; discussion, &#x518D;&#x8FDB;&#x884C;&#x8BC4;&#x8BBA;. &#x8FD9;&#x6837;&#x7684;&#x60C5;&#x51B5;&#x4E0B;, &#x6211;&#x4E00;&#x822C;&#x4F1A;&#x5173;&#x95ED; issue &#x5E76;&#x8BF4;:</p><blockquote><p>Because of ABC, this issue is unsupported/unrelated, therefore closing the issue.</p><p>I think doing XYZ might solve/help the issue.</p></blockquote><p>&#x5728;&#x8FD9;&#x91CC;, &quot;close issue&quot; &#x8868;&#x660E;&#x4E86; issue &#x4E0D;&#x88AB;&#x652F;&#x6301;, &#x8FD9;&#x6837;&#x63D0;&#x524D;&#x907F;&#x514D;&#x7528;&#x6237;&#x7531;&#x4E8E; &quot;&#x5F97;&#x5230;&#x4E86;&#x8BC4;&#x8BBA;&quot; &#x800C;&#x5BF9;&#x4E8E; support &#x6709;&#x4E0D;&#x5207;&#x5B9E;&#x9645;&#x7684;&#x9884;&#x671F;. &#x4E5F;&#x907F;&#x514D;&#x4E86; (&#x5176;&#x4ED6;) maintainer &#x5728;&#x4E0B;&#x6B21;&#x5904;&#x7406; issue &#x5217;&#x8868;&#x65F6;&#x518D;&#x770B;&#x4E00;&#x6B21;.</p><p>&#x540C;&#x65F6;, &#x4E5F;&#x5728;&#x4E0D;&#x9700;&#x8981;&#x82B1;&#x81EA;&#x5DF1;&#x592A;&#x591A;&#x65F6;&#x95F4;&#x7684;&#x524D;&#x63D0;&#x4E0B;&#x7ED9;&#x4E86;&#x7B80;&#x5355;&#x7684;&#x5EFA;&#x8BAE;, &#x4F46;&#x81F3;&#x4E8E;&#x662F;&#x5426;&#x80FD;&#x89E3;&#x51B3;&#x95EE;&#x9898;&#x6211;&#x5C31;&#x4E0D;&#x518D;&#x7BA1;&#x4E86;.</p></li></ol><h2 id="&#x5904;&#x7406;Bugs-Unexpected-Issues">&#x5904;&#x7406; Bugs/Unexpected Issues<a class="markdown-anchor" href="#&#x5904;&#x7406;Bugs-Unexpected-Issues">&#xB6;</a></h2><p>&#x8FD9;&#x4E00;&#x8282;&#x8BF4;&#x8BF4;&#x5BF9;&#x4E8E; bugs/unexpected issues &#x7684;&#x5E38;&#x89C1;&#x5904;&#x7406;&#x6D41;&#x7A0B;&#x548C;&#x6CE8;&#x610F;&#x4E8B;&#x9879;.</p><p><strong>&#x4F7F;&#x7528; Issue Template</strong>: <a href="/blog/2022/Github-Communications-2/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(2): &#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5;bug" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x4E0A;&#x7BC7;&#x6587;&#x7AE0;</a>&#x4E2D;&#x8BF4;&#x4E86;&#x7528;&#x6237;&#x62A5;&#x544A; unexpected issues &#x65F6;&#x9700;&#x8981;&#x63D0;&#x4F9B;&#x7684;&#x51E0;&#x7C7B;&#x4FE1;&#x606F;: expectation, unexpected observation, environment, reproducible example.Maintainer &#x5E94;&#x8BE5;&#x4F7F;&#x7528; issue template &#x6765;&#x544A;&#x77E5; / &#x5F15;&#x5BFC;&#x7528;&#x6237;&#x63D0;&#x4F9B;&#x8FD9;&#x4E9B;&#x4FE1;&#x606F;.</p><p>Detectron2 &#x7684;<a href="https://github.com/facebookresearch/detectron2/blob/main/.github/ISSUE_TEMPLATE/unexpected-problems-bugs.md" aria-label="unexpected-problems-bugs.md &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &quot;unexpected problems&quot; issue template</a> &#x53EF;&#x4EE5;&#x4F5C;&#x4E3A;&#x53C2;&#x8003;. Facebook AI Research &#x7684;&#x5176;&#x4ED6;&#x4E00;&#x4E9B; project &#x4E5F;&#x53C2;&#x8003;&#x4E86;&#x8FD9;&#x4E2A; template (&#x5982;<a href="https://github.com/facebookresearch/pytorch3d/blob/7660ed187683bff6cb21f68bf8659cd88b83062c/.github/ISSUE_TEMPLATE/bugs.md" aria-label="bugs.md &#xB7; facebookresearch/pytorch3d" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> pytorch3d</a>,<a href="https://github.com/facebookresearch/vissl/blob/5a0019809f747666b9f30e6ab3dfbecd3362435b/.github/ISSUE_TEMPLATE/unexpected-problems-bugs.md" aria-label="unexpected-problems-bugs.md &#xB7; facebookresearch/vissl" class="hint--top hint--rounded hint--no-animate hint--no-arrow">vissl</a>).</p><p><strong>&#x68C0;&#x67E5;&#x5FC5;&#x8981;&#x7684;&#x4FE1;&#x606F;</strong>: &#x8FD8;&#x662F;&#x6709;&#x4E0D;&#x5C11;&#x7528;&#x6237;&#x4E0D;&#x5C0A;&#x91CD; issue template, &#x4E0D;&#x63D0;&#x4F9B;&#x9700;&#x8981;&#x7684;&#x4FE1;&#x606F;. &#x4EE5;&#x4E0B;&#x51E0;&#x4E2A;&#x65B9;&#x6848;&#x53EF;&#x80FD;&#x6709;&#x5E2E;&#x52A9;:</p><ul><li>&#x4F7F;&#x7528; github &#x7684;<a href="https://docs.github.com/en/get-started/writing-on-github/working-with-saved-replies" aria-label="Working with saved replies - GitHub Docs" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> saved replies</a> &#x529F;&#x80FD;, &#x4E00;&#x952E;&#x53D1;&#x9001;&#x5E38;&#x7528;&#x56DE;&#x590D;. &#x6211;&#x7684; saved replies &#x91CC;&#x5C31;&#x5305;&#x542B;&#x8FD9;&#x6837;&#x4E00;&#x53E5;&#x8BDD;:<blockquote><p>If you need help to solve an unexpected issue you observed, please include details following theXXX issue template (link).</p></blockquote></li><li>&#x624B;&#x52A8;&#x8BC4;&#x8BBA;&#x8FD8;&#x662F;&#x9EBB;&#x70E6;&#x7684;, &#x6240;&#x4EE5;&#x6211;&#x5B9E;&#x73B0;&#x4E86;&#x4E00;&#x4E2A;<a href="https://github.com/facebookresearch/detectron2/blob/f7bc78e67e3bc21846470d8bbab86cd624361a93/.github/workflows/check-template.yml" aria-label="check-template.yml &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> github bot</a> &#x6765;&#x68C0;&#x6D4B;&#x4E00;&#x4E9B;&#x7279;&#x522B;&#x660E;&#x663E;&#x7684;&#x4FE1;&#x606F;&#x7F3A;&#x5931;, &#x5E76;&#x81EA;&#x52A8;&#x8BC4;&#x8BBA;.</li><li>&#x6211;&#x548C; bot &#x90FD;&#x4F1A;&#x4E3A;&#x7F3A;&#x5C11;&#x4FE1;&#x606F;&#x7684; issue &#x6253;&#x4E0A;<a href="https://github.com/facebookresearch/detectron2/issues?q=is%3Aissue+label%3Aneeds-more-info" aria-label="Issues &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &quot;needs-more-info&quot; &#x6807;&#x7B7E;</a>. &#x8FD9;&#x4E2A;&#x6807;&#x7B7E;&#x53EF;&#x4EE5;&#x544A;&#x8BC9;&#x5176;&#x4ED6; maintainer &#x4E0D;&#x5FC5;&#x518D;&#x67E5;&#x770B;&#x8FD9;&#x4E2A; issue.Maintainer &#x4E5F;&#x53EF;&#x4EE5;&#x4EE5;&#x8FD9;&#x4E2A;&#x6807;&#x7B7E;&#x4E3A;&#x4F9D;&#x636E;&#x5728;&#x4E00;&#x6BB5;&#x65F6;&#x95F4;&#x540E;&#x5173;&#x95ED; issue.</li><li>&#x672A;&#x6765;&#x7684;<a href="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#creating-issue-forms" aria-label="Configuring issue templates for your repository - GitHub Docs" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> issue form</a>, &#x6709;&#x5E0C;&#x671B;&#x901A;&#x8FC7;&#x66F4;&#x4E25;&#x683C;&#x7684;&#x683C;&#x5F0F;&#x6765;&#x7F13;&#x89E3;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;.</li></ul><p><strong>&#x5206;&#x6790;, &#x89E3;&#x51B3; issue</strong>: &#x4EFB;&#x4F55;&#x4E00;&#x4E2A;&#x6709;&#x8DB3;&#x591F;&#x4FE1;&#x606F;&#x7684; unexpected issue, &#x5E94;&#x8BE5; <strong>&#x6709;&#x4E14;&#x4EC5;&#x6709;</strong> &#x5982;&#x4E0B;&#x51E0;&#x79CD;&#x7ED3;&#x679C;:</p><ul><li>Issue &#x4E0D;&#x5B58;&#x5728;&#x6216;&#x65E0;&#x6CD5;&#x786E;&#x8BA4; (&#x4F8B;&#x5982;: Expectation &#x4E0D;&#x6B63;&#x786E;, &#x7A0B;&#x5E8F; working as expected, &#x7528;&#x6237;&#x81EA;&#x5DF1;&#x9519;&#x4E86;, &#x65E0;&#x6CD5; reproduce &#x7B49;&#x7B49;):<ul><li>Maintainer &#x5E94;&#x89E3;&#x91CA;&#x539F;&#x56E0; (&#x6700;&#x597D;&#x662F;&#x76F4;&#x63A5;&#x5F15;&#x7528;, &#x800C;&#x4E0D;&#x662F;&#x4EBA;&#x5DE5;&#x89E3;&#x91CA;) &#x5E76;&#x5173;&#x95ED; issue</li><li>Maintainer &#x5E94;&#x8003;&#x8651;&#x662F;&#x5426;&#x6709;&#x63D0;&#x5347;&#x7528;&#x6237;&#x4F53;&#x9A8C;&#x7684;&#x673A;&#x4F1A;, &#x6765;&#x907F;&#x514D;&#x7C7B;&#x4F3C;&#x7684;&#x95EE;&#x9898;&#x88AB;&#x91CD;&#x590D;. &#x5305;&#x62EC;:<ul><li>&#x4F18;&#x5316;&#x6587;&#x6863;: &#x8BA9;&#x7528;&#x6237;&#x66F4;&#x5BB9;&#x6613;&#x53D1;&#x73B0;&#x6B63;&#x786E;&#x7684;&#x4FE1;&#x606F;, &#x6709;&#x6B63;&#x786E;&#x7684; expectation</li><li>&#x4F18;&#x5316;&#x7A0B;&#x5E8F;&#x7684; logging: &#x8BA9;&#x7528;&#x6237;&#x7406;&#x89E3;&#x7A0B;&#x5E8F;&#x7684;&#x884C;&#x4E3A;</li><li>&#x52A0;&#x5165;&#x4E00;&#x4E9B; early check &#x6765;&#x66F4;&#x65E9;&#x7684;&#x53D1;&#x73B0; error</li><li>&#x63D0;&#x4F9B;&#x66F4;&#x6E05;&#x6670;&#x7684; error message</li><li>&#x8BA9; error message &#x66F4; actionable -- &#x4E0D;&#x4EC5;&#x8BF4;&#x54EA;&#x91CC;&#x9519;&#x4E86;, &#x8FD8;&#x544A;&#x8BC9;&#x7528;&#x6237;&#x8BE5;&#x600E;&#x4E48;&#x529E;</li><li>&#x4F8B;&#x5982;, <a href="https://github.com/facebookresearch/detectron2/pull/4215" aria-label="Better error message in lazyconfig relative import by ppwwyyxx &#xB7; Pull Request #4215 &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">1</a>, <a href="https://github.com/facebookresearch/detectron2/commit/2413859fda93a17581e6e9c17062990c1a87a0b0" aria-label="raise clear error message for mismatched keypoint metadata &#xB7; facebookresearch/detectron2@2413859" class="hint--top hint--rounded hint--no-animate hint--no-arrow">2</a>,<a href="https://github.com/facebookresearch/detectron2/commit/2538c9d1b1f86db507d114b4c7e06a04c828fbc4" aria-label="check for errors in SemSegFPNHead &#xB7; facebookresearch/detectron2@2538c9d" class="hint--top hint--rounded hint--no-animate hint--no-arrow">3</a>,<a href="https://github.com/facebookresearch/detectron2/commit/8bdde383d69a042b0cb6ee8ab7bb7e22d8ce3ef7" aria-label="remove warnings about cell_anchors &#xB7; facebookresearch/detectron2@8bdde38" class="hint--top hint--rounded hint--no-animate hint--no-arrow">4</a> &#x5C31;&#x662F;&#x6211;&#x770B;&#x5230;&#x7528;&#x6237;&#x5728; issue &#x4E2D;&#x7684;&#x56F0;&#x60D1;&#x540E;, &#x5BF9; logging/error &#x7684;&#x4E00;&#x4E9B;&#x5FAE;&#x5C0F;&#x4F18;&#x5316;. &#x4EFB;&#x4F55;&#x7CFB;&#x7EDF;&#x7684;&#x6587;&#x6863; / logging &#x90FD;&#x6C38;&#x8FDC;&#x6709;&#x63D0;&#x5347;&#x7A7A;&#x95F4;, &#x7528;&#x6237;&#x4F53;&#x9A8C;&#x5FC5;&#x987B;&#x7ECF;&#x8FC7;&#x8FD9;&#x6837;&#x7684;&#x8FED;&#x4EE3;&#x624D;&#x80FD;&#x5F97;&#x5230;&#x63D0;&#x5347;.</li><li>&#x4E00;&#x4E2A;&#x5F88;&#x6709;&#x7528;&#x7684;&#x6280;&#x5DE7;&#x662F;, &#x5728;&#x56DE;&#x590D;&#x8FD9;&#x7C7B; issue &#x65F6;, maintainer &#x5E94;&#x5C3D;&#x91CF;&#x5C1D;&#x8BD5;&#x4EC5;&#x901A;&#x8FC7; <strong>&#x76F4;&#x63A5;&#x5F15;&#x7528; log &#x6216;&#x6587;&#x6863;</strong> &#x6765;&#x56DE;&#x7B54;. &#x5982;&#x679C;&#x505A;&#x4E0D;&#x5230;, &#x90A3;&#x5E38;&#x5E38;&#x80FD;&#x53D1;&#x73B0; log &#x6216;&#x6587;&#x6863;&#x7684;&#x4E0D;&#x8DB3;&#x4E4B;&#x5904;.</li></ul></li></ul></li><li>Issue &#x91CD;&#x590D;<ul><li>&#x5E94;&#x5173;&#x95ED;&#x5E76;&#x94FE;&#x63A5;&#x5230;&#x53E6;&#x4E00;&#x4E2A; issue, &#x628A;&#x540C;&#x4E00;&#x4E2A;&#x95EE;&#x9898;&#x7684;&#x5BF9;&#x8BDD;&#x96C6;&#x4E2D;&#x5230;&#x4E00;&#x5904;</li><li>&#x6CA1;&#x6709;&#x4EC0;&#x4E48;&#x68C0;&#x6D4B; duplicate &#x7684;&#x597D;&#x529E;&#x6CD5;, &#x5E0C;&#x671B;&#x672A;&#x6765; NLP &#x6280;&#x672F;&#x80FD;&#x6709;&#x6240;&#x5E2E;&#x52A9;</li></ul></li><li>Issue &#x7531;&#x4E8E;&#x9879;&#x76EE;&#x4EE5;&#x5916;&#x7684;&#x539F;&#x56E0;&#x4EA7;&#x751F; (&#x73AF;&#x5883;, &#x4F9D;&#x8D56;)<ul><li>Maintainer &#x4F9D;&#x636E;&#x5176;&#x4E25;&#x91CD;&#x6027;&#x51B3;&#x5B9A;&#x662F;&#x5426;&#x5173;&#x95ED; issue</li><li>&#x867D;&#x7136; issue &#x4E0D;&#x6765;&#x81EA;&#x9879;&#x76EE;&#x81EA;&#x8EAB;, maintainer &#x5E94;&#x8003;&#x8651;&#x662F;&#x5426;&#x503C;&#x5F97;&#x52A0;&#x5165; workaround / warning &#x6765;&#x589E;&#x52A0;&#x9879;&#x76EE;&#x7684;&#x53EF;&#x7528;&#x6027;</li><li>&#x5BF9;&#x4E8E;&#x4F9D;&#x8D56;&#x7684;&#x95EE;&#x9898;, maintainer &#x5E94;&#x6307;&#x5411;&#x4E0A;&#x6E38;&#x4F9D;&#x8D56;&#x7684;&#x5BF9;&#x5E94; issue. &#x5982;&#x679C;&#x4E0A;&#x6E38;&#x6CA1;&#x6709;&#x8FD9;&#x4E2A; issue, maintainer &#x5E94;&#x5411;&#x4E0A;&#x6E38;&#x62A5;&#x544A;</li></ul></li><li>Issue &#x6765;&#x81EA;&#x4E8E;&#x9879;&#x76EE;&#x81EA;&#x8EAB;&#x7684; bug<ul><li>Issue &#x5E94;&#x6C38;&#x8FDC;&#x4FDD;&#x6301; open, &#x76F4;&#x5230;&#x88AB;&#x4FEE;&#x590D;</li></ul></li><li>Issue &#x53EF;&#x4EE5;&#x786E;&#x8BA4;&#x5B58;&#x5728;, &#x4F46;&#x65E0;&#x6CD5;&#x5224;&#x65AD;&#x539F;&#x56E0;<ul><li>Issue &#x5E94;&#x6C38;&#x8FDC;&#x4FDD;&#x6301; open, &#x76F4;&#x5230;&#x53D1;&#x73B0;&#x539F;&#x56E0;&#x540E;&#x53D8;&#x4E3A;&#x4EE5;&#x4E0A;&#x51E0;&#x79CD;&#x60C5;&#x51B5;&#x4E4B;&#x4E00;</li></ul></li></ul><p>&#x53EF;&#x4EE5;&#x770B;&#x5230;, &#x4EE5;&#x4E0A;&#x51E0;&#x79CD;&#x7ED3;&#x679C;&#x57FA;&#x672C;&#x90FD;&#x662F;&#x5BF9;&#x9879;&#x76EE;&#x6709; contribution &#x7684;. &#x751A;&#x81F3;&#x5373;&#x4F7F; issue &#x6700;&#x7EC8;&#x4E0D;&#x5B58;&#x5728;, maintainer &#x4E5F;&#x53EF;&#x80FD;&#x4ECE; unexpected issues &#x4E2D;&#x770B;&#x5230;&#x63D0;&#x5347;&#x7528;&#x6237;&#x4F53;&#x9A8C;&#x7684;&#x673A;&#x4F1A;. &#x56E0;&#x6B64; unexpected issues / bugs &#x5BF9;&#x9879;&#x76EE;&#x6709;&#x5F88;&#x5927;&#x4EF7;&#x503C;.</p><h2 id="&#x5404;&#x7C7B;bot">&#x5404;&#x7C7B; bot<a class="markdown-anchor" href="#&#x5404;&#x7C7B;bot">&#xB6;</a></h2><p>&#x4ECB;&#x7ECD;&#x4E00;&#x4E9B;&#x7BA1;&#x7406; issue &#x7684; bot:</p><ul><li><p>&#x4E0A;&#x9762;&#x63D0;&#x5230;&#x8FC7;&#x7684;<a href="https://github.com/facebookresearch/detectron2/blob/f7bc78e67e3bc21846470d8bbab86cd624361a93/.github/workflows/check-template.yml" aria-label="check-template.yml &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x68C0;&#x67E5; issue &#x662F;&#x5426;&#x5305;&#x542B;&#x5FC5;&#x8981;&#x4FE1;&#x606F;&#x7684; bot</a>. &#x7136;&#x800C;&#x4E3A;&#x4E86;&#x7528;&#x6237;&#x4F53;&#x9A8C;, &#x8FD9;&#x4E2A; bot &#x662F; precision-driven &#x7684;, &#x53EA;&#x68C0;&#x6D4B;&#x6700;&#x660E;&#x663E;&#x7684;&#x60C5;&#x51B5;, recall &#x5E76;&#x4E0D;&#x9AD8;.</p></li><li><p><a href="https://github.com/facebookresearch/detectron2/blob/f62b8a42e4d995966d0e7d7eb4049f023860b5ff/.github/workflows/needs-reply.yml" aria-label="needs-reply.yml &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x81EA;&#x52A8;&#x5173;&#x95ED; &quot;needs-more-info&quot; &#x7684; issue</a>: &#x5982;&#x679C; issue &#x6709;&#x4E86; &quot;needs-more-info&quot; &#x7684;&#x6807;&#x7B7E;, &#x7B49;&#x5F85;&#x7528;&#x6237;&#x63D0;&#x4F9B;&#x5FC5;&#x8981;&#x7684;&#x4FE1;&#x606F;, &#x5374;&#x957F;&#x65F6;&#x95F4;&#x6CA1;&#x6709; update, &#x5C31;&#x4F1A;&#x88AB; bot &#x81EA;&#x52A8;&#x5173;&#x95ED;. &#x5F53;&#x6709;&#x4E86; update &#x65F6;, &#x6807;&#x7B7E;&#x4F1A;&#x88AB;<a href="https://github.com/facebookresearch/detectron2/blob/f62b8a42e4d995966d0e7d7eb4049f023860b5ff/.github/workflows/remove-needs-reply.yml" aria-label="remove-needs-reply.yml &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x4E2A; workflow</a> &#x81EA;&#x52A8;&#x79FB;&#x9664;.</p></li><li><p><a href="https://github.com/facebookresearch/detectron2/blob/f62b8a42e4d995966d0e7d7eb4049f023860b5ff/.github/workflows/needs-reply.yml#L88-L98" aria-label="needs-reply.yml &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x81EA;&#x52A8;&#x9501;&#x5B9A;&#x53E4;&#x8001; issue</a>: &#x5982;&#x679C;&#x9879;&#x76EE;&#x4E00;&#x76F4;&#x5728;&#x6D3B;&#x8DC3;&#x5F00;&#x53D1;, &#x90A3;&#x4E48;&#x4E00;&#x4E2A;&#x53E4;&#x8001;&#x7684;, &#x5DF2;&#x89E3;&#x51B3;&#x7684; bug &#x5F88;&#x53EF;&#x80FD;&#x6CA1;&#x6709;&#x4EFB;&#x4F55;&#x503C;&#x5F97; follow up &#x7684;&#x4FE1;&#x606F;: &#x5373;&#x4F7F;&#x7C7B;&#x4F3C;&#x7684; bug &#x53C8;&#x51FA;&#x73B0;&#x4E86;, &#x5927;&#x6982;&#x7387;&#x4E5F;&#x548C;&#x65E7;&#x7684; bug &#x6CA1;&#x4EC0;&#x4E48;&#x5173;&#x7CFB;. &#x90A3;&#x4E48;&#x53EF;&#x4EE5;&#x5BF9;&#x6B64;&#x7C7B; issue &#x8BBE;&#x5B9A;&#x4E3A;&#x9759;&#x9ED8;&#x4E00;&#x5E74;&#x540E;&#x81EA;&#x52A8;&#x9501;&#x5B9A; (&#x7981;&#x6B62;&#x8BC4;&#x8BBA;).</p></li><li><p><a href="https://github.com/pytorch/test-infra/blob/8e76798cccde469d413decb29b3decee83341e94/torchci/lib/bot/autoLabelBot.ts" aria-label="autoLabelBot.ts &#xB7; pytorch/test-infra" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x81EA;&#x52A8; label</a>:Github &#x652F;&#x6301;&#x6309;&#x7167; issue template &#x6765;&#x81EA;&#x52A8; label, &#x4F46;&#x662F;&#x90A3;&#x6837;&#x7684;&#x7C92;&#x5EA6;&#x592A;&#x7C97;. &#x5982;&#x679C;&#x5BF9;&#x4E8E;&#x7279;&#x5B9A;&#x7C7B;&#x7684; issue &#x80FD;&#x591F;&#x6839;&#x636E;&#x5185;&#x5BB9;&#x6765;&#x7CBE;&#x51C6;&#x5339;&#x914D;&#x7684;&#x8BDD;, &#x4E5F;&#x53EF;&#x4EE5;&#x7528;&#x8FD9;&#x4E2A; bot &#x6DFB;&#x52A0; label. &#x4F46;&#x662F;&#x9700;&#x8981;&#x6CE8;&#x610F;&#x81EA;&#x7136;&#x8BED;&#x8A00;&#x5904;&#x7406;&#x662F;&#x5F88;&#x56F0;&#x96BE;&#x7684;, &#x7ED9;&#x8FD9;&#x4E2A; bot &#x5199;&#x89C4;&#x5219;&#x5E76;&#x4E0D;&#x5BB9;&#x6613;.</p></li><li><p><a href="https://github.com/pytorch/pytorch/issues/24422" aria-label="Label tracking meta-issue (edit me to get automatically CC&apos;ed on issues! cc bot) &#xB7; Issue #24422 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x81EA;&#x52A8;&#x8BA2;&#x9605; label</a>: &#x5DE8;&#x578B;&#x9879;&#x76EE;&#x4E2D;, &#x5F00;&#x53D1;&#x8005;&#x60F3;&#x8981;&#x81EA;&#x52A8; subscribe &#x7279;&#x5B9A;&#x6A21;&#x5757;&#x76F8;&#x5173;&#x7684; issue. &#x8FD9;&#x4E2A; bot &#x6309;&#x7167; issue &#x7684; label &#x81EA;&#x52A8;&#x6DFB;&#x52A0; &quot;@username&quot; &#x6765; subscribe &#x611F;&#x5174;&#x8DA3;&#x7684;&#x5F00;&#x53D1;&#x8005;.</p></li><li><p><a href="https://github.com/marketplace/stale" aria-label="Stale &#xB7; GitHub Marketplace" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Stale bot</a>: &#x81EA;&#x52A8;&#x5173;&#x95ED;&#x4E00;&#x6BB5;&#x65F6;&#x95F4;&#x6CA1;&#x6709; activity &#x7684; issue. &#x8FD9;&#x4E2A; bot &#x5F88;&#x5E38;&#x89C1;, &#x4F46; <strong>&#x4E0D;&#x5E94;&#x8BE5;&#x88AB;&#x4F7F;&#x7528;</strong>,  &#x56E0;&#x4E3A;&#x6CA1;&#x6709; activity &#x4E0D;&#x4EE3;&#x8868; issue &#x89E3;&#x51B3;&#x4E86;. &#x53C2;&#x8003;:</p><ul><li><a href="https://drewdevault.com/2021/10/26/stalebot.html" aria-label="GitHub stale bot considered harmful" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Github stale bot considered harmful</a></li><li><a href="https://twitter.com/soumithchintala/status/1451213207750721538">Soumith &#x5BF9; stale bot &#x7684;&#x6279;&#x8BC4;</a></li><li><a href="https://tisonkun.org/2021/12/05/effective-open-source-participant/" aria-label="&#x9AD8;&#x6548;&#x53C2;&#x4E0E;&#x5F00;&#x6E90;&#x7684;&#x8BC0;&#x7A8D; | &#x591C;&#x5929;&#x4E4B;&#x4E66;" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x9AD8;&#x6548;&#x53C2;&#x4E0E;&#x5F00;&#x6E90;&#x7684;&#x8BC0;&#x7A8D;</a> &#x4E5F;&#x6279;&#x8BC4;&#x4E86; stale bot.</li></ul><p>&#x6CE8;&#x610F;&#x8FD9;&#x91CC;&#x5047;&#x8BBE;&#x4E86; issue &#x548C; question &#x662F;&#x88AB;&#x533A;&#x5206;&#x5F00;&#x7684;. &#x5982;&#x679C; question &#x4E5F;&#x88AB;&#x5305;&#x62EC;&#x5728; issue &#x91CC;, &#x81EA;&#x52A8;&#x5173;&#x95ED; question &#x662F;&#x53EF;&#x4EE5;&#x63A5;&#x53D7;&#x7684;.</p></li></ul>]]></content>
    
    
    <summary type="html">
&lt;p&gt;&amp;#x6211;&amp;#x542C;&amp;#x8FC7;&amp;#x4E0D;&amp;#x5C11;&amp;#x4EBA;&amp;#x51ED;&amp;#x501F;&amp;#x7231;&amp;#x597D;&amp;#x5F00;&amp;#x6E90;&amp;#x4E86;&amp;#x81EA;&amp;#x5DF1;&amp;#x7684;&amp;#x9879;&amp;#x76EE;&amp;#x540E;, &amp;#x5374;&amp;#x5BF9; issue &amp;#x592A;&amp;#x4E71;&amp;#x611F;&amp;#x5230;&amp;#x56F0;&amp;#x6270;, &amp;#x751A;&amp;#x81F3;&amp;#x60F3;&amp;#x5E72;&amp;#x8106;&amp;#x76F4;&amp;#x63A5;&amp;#x7981;&amp;#x7528; issue.
 &amp;#x5176;&amp;#x5B9E;, &amp;#x4EFB;&amp;#x4F55;&amp;#x9879;&amp;#x76EE;&amp;#x8FBE;&amp;#x5230;&amp;#x4E00;&amp;#x5B9A;&amp;#x89C4;&amp;#x6A21;&amp;#x540E;, &amp;#x5982;&amp;#x679C;&amp;#x4E0D;&amp;#x5BF9; issue &amp;#x8FDB;&amp;#x884C;&amp;#x9002;&amp;#x5F53;&amp;#x7BA1;&amp;#x7406;, &amp;#x90FD;&amp;#x4F1A;&amp;#x4F7F; issue &amp;#x4FE1;&amp;#x566A;&amp;#x6BD4;&amp;#x8FC7;&amp;#x4F4E;, &amp;#x5931;&amp;#x53BB;&amp;#x539F;&amp;#x672C;&amp;#x7684;&amp;#x529F;&amp;#x80FD;.&lt;/p&gt;
&lt;p&gt;&amp;#x8FD9;&amp;#x7BC7;&amp;#x6587;&amp;#x7AE0;&amp;#x4E3B;&amp;#x8981;&amp;#x4ECE; maintainer &amp;#x7684;&amp;#x89D2;&amp;#x5EA6;&amp;#x8BF4;&amp;#x8BF4;, &amp;#x5728;&amp;#x5177;&amp;#x5907;&amp;#x89C4;&amp;#x6A21;&amp;#x7684;&amp;#x9879;&amp;#x76EE;&amp;#x4E2D;&amp;#x7BA1;&amp;#x7406; issue &amp;#x7684;&amp;#x4E00;&amp;#x4E9B;&amp;#x65B9;&amp;#x6CD5;&amp;#x548C;&amp;#x539F;&amp;#x5219;.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Open Source" scheme="https://ppwwyyxx.com/blog/tags/Open-Source/"/>
    
  </entry>
  
  <entry>
    <title>谈谈Github上如何交流(2): 如何科学的报bug</title>
    <link href="https://ppwwyyxx.com/blog/2022/Github-Communications-2/"/>
    <id>https://ppwwyyxx.com/blog/2022/Github-Communications-2/</id>
    <published>2022-05-07T12:00:00.000Z</published>
    <updated>2022-05-07T12:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<div class="message is-info my-custom-sidebar"><div class="message-header">     <i class="fas fa-info-circle mr-2"></i><p>&#x672C;&#x7CFB;&#x5217;&#x6587;&#x7AE0;</p></div><div class="message-body"><ol><li><a href="/blog/2022/Github-Communications-1/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(1)" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8C08;&#x8C08; Github &#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41; (1)</a></li><li><a href="/blog/2022/Github-Communications-2/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(2): &#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5;bug" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5; bug</a></li><li><a href="/blog/2022/Github-Communications-3/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(3): &#x5982;&#x4F55;&#x7BA1;&#x7406;issue" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5982;&#x4F55;&#x7BA1;&#x7406; issue</a></li><li><a href="/blog/2022/Github-Communications-4/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(4): {Feature,Pull} Request" class="hint--top hint--rounded hint--no-animate hint--no-arrow">{Feature,Pull} Request</a></li></ol></div></div><p>&#x62A5;&#x544A;&#x9519;&#x8BEF; / &#x62A5; bug &#x662F;&#x7528;&#x6237;&#x4E0E;&#x5F00;&#x53D1;&#x8005;&#x95F4;&#x6700;&#x5E38;&#x89C1;&#x7684;&#x4E00;&#x7C7B;&#x4EA4;&#x6D41;, &#x4E5F;&#x662F;&#x5E38;&#x89C1;&#x7684; github issue. &#x4F46;&#x662F;&#x5F88;&#x591A;&#x7528;&#x6237;&#x5E76;&#x4E0D;&#x4F1A;&#x79D1;&#x5B66;&#x7684;&#x62A5; bug, maintainer &#x5BF9;&#x6B64;&#x4E5F;&#x7F3A;&#x4E4F;&#x5F15;&#x5BFC;. &#x56E0;&#x6B64;&#x8FD9;&#x7BC7;&#x6587;&#x7AE0;&#x8BA8;&#x8BBA;&#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5; bug.</p><span id="more"></span><p>&#x5982;&#x4F55;&#x62A5; bug, &#x4E0D;&#x4EC5;&#x9002;&#x7528;&#x4E8E;&#x5F00;&#x6E90;&#x793E;&#x533A;, &#x4E5F;&#x9002;&#x7528;&#x4E8E;&#x4EFB;&#x4F55;&#x8F6F;&#x4EF6;&#x5F00;&#x53D1;. &#x4E0A;&#x4E00;&#x7BC7;<a href="/blog/2022/Github-Communications-1/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(1)" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x63D0;&#x5230;</a>, &#x5F00;&#x6E90;&#x793E;&#x533A;&#x7684;&#x4EA4;&#x6D41;&#x96BE;&#x5EA6;&#x6BD4;&#x4E00;&#x822C;&#x7684;&#x56E2;&#x961F;&#x5408;&#x4F5C;&#x66F4;&#x5927;. &#x5982;&#x679C;&#x638C;&#x63E1;&#x4E86;&#x5728;&#x5F00;&#x6E90;&#x793E;&#x533A;&#x4E2D;&#x62A5; bug / &#x4FEE; bug &#x7684;&#x4EA4;&#x6D41;&#x65B9;&#x5F0F;,  &#x5728;&#x516C;&#x53F8;&#x91CC;&#x5904;&#x7406;&#x7C7B;&#x4F3C;&#x7684;&#x4E8B;&#x60C5;&#x4E5F;&#x4F1A;&#x66F4;&#x8F7B;&#x677E;.</p><h2 id="Unexpected-Issues">Unexpected Issues<a class="markdown-anchor" href="#Unexpected-Issues">&#xB6;</a></h2><p>&#x9996;&#x5148;, &quot;&#x62A5; bug&quot; &#x662F;&#x4E00;&#x4E2A;&#x8F83;&#x4E3A;&#x72ED;&#x4E49;&#x7684;&#x8BF4;&#x6CD5;.</p><p>&#x5728;&#x6709;&#x7684;&#x9879;&#x76EE;&#x91CC;, &#x7528;&#x6237;&#x5BB9;&#x6613;&#x786E;&#x5B9A;&#x4E00;&#x4E2A;&#x95EE;&#x9898;&#x662F;&#x4E0D;&#x662F; &quot;bug&quot;. &#x4F46;&#x5728;&#x6709;&#x4E9B;&#x9879;&#x76EE;&#x91CC;,  &#x7528;&#x6237;&#x672A;&#x5FC5;&#x6709;&#x80FD;&#x529B;&#x5224;&#x65AD;&#x95EE;&#x9898;&#x5230;&#x5E95;&#x662F;&#x4E0D;&#x662F;&#x7531;&#x4E8E;&#x9879;&#x76EE;&#x7684; bug &#x4EA7;&#x751F;&#x7684;. &#x7A0B;&#x5E8F;&#x7684;&#x9519;&#x8BEF;&#x53EF;&#x80FD;&#x6765;&#x81EA;&#x4E8E;&#x7528;&#x6237;&#x81EA;&#x5DF1;, &#x7528;&#x6237;&#x7684;&#x73AF;&#x5883;, &#x6216;&#x5176;&#x4ED6;&#x4F9D;&#x8D56;.</p><p>&#x8FD9;&#x65F6;&#x5019;, &#x62A5;&#x544A; &quot;unexpected issues&quot; &#x662F;&#x4E2A;&#x66F4;&#x5408;&#x9002;&#x7684;&#x8BF4;&#x6CD5;: &#x7528;&#x6237;&#x62A5;&#x544A;&#x7684;&#x662F;&#x672A;&#x9884;&#x671F;&#x7684;&#x884C;&#x4E3A; (unexpected observations/behaviors, &#x4E0D;&#x4E00;&#x5B9A;&#x662F; error), &#x7136;&#x540E;&#x7531;&#x66F4;&#x4E86;&#x89E3;&#x60C5;&#x51B5;&#x7684;&#x4EBA;&#x5224;&#x65AD;&#x5B83;&#x4EEC;&#x662F;&#x4E0D;&#x662F; bug.</p><h2 id="What-is-Expected-Unexpected">What is Expected/Unexpected<a class="markdown-anchor" href="#What-is-Expected-Unexpected">&#xB6;</a></h2><p>&#x8981;&#x62A5;&#x544A; unexpected issue, &#x7528;&#x6237;&#x5E94;&#x9996;&#x5148;&#x4E00;&#x5B9A; <strong>&#x786E;&#x4FDD;&#x5BF9;&#x65B9;&#x660E;&#x767D;&#x81EA;&#x5DF1;&#x7684; expectation</strong>.</p><ul><li><p>Expectation &#x6709;&#x65F6;&#x5019;&#x662F;&#x5F88;&#x663E;&#x7136;&#x7684;, &#x6BD4;&#x5982; expect &#x7A0B;&#x5E8F;&#x6B63;&#x5E38;&#x8FD0;&#x884C;&#x4F46;&#x662F;&#x5B83;&#x5D29;&#x6E83;&#x4E86;. &#x7136;&#x800C;, &#x5F88;&#x591A;&#x65F6;&#x5019;, expectation &#x4E5F;&#x8BB8;&#x5BF9;&#x95EE;&#x9898;&#x7684;&#x62A5;&#x544A;&#x8005;&#x663E;&#x7136;, <strong>&#x5BF9;&#x522B;&#x4EBA;&#x5374;&#x672A;&#x5FC5;</strong>.</p><ul><li><p>&#x4F8B;&#x5982;: &#x4E00;&#x4E2A;&#x5E38;&#x89C1;&#x60C5;&#x51B5;&#x662F;&#x7528;&#x6237;&#x5199;&#x4E86;&#x4E00;&#x5927;&#x6BB5;&#x6587;&#x5B57;&#x63CF;&#x8FF0;&#x81EA;&#x5DF1;&#x505A;&#x4E86;&#x4EC0;&#x4E48;, &#x7A0B;&#x5E8F;&#x505A;&#x4E86;&#x4EC0;&#x4E48;&#x8F93;&#x51FA;&#x4E86;&#x4EC0;&#x4E48;,  &#x770B;&#x5B8C;&#x6839;&#x672C;&#x4E0D;&#x660E;&#x767D;&#x5230;&#x5E95;&#x54EA;&#x91CC;&#x662F; unexpected. &#x901A;&#x8FC7;&#x53CD;&#x590D;&#x8BE2;&#x95EE;&#x624D;&#x4E86;&#x89E3;&#x5230;, &#x7528;&#x6237;&#x7684; expectation &#x662F; &quot;&#x7A0B;&#x5E8F;&#x4E0D;&#x8F93;&#x51FA; XXX&quot;. &#x8FD9;&#x6837;&#x7684; expectation, &#x672A;&#x5FC5;&#x90A3;&#x4E48;&#x663E;&#x7136;.</p><p>&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;&#x5F80;&#x5F80;&#x662F;&#x6A21;&#x7CCA;&#x7684;. &#x8981;&#x786E;&#x4FDD;&#x5BF9;&#x65B9;&#x660E;&#x767D;&#x4F60;&#x7684; expectation, &#x4EE5; &quot;&#x6211; expect ...&quot; &#x4E3A;&#x5F00;&#x5934;&#x9020;&#x53E5;&#x6700;&#x6E05;&#x695A;. &#x4E0A;&#x9762;&#x7684;&#x4F8B;&#x5B50;&#x91CC;, &#x5982;&#x679C;&#x7528;&#x6237;&#x80FD;&#x5728;&#x6D41;&#x6C34;&#x5E10;&#x7684;&#x4FE1;&#x606F;&#x4E4B;&#x5916;, &#x6E05;&#x695A;&#x7684;&#x8BF4;&#x51FA; &quot;&#x6211; expect ...&quot;, &#x5219;&#x907F;&#x514D;&#x4E86;&#x4F4E;&#x6548;&#x7684;&#x4EA4;&#x6D41;.</p></li></ul></li><li><p>&#x56E0;&#x4E3A;&#x7528;&#x6237;&#x7684;&#x8BEF;&#x89E3;, expectation &#x672C;&#x8EAB;&#x53EF;&#x80FD;&#x662F; <strong>&#x9519;&#x8BEF;&#x7684;, &#x6CA1;&#x6709;&#x6839;&#x636E;&#x7684;, &#x6216;&#x4E0D;&#x88AB;&#x652F;&#x6301;&#x7684;</strong>. &#x4F8B;&#x5982;:</p><ul><li>&#x7528;&#x6237;: &quot;&#x6211; expect &#x8FD9;&#x4E2A; API &#x8F93;&#x51FA;&#x8FD9;&#x6837;&#x7684;&#x683C;&#x5F0F;&quot;. &#x7EF4;&#x62A4;&#x8005;: &quot;&#x8BF7;&#x770B;&#x6587;&#x6863;, &#x5B83;&#x8F93;&#x51FA;&#x7684;&#x662F;&#x53E6;&#x5916;&#x7684;&#x683C;&#x5F0F;&quot;.</li><li>&#x7528;&#x6237;: &quot;&#x6211; expect &#x65B9;&#x6CD5; A &#x6BD4; B &#x5FEB;&quot;. &#x7EF4;&#x62A4;&#x8005;: &quot;&#x8FD9;&#x4E2A; expectation &#x6CA1;&#x6709;&#x6839;&#x636E;, A &#x548C; B &#x65F6;&#x5FEB;&#x65F6;&#x6162;, &#x4E0D;&#x597D;&#x8BF4;&quot;.</li><li>&#x7528;&#x6237;: &quot;&#x6211; expect &#x8BAD;&#x7EC3;&#x6211;&#x8FD9;&#x4E2A;&#x6A21;&#x578B;&#x4E0D;&#x70B8;&quot;. &#x7EF4;&#x62A4;&#x8005;: &quot;&#x60F3;&#x6CD5;&#x5F88;&#x597D;, &#x4E0B;&#x6B21;&#x4E0D;&#x8981;&#x95EE;&#x4E86;. &#x6211;&#x4EEC;&#x4E0D;&#x8D1F;&#x8D23;&#x8FD9;&#x4E2A;&quot;.</li></ul><p>&#x7531;&#x8BEF;&#x89E3;&#x4EA7;&#x751F;&#x7684; expectation &#x53EF;&#x80FD;&#x5C31;&#x66F4;&#x4E0D;&#x663E;&#x7136;&#x4E86;. &#x53EA;&#x6709;&#x6E05;&#x695A;&#x7684;&#x8BF4;&#x51FA;&#x6765;&#x624D;&#x80FD;&#x5C3D;&#x65E9;&#x6F84;&#x6E05;&#x8FD9;&#x7C7B;&#x8BEF;&#x89E3;.</p></li></ul><p>&#x8981;&#x8BF4;&#x6E05;&#x695A; expectation, &#x4E00;&#x822C;&#x8981;&#x5305;&#x542B;&#x4E24;&#x4E2A;&#x90E8;&#x5206;:</p><ul><li><strong>&#x505A;&#x4E86;&#x4EC0;&#x4E48;</strong>: &#x8FD0;&#x884C;&#x4E86;&#x4EC0;&#x4E48;&#x547D;&#x4EE4;, &#x5199;&#x4E86;&#x4EC0;&#x4E48;&#x4EE3;&#x7801;, &#x70B9;&#x4E86;&#x4EC0;&#x4E48;&#x6309;&#x94AE;, &#x7B49;&#x7B49;.</li><li>&#x671F;&#x5F85;&#x770B;&#x5230;&#x4EC0;&#x4E48; <strong>&#x73B0;&#x8C61;</strong>: &#x671F;&#x5F85;&#x7A0B;&#x5E8F;&#x4E0D;&#x5D29;&#x6E83;, &#x671F;&#x5F85;&#x7A0B;&#x5E8F;&#x8F93;&#x51FA;&#x7279;&#x5B9A;&#x5185;&#x5BB9;, &#x7B49;&#x7B49;.</li></ul><h2 id="Describe-Observations-Not-Presumed-Behaviors">Describe Observations, Not Presumed Behaviors<a class="markdown-anchor" href="#Describe-Observations-Not-Presumed-Behaviors">&#xB6;</a></h2><p>&#x7528;&#x6237;&#x5E94;&#x63CF;&#x8FF0;&#x81EA;&#x5DF1;&#x770B;&#x5230;&#x4E86;&#x4EC0;&#x4E48; <strong>&#x73B0;&#x8C61; (observations)</strong> , &#x800C;&#x4E0D; (&#x4EC5;) &#x662F;&#x81EA;&#x5DF1;&#x4EE5;&#x4E3A;&#x7A0B;&#x5E8F;&#x505A;&#x4E86;&#x4EC0;&#x4E48; (presumed behaviors). &#x56E0;&#x4E3A;&#x7528;&#x6237;&#x672A;&#x5FC5;&#x7406;&#x89E3;&#x7A0B;&#x5E8F;&#x5230;&#x5E95;&#x505A;&#x4E86;&#x4EC0;&#x4E48;, &#x4E5F;&#x672A;&#x5FC5;&#x6709;&#x80FD;&#x529B;&#x63CF;&#x8FF0;&#x597D;&#x7A0B;&#x5E8F;&#x7684;&#x884C;&#x4E3A;.</p><p>&#x4F5C;&#x4E3A;&#x4E00;&#x4E2A;&#x7528;&#x6237;, &#x4F60; expect &#x7A0B;&#x5E8F;&#x505A; X, &#x4F46;&#x662F;&#x7A0B;&#x5E8F;&#x597D;&#x50CF;&#x6CA1;&#x505A; X / &#x505A;&#x4E86; Y, &#x56E0;&#x6B64;&#x4F60;&#x60F3;&#x62A5;&#x544A; unexpected issue. &#x8FD9;&#x65F6;&#x5019;, &#x4E0D;&#x8981;&#x4E0B;&#x7ED3;&#x8BBA;&#x8BF4;&#x7A0B;&#x5E8F;&#x505A;&#x4E86; / &#x6CA1;&#x505A;&#x4EC0;&#x4E48;, &#x56E0;&#x4E3A;:</p><ol><li>&#x8FD9;&#x4E2A;&#x5224;&#x65AD;&#x53EF;&#x80FD;&#x662F;&#x9519;&#x8BEF;&#x7684;. &#x7A0B;&#x5E8F;&#x53EF;&#x80FD;&#x5DF2;&#x7ECF;&#x505A;&#x4E86; X, &#x6216;&#x8005;&#x7A0B;&#x5E8F;&#x505A;&#x4E86; Z (&#x800C;&#x4E0D;&#x662F; Y). &#x58F0;&#x79F0;&#x7A0B;&#x5E8F;&#x505A;&#x4E86;&#x4EC0;&#x4E48;&#x53EF;&#x80FD;&#x4F1A;&#x8BEF;&#x5BFC;&#x522B;&#x4EBA;.</li><li>&#x4F60;&#x7684;&#x63CF;&#x8FF0;&#x53EF;&#x80FD;&#x662F;&#x6A21;&#x7CCA;, &#x4E0D;&#x597D;&#x7406;&#x89E3;&#x7684;. &#x60F3;&#x8C61;&#x4E00;&#x4E2A;&#x4E0D;&#x61C2;&#x7535;&#x8111;&#x7684;&#x4EBA;&#x95EE;&#x4F60; &quot;&#x7535;&#x8111;&#x6253;&#x4E0D;&#x5F00;&#x4E86;&#x600E;&#x4E48;&#x529E;&quot;, &#x201C;&#x4E0D;&#x80FD;&#x4E0A;&#x7F51;&#x600E;&#x4E48;&#x529E; &quot;--- &#x4F60;&#x7684;&#x7B2C;&#x4E00;&#x53CD;&#x5E94;&#x80AF;&#x5B9A;&#x662F;&quot; &#x4EC0;&#x4E48;&#x53EB;&#x6253;&#x4E0D;&#x5F00; / &#x4E0D;&#x80FD;&#x4E0A;&#x7F51;&#xFF1F;&quot;. &#x5F53;&#x4F60;&#x63CF;&#x8FF0;&#x4E00;&#x4E2A;&#x81EA;&#x5DF1;&#x4E0D;&#x592A;&#x4E86;&#x89E3;&#x7684;&#x7A0B;&#x5E8F;&#x7684;&#x884C;&#x4E3A;&#x7684;&#x65F6;&#x5019;, &#x5728;&#x522B;&#x4EBA;&#x773C;&#x91CC;&#x53EF;&#x80FD;&#x4E5F;&#x662F;&#x7C7B;&#x4F3C;&#x7684;.</li></ol><p>&#x5982;&#x679C;&#x4F60;&#x89C9;&#x5F97;&#x7A0B;&#x5E8F;&#x505A;&#x4E86;&#x9519;&#x8BEF;&#x7684;&#x4E8B;&#x60C5;, &#x5F53;&#x7136;&#x53EF;&#x4EE5;&#x63D0;&#x4F9B;&#x81EA;&#x5DF1;&#x7684;&#x5224;&#x65AD;&#x548C;&#x5206;&#x6790;,  &#x4F46;&#x6700;&#x9700;&#x8981;&#x63D0;&#x4F9B;&#x7684;&#x662F;&#x80FD;&#x591F;&#x652F;&#x6301;&#x4F60;&#x7684;&#x5224;&#x65AD;&#x7684; observations, &#x4F8B;&#x5982;&#x539F;&#x59CB;&#x7684; logs (&#x5982;&#x679C; observation &#x4E0E;&#x56FE;&#x7247;&#x6709;&#x5173;, &#x622A;&#x56FE;).</p><p>&#x76F8;&#x6BD4;&#x63CF;&#x8FF0; &quot;behavior&quot; &#x6765;&#x8BF4;, &#x63D0;&#x4F9B; observation &#x6709;&#x8FD9;&#x4E9B;&#x597D;&#x5904;:</p><ol><li><p><strong>&#x66F4;&#x7B80;&#x5355;</strong>: &#x4F60;&#x53EA;&#x8981;&#x590D;&#x5236;&#x7C98;&#x8D34;. &#x4E0D;&#x9700;&#x8981;&#x4E86;&#x89E3;&#x8FD9;&#x4E2A;&#x7A0B;&#x5E8F;</p></li><li><p><strong>&#x65E0;&#x6B67;&#x4E49;</strong>: &#x590D;&#x5236;&#x7C98;&#x8D34;&#x53EF;&#x4EE5;&#x66F4;&#x5B8C;&#x6574;&#x7684;&#x8FD8;&#x539F;&#x4F60;&#x7684; observation, &#x907F;&#x514D;&#x4E86;&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;&#x7684;&#x6B67;&#x4E49;&#x6027;.</p></li><li><p>&#x63D0;&#x4F9B; <strong>&#x5B8C;&#x6574;&#x7684; observations</strong> &#x7684;&#x8BDD;, &#x5176;&#x4ED6;&#x4EBA;&#x5C31;&#x53EF;&#x4EE5;&#x8DF3;&#x8FC7;&#x7528;&#x6237;&#x7684;&#x5224;&#x65AD;, <strong>&#x72EC;&#x7ACB;&#x5224;&#x65AD;</strong> &#x5230;&#x5E95;&#x53D1;&#x751F;&#x4E86;&#x4EC0;&#x4E48;. &#x8FD9;&#x5BF9;&#x5206;&#x6790; unexpected issue &#x662F;&#x81F3;&#x5173;&#x91CD;&#x8981;&#x7684;. &#x7528;&#x6237;&#x81EA;&#x5DF1;&#x7684;&#x5224;&#x65AD;&#x53EF;&#x80FD;&#x662F;&#x9519;&#x7684;, &#x4E3E;&#x51E0;&#x4E2A;&#x4F8B;&#x5B50;:</p><ul><li>&#x7528;&#x6237;&#x5224;&#x65AD;&#x7A0B;&#x5E8F;&#x8DD1;&#x7684;&#x6162;, &#x8FD9;&#x65F6;&#x5019;&#x7528;&#x6237;&#x5E94;&#x8BE5;&#x63D0;&#x4F9B;&#x81EA;&#x5DF1;&#x8DD1; benchmark &#x7684;&#x4EE3;&#x7801; / &#x5DE5;&#x5177;, &#x548C;&#x5B83;&#x4EEC;&#x7684;&#x8F93;&#x51FA;. &#x771F;&#x5B9E;&#x60C5;&#x51B5;&#x4E5F;&#x8BB8;&#x662F;, benchmark &#x7684;&#x65B9;&#x5F0F;&#x4E0D;&#x5BF9;, &#x6216;&#x6D4B;&#x91CF;&#x7684;&#x5355;&#x4F4D;&#x53D8;&#x4E86;.<ul><li>&#x5728; deep learning &#x91CC;&#x592A;&#x5E38;&#x89C1;&#x4E86;: &#x6B63;&#x786E;&#x7684; benchmark<a href="https://www.zhihu.com/question/265848305/answer/311168537" aria-label="TensorFlow &#x5230;&#x5E95;&#x6162;&#x5728;&#x54EA;&#x91CC;&#xFF1F; - &#x77E5;&#x4E4E;" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &#x5E76;&#x4E0D;&#x5BB9;&#x6613;&#x505A;</a>; &#x6D4B;&#x91CF;&#x5355;&#x4F4D;&#x5728;&#x6709;&#x7684;&#x7CFB;&#x7EDF;&#x91CC;&#x4F1A;&#x968F;&#x7740; batch size &#x53D8;&#x5316;.</li></ul></li><li>&#x56E0;&#x4E3A; log &#x91CC;&#x6709; error X, &#x7528;&#x6237;&#x5224;&#x65AD;&#x7A0B;&#x5E8F;&#x7531;&#x4E8E; X &#x5D29;&#x6E83;&#x4E86;. &#x4F46;&#x662F;&#x53EF;&#x80FD; log &#x91CC;&#x53E6;&#x5916;&#x7684; Y &#x624D;&#x662F;&#x5D29;&#x6E83;&#x7684; root cause. &#x7528;&#x6237;&#x5E94;&#x8BE5;&#x63D0;&#x4F9B;&#x5B8C;&#x6574;&#x7684; log, &#x8BA9;&#x522B;&#x4EBA;&#x72EC;&#x7ACB;&#x505A;&#x51FA;&#x5224;&#x65AD;.</li><li>&#x7528;&#x6237;&#x6253;&#x5F00;<code>feature_A=True</code> &#x4E4B;&#x540E;&#x89E6;&#x53D1;&#x4E86; failure X, &#x56E0;&#x6B64;&#x5224;&#x65AD;<code>feature_A</code> &#x5BFC;&#x81F4;&#x4E86; X. &#x4F46;&#x4E8B;&#x5B9E;&#x53EF;&#x80FD;&#x662F;, <code>feature_A=False</code> &#x4E5F;&#x4F1A;&#x89E6;&#x53D1; failure X, &#x53EA;&#x662F;&#x7531;&#x4E8E;&#x5176;&#x4ED6;&#x539F;&#x56E0; X &#x6CA1;&#x6709;&#x66B4;&#x9732;&#x51FA;&#x6765;.</li></ul><p>&#x4E0E;&#x6B64;&#x76F8;&#x5BF9;&#x7684;, maintainer &#x4E0D;&#x8981;&#x8FC7;&#x5EA6;&#x76F8;&#x4FE1;&#x7528;&#x6237;&#x58F0;&#x79F0;&#x7684; behavior. &#x5E94;&#x8BE5;&#x4ECE;&#x7528;&#x6237;&#x63D0;&#x4F9B;&#x7684;&#x4FE1;&#x606F;&#x4E2D;&#x5224;&#x65AD;&#x7528;&#x6237;&#x58F0;&#x79F0;&#x7684; unexpected behavior &#x662F;&#x5426;&#x771F;&#x7684;&#x53D1;&#x751F;&#x4E86;.</p></li></ol><p>&#x6211;&#x4E00;&#x822C;&#x90FD;&#x4F1A;&#x5728; issue template &#x91CC;&#x8981;&#x6C42;&#x7528;&#x6237;&#x63D0;&#x4F9B; <strong>&#x5B8C;&#x6574;&#x7684; log</strong> . &#x8FD9;&#x662F;&#x6027;&#x4EF7;&#x6BD4;&#x6700;&#x9AD8;&#x7684;&#x4FE1;&#x606F;: &#x4E0D;&#x4EC5;&#x80FD;&#x591F;&#x7528;&#x6765;&#x5224;&#x65AD;&#x7A0B;&#x5E8F;&#x7684;&#x884C;&#x4E3A;, &#x8FD8;&#x80FD;&#x591F;&#x5E2E;&#x52A9; debug, &#x7528;&#x6237;&#x4E5F;&#x5F88;&#x5BB9;&#x6613;&#x63D0;&#x4F9B;. &#x4F46;&#x8FD8;&#x662F;&#x603B;&#x6709;&#x4EBA;&#x5728;&#x62A5;&#x544A; error &#x7684;&#x65F6;&#x5019;&#x53EA;&#x7ED9;&#x4E00;&#x884C; error message, &#x8FDE; stack trace &#x90FD;&#x6CA1;&#x6709;,  &#x8BA9;&#x4EBA;&#x5F88;&#x5934;&#x75BC;. &#x5E0C;&#x671B;&#x672A;&#x6765;&#x7684;<a href="https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#creating-issue-forms" aria-label="Configuring issue templates for your repository - GitHub Docs" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> github issue form</a> &#x80FD;&#x591F;&#x901A;&#x8FC7;&#x5F3A;&#x5236;&#x5FC5;&#x586B;&#x7684;&#x8868;&#x5355;&#x6765;&#x66F4;&#x597D;&#x7684;&#x6559;&#x80B2;&#x7528;&#x6237;.</p><p>&#x91CD;&#x8981;&#x7684;&#x4E8B;&#x60C5;&#x518D;&#x8BF4;&#x4E00;&#x904D;: maintainer &#x9700;&#x8981; <strong>&#x5168;&#x90E8;&#x7684;, &#x5B8C;&#x6574;&#x7684; log</strong>, &#x800C;&#x4E0D;&#x4EC5;&#x4EC5;&#x662F; error &#x53D1;&#x751F;&#x524D;&#x7684; log. &#x5728;&#x7528;&#x6237;&#x770B;&#x6765;&#x6CA1;&#x6709;&#x7528;&#x7684;&#x4FE1;&#x606F;&#x5BF9; maintainer &#x53EF;&#x80FD;&#x662F;&#x6709;&#x7528;&#x7684;, &#x4E0D;&#x8981;&#x7701;&#x7565;&#x5B83;&#x4EEC;.</p><p>&#x53E6;&#x5916;, &#x65E2;&#x7136;&#x5728;&#x62A5;&#x544A; unexpected issue, &#x7528;&#x6237;&#x63D0;&#x4F9B;&#x7684; observation &#x5F53;&#x7136;&#x5E94;&#x8BE5;&#x6E05;&#x695A;&#x7684;&#x5305;&#x542B; &quot;unexpected&quot; &#x7684;&#x90E8;&#x5206;. &#x7528;&#x6237;&#x9700;&#x8981;&#x8BA9; maintainer &#x80FD;&#x591F;&#x4ECE; observations &#x4E2D;&#x770B;&#x5230;&#x8FD9;&#x4E2A; unexpected issue &#x786E;&#x5B9E;&#x53D1;&#x751F;&#x4E86;.</p><h2 id="Minimal-Reproducible-Example-MRE">Minimal Reproducible Example (MRE)<a class="markdown-anchor" href="#Minimal-Reproducible-Example-MRE">&#xB6;</a></h2><p>Stackoverflow &#x7684;<a href="https://stackoverflow.com/help/how-to-ask" aria-label="How do I ask a good question? - Help Center - Stack Overflow" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &quot;How to ask a good question&quot;</a> &#x91CC;&#x6709;&#x63D0;&#x5230;<a href="https://stackoverflow.com/help/minimal-reproducible-example" aria-label="How to create a Minimal, Reproducible Example - Help Center - Stack Overflow" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &quot;Minimal Reproducible Example (MRE)&quot;</a> &#x7684;&#x6982;&#x5FF5;,  &#x5EFA;&#x8BAE;&#x9605;&#x8BFB;.</p><p>&#x5728;&#x5F00;&#x6E90;&#x793E;&#x533A;&#x7684;&#x573A;&#x666F;&#x4E0B;, &#x62A5;&#x544A;&#x4E00;&#x4E2A; unexpected issue &#x7684;&#x65F6;&#x5019;, &#x7528;&#x6237;&#x4E5F;&#x5E94;&#x8BE5;&#x5C3D;&#x91CF;&#x4EE5;&#x4EE3;&#x7801;, &#x547D;&#x4EE4;, &#x6570;&#x636E;&#x7684;&#x5F62;&#x5F0F;&#x63D0;&#x4F9B; minimal reproducible example. &#x5176;&#x610F;&#x4E49;&#x5728;&#x4E8E;:</p><ol><li>&#x5E2E;&#x52A9;&#x5224;&#x65AD; issue &#x662F;&#x4E0D;&#x662F; &quot;&#x9879;&#x76EE;&#x7684;&#x95EE;&#x9898;&quot;, &#x56E0;&#x6B64;&#x4F7F;&#x8FD9;&#x4E2A; issue &#x5BF9;&#x9879;&#x76EE;&#x6709; &quot;contribution&quot;.<ul><li><strong>Reproducible</strong>, &#x6216; verifiable, &#x610F;&#x601D;&#x662F;&#x522B;&#x4EBA;&#x80FD;&#x591F;&#x590D;&#x73B0;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;.</li><li><strong>Minimal</strong> &#x7684;&#x610F;&#x601D;&#x662F;, &#x7528;&#x6765;&#x590D;&#x73B0;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#x7684;&#x4EE3;&#x7801; / &#x6570;&#x636E;&#x7279;&#x522B;&#x5C11;. &#x56E0;&#x6B64;&#x5F88;&#x5BB9;&#x6613;&#x5224;&#x65AD;&#x662F;&#x7528;&#x6237;&#x81EA;&#x5DF1;&#x7528;&#x9519;&#x4E86;, &#x8FD8;&#x662F;&#x9879;&#x76EE;&#x9519;&#x4E86;.</li></ul></li><li>&#x5E2E;&#x52A9; maintainer debug, &#x7814;&#x7A76; issue &#x7684;&#x89E3;&#x51B3;&#x65B9;&#x6848;.</li></ol><p>&#x53CD;&#x8FC7;&#x6765;:</p><ul><li>&#x5982;&#x679C;&#x4E00;&#x4E2A; issue &#x4E0D; reproducible &#x7684;&#x8BDD;, maintainer &#x5F88;&#x96BE;&#x76F8;&#x4FE1;&#x8FD9;&#x4E2A;&#x95EE;&#x9898;&#x5B58;&#x5728;, &#x6216;&#x5373;&#x4F7F;&#x5B58;&#x5728;&#x4E5F;&#x5F88;&#x96BE;&#x53BB; debug.</li><li>&#x5982;&#x679C;&#x7528;&#x6237;&#x63D0;&#x4F9B;&#x7684; reproducible example &#x8FC7;&#x4E8E;&#x590D;&#x6742;&#x7684;&#x8BDD;, maintainer &#x4E0D;&#x613F;&#x610F;&#x4E5F;&#x6CA1;&#x6709;&#x4E49;&#x52A1;&#x82B1;&#x65F6;&#x95F4;&#x7406;&#x89E3;&#x7528;&#x6237;&#x7684;&#x4EE3;&#x7801;, &#x66F4;&#x4E0D;&#x613F;&#x610F;&#x5E2E;&#x7740;&#x627E;&#x7528;&#x6237;&#x81EA;&#x5DF1;&#x7684; bug.</li></ul><p>&#x4E3A;&#x4E86;&#x63D0;&#x4F9B;&#x4E00;&#x4E2A;&#x9AD8;&#x8D28;&#x91CF;&#x7684; MRE:</p><ul><li>&#x7528;&#x6237;&#x5E94;&#x8BE5;&#x95EE;&#x81EA;&#x5DF1;: &#x522B;&#x4EBA;&#x6309;&#x7167;&#x6211;&#x63D0;&#x4F9B;&#x7684;&#x6B65;&#x9AA4;&#x80FD;&#x591F;&#x72EC;&#x7ACB;&#x7684; reproduce &#x8FD9;&#x4E2A; issue &#x5417;? &#x6709;&#x6CA1;&#x6709;&#x6F0F;&#x4EC0;&#x4E48;&#x5173;&#x952E;&#x7684;&#x6B65;&#x9AA4;, &#x6570;&#x636E;? &#x80FD;&#x4E0D;&#x80FD;&#x628A;&#x6211;&#x7684;&#x79C1;&#x6709;&#x6570;&#x636E;&#x6362;&#x6210;&#x516C;&#x5F00;&#x6570;&#x636E;&#x6216;&#x8005; fake/mock &#x6570;&#x636E;? <ul><li>Maintainer &#x4E5F;&#x5E94;&#x8BE5;&#x4E3A;&#x9879;&#x76EE;&#x7684;&#x4E0D;&#x540C;&#x6A21;&#x5757;&#x63D0;&#x4F9B;&#x6837;&#x4F8B;&#x8F93;&#x5165;&#x6570;&#x636E;</li></ul></li><li>&#x7528;&#x6237;&#x5982;&#x679C;&#x613F;&#x610F;&#x914D;&#x5408;, &quot;reproducible&quot; &#x5927;&#x90E8;&#x5206;&#x65F6;&#x5019;&#x53EF;&#x4EE5;&#x6EE1;&#x8DB3;.<ul><li>&#x9664;&#x4E86;&#x90A3;&#x79CD;&#x672C;&#x8EAB;&#x9700;&#x8981;&#x5927;&#x91CF;&#x65F6;&#x95F4; / &#x8BA1;&#x7B97;&#x8D44;&#x6E90;&#x624D;&#x80FD;&#x590D;&#x73B0;, &#x6216;&#x968F;&#x673A;&#x51FA;&#x73B0;&#x7684; issue -- &#x90A3;&#x6837;&#x7684;&#x96BE;&#x9898;&#x6CA1;&#x4EC0;&#x4E48;&#x597D;&#x7684;&#x529E;&#x6CD5;, &#x8981;&#x4F9D;&#x8D56;&#x7528;&#x6237;&#x81EA;&#x5DF1;&#x505A;&#x5927;&#x90E8;&#x5206;&#x7684; debug &#x5DE5;&#x4F5C;. <a href="/blog/2020/Fight-Against-Silent-Bugs-in-Deep-Learning-Libraries/" aria-label="Fight Against Silent Bugs in Deep Learning Libraries" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Fight Against Silent Bugs in Deep Learning Libraries</a>&#x5C31;&#x8BB0;&#x5F55;&#x4E86;&#x6211;&#x600E;&#x4E48; debug TensorFlow NCCL &#x91CC;&#x7684;&#x4E00;&#x4E2A;&#x968F;&#x673A;&#x51FA;&#x73B0;&#x7684;&#x8BA1;&#x7B97;&#x9519;&#x8BEF;.</li></ul></li><li>&#x800C; &quot;minimal&quot; &#x5219;&#x4F1A;&#x9700;&#x8981;&#x7528;&#x6237;&#x6295;&#x5165;&#x4E00;&#x5B9A;&#x7684;&#x65F6;&#x95F4;, &#x56E0;&#x4E3A;&#x7528;&#x6237;&#x53D1;&#x73B0;&#x95EE;&#x9898;&#x65F6;, &#x4E5F;&#x8BB8;&#x81EA;&#x5DF1;&#x7684;&#x7A0B;&#x5E8F;&#x4EE3;&#x7801;&#x592A;&#x590D;&#x6742;, &#x5E76;&#x4E0D; minimal. &#x4E3A;&#x4E86;&#x8FBE;&#x5230; &quot;minimal&quot;,Stackoverflow &#x63D0;&#x4F9B;&#x4E86;<a href="https://stackoverflow.com/help/minimal-reproducible-example" aria-label="How to create a Minimal, Reproducible Example - Help Center - Stack Overflow" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x4E24;&#x4E2A;&#x6709;&#x7528;&#x7684;&#x5EFA;&#x8BAE;</a>, &#x4E00;&#x822C;&#x9700;&#x8981;&#x4EA4;&#x66FF;&#x4F7F;&#x7528;:<ol><li>Start from scratch: &#x5982;&#x679C;&#x5BF9;&#x95EE;&#x9898;&#x7684;&#x89E6;&#x53D1;&#x6761;&#x4EF6;&#x6709;&#x4E86;&#x731C;&#x60F3;, &#x53EF;&#x4EE5;&#x4ECE;&#x5934;&#x5199;&#x4E00;&#x4E2A;&#x7B80;&#x5355;&#x7248;&#x672C;&#x770B;&#x770B;&#x662F;&#x5426;&#x80FD;&#x89E6;&#x53D1; issue</li><li>Divide and conquer: &#x5982;&#x679C;&#x5BF9;&#x89E6;&#x53D1;&#x6761;&#x4EF6;&#x6CA1;&#x4EC0;&#x4E48;&#x5934;&#x7EEA;, &#x53EF;&#x4EE5;&#x5F00;&#x59CB;&#x5220;&#x4EE3;&#x7801; / bisection / &#x7B80;&#x5316;&#x65E0;&#x5173;&#x7684;&#x90E8;&#x5206;, &#x76F4;&#x5230; issue &#x6D88;&#x5931;</li></ol></li><li>&#x7528;&#x6237;&#x53D1;&#x8868;&#x524D;, &#x95EE;&#x95EE;&#x81EA;&#x5DF1;: &#x8FD9;&#x4E2A; example &#x91CC;&#x8FD8;&#x6709;&#x54EA;&#x91CC;&#x53EF;&#x4EE5;&#x5220;&#x6389;? <ul><li>&#x5269;&#x4E0B;&#x7684;&#x4EE3;&#x7801;&#x8D8A;&#x5C11;, &#x5BF9; maintainer &#x7684;&#x5E2E;&#x52A9;&#x5C31;&#x8D8A;&#x5927;</li><li>&#x5220;&#x6389;<em>&#x770B;&#x4F3C;</em>&#x65E0;&#x5173;&#x7684;&#x4EE3;&#x7801;&#x4E4B;&#x540E;, &#x52A1;&#x5FC5;&#x518D;&#x786E;&#x8BA4;&#x4E00;&#x4E0B; issue &#x4ECD;&#x7136;&#x53EF;&#x4EE5; reproduce</li><li>&#x4E0D;&#x8981;&#x7701;&#x7565;&#x6709;&#x7528;&#x7684;&#x4EE3;&#x7801;, &#x4F8B;&#x5982; python &#x91CC;&#x7684; import: maintainer &#x4E3A;&#x4E86;&#x590D;&#x73B0;&#x8FD8;&#x5F97;&#x624B;&#x52A8;&#x628A;&#x5B83;&#x4EEC;&#x52A0;&#x56DE;&#x6765;&#x5440;. &#x800C;&#x4E14; import &#x4E5F;&#x6709; side effect, &#x53EF;&#x80FD;&#x5BFC;&#x81F4; bug, &#x4F8B;&#x5982;<a href="https://github.com/gotcha/ipdb/issues/194" aria-label="`import ipdb` stops multiprocessing from working &#xB7; Issue #194 &#xB7; gotcha/ipdb" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x4E2A;</a></li></ul></li><li>&#x9879;&#x76EE;&#x672C;&#x8EAB;&#x7684;&#x826F;&#x597D;&#x8BBE;&#x8BA1;&#x4E5F;&#x80FD;&#x5E2E;&#x7528;&#x6237;&#x63D0;&#x4F9B; MRE.<ul><li>&#x5982;&#x679C; library &#x6709;&#x975E;&#x5E38;&#x6E05;&#x695A;&#x7684;&#x63A5;&#x53E3;, &#x6CA1;&#x6709;&#x4EC0;&#x4E48;&#x5185;&#x90E8;&#x72B6;&#x6001;, &#x90A3;&#x4E48;&#x7528;&#x6237;&#x53EA;&#x8981;&#x628A;&#x63D0;&#x4F9B;&#x7ED9; library &#x7684;&#x8F93;&#x5165;&#x8F93;&#x51FA;&#x8BB0;&#x4E0B;&#x6765;, &#x5C31;&#x80FD;&#x591F;&#x590D;&#x73B0;&#x95EE;&#x9898;</li><li>&#x53CD;&#x8FC7;&#x6765;, &#x5982;&#x679C;&#x9879;&#x76EE;&#x662F;&#x4E00;&#x4E2A; &quot;framework&quot;, &#x63D0;&#x4F9B;&#x4E86;&#x5F88;&#x591A;&#x590D;&#x6742;&#x7684; semantics, &#x5C31;&#x5F88;&#x96BE;&#x7B80;&#x5316; issue</li></ul></li></ul><h2 id="Environment-Information">Environment Information<a class="markdown-anchor" href="#Environment-Information">&#xB6;</a></h2><p>&#x7528;&#x6237;&#x5E94;&#x63D0;&#x4F9B; maintainer &#x8981;&#x6C42;&#x7684;&#x73AF;&#x5883;&#x4FE1;&#x606F; (&#x9879;&#x76EE;&#x7684; version, &#x4F9D;&#x8D56;&#x7684; version, &#x7CFB;&#x7EDF;&#x8F6F;&#x786C;&#x4EF6;&#x7B49;&#x7B49;). &#x5B83;&#x7684;&#x91CD;&#x8981;&#x6027;&#x5728;&#x4E8E;:</p><ul><li>&#x51B3;&#x5B9A;&#x4E86; expectation: &#x7A0B;&#x5E8F;&#x5728;&#x4E0D;&#x540C;&#x73AF;&#x5883;&#x4E0B;&#x7684; expected behavior &#x53EF;&#x80FD;&#x662F;&#x4E0D;&#x540C;&#x7684;</li><li>&#x6709;&#x52A9;&#x4E8E; reproducibility: issue &#x53EF;&#x80FD;&#x53EA;&#x5728;&#x7279;&#x5B9A;&#x73AF;&#x5883;&#x4E0B;&#x80FD;&#x591F; reproduce</li></ul><p>Maintainer &#x6700;&#x6E05;&#x695A;&#x54EA;&#x4E9B;&#x73AF;&#x5883;&#x4FE1;&#x606F;&#x662F;&#x9700;&#x8981;&#x7684;, &#x56E0;&#x6B64; maintainer &#x5E94;&#x5F53;&#x4EE5; issue template &#x7B49;&#x5F62;&#x5F0F;&#x544A;&#x77E5;&#x7528;&#x6237;&#x5982;&#x4F55;&#x63D0;&#x4F9B;&#x73AF;&#x5883;&#x4FE1;&#x606F;. &#x4F8B;&#x5982;, &#x5728; detectron2 &#x4E2D;&#x6211;&#x63D0;&#x4F9B;&#x4E86;&#x4E00;&#x4E2A;<a href="https://github.com/facebookresearch/detectron2/blob/f62b8a42e4d995966d0e7d7eb4049f023860b5ff/detectron2/utils/collect_env.py" aria-label="collect_env.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>collect_env.py</code></a> &#x811A;&#x672C;, &#x8FD0;&#x884C;&#x540E;&#x4F1A;&#x8F93;&#x51FA;&#x5982;&#x4E0B;&#x7684;&#x7ED3;&#x679C;,  &#x6BD4;&#x7528;&#x6237;&#x81EA;&#x5DF1;&#x80FD;&#x60F3;&#x5230;&#x7684;&#x4FE1;&#x606F;&#x8981;&#x8BE6;&#x7EC6;&#x5F97;&#x591A;.</p><figure class="highlight plaintext"><figcaption><span>python collect_env.py</span></figcaption><table><tr><td class="code"><pre><code class="hljs plaintext">----------------------  -----------------------------------------------------------<br>sys.platform            linux<br>Python                  3.10.1 (main, Dec 18 2021, 23:53:45) [GCC 11.1.0]<br>numpy                   1.21.5<br>detectron2              0.6 @/home/xxx/xxx/detectron2/detectron2<br>Compiler                GCC 11.1<br>CUDA compiler           CUDA 11.5<br>detectron2 arch flags   6.1<br>DETECTRON2_ENV_MODULE   &lt;not set&gt;<br>PyTorch                 1.10.1 @/usr/lib/python3.10/site-packages/torch<br>PyTorch debug build     False<br>GPU available           Yes<br>GPU 0                   NVIDIA GeForce GTX 1070 (arch=6.1)<br>Driver version          495.46<br>CUDA_HOME               /opt/cuda<br>Pillow                  8.4.0<br>torchvision             0.11.0a0+7947fc8 @/home/xxx/xxx/torchvision/torchvision<br>torchvision arch flags  6.1<br>fvcore                  0.1.5.post20211023<br>iopath                  0.1.9<br>cv2                     4.5.5<br>----------------------  -----------------------------------------------------------<br>PyTorch built with:<br>  - GCC 11.1<br>  - C++ Version: 201402<br>  - Intel(R) Math Kernel Library Version 2020.0.4 Product Build 20200917 for Intel(R) 64 architecture applications<br>  - Intel(R) MKL-DNN v2.2.3 (Git Hash 7336ca9f055cf1bfa13efb658fe15dc9b41f0740)<br>  - OpenMP 201511 (a.k.a. OpenMP 4.5)<br>  - LAPACK is enabled (usually provided by MKL)<br>  - NNPACK is enabled<br>  - CPU capability usage: AVX2<br>  - CUDA Runtime 11.5<br>  - NVCC architecture flags: -gencode;arch=compute_52,code=sm_52;-gencode;arch=compute_60,code=sm_60;-gencode;arch=compute_62,code=sm_62;-gencode;arch=compute_70,code=sm_70;-gencode;arch=compute_72,code=sm_72;-gencode;arch=compute_75,code=sm_75;-gencode;arch=compute_80,code=sm_80;-gencode;arch=compute_86,code=sm_86;-gencode;arch=compute_86,code=compute_86<br>  - CuDNN 8.3<br>  - Magma 2.6.1<br>  - Build settings: BLAS_INFO=mkl, BUILD_TYPE=Release, CUDA_VERSION=11.5, CUDNN_VERSION=8.3.0, CXX_COMPILER=/usr/bin/c++, CXX_FLAGS=-march=x86-64 -mtune=generic -O2 -pipe -fno-plt -fexceptions         -Wp,-D_FORTIFY_SOURCE=2 -Wformat -Werror=format-security         -fstack-clash-protection -fcf-protection -Wp,-D_GLIBCXX_ASSERTIONS -fvisibility-inlines-hidden -DUSE_PTHREADPOOL -fopenmp -DNDEBUG -DUSE_KINETO -DUSE_FBGEMM -DUSE_QNNPACK -DUSE_PYTORCH_QNNPACK -DUSE_XNNPACK -DSYMBOLICATE_MOBILE_DEBUG_HANDLE -DEDGE_PROFILER_USE_KINETO -O2 -fPIC -Wno-narrowing -Wall -Wextra -Werror=return-type -Wno-missing-field-initializers -Wno-type-limits -Wno-array-bounds -Wno-unknown-pragmas -Wno-sign-compare -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -Wno-unused-result -Wno-unused-local-typedefs -Wno-strict-overflow -Wno-strict-aliasing -Wno-error=deprecated-declarations -Wno-stringop-overflow -Wno-psabi -Wno-error=pedantic -Wno-error=redundant-decls -Wno-error=old-style-cast -fdiagnostics-color=always -faligned-new -Wno-unused-but-set-variable -Wno-maybe-uninitialized -fno-math-errno -fno-trapping-math -Werror=format -Werror=cast-function-type -Wno-stringop-overflow, LAPACK_INFO=mkl, PERF_WITH_AVX=1, PERF_WITH_AVX2=1, PERF_WITH_AVX512=1, TORCH_VERSION=1.10.1, USE_CUDA=1, USE_CUDNN=1, USE_EXCEPTION_PTR=1, USE_GFLAGS=ON, USE_GLOG=ON, USE_MKL=ON, USE_MKLDNN=ON, USE_MPI=OFF, USE_NCCL=ON, USE_NNPACK=ON, USE_OPENMP=ON,<br></code></pre></td></tr></table></figure><p>Maintainer &#x5B9E;&#x73B0;&#x8FD9;&#x6837;&#x7684;&#x811A;&#x672C;&#x65F6;, &#x9700;&#x8981;&#x6CE8;&#x610F;:</p><ul><li>&#x6700;&#x597D;&#x5141;&#x8BB8;&#x5B83;&#x53EF;&#x4EE5;&#x72EC;&#x7ACB;&#x6267;&#x884C;, &#x4E0D;&#x4F9D;&#x8D56;&#x9879;&#x76EE;&#x662F;&#x5426;&#x6210;&#x529F;&#x5B89;&#x88C5;</li><li>Python &#x7684;&#x5305;&#x7BA1;&#x7406;&#x5341;&#x5206;&#x6DF7;&#x4E71;, &#x5E94;&#x4E86;&#x89E3;&#x6211;&#x7684;<a href="/blog/2019/On-Environment-Packaging-in-Python/" aria-label="On Environment/Package Management in Python" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x7BC7;&#x6587;&#x7AE0;</a>&#x91CC;&#x7684;&#x6CE8;&#x610F;&#x4E8B;&#x9879;. &#x4F8B;&#x5982;, PyTorch &#x7684;<a href="https://github.com/pytorch/pytorch/blob/60f131fb6c2e3f4a23e64096a3e718a1e669215b/torch/utils/collect_env.py" aria-label="collect_env.py &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>collect_env.py</code></a> &#x91CC;&#x4F7F;&#x7528;<code>{conda,pip} list</code> &#x5C31;&#x662F;&#x4E0D;&#x79D1;&#x5B66;&#x7684;&#x505A;&#x6CD5;.</li><li>&#x591A;&#x591A;&#x6355;&#x83B7;&#x5F02;&#x5E38;: &#x7528;&#x6237;&#x7684;&#x73AF;&#x5883;&#x91CC;&#x53EF;&#x80FD;&#x6709;&#x5404;&#x79CD;&#x9519;&#x8BEF;, &#x4E0D;&#x8981;&#x5047;&#x8BBE;&#x6240;&#x6709;&#x4FE1;&#x606F;&#x90FD;&#x80FD;&#x88AB; collect &#x5230;.</li></ul><p>&#x6709;&#x65F6;&#x5019;, &#x7528;&#x6237;&#x4EC5;&#x4EC5;&#x63D0;&#x4F9B;&#x81EA;&#x5DF1;&#x7684;&#x73AF;&#x5883;&#x4FE1;&#x606F;&#x8FD8;&#x4E0D;&#x8DB3;&#x4EE5;&#x590D;&#x73B0;&#x95EE;&#x9898;, &#x56E0;&#x4E3A;&#x96BE;&#x4EE5;&#x786E;&#x5B9A;&#x662F;&#x73AF;&#x5883;&#x4E2D;&#x7684;&#x54EA;&#x4E2A;&#x56E0;&#x7D20;&#x5BFC;&#x81F4;&#x4E86; issue. &#x4E3A;&#x4E86;&#x4FDD;&#x8BC1; issue &#x7684; reproducibility, &#x53EF;&#x4EE5;&#x8003;&#x8651;&#x4F7F;&#x7528; docker &#x6216; Colab notebook &#x63D0;&#x4F9B;&#x66F4;&#x5B8C;&#x6574;&#x7684;&#x73AF;&#x5883;. &#x8FD9;&#x79CD;&#x60C5;&#x51B5;&#x5E76;&#x4E0D;&#x5C11;&#x89C1;: &#x6211;&#x5728; PyTorch &#x91CC;&#x6709;<a href="https://github.com/pytorch/pytorch/issues?q=is:issue%20author:ppwwyyxx%20docker%20" aria-label="Issues &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> 4 &#x4E2A; bug report</a> &#x662F;&#x81EA;&#x5E26; docker &#x6765; reproduce &#x7684;.Maintainer &#x4E5F;&#x5E94;&#x63D0;&#x4F9B;&#x5B98;&#x65B9;&#x7684; docker/Colab, &#x65B9;&#x4FBF;&#x7528;&#x6237;&#x5728;&#x62A5; issue &#x65F6;&#x6392;&#x9664;&#x73AF;&#x5883;&#x95EE;&#x9898;: &#x7528;&#x6237;&#x53EF;&#x4EE5;&#x628A;&#x81EA;&#x5DF1;&#x7684; MRE &#x5728;&#x5B98;&#x65B9;&#x7684;&#x73AF;&#x5883;&#x4E2D;&#x6D4B;&#x8BD5;.</p><h2 id="Summary">Summary<a class="markdown-anchor" href="#Summary">&#xB6;</a></h2><p>&#x8FD9;&#x7BC7;&#x6587;&#x7AE0;&#x66F4;&#x591A;&#x4ECE;&#x7528;&#x6237;&#x7684;&#x89D2;&#x5EA6;&#x8BF4;&#x4E86;&#x5982;&#x4F55;&#x62A5;&#x544A; unexpected issues. &#x7528;&#x6237;&#x6700;&#x597D;&#x5E94;&#x63D0;&#x4F9B;:</p><ol><li>Expectation</li><li>Unexpected observations / Full observations</li><li>Environment</li><li>Minimal reproducible example</li></ol><p>&#x5728; maintainer &#x7ED9;&#x4E88;&#x4E86;&#x8DB3;&#x591F;&#x7684;&#x5F15;&#x5BFC;&#x7684;&#x60C5;&#x51B5;&#x4E0B;, 1-3 &#x7684;&#x4EE3;&#x4EF7;&#x90FD;&#x5F88;&#x5C0F;, &#x7528;&#x6237;&#x5E94;&#x5C3D;&#x53EF;&#x80FD;&#x63D0;&#x4F9B;.4 &#x6709;&#x65F6;&#x4F1A;&#x6709;&#x4E00;&#x5B9A;&#x96BE;&#x5EA6;, &#x6587;&#x4E2D;&#x5DF2;&#x4ECB;&#x7ECD;.</p><p>&#x5728;<a href="/blog/2022/Github-Communications-1/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(1)" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x7B2C;&#x4E00;&#x7BC7;&#x6587;&#x7AE0;&#x4E2D;&#x8BF4;&#x5230;</a>, maintainer &#x81EA;&#x5DF1;&#x51B3;&#x5B9A;&#x81EA;&#x5DF1;&#x7684;&#x4E49;&#x52A1; / commitment &#x6709;&#x54EA;&#x4E9B;,  &#x90A3;&#x4E48;&#x4E5F;&#x5C31;&#x53EF;&#x4EE5;&#x8981;&#x6C42; unexpected issue &#x5FC5;&#x987B;&#x5305;&#x542B;&#x7279;&#x5B9A;&#x4FE1;&#x606F;, &#x5E76;&#x51B3;&#x5B9A;&#x5BF9;&#x4E8E;&#x7F3A;&#x5C11;&#x4FE1;&#x606F;&#x7684; issue &#x4E0D;&#x4E88;&#x5904;&#x7406;. &#x4E00;&#x4E2A;&#x5F88;&#x6709;&#x8DA3;&#x7684;&#x6781;&#x7AEF;&#x4F8B;&#x5B50;&#x662F;, <code>you-get</code> &#x9879;&#x76EE;&#x76F4;&#x63A5;&#x7981;&#x7528;&#x4E86; issue &#x529F;&#x80FD;,  &#x8981;&#x6C42;&#x6240;&#x6709;&#x7684; bug report &#x5FC5;&#x987B;&#x4EE5; &quot;&#x5931;&#x8D25;&#x7684;&#x5355;&#x5143;&#x6D4B;&#x8BD5;&quot; &#x7684;<a href="https://github.com/soimort/you-get/blob/develop/CONTRIBUTING.md" aria-label="CONTRIBUTING.md at develop &#xB7; soimort/you-get" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> PR &#x5F62;&#x5F0F;&#x62A5;&#x544A;</a>, &#x76F4;&#x63A5;&#x6EE1;&#x8DB3;&#x4E86;&#x4EE5;&#x4E0A;&#x56DB;&#x70B9;. &#x5BF9;&#x4E8E;&#x8FD9;&#x79CD;&#x63A5;&#x53E3;&#x7B80;&#x5355;&#x7684;&#x5DE5;&#x5177;&#x6765;&#x8BF4;, &#x4E0D;&#x5931;&#x4E3A;&#x4E00;&#x4E2A;&#x597D;&#x529E;&#x6CD5;.</p><p>&#x5927;&#x591A;&#x6570;&#x5177;&#x5907;&#x89C4;&#x6A21;&#x7684;&#x9879;&#x76EE;&#x4F1A;&#x901A;&#x8FC7; issue &#x7C7B;&#x522B;&#x548C; issue template &#x8868;&#x660E;&#x4EC0;&#x4E48;&#x6837;&#x7684; issue &#x662F; maintainer &#x613F;&#x610F;&#x652F;&#x6301;&#x7684;. &#x4E3A;&#x4E86;&#x9AD8;&#x6548;&#x7BA1;&#x7406;, &#x5F80;&#x5F80;&#x90FD;&#x4F1A;&#x5BF9;&#x7528;&#x6237;&#x63D0;&#x4F9B;&#x7684;&#x4FE1;&#x606F;&#x6709;&#x786C;&#x6027;&#x8981;&#x6C42;. &#x5982;&#x679C;&#x9879;&#x76EE;&#x6709; issue template, &#x800C;&#x4F60;&#x53C8;&#x6CA1;&#x6709;&#x81EA;&#x4FE1;&#x5230;&#x89C9;&#x5F97;&#x81EA;&#x5DF1;&#x63D0;&#x4F9B;&#x7684;&#x4FE1;&#x606F;&#x6BD4; template &#x66F4;&#x597D;,  &#x90A3;&#x4E48;&#x8BF7;&#x52A1;&#x5FC5; follow issue template -- &#x8981;&#x83B7;&#x5F97; maintainer &#x7684;&#x5E2E;&#x52A9;, &#x5E94;&#x8BE5;&#x9996;&#x5148;&#x5C0A;&#x91CD; maintainer &#x7684;&#x8981;&#x6C42;, &#x63D0;&#x4F9B;&#x5FC5;&#x8981;&#x7684;&#x4FE1;&#x606F;. <a href="/blog/2022/Github-Communications-3/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(3): &#x5982;&#x4F55;&#x7BA1;&#x7406;issue" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x4E0B;&#x4E00;&#x7BC7;&#x6587;&#x7AE0;</a>&#x4F1A;&#x66F4;&#x8BE6;&#x7EC6;&#x7684;&#x8BF4; maintainer &#x7684;&#x7BA1;&#x7406;&#x65B9;&#x5F0F;.</p>]]></content>
    
    
    <summary type="html">
&lt;p&gt;&amp;#x62A5;&amp;#x544A;&amp;#x9519;&amp;#x8BEF; / &amp;#x62A5; bug &amp;#x662F;&amp;#x7528;&amp;#x6237;&amp;#x4E0E;&amp;#x5F00;&amp;#x53D1;&amp;#x8005;&amp;#x95F4;&amp;#x6700;&amp;#x5E38;&amp;#x89C1;&amp;#x7684;&amp;#x4E00;&amp;#x7C7B;&amp;#x4EA4;&amp;#x6D41;, &amp;#x4E5F;&amp;#x662F;&amp;#x5E38;&amp;#x89C1;&amp;#x7684; github issue.
 &amp;#x4F46;&amp;#x662F;&amp;#x5F88;&amp;#x591A;&amp;#x7528;&amp;#x6237;&amp;#x5E76;&amp;#x4E0D;&amp;#x4F1A;&amp;#x79D1;&amp;#x5B66;&amp;#x7684;&amp;#x62A5; bug, maintainer &amp;#x5BF9;&amp;#x6B64;&amp;#x4E5F;&amp;#x7F3A;&amp;#x4E4F;&amp;#x5F15;&amp;#x5BFC;.
 &amp;#x56E0;&amp;#x6B64;&amp;#x8FD9;&amp;#x7BC7;&amp;#x6587;&amp;#x7AE0;&amp;#x8BA8;&amp;#x8BBA;&amp;#x5982;&amp;#x4F55;&amp;#x79D1;&amp;#x5B66;&amp;#x7684;&amp;#x62A5; bug.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Open Source" scheme="https://ppwwyyxx.com/blog/tags/Open-Source/"/>
    
  </entry>
  
  <entry>
    <title>谈谈Github上如何交流(1)</title>
    <link href="https://ppwwyyxx.com/blog/2022/Github-Communications-1/"/>
    <id>https://ppwwyyxx.com/blog/2022/Github-Communications-1/</id>
    <published>2022-05-02T07:00:00.000Z</published>
    <updated>2022-05-02T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<div class="message is-info my-custom-sidebar"><div class="message-header">     <i class="fas fa-info-circle mr-2"></i><p>&#x672C;&#x7CFB;&#x5217;&#x6587;&#x7AE0;</p></div><div class="message-body"><ol><li><a href="/blog/2022/Github-Communications-1/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(1)" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8C08;&#x8C08; Github &#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41; (1)</a></li><li><a href="/blog/2022/Github-Communications-2/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(2): &#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5;bug" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5; bug</a></li><li><a href="/blog/2022/Github-Communications-3/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(3): &#x5982;&#x4F55;&#x7BA1;&#x7406;issue" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x5982;&#x4F55;&#x7BA1;&#x7406; issue</a></li><li><a href="/blog/2022/Github-Communications-4/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(4): {Feature,Pull} Request" class="hint--top hint--rounded hint--no-animate hint--no-arrow">{Feature,Pull} Request</a></li></ol></div></div><p>&#x76F8;&#x6BD4;&#x4F20;&#x7EDF;&#x7684;&#x90AE;&#x4EF6;&#x5217;&#x8868; / bugzilla/sourceforge &#x7B49;&#x5F00;&#x6E90;&#x5E73;&#x53F0;, github &#x628A;&#x5F00;&#x6E90;&#x793E;&#x533A;&#x4EA4;&#x6D41;&#x7684;&#x6210;&#x672C; / &#x95E8;&#x69DB;&#x964D;&#x7684;&#x5F88;&#x4F4E;, &#x56E0;&#x6B64;&#x4EA4;&#x6D41;&#x7684;&#x8D28;&#x91CF;&#x4E5F;&#x5E38;&#x5E38;&#x968F;&#x4E4B;&#x4E0B;&#x964D;.</p><p>&#x6211;&#x8BA1;&#x5212;&#x5199;&#x51E0;&#x7BC7;&#x6587;&#x7AE0;, &#x4ECE; <strong>&#x7528;&#x6237; (User)</strong> &#x548C; <strong>&#x7EF4;&#x62A4;&#x8005; (Maintainer)</strong> &#x4E24;&#x8005;&#x7684;&#x89D2;&#x5EA6;&#x5199;&#x5199;&#x5F00;&#x6E90;&#x793E;&#x533A;&#x4E2D;&#x5982;&#x4F55;&#x4F7F;&#x7528; issue/PR &#x8FDB;&#x884C;&#x6C9F;&#x901A;, &#x5E0C;&#x671B;&#x80FD;&#x591F;:</p><ul><li>&#x8BA9;&#x666E;&#x901A;&#x7528;&#x6237;&#x5B66;&#x4F1A;&#x63D0;&#x95EE;, &#x5B66;&#x4F1A;&#x4EE5; maintainer &#x66F4;&#x5BB9;&#x6613;&#x63A5;&#x53D7;&#x7684;&#x65B9;&#x5F0F;&#x4F7F;&#x7528; issue/PR &#x505A;&#x8D21;&#x732E;.</li><li>&#x7ED9; maintainer &#x63D0;&#x4F9B;&#x4E00;&#x4E9B;&#x7BA1;&#x7406; issue/PR &#x7684;&#x7ECF;&#x9A8C;, &#x4E0D;&#x8981;&#x518D;&#x4E3A; issue &#x5934;&#x75BC;.</li></ul><span id="more"></span><p>&#x4F5C;&#x4E3A;&#x4E3B;&#x8981;&#x5F00;&#x53D1;&#x8005;&#x548C;&#x7EF4;&#x62A4;&#x8005;,  &#x6211;&#x66FE;&#x7ECF;&#x7BA1;&#x7406;&#x8FC7; <a href="https://github.com/facebookresearch/detectron2/" aria-label="facebookresearch/detectron2: Detectron2 is a platform for object detection, segmentation and other visual recognition tasks." class="hint--top hint--rounded hint--no-animate hint--no-arrow">detectron2</a> &#x548C;<a href="https://github.com/tensorpack/tensorpack/" aria-label="tensorpack/tensorpack: A Neural Net Training Interface on TensorFlow, with focus on speed + flexibility" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> tensorpack</a> &#x7B49;&#x9879;&#x76EE;.2016-2021 &#x5E74;&#x91CC;&#x6211;&#x4E00;&#x4E2A;&#x4EBA;&#x5904;&#x7406;&#x8FC7;&#x8FD9;&#x4E24;&#x4E2A;&#x9879;&#x76EE;&#x91CC;&#x7EA6; <strong>5000 &#x4E2A; issue/PR</strong>&#xFF0C;&#x4F5C;&#x4E3A;&#x7528;&#x6237;, &#x6211;&#x4E5F;&#x53C2;&#x4E0E;&#x4E86; PyTorch / TensorFlow &#x7B49;&#x4E0D;&#x5C11;&#x9879;&#x76EE;&#x7684;&#x793E;&#x533A;&#x8BA8;&#x8BBA;. &#x5728;&#x8FD9;&#x4E2A;&#x8FC7;&#x7A0B;&#x4E2D;,  &#x770B;&#x5230;&#x4E86;&#x5F00;&#x6E90;&#x9879;&#x76EE;&#x4E2D;&#x5404;&#x79CD;&#x4E0D;&#x540C;&#x7684;&#x6C9F;&#x901A;, &#x7BA1;&#x7406;&#x65B9;&#x5F0F;. &#x73B0;&#x5728;&#x6211;&#x5DF2;&#x7ECF;&#x57FA;&#x672C;&#x79BB;&#x5F00;&#x4E86;&#x8FD9;&#x4E9B;&#x9879;&#x76EE;, &#x4E8E;&#x662F;&#x60F3;&#x628A;&#x8FD9;&#x4E9B;&#x7ECF;&#x9A8C;&#x603B;&#x7ED3;&#x4E00;&#x4E0B;.</p><p>&#x8FD9;&#x7BC7;&#x6587;&#x7AE0;&#x4F5C;&#x4E3A;&#x7B2C;&#x4E00;&#x7BC7;, &#x53EA;&#x8BA8;&#x8BBA;&#x4E00;&#x4E9B;&#x57FA;&#x672C;&#x7684;&#x539F;&#x5219;.</p><h2 id="&#x7406;&#x89E3;&#x7528;&#x6237;&#x548C;&#x7EF4;&#x62A4;&#x8005;&#x7684;-Common-Interest">&#x7406;&#x89E3;&#x7528;&#x6237;&#x548C;&#x7EF4;&#x62A4;&#x8005;&#x7684; Common Interest<a class="markdown-anchor" href="#&#x7406;&#x89E3;&#x7528;&#x6237;&#x548C;&#x7EF4;&#x62A4;&#x8005;&#x7684;-Common-Interest">&#xB6;</a></h2><p>&#x5728;&#x4E00;&#x4E2A;&#x9879;&#x76EE;&#x4E2D;, maintainer &#x548C;&#x7528;&#x6237;&#x7684;&#x76EE;&#x7684;&#x5E38;&#x5E38;&#x5E76;&#x4E0D;&#x662F;&#x5B8C;&#x5168;&#x4E00;&#x81F4;&#x7684;. &#x6709;&#x6548;&#x4EA4;&#x6D41;&#x7684;&#x57FA;&#x7840;, &#x662F;&#x8981;&#x7406;&#x89E3;&#x5BF9;&#x65B9;&#x4E0E;&#x81EA;&#x5DF1; Priority &#x4E0A;&#x7684;&#x76F8;&#x540C;&#x548C;&#x4E0D;&#x540C;.</p><p>&#x5927;&#x591A;&#x6570;&#x5F00;&#x6E90;&#x9879;&#x76EE;&#x7684;&#x8D44;&#x6E90;&#x90FD;&#x5F88;&#x6709;&#x9650;, &#x7528;&#x7231;&#x53D1;&#x7535;. &#x56E0;&#x6B64;, <strong>maintainer &#x81EA;&#x5DF1;&#x51B3;&#x5B9A;&#x81EA;&#x5DF1;&#x7684;&#x4E49;&#x52A1;</strong> &#x6709;&#x54EA;&#x4E9B;.</p><p>&#x901A;&#x5E38;, maintainer &#x4E0D;&#x4EE5;&#x6EE1;&#x8DB3;&#x67D0;&#x4E2A;&#x7528;&#x6237;&#x4E3A;&#x76EE;&#x6807;, <strong>&#x4E0D;&#x5F53; &quot;&#x5BA2;&#x670D;&quot;</strong>. &#x8FD9;&#x662F;&#x56E0;&#x4E3A;, &#x76F8;&#x6BD4;&#x4E8E;&#x5176;&#x4ED6;&#x53EF;&#x4EE5;&#x505A;&#x7684;&#x4E8B;&#x60C5;&#x800C;&#x8A00;, &#x7ED9;&#x7F51;&#x4E0A;&#x7684;&#x8DEF;&#x4EBA;&#x63D0;&#x4F9B;&#x4E2A;&#x4EBA;&#x5316;&#x7684; support &#x5BF9;&#x4E00;&#x4E2A;&#x9879;&#x76EE;&#x80FD;&#x591F;&#x5E26;&#x6765;&#x7684;&#x8D21;&#x732E;&#x662F;&#x975E;&#x5E38;&#x975E;&#x5E38;&#x5C0F;&#x7684;. &#x76F8;&#x53CD;, maintainer &#x901A;&#x8FC7;&#x505A;&#x5176;&#x4ED6;&#x4E8B;&#x60C5; (&#x4F8B;&#x5982;&#x4FEE; bug) &#x8BA9;&#x9879;&#x76EE;&#x53D1;&#x5C55;&#x5F97;&#x66F4;&#x597D;, &#x6765; <strong>&#x95F4;&#x63A5;</strong> &#x7684;&#x5E2E;&#x52A9;&#x6240;&#x6709;&#x7528;&#x6237;. &#x901A;&#x5E38;&#x6765;&#x8BF4;, maintainer &#x7684; priority &#x662F;&#x56F4;&#x7ED5; <strong>&#x9879;&#x76EE;</strong> &#x4E3A;&#x4E2D;&#x5FC3;, &#x800C;&#x4E0D;&#x662F;&#x7279;&#x5B9A;&#x7528;&#x6237;.</p><p>&#x4F46;&#x662F;, &#x7528;&#x6237;&#x7684;&#x8BC9;&#x6C42;&#x5F88;&#x591A;&#x65F6;&#x5019;&#x5C31;&#x662F;&#x8981;&#x89E3;&#x51B3;&#x81EA;&#x5DF1;&#x7684;&#x95EE;&#x9898;. &#x8FD9;&#x65F6;&#x5019;&#x7528;&#x6237;&#x4E00;&#x5B9A;&#x8981;&#x8BA4;&#x8BC6;&#x5230;: maintainer &#x5BF9;&#x89E3;&#x51B3;&#x4F60;&#x7684;&#x95EE;&#x9898;&#x5E76;&#x4E0D;&#x4E00;&#x5B9A;&#x6709;&#x5174;&#x8DA3;.maintainer &#x613F;&#x610F;&#x4E0E;&#x7528;&#x6237;&#x4EA4;&#x6D41;, &#x672C;&#x8D28;&#x662F;&#x56E0;&#x4E3A;&#x7528;&#x6237;&#x7684; feedbacks &#x53EF;&#x80FD;&#x8BA9;&#x9879;&#x76EE;&#x53D8;&#x5F97;&#x66F4;&#x597D;.</p><p><strong>&#x8BA9;&#x9879;&#x76EE;&#x53D8;&#x5F97;&#x66F4;&#x597D;</strong> &#x662F;&#x7528;&#x6237;&#x4E0E; maintainer &#x7684; common interest, &#x57FA;&#x4E8E;&#x8FD9;&#x4E00;&#x70B9;&#x7684;&#x4EA4;&#x6D41;&#x624D;&#x662F;&#x6700;&#x6709;&#x6548;&#x7684;, &#x4E8C;&#x8005;&#x624D;&#x80FD;&#x6709;&#x6548;&#x5408;&#x4F5C;.</p><h2 id="&#x6709;&#x6548;&#x7684;&#x4EA4;&#x6D41;&#x57FA;&#x4E8E;-Contribution">&#x6709;&#x6548;&#x7684;&#x4EA4;&#x6D41;&#x57FA;&#x4E8E; &quot;Contribution&quot;<a class="markdown-anchor" href="#&#x6709;&#x6548;&#x7684;&#x4EA4;&#x6D41;&#x57FA;&#x4E8E;-Contribution">&#xB6;</a></h2><p>&#x8BA9;&#x9879;&#x76EE;&#x53D8;&#x5F97;&#x66F4;&#x597D;, &#x6362;&#x53E5;&#x8BDD;&#x8BF4;&#x5C31;&#x662F; &quot;make contribution to the project&quot;.</p><p>&quot;Contribution&quot; &#x8FD9;&#x4E2A;&#x8BCD;&#x5728; github &#x4E0A;&#x4E3B;&#x8981;&#x51FA;&#x73B0;&#x4E8E; &quot;contribution calendar&quot;, &#x8FD9;&#x662F;&#x4E00;&#x4E2A;&#x8BB0;&#x5F55;&#x7528;&#x6237;&#x6BCF;&#x5929;&#x7684; &quot;contribution activity&quot; &#x7684;&#x65E5;&#x5386;:</p><img src="/blog/2022/Github-Communications-1/calendar.jpg" class="center" title="My contribution calendar in 2020"><p>&#x5728; contribution calendar &#x4E0A;, &#x4E0D;&#x4EC5;&#x4E0E;&#x4EE3;&#x7801;&#x76F8;&#x5173;&#x7684;&#x884C;&#x4E3A; (commits, PR, reviews) &#x7B97;&#x4F5C; &quot;contributions&quot;, &#x6709;&#x4E9B;&#x5947;&#x602A;&#x7684;&#x662F;, &#x521B;&#x5EFA; issue &#x4E5F;&#x7B97;&#x4F5C; &quot;contributions&quot;. &#x8FD9;&#x53EF;&#x80FD;&#x6B63;&#x662F;&#x56E0;&#x4E3A;, &#x5728; github &#x8BBE;&#x8BA1;&#x8005;&#x7684;&#x773C;&#x91CC;, issue &#x7406;&#x5E94;&#x662F;&#x4E3A;&#x4E86;&#x8BA9;&#x9879;&#x76EE;&#x53D8;&#x5F97;&#x66F4;&#x597D;, &#x800C;&#x4E0D;&#x4EC5;&#x662F;&#x89E3;&#x51B3;&#x81EA;&#x5DF1;&#x7684;&#x9700;&#x6C42;.</p><p>&#x7528;&#x6237;&#x5982;&#x679C;&#x80FD;&#x591F;&#x7406;&#x89E3;&#x8FD9;&#x4E00;&#x70B9;, &#x5C06;&#x81EA;&#x5DF1;&#x7684;&#x4E2A;&#x4EBA;&#x9700;&#x6C42;&#x8F6C;&#x5316;&#x4E3A;&#x5BF9;&#x9879;&#x76EE;&#x7684; contribution, &#x624D;&#x80FD;&#x628A;&#x4EA4;&#x6D41;&#x53D8;&#x5F97;&#x66F4;&#x6709;&#x6548;. &#x6982;&#x62EC;&#x6765;&#x8BF4;&#x7684;&#x8BDD;:</p><ul><li>&#x5F53;&#x7528;&#x6237;&#x62A5;&#x544A;&#x4E00;&#x4E2A;&#x95EE;&#x9898; (problem, &#x4E0D;&#x662F; question) &#x7684;&#x65F6;&#x5019;, &#x8981;&#x80FD;&#x591F;&#x5C06;&#x5B83;&#x63CF;&#x8FF0;&#x6210; <strong>&#x8FD9;&#x4E2A;&#x9879;&#x76EE;&#x7684;&#x95EE;&#x9898;</strong>, &#x800C;&#x4E0D;&#x4EC5;&#x662F; &quot;&#x67D0;&#x4E2A;&#x7528;&#x6237;&#x7684;&#x95EE;&#x9898;&quot;.</li><li>&#x5F53;&#x7528;&#x6237; request feature &#x7684;&#x65F6;&#x5019;, &#x8981; justify &#x8FD9;&#x4E2A; feature &#x5BF9;&#x9879;&#x76EE;&#x7684;&#x4EF7;&#x503C;, &#x800C;&#x4E0D;&#x4EC5;&#x662F;&#x5BF9;&#x81EA;&#x5DF1;&#x7684;&#x4EF7;&#x503C;.</li><li>&#x5F53;&#x7528;&#x6237;&#x63D0;&#x4EA4; patch &#x7684;&#x65F6;&#x5019;, &#x8981; justify &#x8FD9;&#x4E2A; patch &#x5BF9;&#x9879;&#x76EE;&#x7684;&#x957F;&#x671F;&#x4EF7;&#x503C; (&#x8BBE;&#x8BA1;&#x5408;&#x7406;, &#x4EE3;&#x7801;&#x7F8E;&#x89C2;, &#x53EF;&#x7EF4;&#x62A4;&#x6027;, &#x5355;&#x5143;&#x6D4B;&#x8BD5;&#x7B49;&#x7B49;), &#x800C;&#x4E0D;&#x4EC5;&#x4EC5;&#x662F;&#x5B83;&#x662F;&#x5426;&#x89E3;&#x51B3;&#x4E86;&#x81EA;&#x5DF1;&#x7684;&#x77ED;&#x671F;&#x9700;&#x6C42;.</li></ul><p>&#x4EE5;&#x4E0A;&#x4E09;&#x70B9;&#x5728;&#x5B9E;&#x8DF5;&#x4E2D;&#x610F;&#x5473;&#x7740;&#x4EC0;&#x4E48;, &#x5E94;&#x8BE5;&#x600E;&#x4E48;&#x505A;, &#x4F1A;&#x5728;&#x540E;&#x9762;&#x51E0;&#x7BC7;&#x4E2D;&#x518D;&#x8BF4;&#x660E;.</p><h2 id="&#x5F00;&#x6E90;&#x793E;&#x533A;-vs-&#x516C;&#x53F8;&#x5185;&#x7684;&#x4EA4;&#x6D41;">&#x5F00;&#x6E90;&#x793E;&#x533A; vs. &#x516C;&#x53F8;&#x5185;&#x7684;&#x4EA4;&#x6D41;<a class="markdown-anchor" href="#&#x5F00;&#x6E90;&#x793E;&#x533A;-vs-&#x516C;&#x53F8;&#x5185;&#x7684;&#x4EA4;&#x6D41;"> &#xB6;</a></h2><p>&#x5F00;&#x6E90;&#x793E;&#x533A;&#x7684;&#x4EA4;&#x6D41;&#x548C;&#x516C;&#x53F8;&#x540C;&#x4E8B;&#x95F4;&#x7684;&#x5F00;&#x53D1;&#x4EA4;&#x6D41;&#x5728;&#x5F88;&#x591A;&#x65B9;&#x9762;&#x662F;&#x76F8;&#x4F3C;&#x7684;,  &#x5F00;&#x6E90;&#x793E;&#x533A;&#x4E2D; Maintainer/User &#x7684;&#x5173;&#x7CFB;, &#x4E5F;&#x4E0E;&#x516C;&#x53F8;&#x5185;&#x90E8; Code Owner/User &#x7684;&#x5173;&#x7CFB;&#x7C7B;&#x4F3C;. &#x4F46;&#x662F;, &#x5F00;&#x6E90;&#x793E;&#x533A;&#x91CC;&#x7684;&#x6C9F;&#x901A;&#x96BE;&#x5EA6;&#x4E00;&#x822C;&#x4F1A;&#x66F4;&#x5927;:</p><ul><li>&#x4EA4;&#x6D41;&#x6548;&#x7387;: &#x5F00;&#x6E90;&#x793E;&#x533A;&#x7684;&#x4EA4;&#x6D41;&#x4EE5;&#x539F;&#x59CB;&#x7684; email/forum &#x5F62;&#x5F0F;&#x4E3A;&#x4E3B;, &#x800C;&#x4E0D;&#x662F;&#x5373;&#x65F6;&#x6D88;&#x606F;&#x548C;&#x89C6;&#x9891;&#x4F1A;&#x8BAE;. &#x7531;&#x4E8E;&#x5EF6;&#x8FDF;&#x66F4;&#x9AD8;, &#x5C31;&#x8981;&#x5728;&#x6BCF;&#x6761;&#x6D88;&#x606F;&#x4E2D;&#x4F20;&#x8FBE;&#x5C3D;&#x91CF;&#x591A;&#x7684;&#x6709;&#x7528;&#x4FE1;&#x606F;.</li><li>&#x7F3A;&#x5C11;&#x4FE1;&#x4EFB;: &#x7528;&#x6237;&#x4E0D;&#x4E00;&#x5B9A;&#x4E86;&#x89E3;&#x7EF4;&#x62A4;&#x8005;, &#x7EF4;&#x62A4;&#x8005;&#x901A;&#x5E38;&#x4E0D;&#x4E86;&#x89E3;&#x7528;&#x6237;, &#x4E92;&#x76F8;&#x90FD;&#x6CA1;&#x6709;&#x4EC0;&#x4E48; obligations. &#x7F3A;&#x5C11;&#x4FE1;&#x4EFB;&#x5C31;&#x4E0D;&#x613F;&#x610F;&#x6295;&#x5165;&#x65F6;&#x95F4;&#x5408;&#x4F5C;.</li><li>&#x6CA1;&#x6709;&#x5171;&#x540C;&#x7684; context: &#x516C;&#x53F8;&#x540C;&#x4E8B;&#x95F4;, &#x5927;&#x5BB6;&#x7528;&#x4E00;&#x6837;&#x7684; terminology, &#x4E00;&#x6837;&#x7684;&#x5F00;&#x53D1;&#x73AF;&#x5883;&#x7B49;&#x7B49;, &#x514D;&#x53BB;&#x4E86;&#x5F88;&#x591A;&#x4EA4;&#x6D41;&#x969C;&#x788D;.</li><li>Common Interest: &#x516C;&#x53F8;&#x5185;&#x90E8;&#x6709;&#x66F4;&#x591A;&#x7684; Common Interest, &quot;&#x7528;&#x6237;&#x7684;&#x95EE;&#x9898;&quot; &#x548C; &quot;&#x9879;&#x76EE;&#x7684;&#x95EE;&#x9898;&quot; &#x6700;&#x7EC8;&#x90FD;&#x662F; &quot;&#x516C;&#x53F8;&#x7684;&#x95EE;&#x9898;&quot;. &#x56E0;&#x6B64;, &#x5F00;&#x53D1;&#x4E00;&#x4E2A;&#x9879;&#x76EE;&#x7684; Code Owner &#x5E38;&#x5E38;&#x662F;&#x6709;&#x660E;&#x786E;&#x7684;&#x4E49;&#x52A1;&#x53BB;&#x89E3;&#x51B3;&#x516C;&#x53F8;&#x5185;&#x7528;&#x6237;&#x7684;&#x95EE;&#x9898;&#x7684;. &#x7136;&#x800C;, &#x5982;&#x4E0A;&#x6240;&#x8BF4;, &#x5728;&#x5F00;&#x6E90;&#x793E;&#x533A;&#x4E2D;&#x4E0D;&#x662F;&#x8FD9;&#x6837;.</li></ul><p>&#x56E0;&#x6B64;, &#x5F00;&#x6E90;&#x793E;&#x533A;&#x4E2D;&#x7684;&#x4EA4;&#x6D41;&#x65B9;&#x5F0F;, &#x5BF9;&#x516C;&#x53F8;&#x5185;&#x7684;&#x4EA4;&#x6D41;&#x6709;&#x53C2;&#x8003;&#x610F;&#x4E49;, &#x4F46;&#x4E0D;&#x4E00;&#x5B9A;&#x5B8C;&#x5168;&#x9002;&#x7528;. &#x4F8B;&#x5982;<a href="/blog/2022/Github-Communications-2/" aria-label="&#x8C08;&#x8C08;Github&#x4E0A;&#x5982;&#x4F55;&#x4EA4;&#x6D41;(2): &#x5982;&#x4F55;&#x79D1;&#x5B66;&#x7684;&#x62A5;bug" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x4E0B;&#x4E00;&#x7BC7;&quot;&#x5982;&#x4F55;&#x62A5;bug&quot;</a>&#x5C31;&#x66F4;&#x901A;&#x7528;.</p><h2 id="&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;&#x7684;&#x6A21;&#x7CCA;&#x6027;">&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;&#x7684;&#x6A21;&#x7CCA;&#x6027;<a class="markdown-anchor" href="#&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;&#x7684;&#x6A21;&#x7CCA;&#x6027;"> &#xB6;</a></h2><p>&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;&#x662F;&#x6A21;&#x7CCA;, &#x5BB9;&#x6613;&#x6B67;&#x4E49;&#x7684;,  &#x4E0A;&#x9762;&#x63D0;&#x5230;&#x7684;&#x5F00;&#x6E90;&#x793E;&#x533A;&#x4E2D;&#x4EA4;&#x6D41;&#x7684;&#x969C;&#x788D;, &#x4F1A;&#x628A;&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;&#x7684;&#x6B67;&#x4E49;&#x653E;&#x5927;.</p><p>&#x4E3A;&#x4E86;&#x80FD;&#x591F;&#x5728;&#x6D88;&#x606F;&#x4E2D;&#x4F20;&#x8FBE;&#x66F4;&#x591A;&#x7684;&#x6709;&#x7528;&#x4FE1;&#x606F;, &#x5728;&#x4EA4;&#x6D41;&#x4E2D;&#x8981;&#x610F;&#x8BC6;&#x5230;&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;&#x7684;&#x5C40;&#x9650;&#x6027;. &#x4EA4;&#x6D41;&#x4E2D;&#x7684;&#x6BCF;&#x4E00;&#x65B9;&#x5982;&#x679C;&#x53EF;&#x4EE5;&#x82B1;&#x5C11;&#x91CF;&#x989D;&#x5916;&#x65F6;&#x95F4;,  &#x4F7F;&#x7528;&#x4EE3;&#x7801;, &#x590D;&#x5236;&#x7C98;&#x8D34;&#x7B49;&#x65B9;&#x5F0F;, &#x5C06;&#x4FE1;&#x606F;&#x5C3D;&#x91CF;&#x7EC4;&#x7EC7;&#x7684;&#x66F4;&#x5BA2;&#x89C2;, &#x6D88;&#x9664;&#x6B67;&#x4E49;, &#x5C31;&#x4F1A;&#x4F7F;&#x4EA4;&#x6D41;&#x66F4;&#x6709;&#x6548;. &#x6BD5;&#x7ADF;&#x4EA4;&#x6D41;&#x7684;&#x5EF6;&#x8FDF;&#x5F88;&#x5927; (&#x81F3;&#x5C11;&#x4EE5;&#x5C0F;&#x65F6;&#x4E3A;&#x5355;&#x4F4D;), &#x5982;&#x679C;&#x66F4;&#x7CBE;&#x786E;&#x7684;&#x8868;&#x8FF0;&#x80FD;&#x591F;&#x4E3A;&#x53CC;&#x65B9;&#x8282;&#x7701;&#x4E00;&#x6B21; round trip, &#x5C31;&#x5DF2;&#x7ECF;&#x8D5A;&#x4E86;.</p><p>&#x4F8B;&#x5982;, &#x5728;&#x5F00;&#x6E90;&#x793E;&#x533A;&#x7684;&#x4EA4;&#x6D41;&#x4E2D;:</p><ul><li>&#x4E0D;&#x8981;&#x8BF4; &quot;&#x6211;&#x5728; A &#x51FD;&#x6570;&#x91CC;&#x505A;&#x4E86; B&quot;, &#x590D;&#x5236;&#x7C98;&#x8D34;&#x4EE3;&#x7801;&#x66F4;&#x51C6;&#x786E;<ul><li>&#x5230;&#x5E95;&#x600E;&#x4E48;&#x6837; &quot;&#x5728; A &#x51FD;&#x6570;&#x91CC;&#x52A0;&#x5165; B&quot;, &#x6BCF;&#x4E2A;&#x4EBA;&#x7684;&#x7406;&#x89E3;&#x53EF;&#x80FD;&#x4E0D;&#x4E00;&#x6837;</li></ul></li><li>&#x4E0D;&#x8981;&#x8BF4; &quot;&#x6211;&#x7684; libA &#x7684;&#x7248;&#x672C;&#x662F; X&quot;, &#x8981;&#x63D0;&#x4F9B;&#x68C0;&#x67E5;&#x7248;&#x672C;&#x7684;&#x4EE3;&#x7801; / &#x547D;&#x4EE4;&#x53CA;&#x5176;&#x8F93;&#x51FA;<ul><li>&#x600E;&#x4E48;&#x786E;&#x8BA4;&#x7248;&#x672C;, &#x53EF;&#x80FD;&#x6709;&#x591A;&#x79CD;&#x65B9;&#x6CD5;</li></ul></li><li>&#x4E0D;&#x8981;&#x7528; &quot;&#x6211;&#x5728; xx &#x6A21;&#x5F0F;&#x8FD0;&#x884C;&#x7A0B;&#x5E8F;&quot; &#x6765;&#x4EE3;&#x66FF; <code>./main --mode=xx</code>. &#x540E;&#x8005;&#x66F4;&#x51C6;&#x786E;</li><li>&#x4E0D;&#x8981;&#x7528;&#x8BED;&#x8A00;&#x63CF;&#x8FF0;&#x6765;&#x4EE3;&#x66FF;&#x53EF;&#x4EE5;&#x590D;&#x5236;&#x7C98;&#x8D34;&#x7684; log. &#x540E;&#x8005;&#x66F4;&#x51C6;&#x786E;</li><li>&#x62A5; bug &#x65F6;, &#x5982;&#x679C;&#x5C1D;&#x8BD5;&#x4E86;&#x65B9;&#x6848; X &#x6CA1;&#x7528;, &#x4E0D;&#x8981;&#x8BF4; &quot;It still doesn&apos;t work after X&quot;. &#x56E0;&#x4E3A;&#x8FD9;&#x662F;&#x6A21;&#x7CCA;&#x7684;&#x4EBA;&#x7C7B;&#x8BED;&#x8A00;, &#x6CA1;&#x6709;&#x533A;&#x5206;&#x4E0B;&#x9762;&#x4E24;&#x79CD;&#x60C5;&#x51B5;:<ul><li>&#x5982;&#x679C;&#x7A0B;&#x5E8F;&#x7684;&#x8F93;&#x51FA;&#x548C;&#x4E4B;&#x524D;&#x76F8;&#x540C;, &#x90A3;&#x5C31;&#x8BF4; &quot;&#x8F93;&#x51FA;&#x548C;&#x4E4B;&#x524D;&#x5B8C;&#x5168;&#x76F8;&#x540C;&quot;</li><li>&#x5982;&#x679C;&#x7A0B;&#x5E8F;&#x7684;&#x884C;&#x4E3A;&#x6709;&#x4EFB;&#x4F55;&#x53D8;&#x5316;, &#x90A3;&#x4E48;&#x5E94;&#x590D;&#x5236;&#x7C98;&#x8D34;&#x65B0;&#x7684; log</li></ul></li><li>&#x4EE3;&#x8BCD;&#x5E38;&#x5E38;&#x81EA;&#x5E26;&#x6B67;&#x4E49;, &#x5E94;&#x6CE8;&#x610F;&#x6216;&#x51CF;&#x5C11;&#x4EE3;&#x8BCD;&#x7684;&#x4F7F;&#x7528;.<ul><li><a href="https://www.chiark.greenend.org.uk/~sgtatham/bugs.html" aria-label="How to Report Bugs Effectively" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x7BC7;&#x6587;&#x7AE0;</a>&#x4E2D;&#x6709;&#x4E2A;&#x4F8B;&#x5B50;:&quot;I started FooApp. It put up a warning window. I tried to close it and it crashed.&quot; &#x8FD9;&#x53E5;&#x8BDD;&#x91CC;&#x6BCF;&#x4E2A; &quot;it&quot; &#x5230;&#x5E95;&#x6307;&#x4EC0;&#x4E48;? </li></ul></li></ul><p>&#x7C7B;&#x4F3C;&#x7684;&#x4F8B;&#x5B50;&#x8FD8;&#x6709;&#x5F88;&#x591A;. &#x5C3D;&#x91CF;&#x4F7F;&#x7528;&#x66F4;&#x51C6;&#x786E;&#x7684;&#x8BED;&#x8A00;&#x6765;&#x4EA4;&#x6D41;&#x6280;&#x672F;&#x95EE;&#x9898;&#x662F;&#x4E2A;&#x91CD;&#x8981;&#x7684;&#x597D;&#x4E60;&#x60EF;.</p>]]></content>
    
    
    <summary type="html">
&lt;p&gt;&amp;#x76F8;&amp;#x6BD4;&amp;#x4F20;&amp;#x7EDF;&amp;#x7684;&amp;#x90AE;&amp;#x4EF6;&amp;#x5217;&amp;#x8868; / bugzilla/sourceforge &amp;#x7B49;&amp;#x5F00;&amp;#x6E90;&amp;#x5E73;&amp;#x53F0;, 
github &amp;#x628A;&amp;#x5F00;&amp;#x6E90;&amp;#x793E;&amp;#x533A;&amp;#x4EA4;&amp;#x6D41;&amp;#x7684;&amp;#x6210;&amp;#x672C; / &amp;#x95E8;&amp;#x69DB;&amp;#x964D;&amp;#x7684;&amp;#x5F88;&amp;#x4F4E;, &amp;#x56E0;&amp;#x6B64;&amp;#x4EA4;&amp;#x6D41;&amp;#x7684;&amp;#x8D28;&amp;#x91CF;&amp;#x4E5F;&amp;#x5E38;&amp;#x5E38;&amp;#x968F;&amp;#x4E4B;&amp;#x4E0B;&amp;#x964D;.&lt;/p&gt;
&lt;p&gt;&amp;#x6211;&amp;#x8BA1;&amp;#x5212;&amp;#x5199;&amp;#x51E0;&amp;#x7BC7;&amp;#x6587;&amp;#x7AE0;, &amp;#x4ECE; &lt;strong&gt;&amp;#x7528;&amp;#x6237; (User)&lt;/strong&gt; &amp;#x548C; &lt;strong&gt;&amp;#x7EF4;&amp;#x62A4;&amp;#x8005; (Maintainer)&lt;/strong&gt; &amp;#x4E24;&amp;#x8005;&amp;#x7684;&amp;#x89D2;&amp;#x5EA6;&amp;#x5199;&amp;#x5199;&amp;#x5F00;&amp;#x6E90;&amp;#x793E;&amp;#x533A;&amp;#x4E2D;&amp;#x5982;&amp;#x4F55;&amp;#x4F7F;&amp;#x7528; issue/PR &amp;#x8FDB;&amp;#x884C;&amp;#x6C9F;&amp;#x901A;, &amp;#x5E0C;&amp;#x671B;&amp;#x80FD;&amp;#x591F;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;#x8BA9;&amp;#x666E;&amp;#x901A;&amp;#x7528;&amp;#x6237;&amp;#x5B66;&amp;#x4F1A;&amp;#x63D0;&amp;#x95EE;, &amp;#x5B66;&amp;#x4F1A;&amp;#x4EE5; maintainer &amp;#x66F4;&amp;#x5BB9;&amp;#x6613;&amp;#x63A5;&amp;#x53D7;&amp;#x7684;&amp;#x65B9;&amp;#x5F0F;&amp;#x4F7F;&amp;#x7528; issue/PR &amp;#x505A;&amp;#x8D21;&amp;#x732E;.&lt;/li&gt;
&lt;li&gt;&amp;#x7ED9; maintainer &amp;#x63D0;&amp;#x4F9B;&amp;#x4E00;&amp;#x4E9B;&amp;#x7BA1;&amp;#x7406; issue/PR &amp;#x7684;&amp;#x7ECF;&amp;#x9A8C;, &amp;#x4E0D;&amp;#x8981;&amp;#x518D;&amp;#x4E3A; issue &amp;#x5934;&amp;#x75BC;.&lt;/li&gt;
&lt;/ul&gt;</summary>
    
    
    
    
    <category term="Open Source" scheme="https://ppwwyyxx.com/blog/tags/Open-Source/"/>
    
  </entry>
  
  <entry>
    <title>Effective Use of Python &#39;logging&#39; Module</title>
    <link href="https://ppwwyyxx.com/blog/2022/Effective-Python-Logging/"/>
    <id>https://ppwwyyxx.com/blog/2022/Effective-Python-Logging/</id>
    <published>2022-04-23T07:00:00.000Z</published>
    <updated>2022-04-23T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>In large systems, logs can be terrifying: they are huge in volume, and hard to understand.This note lists some suggestions and common misuse of Python&apos;s <a href="https://docs.python.org/3/library/logging.html" aria-label="logging &#x2014; Logging facility for Python &#x2014; Python 3.11.1 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>logging</code></a> module,with the aim of:</p><ul><li>Reduce redundant logs &amp; spams from libraries.</li><li>Allow more control of logging behaviors.</li><li>Make logs more informative to users.</li></ul><span id="more"></span><h2 id="Libraries-must-not-configure-the-root-logger">Libraries must not configure the root logger<a class="markdown-anchor" href="#Libraries-must-not-configure-the-root-logger">&#xB6;</a></h2><p>Loggers are globally identified by a dot-separated name given to <code>logging.getLogger(name)</code>, such as <code>library.module.submodule</code>.The logger named by an empty string is the &quot;root logger&quot;.</p><p>Libraries must not call <a href="https://docs.python.org/3/library/logging.html#logging.basicConfig" aria-label="logging &#x2014; Logging facility for Python &#x2014; Python 3.11.1 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>logging.basicConfig</code></a>or configure the root logger in any way, unless requested by users.</p><p>Configuration of the root logger affects <strong>all</strong> logs, which is beyond the responsibility of anysingle library. Only application developers, i.e. those who create the program that interacts withusers, should determine how to configure the root logger.</p><h2 id="Libraries-must-not-write-to-the-root-logger">Libraries must not write to the root logger<a class="markdown-anchor" href="#Libraries-must-not-write-to-the-root-logger">&#xB6;</a></h2><p>Never call functions like <code>logging.{info,error}</code> from within a library, because they write to theroot logger. I&apos;ve <a href="https://github.com/python/cpython/pull/31271/" aria-label="[doc] Add a note in howto/logging.rst about &quot;do not log to root logger in libraries&quot; by ppwwyyxx &#xB7; Pull Request #31271 &#xB7; python/cpython" class="hint--top hint--rounded hint--no-animate hint--no-arrow">added this advice</a> into CPython&apos;s official documentation.</p><p>When a library writes to the root logger, applications that use the library <strong>lose control</strong> over thelibrary&apos;s logging behavior: they cannot turn on/off the logs from the library, apply custom filter/formatter, or redirect thelogs from the library.</p><p>Instead, A library should write to a logger with <strong>easily and uniquely identifiable</strong> name, using</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">logger = logging.getLogger(good_name)<br>logger.{info,warning,error,...}<br></code></pre></td></tr></table></figure><p>This way, caller of the library will be able to reconfigure its logger using the same name.</p><h3 id="Choice-of-logger-name">Choice of logger name<a class="markdown-anchor" href="#Choice-of-logger-name">&#xB6;</a></h3><p><code>__name__</code>, i.e. the current module name, is often a good logger name.Occasionally, <code>__name__</code> is not good enough:</p><ul><li>In some deep, internal submodules, a suffix of <code>__name__</code> may beuninformative and can be removed, e.g. <code>my_lib.submodule._internal._impl</code>.Note that there is a trade-off between the name simplicity and the granularity of control.</li><li>In a monorepo, many modules may share a common prefix in <code>__name__</code>, e.g. <code>company.company3.organization</code>.Removing such a common prefix can simplify the logger names while still keeping them unique.</li></ul><p>The &quot;current function/class name&quot; is often a bad logger name because:</p><ul><li>The name is likely not unique.</li><li>The name could be the library&apos;s internal detail and therefore not identifiable to users.</li><li>The name may change more frequently (compared to module names), breaking users&apos; logger configuration.</li></ul><h3 id="Fix-such-issues-automatically">Fix such issues automatically<a class="markdown-anchor" href="#Fix-such-issues-automatically">&#xB6;</a></h3><p>I wrote a <a href="https://gist.github.com/ppwwyyxx/c0c39eb7f72d4bb569cb2ac45500f374" aria-label="Replace logging.xxx by logger.xxx" class="hint--top hint--rounded hint--no-animate hint--no-arrow">simple script</a>that processes Python source files to automatically replace all <code>logging.xxx</code> by <code>logging.getLogger(__name__).xxx</code>.This script has created PRs in a few projects that misuse the root logger, such as<a href="https://github.com/pytorch/pytorch/pull/72649" aria-label="Stop writing logs to root logger by ppwwyyxx &#xB7; Pull Request #72649 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">pytorch/72649</a>,<a href="https://github.com/lanpa/tensorboardX/pull/662" aria-label="Replace logging.xxx by logger.xxx by ppwwyyxx &#xB7; Pull Request #662 &#xB7; lanpa/tensorboardX" class="hint--top hint--rounded hint--no-animate hint--no-arrow">tensorboardX/662</a>.</p><p>I hope someone could create a linter that performs this check.</p><h2 id="Libraries-should-not-add-a-handler-that-s-visible-to-users">Libraries should not add a handler that&apos;s visible to users<a class="markdown-anchor" href="#Libraries-should-not-add-a-handler-that-s-visible-to-users">&#xB6;</a></h2><p><a href="https://docs.python.org/3/library/logging.html#logging.Handler" aria-label="logging &#x2014; Logging facility for Python &#x2014; Python 3.11.1 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Handler</a>can be attached to loggers to decide where/how to log a record.</p><p>Unless requested by users,a library should not add a handler anywhere (even not to its own logger),if the handler has a visible effect on users.This is because the application developer should make the final call onhow each library&apos;s logs are processed.Pre-existing handlers may cause issues such as duplicated logs.This suggestion is present in CPython&apos;s documentation <a href="https://docs.python.org/3/howto/logging.html#configuring-logging-for-a-library" aria-label="Logging HOWTO &#x2014; Python 3.11.1 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>.</p><p>Examples of invisible handlers that libraries may add to their loggers are:</p><ul><li><a href="https://docs.python.org/3/library/logging.handlers.html#nullhandler" aria-label="logging.handlers &#x2014; Logging handlers &#x2014; Python 3.11.1 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">logging.NullHandler</a>can be added to disable logs by default.</li><li>Within a company, one library may add handlers that send logs to a centralized service monitored by the library&apos;s developers.</li></ul><h2 id="Be-good-citizens">Be good citizens<a class="markdown-anchor" href="#Be-good-citizens">&#xB6;</a></h2><p>Libraries should try to be good citizens in reducing the amount of duplicate/unwanted/useless logs theyprinted. Some tips include:</p><ul><li>Use <strong>levels</strong> correctly.<ul><li>In particular, don&apos;t use <code>INFO</code> for debugging.</li></ul></li><li>Think about the <strong>frequency</strong> and ask the following questions when writing logs:<ul><li>How often will this line be printed?</li><li>Can I move logging to an earlier/later place so it&apos;s printed less often? e.g. from a method to<code>__init__</code>.</li><li>Is there a condition we can check and skip printing this message? e.g. <code>if valid(): log(...)</code>.</li></ul></li><li>Use helpers to control <strong>frequency</strong> of logs:<ul><li><a href="https://github.com/facebookresearch/detectron2/blob/dc1b7d14331005eab9c6b67cbf0397d552793b3f/detectron2/utils/logger.py#L140-L158" aria-label="logger.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>log_first_n</code></a>:log only for the first <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: 0;" xmlns="http://www.w3.org/2000/svg" width="2.009ex" height="1.545ex" role="img" focusable="false" viewbox="0 -683 888 683"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g></g></g></svg></mjx-container> times. It limits the total number of certain logs.<ul><li>Example: use it to print deprecation warnings.</li></ul></li><li><a href="https://github.com/facebookresearch/detectron2/blob/dc1b7d14331005eab9c6b67cbf0397d552793b3f/detectron2/utils/logger.py#L191-L200" aria-label="logger.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>log_every_n_seconds</code></a>:limit the frequency of certain logs to be less than once every <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: 0;" xmlns="http://www.w3.org/2000/svg" width="2.009ex" height="1.545ex" role="img" focusable="false" viewbox="0 -683 888 683"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g></g></g></svg></mjx-container> seconds.<ul><li>Example: use it to print progress in a loop whose iteration speed is unknown.</li></ul></li><li><a href="https://github.com/facebookresearch/detectron2/blob/dc1b7d14331005eab9c6b67cbf0397d552793b3f/detectron2/utils/logger.py#L175-L184" aria-label="logger.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>log_every_n</code></a>:log once every <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: 0;" xmlns="http://www.w3.org/2000/svg" width="2.009ex" height="1.545ex" role="img" focusable="false" viewbox="0 -683 888 683"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g></g></g></svg></mjx-container> calls. It reduces the total number of certain logs by a factor of <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: 0;" xmlns="http://www.w3.org/2000/svg" width="2.009ex" height="1.545ex" role="img" focusable="false" viewbox="0 -683 888 683"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g></g></g></svg></mjx-container>.</li><li>I ported these helpers from <a href="https://github.com/abseil/abseil-py/blob/main/absl/logging/__init__.py" aria-label="__init__.py &#xB7; abseil/abseil-py" class="hint--top hint--rounded hint--no-animate hint--no-arrow">abseil-py&apos;s logging module</a> to work with the <code>logging</code> module directly.</li></ul></li></ul><h2 id="Use-a-structured-logging-service">Use a structured logging service<a class="markdown-anchor" href="#Use-a-structured-logging-service">&#xB6;</a></h2><p><strong>Logs are not only strings</strong>. <a href="https://docs.python.org/3/library/logging.html#logging.LogRecord" aria-label="logging &#x2014; Logging facility for Python &#x2014; Python 3.11.1 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>logging.LogRecord</code></a>is a rich structure with useful attributes, and users can even tag logs with custom attributes through the <code>extra=</code> argument.</p><p>Large, distributed systems should <strong>not rely on printing</strong> as the sole method of logging.Whenever logs are printed (to terminal or files), they have to be converted to strings.A lot of useful attributes, such as stack trace and line number, are often lost. The lack of structure also makes it difficult toparse and analyze the logs.</p><p>In addition to printing, we can also use an additional <code>Handler</code> to send structured logs to a logging service,such as <a href="https://cloud.google.com/logging" aria-label="Cloud Logging | Google Cloud" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Google Cloud Logging</a> or <a href="https://www.humio.com/">Humio</a>.The advantages include:</p><ul><li>Can log a much larger volume than what a terminal can hold.</li><li>Can keep the rich structure of logs, enabling many features like querying by filters,rendering as different views, etc.<a href="https://cloud.google.com/logging/docs/view/logs-explorer-summary" aria-label="Logs Explorer features summary &#xA0;|&#xA0; Cloud Logging &#xA0;|&#xA0; Google Cloud" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Here</a> is a list of logging featuresGoogle Cloud provides.</li><li>Can persist and aggregate logs across different instances/services/users, allowing developers to find patterns from big data.</li></ul><h2 id="Reduce-duplicated-logs-among-workers">Reduce duplicated logs among workers<a class="markdown-anchor" href="#Reduce-duplicated-logs-among-workers">&#xB6;</a></h2><p>In an MPI-like distributed job (e.g. many data-parallel deep learning training), workers often print almost identical logs.We should avoid printing them all to the terminal.</p><p>A good strategy could be:</p><ol><li>Print all logs from the main process / first rank. Print some logs (e.g. only errors) from other workers.</li><li>Save all logs from all workers to separate files.</li><li>Save all logs from all workers to a structured logging service, tagged by worker rank.</li></ol><p>Detectron2&apos;s <a href="https://github.com/facebookresearch/detectron2/blob/dc1b7d14331005eab9c6b67cbf0397d552793b3f/detectron2/utils/logger.py#L69-L97" aria-label="logger.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>setup_logger</code></a> implements (1) and (2).</p><h2 id="Use-colors-in-terminal">Use colors in terminal<a class="markdown-anchor" href="#Use-colors-in-terminal">&#xB6;</a></h2><p>When logs are printed to terminal, they are more readable ifseverity is represented by colors rather than strings.I often use this formatter:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">from</span> termcolor <span class="hljs-keyword">import</span> colored<br><span class="hljs-keyword">import</span> logging<br><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">ColorFormatter</span>(logging.Formatter):<br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">__init__</span>(<span class="hljs-params">self</span>):<br>        <span class="hljs-built_in">super</span>().__init__()<br>        <span class="hljs-variable language_">self</span>._debug, <span class="hljs-variable language_">self</span>._info, <span class="hljs-variable language_">self</span>._warn, <span class="hljs-variable language_">self</span>._error = [<br>            logging.Formatter(<br>                colored(<span class="hljs-string">&quot;[%(asctime)s%(name)s]: &quot;</span>, c) + <span class="hljs-string">&quot;%(message)s&quot;</span>,<br>                datefmt=<span class="hljs-string">&quot;%m/%d %H:%M:%S&quot;</span>,<br>            )<br>            <span class="hljs-keyword">for</span> c <span class="hljs-keyword">in</span> [<span class="hljs-string">&quot;blue&quot;</span>, <span class="hljs-string">&quot;green&quot;</span>, <span class="hljs-string">&quot;yellow&quot;</span>, <span class="hljs-string">&quot;red&quot;</span>]<br>        ]<br><br>    <span class="hljs-keyword">def</span> <span class="hljs-title function_">format</span>(<span class="hljs-params">self, record</span>):<br>        <span class="hljs-keyword">if</span> record.name == <span class="hljs-string">&quot;root&quot;</span>:<br>            record.name = <span class="hljs-string">&quot;&quot;</span><br>        <span class="hljs-keyword">if</span> <span class="hljs-built_in">len</span>(record.name):<br>            record.name = <span class="hljs-string">&quot; &quot;</span> + record.name<br><br>        <span class="hljs-keyword">if</span> logging.INFO &gt; record.levelno &gt;= logging.DEBUG:<br>            <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>._debug.<span class="hljs-built_in">format</span>(record)<br>        <span class="hljs-keyword">elif</span> logging.WARNING &gt; record.levelno &gt;= logging.INFO:<br>            <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>._info.<span class="hljs-built_in">format</span>(record)<br>        <span class="hljs-keyword">elif</span> logging.ERROR &gt; record.levelno &gt;= logging.WARNING:<br>            <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>._warn.<span class="hljs-built_in">format</span>(record)<br>        <span class="hljs-keyword">else</span>:<br>            <span class="hljs-keyword">return</span> <span class="hljs-variable language_">self</span>._error.<span class="hljs-built_in">format</span>(record)<br></code></pre></td></tr></table></figure><p>Attach this formatter only when the handler writes to terminals (check <code>sys.stdout.isatty</code>),and we&apos;ll get outputs like:</p><figure class="highlight"><pre><span style="color:#6fa6ff;">[04/23 18:24:43 test]: </span>debug<span style="color:#23e423;">[04/23 18:24:43 test]: </span>info<span style="color:#e7e709;">[04/23 18:24:43 test]: </span>warning<span style="color:red;">[04/23 18:24:43 test]: </span>error</pre></figure><h2 id="logging-module-is-not-enough"><code>logging</code> module is not enough<a class="markdown-anchor" href="#logging-module-is-not-enough">&#xB6;</a></h2><p>Be aware that it&apos;s insufficient to only rely on <code>logging</code> module.A Python program may produce useful logs bypassing the <code>logging</code> module, e.g.:</p><ul><li><code>print</code> statements: they should be avoided, but may still exist.</li><li>Logs from C libraries linked to the process.</li><li>Logs from the interpreter itself, e.g. stack traces.</li><li>Logs printed by the shell, e.g. &quot;Segmentation fault&quot;.</li></ul><p>To not miss important logs, a comprehensive logging solution needs to integrateboth the structured logs from Python and the less common unstructured logs from the above sources.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;In large systems, logs can be terrifying: they are huge in volume, and hard to understand.
This note lists some suggestions and common misuse of Python&amp;apos;s &lt;a href=&quot;https://docs.python.org/3/library/logging.html&quot; aria-label=&quot;logging &amp;#x2014; Logging facility for Python &amp;#x2014; Python 3.11.1 documentation&quot; class=&quot;hint--top hint--rounded hint--no-animate hint--no-arrow&quot;&gt;&lt;code&gt;logging&lt;/code&gt;&lt;/a&gt; module,
with the aim of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reduce redundant logs &amp;amp; spams from libraries.&lt;/li&gt;
&lt;li&gt;Allow more control of logging behaviors.&lt;/li&gt;
&lt;li&gt;Make logs more informative to users.&lt;/li&gt;
&lt;/ul&gt;</summary>
    
    
    
    
    <category term="Python" scheme="https://ppwwyyxx.com/blog/tags/Python/"/>
    
    <category term="Programming" scheme="https://ppwwyyxx.com/blog/tags/Programming/"/>
    
  </entry>
  
  <entry>
    <title>How To Do Ablation Experiments</title>
    <link href="https://ppwwyyxx.com/blog/2022/How-to-do-Ablation-Experiments/"/>
    <id>https://ppwwyyxx.com/blog/2022/How-to-do-Ablation-Experiments/</id>
    <published>2022-04-08T07:00:00.000Z</published>
    <updated>2022-04-08T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<div class="message is-info my-custom-sidebar"><div class="message-header">     <i class="fas fa-info-circle mr-2"></i><p>&#x672C;&#x7CFB;&#x5217;&#x6587;&#x7AE0;</p></div><div class="message-body"><ol><li><a href="/blog/2021/DL-Experiments-and-Claims/" aria-label="Deep Learning Experiments and Claims" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Deep Learning Experiments and Claims</a></li><li><a href="/blog/2022/How-to-do-Ablation-Experiments/" aria-label="How To Do Ablation Experiments" class="hint--top hint--rounded hint--no-animate hint--no-arrow">How To Do Ablation Experiments</a></li></ol></div></div><p>&#x5EF6;&#x7EED; <a href="/blog/2021/DL-Experiments-and-Claims/" aria-label="Deep Learning Experiments and Claims" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x4E0A;&#x4E00;&#x7BC7;&#x6587;&#x7AE0;</a>, &#x518D;&#x8BF4;&#x4E00;&#x8BF4;&#x600E;&#x4E48;&#x79D1;&#x5B66;&#x7684;&#x5728; paper &#x91CC;&#x505A; ablations.</p><span id="more"></span><h2 id="Ablation-&#x63A7;&#x5236;&#x53D8;&#x91CF;">Ablation / &#x63A7;&#x5236;&#x53D8;&#x91CF;<a class="markdown-anchor" href="#Ablation-&#x63A7;&#x5236;&#x53D8;&#x91CF;"> &#xB6;</a></h2><p>&#x4E00;&#x7EC4;&#x7406;&#x60F3;&#x7684; ablation &#x5B9E;&#x9A8C;, &#x5E94;&#x5F53;&#x6240;&#x6709;&#x5B9E;&#x9A8C;&#x5C3D;&#x91CF;&#x4F7F;&#x7528;&#x4E00;&#x4EFD;&#x4EE3;&#x7801;&#x5B9E;&#x73B0;,  &#x548C;&#x76F8;&#x540C;&#x7684;&#x5B9E;&#x9A8C; recipe, &#x8FD9;&#x6837;&#x624D;&#x7B97;&#x662F;&#x771F;&#x7684; ablation. &#x5176;&#x4E2D;&#x5C24;&#x5176;&#x4E0D;&#x8981;&#x5FFD;&#x89C6;&#x5B9E;&#x73B0;&#x7684;&#x91CD;&#x8981;&#x6027;, &#x56E0;&#x4E3A;&#x540C;&#x4E00;&#x4E2A; feature &#x5728;&#x4E0D;&#x540C;&#x7684;&#x5B9E;&#x73B0;&#x91CC;&#x53EF;&#x80FD;&#x4F1A;&#x6709;&#x91CD;&#x8981;&#x7684;&#x533A;&#x522B;. &#x4F8B;&#x5982;, &#x4E00;&#x4E2A; TensorFlow &#x8DD1;&#x7684;&#x5B9E;&#x9A8C;&#x548C;&#x4E00;&#x4E2A; PyTorch &#x8DD1;&#x7684;&#x5B9E;&#x9A8C;&#x5C31;&#x4E0D;&#x80FD;&#x653E;&#x5230;&#x4E00;&#x7EC4; ablation &#x91CC;. &#x6211;&#x7684; <a href="/blog/2021/Where-are-Pixels/" aria-label="Where Are Pixels? -- a Deep Learning Perspective" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Where Are Pixels? -- a Deep Learning Perspective</a> &#x4E5F;&#x8BF4;&#x4E86;&#x5F88;&#x591A;&#x5E95;&#x5C42;&#x5B9E;&#x73B0;&#x7EC6;&#x8282;&#x5BF9;&#x6A21;&#x578B;&#x7684;&#x5F71;&#x54CD;.</p><p>&#x53CD;&#x4F8B;: <a href="https://arxiv.org/abs/1905.11946" aria-label="[1905.11946] EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks" class="hint--top hint--rounded hint--no-animate hint--no-arrow">EfficientNet</a> &#x548C;&#x7C7B;&#x4F3C;&#x7684;&#x4E0D;&#x5C11;&#x6587;&#x7AE0;&#x8BBE;&#x8BA1;&#x4E86;&#x65B0;&#x7684;&#x7F51;&#x7EDC;,  &#x5374;&#x6CA1;&#x6709;&#x8DDF;&#x5DF2;&#x6709;&#x7F51;&#x7EDC;&#x7ED3;&#x6784;&#x7684; ablations, &#x53EA;&#x6709;&#x5728;&#x4E0D;&#x540C; recipe &#x4E0B;&#x7684; system-level &#x7ED3;&#x679C;.<a href="https://arxiv.org/abs/2103.07579" aria-label="[2103.07579] Revisiting ResNets: Improved Training and Scaling Strategies" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Revisiting ResNets: Improved Training and Scaling Strategies</a> &#x4E00;&#x6587;&#x5C31;&#x8BF4;, &#x5176;&#x5B9E; ResNet &#x5728;&#x52A0;&#x5F3A;&#x7684; recipe &#x4E0B;&#x4ECD;&#x7136;&#x5F88; competitive, &#x800C;&#x90A3;&#x4E9B;&#x770B;&#x4F3C;&#x5F88;&#x5389;&#x5BB3;&#x7684;&#x65B0;&#x6A21;&#x578B;, &#x5F88;&#x5927;&#x7A0B;&#x5EA6;&#x4E0A;&#x53D7;&#x76CA;&#x4E8E;&#x5B83;&#x4EEC;&#x4F7F;&#x7528;&#x7684; recipe. &#x8FD9;&#x7BC7;&#x6587;&#x7AE0;&#x7684;&#x5F00;&#x5934;&#x548C;&#x7ED3;&#x5C3E;&#x5199;&#x7684;&#x5F88;&#x597D;, &#x6458;&#x6284;&#x4E00;&#x4E0B;:</p><blockquote><p>Novel computer vision architectures monopolize the spotlight, but the impact of the model architecture is often conflated with simultaneous changes to training methodology and scaling strategies.</p><p>...</p><p>We hope our work encourages further scrutiny in maintaining consistent methodology for both proposed innovations and baselines alike.</p></blockquote><p>&#x5F00;&#x5934;&#x5410;&#x69FD;&#x53EA;&#x60F3; claim &#x5927;&#x65B0;&#x95FB;; &#x7ED3;&#x5C3E;&#x5410;&#x69FD;&#x522B;&#x4EBA;&#x5B9E;&#x9A8C;&#x505A;&#x7684;&#x6CA1;&#x6709; scrutiny.</p><p>&#x53CD;&#x4F8B;: <a href="https://arxiv.org/abs/1904.01355" aria-label="[1904.01355] FCOS: Fully Convolutional One-Stage Object Detection" class="hint--top hint--rounded hint--no-animate hint--no-arrow">FCOS</a> &#x662F;&#x4E00;&#x4E2A; system-level &#x6548;&#x679C;&#x5F88;&#x597D;&#x7684; detector, &#x7136;&#x800C;&#x5B83;&#x5E76;&#x6CA1;&#x6709;&#x5145;&#x5206;&#x7684; ablation &#x6765;&#x8BF4;&#x660E;&#x5B83;&#x7684;&#x6548;&#x679C; <strong>&#x4E3A;&#x4EC0;&#x4E48;&#x597D;</strong>. <a href="https://arxiv.org/abs/1912.02424" aria-label="[1912.02424] Bridging the Gap Between Anchor-based and Anchor-free Detection via Adaptive Training Sample Selection" class="hint--top hint--rounded hint--no-animate hint--no-arrow">ATSS</a> &#x4E00;&#x6587;&#x5C31;&#x628A; FCOS &#x548C; RetinaNet &#x4E4B;&#x95F4;&#x7684;&#x6240;&#x6709;&#x533A;&#x522B;&#x8FDB;&#x884C;&#x4E86; ablation, &#x53D1;&#x73B0; FCOS &#x7684;&#x6027;&#x80FD;&#x63D0;&#x5347;&#x6709;&#x4E0D;&#x5C11;&#x5F97;&#x76CA;&#x4E8E;&#x4E0E;&#x5176;&#x4E2D;&#x5FC3;&#x601D;&#x60F3;&#x65E0;&#x5173;&#x7684;&#x6539;&#x52A8;.</p><h2 id="&#x53D8;&#x91CF;&#x7684;&#x76F8;&#x5173;&#x6027;">&#x53D8;&#x91CF;&#x7684;&#x76F8;&#x5173;&#x6027;<a class="markdown-anchor" href="#&#x53D8;&#x91CF;&#x7684;&#x76F8;&#x5173;&#x6027;"> &#xB6;</a></h2><p>&quot;&#x63A7;&#x5236;&#x53D8;&#x91CF;&quot; &#x5E76;&#x6CA1;&#x6709;&#x770B;&#x4E0A;&#x53BB;&#x7684;&#x90A3;&#x4E48;&#x7F8E;&#x597D;: &#x6DF1;&#x5EA6;&#x5B66;&#x4E60;&#x4F5C;&#x4E3A;&#x6CA1;&#x6709;&#x592A;&#x591A;&#x7406;&#x8BBA;&#x7684;&#x79D1;&#x5B66;, &#x4E0D;&#x540C;&#x53D8;&#x91CF;&#x4E4B;&#x95F4;&#x5E38;&#x5E38;&#x5B58;&#x5728;&#x6F5C;&#x5728;&#x7684;, &#x672A;&#x77E5;&#x7684;&#x76F8;&#x5173;&#x6027;. (&#x5176;&#x4ED6;&#x7F3A;&#x4E4F;&#x7406;&#x8BBA;&#x7684;&#x5B66;&#x79D1;, &#x4F8B;&#x5982;&#x533B;&#x5B66;, &#x5FC3;&#x7406;&#x5B66;, &#x793E;&#x4F1A;&#x5B66;&#x4E5F;&#x6709;&#x7C7B;&#x4F3C;&#x95EE;&#x9898;). &#x8FD9;&#x79CD;&#x76F8;&#x5173;&#x6027;&#x4F1A;&#x5E26;&#x6765;&#x5982;&#x4E0B;&#x4E00;&#x4E9B;&#x540E;&#x679C;:</p><ul><li><p><strong>&#x76F8;&#x5173;&#x6027;&#x8BA9; ablation &#x7684;&#x7ED3;&#x8BBA;&#x66F4;&#x53EF;&#x7591;.</strong> &#x867D;&#x7136;&#x4E00;&#x4E2A;&#x5B9E;&#x9A8C;&#x652F;&#x6301;&#x4E86; claim, &#x4F46;&#x662F;&#x8FD9;&#x4E2A; claim &#x53EF;&#x80FD;&#x8DDF;&#x5B9E;&#x9A8C;&#x91CC;&#x88AB;&#x63A7;&#x5236;&#x7684;&#x53D8;&#x91CF;&#x76F8;&#x5173;,  &#x90A3;&#x4E48;&#x4E5F;&#x8BB8;&#x6362;&#x4E00;&#x7EC4;&#x53D8;&#x91CF;&#x540E;, claim &#x5C31;&#x4E0D;&#x518D;&#x6210;&#x7ACB;&#x4E86;. &#x5BF9;&#x4E8E;&#x6DF1;&#x5EA6;&#x5B66;&#x4E60; paper &#x7684;&#x8BFB;&#x8005;, &#x8FD9;&#x4E5F;&#x662F;&#x4E00;&#x4E2A;&#x5E38;&#x89C1;&#x7684;&#x7684; concern: ablation &#x8BC1;&#x660E;&#x4E86;&#x5728;&#x8FD9;&#x4E2A; baseline &#x4E0B;&#x4F60;&#x7684;&#x65B9;&#x6CD5;&#x6709;&#x7528;, &#x53EF;&#x662F;&#x6362;&#x4E2A; baseline &#x5462;? </p><p>&#x8981;&#x7F13;&#x89E3;&#x8FD9;&#x4E2A; concern, &#x5E94;&#x5F53;&#x9009;&#x62E9; <strong>&#x5E38;&#x7528;&#x7684;, &#x6709;&#x4EE3;&#x8868;&#x6027;&#x7684;</strong> &#x5B9E;&#x9A8C; recipe (&#x5305;&#x62EC; baseline, hyperparameter, evaluation protocol &#x7B49;). &#x4E00;&#x4E2A;&#x597D;&#x7684; baseline &#x5E76;&#x4E0D;&#x9700;&#x8981;&#x662F; SOTA, &#x4F46;&#x662F;&#x9700;&#x8981;&#x662F;&#x4E00;&#x4E2A;&#x9886;&#x57DF;&#x5185;&#x5927;&#x5BB6;&#x516C;&#x8BA4;&#x5177;&#x6709;&#x4EE3;&#x8868;&#x6027;&#x7684;&#x7ED3;&#x679C;. &#x5982;&#x679C;&#x5B9E;&#x9A8C;&#x5E76;&#x4E0D;&#x662F;&#x5728;&#x4E00;&#x4E2A;&#x8BFB;&#x8005;&#x719F;&#x6089;&#x7684;&#x6761;&#x4EF6;&#x548C;&#x8BBE;&#x5B9A;&#x4E0B;,  &#x8BFB;&#x8005;&#x66F4;&#x5BB9;&#x6613;&#x6000;&#x7591; ablation &#x7684;&#x7ED3;&#x8BBA;&#x662F;&#x5426;&#x6362;&#x4E2A; recipe &#x4ECD;&#x7136;&#x901A;&#x7528;, &#x662F;&#x5426;&#x6709;&#x610F;&#x6216;&#x65E0;&#x610F;&#x4E2D;&#x9009;&#x62E9;&#x4E86;&#x5BF9; baseline &#x4E0D;&#x5229;&#x7684;&#x8BBE;&#x5B9A;, &#x662F;&#x4E0D;&#x662F;&#x62FF;&#x7740;&#x9524;&#x5B50;&#x627E;&#x9489;&#x5B50;. &#x8FD9;&#x4E9B;&#x90FD;&#x4F1A;&#x5F31;&#x5316;&#x7ED3;&#x8BBA;&#x7684;&#x53EF;&#x4FE1;&#x5EA6;. &#x6807;&#x65B0;&#x7ACB;&#x5F02;&#x7684;&#x9009;&#x62E9;&#x5F80;&#x5F80;&#x662F;&#x9700;&#x8981; justify &#x7684;, &#x8981;&#x8BF4;&#x660E; &quot;&#x8FD9;&#x4E2A;&#x9524;&#x5B50;&#x4E3A;&#x4EC0;&#x4E48;&#x9002;&#x5408;&#x8FD9;&#x79CD;&#x9489;&#x5B50;&quot; (&#x89C1;<a href="#%E9%94%A4%E5%AD%90%E5%92%8C%E9%92%89%E5%AD%90">&#x6700;&#x540E;&#x4E00;&#x8282;</a>).</p><p>&#x53CD;&#x4F8B;: &#x67D0; paper &#x53D1;&#x660E;&#x4E86;&#x4E00;&#x4E2A;&#x65B0;&#x7684; layer, &#x7136;&#x540E;: &quot;&#x5728;&#x4E00;&#x4E2A; (&#x81EA;&#x5DF1;&#x8BBE;&#x8BA1;&#x7684;) 30 &#x5C42; ResNet &#x4E0A;&#x505A;&#x4E86;&#x5B9E;&#x9A8C;, &#x5B9E;&#x9A8C;&#x8BBE;&#x5B9A;&#x548C;&#x53C2;&#x6570;&#x89C1;&#x9644;&#x5F55;&quot;.</p><p>&#x53CD;&#x4F8B;: &#x67D0; paper &#x53D1;&#x660E;&#x4E86;&#x65B0;&#x7684;&#x4F18;&#x5316;&#x65B9;&#x6CD5;, &#x7136;&#x540E;&#x5B9E;&#x9A8C;&#x662F;&#x536B;&#x661F;&#x56FE;&#x50CF;&#x5206;&#x7C7B;&#x6216;&#x533B;&#x7597;&#x56FE;&#x50CF;&#x5206;&#x7C7B;&#x8FD9;&#x79CD;&#x5C0F;&#x4F17;&#x9886;&#x57DF;.</p></li><li><p><strong>&#x76F8;&#x5173;&#x6027;&#x4F7F;&#x5F97;&#x4E0D;&#x540C;&#x53D8;&#x91CF;&#x5E26;&#x6765;&#x7684;&#x6548;&#x679C;&#x5E38;&#x5E38;&#x4E0D;&#x53EF;&#x53E0;&#x52A0;</strong>: A &#x53D8;&#x91CF;&#x548C; B &#x53D8;&#x91CF;&#x53EF;&#x80FD;&#x5404;&#x81EA;&#x80FD;&#x591F;&#x5C06;&#x7ED3;&#x679C;&#x63D0;&#x9AD8; 1%, &#x4F46;&#x662F;&#x5408;&#x5728;&#x4E00;&#x8D77;&#x4E5F;&#x53EA;&#x80FD;&#x63D0;&#x9AD8; 1%.</p><p>&#x4E3E;&#x4E2A;&#x76F4;&#x89C2;&#x7684;&#x4F8B;&#x5B50;: &#x5047;&#x5982;&#x67D0; paper &#x53D1;&#x660E;&#x4E86;&#x4E00;&#x4E2A;&#x65B0;&#x7684; loss function &#x80FD;&#x591F;&#x63D0;&#x9AD8;&#x7ED3;&#x679C;, &#x4F46;&#x4E5F;&#x8BB8;&#x8FD9;&#x4E2A; loss function &#x7684;&#x4E3B;&#x8981;&#x539F;&#x7406;&#x662F;&#x6539;&#x53D8;&#x4E86; gradient magnitude. &#x8FD9;&#x65F6;&#x5019;, &#x628A;&#x65E7;&#x7684; loss function &#x7684;&#x7CFB;&#x6570;&#x8C03;&#x4E00;&#x8C03;&#x4E5F;&#x80FD;&#x5F97;&#x5230;&#x4E00;&#x6837;&#x7684;&#x6548;&#x679C;. &#x5728;&#x8FD9;&#x91CC;, &quot;loss function&quot; &#x548C;&#x5B83;&#x7684;&#x7CFB;&#x6570;&#x5C31;&#x662F;&#x4E24;&#x4E2A;&#x76F8;&#x5173;&#x7684;&#x53D8;&#x91CF;, &#x4ED6;&#x4EEC;&#x5E26;&#x6765;&#x7684;&#x6548;&#x679C;&#x662F;&#x53EF;&#x4EE5;&#x4E92;&#x76F8;&#x66FF;&#x4EE3;&#x7684;. &#x5982;&#x679C;&#x7814;&#x7A76;&#x8005;&#x4E0D;&#x6CE8;&#x610F;, &#x5199;&#x4E86;&#x8FD9;&#x6837;&#x4E00;&#x7BC7; loss function &#x7684; paper, &#x88AB;&#x4EBA;&#x53D1;&#x73B0;&#x8DDF;&#x8C03;&#x7CFB;&#x6570;&#x6CA1;&#x533A;&#x522B;, &#x90A3; paper &#x7684;&#x4EF7;&#x503C;&#x5C31;&#x6D88;&#x5931;&#x4E86;.</p><p>&#x8FD9;&#x662F;&#x53E6;&#x4E00;&#x4E2A;&#x6211;&#x4EEC;&#x8981;&#x4F7F;&#x7528; <strong>&#x5E38;&#x7528; recipe</strong> &#x7684;&#x91CD;&#x8981;&#x539F;&#x56E0;: &#x4E00;&#x4E2A;&#x5E38;&#x7528;&#x7684; recipe &#x5F80;&#x5F80;&#x662F;&#x5DF2;&#x7ECF;&#x88AB; well-tuned, well-studied &#x7684;. &#x8FD9;&#x610F;&#x5473;&#x7740;&#x5982;&#x679C;&#x80FD;&#x5728;&#x8FD9;&#x4E2A; recipe &#x4E0A;&#x505A;&#x51FA; improvement, &#x8FD9;&#x4E2A; improvement &#x6CA1;&#x6CD5;&#x901A;&#x8FC7;&#x7B80;&#x5355;&#x7684; tuning &#x5F97;&#x5230;. &#x8FD9;&#x4E5F;&#x4F1A;&#x8BA9;&#x7ED3;&#x8BBA;&#x66F4;&#x5F3A;. &#x5373;&#x4F7F;&#x5728; ResNet &#x65E9;&#x5DF2;&#x4E0D;&#x662F; SOTA &#x7684;&#x4ECA;&#x5929;, &#x6211;&#x5982;&#x679C;&#x8981;&#x505A; CNN &#x7ED3;&#x6784;&#x76F8;&#x5173;&#x7684;&#x5B9E;&#x9A8C;, &#x4ECD;&#x7136;&#x4F1A;&#x9009;&#x62E9;&#x4ECE;&#x6807;&#x51C6;&#x7684; ResNet &#x51FA;&#x53D1;.</p></li><li><p><strong>&#x76F8;&#x5173;&#x6027;&#x5BFC;&#x81F4;&#x65B0;&#x7684;&#x65B9;&#x6CD5;&#x9700;&#x8981;&#x6539;&#x53D8; (&#x800C;&#x4E0D;&#x662F;&#x63A7;&#x5236;) &#x53D8;&#x91CF;&#x624D;&#x6709;&#x6548;.</strong> &#x4F8B;&#x5982;, &#x5F88;&#x591A;&#x65B0;&#x7684;&#x6A21;&#x578B;&#x53EF;&#x80FD;&#x9700;&#x8981;&#x627E;&#x4E00;&#x4E2A;&#x65B0;&#x7684; learning rate. &#x8FD9;&#x65F6;&#x5019;, &quot;learning rate&quot; &#x8FD9;&#x4E2A;&#x53D8;&#x91CF;&#x5C31;&#x6CA1;&#x6709;&#x63A7;&#x5236;. &#x5728;&#x6539;&#x53D8;&#x4E86;&#x8FD9;&#x4E2A;&#x53D8;&#x91CF;&#x7684;&#x540C;&#x65F6;&#x8FD8;&#x8981; convince &#x8BFB;&#x8005;&#x8FD9;&#x4E2A;&#x5B9E;&#x9A8C;&#x662F;&#x6709;&#x6548;&#x7684;, &#x662F;&#x9700;&#x8981;&#x505A;&#x989D;&#x5916;&#x7684;&#x5DE5;&#x4F5C;&#x7684;. &#x4E0B;&#x4E00;&#x8282;&#x4F1A;&#x8BE6;&#x7EC6;&#x89E3;&#x91CA;.</p></li></ul><h2 id="Recipe-&#x7684;&#x8FDB;&#x5316;">Recipe &#x7684;&#x8FDB;&#x5316;<a class="markdown-anchor" href="#Recipe-&#x7684;&#x8FDB;&#x5316;"> &#xB6;</a></h2><p>&#x524D;&#x9762;&#x8BF4; ablations &#x8981;&#x4F7F;&#x7528;&#x5E38;&#x7528;&#x7684; recipe. &#x4F46;&#x662F;, recipe &#x4E5F;&#x8981;&#x4E0E;&#x65F6;&#x4FF1;&#x8FDB;: &#x4E00;&#x4E2A;&#x66FE;&#x7ECF;&#x4E0D;&#x5E38;&#x7528;&#x7684; trick &#x53EF;&#x80FD;&#x5728;&#x672A;&#x6765;&#x4F1A;&#x8FDB;&#x5316;&#x6210;&#x5927;&#x5BB6;&#x90FD;&#x5728;&#x7528;&#x7684;&#x6807;&#x51C6; recipe, &#x4E00;&#x4E2A;&#x65B0;&#x7684;&#x65B9;&#x6CD5;&#x53EF;&#x80FD;&#x9700;&#x8981;&#x4E00;&#x4E2A;&#x65B0;&#x7684; recipe. &#x5982;&#x679C;&#x6BCF;&#x7BC7;&#x6587;&#x7AE0;&#x90FD;&#x4E25;&#x683C; &quot;&#x63A7;&#x5236;&#x53D8;&#x91CF;&quot;, &#x53EA;&#x4F7F;&#x7528;&#x65E7;&#x7684; recipe, &#x9886;&#x57DF;&#x53EF;&#x80FD;&#x4F1A;&#x9677;&#x5165; local optimum. &#x90A3;&#x4E48;, recipe &#x7684;&#x8FDB;&#x5316;&#x8981;&#x5982;&#x4F55;&#x53D1;&#x751F;&#x5462;? </p><p>&#x5047;&#x8BBE; A, B, C, ... &#x662F;&#x4E00;&#x4E9B;&#x4E0E; ablation &#x7684;&#x4E3B;&#x8981; claim &#x6CA1;&#x6709;&#x7D27;&#x5BC6;&#x5173;&#x8054;&#x7684; recipe (&#x4F8B;&#x5982; hyperparameter / tricks. &#x4E3A;&#x65B9;&#x4FBF;&#x7406;&#x89E3;, &#x53EF;&#x4EE5;&#x628A;&#x5B83;&#x4EEC;&#x5F53;&#x4F5C;&#x51E0;&#x4E2A;&#x4E0D;&#x540C;&#x7684; learning rate), &#x4E14; baseline + A &#x662F; baseline &#x7684; &quot;&#x6807;&#x51C6;&quot; recipe. &#x5F53;&#x6211;&#x4EEC;&#x5728;&#x5F00;&#x53D1;&#x4E00;&#x4E2A;&#x65B0;&#x7684;&#x65B9;&#x6CD5; &quot;proposed method&quot; &#x65F6;, &#x4E5F;&#x8BB8;&#x4F1A;&#x53D1;&#x73B0;&#x7528; B &#x6765;&#x505A;&#x5B9E;&#x9A8C;&#x6BD4; A &#x66F4;&#x597D; (<code>proposed + B &gt; proposed + A</code>). &#x8FD9;&#x65F6;, &#x4F5C;&#x8005;&#x53EF;&#x4EE5;&#x5C55;&#x793A;&#x4E0B;&#x9762;&#x8FD9;&#x4E9B;&#x5B9E;&#x9A8C;:</p><ol><li><p><code>proposed + B &gt; baseline + A</code>.  &#x8FD9;&#x662F;&#x4E0D;&#x8DB3;&#x4EE5; claim <code>proposed &gt; baseline</code> &#x7684;.</p><p>&#x5F53;&#x7136;, &#x4F5C;&#x8005;&#x4E5F;&#x53EF;&#x4EE5;&#x9009;&#x62E9;&#x5C06; &quot;B&quot; claim &#x4E3A; &quot;proposed method&quot; &#x7684;&#x4E00;&#x90E8;&#x5206; -- &#x4F46;&#x662F;&#x8FD9;&#x4F1A;&#x5F31;&#x5316;&#x6587;&#x7AE0;&#x7684;&#x4EF7;&#x503C;, &#x56E0;&#x4E3A;&#x5B83;&#x8BA9; &quot;proposed method&quot; &#x66F4;&#x590D;&#x6742;&#x4E86;. &#x8BFB;&#x8005;&#x4E5F;&#x4F1A;&#x7591;&#x60D1;: &#x4E5F;&#x8BB8;&#x53EA;&#x6709; B &#x5C31;&#x591F;&#x4E86;, &quot;proposed method&quot; &#x91CC;&#x5269;&#x4E0B;&#x7684;&#x90E8;&#x5206;&#x4E5F;&#x8BB8;&#x4EF7;&#x503C;&#x4E0D;&#x5927;.</p></li><li><p><code>proposed + B &gt; max(baseline + A, baseline + B)</code> : &#x8FD9;&#x6837;&#x6765; claim <code>proposed &gt; baseline</code>, &#x8BFB;&#x8005;&#x4E00;&#x822C;&#x662F;&#x63A5;&#x53D7;&#x7684;.</p><p>&#x5728;&#x6B64;&#x57FA;&#x7840;&#x4E0A;, &#x8BFB;&#x8005;&#x4F1A;&#x597D;&#x5947; <code>proposed + A</code> &#x8868;&#x73B0;&#x5982;&#x4F55;. &#x5982;&#x679C;<code>proposed + A &lt; baseline + A</code>, &#x5219;&#x8BF4;&#x660E; proposed &#x4F9D;&#x8D56; B. &#x5982;&#x679C; B &#x662F;&#x67D0;&#x4E2A;&#x590D;&#x6742;&#x7684; trick &#x7684;&#x8BDD;, &#x8FD9;&#x79CD;&#x4F9D;&#x8D56;&#x4E5F;&#x4F1A;&#x964D;&#x4F4E; proposed &#x7684;&#x4EF7;&#x503C;.</p><p>&#x8981;&#x6CE8;&#x610F;&#x5230;, &#x4EE5;&#x4E0A;&#x7684;&#x7ED3;&#x679C;&#x65E0;&#x6CD5;&#x6392;&#x9664;&#x4E0B;&#x9762;&#x8FD9;&#x79CD;&#x53EF;&#x80FD;&#x6027;: &#x5B58;&#x5728;&#x4E00;&#x4E2A; C, &#x4F7F;&#x5F97; <code>max(proposed + B, proposed + C) &lt; baseline + C</code>. C &#x7684;&#x5B58;&#x5728;&#x4F1A;&#x4F7F;&#x5F97; baseline &#x770B;&#x4E0A;&#x53BB;&#x6BD4; proposed &#x66F4;&#x597D;. &#x4F46;&#x662F;, &#x7531;&#x4E8E;&#x6211;&#x4EEC;&#x5047;&#x8BBE; baseline + A &#x5DF2;&#x7ECF;&#x662F;&#x4E00;&#x4E2A;&#x5E38;&#x7528;&#x7684;&#x6807;&#x51C6; recipe &#x4E86;, &#x5982;&#x679C;&#x5B58;&#x5728;&#x8FD9;&#x6837;&#x7684; C, &#x90A3; C &#x5927;&#x6982;&#x7387;&#x662F; nontrivial &#x7684;, &#x4E0D;&#x592A;&#x53EF;&#x80FD;&#x662F;&#x7B80;&#x5355;&#x7684;&#x8C03;&#x53C2;. &#x8FD9;&#x4E5F;&#x662F;&#x4E3A;&#x4EC0;&#x4E48;&#x8981;&#x5C3D;&#x91CF;&#x4F7F;&#x7528;&#x6807;&#x51C6; recipe.</p></li><li><p>&#x4E3A;&#x4E86;&#x5C3D;&#x91CF;&#x964D;&#x4F4E; C &#x5B58;&#x5728;&#x7684;&#x53EF;&#x80FD;, &#x5728;&#x8BA1;&#x7B97;&#x8D44;&#x6E90;&#x5141;&#x8BB8;&#x7684;&#x60C5;&#x51B5;&#x4E0B;&#x5E94;&#x5F53;&#x5BF9; recipe &#x8FDB;&#x884C;&#x516C;&#x5E73;&#x7684;&#x641C;&#x7D22;: &#x5982;&#x679C; proposed &#x4F7F;&#x7528;&#x7684; hyperparameter B &#x662F; grid search &#x627E;&#x51FA;&#x6765;&#x7684;, &#x90A3;&#x4E48;&#x4E5F;&#x5E94;&#x5BF9; baseline &#x7684; hyperparameter &#x8FDB;&#x884C;&#x7C7B;&#x4F3C;&#x7684; grid search, &#x770B;&#x770B;&#x662F;&#x5426;&#x80FD;&#x627E;&#x5230;&#x4E00;&#x4E2A;&#x66F4;&#x597D;&#x7684; C.</p><p>&#x4F8B;&#x5B50;: <a href="https://arxiv.org/abs/2005.12872" aria-label="[2005.12872] End-to-End Object Detection with Transformers" class="hint--top hint--rounded hint--no-animate hint--no-arrow">DETR</a> &#x7684;&#x8BAD;&#x7EC3;&#x4EE3;&#x4EF7;&#x6BD4;&#x4E3B;&#x6D41; detection &#x6A21;&#x578B;&#x90FD;&#x5927;&#x5F97;&#x591A; (100-500 epochs), &#x8FD9;&#x70B9;&#x5728;&#x6280;&#x672F;&#x4E0A;&#x96BE;&#x4EE5;&#x907F;&#x514D;. &#x8FD9;&#x4E2A;&#x533A;&#x522B;&#x5BFC;&#x81F4;&#x516C;&#x5E73;&#x7684;&#x5B9E;&#x9A8C;&#x4E0D;&#x5BB9;&#x6613;&#x505A;, &#x56E0;&#x4E3A;&#x4E3B;&#x6D41;&#x6A21;&#x578B; (Faster R-CNN) &#x8FD8;&#x6CA1;&#x6709;&#x4E00;&#x4E2A;&#x5E38;&#x7528;&#x7684;,  &#x8BAD;&#x7EC3;&#x8FD9;&#x4E48;&#x957F;&#x65F6;&#x95F4;&#x7684; recipe. &#x636E;&#x6211;&#x4E86;&#x89E3;, &#x4F5C;&#x8005;&#x4EEC;&#x5F53;&#x65F6;&#x5C1D;&#x8BD5;&#x4E86;&#x4E0D;&#x5C11;&#x65B9;&#x6CD5;&#x63D0;&#x9AD8; Faster R-CNN &#x5728;&#x8FD9;&#x4E2A;&#x8BAD;&#x7EC3;&#x957F;&#x5EA6;&#x4E0B;&#x7684;&#x6027;&#x80FD;, &#x5C3D;&#x91CF;&#x8BA9; baseline &#x66F4;&#x5F3A;. &#x8FD9;&#x662F;&#x5F88;&#x8D1F;&#x8D23;&#x4EFB;&#x7684;&#x505A;&#x6CD5;.</p></li></ol><h2 id="Baseline-&#x65E0;&#x6CD5;-reproduce">Baseline &#x65E0;&#x6CD5; reproduce<a class="markdown-anchor" href="#Baseline-&#x65E0;&#x6CD5;-reproduce">&#xB6;</a></h2><p>&#x524D;&#x9762;&#x4E24;&#x8282;&#x90FD;&#x63D0;&#x5230;&#x4E86;, &#x4F7F;&#x7528;&#x4E00;&#x4E2A;&#x5E38;&#x7528;&#x7684;, &#x6709;&#x4EE3;&#x8868;&#x6027;&#x7684;, &#x6807;&#x51C6;&#x7684; baseline &#x662F;&#x5F88;&#x91CD;&#x8981;&#x7684;. &#x8FD9;&#x6837;&#x7684; baseline &#x5728;&#x6587;&#x4E2D;&#x7684;&#x7ED3;&#x679C;&#x5E94;&#x8BE5;&#x81F3;&#x5C11;&#x4E0E;&#x522B;&#x4EBA; paper &#x76F8;&#x540C;&#x5B9E;&#x9A8C;&#x7684;&#x7ED3;&#x679C;&#x63A5;&#x8FD1;. &#x5982;&#x679C; baseline &#x6BD4;&#x522B;&#x4EBA;&#x5DEE;, &#x8BF4;&#x660E; baseline &#x91CC;&#x4E00;&#x5B9A;&#x6709;&#x67D0;&#x4E9B;&#x56E0;&#x7D20;&#x4E0E;&#x90A3;&#x4E2A;&#x5E38;&#x7528;&#x7684; recipe &#x4E0D;&#x540C;, &#x56E0;&#x6B64;&#x4F1A;&#x5F31;&#x5316;&#x7ED3;&#x8BBA;&#x7684;&#x53EF;&#x4FE1;&#x5EA6;.</p><p>&#x53CD;&#x4F8B;: &#x67D0; paper &#x63D0;&#x51FA;&#x4E86; ResNet &#x7684;&#x5C0F;&#x6539;&#x52A8;, &#x4F46;&#x662F;&#x6587;&#x4E2D;&#x7684; ResNet baseline &#x6BD4; pytorch &#x5B98;&#x65B9;&#x6837;&#x4F8B;&#x663E;&#x8457;&#x7684;&#x5DEE;. &#x5728;&#x8FD9;&#x4E2A; baseline &#x4E0A;&#x6709; 1% &#x7684;&#x63D0;&#x5347;, &#x5E76;&#x4E0D;&#x610F;&#x5473;&#x7740;&#x5728;&#x5E38;&#x7528;&#x7684; baseline &#x4E0A;&#x80FD;&#x6709;&#x63D0;&#x5347;: &#x56E0;&#x4E3A;&#x5982;&#x524D;&#x6587;&#x6240;&#x8BF4;, &#x4E0D;&#x540C;&#x7684;&#x56E0;&#x7D20;&#x5E38;&#x5E38;&#x662F;&#x4E0D;&#x53EF;&#x53E0;&#x52A0;&#x7684;. &#x4E8B;&#x5B9E;&#x5C31;&#x662F;, &#x6709;&#x8BB8;&#x591A;&#x65B9;&#x6CD5;&#x53EA;&#x5728;&#x5F31;&#x7684; baseline &#x4E0A;&#x6709;&#x6548;.</p><p>&#x5982;&#x679C; baseline &#x786E;&#x5B9E;&#x65E0;&#x6CD5; reproduce &#x600E;&#x4E48;&#x529E;? &#x8FD9;&#x79CD;&#x5904;&#x5883;&#x5F88;&#x9057;&#x61BE;. &#x4E00;&#x4E2A; research topic &#x5982;&#x679C;&#x6CA1;&#x6709;&#x5927;&#x5BB6;&#x516C;&#x8BA4;&#x7684; reproducible &#x7684;&#x4EE3;&#x7801;&#x548C; baseline &#x8BBE;&#x5B9A;, &#x5C31;&#x5BB9;&#x6613;&#x9677;&#x5165;&#x4E71;&#x8C61;. &#x4F8B;&#x5982; <a href="https://arxiv.org/abs/2003.08505" aria-label="[2003.08505] A Metric Learning Reality Check" class="hint--top hint--rounded hint--no-animate hint--no-arrow">A Metric Learning Reality Check</a>,<a href="https://arxiv.org/abs/1709.06560" aria-label="[1709.06560] Deep Reinforcement Learning that Matters" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Deep Reinforcement Learning that Matters</a> &#x90FD;&#x662F;&#x5728;&#x5410;&#x69FD;&#x5404;&#x81EA;&#x9886;&#x57DF;&#x91CC;&#x7684;&#x95EE;&#x9898;. &#x8FD9;&#x6B63;&#x662F;&#x4E3A;&#x4EC0;&#x4E48;&#x8981;&#x505A;&#x5F00;&#x6E90;&#x9AD8;&#x8D28;&#x91CF; codebase.</p><h2 id="&#x9524;&#x5B50;&#x548C;&#x9489;&#x5B50;">&#x9524;&#x5B50;&#x548C;&#x9489;&#x5B50;<a class="markdown-anchor" href="#&#x9524;&#x5B50;&#x548C;&#x9489;&#x5B50;"> &#xB6;</a></h2><p>&#x524D;&#x9762;&#x8BF4;&#x5230;, &#x5B9E;&#x9A8C;&#x8BBE;&#x5B9A;&#x4E00;&#x822C;&#x4F7F;&#x7528; &quot;&#x5E38;&#x7528;, &#x6807;&#x51C6;&quot; &#x7684; recipe, &#x5426;&#x5219;&#x6709;&#x62FF;&#x7740;&#x9524;&#x5B50;&#x627E;&#x9489;&#x5B50;&#x7684;&#x5ACC;&#x7591;. &#x800C;&#x6709;&#x7684;&#x65F6;&#x5019;, &#x5982;&#x679C;&#x6211;&#x4EEC;&#x6070;&#x597D;&#x8981; claim&quot;&#x6211;&#x7684;&#x9524;&#x5B50;&#x9002;&#x5408;&#x7279;&#x5B9A;&#x7684;&#x9489;&#x5B50;&quot;, &#x90A3;&#x4E48;&#x5DE7;&#x5999;&#x7684;&#x6539;&#x53D8; recipe &#x4E5F;&#x8BB8;&#x4F1A;&#x6709;&#x66F4;&#x597D;&#x7684;&#x6548;&#x679C;. &#x4E0B;&#x9762;&#x4E3E;&#x51E0;&#x4E2A;&#x6B63; / &#x53CD;&#x9762;&#x4F8B;&#x5B50;.</p><p>&#x6B63;&#x4F8B;: <a href="https://arxiv.org/abs/1512.03385" aria-label="[1512.03385] Deep Residual Learning for Image Recognition" class="hint--top hint--rounded hint--no-animate hint--no-arrow">ResNet paper</a> &#x591A;&#x6B21; report &#x4E86; training error (Fig. 4, 6), &#x8FD9;&#x4E5F;&#x8BB8;&#x4F1A;&#x663E;&#x5F97;&#x5947;&#x602A;, &#x6BD5;&#x7ADF; training error &#x4E0D;&#x662F;&#x4E00;&#x4E2A;&#x5927;&#x5BB6;&#x5E38;&#x7528;&#x7684; metric. &#x8FD9;&#x662F;&#x56E0;&#x4E3A;&#x6587;&#x7AE0;&#x7684;&#x5927; claim &#x662F;&#x5173;&#x4E8E; residual connection &#x5BF9;&#x8BAD;&#x7EC3; / &#x4F18;&#x5316;&#x6709;&#x5E2E;&#x52A9;, &#x800C; deep plain network &#x96BE;&#x4EE5;&#x4F18;&#x5316;.Training error &#x624D;&#x662F;&#x8DDF;&#x8FD9;&#x4E2A; claim &#x76F4;&#x63A5;&#x76F8;&#x5173;&#x7684; metric, validation error &#x53D8;&#x597D;&#x53EA;&#x662F; training error &#x53D8;&#x597D;&#x7684;&#x4E00;&#x4E2A;&#x526F;&#x4EA7;&#x54C1;.</p><p>&#x6B63; / &#x53CD;&#x4F8B;: Optimizer &#x7684;&#x6839;&#x672C;&#x76EE;&#x6807;&#x662F;&#x964D;&#x4F4E; training loss, &#x6240;&#x4EE5;&#x6BD4;&#x8F83;&#x4E0D;&#x540C;&#x7684; optimizer (&#x4F8B;&#x5982; SGD/Adam) &#x7684;&#x65F6;&#x5019;&#x4E0D;&#x80FD;&#x4E0D;&#x770B; training loss.<a href="https://arxiv.org/abs/2102.06356" aria-label="[2102.06356] A Large Batch Optimizer Reality Check: Traditional, Generic Optimizers Suffice Across Batch Sizes" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &#x8FD9;&#x7BC7; paper</a> Sec. 5 &#x5C31;&#x5410;&#x4E86;&#x8FD9;&#x4E2A;&#x69FD;: &#x6709;&#x7684; optimizer &#x8DD1;&#x51FA;&#x6765;&#x7684; validation error &#x66F4;&#x4F4E;&#x5C31;&#x58F0;&#x79F0;&#x81EA;&#x5DF1;&#x66F4;&#x597D;, &#x4F46;&#x662F;&#x5B9E;&#x9645;&#x4E0A;&#x53D1;&#x73B0;&#x5B83;&#x7684; training loss &#x66F4;&#x9AD8;.</p><p>&#x53CD;&#x4F8B;: &#x6211; review &#x8FC7;&#x7684;&#x67D0; paper claim &#x4E00;&#x4E2A;&#x65B9;&#x6CD5;&#x80FD;&#x591F;&#x63D0;&#x9AD8;&#x6A21;&#x578B;&#x7684; capacity &#x6216;&#x8868;&#x8FBE;&#x80FD;&#x529B;, &#x4F46;&#x662F;&#x5B9E;&#x9A8C;&#x662F;&#x62FF; ResNet &#x5728; Cifar10 &#x4E0A;&#x770B; validation error. &#x867D;&#x7136; validation error &#x662F;&#x4E00;&#x4E2A;&#x5E38;&#x89C1;&#x7684; metric, &#x4F46;&#x662F; ResNet &#x5728; Cifar10 &#x4E0A;&#x4E25;&#x91CD; overfit (training error = 0),  validation error &#x8DDF;&#x6A21;&#x578B; capacity &#x6CA1;&#x4EC0;&#x4E48;&#x5173;&#x7CFB;.</p><p>&#x6B63;&#x4F8B;: detection &#x91CC;&#x6709;&#x5F88;&#x591A;&#x53EF;&#x7528; metric, &#x5982;&#x5927;&#x5C0F;&#x7269;&#x4F53;&#x7684; AP, &#x4E0D;&#x540C; IoU &#x7684; AP, &#x7B49;&#x7B49;. &#x5F53;&#x6709;&#x5408;&#x9002;&#x7684; justification &#x7684;&#x65F6;&#x5019; (&#x4F8B;&#x5982;&#x6A21;&#x578B;&#x8BBE;&#x8BA1;&#x4E0A;&#x5BF9;&#x5927;&#x7269;&#x4F53;&#x66F4;&#x53CB;&#x597D;), &#x6BD4;&#x8F83;&#x5176;&#x4E2D;&#x67D0;&#x4E2A;&#x7279;&#x5B9A;&#x7684; metric &#x80FD;&#x591F;&#x5E2E;&#x52A9;&#x6587;&#x7AE0;&#x7684; claim. <a href="https://arxiv.org/abs/1912.08193" aria-label="[1912.08193] PointRend: Image Segmentation as Rendering" class="hint--top hint--rounded hint--no-animate hint--no-arrow">PointRend paper</a> &#x91CC;&#x4E3A;&#x4E86;&#x8BC1;&#x660E; &quot;&#x8FB9;&#x754C;&#x7ED3;&#x679C;&#x66F4;&#x51C6;&#x786E;&quot; &#x8FD9;&#x4E2A; claim, &#x8BBE;&#x8BA1;&#x4E86;&#x4E00;&#x4E2A;&#x65B0; metric: &#x62FF; COCO &#x8BAD;&#x7EC3;&#x7684;&#x6A21;&#x578B;&#x5728; LVIS &#x7684;&#x9AD8;&#x8D28;&#x91CF;&#x6807;&#x6CE8;&#x4E0B;&#x7B97; AP. &#x8FD9;&#x6837;&#x5F97;&#x5230;&#x7684;&#x7ED3;&#x679C;&#x6BD4;&#x4F7F;&#x7528;&#x6807;&#x51C6;&#x7684; metric &#x66F4;&#x6709;&#x8BF4;&#x670D;&#x529B;.</p><p>&#x6B63;&#x4F8B;: <a href="https://arxiv.org/abs/1703.06870" aria-label="[1703.06870] Mask R-CNN" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Mask R-CNN</a> &#x7684; Table 2 (d) &#x4F7F;&#x7528;&#x4E86;&#x4E00;&#x4E2A;&#x5F88;&#x5C11;&#x89C1;&#x7684; recipe: &#x57FA;&#x672C;&#x6CA1;&#x4EBA;&#x7528;&#x7684; ResNet-Conv5 backbone. &#x8FD9;&#x662F;&#x4E3A;&#x4E86;&#x8BC1;&#x660E;&#x5173;&#x4E8E; RoIAlign vs. RoIPool &#x7684; claim: RoIPool &#x7684; feature map &#x4E0D;&#x5BF9;&#x9F50;, stride &#x8D8A;&#x5927;, &#x5F71;&#x54CD;&#x8D8A;&#x5927;. &#x901A;&#x8FC7; Conv5 (stride=32) &#x4E0A;&#x7684;&#x5B9E;&#x9A8C;&#x66F4;&#x52A0;&#x5F3A;&#x5316;&#x4E86;&#x8FD9;&#x4E2A; claim. &#x5F53;&#x521D;&#x4E4B;&#x6240;&#x4EE5;&#x5728; detectron2 &#x91CC;&#x4FDD;&#x7559; Conv4 &#x8FD9;&#x4E9B;&#x6027;&#x80FD;&#x5E76;&#x4E0D;&#x597D;&#x7684;&#x6A21;&#x578B;, &#x5C31;&#x662F;&#x56E0;&#x4E3A;&#x5B83;&#x4EEC;&#x5728;&#x8BB8;&#x591A;&#x5B9E;&#x9A8C;&#x4E2D;&#x4ECD;&#x7136;&#x6709;&#x7814;&#x7A76;&#x4EF7;&#x503C;.</p><p>&#x6B63;&#x4F8B;: &#x6211;&#x7684;<a href="https://arxiv.org/abs/2105.07576" aria-label="[2105.07576] Rethinking &quot;Batch&quot; in BatchNorm" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> Rethinking &quot;Batch&quot; in BatchNorm</a> &#x5B9E;&#x9A8C;&#x5F88;&#x591A;, &#x91CC;&#x9762;&#x505A;&#x4E86;&#x5BF9; BatchNorm &#x7684;&#x5404;&#x79CD;&#x9B54;&#x6539;. &#x8FD9;&#x4E9B;&#x9B54;&#x6539;&#x91CC;, &#x5927;&#x591A;&#x6570;&#x7684;&#x76EE;&#x7684;&#x4E0D;&#x662F;&#x4E3A;&#x4E86; propose &#x4E00;&#x79CD;&#x65B0;&#x65B9;&#x6CD5;, &#x800C;&#x662F;&#x901A;&#x8FC7;&#x6539;&#x53D8; BatchNorm &#x7684;&#x884C;&#x4E3A;, &#x6765;&#x9A8C;&#x8BC1;&#x5173;&#x4E8E; &quot;BatchNorm &#x5728;&#x5404;&#x79CD;&#x573A;&#x666F;&#x4E0B;&#x4E3A;&#x4EC0;&#x4E48;&#x4E0D; work&quot; &#x7684;&#x4E00;&#x4E9B; claim.</p><p>&#x5982;&#x4F55;&#x627E;&#x4E00;&#x4E2A;&#x597D;&#x9489;&#x5B50;, &#x8BBE;&#x8BA1;&#x5B9E;&#x9A8C;&#x6765;&#x5DE7;&#x5999;&#x7684;&#x7A81;&#x51FA; claim, &#x662F;&#x4E00;&#x9879;&#x6280;&#x672F;&#x6D3B;, &#x4E5F;&#x662F;&#x589E;&#x52A0;&#x4E00;&#x7BC7; paper &#x7684;&#x8BF4;&#x670D;&#x529B;&#x7684;&#x91CD;&#x8981;&#x624B;&#x6BB5;.</p>]]></content>
    
    
    <summary type="html">
&lt;p&gt;&amp;#x5EF6;&amp;#x7EED; &lt;a href=&quot;/blog/2021/DL-Experiments-and-Claims/&quot; aria-label=&quot;Deep Learning Experiments and Claims&quot; class=&quot;hint--top hint--rounded hint--no-animate hint--no-arrow&quot;&gt;&amp;#x4E0A;&amp;#x4E00;&amp;#x7BC7;&amp;#x6587;&amp;#x7AE0;&lt;/a&gt;,
 &amp;#x518D;&amp;#x8BF4;&amp;#x4E00;&amp;#x8BF4;&amp;#x600E;&amp;#x4E48;&amp;#x79D1;&amp;#x5B66;&amp;#x7684;&amp;#x5728; paper &amp;#x91CC;&amp;#x505A; ablations.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Research" scheme="https://ppwwyyxx.com/blog/tags/Research/"/>
    
    <category term="Deep Learning" scheme="https://ppwwyyxx.com/blog/tags/Deep-Learning/"/>
    
  </entry>
  
  <entry>
    <title>Where Are Pixels? -- a Deep Learning Perspective</title>
    <link href="https://ppwwyyxx.com/blog/2021/Where-are-Pixels/"/>
    <id>https://ppwwyyxx.com/blog/2021/Where-are-Pixels/</id>
    <published>2021-06-11T07:00:00.000Z</published>
    <updated>2021-06-11T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>Technically, an image is a function that maps a <strong>continuous</strong> domain, e.g.a box <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="13.21ex" height="2.262ex" role="img" focusable="false" viewbox="0 -750 5838.8 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"/></g><g data-mml-node="mn" transform="translate(278,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g><g data-mml-node="mo" transform="translate(778,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mi" transform="translate(1222.7,0)"><path data-c="1D44B" d="M42 0H40Q26 0 26 11Q26 15 29 27Q33 41 36 43T55 46Q141 49 190 98Q200 108 306 224T411 342Q302 620 297 625Q288 636 234 637H206Q200 643 200 645T202 664Q206 677 212 683H226Q260 681 347 681Q380 681 408 681T453 682T473 682Q490 682 490 671Q490 670 488 658Q484 643 481 640T465 637Q434 634 411 620L488 426L541 485Q646 598 646 610Q646 628 622 635Q617 635 609 637Q594 637 594 648Q594 650 596 664Q600 677 606 683H618Q619 683 643 683T697 681T738 680Q828 680 837 683H845Q852 676 852 672Q850 647 840 637H824Q790 636 763 628T722 611T698 593L687 584Q687 585 592 480L505 384Q505 383 536 304T601 142T638 56Q648 47 699 46Q734 46 734 37Q734 35 732 23Q728 7 725 4T711 1Q708 1 678 1T589 2Q528 2 496 2T461 1Q444 1 444 10Q444 11 446 25Q448 35 450 39T455 44T464 46T480 47T506 54Q523 62 523 64Q522 64 476 181L429 299Q241 95 236 84Q232 76 232 72Q232 53 261 47Q262 47 267 47T273 46Q276 46 277 46T280 45T283 42T284 35Q284 26 282 19Q279 6 276 4T261 1Q258 1 243 1T201 2T142 2Q64 2 42 0Z"/></g><g data-mml-node="mo" transform="translate(2074.7,0)"><path data-c="5D" d="M22 710V750H159V-250H22V-210H119V710H22Z"/></g><g data-mml-node="mo" transform="translate(2574.9,0)"><path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"/></g><g data-mml-node="mo" transform="translate(3575.1,0)"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"/></g><g data-mml-node="mn" transform="translate(3853.1,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g><g data-mml-node="mo" transform="translate(4353.1,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mi" transform="translate(4797.8,0)"><path data-c="1D44C" d="M66 637Q54 637 49 637T39 638T32 641T30 647T33 664T42 682Q44 683 56 683Q104 680 165 680Q288 680 306 683H316Q322 677 322 674T320 656Q316 643 310 637H298Q242 637 242 624Q242 619 292 477T343 333L346 336Q350 340 358 349T379 373T411 410T454 461Q546 568 561 587T577 618Q577 634 545 637Q528 637 528 647Q528 649 530 661Q533 676 535 679T549 683Q551 683 578 682T657 680Q684 680 713 681T746 682Q763 682 763 673Q763 669 760 657T755 643Q753 637 734 637Q662 632 617 587Q608 578 477 424L348 273L322 169Q295 62 295 57Q295 46 363 46Q379 46 384 45T390 35Q390 33 388 23Q384 6 382 4T366 1Q361 1 324 1T232 2Q170 2 138 2T102 1Q84 1 84 9Q84 14 87 24Q88 27 89 30T90 35T91 39T93 42T96 44T101 45T107 45T116 46T129 46Q168 47 180 50T198 63Q201 68 227 171L252 274L129 623Q128 624 127 625T125 627T122 629T118 631T113 633T105 634T96 635T83 636T66 637Z"/></g><g data-mml-node="mo" transform="translate(5560.8,0)"><path data-c="5D" d="M22 710V750H159V-250H22V-210H119V710H22Z"/></g></g></g></svg></mjx-container>, to intensities such as (R, G, B).To store it on computer memory, an image is <strong>discretized</strong> to an array <code>array[H][W]</code>, where each element<code>array[i][j]</code> is a <strong>pixel</strong>.</p><p>How does discretization work? How does a discrete pixel relate to the abstract notion of the underlying continuous image?These basic questions play an important role in computer graphics &amp; computer vision algorithms.</p><p>This article discusses these low-level details, and how they affect our CNN models and deep learning libraries.If you ever wonder which resize function to use or whether you should add/subtract 0.5 or 1 to some pixel coordinates,you may find answers here.Interestingly, these details have contributed to many accuracy improvements in Detectronand Detectron2.</p><span id="more"></span><h1 id="Formation-of-Discrete-Image">Formation of Discrete Image<a class="markdown-anchor" href="#Formation-of-Discrete-Image">&#xB6;</a></h1><p>Sampling theory tells us howa continuous 2D signal is turned into a discrete array by <strong>sampling</strong> and <strong>filtering</strong>.</p><ol><li><p>We choose a <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.05ex;" xmlns="http://www.w3.org/2000/svg" width="7.146ex" height="1.595ex" role="img" focusable="false" viewbox="0 -683 3158.4 705"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D43B" d="M228 637Q194 637 192 641Q191 643 191 649Q191 673 202 682Q204 683 219 683Q260 681 355 681Q389 681 418 681T463 682T483 682Q499 682 499 672Q499 670 497 658Q492 641 487 638H485Q483 638 480 638T473 638T464 637T455 637Q416 636 405 634T387 623Q384 619 355 500Q348 474 340 442T328 395L324 380Q324 378 469 378H614L615 381Q615 384 646 504Q674 619 674 627T617 637Q594 637 587 639T580 648Q580 650 582 660Q586 677 588 679T604 682Q609 682 646 681T740 680Q802 680 835 681T871 682Q888 682 888 672Q888 645 876 638H874Q872 638 869 638T862 638T853 637T844 637Q805 636 794 634T776 623Q773 618 704 340T634 58Q634 51 638 51Q646 48 692 46H723Q729 38 729 37T726 19Q722 6 716 0H701Q664 2 567 2Q533 2 504 2T458 2T437 1Q420 1 420 10Q420 15 423 24Q428 43 433 45Q437 46 448 46H454Q481 46 514 49Q520 50 522 50T528 55T534 64T540 82T547 110T558 153Q565 181 569 198Q602 330 602 331T457 332H312L279 197Q245 63 245 58Q245 51 253 49T303 46H334Q340 38 340 37T337 19Q333 6 327 0H312Q275 2 178 2Q144 2 115 2T69 2T48 1Q31 1 31 10Q31 12 34 24Q39 43 44 45Q48 46 59 46H65Q92 46 125 49Q139 52 144 61Q147 65 216 339T285 628Q285 635 228 637Z"/></g><g data-mml-node="mo" transform="translate(1110.2,0)"><path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"/></g><g data-mml-node="mi" transform="translate(2110.4,0)"><path data-c="1D44A" d="M436 683Q450 683 486 682T553 680Q604 680 638 681T677 682Q695 682 695 674Q695 670 692 659Q687 641 683 639T661 637Q636 636 621 632T600 624T597 615Q597 603 613 377T629 138L631 141Q633 144 637 151T649 170T666 200T690 241T720 295T759 362Q863 546 877 572T892 604Q892 619 873 628T831 637Q817 637 817 647Q817 650 819 660Q823 676 825 679T839 682Q842 682 856 682T895 682T949 681Q1015 681 1034 683Q1048 683 1048 672Q1048 666 1045 655T1038 640T1028 637Q1006 637 988 631T958 617T939 600T927 584L923 578L754 282Q586 -14 585 -15Q579 -22 561 -22Q546 -22 542 -17Q539 -14 523 229T506 480L494 462Q472 425 366 239Q222 -13 220 -15T215 -19Q210 -22 197 -22Q178 -22 176 -15Q176 -12 154 304T131 622Q129 631 121 633T82 637H58Q51 644 51 648Q52 671 64 683H76Q118 680 176 680Q301 680 313 683H323Q329 677 329 674T327 656Q322 641 318 637H297Q236 634 232 620Q262 160 266 136L501 550L499 587Q496 629 489 632Q483 636 447 637Q428 637 422 639T416 648Q416 650 418 660Q419 664 420 669T421 676T424 680T428 682T436 683Z"/></g></g></g></svg></mjx-container> rectangular grid of points, from which we will draw samples.In order to make the best use of the produced samples, we have to know the exact location where every sample onthis grid is chosen.</p></li><li><p>Values on these sampled points are not directly retrieved from the original signal, but come from afiltering step that removes high-frequency components.A bad choice of filters can lead to aliasing effects.</p></li></ol><p>Sampling and filtering are both important in basic image processing operations, such as resize.Resize operation takes a discrete image, resamples it, and creates a new image.The choice of sampling grid and sampling filter will then affect how such a basic operation is implemented.</p><p>For example, the paper <a href="https://arxiv.org/abs/2104.11222" aria-label="[2104.11222] On Aliased Resizing and Surprising Subtleties in GAN Evaluation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">On Buggy Resizing Libraries and Surprising Subtleties in FID Calculation</a>studies the filtering issues, and shows that the resize operations in many libraries(OpenCV, PyTorch, TensorFlow) don&apos;t take into account the low-pass filtering. This then leadsto incorrect deep learning evaluation.</p><p>In this article, we ignore the issue of sampling filter, and only study the <strong>coordinates of sampling grid</strong>.We&apos;ll see that this choice is also inconsistent among libraries, and can affect the design and performance of CNN models.</p><h1 id="Choices-of-Sampling-Grid">Choices of Sampling Grid<a class="markdown-anchor" href="#Choices-of-Sampling-Grid">&#xB6;</a></h1><p>Pixels are located on a sampling grid we choose.Naturally, we would like to only consider rectangular grids where pixels are spaced evenly.But there are many other factors to be concerned with:</p><ul><li><strong>Offset</strong>: where is the first pixel located relative to the beginning of the signal?</li><li><strong>Stride</strong>: what&apos;s the distance between two neighboring pixels?</li><li><strong>Resolution</strong>: how many pixels are there?</li></ul><p>(These terminologies may have a different meaning elsewhere, but this is how I define them in this article.)</p><p>For simplicity, we look at the one-dimensional case instead.We want to answer this question: for a 1D signal defined on <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="5.404ex" height="2.262ex" role="img" focusable="false" viewbox="0 -750 2388.7 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"/></g><g data-mml-node="mn" transform="translate(278,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g><g data-mml-node="mo" transform="translate(778,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mi" transform="translate(1222.7,0)"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g><g data-mml-node="mo" transform="translate(2110.7,0)"><path data-c="5D" d="M22 710V750H159V-250H22V-210H119V710H22Z"/></g></g></g></svg></mjx-container>, what is the sampling grid with stride=1?There are a few different choices:</p><img src="/blog/2021/Where-are-Pixels/grid.png" class="center" width="500"><p>In this figure, the green bars represent the 1D signal of length <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: 0;" xmlns="http://www.w3.org/2000/svg" width="2.009ex" height="1.545ex" role="img" focusable="false" viewbox="0 -683 888 683"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g></g></g></svg></mjx-container>, and blue dotsrepresent the locations where point samples are taken.On top of each sample we labeled their <strong>coordinates</strong>,while on the bottom are their zero-based <strong>pixel indices</strong>.More formally, given a stride (which equals to 1 here), the offset and resolution of the grid are defined by the following table (assume <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.798ex;" xmlns="http://www.w3.org/2000/svg" width="5.174ex" height="2.782ex" role="img" focusable="false" viewbox="0 -877 2287 1229.7"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mfrac"><g data-mml-node="mi" transform="translate(829.5,394) scale(0.707)"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g><g data-mml-node="mrow" transform="translate(220,-345) scale(0.707)"><g data-mml-node="mi"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"/></g><g data-mml-node="mi" transform="translate(469,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"/></g><g data-mml-node="mi" transform="translate(830,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1281,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1626,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mi" transform="translate(2146,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/></g></g><rect width="2047" height="60" x="120" y="220"/></g></g></g></svg></mjx-container> is an integer):</p><p><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -6.672ex;" xmlns="http://www.w3.org/2000/svg" width="25.692ex" height="14.475ex" role="img" focusable="false" viewbox="0 -3449 11356 6398"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="menclose" data-padding="0"><g transform="translate(67, 0)"><g data-mml-node="mtable"><g data-mml-node="mtr" transform="translate(0,2483.5)"><g data-mml-node="mtd" transform="translate(400,0)"><g data-mml-node="mtext"><path data-c="67" d="M329 409Q373 453 429 453Q459 453 472 434T485 396Q485 382 476 371T449 360Q416 360 412 390Q410 404 415 411Q415 412 416 414V415Q388 412 363 393Q355 388 355 386Q355 385 359 381T368 369T379 351T388 325T392 292Q392 230 343 187T222 143Q172 143 123 171Q112 153 112 133Q112 98 138 81Q147 75 155 75T227 73Q311 72 335 67Q396 58 431 26Q470 -13 470 -72Q470 -139 392 -175Q332 -206 250 -206Q167 -206 107 -175Q29 -140 29 -75Q29 -39 50 -15T92 18L103 24Q67 55 67 108Q67 155 96 193Q52 237 52 292Q52 355 102 398T223 442Q274 442 318 416L329 409ZM299 343Q294 371 273 387T221 404Q192 404 171 388T145 343Q142 326 142 292Q142 248 149 227T179 192Q196 182 222 182Q244 182 260 189T283 207T294 227T299 242Q302 258 302 292T299 343ZM403 -75Q403 -50 389 -34T348 -11T299 -2T245 0H218Q151 0 138 -6Q118 -15 107 -34T95 -74Q95 -84 101 -97T122 -127T170 -155T250 -167Q319 -167 361 -139T403 -75Z"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(500,0)"/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(892,0)"/><path data-c="64" d="M376 495Q376 511 376 535T377 568Q377 613 367 624T316 637H298V660Q298 683 300 683L310 684Q320 685 339 686T376 688Q393 689 413 690T443 693T454 694H457V390Q457 84 458 81Q461 61 472 55T517 46H535V0Q533 0 459 -5T380 -11H373V44L365 37Q307 -11 235 -11Q158 -11 96 50T34 215Q34 315 97 378T244 442Q319 442 376 393V495ZM373 342Q328 405 260 405Q211 405 173 369Q146 341 139 305T131 211Q131 155 138 120T173 59Q203 26 251 26Q322 26 373 103V342Z" transform="translate(1170,0)"/></g></g><g data-mml-node="mtd" transform="translate(3196,0)"><g data-mml-node="mtext"><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z"/><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z" transform="translate(500,0)"/><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z" transform="translate(806,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(1112,0)"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(1506,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(1950,0)"/></g></g><g data-mml-node="mtd" transform="translate(6535,0)"><g data-mml-node="mtext"><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(392,0)"/><path data-c="73" d="M295 316Q295 356 268 385T190 414Q154 414 128 401Q98 382 98 349Q97 344 98 336T114 312T157 287Q175 282 201 278T245 269T277 256Q294 248 310 236T342 195T359 133Q359 71 321 31T198 -10H190Q138 -10 94 26L86 19L77 10Q71 4 65 -1L54 -11H46H42Q39 -11 33 -5V74V132Q33 153 35 157T45 162H54Q66 162 70 158T75 146T82 119T101 77Q136 26 198 26Q295 26 295 104Q295 133 277 151Q257 175 194 187T111 210Q75 227 54 256T33 318Q33 357 50 384T93 424T143 442T187 447H198Q238 447 268 432L283 424L292 431Q302 440 314 448H322H326Q329 448 335 442V310L329 304H301Q295 310 295 316Z" transform="translate(836,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(1230,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1730,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(2008,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(2564,0)"/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(2953,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(3231,0)"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(3731,0)"/></g></g></g><g data-mml-node="mtr" transform="translate(0,886.5)"><g data-mml-node="mtd" transform="translate(963,0)"><g data-mml-node="mn"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">&#x2460;</text></g></g><g data-mml-node="mtd" transform="translate(4115.5,0)"><g data-mml-node="mn"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g><g data-mml-node="mtd" transform="translate(6673.8,0)"><g data-mml-node="mfrac"><g data-mml-node="mi" transform="translate(829.5,394) scale(0.707)"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g><g data-mml-node="mrow" transform="translate(220,-345) scale(0.707)"><g data-mml-node="mi"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"/></g><g data-mml-node="mi" transform="translate(469,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"/></g><g data-mml-node="mi" transform="translate(830,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1281,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1626,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mi" transform="translate(2146,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/></g></g><rect width="2047" height="60" x="120" y="220"/></g><g data-mml-node="mo" transform="translate(2509.2,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"/></g><g data-mml-node="mn" transform="translate(3509.4,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g></g></g><g data-mml-node="mtr" transform="translate(0,-751)"><g data-mml-node="mtd" transform="translate(963,0)"><g data-mml-node="mn"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">&#x2461;</text></g></g><g data-mml-node="mtd" transform="translate(3222,0)"><g data-mml-node="mfrac"><g data-mml-node="mrow" transform="translate(220,394) scale(0.707)"><g data-mml-node="mi"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"/></g><g data-mml-node="mi" transform="translate(469,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"/></g><g data-mml-node="mi" transform="translate(830,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1281,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1626,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mi" transform="translate(2146,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/></g></g><g data-mml-node="mn" transform="translate(966.7,-345) scale(0.707)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"/></g><rect width="2047" height="60" x="120" y="220"/></g></g><g data-mml-node="mtd" transform="translate(7535,0)"><g data-mml-node="mfrac"><g data-mml-node="mi" transform="translate(829.5,394) scale(0.707)"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g><g data-mml-node="mrow" transform="translate(220,-345) scale(0.707)"><g data-mml-node="mi"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"/></g><g data-mml-node="mi" transform="translate(469,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"/></g><g data-mml-node="mi" transform="translate(830,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1281,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1626,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mi" transform="translate(2146,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/></g></g><rect width="2047" height="60" x="120" y="220"/></g></g></g><g data-mml-node="mtr" transform="translate(0,-2380.7)"><g data-mml-node="mtd" transform="translate(963,0)"><g data-mml-node="mn"><text data-variant="normal" transform="scale(1,-1)" font-size="884px" font-family="serif">&#x2462;</text></g></g><g data-mml-node="mtd" transform="translate(4115.5,0)"><g data-mml-node="mn"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g><g data-mml-node="mtd" transform="translate(7535,0)"><g data-mml-node="mfrac"><g data-mml-node="mi" transform="translate(829.5,394) scale(0.707)"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g><g data-mml-node="mrow" transform="translate(220,-345) scale(0.707)"><g data-mml-node="mi"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"/></g><g data-mml-node="mi" transform="translate(469,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"/></g><g data-mml-node="mi" transform="translate(830,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1281,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1626,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mi" transform="translate(2146,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/></g></g><rect width="2047" height="60" x="120" y="220"/></g></g></g><line data-line="v" class="mjx-solid" x1="2661" y1="-2949" x2="2661" y2="3449"/><line data-line="h" class="mjx-solid" x1="0" y1="1998.5" x2="11222" y2="1998.5"/></g></g><line x1="33.5" y1="-2949" x2="33.5" y2="3449" stroke-width="67"/><line x1="11322.5" y1="-2949" x2="11322.5" y2="3449" stroke-width="67"/></g></g></g></svg></mjx-container></p><p>They (or at least the first two) are all <strong>valid</strong> interpretations when we are given an array of pixels.The interpretation we choose affects how we implement operations and models,because they each have some unique weird properties.To understand them more, let&apos;s check how a 2x resize operation should be implemented under each interpretation.</p><h2 id="2x-Resize-Operation">2x Resize Operation<a class="markdown-anchor" href="#2x-Resize-Operation">&#xB6;</a></h2><p>We&apos;ll now see that a simple &quot;2x resize&quot; operation has many possible implementations.</p><p>A unique undesired property of &#x2460;  is that, stride is not the inverse of resolution.So a 2x resize is ambiguous: we have to be clear about whether we want half of stride, or twice more pixels.The new grids after resize look like these:</p><img src="/blog/2021/Where-are-Pixels/resize1.png" class="center" width="500"><p>Resize for grid &#x2461; &amp; &#x2462;  aren&apos;t ambiguous:</p><img src="/blog/2021/Where-are-Pixels/resize23.png" class="center" width="500"><p>You can easily verify that the 4 different resized grids still match thecorresponding definition in our table above.</p><p>For 2D case, the 2x resize in &#x2460;(twice more pixels) and &#x2461; look liks this (image credit: <a href="https://discuss.pytorch.org/t/what-we-should-use-align-corners-false/22663/8" aria-label="What we should use align_corners = False - #8 by Dhruvrnaik - vision - PyTorch Forums" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>),from which you can see why &#x2460;(twice more pixels) is also called <code>align_corners</code>:</p><img src="/blog/2021/Where-are-Pixels/align_corners.jpg" class="center" width="500"><p>These 4 different versions of 2x resize have some issues:</p><ul><li><p><strong>Extrapolation</strong>: &#x2461; and &#x2462; both need extrapolation outside the border of the original grid to perform resize, but &#x2460; only needs interpolation.Extrapolation is sometimes undesirable.</p></li><li><p><strong>Asymmetry</strong>: &#x2462; is asymmetric, and it&apos;s probably a good reason to never use it. One consequence is that <code>resize(flip(x)) != flip(resize(x))</code>. All others are symmetric.</p></li><li><p><strong>Information Loss</strong>: in &#x2460;(half of stride) and &#x2462; , about half of the points on the new grid exist in the old grid.By not having to interpolate their values, we minimize the loss of information.However, in &#x2460;(twice more pixels) and &#x2461;, most or all of the new pixels need to be recomputed.</p><p>For resize with other arbitrary scale factors, all versions have information loss. But 2x/0.5x resize aremost common in deep learning.</p></li></ul><p>The DeepLab series of segmentation models are famous for using grid &#x2460;(half of stride) for all the 2x resize.See <a href="https://github.com/tensorflow/tensorflow/issues/6720#issuecomment-298190596" aria-label="tf.image.resize_images() - weird padding behaviour?  &#xB7; Issue #6720 &#xB7; tensorflow/tensorflow" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a> for words from its author.This matches the inconvenient image shapes they use, such as 321x513.I&apos;ve heard opinions that the benefits of &quot;no information loss&quot; and &quot;no extrapolation&quot; may let itoutperform &#x2461; in segmentation, but I have yet to see more evidence.</p><h2 id="Libraries">Libraries<a class="markdown-anchor" href="#Libraries">&#xB6;</a></h2><p>What do libraries use? Situation is a bit messy. I&apos;ll list what I know and look forward to your help to add more.No guarantee they are all correct, since I didn&apos;t check the source code for all of them.</p><center><table><thead><tr><th>Library &amp; Operation</th><th>Pixel Grid Convention</th></tr></thead><tbody><tr><td>OpenCV <br> <code>cv2.resize</code></td><td><code>interpolation=LINEAR/CUBIC</code>: &#x2461; <br> <code>interpolation=NEAREST</code>: buggy, none of the above. <a href="https://github.com/opencv/opencv/issues/9096" aria-label="Nearest neighbor interpolation does not give expected results &#xB7; Issue #9096 &#xB7; opencv/opencv" class="hint--top hint--rounded hint--no-animate hint--no-arrow">issue</a> <br> <code>interpolation=NEAREST_EXACT</code>: &#x2461;</td></tr><tr><td>Pillow <br> <code>Image.resize</code></td><td>&#x2461;</td></tr><tr><td>scikit-image <br> <code>transform.resize</code></td><td>&#x2461;</td></tr><tr><td>PyTorch <br> <code>F.interpolate</code></td><td><code>mode=linear/cubic, align_corners=False</code>: &#x2461;  <br> <code>mode=linear/cubic, align_corners=True</code>: &#x2460; <br><code>mode=nearest</code>: buggy like OpenCV. <a href="https://github.com/pytorch/pytorch/issues/34808" aria-label="Using torch.nn.functional.interpolate with &apos;nearest&apos; mode introduces misalignment &#xB7; Issue #34808 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">issue</a> <br><code>mode=nearest_exact</code>: &#x2461;</td></tr><tr><td>PyTorch <br> <code>F.grid_sample</code></td><td><code>align_corners=False</code> which <a href="https://github.com/pytorch/pytorch/issues/20785" aria-label="grid_sample is not aligned &#xB7; Issue #20785 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">I requested</a>: &#x2461;  <br> <code>align_corners=True</code>: &#x2460; <br></td></tr><tr><td>TensorFlow <br> <code>tf.image.resize</code></td><td>TFv1 <code>method=BILINEAR/NEAREST, align_corners=False</code>: &#x2462;  <br> TFv1 <code>method=BILINEAR/NEAREST, align_corners=True</code>: &#x2460; <br> TFv2 <code>method=BILINEAR/NEAREST</code>: &#x2461; <br> (In TFv2, <code>align_corners</code> option was removed) <br></td></tr><tr><td>TensorFlow <br> <code>tf.image.crop_and_resize</code></td><td>none of the above. <a href="https://github.com/tensorflow/tensorflow/issues/26278" aria-label="tf.image.crop_and_resize() - weird alignment behavior? &#xB7; Issue #26278 &#xB7; tensorflow/tensorflow" class="hint--top hint--rounded hint--no-animate hint--no-arrow">issue I reported</a></td></tr></tbody></table></center><!--   -* For many shapes, nearest resize produces the same outputs regardless of the 3 choices. An easy way to tell the difference is to resize `arr=range(5)` into 2 pixels. ① produces`[0,4]`; ② produces`[1,3]`, ③ produces`[0,2]`.   --><p>It seems the mess is unique in the deep learning world. How come?From what I can tell, the history looks like this:</p><ul><li><p>TensorFlow is the first place that introduces &#x2462;, in its initial open source.This was later considered as <a href="https://github.com/tensorflow/tensorflow/issues/6720" aria-label="tf.image.resize_images() - weird padding behaviour?  &#xB7; Issue #6720 &#xB7; tensorflow/tensorflow" class="hint--top hint--rounded hint--no-animate hint--no-arrow">a bug</a> and fixedin v1.14 using a <a href="https://github.com/tensorflow/tensorflow/commit/3ae2c6691b7c6e0986d97b150c9283e5cc52c15f" aria-label="Adding &apos;half_pixel_centers&apos; bool attribute (default False) to resize &#x2026; &#xB7; tensorflow/tensorflow@3ae2c66" class="hint--top hint--rounded hint--no-animate hint--no-arrow">new option</a>named <code>half_pixel_centers=True</code> that follows grid &#x2461;.</p><p><code>align_corners=True</code>(&#x2460;) <a href="https://github.com/tensorflow/tensorflow/commit/4f31fb0dc02c3e9c2ac58e79abec57a7a544eef4" aria-label="Change: 111565458 &#xB7; tensorflow/tensorflow@4f31fb0" class="hint--top hint--rounded hint--no-animate hint--no-arrow">appeared in TensorFlow 0.7</a> in 2016.I guess this was probably intended for DeepLab development and not for general use.</p><p>In TensorFlow v2, grid &#x2461; becomes the only version of resize, but it was too late.During all these years, the uncommon version (&#x2460;) and the wrong version (&#x2462;) have propagated to people&apos;smodels and other libraries.</p></li><li><p>PyTorch&apos;s <code>interpolate</code> comes originally from <code>upsample</code> operation. Nearest upsample was buggy when it&apos;s first<a href="https://github.com/torch/nn/pull/21" aria-label="Added SpatialUpSamplingNearest module. by jonathantompson &#xB7; Pull Request #21 &#xB7; torch/nn" class="hint--top hint--rounded hint--no-animate hint--no-arrow">added in LuaTorch</a> in 2014.Bilinear upsample was first <a href="https://github.com/torch/nn/pull/882/" aria-label="Adding SpatialUpSamplingBilinear by paulineluc &#xB7; Pull Request #882 &#xB7; torch/nn" class="hint--top hint--rounded hint--no-animate hint--no-arrow">added in LuaTorch</a> in 2016 andused grid &#x2460;. Grid &#x2461; was <a href="https://github.com/pytorch/pytorch/pull/5927" aria-label="Linearly interpolating upsampling fix by SsnL &#xB7; Pull Request #5927 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">added in 2018</a> to PyTorch under an <code>align_corners=False</code> option,and became the default since then.</p></li><li><p>Due to this mess, resize operator in ONNX has to support<a href="https://github.com/onnx/onnx/blob/master/docs/Operators.md#resize" aria-label="Operators.md &#xB7; onnx/onnx" class="hint--top hint--rounded hint--no-animate hint--no-arrow">5 versions of coordinate transform</a>!Kudos to ONNX maintainers.</p></li></ul><h2 id="Literature">Literature<a class="markdown-anchor" href="#Literature">&#xB6;</a></h2><p>Many computer graphics textbooks and papers talk about this topic and choose &#x2461;, for example:</p><ul><li>Sec. 3.2, &quot;Images, Pixels and Geometry&quot; of <a href="https://www.google.com/books/edition/_/hJPRgRrkXvUC?hl=en&amp;gbpv=0" aria-label="Fundamentals of Computer Graphics - Peter Shirley, Michael Ashikhmin, Steve Marschner - Google Books" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Fundamentals of Computer Graphics</a></li><li>Sec. 7.1.7, &quot;Understanding Pixels&quot; of <a href="https://www.pbr-book.org/3ed-2018/Sampling_and_Reconstruction/Sampling_Theory" aria-label="Sampling Theory" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Physically Based Rendering</a></li><li>&quot;What Are the Coordinates of a Pixel?&quot; from <a href="https://www.google.com/books/edition/_/fvA7zLEFWZgC?hl=en" aria-label="Graphics Gems - Google Books" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Graphics Gems</a>. Explained again in <a href="http://www.realtimerendering.com/blog/the-center-of-the-pixel-is-0-50-5/" aria-label="Real-Time Rendering &#xB7; The Center of the Pixel is (0.5,0.5)" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Real-time Rendering</a>.</li><li>A well-known tech memo, <a href="http://alvyray.com/Memos/CG/Microsoft/6_pixel.pdf">A Pixel is Not a Little Square</a></li></ul><p>(Note that some of them uses &#x2461;  but defines the continuous signal in the range <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.781ex;" xmlns="http://www.w3.org/2000/svg" width="19.147ex" height="2.782ex" role="img" focusable="false" viewbox="0 -884.7 8463 1229.7"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"/></g><g data-mml-node="mo" transform="translate(278,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mfrac" transform="translate(1056,0)"><g data-mml-node="mrow" transform="translate(220,394) scale(0.707)"><g data-mml-node="mi"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"/></g><g data-mml-node="mi" transform="translate(469,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"/></g><g data-mml-node="mi" transform="translate(830,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1281,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1626,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mi" transform="translate(2146,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/></g></g><g data-mml-node="mn" transform="translate(966.7,-345) scale(0.707)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"/></g><rect width="2047" height="60" x="120" y="220"/></g><g data-mml-node="mo" transform="translate(3343,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mi" transform="translate(3787.6,0)"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g><g data-mml-node="mo" transform="translate(4897.9,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mfrac" transform="translate(5898.1,0)"><g data-mml-node="mrow" transform="translate(220,394) scale(0.707)"><g data-mml-node="mi"><path data-c="1D460" d="M131 289Q131 321 147 354T203 415T300 442Q362 442 390 415T419 355Q419 323 402 308T364 292Q351 292 340 300T328 326Q328 342 337 354T354 372T367 378Q368 378 368 379Q368 382 361 388T336 399T297 405Q249 405 227 379T204 326Q204 301 223 291T278 274T330 259Q396 230 396 163Q396 135 385 107T352 51T289 7T195 -10Q118 -10 86 19T53 87Q53 126 74 143T118 160Q133 160 146 151T160 120Q160 94 142 76T111 58Q109 57 108 57T107 55Q108 52 115 47T146 34T201 27Q237 27 263 38T301 66T318 97T323 122Q323 150 302 164T254 181T195 196T148 231Q131 256 131 289Z"/></g><g data-mml-node="mi" transform="translate(469,0)"><path data-c="1D461" d="M26 385Q19 392 19 395Q19 399 22 411T27 425Q29 430 36 430T87 431H140L159 511Q162 522 166 540T173 566T179 586T187 603T197 615T211 624T229 626Q247 625 254 615T261 596Q261 589 252 549T232 470L222 433Q222 431 272 431H323Q330 424 330 420Q330 398 317 385H210L174 240Q135 80 135 68Q135 26 162 26Q197 26 230 60T283 144Q285 150 288 151T303 153H307Q322 153 322 145Q322 142 319 133Q314 117 301 95T267 48T216 6T155 -11Q125 -11 98 4T59 56Q57 64 57 83V101L92 241Q127 382 128 383Q128 385 77 385H26Z"/></g><g data-mml-node="mi" transform="translate(830,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1281,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1626,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mi" transform="translate(2146,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/></g></g><g data-mml-node="mn" transform="translate(966.7,-345) scale(0.707)"><path data-c="32" d="M109 429Q82 429 66 447T50 491Q50 562 103 614T235 666Q326 666 387 610T449 465Q449 422 429 383T381 315T301 241Q265 210 201 149L142 93L218 92Q375 92 385 97Q392 99 409 186V189H449V186Q448 183 436 95T421 3V0H50V19V31Q50 38 56 46T86 81Q115 113 136 137Q145 147 170 174T204 211T233 244T261 278T284 308T305 340T320 369T333 401T340 431T343 464Q343 527 309 573T212 619Q179 619 154 602T119 569T109 550Q109 549 114 549Q132 549 151 535T170 489Q170 464 154 447T109 429Z"/></g><rect width="2047" height="60" x="120" y="220"/></g><g data-mml-node="mo" transform="translate(8185,0)"><path data-c="5D" d="M22 710V750H159V-250H22V-210H119V710H22Z"/></g></g></g></svg></mjx-container>.We&apos;ll discuss this more.)</p><p>Given all the graphics literature, computer vision and deep learning libraries promoting grid &#x2461;, we <strong>use &#x2461;  as the convention</strong>.</p><h1 id="Choices-of-Origin">Choices of Origin<a class="markdown-anchor" href="#Choices-of-Origin">&#xB6;</a></h1><p>We pick &#x2461; as the convention for grid locations, but this is not the end of the story!We now know the grid locations <strong>relative</strong> to the beginning of the signal are 0.5, 1.5, <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: 0.43ex;" xmlns="http://www.w3.org/2000/svg" width="2.652ex" height="0.271ex" role="img" focusable="false" viewbox="0 -310 1172 120"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="22EF" d="M78 250Q78 274 95 292T138 310Q162 310 180 294T199 251Q199 226 182 208T139 190T96 207T78 250ZM525 250Q525 274 542 292T585 310Q609 310 627 294T646 251Q646 226 629 208T586 190T543 207T525 250ZM972 250Q972 274 989 292T1032 310Q1056 310 1074 294T1093 251Q1093 226 1076 208T1033 190T990 207T972 250Z"/></g></g></g></svg></mjx-container>, but what are their <strong>absolute</strong> coordinates?In other words, where is the origin (0, 0) ?</p><p>This is just a choice of convention and has no substantial effect on any algorithms.Two of the graphics literature I listed above put the origin on the first pixel.This has the benefit that all pixel locations have integer coordinates, but then it&apos;s weird that the signal lies oninterval <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="14.582ex" height="2.262ex" role="img" focusable="false" viewbox="0 -750 6445.1 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"/></g><g data-mml-node="mo" transform="translate(278,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(1056,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(2334,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mi" transform="translate(2778.7,0)"><path data-c="1D441" d="M234 637Q231 637 226 637Q201 637 196 638T191 649Q191 676 202 682Q204 683 299 683Q376 683 387 683T401 677Q612 181 616 168L670 381Q723 592 723 606Q723 633 659 637Q635 637 635 648Q635 650 637 660Q641 676 643 679T653 683Q656 683 684 682T767 680Q817 680 843 681T873 682Q888 682 888 672Q888 650 880 642Q878 637 858 637Q787 633 769 597L620 7Q618 0 599 0Q585 0 582 2Q579 5 453 305L326 604L261 344Q196 88 196 79Q201 46 268 46H278Q284 41 284 38T282 19Q278 6 272 0H259Q228 2 151 2Q123 2 100 2T63 2T46 1Q31 1 31 10Q31 14 34 26T39 40Q41 46 62 46Q130 49 150 85Q154 91 221 362L289 634Q287 635 234 637Z"/></g><g data-mml-node="mo" transform="translate(3888.9,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(4889.1,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(6167.1,0)"><path data-c="5D" d="M22 710V750H159V-250H22V-210H119V710H22Z"/></g></g></g></svg></mjx-container>.This convention is sometimes referred to as <strong>&quot;integer centers&quot;</strong>.</p><p>Another convention, <strong>&quot;integer corners&quot;</strong>, or &quot;half-integer centers&quot;, puts the origin at the beginning of the signal, so the first pixel is centered at (0.5, 0.5).The two conventions are demonstrated in this figure:</p><img src="/blog/2021/Where-are-Pixels/origin.png" class="center" width="600"><p><strong>We choose &quot;integer corners&quot;</strong>, and then willhave the following relationship between continuous coordinates and discrete pixel indices:</p><p><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -2.149ex;" xmlns="http://www.w3.org/2000/svg" width="20.282ex" height="5.43ex" role="img" focusable="false" viewbox="0 -1450 8964.6 2400"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtable"><g data-mml-node="mtr" transform="translate(0,700)"><g data-mml-node="mtd" transform="translate(126.8,0)"><g data-mml-node="mi"><path data-c="1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"/></g><g data-mml-node="mi" transform="translate(433,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"/></g><g data-mml-node="mi" transform="translate(918,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"/></g><g data-mml-node="mi" transform="translate(1403,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(1854,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mo" transform="translate(2651.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"/></g><g data-mml-node="mi" transform="translate(3707.6,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(4052.6,0)"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(4652.6,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mi" transform="translate(5172.6,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/></g><g data-mml-node="mi" transform="translate(5638.6,0)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mo" transform="translate(6432.8,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"/></g><g data-mml-node="mn" transform="translate(7433,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g></g></g><g data-mml-node="mtr" transform="translate(0,-700)"><g data-mml-node="mtd"><g data-mml-node="mi"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(345,0)"><path data-c="1D45B" d="M21 287Q22 293 24 303T36 341T56 388T89 425T135 442Q171 442 195 424T225 390T231 369Q231 367 232 367L243 378Q304 442 382 442Q436 442 469 415T503 336T465 179T427 52Q427 26 444 26Q450 26 453 27Q482 32 505 65T540 145Q542 153 560 153Q580 153 580 145Q580 144 576 130Q568 101 554 73T508 17T439 -10Q392 -10 371 17T350 73Q350 92 386 193T423 345Q423 404 379 404H374Q288 404 229 303L222 291L189 157Q156 26 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 112 180T152 343Q153 348 153 366Q153 405 129 405Q91 405 66 305Q60 285 60 284Q58 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(945,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mi" transform="translate(1465,0)"><path data-c="1D452" d="M39 168Q39 225 58 272T107 350T174 402T244 433T307 442H310Q355 442 388 420T421 355Q421 265 310 237Q261 224 176 223Q139 223 138 221Q138 219 132 186T125 128Q125 81 146 54T209 26T302 45T394 111Q403 121 406 121Q410 121 419 112T429 98T420 82T390 55T344 24T281 -1T205 -11Q126 -11 83 42T39 168ZM373 353Q367 405 305 405Q272 405 244 391T199 357T170 316T154 280T149 261Q149 260 169 260Q282 260 327 284T373 353Z"/></g><g data-mml-node="mi" transform="translate(1931,0)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mo" transform="translate(2780.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"/></g><g data-mml-node="mtext" transform="translate(3836.6,0)"><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(306,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(584,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(1084,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(1584,0)"/></g><g data-mml-node="mo" transform="translate(5812.6,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(6201.6,0)"><path data-c="1D450" d="M34 159Q34 268 120 355T306 442Q362 442 394 418T427 355Q427 326 408 306T360 285Q341 285 330 295T319 325T330 359T352 380T366 386H367Q367 388 361 392T340 400T306 404Q276 404 249 390Q228 381 206 359Q162 315 142 235T121 119Q121 73 147 50Q169 26 205 26H209Q321 26 394 111Q403 121 406 121Q410 121 419 112T429 98T420 83T391 55T346 25T282 0T202 -11Q127 -11 81 37T34 159Z"/></g><g data-mml-node="mi" transform="translate(6634.6,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"/></g><g data-mml-node="mi" transform="translate(7119.6,0)"><path data-c="1D45C" d="M201 -11Q126 -11 80 38T34 156Q34 221 64 279T146 380Q222 441 301 441Q333 441 341 440Q354 437 367 433T402 417T438 387T464 338T476 268Q476 161 390 75T201 -11ZM121 120Q121 70 147 48T206 26Q250 26 289 58T351 142Q360 163 374 216T388 308Q388 352 370 375Q346 405 306 405Q243 405 195 347Q158 303 140 230T121 120Z"/></g><g data-mml-node="mi" transform="translate(7604.6,0)"><path data-c="1D45F" d="M21 287Q22 290 23 295T28 317T38 348T53 381T73 411T99 433T132 442Q161 442 183 430T214 408T225 388Q227 382 228 382T236 389Q284 441 347 441H350Q398 441 422 400Q430 381 430 363Q430 333 417 315T391 292T366 288Q346 288 334 299T322 328Q322 376 378 392Q356 405 342 405Q286 405 239 331Q229 315 224 298T190 165Q156 25 151 16Q138 -11 108 -11Q95 -11 87 -5T76 7T74 17Q74 30 114 189T154 366Q154 405 128 405Q107 405 92 377T68 316T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mi" transform="translate(8055.6,0)"><path data-c="1D451" d="M366 683Q367 683 438 688T511 694Q523 694 523 686Q523 679 450 384T375 83T374 68Q374 26 402 26Q411 27 422 35Q443 55 463 131Q469 151 473 152Q475 153 483 153H487H491Q506 153 506 145Q506 140 503 129Q490 79 473 48T445 8T417 -8Q409 -10 393 -10Q359 -10 336 5T306 36L300 51Q299 52 296 50Q294 48 292 46Q233 -10 172 -10Q117 -10 75 30T33 157Q33 205 53 255T101 341Q148 398 195 420T280 442Q336 442 364 400Q369 394 369 396Q370 400 396 505T424 616Q424 629 417 632T378 637H357Q351 643 351 645T353 664Q358 683 366 683ZM352 326Q329 405 277 405Q242 405 210 374T160 293Q131 214 119 129Q119 126 119 118T118 106Q118 61 136 44T179 26Q233 26 290 98L298 109L352 326Z"/></g><g data-mml-node="mo" transform="translate(8575.6,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g></g></g></g></g></g></svg></mjx-container></p><p>The choice doesn&apos;t matter for resize because absolute coordinates are not part of its API.However, for functions that accept or return absolute coordinates, we should be aware of their convention. For example:</p><ul><li><code>cv2.findContours</code> returns integer polygons represented by indices. So we always add 0.5 pixel to itsresults to obtain coordinates that match our convention.</li><li><code>cv2.warpAffine</code> uses &quot;integer centers&quot; and this is complained about in <a href="https://github.com/opencv/opencv/issues/11784" aria-label="warpAffine: correct coordinate system, documentation and incorrect usage &#xB7; Issue #11784 &#xB7; opencv/opencv" class="hint--top hint--rounded hint--no-animate hint--no-arrow">this issue</a>.In fact most OpenCV functions use the &quot;integer centers&quot; convention.</li><li><code>pycocotools.mask.frPyObjects</code> renders polygons as masks. It accepts polygons in &quot;integer corners&quot;convention.Same for <code>PIL.ImageDraw.polygon</code>, but its results are 0.5 pixel &quot;fatter&quot; due to howits implemented. This has affected cityscapes annotations.</li><li><code>RoIAlign</code> in torchvision takes a box in absolute coordinates that match our &quot;integer corners&quot; convention.</li><li><code>scipy.ndimage.map_coordinates</code> takes coordinates in &quot;integer centers&quot; convention.</li></ul><p>If a dataset is annotated with coordinates, we also need to know its choice of coordinate system. Thisinformation is often not provided by dataset owner, so we make guesses. For example, in COCO itappears that polygon annotations match our convention, butkeypoint annotations do not and should be incremented by 0.5.</p><p>Now that we have a convention for the coordinate system, it&apos;s a good practice in computer vision systems to<strong>always use coordinates rather than indices to represent geometries</strong>, such as boxes and polygons. This is because indices are integers, andcan easily lose precision during geometric operations.Using indices for bounding boxes has caused some issues in Detectron.</p><h1 id="Improvements-in-Detectron-Detectron2">Improvements in Detectron &amp; Detectron2<a class="markdown-anchor" href="#Improvements-in-Detectron-Detectron2">&#xB6;</a></h1><p>Models in <a href="https://github.com/facebookresearch/Detectron" aria-label="facebookresearch/Detectron: FAIR&apos;s research platform for object detection research, implementing popular algorithms like Mask R-CNN and RetinaNet." class="hint--top hint--rounded hint--no-animate hint--no-arrow">Detectron</a> / <a href="https://github.com/facebookresearch/detectron2" aria-label="facebookresearch/detectron2: Detectron2 is a platform for object detection, segmentation and other visual recognition tasks." class="hint--top hint--rounded hint--no-animate hint--no-arrow">Detectron2</a>all involve localization of objects in images, so the convention of pixels and coordinates matters a lot.Various improvements and bugfixes in the two libraries are related to pixels.</p><h2 id="Box-Regression-Transform">Box Regression Transform<a class="markdown-anchor" href="#Box-Regression-Transform">&#xB6;</a></h2><p>In detection models, bounding box regression typically predicts &quot;deltas&quot; between the ground truth (GT) box and a reference box (e.g. anchor).In training, GT box is encoded to deltas as training target. In inference, the predicted deltas are decoded to become output boxes.</p><p>Boxes in Detectron often use integer indices, instead of coordinates. So the width of abox is given by <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.375ex;" xmlns="http://www.w3.org/2000/svg" width="11.226ex" height="1.881ex" role="img" focusable="false" viewbox="0 -666 4962 831.6"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mn" transform="translate(605,-150) scale(0.707)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g></g><g data-mml-node="mo" transform="translate(1230.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="msub" transform="translate(2231,0)"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mn" transform="translate(605,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g><g data-mml-node="mo" transform="translate(3461.8,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"/></g><g data-mml-node="mn" transform="translate(4462,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g></g></g></svg></mjx-container> instead of <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.375ex;" xmlns="http://www.w3.org/2000/svg" width="7.329ex" height="1.694ex" role="img" focusable="false" viewbox="0 -583 3239.6 748.6"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mn" transform="translate(605,-150) scale(0.707)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g></g><g data-mml-node="mo" transform="translate(1230.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="msub" transform="translate(2231,0)"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mn" transform="translate(605,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g></g></g></svg></mjx-container>. Its box transform code looked like this for a long time (showing only one dimension for brevity):</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python">ref_x0, ref_x1: <span class="hljs-built_in">int</span>                     <span class="hljs-comment"># the reference box</span><br>ref_w = ref_x1 - ref_x0 + <span class="hljs-number">1</span><br>ref_center = ref_x0 + <span class="hljs-number">0.5</span> * ref_w<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">encode</span>(<span class="hljs-params">x0, x1</span>):                     <span class="hljs-comment"># given reference box and gt box</span><br>  w = x1 - x0 + <span class="hljs-number">1</span><br>  center = x0 + <span class="hljs-number">0.5</span> * w<br>  dx = (center - ref_center) / ref_w    <span class="hljs-comment"># delta between centers</span><br>  dw = log(w / ref_w)                   <span class="hljs-comment"># delta between widths</span><br>  <span class="hljs-keyword">return</span> dx, dw<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">decode</span>(<span class="hljs-params">dx, dw</span>):                     <span class="hljs-comment"># given reference box and deltas</span><br>  center = dx * ref_w + ref_center      <span class="hljs-comment"># undo the encoding</span><br>  w = exp(dw) * ref_w<br>  x0, x1 = center - <span class="hljs-number">0.5</span> * w, center + <span class="hljs-number">0.5</span> * w<br>  <span class="hljs-keyword">return</span> x0, x1<br></code></pre></td></tr></table></figure><p>As innocent as the code seems, the two functions are not inverse of each other: <code>decode(encode(x0, x1)) != (x0, x1)</code>.<code>x1</code> is incorrectly decoded: it should be <code>center + 0.5 * w - 1</code> instead.</p><p>This bug appeared in the <code>py-faster-rcnn</code> project around 2015, and is <a href="https://github.com/rbgirshick/py-faster-rcnn/blob/781a917b378dbfdedb45b6a56189a31982da1b43/lib/fast_rcnn/bbox_transform.py#L10-L61" aria-label="bbox_transform.py &#xB7; rbgirshick/py-faster-rcnn" class="hint--top hint--rounded hint--no-animate hint--no-arrow">still there</a>today.It was carried into Detectron and negatively affected results in the Mask R-CNN paper.Then it&apos;s <a href="https://github.com/facebookresearch/Detectron/blob/1809dd41c1ffc881c0d6b1c16ea38d08894f8b6d/detectron/utils/boxes.py#L185-L188" aria-label="boxes.py &#xB7; facebookresearch/Detectron" class="hint--top hint--rounded hint--no-animate hint--no-arrow">fixed in late 2017</a>after I found it, and contributed to an improvement of 0.4~0.7 box AP.Detectron went open source in 2018 with this fix.In Detectron2, we adopt the rule to always use floating-point coordinates for boxes, so the issue nolonger exists.</p><!-- -D6236781 --><h2 id="Flip-Augmentation">Flip Augmentation<a class="markdown-anchor" href="#Flip-Augmentation">&#xB6;</a></h2><p>How to horizontally flip a geometry? Although pixel indices should be flipped by  <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.186ex;" xmlns="http://www.w3.org/2000/svg" width="14.114ex" height="1.731ex" role="img" focusable="false" viewbox="0 -683 6238.4 765"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mo" transform="translate(622.8,0)"><path data-c="2190" d="M944 261T944 250T929 230H165Q167 228 182 216T211 189T244 152T277 96T303 25Q308 7 308 0Q308 -11 288 -11Q281 -11 278 -11T272 -7T267 2T263 21Q245 94 195 151T73 236Q58 242 55 247Q55 254 59 257T73 264Q121 283 158 314T215 375T247 434T264 480L267 497Q269 503 270 505T275 509T288 511Q308 511 308 500Q308 493 303 475Q293 438 278 406T246 352T215 315T185 287T165 270H929Q944 261 944 250Z"/></g><g data-mml-node="mi" transform="translate(1900.6,0)"><path data-c="1D44A" d="M436 683Q450 683 486 682T553 680Q604 680 638 681T677 682Q695 682 695 674Q695 670 692 659Q687 641 683 639T661 637Q636 636 621 632T600 624T597 615Q597 603 613 377T629 138L631 141Q633 144 637 151T649 170T666 200T690 241T720 295T759 362Q863 546 877 572T892 604Q892 619 873 628T831 637Q817 637 817 647Q817 650 819 660Q823 676 825 679T839 682Q842 682 856 682T895 682T949 681Q1015 681 1034 683Q1048 683 1048 672Q1048 666 1045 655T1038 640T1028 637Q1006 637 988 631T958 617T939 600T927 584L923 578L754 282Q586 -14 585 -15Q579 -22 561 -22Q546 -22 542 -17Q539 -14 523 229T506 480L494 462Q472 425 366 239Q222 -13 220 -15T215 -19Q210 -22 197 -22Q178 -22 176 -15Q176 -12 154 304T131 622Q129 631 121 633T82 637H58Q51 644 51 648Q52 671 64 683H76Q118 680 176 680Q301 680 313 683H323Q329 677 329 674T327 656Q322 641 318 637H297Q236 634 232 620Q262 160 266 136L501 550L499 587Q496 629 489 632Q483 636 447 637Q428 637 422 639T416 648Q416 650 418 660Q419 664 420 669T421 676T424 680T428 682T436 683Z"/></g><g data-mml-node="mo" transform="translate(3170.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(4171,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g><g data-mml-node="mo" transform="translate(4893.2,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mi" transform="translate(5893.4,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g></g></g></svg></mjx-container>,we should follow the rule to always use coordinates, and coordinates should be flipped by <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.186ex;" xmlns="http://www.w3.org/2000/svg" width="11.244ex" height="1.731ex" role="img" focusable="false" viewbox="0 -683 4970 765"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mo" transform="translate(849.8,0)"><path data-c="2190" d="M944 261T944 250T929 230H165Q167 228 182 216T211 189T244 152T277 96T303 25Q308 7 308 0Q308 -11 288 -11Q281 -11 278 -11T272 -7T267 2T263 21Q245 94 195 151T73 236Q58 242 55 247Q55 254 59 257T73 264Q121 283 158 314T215 375T247 434T264 480L267 497Q269 503 270 505T275 509T288 511Q308 511 308 500Q308 493 303 475Q293 438 278 406T246 352T215 315T185 287T165 270H929Q944 261 944 250Z"/></g><g data-mml-node="mi" transform="translate(2127.6,0)"><path data-c="1D44A" d="M436 683Q450 683 486 682T553 680Q604 680 638 681T677 682Q695 682 695 674Q695 670 692 659Q687 641 683 639T661 637Q636 636 621 632T600 624T597 615Q597 603 613 377T629 138L631 141Q633 144 637 151T649 170T666 200T690 241T720 295T759 362Q863 546 877 572T892 604Q892 619 873 628T831 637Q817 637 817 647Q817 650 819 660Q823 676 825 679T839 682Q842 682 856 682T895 682T949 681Q1015 681 1034 683Q1048 683 1048 672Q1048 666 1045 655T1038 640T1028 637Q1006 637 988 631T958 617T939 600T927 584L923 578L754 282Q586 -14 585 -15Q579 -22 561 -22Q546 -22 542 -17Q539 -14 523 229T506 480L494 462Q472 425 366 239Q222 -13 220 -15T215 -19Q210 -22 197 -22Q178 -22 176 -15Q176 -12 154 304T131 622Q129 631 121 633T82 637H58Q51 644 51 648Q52 671 64 683H76Q118 680 176 680Q301 680 313 683H323Q329 677 329 674T327 656Q322 641 318 637H297Q236 634 232 620Q262 160 266 136L501 550L499 587Q496 629 489 632Q483 636 447 637Q428 637 422 639T416 648Q416 650 418 660Q419 664 420 669T421 676T424 680T428 682T436 683Z"/></g><g data-mml-node="mo" transform="translate(3397.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mi" transform="translate(4398,0)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g></g></g></svg></mjx-container>under &quot;integer corner&quot; system.</p><p>Detectron isn&apos;t so rigorous on this and it <a href="https://github.com/facebookresearch/Detectron/blob/1809dd41c1ffc881c0d6b1c16ea38d08894f8b6d/detectron/utils/segms.py#L49-L52" aria-label="segms.py &#xB7; facebookresearch/Detectron" class="hint--top hint--rounded hint--no-animate hint--no-arrow">uses</a><mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.186ex;" xmlns="http://www.w3.org/2000/svg" width="10.328ex" height="1.731ex" role="img" focusable="false" viewbox="0 -683 4564.9 765"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mi"><path data-c="1D44A" d="M436 683Q450 683 486 682T553 680Q604 680 638 681T677 682Q695 682 695 674Q695 670 692 659Q687 641 683 639T661 637Q636 636 621 632T600 624T597 615Q597 603 613 377T629 138L631 141Q633 144 637 151T649 170T666 200T690 241T720 295T759 362Q863 546 877 572T892 604Q892 619 873 628T831 637Q817 637 817 647Q817 650 819 660Q823 676 825 679T839 682Q842 682 856 682T895 682T949 681Q1015 681 1034 683Q1048 683 1048 672Q1048 666 1045 655T1038 640T1028 637Q1006 637 988 631T958 617T939 600T927 584L923 578L754 282Q586 -14 585 -15Q579 -22 561 -22Q546 -22 542 -17Q539 -14 523 229T506 480L494 462Q472 425 366 239Q222 -13 220 -15T215 -19Q210 -22 197 -22Q178 -22 176 -15Q176 -12 154 304T131 622Q129 631 121 633T82 637H58Q51 644 51 648Q52 671 64 683H76Q118 680 176 680Q301 680 313 683H323Q329 677 329 674T327 656Q322 641 318 637H297Q236 634 232 620Q262 160 266 136L501 550L499 587Q496 629 489 632Q483 636 447 637Q428 637 422 639T416 648Q416 650 418 660Q419 664 420 669T421 676T424 680T428 682T436 683Z"/></g><g data-mml-node="mo" transform="translate(1270.2,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(2270.4,0)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g><g data-mml-node="mo" transform="translate(2992.7,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mi" transform="translate(3992.9,0)"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g></g></g></svg></mjx-container> for coordinates. IIRC, fixing the issue leads to ~0.5 mask AP improvement.</p><p>The augmentation library &quot;imgaug&quot; also <a href="https://github.com/aleju/imgaug/pull/307" aria-label="Fix FlipLR augmentation with floating keypoints by Erotemic &#xB7; Pull Request #307 &#xB7; aleju/imgaug" class="hint--top hint--rounded hint--no-animate hint--no-arrow">made this fix</a>.</p><h2 id="Delay-Conversion-to-Mask-Representation">Delay Conversion to Mask Representation<a class="markdown-anchor" href="#Delay-Conversion-to-Mask-Representation">&#xB6;</a></h2><p>COCO&apos;s instance segmentation data is annotated with polygons that have sub-pixel precision.Converting polygons to binary masks loses the precision due to quantization,and the lost might become more severe during augmentations.Therefore it&apos;s preferrable to keep the polygon representation and delay the conversion as much as possible.</p><p>In both Detectron and Detectron2, polygon representation are kept during flipping, scaling, and RoI cropping.Masks are not created until the second stage&apos;s box predictions are made, where the boxes are used to crop the groundtruth polygonsand generate the mask training target.</p><p>On the contrary, in TensorFlow&apos;s detection code <a href="https://github.com/tensorflow/models/blob/0119344c829b483e5470948db5c2b2ab594955d2/research/object_detection/dataset_tools/create_coco_tf_record.py#L228-L237" aria-label="create_coco_tf_record.py &#xB7; tensorflow/models" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>and <a href="https://github.com/tensorflow/tpu/blob/641c1ac6e26ed788327b973582cbfa297d7d31e7/tools/datasets/create_coco_tf_record.py#L164-L173" aria-label="create_coco_tf_record.py &#xB7; tensorflow/tpu" class="hint--top hint--rounded hint--no-animate hint--no-arrow">here</a>polygons are turned to binary masks immediately at dataset creation time.</p><h2 id="Anchor-Generation">Anchor Generation<a class="markdown-anchor" href="#Anchor-Generation">&#xB6;</a></h2><p>The <a href="https://github.com/facebookresearch/Detectron/blob/master/detectron/modeling/generate_anchors.py" aria-label="generate_anchors.py &#xB7; facebookresearch/Detectron" class="hint--top hint--rounded hint--no-animate hint--no-arrow">code to generate anchors</a> in Detectron is quite long,because it tries to generate integer-valued anchor boxes.By adopting coordinates for all boxes in Detectron2, integer boxes are not needed.This simplifies all the logic to just <a href="https://github.com/facebookresearch/detectron2/blob/60fd4885d7cfd52d4267d1da9ebb6b2b9a3fc937/detectron2/modeling/anchor_generator.py#L193-L212" aria-label="anchor_generator.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">a few lines of code</a>.</p><p>This does not affect accuracy, because the exact values of anchors are not that important as long asthe same is used in training &amp; testing.</p><h2 id="RoIAlign">RoIAlign<a class="markdown-anchor" href="#RoIAlign">&#xB6;</a></h2><p>The RoIAlign operation crops a region from an image and resize it to certain shape.It&apos;s easy to make mistakes becausetwo images and two coordinate systems are involved.Let&apos;s derive how to perform RoIAlign.</p><img src="/blog/2021/Where-are-Pixels/roialign.jpg" class="center" width="650"><p>Given an image and a region (the green box), we want to resample a K <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: 0.02ex;" xmlns="http://www.w3.org/2000/svg" width="1.76ex" height="1.09ex" role="img" focusable="false" viewbox="0 -491 778 482"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"/></g></g></g></svg></mjx-container> K output imagethat corresponds to the region.W.l.o.g. we assume the input image has stride=1.Since we know the resolution and the absolute length of output,the output stride derived from the definition of grid &#x2461; is <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.781ex;" xmlns="http://www.w3.org/2000/svg" width="13.438ex" height="2.81ex" role="img" focusable="false" viewbox="0 -897.2 5939.6 1242.2"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mfrac" transform="translate(389,0)"><g data-mml-node="mrow" transform="translate(220,457.1) scale(0.707)"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mn" transform="translate(605,-150) scale(0.707)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g></g><g data-mml-node="mo" transform="translate(1008.6,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="msub" transform="translate(1786.6,0)"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mn" transform="translate(605,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g></g><g data-mml-node="mi" transform="translate(893.9,-345) scale(0.707)"><path data-c="1D43E" d="M285 628Q285 635 228 637Q205 637 198 638T191 647Q191 649 193 661Q199 681 203 682Q205 683 214 683H219Q260 681 355 681Q389 681 418 681T463 682T483 682Q500 682 500 674Q500 669 497 660Q496 658 496 654T495 648T493 644T490 641T486 639T479 638T470 637T456 637Q416 636 405 634T387 623L306 305Q307 305 490 449T678 597Q692 611 692 620Q692 635 667 637Q651 637 651 648Q651 650 654 662T659 677Q662 682 676 682Q680 682 711 681T791 680Q814 680 839 681T869 682Q889 682 889 672Q889 650 881 642Q878 637 862 637Q787 632 726 586Q710 576 656 534T556 455L509 418L518 396Q527 374 546 329T581 244Q656 67 661 61Q663 59 666 57Q680 47 717 46H738Q744 38 744 37T741 19Q737 6 731 0H720Q680 3 625 3Q503 3 488 0H478Q472 6 472 9T474 27Q478 40 480 43T491 46H494Q544 46 544 71Q544 75 517 141T485 216L427 354L359 301L291 248L268 155Q245 63 245 58Q245 51 253 49T303 46H334Q340 37 340 35Q340 19 333 5Q328 0 317 0Q314 0 280 1T180 2Q118 2 85 2T49 1Q31 1 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q147 65 216 339T285 628Z"/></g><rect width="2176.4" height="60" x="120" y="220"/></g><g data-mml-node="mo" transform="translate(2805.4,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mfrac" transform="translate(3250.1,0)"><g data-mml-node="mrow" transform="translate(220,485) scale(0.707)"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mn" transform="translate(523,-150) scale(0.707)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g></g><g data-mml-node="mo" transform="translate(926.6,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="msub" transform="translate(1704.6,0)"><g data-mml-node="mi"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mn" transform="translate(523,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g></g><g data-mml-node="mi" transform="translate(835.9,-345) scale(0.707)"><path data-c="1D43E" d="M285 628Q285 635 228 637Q205 637 198 638T191 647Q191 649 193 661Q199 681 203 682Q205 683 214 683H219Q260 681 355 681Q389 681 418 681T463 682T483 682Q500 682 500 674Q500 669 497 660Q496 658 496 654T495 648T493 644T490 641T486 639T479 638T470 637T456 637Q416 636 405 634T387 623L306 305Q307 305 490 449T678 597Q692 611 692 620Q692 635 667 637Q651 637 651 648Q651 650 654 662T659 677Q662 682 676 682Q680 682 711 681T791 680Q814 680 839 681T869 682Q889 682 889 672Q889 650 881 642Q878 637 862 637Q787 632 726 586Q710 576 656 534T556 455L509 418L518 396Q527 374 546 329T581 244Q656 67 661 61Q663 59 666 57Q680 47 717 46H738Q744 38 744 37T741 19Q737 6 731 0H720Q680 3 625 3Q503 3 488 0H478Q472 6 472 9T474 27Q478 40 480 43T491 46H494Q544 46 544 71Q544 75 517 141T485 216L427 354L359 301L291 248L268 155Q245 63 245 58Q245 51 253 49T303 46H334Q340 37 340 35Q340 19 333 5Q328 0 317 0Q314 0 280 1T180 2Q118 2 85 2T49 1Q31 1 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q147 65 216 339T285 628Z"/></g><rect width="2060.5" height="60" x="120" y="220"/></g><g data-mml-node="mo" transform="translate(5550.6,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g></g></g></svg></mjx-container>.Because grid offset is 0.5<mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: 0.02ex;" xmlns="http://www.w3.org/2000/svg" width="1.76ex" height="1.09ex" role="img" focusable="false" viewbox="0 -491 778 482"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="D7" d="M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z"/></g></g></g></svg></mjx-container>stride, the location of output pixel <code>[i,j]</code> is<mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -1.552ex;" xmlns="http://www.w3.org/2000/svg" width="45.501ex" height="4.4ex" role="img" focusable="false" viewbox="0 -1259 20111.7 1945"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="msub" transform="translate(389,0)"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mn" transform="translate(605,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g><g data-mml-node="mo" transform="translate(1619.8,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"/></g><g data-mml-node="mo" transform="translate(2620,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mn" transform="translate(3009,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(4509.2,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"/></g><g data-mml-node="mi" transform="translate(5509.4,0)"><path data-c="1D457" d="M297 596Q297 627 318 644T361 661Q378 661 389 651T403 623Q403 595 384 576T340 557Q322 557 310 567T297 596ZM288 376Q288 405 262 405Q240 405 220 393T185 362T161 325T144 293L137 279Q135 278 121 278H107Q101 284 101 286T105 299Q126 348 164 391T252 441Q253 441 260 441T272 442Q296 441 316 432Q341 418 354 401T367 348V332L318 133Q267 -67 264 -75Q246 -125 194 -164T75 -204Q25 -204 7 -183T-12 -137Q-12 -110 7 -91T53 -71Q70 -71 82 -81T95 -112Q95 -148 63 -167Q69 -168 77 -168Q111 -168 139 -140T182 -74L193 -32Q204 11 219 72T251 197T278 308T289 365Q289 372 288 376Z"/></g><g data-mml-node="mo" transform="translate(5921.4,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mfrac" transform="translate(6310.4,0)"><g data-mml-node="mrow" transform="translate(220,676)"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mn" transform="translate(605,-150) scale(0.707)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g></g><g data-mml-node="mo" transform="translate(1230.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="msub" transform="translate(2231,0)"><g data-mml-node="mi"><path data-c="1D465" d="M52 289Q59 331 106 386T222 442Q257 442 286 424T329 379Q371 442 430 442Q467 442 494 420T522 361Q522 332 508 314T481 292T458 288Q439 288 427 299T415 328Q415 374 465 391Q454 404 425 404Q412 404 406 402Q368 386 350 336Q290 115 290 78Q290 50 306 38T341 26Q378 26 414 59T463 140Q466 150 469 151T485 153H489Q504 153 504 145Q504 144 502 134Q486 77 440 33T333 -11Q263 -11 227 52Q186 -10 133 -10H127Q78 -10 57 16T35 71Q35 103 54 123T99 143Q142 143 142 101Q142 81 130 66T107 46T94 41L91 40Q91 39 97 36T113 29T132 26Q168 26 194 71Q203 87 217 139T245 247T261 313Q266 340 266 352Q266 380 251 392T217 404Q177 404 142 372T93 290Q91 281 88 280T72 278H58Q52 284 52 289Z"/></g><g data-mml-node="mn" transform="translate(605,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g></g><g data-mml-node="mi" transform="translate(1395.3,-686)"><path data-c="1D43E" d="M285 628Q285 635 228 637Q205 637 198 638T191 647Q191 649 193 661Q199 681 203 682Q205 683 214 683H219Q260 681 355 681Q389 681 418 681T463 682T483 682Q500 682 500 674Q500 669 497 660Q496 658 496 654T495 648T493 644T490 641T486 639T479 638T470 637T456 637Q416 636 405 634T387 623L306 305Q307 305 490 449T678 597Q692 611 692 620Q692 635 667 637Q651 637 651 648Q651 650 654 662T659 677Q662 682 676 682Q680 682 711 681T791 680Q814 680 839 681T869 682Q889 682 889 672Q889 650 881 642Q878 637 862 637Q787 632 726 586Q710 576 656 534T556 455L509 418L518 396Q527 374 546 329T581 244Q656 67 661 61Q663 59 666 57Q680 47 717 46H738Q744 38 744 37T741 19Q737 6 731 0H720Q680 3 625 3Q503 3 488 0H478Q472 6 472 9T474 27Q478 40 480 43T491 46H494Q544 46 544 71Q544 75 517 141T485 216L427 354L359 301L291 248L268 155Q245 63 245 58Q245 51 253 49T303 46H334Q340 37 340 35Q340 19 333 5Q328 0 317 0Q314 0 280 1T180 2Q118 2 85 2T49 1Q31 1 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q147 65 216 339T285 628Z"/></g><rect width="3439.6" height="60" x="120" y="220"/></g><g data-mml-node="mo" transform="translate(9990,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="msub" transform="translate(10434.7,0)"><g data-mml-node="mi"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mn" transform="translate(523,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g><g data-mml-node="mo" transform="translate(11583.4,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"/></g><g data-mml-node="mo" transform="translate(12583.7,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mn" transform="translate(12972.7,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(14472.9,0)"><path data-c="2B" d="M56 237T56 250T70 270H369V420L370 570Q380 583 389 583Q402 583 409 568V270H707Q722 262 722 250T707 230H409V-68Q401 -82 391 -82H389H387Q375 -82 369 -68V230H70Q56 237 56 250Z"/></g><g data-mml-node="mi" transform="translate(15473.1,0)"><path data-c="1D456" d="M184 600Q184 624 203 642T247 661Q265 661 277 649T290 619Q290 596 270 577T226 557Q211 557 198 567T184 600ZM21 287Q21 295 30 318T54 369T98 420T158 442Q197 442 223 419T250 357Q250 340 236 301T196 196T154 83Q149 61 149 51Q149 26 166 26Q175 26 185 29T208 43T235 78T260 137Q263 149 265 151T282 153Q302 153 302 143Q302 135 293 112T268 61T223 11T161 -11Q129 -11 102 10T74 74Q74 91 79 106T122 220Q160 321 166 341T173 380Q173 404 156 404H154Q124 404 99 371T61 287Q60 286 59 284T58 281T56 279T53 278T49 278T41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mo" transform="translate(15818.1,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mfrac" transform="translate(16207.1,0)"><g data-mml-node="mrow" transform="translate(220,676)"><g data-mml-node="msub"><g data-mml-node="mi"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mn" transform="translate(523,-150) scale(0.707)"><path data-c="31" d="M213 578L200 573Q186 568 160 563T102 556H83V602H102Q149 604 189 617T245 641T273 663Q275 666 285 666Q294 666 302 660V361L303 61Q310 54 315 52T339 48T401 46H427V0H416Q395 3 257 3Q121 3 100 0H88V46H114Q136 46 152 46T177 47T193 50T201 52T207 57T213 61V578Z"/></g></g><g data-mml-node="mo" transform="translate(1148.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="msub" transform="translate(2149,0)"><g data-mml-node="mi"><path data-c="1D466" d="M21 287Q21 301 36 335T84 406T158 442Q199 442 224 419T250 355Q248 336 247 334Q247 331 231 288T198 191T182 105Q182 62 196 45T238 27Q261 27 281 38T312 61T339 94Q339 95 344 114T358 173T377 247Q415 397 419 404Q432 431 462 431Q475 431 483 424T494 412T496 403Q496 390 447 193T391 -23Q363 -106 294 -155T156 -205Q111 -205 77 -183T43 -117Q43 -95 50 -80T69 -58T89 -48T106 -45Q150 -45 150 -87Q150 -107 138 -122T115 -142T102 -147L99 -148Q101 -153 118 -160T152 -167H160Q177 -167 186 -165Q219 -156 247 -127T290 -65T313 -9T321 21L315 17Q309 13 296 6T270 -6Q250 -11 231 -11Q185 -11 150 11T104 82Q103 89 103 113Q103 170 138 262T173 379Q173 380 173 381Q173 390 173 393T169 400T158 404H154Q131 404 112 385T82 344T65 302T57 280Q55 278 41 278H27Q21 284 21 287Z"/></g><g data-mml-node="mn" transform="translate(523,-150) scale(0.707)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/></g></g></g><g data-mml-node="mi" transform="translate(1313.3,-686)"><path data-c="1D43E" d="M285 628Q285 635 228 637Q205 637 198 638T191 647Q191 649 193 661Q199 681 203 682Q205 683 214 683H219Q260 681 355 681Q389 681 418 681T463 682T483 682Q500 682 500 674Q500 669 497 660Q496 658 496 654T495 648T493 644T490 641T486 639T479 638T470 637T456 637Q416 636 405 634T387 623L306 305Q307 305 490 449T678 597Q692 611 692 620Q692 635 667 637Q651 637 651 648Q651 650 654 662T659 677Q662 682 676 682Q680 682 711 681T791 680Q814 680 839 681T869 682Q889 682 889 672Q889 650 881 642Q878 637 862 637Q787 632 726 586Q710 576 656 534T556 455L509 418L518 396Q527 374 546 329T581 244Q656 67 661 61Q663 59 666 57Q680 47 717 46H738Q744 38 744 37T741 19Q737 6 731 0H720Q680 3 625 3Q503 3 488 0H478Q472 6 472 9T474 27Q478 40 480 43T491 46H494Q544 46 544 71Q544 75 517 141T485 216L427 354L359 301L291 248L268 155Q245 63 245 58Q245 51 253 49T303 46H334Q340 37 340 35Q340 19 333 5Q328 0 317 0Q314 0 280 1T180 2Q118 2 85 2T49 1Q31 1 31 11Q31 13 34 25Q38 41 42 43T65 46Q92 46 125 49Q139 52 144 61Q147 65 216 339T285 628Z"/></g><rect width="3275.6" height="60" x="120" y="220"/></g><g data-mml-node="mo" transform="translate(19722.7,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g></g></g></svg></mjx-container>Let&apos;s call it <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="6.42ex" height="2.262ex" role="img" focusable="false" viewbox="0 -750 2837.7 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(389,0)"><path data-c="1D44B" d="M42 0H40Q26 0 26 11Q26 15 29 27Q33 41 36 43T55 46Q141 49 190 98Q200 108 306 224T411 342Q302 620 297 625Q288 636 234 637H206Q200 643 200 645T202 664Q206 677 212 683H226Q260 681 347 681Q380 681 408 681T453 682T473 682Q490 682 490 671Q490 670 488 658Q484 643 481 640T465 637Q434 634 411 620L488 426L541 485Q646 598 646 610Q646 628 622 635Q617 635 609 637Q594 637 594 648Q594 650 596 664Q600 677 606 683H618Q619 683 643 683T697 681T738 680Q828 680 837 683H845Q852 676 852 672Q850 647 840 637H824Q790 636 763 628T722 611T698 593L687 584Q687 585 592 480L505 384Q505 383 536 304T601 142T638 56Q648 47 699 46Q734 46 734 37Q734 35 732 23Q728 7 725 4T711 1Q708 1 678 1T589 2Q528 2 496 2T461 1Q444 1 444 10Q444 11 446 25Q448 35 450 39T455 44T464 46T480 47T506 54Q523 62 523 64Q522 64 476 181L429 299Q241 95 236 84Q232 76 232 72Q232 53 261 47Q262 47 267 47T273 46Q276 46 277 46T280 45T283 42T284 35Q284 26 282 19Q279 6 276 4T261 1Q258 1 243 1T201 2T142 2Q64 2 42 0Z"/></g><g data-mml-node="mo" transform="translate(1241,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mi" transform="translate(1685.7,0)"><path data-c="1D44C" d="M66 637Q54 637 49 637T39 638T32 641T30 647T33 664T42 682Q44 683 56 683Q104 680 165 680Q288 680 306 683H316Q322 677 322 674T320 656Q316 643 310 637H298Q242 637 242 624Q242 619 292 477T343 333L346 336Q350 340 358 349T379 373T411 410T454 461Q546 568 561 587T577 618Q577 634 545 637Q528 637 528 647Q528 649 530 661Q533 676 535 679T549 683Q551 683 578 682T657 680Q684 680 713 681T746 682Q763 682 763 673Q763 669 760 657T755 643Q753 637 734 637Q662 632 617 587Q608 578 477 424L348 273L322 169Q295 62 295 57Q295 46 363 46Q379 46 384 45T390 35Q390 33 388 23Q384 6 382 4T366 1Q361 1 324 1T232 2Q170 2 138 2T102 1Q84 1 84 9Q84 14 87 24Q88 27 89 30T90 35T91 39T93 42T96 44T101 45T107 45T116 46T129 46Q168 47 180 50T198 63Q201 68 227 171L252 274L129 623Q128 624 127 625T125 627T122 629T118 631T113 633T105 634T96 635T83 636T66 637Z"/></g><g data-mml-node="mo" transform="translate(2448.7,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g></g></g></svg></mjx-container>.To compute resampled values at location <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="6.42ex" height="2.262ex" role="img" focusable="false" viewbox="0 -750 2837.7 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(389,0)"><path data-c="1D44B" d="M42 0H40Q26 0 26 11Q26 15 29 27Q33 41 36 43T55 46Q141 49 190 98Q200 108 306 224T411 342Q302 620 297 625Q288 636 234 637H206Q200 643 200 645T202 664Q206 677 212 683H226Q260 681 347 681Q380 681 408 681T453 682T473 682Q490 682 490 671Q490 670 488 658Q484 643 481 640T465 637Q434 634 411 620L488 426L541 485Q646 598 646 610Q646 628 622 635Q617 635 609 637Q594 637 594 648Q594 650 596 664Q600 677 606 683H618Q619 683 643 683T697 681T738 680Q828 680 837 683H845Q852 676 852 672Q850 647 840 637H824Q790 636 763 628T722 611T698 593L687 584Q687 585 592 480L505 384Q505 383 536 304T601 142T638 56Q648 47 699 46Q734 46 734 37Q734 35 732 23Q728 7 725 4T711 1Q708 1 678 1T589 2Q528 2 496 2T461 1Q444 1 444 10Q444 11 446 25Q448 35 450 39T455 44T464 46T480 47T506 54Q523 62 523 64Q522 64 476 181L429 299Q241 95 236 84Q232 76 232 72Q232 53 261 47Q262 47 267 47T273 46Q276 46 277 46T280 45T283 42T284 35Q284 26 282 19Q279 6 276 4T261 1Q258 1 243 1T201 2T142 2Q64 2 42 0Z"/></g><g data-mml-node="mo" transform="translate(1241,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mi" transform="translate(1685.7,0)"><path data-c="1D44C" d="M66 637Q54 637 49 637T39 638T32 641T30 647T33 664T42 682Q44 683 56 683Q104 680 165 680Q288 680 306 683H316Q322 677 322 674T320 656Q316 643 310 637H298Q242 637 242 624Q242 619 292 477T343 333L346 336Q350 340 358 349T379 373T411 410T454 461Q546 568 561 587T577 618Q577 634 545 637Q528 637 528 647Q528 649 530 661Q533 676 535 679T549 683Q551 683 578 682T657 680Q684 680 713 681T746 682Q763 682 763 673Q763 669 760 657T755 643Q753 637 734 637Q662 632 617 587Q608 578 477 424L348 273L322 169Q295 62 295 57Q295 46 363 46Q379 46 384 45T390 35Q390 33 388 23Q384 6 382 4T366 1Q361 1 324 1T232 2Q170 2 138 2T102 1Q84 1 84 9Q84 14 87 24Q88 27 89 30T90 35T91 39T93 42T96 44T101 45T107 45T116 46T129 46Q168 47 180 50T198 63Q201 68 227 171L252 274L129 623Q128 624 127 625T125 627T122 629T118 631T113 633T105 634T96 635T83 636T66 637Z"/></g><g data-mml-node="mo" transform="translate(2448.7,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g></g></g></svg></mjx-container>, an easy way is to do bilinear interpolation with its 4nearest pixels (this corresponds to RoIAlign with <code>sampling_ratio=1</code>).We show the 4 neighboring input pixels of <code>output[0,0]</code> in the figure.The <strong>indices</strong> of 4 nearest pixels of <mjx-container class="MathJax" jax="SVG"><svg style="vertical-align: -0.566ex;" xmlns="http://www.w3.org/2000/svg" width="6.42ex" height="2.262ex" role="img" focusable="false" viewbox="0 -750 2837.7 1000"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mo"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(389,0)"><path data-c="1D44B" d="M42 0H40Q26 0 26 11Q26 15 29 27Q33 41 36 43T55 46Q141 49 190 98Q200 108 306 224T411 342Q302 620 297 625Q288 636 234 637H206Q200 643 200 645T202 664Q206 677 212 683H226Q260 681 347 681Q380 681 408 681T453 682T473 682Q490 682 490 671Q490 670 488 658Q484 643 481 640T465 637Q434 634 411 620L488 426L541 485Q646 598 646 610Q646 628 622 635Q617 635 609 637Q594 637 594 648Q594 650 596 664Q600 677 606 683H618Q619 683 643 683T697 681T738 680Q828 680 837 683H845Q852 676 852 672Q850 647 840 637H824Q790 636 763 628T722 611T698 593L687 584Q687 585 592 480L505 384Q505 383 536 304T601 142T638 56Q648 47 699 46Q734 46 734 37Q734 35 732 23Q728 7 725 4T711 1Q708 1 678 1T589 2Q528 2 496 2T461 1Q444 1 444 10Q444 11 446 25Q448 35 450 39T455 44T464 46T480 47T506 54Q523 62 523 64Q522 64 476 181L429 299Q241 95 236 84Q232 76 232 72Q232 53 261 47Q262 47 267 47T273 46Q276 46 277 46T280 45T283 42T284 35Q284 26 282 19Q279 6 276 4T261 1Q258 1 243 1T201 2T142 2Q64 2 42 0Z"/></g><g data-mml-node="mo" transform="translate(1241,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mi" transform="translate(1685.7,0)"><path data-c="1D44C" d="M66 637Q54 637 49 637T39 638T32 641T30 647T33 664T42 682Q44 683 56 683Q104 680 165 680Q288 680 306 683H316Q322 677 322 674T320 656Q316 643 310 637H298Q242 637 242 624Q242 619 292 477T343 333L346 336Q350 340 358 349T379 373T411 410T454 461Q546 568 561 587T577 618Q577 634 545 637Q528 637 528 647Q528 649 530 661Q533 676 535 679T549 683Q551 683 578 682T657 680Q684 680 713 681T746 682Q763 682 763 673Q763 669 760 657T755 643Q753 637 734 637Q662 632 617 587Q608 578 477 424L348 273L322 169Q295 62 295 57Q295 46 363 46Q379 46 384 45T390 35Q390 33 388 23Q384 6 382 4T366 1Q361 1 324 1T232 2Q170 2 138 2T102 1Q84 1 84 9Q84 14 87 24Q88 27 89 30T90 35T91 39T93 42T96 44T101 45T107 45T116 46T129 46Q168 47 180 50T198 63Q201 68 227 171L252 274L129 623Q128 624 127 625T125 627T122 629T118 631T113 633T105 634T96 635T83 636T66 637Z"/></g><g data-mml-node="mo" transform="translate(2448.7,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g></g></g></svg></mjx-container> are obtained after subtracting 0.5 to align their coordinate system:</p><p><mjx-container class="MathJax" jax="SVG" display="true"><svg style="vertical-align: -5.317ex;" xmlns="http://www.w3.org/2000/svg" width="39.69ex" height="11.765ex" role="img" focusable="false" viewbox="0 -2850 17543.1 5200"><g stroke="currentColor" fill="currentColor" stroke-width="0" transform="scale(1,-1)"><g data-mml-node="math"><g data-mml-node="mtable"><g data-mml-node="mtr" transform="translate(0,2100)"><g data-mml-node="mtd"><g data-mml-node="mi"><path data-c="1D434" d="M208 74Q208 50 254 46Q272 46 272 35Q272 34 270 22Q267 8 264 4T251 0Q249 0 239 0T205 1T141 2Q70 2 50 0H42Q35 7 35 11Q37 38 48 46H62Q132 49 164 96Q170 102 345 401T523 704Q530 716 547 716H555H572Q578 707 578 706L606 383Q634 60 636 57Q641 46 701 46Q726 46 726 36Q726 34 723 22Q720 7 718 4T704 0Q701 0 690 0T651 1T578 2Q484 2 455 0H443Q437 6 437 9T439 27Q443 40 445 43L449 46H469Q523 49 533 63L521 213H283L249 155Q208 86 208 74ZM516 260Q516 271 504 416T490 562L463 519Q447 492 400 412L310 260L413 259Q516 259 516 260Z"/></g><g data-mml-node="mo" transform="translate(1027.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"/></g><g data-mml-node="mtext" transform="translate(2083.6,0)"><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(278,0)"/><path data-c="70" d="M36 -148H50Q89 -148 97 -134V-126Q97 -119 97 -107T97 -77T98 -38T98 6T98 55T98 106Q98 140 98 177T98 243T98 296T97 335T97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 61 434T98 436Q115 437 135 438T165 441T176 442H179V416L180 390L188 397Q247 441 326 441Q407 441 464 377T522 216Q522 115 457 52T310 -11Q242 -11 190 33L182 40V-45V-101Q182 -128 184 -134T195 -145Q216 -148 244 -148H260V-194H252L228 -193Q205 -192 178 -192T140 -191Q37 -191 28 -194H20V-148H36ZM424 218Q424 292 390 347T305 402Q234 402 182 337V98Q222 26 294 26Q345 26 384 80T424 218Z" transform="translate(834,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(1390,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(1946,0)"/></g><g data-mml-node="mo" transform="translate(4418.6,0)"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"/></g><g data-mml-node="mtext" transform="translate(4696.6,0)"><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(306,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(584,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(1084,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(1584,0)"/></g><g data-mml-node="mo" transform="translate(6672.6,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(7061.6,0)"><path data-c="1D44C" d="M66 637Q54 637 49 637T39 638T32 641T30 647T33 664T42 682Q44 683 56 683Q104 680 165 680Q288 680 306 683H316Q322 677 322 674T320 656Q316 643 310 637H298Q242 637 242 624Q242 619 292 477T343 333L346 336Q350 340 358 349T379 373T411 410T454 461Q546 568 561 587T577 618Q577 634 545 637Q528 637 528 647Q528 649 530 661Q533 676 535 679T549 683Q551 683 578 682T657 680Q684 680 713 681T746 682Q763 682 763 673Q763 669 760 657T755 643Q753 637 734 637Q662 632 617 587Q608 578 477 424L348 273L322 169Q295 62 295 57Q295 46 363 46Q379 46 384 45T390 35Q390 33 388 23Q384 6 382 4T366 1Q361 1 324 1T232 2Q170 2 138 2T102 1Q84 1 84 9Q84 14 87 24Q88 27 89 30T90 35T91 39T93 42T96 44T101 45T107 45T116 46T129 46Q168 47 180 50T198 63Q201 68 227 171L252 274L129 623Q128 624 127 625T125 627T122 629T118 631T113 633T105 634T96 635T83 636T66 637Z"/></g><g data-mml-node="mo" transform="translate(8046.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(9047,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(10325,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(10714,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mtext" transform="translate(11158.7,0)"><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(306,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(584,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(1084,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(1584,0)"/></g><g data-mml-node="mo" transform="translate(13134.7,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(13523.7,0)"><path data-c="1D44B" d="M42 0H40Q26 0 26 11Q26 15 29 27Q33 41 36 43T55 46Q141 49 190 98Q200 108 306 224T411 342Q302 620 297 625Q288 636 234 637H206Q200 643 200 645T202 664Q206 677 212 683H226Q260 681 347 681Q380 681 408 681T453 682T473 682Q490 682 490 671Q490 670 488 658Q484 643 481 640T465 637Q434 634 411 620L488 426L541 485Q646 598 646 610Q646 628 622 635Q617 635 609 637Q594 637 594 648Q594 650 596 664Q600 677 606 683H618Q619 683 643 683T697 681T738 680Q828 680 837 683H845Q852 676 852 672Q850 647 840 637H824Q790 636 763 628T722 611T698 593L687 584Q687 585 592 480L505 384Q505 383 536 304T601 142T638 56Q648 47 699 46Q734 46 734 37Q734 35 732 23Q728 7 725 4T711 1Q708 1 678 1T589 2Q528 2 496 2T461 1Q444 1 444 10Q444 11 446 25Q448 35 450 39T455 44T464 46T480 47T506 54Q523 62 523 64Q522 64 476 181L429 299Q241 95 236 84Q232 76 232 72Q232 53 261 47Q262 47 267 47T273 46Q276 46 277 46T280 45T283 42T284 35Q284 26 282 19Q279 6 276 4T261 1Q258 1 243 1T201 2T142 2Q64 2 42 0Z"/></g><g data-mml-node="mo" transform="translate(14597.9,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(15598.1,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(16876.1,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(17265.1,0)"><path data-c="5D" d="M22 710V750H159V-250H22V-210H119V710H22Z"/></g></g></g><g data-mml-node="mtr" transform="translate(0,700)"><g data-mml-node="mtd"><g data-mml-node="mi"><path data-c="1D435" d="M231 637Q204 637 199 638T194 649Q194 676 205 682Q206 683 335 683Q594 683 608 681Q671 671 713 636T756 544Q756 480 698 429T565 360L555 357Q619 348 660 311T702 219Q702 146 630 78T453 1Q446 0 242 0Q42 0 39 2Q35 5 35 10Q35 17 37 24Q42 43 47 45Q51 46 62 46H68Q95 46 128 49Q142 52 147 61Q150 65 219 339T288 628Q288 635 231 637ZM649 544Q649 574 634 600T585 634Q578 636 493 637Q473 637 451 637T416 636H403Q388 635 384 626Q382 622 352 506Q352 503 351 500L320 374H401Q482 374 494 376Q554 386 601 434T649 544ZM595 229Q595 273 572 302T512 336Q506 337 429 337Q311 337 310 336Q310 334 293 263T258 122L240 52Q240 48 252 48T333 46Q422 46 429 47Q491 54 543 105T595 229Z"/></g><g data-mml-node="mo" transform="translate(1036.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"/></g><g data-mml-node="mtext" transform="translate(2092.6,0)"><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(278,0)"/><path data-c="70" d="M36 -148H50Q89 -148 97 -134V-126Q97 -119 97 -107T97 -77T98 -38T98 6T98 55T98 106Q98 140 98 177T98 243T98 296T97 335T97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 61 434T98 436Q115 437 135 438T165 441T176 442H179V416L180 390L188 397Q247 441 326 441Q407 441 464 377T522 216Q522 115 457 52T310 -11Q242 -11 190 33L182 40V-45V-101Q182 -128 184 -134T195 -145Q216 -148 244 -148H260V-194H252L228 -193Q205 -192 178 -192T140 -191Q37 -191 28 -194H20V-148H36ZM424 218Q424 292 390 347T305 402Q234 402 182 337V98Q222 26 294 26Q345 26 384 80T424 218Z" transform="translate(834,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(1390,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(1946,0)"/></g><g data-mml-node="mo" transform="translate(4427.6,0)"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"/></g><g data-mml-node="mtext" transform="translate(4705.6,0)"><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(306,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(584,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(1084,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(1584,0)"/></g><g data-mml-node="mo" transform="translate(6681.6,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(7070.6,0)"><path data-c="1D44C" d="M66 637Q54 637 49 637T39 638T32 641T30 647T33 664T42 682Q44 683 56 683Q104 680 165 680Q288 680 306 683H316Q322 677 322 674T320 656Q316 643 310 637H298Q242 637 242 624Q242 619 292 477T343 333L346 336Q350 340 358 349T379 373T411 410T454 461Q546 568 561 587T577 618Q577 634 545 637Q528 637 528 647Q528 649 530 661Q533 676 535 679T549 683Q551 683 578 682T657 680Q684 680 713 681T746 682Q763 682 763 673Q763 669 760 657T755 643Q753 637 734 637Q662 632 617 587Q608 578 477 424L348 273L322 169Q295 62 295 57Q295 46 363 46Q379 46 384 45T390 35Q390 33 388 23Q384 6 382 4T366 1Q361 1 324 1T232 2Q170 2 138 2T102 1Q84 1 84 9Q84 14 87 24Q88 27 89 30T90 35T91 39T93 42T96 44T101 45T107 45T116 46T129 46Q168 47 180 50T198 63Q201 68 227 171L252 274L129 623Q128 624 127 625T125 627T122 629T118 631T113 633T105 634T96 635T83 636T66 637Z"/></g><g data-mml-node="mo" transform="translate(8055.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(9056,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(10334,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(10723,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mtext" transform="translate(11167.7,0)"><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(444,0)"/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(888,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1166,0)"/></g><g data-mml-node="mo" transform="translate(12611.7,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(13000.7,0)"><path data-c="1D44B" d="M42 0H40Q26 0 26 11Q26 15 29 27Q33 41 36 43T55 46Q141 49 190 98Q200 108 306 224T411 342Q302 620 297 625Q288 636 234 637H206Q200 643 200 645T202 664Q206 677 212 683H226Q260 681 347 681Q380 681 408 681T453 682T473 682Q490 682 490 671Q490 670 488 658Q484 643 481 640T465 637Q434 634 411 620L488 426L541 485Q646 598 646 610Q646 628 622 635Q617 635 609 637Q594 637 594 648Q594 650 596 664Q600 677 606 683H618Q619 683 643 683T697 681T738 680Q828 680 837 683H845Q852 676 852 672Q850 647 840 637H824Q790 636 763 628T722 611T698 593L687 584Q687 585 592 480L505 384Q505 383 536 304T601 142T638 56Q648 47 699 46Q734 46 734 37Q734 35 732 23Q728 7 725 4T711 1Q708 1 678 1T589 2Q528 2 496 2T461 1Q444 1 444 10Q444 11 446 25Q448 35 450 39T455 44T464 46T480 47T506 54Q523 62 523 64Q522 64 476 181L429 299Q241 95 236 84Q232 76 232 72Q232 53 261 47Q262 47 267 47T273 46Q276 46 277 46T280 45T283 42T284 35Q284 26 282 19Q279 6 276 4T261 1Q258 1 243 1T201 2T142 2Q64 2 42 0Z"/></g><g data-mml-node="mo" transform="translate(14074.9,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(15075.1,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(16353.1,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(16742.1,0)"><path data-c="5D" d="M22 710V750H159V-250H22V-210H119V710H22Z"/></g></g></g><g data-mml-node="mtr" transform="translate(0,-700)"><g data-mml-node="mtd"><g data-mml-node="mi"><path data-c="1D436" d="M50 252Q50 367 117 473T286 641T490 704Q580 704 633 653Q642 643 648 636T656 626L657 623Q660 623 684 649Q691 655 699 663T715 679T725 690L740 705H746Q760 705 760 698Q760 694 728 561Q692 422 692 421Q690 416 687 415T669 413H653Q647 419 647 422Q647 423 648 429T650 449T651 481Q651 552 619 605T510 659Q484 659 454 652T382 628T299 572T226 479Q194 422 175 346T156 222Q156 108 232 58Q280 24 350 24Q441 24 512 92T606 240Q610 253 612 255T628 257Q648 257 648 248Q648 243 647 239Q618 132 523 55T319 -22Q206 -22 128 53T50 252Z"/></g><g data-mml-node="mo" transform="translate(1037.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"/></g><g data-mml-node="mtext" transform="translate(2093.6,0)"><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(278,0)"/><path data-c="70" d="M36 -148H50Q89 -148 97 -134V-126Q97 -119 97 -107T97 -77T98 -38T98 6T98 55T98 106Q98 140 98 177T98 243T98 296T97 335T97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 61 434T98 436Q115 437 135 438T165 441T176 442H179V416L180 390L188 397Q247 441 326 441Q407 441 464 377T522 216Q522 115 457 52T310 -11Q242 -11 190 33L182 40V-45V-101Q182 -128 184 -134T195 -145Q216 -148 244 -148H260V-194H252L228 -193Q205 -192 178 -192T140 -191Q37 -191 28 -194H20V-148H36ZM424 218Q424 292 390 347T305 402Q234 402 182 337V98Q222 26 294 26Q345 26 384 80T424 218Z" transform="translate(834,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(1390,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(1946,0)"/></g><g data-mml-node="mo" transform="translate(4428.6,0)"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"/></g><g data-mml-node="mtext" transform="translate(4706.6,0)"><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(444,0)"/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(888,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1166,0)"/></g><g data-mml-node="mo" transform="translate(6150.6,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(6539.6,0)"><path data-c="1D44C" d="M66 637Q54 637 49 637T39 638T32 641T30 647T33 664T42 682Q44 683 56 683Q104 680 165 680Q288 680 306 683H316Q322 677 322 674T320 656Q316 643 310 637H298Q242 637 242 624Q242 619 292 477T343 333L346 336Q350 340 358 349T379 373T411 410T454 461Q546 568 561 587T577 618Q577 634 545 637Q528 637 528 647Q528 649 530 661Q533 676 535 679T549 683Q551 683 578 682T657 680Q684 680 713 681T746 682Q763 682 763 673Q763 669 760 657T755 643Q753 637 734 637Q662 632 617 587Q608 578 477 424L348 273L322 169Q295 62 295 57Q295 46 363 46Q379 46 384 45T390 35Q390 33 388 23Q384 6 382 4T366 1Q361 1 324 1T232 2Q170 2 138 2T102 1Q84 1 84 9Q84 14 87 24Q88 27 89 30T90 35T91 39T93 42T96 44T101 45T107 45T116 46T129 46Q168 47 180 50T198 63Q201 68 227 171L252 274L129 623Q128 624 127 625T125 627T122 629T118 631T113 633T105 634T96 635T83 636T66 637Z"/></g><g data-mml-node="mo" transform="translate(7524.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(8525,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(9803,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(10192,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mtext" transform="translate(10636.7,0)"><path data-c="66" d="M273 0Q255 3 146 3Q43 3 34 0H26V46H42Q70 46 91 49Q99 52 103 60Q104 62 104 224V385H33V431H104V497L105 564L107 574Q126 639 171 668T266 704Q267 704 275 704T289 705Q330 702 351 679T372 627Q372 604 358 590T321 576T284 590T270 627Q270 647 288 667H284Q280 668 273 668Q245 668 223 647T189 592Q183 572 182 497V431H293V385H185V225Q185 63 186 61T189 57T194 54T199 51T206 49T213 48T222 47T231 47T241 46T251 46H282V0H273Z"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(306,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(584,0)"/><path data-c="6F" d="M28 214Q28 309 93 378T250 448Q340 448 405 380T471 215Q471 120 407 55T250 -10Q153 -10 91 57T28 214ZM250 30Q372 30 372 193V225V250Q372 272 371 288T364 326T348 362T317 390T268 410Q263 411 252 411Q222 411 195 399Q152 377 139 338T126 246V226Q126 130 145 91Q177 30 250 30Z" transform="translate(1084,0)"/><path data-c="72" d="M36 46H50Q89 46 97 60V68Q97 77 97 91T98 122T98 161T98 203Q98 234 98 269T98 328L97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 60 434T96 436Q112 437 131 438T160 441T171 442H174V373Q213 441 271 441H277Q322 441 343 419T364 373Q364 352 351 337T313 322Q288 322 276 338T263 372Q263 381 265 388T270 400T273 405Q271 407 250 401Q234 393 226 386Q179 341 179 207V154Q179 141 179 127T179 101T180 81T180 66V61Q181 59 183 57T188 54T193 51T200 49T207 48T216 47T225 47T235 46T245 46H276V0H267Q249 3 140 3Q37 3 28 0H20V46H36Z" transform="translate(1584,0)"/></g><g data-mml-node="mo" transform="translate(12612.7,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(13001.7,0)"><path data-c="1D44B" d="M42 0H40Q26 0 26 11Q26 15 29 27Q33 41 36 43T55 46Q141 49 190 98Q200 108 306 224T411 342Q302 620 297 625Q288 636 234 637H206Q200 643 200 645T202 664Q206 677 212 683H226Q260 681 347 681Q380 681 408 681T453 682T473 682Q490 682 490 671Q490 670 488 658Q484 643 481 640T465 637Q434 634 411 620L488 426L541 485Q646 598 646 610Q646 628 622 635Q617 635 609 637Q594 637 594 648Q594 650 596 664Q600 677 606 683H618Q619 683 643 683T697 681T738 680Q828 680 837 683H845Q852 676 852 672Q850 647 840 637H824Q790 636 763 628T722 611T698 593L687 584Q687 585 592 480L505 384Q505 383 536 304T601 142T638 56Q648 47 699 46Q734 46 734 37Q734 35 732 23Q728 7 725 4T711 1Q708 1 678 1T589 2Q528 2 496 2T461 1Q444 1 444 10Q444 11 446 25Q448 35 450 39T455 44T464 46T480 47T506 54Q523 62 523 64Q522 64 476 181L429 299Q241 95 236 84Q232 76 232 72Q232 53 261 47Q262 47 267 47T273 46Q276 46 277 46T280 45T283 42T284 35Q284 26 282 19Q279 6 276 4T261 1Q258 1 243 1T201 2T142 2Q64 2 42 0Z"/></g><g data-mml-node="mo" transform="translate(14075.9,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(15076.1,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(16354.1,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(16743.1,0)"><path data-c="5D" d="M22 710V750H159V-250H22V-210H119V710H22Z"/></g></g></g><g data-mml-node="mtr" transform="translate(0,-2100)"><g data-mml-node="mtd"><g data-mml-node="mi"><path data-c="1D437" d="M287 628Q287 635 230 637Q207 637 200 638T193 647Q193 655 197 667T204 682Q206 683 403 683Q570 682 590 682T630 676Q702 659 752 597T803 431Q803 275 696 151T444 3L430 1L236 0H125H72Q48 0 41 2T33 11Q33 13 36 25Q40 41 44 43T67 46Q94 46 127 49Q141 52 146 61Q149 65 218 339T287 628ZM703 469Q703 507 692 537T666 584T629 613T590 629T555 636Q553 636 541 636T512 636T479 637H436Q392 637 386 627Q384 623 313 339T242 52Q242 48 253 48T330 47Q335 47 349 47T373 46Q499 46 581 128Q617 164 640 212T683 339T703 469Z"/></g><g data-mml-node="mo" transform="translate(1105.8,0)"><path data-c="3D" d="M56 347Q56 360 70 367H707Q722 359 722 347Q722 336 708 328L390 327H72Q56 332 56 347ZM56 153Q56 168 72 173H708Q722 163 722 153Q722 140 707 133H70Q56 140 56 153Z"/></g><g data-mml-node="mtext" transform="translate(2161.6,0)"><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z"/><path data-c="6E" d="M41 46H55Q94 46 102 60V68Q102 77 102 91T102 122T103 161T103 203Q103 234 103 269T102 328V351Q99 370 88 376T43 385H25V408Q25 431 27 431L37 432Q47 433 65 434T102 436Q119 437 138 438T167 441T178 442H181V402Q181 364 182 364T187 369T199 384T218 402T247 421T285 437Q305 442 336 442Q450 438 463 329Q464 322 464 190V104Q464 66 466 59T477 49Q498 46 526 46H542V0H534L510 1Q487 2 460 2T422 3Q319 3 310 0H302V46H318Q379 46 379 62Q380 64 380 200Q379 335 378 343Q372 371 358 385T334 402T308 404Q263 404 229 370Q202 343 195 315T187 232V168V108Q187 78 188 68T191 55T200 49Q221 46 249 46H265V0H257L234 1Q210 2 183 2T145 3Q42 3 33 0H25V46H41Z" transform="translate(278,0)"/><path data-c="70" d="M36 -148H50Q89 -148 97 -134V-126Q97 -119 97 -107T97 -77T98 -38T98 6T98 55T98 106Q98 140 98 177T98 243T98 296T97 335T97 351Q94 370 83 376T38 385H20V408Q20 431 22 431L32 432Q42 433 61 434T98 436Q115 437 135 438T165 441T176 442H179V416L180 390L188 397Q247 441 326 441Q407 441 464 377T522 216Q522 115 457 52T310 -11Q242 -11 190 33L182 40V-45V-101Q182 -128 184 -134T195 -145Q216 -148 244 -148H260V-194H252L228 -193Q205 -192 178 -192T140 -191Q37 -191 28 -194H20V-148H36ZM424 218Q424 292 390 347T305 402Q234 402 182 337V98Q222 26 294 26Q345 26 384 80T424 218Z" transform="translate(834,0)"/><path data-c="75" d="M383 58Q327 -10 256 -10H249Q124 -10 105 89Q104 96 103 226Q102 335 102 348T96 369Q86 385 36 385H25V408Q25 431 27 431L38 432Q48 433 67 434T105 436Q122 437 142 438T172 441T184 442H187V261Q188 77 190 64Q193 49 204 40Q224 26 264 26Q290 26 311 35T343 58T363 90T375 120T379 144Q379 145 379 161T380 201T380 248V315Q380 361 370 372T320 385H302V431Q304 431 378 436T457 442H464V264Q464 84 465 81Q468 61 479 55T524 46H542V0Q540 0 467 -5T390 -11H383V58Z" transform="translate(1390,0)"/><path data-c="74" d="M27 422Q80 426 109 478T141 600V615H181V431H316V385H181V241Q182 116 182 100T189 68Q203 29 238 29Q282 29 292 100Q293 108 293 146V181H333V146V134Q333 57 291 17Q264 -10 221 -10Q187 -10 162 2T124 33T105 68T98 100Q97 107 97 248V385H18V422H27Z" transform="translate(1946,0)"/></g><g data-mml-node="mo" transform="translate(4496.6,0)"><path data-c="5B" d="M118 -250V750H255V710H158V-210H255V-250H118Z"/></g><g data-mml-node="mtext" transform="translate(4774.6,0)"><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(444,0)"/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(888,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1166,0)"/></g><g data-mml-node="mo" transform="translate(6218.6,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(6607.6,0)"><path data-c="1D44C" d="M66 637Q54 637 49 637T39 638T32 641T30 647T33 664T42 682Q44 683 56 683Q104 680 165 680Q288 680 306 683H316Q322 677 322 674T320 656Q316 643 310 637H298Q242 637 242 624Q242 619 292 477T343 333L346 336Q350 340 358 349T379 373T411 410T454 461Q546 568 561 587T577 618Q577 634 545 637Q528 637 528 647Q528 649 530 661Q533 676 535 679T549 683Q551 683 578 682T657 680Q684 680 713 681T746 682Q763 682 763 673Q763 669 760 657T755 643Q753 637 734 637Q662 632 617 587Q608 578 477 424L348 273L322 169Q295 62 295 57Q295 46 363 46Q379 46 384 45T390 35Q390 33 388 23Q384 6 382 4T366 1Q361 1 324 1T232 2Q170 2 138 2T102 1Q84 1 84 9Q84 14 87 24Q88 27 89 30T90 35T91 39T93 42T96 44T101 45T107 45T116 46T129 46Q168 47 180 50T198 63Q201 68 227 171L252 274L129 623Q128 624 127 625T125 627T122 629T118 631T113 633T105 634T96 635T83 636T66 637Z"/></g><g data-mml-node="mo" transform="translate(7592.8,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(8593,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(9871,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(10260,0)"><path data-c="2C" d="M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z"/></g><g data-mml-node="mtext" transform="translate(10704.7,0)"><path data-c="63" d="M370 305T349 305T313 320T297 358Q297 381 312 396Q317 401 317 402T307 404Q281 408 258 408Q209 408 178 376Q131 329 131 219Q131 137 162 90Q203 29 272 29Q313 29 338 55T374 117Q376 125 379 127T395 129H409Q415 123 415 120Q415 116 411 104T395 71T366 33T318 2T249 -11Q163 -11 99 53T34 214Q34 318 99 383T250 448T370 421T404 357Q404 334 387 320Z"/><path data-c="65" d="M28 218Q28 273 48 318T98 391T163 433T229 448Q282 448 320 430T378 380T406 316T415 245Q415 238 408 231H126V216Q126 68 226 36Q246 30 270 30Q312 30 342 62Q359 79 369 104L379 128Q382 131 395 131H398Q415 131 415 121Q415 117 412 108Q393 53 349 21T250 -11Q155 -11 92 58T28 218ZM333 275Q322 403 238 411H236Q228 411 220 410T195 402T166 381T143 340T127 274V267H333V275Z" transform="translate(444,0)"/><path data-c="69" d="M69 609Q69 637 87 653T131 669Q154 667 171 652T188 609Q188 579 171 564T129 549Q104 549 87 564T69 609ZM247 0Q232 3 143 3Q132 3 106 3T56 1L34 0H26V46H42Q70 46 91 49Q100 53 102 60T104 102V205V293Q104 345 102 359T88 378Q74 385 41 385H30V408Q30 431 32 431L42 432Q52 433 70 434T106 436Q123 437 142 438T171 441T182 442H185V62Q190 52 197 50T232 46H255V0H247Z" transform="translate(888,0)"/><path data-c="6C" d="M42 46H56Q95 46 103 60V68Q103 77 103 91T103 124T104 167T104 217T104 272T104 329Q104 366 104 407T104 482T104 542T103 586T103 603Q100 622 89 628T44 637H26V660Q26 683 28 683L38 684Q48 685 67 686T104 688Q121 689 141 690T171 693T182 694H185V379Q185 62 186 60Q190 52 198 49Q219 46 247 46H263V0H255L232 1Q209 2 183 2T145 3T107 3T57 1L34 0H26V46H42Z" transform="translate(1166,0)"/></g><g data-mml-node="mo" transform="translate(12148.7,0)"><path data-c="28" d="M94 250Q94 319 104 381T127 488T164 576T202 643T244 695T277 729T302 750H315H319Q333 750 333 741Q333 738 316 720T275 667T226 581T184 443T167 250T184 58T225 -81T274 -167T316 -220T333 -241Q333 -250 318 -250H315H302L274 -226Q180 -141 137 -14T94 250Z"/></g><g data-mml-node="mi" transform="translate(12537.7,0)"><path data-c="1D44B" d="M42 0H40Q26 0 26 11Q26 15 29 27Q33 41 36 43T55 46Q141 49 190 98Q200 108 306 224T411 342Q302 620 297 625Q288 636 234 637H206Q200 643 200 645T202 664Q206 677 212 683H226Q260 681 347 681Q380 681 408 681T453 682T473 682Q490 682 490 671Q490 670 488 658Q484 643 481 640T465 637Q434 634 411 620L488 426L541 485Q646 598 646 610Q646 628 622 635Q617 635 609 637Q594 637 594 648Q594 650 596 664Q600 677 606 683H618Q619 683 643 683T697 681T738 680Q828 680 837 683H845Q852 676 852 672Q850 647 840 637H824Q790 636 763 628T722 611T698 593L687 584Q687 585 592 480L505 384Q505 383 536 304T601 142T638 56Q648 47 699 46Q734 46 734 37Q734 35 732 23Q728 7 725 4T711 1Q708 1 678 1T589 2Q528 2 496 2T461 1Q444 1 444 10Q444 11 446 25Q448 35 450 39T455 44T464 46T480 47T506 54Q523 62 523 64Q522 64 476 181L429 299Q241 95 236 84Q232 76 232 72Q232 53 261 47Q262 47 267 47T273 46Q276 46 277 46T280 45T283 42T284 35Q284 26 282 19Q279 6 276 4T261 1Q258 1 243 1T201 2T142 2Q64 2 42 0Z"/></g><g data-mml-node="mo" transform="translate(13611.9,0)"><path data-c="2212" d="M84 237T84 250T98 270H679Q694 262 694 250T679 230H98Q84 237 84 250Z"/></g><g data-mml-node="mn" transform="translate(14612.1,0)"><path data-c="30" d="M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z"/><path data-c="2E" d="M78 60Q78 84 95 102T138 120Q162 120 180 104T199 61Q199 36 182 18T139 0T96 17T78 60Z" transform="translate(500,0)"/><path data-c="35" d="M164 157Q164 133 148 117T109 101H102Q148 22 224 22Q294 22 326 82Q345 115 345 210Q345 313 318 349Q292 382 260 382H254Q176 382 136 314Q132 307 129 306T114 304Q97 304 95 310Q93 314 93 485V614Q93 664 98 664Q100 666 102 666Q103 666 123 658T178 642T253 634Q324 634 389 662Q397 666 402 666Q410 666 410 648V635Q328 538 205 538Q174 538 149 544L139 546V374Q158 388 169 396T205 412T256 420Q337 420 393 355T449 201Q449 109 385 44T229 -22Q148 -22 99 32T50 154Q50 178 61 192T84 210T107 214Q132 214 148 197T164 157Z" transform="translate(778,0)"/></g><g data-mml-node="mo" transform="translate(15890.1,0)"><path data-c="29" d="M60 749L64 750Q69 750 74 750H86L114 726Q208 641 251 514T294 250Q294 182 284 119T261 12T224 -76T186 -143T145 -194T113 -227T90 -246Q87 -249 86 -250H74Q66 -250 63 -250T58 -247T55 -238Q56 -237 66 -225Q221 -64 221 250T66 725Q56 737 55 738Q55 746 60 749Z"/></g><g data-mml-node="mo" transform="translate(16279.1,0)"><path data-c="5D" d="M22 710V750H159V-250H22V-210H119V710H22Z"/></g></g></g></g></g></g></svg></mjx-container></p><p>The original implementation of RoIAlign in Detectron doesn&apos;t subtract 0.5 in the end, so it&apos;s actually not very aligned.It turns out this detail does not affect accuracy of R-CNNs, because RoIAlign is applied on CNN features, and CNNis believed to be able to fit slightly misaligned features.</p><p>However, we have new use cases of RoIAlign in other places, e.g. to crop mask head training targets from the ground truth mask, soI fixed it in the detectron2 / torchvision RoIAlign with an <a href="https://pytorch.org/vision/stable/ops.html#torchvision.ops.roi_align" aria-label="Operators &#x2014; Torchvision main documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>aligned=True</code> option</a>.Its <a href="https://github.com/facebookresearch/detectron2/blob/60fd4885d7cfd52d4267d1da9ebb6b2b9a3fc937/tests/layers/test_roi_align.py#L14-L50" aria-label="test_roi_align.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">unittest</a>demonstrates how the old version is misaligned.</p><p>Btw, once we figured out the coordinate transform formula, it&apos;s easy to implement RoIAlign using <a href="https://pytorch.org/docs/stable/generated/torch.nn.functional.grid_sample.html" aria-label="torch.nn.functional.grid_sample &#x2014; PyTorch 1.13 documentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow"><code>grid_sample</code></a>.This shows that RoIAlign is nothing more than a fused bilinear sampling + averaging.Using <code>grid_sample</code> is about 10%-50% slower than the RoIAlign CUDA kernel.</p><h2 id="Paste-Mask">Paste Mask<a class="markdown-anchor" href="#Paste-Mask">&#xB6;</a></h2><p>Mask R-CNN is trained to predict masks of fixed resolution (e.g. 28x28) restrained inside given boxes(we call it <a href="https://github.com/facebookresearch/detectron2/blob/60fd4885d7cfd52d4267d1da9ebb6b2b9a3fc937/detectron2/structures/masks.py#L459-L464" aria-label="masks.py &#xB7; facebookresearch/detectron2" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&quot;RoIMask&quot;</a>).But in the end, we often want to obtain full-image masks.A &quot;paste mask&quot; operation is needed to paste the small RoIMask into the given region in the image.</p><img src="/blog/2021/Where-are-Pixels/paste.png" class="center" width="600"><p>This operation is an inverse of RoIAlign, so it should be implemented similar to our derivation above.In Detectron, this <a href="https://github.com/facebookresearch/Detectron/blob/1809dd41c1ffc881c0d6b1c16ea38d08894f8b6d/detectron/core/test.py#L812-L867" aria-label="test.py &#xB7; facebookresearch/Detectron" class="hint--top hint--rounded hint--no-animate hint--no-arrow">was implemented</a>with some magic rounding &amp; resize that are not exactly the inverse of RoIAlign.Fixing it in detectron2 increases the mask AP by 0.1~0.4.</p><h2 id="Point-based-Algorithms">Point-based Algorithms<a class="markdown-anchor" href="#Point-based-Algorithms">&#xB6;</a></h2><p>Obviously, the paste mask operation can introduce aliasing in the results due to the low resolution RoIMask.This is the motivation behind our work of<a href="https://arxiv.org/abs/1912.08193" aria-label="[1912.08193] PointRend: Image Segmentation as Rendering" class="hint--top hint--rounded hint--no-animate hint--no-arrow">PointRend</a>.</p><p>PointRend is a segmentation method that focuses on point-wise features, where a &quot;point&quot; isnot necessarily a pixel, but any real-valued coordinates.<a href="https://arxiv.org/abs/2104.06404" aria-label="[2104.06404] Pointly-Supervised Instance Segmentation" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Pointly-Supervised Instance Segmentation</a>, also from our team, uses point-wise annotations to train segmentation models.Both projects involve heavy use of point sampling and coordinate transforms.Having a clear and consistent convention of pixels and coordinates was important to their success.</p><h1 id="Summary">Summary<a class="markdown-anchor" href="#Summary">&#xB6;</a></h1><p>Due to some sloppy code in the early days of deep learning libraries,today we&apos;re facing multiple versions of resize functions.Together with the two different coordinate system conventions, they easily cause hidden bugs in computer vision code.</p><p>This article revisits these historical technical debts and shows how these fun details matter in modeling and training.I hope they will help you make proper choices.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Technically, an image is a function that maps a &lt;strong&gt;continuous&lt;/strong&gt; domain, e.g.
a box &lt;mjx-container class=&quot;MathJax&quot; jax=&quot;SVG&quot;&gt;&lt;svg style=&quot;vertical-align: -0.566ex;&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;13.21ex&quot; height=&quot;2.262ex&quot; role=&quot;img&quot; focusable=&quot;false&quot; viewbox=&quot;0 -750 5838.8 1000&quot;&gt;&lt;g stroke=&quot;currentColor&quot; fill=&quot;currentColor&quot; stroke-width=&quot;0&quot; transform=&quot;scale(1,-1)&quot;&gt;&lt;g data-mml-node=&quot;math&quot;&gt;&lt;g data-mml-node=&quot;mo&quot;&gt;&lt;path data-c=&quot;5B&quot; d=&quot;M118 -250V750H255V710H158V-210H255V-250H118Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mn&quot; transform=&quot;translate(278,0)&quot;&gt;&lt;path data-c=&quot;30&quot; d=&quot;M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(778,0)&quot;&gt;&lt;path data-c=&quot;2C&quot; d=&quot;M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mi&quot; transform=&quot;translate(1222.7,0)&quot;&gt;&lt;path data-c=&quot;1D44B&quot; d=&quot;M42 0H40Q26 0 26 11Q26 15 29 27Q33 41 36 43T55 46Q141 49 190 98Q200 108 306 224T411 342Q302 620 297 625Q288 636 234 637H206Q200 643 200 645T202 664Q206 677 212 683H226Q260 681 347 681Q380 681 408 681T453 682T473 682Q490 682 490 671Q490 670 488 658Q484 643 481 640T465 637Q434 634 411 620L488 426L541 485Q646 598 646 610Q646 628 622 635Q617 635 609 637Q594 637 594 648Q594 650 596 664Q600 677 606 683H618Q619 683 643 683T697 681T738 680Q828 680 837 683H845Q852 676 852 672Q850 647 840 637H824Q790 636 763 628T722 611T698 593L687 584Q687 585 592 480L505 384Q505 383 536 304T601 142T638 56Q648 47 699 46Q734 46 734 37Q734 35 732 23Q728 7 725 4T711 1Q708 1 678 1T589 2Q528 2 496 2T461 1Q444 1 444 10Q444 11 446 25Q448 35 450 39T455 44T464 46T480 47T506 54Q523 62 523 64Q522 64 476 181L429 299Q241 95 236 84Q232 76 232 72Q232 53 261 47Q262 47 267 47T273 46Q276 46 277 46T280 45T283 42T284 35Q284 26 282 19Q279 6 276 4T261 1Q258 1 243 1T201 2T142 2Q64 2 42 0Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(2074.7,0)&quot;&gt;&lt;path data-c=&quot;5D&quot; d=&quot;M22 710V750H159V-250H22V-210H119V710H22Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(2574.9,0)&quot;&gt;&lt;path data-c=&quot;D7&quot; d=&quot;M630 29Q630 9 609 9Q604 9 587 25T493 118L389 222L284 117Q178 13 175 11Q171 9 168 9Q160 9 154 15T147 29Q147 36 161 51T255 146L359 250L255 354Q174 435 161 449T147 471Q147 480 153 485T168 490Q173 490 175 489Q178 487 284 383L389 278L493 382Q570 459 587 475T609 491Q630 491 630 471Q630 464 620 453T522 355L418 250L522 145Q606 61 618 48T630 29Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(3575.1,0)&quot;&gt;&lt;path data-c=&quot;5B&quot; d=&quot;M118 -250V750H255V710H158V-210H255V-250H118Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mn&quot; transform=&quot;translate(3853.1,0)&quot;&gt;&lt;path data-c=&quot;30&quot; d=&quot;M96 585Q152 666 249 666Q297 666 345 640T423 548Q460 465 460 320Q460 165 417 83Q397 41 362 16T301 -15T250 -22Q224 -22 198 -16T137 16T82 83Q39 165 39 320Q39 494 96 585ZM321 597Q291 629 250 629Q208 629 178 597Q153 571 145 525T137 333Q137 175 145 125T181 46Q209 16 250 16Q290 16 318 46Q347 76 354 130T362 333Q362 478 354 524T321 597Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(4353.1,0)&quot;&gt;&lt;path data-c=&quot;2C&quot; d=&quot;M78 35T78 60T94 103T137 121Q165 121 187 96T210 8Q210 -27 201 -60T180 -117T154 -158T130 -185T117 -194Q113 -194 104 -185T95 -172Q95 -168 106 -156T131 -126T157 -76T173 -3V9L172 8Q170 7 167 6T161 3T152 1T140 0Q113 0 96 17Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mi&quot; transform=&quot;translate(4797.8,0)&quot;&gt;&lt;path data-c=&quot;1D44C&quot; d=&quot;M66 637Q54 637 49 637T39 638T32 641T30 647T33 664T42 682Q44 683 56 683Q104 680 165 680Q288 680 306 683H316Q322 677 322 674T320 656Q316 643 310 637H298Q242 637 242 624Q242 619 292 477T343 333L346 336Q350 340 358 349T379 373T411 410T454 461Q546 568 561 587T577 618Q577 634 545 637Q528 637 528 647Q528 649 530 661Q533 676 535 679T549 683Q551 683 578 682T657 680Q684 680 713 681T746 682Q763 682 763 673Q763 669 760 657T755 643Q753 637 734 637Q662 632 617 587Q608 578 477 424L348 273L322 169Q295 62 295 57Q295 46 363 46Q379 46 384 45T390 35Q390 33 388 23Q384 6 382 4T366 1Q361 1 324 1T232 2Q170 2 138 2T102 1Q84 1 84 9Q84 14 87 24Q88 27 89 30T90 35T91 39T93 42T96 44T101 45T107 45T116 46T129 46Q168 47 180 50T198 63Q201 68 227 171L252 274L129 623Q128 624 127 625T125 627T122 629T118 631T113 633T105 634T96 635T83 636T66 637Z&quot;/&gt;&lt;/g&gt;&lt;g data-mml-node=&quot;mo&quot; transform=&quot;translate(5560.8,0)&quot;&gt;&lt;path data-c=&quot;5D&quot; d=&quot;M22 710V750H159V-250H22V-210H119V710H22Z&quot;/&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/mjx-container&gt;, to intensities such as (R, G, B).
To store it on computer memory, an image is &lt;strong&gt;discretized&lt;/strong&gt; to an array &lt;code&gt;array[H][W]&lt;/code&gt;, where each element
&lt;code&gt;array[i][j]&lt;/code&gt; is a &lt;strong&gt;pixel&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;How does discretization work? How does a discrete pixel relate to the abstract notion of the underlying continuous image?
These basic questions play an important role in computer graphics &amp;amp; computer vision algorithms.&lt;/p&gt;
&lt;p&gt;This article discusses these low-level details, and how they affect our CNN models and deep learning libraries.
If you ever wonder which resize function to use or whether you should add/subtract 0.5 or 1 to some pixel coordinates,
you may find answers here.
Interestingly, these details have contributed to many accuracy improvements in Detectron
and Detectron2.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Research" scheme="https://ppwwyyxx.com/blog/tags/Research/"/>
    
    <category term="Computer Vision" scheme="https://ppwwyyxx.com/blog/tags/Computer-Vision/"/>
    
    <category term="Deep Learning" scheme="https://ppwwyyxx.com/blog/tags/Deep-Learning/"/>
    
    <category term="PyTorch" scheme="https://ppwwyyxx.com/blog/tags/PyTorch/"/>
    
  </entry>
  
  <entry>
    <title>Deep Learning Experiments and Claims</title>
    <link href="https://ppwwyyxx.com/blog/2021/DL-Experiments-and-Claims/"/>
    <id>https://ppwwyyxx.com/blog/2021/DL-Experiments-and-Claims/</id>
    <published>2021-05-23T07:00:00.000Z</published>
    <updated>2021-05-23T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<div class="message is-info my-custom-sidebar"><div class="message-header">     <i class="fas fa-info-circle mr-2"></i><p>&#x672C;&#x7CFB;&#x5217;&#x6587;&#x7AE0;</p></div><div class="message-body"><ol><li><a href="/blog/2021/DL-Experiments-and-Claims/" aria-label="Deep Learning Experiments and Claims" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Deep Learning Experiments and Claims</a></li><li><a href="/blog/2022/How-to-do-Ablation-Experiments/" aria-label="How To Do Ablation Experiments" class="hint--top hint--rounded hint--no-animate hint--no-arrow">How To Do Ablation Experiments</a></li></ol></div></div><p>&#x8FD9;&#x51E0;&#x5E74;&#x6765;, &#x4ECE; FAIR &#x7684;&#x51E0;&#x4F4D;&#x5927;&#x4F6C;&#x8EAB;&#x8FB9;&#x5B66;&#x4E60;&#x5230;&#x7684;&#x6700;&#x591A;&#x7684;&#x662F;&#x5BF9;&#x5F85; research &#x7684;&#x6001;&#x5EA6;. &#x56E0;&#x6B64;&#x8BF4;&#x8BF4;&#x5199; paper &#x548C;&#x505A;&#x5B9E;&#x9A8C;&#x7684;&#x4F53;&#x4F1A;.</p><h2 id="&#x5B9E;&#x9A8C;&#x4E0E;-claims">&#x5B9E;&#x9A8C;&#x4E0E; claims<a class="markdown-anchor" href="#&#x5B9E;&#x9A8C;&#x4E0E;-claims">&#xB6;</a></h2><p>&#x5B9E;&#x9A8C;&#x662F;&#x4E3A;&#x4E86;&#x8BC1;&#x660E;&#x6216;&#x5F3A;&#x5316;&#x6587;&#x7AE0;&#x91CC;&#x7ED9;&#x51FA;&#x7684; claim/hypothesis &#x7684;.</p><p><a href="https://www.dropbox.com/s/6izllk62dobao9n/ICCV19_generalized_rcnn_tutorial_ross_girshick.pptx?dl=0" aria-label="Dropbox - ICCV19_generalized_rcnn_tutorial_ross_girshick.pptx - Simplify your life" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Ross ICCV 2019 tutorial</a> &#x6700;&#x540E;&#x8C08;&#x4E86;&#x8C08;&#x600E;&#x4E48;&#x5199; paper. &#x7B2C; 126 &#x9875;&#x8BF4;, &#x6587;&#x7AE0;&#x4E2D;&#x6240;&#x6709;&#x7684; claim, &#x7406;&#x60F3;&#x60C5;&#x51B5;&#x4E0B;&#x90FD;&#x5E94;&#x8BE5;<strong>&#x8981;&#x4E48;&#x662F;&#x53C2;&#x8003;&#x6587;&#x732E;&#x4E2D;&#x5DF2;&#x6709;&#x7684;, &#x8981;&#x4E48;&#x662F;&#x88AB;&#x5B9E;&#x9A8C;&#x8BC1;&#x660E;&#x7684;</strong>.</p><span id="more"></span><p>&#x4E3E;&#x4E2A;&#x4F8B;&#x5B50;, <a href="https://arxiv.org/abs/1502.03167" aria-label="[1502.03167] Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift" class="hint--top hint--rounded hint--no-animate hint--no-arrow">BatchNorm paper</a> &#x7684;&#x5B9E;&#x9A8C;&#x53EF;&#x4EE5; claim &#x5F88;&#x591A;&#x4E1C;&#x897F;, &#x5305;&#x62EC; BatchNorm &#x8BA9;&#x7ED3;&#x679C;&#x5F88;&#x597D;, &#x5BF9;&#x521D;&#x59CB;&#x5316;&#x4E0D;&#x654F;&#x611F;, &#x5927; learning rate &#x4E5F;&#x4E0D;&#x70B8;. &#x4F46;&#x662F;&#x6587;&#x7AE0; claim BatchNorm &quot;reduce internal covariate shift&quot;, &#x5C31;&#x906D;&#x5230;&#x4E86;&#x4E00;&#x4E9B;&#x4EBA;&#x7684;&#x8D28;&#x7591;. &#x5982;&#x8457;&#x540D;&#x7684; <a href="https://www.youtube.com/watch?v=Qi1Yry33TQE&amp;t=1025s" aria-label="Ali Rahimi&apos;s talk at NIPS(NIPS 2017 Test-of-time award presentation) - YouTube" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Ali Rahimi Neurips 2017 test-of-time award presentation</a>(<a href="https://www.bilibili.com/video/BV1BW411Y78t" aria-label="Ali Rahimi&apos;s talk at NIPS(NIPS 2017 Test-of-time award presentation)_&#x54D4;&#x54E9;&#x54D4;&#x54E9;_bilibili" class="hint--top hint--rounded hint--no-animate hint--no-arrow">B &#x7AD9;</a>) &#x91CC;&#x7684;&#x4E94;&#x8FDE;&#x95EE; (&#x7B2C; 17 &#x5206;&#x949F;).BatchNorm paper &#x4E2D;&#x628A; &quot;internal covariate shift&quot; &#x7C97;&#x7565;&#x5B9A;&#x4E49;&#x4E3A; feature distribution &#x7684;&#x53D8;&#x5316;,  &#x4F46;&#x552F;&#x4E00;&#x4E00;&#x4E2A;&#x4E0E;&#x6B64;&#x76F8;&#x5173;&#x7684;&#x5B9E;&#x9A8C;&#x662F; Fig.1 (b)(c). &#x867D;&#x7136;&#x5B9E;&#x9A8C;&#x662F;&#x5408;&#x7406;&#x7684;, &#x4F46;&#x7ED3;&#x679C;&#x786E;&#x5B9E;&#x4E0D;&#x7B97;&#x4E0D;&#x592A;&#x663E;&#x8457;. &#x751A;&#x81F3; <a href="https://arxiv.org/abs/1803.08494" aria-label="[1803.08494] Group Normalization" class="hint--top hint--rounded hint--no-animate hint--no-arrow">GroupNorm paper</a> &#x7684; Fig.6 &#x5C55;&#x793A;&#x7684;&#x7ED3;&#x679C;&#x53EF;&#x80FD;&#x90FD;&#x66F4;&#x5F3A;.</p><p>Ross &#x5728; tutorial &#x4E2D;&#x7684;&#x5EFA;&#x8BAE;&#x662F;: &#x5982;&#x679C;&#x4E00;&#x4E2A; claim &#x6CA1;&#x6709;&#x5F97;&#x5230;&#x5B9E;&#x9A8C;&#x7684;&#x6709;&#x529B;&#x652F;&#x6301;,  &#x5728;&#x8868;&#x8FF0;&#x4E0A;&#x53EF;&#x4EE5;&#x5F31;&#x5316;&#x4E00;&#x4E9B;, &#x4F8B;&#x5982;&#x5199; &quot;intuitively/hypothetically, &#x5982;&#x4F55;&#x5982;&#x4F55;...&quot;.</p><h2 id="claim-&#x7ED3;&#x679C;-vs-claim-&#x539F;&#x7406;">claim &#x7ED3;&#x679C; vs. claim &#x539F;&#x7406;<a class="markdown-anchor" href="#claim-&#x7ED3;&#x679C;-vs-claim-&#x539F;&#x7406;"> &#xB6;</a></h2><p>&#x7C7B;&#x4F3C;&#x4E8E;&#x8FD9;&#x4E2A; BatchNorm &#x7684;&#x4F8B;&#x5B50;, &#x5F88;&#x591A; paper &#x4E2D;&#x4E00;&#x4E2A;&#x5E38;&#x89C1;&#x7684;&#x95EE;&#x9898;&#x662F;,  &#x5B9E;&#x9A8C;&#x53EA;&#x8BC1;&#x660E;&#x4E86;&#x7ED3;&#x679C;&#x597D;, &#x6587;&#x5B57;&#x91CC;&#x5374;&#x8BB2;&#x4E86;&#x4E2A;&#x6545;&#x4E8B;&#x5E76; overclaim &#x4E86;&#x7ED3;&#x679C;&#x597D;&#x7684;<strong>&#x539F;&#x7406;</strong>.</p><p>&#x8FD9;&#x91CC;&#x7684; claim &#x5E76;&#x4E0D;&#x4EC5;&#x4EC5;&#x6307;&#x660E;&#x663E;&#x7684; &quot;We claim ...&quot;. &#x8FD8;&#x53EF;&#x4EE5;&#x6709;&#x5176;&#x4ED6;&#x8868;&#x73B0;&#x5F62;&#x5F0F;:</p><ul><li>&#x6587;&#x4E2D;&#x4EFB;&#x4F55; statements &#x90FD;&#x53EF;&#x4EE5;&#x8BA4;&#x4E3A;&#x662F; claim. &#x4F5C;&#x8005;&#x6709;&#x65F6;&#x5BB9;&#x6613;&#x628A;&#x81EA;&#x5DF1;&#x731C;&#x60F3;&#x7684;&#x539F;&#x7406; (speculated stories) &#x5F53;&#x505A;&#x4E8B;&#x5B9E;&#x6765;&#x9648;&#x8FF0;.</li><li>&#x4F5C;&#x8005;&#x5BB9;&#x6613;&#x628A;&#x81EA;&#x5DF1;&#x7684; intuition &#x5F53;&#x505A;&#x65B9;&#x6CD5;&#x6700;&#x540E; work &#x7684;&#x539F;&#x7406;. &#x867D;&#x7136; intuition &#x662F;&#x6709;&#x7528;&#x7684;,  &#x4F46;&#x662F;&#x5199;&#x4F5C;&#x65F6;, &#x5FC3;&#x91CC;&#x8981;&#x6E05;&#x695A;&#x54EA;&#x4E9B;&#x53D1;&#x73B0;&#x662F;&#x6709;&#x8BC1;&#x636E;&#x7684;, &#x54EA;&#x4E9B;&#x662F;&#x51ED;&#x611F;&#x89C9;&#x7684;.</li><li>&#x8D77;&#x540D;&#x5B57;&#x7684;&#x65F6;&#x5019;, &#x7528;&#x8BCD;&#x5BB9;&#x6613;&#x4F7F;&#x7528;&#x4E00;&#x4E9B; suggestive language, &#x5C06;&#x539F;&#x7406;&#x6697;&#x793A;&#x7ED9;&#x8BFB;&#x8005;.&quot;attention&quot; &#x5C31;&#x662F;&#x4E00;&#x4E2A;&#x4F8B;&#x5B50;: &#x5B83;&#x7ED9;&#x4EBA;&#x4E00;&#x79CD;&#x6A21;&#x578B;&#x4F1A;&#x4E3B;&#x52A8; &quot;&#x5173;&#x6CE8;&quot; &#x91CD;&#x8981;&#x7684; feature &#x7684;&#x611F;&#x89C9;,  &#x4F46;&#x662F;&#x5230;&#x5E95;&#x662F;&#x5426;&#x5982;&#x6B64;, &#x662F;&#x9700;&#x8981;&#x5B9E;&#x9A8C;&#x8BC1;&#x660E;&#x7684;. &#x6BD5;&#x7ADF;&#x5B83;&#x8BF4;&#x5230;&#x5E95;&#x8FD8;&#x662F;&#x5728;&#x505A;&#x77E9;&#x9635;&#x4E58;&#x6CD5;.</li></ul><p><a href="https://www.youtube.com/watch?v=YevQi-bW7NY" aria-label="ICCV 2019 pre-registration workshop - Zack Lipton - YouTube" class="hint--top hint--rounded hint--no-animate hint--no-arrow">Troubling Trends in Machine Learning Scholarship</a> &#x7684; talk &#x91CC;&#x4E5F;&#x6307;&#x51FA;&#x4E86;&#x8FD9;&#x4E9B;&#x95EE;&#x9898;.</p><p>&#x7531;&#x4E8E;&#x6574;&#x4E2A;&#x9886;&#x57DF;&#x8FD8;&#x662F;&#x4EE5;&#x5B9E;&#x9A8C;&#x9A71;&#x52A8;&#x7684;, &#x5BF9;&#x539F;&#x7406;&#x7684;&#x7814;&#x7A76;&#x4E0D;&#x6DF1;,  &#x6240;&#x4EE5;&#x539F;&#x7406;&#x5E38;&#x5E38;&#x90FD;&#x662F; speculation / intuition, &#x5728;&#x5199;&#x4F5C;&#x65F6;&#x5BB9;&#x6613; overclaim. &#x56E0;&#x6B64;&#x8981;&#x6CE8;&#x610F;&#x5F31;&#x5316;&#x8868;&#x8FF0;.</p><h2 id="&#x4ECE;-claim-&#x7ED3;&#x679C;&#x5230;-claim-&#x539F;&#x7406;">&#x4ECE; claim &#x7ED3;&#x679C;&#x5230; claim &#x539F;&#x7406;<a class="markdown-anchor" href="#&#x4ECE;-claim-&#x7ED3;&#x679C;&#x5230;-claim-&#x539F;&#x7406;"> &#xB6;</a></h2><p>&#x4ECE;&#x7ED3;&#x679C;&#x6DF1;&#x5165;&#x5230;&#x539F;&#x7406;, &#x5BF9;&#x5E94;&#x7684;&#x5B9E;&#x9A8C;&#x5927;&#x7EA6;&#x6709;&#x8FD9;&#x6837;&#x7684;&#x51E0;&#x7C7B;:</p><ol><li><p>System-level &#x5B9E;&#x9A8C;, &#x5C31;&#x662F;&#x76F4;&#x63A5;&#x8DDF;&#x5DF2;&#x6709;&#x7684;&#x7ED3;&#x679C;&#x6BD4;. &#x8FD9;&#x7C7B;&#x5B9E;&#x9A8C;&#x53EF;&#x4EE5;&#x6CA1;&#x6709;&#x63A7;&#x5236;&#x53D8;&#x91CF;: &#x4E24;&#x4E2A;&#x975E;&#x5E38;&#x4E0D;&#x540C;&#x7684;&#x65B9;&#x6CD5; (e.g. SVM vs. CNN) &#x7167;&#x6837;&#x53EF;&#x4EE5;&#x5728;&#x5C3D;&#x91CF;&#x76F8;&#x4F3C;&#x7684;&#x8BBE;&#x5B9A;&#x4E0B;&#x6BD4;&#x8F83;&#x7ED3;&#x679C;. &#x5B83;&#x8BC1;&#x660E;&#x7684; claim &#x662F; &quot; &#x8FD9;&#x6574;&#x4E2A;<strong>&#x7CFB;&#x7EDF;</strong>&#x80FD;&#x591F;&#x8FBE;&#x5230;&#x597D;&#x7684;&#x6027;&#x80FD; &quot;.</p></li><li><p>Ablations, &#x4E5F;&#x5373;&#x63A7;&#x5236;&#x53D8;&#x91CF;, &#x7528;&#x6765;&#x8BC1;&#x660E; &quot; &#x7CFB;&#x7EDF;<strong>&#x7531;&#x4E8E;&#x8FD9;&#x4E2A;&#x65B9;&#x6CD5;</strong> (&#x800C;&#x4E0D;&#x662F;&#x5176;&#x4ED6;&#x56E0;&#x7D20;) &#x6027;&#x80FD;&#x5F97;&#x5230;&#x63D0;&#x9AD8; &quot;. &#x8FD9;&#x5C31;&#x9700;&#x8981;&#x4E25;&#x683C;&#x63A7;&#x5236;&#x5176;&#x4ED6;&#x56E0;&#x7D20;.</p></li><li><p>&#x6DF1;&#x5165;&#x5206;&#x6790;, &#x8BD5;&#x56FE;&#x89E3;&#x91CA; &quot;<strong> &#x4E3A;&#x4EC0;&#x4E48;</strong>&#x67D0;&#x79CD;&#x65B9;&#x6CD5;&#x80FD;&#x591F;&#x63D0;&#x9AD8;&#x6027;&#x80FD; &quot;. &#x4F46;&#x662F;&#x5728; deep learning &#x4E2D;, &#x7531;&#x4E8E;&#x7406;&#x8BBA;&#x5DE5;&#x5177;&#x7684;&#x7F3A;&#x4E4F;, &#x8FD9;&#x7C7B;&#x5B9E;&#x9A8C;&#x5F80;&#x5F80;&#x4E0D;&#x5BB9;&#x6613;&#x8BBE;&#x8BA1;.</p></li></ol><p>&#x5982;&#x679C;&#x4E00;&#x7BC7;&#x8BBA;&#x6587;&#x63D0;&#x51FA;&#x4E86;&#x5168;&#x65B0;&#x7684;&#x7CFB;&#x7EDF;, &#x7ED3;&#x679C;&#x8FD8;&#x7279;&#x522B;&#x597D; (&#x4F8B;&#x5982; AlexNet, Bert, AlphaGo &#x8FD9;&#x79CD; breakthrough &#x7EA7;&#x522B;), &#x90A3;&#x4E48;&#x5373;&#x4F7F;&#x4EC5;&#x6709; system-level &#x7684;&#x5B9E;&#x9A8C;&#x4E5F;&#x6CA1;&#x5173;&#x7CFB;: &#x4EE5;&#x540E;&#x603B;&#x4F1A;&#x6709;&#x522B;&#x4EBA;&#x53BB;&#x66F4;&#x6DF1;&#x5165;&#x7684;&#x7814;&#x7A76;&#x7684;.Yann LeCun &#x9488;&#x5BF9; Ali Rahimi &#x7684; presentation<a href="https://www.facebook.com/yann.lecun/posts/10154938130592143" aria-label="Yann LeCun - My take on Ali Rahimi&apos;s &quot;Test of Time&quot; award... | Facebook" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &#x66FE;&#x7ECF;&#x8BF4;&#x8FC7;</a>,  &#x5386;&#x53F2;&#x4E0A; engineering &#x5F80;&#x5F80;&#x6BD4; science &#x5FEB;&#x4E00;&#x6B65;, &#x5F88;&#x591A;&#x79D1;&#x6280;&#x7684;&#x53D1;&#x5C55;&#x8FC7;&#x7A0B;&#x90FD;&#x662F;&#x5148;&#x505A; work &#x4E86;&#x518D;&#x53BB;&#x7814;&#x7A76;&#x4E3A;&#x4EC0;&#x4E48;&#x7684;.</p><p>&#x53E6;&#x4E00;&#x4E2A;&#x6781;&#x7AEF;&#x662F;, &#x5982;&#x679C;&#x4E00;&#x7BC7;&#x8BBA;&#x6587;&#x5BF9;&#x7ED3;&#x679C;&#x6BEB;&#x65E0;&#x63D0;&#x9AD8;,  &#x4F46;&#x662F;&#x901A;&#x8FC7;&#x8BE6;&#x7EC6;&#x7684;&#x5206;&#x6790;&#x5E2E;&#x52A9;&#x8BFB;&#x8005;&#x7406;&#x89E3;&#x4E86;&#x66F4;&#x591A;&#x539F;&#x7406;, &#x6216;&#x63D0;&#x4F9B;&#x4E86;&#x7406;&#x89E3;&#x539F;&#x7406;&#x7684;&#x89C6;&#x89D2;&#x548C;&#x5DE5;&#x5177;,  &#x90A3;&#x540C;&#x6837;&#x4E5F;&#x662F;&#x5F88;&#x597D;&#x7684;&#x5DE5;&#x4F5C;.</p><p>&#x5F53;&#x7136;, &#x5927;&#x90E8;&#x5206;&#x5DE5;&#x4F5C;&#x65E2;&#x6CA1;&#x6709; breakthrough &#x7EA7;&#x522B;&#x7684;&#x7ED3;&#x679C;, &#x4E5F;&#x5F88;&#x96BE;&#x7ED9;&#x51FA;&#x4EE4;&#x4EBA;&#x4FE1;&#x670D;&#x7684;&#x5206;&#x6790; (&#x6BD5;&#x7ADF;&#x70BC;&#x4E39;), &#x56E0;&#x6B64;&#x5F80;&#x5F80;&#x9700;&#x8981;&#x591A;&#x79CD;&#x7C7B;&#x578B;&#x7684;&#x5B9E;&#x9A8C;&#x7ED3;&#x5408;: &#x6709;&#x4EC0;&#x4E48;&#x6837;&#x7684;&#x5B9E;&#x9A8C;, &#x51B3;&#x5B9A;&#x4E86;&#x8BBA;&#x6587;&#x80FD;&#x505A;&#x51FA;&#x4EC0;&#x4E48; claim, &#x8FDB;&#x4E00;&#x6B65;&#x624D;&#x80FD; justify &#x8BBA;&#x6587;&#x7684;&#x4EF7;&#x503C;.</p><p>&#x4E3E;&#x4E2A;&#x4F8B;&#x5B50;, &#x6BCF;&#x4E2A;&#x4EBA;&#x90FD;&#x719F;&#x6089;&#x7684; <a href="https://arxiv.org/abs/1512.03385" aria-label="[1512.03385] Deep Residual Learning for Image Recognition" class="hint--top hint--rounded hint--no-animate hint--no-arrow">ResNet paper</a>, &#x540C;&#x6837;&#x7684;&#x6A21;&#x578B;, &#x6587;&#x7AE0;&#x53EF;&#x80FD;&#x6709;&#x4E0B;&#x9762;&#x51E0;&#x79CD;&#x4E0D;&#x540C;&#x5199;&#x6CD5;, &#x5BF9;&#x5E94;&#x4E0D;&#x540C;&#x7684;&#x5B9E;&#x9A8C;&#x548C; claims:</p><ol><li><p><u><em>&#x6211;&#x4EEC;&#x8BBE;&#x8BA1;&#x4E86;&#x4E00;&#x7C7B; VGG &#x7684;&#x53D8;&#x79CD;, &#x6709;&#x4E94;&#x4E2A;&#x6A21;&#x578B;&#x53EB;&#x505A; &quot;MSRANet {18,34,50,101,152}&quot;, claim &#x5B83;&#x4EEC;&#x8FBE;&#x5230;&#x4E86; SOTA. &#x5B9E;&#x9A8C;&#x5185;&#x5BB9;&#x662F;&#x8DDF;&#x4EE5;&#x524D;&#x7684; SOTA &#x6BD4;&#x4E00;&#x6BD4;</em></u>.</p><p>&#x5176;&#x5B9E; AlexNet &#x6587;&#x7AE0;&#x5C31;&#x662F;&#x8FD9;&#x4E48;&#x5199;&#x7684;, &#x4F46;&#x662F; AlexNet &#x7684; SOTA &#x7ED3;&#x679C;&#x672C;&#x8EAB;&#x5C31;&#x662F;&#x4E00;&#x4E2A;&#x5DE8;&#x5927;&#x7684; breakthrough. &#x5982;&#x679C; ResNet &#x4E5F;&#x8FD9;&#x4E48;&#x5199;, &#x5F71;&#x54CD;&#x529B;&#x4F1A;&#x5C0F;&#x5F97;&#x591A;,  &#x6BD5;&#x7ADF; SOTA &#x662F;&#x77ED;&#x6682;&#x7684;. &#x8BF4;&#x7684;&#x4E0D;&#x597D;&#x542C;&#x4E00;&#x70B9;,  &#x8FD9;&#x4E2A;&#x6A21;&#x578B;&#x88AB;&#x4E0B;&#x4E00;&#x4E2A;&#x4EBA;&#x62FF;&#x8FC7;&#x53BB;&#x6539;&#x4E00;&#x6539;, &#x518D;&#x6362;&#x4E2A;&#x540D;&#x5B57;&#x6210;&#x4E86;&#x65B0;&#x7684; SOTA, &#x53EF;&#x80FD;&#x5C31;&#x6CA1;&#x6709;&#x4EBA;&#x8BB0;&#x5F97; &quot;MSRANet&quot; &#x4E86;.2013 &#x5E74;&#x7684;<a href="https://arxiv.org/abs/1311.2901" aria-label="[1311.2901] Visualizing and Understanding Convolutional Networks" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> ZF-Net</a> &#x5927;&#x6982;&#x5C31;&#x662F;&#x8FD9;&#x4E48;&#x4E00;&#x4E2A;&#x5730;&#x4F4D;.</p></li><li><p><u><em>&#x6211;&#x4EEC;&#x8BBE;&#x8BA1;&#x4E86;&#x5305;&#x542B; residual connection &#x7684; &quot;bottleneck block / basic block&quot; &#x65B9;&#x6CD5;, &#x80FD;&#x591F;&#x63D0;&#x9AD8;&#x6A21;&#x578B;&#x6027;&#x80FD;. &#x5B9E;&#x9A8C;&#x505A;&#x4E00;&#x4E9B; ablations, &#x786E;&#x8BA4;&#x4E86;&#x8FD9;&#x4E2A; claim</em></u>.</p><p>&#x8FD9;&#x4E2A;&#x65B9;&#x6CD5;&#x6709;&#x4E00;&#x4E9B;&#x80FD;&#x591F;&#x81EA;&#x5706;&#x5176;&#x8BF4;&#x7684; intuition, ablations &#x8BC1;&#x660E;&#x6709;&#x6548;, &#x540C;&#x65F6;&#x4E5F;&#x80FD;&#x8FBE;&#x5230; SOTA. &#x8FD9;&#x5C31;&#x7C7B;&#x4F3C;&#x4E8E;&#x5927;&#x591A;&#x6570;&#x7684;&#x597D; paper.</p></li><li><p>&#x800C; ResNet &#x539F;&#x6587;&#x7684;&#x5C42;&#x6B21;&#x5C31;&#x66F4;&#x9AD8;&#x4E00;&#x4E9B;&#x4E86;: &#x6587;&#x7AE0;&#x6807;&#x9898;&#x8BF4;&#x7684;&#x662F; &quot;residual learning&quot;, &#x5185;&#x5BB9;&#x5F3A;&#x8C03;&#x7684;&#x662F; <strong>residual connection &#x5BF9;&#x4F18;&#x5316;&#x7684;&#x597D;&#x5904;</strong>. &#x5176;&#x4ED6;&#x7684;&#x5404;&#x79CD; block, ResNet-50, &#x53EA;&#x662F;&#x6D4B;&#x8BD5;&#x8FD9;&#x4E2A; idea/claim &#x7684;&#x624B;&#x6BB5;.</p><p>&#x8FD9;&#x4E2A;&#x5927; claim &#x5BF9;&#x5B9E;&#x9A8C;&#x548C;&#x5206;&#x6790;&#x7684;&#x8981;&#x6C42;&#x5C31;&#x66F4;&#x9AD8;&#x4E86;, &#x4EE5;&#x81F3;&#x4E8E; ResNet &#x6CA1;&#x5206;&#x6790;&#x5B8C;,  &#x5230;&#x4E86; <a href="https://arxiv.org/abs/1603.05027" aria-label="[1603.05027] Identity Mappings in Deep Residual Networks" class="hint--top hint--rounded hint--no-animate hint--no-arrow">ResNet-v2 (pre-activation)</a> &#x7EE7;&#x7EED;&#x628A;&#x8FD9;&#x4E2A;&#x6545;&#x4E8B;&#x8BB2;&#x4E86;&#x4E0B;&#x53BB;, &#x4E13;&#x95E8;&#x5206;&#x6790; residual connection &#x7684;&#x91CD;&#x8981;&#x6027;.</p><p>&#x6700;&#x540E;, &#x5B9E;&#x8DF5;&#x8BC1;&#x660E; residual connection &#x786E;&#x5B9E;&#x662F; deep learning &#x4ECA;&#x5929;&#x4E3A;&#x6B62;&#x6700;&#x91CD;&#x8981;&#x7684;&#x53D1;&#x660E;&#x4E4B;&#x4E00;, &#x51E0;&#x4E4E;&#x7EDF;&#x6CBB;&#x4E86;&#x6240;&#x6709;&#x9886;&#x57DF;, CNN &#x548C; Transformer &#x90FD;&#x79BB;&#x4E0D;&#x5F00;&#x5B83;. &#x8FD9;&#x8FDC;&#x6BD4; ResNet-50 &#x5230;&#x5E95;&#x957F;&#x4EC0;&#x4E48;&#x6837;, &#x91CC;&#x9762;&#x7684; block &#x5230;&#x5E95;&#x662F;&#x4EC0;&#x4E48;&#x8981;&#x91CD;&#x8981;&#x5F97;&#x591A;.</p></li></ol><h2 id="&#x9A6C;&#x540E;&#x70AE;-Harking">&#x9A6C;&#x540E;&#x70AE; / Harking<a class="markdown-anchor" href="#&#x9A6C;&#x540E;&#x70AE;-Harking">&#xB6;</a></h2><p>&#x4EBA;&#x4EEC;&#x4E00;&#x822C;&#x5E0C;&#x671B;&#x63A2;&#x7D22;&#x7ED3;&#x679C;&#x80CC;&#x540E;&#x7684;&#x539F;&#x7406;, &#x56E0;&#x4E3A;&#x8FD9;&#x662F;&#x79D1;&#x5B66;&#x7814;&#x7A76;&#x7684;&#x672C;&#x8D28;, &#x4E5F;&#x8BA9;&#x79D1;&#x7814;&#x5DE5;&#x4F5C;&#x66F4;&#x6709;&#x4EF7;&#x503C;. &#x8FD9;&#x5BFC;&#x81F4;&#x4E86;&#x4E0A;&#x9762;&#x63D0;&#x5230;&#x7684;&#x90A3;&#x79CD; overclaim &#x73B0;&#x8C61;.</p><p>&#x8FD8;&#x6709;&#x53E6;&#x4E00;&#x79CD;&#x73B0;&#x8C61;, &#x4FD7;&#x79F0;&#x9A6C;&#x540E;&#x70AE;, &#x6216; &quot;Harking&quot; (Hypothesizing After Results are Known). &#x4E5F;&#x5373;&#x5148;&#x505A;&#x5B9E;&#x9A8C;, &#x5728;&#x6709;&#x7ED3;&#x679C;&#x4E4B;&#x540E; &quot;&#x770B;&#x56FE;&#x8BF4;&#x8BDD; / &#x5F3A;&#x884C;&#x89E3;&#x91CA;&quot;, &#x627E;&#x4E00;&#x4E2A;&#x53EF;&#x4EE5;&#x88AB;&#x8FD9;&#x4E2A;&#x5B9E;&#x9A8C;&#x8BC1;&#x660E;&#x7684;&#x7ED3;&#x8BBA;, &#x6216;&#x53EF;&#x4EE5;&#x89E3;&#x91CA;&#x5B9E;&#x9A8C;&#x7ED3;&#x679C;&#x7684;&#x731C;&#x60F3; / &#x539F;&#x7406;. &#x7136;&#x800C;&#x5728;&#x5199;&#x4F5C;&#x65F6;, &#x5148; &quot;We hypothesize/claim ...&quot;, &#x518D; &quot;&#x8BBE;&#x8BA1;&#x5B9E;&#x9A8C;&quot; &#x8BC1;&#x660E;&#x81EA;&#x5DF1;&#x7684; hypothesis/claim.</p><p><a href="https://www.wikiwand.com/en/HARKing" aria-label="HARKing - Wikiwand" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&quot;Harking&quot;</a> &#x8FD9;&#x4E2A;&#x8BCD;&#x6700;&#x65E9;&#x51FA;&#x73B0;&#x5728;&#x5FC3;&#x7406;&#x5B66;&#x7814;&#x7A76;&#x91CC;. &#x5728; deep learning &#x4E2D;&#x4E5F;&#x6709;&#x5BF9;&#x6B64;&#x7684;&#x6279;&#x8BC4;: <a href="https://arxiv.org/abs/1904.07633" aria-label="[1904.07633] HARK Side of Deep Learning -- From Grad Student Descent to Automated Machine Learning" class="hint--top hint--rounded hint--no-animate hint--no-arrow">HARK Side of Deep Learning</a>. &#x5B83;&#x4E4B;&#x6240;&#x4EE5;&#x662F;&#x4E00;&#x79CD;&#x4E0D;&#x592A;&#x597D;&#x7684; research practice, &#x662F;&#x56E0;&#x4E3A;&#x5B83;&#x80CC;&#x79BB;&#x4E86;&#x79D1;&#x5B66;&#x7814;&#x7A76;&#x7684;&#x76EE;&#x6807;. &#x79D1;&#x5B66;&#x4E0E;&#x8FF7;&#x4FE1;&#x7684;&#x4E00;&#x5927;&#x533A;&#x522B;, &#x662F;&#x79D1;&#x5B66;&#x5E94;&#x5F53;&#x4E0D;&#x4EC5;&#x80FD;&#x591F;&#x89E3;&#x91CA;&#x5DF2;&#x77E5;, &#x8FD8;&#x80FD;&#x591F;&#x9884;&#x6D4B;&#x672A;&#x77E5;.&quot;&#x5929;&#x4E0A;&#x6253;&#x96F7;&#x662F;&#x96F7;&#x516C;&#x7535;&#x6BCD;&#x751F;&#x6C14;&#x4E86;&quot; &#x5C31;&#x662F;&#x5BF9;&#x5929;&#x6C14;&#x73B0;&#x8C61;&#x7684;&#x4E00;&#x79CD;&#x89E3;&#x91CA;, &#x4F46;&#x5B83;&#x4E0D;&#x662F;&#x79D1;&#x5B66;. &#x5982;&#x679C;&#x4E00;&#x4E2A;&#x5DE5;&#x4F5C;&#x4EC5;&#x4EC5;&#x8FFD;&#x6C42;&#x627E;&#x5230;&#x4E00;&#x4E2A;&#x89E3;&#x91CA;, &#x6765;&#x4E0E;&#x73B0;&#x6709;&#x7684;&#x5C11;&#x91CF;&#x5B9E;&#x9A8C;&#x7ED3;&#x679C;&#x517C;&#x5BB9;&#x7684;&#x8BDD;,  &#x8FD9;&#x4E2A;&#x89E3;&#x91CA;&#x672A;&#x5FC5;&#x5728;&#x5176;&#x4ED6;&#x5B9E;&#x9A8C;&#x4E2D;&#x9002;&#x7528;, &#x56E0;&#x6B64;&#x53EF;&#x80FD;&#x4E0D;&#x662F;&#x4E00;&#x4E2A;&#x79D1;&#x5B66;&#x7684;&#x7ED3;&#x8BBA;.</p><p>&#x7136;&#x800C;, &#x5982;&#x4ECA; deep learning &#x662F;&#x4EE5;&#x5B9E;&#x9A8C;&#x4E3A;&#x57FA;&#x7840;&#x7684;&#x79D1;&#x5B66;. &#x5176;&#x7814;&#x7A76;&#x8FC7;&#x7A0B;&#x786E;&#x5B9E;&#x7ECF;&#x5E38;&#x8981;&#x5148;&#x505A;&#x5B9E;&#x9A8C;, &#x770B;&#x5230;&#x7ED3;&#x679C;, &#x624D;&#x80FD;&#x63D0;&#x51FA;&#x731C;&#x60F3;&#x6216;&#x51B3;&#x5B9A;&#x4E0B;&#x4E00;&#x6B65;&#x7684;&#x5B9E;&#x9A8C;. &#x56E0;&#x6B64;&#x9A6C;&#x540E;&#x70AE;&#x884C;&#x4E3A;&#x4E00;&#x822C;&#x90FD;&#x5B58;&#x5728;. &#x4F46;&#x662F;, &#x4E00;&#x4E2A;&#x79D1;&#x5B66;&#x7684;&#x9A6C;&#x540E;&#x70AE;&#x7814;&#x7A76;&#x8005;&#x5E94;&#x5F53;&#x5728;&#x5236;&#x9020;&#x51FA;&#x4E00;&#x4E2A;&#x731C;&#x60F3;&#x6216;&#x7ED3;&#x8BBA;&#x4E4B;&#x540E;,  &#x518D;&#x53BB;&#x65B0;&#x7684;&#x5B9E;&#x9A8C;&#x91CC;&#x5C1D;&#x8BD5;&#x9884;&#x6D4B;&#x4E00;&#x4E0B;&#x672A;&#x77E5;, &#x6253;&#x4E00;&#x4E2A;&#x9A6C;&#x524D;&#x70AE;.</p><p>&#x4E00;&#x4E2A;&#x6211;&#x81EA;&#x5DF1;&#x6DF1;&#x6709;&#x611F;&#x89E6;&#x7684;&#x4F8B;&#x5B50;, &#x662F;&#x6211;&#x53C2;&#x4E0E;&#x7684;&#x8FD9;&#x7BC7;<a href="https://arxiv.org/abs/1812.03411" aria-label="[1812.03411] Feature Denoising for Improving Adversarial Robustness" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> Feature Denoising for Improving Adversarial Robustness</a>. &#x8D77;&#x521D;&#x6211;&#x4EEC;&#x731C;&#x6D4B; non-local &#x4F1A;&#x5E2E;&#x52A9; adversarial training, &#x5E76;&#x5F88;&#x5FEB;&#x5F97;&#x5230;&#x4E86;&#x5B9E;&#x9A8C;&#x9A8C;&#x8BC1;. &#x8FD9;&#x65F6;&#x5019;, &#x4E00;&#x4E2A;&#x4E2D;&#x89C4;&#x4E2D;&#x77E9;&#x7684; paper &#x5199;&#x6CD5;&#x5C31;&#x662F; claim &quot;non-local &#x5BF9; adversarial training &#x6709;&#x7528;&quot;, &#x5B9E;&#x9A8C;&#x5C31;&#x662F;&#x6709; / &#x65E0; non-local &#x7684; ablations.</p><p>&#x6709;&#x610F;&#x601D;&#x7684;&#x662F;, &#x6211;&#x4EEC;&#x7684; claim &#x662F; &quot;denoising layers &#x5BF9; adversarial training &#x6709;&#x7528;&quot;. &#x8FD9;&#x662F;&#x4E00;&#x4E2A;&#x66F4;&#x5927;&#x7684; claim, &#x800C;&#x4E14;&#x5176;&#x5B9E;&#x662F;&#x4E00;&#x4E2A;&#x9A6C;&#x540E;&#x70AE;&#x7684;&#x7ED3;&#x8BBA;, &#x56E0;&#x4E3A;&#x5B83;&#x4EC5;&#x4EC5;&#x662F; intuitively &#x80FD;&#x89E3;&#x91CA;&#x5DF2;&#x6709;&#x7684;&#x5B9E;&#x9A8C;&#x7ED3;&#x679C;:non-local &#x5728;&#x4F20;&#x7EDF; vision &#x91CC;&#x7528;&#x4F5C; denoising, &#x800C;&#x5BF9;&#x6297;&#x6837;&#x672C;&#x7684;&#x6270;&#x52A8;&#x53EF;&#x4EE5;&#x770B;&#x505A;&#x4E3A; noise.</p><p>&#x4E3A;&#x4E86;&#x652F;&#x6301;&#x8FD9;&#x4E2A;&#x66F4;&#x5927;&#x7684; claim, &#x6211;&#x4EEC;&#x8981;&#x6253;&#x51E0;&#x4E2A;&#x9A6C;&#x524D;&#x70AE;, &#x7528;&#x5B83;&#x53BB;&#x9884;&#x6D4B;&#x66F4;&#x591A;&#x7684;&#x5B9E;&#x9A8C;:</p><ul><li>&#x7528; visualization &#x5C55;&#x793A;&#x4E86; non-local &#x786E;&#x5B9E;&#x5BF9; feature &#x6709; denoise &#x7684;&#x6548;&#x679C;.</li><li>&#x4F7F;&#x7528;&#x4E86;&#x5176;&#x4ED6;&#x7684; denoising layer (median pooling, mean pooling, bilateral filter), &#x6765;&#x9A8C;&#x8BC1;&#x4ED6;&#x4EEC;&#x90FD;&#x6BD4;&#x52A0;&#x666E;&#x901A;&#x7684; conv layer &#x66F4;&#x6709;&#x7528;.</li></ul><p>&#x8FD9;&#x4E9B;&#x5B9E;&#x9A8C;&#x662F;&#x5728;&#x6709;&#x4E86; claim &#x4E4B;&#x540E;, &#x4E13;&#x95E8;&#x4E3A;&#x4E86;&#x9A8C;&#x8BC1;&#x8FD9;&#x4E2A; claim &#x800C;&#x8BBE;&#x8BA1;&#x7684;&#x5B9E;&#x9A8C;. &#x5B83;&#x4EEC;&#x7684;&#x6B63;&#x9762;&#x7ED3;&#x679C;&#x8BA9;&#x6211;&#x4EEC;&#x5BF9;&#x8FD9;&#x4E2A; claim &#x66F4;&#x6709;&#x4FE1;&#x5FC3;, &#x5373;&#x4FBF;&#x5B83;&#x6700;&#x521D;&#x662F;&#x9760;&#x9A6C;&#x540E;&#x70AE;&#x548C;&#x76F4;&#x89C9;&#x731C;&#x51FA;&#x6765;&#x7684;. &#x4ECE;&#x9A6C;&#x540E;&#x70AE;&#x5230;&#x9A6C;&#x524D;&#x70AE;, &#x662F; researcher &#x7684;&#x81EA;&#x6211;&#x8981;&#x6C42;.</p><p>&#x597D;&#x7684;&#x7814;&#x7A76;&#x5E94;&#x5F53;&#x80FD;&#x7ECF;&#x53D7;&#x65F6;&#x95F4;&#x548C;&#x5B9E;&#x8DF5;&#x7684;&#x68C0;&#x9A8C;,  &#x56E0;&#x6B64;&#x4E00;&#x4E2A;&#x597D;&#x7684;&#x7814;&#x7A76;&#x8005;&#x5E94;&#x81EA;&#x5DF1;&#x5148;&#x5BA1;&#x89C6;&#x81EA;&#x5DF1;&#x7684; claim, &#x5E76;&#x771F;&#x5FC3;&#x7684;&#x5C1D;&#x8BD5;&#x7528;&#x5B9E;&#x9A8C;&#x68C0;&#x9A8C;&#x5B83;&#x4EEC;. &#x6709;&#x673A;&#x4F1A;&#x518D;&#x8BE6;&#x7EC6;&#x5199;&#x5199;&#x600E;&#x4E48;&#x8BBE;&#x8BA1;&#x79D1;&#x5B66;&#x7684;&#x5B9E;&#x9A8C;.</p>]]></content>
    
    
    <summary type="html">
&lt;p&gt;&amp;#x8FD9;&amp;#x51E0;&amp;#x5E74;&amp;#x6765;, &amp;#x4ECE; FAIR &amp;#x7684;&amp;#x51E0;&amp;#x4F4D;&amp;#x5927;&amp;#x4F6C;&amp;#x8EAB;&amp;#x8FB9;&amp;#x5B66;&amp;#x4E60;&amp;#x5230;&amp;#x7684;&amp;#x6700;&amp;#x591A;&amp;#x7684;&amp;#x662F;&amp;#x5BF9;&amp;#x5F85; research &amp;#x7684;&amp;#x6001;&amp;#x5EA6;.
 &amp;#x56E0;&amp;#x6B64;&amp;#x8BF4;&amp;#x8BF4;&amp;#x5199; paper &amp;#x548C;&amp;#x505A;&amp;#x5B9E;&amp;#x9A8C;&amp;#x7684;&amp;#x4F53;&amp;#x4F1A;.&lt;/p&gt;
&lt;h2 id=&quot;&amp;#x5B9E;&amp;#x9A8C;&amp;#x4E0E;-claims&quot;&gt;&amp;#x5B9E;&amp;#x9A8C;&amp;#x4E0E; claims&lt;/h2&gt;
&lt;p&gt;&amp;#x5B9E;&amp;#x9A8C;&amp;#x662F;&amp;#x4E3A;&amp;#x4E86;&amp;#x8BC1;&amp;#x660E;&amp;#x6216;&amp;#x5F3A;&amp;#x5316;&amp;#x6587;&amp;#x7AE0;&amp;#x91CC;&amp;#x7ED9;&amp;#x51FA;&amp;#x7684; claim/hypothesis &amp;#x7684;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.dropbox.com/s/6izllk62dobao9n/ICCV19_generalized_rcnn_tutorial_ross_girshick.pptx?dl=0&quot; aria-label=&quot;Dropbox - ICCV19_generalized_rcnn_tutorial_ross_girshick.pptx - Simplify your life&quot; class=&quot;hint--top hint--rounded hint--no-animate hint--no-arrow&quot;&gt;Ross ICCV 2019 tutorial&lt;/a&gt;
 &amp;#x6700;&amp;#x540E;&amp;#x8C08;&amp;#x4E86;&amp;#x8C08;&amp;#x600E;&amp;#x4E48;&amp;#x5199; paper. &amp;#x7B2C; 126 &amp;#x9875;&amp;#x8BF4;, &amp;#x6587;&amp;#x7AE0;&amp;#x4E2D;&amp;#x6240;&amp;#x6709;&amp;#x7684; claim, &amp;#x7406;&amp;#x60F3;&amp;#x60C5;&amp;#x51B5;&amp;#x4E0B;&amp;#x90FD;&amp;#x5E94;&amp;#x8BE5;
&lt;strong&gt;&amp;#x8981;&amp;#x4E48;&amp;#x662F;&amp;#x53C2;&amp;#x8003;&amp;#x6587;&amp;#x732E;&amp;#x4E2D;&amp;#x5DF2;&amp;#x6709;&amp;#x7684;, &amp;#x8981;&amp;#x4E48;&amp;#x662F;&amp;#x88AB;&amp;#x5B9E;&amp;#x9A8C;&amp;#x8BC1;&amp;#x660E;&amp;#x7684;&lt;/strong&gt;.&lt;/p&gt;</summary>
    
    
    
    
    <category term="Research" scheme="https://ppwwyyxx.com/blog/tags/Research/"/>
    
    <category term="Deep Learning" scheme="https://ppwwyyxx.com/blog/tags/Deep-Learning/"/>
    
  </entry>
  
  <entry>
    <title>Patching STB_GNU_UNIQUE of Buggy Binaries</title>
    <link href="https://ppwwyyxx.com/blog/2021/Patch-STB_GNU_UNIQUE/"/>
    <id>https://ppwwyyxx.com/blog/2021/Patch-STB_GNU_UNIQUE/</id>
    <published>2021-04-02T07:00:00.000Z</published>
    <updated>2021-04-02T07:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>&#x5F00;&#x6E90;&#x5DE5;&#x5177;&#x94FE;&#x91CC;&#x6709;&#x5F88;&#x591A;&#x9648;&#x5E74;&#x5C0F; &quot;feature&quot;, &#x6700;&#x521D;&#x7531;&#x4E8E;&#x5404;&#x79CD;&#x539F;&#x56E0; (&#x4F8B;&#x5982;&#x4F5C;&#x4E3A; workaround) &#x5B9E;&#x73B0;&#x4E86;&#x4E4B;&#x540E;,  &#x5373;&#x4F7F;&#x8BED;&#x4E49;&#x6A21;&#x7CCA;&#x6216;&#x8BBE;&#x8BA1;&#x4E0D;&#x5408;&#x7406;, &#x4E5F;&#x56E0;&#x4E3A;&#x517C;&#x5BB9;&#x6027;&#x88AB;&#x7559;&#x5230;&#x4E86;&#x4ECA;&#x5929;.</p><span id="more"></span><p><code>STB_GNU_UNIQUE</code> &#x5C31;&#x662F; ELF &#x4E2D;&#x4E00;&#x4E2A;&#x4E0D;&#x592A;&#x597D;&#x7684;&#x8BBE;&#x8BA1;, &#x5E26;&#x6765;&#x4E86;&#x4E0D;&#x5C11;&#x8BED;&#x4E49;&#x51B2;&#x7A81;. &#x62E5;&#x6709; <code>STB_GNU_UNIQUE</code> binding &#x7684;&#x7B26;&#x53F7;, &#x5373;&#x4F7F;&#x5728;&#x88AB;&#x7528; <code>RTLD_LOCAL</code> &#x65B9;&#x5F0F;&#x88C5;&#x8F7D;&#x7684;&#x65F6;&#x5019;,  &#x4E5F;&#x4F1A;&#x62E5;&#x6709; global linkage. &#x53E6;&#x5916;&#x5B83;&#x8FD8;&#x4F1A;&#x5BFC;&#x81F4; dlclose &#x65E0;&#x6548;. &#x7F51;&#x4E0A;&#x5BF9;&#x6B64;&#x6709;&#x5F88;&#x591A;&#x5410;&#x69FD;, &#x4F8B;&#x5982;<a href="https://news.ycombinator.com/item?id=21555752" aria-label="The GNU executable and linking model has a ton of warts and I wish we could star... | Hacker News" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x8FD9;&#x91CC;</a>, <a href="https://www.openwall.com/lists/musl/2015/08/31/3" aria-label="musl - STB_GNU_UNIQUE not handled as original spec intended" class="hint--top hint--rounded hint--no-animate hint--no-arrow"> &#x8FD9;&#x91CC;</a>.</p><p>&#x8FD9;&#x4E2A; binding &#x6700;&#x521D;&#x7684;&#x5F15;&#x5165;&#x4F3C;&#x4E4E;&#x662F;&#x7531;&#x4E8E;&#x4E00;&#x4E9B;&#x5168;&#x5C40;&#x7B26;&#x53F7;&#x7684;&#x5185;&#x5728;&#x72B6;&#x6001;&#x4E0D;&#x80FD;&#x91CD;&#x590D;&#x591A;&#x6B21;,  &#x56E0;&#x6B64;&#x628A;&#x8FD9;&#x4E9B;&#x7B26;&#x53F7;&#x6807;&#x8BB0;&#x4E3A; unique, &#x5373;&#x4F7F;&#x4ECE;&#x591A;&#x4E2A; plugins &#x91CC;&#x88C5;&#x8F7D;&#x4E86;&#x591A;&#x6B21;, &#x7B26;&#x53F7;&#x4E5F;&#x53EA;&#x6709;&#x4E00;&#x4E2A;&#x5B9A;&#x4E49;. &#x4F46;&#x662F;&#x53E6;&#x4E00;&#x65B9;&#x9762;, &#x7A0B;&#x5E8F;&#x4E5F;&#x4F1A;&#x6709;&#x4E00;&#x4E9B;&#x5168;&#x5C40;&#x7B26;&#x53F7;&#x7684;&#x72B6;&#x6001;&#x5FC5;&#x987B;&#x662F; local &#x7684;. &#x5230;&#x5E95;&#x54EA;&#x79CD;&#x884C;&#x4E3A;&#x662F;&#x7528;&#x6237;&#x9700;&#x8981;&#x7684;, &#x7F16;&#x8BD1;&#x5668;&#x662F;&#x4E0D;&#x77E5;&#x9053;&#x7684;. &#x7ED3;&#x679C;&#x662F;, gcc &quot;&#x806A;&#x660E;&quot; &#x7684;&#x81EA;&#x52A8;&#x628A; template function &amp; inline function &#x91CC;&#x7684; static variable &#x6807;&#x8BB0;&#x4E3A;&#x4E86; unique</p><p>&#x5176;&#x5B9E; C++ &#x6807;&#x51C6;&#x786E;&#x5B9E;&#x89C4;&#x5B9A;&#x4E86;&#x8FD9;&#x6837;&#x7684; variable &#x5FC5;&#x987B;&#x662F; <a href="https://eel.is/c++draft/dcl.inline" aria-label="[dcl.inline]" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&quot;single entity&quot;</a>. &#x7406;&#x8BBA;&#x4E0A;&#x8BF4; gcc &#x6CA1;&#x505A;&#x9519;, &#x4F46;&#x8FD9;&#x5E76;&#x4E0D;&#x603B;&#x662F;&#x7528;&#x6237;&#x7684;&#x9884;&#x671F;&#x884C;&#x4E3A;, &#x800C; C++ &#x6807;&#x51C6;&#x4E5F;&#x6CA1;&#x63D0;&#x4F9B;&#x522B;&#x7684;&#x529E;&#x6CD5;. &#x5982;&#x679C;&#x8981;&#x7981;&#x7528; unique binding, &#x53EF;&#x4EE5;&#x4F7F;&#x7528; <code>-fno-gnu-unique</code> &#x91CD;&#x7F16;&#x8BD1;, &#x6216;&#x8005;&#x66B4;&#x529B; patch &#x7F16;&#x8BD1;&#x597D;&#x7684; ELF binary.</p><p><code>STB_GNU_UNIQUE</code> &#x5BFC;&#x81F4;&#x4E86; PyTorch 1.8.0 &#x6700;&#x8FD1;&#x7684;&#x4E00;&#x4E2A;&#x4E25;&#x91CD; <a href="https://github.com/pytorch/pytorch/issues/54245" aria-label="invalid device ordinal in pytorch1.8+cuda11.1 &#xB7; Issue #54245 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">bug</a>, &#x5F71;&#x54CD;&#x4E86;&#x6240;&#x6709; R-CNN &#x6A21;&#x578B;, torchvision / detectron2 / mmdetection &#x91CC;&#x90FD;&#x6709;&#x7528;&#x6237;&#x62A5;&#x544A;. &#x91CD;&#x65B0;&#x7F16;&#x8BD1; PyTorch &#x592A;&#x9EBB;&#x70E6;&#x4E86;, &#x4E3A;&#x4E86;&#x4EE5;&#x540E;&#x66F4;&#x5FEB;&#x9A8C;&#x8BC1;&#x6B64;&#x7C7B;&#x95EE;&#x9898;, &#x6211;&#x5C31;&#x5199;&#x4E86;&#x4E00;&#x4E2A;&#x66B4;&#x529B; patch ELF &#x7684;&#x811A;&#x672C;:</p><figure class="highlight python"><table><tr><td class="code"><pre><code class="hljs python"><span class="hljs-keyword">import</span> sys<br><span class="hljs-keyword">from</span> elftools.elf.elffile <span class="hljs-keyword">import</span> ELFFile<br><br><span class="hljs-keyword">def</span> <span class="hljs-title function_">process_file</span>(<span class="hljs-params">filename</span>):<br>    <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(filename, <span class="hljs-string">&apos;rb&apos;</span>) <span class="hljs-keyword">as</span> f:<br>        elffile = ELFFile(f)<br><br>        dynsym = elffile.get_section_by_name(<span class="hljs-string">&apos;.dynsym&apos;</span>)<br>        dynsym_offset = dynsym.header.sh_addr<br>        dynsym_idx = []  <span class="hljs-comment"># addresses of Elf64_Sym</span><br>        <span class="hljs-keyword">for</span> idx, sb <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(dynsym.iter_symbols()):<br>            bind = sb.entry.st_info.bind<br>            <span class="hljs-keyword">if</span> <span class="hljs-string">&quot;UNIQUE&quot;</span> <span class="hljs-keyword">in</span> bind <span class="hljs-keyword">or</span> <span class="hljs-string">&quot;LOOS&quot;</span> <span class="hljs-keyword">in</span> bind:<br>                <span class="hljs-built_in">print</span>(<span class="hljs-string">&quot;Found UNIQUE symbol: &quot;</span>, sb.name[:<span class="hljs-number">60</span>])<br>                dynsym_idx.append(dynsym_offset + idx * <span class="hljs-number">24</span>)  <span class="hljs-comment"># 24=sizeof(Elf64_Sym)</span><br><br>    <span class="hljs-built_in">print</span>(<span class="hljs-string">f&quot;Patching <span class="hljs-subst">{<span class="hljs-built_in">len</span>(dynsym_idx)}</span> symbols ...&quot;</span>)<br>    <span class="hljs-keyword">with</span> <span class="hljs-built_in">open</span>(filename, <span class="hljs-string">&apos;rb+&apos;</span>) <span class="hljs-keyword">as</span> f:<br>        <span class="hljs-keyword">for</span> sym_idx <span class="hljs-keyword">in</span> dynsym_idx:<br>            f.seek(sym_idx + <span class="hljs-number">4</span>)  <span class="hljs-comment"># 4=sizeof(st_name)</span><br>            old = <span class="hljs-built_in">ord</span>(f.read(<span class="hljs-number">1</span>))<br>            <span class="hljs-keyword">assert</span> old // <span class="hljs-number">16</span> == <span class="hljs-number">10</span>, <span class="hljs-built_in">hex</span>(old)  <span class="hljs-comment"># STB_GNU_UNIQUE==10</span><br>            f.seek(sym_idx + <span class="hljs-number">4</span>)<br>            f.write(<span class="hljs-built_in">bytes</span>([old % <span class="hljs-number">16</span> + <span class="hljs-number">2</span> * <span class="hljs-number">16</span>]))  <span class="hljs-comment"># STB_WEAK==2</span><br>            f.write(<span class="hljs-built_in">bytes</span>([<span class="hljs-number">2</span>]))  <span class="hljs-comment"># STV_HIDDEN=2</span><br><br>process_file(sys.argv[<span class="hljs-number">1</span>])<br></code></pre></td></tr></table></figure><p>&#x4EE5;&#x4E0A;&#x811A;&#x672C;&#x628A;&#x6240;&#x6709; <code>STB_GNU_UNIQUE</code> &#x7B26;&#x53F7;&#x7684; binding &#x6539;&#x6210;&#x4E86; <code>WEAK</code>, visibility &#x6539;&#x6210;&#x4E86; <code>HIDDEN</code>. &#x7B26;&#x53F7;&#x8868;&#x7684; entry &#x7ED3;&#x6784;&#x53EF;&#x53C2;&#x8003; <code>/usr/include/elf.h::Elf64_Sym</code>.</p><p>&#x7528;&#x8FD9;&#x4E2A;&#x811A;&#x672C; patch &#x4E86;&#x4E00;&#x4E0B; <code>libtorch_cuda_{cpp,cu}.so</code> &#x4E4B;&#x540E;, &#x4EE5;&#x4E0A; bug &#x5C31;&#x6D88;&#x5931;&#x4E86;. &#x540C;&#x65F6;, &#x8FD9;&#x6837;&#x6211;&#x4E5F;&#x80FD;&#x591F;&#x65B9;&#x4FBF;&#x7684;&#x786E;&#x8BA4;&#x53E6;&#x4E00;&#x4E2A;<a href="https://github.com/pytorch/pytorch/issues/55027" aria-label="CUDA error: device-side assert triggered(torch1.8.1+cuda11.1) &#xB7; Issue #55027 &#xB7; pytorch/pytorch" class="hint--top hint--rounded hint--no-animate hint--no-arrow">&#x770B;&#x4F3C;&#x76F8;&#x5173;&#x7684; bug</a> &#x8FD8;&#x662F;&#x8DDF; <code>STB_GNU_UNIQUE</code> &#x6709;&#x5173;&#x7CFB;.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;&amp;#x5F00;&amp;#x6E90;&amp;#x5DE5;&amp;#x5177;&amp;#x94FE;&amp;#x91CC;&amp;#x6709;&amp;#x5F88;&amp;#x591A;&amp;#x9648;&amp;#x5E74;&amp;#x5C0F; &amp;quot;feature&amp;quot;, &amp;#x6700;&amp;#x521D;&amp;#x7531;&amp;#x4E8E;&amp;#x5404;&amp;#x79CD;&amp;#x539F;&amp;#x56E0; (&amp;#x4F8B;&amp;#x5982;&amp;#x4F5C;&amp;#x4E3A; workaround) &amp;#x5B9E;&amp;#x73B0;&amp;#x4E86;&amp;#x4E4B;&amp;#x540E;, 
 &amp;#x5373;&amp;#x4F7F;&amp;#x8BED;&amp;#x4E49;&amp;#x6A21;&amp;#x7CCA;&amp;#x6216;&amp;#x8BBE;&amp;#x8BA1;&amp;#x4E0D;&amp;#x5408;&amp;#x7406;, &amp;#x4E5F;&amp;#x56E0;&amp;#x4E3A;&amp;#x517C;&amp;#x5BB9;&amp;#x6027;&amp;#x88AB;&amp;#x7559;&amp;#x5230;&amp;#x4E86;&amp;#x4ECA;&amp;#x5929;.&lt;/p&gt;</summary>
    
    
    
    
    <category term="PyTorch" scheme="https://ppwwyyxx.com/blog/tags/PyTorch/"/>
    
    <category term="C++" scheme="https://ppwwyyxx.com/blog/tags/C/"/>
    
    <category term="Programming" scheme="https://ppwwyyxx.com/blog/tags/Programming/"/>
    
    <category term="Open Source" scheme="https://ppwwyyxx.com/blog/tags/Open-Source/"/>
    
  </entry>
  
</feed>
