<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>흔하디 흔한 사람의 흔한 딥러닝 공부</title>
        <link>https://velog.io/</link>
        <description>Although. 그럼에도 불구하고.</description>
        <lastBuildDate>Sun, 11 Sep 2022 13:07:16 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>흔하디 흔한 사람의 흔한 딥러닝 공부</title>
            <url>https://velog.velcdn.com/images/jarvis_geun/profile/e6c58ccb-d79d-4a7a-bb70-4ef6d805a610/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. 흔하디 흔한 사람의 흔한 딥러닝 공부. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jarvis_geun" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Ubuntu] CUDA 설치]]></title>
            <link>https://velog.io/@jarvis_geun/Ubuntu-CUDA-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@jarvis_geun/Ubuntu-CUDA-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Sun, 11 Sep 2022 13:07:16 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-shell"># 지원되는 그래픽 드라이버 확인 ==&gt; recommended 설치 권장
ubuntu-drivers devices</code></pre>
<p><img src="https://velog.velcdn.com/images/jarvis_geun/post/9d863b19-0b20-4a72-b448-42392389135b/image.png" alt=""></p>
<pre><code class="language-shell">sudo apt install nvidia-driver-[recommended version]

reboot</code></pre>
<pre><code class="language-shell"># 그래픽 드라이버가 설치되었는지 확인
nvidia-smi</code></pre>
<p><img src="https://velog.velcdn.com/images/jarvis_geun/post/c8e1f066-66c4-4ccb-b851-295c23a88602/image.png" alt=""></p>
<p><a href="https://developer.nvidia.com/cuda-toolkit-archive">CUDA Toolkit Archive</a>에서 <code>nvidia-smi</code> 명령어를 통해 확인한 CUDA Version 설치</p>
<p><img src="https://velog.velcdn.com/images/jarvis_geun/post/463bb98d-d383-4ec0-a11b-3564526d80fd/image.png" alt=""></p>
<pre><code class="language-shell">$ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-ubuntu1804.pin
$ sudo mv cuda-ubuntu1804.pin /etc/apt/preferences.d/cuda-repository-pin-600
$ wget https://developer.download.nvidia.com/compute/cuda/11.7.1/local_installers/cuda-repo-ubuntu1804-11-7-local_11.7.1-515.65.01-1_amd64.deb
$ sudo dpkg -i cuda-repo-ubuntu1804-11-7-local_11.7.1-515.65.01-1_amd64.deb
$ sudo cp /var/cuda-repo-ubuntu1804-11-7-local/cuda-*-keyring.gpg /usr/share/keyrings/
$ sudo apt-get update
$ sudo apt-get -y install cuda</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] Decorator 및 Logging 관련 참고 사이트]]></title>
            <link>https://velog.io/@jarvis_geun/Python-Decorator-%EB%B0%8F-Logging-%EA%B4%80%EB%A0%A8-%EC%B0%B8%EA%B3%A0-%EC%82%AC%EC%9D%B4%ED%8A%B8</link>
            <guid>https://velog.io/@jarvis_geun/Python-Decorator-%EB%B0%8F-Logging-%EA%B4%80%EB%A0%A8-%EC%B0%B8%EA%B3%A0-%EC%82%AC%EC%9D%B4%ED%8A%B8</guid>
            <pubDate>Fri, 26 Aug 2022 02:00:09 GMT</pubDate>
            <description><![CDATA[<ul>
<li><a href="https://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%8D%BC%EC%8A%A4%ED%8A%B8%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%95%A8%EC%88%98-first-class-function/">퍼스트클래스 함수</a></li>
<li><a href="https://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%ED%81%B4%EB%A1%9C%EC%A0%80-closure/">클로저(Closure)</a></li>
<li><a href="https://schoolofweb.net/blog/posts/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%8D%B0%EC%BD%94%EB%A0%88%EC%9D%B4%ED%84%B0-decorator/">데코레이터(decorator)</a></li>
<li><a href="https://www.daleseo.com/python-logging/">logging</a></li>
<li><a href="https://www.daleseo.com/python-logging-config/">파이썬 로깅 설정 - logger, handler, formatter</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ubuntu] Nano editor 명령어]]></title>
            <link>https://velog.io/@jarvis_geun/Ubuntu-Nano-editor-%EB%AA%85%EB%A0%B9%EC%96%B4</link>
            <guid>https://velog.io/@jarvis_geun/Ubuntu-Nano-editor-%EB%AA%85%EB%A0%B9%EC%96%B4</guid>
            <pubDate>Mon, 01 Aug 2022 23:37:32 GMT</pubDate>
            <description><![CDATA[<ul>
<li><code>Esc + a</code> : 블록 지정, 한번 더 누르면 블록 해제</li>
<li><code>Alt + }</code> : 들여쓰기</li>
<li><code>Alt + {</code> : 들여쓰기 해제</li>
<li><code>Ctrl + o</code> : 저장</li>
<li><code>Ctrl + x</code> : 나가기</li>
<li><code>Ctrl + k</code> : 잘라내기</li>
<li><code>Ctrl + u</code> : 잘라낸 코드 붙여넣기</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ubuntu] ssh GUI 실행 관련 사이트]]></title>
            <link>https://velog.io/@jarvis_geun/Ubuntu-ssh-GUI-%EC%8B%A4%ED%96%89-%EA%B4%80%EB%A0%A8-%EC%82%AC%EC%9D%B4%ED%8A%B8</link>
            <guid>https://velog.io/@jarvis_geun/Ubuntu-ssh-GUI-%EC%8B%A4%ED%96%89-%EA%B4%80%EB%A0%A8-%EC%82%AC%EC%9D%B4%ED%8A%B8</guid>
            <pubDate>Fri, 11 Mar 2022 13:21:18 GMT</pubDate>
            <description><![CDATA[<ul>
<li><a href="https://medium.com/naver-cloud-platform/%EC%9D%B4%EB%A0%87%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EC%84%B8%EC%9A%94-ubuntu-gui-server-x11-forwarding-%EC%84%A4%EC%A0%95-%EA%B0%80%EC%9D%B4%EB%93%9C-964cf837c0e0">X forwarding</a></li>
<li><a href="https://unix.stackexchange.com/questions/193827/what-is-display-0">DISPLAY=:0</a></li>
<li><a href="https://harryp.tistory.com/669">X11 Forwarding 성공</a></li>
</ul>
<hr>
<pre><code class="language-bash">sudo vim /etc/ssh/ssh_config</code></pre>
<p>편집기(vim/vi/gedit 등)를 사용하여 <code>ssh_config</code> 파일을 열어줍니다. (혹은 <code>sshd_config</code>...?)</p>
<pre><code># X11Forward no</code></pre><p>위와 같이 주석처리 되어있는 문장을 아래와 같이 변경해줍니다.</p>
<pre><code>X11Forward yes</code></pre><p>이 후, 아래의 명령어를 입력하여 sshd를 재시작해줍니다.</p>
<pre><code>sudo service sshd restart</code></pre><p>그런다음 <code>ssh 접속</code>을 하면 됩니다. 이 때, 주의해야할 사항이 <code>ssh -X -p 포트번호 사용자명@IP주소</code>로 입력해야합니다.</p>
<pre><code class="language-bash">ssh -X -p 포트번호 사용자명@IP주소</code></pre>
<p>이 후, 파이썬 파일을 실행하면 GUI 창이 잘 출력되는 것을 확인할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ubuntu 16.04] OCR-RCNN-V2 실행 에러]]></title>
            <link>https://velog.io/@jarvis_geun/Ubuntu-16.04-OCR-RCNN-V2-%EC%8B%A4%ED%96%89-%EC%97%90%EB%9F%AC</link>
            <guid>https://velog.io/@jarvis_geun/Ubuntu-16.04-OCR-RCNN-V2-%EC%8B%A4%ED%96%89-%EC%97%90%EB%9F%AC</guid>
            <pubDate>Fri, 11 Mar 2022 11:50:47 GMT</pubDate>
            <description><![CDATA[<ul>
<li><a href="https://velog.io/@jarvis_geun/Ubuntu-16.04-OCR-RCNN-V2-%EC%8B%A4%ED%96%89">[Ubuntu 16.04] OCR-RCNN-V2 실행</a></li>
</ul>
<p>이전에 제가 작성한 글을 참고하시면 이 글을 이해하는데 조금이나마 도움이 됩니다.</p>
<hr>
<p>오늘 (3월 11일(금)) 우분투 환경에서 다시 실행시켜봤는데 이전과 똑같은 에러가 발생했다... Ubuntu 환경에서 OCR-RCNN을 실행시켰을 때 아래와 유사한 이미지가 출력된다. (캡처하는 것을 까먹어서 이전 이미지로 대체함)</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/ba47ab51-ce92-4053-93b6-8f9f5ff06282/image.png" alt=""></p>
<p>그냥 우분투를 초기화하고 이전과 동일한 방법으로 설치하면 되겠지만, 정확한 이유를 알고 싶었다. 다음에도 또 이런 결과가 출력될 것 같았기 때문이다. 이전에는 잘 실행되던 것이 갑자기 이런 이유가 궁금해졌다. 이전과 동일하게 OpenCV 버전 문제일 수도 있지만 디스플레이 상에 출력하는 과정에서 발생하는 오류인 것 같다는 생각을 하였다. 이런 생각을 바탕으로 고민해보다가 잊고 있던 사실이 있었다. 정상 출력된 이 후에 추가로 설치해주었던 것이 있었다. <code>Nvidia 그래픽 드라이버</code> 이다.</p>
<p>설마 그래픽 드라이버가 문제가 발생을 일으킬까 반신반의 했지만, 가장 가능성이 높았다. 정상 출력된 이 후, 설치한 파일이 Nvidia 그래픽 드라이버 뿐이었다. 또한, 모델은 잘 돌아가는데 디스플레이에 출력하는 과정에서 문제가 있는 것이라면 그래픽 드라이버 문제일 수 있겠다는 생각을 하였다. 따라서 아래의 명령어를 입력하여 <code>Nvidia 그래픽 드라이버를 제거</code> 해주었다.</p>
<pre><code class="language-bash">sudo apt-get purge nvidia*
sudo apt-get autoremove
sudo apt-get autoclean</code></pre>
<p>위의 명령어를 입력한 후, 제거가 완료되었는지 확인해보기 위해 <code>nvidia-smi</code> 명령어를 입력하면 제거 완료된 것을 확인할 수 있다.</p>
<p>이제 파이썬 파일을 실행시켜보았다.</p>
<pre><code class="language-python">python3 inference-visual.py</code></pre>
<p><a href="https://youtu.be/4oW33oaKMvE"><img src="https://images.velog.io/images/jarvis_geun/post/790158e2-7cad-45fa-8ea5-a65206aa79d0/image.png" alt=""></a></p>
<p>다시 정상 출력되는 것을 확인할 수 있다!</p>
<p>원인이 뭘까?</p>
<p>그래픽 드라이버를 재설치 해보았다. 물론 우분투를 초기화한 후에 처음에 설치해야하지만 혹시 몰라 드라이버를 설치해보았다.</p>
<ul>
<li><a href="https://you-should-i-will-i-am.tistory.com/2">우분투 16.04 그래픽 드라이버 설치</a></li>
</ul>
<p>그래픽 드라이버를 설치한 후 재부팅하였다. 이 후, 파이썬 파일을 실행시켜보았으나 이전과 동일한 에러가 발생하였다. 이는 나중에 우분투를 초기화한 후에 해결해야할 문제인 것 같다. 이번 글에서는 해결하지 않고 미제로 남겨놓는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Git & Github 사용법]]></title>
            <link>https://velog.io/@jarvis_geun/Github%EC%97%90-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EB%B3%80%EA%B2%BD</link>
            <guid>https://velog.io/@jarvis_geun/Github%EC%97%90-%EB%B8%8C%EB%9E%9C%EC%B9%98-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EB%B3%80%EA%B2%BD</guid>
            <pubDate>Sun, 06 Mar 2022 14:46:54 GMT</pubDate>
            <description><![CDATA[<h2 id="github-사용법">Github 사용법</h2>
<ul>
<li><a href="https://velog.io/@jarvis_geun/Github-%EC%82%AC%EC%9A%A9%EB%B2%95">Github 사용법</a></li>
</ul>
<br>

<h2 id="github에-브랜치-생성-및-변경">Github에 브랜치 생성 및 변경</h2>
<p><a href="https://for-it-study.tistory.com/53">[GitHub,Git] Git branch 생성과 변경을 GitHub에 반영하는 방법</a></p>
<pre><code class="language-bash">git clone -b perception &lt;브랜치 이름&gt; https://github.com/Jarvis-Geun/ocr-rcnn-v2.git</code></pre>
<pre><code class="language-bash">git branch &lt;생성할 브랜치 이름&gt;</code></pre>
<ul>
<li>브랜치 생성만 하고 전환하지 않을 경우 사용</li>
</ul>
<br>

<pre><code class="language-bash">git checkout -b Ubuntu &lt;생성할 브랜치 이름&gt;</code></pre>
<ul>
<li>브랜치 생성과 전환 동시에 진행할 경우 사용</li>
</ul>
<br>

<pre><code class="language-bash">git branch -d &lt;제거할 브랜치 이름&gt;</code></pre>
<ul>
<li>제거하고 싶은 브랜치가 있을 경우, 다른 브랜치로 변환한 후에 제거</li>
</ul>
<br>

<p>이후에 변경하고 싶은 파일 수정해준 후 git add와 git commit 입력</p>
<pre><code class="language-bash">git add .
git commit -m &quot;add new branch Ubuntu&quot;</code></pre>
<pre><code class="language-bash">git push origin Ubuntu &lt;추가할 브랜치 이름&gt;</code></pre>
<br>

<p>깃허브 홈페이지에 들어가서 확인해보면 브랜치가 추가된 것을 확인할 수 있다.</p>
<p><a href="https://github.com/Jarvis-Geun/ocr-rcnn-v2/tree/Ubuntu">GitHub - Jarvis-Geun/ocr-rcnn-v2 at Ubuntu</a></p>
<pre><code class="language-bash"># 브랜치 리스트 확인
git branch</code></pre>
<br>

<pre><code class="language-bash"># 사용하고 싶은 브랜치로 변경
git checkout [브랜치명]</code></pre>
<br>

<h2 id="git-submodule">Git submodule</h2>
<p>Github 레포지토리 안에 또 다른 레포지토리를 생성하고 싶을 경우, 아래의 명령어를 입력하면 된다.</p>
<pre><code class="language-shell">git submodule add [레포지토리 링크]</code></pre>
<br>

<h2 id="readme-관련-참고사이트">README 관련 참고사이트</h2>
<ul>
<li><a href="https://www.webfx.com/tools/emoji-cheat-sheet/">README 이모티콘 리스트</a></li>
<li><a href="https://velog.io/@zerra18/%EB%A6%B3%EA%BE%B8readme-%EA%BE%B8%EB%AF%B8%EA%B8%B0-%EB%B1%83%EC%A7%80%EB%8F%84-%EB%8B%AC%EA%B3%A0-%EB%B0%B0%EA%B2%BD%EB%8F%84-%EA%BE%B8%EB%A9%B0%EB%B3%B4%EC%9E%90">README 꾸미기 1</a></li>
<li><a href="https://github.com/demun/blog/blob/master/Github/104_Mastering-Markdown.md">README 꾸미기 2</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ubuntu 16.04] OCR-RCNN-V2 실행]]></title>
            <link>https://velog.io/@jarvis_geun/Ubuntu-16.04-OCR-RCNN-V2-%EC%8B%A4%ED%96%89</link>
            <guid>https://velog.io/@jarvis_geun/Ubuntu-16.04-OCR-RCNN-V2-%EC%8B%A4%ED%96%89</guid>
            <pubDate>Sun, 06 Mar 2022 14:41:24 GMT</pubDate>
            <description><![CDATA[<ul>
<li><a href="https://velog.io/@jarvis_geun/Jetson-TX2%EC%97%90%EC%84%9C-OCR-RCNN-V2-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%A4%EA%B8%B0">Jetson TX2에서 OCR-RCNN-V2 실행시키기</a></li>
<li><a href="https://github.com/zhudelong/ocr-rcnn-v2">ocr-rcnn-v2 Github</a></li>
<li><a href="https://github.com/Jarvis-Geun/ocr-rcnn-v2">folk하여 수정해준 나의 Github repository</a></li>
</ul>
<hr>
<p>X2에서 실행이 성공한 것 때문에 우분투 환경에서 실행이 안되는 것이 오히려 더 찝찝해졌다. 우분투 환경에서 실행했을 때는 코드가 문제이거나 OpenCV 버전이 문제일 것으로 생각했었다. 하지만 TX2에서 정상 출력하는 것으로 보아 코드 문제는 아닐 것으로 판단했다. 따라서 OpenCV 버전 문제일 것으로 생각했다. 알고리즘을 재현하는 과정에서 OpenCV 3.4.2 에러가 자주 발생했기 때문이다. 더군다나 출력이 되어도 버튼과 글자가 제대로 인식되지 않고 깨지는 상태로 출력되었기 때문에 OpenCV 문제일 것으로 생각했다.</p>
<pre><code class="language-bash"># 에러 발생
If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function &#39;cvShowImage&#39;</code></pre>
<p><img src="https://images.velog.io/images/jarvis_geun/post/42a441f9-04e2-4167-bae3-ba3e3fa8ea14/image.png" alt=""></p>
<p>이러한 이유로 <code>버전 충돌</code> 문제일 것이라는 전체 하에, 디버깅을 진행하였다. 우선 혹시 모르니 재부팅하여 실행해보았으나 마찬가지의 오류가 발생하여 초기화를 진행하기로 하였다. 내가 버전을 확인하여 수정할 수 있을 정도의 실력이었다면 굳이 초기화를 하지 않았겠지만, 실력이 부족하기 때문에 그냥 우분투를 밀어버리고 <code>우분투 16.04</code> 를 재설치하였다. 사실 버전을 확인하여 수정하는 시간보다 우분투를 재설치하는 시간이 덜 걸릴 것으로 판단하였다. ocr-rcnn-v2를 재현하기 위해 최근에 TX2 JetPack 재설치만 5번, Ubuntu 재설치만 6번은 족히 넘게 하였다. 이러한 이유로 나는 나를 믿고 그냥 우분투를 재설치하였다.</p>
<p>우분투를 재설치한 후 anaconda3를 설치해주었다. 이전에는 python 2.7에 억지로 끼워맞추기 위해서 anaconda2를 설치해주었는데 TX2에서 python3 버전도 잘 돌아가는 것을 확인하여, 최신버전을 그냥 설치해주었다(python 3.x 버전에서 python 2.x 버전이 어느정도 잘 돌아간다고 한다). 또한, 아래의 명령어를 입력하여 tensorflow-gpu 설치해 필요한 것들을 설치해주었다.</p>
<pre><code class="language-bash">conda create -n tf-gpu python=3.6
conda activate tf-gpu
conda install -c anaconda cudatoolkit=10.0</code></pre>
<p>ocr-rcnn-v2에서는 <code>Tensorflow==1.12</code> 버전으로 실행하였으나 TX2에서 실행해본 바로는, <code>Tensorflow==1.15</code> 버전도 잘 돌아가는 것을 확인하였다. 이러한 이유로 해당 버전을 설치해주었다.</p>
<pre><code class="language-bash">conda install -c anaconda tensorflow-gpu==1.15</code></pre>
<p>💡 참고사항</p>
<blockquote>
<p>우분투에 직접 Cuda Toolkit이나 cuDNN을 설치해주지 않아도 된다. 아나콘다를 설치하여 가상환경을 만들어준 후, 만들어준 가상환경 위에서 Cuda Toolkit을 설치해주면 된다. cuDNN의 경우, Tensorflow를 설치하면 알아서 버전에 맞게 설치된다. 다만 주의할 것은, 설치하려는 Tensorflow 버전에 맞는 Cuda Toolkit을 설치해주어야 한다.</p>
</blockquote>
<p>Tensorflow-gpu 설치를 완료하였으면 필요한 패키지들을 설치해준다.</p>
<pre><code class="language-bash">conda activate tf-gpu
conda install pillow
conda install imageio
conda install matplotlib</code></pre>
<p>기억이 나지 않아 위의 3개의 패키지만 적었는데, 이외에 추가로 필요한 패키지가 있을 수 있으니 당황하지 않고 설치해주면 된다.</p>
<p>⚠️ 주의!</p>
<blockquote>
<p>lxml 패키지도 설치해야하는지는 확실하지 않다. 깃허브에서는 설치하라고 나와있으나 설치를 안해줬던 것 같다. 해당 패키지는 먼저 설치하지 말고 추후에 에러가 발생하면 설치해주도록 하자. 크게 영향은 없을 것으로 생각하지만..</p>
</blockquote>
<pre><code class="language-bash"># font 패키지 설치 =&gt; tab 누르고 =&gt; ok 누르기
sudo apt install ttf-mscorefonts-installer</code></pre>
<pre><code class="language-bash">sudo apt install libjpeg-dev libpng12-dev libfreetype6-dev libxml2-dev libxslt1-dev</code></pre>
<p>TX2에서는 여기까지만 설치해주면 실행이 됐던 것으로 기억한다. 그래서 혹시 몰라 실행해보았다.</p>
<pre><code class="language-bash">cd ~/Desktop
git clone -b perception https://github.com/zhudelong/ocr-rcnn-v2.git
cd ~/Desktop/ocr-rcnn-v2

python3 inference-visual.py</code></pre>
<p>이전에 보았던 OpenCV 에러를 또 보게 되었다. 멘탈 바사삭...</p>
<p>그래도 혹시 몰라 이전에 해보았던 방법들을 시도해보았다.</p>
<pre><code class="language-bash"># 이전에 보았던 OpenCV 에러

dules/highgui/src/window.cpp:632: error: (-2:Unspecified error) The function is not implemented. Rebuild the library with Windows, GTK+ 2.x or Carbon support. If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function &#39;cvShowImage&#39;</code></pre>
<p>아래의 링크를 참고하여 디버깅을 시도해보았다.</p>
<ul>
<li><a href="https://light-tree.tistory.com/150">우분투 아나콘다로 opencv python 버전 설치, 에러 해결</a></li>
</ul>
<pre><code class="language-bash">sudo apt-get -y install libgtk2.0-dev
sudo apt-get -y install pkg-config
conda remove opencv
conda install --channel menpo opencv
pip install opencv-contrib-python</code></pre>
<p>이전과 마찬가지로 변하는 것은 없었다. 다시 한 번 멘탈 바사삭...</p>
<p>그럼에도 이번에는 뭔가 될 것 같은 느낌이 들었다. TX2에서 원인을 파악했기 때문에 자신있었다. 이번에는 구글에 조금 다르게 검색을 해보았다.</p>
<blockquote>
<p>If you are on Ubuntu or Debian, install libgtk2.0-dev and pkg-config, then re-run cmake or configure script in function &#39;cvShowImage&#39;</p>
</blockquote>
<p>위와 같이 검색하여 아래 링크에서 해결책을 찾았다.</p>
<ul>
<li><a href="https://stackoverflow.com/questions/42843316/how-to-include-libgtk2-0-dev-and-pkg-config-in-cmake-when-installing-opencv-on-u">How to include libgtk2.0-dev and pkg-config in cmake when installing openCV on Ubuntu 16</a></li>
</ul>
<pre><code class="language-bash">conda remove opencv
conda install -c menpo opencv
pip install --upgrade pip
pip install opencv-contrib-python</code></pre>
<pre><code class="language-bash">cd ~/Desktop/ocr-rcnn-v2
python3 inference-visual.py</code></pre>
<p><img src="https://images.velog.io/images/jarvis_geun/post/33842183-9723-4e92-9138-096e6d6992b6/image.png" alt=""></p>
<p><a href="https://youtu.be/ThqGGQXxfiA"><img src="https://images.velog.io/images/jarvis_geun/post/34c8d1ac-11e5-4b20-a596-4c610726dbbc/image.png" alt="Youtube Link"></a></p>
<ul>
<li>위의 이미지를 클릭하면 유튜브 동영상으로 이동한다. 직접 실행한 동영상을 유튜브에 올려놓았다.</li>
</ul>
<p>결과적으로 제대로된 출력 이미지를 얻을 수 있었다!! 부서져버린 나의 멘탈조각들이 다시 제자리로 돌아오는 순간이었다. 해결된 명령어를 확인해보니 pip와 OpenCV 문제이지 않았나 생각한다. TX2에서 Python 2.7 버전을 설치한 후에 ocr-rcnn-v2 깃허브에서 아래의 명령어로 패키지를 설치하였었다.</p>
<pre><code class="language-bash">pip install six mock h5py enum34 scipy numpy --user</code></pre>
<p>이 과정에서 에러가 발생하였었는데 이에 대한 해결책으로 pip의 버전을 업그레이드 혹은 pip3 명령어를 사용했었던 것을 기억한다(물론 최종적으로 이러한 방법은 나중에 또 에러를 발생시켰지만).</p>
<p>어찌됐든 <code>pip install --upgrade pip</code> 명령어를 입력한 후 OpenCV가 정상 설치된 것으로 보아, 추측컨대 Python 3.6 버전(anaconda3와 anaconda2의 차이)과 pip 그리고 OpenCV의 버전이 문제였던 것으로 생각해보았다. 이것이 확실한 이유일지는 모르겠지만 추후에 이러한 문제가 발생한다면, 다른 버전을 설치해보는 것을 고려해보는 것이 좋은 해결책이 될 수도 있겠다는 생각을 하였다.</p>
<p>알고리즘을 구현한 사람이 특정 버전에서 실험을 하였다고 하여도, 이후에 시간이 지나 이전 버전이 에러가 발생할 수 있기 때문이다. 오히려 이 후에 나온 버전이 기존의 문제를 개선시켰을 수도 있기 때문에, 버전 간의 너무 큰 차이가 아니라면 상위 버전으로 재현해보는 것도 좋지 않을까 생각한다.</p>
<p>💡 참고사항
아마 ocr-rcnn-v2 깃허브에서 직접 클론하여 실행시켜보면 <code>encoding error</code> 발생할 것이다. 이럴 경우에 아래처럼 <code>rb</code> 인자를 추가해주면 된다.</p>
<ul>
<li><a href="https://stackoverflow.com/questions/45882520/tensorflow-something-wrong-in-tf-gfile-gfile-with-utf-8-codec-cant-decod">tensorflow: Something wrong in &quot; tf.gfile.GFile&quot; with &quot;&#39;utf-8&#39; codec can&#39;t decode...&quot;</a></li>
</ul>
<pre><code class="language-python">img_np = np.asarray(PIL.Image.open(tf.gfile.GFile(img_path, &#39;rb&#39;)))</code></pre>
<blockquote>
<p>nvidia 그래픽 드라이버가 설치되어있다면 제거를 하고 실행시켜주면 된다. 아마 그래픽 드라이버와 충돌하는 것 같은데 명확한 답이 못내렸다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ubuntu] SSH 서버 무선통신]]></title>
            <link>https://velog.io/@jarvis_geun/Ubuntu-SSH-%EC%84%9C%EB%B2%84-%EB%AC%B4%EC%84%A0%ED%86%B5%EC%8B%A0</link>
            <guid>https://velog.io/@jarvis_geun/Ubuntu-SSH-%EC%84%9C%EB%B2%84-%EB%AC%B4%EC%84%A0%ED%86%B5%EC%8B%A0</guid>
            <pubDate>Sat, 05 Mar 2022 08:09:34 GMT</pubDate>
            <description><![CDATA[<h2 id="집에서-연구실-컴퓨터-무선-통신하기">집에서 연구실 컴퓨터 무선 통신하기</h2>
<p>이전에는 윈도우 환경에서 <code>크롬 원격 데스크톱</code>을 이용하여 집에서 무선으로 접속하였으나, 우분투 환경에서는 따로 프로그램을 설치하지 않아도 되는 것 같아 한 번 시도해보았다. 무엇보다 젯슨 TX2 보드에 디스플레이를 연결하지 않고도 연결할 수 있을 것으로 생각되어 정리해보았다.</p>
<br>

<h3 id="무선-공유기-설정-웹페이지-접속">무선 공유기 설정 웹페이지 접속</h3>
<ul>
<li>일반적으로 <code>192.168.0.1</code> 를 통해 접속한다.</li>
<li>tplink의 경우, <a href="http://tplinkwifi.net">tplinkwifi.net</a> 도 가능하다.</li>
</ul>
<p><img src="https://images.velog.io/images/jarvis_geun/post/ffadd513-a83d-412a-b530-13187e9beca5/image.png" alt=""></p>
<ul>
<li>192.168.01 접속 시, 403 Forbidden 에러 발생</li>
</ul>
<p><img src="https://images.velog.io/images/jarvis_geun/post/ed34eac2-fbeb-469a-bd1e-0dea7171650e/image.png" alt=""></p>
<ul>
<li><a href="http://tplinkwifi.net">tplinkwifi.net</a> 접속 시, 로그인을 해야한다. 기본 값은 <code>admin</code>이다. 하지만, 이전에 보안 때문에 아이디와 비밀번호를 변경했는데 잊어버렸다..</li>
</ul>
<p>사용자 이름과 비밀번호를 잊어버려 tplink 공유기를 새로 초기화하기로 한다.</p>
<ul>
<li><a href="https://www.tp-link.com/kr/support/faq/426/">TP-Link 무선 공유기의 로그인 암호를 잊어 버린 경우 어떻게 해야 합니까?</a></li>
</ul>
<p>❗️ 주의</p>
<blockquote>
<p>사실 TP-Link 공유기를 초기화할 필요가 없었다. 집에서 사용하는 공유기이기 때문에, DDNS나 포트포워딩을 할 때 건드릴 일이 없다. 다만, 연구실에서 사용하는 공유기의 192.168.0.1의 아이디와 비밀번호는 알아야 한다. 외부에서 연구실 컴퓨터를 원격접속할 것이기 때문에 연구실 컴퓨터에 연결된 공유기의 아이디와 비밀번호를 알아야 한다. 연구실에는 iptime의 공유기가 설치되어 있다. 결론은 집의 공유기(TP-Link)는 초기화할 필요가 없었다..</p>
</blockquote>
<p>💡 참고</p>
<blockquote>
<p>우분투 파이어폭스에서는 <code>192.168.0.1</code> 이 접속 불가능하다. 아마 별도로 파이어폭스 설정을 변경해주거나, 크롬 브라우저 등 다른 브라우저를 사용하면  접속할 수 있을 것 같다. 하지만 나의 경우, 그냥 TP-Link 페이지(<code>tplinkwifi.net</code>)에 접속하였다. 윈도우에서는 둘 다 접속 가능한 것을 확인하였다.</p>
</blockquote>
<br>

<h3 id="ip-주소-확인">ip 주소 확인</h3>
<pre><code class="language-bash">$ ifconfig</code></pre>
<p>💡 참고</p>
<blockquote>
<p>ifconfig는 “interface configuration”의 약자이다.</p>
</blockquote>
<ul>
<li><a href="https://www.whatap.io/ko/blog/11/">ifconfig에 대한 설명 1</a></li>
<li><a href="https://docs.oracle.com/cd/E38901_01/html/E38894/ipconfig-141.html">ifconfig에 대한 설명 2</a></li>
</ul>
<p>대충 읽어본 후 나의 생각은, <code>ifconfig</code> 명령어만 사용할 경우 학교 내부 ip 주소가 출력되는듯 하다. <code>192.168.xxx</code> 형태로 출력되는데 구글링을 해보았을 때, 이런 형태의 주소가 상당히 많았었던 걸로 기억한다. 이는 곧, 다른 지역에서도 중복된 ip 주소가 존재할 수 있다는 건데 이럴 경우 내가 흔히 알던 ip 주소 추적과는 거리가 멀다고 생각한다. 따라서 <code>ifconfig</code> 명령어는 공통된 (내부) 이더넷을 쓰고 있을 때 내가 몇번째 이더넷을 의미하는지를 나타내는 것일지 않을까 생각해본다.</p>
<p>예를 들면, <code>ifconfig</code> 명령어를 입력하여 <code>192.168.0.34</code> 의 <code>inet 주소</code> 를 얻었다면 이는 곧, 학교 내부 ip에서 <code>34</code> 라는 번호를 부여받은 것으로 생각할 수 있지 않을까 생각해본다. 즉, <code>192.168.0.34</code> 라는 ip 주소는 동일한 이더넷을 사용하는 범위 내에서 “<strong>유일한 내부 ip 주소</strong>”로 볼 수 있지않을까.</p>
<p>어찌됐든 외부에서 연구실 컴퓨터로 접속하기 위해서는, 내부 ip 주소가 아닌 공인 ip 주소가 필요하므로 <code>curl ifconfig.me</code> 명령어를 입력하여 주소를 획득한다.</p>
<p>이 후에는 아래의 <code>iptime DDNS 설정</code> 링크를 참고하여 진행한다.</p>
<br>

<h3 id="공인-ip-주소-확인">공인 ip 주소 확인</h3>
<pre><code class="language-bash">$ curl ifconfig.me</code></pre>
<br>

<h3 id="iptime-ddns-설정">iptime DDNS 설정</h3>
<p><a href="https://ca.ramel.be/74">[UBUNTU] 우분투 서버 SSH로 접속 세팅하기</a></p>
<p>만약에 호스트 pc가 TP-Link의 공유기를 사용하고 있다면 아래의 링크를 참고하여 외부에서 접속하면 될 것으로 생각한다. 다만, 직접 해보지는 않아서 확실하지는 않다.</p>
<br>

<h3 id="tp-link-ddns-설정">TP-Link DDNS 설정</h3>
<p><a href="https://www.tp-link.com/kr/support/faq/1367/">TP-Link 무선 공유기에서 TP-LINK DDNS를 설정하는 방법은 무엇입니까? (신규)</a></p>
<p><a href="https://www.noip.com/">Free Dynamic DNS - Managed DNS - Managed Email - Domain Registration - No-IP</a></p>
<br>

<h2 id="ubuntu-환경에서-ssh-접속하여-vscode-실행하기">Ubuntu 환경에서 SSH 접속하여 Vscode 실행하기</h2>
<ul>
<li><a href="https://mclearninglab.tistory.com/171">SSH 이용하여 우분투 Vscode 접속하기</a></li>
</ul>
<br>

<h2 id="ubuntu-환경-방화벽-설정">Ubuntu 환경 방화벽 설정</h2>
<ul>
<li><a href="https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&amp;blogId=adsl6662&amp;logNo=221175610821">Ubuntu 방화벽 확인</a></li>
</ul>
<br>

<h2 id="ubuntu-ssh-접속시-conda-command-not-found-에러">Ubuntu ssh 접속시, conda: command not found 에러</h2>
<ul>
<li><a href="https://stackoverflow.com/questions/820517/bashrc-at-ssh-login/820533#820533">.bash_profile 수정하여 ssh conda path 설정해주기</a></li>
</ul>
<p>먼저 ssh를 통해 외부에서 호스트 pc로 원격접속한다. 이 후, 아래의 명령어를 입력한다.</p>
<pre><code class="language-bash">sudo vim ~/.bash_profile</code></pre>
<p>그 다음 아래의 코드를 추가해준다.</p>
<pre><code class="language-bash">if [ -f ~/.bashrc ]; then
  . ~/.bashrc
fi</code></pre>
<p>이후에는 변경시킨 bash 파일을 <code>source</code> 해준다.</p>
<pre><code class="language-bash">source ~/.bashrc</code></pre>
<p>그러면 이제 ssh에 접속하면 자동으로 conda path를 지정해주어 정상동작하는 것을 확인할 수 있다.</p>
<br>


<h2 id="ssh-접속후-파이썬-파일-실행-시-gui-창-실행시키는-방법">ssh 접속후 파이썬 파일 실행 시, GUI 창 실행시키는 방법</h2>
<ul>
<li><a href="https://velog.io/@jarvis_geun/Ubuntu-ssh-GUI-%EC%8B%A4%ED%96%89-%EA%B4%80%EB%A0%A8-%EC%82%AC%EC%9D%B4%ED%8A%B8">[Ubuntu] ssh GUI 실행 관련 사이트</a></li>
</ul>
<br>

<h2 id="ssh-접속후-터미널-종료해도-진행중인-작업-유지하는-방법">ssh 접속후 터미널 종료해도 진행중인 작업 유지하는 방법</h2>
<ul>
<li><a href="https://ca.ramel.be/25">screen 명령어를 사용하여 ssh 터미널 작업 유지하는 방법</a></li>
</ul>
<br>

<h2 id="ssh-접속하여-tensorboard-확인하는-방법">ssh 접속하여 tensorboard 확인하는 방법</h2>
<ul>
<li><a href="https://data-newbie.tistory.com/363">ssh 접속하여 tensorboard 확인하는 방법</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Zenbook Duo 우분투 참고 사이트]]></title>
            <link>https://velog.io/@jarvis_geun/Zenbook-Duo-%EC%9A%B0%EB%B6%84%ED%88%AC-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@jarvis_geun/Zenbook-Duo-%EC%9A%B0%EB%B6%84%ED%88%AC-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Sat, 05 Mar 2022 02:47:54 GMT</pubDate>
            <description><![CDATA[<ul>
<li><a href="https://www.reddit.com/r/Ubuntu/comments/hrbadh/setup_your_screenpad_plus_and_keyboard_shortcut/">Setup your Screenpad Plus and keyboard shortcut for Asus Zenbook (Pro) Duo owners</a></li>
<li><a href="https://github.com/Plippo/asus-wmi-screenpad">젠북 듀오 우분투 환경에서 스크린패드 밝기 설정(명령어 입력)</a></li>
<li><a href="https://github.com/Plippo/screenpad-tools">젠북 듀오 우분투 환경에서 스크린 패드 밝기 설정(단축키 지정)</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Jetson TX2에서 OCR-RCNN-V2 실행시키기]]></title>
            <link>https://velog.io/@jarvis_geun/Jetson-TX2%EC%97%90%EC%84%9C-OCR-RCNN-V2-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%A4%EA%B8%B0</link>
            <guid>https://velog.io/@jarvis_geun/Jetson-TX2%EC%97%90%EC%84%9C-OCR-RCNN-V2-%EC%8B%A4%ED%96%89%EC%8B%9C%ED%82%A4%EA%B8%B0</guid>
            <pubDate>Thu, 03 Mar 2022 09:39:38 GMT</pubDate>
            <description><![CDATA[<h2 id="1-배경">1. 배경</h2>
<p>대학 졸업작품의 일환으로 다른 학생들과 함께 프로젝트를 진행하게 되었다. 공과대학 특정 건물의 엘리베이터 버튼을 인식하는 것이 그 중 일부이다. 인간이 주어진 정보와 경험을 바탕으로 무언가를 학습하듯이, 인공지능 또한 그렇다.</p>
<p>엘리베이터 버튼 사진을 바탕으로, 다른 사진이 엘리베이터 버튼인지 아닌지를 구별할 수 있게 만드는 것이 1차 목표이다. 이러한 까닭에, 프로젝트를 진행하게 위해서는 가장 먼저 라벨링이 되어있는 데이터셋 확보가 우선이었다. 처음에는 건물의 엘리베이터 사진을 여러장 촬영 후, <a href="https://github.com/tzutalin/labelImg">labelImg</a>를 사용하여 annotation을 직접 해보려고 하였다. 하지만 현실적으로 생각해보면, 주어진 시간이 얼마 없었고 지루하고 힘든 작업이 될 것으로 생각하였습니다. 속된 말로, <code>노가다</code>가 될 것이 불보듯 뻔하였다(라벨링 작업을 하는 모든 분들께 존경을 표합니다...). 그렇기 때문에 자연스럽게 엘리베이터 버튼 데이터셋이 있는지 구글링을 하게 되었다.</p>
<p>결과적으로 <a href="https://ieeexplore.ieee.org/document/9324975"><code>ocr-rcnn</code></a>이라는 주제의 논문과 <a href="https://github.com/zhudelong/ocr-rcnn-v2"><code>github code</code></a>를 찾게 되었다. 해당 github에는 수천장의 엘리베이터 버튼 데이터셋과 관련 논문이 업로드 되어있다. 이를 바탕으로 github 코드를, Ubuntu와 TX2 환경에서 실행해보기로 하였다.</p>
<p>결과부터 말하자면 2022년 03월 03일을 기준으로, 우분투 환경에서 실행시키는 것은 실패하였다. 파이썬, 텐서플로우 그리고 OpenCV 간의 버전 충돌?로 인한 에러로 생각된다(추후에 시간이 될 때 해당 문제를 해결하여 올릴 예정입니다).</p>
<p>TX2 환경에서는, 1~2주 간의 끝이 보이지 않는 디버깅 끝에 에러를 바로잡고 원하던 출력 결과를 얻을 수 있었다. 본격적으로 이러한 과정을 설명하기에 앞서, 이 글을 읽는 누군가 다음과 같은 사항을 인지해주셨으면 합니다.</p>
<blockquote>
<p>저는 해당 프로젝트를 시작하기 전까지 윈도우만 사용해왔고, 우분투 환경에서 프로그램을 실행해본 적이 없습니다. 그렇기 때문에 보는 사람에 따라, 초보 수준의 글이 될 수 있다는 점을 이해해주셨으면 합니다(변명입니다).</p>
</blockquote>
<blockquote>
<p>사실 이전에 Jetson nano는 사용해보았습니다. 그럼에도 저는 정말 부족한 수준이기 때문에 이 점 이해해주셨으면 합니다(두번째 변명).</p>
</blockquote>
<blockquote>
<p>사실상 <code>혼자서</code> 우분투와 젯슨 보드를 다루어보는 것은 이번이 처음입니다. 그렇다보니 아무래도 부족한 점이 많았을 뿐 아니라, 디버깅 또한 오래 걸릴 수 밖에 없었습니다. 그러니 이해주셨으면 합니다(마지막 변명).</p>
</blockquote>
<p>이제, 위의 터무니 없는 변명들을 뒤로 하고 본격적으로 실행과정을 설명해보려 한다.</p>
<hr>
<h2 id="2-jetpack-설치하기">2. JetPack 설치하기</h2>
<p>TX2의 환경을 설정하기까지, 정말 많은 시행착오를 겪었다. 정확히 말하면 깃허브 코드를 실행하기 위한 환경을 만들어주는 것은 기다림과 인내의 연속이었다.
그런 시행착오 과정은 다른 글에서 기록해두려 한다. 이 글에서는 오직 성공과정만을 기록한다.</p>
<br>

<h3 id="21-tx2-전원-공급-및-실행">2.1 TX2 전원 공급 및 실행</h3>
<p>TX2는 젯슨 나노와 다른 전원 어댑터를 사용한다. 젯슨 나노 2GB와 4GB가 다른 전압과 전류로 공급을 받듯이, TX2 역시 마찬가지이다. 각 기기마다 필요로 하는 전원 규격이 정해져있기 때문에 검색해서 알아봐야 한다.</p>
<ul>
<li><a href="https://forums.developer.nvidia.com/t/replacement-power-brick/66954#5294538">TX2 전원 어댑터 규격</a></li>
</ul>
<pre><code class="language-text">Input: 100-240V~, 50/60Hz, 1.3A Output: 19V --- 4.74A, 90W max</code></pre>
<p>엔비디아 포럼에서 확인한 바로는, TX2는 <code>19V, 4.74A</code>의 전원을 필요로 한다. TX2의 전원 어댑터가 없어 추가로 구매해주었다. 구매 링크는 아래에 첨부한다.</p>
<ul>
<li><a href="https://www.devicemart.co.kr/goods/view?no=1105672">[디바이스마트] 110 ~ 240V / 19V 4.74A / 내경 2.1 ~ 2.5mm / 외경 5.5mm</a></li>
<li><a href="https://www.devicemart.co.kr/goods/view?no=1340916">[디바이스마트] 전원 파워케이블</a></li>
<li><a href="https://www.devicemart.co.kr/goods/view?no=10991068">[디바이스마트] ipTIME USB 허브</a></li>
</ul>
<p>USB 허브의 경우는 반드시 필요한 것은 아니지만, 구글링을 해보면 대부분 구매하는 것을 추천한다고 한다. USB 포트가 단 한개 밖에 없기 때문이다.</p>
<p>구매한 전원 어댑터를 사용하여 TX2를 실행시켜주었다. 전원이 켜진 후 기다리면 우분투가 설치되어 있는 것을 확인할 수 있다. 젯슨 나노는 별도로 TF 카드를 구워주어야 했었는데(flash), TX2는 구워주지 않아도 알아서 우분투를 설치해주는 것 같다.</p>
<p>하지만 TX2의 성능을 온전히 사용하려면 젯슨 나노처럼 JetPack을 설치하는 과정이 필수적이다.</p>
<br>

<h3 id="22-tx2에-jetpack-설치">2.2 TX2에 JetPack 설치</h3>
<p><a href="https://github.com/zhudelong/ocr-rcnn-v2">ocr-rcnn-v2 github</a>에서 필요로 하는 버전은 아래와 같다.</p>
<ul>
<li>JetPack 4.2</li>
<li>Tensorflow == 1.12.0</li>
<li>Python == 2.7</li>
</ul>
<p>TX2의 경우, JetPack을 설치하기 위해서는 호스트 PC에 엔비디아에서 제공하는 SDK Manager라는 프로그램을 설치해주어야 한다. SDK Manager는 호스트 PC가 <code>우분투 16.04</code> 혹은 <code>우분투 18.04</code> 버전인 경우에만 설치가 가능하다고 한다.</p>
<ul>
<li><a href="https://docs.nvidia.com/sdk-manager/system-requirements/index.html">[NVIDIA] System Requirements</a></li>
</ul>
<p>노트북(Zenbook Duo UX481FL)에 듀얼부팅을 하여 우분투 18.04를 설치해주었다. 내 노트북의 경우, 듀얼 모니터가 있는 조금 특이한 노트북이어서 그런지 몰라도 듀얼부팅 하는 과정에서 우분투 설치가 되지 않았다. 이에 대한 정보는 <a href="">젠북 듀오 듀얼부팅</a>을 참고한다.</p>
<p>호스트 PC에 우분투 16.04 혹은 18.04를 설치하였다면 이제 JetPack 4.2를 설치해야한다. 하지만 JetPack 4.2 버전을 설치하려고 시도해보았는데 설치가 불가능하였다. 엔비디아 측에서 서버를 내린건지 정확한 이유는 모르겠지만 아래 사진처럼 클릭조차 할 수 없게 되어있다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/d8acceff-5c74-4b2d-ba36-fc4628aa5fc9/image.png" alt=""></p>
<p>꽤 오랫동안 JetPack 4.2 버전을 설치해보려 시도해보았지만 결국에는 포기하고 최신버전인 JetPack 4.6 버전을 설치하였다(JetPack 설치만 5번 이상은 한 것 같네요). 아래의 링크를 참고하여 설치하면 된다. 설치하는 과정에서 약간의 차이(ex. Recovery mode 진입 등)가 있을 수 있지만 전체적인 과정은 링크와 같다는 것을 기억하도록 한다.</p>
<ul>
<li><a href="https://developer.nvidia.com/embedded/jetpack-archive">[NVIDIA] SDK Manager Archive</a></li>
<li><a href="https://mickael-k.tistory.com/87#recentComments">TX2에 JetPack 설치하기</a></li>
</ul>
<p>JetPack 4.6 설치를 완료했다면 이제 Tensorflow와 다른 패키지들을 설치해야 한다.</p>
<br>

<h2 id="3-tx2에-tensorflow-설치하기">3. TX2에 Tensorflow 설치하기</h2>
<p>이 부분에서 정말 많은 시행착오를 겪었다. 처음에는 <a href="https://github.com/zhudelong/ocr-rcnn-v2#:~:text=For%20Nvidia%20TX%2D2%20platform">ocr-rcnn-v2 Github</a>에서 제공하는 과정대로 텐서플로우를 설치하려고 하였다. 하지만 몇 개의 패키지 버전이 맞지 않는 에러가 지속적으로 발생하여, 터미널창에 <code>pip 명령어</code>를 사용하여 설치하는 방법은 포기하였다. 대신에 Nvidia에서 제공하는 Tensorflow를 설치하였다. Nvidia에서 제공하는 Tensorflow의 경우, Jetson 계열 보드에 최적화되어 있을 뿐 아니라 원한다면 가상환경을 만들 수도 있어 설치 및 제거에 용이하다는 장점이 있다. 또한 지금까지의 내 경험 상, pip 명령어를 통해 직접 설치하는 방법보다는 가상환경을 만들어 설치하는 것이 여러 측면에서 우위를 가진다고 볼 수 있다.</p>
<p>가상환경을 만들어 설치하면 텐서플로우에 맞는 여러 패키지들이 자동으로 설치될 뿐 아니라, 버전 에러가 발생하면 간단하게 지워버리고 다른 버전을 설치할 수 있다. 이 덕분에 설치시간이 단축된다는 장점도 있다.</p>
<p>TX2에 텐서플로우를 설치하는 과정은 아래를 참고한다.</p>
<ul>
<li><a href="https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform/index.html#benefits">Nvidia Deep Learning Frameworks Documentation</a></li>
</ul>
<p>위의 링크를 바탕으로 설치과정을 정리해보았다.</p>
<hr>
<ol>
<li><p>JetPack을 설치한다. 나의 경우, 이미 설치하였기 때문에 넘어간다.</p>
</li>
<li><p>텐서플로우 설치에 필요한 <code>system package</code>를 설치한다.</p>
<pre><code class="language-bash">$ sudo apt-get update
$ sudo apt-get install libhdf5-serial-dev hdf5-tools libhdf5-dev zlib1g-dev zip libjpeg8-dev liblapack-dev libblas-dev gfortran</code></pre>
</li>
<li><p><code>pip3</code>를 업그레이드 한다.</p>
</li>
</ol>
<pre><code class="language-bash">$ sudo apt-get install python3-pip
$ sudo pip3 install -U pip testresources setuptools==49.6.0 </code></pre>
<ol start="4">
<li><p><code>Python package dependencies</code>를 설치한다.</p>
<pre><code class="language-bash">$ sudo pip3 install -U --no-deps numpy==1.19.4 future==0.18.2 mock==3.0.5 keras_preprocessing==1.1.2 keras_applications==1.0.8 gast==0.4.0 protobuf pybind11 cython pkgconfig
$ sudo env H5PY_SETUP_REQUIRES=0 pip3 install -U h5py==3.1.0</code></pre>
</li>
<li><p><code>pip3</code> 명령어를 사용하여 텐서플로우를 설치한다. 아래 명령어의 경우 최신버전인 JetPack 4.6에 호환되는 텐서플로우를 설치하는 명령어이다.</p>
</li>
</ol>
<pre><code class="language-bash">$ sudo pip3 install --pre --extra-index-url https://developer.download.nvidia.com/compute/redist/jp/v46 &#39;tensorflow&lt;2&#39;</code></pre>
<hr>
<p>위의 과정을 완료하였다면 Tensorflow 설치를 완료한 것이다. Tensorflow가 설치되었는지 확인하기 위해 아래의 명령어를 입력한다.</p>
<pre><code class="language-bash">print(tensorflow.__version__)</code></pre>
<pre><code># 출력결과
1.15.5</code></pre><p>Nvidia에서 제공하는 Tensorflow 1.x 버전 중 최신버전인, <code>1.15.5</code> 버전이 정상 설치된 것을 확인할 수 있다.</p>
<p>이제 실행만 하면 끝이다. 참고로 Jetson 보드에 <code>Tensorflow의 여러 버전</code>을 설치하고 싶다면 <a href="https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform/index.html#:~:text=gpu%3D%3D1.13.1%2Bnv19.3-,3.1.%C2%A0Installing%20Multiple%20TensorFlow%20Versions,-If%20you%20want">링크</a>를 참고한다. 앞서 말한 <code>가상환경</code>을 이용한 설치방법이다.</p>
<br>

<h2 id="4-ocr-rcnn-v2-실행하기">4. OCR-RCNN-V2 실행하기</h2>
<p>실행결과는 아래와 같다. OCR-RCNN-V2 Github에서 요구하는대로 <code>model</code>을 다운받아 위치를 변경해주었다. 수정한 Github 코드는 <a href="https://github.com/Jarvis-Geun/ocr-rcnn-v2">내 Github</a>를 참고하면 된다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/37b567ce-115a-41c2-bdf9-dec38170cb91/image.png" alt=""></p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/d8948f2c-6f79-4806-a757-cb93e0bb1c0d/image.png" alt=""></p>
<hr>
<h2 id="5-느낀점">5. 느낀점</h2>
<p>JetPack 상위버전을 설치하면 아마 하위 Tensorflow 버전도 설치가 가능할 것으로 추측했었는데, 얼추 맞는듯 하다. 원래는 JetPack 4.6 버전에 맞는 Tensorflow 버전은 2.5 ~ 2.7인데, Tensorflow 설치하는 과정에서 의도적으로 <code>tensorflow&lt;2</code> 인자를 입력해주었다. 그러면 자동으로 1.x 버전 중 가장 상위 버전인 <code>1.15.5</code> 버전이 설치된다. <a href="https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform/index.html#:~:text=redist/jp/v46%20%27-,tensorflow%3C2,-%27">링크 참고</a></p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/67973bff-fa9e-4db4-9dca-562ef9e06293/image.png" alt=""></p>
<p>사실 ocr-rcnn-v2 깃허브에서 요구하는 사항은 <code>Tensorflow 1.12.0</code> 버전이지만 1.12.0 과 1.15.5 버전은 큰 차이가 없을 것으로 생각되어 그냥 설치했다.</p>
<p>파이썬 버전도 마찬가지이다. 원래는 2.7 버전이 권장사항이지만 파이썬 2.x 버전을 설치하려면 pip install 문제가 발생하여 어쩔 수 없이 <code>Python 3</code> 버전을 설치하였다. <code>pip install 이 아닌 pip3 install</code> 을 사용하여 설치하니 문제가 발생하지 않았다.</p>
<p>Jetpack 4.6을 다시 flash 한 후에 바로 <a href="https://github.com/zhudelong/ocr-rcnn-v2#:~:text=to%20try%20our-,wheel,-%2C%20just%20ignore%20the">wheel 파일</a>을 사용하여 설치해보려다가 그냥 명령어를 입력하여 설치하였다. wheel 파일은 어차피 Tensorflow 1.12.0 버전으로 설정되어있어 충돌이 발생할 것으로 생각되어 그냥 시도를 하지않았다. 따라서 명령어 한 문장씩 입력해가며 필요한 패키지와 파일을 설치하였다. 명령어는 아래와 같다.</p>
<pre><code class="language-bash">sudo nvpmodel -m 0
sudo apt-get install openjdk-8-jdk
sudo apt-get install libhdf5-dev libblas-dev gfortran
sudo apt-get install libfreetype6-dev libpng-dev pkg-config
pip install six mock h5py enum34 scipy numpy --user</code></pre>
<p>위에서 마지막 명령어 <code>pip install</code> 부분을 입력한 뒤 이전과 동일한 <code>h5py error</code> 설치 문제가 발생하였다. 실행이 성공적으로 된 후, 지금와서 돌이켜보면 확실히 버전문제였던 것 같다. pip와 pip3, Tensorflow 1.12.0 설치 중 발생하는 에러 등 종합적으로 생각해보았을 때, 논문의 저자와 지금의 내가 실행시키려는 환경은 많이 달랐다. 2022년에 살고있는 현재의 나는, 파이썬 3.x 버전과 텐서플로우 2.x 버전을 보고있지만, 과거의 논문 저자는 파이썬 2.7 버전과 텐서플로우 1.x 버전이 최신 버전이었을 것이다. 과거에는 발생하지 않던 문제들이, 시간이 지나 버전이 업그레이드 되면서 문제를 발생시킨듯 하다.</p>
<p>리눅스를 정말 잘 다룰줄 아는 사람은 이러한 문제를 해결할 수 있겠지만 나의 경우는 그렇지 않다. 사실상 리눅스를 제대로 다뤄보기 시작한 것은 이번이 처음이다(사실 겉핥기 수준이지만). 그렇기에 나는 지금으로써 내가 할 수 있는 최선이 방법이 무엇인지 생각해보았다. 설치된 패키지와 파일을 하나하나 대조해가며 버전에 맞추거나 발생한 에러를 보고 코드를 뜯어고치는 것은 지금의 나는 역부족이었다. 결국 가장 친숙한 아나콘다를 떠올렸다. 아나콘다는 동일한 컴퓨터에 가상환경이라는 것을 만들어 필요하거나 필요없을 때 언제든지 추가 및 제거할 수 있는, 신이 내린 선물이었다. 추가 및 제거하는 과정에서 다른 환경에 영향을 주지않는다는 것이 가장 큰 장점이다.</p>
<p>결국 나는 구글에서 Jetson TX2에서 아나콘다를 설치할 수 있는지를 찾아보게 되었고 실망스럽게도 불가능하였다(<a href="https://forums.developer.nvidia.com/t/anaconda-or-miniconda-on-jetson-tx2/49073">링크 참고</a>). 하지만 이 과정에서 뜻밖의 정보를 접하게 되었다. ocr-rcnn-v2 깃허브에서처럼 Tensorflow를 설치하는 방법도 있지만, Nvidia에서 제공하는 <code>Tensorflow on Jetson Platform</code> 의 방법도 있었다(<a href="https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform/index.html#install_multiple_versions_tensorflow">링크 참고</a>). 이전부터 계속 문제를 일으켰던 부분이 Tensorflow를 설치하는 과정이었는데 여기서 문득 이런 생각을 하게 되었다.</p>
<blockquote>
<p>꼭 ocr-rcnn-v2 깃허브의 설치과정을 따라가야만 하는걸까?</p>
</blockquote>
<p>이전에도 설명했듯이 과거의 논문저자와 지금의 나는 서로 다른 시간에서 실험을 한다. 그렇기에 실험환경에는 차이가 발생할 수 밖에 없으며 에러가 발생한다면 다른 길로 가는 것도 나쁘지 않을 것으로 판단했다. 이러한 생각을 한 후 곧장 Nvidia에서 제공하는 방법으로 설치하였다. 그 결과 설치를 성공하였으며 연구실 pc에서 발생하던 문제들은 단 하나도 발생하지 않고 결과가 아주 잘 출력되는 것을 확인할 수 있었다.</p>
<p>그리고 <code>Tensorflow on Jetson Platform</code> 을 따라가면서 새로 알게된 사실이 있다. Jetson board에서도 아나콘다처럼 가상환경을 만들 수 있다는 것이었다. <a href="https://docs.nvidia.com/deeplearning/frameworks/install-tf-jetson-platform/index.html#:~:text=gpu%3D%3D1.13.1%2Bnv19.3-,3.1.%C2%A0Installing%20Multiple%20TensorFlow%20Versions,-If%20you%20want">3.1. Installing Multiple Tensorflow Versions</a> 에 가면 이러한 방법이 자세히 나와있다. 사실 이 방법 말고도 리눅스 환경에서 가상환경을 만드는 방법이 있는 것 같은데, 나에게 가장 쉬운 방법은 Nvidia에서 제공하는 과정이었다. 이외의 방법은 <a href="https://virtualenv.pypa.io/en/stable/">링크 1,</a> <a href="https://docs.python.org/3/tutorial/venv.html">링크 2</a>를 따라해보는 것도 좋을 것 같다. 다만, 직접 해보지는 않아서 결과는 보장할 수 없다...</p>
<ul>
<li><a href="https://github.com/zhudelong/ocr-rcnn-v2">ocr-rcnn-v2 github 링크</a></li>
<li><a href="https://ieeexplore.ieee.org/document/9324975">OCR-RCNN 논문 링크</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Ubuntu] 참고사이트]]></title>
            <link>https://velog.io/@jarvis_geun/Ubuntu-%EC%B0%B8%EA%B3%A0%EC%82%AC%EC%9D%B4%ED%8A%B8</link>
            <guid>https://velog.io/@jarvis_geun/Ubuntu-%EC%B0%B8%EA%B3%A0%EC%82%AC%EC%9D%B4%ED%8A%B8</guid>
            <pubDate>Thu, 03 Mar 2022 08:32:57 GMT</pubDate>
            <description><![CDATA[<h3 id="20220303thu-업데이트">2022.03.03(THU) 업데이트</h3>
<ul>
<li><a href="https://ca.ramel.be/74">[UBUNTU] 우분투 서버 SSH로 접속 세팅하기</a></li>
<li><a href="https://github.com/bulletmark/libinput-gestures">[Libinput-Gestures] 노트북 터치패드 제스처 설정</a></li>
<li><a href="https://velog.io/@legendre13/Ubuntu-%EB%8B%A8%EC%B6%95%ED%82%A4">Ubuntu 단축키</a></li>
<li><a href="https://coding-groot.tistory.com/90">apt vs apt-get 차이점</a></li>
</ul>
<h3 id="20220304fri-업데이트">2022.03.04(FRI) 업데이트</h3>
<ul>
<li><a href="https://www.reddit.com/r/firefox/comments/fzf77z/how_can_i_change_the_touchpad_scroll_sensitivity/">파이어폭스 터치패드 스크롤 속도 제어</a>
<code>mousewheel.default.delta_multiplier_y</code></li>
</ul>
<h3 id="20220305sat-업데이트">2022.03.05(SAT) 업데이트</h3>
<ul>
<li><a href="https://kasterra.github.io/fixing-audio-issue-in-ubuntu/">우분투에서 소리가 출력되지 않을 시</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Vim editor 설치 및 설정]]></title>
            <link>https://velog.io/@jarvis_geun/Vim-editor-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@jarvis_geun/Vim-editor-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 03 Mar 2022 08:16:58 GMT</pubDate>
            <description><![CDATA[<h2 id="1-vim-에디터-설치">1. Vim 에디터 설치</h2>
<p>터미널 창에 아래의 명령어를 입력하여 vim 에디터 설치</p>
<pre><code class="language-bash">sudo apt-get install vim</code></pre>
<br>


<h2 id="2-vim-에디터-환경-설정">2. Vim 에디터 환경 설정</h2>
<p>아래의 명령어를 입력하여 vim 에디터의 설정을 변경해줄 수 있다.</p>
<pre><code class="language-bash">vim ~/.vimrc</code></pre>
<p>위의 명령어를 통해 vim 에디터의 여러가지 설정을 변경해줄 수 있다. 예를 들면, vim 에디터에서 tab 키는 기본(default) 값으로 공백(space) 8칸이 설정되어 있는데 이를 공백 4칸으로 설정해줄 수 있다. 또한 코드가 몇번째 줄인지 알 수 있게 줄 번호를 설정해줄수도 있다. 이외에도 여러가지 기능이 있으며 vimrc 파일에서 코드만 간단하게 설정해주면 이러한 것들을 커스텀 할 수도 있다. vim 에디터를 설정하는 방법을 간단하게 알아보도록 한다.</p>
<h3 id="syntax-highlighting">Syntax Highlighting</h3>
<p>Vscode나 Pycharm을 써보면 기본적으로 각 프로그래밍 언어에 대해 여러가지 색깔로 코드를 구성한 것을 확인할 수 있다. 이는 프로그래밍 언어마다 문법에 따라 가독성을 위해, 프로그램에서 제공해주는 기능이다. vim 에디터 역시 파이썬 문법에 맞게 색깔이 자동으로 변하게 지정해줄 수 있다. 설정 코드는 아래와 같다.</p>
<pre><code class="language-python">if has (&quot;syntax&quot;)
    syntax on
endif</code></pre>
<p>위의 코드를 새로 만든 <code>.vimrc</code>  코드에 입력해주면 된다. 이 이야기와는 별개로, vim 에디터의 주석처리는 <code>&quot;</code> 을 사용하면 된다. 파이썬의 <code>#</code> 와 같은 역할이다. 예를 들면 아래와 같다.</p>
<pre><code class="language-python">&quot;Syntax Highlighing
if has (&quot;syntax&quot;)
    syntax on
endif</code></pre>
<h3 id="auto-indent">Auto Indent</h3>
<p>말그대로 자동 들여쓰기 기능을 설정해주는 방법에 대해 설명하려 한다. vim 에디터는 기본적으로 자동 들여쓰기가 설정되어 있지않다. 파이썬에 비교해보자면, 반복문이나 조건문을 사용할 경우 해당 블록 안에서 줄바꿈을 하면 그에 맞게 자동으로 들여쓰기가 된다. 그러나 vim 에디터의 경우 줄바꿈을 해도 커서가 맨 처음 부분에 위치하게 된다. 자동 들여쓰기를 설정하기 위해 <code>.vimrc</code> 파일에 아래의 코드를 추가해준다.</p>
<pre><code class="language-python">set autoindent
set cindent</code></pre>
<h3 id="set-number">Set number</h3>
<p>디버깅을 하는데 있어 정말 중요한 기능이라고 생각합니다. 일반적으로 에러가 발생하면 어느 파일의 몇번째 줄에 문제가 있는지를 알려주는데 vim 에디터는 기본적으로 줄 번호를 표시해주지 않습니다. 이것 또한 코드를 추가하여 설정해주어야 합니다. 코드는 아래와 같습니다.</p>
<pre><code class="language-python">set nu

또는

set number</code></pre>
<p>이외에도 다양한 기능들을 추가할 수 있으며, 별도의 설명은 하지 않고 간단히 코드만 추가해놓도록 하겠습니다.</p>
<h3 id="set-tab-indent">Set Tab Indent</h3>
<pre><code class="language-python">set smartindent   &quot;줄바꿈 시, 이전 문장의 indent에서 새로 시작
set tabstop=4     &quot;또는 set ts=4 (tab 너비)
set expandtab     &quot;tab안에 space 채우기
set shiftwidth=4  &quot;자동 들여쓰기(auto indent)할 때의 너비</code></pre>
<h3 id="locate-cursor-where-ive-modified-most-recently">Locate cursor where I&#39;ve modified most recently</h3>
<pre><code class="language-python">&quot;가장 최근에 수정한 곳에 커서 위치
au BufReadPost *
\ if line(&quot;&#39;\&quot;&quot;) &gt; 0 &amp;&amp; line(&quot;&#39;\&quot;&quot;) &lt;= line(&quot;$&quot;) |
\ exe &quot;norm g`\&quot;&quot; |
\ endif</code></pre>
<h3 id="present-location-of-cursor-with-line--column">Present location of cursor with “line : column”</h3>
<pre><code class="language-python">set laststatus=2 &quot;상태바 상시 출력
set statusline=\ %&lt;%l:%v\ [%P]%=%a\ %h%m%r\ %F\</code></pre>
<h3 id="total-codes">Total codes</h3>
<pre><code class="language-python">&quot;Syntax Highligthing
if has (&quot;syntax&quot;)
    syntax on
endif

set autoindent
set cindent      &quot;C언어 자동 들여쓰기
set nu

set smartindent
set tabstop=4
set expandtab
set shiftwidth=4

&quot;가장 최근에 수정한 곳에 커서 위치
au BufReadPost *
\ if line(&quot;&#39;\&quot;&quot;) &gt; 0 &amp;&amp; line(&quot;&#39;\&quot;&quot;) &lt;= line(&quot;$&quot;) |
\ exe &quot;norm g`\&quot;&quot; |
\ endif

set laststatus=2 &quot;상태바 상시 출력
set statusline=\ %&lt;%l:%v\ [%P]%=%a\ %h%m%r\ %F\</code></pre>
<hr>
<h3 id="참고자료">참고자료</h3>
<ul>
<li><p><a href="https://medium.com/sunhyoups-story/vim-%EC%97%90%EB%94%94%ED%84%B0-%EC%9D%B4%EC%81%98%EA%B2%8C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0-5b6b8d546017">vim 에디터 이쁘게 사용하기</a></p>
</li>
<li><p><a href="https://www.enqdeq.net/232">.vimrc 설정하기 (set)</a></p>
</li>
</ul>
<h3 id="함께-보면-좋을만한-사이트">함께 보면 좋을만한 사이트</h3>
<ul>
<li><a href="https://mamu2830.blogspot.com/2019/09/vi-vim-gedit.html">리눅스 vi, vim, gedit 차이점 매우 쉽게!</a></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[(Jetson-inference) Locating Objects with DetectNet 실습]]></title>
            <link>https://velog.io/@jarvis_geun/Jetson-inference-Locating-Objects-with-DetectNet-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@jarvis_geun/Jetson-inference-Locating-Objects-with-DetectNet-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Fri, 07 Jan 2022 06:02:00 GMT</pubDate>
            <description><![CDATA[<p>본 글은 <a href="https://github.com/dusty-nv/jetson-inference#hello-ai-world">jetson-inference</a>를 바탕으로 정리한 글입니다. 영어를 한글로 번역하는(이해하는) 과정에서 오류가 있을 수 있으며, 해당 글은 Python만을 다루므로 참고해주시기 바랍니다.</p>
<hr>
<h1 id="inference">Inference</h1>
<h2 id="2-locating-objects-with-detectnet">2. Locating Objects with DetectNet</h2>
<p>이전의 <a href="https://velog.io/@jarvis_geun/Jetson-inference-%EA%B3%B5%EB%B6%80">Classifying Images with ImageNet</a> 글에서는 이미지 전체에 대해 분류를 하였습니다. 하지만 이번 글에서는 <a href="https://www.secmem.org/blog/2021/06/20/Object_Detection/#:~:text=vision%20%2C%20deep-learning-,Object%20Detection,-Computer%20Vision(%EC%BB%B4%ED%93%A8%ED%84%B0)">Object Detection</a>을 실습해보도록 하겠습니다.</p>
<ul>
<li><a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/detectnet-console-2.md#detecting-objects-from-the-command-line">Locating Objects with DetectNet</a></li>
</ul>
<h3 id="21-detecting-objects-from-images">2.1 Detecting Objects from Images</h3>
<p>이미지 분류와 유사하게, 객체 탐지를 해보겠습니다. 먼저 아래의 명령어를 입력해줍니다.</p>
<pre><code>$ cd ~/jetson-inference/build/aarch64/bin    # 현재 위치가 해당 디렉토리에 있다면 생략해도 됩니다
$ ./detectnet.py --network=ssd-mobilenet-v2 images/peds_0.jpg images/test/output.jpg</code></pre><p>위의 코드에서 --network 이후로는 필요에 따라 선택해서 사용하시면 됩니다. Default로 사용하고 싶으시면 그냥 <code>./detectnet.py</code>만 입력하면 됩니다.</p>
<p>입력 이미지는 아래와 같습니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/b718b65e-7db8-44b0-af91-37cc07f0d042/peds_0.jpg" alt=""></p>
<p>출력 이미지는 아래와 같습니다.
<img src="https://images.velog.io/images/jarvis_geun/post/edd5a710-6c50-4b06-84ac-7e1702dc3ef3/output.jpg" alt=""></p>
<p>결과적으로 네명의 사람을 한 이미지 내에서 분류할 수 있으며 이를 확률적으로 나타낸 것이 이전의 image classification과, object detection의 차이라고 볼 수 있습니다.</p>
<p>이외에도 다른 여러 이미지에 대해 object detection을 수행하고 싶다면, 아래의 명령어와 유사한 방식으로 입력해주면 됩니다.</p>
<pre><code>$ ./detectnet.py &quot;images/peds_*.jpg&quot; images/test/peds_output_%i.jpg&quot;    # detectnet.py가 있는 디렉토리에서 실행</code></pre><p><a href="https://www.pinterest.co.kr/pin/611926668099900577/"><img src="https://i.pinimg.com/originals/84/a5/0e/84a50e917c35c978e9220c5c6928d841.png" alt="cat dog image" title="cat dog" alt="cat dog image"></a></p>
<p>실습을 하다 궁금하여 구글에서 <code>캣독</code> 이미지를 입력으로 넣어보았습니다. 결과는 아래와 같습니다.</p>
<img src="https://images.velog.io/images/jarvis_geun/post/62c216ac-2760-45e2-ae24-df2fea8c9cf9/cat_dog_output.png" title="cat dog object detection" alt="cat dog output">

<p>혹시나 했지만 cat이나 dog이 아닌 kite(연)으로 인식을 했습니다. 연이라니...</p>
<p>이외에도 자신이 가지고 있는 이미지로 실습해보는 것도 기억에 남을 것 같습니다.</p>
<h3 id="processing-video-files">Processing Video Files</h3>
<p>이번에는 이미지가 아닌 비디오 파일에 대해 Object detection을 실습해보도록 하겠습니다.</p>
<p>비디오 파일은 <code>/usr/share/visionworks/sources/data/</code> 경로에 저장되어있는 영상을 사용하도록 하겠습니다.</p>
<pre><code>$ ./detectnet.py /usr/share/visionworks/sources/data/pedestrians.mp4 images/test/pedestrians_ssd.mp4</code></pre><p>명령어를 입력하면 저의 경우, 에러가 뜹니다. 정확한 이유는 모르겠으나 젯슨 나노(2GB)의 성능 때문인 것으로 생각됩니다(혹은 mp4 파일이라...?). 추후에 젯슨 나노(4GB)로 다시 해봐야겠습니다.</p>
<p>혹시 몰라 다른 비디오 파일로 실습해보도록 하겠습니다.</p>
<pre><code>$ ./detectnet.py /usr/share/visionworks/sources/data/parking.avi images/test/parking_ssd.avi</code></pre><p>해당 비디오 파일의 경우 정상적으로 인식이 되는 것을 확인하였습니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/34d9df6d-b1de-4754-80c0-dc73b5e6f156/image.png" alt=""></p>
<ul>
<li><a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/detectnet-console-2.md#:~:text=jpg%20output.jpg-,Source%20Code,-For%20reference%2C%20below">detectnet.py Source Code</a></li>
</ul>
<br>


<h3 id="22-running-the-live-camera-detection-demo">2.2 Running the Live Camera Detection Demo</h3>
<ul>
<li><a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/detectnet-camera-2.md">Running the Live Camera Detection Demo</a></li>
</ul>
<hr>
<h3 id="reference">Reference</h3>
<ul>
<li><a href="https://github.com/dusty-nv/jetson-inference#hello-ai-world">jetson-inference</a></li>
</ul>
<h3 id="참고할만한-사이트">참고할만한 사이트</h3>
<ul>
<li><a href="https://www.mincasurong.com/teaching-experience/jetson-nano-tutorial/1-jetson-nano-%EC%A4%80%EB%B9%84">젯슨 나노 원격접속</a></li>
</ul>
<h3 id="추가로-알고싶은-내용">추가로 알고싶은 내용</h3>
<ul>
<li>Docker</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Jetson-inference] Classifying Images with ImageNet 실습]]></title>
            <link>https://velog.io/@jarvis_geun/Jetson-inference-%EA%B3%B5%EB%B6%80</link>
            <guid>https://velog.io/@jarvis_geun/Jetson-inference-%EA%B3%B5%EB%B6%80</guid>
            <pubDate>Thu, 06 Jan 2022 06:18:25 GMT</pubDate>
            <description><![CDATA[<p>본 글은 <a href="https://github.com/dusty-nv/jetson-inference#hello-ai-world">jetson-inference</a>를 바탕으로 정리한 글입니다. 영어를 한글로 번역하는(이해하는) 과정에서 오류가 있을 수 있으니 참고해주시기 바랍니다. 또한, 해당 글은 Python만을 다루므로 참고해주시기 바랍니다.</p>
<hr>
<p>먼저 jetson nano의 시스템 설정(Jetpack 설치 등)을 해주어야 합니다. 관련 <a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/jetpack-setup-2.md">링크</a>에서 기기의 스펙에 따라 참고하여 설치해주면 됩니다. 추가로 <a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/building-repo-2.md">Building the Project from Source</a>에서 필요한 파일을 설치해주면 됩니다. 이에 대한 자세한 설명은 아래와 같습니다.</p>
<h3 id="building-the-project-from-source">Building the Project from Source</h3>
<ul>
<li>원하는 위치에 jetson-inference 파일을 clone 해줍니다. 저의 경우, 바탕화면에 github라는 폴더를 생성한 후 그곳에 clone을 해주었습니다. 터미널 창을 열어 아래의 명령어를 차례대로 입력해줍니다.</li>
</ul>
<pre><code>$ sudo apt-get update
$ sudo apt-get install git cmake
$ git clone --recursive https://github.com/dusty-nv/jetson-inference
$ cd jetson-inference
$ sudo apt-get install libpython3-dev python3-numpy</code></pre><p>위의 명령어 입력을 완료했다면 다음은 아래의 명령어를 입력해줍니다. 각각의 명령어가 무엇을 의미하는지 궁금하다면 <a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/building-repo-2.md">링크</a>를 참고해주시기 바랍니다.</p>
<pre><code>$ cd jetson-inference    # 만약에 jetson-inference 폴더에 이미 위치해있다면 생략합니다
$ mkdir build
$ cd build
$ cmake ../</code></pre><p>위의 명령어 입력을 완료했다면 아래의 창이 출력될 것입니다. 필요한 Model을 설치해줍니다. 저의 경우, 우선 기본 설정되어있는 모델을 설치했습니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/8699ee49-ff96-4c59-9b6e-466489da9d4a/image.png" alt=""></p>
<p>추후에 필요한 모델을 추가로 설치하고 싶다면 아래의 명령어를 통해 설치할 수 있습니다.</p>
<pre><code>$ cd jetson-inference/tools
$ ./download-models.sh</code></pre><p>위의 과정을 수행했다면 다음은 파이토치(PyTorch)를 설치하라는 창이 출력됩니다. 파이썬 버전에 맞추어 파이토치를 설치해줍니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/90355e99-57b7-4f69-b6ea-4dc6c8ba8ddc/image.png" alt=""></p>
<p>파이토치 설치는 다소 오래 걸리니 참고해주시기 바랍니다.</p>
<p>파이토치 설치가 완료되었다면 아래의 명령어를 입력해줍니다.</p>
<pre><code>$ cd jetson-inference/build        # 이미 build 디렉토리 내에 있다면 생략해도 됩니다
$ make
$ sudo make install
$ sudo ldconfig</code></pre><p>위의 과정을 완료했다면 시스템 설정은 끝 마치게됩니다. 이제부터 ImageNet 데이터셋을 활용하여 이미지를 분류해보겠습니다.</p>
<hr>
<h1 id="inference">Inference</h1>
<h2 id="1-classifying-images-with-imagenet">1. Classifying Images with ImageNet</h2>
<h3 id="11-using-the-imagenet-program-on-jetson">1.1 Using the ImageNet Program on Jetson</h3>
<p>이전의 <code>Building the Project from Source</code> 과정이 성공적으로 설치되었는지 확인해보기 위해 아래의 명령어를 터미널에 입력하여 이미지 분류를 해보도록 한다.</p>
<pre><code>$ cd jetson-inference/build/aarch64/bin

$ ./imagenet.py images/orange_0.jpg images/test/output_0.jpg    # 필자의 경우, 파이썬만을 사용하므로 python 파일을 실행함</code></pre><p>파이썬 파일을 실행할 경우, 처음에는 몇분 정도 걸립니다. 처음 실행할 경우 네트워크를 최적화하는 과정이 오래 걸리지만 이후 실행할 경우에는 해당 네트워크가 디스크에 저장되어있어 더 빨리 실행시킬 수 있습니다.</p>
<p>출력 이미지가 지정한 위치(images/test/output_0.jpg)에 잘 저장되었는지 확인해보기 위해 아래의 명령어를 입력하여 이미지를 출력해봅니다.</p>
<pre><code>$ cd images/test
$ eog output_0.jpg</code></pre><p>아래의 이미지를 통해 정상적으로 출력 이미지가 저장되어있는 것을 확인할 수 있습니다. 오렌지 이미지에 대해 왼쪽 상단에 96.68%라는 분류 결과값을 출력해주었습니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/94b1eebe-0700-4b35-a7db-aaa5bf5032c9/image.png" alt=""></p>
<p>이번에는 딸기 이미지에 대해 분류해보도록 하겠습니다. 아래의 명령어를 입력합니다.</p>
<pre><code>$ cd jetson-inference/build/aarch64/bin

$ ./imagenet.py images/strawberry_0.jpg images/test/output_1.jpg

or

$ python3 imagenet.py images/strawberry_0.jpg images/test/output_1.jpg</code></pre><p><img src="https://images.velog.io/images/jarvis_geun/post/1a804ef5-b0a8-4ba2-ae95-dcd55d3c6eca/image.png" alt=""></p>
<p>추가로 다른 분류 모델을 사용해보고 싶다면 <a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/imagenet-console-2.md#:~:text=Downloading%20Other%20Classification%20Models">링크</a>를 참고해주시기 바랍니다.</p>
<h3 id="using-different-classification-models">Using Different Classification Models</h3>
<p>다른 분류모델(resnet-18)을 사용하기 위해 아래의 명령어를 입력합니다.</p>
<pre><code>$ ./imagenet.py --network=resnet-18 images/jellyfish.jpg images/test/output_jellyfish.jpg</code></pre><p>이전과 마찬가지 이유로, 다른 모델을 처음 실행할 때는 상대적으로 오래 걸리는점 참고해주시기 바랍니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/c8199231-2b67-488d-9baa-c464787b394f/image.png" alt=""></p>
<p>이외에도 비디오나 다른 스트림 방법을 알고 싶다면 <a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/aux-streaming.md">Camera Streaming and Multimedia</a>를 참고해주시기 바랍니다.</p>
<hr>
<h3 id="12-coding-your-own-image-recognition-program-python">1.2 Coding Your Own Image Recognition Program (Python)</h3>
<ul>
<li><a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/imagenet-example-python-2.md">Coding Your Own Image Recognition Program (Python)</a></li>
</ul>
<h3 id="13-running-the-live-camera-recognition-demo">1.3 Running the Live Camera Recognition Demo</h3>
<ul>
<li><a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/imagenet-camera-2.md">Running the Live Camera Recognition Demo</a></li>
</ul>
<p>본인이 구매한 카메라에 맞게 관련 설정을 해줍니다. 저의 경우, 라즈베리파이 카메라 V2를 구매하여 관련 모듈을 설치하여 필요한 설정을 해주었습니다. 카메라 설정이 궁금하시다면 <a href="https://github.com/dusty-nv/jetson-inference/blob/master/docs/aux-streaming.md">Camera Streaming and Multimedia</a>를 참고해주시기 바랍니다.</p>
<p>필자가 사용한 명령어</p>
<pre><code># 관련 모듈 설치
$ sudo apt-get install v4l-utils

# 카메라 연결 및 포트 확인
$ v4l2-ctl --list-devices

# 카메라 영상의 포맷 확인
$ v4l2-ctl --device=/dev/video0 --list-formats-ext</code></pre><pre><code>$ ./imagenet.py /dev/video0        # V4L2 camera</code></pre><p>위의 명령어를 입력하면 된다고 하지만, 저는 작동하지 않습니다. 이유는 모르겠으나(젯슨 나노 2GB라 성능이 부족한가...?) 추후에 재도전 해보도록 하고 다음 단계로 넘어가도록 하겠습니다.</p>
<hr>
<h3 id="reference">Reference</h3>
<ul>
<li><a href="https://github.com/dusty-nv/jetson-inference#hello-ai-world">jetson-inference</a></li>
</ul>
<h3 id="참고할만한-사이트">참고할만한 사이트</h3>
<ul>
<li><a href="https://www.mincasurong.com/teaching-experience/jetson-nano-tutorial/1-jetson-nano-%EC%A4%80%EB%B9%84">젯슨 나노 원격접속</a></li>
</ul>
<h3 id="추가로-알고싶은-내용">추가로 알고싶은 내용</h3>
<ul>
<li>Docker</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Transfer Learning(전이학습) 톺아보기]]></title>
            <link>https://velog.io/@jarvis_geun/Transfer-Learning-%EC%A0%84%EC%9D%B4%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@jarvis_geun/Transfer-Learning-%EC%A0%84%EC%9D%B4%ED%95%99%EC%8A%B5</guid>
            <pubDate>Sun, 29 Aug 2021 00:43:05 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요. 이번 포스팅에서는 Transfer Learning, 즉 전이학습에 대해 공부해보는 시간을 갖도록 하겠습니다. 전이학습에 대한 자세한 내용은 <a href="https://bskyvision.com/698">링크</a>로 남겨놓겠습니다.</p>
<p>간단하게 전이학습이 무엇인지에 대해 말씀드리겠습니다. 특정 이미지가 &#39;개&#39;인지, 아닌지에 대해 분류해야 하는 경우 일반적으로 우리는 방대한 양의 &#39;개&#39; 이미지로 모델을 학습시킵니다. 이 과정에서 오랜 시간이 걸리고 다양한 제약조건(데이터를 확보하거나 비용 문제)이 있을 수 있습니다. 이러한 문제를 해결하기 위해 사용되는 방법이 전이학습(=Transfer Learning)입니다.</p>
<p>ImageNet과 같은 방대한 양의 데이터를 기반으로 학습한 모델(&#39;개&#39;, &#39;고양이&#39; 등에 대한 동물 분류)이 있다고 했을 때, 우리는 이 모델을 가져와 우리에게 맞는 용도로 재학습 시킬 수 있습니다. 기존에 있던 모델을 &#39;Pretrained model&#39;, 즉 <strong>사전학습된 모델</strong>이라고 부릅니다.</p>
<p>이 모델은 ImageNet의 데이터를 기반으로 학습된 모델입니다. 이 덕분에, 동물을 분류할 수 있는 사전학습된 모델은, 우리가 분류하려는 목적(&#39;개&#39;인지 아닌지)에 대해 분류할 수 있게 됩니다. 사전학습된 모델의 가중치가 우리의 목적(이미지가 &#39;개&#39;인지 아닌지 분류)에도 유효하기 때문입니다.</p>
<p>전이학습에 대한 자세한 내용 및 코드는 아래에서 설명드리겠습니다.</p>
<hr>
<blockquote>
<p>톺아보다 : 틈이 있는 곳마다 모조리 더듬어 뒤지면서 찾다(순우리말).</p>
</blockquote>
<p>Pytorch로 전이학습에 대한 코드실습을 해보겠습니다. 해당 코드는 <a href="https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html">파이토치 홈페이지</a>의 내용을 바탕으로 작성하였음을 알려드립니다.</p>
<h2 id="step-01-모델-설계">Step 01. 모델 설계</h2>
<h3 id="11-라이브러리-불러오기">1.1 라이브러리 불러오기</h3>
<pre><code class="language-python">from __future__ import print_function, division

import torch
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
import numpy as np
import torchvision
from torchvision import datasets, models, transforms
import matplotlib.pyplot as plt
import time
import os
import copy

# interactive mode
plt.ion()</code></pre>
<p><a href="https://sanghyu.tistory.com/113">lr-scheduler - 1</a>
<a href="https://pytorch.org/docs/stable/optim.html#:~:text=optionally%20with%20momentum">lr_scheduler - 2</a>
<a href="https://myjr52.tumblr.com/post/109586202081/python-16">plt.ion()</a></p>
<h3 id="12-데이터-불러오기">1.2 데이터 불러오기</h3>
<p>우리는 개미와 벌의 이미지를 분류하려고 합니다. 해당 데이터셋은 개미와 벌 각각에 대한 120여개의 training 이미지(총 240여개)와 150여개의 validation 이미지로 이루어져 있습니다. 이는 ImageNet의 데이터에서 가져온 극히 일부의 이미지이기에, 일반적으로 이러한 데이터로 모델을 학습시키기에는 부족한 감이 없지않아 있습니다. 따라서 이런 문제점을 개선하기 위해 앞서 말씀드린, 전이학습을 사용하는 것입니다.</p>
<pre><code class="language-python"># Data augmentation and normalization for training
# Just normalization for validation
# 딕셔너리를 사용하여 &#39;train&#39;과 &#39;val&#39;이라는 key 값에 transforms.Compose라는 value 값 정의.

data_transforms = {
    &#39;train&#39; : transforms.Compose([
                                  transforms.RandomResizedCrop(224),
                                  transforms.RandomHorizontalFlip(),
                                  transforms.ToTensor(),
                                  transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),

    &#39;val&#39; : transforms.Compose([
                                transforms.Resize(256),
                                transforms.CenterCrop(224),
                                transforms.ToTensor(),
                                transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
}</code></pre>
<pre><code class="language-python"># Google Colab에서 사용하여 아래와 같은 경로로 저장하였습니다.
# 개인의 경로에 맞게 아래의 경로를 수정하시기 바랍니다.
data_dir = &#39;/content/hymenoptera_data&#39;
image_datasets = {x : datasets.ImageFolder(os.path.join(data_dir, x),
                                           data_transforms[x])
                  for x in [&#39;train&#39;, &#39;val&#39;]}

dataloaders = {x : torch.utils.data.DataLoader(image_datasets[x], batch_size = 4,
                                               shuffle = True, num_workers = 4)
              for x in [&#39;train&#39;, &#39;val&#39;]}

dataset_sizes = {x : len(image_datasets[x]) for x in [&#39;train&#39;, &#39;val&#39;]}

class_names = image_datasets[&#39;train&#39;].classes

device = torch.device(&quot;cuda:0&quot; if torch.cuda.is_available() else &quot;cpu&quot;)</code></pre>
<p><a href="https://jybaek.tistory.com/799">num_workers = 4</a></p>
<p>데이터가 잘 저장되었는지 아래의 코드를 통해 확인해보겠습니다.</p>
<pre><code class="language-python">print(&quot;class_names : &quot;, class_names)
print(&quot;\n&quot;)
print(&quot;datasets_sizes : &quot;, dataset_sizes)
print(&quot;\n&quot;)
print(&quot;image_datasets : &quot;, image_datasets)</code></pre>
<pre><code># 출력결과
class_names :  [&#39;ants&#39;, &#39;bees&#39;]


datasets_sizes :  {&#39;train&#39;: 244, &#39;val&#39;: 153}


image_datasets :  {&#39;train&#39;: Dataset ImageFolder
    Number of datapoints: 244
    Root location: /content/hymenoptera_data/train
    StandardTransform
Transform: Compose(
               RandomResizedCrop(size=(224, 224), scale=(0.08, 1.0), ratio=(0.75, 1.3333), interpolation=bilinear)
               RandomHorizontalFlip(p=0.5)
               ToTensor()
               Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
           ), &#39;val&#39;: Dataset ImageFolder
    Number of datapoints: 153
    Root location: /content/hymenoptera_data/val
    StandardTransform
Transform: Compose(
               Resize(size=256, interpolation=bilinear, max_size=None, antialias=None)
               CenterCrop(size=(224, 224))
               ToTensor()
               Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
           )}</code></pre><p>출력결과를 확인해보겠습니다. datasets_sizes에 244개의 train 이미지와 153개의 validation 이미지가 출력되는 것으로 보아, image_datasets에 이미지가 정상적으로 저장되어있음을 확인할 수 있습니다.</p>
<h3 id="13-샘플-이미지-시각화">1.3 샘플 이미지 시각화</h3>
<pre><code class="language-python">def imshow(inp, title = None) :
  # Imshow for Tensor
  inp = inp.numpy().transpose((1, 2, 0))
  mean = np.array([0.485, 0.456, 0.406])
  std = np.array([0.229, 0.224, 0.225])
  inp = std + inp * mean
  inp = np.clip(inp, 0, 1)
  plt.imshow(inp)

  if title is not None :
    plt.title(title)

# pause a bit so that plots are plotted
plt.pause(0.001)

# Get a batch of training data
inputs, classes = next(iter(dataloaders[&#39;train&#39;]))

# Make a grid from batch
out = torchvision.utils.make_grid(inputs)

imshow(out, title = [class_names[x] for x in classes])</code></pre>
<p><img src="https://images.velog.io/images/jarvis_geun/post/1bc668cb-4928-4e89-a653-cbc3324758aa/image.png" alt=""></p>
<p><a href="https://numpy.org/doc/stable/reference/generated/numpy.clip.html">np.clip()</a></p>
<p><a href="https://aigong.tistory.com/183">make_grid()</a></p>
<pre><code class="language-python">print(&quot;inputs : &quot;, inputs)
print(&quot;\n&quot;)
print(&quot;out : &quot;, out)</code></pre>
<pre><code># 출력결과
inputs :  tensor([[[[-0.4397, -0.4568, -0.4397,  ..., -0.1828, -0.1828, -0.1999],
          [-0.4397, -0.4226, -0.4226,  ..., -0.1999, -0.1999, -0.2342],
          [-0.4226, -0.4226, -0.4226,  ..., -0.2342, -0.2513, -0.2684],
          ...,
          [-0.6281, -0.6794, -0.7479,  ..., -0.6794, -0.6623, -0.6452],
          [-0.5767, -0.6623, -0.7308,  ..., -0.6794, -0.6452, -0.5938],
          [-0.5767, -0.6623, -0.6965,  ..., -0.5938, -0.5767, -0.5424]],

         [[-0.7752, -0.7927, -0.7752,  ..., -0.5126, -0.5126, -0.5301],
          [-0.7752, -0.7752, -0.7752,  ..., -0.5301, -0.5301, -0.5651],
          [-0.7577, -0.7752, -0.7752,  ..., -0.5651, -0.5826, -0.6001],
          ...,
          [-0.8277, -0.8978, -0.9503,  ..., -1.0553, -1.0378, -1.0203],
          [-0.8277, -0.8627, -0.9153,  ..., -1.0553, -1.0378, -1.0203],
          [-0.8277, -0.8627, -0.9153,  ..., -1.0203, -0.9853, -0.9503]],

         [[-0.8458, -0.8633, -0.8458,  ..., -0.7238, -0.7238, -0.7413],
          [-0.8458, -0.8458, -0.8458,  ..., -0.7413, -0.7413, -0.7761],
          [-0.8284, -0.8458, -0.8458,  ..., -0.7761, -0.7936, -0.8110],
          ...,
          [-0.9853, -0.9853, -1.0027,  ..., -1.2119, -1.1944, -1.1770],
          [-0.9678, -1.0027, -1.0027,  ..., -1.2119, -1.1944, -1.1596],
          [-0.9678, -1.0027, -0.9853,  ..., -1.1770, -1.1770, -1.1421]]],


        [[[ 0.3652,  0.3138,  0.3994,  ..., -0.8507, -0.8507, -0.7822],
          [ 0.2111,  0.3309,  0.5193,  ..., -0.8507, -0.7822, -0.7650],
          [ 0.1939,  0.1768,  0.3823,  ..., -0.8335, -0.8164, -0.8507],
          ...,
          [-1.6213, -1.6213, -1.6555,  ..., -1.5528, -1.5357, -1.5357],
          [-1.6213, -1.6213, -1.6555,  ..., -1.5528, -1.5699, -1.5357],
          [-1.5870, -1.6384, -1.6555,  ..., -1.5699, -1.5528, -1.6384]],

         [[-1.1954, -1.2304, -1.1253,  ..., -0.3025, -0.3375, -0.3550],
          [-1.2654, -1.1078, -1.1253,  ..., -0.3025, -0.3375, -0.3200],
          [-1.2829, -1.1954, -1.3004,  ..., -0.3200, -0.3025, -0.2850],
          ...,
          [-1.2829, -1.2829, -1.3179,  ..., -0.8978, -0.9153, -0.8803],
          [-1.2829, -1.2829, -1.3004,  ..., -0.9503, -0.9328, -0.9503],
          [-1.2479, -1.3004, -1.3004,  ..., -1.0028, -0.9678, -1.0203]],

         [[-1.6999, -1.7870, -1.8044,  ..., -1.6127, -1.6476, -1.6302],
          [-1.7870, -1.7522, -1.7696,  ..., -1.5953, -1.6127, -1.5953],
          [-1.7522, -1.7696, -1.7696,  ..., -1.6127, -1.6302, -1.6127],
          ...,
          [-1.6302, -1.6302, -1.6302,  ..., -1.2816, -1.3339, -1.2990],
          [-1.6302, -1.6302, -1.6127,  ..., -1.2816, -1.3513, -1.3687],
          [-1.6302, -1.6476, -1.5953,  ..., -1.3513, -1.3513, -1.3861]]],


        [[[ 0.5878,  0.5878,  0.5878,  ...,  2.0434,  2.0434,  1.9749],
          [ 0.6049,  0.6392,  0.6221,  ...,  2.0263,  2.0092,  1.9407],
          [ 0.6392,  0.6563,  0.6563,  ...,  2.0263,  2.0092,  1.9064],
          ...,
          [-1.1418, -1.0390, -1.0219,  ...,  0.8104,  0.8276,  0.8276],
          [-1.0904, -1.0219, -1.0048,  ...,  0.8447,  0.8618,  0.8618],
          [-1.0390, -1.0219, -1.0048,  ...,  0.8447,  0.8618,  0.8618]],

         [[-0.6702, -0.6702, -0.6702,  ...,  1.8859,  1.8508,  1.7283],
          [-0.6877, -0.6527, -0.6702,  ...,  1.8683,  1.8158,  1.6933],
          [-0.6527, -0.6352, -0.6352,  ...,  1.8333,  1.7983,  1.6583],
          ...,
          [-1.2129, -1.1253, -1.0903,  ...,  0.0476,  0.0651,  0.0651],
          [-1.1604, -1.0903, -1.0728,  ..., -0.0049,  0.0126,  0.0126],
          [-1.1429, -1.0903, -1.0553,  ..., -0.0399, -0.0224, -0.0224]],

         [[-0.4275, -0.3927, -0.3927,  ...,  2.2914,  2.2566,  2.1694],
          [-0.4101, -0.3578, -0.3753,  ...,  2.2740,  2.2391,  2.1346],
          [-0.3753, -0.3404, -0.3404,  ...,  2.2566,  2.2217,  2.0997],
          ...,
          [-1.1247, -0.9678, -0.9330,  ...,  0.0431,  0.0605,  0.0605],
          [-1.0724, -0.9330, -0.8807,  ...,  0.0256,  0.0431,  0.0431],
          [-1.0201, -0.8981, -0.8807,  ...,  0.0431,  0.0605,  0.0605]]],


        [[[-1.6042, -1.5014, -1.5528,  ..., -1.4158, -1.3815, -1.5357],
          [-1.5870, -1.6042, -1.5357,  ..., -1.4843, -1.4843, -1.4158],
          [-1.4158, -1.4329, -1.3473,  ..., -1.4672, -1.5014, -1.4158],
          ...,
          [-1.7412, -1.7240, -1.5699,  ..., -1.6727, -1.6213, -1.5699],
          [-1.8097, -1.7412, -1.6727,  ..., -1.7069, -1.6042, -1.6555],
          [-1.7583, -1.6727, -1.7069,  ..., -1.8268, -1.6727, -1.7412]],

         [[-0.8803, -0.9153, -0.7752,  ..., -0.6702, -0.7402, -0.6702],
          [-0.8452, -0.8277, -0.8277,  ..., -0.6702, -0.7227, -0.8102],
          [-0.9328, -0.7752, -0.7227,  ..., -0.7227, -0.7752, -0.8102],
          ...,
          [-1.4230, -1.4580, -1.4930,  ..., -1.3704, -1.3004, -1.1779],
          [-1.3880, -1.3704, -1.3880,  ..., -1.3529, -1.4230, -1.3004],
          [-1.3704, -1.3529, -1.4230,  ..., -1.3704, -1.3704, -1.3179]],

         [[-1.4210, -1.2990, -1.4210,  ..., -0.8633, -1.0724, -1.0201],
          [-1.3513, -1.3339, -1.2816,  ..., -0.9678, -1.1421, -1.1247],
          [-1.4384, -1.3339, -1.2990,  ..., -1.0550, -1.0550, -1.2293],
          ...,
          [-1.4907, -1.4907, -1.4210,  ..., -1.2467, -1.4036, -1.2816],
          [-1.4907, -1.4733, -1.4210,  ..., -1.1770, -1.2816, -1.3164],
          [-1.4559, -1.4384, -1.4907,  ..., -1.2467, -1.2119, -1.1944]]]])


out :  tensor([[[ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000],
         [ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000],
         [ 0.0000,  0.0000, -0.4397,  ..., -1.5357,  0.0000,  0.0000],
         ...,
         [ 0.0000,  0.0000, -0.5767,  ..., -1.7412,  0.0000,  0.0000],
         [ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000],
         [ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000]],

        [[ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000],
         [ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000],
         [ 0.0000,  0.0000, -0.7752,  ..., -0.6702,  0.0000,  0.0000],
         ...,
         [ 0.0000,  0.0000, -0.8277,  ..., -1.3179,  0.0000,  0.0000],
         [ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000],
         [ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000]],

        [[ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000],
         [ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000],
         [ 0.0000,  0.0000, -0.8458,  ..., -1.0201,  0.0000,  0.0000],
         ...,
         [ 0.0000,  0.0000, -0.9678,  ..., -1.1944,  0.0000,  0.0000],
         [ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000],
         [ 0.0000,  0.0000,  0.0000,  ...,  0.0000,  0.0000,  0.0000]]])</code></pre><br/>
<br/>

<h2 id="step-02-모델-학습-및-시각화">Step 02. 모델 학습 및 시각화</h2>
<h3 id="21-모델-학습하기">2.1 모델 학습하기</h3>
<pre><code class="language-python">def train_model(model, criterion, optimizer, scheduler, num_epochs = 25)  :
  since = time.time()

# 모델 가중치(weights) 불러오기
  best_model_wts = copy.deepcopy(model.state_dict())
  best_acc = 0.0

  for epoch in range(num_epochs)  :
    print(&quot;Epoch {}/{}&quot;.format(epoch, num_epochs - 1))
    print(&quot;-&quot; * 10)

    # Each epoch has a training and validation phase
    for phase in [&#39;train&#39;, &#39;val&#39;] :
      if phase == &#39;train&#39; :
        model.train()   # Set model to training mode

      else :
        model.eval()    # Set model to evaluate mode

      running_loss = 0.0
      running_corrects = 0

      # Iterate over data.
      for inputs, labels in dataloaders[phase] :
        inputs = inputs.to(device)
        labels = labels.to(device)

        # zero the parameter gradients
        optimizer.zero_grad()

        # forward
        # track history if only in train
        with torch.set_grad_enabled(phase == &#39;train&#39;) :
          outputs = model(inputs)
          _, preds = torch.max(outputs, 1)
          loss = criterion(outputs, labels)

          # backward + optimize only if in training phase
          if phase == &#39;train&#39; :
            loss.backward()
            optimizer.step()

        # statistics
        running_loss += loss.item() * inputs.size(0)
        running_corrects += torch.sum(preds == labels.data)

      if phase == &#39;train&#39; :
        scheduler.step()

      epoch_loss = running_loss / dataset_sizes[phase]
      epoch_acc = running_corrects.double() / dataset_sizes[phase]

      print(&quot;{} Loss : {:.4f} Acc : {:.4f}&quot;.format(
        phase, epoch_loss, epoch_acc))

      # deep copy the model
      if phase == &#39;val&#39; and epoch_acc &gt; best_acc :
        best_acc = epoch_acc
        best_model_wts = copy.deepcopy(model.state_dict())

    print()

  time_elapsed = time.time() - since
  print(&quot;Training complete in {:.0f}m {:.0f}s&quot;.format(
      time_elapsed // 60, time_elapsed % 60))
  print(&quot;Best val Acc : {:4f}&quot;.format(best_acc))

  # load best model weights
  model.load_state_dict(best_model_wts)
  return model</code></pre>
<p><a href="https://discuss.pytorch.org/t/confused-about-set-grad-enabled/38417">with torch.set_grad_enabled(phase == &#39;train&#39;)</a>
<a href="https://www.daleseo.com/python-enumerate/">enumerate</a>
<br/></p>
<h3 id="22-예측한-모델-시각화하기">2.2 예측한 모델 시각화하기</h3>
<pre><code class="language-python">def visualize_model(model, num_images = 6) :
  was_training = model.training
  model.eval()
  images_so_far = 0
  fig = plt.figure()

  with torch.no_grad() :
    for i, (inputs, labels) in enumerate(dataloaders[&#39;val&#39;]) :
      inputs = inputs.to(device)
      labels = labels.to(device)

      outputs = model(inputs)
      _, preds = torch.max(outputs, 1)

      for j in range(inputs.size()[0]) :
        images_so_far += 1
        ax = plt.subplot(num_images // 2, 2, images_so_far)
        ax.axis(&quot;off&quot;)
        ax.set_title(&quot;predicted : {}&quot;.format(class_names[preds[j]]))
        imshow(inputs.cpu().data[j])

        if images_so_far == num_images :
          model.train(mode = was_training)
          return
    model.train(mode = was_training)</code></pre>
<br/>

<h3 id="23-미세조정하기finetuning">2.3 미세조정하기(=Finetuning)</h3>
<pre><code class="language-python"># Finetuning the convnet

model_ft = models.resnet18(pretrained = True)
num_ftrs = model_ft.fc.in_features
# Here the size of each output sample is set to 2.
# Alternatively, it can be generalized to nn.Linear(num_ftrs, len(class_names)).

# resnet18 모델을 가져와 기존의 feature를, nn.Linear를 사용하여 2개의 feature(ants, bees)를 분류할 수 있게 변경.
model_ft.fc = nn.Linear(num_ftrs, 2)

model_ft = model_ft.to(device)

# 손실함수 정의
criterion = nn.CrossEntropyLoss()

# Optimizer 정의
# Observe that all parameters are being optimized
optimizer_ft = optim.SGD(model_ft.parameters(), lr = 0.001, momentum = 0.9)

# Decay LR by a factor of 0.1 every 7 epochs
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_ft, step_size = 7, gamma = 0.1)</code></pre>
<br/>

<h2 id="step-03-모델-학습-및-평가">Step 03. 모델 학습 및 평가</h2>
<h3 id="31-모델-학습">3.1 모델 학습</h3>
<pre><code class="language-python">model_ft = train_model(model_ft, criterion, optimizer_ft, exp_lr_scheduler, num_epochs = 25)</code></pre>
<pre><code># 출력결과

Epoch 0/24
----------
/usr/local/lib/python3.7/dist-packages/torch/nn/functional.py:718: UserWarning: Named tensors and all their associated APIs are an experimental feature and subject to change. Please do not use them for anything important until they are released as stable. (Triggered internally at  /pytorch/c10/core/TensorImpl.h:1156.)
  return torch.max_pool2d(input, kernel_size, stride, padding, dilation, ceil_mode)
train Loss : 0.5040 Acc : 0.7377
val Loss : 0.3973 Acc : 0.8693

Epoch 1/24
----------
train Loss : 0.4806 Acc : 0.8484
val Loss : 0.3875 Acc : 0.8366

Epoch 2/24
----------
train Loss : 0.5357 Acc : 0.7705
val Loss : 0.4966 Acc : 0.8105

...

(중략)

...

Epoch 23/24
----------
train Loss : 0.2226 Acc : 0.8975
val Loss : 0.2333 Acc : 0.9150

Epoch 24/24
----------
train Loss : 0.2859 Acc : 0.8852
val Loss : 0.2210 Acc : 0.9085

Training complete in 2m 14s
Best val Acc : 0.928105</code></pre><br/>

<h3 id="311-궁금한-사항-실험">3.1.1 궁금한 사항 실험</h3>
<pre><code class="language-python"># Train_model 함수에서 특정값들이 출력될 수 있게 코드 수정함
# forward
        # track history if only in train
        with torch.set_grad_enabled(phase == &#39;train&#39;) :
          outputs = model(inputs)
          print(&quot;outputs : &quot;, outputs)
          print(&quot;\n&quot;)
          print(&quot;torch.max(outputs, 1) : &quot;, torch.max(outputs, 1))
          print(&quot;\n&quot;)
          _, preds = torch.max(outputs, 1)
          print(&quot;\n&quot;)
          print(&quot;_, preds : &quot;, _, preds)
          print(&quot;\n&quot;)
          loss = criterion(outputs, labels)

          # backward + optimize only if in training phase
          if phase == &#39;train&#39; :
            loss.backward()
            optimizer.step()</code></pre>
<pre><code># 출력결과(일부분)

Epoch 0/24
----------
outputs :  tensor([[-0.0880, -0.3602],
        [-0.5123, -0.2996],
        [ 0.1827, -0.6169],
        [-0.6210, -0.7068]], device=&#39;cuda:0&#39;, grad_fn=&lt;AddmmBackward&gt;)


torch.max(outputs, 1) :  torch.return_types.max(
values=tensor([-0.0880, -0.2996,  0.1827, -0.6210], device=&#39;cuda:0&#39;,
       grad_fn=&lt;MaxBackward0&gt;),
indices=tensor([0, 1, 0, 0], device=&#39;cuda:0&#39;))




_, preds :  tensor([-0.0880, -0.2996,  0.1827, -0.6210], device=&#39;cuda:0&#39;,
       grad_fn=&lt;MaxBackward0&gt;) tensor([0, 1, 0, 0], device=&#39;cuda:0&#39;)
</code></pre><p>위의 출력된 결과를 분석해보겠습니다. 먼저 outputs을 보면, 4개의 행이 포함된 텐서가 출력된 것을 확인할 수 있습니다. 이는 batch_size = 4로 주었기 때문입니다. 이를 다른 숫자로 바꾸면 출력결과 역시 바뀝니다.</p>
<p>torch.max(outputs, 1)을 관찰해보면, outputs 텐서에서 각 행마다 최댓값이 저장되어있는 것을 확인할 수 있습니다. 이는 torch.max가 value와 index 둘 다를 저장하므로 위와 같은 결과를 얻을 수 있습니다. -0.0880과 -0.3602 중 큰 값은 -0.0880이므로 인덱스는 0이 저장되었습니다. 이런 과정을 총 4번(batch_size) 반복하여 저장합니다.</p>
<p>_, preds에서 preds에는 출력된 값의 맨 뒷부분인 tensor([0, 1, 0, 0]만 저장됩니다.
<br/></p>
<h3 id="32-시각화">3.2 시각화</h3>
<pre><code class="language-python">visualize_model(model_ft)</code></pre>
<p><img src="https://images.velog.io/images/jarvis_geun/post/5b08c254-7e18-4baa-ad65-4195e374c53b/image.png" alt="">
<br/></p>
<h2 id="step-04-fc-layer를-제외한-나머지-parameter-동결">Step 04. FC layer를 제외한 나머지 parameter 동결</h2>
<h3 id="41-모델-학습-및-평가동결된-모델">4.1 모델 학습 및 평가(동결된 모델)</h3>
<pre><code class="language-python">model_conv = torchvision.models.resnet18(pretrained=True)
for param in model_conv.parameters():
    param.requires_grad = False

# Parameters of newly constructed modules have requires_grad=True by default
num_ftrs = model_conv.fc.in_features
model_conv.fc = nn.Linear(num_ftrs, 2)

model_conv = model_conv.to(device)

criterion = nn.CrossEntropyLoss()

# Observe that only parameters of final layer are being optimized as
# opposed to before.
optimizer_conv = optim.SGD(model_conv.fc.parameters(), lr=0.001, momentum=0.9)

# Decay LR by a factor of 0.1 every 7 epochs
exp_lr_scheduler = lr_scheduler.StepLR(optimizer_conv, step_size=7, gamma=0.1)</code></pre>
<pre><code class="language-python">model_conv = train_model(model_conv, criterion, optimizer_conv,
                         exp_lr_scheduler, num_epochs=25)</code></pre>
<pre><code># 출력결과
Epoch 0/24
----------
train Loss : 0.5879 Acc : 0.7049
val Loss : 0.3701 Acc : 0.8366

Epoch 1/24
----------
train Loss : 0.5626 Acc : 0.7131
val Loss : 0.2810 Acc : 0.9085

Epoch 2/24
----------
train Loss : 0.5619 Acc : 0.7541
val Loss : 0.2008 Acc : 0.9346

...

(중략)

...

Epoch 23/24
----------
train Loss : 0.3277 Acc : 0.8402
val Loss : 0.2132 Acc : 0.9412

Epoch 24/24
----------
train Loss : 0.3313 Acc : 0.8607
val Loss : 0.1757 Acc : 0.9412

Training complete in 1m 34s
Best val Acc : 0.967320</code></pre><p>위의 결과(FC 레이어만 학습)와 이전의 결과(모든 layer 학습)를 비교해보면, Best val Acc가 위의 결과가 0.967320으로 더 높은 것을 확인할 수 있습니다. 즉, 모든 레이어를 학습시키는 것보다 마지막 레이어(FC layer)만 학습시켰을 때가 더 좋은 성능을 내는 것을 관찰할 수 있습니다.
<br/></p>
<pre><code class="language-python">visualize_model(model_conv)

plt.ioff()
plt.show()</code></pre>
<p><img src="https://images.velog.io/images/jarvis_geun/post/e0800614-8f6d-4458-b31d-05692ac02b15/image.png" alt=""></p>
<hr>
<h3 id="reference">Reference</h3>
<ul>
<li><p>파이토치 홈페이지
<a href="https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html">https://pytorch.org/tutorials/beginner/transfer_learning_tutorial.html</a></p>
</li>
<li><p>소스코드
<a href="https://github.com/Jarvis-Geun/DeepLearning_Architecture/blob/main/Transfer%20Learning_Pytorch.ipynb">https://github.com/Jarvis-Geun/DeepLearning_Architecture/blob/main/Transfer%20Learning_Pytorch.ipynb</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[torch.argmax() & torch.max()]]></title>
            <link>https://velog.io/@jarvis_geun/torch.argmax-torch.max</link>
            <guid>https://velog.io/@jarvis_geun/torch.argmax-torch.max</guid>
            <pubDate>Thu, 26 Aug 2021 06:46:25 GMT</pubDate>
            <description><![CDATA[<h2 id="torchargmax">torch.argmax()</h2>
<p><a href="https://pytorch.org/docs/stable/generated/torch.argmax.html">Pytorch docs : torch.argmax()</a></p>
<pre><code class="language-python">import torch

# dim 파라미터를 추가하지 않은 경우
a = torch.randn(4, 4)
argmax = torch.argmax(a)
print(a)
print(argmax)</code></pre>
<pre><code># 출력결과
tensor([[ 0.5478,  0.6609,  0.6214,  1.0270],
        [-0.0406, -1.1121,  0.7632,  0.3169],
        [-1.2267,  1.6110, -0.3082, -0.4010],
        [-0.9153,  0.1607, -1.0582,  0.8812]])
tensor(9)</code></pre><p>위의 결과를 보면 알 수 있듯이, 1행 → 4행의 순서로 큰 숫자를 파악하여 tensor를 출력한다.
<br/></p>
<pre><code class="language-python"># dim = 0으로 할 경우
import torch

a = torch.randn(5, 3)
argmax_dim_0 = torch.argmax(a, dim = 0)
print(a)
print(argmax_dim_0)</code></pre>
<pre><code># 출력결과
tensor([[ 0.7142,  0.4088,  0.1844],
        [-0.9138, -1.2394,  0.1528],
        [ 0.5173,  0.5606, -0.4164],
        [ 1.7521,  0.4822,  0.6333],
        [ 0.4563, -0.3746,  0.7837]])
tensor([3, 2, 4])</code></pre><p>dim = 0의 파라미터를 추가할 경우, 결과는 위와 같다. <strong>열을 기준</strong>으로 max 값이 있는 index를 출력한다.</p>
<pre><code class="language-python"># dim = 1으로 할 경우
argmax_dim_1 = torch.argmax(a, dim = 1)
print(a)
print(argmax_dim_1)</code></pre>
<pre><code>tensor([[ 0.7142,  0.4088,  0.1844],
        [-0.9138, -1.2394,  0.1528],
        [ 0.5173,  0.5606, -0.4164],
        [ 1.7521,  0.4822,  0.6333],
        [ 0.4563, -0.3746,  0.7837]])
tensor([0, 2, 1, 0, 2])</code></pre><p>이전의 결과에서 dim = 0 → dim = 1로 변경할 경우의 결과이다. dim을 파라미터로 입력할 경우, 차원에 따라 값이 달라지는 것을 확인할 수 있다. <strong>행을 기준</strong>으로 max 값이 있는 index를 출력한다.</p>
<hr>
<h2 id="torchmax">torch.max()</h2>
<p><a href="https://pytorch.org/docs/stable/generated/torch.max.html#torch.max">Pytorch docs : torch.max()</a></p>
<pre><code class="language-python">import torch

a = torch.randn(5, 3)
argmax_dim_0 = torch.max(a, dim = 0)
print(a)
print(argmax_dim_0)</code></pre>
<pre><code>tensor([[-1.1839, -0.2547, -1.2708],
        [ 0.6713,  0.0494, -0.2655],
        [-0.3116,  0.7832, -1.2234],
        [-0.6373,  0.1265, -1.8030],
        [-0.1249, -1.7748,  1.2657]])
torch.return_types.max(
values=tensor([0.6713, 0.7832, 1.2657]),
indices=tensor([1, 2, 4]))</code></pre><p>torch.max()의 경우 최댓값과 인덱스 모두 출력해준다. torch.argmax()와 마찬가지로 dim = 0이면, <strong>열을 기준</strong>(각 열마다)으로 최댓값과 인덱스를 출력해준다.</p>
<pre><code class="language-python">argmax_dim_1 = torch.max(a, dim = 1)
print(a)
print(argmax_dim_1)</code></pre>
<pre><code>tensor([[-1.1839, -0.2547, -1.2708],
        [ 0.6713,  0.0494, -0.2655],
        [-0.3116,  0.7832, -1.2234],
        [-0.6373,  0.1265, -1.8030],
        [-0.1249, -1.7748,  1.2657]])
torch.return_types.max(
values=tensor([-0.2547,  0.6713,  0.7832,  0.1265,  1.2657]),
indices=tensor([1, 0, 1, 1, 2]))</code></pre><p>dim = 1이면, <strong>행을 기준</strong>(각 행마다)으로 최댓값과 인덱스를 동시에 출력해준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[U-Net 실습]]></title>
            <link>https://velog.io/@jarvis_geun/U-Net-%EC%8B%A4%EC%8A%B5</link>
            <guid>https://velog.io/@jarvis_geun/U-Net-%EC%8B%A4%EC%8A%B5</guid>
            <pubDate>Tue, 24 Aug 2021 01:04:04 GMT</pubDate>
            <description><![CDATA[<p>이번 포스팅에서는 지난 시간에 공부한 <a href="https://velog.io/@jarvis-geun/U-Net-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0in-depth">U-Net 톺아보기</a>에 대한 실습을 해보려고 합니다. 사실 실습이라기 보다는 <a href="https://blog.jovian.ai/semantic-segmentation-in-self-driving-cars-3cb89aa08e9b">링크</a>의 내용을 분석해보는 것에 불과합니다. 실습에 관련된 대부분의 내용(소스코드 포함)은 아래의 사이트를 바탕으로 작성했음을 미리 알려드립니다. 또한, 데이터셋도 아래 링크에서 다운받으실 수 있습니다.</p>
<p><a href="https://blog.jovian.ai/semantic-segmentation-in-self-driving-cars-3cb89aa08e9b">[Semantic Segmentation in Self-driving Cars]</a>
<a href="https://jovian.ai/chakravarthy-sudharshan/semantic-segmentation-cityscapes/v/1?utm_source=embed">[Source Code]</a>
<a href="https://www.kaggle.com/dansbecker/cityscapes-image-pairs">[Dataset]</a></p>
<p>기존의 코드에서 제가 따로 추가하거나 수정한 소스코드는, 아래의 깃허브에서 다운받으실 수 있습니다.
<a href="https://github.com/Jarvis-Geun/DeepLearning_Architecture">Github</a> : U-Net(Semantic Segmentation.ipynb) 파일</p>
<hr>
<h2 id="cityscape-dataset">Cityscape Dataset</h2>
<p>어떤 데이터를 바탕으로 학습을 하는지를 확인해보기 위해, 데이터셋 중 한 개의 이미지만을 확인해봅니다.<img src="https://images.velog.io/images/jarvis_geun/post/cd439158-ee6d-41d6-9f6f-964fe12d0fe1/image.png" alt=""></p>
<p>이미지를 확인해보면 256(세로, 행) x 512(가로, 열)의 픽셀로 구성되어 있는 것을 확인할 수 있습니다. 위의 사진에서 왼쪽의 이미지는 실제 이미지(original image)를, 오른쪽 이미지는 labeled 이미지를 나타냅니다. Cityscape Dataset은 2975개의 training 이미지 파일과 500개의 validation 이미지 파일로 이루어져 있습니다. 해당 데이터셋에 대한 자세한 설명은 <a href="https://www.kaggle.com/dansbecker/cityscapes-image-pairs">다운받은 사이트</a>에서 확인할 수 있습니다.</p>
<hr>
<h2 id="step-1-모델-설계">Step 1. 모델 설계</h2>
<h3 id="11-라이브러리-불러오기">1.1 라이브러리 불러오기</h3>
<pre><code class="language-python">import os
from PIL import Image
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms

from tqdm.notebook import tqdm</code></pre>
<p>모델 설계하는데 필요한 라이브러리를 불러옵니다.
<a href="https://ko.wikipedia.org/wiki/Python_Imaging_Library">PIL 라이브러리</a></p>
<h3 id="12-gpu-설정하기">1.2 GPU 설정하기</h3>
<pre><code class="language-python"># GPU 사용이 가능할 경우, GPU를 사용할 수 있게 함.
device = &quot;cuda:0&quot; if torch.cuda.is_available() else &quot;cpu&quot;
device = torch.device(device)
print(device)</code></pre>
<br/>

<h3 id="13-파일-시스템">1.3 파일 시스템</h3>
<pre><code class="language-python"># 자신의 폴더 경로에 맞게 재지정해주세요.
root_path = &#39;~/archive/ityscapes_data/&#39;

data_dir = root_path

# data_dir의 경로(문자열)와 train(문자열)을 결합해서 train_dir(train 폴더의 경로)에 저장합니다.
train_dir = os.path.join(data_dir, &quot;train&quot;)

# data_dir의 경로(문자열)와 val(문자열)을 결합해서 val_dir(val 폴더의 경로)에 저장합니다.
val_dir = os.path.join(data_dir, &quot;val&quot;)

# train_dir 경로에 있는 모든 파일을 리스트의 형태로 불러와서 train_fns에 저장합니다.
train_fns = os.listdir(train_dir)

# val_dir 경로에 있는 모든 파일을 리스트의 형태로 불러와서 val_fns에 저장합니다.
val_fns = os.listdir(val_dir)

print(len(train_fns), len(val_fns))</code></pre>
<p>위의 결과를 출력해보면 아래와 같은 결과를 얻을 수 있습니다.</p>
<pre><code>2975 500</code></pre><p>즉, train_fns의 길이는 2975이며 val_fns의 길이는 500입니다. 이는 데이터셋인 <strong>Cityscape Dataset</strong>의 학습(train) 및 검증(validation) 데이터와 일치하는 것을 확인할 수 있습니다.
<br/></p>
<h3 id="14-샘플-이미지-검색">1.4 샘플 이미지 검색</h3>
<p>경로를 지정했으므로 이제 이 경로를 사용하여 샘플 이미지를 불러오도록 하겠습니다. 이 과정은 생략해도 되지만 불러오는 과정이 원활하게 동작하는지 확인하기 위해 실습해보도록 하겠습니다.</p>
<pre><code class="language-python"># train_dir(문자열)와 train_fns[0](문자열)의 경로를 결합하여 sample_image_fp(샘플 이미지의 경로)에 저장합니다.
sample_image_fp = os.path.join(train_dir, train_fns[0])

# PIL 라이브러리의 Image 모듈을 사용하여, sample_image_fp를 불러옵니다.
# RGB 형태로 변환하여 sample_image에 저장하는 것으로 이해했는데, &quot;.convert(&quot;RGB&quot;)&quot; 코드는 없어도 될 것 같습니다.
# Image.open() 함수 자체가 RGB의 형태로 불러오는 것으로 이해했습니다. 확실하지 않습니다...
sample_image = Image.open(sample_image_fp).convert(&quot;RGB&quot;)

plt.imshow(sample_image)
plt.show()</code></pre>
<p><a href="https://stackoverflow.com/questions/3497578/matplotlib-plot-and-imshow">plt.show() vs plt.imshow()</a></p>
<p>위의 소스코드를 실행하면 아래의 이미지가 출력됩니다.
<img src="https://images.velog.io/images/jarvis_geun/post/934d34a0-4b62-463c-b776-152461413d5b/output.png" alt="">
<br/></p>
<h3 id="15-output-label-정의하기">1.5 Output Label 정의하기</h3>
<pre><code class="language-python">num_items = 1000

# 0~255 사이의 숫자를 3*num_items번 랜덤하게 뽑기
color_array = np.random.choice(range(256), 3*num_items).reshape(-1, 3)
print(color_array.shape)</code></pre>
<p>출력결과</p>
<pre><code>(1000, 3)</code></pre><pre><code class="language-python">num_classes = 10

# K-means clustering 알고리즘을 사용하여 label_model에 저장합니다.
label_model = KMeans(n_clusters = num_classes)
label_model.fit(color_array)</code></pre>
<br/>

<p><a href="https://blog.mathpresso.com/mathpresso-%EB%A8%B8%EC%8B%A0-%EB%9F%AC%EB%8B%9D-%EC%8A%A4%ED%84%B0%EB%94%94-9-%EA%B5%B0%EC%A7%91%ED%99%94-clustering-542390bb4b74">K-means clustering</a></p>
<p><a href="https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html#:~:text=fit(X%2C%20y%3DNone%2C%20sample_weight%3DNone)">label_model.fit(color_array)</a>
<br/></p>
<pre><code class="language-python"># 이전에 샘플이미지에서 볼 수 있듯이, original image와 labeled image가 연결되어 있는데 이를 분리해줍니다.
def split_image(image) :
   image = np.array(image)

   # 이미지의 크기가 256 x 512 였는데 이를 original image와 labeled image로 분리하기 위해 리스트로 슬라이싱 합니다.
   # 그리고 분리된 이미지를 각각 cityscape(= original image)와 label(= labeled image)에 저장합니다.
   cityscape, label = image[:, :256, :], image[:, 256:, :]
   return cityscape, label</code></pre>
<pre><code class="language-python"># 바로 이전 코드에서 정의한 split_image() 함수를 이용하여 sample_image를 분리한 후, cityscape과 label에 각각 저장합니다.
cityscape, label = split_image(sample_image)

label_class = label_model.predict(label.reshape(-1, 3)).reshape(256, 256)
fig, axes = plt.subplots(1, 3, figsize = (15, 5))
axes[0].imshow(cityscape)
axes[1].imshow(label)
axes[2].imshow(label_class)

plt.show()</code></pre>
<p><img src="https://images.velog.io/images/jarvis_geun/post/91b27e49-7352-45ee-a55e-f6d0b70fbd07/output.png" alt=""></p>
<p><a href="https://scikit-learn.org/stable/modules/generated/sklearn.cluster.KMeans.html#:~:text=predict(X%2C%20sample_weight%3DNone)">label_model.predict( )</a>
<a href="https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.subplots.html#:~:text=nrows%2C%20ncolsint%2C%20default%3A%201">plt.subplots( )</a></p>
<br/>


<h4 id="151-label_modelpredict--궁금한-것">1.5.1 label_model.predict( ) 궁금한 것</h4>
<pre><code class="language-python"># label_class = label_model.predict(label.reshape(-1, 3)).reshape(256, 256) 코드를 아래의 코드로 바꿔봄.
label_class = label_model.predict(label.reshape(-1, 1)).reshape(256, 256)</code></pre>
<pre><code>KMeans의 predict 메소드의 경우 3개의 feature를 입력으로 받으므로 reshape(-1, 3)을 해주어야 정상적으로 동작함.
ValueError: X has 1 features, but KMeans is expecting 3 features as input.</code></pre><pre><code class="language-python">print(label.shape)
print(label.reshape(-1, 3).shape)
print(label_model.predict(label.reshape(-1, 3)).shape
print(label_class.shape)</code></pre>
<pre><code>위의 출력 결과는 각각 아래와 같습니다.

(256, 256, 3)
(65536, 3)
(65536,)
(256, 256)</code></pre><br/>

<h3 id="16-데이터셋-정의하기">1.6 데이터셋 정의하기</h3>
<pre><code class="language-python">class CityscapeDataset(Dataset):

  def __init__(self, image_dir, label_model):
    self.image_dir = image_dir
    self.image_fns = os.listdir(image_dir)
    self.label_model = label_model

  def __len__(self) :
    return len(self.image_fns)

  def __getitem__(self, index) :
    image_fn = self.image_fns[index]
    image_fp = os.path.join(self.image_dir, image_fn)
    image = Image.open(image_fp)
    image = np.array(image)
    cityscape, label = self.split_image(image)
    label_class = self.label_model.predict(label.reshape(-1, 3)).reshape(256, 256)
    label_class = torch.Tensor(label_class).long()
    cityscape = self.transform(cityscape)
    return cityscape, label_class

  def split_image(self, image) :
    image = np.array(image)
    cityscape, label = image[ : , :256, : ], image[ : , 256: , : ]
    return cityscape, label

  def transform(self, image) :
    transform_ops = transforms.Compose([
                  transforms.ToTensor(),
                        transforms.Normalize(mean = (0.485, 0.56, 0.406), std = (0.229, 0.224, 0.225))
    ])
    return transform_ops(image)            </code></pre>
<p><a href="https://pytorch.org/docs/stable/generated/torch.Tensor.long.html#torch.Tensor.long">Tensor.long()</a></p>
<br/>


<pre><code class="language-python">dataset = CityscapeDataset(train_dir, label_model)
print(len(dataset))

cityscape, label_class = dataset[0]
print(cityscape.shape)
print(label_class.shape)</code></pre>
<pre><code># 출력 결과
2975
torch.Size([3, 256, 256])
torch.Size([256, 256])</code></pre><p>위의 출력 결과를 통해, 학습 데이터가 2975개 있다는 것을 다시 한번 확인할 수 있습니다. 또한, cityscape과 label_class의 shape도 알 수 있습니다. cityscape의 경우 <strong>transforms.ToTensor()</strong>를 통과하여 [3, 256, 256]의 텐서 형태를 가지게 되는 것을 확인할 수 있습니다.
<br/></p>
<h3 id="17-u-net-모델-정의하기">1.7 U-Net 모델 정의하기</h3>
<p><a href="https://velog.io/@jarvis_geun/U-Net-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0in-depth">이전 포스팅</a>에서 다룬 U-Net 모델을 사용하여 <strong>Sementic Segmentation</strong>을 진행하겠습니다. 아래의 사진을 바탕으로 U-Net 모델을 만듭니다.
<a href="https://velog.io/@jarvis_geun/U-Net-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0in-depth"><img src="https://images.velog.io/images/jarvis_geun/post/3e74c2f1-a1e9-4248-b9fc-680fce3a0d8c/image.png" alt=""></a></p>
<pre><code class="language-python">class UNet(nn.Module):

    def __init__(self, num_classes):
        super(UNet, self).__init__()
        self.num_classes = num_classes
        self.contracting_11 = self.conv_block(in_channels=3, out_channels=64)
        self.contracting_12 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.contracting_21 = self.conv_block(in_channels=64, out_channels=128)
        self.contracting_22 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.contracting_31 = self.conv_block(in_channels=128, out_channels=256)
        self.contracting_32 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.contracting_41 = self.conv_block(in_channels=256, out_channels=512)
        self.contracting_42 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.middle = self.conv_block(in_channels=512, out_channels=1024)
        self.expansive_11 = nn.ConvTranspose2d(in_channels=1024, out_channels=512, kernel_size=3, stride=2, padding=1, output_padding=1)
        self.expansive_12 = self.conv_block(in_channels=1024, out_channels=512)
        self.expansive_21 = nn.ConvTranspose2d(in_channels=512, out_channels=256, kernel_size=3, stride=2, padding=1, output_padding=1)
        self.expansive_22 = self.conv_block(in_channels=512, out_channels=256)
        self.expansive_31 = nn.ConvTranspose2d(in_channels=256, out_channels=128, kernel_size=3, stride=2, padding=1, output_padding=1)
        self.expansive_32 = self.conv_block(in_channels=256, out_channels=128)
        self.expansive_41 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=3, stride=2, padding=1, output_padding=1)
        self.expansive_42 = self.conv_block(in_channels=128, out_channels=64)
        self.output = nn.Conv2d(in_channels=64, out_channels=num_classes, kernel_size=3, stride=1, padding=1)

    def conv_block(self, in_channels, out_channels):
        block = nn.Sequential(nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=3, stride=1, padding=1),
                                    nn.ReLU(),
                                    nn.BatchNorm2d(num_features=out_channels),
                                    nn.Conv2d(in_channels=out_channels, out_channels=out_channels, kernel_size=3, stride=1, padding=1),
                                    nn.ReLU(),
                                    nn.BatchNorm2d(num_features=out_channels))
        return block

    def forward(self, X):
        contracting_11_out = self.contracting_11(X) # [-1, 64, 256, 256]
        contracting_12_out = self.contracting_12(contracting_11_out) # [-1, 64, 128, 128]
        contracting_21_out = self.contracting_21(contracting_12_out) # [-1, 128, 128, 128]
        contracting_22_out = self.contracting_22(contracting_21_out) # [-1, 128, 64, 64]
        contracting_31_out = self.contracting_31(contracting_22_out) # [-1, 256, 64, 64]
        contracting_32_out = self.contracting_32(contracting_31_out) # [-1, 256, 32, 32]
        contracting_41_out = self.contracting_41(contracting_32_out) # [-1, 512, 32, 32]
        contracting_42_out = self.contracting_42(contracting_41_out) # [-1, 512, 16, 16]
        middle_out = self.middle(contracting_42_out) # [-1, 1024, 16, 16]
        expansive_11_out = self.expansive_11(middle_out) # [-1, 512, 32, 32]
        expansive_12_out = self.expansive_12(torch.cat((expansive_11_out, contracting_41_out), dim=1)) # [-1, 1024, 32, 32] -&gt; [-1, 512, 32, 32]
        expansive_21_out = self.expansive_21(expansive_12_out) # [-1, 256, 64, 64]
        expansive_22_out = self.expansive_22(torch.cat((expansive_21_out, contracting_31_out), dim=1)) # [-1, 512, 64, 64] -&gt; [-1, 256, 64, 64]
        expansive_31_out = self.expansive_31(expansive_22_out) # [-1, 128, 128, 128]
        expansive_32_out = self.expansive_32(torch.cat((expansive_31_out, contracting_21_out), dim=1)) # [-1, 256, 128, 128] -&gt; [-1, 128, 128, 128]
        expansive_41_out = self.expansive_41(expansive_32_out) # [-1, 64, 256, 256]
        expansive_42_out = self.expansive_42(torch.cat((expansive_41_out, contracting_11_out), dim=1)) # [-1, 128, 256, 256] -&gt; [-1, 64, 256, 256]
        output_out = self.output(expansive_42_out) # [-1, num_classes, 256, 256]
        return output_out</code></pre>
<br/>

<p><a href="https://velog.io/@gwkoo/%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%83%81%EC%86%8D-%EB%B0%8F-super-%ED%95%A8%EC%88%98%EC%9D%98-%EC%97%AD%ED%95%A0">super(UNet, self).<strong>init</strong>()</a>
<a href="https://pytorch.org/docs/stable/generated/torch.nn.Module.html">nn.Module</a>
<br/></p>
<pre><code class="language-python">model = UNet(num_classes=num_classes)</code></pre>
<pre><code class="language-python">data_loader = DataLoader(dataset, batch_size = 4)
print(len(dataset), len(data_loader))

X, Y = iter(data_loader).next()
print(X.shape)
print(Y.shape)</code></pre>
<pre><code># 출력결과
2975 744
torch.Size([4, 3, 256, 256])
torch.Size([4, 256, 256])</code></pre><pre><code class="language-python">Y_pred = model(X)
print(Y_pred.shape)</code></pre>
<pre><code>torch.Size([4, 10, 256, 256])</code></pre><p>원래 U-Net 논문에는 no padding이지만 링크의 저자는 padding을 추가해주었습니다. 또한 논문에서는 1x1 convolution이 마지막 레이어에 존재하지만 이 포스팅에서는 존재하지 않습니다. 추측하건대 논문의 이미지 크기와, 우리가 인식하려는 이미지의 크기가 다르기 때문에 약간의 변형을 해준 것 같습니다.
<br/></p>
<h4 id="171-u-net-모델-코드-수정">1.7.1 U-Net 모델 코드 수정</h4>
<p>논문에서 마지막 레이어에 1x1 convolution을 추가하였기 때문에, 궁금하여 이전의 소스코드에서 1x1 conv. layer를 추가해보았습니다. 수정한 코드는 아래와 같습니다.</p>
<pre><code class="language-python">class UNet(nn.Module):

    def __init__(self, num_classes):
        super(UNet, self).__init__()
        self.num_classes = num_classes
        self.contracting_11 = self.conv_block(in_channels=3, out_channels=64)
        self.contracting_12 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.contracting_21 = self.conv_block(in_channels=64, out_channels=128)
        self.contracting_22 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.contracting_31 = self.conv_block(in_channels=128, out_channels=256)
        self.contracting_32 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.contracting_41 = self.conv_block(in_channels=256, out_channels=512)
        self.contracting_42 = nn.MaxPool2d(kernel_size=2, stride=2)
        self.middle = self.conv_block(in_channels=512, out_channels=1024)
        self.expansive_11 = nn.ConvTranspose2d(in_channels=1024, out_channels=512, kernel_size=3, stride=2, padding=1, output_padding=1)
        self.expansive_12 = self.conv_block(in_channels=1024, out_channels=512)
        self.expansive_21 = nn.ConvTranspose2d(in_channels=512, out_channels=256, kernel_size=3, stride=2, padding=1, output_padding=1)
        self.expansive_22 = self.conv_block(in_channels=512, out_channels=256)
        self.expansive_31 = nn.ConvTranspose2d(in_channels=256, out_channels=128, kernel_size=3, stride=2, padding=1, output_padding=1)
        self.expansive_32 = self.conv_block(in_channels=256, out_channels=128)
        self.expansive_41 = nn.ConvTranspose2d(in_channels=128, out_channels=64, kernel_size=3, stride=2, padding=1, output_padding=1)
        self.expansive_42 = self.conv_block(in_channels=128, out_channels=64)
        self.output = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1, padding=1)
    # 1x1 convolution layer 추가
        self.output1 = nn.Conv2d(in_channels=64, out_channels=num_classes, kernel_size=1, stride=1, padding=1)

    def conv_block(self, in_channels, out_channels):
        block = nn.Sequential(nn.Conv2d(in_channels=in_channels, out_channels=out_channels, kernel_size=3, stride=1, padding=1),
                                    nn.ReLU(),
                                    nn.BatchNorm2d(num_features=out_channels),
                                    nn.Conv2d(in_channels=out_channels, out_channels=out_channels, kernel_size=3, stride=1, padding=1),
                                    nn.ReLU(),
                                    nn.BatchNorm2d(num_features=out_channels))
        return block

    def forward(self, X):
        contracting_11_out = self.contracting_11(X) # [-1, 64, 256, 256]
        contracting_12_out = self.contracting_12(contracting_11_out) # [-1, 64, 128, 128]
        contracting_21_out = self.contracting_21(contracting_12_out) # [-1, 128, 128, 128]
        contracting_22_out = self.contracting_22(contracting_21_out) # [-1, 128, 64, 64]
        contracting_31_out = self.contracting_31(contracting_22_out) # [-1, 256, 64, 64]
        contracting_32_out = self.contracting_32(contracting_31_out) # [-1, 256, 32, 32]
        contracting_41_out = self.contracting_41(contracting_32_out) # [-1, 512, 32, 32]
        contracting_42_out = self.contracting_42(contracting_41_out) # [-1, 512, 16, 16]
        middle_out = self.middle(contracting_42_out) # [-1, 1024, 16, 16]
        expansive_11_out = self.expansive_11(middle_out) # [-1, 512, 32, 32]
        expansive_12_out = self.expansive_12(torch.cat((expansive_11_out, contracting_41_out), dim=1)) # [-1, 1024, 32, 32] -&gt; [-1, 512, 32, 32]
        expansive_21_out = self.expansive_21(expansive_12_out) # [-1, 256, 64, 64]
        expansive_22_out = self.expansive_22(torch.cat((expansive_21_out, contracting_31_out), dim=1)) # [-1, 512, 64, 64] -&gt; [-1, 256, 64, 64]
        expansive_31_out = self.expansive_31(expansive_22_out) # [-1, 128, 128, 128]
        expansive_32_out = self.expansive_32(torch.cat((expansive_31_out, contracting_21_out), dim=1)) # [-1, 256, 128, 128] -&gt; [-1, 128, 128, 128]
        expansive_41_out = self.expansive_41(expansive_32_out) # [-1, 64, 256, 256]
        expansive_42_out = self.expansive_42(torch.cat((expansive_41_out, contracting_11_out), dim=1)) # [-1, 128, 256, 256] -&gt; [-1, 64, 256, 256]
        output_out = self.output(expansive_42_out) # [-1, 64, 256, 256] -&gt; [-1, 64, 256, 256]
        output_out1 = self.output(output_out) # [-1, num_classes, 256, 256]

        return output_out1</code></pre>
<br/>
<br/>

<h2 id="step-2-모델-학습">Step 2. 모델 학습</h2>
<pre><code class="language-python">batch_size = 4

epochs = 10
lr = 0.01

dataset = CityscapeDataset(train_dir, label_model)
data_loader = DataLoader(dataset, batch_size = batch_size)

model = UNet(num_classes = num_classes).to(device)

# 손실함수 정의
criterion = nn.CrossEntropyLoss()
# Optimizer 정의
optimizer = optim.Adam(model.parameters(), lr = lr)


step_losses = []
epoch_losses = []

for epoch in tqdm(range(epochs)) :
  epoch_loss = 0

  for X, Y in tqdm(data_loader, total = len(data_loader), leave = False) :
    X, Y = X.to(device), Y.to(device)
    optimizer.zero_grad()
    Y_pred = model(X)
    loss = criterion(Y_pred, Y)
    loss.backward()
    optimizer.step()
    epoch_loss += loss.item()
    step_losses.append(loss.item())
  epoch_losses.append(epoch_loss/len(data_loader))</code></pre>
<p><a href="https://github.com/tqdm/tqdm#table-of-contents">tqdm 라이브러리</a></p>
<p>학습을 통해 얻은 손실함수를 확인해보겠습니다.</p>
<pre><code class="language-python">print(len(epoch_losses))
print(epoch_losses)</code></pre>
<pre><code>출력 결과
10
[1.2644546631202902, 0.8764938149721392, 0.7952614437828782, 0.7531729845270034, 0.7165372273934785, 0.6903373579023987, 0.6792501089393451, 0.6337115100474768, 0.6319557054629249, 0.6149069263890226]</code></pre><p>학습을 통해 손실함수가 감소한 것을 확인할 수 있습니다.</p>
<p>이제 학습의 결과(training losses)를 그래프를 통해 확인해보겠습니다.</p>
<pre><code class="language-python">fig, axes = plt.subplots(1, 2, figsize=(10, 5))
axes[0].plot(step_losses)
axes[1].plot(epoch_losses)

plt.show()</code></pre>
<p><img src="https://images.velog.io/images/jarvis_geun/post/b731d78f-ef34-4aca-a8c7-7f294373d8b4/output.png" alt=""></p>
<p>왼쪽이 step_losses, 오른쪽이 epoch_losses의 결과 그래프입니다.
<br/></p>
<p><strong>모델 저장</strong>
이제 학습시킨 모델을 저장합니다.</p>
<pre><code class="language-python">model_name = &quot;UNet.pth&quot;
torch.save(model.state_dict(), root_path + model_name)</code></pre>
<br/>

<h2 id="step-3-모델-평가하기">Step 3. 모델 평가하기</h2>
<pre><code class="language-python">model_path = root_path + model_name
model_ = UNet(num_classes = num_classes).to(device)
model_.load_state_dict(torch.load(model_path))
</code></pre>
<p><a href="https://tutorials.pytorch.kr/beginner/saving_loading_models.html#state-dict">model_.load_state_dict( ) - 1</a>
<a href="https://stackoverflow.com/questions/48419626/pytorch-cant-load-cnn-model-and-do-prediction-typeerror-collections-orderedd">model_load_state_dict( ) - 2</a>
<a href="https://wayhome25.github.io/cs/2017/04/04/cs-04/">pickle 모듈</a>
<a href="https://devmason.tistory.com/53#:~:text=%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94(Deserialization)%20%3A%20%EC%A7%81%EB%A0%AC%ED%99%94%EB%90%9C,%EA%B0%9D%EC%B2%B4%EC%9D%98%20%ED%98%95%ED%83%9C%EB%A1%9C%20%EB%B3%B5%EC%9B%90%ED%95%9C%EB%8B%A4.">역직렬화</a></p>
<pre><code class="language-python">test_batch_size = 8
dataset = CityscapeDataset(val_dir, label_model)
data_loader = DataLoader(dataset, batch_size = test_batch_size)

X,Y = next(iter(data_loader))
X,Y = X.to(device), Y.to(device)
Y_pred = model_(X)
print(Y_pred.shape)
Y_pred = torch.argmax(Y_pred, dim=1)
print(Y_pred.shape)</code></pre>
<pre><code># 출력결과 =&gt; test_batch_size에 따라 8은 변할 수 있음
torch.Size([8, 64, 256, 256])
torch.Size([8, 256, 256])</code></pre><pre><code class="language-python">inverse_transform = transforms.Compose([
    transforms.Normalize((-0.485/0.229, -0.456/0.224, -0.406/0.225), (1/0.229, 1/0.224, 1/0.225))
])</code></pre>
<pre><code class="language-python">fig, axes = plt.subplots(test_batch_size, 3, figsize=(3*5, test_batch_size*5))

iou_scores = []

for i in range(test_batch_size):

    landscape = inverse_transform(X[i]).permute(1, 2, 0).cpu().detach().numpy()
    label_class = Y[i].cpu().detach().numpy()
    label_class_predicted = Y_pred[i].cpu().detach().numpy()

    # IOU score
    intersection = np.logical_and(label_class, label_class_predicted)
    union = np.logical_or(label_class, label_class_predicted)
    iou_score = np.sum(intersection) / np.sum(union)
    iou_scores.append(iou_score)

    axes[i, 0].imshow(landscape)
    axes[i, 0].set_title(&quot;Landscape&quot;)
    axes[i, 1].imshow(label_class)
    axes[i, 1].set_title(&quot;Label Class&quot;)
    axes[i, 2].imshow(label_class_predicted)
    axes[i, 2].set_title(&quot;Label Class - Predicted&quot;)

plt.show()</code></pre>
<p><a href="https://sanghyu.tistory.com/3#:~:text=%EC%9D%80%20%EA%B7%B8%EB%A0%87%EC%A7%80%20%EC%95%8A%EB%8B%A4.-,transpose()%20vs%20permute(),-permute()%EC%99%80%20transpose">pytorch의 permute( ) - 1</a>
<a href="https://pytorch.org/docs/stable/generated/torch.Tensor.permute.html?highlight=torch%20permute#torch.Tensor.permute">pytorch의 permute( ) - 2</a></p>
<pre><code># 출력결과
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).</code></pre><p><img src="https://images.velog.io/images/jarvis_geun/post/2978c23c-12b3-4368-be92-4071b9f36d8f/output.png" alt=""></p>
<p>test_batch_size = 8로 설정하여 8개의 이미지에 대한 결과가 출력되었습니다.</p>
<h4 id="311-궁금한-것">3.1.1 궁금한 것</h4>
<pre><code class="language-python"># dim = 1 -&gt; dim = 0으로 변경해봄
Y_pred = torch.argmax(Y_pred, dim=0)</code></pre>
<p>위와 같이 이전의 코드를 변경하면 아래와 같은 이미지를 얻을 수 있습니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/bb90c55a-2825-47a3-a42d-f1e99d4f4466/output.png" alt=""></p>
<p>dim = 0과 dim = 1의 차이로 인해 정확도는 71.9%로 떨어졌다. 아직 이러한 결과가 발생하는 원인에 대해서 파악하지 못했습니다. 어째서 dim = 1로 주었는지 조금 더 생각해봐야겠습니다.</p>
<h2 id="step-4-iou-score">Step 4. IOU Score</h2>
<p>지금까지 학습시킨 모델에 대하여, 정확도를 산출해보겠습니다. 흔히 알려진 평가지표인, IOU를 기준으로 정확도를 계산하겠습니다.</p>
<pre><code class="language-python">print(sum(iou_scores) / len(iou_scores))</code></pre>
<pre><code># 출력결과
0.9954032897949219</code></pre><p>우리가 학습시킨 모델의 IOU는 99.5%가 넘는 정확도를 보여줍니다!</p>
<p>+ 추가
batch_size와 test_batch_size 등에 따라 정확도가 달라지는 것을 확인하였습니다. 때문에 이 포스팅의 내용처럼 99프로가 나오지 않을 수 있습니다. 하지만 대부분 90프로 이상의 성능을 보여줍니다.</p>
<hr>
<p>마지막으로, 긴 글 읽어주셔서 고맙다는 말씀드리며 딥러닝을 공부한지 얼마되지 않아 틀린 것이 있더라도 너그러이 이해해주시기 바랍니다~! 수정해야할 사항은 언제든지 댓글로 알려주세요!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Github 사용법]]></title>
            <link>https://velog.io/@jarvis_geun/Github-%EC%82%AC%EC%9A%A9%EB%B2%95</link>
            <guid>https://velog.io/@jarvis_geun/Github-%EC%82%AC%EC%9A%A9%EB%B2%95</guid>
            <pubDate>Tue, 24 Aug 2021 01:02:07 GMT</pubDate>
            <description><![CDATA[<p>개발자에게 있어 가장 필요하다고 알려진, &quot;Github(깃허브)&quot;를 사용하는 방법에 대해 이야기해보려 합니다. 사실은 제가 깃허브를 사용하는 방법을 몰라 유튜브를 참고하여 공부한 내용을 정리한 것입니다. 틀린 내용이 있다면 언제든지 댓글로 알려주시기 바랍니다! 지금부터 기술할 거의 대부분의 내용은, 유튜브 &quot;<a href="https://www.youtube.com/playlist?list=PLuHgQVnccGMDWjb0TWItMCfDWDs8Y3Oo7">생활코딩</a>&quot;님의 강의를 바탕으로 작성하였음을 알려드립니다(제 글보다는 생활코딩님의 강의를 보시는 것이 더 도움이 될 것입니다...).</p>
<hr>
<h3 id="1-깃허브-가입">1. 깃허브 가입</h3>
<p>제일 먼저 깃허브에 회원가입 합니다. <a href="github.com">깃허브 홈페이지</a>에 들어가셔서 가입을 진행합니다. 회원가입의 자세한 과정은 <a href="https://goddaehee.tistory.com/218">링크</a>로 남기겠습니다.
<br/></p>
<h3 id="2-저장소repository-생성">2. 저장소(repository) 생성</h3>
<p><img src="https://images.velog.io/images/jarvis_geun/post/8f151206-bec8-484f-a5ee-f6c99ad9ea92/image.png" alt="">
가입을 완료했으면 로그인 후 깃허브 홈페이지에 들어갑니다. 홈페이지에 들어가면 위와 같은 화면이 보입니다. 위의 화면의 좌측에서 &quot;Create repository&quot;를 클릭합니다. 이는 깃허브에 새로운 저장소를 생성하는 과정입니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/5686e984-6376-42e4-85c3-fb30b78ee47c/image.png" alt="">
이 후, 위의 사진처럼 저장소를 생성하기 위한 화면이 나타납니다. 이 페이지에서는 어떤 이름(repository name)으로 생성할지, 공개여부(Public / Private) 등을 결정하게 됩니다. 우리는 처음 시작하는 단계이므로 간단하게 Repository name에 <strong>github_practice</strong>라는 이름으로 생성해줍니다.<img src="https://images.velog.io/images/jarvis_geun/post/4db311b5-e176-4f5c-8c37-def5819ba0d1/image.png" alt="">또한, 공개여부는 <strong>Private</strong>으로 선택합니다(공개여부는 자신의 상황에 맞게 진행합니다). 이외의 다른 사항은 무시하고 <strong>Create repository</strong>를 클릭합니다.<img src="https://images.velog.io/images/jarvis_geun/post/3205f23c-8ee8-4d64-85f4-012b91c3ce7d/image.png" alt="">
짜잔!! 드디어 첫 깃허브 저장소를 생성했습니다! 위의 화면이 보인다면 정상적으로 저장소를 생성한 것입니다. 이제 이 저장소에 파일을 생성해보겠습니다.<img src="https://images.velog.io/images/jarvis_geun/post/9b5fc26e-6263-49dd-bc7b-168dfdef0391/image.png" alt="">위의 사진에서 Quick setup 부분에서 파란색 하이퍼링크로 <strong>creating a new file</strong> 를 클릭해줍니다.<img src="https://images.velog.io/images/jarvis_geun/post/f55ffcac-e21f-4986-a322-ac6deebafddd/image.png" alt="">이제 파일을 만듭니다. 자신이 원하는 이름으로 텍스트파일을 작성합니다. 저의 경우, &quot;<strong>file1.txt</strong>&quot;라는 이름의 텍스트 파일을 작성해보겠습니다. 파일의 이름은 &quot;<strong>Name your file...</strong>&quot;란에 기입합니다.<br/></p>
<p>다음은 파일의 내용을 작성합니다.
<img src="https://images.velog.io/images/jarvis_geun/post/5168b86a-8e9f-4f5c-a89f-f9562b7cef4f/image.png" alt="">
저는 &quot;<strong>Hello Github!</strong>&quot;를 적어보았습니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/6ae78fdb-718a-4610-ae38-2ad9589b290b/image.png" alt=""></p>
<p>Commit New file에는 자신이 무엇을 추가(변경) 하였는지를 적어줍니다. 이렇게 할 경우, 나중에 파일을 보았을 때 어떤 내용을 변경했는지 알 수 있게 됩니다.<img src="https://images.velog.io/images/jarvis_geun/post/a014a63d-1160-4baa-8303-bf0fa655c163/image.png" alt="">
작성을 완료했으면 <strong>Commit new file(초록색 상자)</strong>을 클릭합니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/9876a182-8285-48fb-a117-a9b4570026a3/image.png" alt="">
오! 파일이 생성되었습니다. file1.txt를 클릭해서 들어가보면 해당내용이 잘 입력되어있는 것을 확인할 수 있습니다. 위의 사진에서 주의깊게 볼 것은 오른쪽의 초록색 상자(Code)의 바로 밑에 <strong>1 commit</strong>이라고 적혀있는 부분입니다. 클릭해서 들어가봅니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/879a38d7-209b-46a0-b0b4-57199883b437/image.png" alt=""></p>
<p>저장소에서 어떠한 내용이 변경되고 추가되었는지를 확인할 수 있습니다. 해당 내용을 클릭해보면 무엇이 변경되었고 추가되었는지를 확인할 수 있습니다. 저는 여기서 정말 감탄했습니다. 자신이 무엇을 변경했는지를 정말 간결하게 확인할 수 있다는 것이 신기했습니다. 깃허브를 만든 사람들은 정말 대단한 것 같네요!
<img src="https://images.velog.io/images/jarvis_geun/post/6c17fef7-e601-41af-8835-c43d0c7ac20e/image.png" alt=""></p>
<h2 id="3-저장소에-파일-올리기upload-files">3. 저장소에 파일 올리기(Upload files)</h2>
<p><img src="https://images.velog.io/images/jarvis_geun/post/a0891b67-d5c5-45d6-94e3-2bc2d6e6949f/image.png" alt="">
이제는 로컬환경에 있는 파일을 깃허브 저장소에 올려보겠습니다.</p>
<ol>
<li><p><strong>&lt;&gt; Code</strong>를 눌러 위의 화면으로 이동합니다. </p>
</li>
<li><p><strong>Add file</strong>을 누르고 <strong>Upload files</strong>를 선택합니다.</p>
</li>
</ol>
<p>위의 과정을 완료하면 아래의 화면이 보일 것입니다.
<img src="https://images.velog.io/images/jarvis_geun/post/98359413-f334-4558-8b39-727eab76cf9c/image.png" alt="">
그러면 자신이 올리고자 하는 파일을 드래그하거나 선택해서 올려주면 됩니다. 저의 경우, 메모장에서 텍스트파일을 생성하여 올려주었습니다. 자신이 올리고자 하는 파일을 올려주면 됩니다.</p>
<hr>
<p>다음 포스팅은 <strong>git 명령어</strong>를 이용하여 깃허브에 접근하는 방법에 대해 설명해보려 합니다(사실 설명이 아닌 동영상 내용을 복습하는 것일뿐이지만..). 그럼 다음 포스팅에서 만나요!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[U-Net 톺아보기(in-depth)]]></title>
            <link>https://velog.io/@jarvis_geun/U-Net-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0in-depth</link>
            <guid>https://velog.io/@jarvis_geun/U-Net-%ED%86%BA%EC%95%84%EB%B3%B4%EA%B8%B0in-depth</guid>
            <pubDate>Tue, 24 Aug 2021 00:57:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>톺아보다 : 틈이 있는 곳마다 모조리 더듬어 뒤지면서 찾다(순우리말).</p>
</blockquote>
<p>이 글이 저의 Velog 첫 글이랍니다! 무슨 내용으로 글을 올려볼까 생각하다가, 최근에 공부하게 된 U-Net에 대해 정리해서 올려보려 합니다. 학부 인턴생으로 딥러닝을 제대로 공부한지, 2021.08.14 기준으로 한달이 채 되지 않았다는 점을 미리 알려드립니다(틀린 내용이 있을 수 있다는 밑밥이라는..). 그렇기 때문에 틀린 내용이 있어도 너그러이 봐주시면 고맙겠습니다!</p>
<p>일단 먼저 제가 참고한 링크를 맨 아래에 적어놓겠습니다. 링크 덕분에 정말 많은 도움 받았습니다. 작성자분들께 고맙다는 말씀드립니다. 또한, 이 글의 모든 사진은 <a href="https://arxiv.org/abs/1505.04597">U-Net 논문</a>의 사진을 공부 내용에 맞게 재창작 했으며 인용문 또한 동일한 논문에서 발췌하였다는 것을 알려드립니다.</p>
<hr>
<h1 id="u-net-architecture">U-Net architecture</h1>
<p>U-Net은 본래 Biomedical 분야를 위해 만들어진 FCN 기반의 모델입니다. 논문의 제목은 아래와 같습니다.</p>
<blockquote>
<p><em>U-Net : Convolutional Networks for Biomedical Image Segmentation</em></p>
</blockquote>
<p>위의 제목을 보면 알 수 있듯이 논문의 저자는 U-Net을 Biomedical 분야에 적용하기 위해 만든 것을 알 수 있습니다. 그 결과로 ISBI cell tracking challenge 2015라는 biomedical 분야의 대회에서 우승할 수 있었습니다. 또한, U-Net은 <a href="https://eehoeskrap.tistory.com/183">End-to-End</a> 방식의 Fully-Convolutional Network(=FCN)를 기반으로 한 모델입니다.</p>
<p>본격적으로 U-Net의 구조를 알아보기에 앞서 간략한 이미지를 먼저 보여드리겠습니다.</p>
<p><img src="https://images.velog.io/images/jarvis_geun/post/fc5e6047-d13f-4588-a63c-03e2b5dc1958/image.png" alt=""></p>
<p>위의 사진에서 다른 내용은 다 제쳐두고 이거 하나만 기억하시면 됩니다. 빨간색 선을 기준으로 왼쪽은 Contracting Path, 오른쪽은 Expanding Path 입니다.</p>
<p>Contracting path를 한글로 번역해보면 &quot;수축 경로&quot; 정도의 의미가 되겠습니다. 반대로 Expanding path는 &quot;확장 경로&quot;라고 보시면 됩니다.</p>
<p>단어의 의미에서 알 수 있듯이 기존에 있던 이미지를 수축해주는 단계가 Contracting Path, 수축된 이미지를 확장해주는 단계가 Expanding Path입니다.</p>
<p>이러한 기본개념을 기억하시고 글을 읽으시면 더 원활한 이해가 될 것이라 생각합니다.</p>
<hr>
<h2 id="1-introduction">1. Introduction</h2>
<p>이제 본격적으로 U-Net에 대해 설명드리겠습니다.</p>
<h3 id="sliding-window-setup">Sliding window setup</h3>
<p>U-Net의 저자는 기존에 있던 sliding window 방식에 대해 언급하였습니다. Sliding window에 관한 자세한 설명은 <a href="https://ramees.tistory.com/52">링크</a>를 참고해주시기 바랍니다. Sliding window 방식은 이전의 방법보다 두가지 측면에서 더 좋은 성과를 보여주었습니다.</p>
<blockquote>
<p><em>First, this network can localize.</em></p>
</blockquote>
<blockquote>
<p><em>Secondly, the training data in terms of patches is much larger than the number of training images.</em></p>
</blockquote>
<p>즉, localize를 할 수 있으며 training data의 patch가 training image 보다 훨씬 많다는 장점을 가졌습니다. 이러한 장점에도 불구하고 두가지의 단점(=Two drawbacks of sliding-window)이 존재합니다.</p>
<ol>
<li><strong>매우 느리다(=quite slow)</strong>는 것입니다. Sliding-window 방식의 경우 아래의 사진처럼, 검증이 완료된 patch에 대해 중복(=redundancy)해서 다음 patch를 검증하기 때문입니다. </li>
</ol>
<p><a href="https://m.blog.naver.com/PostView.nhn?blogId=worb1605&amp;logNo=221333597235&amp;proxyReferer=https:%2F%2Fwww.google.com%2F%20%EC%B6%9C%EC%B2%98:%20https:%2F%2Fmylifemystudy.tistory.com%2F87%20%5BENCAPSULATION%5D&amp;view=img_4#:~:text=%EA%B8%B0%EC%A1%B4%20%EB%B0%A9%EC%8B%9D%EC%9D%98%20sliding%20window"><img src="https://images.velog.io/images/jarvis_geun/post/ca865d67-b76f-45ac-9d83-aafa6f7e3997/image.png" alt=""></a></p>
<ul>
<li><strong>해결방법</strong>
아래의 사진처럼 검증이 완료된 patch에 대해서는, 검증을 수행하지 않고 다음 patch로 넘어가게 됩니다. 이 덕분에 기존의 Sliding window 방식보다 더 빠른 속도로 patch를 검증할 수 있게 됩니다.</li>
</ul>
<p><a href="https://m.blog.naver.com/PostView.nhn?blogId=worb1605&amp;logNo=221333597235&amp;proxyReferer=https:%2F%2Fwww.google.com%2F%20%EC%B6%9C%EC%B2%98:%20https:%2F%2Fmylifemystudy.tistory.com%2F87%20%5BENCAPSULATION%5D&amp;view=img_4#:~:text=%EB%B0%A9%EC%8B%9D%EC%9D%98%20sliding%20window-,U-net%EC%9D%98%20patch%20%ED%83%90%EC%83%89%20%EB%B0%A9%EC%8B%9D,-%EB%91%90%EB%B2%88%EC%A7%B8%EB%A1%9C%EB%8A%94%2C%20Trade%20off"><img src="https://images.velog.io/images/jarvis_geun/post/64682170-d70a-4180-9484-4b51d8c3d1fb/image.png" alt=""></a></p>
<ol start="2">
<li></li>
</ol>
<p><strong><a href="https://light-tree.tistory.com/75#:~:text=%EC%B6%9C%EB%A0%A5%EC%9D%84%20%ED%95%9C%20%EC%85%88%EC%9D%B4%EB%8B%A4.-,Localization,-Localization%EC%9D%B4%EB%9E%80%20%5B1">Localization</a>의 정확도와 Context의 사용 간에 &quot;<a href="https://www.google.com/search?q=trade-off+%EB%9C%BB&amp;ei=oT0XYcWdOYfv-QavvoG4Bg&amp;oq=trade-off+%EB%9C%BB&amp;gs_lcp=Cgdnd3Mtd2l6EAMyCggAEIAEEEYQ-QE6BwgAEEcQsAM6BQgAEIAESgQIQRgAUM0sWJg_YJlAaAJwAngAgAGaAYgBsAWSAQMwLjWYAQCgAQHIAQLAAQE&amp;sclient=gws-wiz&amp;ved=0ahUKEwiF8sCBzq_yAhWHd94KHS9fAGcQ4dUDCA4&amp;uact=5#:~:text=%EC%99%84%EC%A0%84%20%EA%B3%A0%EC%9A%A9%EA%B3%BC%20%EB%AC%BC%EA%B0%80%20%EC%95%88%EC%A0%95%EC%9D%98%20%EA%B4%80%EA%B3%84.%20%EA%B3%A7%2C%20%EC%8B%A4%EC%97%85%EB%A5%A0(%E5%A4%B1%E6%A5%AD%E7%8E%87)%EC%9D%84%20%EC%A4%84%EC%9D%B4%EB%A9%B4%20%EB%AC%BC%EA%B0%80%EA%B0%80%20%EC%83%81%EC%8A%B9%ED%95%98%EA%B3%A0%2C%20%EB%AC%BC%EA%B0%80%EB%A5%BC%20%EC%95%88%EC%A0%95%EC%8B%9C%ED%82%A4%EB%A9%B4%20%EC%8B%A4%EC%97%85%EB%A5%A0%EC%9D%B4%20%EB%86%92%EC%95%84%EC%A7%84%EB%8B%A4%EB%8A%94%20%EB%AA%A8%EC%88%9C%EC%A0%81%20%EA%B4%80%EA%B3%84%EB%A5%BC%20%EC%9D%B4%EB%A5%B4%EB%8A%94%20%EB%A7%90%EC%9E%84.">trade-off</a>&quot; 가 발생합니다.</strong></p>
<p> Context란, 번역하면 &quot;문맥, 전후 사정, 맥락&quot; 등으로 이해할 수 있습니다. 우리가 글을 읽을 때 단어 하나 하나의 의미를 통해 글의 전체 맥락을 파악하듯이, 이미지 처리에서도 마찬가지입니다. 패치를 이용하여 이미지의 일부분을 보고, 전체 이미지의 맥락을 파악하게 됩니다.</p>
<ul>
<li><p>Larger patches의 경우, 더 많은 양의 max-pooling layer를 필요로 하는데 이는 곧 localization의 정확도를 떨어뜨리게 됩니다.</p>
<p>⇒ patch size가 커지면 더 넓은 범위의 이미지를 한 번에 인식할 수 있어 context 파악에 큰 효과가 있습니다. 하지만 넓은 범위를 인식하게 되므로 localization의 정확도에서는 성능이 떨어지게 됩니다.</p>
</li>
<li><p>반대로, small patches는 <strong>little</strong> context만을 관찰하게 됩니다.</p>
<p>⇒ 너무 작은 patch size로 인하여 좁은 범위의 이미지만을 관찰하게 됩니다. 이는 곧, context를 파악하는데 어려움을 겪게 됩니다.</p>
</li>
</ul>
<ul>
<li><p>** 해결방법**
논문의 reference 4, 11번 논문에서 확인할 수 있듯이, 여러 layer의 output을 동시에 검증하게 되면 localization과 context 문제를 해결할 수 있게 됩니다.</p>
<p>⇒ Contracting path에서 이미지의 context를 파악합니다. Expanding path에서는 feature map을 upsampling한 후에 Contracting path의 context와 결합하여 localization의 정확도를 높이게 됩니다.</p>
<p>∴ <strong>context (Contracting path) + upsampling (Expanding path) ⇒ accuracy of localization ↑ (combine)</strong></p>
</li>
</ul>
<hr>
<h2 id="2-u-net-architecture">2. U-Net architecture</h2>
<p><img src="https://images.velog.io/images/jarvis_geun/post/7753a9bf-6b3e-40ec-ad3a-6cb081459517/image.png" alt=""></p>
<p>위의 사진은 논문에서 가져왔습니다. 위의 사진에 대한 설명은 아래와 같습니다.</p>
<ul>
<li>Blue box : multi-channel feature map</li>
<li>White box : 복사된(=copied) feature map</li>
<li>Top of the box : 채널의 수 표기</li>
<li>The arrow : different operation</li>
<li>Lower left edge of the box : X-Y size<br/>

</li>
</ul>
<p>U-Net의 특징에 대해 설명해보겠습니다.</p>
<h3 id="21-no-fully-connected-layer--⇒--연산속도-↑">2.1 <strong>No Fully Connected Layer</strong>  ⇒  연산속도 ↑</h3>
<p>우선, U-Net의 경우 Fully connected layer가 아닌 Fully convolutional layer를 기반으로 하기 때문에, FC layer가 있는 네트워크보다 상대적으로 연산속도가 빠릅니다.
<br/><br/></p>
<h3 id="22-overlap-tile-strategy">2.2 <strong>Overlap-tile strategy</strong></h3>
<p><img src="https://images.velog.io/images/jarvis_geun/post/2ece52a9-7e34-441e-81df-67667c5376bd/image.png" alt=""></p>
<blockquote>
<p>Overlap-tile strategy for seamless segmentation of arbitrary large images</p>
</blockquote>
<p> <a href="https://joungheekim.github.io/2020/09/28/paper-review/#:~:text=%EC%A6%9D%EA%B0%95%20%EB%B0%A9%EB%B2%95%EC%9D%84%20%ED%99%9C%EC%9A%A9%ED%95%A9%EB%8B%88%EB%8B%A4.-,Overlap-tile%20strategy,-Overlap%20Tile%20%EC%98%88%EC%8B%9C">Overlap-tile strategy</a>에 관한 설명은 링크를 참고하시면 되겠습니다.</p>
<p> 저자는 U-Net에 padding을 추가하지 않았습니다. 이로 인해 Input size가 Output size보다 더 크게 되는데, 위의 사진처럼 파란색 박스로 된 Input 이미지에 대해 노란색 박스로 된 Output  이미지의 결과를 얻게 됩니다.
 <br/><br/></p>
<h3 id="23-missing-input-data">2.3 <strong>Missing input data</strong></h3>
<p>위에서 설명했듯이, padding을 하지 않으므로 데이터 손실이 발생하게 됩니다.
<br/></p>
<ul>
<li><strong>해결방법</strong>
저자는 이에 대한 해결방법으로 <strong>&quot;Mirroring extrapolation&quot;</strong>이라는 방법을 사용하였습니다.</li>
</ul>
<p><a href="https://medium.com/@msmapark2/u-net-%EB%85%BC%EB%AC%B8-%EB%A6%AC%EB%B7%B0-u-net-convolutional-networks-for-biomedical-image-segmentation-456d6901b28a"> <img src="https://images.velog.io/images/jarvis_geun/post/d581b940-7378-48a9-a6b8-8773fe4b1725/image.png" alt=""></a>
위의 사진을 보면 알 수 있듯이, 점선을 기준으로 mirroring 하는 것을 의미합니다. 즉, 거울에 반사되는 것처럼, 점선을 기준으로 반사 및 복제 되는 것을 의미합니다. 이 덕분에 손실된 이미지 데이터를 채울 수 있게 됩니다.<br/><br/>
<br/></p>
<h3 id="24--contracting-path-vs-expanding-path">2.4  <strong>Contracting path vs Expanding path</strong></h3>
<p><img src="https://images.velog.io/images/jarvis_geun/post/4be97525-4128-41f3-b3e3-aca290e21d58/image.png" alt=""></p>
<ul>
<li><p>위의 사진에서, Contracting path의 풀링 바로 전 단계의 feature map을 copy한 것을 확인할 수 있습니다. Copy한 것을 Expanding path의 feature map에 concatenate(연결) 한 결과가, 흰색 박스(white box)입니다.</p>
<p>이 때, 손실된 데이터가 있으므로 Contracting path의 feature map의 크기가 Expanding path의 feature map 크기보다 크게 됩니다. 따라서 이미지의 크기를 맞춰주기 위해, 가운데 부분을 적당한 크기로 crop을 해주어서 연결합니다.</p>
<ul>
<li><p>Expanding path의 Upsampling 단계 이후에, Copy &amp; Crop &amp; Concatenate + Paste 을 단계마다 반복해서 수행합니다. 이로 인해 2장의 feature map이 겹쳐진 상태로 다음 단계인 Convolution을 거치게 됩니다.</p>
</li>
<li><p>Channel 수를 조정하기 위해서 1 x 1 Convolution 연산을 마지막 layer에서 수행하게 됩니다. 즉,
64개의 channel ⇒ 2개의 channel</p>
</li>
</ul>
</li>
</ul>
<p>Data augmentation에 대한 내용도 있으나, 생략하도록 하겠습니다.</p>
 <br/>
---
## 3. Experiments
![](https://images.velog.io/images/jarvis_geun/post/1af1c223-42eb-4490-89e8-dba420b8739c/image.png)

<blockquote>
<p>Ranking on the EM segmentation challenge [14] (march 6th, 2015), sorted
by warping error</p>
</blockquote>
<p><img src="https://images.velog.io/images/jarvis_geun/post/64f04589-7289-41c9-8866-e898ade5e8ee/image.png" alt=""></p>
<blockquote>
<p>Segmentation results (IOU) on the ISBI cell tracking challenge 2015</p>
</blockquote>
<p>이러한 구조를 바탕으로 결과적으로, 대회에서 이전의 네트워크보다 훨씬 더 좋은 성능을 내 우승을 했습니다.
<br/></p>
<hr>
<p>참고자료</p>
<ul>
<li><a href="https://velog.io/@guide333/U-Net-%EC%A0%95%EB%A6%AC">링크 1</a></li>
<li><a href="https://medium.com/@msmapark2/u-net-%EB%85%BC%EB%AC%B8-%EB%A6%AC%EB%B7%B0-u-net-convolutional-networks-for-biomedical-image-segmentation-456d6901b28a">링크 2</a></li>
<li><a href="https://m.blog.naver.com/9709193/221979612209">링크 3</a></li>
<li><a href="https://lmb.informatik.uni-freiburg.de/people/ronneber/u-net/">링크 4</a></li>
<li><a href="https://arxiv.org/abs/1505.04597">논문</a></li>
</ul>
]]></description>
        </item>
    </channel>
</rss>