<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Good Yunmorning</title>
    <link>https://yunmorning.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sat, 13 Jun 2026 14:40:44 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>yunmorning</managingEditor>
    <image>
      <title>Good Yunmorning</title>
      <url>https://tistory1.daumcdn.net/tistory/3560481/attach/f30846175fbd4d1c916eba410a60ce8c</url>
      <link>https://yunmorning.tistory.com</link>
    </image>
    <item>
      <title>[EuroSys '19] Parallax: Sparsity-aware Data Parallel Training of Deep Neural Networks</title>
      <link>https://yunmorning.tistory.com/67</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Problems To Solve&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;In the context of distributed training, DL frameworks provide good support for training models used in the image classification tasks, but it is less scalable for training NLP models due to the lack of consideration of the difference in the sparsity of model parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;How to Solve&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;To optimize the amount of data transfer with considering sparsity, Parallax adopts a hybrid approach that uses &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Parameter Server architecture&lt;/b&gt;&lt;/span&gt; for handling &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;sparse variables&lt;/b&gt;&lt;/span&gt; and &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;AllReduce&lt;/b&gt;&lt;/span&gt; architecture for &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;dense variables&lt;/b&gt;&lt;/span&gt;.&lt;/li&gt;
&lt;li&gt;Partitions large sparse variables by a near-optimal number of partitions to maximize parallelism.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lv08Y/btqGX6zdmvc/3quOCUnkqVkEDMkLTFPOC1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lv08Y/btqGX6zdmvc/3quOCUnkqVkEDMkLTFPOC1/img.png&quot; data-alt=&quot;PS and AR architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lv08Y/btqGX6zdmvc/3quOCUnkqVkEDMkLTFPOC1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLv08Y%2FbtqGX6zdmvc%2F3quOCUnkqVkEDMkLTFPOC1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PS and AR architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Parameter Server (shortened to PS)&lt;/h3&gt;
&lt;p&gt;PS consists of server and worker processes.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Server: stores subsets of model variables ($V_1, V_2, V_3, V_4$) in memory.&lt;/li&gt;
&lt;li&gt;Workers: pull variables from servers to perform local computations on their respective mini-batches ($X_1, X_2, X_3$), and push gradients with respect to variables back to servers.&lt;/li&gt;
&lt;li&gt;Variable synchronization between workers is done by servers.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AllReduce (shortened to AR)&lt;/h3&gt;
&lt;p&gt;All workers have a replica of variables and share locally computed gradients via collective communication primitives. (`AllReduce` and `AllGatherv`)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`AllReduce`: reduces values from all processes to a single value.
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;aggregates gradients from all workers by computing the sum of gradients. ($\sum_{i=1}^N \frac{\partial L}{\partial v}(X_i)$)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;`AllGatherv`: gathers the values from all processes.
&lt;ul style=&quot;list-style-type: disc;&quot;&gt;
&lt;li&gt;aggregates gradients by concatenating the gradients into $[\frac{\partial L}{\partial v}(X_1), \dots , \frac{\partial L}{\partial v}(X_N)]$ $\to$ broadcast the aggregated gradients back to all processes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;p&gt;AR architecture is preferable for dense models, while the PS architecture performs better for sparse models.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;i&gt;&lt;b&gt;Dense vs Sparse&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;Dense feature: all elements are accessed at least once during a single training step.&lt;br /&gt;Sparse feature: only a subset of the elements are accessed in one iteration.&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Discussion&lt;/h2&gt;</description>
      <category>Research</category>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/67</guid>
      <comments>https://yunmorning.tistory.com/67#entry67comment</comments>
      <pubDate>Fri, 21 Aug 2020 03:11:05 +0900</pubDate>
    </item>
    <item>
      <title>[ICLR 2019] ProxylessNAS: Direct Neural Architecture Search on Target Task and Hardware</title>
      <link>https://yunmorning.tistory.com/66</link>
      <description>&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Directly applying NAS to a large scale task (e.g. ImageNet) is computationally expensive or impossible.&lt;/li&gt;
&lt;li&gt;To solve this problem, Some works proposed to search for building blocks on proxy tasks, such as training for fewer epochs, starting with a smaller dataset (e.g. CIFAR-10), or learning with fewer blocks. $\to$ Cannot guarantee to be optimal on the target task.&lt;/li&gt;
&lt;li&gt;ProxylessNAS directly learns the architectures on the target task and HW without proxy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBjwis/btqF5AGAF7k/Vn56TOiemQcKvnYdS8qkaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBjwis/btqF5AGAF7k/Vn56TOiemQcKvnYdS8qkaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBjwis/btqF5AGAF7k/Vn56TOiemQcKvnYdS8qkaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBjwis%2FbtqF5AGAF7k%2FVn56TOiemQcKvnYdS8qkaK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Method&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Construction of Over-Parameterized Network&lt;/h3&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;Notation&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$N(e, ..., e_n)$: Neural networks composed with $e_i$ which represents a certain edge in the DAG.&lt;/li&gt;
&lt;li&gt;$O = \{o_i\}$: the set of $N$ candidate primitive operations (e.g. conv, pooling, ...)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLrGMX/btqF4yW0YHz/4k7cc3mdBosBkgwYDcQolK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLrGMX/btqF4yW0YHz/4k7cc3mdBosBkgwYDcQolK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLrGMX/btqF4yW0YHz/4k7cc3mdBosBkgwYDcQolK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLrGMX%2FbtqF4yW0YHz%2F4k7cc3mdBosBkgwYDcQolK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;To construct the over-parameterized network that includes any architecture in the search space, each edge is set to be a mixed operation ($m_O$) with $N$ parallel paths as shown in Figure 2.&lt;/li&gt;
&lt;li&gt;Given input $x$, the output of a mixed operation $m_O$ is defined based on the outputs of its $N$ paths.&lt;/li&gt;
&lt;li&gt;By the following equation, the output feature maps of all $N$ paths are calculated and stored in the memory, while training a compact model only involves one path.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;250&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCNIAy/btqF4yiwJh0/9RDCkeVcjVN37wjGGdygGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCNIAy/btqF4yiwJh0/9RDCkeVcjVN37wjGGdygGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCNIAy/btqF4yiwJh0/9RDCkeVcjVN37wjGGdygGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCNIAy%2FbtqF4yiwJh0%2F9RDCkeVcjVN37wjGGdygGk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;250&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Due to the existence of $N$ different paths, it requires $N$ times memory compared to a compact model $to$ exceeds memory limits.&lt;/li&gt;
&lt;li&gt;The problem can be solved with the &lt;b&gt;binarized path&lt;/b&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Learning Binarized Path&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bLrGMX/btqF4yW0YHz/4k7cc3mdBosBkgwYDcQolK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bLrGMX/btqF4yW0YHz/4k7cc3mdBosBkgwYDcQolK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bLrGMX/btqF4yW0YHz/4k7cc3mdBosBkgwYDcQolK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbLrGMX%2FbtqF4yW0YHz%2F4k7cc3mdBosBkgwYDcQolK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;To save memory, only one path is kept when training the over-parameterized network.&lt;/li&gt;
&lt;li&gt;For each $N$ different path, only one path of activation is active in memory at run-time by using binary gates $g$.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;700&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/t7EaF/btqF5eKziCz/YTyYbvbC3BcG1is1vYhy71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/t7EaF/btqF5eKziCz/YTyYbvbC3BcG1is1vYhy71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/t7EaF/btqF5eKziCz/YTyYbvbC3BcG1is1vYhy71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Ft7EaF%2FbtqF5eKziCz%2FYTyYbvbC3BcG1is1vYhy71%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;700&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;The output of the mixed operation is given as:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;700&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZA9BU/btqF3GVzZlz/CO0e7w4itq1grfrAIeUeRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZA9BU/btqF3GVzZlz/CO0e7w4itq1grfrAIeUeRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZA9BU/btqF3GVzZlz/CO0e7w4itq1grfrAIeUeRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZA9BU%2FbtqF3GVzZlz%2FCO0e7w4itq1grfrAIeUeRk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;700&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;When &lt;u&gt;training weights parameters&lt;/u&gt;, the &lt;u&gt;architecture parameters are frozen&lt;/u&gt;, and binary gates are stochastically sampled according to the above equation for each input batch.&lt;/li&gt;
&lt;li&gt;Then the weight parameters of active paths are updated via standard gradient descent.&lt;/li&gt;
&lt;li&gt;When &lt;u&gt;training architecture parameters&lt;/u&gt;, the &lt;u&gt;weight parameters are frozen&lt;/u&gt;, then binary gates are reset, and architecture parameters are updated based on the validation set.&lt;/li&gt;
&lt;li&gt;These two steps are performed alternatively.&lt;/li&gt;
&lt;li&gt;Once the training of architecture parameters is finished, the path with the highest path weight is kept and the other redundant paths are pruned.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/66</guid>
      <comments>https://yunmorning.tistory.com/66#entry66comment</comments>
      <pubDate>Tue, 28 Jul 2020 02:41:54 +0900</pubDate>
    </item>
    <item>
      <title>[SOSP' 11] PTask: Operating System Abstractions To Manage GPUs as Compute Device</title>
      <link>https://yunmorning.tistory.com/64</link>
      <description>&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 포스팅에서 리뷰할 논문은 2011년 SOSP에 나온 &quot;&lt;a href=&quot;https://www.cs.utexas.edu/users/witchel/pubs/sosp11rossbach-ptask.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;PTask: Operating System Abstractions To Manage GPUs as Compute Devices&lt;/a&gt;&quot;이다. 상당히 오래된 논문이지만 GPU 기반 이기종 시스템과 관련하여 중요하게 여겨지는 개념들과 아이디어들이 많기 때문에 구체적으로 리뷰를 하려고 한다. 염두에 두어야할 점은 이 연구에선 딥러닝의 맥락에서 GPU 스케줄링을 언급하고 있지 않으며 그 당시와 현재의 상황이 꽤 많이 달라졌다는 것이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;ABSTRACTION&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 논문에서는 GPU를 CPU와 같은 1순위 계산 자원으로 활용할 수 있도록 하기 위한 OS abstraction(OS API)인 PTask API를 소개한다. PTask API에서 OS가 관리하는 여러 객체들은 PTask 그래프의 컴포넌트들로 표현이 되고, 이렇게 만들어진 그래프를 기반으로 dataflow programming model을 제공한다. Dataflow 그래프로 프로그램을 표현하면 시스템의 전체적인 정보를 활용하여 스케줄링에서의 효율성과 fairness를 증대시킬 수 있기 때문에 CUDA나 OpenCL만을 사용하는 GPU programming model과 비교하였을 때 큰 이점을 얻을 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 논문에서는 Windows 7과 Linux 상에서 PTask API를 개발하여 &lt;a href=&quot;https://en.wikipedia.org/wiki/Gesture_recognition&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;gestural interface&lt;/a&gt; 프로그램에 대해 성능 실험을 하였고, 그 결과 최대 throughput에서 5배까지의 성능 향상을 얻었다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;INTRODUCTION&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;2011년 기준으로 성능 TOP 5 안에 드는 슈퍼컴퓨터들은 모두 GPU를 프로세싱 유닛으로 사용하고 있었다. GPGPU를 위한 프레임워크인 DirectX, CUDA, OpenCL 등이 등장하면서 프로그래머들은 GPU를 병렬 연산장치로 활용하여 고성능 프로그램을 만들 수 있게 되었다. 이와 같이 GPU 프로그래밍 플랫폼이 등장함에 따라 GPU를 연산 장치로 적극 활용할 수 있게 되었지만, GPU가 연산에 활용되는 경우는 주로 렌더링과 같은 batch-oriented computation이며, interactive 어플리케이션(e.g: gestural input, brain-computer interface, interative video recognition, ...)에 대해서는 OS abstraction이 부족하여 제대로 활용되지 못하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 연구에서는 interative application에서 GPU가 제대로 활용되지 못 하는 이유가 적절한 OS-level abstraction이 없기 때문이라고 보고, GPU를 더 이상 I/O device가 아닌 CPU와 동등한 computational device로 관리하여 이러한 문제를 해결하고자 하였다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nDbis/btqFqNgrlBS/XWHl9K3ESlH320aKuzrRzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nDbis/btqFqNgrlBS/XWHl9K3ESlH320aKuzrRzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nDbis/btqFqNgrlBS/XWHl9K3ESlH320aKuzrRzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnDbis%2FbtqFqNgrlBS%2FXWHl9K3ESlH320aKuzrRzk%2Fimg.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;위의 그림에서는 각각 CPU(왼쪽)와 GPU(오른쪽)의 abstraction layer를 보여준다. 그림에서 알 수 있는 건 CPU와 비교하였을 때 GPU에 대해서 제공되는 kernel-level abstraction이 상당히 제한적이라는 것이다. OS는 기본적으로 GPU를 shared compute resource가 아니라 주변 기기로 취급, 관리하고 있기 때문에 OS는 GPU 자원의 관리를 제조업체에서 제공하는 드라이버와 유저 모드에서 동작하는 런타임에 맡기고 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 연구에서는 interative, compute-intensive device를 위한 새로운 kernel-level abstarction을 구현한다. 새롭게 정의되는 abstraction은 모듈화와 고성능 두 가지 조건을 모두 만족시켜야 한다. 이를 위해서 커널은 프로그래머에게 주변 기기에 대한 충분한 하드웨어 정보를 제공하여 micro-tuning을 통한 성능상의 이점을 누릴 수 있도록 해야하고, 성능을 해치지 않으면서도 복잡한 정보들에 대해 유저가 신경쓰지 않고 프로그래밍 할 수 있도록 적절한 고수준 API를 제공해야 한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;PTask API&lt;/h4&gt;
&lt;p&gt;&lt;b&gt;(1) PTask graph&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;PTask API는 dataflow graph를 통해 프로그램을 표현하는 dataflow programming model을 기반으로 한다. 그래프의 노드는 ptask(parallel task)라고 불리며, 이는 GPU에서 돌아가는 프로그램, 또는 CPU나 다른 가속기에서 돌아가는 프로그램들을 표현한다. 각 노드들은 입력 포트와 출력 포트를 가지고 있고, 포트끼리는 그래프의 엣지를 통해 연결된다. 이 엣지를 channel이라고 부르며, 프로그램에서의 데이터 흐름을 표현한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Usability&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;위와 같은 그래프 컴포넌트들을 통해서 PTask graph는 프로그램에서 데이터의 흐름과 잠재적인 동시성을 직접적으로 표현할 수 있고, 이를 통해 프로그래밍 과정을 간단하게 만들 수 있다. 그래프의 정보를 통해 시스템이 자동으로 최적화를 할 수 있기 때문에, 프로그래머는 어떻게(how), 언제(when) 데이터를 옮길 것인가에 대해서 신경 쓸 필요없이 오직 어디(where)에서 어디로 데이터를 옮길지만 생각해서 프로그램을 짜면 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) GPU scheduling by OS kernel&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;PTask graph는 OS-managed object들로 구성되어 있기 때문에 OS 커널은 이들에 대한 충분한 컨트롤을 가질 수 있다. 즉, PTask 런타임은 GPU 커널의 상태에 대해서도 파악을 하면서 마치 CPU 프로세스를 스케줄링하듯이 효율적인 스케줄링을 할 수 있다. 기존의 GPU 프레임워크에서 GPU 스케줄링은 OS 커널에게는 블랙 박스로 감춰져있고 전적으로 제조업체가 제공하는 드라이버에 의해 이루어졌다. (이 경우 주로 round-robin 방식으로 단순하게 스케줄링을 하는 것이 일반적이다.) PTask는 performance와 fairness가 제대로 보장되지 않던 기존 GPU 프레임워크와는 달리 OS 커널에서 직접 효율적인 GPU 스케줄링을 수행할 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Efficient data movement&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;PTask의 또 다른 장점은 데이터의 이동을 최적화할 수 있다는 것이다. 예를 들어 카메라와 같은 주변기기를 통해 들어오는 데이터를 가지고 실시간 이미지 처리를 하는 데에 GPU를 사용한다고 해보자. 기존의 GPU 프레임워크에서 이러한 상황을 처리하려면 CPU와 GPU간에 반복적인 데이터 카피가 발생하게 되고,&amp;nbsp; 굳이 CPU를 거치지 않아도 됨에도 불구하고 CPU를 거쳐 GPU로 메모리가 전달되기 때문에 double buffering 문제가 생길 수 있다. 반면, PTask를 사용하는 경우엔 PTask graph에서 데이터의 origin과 destination에 대한 정확한 정보를 제공하기 때문에 OS가 이 정보들을 통해 불필요한 데이터 복사를 제거할 수 있다.&amp;nbsp; 이 경우엔 카메라 드라이버의 데이터를 CPU host로 불필요하게 카피하지 않고 즉시 GPU 드라이버로 카피할 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;MOTIVATION&lt;/h2&gt;
&lt;p&gt;이 연구에서는 interactive application에 GPU를 활용하는 것에 초점을 두고 있으며, interative application이라 함은 다음과 같은 프로그램을 의미한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://www.igi-global.com/dictionary/gestural-interfaces/12177&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Gestural interface&lt;/a&gt;: 물리적인 제스처를 감지하고 디지털 데이터화하여 입력으로 사용하는 프로그램&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ko.wikipedia.org/wiki/%EB%87%8C-%EC%BB%B4%ED%93%A8%ED%84%B0_%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Neural interface&lt;/a&gt;: BCI(brain computer interface)와 같이 뇌를 통해 장치를 조종하는 인터페이스&lt;/li&gt;
&lt;li&gt;Encrypting file system&lt;/li&gt;
&lt;li&gt;Real-time audio/visual interface: 실시간 음성 인식...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;빠른 연산를 통한 데이터 처리가 필요한 이러한 어플리케이션들은 latency를 줄이기 위해 GPU를 사용하면 큰 이득을 볼 가능성이 있지만, 적절한 OS의 지원이 없기 때문에 GPU를 제대로 활용하지 못 하고 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4kUO6/btqFrDc6nQJ/NDfKhxMOFNScpqO76WbeDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4kUO6/btqFrDc6nQJ/NDfKhxMOFNScpqO76WbeDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4kUO6/btqFrDc6nQJ/NDfKhxMOFNScpqO76WbeDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4kUO6%2FbtqFrDc6nQJ%2FNDfKhxMOFNScpqO76WbeDk%2Fimg.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이 연구에서는 카메라를 통해 감지된 손의 제스처를 마우스 이동/클릭과 같은 OS 입력 이벤트로 처리하는 제스처 인식 프로그램을 예시로 하여 case study를 진행하였다. 이러한 제스처 인식 프로그램은 처리해야 할 계산의 양이 상당히 크고, 동시에 실시간으로 서빙가능한 짧은 latency를 요구하는 interactive 프로그램이다. 따라서 GPU를 활용한 데이터 병렬화를 통해 성능 향상을 보기 좋은 예시라고 할 수 있다. 아래의 &lt;b&gt;Figure 2&lt;/b&gt;는 제스처 인식 프로그램의 전체적인 시스템 구조를 보여준다. 시스템은 몇 개의 카메라 센서들, 카메라로부터 얻은 이미지를 분석, 처리하기 위한 소프트웨어로 이루어져 있다. 인식된 데이터가 마우스나 키보드 입력과 같이 빠르게 처리 되기 위해선 제스처 인식 결과가 high frequency &amp;amp; low latency로 전달될 수 있어야 한다. 제스처 인식 프로그램의 구성 요소는 다음과 같다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;catusb&lt;/b&gt;&lt;/span&gt;: USB 버스와 연결된 카메라로부터 이미지 데이터를 얻는다.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;xform&lt;/b&gt;&lt;/span&gt;: geometric transformation을 통해 여러 개의 카메라로부터 얻은 이미지를 단일 &lt;a href=&quot;https://en.wikipedia.org/wiki/Point_cloud&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;point cloud&lt;/a&gt;로 변환한다.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;filter&lt;/b&gt;&lt;/span&gt;: xform이 생성한 point cloud는 노이즈가 많이 낀 상태이기 때문에 filter를 통해 노이즈 필터링을 수행한다. 데이터 병렬화를 통해 처리된다.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;hidinpit&lt;/b&gt;&lt;/span&gt;: 노이즈가 처리된 point cloud를 통해 제스처를 감지하고, 감지한 결과를 &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%9D%B8%EA%B0%84_%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4_%EC%9E%A5%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;HID(human interface device)&lt;/a&gt; 입력으로 보낸다. (병렬화는 사용하지 않는다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;제스처 인식 시스템은 이와 같이 모듈화된 네 단계 시스템 파이프라인으로 구성되어 있으며, 이 중에서 연산량이 많고 병렬화 적용의 여지가 큰 xform과 filter 프로그램에는 GPU를 통한 데이터 병렬화를 적용한다. 만약 GPU를 사용하지 않고 4-core CPU만을 사용해서 이와 같은 시스템을 서빙하려 한다면 거의 100% CPU 자원을 사용해야 가까스로 실시간 서빙이 가능하다. 반면 GPU를 사용한다면 무거운 계산은 GPU에 맡길 수 있기 때문에 CPU는 거의 사용하지 않을 수 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Motivation 1: 데이터 이동의 문제&lt;/h3&gt;
&lt;p&gt;GPU를 위한 OS abtraction이 없는 상태에서 제스처 인식 시스템을 돌리려면 유저 레벨 GPU 프로그래밍 프레임워크인 DirectX, CUDA, OpenCL 등을 사용할 수밖에 없다. xform과 filter 프로그램을 이러한 프레임워크를 통해 구현한다면 병렬화를 통해 연산 자체에 드는 시간은 크게 단축시킬 수 있지만, PCIe 버스를 통해 CPU(host)와 GPU(device) 간에 왔다갔다 하는 데이터 복사 오버헤드가 매우 크기 때문에 성능상의 이점이 사라질 수 있다. 제스처 인식 프로그램에서도 catusb -&amp;gt; xform -&amp;gt; filter -&amp;gt; hidinput으로 넘어가는 사이사이마다 CPU-GPU 간의 메모리 복사가 이루어지기 때문에 오버헤드가 상당히 크다. 아래의 그래프는 실제로 시스템에서 오버헤드가 얼마나 큰지 보여주는데, 짙은 색으로 표시된 부분이 GPU에서 실제 커널 연산이 이루어지는 시간에 해당하고, 밝은 부분으로 표시된 부분이 커뮤니케이션 오버헤드를 나타낸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이와 같은 커뮤니케이션 오버헤드를 효과적으로 줄이기 위해서는 OS 레벨의 지원이 필요하다. 만약 충분한 OS의 지원이 있다면, catusb에서 캡처한 이미지 데이터를 CPU를 거치지 않고 바로 GPU 메모리로 보내서 xform을 수행할 수 있을 것이고, xform 이후에 이어지는 filter도 GPU에서 연산이 이루어지기 때문에 CPU로 메모리를 복사할 필요없이 GPU에 있는 데이터를 그대로 사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGNi7i/btqFpAB5w19/21qh6J8kHOSyIzqdy3zRR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGNi7i/btqFpAB5w19/21qh6J8kHOSyIzqdy3zRR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGNi7i/btqFpAB5w19/21qh6J8kHOSyIzqdy3zRR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGNi7i%2FbtqFpAB5w19%2F21qh6J8kHOSyIzqdy3zRR0%2Fimg.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;Motivation 2: 데이터 이동 문제 해결을 위한 구현이 쉽지 않다.&lt;/h3&gt;
&lt;p&gt;CPU-GPU 간의 데이터 복사 문제는 GPGPU 프레임워크 개발자들에게 이미 잘 알려진 문제이며, 이를 해결하기 위한 몇 가지 해결책들이 만들어져 왔다. 예를 들어 CUDA는 비동기 버퍼 복사, CUDA stream를 지원하고, 메모리 버퍼를 고정하여 computation과 communication을 중첩시킴으로써 데이터 이동의 latency hiding을 하기도 한다. 하지만 이러한 기능들을 프로그래머가 제대로 활용하려면 메모리 매핑과 같은 OS 레벨의 이해가 필요하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;멀티 스트림을 효율적으로 사용하기 위해서는 어떤 GPU computation이 데이터 이동과 중첩될 수 있는지에 대한 정적인 정보(디펜던시 등)가 필요한데, 이러한 정보는 항상 정적으로 고정되어 있지가 않다. 이러한 문제가 해결되기 위해선 CPU-GPU 간의 메모리 관리를 위한 아키텍쳐와 소프트웨어 차원의 지원이 필요하다.&lt;a href=&quot;https://ko.wikipedia.org/wiki/AMD_%EA%B0%80%EC%86%8D_%EC%B2%98%EB%A6%AC_%EC%9E%A5%EC%B9%98&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; AMD Fusion&lt;/a&gt;은 CPU와 GPU를 하나의 die로 집적시킴으로써 양 쪽 프로세서에서 shared memory에 접근할 때 coherency를 보장하고자 하였다. 하지만 GPU 프로그래밍 경험이 있는 사람들을 알다시피 성능을 높이기 위해서는 coherency를 희생하여 GPU private memory 접근을 높여야하기 때문에 한계가 있다. &lt;a href=&quot;https://ko.wikipedia.org/wiki/%EC%83%8C%EB%94%94%EB%B8%8C%EB%A6%AC%EC%A7%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Intel Sandy Bridge&lt;/a&gt; 역시도 CPU와 GPU를 하나의 die에 통합시키는 등의 시도가 있었다. 하지만 여전히 고성능과 coherency를 동시에 만족시키기엔 부족함이 많다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Motivation 3: 스케줄링 문제&lt;/h3&gt;
&lt;p&gt;아직까지 OS는 계산에 GPU를 사용하는 경우 performance와 fairness를 동시에 만족시키기 위한 스케줄링을 제공하지 못하고 있다. 근본적인 이유 중에 하나는 OS는 GPU를 입출력 장치와 같은 주변기기로 대할 뿐, 공유 계산 자원으로 여기고 있지 않는다는 점이다. 설령 OS에서 GPU task의 priority를 어느 정도 조절하려고 하더라도 이는 블랙박스로 감춰진 GPU driver에 의해 의도대로 스케줄링이 되지 않을 수 있다는 문제도 있다. 밑에서 살펴볼 두 가지 케이스는 CPU task와 GPU task와 서로 무관함에도 불구하고 한쪽에 부하가 걸리면 다른 한 쪽의 성능이 크게 저하되는 경우이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) GPU work가 CPU work를 멈추게 할 수 있다.&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuWHBW/btqFpLQXofp/Q1wahMjzPPLIyls1tzXK00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuWHBW/btqFpLQXofp/Q1wahMjzPPLIyls1tzXK00/img.png&quot; data-alt=&quot;GPU 프로그램(xform)이 돌아가지 않을 때와 돌아갈 때 마우스 이벤트의 frequency(Hz)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuWHBW/btqFpLQXofp/Q1wahMjzPPLIyls1tzXK00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuWHBW%2FbtqFpLQXofp%2FQ1wahMjzPPLIyls1tzXK00%2Fimg.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GPU 프로그램(xform)이 돌아가지 않을 때와 돌아갈 때 마우스 이벤트의 frequency(Hz)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;위의 그림은 60초 동안 HID class driver에 전달된 마우스 이벤트의 frequency를 보여준다. 앞서 gestural interface 시스템의 GPU 프로그램인 xform이 CPU task와 동시에 돌아가지 않을 때, 시스템은 마우스 이벤트를 120Hz 정도에서 안정적으로 전달하고 있다. 반면, xform이 동시에 실행 중일 때는 마우스 이벤트의 주기가 요동치며 20Hz 아래까지 떨어진다. GPU task는 근본적으로 CPU가 아닌 GPU에서 돌아가기 때문에 설령 무거운 GPU 프로그램(xform)이 실행되더라도 CPU utilization은 25%를 넘지 않는다. 즉 CPU에 사용 가능한 자원이 있음에도 불구하고 성능 저하가 발생하는 것이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;OS에서 GPU를 컨트롤하기 어려운 이유 중에 하나는 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;GPU는 preemptive하지 않다&lt;/span&gt;&lt;/b&gt;는 점이다. 즉, request가 한 번 전송되어서 GPU task가 시작되면 끝날 때까지 OS에서 control을 뺏어서 중지시키는 것이 불가능하다. 특히나 GPU 런타임을 개발할 때 GPU 프로그램의 throughput을 높이기 위해서 큰 단위로 &lt;u&gt;&lt;i&gt;&lt;b&gt;request batching&lt;/b&gt;&lt;/i&gt;&lt;/u&gt;을 하기 때문에 preemption이 불가능한 GPU task 하나의 수행시간이 더욱 길어질 수 있다. 이처럼 OS가 GPU를 CPU task와 통합하여 전체적인 맥락에서 로드 밸런싱할 수 없다는 사실은 상당히 큰 비효율을 낳는다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;text-align: left;&quot; data-ke-size=&quot;size20&quot;&gt;(2) CPU work가 GPU throughput을 저하시킬 수 있다.&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/biPWLg/btqFpz4jNmf/Zd75JKf6ZaKBkoHKzKq3fK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/biPWLg/btqFpz4jNmf/Zd75JKf6ZaKBkoHKzKq3fK/img.png&quot; data-alt=&quot;w. Windows 7, Intel Core 2 Quad 2.66Hz, 8GB RAM, &amp;amp;amp;amp; NVIDIA GeForce GT230&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/biPWLg/btqFpz4jNmf/Zd75JKf6ZaKBkoHKzKq3fK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbiPWLg%2FbtqFpz4jNmf%2FZd75JKf6ZaKBkoHKzKq3fK%2Fimg.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;w. Windows 7, Intel Core 2 Quad 2.66Hz, 8GB RAM, &amp;amp; NVIDIA GeForce GT230&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;위의 그림은 서로 무관한 CPU, GPU 프로그램이 동시에 수행되고 있을 때 Windows 7에서 로드 밸런싱을 하지 못하는 상황을 보여준다. CPU에서는 4개의 코어를 모두 사용하는 CPU-bound task가 실행되고 있고, CPU에서는 xform 프로그램이 돌아가는 상황이다. xform은 CPU에서 커널을 실행할 때를 제외하면 CPU의 자원을 사용하지 않는데도 불구하고 CPU에 부하가 걸린 상태에서는 frame rate가 2배나 떨어진다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;이와 같은 문제를 해결하기 위해서는 OS가 GPU를 CPU와 동일한 지위의 연산 자원으로 다루어야 하고, 프로그램에서 GPU를 사용하는 경우엔 CPU 쓰레드와 프로세스를 다루는 것처럼 처리될 수 있어야 한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;DESIGN&lt;/h2&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;앞에서는 GPU 프로그램, 특히 interactive GPU 프로그램에서 왜 새로운 OS abstraction이 필요한지에 대한 이유를 살펴보았다. 이번 섹션에서는 본격적으로 문제 해결을 위한 새로운 abstraction인 PTask API에 대해서 살펴볼 것이다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;INTRODUCTION에서 언급했다시피 PTask는 dataflow programming model에 기반하며, 프로그램은 노드(ptask)와 엣지(channel)로 이루어진 DAG 형태로 표현 된다. PTask의 디자인에는 다음 세 가지의 철학이 반영되어 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;u&gt;스케줄링의 fairness와 isolation을 위하여 리소스 매니저가 GPU를 CPU와 더불어 하나의 통합적인 시스템 하에서 파악할 수 있도록 한다.&lt;/u&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;GPU 프로그래밍 편의성을 높인 programming model을 제공한다.&lt;/b&gt;&lt;/u&gt; OpenCL이나 CUDA 같은 기존 GPU 프레임워크에서는 실제 GPU에서 실행되는 커널의 코드 자체는 그리 길지 않음에도 불구하고, CPU와 GPU 간의 데이터 이동을 조작하기 위한 코드의 양이 매우 많았다. (context, command queue, buffer, argument 셋업을 해주고 명시적으로 memcpy 코드를 작성해야하는 것 등) PTask는 device-specific한 코드들은 캡슐화하여 프로그래머들이 오직 커널 자체의 알고리즘 구현과 데이터 흐름에만 신경 쓸 수 있도록 고수준 API를 제공한다.&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;&lt;b&gt;모듈화와 빠른 속도, 두 가지 이점을 모두 챙길 수 있는 시스템을 구축한다.&lt;/b&gt;&lt;/u&gt; 현재 GPU 프로그래밍 프레임워크에서는 커널을 실행하고, 메모리를 복사하는 CPU host 코드와 GPU 커널 코드가 타이트하게 커플링되어 있기 때문에 GPU 커널 코드와 데이터 이동을 위한 host 코드를 모두 작성해야 한다. 이러한 프로그래밍 방식은 최적의 데이터 이동을 보장할 수 없기 때문에 성능 저하를 일으킨다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;1. PTask 스케줄링을 OS와 통합&lt;/h3&gt;
&lt;p&gt;기존에 CPU task에 대해서만 적용되던 OS 스케줄링이 GPU task에도 적용되었을 때 얻을 수 있는 이점은 크게 두 가지이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Efficiency: 스케줄링을 통해 CPU와 GPU의 utilization을 올릴 수 있다.&lt;/li&gt;
&lt;li&gt;Fairness: task priority를 통해 로드 밸런싱을 하던 기존 OS 스케줄러에 GPU task를 통합시킴으로써 interative한 CPU task와 batch-oriented GPU task 사이에 contention이 발생한 상황 등에서 보다 공평하고 효율적인 로드 밸런싱을 적용할 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Efficiency &amp;amp; Modularity&lt;/h3&gt;
&lt;pre id=&quot;code_1594309658493&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;matrix gemm(A, B) {
  matrix res = new matrix();
  copyToDevice(A);
  copyToDevice(B);
  invokeGPU(gemm_kernel, A, B, res);
  return res;
}

matrix modularSlowAxBxC(A, B, C) {
  matrix AxB = gemm(A, B);
  matrix AxBxC = gemm(AxB, C);
  return AxBxC;
}

matrix modularFastAxBxC(A, B, C) {
  matrix intermed = new matrix();
  matrix res = new matrix();
  copyToDevice(A);
  copyToDevice(B);
  copyToDevice(C);
  invokeGPU(gemm_kernel, A, B, intermed);
  invokeGPU(gemm_kernel, intermed, C, res);
  copyFromDevice(res);
  return res;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;위의 pseudo-code는 $((A \times B) \times C)$를 계산하는 gemm 서브루틴 예시이다. GPU는 CPU와는 독립적인 private memory 공간을 가지고 있기 때문에 GPU에서 gemm 연산을 하기 위해서는 CPU 메모리에서 입력 행렬인 $A$와 $B$의 데이터를 GPU 메모리로 복사해야 한다. 이후 GPU 커널이 호출되면 실제 연산이 이루어지고, 계산 결과는 다시 CPU 메모리 공간으로 복사된다. 만약 프로그래머가 modularity를 위해서 gemm 커널을 위의 예시에서 modularSlowAxBxC처럼 단일 행렬 곱 연산을 하도록 구현한다면, &lt;span style=&quot;color: #333333;&quot;&gt;$((A \times B) \times C)$는 gemm(gemm(A, B), C)와 같이 함수가 nesting 되어 계산 될 것이다. 이 경우 중간 계산 결과인 $(A \times B)$는 첫번째 gemm 커널의 계산 후에 CPU 메모리로 복사될 것이고, 두 번째 gemm 커널 런치시에 다시 GPU 메모리 공간으로 복사된다. 이와 같이 granularity가 작은 모듈화된 커널 구현에서는 불필요한 여러 번의 데이터 복사로 인해 상당한 오버헤드가 발생한다. &lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;반대로 &lt;span style=&quot;color: #333333;&quot;&gt;$((A \times B) \times C)$를 한 번에 계산하는&amp;nbsp;&lt;/span&gt;modularFastAxBxC와 같이 커널 하나에서 많은 연산을 하도록 granularity를 증가시키면 메모리 복사로 인한 오버헤드는 감소하지만 modularity와 코드의 재사용성이 떨어지는 문제가 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/puadV/btqFqNgth9e/VTi9kebtQpE7Ky53TN71wk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/puadV/btqFqNgth9e/VTi9kebtQpE7Ky53TN71wk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/puadV/btqFqNgth9e/VTi9kebtQpE7Ky53TN71wk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpuadV%2FbtqFqNgth9e%2FVTi9kebtQpE7Ky53TN71wk%2Fimg.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;PTask에서는 런타임이 CPU와 GPU task 간의 디펜던시를 파악하여 자동으로 불필요한 데이터 복사와 버퍼링을 제거하여 위와 같은 modularity와 performance 간의 trade-off 문제를 해결할 수 있다. PTask API를 통해 행렬곱을 구현하는 경우 행렬곱 연산은 위의 그림과 같이 그래프 형태로 표현된다. 행렬 $A$와 $B$는 gemm ptask의 입력으로 사용되고, 그 계산 결과는 출력으로 나간다. 출력으로 나간 중간 계산 결과($(A \times B)$)는 또 다른 gemm ptask의 입력으로 사용되어 또 다른 입력인 $C$와 함께 처리된다. 이와 같이 PTask API를 사용하면 프로그래머는 단지 그래프를 구성하여 노드 간의 데이터 흐름(where to where)만 표현하면 되고, 그 밖의 정보는 시스템에서 그래프를 통해 자동으로 파악할 수 있다. 예를 들어, 시스템은 중간 계산 결과인 &lt;span style=&quot;color: #333333;&quot;&gt;$(A \times B)$가 GPU 커널로부터 생산되었고 뒤에 이어지는 GPU 커널의 입력으로 사용될 것임을 그래프를 통해 파악할 수 있다. 따라서 굳이 중간 계산 결과를 CPU 메모리로 복사할 필요가 없음을 알 수 있고 CPU-GPU 간의 불필요한 메모리 복사를 제거할 수 있게 된다. 동시에 gemm 커널은 단일 행렬 연산만을 수행하기 때문에 메모리 복사 오버헤드를 줄이면서도 modularity를 유지 가능하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;프로그램을 DAG 형태의 dataflow graph로 표현하여 얻을 수 있는 또 다른 이점은 DAG 자체가 명시적으로 프로그램의 병렬화 가능 부분을 표현한다는 것이다. DAG 상에서 디펜던시가 없는 연산끼리는 동시에 수행이 가능하기 때문에 멀티 GPU를 사용하는 경우에도 유저가 직접 멀티 GPU를 위한 셋업 코드를 작성하지 않더라도 시스템이 알아서 처리를 할 수 있다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;3. Limitations&lt;/span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;PTask graph는 반드시 DAG 형태를 만족해야 하며, 그래프는 반드시 정적으로 고정되어야 한다는 제약이 있다. 따라서 recursion이나 dynamic한 특성을 가진 프로그램을 표현하는 것이 불가능하다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;PTASK API&lt;/span&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;PTask API는 아래의 표(&lt;b&gt;Table 1&lt;/b&gt;)과 같은 OS 레벨의 abstraction으로 구성되어 있다. (시스템 콜로 구현됨)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/szrtu/btqFq9DzlHR/wL6Djr3dFJeirc5lALvhyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/szrtu/btqFq9DzlHR/wL6Djr3dFJeirc5lALvhyk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/szrtu/btqFq9DzlHR/wL6Djr3dFJeirc5lALvhyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fszrtu%2FbtqFq9DzlHR%2FwL6Djr3dFJeirc5lALvhyk%2Fimg.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(1) PTask&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;ptask는 OS 프로세스와 비슷하지만 GPU를 비롯한 다른 가속기에서 실행되는 task를 포함한다는 점에서 차이가 있다. 각 ptask는 stdin, sdtout, sdterr file descriptor와 같은 입출력 포트를 가지고 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(2) Port&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Port는 데이터의 ptask에 사용되는 데이터의 source나 sink가 된다. Port는 다음과 같은 세 가지 종류가 있다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;InputPort: 입력 포트&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;OutputPort: 출력 포트&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;StickyPort: 여러번 ptask가 호출되더라도 지속적으로 유지되는 값을 가진 입력 포트&lt;/li&gt;
&lt;/ol&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;PTask graph에서 각 ptask는 자신의 모든 InputPort가 계산에 사용될 데이터를 가지고 있을 때 실행되며, 생산된 결과값은 OutputPort로 전달된다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(3) Channel&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;특정 port와 또 다른 port를 연결하는 pipe 역할을 한다. channel은 반드시 한 개의 source와 한 개의 destination port 간에 연결되며, InputPort와 StickyPort는 오직 하나의 channel에만 연결될 수 있고, OutputPort는 여러 개의 channel들에 연결 가능하다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(4) Graphs&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;여러 ptask들의 port가 channel을 통해 연결되어 하나의 graph를 형성한다. 여러 개의 graph가 존재할 경우, 각각은 독립적으로 생성, 수행되며 PTask 런타임이 각각의 그래프를 공평하고 효율적으로 스케줄링 한다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(5) Datablock &amp;amp; Template&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;Datablock은 graph에서 특정 channel을 따라서 흐르는 개별 dataflow의 단위이며, template은 datablock에 대한 meta-data(data layout 등)를 표현하고 datablock에 흐르는 raw data를 GPU 하드웨어 쓰레드에 매핑하는 데 도움을 준다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;text-align: left;&quot; data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;1. Graph에서의 dataflow&lt;/h3&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;PTask에서의 데이터는 개별 datablock 단위로 channel을 통해 흐르게 되며, ptask의 입력 port가 channel을 통해 데이터를 받았을 때 해당 ptask가 실행될 수 있고, 생산된 결과는 출력 port에 전달된다. Port는 port가 datablock을 가지고 있는지 여부에 따라서 &quot;Occupied&quot;, 또는 &quot;Unoccupied&quot; 두 가지 상태를 가질 수 있다. 이 때 port는 종류에 따라서 다른 방식으로 동작한다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;InputPort&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Occupied: 이전 노드와 연결된 channel로부터 datablock을 읽는다.&lt;/span&gt;&lt;/li&gt;
&lt;li style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Unoccupied: 비어있던 channel의 queue에 새로운 데이터가 들어오면 즉시 해당 datablock을 읽는다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;OutputPort&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Occupied: 뒤에 이어지는 모든 channel들로 datablock을 push한다. (OutputPort는 여러 channel들과 연결 가능)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;Unoccupied: 비어있던 queue에 새로운 데이터가 들어오면 즉시 push한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333;&quot;&gt;StickyPort: 새로운 datablock이 연결된 channel에 쓰이기 전까지는 이전에 쓰인 datablock을 보존하며 Occupied 상태를 유지한다.&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Port들은 ptflags라는 meta-data를 가지는데 , 이는 해당 port가 GPU 측 자원에 바운드되어 있는지, 아니면 런타임에 바운드되어 있는지를 나타낸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;Channel들은 주어진 capacity 양만큼의 datablock들을 큐잉할 수 있다.&lt;/b&gt;&lt;/span&gt; 이는 Producer-Consumer 상황을 생각하면 되는데, &lt;b&gt;sys_push&lt;/b&gt; 시스템콜을 통해서 어플리케이션이 데이터를 channel로 push하면, 이미 큐가 capacity만큼 꽉 찬 상태라면 공간이 날 때까지 request blocking 된다. 마찬가지로 &lt;b&gt;sys_pull&lt;/b&gt; 시스템콜은 channel의 큐에 데이터가 하나도 없는 상황이라면 데이터가 들어올 때까지 request blocking 된다.&lt;/p&gt;
&lt;p style=&quot;text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Datablock은 메모리 공간 간에 이동이 이루어지는 데이터들에 대해서도 coherent view를 제공한다. Datablock은 메모리 공간을 특정 디바이스의 버퍼 객체로 매핑하는 buffer-map을 통해서 여러 메모리 공간의 버퍼들을 캡슐화한다. buffer-map은 어떤 버퍼가 해당 데이터에 대해서 가장 최신 업데이트 값을 가지는지 추적하여 서로 다른 주소 공간 사이에서도 coherency를 유지한다. Datablock은 record-count를 가지는데, channel에 push되면 record-count가 증가한다. 컴파일러 ref-count와 마찬가지로 record-count가 0이 되면 garbage collect된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Iteration Space&lt;/h4&gt;
&lt;p&gt;SIMT방식으로 동작하는 GPU는 iteration space(= index space, grid, NDRange...)에 따라서 하나의 커널 인스턴스에서 병렬적으로 수행할 work-item(= thread)의 개수(dimension)을 정의하고, 이에 따라 하드웨어 쓰레드를 할당하여 병렬 연산을 수행한다. PTask에서는 sys_get_geometry 시스템 콜을 통해서 프로그래머가 명시적으로 iteration space를 정의할 수 있으며, 일반적인 경우에는 프로그래머가 명시를 하지 않더라도 PTask 런타임이 template의 정보를 통해서 적절한 iteration space와 GPU thread configuration을 파악하여 자동으로 세팅할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Template은 datablock 버퍼에 있는 raw data에 대한 meta-data 정보를 담는다. 다음은 template이 포함하고 있는 멤버들의 예시이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;dimension: 3차원 iteration space 정보&lt;/li&gt;
&lt;li&gt;dbtflags: 데이터가 바운드된 리소스의 유형을 명시 (e.g: GPU global memory, constant memory, ...)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PTask에서 template이 필요한 이유는 3가지이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;프로그래머가 명시를 하지 않더라도 런타임이 자동으로 iteration space를 추론할 수 있게 하기 위해서&lt;/li&gt;
&lt;li&gt;channel을 통해서 소비될 datablock을 런타임이 할당할 수 있도록 하기 위해서&lt;/li&gt;
&lt;li&gt;프로그래머가 잘못된 API call을 하였을 때 런타임이 이를 파악하여 유저 피드백을 제공할 수 있도록 하기 위해서&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. PTask invocation&lt;/h3&gt;
&lt;p&gt;ptask는 다음 4가지 상태 중 하나의 상태를 갖는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Waiting&lt;/b&gt;&lt;/span&gt;: 입력이 오길 기다림&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Queued&lt;/b&gt;&lt;/span&gt;: 입력은 준비되었고 GPU 자원을 기다림&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Executing&lt;/b&gt;&lt;/span&gt;: GPU에서 실행 중&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Completed&lt;/span&gt;&lt;/b&gt;: 계산이 끝나고 출력이 소비되길 기다림&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;위와 같은 상태를 통해 ptask가 실행되는 과정을 살펴보면 다음과 같다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(1) Waiting =&amp;gt; Queued&lt;/p&gt;
&lt;p&gt;특정 ptask의 모든 입력 port가 Occupied 상태면, 런타임은 해당 ptask를 런큐에 넣고 상태를 Waiting에서 Queued로 바꾼다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(2) Queued =&amp;gt; Executing&lt;/p&gt;
&lt;p&gt;런큐에 큐잉된 ptask는 GPU 자원이 free해졌을 때 런큐의 head에 있다면 비로소 실행될 수 있다. 실행시엔 ptask의 상태를 Executing으로 변경한다. ptask가 실행되면 런타임은 ptask의 InputPort에 있는 datablock들을 읽는다. Port에서 datablock이 읽히면 (StickyPort가 아닌 경우에) datablock은 port로부터 제거되며, channel로부터 새로운 데이터를 pull한다. 이 때 channel에 큐잉된 datablock이 없다면, 해당 port는 Unoccupied 상태가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(3) Executing =&amp;gt; Completed&lt;/p&gt;
&lt;p&gt;실행이 끝나면 ptask는 Completed 상태가 되고, 출력 datablock을 출력 port로 이동시킨다. 이 때 모든 출력 port는 Unoccupied 상태여야 한다. 만약 하나의 출력 port라도 Occupied 상태라면 ptask는 계속해서 Completed 상태에서 기다린다. 출력 port로 데이터 이동이 끝난 후엔 ptask는 다시 Waiting 상태로 되돌아간다. Completed 상태가 따로 필요한 이유는 channel이 유한한 capacity만큼의 datablock만 큐잉할 수 있기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;PTask의 핵심 중 하나는 SEDA 아키텍처 구조와 비슷하게 channel에서 datablock을 큐잉을 하는 것이다. 큐잉을 하면 다음 ptask에서 아직 GPU 연산이 이루어지고 있더라도 이전 ptask는 (적어도 channel의 큐가 꽉찰 때까지는) GPU를 놀게 하지 않고 연산 결과를 계속 생성해낼 수 있다.&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Gestural Interface PTask Graph&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yXGgM/btqFrnO7z9f/z4oFmgEXg4eGMNc4v1Kcj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yXGgM/btqFrnO7z9f/z4oFmgEXg4eGMNc4v1Kcj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yXGgM/btqFrnO7z9f/z4oFmgEXg4eGMNc4v1Kcj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyXGgM%2FbtqFrnO7z9f%2Fz4oFmgEXg4eGMNc4v1Kcj0%2Fimg.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;이 논문에서 케이스 스터디 대상으로 선정한 gestural interface를 PTask graph로 표현하면 다음과 같은 이점이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(1) 그래프 정보를 통해 불필요한 커뮤니케이션을 없앨 수 있다.&lt;/p&gt;
&lt;p&gt;위의 그림(&lt;b&gt;Figure 8&lt;/b&gt;)에서 USB 포트(usbsrc_0, usbsrc_1)과 이미지 입력 포트(raw_0, raw_1)는 channel을 통해 연결되어 있다. 여기에서 USB =&amp;gt; CPU 메모리 =&amp;gt; GPU 메모리로 이어지는 더블 버퍼링 문제가 발생할 수 있는데, USB 드라이버, PTask 런타임, GPU 드라이버 간에 버퍼 공유가 있다면 USB의 데이터를 CPU 메모리를 거치지 않고 즉시 GPU 메모리로 이동시킬 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;xform의 출력 port(cloud0, cloud1)은 filter의 입력 port(i_0, i_1)과 연결되어 있는데, xform과 filter 모두 GPU에서 실행되는 ptask임을 PTask 런타임이 파악할 수 있기 때문에 CPU 메모리로의 불필요한 데이터 카피를 하지 않을 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;(2) Concurrency 파악이 가능하다.&lt;/p&gt;
&lt;p&gt;그래프를 통해 프로그램을 표현하면 프로그래머가 따로 명시하지 않더라도 런타임이 concurrency를 파악할 수 있다. 예를 들어 위의 그림에서는 여러 개의 GPU 환경이라면 두 개의 xform ptask가 동시에 수행될 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;SCHEDULING&lt;/h2&gt;
&lt;p&gt;PTask에서 스케줄링에는 몇 가지 어려운 점이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;GPU 하드웨어는 preemptive하지 않고, CPU task 처럼 context switch가 불가능하다.&lt;/li&gt;
&lt;li&gt;GPU를 완전히 컨트롤할 수 있는 인터페이스가 존재하지 않기 때문에 CPU 프로세스 스케줄러를 통해 GPU task를 온전히 컨트롤하는 것은 불가능하다.&lt;/li&gt;
&lt;li&gt;멀티 GPU 환경에서 GPU 메모리의 locality가 성능에 매우 중요한 요소이기 때문에 잘 고려되어야 한다. (만약 GPU 간의 메모리 이동 오버헤드가 크다면 여러 GPU를 사용하는 것이 오히려 성능을 저하시킬 수도 있다.)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 사항을 고려하여 PTask에서는 4가지 방식의 스케줄링 방식을 실험하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;(1) First-available&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;모든 ptask가 하나의 매니저 쓰레드에 할당된다. ptask는 큐잉되지 않기 때문에 READY 상태의 ptask가 사용 가능한 GPU 자원의 수보다 많다면 GPU를 할당 받지 못한 ptask들은 lock이 걸려 blocking 된다. 사용 가능한 GPU 자원이 생긴다면 signal이 발생하고, lock을 잡고 기다리던 ptask가 깨어나서 GPU를 할당 받을 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;(2) FIFO&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;큐를 사용하여 FIFO 순서로 ptask를 스케줄링한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;(3) Priority&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;각 ptask는 static priority와 proxy priority를 갖는데, static priority는 말 그대로 정적으로 고정된 priority이고, proxy priority는 ptask의 실행 및 데이터 관리를 담당하는 매니저 쓰레드의 priority이다. 스케줄러는 &lt;u&gt;ptask들의 ready queue&lt;/u&gt;와 &lt;u&gt;사용가능한 GPU 리스트&lt;/u&gt;를 동시에 관리하는데, ptask의 상태가 Queued, Executing, Completed로 바뀔 때마다 스케줄러가 깨어나서 ready queue 내의 각 ptask의 effective priority를 계산한다. Effective priority는 static priority, proxy priority, 큐에서 기다린 시간, 평균 수행 시간을 weigted sum하여 계산된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ptask의 effective priority는 다음과 같은 경우에 증가한다. (&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;priority boosting&lt;/b&gt;&lt;/span&gt;)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;평균 대기 시간보다 더 오랜 시간을 큐에서 대기한 경우&lt;/li&gt;
&lt;li&gt;평균 GPU 수행시간보다 더 짧은 수행 시간을 가질 경우&lt;/li&gt;
&lt;li&gt;proxy priority가 높은 경우&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;전통적인 OS 스케줄링에서 priority boosting의 목적과 마찬가지로 ptask에서 priority boosting이 존재하는 이유 역시 starvation을 방지하기 위함이다. 짧은 수행 시간의 interative job의 priority를 높이는 방향으로 스케줄링을 해야 throughput을 늘릴 수 있으므로 위와 같은 방식으로 priority를 조절한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;스케줄러는 ready queue의 head에 있는 ptask에 &lt;i&gt;fitness&lt;/i&gt;와 &lt;i&gt;strength&lt;/i&gt;를 고려하여 사용가능한 GPU 리스트 중 하나를 할당한다. Fitness는 GPU가 실행환경 및 ptask의 세팅에 대해 지원을 하는지 여부를 의미하고, strength는 GPU의 코어 개수, 클락 속도와 같은 성능 관련 요소를 의미한다. 스케줄러는 항상 strength가 가장 강한 GPU부터 선택하여 ptask와 매칭을 시킨다. 성공적으로 매칭이 된 경우, 스케줄러는 ptask를 큐에서 제거하고 ptask 매니저 쓰레드에 signal을 보내 실행을 시키면서 Executing 상태가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;다음은 priority policy를 구현한 pseudo-code이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1594447079631&quot; class=&quot;c++ arduino&quot; data-ke-language=&quot;c++&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;void update_eff_prio(ptasks) {
  avg_gpu = avg_gpu_time(ptasks);
  avg_cwait = avg_current_wait(ptasks);
  avg_dwait = avg_decayed_wait(ptasks);
  avg_pprio = avg_proxy_prio(ptasks);
  
  // weight sum을 통해 effective priority 계산
  foreach(p in ptasks) {
    boost = W_0 * (p-&amp;gt;last_cwait - avg_cwait);
    boost += W_1 * (p-&amp;gt;avg_wait - avg_dwait);
    boost += W_2 * (p-&amp;gt;avg_gpu - avg_gpu);
    boost += W_3 * (p-&amp;gt;proxy_prio - avg_pprio);
    p-&amp;gt;eff_prio = p-&amp;gt;prio + boost;
  }
}

gpu match_gpu(ptask) {
  // 사용 가능한 GPU 리스트를 얻음
  gpu_list = available_gpus();
  
  // fitness 체크
  remove_unfit(gpu_list, ptask);
  
  // strength가 큰 것부터 내림차순으로 정렬
  sort(gpu_list);
  
  // head에 있는 가장 strength가 큰 GPU를 가용 GPU 리스트에서 제거
  return remove_head(gpu_list);
}

void schedule() {
  update_eff_prio(ptask);
  sort(ptasks);		// effective prio가 큰 것부터 내림차순 정렬
  
  while(gpus_available() &amp;amp;&amp;amp; size(ptasks) &amp;gt; 0) {
    foreach(p in ptasks) {
      best_gpu = match_gpu(p);
      if (best_gpu != null) {
        remove(ptasks, p);
        p-&amp;gt;dispatch_gpu = best_gpu;
        signal_dispatch(p);
        return;
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;(4) Data-aware&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;위에서 살펴본 priority policy를 따르되, GPU 선택 알고리즘에서 ptask의 입력이 가장 최신 버전으로 업데이트된 메모리 공간이 어디인지를 고려한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Research</category>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/64</guid>
      <comments>https://yunmorning.tistory.com/64#entry64comment</comments>
      <pubDate>Tue, 7 Jul 2020 00:11:02 +0900</pubDate>
    </item>
    <item>
      <title>[CV Study] Accelerating the Super-Resolution Convolutional Neural Network</title>
      <link>https://yunmorning.tistory.com/62</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Paper Link: &lt;a href=&quot;https://arxiv.org/pdf/1608.00367.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://arxiv.org/pdf/1608.00367.pdf&lt;/a&gt;&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Image super-resolution(SR) 태스크에서 &lt;a href=&quot;https://arxiv.org/pdf/1501.00092.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SRCNN&lt;/a&gt;이 좋은 성능을 거두었지만 real-time 서빙을 하기에는 연산 비용이 너무 크다는 문제가 있다. 이 논문에서는 모래시계 모양의(encoder-decoder 구조를 생각하면 된다.) CNN 구조를 활용하여 기존의 SRCNN을 경량화, 가속화하는 것에 중점을 두었다. 이를 위해서 다음과 같은 3가지 방법을 취하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DCGAN, pix2pix 등에서 사용하는 &lt;a href=&quot;https://github.com/vdumoulin/conv_arithmetic&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;transposed convolution&lt;/a&gt;(deconv)을 네트워크의 후반부에 활용하여, 저해상도(LR)의 입력 이미지와 고해상도(HR)의 출력 이미지 간의 매핑이 E2E로 학습가능한 네트워크 구조를 만든다.&lt;/li&gt;
&lt;li&gt;앞서 모래시계 모양의 CNN 구조라고 언급했던 것처럼 transposed convolution을 통해 feature map을 확장시키기 이전에 input feature map의 차원을 축소시켜 latent feature를 추출할 수 있도록 한다.&lt;/li&gt;
&lt;li&gt;더 작은 필터 크기를 사용하여 연산량을 줄였고, 그 대신에 더 많은 레이어를 사용한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;이 논문에서는 deconvolution이라는 용어를 사용하는데 이는 잘못된 용어이다.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;&lt;u&gt;Some sources use the name deconvolution, which is inappropriate because it&amp;rsquo;s not a deconvolution.&lt;/u&gt; To make things worse deconvolutions do exists, but they&amp;rsquo;re not common in the field of deep learning. An actual deconvolution reverts the process of a convolution. Imagine inputting an image into a single convolutional layer. Now take the output, throw it into a black box and out comes your original image again. This black box does a deconvolution. It is the mathematical inverse of what a convolutional layer does.&lt;/i&gt;&lt;br /&gt;(출처: &lt;a href=&quot;https://towardsdatascience.com/types-of-convolutions-in-deep-learning-717013397f4d&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;An Introduction to different Types of Convolutions in Deep Learning&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;따라서 이 포스팅에서는 deconvolution 대신에 transposed convolution이라는 용어를 사용하도록 하겠다.&lt;/blockquote&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Single image super-resolution(SR)은 저해상도(LR)의 이미지를 고해상도(HR)의 이미지로 복원하는 태스크를 의미한다. SRCNN은 SR 태스크에 최초로 딥러닝을 활용한 모델인데, 상당히 뛰어난 성과를 거두었고 이에 기반하여 이후에 Fast R-CNN, Faster R-CNN 등이 등장하였다. SRCNN은 SR 문제를 해결하는 데에 좋은 성능을 보이긴 하지만 real-time 서빙을 하기에는 너무 느리다는 문제가 있다. 예를 들어 240x240 이미지를 3배 크기로 upsample 하는 경우 SRCNN은 1.32 fps의 throughput을 보이지만 real-time 성능을 내기위해서는 약 17배에 해당하는 24 fps에 도달해야 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;SRCNN의 속도 한계에는 2가지 이유가 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;전처리 단계에서 입력 LR 이미지는 &lt;a href=&quot;https://bskyvision.com/789&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;삼차보간법(bicubic interpolation)&lt;/a&gt;을 통해서 원하는 이미지 사이즈(HR 이미지 사이즈와 동일한 크기)로 upsampling 되는데 이렇게 upsampling 된 이미지에 대해서 convolution을 수행하면 계산 비용이 크게 증가한다. 만약 n배의 크기로 upsampling 하는 경우, upsampling 하지 않은 original LR 이미지 대비 $n^2$배 만큼 연산량이 증가한다.&lt;/li&gt;
&lt;li&gt;SRCNN은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;patch extraction =&amp;gt; non-linear mapping =&amp;gt; reconstruction&lt;/b&gt;&lt;/span&gt;의 과정을 거치는데, 다차원 LR feature space에서 또 다른 다차원 HR feature sapce로 매핑하는 non-linear mapping 단계에서 mapping 레이어(= convolution 레이어)의 너비를 늘릴 수록 좋은 accuracy를 얻을 수 있지만 연산 비용이 증가하는 문제가 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 두 가지 문제에 대해서 이 논문에서는 각각 다음과 같은 해결책을 제시한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Bicubic interpolation 대신 네트워크 후반부에 transposed convolution을 사용한다. 이 경우엔 original LR 이미지에 대해서 convolution을 수행하기 때문에 bicubic interpolation을 통해 upsampling 된 전처리 이미지에 대해 convolution을 하는 것보다 $n^2$배 만큼 연산량을 줄일 수 있다.&lt;/li&gt;
&lt;li&gt;&amp;nbsp;Autoencoder나 pix2pix에서 그러하듯이 대칭적인 encoder-decoder 구조를 사용한다.(논문에서는 모래시계 구조라고 설명한다.) Encoder에서는 저차원 feature space로의 매핑을 통해 중요한 feature들을 추출해내고 decoder에서 이를 확장한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 두 가지 기법들이 적용된 모델이 바로 이 논문에서 소개하는 Fast Super-Resolution Convolutional Neural Network(FSRCNN)이다. FSRCNN은 기존의 SRCNN-Ex(SRCNN보다 6배 많은 파라미터를 가진 larger version) 보다도 높은 성능을 보이면서도 40배나 빠른 속도를 낸다. 마찬가지로 FSRCNN의 small version인 FSRCNN-s는 SRCNN과 비교했을 때 거의 동일한 성능을 내면서도 17.36배나 빠른 속도를 낸다. 이는 CPU에서 24 fps의 처리량을 낼 수 있는 속도이기 때문에 real-time 서빙이 가능한 속도라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b7G1em/btqFnPlC93W/0Ctw3kYuFs4noBJUqHdV11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b7G1em/btqFnPlC93W/0Ctw3kYuFs4noBJUqHdV11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b7G1em/btqFnPlC93W/0Ctw3kYuFs4noBJUqHdV11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb7G1em%2FbtqFnPlC93W%2F0Ctw3kYuFs4noBJUqHdV11%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;FSRCNN이 갖는 또 한 가지 중요한 장점은 encoder 부분에 있는 모든 convolution 레이어가 decoder의 upscaling factor가 달라져도 동일하게 재사용될 수 있기 때문에 다양한 upsampling factor에 대해서 학습, 테스트가 용이하다는 것이다. 따라서 학습 과정에서 encoder의 convolution 레이어는 pretraining된 것을 그대로 사용하고, decoder의 transposed convolution만 upsampling factor에 맞게 fine-tuning하면 된다. 마찬가지로, 테스트 과정에서 역시 encoder의 convolution은 한 번만 수행하고 decoder의 transposed convolution만 각 upsampling factor에 맞게 따로 수행하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Fast Super-Resolution by CNN&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. SRCNN&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl61Yf/btqFoQcUvGa/QrTjBH8PKE6yTuXYKZpyik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl61Yf/btqFoQcUvGa/QrTjBH8PKE6yTuXYKZpyik/img.png&quot; data-alt=&quot;SRCNN 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl61Yf/btqFoQcUvGa/QrTjBH8PKE6yTuXYKZpyik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl61Yf%2FbtqFoQcUvGa%2FQrTjBH8PKE6yTuXYKZpyik%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRCNN 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;FSRCNN을 살펴보기 전에 SRCNN에 대해서 먼저 간단히 살펴볼 필요가 있다. SRCNN에서는 bicubic interpolation을 통해서 전처리 된 LR 이미지 $Y$와 HR 이미지 $X$ 간의 E2E mapping function인 $F$를 학습하는 것이 목적이다. 위의 그림에서 볼 수 있듯, 총 3 단계를 거쳐서 HR 이미지를 얻어내는데, 각각의 부분의 역할은 다음과 같다. ($W_i$는 convolution의 filter 파라미터, $B_i$는 bias, $*$는 convolution 연산을 의미한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) Patch extraction and representation (Conv + ReLU)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;380&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtcmer/btqFoBGZhZx/eKXHMP30Zkoit58pqiAv0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtcmer/btqFoBGZhZx/eKXHMP30Zkoit58pqiAv0k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtcmer/btqFoBGZhZx/eKXHMP30Zkoit58pqiAv0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdtcmer%2FbtqFoBGZhZx%2FeKXHMP30Zkoit58pqiAv0k%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;380&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Bicubic interpolation을 통해 전처리된 입력 이미지 $Y$에 대해서 patch들을 추출해내고, 각 patch가 다차원 feature vector로 표현되도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) Non-linear mapping (Conv + ReLU)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;410&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Na7ea/btqFnPze7es/5BoNDkySx192XYDvGtOylK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Na7ea/btqFnPze7es/5BoNDkySx192XYDvGtOylK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Na7ea/btqFnPze7es/5BoNDkySx192XYDvGtOylK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNa7ea%2FbtqFnPze7es%2F5BoNDkySx192XYDvGtOylK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;410&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비선형 매핑 함수를 통해서 LR feature vector를 또 다른 다차원 feature vector(HR feature vector)로 매핑한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) Reconstruction (Conv)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;350&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cmEkhF/btqFogwqgZj/WOSIhnQ2SZ6ValJInPnuN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cmEkhF/btqFogwqgZj/WOSIhnQ2SZ6ValJInPnuN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cmEkhF/btqFogwqgZj/WOSIhnQ2SZ6ValJInPnuN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcmEkhF%2FbtqFogwqgZj%2FWOSIhnQ2SZ6ValJInPnuN0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;350&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HR feature vector로부터 최종 출력 HR 이미지를 얻는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. FSRCNN&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UtSA2/btqFnD6LpjG/LpYcRRunk4i8BA2XP0zudk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UtSA2/btqFnD6LpjG/LpYcRRunk4i8BA2XP0zudk/img.png&quot; data-alt=&quot;SRCNN vs FSRCNN&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UtSA2/btqFnD6LpjG/LpYcRRunk4i8BA2XP0zudk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUtSA2%2FbtqFnD6LpjG%2FLpYcRRunk4i8BA2XP0zudk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SRCNN vs FSRCNN&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;FSRCNN은 총 5단계의 네트워크 구조를 갖는다. 앞의 네 단계는 convolution 레이어들로 구성되어 있고, 마지막 단계는 transposed convolution 레이어로 구성된다. 편의를 위해 다음과 같이 notation을 정의하기로 하자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$f_i$: $i$번째 레이어의 필터 크기&lt;/li&gt;
&lt;li&gt;$n_i$: $i$번째 레이어의 필터 개수 (= 출력 채널 수)&lt;/li&gt;
&lt;li&gt;$c_i$: $i$번째 레이어의 입력 채널 수&lt;/li&gt;
&lt;li&gt;$Conv(f_i, n_i, c_i)$: convolution&lt;/li&gt;
&lt;li&gt;$DeConv(f_i, n_i, c_i)$: transposed convolution&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(1) Feature extraction: $Conv(5, d, 1)$&lt;/h4&gt;
&lt;p&gt;이 부분은 SRCNN에서 첫 번째 단계와 동일한 연산을 수행하지만 입력으로 들어오는 이미지가 bicubic interpolation 전처리를 거친 이미지($Y$)가 아니라 original LR 이미지($Y_s$)라는 점에서 차이가 있다. SRCNN에서는 첫 번째 레이어에서 9x9 필터를 사용하는데, 이는 interpolation을 통해 upscaling 된 이미지인 $Y$에 대해 적용되는 것이다. 따라서 interpolation 없이 original LR 이미지 $Y_s$를 사용하는 FSRCNN에서는 5x5 필터를 사용한다. $Y$의 대부분 픽셀들은 $Y_s$에서 interpolate 된 것이므로 이렇게 하더라도 정보의 소실은 거의 없다고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;첫 번째 레이어의 입력 채널 수 $c_1$은 SRCNN과 같이 1로 설정하였고, 출력 채널 수 $n_1$는 $d$로 설정하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(2) Shrinking: $Conv(1, s, d)$&lt;/h4&gt;
&lt;p&gt;SRCNN에서는 non-linear mapping 단계에서 LR feature space에서 HR feature space로의 매핑이 바로 이루어졌지만, LR feature 벡터의 채널 수(= $d$)가 일반적으로 상당히 큰 값이기 때문에 이대로 convolution을 수행하면 연산 비용이 너무 커진다. 따라서 GoogLeNet을 비롯한 여러 모델들이 활용한 1x1 convolution을 통해 채널 수를 줄임으로써 연산 오버헤드를 감소시킨다. 출력 채널 수에 해당하는 $s$는 입력 채널 수 $d$보다 더 작은 수이다. ($n_2 = s &amp;lt;&amp;lt; d$)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(3) Non-linear mapping: $Conv(3, s, s) \times m$&lt;/h4&gt;
&lt;p&gt;Non-linear mapping 단계는 SR 성능에 가장 큰 영향을 주는 핵심 단계이며, 이 때 제일 중요한 요소가 레이어의 너비(필터 개수)와 깊이(레이어 개수)이다. SRCNN에서는 5x5 레이어를 사용했을 때 1x1 레이어를 사용했을 때보다 좋은 성능을 보였지만 이는 레이어의 깊이가 얕을 때에 대해서만 실험한 것이었다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;FSRCNN에서는 1과 5의 중간 크기인 3을 필터 크기로 잡았고, 줄어든 필터 크기로 절약된 연산량만큼 레이어의 깊이를 늘렸다. (총 $m$개의 3x3 convolution 레이어) 이 때 feature map 채널 수의 일관성을 위하여 필터 개수는 입력 채널 수와 같은 $s$로 잡았다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;(4) Expanding: $Conv(1, d, s)$&lt;/h4&gt;
&lt;p&gt;Shrinking 단계에서 1x1 convolution을 통해서 채널 수를 줄여 연산량을 감소시켰다면, expanding 단계에서는 SR 성능을 위해서 feature map 채널 수를 shrinking 이전으로 복원한다. 이 때도 shrinking에서처럼 연산량이 적은 1x1 convolution을 활용한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;(5) Transposed Convolution: $DeConv(9, 1, d)$&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;400&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ryv5V/btqFogpyAWM/jfqaTCK713EsklklvSXcC0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ryv5V/btqFogpyAWM/jfqaTCK713EsklklvSXcC0/img.gif&quot; data-alt=&quot;Transposed Convoluton: 청록색은 출력 feature map, 파란색은 입력 feature map&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ryv5V/btqFogpyAWM/jfqaTCK713EsklklvSXcC0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/ryv5V/btqFogpyAWM/jfqaTCK713EsklklvSXcC0/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;400&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Transposed Convoluton: 청록색은 출력 feature map, 파란색은 입력 feature map&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Transposed convolution은 convolution을 통해 추출한 feature map을 다시 확장시키는 upsampling 기능을 한다. 위의 그림은 stride=2, padding=1인 경우에 deconvolution을 수행한 것이다. Transposed convolution에서는 출력 feature map의 크기가 stride 크기의 배수로 증가하기 때문에 stride가 곧 upsampling factor가 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Transposed convolution의 필터 크기는 transposed convolution의 역에 해당하는 convolution의 필터 크기와 동일하게 잡아야 한다. 예를 들어 3x3 feature map에서 6x6 feature map을 만들어내는 transposed convolution(위의 그림)의 반대는 6x6 feature map에서 3x3 feature map을 추출하는 convolution이다. 6x6 feature map에서 3x3 feature map을 추출하기 위해서는 아래의 그림과 같이 padding=1, stride=2, filter size=3을 사용해야 하는데, 이는 위의 그림에서 살펴본 transposed convolution의 세팅과 완전히 동일하다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;SRCNN의 첫 번째 레이어에서 bicubic interpolation을 통해 upsampling 된 LR 이미지(HR 이미지와 동일한 사이즈)에 convolution을 적용할 때 9x9 필터를 사용했으므로, FSRCNN에서도 같은 크기로 upsampling 하려면 동일하게 9x9 필터를 사용하여 transposed convolution을 수행하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;400&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSTFZo/btqFoAg6iOC/qR2pvL7K9FQKxkifdTOyL0/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSTFZo/btqFoAg6iOC/qR2pvL7K9FQKxkifdTOyL0/img.gif&quot; data-alt=&quot;Convolution: 청록색은 출력 feature map, 파란색은 입력 feature map&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSTFZo/btqFoAg6iOC/qR2pvL7K9FQKxkifdTOyL0/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bSTFZo/btqFoAg6iOC/qR2pvL7K9FQKxkifdTOyL0/img.gif&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;400&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Convolution: 청록색은 출력 feature map, 파란색은 입력 feature map&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. SCRNN과 FSRCNN의 차이점&lt;/h3&gt;
&lt;p&gt;다시 한번 정리하자면, FSRCNN은 다음과 같은 새로운 방법을 도입하여 SRCNN보다 좋은 성능을 보이면서도 더 빠른 속도를 낼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;FSRCNN에서는 SRCNN과 달리 bicubic interpolation을 통해 전처리된 LR 이미지를 사용하지 않고 original LR 이미지를 그대로 사용하여 convolution에서의 연산량을 줄인다.($n^2$배)&lt;/li&gt;
&lt;li&gt;네트워크 후반부에서 upscaling을 위해서 transposed convolution이 활용된다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;SRCNN의 non-linear mapping 단계가 FSRCNN에서는 shrinking, mapping, expanding 세 단계로 분리된다.&lt;/li&gt;
&lt;li&gt;FSRCNN에서는 더 작은 필터 크기를 사용하고, 네트워크의 깊이는 더 깊어졌다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p&gt;[1] &lt;a href=&quot;https://github.com/vdumoulin/conv_arithmetic&quot;&gt;https://github.com/vdumoulin/conv_arithmetic&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1593936042533&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;object&quot; data-og-title=&quot;vdumoulin/conv_arithmetic&quot; data-og-description=&quot;A technical report on convolution arithmetic in the context of deep learning - vdumoulin/conv_arithmetic&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/vdumoulin/conv_arithmetic&quot; data-og-url=&quot;https://github.com/vdumoulin/conv_arithmetic&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/ccEmWh/hyGEoluMPB/xqUkFNp9h4S6tLhS5QedF1/img.jpg?width=400&amp;amp;height=400&amp;amp;face=81_122_218_259&quot;&gt;&lt;a href=&quot;https://github.com/vdumoulin/conv_arithmetic&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/vdumoulin/conv_arithmetic&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/ccEmWh/hyGEoluMPB/xqUkFNp9h4S6tLhS5QedF1/img.jpg?width=400&amp;amp;height=400&amp;amp;face=81_122_218_259');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;vdumoulin/conv_arithmetic&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;A technical report on convolution arithmetic in the context of deep learning - vdumoulin/conv_arithmetic&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;[2] &lt;a href=&quot;https://jamiekang.github.io/2017/04/24/image-super-resolution-using-deep-convolutional-networks/&quot;&gt;https://jamiekang.github.io/2017/04/24/image-super-resolution-using-deep-convolutional-networks/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1593936055227&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Image Super-Resolution Using Deep Convolutional Networks &amp;middot; Pull Requests to Tomorrow&quot; data-og-description=&quot;Image Super-Resolution Using Deep Convolutional Networks 24 Apr 2017 | PR12, Paper, Machine Learning, CNN, SRCNN 이번 논문은 2015년 IEEE Transactions on Pattern Analysis and Machine Intelligence에 발표된 &amp;ldquo;Image Super-Resolution Using Deep Convo&quot; data-og-host=&quot;jamiekang.github.io&quot; data-og-source-url=&quot;https://jamiekang.github.io/2017/04/24/image-super-resolution-using-deep-convolutional-networks/&quot; data-og-url=&quot;https://jamiekang.github.io/2017/04/24/image-super-resolution-using-deep-convolutional-networks/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/Bws98/hyGEd5jM3K/8pkAKPlKtk6DD6tQgf4F5k/img.jpg?width=2045&amp;amp;height=2684&amp;amp;face=0_0_2045_2684,https://scrap.kakaocdn.net/dn/MbpMW/hyGFL7cg49/TaZvUq7XlxIKJeRRRAY3eK/img.jpg?width=3633&amp;amp;height=1474&amp;amp;face=0_0_3633_1474,https://scrap.kakaocdn.net/dn/bzkpEE/hyGFNKHsQC/khyqsiY8TW7S3W75Y6r1Hk/img.jpg?width=3435&amp;amp;height=1501&amp;amp;face=0_0_3435_1501&quot;&gt;&lt;a href=&quot;https://jamiekang.github.io/2017/04/24/image-super-resolution-using-deep-convolutional-networks/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://jamiekang.github.io/2017/04/24/image-super-resolution-using-deep-convolutional-networks/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/Bws98/hyGEd5jM3K/8pkAKPlKtk6DD6tQgf4F5k/img.jpg?width=2045&amp;amp;height=2684&amp;amp;face=0_0_2045_2684,https://scrap.kakaocdn.net/dn/MbpMW/hyGFL7cg49/TaZvUq7XlxIKJeRRRAY3eK/img.jpg?width=3633&amp;amp;height=1474&amp;amp;face=0_0_3633_1474,https://scrap.kakaocdn.net/dn/bzkpEE/hyGFNKHsQC/khyqsiY8TW7S3W75Y6r1Hk/img.jpg?width=3435&amp;amp;height=1501&amp;amp;face=0_0_3435_1501');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Image Super-Resolution Using Deep Convolutional Networks &amp;middot; Pull Requests to Tomorrow&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Image Super-Resolution Using Deep Convolutional Networks 24 Apr 2017 | PR12, Paper, Machine Learning, CNN, SRCNN 이번 논문은 2015년 IEEE Transactions on Pattern Analysis and Machine Intelligence에 발표된 &amp;ldquo;Image Super-Resolution Using Deep Convo&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;jamiekang.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Research</category>
      <category>CV</category>
      <category>FSRCNN</category>
      <category>SRCNN</category>
      <category>논문리뷰</category>
      <category>딥러닝</category>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/62</guid>
      <comments>https://yunmorning.tistory.com/62#entry62comment</comments>
      <pubDate>Sun, 5 Jul 2020 16:57:56 +0900</pubDate>
    </item>
    <item>
      <title>[Facebook 추천 시스템: DLRM] Deep Learning Recommendation Model for Personalization and Recommendation Systems</title>
      <link>https://yunmorning.tistory.com/61</link>
      <description>&lt;p&gt;이번 포스팅에서는 2019년 페이스북에서 나온 추천 시스템 논문인 &lt;a href=&quot;https://arxiv.org/pdf/1906.00091.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DLRM&lt;/a&gt;을 다룬다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Introduction&lt;/h2&gt;
&lt;p&gt;기존 personalization 및 recommendation 태스크에서 딥러닝이 활용된 연구들을 살펴보면 크게 두 부류로 구분할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1. 추천 시스템&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;가장 원시적인 추천 시스템에서는 몇몇 전문가들이 상품들을 몇 개의 카테고리로 묶은 뒤, 유저들이 기호에 따라 카테고리를 선택하도록 하는 방식을 사용하였다.&lt;/li&gt;
&lt;li&gt;이것이 발전되어서 만들어진 것이 과거의 유저의 행동(상품을 장바구니에 넣는다든지, 구독을 한다든지, 좋아요를 누른다든지...)에 기반하여 추천을 하는 CF(collaborative filtering) 기법이다.&lt;/li&gt;
&lt;li&gt;그 밖에도 유저와 연관성이 높은 상품을 함께 grouping하여 추천을 하는 neighborhood method, 행렬 분해를 통해 유저와 상품을 latent vector로 표현하여 활용하는 방법 등이 존재한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;2. Predictive Analysis&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주어진 데이터를 기반으로 확률을 예측하는 방법이다.&lt;/li&gt;
&lt;li&gt;과거에는 간단하게 linear regression, logistic regression 정도를 활용하였다.&lt;/li&gt;
&lt;li&gt;카테고리 데이터를 처리하기 위해서 one-hot, 또는 multi-hot 벡터를 dense representation으로 임베딩하여 사용한다.&lt;/li&gt;
&lt;li&gt;word2vec 등에서 단어 임베딩을 통해 단어의 유사도와 같은 의미상 특징을 벡터 공간상에서 표현하듯이 상품의 특징, 유저의 특징 등을 벡터 공간에서 표현할 수 있게 된다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;DLRM은 위에서 살펴본 두 부류의 방법들에서 유용한 부분을 잘 섞어서 만들어졌다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Model Design and Architecture&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blLlCj/btqFrn2oIFe/ASBPQtcPcrkuCFnDPtubmK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blLlCj/btqFrn2oIFe/ASBPQtcPcrkuCFnDPtubmK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blLlCj/btqFrn2oIFe/ASBPQtcPcrkuCFnDPtubmK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblLlCj%2FbtqFrn2oIFe%2FASBPQtcPcrkuCFnDPtubmK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. DLRM의 구성요소&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1. Embeddings&lt;/h4&gt;
&lt;p&gt;추천 시스템에서 왜 임베딩을 사용하는 이유는 NLP에서 워드 임베딩을 사용하는 이유와 동일하다고 볼 수 있다. NLP에서 감정분석, 개체명 인식, 문서 분류와 같은 문제들을 풀기 위해선 분류 대상의 특징을 수학적으로 표현하여 파악해야 한다. NLP 태스크에서 데이터가 되는 corpus를 가지고 word dictionary를 만든 뒤 이를 one-hot 벡터로 표현하면 어떤 단어가 몇 번째 위치에 있다는 것 외에는 아무런 유의미한 정보를 갖지 못한다. 각 단어가 가지는 '의미'를 표현하기 위해서는 해당 단어의 주변 단어들을 통해 그 단어의 의미를 파악해야 하고, 그를 위해 존재하는 것이 임베딩 기법이다. 임베딩을 거친 후에야 비로소 해당 단어에 대한 의미상 특징을 표현하는 dense 벡터를 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;추천 시스템에서도 마찬가지로 유저들의 리스트, 상품들의 리스트, 어떤 유저가 어떤 상품에 좋아요를 했는지에 대한 리스트 등이 있을 때, 이를 one-hot, multi-hot 벡터로 표현 한다면 의미를 가지지 못한다. 따라서 이러한 정보를 dense represention으로 임베딩할 필요가 있고, 이를 통해서 어떤 categorical feature의 관계적인 특징 등을 입체적으로 파악할 수 있게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;DLRM에서는 categorical feature를 dense representation으로 매핑하기 위하여 임베딩 테이블을 활용한다. $W$라는 임베딩 테이블이 있을 때, $i$번째 아이템의 임베딩 벡터를 얻기 위해서는 단순히 $i$번째 인덱스만 1이고 나머지는 0인 one-hot 벡터 $e_i$를 $W$에 내적하기만 하면 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;200&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqmY21/btqFfugaNOP/vPNPNHYPo5ECgRDuhBkr3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqmY21/btqFfugaNOP/vPNPNHYPo5ECgRDuhBkr3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqmY21/btqFfugaNOP/vPNPNHYPo5ECgRDuhBkr3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqmY21%2FbtqFfugaNOP%2FvPNPNHYPo5ECgRDuhBkr3K%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;200&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;한 개의 임베딩 벡터가 아니라 여러 개의 임베딩 벡터를 lookup하는 경우에도 마찬가지로 임베딩 테이블 $W$에 multi-hot 벡터 $a$를 내적하기만 하면 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$a^T = [0, ..., a_{i_1}, ..., a_{i_k}, ..., 0]$&lt;/li&gt;
&lt;li&gt;$A = [a_1, ..., a_t]$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;200&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhrN6H/btqFcuiiAPp/ZKXYQnxsuclkyo8KEtOZN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhrN6H/btqFcuiiAPp/ZKXYQnxsuclkyo8KEtOZN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhrN6H/btqFcuiiAPp/ZKXYQnxsuclkyo8KEtOZN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhrN6H%2FbtqFcuiiAPp%2FZKXYQnxsuclkyo8KEtOZN0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;200&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2. Matrix Factorization&lt;/h4&gt;
&lt;p&gt;임베딩 테이블을 통해 얻은 임베딩 벡터를 활용하여 정확한 예측을 하기 위해선 introduction에서 잠시 언급한 latent factor method의 인사이트가 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$w_i$: $i$번째 상품의 임베딩 벡터&lt;/li&gt;
&lt;li&gt;$v_j$: $j$번째 유저의 임베딩 벡터&lt;/li&gt;
&lt;li&gt;$r_{ij}$: $j$번째 유저의 $i$번째 아이템에 대한 평가&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같은 notation이 주어졌을 때 DLRM에서는 아래의 식을 objective function으로 활용하여 문제를 해결한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dC0pMb/btqFcuo5BaA/nHPZkgch6XxfMscMt7atR0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dC0pMb/btqFcuo5BaA/nHPZkgch6XxfMscMt7atR0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dC0pMb/btqFcuo5BaA/nHPZkgch6XxfMscMt7atR0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdC0pMb%2FbtqFcuo5BaA%2FnHPZkgch6XxfMscMt7atR0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;식의 의미를 생각해보자.&lt;/p&gt;
&lt;p&gt;NLP에서는 두 단어에 대한 임베딩 벡터가 주어졌을 때, 두 벡터를 내적하여 두 단어 간의 의미상 유사도를 구할 수 있다. 마찬가지로 $i$번째 상품에 대한 임베딩 벡터 $w_i$와 $j$번째 유저에 대한 임베딩 벡터 $v_j$를 내적하면 해당 상품과 해당 유저의 연관도를 계산할 수 있다. 상품과 유저 간의 연관도는 곧 유저가 해당 상품을 구매할 확률, 또는 좋은 평가를 내릴 확률이라고 생각할 수 있다.&amp;nbsp; 위의 식에서 $r_{ij}$는 $j$번째 유저가 실제로 $i$번째 상품에 대해 평가한 true label이고, 여기에서 $w_i$와 $v_j$를 내적한 값을 빼면, 실제 유저의 상품 평가와 모델이 예측한 유저의 상품 평가의 오차를 구할 수 있다. 이렇게 구해진 오차를 최소화 하는 방향으로 학습을 하면 유의미한 예측이 가능할 것이라고 생각할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.3. Factorization Machine&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;일반적으로 예측 문제는 입력 데이터 &lt;span style=&quot;color: #333333;&quot;&gt;$x \in \mathbb{R}^n$&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333;&quot;&gt;과 타겟 레이블 $y \in T$ 간의 매핑을 표현하는 예측 함수 $\phi: \mathbb{R} \to T$를 구하는 것이라고 할 수 있다. (이 때 클릭 확률을 나타내는 $T$는 $T = \{+1, -1\}$이며, $+1$은 '클릭을 함', $-1$은 '클릭을 하지 않음'을 의미한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Factorization mahcine(FM)은 유저와 상품 간의 상호작용을 추정하여 유저가 해당 상품을 클릭(또는 구매)할 확률을 예측하는데, 다음과 같은 식을 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d5Ftxa/btqFo5oFnmC/mfBXzKFhMMlj3P4oMkeA41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d5Ftxa/btqFo5oFnmC/mfBXzKFhMMlj3P4oMkeA41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d5Ftxa/btqFo5oFnmC/mfBXzKFhMMlj3P4oMkeA41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd5Ftxa%2FbtqFo5oFnmC%2FmfBXzKFhMMlj3P4oMkeA41%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;FM은 다음과 같은 장점을 갖는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span&gt;SVM과는 다르게 sparse data에서도 파라미터 추정이 가능하다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;Linear complexity를 갖고 있기 때문에 효율적이다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.4. Multilayer Perceptron&lt;/h4&gt;
&lt;p&gt;딥러닝에서 가장 기본적인 레이어라고 할 수 있는 MLP는 fully connected layer와 활성화 함수 &lt;span style=&quot;color: #333333;&quot;&gt;$\sigma: \mathbb{R} \to \mathbb{R}$&lt;/span&gt;로 이루어져 있으며, 아래의 식과 같이 표현할 수 있다. ($l$번째 레이어에 대해서 $W_l \in \mathbb{R}^{n_l \times n_{l-1}}$은 가중치 행렬을 나타내고, $b_l \in \mathbb{R}^{n_l}$은 bias를 나타낸다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p3UyD/btqFrnnSwIX/dzvMtQIJINO59qo26QZnG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p3UyD/btqFrnnSwIX/dzvMtQIJINO59qo26QZnG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p3UyD/btqFrnnSwIX/dzvMtQIJINO59qo26QZnG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp3UyD%2FbtqFrnnSwIX%2FdzvMtQIJINO59qo26QZnG0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;MLP는 FM을 사용하는 경우보다도 복잡한 상호작용을 파악할 때 효과적이다. 그래서 &lt;a href=&quot;https://arxiv.org/pdf/1708.05031.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;NCF(Neural Collaborative Filtering)&lt;/a&gt;에서는 내적을 통해서 임베딩 벡터 간의 상호작용을 계산하지 않고 MLP를 사용한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. DLRM Architecture&lt;/h3&gt;
&lt;p&gt;&lt;b&gt;1.1~1.4&lt;/b&gt;에서는 recommendation system과 predictive analytics에서 일반적으로 사용되는 모델들에 대해서 살펴보았다. DLRM은 이러한 모델들에서 사용하는 기법들을 적절히 혼합하여 만들어진 SOTA personalization 모델이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일반적으로 어떤 대상을 수로 표현하는 경우에 그 대상의 특징을 표현하는 방식은 continuous할 수도 있고, categorical할 수도 있다. Continuous feature는 가격, 나이, 연봉 등과 같이 연속적인 값을 통해 표현되는 특징을 의미하고, categorical feature는 성별, 연령대와 같은 카테고리화된 특징을 의미한다. 추천 시스템에서 유저와 상품을 수로 표현할 때 여러 개의 categorical &amp;amp; continuous feature들로 표현이 되는데, categorical feature인지 &lt;span style=&quot;color: #333333;&quot;&gt;continuous&lt;/span&gt; feature인지에 따라서 처리하는 방식이 달라진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Categorical feature의 경우, 각 feature는 임베딩을 통해 동일한 차원의 임베딩 벡터로 표현된다.&lt;/li&gt;
&lt;li&gt;Continuous feature의 경우, 각 feature는 MLP를 통해 임베딩 벡터와 동일한 길이의 dense representation으로 변환된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위와 같은 방식으로 continuous &amp;amp; categorical feature를 처리하면 각각에 대해 임베딩 벡터와 dense representation을 얻을 수 있는데, 이 둘 간의 상호작용을 계산하기 위해서 내적을 한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;내적을 한 뒤에는 내적을 하여 얻은 결과값 벡터와 내적 이전의 dense feature들을 concat하여 MLP를 거치게 한다. MLP를 거친 결과에 대해 sigmoid를 적용하면 최종 결과 값인 유저가 상품을 클릭할 확률을 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 &lt;a href=&quot;https://github.com/facebookresearch/dlrm/blob/529091f61e62aa29d29cb50faa3634a273ec2bcb/dlrm_s_pytorch.py&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;DLRM PyTorch 구현&lt;/a&gt;에서의 forward 함수이다. 모델에서 사용하는 각각의 연산들은 Table 1에 제시된 것과 같이 프레임워크 상에서 제공하는 함수를 통해 구현되었다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;225&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lcvqy/btqFrDD2Fcf/huCzUDJJJ5XiJZT3eLmYXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lcvqy/btqFrDD2Fcf/huCzUDJJJ5XiJZT3eLmYXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lcvqy/btqFrDD2Fcf/huCzUDJJJ5XiJZT3eLmYXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Flcvqy%2FbtqFrDD2Fcf%2FhuCzUDJJJ5XiJZT3eLmYXk%2Fimg.png&quot; data-origin-width=&quot;2000&quot; data-origin-height=&quot;225&quot; data-filename=&quot;blob&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1594025814202&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def sequential_forward(self, dense_x, lS_o, lS_i):
    # process dense features (using bottom mlp), resulting in a row vector
    x = self.apply_mlp(dense_x, self.bot_l)
 
    # process sparse features(using embeddings), resulting in a list of row vectors
    ly = self.apply_emb(lS_o, lS_i, self.emb_l)
  
    # interact features (dense and sparse)
    z = self.interact_features(x, ly)
  
    # obtain probability of a click (using top mlp)
    p = self.apply_mlp(z, self.top_l)

    # clamp output if needed
    if 0.0 &amp;lt; self.loss_threshold and self.loss_threshold &amp;lt; 1.0:
        z = torch.clamp(p, min=self.loss_threshold, max=(1.0 - self.loss_threshold))
    else:
        z = p
 
    return z&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;아래는 배치 크기가 1일 때 DLRM의 forward를 GPU 프로파일링한 결과이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ccF6q6/btqFoQSLT0S/SGiasA8t8jlFwlEdrwLKp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ccF6q6/btqFoQSLT0S/SGiasA8t8jlFwlEdrwLKp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ccF6q6/btqFoQSLT0S/SGiasA8t8jlFwlEdrwLKp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FccF6q6%2FbtqFoQSLT0S%2FSGiasA8t8jlFwlEdrwLKp0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Research</category>
      <category>DLRM</category>
      <category>Facebook</category>
      <category>Recommendation</category>
      <category>추천시스템</category>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/61</guid>
      <comments>https://yunmorning.tistory.com/61#entry61comment</comments>
      <pubDate>Tue, 30 Jun 2020 02:04:14 +0900</pubDate>
    </item>
    <item>
      <title>[CV Study] Densely Connected Convolutional Networks</title>
      <link>https://yunmorning.tistory.com/60</link>
      <description>&lt;p&gt;ResNet에서는 깊은 네트워크에서 발생하는 gradient vanishing 문제를 해결하기 위해 block 마다 한 개의 shortcut connection을 두었다. 이번에 살펴볼 DenseNet은 shortcut connection을 극단적으로 늘린 케이스라고 생각하면 된다. 얼마나 극단적이냐면 어떤 레이어와 그 이전에 있던 모든 레이어 간에 direct connection이 존재한다. 즉, $l$번째 레이어에는 $0$~$l-1$번째 레이어의 모든 출력이 입력으로 들어오고, $l$번째 레이어의 출력은 그 이후에 등장하는 $L-l$개의 레이어로 전달된다. VGG에서는 총 $L$개의 레이어가 존재하는 경우에, 전체 connection의 개수는 $L$개였다. 반면 DenseNet에서는 $L$개의 레이어가 존재할 때 무려 $1+2+&amp;nbsp; ... + L = \frac{L(L+1)}{2}$개의 connection이 생긴다. 이렇게 layer간의 상호작용을 극대화함으로써 다음과 같은 이점을 얻을 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Gradient vanishing 문제를 해결&lt;/li&gt;
&lt;li&gt;모든 레이어가 연결되어 있어서 feature propagation이 잘 이루어짐 (빠른 학습이 가능)&lt;/li&gt;
&lt;li&gt;Dense connection을 통해 이전 레이어들에서 구한 feature가 재사용될 수 있다.&lt;/li&gt;
&lt;li&gt;Parameter 수 감소&lt;/li&gt;
&lt;li&gt;Dense connection 자체가 정규화 효과가 있어서 오버피팅 방지&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Dense connection은 얼핏보면 비효율적이지 않을까라는 생각이 들지만 생각보다 매우 효율적이다. ResNet에서는 네트워크의 깊이를 깊게 만들어서 성능을 높였고, GoogLeNet에서는 같은 level에 여러 레이어(1x1 Conv, 3x3 Conv, 5x5 Conv)를 두고 이를 concat하여 다양한 feature를 학습하였다. 반면 DenseNet에서는 $l$번째 레이어가 $0$~$l-1$번째 레이어에서 추출한 모든 feature map을 concat하여 입력으로 받기 때문에 추가적인 연산 없이 input variation을 높일 수 있다. 이전 레이어들의 output을 입력으로 받는 데엔 별다른 연산이 필요하지 않기 때문에 연산량이나 파라미터 수에 있어서 매우 효율적이며, input variation이 높아지는 것을 통해 정규화 효과를 누릴 수도 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;DenseNet&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;700&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P63RC/btqEPvHoJoI/W76A9KXsVFi7gyALCZPaQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P63RC/btqEPvHoJoI/W76A9KXsVFi7gyALCZPaQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P63RC/btqEPvHoJoI/W76A9KXsVFi7gyALCZPaQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP63RC%2FbtqEPvHoJoI%2FW76A9KXsVFi7gyALCZPaQk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;700&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Dense Connectivity&lt;/h3&gt;
&lt;p&gt;DenseNet의 핵심은 dense connectivity라고 할 수 있다. 일단 다음과 같이 notation을 정하도록 하자.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$x_0$&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;네트워크로 들어오는 최초 입력 데이터&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$L$&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;전체 레이어 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$H_l(x)$&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$x$가 입력으로 들어온 $l$ 번째 non-linear transformation 레이어 (일반적으로 Conv + BN + ReLU 조합으로 구성됨)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$x_l$&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$l$ 번째 레이어의 출력 feature map&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;ResNet의 경우, Conv + BN + ReLU 조합에 identity shortcut connection이 합쳐져 있으므로 block 하나의 동작을 다음 식으로 표현할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDVreQ/btqEO0uB5yI/fGpf9dwlchhCDfJoKxy9oK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDVreQ/btqEO0uB5yI/fGpf9dwlchhCDfJoKxy9oK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDVreQ/btqEO0uB5yI/fGpf9dwlchhCDfJoKxy9oK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDVreQ%2FbtqEO0uB5yI%2FfGpf9dwlchhCDfJoKxy9oK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;ResNet에서 shortcut connection은 gradient vanishing을 해결하기 위해서 존재하는데, summation 연산을 통해서 정보가 전달되기 때문에 concat 연산과 비교했을 때 정보 전달력이 떨어진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;DenseNet에서 각 레이어의 동작은 다음과 같이 표현 가능하다. $l$번째 레이어는 네트워크 입력값인 $x_0$부터 $l-1$번째 레이어의 출력값인 $x_{l-1}$까지의 모든 값을 입력으로 받는다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G5j4x/btqEPuIwr2h/K4RJt4nXKuRWyo4Yr4j5T0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G5j4x/btqEPuIwr2h/K4RJt4nXKuRWyo4Yr4j5T0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G5j4x/btqEPuIwr2h/K4RJt4nXKuRWyo4Yr4j5T0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG5j4x%2FbtqEPuIwr2h%2FK4RJt4nXKuRWyo4Yr4j5T0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;이 때 $[x_0, x_1, ..., x_{l-1}]$은 $x_0$부터 $x_{l-1}$까지의 값들을 모두 concat한다는 의미이다. Concatenation 연산은 summation 연산과 비교했을 때 연산량도 적고, information flow에도 유리하다는 장점이 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Composite Function&lt;/h3&gt;
&lt;p&gt;DenseNet에서는 대부분의 최신 CNN 모델들에서 그러하듯 3x3 Conv + BN + ReLU를 하나의 composite function으로 사용했다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Pooling Layers&lt;/h3&gt;
&lt;p&gt;앞에서 살펴본 concat 연산(&lt;span style=&quot;color: #333333;&quot;&gt;$[x_0, x_1, ..., x_{l-1}]$&lt;/span&gt;)의 경우에 feature map의 사이즈가 일치하지 않으면 사용할 수 없다. 하지만 실제 CNN 네트워크에서 downsampling은 필수적이므로 레이어마다 feature map 크기가 달라질 수밖에 없다. 이 문제를 해결하기 위해서 DenseNet에선 아래의 그림과 같이 전체 네트워크를 3개의 dense block으로 분리하고 각 block 사이에 2x2 average pooling과 1x1 convolution을 두어서 feature map 크기를 조절하게 하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6UHPm/btqEPuIumka/EEdYySXfFPkxmjOAeiEoG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6UHPm/btqEPuIumka/EEdYySXfFPkxmjOAeiEoG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6UHPm/btqEPuIumka/EEdYySXfFPkxmjOAeiEoG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6UHPm%2FbtqEPuIumka%2FEEdYySXfFPkxmjOAeiEoG1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Growth Rate&lt;/h3&gt;
&lt;p&gt;DenseNet에서는 이전에 존재한 모든 레이어의 출력값들을 concat하여 입력으로 사용하기 때문에 각 레이어의 feature map의 수(= channel 수)를 작게 잡을 필요가 있다. DenseNet에는 &lt;i&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;growth rate $k$&lt;/b&gt;&lt;/span&gt;&lt;/i&gt;라는 하이퍼파라미터를 두고 있는데, $k$는 각 레이어($H_l$)의 output feature map 수(= ouput channel 수 = filter 개수)로 사용된다. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;$k$에 따라서 각 레이어에서 얼마나 많은 양의 feature가 이후에 등장하는 레이어들에게 전달될 지가 결정된다.&lt;/span&gt; 다시 말하면, $k$가 각 레이어의 global state contribution을 정하는 scale factor가 된다.&lt;/p&gt;
&lt;p&gt;DenseNet에선 $k=12$ 정도의 작은 growth rate를 사용했는데, 이 정도로도 SOTA 성능을 내기에 충분하였다고 한다. 그 이유에 대해서 생각해보면, 각 레이어는 모든 이전 레이어의 feature map에 대한 직접적인 접근을 하기 때문에 일종의 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;&lt;b&gt;&quot;collective knowledge&quot;&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;를 학습할 수 있기 때문이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Bottleneck Layers&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lF7Z1/btqEPtJE2Nl/WT3uqpnpOqoCWgrRjN7Ku1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lF7Z1/btqEPtJE2Nl/WT3uqpnpOqoCWgrRjN7Ku1/img.png&quot; data-alt=&quot;ResNet과 DenseNet의 bottleneck block [1]&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lF7Z1/btqEPtJE2Nl/WT3uqpnpOqoCWgrRjN7Ku1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlF7Z1%2FbtqEPtJE2Nl%2FWT3uqpnpOqoCWgrRjN7Ku1%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ResNet과 DenseNet의 bottleneck block [1]&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;DenseNet에서도 ResNet과 비슷하게 1x1 Conv를 사용하는 bottleneck block이 존재한다. ResNet에서는 3x3 Conv이전에 1x1 Conv를 두어서 연산량이 큰 3x3 Conv로 들어가는 input featrue map의 채널 수를 줄이도록 하였다. DenseNet에서는 1x1 Conv에서 $4k$개의 필터를 사용해서 $4k$개 채널을 갖는 feature map이 3x3 Conv로 입력되도록 하였다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6.&amp;nbsp; Compression&lt;/h3&gt;
&lt;p&gt;DenseNet에서는 모델을 더욱 가볍게 만들기 위하여 transition layer들에서 feature map 수를 줄이도록 하였다. Compression factor인 $\theta$에 따라서 dense block에서 나온 feature map의 $m$개의 채널은 transition layer를 거치면서 $\lfloor \theta m \rfloor$개로 줄어든다. ($0 &amp;lt; \theta \leq 1)$&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Torchvision DenseNet-161 Graph&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-filename=&quot;스크린샷 2020-06-14 오전 2.08.08.png&quot; data-origin-width=&quot;3162&quot; data-origin-height=&quot;2134&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MZUjX/btqEOEFAAWp/K8qtKaHfKBfvqSJQm9KVKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MZUjX/btqEOEFAAWp/K8qtKaHfKBfvqSJQm9KVKK/img.png&quot; data-alt=&quot;DenseNet-161&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MZUjX/btqEOEFAAWp/K8qtKaHfKBfvqSJQm9KVKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMZUjX%2FbtqEOEFAAWp%2FK8qtKaHfKBfvqSJQm9KVKK%2Fimg.png&quot; data-filename=&quot;스크린샷 2020-06-14 오전 2.08.08.png&quot; data-origin-width=&quot;3162&quot; data-origin-height=&quot;2134&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DenseNet-161&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;DenseNet-161 그래프의 일부분을 캡쳐한 것인데 총&amp;nbsp;&lt;span style=&quot;color: #333333;&quot;&gt;&lt;span&gt;13041&lt;/span&gt;개의 edge가 존재한다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style8&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p&gt;[1] &lt;a href=&quot;https://hoya012.github.io/blog/DenseNet-Tutorial-1/&quot;&gt;https://hoya012.github.io/blog/DenseNet-Tutorial-1/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1592110762688&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;DenseNet&amp;nbsp;Tutorial&amp;nbsp;[1] Paper Review &amp;amp; Implementation details&quot; data-og-description=&quot;대표적인 CNN architecture인 DenseNet에 대한 리뷰와 구현을 위한 detail들을 분석하고 정리하였습니다.&quot; data-og-host=&quot;hoya012.github.io&quot; data-og-source-url=&quot;https://hoya012.github.io/blog/DenseNet-Tutorial-1/&quot; data-og-url=&quot;https://hoya012.github.io//blog/DenseNet-Tutorial-1/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/3YotY/hyGoPjhqvS/ak86rTDYXWkJTxRzuTk9Y0/img.png?width=583&amp;amp;height=289&amp;amp;face=0_0_583_289,https://scrap.kakaocdn.net/dn/bAYz3z/hyGqdbKhe6/elvb0XedlAwvkgRCKYBJL1/img.png?width=2460&amp;amp;height=622&amp;amp;face=0_0_2460_622,https://scrap.kakaocdn.net/dn/cnFONb/hyGoZ7fG5F/DJxCfMixx4rETOn4EJ2Gsk/img.png?width=1410&amp;amp;height=800&amp;amp;face=0_0_1410_800&quot;&gt;&lt;a href=&quot;https://hoya012.github.io/blog/DenseNet-Tutorial-1/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hoya012.github.io/blog/DenseNet-Tutorial-1/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/3YotY/hyGoPjhqvS/ak86rTDYXWkJTxRzuTk9Y0/img.png?width=583&amp;amp;height=289&amp;amp;face=0_0_583_289,https://scrap.kakaocdn.net/dn/bAYz3z/hyGqdbKhe6/elvb0XedlAwvkgRCKYBJL1/img.png?width=2460&amp;amp;height=622&amp;amp;face=0_0_2460_622,https://scrap.kakaocdn.net/dn/cnFONb/hyGoZ7fG5F/DJxCfMixx4rETOn4EJ2Gsk/img.png?width=1410&amp;amp;height=800&amp;amp;face=0_0_1410_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;DenseNet&amp;nbsp;Tutorial&amp;nbsp;[1] Paper Review &amp;amp; Implementation details&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;대표적인 CNN architecture인 DenseNet에 대한 리뷰와 구현을 위한 detail들을 분석하고 정리하였습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;hoya012.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Research</category>
      <category>DenseNet</category>
      <category>DenseNet 리뷰</category>
      <category>DenseNet 설명</category>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/60</guid>
      <comments>https://yunmorning.tistory.com/60#entry60comment</comments>
      <pubDate>Sun, 14 Jun 2020 02:32:59 +0900</pubDate>
    </item>
    <item>
      <title>[CV Study] ShuffleNet: An Extremely Efficient Convolutional Neural Network for M</title>
      <link>https://yunmorning.tistory.com/59</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;&lt;a href=&quot;https://yunmorning.tistory.com/58&quot; target=&quot;_blank&quot;&gt;MobileNet&lt;/a&gt; 논문의 제목인 &quot;MobileNet: Efficient Convolutional Neural Networks for Mobile Vision Applications&quot;과 ShuffleNet 논문 제목이 상당히 비슷한 것을 보면 알 수 있듯, ShuffleNet 역시 MobileNet처럼 보다 가벼운 모델을 만드는 데 중점을 두고 있으며 MobileNet 보다 뛰어난 성능을 보인다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;MobileNet이나 GoogLeNet에서는 1x1 convolution(pointwise convolution)이 연산량과 파라미터 수를 줄이는데 효율적이어서 적극적으로 활용되었다. 하지만 Xception이나 ResNeXt의 op별 연산량을 살펴보면 1x1 conv에서 매우 많은 시간을 소요하고 있다는 사실을 발견할 수 있다. (ResNeXt의 경우 matmul-add 연산의 93.4%가 1x1 conv에서 이루어진다.) 즉, 3x3 conv와 같은 경우엔 연산량이 많이 들 것을 알기 때문에 group conv, 또는 depthwise conv로 처리하여 효율성을 확보하였으나 정작 연산량이 적을 것이라 생각한 1x1 conv에서 생각보다 많은 시간이 소요되고 있는 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;이와 같은 문제를 해결하기 위하여 ShuffleNet에선 다음 두 가지 operation을 제안한다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;&lt;li&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;Pointwise Group Convolution&lt;/span&gt;&lt;/li&gt;&lt;li&gt;Channel Shuffle&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;Group Convolution이란?&lt;/span&gt;&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Pointwise group convolution을 설명하기 앞서 기본적인 group convolution을 먼저 이해해야 한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/TRgNP/btqEOEyrBr5/tJj0wixZTvhoboJoQNCmxK/img.png&quot; width=&quot;700.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TRgNP/btqEOEyrBr5/tJj0wixZTvhoboJoQNCmxK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TRgNP/btqEOEyrBr5/tJj0wixZTvhoboJoQNCmxK/img.png&quot; data-alt=&quot;Grouped Convolution: 2개의 filter group을 사용한 경우&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TRgNP/btqEOEyrBr5/tJj0wixZTvhoboJoQNCmxK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTRgNP%2FbtqEOEyrBr5%2FtJj0wixZTvhoboJoQNCmxK%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/TRgNP/btqEOEyrBr5/tJj0wixZTvhoboJoQNCmxK/img.png&quot; width=&quot;700.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Grouped Convolution: 2개의 filter group을 사용한 경우&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;&lt;a href=&quot;https://yunmorning.tistory.com/58&quot; target=&quot;_blank&quot;&gt;MobileNet 포스팅&lt;/a&gt;에서 depthwise conv, pointwise conv, depthwise separable conv에 대해서 살펴 보았는데, group conv는 depthwise conv를 떠올리면 쉽게 이해할 수 있다. 위의 그림에선 2개의 그룹을 사용하였고($g=2$), 이에 따라 좌측의 input feature map의 채널 수 $c_1$과 우측의 output feature map의 채널 수 $c_2$가 2등분 된다. 이렇게 2개로 나뉘어진 그룹에서 독립적으로 convolution이 이루어진다. 좀 더 쉽게 설명하자면&lt;/span&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;, $c_1$개의 input feature map 중 $1$~$c_1/2$번째 채널의 feature map들은 $1$~$c_2/2$번째 필터들을 통해 $1$~$c_2/2$ 번째 output feature map들로 mapping 되고,&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt; $c_1/2-1$~$c_1$번째 채널의 inpute feature map들은 $c_2/2-1$~$c_2$번째 필터들을 통해 $c_2/2-1$~$c_2$ 번째 output feature map으로 mapping 된다. MobileNet에서 사용한 depthwise conv는 $g = c_1$인 group conv와 동일하다고 생각하면 된다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Group conv를 사용하면 다음과 같은 효과를 거둘 수 있다[1].&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;ol style=&quot;list-style-type: decimal;&quot;&gt;&lt;li&gt;병렬처리에 효율적&lt;/li&gt;&lt;li&gt;파라미터 수 감소 (채널 방향의 연산이 줄어들기 때문)&lt;/li&gt;&lt;li&gt;각 channel group 마다 높은 coorelation을 갖는 feature들이 학습된다.&amp;nbsp;&lt;/li&gt;&lt;/ol&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;위 세 가지는 모두 장점 같아 보이지만 3번 특징으로 인해서 문제가 발생한다. &lt;span style=&quot;background-color: rgb(246, 225, 153);&quot;&gt;Channel group 마다 독립적으로 convolution 연산이 수행되기 때문에 서로 다른 group에 있는 채널끼리는 feature 정보가 교환되지 못하고 막혀있다.&lt;/span&gt; 이는 분명 유의미한 feature 추출에는 방해가 되는 요소이다. ShuffleNet에서는 이 문제를 &lt;b&gt;&lt;i&gt;&lt;u&gt;&lt;span style=&quot;color: rgb(238, 35, 35);&quot;&gt;channel shuffle&lt;/span&gt;&lt;/u&gt;&lt;/i&gt;&lt;/b&gt; 연산을 통해서 해결한다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;Channel Shuffle for Group Convolutions&lt;/span&gt;&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/Smygi/btqEOD7qwJR/aDXRVwipnihAMviKGNVdHK/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Smygi/btqEOD7qwJR/aDXRVwipnihAMviKGNVdHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Smygi/btqEOD7qwJR/aDXRVwipnihAMviKGNVdHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Smygi/btqEOD7qwJR/aDXRVwipnihAMviKGNVdHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSmygi%2FbtqEOD7qwJR%2FaDXRVwipnihAMviKGNVdHK%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/Smygi/btqEOD7qwJR/aDXRVwipnihAMviKGNVdHK/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;(a)는 3개의 channel group을 통한 group convolution의 모습을 묘사하고 있다. 그림에서도 보이듯이 빨간색 그룹, 초록색 그룹, 파란색 그룹 간에 독립적으로 convolution이 이루어지므로 다른 channel group 간엔 정보가 막혀있는 상태이다. 이를 해결하기 위해 (b)와 같이 각 채널 그룹 내의 채널들을 한 번 더 3등분 하여 sub-group을 만들고 이를 각 channel group끼리 교환한다. 그 결과 (c)와 같이 채널 간의 정보가 고루 섞인 모습이 된다. &lt;/span&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;Channel shuffle의 또 다른 장점은 미분가능하기 때문에 gradient를 구할 수 있고 E2E로 학습가능하다는 것이다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;ShuffleNet Unit&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/dEdSDK/btqEPmcy0Pa/fXFZ1AWhkWkapeGvdMzQ8K/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEdSDK/btqEPmcy0Pa/fXFZ1AWhkWkapeGvdMzQ8K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEdSDK/btqEPmcy0Pa/fXFZ1AWhkWkapeGvdMzQ8K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEdSDK/btqEPmcy0Pa/fXFZ1AWhkWkapeGvdMzQ8K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEdSDK%2FbtqEPmcy0Pa%2FfXFZ1AWhkWkapeGvdMzQ8K%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/dEdSDK/btqEPmcy0Pa/fXFZ1AWhkWkapeGvdMzQ8K/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;앞서 ShuffleNet에선 1x1 Conv의 오버헤드를 줄이는 것이 주요한 목적이라 하였다. 위의 그림을 보면 ShuffleNet에서 어떻게 오버헤드를 줄였는지 알 수 있다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;(a)에선 MobileNet에서 처럼 1x1 Conv를 하고 3x3 depthwise conv를 수행하고 있다(depthwise separable convolution). 여기에서 1x1 Conv의 오버헤드를 줄이기 위해서 (b)와 같이 &lt;span style=&quot;background-color: rgb(246, 225, 153);&quot;&gt;1x1 Conv를 group conv로&lt;/span&gt; 처리하고, 그 뒤에 &lt;span style=&quot;background-color: rgb(246, 225, 153);&quot;&gt;channel shuffle&lt;/span&gt; 연산을 하여 효과적인 feature 추출을 보장하도록 한다. 두 번째 1x1 GConv에선 channel shuffle을 따로 하지 않고 있는데, 그 이유는 channel shuffle을 빼더라도 성능상의 손실이 없었기 때문이라고 한다. 마지막으로 (c)는 convolution에서 1보다 큰 stride가 사용되는 경우에 쓰이는 모듈 구조이다. 3x3 DWConv에서 stride가 2이기 때문에 차원을 맞추기 위해서 shorcut connection 부분에 stride 2짜리 AVG Pool을 넣어준다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;이렇게 pointwise group convolution을 적용한 결과 연산량이 상당히 줄어들게 된다. 입력 데이터의 크기가 $c \times h \times w$, bottleneck channel 개수가 $m$, channel group 개수가 $g$일 때 ResNet, ResNeXt, SqueezeNet의 FLOPs를 비교하면 다음과 같다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;ul style=&quot;list-style-type: disc;&quot;&gt;&lt;li&gt;ResNet: $hw(2cm + 9m^2)$ FLOPs&lt;/li&gt;&lt;li&gt;ResNeXt: $hw(2cm + 9m^2/g)$ FLOPs&lt;/li&gt;&lt;li&gt;SqueezeNet: $hw(2cm/g + gm)$ FLOPs&lt;/li&gt;&lt;/ul&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;Network Architecture&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;Pointwise group convolution을 통해 상당히 큰 연산량의 감소가 이루어졌기 때문에 동일한 computation budget이 주어진다면 SqueezeNet은 남는 budget을 더 큰 feature map을 만드는 데 사용할 수 있다. 일반적으로 작은 네트워크일수록 효율적으로 feature를 뽑아내기엔 채널 수가 부족하기 때문에 이는 상당히 큰 이득이라 할 수 있다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;아래의 표를 보면 연산 모든 옵션에 대해서 complexity는 140 MFLOPs 정도로 맞춰져 있는데, $g$가 커질수록 더 많은 수의 채널을 사용한 것을 볼 수 있다. 즉, group convolution을 통해 절약한 computaion budget을 channel 수를 늘리는 데 사용한 것이다.&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/GReZ3/btqEQt9GrDz/hkpeP1AxngqnewSshFwOo1/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GReZ3/btqEQt9GrDz/hkpeP1AxngqnewSshFwOo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GReZ3/btqEQt9GrDz/hkpeP1AxngqnewSshFwOo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GReZ3/btqEQt9GrDz/hkpeP1AxngqnewSshFwOo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGReZ3%2FbtqEQt9GrDz%2FhkpeP1AxngqnewSshFwOo1%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/GReZ3/btqEQt9GrDz/hkpeP1AxngqnewSshFwOo1/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;전체적인 네트워크 구조를 보면 3개의 stage들로 구성이 되어 있고, stage가 넘어갈 때마다 channel 수가 2배가 된다. 그룹 개수인 $g$는 pointwise convolution의 connection sparsity를 결정하는 하이퍼파라미터이며, $g$가 증가함에 따라 연산량이 줄어들어서 줄어든 만큼의 computation budget을 channel 수를 늘리는 데 활용할 수 있다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;논문에 제시된 실험 결과를 보면 보통 $g$를 어느정도 높게 잡았을 때 성능이 더 좋은 것을 알 수 있다. (아래의 표에서 각 숫자는 classification error를 나타낸다.)&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/9rWIY/btqEOaj8EIA/tSOc31g5f4KMJe7dgeuDa1/img.png&quot; width=&quot;800.0&quot; data-origin-width=&quot;1844.0&quot; data-origin-height=&quot;440.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9rWIY/btqEOaj8EIA/tSOc31g5f4KMJe7dgeuDa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9rWIY/btqEOaj8EIA/tSOc31g5f4KMJe7dgeuDa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9rWIY/btqEOaj8EIA/tSOc31g5f4KMJe7dgeuDa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9rWIY%2FbtqEOaj8EIA%2FtSOc31g5f4KMJe7dgeuDa1%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/9rWIY/btqEOaj8EIA/tSOc31g5f4KMJe7dgeuDa1/img.png&quot; width=&quot;800.0&quot; data-origin-width=&quot;1844.0&quot; data-origin-height=&quot;440.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;위의 표를 보면 &quot;ShuffleNet 1X&quot;, &quot;ShuffleNet 0.5X&quot;와 같이 뒤에 어떤 scale factor 값이 붙어 있는데 이는 computation complexity에 대한 것이다. 즉, &quot;ShuffleNet $s$X&quot;는 &quot;ShuffleNet 1X&quot;에서 사용한 필터의 $s$배만큼의 필터를 사용했다는 의미이며, $s$배 만큼의 필터를 사용했기 때문에 complexity는 약 $s^2$배가 된다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;ShuffleNet에서 한 가지 더 주목할 점은 3x3 depthwise convolution을 오직 bottleneck block에서만 사용했다는 점이다. DWConv는 MobileNet에서 말하듯이 이론상으로는 연산 cost가 크지 않지만 low-power mobile device에서 이를 실제로 사용하는 경우엔 상당히 비효율성이 크다.&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; contenteditable=&quot;false&quot; data-ke-style=&quot;style7&quot;&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;Reference&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;[1] &lt;a href=&quot;https://hichoe95.tistory.com/48&quot; target=&quot;_blank&quot;&gt;https://hichoe95.tistory.com/48&lt;/a&gt;&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;Different types of Convolutions (Grouped convolution, depthwise convolution, pointwise convolution, depthwise separable convolut&quot; data-og-description=&quot;오늘 정리해 볼 것들은 앞으로 정리할 논문들을 위해 미리 알아두면 좋은 convolution입니다. 각 convolution들에 대한 간단한 특징과 param수, 연산량 등에대해서 알아봅시다 ㅎㅎ 들어가기에 앞서 몇��&quot; data-og-host=&quot;hichoe95.tistory.com&quot; data-og-source-url=&quot;https://hichoe95.tistory.com/48&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/BxiY6/hyGo1q2yGO/QpR1T4bcZX4VfgadVSx7oK/img.png?width=800&amp;amp;height=547&amp;amp;face=0_0_800_547&quot; data-og-url=&quot;https://hichoe95.tistory.com/48&quot;&gt;&lt;a href=&quot;https://hichoe95.tistory.com/48&quot; target=&quot;_blank&quot; data-source-url=&quot;https://hichoe95.tistory.com/48&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image:url(https://scrap.kakaocdn.net/dn/BxiY6/hyGo1q2yGO/QpR1T4bcZX4VfgadVSx7oK/img.png?width=800&amp;amp;height=547&amp;amp;face=0_0_800_547)&quot;&gt;&lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;Different types of Convolutions (Grouped convolution, depthwise convolution, pointwise convolution, depthwise separable convolut&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;오늘 정리해 볼 것들은 앞으로 정리할 논문들을 위해 미리 알아두면 좋은 convolution입니다. 각 convolution들에 대한 간단한 특징과 param수, 연산량 등에대해서 알아봅시다 ㅎㅎ 들어가기에 앞서 몇��&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;hichoe95.tistory.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Research</category>
      <category>CV</category>
      <category>GroupConv</category>
      <category>PointwiseGroupConv</category>
      <category>ShuffleNet</category>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/59</guid>
      <comments>https://yunmorning.tistory.com/59#entry59comment</comments>
      <pubDate>Sun, 14 Jun 2020 00:20:40 +0900</pubDate>
    </item>
    <item>
      <title>[CV Study] MobileNet: Efficient Convolutional Neural Networks for Mobile Vision Applications</title>
      <link>https://yunmorning.tistory.com/58</link>
      <description>&lt;p&gt;MobileNet(MobileNet V1)은 모바일 기기에서 서빙 가능한 가벼운 모델을 만들겠다는 동기로 진행된 연구이다.&lt;/p&gt;
&lt;p&gt;MobileNet의 핵심은 연산량과 파라미터 수를 줄이는 역할을 하는 depthwise separable convolution이다. 사실 이것만 이해해도 MobileNet을 이해했다고 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Depthwise Separable Convolution&lt;/h2&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2FdOC/btqEOFqwSBQ/fFgPmOlDf6pA7u2kEQs7KK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2FdOC/btqEOFqwSBQ/fFgPmOlDf6pA7u2kEQs7KK/img.png&quot; data-alt=&quot;Depthwise Separable Convolution은 depthwise convolution과 그에 따르는 pointwise convolution으로 이루어져 있다[1].&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2FdOC/btqEOFqwSBQ/fFgPmOlDf6pA7u2kEQs7KK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2FdOC%2FbtqEOFqwSBQ%2FfFgPmOlDf6pA7u2kEQs7KK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Depthwise Separable Convolution은 depthwise convolution과 그에 따르는 pointwise convolution으로 이루어져 있다[1].&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 그림이 depthwise separable convolution이며, depthwise convolution과 pointwise convolution이 연속적으로 이어진 형태이다. 이러한 형태의 convolution이 어떤 이점이 있는 지를 살펴보기 위해 일반적인 convolution, depthwise convolution, pointwise convolution 각각을 살펴볼 필요가 있다[2].&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;1. 일반적인 Convolution 연산&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCirpD/btqEP3XDrkB/HQKIjtG2sbd4PuOGKZjuoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCirpD/btqEP3XDrkB/HQKIjtG2sbd4PuOGKZjuoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCirpD/btqEP3XDrkB/HQKIjtG2sbd4PuOGKZjuoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCirpD%2FbtqEP3XDrkB%2FHQKIjtG2sbd4PuOGKZjuoK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 그림은 일반적인 convolution 연산의 모습이다. 필터 크기는 $K \times K$이고 이미지의 높이 너비는 $F$, 입력 채널 수는 $N$, 출력 채널 수(= 필터 수)는 $M$이다. 이 경우 총 연산량은 $F^2 * K^2 * N * M$이다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;2. Depthwise convolution (공간 방향의 convolution)&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IxdZz/btqEPlkmONl/kOH6gwLQIClVZcOa0ZTLak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IxdZz/btqEPlkmONl/kOH6gwLQIClVZcOa0ZTLak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IxdZz/btqEPlkmONl/kOH6gwLQIClVZcOa0ZTLak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIxdZz%2FbtqEPlkmONl%2FkOH6gwLQIClVZcOa0ZTLak%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;Depthwise convolution은 입력 feature map의 각 채널마다 각기 다른 커널을 사용한다. 채널마다 다른 필터가 적용되기 때문에 출력 채널의 수는 입력 채널의 수와 동일하다. (&lt;span&gt;∵&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; 필터 수 = 출력 채널 수)&lt;/p&gt;
&lt;p&gt;총 연산량을 계산해보면 $F^2 * K^2 * N$이다. (일반적인 convolution보다 연산량이 적어지는 이유는 채널 axis로 연산이 이루어지지 않기 때문이다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;3. Pointwise convolution (= 1x1 convolution) (&lt;i&gt;&lt;b&gt;채널 방향의 convolution)&lt;/b&gt;&lt;/i&gt;&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cFofz5/btqEOaqLYHd/ZbcqsUKUjeM6AhUl8m9xtK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cFofz5/btqEOaqLYHd/ZbcqsUKUjeM6AhUl8m9xtK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cFofz5/btqEOaqLYHd/ZbcqsUKUjeM6AhUl8m9xtK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcFofz5%2FbtqEOaqLYHd%2FZbcqsUKUjeM6AhUl8m9xtK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;1x1 conv는 &lt;a href=&quot;https://yunmorning.tistory.com/57&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GoogLeNet 포스팅&lt;/a&gt;에서 다루었듯이 연산량을 줄이는데 효과적이며, 필터 수를 줄여서 차원 축소를 할 수 있다고 하였다. pointwise convolution에서는 depthwise convolution과 반대로 채널 방향으로 연산을 수행하고, 공간 방향으로는 연산을 하지 않는다. 총 연산량을 계산하면 $F^2 * 1^2 * N * M$이 나온다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;4. Depthwise Separable Convolution (채널 방향 + 공간 방향)&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKQkYA/btqEOO8Em6e/jBYtgRJsgEKGQJ024btSj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKQkYA/btqEOO8Em6e/jBYtgRJsgEKGQJ024btSj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKQkYA/btqEOO8Em6e/jBYtgRJsgEKGQJ024btSj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKQkYA%2FbtqEOO8Em6e%2FjBYtgRJsgEKGQJ024btSj0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;500&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;일반적인 convolution에서는 채널 방향과 공간 방향의 convolution을 동시에 수행한다. 반면, depthwise separable convolution은 공간 방향의 depthwise convolution과 채널 방향의 pointwise convolution을 따로 수행하여 합치는 방식이다. 아래 표를 보면 각 op의 연산량이 정리되어 있는데 기본 conv와 depthwise separable conv의 연산량 비율은 $K^2 * M : K^2 + M$이다. 만약 커널 사이즈 $K =2$, 입력 채널 수 $M = 64$로 잡는다면, 무려 18배의 연산량 차이가 난다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;기본적인 Convolution&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$F^2 * K^2 * N * M$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Depthwise Convolution&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$F^2 * K^2 * N$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Pointwise Convolution&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$F^2 * 1^2 * N * M$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;Depthwise Separable Convolution&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;$(F^2 * K^2 * N) + (F^2 * 1^2 * N * M)$&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;MobileNet에서는 아래 그림의 오른쪽과 같이 (3x3 Depthwise Conv + BN + ReLU) + (1x1 Conv + BN + ReLU) 형태로 depthwise separable convolution layer를 구성하였다. (왼쪽은 일반적인 Conv + BN + ReLU 조합의 모습이다.)&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;400&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/plNPN/btqEPeTj5IH/IRYTQxNnqRYZjusa4Jwbz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/plNPN/btqEPeTj5IH/IRYTQxNnqRYZjusa4Jwbz0/img.png&quot; data-alt=&quot;좌측: 일반적인 conv layer / 우측: depthwise separable conv layer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/plNPN/btqEPeTj5IH/IRYTQxNnqRYZjusa4Jwbz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FplNPN%2FbtqEPeTj5IH%2FIRYTQxNnqRYZjusa4Jwbz0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;400&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;좌측: 일반적인 conv layer / 우측: depthwise separable conv layer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;MobileNet 구조&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKNtRB/btqEPfLriFr/kQRIkOMPeFnS9J6RHy4YEk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKNtRB/btqEPfLriFr/kQRIkOMPeFnS9J6RHy4YEk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKNtRB/btqEPfLriFr/kQRIkOMPeFnS9J6RHy4YEk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKNtRB%2FbtqEPfLriFr%2FkQRIkOMPeFnS9J6RHy4YEk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 표는 MobileNet의 전체 아키텍쳐를 보여준다. &quot;Conv dw&quot;은 depthwise separable convolution을 의미하며, &quot;s1&quot;은 stride = 1, &quot;s2&quot;는 stride = 2를 의미한다. 위의 구조에서 눈에 띄는 점은 pooling layer를 따로 사용하지 않다는 점인데, pooling 대신에 convolution의 stride를 2로 잡아서 차원 축소 및 정규화의 효과를 낸다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style7&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;p&gt;[1] &lt;a href=&quot;https://mc.ai/review-xception-with-depthwise-separable-convolution-better-than-inception-v3-image/&quot;&gt;https://mc.ai/review-xception-with-depthwise-separable-convolution-better-than-inception-v3-image/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1592042117577&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Review: Xception, With Depthwise Separable Convolution, Better Than Inception-v3 (Image&amp;hellip;&quot; data-og-description=&quot;In this story, Xception [1] by Google, stands for Extreme version of Inception, is reviewed. With a modified depthwise separable convolution, it is even better than Inception-v3 [2] (also by Google&amp;hellip;&quot; data-og-host=&quot;mc.ai&quot; data-og-source-url=&quot;https://mc.ai/review-xception-with-depthwise-separable-convolution-better-than-inception-v3-image/&quot; data-og-url=&quot;https://mc.ai/review-xception-with-depthwise-separable-convolution-better-than-inception-v3-image/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/sxH6M/hyGo14iv1n/aPuFLzFpnx86CycikGTc50/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/da1gNj/hyGoNSw1NB/BkHGBhXgqGo2kvx5SyFJqk/img.png?width=270&amp;amp;height=270&amp;amp;face=0_0_270_270&quot;&gt;&lt;a href=&quot;https://mc.ai/review-xception-with-depthwise-separable-convolution-better-than-inception-v3-image/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mc.ai/review-xception-with-depthwise-separable-convolution-better-than-inception-v3-image/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/sxH6M/hyGo14iv1n/aPuFLzFpnx86CycikGTc50/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/da1gNj/hyGoNSw1NB/BkHGBhXgqGo2kvx5SyFJqk/img.png?width=270&amp;amp;height=270&amp;amp;face=0_0_270_270');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Review: Xception, With Depthwise Separable Convolution, Better Than Inception-v3 (Image&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;In this story, Xception [1] by Google, stands for Extreme version of Inception, is reviewed. With a modified depthwise separable convolution, it is even better than Inception-v3 [2] (also by Google&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;mc.ai&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;[2] &lt;a href=&quot;https://www.slideshare.net/ssuser06e0c5/convolution-77257148&quot;&gt;https://www.slideshare.net/ssuser06e0c5/convolution-77257148&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1592042124031&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-og-type=&quot;slideshare:presentation&quot; data-og-title=&quot;Convolution 종류 설명&quot; data-og-description=&quot;Xception, MobileNets등에서 활용되는 pointwise(1x1)convolution, depthwise convolution에 대한 설명&quot; data-og-host=&quot;www.slideshare.net&quot; data-og-source-url=&quot;https://www.slideshare.net/ssuser06e0c5/convolution-77257148&quot; data-og-url=&quot;https://www.slideshare.net/ssuser06e0c5/convolution-77257148&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/kkOm4/hyGoQVZ1cA/OlH98jIerSMk8duyB1bk21/img.jpg?width=768&amp;amp;height=576&amp;amp;face=0_0_768_576,https://scrap.kakaocdn.net/dn/1hLFI/hyGoNZia8A/D7MxBBRTs9cFmzKp0FN7Bk/img.jpg?width=638&amp;amp;height=479&amp;amp;face=0_0_638_479&quot;&gt;&lt;a href=&quot;https://www.slideshare.net/ssuser06e0c5/convolution-77257148&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.slideshare.net/ssuser06e0c5/convolution-77257148&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/kkOm4/hyGoQVZ1cA/OlH98jIerSMk8duyB1bk21/img.jpg?width=768&amp;amp;height=576&amp;amp;face=0_0_768_576,https://scrap.kakaocdn.net/dn/1hLFI/hyGoNZia8A/D7MxBBRTs9cFmzKp0FN7Bk/img.jpg?width=638&amp;amp;height=479&amp;amp;face=0_0_638_479');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot;&gt;Convolution 종류 설명&lt;/p&gt;
&lt;p class=&quot;og-desc&quot;&gt;Xception, MobileNets등에서 활용되는 pointwise(1x1)convolution, depthwise convolution에 대한 설명&lt;/p&gt;
&lt;p class=&quot;og-host&quot;&gt;www.slideshare.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Research</category>
      <category>CV</category>
      <category>Depthwise_Separable_Convolution</category>
      <category>MobileNet</category>
      <category>MobileNetV1</category>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/58</guid>
      <comments>https://yunmorning.tistory.com/58#entry58comment</comments>
      <pubDate>Sat, 13 Jun 2020 21:27:43 +0900</pubDate>
    </item>
    <item>
      <title>[CV Study] GoogLeNet: Going Deeper With Convolutions</title>
      <link>https://yunmorning.tistory.com/57</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Inception이라는 이름은 &quot;Network In Network&quot;에서 영감을 받아서 지어진 이름이다.&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;딥러닝 모델의 성능을 높이는 방법 중 하나는 모델 depth와 width를 늘리는 것이다. 하지만 이는 곧 더 많은 파라미터, 더 큰 용량, 더 많은 연산량, 오버피팅 가능성으로 이어진다는 문제가 있다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;이 문제를 해결 하기 위한 방법 중 하나는 fully connected layer를 sparsely connected layer 구조로 바꾸는 것인데, dropout을 통해 정규화(regularization) 효과를 거두는 경우가 이에 해당한다. 하지만, CPU/GPU 특성상 non-uniform sparse data에 대해서는 효율적인 커널 연산이 어렵다는 문제가 있다. (연산량을 100배 줄이더라도 cache miss, memory copy 오버헤드 등으로 인해 성능이 나아지지 않을 수 있다.)&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;이러한 이유로 Inception에서는 sparse matrix operation 대신 sparse connectivity를 통해 정규화 및 파라미터 감소 효과를 거두는 방안을 택하였고, 행렬 연산은 densely 수행하도록 하였다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;NIN (Network In Network)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/dbciZH/btqEOEkJBif/oABKVcoVe2KTK9aSowXKe1/img.png&quot; width=&quot;800.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbciZH/btqEOEkJBif/oABKVcoVe2KTK9aSowXKe1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbciZH/btqEOEkJBif/oABKVcoVe2KTK9aSowXKe1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbciZH/btqEOEkJBif/oABKVcoVe2KTK9aSowXKe1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbciZH%2FbtqEOEkJBif%2FoABKVcoVe2KTK9aSowXKe1%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/dbciZH/btqEOEkJBif/oABKVcoVe2KTK9aSowXKe1/img.png&quot; width=&quot;800.0&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;GoogLeNet에서 사용하는 inception 모듈은 NIN에서 소개한 MLPConv의 개념에 기반을 두고 있기 때문에 이를 먼저 알아야 한다. Convolution 연산은 데이터의 linear한 특징을 추출하는 데에는 유용하지만 non-linear한 특징을 추출하려면 conv layer 이후에 오는 activation function(ReLU...)의 도움을 받아야 한다. 이로 인해 의미있는 특징을 학습하기 위해 네트워크를 더 깊게 만들어야하는 문제가 생기는데, 이는 곧 파라미터 수의 증가, 연산량의 증가, 오버피팅과 같은 문제로 이어진다. 따라서 conv layer 자체가 비선형 연산을 하도록 만들어주면 매우 효율적일 것이다. 위의 그림에서 (a)는 filter가 입력 feature map을 훑으면서 새로운 feature map을 뽑아내는 일반적인 conv layer의 동작을 보여준다. 반면, (b)는 filter를 가지고 합성곱 연산을 하는 것이 아니라 grid에 걸린 원소들에 대해서 MLP 연산을 수행한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;span style=&quot;color: rgb(102, 102, 102);&quot;&gt;&quot;The cross channel parametric pooling layer is also equivalent to a convolution with 1x1 convolution kernel.&quot;&lt;/span&gt;&lt;/blockquote&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;MLPConv layer에서 주목할 부분은 layer 한 개로 이루어진 &lt;b&gt;&lt;u&gt;MLP를 1x1 conv로 치환이 가능하다&lt;/u&gt;&lt;/b&gt;는 점이다. (이 때 MLP 뉴런 개수는 1x1 conv의 필터 개수와 동일하다.) 즉, 일반적인 linear conv layer 뒤에 1x1 conv를 붙이면 MLPConv와 같은 기능을 하게 된다. 1x1 conv는 그 자체로 &lt;span style=&quot;background-color: rgb(246, 225, 153);&quot;&gt;연산량이 적고&lt;/span&gt;(3x3 conv보다 9배 적음), 필터 수(=출력 채널 수)를 적게 하면 &lt;span style=&quot;background-color: rgb(246, 225, 153);&quot;&gt;차원 축소의 효과&lt;/span&gt;도 거둘 수 있기 때문에 매우 효율적이다. 또한 VGG에서 밝힌 바와 같이 ReLU를 통한 &lt;span style=&quot;background-color: rgb(246, 225, 153);&quot;&gt;비선형성 증대&lt;/span&gt; 효과도 거둘 수 있어 feature 추출에도 효과적이다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/bUrLkv/btqEP3JWO9Y/zzrixKW5k006OfUtV4jyHk/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUrLkv/btqEP3JWO9Y/zzrixKW5k006OfUtV4jyHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUrLkv/btqEP3JWO9Y/zzrixKW5k006OfUtV4jyHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUrLkv/btqEP3JWO9Y/zzrixKW5k006OfUtV4jyHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUrLkv%2FbtqEP3JWO9Y%2FzzrixKW5k006OfUtV4jyHk%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/bUrLkv/btqEP3JWO9Y/zzrixKW5k006OfUtV4jyHk/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;위 그림은 NIN의 전체 네트워크 구조인데, 마지막에 FC layer 대신 GAP(Global Average Pooling) layer가 들어가 있다.&amp;nbsp;FC layer는 dropout과 같은 정규화 연산을 따로 하지 않는 한 오버피팅의 위험이 큰 반면, GAP는 그 자체로 정규화 효과를 가지고 있기 때문에 효율적이다. 앞에서 MLPConv layer들을 통해 특징 추출이 잘 이루어졌기 때문에 이와 같이 FC layer를 제거할 수 있다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/blgNzh/btqENYxhG3f/QlIzjJZ9lMlx29o5VOa4P0/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blgNzh/btqENYxhG3f/QlIzjJZ9lMlx29o5VOa4P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blgNzh/btqENYxhG3f/QlIzjJZ9lMlx29o5VOa4P0/img.png&quot; data-alt=&quot;AlexNet의 op별 latnecy: FC layer에서 매우 많은 시간이 소요됨을 알 수 있다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blgNzh/btqENYxhG3f/QlIzjJZ9lMlx29o5VOa4P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblgNzh%2FbtqENYxhG3f%2FQlIzjJZ9lMlx29o5VOa4P0%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/blgNzh/btqENYxhG3f/QlIzjJZ9lMlx29o5VOa4P0/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;AlexNet의 op별 latnecy: FC layer에서 매우 많은 시간이 소요됨을 알 수 있다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;Inception Module&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/1rKOA/btqEPVrQ1z4/1zlXZrZ2ss1YXa5jAF4CT0/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1rKOA/btqEPVrQ1z4/1zlXZrZ2ss1YXa5jAF4CT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1rKOA/btqEPVrQ1z4/1zlXZrZ2ss1YXa5jAF4CT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1rKOA/btqEPVrQ1z4/1zlXZrZ2ss1YXa5jAF4CT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1rKOA%2FbtqEPVrQ1z4%2F1zlXZrZ2ss1YXa5jAF4CT0%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/1rKOA/btqEPVrQ1z4/1zlXZrZ2ss1YXa5jAF4CT0/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;(a)에서는 1x1, 3x3, 5x5 convolution과 3x3 max pooling을 따로 수행하고, 이를 concat하고 있다. 각각 다른 크기의 필터를 사용하는 이유는 보다 다양한 feature를 뽑아내기 위함이고, max pooling을 하는 건 정규화 목적 때문이다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&lt;span style=&quot;color: rgb(51, 51, 51);&quot;&gt;이 때 5x5 conv는 연산량이 매우 크다는 문제가 있는데, 이를 해결하기 위해 NIN에서 살펴본 1x1 conv가 활용된다. 1x1 conv는 (1) 비선형성 증대, (2) 연산량, 파라미터 감소, (3) 차원축소의 효과가 있기 때문에 연산량이 큰 3x3 conv와 5x5 conv 이전에 1x1 conv를 두어 채널 수를 줄인다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;앞서 inception module의 디자인 철학은 sparsity와 density를 동시에 잡는 것이라고 언급하였는데, 실제로 inception module을 구조를 보면 이러한 아이디어가 반영된 모습을 볼 수 있다. 우선, 1x1 conv, 3x3 conv, 5x5 conv를 각각 병렬적인 구조로 연산을 수행하여 connection에서의 sparsity를 확보하여 정규화의 효과를 얻을 수 있다. 또한 connection 자체는 sparse하지만 각각의 op은 dense matrix operation이기 때문에 GPU utilization 문제가 적다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;GoogLeNet&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;다음은 최종적인 GoogLeNet의 구조이다. 기본적으로 9개의 inception module들로 구성되어 있고, 네트워크의 초반부에는 inception module 대신 일반적인 conv + (bn) + relu 연산을 수행한다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;또한 중간과 끝에 auxiliary classifier로 FC + softmax 연산을 수행하는데, 이는 gradient signal이 보다 잘 전파될 수 있도록 하기 위함이다. ResNet에서 shortcut connection을 두는 것과 비슷한 원리라고 보면 된다. Inference 단계에서는 gradient vanishing이 고려사항이 아니기 때문에 auxiliary classifier 부분은 제거한다.&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/bfNzTF/btqEPVyFuKO/kLHYD9VLthPVK8lSMm6No1/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfNzTF/btqEPVyFuKO/kLHYD9VLthPVK8lSMm6No1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfNzTF/btqEPVyFuKO/kLHYD9VLthPVK8lSMm6No1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfNzTF/btqEPVyFuKO/kLHYD9VLthPVK8lSMm6No1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfNzTF%2FbtqEPVyFuKO%2FkLHYD9VLthPVK8lSMm6No1%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/bfNzTF/btqEPVyFuKO/kLHYD9VLthPVK8lSMm6No1/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;h2 data-ke-size=&quot;size26&quot; style=&quot;text-align: left;&quot;&gt;Torchvision GoogLeNet (Inference Dataflow Graph)&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/A0k1s/btqEOO1IHFG/LpcSuw6vX8jHvGB8tnkjxk/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/A0k1s/btqEOO1IHFG/LpcSuw6vX8jHvGB8tnkjxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/A0k1s/btqEOO1IHFG/LpcSuw6vX8jHvGB8tnkjxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/A0k1s/btqEOO1IHFG/LpcSuw6vX8jHvGB8tnkjxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FA0k1s%2FbtqEOO1IHFG%2FLpcSuw6vX8jHvGB8tnkjxk%2Fimg.png&quot; data-image-src=&quot;https://k.kakaocdn.net/dn/A0k1s/btqEOO1IHFG/LpcSuw6vX8jHvGB8tnkjxk/img.png&quot; data-origin-width=&quot;0.0&quot; data-origin-height=&quot;0.0&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Research</category>
      <category>CV</category>
      <category>GoogLeNet</category>
      <category>Inception</category>
      <category>NIN</category>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/57</guid>
      <comments>https://yunmorning.tistory.com/57#entry57comment</comments>
      <pubDate>Sat, 13 Jun 2020 03:12:58 +0900</pubDate>
    </item>
    <item>
      <title>[CV Study] SqueezeNet: AlexNet-Level Accuracy With 50x Fewer Parameters and &amp;lt;0.5MB Model Size</title>
      <link>https://yunmorning.tistory.com/56</link>
      <description>&lt;p&gt;Accuracy가 같다면 더 작을 모델일수록 (1) 서버 간의 커뮤니케이션을 줄일 수 있고, (2) 더 작은 대역폭(bandwidth)로도 서빙이 가능하고, (3) 한정된 메모리의 가속기에서 사용에 용이하다. SqueezeNet은 ImageNet에서 AlexNet과 동일한 수준의 accuracy를 유지하면서도 50배 더 적은 파라미터를 사용하고, compression 기법을 적용했을 때 AlexNet보다 510배 작은(&amp;lt;0.5MB) 용량을 가진다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;용어&lt;/h4&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;1. CNN Microarchitecture&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;논문에서는 ResNet의 bottleneck block, GoogLeNet의 inception module과 같이 몇몇 op들이 모여서 이루어진 네트워크 building block을 microarchitecture라고 부른다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;2. CNN Macroarchitecture&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Microarchitecture가 개별 레이어나 모듈을 지칭하는 반면, macroarchitecture는 전체 E2E CNN 모델 속에서 발견되는 구조를 의미한다. VGG에서 언급한 깊은 network의 중요성이라든지, ResNet에서 gradient vanishing을 막기 위한 shorcut connection이 그 예시라고 할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Design Strategies&lt;/h2&gt;
&lt;p&gt;SqueezeNet의 목표는 accuracy를 보존하면서 최대한 적은 파라미터를 사용하는 것이다. 이를 위해서 다음 3가지 디자인 방법론을 적용하였다. [전략 1], [전략 2]는 모델의 파라미터 수를 감소시키기 위한 방법에 해당하고 [전략 3]은 파라미터 수를 감소시키되 accuracy를 고려한 방법에 해당한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;[전략 1]. 3x3 필터를 1x1로 교체&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;3x3 conv 대신에 1x1 conv를 사용하면 연산량이 9배 줄어들고, 9배 적은 파라미터를 갖게 된다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;[전략 2]. 3x3 convolution의 입력 채널 수를 감소&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;3x3 conv가 갖는 파라미터 수는 $(\text{input 채널 수}) \times (\text{필터 개수}) \times (3 * 3)$이다. (참고: 필터 개수 = output 채널 수) 따라서 input 채널 수를 줄이면 파라미터 수가 줄게 되는데, 뒤에서 살펴볼 squeeze layer를 통해서 3x3 conv로 들어가는 입력의 채널을 줄일 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;i&gt;&lt;b&gt;[전략 3]. 큰 activation map을 유지하기 위하여 downsampling은 네트워크의 후반부에서 수행&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Activation map의 크기(HW)는 입력 데이터의 크기(e.g. 256 x 256 이미지)와 downsampling 비율에 의해서 결정된다. 일반적으로 CNN에서 downsampling은 convolution의 stride를 1보다 크게 잡거나, pooling을 통해서 이루어진다.&lt;/p&gt;
&lt;p&gt;그런데 만약 downsampling이 네트워크의 초반부에 이루어진다면 그 여파로 네트워크 전체에서 작은 activation map을 갖게 되고, 모델의 성능을 저하시킬 수 있다. 따라서 accuracy와 더 작은 파라미터 두 가지 목적을 모두 달성하기 위해 downsampling은 오직 네트워크의 후반부에서만 수행한다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Microarchitecture: The Fire Module&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6Mp7j/btqEODlyF4l/cFo9uhx80VIKWDtUWVlpOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6Mp7j/btqEODlyF4l/cFo9uhx80VIKWDtUWVlpOK/img.png&quot; data-alt=&quot;fire module&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6Mp7j/btqEODlyF4l/cFo9uhx80VIKWDtUWVlpOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6Mp7j%2FbtqEODlyF4l%2FcFo9uhx80VIKWDtUWVlpOK%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;600&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;fire module&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위의 그림은 SqueezeNet의 주요 building block(microarchitecture)인 fire module이다. 이 fire module에는 앞서 살펴본 3가지 디자인 전략중 [전략 1]과 [전략 2]의 아이디어가 담겨있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;[전략 1]: Squeeze layer와 expand layer에서 모두 1x1 conv가 사용되고 있고, 이는 파라미터 수를 줄이기 위함이다.&lt;/li&gt;
&lt;li&gt;[전략 2]: 하이퍼파라미터 $s_{1\times1}$은 squeeze layer에 있는 1x1 conv의 필터 수이고, $e_{1\times1}$은 expand layer의 1x1 conv 필터 수, $e_{3\times3}$은 expand layer의 3x3 conv 필터 수를 의미한다. Fire module에서는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;u&gt;$s_{1\times1} &amp;lt; e_{1\times1} + e_{3\times3}$&lt;/u&gt;&lt;/span&gt;을 만족하도록 하이퍼파라미터를 설정한다. (filter 수) = (output channel 수)이기 때문에 squeeze layer의 필터 수를 작게 잡아야 expand layer의 3x3 conv로 들어가는 input channel 수를 작게 만들어 모델 파라미터를 줄일 수 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Macroarchitecuture: SqueezeNet&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;850&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v2FA6/btqENAXjqH3/VUwgv4eRrUDRaC7Dqp2Zfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v2FA6/btqENAXjqH3/VUwgv4eRrUDRaC7Dqp2Zfk/img.png&quot; data-alt=&quot;SqueezeNet / SqueezNet + simple bypass / SqueezeNet + complex bypass&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v2FA6/btqENAXjqH3/VUwgv4eRrUDRaC7Dqp2Zfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv2FA6%2FbtqENAXjqH3%2FVUwgv4eRrUDRaC7Dqp2Zfk%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;850&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SqueezeNet / SqueezNet + simple bypass / SqueezeNet + complex bypass&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;위 그림은 SqueezeNet의 전체 구조(macroarchitecture)를 보여준다. 보다시피 모델은 conv 1개 $\to$ fire module 8개 $\to$ conv 1개로 구성되어 있고, 모델의 후반부로 갈수록 fire module 당 필터 수를 늘리고 있다. 또한 conv1, fire4, fire8, conv10 뒤에선 2x2 max-pooling을 수행하여 downsampling을 한다. 이렇게 간헐적으로 pooling을 하는 것은 앞서 살펴본 [전략 3]을 염두에 둔 것이라 생각할 수 있다.&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;PyTorch Model Graph&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1. Fire Module&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;450&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcuPeg/btqENAQCGNw/rInvV0gKqNwYzmxnPgNd80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcuPeg/btqENAQCGNw/rInvV0gKqNwYzmxnPgNd80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcuPeg/btqENAQCGNw/rInvV0gKqNwYzmxnPgNd80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcuPeg%2FbtqENAQCGNw%2FrInvV0gKqNwYzmxnPgNd80%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;450&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2. 모델 전체&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPk7FS/btqEPl5iGQI/zPnALJg6UyUdDk8lkCOcT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPk7FS/btqEPl5iGQI/zPnALJg6UyUdDk8lkCOcT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPk7FS/btqEPl5iGQI/zPnALJg6UyUdDk8lkCOcT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPk7FS%2FbtqEPl5iGQI%2FzPnALJg6UyUdDk8lkCOcT0%2Fimg.png&quot; data-origin-width=&quot;0&quot; data-origin-height=&quot;0&quot; width=&quot;300&quot; data-ke-mobilestyle=&quot;widthContent&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>Research</category>
      <category>CV</category>
      <category>FireModule</category>
      <category>SqueezeNet</category>
      <author>yunmorning</author>
      <guid isPermaLink="true">https://yunmorning.tistory.com/56</guid>
      <comments>https://yunmorning.tistory.com/56#entry56comment</comments>
      <pubDate>Thu, 11 Jun 2020 15:56:09 +0900</pubDate>
    </item>
  </channel>
</rss>