<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Doby's Lab</title>
    <link>https://draw-code-boy.tistory.com/</link>
    <description>내가 할 수 있는 일에 집중할 뿐</description>
    <language>ko</language>
    <pubDate>Tue, 16 Jun 2026 04:04:29 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>도비(Doby)</managingEditor>
    <image>
      <title>Doby's Lab</title>
      <url>https://tistory1.daumcdn.net/tistory/4848899/attach/4f1c5f31d1b943efbb5c44cfe4b09d1d</url>
      <link>https://draw-code-boy.tistory.com</link>
    </image>
    <item>
      <title>&amp;lt;물고기는 존재하지 않는다&amp;gt;, 신념이 가져온 선과 악</title>
      <link>https://draw-code-boy.tistory.com/622</link>
      <description>&lt;p data-ke-size=&quot;size16&quot; style=&quot;text-align: left;&quot;&gt;책을 읽기도 시작하기 한참 전에 사둔 책을 꺼냈다. 난 가끔 아무 이유 없이 서점에 가서 책을 사고는 한다. 읽어야겠다는 생각보다는 ‘언젠가 읽겠지’의 기저가 더 강하다. 이번 책도 이런 식으로 사두었다가 방 한 켠에 조용히 꽂혀있었다. 책 읽기를 마음 먹은 뒤로 새로운 책을 사기보다는 사두었던 책을 읽어보고자 했다. 이번 책은 룰루 밀러의 &amp;lt;물고기는 존재하지 않는다&amp;gt;이다. 처음 보면 제목이 무슨 의미인지조차 감이 오지 않는데, 다 읽고 나서 다시 이 제목을 다시 보면 이런 생각이 든다.&lt;br&gt;&lt;br&gt;&lt;i&gt;‘이 세상에 의미가 있는 게 있을까, 사실이 존재할까’&lt;/i&gt;&lt;br&gt;&lt;br&gt;사실 이것은 책을 보고나서 든 생각일 뿐, 더 크게 느낀 것은 따로 있다. 소설은 한 사람의 시선으로부터 시작한다. 많은 방황을 겪고 있는 사람이 이를 벗어나기 위해 고군분투 하는데, 그 과정에서 ‘데이비드 스타 조던'이라는 과학자를 알게 되고 이 과학자의 태도에서 본인이 찾고자 하는 답은 여기에 있다며 ‘데이비드 스타 조던'이라는 자의 일생을 톺아보는 논픽션 이야기이다.&lt;br&gt;&lt;br&gt;초반부를 읽으면 나 또한 이 소설의 주인공처럼 데이비드 스타 조던을 존경하게 된다. 좋은 말들이 있어서 따로 필사를 해둘 정도로 그의 태도에서는 배울만한 것들이 많았다. 그의 직업은 생물학을 연구하는 학자라고 볼 수 있는데, 그 중에서도 주요하게 다루는 것은 새로운 어류를 찾고 이들에게 이름을 붙이는 일이다. 그가 겪은 일화 중에는 오래 수집한 물고기들을 보관하고 있는 곳에서 어느 날 자연재해에 의해 물고기들을 담은 병들이 깨지거나 형체를 알아볼 수도 없을 정도의 찢어진 살갗들로 좌절을 겪을 만한 상황에서도 그는 담을 수 있는 물고기들을 하나씩 잡아 다시 이름표를 바늘로 꿰메었다는 이야기이다. 그가 쌓아온 적어도 몇 년, 몇 십 년의 아카이브가 무너지는 상황에서 어찌 그리 다시 주어담을 수 있었는가는 나로 하여금 소설에 몰입하도록 했다. 나도 주인공처럼 이 사람이 그럴 수 있는 이유가 궁금하기 시작했기 때문이다. 소설 중에는 이런 말이 있다.&lt;br&gt;&lt;br&gt;&lt;i&gt;“망해버린 사명을 계속 밀고 나아가는 일을 정당화하는 그 정확한 문장을 찾아내는 것이 내게는 절박했다. (p. 120)”&lt;/i&gt;&lt;br&gt;&lt;br&gt;이처럼 소설의 주인공이 나 대신 ‘데이비드 스타 조던’의 역사를 파헤치면서 사명에 대한 원동력 비스무리한 것들을 찾아준다. 그런 식으로 이어서 ‘데이비드 스타 조던'의 일화들을 전개하는 내용으로 이 책을 구성하고 있다. 조금 더 읽어서야 이게 답인가 싶은 내용도 있었다. &lt;br&gt;&lt;br&gt;&lt;i&gt;“데이비드 스타 조던은 지속적으로 오만을 복용하는 것이야말로 실패할 운명을 극복하는 최선의 방법임을 보여주는 증거인지도 모른다. (p.146)”&lt;/i&gt;&lt;br&gt;&lt;br&gt;긍정적 착각이라는 표현도 쓰는데, 이런 착각이 좋다라고 밝혀진 연구들도 있고 조금 더 시간이 지나고서는 그닥 좋은 것만 가지고 있는 건 아니라는 것을 밝혀낸 연구들도 있었다. 즉, 이것도 명쾌한 정답은 아니었다. 아마 주인공도 그리 느꼈던 거 같다. ‘그래서, 정답이 무엇이냐?’라고 내가 의문을 품기 시작할 때는 ‘데이비드 스타 조던'의 추악한 이야기들이 전개되기 시작했다. 제인의 살인은 물론이고, 가장 충격적인 것은 ‘우생학’ 때문이다. 그의 믿음 때문에 피해를 받아 지금까지 살아있는 사람들의 이야기들이 후반부를 이루는데, 정말 충격적이다.&lt;br&gt;&lt;br&gt;한 사람의 믿음, 신념이 올곧아도 너무 올곧아있기에 벌어진 일들이었다. 초반부에는 그의 이런 마음을 존경하였지만 말이다. 그래서, 굉장히 역했다. 한 편으로는 신기했다. 내가 한 인물을 생각하기를 긍정에서 부정으로 바뀌어가는 과정을 책으로 몸소 느낀다는 것이 흥미로웠다. 더 나아가 후반부에서는 어류라는 것이 존재하지 않음을 밝혀낸 연구들을 이야기 하는데, ‘데이비드 스타 조던'의 일생과 신념을 부정하기라도 하는 듯한 통쾌한 연구가 있었음에 속이 시원했다. &lt;br&gt;&lt;br&gt;이 책을 읽은지는 사실 오래 되었다. 거의 2주 전에 다 읽고 이제서야 독후감을 쓰고 있는데, 그러다보니 읽었던 당시의 어떤 감정들이 조금은 휘발되어 있을 수도 있다. 다만, 이 소설을 읽고나서 가장 굵직하게 느낀 것들은 남아있는 듯하다. 첫 번째로는 한 사람에 대한 존경심이 역겨움으로 넘어가는 과정을 겪을 때의 신비로움, 두 번째는 신념에 대한 것이다. 사실 나는 신념이라는 것이 최근 들어 중요하다고 생각하는 입장이다. 많은 것들이 불확실한 이 세상에서 ‘이러면 될 거야, 이러는 것이 정답이야'라고 굳게 믿을 수 있는 마음은 소중하다고 본다. 다만, 그것이 너무 과한 나머지 주변 사람들에게 피해를 주게 된다면 정말 최악이라는 것을 느끼게 되었다. ‘내가 이랬으니까, 너도 그래야 해'라는 태도 말이다. 이 태도는 정말 중요하다. 연구를 하는 입장으로서 앞으로 많은 후배를 만나게 될 때도 말이다. 하지만, ‘데이비드 스타 조던'에 동조하는 것은 절대 절대 아니다만, 신념은 중요하다고 말하고 싶은 것이다. &lt;br&gt;&lt;br&gt;이 소설의 독후감은 여기서 마친다. 빨리 다음 책을 사서 읽고 싶은데, 양심에 찔린 나머지 이 글을 쓰지 않는다면 책을 못 살 거 같아 빨리 쓴다. 다음 책은 정말 궁금한 &amp;lt;스토너&amp;gt;다.&lt;br&gt;&lt;br&gt;그럼에도 불구하고 좋은 말 하나를 발췌한다면,&lt;br&gt;&lt;br&gt;&lt;i&gt;“운명의 형태를 만드는 것은 사람의 의지다. (p.137)”&lt;/i&gt;&lt;/p&gt;</description>
      <category>Daily/Book Reports</category>
      <category>독후감</category>
      <category>물고기는 존재하지 않는다</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/622</guid>
      <comments>https://draw-code-boy.tistory.com/622#entry622comment</comments>
      <pubDate>Sun, 15 Feb 2026 23:43:59 +0900</pubDate>
    </item>
    <item>
      <title>&amp;lt;자몽살구클럽&amp;gt;, 그저 응원할 수 밖에 없다</title>
      <link>https://draw-code-boy.tistory.com/620</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;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; 이번에 고른 책은 노래들을 통해 관심을 갖게 된 한로로의 &amp;lt;자몽살구클럽&amp;gt;이다. 앨범과 동명의 소설인데 소설에서 다루는 많은 이야기들이 노래와 접점이 있다 하여 관심을 갖고 있었고, 우연히 서점에 들렸을 때 발견한 이 책은 생각했던 것보다 얇은 책이라 읽어보고 싶었다. &amp;lsquo;시간을 달리네&amp;rsquo;, &amp;lsquo;0+0&amp;rsquo;의 가사를 좋아했고, 이를 곱씹으며 자주 들었던 노래들이 소설과 어떤 연결이 되는지 궁금증을 갖고 책을 읽기 시작했다.&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;
&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;
&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;
&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>Daily/Book Reports</category>
      <category>독후감</category>
      <category>자몽살구클럽</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/620</guid>
      <comments>https://draw-code-boy.tistory.com/620#entry620comment</comments>
      <pubDate>Fri, 26 Dec 2025 11:17:40 +0900</pubDate>
    </item>
    <item>
      <title>당신이 Neural ODE가 어려운 이유는 수학 때문이 아닐 수도 있다.</title>
      <link>https://draw-code-boy.tistory.com/619</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;연구노트 긁어오기 3&lt;/b&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 미분 방정식이 처음이라서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Neural ODE에서 다루는 미분 방정식은 크게 어렵지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;미분 방정식이 무엇인지? 방정식인데, 그 해가 함수이거나 함수 집합이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수치적으로 푸는 방법 = Euler&amp;rsquo;s method (단순한 ODE Solver)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것만 알아도 크게 어려움이 없다. 그리고, 위 두 개념은 크게 어렵지 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, Runge-Kunta까지 안다면 더더욱 어려움이 없을 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 2번 Section의 설명이 조금 난해해서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리뷰를 해본 자의 입장으로서, 2번 Section이 Neural ODE의 핵심으로 보인다. &lt;b&gt;(Sensitivity Adjoint Method를 제안)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이에 대한 수식 전개가 없고 뜬금 없이 등장한 거 같아 매우 혼란을 겪는다. 필자 또한 이를 파훼할 방법을 찾느라 3일이 걸렸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, 2번 Section 마지막에 짤막하게 Appendix B에 Sensitivity Adjoint Method에 대한 수식 전개가 있음을 발견하게 된다. (Augmented state를 사용하여 Adjoint state를 ODE Solver를 통해 풀었을 때 Solution이 왜 그렇게 나오는지 설명)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러니 수식에 당황하고 있다면, Appendix B를 살펴봐라. (찾기 어려웠던 이유는 Appendix A가 3번 Section의 Normalizing Flow에 관한 내용이라 당연히 없는 줄 알았다.)&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 파라미터의 단일화, time-conditioned dynamics (Non-autonomous dynamics)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 Residual Networks, Normalizing Flows, RNN Decoders를 먼저 접해본 사람이라면, Neural ODE에서 말하는 모델링 방법은 무한의 파라미터를 요구하는 것처럼 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 실제 구현과 논문의 Section을 보면, 하나의 Layer(파라미터)를 가지고, 모델링을 하는 것에 있어서 혼란을 겪게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DDPM을 생각해보라. 각 step마다 하나의 모델(U-Net)을 사용했는지 말이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니다. time을 나타내는 \(t\)를 입력으로 사용하여 어떻게 보면 \(t\)를 Hypernetwork의 역할을 하도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 time-conditioned dynamics(수학에서는 non-autonomous dynamics라 한다.)라 한다더라.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;즉, \(t\)가 들어감으로써 하나의 파라미터로 서로 다른 layer 효과를 낸다는 것이다. (핵심)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Neural ODE에서도 암묵적으로 이 방법을 사용하고 있고, 그게 직접 명시되지 않았기에 직관에 어려움을 겪게 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 핵심인 내용인데, 논문에서는 왜 언급되지 않았나? 아예 언급이 안 된 것은 아니다. 우리가 아직 time-conditioned dynamics에 익숙하지 않아서 수식으로 간단하게 표현한 것들을 놓친 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Introduction에서 Residual Networks를 아래와 같이 정의한다. 이 때는 각 layer에 따라 파라미터가 다름을 알 수 있다. \(\theta_t\)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \mathbf{h}_{t+1}=\mathbf{h}_t+f(\mathbf{h}_t,\theta_t) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 극한으로 보내서 ODE로 정의하면서 수식이 조금 바뀌게 된다. 지금은 time \(t\)와 \(\theta\)가 따로 분리되어 들어가는 것을 알 수 있다. &lt;b&gt;즉, 저자들이 명시적으로 글에서 나타내지는 않았지만, 각 time에 따른 변환을 \(t\)&lt;/b&gt;&lt;b&gt;로 구분하고 paramter sharing하겠다 그 얘기다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \frac{d\mathbf{h}(t)}{dt}=f(\mathbf{h}(t),t,\theta) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면, 모든 \(t\)에 대한 gradient는 \(\theta\)가 받나? 그렇다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 구현의 어려움 (=Autograd)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어찌 논문을 다 리뷰하고, 구현을 해볼까 하면 Backpropagation을 직접 구현해야 하는 것에 있어 난항을 겪게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분은 우리가 Autograd에 대한 핸들링을 해볼 일이 거의 없었기 때문에 그렇다. torchdiffeq 구현을 보지말고, 다른 사람들이 간단하게 구현해둔 것을 찾아봐라. 난 아래 repository가 도움이 되는 거 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/msurtsukov/neural-ode/tree/master&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/msurtsukov/neural-ode/tree/master&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1766167634630&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - msurtsukov/neural-ode: Jupyter notebook with Pytorch implementation of Neural Ordinary Differential Equations&quot; data-og-description=&quot;Jupyter notebook with Pytorch implementation of Neural Ordinary Differential Equations - msurtsukov/neural-ode&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/msurtsukov/neural-ode/tree/master&quot; data-og-url=&quot;https://github.com/msurtsukov/neural-ode&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c8z3dC/hyZP4URfNR/9AshmXYmND1YnBpFIuzkk1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/vuHQ3/hyZPmW9UUx/3KPAVd8dRNzfbHMEWPGqd0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/msurtsukov/neural-ode/tree/master&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/msurtsukov/neural-ode/tree/master&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c8z3dC/hyZP4URfNR/9AshmXYmND1YnBpFIuzkk1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/vuHQ3/hyZPmW9UUx/3KPAVd8dRNzfbHMEWPGqd0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;GitHub - msurtsukov/neural-ode: Jupyter notebook with Pytorch implementation of Neural Ordinary Differential Equations&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Jupyter notebook with Pytorch implementation of Neural Ordinary Differential Equations - msurtsukov/neural-ode&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.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;이 부분에 대해서도 조금만 공부를 해보면 되니 크게 어려울 바 없다. (사실 아직 내가 해본 것은 아니다. 하지만, 간단하게 구현한 버전을 보니, Autograd에 대해 그렇게 많이 다루지는 않는다.)&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;저자들은 구현을 중심으로 이 논문을 작성했단 걸.. (특히, Pseudo code 진짜..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;논문을 한 번 brief하게 리뷰하고 간단하게 구현된 오픈 소스들을 한 번 찾아보는 걸 추천한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 정리하자면&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하자면, &lt;b&gt;Neural ODE가 어려운 이유는 수학 때문이 아닐 수도 있다.&lt;/b&gt; &amp;ldquo;중요한 수식의 유도 생략 + 단일 파라미터에 대한 혼란 + autograd 조작&amp;rdquo;같은 점 때문에 우리들은 어려워했을 수도 있다.&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;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;</description>
      <category>연구노트 긁어오기</category>
      <category>Neural ODE</category>
      <category>미분 방정식</category>
      <category>어려운 이유</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/619</guid>
      <comments>https://draw-code-boy.tistory.com/619#entry619comment</comments>
      <pubDate>Sat, 20 Dec 2025 03:08:00 +0900</pubDate>
    </item>
    <item>
      <title>왜 Graph의 Laplacian matrix의 eigenvector는 Fourier basis라 불리는가?</title>
      <link>https://draw-code-boy.tistory.com/618</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;연구노트 긁어오기 2&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;어제 논문을 읽던 도중이었나? Graph Spectral Convolution을 이해하고자 보고 있었는데, Laplacian matrix의 eigenvector를 보고 왜 Fourier basis라 하는지 궁금했다. 그래서, 기초적인 신호 처리부터 오래 걸리겠지만, 한 번 보자고 마음 먹고 푸리에 변환을 공부했었다. 특히, 1D Signal에서 Basis function이 Complex exponential이라는 점이 위에서 말하는 Fourier basis라고 하는 부분에 대해서 꽤 중요했다. 또, Laplacian operator와 Eigenfunction이 뭔지 알게 되었다. 이 때가 오늘의 스파크가 터지는 지점이었는데, &lt;b&gt;&amp;lsquo;Laplacian operator의 Eigen function이 Fourier transform의 Complex exponential(basis function)이다&amp;rsquo;&lt;/b&gt;는 것을 포착했다. &lt;b&gt;&amp;lsquo;그럼 Graph의 Laplacian matrix의 Eigenvector도 Fourier basis라 볼 수 있다&amp;rsquo;&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;하지만, 그런 생각도 들었다. Laplacian matrix가 Laplacian이라는 이름을 갖게 된 이유? 왜냐하면, Laplacian operator와 Laplacian matrix의 공통점을 찾지 못 했다 보니 끼워 맞추기가 되는 느낌이 들었다. 이로 인해 eigenvector를 바로 Fourier basis라 해석하는 것이 조금 낯설었던..? 이 부분은 조금 더 탐색을 해봐야겠다.&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;&amp;lt;The Emerging Field of Signal Processing on Graphs Extending High-Dimensional Data Analysis to Networks and Other Irregular Domain&amp;gt;&lt;/i&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;Graph signal processing, Graph neural networks를 잘 이해하는 것은 결국 signal processing의 근간이 되는 technique들을 이해하는 데에 있었다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빨리 Filtering, Polynomial parameterization을 이해하고 싶다. 왜냐하면, 매번 막히다가 여기에 내가 원하는 질문과 답들이 있다는 걸 깨달았으니까!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;1138&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/STLkx/dJMcafd53vD/sT7ozaULl7ZEWM6sbm6dOK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/STLkx/dJMcafd53vD/sT7ozaULl7ZEWM6sbm6dOK/img.png&quot; data-alt=&quot;노트 필기 중 (틀린 부분이 있을 수도 있습니다..!)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/STLkx/dJMcafd53vD/sT7ozaULl7ZEWM6sbm6dOK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSTLkx%2FdJMcafd53vD%2FsT7ozaULl7ZEWM6sbm6dOK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;715&quot; height=&quot;655&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;1138&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;노트 필기 중 (틀린 부분이 있을 수도 있습니다..!)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>연구노트 긁어오기</category>
      <category>Fourier</category>
      <category>Graph</category>
      <category>Laplace</category>
      <category>Signal Processing</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/618</guid>
      <comments>https://draw-code-boy.tistory.com/618#entry618comment</comments>
      <pubDate>Wed, 17 Dec 2025 22:40:02 +0900</pubDate>
    </item>
    <item>
      <title>Neural ODE에서 말하는 메모리 문제는 무엇인가요?</title>
      <link>https://draw-code-boy.tistory.com/617</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&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;p data-ke-size=&quot;size16&quot;&gt;요즘 글을 너무 안 올려서... 제 연구노트인데요, 요거라도 올려봅니다. Neural ODE 논문 리뷰를 하는 블로그에서 이 문제를 심층적으로 다루는 글들은 많이 못 봐서요. 제 연구 노트에는 요런 내용들이 좀 많습니다. 괜히 제 딴에는 보다 깊게 이해하려고 하는 것이지만, 시간을 많이 소비한답니다.. 호호&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;아무튼 요즘 근황 겸 요런 글을 어떠신지요. 올 한 해 의도치 않게 Generative Model에 빠져있었는데, GAN, VAE, Normalizing Flow, DDPM 등에 대해서도 요런 고군분투스러운 글들이 많답니다. (조금의 반응이라도 있다면, 앞으로는 조금씩 정제해서 올려볼까 합니다. 제 생각이 맞는지 의심스러울 때가 많거든요..ㅎ 여기를 discussion의 장으로 만들어보고 싶다는 생각도 있네요. Generative Models는 할 얘기가 너무 많답니다!)&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;lt;한 해 회고록&amp;gt;의 시기가 다가오는데요. 올해도 정말 일들이 많았습니다. 저의 예상보다 더 많은 일들이 있었더군요.. 아무튼 오랜만에 쓴 거라 중구난방으로 남겨두고 싶은 말들이 많은데요. 2025년이 끝나는 그 즈음에 저의 회고록으로 뵙겠습니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;연구노트 긁어오기 1&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;Reverse-mode Differentiation은 Wikipedia를 참조했을 때 Backpropagation과 같다는 걸 알았다. (Chain rule인데, 어디서부터 계산해야 되냐 이런 거)&lt;/li&gt;
&lt;li&gt;자 그럼 논문에서 말한 &lt;i&gt;&amp;ldquo;but incurs a high memory and introduces additional numerical error&amp;rdquo;&lt;/i&gt;라고 적은 이유는 무엇인가? (numerical error는 아직 패스)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음과 같은 상황을 가정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \mathbf{h}_{t+1}=\mathbf{h}_t+f(\mathbf{h}_t, t, \theta_t)\:\:(\text{where}\:t\in\{1,2,\dots,N\}) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 변환에서 \(\theta_t\)는 Neural network의 파라미터를 의미한다. &lt;b&gt;그런데, 이 때 \(N\)이 엄청 크면 backpropagation 시에 메모리를 많이 잡아먹는 문제가 incur한다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 그런지 \(N=1\) case를 통해 알아보자. (초기 조건 \(\mathbf{h}_0\))&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \mathbf{h}_{2}=\mathbf{h}_1+f(\mathbf{h}_1, t=1, \theta_1) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \mathbf{h}_{1}=\mathbf{h}_0+f(\mathbf{h}_0, t=0, \theta_0) $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번에 거친 변환 \(\mathbf{h}_2\)를 Loss function에 입력하고, \(\theta_0\)에 대한 update를 위해 chain rule을 이용하여 \(\frac{\partial L}{\partial \theta_0}\)을 전개할 것이다. 이는 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \frac{\partial L}{\partial\theta_0} = \frac{\partial L}{\partial \mathbf{h}_2}\cdot\frac{\partial \mathbf{h}_2}{\partial \mathbf{h}_1}\cdot\frac{\partial \mathbf{h}_1}{\partial f_0}\cdot\frac{\partial f_0}{\partial \theta_0} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 \(\frac{\partial\mathbf{h}_2}{\partial\mathbf{h}_1}\)를 역전파를 위해 실제로 구해보자. (\(\sigma=\text{sigmoid}\))&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \begin{align} \frac{\partial\mathbf{h}_2}{\partial\mathbf{h}_1}&amp;amp;=\frac{\partial\mathbf{h}_1}{\partial\mathbf{h}_1}+\frac{\partial f(\mathbf{h}_1, t=1, \theta_1)}{\partial\mathbf{h}_1}\:\:(\text{where}\:\:f=\sigma(W\mathbf{h}_1+b), \theta_1=\{W, b\})\\ &amp;amp;=I+\frac{\partial\sigma(\mathbf{z})}{\partial \mathbf{z}}\vert_{\mathbf{z}=W\mathbf{h}_1+b}\cdot\frac{\partial(W\mathbf{h}_1+b)}{\partial \mathbf{h}_1} \\ &amp;amp;=I+\{\sigma(\mathbf{z})(1-\sigma(\mathbf{z}))\}\vert_{\mathbf{z}=W\mathbf{h}_1+b}\cdot W \\ &amp;amp;=I+\sigma(W\mathbf{h}_1+b)(1-\sigma(W\mathbf{h}_1+b))\cdot W \end{align} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위 \(\frac{\partial\mathbf{h}_2}{\partial\mathbf{h}_1}\)를 구하기 위해서는 \(\mathbf{h}_1\)을 알아야 한다!&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hidden state가 무엇인지 값을 알고 있어야 한다. &amp;rarr; 메모리 상에 올라가 있어야 한다.&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;hidden state 하나 메모리에 올려둔다고 해서 크게 문제가 되진 않을 것으로 예상된다. 하지만, continuous-depth neural network라 하지 않았는가. 무수히 많은 변환의 case라면 어떻겠는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(N=K-1\) case를 생각해보자. \(\frac{\partial L}{\partial\theta_0}\)을 동일하게 구해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;$$ \frac{\partial L}{\partial\theta_0} = \frac{\partial L}{\partial \mathbf{h}_K}\cdot\frac{\partial \mathbf{h}K}{\partial \mathbf{h}_{K-1}}\cdot\dots\cdot\frac{\partial \mathbf{h}_2}{\partial \mathbf{h}_{1}}\cdot\frac{\partial \mathbf{h}_1}{\partial f_0}\cdot\frac{\partial f_0}{\partial \theta_0} $$&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;\(\frac{\partial \mathbf{h}_K}{\partial \mathbf{h}_{K-1}}\)을 알려면, \(\mathbf{h}_{k-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;&lt;b&gt; 즉, \(K-1\)개의 hidden state를 메모리에 계속 올려두고 있어야 한다는 의미가 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 정리하자면 Residual Networks를 Euler&amp;rsquo;s Method로 보는 관점(ODE의 수치적 해)으로부터 이는 ODE Solver를 통해 해를 구하는 과정과 유사한 것이라 보았고, 변화량만 Neural network를 그대로 써서 ODE의 수치적 해를 구하는 게 Residual Network의 output을 출력하는 것이라 보고 ODE Solver로 forward를 주려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 이 과정에서 ODE Solver의 변화량을 담당하는 Neural network의 학습을 해야 하는데, 모든 hidden state를 메모리에 저장하고 있어야 하는 문제가 생긴다.&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;그렇다면, Neural ODE 논문은 학습 시 hidden state를 저장하지 않아도 되는 방법을 도출해야 한다. 그게 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;adjoint&lt;span&gt; &lt;/span&gt;&lt;/span&gt;sensitivity method라 하는데, 그건 좀 더 리뷰를 해봐야겠다.&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;(2025.12.25) 간단하게 얘기해서 여기서 말하는 메모리 문제라는 건 forward에서 썼던 hidden state를, backward의 chain rule을 수식 전개 해보니 저장해둘 필요가 있었다는 것이고, 그 hidden state가 너무 많으면 과도하게 메모리를 잡아먹는다는 것이다. 그래서, 이를 저장할 필요가 없이 hidden state나 adjoint state의 dynamics를 ODE를 통해서 풀어서 해결하겠다는 것이다.&lt;/p&gt;
&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;</description>
      <category>연구노트 긁어오기</category>
      <category>Backpropagation</category>
      <category>hidden state</category>
      <category>memory</category>
      <category>NeuralODE</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/617</guid>
      <comments>https://draw-code-boy.tistory.com/617#entry617comment</comments>
      <pubDate>Mon, 8 Dec 2025 03:03:07 +0900</pubDate>
    </item>
    <item>
      <title>self.register_buffer(), 학습하지 않을 파라미터라면? (tensor와 명백하게 다른 점 2)</title>
      <link>https://draw-code-boy.tistory.com/612</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  &lt;/span&gt;&lt;/span&gt;Problem&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 PyTorch 관련 글입니다. 최근에는 Generative Model 쪽을 공부하면서 DDPM을 구현하다가 PyTorch의 새로운 기능을 발견했는데요. 바로 오늘 글의 주제가 되는 &lt;code&gt;self.register_buffer()&lt;/code&gt;입니다. 본 포스트는 예전에 작성한 포스트들 중에 &lt;a href=&quot;https://draw-code-boy.tistory.com/595&quot;&gt;'nn.Parameter(), 이걸 써야 하는 이유가 뭘까? (tensor와 명백하게 다른 점)'&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;이전 포스트의 내용을 간략하게 리뷰해 보면 '모델 내에서 단순히 &lt;code&gt;torch.tensor()&lt;/code&gt;를 통해 선언한 텐서는 학습의 대상이 되지 못하고, 이를 명확하게 모델 내 학습을 하는 파라미터로 정의하기 위해서는 &lt;code&gt;nn.Parameter()&lt;/code&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;b&gt;'&lt;code&gt;torch.tensor()&lt;/code&gt;는 학습을 하지는 않지만, &lt;code&gt;nn.Parameter()&lt;/code&gt;처럼 모델 내에 속해 있다고 볼 수는 없다.'&lt;/b&gt;라는 내용을 먼저 전달드리고 싶습니다. '모델 코드 안에 선언을 해두었는데 모델에 속해있지 않다는 게 무슨 말이지?'라고 보실 수 있습니다만, 제가 전달하고자 하는 '모델 내에 속해있다'라는 말의 정의는 &lt;code&gt;model.state_dict()&lt;/code&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;model.state_dict()&lt;/code&gt;는 모델의 weight를 저장할 때 사용하는 메서드입니다. 아래와 같이 &lt;code&gt;torch.tensor()&lt;/code&gt;를 통해 정의한 weight가 포함된 모델을 저장하려고 메서드를 사용했을 때는 해당 weight가 보이지 않습니다. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;즉, 모델의 구성요소로 인정하고 있지 않습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1736860611116&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Model(nn.Module):
    def __init__(self):
        super().__init__()
        
        self.tensor = torch.randn(3, 3)
        
        self.param = nn.Parameter(torch.randn(3, 3))
    ...

model = Model()
print(model.state_dict())

[Output]
OrderedDict({'param': tensor([[ 0.3108,  0.5101, -0.8290],
        [ 0.3511, -0.4658,  0.2131],
        [ 0.6602, -1.0786, -0.3299]])})&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;하지만, 프로젝트를 진행하거나 논문을 보다 보면, 학습을 하지 않음에도 불구하고 모델 내 구성 요소로 취급되는 텐서, 행렬, 스칼라를 보게 됩니다.(ex: DDPM의 alpha, beta)&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 style=&quot;letter-spacing: 0px;&quot;&gt;라는 것을 나타낼 단어가 필요합니다. 학계에서는 이를 단순히 &lt;/span&gt;&lt;b&gt;Non-trainable Parameter, Non-learnable Parameter, Fixed Parameter&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;라는 단어로 정의를 합니다. 본 포스트에서는 편하게 &lt;/span&gt;&lt;b&gt;Fixed Parameter&lt;/b&gt;&lt;span style=&quot;letter-spacing: 0px;&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;다시 돌아와서 이 Fixed Parameter는 단순히 &lt;code&gt;torch.tensor()&lt;/code&gt;로 정의되지는 못 한다는 사실을 알게 되었습니다. 그렇다면, PyTorch에서는 이에 대해 어떠한 방법을 제시하고 있을까요?&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  &lt;/span&gt;Solution&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;PyTorch에서는 Fixed Parameter를 Buffer&lt;/b&gt;라고 정의합니다. 최근 논문들에서도 Buffer라는 단어를 일컫는 경우들이 종종 있기도 합니다. 그리고, 이 Buffer를 사용하기 위해서 PyTorch는 &lt;code&gt;self.register_buffer()&lt;/code&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;code&gt;torch.tensor()&lt;/code&gt;로 선언한 다음에 &lt;code&gt;self.register_buffer()&lt;/code&gt;에 할당하여 선언할 수 있습니다. 첫 번째 인자에는 Buffer가 사용할 이름이 되고, 두 번째 인자에는 Buffer로 사용할 텐서가 들어가게 됩니다. 그리고, &lt;code&gt;forward()&lt;/code&gt; 함수에서 다른 Parameter처럼 똑같은 접근이 가능한 걸 볼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1736861467444&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Model(nn.Module):
    def __init__(self):
        super().__init__()
        
        buff = torch.randn(3, 3)
        self.register_buffer('buff', buff)
    
    def forward(self, x):
        print(self.buff)
        return x&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;self.register_buffer()&lt;/code&gt;의 장점은 이것뿐만이 아닙니다. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;디바이스 간 이동의 자율성이 높다&lt;/b&gt;&lt;/span&gt;는 장점이 하나 더 있습니다. &lt;code&gt;torch.tensor()&lt;/code&gt;로 선언한 경우에는 &lt;code&gt;model = Model.to(device)&lt;/code&gt;를 하더라도 다른 디바이스로 넘어가지 않는 불편함이 있었습니다만, &lt;code&gt;self.register_buffer()&lt;/code&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;즉, Buffer로 정의하는 방법은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;2가지 장점&lt;/b&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;학습을 하지 않지만, 모델의 구성요소로 인정할 수 있다. (가중치로 저장할 수 있다.)&lt;/li&gt;
&lt;li&gt;모델의 디바이스 이동 작업이 생길 때 번거로운 작업이 불필요해졌다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, &lt;code&gt;model.parameters()&lt;/code&gt;나 &lt;code&gt;model.name_parameters()&lt;/code&gt;로 파라미터를 확인할 수 있는 것처럼 &lt;code&gt;model.buffers()&lt;/code&gt;나 &lt;code&gt;model.named_buffers()&lt;/code&gt;와 같은 메서드로 Buffer를 확인할 수 있습니다.&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;또한, Buffer라는 특성을 통해 Distribution(&lt;code&gt;torch.distributions&lt;/code&gt;)도 관리가 가능한지 실험해 보았으나, Buffer는 Tensor type만 담을 수 있는 것으로 확인하였습니다.&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가지 장점을 확인할 수 있는 코드와 Output을 같이 올리면서 글을 마무리하겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1736862691193&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Expr 1
import torch
from torch import nn

class Model(nn.Module):
    def __init__(self):
        super().__init__()
        
        self.tensor = torch.randn(3, 3)
        
        self.param = nn.Parameter(torch.randn(3, 3))
        
        buff = torch.randn(3, 3)
        self.register_buffer('buff', buff)
    
    def forward(self, x):
        print(self.buff)
        return x

device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = Model().to(device)

print(&quot;===========================================&quot;)
print(f&quot;tensor: \n{model.tensor}&quot;)
print(f&quot;{model.tensor.device}&quot;)

print(&quot;===========================================&quot;)
for name, parameter in model.named_parameters():
    print(f&quot;{name}: \n{parameter}&quot;)
    print(f&quot;{parameter.device}&quot;)

print(&quot;===========================================&quot;)
for name, buff in model.named_buffers():
    print(f&quot;{name}: \n{buff}&quot;)
    print(f&quot;{buff.device}&quot;)

print(&quot;===========================================&quot;)
print(f&quot;model state_dict(): param O, buff O, tensor X&quot;)
print(model.state_dict())

# Expr 2
# Distribution은 buffer에 register 안 된다.
from torch.distributions.normal import Normal

class Model2(nn.Module):
    def __init__(self):
        super().__init__()
        
        self.gaussian = Normal(loc=torch.zeros(3),
                               scale=torch.ones(3))
        
        gaussian2 = Normal(loc=torch.zeros(3),
                           scale=torch.ones(3))
        
        self.register_buffer('gaussian2', gaussian2)
    
    def forward(self, x):
        sample = self.gaussian.sample()
        
        sample2 = self.gaussian2.sample()
        
        print(f&quot;sample 1 device: {sample.device}&quot;)
        print(f&quot;sample 2 device: {sample2.device}&quot;)
        
        return x

model2 = Model2()
model2(torch.randn(3, 3))

'''
(.venv) E:\DDPM&amp;gt;python register_buffer_test.py
===========================================
tensor: 
tensor([[ 0.5325,  1.3698, -1.2790],
        [-0.5546,  0.3236,  0.6196],
        [ 2.1521,  1.8287,  1.0600]])
cpu
===========================================
param:
Parameter containing:
tensor([[-1.5823, -1.0639, -0.9007],
        [-0.8665, -0.0151,  0.8802],
        [ 0.3128,  2.1903, -0.2867]], requires_grad=True)
cpu
===========================================
buff:
tensor([[-0.0590,  0.4823,  2.1716],
        [-0.6110,  0.1420,  1.5730],
        [ 1.2040, -0.0654, -0.4525]])
cpu
===========================================
model state_dict(): param O, buff O, tensor X
OrderedDict({'param': tensor([[-1.5823, -1.0639, -0.9007],
        [-0.8665, -0.0151,  0.8802],
        [ 0.3128,  2.1903, -0.2867]]), 'buff': tensor([[-0.0590,  0.4823,  2.1716],
        [-0.6110,  0.1420,  1.5730],
        [ 1.2040, -0.0654, -0.4525]])})
Traceback (most recent call last):
  File &quot;E:\DDPM\register_buffer_test.py&quot;, line 64, in &amp;lt;module&amp;gt;
    model2 = Model2()
             ^^^^^^^^
  File &quot;E:\DDPM\register_buffer_test.py&quot;, line 52, in __init__
    self.register_buffer('gaussian2', gaussian2)
  File &quot;E:\DDPM\.venv\Lib\site-packages\torch\nn\modules\module.py&quot;, line 566, in register_buffer
    raise TypeError(
TypeError: cannot assign 'torch.distributions.normal.Normal' object to buffer 'gaussian2' (torch Tensor or None required)
'''&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pytorch.org/docs/stable/generated/torch.nn.Module.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://pytorch.org/docs/stable/generated/torch.nn.Module.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736862300975&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;Module &amp;mdash; PyTorch 2.5 documentation&quot; data-og-description=&quot;Shortcuts&quot; data-og-host=&quot;pytorch.org&quot; data-og-source-url=&quot;https://pytorch.org/docs/stable/generated/torch.nn.Module.html&quot; data-og-url=&quot;https://pytorch.org/docs/stable/generated/torch.nn.Module.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pytorch.org/docs/stable/generated/torch.nn.Module.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pytorch.org/docs/stable/generated/torch.nn.Module.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Module &amp;mdash; PyTorch 2.5 documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Shortcuts&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pytorch.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.ai-bio.info/pytorch-register-buffer&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.ai-bio.info/pytorch-register-buffer&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1736862265421&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;PyTorch에서 register_buffer를 써야하는 이유&quot; data-og-description=&quot;PyTorch model에서 register_buffer를 사용하는 이유에 대해 알아봅니다.&quot; data-og-host=&quot;www.ai-bio.info&quot; data-og-source-url=&quot;https://www.ai-bio.info/pytorch-register-buffer&quot; data-og-url=&quot;https://www.ai-bio.info/pytorch-register-buffer&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/swWyA/hyX0ou9nrk/IqCqzkS2BAhaRFfKd9cCHk/img.jpg?width=1500&amp;amp;height=1009&amp;amp;face=0_0_1500_1009,https://scrap.kakaocdn.net/dn/kD5b2/hyX0tDeuRa/DnZL31dtgKp8sQkZJlHu6k/img.jpg?width=1500&amp;amp;height=1009&amp;amp;face=0_0_1500_1009,https://scrap.kakaocdn.net/dn/SwN2i/hyX0rZF7F5/QhUGZowxPzujddfZVIuRVk/img.jpg?width=1500&amp;amp;height=1009&amp;amp;face=0_0_1500_1009&quot;&gt;&lt;a href=&quot;https://www.ai-bio.info/pytorch-register-buffer&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.ai-bio.info/pytorch-register-buffer&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/swWyA/hyX0ou9nrk/IqCqzkS2BAhaRFfKd9cCHk/img.jpg?width=1500&amp;amp;height=1009&amp;amp;face=0_0_1500_1009,https://scrap.kakaocdn.net/dn/kD5b2/hyX0tDeuRa/DnZL31dtgKp8sQkZJlHu6k/img.jpg?width=1500&amp;amp;height=1009&amp;amp;face=0_0_1500_1009,https://scrap.kakaocdn.net/dn/SwN2i/hyX0rZF7F5/QhUGZowxPzujddfZVIuRVk/img.jpg?width=1500&amp;amp;height=1009&amp;amp;face=0_0_1500_1009');&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;PyTorch에서 register_buffer를 써야하는 이유&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;PyTorch model에서 register_buffer를 사용하는 이유에 대해 알아봅니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.ai-bio.info&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;</description>
      <category>Code about AI/PyTorch</category>
      <category>buffer</category>
      <category>fixed parameter</category>
      <category>non-learnable parameter</category>
      <category>non-trainable parameter</category>
      <category>parameter</category>
      <category>pytorch</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/612</guid>
      <comments>https://draw-code-boy.tistory.com/612#entry612comment</comments>
      <pubDate>Tue, 14 Jan 2025 22:56:15 +0900</pubDate>
    </item>
    <item>
      <title>GPU 없이 Colab(코랩)으로 AI를 하고 있는 당신에게 전하는 3가지 팁</title>
      <link>https://draw-code-boy.tistory.com/609</link>
      <description>&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;✅ Introduction&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;많은 사람들이 AI를 공부하면서 모델을 학습시킬 수 있는 능력들을 갖추었지만, 개인 GPU를 보유하고 있는 경우는 많이 없습니다. 이에 대한 해결책으로 Google의 Colab을 많이 사용합니다. 하지만, Colab 자체가 본질적으로는 가상 머신이다 보니 개인 컴퓨터에 GPU가 있는 환경과 다른 부분들이 있습니다. 다시 말해서, 모델 학습을 시키는 작업을 할 때, 추가적인 작업들을 필요로 합니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그래서, 이러한 추가적인 작업들에 대해 지난 경험들을 바탕으로 얻은 팁들을 다루어보고자 합니다. 정리하고자 하는 팁들은 새로운 작업들을 뜻하는 것은 아니고, 기존 작업들을 더 효율적으로 처리할 수 있는 방법들입니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;GPU를 갖고 있는 일반적인 환경이라면 작업을 수행하는 소스 코드, 데이터셋, 가중치 파일 등이 모두 한 곳에 있기 때문에 이 구성 요소들을 옮기는 작업은 불필요합니다. 하지만, Colab이라는 가상 머신은 런타임을 실행할 때마다 새로운 환경이 주어집니다. 즉, Colab에서는 매번 새롭게 실행할 때마다 각 요소들을 가져올 수 있어야 합니다. 가져오는 방법에 따라 실행 시간이 오래 걸리는 경우들이 많습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그래서, 본 포스팅에서는 &lt;b&gt;&lt;span style=&quot;background-color: #F6E199;&quot;&gt;각 요소들(소스 코드, 데이터셋, 가중치 파일)을 효율적으로 옮기는 방법들에 대해 정리합니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  1. 소스 코드 복제 및 코드 모듈화 역량&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;소스 코드를 Colab에 가져오는 방법은 깃허브의 리포지토리를 복제하고, 리포지토리가 있는 디렉터리로 이동하여 사용하는 방법이 일반적입니다. 제가 제안하는 방법도 이와 똑같은 방법이기 때문에, 굳이 이 글에 적을 필요는 없습니다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 복제하기
!git clone [리포지토리 주소]

# 복제한 리포지토리로 이동하기
os.chdir(&quot;[복제한 리포지토리 경로]&quot;)&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 일반적으로 복제해 오는 경우는 다른 사람의 코드를 복제하는 경우가 많습니다. 이 단락에서 말하고자 하는 것은 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;오픈 소스와 같은 형태의 코드를 작성할 수 있는 역량을 구축해야 한다는 것입니다&lt;/span&gt;.&lt;/b&gt; 로컬에서 오픈 소스의 형태로 코드를 작성하여, 이를 깃허브 리포지토리에 올립니다. 그리고, Colab에서 학습을 위해 본인의 리포지토리를 복제할 수 있도록 하는 능력을 필요로 합니다. 이러한 역량을 갖추기 위해서는 모듈화(패키지), &lt;code&gt;argparse&lt;/code&gt;, &lt;code&gt;venv&lt;/code&gt;에 대해서 알고 있으면 좋습니다.&lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;br&gt;아래 링크에서는 대부분의 딥러닝 오픈 소스가 가지는 공통적인 특징들을 템플릿입니다. 해당 템플릿을 가지고 본인의 목적에 맞게끔 수정하여 모델을 학습시킬 수 있습니다. 그 아래의 링크에서는 필자가 작성한 소스 코드입니다. 템플릿의 복잡한 내용들을 덜어내어, MNIST 데이터셋을 간단하게 Convolutional Network를 학습시키는 소스 코드 리포지토리입니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;첫 키워드부터 글의 전체적인 목적과 어긋나는 것이 있지만, &lt;b&gt;&lt;span style=&quot;background-color: #F6E199;&quot;&gt;이러한 역량은 딥러닝을 하는 사람으로서 알고 있으면 많은 부분들이 편해지는 역량&lt;/span&gt;&lt;/b&gt;입니다.&lt;/p&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub - victoresque/pytorch-template: PyTorch deep learning projects made easy.&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;PyTorch deep learning projects made easy. Contribute to victoresque/pytorch-template development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/victoresque/pytorch-template&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/OAhxm/hyXlQTBP5w/r9mU40vDkc5aZt74sE1rVK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/QaauM/hyXlI88Gjd/vY1pLvByb1SFjtOU8lQCw0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot; data-og-url=&quot;https://github.com/victoresque/pytorch-template&quot;&gt;&lt;a href=&quot;https://github.com/victoresque/pytorch-template&quot; target=&quot;_blank&quot; data-source-url=&quot;https://github.com/victoresque/pytorch-template&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/OAhxm/hyXlQTBP5w/r9mU40vDkc5aZt74sE1rVK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/QaauM/hyXlI88Gjd/vY1pLvByb1SFjtOU8lQCw0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;GitHub - victoresque/pytorch-template: PyTorch deep learning projects made easy.&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;PyTorch deep learning projects made easy. Contribute to victoresque/pytorch-template development by creating an account on GitHub.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;figure data-ke-type=&quot;opengraph&quot; data-og-title=&quot;GitHub - drawcodeboy/ConvNet-for-MNIST&quot; data-ke-align=&quot;alignCenter&quot; data-og-description=&quot;Contribute to drawcodeboy/ConvNet-for-MNIST development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/drawcodeboy/ConvNet-for-MNIST&quot; data-og-image=&quot;&quot; data-og-url=&quot;https://github.com/drawcodeboy/ConvNet-for-MNIST&quot;&gt;&lt;a href=&quot;https://github.com/drawcodeboy/ConvNet-for-MNIST&quot; target=&quot;_blank&quot; data-source-url=&quot;https://github.com/drawcodeboy/ConvNet-for-MNIST&quot;&gt;&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('')&quot;&gt; &lt;/div&gt;&lt;div class=&quot;og-text&quot;&gt;&lt;p class=&quot;og-title&quot;&gt;GitHub - drawcodeboy/ConvNet-for-MNIST&lt;/p&gt;&lt;p class=&quot;og-desc&quot;&gt;Contribute to drawcodeboy/ConvNet-for-MNIST development by creating an account on GitHub.&lt;/p&gt;&lt;p class=&quot;og-host&quot;&gt;github.com&lt;/p&gt;&lt;/div&gt;&lt;/a&gt;&lt;/figure&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  2. 데이터셋 접근&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;데이터셋의 경우, Colab에서 학습시킬 때는 Google Drive에 업로드해 두고, 런타임 실행 중에 Drive와 마운트 하여 데이터를 가져오는 것이 일반적입니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;하지만, &lt;b&gt;&lt;span style=&quot;background-color: #F6E199;&quot;&gt;마운트를 하고 난 후 데이터셋이 여전히 Drive에 있다면 학습 및 추론 속도가 매우 느립니다.&lt;/span&gt;&lt;/b&gt; 이에 대한 이유는 학습 및 추론을 하는 과정에서 배치를 가져올 때, 계속 Drive에 접근을 해야 하기 때문입니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;그래서, 마운트를 하고 난 후에 &lt;b&gt;&lt;span style=&quot;background-color: #F6E199;&quot;&gt;데이터셋을 Drive에서 Colab 가상 머신 내부 경로로 복사&lt;/span&gt;&lt;/b&gt;시키고, 데이터셋 관련 코드에서 데이터에 접근하는 주소를 Colab 가상 머신 경로로 설정해 두면 Drive에 접근할 필요가 없어지기 때문에 매우 빨라집니다. &lt;b&gt;&lt;span style=&quot;background-color: #F6E199;&quot;&gt;즉, 소스 코드와 데이터셋을 동일한 환경에 위치시킨다는 의미입니다.&lt;br&gt;&lt;br&gt;&lt;/span&gt;&lt;/b&gt;이 과정에서는 데이터셋을 zip으로 관리하고 마운트했을 때 압축을 푸는 게 빠르고 데이터를 안전하게 이동할 수 있습니다. 마운트한 zip 파일은 파이썬 모듈을 통해 압축을 풀 수 있습니다.&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;867&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yCR8R/btsKiyE1DVl/BoIs5ZnVchnm7AQ7qem0Lk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yCR8R/btsKiyE1DVl/BoIs5ZnVchnm7AQ7qem0Lk/img.png&quot; data-alt=&quot;(왼) 데이터셋 복사 전, (오) 데이터셋 복사 후&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yCR8R/btsKiyE1DVl/BoIs5ZnVchnm7AQ7qem0Lk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyCR8R%2FbtsKiyE1DVl%2FBoIs5ZnVchnm7AQ7qem0Lk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;1706&quot; height=&quot;867&quot; data-origin-width=&quot;1706&quot; data-origin-height=&quot;867&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;(왼) 데이터셋 복사 전, (오) 데이터셋 복사 후&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이와 같은 과정을 수행하기 위해서 위 1번(소스 코드)을 Colab 가상 머신에 복제합니다. 그 후, &lt;code&gt;os&lt;/code&gt;, &lt;code&gt;zipfile&lt;/code&gt; 라이브러리를 활용하여 아래와 같은 셀을 작성하여 Drive에 있던 데이터셋을 Colab 가상 머신으로 복사할 수 있습니다. (아래의 셀은 데이터셋이 zip 파일인 경우에 해당합니다.)&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;import os
import zipfile

zip_file_name = &quot;[Drive 내에 데이터셋 경로]&quot;

os.mkdir(&quot;[Colab 가상 머신 내에 데이터셋을 위치할 경로 + 디렉터리 이름]&quot;)

extraction_dir = &quot;[Colab 가상 머신 내에 데이터셋을 위치할 경로 + 디렉터리 이름]&quot;

# Drive에서 Colab 가상 머신으로
with zipfile.ZipFile(zip_file_name, 'r') as zip_ref:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;zip_ref.extractall(extraction_dir)&lt;/code&gt;&lt;/pre&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  3. 가중치 파일(.pth) 다운로드&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;모델을 학습시킨 후에 생성된 가중치 파일은 런타임이 중지되면 사라집니다. 그렇기 때문에 가중치 파일을 본인의 로컬 환경에 다운로드하는 것은 일반적입니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;다운로드하는 방법은 일반적으로 2가지 방법이 제안됩니다. 첫 번째는 Colab 좌측 탭의 [파일]에 들어가서 원하는 파일에 마우스를 올린 후에 뜨는 설정 탭을 눌러 [다운로드]를 하는 방법이 있고, 두 번째는 아래의 셀과 같이 Colab 자체에서 제공하는 라이브러리를 사용해 다운로드하는 방법이 있습니다. &lt;b&gt;&lt;span style=&quot;background-color: #F6E199;&quot;&gt;하지만, 두 방법 모두 매우 느리다는 단점이 있습니다.&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;373&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/to5oY/btsKh2fw8ac/CAST9sZsh2s53630ywJFGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/to5oY/btsKh2fw8ac/CAST9sZsh2s53630ywJFGk/img.png&quot; data-alt=&quot;1번째 방법 (느린 경우)&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/to5oY/btsKh2fw8ac/CAST9sZsh2s53630ywJFGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fto5oY%2FbtsKh2fw8ac%2FCAST9sZsh2s53630ywJFGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;472&quot; height=&quot;373&quot; data-origin-width=&quot;472&quot; data-origin-height=&quot;373&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1번째 방법 (느린 경우)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# 2번째 방법 (느린 경우)
from google.colab import files

files.download('[다운로드 할 파일 경로]')&lt;/code&gt;&lt;/pre&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;다운로드를 하는 더 빠른 방법은 Google Drive를 이용하는 것입니다. 데이터셋을 Google Drive에서 Colab 가상 머신으로 복사한 것과 유사하게, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;가중치 파일은 Colab 가상 머신에서 Google Drive로 복사하는 것이 제안하고자 하는 방법&lt;/b&gt;&lt;/span&gt;입니다. Drive로 복사한 후, Drive에 직접 가서 가중치 파일을 다운로드하는 경우가 더 빨랐습니다. (모든 실험이 그랬음) 그래서, Drive로 가중치 파일을 복사하는 방법은 &lt;code&gt;shutil&lt;/code&gt;이라는 파이썬 내장 라이브러리를 사용하여 아래와 같이 구현할 수 있습니다.&lt;/p&gt;&lt;pre data-ke-type=&quot;codeblock&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot;&gt;&lt;code&gt;# Solution (빠른 경우)
import shutil

from_path = &quot;[Colab 가상 머신 내에 다운로드 할 파일 경로]&quot;
to_path = &quot;[Drive에 복사할 경로 + 파일 이름]&quot;

shutil.copy(from_path, to_path)

# 복사가 끝나면, Drive에 직접 가서 파일을 로컬 환경으로 다운 받을 것!&lt;/code&gt;&lt;/pre&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot;&gt;&lt;h2 style=&quot;text-align: left;&quot; data-ke-size=&quot;size26&quot;&gt;✅ Conclusion&lt;/h2&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글은 &lt;b&gt;&lt;span style=&quot;background-color: #F6E199;&quot;&gt;Colab을 효율적으로 다룰 수 있는 방법&lt;/span&gt;&lt;/b&gt;들에 대해 다루었습니다. 소스 코드를 깃허브를 통해서 가져오는 방법(+ 모듈화 역량 필요), 데이터셋에 빠르게 접근하는 방법, 가중치 파일을 빠르게 다운로드하는 방법을 정리하였습니다.&lt;br&gt;&amp;nbsp;&lt;br&gt;이를 통해, Colab을 활용한 딥러닝 프로젝트를 수행한다면 보다 효율적인 작업을 수행할 수 있을 것으로 예상됩니다.&lt;/p&gt;</description>
      <category>Code about AI/etc</category>
      <category>COLAB</category>
      <category>다운로드</category>
      <category>데이터셋</category>
      <category>시간 단축</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/609</guid>
      <comments>https://draw-code-boy.tistory.com/609#entry609comment</comments>
      <pubDate>Fri, 25 Oct 2024 00:28:55 +0900</pubDate>
    </item>
    <item>
      <title>scheduler.get_last_lr(), 이제는 Learning Rate를 이렇게 확인하더랍니다</title>
      <link>https://draw-code-boy.tistory.com/607</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  &lt;/span&gt;&lt;/span&gt;Problem&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모델을 학습하다가 이상하게 스케줄러에 대한 출력(Learning Rate에 대한 변경 출력)이 없어서 이에 대해 &lt;code&gt;verbose&lt;/code&gt;를 &lt;code&gt;True&lt;/code&gt;로 바꿔주려고 작업할 내용을 확인하기 위해 공식 문서를 들어가 봤다가 아래 내용을 확인했습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;151&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pIYdW/btsIQKe3vYP/lK8XEbzcOBIaE9LzkkMjZK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pIYdW/btsIQKe3vYP/lK8XEbzcOBIaE9LzkkMjZK/img.png&quot; data-alt=&quot;PyTorch: &amp;quot;우리 이제 verbose 기능 뺄 거예요, 대신 이제 이거 쓰세요.&amp;quot;&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pIYdW/btsIQKe3vYP/lK8XEbzcOBIaE9LzkkMjZK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpIYdW%2FbtsIQKe3vYP%2FlK8XEbzcOBIaE9LzkkMjZK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.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;151&quot; data-origin-width=&quot;739&quot; data-origin-height=&quot;151&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;PyTorch: &quot;우리 이제 verbose 기능 뺄 거예요, 대신 이제 이거 쓰세요.&quot;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  &lt;/span&gt;Solution&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;이제는 스케줄러에 대해서&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;code&gt;verbose&lt;/code&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;argument를 지원하지 않겠다는 내용이었습니다. 하지만, 스케줄러에 대한 메서드 &lt;code&gt;get_last_lr()&lt;/code&gt;을 사용해 보라고 합니다. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;이 메서드는 스케줄러가 마지막으로 계산한 Learning Rate를 출력하며, 이는 '현재 에포크에서 어떠한 Learning Rate로 학습을 진행하게 되는가'&lt;/b&gt;&lt;/span&gt;를 의미합니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서, 아래 내용으로 잘 확인이 되는지를 실험하기 위해 간단한 코드를 만들어줬습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;매 에포크마다 현재의 Learning Rate를 출력하도록 해주었으며, 그에 대한 결과는 아래와 같습니다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;(아래 코드는 의도적으로 &lt;code&gt;mode&lt;/code&gt;를 &lt;code&gt;max&lt;/code&gt;로 두어, 줄어드는 &lt;code&gt;loss&lt;/code&gt; 값에 대해 스케줄러 빠르게 작동하도록 설계하였습니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1722141522295&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;... # 위 모델, 손실 함수 등 불필요한 내용 생략
optimizer = Adam(model.parameters(), lr=0.001)
scheduler = ReduceLROnPlateau(optimizer, mode='max', patience=3)

epochs = 20

for epoch in range(epochs):
    print(f&quot;Epoch: {epoch+1:04d} - Learning Rate: {scheduler.get_last_lr()[0]:.8f}&quot;, end=' ')
    
    optimizer.zero_grad()
    output_tensor = model(input_tensor)
    
    loss = loss_fn(output_tensor, target_tensor)
    
    loss.backward()
    optimizer.step()
    
    print(f&quot;Loss: {loss.item():.8f}&quot;)
    scheduler.step(loss)&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722141773104&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Epoch: 0001 - Learning Rate: 0.00100000 Loss: 0.25520608
Epoch: 0002 - Learning Rate: 0.00100000 Loss: 0.25505885
Epoch: 0003 - Learning Rate: 0.00100000 Loss: 0.25491315
Epoch: 0004 - Learning Rate: 0.00100000 Loss: 0.25476906
Epoch: 0005 - Learning Rate: 0.00100000 Loss: 0.25462645
Epoch: 0006 - Learning Rate: 0.00010000 Loss: 0.25448543
Epoch: 0007 - Learning Rate: 0.00010000 Loss: 0.25447142
Epoch: 0008 - Learning Rate: 0.00010000 Loss: 0.25445747
Epoch: 0009 - Learning Rate: 0.00010000 Loss: 0.25444353
Epoch: 0010 - Learning Rate: 0.00001000 Loss: 0.25442955
Epoch: 0011 - Learning Rate: 0.00001000 Loss: 0.25442815
Epoch: 0012 - Learning Rate: 0.00001000 Loss: 0.25442678
Epoch: 0013 - Learning Rate: 0.00001000 Loss: 0.25442535
Epoch: 0014 - Learning Rate: 0.00000100 Loss: 0.25442401
Epoch: 0015 - Learning Rate: 0.00000100 Loss: 0.25442386
Epoch: 0016 - Learning Rate: 0.00000100 Loss: 0.25442374
Epoch: 0017 - Learning Rate: 0.00000100 Loss: 0.25442359
Epoch: 0018 - Learning Rate: 0.00000010 Loss: 0.25442347
Epoch: 0019 - Learning Rate: 0.00000010 Loss: 0.25442344
Epoch: 0020 - Learning Rate: 0.00000010 Loss: 0.25442341&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;위 결과를 통해 &lt;code&gt;scheduler.get_last_lr()[0]&lt;/code&gt; 코드가 현재의 Learning Rate를 가져오는 것을 확인할 수 있었습니다. 인덱싱을 해준 이유는 해당 메서드의 리턴 타입이 &lt;code&gt;List[float]&lt;/code&gt;라서 위와 같이 값을 받아오도록 해주었습니다.&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ReduceLROnPlateau.html#torch.optim.lr_scheduler.ReduceLROnPlateau&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ReduceLROnPlateau.html#torch.optim.lr_scheduler.ReduceLROnPlateau&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1722141787297&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;ReduceLROnPlateau &amp;mdash; PyTorch 2.4 documentation&quot; data-og-description=&quot;Shortcuts&quot; data-og-host=&quot;pytorch.org&quot; data-og-source-url=&quot;https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ReduceLROnPlateau.html#torch.optim.lr_scheduler.ReduceLROnPlateau&quot; data-og-url=&quot;https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ReduceLROnPlateau.html#torch.optim.lr_scheduler.ReduceLROnPlateau&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ReduceLROnPlateau.html#torch.optim.lr_scheduler.ReduceLROnPlateau&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://pytorch.org/docs/stable/generated/torch.optim.lr_scheduler.ReduceLROnPlateau.html#torch.optim.lr_scheduler.ReduceLROnPlateau&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;ReduceLROnPlateau &amp;mdash; PyTorch 2.4 documentation&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Shortcuts&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;pytorch.org&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>Code about AI/PyTorch</category>
      <category>get_last_lr()</category>
      <category>Learning rate</category>
      <category>pytorch</category>
      <category>ReduceLROnPlateau</category>
      <category>Verbose</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/607</guid>
      <comments>https://draw-code-boy.tistory.com/607#entry607comment</comments>
      <pubDate>Sun, 28 Jul 2024 13:52:38 +0900</pubDate>
    </item>
    <item>
      <title>pyvenv.cfg, venv에서는 파이썬을 이렇게 실행하더랍니다</title>
      <link>https://draw-code-boy.tistory.com/606</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  &lt;/span&gt;&lt;/span&gt;Problem&lt;/h2&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;우선, &lt;code&gt;venv&lt;/code&gt;를 통해서 가상 환경을 만들어 작업을 해오던 입장이었는데 아나콘다(아나콘다 내부의 파이썬 포함)를 완전히 삭제하고, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;파이썬을 재설치해서 환경 변수까지 다시 세팅을 해주었지만, 삭제 이전의 가상 환경들은 모두 이전 아나콘다의 파이썬을 찾는 게 문제점&lt;/b&gt;&lt;/span&gt;이었습니다. (파이썬 파일을 실행시키면, 아래와 같은 오류가 발생합니다.)&lt;/p&gt;
&lt;pre id=&quot;code_1722134535833&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python test.py
&amp;gt;&amp;gt;&amp;gt; No Python at '&quot;C:\Users\user\anaconda3\python.exe'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분명히 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;환경 변수를 새로운 파이썬의 경로에 맞게 잡아주었는데, 어떻게 이전 파이썬 경로를 기억하고 있는지가 의문&lt;/b&gt;&lt;/span&gt;이었습니다. 기존 환경 변수의 경로는 삭제를 해주었으니 말입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  &lt;/span&gt;Solution&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;'현재 가상 환경 내부의 파일 중에 파이썬에 관하여 관리를 하고 있는 파일이 있을 것이다'&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;python.exe&lt;/code&gt;는 심볼릭링크다.'&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;code&gt;python.exe&lt;/code&gt; 파일이 심볼릭링크 파일일 것이다.'는 가설을 세우고 접근했지만, &lt;code&gt;venv&lt;/code&gt; 공식 문서에서는 그걸 추천하지 않을뿐더러 의도적으로 모든 파일이 심볼릭링크가 되게끔 만들려고 했는데 심볼릭링크로는 만들 수 없다는 에러가 발생되어 이 가설은 틀렸다고 판단했습니다.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&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;b&gt;&lt;code&gt;pyvenv.cfg&lt;/code&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;라는 파일에서 이런 내용이 담긴 것을 발견했습니다.&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1722135324945&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;home = C:\Users\user\anaconda3
include-system-site-packages = false
version = 3.10.9&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;home&lt;/code&gt;은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;해당 가상 환경에서 사용하는 파이썬이 어디에 위치해 있는가를 나타낸 경로&lt;/b&gt;&lt;/span&gt;입니다. 위에서 에러가 발생한 경로와 같은 경로입니다. 즉, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;이 파일에서 원래 파이썬 경로를 참조하고 있다&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;'가상 환경에서 사용하는 파이썬은 심볼릭링크가 아니라고 판단이 되는데, 왜 여전히 원본 파이썬의 경로를 참조해야만 하는가?'&lt;/b&gt;인데 이것은 풀리지는 않았습니다. 단, 여전히 &lt;u&gt;&lt;b&gt;원래 경로의 파이썬을 참조를 해야 한다는 사실만은 알고 있어야 합니다.&lt;/b&gt;&lt;/u&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;include-system-site-packages&lt;/code&gt;는 시스템 전역(base 환경)에 설치된 라이브러리들을 사용할 것이냐를 묻는 것인데 패키지의 독립성을 보장하기 위해서라면, 기본적으로 false에 두어야 합니다.&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;version&lt;/code&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;pyvenv.cfg&lt;/code&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt; 파일의 내용에 기반하여 새롭게 설치한 파이썬의 경로와 버전에 맞게끔 아래와 같이 재설정해주면, 기존 가상 환경의 파이썬이 새로운 파이썬을 찾아 실행하는 것을 볼 수 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1722136448661&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;home = C:\Python\Python312
include-system-site-packages = false
version = 3.12.4&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1722136485379&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;python test.py
&amp;gt;&amp;gt;&amp;gt; Hello World&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, 이렇게 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;가상 환경이 바뀔 때 파이썬의 버전만을 바꾸는 것은 조금 위험할 수도 있습니다.&lt;/b&gt;&lt;/span&gt; 파이썬 버전 자체에 의존하여 작동하는 패키지들이 있을 수 있기 때문에 &lt;code&gt;requirements.txt&lt;/code&gt;를 추출해서 따로 관리를 하는 것이 조금 더 바람직합니다.&lt;/p&gt;</description>
      <category>Code about AI/Python</category>
      <category>pyvenv.cfg</category>
      <category>가상 환경</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/606</guid>
      <comments>https://draw-code-boy.tistory.com/606#entry606comment</comments>
      <pubDate>Sun, 28 Jul 2024 12:27:20 +0900</pubDate>
    </item>
    <item>
      <title>DataLoader의 collate_fn, 서로 다른 샘플의 크기를 하나의 배치로 묶는 방법</title>
      <link>https://draw-code-boy.tistory.com/605</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  &lt;/span&gt;&lt;/span&gt;Problem&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업을 하다가 &lt;code&gt;DataLoader&lt;/code&gt;를 선언하는 코드 부분에서 처음 보는 Argument가 있었습니다. 이 Argument에 대해서 공부를 하면서 &lt;b&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;우리는 대부분 모델에 학습을 시킬 때, 각 샘플의 shape이 거의 다 같도록 전처리를 해서 학습을 시키기 때문에 사실 이 기능이 흔하게 쓰이지는 않을 것입니다. 하지만, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;특수한 경우에는 각 데이터 샘플의 shape이 똑같이 처리될 수 없는 경우들이 있습니다.&lt;/b&gt;&lt;/span&gt; 예를 들어, Object Detection에 관한 프로젝트를 한다고 가정하면, 각 이미지에 대해 Bounding Box의 수가 모두 같나요? 거의 대부분 그렇지는 않습니다.&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;/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;background-color: #f6e199;&quot;&gt;&lt;b&gt;여러 개의 샘플을 묶은 배치를 하나의 텐서로 모델에 넘겨주게 됩니다. 그래서, 하나의 텐서로 묶어주려면 각 샘플의 크기가 완전히 같아야 합니다.&lt;/b&gt;&lt;/span&gt; (물론, 배치의 사이즈가 하나라면 제각각이어도 됩니다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #ffffff;&quot;&gt;  &lt;/span&gt;Solution&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 말해, 배치를 가져오는 상황에서 각 샘플의 크기가 다르다면, 제일 크기가 작은 샘플에 대해서 Zero-Padding을 하든, 특정 Constant로 값들을 채워 넣어 크기를 맞추어주든 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;추가적인 작업을 필요&lt;/b&gt;&lt;/span&gt;로 합니다. 이러한 작업을 해줄 수 있는 것이 바로 &lt;code&gt;DataLoader&lt;/code&gt;의 &lt;code&gt;collate_fn&lt;/code&gt;이라는 Argument입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1721919953346&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dataloader = DataLoader(dataset, batch_size=batch_size, collate_fn=collate_fn)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 선언할 수 있으며, Argument로는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;각 텐서를 어떻게 합칠 것인지에 대한 함수&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;collate_fn&lt;/code&gt;의 Argument로는 &lt;b&gt;batch&lt;/b&gt;가 들어오게 됩니다. 이 Batch는 &lt;code&gt;Dataset&lt;/code&gt;의 &lt;code&gt;__getitem__&lt;/code&gt; &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;메서드의 리턴 값들로 리스트를 구성하게 됩니다.&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;a href=&quot;https://seokhee0516.tistory.com/entry/Pytorch-collatefn-%EC%9D%B4%EB%9E%80&quot;&gt;레퍼런스 링크&lt;/a&gt;)&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1721922020621&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class CustomDataset(Dataset):
    def __init__(self, num=10):
        self.num = num
    
    def __len__(self):
        return self.num

    def __getitem__(self, idx):
        x = torch.tensor([idx] * (idx+1))
        y = torch.tensor([idx])
        return x, y&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;레이블은 전부 같지만, input으로 들어오는 텐서의 크기는 다 다르네요.&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 Size가 2인 DataLoader를 선언해 줄 것이라면, 아래와 같이 선언할 것입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1721922114448&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;dl = DataLoader(ds, batch_size=2, collate_fn=collate_fn)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고, 이때 input으로 들어갈 텐서의 크기가 제각각이기 때문에 &lt;code&gt;collate_fn&lt;/code&gt;을 통해서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;&lt;b&gt;'크기가 다른 텐서들을 어떻게 처리할 것이며, 어떻게 하나의 Batch로 합쳐줄 것인가'&lt;/b&gt;&lt;/span&gt;에 대한 과정을 아래와 같은 함수로 만들어줘야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1721922381413&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;def collate_fn(batch):
    &quot;&quot;&quot;
    Args:
        - batch : [(sample_1), (sample_2), ...]
        - sample_n : Dataset.__getitem__(n) =&amp;gt; Dataset의 getitem 메서드에서 선언한대로 튜플이 들어옴
    Description:
        - 결론적으로 collate_fn의 argument는 Batch에 대한 각 샘플이 한 List에 대한 원소로 구성되어 있으며, 
        - 이 모든 원소(샘플)들을 본 함수 내에서 처리하여, 같은 크기의 Tensor를 갖도록 한 다음에
        - 마지막으로, 이 Tensor들을 모두 합치는 과정을 거치도록 한다.
    &quot;&quot;&quot;
    
    # Batch 중에 가장 긴 size를 갖는 텐서의 size 찾기
    longest_size = max([sample[0].shape[0] for sample in batch])
    
    # 긴 사이즈의 텐서에 맞춰서 Zero-padding
    x_batch = [F.pad(sample[0], (0, longest_size - sample[0].shape[0]), 'constant', 0) for sample in batch]
    
    # 사이즈를 맞춘 텐서들을 하나의 텐서로 결합
    x_batch = torch.stack([x for x in x_batch], dim=0)
    
    # label 또한 결합
    y_batch = torch.stack([sample[1] for sample in batch], dim=0)
    return x_batch, y_batch&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 Argument로 들어오는 &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;Batch가 어떤 형태인지를 잘 이해&lt;/span&gt;하고 있다면, 원하는 형태의 Batch로 리턴하여 동일한 크기의 텐서로 만들어서 학습을 진행할 수 있습니다.&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;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;'Batch로 들어오는 Argument는 기존 데이터셋 코드의&lt;/span&gt;&lt;code&gt;__getitem__&lt;/code&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt; 메서드가 여러 번 호출된 리턴 값들이 리스트로 담겨있을 뿐이고, 이를 함수 내에서 어떻게 처리하여 원하는 형태로 나타낼 것인가'&lt;/span&gt;&lt;/b&gt;를 제일 핵심으로 여긴다면 어렵지 않습니다.&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;  원래의 DataLoader로 Batch를 하나씩 출력했을 때&lt;/h3&gt;
&lt;pre id=&quot;code_1721923200109&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;tensor([[0]])
tensor([[0]])
---
tensor([[1, 1]])
tensor([[1]])
---
tensor([[2, 2, 2]])
tensor([[2]])
---
tensor([[3, 3, 3, 3]])
tensor([[3]])
---
tensor([[4, 4, 4, 4, 4]])
tensor([[4]])
---
tensor([[5, 5, 5, 5, 5, 5]])
tensor([[5]])
---
tensor([[6, 6, 6, 6, 6, 6, 6]])
tensor([[6]])
---
tensor([[7, 7, 7, 7, 7, 7, 7, 7]])
tensor([[7]])
---
tensor([[8, 8, 8, 8, 8, 8, 8, 8, 8]])
tensor([[8]])
---
tensor([[9, 9, 9, 9, 9, 9, 9, 9, 9, 9]])
tensor([[9]])
---&lt;/code&gt;&lt;/pre&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;  collate_fn을 구성하고, Batch Size를 2로 설정하여 DataLoader를 출력했을 때&lt;/h3&gt;
&lt;pre id=&quot;code_1721922802210&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;for x, y in dl:
	print(x, y, end='\n---\n', sep='\n')
    
[Output]
tensor([[0, 0],
        [1, 1]])
tensor([[0],
        [1]])
---
tensor([[2, 2, 2, 0],
        [3, 3, 3, 3]])
tensor([[2],
        [3]])
---
tensor([[4, 4, 4, 4, 4, 0],
        [5, 5, 5, 5, 5, 5]])
tensor([[4],
        [5]])
---
tensor([[6, 6, 6, 6, 6, 6, 6, 0],
        [7, 7, 7, 7, 7, 7, 7, 7]])
tensor([[6],
        [7]])
---
tensor([[8, 8, 8, 8, 8, 8, 8, 8, 8, 0],
        [9, 9, 9, 9, 9, 9, 9, 9, 9, 9]])
tensor([[8],
        [9]])
---&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://seokhee0516.tistory.com/entry/Pytorch-collatefn-%EC%9D%B4%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://seokhee0516.tistory.com/entry/Pytorch-collatefn-%EC%9D%B4%EB%9E%80&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1721922007092&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;Pytorch collate_fn 이란?&quot; data-og-description=&quot;DataLoader에는 여러 파라미터가 있어 필요시 적절한 파라미터를 활용해 여러 설정을 줄 수 있다. 그 중에서도 collate_fn은 variable length가 달라서, 패딩해줄 때 사용한다. from torch.utils.data import Dataset, &quot; data-og-host=&quot;seokhee0516.tistory.com&quot; data-og-source-url=&quot;https://seokhee0516.tistory.com/entry/Pytorch-collatefn-%EC%9D%B4%EB%9E%80&quot; data-og-url=&quot;https://seokhee0516.tistory.com/entry/Pytorch-collatefn-%EC%9D%B4%EB%9E%80&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jJcHM/hyWGYi5K6k/lbBwF2YOC2jHPQL35Kqgk0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/n4HNl/hyWCObm3IR/yk2tlnC3rGODxsB7VeTZi1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/vjBau/hyWGZI3bBQ/e65CPyYr5RkUDN9AIfbK91/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600&quot;&gt;&lt;a href=&quot;https://seokhee0516.tistory.com/entry/Pytorch-collatefn-%EC%9D%B4%EB%9E%80&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://seokhee0516.tistory.com/entry/Pytorch-collatefn-%EC%9D%B4%EB%9E%80&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jJcHM/hyWGYi5K6k/lbBwF2YOC2jHPQL35Kqgk0/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/n4HNl/hyWCObm3IR/yk2tlnC3rGODxsB7VeTZi1/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600,https://scrap.kakaocdn.net/dn/vjBau/hyWGZI3bBQ/e65CPyYr5RkUDN9AIfbK91/img.png?width=600&amp;amp;height=600&amp;amp;face=0_0_600_600');&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;Pytorch collate_fn 이란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;DataLoader에는 여러 파라미터가 있어 필요시 적절한 파라미터를 활용해 여러 설정을 줄 수 있다. 그 중에서도 collate_fn은 variable length가 달라서, 패딩해줄 때 사용한다. from torch.utils.data import Dataset,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;seokhee0516.tistory.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>Code about AI/PyTorch</category>
      <category>batch</category>
      <category>collate_fn</category>
      <category>DataLoader</category>
      <category>dataset</category>
      <category>pytorch</category>
      <author>도비(Doby)</author>
      <guid isPermaLink="true">https://draw-code-boy.tistory.com/605</guid>
      <comments>https://draw-code-boy.tistory.com/605#entry605comment</comments>
      <pubDate>Fri, 26 Jul 2024 00:53:56 +0900</pubDate>
    </item>
  </channel>
</rss>