<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>da2so</title>
    <link>https://da2so.tistory.com/</link>
    <description>Explainable AI (XAI), Model Compression, Image and Video Encoding and NAS</description>
    <language>ko</language>
    <pubDate>Wed, 1 Jul 2026 18:11:42 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Sin-Han Kang</managingEditor>
    <image>
      <title>da2so</title>
      <url>https://tistory1.daumcdn.net/tistory/5290078/attach/25a1a130ea3b409aa82e0d58e183aeaf</url>
      <link>https://da2so.tistory.com</link>
    </image>
    <item>
      <title>[파리] 헬스장 Neoness Paris 리뷰</title>
      <link>https://da2so.tistory.com/85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;파리 여행을 &lt;b&gt;2025년 4월 29일부터~5월5일까지&lt;/b&gt; 다녀왔습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호텔은 오페라 가르니에(Palais Garnier) 근처였고 파리의 헬스장을 가려고 와이프 허락받고 갓다왔습니다.ㅋㅋ&amp;nbsp;&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; 그럼 이제 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Neoness Paris 헬스장 리뷰&lt;/b&gt;&lt;/span&gt;를 해봅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Neoness Paris는 체인점이기 때문에 파리 곳곳에 있을겁니다. 저는 그 중에 &lt;a href=&quot;https://www.google.com/maps/place/Neoness+Paris+9+Saint-Lazare/@48.873966,2.330288,15.71z/data=!4m9!1m2!2m1!1sgym!3m5!1s0x47e66e4bd7a81ad7:0x7e5c1c92417d948a!8m2!3d48.8796968!4d2.3294146!16s%2Fg%2F11bc745zrz?hl=ko-DE&amp;amp;entry=ttu&amp;amp;g_ep=EgoyMDI1MDUwNy4wIKXMDSoJLDEwMjExNDU1SAFQAw%3D%3D&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Neoness Paris 9 Saint-Lazare&lt;/a&gt; 헬스장을 다녀왔습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div id=&quot;code_1747035237536&quot; data-ke-type=&quot;html&quot; data-source=&quot;&amp;lt;iframe src=&amp;quot;https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d6408.703341322663!2d2.3302879676198627!3d48.87396598019768!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47e66e4bd7a81ad7%3A0x7e5c1c92417d948a!2sNeoness%20Paris%209%20Saint-Lazare!5e0!3m2!1sko!2skr!4v1747035227502!5m2!1sko!2skr&amp;quot; width=&amp;quot;600&amp;quot; height=&amp;quot;450&amp;quot; style=&amp;quot;border:0;&amp;quot; allowfullscreen=&amp;quot;&amp;quot; loading=&amp;quot;lazy&amp;quot; referrerpolicy=&amp;quot;no-referrer-when-downgrade&amp;quot;&amp;gt;&amp;lt;/iframe&amp;gt;&quot;&gt;&lt;iframe src=&quot;https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d6408.703341322663!2d2.3302879676198627!3d48.87396598019768!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47e66e4bd7a81ad7%3A0x7e5c1c92417d948a!2sNeoness%20Paris%209%20Saint-Lazare!5e0!3m2!1sko!2skr!4v1747035227502!5m2!1sko!2skr&quot; width=&quot;600&quot; height=&quot;450&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. 가격&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Day pass 가격은 당시에 &lt;b&gt;15유로&lt;/b&gt;였습니다. (카드, 현금 모두 가능)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;(여담으로&amp;nbsp; &lt;span style=&quot;background-color: #ffffff; color: #1f1f1f; text-align: center;&quot;&gt;Fitness Park &lt;/span&gt;헬스장에서는 Day pass가 20유로였고 타월이 8유로 달라그랬는데 타월 없으면 못드간다 해서 안가긴했습니다...)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. 시설&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시설은 총 2층으로 구분되어 있었습니다. &lt;b&gt;1층은 웨이트 + 스트레칭 존, 2층은 유산소 + 복싱 존&lt;/b&gt;&amp;nbsp;이었습니다.&amp;nbsp;&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;1층부터 설명드리면 &lt;b&gt;1층은 2개의 파트로 나눠져&lt;/b&gt;있습니다. 첫 번째 파트는 입구(1.5층)에서 내려가면 바로 보이는 머신 존 + 스트레칭 존입니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbmjQv/btsNU8hpv6Z/NrmUWllGKA9FsqTIJFVtR1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbmjQv/btsNU8hpv6Z/NrmUWllGKA9FsqTIJFVtR1/img.jpg&quot; data-alt=&quot;스트레칭 존&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbmjQv/btsNU8hpv6Z/NrmUWllGKA9FsqTIJFVtR1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbmjQv%2FbtsNU8hpv6Z%2FNrmUWllGKA9FsqTIJFVtR1%2Fimg.jpg&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; loading=&quot;lazy&quot; width=&quot;466&quot; height=&quot;621&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;스트레칭 존&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lM2y6/btsNUQnHaWE/NlWDlvw8jTDI5VkUcQSHlk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lM2y6/btsNUQnHaWE/NlWDlvw8jTDI5VkUcQSHlk/img.jpg&quot; data-alt=&quot;머신 존&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lM2y6/btsNUQnHaWE/NlWDlvw8jTDI5VkUcQSHlk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlM2y6%2FbtsNUQnHaWE%2FNlWDlvw8jTDI5VkUcQSHlk%2Fimg.jpg&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; loading=&quot;lazy&quot; width=&quot;466&quot; height=&quot;621&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;머신 존&lt;/figcaption&gt;
&lt;/figure&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;두번 째 파트는 스트레칭존 쪽으로 쭉 드가면 있는데요. 프리웨이트 + 몇몇의 머신이 있었습니다. (아래 사진 뒤쪽에 프리웨이트 더 할수있는 공간이 있습니다.)&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kXzbb/btsNUVWRUZr/KptBbeicSBT0emQAfYh5C1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kXzbb/btsNUVWRUZr/KptBbeicSBT0emQAfYh5C1/img.png&quot; data-alt=&quot;프리웨이트 + 머신 존&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kXzbb/btsNUVWRUZr/KptBbeicSBT0emQAfYh5C1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkXzbb%2FbtsNUVWRUZr%2FKptBbeicSBT0emQAfYh5C1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;499&quot; height=&quot;665&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;프리웨이트 + 머신 존&lt;/figcaption&gt;
&lt;/figure&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;그리고 이제 2층으로 가보면 이제 유산소 존이 있습니다. 보통 한국 헬스장에 있는 유산소 운동기구와의 종류는 비슷한데 기능이 좀 다른게 있습니다. 유산소 기구들에서 제공하는 기능이 많아요. 화면에서 (자기 계정 로그인해서) netflix, instagram, youtube, X 등등 다 할수가 있더라고요. 블루투스도 되고 머 여러개 더 많은데 사진이 없네요. 여튼 유산소 기구는 한국보다 훨씬 좋다고 느꼈습니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brqLVb/btsNStOBWRf/NNCHKiiRw3jzGtF9znClB0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brqLVb/btsNStOBWRf/NNCHKiiRw3jzGtF9znClB0/img.jpg&quot; data-alt=&quot;유산소존&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brqLVb/btsNStOBWRf/NNCHKiiRw3jzGtF9znClB0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrqLVb%2FbtsNStOBWRf%2FNNCHKiiRw3jzGtF9znClB0%2Fimg.jpg&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; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;608&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;유산소존&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.  추가 정보&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 전체적으로 헬스장에 냄새 안남&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 바닥은 한국 헬스장보다 더러움&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사람들 착함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 금요일 오후 4~6시에 이용해서 그런지 사람이 적은거 같았음&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>일상</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/85</guid>
      <comments>https://da2so.tistory.com/85#entry85comment</comments>
      <pubDate>Mon, 12 May 2025 19:17:00 +0900</pubDate>
    </item>
    <item>
      <title>VMAF Optimization과 VMAF NEG 이해</title>
      <link>https://da2so.tistory.com/84</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://da2so.tistory.com/57&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;지난 글&lt;/a&gt;에서 VMAF(&lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;Video Multimethod Assessment Fusion&lt;/span&gt;)에 대해 알아보았습니다. 그리고 2020년에 기존 VMAF의 성능을향상시킨 방법들을 오늘 소개합니다. (&lt;a href=&quot;https://netflixtechblog.com/toward-a-better-quality-metric-for-the-video-community-7ed94e752a30&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 글&lt;/a&gt;을 참고하였습니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;b&gt;목차&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Speed Optimization&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;New&amp;nbsp;libvmaf&amp;nbsp;API&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;No Enhancement Gain&amp;rdquo; Mode: VMAF NEG&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Speed Optimization&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 VMAF 을 사용하는 데 있어서 단점은 VMAF score를 구하는데 time cost가 크다는 것이었습니다. Time cost를 줄이기 위해 다음과 같은 방법을 개발해왔습니다.&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;b&gt;Low-level code optimization and vectorization (2016년)&lt;/b&gt;: python + C에서 standalone C++로 변경&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기존 VMAF보다 4배 빨라짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Frame-level multi-threading and frame skipping (2018년)&lt;/b&gt;: 각 frame별로 vmaf를 구할때 multi-threading을 활용하였고 특정 frame을 skip하여 vmaf구하는 방법 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;4k videos를 대상으로 real time으로 VMAF측정 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Floating-point based representation을 fixed-point로 변경 (2020년)&lt;/b&gt;: &lt;span style=&quot;background-color: #ffffff; color: #34343c; text-align: left;&quot;&gt;실수를 표현하는 방법이 단순한 방법인&amp;nbsp;&lt;/span&gt;fixed-point를 사용하기 때문에 속도가 빠름 (대신 precision이 floating-point보다는 떨어짐)&lt;/li&gt;
&lt;li&gt; &lt;b&gt;Vectorization on the fixed-point data piepline (2020년)&lt;/b&gt;: fixed-point data에 대해 vectorization&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 3,4번 항목을 이용하게 된다면 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;VMAF 측정하는 속도가&lt;/span&gt; 평균적으로 2배정도 빨라집니다. 다만, fixed-point로 계산하기 때문에 VMAF score값은 완전히 정확하지는 않지만 소수점 첫째자리까지는 같다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림을 통해 Intel AVX2(Advanced Vector Extension 2), Intel AVX-512에서 2018년 기준의 VMAF구하는 방식보다 몇배 빨라졌는지 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;742&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9KR1l/btsEjGVhebV/d6WWdMe5ZDSAHXZRp4lXX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9KR1l/btsEjGVhebV/d6WWdMe5ZDSAHXZRp4lXX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9KR1l/btsEjGVhebV/d6WWdMe5ZDSAHXZRp4lXX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9KR1l%2FbtsEjGVhebV%2Fd6WWdMe5ZDSAHXZRp4lXX0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;682&quot; height=&quot;422&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;742&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. New&amp;nbsp;libvmaf&amp;nbsp;API&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;FFMPEG의 license가 &lt;/span&gt;Apache 2.0에서 BSD+Patent로 변경되면서 다른 open source project들과 호환이 가능해졌습니다.  그래서 대대적으로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;API의 개조하여 libvmaf v2.0.0을 relesae&lt;/b&gt;&lt;/span&gt;하였습니다. 아래와 같은 API가 새롭게 사용가능해졌습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1096&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XFkXi/btsEjEXImAK/tKU6Nqhp4nkaSF2KKlxXJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XFkXi/btsEjEXImAK/tKU6Nqhp4nkaSF2KKlxXJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XFkXi/btsEjEXImAK/tKU6Nqhp4nkaSF2KKlxXJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXFkXi%2FbtsEjEXImAK%2FtKU6Nqhp4nkaSF2KKlxXJK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;395&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;1096&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 표에 대한 특징은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;API를 손상시키지 않고 확장가능&lt;/li&gt;
&lt;li&gt;새로운 feature extractor를 추가하기 쉬워짐&amp;nbsp;&amp;rarr; 미래의 새로운 VMAF 알고리즘을 쉽게 support가능&lt;/li&gt;
&lt;li&gt;Memory 할당이 유연해지고 frame-level에서 점진적으로 VMAF 계산 가능
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;해당 feature는 encoding loop에서 VMAF를 integrate 가능토록 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. &quot;No Enhancement Gain&amp;rdquo; Mode: VMAF NEG&lt;/b&gt;&lt;/h2&gt;
&lt;p id=&quot;f1f8&quot; style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;VMAF의 고유 특징 중 하나는 전통적인 방법들(e.g. PSNR, SSIM)과 다르게 image enchancement operations(e.g. sharpen)으로부터의 visual gain을 확인가능하다는 것입니다. I&lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;mage enchancement operations&lt;/span&gt;은 사람에 의해 인식되는 주관적인 품질 향상을 목표로 하는 operations을 뜻합니다.)&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1439&quot; data-origin-height=&quot;910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cH9Iwy/btsEfuhV0vs/vFj0nmSOALzLaun65lXD70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cH9Iwy/btsEfuhV0vs/vFj0nmSOALzLaun65lXD70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cH9Iwy/btsEfuhV0vs/vFj0nmSOALzLaun65lXD70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcH9Iwy%2FbtsEfuhV0vs%2FvFj0nmSOALzLaun65lXD70%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;454&quot; data-origin-width=&quot;1439&quot; data-origin-height=&quot;910&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;예시로 위그램에서 (a)는 원본 image를 encoding하고 VMAF score를 구한것이고 (b), (c)는 enhacnement operations preprocessing을 거친 후 encoding을 한 image이며 그에 대해 VMAF값을 구한것입니다.&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt; E&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;nhacnement operations을 통해 visual gain(improvement)가 생겼기 때문에 (A)보다 (B),(C)의 VMAF 값이 더 높게 측정&lt;/b&gt;&lt;/span&gt;되는 것을 확인가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;최근에는 &lt;span style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot;&gt;libaom library에서&lt;/span&gt; tune=vmaf mode라는 option으로 사용가능한데 이것은 quality-optimized AV1 encoding을 수행합니다. 해당 모드는 BD-rate gain을 가져오면 video compression전에 frame-based image sharpening을 수행합니다. 위 그림에서 해당 모드 사용 유무에 따른 결과 차이를 볼 수 있습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;263&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVUzxM/btsEf3duGjw/OVCdw68e5hsHV5rzlEy6a1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVUzxM/btsEf3duGjw/OVCdw68e5hsHV5rzlEy6a1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVUzxM/btsEf3duGjw/OVCdw68e5hsHV5rzlEy6a1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVUzxM%2FbtsEf3duGjw%2FOVCdw68e5hsHV5rzlEy6a1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;693&quot; height=&quot;208&quot; data-origin-width=&quot;876&quot; data-origin-height=&quot;263&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;하지만 codec evaulation을 위해 pre-processing동안의 image enhancement으로부터 생긴 visual gain을 제외한 gain만을 측정할 필요가 종종 있습니다. 위 그림과 같이 encoder는 pre-process과정과 encode과정을 모두 포함하기 때문에 encode 과정에서 발생된&amp;nbsp; 순수(pure) compression gain을 VMAF를 통해서 평가하기 힘듭니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;그래서 Netflix는&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt; image enhancement operations으로부터의 visual gain을 제외한 pure compression gain만을 측정할 수 있는 VMAF NEG 모드를 개발&lt;/b&gt;&lt;/span&gt;하였습니다. (NEG는 no enhancement gain을 의미합니다.)&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;&lt;b&gt;VMAF NEG mode는 어떻게 작동하는 가?&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;div style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Image enchancement로부터 발생된 VMAF gain을 측정가능하며 해당 gain을 기존 측정방식에서 subtract해준다고 합니다. 그 예시로 위 그림 (f)에서 tune=vmaf 모드를 사용해서 발생된 image sharpening의 magnitude를 grayscale map으로 표현하였습니다. 그리고 (a)~(e)까지 VMAF NEG score를 보았을 때 image enhancement의 gain효과가 제거된 score가 측정된 것을 확인가능합니다.&lt;/p&gt;
&lt;p id=&quot;489a&quot; style=&quot;background-color: #ffffff; color: #242424; text-align: start;&quot; data-selectable-paragraph=&quot;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/84</guid>
      <comments>https://da2so.tistory.com/84#entry84comment</comments>
      <pubDate>Thu, 1 Feb 2024 20:04:46 +0900</pubDate>
    </item>
    <item>
      <title>Rate-Perception Optimized Preprocessing for Video Coding 논문 리뷰</title>
      <link>https://da2so.tistory.com/83</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 리뷰할 논문은 &lt;a href=&quot;https://arxiv.org/pdf/2301.10455.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Rate-Perception Optimized Preprocessing for Video Coding&lt;/a&gt;으로 &lt;b&gt;Bilibili &lt;/b&gt;에서 나온 논문입니다. Bilibili는 동영상 플랫폼 회사로 중국의 유튜브로 생각하시면 편합니다.&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;논문 내용을 요약하면 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Video의 각 frame(image)에 대해 preprocessing을 적용하여 image quality는 그대로 유지하되 image size를 최대한 줄여서 최종적으로 video size를 줄이는 것을 목적&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;Preprocessing은 Rate-Perception Optimized Preprocessing (RPP) model을 통해 적용됨&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;380&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bXsVOQ/btsA2vpAyyG/oQCeJyWc9JNVxk9lJpFBx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bXsVOQ/btsA2vpAyyG/oQCeJyWc9JNVxk9lJpFBx0/img.png&quot; data-alt=&quot;RPP 적용 전(a), 후(b)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bXsVOQ/btsA2vpAyyG/oQCeJyWc9JNVxk9lJpFBx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbXsVOQ%2FbtsA2vpAyyG%2FoQCeJyWc9JNVxk9lJpFBx0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;222&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;380&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RPP 적용 전(a), 후(b)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Introduction&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;HD 비디오는 고객 전체 internet traffic 중 80%이상을 차지할 정도로 엄청나게 큰 bandwidth를 사용함&lt;/li&gt;
&lt;li&gt;그래서, traditional codec(e.g. H.264, H.265, H.266, AV1)은 efficient video compression system을 만들려고 노력해 옴
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기존 codec들은 대부분 handcrafted modules (e.g. DCT, intra/inter prediction, block partition)로 이루어져 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;더 효과적인 compression을 위해 codec의 encoder, decoder를 DL model로 대체하는 방식이 최근에 연구됨
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;DL model을 사용하기 때문에 image quality나 encoding size면에서는 기존 codec들보다 성능은 우수하지만 inference cost가 너무 큼&lt;/li&gt;
&lt;li&gt;심지어, decoder side에서 보면 모든 고객의 핸드폰이나 컴퓨터에 해당 model이 deploy되어야 하기 때문에 model이 클 경우 사용 불가함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;그래서, 해당 논문은&amp;nbsp;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Rate-Perception optimized Preprocessor (RPP) model을 사용하여 image frame의 image quality는 그대로 유지하되 encoding size를 최소화시키는 것을 목적&lt;/b&gt;&lt;/span&gt;함
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기존의 codec의 encoder을 사용하기 전에 각 image frame에 preprocessing을 적용하여 새로운 image frame을 생성&lt;/li&gt;
&lt;li&gt;기존 codec의 decoder를 그대로 사용하므로 DL model의 decoder와 다르게 inference cost가 상대적으로 매우 낮음&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Bitrate compression&lt;/b&gt;은 &lt;b&gt;Discrete Cosine Transform (DCT) loss&lt;/b&gt;를 통해 적용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Image quality 보존&lt;/b&gt;은 &lt;b&gt;MS-SSIM loss을 이용한 perceptual loss&lt;/b&gt;, &lt;b&gt;원본 frame과 생성된 frame간의 MSE loss을 적용한 reconstruction loss&lt;/b&gt;로 적용&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b8cRA9/btsASuZEDUT/NkkleLPAncAMypdpEYpYxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b8cRA9/btsASuZEDUT/NkkleLPAncAMypdpEYpYxk/img.png&quot; data-alt=&quot;RPP 모델 process&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b8cRA9/btsASuZEDUT/NkkleLPAncAMypdpEYpYxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb8cRA9%2FbtsASuZEDUT%2FNkkleLPAncAMypdpEYpYxk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;562&quot; height=&quot;179&quot; data-origin-width=&quot;1168&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RPP 모델 process&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Method&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Overview&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RPP model의 목적은 preprocessed input frame을 제공하는 것이며 preprocessing을 통해 bitrate 감소, 동일한 image quality를 유지시킴&lt;/li&gt;
&lt;li&gt;Bitrate를 감소시키기 위해 adaptive DCT Loss 제안
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Spatial redundancy를 감소시키고 high frequency 영역 중 중요 한 부분만 남도록 학습&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;원본 input frame과 동일한 quality를 유지하기 위해서 IQA model인 MS-SSIM을 이용한 perceptual loss제안&amp;nbsp;&lt;/li&gt;
&lt;li&gt;원본 input frame에 대해 high-order degradation을 training input data로 사용함으로써 real world image랑 비슷하게 만들고 모델의 perceptual quality를 향상시키도록 함&lt;/li&gt;
&lt;li&gt;Lightweight CNN model로 RPP model을 구성하여 효율성을 극대화&lt;/li&gt;
&lt;li&gt; Deployment 시에는 input frame \(&amp;nbsp; f_i \)가 RPP model에 의해 preprocessing되어 \( f_o \)가 되고 이는 기존 codec(e.g. H.264, H.265)에 encoding 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJ2VHB/btsCMqayrZE/l3GX5eYcBUzU4VKufTLlJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJ2VHB/btsCMqayrZE/l3GX5eYcBUzU4VKufTLlJ0/img.png&quot; data-alt=&quot;RPP model framework&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJ2VHB/btsCMqayrZE/l3GX5eYcBUzU4VKufTLlJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJ2VHB%2FbtsCMqayrZE%2Fl3GX5eYcBUzU4VKufTLlJ0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;743&quot; height=&quot;331&quot; data-origin-width=&quot;1328&quot; data-origin-height=&quot;592&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RPP model framework&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Adaptive&amp;nbsp;Discrete Cosine Transform Loss&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 codec 대부분은 encoding과정에 DCT(Discrete Cosine Transform)을 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;2D DCT의 basis function은 Eq. (1), 2D DCT는 Eq. (2)로 수식화됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;B^{i,j}_{h,w} = cos \frac{h \phi}{H} ( i+&amp;nbsp; \frac{1}{2}) + sin \frac{w \phi}{H} ( j + \frac{1}{2}) \quad \cdots Eq.(1)&lt;br /&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;\[&lt;br /&gt;F_{h,w} = \sum^{H-1}_{i=0} \sum^{W-1}_{j=0} f_{i,j} B^{i,j}_{h,w} \quad \cdots Eq. (2)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;s.t. \quad h \in \{ 0, 1, \cdots, H-1 \}, w \in \{ 0, 1, \cdots, W-1&amp;nbsp; \}&lt;br /&gt;\]&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( F \in \mathbb{R}^{H \times W} \)는 2D DCT frequency spectrum이며 \( f \in \mathbb{R}^{H \times W} \)는 input image임&lt;/li&gt;
&lt;li&gt;보통은 \( H \)와 \( W \)는 같은 size를 가지므로 \( N \)으로 표기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Input image를  2D DCT을 거치고 나면 frequency domain으로 변환됨
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;2D DCT의 왼쪽 위부분이 low frequecny영역이고 오른쪽 밑부분이 high frequency 영역&lt;/li&gt;
&lt;li&gt;이미지를 구성하는  대부분 중요한 energy는 low freqeucny 영역에 포함되어 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2424&quot; data-origin-height=&quot;944&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqL5V5/btsCQ3yBKSr/JdfoKSxeW0kH54H4hbaFnk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqL5V5/btsCQ3yBKSr/JdfoKSxeW0kH54H4hbaFnk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqL5V5/btsCQ3yBKSr/JdfoKSxeW0kH54H4hbaFnk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqL5V5%2FbtsCQ3yBKSr%2FJdfoKSxeW0kH54H4hbaFnk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;262&quot; data-origin-width=&quot;2424&quot; data-origin-height=&quot;944&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;High frequency영역의 (coefficient) 값은 이미지를 구성하는 데 &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;상대적으로&lt;span&gt; 덜&amp;nbsp;&lt;/span&gt;&lt;/span&gt;중요한 역할을 하기 때문에 본 논문에서는 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;high frequecny 영역 중 중요하지 않은 영역의 값을 제거하도록 RPP model을 학습&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;중요하지 않는 영역을 &lt;b&gt;전체 &lt;/b&gt;high frequency평균값보다 낮은 영역의 값으로 정의함&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위의 내용을 수식화 하기위해 먼저 2D DCT에서 high frequency영역만을 추출하기 위해 \( I \)를 도입&amp;nbsp;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Zig-Zag order traversal 을 이용하여 위 그림처럼 high frequency영역만 추출&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;F'_{h,w} = F_{h,w} \ast I_{h, w} \quad \cdots Eq. (3)&lt;br /&gt;\]\[&lt;br /&gt;where \quad I_{h, w}= \left\{ \begin{array}{ll} 0, &amp;amp; if (h+w) &amp;lt; S, \cr&lt;br /&gt;1, &amp;amp; if (h+w) \geq S.&lt;br /&gt;\end{array} \quad \cdots Eq .(4) \right.&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;S \in \{ 0, 1, \cdots, (H-1)(W-1) \}&lt;br /&gt;\]&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;high frequency영역 중 중요하지 않는 영역을 &lt;b&gt;절댓값을 취한 뒤 전체&lt;/b&gt;&lt;b&gt;&amp;nbsp;&lt;/b&gt;high frequency 평균값보다 낮은 영역의 값으로 정의&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;high frequecny영역의 평균값을 threshold \(T\)로 정의&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;T = \frac{1}{H&amp;nbsp; \cdot W} \sum^{H-1}_{h=i} \sum^{W-1}_{w=j} ( | F'_{h,w} | ) \quad \cdots Eq. (5)&lt;br /&gt;\]\[&lt;br /&gt;where \quad i + j \geq N&lt;br /&gt;\]&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\(T\)보다 작은 \( F'_{h,w} \)&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;은 high frequency 영역 중에서도 중요하지 않은 값들이므로 제거하기 위해 해당 값들을 \( F''_{hw} \)&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;으로&lt;/span&gt; 정의&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;\( | F''_{h,w} | \)값들을 0으로 만들기 위해 아래와 같은 DCT loss 제안&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;즉, &lt;b&gt;중요치 않은 high frequency영역의 값들을 0으로 만들어 encoding size를 줄여 bitrate 줄이는 목적을 달성&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;L_{dct} = \sum^{H-1}_{h=i} \sum^{W-1}_{w=j} ( | F''_{h,w} - 0 | )&amp;nbsp; \quad \cdots Eq. (6)&lt;br /&gt;\]\[&lt;br /&gt;F''_{h,w} \in \{ F'_{h,w} &amp;lt; T \} \quad and \quad i + j \geq N&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 Network and Image Degradation&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Network는 light-weight architecture로 구성함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Channel attention module로 SE block을 사용&lt;/li&gt;
&lt;li&gt;Efficient sub-pixel convolution(&lt;span style=&quot;color: #262626; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #f3f4f7; color: #6c6c6d;&quot;&gt;torch.nn.&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #262626; text-align: left;&quot;&gt;&lt;span style=&quot;background-color: #f3f4f7; color: #6c6c6d;&quot;&gt;PixelUnshuffle&lt;/span&gt;&lt;/span&gt;)를 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;RPP model로부터 image quality가 높아진 image가 출력되도록 학습하기 위해 일부러 원본 image를 degradation하여 RPP model 입력으로 사용함
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;4 가지의 image degradation 방법 사용
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Blur&lt;/b&gt;: Isotropic and anisotropic Gaussian filter&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Noise&lt;/b&gt;: Gaussian and Poisson noise&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Upsampling and Downsampling&lt;/b&gt;: Area, bilinear, and bicubic operations&lt;/li&gt;
&lt;li&gt;&lt;b&gt;JPEG Compression&lt;/b&gt;: jpeg quality를 낮게 설정 (blocking and ringing artifact 유도)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.4 Loss Functions&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt; 위에서 정의된 \( L_{dct} \)이외에 2개의 loss를 제안함&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;1. Reconstruction loss &lt;/b&gt;&lt;/span&gt;\( L_r \)을 제안하여 RPP model로 출력된 output frame \( \hat{f} \) 가 입력 frame \( f \)의 image quality와 같아지도록 학습함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;L_{r} = \frac{1}{HW} \sum^{H-1}_{i=0} \sum^{W-1}_{j=0}&amp;nbsp;&amp;nbsp;| f^{GT}_{i,j} -&amp;nbsp;&amp;nbsp;&amp;nbsp;\hat{f}_{i,j} |&amp;nbsp;&amp;nbsp;&amp;nbsp;\quad \cdots Eq. (7)&lt;br /&gt;\]&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( f^{GT} \)는 Ground Truth(GT)인 \(f \)을 shapren processing한 것
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Sharpen은 이미지의 (high frequency영역의) contrast나 edge성분을 더 돋보여주게 함&lt;/li&gt;
&lt;li&gt;Contrast나 edge성분은 인간의 인지 시스템과 high correlation을 가지므로 GT에 sharpen을 사용함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;2. Perceptual loss&lt;/b&gt;&lt;/span&gt; \( L_p \)을 제안하여 reconstruction loss와 같은 목표를 가짐
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MS-SSIM metric을 이용하여 원본 image의 high frequency영역의 structural information과 contrast가 잘 보존되도록 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;L_{p} = 1 - L_{ms-ssim} (f^{GT}_{i,j},&amp;nbsp; \hat{f}_{i,j} )&amp;nbsp; \quad \cdots Eq. (8)&lt;br /&gt;\]&lt;/p&gt;
&lt;p 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&gt;결론적으로, 모든 loss를 종합하면 아래와 같음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;L_{all} = \lambda_1 L_{dct} + \lambda_2 L_{p} +&amp;nbsp; L_{r}&amp;nbsp; \quad \cdots Eq. (9)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Experiment&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Experiment Setup&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.1.1 Datasets&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Training으로 DIV2K and Flickr2K datasets을 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DIV2K는 2k resolution image로 구성, 데이터 수는&amp;nbsp; 1,000&lt;/li&gt;
&lt;li&gt;Flickr2K는 2k resolution image로 구성, 데이터 수는 2,650&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing으로 UVG, HEVC Standard 1080p Test Sequences, and MCL-JCV datasets을 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;해당 dataset들은 video compression algorithm을 평가하기 위해 널리 사용됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.1.2 Implementation Detatails&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;RPP model을 학습하기 위해 two stages training 사용
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;warm-up stage
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;\(L_r\) loss만 사용&lt;/li&gt;
&lt;li&gt;initial lr: 1e-3&lt;/li&gt;
&lt;li&gt;600k iterations training&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Main stage
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;\( L_{all} \) loss 사용 ( \( \lambda_1 \): 10, \( \lambda_2 \): 0.1 )&lt;/li&gt;
&lt;li&gt;initial lr: 1e-4&lt;/li&gt;
&lt;li&gt;700k iterations training&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Batch size: 32&lt;/li&gt;
&lt;li&gt;Image resolution: 128x128&lt;/li&gt;
&lt;li&gt;N (DCT loss계산시의 block size): 8 or 16&lt;/li&gt;
&lt;li&gt;Adam optimizer (\( \beta_1 \): 0.9,&lt;span&gt;&amp;nbsp;&lt;/span&gt;\( \beta_2 \): 0.999)&lt;/li&gt;
&lt;li&gt;Inference 시에는 RPP output image의 intensity를 조절하기 위해 hyperparameter \( \alpha \)을 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;\(f_p = &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\alpha&lt;span&gt;&amp;nbsp; f_o + (1 - &lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\alpha&lt;span&gt;&amp;nbsp;) f_i \)로 최종 성능을 표기한다는 뜻...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt;&lt;span style=&quot;color: #333333; text-align: left;&quot;&gt;&lt;span&gt; \( f_o \)는 RPP model의 output frame이고  \( f_i \) 는 input frame&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;모든 test dataset에 똑같은 \( \alpha \)사용하는것이 아니고 dataset마다 다른 값을 사용..&lt;/li&gt;
&lt;li&gt;HEVC and MCL-JCV datasets에는 \( \alpha \) = 0.5, UVG dataset에는 \( \alpha \)= 1 사용&lt;/li&gt;
&lt;li&gt;&lt;b&gt;꼼수라고 보면 됨...&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;RPP model을 TensorRT로 변환하여 속도 측정시 single RTX3090로 1080p 동영상에 대해 87.7FPS 성능&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Experiment Results&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존 codec(e.g. H.264, H.265)에 RPP model적용 시 아래와 같이 BD rate 성능 향상&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2552&quot; data-origin-height=&quot;984&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Boh2u/btsCN8nhIlS/UKRtZd1sxQ2xtwYakV9VH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Boh2u/btsCN8nhIlS/UKRtZd1sxQ2xtwYakV9VH0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Boh2u/btsCN8nhIlS/UKRtZd1sxQ2xtwYakV9VH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBoh2u%2FbtsCN8nhIlS%2FUKRtZd1sxQ2xtwYakV9VH0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;847&quot; height=&quot;327&quot; data-origin-width=&quot;2552&quot; data-origin-height=&quot;984&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;같은 VMAF, MS-SSIM값 기준으로 3 datsets에 대해 아래와 같은 BD rate saving&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;1442&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6Lh2c/btsCRiP7SLc/dCAXqRf4DZHKZUzfKhaK6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6Lh2c/btsCRiP7SLc/dCAXqRf4DZHKZUzfKhaK6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6Lh2c/btsCRiP7SLc/dCAXqRf4DZHKZUzfKhaK6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6Lh2c%2FbtsCRiP7SLc%2FdCAXqRf4DZHKZUzfKhaK6K%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;424&quot; height=&quot;454&quot; data-origin-width=&quot;1346&quot; data-origin-height=&quot;1442&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI paper review</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/83</guid>
      <comments>https://da2so.tistory.com/83#entry83comment</comments>
      <pubDate>Sun, 31 Dec 2023 11:02:38 +0900</pubDate>
    </item>
    <item>
      <title>[NVIDIA] DALI multi-GPU 사용법 with PyTorch</title>
      <link>https://da2so.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;※ 해당 글은 vision ai, classification task 관련된 내용만 다룹니다.&lt;/p&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;오늘은  &lt;a href=&quot;https://da2so.tistory.com/81&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이전 글&lt;/a&gt;에 이어서 DALI dataloader를 multi-gpu로 load하는 방법을 설명드리고 single-gpu와 multi-gpu간의 속도 차이를 확인해보겠습니다. 그리고 &lt;a href=&quot;https://da2so.tistory.com/81&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이전 글&lt;/a&gt;에서 추가된 부분만 설명드리도록 하겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;0.&amp;nbsp; Experiment Setup&lt;/b&gt;&lt;/h2&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;DALI를 적용해볼 data type은 image와 video 이며 실험환경은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;PyPI&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;PyTorch: 1.10.0&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;DALI: 1.6&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;decord: 0.6.0&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;OpenCV: 4.5.3.56&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;Hardware&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;CPU: Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz&amp;nbsp;&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;GPU: Tesla&amp;nbsp;V100-PCIE-32G&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;&lt;b&gt;Dataset&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;Image: 21,453 VOC images&amp;nbsp;&lt;/li&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;Video: 173 random sampled videos
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: circle;&quot;&gt;Info: 300 frames, 1080p&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size16&quot;&gt;아래 설명에 사용한 모든 코드는&amp;nbsp;&lt;a href=&quot;https://github.com/da2so/dali_pytorch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에 올려두었습니다.&lt;/p&gt;
&lt;h2 style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1.&amp;nbsp; Multi-GPU DALI Image Loader&lt;/b&gt;&lt;/h2&gt;
&lt;pre id=&quot;code_1687309739263&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# multigpu_dali_imageloader.py
import os
import argparse
import time

import torch
import torch.distributed as dist

from nvidia.dali import pipeline_def
from nvidia.dali.plugin import pytorch
import nvidia.dali.fn as fn
import nvidia.dali.types as types

LOCAL_RANK = int(os.getenv('LOCAL_RANK', -1)) 
RANK = int(os.getenv('RANK', -1))
WORLD_SIZE = int(os.getenv('WORLD_SIZE', 1))

@pipeline_def
def image_pipe(file_root: str,
               local_rank: int,
               world_size: int,
               image_size: int=640):
    jpegs, labels = fn.readers.file(file_root=file_root,
                                    initial_fill=1024,
                                    random_shuffle=True,
                                    shard_id=local_rank, # added for multi-gpu
                                    num_shards=world_size, # added for multi-gpu
                                    name=&quot;Reader&quot;)
    images = fn.decoders.image(jpegs, 
                               device=&quot;mixed&quot;, 
                               output_type=types.RGB)
    
    images = fn.resize(images, 
                       device=&quot;gpu&quot;, 
                       size=[image_size, image_size],
                       interp_type=types.INTERP_LINEAR)
    return images, labels[0]

class DALIImageLoader():
    def __init__(self, 
                 path: str, 
                 batch_size: int, 
                 num_threads: int,
                 local_rank: int,
                 world_size: int):
        pipe = image_pipe(batch_size=batch_size,
                          num_threads=num_threads, 
                          device_id=local_rank, # added for multi-gpu
                          local_rank=local_rank,
                          world_size=world_size,
                          file_root=path,
                          seed=123456)
        pipe.build()

        self.dali_iterator = pytorch.DALIGenericIterator(pipe,
                                                         [&quot;data&quot;, &quot;label&quot;],
                                                         reader_name=&quot;Reader&quot;,
                                                         last_batch_policy=pytorch.LastBatchPolicy.PARTIAL,
                                                         auto_reset=True)
    def __len__(self):
        return int(self.epoch_size)

    def __iter__(self):
        return self.dali_iterator.__iter__()

if __name__ == &quot;__main__&quot;:
    parser = argparse.ArgumentParser()
    parser.add_argument('--local_rank', type=int, default=-1, help='Automatic DDP Multi-GPU argument, do not modify')
    parser.add_argument('--device', default='6,7', help='cuda device, i.e. 0 or 0,1,2,3')
    parser.add_argument('--batch_size', type=int, default=32, help='total batch size for all GPUs, -1 for autobatch')
    parser.add_argument('--num_threads', type=int, default=8, help='number of threads')
    parser.add_argument('--data_dir', type=str, default='/usr/src/app/da2so/datasets/VOC/images', help='dataset directory')    
    args = parser.parse_args()

    os.environ['CUDA_VISIBLE_DEVICES'] = args.device 
    torch.cuda.set_device(LOCAL_RANK)
    device = torch.device('cuda', LOCAL_RANK)
    os.environ['NCCL_BLOCKING_WAIT'] = '1'  # set to enforce timeout
    dist.init_process_group('nccl' if dist.is_nccl_available() else 'gloo')

    daliloader = DALIImageLoader(path=args.data_dir,
                                 batch_size=args.batch_size,
                                 num_threads=args.num_threads,
                                 local_rank=LOCAL_RANK,
                                 world_size=WORLD_SIZE)
    if RANK == 0:
        start_time = time.time()
        for idx, inp in enumerate(daliloader):
            print(f'image shape: {inp[0][&quot;data&quot;].shape}')
            print(f'label shape: {inp[0][&quot;label&quot;].shape}')
        print(f'[Multi-GPU {args.device} DALI Imageloader] time: {time.time() - start_time}')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi-GPU를 사용하기 위해 기존 &lt;code&gt;DALIImageLoader&lt;/code&gt;에서 추가된 arugments는 &lt;code&gt;local_rank&lt;/code&gt;, &lt;code&gt;world_size&lt;/code&gt; 2개입니다. Multi-GPU학습에 익숙하신분들은 아시겠지만 &lt;code&gt;local_rank&lt;/code&gt;는 사용되는 GPU number(id)를 뜻하고 &lt;code&gt;world_size&lt;/code&gt;는 사용되는 GPU 전체 개수를 의미합니다. 해당 인자들은 &lt;code&gt;DALIImageLoader&lt;/code&gt;의 &lt;code&gt;image_pipe&lt;/code&gt;함수에 사용됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;image_pipe&lt;/code&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;device_id=local_rank&lt;/code&gt;: local_rank에 해당하는 하나의 GPU를 할당
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;해당 인자는 &lt;code&gt;@pipeline_def&lt;/code&gt; decorator의 파라미터임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;device_id=local_rank&lt;/code&gt;을 통해 각 GPU로 pipeline을 실행할 수 있게 되었습니다. 여기서 추가적으로 각 GPU가 서로 다른 samples을 managing할 수 있도록 하는 기술인 sharding을 사용합니다. Dataset을 여러 parts(shards)로 나누어 각 GPU는 고유의 shard로 data load를 진행하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;177&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/67xYk/btskIPZVsBO/ib3bAUK7Ox4nV5Ob0itsC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/67xYk/btskIPZVsBO/ib3bAUK7Ox4nV5Ob0itsC0/img.png&quot; data-alt=&quot;Sharding&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/67xYk/btskIPZVsBO/ib3bAUK7Ox4nV5Ob0itsC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F67xYk%2FbtskIPZVsBO%2Fib3bAUK7Ox4nV5Ob0itsC0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;148&quot; data-origin-width=&quot;769&quot; data-origin-height=&quot;177&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Sharding&lt;/figcaption&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;&lt;code&gt;fn.readers.file&lt;/code&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;shard_id=local_rank&lt;/code&gt;: 각 GPU가 고유의 shard_id를 가지도록 함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;num_shards=world_size&lt;/code&gt;: 사용하는 총 shard개수를 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위 코드를 &lt;code&gt;torchrun --standalone --nnodes=1 --nproc_per_node=2 multigpu_dali_imageloader.py --device=0,1&lt;/code&gt; 명령어로 실행시켜봅니다. GPU 2개를 사용하여 각 GPU당 32 batch size으로 설정하였고 image는 640으로 resize 하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;174&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJySlg/btskS2cqOmG/SvbB5O04BJKBeEf7dC7WP1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJySlg/btskS2cqOmG/SvbB5O04BJKBeEf7dC7WP1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJySlg/btskS2cqOmG/SvbB5O04BJKBeEf7dC7WP1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJySlg%2FbtskS2cqOmG%2FSvbB5O04BJKBeEf7dC7WP1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;106&quot; data-origin-width=&quot;546&quot; data-origin-height=&quot;174&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;총 21,453개의 image를 load하는데 3.1초 정도밖에 걸리지 않네요!&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Multi-GPU DALI Video Loader&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Video loader부분에 대한 설명은 위의 image loader부분과 내용이 다수 겹치고 기존 single-gpu video loader부분과도 유사한 부분이 많아 바뀐 코드만 보여드립니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1687328890470&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;... 생략

@pipeline_def
def video_pipe(filenames: List[str],
               labels: List[int], 
               sequence_length: int,
               stride: int,
               local_rank: int,
               world_size: int):

    videos, label = fn.readers.video(device=&quot;gpu&quot;, 
                              filenames=filenames,
                              labels=labels, 
                              sequence_length=sequence_length,
                              normalized=False, 
                              random_shuffle=True, 
                              image_type=types.RGB,
                              dtype=types.UINT8,
                              initial_fill=16,
                              num_shards=world_size, # added for multi-gpu
                              shard_id=local_rank, # added for multi-gpu
                              stride=stride,
                              name=&quot;Reader&quot;)
    return videos, label[0]

... 생략&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;image loader와 다른점은 &lt;code&gt;fn.readers.video&lt;/code&gt; 함수의 인자로 &lt;code&gt;shard_id&lt;/code&gt;와 &lt;code&gt;num_shards&lt;/code&gt;를 인자를 추가해주어야 한다는 점입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Multi-GPU DALI video loader에서도 위 코드를 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;code&gt;torchrun --standalone --nnodes=1 --nproc_per_node=2 multigpu_dali_videoloader.py --device=0,1&lt;/code&gt;&lt;span&gt; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;명령어로 실행시켜봅니다. GPU 2개로 각 GPU당 8&amp;nbsp; batch size를 가지도록 하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mzz0z/btskRMunEuf/O7ZKYZkDnIF6ZmyvKUh3eK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mzz0z/btskRMunEuf/O7ZKYZkDnIF6ZmyvKUh3eK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mzz0z/btskRMunEuf/O7ZKYZkDnIF6ZmyvKUh3eK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmzz0z%2FbtskRMunEuf%2FO7ZKYZkDnIF6ZmyvKUh3eK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;401&quot; height=&quot;102&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;총 173개의 video를 load하는데 48.2초 소요되었습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;2. Single-GPU vs Multi-GPU DALI Loader 시간 비교&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;해당 섹션에서는 GPU개수에 따른 DALI Loader의 시간 측정을 진행합니다. 추가적으로 Pytorch dataloader의 data load시간을 baseline으로 잡고 진행하였습니다.&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Experiment Setting
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;[DALI, PyTorch]&lt;/b&gt; batch size = 32(image), 8(video)&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 GPU당 batch size임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[DALI]&lt;/b&gt; num threads = 8&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[PyTorch]&lt;/b&gt; num workers = 8&lt;/li&gt;
&lt;li&gt;&lt;b&gt;[PyTorch]&lt;/b&gt; pin_memory = True&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.5117%; height: 88px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 25%; text-align: center; height: 20px;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Time cost for image loader (s)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 20px; text-align: center;&quot;&gt;&lt;b&gt;Time cost for video loader (s)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;PyTorch CPU&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;101.06&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;59.01&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;DALI 1-GPU&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;6.24&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;97.45&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;DALI 2-GPU&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;3.07&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;48.28&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;DALI 4-GPU&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;1.57&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;21.34&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Image loader 결과&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;CPU로 data load하는 PyTorch보다 DALI사용 시 data load속도가 훨씬 빠름&lt;/li&gt;
&lt;li&gt;GPU가 배로 늘어날수록 속도도 비례하여 빨라짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Video loader 결과&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU로 data load하는 PyTorch보다 Single GPU를 사용하는 DALI가 더 느림&lt;/li&gt;
&lt;li&gt;하지만, GPU 2개이상 사용시 PyTorch data loader보다 빨라짐&lt;/li&gt;
&lt;li&gt;Image loader와 동일하게 GPU 개수에 따라 속도도 비례하게 빨라짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI Engineering/NVIDIA</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/82</guid>
      <comments>https://da2so.tistory.com/82#entry82comment</comments>
      <pubDate>Thu, 22 Jun 2023 13:39:31 +0900</pubDate>
    </item>
    <item>
      <title>[NVIDIA] DALI 사용법 with PyTorch</title>
      <link>https://da2so.tistory.com/81</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;※ 해당 글은 vision ai, classification task 관련된 내용만 다룹니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 DALI를 사용하는 방법을 알아보고 DALI를 사용했을 때와 사용하지 않았을 경우의 time cost차이를 직접 측정해보도록 하겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. DALI 란?&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Data Loading Library (DALI)는 DNN을 training할때 data loading 및 pre-processing을 GPU을 사용할 수 있도록 하는 GPU-accelerated library&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;그래서 CPU를 사용할 때보다 훨씬 빠르게 training이 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DALI는 자체적인 execution engine을 가지며 input pipeline의 throughput을 최대화 시키기위해 설계됨&amp;nbsp;&lt;/li&gt;
&lt;li&gt;DALI는 portable하기 때문에 PyTorch, TensorFlow, MXNet에 쉽게 integrated 가능&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;다양한 data format 지원: TFRecord, COCO, JPEG, JPEG 2000, WAV, FLAC, OGG, H.264, VP9 and HEVC&lt;/li&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;여러 GPUs에 scaleable가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;569&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oq0lT/btsis12fKf9/G4KwkhHYggDwgJwUm18PPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oq0lT/btsis12fKf9/G4KwkhHYggDwgJwUm18PPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oq0lT/btsis12fKf9/G4KwkhHYggDwgJwUm18PPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Foq0lT%2Fbtsis12fKf9%2FG4KwkhHYggDwgJwUm18PPk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;262&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;569&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. DALI 사용법&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Experiment Setup&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DALI를 적용해볼 data type은 image와 video 이며 실험환경은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;PyPI&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;PyTorch: 1.10.0&lt;/li&gt;
&lt;li&gt;DALI: 1.6&lt;/li&gt;
&lt;li&gt;decord: 0.6.0&lt;/li&gt;
&lt;li&gt;OpenCV: 4.5.3.56&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Hardware&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;CPU: Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz&amp;nbsp;&lt;/li&gt;
&lt;li&gt;GPU: Tesla&amp;nbsp;V100-PCIE-32G&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dataset&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Image: 21,453 VOC images&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Video: 173 random sampled videos
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Info: 300 frames, 1080p&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 설명에 사용한 모든 코드는 &lt;a href=&quot;https://github.com/da2so/dali_pytorch&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기&lt;/a&gt;에 올려두었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 DALI Image data loading&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DALI를 이용하여 Image data를 loading하는 방법을 소개드립니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1685771281995&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import time

from nvidia.dali import pipeline_def
from nvidia.dali.plugin import pytorch
import nvidia.dali.fn as fn
import nvidia.dali.types as types

@pipeline_def
def image_pipe(file_root: str,
               image_size: int=640):
    jpegs, labels = fn.readers.file(file_root=file_root,
                                    initial_fill=1024,
                                    random_shuffle=True,
                                    name=&quot;Reader&quot;)
    images = fn.decoders.image(jpegs, 
                               device=&quot;mixed&quot;, 
                               output_type=types.RGB)
    images = fn.resize(images, 
                       device=&quot;gpu&quot;, 
                       size=[image_size, image_size],
                       interp_type=types.INTERP_LINEAR)
    return images, labels[0]

class DALIImageLoader():
    def __init__(self, 
                 path: str, 
                 batch_size: int, 
                 num_threads: int):
        pipe = image_pipe(batch_size=batch_size,
                          num_threads=num_threads, 
                          device_id=0, 
                          file_root=path,
                          seed=123456)
        pipe.build()
        self.dali_iterator = pytorch.DALIGenericIterator(pipe,
                                                         [&quot;data&quot;, &quot;label&quot;],
                                                         reader_name=&quot;Reader&quot;,
                                                         last_batch_policy=pytorch.LastBatchPolicy.PARTIAL,
                                                         auto_reset=True)
    def __len__(self):
        return int(self.epoch_size)

    def __iter__(self):
        return self.dali_iterator.__iter__()

if __name__ == &quot;__main__&quot;:
    start_time = time.time()
    daliloader = DALIImageLoader(path='/usr/src/app/da2so/datasets/VOC/images',
                                 batch_size=32,
                                 num_threads=8)
    for inp in daliloader:
        print(f'image shape: {inp[0][&quot;data&quot;].shape}')
        print(f'label shape: {inp[0][&quot;label&quot;].shape}')
    print(f'[DALI Imageloader] time: {time.time() - start_time}')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에서는 &lt;code&gt;batch_size&lt;/code&gt;를 32로 고정하여 &lt;code&gt;DALIImageLoader&lt;/code&gt;를 intialization하는 것을 시작으로 daliloader를 통해 data load하는 데까지 소요되는 시간을 측정하는 코드입니다. 이제 그럼 &lt;code&gt;DALIImageLoader&lt;/code&gt;에 대해 하나하나 자세히 살펴보죠.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;DALIImageLoader&lt;/code&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;image_pipe&lt;/code&gt; 함수 호출을 통해 DALI의 pipeline object을 initalization&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pipe.build()&lt;/code&gt; 를 통해 pipeline build&lt;/li&gt;
&lt;li&gt;&lt;code&gt;torch.DALIGenericIterator&lt;/code&gt;는 build한 pipeline을 대상으로 Pytorch용 DALI iterator 생성
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;즉, DALI pipeline으로 load된 data가 gpu device로 mapping된 tensor를 형태를 가지게 됨&lt;/li&gt;
&lt;li&gt;&lt;code&gt;last_batch_policy&lt;/code&gt;: data수가 정확히 batch로 나눠지지 않을경우 마지막 batch를 어떻게 처리할 지 정함
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;PARTIAL&lt;/code&gt;은 마지막 batch가 setting한 batch 수보다 작을 경우에 그대로 작은 batch로 data load해줌&lt;/li&gt;
&lt;li&gt;예를 들어 전체 data가 10개이고 batch가 4라면 마지막 iteration에서는 batch가 2로 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;auto_reset=True&lt;/code&gt;: DALI iterator의 마지막 iteration에서 StopIteration이 발생하고 자동적으로 &lt;code&gt;reset()&lt;/code&gt; 호출해줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image_pipe&lt;/code&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;@pipeline_def&lt;/code&gt;라는 decorator를 사용해서 DALI pipeline구성하게 해줌&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 decorator를 arugment로 image_pipe함수의 &lt;code&gt;batch_size&lt;/code&gt;, &lt;code&gt;num_threads&lt;/code&gt;, &lt;code&gt;device_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;device_id&lt;/code&gt;: gpu device id를 의미&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fn.reader.file&lt;/code&gt;: file 또는 directory경로를 입력으로 image content와 label을 return (아래 그림 참조)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 process는 cpu로 동작함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;file_root&lt;/code&gt;: data files을 담고있는 directory&lt;/li&gt;
&lt;li&gt;&lt;code&gt;initial_fill&lt;/code&gt;: shuffling에 사용될 buffer_size&lt;/li&gt;
&lt;li&gt;&lt;code&gt;random_shuffle&lt;/code&gt;: data shuffling 유무&lt;/li&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt;: &lt;code&gt;torch.DALIGenericIterator&lt;/code&gt;의 &lt;code&gt;reader_name&lt;/code&gt;과 일치되는 이름&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fn.decoders.image&lt;/code&gt;: image content를 decoding하는 processing
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;device='mixed'&lt;/code&gt;: cpu와 gpu를 mix해서 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;jpeg인 경우 nvJPEG&lt;span style=&quot;background-color: #fcfcfc; color: #404040; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;library(or&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;libjpeg-turbo)을 사용하고 다른 image format일 경우 OpenCV사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fn.resize&lt;/code&gt;: batch로 data load하기위해 image size를 동일시 함
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;device='gpu'&lt;/code&gt;은 gpu를 통해 resize operation진행함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;429&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c05NDK/btsjJUgoGWd/pnrjQ2nK9yUZWXBUAvjocK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c05NDK/btsjJUgoGWd/pnrjQ2nK9yUZWXBUAvjocK/img.png&quot; data-alt=&quot;fn.reader.file return 형식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c05NDK/btsjJUgoGWd/pnrjQ2nK9yUZWXBUAvjocK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc05NDK%2FbtsjJUgoGWd%2FpnrjQ2nK9yUZWXBUAvjocK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;275&quot; height=&quot;216&quot; data-origin-width=&quot;545&quot; data-origin-height=&quot;429&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;fn.reader.file return 형식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드 실행하면 아래와 같이 정상적으로 실행되는 것을 확인가능합니다. 총 21,453개의 image를 load하는데 6.2초 정도밖에 걸리지 않네요!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;180&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FCONj/btskRnImQF0/SLZTtASqSzXaTKvqT1acoK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FCONj/btskRnImQF0/SLZTtASqSzXaTKvqT1acoK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FCONj/btskRnImQF0/SLZTtASqSzXaTKvqT1acoK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFCONj%2FbtskRnImQF0%2FSLZTtASqSzXaTKvqT1acoK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;325&quot; height=&quot;111&quot; data-origin-width=&quot;526&quot; data-origin-height=&quot;180&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래사진을 통해서는 DALIImageLoader를 실행시킴으로써 GPU memory를 사용하는 것을 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;96&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G3O4U/btsjIKZuv0f/0PfBjj9cIokLcr19TLgHG1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G3O4U/btsjIKZuv0f/0PfBjj9cIokLcr19TLgHG1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G3O4U/btsjIKZuv0f/0PfBjj9cIokLcr19TLgHG1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG3O4U%2FbtsjIKZuv0f%2F0PfBjj9cIokLcr19TLgHG1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;564&quot; height=&quot;63&quot; data-origin-width=&quot;862&quot; data-origin-height=&quot;96&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 DALI Video data loading&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 DALI를 이용하여 video를 load하는 방법을 소개드립니다. Image를 load하는 부분과 동일한 부분은 생략하고 설명드리도록 하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1686363052499&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os
import time
import glob
from typing import List
from pathlib import Path

from nvidia.dali import pipeline_def
from nvidia.dali.plugin import pytorch
import nvidia.dali.fn as fn
import nvidia.dali.types as types

VID_FORMATS = 'avi', 'm4v', 'mkv', 'mov', 'mp4', 'mpeg' # include video suffixes

@pipeline_def
def video_pipe(filenames: List[str],
               labels: List[int], 
               sequence_length: int,
               stride: int):

    videos, label = fn.readers.video(device=&quot;gpu&quot;, 
                              filenames=filenames,
                              labels=labels, 
                              sequence_length=sequence_length,
                              normalized=False, 
                              random_shuffle=True, 
                              image_type=types.RGB,
                              dtype=types.UINT8, 
                              initial_fill=16,
                              stride=stride,
                              name=&quot;Reader&quot;)
    return videos, label[0]

def video2label_paths(video_path: List) -&amp;gt; List:        
    return [int(Path(x).parts[-2]) for x in video_path]


class DALIVideoLoader():
    def __init__(self, 
                 path: str, 
                 batch_size: int, 
                 num_threads: int,
                 sequence_length: int,
                 stride: int):
        
        try:
            f = [] # video files 
            for p in path if isinstance(path, list) else [path]:
                p = Path(p) 
                if p.is_dir():  # dir
                    f += glob.glob(str(p / '**' / '*.*'), recursive=True)
                elif p.is_file():  # file
                    with open(p) as t:
                        t = t.read().strip().splitlines()
                        parent = str(p.parent) + os.sep
                        f += [x.replace('./', parent) if x.startswith('./') else x for x in t]  # local to global path
        except Exception as e:
            raise Exception(f'Error loading data from {path}: {e}\n')
        self.vd_files = sorted(x.replace('/', os.sep) for x in f if x.split('.')[-1].lower() in VID_FORMATS)
        assert self.vd_files, f'No videos found'
        self.labels = video2label_paths(self.vd_files)
        assert len(self.vd_files) == len(self.labels), f'The number of video files are not matched with label files'

        pipe = video_pipe(batch_size=batch_size, 
                          num_threads=num_threads, 
                          device_id=0, 
                          filenames=self.vd_files,
                          labels=self.labels,
                          stride=stride,
                          sequence_length=sequence_length,
                          seed=123456)
        pipe.build()

        self.dali_iterator = pytorch.DALIGenericIterator(pipe,
                                                         [&quot;data&quot;, &quot;label&quot;],
                                                         reader_name=&quot;Reader&quot;,
                                                         last_batch_policy=pytorch.LastBatchPolicy.PARTIAL,
                                                         auto_reset=True)
    def __len__(self):
        return int(self.epoch_size)

    def __iter__(self):
        return self.dali_iterator.__iter__()

if __name__ == &quot;__main__&quot;:
    start_time = time.time()
    daliloader = DALIVideoLoader(path='./videos',
                                 sequence_length=60,
                                 stride=5,
                                 batch_size=8,
                                 num_threads=8)
    for inp in daliloader:
        print(f'video shape: {inp[0][&quot;data&quot;].shape}')
        print(f'label shape: {inp[0][&quot;label&quot;].shape}')
    print(f'[DALI Videoloader] time: {time.time() - start_time}')&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Image파트와 비슷하게 &lt;code&gt;DALIVideoLoader&lt;/code&gt;를 intialization하고 data load하는 데까지 소요되는 시간을 측정하는 코드입니다. 여기서 &lt;code&gt;DALIVideoLoader&lt;/code&gt;의 &lt;code&gt;__init__&lt;/code&gt;함수에서 &lt;code&gt;image_pipe&lt;/code&gt; 아닌 &lt;code&gt;video_pipe&lt;/code&gt;를 호출하는 것과 path를 입력으로 video files과 그에 대한 labels를 추출하는 것 말고는 &lt;code&gt;DALIImageLoader&lt;/code&gt;와 크게 다르지 않습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;DALIVideoLoader&lt;/code&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;self.vd_files&lt;/code&gt;: path directory에 포함된 모든 video files path&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.labels&lt;/code&gt;: video파일에 대응되는 labels&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;video_pipe&lt;/code&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;sequence_length&lt;/code&gt;: video에서 가져올 frame 수&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stride&lt;/code&gt;: 가져올 frame간의 interval&lt;/li&gt;
&lt;li&gt;&lt;code&gt;normalized&lt;/code&gt;: video영상을 normalize 할건지&lt;/li&gt;
&lt;li&gt;&lt;code&gt;image_type&lt;/code&gt;: video의 각 frame(image)의 type을 명시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 &lt;code&gt;sequence_length&lt;/code&gt;가 60, &lt;code&gt;stride&lt;/code&gt;가 5라는 것은 video로부터 1, 5, 10, 15, ...,295, 300 번째 frames(총 60 frames)을 data load하겠다는 의미입니다. 위 파일을 실행시키면 아래 사진과 같이 정상적으로 실행이 됨을 확인가능합니다. 하지만 video가 173개 load하는 데 97초나 걸리네요...&amp;nbsp; (많이 느리네요..ㅠ)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;176&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfpQOt/btskQjNEs8A/ctcqUHHx2Wkk220CsTFh20/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfpQOt/btskQjNEs8A/ctcqUHHx2Wkk220CsTFh20/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfpQOt/btskQjNEs8A/ctcqUHHx2Wkk220CsTFh20/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfpQOt%2FbtskQjNEs8A%2FctcqUHHx2Wkk220CsTFh20%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;375&quot; height=&quot;114&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;176&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Video load할 경우에 GPU memory를 사용하긴 하는데 GPU utils이 image loader랑 다르게 너무 낮은 느낌이... 있네요.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;84&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nXMed/btsjJSXhbMt/eaawwgamXPpyHFqF72ohbk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nXMed/btsjJSXhbMt/eaawwgamXPpyHFqF72ohbk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nXMed/btsjJSXhbMt/eaawwgamXPpyHFqF72ohbk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnXMed%2FbtsjJSXhbMt%2FeaawwgamXPpyHFqF72ohbk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;58&quot; data-origin-width=&quot;870&quot; data-origin-height=&quot;84&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.4 DALI vs OpenCV(decord) 속도 비교&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;해당 section에서는 DALI를 이용한 GPU data load방식과 CPU data load방식을 사용하였을때의 data load time cost를 비교하겠습니다. 비교를 위해서 GPU 1개만 사용하였고 CPU는 모두 사용하였습니다. CPU image data load는 OpenCV를, CPU video data load는 decord(cpu version)를 사용하였습니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2.4.1  DALI&amp;nbsp; vs OpenCV&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;Tim cost비교를 위해 21,453 VOC images를 사용하였고 OpenCV를 이용한 Dataloader는 기본적인 &lt;code&gt;torch.utils.data.dataloader.DataLoader&lt;/code&gt;를 사용하였습니다. (image size는 640으로 resize함)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;268&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ctnCKK/btskVMmBBSS/1ekCoAv0HNZOufwybiVhC0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ctnCKK/btskVMmBBSS/1ekCoAv0HNZOufwybiVhC0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ctnCKK/btskVMmBBSS/1ekCoAv0HNZOufwybiVhC0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FctnCKK%2FbtskVMmBBSS%2F1ekCoAv0HNZOufwybiVhC0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;127&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;268&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;위 결과로부터 DALI ImageLoader가 약 16배 정도 빠른것을 알 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;2.4.2  DALI&amp;nbsp; vs   decord&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Time cost비교를 위해 173개의 video를 사용하였고 image부분과 동일하게 decord를 이용한 Datloader는 &lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;&lt;code&gt;torch.utils.data.dataloader.DataLoader&lt;/code&gt;를 사용하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;decord_vs_dali.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;264&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZUy0N/btskRRQcuyd/KR4xluoKmifHJgebtFNSUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZUy0N/btskRRQcuyd/KR4xluoKmifHJgebtFNSUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZUy0N/btskRRQcuyd/KR4xluoKmifHJgebtFNSUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZUy0N%2FbtskRRQcuyd%2FKR4xluoKmifHJgebtFNSUk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;132&quot; data-filename=&quot;decord_vs_dali.png&quot; data-origin-width=&quot;1254&quot; data-origin-height=&quot;264&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과를 보시면 GPU를 사용한 DALI가 약 2배정도 더 느린것을 알 수 있습니다. DALI를 이용하여 GPU 1개로 video load하는 것은 cpu보다 느리네요... 하지만, training시에 CPU data loader를 사용하였을 경우 CPU &amp;rarr; GPU &amp;rarr; CPU로 context switching이 많이 일어나기 때문에 이 부분에 대 한 고려를 했을 때 비슷해지지않을까 싶습니다.&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;다음 글에서는 multi-gpu를 사용한 DALI에 대해 설명드리겠습니다.&lt;/p&gt;</description>
      <category>AI Engineering/NVIDIA</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/81</guid>
      <comments>https://da2so.tistory.com/81#entry81comment</comments>
      <pubDate>Sun, 18 Jun 2023 07:27:34 +0900</pubDate>
    </item>
    <item>
      <title>[BentoML] ML model serving 방법 (feat. YOLOv8)</title>
      <link>https://da2so.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 BentoML을 이용한 model serving 방법을 설명드리려고 합니다.  정확히는 BentoML을 사용하여 model serving을 위한 model prediction api를 생성하는 것을 목표로 하겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1.&amp;nbsp; BentoML이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model serving 방법을 설명드리기 전 BentoML에 대해 간단히 알아보죠. BentoML의 Bento는 일본어이며 한국어로는 도시락을 의미합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0e1XI/btsgl6wFffN/Ft2m96cRb49QkRYKOrRlf1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0e1XI/btsgl6wFffN/Ft2m96cRb49QkRYKOrRlf1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0e1XI/btsgl6wFffN/Ft2m96cRb49QkRYKOrRlf1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0e1XI%2Fbtsgl6wFffN%2FFt2m96cRb49QkRYKOrRlf1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;467&quot; height=&quot;194&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;568&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;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;BentoML&lt;/b&gt;&lt;/span&gt;: ML-powered prediction service 생성을 쉽게 해주는 framework
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;BentoML의 Bento는 일본어이며 한국어로는 도시락을 의미&lt;/li&gt;
&lt;li&gt;도시락이 밥과 반찬이 모두 있는 것처럼 BentoML은 model serving에 필요한 요소들을 모아주기 때문에 이와 같이 naming&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;BentoML 장점&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;실제 production service에 ML model을 serving하는 데 있어서 필요한 지식과 시간을 최소화시켜주는 도구&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다수의 ML 개발자들은 API, docker 사용법을 잘 모른다는 문제를 해소시켜줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ML model의 production에 deploy하기까지의 process를 accelerate 및 standarize 함&lt;/li&gt;
&lt;li&gt;Scalable하고 high performance의 prediction service 제공&lt;/li&gt;
&lt;li&gt;지속적으로 prediction service에 대해 deploy, monitor, operate 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Environment Setup&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Model serving에 필요한 BentoML version 및 사용한 enviornment입니다. 오늘 serving할 모델은 &lt;a href=&quot;https://github.com/ultralytics/ultralytics&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;YOLOv8&lt;/a&gt;을 사용할 것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;BentoML version&lt;/b&gt;: 1.0.19 &lt;span style=&quot;color: #ef5369;&quot;&gt;&lt;b&gt;(중요!)&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Torch version&lt;/b&gt;: 1.9.0&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Model&lt;/b&gt;: YOLOv8s&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Docker&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CPU&lt;/b&gt;: &amp;nbsp;Intel(R)&amp;nbsp;Xeon(R)&amp;nbsp;Gold&amp;nbsp;5120&amp;nbsp;CPU&amp;nbsp;@&amp;nbsp;2.20GHz&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GPU&lt;/b&gt;: Tesla&amp;nbsp;V100-PCIE-32GB&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Model serving with BentoML&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BentoML을 이용한 YOLOv8s을 serving하는 방법을 설명드리도록 하겠습니다. 전체 코드는 &lt;a href=&quot;https://github.com/da2so/bentoml_yolov8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 확인 가능하십니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Saving a Model&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BentoML을 이용하여 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;YOLOv8s 모델을 BentoML전용 model store 저장&lt;/b&gt;&lt;/span&gt;합니다.&amp;nbsp; 따로 model store(in local dir)에 저장하는 이유는 모델 버전 관리 및 meta data를 같이 저장하기 위함입니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1684552038664&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# bentoml_packer.py
import bentoml
from ultralytics import YOLO

model = YOLO(&quot;yolov8s.pt&quot;).model
model.eval()
saved_model = bentoml.pytorch.save_model(name='yolov8s_model',
                                         model=model,
                                         signatures={&quot;__call__&quot;: {&quot;batchable&quot;: False}})
print(saved_model)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 &lt;code&gt;bentoml.pytorch.save_model&lt;/code&gt; 함수를 통해 load한 YOLOv8s model을 &lt;code&gt;yolov8s_model&lt;/code&gt; 이라는 이름으로 저장합니다. &lt;code&gt;signatures&lt;/code&gt; parameter에 batchable을 False로 하여 batch를 1만 사용하도록 제한하였습니다.&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;위 파일을 실행시키면 아래와 같이 정상적으로 model saving되는 것을 확인가능합니다. Tag부분을 보면 docker에 image_name:tag와 똑같이 model_name:tag(&lt;code&gt;yolov8s_model:ukgv3lhwxstqdibw&lt;/code&gt;)형태로 저장됩니다. 그리고&amp;nbsp;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;bentoml models list cli를 통해 저장된 model size나 생성 시간 등을 확인가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;126&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dVZl3A/btsgELeUQYe/Mdw6GYGHBIy35eppKdyk1K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dVZl3A/btsgELeUQYe/Mdw6GYGHBIy35eppKdyk1K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dVZl3A/btsgELeUQYe/Mdw6GYGHBIy35eppKdyk1K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdVZl3A%2FbtsgELeUQYe%2FMdw6GYGHBIy35eppKdyk1K%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;522&quot; height=&quot;65&quot; data-origin-width=&quot;1014&quot; data-origin-height=&quot;126&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Creating a Service&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Service는 BentoML의 core component이며 model serving의 logic을 담는 주체&lt;/b&gt;&lt;/span&gt;입니다. YOLOv8 모델을 통해 detection (inference)할 수 있도록 하는 api endpoint를 쉽게 만들 수 있으며 해당 endpoint의 input과 output의 type, shape을 정의가능합니다. 또한 model inference에 필요한 코드도 아래와 같이 작성하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684554009578&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# service.py
... 생략 ... 

import bentoml
from bentoml.io import Image, JSON

yolov8s_runner = bentoml.pytorch.get(&quot;yolov8s_model:latest&quot;).to_runner()
svc = bentoml.Service(&quot;yolov8s_svc&quot;, runners=[yolov8s_runner])

def encode_image(input_img):
    ratio = 3  # 0~9
    encode_param = [cv2.IMWRITE_PNG_COMPRESSION, ratio]
    encoded_img = base64.b64encode(cv2.imencode(&quot;.png&quot;, input_img, encode_param)[1])

    return encoded_img.decode(&quot;utf8&quot;)
... 생략 ...

@svc.api(input=Image(),
        output=JSON())
def predict(f: Image):
    img_origin, img_tensor = pre_processing(f=f)
    out = yolov8s_runner.run(img_tensor)
    out_bbox_info, out_img = post_processing(img_origin=img_origin,
                                             img_tensor=img_tensor,
                                             out=out)
    enc_out_img = encode_image(out_img) 
    cls = out_bbox_info.cls.detach().cpu().numpy()
    conf = out_bbox_info.conf.detach().cpu().numpy()
    coord = out_bbox_info.data[:,:4].detach().cpu().numpy()

    res = {'enc_out_img': enc_out_img, 'cls': cls, 'conf': conf, 'coord': coord}
    return res&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;BentoML 코드에만 초점을 맞추어 설명드리기 위해 YOLOv8을 위한 process는 위 코드에서 생략하였습니다.&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;저장한 yolov8s_model을 load하기위해 &lt;code&gt;bentoml.pytorch.get&lt;/code&gt; 함수를 사용하였습니다. 그리고 &lt;code&gt;to_runner&lt;/code&gt; 함수를 통해 모델을 실행(inference)할 수 있는 하나의 computation unit로 만듦
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Runner는 remote python worker에서 실행되며 scaling기능을 가지고 있음&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bentoml.Service&lt;/code&gt; 함수를 통해 yolov8s_svc이름의 service 생성함
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Service를 handling하는 주체는 &lt;code&gt;svc&lt;/code&gt; variable&lt;/li&gt;
&lt;li&gt;runners인자에 위의 정의한 runner인 &lt;code&gt;yolov8s_runner&lt;/code&gt;를  넣어줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@svc.api(input=Image(), output=JSON())&lt;/code&gt; 을 통해 svc service의 inference api endpoint를 만듦
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Input의 type을 &lt;code&gt;Image&lt;/code&gt;형태로 받을 것이고 output은 &lt;code&gt;Json&lt;/code&gt;형태임을 명시함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;predict(f: Image)&lt;/code&gt;을 통해 inference api endpoint 이름은 predict로 정의하고 f라는 parameter를 통해 Image을 받음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;predict&lt;/code&gt; 함수 내에서 inference를 위한 service logic을 구현함
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;pre_processing&lt;/code&gt; 함수를 통해 PIL형태의 image를 torch tensor로 바꿈&lt;/li&gt;
&lt;li&gt;위의 &lt;code&gt;bentoml.Service&lt;/code&gt;에서 runners의 인자로 들어간 &lt;code&gt;yolov8s_runner&lt;/code&gt;의 &lt;code&gt;run&lt;/code&gt;함수를 실행하여 inference진행&lt;/li&gt;
&lt;li&gt;&lt;code&gt;post_processing&lt;/code&gt; 함수를 통해 detection result image와 detection result info(bbox, class, confidence)를 출력&lt;/li&gt;
&lt;li&gt;endpoint선언 시 output은 JSON형태로 보내기로 선언했기 때문에 encoding한 detection result image와 detection result info를 json형태로 보냄
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;detection result image인 &lt;code&gt;out_img&lt;/code&gt;를 그대로 json형태로 보낼 경우에 &lt;code&gt;out_img&lt;/code&gt;의 data size가 크기 때문에 response time이 느려지는 문제가 발생하므로 encoding하여 보냄&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 코드에 대해 service 테스트해보도록 하겠습니다. &amp;nbsp;&lt;code&gt;bentoml serve service:svc&lt;/code&gt; cli 을 입력하면 serving 테스트진행하게 됩니다. &lt;code&gt;service:svc&lt;/code&gt;에서 service는 &lt;code&gt;service.py&lt;/code&gt;를 의미하고 svc는 service.py내의 service 주체인 &lt;code&gt;svc&lt;/code&gt; variable을 의미합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;104&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dxMq6p/btsgDPbuev9/kmkQj1gQlWQzlZ36KTLyo1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dxMq6p/btsgDPbuev9/kmkQj1gQlWQzlZ36KTLyo1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dxMq6p/btsgDPbuev9/kmkQj1gQlWQzlZ36KTLyo1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdxMq6p%2FbtsgDPbuev9%2FkmkQj1gQlWQzlZ36KTLyo1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;45&quot; data-origin-width=&quot;1822&quot; data-origin-height=&quot;104&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 출력된다면 정상적으로 service가 실행 중입니다. 위의 log를 살펴보면 &lt;code&gt;http://0.0.0.0:3000&lt;/code&gt;으로 &lt;code&gt;service:svc&lt;/code&gt;을 listening(요청을 받음)하는 것을 알 수 있습니다. 기본적으로 bentoml service에서 사용되는 port는 &lt;code&gt;3000&lt;/code&gt;입니다. 그렇다면 &lt;code&gt;http://0.0.0.0:3000&lt;/code&gt;에 inference를 담당하는 predict api가 정상적으로 작동하는지 테스트하기 위해 다른 terminal를 실행시켜 아래 코드를 실행시켜 봅니다.&lt;/p&gt;
&lt;pre id=&quot;code_1684559277276&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# request.py
... 생략 ...

PREDICT_API = &quot;http://0.0.0.0:3000/predict&quot;
ORI_IMG_PATH = './bus.jpeg'

data = subprocess.run(shlex.split(f&quot;curl -F 'fileobj=@{ORI_IMG_PATH};type=image/jpeg' {PREDICT_API}&quot;), stdout=subprocess.PIPE).stdout
dict = json.loads(data)  

def decode_image(input_img):
    output_img = np.frombuffer(base64.b64decode(input_img.encode('utf8')), np.uint8)
    output_img = cv2.imdecode(output_img, cv2.IMREAD_COLOR)
    
    return output_img

out_img = decode_image(dict['enc_out_img'])
cv2.imwrite('./recv_out_img.jpg', out_img)
for coord, cls, conf in zip(dict['coord'], dict['cls'], dict['conf']):
    print(f'bbox: {[int(x) for x in coord ]}, class: {int(cls)}, confidence: {conf:.2f}')&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;curl 을 통해 image를 predict endpoint에 보냄
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;predict endpoint인 &lt;code&gt;http://0.0.0.0:3000/predict&lt;/code&gt;에 image(bus.jpeg)를 post&lt;/li&gt;
&lt;li&gt;response받은 &lt;code&gt;dict['enc_out_img']&lt;/code&gt;은 encoded image이므로 decoded하여 저장하였음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1641&quot; data-origin-height=&quot;870&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qSrO1/btsgDMZX8Me/x4bIzPQipjifZarXgSYWAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qSrO1/btsgDMZX8Me/x4bIzPQipjifZarXgSYWAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qSrO1/btsgDMZX8Me/x4bIzPQipjifZarXgSYWAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqSrO1%2FbtsgDMZX8Me%2Fx4bIzPQipjifZarXgSYWAk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;322&quot; data-origin-width=&quot;1641&quot; data-origin-height=&quot;870&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 정상적으로 &lt;code&gt;predict&lt;/code&gt; api에 image가 post되어 YOLOv8s모델을 통해 detection 된 결과를 받을 수 있습니다. 왼쪽 log에는 detection result info을, 오른쪽 사진은 detection result image를 나타내었습니다. (Class 0은 person, class 5은 bus를 의미합니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 Building a Bento&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 테스트가 A라는 서버에서 정상적으로 이루어졌다고 가정하고 만약 해당 서비스를 B라는 서버에서 하고 싶다면 어떻게 해야 할까요? B에 가서 A와 똑같은 환경을 만들고 위의 과정을 반복해야 할까요? 이렇게 하는 것은 불필요한 작업 및 시간을 필요로 하고 에러도 발생 시킬 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이를 해결하기 위해 BentoML을 이용하여 service를 실행시키기 위한 모든 것을 dockerizing하게 됩니다. 구체적으로 &lt;b&gt;dockerizing은 model, service 파일, source code, service에 필요한 환경(PyTorch, Numpy 등등)을 모두 모아 놓는 것이기 때문에&lt;span style=&quot;color: #6164c6;&quot;&gt; Bento(도시락)&lt;/span&gt;를 만든다&lt;/b&gt;고도 말할 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q6pH8/btsgE6qa9MR/jIDFKKu4iM9zffvGHRrHCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q6pH8/btsgE6qa9MR/jIDFKKu4iM9zffvGHRrHCk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q6pH8/btsgE6qa9MR/jIDFKKu4iM9zffvGHRrHCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ6pH8%2FbtsgE6qa9MR%2FjIDFKKu4iM9zffvGHRrHCk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;221&quot; height=&quot;206&quot; data-origin-width=&quot;591&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1684591246021&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# bentofile.yaml
service: &quot;service:svc&quot;  # Same as the argument passed to `bentoml serve`
labels:
   owner: da2so
   stage: dev
include:
- &quot;*.py&quot;  # A pattern for matching which files to include in the bento
- &quot;*.yaml&quot;
exclude:
- &quot;*.pyc&quot;
python:
    packages:
    - torch==1.9.0+cu111
    - torchvision==0.10.0+cu111
    - PyYAML==6.0
    - loguru
    - pandas==1.5.2
    - Pillow==9.3.0
    - numpy==1.23.5
    - opencv-python==4.5.3.56
    - thop
    - py-cpuinfo
    - psutil
    - seaborn==0.12.2
    - tensorboard==2.8.0
    - pybboxes==0.1.6
    - tqdm
    extra_index_url:
    - &quot;https://download.pytorch.org/whl/cu111&quot;
docker:
    distro: debian
    python_version: &quot;3.8&quot;
    cuda_version: &quot;11.2.2&quot;
    setup_script: &quot;./setup.sh&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 yaml 파일을 통해 bento만드는 데 필요한 것을 모두 명시해야합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;service: &quot;service:svc&quot;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;서비스하고자 하는 service file:service class(&lt;code&gt;service:svc&lt;/code&gt;)을 명시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;labels&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;meta data를 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;include / exclude&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;include&lt;/code&gt;는 &lt;code&gt;bentofile.yaml&lt;/code&gt;가 존재하는 directory 위치에 있는 파일 들중 포함하고자 하는 파일을 의미하고 &lt;code&gt;exclude&lt;/code&gt;는 그 반대&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;python&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스에 필요한 python package를 명시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;docker&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;docker base image에 대한 내용으로 &lt;code&gt;debian&lt;/code&gt; os를 사용할 것이며 python, cuda version을 명시할 수 있음&lt;/li&gt;
&lt;li&gt;&lt;code&gt;setup_script&lt;/code&gt;에는 docker image에 setup되어야 하는 명령어들이 포함된 쉘 스크립트를 의미함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;bentoml build cli&lt;/code&gt; 입력하면 &lt;code&gt;benfile.yaml&lt;/code&gt;을 기반으로 bento를 만들어 줍니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;334&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Uba4H/btsgC3nqPFJ/tuSrPsTXj6rrV84Jvf7L61/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Uba4H/btsgC3nqPFJ/tuSrPsTXj6rrV84Jvf7L61/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Uba4H/btsgC3nqPFJ/tuSrPsTXj6rrV84Jvf7L61/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUba4H%2FbtsgC3nqPFJ%2FtuSrPsTXj6rrV84Jvf7L61%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;660&quot; height=&quot;145&quot; data-origin-width=&quot;1516&quot; data-origin-height=&quot;334&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Log를 보면 model store에 저장된 &lt;code&gt;yolov8s_model:ukgv3lhwxstqdibw&lt;/code&gt;를 load하여 packing하는 것을 알 수 있고 &lt;code&gt;service:svc&lt;/code&gt;에서 정의한 service 이름인 yolov8s_model을 기반으로 tag(&lt;code&gt;slops5xxc2yhdibw&lt;/code&gt;)도 생성되었음을 알 수 있습니다. 위의 과정을 통해 무엇이 생성되었는 지 알려드리기 위해 &lt;code&gt;~/bentoml/bentos/yolov8s_svc/slops5xxc2yhdibw/&lt;/code&gt; (~/bentoml/bentos/${SERVICE_NAME}/${SERVICE_TAG}) 디렉토리로 가봅니다.&amp;nbsp;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1970&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQKeQ8/btsgC3AY2qG/VaueGJnqjffF3mYc6UPbJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQKeQ8/btsgC3AY2qG/VaueGJnqjffF3mYc6UPbJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQKeQ8/btsgC3AY2qG/VaueGJnqjffF3mYc6UPbJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQKeQ8%2FbtsgC3AY2qG%2FVaueGJnqjffF3mYc6UPbJK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;187&quot; data-origin-width=&quot;1970&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 파일들이 docker build를 통해 생성되는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.4 Generating Docker Image from Bento&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 bento 파일들을 기반으로 최종적으로 docker image를 만들어봅니다. &lt;code&gt;DOCKER_BUILDKIT=0 bentoml containerize ${service_name}:${service_tag}&lt;/code&gt; 명령어를 사용합니다.&amp;nbsp;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;bentoml_contain.png&quot; data-origin-width=&quot;1745&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0uxew/btsgMaZQilY/1SHMHE0ecIjEKeva7VSgdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0uxew/btsgMaZQilY/1SHMHE0ecIjEKeva7VSgdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0uxew/btsgMaZQilY/1SHMHE0ecIjEKeva7VSgdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0uxew%2FbtsgMaZQilY%2F1SHMHE0ecIjEKeva7VSgdk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;573&quot; height=&quot;345&quot; data-filename=&quot;bentoml_contain.png&quot; data-origin-width=&quot;1745&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 완료되었으니 &lt;code&gt;docker images&lt;/code&gt; 명령어를 통해 생성된 docker image를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1990&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qXpb6/btsgUsezPPY/UIZzGARN3hI7aTnomPZ031/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qXpb6/btsgUsezPPY/UIZzGARN3hI7aTnomPZ031/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qXpb6/btsgUsezPPY/UIZzGARN3hI7aTnomPZ031/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqXpb6%2FbtsgUsezPPY%2FUIZzGARN3hI7aTnomPZ031%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;759&quot; height=&quot;30&quot; data-origin-width=&quot;1990&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 docker image로 container를 생성하여 container에서도 서비스가 정상적으로 작동되는 지 확인해 보겠습니다. 저는 &lt;code&gt;docker run -it --gpus &quot;device=0&quot; --ipc=host --name yolov8s_model -p 3000:3000 yolov8s_svc:slops5xxc2yhdibw&lt;/code&gt; 명령어로 container를 생성하였습니다.&amp;nbsp;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/odN02/btsgTi39hJm/13NQ4N0ctu2PMnj00htJJ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/odN02/btsgTi39hJm/13NQ4N0ctu2PMnj00htJJ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/odN02/btsgTi39hJm/13NQ4N0ctu2PMnj00htJJ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FodN02%2FbtsgTi39hJm%2F13NQ4N0ctu2PMnj00htJJ1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;699&quot; height=&quot;203&quot; data-origin-width=&quot;1912&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker image를 통해 서비스를 생성하니 전과 다르게 &lt;code&gt;[api_server:${number}]&lt;/code&gt; 부분과 &lt;code&gt;[runner:yolov8s_model:${number}]&lt;/code&gt; 부분이 추가되었습니다. api_server는 request를 받는 api server를 의미하고 그에 대한 ${number}는 몇번째 api server인지를 나타냅니다. 그리고 runner:yolov8s_model는 runner를 의미하고 그에 대한 ${number}는 몇번째 runner인지를 나타냅니다. 아래 사진은 api server가 3개인 경우와 runner가 1개인 경우의 서비스를 의미합니다. (다음 글에서는 서비스 성능 최적화를 위해 api server개수와 runner개수를 조절하는 방법을 알아보도록 할게요!)&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;673&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpfZLA/btsgEcYLuOd/l2sfcnJTycyhnacAWqRXik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpfZLA/btsgEcYLuOd/l2sfcnJTycyhnacAWqRXik/img.png&quot; data-alt=&quot;BentoML service architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpfZLA/btsgEcYLuOd/l2sfcnJTycyhnacAWqRXik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpfZLA%2FbtsgEcYLuOd%2Fl2sfcnJTycyhnacAWqRXik%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;324&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;673&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BentoML service architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3.2에서 만든 &lt;code&gt;request.py&lt;/code&gt;으로 테스트 다시 해보면 이전과 똑같이 inference api가 정상적으로 작동하는 것을 확인할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2383&quot; data-origin-height=&quot;1352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ldn9Y/btsgJZK4d5Q/Mogtv8iI4nRMfP4yOCKXAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ldn9Y/btsgJZK4d5Q/Mogtv8iI4nRMfP4yOCKXAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ldn9Y/btsgJZK4d5Q/Mogtv8iI4nRMfP4yOCKXAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fldn9Y%2FbtsgJZK4d5Q%2FMogtv8iI4nRMfP4yOCKXAK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;815&quot; height=&quot;462&quot; data-origin-width=&quot;2383&quot; data-origin-height=&quot;1352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스가 실행되는 container(request 받는 side)의 log를 보면 &lt;code&gt;api_server:48&lt;/code&gt;를 통해 정상적으로 request받아서&amp;nbsp; &lt;code&gt;runner:yolov8s_model:1&lt;/code&gt;으로 inference진행완료한 것을 알 수 있습니다!&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/MLOps</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/80</guid>
      <comments>https://da2so.tistory.com/80#entry80comment</comments>
      <pubDate>Mon, 22 May 2023 08:32:54 +0900</pubDate>
    </item>
    <item>
      <title>LoRA: Low-Rank Adaptation of Large Language Models 논문 리뷰</title>
      <link>https://da2so.tistory.com/79</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Microsoft에서 나온 논문인 LoRA를 오늘 리뷰해 봅니다. &lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;LoRA&lt;/span&gt;는 GPT와 같은 Large Language Models(LLM)을 특정 task에 fine-tuning(adaptation)하는 데 있어서 time, resource cost가 너무 크다는 단점을  해결하기 위한 방법&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Introduction&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LLM은 기본적으로 pre-trained model로부터 특정 task(e.g. summarization, question and answering, ...)에 adaptation하기 위해 fine-tuning을 해야 합니다. Fine-tuning을 하면서 LLM모델의 weight parameters를 모두 다시 학습하게 되는데 이게&amp;nbsp;&lt;b&gt;엄청난 cost!!&lt;/b&gt;입니다. 예를 들어 GPT-2(or 3), RoBERTa large모델의 경우 fine-tuning만 몇 달이 걸리게 됩니다.&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;그래서 이를 해결하기 위해 해당 논문에서는 Low-Rank Adaptation(LoRA)를 제안하게 됩니다. 이름에서 유추가능하듯이 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;LoRA는 Low-Rank 방법을 이용&lt;/b&gt;&lt;/span&gt;하여 time, resource cost를 줄이게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Low-Rank 방법을 사용하게 된 motivation 및 basis&lt;/b&gt;는 &quot;&lt;i&gt;Measuring the Intrinsic Dimension &lt;/i&gt;&lt;i&gt;of Objective Landscapes&quot; &lt;/i&gt;논문과 &quot;&lt;i&gt;Intrinsic Dimensionality Explains the &lt;/i&gt;&lt;i&gt;Effectiveness of Language Model Fine-Tuning.&quot;&amp;nbsp;&lt;/i&gt;논문에서 말하길 &quot;over-parameterized model은 low intrinsic dimension으로 존재하고 있다&quot;라는 사실에서 기반하고 있습니다. 그래서 저자들은 model adaptation동안의 weight change에도 low intrinsic rank를 가질 거라고 가정하게 되고 Low-Rank 방법을 사용하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;LoRA는 기존 pre-trained weights는 frozen해두고 몇 개의 dense(fc) layers만 학습하는 것인데 이때 학습방법이 dense layer의 weight을 low rank로 decomposition한 matrices만을 optimization하는 것&lt;/b&gt;&lt;/span&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;lora1.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;474&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/djg3BY/btsgbIW06Oy/84lBLwcH7L50AK3omXiy51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/djg3BY/btsgbIW06Oy/84lBLwcH7L50AK3omXiy51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/djg3BY/btsgbIW06Oy/84lBLwcH7L50AK3omXiy51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdjg3BY%2FbtsgbIW06Oy%2F84lBLwcH7L50AK3omXiy51%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;334&quot; height=&quot;241&quot; data-filename=&quot;lora1.png&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;474&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위 Figure 1과 같이 fine-tuning시에 pre-trained weights \( W \)는 frozen해두고 low rank decomposition된 weights \(A\), \(B\)만 학습하고 \( W \)에 summation하게 됩니다. Low rank로 decomposition된 weights는 당연하게도 기존 \(W\)보다 훨씬 작은 크기의 weight이기 때문에 time, resource cost를 줄일 수 있게 됩니다.  또한 pre-trained model을 가지고 있는 상태에서 특정 task에 adaptation하기 위해서 \(A\)와 \(B\)만 storage에 저장하면 되고 다른 task에 adaptation하기 위해 또 다른 \(A'\), \(B'\)만 갈아 끼우면 되기 때문에 &lt;b&gt;storage, task switching면에서 매우 효율&lt;/b&gt;적입니다. 추가적으로&lt;b&gt; inference시에도 fine-tuned model의 latency성능이 낮아지지도 않습니다&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 Terminologies and Conventions&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( d_{model} \): Transformer의 input, output dimension size&amp;nbsp;&lt;/li&gt;
&lt;li&gt;\( W_q, W_k, W_v, W_o \): Self-attention moduel의 query/key/value/output projection matrices&amp;nbsp;&lt;/li&gt;
&lt;li&gt;\(W \) or \(W_0 \): Pre-trained weight&lt;/li&gt;
&lt;li&gt;\( \Delta W \): Adaptation동안의 accumulated gradient update&lt;/li&gt;
&lt;li&gt;\( r \): LoRA module의 rank&lt;/li&gt;
&lt;li&gt;Model optimization방법으로 Adam을 사용&lt;/li&gt;
&lt;li&gt;Transformer MLP feedforward dimension: \(d_{ffn} = 4 \times d_{model} \)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Problem Statement&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;LoRA방법은 training objective에 상관없이 모두 사용가능(agnostic)하지만 해당 논문에서는 LLM에 focus맞추어 설명&lt;/b&gt;합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( \Phi \)로 parameterized되어 있는 pre-trained language model \( P_{\Phi} (y| x) \)가 주어졌다고 가정합니다. &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\( P_{\Phi} (y| x) \)는 GPT와 같은 generic한 multi-task learner입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;해당 pre-trained language model을 downstream text generation task에 adaptation 하는 상황을 생각해봅시다. Downstream task의 예시로는 summarization, natural language to SQL (NL2SQL) 등이 있습니다. Adaptation을 위해 각 downstream task은 context-target pair의 training dataset \( Z = \{( x_i , y_i)&amp;nbsp; \}_{i=1, \ldots, N} \)을 가집니다. ( \(x_i \)와 \(y_i \)는 token sequences) NL2SQL task의 경우 \(x_i \)은 natural language query이고 \( y_i \)은 그에 대한 SQL command일 것이고 summarization task의 경우에는 \(x_i \)은 article 내용이고 \(y_i \)는 그에 대한 요약내용이겠죠.&amp;nbsp;&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;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;기존의 full fine-tuning이라면 model은 pre-trained weights \( \Phi_0 \)으로 initialized될 것이고 아래와 같은 conditional language modeling objective를 minimize하기위해 \( \Phi_0 + \Delta \Phi \)을 update 합니다:&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;\[&lt;br /&gt;&amp;nbsp;max_{\Phi} \sum_{ (x,y) \in Z} \sum^{ |y|}_{t=1} log(P_{\Phi} (y_t | x, y_{ &amp;lt; t} ))\quad \cdots Eq. (1)&lt;br /&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;위의 full fine-tuning을 사용할 경우에 &quot;각&quot; downstream task를 위해 \( | \Phi_0 | \) dimension과 같은 크기의 \( | \Delta \Phi |\)을 매번 재학습해야 한다는 문제점을 가집니다. GPT-3와 같이 1,750억개의 weights를 가진 pre-trained model을 사용하게 되면 엄청난 cost가 들것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기위해 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;LoRA는 update해야하는 parameter를 \( \Delta \Phi = \Delta \Phi ( \Theta ) \)와 같이 encode하여 훨씬 작은 size의 parameter \( \Theta \)로 대체 학습하는 것&lt;/b&gt;&lt;/span&gt;입니다( \( | \Theta | \ll | \Phi_0 |&amp;nbsp; \) ). 그래서 최적의 \( \Delta \Phi \)를 찾는 task는 \( \Theta \)를 optimization하는 것으로 대체됩니다:&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;\[&lt;br /&gt;&amp;nbsp;max_{\Phi} \sum_{ (x,y) \in Z} \sum^{ |y|}_{t=1} log(P_{\Phi_0 + \Delta \Phi ( \Theta ) } (y_t | x, y_{ &amp;lt; t} ))\quad \cdots Eq. (2)&lt;br /&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;위와 같은 LoRA방식으로 GPT-3을 fine-tuning할 경우 기존 full fine-tuning보다 학습해야 할 parameter수가 전체의 0.01%로 줄어듭니다. 아래 section에서는 LoRA방법에서 정확히 어떻게 \( \Theta \)가 표현되는지 얼마나 작은 size로 encode되는 지 알아보도록 하죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Our method&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Low-Rank Parameterized Update&amp;nbsp; Matrices&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리마인드하면 LoRA는 adaptation 동안에 low intrinsic rank를 가진 weight로 update하는 방법입니다. 수학적으로 pre-trained weight matrix \( W_0 \in \mathbb{R}^{d \times k } \) 에 대해 \( W_0 + \Delta W = W_0 + BA \)로 update하는 것입니다. 즉, &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\( W_0 \)은 frozen되고&lt;span&gt; l&lt;/span&gt;&lt;/span&gt;ow rank로 decomposition된 \(B \in \mathbb{R}^{d \times r} \)와 \( A \in \mathbb{R}^{r \times k} \)&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;만을 학습하는 것입니다(rank \(r \ll min(d,k) \)을 만족함 ).&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;그리고 \(W_0 \)와 \( \Delta W = BA \)는 같은 input에 곱해지고 그들의 output vector는 coordinate-wise하게 합(summation)해집니다. 이에 대해 forward pass를 표현하면 다음과 같습니다:&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;\[&lt;br /&gt;&amp;nbsp;h = W_0 x + \Delta W x = W_0 x + BAx \quad \cdots Eq. (3)&lt;br /&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;\(A\)는 random Gaussian initialization되고 \(B\)는 0으로 initialization됩니다. 그래서 training 시작 시에 \( \Delta W = BA \)또한 0입니다. 그리고 \( \Delta W x\)는 \( \frac{ \alpha}{r} \)으로 scaling됩니다. Adam으로 optimization 할 때 \( \alpha \)를 tuning하는 것은 learning rate를 tuning하는 것과 같이 하였습니다. 그래서 \( \alpha \)을 처음 \( r\)값으로 정하였다고 합니다. Scaling은 \(r\)값을 변화 시킬때 hyperparameter를 재조정할 필요를 줄이는 데 도움이 됩니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;824&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcB9lb/btsgdyl9Nen/JaUboDjpgDivaCASztpk0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcB9lb/btsgdyl9Nen/JaUboDjpgDivaCASztpk0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcB9lb/btsgdyl9Nen/JaUboDjpgDivaCASztpk0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcB9lb%2Fbtsgdyl9Nen%2FJaUboDjpgDivaCASztpk0K%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;623&quot; height=&quot;434&quot; data-origin-width=&quot;1183&quot; data-origin-height=&quot;824&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 실제 LoRA코드를 snippet한 것인데 위에 설명드린 수식과 내용과 일치하는 것을 알 수 있습니다. (&lt;span style=&quot;color: #009a87;&quot;&gt;코드에서 확인해보니 \(r, \alpha \)값은 보통 (8, 16) 또는 (16, 32)을 사용하였습니다.&lt;/span&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.1.1 No additional Inference Latency&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LoRA를 사용하여 inference하려고 할 때는 기존 pre-trained weight \(W_0\)에 학습한 \(BA\)를 더해주고 사용하면 되기 때문에&amp;nbsp; infernece latency성능 하락은 전혀 없습니다. 그리고 \(W_0\)을 기반으로 또 다른 task로 학습한 \(B'A'\)가 있을 경우 \(BA\)을 빼주고 \(B'A'\)을 더해주어 사용하면 되기 때문에 reusability이 좋습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Applying LoRA to Transformer&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문에서는 trainable weight를 최소화하기위해 LoRA를 모든 layer 및 module에 적용하지않습니다. &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;오직 LoRA를 Transformer의 attention weights인 \(W_q\)또는&amp;nbsp; \(W_k \), \(W_v\)에만 적용하였고 나머지 MLP module에는 적용하지 않았습니다&lt;/b&gt;&lt;/span&gt;. (실제 성능 실험에서는 \(W_q \)와 \(W_v\)에만 LoRA적용하였습니다.) 이렇게 셋팅하고 진행함으로써 1,750억개의 parameter를 가진 GPT-3에 대해 fine-tuning시에 원래 VRAM를 1.2TB사용하던 것이 LoRA를 통해 350GB로 줄어들었습니다. 또한 training speed또한 25%가량 줄었다고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Experiment Results&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1631&quot; data-origin-height=&quot;1122&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UUpyJ/btsf5fu9a2C/3EUh6ZJb21lQ6RnT5EpBY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UUpyJ/btsf5fu9a2C/3EUh6ZJb21lQ6RnT5EpBY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UUpyJ/btsf5fu9a2C/3EUh6ZJb21lQ6RnT5EpBY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUUpyJ%2Fbtsf5fu9a2C%2F3EUh6ZJb21lQ6RnT5EpBY1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;597&quot; height=&quot;411&quot; data-origin-width=&quot;1631&quot; data-origin-height=&quot;1122&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPT-2기준 성능비교 시 기존 방법들보다 trainable weight도 적으며 다양한 데이터셋에 대해 성능도 잘 나온것을 확인가능합니다. (실험에 대한 더 많은 내용은 논문 참조부탁드립니다. ㅠ)&lt;/p&gt;</description>
      <category>AI paper review</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/79</guid>
      <comments>https://da2so.tistory.com/79#entry79comment</comments>
      <pubDate>Tue, 16 May 2023 18:23:15 +0900</pubDate>
    </item>
    <item>
      <title>Segment Anything 논문 리뷰</title>
      <link>https://da2so.tistory.com/78</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 Meta AI의 Segment Anything논문을 리뷰합니다. 논문 이름이 목적과 내용을 뜻하는 논문 이네요. 아래 사진과 같이 어떤 이미지든(zero-shot) segment할 수 있다는 것을 의미합니다. &lt;b&gt;Zero-shot transfer이 가능하며 어떤 task에도 generalization될 수 있다는 점에서 ChatGPT와 같이 이러한 모델을 foundation model이라합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;sam.png&quot; data-origin-width=&quot;1637&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qVMI6/btr8zmfX99A/QZEAxzkNat9BEocpxObe11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qVMI6/btr8zmfX99A/QZEAxzkNat9BEocpxObe11/img.png&quot; data-alt=&quot;Segment Anything은 원하는 모든 object를 segmentation 해줌...&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qVMI6/btr8zmfX99A/QZEAxzkNat9BEocpxObe11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqVMI6%2Fbtr8zmfX99A%2FQZEAxzkNat9BEocpxObe11%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;659&quot; height=&quot;321&quot; data-filename=&quot;sam.png&quot; data-origin-width=&quot;1637&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Segment Anything은 원하는 모든 object를 segmentation 해줌...&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Introduction&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ChatGPT와 같은 Large Language Models (LLM)은 &lt;b&gt;(1)&lt;/b&gt; zero-shot generalization이 뛰어나고 &lt;b&gt;(2)&lt;/b&gt; hand-crafted 질문 text을 입력으로 아주 적절한 대답(response)을 즉각적으로 출력할 수 있는 prompt engineering이 가능합니다.&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이러한 모델을&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;foundation model&lt;/b&gt;&lt;/span&gt;이라고 지칭&lt;/li&gt;
&lt;li&gt;전체적인 학습 flow는 web-scale의 dataset으로 pre-trained하고 특정 task(e.g. QA, &lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;translation)에 맞춰 fine-tuning&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;해당 논문의 목표:&lt;/b&gt;&lt;/span&gt;&lt;b&gt; LLM과 비슷한 학습 방법을 사용해서 image segmentation용 foundation model을 만들어 보자!&lt;/b&gt;&amp;nbsp;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;하지만... 다음과 같은 문제가 있음&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;어떤 &lt;b&gt;task&lt;/b&gt;가 zero-shot generalization을 가능하게 할까?&lt;span style=&quot;background-color: #ffffff; color: #333333; text-align: left;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;어떤 &lt;b&gt;model&lt;/b&gt; architecture을 사용해야할까?&lt;/li&gt;
&lt;li&gt;어떤 &lt;b&gt;data&lt;/b&gt;가 해당 task와 model에 적합할까?&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 문제를 해결하기 위해 논문에서 해당 방법론 제안&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. Task&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LLM에서 사용하는 방식과 비슷하게 prompting technique을 기반으로 &lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;promptable segmentation task&lt;/span&gt;&lt;/b&gt; 제안&lt;/li&gt;
&lt;li&gt;목표는 어떠한 형태의 segmentation prompt가 주어져도 valid한 segmentation mask(result)을 출력하도록 하는 것&lt;/li&gt;
&lt;li&gt;Segmentation prompt는 아래 사진과 같이 segment할 image object에 대한 spatial 또는 text information이 기만 하면 됨
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;아래 사진에서 가능한 segmentation prompt 종류가 명시됨: points, boxes, segment mask, text&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;심지어 해당 prompt가 애매모호(ambiguity)하거나 여러 object를 지칭해도 됨
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예를 들어 prompt 중 한종류인 point label이 shirt를 가리키고 있을 때 실제 의도는 shirt그 자체일 수도 있지만 shirt를 입고있는 사람이 될 수 도 있음&amp;nbsp;&lt;/li&gt;
&lt;li&gt;해당 prompt가 여러 object를 지칭해도 model의 output은 반드시 여러 object중 하나의 reasonable mask를 뽑아내도록 학습할 것임&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mX7Fc/btr8JlAXdc3/XhEQk8X0pGKaNek1nFxPn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mX7Fc/btr8JlAXdc3/XhEQk8X0pGKaNek1nFxPn0/img.png&quot; data-alt=&quot;promptable segmentation&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mX7Fc/btr8JlAXdc3/XhEQk8X0pGKaNek1nFxPn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmX7Fc%2Fbtr8JlAXdc3%2FXhEQk8X0pGKaNek1nFxPn0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;260&quot; height=&quot;216&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;promptable segmentation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;2. Model&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다음과 같은 3가지 constraints를 만족하는 model을 만들어야 함
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;i&gt;Flexible prompts&lt;/i&gt;를 지원&lt;/li&gt;
&lt;li&gt;&lt;i&gt;interactive하게, real-time&lt;/i&gt;으로 segmentation mask를 compute 가능&lt;/li&gt;
&lt;li&gt;(prompt에 대한) &lt;i&gt;ambiguity-aware&lt;/i&gt;한 특성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;위 constraint을 만족시키는 모델 &lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;Segmen Anything Model (SAM)&lt;/span&gt;&lt;/b&gt; 제안
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;SAM은 image encoder, prompt encoder, mask decoder로 구성 됨
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Image encoder:&lt;/b&gt; image을 입력으로 image embedding 출력&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Prompt encoder&lt;/b&gt;: prompt를 입력으로 prompt embedding 출력 (flexible!)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Mask decoder&lt;/b&gt;: 위의 두 embedding값을 입력으로 segmentation mask 출력 (fast!)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Input image는 같고 prompt가 다를 경우 image embedding reuse가능&lt;/li&gt;
&lt;li&gt;Prompt는 point, box, mask, text를 받을 수 있도록 flexible하게 구성&lt;/li&gt;
&lt;li&gt;하나의 prompt입력에 대해 여러 segmentation mask를 출력할 수 있도록 하여 ambiguity-aware특성 만족&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;730&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAjJDX/btr8IvRJClf/0BpJ0eLMQmHYS8zr0r67yk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAjJDX/btr8IvRJClf/0BpJ0eLMQmHYS8zr0r67yk/img.png&quot; data-alt=&quot;Segment Anything Model (SAM)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAjJDX/btr8IvRJClf/0BpJ0eLMQmHYS8zr0r67yk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAjJDX%2Fbtr8IvRJClf%2F0BpJ0eLMQmHYS8zr0r67yk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;317&quot; height=&quot;258&quot; data-origin-width=&quot;896&quot; data-origin-height=&quot;730&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Segment Anything Model (SAM)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. Data engine&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;SAM model이 strong generalization을 얻기 위해서는 방대한 dataset이 필요함&lt;/li&gt;
&lt;li&gt;이를 위해 &lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;data engine&lt;/span&gt;&lt;/b&gt;을 구축
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Model-in-the-loop dataset annotation을 사용하는 방식&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Data engine은 총 3가지 strategy로 구성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Assisted-manual&lt;/b&gt;: 기존의 annotation task와 비슷하게 SAM이 annotator를 assist하는 형식&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Semi-automatic&lt;/b&gt;: prompting하여  선택된 objects들 중 subset만 SAM이 automatic하게 mask를 생성해주고 나머지는 annotator가 진행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fully-automatic&lt;/b&gt;: foreground points들의 regular grid prompt를 입력으로 SAM이 이미지당 100개까지의 high-quality mask를 생성 (아래 왼쪽 그림 참조)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1737&quot; data-origin-height=&quot;797&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blG2T3/btr8JkPIXNo/skdTmdThNfCySKnswer4O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blG2T3/btr8JkPIXNo/skdTmdThNfCySKnswer4O0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blG2T3/btr8JkPIXNo/skdTmdThNfCySKnswer4O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblG2T3%2Fbtr8JkPIXNo%2FskdTmdThNfCySKnswer4O0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;289&quot; data-origin-width=&quot;1737&quot; data-origin-height=&quot;797&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;4. Dataset&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Data engine의 fully automatic strategy로 생성된 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;최종 dataset이 SA-1B&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1B masks와 11M의 licensed, privacy-preserving images로 구성됨&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;980&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FqTi2/btr8A4VlaJp/1XDkFloaAAUvKNPaoV9j3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FqTi2/btr8A4VlaJp/1XDkFloaAAUvKNPaoV9j3k/img.png&quot; data-alt=&quot;SA-1B dataset examples&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FqTi2/btr8A4VlaJp/1XDkFloaAAUvKNPaoV9j3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFqTi2%2Fbtr8A4VlaJp%2F1XDkFloaAAUvKNPaoV9j3k%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;379&quot; data-origin-width=&quot;1452&quot; data-origin-height=&quot;980&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;SA-1B dataset examples&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Segment Anything Task&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Task&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;Segment Anything Task인 &lt;/span&gt;promptable segmentation task에 대해 더 자세히 설명드리겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본 논문에서는 &lt;b&gt;&lt;i&gt;promptable segmentation task을 주어진 어떠한 prompt라도 valid한 segmenation mask를 출력&lt;/i&gt;&lt;/b&gt;하는 것으로 정의합니다.&amp;nbsp; 여기서 'valid'의 정의는 ambiguous(모호한)해도 되고 여러 물체를 가리키고 있어도 괜찮습니다. 다만 반드시 여러 물체 중 하나를 꼭 가리키는 segmentation mask이어야합니다. 이렇게 task를 정의한 이유는 이 방법이 natural pre-training algorithnm이며 prompt를 이용한 zero-shot transfer하기에 가장 general한 방법이기 때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Pre-training&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pre-training은 image와 prompts(points, boxes, masks)을 입력으로 하여 나온 model output인 predicted segmentation mask와 ground truth의 차이를 최소화하도록 학습합니다. 여기서의 주된 목적은 prompt가 ambiguous해도 어떤 prompt에 대해서도 valid한 mask를 prediction하도록 하는 것이 목적입니다.  그래서 model의 prediction이 ambiguity를 포함하게 되는데 이게 user가 사용하기에 효과적이고 data engine의 automatic annotation에도 flexible하게 사용가능하게 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 Zero-shot transfer&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떠한 prompt에도 적절하게 pre-training되기 때문에 특정 task에 zero-transfer하기 용이합니다. 예를 들어 '고양이'를 detect하는 bounding box detector를 한 유저가 가지고 있는 상태에서 '고양이'를 segmentation하고 싶다면 SAM모델에 bounding box output을 prompt로 주는 방식으로 해결할 수 있습니다. (SAM은 bounding box를 prompt로 입력받아 pre-training되어있기 떄문입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Segment Anything Model (SAM)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Promptable segmentation task을 위한 SAM모델은 (1) Image encoder, (2) flexible prompt encoder, (3) fast mask decoder 로 이루어져 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;492&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blFfrE/btr8Mr2YhDK/dJDxW55R7sP5bmoo8hKXQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blFfrE/btr8Mr2YhDK/dJDxW55R7sP5bmoo8hKXQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blFfrE/btr8Mr2YhDK/dJDxW55R7sP5bmoo8hKXQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FblFfrE%2Fbtr8Mr2YhDK%2FdJDxW55R7sP5bmoo8hKXQk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;821&quot; height=&quot;253&quot; data-origin-width=&quot;1598&quot; data-origin-height=&quot;492&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Image Encoder&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pre-trained Vision Transfomer(ViT)의 하나인 &lt;b&gt;Masked autoencoders (MAE)&lt;/b&gt;의 enocder 사용하여 image encoder를 구성하였습니다. MAE는 high-resolution Input을 process하기위해 적용되었습니다. Image에 대해 여러 prompt가 존재한다면 Image encoder는 image당 한번만 실행됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1420&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kR2uQ/btr8OVoVdS0/HrVYh5WlkA6jL1yysgSsUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kR2uQ/btr8OVoVdS0/HrVYh5WlkA6jL1yysgSsUk/img.png&quot; data-alt=&quot;MAE architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kR2uQ/btr8OVoVdS0/HrVYh5WlkA6jL1yysgSsUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkR2uQ%2Fbtr8OVoVdS0%2FHrVYh5WlkA6jL1yysgSsUk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;423&quot; height=&quot;405&quot; data-origin-width=&quot;1482&quot; data-origin-height=&quot;1420&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MAE architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Prompt encoder&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Prompt를 (1) &lt;b&gt;sparse&lt;/b&gt;(points, boxes, text)와 &lt;b&gt;dense&lt;/b&gt;(mask)으로 나누어 정의하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Sparse set에 해당하는  points와 boxes 는 learned embeddings으로 합산된 positional encodings을 사용하여 표현하고 text는 CLIP으로부터 상용화된 text encoder로 text를 표현합니다. Dense set은 convolutions과&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;image embedding(from image encoder)과 함께&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;summed element-wise으로 embedding됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 Mask decoder&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mask decoder의 역할은 image embedding, prompt embedding과 output token을 효과적으로 segmentation mask에 mapping하는 것입니다. Mask decoder는 &lt;b&gt;transformer decoder block과 dynamic mask prediction head를 사용&lt;/b&gt;하였습니다. 해당 decoder block은 전체 embedding을 update하기위해 prompt self-attention과 cross-attention을 2가지 방향으로 사용하였습니다. ([1] prompt-to-image, [2] image-to-prompt embeddings) 아래와 그림과 같이 2개의 blocks이후에는 image embedding을 upsampling하고 MLP는 output token을 dynamic linear classifier에 mapping하게 됩니다. 그리고 각 image location에 대해 mask foreground probability를 계산합니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zRXhT/btr8KIRXq7b/Bt1lnsqAtgVmZA8oXriDEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zRXhT/btr8KIRXq7b/Bt1lnsqAtgVmZA8oXriDEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zRXhT/btr8KIRXq7b/Bt1lnsqAtgVmZA8oXriDEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzRXhT%2Fbtr8KIRXq7b%2FBt1lnsqAtgVmZA8oXriDEK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;402&quot; height=&quot;323&quot; data-origin-width=&quot;794&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.4 Resolving ambiguity&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SAM은 하나의 prompt에 대해 여러개의 output masks를 predict하도록 합니다. 아래 사진과 같이 3개의 mask outputs이면 대부분 cases을 처리할 수 있다는 것을 발견하여 &lt;b&gt;한 prompt에 대해 총 3개의 output masks를 예측&lt;/b&gt;하도록 학습하였습니다. Training시에 masks에 대해 minimum loss만 backprop하였다고 합니다. 그리고 3개의 masks에 대해 rank를 매기기 위해 confidence score(estimated IOU)를 예측하도록 하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;948&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cu3LSX/btr8UWA37pf/KFaWyLv7OQKQOpCLjGfQ71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cu3LSX/btr8UWA37pf/KFaWyLv7OQKQOpCLjGfQ71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cu3LSX/btr8UWA37pf/KFaWyLv7OQKQOpCLjGfQ71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcu3LSX%2Fbtr8UWA37pf%2FKFaWyLv7OQKQOpCLjGfQ71%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;340&quot; height=&quot;412&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;948&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.5 Losses and training&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Focal loss와 Dice loss의 linear comibation으로 mask prediction에 대해 supervise learning진행하였습니다. 그리고 여러 geometric prompts를 섞어서 training 진행하여 단일로 prompt를 사용했을 때보다 robust하게 학습되도록 하였습니다. (text prompt사용 시에만인듯 합니다?) Mask마다 총 11번의 random sampling prompts를 진행하여 data engine에 SAM 모델이 integrate되도록 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Segment&amp;nbsp;Anything&amp;nbsp;Data&amp;nbsp;Engine&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data engine은 3가지 stage로 구성: (1) model-assisted manual annotation stage, (2) semi-automatic stage, (3) fully automatic stage 되어있습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1&amp;nbsp; &amp;nbsp;Assisted-mamanual stage&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전문적인 annotator들이 SAM을 기반한 browser-based segmentation tool을 통해 foreground/ background object를 클릭해가며 labeled mask를 만드는 stage입니다.&amp;nbsp;(노가다ㅠ..) Labeling시에 object의 semantic constraints를 따로 두지않았다고 하며 stuff, things에 대해 자유롭게 labeling하도록 하였다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 stage에 사용되는 SAM은 public segmentation datasets으로 학습되었으며 어느정도 충분한 data가 더 쌓이면 SAM을 retrain하였다고 합니다. 그리고 더 많이 쌓였다면 image encoder를 ViT-B에서 더 큰 모델인 ViT-H로 변경하였다고 합니다. 총 6번의 retraining작업을 진행하였습니다. (retraining이 진행됨에 따라 annotation속도가 빨라진다고 하네요) 이 stage에서 총 120k images에 대해 4.3M masks를 얻었다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.2 Semi-automatic stage&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 단계에서는 masks의 다양성을 높여 SAM의 성능을 향상시키는 데 목표합니다. Annotator들에게 SAM으로부터 어느정도 masks labeling이 되어있는 이미지를 주고 annotate되지않은 부분을 추가적으로 annotate하도록 합니다. 즉, 덜 중요한 object들에 대해 초점을 더 맞춘것입니다. Confident mask를 detect하기 위해 첫 번째 stage에서 얻은 masks에 대해 generic object category를 이용하여 detect하도록 bounding box detector(Faster R-CNN)를 학습하였습니다. 해당 stage에서는 추가적으로 180k images에 대해 5.9M masks를 만들었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.3 Fully&amp;nbsp;automatic&amp;nbsp;stage&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서부터는 annotator들이 없습니다. 이전 2개의 stage을 통해 충분히 다양한 masks을 모아서 모델의 성능을 향상시켰고 여기서는 ambiguity-aware model을 만듭니다. 즉, 애매모호한 prompt가 입력으로 들어와도 납득할만한 masks를 출력하도록 합니다. 32x32의 regular grid에 point prompt을 입력으로 넣어 각 point에 대해 valid object를 segment하도록 합니다. 예를 들어, 한 point가 물체의 part 또는 subpart에 놓여있다면 SAM은 subpart, part, 전체 object에 대한 masks를 출력하도록 하는 것입니다. 그리고 IOU prediction module을 사용하여 confident(stable) masks만 선택되도록 합니다. (0.5-threshold부터 0.5+threshold값안에 속하는 것만 stable하다고 정의) 마지막으로 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;non-maximal suppression (NMS)을 통해 duplicate된 mask결과를 filtering합니다. 작은 masks를 찾기 위해 여러개의 overlapping zoomed-in image crop방식도 사용했다고 합니다. 이렇게 하여 총 11M image에 대해 1.1B masks를 생성해내었다고 합니다. (이 결과가 그대로 SA-1B dataset이 된것은 아닙니다!)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. Segment&amp;nbsp;Anything&amp;nbsp;Dataset&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.1 Images&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1,100만개의 image는 license가 있다고 하며 high-resolution(평균 3300x4950사이즈)이라고 합니다. 해당 이미지들을 SA-1B dataset으로 release할때는 1500 pixels까지 downsampling하였다고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.2 Masks&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;11억개의 masks를 만들었으며 그중 99.1%가 automatic하게 생성된 것이라 합니다. Automatic하게 생성된 masks가 사람이 annotate한 결과와 별반 다르지 않았다고 하여 SA-1B dataset은 오직 automatic하게 생성된 masks로만 구성되어있다고 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.3 Mask properties&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진은 SA-1B dataset과 다른 segmentation dataset간의 object center의 spatial distribution을 나타낸것입니다. SA-1B가 다른 dataset에 비해 image corner의 coverage가 뛰어난 것을 알 수 있습니다. 특히 COCO나 Open Images dataset은 중앙에 편향된 masks만 가진것을 볼 수 있습니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;274&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cp5LD3/btr8LKIzsH7/VtsQlSPdRmdXnixwTksPb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cp5LD3/btr8LKIzsH7/VtsQlSPdRmdXnixwTksPb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cp5LD3/btr8LKIzsH7/VtsQlSPdRmdXnixwTksPb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcp5LD3%2Fbtr8LKIzsH7%2FVtsQlSPdRmdXnixwTksPb0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;411&quot; height=&quot;146&quot; data-origin-width=&quot;770&quot; data-origin-height=&quot;274&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 Fig 6의 legend에서는 다른 dataset들간의 크기를 비교하였습니다. SA-1B가 2번째로 큰 Open Images dataset보다 11배 많은 image, 400배 많은 masks를 가진다고 합니다. Fig 6 왼쪽에서는 image당 mask의 distribution을 비교하였습니다. SA-1B가 다른 dataset들에 비해 한 image에 더 많은 mask label이 있는 것을 알 수 있습니다. Fig 6 중앙에서는 SA-1B가 masks의 개수가 많기 때문에 다른 dataset들에 비해 small, midium size의 masks가 많습니다. 마지막으로 Fig 6 오른쪽에서는 masks shape의 complexity를 측정한 표입니다. Complexity를 측정하기 위해 mask concavity(오목함)를 측정하였고 SA-1B dataset에는 작은 masks가 많지만 다른 dataset들과 비슷하게 concavity을 가진다는 것을 알 수 있습니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1626&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eObmLI/btr8Jp6uukf/GNSCkGIBp2zKakVKWwHCJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eObmLI/btr8Jp6uukf/GNSCkGIBp2zKakVKWwHCJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eObmLI/btr8Jp6uukf/GNSCkGIBp2zKakVKWwHCJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeObmLI%2Fbtr8Jp6uukf%2FGNSCkGIBp2zKakVKWwHCJK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;174&quot; data-origin-width=&quot;1626&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI paper review</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/78</guid>
      <comments>https://da2so.tistory.com/78#entry78comment</comments>
      <pubDate>Fri, 7 Apr 2023 15:26:40 +0900</pubDate>
    </item>
    <item>
      <title>GPT-1: Improving Language Understanding by Generative Pre-Training 논문 리뷰</title>
      <link>https://da2so.tistory.com/76</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 OpenAI의 &lt;a href=&quot;https://s3-us-west-2.amazonaws.com/openai-assets/research-covers/language-unsupervised/language_understanding_paper.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;GPT-1 논문&lt;/a&gt;을 리뷰하겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Introduction&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Natural Language Processing (NLP)를 포함한 대부분의 deep learning methods는 supervised learning을 통해 뛰어난 성능을 내는 모델을 만들었습니다. 뛰어난 성능을 내려면 기본적으로 많은 양의 labeled data을 필요로 합니다. 하지만 현실적으로 labeling은 사람이 하다 보니 정말~~ 많은 양의 labeled data는 구하기 힘들게 됩니다. 그에 비해 unlabeled data는 엄청 많습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서,&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt; GPT-1은 수많은 unlabeled data로 unsupervised pre-training을 하고 labeled data로 supervised fine-tuning을 진행하는 semi-supervised방법을 사용&lt;/b&gt;&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;그럼 &lt;b&gt;학습하는 데 unlabeled data 쓰는 게 어려움이 없냐??&lt;/b&gt; 당연히 있습니다. Unlabeled data는 word-level information이상으로 활용하기가 어렵습니다.(성능 향상에 쓰이기 어렵다) 그 이유는 크게 2가지입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;특정 task(e.g. translation, question answering)에 transfer하기에 유용하다고 여겨지는 text representation을 학습하는 데 있어서 어떤 optimization objective이 효과적인지 알기 힘듦&lt;/li&gt;
&lt;li&gt;학습된 text representation을 target task에 효과적으로 transfer하기 위한 일치된 의견(consensus)가 없음. 즉, 이러한 uncertainties가 language processing에서 semi-supervised learning방법을 발전시키기 어려움&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 본 논문에서는 &lt;b&gt;semi-supervised learning에서 다양한 task에 transfer하기에 적합한 universal representation을 학습하는게 목적&lt;/b&gt;입니다. 즉, 어떤 특정한 task를 수행하는 모델을 만들기 위해 해당 task와 관련된 domain의 unlabeled corpus를 필요로 하지 않습니다. GPT-1의 training은 two-stages procedure를 거칩니다.&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;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Two-stages Training Procedure&lt;/b&gt;&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Model의 initial parameters를 학습하기 위해 unlabeled data에 대해 language modeling objective를 사용&lt;/li&gt;
&lt;li&gt;학습된 initial parameters을 target task에 적용시키기 위해 supervised learning 학습&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;GPT-1의 기본 model architecture는 Transformer를 사용&lt;/b&gt;합니다. Transformer는 long-term dependencies을 제어하기 위해 structured memory를 제공할 수 있고 이 구조는 다양한 task에 transfer하기 용이합니다. Transfer(fine-tuning) 할 때는 &lt;b&gt;traversal-style approach&lt;/b&gt;를 기반으로 하는 task-specific input adaptations을 사용합니다. 이 approach는 structured text input을 하나의 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;contiguous sequence of tokens으로 만들 게 되고 이를 통해서 모델의 최소한의 변경만으로 효과적인 성능을 얻을 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;Traversal-style approach란?&lt;/b&gt;&lt;br /&gt;아래 그림과 같이 다양한 task의 입력을 start token, delimiter(문장 구분), end tokens(Extract)으로 이루어지도록 하는 것. 이를 통해 모델의 최소한의 변경만으로 효과적인 fine-tuning가능함&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epstJA/btrYDgkN1N8/kEgG1HCQ2S6kslTcIzahH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epstJA/btrYDgkN1N8/kEgG1HCQ2S6kslTcIzahH0/img.png&quot; data-alt=&quot;Traversal-style approach for effective fine-tuning&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epstJA/btrYDgkN1N8/kEgG1HCQ2S6kslTcIzahH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FepstJA%2FbtrYDgkN1N8%2FkEgG1HCQ2S6kslTcIzahH0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;315&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Traversal-style approach for effective fine-tuning&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Methods&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Training procedure는 two-stages로 나뉩니다. (1) 수많은 unlabeled text data로 high-capacity language model을 학습하는 1번째 stage, (2) labeled data을 통해서 특정 task에 맞게 fine-tuning하는 2번째 stage를 거칩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Unsupervised pre-training&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unlabeled된 token corpus \( U = \{ u_1, \ldots, u_n \} \)이 주어졌을 때 다음과 같이 likelihood를 최대화하기 위한 standard language modeling objective를 사용합니다.&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;\[&lt;br /&gt;&amp;nbsp;L_1(U) = \sum_i log P( u_i | u_{i-k}, \ldots, u_{i-1} ; \Theta) \quad \cdots Eq. (1)&lt;br /&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;\( k \)는 context window size, \( P \)는 conditiional probability이며 이는 \( \Theta \) parameters로 구성된 network로 모델링되어있습니다. 해당 식을 풀이하자면 어떤 단어들을\(u_{i-k}, \ldots, u_{i-1} \)을 입력으로 주었을 때 그다음으로 나올 단어가 \( u_i \)일 확률이 높도록 \( \Theta \)을 학습하는 것입니다. 해당 network는 SGD optimizer로 학습했다고 합니다.&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;Language model로는 multi-layer &lt;b&gt;&lt;i&gt;Transformer Decoder&lt;/i&gt;&lt;/b&gt;를 사용하였습니다. Transformer Decoder는 기존의 Transformer의 encoder-decoder구조에서 encoder module을 제거하고(parameter반으로 줌) 원래의 input, output sentence를 a single sentence로 합쳐 해당 sentence를 모델의 입력으로 사용하는 구조입니다. 예를 들어, sequence-transduction data \( (m^1, \ldots , m^n) \mapsto (y^1, \ldots, y^{\gamma} ) \)을 \( (w^1, \ldots ,w^{n+\gamma+1}) = (m^1, \ldots , m^n, \delta, y^1, \ldots, y^\gamma) \) 로 치환하는 것을 의미합니다. ( \( \delta \)는 separator token)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KFMmd/btrYTOAGUYd/lTUCq8Wtb94UPLkEgaik30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KFMmd/btrYTOAGUYd/lTUCq8Wtb94UPLkEgaik30/img.png&quot; data-alt=&quot;Transformer architecture and training objectives&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KFMmd/btrYTOAGUYd/lTUCq8Wtb94UPLkEgaik30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKFMmd%2FbtrYTOAGUYd%2FlTUCq8Wtb94UPLkEgaik30%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;178&quot; height=&quot;314&quot; data-origin-width=&quot;322&quot; data-origin-height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Transformer architecture and training objectives&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 모델은 context tokens을 입력으로 multi-headed self-attention operation을 적용하였고 position-wise feed-forward layer를 통해 target token에 대한 output distribution을 산출하였습니다.&amp;nbsp;&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;\[&lt;br /&gt;\begin{array}{l} h_0 = U W_e + W_p , \cr h_l = transformer_block (h_{l-1} \forall i \in [1,n]&amp;nbsp; ,&amp;nbsp; \cr&amp;nbsp; P(u) = softmax(h_n W^T_e)&amp;nbsp; \end{array} \quad \cdots Eq. (2)&lt;br /&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;\( U = (u_{-k} , \ldots , u_{-1} ) \)은 tokens의 context vector, \(n\)은 layer 수, \(W_e \)는 token embedding matrix, \(W_p \) 은 position embedding matrix입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Supervised Learning&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eq (1)을 통해 unsupervised learning을 완료했다면 supervised target task에 맞게 fine-tuning하게됩니다. 논문에서는 labeled dataset \( C \)을 가정합니다. (각 instance는 input tokens \(x_1, \ldots, x_m \)과 label \(y\)으로 이루어짐) Input tokens은 pre-trained model의 입력으로 들어가 마지막 transformer의 activation \( h^m_l \)을 출력해 내고 (target task을 위해) 추가된 linear output layer( with parameter \(W_y \) )을 거쳐 최종 output \( y \)를 predict하게 됩니다.&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;\[&lt;br /&gt;P(y | x^1 , \ldots , x^m ) = softmax(h^m_l W_y)\quad \cdots Eq. (3)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Eq. (3)을 통해 나온 output \(&amp;nbsp; P(y | x^1 , \ldots , x^m ) \)은 아래와 같은 objective function을 maximize하도록 학습됩니다.&amp;nbsp;&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;\[&lt;br /&gt;L_2 (C) = \sum_{(x,y)} log P (y| x^1 , \ldots , x^ m) &amp;nbsp;\quad \cdots Eq. (4)&lt;br /&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;추가적으로 (1) supervised model의 generalization ability를 향상시키고&amp;nbsp;(2) convergence를 가속화시키기 위해 fine-tuning시에 auxiliary objective function을 사용하였습니다. 아래와 같이 기존의 unsupervised learning에 사용했던 loss term \( L_1(C) \)도 추가하여서 supervised, unsupervised learning의 task모두 잘할 수 있도록 합니다. ( \( \lambda \)는 weight parameter입니다.)&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;\[&lt;br /&gt;L_3 (C) = L_2(C) + \lambda L_1(C)&amp;nbsp; \quad \cdots Eq. (5)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 Task-specific input transformations&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Text classfication같은 task경우 위의 방법 그대로 fine-tuning가능하지만 question answering, textual entailment와 같은 다른 task에는 고유한 structured input을 필요로 합니다. 그렇다고 각 task의 input 형태에 따라 모델을 크게 변경해야 한다면 이는 customization하는 cost가 많이 듭니다. 이를 해결하기 위해 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;traversal-style approach&lt;/b&gt;&lt;/span&gt;를 사용합니다. 해당 방법은 structured input을 unsupervised learning을 통해 pre-trained model에 process할 수 있도록 ordered sequence로 바꾸게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 모든 task에 대해 input transformations은 랜덤하게 initialized start, end tokens ( \( \langle s \rangle ,\langle e \rangle \) ) 을 포함합니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epstJA/btrYDgkN1N8/kEgG1HCQ2S6kslTcIzahH0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epstJA/btrYDgkN1N8/kEgG1HCQ2S6kslTcIzahH0/img.png&quot; data-alt=&quot;Traversal-style approach for effective fine-tuning&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epstJA/btrYDgkN1N8/kEgG1HCQ2S6kslTcIzahH0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FepstJA%2FbtrYDgkN1N8%2FkEgG1HCQ2S6kslTcIzahH0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;315&quot; data-origin-width=&quot;985&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Traversal-style approach for effective fine-tuning&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3.1 Textual Entailment&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림과 같이 premise \( p \)와 hypothesis \( h \) token sequence들을 concatenate 합니다. 두 tokens을 구분하기 위해 delimiter(구분자) token \( $ \)을 사용합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3.2 Similarity&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 task에서는 두 문장 사이에 가능한 ordering모두 고려하여 input transformations하여 두 개의 input을 만들고 각각 독립적으로 transformer의 output representations \( h^m_l \)을 뽑아내고 element-wise하게 add operation하고 linear output layer을 거쳐 최종 output을 얻습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.3.3 Question Answering&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Document \(z\), question \(q\)와 가능한 answer set \( { a_k} \)가 주어질 때 document context와 question을 한 묶음하고 각 가능한 answer을 delimiter token을 이용해 concatenate합니다. 즉, \( [z; q; $; a_k] \)형태의 여러 개의 input을 만듭니다. 각 input sentence는 독립적으로 model을 거친 다음 sofmax layer을 통해 normalize시켜 possible answer들에 대해 output distribution을 얻습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Experiment&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Setup&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Unsupervised learning에 BooksCorpus dataset을 사용하였습니다. 7000개의 미출판된 책의 내용을 담고 있으며 장르도 어드벤쳐, 판타지, 로맨스로 다양합니다. 대체가능한 dataset으로는 1B Word Benchmark이며 sentence-level로 shuffled되어 long-range structure를 없앴습니다. Unsupervised learning을 통해 해당 corpus에서 18.4이라는 낮은 token level perplexity(복잡성)을 도출하였다고 합니다. (저 perplexity 어떻게 scoring하는 지 아시는 분 있으신가요..?)&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;It contains over 7,000 unique unpublished books from a variety of genres including Adventure,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fantasy, and Romance. Crucially, it contains long stretches of contiguous text, which allows the&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;generative model to learn to condition on long-range information. An alternative dataset, the 1B&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Word Benchmark, which is used by a similar approach, ELMo [44], is approximately the same size&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1.1 Model specifications&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델은 original transformer의 decoder부분만 사용하였습니다. masked self-attention heads(768 dim and 12 attentions heads)가 포함된 12 layer로 이루어져 있습니다. Position-wise feed-forward networks을 위해 3,072 dimension의 inner state를 사용하였습니다.&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;Model: original transformer의 decoder부분
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Masked self-attention heads(768 dim and 12 attentions heads)가 포함된 12 layers&lt;/li&gt;
&lt;li&gt;Position-wise feed-forward networks을 위해 3,072 dimension의 inner state를 사용&lt;/li&gt;
&lt;li&gt;Layer-Norm 사용됨&lt;/li&gt;
&lt;li&gt;N(0, 0.02)로 weight initialization&lt;/li&gt;
&lt;li&gt;Activation function으로 Gasuusian Error Linear Unit (GELU)사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Optimizer: Adam&lt;/li&gt;
&lt;li&gt;Learning rate: 2.5e-4&lt;/li&gt;
&lt;li&gt;Scheduler: cosine annealing&lt;/li&gt;
&lt;li&gt;Epochs: 100&lt;/li&gt;
&lt;li&gt;Batch size: 64&lt;/li&gt;
&lt;li&gt;Token length: 512&lt;/li&gt;
&lt;li&gt;bytepair encoding (BPE) vocabulary 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;40,000 merges와 residual, embedding, attention dropouts 포함&lt;/li&gt;
&lt;li&gt;0.1 regularization&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;modified L2 regularization 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;모든 non biasd와 gain weights에 대해 w=0.01&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ftfy library을 사용하여 BooksCorpus의 raw text를 정리&lt;/li&gt;
&lt;li&gt;spaCy tokenizer 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2.2 Fine-tuning details&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;명시되지 않았으면 unsupervised pre-training에 썼던 hyperparameter를 그대로 사용&lt;/li&gt;
&lt;li&gt;0.1 rate의 dropout을 classifier에 추가&lt;/li&gt;
&lt;li&gt;Learning rate: 6.25e-5&lt;/li&gt;
&lt;li&gt;Batch size: 32&lt;/li&gt;
&lt;li&gt;Epochs: 3 (3이면 충분하다고 합니다.)&lt;/li&gt;
&lt;li&gt;Scheduler: linear learning rate decay&lt;/li&gt;
&lt;li&gt;0.2%의 warm-up training&lt;/li&gt;
&lt;li&gt;\( \lambda \): 0.5&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 성능&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능은 기존보다 당연히 좋을 거라 제 관심사가 아니므로 결과표만 보여드립니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;1530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TszIM/btrY2nwZE7W/zQpGLKBBlftjUxoXCbCsak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TszIM/btrY2nwZE7W/zQpGLKBBlftjUxoXCbCsak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TszIM/btrY2nwZE7W/zQpGLKBBlftjUxoXCbCsak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTszIM%2FbtrY2nwZE7W%2FzQpGLKBBlftjUxoXCbCsak%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;674&quot; height=&quot;556&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;1530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI paper review</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/76</guid>
      <comments>https://da2so.tistory.com/76#entry76comment</comments>
      <pubDate>Mon, 13 Feb 2023 21:19:55 +0900</pubDate>
    </item>
    <item>
      <title>Per-shot Encoding 설명</title>
      <link>https://da2so.tistory.com/75</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&lt;b&gt;※&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://netflixtechblog.com/dynamic-optimizer-a-perceptual-video-encoding-optimization-framework-e19f1e3a277f&quot;&gt;해당 블로그&lt;/a&gt;를 reference하였습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Per-shot Encoding 이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Conventional Encoding 방법은 하나의 video에 대하여 압축의 정도를 결정하는 Quantization Parameters(QPs)(e.g. CRF)값 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;'하나'&lt;/b&gt;&lt;/span&gt;을 인자로 encoding하는 방법을 취합니다. (CRF값이 클수록 compression을 많이 하게 되고 visual quality는 낮아집니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이는 video내의 frame간의 특성을 고려하지 않은 채 단일한 QPs로 encoding하기 때문에 B(Bitrate)-D(Distortion) rate관계에서 최적의 성능을 뽑아내지 못합니다. 예를 들어, 한 video내의 초중반 frame들은 flat region이 많고 motion vector의 값이 작은 경우이고 중후반 frame들은 그 반대일 경우 하나의 CRF값으로 해당 video를 encoding하는 것은 효율적이지 않겠죠.&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;이를 해결하기 위해 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Per-shot Encoding은 하나의 video를 여러 개의 shot으로 split한 다음에 각 shot마다 적절한 CRF, Resolution값을 encoding하고 concatenate하는 방법&lt;/b&gt;&lt;/span&gt;을 의미합니다.&amp;nbsp; 각 shot마다 적절한 CRF와 Resolution을 주어 encoding가능하므로 B-D rate효율이 좋을 것입니다. Per-shot Encoding은 Dynamic Optimizer라고도 명명하며 Netflix에서 제안한 방법입니다.&amp;nbsp;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;881&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgcLAW/btrYkEdtlfR/eAloamsSmEdQAZhq2ZFXf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgcLAW/btrYkEdtlfR/eAloamsSmEdQAZhq2ZFXf0/img.png&quot; data-alt=&quot;Conventional Encoding vs Per-shot Encoding&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgcLAW/btrYkEdtlfR/eAloamsSmEdQAZhq2ZFXf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgcLAW%2FbtrYkEdtlfR%2FeAloamsSmEdQAZhq2ZFXf0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;327&quot; data-origin-width=&quot;1852&quot; data-origin-height=&quot;881&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Conventional Encoding vs Per-shot Encoding&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 특징으로는 per-shot encoding은 shot마다 병렬적으로 encoding처리가 가능하고 하나의 shot에서 encoding error가 발생해도 오류가 발생한 shot만 다시 encoding하면 된다는 장점을 가집니다.&amp;nbsp;&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;이후에는 좀 더 구체적으로 &lt;b&gt;(i)&lt;/b&gt; 어떤 방법으로 video을 여러 개의 shot으로 나누는 지?&lt;b&gt; (ii)&lt;/b&gt; 각 shot마다 적절한 CRF 값은 어떻게 찾는 지에 대한 방법은 Per-shot Encoding 알고리즘을 설명하면서 공유드리겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Per-shot Encoding 알고리즘&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Per-shot Encoding은 video을 입력으로 &lt;b&gt;(1)&lt;/b&gt; Scene Detection을 통해 여러 개의 shot으로 나누고 (2) 각 shot별로 다양한 encoding configurations(e.g. CRF, Resolution, ...)을 인자로 encoding합니다. (3)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Scene(Shot) Detection&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Per-shot Encoding은 video을 입력으로 받아 scene detection을 통해 여러 개의 shot으로 나누게 됩니다. 여기서 scene detection은 ffmpeg에서 제공하는 방법을 사용합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;ffmpeg -i ${input_video} -filter_complex &quot;select='gt(scene, ${threshold})',metadata=print:file=${output_file}&quot; -vsync vfr img%03d.png&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 ${threshold}값은 0~1사이의 값이고 해당 값이 클수록 frame간의 변화가 커야 scene split이 일어납니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ss5Hh/btrYfZJ25FF/gH8eK4lBhUVhXSuKd736GK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ss5Hh/btrYfZJ25FF/gH8eK4lBhUVhXSuKd736GK/img.png&quot; data-alt=&quot;Scene Detection&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ss5Hh/btrYfZJ25FF/gH8eK4lBhUVhXSuKd736GK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSs5Hh%2FbtrYfZJ25FF%2FgH8eK4lBhUVhXSuKd736GK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;755&quot; height=&quot;180&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Scene Detection&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Encoding shots with various encoding parameters&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 shot에 대해서 여러 CRF와 Resolution 값에 대해 모두 encoding합니다. CRF값과 resolution의 값이 정확히 어떤 범위 및 값인지는 확인할 수는 없지만 해당 &lt;a href=&quot;https://arxiv.org/pdf/2206.04877.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Netflix 논문&lt;/a&gt;을 참고했을 때 CRF는 16, 20, 24, 28, 32, 36, 40, 44, 48이고 resolution은 1080p, 720p, 540p, 432p, 360p, 270p, 216p를 사용하였습니다.&amp;nbsp;&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;CRF는 9개, resolution은 7개입니다. 예를 들어 shot의 개수가 10개라면 총 encoding trial 횟수는 9*7*10=630번입니다. 그래서 해당 부분의 processing이 시간이 많이 듭니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2.3 Evaluating each encoded shots using VMAF&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 shot에 대해 여러 CRF와 resolution값으로 encoding했다면 각 encoding된 shot이 원본과 비교했을 때&lt;b&gt; (1)&lt;/b&gt; 얼마나 distortion되었는 지, &lt;b&gt;(2)&lt;/b&gt; 얼마나 압축되었는지 측정해야합니다. Distortion 측정 방법으로는 Netflix에서 제안한 &lt;a href=&quot;https://da2so.tistory.com/57&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;VMAF&lt;/a&gt;를 사용하고 압축정도는bitrate로 계산합니다. 여기서 VMAF값은 0~100사이의 값을 가지고 값이 높을 수록 원본으로부터 distortion(왜곡)이 &quot;안된것&quot;입니다. 그래서 그대로 VMAF값을 사용하면 distortion의미와 반대이므로 아래와 같이 둘 중에 하나의 값으로 distortion 정도를 계산합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( D_{Linear} (VMAF) = 100 - VMAF\)&lt;/li&gt;
&lt;li&gt;\( D_{Inverse} (VMAF) = \frac{1}{1+VMAF} \)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;703&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZUgbV/btrYmycMG4K/yxLPUr08qBbgkI1mMQK3O0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZUgbV/btrYmycMG4K/yxLPUr08qBbgkI1mMQK3O0/img.png&quot; data-alt=&quot;Calculate bitrate and VMAF for each encoded shot&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZUgbV/btrYmycMG4K/yxLPUr08qBbgkI1mMQK3O0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZUgbV%2FbtrYmycMG4K%2FyxLPUr08qBbgkI1mMQK3O0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;739&quot; height=&quot;371&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;703&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Calculate bitrate and VMAF for each encoded shot&lt;/figcaption&gt;
&lt;/figure&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;하나의 shot에 대해 여러 CRF, resolution으로 encoding하고 각각 encoded shot에 대해 bitrate, VMAF값을 구한 뒤에 R-D points를 graph로 visualization하면 다음과 같을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;685&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bECDwZ/btrYjy5WncD/sp4DE4piKvydEycIfOmIu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bECDwZ/btrYjy5WncD/sp4DE4piKvydEycIfOmIu0/img.png&quot; data-alt=&quot;(R,D) points for a certain single shot&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bECDwZ/btrYjy5WncD/sp4DE4piKvydEycIfOmIu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbECDwZ%2FbtrYjy5WncD%2Fsp4DE4piKvydEycIfOmIu0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;588&quot; height=&quot;405&quot; data-origin-width=&quot;995&quot; data-origin-height=&quot;685&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(R,D) points for a certain single shot&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.4 Extracting convex hull points for (R, D) points of each shot&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그래프는 하나의 예시이지만 거의 대부분 shot에 대해서 convex hull모양의 R-D rate 그래프를 얻을 것입니다. 그럴 경우 Distortion &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;T&lt;/b&gt;&lt;/span&gt;값을 기준으로 했을 때 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;a&lt;/b&gt;&lt;/span&gt;값을 가지는 180p가 &lt;b&gt;b&lt;/b&gt;값을 가지는 144p보다 bitrate가 더 작은 것을 확인가능합니다. 즉, &lt;b&gt;convex hull에 포함되는 point들이 특정 bitrate에서 가장 distortion이 낮은 것을 의미하고 또한 특정 distortion에서 가장 bitrate가 낮은 것을 의미&lt;/b&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;convex hull points들이 각 axis에서 가장 효율적인 encoded shot이므로 아래와 같이 convex hull points들만 뽑도록 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;490&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NVJOA/btrYkLjn5wE/DzkzaYP1MVf9hnBnKp8AHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NVJOA/btrYkLjn5wE/DzkzaYP1MVf9hnBnKp8AHk/img.png&quot; data-alt=&quot;Convex hull of (R, D) points for a certain single shot&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NVJOA/btrYkLjn5wE/DzkzaYP1MVf9hnBnKp8AHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNVJOA%2FbtrYkLjn5wE%2FDzkzaYP1MVf9hnBnKp8AHk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;605&quot; height=&quot;402&quot; data-origin-width=&quot;737&quot; data-origin-height=&quot;490&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Convex hull of (R, D) points for a certain single shot&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그래프처럼 convex hull points들만 뽑기 위해서 &lt;code&gt;scipy.spatial.ConvexHull&lt;/code&gt;를 사용하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.5 Constant slope principle for convex hull points of all shots&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 아래와 같이 각 shot마다 convex hull points를 뽑인 상태입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;352&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkvQmg/btrYapvHQ0s/zIRS1wRU03RPixMgq55lqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkvQmg/btrYapvHQ0s/zIRS1wRU03RPixMgq55lqK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkvQmg/btrYapvHQ0s/zIRS1wRU03RPixMgq55lqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkvQmg%2FbtrYapvHQ0s%2FzIRS1wRU03RPixMgq55lqK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;731&quot; height=&quot;164&quot; data-origin-width=&quot;1570&quot; data-origin-height=&quot;352&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서 각 shot마다 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;&quot;최적의 encoded shot을 뽑아&quot;&lt;/b&gt;&lt;/span&gt;서 concatenate하여 최종 per-shot encoding의 결과를 만들게 됩니다. 그렇다면 어떻게 최적의 encoded shot을 뽑을까요?&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;바로 constant slope principle을 이용하게 됩니다. Constant slope principle은 Integer Programming(IP) 문제랑 거의 같습니다. (저는 그냥 같다고 봅니다..ㅋㅋ) 그래서 풀어얘기하자면 &lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;constraint(e.g. 93 VMAF)를 만족하면서 objective function&lt;b&gt;(e.g. bitrate 최소화)&lt;/b&gt;을 최대 or 최소화 시키는 알고리즘&lt;/b&gt;&lt;/span&gt;입니다. 즉, 각 shot마다 어떤 convex hull point을 골라야 (concatenate했을 때) 위의 constraint를 만족하면서 objective function을 최적화할 수 있는 지 알려주는 알고리즘이 constant slope priciple입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(아시겠지만 convex hull points들이 continous한 값을 가지지 않고 discrete한 값을 가지므로 IP 문제입니다.)&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Integer Programming 예시&lt;/b&gt;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1iLJB/btrYjXLcA1l/LB4XsEUl4VgUFnZH6YzWM0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1iLJB/btrYjXLcA1l/LB4XsEUl4VgUFnZH6YzWM0/img.png&quot; data-alt=&quot;Example of integer programming&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1iLJB/btrYjXLcA1l/LB4XsEUl4VgUFnZH6YzWM0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1iLJB%2FbtrYjXLcA1l%2FLB4XsEUl4VgUFnZH6YzWM0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;204&quot; data-origin-width=&quot;970&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Example of integer programming&lt;/figcaption&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;per-shot enocoding으로 예를 들면 problem은 선택된 encoded shot들의 bitrate합이 Minimize되도록 할 것이고 subject to로는 선택된 encoded shot들의 평균 VMAF값이 93으로 설정하면 됩니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;592&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m6uuR/btrYlvm5DkY/tDTG5FfTH8x8FIA5o0HUD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m6uuR/btrYlvm5DkY/tDTG5FfTH8x8FIA5o0HUD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m6uuR/btrYlvm5DkY/tDTG5FfTH8x8FIA5o0HUD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm6uuR%2FbtrYlvm5DkY%2FtDTG5FfTH8x8FIA5o0HUD0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;765&quot; height=&quot;323&quot; data-origin-width=&quot;1400&quot; data-origin-height=&quot;592&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 그림처럼 &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;constraint를 bitrate로 주었을 때는 파란색 경로를 따라 평균 visual quality가 가장 높도록(distortion이 낮도록) 각 encoded shot들이 선택&lt;/span&gt;&lt;/b&gt;된 것을 볼 수 있지만 반대로 &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;constraint를 visual quality로 주었을 때 빨간색 경로를 따라 평균 bitrate가 가장 낮도록 encoded shot들이 선택&lt;/span&gt;&lt;/b&gt;된 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(constraint는 개발자가 주는 특정한 값입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.6 concatenate selected convex hull points&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 위의 constant slope principle을 통해 선택된 (각 shot별로) encoded shot을 concatenate하면 최종 per-shot encoding의 결과물 video가 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Results&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Conventional encoding(Fixed Q)과 비교했을 때 per-shot encoding(Dynamic Optimizer)의 성능은 아래와 같다고 합니다. 같은 distortion기준으로 per-shot encoding의 bitrate가 15~20%정도 적은 것을 확인가능합니다. 굳!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;625&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cKj1XY/btrYhbDEZUG/SJT1WX1BcbviXSR1KO1eHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cKj1XY/btrYhbDEZUG/SJT1WX1BcbviXSR1KO1eHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cKj1XY/btrYhbDEZUG/SJT1WX1BcbviXSR1KO1eHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcKj1XY%2FbtrYhbDEZUG%2FSJT1WX1BcbviXSR1KO1eHK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;377&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;625&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/75</guid>
      <comments>https://da2so.tistory.com/75#entry75comment</comments>
      <pubDate>Mon, 6 Feb 2023 23:15:18 +0900</pubDate>
    </item>
    <item>
      <title>The Forward-Forward Algorithm: Some Preliminary Investigations 논문 리뷰</title>
      <link>https://da2so.tistory.com/74</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;오늘은 Hinton님의 &lt;a href=&quot;https://www.cs.toronto.edu/~hinton/FFA13.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;The Forward-Forward Algorithm: Some Preliminary Investigations&lt;/a&gt; 논문을 리뷰입니다!&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;해당 논문의 목적은 기존 deep learning model의 학습방법인&lt;b&gt; backpropagation에 대한 단점을 지적하고 새로운 학습방법인 Forward-Forward 알고리즘을 제안&lt;/b&gt;하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;1. What is wrong with Backpropagation&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deep learning model의 backpropagation은 인간의 뇌가 학습하는 방법과 유사하게 설계되어있다고 알고 계신분들이 많은데요. 실제로 그렇지 않다고 하고 근거는 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Backward pass를 하기위해 neural activity를 저장하거나 error derivate를 전파하는 과정이 인간의 뇌에서 일어나지 않았고 그런 증거가 발견되지 않았다고 함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;인간의 뇌는 중단되는 시간이 따로 없이 다른 sensory processing stage을 통해서 sensory data을 전달할 필요가 있고 그때 그때 봐가며 learning이 될 수 있어야 함&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Error derivatives를 propagate하기위해 중단되는 시간이 생김&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Backpropagation으로는 real time으로 inference와 learning이 불가함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Backprogation은 모델의 forward계산의 정확한 knowledge가 필요. 즉, differentiable할 수 없는 black-box에 대해 forward-pass한다면 backpropatgation못하는 문제점이 있음&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;이에 대한 방안으로 강화학습이 있지만 강화학습은 high variance를 가진다는 문제점을 가짐&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서, 해당 논문의 주요 목적은 unknown non-linearities가 포함된 neural network에 강화학습을 사용할 필요가 없고 Foward-Foward algorithm(FF)을 사용하면 된다는 것입니다. 그리고 FF는 neural acitivities를 저장하지 않거나 error derivatives를 propagate하지 않고도 sequential data를 pipelining하면서 학습가능하다는 장점을 가집니다.&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;그럼 FF가 장점만 가지냐? 그건 아닙니다. 단점은 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;FF는 어떤 때에는 backpropagation보다 느림&lt;/li&gt;
&lt;li&gt;Generalized되지않았기 때문에 다양한 task, application에 사용하기 아직 힘듬
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;큰 dataset으로 학습된 큰 model의 학습능력을 내기위해서는 backpropagation을 사용해야함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fdfdfd; color: #000000;&quot;&gt;FF가 backpropagation보다 우수할 수 있는 두 가지 영역은 cortex안에서 model의 학습과 강화학습에 의존하지 않고 매우 낮은 전력의 analog hardward를 사용하는 방법입니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;The Forward-Forward Algorithm&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Forward-Forward Algorithm (FF)는 &lt;b&gt;&lt;i&gt;Boltzmann machine&lt;/i&gt;&lt;/b&gt;과 &lt;b&gt;&lt;i&gt;Noise Contrastive Estimation&lt;/i&gt;&lt;/b&gt;의 영감을 받은 &lt;b&gt;greedy multi-layer learning방법&lt;/b&gt;입니다. 아이디어는 backpropagation의 forward, backward passes를 2개의 forward passes로 대체하는 것입니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1994&quot; data-origin-height=&quot;986&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3zIXE/btrXn5DvbHk/XLKWd5YwxT70PoMedJpKqk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3zIXE/btrXn5DvbHk/XLKWd5YwxT70PoMedJpKqk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3zIXE/btrXn5DvbHk/XLKWd5YwxT70PoMedJpKqk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3zIXE%2FbtrXn5DvbHk%2FXLKWd5YwxT70PoMedJpKqk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;496&quot; height=&quot;245&quot; data-origin-width=&quot;1994&quot; data-origin-height=&quot;986&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FF의 &lt;b&gt;첫 번째 forward는&lt;/b&gt; 기존과 같이 top-down 형식으로 forward를 진행하고 &lt;b&gt;두 번째 forward는&lt;/b&gt; 각 layer에서 weight update를 진행합니다.&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;기존과 다른 또다른 점은 contrastive learning을 하는것입니다. 즉, positive pass와 negative pass를 나누어 weight를 update합니다. &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Positive pass는 real(positive) data에서 작동하며 매 hidden layer에서 goodness(잘햇어!)을 향상시키기 위해 weight를 조절합니다. 반대로 negative pass에서는 negative data에서 작동하며 매 hidden layer에서 goodness를 낮추기 위해 weight를 조절합니다. 그래서, FF를 사용하기 위한 충분 조건은 different data와 opposite objectives를 가져야하는 것입니다.&amp;nbsp;&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;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;그럼 각 layer마다 goodness function을 어떻게 정의할까요? 해당 논문에서는 해당 layer안에서 rectified linear neurons의 activities의 제곱(square)의 합으로 정의한다고 합니다. FF learning은 real data에 대해서는 특정 threshold보다 높게 goodness가 출력되도록 하고 negative data에 대해서는 threshold보다 낮게 측정되도록 하는게 목적입니다. 이를 수식화하면 다음과 같습니다.&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;\[&lt;br /&gt;p(positive) = \sigma ( \sum_j y^2_j - \theta ) , \quad \cdots Eq. (1)&lt;br /&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;\( \sigma \)는 logistic function(i.e. sigmoid)이며 \( \theta \)는 threshold이며 \( y_j \)는 layer normalization전의 \( j \)번째 hidden unit의 acitivity값입니다. 위의 objective function이 loss function이 되고 Pytorch기준으로 loss.backward, optimizer.step을 통해 weight update합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 negative data는 외부에서 제공되거나 neural net의 top-down connection을 이용해서 predict되어 생성가능하다 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;Learning multiple layers of representation with a simple layer-wise goodness function&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;div&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약&amp;nbsp;first&amp;nbsp;hidden&amp;nbsp;layer의&amp;nbsp;acitivities를&amp;nbsp;second&amp;nbsp;hidden&amp;nbsp;layer의&amp;nbsp;input으로&amp;nbsp;사용하고&amp;nbsp;싶다면&amp;nbsp;어떻게&amp;nbsp;해야할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FF는 first hidden layer의 hidden vector의 length을 normalize하여 second layer의 input으로 보낸다고 합니다. 이렇게 하면 first hidden layer에서 goodness를 계산하기 위해 사용했던 정보를 제거할 수 있고 next hidden layer에 first hidden layer의 relative acitivities의 정보를 사용할 수 있도록 합니다. (즉, relative acitivies는 layer normalization에 영향을 받아 없어지거나 하지 않는 것) 달리 표현하면 first hidden layer의 activity vector는 length와 orientation을 가지고 length(before layer normalization)는 그 layer의 goodness를 define하는데 사용되고 orientation(after layer normalization)은 next layer에 전달하기위해 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3.&amp;nbsp;Some&amp;nbsp;experiments&amp;nbsp;with&amp;nbsp;FF&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FF가 small neural network에서 어떻게 작동하지 설명하도록 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1&amp;nbsp;The&amp;nbsp;backpropagation&amp;nbsp;baseline&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운&amp;nbsp;learning&amp;nbsp;알고리즘을&amp;nbsp;설명하고&amp;nbsp;성능을&amp;nbsp;확인하기에&amp;nbsp;가장&amp;nbsp;적합한&amp;nbsp;MNIST에&amp;nbsp;대해&amp;nbsp;실험하려고&amp;nbsp;합니다.&amp;nbsp;그&amp;nbsp;전에&amp;nbsp;backpropagation을&amp;nbsp;사용했을&amp;nbsp;때&amp;nbsp;성능에&amp;nbsp;대해&amp;nbsp;이야기합니다.&amp;nbsp;CNN을&amp;nbsp;사용하면&amp;nbsp;0.6%&amp;nbsp;test&amp;nbsp;error을&amp;nbsp;가진다고&amp;nbsp;합니다.&amp;nbsp;그리고&amp;nbsp;permutation-invarient&amp;nbsp;task에서는&amp;nbsp;FC&amp;nbsp;layer와&amp;nbsp;ReLU를&amp;nbsp;사용하면&amp;nbsp;1.4%&amp;nbsp;test&amp;nbsp;error를&amp;nbsp;가진다고&amp;nbsp;합니다.&amp;nbsp;즉,&amp;nbsp;complicated&amp;nbsp;regularizer사용&amp;nbsp;없이&amp;nbsp;backpropagation을&amp;nbsp;사용하면&amp;nbsp;1.4%&amp;nbsp;test&amp;nbsp;error&amp;nbsp;성능을&amp;nbsp;가지게&amp;nbsp;됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;permutation-invarient&amp;nbsp;task란?&lt;/b&gt;&lt;br /&gt;입력&amp;nbsp;벡터&amp;nbsp;요소의&amp;nbsp;순서와&amp;nbsp;상관없이&amp;nbsp;같은&amp;nbsp;출력을&amp;nbsp;생성하는&amp;nbsp;모델을&amp;nbsp;뜻하며&amp;nbsp;대표적인&amp;nbsp;모델로는&amp;nbsp;MLP이다.&amp;nbsp;permuation-invarient&amp;nbsp;task가&amp;nbsp;아닌&amp;nbsp;모델로는&amp;nbsp;입력&amp;nbsp;이미지의&amp;nbsp;픽셀의&amp;nbsp;순서를&amp;nbsp;고려하는&amp;nbsp;CNN이&amp;nbsp;있다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2&amp;nbsp;A&amp;nbsp;simple&amp;nbsp;supervised&amp;nbsp;example&amp;nbsp;of&amp;nbsp;FF&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;※&amp;nbsp;해당&amp;nbsp;글에서는&amp;nbsp;unsuperivsed,&amp;nbsp;nlp,&amp;nbsp;reccurent&amp;nbsp;net에&amp;nbsp;대한&amp;nbsp;내용은&amp;nbsp;skip하고&amp;nbsp;supervised에&amp;nbsp;대한&amp;nbsp;내용만&amp;nbsp;다루겠습니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;Supervised leraning은 single task, small model을 사용하고 싶을 때 유용한 방법입니다. 이를 FF에 적용하기 위해서는 input에 label을 포함시키는 방법을 사용합니다. Positive data에는 옳바른 label을 포함하고 있는 input image들로 구성되고 negative data는 틀린 label을 포함하고 있는 input image들로 구성됩니다. Positive, negative data의 다른 점은 오직 label이기 때문에 FF알고리즘은 label과 연관되어 있지 않은 image의 feature 정보는 모두 무시할 것입니다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;그럼 label을 어떻게 data에 포함할까요?&lt;/b&gt;&lt;br /&gt;MNIST기준으로 설명드리면 class가 10개이므로 image의 첫 10 pixels에 label정보를 기입하는 것입니다. 이렇게 하여 논문에서는 60 epochs, 4 hidden fc layers, 2000 ReLUs로 구성된 network로 1.36% test errors를 얻었다고 합니다. 해당 결과는 backpropagation을 사용했을 때는 20 epoch으로 낼 수 있는 성능이라고 합니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;FF로 training하고 나면 inference는 어떻게 진행할까요?&lt;/b&gt;&lt;br /&gt;Inference시에는 test image의 첫 10 pixel에 neutral label(모두 0.1값)을 포함시켜서 single forward pass를 통해 classify한다고 합니다. 첫 번째 hidden layer의 activities(features)을 제외하고 다른 모든 hidden layer의 acitivities값에 대해 softmax을 적용하고 다 더해줍니다. 더했을 때 가장 큰 값을 가진 class index가 model의 최종 output class가 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(이 부분이 제가 읽은 책중에 천개의 뇌 이론과 비슷하더라고요. 책에서는 수많은 뇌세포가 투표를 통해 객체가 무엇인지 판단한다고 하는데 각 뇌세포를 layer의 node라고 생각한다면 여러 layer의 여러 nodes의 acitivity값의 합(투표)으로 최종 class를 결정하니 비슷하네요)&lt;br /&gt;&lt;br /&gt;이&amp;nbsp;방법은&amp;nbsp;neutral&amp;nbsp;label을&amp;nbsp;사용하기&amp;nbsp;때문에&amp;nbsp;빠르지만&amp;nbsp;sub-optimal한&amp;nbsp;방법입니다.&amp;nbsp;그래서&amp;nbsp;논문에서는&amp;nbsp;input&amp;nbsp;image에&amp;nbsp;특정한&amp;nbsp;하나의&amp;nbsp;label을&amp;nbsp;가진&amp;nbsp;input을&amp;nbsp;사용하는&amp;nbsp;것이&amp;nbsp;좋다고&amp;nbsp;합니다.&amp;nbsp;0&amp;nbsp;label을&amp;nbsp;가진&amp;nbsp;image,&amp;nbsp;1&amp;nbsp;label을&amp;nbsp;가진&amp;nbsp;image,&amp;nbsp;....,&amp;nbsp;9&amp;nbsp;label을&amp;nbsp;가진&amp;nbsp;image를&amp;nbsp;개별적으로&amp;nbsp;넣어보고&amp;nbsp;더했을&amp;nbsp;때&amp;nbsp;가장&amp;nbsp;높은&amp;nbsp;gooodness를&amp;nbsp;가진&amp;nbsp;label을&amp;nbsp;최종&amp;nbsp;output&amp;nbsp;class&amp;nbsp;선택합니다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;추가적으로&amp;nbsp;해당&amp;nbsp;논문에서는&amp;nbsp;FF를&amp;nbsp;위한&amp;nbsp;data&amp;nbsp;augmentation방법인&amp;nbsp;image&amp;nbsp;jitttering을&amp;nbsp;제안하였습니다.&amp;nbsp;각&amp;nbsp;image마다&amp;nbsp;모든&amp;nbsp;방향으로&amp;nbsp;최대&amp;nbsp;2&amp;nbsp;pixels까지&amp;nbsp;shifting하여&amp;nbsp;총&amp;nbsp;25개의&amp;nbsp;다른&amp;nbsp;image를&amp;nbsp;생성하게&amp;nbsp;됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;802&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/briR1y/btrXoC2cNVs/gLU5wx742Ku7DzEdiRpBOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/briR1y/btrXoC2cNVs/gLU5wx742Ku7DzEdiRpBOK/img.png&quot; data-alt=&quot;Image jittering&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/briR1y/btrXoC2cNVs/gLU5wx742Ku7DzEdiRpBOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbriR1y%2FbtrXoC2cNVs%2FgLU5wx742Ku7DzEdiRpBOK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;238&quot; height=&quot;232&quot; data-origin-width=&quot;824&quot; data-origin-height=&quot;802&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Image jittering&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Image jittering을 통하여 pixel간의 spatial layout knowledge를 학습하도록 하게 하였고 결론적으로 permutation invariant을 없앴다고 합니다. 해당 augmentation과 함께 500 epochs을 학습하였을 때 CNN과 비슷한 test error인 0.64%을&amp;nbsp;&amp;nbsp;도출하였다고 합니다.&lt;br /&gt;&lt;br /&gt;그리고&amp;nbsp;흥미로운&amp;nbsp;결과로는&amp;nbsp;first&amp;nbsp;hidden&amp;nbsp;layer의&amp;nbsp;recpetive&amp;nbsp;field를&amp;nbsp;보았을&amp;nbsp;때&amp;nbsp;아래와&amp;nbsp;같이&amp;nbsp;image의&amp;nbsp;첫&amp;nbsp;10&amp;nbsp;pixels에서&amp;nbsp;class&amp;nbsp;label이&amp;nbsp;학습되는&amp;nbsp;것을&amp;nbsp;볼수&amp;nbsp;있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1160&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/boZY2U/btrXn6P0QrM/fKaK6HowMOioZnX53X94e1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/boZY2U/btrXn6P0QrM/fKaK6HowMOioZnX53X94e1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/boZY2U/btrXn6P0QrM/fKaK6HowMOioZnX53X94e1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FboZY2U%2FbtrXn6P0QrM%2FfKaK6HowMOioZnX53X94e1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;380&quot; data-origin-width=&quot;1554&quot; data-origin-height=&quot;1160&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI paper review</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/74</guid>
      <comments>https://da2so.tistory.com/74#entry74comment</comments>
      <pubDate>Sat, 28 Jan 2023 17:11:28 +0900</pubDate>
    </item>
    <item>
      <title>ML/DL Experiments and Analysis</title>
      <link>https://da2so.tistory.com/73</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;A.&amp;nbsp; Model &amp;amp; Module&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span data-slate-fragment=&quot;JTdCJTIyb2JqZWN0JTIyJTNBJTIyZG9jdW1lbnQlMjIlMkMlMjJkYXRhJTIyJTNBJTdCJTdEJTJDJTIybm9kZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJibG9jayUyMiUyQyUyMnR5cGUlMjIlM0ElMjJoZWFkaW5nLTMlMjIlMkMlMjJpc1ZvaWQlMjIlM0FmYWxzZSUyQyUyMmRhdGElMjIlM0ElN0IlN0QlMkMlMjJub2RlcyUyMiUzQSU1QiU3QiUyMm9iamVjdCUyMiUzQSUyMnRleHQlMjIlMkMlMjJsZWF2ZXMlMjIlM0ElNUIlN0IlMjJvYmplY3QlMjIlM0ElMjJsZWFmJTIyJTJDJTIydGV4dCUyMiUzQSUyMlB5cmFtaWQlMjBQb29saW5nJTIwTW9kdWxlJTIyJTJDJTIybWFya3MlMjIlM0ElNUIlNUQlMkMlMjJzZWxlY3Rpb25zJTIyJTNBJTVCJTVEJTdEJTVEJTJDJTIya2V5JTIyJTNBJTIyZGI4ZDlhNzgwMjVhNDZhMGJkMWYxYmQ3ZTZlYzExNTUlMjIlN0QlNUQlMkMlMjJrZXklMjIlM0ElMjI5ZjA0NGU4NThlZGI0NDcyYTZjNzZjNDQxODY1OGVlZiUyMiU3RCU1RCUyQyUyMmtleSUyMiUzQSUyMmY0OGViMDVmMTQxYTRkMWU5ZDRiNzU5NjU2OWM4MWE4JTIyJTdE&quot;&gt;&lt;a href=&quot;https://jiaya.me/papers/PSPNet_cvpr17.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Pyramid Pooling Module(PPM)&lt;/a&gt;: 기존의 local feature(b)와 pooling을 통한 global feature(c의 색깔 있는 output들)을 모두 학습하기 위함&lt;/span&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;서로 다른 kernel size로 여러 차례 avg pooling(논문에서 1x1, 2x2, 3x3, 6x6 kernel size 사용)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;1x1 size의 feature map은 가장 global feature이고 feature map size가 커질수록 local feature에 가까워짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span&gt;1x1 convolution을 통해 channel 수를 조정&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span&gt; pooling layer의 개수를 N이라고 할 때, &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;출력 channel 수 = 입력 채널 수 / N&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;input size에 맞춰 feature map을 upsample(bilinear interpolation)&lt;/li&gt;
&lt;li&gt;
&lt;div&gt;
&lt;div data-key=&quot;00923df94c0e4a98b760ef8b8cf11576&quot;&gt;
&lt;div&gt;&lt;span data-key=&quot;836ebd22cf4e4b8eb4f9f1c6404d53eb&quot;&gt;&lt;span data-offset-key=&quot;836ebd22cf4e4b8eb4f9f1c6404d53eb:0&quot;&gt;원래의 feature map과 생성된 새로운 feature map들을 concatenate&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;5104&quot; data-origin-height=&quot;1367&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qYTco/btrWq9gp2Le/sRukVTRG3BkDmcDB7X8M2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qYTco/btrWq9gp2Le/sRukVTRG3BkDmcDB7X8M2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qYTco/btrWq9gp2Le/sRukVTRG3BkDmcDB7X8M2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqYTco%2FbtrWq9gp2Le%2FsRukVTRG3BkDmcDB7X8M2k%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;643&quot; height=&quot;172&quot; data-origin-width=&quot;5104&quot; data-origin-height=&quot;1367&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;&lt;a href=&quot;https://arxiv.org/abs/1709.01507&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;SE(Squeeze &amp;amp; Excitation) Module&lt;/a&gt;: channel relationship에 초점을 맞추어 학습에 중요한 channel에 가중치를 주는 방법&amp;nbsp;&lt;br /&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;\(U \)의 \(C \)는 각각이 고유한 특징을 가짐, \( U \)에 대해 squeeze operation(global avg pool)을 진행하여 \( U\)를 대표하는 feature vector \( 1 \times 1 \times C \)을 뽑음&lt;/li&gt;
&lt;li&gt;Channel간의 relationship을 feature map \( U \)에 적용시키기위해 다음과 같이 Excitation operation 진행
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Excitation은 FC1(T) - ReLU(T) - FC2(C) - Sigmoid(C)로 구성 (각 layer옆은 output channel을 의미, \(T &amp;lt; C \))&lt;/li&gt;
&lt;li&gt;FC1 layer와 ReLU을 통해 \(C\)보다 작은 \(T\)채널로 수축시켜 channel간의 관계를 고려할수 있게 함&lt;/li&gt;
&lt;li&gt;FC2 layer를 통해 원래 채널수 \( C \)로 돌려놓고 sigmoid를 통해 가중치 형태인 0~1값 갖도록 하여 \( U \)에 곱해줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;287&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnYDqW/btrXg4efhfs/K5aJMbyxpHX7qkU2Fh9JD1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnYDqW/btrXg4efhfs/K5aJMbyxpHX7qkU2Fh9JD1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnYDqW/btrXg4efhfs/K5aJMbyxpHX7qkU2Fh9JD1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnYDqW%2FbtrXg4efhfs%2FK5aJMbyxpHX7qkU2Fh9JD1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;679&quot; height=&quot;203&quot; data-origin-width=&quot;959&quot; data-origin-height=&quot;287&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;B.&amp;nbsp; Loss&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Focal loss&lt;/span&gt;: CE에서 well-classified(easy sample)에 대해서는 loss를 더 작게 만들기 위해 \( (1 - p_t)^\gamma \)을 추가
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;아래는 label이 1인 경우의 loss식이며 \( p_t \)가 1에 가까울 수록 \( \gamma \)에 의해 loss가 exponential 하게 작아지게하여 상대적으로 easy sample의 loss를 CE때보다 급격히 줄임&lt;/li&gt;
&lt;li&gt;\( \gamma \)가 0일때 CE랑 같음&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KbIYm/btrVRCa1XRs/aPtz2XNnpwgmVTxEwsI1Zk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KbIYm/btrVRCa1XRs/aPtz2XNnpwgmVTxEwsI1Zk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KbIYm/btrVRCa1XRs/aPtz2XNnpwgmVTxEwsI1Zk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKbIYm%2FbtrVRCa1XRs%2FaPtz2XNnpwgmVTxEwsI1Zk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;423&quot; height=&quot;677&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;677&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;CIoU loss&lt;/span&gt;: 겹치는 영역(IoU), 중심점 사이의 거리, 종횡비 세가지 메트릭을 동시에 고려한 것 (DIoU의 확장버전)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( CIoU = 1- IoU + \frac{\rho^2(b, b^{gt})}{c^2} + \alpha v \)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;631&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QWKZR/btrVPuEI0X6/S4K3CiNO939coNhBRoR2A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QWKZR/btrVPuEI0X6/S4K3CiNO939coNhBRoR2A1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QWKZR/btrVPuEI0X6/S4K3CiNO939coNhBRoR2A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQWKZR%2FbtrVPuEI0X6%2FS4K3CiNO939coNhBRoR2A1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;187&quot; data-origin-width=&quot;1727&quot; data-origin-height=&quot;631&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;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;C. Optimizer&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;D.&amp;nbsp; Data Augmentation&lt;/b&gt;&lt;/h2&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://openaccess.thecvf.com/content_ICCV_2019/papers/Yun_CutMix_Regularization_Strategy_to_Train_Strong_Classifiers_With_Localizable_Features_ICCV_2019_paper.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;CutMix (CVPR 2019)&lt;/a&gt;: &lt;span style=&quot;color: #555555;&quot;&gt;모델이 객체의 차이를 식별할 수 있는 부분에 집중하지 않고, 덜 구별되는 부분 및 이미지의 전체적인 구역을 보고 학습도록 하여 일반화와 localization 성능을 높이는 방법.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #555555;&quot;&gt; OOD(out-of-distribution)와 이미지가 가려진 sample, adversarial sample에서의 robustness도 좋은 성능&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/btZzku/btrVOSTlHb8/H6jdkwcIWSPgWKS6R9uTZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/btZzku/btrVOSTlHb8/H6jdkwcIWSPgWKS6R9uTZK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/btZzku/btrVOSTlHb8/H6jdkwcIWSPgWKS6R9uTZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbtZzku%2FbtrVOSTlHb8%2FH6jdkwcIWSPgWKS6R9uTZK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;383&quot; height=&quot;270&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;666&quot; data-origin-height=&quot;470&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;&lt;a href=&quot;https://openaccess.thecvf.com/content/CVPR2021/papers/Ghiasi_Simple_Copy-Paste_Is_a_Strong_Data_Augmentation_Method_for_Instance_CVPR_2021_paper.pdf&quot;&gt;Copy &amp;amp; Paste (CVPR 2021)&lt;/a&gt; : Segmentaiton에서 사용가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1646&quot; data-origin-height=&quot;774&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6OBHn/btrVKZF2EcB/HkPsPYKS2jnNAreEk4h7Sk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6OBHn/btrVKZF2EcB/HkPsPYKS2jnNAreEk4h7Sk/img.png&quot; data-alt=&quot;Copy and Paste&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6OBHn/btrVKZF2EcB/HkPsPYKS2jnNAreEk4h7Sk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6OBHn%2FbtrVKZF2EcB%2FHkPsPYKS2jnNAreEk4h7Sk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;535&quot; height=&quot;774&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1646&quot; data-origin-height=&quot;774&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Copy and Paste&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;E. Engineering&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Pytorch training 최적화: &lt;a href=&quot;https://da2so.tistory.com/70&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;요기&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Pytorch output slicing을 통한 loss 계산시(graident에 사용되는) 주의점
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;If the shape of output is (4(B), 2(C), 320(W), 320(H)]&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Wrong&lt;/b&gt; &amp;rarr; out1 = output[:, 0, :, :], out2 = output[:, 1, :, :]&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Correct&lt;/b&gt; &amp;rarr; out1 = output[:, :1, :, :], out2 = output[:, 1:, :, :]&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SOD에서 encoder(i.e. resnet, efficientnet)의 low level feature는 너무 많은 details을 가지는 반면에 high level feature는 rough한 결과를 내뽑음 (Reference: &lt;a href=&quot;https://openaccess.thecvf.com/content_CVPR_2019/papers/Zhao_Pyramid_Feature_Attention_Network_for_Saliency_Detection_CVPR_2019_paper.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Pyramid Feature Attention Network for Saliency detection&lt;/a&gt;)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Low level feature에는 detail이 많고 sod는 boundary를 찾는 게 목적이니 spatial attention을 사용&lt;/li&gt;
&lt;li&gt;High level feature에는 rough한 영역이 많으니 channel attention을 통해 high response를 내는 channel에 가중치 줌 (salient object찾는데 좋음)&lt;/li&gt;
&lt;li&gt;Low level feature + high level feature를 aggregation하고 channel attention, spatial attention한 논문이 &lt;a href=&quot;https://arxiv.org/abs/2112.07380&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Tracer&lt;/a&gt;(AAAI 2022)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cz0bUc/btrXmj9U5a9/Kk1JkrZM6gFL2rFQiAsBY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cz0bUc/btrXmj9U5a9/Kk1JkrZM6gFL2rFQiAsBY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cz0bUc/btrXmj9U5a9/Kk1JkrZM6gFL2rFQiAsBY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcz0bUc%2FbtrXmj9U5a9%2FKk1JkrZM6gFL2rFQiAsBY0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;622&quot; height=&quot;193&quot; data-origin-width=&quot;972&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI Engineering</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/73</guid>
      <comments>https://da2so.tistory.com/73#entry73comment</comments>
      <pubDate>Tue, 10 Jan 2023 10:37:06 +0900</pubDate>
    </item>
    <item>
      <title>Per-title Encoding 설명</title>
      <link>https://da2so.tistory.com/72</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;※&lt;/b&gt; &lt;a href=&quot;https://netflixtechblog.com/per-title-encode-optimization-7e99442b62a2&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 블로그&lt;/a&gt;를 reference하였습니다.&amp;nbsp;&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;Per-title Encoding은 Netflix에서 제안한 video encoding방식입니다. 말 그대로 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;per-title encoding은 title에 따라 encoding을 다르게 하겠다&lt;/b&gt;&lt;/span&gt;라는 말입니다. 이는 video가 속한 title(category)에 따라 특성이 다르다는 것을 의미하며 title에 따라 encoding 압축율을 다르게 하겠다는 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. 기존의 Encoding 방식&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Netflix에서는 2010년 후반부터 H.264/AVC를 사용하기 시작하면서 engineer들은 각 resolution에 따라 경험적으로 optimal한 bitrate를 찾는 데 수많은 실험을 하였습니다. Codec parameters(e.g. crf, QPs, resolution, profile)을 바꿔가면서 compression rate(bitrate)과 visual quality의 trade-off을 최소화 시킬수 있는 bitrate-resoultion pairs(&lt;b&gt;bitrate ladder &lt;/b&gt;라고 명명)을 아래 표와 같이 찾아 내었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;1740&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cx3EbL/btrSeOfi5Md/cK0ALLaPR719n8a8XRhWsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cx3EbL/btrSeOfi5Md/cK0ALLaPR719n8a8XRhWsk/img.png&quot; data-alt=&quot;Fixed bitrate ladder&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cx3EbL/btrSeOfi5Md/cK0ALLaPR719n8a8XRhWsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcx3EbL%2FbtrSeOfi5Md%2FcK0ALLaPR719n8a8XRhWsk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;252&quot; height=&quot;386&quot; data-origin-width=&quot;1136&quot; data-origin-height=&quot;1740&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Fixed bitrate ladder&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 표를 해석하면 320x240 resolution으로 encoding하기에는 235 kbps bitrate면 충분하다라는 의미입니다. 위 표처럼 resolution에 따라 bitrate가 정해진 표를 fixed bitrate ladder라고 합니다.&amp;nbsp;&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;하지만 하나의 resolution에 대해 동영상들을 모두 같은 bitrate로 압축하는 것은 각 동영상들의 특성을 고려하지 않는 것이며 이는 압축 효율이 떨어진다고 말할 수 있습니다. 그래서 &lt;i&gt;동영상마다의 특성을 고려해서 bitrate ladder을 구해보자!&lt;/i&gt; 라는 생각에서 Per-title encoding이 개발되었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Per-title Encoding 이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Animation이라는 title을 가진 동영상의 경우 대부분 content가 simple합니다. Simple하다는 것은 flat region이 많으며 frame사이의 minimal한 motion만 존재한다는 뜻입니다. 이러한 특성때문에 animation이라는 title을 가진 동영상은 low bitrate를 가져도 visual quality(e.g. PSNR, VMAF)가 크게 낮아지지 않습니다. 그래서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;title마다 성격을 고려하여 bitrate ladder를 구성하는 것이 per-title encoding&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Algorithm&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;per-title encoding bitrate ladder을 구성하기 위해 각 title category마다 아래와 같은 finite set으로 encoding configuration을 제한하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Resolution&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;1920x1080,&amp;nbsp;&lt;span style=&quot;color: #292929;&quot;&gt;1280&amp;times;720, 720&amp;times;480, 512&amp;times;384, 384&amp;times;288 and 320&amp;times;240&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;QPs&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bitrate간의 간격이 5%정도가 될 수 있도록 하는 QPs선택
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;bitrate interval examples: 100, 105, 110.25, ...&amp;nbsp;&lt;/li&gt;
&lt;li&gt;5%의 차이가 시각적인 JND(Just Noticeable Difference)를 느낄 수 있는 수치임&lt;br /&gt;(1 JND가 vmaf값 6정도차이를 말함)&lt;/li&gt;
&lt;li&gt;정확히 어떤 QPs값을 사용하였는지는 모름...ㅠ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 셋팅으로 encoding진행하였을 때 아래와 비슷한 PSNR(quality)-bitrate rate 그래프를 얻을 수 있었습니다. (아래는 설명을 위한 예시 그래프입니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzB73V/btrSM5f2UK9/e2zEjeI4h3z2yX5HUkpkik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzB73V/btrSM5f2UK9/e2zEjeI4h3z2yX5HUkpkik/img.png&quot; data-alt=&quot;파란색 점: encoding point, 빨간색 커브: the PSNR-bitrate convex hull.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzB73V/btrSM5f2UK9/e2zEjeI4h3z2yX5HUkpkik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzB73V%2FbtrSM5f2UK9%2Fe2zEjeI4h3z2yX5HUkpkik%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;466&quot; height=&quot;326&quot; data-origin-width=&quot;1098&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;파란색 점: encoding point, 빨간색 커브: the PSNR-bitrate convex hull.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PSNR과 bitrate의 trade off관계를 봤을때 위 사진의 빨간색 커브에 속하는 점들이 optimal encoding configuration이고 A, B점은 sub-optimal한 점들입니다. (위 그래프의 한정으로) 1920x1080 resolution으로 높은 range의 bitrate(1500~3000kbps)에 대해 encoding할 경우 optimal하지만 낮은 range의 bitrate(0~1000kbps)에 대해서는 1280x720이나 720x480 resolution이 optimal한 점입니다.&amp;nbsp;&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;이러한 특성을 종합하였을 때 결론적으로 quality-bitrate relationship은 아래와 같은 형태를 띈다고 실험적으로 증명하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;468&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bulwl3/btrSLJdjjsJ/aICD289IPlumnlvg1zVFsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bulwl3/btrSLJdjjsJ/aICD289IPlumnlvg1zVFsk/img.png&quot; data-alt=&quot;Convex hull for bitrate-quality graph&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bulwl3/btrSLJdjjsJ/aICD289IPlumnlvg1zVFsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbulwl3%2FbtrSLJdjjsJ%2FaICD289IPlumnlvg1zVFsk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;389&quot; height=&quot;238&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;468&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Convex hull for bitrate-quality graph&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Convex hull에 포함되는 점들이 optimal한 encoding configuration이고 convex hull points들은 Pareto efficiency(trade-off관계를 가진다고 이해)를 가집니다. 그래서 Per-title encoding에서는 convex hull에 포함되는 bitrate-resoultion pair를 선택할 수 있도록 하였습니다.&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;최종적으로 algorithm process를 정리하면 다음과 같습니다.&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;b&gt;Title별로 video를 분류&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;위에서 언급한 finite한 resolution, QPs set으로 encoding하여 bitrate와 quality을 측정&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;bitrate-quality relationship그래프에서 각 candidate resolution마다 convex hull에 근접한 point들을 골라 bitrate ladder를 구성함&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.2&lt;span&gt; Q&lt;/span&gt;&lt;/span&gt;uantitative Results&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Fixed QP encoding과 비교하였을 때 Per-title Encoding의 성능개선 결과를 정량적으로 보여드립니다. 특히나, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Anmation title&lt;/b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;(flat region이 많고 frame간의 움직임이 적음)&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;을 가진 video에 대한 per-title encoding성능을 측정 및 비교하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;438&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UMKnF/btrSIRDBA7Y/jfxjlmvm43Bpw1wscJWyK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UMKnF/btrSIRDBA7Y/jfxjlmvm43Bpw1wscJWyK0/img.png&quot; data-alt=&quot;Per-title encoding performance for an animation title&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UMKnF/btrSIRDBA7Y/jfxjlmvm43Bpw1wscJWyK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUMKnF%2FbtrSIRDBA7Y%2Fjfxjlmvm43Bpw1wscJWyK0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;430&quot; height=&quot;302&quot; data-origin-width=&quot;624&quot; data-origin-height=&quot;438&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Per-title encoding performance for an animation title&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;1920x1080 resolution기준으로 per-title encoding의 A는&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;2350 kbps로 encoding했을때 PSNR가 46정도로 high visual quality를 가지지만 fixed QP encoding의 B나 C는 A보다 bitrate는 훨씬 높지만 비슷한 visual quality을 가집니다. 또한 A와 D를 비교했을 때 fixed QP encoding의 D는 A와 비슷한 bitrate를 가지지만 PSNR점수가 훨씬 낮으므로 per-title encoding이 fixed QP encoding보다 좋은 성능을 내었음을 알수 있습니다.&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;&lt;b&gt;2.3 Qualitative Results&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;814&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/diT5A4/btrSIp8tFj2/ucnVNP4RBsrOUKV24bafrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/diT5A4/btrSIp8tFj2/ucnVNP4RBsrOUKV24bafrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/diT5A4/btrSIp8tFj2/ucnVNP4RBsrOUKV24bafrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdiT5A4%2FbtrSIp8tFj2%2FucnVNP4RBsrOUKV24bafrK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;481&quot; height=&quot;332&quot; data-origin-width=&quot;1180&quot; data-origin-height=&quot;814&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;(왼쪽)&lt;/i&gt; fixed bitrate ladder으로 480p resolution에 대해 encoding한 경우 1750 kbps의 bitrate를 가지지만 &lt;i&gt;(오른쪽)&lt;/i&gt; per-title bitrate ladder을 사용시에는 1080p resolution에 대해 1540 kbps의 bitrate를 가집니다. 즉, per-title bitrate ladder를 사용하였을 경우 더 좋은 visual quality와 compression efficiency을 가지는 것을 볼 수 있습니다.&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/72</guid>
      <comments>https://da2so.tistory.com/72#entry72comment</comments>
      <pubDate>Sat, 3 Dec 2022 19:44:54 +0900</pubDate>
    </item>
    <item>
      <title>PyTorch training/inference 성능 최적화 (2/2)</title>
      <link>https://da2so.tistory.com/71</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://da2so.tistory.com/70&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이전 글&lt;/a&gt;에서 Pytorch framework에서 성능 최적화하는 방법을 소개해드렸습니다. 이번 글에서는 설명드린 각 방법들이 얼마만큼 time cost 성능 최적화가 되는지 실험해보도록 하겠습니다. 실험 코드는 &lt;a href=&quot;https://github.com/da2so/da2so_tutorial/tree/main/pytorch_optimization&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&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;실험해볼 최적화 방법 목록입니다.&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;b&gt;Data Loading 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;num worker 설정&lt;/li&gt;
&lt;li&gt;pinned memory 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Data Operation 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;tensor.to(non_blocking=True) 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Training 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Architecture design과 batch size를 8의 배수로 설정&lt;/li&gt;
&lt;li&gt;Mixed Precision Training 사용&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Optimizer로 weight를 update하기 전에 gradient을 None으로 설정&lt;/li&gt;
&lt;li&gt;Gradient accumulation 사용&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Inference 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Inference시에 gradient calculation 끄기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CNN 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;torch.backends.cudnn.benchmark = True 사용&lt;/li&gt;
&lt;li&gt;4D NCHW tensors에 대해 channel_last memory format를 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;0. 실험 환경&lt;/b&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Device 및 PyPI&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CPU&lt;/b&gt;: Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz (가상 core수:&amp;nbsp;56)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GPU&lt;/b&gt;: Tesla V100&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CUDA&lt;/b&gt;: 11.2&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Driver Version&lt;/b&gt;: 460.73.01&lt;/li&gt;
&lt;li&gt;&lt;b&gt;torch: &lt;/b&gt;1.8.1&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Dataset&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;CIFAR10
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shape: 32(H)x32(W)x3(C)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Model&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;ResNet18, 50, 101&lt;/li&gt;
&lt;li&gt;MobileNetv2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기본 Config&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;num_workers=4, pin_memory=True&lt;/li&gt;
&lt;li&gt;batch_size=128&lt;/li&gt;
&lt;li&gt;epochs=5&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #5733b1;&quot;&gt;&lt;b&gt;※ 모든 실험의 결과의 단위는 초(s)이며 5번의 epoch에 대한 평균치를 낸 것입니다. (첫 epoch제외)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. DataLoading 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 &amp;amp; 1.2 num_workers와 pinned_memory의 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;num_workers와 pinned_memory 설정은 각각 단독으로 사용하기보다는 같이 사용합니다. 그렇기 때문에 두 설정을 동시에 사용했을 때와 그렇지 않을 때의 성능 차이를 보도록 하겠습니다. 사용 방법은 아래와 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1668518707962&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Use num_workers=4 and pin_memory=True
Dataloader(dataset, num_workers=4*num_GPU, pin_memory=True)&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.6279%; height: 94px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;num_workers=0,&lt;/b&gt;&lt;br /&gt;&lt;b&gt;pin_memory=False&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 36.5s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 2.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 74.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.1s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 113.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 7.3s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 35.8s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 2.8s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;num_workers=4,&lt;/b&gt;&lt;br /&gt;&lt;b&gt;pin_memory=True&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.5s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.5s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 58.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 96.8s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.1s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;width: 20%; height: 36px; text-align: center;&quot;&gt;&lt;b&gt;num_workers=8,&lt;/b&gt;&lt;br /&gt;&lt;b&gt;pin_memory=True&lt;br /&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 36px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 36px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 58.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 36px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 97.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 6.0s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 36px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.5s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.7s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 분석하자면 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;num_workers와 pin_memory 설정을 사용하였을 때 time cost가 줄어든 것을 확인 가능&lt;/li&gt;
&lt;li&gt;data loading에 최적화된 방법이므로 model에 상관없이 고정된 time cost 성능 효과를 보임&lt;/li&gt;
&lt;li&gt;(GPU 1개 기준) num_workers의 optimal한 값은 4이고 그 이상인 경우(i.e. 8) 성능 효과가 보이지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Data operation 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 tensor.to(non_blocking=True) 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;non_blocking에 대한 설정은 아래와 같이 input, target(label)에서 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1668519467433&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for input, target in Dataloader:
    # 아래 2 lines을 통해 non-blocking과 overlapping이 진행
    input = input.to('cuda:0', non_blocking=True)
    target = target.to('cuda:0', non_blocking=True)
    
    # 해당 구간에서 input과 target의 변수가 사용되지 않는 선에서 코딩을 할경우
    # 비동기적으로 실행되므로 execution time을 줄일 수 있음 
    
    # synchronization시점으로 위의 2 lines을 기다리는 구간
    output = model(input)&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.6279%; height: 58px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;w/o non_blocking&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.4s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 58.1s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 96.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.1s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;w non_blocking&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.4s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 57.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 97.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p 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&gt;성능 효과가 없는 것으로 보임&lt;/li&gt;
&lt;li&gt;구글링해보니 설정으로 time cost성능 효과를 내지 못한 경우가 있다고 함..ㅠ
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이에 대해 더 궁금한 점은 &lt;a href=&quot;https://discuss.pytorch.org/t/should-we-set-non-blocking-to-true/38234&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;패트릭형님의 답변&lt;/a&gt;을 보세용.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Training 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Architecture design과 batch size를 8의 배수로 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 network보다 input, output channel을 1씩 줄이고 batch size는 1을 올려서 실험하였습니다. (&lt;span&gt;이에 해당하는 모델들은 아래 표에서 rec model이라고 명명하고 기존 모델을 base model이라고 칭하겠습니다.) &lt;/span&gt;일반적으로 생각하면 batch가 클수록 channel수가 작을수록(model이 작을수록) time cost가 줄어야 하지만 해당 최적화 방법에 근거하면 batch size와 channel수가 8의 배수가 아닐 경우 NVIDIA GPU 최적화가 되어있지 않아 있으므로 time cost가 늘어나게 됩니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.6279%; height: 58px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;base model&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.4s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.5s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 57.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 96.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 19.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;rec model&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 22.7s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.5s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 59.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.7s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 103.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p 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&gt;batch size가 커지고 channel수가 줄어듬에도 불구하고 training time이 느려지는 것을 확인 가능!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Mixed Precision Training 사용&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1669101486775&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch

scaler = torch.cuda.amp.GradScaler() # Training시에 생성

for data, label in data_iter:
   optimizer.zero_grad()
   with torch.cuda.amp.autocast(): # Mixed precision으로 operation들을 casting 
      outputs = model(data)

   scaler.scale(loss).backward() # Loss를 scaling한 후에 backward진행
   scaler.step(optimizer) # 원래 scale에 맞추어 gradient를 unscale하고 optimizer를 통한 gradient update
   scaler.update() # 다음 iteration을 위해 scale update&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 95.2326%; height: 140px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;w/o mixed precision&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.4s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 58.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 97.1s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.4s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.5s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;w mixed precision&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 10.4s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 25.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 43.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 22.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.5s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과를 분석하면 다음과 같습니다. (해당 방법은 training time성능에만 영향을 미침)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt; mixed precision사용 시 50%정도의 training time cost성능 향상을 보임&lt;/li&gt;
&lt;li&gt;MobileNetv2과 같은 depthwise conv가 있는 경우 또는 작은 model인 경우에는 time cost가 줄어들지 않고 늘어나는 것으로 추측&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 Optimizer로 weight를 update하기 전에 gradient을 None으로 설정&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1669101468973&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# gradient를 None으로 설정 (PyTorch &amp;gt;= 1.7)
optimizer.zero_grad(set_to_none=True)&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.6279%; height: 58px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;w/o gradient none&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.5s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 58.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.5s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 97.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.3s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;w gradient none&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 57.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.7s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 95.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 19.6s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과를 분석하면 다음과 같습니다. (해당 방법은 training time성능에만 영향을 미침)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델이 작을 경우 time이 거의 줄지 않지만 모델이 커질수록 해당 설정으로 인한 time cost성능 향상 효과를 보임&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.4 Gradient accumulation 사용&amp;nbsp;&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1669101989928&quot; class=&quot;sas&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i, (input, target) in enumerate(dataloader):
    output = model(features)
    loss = criterion(output, target)
    loss.backward()
    
    # 매 2번의 iteration이 끝난 뒤에 weight를 update하여 batch size가 doubled되어 학습하는 효과를 줌 
    if (i+1) % 2 == 0 or (i+1) == len(dataloader):
        optimizer.step() # weight update
        optimizer.zero_grad(set_to_none=True)&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.6279%; height: 58px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;w/o gradient &lt;br /&gt;accumulation&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 57.5s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 96.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 19.8s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;w gradient &lt;br /&gt;accumulation&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 19.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 56.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 95.3s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 19.4s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과를 분석하면 다음과 같습니다. (해당 방법은 training time성능에만 영향을 미침)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델에 커짐에 따라 조금의 time cost향상이 보임
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;위의 근거를 뒷받침하기에는 실험이 적으므로 더 큰 모델로 실험해볼 필요가 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Inference 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1 Inference시에 gradient calculation 끄기&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1669102194288&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# inference코드에서 (decorator) torch.no_grad() 사용
@torch.no_grad()
def validation(model, input):
    output = model(input)
return output&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.6279%; height: 58px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;w/o no_grad&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;w no_grad&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;test&lt;/b&gt;: 5.7s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과를 분석하면 다음과 같습니다. (Inference에 대한 최적화이므로 test에 대한 수치만 표시함)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;time cost의 성능에 향상은 없다고 보임&lt;/li&gt;
&lt;li&gt;아마.. memory cost측면에서 성능 개선이 있을 것으로 추측&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. CNN 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.1 torch.backends.cudnn.benchmark = True 사용&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1669103073692&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;torch.backends.cudnn.benchmark = True&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.6279%; height: 58px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;w/o cudnn.&lt;br /&gt;benchmark&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 55.1s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 93.8s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.7s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 19.8s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;w cudnn.&lt;br /&gt;benchmark&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 55.2s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 93.8s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 19.7s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p 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&gt;cudnn.benchmark사용설정을 해도 time cost성능 효과는 없어 보입니다..ㅠㅠ&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.2 4&lt;span style=&quot;color: #000000;&quot;&gt;D NCHW tensors에 대해 channel_last memory format를 사용&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1669103266955&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;inputs = inputs.to(self.device, memory_format=torch.channels_last)&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 91.6279%; height: 58px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style3&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;w/o channel_last&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 10.4s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 26.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 46.3s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8 s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 22.1s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;w channel_last&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 10.0s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 22.8s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 40.8s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 20.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 결과를 분석하면 다음과 같습니다. (test때는 channel last를 안썻습니다..ㅋㅋ)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델의 크기가 증가할수록 memory format의 설정이 time cost를 줄이는 데 효과적임&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/PyTorch</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/71</guid>
      <comments>https://da2so.tistory.com/71#entry71comment</comments>
      <pubDate>Tue, 22 Nov 2022 23:09:46 +0900</pubDate>
    </item>
    <item>
      <title>PyTorch training/inference 성능 최적화 (1/2)</title>
      <link>https://da2so.tistory.com/70</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 해당 블로그의 내용을 베이스로 하여 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;PyTorch framework에서 training/inference 성능 최적화를 하는 것을 목적&lt;/b&gt;&lt;/span&gt;으로 설명드릴 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능이라 함은&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt; 1.&lt;/span&gt; speed&lt;span style=&quot;color: #000000;&quot;&gt;,&lt;/span&gt; &lt;span style=&quot;color: #1b711d;&quot;&gt;2.&lt;/span&gt; memory&lt;/b&gt;&lt;/span&gt;에 대한 성능을 뜻합니다. speed에 대한 성능이 좋다함은 training 및 inference time cost가 적다는 것이고 memory에 대한 성능이 좋다는 것은 training 및 inference에 사용되는 memory가 적다는 것입니다.&amp;nbsp;&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;오늘 소개할 최적화 방법에 대한 목록은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Data Loading 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;num worker 설정&lt;/li&gt;
&lt;li&gt;pinned memory 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Data Operation 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;torch.Tensor 사용과 device 할당&lt;/li&gt;
&lt;li&gt;CPU와 GPU간의 data transfer 줄이기&lt;/li&gt;
&lt;li&gt;tensor.to(non_blocking=True) 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Training 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Architecture design과 batch size를 8의 배수로 설정&lt;/li&gt;
&lt;li&gt;Mixed Precision Training 사용&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Optimizer로 weight를 update하기 전에 gradient을 None으로 설정&lt;/li&gt;
&lt;li&gt;Gradient accumulation 사용&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Inference 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Inference시에 gradient calculation 끄기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CNN 최적화&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;torch.backends.cudnn.benchmark = True 사용&lt;/li&gt;
&lt;li&gt;4D NCHW tensors에 대해 channel_last memory format를 사용&lt;/li&gt;
&lt;li&gt;Conv-BN 구조에서 Conv의 bias 사용하지 않기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1.&amp;nbsp; Data Loading 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt; 1.1 num worker 설정 (time cost &amp;darr;)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dataloader의 parameter인 num_workers는 data loading 및 augmentation을 cpu작업을 통해 하는 데 몇 개의 cpu core를 사용할 것인지 결정합니다. &lt;code&gt;num_workers=0&lt;/code&gt; 은 weight update나 전에 실행되었던 process가 끝난 뒤에만 data loading을 하게 됩니다. 이는  동기적(synchronuous)으로 작동하기 때문에 speed성능측면에서 좋지 않습니다. 그래서 &lt;code&gt;num_workers &amp;gt;0&lt;/code&gt;으로 설정 하여 data loading 및 augmentation 작업이 비동기적(asynchronuous)으로 가능해지기 때문에 training시에 time cost를 줄이게 됩니다. 그렇다고 num_workers의 값을 너무 크게 준다면 memory 사용에 overhead를 주기 때문에 &lt;code&gt;num_workers=4*num_GPU&lt;/code&gt;가 실험적으로 적절한 값이라고 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667820479146&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dataloader(dataset, num_workers=4*num_GPU)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.2 pinned memory 사용 &lt;b&gt;(time cost &amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 왼쪽 사진과 같이 GPU는 CPU의 pageable memory에 direct로 접근이 불가하며 staging memory(a.k.a pinned memory)를 거쳐서 data에 접근가능합니다. 이렇게 거쳐서 간다면 time cost가 오르겠죠. 해당 문제를 해결하기 위해 pin_memory=True를 사용하여 data를 CPU위의 staging memory(a.k.a pinned memory)에 할당합니다. 이렇게 되면 pageable memory가 staging memory를 거쳐가는(transfer) 시간을 줄이게 됩니다. 해당 옵션은 위의 num_worker와 같이 사용되는 파라미터입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;pin_memory.png&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yhTsi/btrQDwah7H6/lDwHLFeCQkTh2csCFKTQVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yhTsi/btrQDwah7H6/lDwHLFeCQkTh2csCFKTQVk/img.png&quot; data-alt=&quot;https://miro.medium.com/max/1400/1*M8mejDZ5WbnFl8h59UfjCg.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yhTsi/btrQDwah7H6/lDwHLFeCQkTh2csCFKTQVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyhTsi%2FbtrQDwah7H6%2FlDwHLFeCQkTh2csCFKTQVk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;475&quot; height=&quot;301&quot; data-filename=&quot;pin_memory.png&quot; data-origin-width=&quot;905&quot; data-origin-height=&quot;574&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://miro.medium.com/max/1400/1*M8mejDZ5WbnFl8h59UfjCg.png&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1667866517934&quot; class=&quot;reasonml&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Dataloader(dataset, pin_memory=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Data Operation 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 torch.Tensor 사용과 device 할당 &lt;b&gt;&lt;b&gt;(time cost &amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Data를 정의하거나 만들때 torch.Tensor를 사용하고 device를 torch.Tensor사용시에 할당하는 것이 효율적이다. 반대로 말하면 data를 정의할때 Python이나 Numpy를 사용해서 만들지 말라는 것이다.&amp;nbsp; 모델을 학습할 경우 대부분 GPU를 통해 학습할텐데 Python이나 Numpy를 통해 만들고 torch.Tensor로 transfer한다면 CPU로 만들고 GPU로 변환하는 과정을 겪기때문에 time cost가 더 늘어난다. 하지만 torch.Tenosr로 즉시 GPU device에 할당하여 data를 정의한다면 time cost가 최적화된다.&lt;/p&gt;
&lt;pre id=&quot;code_1667867148459&quot; class=&quot;angelscript&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# np.random.rand([10,5])와 같음
tensor = torch.rand([10, 5], device=torch.device('cuda:0'))

# np.random.randn([10,5])와 같음
tensor = torch.randn([10, 5], device=torch.device('cuda:0'))&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 CPU와 GPU간의 data transfer 줄이기 &lt;b&gt;&lt;b&gt;(time cost &amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I/O cost를 최대한 줄이기위해 아래와 같은 CPU와 GPU간의 data transfer를 자제하는 것이 좋습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1667870530028&quot; class=&quot;less&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# BAD! AVOID THEM IF UNNECESSARY!
print(cuda_tensor)
cuda_tensor.cpu()
cuda_tensor.to_device('cpu')
cpu_tensor.cuda()
cpu_tensor.to_device('cuda')
cuda_tensor.item()
cuda_tensor.numpy()
cuda_tensor.nonzero()
cuda_tensor.tolist()&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 tensor.to(non_blocking=True) 사용 &lt;b&gt;&lt;b&gt;(time cost &amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진과 같이 &lt;code&gt;tensor.to(non_blocking=True)&lt;/code&gt;으로 설정하면 data transfer가 비동기적으로 진행되어 execution time을 줄일 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;non_blocking.png&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;581&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOSNIH/btrQKbYOi0M/E673e9mb4fRZx4U8VNTk91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOSNIH/btrQKbYOi0M/E673e9mb4fRZx4U8VNTk91/img.png&quot; data-alt=&quot;https://miro.medium.com/max/1390/1*no-gQHz8daJbmYhCfAGNOA.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOSNIH/btrQKbYOi0M/E673e9mb4fRZx4U8VNTk91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOSNIH%2FbtrQKbYOi0M%2FE673e9mb4fRZx4U8VNTk91%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;378&quot; height=&quot;316&quot; data-filename=&quot;non_blocking.png&quot; data-origin-width=&quot;695&quot; data-origin-height=&quot;581&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://miro.medium.com/max/1390/1*no-gQHz8daJbmYhCfAGNOA.png&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1667974580233&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for input, target in Dataloader:
    # 아래 2 lines을 통해 non-blocking과 overlapping이 진행
    input = input.to('cuda:0', non_blocking=True)
    target = target.to('cuda:0', non_blocking=True)
    
    # 해당 구간에서 input과 target의 변수가 사용되지 않는 선에서 코딩을 할경우
    # 비동기적으로 실행되므로 execution time을 줄일 수 있음 
    
    output = model(input)# synchronization시점으로 위의 2 lines을 기다리는 구간&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Training 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Architecture design과 batch size를 8의 배수로 설정 &lt;b&gt;&lt;b&gt;(time cost &amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GPU의 computation efficiency를 최대화 하기위해서는 모델의 input과 output의 size, channel 수, batch size모두를 8의 배수로 설정해야한다. 그 이유로는 Nvidia GPU의 Tensor core들이 8의 배수로 matrix로 align되어있을때 가장 optimal한 성능을 내기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.nvidia.com/deeplearning/performance/dl-performance-fully-connected/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 실험&lt;/a&gt;에서 보이듯이 output size와 batch size를 8의 배수(i.e. 33712, 4088, 4096)으로 설정하였을 때 8의 배수가 아닌 수(i.e. 33708, 4084, 4095)로 설정하였을 때보다 1.3~4배정도 computation이 빨랐다고 합니다. 이렇게 속도 차이를 나게하는 주 component는 process type(e.g. forward pass, gradient calculation)와 cuBLAS version입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Mixed Precision Training 사용&amp;nbsp;&lt;b&gt;(time, memory cost&amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mixed Precision Training은 single-precision(FP32)와 half-precision(FP16) format을 결합하여 사용하여 training하는 방식을 말합니다. 기존의 FP32만 사용하는 방식보다 data size가 작은 FP16을 섞어 사용하기 때문에 memory 사용이나 training 속도면에서도 이득을 취할 수 있습니다.&amp;nbsp;&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;해당 방법에 대한 자세한 내용은 &lt;a href=&quot;https://da2so.tistory.com/67&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이전 글&lt;/a&gt;에서 읽어보시고 사용하시면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 Optimizer로 weight를 update하기 전에 gradient을 None으로 설정 &lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;(time cost&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존처럼 &lt;code&gt;model.zero_grad()&lt;/code&gt;나 &lt;code&gt;optimizer.zero_grad()&lt;/code&gt;함수를 통해 gradient를 0으로 설정하는 것은 모든 파라미터에 &lt;code&gt;memset&lt;/code&gt;을 실행시키고 reading과 writing operations으로 gradient을 update하는 것이다. 하지만 graident를 None으로 설정하게 되면 &lt;code&gt;memse&lt;/code&gt;함수를 실행하지 않고 writing operation만으로 gradient를 update가능하다. 그래서 &lt;code&gt;optimizer.zero_grad()&lt;/code&gt;를 사용하는 것보다 gradient를 None으로 설정하는 것이 더 빠르다.&amp;nbsp;&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# gradient를 None으로 설정 (PyTorch &amp;lt; 1.7)
for param in model.parameters():
    param.grad = None

# gradient를 None으로 설정 (PyTorch &amp;gt;= 1.7)
optimizer.zero_grad(set_to_none=True)&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.4 Gradient accumulation 사용 &lt;b&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;(time cost&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gradient accumulation은 한 batch에서 계산된 loss을 통해 바로 gradient를 update하는 것이 아닌 여러 batch으로부터 gradient을 쌓은(accumulation) 뒤에 gradient를 update하는 방법이다. 이는 Input data의 size가 너무 크거나 GPU memoy가 작아서 batch size를 작게 설정하였을 때 사용하면 time cost는 줄일 수 있고 accuracy성능은 올릴 수 있는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1668238834369&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for i, (input, target) in enumerate(dataloader):
    output = model(features)
    loss = criterion(output, target)
    loss.backward()
    
    # 매 2번의 iteration이 끝난 뒤에 weight를 update하여 batch size가 doubled되어 학습하는 효과를 줌 
    if (i+1) % 2 == 0 or (i+1) == len(dataloader):
        optimizer.step() # weight update
        optimizer.zero_grad(set_to_none=True)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Inferecne 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1 Inference시에 gradient calculation 끄기 &lt;b&gt;&lt;b&gt;(time, memory cost&amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;inference시에는 training하는 것이 아니므로 gradient에 대한 계산이 불필요하므로 gradient-involved된 operation을 disable시킨다.&lt;/p&gt;
&lt;pre id=&quot;code_1668239165424&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# inference코드에서 (decorator) torch.no_grad() 사용
@torch.no_grad()
def validation(model, input):
    output = model(input)
return output&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. CNN 최적화&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.1 torch.backends.cudnn.benchmark = True 사용 &lt;b&gt;&lt;b&gt;&lt;b&gt;(time cost&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Training loop전에 &lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; &lt;code&gt;torch.backends.cudnn.benchmark = True&lt;/code&gt; 으로 설정할 경우 computation을 가속화가능하다. cuDNN algorithm의 성능은 변화하는 서로 다른 kernel size에 따라 달라지기 때문에 auto-tuner는 best algorithm을 찾기위해 benchmark를 실행한다. Input size가 변화하지 않는 구조에서 해당 setting이 유효하므로 CNN모델을 학습할 경우 사용해야한다.&lt;/p&gt;
&lt;pre id=&quot;code_1668239920707&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;torch.backends.cudnn.benchmark = True&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.2 4D NCHW tensors에 대해 channel_last memory format를 사용 &lt;b&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;(time cost&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4d.png&quot; data-origin-width=&quot;1189&quot; data-origin-height=&quot;503&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GW6F7/btrQ3950Ut1/OHhfBg7ttVZ44SzYGCEp3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GW6F7/btrQ3950Ut1/OHhfBg7ttVZ44SzYGCEp3K/img.png&quot; data-alt=&quot;https://miro.medium.com/max/1400/1*yZF37VL9xLoYs6EpwpnyqQ.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GW6F7/btrQ3950Ut1/OHhfBg7ttVZ44SzYGCEp3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGW6F7%2FbtrQ3950Ut1%2FOHhfBg7ttVZ44SzYGCEp3K%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;631&quot; height=&quot;267&quot; data-filename=&quot;4d.png&quot; data-origin-width=&quot;1189&quot; data-origin-height=&quot;503&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://miro.medium.com/max/1400/1*yZF37VL9xLoYs6EpwpnyqQ.png&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 이미지는 NCHW 형태로 &lt;b&gt;(memory상에서)&lt;/b&gt; RGB 각 채널별로 clustering되어 있다. 이를 x = x.to(memory_format=torch.channels_last) 통해&lt;b&gt; memory상에서&lt;/b&gt; NHWC로 바꾸게 되면 위 그림과 같이 RGB layer가 교차되어 표현가능하다. NHWC format은 Mixed Precision Training와 같이 사용할 경우에 NHWC format보다 7~19%의 speed up 효과를 가져온다고 합니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;memory상에서의 pixel표현 방식이 다른것이지 실제 데이터의 shape은 바뀌지 않는다.&lt;/blockquote&gt;
&lt;pre id=&quot;code_1668311057296&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;N, C, H, W = 10, 3, 32, 32
x = torch.rand(N, C, H, W)

# Stride는 한 element와 다은 element사이의 gap(distance)을 나타냄
print(x.stride()) # shape: (3072, 1024, 32, 1)

x2 = x.to(memory_format=torch.channels_last) # memory상에서 NHWC format으로 변경
print(x2.shape)  # shape은 (10, 3, 32, 32)으로 변경되지 않음
print(x2.stride())  # NHWC로 바꾸면서 stride결과(3072, 1, 96, 3)가 작아짐
print((x==x2).all()) # 해당 값은 True로 value자체는 변경되지 않음 오직 memory상에서의 format이 변경&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.3 Conv-BN 구조에서 Conv의 bias 사용하지 않기 &lt;b&gt;&lt;b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;(time, memory cost&lt;span&gt;&amp;nbsp;&lt;/span&gt;&amp;darr;)&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Batch Normalization(BN)에 대해 이론적으로 잘 아시는 분은 아시겠지만 BN layer에 bias weight가 들어가있기 때문에 Conv의 bias을 사용한다고 해서 성능이 오르지 않고 그저 중복된 weight값이 되버린다. 그래서 Conv-BN구조에서는 Conv의 bias을 사용하지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1668311407815&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nn.Conv2d(..., bias=False)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;오늘은 이렇게 PyTorch framework에서 사용가능한 성능 최적화 방법을 알아보았습니다. 다음 글에서는 해당 방법들을 실제로 사용하였을 때 얼마나 빨라지는 지 확인해보겠습니다.&lt;/p&gt;</description>
      <category>AI Engineering/PyTorch</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/70</guid>
      <comments>https://da2so.tistory.com/70#entry70comment</comments>
      <pubDate>Sun, 13 Nov 2022 12:58:50 +0900</pubDate>
    </item>
    <item>
      <title>Mixed Precision Training 이해 및 설명</title>
      <link>https://da2so.tistory.com/67</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Mixed Precision Training 이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 deep learning framework(e.g. PyTorch, TensorFlow)들은 모델을 training할 때 float32(FP32) data type을 사용하게 됩니다. 즉, 모델의 weight와 input data가 모두 FP32(32bit)의 data type을 가진다는 뜻입니다. 이와 다르게 &lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;Mixed-precision training&lt;/span&gt;은 single-precision(FP32)와 half-precision(FP16) format을 결합하여 사용하여 모델을 training하는 방식입니다.&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(FP16 data type은 FP32와 다르게 16bit만을 사용하게 됩니다.)&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;Mixed-precision training방식을 통해 다음과 같은 장점을 가집니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;FP32로만 training한 경우와 같은 accuracy 성능을 도출&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Training time이 줄음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Memory 사용량이 줄음&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이로 인해 더 큰 batch size, model, input을 사용 가능하게 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Mixed-precision training은 NVIDIA에 의해 처음 제안되었고 Automatic Mixed Precision(AMP)라는 feature를 개발하였습니다. AMP  feature는 특정 GPU operation을 FP32에서 mixed precision으로 자동으로 바꿔주었으며 이는 performance를 향상시키면서 accuracy는 유지하는 효과를 가져왔습니다.&amp;nbsp;&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;PyTorch 1.6부터는 PyTorch 안에 AMP package(torch.cuda.amp)를 추가하였습니다. 아래와 같이 torch.cuda.amp는 기존의 NVIDIAd의 AMP의 pain point를 보완하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Window OS&amp;nbsp; 지원&lt;/li&gt;
&lt;li&gt;DataParallel과 intra-process model parallelism 지원 (Multi-GPU 지원)&lt;/li&gt;
&lt;li&gt;Gradient penalty (double backward) 지원
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;e.g) L1 regularization, L2 regularization, ...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;sparse gradient 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 PyTorch에서 FP16과 FP32으로 autocast가능한 CUDA operation은 아래와 같습니다. 보시면 모델의 구성 layer(e.g. conv, linear, LSTMCell, ...)들은 FP16으로 auto cast가능하신것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;658&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/brZmFL/btrMN9vIilz/BL6OgTu27Q3xokUKEk7Yk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/brZmFL/btrMN9vIilz/BL6OgTu27Q3xokUKEk7Yk0/img.png&quot; data-alt=&quot;https://pytorch.org/docs/stable/amp.html#cuda-ops-that-can-autocast-to-float16&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/brZmFL/btrMN9vIilz/BL6OgTu27Q3xokUKEk7Yk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbrZmFL%2FbtrMN9vIilz%2FBL6OgTu27Q3xokUKEk7Yk0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;293&quot; data-origin-width=&quot;1686&quot; data-origin-height=&quot;658&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://pytorch.org/docs/stable/amp.html#cuda-ops-that-can-autocast-to-float16&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Mixed Precision Training 사용 예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;※&lt;/b&gt; 저는 PyTorch framework에서만 사용하는 예제를 설명드립니다.&lt;/p&gt;
&lt;pre id=&quot;code_1663831462332&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import torch

scaler = torch.cuda.amp.GradScaler() # Training시에 생성

for data, label in data_iter:
   optimizer.zero_grad()
   with torch.cuda.amp.autocast(): # Mixed precision으로 operation들을 casting 
      outputs = model(data)

   scaler.scale(loss).backward() # Loss를 scaling한 후에 backward진행
   scaler.step(optimizer) # 원래 scale에 맞추어 gradient를 unscale하고 optimizer를 통한 gradient update
   scaler.update() # 다음 iteration을 위해 scale update&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 mixed precision은 오직 &lt;code&gt;with torch.cuda.amp.autocast():&lt;/code&gt; context안에서만 일어나게 되고 이를 통해 위에서 말씀드린 performance효과를 보게 됩니다. 그럼 scaler라는 class instance는 어떤 역할을 하는 것일까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Scaler의 역할&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Forward-pass시에 FP16으로 계산된 결과를 통해 backward-pass에서도 FP16으로 계산됩니다. 이 때 gradient의 값이 작아 float16으로 표현할 수 없다면 underflow가 발생하게 됩니다. 이런 문제점을 해결하기 위해 loss를 scale factor(이는 GradScaler()를 초기화할때 설정가능)와 곱하여 loss의 값을 크게 만드는 &lt;code&gt;scaler.scale(loss).backward()&lt;/code&gt;를 사용하게 됩니다. loss의 값이 커지게 되면 자연스럽게 underflow문제점이 사라지겠죠.&amp;nbsp;&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;Loss가 scale factor와 곱해졌었는데 weight update전에 원래 scale로 돌려놓기 위해 위의 scale factor만큼 나누어 주어 unscale하게 되는 과정은 &lt;code&gt;scaler.step(optimizer)&lt;/code&gt;에서 진행됩니다. 이때, 이미 scaled gradient가 inf, NaN값을 가지면 optimizer.step() 함수는 skip되고 해당 gradients는 weight update에 사용되지 않고 버리게 됩니다.&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;그리고 &lt;code&gt;scaler.update()&lt;/code&gt;를 통해 다음 iteration을 위한 scale factor를 업데이트합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Mixed precision training 실험&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PyTorch framework안에서 mixed precision training을 사용했을 경우와 아닌 경우의 time cost를 비교하려고 합니다. cifar10 dataset을 사용하여 ResNet18, 50, 101, MobileNetv2의 성능을 비교해보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실험에 사용한 코드는 해당 &lt;a href=&quot;https://github.com/da2so/da2so_tutorial/tree/main/mixed_precision&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;url&lt;/a&gt; 에서 사용가능합니다.&amp;nbsp;&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;아래 표는 한 epoch당 소요되는 training/test 시간(단위 seconds)을 나타낸것입니다. 사용된 GPU는 V100 1개입니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 95.2326%; height: 140px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet18&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet50&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;ResNet101&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;MobileNetv2&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 22px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;w/o mixed precision&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 20.4s&lt;/span&gt;&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 1.4s&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 58.0s&lt;/span&gt;&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 3.6s&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 97.1s&lt;/span&gt;&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 5.8s&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 22px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 20.4s&lt;/span&gt;&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 1.5s&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 20.1269%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;w mixed precision&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 19.8731%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 10.4s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 1.4s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 25.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 3.6s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;: 43.9s&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;: 5.8s&lt;/td&gt;
&lt;td style=&quot;width: 20%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;train&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 22.2s&lt;/span&gt;&lt;br /&gt;&lt;b&gt;test&lt;/b&gt;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;: 1.5s&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 통해 mixed precision training을 사용하였을 때 time cost가 적게 드는 것을 확인가능합니다.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/PyTorch</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/67</guid>
      <comments>https://da2so.tistory.com/67#entry67comment</comments>
      <pubDate>Wed, 2 Nov 2022 16:52:10 +0900</pubDate>
    </item>
    <item>
      <title>전문연구요원 훈련소 준비물 및 후기</title>
      <link>https://da2so.tistory.com/66</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;저는 2022년 10월 7일자로 논산 훈련소를 들어가 10월 27일에 훈련소 수료를 마친 전문연구요원입니다. 오늘은 훈련소에서 겪은 일들과 그에 대한 꿀팁을 알려드릴려고 합니다!&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;※ 내용은 26연대를 기준으로 말씀드립니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;준비물&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;훈련소의 생활관안에서 사회에서 사용하는 물품들을 사용할 수 있으므로 다음과 같은 준비물을 챙기시면 좋습니다. 참고로 챙겨간 물품을 입소시에 검사를 제대로 하지 않기때문에 물품은 너무 크지 않는 선에서 다 가져갈 수 있는것 같습니다. (제 생활관 동기중에는 담배를 가져왔는데 검사 시에 걸리지 않았습니다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;세면 도구: 샴푸, 바디워시 또는 올인원&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;칫솔, 치약은 줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;스킨, 로션&lt;/li&gt;
&lt;li&gt;책, 논문
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;불침번동안이나 주말에 읽기 좋습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;물티슈&lt;/li&gt;
&lt;li&gt;필기도구&lt;/li&gt;
&lt;li&gt;이어플러그&lt;/li&gt;
&lt;li&gt;텀블러&lt;/li&gt;
&lt;li&gt;선크림&lt;/li&gt;
&lt;li&gt;신분증&lt;/li&gt;
&lt;li&gt;커피 스틱&lt;/li&gt;
&lt;li&gt;깔창&lt;/li&gt;
&lt;li&gt;등.. 아무거나ㅋㅋㅋ&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;머리는 25mm~30mm로 짜르면 안전빵이고 몇몇분들은 더 길게 하시는분들&lt;/b&gt;&lt;/span&gt;도 있었습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1 주차: 코로나 걸렸다고 말하지마!&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;큰 체육관에서 코로나검사를 먼저 실시하고 분대장(조교)이 저에게 코로나 걸린 적있냐고 물어보았습니다. 저는 9월 5일 즉, 입소하기 한달 전에 걸렸다고 말씀드렸죠. 그러더니 분대장이 저를 대부분의 사람들이 앉아있는 자리와 다른 자리에 저를 앉혔습니다. 이 때부터 슬슬 불길한 예감이... 역시는 역시나 코로나를 입소전 45일이내에 걸린사람들만 모아놓은 자리였고 그 자리에 있던 사람들은 제 생활관 동기가 되었습니다. 저희는 1생활관으로 기확진자 모아놓은 생활관이었습니다. 소대장은 우리에게 와서 너희는 슈퍼면역자이니 우리 좀 도와서 일좀 하게 될거라고 하였고 보상은 많이 주겠다고 하였습니다. (물론 보상이 많을거란 기대는 거의 없었죠... 하지만 이렇게 시키는 일이 많을 지는 몰랐습니다..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;1~4중대 인원이 식사한 모든 설거지거리를 처리했습니다..&lt;/li&gt;
&lt;li&gt;저는 하루에 숫가락 약 3천개를 닦았습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;짐나르기
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;물 옮기기&lt;/li&gt;
&lt;li&gt;음식 및 부식 옮기고 배분&lt;/li&gt;
&lt;li&gt;훈련 준비 용품 가져오기&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;배식 하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주차는 코로나 격리주이기 때문에 생활관 내에서 밥을 먹고 훈련이 없습니다. 하지만 저희는 위와 같은 일을 하느라 몸이 망가지고 있었죠... ㅠㅠ (설거지로 인해 생활관 동기 3명은 허리디스크터지고 저는 발가락에 피가 터졌습니다.) 보상은 핸드폰 시간을 많이 주는 것이었습니다. 26연대 기준으로 코로나 격리주에는 모든 생활관 인원에게 하루에 15~30분 핸드폰시간을 주었지만 저희 생활관은 하루에 30~1시간정도 주었습니다.&amp;nbsp;&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;결론은 절대 입소기준 45일이내 기확진자라고 말하지 마세요!&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2주차: 훈련 시작&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1주차의 악몽같은 설거지가 끝나니 저희 생활관 사람들의 몸은 하자가 생겨있었습니다..ㅠ 코로나 격리주가 끝났기 때문에 본격적으로 훈련을 시작하였습니다. 또한 각 생활관마다 담당 업무를 맡게 되는데 우리는 세척(설거지)을 1주일동안했기때문에 소대장이 세척은 제외할 수 있도록 해주었고 그나마 편한 업무를 배정받았습니다.&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차 체력 검정: 3km 달리기, 윗몸일으키기, 팔굽혀펴기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 체력 검정으로 등급을 나누게 되는 데 3급이상 받으면 치킨 or 햄버거을 주니 잘하시면 좋습니다.&lt;/li&gt;
&lt;li&gt;6급이하이면 매일매일 체력 보충을 하게되니 운동 조금 하고 가세요&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;화생방 훈련
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;방독면 마스크의 성능을 직접느껴보기위해 cs탄이 퍼져있는 컨테이너를 그냥 들어갔다가 나오는것을 합니다.&lt;/li&gt;
&lt;li&gt;아프다고 열외했다면 보충훈련을 토요일에 받습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;이 훈련도 안했다면 보충훈련받았던걸로 기억....?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&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;잘 못쏘면 보충훈련받습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 2주차부터는 핸드폰을 사용하지 못하며 야외에서 아침점호를 하게되고 밥이 맛없어지고 많이 남기게됩니다.ㅋㅋㅋ&amp;nbsp; 그리고 주말에는 정말 많은 시간이 남기때문에 티비를 많이 보았습니다. 책도 읽고 생활관 사람들이랑 마피아도 하고... 여튼 주말에 시간이 많습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3주차: 언제 집 가지..?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 각개전투와 행군이 남았습니다. 밥도 맛없고 시간도 많이 남아서 집에 가고싶은 욕구가 엄청난 시기입니다. 각개전투와 행군은 저는 발가락 부상으로 하지는 않았지만 하신분들 보면 그렇게 까지 힘들어하시지 않았습니다. 또한 2차 체력검정도 실시하였는데 위와 동일하게 3급이상 받으면 인센티브를 주었습니다. 다만 2차 체력검정에서는 5급이하면 체력보충을 매일 나가게 됩니다. &lt;b&gt;수료식은 아침 10시에 시작하여 10시 20분~30분에 끝나고 핸드폰은 오전 9시즈음 받았습니다.&amp;nbsp;&lt;/b&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&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;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>일상</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/66</guid>
      <comments>https://da2so.tistory.com/66#entry66comment</comments>
      <pubDate>Tue, 1 Nov 2022 13:57:58 +0900</pubDate>
    </item>
    <item>
      <title>OpenVINO 뽀개기 (3) OpenVINO Quantization</title>
      <link>https://da2so.tistory.com/65</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;OpenVINO 모델을 optimization하기 위한 방법으로 Quantization에 대해 설명드립니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Quantization이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 Torch, ONNX model의 parameters(i.e. weights, bias)들은 각각이&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;float32&lt;/b&gt;로 표현되어 있습니다. Quantization은 float32의 data를 그 보다 낮은 bit(e.g. float16, int8)로 표현시켜 경량화시킵니다. 이렇게 함으로써 &lt;span&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;(1)&lt;/span&gt; inference time, &lt;span style=&quot;color: #8a3db6;&quot;&gt;(2)&lt;/span&gt; model size를 줄일 수 있다는 장점&lt;/b&gt;을 가집니다. 단점으로는 data의 정보손실이 발생하므로 Accuracy, mAP는 떨어지게 됩니다.&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;그럼, 다음과 같은 2가지 궁금증이 생기실 겁니다. 질문과 함께 답변드려볼게요.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;몇 bit로 줄일 거냐?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본적으로 TFLite에서 제공하는 타입 및 bit수는 float16(16bit), int8(8bit), uint8(8bit).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;어떻게 줄일 거냐?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;float32로 표현된 parameters들을 줄이고자 하는 bit에 맞춰 mapping시킴.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번에 대해 좀더 설명하기 위해 아래 uint8로 Quantization하는 예시를 보여드립니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1591&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEXg1Q/btrKgrZBVx7/YYUkB9bLhBEb8wfnL7F6pk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEXg1Q/btrKgrZBVx7/YYUkB9bLhBEb8wfnL7F6pk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEXg1Q/btrKgrZBVx7/YYUkB9bLhBEb8wfnL7F6pk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEXg1Q%2FbtrKgrZBVx7%2FYYUkB9bLhBEb8wfnL7F6pk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;752&quot; height=&quot;251&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1591&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 OpenVINO Quantization의 2가지 방법에 대해 설명드리겠습니다. 1번째는 FP16 Quantization, 2번째 Post-training Quantization입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2.&amp;nbsp; 환경 설정&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenVINO모델을 optimization하는 방법을 설명드리기 위해서 사용한 model, device, package 정보는 다음과 같습니다.&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;b&gt;Packages&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;openvino&lt;/b&gt;: 2022.1.0&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;openvino-dev&lt;/b&gt;: 2022.1.0&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CPU&lt;/b&gt;: Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz (가상 core수:&amp;nbsp;56)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Model&lt;/b&gt;: yolov7 OpenVINO model
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;yolov7.xml&lt;/li&gt;
&lt;li&gt;yolov7.bin&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. FP16 Quantization&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 FP32로 표현되던 weight를 FP16으로 변경하는 것은 ONNX모델을 OpenVINO로 converting하는 CLI 명령어의 parameter로 줄 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1661071923011&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mo --input_model ${onnx_path} --output_dir ${output_dir} --data_type ${d_type}
# Ex) mo --input_model yolov7.onxx --output_dir yolov7_openvino_fp16 --data_type FP16&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;318&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2lg4g/btrJ8IaLfhI/uYY97K0KW7FMknxkQblzm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2lg4g/btrJ8IaLfhI/uYY97K0KW7FMknxkQblzm1/img.png&quot; data-alt=&quot;Successfully converted into FP16 OpenVINO&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2lg4g/btrJ8IaLfhI/uYY97K0KW7FMknxkQblzm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2lg4g%2FbtrJ8IaLfhI%2FuYY97K0KW7FMknxkQblzm1%2Fimg.png&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; alt=&quot;Successfully converted into FP16 OpenVINO&quot; loading=&quot;lazy&quot; width=&quot;664&quot; height=&quot;146&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;318&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Successfully converted into FP16 OpenVINO&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 출력된다면 정상적으로 변환 된것입니다. 여기서 실제로 inference와 model size가 줄어드는 지 확인하기 위해 Torch(FP32), OpenVINO(FP32)와 비교해보았습니다. Inference time은 총 50번 실행에 대해 평균을 내었습니다. 성능 측정코드는 이전 글을 참조바랍니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 69.0699%; height: 68px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Model (data_type)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;File size(MB)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Inference time (s)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; height: 17px; text-align: center;&quot;&gt;Yolov7 Torch (FP32)&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center; height: 17px;&quot;&gt;147.7&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; height: 17px; text-align: center;&quot;&gt;1.093&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; height: 17px; text-align: center;&quot;&gt;Yolov7 OpenVINO (FP32)&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center; height: 17px;&quot;&gt;148.1&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; height: 17px; text-align: center;&quot;&gt;0.118&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;Yolov7 OpenVINO (FP16)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center; height: 17px;&quot;&gt;74.4&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; text-align: center; height: 17px;&quot;&gt;0.116&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FP16으로 data type이 변경되면서 당연하게도 model size는 50% 정도로 줄어들었지만 Inference time은 거의 비슷함을 확인가능합니다. (Inference time도 줄어들었으면 좋앗을 걸..)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Post-Training Quantization&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Post-Training Quantization(PTQ)는 말 그대로 Training이 끝난 모델에 대해 Quantization하겠다는 말입니다. OpenVINo의 PTQ는 int8 Quantization이 가능하므로 inference time과 model size모두 줄일 수 있습니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;522&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TuR5F/btrKcvnYq1r/jU8PkHW7eLwWhb8zjlScUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TuR5F/btrKcvnYq1r/jU8PkHW7eLwWhb8zjlScUk/img.png&quot; data-alt=&quot;PTQ process in OpenVINO&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TuR5F/btrKcvnYq1r/jU8PkHW7eLwWhb8zjlScUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTuR5F%2FbtrKcvnYq1r%2FjU8PkHW7eLwWhb8zjlScUk%2Fimg.png&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; alt=&quot;PTQ process in OpenVINO&quot; loading=&quot;lazy&quot; width=&quot;674&quot; height=&quot;196&quot; data-origin-width=&quot;1794&quot; data-origin-height=&quot;522&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PTQ process in OpenVINO&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 PTQ process를 보여주는 그림으로 Quantization을 하고자 하는 모델을 training하는 데 사용된 dataset을 필요로 하는 것을 알 수 있습니다. (Label은 필요하지 않음.) 또한 PTQ를 하기위해서는 유저가 직접 DataLoader에 대한 구현이 필요합니다.&amp;nbsp;&amp;nbsp;&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;그렇다면 왜 PTQ을 하는데 dataset을 필요로 할까요?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Int8 Quantization은 float32인 data size를 int8로 줄이는 작업이기 때문에 정보의 손실이 상대적으로 크겠죠. 그만큼 Quantization을 잘해야 기존의 Accuracy(or mAP) 성능이 떨어지지 않겠죠. 그러기 위해서는 각각의 weight에 대해 적절한 rmin/rmax (Quantization mapping range)를 선택하는 것이 중요하게 됩니다. 그래서 실제 input data들을 model에 흘려보내면서 Accuracy(or mAP)성능을 떨어트리지 않는 적절한 rmin/rmax를 찾기위해 dataset이 필요하게 됩니다.&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;전체적인 PTQ process flow는 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Data와 dataset interface를 준비&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Quantization parameter를 설정&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Quantization process 실행&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div id=&quot;introduction&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1 Data and Dataset Interface 준비&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제가 사용한 yolov7 모델은 COCO dataset을 사용했으므로 COCO training data를 준비하였습니다. 그리고 &lt;code&gt;openvino.tools.pot.DataLoader&lt;/code&gt; interface를 통해 dataloader를 구성하여야합니다. 그리고 해당 &lt;code&gt;DataLoader&lt;/code&gt; class에서 구현해야 할 함수는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;__len__()&lt;/code&gt;: dataset의 크기를 return&lt;/li&gt;
&lt;li&gt;&lt;code&gt;__getitem__()&lt;/code&gt;: index에 의해 data에 access해야하는 데 model-specific한 preprocessing후에 &lt;code&gt;(data, annotation)&lt;/code&gt;을 return
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;data&lt;/code&gt;: numpy.array이거나 dictionary형태여야 함&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;annotation&lt;/code&gt;: quantization에 사용되지 않으므로 None값을 줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1661085593358&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import os
import numpy as np
import cv2 as cv
from openvino.tools.pot import DataLoader

class ImageLoader(DataLoader):
    def __init__(self, dataset_path):
        # folder로 부터 image files 이름 가져오기
        self._files = []
        all_files_in_dir = os.listdir(dataset_path)
        for name in all_files_in_dir:
            file = os.path.join(dataset_path, name)
            if cv.haveImageReader(file):
                self._files.append(file)

        # model input의 shape정의
        self._shape = (640, 640)

    def __len__(self):
        &quot;&quot;&quot; dataset의 총 image file 개수 return &quot;&quot;&quot;
        return len(self._files)

    def __getitem__(self, index):
        &quot;&quot;&quot; 
        index에 의해 image data return  (NCHW shape)
        &quot;&quot;&quot;
        if index &amp;gt;= len(self):
            raise IndexError(&quot;Index out of dataset size&quot;)

        image = cv.imread(self._files[index]) # read image with OpenCV
        image = cv.resize(image, self._shape) # resize to a target input size
        image = np.expand_dims(image, 0)  # add batch dimension
    	image = image.astype(np.float32) # input data type to float32
        image /= 255. # normalize
        image = image.transpose(0, 3, 1, 2)  # convert to NCHW layout
        return image, None   # annotation is set to None

data_loader = ImageLoader(${coco_dataset_path})
# Ex) data_loader = ImageLoader(&quot;/usr/src/app/datasets/coco/images/val2017/&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 COCO dataset용으로 DataLoader를 구성하였습니다. 마지막 줄 예시에서도 보이듯이 저는 COCO validation set을 load하였습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.2 Quantization Parameter 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;PTQ를 진행하기 위한 parameter설정에 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1661088760847&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;q_params = [{
      	&quot;name&quot;: &quot;DefaultQuantization&quot;,
        &quot;params&quot;: {
            &quot;target_device&quot;: &quot;CPU&quot;,
            &quot;preset&quot;: &quot;performance&quot;,
            &quot;stat_subset_size&quot;: 1000},
        }]&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;&quot;name&quot;: &quot;DefaultQuantization&quot;&lt;/code&gt;&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;DefaultQuantization&lt;/code&gt;은 PTQ의 가장 기본적인 방법이며 fast하며 accurate한 결과를 제공한다고 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;target_device&quot;: &quot;CPU&quot;&lt;/code&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;target device에 대한 명시이며 다른 옵션으로는 &lt;code&gt;&quot;GPU&quot;&lt;/code&gt;, &lt;code&gt;&quot;ANY&quot;&lt;/code&gt;이 가능함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;preset&quot;: &quot;performance&quot;&lt;/code&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;preset&lt;/code&gt;은 quantization mode로 &lt;code&gt;performance&lt;/code&gt;값은 weight와 activation모두 symmetric quantization을 하며 모든 HW에 성능이 가장 우수함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;stat_subset_size&quot;:&amp;nbsp;300&lt;/code&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;위의 입력한 dataset path(validation set)에서 얼마만큼의 data를 사용할 건지 명시&lt;/li&gt;
&lt;li&gt;300이라는 값이 OpenVINO에서 실험해보았을 때 가장 최적의 값이었다고 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.3 Quantization Process 실행&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1661089703863&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from openvino.tools.pot import IEEngine
from openvino.tools.pot import load_model, save_model
from openvino.tools.pot import compress_model_weights
from openvino.tools.pot import create_pipeline

model_config = {
    &quot;model_name&quot;: &quot;yolov7&quot;,
    &quot;model&quot;: &quot;/usr/src/app/yolov5_inference/yolov7_openvino_fp32/yolov7.xml&quot;,
    &quot;weights&quot;: &quot;/usr/src/app/yolov5_inference/yolov7_openvino_fp32/yolov7.bin&quot;,
}
engine_config = {&quot;device&quot;: &quot;CPU&quot;}

# Step 1: Load model
model = load_model(model_config=model_config)

# Step 2: Device, data loader config와 함께 engine을 초기화
engine = IEEngine(config=engine_config, data_loader=data_loader)

# Step 3: PTQ parameter와 함께 pipeline생성 및 실행
pipeline = create_pipeline(q_params, engine)
compressed_model = pipeline.run(model=model)

# Step 4 (Optional): .bin file size를 줄이기 위해 model weight를 compress함
compress_model_weights(compressed_model)

# Step 5: save_path에 model_name이름으로 PTQ진행한 model 저장 
compressed_model_paths = save_model(
    model=compressed_model,
    save_path=&quot;yolov7_openvino_ptq&quot;,
    model_name=&quot;optimized_yolov7&quot;,
)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;load_model&lt;/code&gt;: PTQ를 진행하고자 하는 model을 load함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;lEEngine&lt;/code&gt;: PTQ에 필요한 device정보와 DataLoader를 입력하여 PTQ engine초기화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;create_pipeline&lt;/code&gt;: 위에서 설정한 PTQ parameter를 입력으로 pipeline생성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pipeline.run&lt;/code&gt;: PTQ를 실행!&lt;/li&gt;
&lt;li&gt;&lt;code&gt;save_model&lt;/code&gt;: PTQ가 완료된 모델을 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드를 실행하여 PTQ가 완료되면 아래와 같이 정상적으로 int8로 quantization된 OpenVINO 모델이 생성된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;136&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzA6Jt/btrJ8rl5z9Z/9igQUY9Q2rtf8l3y1I0L5k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzA6Jt/btrJ8rl5z9Z/9igQUY9Q2rtf8l3y1I0L5k/img.png&quot; data-alt=&quot;PTQ OpenVINO model&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzA6Jt/btrJ8rl5z9Z/9igQUY9Q2rtf8l3y1I0L5k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzA6Jt%2FbtrJ8rl5z9Z%2F9igQUY9Q2rtf8l3y1I0L5k%2Fimg.png&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; alt=&quot;PTQ OpenVINO model&quot; loading=&quot;lazy&quot; width=&quot;390&quot; height=&quot;89&quot; data-origin-width=&quot;596&quot; data-origin-height=&quot;136&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PTQ OpenVINO model&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; model size면에서는 FP32모델(148MB)에 비해 거의 2배줄은 것을 위로부터 확인가능합니다. 50번의 inference에 대해 평균을 내어 inference time을 측정하였습니다. 그리하여 기존 모델들과 비교했을 때 아래와 같이 PTQ를 사용하여 생성된 int8 quantized모델이 뛰어난 성능을 보임을 알 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 69.0699%; height: 85px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Model (data_type)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;File size(MB)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Inference time (s)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; height: 17px; text-align: center;&quot;&gt;Yolov7 Torch (FP32)&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center; height: 17px;&quot;&gt;147.7&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; height: 17px; text-align: center;&quot;&gt;1.093&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; height: 17px; text-align: center;&quot;&gt;Yolov7 OpenVINO (FP32)&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center; height: 17px;&quot;&gt;148.1&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; height: 17px; text-align: center;&quot;&gt;0.118&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; text-align: center; height: 17px;&quot;&gt;Yolov7 OpenVINO (FP16)&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center; height: 17px;&quot;&gt;74.4&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; text-align: center; height: 17px;&quot;&gt;0.116&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; text-align: center; height: 17px;&quot;&gt;&lt;b&gt;Yolov7 OpenVINO (int8)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center; height: 17px;&quot;&gt;38.0&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; text-align: center; height: 17px;&quot;&gt;0.073&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 int8로 quantized되었다면 mAP성능이 하락할 수도 있는데 해당 문제가 있는 지 확인하기위해 Yolov7 OpenVINO (FP32)모델의 detection결과와 비교를 해보았습니다. (detection에 사용한 코드와 모델은 Appendix에서 확인가능합니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2493&quot; data-origin-height=&quot;1222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSzf0X/btrKhRX3g5e/lofNuzDSpKUGZhUkMmm5EK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSzf0X/btrKhRX3g5e/lofNuzDSpKUGZhUkMmm5EK/img.png&quot; data-alt=&quot;Detection result comparison between yolov7 FP32 and yolov7 Int8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSzf0X/btrKhRX3g5e/lofNuzDSpKUGZhUkMmm5EK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSzf0X%2FbtrKhRX3g5e%2FlofNuzDSpKUGZhUkMmm5EK%2Fimg.png&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; alt=&quot;Detection result comparison between yolov7 FP32 and yolov7 Int8&quot; loading=&quot;lazy&quot; width=&quot;756&quot; height=&quot;371&quot; data-origin-width=&quot;2493&quot; data-origin-height=&quot;1222&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Detection result comparison between yolov7 FP32 and yolov7 Int8&lt;/figcaption&gt;
&lt;/figure&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;INT8로 quantization된 모델은 FP32의 모델의 detection결과에서 사람중에 하나를&lt;span style=&quot;color: #ee2323;&quot;&gt;(빨간색 박스)&lt;/span&gt; detection하지 못하는 것을 볼 수 있다. INT8로 quantization하여 mAP성능이 조금 떨어짐을 볼 수 있다. 좀 더 정확하게 bbox의 좌표와 confidence를 비교해보겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;1310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UsTFq/btrKbsZwbq1/VNNHafI9nNQHieyQERL23k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UsTFq/btrKbsZwbq1/VNNHafI9nNQHieyQERL23k/img.png&quot; data-alt=&quot;Second detection result comparison between yolov7 FP32 and yolov7 Int8&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UsTFq/btrKbsZwbq1/VNNHafI9nNQHieyQERL23k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUsTFq%2FbtrKbsZwbq1%2FVNNHafI9nNQHieyQERL23k%2Fimg.png&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; alt=&quot;Second detection result comparison between yolov7 FP32 and yolov7 Int8&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;492&quot; data-origin-width=&quot;1256&quot; data-origin-height=&quot;1310&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Second detection result comparison between yolov7 FP32 and yolov7 Int8&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 보시면 INT8로 quantization되면서 조금씩 bbox 좌표와 confidence가 달라짐을 볼 수 있고 사람 하나를 detection못하였기 때문에 Detect 9에 대한 정보가 없음을 알 수 있다. 특징점은 FP32모델에서 confidence가 작은 Detect 9가 INT8에서 없어진 것을 보면 quantization을 통한 정보손실은 decision boundary근처에서 많이 일어나는 것을 추측가능하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;Appendix&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FP16, INT8로 quantization된 모델은 &lt;a href=&quot;https://drive.google.com/file/d/1Mz4QRJuSB3EC7HTGKzNd9EfpOnaQNW4U/view?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 다운가능하고 detection에 사용한 코드와 파일은 &lt;a href=&quot;https://drive.google.com/file/d/1X0bZz7TIpqLk3nONGuFi5zgwS7nc4XKH/view?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기에서&lt;/a&gt; 받으세요.&lt;/p&gt;</description>
      <category>AI Engineering/OpenVINO</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/65</guid>
      <comments>https://da2so.tistory.com/65#entry65comment</comments>
      <pubDate>Mon, 22 Aug 2022 00:06:52 +0900</pubDate>
    </item>
    <item>
      <title>OpenVINO 뽀개기 (2) OpenVINO Inference</title>
      <link>https://da2so.tistory.com/64</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt; &lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://da2so.tistory.com/63&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;저번 글&lt;/a&gt;에 이어 이번에는 OpenVINO모델을 Inference하는 방법에 대해 설명드리도록 하겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. OpenVINO Runtime&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenVINO (IR)모델을 &lt;b&gt;inference할 수 있도록 하는 것이&amp;nbsp; OpenVINO runtime&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l0Ris/btrJKubsIwU/ZIaQUArlWewfFUzhE8U1qK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l0Ris/btrJKubsIwU/ZIaQUArlWewfFUzhE8U1qK/img.png&quot; data-alt=&quot;OpenVINO Runtime&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l0Ris/btrJKubsIwU/ZIaQUArlWewfFUzhE8U1qK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl0Ris%2FbtrJKubsIwU%2FZIaQUArlWewfFUzhE8U1qK%2Fimg.png&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; alt=&quot;OpenVINO Runtime&quot; loading=&quot;lazy&quot; width=&quot;294&quot; height=&quot;223&quot; data-origin-width=&quot;680&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OpenVINO Runtime&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenVINO runtime은 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;C, python의 binding과 함께&amp;nbsp;&lt;/span&gt; C++ library로 구현되어 있습니다. 그리고 위 그림에서 알 수 있듯이 OpenVINO runtime을 통해 IR모델 뿐만아니라 ONNX, PaddlePaddle(바이두)모델도 Inference가능하도록 API를 제공합니다. 또한 plugin architecture를 사용하기 때문에 해당 plugin들은 각 hardware device에 맞춰진 complete한 구현이 되어있습니다.&amp;nbsp;&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;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;OpenVINO runtime을 사용하기 위해서 openvino PyPI을 설치&lt;/b&gt;&lt;/span&gt;하였습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. OpenVINO Inference&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 환경 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 생성한 &lt;a href=&quot;https://drive.google.com/file/d/1u0F6gba5YOn5Vq2sP23ZKe0HU-6QUaUP/view?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;yolov7 OpenVINO&lt;/a&gt;모델을 사용하여 infernece를 진행할 것이며 inference환경은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;openvino&lt;/b&gt;: 2022.1.0&lt;/span&gt; (from PypI)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CPU&lt;/b&gt;: Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz (가상 core수:&amp;nbsp;56)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Model&lt;/b&gt;: yolov7 OpenVINO model
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;yolov7.xml&lt;/li&gt;
&lt;li&gt;yolov7.bin&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Load OpenVINO model&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 OpenVINO model을 load해봅니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1660530567912&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from openvino.runtime import Core

model_path = &quot;./yolov7_openvino/yolov7.xml&quot;
ie = Core() # initialize inference engine
network = ie.read_model(model=model_path, weights=Path(model_path).with_suffix('.bin'))
executable_network = ie.compile_model(model=network, device_name=&quot;CPU&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;ie=Core()&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;inference engine을 초기화 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ie.read_model()&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Core클래스 함수의 read_model함수로 OpenVINO model을 read함&lt;/li&gt;
&lt;li&gt;Model인자의 값으로 model topology가 담긴 xml파일을, weights에는 weight(bias) binary 파일을 넣어줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ie.compile_model()&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지정한 device(CPU)에서 model을 compile시켜 inference가능한 형태로 만들어 줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 Inference OpenVINO model&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1660656321994&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;im = np.random.randn(1,3,640,640) #random input
output_layer = next(iter(executable_network.outputs)) # OpenVINO model의 output layer를 가져옴
y = executable_network([im])[output_layer] # Inference 실행하여 output_layer에 해당하는 output을 y에 할당&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;next(iter(executable_network.outputs))&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;yolov7 OpenVINO모델의 outputs중 가장 첫 번째 output을 가져와 output_layer에 할당 (아래 사진 참조)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;executable_network([im])[output_layer]&lt;/code&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Inference 실행&lt;/b&gt;하여&amp;nbsp;output_layer에&amp;nbsp;해당하는&amp;nbsp;output을&amp;nbsp;y에&amp;nbsp;할당&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lWHBA/btrJREZ4EI4/tcXUFDeyNT0U6OiFHVWr7k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lWHBA/btrJREZ4EI4/tcXUFDeyNT0U6OiFHVWr7k/img.png&quot; data-alt=&quot;debugging for output_layer&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lWHBA/btrJREZ4EI4/tcXUFDeyNT0U6OiFHVWr7k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlWHBA%2FbtrJREZ4EI4%2FtcXUFDeyNT0U6OiFHVWr7k%2Fimg.png&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; alt=&quot;debugging for output_layer&quot; loading=&quot;lazy&quot; width=&quot;496&quot; height=&quot;215&quot; data-origin-width=&quot;1048&quot; data-origin-height=&quot;454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;debugging for output_layer&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.4 Torch와 OpenVINO 모델 inference time 비교&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenVINO모델로 inference해보았으니 Torch모델과 비교했을 때 Intel cpu에서 얼마나 빨라는 지 확인해보았습니다. 사용한 cpu는 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz (가상 core수:&amp;nbsp;56)이며&amp;nbsp;&lt;/span&gt;총 50번의 inference에 대해 평균을 내어 결과를 도출하였습니다. Torch, OpenVINO 모델 모두 weight의 data type은 FP32입니다. OpenVINO 모델은 &lt;a href=&quot;https://drive.google.com/file/d/1yjHq59pz83osozttlBQ4qa1_iUSglilx/view?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 다운 가능하며 Torch모델은 yolov7 공식 repo에서 받으시면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1660812079256&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;start = time.time()
y = executable_network([im])[output_layer
prinf(f'time lapse: {time.time()-start}')&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 69.0699%; height: 138px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Model&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center;&quot;&gt;&lt;b&gt;File size(MB)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Inference time (s)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; height: 17px; text-align: center;&quot;&gt;Yolov7 Torch&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center;&quot;&gt;147.7&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; height: 17px; text-align: center;&quot;&gt;1.093&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 33.2492%; height: 17px; text-align: center;&quot;&gt;Yolov7 OpenVINO&lt;/td&gt;
&lt;td style=&quot;width: 28.7037%; text-align: center;&quot;&gt;148.1&lt;/td&gt;
&lt;td style=&quot;width: 44.2236%; height: 17px; text-align: center;&quot;&gt;0.118&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 모델의 file size는 비슷한데 Inference time의 경우에는 OpenVINO모델이 10배정도 빠른 것을 알 수 있다!!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.5 Batch 수에 따른 Inference time비교&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1660660120918&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from openvino.runtime import Core, PartialShape
ie = Core()
network = ie.read_model(model=model_path, weights=Path(model_path).with_suffix('.bin'))

batch = 2
inputs = next(iter(network.inputs))
new_shape = PartialShape([batch, 3, 640, 640]) # batch 2
network.reshape({inputs.any_name: new_shape}) # reshape batch size of input

executable_network = ie.compile_model(model=network, device_name=&quot;CPU&quot;)

im = np.random.randn(batch, 3, 640, 640)    
output_layer = next(iter(executable_network.outputs))
y = executable_network([im])[output_layer]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;PartialShape&lt;/code&gt;과 &lt;code&gt;network.reshape&lt;/code&gt;을 통해 network의 input batch size를 변경 가능합니다.&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;위의 코드는 batch가 2일 경우입니다. 위의 코드를 기반으로&amp;nbsp;&lt;b&gt;Batch가 1, 2, 4, 8인 경우를 모두 측정&lt;/b&gt;하여 아래와 같은 결과를 보여드립니다. (50번의 inference에 대해 average하였습니다.)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 36.0465%; height: 85px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25.5914%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Batch size&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 41.0752%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Inference time (s)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25.5914%; height: 17px; text-align: center;&quot;&gt;1&lt;/td&gt;
&lt;td style=&quot;width: 41.0752%; height: 17px; text-align: center;&quot;&gt;0.118&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25.5914%; height: 17px; text-align: center;&quot;&gt;2&lt;/td&gt;
&lt;td style=&quot;width: 41.0752%; height: 17px; text-align: center;&quot;&gt;0.229&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25.5914%; height: 17px; text-align: center;&quot;&gt;4&lt;/td&gt;
&lt;td style=&quot;width: 41.0752%; height: 17px; text-align: center;&quot;&gt;0.480&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 25.5914%; height: 17px; text-align: center;&quot;&gt;8&lt;/td&gt;
&lt;td style=&quot;width: 41.0752%; height: 17px; text-align: center;&quot;&gt;0.935&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과표를 보시면 아시겠지만 batch 수에 비례하여 inference time이 늘어나는 것을 확인 가능합니다. (완전한 정비례는 아니네요..)&lt;/p&gt;</description>
      <category>AI Engineering/OpenVINO</category>
      <category>OpenVINO</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/64</guid>
      <comments>https://da2so.tistory.com/64#entry64comment</comments>
      <pubDate>Tue, 16 Aug 2022 22:57:06 +0900</pubDate>
    </item>
    <item>
      <title>OpenVINO 뽀개기 (1) OpenVINO 이해 및 변환</title>
      <link>https://da2so.tistory.com/63</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 1. OpenVINO란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/openvinotoolkit/openvino&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenVINO&lt;/a&gt;는 intel에서 주도적으로 진행 중인 프로젝트입니다. OpenVINO는 다양한 Deep Learning(DL) framework(e.g. PyTorch, TF)의 모델들을 OPenVINO 모델로 변환하여 intel device에 최적화된 inference를 할 수 있도록 해줍니다. 그래서 intel cpu나 gpu에서 DL 모델을 inference할 경우가 생기신다면 OpenVINO를 사용하셔야 latency성능이 좋아집니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yyFl8/btrJAIpcpEw/FN8tre0fS0ti7sYSApZ4qk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yyFl8/btrJAIpcpEw/FN8tre0fS0ti7sYSApZ4qk/img.png&quot; data-alt=&quot;OpenVINO 목표&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yyFl8/btrJAIpcpEw/FN8tre0fS0ti7sYSApZ4qk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyyFl8%2FbtrJAIpcpEw%2FFN8tre0fS0ti7sYSApZ4qk%2Fimg.png&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; alt=&quot;OpenVINO 목표&quot; loading=&quot;lazy&quot; width=&quot;599&quot; height=&quot;356&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OpenVINO 목표&lt;/figcaption&gt;
&lt;/figure&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;추가적으로 OpenVINO는 다음과 같은 특성은 제공합니다.&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;Pruning, Quantization을 통한 model size 및 inference 최적화&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Model의 preprocessing, postprocessing 기능 제공&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;intel cpu뿐만이 아닌 arm cpu, mac m1 chip에 대한 연산 지원&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 특성에 대해서는 이후 글에서 차차 알아보고 오늘은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;(1)&lt;/b&gt;&lt;/span&gt; &lt;b&gt;OpenVINO변환을 어떻게 하는 지 &lt;/b&gt;&amp;nbsp;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;(2)&lt;/b&gt; &lt;/span&gt;&lt;b&gt;변환된 OpenVINO 모델이 어떤 특성을 갖는지&lt;/b&gt; 설명드리겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. OpenVINO 모델 변환&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 환경 설정&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 ONNX모델을 input 모델로 사용하여 OpenVINO형태의 모델로 변환할 것이고 제가 사용한 intel cpu정보와 OpenVINO toolkit의 버전은 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;openvino-dev&lt;/b&gt;: 2022.1.0 (from PypI)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CPU&lt;/b&gt;: Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz (가상 core수:&amp;nbsp;56)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Model&lt;/b&gt;: yolov7 ONNX model (for detection task)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ONNX 정보: 1.9.0 version, 12 opset&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 OpenVINO Model Converting&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONNX모델을 OpenVINo로 변경하는 방법은 엄청 간단합니다. CLI하나면 끝납니다.&lt;/p&gt;
&lt;pre id=&quot;code_1660464776415&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;mo --input_model ${onnx_path} --output_dir ${output_dir} 
# Ex) mo --input_model yolov7.onxx --output_dir yolov7_openvino&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openvino-dev PyPI를 설치하셨다면 &lt;code&gt;mo&lt;/code&gt; 명령어를 사용 가능합니다. 그래서 &lt;code&gt;--input_model&lt;/code&gt;에는 onnx model의 path를 &lt;code&gt;--output_dir&lt;/code&gt;에는 OpenVINO 모델이 저장될 directory를 의미합니다.&amp;nbsp;&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;명령어가 정상적으로 실행되었다면 아래와 같은 결과를 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eiz2NX/btrJCoYfT61/WmNGRgk52uesk8Hf6At491/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eiz2NX/btrJCoYfT61/WmNGRgk52uesk8Hf6At491/img.png&quot; data-alt=&quot;OpenVINO model converting&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eiz2NX/btrJCoYfT61/WmNGRgk52uesk8Hf6At491/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Feiz2NX%2FbtrJCoYfT61%2FWmNGRgk52uesk8Hf6At491%2Fimg.png&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; alt=&quot;OpenVINO model converting&quot; loading=&quot;lazy&quot; width=&quot;748&quot; height=&quot;536&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;1583&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OpenVINO model converting&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 출력을 통해 다양한 정보를 알 수 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OpenVINO모델 변환 시 weight type은 따로 지정하지 않았으므로 default인 FP32로 변환됨&lt;/li&gt;
&lt;li&gt;OpenVINO모델은 Intermediate Representation(IR)이므로 여러 DL framework들을 하나의 OpenVINO모델로 변환 가능하게 하는 이유기도 함&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;version은 11&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;OpenVINO의 IR은 yolov7_openvino 폴더에 yolov7.xml, yolov7.bin파일로 생성
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;yolov7.xml: model의 topology를 표현
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Topology란 model내의 layer 순서, layer의 특성 등을 포함함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;yolov7.bin: model의 weight와 bias값을 binary형태로 가짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 yolov7.xml의 내용을 부분적으로 보여드립니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1125&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dyEykG/btrJEMxiURA/w9K2W9cJQ5xNsbGS21qQN1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dyEykG/btrJEMxiURA/w9K2W9cJQ5xNsbGS21qQN1/img.png&quot; data-alt=&quot;OpenVINO xml file&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dyEykG/btrJEMxiURA/w9K2W9cJQ5xNsbGS21qQN1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdyEykG%2FbtrJEMxiURA%2Fw9K2W9cJQ5xNsbGS21qQN1%2Fimg.png&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; alt=&quot;OpenVINO xml file&quot; loading=&quot;lazy&quot; width=&quot;651&quot; height=&quot;504&quot; data-origin-width=&quot;1454&quot; data-origin-height=&quot;1125&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OpenVINO xml file&lt;/figcaption&gt;
&lt;/figure&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;마지막으로 ONNX모델과 OpenVINO model size차이를 비교해봅니다. 모델은 &lt;a href=&quot;https://drive.google.com/file/d/1yjHq59pz83osozttlBQ4qa1_iUSglilx/view?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 다운 가능하십니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 38.4884%; height: 144px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Model Type&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Model size (MB)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;ONNX&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;147.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;OpenVINO&lt;/td&gt;
&lt;td style=&quot;width: 50%; height: 17px; text-align: center;&quot;&gt;bin:147.6&lt;br /&gt;xml:0.4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 글에서는 OpenVINO모델을 inference 하는 글을 작성하겠습니다. 수고하셨습니다~&lt;/p&gt;</description>
      <category>AI Engineering/OpenVINO</category>
      <category>OpenVINO</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/63</guid>
      <comments>https://da2so.tistory.com/63#entry63comment</comments>
      <pubDate>Sun, 14 Aug 2022 20:50:07 +0900</pubDate>
    </item>
    <item>
      <title>TFLite 뽀개기 (4) XNNPACK 이해 및 성능 비교</title>
      <link>https://da2so.tistory.com/62</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. XNNPACK이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XNNPACK은 아래와 같은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;다양한 device(architecture)를 위해 floating-point neural netowrk의 inference operator를 최적화한 library&lt;/b&gt;&lt;/span&gt;입니다. (floating-point란 fp32, fp16 모델만 가속화 가능하다는 뜻입니다.) 한마디로 DL 모델의 inference속도를 가속화 해주는 library입니다.&amp;nbsp;&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;Desktop기준으로 XNNPACK을 사용하기 위해서는 bazel build할때 XNNPACK사용에 대한 명시를 해주어야 합니다. 그리고 TFLite모델에만 사용이 가능합니다.&amp;nbsp; 또 다른 특징으로는 XNNPACK은 PAD operator와 CONV_2D operator(with VALID padding)을 감지하여 하나의 convolution operator로 fusing해주는 역할도 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 Supported architectures&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ARM64&lt;/b&gt; on Android, Linux, macOS, and IOS&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ARMv6&lt;/b&gt; (with VFPv2) on Linux&lt;/li&gt;
&lt;li&gt;&lt;b&gt;x86 and x86-64&lt;/b&gt; (up to AVX512) on Windows, Linux, macOs, Android, and IOS simulator&lt;/li&gt;
&lt;li&gt;&lt;b&gt;WebAssembly MVP and SIMD&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;RISC-V&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 XNNPACK의 지원범위는 넓네요. ARM CPU, Intel CPU, Chrome과 같이 web에서 사용되는 WebAssembly 심지어 축소된 명령어 세트로만 설계된 RISC-V에도 XNNPACK이 사용가능합니다. (XNNPack이 구현되어있는 neural network operator는 &lt;a href=&quot;https://github.com/google/XNNPACK&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;공식 repo&lt;/a&gt; 참고하세요! CNN계열은 거의 다 있네요...) &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;다양한 device환경에서 모두 사용될 수 있다는 점에서 generality가 좋고 실제로 사용해보면 inference time성능이 매우 좋아집니다.&amp;nbsp;&lt;/b&gt;&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;이번 글에서는 benchmark tool 를 통해 XNNPACK을 사용하였을 때와 아닐 때를 비교하며 inference time에 대한 성능비교를 해보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. XNNPACK 성능 비교&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Environment Setting&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1.1 Docker environment&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bazel build를 통해 TFLite모델을 benchmark할 수 있는 환경을 docker image로 만들어놓았습니다. 해당 &lt;b&gt;benchmark tool&lt;/b&gt;을 통해 XNNPACK사용 했을 경우와 아닌경우의 inference time차이를 볼것이며 profiling기능까지 제공하므로 profile을 통해 각 모델에 대한 inference time에 대한 분석 또한 해보겠습니다.&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;docker image는 아래 명령어로 받을 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1660022708620&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker pull da2so/tf_bazel:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 image를 다운받으셨다면 아래 명령어를 통해 container를 만들어 들어가서 benchmark tool을 실행 해봅시다!&lt;/p&gt;
&lt;pre id=&quot;code_1660026125697&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Option 1: GPU 있을 시 
docker run -it -d --gpus '&quot;device=0&quot;' --ipc=host --name da2so_test -p 3322:3322 -v /test/:/usr/src/app da2so/tf_bazel:latest /bin/bash
# Option 2: GPU 없을 시
docker run -it -d --ipc=host --name da2so_test -p 3322:3322 -v /test/:/usr/src/app da2so/tf_bazel:latest /bin/bash

docker attach da2so_test

# in container
cd tensorflow_src
bazel-bin/tensorflow/lite/tools/benchmark/benchmark_model --help&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 동작했다면 아래와 같을 것입니다. 저는 GPU를 사용하는 option으로 docker container를 생성하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JhibB/btrJhyzq6Dh/VM5kMWnQIOcK9kNmXtx3a1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JhibB/btrJhyzq6Dh/VM5kMWnQIOcK9kNmXtx3a1/img.png&quot; data-alt=&quot;check benchmark tool&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JhibB/btrJhyzq6Dh/VM5kMWnQIOcK9kNmXtx3a1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJhibB%2FbtrJhyzq6Dh%2FVM5kMWnQIOcK9kNmXtx3a1%2Fimg.png&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; alt=&quot;check benchmark tool&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;111&quot; data-origin-width=&quot;1870&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;check benchmark tool&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1.2 Models&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;XNNPACK inference time성능 비교에 사용된 TFLite 모델은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Classification&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;MobileNetv2, v3 모두 depth multiplier 0.75사용&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 45.8139%; height: 121px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 39.4246%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Model (data type)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 27.242%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Model size (MB)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 39.4246%; height: 17px; text-align: center;&quot;&gt;MobileNetv2 (FP32)&lt;/td&gt;
&lt;td style=&quot;width: 27.242%; height: 17px; text-align: center;&quot;&gt;10.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 39.4246%; text-align: center; height: 18px;&quot;&gt;MobileNetv2 (FP16)&lt;/td&gt;
&lt;td style=&quot;width: 27.242%; text-align: center; height: 18px;&quot;&gt;5.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 39.4246%; height: 17px; text-align: center;&quot;&gt;MobileNetv3 (FP32)&lt;/td&gt;
&lt;td style=&quot;width: 27.242%; height: 17px; text-align: center;&quot;&gt;16.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 39.4246%; text-align: center; height: 17px;&quot;&gt;MobileNetv3 (FP16)&lt;/td&gt;
&lt;td style=&quot;width: 27.242%; text-align: center; height: 17px;&quot;&gt;8.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 39.4246%; text-align: center; height: 17px;&quot;&gt;EfficientNetv2_B0 (FP32)&lt;/td&gt;
&lt;td style=&quot;width: 27.242%; text-align: center; height: 17px;&quot;&gt;28.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 39.4246%; text-align: center; height: 18px;&quot;&gt;EfficientNetv2_B0 (FP16)&lt;/td&gt;
&lt;td style=&quot;width: 27.242%; text-align: center; height: 18px;&quot;&gt;14.3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Object detection&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 46.2791%; height: 90px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 59.1371%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;Model (data type)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 40.8629%; text-align: center; height: 18px;&quot;&gt;&lt;b&gt;Model size (MB)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 59.1371%; text-align: center; height: 18px;&quot;&gt;yolov5s (FP32)&lt;/td&gt;
&lt;td style=&quot;width: 40.8629%; text-align: center; height: 18px;&quot;&gt;29.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 59.1371%; text-align: center; height: 18px;&quot;&gt;yolov5s (FP16)&lt;/td&gt;
&lt;td style=&quot;width: 40.8629%; text-align: center; height: 18px;&quot;&gt;14.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 59.1371%; text-align: center; height: 18px;&quot;&gt;yolov7 (FP32)&lt;/td&gt;
&lt;td style=&quot;width: 40.8629%; text-align: center; height: 18px;&quot;&gt;147.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 59.1371%; text-align: center; height: 18px;&quot;&gt;yolov7 (FP16)&lt;/td&gt;
&lt;td style=&quot;width: 40.8629%; text-align: center; height: 18px;&quot;&gt;73.9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 모델은 &lt;a href=&quot;https://drive.google.com/file/d/121ul76l_sxGlWyIGykk6mb7bUeAoemN3/view?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 다운받을 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1.3 Device info&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Inference에 사용되는 device정보는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;CPU&lt;/b&gt;: Intel(R) Xeon(R) Gold 5120 CPU @ 2.20GHz (56 core)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Benchmark tool로 inference time 측정 예시&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Benchmark tool로 yolov5s (fp32) 모델의 inference time을 측정하는 예시를 보여드립니다. &lt;code&gt;bazel-bin/tensorflow/lite/benchmark/benchmark_model&lt;/code&gt; 명령어로 모델의 inference time을 측정가능합니다. 아래에서는 XNNPACK을 사용하지 않았으며 warm up으로 10번 후, average inference time을 재기위해 100번 inference진행하였습니다.&amp;nbsp;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;xnnpack_result.png&quot; data-origin-width=&quot;3515&quot; data-origin-height=&quot;1549&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y2KjX/btrJmwVdYPH/FJxKY4svba2nK8QGTk9ak1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y2KjX/btrJmwVdYPH/FJxKY4svba2nK8QGTk9ak1/img.png&quot; data-alt=&quot;Benchmark tool result&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y2KjX/btrJmwVdYPH/FJxKY4svba2nK8QGTk9ak1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy2KjX%2FbtrJmwVdYPH%2FFJxKY4svba2nK8QGTk9ak1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;765&quot; height=&quot;337&quot; data-filename=&quot;xnnpack_result.png&quot; data-origin-width=&quot;3515&quot; data-origin-height=&quot;1549&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Benchmark tool result&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 보시면 yolov5s의 model path, model size, inference time, memory footprint까지 출력되는 것을 알 수 있습니다.&amp;nbsp; CPU로 측정된 yolov5s (fp32)의 average inference time은 513.534ms (0.5초정도) 인것을 확인가능합니다.&amp;nbsp;&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;&lt;i&gt;※ 참고로 use_gpu옵션을 주어 gpu acceleration을 할 수 있는 데 gpu acceleration은 Android 또는 iOS platform에서 사용가능하다고 하네요.&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1888&quot; data-origin-height=&quot;62&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OZfDp/btrJn71J066/Rp559i7O9cCtWVKhM2jbik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OZfDp/btrJn71J066/Rp559i7O9cCtWVKhM2jbik/img.png&quot; data-alt=&quot;GPU delegate is only supported on specific platforms&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OZfDp/btrJn71J066/Rp559i7O9cCtWVKhM2jbik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOZfDp%2FbtrJn71J066%2FRp559i7O9cCtWVKhM2jbik%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;768&quot; height=&quot;25&quot; data-origin-width=&quot;1888&quot; data-origin-height=&quot;62&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;GPU delegate is only supported on specific platforms&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 XNNPACK 성능비교&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령어를 통해 위에서 언급드린 TFLite모델들에 대해 XNNPACK을 사용했을 경우와 아닌 경우에 대한 inference time 성능 비교를 해보겠습니다. 추가로 thread수를 늘렸을 때 성능또한 어떻게 변화하는 지 알아보도록 하죠!&lt;/p&gt;
&lt;pre id=&quot;code_1660054606198&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;bazel-bin/tensorflow/lite/tools/benchmark/benchmark_model \
--graph=${model_path} \
--use_xnnpack=true (or false) \
--warmup_runs=10 \
--num_runs=100 \
--num_threads=1 (or 2, 4)&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3.1 Classifcation Results&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 thread수를 1기준으로 측정하였습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 95.6977%; height: 234px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;Model (data type, model size)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;USE_XNNPACK&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;Inference time (ms)&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; text-align: center; height: 18px;&quot;&gt;MobileNetv2 (FP32, 10.6MB)&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; text-align: center; height: 18px;&quot;&gt;True&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; text-align: center; height: 18px;&quot;&gt;11.746 &amp;plusmn; 0.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;MobileNetv2 (FP32, 10.6MB)&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;False&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;24.344 &amp;plusmn; 2.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;MobileNetv2 (FP16, 5.3MB)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;True&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;11.745 &amp;plusmn; 0.3&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;MobileNetv2 (FP16, 5.3MB)&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;False&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;23.677 &amp;plusmn; 1.5&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;MobileNetv3 (FP32, 16MB)&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;True&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;10.346 &amp;plusmn; 0.3&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;&amp;nbsp;MobileNetv3 (FP32, 16MB)&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;False&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;20.946 &amp;plusmn; 1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;MobileNetv3 (FP16, 8.1MB)&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;True&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;10.444 &amp;plusmn; 0.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;MobileNetv3 (FP16, 8.1MB)&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;False&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;20.745 &amp;plusmn; 1.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;EfficientNetv2_B0 (FP32, 28.5MB)&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;True&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;38.183 &amp;plusmn; 3.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;EfficientNetv2_B0 (FP32, 28.5MB)&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;False&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;68.230 &amp;plusmn; 3.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;EfficientNetv2_B0 (FP16, 14.3MB)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;True&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;37.895 &amp;plusmn; 1.0&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;width: 32.898%; height: 18px; text-align: center;&quot;&gt;EfficientNetv2_B0 (FP16, 14.3MB)&lt;/td&gt;
&lt;td style=&quot;width: 18.317%; height: 18px; text-align: center;&quot;&gt;False&lt;/td&gt;
&lt;td style=&quot;width: 23.785%; height: 18px; text-align: center;&quot;&gt;69.594 &amp;plusmn; 2.7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과에서  분석 내용은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;XNNPACK을 사용하였을때 2배이상 inference 속도가 빨라짐을 확인&lt;/li&gt;
&lt;li&gt;FP32와 FP16간의 model size는 차이가 많이 나지만 실제 inference 속도는 거의 비슷함&lt;/li&gt;
&lt;li&gt;MobileNetv3가 MobileNetv2에 비해 model size는 더 크지만 inference 속도가 더 빠름&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3.2 Object detection Results&lt;/b&gt;&lt;/h4&gt;
&lt;table style=&quot;border-collapse: collapse; width: 95.6977%; height: 162px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 44.4714%; text-align: center;&quot;&gt;&lt;b&gt;Model (data type, model size)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 25.0304%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;USE_XNNPACK&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 30.3767%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;Inference time (ms)&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 44.4714%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;yolov5s (FP32, 29.0MB)&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 25.0304%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;True&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 30.3767%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;403.578 &amp;plusmn; 5.5&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 44.4714%; text-align: center;&quot;&gt;yolov5s (FP32, 29.0MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 25.0304%; text-align: center;&quot;&gt;False&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 30.3767%; text-align: center;&quot;&gt;512.900 &amp;plusmn; 19.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 44.4714%; text-align: center;&quot;&gt;yolov5s (FP16, 14.5MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 25.0304%; text-align: center;&quot;&gt;True&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 30.3767%; text-align: center;&quot;&gt;407.494&amp;nbsp;&amp;plusmn;&amp;nbsp;18.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 44.4714%; text-align: center;&quot;&gt;yolov5s&amp;nbsp;(FP16, 14.5MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 25.0304%; text-align: center;&quot;&gt;False&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 30.3767%; text-align: center;&quot;&gt;516.036 &amp;plusmn; 34.6&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 44.4714%; text-align: center;&quot;&gt;yolov7 (FP32, 147.7MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 25.0304%; text-align: center;&quot;&gt;True&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 30.3767%; text-align: center;&quot;&gt;2420.893&amp;nbsp;&amp;plusmn; 41.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 44.4714%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt; yolov7 (FP32, 147.7MB )&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 25.0304%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;False&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 30.3767%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;2199.132 &amp;plusmn; 41.1&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 44.4714%; text-align: center;&quot;&gt;yolov7&amp;nbsp;(FP16, 73.9MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 25.0304%; text-align: center;&quot;&gt;True&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 30.3767%; text-align: center;&quot;&gt;2424.714 &amp;plusmn; 41.3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 44.4714%; text-align: center;&quot;&gt;yolov7&amp;nbsp;(FP16, 73.9MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 25.0304%; text-align: center;&quot;&gt;False&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 30.3767%; text-align: center;&quot;&gt;2232.653 &amp;plusmn; 54.3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과에서  분석 내용은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;yolov5s 모델의 경우 XNNPACK을 사용했을 때 inference 속도가 빨라짐
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;classification모델의 경우 2배정도 inference time이 줄었는데 모델이 커져서 XNNPACK의 효과가 작아진 건가..?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;yolov7 모델의 경우는 XNNPACK의 효과가 없는 것으로 보임
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모델이 너무 커서 그런것인가..?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3.3 Thread 수에 따른 inference time results&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;thread 수에 따른 inference time을 비교하기 위해 yolov5s 모델 기준으로 측정해보았습니다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 95.6977%; height: 234px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;&lt;b&gt;Model (data type, model size)&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;USE_XNNPACK / Thread num&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ffffff;&quot;&gt;Inference time (ms)&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP32,&amp;nbsp;29.0MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;True / 1&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;403.578 &amp;plusmn; 5.5&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP32,&amp;nbsp;29.0MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;True / 2&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;228.207 &lt;span style=&quot;color: #000000;&quot;&gt;&amp;plusmn; 16.8&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP32,&amp;nbsp;29.0MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;True / 4&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;142.095 &lt;span style=&quot;color: #000000;&quot;&gt;&amp;plusmn; 11.6&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP32,&amp;nbsp;29.0MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;False / 1&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;1022.063 &amp;plusmn; 7.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP32,&amp;nbsp;29.0MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;False / 2&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;733.723 &lt;span style=&quot;color: #000000;&quot;&gt;&amp;plusmn; 17.7&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP32,&amp;nbsp;29.0MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;False / 4&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;513.892 &lt;span style=&quot;color: #000000;&quot;&gt;&amp;plusmn; 15.0&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP16,&amp;nbsp;14.5MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;True / 1&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;407.494&amp;nbsp;&amp;plusmn;&amp;nbsp;18.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP16,&amp;nbsp;14.5MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;True / 2&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;231.054 &amp;plusmn; 10.7&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;yolov5s (FP16,&amp;nbsp;14.5MB)&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;True / 4&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;138.392 &amp;plusmn; 8.2&lt;/span&gt;&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP16,&amp;nbsp;14.5MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;False / 1&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;1020.223 &amp;plusmn; 6.4&amp;nbsp;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP16,&amp;nbsp;14.5MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;False / 2&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;734.001 &amp;plusmn; 21.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 18px;&quot;&gt;
&lt;td style=&quot;height: 18px; width: 36.0875%; text-align: center;&quot;&gt;yolov5s (FP16,&amp;nbsp;14.5MB)&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 28.7971%; text-align: center;&quot;&gt;False / 4&lt;/td&gt;
&lt;td style=&quot;height: 18px; width: 32.1993%; text-align: center;&quot;&gt;506.902 &amp;plusmn; 15.4&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과에서  분석 내용은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(A) XNNPACK을 사용하지 않고 thread를 4개를 쓴 경우가 (B) XNNPACK을 사용하고 thread수가 1일경우보다 inference time오래 걸림을 알 수 있음&amp;nbsp;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;(A)'s inference time: &lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;513.892&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&amp;plusmn; 15.0 (FP32), 506.902 &lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;&amp;plusmn; 15.4 (FP16)&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;(B)'s inference time: &lt;span style=&quot;color: #000000;&quot;&gt;403.578 &amp;plusmn; 5.5 (FP32), 407.494&amp;nbsp;&lt;span style=&quot;background-color: #f9f9f9;&quot;&gt;&amp;plusmn;&lt;/span&gt; 18.5(FP16)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;XNNPACK을 사용하고 thread num이 4인경우가 가장 inference time 성능이 가장 좋음 (초록색)&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/TensorFlow</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/62</guid>
      <comments>https://da2so.tistory.com/62#entry62comment</comments>
      <pubDate>Wed, 10 Aug 2022 00:35:20 +0900</pubDate>
    </item>
    <item>
      <title>[NVIDIA] TensorRT inference 코드 및 예제 (feat. yolov7)</title>
      <link>https://da2so.tistory.com/61</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전의 TensorRT plugin 사용하는 방법을 설명드렸는데요. TRT모델로 inference하는 코드에 대한 설명이 부족하고 저도 잘 이해하지 못한 부분이 있어 이번 글에서 설명드립니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;0. Inference용 모델 및 개발 환경&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Inference를 위해 사용한 모델은 YOLOv7 모델입니다. 모델은 &lt;a href=&quot;https://drive.google.com/file/d/1ZhbfyR2NUoC-1vV2NrzvoemQprlI8nha/view?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;yolov7.trt&lt;/a&gt; 다운가능하며 input의 shape은 &lt;b&gt;(1,3,640,640)&lt;/b&gt;로 설정하였으며 output은 총 4개로 나뉘면&amp;nbsp; 각각 num_detections(detection된 object개수), nmsed_boxes(object의 bounding box 좌표), nmsed_scores(object의 confidence score), nmsed_classes(object의 class)입니다. output shape은 아래와 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eEzvid/btrIuZjmtnz/MqdCMq0G08iSC49FS4XCZ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eEzvid/btrIuZjmtnz/MqdCMq0G08iSC49FS4XCZ0/img.png&quot; data-alt=&quot;output shape of yolov7 with NMSPlugin&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eEzvid/btrIuZjmtnz/MqdCMq0G08iSC49FS4XCZ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeEzvid%2FbtrIuZjmtnz%2FMqdCMq0G08iSC49FS4XCZ0%2Fimg.png&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; alt=&quot;output shape of yolov7 with NMSPlugin&quot; loading=&quot;lazy&quot; width=&quot;706&quot; height=&quot;156&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;output shape of yolov7 with NMSPlugin&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개발 환경은 다음과 같습니다. (Docker container에서 구축했습니다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;onnx&lt;/b&gt;: 1.8.0&lt;/li&gt;
&lt;li&gt;&lt;b&gt;torch&lt;/b&gt;: 1.9.0a0+df837d0&lt;/li&gt;
&lt;li&gt;&lt;b&gt;onnx-graphsurgeon&lt;/b&gt;: 0.2.8&lt;/li&gt;
&lt;li&gt;&lt;b&gt;tensorrt:&lt;/b&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;7.2.2.3&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CUDA&lt;/b&gt;: 11.2&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Driver Version&lt;/b&gt;: 460.73.01&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GPU&lt;/b&gt;: Tesla V100&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Input image는 아래 사진을 사용하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FQkwX/btrIq5rtyh7/Kf6T2TOlnHukOReFxCTRBK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FQkwX/btrIq5rtyh7/Kf6T2TOlnHukOReFxCTRBK/img.png&quot; data-alt=&quot;horse.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FQkwX/btrIq5rtyh7/Kf6T2TOlnHukOReFxCTRBK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFQkwX%2FbtrIq5rtyh7%2FKf6T2TOlnHukOReFxCTRBK%2Fimg.png&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; alt=&quot;horse.jpg&quot; loading=&quot;lazy&quot; width=&quot;474&quot; height=&quot;315&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;horse.jpg&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. TensorRT Inference&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;python 환경에서 TRT 모델을 inference하겠습니다. 코드 한줄씩 설명드리며 어떻게 TRT 모델이 작동하는 지 봐보죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;아래에 사용된 코드와 전체 코드는 &lt;a href=&quot;https://github.com/da2so/yolov7_trt/blob/main/detect_trt_plugin.py&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 확인가능합니다.&amp;nbsp;&lt;/b&gt;&lt;b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 Load TensorRT model&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1659014928574&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def load_model(self):
        TRT_LOGGER = trt.Logger(trt.Logger.WARNING)
        runtime = trt.Runtime(TRT_LOGGER) # serialized ICudEngine을 deserialized하기 위한 클래스 객체
        trt.init_libnvinfer_plugins(None, &quot;&quot;) # plugin 사용을 위함
        with open(self.model_path, 'rb') as f:
            self.engine = runtime.deserialize_cuda_engine(f.read()) # trt 모델을 읽어 serialized ICudEngine을 deserialized함
        
        self.context = self.engine.create_execution_context() # ICudEngine을 이용해 inference를 실행하기 위한 context class생성
        assert self.engine 
        assert self.context&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;trt.Runtime&lt;/code&gt;: Serialized된 &lt;code&gt;ICudaEngine&lt;/code&gt;을 deserialized하기 위한 클래스 객체
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기본적으로 .trt 파일은 serialized 즉, bytestream으로 저장되어 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;runtime.deserialize_cuda_engine&lt;/code&gt;: .trt&amp;nbsp;모델을&amp;nbsp;읽어&amp;nbsp;serialized&amp;nbsp;&lt;code&gt;ICudEngine&lt;/code&gt;을&amp;nbsp;deserialized함&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;bytestream인 &lt;code&gt;ICudaEngine&lt;/code&gt;을 deserailized하게 되면 아래와 같이 &lt;code&gt;self.engine&lt;/code&gt;에 &lt;code&gt;ICudaEngine&lt;/code&gt; 클래스 객체가 생성됨&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ICudaEngine&lt;/code&gt;는 아래의 사진과 같이 모델의 다양한 정보를 가짐
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;device_memory_size&lt;/code&gt;: trt 모델을 실행시키는 데 필요한 총 memory 양&lt;/li&gt;
&lt;li&gt;&lt;code&gt;max_batch_size&lt;/code&gt;: 최대 batch 수&lt;/li&gt;
&lt;li&gt;&lt;code&gt;num_bindings&lt;/code&gt;: I/O binding의 수 (Input 수 1개 + Output수 4개=5)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;num_layers&lt;/code&gt;: trt 모델의 layer개수&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;trtinf_1.png&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;1530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPT6EE/btrInEA9E70/N3rxKRlyaGpq93OvLy2QOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPT6EE/btrInEA9E70/N3rxKRlyaGpq93OvLy2QOk/img.png&quot; data-alt=&quot;ICudaEngine class&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPT6EE/btrInEA9E70/N3rxKRlyaGpq93OvLy2QOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPT6EE%2FbtrInEA9E70%2FN3rxKRlyaGpq93OvLy2QOk%2Fimg.png&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; alt=&quot;IcudaEngine class&quot; loading=&quot;lazy&quot; width=&quot;429&quot; height=&quot;425&quot; data-filename=&quot;trtinf_1.png&quot; data-origin-width=&quot;1544&quot; data-origin-height=&quot;1530&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ICudaEngine class&lt;/figcaption&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;&lt;code&gt;self.engine.create_execution_context&lt;/code&gt;: &lt;code&gt;ICudEngine&lt;/code&gt;을 이용해 inference를 실행하기 위한 &lt;code&gt;IExecutionContext&lt;/code&gt; class생성
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;해당 class의 함수로 이후에 Inference 실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;trtinf_2.png&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmQ3bG/btrInHZmrpp/A7gXlyYuOQyxSFVYV6Cl2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmQ3bG/btrInHZmrpp/A7gXlyYuOQyxSFVYV6Cl2k/img.png&quot; data-alt=&quot;IExecutionContext&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmQ3bG/btrInHZmrpp/A7gXlyYuOQyxSFVYV6Cl2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmQ3bG%2FbtrInHZmrpp%2FA7gXlyYuOQyxSFVYV6Cl2k%2Fimg.png&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; alt=&quot;IExecutionContext&quot; loading=&quot;lazy&quot; width=&quot;484&quot; height=&quot;38&quot; data-filename=&quot;trtinf_2.png&quot; data-origin-width=&quot;636&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;IExecutionContext&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.2 Setup I/O binding&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;I/O binding이란 trt모델의 Input과 Output의 정보를 저장하고 이는 이후에 GPU연산을 위해 또한 Inference에 사용됩니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1659017919115&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def alloc_buf(self):
        self.inputs = []
        self.outputs = []
        self.allocations = []

        for i in range(self.engine.num_bindings): # input과 output의 개수만큼
            is_input = False
            if self.engine.binding_is_input(i): # i번째 binding이 input인지 확인
                is_input = True 
            name = self.engine.get_binding_name(i) # i번째 binding의 이름
            dtype = np.dtype(trt.nptype(self.engine.get_binding_dtype(i))) # i번째 binding의 data type
            shape = self.context.get_binding_shape(i) # i번째 binding의 shape

            if is_input and shape[0] &amp;lt; 0:
                assert self.engine.num_optimization_profiles &amp;gt; 0
                profile_shape = self.engine.get_profile_shape(0, name)
                assert len(profile_shape) == 3  # min,opt,max
                # Set the *max* profile as binding shape
                self.context.set_binding_shape(i, profile_shape[2])
                shape = self.context.get_binding_shape(i)
            if is_input:
                self.batch_size = shape[0]
            size = dtype.itemsize # data type의 byte수
            for s in shape:
                size *= s # data type의 byte수 * 각 shape(e.g input의 경우 [1,3,640,640]) element 을 곱하여 size에 할당

            allocation = cuda.mem_alloc(size) # 해당 size만큼의 GPU memory allocation함
            host_allocation = None if is_input else np.zeros(shape, dtype)
            binding = {
                &quot;index&quot;: i,
                &quot;name&quot;: name,
                &quot;dtype&quot;: dtype,
                &quot;shape&quot;: list(shape),
                &quot;allocation&quot;: allocation,
                &quot;host_allocation&quot;: host_allocation,
            }
            self.allocations.append(allocation)
            if self.engine.binding_is_input(i): # binding이 input이면
                self.inputs.append(binding)
            else: # 아니면 binding은 모두 output임
                self.outputs.append(binding)&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;self.engine.binding_is_input(i)&lt;/code&gt;: i번째 index를 가진 binding이 input을 의미하는 지 확인&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.engine.get_binding_name(i)&lt;/code&gt;: i번째 index binding의 name&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.engine.get_binding_dtype(i)&lt;/code&gt;:&amp;nbsp; i번째 index binding의 data type
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;trt.nptype&lt;/code&gt;: 해당 함수를 통해 trt의 data type을 numpy type으로 바꿔줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.context.get_binding_shape(i)&lt;/code&gt;: i번째 index binding의 shape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;size = dtype.itemsize, size *= s&lt;/code&gt;: i번째 index를 가진 binding의 data shape과 data type에 따른 data size할당&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Input shape이 (1, 3, 640, 640)인경우 (data type의 byte수(4byte) x 1 x 3 x 640 x 640)를 size에 할당&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cuda.mem_alloc(size)&lt;/code&gt;: GPU memory에 해당 size만큼 allocation(할당)함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;host_allocation = None if is_input else np.zeros(shape, dtype)&lt;/code&gt;: GPU로 inference후의 cpu host로 output의 정보를 받기위한 allocation된 배열&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 사진은 1번째, 2번째 index를 가진 binding에 대한 정보를 출력한 것입니다. 1번째는 input에 해당하는 binding이며 2번째는 num_detections에 대한 output에 해당하는 binding입니다. Num_detections에 대한 binding은 host_allocation을 가진다는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;trtinf_3.png&quot; data-origin-width=&quot;3469&quot; data-origin-height=&quot;1214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yak9F/btrIq5x8F6k/4s0eAAnDNlqrWomVHgRTzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yak9F/btrIq5x8F6k/4s0eAAnDNlqrWomVHgRTzk/img.png&quot; data-alt=&quot;I/O Binding examples&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yak9F/btrIq5x8F6k/4s0eAAnDNlqrWomVHgRTzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYak9F%2FbtrIq5x8F6k%2F4s0eAAnDNlqrWomVHgRTzk%2Fimg.png&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; alt=&quot;I/O Binding examples&quot; loading=&quot;lazy&quot; width=&quot;817&quot; height=&quot;286&quot; data-filename=&quot;trtinf_3.png&quot; data-origin-width=&quot;3469&quot; data-origin-height=&quot;1214&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;I/O Binding examples&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.3 Inference TRT model&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Setup완료하였으니 이제 inference해보죠.&lt;/p&gt;
&lt;pre id=&quot;code_1659019426728&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    def inference(self, input_image):
        image = input_image.transpose(0, 3, 1, 2) # NHWC to NWHC
        image = np.ascontiguousarray(image) 
        cuda.memcpy_htod(self.inputs[0]['allocation'], image) # input image array(host)를 GPU(device)로 보내주는 작업
        self.context.execute_v2(self.allocations) #inference 실행!
        for o in range(len(self.outputs)):
            cuda.memcpy_dtoh(self.outputs[o]['host_allocation'], self.outputs[o]['allocation']) # GPU에서 작업한 값을 host로 보냄
        
        num_detections = self.outputs[0]['host_allocation'] # detection된 object개수
        nmsed_boxes = self.outputs[1]['host_allocation'] # detection된 object coordinate
        nmsed_scores = self.outputs[2]['host_allocation'] # detection된 object confidence
        nmsed_classes = self.outputs[3]['host_allocation'] # detection된 object class number
        result = [num_detections, nmsed_boxes, nmsed_scores, nmsed_classes]
        return result&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;cuda.memcpy_htod(self.inputs[0]['allocation'], image)&lt;/code&gt;: input image array(host)를 GPU(device)를 copy하여 보냄
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;htod: h(host) to d(device)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.context.execute_v2(self.allocations)&lt;/code&gt;: 실제 Inference를 진행하는 함수&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cuda.memcpy_dtoh(self.outputs[o]['host_allocation'], self.outputs[o]['allocation'])&lt;/code&gt;: GPU device에서 진행한 inference값을 host로 copy하여 보냄&lt;/li&gt;
&lt;li&gt;&lt;code&gt;self.outputs[i]['host_allocation']&lt;/code&gt;: i번째 output의 값이 저장되어 있는 변수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yolov7 model의 output인 num_detections, nmsed_boxes, nmsed_scores, nmsed_classes에 대한 값과 그에 대한 설명은 다음과 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;trtinf_4.png&quot; data-origin-width=&quot;2974&quot; data-origin-height=&quot;1998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PkHim/btrIuYZo2Y3/0DYpEp85KutdK0zWqOocZ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PkHim/btrIuYZo2Y3/0DYpEp85KutdK0zWqOocZ1/img.png&quot; data-alt=&quot;yolov7 with NMSPlugin의 output&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PkHim/btrIuYZo2Y3/0DYpEp85KutdK0zWqOocZ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPkHim%2FbtrIuYZo2Y3%2F0DYpEp85KutdK0zWqOocZ1%2Fimg.png&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; alt=&quot;yolov7 with NMSPlugin의 output&quot; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;459&quot; data-filename=&quot;trtinf_4.png&quot; data-origin-width=&quot;2974&quot; data-origin-height=&quot;1998&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;yolov7 with NMSPlugin의 output&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.4 detection 결과 및 inference 속도&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;horse.jpg에 대한 detection 결과는 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;530&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VbIpH/btrInHE6VJK/0aU9ZpKYH2LjUvpBj5ubyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VbIpH/btrInHE6VJK/0aU9ZpKYH2LjUvpBj5ubyK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VbIpH/btrInHE6VJK/0aU9ZpKYH2LjUvpBj5ubyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVbIpH%2FbtrInHE6VJK%2F0aU9ZpKYH2LjUvpBj5ubyK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;814&quot; height=&quot;337&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;530&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 V100으로 측정한 &lt;b&gt;yolov7 모델의 inference&lt;/b&gt; &lt;b&gt;속도는 7.552ms&lt;/b&gt;입니다. 20번의 warm-up하고 iteration 200번에 대해 평균을 낸 속도입니다. 또한 Host2Device와 Device2Host에 대한 Memory copy가 포함된 inference time입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lbLav/btrInIRvObN/uxVhLWaWsBsqiZymtkuBi0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lbLav/btrInIRvObN/uxVhLWaWsBsqiZymtkuBi0/img.png&quot; data-alt=&quot;yolov7 with NMSPlugin model inference time&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lbLav/btrInIRvObN/uxVhLWaWsBsqiZymtkuBi0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlbLav%2FbtrInIRvObN%2FuxVhLWaWsBsqiZymtkuBi0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;62&quot; data-origin-width=&quot;858&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;yolov7 with NMSPlugin model inference time&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/NVIDIA</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/61</guid>
      <comments>https://da2so.tistory.com/61#entry61comment</comments>
      <pubDate>Fri, 29 Jul 2022 09:22:32 +0900</pubDate>
    </item>
    <item>
      <title>[NVIDIA] TensorRT plugin 사용 및 예제 (feat. yolov7)</title>
      <link>https://da2so.tistory.com/60</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. TensorRT Plugin이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/NVIDIA/TensorRT&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TensorRT&lt;/a&gt;는 C++ library이고 nvidia GPUs와 deep learning accelerator를 제공함으로써 뛰어난 performance를 제공합니다.&amp;nbsp;그래서&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;nvidia GPU가 장착된 서버를 쓰신다면&lt;/span&gt; TensorRT(.trt)모델로 변환하여 inference하는 것이 효과적입니다.&amp;nbsp;&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;추가로 TensorRT에서는 plugin기능을 제공하는데요. &lt;b&gt;Plugin을 사용하여 model의 추가적인 연산(preprocess, postprocess)를 C++, cuda programming 으로 대체할 수 있어서 &lt;span style=&quot;color: #1a5490;&quot;&gt;(1)&lt;/span&gt; 코드의 간결화 &lt;span style=&quot;color: #1a5490;&quot;&gt;(2)&lt;/span&gt; 연산속도의 효율의 장점&lt;/b&gt;이 있습니다. &lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;대표적인 예시로 대부분의 AI개발자분들은 detection model의 NMS(&lt;span style=&quot;background-color: #ffffff; color: #24292f;&quot;&gt;Non Maximum Suppression)&lt;/span&gt; 코드를 python으로 개발하실텐데 TensorRT에서는 detection model의 뒤에 NMS plugin을 붙일 수 있어서 model의 output이 NMS를 통과한 output으로 간결화되고 python이 아닌 C++이기때문에 연산속도의 효율도 가지게 됩니다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;&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;TensorRT에서 제공하는 plugin은 아래의 그림을 통해 확인 가능하며 사용 가능한 모든 plugin은 &lt;a href=&quot;https://github.com/NVIDIA/TensorRT/tree/main/plugin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 보시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;1260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dDQ4kT/btrHnTzc9jh/zVUp34fUKW1mPR6Uo6C8Rk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dDQ4kT/btrHnTzc9jh/zVUp34fUKW1mPR6Uo6C8Rk/img.png&quot; data-alt=&quot;TensorRT plugins&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dDQ4kT/btrHnTzc9jh/zVUp34fUKW1mPR6Uo6C8Rk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdDQ4kT%2FbtrHnTzc9jh%2FzVUp34fUKW1mPR6Uo6C8Rk%2Fimg.png&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; alt=&quot;TensorRT plugins&quot; loading=&quot;lazy&quot; width=&quot;551&quot; height=&quot;526&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;1260&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TensorRT plugins&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;2. TensorRT plugin 예제 및 실습&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;최근에 새로나온&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #781b33;&quot;&gt;&lt;b&gt;yolov7&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;b&gt;&lt;span style=&quot;color: #781b33;&quot;&gt;&amp;nbsp;model에 batchedNMSPlugin&lt;/span&gt;&lt;/b&gt;을 추가해보는 실습을 해보겠습니다. &lt;a href=&quot;https://github.com/NVIDIA/TensorRT/tree/main/plugin/batchedNMSPlugin&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;batchedNMSPlugin&lt;/a&gt;은 NMS step을 C++언어와 GPU로 inference가능하다는 장점이 있습니다. &lt;/span&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;개발 환경은 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;onnx&lt;/b&gt;: 1.8.0&lt;/li&gt;
&lt;li&gt;&lt;b&gt;torch&lt;/b&gt;: 1.9.0a0+df837d0&lt;/li&gt;
&lt;li&gt;&lt;b&gt;onnx-graphsurgeon&lt;/b&gt;: 0.2.8&lt;/li&gt;
&lt;li&gt;&lt;b&gt;tensorrt:&lt;/b&gt; 7.2.2.3&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CUDA&lt;/b&gt;: 11.2&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Driver Version&lt;/b&gt;: 460.73.01&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 batchedNMSPlugin 이란?&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 먼저 &lt;span style=&quot;color: #000000;&quot;&gt;batchedNMSPlugin의&lt;/span&gt; input, output 형태가 어떤 지 알아봅시다. 해당 plugin의 input 형태는 yolov7모델의 output과 동일해야 해당 plugin을 사용가능하다는 것을 말합니다. 그리고 output형태는 해당 plugin의 결과 의미합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Input&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Boxes input: &lt;/b&gt;[batch_size, number_boxes, 1, number_box_parameters]
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;number_box_parameters는 bbox의 정보를 담고 있는데 [x1, y1, x2, y2]으로 (x1, y1), (x2,y2)는 각각 왼쪽 위, 오른쪽 아래 bbox좌표를 나타냄&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Scores input: &lt;/b&gt;[batch_size, number_boxes,  class_with_confidence]
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;class_with_condience = number_classes(각 클래스의 확률) * confidence(objectness)를 의미함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Output&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;num_detections:&lt;/b&gt; [bacth_size]
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;batch마다 detection된 object수를 나타냄&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;nmsed_boxes:&lt;/b&gt; [batch_size, keepTopK, 4]
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NMS를 통과한 bounding box 좌표 [x1, y1, x2, y2]&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;nmsed_scores:&lt;/b&gt; [batch_size, keepTopK]
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NMS를 통과한 bounding box score&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;nmsed_classes:&lt;/b&gt; [batch_size, keepTopK]
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NMS를 통과한 bounding box class&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 batchedNMSPlugin에 필요한 parameter(중요한 것은 highlight)은 다음과 같습니다.&amp;nbsp;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;plugin_parameter.png&quot; data-origin-width=&quot;3135&quot; data-origin-height=&quot;2195&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beXEl6/btrH5fAV7AB/cSL97NhqUjKVGxgKeaVOyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beXEl6/btrH5fAV7AB/cSL97NhqUjKVGxgKeaVOyK/img.png&quot; data-alt=&quot;batchedNMSPlugin parameters&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beXEl6/btrH5fAV7AB/cSL97NhqUjKVGxgKeaVOyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeXEl6%2FbtrH5fAV7AB%2FcSL97NhqUjKVGxgKeaVOyK%2Fimg.png&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; alt=&quot;batchedNMSPlugin parameters&quot; loading=&quot;lazy&quot; width=&quot;704&quot; height=&quot;493&quot; data-filename=&quot;plugin_parameter.png&quot; data-origin-width=&quot;3135&quot; data-origin-height=&quot;2195&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;batchedNMSPlugin parameters&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Torch모델 ONNX모델로 변환&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TensorRT Plugin을 사용하기 위해서는 TRT모델로 만들어야 합니다. TRT모델은 ONNX모델으로부터 생성 가능하므로 Torch모델을 ONNX모델로 변환해보죠. 하지만 YOLOv7 모델의 output shape은 [1, 25200, 85]이기 때문에 batchedNMSPlugin의 input shape으로는 맞지 않습니다. 그래서 아래의 코드를 통해 YOLOv7의 output shape을 바꿔보죠.&lt;/p&gt;
&lt;pre id=&quot;code_1658671125125&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    class ProcModel(nn.Module):
        def __init__(self, model, class_num):
            super(ProcModel, self).__init__()
            self.model = model
            self.class_num = class_num
        def forward(self, x):

            out = self.model(x)[0] # out shape = [batch, num_object, 85], 85 = class_num(80)+bbox(4)+confidence(1)
            bbox_out = torch.unsqueeze(out[:,:,:4], 2) # bbox_out shape = [batch, num_object, 1, bbox], bbox = [cx,cy,w,h]

            x1 = bbox_out[:,:,:,0] - bbox_out[:,:,:,2] / 2
            y1 = bbox_out[:,:,:,1] - bbox_out[:,:,:,3] / 2
            x2 = bbox_out[:,:,:,0] + bbox_out[:,:,:,2] / 2
            y2 = bbox_out[:,:,:,1] + bbox_out[:,:,:,3] / 2
            bbox_out = torch.stack((x1,y1,x2,y2), dim=3) # bbox_out shape = [batch, num_object, 1, bbox], bbox = [x1,y1,x2,y2]

            conf_out = out[:,:,4] # [batch, num_object, 1]

            conf_out = torch.reshape(conf_out, (conf_out.shape[1],)) # [batch, num_object]
            class_out = torch.mul(out[:,:,5:].transpose(1,2) , conf_out).transpose(1,2) # [batch, num_object, num_classes]
            return [bbox_out, class_out]

    procmodel = ProcModel(model, 80)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 YOLOv7의 output shape을 batchedNMSPlugin의 input shape에 맞춰 변환하였습니다. YOLOv7 모델은 coco dataset(class num: 80)으로 학습되었으므로 bbox_out의 shape은 [1, 25200, 1, 4]이고 class_out의 shape은 [1,25200, 80]입니다.&amp;nbsp; (bbox_out은 batchedNMSPIugin의 input중 하나인 Boxes input에 대응되고 class out은 Scores out에 대응됨)&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;이제 &lt;code&gt;torch.onnx.export&lt;/code&gt;함수를 통해 ONNX 모델로 변환해봅시다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1658707234301&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;    f = str(weights).replace('.pt', '.onnx')  #  yolov7.pt -&amp;gt; yolov7.onnnx
    input_names = ['images']
    output_names = ['bbox_out','class_out']
    train = False
    opset_version = 12

    torch.onnx.export(procmodel, img, f, verbose=False, opset_version=opset_version,
                      training=torch.onnx.TrainingMode.TRAINING if train else torch.onnx.TrainingMode.EVAL,
                      do_constant_folding=not train,
                      input_names=input_names,
                      output_names=output_names,
                      dynamic_axes=None)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;opset은 12를 기준으로 하였고 training이 아닌 eval버전으로 export하였습니다. 그리고 output_names=[bbox_out, class_out]인것을 기억해야 합니다. 다음 스텝에서 중요한 요소이거든요.&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;위의 코드를 실행 시키면 ONNX 모델의 output이 잘 바뀐 것을 확인 가능합니다. (전체 변환 코드는 &lt;a href=&quot;https://github.com/da2so/yolov7_trt/blob/main/export.py&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;export.py&lt;/a&gt;에서 확인합니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OFPd3/btrH32916FN/L4dOlsBRcULuaCWJt2ZKTk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OFPd3/btrH32916FN/L4dOlsBRcULuaCWJt2ZKTk/img.png&quot; data-alt=&quot;yolov7 onnx model output&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OFPd3/btrH32916FN/L4dOlsBRcULuaCWJt2ZKTk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOFPd3%2FbtrH32916FN%2FL4dOlsBRcULuaCWJt2ZKTk%2Fimg.png&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; alt=&quot;yolov7 onnx model output&quot; loading=&quot;lazy&quot; width=&quot;704&quot; height=&quot;186&quot; data-origin-width=&quot;1832&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;yolov7 onnx model output&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 onnx_graphsurgeon사용하여 batchedNMSPlugin추가&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONNX모델로 변환하면 ONNX 모델의 output shape이 위에서 설명드린 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;batchedNMSPlugin의 input shape과 같은 형태로 변환되었을 것입니다. 이제 onnx모델을 TRT모델로 변환하는 CLI인 &lt;code&gt;trtexec&lt;/code&gt;를 사용하기 전에 onnx_graphsurgeon으로 ONNX 모델에 batchedNMSPlugin을 추가해주어야 합니다.&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;&lt;span&gt;onnx_graphsurgeon는 &lt;a href=&quot;https://github.com/NVIDIA/TensorRT/tree/release/7.2/tools/onnx-graphsurgeon&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TensorRT/tools/onnx-graphsurgeon&lt;/a&gt;에서 제공하는 TensoRT tool로 ONNX model에 ONNX graph를 추가하거나 수정 가능하게 해 줍니다. 해당 폴더에서 1. &lt;code&gt;make install&lt;/code&gt; 2. &lt;code&gt;make build&lt;/code&gt; 명령어로 쉽게 설치 가능합니다. (저는 release/7.2 version을 설치하였습니다)&amp;nbsp;&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;&lt;span&gt;이제 batchedNMSPlugin을 추가하는 코드를 설명드립니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1658708002490&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import onnx_graphsurgeon as gs

def create_attrs(input_h, input_w, topK, keepTopK):
    attrs = {}
    attrs[&quot;shareLocation&quot;] = 1
    attrs[&quot;backgroundLabelId&quot;] = -1
    attrs[&quot;numClasses&quot;] = 80
    attrs[&quot;topK&quot;] = topK
    attrs[&quot;keepTopK&quot;] = keepTopK
    attrs[&quot;scoreThreshold&quot;] = 0.25
    attrs[&quot;iouThreshold&quot;] = 0.6
    attrs[&quot;isNormalized&quot;] = False
    attrs[&quot;clipBoxes&quot;] = False

    # 001 is the default plugin version the parser will search for, and therefore can be omitted,
    # but we include it here for illustrative purposes.
    attrs[&quot;plugin_version&quot;] = &quot;1&quot;

    return attrs

graph = gs.import_onnx(onnx.load('yolov7.onxx')) # load onnx model

batch_size = graph.inputs[0].shape[0]
input_h = graph.inputs[0].shape[2]
input_w = graph.inputs[0].shape[3]
tensors = graph.tensors()

boxes_tensor = tensors[&quot;bbox_out&quot;] # match with onnx model output name
confs_tensor = tensors[&quot;class_out&quot;] # match with onnx model output name
topK = 100
keepTopK = 50

num_detections = gs.Variable(name=&quot;num_detections&quot;).to_variable(dtype=np.int32, shape=[batch_size, 1]) # do not change
nmsed_boxes = gs.Variable(name=&quot;nmsed_boxes&quot;).to_variable(dtype=np.float32, shape=[batch_size, keepTopK, 4])  # do not change
nmsed_scores = gs.Variable(name=&quot;nmsed_scores&quot;).to_variable(dtype=np.float32, shape=[batch_size, keepTopK])  # do not change
nmsed_classes = gs.Variable(name=&quot;nmsed_classes&quot;).to_variable(dtype=np.float32, shape=[batch_size, keepTopK])  # do not change
new_outputs = [num_detections, nmsed_boxes, nmsed_scores, nmsed_classes]  # do not change

nms_node = gs.Node( # define nms plugin
    op=&quot;BatchedNMSDynamic_TRT&quot;, # match with batchedNMSPlugn
    attrs=create_attrs(input_h, input_w, topK, keepTopK), # set attributes for nms plugin
    inputs=[boxes_tensor, confs_tensor],
    outputs=new_outputs)

graph.nodes.append(nms_node) # nms plugin added 
graph.outputs = new_outputs

graph = graph.cleanup().toposort()

onnx.save(gs.export_onnx(graph), 'yolov7_gs.onnx') # save model&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;create_attrs&lt;/code&gt; 함수를 통해 위에서 표로 설명드린 batchedNMSPlugin의 parameter를 설정함
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;numClasses&lt;/code&gt;는 coco dataset를 사용하므로 80&lt;/li&gt;
&lt;li&gt;&lt;code&gt;keepTopK&lt;/code&gt;를 50으로 설정하여 한 이미지 당 detect 가능 object개수 제한&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isNormalized&lt;/code&gt;를 False를 설정하여 yolov7의 bbox 좌표 output이 normalized안되어 있음을 명시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gs.import_onnx&lt;/code&gt; 함수를 통하여 onnx model를 변경할 수 있도록 graph형태로 생성&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tensors['bbox_out'] , tensors['class_out']&lt;/code&gt;은 ONNX model로 변환 시에 output_names로 명시한 이름&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gs.Variable&lt;/code&gt;을 통해 batchedNMSPlugin의 output형태를 만듦&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gs.Node&lt;/code&gt;를 통해 추가하는 batchedNMSPlugin의 노드를 만듦
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;op에 &lt;code&gt;BatchedNMSDynamic_TRT&lt;/code&gt;은 노드 이름이며 이후 trtexec cli실행 시에 해당 이름을 보고 plugin 구현체 만듦&lt;/li&gt;
&lt;li&gt;여기서 attrs인자의 값으로 위의 &lt;code&gt;create_attrs&lt;/code&gt;함수의 리턴 값을 넘김&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;graph.nodes.append(nms_node)&lt;/code&gt;으로 ONNXM model의 뒤에 batchedNMSPlugin삽입
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;여기서 &lt;b&gt;중요한 것은&lt;/b&gt; batchedNMSPlugin의 노드의 이름, 입출력 형태, 해당 노드의 속성만 정의한 것임&lt;/li&gt;
&lt;li&gt;그래서 실제 batchedNMSPlugin의 구현체는 &lt;code&gt;trtexec&lt;/code&gt;를 통해 TRT모델로 변환 시에 생성됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위 코드를 기반으로 실행하면 &lt;b&gt;yolov7_gs.onnx&lt;/b&gt;파일이 새로 생성되었으며 netron으로 까 보면 위에서 정한 노드 이름(BatchedNMSDynamic_TRT)으로 추가된 output 노드를 볼 수 있다. (전체 코드는 &lt;a href=&quot;https://github.com/da2so/yolov7_trt/blob/main/add_nmsplugin.py&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;add_nmsplugin.py&lt;/a&gt; 확인!)&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1646&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YFL2I/btrH0Qa0wO0/P0Smi1u5fUaPdmUAjKiCKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YFL2I/btrH0Qa0wO0/P0Smi1u5fUaPdmUAjKiCKK/img.png&quot; data-alt=&quot;yolov7_gs.onxx: onnx_graphsurgeon으로 추가된 노드&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YFL2I/btrH0Qa0wO0/P0Smi1u5fUaPdmUAjKiCKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYFL2I%2FbtrH0Qa0wO0%2FP0Smi1u5fUaPdmUAjKiCKK%2Fimg.png&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; alt=&quot;onnx_graphsurgeon으로 추가된 노드&quot; loading=&quot;lazy&quot; width=&quot;720&quot; height=&quot;159&quot; data-origin-width=&quot;1646&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;yolov7_gs.onxx: onnx_graphsurgeon으로 추가된 노드&lt;/figcaption&gt;
&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.4 ONNX모델 TRT모델로 변환&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 TRT모델을 만들어 볼 시간입니다. &lt;code&gt;trtexec&lt;/code&gt;명령어로 ONNX 모델을 TRT모델로 변경해보죠.&lt;/p&gt;
&lt;pre id=&quot;code_1658725872780&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;trtexec --onnx=yolov7_gs.onnx --fp16 --workspace=1024 --saveEngine=yolov7_gs.trt&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; --onnx&lt;/b&gt;: input model이며 BatchedNMSDynamic_TRT node가 추가된 ONNX model path&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--fp16&lt;/b&gt;: weight의 data type을 fp16으로 함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--workspace&lt;/b&gt;: workspace의 최대 크기를 정함 (클수록 성능이 올라갈 수 있다 함)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--saveEngine&lt;/b&gt;: TRT모델이 저장될 path&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 CLI를 실행하고 출력된 부분을 보았을 때 아래와 같이 plugin이 정상적으로 생성됨을 알 수 있다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;88&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b0Ifec/btrH75ZQgq8/Azk9nHbvZeLhK8reUlLGj1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b0Ifec/btrH75ZQgq8/Azk9nHbvZeLhK8reUlLGj1/img.png&quot; data-alt=&quot;BatchedNMSDynamic_TRT plugin added&amp;amp;amp;nbsp;successfully&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b0Ifec/btrH75ZQgq8/Azk9nHbvZeLhK8reUlLGj1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb0Ifec%2FbtrH75ZQgq8%2FAzk9nHbvZeLhK8reUlLGj1%2Fimg.png&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; alt=&quot;BatchedNMSDynamic_TRT plugin added successfully&quot; loading=&quot;lazy&quot; width=&quot;763&quot; height=&quot;47&quot; data-origin-width=&quot;1426&quot; data-origin-height=&quot;88&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;BatchedNMSDynamic_TRT plugin added&amp;amp;nbsp;successfully&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 CLI의 동작은 기기마다 다르겠지만 10~30분 걸릴 것이니 직접 하신 다면 편안하게 기다리시죠. 기다리고 나면&lt;b&gt; yolov7_gs.trt&lt;/b&gt;모델이 생성될 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.5Plugin 추가된 TRT모델  실행&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 batchedNMSPlugin이 추가된 TRT모델이 정상적으로 detection 되는지 확인해보죠. TRT모델을 Load하고 inference하는 코드는 이번 글의 목적은 아니니 자세히 설명드리지는 않습니다. (다음 글에서 설명드릴게요!) batchedNMSPlugin이 잘 작동하여 위에서 말씀드린 output대로 잘 나오는지 detection이 잘 되는 지 확인하는 것을 목적으로 합니다.&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;TRT모델로 detection 하는 코드는 &lt;a href=&quot;https://github.com/da2so/yolov7_trt/blob/main/detect_trt_plugin.py&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;detect_trt_plugin.py&lt;/a&gt; 확인 가능합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.5.1 batchedNMSPlugin의 output 확인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yolov7 모델의 input image는 아래와 같이 horse.jpg입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;716&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5KruN/btrH5aNSfg1/knSNelpdNzLZksSULaL1k1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5KruN/btrH5aNSfg1/knSNelpdNzLZksSULaL1k1/img.png&quot; data-alt=&quot;Input image for yolov7 with batchedNMSPlugin&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5KruN/btrH5aNSfg1/knSNelpdNzLZksSULaL1k1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5KruN%2FbtrH5aNSfg1%2FknSNelpdNzLZksSULaL1k1%2Fimg.png&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; alt=&quot;Input image for yolov7 with batchedNMSPlugin&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;352&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;716&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Input image for yolov7 with batchedNMSPlugin&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TensorRT모델을 통해 inference 하는 (부분) 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1658753785713&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        image = input_image.transpose(0, 3, 1, 2) # NHWC to NWHC
        image = np.ascontiguousarray(image) 
        cuda.memcpy_htod(self.inputs[0]['allocation'], image) # input image array(host)를 GPU(device)로 보내주는 작업
        self.context.execute_v2(self.allocations) #inference 실행!
        for o in range(len(self.outputs)):
            cuda.memcpy_dtoh(self.outputs[o]['host_allocation'], self.outputs[o]['allocation']) # GPU에서 작업한 값을 host로 보냄
        
        num_detections = self.outputs[0]['host_allocation'] # detection된 object개수
        nmsed_boxes = self.outputs[1]['host_allocation'] # detection된 object coordinate
        nmsed_scores = self.outputs[2]['host_allocation'] # detection된 object confidence
        nmsed_classes = self.outputs[3]['host_allocation'] # detection된 object class number
        result = [num_detections, nmsed_boxes, nmsed_scores, nmsed_classes]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;batchedNMSPlugin의 output은 위와 같이 총 4개이며 각각이 제대로 출력되는지 확인해봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;trtinf_4.png&quot; data-origin-width=&quot;2974&quot; data-origin-height=&quot;1998&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Hdb8e/btrItm0kVAG/hRtEiursXBN7u8gWVSnjkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Hdb8e/btrItm0kVAG/hRtEiursXBN7u8gWVSnjkK/img.png&quot; data-alt=&quot;yolov7 with NMSPlugin의 output&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Hdb8e/btrItm0kVAG/hRtEiursXBN7u8gWVSnjkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHdb8e%2FbtrItm0kVAG%2FhRtEiursXBN7u8gWVSnjkK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;683&quot; height=&quot;459&quot; data-filename=&quot;trtinf_4.png&quot; data-origin-width=&quot;2974&quot; data-origin-height=&quot;1998&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;yolov7 with NMSPlugin의 output&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위를 통해&amp;nbsp; 총 6개의 object가 detection 되었고 각 object의 좌표는 nms_boxes에서 확인 가능하며 nms_scores와 nmsed_classes 또한 순차적으로 object의 confidence와 class를 나타냅니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.5.2 detection결과 확인&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 output기반으로 detection결과를 확인하면 아래와 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;horse_result.png&quot; data-origin-width=&quot;4652&quot; data-origin-height=&quot;1929&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/s8HeI/btrIdW8BTxz/ScNt9H3X9XAY1xc0qxvTKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/s8HeI/btrIdW8BTxz/ScNt9H3X9XAY1xc0qxvTKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/s8HeI/btrIdW8BTxz/ScNt9H3X9XAY1xc0qxvTKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fs8HeI%2FbtrIdW8BTxz%2FScNt9H3X9XAY1xc0qxvTKk%2Fimg.png&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; alt=&quot;detection result by yolov7&quot; loading=&quot;lazy&quot; width=&quot;834&quot; height=&quot;346&quot; data-filename=&quot;horse_result.png&quot; data-origin-width=&quot;4652&quot; data-origin-height=&quot;1929&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;batchedNMSPlugin이 추가된 상태로 잘 detection 되는 것을 확인 가능합니다! 아래는 실제 &lt;a href=&quot;https://github.com/WongKinYiu/yolov7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;yolov7 repo&lt;/a&gt;(pytorch 모델 + python NMS code)에서 제공하는 horse.jpg에 대한 결과입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m0npX/btrIeLlt9rt/DMKxDsHlfQb9ysqWfVwbzk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m0npX/btrIeLlt9rt/DMKxDsHlfQb9ysqWfVwbzk/img.jpg&quot; data-alt=&quot;yolov7 result (pytorch model + python NMS)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m0npX/btrIeLlt9rt/DMKxDsHlfQb9ysqWfVwbzk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm0npX%2FbtrIeLlt9rt%2FDMKxDsHlfQb9ysqWfVwbzk%2Fimg.jpg&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; alt=&quot;yolov7 result (pytorch model + python NMS)&quot; loading=&quot;lazy&quot; width=&quot;472&quot; height=&quot;313&quot; data-origin-width=&quot;773&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;yolov7 result (pytorch model + python NMS)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/NVIDIA</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/60</guid>
      <comments>https://da2so.tistory.com/60#entry60comment</comments>
      <pubDate>Mon, 25 Jul 2022 23:10:19 +0900</pubDate>
    </item>
    <item>
      <title>YOLOv7: Trainable bag-of-freebies sets new state-of-the-art for real-time object detectors 논문 리뷰</title>
      <link>https://da2so.tistory.com/59</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 현시점에서 YOLO계열 중 가장 성능이 좋은 &lt;a href=&quot;https://arxiv.org/pdf/2207.02696.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;YOLOv7&lt;/a&gt;&amp;nbsp;논문 리뷰해보겠습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Introduction&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문 제목을 보았을 때 가장 먼저 눈에 띄는 것은 &lt;b&gt;&quot;bag-of-freebies&quot;&lt;/b&gt;일텐데요. 이게&amp;nbsp; 무엇이냐!?&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;bag-of-freebies란?&lt;/b&gt;&lt;br /&gt;inference시에 추가적인 cost비용 없이 네트워크의 성능을 향상하기 위한 방법 &lt;br /&gt;(e.g. reparameterization, data augmentation, bbox regression, label smoothing)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 저자들은 inference cost를 증가시키지 않는 training optimization 방법들을 제안하여 성능을 올리 는 것을 목적으로 합니다.(해당 optimization은 cost가 들 수 있음) 그래서 해당 방법들이 적용된 모델이 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;YOLOv7&lt;/b&gt;&lt;/span&gt;이며 GPU device 타겟으로 real-time object detection이 가능하게 합니다.&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;그렇다면 어떤 bag-of-freebies방법을 사용하였을까요?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Model reparameterization&lt;/b&gt;: Training시에 여러 개의 layer(Conv or BN)들을 학습하고 inference시에는 해당 layer들을 하나의 layer로 fusing함
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;대표적인 예시: RepVGG, Conv-BN folding&lt;/li&gt;
&lt;li&gt;YOLOv7에서는 RepVGG를 (조금!) 변형시킨 형태의 reparameterziation방법 제안&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Label assignment:&amp;nbsp;&lt;/b&gt;Ground truth를 그냥 사용하는 것이 아닌 모델의 prediction, ground truth의 distribution을 고려하여 새로 soft label을 만들어냄
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;대표적인 예시: ATSS, OTA, SimOTA(in YOLOX)&lt;/li&gt;
&lt;li&gt;YOLOv7에서는 기존의 label assignment방법들이 다른 branch에서 도출되는 output(output이 두갈래)에 대해 dynamic target을 assignment할 수없다는 것을 문제 삼아 이를 해결함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 두가지 bag-of-freebies가 큰 contribution을 가지며 이외에도 아래 섹션에서 자잘한 방법들도 소개됩니다. 또한 model scaling에 있어서 자체적으로 parameter와 computation을 고려한 방법을 제시하 다양한 계열의 YOLOv7을 제공합니다. 결과적으로 YOLOv7는 설명드린 방법들을 통해 아래와 같이 뛰어난 성능을 보입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cMsKEY/btrHK1CsxAQ/Krzdwag5KSuZ2BCzPpAtKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cMsKEY/btrHK1CsxAQ/Krzdwag5KSuZ2BCzPpAtKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cMsKEY/btrHK1CsxAQ/Krzdwag5KSuZ2BCzPpAtKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcMsKEY%2FbtrHK1CsxAQ%2FKrzdwag5KSuZ2BCzPpAtKK%2Fimg.png&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; alt=&quot;yolov7 performance&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;384&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Architecture&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Extended efficient layer aggregation networks&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;918&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EM020/btrHM7wEA7o/gW9TjZMzQd8uh6SpvLYNF0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EM020/btrHM7wEA7o/gW9TjZMzQd8uh6SpvLYNF0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EM020/btrHM7wEA7o/gW9TjZMzQd8uh6SpvLYNF0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEM020%2FbtrHM7wEA7o%2FgW9TjZMzQd8uh6SpvLYNF0%2Fimg.png&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; alt=&quot;E-ELAN&quot; loading=&quot;lazy&quot; width=&quot;837&quot; height=&quot;401&quot; data-origin-width=&quot;1914&quot; data-origin-height=&quot;918&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Device의 memory cost나 computational density를 줄이기 위해 많은 연구에서 efficient한 architecture구조를 제안해왔습니다. 아래와 같이 VoVNet, CSPVoVNet, ELAN이 그에 대한 예시이며 YOLOv7 모델의 base line 구조입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 연구들에서 제안한 구조와 그에 대한 특징 및 장점을 소개합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;VoVNet&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;DenseNet과 다르게 input channel수가 일정하다는 장점이 있음&lt;/li&gt;
&lt;li&gt;Input channel수가 일정하기 때문에 DenseNet과 다르게 1x1 Depthwise conv를 사용안하기 때문에 Inference time이 더 빠름 (1x1 depthwise conv는 하드웨어 상으로 대부분의 GPU에 가속화되기힘든 구조)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CSPVoVNet&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;VoVNet에 Cross Stage Partial(CSP)구조를 추가한것으로 input channel을 반으로 나누어(partial) 왼쪽의 \(c\)는 그대로 transition layer에 더해짐&lt;/li&gt;
&lt;li&gt;나눠진 \(c\)때문에 기존보다 gradient flow가 truncate되어 과도한 양의 gradient information을 방지함&lt;/li&gt;
&lt;li&gt;뿐만 아니라 \(c\)를 나누고 transition layer에서 병합하기 때문에 gradient path는 2배로 증가하여 다양한 features를 학습할 수 있음&lt;/li&gt;
&lt;li&gt;VoVNet의 장점은 그대로 가져감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ELAN&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기존의 CSPVoVNet의 장점은 그대로 가져감&lt;/li&gt;
&lt;li&gt;CSPVoVNet의 가장 짧고 그리고 가장 긴 gradient path 차이를 더 극대화 시키고 이로 인해 모듈 간소화&lt;/li&gt;
&lt;li&gt;가장 짧고 그리고 가장 긴 gradient path를 controll하여 deep한 네트워크도 학습가능하고 수렴도 효과적으로 잘되게함&amp;nbsp;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;단점:&lt;/b&gt; Computation block을 어느정도 많이 쌓아도 잘 학습됨 하지만 무한대 가까이로 쌓을 경우 stable state가 망가질것이며 이는 parameter utilization이 낮아짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 ELAN의 단점때문에 YOLOv7에서는 마음껏 쌓아도 학습이 잘되도록 하기 위해 E-ELAN을 제안합니다.&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;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;E-ELAN (YOLOv7)&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Expand, shuffle, merge cardinality를 통해 compuational block을 많이 쌓아도 학습능력이 뛰어남&lt;/li&gt;
&lt;li&gt;오직 computational block만 바뀌고(scaling에 따라) transition layer는 절대 바뀌지 않음&lt;/li&gt;
&lt;li&gt;Process
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;computational block들에 대해 channel수를 multiplier 하는 Group conv를 적용 (with \(g\) group parameter)&lt;/li&gt;
&lt;li&gt;CSP로 나눠진 feature와 computational block의 output feature들이 shuffle후 concatenate됨&lt;/li&gt;
&lt;li&gt;Merge cardinality 수행&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Model scaling for concatenation-based models&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 EfficientNet같은 경우 model scaling(e.g. EfficientNet-b0, b1,...)을 width, depth, resolution을 조절해 가며 진행하였습니다. 하지만 위에서 제안한 E-ELAN과 같이 concatenation-based architecture는 scaling-up 또는 down시에 transition layer의 input degree(channel)이 증가하게 됩니다. (아래그림 참조)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;534&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ezaocG/btrHNEvcFIL/ehsvKsPNavfwAIpHzXrkUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ezaocG/btrHNEvcFIL/ehsvKsPNavfwAIpHzXrkUk/img.png&quot; data-alt=&quot;scaling up시에 width(channel 수)도 자동적으로 변경되어 scaling factor에 대한 분석이 힘듬&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ezaocG/btrHNEvcFIL/ehsvKsPNavfwAIpHzXrkUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FezaocG%2FbtrHNEvcFIL%2FehsvKsPNavfwAIpHzXrkUk%2Fimg.png&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; alt=&quot;scaling up시 문제점&quot; loading=&quot;lazy&quot; width=&quot;555&quot; height=&quot;198&quot; data-origin-width=&quot;1496&quot; data-origin-height=&quot;534&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;scaling up시에 width(channel 수)도 자동적으로 변경되어 scaling factor에 대한 분석이 힘듬&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 transition layer의 input, output channel이 모두 변경되고 output channel이 변경된다는 것은 그 다음 transition layer의 input channel또한 증가하여 이는 반복적으로 발생하며 scaling factor에 대해 분석이 불가능하게 됩니다.&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;그래서 이러한 문제점을 해결하기 위해 YOLOv7은 다음과 같은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;compound model scaling method&lt;/b&gt;&lt;/span&gt;를 제안합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCFgQE/btrHPd39cTS/jNibLa73DiXDVLgWJePGHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCFgQE/btrHPd39cTS/jNibLa73DiXDVLgWJePGHk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCFgQE/btrHPd39cTS/jNibLa73DiXDVLgWJePGHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCFgQE%2FbtrHPd39cTS%2FjNibLa73DiXDVLgWJePGHk%2Fimg.png&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; alt=&quot;compound scaling method&quot; loading=&quot;lazy&quot; width=&quot;523&quot; height=&quot;203&quot; data-origin-width=&quot;1446&quot; data-origin-height=&quot;560&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;Computation block에만 depth scaling up을 적용하고 이에 맞춰 output channel 변경&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Computation block의 output channel이 늘어났으므로 transition layer의 width scaling up적용&lt;/li&gt;
&lt;li&gt;Partial 부분의 input feature는 전 transition layer의 output feature(scaled up)이므로&amp;nbsp; 똑같이 partial 부분도 width scaling up됨&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Trainable bag-of-freebies&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Planned re-parameterized convolution&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLOv7은 RepVGG의 변형을 사용하였습니다. RepVGG는 reparameterization방법을 사용한 것으로 아래와 같이 training시에는 병렬적으로 여러개의 conv+BN들을 학습하다가 inference시에 해당 conv+BN들을 하나의 conv로 reparameterization(fusing)합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;1524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kyGO6/btrHOjYD4lc/WYNXhPcKZHiEabYGrdDT1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kyGO6/btrHOjYD4lc/WYNXhPcKZHiEabYGrdDT1k/img.png&quot; data-alt=&quot;RepVGG&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kyGO6/btrHOjYD4lc/WYNXhPcKZHiEabYGrdDT1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkyGO6%2FbtrHOjYD4lc%2FWYNXhPcKZHiEabYGrdDT1k%2Fimg.png&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; alt=&quot;RepVGG&quot; loading=&quot;lazy&quot; width=&quot;457&quot; height=&quot;535&quot; data-origin-width=&quot;1302&quot; data-origin-height=&quot;1524&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;RepVGG&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 RepVGG의 RepConv의 identity connection은 ResNet의 residual이나 DenseNet의 concatenation을 destroy하게 됩니다. (d)를 예시로 볼때 RepConv의 identity connection과 맨 위의 input \(c\)에서 오는 residual(or concatenation) connection이 중복됨&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;1398&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AdkcW/btrHPKOwIVA/tnmFVizucQueO8XKlSDJ60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AdkcW/btrHPKOwIVA/tnmFVizucQueO8XKlSDJ60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AdkcW/btrHPKOwIVA/tnmFVizucQueO8XKlSDJ60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAdkcW%2FbtrHPKOwIVA%2FtnmFVizucQueO8XKlSDJ60%2Fimg.png&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; alt=&quot; RepConvN&quot; loading=&quot;lazy&quot; width=&quot;475&quot; height=&quot;462&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;1398&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 YOLOv7에서는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;RepConv에서 identity connection이 없는 RepConvN&lt;/b&gt;&lt;/span&gt;을 제안합니다. 그래서 residual이나 concatenation connection은 reparameterization되는 구조입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Coarse for auxiliary and fine for lead loss&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deep supervision은 network의 중간에 auxiliary head를 추가하여 assistant loss를 도입하게 되고 이렇게 추가된 assistant loss를 통해 효과적으로 성능을 올릴 수 있습니다. 아래는 object detector는 deep supervision을 사용했을 경우와 아닌 경우를 직관적으로 보여줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HuNqR/btrHOTys0ZT/IAOFbAan3sRyw7g9MVS4A1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HuNqR/btrHOTys0ZT/IAOFbAan3sRyw7g9MVS4A1/img.png&quot; data-alt=&quot;Deep supervision for object detector&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HuNqR/btrHOTys0ZT/IAOFbAan3sRyw7g9MVS4A1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHuNqR%2FbtrHOTys0ZT%2FIAOFbAan3sRyw7g9MVS4A1%2Fimg.png&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; alt=&quot;Deep supervision for object detector&quot; loading=&quot;lazy&quot; width=&quot;435&quot; height=&quot;234&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Deep supervision for object detector&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 논문에서는 원래의 final output을 lead head, 추가된 output을 auxiliary head라 명명합니다.&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;위와&amp;nbsp; 같이 YOLOv7은 deep supervision을 사용하게 되는 데 문제점이 발생합니다. 바로 대부분의 기존 label assignment방법들은 lead와 auxiliary head와 같이 2가지 head가 동시에 있을 경우 soft label을 만들어 낼 수 없습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;YOLO의 label assignment의 example&lt;/b&gt;&lt;br /&gt;&amp;rarr; prediction한 bounding box와 ground truth의 IoU를 objectness의 soft label로 사용하여 오직 ground truth를 사용한것 보다 성능이 향상&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그나마 아래와 같이 lead head와 auxiliary head를 독립적으로 계산하여 label assignment를 구현한 것이 최근의 연구입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;608&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgS9S8/btrHPKgHZsW/Yk8MwjWVgYwcJvNo1YZme0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgS9S8/btrHPKgHZsW/Yk8MwjWVgYwcJvNo1YZme0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgS9S8/btrHPKgHZsW/Yk8MwjWVgYwcJvNo1YZme0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgS9S8%2FbtrHPKgHZsW%2FYk8MwjWVgYwcJvNo1YZme0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;194&quot; height=&quot;237&quot; data-origin-width=&quot;498&quot; data-origin-height=&quot;608&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLOv7에서는&lt;span style=&quot;color: #0593d3;&quot;&gt; &lt;b&gt;(1)&lt;/b&gt; &lt;/span&gt;lead head prediction을 사용하여 &lt;span&gt;lead head와&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;auxiliary head학습하거나 &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;(2) &lt;/b&gt;&lt;/span&gt;lead head prediction을 guidance로 사용하여 coarse-to-fine hierarchical labels을 생성해냅니다. coarse-to-fine hierarchical labels은 auxiliary head와 lead head 학습에 사용됩니다. 제안하는 2가지 Deep supervision label assignment의 구조는 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MiXYe/btrHN2CUSeO/R2LePUFM9hfKOPt2nQoqyK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MiXYe/btrHN2CUSeO/R2LePUFM9hfKOPt2nQoqyK/img.png&quot; data-alt=&quot;Deep supervision label assignment&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MiXYe/btrHN2CUSeO/R2LePUFM9hfKOPt2nQoqyK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMiXYe%2FbtrHN2CUSeO%2FR2LePUFM9hfKOPt2nQoqyK%2Fimg.png&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; alt=&quot;Deep supervision label assignment&quot; loading=&quot;lazy&quot; width=&quot;454&quot; height=&quot;233&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Deep supervision label assignment&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.2.1 Lead head guided label assigner&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말씀드렸듯이 lead head의 prediction과 ground truth을 기반으로 soft label을 생성해냅니다. 해당 soft label은 모델의 auxiliary, lead head의 training에 사용됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 lead head의 prediction만 이용한 이유는 lead head가 상대적으로 강한 learning capability를 가지고 있어 prediction과 ground truth의 distribution이나 correlation을 더 잘 표현가능하기 때문입니다. 또한 auxiliary head가 lead head가 학습했던 내용을 학습할 수 있으므로 lead head는 학습하지 못한 다른 featurea를 학습할 수 있는 residual learning이 가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.2.2 Coarse-to-fine lead head guided label assigner&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 방법도 lead head와 ground truth를 이용해 soft label을 생성합니다. 그러나 여기서는 두가지 다른 set의 soft label을 만들어냅니다.&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;b&gt;Coarse label&lt;/b&gt;: grid를 좀 더 positive target으로 여겨지도록 하여 생성된 label
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Positive sample assignment process의 constraint를 완화시키는 방법으로 만듬&lt;/li&gt;
&lt;li&gt;constraint를 완화했다는 것은 coarse positive grids(label)가 fine label(soft label)처럼 완벽하고 섬세한 label을 만들어 지지 않게 함&lt;/li&gt;
&lt;li&gt;Auxiliary head의 학습에만 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Fine label&lt;/b&gt;: 위의 lead head guided label assigner와 같은 soft label
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Lead head의 학습에 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lead head는 high recall, precision을 모두 가능하지만 auxiliary head는 그럴 능력이 없기 때문에 coarse label만 학습하게 하여 recall에만 optimization하는것을 목적으로 합니다. ( auxiliary head를 학습하는데 coarse label을 fine label처럼 만들면 결국 bad prior만 학습하게된다고 하네요.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 Other traininable bag-of-freebies&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;총 3가지의 추가적인 bag-of-freebies방법을 사용하였습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Batch normalization in conv-bn-activation topology
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Conv와 BN의 연결은 선형식이므로 fusing함&lt;/li&gt;
&lt;li&gt;이는 RepConv에서도 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implicit knowledge (아래 그림 참조)&amp;nbsp;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;관찰(입력데이터)과 상관없이 모델에 내재된 지식을 뜻함&lt;/li&gt;
&lt;li&gt;YOLOR에서 제안된 방법으로 convolution feature map에 Implicit knowledge로 학습된 vector를 곱(or 더)해줌으로써 성능향상에 도움을 줌&lt;/li&gt;
&lt;li&gt;해당 Implicit knowledge또한 inference시에는 이전 또는 이후 convolution layer와 fuisng 됨 (선형적인 vector의 곱셈 또는 덧셈이므로)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;EMA(Expoinential Moving Average) model
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;원래(이전의) 학습하던 방향에서 급격하게 다른 방향으로 학습하지 못하게 하여 일정한 학습방향성을 유지시켜주는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;implicit.png&quot; data-origin-width=&quot;2708&quot; data-origin-height=&quot;2310&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwW1YQ/btrHWwa3xQm/ZfvWtEkVHZqzqkpY3fzqr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwW1YQ/btrHWwa3xQm/ZfvWtEkVHZqzqkpY3fzqr0/img.png&quot; data-alt=&quot;Implicit knowledge&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwW1YQ/btrHWwa3xQm/ZfvWtEkVHZqzqkpY3fzqr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwW1YQ%2FbtrHWwa3xQm%2FZfvWtEkVHZqzqkpY3fzqr0%2Fimg.png&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; alt=&quot;Implicit knowledge&quot; loading=&quot;lazy&quot; width=&quot;425&quot; height=&quot;363&quot; data-filename=&quot;implicit.png&quot; data-origin-width=&quot;2708&quot; data-origin-height=&quot;2310&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Implicit knowledge&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Experiments&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1 Experimental setup&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;COCO dataset에 대해 성능 평가를 진행하였으며 제안한 YOLOv7모델은 pretrained model을 사용하지 않았다고 합니다. validation set을 통해 hyperparameter를 조정하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Edge GPU, normal GPU, cloud GPU용 모델을 각각 &lt;b&gt;YOLOv7-tiny, YOLOv7, YOLOv7-W6&lt;/b&gt;이라고 지칭
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;YOLOv7-tiny는 leaky ReLU사용&lt;/li&gt;
&lt;li&gt;나머지는 다 SiLU사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;YOLOv7기준으로 Neck부분에 제안한 compound scaling up을 한것이 &lt;b&gt;YOLOv7-X&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;YOLOv7-W6기준으로 compound scaling up한것이 &lt;b&gt;YOLOv7-E6, YOLOv7-D6&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;YOLOv7-E6에 E-ELAN을 사용한 모델이 &lt;b&gt;YOLOv7-E6E&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.2 Comparison with Baselines&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2370&quot; data-origin-height=&quot;1614&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m4qEY/btrHWFsnCDa/hTCmnvkR3PzX12qE6SUrh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m4qEY/btrHWFsnCDa/hTCmnvkR3PzX12qE6SUrh1/img.png&quot; data-alt=&quot;Comparison with Baselines&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m4qEY/btrHWFsnCDa/hTCmnvkR3PzX12qE6SUrh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm4qEY%2FbtrHWFsnCDa%2FhTCmnvkR3PzX12qE6SUrh1%2Fimg.png&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; alt=&quot;Comparison with Baselines&quot; loading=&quot;lazy&quot; width=&quot;763&quot; height=&quot;520&quot; data-origin-width=&quot;2370&quot; data-origin-height=&quot;1614&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Comparison with Baselines&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.3 &lt;span style=&quot;color: #000000;&quot;&gt;Comparison&lt;/span&gt; with state-of-the-arts&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1690&quot; data-origin-height=&quot;1370&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/epvcfY/btrHXTqmQvw/ArYleWgW83HSrz5pX34bbK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/epvcfY/btrHXTqmQvw/ArYleWgW83HSrz5pX34bbK/img.png&quot; data-alt=&quot;Comparison with state-of-the-arts&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/epvcfY/btrHXTqmQvw/ArYleWgW83HSrz5pX34bbK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FepvcfY%2FbtrHXTqmQvw%2FArYleWgW83HSrz5pX34bbK%2Fimg.png&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; alt=&quot;Comparison with state-of-the-arts&quot; loading=&quot;lazy&quot; width=&quot;793&quot; height=&quot;643&quot; data-origin-width=&quot;1690&quot; data-origin-height=&quot;1370&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Comparison with state-of-the-arts&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;1298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zd8yu/btrHTEVYzXI/LtxN8iYykyK6M186Ww8tv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zd8yu/btrHTEVYzXI/LtxN8iYykyK6M186Ww8tv1/img.png&quot; data-alt=&quot;More comparison for YOLOv7&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zd8yu/btrHTEVYzXI/LtxN8iYykyK6M186Ww8tv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fzd8yu%2FbtrHTEVYzXI%2FLtxN8iYykyK6M186Ww8tv1%2Fimg.png&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; alt=&quot;More comparison for YOLOv7&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;1001&quot; data-origin-width=&quot;1040&quot; data-origin-height=&quot;1298&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;More comparison for YOLOv7&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI paper review/Mobile-friendly</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/59</guid>
      <comments>https://da2so.tistory.com/59#entry59comment</comments>
      <pubDate>Fri, 22 Jul 2022 09:47:32 +0900</pubDate>
    </item>
    <item>
      <title>[NVIDIA] DeepStream 이해 및 설명</title>
      <link>https://da2so.tistory.com/58</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1. DeepStream이란? &lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Deepstream&lt;/span&gt;은 SDK형태로 제공하며 Vision AI application과 service를 쉽고 빠르게 개발할 수 있게 해줍니다.&lt;/b&gt; 그리고 DeepStream은 multi-platform, scaleablity를 제공하며 on-premise, on-edge, cloud환경 모두에서 deploy가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Multi-platform&lt;/b&gt;: window, mac, linux 등의 다양한 플랫폼을 의미&lt;br /&gt;&lt;b&gt;Scaleability&lt;/b&gt;: 유저수나 트래픽이 많아져도 application이 잘 작동함을 의미&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흠... 여기까지 들었을때는 그래서 DeepStream이 먼지 모르시겠죠? 쉽게 설명을 위해 NVIDIA 세계관(?)의 End-to-END AI Development는 아래와 같습니다. 빨간색 부분이 DeepStream이며 해당부분(3번)에서 알 수 있듯이 &lt;b&gt;만들어진(training이 끝난) ML 모델을 특정 application이나 service에 deploy하기 위해 사용되는 것이 DeepStream&lt;/b&gt;입니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;deepstream.png&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;722&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/buu4J9/btrG5qxWuCd/hT1QO5CN3c6rCew9og7T0k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/buu4J9/btrG5qxWuCd/hT1QO5CN3c6rCew9og7T0k/img.png&quot; data-alt=&quot;End-to-END AI Development in NVIDIA&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/buu4J9/btrG5qxWuCd/hT1QO5CN3c6rCew9og7T0k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbuu4J9%2FbtrG5qxWuCd%2FhT1QO5CN3c6rCew9og7T0k%2Fimg.png&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; alt=&quot;End-to-END AI Development in NVIDIA&quot; loading=&quot;lazy&quot; width=&quot;785&quot; height=&quot;284&quot; data-filename=&quot;deepstream.png&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;722&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;End-to-END AI Development in NVIDIA&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 DeepStream은 &lt;b&gt;(1)&lt;/b&gt;입력 데이터(e.g. 드론이 찍은 항공이미지, 로봇의 카메라, 미디어 동영상)를 받아 &lt;b&gt;(2)&lt;/b&gt;ML 모델의 입력 shape이나 특성에 맞게 preprocess를 하고 &lt;b&gt;(3)&lt;/b&gt;ML 모델로 inference를 하고 &lt;b&gt;(4)&lt;/b&gt;postprocess까지 한 뒤 &lt;b&gt;(5)&lt;/b&gt;분석 및 시각화까지 하는 것을 목적으로 합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2276&quot; data-origin-height=&quot;1022&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zVBrE/btrG6XWGacm/un9lhl7gDmVG7Vo0UapfcK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zVBrE/btrG6XWGacm/un9lhl7gDmVG7Vo0UapfcK/img.png&quot; data-alt=&quot;DeepStream SDK&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zVBrE/btrG6XWGacm/un9lhl7gDmVG7Vo0UapfcK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzVBrE%2FbtrG6XWGacm%2Fun9lhl7gDmVG7Vo0UapfcK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;726&quot; height=&quot;326&quot; data-origin-width=&quot;2276&quot; data-origin-height=&quot;1022&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;DeepStream SDK&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. DeepStream을 왜 써야할까?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepStream만의 강점은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Seamless Development&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;C/C++, Python, low-code graphical programming(Graph Composer)와 같이 다양한 development를 제공&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;DeepStream ships은 다양한 hardware accelerated plugins를 제공&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;다양한 pretrained된 ML model(e.g. SSD, YOLO, MaskRCNN)을 제공&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Custom function과 libraries과 통합가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;TensoRT를 이용하여 multi-GPU, mult-stream, batch support 등 다양한 옵션을 제공하여 극대화된 performance를 제공&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Low-Code Programming with Graph Composer&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;Graph Composer는 low-code graphical progamming을 제공을 통해 손쉽게 복잡한 pipelines을 만들어 줄 수 있음&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;위의  pipelining을 docker container builder를 통해 processing 및 deploy 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b74YV8/btrG6Xbinsc/kEfzSHkM6gyugaRSFLbAJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b74YV8/btrG6Xbinsc/kEfzSHkM6gyugaRSFLbAJ0/img.png&quot; data-alt=&quot;Graph Composer를 통한 pipelining예시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b74YV8/btrG6Xbinsc/kEfzSHkM6gyugaRSFLbAJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb74YV8%2FbtrG6Xbinsc%2FkEfzSHkM6gyugaRSFLbAJ0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;319&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Graph Composer를 통한 pipelining예시&lt;/figcaption&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;&lt;b&gt;Securely Manage Apps &amp;amp; Services&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;DeepStream SDK은 cloud 또는 edge system에 유연하게 실행될 수 있음&lt;/li&gt;
&lt;li&gt;안전한 IoT device communication을 위해 two-way TLS &lt;span style=&quot;background-color: #ffffff;&quot;&gt;authentication(with SSL certificates&lt;span&gt;)을 제공&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;IoT 통합 인터페이스를&lt;/span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Redis, Kafka, MQTT,&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;and&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;AMQP와 함께 제공&lt;/li&gt;
&lt;li&gt;Containerized apps을 관리하기위해 &lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Kubernetes와&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;Helm Charts 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DeepStream SDK plug-ins&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Stereo Camera 지원&lt;/li&gt;
&lt;li&gt;H.264, H.265 video decoding&lt;/li&gt;
&lt;li&gt;JPEG decoding&lt;/li&gt;
&lt;li&gt;Metadata generation and encoding&lt;/li&gt;
&lt;li&gt;Metadata serialization/deserialization&lt;/li&gt;
&lt;li&gt;Stream aggregation and batching&lt;/li&gt;
&lt;li&gt;Object tracking 지원&lt;/li&gt;
&lt;li&gt;Accelerated X11/EGL-based rendering&lt;/li&gt;
&lt;li&gt;Scaling, format conversion, and rotation&lt;/li&gt;
&lt;li&gt;Filtering based on Region of Interest (ROI)&lt;/li&gt;
&lt;li&gt;Audio/Video Template Plug-In&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. DeepStream의 Performance&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DeepStream을 이용했을 때 end-to-end application performance를 측정하였습니다. End-to-end application에는 d&lt;span style=&quot;background-color: #ffffff;&quot;&gt;ata ingestion, decoding, image processing이 포함되어있고 1080p/30fps streams을 input으로 사용하였습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2670&quot; data-origin-height=&quot;854&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3yFar/btrG8XBliXh/K1jHYxo0Fs2vpdAdy11Itk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3yFar/btrG8XBliXh/K1jHYxo0Fs2vpdAdy11Itk/img.png&quot; data-alt=&quot;Performance of Deepstream&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3yFar/btrG8XBliXh/K1jHYxo0Fs2vpdAdy11Itk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3yFar%2FbtrG8XBliXh%2FK1jHYxo0Fs2vpdAdy11Itk%2Fimg.png&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; alt=&quot;Performance of Deepstream&quot; loading=&quot;lazy&quot; width=&quot;807&quot; height=&quot;258&quot; data-origin-width=&quot;2670&quot; data-origin-height=&quot;854&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Performance of Deepstream&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 다양한 task의 application과 다양한 모델에 대해 model inference속도(FPS)를 측정한 것을 알 수 있습니다. 이 Performance가 좋은 것은지는 다른 framework/system이랑 비교를 통해 확인해봐야겟지만 그래두 좋다는 거겠죠? ㅋㅋ&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;다음 글에서는 DeepStream을 실제로 사용하는 방법 및 코드를 설명드리도록 하겠습니다. 감사합니다~~&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Reference&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://developer.nvidia.com/deepstream-sdk&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://developer.nvidia.com/deepstream-sdk&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1657639602568&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;NVIDIA DeepStream SDK&quot; data-og-description=&quot;Build and deploy AI-powered Intelligent Video Analytics apps and services. DeepStream offers a multi-platform scalable framework to deploy on the edge or connect to any cloud.&quot; data-og-host=&quot;developer.nvidia.com&quot; data-og-source-url=&quot;https://developer.nvidia.com/deepstream-sdk&quot; data-og-url=&quot;https://developer.nvidia.com/deepstream-sdk&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dDN1GZ/hyO3oq9ptT/xfFd7FbFUvozmNtaxSSvS0/img.png?width=4078&amp;amp;height=1646&amp;amp;face=0_0_4078_1646,https://scrap.kakaocdn.net/dn/k6nfd/hyO3gmnmbV/z8ontOxdQjEsFuj3Ak6HoK/img.png?width=1494&amp;amp;height=744&amp;amp;face=0_0_1494_744&quot;&gt;&lt;a href=&quot;https://developer.nvidia.com/deepstream-sdk&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://developer.nvidia.com/deepstream-sdk&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dDN1GZ/hyO3oq9ptT/xfFd7FbFUvozmNtaxSSvS0/img.png?width=4078&amp;amp;height=1646&amp;amp;face=0_0_4078_1646,https://scrap.kakaocdn.net/dn/k6nfd/hyO3gmnmbV/z8ontOxdQjEsFuj3Ak6HoK/img.png?width=1494&amp;amp;height=744&amp;amp;face=0_0_1494_744');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;NVIDIA DeepStream SDK&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Build and deploy AI-powered Intelligent Video Analytics apps and services. DeepStream offers a multi-platform scalable framework to deploy on the edge or connect to any cloud.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;developer.nvidia.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.nvidia.com/en-us/on-demand/session/gtcspring22-s41777/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.nvidia.com/en-us/on-demand/session/gtcspring22-s41777/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1657639656735&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;How To Develop and Optimize Edge AI apps with NVIDIA DeepStream | NVIDIA On-Demand&quot; data-og-description=&quot;Learn how the latest features of DeepStream are making it easier than ever to achieve real-time performance even for complex video AI applications&quot; data-og-host=&quot;www.nvidia.com&quot; data-og-source-url=&quot;https://www.nvidia.com/en-us/on-demand/session/gtcspring22-s41777/&quot; data-og-url=&quot;https://www.nvidia.com/en-us/on-demand/session/gtcspring22-s41777/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cYnYbn/hyO3o5KLcu/23SMDv94FQEnLOnW1NpEDk/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=862_127_989_265,https://scrap.kakaocdn.net/dn/pCDIE/hyO3oSdDTg/yRRNicKlzPU1mbsKCY9Qmk/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=862_127_989_265&quot;&gt;&lt;a href=&quot;https://www.nvidia.com/en-us/on-demand/session/gtcspring22-s41777/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.nvidia.com/en-us/on-demand/session/gtcspring22-s41777/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cYnYbn/hyO3o5KLcu/23SMDv94FQEnLOnW1NpEDk/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=862_127_989_265,https://scrap.kakaocdn.net/dn/pCDIE/hyO3oSdDTg/yRRNicKlzPU1mbsKCY9Qmk/img.jpg?width=1200&amp;amp;height=675&amp;amp;face=862_127_989_265');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;How To Develop and Optimize Edge AI apps with NVIDIA DeepStream | NVIDIA On-Demand&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Learn how the latest features of DeepStream are making it easier than ever to achieve real-time performance even for complex video AI applications&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.nvidia.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/NVIDIA</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/58</guid>
      <comments>https://da2so.tistory.com/58#entry58comment</comments>
      <pubDate>Wed, 13 Jul 2022 00:29:57 +0900</pubDate>
    </item>
    <item>
      <title>VMAF score 란?</title>
      <link>https://da2so.tistory.com/57</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;해당 글은 Netflix의 &lt;a href=&quot;https://netflixtechblog.com/toward-a-practical-perceptual-video-quality-metric-653f208b9652#6aad&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;VMAF post&lt;/a&gt;를 참고 및 번역하였으며 VMAF의 이해와 사용 방법에 대해 적어보려 합니다. 목차는 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #1b711d;&quot;&gt;&lt;b&gt;목차&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Video Quality Metric이란?&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;기존의 Video Quality Metric 방법들과 문제점&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;VMAF란?&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;VMAF의 사용 방법&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Video Quality Metric 이란?&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 .png, .jpg와.mp4와 같은 이미지/비디오 파일을 많이 봐왔을 것입니다. 해당 확장자를 가진 파일들은 원본 이미지/비디오 파일을 의미하는 것이 아닌 encoding된 파일을 의미합니다. 원본 파일의 모든 픽셀값을 그대로 local/cloud storage에 저장하는 것은 용량에 부하가 크기 때문에 해당 문제를 해결하기 위해 encoding이라는 압축방법을 사용하며 encoding된 파일을 담는 그릇 개념의 확장자가 .mp4입니다. (encoding은 코덱으로 진행가능하며 대표적인 방법으로는 H.264, H.265 가 있습니다.)&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;Encoding 즉, 압축을 하는 방법은 여러가지 일텐데 각 방법들이 압축을 잘하고 있는지(인간의 눈에 맞춰진 압축인지) 비교 및 확인하려면 어떻게 해야 할까요? &lt;b&gt;바로 그 확인을 &lt;span style=&quot;color: #006dd7;&quot;&gt;Video Quality Metric&lt;/span&gt;을 통해 하게됩니다.&amp;nbsp;&lt;/b&gt;그렇기 때문에 Video Quality Metric이 정확해야 각 encoding방법들이 효과적인지 판단하는 지표가 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 Dataset&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Video Quality Metric을 측정하기 위한 데이터가 필요할 텐데요. 그래서 Netflix에서는 자신들이 가진 다양한 장르와 다양한 화질의 비디오를 이용해 dataset을 구성하였습니다. 정확히 총 34개의 clip으로 이루어진 &lt;b&gt;&lt;i&gt;reference video&lt;/i&gt;&lt;/b&gt;로 dataset을 구성하였으며 자세한 특성은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Dataset(videos)에는 high-level features와 low-level features가 모두 포함됨
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;High-level features&lt;/span&gt;: animation, indoor/outdoor, camera motion, face close-up, people, water, number of objects&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;Low-level features&lt;/span&gt;: film grain noise, brightness, contrast, texture, motion, color variance, color richness, sharpness&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;H.264/AVC로 encoding 및 decoding&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;384x288 ~ 1920x1080사이의 다양한 해상도 사용&lt;/li&gt;
&lt;li&gt;375 kbps ~ 20,000 kbps사의 bitrates를 가짐&lt;/li&gt;
&lt;li&gt;해당 codec을 거친 video을 &lt;i&gt;&lt;b&gt;distorted videos&lt;/b&gt;&lt;/i&gt;라 명명&lt;/li&gt;
&lt;li&gt;총 300개의 distorted video생성&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.2 Differential Mean Opinion Score (DMOS)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 Video Quality Metric들과 제안하는 VMAF가 실제 인간이 보는 시선과 같은 지 확인하기 위해 &lt;i&gt;사람들에게 distorted video가 reference video로부터 얼마나 손상(impairment)되었는 지 측정&lt;/i&gt;하도록 하였습니다. 손상이 클수록 0점에 가깝고 작을수록 100점에 가깝도록 기록하였습니다. 여러 사람의 해당 기록을 모아 평균하여 점수로 표현한 것이 DMOS이며 이는 ground truth(label)로 사용됩니다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 기존의 Video Quality Metric 방법들과 문제점&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존에 자주 쓰이는 Video Quality Metric은 아래와 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;PSNR(Peak signal to noise ratio)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;최대 전력에 대한 잡음의 전력을 의미하며 distorted video와 reference video의 차이(MSE)을 잡음이라 정의하여 구함&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #202122;&quot;&gt;값의 차이만 구하기 때문에 실제 사람의 인지 시각을 정확히 반영하지 못함&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;SSIM(&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;Structural Similarity Index)&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #ffffff; color: #292929;&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;시각적 화질 차이를 평가하기위한 방법으로 &lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;Luminance, Contrast, Structural 이 3가지 측면에서 품질을 평가함.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li id=&quot;8a96&quot; data-selectable-paragraph=&quot;&quot;&gt;&lt;b&gt;Multiscale FastSSIM&lt;/b&gt;&lt;/li&gt;
&lt;li id=&quot;87c3&quot; data-selectable-paragraph=&quot;&quot;&gt;&lt;b&gt;PSNR-HVS&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 metric들은 실제 사람이 평가한 DMOS와 비교했을 때 문제점을 갖습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Qualitative Comparison&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;DMOS와 PSNR을&lt;span&gt; 정성적으로 비교해보았습니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;아래와 같이 4개의 distorted video가 존재할 때  위쪽의 두 video은 PSNR이 31dB가 측정되었고 아래 두 개의 video는 PSNR이 34dB로 측정되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;818&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/NZAUq/btrGQofOKcO/8hKVRCx0OLFJwSNEfPYW2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/NZAUq/btrGQofOKcO/8hKVRCx0OLFJwSNEfPYW2K/img.png&quot; data-alt=&quot;Distorted video examples&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/NZAUq/btrGQofOKcO/8hKVRCx0OLFJwSNEfPYW2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FNZAUq%2FbtrGQofOKcO%2F8hKVRCx0OLFJwSNEfPYW2K%2Fimg.png&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; alt=&quot;distorted video examples&quot; loading=&quot;lazy&quot; width=&quot;813&quot; height=&quot;472&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;818&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Distorted video examples&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 보기에 왼쪽의 Crowd 사진에서는 위아래 사진이 별 차이가 없기 때문에 위 사진이 DMOS가 82, 아래가 96이 기록되었습니다. 하지만 오른쪽 fox 사진은 사람이 보기에도 차이가 분명하기 때문에 위 사진은 DMOS가 27, 아래가 58로 기록되어 큰 차이를 보입니다. 즉, PSNR의 값차이가 DMOS차이와 사진에 따라 다를 수 있으므로 적절한 video quality metric이 아님을 알 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Quantative Comparison&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서는 위의 각각의 4가지 metric을 DMOS와의 상관관계를 측정하였으며 다수의 distorted video을 대상으로 진행하였습니다. 각각의 video quality metric이 optimal 하다고 하면 DMOS와 정비례 관계가 되어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;1384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nIMWu/btrGSyuI8WH/Qi7lACW1u0q0a5yIwDLKn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nIMWu/btrGSyuI8WH/Qi7lACW1u0q0a5yIwDLKn1/img.png&quot; data-alt=&quot;The points with the same color correspond to distorted videos stemming from the same reference video. Due to subject variability and reference video normalization to 100, some DMOS scores can exceed 100.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nIMWu/btrGSyuI8WH/Qi7lACW1u0q0a5yIwDLKn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnIMWu%2FbtrGSyuI8WH%2FQi7lACW1u0q0a5yIwDLKn1%2Fimg.png&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; alt=&quot;previous video quality metrics comparison&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;652&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;1384&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;The points with the same color correspond to distorted videos stemming from the same reference video. Due to subject variability and reference video normalization to 100, some DMOS scores can exceed 100.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 위 그림처럼 &lt;b&gt;각각의 기존 metric들은 DMOS와 정비례 관계가 아님을 알 수 있으며 이는 적절한 video quality metric 또한 아님&lt;/b&gt;을 말합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 video의 성격을 나누어 DMOS와 각각의 metric을 측정하였을 경우에도 정비례가 관계가 아닙니다. (video의 성격은 아래와 같이 High Noise, CG Animation, TV Drama로 구분하였습니다.) 예를 들어 PSNR의 경우 TV Drama에 대해 32~36 dB사이의 값 안에서만 측정되어 비슷한 quality의 동영상들이라고 말하고 있지만 실제 사람들이 측정한 DMOS경우 20~100까지의 폭넓은 범위를 가지며 각기 다른 화질의 비디오들이라고 판단하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;1452&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/C4cr4/btrGP4IHkW5/vct173Jr5i94HWaRfD7kOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/C4cr4/btrGP4IHkW5/vct173Jr5i94HWaRfD7kOk/img.png&quot; data-alt=&quot;Previous video quality metrics comparison by title&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/C4cr4/btrGP4IHkW5/vct173Jr5i94HWaRfD7kOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FC4cr4%2FbtrGP4IHkW5%2Fvct173Jr5i94HWaRfD7kOk%2Fimg.png&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; alt=&quot;previous video quality metrics comparison by title&quot; loading=&quot;lazy&quot; width=&quot;608&quot; height=&quot;630&quot; data-origin-width=&quot;1402&quot; data-origin-height=&quot;1452&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Previous video quality metrics comparison by title&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. VMAF란?&lt;/b&gt;&lt;/h2&gt;
&lt;p id=&quot;9d4f&quot; data-ke-size=&quot;size16&quot;&gt;Video Multimethod Assessment Fusion (VMAF)는 Netflix에서 개발한 video quality metric으로 ML을 이용합니다. &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;기존의 여러 개의 video quality metric들을 가중치의 합(weighted sum)을 하여 나온 score를 &lt;span style=&quot;color: #5733b1;&quot;&gt;VMAF&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;라고 합니다. 여기서 각각의 기존 video quality metric을 elementary metric이라고 명시합니다.&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;그럼 여기서 궁금증은 3개로 나뉠 것이고 그에 대한 답은 아래와 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;왜 weighted sum을 하는가?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존의 각각의 elementary metric은 각각 장단점을 가지고 있기 때문에 weighted sum을 통해 장점만 보존하여 점수를 도출하도록 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;어떻게 weigthed sum 을 하는가?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 elementary metric의 learninable parameter가 하나씩 곱해질 것이고 이는 Support Vector Machine (SVM) regressor으로부터 학습을 함&lt;/li&gt;
&lt;li&gt;SVM학습을 위한 dataset 구성을 위해 위의 언급해드린 dataset을 train, test dataset으로 나누고 ground truth(label)은 사람이 측정한 값인 DMOS로 함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;VMAF에 사용되는 기존의 elementary metric은 무엇인가?&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;VMAF(0.3.1)기준으로 총 3가지를 사용함 &amp;rarr; [Visual Infromation Fidelity(VIF), Detail Loss Metric (DLM), Motion]&lt;/li&gt;
&lt;li&gt;VIF: reference video에 존재하는 정보량과 distorted video의 정보량을 비교하여 품질을 평가 (두 비디오가 공유하는 엔트로피를 계산)&lt;/li&gt;
&lt;li&gt;DLM: video의 content visibility나 사람의 집중도를 방해하는 redundant impairment를 평가&lt;/li&gt;
&lt;li&gt;Motion: 인접하는 frame간의 temporal difference(시간상의 다름?)을 측정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 방식으로 VMAF를 구성하고 Section 2번과 같이 DMOS와 VMAF가 정비례를 가지는 지 확인하는 실험을 하였습니다. 기존의 PSNR-HVS와 비교했을 때 확연히 VMAF가 DMOS와의 정비례 관계를 가지는 것을 확인가능합니다. 그리고 이는 &lt;b&gt;VMAF score가 사람의 인지하는 화질과 매우 유사하고 잘 반영&lt;/b&gt;하고 있다고 말 할수 있습니다. 또한 video의 성격을 나누어 측정하였을 때에도 정비례 관계를 가집니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;1518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cv6IUp/btrGSxP8BdT/4dJKDWXY4t0bO9k7BV0hp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cv6IUp/btrGSxP8BdT/4dJKDWXY4t0bO9k7BV0hp0/img.png&quot; data-alt=&quot;VMAF performance results&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cv6IUp/btrGSxP8BdT/4dJKDWXY4t0bO9k7BV0hp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcv6IUp%2FbtrGSxP8BdT%2F4dJKDWXY4t0bO9k7BV0hp0%2Fimg.png&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; alt=&quot;VMAF performance results&quot; loading=&quot;lazy&quot; width=&quot;615&quot; height=&quot;651&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;1518&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;VMAF performance results&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Computer Science</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/57</guid>
      <comments>https://da2so.tistory.com/57#entry57comment</comments>
      <pubDate>Sat, 9 Jul 2022 17:32:26 +0900</pubDate>
    </item>
    <item>
      <title>[Torch2TFLite] Torch 모델 TFLite 변환 (feat. yolov5)</title>
      <link>https://da2so.tistory.com/56</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;AI 개발자라면 가장 많이 사용하는 framework는 Torch이나 TensorFlow일것입니다. 저는 Torch을 조금 더 주력으로 사용합니다. (대부분 연구자 분들도 Torch를 사용하시겠죠) 하지만 Torch 모델은 Arm cpu를 사용하는 device에서는 최적화되지않아 inference 속도가 다소 느립니다. 그래서 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Torch모델을 Arm cpu 연산에 최적화된 TFLite 모델로 변환시키는 방법&lt;/b&gt;&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;Torch모델을 TFLite로 변환시키는 과정은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Torch 모델&amp;nbsp;&amp;rarr; ONNX 모델&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ONNX 모델 &amp;rarr; OpenVINO 모델&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;OpenVINO모델 &amp;rarr; TFLite 모델&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Torch모델은 detection 모델 중 하나인 yolov5를 사용하겠습니다. 위의 과정을 거치면서 변환이 정상적으로 되었는 지 확인하기 위해서 Torch모델일때의 detection결과와 변환과정에서 생긴 모델들의 detection결과와 같은 지 확인하면서 진행하겠습니다.&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;실험에 사용한 환경은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Enviornment&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;torch: 1.10.1+cu111&lt;/li&gt;
&lt;li&gt;tensorflow: 2.4.1&lt;/li&gt;
&lt;li&gt;onnx: 1.8.0&lt;/li&gt;
&lt;li&gt;openvino-dev: 2022.1.0&lt;/li&gt;
&lt;li&gt;openvino2tensorflow: 1.31.3&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위의 사용된 환경꼭 맞춰주셔야 코드 정상실행됩니다. (특히 torch버전 맞춰주세요!)  &lt;/b&gt;사용된 모든 코드는 &lt;a href=&quot;https://github.com/da2so/yolov5_inference&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;여기서&lt;/a&gt; 사용가능합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Torch 모델 &amp;rarr; ONNX 모델&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Torch 모델을 OpenVINO 또는 TensorFlow(TFLite)모델로 변환하기 위해서는 &lt;a href=&quot;https://onnx.ai/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;ONNX(Open Neural Network Exchange)&lt;/a&gt;라는 중간자적 모델을 거치게 됩니다. 그래서 Torch 모델을 ONNX모델로 변환하기 위해서는 다음과 같은 코드를 사용합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1655780079345&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# yolov5_convert.py
model = attempt_load(save_path, device=device, inplace=True, fuse=True)  # load FP32 model
nc, names, stride = model.nc, model.names, model.stride

gs = int(max(model.stride))  # grid size (max stride)
imgsz = [check_img_size(x, gs) for x in imgsz]  # verify img_size are gs-multiples
im = torch.zeros(bs, 3, *imgsz).to(device)  # image size(1,3,640,640) BCHW iDetection

# torch2onnx.py
def torch2onnx(model, im, save_path, train, dynamic):
    try:
        logger.info(f'ONNX: starting export with onnx {onnx.__version__}...')
        torch.onnx.export(model, im, save_path, verbose=False, opset_version=12,
                            training=torch.onnx.TrainingMode.TRAINING if train else torch.onnx.TrainingMode.EVAL,
                            do_constant_folding=not train,
                            input_names=['images'],
                            output_names=['output'],
                            dynamic_axes={'images': {0: 'batch'},  # shape(1,3,640,640)
                                        'output': {
                                            0: 'batch',
                                            }  # shape(1,25200,85)
                                        }if dynamic else None)
		...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yolov5 공식 repo에서는 &lt;code&gt;attempt_load&lt;/code&gt; 함수로 model을 load한다는 것만 알아두시고 직접 모델을 load하고 싶으시면 자신만의 방법&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;(e.g. &lt;code&gt;torch.load&lt;/code&gt;)&lt;/span&gt;으로 model을 load하시면 됩니다. Torch모델을 ONNX모델로 변환하기 위해서 &lt;code&gt;torch.onnx.export&lt;/code&gt;함수를 사용하시면 됩니다. 중요하게 볼 parameter는 아래와 같습니다.&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;b&gt;model&lt;/b&gt;: Torch model&lt;/li&gt;
&lt;li&gt;&lt;b&gt;im&lt;/b&gt;: 모델의 input image shape을 결정하는 dummy tensor&lt;/li&gt;
&lt;li&gt;&lt;b&gt;do_cosntant_folding&lt;/b&gt;: True로 설정 할 경우 Conv-BN layers가 Conv하나로 folding(fusing)되어 graph 최적화함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;dynamic_axes&lt;/b&gt;: batch size에 대한 static한 설정을 할지 말지를 결정 (저는 dynamic변수에 False를 할당했습니다)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 함수를 통해 yolov5s Torch모델을 ONNX모델로 변환하는 CLI와 그에 대한 결과는 다음과 같습니다. &lt;span&gt;아래와 같이 ONNX 모델로 변환된것을 확인 가능하며 ONNX모델로 detection한 결과가 Torch모델로 detection한 결과와 같은 것을 확인가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v1VbJ/btrFGna4H1V/vsQXFedwtwkPijpjyVspUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v1VbJ/btrFGna4H1V/vsQXFedwtwkPijpjyVspUk/img.png&quot; data-alt=&quot;ONNX 변환 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v1VbJ/btrFGna4H1V/vsQXFedwtwkPijpjyVspUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv1VbJ%2FbtrFGna4H1V%2FvsQXFedwtwkPijpjyVspUk%2Fimg.png&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; alt=&quot;ONNX 변환 확인&quot; loading=&quot;lazy&quot; width=&quot;763&quot; height=&quot;431&quot; data-origin-width=&quot;1664&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ONNX 변환 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. ONNX 모델 &amp;rarr; OpenVINO 모델&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenVINO는 Intel에서 개발한 AI 모델 inference와 deploy에 특화된 toolkit입니다. 아래와 같이 OpenVINO형태로 변환된 AI모델은 Intel CPU 대상으로 최적화된 연산이 가능합니다. 또한 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;다른 framework의 모델로부터 변환이 자유롭게 &quot;잘&quot; 된다는 특징때문에 저는 ONNX 모델에서 OpenVINO를 거쳐 TensorFlow 모델로 변환&lt;/b&gt;&lt;/span&gt;을 할 것입니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YeSHe/btrFNGUyPLg/mEwgDR18eNmwHf9tUV4F81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YeSHe/btrFNGUyPLg/mEwgDR18eNmwHf9tUV4F81/img.png&quot; data-alt=&quot;출처:&amp;amp;amp;nbsp;https://www.intel.com/content/www/us/en/developer/tools/openvino-toolkit/overview.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YeSHe/btrFNGUyPLg/mEwgDR18eNmwHf9tUV4F81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYeSHe%2FbtrFNGUyPLg%2FmEwgDR18eNmwHf9tUV4F81%2Fimg.png&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; alt=&quot;OpenVINO 장점&quot; loading=&quot;lazy&quot; width=&quot;709&quot; height=&quot;433&quot; data-origin-width=&quot;2032&quot; data-origin-height=&quot;1240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처:&amp;amp;nbsp;https://www.intel.com/content/www/us/en/developer/tools/openvino-toolkit/overview.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ONNX모델에서 OpenVINO모델로 변환하는 주요 코드는 다음과 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656227694452&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def onnx2openvino(model, onnx_path, save_path, data_type):
    try:
        logger.info(f'OpenVINO: starting export with openvino {ie.__version__}...')
        f = str(save_path).replace('.pt', f'_openvino_model{os.sep}')

        cmd = f&quot;mo --input_model {onnx_path} --output_dir {f} --data_type {'FP16' if data_type=='fp16' else 'FP32'}&quot;
        subprocess.check_output(cmd.split())  # converting

	...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenVINO모델로 변환하기 위해서 CLI를 사용하게 됩니다. CLI는 python의 subprocess를 통해 실행하였고 &lt;code&gt;mo&lt;/code&gt;라는 명령어를 통해서 ONNX모델에서 OpenVINO모델로 변환하였습니다. (&lt;code&gt;mo&lt;/code&gt;는 openvino-dev &lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;PyPI가 설치되어 있어야 함!)&lt;/span&gt;&amp;nbsp;변환에 필요한 parameter는 다음과 같습니다.&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;b&gt;--input_model&lt;/b&gt;: ONNX model이 저장된 path&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--output_dir&lt;/b&gt;: OpenVINO모델이 저장될 directory
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;기본적으로 OpenVINO모델은 .xml파일과 .bin파일로 나누어 저장됨&lt;/li&gt;
&lt;li&gt;.xml 파일은 모델의 meta data를 저장한 파일, .bin파일은 모델의 weight값을 저장한 파일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--data_tpye&lt;/b&gt;: Converting시에 weight의 data type을 결정
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;저는 Float32을 사용하였음.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 함수를 통해 yolov5s ONNX모델을 OpenVINO모델로 변환하는 CLI와 그에 대한 결과는 다음과 같습니다. 아래와 같이 OpenVINO 모델이 잘 생성된것을 확인 가능하며 OpenVINO모델로 detection한 결과가 Torch모델로 detection한 결과와 같은 것을 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;950&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvsZmP/btrFMDKyqZh/TxYtngtF7YgK3OPeKqZcsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvsZmP/btrFMDKyqZh/TxYtngtF7YgK3OPeKqZcsk/img.png&quot; data-alt=&quot;OpenVINO 변환 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvsZmP/btrFMDKyqZh/TxYtngtF7YgK3OPeKqZcsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvsZmP%2FbtrFMDKyqZh%2FTxYtngtF7YgK3OPeKqZcsk%2Fimg.png&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; alt=&quot;OpenVINO 변환 확인&quot; loading=&quot;lazy&quot; width=&quot;779&quot; height=&quot;434&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;950&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OpenVINO 변환 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. OpenVINO 모델 &amp;rarr; TFLite 모델&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 우리가 원하는 TFLite로 모델로 변환하는 과정만 남았습니다. OpenVINO모델을 TFLite모델로 변환하기위해 PINTO님의 &lt;a href=&quot;https://github.com/PINTO0309/openvino2tensorflow&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;openvino2tensorflow&lt;/a&gt; PyPI를 사용할 것입니다. 변화 코드는 다음과 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1656230165648&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def openvino2tflite(model_name, openvino_path, save_dir):
    try:
        logger.info(f'TFLite: starting starting export with TF {tf.__version__}...')
        if not Path(openvino_path).is_file():
            openvino_path = next(Path(openvino_path).glob('*.xml'))  # get *.xml file from *_openvino_model dir

        saved_model_path = str(save_dir / 'model_float32.tflite')
        cmd = f'openvino2tensorflow --model_path {openvino_path} --model_output_path {save_dir}\
                --output_no_quant_float32_tflite  --weight_replacement_config ./data/convert/replace_{model_name}.json'
        
        subprocess.check_output(cmd.split())        
    ...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에도 CLI를 통해서 변환을 합니다. openvino2tensorflow 명령어를 사용하며 주요 parameter는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;--model_path&lt;/b&gt;: OpenVINO 모델(.xml, .bin)이 저장된 directory&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--model_output_path&lt;/b&gt;: TFLite모델이 저장될 directory&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--output_no_quant_float32_tflite&lt;/b&gt;: Float32 data type을 TFLite모델로 변환함을 명시&lt;/li&gt;
&lt;li&gt;&lt;b&gt;--weight_replacement_config&lt;/b&gt;: OpenVINO 모델에서 TFLite변환시 수정해야하는 configuration
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;주로 Transpose나 Reshape연산을 할 때 차원 axis가 안맞을 경우 수동으로 convert시에 axis값을 수정해줘야함&lt;/li&gt;
&lt;li&gt;이는 OpenVINO와 TensorFlow간의 모델선언의 차이로 생기는 문제로 수동으로 수정해서 해결해줘야함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;--weight_replacement_config&lt;/code&gt;가 이해가 잘 안되실 테니 좀 더 설명드립니다. TFLite 모델로 정상적으로 변환하기 위해서는&amp;nbsp; transpose연산의 dimension axis를 기존 [0,1,3,4,2]에서 [0,3,1,2,4]로 바꿔줘야 합니다. 그렇게 하기위해서 &lt;code&gt;--weight_replacement_config&lt;/code&gt;의 parameter값으로&amp;nbsp; &lt;code&gt;./data/convert/replace_yolov5s.json&lt;/code&gt;을 설정하는데 해당 파일안의 내용은 아래와 같이 변경하고 싶은 layer_id를 선택하고 &quot;values&quot;의 key값으로 변경하려 하는 dimension axis를 설정해주면 됩니다. (아래는 하나의 transpose operation에 대한 dimension axis를 변경하는 내용을 설명드린것이고 전체 파일 내용은 제 github의 &lt;code&gt;./data/convert/replace_yolov5s.json&lt;/code&gt;을 참고해주세요.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blog_openvino.png&quot; data-origin-width=&quot;1639&quot; data-origin-height=&quot;1170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rWoLZ/btrFKHUjyYu/Zoi6otKomtXoJaDukswlJk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rWoLZ/btrFKHUjyYu/Zoi6otKomtXoJaDukswlJk/img.png&quot; data-alt=&quot;--weight_replacement_config 인자 설명&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rWoLZ/btrFKHUjyYu/Zoi6otKomtXoJaDukswlJk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrWoLZ%2FbtrFKHUjyYu%2FZoi6otKomtXoJaDukswlJk%2Fimg.png&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; alt=&quot;replace.json 설명&quot; loading=&quot;lazy&quot; width=&quot;772&quot; height=&quot;551&quot; data-filename=&quot;blog_openvino.png&quot; data-origin-width=&quot;1639&quot; data-origin-height=&quot;1170&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;--weight_replacement_config 인자 설명&lt;/figcaption&gt;
&lt;/figure&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;위의 openvino2tflite 함수를 통해 yolov5s OpenVINO모델을 TFLite모델로 변환하는 CLI와 그에 대한 결과는 다음과 같습니다. 아래와 같이 TFLite 모델이 잘 생성된것을 확인 가능하며 TFLite모델로 detection한 결과가 Torch모델로 detection한 결과와 같은 것을 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;TFLite_blog.png&quot; data-origin-width=&quot;1737&quot; data-origin-height=&quot;956&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bb0HOd/btrFLB7oYQf/7ae5FcLhKVe9lOyCNr4oY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bb0HOd/btrFLB7oYQf/7ae5FcLhKVe9lOyCNr4oY0/img.png&quot; data-alt=&quot;TFLite 변환 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bb0HOd/btrFLB7oYQf/7ae5FcLhKVe9lOyCNr4oY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbb0HOd%2FbtrFLB7oYQf%2F7ae5FcLhKVe9lOyCNr4oY0%2Fimg.png&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; alt=&quot;TFLite 변환 확인&quot; loading=&quot;lazy&quot; width=&quot;776&quot; height=&quot;427&quot; data-filename=&quot;TFLite_blog.png&quot; data-origin-width=&quot;1737&quot; data-origin-height=&quot;956&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;TFLite 변환 확인&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 각 모델 별 Inference 속도 비교&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;속도 비교에 사용한 desktop의 spec은 다음과 같습니다. inference 속도를 측정하는데 사용되는 이미지는 2장(위의 bus.jpg와 zidane.jpg)입니다. &lt;b&gt;Inference속도는 CPU, Float32 data type기준으로 출력하였습니다.&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Device&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU: Intel(R) Xeon(R) Silver 4210R CPU @ 2.40GHz (40core)&lt;/li&gt;
&lt;li&gt;GPU: GeForce RTX 3080 (4개)&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;inference.png&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;808&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P680s/btrFHIzUrlJ/WUTH8qkhGT4A9tCxXIgII0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P680s/btrFHIzUrlJ/WUTH8qkhGT4A9tCxXIgII0/img.png&quot; data-alt=&quot;inference 속도 비교&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P680s/btrFHIzUrlJ/WUTH8qkhGT4A9tCxXIgII0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP680s%2FbtrFHIzUrlJ%2FWUTH8qkhGT4A9tCxXIgII0%2Fimg.png&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; alt=&quot;inference 속도 비교&quot; loading=&quot;lazy&quot; width=&quot;812&quot; height=&quot;409&quot; data-filename=&quot;inference.png&quot; data-origin-width=&quot;1604&quot; data-origin-height=&quot;808&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;inference 속도 비교&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림의 inference 속도에 대한 평균을 내어 정리한 테이블은 아래와 같습니다. 확실히 inference에 사용한 device의 cpu가 intel이므로 OpenVINO모델에서 가장 빠른것을 알 수 있고 arm cpu에 최적화된 TFLite 모델은 inference가 느린 것을 확인가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 44.186%; height: 162px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style15&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.627%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Model type&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 26.373%; height: 17px; text-align: center;&quot;&gt;&lt;b&gt;Inference speed (s)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.627%; height: 17px; text-align: center;&quot;&gt;Torch&lt;/td&gt;
&lt;td style=&quot;width: 26.373%; height: 17px; text-align: center;&quot;&gt;0.088s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.627%; height: 17px; text-align: center;&quot;&gt;ONNX&lt;/td&gt;
&lt;td style=&quot;width: 26.373%; height: 17px; text-align: center;&quot;&gt;0.077s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.627%; height: 17px; text-align: center;&quot;&gt;OpenVINO&lt;/td&gt;
&lt;td style=&quot;width: 26.373%; height: 17px; text-align: center;&quot;&gt;0.042s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 23.627%; height: 17px; text-align: center;&quot;&gt;TFLite&lt;/td&gt;
&lt;td style=&quot;width: 26.373%; height: 17px; text-align: center;&quot;&gt;0.360s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후에는 arm cpu에 대해서도 inference비교할 것이며 quantization사용 시에 따른 속도 비교또한 진행하도록 하겠습니다. 감사합니당~~&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/PyTorch</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/56</guid>
      <comments>https://da2so.tistory.com/56#entry56comment</comments>
      <pubDate>Sun, 26 Jun 2022 18:20:57 +0900</pubDate>
    </item>
    <item>
      <title>[MobileOne] An Improved One millisecond Mobile Backbone 논문 리뷰</title>
      <link>https://da2so.tistory.com/55</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 6월 Apple에서 mobile friendly한 모델을 제안하는 논문인&amp;nbsp;&lt;a href=&quot;https://arxiv.org/pdf/2206.04040.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;An&amp;nbsp;Improved&amp;nbsp;One&amp;nbsp;millisecond&amp;nbsp;Mobile&amp;nbsp;Backbone&lt;/a&gt; 을 리뷰해보도록 하겠습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Introduction&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자들은 실제 mobile에서 latency(inference speed)를 최적화시키는 것을 목표로 하고 있으며 기존의 논문들이 제안했던 mobile-friendly하다는 모델들에 대해 아래와 같은 단점을 지적하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;FLOPs와 parameter수가 (상대적으로) 작다고 하여 실제 latency가 빠르지 않음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;skip-connection과 branching은 많은 memory access cost를 발생시키므로 latency가 최적화되지 않음&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;736&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dS4qvu/btrFyLBtemc/VmhUoJbnpnEsc4hSVUC200/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dS4qvu/btrFyLBtemc/VmhUoJbnpnEsc4hSVUC200/img.png&quot; data-alt=&quot;skip connection &amp;amp;amp; branching examples&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dS4qvu/btrFyLBtemc/VmhUoJbnpnEsc4hSVUC200/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdS4qvu%2FbtrFyLBtemc%2FVmhUoJbnpnEsc4hSVUC200%2Fimg.png&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; alt=&quot;skip connection &amp;amp; branching examples&quot; loading=&quot;lazy&quot; width=&quot;357&quot; height=&quot;205&quot; data-origin-width=&quot;1282&quot; data-origin-height=&quot;736&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;skip connection &amp;amp; branching examples&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 저자들은 &lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;(1)&lt;/span&gt; &lt;/b&gt;&lt;/span&gt;&lt;i&gt;mobile device에서 latency의 bottleneck 요인을 분석하고 이를 바탕으로&lt;/i&gt; &lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;(2)&lt;/b&gt;&lt;/span&gt; &lt;i&gt;mobile device에서 inference 속도를 최적화&lt;/i&gt;한 모델인 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;MobileOne&lt;/b&gt;&lt;/span&gt;을 제안하게 됩니다. MobileOne은 아래와 같이 SOTA accuracy와 latency performance를 도달하였으며 전에 제가 리뷰한 MobileViT-S보다 5배 빠르다고 하네요! latency cost는 CoreML을 이용하여 IPhone12 device에서 측정하였다고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;1204&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvQRbe/btrFya9qzKs/Te5RnudVGVQzdgyDPctX9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvQRbe/btrFya9qzKs/Te5RnudVGVQzdgyDPctX9k/img.png&quot; data-alt=&quot;Comparisons of Top-1 accuracy on image classification vs latency on an iPhone 12&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvQRbe/btrFya9qzKs/Te5RnudVGVQzdgyDPctX9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvQRbe%2FbtrFya9qzKs%2FTe5RnudVGVQzdgyDPctX9k%2Fimg.png&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; alt=&quot;Comparisons of Top-1 accuracy on image classification vs latency on an iPhone 12&quot; loading=&quot;lazy&quot; width=&quot;687&quot; height=&quot;437&quot; data-origin-width=&quot;1894&quot; data-origin-height=&quot;1204&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Comparisons of Top-1 accuracy on image classification vs latency on an iPhone 12&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Method&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Metric Correlations&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;모델의 efficiency를 측정하는 (기존의) metric인 parameter수와 FLOPs가 실제 latency와 correlation이 있는 지 확인하였습니다. &lt;/b&gt;저자들은 PyTorch 모델들을 ONNX 형태로 1차 변환하였고 ONNX 모델을 CoreML tool을 이용해 coreml package형태로 2차 변환하여 IPhone12에서 latency를 측정하였습니다.&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;실제로  실험해보니 아래와 같이 대다수의 모델이 parameter수가 높은 반면에 latency는 낮게 측정되었습니다. FLOPs와 latency관계에서도 비슷한 경향을 보인다는 것도 확인하였습니다. 또한 MobileNet이 Transformer계열 모델들과 FLOPs와 parameter 수는 비슷하지만 latency가 낮은 것을 확인하였다고 합니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;842&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqI7nd/btrFvVLW47e/MJegqXeW2aKdXI0b1VILJ0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqI7nd/btrFvVLW47e/MJegqXeW2aKdXI0b1VILJ0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqI7nd/btrFvVLW47e/MJegqXeW2aKdXI0b1VILJ0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqI7nd%2FbtrFvVLW47e%2FMJegqXeW2aKdXI0b1VILJ0%2Fimg.png&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; alt=&quot;correlations of latency and FLOPs(or parameter number)&quot; loading=&quot;lazy&quot; width=&quot;789&quot; height=&quot;357&quot; data-origin-width=&quot;1860&quot; data-origin-height=&quot;842&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 아래와 같이 Spearman rank correlation측정시에도 latency와 FLOPs는 어느 정도만의 correlation이 존재하고 parameter와 latency는 correlation이 적다는 것을 확인하였습니다. 뿐만 아니라 desktop CPU에서 측정하였을 때도 correlation이 낮게 나오는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtz5wk/btrFwt9vZPu/ks8HuwZLvC4KcSHDwsUuEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtz5wk/btrFwt9vZPu/ks8HuwZLvC4KcSHDwsUuEK/img.png&quot; data-alt=&quot; Spearman rank correlation coefficient between latency-flops&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtz5wk/btrFwt9vZPu/ks8HuwZLvC4KcSHDwsUuEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdtz5wk%2FbtrFwt9vZPu%2Fks8HuwZLvC4KcSHDwsUuEK%2Fimg.png&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; alt=&quot;Spearman rank correlation coefficient between latency-flops&quot; loading=&quot;lazy&quot; width=&quot;449&quot; height=&quot;126&quot; data-origin-width=&quot;1042&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt; Spearman rank correlation coefficient between latency-flops&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 실험들을 통해 FLOPs(parameter수)와 latency는 correlation이 적다는 것을 확인하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Key Bottlenecks&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 섹션에서는 latency에 악영향을 미치는 network architecture 및 operation에 대해 분석합니다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.2.1 Activation Fucnctions&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;30 layers구조의 CNN을 구성하였고 해당 CNN에서 activation function에 따른 latency 측정 실험을 하였습니다. 최근에  efficient하다고 제안된 SE-ReLU, Dynamic Shift-Max, DynamicReLUs들은 모두 hardware acceleration에 최적화되지 않아 latency가 높게 측정되었습니다. 그에 비해 ReLU와 GELU는 latency가 낮게 측정됨을  확인하였고 그래서 저자들은 &lt;b&gt;&lt;i&gt;MobileOne에 ReLU activation만 사용&lt;/i&gt;&lt;/b&gt;한다고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DDyHc/btrFuwlox6V/F2i7SzUMXtLfF3CwebyM00/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DDyHc/btrFuwlox6V/F2i7SzUMXtLfF3CwebyM00/img.png&quot; data-alt=&quot;Comparison of latency on mobile device of different activation functions&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DDyHc/btrFuwlox6V/F2i7SzUMXtLfF3CwebyM00/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDDyHc%2FbtrFuwlox6V%2FF2i7SzUMXtLfF3CwebyM00%2Fimg.png&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; alt=&quot;Comparison of latency on mobile
device of different activation functions&quot; loading=&quot;lazy&quot; width=&quot;337&quot; height=&quot;197&quot; data-origin-width=&quot;694&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Comparison of latency on mobile device of different activation functions&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.2.2 Architectural Blocks&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Runtime performance에  큰 (악) 영향을 끼치는 요인은 memory access와 degree of parallelism입니다. memory access는 multi-branch network 구조인 경우에 크게 상승한다고 말합니다. 이유는 각 branch로부터 activation들이 다음 tensor를 연산하기 위해서 저장해야하기 때문입니다. 또한 Squeeze-Excitation(SE) block에서 사용되는 global pooling operation은 synchronization cost가 크기 때문에 runtime performance에 악영향을 줍니다.&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;그래서 위의 내용을 정량적으로 확인하기 위해 30 layers구조의 기본적인 CNN에서 skip-connection과 SE block을 추가했을 때 inference time을 측정하였습니다. 아래와 같이 skip-connection과 SE block이 추가되었을 때 latency가 높아지는 것을 확인하였으며 이를 바탕으로 &lt;i&gt;&lt;b&gt;MobileOne에서는 skip-connection을 완전히 제거하였고 SE block사용은 최대한 제한하였습니다. (SE block이 accuracy 향상에 좋다고 하여 쓴다고는 하네요)&lt;/b&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceNeSd/btrFzxQSbiT/tF4SYhFhiRiTNYxbO0OOK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceNeSd/btrFzxQSbiT/tF4SYhFhiRiTNYxbO0OOK1/img.png&quot; data-alt=&quot;Ablation on latency of different architectural blocks&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceNeSd/btrFzxQSbiT/tF4SYhFhiRiTNYxbO0OOK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceNeSd%2FbtrFzxQSbiT%2FtF4SYhFhiRiTNYxbO0OOK1%2Fimg.png&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; alt=&quot;Ablation on latency of
different architectural blocks&quot; loading=&quot;lazy&quot; width=&quot;361&quot; height=&quot;128&quot; data-origin-width=&quot;806&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ablation on latency of different architectural blocks&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 MobileOne Architecture&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MobileOne의 구조는 위의 분석을 통해 디자인되기도 하였지만 또 다른 중요한 구조는 re-parameterization이 가능한 형태를 사용하여 training-time과 inference-time구조가 다르다는 점입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3.1 MobileOne Block&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 그림에서 보이듯이 RepVGG논문에서는 training시에는 multi-branch구조로 있다가 inference-time에는 여러 branch의 layer(또는 operation)을 하나의 conv layer로 reparameterization하게 됩니다. 이렇게 reparameterization하게 되면 하나의 con layer로 연산 가능하기 때문에 inference time에는 memory access을 많이 하는 multi-branch구조를 제거시킵니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;950&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5Ow7n/btrFxj60KrS/i7pPo6HrRy7HIuRe8pVJkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5Ow7n/btrFxj60KrS/i7pPo6HrRy7HIuRe8pVJkk/img.png&quot; data-alt=&quot;Structural re-parameterization of a RepVGG block&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5Ow7n/btrFxj60KrS/i7pPo6HrRy7HIuRe8pVJkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5Ow7n%2FbtrFxj60KrS%2Fi7pPo6HrRy7HIuRe8pVJkk%2Fimg.png&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; alt=&quot;Structural re-parameterization of a RepVGG
block&quot; loading=&quot;lazy&quot; width=&quot;480&quot; height=&quot;457&quot; data-origin-width=&quot;998&quot; data-origin-height=&quot;950&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Structural re-parameterization of a RepVGG block&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RepVGG에서는 reparameterization을 Standard Conv layer에 적용하였는데요. &lt;span&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;MobilONE은&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt; RepVGG와 다르게 MobileNet-V1 의 block인 3x3 depthwise conv와 1x1 pointwise conv로 block을 구성&lt;/span&gt;&lt;/b&gt;하였습니다. 해당 block으로 RepVGG처럼 reparameterizable한 skip-connection추가 된 형태로 training하게 되고 testing때는 reparameterization하게됩니다.&lt;/span&gt;&lt;/span&gt;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;932&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWXOIU/btrFAjNnUjG/HUjNrjFk4WV76cCCxMuUn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWXOIU/btrFAjNnUjG/HUjNrjFk4WV76cCCxMuUn1/img.png&quot; data-alt=&quot;MobileOne block has two different structures at train time and test time&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWXOIU/btrFAjNnUjG/HUjNrjFk4WV76cCCxMuUn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWXOIU%2FbtrFAjNnUjG%2FHUjNrjFk4WV76cCCxMuUn1%2Fimg.png&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; alt=&quot;MobileOne block has two different
structures at train time and test time&quot; loading=&quot;lazy&quot; width=&quot;429&quot; height=&quot;476&quot; data-origin-width=&quot;840&quot; data-origin-height=&quot;932&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MobileOne block has two different structures at train time and test time&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그리고 저자들은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;over-parameterization branch를 제안&lt;/b&gt;&lt;/span&gt;하였는데 위 그림과 같이 over-parameterization factor \(k\) 추가하여 해당 (depthwise or pointwise) conv-BN을 연속해서 \(k\)개 쌓도록 하였습니다.&amp;nbsp; Conv-BN이 여러 개 쌓인 구조는 사이에 비선형 함수인 activation이 없기 때문에 결국 linearlity성격을 가지므로 하나의 conv로 치환(fusing) 가능하기 때문에 저자들은 이와 같이 over-parameterization방법을 제안하게 되었습니다. \(k\)는 hyperparameter로 1~5 사이의 값을 가집니다.&amp;nbsp;&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;Conv-BN이 어떻게 folding(fusing) 될 수 있는지 수학적으로 알아봅니다.&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;\( W' \in \mathcal{R}^{C_{out} \times C_{in} \times K \times K}, b' \in \mathcal{R}^D \): Weight and bias
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\(C_{out}\) ,\(C_{in}\): Output and input channel dimension&lt;/li&gt;
&lt;li&gt;\(K\): Kernel size&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Conv-BN layer는 linear operations으로만 구성되어 있기 때문에 위의 Conv layer의 weight \(W'\)가 아래와 같이 하나의 Conv weight로 folding가능합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\( \widehat{W} = W' * \frac{\gamma}{\sigma}, \widehat{b} = ( b' - \mu ) * \frac{\gamma}{\sigma} + \beta \)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국,&lt;b&gt; inference시에 모든 branch안의 존재하는 batch norm layer는 이전 Conv layer에 folding됩니다.&lt;/b&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;또한 skip connection(multi-branch)은 RepVGG의 reparameterization방법을 통해 folding된다. 즉, 1x1 conv은 \(K-1\) zero padding이 추가되어 \(K \times K\) Conv과 똑같은 shape으로 만들어 여러 skip connection(multi-branch)는 하나의 Conv layer weight로 합(summation)쳐진다. 수식으로 표현하면 \(W = \sum^M_i \widehat{W}_i, b= \sum^M_i \widehat{b}_i \) 와 같고 \(M\)은 branch의 수를 의미합니다.&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;저자들이 주장하는 Reparameterization과 over-parameterization의 효과를 주장하기 위해 아래와 같은 실험을 진행하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;910&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XyRlQ/btrFGgwvkSW/KtTxHDXD4OeY9AzTRkWO60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XyRlQ/btrFGgwvkSW/KtTxHDXD4OeY9AzTRkWO60/img.png&quot; data-alt=&quot;Effects for&amp;amp;amp;nbsp; Reparameterization &amp;amp;amp; over-parameterization&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XyRlQ/btrFGgwvkSW/KtTxHDXD4OeY9AzTRkWO60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXyRlQ%2FbtrFGgwvkSW%2FKtTxHDXD4OeY9AzTRkWO60%2Fimg.png&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; alt=&quot;Effects for Reparameterization &amp;amp; over-parameterization&quot; loading=&quot;lazy&quot; width=&quot;704&quot; height=&quot;347&quot; data-origin-width=&quot;1848&quot; data-origin-height=&quot;910&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Effects for&amp;amp;nbsp; Reparameterization &amp;amp; over-parameterization&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Table 6&lt;/b&gt;을 통해 over-parameterization factor \(k\)에 변화에 따른 성능 비교를 하였을 때 MobileOne의 scale이 큰 경우(MobileOne-S1) \(k\)이 커짐에 따라 큰 성능 향상 효과를 보지 못했지만 MobileOne의 scale이 작은 경우에는(MobileOne-S1) \(k\)가 4일때 0.5%의 성능향상 효과를 보았습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Table 7&lt;/b&gt;을 통해서는 reparameterizable한 branch를 추가하였을 때 model의 scale variant에 상관없이 성능이 더 높게 나왔다는 결과를 보여주고 &lt;b&gt;Figure 4&lt;/b&gt;에서는 multi-branch를 사용하고 k가 4일 경우&amp;nbsp; train, validation loss가 가장 낮게 학습된다는 실험 결과를 보여주었다. 위의 3개의 실험결과를 통해 저자들은 주장한 방법론에 대한 성능 검증을 하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3.2 Modle Scaling&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자들은 width scale에 따라 5개의 MobileOne 버전을 아래와 같이 제안하였다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1836&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zwCea/btrFHJxJn7I/ThLN3x28ikILRQLyKv2Cz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zwCea/btrFHJxJn7I/ThLN3x28ikILRQLyKv2Cz1/img.png&quot; data-alt=&quot;MoblieOne network specifications&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zwCea/btrFHJxJn7I/ThLN3x28ikILRQLyKv2Cz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzwCea%2FbtrFHJxJn7I%2FThLN3x28ikILRQLyKv2Cz1%2Fimg.png&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; alt=&quot;MoblieOne network specifications&quot; loading=&quot;lazy&quot; width=&quot;805&quot; height=&quot;191&quot; data-origin-width=&quot;1836&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MoblieOne network specifications&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.4 Training&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 모델일수록 regularization이 크면 underfitting되기 때문에 regularization을 약하게 주어야 합니다. 그래서 저자들은 regularization term인 weigt decay coefficient의 값을 annealing하게 됩니다. (weight decay을 아예 없애는 것이 아닌!) 저자들은 learning rate에 cosine annealing scheduler를 사용할 뿐만 아니라 weight decay에 똑같은 scheduling을 적용하였다고 합니다. 추가적으로 (Exponential Moving Average)EMA와 progressive learning curriculum 방법을 추가하여 성능을 아래와 같이 높였다고 합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;EMA&lt;/b&gt;: training parameter들이 moving average를 유지시키는 방법으로 원래 잘 학습하던 방향에서 크게 벗어나지 않도록 함&lt;br /&gt;&lt;b&gt;Progressive learning&lt;/b&gt;: epoch이 증가할수록 input image size가 커지며 augmentation강도가 커지게 하여 점점 어려운 이미지의 특성을 학습하도록 하는 방법론&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cnKyn8/btrFHb8EDFM/gw5pRXpdKlidY1zU95wDeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cnKyn8/btrFHb8EDFM/gw5pRXpdKlidY1zU95wDeK/img.png&quot; data-alt=&quot;Ablation on various train settings for MobileOne-S2 showing Top-1 accuracy on Imagenet&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cnKyn8/btrFHb8EDFM/gw5pRXpdKlidY1zU95wDeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcnKyn8%2FbtrFHb8EDFM%2Fgw5pRXpdKlidY1zU95wDeK%2Fimg.png&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; alt=&quot;Ablation on various train settings for MobileOne-S2 showing Top-1 accuracy on Imagenet&quot; loading=&quot;lazy&quot; width=&quot;563&quot; height=&quot;74&quot; data-origin-width=&quot;1292&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Ablation on various train settings for MobileOne-S2 showing Top-1 accuracy on Imagenet&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.5 Benchmarking&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iPhone12으로 latency를 측정하는 데 있어서 모델 그 자체만 실행되는 시간을 측정하는 것은 불가능하다고 합니다. 그래도 최대한 모델의 latency를 정확히 측정하기 위해 아래와 같은 셋팅으로 실험 진행하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Benchmarking하기 전에 model graph을 load하고 input tensor를 preallocate하였으며 model warmup을 한번 진행함&lt;/li&gt;
&lt;li&gt;Benchmarking시에는 모델을 1000번 run하여 lowest, highest latency를 측정함&lt;/li&gt;
&lt;li&gt;Benchmarking시에는 다른 모든 application은 종료하였음&lt;/li&gt;
&lt;li&gt;모든 모델의 latency 측정 결과는 lowest latency임
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;lowest latency가 다른 process의 interrupt를 받지 않았다고 가정하기 때문에 해당 결과로 표를 작성함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 Desktop CPU latency도 측정하였는데 이는 Ubuntu desktop(2.3 GHz Intel Xeon Gold 5118 processor)에서 결과를 도출하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Experiments&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Image Classification on ImageNet-1K&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실험셋팅은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;300 epochs&lt;/li&gt;
&lt;li&gt;256 batch sizes&lt;/li&gt;
&lt;li&gt;SGD optimizer with momentum&lt;/li&gt;
&lt;li&gt;Cross entropy loss with label smoothing (0.1 smoothing factor)&lt;/li&gt;
&lt;li&gt;Initial learning rate 0.1 with cosine annealing scheduler&lt;/li&gt;
&lt;li&gt;Initial weight decay 1e-4 and annealed to 1e-5 with cosine annealing scheduler&lt;/li&gt;
&lt;li&gt;AutoAugmentation for MobileOne-S2, 3, 4&lt;/li&gt;
&lt;li&gt;Standard augmentation for MobileOne-S0, 1&lt;/li&gt;
&lt;li&gt;EMA weight averaging with decay constant of 0.9995&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MobileOne의 성능 비교 및 결과는 아래와 같습니다. CNN기반의 모델과 Transformer계열 네트워크 모두와 비교하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;1454&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhBK8U/btrFHFhoieY/3KqFXmnin2GB76PdoKGK3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhBK8U/btrFHFhoieY/3KqFXmnin2GB76PdoKGK3k/img.png&quot; data-alt=&quot;Performance of various models on ImageNet-1k validation set&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhBK8U/btrFHFhoieY/3KqFXmnin2GB76PdoKGK3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhBK8U%2FbtrFHFhoieY%2F3KqFXmnin2GB76PdoKGK3k%2Fimg.png&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; alt=&quot;Performance of various models on ImageNet-1k validation set&quot; loading=&quot;lazy&quot; width=&quot;565&quot; height=&quot;620&quot; data-origin-width=&quot;1326&quot; data-origin-height=&quot;1454&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Performance of various models on ImageNet-1k validation set&lt;/figcaption&gt;
&lt;/figure&gt;
&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDtR2z/btrFHKpVD5V/RbJY0GpHqyf321WBzezacK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDtR2z/btrFHKpVD5V/RbJY0GpHqyf321WBzezacK/img.png&quot; data-alt=&quot;Comparison of Top-1 Accuracy on ImageNe&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDtR2z/btrFHKpVD5V/RbJY0GpHqyf321WBzezacK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDtR2z%2FbtrFHKpVD5V%2FRbJY0GpHqyf321WBzezacK%2Fimg.png&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; alt=&quot;Comparison of Top-1 Accuracy
on ImageNe&quot; loading=&quot;lazy&quot; width=&quot;427&quot; height=&quot;290&quot; data-origin-width=&quot;728&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Comparison of Top-1 Accuracy on ImageNe&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Object detection on MS-COCO&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSDLite의 backbone을 MobileOne으로 변경하여 성능 비교를 하였습니다. Input resolution은 320x320이며 200 epochs 학습하였다고 합니다. (자세한 training configuration은 논문 참고 바랍니다!) 성능 측정은 MS COCO validation set을 사용하였으며 mAP@IoU 0.50:0.05:0.95 기준으로 하였습니다. 아래  backbone에 따른 성능 비교 테이블을 확인하였을 때 MobileOne의 퍼포먼스가 가장 뛰어남을 확인 가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;508&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cz6fQd/btrFHbOqfEt/ZC3TddYaeUK6OEK5WZIUHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cz6fQd/btrFHbOqfEt/ZC3TddYaeUK6OEK5WZIUHK/img.png&quot; data-alt=&quot;Quantitative performance of object detection on MS-COCO&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cz6fQd/btrFHbOqfEt/ZC3TddYaeUK6OEK5WZIUHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcz6fQd%2FbtrFHbOqfEt%2FZC3TddYaeUK6OEK5WZIUHK%2Fimg.png&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; alt=&quot;Quantitative performance of object detection on MS-COCO&quot; loading=&quot;lazy&quot; width=&quot;254&quot; height=&quot;292&quot; data-origin-width=&quot;442&quot; data-origin-height=&quot;508&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Quantitative performance of object detection on MS-COCO&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 Semantic Segmentation on Pascal VOC and ADE 20k&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deeplab V3의 backbone을 MobileOne으로 변경하여 semantic segmentation성능 비교를 하였습니다. VOC와 ADE 20k dataset에 대해 모두 50 epochs 학습하였으며 자세한 training configuration은 논문 참고 바랍니다) 성능 측정은 mean intersection-over-union (mIOU)기준으로 하였으며 아래와 같이 MobileOne이 좋은 성능을 내었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;526&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfNt1w/btrFHdlbUfc/3Z3hnpk76OyYXHBIgAxjv1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfNt1w/btrFHdlbUfc/3Z3hnpk76OyYXHBIgAxjv1/img.png&quot; data-alt=&quot;Quantitative performance of semantic segmentation on Pascal-VOC and ADE20k datasets&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfNt1w/btrFHdlbUfc/3Z3hnpk76OyYXHBIgAxjv1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfNt1w%2FbtrFHdlbUfc%2F3Z3hnpk76OyYXHBIgAxjv1%2Fimg.png&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; alt=&quot;Quantitative performance
of semantic segmentation on Pascal-VOC and ADE20k datasets&quot; loading=&quot;lazy&quot; width=&quot;333&quot; height=&quot;312&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;526&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Quantitative performance of semantic segmentation on Pascal-VOC and ADE20k datasets&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI paper review/Mobile-friendly</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/55</guid>
      <comments>https://da2so.tistory.com/55#entry55comment</comments>
      <pubDate>Sat, 25 Jun 2022 17:03:30 +0900</pubDate>
    </item>
    <item>
      <title>라인플러스 AI개발자 면접 및 이직 후기</title>
      <link>https://da2so.tistory.com/54</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #555555;&quot;&gt;1. 서론..&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;저는 고려대에서 석사를 마치고 스타트업 노타에서 &lt;b&gt;전문 연구요원&lt;/b&gt; 복무를 시작하게 되었습니다. (석사기간에는 Explainable AI주제로 논문을 썼었고 노타에서는 On-device 경량화 플랫폼 서비스 개발을 하였습니다.) 그러다가 시간이 지난 후... 여러 가지 이유로 인해 이직을 결심하게 되었고&amp;nbsp; &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;LINE PLUS의 AI 개발자&lt;/b&gt;&lt;/span&gt;로 이직을 하게 되었습니다. 그래서 LINE PLUS의 면접 프로세스와&lt;span style=&quot;color: #555555;&quot;&gt; 이직에 대한 후기를 적어 보려고 합니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;i&gt;(아래 내용에서 문제가 될 만한 내용이 있다면 댓글로 알려주세요!)&lt;/i&gt;&lt;/span&gt;&lt;/span&gt;&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;&lt;span style=&quot;color: #555555;&quot;&gt;&lt;span style=&quot;color: #555555;&quot;&gt;저는 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;서류전형 &amp;gt; 온라인 코딩 테스트 &amp;gt; Pre-test+1차면접 &amp;gt; 2차 면접 &amp;gt; 레퍼 체크 및 처우 협의 &amp;gt; 최종 합격의&lt;/b&gt; &lt;/span&gt; 프로세스를 거쳤습니다. AI개발자 또는 전문 연구요원으로 이직을 준비하시는 분들께 많은 도움이 되었으면 하고 다른 분들도 참고하시면 좋을 것 같습니다! &lt;span style=&quot;color: #555555;&quot;&gt; &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. 서류전형&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서류전형은 당연하게도 경력사항에 제가 속해있던 팀에서 어떤 일을 했는지 적었고 포트폴리오와 운영하는 블로그의 url을 첨부하였습니다.&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;&quot;대표적인 프로젝트 세 가지를 적어 주시돼, 각각의 프로젝트에서 어떤 기술을 활용하여 어떤 방식으로 개발했는지 구체적으로 설명해 주세요.&quot;라는 직무 질문 있었습니다. 여기에 당연하게도 지원하는 직무와 관련된 내용으로 써 내려갔습니다.&amp;nbsp; 한 프로젝트당 10~14줄 정도 적었던 것 같네요..&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;1086&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/wXszM/btrEX1zuv5a/kvPfQzMrHxRIQfTz4ZEOH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/wXszM/btrEX1zuv5a/kvPfQzMrHxRIQfTz4ZEOH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/wXszM/btrEX1zuv5a/kvPfQzMrHxRIQfTz4ZEOH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FwXszM%2FbtrEX1zuv5a%2FkvPfQzMrHxRIQfTz4ZEOH1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;511&quot; height=&quot;495&quot; data-origin-width=&quot;1120&quot; data-origin-height=&quot;1086&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. 온라인 코딩 테스트&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LINE PLUS의 코딩 테스트는 조금 길었습니다. 3시간 30분이었습니다..(4시간이었나?) &lt;b&gt;총 4문제&lt;/b&gt;로 구성되어있었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저는 AI개발자다 보니 python을 많이 쓰기 때문에 &lt;span style=&quot;color: #ee2323;&quot;&gt;python&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;저는 총 4문제 중 2문제를 맞히고 2문제는 풀긴 했는데 모든 테스트에 통과하지 못하였습니다.&amp;nbsp; 못 푼 문제 각각에서 몇 개의 테스트는 통과하고 몇 개는 테스트를 통과하지 못했는데요. 모두 runtime error가 떠서.. 제 머리로는 왜 틀렸는지 1차 면접 볼 때까지 몰랐습니다. (1차 면접에서 면접관님이 왜 runtime error가 발생했는지 알려주셨답니다..ㅎㅎ 감사합니당 사이다...)&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;runtime error때문에 코딩 테스트 결과를 조마조마하며 기다렸고 결국 통과하였습니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;50&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P0ezp/btrE0a9Pi5c/9yDkhUH6wVoP2aTPjKn7X0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P0ezp/btrE0a9Pi5c/9yDkhUH6wVoP2aTPjKn7X0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P0ezp/btrE0a9Pi5c/9yDkhUH6wVoP2aTPjKn7X0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP0ezp%2FbtrE0a9Pi5c%2F9yDkhUH6wVoP2aTPjKn7X0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;624&quot; height=&quot;39&quot; data-origin-width=&quot;800&quot; data-origin-height=&quot;50&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Pre-test+1차 면접&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Pre-test와 1차 면접은 온라인으로 진행되었습니다. Pre-test의 내용은 발설하면 안 될것&amp;nbsp;같아 말씀 못 드리지만 제 역량을 평가하기 위한 test였습니다.. 나름 석사 생활이 그리워지는 test였습니다.&amp;nbsp;&amp;nbsp;&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;1차 면접은 60분 동안 진행되었습니다. 1차 면접은 &lt;span style=&quot;color: #ee2323;&quot;&gt;기술 면접&lt;/span&gt;으로 면접관님들은 3분이 들어오셨습니다. 제 자기소개를 하는 것을 시작으로 면접관님들도 자기소개를 친절하게 해 주셨습니다. 제가 쓴 이력서에 대한 내용 을 질문해주셨고 지원한 직무에 대한 이해도가 있는지에 대한 질문도 하셨습니다. 당연히 지원한 직무와 관련한 논문도 읽고 관련 블로그도 찾아보고 했기 때문에 생각보다 수월하기 답할 수 있었습니다.&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;그리고 제 블로그 내용에 대한 질문도 하시면서 주로 어떤 (AI) 분야에 관심이 있는 지 블로그 글에 대해 얼마나 이해하고 있는 지 물어보셨습니다. (모르는 것은 모른다고 말씀드렸습니다ㅎㅎ..)추가로 이직 사유와 지원 이유를 여쭤봐 주셨고 마지막으로 제가 하고 싶은 질문을 하면서 마무리하였습니다.&amp;nbsp;&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;대체적으로 만족스럽게 잘 답변하였다고 생각하며 이번에도 좋은 결과를 기대하였습니다...!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;52&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjRlcO/btrEXOUR0xK/hmdKVFpkNzGFOqactWPhEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjRlcO/btrEXOUR0xK/hmdKVFpkNzGFOqactWPhEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjRlcO/btrEXOUR0xK/hmdKVFpkNzGFOqactWPhEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjRlcO%2FbtrEXOUR0xK%2FhmdKVFpkNzGFOqactWPhEK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;536&quot; height=&quot;36&quot; data-origin-width=&quot;774&quot; data-origin-height=&quot;52&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5.&amp;nbsp; 2차 면접&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차 면접 또한&amp;nbsp; 60분으로 진행한다고 안내가 되었습니다. 2차 면접은 면접관 1분이 들어오셨고 &lt;span style=&quot;color: #ee2323;&quot;&gt;기술+인성 면접&lt;/span&gt; 느낌이었습니다. 1차와 비슷하게 자기소개를 시작으로 이력서에 대해 간단한 질문을 해주셨습니다. 그리고 석사생활이나 직장생활하면서 겪은 문제점이나 해결방안을 얘기하면서 스무스 하게 진행하였습니다. 그리고 제가 궁금한 내용을 질문드리며 면접을 마무리하였습니다. (&lt;span&gt;60분 예정이었던 면접은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;30분만에 끝났습니다. ) 1차와 비슷하게 면접관님이 친절하고 잘 대해주셔서 편안하게 진행 할 수 있었던 면접이었습니다.&amp;nbsp;&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;2차 면접에서 기술+인성 질문에 대해 답변을 자연스럽게 잘 하였다고 생각하였고 2~3일 뒤에 아래와 같이 합격 소식을 듣게 되었습니다. (2차 면접합격은 메일로 합격메일이 오기전에 인사 담당자 분이 전화가 오시더라고여..ㅎㅎ)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;114&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bsgA8E/btrEYGu5oeI/JEcKNnRlK8ukUhKA7eNXf0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bsgA8E/btrEYGu5oeI/JEcKNnRlK8ukUhKA7eNXf0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bsgA8E/btrEYGu5oeI/JEcKNnRlK8ukUhKA7eNXf0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbsgA8E%2FbtrEYGu5oeI%2FJEcKNnRlK8ukUhKA7eNXf0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;728&quot; height=&quot;61&quot; data-origin-width=&quot;1370&quot; data-origin-height=&quot;114&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;6. 레퍼 체크 및 처우 협의&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2차 합격 후에 레퍼 체크를 진행하는데 이 과정에서 재직 중인 회사(노타)사람들이 많이 알게 되면서 눈치가 보였습니다... 그리고 레퍼체크까지 잘 마무리되고 처우 협의를 LINE PLUS 인사담당자분과 잘 진행하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;7. 최종합격&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 한달~한달반 정도 진행된 면접 프로세스를 통해 결국 최종합격되었고 LINER가 되었습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/G923L/btrEYIfmmf0/niG8ZbRdYjzlzpzEYvQvA1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/G923L/btrEYIfmmf0/niG8ZbRdYjzlzpzEYvQvA1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/G923L/btrEYIfmmf0/niG8ZbRdYjzlzpzEYvQvA1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FG923L%2FbtrEYIfmmf0%2FniG8ZbRdYjzlzpzEYvQvA1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;389&quot; height=&quot;230&quot; data-origin-width=&quot;886&quot; data-origin-height=&quot;524&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 제가 면접을 진행하면서 이직 할 때 중요하게 준비했던 것들은 아래와 같습니다! (개인적인 의견임을 알려드립니다.)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;지원하는 직무와 지금 하고 있는 직무의 연관성이 있어야 함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;지원하는 직무 에 대해 공부하고 알아보는 게 당연시 되어야함&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;지원하는 회사와 직무에 대해 잘 모르는 것으로 보이면 어떤 면접관도 좋아하지 않겟죠&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;이력서에 쓴 내용은 물론이고 관련된 방법론과 결과는 모두 정리정돈 되어 있어야 하고 잘 말할 수 있어야 함&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&quot;자기소개 &amp;gt; 이직 사유 &amp;gt; 해당 직무 지원이유 &amp;gt; 궁금한 점&quot;이 스토리 텔링 되면 좋음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;결국 면접은 (1) 면접관님이 질문한 내용을 잘 이해하고 (2) 그에 대한 대답을 &lt;span&gt;설득력있게 전달하면 끝&lt;/span&gt;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span&gt;설득력 있다는 것은 결국 내 생각(의견)과 함께 그에 알맞는 근거를 같이 말씀드리는 것이라 생각합니다!&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;만약 면접에서 떨어졌다면 왜 떨어졌는 지 자기분석하고 분석한 내용을 바탕으로 성장해야함&amp;nbsp;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>일상</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/54</guid>
      <comments>https://da2so.tistory.com/54#entry54comment</comments>
      <pubDate>Fri, 17 Jun 2022 00:43:48 +0900</pubDate>
    </item>
    <item>
      <title>EfficientFormer: Vision Transformers at MobileNet Speed 논문 리뷰</title>
      <link>https://da2so.tistory.com/53</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 Snap Inc. 에서 게재한 논문인 &lt;a href=&quot;https://arxiv.org/pdf/2206.01191.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;EfficentFormer&lt;/a&gt; 논문을 리뷰합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt; 1. Introduction&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 논문은 주요 내용은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;&amp;nbsp;&quot;Vision Transformer(ViT)가 high performance를 내면서 (mobile device에서) mobilenet만큼 빨라질 수 있을까&quot;에 대한 의문점에서 시작&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본적으로 VIT는 accuracy 성능은 좋은데 lightweight CNN(e.g. MobileNet)보다 느리다는 단점을 가짐&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;그 의문점을 풀기 위해 기존 ViT의 inefficient한 구조에 대해 분석&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Efficient한 구조를 갖는 dimension-consistent한 ViT 모델(&lt;span style=&quot;color: #006dd7;&quot;&gt;EfficientFormer&lt;/span&gt;) 을 제안&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특히나, 해당 논문은 FLOPs나 parameter수가 아닌 inference speed에 초점을 맞춤&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 EfficientFormer 모델은 아래 그림에서 보이듯이 inference speed도 빠르면서 좋은 accuracy성능을 보임을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;646&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dOwbaQ/btrD8N9K62L/1gJpwbalzHbjZKCAKtbQGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dOwbaQ/btrD8N9K62L/1gJpwbalzHbjZKCAKtbQGk/img.png&quot; data-alt=&quot;All models are trained on ImageNet-1K and inference speeds are measured by iPhone 12 with CoreMLTools&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dOwbaQ/btrD8N9K62L/1gJpwbalzHbjZKCAKtbQGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdOwbaQ%2FbtrD8N9K62L%2F1gJpwbalzHbjZKCAKtbQGk%2Fimg.png&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; alt=&quot;EfficientFormer accuracy and inference speed&quot; loading=&quot;lazy&quot; width=&quot;478&quot; height=&quot;373&quot; data-origin-width=&quot;827&quot; data-origin-height=&quot;646&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;All models are trained on ImageNet-1K and inference speeds are measured by iPhone 12 with CoreMLTools&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. On-device Latency Analysis of Vision Transformers&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 ViT의 구조중 어떤 operation이나 architecture가 on-device inference speed에 악영향을 주는지 확인하기 위해 실험 및 분석을 하였고 아래와 같은 &lt;b&gt;4가지 observations&lt;/b&gt;을 확인하였습니다. 아래 그림은 iPhone12 device에서 latency profiling을 진행한 결과를 나타냅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;593&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tFyZJ/btrD9GidG31/0kpwFBtOW460NWQBDW1A9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tFyZJ/btrD9GidG31/0kpwFBtOW460NWQBDW1A9k/img.png&quot; data-alt=&quot;Latency profiling on iPhone 12 with CoreML. The accuracy is measured on ImageNet-1K. LeViT uses GeLU activation this is because HardSwish is not supported bty CoreML.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tFyZJ/btrD9GidG31/0kpwFBtOW460NWQBDW1A9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtFyZJ%2FbtrD9GidG31%2F0kpwFBtOW460NWQBDW1A9k%2Fimg.png&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; alt=&quot;Latency profiling on iPhone 12 with CoreML&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;359&quot; data-origin-width=&quot;1185&quot; data-origin-height=&quot;593&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Latency profiling on iPhone 12 with CoreML. The accuracy is measured on ImageNet-1K. LeViT uses GeLU activation this is because HardSwish is not supported bty CoreML.&lt;/figcaption&gt;
&lt;/figure&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;&lt;b&gt;Observation 1: &lt;span style=&quot;color: #8a3db6;&quot;&gt;큰 kernel과 stride를 갖는 patch embedding이 mobile device의 inference speed에 악영향을 줌.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;856&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxJcgg/btrEcAaGEDu/TD6OERrsCSro0O01paEksK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxJcgg/btrEcAaGEDu/TD6OERrsCSro0O01paEksK/img.png&quot; data-alt=&quot;Patch Embedding in ViT&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxJcgg/btrEcAaGEDu/TD6OERrsCSro0O01paEksK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxJcgg%2FbtrEcAaGEDu%2FTD6OERrsCSro0O01paEksK%2Fimg.png&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; alt=&quot;Patch Embedding in ViT&quot; loading=&quot;lazy&quot; width=&quot;572&quot; height=&quot;299&quot; data-origin-width=&quot;1636&quot; data-origin-height=&quot;856&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Patch Embedding in ViT&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림에서 Patch Embedding은 일반적으로 non-overlapping convolution layer로 구현되는데 non-overlapping하게 하기 위해 large kernel과 stride를 사용하게 됩니다. 하지만 대부분의 compiler에서는 large kernel convolution을 지원하지 않고 &lt;span&gt;large kernel convolution은&amp;nbsp;&lt;/span&gt;기존의 acceleration algorithm(e.g. Winograd)으로부터 가속화 되지 않습니다. 위의 latency profiling그림에서 실제로 일반적인 transformer의 patch embedding이 inference하는데 시간을 많이 잡아먹는 것을 확인 가능합니다.&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;저자들은 기존의 non-overlapping patch embedding을 여러 개의 3x3 convolutions(hardware-efficient한 구조)으로 대체하여 모델을 design하겠다고 합니다.&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;&lt;b&gt;Observation 2.&lt;span style=&quot;color: #8a3db6;&quot;&gt; token mixer의 선택에 consistent feature dimension이 중요하고 Multi-Head Self Attention(MSHA)이 주된 speed bottleneck아님.&lt;/span&gt;&lt;/b&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;Token mixer란 말 그대로 token간의 information을 섞는 역할을 하며 token mixer의 대표적인 예시로는 MSHA가 있고 아래 그림에서 보이듯이 transformer variant 논문들에서는 token mixer을 spatial MLP나 Pooling layer을 사용함을 알 수 있다.&amp;nbsp;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;450&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/csDNLi/btrEaNBP5D2/zzvKueqmzg1cpOBEdmxDc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/csDNLi/btrEaNBP5D2/zzvKueqmzg1cpOBEdmxDc1/img.png&quot; data-alt=&quot;Token mixer and its examples&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/csDNLi/btrEaNBP5D2/zzvKueqmzg1cpOBEdmxDc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcsDNLi%2FbtrEaNBP5D2%2FzzvKueqmzg1cpOBEdmxDc1%2Fimg.png&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; alt=&quot;Token mixer and its examples&quot; loading=&quot;lazy&quot; width=&quot;417&quot; height=&quot;294&quot; data-origin-width=&quot;639&quot; data-origin-height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Token mixer and its examples&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자들은 token mixer의 선택으로 머가 좋은 지 분석하기 위해 pooling과 MSHA를 비교 실험하였습니다. (token mixer을 shifted window attention을 사용한 논문이 있는데 해당 모듈은 대부분의 mobile compiler에서 지원하지 않는다고 하여 비교 안 하였음)&lt;/p&gt;
&lt;p 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&gt;pooling을 사용하는 PoolFormer-s24, MSHA를 사용하는 LeViT-256를 비교
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;LeViT는 4D tensor에 대해 Conv연산후, MSHA연산을 하는데 MSHA는 3D tensor에 대해 연산하므로 reshape이 빈번하게 필요함. 헌데 reshape연산이 inference speed의 bottleneck으로 작용(위의 latency profiling그림 참고!)&lt;/li&gt;
&lt;li&gt;PoolFormer는 4D tensor에 대해 Conv연산후 4D tensor대상으로 연산하는 Pooling을 사용하기 때문에 reshape이 필요 없고 결과적으로 PoolFormer가 LeViT보다 빠름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;여기서, 4D tensor 연산에서 3D tensor연산으로 바뀌지 않고 그대로 4D에서 4D tensor연산하는 것을 &quot;&lt;span style=&quot;color: #6164c6;&quot;&gt;consistent feature dimension&lt;/span&gt;&quot; 하다고 함&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DeiT-S와 LeViT-256를 비교했을 때 3D연산을 하는 MSHA자체는 inference speed에 대해 큰 overhead를 가져오지 않음을 확인
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;오직 빈번한 reshape 연산이 없을 경우에만!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 분석을 통해서 token mixer선택에 있어서 consistent feature dimension을 유지하기 위해서 reshape operation을 거의 사용하지 않도록 하는 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;dimension-consistent network&lt;/b&gt;&lt;/span&gt;를 제안하게 됩니다. 제안하는 EfficientFormer는 token mixer선택에 있어서 4D tensor연산을 하는 pooling과 3D tensor연산을 하는 MSHA를 모두 사용하게 됩니다. (자세한 EfficientFormer의 내용은 뒤에서...)&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;&lt;b&gt;Observation 3: &lt;span style=&quot;color: #8a3db6;&quot;&gt;Conv-BN이 LN(Layer Normalization)-Linear보다 latency-favorable하며 Conv-BN을 사용했을 경우 Accuracy drop이 acceptable함&lt;/span&gt;&lt;/b&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;기본적으로 Layer Normalization(LN)-Linear구조는 3D linear projection을 하게 되므로 MSHA와 같이 사용됩니다. 하지만 LN은 전체 network inference time 중&amp;nbsp; 10~20% 정도를 차지하는 것을 위의 latency profiling 그림에서 볼 수 있습니다. 이는 LN이 inference를 잴 경우에 running statistics를 collect 해야 하기 때문에 생기는 시간입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 반해, Conv-BN구조는 4D tensor에 대해 연산을 하고 inference시에 BN이 Conv구조에 folding 될 수 있으므로 latency를 낮추는 데 더 용이합니다. 하지만 Conv-BN은 LN-Linear보다는 &quot;조금&quot; accuracy성능이 낮게 나온다고 하네요.&amp;nbsp;&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;그래서 EfficientFormer가 token mixer가 pooling일 때는 Conv-BN구조를 사용할 것이고 MSHA일 경우는 LN-Linear를 사용하도록 할 것입니다.&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;&lt;b&gt;Observation 4: &lt;span style=&quot;color: #8a3db6;&quot;&gt;nonlinearity(activation function)의 latency는 hardware와 compiler에 의존적&lt;/span&gt;&lt;/b&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;&lt;i&gt;&quot;Towards efficient vision transformer inference: a first study of transformers on mobile devices.&quot;&lt;/i&gt; 논문에서는 GeLU가 hardware에 inefficient 하다고 했지만 실제로 저자들이 iPhone12에서 실험해봤을 때 GeLU가 ReLU만큼 느리지 않다는 것을 확인하였습니다. 반대로, HardSwish는 iPhone12에서 느린 것을 확인하였습니다. (LeViT-256에서 HardSwish 사용 시: 44.5ms, GeLu사용 시: 11.9ms)&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;그래서, EfficientFormer에서는 GeLU을 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Design of EfficientFormer&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8ibPM/btrEcRkwsNo/F5VMTMhkukp600Pb5gKiik/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8ibPM/btrEcRkwsNo/F5VMTMhkukp600Pb5gKiik/img.png&quot; data-alt=&quot;EfficientFormer 구조.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8ibPM/btrEcRkwsNo/F5VMTMhkukp600Pb5gKiik/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8ibPM%2FbtrEcRkwsNo%2FF5VMTMhkukp600Pb5gKiik%2Fimg.png&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; alt=&quot;EfficientFormer architecture&quot; loading=&quot;lazy&quot; width=&quot;799&quot; height=&quot;326&quot; data-origin-width=&quot;1222&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;EfficientFormer 구조.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 observations을 기반으로 저자들은  EfficientFormer을 제안하게 됩니다. EfficientFormer는 patch embedding(\(PatchEmbed\))와 여러 개로 쌓은 meta block(\(MB\))로 구성됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;Y = \prod^m_i MB_i (PatchEmbed(X^{B,3,H,W}_0 )). \quad&amp;nbsp; Eq.(1)&lt;br /&gt;\]&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;\(X_0\)&lt;/b&gt;: Input image
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;\(B\), \(H\), \(W\)&lt;/b&gt;: Batch size, Height, Width&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;\(Y\)&lt;/b&gt;: Output&lt;/li&gt;
&lt;li&gt;&lt;b&gt;\(m\)&lt;/b&gt;: transformer block 총 개수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 \(MB\)는 token mixer(\(TokenMixer\))와 \(MLP\) block으로 표현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;X_{i+1} = MB_i (X_i) = MLP(TokenMixer(X_i)). \quad&amp;nbsp; Eq.(2)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \(X_{i | i &amp;gt;0 } \)은 \(i^{th} MB\)으로부터 forward된 intermediate feature를 뜻합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가로 저자들은 &lt;i&gt;Stage(\(S\))&lt;/i&gt;를 정의하였는데요. Stage는 여러개의 MetaBlock들로 구성되어 있고 각 Stage는 같은 spatial size를 가지며 각 Stage가 가진 MetaBlock의 수를 \(N_i \times\)로 표현한다고 하네요. 총 stage개수는 4개이며 각 stage 사이마다 embedding operation(\(Embedding\))이 있습니다. Embedding operation은 embedding dimension으로 project시키기 위함과 token 길이를 downsample하기위해 사용됩니다.&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;아래부터는 EfficientFormer의 상세한 구조 디자인 설명을 드리도록 하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Dimension-consistent Design&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Section 2에서 말씀드린 observation을 통해 dimension consistent design을 제안하게 됩니다. 위의 그림에서 보이듯이 4D partition부분과 3D partition부분으로 나누는데 처음에는 4D partition으로 stage를 시작하고 마지막부분에 3D partition부분을 수행하여 reshape연산을 최소화하여 dimension consistent design을 구성하게됩니다.&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;b&gt;4D partition: \(MB^{4D} \)로 표현되며 Conv-net style과 token mixer로 pooling layer 사용하여 구현&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;3D partition: \(MB^{3D} \)로 표현되며 linear projection과 token mixer로 MSHA 사용하여 구현&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;위의 EfficientFormer구조 그림은 예시일 뿐이며 실제 4D, 3D partition길이는 NAS를 통해 찾는다고 합니다!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 , input image는 patch embedding에 의해 processing된다고 말씀드렸는데 observation 1에 근거하여 patch embedding은 2개의 3x3 convolution(stride 2)으로 구현됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;X_i^{B,C_{j|j=1}, \frac{H}{4}, \frac{W}{4} }&amp;nbsp; = PatchEmbed(X^{B,3,H,W}_0). \quad&amp;nbsp; Eq.(3)&lt;br /&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;\(C_j\)는 j-th stage의 channel 수를 의미합니다. 그다음으로 \(MB^{4D} \)는 \(Pool\) mixer를 사용하여 다음과 같이 표현됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;\begin{array}{l}&amp;nbsp; I_i = Pool(X_i^{ B,C \frac{H}{2^{j+1}},\frac{W}{2^{j+1}}}) + X_i^{ B,C \frac{H}{2^{j+1}},\frac{W}{2^{j+1}}} \cr X_i^{ B,C \frac{H}{2^{j+1}},\frac{W}{2^{j+1}}} = Conv_B(Conv_{B,G} ( I_i ) ) + I_i&amp;nbsp;&amp;nbsp;\end{array} \quad Eq.(4)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\( Conv_{B,G} \)는 연속된 Conv-BN-GeLU을 의미합니다. &lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;\(MB^{4D} \) block 연산후에는 한번의(one-time) reshape 연산으로 4D에서 3D로 feature dimension을 변경합니다&lt;/b&gt;&lt;/span&gt;. 해당 feature를 입력으로 \(MB^{3D}\)는 다음과 같이 연산합니다.&amp;nbsp;&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;\[&lt;br /&gt;\begin{array}{l} I_i = Linear(MSHA(Linear(LN(X_i^{B, \frac{HW}{4^{j+1}},C_j})))) +&amp;nbsp; X_i^{B, \frac{HW}{4^{j+1}},C_j}, \cr X_i^{B, \frac{HW}{4^{j+1}},C_j} = Linear(Linear_G(LN(I_i))) + I_i \end{array} \quad Eq.(5)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(Linear_G\)는 Linear-GeLU를 의미하고 MSHA연산은 다음과 같다.&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;\[&lt;br /&gt;MSHA(Q,K,V)= Softmax(\frac{Q \cdot K^T}{\sqrt C_j } +b ) \cdot V. \quad&amp;nbsp; Eq.(6)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(Q, K,V\)는 각각 query, key, value를 뜻하며 linear projection으로부터 학습되는 variable이다. 또한 \(b\)는 parameterized attention bias로 position encoding역할로 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Latency Driven Slimming&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.2.1 Design of Supernet&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dimension-consistent design을 하기위해 저자들은 supernet으로부터 architecture search를 하는 NAS방법을 사용한다. Supernet는 다음과 같은 MetaPath(\(MP\))을 정의하여 구성됩니다.&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;\[&lt;br /&gt;\begin{array}{l} MP_{i,j=1,2} \in \{ MB^{4D}_i , II_i \}, \cr MP_{i,j=3,4} \in \{ MB^{4D}_i&amp;nbsp; , MB^{3D}_i II_i \}. \end{array} \quad Eq.(7)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \( II \)은 identity path을 의미하고 \(j\)는 \(j^{th}\) stage, \(i\)는 \(i^{th}\)block을 의미한다. 즉, supernet의 training시에 stage 1,2에는 \(MB^{4D}_i\)&amp;nbsp;&lt;span&gt;또는 \(II_i\)이 선택될 수 있는 것이고 stage 3,4에는&amp;nbsp;&lt;span&gt; \(MB^{4D}_i\),&lt;span&gt;&lt;span&gt; \(MB^{3D}_i\) &lt;/span&gt;&lt;/span&gt;&amp;nbsp;또는 \(II_i\)이 선택 가능하다는 것이다.&lt;/span&gt;&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;&lt;span&gt;&lt;span&gt;여기서 stage 3,4에서만 &lt;span&gt;&lt;span&gt; \(MB^{3D}_i\) &lt;/span&gt;&lt;/span&gt;이 추가된 이유는 아래와 같다.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;MSHA는 token 길이에 따라 quadratic(4배)하게 computation cost가 커지므로 상대적으로 token 길이가 작은 뒤쪽의 stage를 사용&lt;/li&gt;
&lt;li&gt;초기 stage에는 low-level feature를 학습하고 마지막 stage들은 long-term dependencies을 학습한다는 측면에서 뒤쪽의 stage에 MSHA를 적용하는 게 옳음&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.2.2 Search Space&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;\(C_j \): 각 stage의 channel 수&lt;/li&gt;
&lt;li&gt;\( N_j\): 각 stage의 block의 수&lt;/li&gt;
&lt;li&gt;\(\mathcal{N} \): \(MB^{3D}\)에 적용할 마지막 block수&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.2.3 Search algorithm&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAS에서는 supernet을 학습을 완료하고 나면 어떤 path(subnet)가 best인지 찾는 search algorithm이 필요합니다. 저자들은 supernet의 학습이 완료되면 바로 어떤 path가 best인지 알 수 있는 efficient한 gradient-based search algorithm을 제안합니다.&amp;nbsp;&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;해당 search algorithm은 3가지 step을 수행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;(1) &lt;/span&gt;supernet training시에 Gumble Softmax sampling을 함께 사용하여 선택된 \(MP\)의 importance score을 측정합니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;X_{i+1}&amp;nbsp; = \sum_n&amp;nbsp; \frac{e^{ ( \alpha^n_i + \epsilon^n_i )} / \tau }{\sum_n&amp;nbsp;&amp;nbsp;e^{ ( \alpha^n_i + \epsilon^n_i ) /&amp;nbsp;&amp;nbsp;\tau }} \cdot MP_{i,j} \cdot (X_i). \quad Eq.(8)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(\alpha\)는 trainable parameter로 MP의 importance score를 나타내고 해당 block이 선택될 확률을 뜻한다. \( \epsilon \sim U(0,1)\)은 exploration역할을 하게 되고 \( \tau\)는 temperature, \(n\)은 선택 가능한 block은 type을 의미한다.&amp;nbsp;&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;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;(2)&amp;nbsp;&lt;/span&gt; 16배수로 나누어진 channel(width)들을 가지는 여럿 \(MB^{4D}\)와 \(MB^{3D}\)의 on-device latency lookup table을 구축한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #009a87;&quot;&gt;(3)&lt;/span&gt; single-width를 가지는 supernet기준으로 채널 수를 조절하는 &lt;span style=&quot;color: #f3c000;&quot;&gt;&lt;i&gt;gradual slimming&lt;/i&gt;&lt;/span&gt; 을 진행한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;supernet를 구성할 때 각 MP에 대해 여럿 다양한 channel(width) 수 path가 없었는데 그 이유는 저자들은 single-width supernet구조에서 channel수를 줄이는 작업을 진행하였다. (이는 여럿 다양한 channel수에 대한 search도 supernet training시에 할 경우 memory-consuming이 크기 때문)&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;&lt;b&gt;&lt;span style=&quot;color: #f3c000;&quot;&gt;Gradual slimming은 다음과 같이 수행됩니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;\(S_{1,2}, S_{3,4}\)의 각 \(MP_i\)에 대해 importance score을 \( \frac{\alpha^{4D}_i}{\alpha^I_i}, \frac{\alpha^{4D}_i+ \alpha^{3D}_i}{\alpha^I_i} \)로 정의&lt;/li&gt;
&lt;li&gt;각 stage에 대한 importance score를 구하기 위해 각 stage안에 포함되는 \(MP_i\)의 importance score를 summation&lt;/li&gt;
&lt;li&gt;다음 3가지 옵션에 대해 action(수행)해보면서 &lt;b&gt;&lt;i&gt;per-latency accuracy drop&lt;/i&gt;&lt;/b&gt; \( \frac{- \%}{ms}\)을 기준으로 3개 중 1개의 옵션을 취함
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Option 1: 가장 낮은 importance score를 가지는 \(MP\)에 대해 \(II\))(identity)를 선택&lt;/li&gt;
&lt;li&gt;Option 2: 첫 번째 \(MB^{3D}\)를 제거&lt;/li&gt;
&lt;li&gt;Option3: 가장 낮은 importance score를 가지는 \(MP\)에 대해 channel수를 16으로 나눔 (16으로 나눈 \(MP\)에 대해 latency lookup table이 존재하므로 해당 latency 사용)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 gradual slimming에 대한 algorithm은 아래를 참고하시면 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;786&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvSiGX/btrEnwFw7Lx/2MFxGKveUSyKp6icJeVHS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvSiGX/btrEnwFw7Lx/2MFxGKveUSyKp6icJeVHS1/img.png&quot; data-alt=&quot;gradual slimming algorithm&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvSiGX/btrEnwFw7Lx/2MFxGKveUSyKp6icJeVHS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvSiGX%2FbtrEnwFw7Lx%2F2MFxGKveUSyKp6icJeVHS1%2Fimg.png&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; alt=&quot;gradual slimming algorithm&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;560&quot; data-origin-width=&quot;950&quot; data-origin-height=&quot;786&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;gradual slimming algorithm&lt;/figcaption&gt;
&lt;/figure&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;위의 graudal slimming을 수행 완료하여 최종 선택된 EfficientFormer 구조는 아래와 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;701&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Q65BY/btrEjL5fzGr/h6bzVnH6Bc11Jyp0BYMKwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Q65BY/btrEjL5fzGr/h6bzVnH6Bc11Jyp0BYMKwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Q65BY/btrEjL5fzGr/h6bzVnH6Bc11Jyp0BYMKwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQ65BY%2FbtrEjL5fzGr%2Fh6bzVnH6Bc11Jyp0BYMKwk%2Fimg.png&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; alt=&quot;EfficientFormer architecture&quot; loading=&quot;lazy&quot; width=&quot;702&quot; height=&quot;520&quot; data-origin-width=&quot;946&quot; data-origin-height=&quot;701&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Experiments and Discussion&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자들은 PyTorch 1.11과 Timm library를 통해 EfficientFormer를 구현하였고 mobile speed는 A14 bionic chip이 장착되고 NPU사용이 가능한 iPhone12에서 1000번 inference하고 평균을 내어 결과를 내었다고 합니다. CoreMLToolssms run-time model을 deploy하기 위해 사용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1 Image classifciation&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ImageNet-1K dataset에 대해 실험하였고 300 epochs 학습 기준으로 결과를 비교하였습니다. EfficientFormer는 AdamW optimizer를 사용하였으며 5 epochs의 warm-up training과 consine annealing scheduler적용하였습니다. 또한 initial learning rate는 \(10^{-3} \times (batch sizze / 1024) \), minimum learning rate는 \(10^{-5}\)이며 distillation을 위한 teacher model을 RegNetY-16GF(82.9% top-1 accuracy on ImageNet-1k)으로 설정하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;696&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZjJTn/btrEjMpztqP/l7j4Xj5l0tymaXB5YIA3kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZjJTn/btrEjMpztqP/l7j4Xj5l0tymaXB5YIA3kk/img.png&quot; data-alt=&quot;EfficientFormer results in ImageNet-1K&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZjJTn/btrEjMpztqP/l7j4Xj5l0tymaXB5YIA3kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZjJTn%2FbtrEjMpztqP%2Fl7j4Xj5l0tymaXB5YIA3kk%2Fimg.png&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; alt=&quot;EfficientFormer results in ImageNet-1K&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;550&quot; data-origin-width=&quot;949&quot; data-origin-height=&quot;696&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;EfficientFormer results in ImageNet-1K&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.2 EfficientFormer as Backbone&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Detection이나 segmentation task에서도 performance가 뛰어난지 확인하기 위해 EfficientFormer를 backbone으로 사용하였습니다. Mask-RCNN에 EfficientFormer을 combine하였고 COCO-2017 dataset기준으로 결과를 측정하였습니다. EfficientFormer의 weight는 ImageNet-1K pretrained weight로 initialization하였고 AdamW optimizer와 initial learning \( 1 \times 10^{-4} \)을 사용하였고 12 epochs만 학습했다고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;392&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/blw5oF/btrEnKX5QTT/lyKa74DlHa6keZ7prDsKhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/blw5oF/btrEnKX5QTT/lyKa74DlHa6keZ7prDsKhK/img.png&quot; data-alt=&quot;EfficientFormer results in COCO 2017&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/blw5oF/btrEnKX5QTT/lyKa74DlHa6keZ7prDsKhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fblw5oF%2FbtrEnKX5QTT%2FlyKa74DlHa6keZ7prDsKhK%2Fimg.png&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; alt=&quot;EfficientFormer results in COCO 2017&quot; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;332&quot; data-origin-width=&quot;934&quot; data-origin-height=&quot;392&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;EfficientFormer results in COCO 2017&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI paper review/Mobile-friendly</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/53</guid>
      <comments>https://da2so.tistory.com/53#entry53comment</comments>
      <pubDate>Wed, 8 Jun 2022 15:00:15 +0900</pubDate>
    </item>
    <item>
      <title>Python (2) Dict와 Set 차이</title>
      <link>https://da2so.tistory.com/52</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Dict과 Set은 &lt;b&gt;특정 데이터를 unique하게 참조할 수 있는 별도 객체가 있는 자료구조&lt;/b&gt;입니다. 데이터를 참조하는 일명 참조 객체는 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;키(key)&lt;/b&gt;&lt;/span&gt;, 데이터를 &lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;'값(value)'&lt;/span&gt;&lt;/b&gt;이라고 한다. (Set에서 key-value쌍이 없고 참조 객체 key만 있습니다.) 하나의&amp;nbsp; 참조하는 객체 키는 일반적으로 문자열을 사용하지만 hashable하다면 어떤 타입이든 상관없습니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;hashable 타입은 __hash__ 매직함수 그리고 __eq__또는 __cmp__ 매직함수를 구현한 타입입니다. 파이썬 내부 타입은 모두 매직함수가 구현되어져 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dict과 Set은 모두 &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;유일한 키를 가지므로 주어진(찾고싶은) 색인(객체)을 O(1) 시간복잡도로 찾을 수 있습니다&lt;/b&gt;&lt;/span&gt;. (리스트는 선형탐색의 경우 O(n)이 걸리는데 말이죠)&amp;nbsp; 그래서 주어진 색인을 빠르게 찾을 수 있다는 것인 Dict과 Set의 특징점이자 장점입니다. 하지만 Dict과 Set은 메모리를 많이 사용하고 hash함수에 의존적이므로 hash 함수가 느리다면 Dict과 Set연산도 느려질 수 있다는 단점을 가집니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Dict과 Set의 차이&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;Dict과 Set의 차이는 Set에서는 값(Value)이 없다는 점이다&lt;/span&gt;&lt;/b&gt;. 즉, Set은 유일한 키를 저장하는 자료구조이며 이는 집합 연산에 유용하다는 것을 의미합니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그래밍적으로는 다음과 같은 선언 차이가 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1653032723700&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = {1: 'a', 2: 'b'} #Dict
b = {1,2,'a','b'} #Set&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;346&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k6sDw/btrCH4D76ao/Wckt65oBSDcuKV9SYuOVhK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k6sDw/btrCH4D76ao/Wckt65oBSDcuKV9SYuOVhK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k6sDw/btrCH4D76ao/Wckt65oBSDcuKV9SYuOVhK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk6sDw%2FbtrCH4D76ao%2FWckt65oBSDcuKV9SYuOVhK%2Fimg.png&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; alt=&quot;set vs dict&quot; loading=&quot;lazy&quot; width=&quot;241&quot; height=&quot;209&quot; data-origin-width=&quot;400&quot; data-origin-height=&quot;346&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 알수 있듯이 Set과 Dict모두 중괄호({ })로 정의 되지만 key와 value쌍으로 정의되면 dict type을 가지고 key만 주어지면 set type을 가집니다. (마지막에 보이듯이 빈 중괄호(c={})는 dict type으로 정의됩니다.)&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Dict과 Set의 동작 원리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파이썬 내부적으로 Dict과 Set의 동작원리에 알고싶다면 계속 읽으시면 좋을 것 같습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말씀드렸듯이 Dict과 Set은 모두 hashtable을 사용한다고 말씀드렸습니다. 좀 더 구체적으로 hashtable이 어떻게 사용되는 지 알 아보죠.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Dict의 hashtable은 key, value, hash 을 (메모리에) 저장합니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Set의 hashtable은 key, hash을 (메모리에) 저장합니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;hash값은 어떻게 계산되는지부터 알아보면&lt;/b&gt; hash값은 key 데이터를 입력으로 hash function의 결괏값을 의미합니다. 여기서 중요한 점은 우리가 입력할 수 있는 수 만 가지(실제로 더 많죠 ㅎ) key값이 있을 텐데... 이 수만 가지의 key값을 입력으로 &lt;b&gt;서로 다른&lt;/b&gt; hash값을 내뱉을 수 있어야 합니다. (이를 만족하는 것을 최소 충돌이라 합니다.) key의 고유한 hash값이 존재해야 O(1) 시간 복잡도로 색인 탐색이 가능하니까요!&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;실제로 hash function을 거친 output값(hash 값)과 mask와의 연산을 해야 하는데 해당 부분 내용이 많아질 거 같아 생략합니다. 간단하게 말씀드리면 mask는 hash 값이 할당된 메모리 블록의 수보다 작아지도록 조정하는 데 사용됩니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;184&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cCZVTd/btrCS0H2VP7/p04s5fjLwEhk2ECsT4pBU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cCZVTd/btrCS0H2VP7/p04s5fjLwEhk2ECsT4pBU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cCZVTd/btrCS0H2VP7/p04s5fjLwEhk2ECsT4pBU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcCZVTd%2FbtrCS0H2VP7%2Fp04s5fjLwEhk2ECsT4pBU1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;303&quot; height=&quot;232&quot; data-origin-width=&quot;240&quot; data-origin-height=&quot;184&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 해시 함수는 entropy(엔트로피)가 커지도록 설계해야합니다. entropy는 불확실성을 의미하니 불확실성이 클수록 고르고 균일한 분포의 hash값을 만들어 낼 것입니다. (entropy에 더 궁금하시면 구글링 해보세요 Machine Learning하시는 분이라면 다 이해하실거라 생각하며 저는 넘어갑니다) 그리고 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;엔트로피가 최대가 되는 hash fucntion은 최소 충돌을 보장하며 Complete hash function이라 합니다.&lt;/b&gt;&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;&lt;b&gt;이제는 hash table이 데이터를 어떻게 저장하는지 알아보죠&lt;/b&gt;! 그러려면 옛날 버전 python의 hash table 구조부터 알아보죠. (Dict에 대한) 연속된 메모리 주소에 hash, key, value를 저장하는 것을 보실 수 있죠.&lt;/p&gt;
&lt;pre id=&quot;code_1653218909118&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;--+-------------------------------+
  |    hash       key     value
--+-------------------------------+
0 |    hash0      key0    value0
--+-------------------------------+
1 |    hash1      key1    value1
--+-------------------------------+
2 |    hash2      key2    value2
--+-------------------------------+
. |           ...
__+_______________________________+&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음과 같이 dictionary를 정의하였다면&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653219723790&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;my_info = {'name': 'sinhan', 'birth': '1995-06-23', 'gender': 'male'}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hash table은 다음과 같이 데이터를 저장합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1653219859694&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;entries = [
    ['--', '--', '--']
    [-230273521, 'birth', '1995-06-23'],
    ['--', '--', '--'],
    ['--', '--', '--'],
    [1231236123, 'name', 'sinhan'],
    ['--', '--', '--'],
    [9371539127, 'gender', 'male']
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 아시겠지만 index 0, 2, 3, 5번 index에는&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt; &lt;code&gt;['--', '--', '--']&lt;/code&gt; 와 같이 불필요한 데이터가 저장되고 있습니다. 이는 hash table의 데이터 저장이 비효율 적인 것을 알 수 있습니다.&amp;nbsp; (실제로는 index가 더 많을 것이니 해당 문제가 더 심각해지겟죠!)&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;그래서 현재 pyhton은 index와 (hash, key, value)을 분리시켜 메모리 효율성을 올렸습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1653220283405&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Indices
----------------------------------------------------
None | index | None | None | index | None | index ...
----------------------------------------------------

Entries
--------------------
hash0   key0  value0
---------------------
hash1   key1  value1
---------------------
hash2   key2  value2
---------------------
        ...
---------------------&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 위 예제의 hash, key, value값은 아래와 같이 효율적으로 저장됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1653220452951&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;indices = [None, 1, None, None, 0, None, 2]
entries = [
    [-230273521, 'birth', '1995-06-23'],
    [1231236123, 'name', 'sinhan'],
    [9371539127, 'gender', 'male']
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 그래서 index 1, 4에 해당하는 [hash, key, value]는 각각 &lt;code&gt;[1231236123, 'name', 'sinhan']&lt;/code&gt;, &lt;code&gt;[-230273521,&amp;nbsp;'birth',&amp;nbsp;'1995-06-23']&lt;/code&gt;이 되겠죠. 이를 통해 hash table의 데이터 저장 구조도 알아보았습니다.&lt;/p&gt;</description>
      <category>Computer Science</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/52</guid>
      <comments>https://da2so.tistory.com/52#entry52comment</comments>
      <pubDate>Sun, 22 May 2022 21:00:29 +0900</pubDate>
    </item>
    <item>
      <title>Python (1) List와 Tuple 차이</title>
      <link>https://da2so.tistory.com/51</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Machine learning이 대부분 python기반이다 보니 python을 더 정진하면 좋겠다는 생각에 시작합니다. 가시죠!&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;List와 Tuple은 &lt;b&gt;배열&lt;/b&gt;이라는 자료구조 특성을 가집니다. 아래 그림은 배열이 메모리에 할당되는 방식입니다. 배열이 연속적인 메모리에 정렬되있음을 알 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;배열이란?&lt;/b&gt; 정해진 고유의 순서에 따라 데이터를 나열한것을 말합니다. 순서가 있기 때문에 배열 내 특정 위치의 데이터를 O(1) 시간 복잡도로 접근 가능합니다.&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/6yxdB/btrCDbRGVAq/BNdMKqgMKx1Mk6AEJAy9L0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/6yxdB/btrCDbRGVAq/BNdMKqgMKx1Mk6AEJAy9L0/img.png&quot; data-alt=&quot;배열 메모리 할당 방식&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/6yxdB/btrCDbRGVAq/BNdMKqgMKx1Mk6AEJAy9L0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F6yxdB%2FbtrCDbRGVAq%2FBNdMKqgMKx1Mk6AEJAy9L0%2Fimg.png&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; alt=&quot;array memory allocation&quot; loading=&quot;lazy&quot; width=&quot;366&quot; height=&quot;243&quot; data-origin-width=&quot;469&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;배열 메모리 할당 방식&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 시작주소를 알고 있고 순서에 따라 나열되어있기 때문에 특정 위치의 데이터에&amp;nbsp; 바로 접근가능합니다. 예를 들어 3번째 위치의 데이터에 접근하고 싶으면 0x06에서 2칸떨어진 0x08위치의 값을 읽으면 됩니다.&amp;nbsp;&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;그렇다면 List와 Tuple의 차이는 멀까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;List&lt;/b&gt;:&lt;/span&gt; 저장하는 데이터나 배열 크기를 변경할 수 있는 &lt;b&gt;동적 배열&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Tuple&lt;/b&gt;:&lt;/span&gt; 내용이 고정된 변경 불가능한 &lt;b&gt;정적 배열&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. List&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;List는 &lt;i&gt;&lt;b&gt;동적 배열&lt;/b&gt;&lt;/i&gt;임을 기억하자! 그래서 저장 용량을 늘리거나 줄일 수도 있다. 추가적으로 수정이 가능하다는 것이 특징입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수시로 데이터가 변경되거나 추가되고 삭제되는 내용을 나타내려면 리스트를 사용해야 합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653010168538&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = [1, 'a', 2, 'b']
a.append(3)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 코드에서 보이듯이 list는 대괄호([ ])로 선언 가능하며 데이터 타입을 섞어서 사용할 수 있고 (뒤에서 말하겠지만 물론 tuple도 가능하다) &lt;code&gt;append&lt;/code&gt;함수로 데이터를 추가 가능합니다. 이는 동적 배열이 배열의 크기를 변경하는 resize 연산을 지원하기 때문입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징점은 배열의 크기가 \(N\)일때 1개의 요소가 더 추가될 때 배열의 크기가 \(N+1\)이 되는 것이 아니라 나중을 위한 여유분으로 \(N\)보다 큰 \(M\)만큼의 메모리를 할당한다. 이는 데이터가 추가될 때마다 메모리 할당과 복사 요청을 줄이기 위함이에요. (복사 비용이 큽니다!)&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;다음은 파이썬 3.7기준으로 리스트 크기 할당 방정식이에요. 해석하자면 \(N\)이 현재 리스트 안의 데이터의 개수(크기)이고 \(M\)은 해당 리스트의 할당된 메모리 크기이다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653010967225&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;M = (N&amp;gt;&amp;gt;3) + (3 if N &amp;lt; 9 else 6)

# N | 0 | 1-4 | 5-8 | 17-25 | 26-35| ... | 991-1120|
# M | 0 |  4  |  8  |   25  |  35  | ... |   1120  |&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 현재 리스트 크기가 4이고 하나의 데이터를 더 추가하게 되면 8개의 메모리 크기를 가진&lt;b&gt; 새로운 리스트&lt;/b&gt;를 생성하게 됩니다! (&lt;i&gt;만약 우리가 데이터를 991개만 사용하는 리스트를 반복적으로 새로운 변수에 할당하는 프로그램이 있다면 사실상 각 변수는 메모리를 1120개 데이터를 사용하는 것과 같으므로 리스트를 사용할때는 조심해야하는 부분이다. 손해다..ㅠ)&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Tuple&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tuple은 정적 배열이며 내용을 바꾸거나 크기를 변경하지 못합니다. Tuple은 소괄호('( )')로 선언가능합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653011766746&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;b = (1, 'a', 2, 'b')
b[1] = 'c' # error&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;97&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O6xmY/btrCE1HDXwr/uKRpv0Ykd5j5lGEhP6krnK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O6xmY/btrCE1HDXwr/uKRpv0Ykd5j5lGEhP6krnK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O6xmY/btrCE1HDXwr/uKRpv0Ykd5j5lGEhP6krnK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO6xmY%2FbtrCE1HDXwr%2FuKRpv0Ykd5j5lGEhP6krnK%2Fimg.png&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; alt=&quot;tuple example&quot; loading=&quot;lazy&quot; width=&quot;434&quot; height=&quot;87&quot; data-origin-width=&quot;484&quot; data-origin-height=&quot;97&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Tuple이 크기를 변경할 순 없어도 두 개의 튜플을 새로운 튜플로 합칠 수 있습니다. 이는 정확히 합친 만큼의 메모리를 할당하는 것이기 때문에 List와 달리 여유공간 메모리를 주지 않습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1653012225340&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;b1 = (1,2,3,4)
b2 = (5,6,7,8)
b3 = b1 + b2 # b3 is (1,2,3,4,5,6,7,8)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Tuple이 정적인 데이터를 쓸 때(e.g. 주민등록번호, 여권번호) 더 가볍고 효과적입니다.&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;그리고 정적이기 때문에 파이썬이 내부적으로 수행하는 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;리소스 캐싱&lt;/b&gt;&lt;/span&gt;이 가능하다는 것이 특징입니다. 파이썬은 Garbage Collection(GC)을 통해 더 이상 사용되지 않는 변수에 할당된 메모리를 반환해 해제합니다. 하지만 크기가&amp;nbsp; 20이하인 Tuple은 크기별로 최대 2만개(e.g. 크기가 1인 tuple 2만개, 2인 tuple 2만개, ....)까지 즉시 해제하지 않고 나중을 위해 저장해둡니다. 그래서 나중에 다시 필요해지면 OS에서 메모리를 새로 할당받지 않고 기존에 할당해둔 메모리를 재사용합니다.&amp;nbsp;&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;아래는&amp;nbsp; 캐싱의 차이를 보여주기 위해 list와 tuple 차이 예시입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qOPxa/btrCCglLWba/Mwgyq3YKTFU3oq3LQzMZ40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qOPxa/btrCCglLWba/Mwgyq3YKTFU3oq3LQzMZ40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qOPxa/btrCCglLWba/Mwgyq3YKTFU3oq3LQzMZ40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqOPxa%2FbtrCCglLWba%2FMwgyq3YKTFU3oq3LQzMZ40%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;128&quot; data-origin-width=&quot;992&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;timeit모듈은 작은 python code의 &lt;span style=&quot;background-color: #ffffff; color: #222222;&quot;&gt;execution&lt;/span&gt; time을 측정하는 것입니다. 위 그림으로부터 List와 Tuple의 할당을 100,000번 실행한 결과를 볼 수 있습니다. List는 100,000번 운영체제에서 새롭게 메모리를 할당받아 실행하고 해제하는 것을 반복하지만 Tuple은 한번 메모리를 할당하고 해제하지 않기 때문에 바로 캐싱을 통해 메모리를 재사용합니다. 그래서 속도 차이를 보면 Tuple이 List보다 약 6배 빠른 것을 볼 수 있습니다.&lt;/p&gt;</description>
      <category>Computer Science</category>
      <category>Python</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/51</guid>
      <comments>https://da2so.tistory.com/51#entry51comment</comments>
      <pubDate>Fri, 20 May 2022 12:57:57 +0900</pubDate>
    </item>
    <item>
      <title>Learning Features with Parameter-free Layers 논문 리뷰</title>
      <link>https://da2so.tistory.com/50</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 &lt;b&gt;ICLR 2022에 accept된 Naver clova 논문&lt;/b&gt;인 &lt;a href=&quot;https://openreview.net/pdf?id=bCrdi4iVvv&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Learning Features with Parameter-free Layers &lt;/a&gt;&amp;nbsp;을 리뷰하도록 하겠습니다. 해당 논문은 accuracy성능은 유지하면서 latency을 상당히 줄일 수 있는 (operation)layer를 제안하는 데 기여하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Introduction&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 많은 논문들이 efficient한 operation 또는 layer들을 제안하였습니다. 여기서 efficient의 의미는 accuracy성능은 향상시키거나 유지하면서 적은 parameter와 낮은 latency를 도달할 수 있다라는 것입니다. (기존의 efficient한 operation 또는 layer는 뒤에서 더 자세히 설명드리겠습니다.)&amp;nbsp;&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;해당 논문은 기존 논문들에서 제안한 operation 또는 layer의 단점을 보완할 수 있는 operation(layer)을 제안하게 됩니다. 그래서 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;parameter를 사용하지 않는 parameter-free operation을 제안&lt;/b&gt;&lt;/span&gt;합니다. 제안한 parameter-free operation을 적용했을 때 trainable layers을 사용했을 때만큼의 accuracy 성능을 도달할 수 있는 지 많은 실험하였고 증명하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;더하여 제안한 Parameter-free operation이 search space에 포함되었을 때 Neural Architecture Search(NAS)에서 해당 operation이 잘 찾아지는 지 보아 제안한 operation의 효용성을 판단하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. &lt;span&gt;Preliminaries&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;해당 논문에서 제안한 parameter-free operation을 설명하기전에 알아야할 기본적인 convolution layer(or block)부터 기존 논문들에서 제안한 efficient한 layer까지에 대해 설명드리겠습니다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.1 Basic block&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;2.1.1 Convolution layer&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기본적인 convolution operation은 matrix multiplication으로 표현가능합니다. \(f \in \mathcal{R}^{c_{in} \times H \times W} \)을 input feature, kernel size \(k\), stride \(s\)가 주어 질 때 convolution operation은 다음과 같습니다.&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;y_{o, i, j} = \sigma ( \sum^{ \lfloor k/2 \rfloor}_{ h,w = - \lfloor k/2 \rfloor } \sum^{ c_{in} }_{u=1} W_{o, u, h, w} \cdot f_{u, r*i+h, r*j+w} ), \quad&amp;nbsp; Eq. (1)&lt;br /&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;&lt;span&gt;위에서 \(W\)는 weight matrix를 의미하고 \( \sigma \)는 activation function(e.g. ReLU)을 의미합니다. 여기서는 BN layer는 표현하지는 않았습니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;2.1.2 Bottleneck block&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Bottleneck block부터는 efficiency를 목적으로 제안된 것입니다. 아래의 그림과 같이 conv 1x1 operation을 통해 채널 수를 줄인 다음 conv3x3 operation을 진행하는 것이 1x1 conv없이 홀로 큰 채널을 가지고 conv 3x3 operation을 수행하는 것보다 효율적(efficiency)임을 보여줍니다.&lt;/span&gt;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;782&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQ335X/btrApl3k49b/blATQksYUjLpJKrQa0Kfg0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQ335X/btrApl3k49b/blATQksYUjLpJKrQa0Kfg0/img.png&quot; data-alt=&quot;Bottleneck block&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQ335X/btrApl3k49b/blATQksYUjLpJKrQa0Kfg0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQ335X%2FbtrApl3k49b%2FblATQksYUjLpJKrQa0Kfg0%2Fimg.png&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; alt=&quot;Bottleneck block&quot; loading=&quot;lazy&quot; width=&quot;271&quot; height=&quot;283&quot; data-origin-width=&quot;750&quot; data-origin-height=&quot;782&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Bottleneck block&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;위의 bottleneck block을 수식적으로 표현하면 다음과 같습니다.&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;\[&lt;br /&gt;y_{o, i, j} = \sigma ( \sum^{ \rho c_{in} }_{v=1} P_{o,v}\cdot&amp;nbsp; &amp;nbsp;\sigma ( \sum^{ \lfloor k/2 \rfloor}_{ h,w = - \lfloor k/2 \rfloor } \sum^{ \rho&amp;nbsp;&amp;nbsp;c_{in} }_{u=1} W_{v, u, h, w} \cdot g_{v, r*i+h, r*j+w} )), \quad Eq. (2)&lt;br /&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;여기서 \( g_{v, r*i+h, r*j+w} = \sigma ( \sum^{c_{in}}_{u=1} Q_{o,u} \cdot f_{u,i,j} ) \), 행렬 \( P \)와 \(Q\)는 1x1 convolution의 weight를 의미한다.&amp;nbsp; 1x1 conv에서 height, width은 유지한채 오직 output channel수를&amp;nbsp; \(\rho c_{in}\)만큼으로 줄이도록 하여 3x3 conv가 처리해야할 channel연산을 줄이게 됩니다. 그래서 해당 bottleneck 구조에서 efficiency는 3x3 conv에서 연산해야할 channel개수인 \(\rho c_{in}\)으로 결정됩니다. &lt;b&gt;저자들은 efficiecny가 오직 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\(\rho c_{in}\)으로 결정된다는 것을 문제점으로 삼습니다.&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span&gt;2.2 Efficient Building Blocks&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;2.2.1 Inverted Bottleneck&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;bottleneck구조안에 depthwise convolution을 추가한 구조인 inverted bottleneck은 기존 bottleneck의 3x3 conv가 depthwise로 변경되면서 채널에 대한 연산수가 줄게 되고 accuracy성능은 높아집니다. 추가적으로 기존과 다르게 적은 channel수의 feature map을 입력으로 1x1 conv로 channel수를 늘리게 되고 그 다음 depthwise conv를 거친다는 특징을 가집니다.&lt;/span&gt;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;470&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BzJiG/btrAL7bM6Gs/RpgUcWCYbmgJvRdqOX1WRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BzJiG/btrAL7bM6Gs/RpgUcWCYbmgJvRdqOX1WRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BzJiG/btrAL7bM6Gs/RpgUcWCYbmgJvRdqOX1WRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBzJiG%2FbtrAL7bM6Gs%2FRpgUcWCYbmgJvRdqOX1WRk%2Fimg.png&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; alt=&quot; Inverted Bottleneck&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;380&quot; data-origin-width=&quot;943&quot; data-origin-height=&quot;470&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Inverted bottleneck을 수식적으로 표현하면 아래와 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;y_{o, i, j} = \sigma ( \sum^{ \rho c_{in} }_{v=1}&amp;nbsp;&amp;nbsp;P_{o,v} \cdot&amp;nbsp; \sigma ( \sum^{ \lfloor k/2 \rfloor}_{ h,w = - \lfloor k/2 \rfloor } W_{v, h, w} \cdot g_{v, r*i+h, r*j+w} )), \quad Eq. (3)&lt;br /&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;수식에서 알수 있듯이 Eq (2)와 다르게 channel에 대한 summation 연산이 제거되었습니다. 이 작은 차이가 generalization 효과를 더 극대화 시켜 좋은 성능을 얻은것입니다. &lt;b&gt;해당 inverted bottleneck도 efficient하지만 narrow에서 wide로 갈때 channel수가 늘어나는 정도(expansion ratio)에 따라 efficiecny가 결정된다는 문제점은 여전히 똑같습니다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;2.2.1 Variants of Inverted Bottleneck&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;Inverted Bottleneck보다 더 효율적인 layer또는 module을 제안한 기존 논문들은 다음과 같습니다.&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;VersatileNet&lt;/b&gt;: 기존의 conv를 여러개의 convolutional filter로 구성된 filter로 교체&lt;/li&gt;
&lt;li&gt;&lt;b&gt;GhostNet&lt;/b&gt;: 기존 layers들을 regular conv와 추가적인 depthwise conv의 concatenate로 교체&lt;/li&gt;
&lt;li&gt;&lt;b&gt;EfficientNetv2&lt;/b&gt;: pointwise와 depthwise conv를 하나의 regular conv로 fusing시킴&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ShiftNet&lt;/b&gt;: depthwise operation을 shift operation으로 대체함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ShiftNet의 수식은 다음과 같습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\[&lt;br /&gt;y_{o, i, j} = \sigma ( \sum^{ \rho c_{in} }_{v=1}&amp;nbsp;&amp;nbsp;P_{o,v} \cdot&amp;nbsp; \sigma ( \sum^{ \lfloor k/2 \rfloor}_{ h,w = - \lfloor k/2 \rfloor } W_{v, h, w} \cdot g_{v, r*i+h, r*j+w} )), \quad Eq.(4)&lt;br /&gt;\]&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 수식에서 Eq (3)과 다른점은 여기에서 \(W_{v, h, w}\)은 각 채널 \(v\)에 대해 \(h\)와 \(w\)가 1과 0의 값을 가진다는 것이다. 해당 operation을 shift라 명칭하였으며 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;해당 shift operation은 parameter-free으로 즉, parameter를 사용하지 않는다는 efficiency를 가진다&lt;/b&gt;.&lt;/span&gt; 하지만 &lt;b&gt;문제점으로는 여전히 expansion ratio가 커야 accuracy성능이 보장되고 실제로 해당 operation을 구현했을때 최적화 되지 않음을 보였다고 합니다. (CUDA 구현에서도 똑같이 최적화 안된다고 하네여)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Efficient Building Block with Parameter-free operations&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터 논문에서 제안한 parameter-free operation에 대해 설명드리도록 하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Motivation&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 연구에서 ResNet의 layer들은 학습에 기여하지 않기 때문에 해당 layer들을 제거해도 된다는 사실을 근거하여 &lt;b&gt;저자들은 기존 residual block중 몇 개를 parameter free operation으로 대체&lt;/b&gt;하기로 합니다&lt;b&gt;.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Rethinking parameter-free operations&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ShiftNet의 Eq (4)는 그저&amp;nbsp; &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\(W_{v, h, w}\)을 1또는 0의 값으로 할당하였는데요. 본 논문에서는 그렇게 하지 않고 함수 \( s(\cdot) \)을 제안 및 적용하여 \(W\)가 feature map \(g \)의 의존성을 가지도록 합니다. (i.e., \( W_{v, h, w} = s(g_{v, r*i+h, r*j+w} ) \))&amp;nbsp;&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;&lt;span&gt;여기서 의존성을 가지게 하기 위해 함수 \(s(\cdot) \)는 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;각 channel \(v\)에 대해 모든 \(h\), \(w\) 범위 안에서 &lt;span&gt;feature map&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;\(g\)의 가장 큰값만 취하도록 하였습니다. 마치 impulse 함수처럼 말이죠. &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;수식적으로 표현하면 &lt;b&gt;\( W_{v,h^{\ast},w^{\ast}}= 1 \) , \((h^{\ast}, w^{\ast}) =argmax_{ (h,w) } g_{v, r*i+h, r*j+w} \)&lt;/b&gt;&lt;/b&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;&amp;nbsp;이며 다른 이외의 \( W_{v,h,w} \) 의 값은 0으로 할당합니다.&lt;/b&gt; &lt;/span&gt;실제로 함수 \(s\)는 max pooling layer으로 대체하여 사용하였다고 합니다.&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&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;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;즉, parameter-free을 만족시키며 feature map의 의존성을 고려한 operation을 제안하는 것입니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 Empirical Studies&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.3.1 On a Single Bottleneck&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResNet의 하나의 bottleneck block에 대해 제안한 parameter-free operation을 사용했을 때 효용성이 있는 지 실험하였습니다. 다음과 같은 서로 다른 조건으로 학습시킨 모델들의 성능을 비교합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;i&gt;Channel expansion ratio&lt;/i&gt;: [0.25, 0.5, 1.0, 2.0]&lt;/li&gt;
&lt;li&gt;&lt;i&gt;Base channel width&lt;/i&gt;: [32, 64]&lt;/li&gt;
&lt;li&gt;&lt;i&gt;Optimizer&lt;/i&gt;: [SGD, AdamW, AdamP]&lt;/li&gt;
&lt;li&gt;&lt;i&gt;비교 operation&lt;/i&gt;: [conv, dwconv, max]
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;max: 논문에서 제안한 Parameter-free operation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 4x2x3x3=72개의 모델을 비교했을때 아래와 같은 결과를 보았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;435&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RfdAd/btrAKTE4m5t/mljHI0D4beVQp7aQKKN11k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RfdAd/btrAKTE4m5t/mljHI0D4beVQp7aQKKN11k/img.png&quot; data-alt=&quot;Single bottleneck study for top-1 accuracy&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RfdAd/btrAKTE4m5t/mljHI0D4beVQp7aQKKN11k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRfdAd%2FbtrAKTE4m5t%2FmljHI0D4beVQp7aQKKN11k%2Fimg.png&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; alt=&quot;Single bottleneck study for top-1 accuracy&quot; loading=&quot;lazy&quot; width=&quot;737&quot; height=&quot;325&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;435&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Single bottleneck study for top-1 accuracy&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과에서 알 수 있듯이 channel expansion ratio가 작을때 regular conv와 parameter-free operation이 거의 비슷한 성능을 냄을 알 수 있다는 것을 보여주었다. 이는 parameter-free가 regular conv를 대체가능하다는 것을 의미한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.3.2 On Multiple Bottlenecks&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에는 여러개의 bottleneck에 parameter-free operation을 적용했을때도 효과적인지 검증하는 실험입니다.&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;ResNet-26기반으로&amp;nbsp;&lt;/span&gt;depthwise conv와 parameter-free operation을 비교하였다고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYarke/btrAJ8woZ89/8WmItW7NkavKV4AqaJgYR1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYarke/btrAJ8woZ89/8WmItW7NkavKV4AqaJgYR1/img.png&quot; data-alt=&quot;Multiple bottlenecks study&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYarke/btrAJ8woZ89/8WmItW7NkavKV4AqaJgYR1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYarke%2FbtrAJ8woZ89%2F8WmItW7NkavKV4AqaJgYR1%2Fimg.png&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; alt=&quot;Multiple bottlenecks study&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;167&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Multiple bottlenecks study&lt;/figcaption&gt;
&lt;/figure&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;결과적으로 비슷한 top-1 accuracy를 보이지만 inference speed차이에 있어서 parameter-free operation이 월등히 빠름을 보여주고 있습니다. (parameter 수와 FLOPs는 비슷하지만요!)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;3.3.3 On Neural Architecture Searches (NAS)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAS의 search space에 parameter-free operation을 넣었을때 해당 operation이 search되는지 확인하고 성능을 검증하는 실험입니다. 실험을 위해 DARTS라는 NAS방법을 사용하였으며 CIFAR10 dataset에 대해 진행하였습니다. 실험 환경은 다음과 같습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;모든 normal cell을 각각 찾도록 하였음.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;DARTS의 operation search space를 아래와 같이simplify시킴
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;search space: [MAX_POOL 3x3, AVG_POOL 3x3, CONV 1x1, CONV 3x3, DW_CONV 3x3, ZERO, SKIP_CONNECT]&lt;/li&gt;
&lt;li&gt;simplify시켜도 위의 operation들의 조합으로 DARTS의 operation search space를 만들수 있으므로 이렇게 진행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;search를 3번을 진행하여 지속적으로 parameter free operation이 선택되는 지 확인&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;415&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WlMHS/btrAL6437Q6/3vbniyvkfVAN8DvPvCumeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WlMHS/btrAL6437Q6/3vbniyvkfVAN8DvPvCumeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WlMHS/btrAL6437Q6/3vbniyvkfVAN8DvPvCumeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWlMHS%2FbtrAL6437Q6%2F3vbniyvkfVAN8DvPvCumeK%2Fimg.png&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; alt=&quot;NAS result when adding parameter-free operation&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;361&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;415&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3번 search했을 때 모두 normal cell에 parameter-free operation(max pooling)이 선택되었음을 알 수 있고 DARTS와 유사한 결과인 첫번째 행과 비교했을때도 성능이 비슷함을 볼 수 있다. 또한 재밌는 것은 node의 수가 증가할 수록(모델이 커질 수록) parameter-free cell이 많이 선택됨을 알 수 있다. 즉, NAS에서도 parameter-free operation이 효과적으로 사용될 수 있음을 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Designing Efficient Deep Neural Networks&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 parameter-free operation의 효용성은 실험적으로 확인하였으니 이제 CNN과 ViT에서 parameter-free operation이 들어간network design을 해보죠.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1 Efficient CNN Architecture&lt;/b&gt;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.1.1 Hybrid Architecture with Efficient Bottlenecks&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 연속적인 3x3 conv, BN와 ReLU 연산을 parameter-free operation인 max pool operation으로 대체시켜 공간적인(spatial) feature를 추출하도록 한다. Base가 되는 모델은 ResNet50이며 기존의 bottleneck과 max pool operation이 들어간 efficient bottleneck의 조합으로 새로운 모델을 제안한다.&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;위의 NAS실험에서 알 수 있듯이 normal cell에서만 parameter-free operation이 사용되었으므로 여기서도 &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;downsampling block이 아닌 오직 normal block에만 parameter-free operation이 사용되도록 하였습니다&lt;/b&gt;.&lt;/span&gt; 해당 모델을&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;i&gt;&lt;b&gt; Hybrid architecture&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;라고 명칭하네요!&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;4.1.2 Architecture Study&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ResNet50을 기준으로 efficient block을 여러가지의 경우의 수로 나누어 실험했을 경우 측정한 accuracy, latency 성능을 비교하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k1hI1/btrALUxIj3B/qmtWkRkRxvyo37tK3zLRjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k1hI1/btrALUxIj3B/qmtWkRkRxvyo37tK3zLRjK/img.png&quot; data-alt=&quot;Model Study on ResNet50&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k1hI1/btrALUxIj3B/qmtWkRkRxvyo37tK3zLRjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk1hI1%2FbtrALUxIj3B%2FqmtWkRkRxvyo37tK3zLRjK%2Fimg.png&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; alt=&quot;Model Study on ResNet50&quot; loading=&quot;lazy&quot; width=&quot;400&quot; height=&quot;345&quot; data-origin-width=&quot;553&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Model Study on ResNet50&lt;/figcaption&gt;
&lt;/figure&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;\( B \rightarrow B \rightarrow B \rightarrow B \)는 regular bottleneck만 사용한 baseline ResNet50모델이고 \( E / B \)는 regular bottleneck과 efficient bottleneck을 번갈아가며 사용한 것을 의미한다. 또한 &lt;span&gt;&lt;span&gt; \( E \rightarrow E \rightarrow E \rightarrow E \)&lt;/span&gt;&lt;/span&gt;는 오직 efficient bottleneck만 사용한 것을 의미한다. 그결과 \( E / B \)인 hybrid architecture가 baseline 모델과 비슷한 성능을 도출해내면서 latecny 성능 향상을 이뤗음을 보여준다.&amp;nbsp; (모든 모델은 ImageNet dataset으로&amp;nbsp; 90epochs학습함.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.2&amp;nbsp; Efficient ViT Architecture&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;ViT 구조에서 self-attention layer를 대신하여 parameter-free operation을 적용하였다고 합니다&lt;/span&gt;&lt;/b&gt;. 그리고 ViT의 classification token을 사용하지 않고 global average pooling (GAP)를 사용하였는데 이는 classification token은 self-attention layer없이는 사용이 불가하기 때문입니다. 기본적인 ViT말고도 Pooling-based Vision Transformer (PiT)에도 Parameter-free operation을 적용하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. Experiments&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Classification task용 dataset인 ImageNet과 object detection용인 COCO2017 dataset을 사용하여 제안한 efficient architecture의 성능결과를 보여줍니다.&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;먼저 ImagNet에 대한 성능 비교입니다. max는 parameter-free operaion만 사용하는 efficient block만 있는 것이고 hybrid는 regular와 efficient block을 번갈아 사용한것이고 deform_max는 deformable max pool operation만 사용한것이다. ( \(\dagger \)는 training trick을 쓴 결과입니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAeG5p/btrANfVQe4W/JoXRYIUau6lc7PfLft9dRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAeG5p/btrANfVQe4W/JoXRYIUau6lc7PfLft9dRk/img.png&quot; data-alt=&quot;ImageNet results for proposed ResNet and others&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAeG5p/btrANfVQe4W/JoXRYIUau6lc7PfLft9dRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAeG5p%2FbtrANfVQe4W%2FJoXRYIUau6lc7PfLft9dRk%2Fimg.png&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; alt=&quot;ImageNet results for proposed ResNet and others&quot; loading=&quot;lazy&quot; width=&quot;808&quot; height=&quot;405&quot; data-origin-width=&quot;1130&quot; data-origin-height=&quot;566&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ImageNet results for proposed ResNet and others&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyFEYA/btrANgmWYrn/SpAgHKPS2e4VIruKMU0Tfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyFEYA/btrANgmWYrn/SpAgHKPS2e4VIruKMU0Tfk/img.png&quot; data-alt=&quot;ImageNet performance of CNN models&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyFEYA/btrANgmWYrn/SpAgHKPS2e4VIruKMU0Tfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyFEYA%2FbtrANgmWYrn%2FSpAgHKPS2e4VIruKMU0Tfk%2Fimg.png&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; alt=&quot;ImageNet performance of CNN models&quot; loading=&quot;lazy&quot; width=&quot;860&quot; height=&quot;295&quot; data-origin-width=&quot;1213&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ImageNet performance of CNN models&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;363&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B1S5F/btrAM3tZoEW/J77t6ekI8tEZuWAsoD2VD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B1S5F/btrAM3tZoEW/J77t6ekI8tEZuWAsoD2VD0/img.png&quot; data-alt=&quot;ImageNet performance of ViTs.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B1S5F/btrAM3tZoEW/J77t6ekI8tEZuWAsoD2VD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB1S5F%2FbtrAM3tZoEW%2FJ77t6ekI8tEZuWAsoD2VD0%2Fimg.png&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; alt=&quot;ImageNet performance of ViTs.&quot; loading=&quot;lazy&quot; width=&quot;686&quot; height=&quot;264&quot; data-origin-width=&quot;944&quot; data-origin-height=&quot;363&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ImageNet performance of ViTs.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt; COCO dataset에 대한 성능 비교입니다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1196&quot; data-origin-height=&quot;192&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cHPMtd/btrAJKW0oOR/6CJFZBdRDkXKyVAQA3KsuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cHPMtd/btrAJKW0oOR/6CJFZBdRDkXKyVAQA3KsuK/img.png&quot; data-alt=&quot;COCO object detection results&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cHPMtd/btrAJKW0oOR/6CJFZBdRDkXKyVAQA3KsuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcHPMtd%2FbtrAJKW0oOR%2F6CJFZBdRDkXKyVAQA3KsuK%2Fimg.png&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; alt=&quot;COCO object detection results&quot; loading=&quot;lazy&quot; width=&quot;847&quot; height=&quot;136&quot; data-origin-width=&quot;1196&quot; data-origin-height=&quot;192&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;COCO object detection results&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI paper review/Model Compression</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/50</guid>
      <comments>https://da2so.tistory.com/50#entry50comment</comments>
      <pubDate>Thu, 28 Apr 2022 10:01:50 +0900</pubDate>
    </item>
    <item>
      <title>Lite Pose 논문 리뷰</title>
      <link>https://da2so.tistory.com/49</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 CVPR 2022에 accept된 논문인 &lt;a href=&quot;https://tinyml.mit.edu/wp-content/uploads/2022/04/CVPR2022__Lite_Pose.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Lite Pose: Efficient Architecture Design for 2D Human Pose Estimation&lt;/a&gt; 을 리뷰하도록 하겠습니다.&amp;nbsp;&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;해당 논문은 기존 2D Human Pose Estimation task를 수행하는 모델들이 high computational cost를 가진다는 문제점을 해결하고자 합니다. 그래서 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Lite Pose라는 모델을 제안하여 low latency와 small parameter numbers를 가지면서 좋은 성능(mAP)을 얻어냈다&lt;/b&gt;&lt;/span&gt;는 성과를 보여주었습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Introduction&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Lite Pose는 기존의 bottom-up 방식의 architecture인 (Higher)HRNet의 단점을 보완한 모델입니다. 기존 HRNet은 multi-branch 구조를 사용하여 다양한 image scale에 대해 학습할 수 있도록 하였습니다. 이는 mAP성능을 높이는 데 기여하게 됩니다. 하지만 muti-branch인 구조이다 보니 당연히 model의 크기도 커지고 latency도 높아지게 되는 문제점을 가지죠.&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;이를 해결하기 위해 저자들은 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;gradual shrinking을 이용한 single-branch구조를 사용하여 latency와 parameter수를 모두 줄이게 됩니다&lt;/b&gt;&lt;/span&gt;. gradual shrinking은 말그대로 &lt;b&gt;&lt;i&gt;&quot;점차적으로 줄이기&quot;&lt;/i&gt;&lt;/b&gt;을 뜻하며 multi-branch(a)를 아래와 같이 점차 줄여가는 모듈 구조&lt;b&gt;(shrink1, 2, 3)&lt;/b&gt;를 말합니다. 모듈 구조를 줄였는데도도 좋은 성능을 얻었다고 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;litepose1.png&quot; data-origin-width=&quot;1695&quot; data-origin-height=&quot;891&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bWjqAW/btrzItHHfaN/pN728zkCGwdzFNLTZ54hO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bWjqAW/btrzItHHfaN/pN728zkCGwdzFNLTZ54hO1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bWjqAW/btrzItHHfaN/pN728zkCGwdzFNLTZ54hO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbWjqAW%2FbtrzItHHfaN%2FpN728zkCGwdzFNLTZ54hO1%2Fimg.png&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; alt=&quot;Four architecture configurations in gradual shrinking&quot; loading=&quot;lazy&quot; width=&quot;761&quot; height=&quot;400&quot; data-filename=&quot;litepose1.png&quot; data-origin-width=&quot;1695&quot; data-origin-height=&quot;891&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 multi-branch에서는 scale variation에 영향이 거의 없지만 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;single-branch에서는 scale variation prolbem이 있기 때문에 이를 해결하기위해 fusion deconv head와 large kernel conv를 제안&lt;/b&gt;&lt;/span&gt;하게 됩니다. 마지막으로는 &lt;b&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;Neural Architecture Search (NAS)를 통하여 model architecture를 최적화 하여 최종적으로 Lite Pose 모델을 완성&lt;/span&gt;&lt;/b&gt;하게 됩니다. (자세한 설명은 아래의 Method에서 하도록 할게여!!) 그리하여 아래와 같이 LitePose는 기존 pose estimation 모델들보다 좋은 성능과 빠른 latency를 가지게 됩니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;439&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xrcSL/btrzFFPDJoP/jCBOjuqk8jUXSi2orbTO21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xrcSL/btrzFFPDJoP/jCBOjuqk8jUXSi2orbTO21/img.png&quot; data-alt=&quot;Comparison results between LitePose and other pose estimation models&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xrcSL/btrzFFPDJoP/jCBOjuqk8jUXSi2orbTO21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxrcSL%2FbtrzFFPDJoP%2FjCBOjuqk8jUXSi2orbTO21%2Fimg.png&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; alt=&quot;Comparison between LitePose and other pose estimation models&quot; loading=&quot;lazy&quot; width=&quot;414&quot; height=&quot;324&quot; data-origin-width=&quot;560&quot; data-origin-height=&quot;439&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Comparison results between LitePose and other pose estimation models&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Rethinking the Efficient Design Space&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제안하는 LitePose의 상세한 architecture design에 대해 설명드리도록 하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Redundancy in High-Resolution Branches&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맨위의 그림에서 보이듯이 &lt;b&gt;HRNet은 multi-branch 구조를 가집&lt;/b&gt;니다. 구체적으로 각 \(n\) stage마다 \(n\)개의 다른 branch를 가지는데 이는 \(n\)개의 다른 resolution의 input feature를 받기 때문에 다양한 image scale에 대한 학습이 가능하게 되고 이는 mAP성능향상에 큰 도움을 주었죠. 하지만 multi-branch이다 보니 수많은 parameter를 사용하게 되고 이는 Latency를 느리게하는 주범이 됩니다.&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;이러한 multi-branch구조는 edge device에서 사용할 수 없다는 것을 지적하였고 이를 해결하기 위해 &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;gradual shrinking방법을 통해 multi-branch구조가 redundancy를 가진다는 것을 증명&lt;/b&gt;&lt;/span&gt;하였습니다. gradual shrinking방법은 맨위 사진에서 보여드린 3가지(shrink 1,2,3) configuration입니다. 저자들은 3가지 configuration과 HRNet구조의 성능비교를 해보았을 때 아래와 같이 shrinking 될수록 mAP성능이 높아짐을 확인하였습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;298&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kgbqq/btrzI2De6gG/IXxmWCyhe7tq1HXLp7efX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kgbqq/btrzI2De6gG/IXxmWCyhe7tq1HXLp7efX1/img.png&quot; data-alt=&quot;Comparison performances between gradual shrinking and HigherHRNet&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kgbqq/btrzI2De6gG/IXxmWCyhe7tq1HXLp7efX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKgbqq%2FbtrzI2De6gG%2FIXxmWCyhe7tq1HXLp7efX1%2Fimg.png&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; alt=&quot;comparison performance between gradual shrinking and HigherHRNet&quot; loading=&quot;lazy&quot; width=&quot;461&quot; height=&quot;250&quot; data-origin-width=&quot;549&quot; data-origin-height=&quot;298&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Comparison performances between gradual shrinking and HigherHRNet&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결론적으로 single branch에 가까워 질수록 성능은 높아졌다는 것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.1.1 Gradual Shrinking&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Gradual shrinking에 대해 수학적으로 풀어보죠. 기존의 HRNet의 branch와 block은 다음과 같이 정의됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt; \(A_n = [ a_1 , \cdots, a_n] \):&lt;/b&gt; 각 branch에서 사용되는 block 수&lt;/li&gt;
&lt;li&gt;&lt;b&gt;\(A = \{ A_1, A_2, A_3, A_4 \} \):&lt;/b&gt; 전체 multi-branch 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;litepose2.png&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;385&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sbyDx/btrzKtU9t5Z/cpvjLodLaZEHigMT6AKByk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sbyDx/btrzKtU9t5Z/cpvjLodLaZEHigMT6AKByk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sbyDx/btrzKtU9t5Z/cpvjLodLaZEHigMT6AKByk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsbyDx%2FbtrzKtU9t5Z%2FcpvjLodLaZEHigMT6AKByk%2Fimg.png&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; alt=&quot;HigherHRNet architecture&quot; loading=&quot;lazy&quot; width=&quot;453&quot; height=&quot;198&quot; data-filename=&quot;litepose2.png&quot; data-origin-width=&quot;881&quot; data-origin-height=&quot;385&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, gradual shrinking방법으로 정의된 branch와 block은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;b&gt;\( A'_i= [ a'_1, \cdots, a'_i ] \)&lt;/b&gt;&amp;nbsp;:&lt;/b&gt; \( A_i \)로 부터 줄어든(shrinking) block수
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;\( s.t. \forall j \in \{1, \cdots, i\}, a'_j \leq a_j \)&lt;/li&gt;
&lt;li&gt;\( A'_i \leq A_i \)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;\( [C_1 , \cdots , C_m] s.t. C_{i+1} \leq C_i \)&lt;/b&gt; : gradual shrinking의 sequence configurations
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;C1: HigherHRNet&lt;/li&gt;
&lt;li&gt;C2: Shrink1&lt;/li&gt;
&lt;li&gt;C3: Shrink2&lt;/li&gt;
&lt;li&gt;C4: Shrink3&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;litepose3.png&quot; data-origin-width=&quot;1577&quot; data-origin-height=&quot;414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVHNuk/btrzGhVgmeg/s149CzfU5BNkvKb1ECDJs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVHNuk/btrzGhVgmeg/s149CzfU5BNkvKb1ECDJs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVHNuk/btrzGhVgmeg/s149CzfU5BNkvKb1ECDJs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVHNuk%2FbtrzGhVgmeg%2Fs149CzfU5BNkvKb1ECDJs0%2Fimg.png&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; alt=&quot;LitePose architecture&quot; loading=&quot;lazy&quot; width=&quot;769&quot; height=&quot;202&quot; data-filename=&quot;litepose3.png&quot; data-origin-width=&quot;1577&quot; data-origin-height=&quot;414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Fusion Deconv Head: Remove the Redundancy&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;single-branch를 사용하여 기존 multi-branch의 redundancy를 줄였지만 single-branch는 scale variation problem이라는 단점을 가집니다. 이를 해결하기위해 저자들은 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;fusion deconvolutional layers을 제안&lt;/b&gt;&lt;/span&gt;하였습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;466&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dZiX48/btrzKmBxuLG/9QDL1VZBwxNq3k3QL2xqN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dZiX48/btrzKmBxuLG/9QDL1VZBwxNq3k3QL2xqN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dZiX48/btrzKmBxuLG/9QDL1VZBwxNq3k3QL2xqN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdZiX48%2FbtrzKmBxuLG%2F9QDL1VZBwxNq3k3QL2xqN0%2Fimg.png&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; alt=&quot;fusion deconv head&quot; loading=&quot;lazy&quot; width=&quot;564&quot; height=&quot;246&quot; data-origin-width=&quot;1068&quot; data-origin-height=&quot;466&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Fusion deconvolutional layer&lt;/span&gt;는 직접적으로 이전의 Stage들로부터 생성된 low-level high resolution feature를 head layer의 output에 concatenate하는 방법&lt;/b&gt;입니다. 기존의 HR Fusion과 다르게 추가적인 conv연산을 없앳다는 점에서 efficiency를 가집니다. 아래와 같이 Litepose network에서 head layer인 각 deconv layer와 final layer의 output에 이전 stage의 output을 concatenate한 것입니다. 위의 (b)사진에서 보이듯이 기존의 deconv보다 제안한 fusion deconv가 높은 mAP를 도출하는데 큰 기여를 한것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2264&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVuuvW/btrzKt8KKe2/1UFQ2miKSS0HXvdJrJDnCk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVuuvW/btrzKt8KKe2/1UFQ2miKSS0HXvdJrJDnCk/img.png&quot; data-alt=&quot;LitePose Architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVuuvW/btrzKt8KKe2/1UFQ2miKSS0HXvdJrJDnCk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVuuvW%2FbtrzKt8KKe2%2F1UFQ2miKSS0HXvdJrJDnCk%2Fimg.png&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; alt=&quot;LitePose Architecture&quot; loading=&quot;lazy&quot; width=&quot;814&quot; height=&quot;230&quot; data-origin-width=&quot;2264&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;LitePose Architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 Mobile Backbone with Large Kernel Convs&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 LitePose구조에서 알 수 있듯이 &lt;b&gt;backbone은 변형한 MobileNetv2를 사용하였습니다. 변형한 것은 마지막의 down-sampling layer을 제거한 것&lt;/b&gt;이고 해당 layer가 high resolution의 정보를 없애기 때문에 없앳다고 합니다. 또한 특별하게 pose estimation task에서는 기존 image classification과 다르게 kernel size를 7x7로 했을 때 성능이 좋게 나왔다고 하여 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;LitePose에서의 7x7 kernel size의 convolution layer를 사용하였습니다.&lt;/b&gt;&lt;/span&gt;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;488&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bQg4T9/btrzMatQCxz/cTQ9lLE0z2VJDkdEAKhEXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bQg4T9/btrzMatQCxz/cTQ9lLE0z2VJDkdEAKhEXk/img.png&quot; data-alt=&quot;k는 kernel size를 뜻함.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bQg4T9/btrzMatQCxz/cTQ9lLE0z2VJDkdEAKhEXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbQg4T9%2FbtrzMatQCxz%2FcTQ9lLE0z2VJDkdEAKhEXk%2Fimg.png&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; alt=&quot;kernel size in pose estimation&quot; loading=&quot;lazy&quot; width=&quot;549&quot; height=&quot;252&quot; data-origin-width=&quot;1064&quot; data-origin-height=&quot;488&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;k는 kernel size를 뜻함.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Neural Architecture Search (NAS)&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 LitePose모델을 만드는데 있어서 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;최적의 input resolution과 channel size를 찾기 위해 NAS의 한 방법인 Once-for-all을 사용&lt;/b&gt;&lt;/span&gt;하였습니다. (Once-for-all은 저자 중 한명이신 song han님의 NAS논문이고 자세히 알고싶으시면 논문 찾아보시는 것을 추천합니다. 좋은 논문이거든요ㅎㅎ..) 그래서 &lt;b&gt;NAS를 통하여 4개의 LitePose모델: LitePose XS, S, M and L 을 찾아내었습니다.&lt;/b&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;NAS에 사용된 방법내용은 다음과 같습니다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Optimization goal&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 LitePose는 \(K\)개의 layer를 가지며 각 layer가 \( \{ c_k\}^K_{k=1} \)channels 을 가진다고 할 때, NAS의 optimization 목표는 best mAP는 유지하면서 기존보다 채널수를 작게하고 \( \{ c'_k\}^K_{k=1} \) ( \(c'_k \leq c_k\) )와 기존 input resolution 보다 작도록 하는 \( r' &amp;lt; r \) 것입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 One-shot Supernet Training&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAS에서 supernet을 학습할때 one-shot방법을 사용하게 됩니다. 서로 다른 channel number configurations는 weight sharing을 하게 됩니다. 각 training iteration마다 하나의 channel configuration은 uniform sampling으로 선택되도록 하였고 선택된 configuration만 training되도록 합니다. 또한 grouping을 위한 associate embedding를 학습하기 위해 pretrained weight로 supernet의 weight를 초기화하였습니다. (associate embedding은 &quot;Associative Embedding: End-to-End Learning for Joint Detection and Grouping&quot;논문을 참고해주세요)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 Search &amp;amp; Fine-tune&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;supernet에서 최적의 sub-network를 찾기위해 evolutionary algorithm을 사용하게 되고 찾아진 sub-network는 Fine-tuning을 통해 최종 performance를 뽑아내게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Experiment&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1&amp;nbsp; Dataset &amp;amp; Evalution Metrics&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Dataset&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Microsoft COCO:&lt;/b&gt; 20,000 images, 17 keypoints&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CrowdPose:&lt;/b&gt; 20,000 images, 14 keypoints&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Evalution metrics&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;Object Keypoint Similarity (OKS)&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OKS는 가우시안 분포를 따르며 \( -d^2_i \)는 평균,&amp;nbsp; \( 2s^2 k^2_i \)는 분산을 의미함.&lt;/li&gt;
&lt;li&gt;\( d_i \)는 \(i\)-th keypoint에 대해 ground truth와 prediction의 distance &lt;br /&gt;(distance \(d_i \)가 0이면 OKS값이 1이 되므로 가장 최적의 성능을 뜻함)&lt;/li&gt;
&lt;li&gt;\(v_i \)는 ground truth의 visibility flag을 의미하고 \( \delta (v_i &amp;gt; 0) \)는 \( v_i \)가 1인 instance에만 측정하겠다는 것을 의미함.&lt;/li&gt;
&lt;li&gt;\( s\)는 object scale을 의미하게 되는데 object의 크기가 작을수록 작은 distance차이도 크게 작용해야 하도록 하는 변수&lt;/li&gt;
&lt;li&gt;\(&amp;nbsp; k_i \)는 keypoint마다 중요도를 constant(상수)로 표현 (예를 들어 눈,코,입은 조금 틀려도 되지만 허리,팔은 Pose estimation에 중요하므로 조금 틀려도 안됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bshMBa/btrzLHZvpkU/RMu3H53sClSUjoMANfQhW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bshMBa/btrzLHZvpkU/RMu3H53sClSUjoMANfQhW1/img.png&quot; data-alt=&quot;OKS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bshMBa/btrzLHZvpkU/RMu3H53sClSUjoMANfQhW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbshMBa%2FbtrzLHZvpkU%2FRMu3H53sClSUjoMANfQhW1%2Fimg.png&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; alt=&quot;Object Keypoint similarity&quot; loading=&quot;lazy&quot; width=&quot;582&quot; height=&quot;243&quot; data-origin-width=&quot;1490&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;OKS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.2 Experiment Setting&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Data Augmentation&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Random rotation [-30, 30]&lt;/li&gt;
&lt;li&gt;Random scale [0.75, 1.5]&lt;/li&gt;
&lt;li&gt;Random translation [-40, 40]&lt;/li&gt;
&lt;li&gt;Random flip&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Pre-training Details&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Associative Embedding loss없이 heatmap loss(ground truth랑 prediction의 Distance구하는 간단한 식)를 통해서만 supernet을 학습하여 supernet의 weight initialization으로 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Microsoft COCO dataset으로 100 epochs 학습&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Supernet Training Setting&amp;nbsp;&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Training SuperNet for LitePose-L/M/S/XS on CrowdPose dataset
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;LitePose-L/M/S에 대해 800 epochs, batch size 32, lr = 0.001 사용&lt;/li&gt;
&lt;li&gt;LitePose-XS에 대해 2400 epochs, batch size 128, lr = 0.004 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;각 training step마다, 모델 구조는 uniform sampling을 통해 선택&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Fine-tuning Setting&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;NAS로 찾아진 최적의 sub-network에 대해 아래와 같이 설정
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CrowdPose dataset에 대해 200 epochs, batch size 32, lr = 1e-3 (각 50, 180 epoch에서 1e-4, 1e-5사용)&lt;/li&gt;
&lt;li&gt;COCO dataset에 대해 500 epochs, batch size 32, lr = 1e-3 (각 350, 480 epoch에 1e-4, 1e-5 사용)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Search Details&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;NAS는 CrowdPose dataset에 사용하였으며
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;LitePose-L/M/S Supernet 학습시 input resolution search space는 [512, 448], channel width ratio는 [1.0, 0.75, 0.5]로 설정&lt;/li&gt;
&lt;li&gt;LitePose-XS Supernet 학습시 input resolution search space는 [512, 448, 384, 320, 256], channel width ratio는 [1.0, 0.75, 0.5, 0.25]로 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.3 Main Result&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연히 기존의 pose estimation model들보다 뛰어난 mAP와 낮은 Latency를 갖게되었음을 보여준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;266&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Lzu1y/btrzKntPgDS/GafZKsb4FIxcztglMCrKqK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Lzu1y/btrzKntPgDS/GafZKsb4FIxcztglMCrKqK/img.png&quot; data-alt=&quot;On CrowdPose dataset, Comparison performance between LitePose and other pose estimation models&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Lzu1y/btrzKntPgDS/GafZKsb4FIxcztglMCrKqK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FLzu1y%2FbtrzKntPgDS%2FGafZKsb4FIxcztglMCrKqK%2Fimg.png&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; alt=&quot;Litepose performance on device&quot; loading=&quot;lazy&quot; width=&quot;744&quot; height=&quot;205&quot; data-origin-width=&quot;966&quot; data-origin-height=&quot;266&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;On CrowdPose dataset, Comparison performance between LitePose and other pose estimation models&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;961&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgi59y/btrzPhlGoYP/Kw5SSkx7XwQ1esKSqyecuK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgi59y/btrzPhlGoYP/Kw5SSkx7XwQ1esKSqyecuK/img.png&quot; data-alt=&quot;Results on COCO val/test-dev set&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgi59y/btrzPhlGoYP/Kw5SSkx7XwQ1esKSqyecuK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbgi59y%2FbtrzPhlGoYP%2FKw5SSkx7XwQ1esKSqyecuK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;796&quot; height=&quot;224&quot; data-origin-width=&quot;961&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Results on COCO val/test-dev set&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI paper review/Mobile-friendly</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/49</guid>
      <comments>https://da2so.tistory.com/49#entry49comment</comments>
      <pubDate>Mon, 18 Apr 2022 22:14:01 +0900</pubDate>
    </item>
    <item>
      <title>TensorFlow.js (4) YOLOv5 Live demo</title>
      <link>https://da2so.tistory.com/48</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://github.com/ultralytics/yolov5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;YOLOv5&lt;/a&gt;&lt;/b&gt;로 실시간으로 detection하는 방법을 공유해보도록 하겠습니다. YOLOv5는 ultralytics회사에서 주도적으로 개발하는 object detection model입니다. detection 성능은 현재 2022년까지 최상의 성능을 내고있습니다. 오늘은 YOLOv5모델 중 YOLOv5n를 사용해 live demo를 진행해보도록 하겠습니다.&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;&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;해당 블로그에서 실시간으로 demo가 가능하므로 카메라를 요청할수 있어요! 카메라로 어떤 정보나 해킹은 없으니 안심하고 사용하세요! ㅠㅠ&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. YOLOv5n TensorFlow.js 변환&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YOLOv5에서는 다음 명령어로 TensorFlow.js모델로 변환하는 코드를 간단하게 제공하고 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1649685437705&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone https://github.com/ultralytics/yolov5.git
python export.py --weights yolov5n.pt --include tfjs&lt;/code&gt;&lt;/pre&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-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;246&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ltXS7/btry6EcCTEm/eEsPbEGGXWlsLzBSAyoeUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ltXS7/btry6EcCTEm/eEsPbEGGXWlsLzBSAyoeUK/img.png&quot; data-alt=&quot;yolov5n tensorflow.js&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ltXS7/btry6EcCTEm/eEsPbEGGXWlsLzBSAyoeUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FltXS7%2Fbtry6EcCTEm%2FeEsPbEGGXWlsLzBSAyoeUK%2Fimg.png&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; alt=&quot;yolov5n fjs model&quot; loading=&quot;lazy&quot; width=&quot;371&quot; height=&quot;123&quot; data-origin-width=&quot;742&quot; data-origin-height=&quot;246&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;yolov5n tensorflow.js&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;.bin파일들은 모델의 weight를 저장하는 것이며 model.json은 모델의 구조를 담고 있는 파일입니다.  &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변환 과정에 대해 조금 설명하겠습니다. 이후에 tensorflow.js코드에 필요한 정보라서요! export.py 코드에서는 yolov5n을 다음과 같은 변환을 거칩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;PyTorch -&amp;gt; TensorFlow -&amp;gt; TensorFlow.js&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 TensorFlow로 변환될 때 agnosticNMS를 추가해주었기 때문에 output의 형태가 &lt;b&gt;[&lt;span&gt;&lt;span data-hydro-click=&quot;{&amp;quot;event_type&amp;quot;:&amp;quot;code_navigation.click_on_symbol&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;action&amp;quot;:&amp;quot;click_on_symbol&amp;quot;,&amp;quot;repository_id&amp;quot;:264818686,&amp;quot;ref&amp;quot;:&amp;quot;fa569cdae52dfd3074561129c3a5185bded60b16&amp;quot;,&amp;quot;language&amp;quot;:&amp;quot;Python&amp;quot;,&amp;quot;backend&amp;quot;:&amp;quot;ALEPH_PRECISE&amp;quot;,&amp;quot;code_nav_context&amp;quot;:&amp;quot;BLOB_VIEW&amp;quot;,&amp;quot;retry_backend&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;originating_url&amp;quot;:&amp;quot;https://github.com/ultralytics/yolov5/find-definition?q=padded_boxes&amp;amp;blob_path=models%2Ftf.py&amp;amp;ref=fa569cdae52dfd3074561129c3a5185bded60b16&amp;amp;language=Python&amp;amp;row=440&amp;amp;col=15&amp;amp;code_nav_context=BLOB_VIEW&amp;quot;,&amp;quot;user_id&amp;quot;:47438617}}&quot; data-hydro-click-hmac=&quot;6f2eaf2447b27e0f89299b62364a25e589a91f10720c92e08e0740b4c92de177&quot;&gt;boxes&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span data-hydro-click=&quot;{&amp;quot;event_type&amp;quot;:&amp;quot;code_navigation.click_on_symbol&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;action&amp;quot;:&amp;quot;click_on_symbol&amp;quot;,&amp;quot;repository_id&amp;quot;:264818686,&amp;quot;ref&amp;quot;:&amp;quot;fa569cdae52dfd3074561129c3a5185bded60b16&amp;quot;,&amp;quot;language&amp;quot;:&amp;quot;Python&amp;quot;,&amp;quot;backend&amp;quot;:&amp;quot;ALEPH_PRECISE&amp;quot;,&amp;quot;code_nav_context&amp;quot;:&amp;quot;BLOB_VIEW&amp;quot;,&amp;quot;retry_backend&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;originating_url&amp;quot;:&amp;quot;https://github.com/ultralytics/yolov5/find-definition?q=padded_scores&amp;amp;blob_path=models%2Ftf.py&amp;amp;ref=fa569cdae52dfd3074561129c3a5185bded60b16&amp;amp;language=Python&amp;amp;row=440&amp;amp;col=29&amp;amp;code_nav_context=BLOB_VIEW&amp;quot;,&amp;quot;user_id&amp;quot;:47438617}}&quot; data-hydro-click-hmac=&quot;6bc1dab8725c55206610d92d73c063ecad0bbdd16af2554d7ba3a57aef33e479&quot;&gt;scores&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span data-hydro-click=&quot;{&amp;quot;event_type&amp;quot;:&amp;quot;code_navigation.click_on_symbol&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;action&amp;quot;:&amp;quot;click_on_symbol&amp;quot;,&amp;quot;repository_id&amp;quot;:264818686,&amp;quot;ref&amp;quot;:&amp;quot;fa569cdae52dfd3074561129c3a5185bded60b16&amp;quot;,&amp;quot;language&amp;quot;:&amp;quot;Python&amp;quot;,&amp;quot;backend&amp;quot;:&amp;quot;ALEPH_PRECISE&amp;quot;,&amp;quot;code_nav_context&amp;quot;:&amp;quot;BLOB_VIEW&amp;quot;,&amp;quot;retry_backend&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;originating_url&amp;quot;:&amp;quot;https://github.com/ultralytics/yolov5/find-definition?q=padded_classes&amp;amp;blob_path=models%2Ftf.py&amp;amp;ref=fa569cdae52dfd3074561129c3a5185bded60b16&amp;amp;language=Python&amp;amp;row=440&amp;amp;col=44&amp;amp;code_nav_context=BLOB_VIEW&amp;quot;,&amp;quot;user_id&amp;quot;:47438617}}&quot; data-hydro-click-hmac=&quot;5deac1a9b8bf7b4cfbbc6a155d0618761c5eaba3e0db44d9c532e638d51a821a&quot;&gt;classes&lt;/span&gt;&lt;/span&gt;&lt;span&gt;,&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;/b&gt;&lt;span&gt;&lt;span data-hydro-click=&quot;{&amp;quot;event_type&amp;quot;:&amp;quot;code_navigation.click_on_symbol&amp;quot;,&amp;quot;payload&amp;quot;:{&amp;quot;action&amp;quot;:&amp;quot;click_on_symbol&amp;quot;,&amp;quot;repository_id&amp;quot;:264818686,&amp;quot;ref&amp;quot;:&amp;quot;fa569cdae52dfd3074561129c3a5185bded60b16&amp;quot;,&amp;quot;language&amp;quot;:&amp;quot;Python&amp;quot;,&amp;quot;backend&amp;quot;:&amp;quot;ALEPH_PRECISE&amp;quot;,&amp;quot;code_nav_context&amp;quot;:&amp;quot;BLOB_VIEW&amp;quot;,&amp;quot;retry_backend&amp;quot;:&amp;quot;&amp;quot;,&amp;quot;originating_url&amp;quot;:&amp;quot;https://github.com/ultralytics/yolov5/find-definition?q=valid_detections&amp;amp;blob_path=models%2Ftf.py&amp;amp;ref=fa569cdae52dfd3074561129c3a5185bded60b16&amp;amp;language=Python&amp;amp;row=440&amp;amp;col=60&amp;amp;code_nav_context=BLOB_VIEW&amp;quot;,&amp;quot;user_id&amp;quot;:47438617}}&quot; data-hydro-click-hmac=&quot;804ed29183f333a6b1ca1e0463fc8ce6495103a3af2a7360b84e66e0762c4b64&quot;&gt;&lt;b&gt;valid_detections]&lt;/b&gt;가 됨을 아셔야하는데요. 그 이유는 TensorFlow.js로 변환하고 난 뒤 tensorflow.js 코드에서 yolov5n모델의 결과(output)를 웹상에 그려줘야하기 때문입니다!&amp;nbsp; (output형태가 실제로 어떻게 변화하는 지 코드로 보고싶으면 여기로 -&amp;gt; &lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://github.com/ultralytics/yolov5/blob/fa569cdae52dfd3074561129c3a5185bded60b16/models/tf.py#L441&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;TensorFlow변환시 output변화 &lt;/a&gt;)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;output의 의미는 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;boxes&lt;/b&gt;: detect된 물체의 bounding box position (x1, y1, x2, y2)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;x1: bounding box의 왼쪽 x좌표&lt;/li&gt;
&lt;li&gt;y1: bounding box의 위 y좌표&lt;/li&gt;
&lt;li&gt;x2: bounding box의 오른쪽 x 좌표&lt;/li&gt;
&lt;li&gt;y2: bounding box의 아래 y좌표&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;scores&lt;/b&gt;: detect된 물체의 confidence score&lt;/li&gt;
&lt;li&gt;&lt;b&gt;classes&lt;/b&gt;: detect된 물체의 class index&lt;/li&gt;
&lt;li&gt;&lt;b&gt;valid_detections&lt;/b&gt;: nms를 통한 최종 detect된 물체 총 개수&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. YOLOv5n Live demo&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전글들에서 live demo를 해보았기때문에 해당 글에서는 YOLOv5모델을 돌리기 위한 중요한 코드만 설명드릴게요. 티스토리블로그에서 돌릴 수 있는 전체 코드는 다음 github &lt;a href=&quot;https://github.com/da2so/tfjs_tutorial&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;tfjs_tutorial&lt;/a&gt; 에서 찾아 보시면 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 HTML skeleton&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1649687259550&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;body&amp;gt;
  &amp;lt;div id=&quot;main&quot;&amp;gt;
    &amp;lt;div class=&quot;container&quot;&amp;gt;
      &amp;lt;div class=&quot;canvas-wrapper&quot;&amp;gt;
        &amp;lt;canvas id=&quot;output&quot;&amp;gt;&amp;lt;/canvas&amp;gt;
        &amp;lt;video id=&quot;video&quot; playsinline style=&quot;
          -webkit-transform: scaleX(-1);
          transform: scaleX(-1);
          visibility: hidden;
          width: auto;
          height: auto;
          &quot;&amp;gt;
        &amp;lt;/video&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;html코드에 대한 내용입니다. &lt;code&gt;video&lt;/code&gt; element를 통해 실시간으로 camera를 읽어오는 것이고 &lt;code&gt;canvas&lt;/code&gt; element를 통해 model detection한 결과를 camera화면 위에 그릴 것입니다. 그리고 아래의 &lt;code&gt;script&lt;/code&gt; element를 보면 tfjs를 cdn을 통해 import하여 tfjs의 모듈들을 사용함을 정의하였습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2&amp;nbsp; javascript 기본구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;javascript 큰 구조는 다음과 같습니다. 아래의 함수들을 통해 live로 yolov5 detection을 진행할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1649688435647&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function app() {
  camera = await Camera.setupCamera(); //camera setup
  detector = await createDetector(); //load yolov5n model
  renderPrediction(); //draw detection result into canvas
};
app();&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 Load Model&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터 javascript코드를 작성할것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1649687042826&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const yolov5n_weight = &quot;https://raw.githubusercontent.com/da22so/tfjs_models/main/yolov5n_web_model/model.json&quot;

async function createDetector() {
  return tf.loadGraphModel(yolov5n_weight);
}​&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만들어진 yolovn5의 model.json과 .bin파일들은  위의 github url에 올려놓은 것이며 &lt;code&gt;tf.loadGraphModel&lt;/code&gt;을 통해 model을 load하게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.4 Model input shape에 맞게 stream image 변환&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실시간으로 들어오는 image stream을 yolov5n모델의 input shape에 맞춰 주기위해 다음과 같은 코드를 추가하였습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1649688111659&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  let [modelWidth, modelHeight] = detector.inputs[0].shape.slice(1, 3); //get model's input shape
  const input = tf.tidy(() =&amp;gt; {
    return tf.image.resizeBilinear(tf.browser.fromPixels(camera.video), [modelWidth, modelHeight])
      .div(255.0).expandDims(0);
      // 실시간으로 들어오는 camera.video를 model input shape에 맞게 변환
      // normalize를 위해 255로 나눠줌
      // 3차원을 4차원으로 변환
  });&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.5 object detection from yolov5n&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 코드로 detection하게 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1649688853896&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;      detect_res = await detector.executeAsync(input,); //detection!!&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;&amp;nbsp;2.6&amp;nbsp; detection result를 canvas에 그리기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 &lt;code&gt;detect_res&lt;/code&gt;의 값이 아래 &lt;code&gt;res&lt;/code&gt;와 동일한데요. 위에서 tensorflow로 변환시에 output shape이&lt;b&gt; [boxes, scores, classes, valid_detections]&lt;/b&gt;이었음을 기억하면 아래의 코드가 이해되실거예여!&lt;/p&gt;
&lt;pre id=&quot;code_1649689436689&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const [boxes, scores, classes, valid_detections] = res;
    const boxes_data = boxes.dataSync();
    const scores_data = scores.dataSync();
    const classes_data = classes.dataSync();
    const valid_detections_data = valid_detections.dataSync()[0];
    tf.dispose(res);
    var i;
    for (i = 0; i &amp;lt; valid_detections_data; ++i) { // valid_detections수만큼 물체 인식
      let [x1, y1, x2, y2] = boxes_data.slice(i * 4, (i + 1) * 4); //slicing을 통한 한 물체의 bounding box좌표 가져오기
	  ...// 생략
      const width = x2 - x1; 
      const height = y2 - y1;
      const klass = coco_names[classes_data[i]]; // class index를 coco class이름으로 매칭
      const score = scores_data[i].toFixed(2); 

      // bounding box 그리기
      this.ctx.strokeStyle = &quot;#00FFFF&quot;;
      this.ctx.lineWidth = 4;
      this.ctx.strokeRect(x1, y1, width, height);

      // label과 confidence score 그리기
      this.ctx.fillStyle = &quot;#00FFFF&quot;;
      const textWidth = this.ctx.measureText(klass + &quot;:&quot; + score).width;
      const textHeight = parseInt(font, 10); // base 10
      this.ctx.fillRect(x1, y1, textWidth + 4, textHeight + 4);
    }
	...//생략
    }&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. YOLOv5 Live demo 결과&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;webcam사용을 승낙하셧다면 아래에 detection결과가 짜짠!!!!!&amp;nbsp;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;div id=&quot;stats&quot;&gt;
&lt;div id=&quot;main&quot;&gt;
    &lt;div class=&quot;container&quot;&gt;
      &lt;div class=&quot;canvas-wrapper&quot;&gt;
        &lt;canvas id=&quot;output&quot;&gt;&lt;/canvas&gt;
        &lt;video id=&quot;video&quot; playsinline style=&quot;
          -webkit-transform: scaleX(-1);
          transform: scaleX(-1);
          visibility: hidden;
          width: auto;
          height: auto;
          &quot;&gt;
        &lt;/video&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.7.0&quot;&gt;&lt;/script&gt;
&lt;script&gt;
 
const coco_names = ['person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', 'truck', 'boat', 'traffic light',
  'fire hydrant', 'stop sign', 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow',
  'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', 'tie', 'suitcase', 'frisbee',
  'skis', 'snowboard', 'sports ball', 'kite', 'baseball bat', 'baseball glove', 'skateboard', 'surfboard',
  'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', 'bowl', 'banana', 'apple',
  'sandwich', 'orange', 'broccoli', 'carrot', 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch',
  'potted plant', 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', 'keyboard', 'cell phone',
  'microwave', 'oven', 'toaster', 'sink', 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear',
  'hair drier', 'toothbrush']

function isiOS() {
  return /iPhone|iPad|iPod/i.test(navigator.userAgent);
}

function isAndroid() {
  return /Android/i.test(navigator.userAgent);
}

function isMobile() {
  return isAndroid() || isiOS();
}

class Camera {
  constructor() {
    this.video = document.getElementById('video');
    this.canvas = document.getElementById('output');
    this.ctx = this.canvas.getContext('2d');
  }

  /**
   * Initiate a Camera instance and wait for the camera stream to be ready.
   */
  static async setupCamera() {
    if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
      throw new Error(
        'Browser API navigator.mediaDevices.getUserMedia not available');
    }

    const $size = { width: 640, height: 480 };
    const $m_size = { width: 360, height: 270 };
    const videoConfig = {
      'audio': false,
      'video': {
        facingMode: 'user',
        // Only setting the video to a specified size for large screen, on
        // mobile devices accept the default size.
        width: isMobile() ? $m_size.width : $size.width,
        height: isMobile() ? $m_size.height : $size.height,
      }
    };

    const stream = await navigator.mediaDevices.getUserMedia(videoConfig);

    const camera = new Camera();
    camera.video.srcObject = stream;

    await new Promise((resolve) =&gt; {
      camera.video.onloadedmetadata = () =&gt; {
        resolve(video);
      };
    });

    camera.video.play();

    const videoWidth = camera.video.videoWidth;
    const videoHeight = camera.video.videoHeight;
    // Must set below two lines, otherwise video element doesn't show.
    camera.video.width = videoWidth;
    camera.video.height = videoHeight;

    camera.canvas.width = videoWidth;
    camera.canvas.height = videoHeight;
    const canvasContainer = document.querySelector('.canvas-wrapper');
    canvasContainer.style = `width: ${videoWidth}px; height: ${videoHeight}px`;

    // Because the image from camera is mirrored, need to flip horizontally.
    //camera.ctx.translate(camera.video.videoWidth, 0);
    //camera.ctx.scale(-1, 1);

    return camera;
  }

  drawCtx() {
    this.ctx.drawImage(
      this.video, 0, 0, this.video.videoWidth, this.video.videoHeight);
  }

  clearCtx() {
    this.ctx.clearRect(0, 0, this.video.videoWidth, this.video.videoHeight);
  }


  drawResult(res) {
    const font = &quot;16px sans-serif&quot;;
    this.ctx.font = font;
    this.ctx.textBaseline = &quot;top&quot;;

    const [boxes, scores, classes, valid_detections] = res;
    const boxes_data = boxes.dataSync();
    const scores_data = scores.dataSync();
    const classes_data = classes.dataSync();
    const valid_detections_data = valid_detections.dataSync()[0];
	
    tf.dispose(res);

    var i;
    for (i = 0; i &lt; valid_detections_data; ++i) {
      let [x1, y1, x2, y2] = boxes_data.slice(i * 4, (i + 1) * 4);
      x1 *= this.canvas.width;
      x2 *= this.canvas.width;
      y1 *= this.canvas.height;
      y2 *= this.canvas.height;
      const width = x2 - x1;
      const height = y2 - y1;
      const klass = coco_names[classes_data[i]];
      const score = scores_data[i].toFixed(2);

      // Draw the bounding box.
      this.ctx.strokeStyle = &quot;#00FFFF&quot;;
      this.ctx.lineWidth = 4;
      this.ctx.strokeRect(x1, y1, width, height);

      // Draw the label background.
      this.ctx.fillStyle = &quot;#00FFFF&quot;;
      const textWidth = this.ctx.measureText(klass + &quot;:&quot; + score).width;
      const textHeight = parseInt(font, 10); // base 10
      this.ctx.fillRect(x1, y1, textWidth + 4, textHeight + 4);

    }
    for (i = 0; i &lt; valid_detections_data; ++i) {
      let [x1, y1, ,] = boxes_data.slice(i * 4, (i + 1) * 4);
      x1 *= this.canvas.width;
      y1 *= this.canvas.height;
      const klass = coco_names[classes_data[i]];
      const score = scores_data[i].toFixed(2);

      // Draw the text last to ensure it's on top.
      this.ctx.fillStyle = &quot;#000000&quot;;
      this.ctx.fillText(klass + &quot;:&quot; + score, x1, y1);

    }

  }

}


let detector, camera, stats;
let startInferenceTime, numInferences = 0;
let inferenceTimeSum = 0, lastPanelUpdate = 0;
let rafId;

const yolov5n_weight = &quot;https://raw.githubusercontent.com/da22so/tfjs_models/main/yolov5n_web_model/model.json&quot;

async function createDetector() {
  return tf.loadGraphModel(yolov5n_weight);
}



async function renderResult() {
  if (camera.video.readyState &lt; 2) {
    await new Promise((resolve) =&gt; {
      camera.video.onloadeddata = () =&gt; {
        resolve(video);
      };
    });
  }

  let detect_res = null;
  //const webcam = await tf.data.webcam(camera.video, { resizeWidth: 640, resizeHeight: 640 });
  //const img = await webcam.capture();
  let [modelWidth, modelHeight] = detector.inputs[0].shape.slice(1, 3);
  const input = tf.tidy(() =&gt; {
    return tf.image.resizeBilinear(tf.browser.fromPixels(camera.video), [modelWidth, modelHeight])
      .div(255.0).expandDims(0);
  });
  // Detector can be null if initialization failed (for example when loading
  // from a URL that does not exist).
  if (detector != null) {

    // Detectors can throw errors, for example when using custom URLs that
    // contain a model that doesn't provide the expected output.
    try {
      detect_res = await detector.executeAsync(input); 
    } catch (error) {
      console.log(error);
      // detector.dispose();
      // detector = null;
      // alert(error);
    }

  }
  camera.drawCtx();
  if (detect_res != null){
  	camera.drawResult(detect_res);
  }
  tf.dispose(input);
}

async function renderPrediction() {
  await renderResult();

  rafId = requestAnimationFrame(renderPrediction);
};

async function app() {

  camera = await Camera.setupCamera();

  detector = await createDetector();

  renderPrediction();
};

app();
&lt;/script&gt;
&lt;/div&gt;
&lt;div id=&quot;main&quot;&gt;
&lt;div class=&quot;container&quot;&gt;
&lt;div class=&quot;canvas-wrapper&quot;&gt;&lt;video id=&quot;video&quot; style=&quot;-webkit-transform: scaleX(-1); transform: scaleX(-1); visibility: hidden; width: auto; height: auto;&quot;&gt;
        &lt;/video&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/TensorFlow</category>
      <category>TensorFlow.js</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/48</guid>
      <comments>https://da2so.tistory.com/48#entry48comment</comments>
      <pubDate>Mon, 11 Apr 2022 23:30:46 +0900</pubDate>
    </item>
    <item>
      <title>TensorFlow.js (3) TensorFlow.js 변환</title>
      <link>https://da2so.tistory.com/47</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;tf saved model(.pb)을 TensorFlow.js model(.json)으로 변환&lt;/b&gt;&lt;/span&gt;시키는 것을 목적으로 합니다. 2021년 google에서 나온 Efficientnetv2을 대상으로 TensorFlow.js로 변환하고 웹사이트에서 Efficientetv2으로 classification까지 해보죠! (만약 Efficientnetv2에 대해 알고싶다면 &lt;a href=&quot;https://da2so.tistory.com/45&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;EfficientNetv2 논문 리뷰&lt;/a&gt; 참고해주세요~)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;0.&amp;nbsp; keras model를 tf saved model로 변환&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TensorFlow.js 변환 하기 전에&amp;nbsp; EfficientNetv2는 keras model로 제공하고 있기 때문에 tf saved model로 변환부터 해보죠.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 github을 clone하여 필요한 lib들을 설치하고 save_to_pb.py파일을 실행시켜 efficientnetv2-b0를 tf saved model로 변환해봅시다. (save_to_pb.py는 제가 efficientnetv2-b0모델을 tf saved model로 변경하기 위해 만든 코드입니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1648960603424&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;git clone https://github.com/da2so/efficientnetv2.git
cd efficientnetv2
pip install -r requirements.txt

python save_to_pb.py&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어가 모두 정상적으로 동작했다면 다음과 같이 model(.pb)와 폴더가 생성되어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/urKyY/btryiGBwa62/ZqQPiNJLMjKX6KDmvIVpd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/urKyY/btryiGBwa62/ZqQPiNJLMjKX6KDmvIVpd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/urKyY/btryiGBwa62/ZqQPiNJLMjKX6KDmvIVpd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FurKyY%2FbtryiGBwa62%2FZqQPiNJLMjKX6KDmvIVpd1%2Fimg.png&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; alt=&quot;tf_saved_model&quot; loading=&quot;lazy&quot; width=&quot;405&quot; height=&quot;181&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;save_to_pb.py의 (중요 부분) 코드는 다음과 같습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1648962928024&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def main(_) -&amp;gt; None:
    model = build_tf2_model() #build efficientnetv2-b0 model 
    input = tf.keras.Input(shape=(224,224,3), batch_size=1) # input shape: (1x3x224x224)
    
    keras_model = tf.keras.Model(inputs=[input], outputs=tf.nn.softmax(model.call(input, training=False))) #keras model
    keras_model.save('./efficientnetv2-b0_saved_model', save_format='tf') #save to tf saved model&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;efficientnetv2-b0을 keras model로 만들 때 softmax부분을 추가해주었고 save함수을 통해 tf saved model 형태로 저장하였습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. TensorFlow.js model로 변환&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TensorFlow.js로 변환하는 방법은 아주 간단합니다. 먼저 tensorflowjs를 설치합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1648963764808&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;pip install tensorflowjs&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 tf saved model을 &lt;code&gt;tensorflowjs_converter&lt;/code&gt;명령어를 통해 TensorFlow.js 모델로 변환해봅시다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648964489149&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tensorflowjs_converter --input_format=tf_saved_model efficientnetv2-b0_saved_model  efficientnetv2-b0_web_model&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;--input_format:&lt;/b&gt;&lt;/span&gt; 입력 모델 형식
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;tf saved model -&amp;gt; tf_saved_model (제가 사용한 옵션)&lt;/li&gt;
&lt;li&gt;keras model(.h5) -&amp;gt; keras&lt;/li&gt;
&lt;li&gt;frozen model -&amp;gt; tf_frozen_model&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, AppleSDGothicNeo-Regular, 'Malgun Gothic', '맑은 고딕', dotum, 돋움, sans-serif; letter-spacing: 0px;&quot;&gt;옵션에 대한 설정이 끝났으면 다음으로는 source_model인 efficientnetv2-b0_saved_model을 설정하고 마지막은 TensorFlow.js파일들이 &lt;b&gt;저장될 디렉토리(efficientnetv2-b0_web_model)&lt;/b&gt;을 설정합니다. 위의 명령어가 정상적으로 작동했다면 다음과 같이 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;모델의 구조를 담는 model.json&lt;/b&gt;&lt;/span&gt;과 &lt;b&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;weight를 담고 있는 bin파일들&lt;/span&gt;&lt;/b&gt;로 저장됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/n9TBj/btryiG2yajK/KfWpkPZofZk9VkKy2AuFm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/n9TBj/btryiG2yajK/KfWpkPZofZk9VkKy2AuFm1/img.png&quot; data-alt=&quot;Efficientnetv2-b0_web_model&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/n9TBj/btryiG2yajK/KfWpkPZofZk9VkKy2AuFm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fn9TBj%2FbtryiG2yajK%2FKfWpkPZofZk9VkKy2AuFm1%2Fimg.png&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; alt=&quot;Efficientnetv2-b0_web_model&quot; loading=&quot;lazy&quot; width=&quot;283&quot; height=&quot;212&quot; data-origin-width=&quot;478&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Efficientnetv2-b0_web_model&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. EfficientNetv2 웹사이트에 deploy&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EfficientNetv2를 TensorFlow.js 형태로 만들었으니 실제 웹사이트에서 inference가 잘 작동하는 지 확인해 봐야겠죠??&amp;nbsp; 저는 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;javascript기반 react를 기반으로 코딩하였습니다. &lt;/span&gt;직접 자신의 웹사이트에서 작동 확인하시려면 제 github을 clone: &lt;code&gt;git clone https://github.com/da2so/tfjs-efficientnetv2.git&lt;/code&gt; 하셔서 &amp;nbsp;사용하시면됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(npm사용해서 localhost에서 실습하시거나 github page deploy사용하시면 됩니다.)&amp;nbsp;&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;TensorFlow.js 모델을 react코드에서 어떻게 load하고 inference하는 지 중요한 부분만 골라서 알려드리겠습니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1648966061625&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const weights = 'https://raw.githubusercontent.com/da22so/tfjs_models/main/efficientnetv2-b0_web_model/model.json';

... //생략
class App extends React.Component {
  state = {
    model: null,
	... //생략
  };
  componentDidMount() {
    tf.loadGraphModel(weights).then(model =&amp;gt; { //Efficientnetv2-b0 모델 load
      this.setState({
        model: model
      });
    });
  }
  this.state.model.executeAsync(input).then(res =&amp;gt; { // classification execute! 
  ... //생략
  const pred = res;
  const pred_data = pred.dataSync(); // classification compelete done!&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;tf.loadGraphModel:&lt;/b&gt;&lt;/span&gt; TensorFlow.js모델을 Load하는 함수
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;.json 확장자 파일을 입력으로 받으며 해당 json파일은 위에서 만든 model.json이랑 같음&lt;/li&gt;
&lt;li&gt;url을 통해서만 TensorFlow.js model을 load하는 함수임&lt;/li&gt;
&lt;li&gt;load된 모델은 this.state.model에 할당&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;this.state.model.executeAsync(input)&lt;/b&gt;&lt;/span&gt;: model의 inference을 execute하는 함수
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;input은 입력 이미지(1x3x224x224)를 말함 (자세한거는 코드에서 확인해주세요!)&lt;/li&gt;
&lt;li&gt;classification결과는 res에 할당되지만 javascript 특성상 비동기적이므로 classification이 execute되고 complete되면 pred_data에 할당함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서&amp;nbsp;&lt;a href=&quot;https://da2so.github.io/tfjs-efficientnetv2/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;tfjs-efficientnetv2 실습&lt;/a&gt; &amp;nbsp;EfficientNetv2-b0을 실제로 사용해보실 수 있게 해 놓았으니 이미지 업로드해보세요~~ 저는 저희 집 고양이인 코넛이 사진을 넣어 classification해보았습니다!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;conut.png&quot; data-origin-width=&quot;1691&quot; data-origin-height=&quot;791&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b09809/btryhOGyemd/rsoa6J5XCqnRHw8DxUUE3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b09809/btryhOGyemd/rsoa6J5XCqnRHw8DxUUE3K/img.png&quot; data-alt=&quot;Efficientnetv2-b0 example&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b09809/btryhOGyemd/rsoa6J5XCqnRHw8DxUUE3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb09809%2FbtryhOGyemd%2Frsoa6J5XCqnRHw8DxUUE3K%2Fimg.png&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; alt=&quot;Efficientnetv2-b0 example&quot; loading=&quot;lazy&quot; width=&quot;805&quot; height=&quot;377&quot; data-filename=&quot;conut.png&quot; data-origin-width=&quot;1691&quot; data-origin-height=&quot;791&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Efficientnetv2-b0 example&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/TensorFlow</category>
      <category>TensorFlow.js</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/47</guid>
      <comments>https://da2so.tistory.com/47#entry47comment</comments>
      <pubDate>Sun, 3 Apr 2022 15:26:15 +0900</pubDate>
    </item>
    <item>
      <title>MobileViT 논문 리뷰</title>
      <link>https://da2so.tistory.com/46</link>
      <description>&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;ICML 2022에 accept된 Apple직원분들의 논문인 &lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://openreview.net/pdf?id=vh-0sUt8HlG&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;MOBILEVIT: LIGHT-WEIGHT, GENERAL-PURPOSE,&lt;/span&gt;&lt;/b&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://openreview.net/pdf?id=vh-0sUt8HlG&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;AND MOBILE-FRIENDLY VISION TRANSFORMER&lt;/span&gt; &lt;/b&gt;&lt;/a&gt;을 리뷰해보겠습니다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;1. Introduction &lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;MobileViT는 mobile과 같은 하드웨어의 자원이 제한된 곳에서 사용할 수 있도록 만든 작고(Light-weight) 빠른(low-latency) ViT(Visual Transformer) 모델&lt;/b&gt;&lt;/span&gt;입니다. 위와 같은 성능을 도출하기위해 MobileViT는 CNN과 ViT의 장점을 결합하였다고 합니다.&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;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CNN의 장점&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;spatial(local) inductive bias&lt;/li&gt;
&lt;li&gt;data augmentation에 덜 민감&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;ViT의 장점&amp;nbsp;&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;input-adaptive weighting&lt;/li&gt;
&lt;li&gt;global processing (spatial inductive bias와 대비되는 특성)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;i&gt;&lt;b&gt;Inductive bias란?? ML 모델에 대해 정확한 예측을 위해 사용하는 추가적인 가정임. &lt;/b&gt;&lt;/i&gt;&lt;br /&gt;예를 들어 CNN은 convolution filter가 Window sliding을 하기 때문에 local한 영역에서 spatial한(공간적인) 정보를 뽑아내는데요. 이는 &quot;Vistion task는 local한 영역에서 정보를 얻을게 많다&quot;라고 inductive bias가 들어가게 되는 것입니다. 이러한 가정이 옳기 때문에 CNN은 효과적인 성능을  보이는 것입니다. RNN 또한 모델설계과정에서 sequential한 inductive bias가 들어간것입니다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 MobileViT block은 local, global한 information모두 효과적으로 encoding할 수 있으므로 accuracy도 높고 latency도 낮습니다.&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;그럼 결합은 어떻게 했느냐?? 물론 기본적으로 CNN을 쓰기도 했지만 MobileViT는 기존 ViT와 ViT의 variants(다른 논문들)과 다르게 global representation을 학습하기 위해 기존의 convolution의 연산을 변경하였습니다. 기본적으로 standard 한 convolution은 unfolding, local processing, folding의 순차적 operation을 포함하지만 MobileViT block은 local processing 부분을 transformer를 이용한 global processing으로 대체하였습니다. 이를 통해 해당 block은 CNN의 특성과 ViT의 특성 모두 가지게 되고 이는 적은 parameter와 간단한 training recipe(basic augmentation)으로도 좋은 성능을 가져왔다고 말합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;278&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkyKpn/btrxr5PrQpq/cn5WDbzVUym0nqrIVxOS01/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkyKpn/btrxr5PrQpq/cn5WDbzVUym0nqrIVxOS01/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkyKpn/btrxr5PrQpq/cn5WDbzVUym0nqrIVxOS01/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbkyKpn%2Fbtrxr5PrQpq%2Fcn5WDbzVUym0nqrIVxOS01%2Fimg.png&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; alt=&quot;transformer as convolution&quot; loading=&quot;lazy&quot; width=&quot;456&quot; height=&quot;171&quot; data-origin-width=&quot;741&quot; data-origin-height=&quot;278&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;/h2&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. MobileViT: A Light-weight Transformer&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MobileViT의 구조를 알아보기 전에 ViT먼저 간단하게 알아보고 가죠!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 ViT(Visual Transformer)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래오 같이 ViT는 이미지 \( X \in \mathbb{R}^{&amp;nbsp; H \times W \times C } &amp;nbsp;\)가 sequential한 flatten된 patches \( X_f \in \mathbb{R}^{&amp;nbsp; N \times PC } \) 로 reshape됩니다. 그리고 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\(X_f \)는&lt;/span&gt; linear layer을 통해 \( X_p&amp;nbsp; \in \mathbb{R}^{ N \times d }\)로 &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;linear projection되어 fixed된 \(d\)-dimension을 가지게 됩니다. 그리고 \(X_p \)을 입력으로 \(L\)개의 transformer block을 학습하게 됩니다.&amp;nbsp; \(C, H, W\)은 각각 image의 channel, height, width을 의미하고 \( P=wh \)은 임의의 height \(h\)와 \(w\)을 가지는 patch안의 image pixel을 말하고 \(N \)은 patch의 개수를 의미합니다.&amp;nbsp;&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;&lt;span&gt;예를 들어 아래의 이미지가 15(H)x15(W)x(3)이라면 9개의 patch로 나눠지는 것이고 각 patch는 h = 15(H)/3 = 5, w= 15(W)/3 =5 를 가지며 flatten되어 5(h)*5(w) 25의 차원을 가지게 됩니다. 즉, \(X_f \in \mathbb{R}^{ 9(N) \times 25(P)* 3(C) } \)의 형태를 가지게 됩니다. 그리고 \( X_f \) &lt;span&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;(보라색 부분!)&lt;/span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;는 linear layer의 weight와 곱해져서 \(d\) dimension으로 표현됩니다. linear projection을 통해 sequential한 정보를 학습할 수 있도록 positional encoding하는 것입니다.&amp;nbsp; (아래의 extra learnable class embedding은 class에 대한 정보를 학습할 parameter라고 인지하시면 됩니다. )&lt;/span&gt;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;vit.gif&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xhaRM/btrxwAoZJfr/obDS7cjkCiRGlQPr4FOi11/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xhaRM/btrxwAoZJfr/obDS7cjkCiRGlQPr4FOi11/img.gif&quot; data-alt=&quot;https://github.com/gupta-abhay/pytorch-vit&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xhaRM/btrxwAoZJfr/obDS7cjkCiRGlQPr4FOi11/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/xhaRM/btrxwAoZJfr/obDS7cjkCiRGlQPr4FOi11/img.gif&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; alt=&quot;ViT system&quot; loading=&quot;lazy&quot; width=&quot;638&quot; height=&quot;438&quot; data-filename=&quot;vit.gif&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1100&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://github.com/gupta-abhay/pytorch-vit&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 ViT는 CNN이 가지는 spatial inductive bias를 가지지 못합니다. 그래서 visual representation을 학습하기 위해서는 수많은 데이터를 필요로 하는 문제점을 발생시키죠.&amp;nbsp; 이러한 문제점을 해결하기 위해 MobileViT를 제안하게 되는 것이죠!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 MobileViT Architecture&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1279&quot; data-origin-height=&quot;305&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kTimW/btrxwZaYzuE/6ewxTGzGl9rK1EbSdjIc71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kTimW/btrxwZaYzuE/6ewxTGzGl9rK1EbSdjIc71/img.png&quot; data-alt=&quot;MobileViT block&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kTimW/btrxwZaYzuE/6ewxTGzGl9rK1EbSdjIc71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkTimW%2FbtrxwZaYzuE%2F6ewxTGzGl9rK1EbSdjIc71%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;797&quot; height=&quot;190&quot; data-origin-width=&quot;1279&quot; data-origin-height=&quot;305&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MobileViT block&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;MobileViT block은 적은 parameter로 input tensor \(X \in \mathbb{R}^{ H \times W \times C} \)의 local, global information을 모두 학습할 수 있도록 하는 것을 목표로 합니다. 먼저, Input tensor을 입력으로 MobileViT는 \( n \times n \)의 convolution과 point-wise (or 1x1) convolution을 적용하여 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;local spatial information&lt;/b&gt;&lt;/span&gt;을 학습하게 되는 것이죠. Convolution울 적용한 Ouput은&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\( X_L \in \mathbb{R}^{ H \times W \times d } \)이며&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;\( d &amp;gt; C \)을 만족합니다.&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;그리고 MobileViT이 long-range non-local dependencies를 가지게 하기 위한 모델링을 하게됩니다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;long-range란?&lt;/b&gt; patch간의 position이 멀리 떨어져있어도 서로 간의 정보를 주고 받을 수 있도록 함. 반대로 RNN과 같이 단어(token)간의 거리가 멀 경우 서로의 정보를 교환 및 학습하기 힘든 것을 short-range라고 함.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;long-range dependency modeling을 위해서는 가장 흔히 쓰는 방법 중 하나는 dilated convolution입니다. 하지만 이러한 방법은 dilation rate에 따라 성능이 크게 좌우되므로 좋지 않죠. 다른 좋은 solution으로는 self-attention방법론이 있는데 이 중에 하나가 ViT의 multi-head attention입니다. 하지만 기존의 ViT는 parameter도 많고 sub-standard optimizability를 가지게 되고 이는 ViT가 spatial inductive bias를 가지지 못하는 이유가 됩니다.&amp;nbsp;&amp;nbsp;&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;그래서 저자들은&amp;nbsp; &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;ViT의 multi-head attention으로 long range dependency를 만족시키고 더하여 MobileViT block을 제안하여 기 spatial inductive bias까지 가지도록 하게 하는 것을 목적&lt;/b&gt;&lt;/span&gt;으로 하는 것입니다.&amp;nbsp; 그래서 \( X_L \)을 unfold시켜 image와 같은 3차원의 non-overlapping flattend patches \( X_U \in \mathbb{R}^{P \times N \times d } \)를 만들어 냅니다. \(N = \frac{HW}{P} \)는 patch의 수, &lt;span&gt;\(P = wh \)는&lt;span&gt; 각 patch의 dimension&lt;/span&gt;&lt;/span&gt;이며 \( h \leq n\)과 \( w \leq n\)을 만족합니다.&amp;nbsp; (위에서 n=3임을 기억!) 각 \( p \in \{ 1, ... , P\}\)에 대해, inner-patch간의 relationship은 \(L\)개의 transformer를 거쳐 encoding되면서 &lt;span&gt;global information을 학습하게 됩니다.&lt;span&gt; encoding된 ouput은&lt;/span&gt;&lt;/span&gt; \(X_G \in \mathbb{R}^{ P \times N \times d} \)입니다. (기존 ViT와 다르게 positional encoding이 없다는 것을 확인!)&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;\[&lt;br /&gt;X_G(p) = Transformer(X_U (p)), 1 \leq p \leq P , \quad \cdots Eq. (1)&lt;br /&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;&amp;nbsp;\(n \times n\) convolution을 거쳐나온 \(X_U(p)\)는 \(n \times n\)크기의 local information을 encode하고 있으며 \(X_G(p)\)는 하나의 \(p\)-th location에 대해 다른 \(P\) patch들간의 global infromation을 encoding하고 있으므로 \(X_G(p)\)의 각 pixel은 \(X\)의 모든 pixel을 encoding한다고 말할 수 있으며 이는 MobileViT의 effective한 receptive field의 크기는 \(H \times W\)입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;mobileViT.png&quot; data-origin-width=&quot;1595&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b70thY/btrxyiIrQsn/jPPPDoLIYO3subKxGiMKu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b70thY/btrxyiIrQsn/jPPPDoLIYO3subKxGiMKu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b70thY/btrxyiIrQsn/jPPPDoLIYO3subKxGiMKu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb70thY%2FbtrxyiIrQsn%2FjPPPDoLIYO3subKxGiMKu0%2Fimg.png&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; alt=&quot;MobileViT novelty&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;168&quot; data-filename=&quot;mobileViT.png&quot; data-origin-width=&quot;1595&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기 때문에 MobileViT는 각 patch의 patch order와 pixel의 spatial order 모두 잘 학습되는 것입니다. 그리고 다시 \(X_G\)를 fold시켜 \( X_F \in \mathbb{R}^{H \times W \times d} \)를 얻습니다. \(X_F \)는 다시 원래의 \(C\)-dimension으로 projection 시키기 위해 point-wise convoution을 사용하게 되고 해당 output은 \(X\)와 concatenation operation을 통해 결합됩니다. 이후 \(n \times n\) convolution을 사용해 concatenated된 features들을 Fusing하게 되죠.&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.2.1 MobileViT architecture&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;168&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OJBw2/btrxwzYbOBe/qpVHLTta21ruwNnSqQsY11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OJBw2/btrxwzYbOBe/qpVHLTta21ruwNnSqQsY11/img.png&quot; data-alt=&quot;MobileViT architecture&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OJBw2/btrxwzYbOBe/qpVHLTta21ruwNnSqQsY11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOJBw2%2FbtrxwzYbOBe%2FqpVHLTta21ruwNnSqQsY11%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;809&quot; height=&quot;127&quot; data-origin-width=&quot;1070&quot; data-origin-height=&quot;168&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;MobileViT architecture&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 MobileViT의 전체 구조입니다. MobileViT의 첫번째 layer는 strided 3x3 convolution을 사용하고 다음으로는 MobileNetv2 (or MV2) blocks과 MobileViT blocks을 사용합니다. activation function은 swish를 사용하였고 \(h = w =2\)로 설정하였습니다. 그리고 MobileViT는 3가지 다른 사이즈의 모델이 있습니다. S: small, XS: extra small, XXS: extra extra small입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;349&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/p94fh/btrxGcgk9EH/jf3tGfAZ7h9w8fWKDxsWh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/p94fh/btrxGcgk9EH/jf3tGfAZ7h9w8fWKDxsWh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/p94fh/btrxGcgk9EH/jf3tGfAZ7h9w8fWKDxsWh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fp94fh%2FbtrxGcgk9EH%2Fjf3tGfAZ7h9w8fWKDxsWh1%2Fimg.png&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; alt=&quot;MobileViT series&quot; loading=&quot;lazy&quot; width=&quot;801&quot; height=&quot;260&quot; data-origin-width=&quot;1076&quot; data-origin-height=&quot;349&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.2.1 Light weight&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 ViT는 spatial inductive bias가 없으므로 model의 capacity를 올려 visual representation을 학습하도록 하였습니다. 하지만 &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;MobileViT는 spatial inductive bias를 포함하므로 model의 capacity를 낮출 수 있습니다&lt;/b&gt;&lt;/span&gt;. 그래서 transformer layer \(L = \{2, 4, 3 \} \), dimension \(d = \{ 96, 120,144\}\)로 설정하였고 각 spatial level은 \(32 \times 32\),&amp;nbsp;\(16 \times 16\),&amp;nbsp;\(8 \times 8\)으로 셋팅하였습니다. (기존의 ViT-based 모델 DeIT는 \(L=12, d=192\)을 사용했었음)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 Multi-scale Sampler for Training Efficiency&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 ViT-based model은 multi-scale representation을 학습하기위해 &lt;b&gt;&lt;i&gt;fine-tuning&lt;/i&gt;&lt;/b&gt;을 진행했었죠. 이는 기존의 ViT모델의 positional embedding이 input size에 따라 interpolated되어야 하기때문입니다. (다양한 size의 input을 받아야 성능이 높아지는 구조라고 이해하시면 됩니다.) 하지만, MobileViT는 CNN과 비슷하므로 postional embedding이 필요없는 것이고 이는 Fine-tuning이 필요없다는 뜻입니다.&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;다만, &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;i&gt;&lt;b&gt;multi-scale training은 기존 CNN의 성능에도 효과적이므로 저자들은 MobileViT에도 해당 방법론을 사용하게 됩니다&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;. spatial resoltuion \( S = \{ (H_1, W_1), ... , (H_n, W_n) \} \)이 정렬된 채로 주어집니다. 그리고 \(t\)-th training iteration에서 각 GPU마다 하나의 spatial resoultion을 랜덤하게 sampling \( (H_t, W_t) \in S \)되고 batch size는 \(b_t = \frac{H_n W_n b}{H_t W_t}를 갖습니다. 그 결과 작은 spatial resolution을 가질경우 큰 batch size이 사용됩니다. 이는 optimization update를 감소시키므로 빠르게 training할 수 있게 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1047&quot; data-origin-height=&quot;275&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnXq3u/btrxH0zCV4C/nPMylD3IepjTcM54x0X8U1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnXq3u/btrxH0zCV4C/nPMylD3IepjTcM54x0X8U1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnXq3u/btrxH0zCV4C/nPMylD3IepjTcM54x0X8U1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnXq3u%2FbtrxH0zCV4C%2FnPMylD3IepjTcM54x0X8U1%2Fimg.png&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; alt=&quot;Muti-scale vs standard. sample&quot; loading=&quot;lazy&quot; width=&quot;788&quot; height=&quot;207&quot; data-origin-width=&quot;1047&quot; data-origin-height=&quot;275&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 그림은 standard한 sampler와 multi-scale을 비교한것인데 (b)에서 알 수 있듯이 update되는 횟수가 적으므로 epoch time도 적은것을 알 수 있습니다. 또한 Multi-scale sampler를 통해 0.5%의 성능 향상도 보았다고 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3.  Experiment results&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Experiment setting for classification&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ImageNet-1K dataset&lt;/li&gt;
&lt;li&gt;300 epoch&lt;/li&gt;
&lt;li&gt;1024 batch size with AdamW optimizer and label smoothing cross entropy(smoothing=0.1)&lt;/li&gt;
&lt;li&gt;multi-scale sampler \(S = \{ (160,160), (192,192), (256,256), (288,288), (320,320) \} \)&lt;/li&gt;
&lt;li&gt;learning rate from 0.0002 to 0.002 for the first 3k iteration, annealed to 0.0002 using consine scheduler&lt;/li&gt;
&lt;li&gt;0.01 L2 weight decay&amp;nbsp;&lt;/li&gt;
&lt;li&gt;basic data augmentation (i.e. random resized cropping, horizontal flipping)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Experiment result for classification&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPvE87/btrxHypVEcX/57gjMBV7SI64TR9g1Qi4Ok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPvE87/btrxHypVEcX/57gjMBV7SI64TR9g1Qi4Ok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPvE87/btrxHypVEcX/57gjMBV7SI64TR9g1Qi4Ok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPvE87%2FbtrxHypVEcX%2F57gjMBV7SI64TR9g1Qi4Ok%2Fimg.png&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; alt=&quot;MobileViT classification results&quot; loading=&quot;lazy&quot; width=&quot;673&quot; height=&quot;348&quot; data-origin-width=&quot;1118&quot; data-origin-height=&quot;579&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 Experiment setting for object detection&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MS-COCO dataset&lt;/li&gt;
&lt;li&gt;SSD-Lite의 backbone으로 MobileViT사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;다른 backbone들과 성능 비교&lt;/li&gt;
&lt;li&gt;SSD-Lite는 기존 SSD head의 conv를 separable convolution으로 바꾼 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;320 x 320 input resolution&amp;nbsp;&lt;/li&gt;
&lt;li&gt;AdamW optimizer&lt;/li&gt;
&lt;li&gt;smooth L1 (localization용) and cross entropy (classification용)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.4 Experiment setting for object detection&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cOuzTA/btrxHWD6NuO/0Ax9FChxPCijGiClKvbvb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cOuzTA/btrxHWD6NuO/0Ax9FChxPCijGiClKvbvb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cOuzTA/btrxHWD6NuO/0Ax9FChxPCijGiClKvbvb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcOuzTA%2FbtrxHWD6NuO%2F0Ax9FChxPCijGiClKvbvb0%2Fimg.png&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; alt=&quot; MobileViT object detection result&quot; loading=&quot;lazy&quot; width=&quot;325&quot; height=&quot;319&quot; data-origin-width=&quot;424&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>AI paper review/Mobile-friendly</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/46</guid>
      <comments>https://da2so.tistory.com/46#entry46comment</comments>
      <pubDate>Mon, 28 Mar 2022 10:24:26 +0900</pubDate>
    </item>
    <item>
      <title>EfficientNetv2 논문 리뷰</title>
      <link>https://da2so.tistory.com/45</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://polyfill.io/v3/polyfill.min.js?features=es6&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ICML 2021에 accept된 구글 논문인 &lt;a href=&quot;https://arxiv.org/pdf/2104.00298.pdf&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;&lt;b&gt;EfficientNetV2: Smaller Models and Faster Training&lt;/b&gt;&lt;/i&gt;&lt;/span&gt;&lt;/a&gt;을 리뷰해보겠습니다!&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Introduction&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EfficientNet의 후속모델로 EfficientNetv2는 기존 모델보다 다음과 같은 목적성을 이룰려고 하고 이루게 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;More efficient &lt;span style=&quot;color: #6164c6;&quot;&gt;Training time&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;More efficient &lt;span style=&quot;color: #6164c6;&quot;&gt;Parameter number&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;More efficient &lt;span style=&quot;color: #6164c6;&quot;&gt;Accuracy&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 아래그림은 EfficientNetv2의 결과인데 보면 위의 목적성을 모두 잘 이뤗네요. 역시 구글..&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림11024_1.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;699&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtPbhu/btrw5POp9uI/Nt8hx7og70J1DtJMPbM7cK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtPbhu/btrw5POp9uI/Nt8hx7og70J1DtJMPbM7cK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtPbhu/btrw5POp9uI/Nt8hx7og70J1DtJMPbM7cK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtPbhu%2Fbtrw5POp9uI%2FNt8hx7og70J1DtJMPbM7cK%2Fimg.jpg&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; alt=&quot;efficientNetv2 result&quot; loading=&quot;lazy&quot; width=&quot;567&quot; height=&quot;387&quot; data-filename=&quot;그림11024_1.jpg&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;699&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 EfficientNetv2의 어떤 contribution이 있었기에 이런 결과를 도출해냈을까요? &lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;(1)&lt;/span&gt; &lt;/b&gt;EfficientNet을 기반으로하는 search space를 구성하여 NAS search (&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;Training-aware NAS&lt;/b&gt;&lt;/span&gt;)를 통해 EfficientNetv2 구조를 찾아 내었고 &lt;b&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;(2)&lt;/span&gt;&lt;/b&gt; image size에 따라 augmentation magnitude를 달리하는 &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;Progressive Learning&lt;/b&gt;&lt;/span&gt;을 제안하였습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2  EfficientNetV2 Architecture Design&amp;nbsp;&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Depthwise convolutions are slow in early layers but effective in later stages&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 EfficientNet의 extensive depthwise convolution (&lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;MBConv&lt;/b&gt;&lt;/span&gt;)은 training의 bottleneck을 가져왔습니다. MBConv는 paramter수와 FLOPs낮지만 최신 (movbile or server) accelerators를 온전히 활용하지 못하는 문제가 있기 때문에 training에 악영향을 끼쳤습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 EfficientNetv2에서는 &lt;span style=&quot;color: #009a87;&quot;&gt;&lt;b&gt;Fused-MBConv&lt;/b&gt;&lt;/span&gt;를 추가하게 됩니다. Fused-MBConv는 MBConv의 depthwise conv3x3 and expansion conv 1x1을 하나의 conv 3x3으로 바꾼 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;478&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/seBLV/btrw8PGI48H/0Uka4gDt6NnKjnv7mkkQUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/seBLV/btrw8PGI48H/0Uka4gDt6NnKjnv7mkkQUK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/seBLV/btrw8PGI48H/0Uka4gDt6NnKjnv7mkkQUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FseBLV%2Fbtrw8PGI48H%2F0Uka4gDt6NnKjnv7mkkQUK%2Fimg.png&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; alt=&quot;MBConv vs FusedMBConv&quot; loading=&quot;lazy&quot; width=&quot;353&quot; height=&quot;300&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;478&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 stage1-3까지 Fused-MBConv를 적용했을때 적은 parameter수와 FLOPs를 유지하면서 training speed도 함께 줄었습니다. (stage4-7까지는 기존의 MBConv를 사용) 그렇지만 staget1-7에 paramter수와 FLOPs, 그리고 training speed모두에 좋지 않았다고 하네요. 아래는 Fused를 사용하지 않았을때와 특정 범위에 Fused를 사용했을때의 성능 결과입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b65VZ7/btrxcxERd65/RVrmF7fizsZ54E35Ktie81/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b65VZ7/btrxcxERd65/RVrmF7fizsZ54E35Ktie81/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b65VZ7/btrxcxERd65/RVrmF7fizsZ54E35Ktie81/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb65VZ7%2FbtrxcxERd65%2FRVrmF7fizsZ54E35Ktie81%2Fimg.png&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; alt=&quot;Fused and No Fused MBConv result&quot; loading=&quot;lazy&quot; width=&quot;453&quot; height=&quot;178&quot; data-origin-width=&quot;771&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 stage마다 둘중 어떤 모듈을 사용하는 것이 최적인지 사람이 직접찾기 힘드니 여기서! NAS를 사용하게 됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Training-Aware NAS and Scaling&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;제안된 Training-Aware NAS는 EfficientNet을 backbone으로 사용하여 Search space을 구성하였습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;Search space&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;convolution operation types&lt;/b&gt;: {MBConv, Fused-MBConv}&lt;/li&gt;
&lt;li&gt;&lt;b&gt;the number of layers&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kernel size&lt;/b&gt;: {3x3, 5x5}&lt;/li&gt;
&lt;li&gt;&lt;b&gt;expansion ratio&lt;/b&gt;: {1, 4, 6}&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 EfficientNet의 pooling이나 skip ops에 대한 search는 수행하지 않았다고 하며 EfficientNet의 channel size을 그대로 사용하였다고 합니다. 그리고 기존의 &lt;b&gt;MnasNet의 RL 방법론을 search strategy&lt;/b&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;구체적으로, search space로부터 1000개의 모델을 뽑고 reduce된 image사이즈로 10 epoch만 Training하게 됩니다. MnasNet의 RL방법론은 보상(reward)를 최대화 시키도록 RNN을 학습시켜 최적으로 network를 찾게됩니다. 보상(reward)은 \(A \cdot S^w \cdot P^v&amp;nbsp; \) 으로 설정됩니다. &lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;\( A \)는&lt;/span&gt;&amp;nbsp; model accuracy, \( S \)는 normalized training step time과 parameter size인 \( P \)으로 구성됩니다. 그리고 \( w \) = -0.07, \( v\) =-0.05으로 설정된다고 합니다.&amp;nbsp;&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;그렇게 해서 찾은 EfficientNetv2 은 아래와 같습니다. stage1~3은 Fused-MBConv가 사용됨을 볼 수 있네요!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;309&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFst8u/btrw64LlBDo/6ieJxOznfEvUfOEa9C2KBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFst8u/btrw64LlBDo/6ieJxOznfEvUfOEa9C2KBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFst8u/btrw64LlBDo/6ieJxOznfEvUfOEa9C2KBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFst8u%2Fbtrw64LlBDo%2F6ieJxOznfEvUfOEa9C2KBk%2Fimg.png&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; alt=&quot;EfficientNetv2 architecture&quot; loading=&quot;lazy&quot; width=&quot;542&quot; height=&quot;261&quot; data-origin-width=&quot;642&quot; data-origin-height=&quot;309&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 EfficientNetV2 Scaling&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EfficientNetV2-S부터 EfficientNetv2-M/L의 차이를 주기 위해 EfficientNet에서 사용했던 compound scaling을 사용합니다. 다만, 빠른 training time을 위해 image maximum size를 480으로 제한하였고 뒤 쪽의(stage 5,6)에 점차적으로 layer의 추가 했다고 하네요. (heuristic하게 함)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Progressive Learning&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;저자들의 가설은 다음과 같습니다 &lt;i&gt;&quot;training image size에 따라 fixed된 regularization은 accuracy 성능에 악영향을 미칠 것이야!&quot;&lt;/i&gt; 입니다. &lt;b&gt;image size이 작으면 network의 output feature map이 작아지고 이는 Network의 capacity가 작아지므로 작은 Image에는 regularization을 약하게(weak)주어야 한다는 말입니다. 그 반대도 마찬가지고요. image size가 크면 overfitting될 수 있으니 강하게(strong) regularization을 주어야한다는 것이죠.&lt;/b&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;그래서 해당 가설을 증명하기위해 &amp;nbsp;저자들은 search space에서 샘플링된 model에 대해 image size와 RandAugmentation의 magnitude(regularization)을 달리했을 때 성능 결과를 보았고 가설은 맞아떨어졌습니다! image size가 작을때 magnitude를 작게 줘야 성능이 높았고 image size가 클때는 magnitude가 커야 성능이 좋았습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Ue9D7/btrxc3wWMMn/RWE0rBlSZHGUcgwipINkPK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Ue9D7/btrxc3wWMMn/RWE0rBlSZHGUcgwipINkPK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Ue9D7/btrxc3wWMMn/RWE0rBlSZHGUcgwipINkPK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUe9D7%2Fbtrxc3wWMMn%2FRWE0rBlSZHGUcgwipINkPK%2Fimg.png&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; alt=&quot;performance by image size and randaug&amp;amp;#39;s magnitude&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;179&quot; data-origin-width=&quot;630&quot; data-origin-height=&quot;209&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Progressive Learning with adaptive Regularization&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 결과를 통해 Progressive Learning은 다음과 같은 프로세스를 진행합니다.&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: #6164c6;&quot;&gt;&lt;b&gt;초기 training epoch에는 smaller image size와 weak regularization을 준 상태로 training 진행&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;image size가 작으니 빠르게 학습되고 simple한 representation을 학습하게 해줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;점차적으로 image size와 strong regularization을 주어 training 진행&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;240&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/m9mPG/btrw7SRsSpE/dGcCOUKmRpjeOjJ29s9uq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/m9mPG/btrw7SRsSpE/dGcCOUKmRpjeOjJ29s9uq1/img.png&quot; data-alt=&quot;progressive learning&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/m9mPG/btrw7SRsSpE/dGcCOUKmRpjeOjJ29s9uq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fm9mPG%2Fbtrw7SRsSpE%2FdGcCOUKmRpjeOjJ29s9uq1%2Fimg.png&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; alt=&quot;progressive learning&quot; loading=&quot;lazy&quot; width=&quot;528&quot; height=&quot;199&quot; data-origin-width=&quot;637&quot; data-origin-height=&quot;240&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;progressive learning&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt; 수식적으로 표현해보죠.&amp;nbsp; 전체 training은 총 \( N \) 번의 step을 가지며 target image size는 \(S_e \), regularization magnitude list \( \Phi_e = { \phi^k_e } \)로 정의합니다. \(&amp;nbsp; k\)는 regularzation type으로 dropout, mixup, randaugmentation중 하나 입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;351&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uBUO7/btrxaPzKRVD/dyRr8tZpvv9jKhUalZRkn0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uBUO7/btrxaPzKRVD/dyRr8tZpvv9jKhUalZRkn0/img.png&quot; data-alt=&quot;Augmentation type&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uBUO7/btrxaPzKRVD/dyRr8tZpvv9jKhUalZRkn0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuBUO7%2FbtrxaPzKRVD%2FdyRr8tZpvv9jKhUalZRkn0%2Fimg.png&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; alt=&quot;Augmentation type&quot; loading=&quot;lazy&quot; width=&quot;422&quot; height=&quot;240&quot; data-origin-width=&quot;617&quot; data-origin-height=&quot;351&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Augmentation type&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 training을 총 \( M \) stage 으로 나누게 됩니다.&amp;nbsp; 각 stage마다 같은 image size \(S_i \)와 regularization \( \phi^k_i \)을 사용하겠죠. 그리고 저자들은 heuristic하게&amp;nbsp; \(S_0 \)와 \( \phi^k_0 \)을 정했고 stage가 올라갈때마다 linear interpolation을 사용하여 더 큰 image size와 strong regularization을 주었습니다. 다음은 위의 progressive learning 프로세스를 알고리즘으로 표현한 것입니다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;303&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c8WVKs/btrw7RyeFis/rRrLCmMzfge0P4JJawBUJK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c8WVKs/btrw7RyeFis/rRrLCmMzfge0P4JJawBUJK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c8WVKs/btrw7RyeFis/rRrLCmMzfge0P4JJawBUJK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc8WVKs%2Fbtrw7RyeFis%2FrRrLCmMzfge0P4JJawBUJK%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;488&quot; height=&quot;226&quot; data-origin-width=&quot;654&quot; data-origin-height=&quot;303&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4.&amp;nbsp; Experiment setting for ImageNet&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ImageNet dataset에 대해 RMSProp optimizer를 상요하였고 0.9 decay, 0.9 momentum, batch norm momentum 0.99와 weight 1-5을 주었습니다. 그리고 350epoch으로 학습하였고 batch size는 4096입니다. learning rate는 0에서 0.256까지 설정하였고 2.4 epoch마다 0.97씩 learning rate를 decay하였습니다. 추가적으로 exponential moving average with 0.9999 decay rate, RandAugment , Mixup, Dropout, and stochastic depth( 0.8 survival probability)을 사용하였습니다. 다음은 네트워크 설정입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RLg9G/btrxcziAiLF/pAGbPKVf5U3QUsOgEfGDYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RLg9G/btrxcziAiLF/pAGbPKVf5U3QUsOgEfGDYk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RLg9G/btrxcziAiLF/pAGbPKVf5U3QUsOgEfGDYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRLg9G%2FbtrxcziAiLF%2FpAGbPKVf5U3QUsOgEfGDYk%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;458&quot; height=&quot;188&quot; data-origin-width=&quot;562&quot; data-origin-height=&quot;231&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5.&amp;nbsp;  Result for ImageNet&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;750&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dfbPNP/btrxcLiPsbo/QUx3WK7twp8llKd4AkUs3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dfbPNP/btrxcLiPsbo/QUx3WK7twp8llKd4AkUs3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dfbPNP/btrxcLiPsbo/QUx3WK7twp8llKd4AkUs3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdfbPNP%2FbtrxcLiPsbo%2FQUx3WK7twp8llKd4AkUs3K%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;792&quot; height=&quot;642&quot; data-origin-width=&quot;925&quot; data-origin-height=&quot;750&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;409&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dTH5rY/btrw7ZCrfiw/trvhgdyb5Eesw8wFPKPic0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dTH5rY/btrw7ZCrfiw/trvhgdyb5Eesw8wFPKPic0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dTH5rY/btrw7ZCrfiw/trvhgdyb5Eesw8wFPKPic0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdTH5rY%2Fbtrw7ZCrfiw%2Ftrvhgdyb5Eesw8wFPKPic0%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;836&quot; height=&quot;248&quot; data-origin-width=&quot;1380&quot; data-origin-height=&quot;409&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI paper review/Mobile-friendly</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/45</guid>
      <comments>https://da2so.tistory.com/45#entry45comment</comments>
      <pubDate>Thu, 24 Mar 2022 16:54:50 +0900</pubDate>
    </item>
    <item>
      <title>TensorFlow.js (2) - WebGL 기반 hand pose detection</title>
      <link>https://da2so.tistory.com/44</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;0.&amp;nbsp; WebGL 기반 hand pose detection&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 TensorFlow.js의 backend가 무엇이 있는 지 알아보고 사용가능한 backend 중 하나인 WebGL을 기반으로 hand pose detection을 해볼것입니다.&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;hand pose detection만 하면 재미가 없으니 hand pose 에 따라 다음과 같이 이모티콘을 보여줄 수 있도록 해봅니다. 엄지를 위로 올리면 엄지척하는 이모티콘이 나오도록 하고 아래로 내리면 OMG하는 이모티콘을 나오도록 하겠습니다.&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;up_and_down.png&quot; data-origin-width=&quot;2331&quot; data-origin-height=&quot;1137&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cQZeOJ/btrw64pzy57/KOmsf6BECcOGJIqm8jT1v0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cQZeOJ/btrw64pzy57/KOmsf6BECcOGJIqm8jT1v0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cQZeOJ/btrw64pzy57/KOmsf6BECcOGJIqm8jT1v0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcQZeOJ%2Fbtrw64pzy57%2FKOmsf6BECcOGJIqm8jT1v0%2Fimg.png&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; alt=&quot;hand pose detection with emoticon&quot; loading=&quot;lazy&quot; width=&quot;811&quot; height=&quot;396&quot; data-filename=&quot;up_and_down.png&quot; data-origin-width=&quot;2331&quot; data-origin-height=&quot;1137&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1.&amp;nbsp; TensorFlow.js backend&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TensorFlow.js에는 다양한 backend가 존재합니다. 일단 backend란 모델 그래프의 연산들을 수행하는 내부적인 플랫폼이라고 이해하시면 됩니다. 그래서 어떤 backend를 사용하느냐에&amp;nbsp; 따라 같은 모델이라도 inference time 성능에 영향을 미치게 되는 것이죠!&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 CPU (순수 javascript)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CPU backend는 가장 기본적인 backend입니다. 가용성과 보편성을 가지고 작은 오버헤드와 자동으로 메모리관리를 해주는 장점을 갖지만 단일 thread로 실행되므로 하드웨어 가속의 장점은 가져갈 수 없습니다. 가용 리소스는 javascript runtime에 의해 제한되는 것도 문제이죠.&amp;nbsp; 결국 많이 느리다는 문제로 일반적으로 웹 애플리케이션에서 사용되지 않는 backend입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.2 WebGL&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebGL API를 사용하는 backend입니다. &lt;b&gt;&lt;i&gt;WebGL은 웹에서의 그래픽 처리에 사용되는 표준 API입니다&lt;/i&gt;&lt;/b&gt;. 고수준의 병렬 처리로 가능한 작업들을 활용할 수 있도록 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Shader&lt;/b&gt;&lt;/span&gt; 프로그램을 사용하여 커널 연산들을 구현합니다. WebGL backend는 텐서를 GPU에 올릴 수 있는 텍스쳐 형식으로 저장하고 각 텍스쳐 좌표마다 GPU를 통해 병렬로 처리하게 되는 것입니다. 그래서 WebGL backend사용시 cpu보다 100배 빠르다고 하네요!&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Shader란?&lt;/b&gt; 컴퓨터 그래픽스 분야에서 그래픽 하드웨어의 랜더링 효과를 계산하는 데 쓰이는 소프트웨어 명령의 집합&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 다른 특징으로는 TensorFlow.js는 shader 자원을 최대한 활용하기 위해 미리 컴파일된 shader 프로그램을 따로 캐시에 저장둡니다. 그래서 model의 inference을 수행하기 전 준비 프로세스는 컴파일된 코들르 캐시에 복사해 둡니다. 그리고 캐시에 저장된 코드는 이후 연산의 실행이 발생할 때마다 재사용하여 shader 컴파일 과정의 오버헤드를 줄이게 됩니다. 특히나 ML 어플리케이션의 경우 같은 연산을 반복하는 경우가 많기 때문에 해당 특징은 inference time을 줄이는 데 효과적입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;webgl.png&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;704&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZm3wS/btrwVrT5o9w/hrZmSGl1kplquLunOxkWU1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZm3wS/btrwVrT5o9w/hrZmSGl1kplquLunOxkWU1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZm3wS/btrwVrT5o9w/hrZmSGl1kplquLunOxkWU1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZm3wS%2FbtrwVrT5o9w%2FhrZmSGl1kplquLunOxkWU1%2Fimg.png&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; alt=&quot;webGL&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;266&quot; data-filename=&quot;webgl.png&quot; data-origin-width=&quot;1410&quot; data-origin-height=&quot;704&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;&lt;b&gt;1.3 Node.js&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js는 서버 사이드 javascript 플랫폼이며 웹 어플리케이션 만들때 사용많이합니다. javascript 런타임으로 v8을 사용합니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;&amp;nbsp; v8이란?&lt;/b&gt; V8 엔진은 구글이 만들었으며 오픈소스이고 C++로 제작됩니다. 구글크롬에서 사용 중입니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js는 이벤트 중심의 I/O를 사용하여 여러 네트워크 연결을 범위성 있게 다룰 수 있도록합니다. 이러한 동시 연결 모델은 ML과 같이 CPU자원을 집중적으로 사용하는 작업일 경우 좋은 선택지는 아닌데 TenosorFlow.js에서 Node.js backend를 선택한 이유는 Node.js의 잠재력때문입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Node.js backend는 Node.js를 C언어로 확장한 것인데 이 backend는 Tensorflow의 C언어 API를 사용가능하도록 하기 때문에 GPU와 TPU 사용 측면에서 많은 잠재력을 지닙니다. Tensorflow의 C언어 구현부는 하드웨어 가속에 최적화 되어있기때문에 Node.js backend만 잘 갖춰진다면 웹어플리케이션 배포까지의 최적화가 모두 이루어지는 것입니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. hand pose detection 코드&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 사용할 model은 tfjs-models에서 제공하는 model을 사용할것이고&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt; webcam을 통해서 live로 hand pose detection을 수행&lt;/b&gt;&lt;/span&gt;하도록 하겠습니다. 해당 detection은 위에서 말씀드린 webGL backend을 사용하게 됩니다. 추가적으로 손모양에 따라 이모티콘이 보여질 수 있도록 하는 것을 목적으로 합니다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 HTML skeleton&amp;nbsp;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML를 통해 뼈대부터 만들어보죠.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647958617175&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div id=&quot;main&quot;&amp;gt;
&amp;lt;div class=&quot;container&quot;&amp;gt;
&amp;lt;div class=&quot;canvas-wrapper&quot;&amp;gt;
	&amp;lt;canvas id=&quot;output&quot;&amp;gt;&amp;lt;/canvas&amp;gt;
	&amp;lt;video id=&quot;video&quot; 
    	playsinline=&quot;&quot;
    	style=&quot;-webkit-transform: scaleX(-1);
		transform: scaleX(-1); 
 		visibility: hidden; 
		width: auto; height: auto;&quot;&amp;gt;
	&amp;lt;/video&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&quot;emo&quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요하게 보실 내용은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;output Id를 가지는 canvas를 통해 hand pose detection의 결과가 webcam 위에 그려짐&lt;/li&gt;
&lt;li&gt;video element를 통해 webcam이 실시간으로 streaming됨
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;-webkit-transform&lt;/code&gt;과 &lt;code&gt;transform&lt;/code&gt;을 통해 좌우반전시킴&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;emo id를 가지는 div를 통해 hand pose에 따라 이모티콘 출력&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 CDN을 통한 필요한 javascript import&lt;/b&gt;&lt;/h3&gt;
&lt;pre id=&quot;code_1648022146397&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- Require the peer dependencies of hand-pose-detection. --&amp;gt;
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- You must explicitly require a TF.js backend if you're not using the TF.js union bundle. --&amp;gt;
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow-models/hand-pose-detection&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebGL backend를 사용하기 위해 3번째 script 코드를 작성하였고 4번째 script 코드는 저희가 사용할 hand pose detection model을 사용하려면 필요한 javascript입니다.&amp;nbsp; 해당 javascript사용하기 위해 tfjs-core와 tfjs-converter가 필요하기 때문에 위의 2개 script코드를 추가하였습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3  webcam을 통한 hand pose detection javascript&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;app이라는 function을 다음과 같이 만들어 큰 틀부터 잡고 시작하죠!&lt;/p&gt;
&lt;pre id=&quot;code_1648022771537&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;
async function app() {

  camera = await Camera.setupCamera(); //webcam 셋팅

  detector = await createDetector(); // hand pose detection model 셋팅
  console.log(tf.getBackend()); // 사용되는 TensorFlow.js backend확인
  renderPrediction(); // detection을 통한 result를 draw
};
app(); // app function 실행
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 script를 통해 &lt;code&gt;app&lt;/code&gt;함수가 실행될 건데 해당 함수에서는 webcam과 detection model을 load하고 webcam을 입력으로 detection model이 도출한 결과를 renderPrediction을 통해 보여지는 flow를 가집니다.&amp;nbsp;&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;이제&amp;nbsp; 하나하나 위의 함수에 대한 구현부와 그에 대한 주석을 보면서 이해해보죠. (함수들의 구현부를 모두 보여드리는 것은 너무 길어서 중요부분만 보여드리고 설명 할수도 있다는 점  알려드릴게요. 그래두 전체 코드는 제 github &lt;b&gt;&lt;a href=&quot;https://github.com/da2so/tfjs_tutorial&quot;&gt;tfjs_tutorial&lt;/a&gt;&lt;/b&gt;&lt;span&gt;&lt;span&gt; &amp;nbsp;&lt;/span&gt;&lt;/span&gt;에 올려두었으니 걱정마세여)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3.1 webcam 설정 및 활성화&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1648023876941&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function isiOS() {
  return /iPhone|iPad|iPod/i.test(navigator.userAgent);
}
function isAndroid() {
  return /Android/i.test(navigator.userAgent);
}
function isMobile() { // mobile인지 확인
  return isAndroid() || isiOS();
}
class Camera {
  constructor() {
    this.video = document.getElementById('video'); //video id를 가진 HTML code의 element가져옴
    this.canvas = document.getElementById('output');
    this.ctx = this.canvas.getContext('2d');
  }

  static async setupCamera() {
	... //생략

    const $size = { width: 640, height: 480 }; //desktop용 사이즈
    const $m_size = { width: 360, height: 270 }; //mobile용 사이즈
    const videoConfig = {
      'audio': false,
      'video': {
        facingMode: 'user',
        width: isMobile() ? $m_size.width : $size.width,
        height: isMobile() ? $m_size.height : $size.height,
      }
    };
    const stream = await navigator.mediaDevices.getUserMedia(videoConfig);
    const camera = new Camera();
    camera.video.srcObject = stream; // webcam의 live stream을 video id가진 HTML코드의 video element에 할당

    await new Promise((resolve) =&amp;gt; {
      camera.video.onloadedmetadata = () =&amp;gt; {
        resolve(video);
      };
    });
    camera.video.play(); 

    const videoWidth = camera.video.videoWidth; 
    const videoHeight = camera.video.videoHeight;
    camera.video.width = videoWidth; 
    camera.video.height = videoHeight;
    // canvas는 나중에 detection result를 그리는데 사용 됨 
    camera.canvas.width = videoWidth; // videoWidth와 일치시켜 detection result가 video cam위에 맵핑되도록함
    camera.canvas.height = videoHeight;
    const canvasContainer = document.querySelector('.canvas-wrapper');
    canvasContainer.style = `width: ${videoWidth}px; height: ${videoHeight}px`; // css부분도 video cam과 같은 크기로 할당

    // 기본적으로 camera가 mirroring되어있으므로 horizontal flipping함
    camera.ctx.translate(camera.video.videoWidth, 0);
    camera.ctx.scale(-1, 1);

    return camera;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드의 설명은 주석으로 이해하시면 되고 중요 내용은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;document.getElementById&lt;/code&gt;를 통해 해당 Id를 가진 HTML element를 가져옴&lt;/li&gt;
&lt;li&gt;desktop용, mobile용 webcam사이즈를 달리함&lt;/li&gt;
&lt;li&gt;video와 canvas의 크기 설정을 동일시함&lt;/li&gt;
&lt;li&gt;webcam이 mirroring되어있으므로 horizontal flipping&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3.2 hand pose detection model 셋업&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1648026522561&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function createDetector() {
  const hands = handPoseDetection.SupportedModels.MediaPipeHands; //mediapipe에서 제공하는 hand pose detection model사용

  return handPoseDetection.createDetector(hands, {
    runtime: 'tfjs', //runtime을 tfjs로 설정함에 따라 webGL을 Default로 사용함
    modelType: 'full', //full(큰 모델) or lite(작은 모델)
    maxHands: 1, // or 2~10 : detect할 손의 개수
  })
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;runtime&lt;/code&gt;을 tfjs설정하면 자동으로 webGL backend가 사용됩니다. HTML skeleton에서 작성했던 &lt;code&gt;console.log(tf.getbackend())&lt;/code&gt;의 결과를 먼저 보여드리면 다음과 같이 webGL을 사용하는것을 알 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;54&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Iz9Eb/btrw63qFXIB/y1R0aWPFsOgWsEuXJKpXuk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Iz9Eb/btrw63qFXIB/y1R0aWPFsOgWsEuXJKpXuk/img.png&quot; data-alt=&quot;console.log(tf.getbackend()) 결과&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Iz9Eb/btrw63qFXIB/y1R0aWPFsOgWsEuXJKpXuk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIz9Eb%2Fbtrw63qFXIB%2Fy1R0aWPFsOgWsEuXJKpXuk%2Fimg.png&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; alt=&quot;tf.getbackend()&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;30&quot; data-origin-width=&quot;1418&quot; data-origin-height=&quot;54&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;console.log(tf.getbackend()) 결과&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3.3 hand pose detection&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1648039533126&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;async function renderResult() {
  ... // 생략
  let hands = null;

  if (detector != null) {
    try {
      hands = await detector.estimateHands( 
        camera.video,
        { flipHorizontal: false }); //hand pose detection 결과를 hands에 반환
    } catch (error) {
      detector.dispose(); //detector에대한 tensor memory를 없앰
      ... // 생략
    }
  }
  ... // 생략
  if (hands &amp;amp;&amp;amp; hands.length &amp;gt; 0) {
    camera.drawResults(hands); // detection결과인 hands를 인자로 결과를 visualize하는 drawResults 실행
  }
}

async function renderPrediction() {
  await renderResult();
  rafId = requestAnimationFrame(renderPrediction); //실시간으로 renderPrediction을 계속 실행
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HTML skeleton 에서 &lt;code&gt;renderPrediction&lt;/code&gt;에 대한 함수입니다. 해당 함수에서는 &lt;code&gt;renderResult&lt;/code&gt;함수를 지속적으로 실행시키게 됩니다. &lt;code&gt;renderResult&lt;/code&gt;는 실제 hand pose detection을 실행하며 결과를 web에 그려주기 위해 &lt;code&gt;drawResults&lt;/code&gt;를 호출하게 됩니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2.3.4 detection result 그리기&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;detection result를 그리는 코드를 알아보기전에 hand pose detection의 결과값의 의미부터 해석해보시죠.&amp;nbsp;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;handposeoutput.png&quot; data-origin-width=&quot;1745&quot; data-origin-height=&quot;1097&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvesC5/btrw44qksVV/hS56tknkDIkzX003I68Ci1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvesC5/btrw44qksVV/hS56tknkDIkzX003I68Ci1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvesC5/btrw44qksVV/hS56tknkDIkzX003I68Ci1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvesC5%2Fbtrw44qksVV%2FhS56tknkDIkzX003I68Ci1%2Fimg.png&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; alt=&quot;hand pose detection result example&quot; loading=&quot;lazy&quot; width=&quot;812&quot; height=&quot;511&quot; data-filename=&quot;handposeoutput.png&quot; data-origin-width=&quot;1745&quot; data-origin-height=&quot;1097&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;detection결과에서 중요하게 볼것은 왼손인지 오른손인지 handness로 확인가능하며 keypoints 배열에 각 hand keypoint에 대한 이름과 좌표를 담고 있습니다. 총 hand keypoint는 0~20까지 이므로 총 21가지 존재하겠죠. 이제 위의 결과를 이해하셨다면 코드를 보러 가보죠.&lt;/p&gt;
&lt;pre id=&quot;code_1648041156190&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const fingerLookupIndices = {
  thumb: [0, 1, 2, 3, 4],
  indexFinger: [0, 5, 6, 7, 8],
  middleFinger: [0, 9, 10, 11, 12],
  ringFinger: [0, 13, 14, 15, 16],
  pinky: [0, 17, 18, 19, 20],
}; // 각 keypoint(손가락)을 이어주는 연결을 표현하기 위함

class Camera {
  ... //생략
  
  static async setupCamera() {
  ... //생략
  }
  
  drawResults(hands) {
    ... // 생략 
    for (let i = 0; i &amp;lt; hands.length; ++i) { 
      this.drawResult(hands[i]); //detection된 모든 hand에 모두에 대해 
    }
  }
  drawResult(hand) {
    if (hand.keypoints != null) {
      this.drawKeypoints(hand.keypoints, hand.handedness); 
      const emo_type = this.drawEmoticon(hand.keypoints) // keypoints을 Parsing해서 emo_type을 반환합니다.
      //위의 drawEoticon은 github에서 확인하세여
      if (emo_type == 'up') { // 엄지가 위로 올라갈경우 따봉 이모티콘
        emo.innerHTML = '&amp;lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;032&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/032.gif&quot;&amp;gt;&amp;lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/032.gif&quot; width=&quot;150&quot; /&amp;gt;&amp;lt;/figure&amp;gt;';
      }
      else if (emo_type == 'down') { //엄지가 아래로 내려갈경우 OMG 이모티콘
        emo.innerHTML = '&amp;lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;029&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/029.gif&quot;&amp;gt;&amp;lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/029.gif&quot; width=&quot;150&quot; /&amp;gt;&amp;lt;/figure&amp;gt;'
      }
      else { // 이외일 경우 아무것도 보여주지 않음
        emo.innerHTML = '&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;' 
      }
    }
  }
  drawKeypoints(keypoints, handedness) {
    const keypointsArray = keypoints;
    this.ctx.fillStyle = handedness === 'Left' ? 'Red' : 'Blue'; //왼손, 오른손에 따라 색 구분
    this.ctx.strokeStyle = 'White'; // keypoints를 이어주는 색을 흰색으로
    this.ctx.lineWidth = 2;

    for (let i = 0; i &amp;lt; keypointsArray.length; i++) {
      const y = keypointsArray[i].x;
      const x = keypointsArray[i].y;
      this.drawPoint(x - 2, y - 2, 3);
    }

    const fingers = Object.keys(fingerLookupIndices);
    for (let i = 0; i &amp;lt; fingers.length; i++) {
      const finger = fingers[i];
      const points = fingerLookupIndices[finger].map(idx =&amp;gt; keypoints[idx]); //기준 keypoint와 연결된 keypoint들을 맵핑
      this.drawPath(points, false);
    }
  }

  drawPath(points, closePath) { // hand keypoints끼리 연결된 경우 연결(Path)을 시각화
    const region = new Path2D();
    region.moveTo(points[0].x, points[0].y);
    for (let i = 1; i &amp;lt; points.length; i++) {
      const point = points[i];
      region.lineTo(point.x, point.y); // points[0]과 연결된 points[1:]의 path를 그림
    }
	... // 생략
  }

  drawPoint(y, x, r) { // hand keypoint(Point)을 시각화
    this.ctx.beginPath();
    this.ctx.arc(x, y, r, 0, 2 * Math.PI);
    this.ctx.fill();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p 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&gt;&lt;code&gt;drawResults&lt;/code&gt;는 detect된 손의 수만큼을 rendering함&lt;/li&gt;
&lt;li&gt;&lt;code&gt;drawResult&lt;/code&gt;는 (1) detect된 손의 keypoint와 keypoint간의 연결을 그리는 &lt;code&gt;drawkeypoints&lt;/code&gt;함수,&amp;nbsp; (2)손의 모양에 따라 emoticon을 보여줄 수 있는 &lt;code&gt;drawEmoticon&lt;/code&gt;함수를 사용
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;code&gt;drawPoint&lt;/code&gt;는 hand keypoint자체를 시각화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;drawPath&lt;/code&gt;는 hand keypoint간의 연결된 부분을 시각화 (이때 &lt;code&gt;fingerLookupIndices&lt;/code&gt;를 참조)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;emo.innerHTML&lt;/code&gt;을 통해 emo라는 id를 가지는 HTML element에 할당된 HTML code(이모티콘)를 넣음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 코드는 모두 설명드렸습니다. 생략된부분이 모두 포함된 코드는 &lt;b&gt;&lt;a href=&quot;https://github.com/da2so/tfjs_tutorial&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;tfjs_tutorial&lt;/a&gt;&lt;/b&gt; 에 hand_pose_detection.html이라는 파일에 모아두었습니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. hand pose detection&lt;span&gt;&amp;nbsp;결과&lt;/span&gt;&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;webcam사용을 승낙하셧다면 아래에 detection결과가 짜라짠짜!!!!&amp;nbsp; 엄지를 위로 하는 따봉이나 엄지를 아래로 하는 hand pose를 취하시면 이모티콘이 나타나실거예요!!&lt;/p&gt;
&lt;div id=&quot;main&quot;&gt;
&lt;div class=&quot;container&quot;&gt;
&lt;div class=&quot;canvas-wrapper&quot;&gt;&lt;canvas id=&quot;output&quot;&gt;&lt;/canvas&gt; &lt;video id=&quot;video&quot; playsinline=&quot;&quot; style=&quot;-webkit-transform: scaleX(-1); transform: scaleX(-1); visibility: hidden; width: auto; height: auto;&quot;&gt;
        &lt;/video&gt;&lt;/div&gt;
&lt;div id=&quot;emo&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;!-- Require the peer dependencies of hand-pose-detection. --&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-core&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-converter&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;!-- You must explicitly require a TF.js backend if you're not using the TF.js union bundle. --&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-backend-webgl&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow-models/hand-pose-detection&quot;&gt;&lt;/script&gt;
&lt;/p&gt;
&lt;!--&lt;script src=&quot;https://tistory3.daumcdn.net/tistory/5290078/skin/images/camera.js&quot;&gt;&lt;/script&gt; --&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;
&lt;script&gt;
/**
 * @license
 * Copyright 2021 Google LLC. All Rights Reserved.
 * Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an &quot;AS IS&quot; BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * =============================================================================
 */



let detector, camera, stats;
let startInferenceTime, numInferences = 0;
let inferenceTimeSum = 0, lastPanelUpdate = 0;

const fingerLookupIndices = {
  thumb: [0, 1, 2, 3, 4],
  indexFinger: [0, 5, 6, 7, 8],
  middleFinger: [0, 9, 10, 11, 12],
  ringFinger: [0, 13, 14, 15, 16],
  pinky: [0, 17, 18, 19, 20],
}; // for rendering each finger as a polyline

function isiOS() {
  return /iPhone|iPad|iPod/i.test(navigator.userAgent);
}

function isAndroid() {
  return /Android/i.test(navigator.userAgent);
}

function isMobile() {
  return isAndroid() || isiOS();
}

class Camera {
  constructor() {
    this.video = document.getElementById('video');
    this.canvas = document.getElementById('output');
    this.ctx = this.canvas.getContext('2d');
  }

  /**
   * Initiate a Camera instance and wait for the camera stream to be ready.
   */
  static async setupCamera() {
    if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
      throw new Error(
        'Browser API navigator.mediaDevices.getUserMedia not available');
    }

    const $size = { width: 640, height: 480 };
    const $m_size = { width: 360, height: 270 };
    const videoConfig = {
      'audio': false,
      'video': {
        facingMode: 'user',
        // Only setting the video to a specified size for large screen, on
        // mobile devices accept the default size.
        width: isMobile() ? $m_size.width : $size.width,
        height: isMobile() ? $m_size.height : $size.height,
      }
    };

    const stream = await navigator.mediaDevices.getUserMedia(videoConfig);

    const camera = new Camera();
    camera.video.srcObject = stream;

    await new Promise((resolve) =&gt; {
      camera.video.onloadedmetadata = () =&gt; {
        resolve(video);
      };
    });

    camera.video.play();

    const videoWidth = camera.video.videoWidth;
    const videoHeight = camera.video.videoHeight;
    // Must set below two lines, otherwise video element doesn't show.
    camera.video.width = videoWidth;
    camera.video.height = videoHeight;

    camera.canvas.width = videoWidth;
    camera.canvas.height = videoHeight;
    const canvasContainer = document.querySelector('.canvas-wrapper');
    canvasContainer.style = `width: ${videoWidth}px; height: ${videoHeight}px`;

    // Because the image from camera is mirrored, need to flip horizontally.
    camera.ctx.translate(camera.video.videoWidth, 0);
    camera.ctx.scale(-1, 1);

    return camera;
  }

  drawCtx() {
    this.ctx.drawImage(
      this.video, 0, 0, this.video.videoWidth, this.video.videoHeight);
  }

  clearCtx() {
    this.ctx.clearRect(0, 0, this.video.videoWidth, this.video.videoHeight);
  }

  /**
   * Draw the keypoints on the video.
   * @param hands A list of hands to render.
   */
  drawResults(hands) {
    // Sort by right to left hands.
    hands.sort((hand1, hand2) =&gt; {
      if (hand1.handedness &lt; hand2.handedness) return 1;
      if (hand1.handedness &gt; hand2.handedness) return -1;
      return 0;
    });

    // Pad hands to clear empty scatter GL plots.
    while (hands.length &lt; 2) hands.push({});

    for (let i = 0; i &lt; hands.length; ++i) {

      this.drawResult(hands[i]);
    }
  }

  /**
   * Draw the keypoints on the video.
   * @param hand A hand with keypoints to render.
   * @param ctxt Scatter GL context to render 3D keypoints to.
   */
  drawResult(hand) {
    if (hand.keypoints != null) {
      this.drawKeypoints(hand.keypoints, hand.handedness);
      const emo_type = this.drawEmoticon(hand.keypoints)

      if (emo_type == 'up') {
        emo.innerHTML = '&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;friends1&quot; data-emoticon-name=&quot;032&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/032.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/friends1/large/032.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;';
      }
      else if (emo_type == 'down') {
        emo.innerHTML = '&lt;figure contenteditable=&quot;false&quot; data-ke-type=&quot;emoticon&quot; data-ke-align=&quot;alignCenter&quot; data-emoticon-type=&quot;niniz&quot; data-emoticon-name=&quot;029&quot; data-emoticon-isanimation=&quot;false&quot; data-emoticon-src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/029.gif&quot;&gt;&lt;img src=&quot;https://t1.daumcdn.net/keditor/emoticon/niniz/large/029.gif&quot; width=&quot;150&quot; /&gt;&lt;/figure&gt;'
      }
      else {
        emo.innerHTML = '&lt;p&gt;&lt;/p&gt;'
      }
    }


  }
  drawEmoticon(keypoints) {
    const keypointsArray = keypoints;

    const thumb_tip = keypointsArray[4].y;
    const thumb_ip = keypointsArray[3].y;
    const thumb_mcp = keypointsArray[2].y;
    const index_finger_mcp = keypointsArray[5].y;
    const middle_finger_mcp = keypointsArray[9].y;
    const ring_finger_mcp = keypointsArray[13].y;
    const pinky_finger_mcp = keypointsArray[17].y;

    if (thumb_tip &lt; thumb_ip
      &amp;&amp; thumb_ip &lt; thumb_mcp
      &amp;&amp; index_finger_mcp &lt; middle_finger_mcp
      &amp;&amp; middle_finger_mcp &lt; ring_finger_mcp
      &amp;&amp; ring_finger_mcp &lt; pinky_finger_mcp
      &amp;&amp; thumb_ip &lt; pinky_finger_mcp) {
      return this.is_up_or_down(keypoints, true)

    }
    else if (thumb_tip &gt; thumb_ip
      &amp;&amp; thumb_ip &gt; thumb_mcp
      &amp;&amp; index_finger_mcp &gt; middle_finger_mcp
      &amp;&amp; middle_finger_mcp &gt; ring_finger_mcp
      &amp;&amp; ring_finger_mcp &gt; pinky_finger_mcp
      &amp;&amp; thumb_ip &gt; pinky_finger_mcp) {
      return this.is_up_or_down(keypoints, false)
    }
    else {
      return 'none'
    }
  }

  is_up_or_down(keypoints, is_up) {
    const keypointsArray = keypoints;
    // for x axis
    const wrist = keypointsArray[0].x;
    const index_finger_pip = keypointsArray[6].x;
    const index_finger_tip = keypointsArray[8].x;
    const middle_finger_pip = keypointsArray[10].x;
    const middle_finger_tip = keypointsArray[12].x;
    const ring_finger_pip = keypointsArray[14].x;
    const ring_finger_tip = keypointsArray[16].x;

    if (
      (wrist &gt; index_finger_pip
        &amp;&amp; index_finger_tip &gt; index_finger_pip
        &amp;&amp; middle_finger_tip &gt; middle_finger_pip
        &amp;&amp; ring_finger_tip &gt; ring_finger_pip)
      || (wrist &lt; index_finger_pip
        &amp;&amp; index_finger_tip &lt; index_finger_pip
        &amp;&amp; middle_finger_tip &lt; middle_finger_pip
        &amp;&amp; ring_finger_tip &lt; ring_finger_pip)) {
      if (is_up == true) {
        return 'up'
      }
      else {
        return 'down'
      }
    }
    else {
      return 'none'
    }
  }


  /**
   * Draw the keypoints on the video.
   * @param keypoints A list of keypoints.
   * @param handedness Label of hand (either Left or Right).
   */
  drawKeypoints(keypoints, handedness) {
    const keypointsArray = keypoints;
    this.ctx.fillStyle = handedness === 'Left' ? 'Red' : 'Blue';
    this.ctx.strokeStyle = 'White';
    this.ctx.lineWidth = 2;

    for (let i = 0; i &lt; keypointsArray.length; i++) {
      const y = keypointsArray[i].x;
      const x = keypointsArray[i].y;
      this.drawPoint(x - 2, y - 2, 3);
    }

    const fingers = Object.keys(fingerLookupIndices);
    for (let i = 0; i &lt; fingers.length; i++) {
      const finger = fingers[i];
      const points = fingerLookupIndices[finger].map(idx =&gt; keypoints[idx]);
      this.drawPath(points, false);
    }
  }

  drawPath(points, closePath) {
    const region = new Path2D();
    region.moveTo(points[0].x, points[0].y);
    for (let i = 1; i &lt; points.length; i++) {
      const point = points[i];
      region.lineTo(point.x, point.y);
    }

    if (closePath) {
      region.closePath();
    }
    this.ctx.stroke(region);
  }

  drawPoint(y, x, r) {
    this.ctx.beginPath();
    this.ctx.arc(x, y, r, 0, 2 * Math.PI);
    this.ctx.fill();
  }


}

async function createDetector() {
  const hands = handPoseDetection.SupportedModels.MediaPipeHands;

  return handPoseDetection.createDetector(hands, {
    runtime: 'tfjs',
    modelType: 'full', //or lite
    maxHands: 1, // or 2~10
  })

}


async function renderResult() {
  if (camera.video.readyState &lt; 2) {
    await new Promise((resolve) =&gt; {
      camera.video.onloadeddata = () =&gt; {
        resolve(video);
      };
    });
  }

  let hands = null;

  if (detector != null) {

    try {
      hands = await detector.estimateHands(
        camera.video,
        { flipHorizontal: false });
    } catch (error) {
      detector.dispose();
      detector = null;
      alert(error);
    }
  }

  camera.drawCtx();

  if (hands &amp;&amp; hands.length &gt; 0) {
    camera.drawResults(hands);
  }
}

async function renderPrediction() {
  await renderResult();

  rafId = requestAnimationFrame(renderPrediction);
};

async function app() {

  camera = await Camera.setupCamera();
  console.log(tf.getBackend());
  detector = await createDetector();
  console.log(tf.getBackend());
  renderPrediction();
};

app();

&lt;/script&gt;
&lt;/p&gt;</description>
      <category>AI Engineering/TensorFlow</category>
      <category>TensorFlow.js</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/44</guid>
      <comments>https://da2so.tistory.com/44#entry44comment</comments>
      <pubDate>Wed, 23 Mar 2022 23:16:35 +0900</pubDate>
    </item>
    <item>
      <title>TensorFlow.js (1) - TensorFlow.js 이해 및 detection 예제</title>
      <link>https://da2so.tistory.com/42</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. TensorFlow.js 란??&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TenosorFlow.js는 &lt;b&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;javascript를 기반으로 하여 웹환경에서 사용되는 머신러닝 라이브러리&lt;/span&gt;&lt;/b&gt;입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 TensorFlow.js의 특징&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Web 기술과의 통합&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;TensorFlow.js는 javascript로 작성되므로 웹 브라우저에서 ML model을 별도의 수정없이 동작가능하게 해줌&lt;/li&gt;
&lt;li&gt;ML 애플리케이션 배포에 용이&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Web browser 상에서의 다수의 backend&amp;nbsp; 지원&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;WebGL [GPU]&lt;/b&gt;&lt;/span&gt;: 웹 브라우저에서 GPU를 사용할 수 있게 해주는 표준 명세서로 GPU acceleration가능함. (3MB 이상의 ML모델에 적합)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;Web Assembly (WASM) [CPU]&lt;/span&gt;&lt;/b&gt;: CPU performance의 향상시킬수 있다는 특징. (3MB 이하의 ML 모델사용 시 WASM이 WebGL보다 빠름)&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;CPU execution&lt;/b&gt;&lt;/span&gt;: CPU로 inference가 진행되며 3중에 가장 느림&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;TensorFlow python API와의 호환&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;TensorFlow로 학습된 모델과 호환되도록 API도 지원&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;data privacy&amp;nbsp;&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&amp;nbsp;TensorFlow.js의 client-side machine learning 기술이 서버로 data나 model을 전송하지 않고 ML service를 사용할 수 있게끔 해줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.2 TensorFlow.js는 어디에 사용하지??&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Javascript를 기반으로 하기 때문에 다양한 platform에서 쉽게 TensorFlow.js를 사용가능합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt; Client-side in the web browser (using Javascript)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;nbsp;Server-side even IoT devices (using Node.js)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;nbsp;Desktop apps (using Electron)&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&amp;nbsp;Native mobile apps (using React Native)&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. TensorFlow.js 설치&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TensorFlow.js의 설치방법은 2가지입니다.&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: #8a3db6;&quot;&gt;&lt;b&gt;CDN(content delivery network)을 통해 배포되넌 축소된(minified) javascript code 사용&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;설치&lt;/b&gt;: &lt;code class=&quot;javascript&quot;&gt;&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js&quot; type=&quot;text/javascript&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;CDN service는 빠르며 정적인 리소스를 사용자들에게 제공하기에 안정적&lt;/li&gt;
&lt;li&gt;공유 네트워크를 사용할 수 있는 환경에 적합 (인터넷이 되어야한다고 이해하세용ㅎ)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;npm 같이 package manager를 통해 배포되는 번들(bundle) package 사용&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;&lt;b&gt;설치&lt;/b&gt;: &lt;code&gt;npm install @tensorflow/tfjs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;공유 네트워크 사용할 수 없는 환경에서 사용되며 애플리케이션에 TensorFlow.js를 직접 포함시키는 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘중에 한 방법으로 설치를 하셨다면 TensorFlow.js의 각종 클래스는 tf라는 이름의 namespace하위에서 찾을 수 있음을 알아두시면 됩니다. (저는 CDN방법으로 실습을 이어나갈 예정입니다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. TensorFlow.js를 이용한 Detection model 예제&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘 알려드릴 detection model 실습은 TensorFlow.js 에코시스템을 기반으로 진행하므로 먼저 TensorFlow.js 에코시스템부터 알아보져!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 TensorFlow.js 에코시스템&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;TensorFlow.js의 에코시스템을 통해 다양한 고수준 라이브러리를 사용할 수 있으며 이는 새로운 연구 성과(ML model, algorithm, ...)을 빠르게 도입할 수 있게 해줍니다. 고수준 라이브러리 중에서는 pretrained된 ML모델을 제공하는 &lt;a href=&quot;https://github.com/tensorflow/tfjs-models&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;tfjs-models&lt;/b&gt;&lt;/span&gt;&lt;/a&gt;이 있고 다양한 종류의 데이터셋을 불러올 수 있는 &lt;span style=&quot;color: #1a5490;&quot;&gt;&lt;b&gt;tfjs-data&lt;/b&gt;&lt;/span&gt;도 있고 AI 비전공자도 쉽게 ML framework를 사용할 수 있게 해주는 &lt;b&gt;&lt;span style=&quot;color: #1a5490;&quot;&gt;ML5.js&lt;/span&gt;&lt;/b&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;저는 이중에서 tfjs-models 라이브러리에서 제공하는 detection model(ssdlite)을 사용하여 실습을 진행할것입니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 TensorFlow.js 으로 detection model inference&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹브라우저를 통해 inference를 하기 위해서는 html, javascript, css code가 필요합니다. (사실 실습을 진행하는 데 javascript(React) code가 거의 다입니다ㅎㅎ..) 저는 이번에 티스토리 블로그 글에 맞는 inference 코드를 만들어보겠습니다.&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;먼저 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;HTML skeleton&lt;/b&gt; &lt;/span&gt;부터 보시죠!&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647479550998&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;p data-ke-size=&quot;size16&quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/p&amp;gt;

&amp;lt;noscript&amp;gt;
&amp;lt;!--클라이언트 사이드 스크립트(client-side scripts)를 사용하지 않도록 설정했거나
스크립트를 지원하지 않는 브라우저를 위한 별도의 콘텐츠를 정의할 때 사용합니다. --&amp;gt;
      You need to enable JavaScript to run this app.
&amp;lt;/noscript&amp;gt;
&amp;lt;div id=&quot;inference&quot;&amp;gt;&amp;amp;nbsp;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블로그용이다 보니까 &lt;code&gt;&amp;lt;html&amp;gt;...&amp;lt;/html&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;head&amp;gt;...&amp;lt;/head&amp;gt;&lt;/code&gt;과 &lt;code&gt;&amp;lt;body&amp;gt;...&amp;lt;/body&amp;gt;&lt;/code&gt;에 대한 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;DOM(&lt;/b&gt;&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Document Object Model)&lt;/b&gt;&lt;/span&gt;을 글안에 작성안해도 이미 들어가있습니다!! (짧아서 좋구만 허허)&lt;/span&gt;&lt;span style=&quot;background-color: #ffffff; color: #4d5156;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;문서 객체 모델(DOM)이란?&lt;/b&gt;&lt;br /&gt;문서 객체 모델(DOM)은 XML이나 HTML 문서에 접근하기 위한 일종의 인터페이스입니다. 이 객체 모델은 문서 내의 모든 요소를 정의하고, 각각의 요소에 접근하는 방법을 제공합니다. (e.g. &amp;lt;html&amp;gt;, &amp;lt;a&amp;gt;, &amp;lt;p&amp;gt;, &amp;lt;div&amp;gt;, ...)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&quot;&amp;lt;div&amp;gt;의 inference라는 id를 가지는 element는 머하는 얘지??&quot;&lt;/i&gt;하는 생각하실텐데요. 뒤의 react코드를 작성해보면 알게되니 기억해두고 다음을 읽어봅시다! 먼저 react를 쓰기위해 필요한 javascript들을 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;CDN(Contents Delivery Network)&lt;/b&gt;&lt;/span&gt;으로 가져와보죠.&lt;/p&gt;
&lt;pre id=&quot;code_1647483525697&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;!-- react에 필요한 javascript --&amp;gt;
&amp;lt;script src=&quot;https://unpkg.com/react@17/umd/react.development.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;https://unpkg.com/react-dom@17/umd/react-dom.development.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- html에서 react compile에 필요한 javascript --&amp;gt;
&amp;lt;script src=&quot;https://unpkg.com/@babel/standalone/babel.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;!-- tensorflow.js 사용하기 위한 javascript --&amp;gt;
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.1&quot;&amp;gt; &amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd&quot;&amp;gt; &amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용되는 javascript의 역할은 주석으로 써놓았습니다. 이제야 필요한 준비물이 모두 갖추어 졌으니 react로 가즈아! (부분부분 단락나눠가면서 설명 진행할게여)&lt;/p&gt;
&lt;pre id=&quot;code_1647484306894&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;script type=&quot;text/babel&quot;&amp;gt;
      class App extends React.Component {
        //React.createRef() 는 특정 노드나 컴포넌트에 레퍼런스 값을 만들어주는 것
        //Ref를 통해 인스턴스를 생성 후 render 코드 블록 쪽만 리랜더링후 다시 실행
        videoRef = React.createRef();
        canvasRef = React.createRef();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;&amp;lt;script type=&quot;text/babel&quot;&amp;gt;&lt;/code&gt; 의 text/babel은 react(javascript)를 compile을 담당하고 아래부터 react code입니다.&amp;nbsp; &lt;code&gt;App&lt;/code&gt;이라는 class이름을 설정하고 React의 component로 지정합니다. 그리고 &lt;code&gt;videoRef&lt;/code&gt;는 실시간으로 들어오는 webcam의 stream값을 받아 지속적으로 리랜더링 (&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;task A라 칭함&lt;/b&gt;&lt;/span&gt;)하고 &lt;code&gt;canvasRef&lt;/code&gt;는 detection 결과를 visualization하는 역할(&lt;b&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;task B라 칭함&lt;/span&gt;&lt;/b&gt;)라 칭함)을 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1647487775070&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        componentDidMount() {
          if (navigator.mediaDevices &amp;amp;&amp;amp; navigator.mediaDevices.getUserMedia) {
            const webCamPromise = navigator.mediaDevices //웹캠 사용하기
              .getUserMedia({
              	//가져올 미디어에 대한 설정
                audio: false,
                video: {
                  facingMode: &quot;user&quot;
                }
              })
              .then((stream) =&amp;gt; {
                window.stream = stream;
                this.videoRef.current.srcObject = stream; //웹캠 stream을 videoRef에 할당
                return new Promise((resolve, reject) =&amp;gt; {
                  this.videoRef.current.onloadedmetadata = () =&amp;gt; {
                    resolve();
                  };
                });
              });
            const modelPromise = cocoSsd.load(); //detection 모델 로드
            Promise.all([modelPromise, webCamPromise])
              .then((values) =&amp;gt; { //values: [modelPromise, webCamPromise]
                this.detectFrame(this.videoRef.current, values[0]);
              })
              .catch((error) =&amp;gt; {
                console.error(error);
              });
          }
        }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;componentDidMount()&lt;/code&gt; 는 클래스를 생성하고 한번만 실행되는 함수인데 &lt;b&gt;&lt;i&gt;DOM을 제어가능한 생성자&lt;/i&gt;&lt;/b&gt;입니다.&amp;nbsp;&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;if구문을 통해 &lt;code&gt;navigator.mediaDevices.getUserMedia&lt;/code&gt; 을 통해 유저에게 webcam을 사용요청을 하게 되고 요청을 승락하면 해당 getUserMedia함수를 실행시켜 반환값을 stream이라는 arugment로 넘겨 위에서 만든 &lt;code&gt;videoRef.current.srcObject&lt;/code&gt;에 할당하게 되면서 &lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;task A&lt;/b&gt;&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;그리고 &lt;code&gt;cocoSsd.load()&lt;/code&gt;을 통해 coco dataset으로 학습된 ssd 라는 detection 모델을 가져오게됩니다. 그리고 다음 &lt;code&gt;Promise.all&lt;/code&gt;을 통해 인자안의 모든 promise인 &lt;code&gt;modelPromise&lt;/code&gt;와 &lt;code&gt;webCamPromise&lt;/code&gt;가 준비될때까지 기다리게 됩니다. 준비가 완료되면 &lt;code&gt;.then&lt;/code&gt;을 통해 modelPromise와 webCamPromise가 &lt;code&gt;values&lt;/code&gt;라는 이름으로 wrapping됩니다. (values[0] 은 modelPromise임) 마지막으로 이제 webcam의 stream(videoRef.current)와 detection model(values[0]을 인자로 주어 &lt;code&gt;detectFrame&lt;/code&gt;을 실행합니다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647489506800&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        detectFrame = (video, model) =&amp;gt; {
          model.detect(video).then((predictions) =&amp;gt; { //model.detect(video)의 return은 predictions으로 전달
            this.renderPredictions(predictions);
            requestAnimationFrame(() =&amp;gt; { //지속적으로 detectFrame함수 을 실행시킴
              this.detectFrame(video, model);
            });
          });
        };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;detectFrame&lt;/code&gt;이라는 함수선언을 한것이고 &lt;code&gt;(video, model)&lt;/code&gt;이라는 이름의 2가지 인자를 받는다고 정의한 것이고 해당 인자들로 detect를 실행하고 반환값인 &lt;code&gt;predictions&lt;/code&gt;을 인자로 &lt;code&gt;renderPredictions&lt;/code&gt;함수를 호출하게 됩니다. 여기서 detect는 webcam의 실시간 frame image을 입력으로 ssd모델의 detection을 수행하는 것이죠. &lt;code&gt;requestAnimationFrame()&lt;/code&gt;을 통해 지속적으로 wrapping되어 있는 &lt;code&gt;detectFrame&lt;/code&gt;함수를 실행을 하게합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1647489686418&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        renderPredictions = (predictions) =&amp;gt; {
          const ctx = this.canvasRef.current.getContext(&quot;2d&quot;);
          ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
          // 폰트 설정
          const font = &quot;16px sans-serif&quot;;
          ctx.font = font;
          ctx.textBaseline = &quot;top&quot;;
          predictions.forEach((prediction) =&amp;gt; {
            const x = prediction.bbox[0];
            const y = prediction.bbox[1];
            const width = prediction.bbox[2];
            const height = prediction.bbox[3];
            // bounding box 그리기
            ctx.strokeStyle = &quot;#0072B5&quot;;
            ctx.lineWidth = 2;
            ctx.strokeRect(x, y, width, height);
            // label background 그리기
            ctx.fillStyle = &quot;#0072B5&quot;;
            const textWidth = ctx.measureText(prediction.class).width;
            const textHeight = parseInt(font, 10); // base 10
            ctx.fillRect(x, y, textWidth + 4, textHeight + 4);
          });

          predictions.forEach((prediction) =&amp;gt; {
            const x = prediction.bbox[0];
            const y = prediction.bbox[1];
            // label text 그리기
            ctx.fillStyle = &quot;#000000&quot;;
            ctx.fillText(prediction.class, x, y);
          });
        };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 말씀드린 task B를 하도록 하기위해 &lt;code&gt;canvasRef&lt;/code&gt;를 여기서 사용하게 됩니다. &lt;code&gt;predictions&lt;/code&gt;이라는 인자를 받아 detection result을 그리는 &lt;code&gt;renderPredictions&lt;/code&gt;함수입니다.&amp;nbsp; 한 frame image에 여러 detect결과값이 있을 수 있으므로 &lt;code&gt;forEach&lt;/code&gt;를 사용한것입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;prediction.bbox:&lt;/b&gt; detection된 객체의 bounding box의 정보를 담고 있음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;prediction.bbox[0]&lt;/b&gt;: bounding box의 x축 좌표의 center값&lt;/li&gt;
&lt;li&gt;&lt;b&gt;prediction.bbox[1]&lt;/b&gt;: bounding box의 y축 좌표의 center값&lt;/li&gt;
&lt;li&gt;&lt;b&gt;prediction.bbox[2]&lt;/b&gt;: bounding box의 width(너비)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;prediction.bbox[3]&lt;/b&gt;: bounding box의 height(높이)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;prediction.class:&lt;/b&gt; bounding box의 클래스 이름&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1647490148341&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;        render() {
          return (
            &amp;lt;div&amp;gt;
              &amp;lt;video
                autoPlay
                playsInline
                muted
                ref={this.videoRef}
                width=&quot;600&quot;
                height=&quot;450&quot;
              /&amp;gt;
              &amp;lt;canvas
                className=&quot;tfjs_1_size&quot;
                ref={this.canvasRef}
                width=&quot;600&quot;
                height=&quot;450&quot;
              /&amp;gt;
            &amp;lt;/div&amp;gt;
          );
        }
      }

      const rootElement = document.getElementById(&quot;inference&quot;);
      ReactDOM.render(&amp;lt;App /&amp;gt;, rootElement);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;render()&lt;/code&gt; 함수를 통해 html에 랜더링하게 되는 부분입니다. webcam의 실시간 frame image을 담는 &lt;code&gt;videoRef&lt;/code&gt;는 ref인자로 들어가 video라는 DOM을 실행하게 되는것이고 detection result를 기록하는 &lt;code&gt;canvasRef&lt;/code&gt;는 canvas를 실행하게 됩니다.&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;inference라는 id를 가진 element(html skeleton에서 만든 element)를 가져와 &lt;code&gt;rootElement&lt;/code&gt;에 할당하고 해당 element를 대상으로 App라는 클래스의 render함수의 return값으로 html을 rendering시키는 것입니다. 이제 마지막으로 &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;css부분만 작업&lt;/b&gt;&lt;/span&gt;하면 끝!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1647491441085&quot; class=&quot;css&quot; data-ke-language=&quot;css&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.tfjs_1_size {
  position: relative;
  top: -450px;
  left: 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;css작업인데요. 위 canvas에서 &lt;code&gt;className&lt;/code&gt;과 동일한 이름으로 해당 css를 canvas에 적용하게됩니다. 블로그 특성상 &lt;code&gt;video&lt;/code&gt;라는 element밑에 &lt;code&gt;canvas&lt;/code&gt;가 위치하게되는데 이는 canvas와 video가 겹치게 보이지 않아 detection되지 않는 것처럼 보이게 되죠. 그래서 이를 위해 위의 css가 필요합니다.&amp;nbsp; 그래서 video의 height만큼 canvas의 위치를 올려주면 완료되는 코드입니다.&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;이제 모든것이 완료되었습니다. 위에 설명드린 코드는 &lt;b&gt;&lt;a href=&quot;https://github.com/da2so/tfjs_tutorial&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;tfjs_tutorial&lt;/a&gt;&lt;/b&gt; 에 ssd_detection.html이라는 파일에 모아두었습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 detection 결과&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;webcam사용을 승낙하셧다면 아래에 detection결과가 짜짠!!!! (가끔 detection이 안될경우가 있는데 새로고침하시면 될거예요..ㅠ)&lt;/p&gt;
&lt;noscript&gt;
      You need to enable JavaScript to run this app.
&lt;/noscript&gt;
&lt;div id=&quot;inference&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;script src=&quot;https://unpkg.com/react@17/umd/react.development.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/react-dom@17/umd/react-dom.development.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://unpkg.com/@babel/standalone/babel.min.js&quot;&gt;&lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.1&quot;&gt; &lt;/script&gt;
&lt;script src=&quot;https://cdn.jsdelivr.net/npm/@tensorflow-models/coco-ssd&quot;&gt; &lt;/script&gt;
&lt;script type=&quot;text/babel&quot;&gt;
      class App extends React.Component {
        //React.createRef() 는 특정 노드나 컴포넌트에 레퍼런스 값을 만들어주는 것
        //Ref를 통해 인스턴스를 생성 후 render 코드 블록 쪽만 리랜더링후 다시 실행
        videoRef = React.createRef();
        canvasRef = React.createRef();

        componentDidMount() {
          if (navigator.mediaDevices &amp;&amp; navigator.mediaDevices.getUserMedia) {
            const webCamPromise = navigator.mediaDevices //웹캠 사용하기
              .getUserMedia({
              	//가져올 미디어에 대한 설정
                audio: false,
                video: {
                  facingMode: &quot;user&quot;
                }
              })
              .then((stream) =&gt; {
                window.stream = stream;
                this.videoRef.current.srcObject = stream; //웹캠 stream을 videoRef에 할당
                return new Promise((resolve, reject) =&gt; {
                  this.videoRef.current.onloadedmetadata = () =&gt; {
                    resolve();
                  };
                });
              });
            const modelPromise = cocoSsd.load(); //detection 모델 로드
            Promise.all([modelPromise, webCamPromise])
              .then((values) =&gt; {
                this.detectFrame(this.videoRef.current, values[0]);
              })
              .catch((error) =&gt; {
                console.error(error);
              });
          }
        }

        detectFrame = (video, model) =&gt; {
          model.detect(video).then((predictions) =&gt; {
            this.renderPredictions(predictions);
            requestAnimationFrame(() =&gt; { 
              this.detectFrame(video, model); //지속적으로 webcam과 
            });
          });
        };

        renderPredictions = (predictions) =&gt; {
          const ctx = this.canvasRef.current.getContext(&quot;2d&quot;);
          ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
          // Font options.
          const font = &quot;16px sans-serif&quot;;
          ctx.font = font;
          ctx.textBaseline = &quot;top&quot;;
          predictions.forEach((prediction) =&gt; {
            const x = prediction.bbox[0];
            const y = prediction.bbox[1];
            const width = prediction.bbox[2];
            const height = prediction.bbox[3];
            // Draw the bounding box.
            ctx.strokeStyle = &quot;#0072B5&quot;;
            ctx.lineWidth = 2;
            ctx.strokeRect(x, y, width, height);
            // Draw the label background.
            ctx.fillStyle = &quot;#0072B5&quot;;
            const textWidth = ctx.measureText(prediction.class).width;
            const textHeight = parseInt(font, 10); // base 10
            ctx.fillRect(x, y, textWidth + 4, textHeight + 4);
          });

          predictions.forEach((prediction) =&gt; {
            const x = prediction.bbox[0];
            const y = prediction.bbox[1];
            // Draw the text last to ensure it's on top.
            ctx.fillStyle = &quot;#000000&quot;;
            ctx.fillText(prediction.class, x, y);
          });
        };
		
        render() {
          return (
            &lt;div&gt;
              &lt;video
                autoPlay
                playsInline
                muted
                ref={this.videoRef}
                width=&quot;600&quot;
                height=&quot;450&quot;
              /&gt;
              &lt;canvas
                className=&quot;tfjs_1_size&quot;
                ref={this.canvasRef}
                width=&quot;600&quot;
                height=&quot;450&quot;
              /&gt;
            &lt;/div&gt;
          );
        }
      }

      const rootElement = document.getElementById(&quot;inference&quot;);
      ReactDOM.render(&lt;App /&gt;, rootElement);
&lt;/script&gt;</description>
      <category>AI Engineering/TensorFlow</category>
      <category>TensorFlow.js</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/42</guid>
      <comments>https://da2so.tistory.com/42#entry42comment</comments>
      <pubDate>Thu, 17 Mar 2022 13:27:13 +0900</pubDate>
    </item>
    <item>
      <title>Airflow (2) - DAG workflow 작성 및 실행</title>
      <link>https://da2so.tistory.com/40</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Workflow(DAG) 작성 및 실행&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제부터 직접 python을 통해 workflow를 DAG형태로 만들어보고 해당 workflow를 airflow에서 실행하고 이해해봅시다. airflow안에서 yolov5 model으로 inference하는 것을 목적으로 하겠습니다. 따라오시죠!&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 실행 환경 준비&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행 환경을 다음과 같이 셋팅합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;airflow webserver 실행 (localhost의 8080포트로 연결)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;airflow webserver -p 8080&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;airflow scheduler 실행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;airflow scheduler&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;DAG file을 생성할 저장소 생성 (맥북 기준)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;cd ~/airflow/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mkdir dags&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;206&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/daUcN0/btrv8chgLWM/MksAEPpz3mvNaxkjksQi0K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/daUcN0/btrv8chgLWM/MksAEPpz3mvNaxkjksQi0K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/daUcN0/btrv8chgLWM/MksAEPpz3mvNaxkjksQi0K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdaUcN0%2Fbtrv8chgLWM%2FMksAEPpz3mvNaxkjksQi0K%2Fimg.png&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; alt=&quot;create dags folder&quot; loading=&quot;lazy&quot; width=&quot;749&quot; height=&quot;81&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1906&quot; data-origin-height=&quot;206&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;airflow.cfg&lt;/b&gt;&lt;/span&gt; 는 Airflow 관련 설정에 대한 파일, &lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;airflow.db&lt;/b&gt;&lt;/span&gt;은 sqlite database파일이며 이는 airflow설치 시 자동으로 사용되는 db이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.2 workflow 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 workflow 즉, DAG를 실행시키고 싶은 지에 대한 정의는 python 파일을 통해 가능하다고 말씀드렸죠. 그래서 이번에 python을 통해 다음과 같은 workflow를 만들 수 있도록 해보겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1616&quot; data-origin-height=&quot;579&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Yb38M/btrvX8nFncU/YTrPN07bAOlfkE1uukzuMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Yb38M/btrvX8nFncU/YTrPN07bAOlfkE1uukzuMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Yb38M/btrvX8nFncU/YTrPN07bAOlfkE1uukzuMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYb38M%2FbtrvX8nFncU%2FYTrPN07bAOlfkE1uukzuMk%2Fimg.png&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; alt=&quot;Pipeline for yolov5 inference&quot; loading=&quot;lazy&quot; width=&quot;787&quot; height=&quot;282&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1616&quot; data-origin-height=&quot;579&quot;/&gt;&lt;/span&gt;&lt;/figure&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;해당 workflow의 목적은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: crimson;&quot;&gt;&lt;b&gt;목적&lt;/b&gt;&lt;/span&gt;: &lt;b&gt;랜덤하게 사람 이미지를 다운로드하고 해당 이미지를 yolov5 model로 inference하고 해당 결과를 저장&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;make_image_store&lt;/b&gt;: 이미지가 저장될 장소를 만듬&lt;/li&gt;
&lt;li&gt;&lt;b&gt;download_person_image&lt;/b&gt;: unsplash으로부터 사람(person) 사진을 이미지 저장소에 저장
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;unsplash에서는 무료로 사진을 다운받을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Inference_using_yolov5&lt;/b&gt;: yolov5 model을 다운받아 위의 이미지로 inference하고 결과를 이미지 저장소에 저장함&lt;/li&gt;
&lt;li&gt;해당 workflow는 한번만 실행되도록 schedule_interval을 &lt;b&gt;None&lt;/b&gt;으로 설정함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. workflow 작성 in Python&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 DAG 정의&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만든 &lt;b&gt;~/airflow/dags&lt;/b&gt;폴더안에 workflow가 정의된 python파일을 만들면 webserver에서 자동으로 해당 DAG를 등록해줍니다. (만들고 webserver에 등록되는(보여지는) 데 30초정도 걸려요!) 그럼 이제 해당 폴더안에 yolov5_inference.py라는 파일을 만들겠습니다. 그리고 다음과 같이 작성해봅시다.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;#yolov5_inference.py
from datetime import datetime
from pathlib import Path
from airflow import DAG
from airflow.operators.python import PythonOperator
from airflow.operators.bash import BashOperator


# DAG 정의
dag = DAG(
        dag_id=&quot;yolov5_inference&quot;, # dag의 고유 이름 (webserver에 표시될 dag 이름)
        description=&quot;Download person picture and inference it using yolov5&quot;, # dag 설명
        start_date=datetime(2022, 1, 1), #해당 pipeline 실행 시작 시간
        tags=[&quot;yolov5&quot;],
        schedule_interval=None, #해당 pipeline 실행 주기 (None으로 한번만 실행하도록 함)
        catchup=False # 이전에 실행되지 않았던 dag를 실행할지 말지 결정
        )&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 중요하게 볼 요소는 4가지입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;dag_id&lt;/b&gt;&lt;/span&gt;: dag의 고유 이름&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;start_date&lt;/b&gt;&lt;/span&gt;: dag의 실행 시작 날짜&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;schedule_interval&lt;/b&gt;&lt;/span&gt;: dag의 실행 주기
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;None일 경우 한번만 실행됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;catchup&lt;/b&gt;&lt;/span&gt;: 이전에 실행되지 않았던 dag를 실행할지 말지 결정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;False 값은 실행을 안함을 명시&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;start_date와 schedule_interval의 이해&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 start_date를 2022년1월1일 0시, schedule_interval이 @daily(하루마다 실행)이고 실제 dag를 trigger run하는 시간도 2022년1월1일 0시이라면 실제 dag가 실행되는 시간은 (start_date + schedule_interval)2022년1월2일 0시가 됩니다. 그리고 schedule_interval이 daily이므로 2022년1월3일 0시에는 2번째 dag run이 실행됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;768&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYAkmB/btrv4lT2glt/lJrDRarzDRkAZZnxm6hwF1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYAkmB/btrv4lT2glt/lJrDRarzDRkAZZnxm6hwF1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYAkmB/btrv4lT2glt/lJrDRarzDRkAZZnxm6hwF1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYAkmB%2Fbtrv4lT2glt%2FlJrDRarzDRkAZZnxm6hwF1%2Fimg.png&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; alt=&quot;airflow schedule explanation&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;294&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1464&quot; data-origin-height=&quot;768&quot;/&gt;&lt;/span&gt;&lt;/figure&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;아래는 schedule interval의 가능한 다른 옵션과 그에 대한 의미는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ciElEm/btrvZIIJ8og/iTX1OWjWlLDkQNW3pKuVXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ciElEm/btrvZIIJ8og/iTX1OWjWlLDkQNW3pKuVXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ciElEm/btrvZIIJ8og/iTX1OWjWlLDkQNW3pKuVXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FciElEm%2FbtrvZIIJ8og%2FiTX1OWjWlLDkQNW3pKuVXk%2Fimg.png&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; alt=&quot;crontab&quot; loading=&quot;lazy&quot; width=&quot;537&quot; height=&quot;272&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1216&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;catchup 이해&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 2022년1월1일부터 하루마다 주기로(@daily) dag run이 실행되고 있다가 2,3일에 서버가 다운되거나 dag의 코딩오류와 같은 특정 이유로 dag가 run이 되지않았다고 하였다고 해봅시다. 그리고 1월 4일에 해당 dag를 다시 실행시킬려고 할때 catchup를 True로 할경우 이전에 실행되지 않았던(2일,3일) dag run을 실행하면서 catchup하는 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;916&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bScZE4/btrv6j8Nhaq/HmwWrBg3nKRB79bGNr3IA0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bScZE4/btrv6j8Nhaq/HmwWrBg3nKRB79bGNr3IA0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bScZE4/btrv6j8Nhaq/HmwWrBg3nKRB79bGNr3IA0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbScZE4%2Fbtrv6j8Nhaq%2FHmwWrBg3nKRB79bGNr3IA0%2Fimg.png&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; alt=&quot;airflow catchup&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;338&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1518&quot; data-origin-height=&quot;916&quot;/&gt;&lt;/span&gt;&lt;/figure&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;2,3일에 실행되지 않았던 dag run때문에 2,3일에 대한 workflow 결과물이 없습니다. 이런 문제점을 해결하기 위해 4일에 catchup을 True로 해놓으면 실행되지 않았던 2,3일에 대한 dag run을 하면서 해당 날짜들에 결과물도 얻을 수 있는 것입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 DAG의 task 정의 (make_image_store+ download_person_picture)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 task들을 만들어 볼텐데요. &lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;task는 operator를 wrapping하고 있으며 실제로 operator를 통해 task가 이루어진&lt;/b&gt;&lt;/span&gt;다고 생각하시면 됩니다. 먼저 이미지를 저장할 저장소를 만들어 주기 위해 make_image_store의 task는 pythonOperator를 사용하게됩니다. &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;pythonOperator&lt;/b&gt;&lt;/span&gt;는 python함수를 실행시킬 수 있는 operator입니다. 그리고 이미지를 저장소를 만들어주었다면 해당 저장소에 이미지를 다운로드하는 것은 Bash 명령어를 사용하게 되므로 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;BashOperator&lt;/b&gt;&lt;/span&gt;을 사용하게 됩니다. (다음 코드는 위의 DAG정의 뒤에 붙여주시면 됩니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RmlA0/btrv3g6wJju/aGUXrY8LwVOCj87uECKKs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RmlA0/btrv3g6wJju/aGUXrY8LwVOCj87uECKKs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RmlA0/btrv3g6wJju/aGUXrY8LwVOCj87uECKKs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRmlA0%2Fbtrv3g6wJju%2FaGUXrY8LwVOCj87uECKKs1%2Fimg.png&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; alt=&quot;operator examples&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;244&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1143&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;IMAGE_DIR = '/tmp/images' # image 저장 장소 지정 (원하는 곳으로 지정)
def _make_img_store():
    Path(IMAGE_DIR).mkdir(exist_ok=True, parents=True)  # image가 저장될 장소 만듬

# task 정의
make_image_store = PythonOperator(
        task_id=&quot;make_image_store&quot;,
        python_callable=_make_img_store,
        dag=dag
    )
# task 정의
download_person_picture = BashOperator(
    task_id=&quot;download_person_picture&quot;,
    bash_command=f&quot;curl -L https://source.unsplash.com/random?person --output {Path(IMAGE_DIR)/'image.png'}&quot;,  # download person pic
    dag=dag,
)

make_image_store &amp;gt;&amp;gt; download_person_picture&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;make_image_store&lt;/b&gt;&lt;/span&gt;: pythonOperator를 사용하여 python 함수 실행(make_img_store)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;role: &lt;i&gt;/tmp/images&lt;/i&gt; 디렉토리를 생성함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;download_person_picture&lt;/b&gt;&lt;/span&gt;: BashOperator를 사용하여 curl 명령어 사용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;role: unsplash를 이용하여 random한 사람이미지를 다운로드하여 /tmp/images에 저장&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;make_image_store &amp;gt;&amp;gt; download_person_picture&lt;/b&gt;:&lt;/span&gt; task간의 dependency를 나타냄
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;make_image_store가 실행되고 나서야 download_person_picture이 실행됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼 코딩해놓았다면 webserver에 접속해보면 &lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;dag_id(yolov5_inference)&lt;/span&gt;&lt;/b&gt;이름으로 dag가 등록되어있음을 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;2130&quot; data-origin-height=&quot;418&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/siBPq/btrv6X5Ky09/kAVJYOiSzk32ck3ZwJkbn1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/siBPq/btrv6X5Ky09/kAVJYOiSzk32ck3ZwJkbn1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/siBPq/btrv6X5Ky09/kAVJYOiSzk32ck3ZwJkbn1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsiBPq%2Fbtrv6X5Ky09%2FkAVJYOiSzk32ck3ZwJkbn1%2Fimg.png&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; alt=&quot;yolov5_inference in airflow webserver&quot; loading=&quot;lazy&quot; width=&quot;738&quot; height=&quot;145&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;2130&quot; data-origin-height=&quot;418&quot;/&gt;&lt;/span&gt;&lt;/figure&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;생성한 yolov5_inference dag에서 &lt;b&gt;&lt;span style=&quot;color: #45b39d;&quot;&gt;tree 메뉴&lt;/span&gt;&lt;/b&gt;와 &lt;b&gt;&lt;span style=&quot;color: #45b39d;&quot;&gt;graph메뉴&lt;/span&gt;&lt;/b&gt;를 눌러보면 다음과 같은 화면을 확인가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1560&quot; data-origin-height=&quot;1154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uelNP/btrvZGYu35H/oOPij7tJ336bjmYdsK6hPk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uelNP/btrvZGYu35H/oOPij7tJ336bjmYdsK6hPk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uelNP/btrvZGYu35H/oOPij7tJ336bjmYdsK6hPk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuelNP%2FbtrvZGYu35H%2FoOPij7tJ336bjmYdsK6hPk%2Fimg.png&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; alt=&quot;tree and graph menu in airflow webserver&quot; loading=&quot;lazy&quot; width=&quot;680&quot; height=&quot;503&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1560&quot; data-origin-height=&quot;1154&quot;/&gt;&lt;/span&gt;&lt;/figure&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;뿐만아니라 &lt;b&gt;&lt;span style=&quot;color: #45b39d;&quot;&gt;code 메뉴&lt;/span&gt;&lt;/b&gt;를 통해 우리가 만든 코드는 확인가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pHTqX/btrv4JAqMo1/6vralvm8DlQV9l4fX2BD90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pHTqX/btrv4JAqMo1/6vralvm8DlQV9l4fX2BD90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pHTqX/btrv4JAqMo1/6vralvm8DlQV9l4fX2BD90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpHTqX%2Fbtrv4JAqMo1%2F6vralvm8DlQV9l4fX2BD90%2Fimg.png&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; alt=&quot;code in airflow webserver&quot; loading=&quot;lazy&quot; width=&quot;613&quot; height=&quot;414&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1344&quot; data-origin-height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;DAG run&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;webserver 오른쪽에 보면 실행버튼이 있을텐데 눌러서 이제 직접 dag run을 해보자. 정상적으로 실행되었다면 다음과 같은 화면으로 변경된다. 저는 2월 14일에 dag run을 시켰으므로 해당 시간으로 기록되고 schedule_interval이 None이므로 dag run을 한 시간에 한번만 실행되고 종료된다. (dependency에 따라 make_image_store가 실행되고 download_person_picture이 실행된다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;1068&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zFMY5/btrv4KMQDRX/o7fgkHfgGsgCxKWw2U00wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zFMY5/btrv4KMQDRX/o7fgkHfgGsgCxKWw2U00wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zFMY5/btrv4KMQDRX/o7fgkHfgGsgCxKWw2U00wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzFMY5%2Fbtrv4KMQDRX%2Fo7fgkHfgGsgCxKWw2U00wK%2Fimg.png&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; alt=&quot;execute airflow dag&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;534&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;954&quot; data-origin-height=&quot;1068&quot;/&gt;&lt;/span&gt;&lt;/figure&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;정상적으로 실행됨을 확인 했으니 실제로 /tmp/images에 이미지가 잘 저장되었는지 확인해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1560&quot; data-origin-height=&quot;1110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QUEP0/btrv8bCFZHq/C3TudjZtlDkPY4RePac1yK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QUEP0/btrv8bCFZHq/C3TudjZtlDkPY4RePac1yK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QUEP0/btrv8bCFZHq/C3TudjZtlDkPY4RePac1yK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQUEP0%2Fbtrv8bCFZHq%2FC3TudjZtlDkPY4RePac1yK%2Fimg.png&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; alt=&quot;check airflow dag runs&quot; loading=&quot;lazy&quot; width=&quot;606&quot; height=&quot;432&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1560&quot; data-origin-height=&quot;1110&quot;/&gt;&lt;/span&gt;&lt;/figure&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;현재까지 dag를 정의하고 task를 정의하고 task들이 실행됨을 확인하였다. 뒤에서 yolov5 task를 추가하고 새로 dag run을 위해 기존의 dag run의 record를 삭제(초기화)해보자. (cmd창에서하세용)&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;airflow dags delete yolov5_inference -y #yolov5_inference는 위에서 정의한 dag_id&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 DAG의 task 정의 (inference_using_yolov5)&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yolov5s model로 다운로드한 random한 person 이미지를 inference하기 위해 다음과 같은 shell script(&lt;b&gt;yolov5_inference.sh&lt;/b&gt;)를 &lt;b&gt;~/airflow/dags/&lt;/b&gt;에 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;#yolov5_inference.sh
git clone https://github.com/ultralytics/yolov5.git #yolov5 repo을 clone함
cd yolov5 # clone한 repo 폴더로 접속
curl -L https://github.com/ultralytics/yolov5/releases/download/v6.0/v61_yolov5s.pt --output v61_yolov5s.pt # yolov5s model(.pt)를 다운로드함
pip install -r requirements.txt # inference(detect)를 위한 library설치
python3 detect.py --weights v61_yolov5s.pt --source $1 --project /tmp/images --name result # 첫번째 인자로받은($1) image를 입력으로 yolov5s model로 inference&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 shell script 명령어의 의미를 주석으로 적어놓았고 자세한 특징점은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;v6_yolov5s.pt&lt;/code&gt;로 저장된 model은 coco dataset으로 학습됨
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;coco dataset에는 person detection을 위한 class도 포함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python3 detect.py ...&lt;/code&gt;의 $1의 값은 shell script을 실행하는 인자로 받음을 명시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인자로 받은 path는 image path가 될것이며 이는 detection model의 입력 source로 들어감&lt;/li&gt;
&lt;li&gt;detect하는 데 사용되는 모델은 다운받은 v61_yolov5s.pt가 됨&lt;/li&gt;
&lt;li&gt;해당 명령어의 --project와 --name의 value값을 detection 결과 이미지 저장 디렉토리로 지정함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;저장 장소: &lt;b&gt;/tmp/images/result/&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 shell script를 작성했다면 이제 dag의 task를 추가 정의해보자.&lt;/p&gt;
&lt;pre class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# task 정의
inference_using_yolov5 = BashOperator(
    task_id=&quot;inference_using_yolov5&quot;,
    bash_command=f&quot;sudo sh ~/airflow/dags/yolov5_inference.sh {Path(IMAGE_DIR)/'image.png'}&quot;,  # inference person image using yolov5
    dag=dag,
)

make_image_store &amp;gt;&amp;gt; download_person_picture &amp;gt;&amp;gt; inference_using_yolov5&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보시면 아시겠지만 yolov5_inference.sh를 실행시키기 위해 BashOperator를 사용하는 것이고 인자값으로 다운로드한 random한 image의 path가 들어간다. 그럼 이제 다시 webserver에 접속해 해당 task가 추가되었는지 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1377&quot; data-origin-height=&quot;1089&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dEhGrL/btrv11IPmqu/nhIgQevi9bJ0kLB8QkKt60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dEhGrL/btrv11IPmqu/nhIgQevi9bJ0kLB8QkKt60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dEhGrL/btrv11IPmqu/nhIgQevi9bJ0kLB8QkKt60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdEhGrL%2Fbtrv11IPmqu%2FnhIgQevi9bJ0kLB8QkKt60%2Fimg.png&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; alt=&quot;add inference_using_yolov5 in dags&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;442&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1377&quot; data-origin-height=&quot;1089&quot;/&gt;&lt;/span&gt;&lt;/figure&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;잘 생성되었음을 확인하고 해당 dag trigger run을 해보자!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;1033&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcUQY1/btrv3PUSBZr/bZaCKKWBVMURu9x4739i1k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcUQY1/btrv3PUSBZr/bZaCKKWBVMURu9x4739i1k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcUQY1/btrv3PUSBZr/bZaCKKWBVMURu9x4739i1k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcUQY1%2Fbtrv3PUSBZr%2FbZaCKKWBVMURu9x4739i1k%2Fimg.png&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; alt=&quot;check dag runs well&quot; loading=&quot;lazy&quot; width=&quot;498&quot; height=&quot;377&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1366&quot; data-origin-height=&quot;1033&quot;/&gt;&lt;/span&gt;&lt;/figure&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;dag run이 정상적으로 확인되었음을 확인가능하다. 그렇다면 &lt;b&gt;/tmp/images/result&lt;/b&gt; 폴더에 가서 detection이 잘 되었는지 확인한다. (새롭게 run시킨것이기 때문에 위에서 보여드린 여자분 사진으로 detection을 진행되지 않았습니다.)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4fUbc/btrv11hJhx2/PKs8a9hFrZiaqFmx3Kb8rK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4fUbc/btrv11hJhx2/PKs8a9hFrZiaqFmx3Kb8rK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4fUbc/btrv11hJhx2/PKs8a9hFrZiaqFmx3Kb8rK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4fUbc%2Fbtrv11hJhx2%2FPKs8a9hFrZiaqFmx3Kb8rK%2Fimg.png&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; alt=&quot;complete detection using yolov5&quot; loading=&quot;lazy&quot; width=&quot;707&quot; height=&quot;327&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;/figure&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;추가로 webserver에서 다음과 같이 task를 누르고 log를 누르면 task에 대한 log도 확인가능하며 이는 만약 dag run을 하면서 fail될때 유용하게 사용될 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1945&quot; data-origin-height=&quot;725&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6x8lm/btrv462Xj1f/SAKKUOZDPQkMaz2va788N0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6x8lm/btrv462Xj1f/SAKKUOZDPQkMaz2va788N0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6x8lm/btrv462Xj1f/SAKKUOZDPQkMaz2va788N0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6x8lm%2Fbtrv462Xj1f%2FSAKKUOZDPQkMaz2va788N0%2Fimg.png&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; alt=&quot;way to address error log&quot; loading=&quot;lazy&quot; width=&quot;798&quot; height=&quot;298&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1945&quot; data-origin-height=&quot;725&quot;/&gt;&lt;/span&gt;&lt;/figure&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;이렇게 해서 우리가 원했던 workflow pipeline을 작성하고 실행하는 법을 알아보았습니다. 해당 예제 코드는 &lt;b&gt;&lt;a href=&quot;https://github.com/da2so/airflow_tutorial&quot;&gt;airflow_tutorial&lt;/a&gt;&lt;/b&gt;에서 사용가능합니다.&lt;/p&gt;</description>
      <category>AI Engineering/MLOps</category>
      <category>Airflow</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/40</guid>
      <comments>https://da2so.tistory.com/40#entry40comment</comments>
      <pubDate>Wed, 16 Mar 2022 01:45:41 +0900</pubDate>
    </item>
    <item>
      <title>Airflow (1) - Airflow 이해 및 설치</title>
      <link>https://da2so.tistory.com/39</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;0. Machine Learning pipelining&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #409d00;&quot;&gt;&lt;b&gt;(ML pipelining 필요성 및 동기에 대한 밑의 단락입니다 읽어보세용!)&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기업에서 ML model을 서비스할 경우 &lt;b&gt;정제되어 있는 데이터셋은 없으며, 주기적으로 라벨이 수정되거나 새로운 데이터가 끊임 없이 계속 쌓이거나 바뀌게 됩니다&lt;/b&gt;. 그때마다 새로 training dataeset을 구성해야 하며, validation set 또한 주기적으로 업데이트해야 할 것입니다. 또한, 좀 더 유저 경험을 고려한 새로운 evaluation metrics를 개발해야 하며 model serving이나 monitoring, data와 model의 버전 컨트롤까지 고려해야합니다. 그래서 &lt;b&gt;data가 업데이트될 때마다, model이 변경될 때마다, error가 발생하거나 원하는 수준의 성능이 나오지 않을 때마다, 매번 일일이 dataset을 다시 구성하고 학습 스크립트를 실행하고, 평가를 진행하는 등 모든 과정에서 많은 노력과 시간을 필요로 하게 됩니다.&lt;/b&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;이를 해결하기 위해 각 과정들의 in/out만 표준화 하고 각 task의 결과에 따른 조건부 처리 및 trigger설정하여 pipeline에 맞춰 실행하는 &lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;workflow&lt;/span&gt;&lt;/b&gt;를 만든다면 반복적인 과정을 줄일수 있으며 이는 engineer/scientist가 model 개선과 연구에만 집중가능하게 합니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Airflow란?&lt;/b&gt;&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;494&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTVhZk/btrv4yrPUG8/kOmeL0KUL9sXFkttSLlj6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTVhZk/btrv4yrPUG8/kOmeL0KUL9sXFkttSLlj6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTVhZk/btrv4yrPUG8/kOmeL0KUL9sXFkttSLlj6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTVhZk%2Fbtrv4yrPUG8%2FkOmeL0KUL9sXFkttSLlj6k%2Fimg.png&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; alt=&quot;airflow&quot; loading=&quot;lazy&quot; width=&quot;479&quot; height=&quot;185&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;494&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;Apache airflow&lt;/span&gt;&lt;/b&gt;는 &lt;i&gt;python기반의 workflow mangement platform으로 여러가지 tasks을 일련의 그래프로 연결하고 스케줄링, 모니터링 등 piepline 관리를 위한 다양한 기능&lt;/i&gt;을 제공합니다. 즉, airflow는 data processing tool이 아니며 data processing하는 데 있어서 필요한 서로 다른 task를 &lt;b&gt;orchestrating&lt;/b&gt;하는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Airflow는 다음 그림과 같이 task(compoenent)들이 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;directed acyclic graph(DAG)&lt;/b&gt;&lt;/span&gt;형태로 구성되어있습니다. 그래서 전체 task들은 순환(cyclic) 구조를 가지지않으며 각 task는 하나의 node를 뜻하며 edge를 통해 task간의 의존성을 나타냅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2214&quot; data-origin-height=&quot;764&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/01ysl/btrv0QU0a09/1hEFsUEnledfy0nbM98D30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/01ysl/btrv0QU0a09/1hEFsUEnledfy0nbM98D30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/01ysl/btrv0QU0a09/1hEFsUEnledfy0nbM98D30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F01ysl%2Fbtrv0QU0a09%2F1hEFsUEnledfy0nbM98D30%2Fimg.png&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; alt=&quot;airflow dag examples&quot; loading=&quot;lazy&quot; width=&quot;756&quot; height=&quot;261&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;2214&quot; data-origin-height=&quot;764&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 Airflow를 써야하는 이유&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;batch-oriented task를 서비스할 경우 사용합니다. (아래 그림 참조)&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;batch-oriented란 특정 size만큼(batch)의 데이터를 처리하는 것을 반복&lt;/li&gt;
&lt;li&gt;실시간 데이터를 처리하는 streaming pipeline서비스를 처리할경우 airflow는 적합하지 않을 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;python code로 복잡한 pipeline을 implement가능합니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Airflow이 python 기반이므로 많고 다양한 system(DB, cloud services, ...)과의 integration 및 extension이 가능합니다.&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;일정 interval마다 pipeline을 스케줄링가능하며 backfilling을 통하여 historical data를 쉽게 re(process)가능합니다.&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;backfill에 대해서는 추후에 더 자세하게!!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;web interface를 제공하며 이는 pipeline의 결과를 모니터링하고 디버깅하기 좋습니다.&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JCS0E/btrv4KeWaba/zNwjn69JEfoKuFB4wK2Tpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JCS0E/btrv4KeWaba/zNwjn69JEfoKuFB4wK2Tpk/img.png&quot; data-alt=&quot;batch processing&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JCS0E/btrv4KeWaba/zNwjn69JEfoKuFB4wK2Tpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJCS0E%2Fbtrv4KeWaba%2FzNwjn69JEfoKuFB4wK2Tpk%2Fimg.png&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; alt=&quot;batch processing&quot; loading=&quot;lazy&quot; width=&quot;559&quot; height=&quot;357&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;506&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;batch processing&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.2 Airflow pipelinling 정의 방법&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Airflow는 pipeline을 DAG형태로 구성합니다. DAG는 DAG files안의 python code를 사용해서 정의가능합니다. python code로 각 task(node)를 정의할수 있으며 task간의 dependency와 해당 pipeline의 실행 시간,주기까지 설정가능합니다. python code를 통해 DAG를 만들어 보는 것은 다음 글에서 진행해보도록 하겠습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;dag_examples.png&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;556&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lrPPj/btrv3PACBSh/VzKdF8HTtOZXmuDDWzfKS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lrPPj/btrv3PACBSh/VzKdF8HTtOZXmuDDWzfKS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lrPPj/btrv3PACBSh/VzKdF8HTtOZXmuDDWzfKS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlrPPj%2Fbtrv3PACBSh%2FVzKdF8HTtOZXmuDDWzfKS0%2Fimg.png&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; alt=&quot;DAG definition&quot; loading=&quot;lazy&quot; width=&quot;636&quot; height=&quot;269&quot; data-filename=&quot;dag_examples.png&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;556&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.3 Airflow 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 정의된 pipeline은 airflow가 어떻게 실행시키는 지 알려면 airflow의 구조를 이해하셔야합니다. Airflow는 크게 다음과 같이 3가지의 main components로 구성되어있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Airflow scheduler&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DAG를 parsing하며 해당 pipeline의 실행 start와 interval을 scheduling을 하여 Airflow worker(s)에게 task를 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Airflow worker&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 task를 실제로 실행시키는 주체&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Airflow webserver&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;parsing된 DAG를 시각화하며 DAG의 실행과 결과에 대해 모니터링가능하게 해줌&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;689&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqJ4gr/btrv4JtBGxz/qwBveVFH5zDSQLKgWTz64k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqJ4gr/btrv4JtBGxz/qwBveVFH5zDSQLKgWTz64k/img.png&quot; data-alt=&quot;Airflow system&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqJ4gr/btrv4JtBGxz/qwBveVFH5zDSQLKgWTz64k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqJ4gr%2Fbtrv4JtBGxz%2FqwBveVFH5zDSQLKgWTz64k%2Fimg.png&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; alt=&quot;Airflow System&quot; loading=&quot;lazy&quot; width=&quot;696&quot; height=&quot;339&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1416&quot; data-origin-height=&quot;689&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Airflow system&lt;/figcaption&gt;
&lt;/figure&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;Airflow scheduler는 Airflow의 핵심이며 우리가 설계해놓은 pipeline을 처리하는 시간 및 방법을 모두 결정하게 됩니다. 조금 더 디테일하게 scheduler내부 동작 방식을 보면 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1441&quot; data-origin-height=&quot;635&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O29AU/btrvYIWzoB4/Ts2U0aGlvZkEKty4I0Ygd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O29AU/btrvYIWzoB4/Ts2U0aGlvZkEKty4I0Ygd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O29AU/btrvYIWzoB4/Ts2U0aGlvZkEKty4I0Ygd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO29AU%2FbtrvYIWzoB4%2FTs2U0aGlvZkEKty4I0Ygd1%2Fimg.png&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; alt=&quot;airflow scheduler&quot; loading=&quot;lazy&quot; width=&quot;678&quot; height=&quot;299&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1441&quot; data-origin-height=&quot;635&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;scheduler는 유저가 만든 DAG file을 읽음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DAG file에 정의된 execution start time과 interval에 맞춰 DAG task들을 스케줄링&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;스케줄링된 task(A)가 다른 task(B)에 의존성 판단&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;task A가 task B에 의존되어있고 task B가 완료된 경우 queue에 task A 추가&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;의존하고 있는 task B가 끝나지 않은 경우 일정 시간 기다림&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 모든 과정 및 결과들은 airflow의 metastore에 저장되므로 유저는 task의 진행상황이나 로그들을 metastore와 연동되어있는 airflow webserver interface를 통해 확인가능하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Airflow 설치&lt;/b&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;MacOS 12.1, anaconda, python 3.9, airflow 2.2.3 환경에서 실습을 진행함을 알려드립니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 anaconda에서 실습을 진행하기 때문에 다음과 같이 pip가 anaconda에서 사용됨을 확인하고 airflow을 설치합시다.&lt;/p&gt;
&lt;pre class=&quot;properties&quot;&gt;&lt;code&gt;pip --version
pip install apache-airflow&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;94&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/IXDDf/btrv0QnbpzD/DdEFuslkqBr7e5iCsEIMr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/IXDDf/btrv0QnbpzD/DdEFuslkqBr7e5iCsEIMr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/IXDDf/btrv0QnbpzD/DdEFuslkqBr7e5iCsEIMr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FIXDDf%2Fbtrv0QnbpzD%2FDdEFuslkqBr7e5iCsEIMr1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;805&quot; height=&quot;58&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1300&quot; data-origin-height=&quot;94&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 다 되었다면 &lt;code&gt;airflow version&lt;/code&gt; 명령어를 통해 설치가 잘 되었는 지 확인합니다. 그리고 default로 airflow 디렉토리는 home디렉토리에 저장되기때문에 다음과 같이 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;100&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4vFpy/btrv3gyG2FJ/gixaI91yW6PTJ3GidGcQT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4vFpy/btrv3gyG2FJ/gixaI91yW6PTJ3GidGcQT1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4vFpy/btrv3gyG2FJ/gixaI91yW6PTJ3GidGcQT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4vFpy%2Fbtrv3gyG2FJ%2FgixaI91yW6PTJ3GidGcQT1%2Fimg.png&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; loading=&quot;lazy&quot; width=&quot;719&quot; height=&quot;51&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;100&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Airflow DB initialize&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB를 초기화 합니다. 이를 통해서 예제 pipeline들이 생겨나기도 합니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;airflow db init&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 Airflow scheduler 실행&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 설명드린 airflow scheduler를 실행하는 명령어 입니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;airflow scheduler&lt;/code&gt;&lt;/pre&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;590&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b4IWCa/btrv5mEWa5G/DYUvYniDPZNLyXsL4FKIvK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b4IWCa/btrv5mEWa5G/DYUvYniDPZNLyXsL4FKIvK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b4IWCa/btrv5mEWa5G/DYUvYniDPZNLyXsL4FKIvK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb4IWCa%2Fbtrv5mEWa5G%2FDYUvYniDPZNLyXsL4FKIvK%2Fimg.png&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; alt=&quot;execute airflow scheduler&quot; loading=&quot;lazy&quot; width=&quot;695&quot; height=&quot;286&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1434&quot; data-origin-height=&quot;590&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 Airflow webserver 실행&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;airflow webserver를 실행시킵니다. &lt;b&gt;-p&lt;/b&gt;옵션은 port를 의미합니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;airflow webserver -p 8080&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 명령어가 정상적으로 작동되었다면 이제 웹에서 localhost:8080에 들어가보면 다음과 같은 화면이 나오는지 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;489&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzNXOO/btrv11PurV7/7RBKbfRewQ6R9KAG4f0WD0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzNXOO/btrv11PurV7/7RBKbfRewQ6R9KAG4f0WD0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzNXOO/btrv11PurV7/7RBKbfRewQ6R9KAG4f0WD0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzNXOO%2Fbtrv11PurV7%2F7RBKbfRewQ6R9KAG4f0WD0%2Fimg.png&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; alt=&quot;airflow webserver&quot; loading=&quot;lazy&quot; width=&quot;445&quot; height=&quot;422&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;516&quot; data-origin-height=&quot;489&quot;/&gt;&lt;/span&gt;&lt;/figure&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;webserver에 접속하니 airflow의 username과 password가 필요합니다. 그렇다면 airflow cli를 통해 유저를 생성해봅시다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;airflow users create --role Admin --username admin --email admin.com --firstname sinhan --lastname kang --password admin&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;364&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dlN5oc/btrv6jVhjch/jAHS757RdDK3CWR44b7fq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dlN5oc/btrv6jVhjch/jAHS757RdDK3CWR44b7fq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dlN5oc/btrv6jVhjch/jAHS757RdDK3CWR44b7fq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdlN5oc%2Fbtrv6jVhjch%2FjAHS757RdDK3CWR44b7fq1%2Fimg.png&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; alt=&quot;airflow add user&quot; loading=&quot;lazy&quot; width=&quot;784&quot; height=&quot;166&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;364&quot;/&gt;&lt;/span&gt;&lt;/figure&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;Admin계정으로 user를 생성하였고 username과 password는 동일하게 admin으로 지정하였습니다. 생성된 유저 정보는 &lt;code&gt;airflow users list&lt;/code&gt;명령어로 확인가능합니다. 확인하였다면 webserver에 만든 유저 정보로 다음과 같이 로그인가능하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ky8aB/btrv3PUSsEx/7LaOuyHDIFhDtFSsbUQWgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ky8aB/btrv3PUSsEx/7LaOuyHDIFhDtFSsbUQWgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ky8aB/btrv3PUSsEx/7LaOuyHDIFhDtFSsbUQWgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fky8aB%2Fbtrv3PUSsEx%2F7LaOuyHDIFhDtFSsbUQWgk%2Fimg.png&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; alt=&quot;airflow users list&quot; loading=&quot;lazy&quot; width=&quot;474&quot; height=&quot;90&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;852&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1954&quot; data-origin-height=&quot;516&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/49C4Y/btrv4JtBQ3o/CKxnTAvFAE1RgUI5RN7nkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/49C4Y/btrv4JtBQ3o/CKxnTAvFAE1RgUI5RN7nkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/49C4Y/btrv4JtBQ3o/CKxnTAvFAE1RgUI5RN7nkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F49C4Y%2Fbtrv4JtBQ3o%2FCKxnTAvFAE1RgUI5RN7nkK%2Fimg.png&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; alt=&quot;airflow webserver elements&quot; loading=&quot;lazy&quot; width=&quot;773&quot; height=&quot;204&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1954&quot; data-origin-height=&quot;516&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위 그림에서 DAG의 example과 각 column이 의미하는 내용을 적어놓았습니다. schedule부분에서 crontab형태로 나타냅니다. (모르시면 구글링!) 다음과 같은 그리고 example_bash_operator를 클릭하고 'Graph'라는 menu선택을 하면 다음과 같이 해당 workflow의 DAG를 확인가능합니다.&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;해당 example들은 db initialize를 통해 생성된 것이며 &lt;code&gt;airflow dags list&lt;/code&gt;명령어를 통하여 webserver에서 보이는 example들을 terminal에서 볼 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;569&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvf9Dl/btrv4530JSz/GwNGwaM1n0UCmXJpRvbomk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvf9Dl/btrv4530JSz/GwNGwaM1n0UCmXJpRvbomk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvf9Dl/btrv4530JSz/GwNGwaM1n0UCmXJpRvbomk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbvf9Dl%2Fbtrv4530JSz%2FGwNGwaM1n0UCmXJpRvbomk%2Fimg.png&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; alt=&quot;airflow dag examples in webserver&quot; loading=&quot;lazy&quot; width=&quot;721&quot; height=&quot;358&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1147&quot; data-origin-height=&quot;569&quot;/&gt;&lt;/span&gt;&lt;/figure&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;다음 글에서는 위의 예제와 같이 DAG를 python code로 만드는 법과 실제로 workflow를 실행시켜보면서 결과를 얻어보도록 하겠습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Appendix&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 다음과 같은 warning이 뜬다면 sqlalchemy버전이 너무 높아 생기는 것이므로 다음과 같은 명령어로 warning을 해결가능합니다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;# warning: add the parameter 'overlaps=&quot;dag_run&quot;' to the 'SerializedDagModel.dag_runs' relationship
pip uninstall sqlalchemy
pip install 'sqlalchemy &amp;lt; 1.4.0'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/MLOps</category>
      <category>Airflow</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/39</guid>
      <comments>https://da2so.tistory.com/39#entry39comment</comments>
      <pubDate>Wed, 16 Mar 2022 01:27:40 +0900</pubDate>
    </item>
    <item>
      <title>Docker/Kubernetes - (12) Kubernetes Ingress</title>
      <link>https://da2so.tistory.com/38</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Environment&lt;/b&gt;: Ubuntu 18.04&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Ingress&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ingress network는 외부에서 서버로 들어오는 트래픽을 처리하며 네트워크 7계층 레벨에서 정의되는 k8s object입니다. 주요 기능은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;외부 요청의 라우팅&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;/request, /request/you 등과 같이 특정 경로로 들어오는 요청을 어떠한 service로 전달할지 결정&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;가상 호스트의 요청 처리&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;같은 IP에 대해 다른 도메인 이름으로 요청이 도착했을 때, 어떻게 처리할 지 정의&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;SSL/TLS 보안 연결 처리&lt;/b&gt;&lt;/span&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 Ingress 사용 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구체적으로 기존에 설명하였던 NodePort, LoadBalancer타입의 service도 위와 같은 기능을 구현가능하지만 Ingress를 사용하는 이유는 다음과 같습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;841&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZuT9D/btrv6efqNO9/CQIOJZwdn7dUkr7KNG8Vgk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZuT9D/btrv6efqNO9/CQIOJZwdn7dUkr7KNG8Vgk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZuT9D/btrv6efqNO9/CQIOJZwdn7dUkr7KNG8Vgk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZuT9D%2Fbtrv6efqNO9%2FCQIOJZwdn7dUkr7KNG8Vgk%2Fimg.png&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; alt=&quot;ingress&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;345&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1958&quot; data-origin-height=&quot;841&quot;/&gt;&lt;/span&gt;&lt;/figure&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;왼쪽 그림처럼 기존의 NodePort 또는 LoadBalancer타입의 service는 deployement 3개를 외부에 노출해야한다면 service가 3개필요합니다. 그리고 service마다 세부적인 설정을 할때 추가적인 복잡성이 발생하게 되고 SSL/TLS 보안 연결, 접근 도메인 및 클라이언트 상태에 기반한 라우팅을 구현하려면 각 service와 deployement에 일일이 설정해야합니다.&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;하지만 오른쪽과 같이 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Ingress&lt;/b&gt;&lt;/span&gt;를 사용하면 3개의 service에 각각 URL이 존재하지 않고 Ingress에 접근하기위한 단 하나의 URL만 존재합니다. 그래서 클라이언트는 Ingress의 URL로 접근하게 되며 해당 요청은 Ingress에 정의된 규칙에 따라 처리된뒤 deployment에 전달됩니다. 이 과정에서 라우팅 정의나 보안 연결 등과 같은 세부 설정은 Ingress에서 수행됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.2 Ingress 구조&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;kubectl get ing&lt;/code&gt;으로 ingress의 목록을 확인가능하다. 확인 시 ingress목록이 없으니 ingress-example.yaml파일로 생성해보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;74&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUIDAQ/btrv0PaxBOg/76K7ruK7PxZAVZiCQoYJKk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUIDAQ/btrv0PaxBOg/76K7ruK7PxZAVZiCQoYJKk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUIDAQ/btrv0PaxBOg/76K7ruK7PxZAVZiCQoYJKk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUIDAQ%2Fbtrv0PaxBOg%2F76K7ruK7PxZAVZiCQoYJKk%2Fimg.png&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; alt=&quot;kubectl get in&quot; loading=&quot;lazy&quot; width=&quot;429&quot; height=&quot;43&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;738&quot; data-origin-height=&quot;74&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#ingress-example.yaml  
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: &quot;nginx&quot;
spec:
  rules:
  - host: da2so.com
    http:
      paths:
      - path: /hostname
        pathType: Prefix
        backend:
          service:
            name: hostname-svc
            port:
              number: 8080 &lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;host&lt;/b&gt;: 해당 도메인 이름으로 접근하는 요청에 대해서 처리 규칙을 적용
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러개의 host를 가질 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;path&lt;/b&gt;: 해당 경로에 들어온 요청은 어느 서비스로 전달할지 정의
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에서는 /etc-hostname으로 온 요청을 backend에 정의된 service에 전달&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;pathType&lt;/b&gt;: 경로의 유형
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Prefix: URL 경로의 접두사를 / 를 기준으로 분리한 값과 일치시킴 (/hostname, /hostanme/ 가능)&lt;/li&gt;
&lt;li&gt;Exact: URL 경로의 대소문자를 엄격하게 일치시킴 (/hostname 만 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;service&lt;/b&gt;: path로 들어온 요청이 전달될 내용을 담고있다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;name, port.number&lt;/b&gt;: service 이름, port 번호&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;118&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dE5t7E/btrv2El95Zr/f1jYy3TxbuCQxLbitp2eU0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dE5t7E/btrv2El95Zr/f1jYy3TxbuCQxLbitp2eU0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dE5t7E/btrv2El95Zr/f1jYy3TxbuCQxLbitp2eU0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdE5t7E%2Fbtrv2El95Zr%2Ff1jYy3TxbuCQxLbitp2eU0%2Fimg.png&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; alt=&quot;kubectl get ing2&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;60&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1108&quot; data-origin-height=&quot;118&quot;/&gt;&lt;/span&gt;&lt;/figure&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;minimal-ingress라는 이름으로 ingress를 생성했지만 이는 단지 요청을 처리하는 규칙을 정의하는 선언적인 object이다. 그래서 외부 요청을 받아들일 수 있는 실제 서버가 아니기 때문에 &lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;Ingress Controller&lt;/span&gt;&lt;/b&gt;라는 특수한 서버에 적용해야만 그 규칙을 사용가능하다. 즉, 실제로 외부 요청을 받아들이는 것은 Ingress controller server이며 이 서버가 ingress 규칙을 로드해 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1085&quot; data-origin-height=&quot;835&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OriGX/btrv119JU3N/DpbvYx8k0ETaz3AzfORh5K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OriGX/btrv119JU3N/DpbvYx8k0ETaz3AzfORh5K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OriGX/btrv119JU3N/DpbvYx8k0ETaz3AzfORh5K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOriGX%2Fbtrv119JU3N%2FDpbvYx8k0ETaz3AzfORh5K%2Fimg.png&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; alt=&quot;ingress process&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;421&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1085&quot; data-origin-height=&quot;835&quot;/&gt;&lt;/span&gt;&lt;/figure&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;그래서 k8s의 ingress는 반드시 ingress controller를 필요로하며 우리는 nginx 웹서버를 사용하므로 &lt;b&gt;Ngnix 웹서버 Ingress controller&lt;/b&gt;를 사용합니다. Kong이라는 API gateway나 GKE의 클라우드 플랫폼에서 제공되는 ingress controller도 있음을 알아두면 좋다. Nginx 웹서버 Ingress controller는 다음과 같은 명령어로 Nginx ingress controller와 관련된 resource를 다운받습니다. (제가 사용한 k8s 버전이 1.23인데 이는 controller 버전 1.1.1과 연동가능합니다. )&lt;/p&gt;
&lt;pre class=&quot;awk&quot;&gt;&lt;code&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/cloud/deploy.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1964&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bS6pWX/btrv4JUyFaG/gitojYjzR3nGEtz7mBDHaK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bS6pWX/btrv4JUyFaG/gitojYjzR3nGEtz7mBDHaK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bS6pWX/btrv4JUyFaG/gitojYjzR3nGEtz7mBDHaK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbS6pWX%2Fbtrv4JUyFaG%2FgitojYjzR3nGEtz7mBDHaK%2Fimg.png&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; alt=&quot;kubectl apply ingress-controller&quot; loading=&quot;lazy&quot; width=&quot;761&quot; height=&quot;139&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1964&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위의 그림은 ingress controller의 역할 및 관계를 나타낸 그림&lt;b&gt;(A라 명칭)&lt;/b&gt;이다. 이제 &lt;code&gt;kubectl get all -n ingress-nginx&lt;/code&gt;명령어로 생성한 ingress-controller에 의해 생성된 ingress-nginx namespace의 모든 object를 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;2274&quot; data-origin-height=&quot;694&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZWgTD/btrv5oo5ENH/U4se7nLrGp4e46lLqWiKQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZWgTD/btrv5oo5ENH/U4se7nLrGp4e46lLqWiKQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZWgTD/btrv5oo5ENH/U4se7nLrGp4e46lLqWiKQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZWgTD%2Fbtrv5oo5ENH%2FU4se7nLrGp4e46lLqWiKQK%2Fimg.png&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; alt=&quot;kubectl get all -n ingress-nginx&quot; loading=&quot;lazy&quot; width=&quot;834&quot; height=&quot;255&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;2274&quot; data-origin-height=&quot;694&quot;/&gt;&lt;/span&gt;&lt;/figure&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;여기서 default로 ingress-nginx-controller의 service type이 LoadBalancer로 되어있는데 저는 NodePort service로 진행할것이기 때문에 &lt;code&gt;kubectl -n ingress-nginx edit service/ingress-nginx-controller&lt;/code&gt;명령어를 통해 다음과 같이 해당 내용을 수정해줍니다. 그리고 다시 ingress-nginx-controller의 service type이 NodePort로 변환된것을 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1883&quot; data-origin-height=&quot;572&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EHFz7/btrv3fTUdgv/LW5Ma6Bp3BgDUAMerSXUKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EHFz7/btrv3fTUdgv/LW5Ma6Bp3BgDUAMerSXUKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EHFz7/btrv3fTUdgv/LW5Ma6Bp3BgDUAMerSXUKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEHFz7%2Fbtrv3fTUdgv%2FLW5Ma6Bp3BgDUAMerSXUKK%2Fimg.png&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; alt=&quot;ingress-nginx edited&quot; loading=&quot;lazy&quot; width=&quot;731&quot; height=&quot;222&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1883&quot; data-origin-height=&quot;572&quot;/&gt;&lt;/span&gt;&lt;/figure&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;이제 &lt;b&gt;A&lt;/b&gt; 그림에서 hostname-service-nodeport 서비스 부분과 deployment에 대한 yaml을 다음과 같이 작성합니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#ingress-deployment-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ingress-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webserver
  template:
    metadata:
      name: my-webserver
      labels:
        app: webserver
    spec:
      containers:
      - name: my-webserver
        image: nginx:1.10
        ports:
        - containerPort: 80
          name: nginx-port
---
apiVersion: v1
kind: Service
metadata:
  name: hostname-svc
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: webserver
  type: NodePort
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;kubectl apply -f ingress-deployment-service.yaml&lt;/code&gt;명령어로 object들을 실행시키고 나면 다음과 같이 deployment와 &lt;b&gt;NodePort&lt;/b&gt;타입의 service가 생성된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;330&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/58QrC/btrv6dni4Rq/xOkFHwDk7Q9YN5DiekJjs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/58QrC/btrv6dni4Rq/xOkFHwDk7Q9YN5DiekJjs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/58QrC/btrv6dni4Rq/xOkFHwDk7Q9YN5DiekJjs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F58QrC%2Fbtrv6dni4Rq%2FxOkFHwDk7Q9YN5DiekJjs0%2Fimg.png&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; alt=&quot;create nodeport service&quot; loading=&quot;lazy&quot; width=&quot;703&quot; height=&quot;125&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1856&quot; data-origin-height=&quot;330&quot;/&gt;&lt;/span&gt;&lt;/figure&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;마지막으로 현재 예시는 on-premise환경이기 때문에 마스터노드에서 /etc/hosts파일에 IP와 도메인을 설정해 임시로 동작 여부를 테스트하도록한다. 다음과 같이 /etc/hosts에 da2so.example.com과 워커 노드 IP와 연결한다. 이는 ingress controller는 기본적으로 도메인 이름으로 연결되기 때문에 도메인을 IP연동시켜줘야하는 부분이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1427&quot; data-origin-height=&quot;412&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/trVZ1/btrv2Gc7y9l/OHbk8qPGLJpcSxXEX9ak6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/trVZ1/btrv2Gc7y9l/OHbk8qPGLJpcSxXEX9ak6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/trVZ1/btrv2Gc7y9l/OHbk8qPGLJpcSxXEX9ak6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtrVZ1%2Fbtrv2Gc7y9l%2FOHbk8qPGLJpcSxXEX9ak6K%2Fimg.png&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; alt=&quot;connect IP using ingress&quot; loading=&quot;lazy&quot; width=&quot;636&quot; height=&quot;184&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1427&quot; data-origin-height=&quot;412&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위와 같이 도메인과 IP를 연결해주는 내용을 추가해주면 ingress의 address에 ingress controller service clusterIP로 연결이 되었다. nginx ingress controller는 항상 ingress 리소스의 상태를 지켜보고 있으며 기본적으로 모든 namespace의 ingress리소스를 읽어와 규칙을 적용하게 되는 것입니다. 위의 모든 설정을 그림으로 나타내면 아래와 같고 외부에서 da2so.com:30172/hostname에 접속하는 것은 다음과 같은 프로세스를 거친다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1427&quot; data-origin-height=&quot;1097&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Uy00B/btrv5mrgQ3D/Kx9mUuKAhG7Hj0qS2VBbE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Uy00B/btrv5mrgQ3D/Kx9mUuKAhG7Hj0qS2VBbE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Uy00B/btrv5mrgQ3D/Kx9mUuKAhG7Hj0qS2VBbE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUy00B%2Fbtrv5mrgQ3D%2FKx9mUuKAhG7Hj0qS2VBbE1%2Fimg.png&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; alt=&quot;ingress example&quot; loading=&quot;lazy&quot; width=&quot;644&quot; height=&quot;495&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1427&quot; data-origin-height=&quot;1097&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;외부에서 da2so.com:30172/hostname로 request&lt;/b&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;일반적으로 외부에서 request가 들어오지만 저는 예시를 위해 마스터노드에서 request를 진행&lt;/li&gt;
&lt;li&gt;마스터노드의 /etc/hosts 에 da2so.com을 worker node ip와 연결시켜놓은 상태&lt;/li&gt;
&lt;li&gt;그래서 외부에서 접속되는 경우에는 일반적으로 router에다가 ingress에서 설정한 domain과 ip를 연결시켜줘야함&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;da2so.com:30172는 ingress controller service로 보내짐&lt;/b&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nodeport 타입의 service를 가지는 ingress controller이기 때문에 port(30172)를 지정하였음&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ingress controller의 ingress규칙에 따라 da2so.com:30172/hostname은 da2so.com은 worker node ip에 연결되고 해당 request는 hostname-svc:8080라는 service로 전달됨&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;hostname-svc는 nginx:1.10 image기반인 pod와 연결되므로 해당 pod의 80번 Port에 접근하여 웹서비스 request를 받음&lt;/b&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.3 Ingress 세부 기능: annotation을 이용한 설정&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 &lt;b&gt;ingress-example.yaml&lt;/b&gt;에서 annotation부분에 대해 설명하지 않았는데 여기서 설명하겠다. 다음은 위에서 작성한 anntotation부분을 가져온 것이다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#ingress-example.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: &quot;nginx&quot;
...
      paths:
      - path: /hostname
...&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;kubernetes.io/ingress.class&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ingress 규칙을 어떤 ingress controller에 적용할것인지 정함 (Ex: Nginx, Kong, GKE, ...)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;nginx.ingress.kubernetes.io/rewirte-target&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nginx ingress controller에서만 사용가능하며 ingress에 정의된 경로로 들어오는 요청을 rewrite-target에 설정된 경로로 전달합니다.&lt;/li&gt;
&lt;li&gt;위에서는 path에 적힌 /hostname으로 접근하면 hostname-svc에는 / 경로로 전달됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 다음(yaml)과 같이 정규식으로 설정할 경우 ingress에 요청온 path는 hostname-svc으 다음(밑의 그림)경로로 전달됩니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#ingress-example.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2 # path의 (.*)에서 전달받은 경로로 전달합니다.
    kubernetes.io/ingress.class: &quot;nginx&quot;
...
      paths:
      - path: /hostname(/|$)(.*) # (.*)을 통해 경로를 얻습니다.
...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;333&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vmnR3/btrv4lGnNVF/1l2heVhrFyYb4KGvWhcjd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vmnR3/btrv4lGnNVF/1l2heVhrFyYb4KGvWhcjd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vmnR3/btrv4lGnNVF/1l2heVhrFyYb4KGvWhcjd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvmnR3%2Fbtrv4lGnNVF%2F1l2heVhrFyYb4KGvWhcjd1%2Fimg.png&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; alt=&quot;ingress annotation&quot; loading=&quot;lazy&quot; width=&quot;431&quot; height=&quot;170&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;845&quot; data-origin-height=&quot;333&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;참고!&lt;/b&gt; Nginx Ingress Controller는 bypassing이라는 기능을 통하여 application pod에 트래픽을 직접 전달합니다. 해당 Pod의 Service를 경유해야 하는 네트워크 홉을 줄이게 됩니다.&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.4 Nginx ingress controller에 SSL/TLS보안 연결 적용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ingress의 장점은 ingress controller에서 편리하게 SSL/TLS 보안 연결을 설정할 수 있다는 것입니다. 즉, Ingress controlller지점에서 인증서를 적용해 두면 요청이 전달되는 application에 대해 모두 인증서 처리가능하다. 이번 글에서는 직접 서명한 루트 인증서를 통해 nginx ingress controller에 적용해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 연결에 사용할 인증서와 비밀키를 생성해보자.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj &quot;/CN=da2so.com/O=da2so&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;2404&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/2ju33/btrv12nf4DX/AKgse6a2kDvBkSzl5Z2Nwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/2ju33/btrv12nf4DX/AKgse6a2kDvBkSzl5Z2Nwk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/2ju33/btrv12nf4DX/AKgse6a2kDvBkSzl5Z2Nwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F2ju33%2Fbtrv12nf4DX%2FAKgse6a2kDvBkSzl5Z2Nwk%2Fimg.png&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; alt=&quot;ingress ssl/tls&quot; loading=&quot;lazy&quot; width=&quot;793&quot; height=&quot;71&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;2404&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&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;tls.key라는 비밀키와 tls.crt라는 인증서가 생성되었습니다. 그리고 secret object를 다음과 같이 만든다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt; kubectl create secret tls tls-secret --key tls.key --cert tls.crt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXFm92/btrv0QUR8ma/0jZZT9jCfrZHcMAPeA0c2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXFm92/btrv0QUR8ma/0jZZT9jCfrZHcMAPeA0c2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXFm92/btrv0QUR8ma/0jZZT9jCfrZHcMAPeA0c2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXFm92%2Fbtrv0QUR8ma%2F0jZZT9jCfrZHcMAPeA0c2K%2Fimg.png&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; alt=&quot;kubectl create secret&quot; loading=&quot;lazy&quot; width=&quot;724&quot; height=&quot;69&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;1618&quot; data-origin-height=&quot;154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tls을 적용한 ingress를 작성하기 전에 위에서 사용한 &lt;b&gt;ingress-deployment-service.yaml&lt;/b&gt;을 통해 service와 deployment를 실행시키자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1582&quot; data-origin-height=&quot;512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVbV8I/btrvZ2VAiAU/jxONAP16YZGStk3BvGtfs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVbV8I/btrvZ2VAiAU/jxONAP16YZGStk3BvGtfs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVbV8I/btrvZ2VAiAU/jxONAP16YZGStk3BvGtfs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVbV8I%2FbtrvZ2VAiAU%2FjxONAP16YZGStk3BvGtfs1%2Fimg.png&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; alt=&quot;kubectl apply ingress-deployment-service yaml&quot; loading=&quot;lazy&quot; width=&quot;718&quot; height=&quot;233&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1582&quot; data-origin-height=&quot;512&quot;/&gt;&lt;/span&gt;&lt;/figure&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;이제 tls가 적용될 ingress yaml을 다음과 같이 작성한다. 그리고 해당 ingress을 생성하고 생성한 ingress의 정보와 nginx ingress controller의 https(443port)의 정보를 확인한다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    kubernetes.io/ingress.class: &quot;nginx&quot;
spec:
  tls:
  - hosts:
    - da2so.com
    secretName: tls-secret
  rules:
  - host: da2so.com
    http:
      paths:
      - path: /hostname
        pathType: Prefix
        backend:
          service:
            name: hostname-svc
            port:
              number: 8080&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;spec.tls.hosts&lt;/b&gt;: 보안 연결을 적용할 도메인 이름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spec.tls.secretName:&lt;/b&gt; 위에서 생성하였던 tls 타입의 secret 이름&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1777&quot; data-origin-height=&quot;833&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b9tZRv/btrv112Xxvg/BIz7KPKWSRwzsoWonhCIj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b9tZRv/btrv112Xxvg/BIz7KPKWSRwzsoWonhCIj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b9tZRv/btrv112Xxvg/BIz7KPKWSRwzsoWonhCIj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb9tZRv%2Fbtrv112Xxvg%2FBIz7KPKWSRwzsoWonhCIj0%2Fimg.png&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; alt=&quot;ingress tls applied&quot; loading=&quot;lazy&quot; width=&quot;732&quot; height=&quot;343&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1777&quot; data-origin-height=&quot;833&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위의 그림에서 알 수 있듯이 ingress의 정보에 tls연결 정보가 새로 생긴것을 확인가능하며 tls보안이 있기때문에 https로 접속해야하는데 https로 접속하기 위한 ingress controller의 https포트는 31355인것을 알 수 있다. 즉, &lt;b&gt;https://da2so.com:31355&lt;/b&gt;는 https와 31355와 맵핑되는 443포트(https)를 통해 ingress controller의 https로 접근을 명시하는 것이고 그다음은 위에서 설명한것과 같이 ingress controller에게 da2so.com과 연결되는 ip주소에 접속하도록 요청하는것이다. 다음 명령어를 통해 https연결을 통해 web service에 접속해보자.&lt;/p&gt;
&lt;pre class=&quot;nginx&quot;&gt;&lt;code&gt;curl https://da2so.com:31355/hostname -k
# -k 옵션은 신뢰할 수 없는 인증서로 보안연결을 위함이다.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;462&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kERRw/btrv4lsRSak/cLOFJRF5n7iAzWpZZCkO4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kERRw/btrv4lsRSak/cLOFJRF5n7iAzWpZZCkO4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kERRw/btrv4lsRSak/cLOFJRF5n7iAzWpZZCkO4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkERRw%2Fbtrv4lsRSak%2FcLOFJRF5n7iAzWpZZCkO4K%2Fimg.png&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; alt=&quot;curl k option&quot; loading=&quot;lazy&quot; width=&quot;547&quot; height=&quot;218&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1160&quot; data-origin-height=&quot;462&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>AI Engineering/MLOps</category>
      <category>kubernetes</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/38</guid>
      <comments>https://da2so.tistory.com/38#entry38comment</comments>
      <pubDate>Tue, 15 Mar 2022 22:30:27 +0900</pubDate>
    </item>
    <item>
      <title>Docker/Kubernetes - (11) Kubernetes 리소스의 관리와 설정</title>
      <link>https://da2so.tistory.com/37</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Environment&lt;/b&gt;: Ubuntu 18.04&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Namespace: 리소스를 논리적으로 구분하는 장벽&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k8s에서 용도에 따라 container와 그와 관련된 리소스를 구분 지어 관리할 수 있는, 하나의 논리적인 그룹을 제공하기 위해 Namespace라는 object를 사용합니다. 예를 들어 모니터링을 위한 리소스들은 monitoring이라는 이름의 namespace로 생성될수 있고 테스트를 위한 리소스들은 test라는 namespace를 생성가능합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.1 Namespace 이해&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Namespace는 namespace(ns)라는 이름으로 k8s에서 사용가능하며 다음과 같이 namespace목록을 확인가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgj68A/btrv6emcsxU/UN8mSwv34DFJHdQPnqAJAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgj68A/btrv6emcsxU/UN8mSwv34DFJHdQPnqAJAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgj68A/btrv6emcsxU/UN8mSwv34DFJHdQPnqAJAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcgj68A%2Fbtrv6emcsxU%2FUN8mSwv34DFJHdQPnqAJAK%2Fimg.png&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; alt=&quot;kubectl get ns&quot; loading=&quot;lazy&quot; width=&quot;309&quot; height=&quot;110&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;612&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&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;기본적으로 3개의 namespace가 존재하는데 각각의 namespace는 논리적인 리소스 공간이기 때문에 pod, replicaset, service와 같은 리소스가 따로 존재합니다. 예로 default라는 이름의 namepspace에 생성된 pod를 확인하려면 다음과 같이 &lt;b&gt;--namespace&lt;/b&gt;또는 &lt;b&gt;-n&lt;/b&gt;옵션을 사용한다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;kubectl get po -n default&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;260&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjVnpK/btrv4I88PoH/Z2KpEAJVKtMg0mhj5sOGk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjVnpK/btrv4I88PoH/Z2KpEAJVKtMg0mhj5sOGk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjVnpK/btrv4I88PoH/Z2KpEAJVKtMg0mhj5sOGk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjVnpK%2Fbtrv4I88PoH%2FZ2KpEAJVKtMg0mhj5sOGk0%2Fimg.png&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; alt=&quot;kubectl get pods -n default&quot; loading=&quot;lazy&quot; width=&quot;692&quot; height=&quot;140&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1288&quot; data-origin-height=&quot;260&quot;/&gt;&lt;/span&gt;&lt;/figure&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;default는 자동으로 사용하도록 설정되는 namespace로 kubectl 명렁어로 k8s 리소스를 사용할때 default namespace를 사용합니다. 즉, &lt;b&gt;--namespace&lt;/b&gt;옵션을 명시하지 않으면 기본적으로 default namespace를 사용한다는 것이다. 이전 글에서 사용했던 명령어는 모두 default namespace를 사용한것과 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;612&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QRSbz/btrv6dAPI7n/q5ZEOdMbri5JFiWJHccF91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QRSbz/btrv6dAPI7n/q5ZEOdMbri5JFiWJHccF91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QRSbz/btrv6dAPI7n/q5ZEOdMbri5JFiWJHccF91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQRSbz%2Fbtrv6dAPI7n%2Fq5ZEOdMbri5JFiWJHccF91%2Fimg.png&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; alt=&quot;kubectl get pods -n kube-system&quot; loading=&quot;lazy&quot; width=&quot;733&quot; height=&quot;301&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1494&quot; data-origin-height=&quot;612&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위와 같이 kube-system namespace는 k8s 클러스터 구성에 필수적인 컴포넌트들과 설정값이 존재한는 namespace입니다. 위의 namespace는 pod에 관한 것들이지만 당연하게도 service, replicaset에도 별도의 namespace를 가지고 있습니다. 예로 다음은 kube-system namespace에는 k8s의 pod, service을 이름으로 찾을 수 있게 하는 DNS 서버의 service가 기본적으로 생성되어있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;112&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bhYVtO/btrv6cPre5a/2TV3H3JKBcyCh3mKGJCyB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bhYVtO/btrv6cPre5a/2TV3H3JKBcyCh3mKGJCyB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bhYVtO/btrv6cPre5a/2TV3H3JKBcyCh3mKGJCyB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbhYVtO%2Fbtrv6cPre5a%2F2TV3H3JKBcyCh3mKGJCyB1%2Fimg.png&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; alt=&quot;kube get svc -n kube-system&quot; loading=&quot;lazy&quot; width=&quot;706&quot; height=&quot;55&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1440&quot; data-origin-height=&quot;112&quot;/&gt;&lt;/span&gt;&lt;/figure&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;namespace는 k8s의 리소스를 논리적으로 묶을 수 있는 가상 클러스터처럼 사용할수 있고 여러명이 사용한다면 사용자마다 namespace를 별도로 취할수있습니다. 하지만 중요한 점은 논리적으로 구분된것이므로 물리적으로 격리된것이 아니기때문에 서로 다른 namespace에서 생성된 pod라도 같은 node에 존재가능합니다. 기존의 배웠던 label과의 차이점 및 장점은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;ResourceQuota object를 이용해 특정 namespace에서 생성되는 pod의 자원 사용량을 제한&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;애드미션 controller 기능을 이용해 특정 namespace에서 생성되는 pod에서는 항상 사이드카 container붙이도록 할수있음&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;사용목적에 따라 pod, service등의 리소스를 격리하여 구분가능&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.2 Namespace 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;namespace는 다음과 같이 production-ns.yaml을 통해서나 &lt;code&gt;kubectl create namespace&lt;/code&gt;로 생성가능합니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#production-ns.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: production&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# from yaml
kubectl apply -f production-ns.yaml
# from create
kubectl create namespace production&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;158&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bDLWKr/btrv0OQj1H0/7LWpKXgLkmlBDzMiqPlFW1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bDLWKr/btrv0OQj1H0/7LWpKXgLkmlBDzMiqPlFW1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bDLWKr/btrv0OQj1H0/7LWpKXgLkmlBDzMiqPlFW1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbDLWKr%2Fbtrv0OQj1H0%2F7LWpKXgLkmlBDzMiqPlFW1%2Fimg.png&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; alt=&quot;kubectl create namespace production&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;87&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;158&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 해당 namespace에 리소스를 생성하는 방법은 다음과 같이 metadata.namespace 항목에 만들어놓은 namespace를 입력하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#deployment-nginx-svc-ns.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-nginx-svc-ns
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      name: my-nginx-pod
      labels:
        app: my-nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.10
        ports:
        - containerPort: 80
--- 
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip-ns
  namespace: production
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: my-nginx
  type: ClusterIP&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;---&lt;/b&gt;을 통해 여러개의 리소스를 정의를 한 것이고 각 리소스에 namespace를 production으로 설정하고 다음과 같이 apply해보면 service와 deployment가 해당 production namespace에 생성된것을 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1539&quot; data-origin-height=&quot;456&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/czK7Lt/btrv4mSOCxJ/GEc2frSG1zzhbJJJ8L2xm1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/czK7Lt/btrv4mSOCxJ/GEc2frSG1zzhbJJJ8L2xm1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/czK7Lt/btrv4mSOCxJ/GEc2frSG1zzhbJJJ8L2xm1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FczK7Lt%2Fbtrv4mSOCxJ%2FGEc2frSG1zzhbJJJ8L2xm1%2Fimg.png&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; alt=&quot;kubectl apply svc and deployment&quot; loading=&quot;lazy&quot; width=&quot;691&quot; height=&quot;205&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1539&quot; data-origin-height=&quot;456&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.3 Namespace의 service에 접근&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 namespace에 존재하는 service에는 service이름만으로 접근 불가하게 됩니다. 예를 들어 다음과 같이 테스트용으로 만든 임시용 pod는 default namespace를 사용하므로 production namespace의 service에 접근하지 못합니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# run ubuntu pod for testing and connect to it
kubectl run -i --tty --rm debug --image=ubuntu:16.04 --restart=Never -- bash

#install curl
sudo apt-get update
sudo apt-get install curl

# http request to service of production namespace
curl svc-clusterip-ns:8080&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;64&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lS7yi/btrv0O3OZuA/So8nzdtZWQXPCNzYv4LZ6K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lS7yi/btrv0O3OZuA/So8nzdtZWQXPCNzYv4LZ6K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lS7yi/btrv0O3OZuA/So8nzdtZWQXPCNzYv4LZ6K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlS7yi%2Fbtrv0O3OZuA%2FSo8nzdtZWQXPCNzYv4LZ6K%2Fimg.png&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; alt=&quot;curl svc-clusterip-ns:8080&quot; loading=&quot;lazy&quot; width=&quot;525&quot; height=&quot;36&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;940&quot; data-origin-height=&quot;64&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 &lt;b&gt;$&amp;lt;$service 이름$&amp;gt;$$&amp;lt;$namespace 이름$&amp;gt;$.svc&lt;/b&gt;와 같이 service이름 뒤에 namespace를 붙이면 다른 namespace에 접근가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dHNKy1/btrv5mx0MqT/6MJxBJPjK7MmUGhAw8imeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dHNKy1/btrv5mx0MqT/6MJxBJPjK7MmUGhAw8imeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dHNKy1/btrv5mx0MqT/6MJxBJPjK7MmUGhAw8imeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdHNKy1%2Fbtrv5mx0MqT%2F6MJxBJPjK7MmUGhAw8imeK%2Fimg.png&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; alt=&quot;curl by namespace&quot; loading=&quot;lazy&quot; width=&quot;560&quot; height=&quot;99&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;1008&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;namespace는 &lt;code&gt;kubectl delete -f [yaml 파일]&lt;/code&gt; 또는 &lt;code&gt;kubectl delete namespace&lt;/code&gt;명령어로 삭제가능하며 namespace삭제시 해당 namespace에 존재하는 리소스도 함께 삭제됩니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1.4 namespace에 종속되는 k8s object와 독립적인 object&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A라는 namespace에 존재한는 pod는 A namespace에서만 보이고 B namespace에는 보이지 않습니다. 이를 &lt;b&gt;o&lt;span style=&quot;color: #0593d3;&quot;&gt;bject가 namespace에 속한다(namespaced)&lt;/span&gt;&lt;/b&gt;라고 표현합니다. namespace에 속하는 object의 종류는 &lt;code&gt;kubectl api-resources --namespaced=true&lt;/code&gt;로 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;550&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HDU4D/btrv0PBHkZV/ke6CXpZPia53SHfbamkwzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HDU4D/btrv0PBHkZV/ke6CXpZPia53SHfbamkwzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HDU4D/btrv0PBHkZV/ke6CXpZPia53SHfbamkwzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHDU4D%2Fbtrv0PBHkZV%2Fke6CXpZPia53SHfbamkwzk%2Fimg.png&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; alt=&quot;kubectl namespaced&quot; loading=&quot;lazy&quot; width=&quot;815&quot; height=&quot;254&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;550&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위에서 설명한 pod, service, deployment가 namespace에 속하는 것을 확인가능하다. 그럼 반대로 속하지 않는 object로는 다음과 같이 node, namespace그 자체도 포함이다. 그래서 namespace에 포함하려해도 포함되지 않는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;283&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bf10v9/btrv3hxp0Q4/PK1NH6tjEH1AVlNaTm1WT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bf10v9/btrv3hxp0Q4/PK1NH6tjEH1AVlNaTm1WT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bf10v9/btrv3hxp0Q4/PK1NH6tjEH1AVlNaTm1WT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbf10v9%2Fbtrv3hxp0Q4%2FPK1NH6tjEH1AVlNaTm1WT0%2Fimg.png&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; alt=&quot;kubectl not namespaced&quot; loading=&quot;lazy&quot; width=&quot;791&quot; height=&quot;127&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;283&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Configmap, Secret: 설정값을 Pod에 전달&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 application은 설정값을 가지고 있습니다. 예를 들어 application loggin level을 정의하는 LOG_LEVEL=INFO와 같이 단순한 key-value형태의 설정을 사용할 수 있습니다. 이를 위해 k8s는 pod를 정의하는 YAML파일에 환경변수를 직접 적어놓은 하드 코딩방식을 사용할 수 있습니다. 아래는 기존에 사용하던 deployment-nginx.yaml에 env환경변수를 추가해 env-deployment-nginx.yaml로 만든 예시입니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#env-deployment-nginx.yaml
...
  spec:
    containers:
    - name: nginx
      env:
      - name: LOG_LEVEL
        value: INFO
      image: nginx:1.10
...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위는 pod의 LOG_LEVEL이라는 이름의 환경변수를 INFO라고 설정한 것입니다. 이렇게 환경 변수를 직접 pod template에 명시해두 되지만 운영 및 개발 환경에서 각각 다른 deplotment를 생성해야한다면 환경변수가 서로 다르게 설정된 두 가지 버전의 YAML이 필요하게 됩니다. k8s에서는 YAML파일과 설정값을 분리 할 수 있는 &lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;Configmap&lt;/span&gt;&lt;/b&gt;과 &lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;Secret&lt;/span&gt;&lt;/b&gt;의 object를 제공합니다. Configmap은 설정값을, Secret은 노출되어서는 안되는 비밀 값을 저장합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 configmap을 이용한다면 1개의 pod YAML파일만을 사용하되 환경에 따라 다른 configmap을 생성해 사용할 수 있다. 즉, 환경 변수나 설정값까지 k8s object에서 관리하므로 이러한 설정값 또한 YAML파일로 pod와 함께 배포가능하다는 장점이 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Configmap 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적인 설정값을 담아 저장할 수 있는 k8s object이고 namespace에 속하기 때문에 namespace별로 configmap이 존재한다. YAML을 사용해서 configmap을 생성할 수 있지만 &lt;code&gt;kubectl create cm [configmap 이름] $[$각종 설정값들$]$ &lt;/code&gt;을 통해 쉽게 생성가능하다. (cm은 configmap과 동일)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;78&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d6AdPu/btrv0QN7FG7/k0RP14bu7qPESFSTBFxlUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d6AdPu/btrv0QN7FG7/k0RP14bu7qPESFSTBFxlUk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d6AdPu/btrv0QN7FG7/k0RP14bu7qPESFSTBFxlUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd6AdPu%2Fbtrv0QN7FG7%2Fk0RP14bu7qPESFSTBFxlUk%2Fimg.png&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; alt=&quot;kubectl create configmap&quot; loading=&quot;lazy&quot; width=&quot;775&quot; height=&quot;36&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;1676&quot; data-origin-height=&quot;78&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 &lt;b&gt;--from-literal&lt;/b&gt;옵션을 여러번 사용함으로써 여러 개의 key-value을 configmap에서 사용하도록 할수 있습니다. 그리고 configmap에 저장된 설정값은 &lt;code&gt;kubectl describe cm&lt;/code&gt;과 &lt;code&gt;kubectl get cm -o yaml&lt;/code&gt;으로 확인 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;2004&quot; data-origin-height=&quot;68&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/B7no3/btrv4KlDgJ2/hkIGZkYNJYO2kCkxyIdql1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/B7no3/btrv4KlDgJ2/hkIGZkYNJYO2kCkxyIdql1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/B7no3/btrv4KlDgJ2/hkIGZkYNJYO2kCkxyIdql1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FB7no3%2Fbtrv4KlDgJ2%2FhkIGZkYNJYO2kCkxyIdql1%2Fimg.png&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; alt=&quot;kubectl create configmap --from-literal&quot; loading=&quot;lazy&quot; width=&quot;830&quot; height=&quot;28&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;2004&quot; data-origin-height=&quot;68&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;762&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tsyN2/btrv0Qghkt2/zA2Xe08F9crcracPeLdkRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tsyN2/btrv0Qghkt2/zA2Xe08F9crcracPeLdkRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tsyN2/btrv0Qghkt2/zA2Xe08F9crcracPeLdkRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtsyN2%2Fbtrv0Qghkt2%2FzA2Xe08F9crcracPeLdkRK%2Fimg.png&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; alt=&quot;kubectl describe cm&quot; loading=&quot;lazy&quot; width=&quot;431&quot; height=&quot;330&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;996&quot; data-origin-height=&quot;762&quot;/&gt;&lt;/span&gt;&lt;/figure&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;configmap의 설정값을 pod에 적용해보기 전에 어떤 방법으로 configmap이 사용되는지를 알아보죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eD2oBQ/btrv4I89ywi/eu169q2VG2pOo12kjTUot1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eD2oBQ/btrv4I89ywi/eu169q2VG2pOo12kjTUot1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eD2oBQ/btrv4I89ywi/eu169q2VG2pOo12kjTUot1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeD2oBQ%2Fbtrv4I89ywi%2Feu169q2VG2pOo12kjTUot1%2Fimg.png&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; alt=&quot;pricimple of configmap&quot; loading=&quot;lazy&quot; width=&quot;716&quot; height=&quot;273&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1614&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p 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&gt;&lt;b&gt;configmap의 값을 container 환경 변수로 사용&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위와 같은 그림이 해당 예시입니다. container내부의 환경변수 key-value값으로 설정하는 경우입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;configmap의 값을 pod 내부의 파일로 마운트해 사용&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;configmap의 값을 pod container내부의 특정 파일로 마운트합니다.&lt;/li&gt;
&lt;li&gt;예를 들어 LOG_LEVEL=INFO라는 값을 가지는 configmap을 /etc/config/log_level이라는 파일로 마운트하면 log_level파일에 INFO라는 값이 저장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;config map의 데이터를 container 환경 변수로 가져오기&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 실제로 configmap의 설정값을 pod에 적용해보기 위해 다음과 같은 내용으로 env-configmap.yaml을 만들어보죠.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#env-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: container-env-configmap
spec:
  containers:
    - name: my-container
      image: busybox
      args: ['tail', '-f', '/dev/null']
      envFrom:
      - configMapRef:
          name: log-level-configmap
      - configMapRef:
          name: start-k8s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;configmap과 연동되는 부분이 &lt;b&gt;envFrom&lt;/b&gt;과 &lt;b&gt;configMapRef&lt;/b&gt; 항목입니다. 이전에 생성한 log-level-configmap과 start-k8s라는 configmap의 값을 가져와 환경변수로 설정하는 YAML입니다. &lt;b&gt;envFrom&lt;/b&gt;은 하나의 configmap에 여러 개의 key-value 쌍이 존재하더라도 모두 환경변수로 가져올수 있게 하고 &lt;b&gt;configMapRef&lt;/b&gt;하위항목의 name와 매칭되는 configmap을 명시하게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;368&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgAyVt/btrv6d8EADu/cbUnXCTFBhfrVBIP4tz440/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgAyVt/btrv6d8EADu/cbUnXCTFBhfrVBIP4tz440/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgAyVt/btrv6d8EADu/cbUnXCTFBhfrVBIP4tz440/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgAyVt%2Fbtrv6d8EADu%2FcbUnXCTFBhfrVBIP4tz440%2Fimg.png&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; alt=&quot;kubectl apply env&quot; loading=&quot;lazy&quot; width=&quot;594&quot; height=&quot;185&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1182&quot; data-origin-height=&quot;368&quot;/&gt;&lt;/span&gt;&lt;/figure&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;그리고 다음과 같이 &lt;b&gt;envFrom&lt;/b&gt;과 다르게 &lt;b&gt;valueFrom&lt;/b&gt;, &lt;b&gt;configMapKeyRef&lt;/b&gt;으로는 key값도 넣어서 해당 configmap에 해당하는 key값을 선택하여 그에 대한 value값을 환경변수로 설정하게됩니다. 그래서 아래를 보면 ENV_KEYNAME_1이라는 환경변수 key값을 만들고 그에 대한 value값을 log-level-configmap의 LOG_LEVEL의 value값으로 설정합니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#env-valuefrom-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: container-valuefrom-env-configmap
spec:
  containers:
    - name: my-container
      image: busybox
      args: ['tail', '-f', '/dev/null']
      env:
      - name: ENV_KEYNAME_1 #container에 새롭게 등록될 환경 변수 key값
        valueFrom:
          configMapKeyRef:
            name: log-level-configmap
            key: LOG_LEVEL
      - name: ENV_KEYNAME_2
        valueFrom:
          configMapKeyRef:
            name: start-k8s
            key: k8s&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dn4JRa/btrvZ3mDPrY/RF49BkygUJBBV6txWWm3fk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dn4JRa/btrvZ3mDPrY/RF49BkygUJBBV6txWWm3fk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dn4JRa/btrvZ3mDPrY/RF49BkygUJBBV6txWWm3fk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fdn4JRa%2FbtrvZ3mDPrY%2FRF49BkygUJBBV6txWWm3fk%2Fimg.png&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; alt=&quot;kubectl exec env&quot; loading=&quot;lazy&quot; width=&quot;716&quot; height=&quot;109&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1404&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;configmap의 내용을 파일로 pod 내부에 마운트&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;application이 nginx.conf와 같은 특정 파일로부터 설정값을 읽어올 수 있습니다. 예를 들어, 다음과 같은 YAML파일은 start-k8s configmap에 존재하는 모든 key-value 쌍을 /etc/config 디렉터리에 위치시킵니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#env-volume-configmap.yaml
apiVersion: v1
kind: Pod
metadata:
  name: volume-env-configmap
spec:
  containers:
    - name: my-container
      image: busybox
      args: [ &quot;tail&quot;, &quot;-f&quot;, &quot;/dev/null&quot; ]
      volumeMounts:
      - name: configmap-volume          # volumes에서 정의한 컨피그맵 볼륨 이름 
        mountPath: /etc/config             # 컨피그맵의 데이터가 위치할 경로

  volumes:
    - name: configmap-volume            # 컨피그맵 볼륨 이름
      configMap:
        name: start-k8s&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;volumeMounts&lt;/b&gt;와 &lt;b&gt;volumes&lt;/b&gt;라는 항목은 다음과 같은 역할을 수행합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;spec.volumes:&lt;/b&gt; YAML파일에서 사용할 볼륨의 목록을 정의합니다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에서는 start-k8s라는 configmap을 통해 configmap-volume을 정의하였습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spec.containers.volumeMounts:&lt;/b&gt; volume 항목에서 정의된 볼륨을 container내부의 어떤 디렉터리에 마운트할것인지 명시
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;위에서는 /etc/config 디렉터리에 configmap의 값이 담긴 파일이 마운트됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nsXQg/btrv3hjUs1u/dr20eVp6PYKK2qSeABVFd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nsXQg/btrv3hjUs1u/dr20eVp6PYKK2qSeABVFd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nsXQg/btrv3hjUs1u/dr20eVp6PYKK2qSeABVFd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnsXQg%2Fbtrv3hjUs1u%2Fdr20eVp6PYKK2qSeABVFd0%2Fimg.png&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; alt=&quot;kubectl exec volume&quot; loading=&quot;lazy&quot; width=&quot;645&quot; height=&quot;99&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;1428&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위처럼 /etc/config라는 폴더에 start-k8s의 key값들로 파일을 만들고 해당 파일내용에는 value값이 들어간다. 그리고 다음과 같이 volumes항목을 다음과 같이 바꾸면 원하는 key-value쌍만 가져올수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#env-volume-configmap.yaml (원하는 key-value선택)
...
  volumes:
    - name: configmap-volume            # 컨피그맵 볼륨 이름
      configMap:
        name: start-k8s
        items:
        - key: k8s
          path: k8s_fullname&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;items&lt;/b&gt;: configmap에서 가져올 key-value의 목록을 뜻함&lt;/li&gt;
&lt;li&gt;&lt;b&gt;path&lt;/b&gt;: 최종적으로 디렉터리에 위치할 파일의 이름을 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;144&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mc6Ty/btrv6dt4Vdi/aYKAZjGgtBbWgHkMiQCLKK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mc6Ty/btrv6dt4Vdi/aYKAZjGgtBbWgHkMiQCLKK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mc6Ty/btrv6dt4Vdi/aYKAZjGgtBbWgHkMiQCLKK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fmc6Ty%2Fbtrv6dt4Vdi%2FaYKAZjGgtBbWgHkMiQCLKK%2Fimg.png&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; alt=&quot;kubectl exec get key and value&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;65&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;1468&quot; data-origin-height=&quot;144&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;파일로부터 configmap 생성&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 환경에서는 설정 파일 그자체를 configmap으로 사용하는 경우가 많습니다. 이를 위해 기존의 configmap생성시 &lt;b&gt;--from-literal&lt;/b&gt;옵션 대신 &lt;b&gt;--from-file&lt;/b&gt;이라는 옵션을 통해 파일로 부터 configmap을 생성가능합니다.&lt;/p&gt;
&lt;pre class=&quot;sas&quot;&gt;&lt;code&gt;kubectl create configmap &amp;lt;configmap 이름&amp;gt; --from-file &amp;lt;파일 이름&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;502&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmaFxs/btrv4lGmmvR/vUgtKbCTfotzOPjcsozHsk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmaFxs/btrv4lGmmvR/vUgtKbCTfotzOPjcsozHsk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmaFxs/btrv4lGmmvR/vUgtKbCTfotzOPjcsozHsk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmaFxs%2Fbtrv4lGmmvR%2FvUgtKbCTfotzOPjcsozHsk%2Fimg.png&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; alt=&quot;kubectl create configmap using file&quot; loading=&quot;lazy&quot; width=&quot;665&quot; height=&quot;235&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;1420&quot; data-origin-height=&quot;502&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위와같이 index.html에서 별도의 key를 정의하지 않았으므로 파일 이름(index.html)이 key이고 해당 value는 파일내용이됩니다. 그리고 &lt;b&gt;--from-env-file&lt;/b&gt;옵션으로 여러개의 key-value형태로 구성된 설정파일을 한번에 가져올 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;#file-configmap.env
a=1
b=2
c=3&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;mel&quot;&gt;&lt;code&gt;kubectl create configmap file-env-configmap --from-env-file file-configmap.env&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20.png&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpsRGY/btrv6WZUEfM/bOt9BkvMz2gL2cUaJaTbhk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpsRGY/btrv6WZUEfM/bOt9BkvMz2gL2cUaJaTbhk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpsRGY/btrv6WZUEfM/bOt9BkvMz2gL2cUaJaTbhk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpsRGY%2Fbtrv6WZUEfM%2FbOt9BkvMz2gL2cUaJaTbhk%2Fimg.png&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; alt=&quot;kubectl create configmap using file&quot; loading=&quot;lazy&quot; width=&quot;702&quot; height=&quot;133&quot; data-filename=&quot;20.png&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&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;정적 파일을 pod에 제공하려면 --from-file을 사용하고 여러개의 환경 변수를 pod로 가져올 경우는 --from-env-file을 쓰시면 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Secret&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #6164c6;&quot;&gt;&lt;b&gt;Secret은 ssh key, 비밀번호와 같은 보안이 필요한 정보를 저장하기 위해 사용되는 object&lt;/b&gt;&lt;/span&gt;입니다. 사용방법은 configmap과 유사하다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 image registry 접근을 위한 docker&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 docker hub에 private한 docker image를 올렸습니다. (직접 예제를 진행해보려면 docker hub에서 image push하셔야 하시고 private로 변경해야합니다.) 해당 private docker image를 pull받으려면 해당 docker image를 소유하는 계정의 id, password가 필요하겠죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4vlqC/btrv6WMm7e8/kxdIy48rL7VngSIGnXiDWk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4vlqC/btrv6WMm7e8/kxdIy48rL7VngSIGnXiDWk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4vlqC/btrv6WMm7e8/kxdIy48rL7VngSIGnXiDWk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4vlqC%2Fbtrv6WMm7e8%2FkxdIy48rL7VngSIGnXiDWk%2Fimg.png&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; alt=&quot;private docker hub&quot; loading=&quot;lazy&quot; width=&quot;334&quot; height=&quot;183&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;616&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&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;그러면 지금부터 secret object로 id, password에 대한 정보를 생성하고 이를 deployment를 만드는 yaml에 해당 secret정보를 넣어 pod를 만드는 예제를 진행해보죠. secret키를 만드는 명령어는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl create secret docker-registry registry-auth --docker-username=das2o --docker-password=&amp;lt;비밀번호&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;docker-registry:&lt;/b&gt; secret 타입으로 이외의 다른것(Opaque)등이 존재&lt;/li&gt;
&lt;li&gt;&lt;b&gt;registry-auth&lt;/b&gt;: secret 이름&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;618&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Kqvka/btrv6WS8LK1/l0mJZ4GeN8edvSK9Wqv9lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Kqvka/btrv6WS8LK1/l0mJZ4GeN8edvSK9Wqv9lk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Kqvka/btrv6WS8LK1/l0mJZ4GeN8edvSK9Wqv9lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKqvka%2Fbtrv6WS8LK1%2Fl0mJZ4GeN8edvSK9Wqv9lk%2Fimg.png&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; alt=&quot;kubectl create secret&quot; loading=&quot;lazy&quot; width=&quot;744&quot; height=&quot;230&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;618&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위와 같이 &lt;code&gt;kubectl get secret [secret 이름] -o yaml&lt;/code&gt;로 .dockerconfigjson이라는 데이터를 확인가능하고 해당 데이터는 base64로 인코딩되어있으므로 다음과 같이 디코딩가능하다. 디코딩해보면 secret을 만들때의 정보들을 볼 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;23.png&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;150&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bcsISv/btrv11aQ6zT/Ma0SfTLGzuuCKZGPAFZ7P1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bcsISv/btrv11aQ6zT/Ma0SfTLGzuuCKZGPAFZ7P1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bcsISv/btrv11aQ6zT/Ma0SfTLGzuuCKZGPAFZ7P1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbcsISv%2Fbtrv11aQ6zT%2FMa0SfTLGzuuCKZGPAFZ7P1%2Fimg.png&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; alt=&quot;get secret value&quot; loading=&quot;lazy&quot; width=&quot;781&quot; height=&quot;59&quot; data-filename=&quot;23.png&quot; data-origin-width=&quot;1999&quot; data-origin-height=&quot;150&quot;/&gt;&lt;/span&gt;&lt;/figure&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;그리고 다음은 deployment를 생성하는 yaml이다. 해당 object에서는 private image를 pull하기위해서 secret object를 지정해줘야만 한다. 다음과 같이 정의해주면 secret object의 key-value값을 통하여 계정 인증을 하고난뒤 private image pull이 가능해지는 것이다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#deployment-secret.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment-secret
spec:
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      name: mypod
      labels:
        app: myapp
    spec:
      containers:
      - name: test-container
        image: da2so/test_repo:0.0
        args: ['tail', '-f', '/dev/null']
      imagePullSecrets:
      - name: registry-auth&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;24.png&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;222&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K6N4p/btrv4medi1g/4pr0fzckWLTfGV7Arufl60/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K6N4p/btrv4medi1g/4pr0fzckWLTfGV7Arufl60/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K6N4p/btrv4medi1g/4pr0fzckWLTfGV7Arufl60/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK6N4p%2Fbtrv4medi1g%2F4pr0fzckWLTfGV7Arufl60%2Fimg.png&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; alt=&quot;kubectl apply secret&quot; loading=&quot;lazy&quot; width=&quot;549&quot; height=&quot;97&quot; data-filename=&quot;24.png&quot; data-origin-width=&quot;1260&quot; data-origin-height=&quot;222&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. 리소스 정리&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제들은 생성한 리소스가 많을텐데 다 지우고 싶으면 다음과 같은 명령어를 사용한다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl delete deployment --all
kubectl delete pod --all
kubectl delete configmap --all
kubectl delete secret --all &lt;/code&gt;&lt;/pre&gt;</description>
      <category>AI Engineering/MLOps</category>
      <category>kubernetes</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/37</guid>
      <comments>https://da2so.tistory.com/37#entry37comment</comments>
      <pubDate>Tue, 15 Mar 2022 21:55:40 +0900</pubDate>
    </item>
    <item>
      <title>Docker/Kubernetes - (10) Kubernetes 이해 및 사용</title>
      <link>https://da2so.tistory.com/36</link>
      <description>&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&lt;b&gt;Environment&lt;/b&gt;: Ubuntu 18.04&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;1. Kubernetes(k8s) 이해&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kubernetes(k8s)이 가지는 고유한 특성에 대해 알아봅시다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: darkorchid;&quot;&gt;1. 모든 리스소는 object형태로 관리됨&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 swarm mode의 container 집합을 service(서비스)라고 하였습니다. K8s은 이러한 개념을 폭넓고 세밀한 단위로 사용하기 위해 &lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;object&lt;/span&gt;&lt;/b&gt;라는 개념을 사용합니다. 예로 container 집합(Pods), Pods을 관리하는 컨트롤러(Replica Set), 사용자(Service Account), 노드(Node) 모두를 하나의 object로 사용가능합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용가능 한 object는 &lt;code&gt;kubectl api-resources&lt;/code&gt;명령어로 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;2034&quot; data-origin-height=&quot;170&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bVRNAf/btrv5mK9N9F/hcoCk0KrKjRvvPH7MlPldk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bVRNAf/btrv5mK9N9F/hcoCk0KrKjRvvPH7MlPldk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bVRNAf/btrv5mK9N9F/hcoCk0KrKjRvvPH7MlPldk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbVRNAf%2Fbtrv5mK9N9F%2FhcoCk0KrKjRvvPH7MlPldk%2Fimg.png&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; alt=&quot;kubectl api-resources&quot; loading=&quot;lazy&quot; width=&quot;824&quot; height=&quot;69&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;2034&quot; data-origin-height=&quot;170&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: darkorchid;&quot;&gt;2. YAML파일을 통한 k8s 사용&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;swarm mode의 container service를 생성하는데 사용했던 &lt;code&gt;docker service create&lt;/code&gt;명령어와 같이 k8s은 &lt;code&gt;kubectl&lt;/code&gt;을 사용합니다. swarm mode의 stack과 같이 k8s에서도 YAML파일을 사용가능하며 많이 사용합니다. YAML파일을 통해 container뿐만 아니라 거의 모든 리소스 object들에 사용될 수 있다는 장점을 가집니다. 또한 kubectl 명령어가 아닌 여러 개의 YAML파일을 정의해 서비스를 k8s에 적용시킬 수 있습니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: darkorchid;&quot;&gt;3. 여러 개의 컴포넌트로 구성됨&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k8s에서 노드의 역할은 크게 &lt;b&gt;마스터&lt;/b&gt;와 &lt;b&gt;워커&lt;/b&gt;로 나뉘어져 있습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;마스터 노드&lt;/b&gt;&lt;/span&gt;: clutser를 관리&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;워커 노드&lt;/b&gt;&lt;/span&gt;: apllication container생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;swarm mode에서 단일 docker daemon만을 설치한 것과 다르게 k8s는 docker를 포함한 많은 컴포넌트들을 설치 및 실행하게 됩니다. 예로 마스터 노드에서는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;API 서버&lt;/b&gt;&lt;/span&gt;(kube-apiserver), &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;컨트롤러 매니저&lt;/b&gt;&lt;/span&gt;(kube-controller-manager), &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;스케줄러&lt;/b&gt;&lt;/span&gt;(kube-scheduler), &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;DNS서버&lt;/b&gt;&lt;/span&gt;(coreDNS)등이 실행되며, 모든 노드에서는 네트워크 구성을 위해 &lt;b&gt;프락시&lt;/b&gt;(proxy)와 &lt;b&gt;네트워크 플러그인&lt;/b&gt;(calico, flannel 등)이 설치 및 실행됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 k8s clutser 구성을 위해 &lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;kubelet&lt;/span&gt;&lt;/b&gt;이라는 agent가 모든 노드에서 실행됩니다. kubelet은 container 생성, 삭제뿐만 아니라 마스터와 워커 노드간의 통신 역할을 함께 담당하는 agent입니다. k8s입장에서 docker daemon도 하나의 컴포넌트로 인식한다는 것도 알아두면 좋겠네요.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;2. Pod: container를 다루는 기본 단위&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.1 Pod 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;container applicaton의 기본 단위를 Pod라고 부르며 Pod는 1개 이상의 container로 구성된 container집합입니다. 예로 Nginx 웹 서비스를 k8s에서 생성하려고 다음 그림과 같이 pod 1개에 nginx 1개만을 포함하도록 생성가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;345&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w4fux/btrv12NSGkD/HmaT8CWkb3I2Jktkika431/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w4fux/btrv12NSGkD/HmaT8CWkb3I2Jktkika431/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w4fux/btrv12NSGkD/HmaT8CWkb3I2Jktkika431/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw4fux%2Fbtrv12NSGkD%2FHmaT8CWkb3I2Jktkika431%2Fimg.png&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; alt=&quot;pod&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;147&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;345&quot;/&gt;&lt;/span&gt;&lt;/figure&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;이제 실제로 Nginx container로 구성된 pod을 생성해봅시다. &lt;b&gt;nginx-pod.yaml&lt;/b&gt;파일에 다음 내용을 담도록 합시다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# nginx-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: my-nginx-pod
spec:
  containers:
  - name: nginx-container
    image: nginx:latest
    ports:
    - containerPort: 80
      protocol: TCP&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;apiVersion&lt;/b&gt;: YAML 파일에서 정의한 object의 API 버전&lt;/li&gt;
&lt;li&gt;&lt;b&gt;kind&lt;/b&gt;: 리소스의 종류
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용가능한 목록은 &lt;code&gt;kubectl api-resources&lt;/code&gt;명령어의 KIND항목에서 확인 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;metadata&lt;/b&gt;: 라벨, 주석, 이름과 같은 부가 정보를 입력
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;name항목을 통해 pod의 고유 이름을 지정함&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spec&lt;/b&gt;: 리소스를 생성하기 위한 정보를 입력
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;containers 항목을 입력해 다음과 하위항목을 지정&lt;/li&gt;
&lt;li&gt;&lt;b&gt;image&lt;/b&gt;: docker image&lt;/li&gt;
&lt;li&gt;&lt;b&gt;name&lt;/b&gt;: container 이름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;ports&lt;/b&gt;: nginx container가 사용할 port번호&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 해당 YAML파일을 통해 &lt;code&gt;kubectl apply -f&lt;/code&gt;명령어를 통해 pod를 생성한다. 그리고 생성 확인을 위해 k8s에 존재하는 pod를 출력하는 &lt;code&gt;kubectl get pods&lt;/code&gt;명령어를 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;220&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sYtgS/btrv4KZP7ez/4aCnH3BhRw6h71sJFw9Gtk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sYtgS/btrv4KZP7ez/4aCnH3BhRw6h71sJFw9Gtk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sYtgS/btrv4KZP7ez/4aCnH3BhRw6h71sJFw9Gtk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsYtgS%2Fbtrv4KZP7ez%2F4aCnH3BhRw6h71sJFw9Gtk%2Fimg.png&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; alt=&quot;kubectl get pods&quot; loading=&quot;lazy&quot; width=&quot;477&quot; height=&quot;118&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;890&quot; data-origin-height=&quot;220&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위의 YAML에서 사용할 포트(containerPort)는 정의하였지만 아직 외부에서 접근할 수 있도록 노출 된 상태는 아닙니다. 그래서 pod의 Nginx server로 요청을 보내려면 pod container내부 ip로 접근해야합니다. 생성된 리소스의 자세한 정보를 가져올 수 있는 명령어인 &lt;code&gt;kubectl describe&lt;/code&gt;을 이용해 해당 pod의 ip주소를 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;458&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r9wvu/btrv5oa9UkE/nKxTBzyBLhzPI787EkK9Uk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r9wvu/btrv5oa9UkE/nKxTBzyBLhzPI787EkK9Uk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r9wvu/btrv5oa9UkE/nKxTBzyBLhzPI787EkK9Uk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr9wvu%2Fbtrv5oa9UkE%2FnKxTBzyBLhzPI787EkK9Uk%2Fimg.png&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; alt=&quot;kubectl describe&quot; loading=&quot;lazy&quot; width=&quot;620&quot; height=&quot;246&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;458&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위에서 주목해야할 부분은 2개인데요. 하나는 IP주소입니다. 저희가 이전 글에서 설치했을때 설정한 k8s container의 네트워크 대역폭(172.31.0.0/16)대로 IP주소가 할당된 것을 확인가능하며 2번째는 마스터 노드에서 kubectl명령어를 사용했지만 할당된 노드는 워커 노드(worker2)인것을 확인할 수 있습니다.&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;본론으로 돌아와서 위의 IP주소는 172.31.189.65인데요 이는 외부에서 접근가능한 IP가 아니기때문에 cluster 내부에서만 접근가능합니다. 외부, 내부 모두에서 pod에 접근하려면 service라고 하는 object를 생성해야하지만 지금은 IP만으로 nginx pod에 접근해보죠. worker3에서 nginx pod의 ip로 http 요청을 전송해보고 잘 다음과 같이 잘되는 지 확인합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;324&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uDj5X/btrv2EzhUa3/gAnBkkUoZCLmgLAZINqd90/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uDj5X/btrv2EzhUa3/gAnBkkUoZCLmgLAZINqd90/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uDj5X/btrv2EzhUa3/gAnBkkUoZCLmgLAZINqd90/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuDj5X%2Fbtrv2EzhUa3%2FgAnBkkUoZCLmgLAZINqd90%2Fimg.png&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; alt=&quot;curl&quot; loading=&quot;lazy&quot; width=&quot;453&quot; height=&quot;161&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;324&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 다음명령어로 worker2가 아닌 마스터 노드에서 pod container내부로 직접 들어가봅시다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;kubectl exec -it my-nginx-pod bash&lt;/code&gt;&lt;/pre&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;262&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uEYEH/btrv3PG30pJ/7lK2n9YKWQv7RBwvssz2m1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uEYEH/btrv3PG30pJ/7lK2n9YKWQv7RBwvssz2m1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uEYEH/btrv3PG30pJ/7lK2n9YKWQv7RBwvssz2m1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuEYEH%2Fbtrv3PG30pJ%2F7lK2n9YKWQv7RBwvssz2m1%2Fimg.png&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; alt=&quot;kubectl exec&quot; loading=&quot;lazy&quot; width=&quot;717&quot; height=&quot;125&quot; data-filename=&quot;6.png&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;262&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bash를 셸을 실행시키고 &lt;b&gt;-it&lt;/b&gt;옵션은 셸을 유지할수 있게 해줍니다. 또한 &lt;code&gt;kubectl logs [pod 이름]&lt;/code&gt;을 통해 pod의 로그도 확인가능합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;286&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bOYnWu/btrvX7BYo4k/STP9OFiXsNFls0vPxmf4Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bOYnWu/btrvX7BYo4k/STP9OFiXsNFls0vPxmf4Kk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bOYnWu/btrvX7BYo4k/STP9OFiXsNFls0vPxmf4Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbOYnWu%2FbtrvX7BYo4k%2FSTP9OFiXsNFls0vPxmf4Kk%2Fimg.png&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; alt=&quot;kubectl logs&quot; loading=&quot;lazy&quot; width=&quot;786&quot; height=&quot;129&quot; data-filename=&quot;7.png&quot; data-origin-width=&quot;1746&quot; data-origin-height=&quot;286&quot;/&gt;&lt;/span&gt;&lt;/figure&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;k8s object는 &lt;code&gt;kubectl delete -f&lt;/code&gt;명령어로 삭제가능합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.2 pod vs docker container&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k8s에서 container가 아닌 pod를 사용하는 이유는 container runtime의 interface 제공 등 여러가지 이유가 있지만 그 중 하나는 &lt;b&gt;여러 리눅스 네임스페이스(namespace)&lt;/b&gt;을 공유하는 여러 container들을 추상화된 집합으로 사용하기 위함입니다. 예제를 위해 다음과 같이 nginx-ubuntu-pod.yaml파일을 작성해보죠.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#nginx-ubuntu-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx-ubuntu-pod
spec:
  containers:
  - name: nginx-container
    image: nginx:latest
    ports:
    - containerPort: 80
      protocol: TCP

  - name: ubuntu-container
    image: ubuntu:16.04
    command: [&quot;tail&quot;]
    args: [&quot;-f&quot;, &quot;/dev/null&quot;] # 포드가 종료되지 않도록 유지합니다&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전과 같이 &lt;code&gt;kubectl apply -f&lt;/code&gt;명령어로 해당 YAML을 k8s에 적용시켜 2개의 container가 실행중인 nginx 포드를 실행시킵니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;2096&quot; data-origin-height=&quot;182&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FcQMj/btrv3hw1VRj/lmOaY0KKLgkeeb5JskPBI0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FcQMj/btrv3hw1VRj/lmOaY0KKLgkeeb5JskPBI0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FcQMj/btrv3hw1VRj/lmOaY0KKLgkeeb5JskPBI0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFcQMj%2Fbtrv3hw1VRj%2FlmOaY0KKLgkeeb5JskPBI0%2Fimg.png&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; alt=&quot;kubectl apply -f&quot; loading=&quot;lazy&quot; width=&quot;803&quot; height=&quot;70&quot; data-filename=&quot;8.png&quot; data-origin-width=&quot;2096&quot; data-origin-height=&quot;182&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위와 같이 container2개를 생성했으므로 READY항목의 값이 2인것을 확인가능합니다. 그리고&lt;code&gt;kubectl exec&lt;/code&gt;로 ubuntu container에 접속합니다. &lt;b&gt;-c&lt;/b&gt; 옵션은 pod의 어떤 container에 대해 명령어를 수행할 지 명시합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;2316&quot; data-origin-height=&quot;110&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnpgXK/btrv4KS4dW2/bK6fUwiIDKNcT9i0kry0M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnpgXK/btrv4KS4dW2/bK6fUwiIDKNcT9i0kry0M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnpgXK/btrv4KS4dW2/bK6fUwiIDKNcT9i0kry0M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbnpgXK%2Fbtrv4KS4dW2%2FbK6fUwiIDKNcT9i0kry0M0%2Fimg.png&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; alt=&quot;kubectl exec c option&quot; loading=&quot;lazy&quot; width=&quot;839&quot; height=&quot;40&quot; data-filename=&quot;9.png&quot; data-origin-width=&quot;2316&quot; data-origin-height=&quot;110&quot;/&gt;&lt;/span&gt;&lt;/figure&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;ubuntu container안에서 다음 명령어를 입력합니다.&lt;/p&gt;
&lt;pre class=&quot;bash&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;# curl install
apt-get update
apt-get install curl -y

# http request to localhost
curl localhost&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bY4MIJ/btrv2Ezijdp/9Tv6OyWKkVyR9XFNlBBbkK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bY4MIJ/btrv2Ezijdp/9Tv6OyWKkVyR9XFNlBBbkK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bY4MIJ/btrv2Ezijdp/9Tv6OyWKkVyR9XFNlBBbkK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbY4MIJ%2Fbtrv2Ezijdp%2F9Tv6OyWKkVyR9XFNlBBbkK%2Fimg.png&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; alt=&quot;curl localhost&quot; loading=&quot;lazy&quot; width=&quot;414&quot; height=&quot;112&quot; data-filename=&quot;10.png&quot; data-origin-width=&quot;790&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위와 같이 localhost로 http 요청을 했는데도 응답이 도착합니다. ubuntu container가 nginx 서버를 실행하고 있지 않는데도 말이죠. 이는 pod내의 container들이 namespace등과 같은 linux namespace을 공유하기 때문입니다. container 네트워크 타입은 네트워크 namespace를 container간에 공유해 사용할 수 있도록 설정하기 때문에 여러개의 container가 동일한 네트워크 환경을 가지게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;531&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zdIil/btrvY31aknF/xqq9EGJnfkti05ccQbzJs0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zdIil/btrvY31aknF/xqq9EGJnfkti05ccQbzJs0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zdIil/btrvY31aknF/xqq9EGJnfkti05ccQbzJs0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzdIil%2FbtrvY31aknF%2Fxqq9EGJnfkti05ccQbzJs0%2Fimg.png&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; alt=&quot;request localhost from ubuntu to nginx&quot; loading=&quot;lazy&quot; width=&quot;361&quot; height=&quot;305&quot; data-filename=&quot;11.png&quot; data-origin-width=&quot;629&quot; data-origin-height=&quot;531&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2.3 완전한 application로서의 pod&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 k8s환경에서 1개의 container로 구성된 pod를 사용하는 경우가 많습니다. 이는 하나의 pod는 하나의 완전한 application이라는 점에 그렇습니다. 그러나 nginx container가 실행되기 위해 다른 부가적인 기능이 필요할 경우에는 pod의 주 container는 nginx가 되고 기능 확장을 위한 추가 container를 함께 pod에 포함할 수 있습니다. 부가적인 container를 &lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;sidecar container&lt;/span&gt;&lt;/b&gt;라고 부릅니다. pod에 포함된 container들은 모두 같은 워커 노드에서 함께 실행되고 이러한 구조 및 원리에 따라 pod에 정의된 여러개의 container는 &lt;b&gt;하나의 완전한 application&lt;/b&gt;으로 동작하게 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;3. Replica set: 일정 개수의 pod를 유지하는 controller&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.1 Replica set 사용 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같이 여러개의 동일한 container를 생성한 뒤 외부 요청이 각 container에 적절히 분배될 수 있도록 하는 마이크로서비스 구조에서 k8s는 replica set을 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;620&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FL4wL/btrvY3NFYLQ/gLFQnn7iS8tv65JANjUk3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FL4wL/btrvY3NFYLQ/gLFQnn7iS8tv65JANjUk3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FL4wL/btrvY3NFYLQ/gLFQnn7iS8tv65JANjUk3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFL4wL%2FbtrvY3NFYLQ%2FgLFQnn7iS8tv65JANjUk3K%2Fimg.png&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; alt=&quot;replica set&quot; loading=&quot;lazy&quot; width=&quot;605&quot; height=&quot;318&quot; data-filename=&quot;12.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;620&quot;/&gt;&lt;/span&gt;&lt;/figure&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;즉, replica set이라는 object를 통해서 다음과 같은 역할을 수행하도록합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;정해진 수의 동일한 Pod가 항상 실행되도록 관리&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #0593d3;&quot;&gt;&lt;b&gt;노드 장애 등의 이유로 pod를 사용할수 없다면 다른 노드에서 pod를 다시 생성함&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.2 Replica set 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx pod를 생성하는데 replica set을 사용해보겠습니다. 다음과 같은 내용으로 replicaset-nginx.yaml을 만들어봅시다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#replicaset-nignx.yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: replicaset-nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-nginx-pods-label
  template:
    metadata:
      name: my-nginx-pod
      labels: 
        app: my-nginx-pods-label
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;spec.replicas: 동일한 pod을 몇개 유지 시킬 것인지 설정&lt;/li&gt;
&lt;li&gt;spec.template 아래 내용들: pod생성할 때 사용할 template정의
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pod 생성에 사용했던 내용을 동일하게 replicaset에서도 정의하여 pod의 구성 내용을 담음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스의 고유한 이름은 모든 object에 설정가능하므로 replicaset의 name을 replicaset-nginx로 설정하였습니다. 위 내용의 파일로 replicaset을 만들어보죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;72&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1Ml6W/btrv3fsDFCa/SzKuYu3i3ah5IOePwRSvQ1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1Ml6W/btrv3fsDFCa/SzKuYu3i3ah5IOePwRSvQ1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1Ml6W/btrv3fsDFCa/SzKuYu3i3ah5IOePwRSvQ1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1Ml6W%2Fbtrv3fsDFCa%2FSzKuYu3i3ah5IOePwRSvQ1%2Fimg.png&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; alt=&quot;kubectl apply replicaset&quot; loading=&quot;lazy&quot; width=&quot;573&quot; height=&quot;42&quot; data-filename=&quot;13.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;72&quot;/&gt;&lt;/span&gt;&lt;/figure&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;&lt;code&gt;kubectl get po&lt;/code&gt;와 &lt;code&gt;kubectl get rs&lt;/code&gt;으로 정상적으로 3개의 pod가 생성되었는지 확인합니다. po는 pods alias, rs는 replicasets의 alias로 사용됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;302&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zgqa8/btrv6eMWko9/kxR8qkQACk9pk3gmGG4AIk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zgqa8/btrv6eMWko9/kxR8qkQACk9pk3gmGG4AIk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zgqa8/btrv6eMWko9/kxR8qkQACk9pk3gmGG4AIk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZgqa8%2Fbtrv6eMWko9%2FkxR8qkQACk9pk3gmGG4AIk%2Fimg.png&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; alt=&quot;kubectl get rs&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;172&quot; data-filename=&quot;14.png&quot; data-origin-width=&quot;1102&quot; data-origin-height=&quot;302&quot;/&gt;&lt;/span&gt;&lt;/figure&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;여기서 pod의 개수를 4개로 늘리고 싶다면 YAML파일에서 replicas의 숫자를 4로만 변경하고 다시 &lt;code&gt;kubectl apply -f&lt;/code&gt;명령어를 사용합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/29bru/btrv3hjAa7P/AhHEsCdIewTNkjvHtTQ8t1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/29bru/btrv3hjAa7P/AhHEsCdIewTNkjvHtTQ8t1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/29bru/btrv3hjAa7P/AhHEsCdIewTNkjvHtTQ8t1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F29bru%2Fbtrv3hjAa7P%2FAhHEsCdIewTNkjvHtTQ8t1%2Fimg.png&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; alt=&quot;change the number of pods to 4&quot; loading=&quot;lazy&quot; width=&quot;591&quot; height=&quot;158&quot; data-filename=&quot;15.png&quot; data-origin-width=&quot;1086&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;3.3 replicaset 동작원리&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;pod와 replicaset은 느슨한 연결(loosely coupled)을 유지하며 이러한 느슨한 연결은 pod와 replicaset의 정의 중&lt;b&gt; &lt;span style=&quot;color: crimson;&quot;&gt;Label Selector&lt;/span&gt;&lt;/b&gt;를 이용해 이뤄집니다. 위에서 만든 YAML을 봐봅시다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;793&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cAo0fj/btrv11hegdT/d0LtWloTfAOku0heW6j4I1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cAo0fj/btrv11hegdT/d0LtWloTfAOku0heW6j4I1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cAo0fj/btrv11hegdT/d0LtWloTfAOku0heW6j4I1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcAo0fj%2Fbtrv11hegdT%2Fd0LtWloTfAOku0heW6j4I1%2Fimg.png&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; alt=&quot;replicaset.yaml&quot; loading=&quot;lazy&quot; width=&quot;511&quot; height=&quot;376&quot; data-filename=&quot;16.png&quot; data-origin-width=&quot;1079&quot; data-origin-height=&quot;793&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위에서 replicaset영역과 pod영역에 정의된 highligt된 label은 서로 다른 object가 서로를 찾기 위해 사용됩니다. replicaset은 &lt;b&gt;spec.selector.matchLabel&lt;/b&gt;에 정의된 label을 통해 생성해야하는 pod를 찾습니다. 즉, &lt;b&gt;app:my-nginx-pods-label&lt;/b&gt; label을 가지는 pod의 개수가 replicas 항목에 정의된 숫자인 3개와 일치하지 않으면 pod을 정의하는 pod template항목의 내용으로 pod를 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;795&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bJxfb5/btrvZIV3tWT/izzvhqyeK6P9oM9FbjsC2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bJxfb5/btrvZIV3tWT/izzvhqyeK6P9oM9FbjsC2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bJxfb5/btrvZIV3tWT/izzvhqyeK6P9oM9FbjsC2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbJxfb5%2FbtrvZIV3tWT%2FizzvhqyeK6P9oM9FbjsC2K%2Fimg.png&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; alt=&quot;principle of replicaset&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;359&quot; data-filename=&quot;17.png&quot; data-origin-width=&quot;1225&quot; data-origin-height=&quot;795&quot;/&gt;&lt;/span&gt;&lt;/figure&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;그래서 app:my-nginx-pods-label이라는 label을 가지는 pod를 미리 생성해두고 replicaset을 생성하면 어떻게 될까요? 먼저 해당 label을 가지는 pod을 수동으로 생성해보죠.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;#nginx-label-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: my-nginx-pod
  labels:
    app: my-nginx-pods-label
spec:
  containers:
  - name: my-nginx-container
    image: nginx:latest
    ports:
    - containerPort: 80&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZN3g0/btrv2EGn0Yu/q2Kd0Vg1Br48NavwtnLD2k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZN3g0/btrv2EGn0Yu/q2Kd0Vg1Br48NavwtnLD2k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZN3g0/btrv2EGn0Yu/q2Kd0Vg1Br48NavwtnLD2k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZN3g0%2Fbtrv2EGn0Yu%2Fq2Kd0Vg1Br48NavwtnLD2k%2Fimg.png&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; alt=&quot;replicaset label&quot; loading=&quot;lazy&quot; width=&quot;688&quot; height=&quot;96&quot; data-filename=&quot;18.png&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&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;이 상태에서 replicaset을 생성해보죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;288&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/7ioGa/btrv4mZyaGa/gI5mRLH9EaZwFM5Kr7gYY1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/7ioGa/btrv4mZyaGa/gI5mRLH9EaZwFM5Kr7gYY1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/7ioGa/btrv4mZyaGa/gI5mRLH9EaZwFM5Kr7gYY1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F7ioGa%2Fbtrv4mZyaGa%2FgI5mRLH9EaZwFM5Kr7gYY1%2Fimg.png&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; alt=&quot;replicaset label2&quot; loading=&quot;lazy&quot; width=&quot;596&quot; height=&quot;158&quot; data-filename=&quot;19.png&quot; data-origin-width=&quot;1088&quot; data-origin-height=&quot;288&quot;/&gt;&lt;/span&gt;&lt;/figure&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;replicaset의 selector.matchLabel에 정의된 app:my-nginx-pods-label을 가지는 label을 이미 1개(my-nginx-pod) 존재하기 때문에 template에 정의된 pod설정을 통해 3개의 pod만 생성된다. 그리고 수동으로 생성된 pod를 삭제하면 다음과 같이 replicaset이 알아서 새로운 pod를 생성해줍니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20.png&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;290&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/OIgvQ/btrv6dnf3LQ/QaKqtl5xch1C3kRiJKG230/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/OIgvQ/btrv6dnf3LQ/QaKqtl5xch1C3kRiJKG230/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/OIgvQ/btrv6dnf3LQ/QaKqtl5xch1C3kRiJKG230/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FOIgvQ%2Fbtrv6dnf3LQ%2FQaKqtl5xch1C3kRiJKG230%2Fimg.png&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; alt=&quot;replicaset delete&quot; loading=&quot;lazy&quot; width=&quot;622&quot; height=&quot;167&quot; data-filename=&quot;20.png&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;290&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 replicase이 생성해 놓은 pod의 label을 삭제하면 예상하셨듯이 label을 통해 replicaset 숫자를 결정하므로 app:my-nginx-pods-label이름의 label을 가지는 새로운 pod가 생성됩니다. 예시를 위해 &lt;code&gt;kubectl edit&lt;/code&gt;명령어을 사용하여 pod 중 하나의 label을 삭제해봅니다. label삭제는 아래 그림과 같이 label에 대한 정보를 담는 내용을 삭제하면 됩니다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# replicaset-nginx-vmnrz는 pod의 이름 중 하나임
kubectl edit pods replicaset-nginx-vmnrz&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;1929&quot; data-origin-height=&quot;491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sb8qe/btrvZ4MwolG/oZKd4aNMPD3NlHgB0PZGr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sb8qe/btrvZ4MwolG/oZKd4aNMPD3NlHgB0PZGr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sb8qe/btrvZ4MwolG/oZKd4aNMPD3NlHgB0PZGr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fsb8qe%2FbtrvZ4MwolG%2FoZKd4aNMPD3NlHgB0PZGr1%2Fimg.png&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; alt=&quot;kubectl edit&quot; loading=&quot;lazy&quot; width=&quot;762&quot; height=&quot;194&quot; data-filename=&quot;21.png&quot; data-origin-width=&quot;1929&quot; data-origin-height=&quot;491&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;1558&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/P40CY/btrv6d1QQdO/5LHkrLpHraae58nijhxPc0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/P40CY/btrv6d1QQdO/5LHkrLpHraae58nijhxPc0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/P40CY/btrv6d1QQdO/5LHkrLpHraae58nijhxPc0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FP40CY%2Fbtrv6d1QQdO%2F5LHkrLpHraae58nijhxPc0%2Fimg.png&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; alt=&quot;rename nodes&quot; loading=&quot;lazy&quot; width=&quot;722&quot; height=&quot;120&quot; data-filename=&quot;22.png&quot; data-origin-width=&quot;1558&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;/figure&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;edit한 부분을 저장하면 다시 pod의 목록을 보면 새로운 하나의 pod가 생성되었고 label의 정보를 삭제한 pod는 label 정보가 사라졌음을 알 수 있습니다. 그리고 label이 없는 pod는 &lt;code&gt;kubectl delete rs&lt;/code&gt;명령어로부터 삭제되지 않으므로 직접 삭제해주어야 합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;23.png&quot; data-origin-width=&quot;1089&quot; data-origin-height=&quot;375&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/za6f1/btrv6c9JnOX/fAgD1J2DEKwkGxkK4ibd8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/za6f1/btrv6c9JnOX/fAgD1J2DEKwkGxkK4ibd8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/za6f1/btrv6c9JnOX/fAgD1J2DEKwkGxkK4ibd8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fza6f1%2Fbtrv6c9JnOX%2FfAgD1J2DEKwkGxkK4ibd8k%2Fimg.png&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; alt=&quot;delete node when labeling&quot; loading=&quot;lazy&quot; width=&quot;549&quot; height=&quot;189&quot; data-filename=&quot;23.png&quot; data-origin-width=&quot;1089&quot; data-origin-height=&quot;375&quot;/&gt;&lt;/span&gt;&lt;/figure&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;그리고 중요한 특징 중 하나로 replicaset은 다음과 같은 YAML파일에서 표현식(matchExpressions)으로 정의가능합니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# nginx-expression-pod.yaml
...
spec:
  replicas: 3
  selector:
    matchExpressions:
      - key: app
        values:
          - my-nginx-pods-label
          - your-nginx-pods-label
        operator: In&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시는 key가 appd인 label을 가지고 있는 pod들 중에서 values항목에 정의된 값들이 존재(In)하는 pod들 대상으로하겠다는 말으로 &lt;b&gt;my-nginx-pods-label&lt;/b&gt;뿐만 아니라 &lt;b&gt;your-nginx-pods-label&lt;/b&gt;이라는 label을 가진 pod또한 replicaset 관리하에 놓이게 됩니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;4. Deployment: replicaset, pod의 배포를 관리&lt;/b&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.1 Deployment 사용&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 k8s 운영환경에서 replicaset을 YAML파일에서 사용하는 경우는 없습니다. 대부분은 replicaset과 pod의 정보를 정의하는 &lt;b&gt;Deployment&lt;/b&gt;라는 object를 YAML파일에 정의해 사용합니다.&lt;br /&gt;Deployment는 replicaset의 상위 object이기 때문에 deployment생성시 자동으로 대응되는 replicaset도 생성됩니다. 다음과 같은 &lt;b&gt;deployment-nginx.yaml&lt;/b&gt;로 deployment를 생성해봅니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;24.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;772&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dzvSoI/btrv4KeMkBW/lXs9uj0AiEKo456dZv2FT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dzvSoI/btrv4KeMkBW/lXs9uj0AiEKo456dZv2FT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dzvSoI/btrv4KeMkBW/lXs9uj0AiEKo456dZv2FT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdzvSoI%2Fbtrv4KeMkBW%2FlXs9uj0AiEKo456dZv2FT0%2Fimg.png&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; alt=&quot;deployment&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;404&quot; data-filename=&quot;24.png&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;772&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      name: my-nginx-pod
      labels:
        app: my-nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.10
        ports:
        - containerPort: 80&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보시면 아시겠지만 replicaset과 비교했을 때 kind부분만 &lt;b&gt;&lt;i&gt;Deployement&lt;/i&gt;&lt;/b&gt;로 바뀌었지 다른변화는 거의 없습니다. 일단 deployment을 생성해보죠.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;25.png&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;76&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cyEMUT/btrv3gkZ0td/PEvM3NGt8JY9zhmNGsaVI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cyEMUT/btrv3gkZ0td/PEvM3NGt8JY9zhmNGsaVI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cyEMUT/btrv3gkZ0td/PEvM3NGt8JY9zhmNGsaVI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcyEMUT%2Fbtrv3gkZ0td%2FPEvM3NGt8JY9zhmNGsaVI1%2Fimg.png&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; alt=&quot;kubectl apply deployment&quot; loading=&quot;lazy&quot; width=&quot;564&quot; height=&quot;44&quot; data-filename=&quot;25.png&quot; data-origin-width=&quot;984&quot; data-origin-height=&quot;76&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;kubectl get deploy&lt;/code&gt;로 deployment의 실행을 확인하고 replicaset셋 또한 생성됨을 확인하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;26.png&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BmzQZ/btrv5obwkBX/VIfBBDJUmvZnPbeDw1yua0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BmzQZ/btrv5obwkBX/VIfBBDJUmvZnPbeDw1yua0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BmzQZ/btrv5obwkBX/VIfBBDJUmvZnPbeDw1yua0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBmzQZ%2Fbtrv5obwkBX%2FVIfBBDJUmvZnPbeDw1yua0%2Fimg.png&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; alt=&quot;kubectl get deploy&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;195&quot; data-filename=&quot;26.png&quot; data-origin-width=&quot;1314&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&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;deployement로 생성하였지만 replicaset과 크게 다르지 않는 데 차이점이라고는 NAME항목에서 중간에 해시값(&lt;b&gt;6b4b7f7cdc&lt;/b&gt;)이 포함되어있는데 이는 pod를 정의하는 template으로부터 생성된 것인데 자세히는 뒤에서 설명할것이므로 기억해두자. deployment삭제는 다음 명령어로 진행한다.&lt;/p&gt;
&lt;pre class=&quot;perl&quot;&gt;&lt;code&gt;kubectl delete deploy my-nginx-deployment&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;4.2 Deployment 사용 이유&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deployment를 사용하는 큰 이유 중 하나는 application의 업데이트와 배포 및 관리를 해준다. 예로 application을 업데이트할 때 replicaset의 변경사항을 저장하는 revision을 남겨 rollback를 가능하게 해주고 무중단 서비스를 위해 Pod의 롤링 업데이트 전략을 지정가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Deployment을 이용해 application의 버전을 업데이트해 배포하는 예시를 알아보자. 위의 deployment-nginx.yaml을 이용해 다시 deployment을 실행하자. &lt;b&gt;--record&lt;/b&gt;옵션을 통해 deployment의 변경사항을 저장하도록 한다.&lt;/p&gt;
&lt;pre class=&quot;coq&quot;&gt;&lt;code&gt;kubectl apply -f deployment-nginx.yaml --record&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 만약 당신이 nginx:1.10 을 nginx:1.11로 업데이트하고 싶다고 할때 deployment에서 생성된 pod의 image을 &lt;code&gt;kubectl set image&lt;/code&gt;명령어로 업데이트 가능하다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;kubectl set image deployment my-nginx-deployment nginx=nginx:1.11 --record&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&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-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;27.png&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bEbadE/btrv5obwkZb/Q4LDTtYP1EB6c9Q67cDkBk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bEbadE/btrv5obwkZb/Q4LDTtYP1EB6c9Q67cDkBk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bEbadE/btrv5obwkZb/Q4LDTtYP1EB6c9Q67cDkBk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbEbadE%2Fbtrv5obwkZb%2FQ4LDTtYP1EB6c9Q67cDkBk%2Fimg.png&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; alt=&quot;kubectl set image&quot; loading=&quot;lazy&quot; width=&quot;810&quot; height=&quot;113&quot; data-filename=&quot;27.png&quot; data-origin-width=&quot;1566&quot; data-origin-height=&quot;218&quot;/&gt;&lt;/span&gt;&lt;/figure&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;위에서 알 수 있듯이 기존의 사용되었던 nginx:1.10이미지를 가지는 replicaset의 해시값은 (6b4b7f7cdc)이며 기존의 replicaset의 값이 0으로 설정된 것을 보아 정지된 것을 알 수 있고 새로 nginx:1.11로 실행되는 replicaset과 그에 대한 해시값(55bbf495bd)을 확인가능하다. 그리고 이는 이전 버전의 replicaset을 삭제하지 않고 남겨두고 있는것을 말하고 이전의 정보를 리비전으로서 보존하는 것입니다. &lt;code&gt;kubectl rollout history deploy&lt;/code&gt;명령어로 리비전 정보를 확인하자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;28.png&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;186&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zKGku/btrv4JAesfc/IjKPusc8FEtuzLNFdbzvL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zKGku/btrv4JAesfc/IjKPusc8FEtuzLNFdbzvL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zKGku/btrv4JAesfc/IjKPusc8FEtuzLNFdbzvL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzKGku%2Fbtrv4JAesfc%2FIjKPusc8FEtuzLNFdbzvL0%2Fimg.png&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; alt=&quot;kubectl rollout&quot; loading=&quot;lazy&quot; width=&quot;802&quot; height=&quot;96&quot; data-filename=&quot;28.png&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;186&quot;/&gt;&lt;/span&gt;&lt;/figure&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;CHANCE-CAUSE에 나오는 명령어들은 &lt;b&gt;--record&lt;/b&gt;에 의해 저장된 것이며 이제 nginx:1.10으로 다시 롤백을 해보자. &lt;b&gt;--to-revision&lt;/b&gt;옵션의 값으로 되돌리고자하는 revision번호의 값을 설정하면 된다.&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;kubectl rollout undo deploy my-nginx-deployment --to-revision 1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;29.png&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QaIqZ/btrv6eGtAmE/PWrGnEzsH6G9HgXLN4egpk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QaIqZ/btrv6eGtAmE/PWrGnEzsH6G9HgXLN4egpk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QaIqZ/btrv6eGtAmE/PWrGnEzsH6G9HgXLN4egpk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQaIqZ%2Fbtrv6eGtAmE%2FPWrGnEzsH6G9HgXLN4egpk%2Fimg.png&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; alt=&quot;kubectl rollout undo&quot; loading=&quot;lazy&quot; width=&quot;751&quot; height=&quot;117&quot; data-filename=&quot;29.png&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;224&quot;/&gt;&lt;/span&gt;&lt;/figure&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;롤백이 잘된것을 확인가능하다. 생성된 모든 리소스를 삭제하려면 &lt;code&gt;kubectl delete deploy,po,rs --all&lt;/code&gt;를 사용한다.. 정리하자면 deployment를 통해 replicaset의 리비전 관리뿐만 아니라 다양한 pod의 롤링 업데이트 정책을 사용할수 있으므로 deployment를 통해 application을 서비스하자.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;b&gt;5. Service: pod를 연결하고 외부에 노출&lt;/b&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;deployment을 실행시키기 위해 사용한 YAML에서 pod를 외부로 노출하지 않았으므로 외부에서 접근이 불가하다. Ngnix의 containerPort항목은 80번 port로 웹서버로 제공하기 때문에 설정한 값일뿐이기 때문에 해당 Nginx pod가 외부로 노출되는 것은 아닙니다. 그래서 외부에서 pod에 접근하기 위해 &lt;b&gt;&lt;span style=&quot;color: crimson;&quot;&gt;서비스(service)&lt;/span&gt;&lt;/b&gt; object를 생성해야 합니다. service의 핵심 역할은 다음과 같습니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;여러 개의 pod에 쉽게 접근할수 있도록 고유한 domain 이름을 부여&lt;/li&gt;
&lt;li&gt;여러 개의 pod에 접근할 때 요청을 분산하는 로드 밸런서 기능을 수행&lt;/li&gt;
&lt;li&gt;클라우드 플랫폼의 로드 밸런서, 클러스터 노드의 Port등을 통해 포드를 외부로 노출&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;5.1 service 종류&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k8s 서비스는 pod에 어떻게 접근할 것이냐에 따라 종류가 여러개로 세분화 되어있는데 다음과 같은 특징을 고려하여 서비스 종류를 고르셔야합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Clutser IP&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;k8s 내부에서만 pod들에 접근할 때 사용함, 외부로 pod가 노출되지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;NodePoint&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pod에 접근할 수 있는 port를 클러스터의 모든 노드에 동일하게 개방함 외부에서 접근 가능&lt;/li&gt;
&lt;li&gt;접근 가능한 port는 랜덤으로 정해지지만 특정 Port로 접근하도록 정할 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;LoadBalancer&lt;/b&gt;&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클라우드 플랫폼(AWS, GCP)에서 제공하는 로드 밸런서를 동적으로 프로비저닝하여 pod에 연결, 외부에서 접근 가능&lt;/li&gt;
&lt;li&gt;실제 운영 환경에 많이 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Clutser IP&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 지금부터 deployment를 설명할때 사용했던 deployment-nginx.yaml과 함께 예제를 진행할 것입니다. 다음과 같이 svc-clusterip.yaml을 생성해봅시다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# svc-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: my-nginx
  type: ClusterIP&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;spec.selector:&lt;/b&gt; 해당 label을 가지는 pod에 접근하게 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;my-nginx는 deployment설명 때 사용한 deployment-nginx.yaml의 nginx container의 label이름&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spec.ports.port:&lt;/b&gt; k8s 내부에서만 사용할 수 있는 고유한 IP(ClusterIP)를 할당 받음
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스의 IP에 접근할 때 사용하는 port설정 값&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spec.ports.targetPort:&lt;/b&gt; selector항모게서 정의된 label에 의해 접근 대상이 된 pod들이 사용하는 내부 port번호
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;deployment-nginx.yaml의 containerPort항목의 값을 입력해야 함.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;spec.type:&lt;/b&gt; 서비스의 타입을 입력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;deployment-nginx.yaml을 통해 deployment를 실행시키고 svc-clusterip.yaml로 service를 생성해보자. 그리고 &lt;code&gt;kubectl get svc&lt;/code&gt;명령어로 생성된 service의 목록을 확인한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;30.png&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;360&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Pa728/btrv0PuVAJ4/k0h7xeTzLuuV0KVoRoecOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Pa728/btrv0PuVAJ4/k0h7xeTzLuuV0KVoRoecOk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Pa728/btrv0PuVAJ4/k0h7xeTzLuuV0KVoRoecOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPa728%2Fbtrv0PuVAJ4%2Fk0h7xeTzLuuV0KVoRoecOk%2Fimg.png&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; alt=&quot;kubectl apply svc&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;181&quot; data-filename=&quot;30.png&quot; data-origin-width=&quot;1316&quot; data-origin-height=&quot;360&quot;/&gt;&lt;/span&gt;&lt;/figure&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;svc-clutserip라는 이름으로 service를 생성하였습니다. &lt;code&gt;kubectl run&lt;/code&gt;명령어를 통해 임시 ubuntu pod를 만들고 출력된 CLUSTER-IP와 PORT(S)로 curl를 통한 http 요청을 보내면 응답받을 수 있습니다. 또한 service이름 자체로도 접근가능한데 이는 k8s가 application이 service나 pod를 쉽게 찾을 수 있도록 내부 DNS를 구동하고 있고 pod들은 자동으로 이 DNS을 사용된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# run ubuntu pod and connect to it
kubectl run -i --tty --rm debug --image=ubuntu:16.04 --restart=Never -- bash

#install curl
sudo apt-get update
sudo apt-get install curl

# http request using cluster ip + port
curl 10.99.228.48:8080
# http request using inner DNS
curl svc-clusterip:8080&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;31.png&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;408&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bphNya/btrv4lM5p9Q/qE6UnpPyWeCGUYTINtP7h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bphNya/btrv4lM5p9Q/qE6UnpPyWeCGUYTINtP7h0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bphNya/btrv4lM5p9Q/qE6UnpPyWeCGUYTINtP7h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbphNya%2Fbtrv4lM5p9Q%2FqE6UnpPyWeCGUYTINtP7h0%2Fimg.png&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; alt=&quot;curl svc-clusterip&quot; loading=&quot;lazy&quot; width=&quot;394&quot; height=&quot;219&quot; data-filename=&quot;31.png&quot; data-origin-width=&quot;735&quot; data-origin-height=&quot;408&quot;/&gt;&lt;/span&gt;&lt;/figure&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;서비스를 삭제하기 위한 명령어는 다음과 같습니다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;kubectl delete svc svc-clusterip&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;NodePort&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 svc-nodeport.yaml을 작성(ClusterIP와 비교했을 때 type만 다릅니다.)하고 apply시켜 service를 생성해봅니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-clusterip
spec:
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: my-nginx
  type: NodePort&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;32.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfqKSe/btrvZ4MwHJQ/YykSd8qG8de432RXfYSWG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfqKSe/btrvZ4MwHJQ/YykSd8qG8de432RXfYSWG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfqKSe/btrvZ4MwHJQ/YykSd8qG8de432RXfYSWG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbfqKSe%2FbtrvZ4MwHJQ%2FYykSd8qG8de432RXfYSWG0%2Fimg.png&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; alt=&quot;kubectl apply nodeport&quot; loading=&quot;lazy&quot; width=&quot;733&quot; height=&quot;110&quot; data-filename=&quot;32.png&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;214&quot;/&gt;&lt;/span&gt;&lt;/figure&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;Cluster IP와 다르게 PORT(S)항목에서 &lt;b&gt;32453&lt;/b&gt;라는 숫자가 생겼고 이는 모든 노드에서 동일하게 접근 가능한 port를 의미합니다. (해당 Port는 랜덤으로 정해집니다.) 즉, 클러스터의 모든 노드에 내부 IP또는 외부 IP를 통해 32453 port로 접근하면 동일한 service에 연결가능합니다. 또한 가상머신을 아닌 제 맥북에서도 응답을 받을 수 있습니다. 당연히 nodeport를 삭제하면 연결은 끊기고 response를 받을 수 없을 것입니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;33.png&quot; data-origin-width=&quot;1385&quot; data-origin-height=&quot;864&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bSb0v5/btrv6dOk3cr/gGW9TsSxg9ukhuk7LJPk41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bSb0v5/btrv6dOk3cr/gGW9TsSxg9ukhuk7LJPk41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bSb0v5/btrv6dOk3cr/gGW9TsSxg9ukhuk7LJPk41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbSb0v5%2Fbtrv6dOk3cr%2FgGW9TsSxg9ukhuk7LJPk41%2Fimg.png&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; alt=&quot;nodeport check&quot; loading=&quot;lazy&quot; width=&quot;645&quot; height=&quot;403&quot; data-filename=&quot;33.png&quot; data-origin-width=&quot;1385&quot; data-origin-height=&quot;864&quot;/&gt;&lt;/span&gt;&lt;/figure&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;추가로 NodePort는 ClusterIP를 가지고 있음을 알 수 있는데 이는 NodePort는 ClusterIP의 기능을 포함하고 있기 때문이다. 다음은 NodePort의 정리 그림이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;34.png&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;641&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c5VpW0/btrv4mSNKLZ/SsnhoeKinXniNYbCTukiak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c5VpW0/btrv4mSNKLZ/SsnhoeKinXniNYbCTukiak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c5VpW0/btrv4mSNKLZ/SsnhoeKinXniNYbCTukiak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc5VpW0%2Fbtrv4mSNKLZ%2FSsnhoeKinXniNYbCTukiak%2Fimg.png&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; alt=&quot;nodeport explanations&quot; loading=&quot;lazy&quot; width=&quot;689&quot; height=&quot;342&quot; data-filename=&quot;34.png&quot; data-origin-width=&quot;1291&quot; data-origin-height=&quot;641&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p 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&gt;외부에서 pod에 접근하기 위해 각 노드에 개방된 port로 요청을 전송함. 위 그림에서 32453 port로 들어온 요청은 service와 연결된 pod 중 하나로 라우팅됩니다.&lt;/li&gt;
&lt;li&gt;클러스터 내부에는 ClusterIP의 service와 동일하게 접근할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 운영에서는 NodePort service 그 자체를 통해 service를 외부로 제공하기 보다는 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;인그레스(ingress)&lt;/b&gt;&lt;/span&gt;라고 부르느 object에서 간접적으로 사용을 많이 합니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;LoadBalancer&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 type의 service는 로드밸러서를 동적으로 생성하는 기능을 제공하는 환경(AWS, GCP)에서만 사용가능하다. 지금 제가 하는 진행하는 가상 환경이나 on-premise에서는 사용이 힘들 수 있습니다. 개념만 알아두자면 nodeport와 유사하지만 External IP가 클라우드 플랫폼에 맞춰 설정된다는 점이 다릅니다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;externalTrafficPolicy: 트래픽 분배를 결정하는 service 속성&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LoadBalanacer service를 사용하면 외부로부터 들어온 요청은 각 노드 중 하나로 보내지며 그 노드에서 다시 pod 중 하나로 전달됩니다. NodePort 타입을 사용했을 때도 각 노드로 들어오는 요청은 다시 pod 중 하나로 전달됩니다. 그렇지만 이러한 요청 전달 원리는 경우에 따라 효율적이지 않은데 해당 예시를 위해 다음과 같은 상황을 가정합니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;35.png&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;495&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bBRUqw/btrv12HtuVg/JCFHf5CVvpCiNpu1HeVgwK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bBRUqw/btrv12HtuVg/JCFHf5CVvpCiNpu1HeVgwK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bBRUqw/btrv12HtuVg/JCFHf5CVvpCiNpu1HeVgwK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbBRUqw%2Fbtrv12HtuVg%2FJCFHf5CVvpCiNpu1HeVgwK%2Fimg.png&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; alt=&quot;externalTrafficPolicy&quot; loading=&quot;lazy&quot; width=&quot;561&quot; height=&quot;308&quot; data-filename=&quot;35.png&quot; data-origin-width=&quot;902&quot; data-origin-height=&quot;495&quot;/&gt;&lt;/span&gt;&lt;/figure&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;모든 노드에서 31000번 port가 개방되어 pod에 접근할수 있으며, 워커 노드 A, B에 pod가 각각 생성되어 있다고 가정해봅시다. 이때 워커 노드 A로 들어오는 요청은 (1) A에 위치한 a pod 또는 (2) B에 위치한 b pod중 하나로 전달됩니다. 이 때 A 노드로 들어오는 요청이 굳이 a pod로 전달되지 않고 b pod로 전달된다면 네트워크 hob이 한단 계 더 발생합니다. 그리고 노드 간의 redirect가 발생하게 되어 트래픽이 출발지 주소가 바뀌는 SNAT현상이 발생하게 되고 이로 인해 client IP주소 또한 보존되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 요청 전달 메커니즘은 service 속성 중 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;exeternalTrafficPolicy&lt;/b&gt;&lt;/span&gt; 항목에 정의되어있습니다. &lt;code&gt;kubectl get -o yaml&lt;/code&gt; 명령어로 service의 모든 속성을 출력해 보면 externalTrafficPolicy가 &lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;Cluster&lt;/b&gt;&lt;/span&gt;로 설정되어있는 것을 알 수 있습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;36.png&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZkmB3/btrv11u3gPX/OErNiLNI3hlk2CnR7DQTRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZkmB3/btrv11u3gPX/OErNiLNI3hlk2CnR7DQTRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZkmB3/btrv11u3gPX/OErNiLNI3hlk2CnR7DQTRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZkmB3%2Fbtrv11u3gPX%2FOErNiLNI3hlk2CnR7DQTRK%2Fimg.png&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; alt=&quot;kubctl get loadbalancer&quot; loading=&quot;lazy&quot; width=&quot;496&quot; height=&quot;358&quot; data-filename=&quot;36.png&quot; data-origin-width=&quot;989&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&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;Cluster값은 default설정값으로 클러스터의 모든 노드에 랜덤한 port를 개방하는 기존 방식입니다. 다음 service YAML 파일처럼 externalTrafficPolicy를 &lt;b&gt;Local&lt;/b&gt;로 설정하면 pod가 생성한 노드에서만 pod로 접근할 수 있게하며 이는 추가적인 네트워크 hob이 발생하지 않으며 전달되는 요청의 client IP또한 보존됩니다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# svc-local-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-local-nodeport
spec:
  externalTrafficPilicy: Local
  ports:
    - name: web-port
      port: 8080
      targetPort: 80
  selector:
    app: my-nginx
  type: NodePort&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 yaml로 service을 실행시키면 다음과 같은 차이점을 보이게 됩니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;37.png&quot; data-origin-width=&quot;1724&quot; data-origin-height=&quot;622&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dAbqMW/btrv6WFADB2/W6JWk2rQCFHlbOUn7dMMb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dAbqMW/btrv6WFADB2/W6JWk2rQCFHlbOUn7dMMb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dAbqMW/btrv6WFADB2/W6JWk2rQCFHlbOUn7dMMb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdAbqMW%2Fbtrv6WFADB2%2FW6JWk2rQCFHlbOUn7dMMb0%2Fimg.png&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; alt=&quot;externalTrafficPolicy explanations&quot; loading=&quot;lazy&quot; width=&quot;799&quot; height=&quot;289&quot; data-filename=&quot;37.png&quot; data-origin-width=&quot;1724&quot; data-origin-height=&quot;622&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;ExternalName: 요청을 외부로 redirect하는 service&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;k8s를 외부 시스템과 연동해야할 때 사용하는 타입의 service입니다. External타입을 사용해 service를 생성하면 service가 외부 도메인을 가리키도록 설정가능하다. 예를 들어 아래의 설정대로 한다면 k8s 내부의 pod들이 externalname-svc라는 이름으로 요청을 보낼 경우, k8s의 DNS는 my.database.com으로 접근할 수 있도록 CNAME 레코드를 반환합니다. 즉, externalname-svc로 요청을 보내면 my.database.com에 접근하게 되는 것이다. 해당 service는 k8s와 별개로 레거시 시스템에 연동하는 경우에 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;CNAME은 Canonical Name의 약자로 도메인 주소를 또 다른 도메인 주소로 매핑 시키는 형태의 DNS 레코드 타입&lt;/blockquote&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;# svc-external.yaml
apiVersion: v1
kind: Service
metadata:
  name: svc-externalname
spec:
  type: ExternalName
  externalName: my.database.com&lt;/code&gt;&lt;/pre&gt;</description>
      <category>AI Engineering/MLOps</category>
      <category>kubernetes</category>
      <author>Sin-Han Kang</author>
      <guid isPermaLink="true">https://da2so.tistory.com/36</guid>
      <comments>https://da2so.tistory.com/36#entry36comment</comments>
      <pubDate>Tue, 15 Mar 2022 17:30:27 +0900</pubDate>
    </item>
  </channel>
</rss>