<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Scope's log</title>
        <link>https://velog.io/</link>
        <description>개발자이고 싶은 미니 코딩쟁이... TIL 글을 주로 올립니다.</description>
        <lastBuildDate>Sun, 05 Jan 2025 15:50:59 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Scope's log</title>
            <url>https://velog.velcdn.com/images/scope_hyena/profile/34296fbd-98b8-44fb-8666-71b0de7b7008/social_profile.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. Scope's log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/scope_hyena" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[PICO-8 Fanzine 이슈 1 정리 (1) - 스쿼시]]></title>
            <link>https://velog.io/@scope_hyena/PICO-8-Fanzine-1-1</link>
            <guid>https://velog.io/@scope_hyena/PICO-8-Fanzine-1-1</guid>
            <pubDate>Sun, 05 Jan 2025 15:50:59 GMT</pubDate>
            <description><![CDATA[<p>주: 직접 해보면서 정리하고 있기 때문에 실제 Fanzine 내용과는 차이가 있을 수 있음</p>
<h2 id="스쿼시">스쿼시</h2>
<p>고전 게임 <strong><code>PONG</code></strong> 과 같은 스타일의 스쿼시 게임을 만들어보자.</p>
<blockquote>
<p>스쿼시: 벽에 대고 라켓으로 테니스 공을 쳐서 받아내는 1인용 구기 운동</p>
</blockquote>
<p>PICO-8을 처음 기동하면, <code>명령줄 모드(Command mode)</code> 에서 시작하게 된다.
여기서 ESC 키를 누르면 <code>에디터(Editor) 모드</code> 로 들어가게 되며, 여기에서 게임을 만들 수 있다.</p>
<p>게임을 실행시키려면 <code>ESC</code> 키를 눌러 명령줄 모드로 돌아간 뒤 <code>run</code> 을 입력하고 <code>enter</code> 키를 누른다.
명령줄 모드로 돌아가려면 언제든지 <code>ESC</code> 키를 눌러 돌아갈 수 있다. </p>
<p>명령줄 모드에 있을 때, 다음을 입력하여 게임을 저장할 수 있다.</p>
<pre><code class="language-shell">save GAME_NAME</code></pre>
<p>게임을 로드하기 위해서는 다음을 입력한다.</p>
<pre><code class="language-shell">load GAME_NAME</code></pre>
<p>이 게임의 경우, 스쿼시(Squasy)라는 이름을 사용할 것이므로, 다음과 같이 입력한다.</p>
<pre><code class="language-shell">save squashy</code></pre>
<blockquote>
<p>주: PICO-8은 대/소문자 구분을 지원하지 않는다. 따라서 모든 알파벳은 Shift 없이 입력하도록 한다. Shift 입력에 대해서는 뒤에서 자세히 다루도록 한다.</p>
</blockquote>
<p>이제 PICO-8에서 첫 번째 게임을 만들어보도록 하자.</p>
<h3 id="1-움직이는-라켓">1. 움직이는 라켓</h3>
<p>움직이는 방망이(라켓)를 만들어보도록 하자.</p>
<p>PICO-8은 Lua 5를 기반으로 약간의 커스텀이 가해진 스크립트 언어를 사용한다. 
이제 다음 코드를 입력한다.</p>
<pre><code class="language-lua">-- paddle
padx=52
pady=122
padw=24
padh=4

function mov_pad()
    if btn(⬅️) then padx-=3
    elseif btn(➡️) then padx+=3 end
end

function _update()
    mov_pad()
end

function _draw()
    cls(0)
    -- draw paddle
    rectfill(padx,pady,padx+padw,pady+padh,6)
end</code></pre>
<p>입력을 완료했다면 저장하도록 하자.</p>
<blockquote>
<p>주: 저장 단축키는 시스템 상관없이 <code>CTRL + S</code>, 실행 단축키는 <code>CTRL + R</code>.</p>
</blockquote>
<h4 id="코드-해설">코드 해설</h4>
<pre><code class="language-lua">function _update()</code></pre>
<p>1초당 30번씩 실행되는 함수이다. 이 함수 내에 들어있는 내용대로 값을 업데이트한다.
즉, PICO-8은 기본적으로 30fps로 실행되는 게임 엔진이다.</p>
<pre><code class="language-lua">function _draw()</code></pre>
<p><code>_update()</code> 함수 실행 직후마다 호출되어 게임상의 내용을 화면에 그리는 함수이다.</p>
<pre><code class="language-lua">btn()</code></pre>
<p>어떤 버튼이 눌렸는지를 체크하는 함수이다. 0, 1, 2, 3으로 좌, 우, 상, 하를, 4, 5로 O, X 버튼을 처리한다. (O, X는 키보드상에서 Z, X에 해당)</p>
<blockquote>
<p>주: <code>btn(⬅️)</code>에서 보듯, 코드에서는 숫자가 아닌 기호를 입력하였는데, 이는 PICO-8만의 기능으로, Shift를 누르고 알파벳을 입력할 시 기호가 입력되는 특징이 있다. 따라서 Shift를 누른 채로 L/R/U/D를 누르면 ⬅️/➡️/⬆️/⬇️ 기호가 입력되고, 이는 숫자를 입력한 것과 동일하게 취급된다. O, X 버튼 역시 Shift를 누른 채로 O/X를 입력하면 해당되는 기호가 입력된다.</p>
</blockquote>
<pre><code class="language-lua">cls()</code></pre>
<p>PICO-8의 화면을 지우는 함수이다. 0은 어떤 색으로 지울지를 지정하는 색상 코드로, 0-15까지 사용가능하다. 여기서는 검은색으로 지웠다. (색상 코드는 나중에 다루도록 한다)</p>
<pre><code class="language-lua">rectfill()</code></pre>
<p>시작 좌표, 끝 좌표, 색상 코드를 지정하여 화면에 사각형을 그리는 함수로 시작 좌표는 끝 좌표보다 항상 왼쪽 위에 있어야 한다.</p>
<pre><code class="language-lua">-- comment</code></pre>
<p>주석이다. 코드에 대한 설명을 적을 때 사용되며, 실행에 영향을 주지는 않는다.</p>
<h3 id="2-공-추가">2. 공 추가</h3>
<p>이제 코드 상단에 다음 코드를 추가한다. paddle에 대해 정의해둔 부분 아래에 입력한다.</p>
<pre><code class="language-lua">-- ball
ballx=64
bally=64
ballr=3
ballxdir=5
ballydir=-3</code></pre>
<p>그리고 <code>_draw()</code> 함수 안에 다음 코드를 추가한다.</p>
<pre><code class="language-lua">-- draw ball
circfill(ballx,bally,ballr,6)</code></pre>
<h4 id="코드-해설-1">코드 해설</h4>
<pre><code class="language-lua">circfill()</code></pre>
<p>(x, y)좌표를 중심으로 하는 반지름 R의 원을 지정된 색상으로 그리는 함수</p>
<h3 id="3-공-움직이게-하기">3. 공 움직이게 하기</h3>
<p>이제 다음 코드를 <code>mov_pad()</code> 함수 바로 밑에 입력하도록 하자.</p>
<pre><code class="language-lua">function mov_ball()
    ballx+=ballxdir
    bally+=ballydir
end</code></pre>
<p>그리고 <code>_update()</code> 함수에 다음을 추가하여 공이 움직일 수 있도록 한다.</p>
<pre><code class="language-lua">function _update()
    mov_pad()
    mov_ball()    -- here
end</code></pre>
<p>이제 실행시켜 보면 공이 꽤 빠른 속도로 화면 우상단으로 비스듬히 움직여 화면 밖으로 사라지는 걸 볼 수 있다.</p>
<h3 id="4-공이-코트를-벗어나지-않도록-하기">4. 공이 코트를 벗어나지 않도록 하기</h3>
<p>위에서 확인한 바와 같이 지금 우리의 공은 화면 밖을 벗어나 (아마도) 무한한 우주를 계속해서 가로지르고 있다. 스쿼시를 하기 위해선 공이 벽에 튕겨 우리의 라켓으로 와야 하므로, 이번에는 공이 <strong>화면 가장자리에서 반사</strong>되도록 하자. 어렵지 않다. 공의 x, y 좌표를 확인해서 화면 가장자리인지를 체크하는 로직이 있으면 된다.</p>
<p>그 전에... 재미 요소를 하나 추가해보도록 하자. 바로 소리다.</p>
<p>편집기 모드에서 화면 우상단을 보면 아이콘 5개가 있는 것을 확인할 수 있다. 이 중 4번째, 역재생 아이콘처럼 생긴 것을 눌러보자.</p>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/4c45ba4c-af85-4c92-8ed7-f25420405812/image.png" alt=""></p>
<p><strong>사운드 편집기</strong>가 나타난다.</p>
<p>이제 &quot;띡&quot; 하는 소리를 추가해주도록 하자. 마우스를 이용해 편집기 화면 가장 왼쪽을 누르면 음이 입력되고 <code>SPACEBAR</code>를 누르면 소리를 들어볼 수 있다. </p>
<p>마음에 들 때까지 소리를 조정한 뒤 다시 코드 편집기 (우상단 아이콘 중 소괄호 아이콘을 누르면 된다)로 돌아와 <code>mov_ball()</code> 함수 밑에 다음 코드를 입력한다.</p>
<pre><code class="language-lua">function bounce()
    -- left
    if ballx&lt;ballr then ballxdir=-ballxdir sfx(0) end
    -- right
    if ballx&gt;128-ballr then ballxdir=-ballxdir sfx(0) end
    -- top
    if bally&lt;ballr then ballydir=-ballydir sfx(0) end
end</code></pre>
<p>그리고 <code>_update()</code>에 다음과 같이 추가한다.</p>
<pre><code class="language-lua">function _update()
    mov_pad()
    mov_ball()
    bounce()    -- here
end</code></pre>
<p>이제 실행시켜 보면 오른쪽 위로 날아가는 공이 오른쪽 가장자리에 닿아 위로 튕기고, 벽에 부딪힌 후, 다시 왼쪽 가장자리에 튕겨 오른쪽 아래로 날아오는 걸 볼 수 있다. 공이 튕길 때마다 띡띡 소리도 난다.</p>
<p>이제 라켓으로 공을 받아보자....는 공이 라켓을 통과해 이번에는 아주 깊은 심연으로 떨어지는 것을 볼 수 있다. 다음은 이걸 해결해보도록 하자.</p>
<h4 id="코드-설명">코드 설명</h4>
<pre><code class="language-lua">sfx()</code></pre>
<p>PICO-8의 사운드 편집기에서 생성된 n번째 파일의 사운드를 재생한다.</p>
<h3 id="5-공-쳐내기">5. 공 쳐내기</h3>
<p>이 부분이 조금 까다롭다. 공이 라켓의 범위 안에 들어왔는지 체크해야 하는데, 우리의 라켓은 가느다란 선이 아닌 굵직한 <strong>상자</strong>이기에, x와 y의 범위를 모두 체크해야 한다. 그 전에, 라켓에 부딪히는 소리를 하나 더 만들어보도록 하자.</p>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/7ef3fc75-62c8-4916-bd4b-3c6fd4eed46f/image.png" alt=""></p>
<p>사운드 편집기로 가서 화면 좌상단 <code>&lt; 00 &gt;</code> 라고 되어 있는 부분의 오른쪽 화살표를 눌러 <code>01</code> 로 바꾸자. 아까와 마찬가지로 화면 가장 왼쪽을 적당히 눌러 띡 소리를 만들어주고 (먼젓번 소리보다 약간 낮게 만들어주는 것을 추천한다) 다시 코드 편집기로 돌아와 <code>bounce()</code> 함수 밑에 다음 코드를 입력한다.</p>
<pre><code class="language-lua">function bounce_pad()
    if ballx&gt;=padx and ballx&lt;=padx+padw and bally&gt;pady then
        sfx(1)
        ballydir=-ballydir
    end
end</code></pre>
<p><code>and</code> 명령이 새로 나왔다. <code>and</code> 는 연결된 조건이 모두 참일 경우에 참으로 하라는 의미이다. 한국어로는 &#39;그리고&#39;에 해당한다.</p>
<blockquote>
<p>주: 이전까지는 <code>if</code> 문을 작성 시 한 줄로 작성하였는데, 이는 PICO-8의 문자 수/토큰 수 제한 조건 때문으로, 이번 <code>if</code> 문은 조건이 길기 때문에 여러 줄로 나누어 작성하였다. lua는 여러 줄로 쓰던 한 줄로 쓰던, 토큰이 적절히 구분만 가능하면 문제 없이 작동한다는 특이점이 있다.</p>
</blockquote>
<p><code>sfx(1)</code> 로 아까 만든 두번째 소리를 씀에 유의한다.</p>
<p>이제, <code>_update()</code> 함수에 다음과 같이 코드를 추가한다.</p>
<pre><code class="language-lua">function _update()
    mov_pad()
    bounce()
    bounce_pad()    -- here
    mov_ball()
end</code></pre>
<p>코드의 순서를 약간 바꾸었음에 주의하라. 공이 움직이는 것이 가장 후순위로 되어 있다.</p>
<p>이제 게임을 실행하고 라켓을 움직여 공을 받아내보자. 공이 라켓에 부딪히고 다시 퉁겨나가는 걸 볼 수 있다. 하지만 여전히 라켓이 공을 따라가지 못하면 공이 화면 밖으로 나가 밑에 자리잡은 심연 속에서 튀어다니는 걸 알 수 있다.</p>
<p>다음에는 라켓으로 공을 받아내지 못하면 &#39;죽은 공(dead ball)&#39;으로 처리할 수 있도록 해보자.</p>
<h3 id="6-공을-다시-로드하기">6. 공을 다시 로드하기</h3>
<p>공이 라켓에 맞지 않고, 화면 밑으로 빠질 경우, 공을 다시 화면 중앙으로 돌려놓도록 하자, 이때, 공을 받아내지 못하면 라이프를 잃는 기능은 추후에 만들어보도록 하자.</p>
<p>일단, 공을 놓쳤음을 알 수 있게 3번째 사운드(<code>sfx(2)</code>)를 만들자.</p>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/bed9474e-7df1-4bd1-a3e3-c3fd7576bf96/image.png" alt=""></p>
<p>그리고 코드 편집기로 돌아와 새 함수를 <code>mov_ball()</code> 밑에 추가한다.</p>
<pre><code class="language-lua">function lose_ball()
    if bally&gt;128 then sfx(2) bally=24 end
end</code></pre>
<p>그리고 <code>_update()</code> 에 위 함수를 추가한다.</p>
<pre><code class="language-lua">function _update()
    mov_pad()
    bounce()
    bounce_pad()
    mov_ball()
    lose_ball()    -- here
end</code></pre>
<p>이제 실행해 보면 라켓으로 공을 맞히지 못할 경우 사운드 효과와 함께 공이 화면 중앙에 나타나는 걸 확인할 수 있다.</p>
<blockquote>
<p>팁: 테스트할 때 가지고 있는 게임 콘솔용 컨트롤러를 연결해 보자. 훨씬 플레이하기 수월해진다.</p>
</blockquote>
<h3 id="7-score">7. SCORE!</h3>
<p>스쿼시 <strong>게임</strong>인 만큼 점수 시스템을 도입해 최고 점수를 노릴 수 있도록 만들어보자.</p>
<p>먼저 프로그램 상단에 다음을 추가한다.</p>
<pre><code class="language-lua">score=0</code></pre>
<p>그리고 <code>bounce_pad()</code> 함수를 수정하자</p>
<pre><code class="language-lua">function bounce_pad()
    if ballx&gt;=padx and ballx&lt;=padx+padw and bally&gt;pady then
        sfx(1)
        ballydir=-ballydir
        -- score
        score+=10    -- here
    end
end</code></pre>
<p>마지막으로 <code>_draw()</code> 함수도 다음과 같이 수정하자.</p>
<pre><code class="language-lua">function _draw()
    cls(0)
    -- draw score
    print(&quot;score  &quot;..score,12,6,6)    -- here
    -- draw paddle
    rectfill(padx,pady,padx+padw,pady+padh,6)
    -- draw ball
    circfill(ballx,bally,ballr,6)
end</code></pre>
<p>이제 실행해 보면 제법 그럴듯한 모습의 게임이 나오고, 라켓으로 공을 받아칠 때마다 점수가 10점씩 올라가는 걸 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/93734c46-e3a0-44ab-bd0b-c2ac916c9b0b/image.png" alt=""></p>
<h4 id="코드-설명-1">코드 설명</h4>
<pre><code class="language-lua">print()</code></pre>
<p>지정된 내용을 해당 좌표에 지정된 색으로 글을 출력한다.
문자열에 변수를 포함하고 싶다면 <code>..</code> 를 쓴다.</p>
<h3 id="8-잔기-시스템">8. 잔기 시스템</h3>
<p>먼저 잔기를 표시할 이미지(스프라이트)를 그려보도록 하자.</p>
<p>편집기 모드 상단 우측에 고양이(?) 아이콘이 있는 버튼(2번째)을 누르면 <strong>스프라이트 편집기</strong>가 나온다. 다음과 같이 아래쪽 팔레트 상단 위의 x자가 쳐진 부분을 누르고, 그 위에 하트를 그린다. 잘못 그렸을 때는 검정색을 사용해 덧칠하여 지우도록 하자.</p>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/05b14c93-de60-4d27-a395-f362a68c4d57/image.png" alt=""></p>
<p>그런 다음 코드 편집기로 돌아와 아래와 같이 프로그램 상단에 잔기 변수를 추가한다.</p>
<pre><code class="language-lua">life=5</code></pre>
<p>그리고 <code>_draw()</code> 함수에 다음 내용을 추가한다.</p>
<pre><code class="language-lua">-- draw the lives
spr(000, 12,13) print(&quot; x &quot;..life,20,15,6)</code></pre>
<p>마지막으로 <code>lose_ball()</code> 함수의 내용을 아래와 같이 수정한다.</p>
<pre><code class="language-lua">function lose_ball()
    if bally&gt;128 then
        -- from here
        if life&gt;0 then
            sfx(2) bally=24 life-=1
        else
            ballxdir=0 ballydir=0 bally=64
        end
        -- to here
    end
end</code></pre>
<p>이제 저장하고 실행시켜 보자. 제법 그럴싸한 게임이 완성되었다. 잔기가 소진되면 게임이 멈춘다.</p>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/9db03b67-ed5a-47fa-9104-782a58a36175/image.png" alt=""></p>
<h4 id="코드-설명-2">코드 설명</h4>
<pre><code class="language-lua">spr()</code></pre>
<p>지정된 번호의 스프라이트를 지정된 좌표 위치에 그린다.</p>
<h3 id="마무리">마무리</h3>
<p>이렇게 하여 PICO-8의 첫 게임인 스쿼시 게임을 완성하였다. 눈치챈 독자도 있겠지만, 이걸 응용한 게임이 바로 <strong>벽돌깨기(breakout)</strong> 이다. 관심 있으면 추가로 더 구현해보길 바란다.<img src="https://velog.velcdn.com/images/scope_hyena/post/5deaddc6-c8fd-43c3-a908-019b0cbc4f53/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Deep Q Network - TIL]]></title>
            <link>https://velog.io/@scope_hyena/DQN-TIL</link>
            <guid>https://velog.io/@scope_hyena/DQN-TIL</guid>
            <pubDate>Fri, 07 Jun 2024 08:40:59 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 다음을 참고하여 작성하였음</p>
</blockquote>
<ul>
<li><a href="https://wikidocs.net/165847">위키독스 - Deep Learning Bible(한국어) - 5. Deep Q Networks</a></li>
<li><a href="https://pytorch.org/tutorials/intermediate/reinforcement_q_learning.html">PyTorch Tutorial - DQN</a></li>
</ul>
<p>PyTorch의 Deep Q Learning 예제를 분석하면서 설명을 추가하려고 한다.
먼저 PyTorch에서 제공하는 Gymnasium의 CartPole 데이터를 다운로드한다.</p>
<h1 id="준비">준비</h1>
<h2 id="gymnasium-설치">Gymnasium 설치</h2>
<pre><code class="language-bash">%%bash
pip install gymnasium[classic_control]</code></pre>
<p>CartPole 예제는 Gymnasium 패키지에 포함되어 있는 것으로 Cart 안에 세워 놓은 Pole이 쓰러지지 않도록 균형을 맞추는 사고 실험을 기반으로 만들어진 데이터이다.</p>
<h2 id="필요-패키지-import-및-기본-설정">필요 패키지 import 및 기본 설정</h2>
<p>패키지 import</p>
<pre><code class="language-python">import gymnasium as gym
import math
import random
import matplotlib
import matplotlib.pyplot as plt
from collections import namedtuple, deque
from itertools import count

import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F</code></pre>
<p>Environment를 CartPole로 설정 (CartPole 예제에 대한 데이터와 상태가 모두 정의되어 환경을 구성함)</p>
<pre><code class="language-python">env = gym.make(&quot;CartPole-v1&quot;)</code></pre>
<p>matplotlib 설정(이후에 실행 결과를 그래프로 나타낼 수 있도록 함)</p>
<pre><code class="language-python"># set up matplotlib
is_ipython = &#39;inline&#39; in matplotlib.get_backend()
if is_ipython:
    from IPython import display

plt.ion()</code></pre>
<blockquote>
</blockquote>
<pre><code>&lt;contextlib.ExitStack at 0xfd28b3e4f320&gt;</code></pre><p>torch 사용 환경 설정
(실습 환경은 그래픽 카드를 사용할 수 없는 가상 머신 환경이므로 cpu를 사용하여 학습을 진행하게 되었음)</p>
<pre><code class="language-python">device = torch.device(&quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;)</code></pre>
<h1 id="overview">Overview</h1>
<h2 id="q-function">Q-function</h2>
<ul>
<li>state의 수가 Q-Learning에 비해 너무 많기 때문에 function을 사용<blockquote>
<p>$f(s)\  : \ {(s, a) \rarr Q(s,a)}$</p>
</blockquote>
</li>
</ul>
<h2 id="dqn-architecture">DQN Architecture</h2>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/4de1c149-ac20-45c1-aa8b-d3b9baffd8c2/image.png" alt=""></p>
<ul>
<li>QNN - optimal Q-Value를 생성, 표준적인 신경망 아키텍처</li>
<li>Target Network - 복제된 QNN</li>
</ul>
<h2 id="replay-memory">Replay Memory</h2>
<ul>
<li><p>Experience Replay를 이용해</p>
<ul>
<li>Environment와 상호작용하여 $\epsilon$-greedy에 따라 action을 선택하여 실행</li>
<li>reward와 next state를 받음<blockquote>
<p>Transition : (state, action) pair -&gt; (next-state, reward) pair</p>
</blockquote>
</li>
</ul>
</li>
<li><p>Transition (state, action, next-state, reward)를 받아 Replay Memory에 Training Data로 저장</p>
</li>
<li><p>Q-Network의 예측 Q-Value 및 Target Network의 Target Q-Value를 산출, Deep Q Learning을 위한 sample data 추출</p>
</li>
</ul>
<pre><code class="language-python"># Transition 설정
Transition = namedtuple(&#39;Transition&#39;, (&#39;state&#39;, &#39;action&#39;, &#39;next_state&#39;, &#39;reward&#39;))</code></pre>
<pre><code class="language-python"># Replay Memory 정의
class ReplayMemory(object):
    def __init__(self, capacity):
        self.memory = deque([], maxlen=capacity)

    def push(self, *args):
        # Save a Transition
        self.memory.append(Transition(*args))

    # sampling transition for training agent randomly
    def sameple(self, batch_size):
        return random.sample(self.memory, batch_size)

    def __len__(self):
        return len(self.memory)</code></pre>
<h1 id="q-network">Q-Network</h1>
<ul>
<li>Agent로써 작동하며, input으로 current state를 받는다.</li>
<li>state에 대한 가능한 모든 Q-Value를 output으로 내놓는다.</li>
</ul>
<p>여기서는 간단한 구조의 신경망 네트워크를 사용한다.</p>
<pre><code class="language-python">class DQN(nn.Module):
    def __init__(self, n_observations, n_sactions):

        super(DQN, self).__init__()
        self.layer1 = nn.Linear(n_observations, 128)
        self.layer2 = nn.Linear(128,128)
        self.layer3 = nn.Linear(128, n_actions)

    # input layer - relu(get value that more than 0) - hidden layer (1) - relu - output layer
    def forward(self, x):
        x = F.relu(self.layer1(x))
        x = F.relu(self.layer2(x))
        return self.layer3(x)</code></pre>
<h1 id="training">Training</h1>
<h2 id="hyperparameters-및-기본-설정">hyperparameters 및 기본 설정</h2>
<p>이곳에서 policy net, target net 및 replay memory를 위한 메모리 사이즈 등을 설정한다.</p>
<pre><code class="language-python">BATCH_SIZE = 128      # replay buffer에서 샘플링하는 transition 수
GAMMA = 0.99          # discount factor
EPS_START = 0.9       # epsilon(exploration speed)의 초기값 (고점)
EPS_END = 0.05        # epsilon의 나중 값 (저점)
EPS_DECAY = 1000      # epsilon의 감쇠율 (클수록 감쇠율이 낮음)
TAU = 0.005           # target net의 갱신 주기
LR = 1e-4             # AdamW optimizer의 학습 주기</code></pre>
<pre><code class="language-python"># gym data로부터 받아오는 action 수
n_actions = env.action_space.n
# 관찰할 state의 수
state, info = env.reset()
n_observations = len(state)

# QNN (policy net) 및 Target Net 설정
policy_net = DQN(n_observations, n_actions).to(device)
target_net = DQN(n_observations, n_actions).to(device)
# copy policy net to target net
target_net.load_state_dict(policy_net.state_dict())</code></pre>
<blockquote>
</blockquote>
<pre><code>&lt;All keys matched successfully&gt;</code></pre><pre><code class="language-python">optimizer = optim.AdamW(policy_net.parameters(), lr=LR, amsgrad=True)
memory = ReplayMemory(10000)</code></pre>
<pre><code class="language-python">steps_done = 0</code></pre>
<pre><code class="language-python">def select_action(state):
    global steps_done
    # action selected randomly
    sample = random.random()
    # epsilon-greedy strategy curve
    eps_threshold = EPS_END + (EPS_START - EPS_END) * math.exp(-1.  * steps_done / EPS_DECAY)
    steps_done += 1
    # action select with max result for pick action with the largest reward
    if sample &gt; eps_threshold:
        with torch.no_grad():
            return policy_net(state).max(1).indices.view(1,1)
    else:
        return torch.tensor([[env.action_space.sample()]], device=device, dtype=torch.long)</code></pre>
<pre><code class="language-python">episode_durations = []</code></pre>
<ul>
<li>이 코드는 시각화를 위한 설정이다. 이 코드를 통해 실시간으로 Training 결과를 출력하고 갱신한다.</li>
</ul>
<pre><code class="language-python"># get results and plot it
def plot_durations(show_result=False):
    plt.figure(1)
    durations_t = torch.tensor(episode_durations, dtype=torch.float)
    if show_result:
        plt.title(&#39;Result&#39;)
    else:
        plt.clf()
        plt.title(&#39;Training...&#39;)
    plt.xlabel(&#39;Episode&#39;)
    plt.ylabel(&#39;Duration&#39;)
    plt.plot(durations_t.numpy())

    if len(durations_t) &gt;= 100:
        means = durations_t.unfold(0,100,1).mean(1).view(-1)
        means = torch.cat((torch.zeros(99), means))
        plt.plot(means.numpy())

    plt.pause(0.001)
    if is_ipython:
        if not show_result:
            display.display(plt.gcf())
            display.clear_output(wait=True)
        else:
            display.display(plt.gcf())</code></pre>
<h1 id="training-loop">Training Loop</h1>
<h2 id="optimizer">optimizer</h2>
<ul>
<li>main training loop를 설정하는 코드이다.<ol>
<li>Replay Memory에서 Random으로 Transition을 가져온다.</li>
<li>Transition의 state를 policy net에 input하여 Q를 얻고 Transition의 action에 따른 추정 Q-Value를 취한다.</li>
<li>Transition의 reward 및 next state를 input으로 하여 Target net을 통해 나온 Q 값 중 max Q를 취해 Target Q-Value를 얻는다.</li>
<li>Target Q-Value 및 추정 Q 값을 이용하여 Loss를 계산한다.</li>
<li>loss를 이용해 policy net의 weight를 업데이트하고 이를 Target net에 반영한다.</li>
</ol>
</li>
</ul>
<pre><code class="language-python">def optimize_model():
    if len(memory) &lt; BATCH_SIZE:
        return
    transitions = memory.sameple(BATCH_SIZE)
    # batch-array of Transition to transition of batch-array
    batch = Transition(*zip(*transitions))

    # compute a mask of non-final state and concatenate(like chain) the batch elements
    non_final_mask = torch.tensor(tuple(map(lambda s: s is not None, batch.next_state)), device=device, dtype=torch.bool)
    non_final_next_states = torch.cat([s for s in batch.next_state if s is not None])

    state_batch = torch.cat(batch.state)
    action_batch = torch.cat(batch.action)
    reward_batch = torch.cat(batch.reward)

    # compute Q(s_t, a) - get Q and select action, according to policy_net
    state_action_values = policy_net(state_batch).gather(1, action_batch)

    # compute V(s_{t+1}) for all next states, expected values of actions for non-final_next_state are computed
    # selecting their best reward with max(1).values
    next_state_values = torch.zeros(BATCH_SIZE, device=device)
    with torch.no_grad():
        next_state_values[non_final_mask] = target_net(non_final_next_states).max(1).values
    # compute expected Q 
    expected_state_action_values = (next_state_values * GAMMA) + reward_batch

    # compute huber loss (in this example)
    criterion = nn.SmoothL1Loss()
    loss = criterion(state_action_values, expected_state_action_values.unsqueeze(1))

    # optimize the model
    optimizer.zero_grad()
    loss.backward()

    # in-place gradient clipping
    torch.nn.utils.clip_grad_value_(policy_net.parameters(), 100)
    optimizer.step()</code></pre>
<h2 id="rl-set-episode">RL set episode</h2>
<p>이제 initialization -&gt; optimization(main training loop) 순서로 학습을 진행한다.</p>
<ul>
<li><p>600 episode for GPU, otherwise 200 episodes</p>
<ul>
<li>이 테스트에서는 cuda 지원이 되지 않는 M2 Mac Mini의 Ubuntu 가상 머신 환경에서 테스트했기 때문에 200회만 학습을 진행시켜 보았다. (학습시키는 데 약 30초 정도가 소요되었다)</li>
</ul>
</li>
<li><p>initialization</p>
<ol>
<li>Environment reset<ol start="2">
<li>replay memory에 저장한 Transition sample을 policy net을 이용해서 생성한다.</li>
<li>policy net에서 나온 Q-value를 이용해 $\epsilon$-greedy에 따라 action을 선택하여 Transition에 저장한다.</li>
<li>Transition을 Memory에 저장한다.</li>
</ol>
</li>
</ol>
</li>
<li><p>optimization</p>
<pre><code class="language-python">if torch.cuda.is_available():
  num_episodes = 600
else:
  num_episodes = 200
</code></pre>
</li>
</ul>
<p>for i_episode in range(num_episodes):
    # initialization
    state, info = env.reset()
    state = torch.tensor(state, dtype=torch.float32, device=device).unsqueeze(0)
    for t in count():
        action = select_action(state)
        observation, reward, terminated, truncated, _ = env.step(action.item())
        reward = torch.tensor([reward], device=device)
        done = terminated or truncated</p>
<pre><code>    if terminated:
        next_state = None
    else:
        next_state = torch.tensor(observation, dtype=torch.float32, device=device).unsqueeze(0)

    # store the transition in memory
    memory.push(state, action, next_state, reward)

    # move to the next state
    state = next_state

    # perform one step of the optimization (policynet)
    optimize_model()

    # target net update: θ&#39; &lt;- τθ + (1-τ)θ&#39;
    target_net_state_dict = target_net.state_dict()
    policy_net_state_dict = policy_net.state_dict()
    for key in policy_net_state_dict:
        target_net_state_dict[key] = policy_net_state_dict[key]*TAU + target_net_state_dict[key]*(1-TAU)
        target_net.load_state_dict(target_net_state_dict)

    if done:
        episode_durations.append(t+1)
        plot_durations()
        break</code></pre><p>print(&#39;Complete.&#39;)
plot_durations(show_result=True)
plt.ioff()
plt.show()</p>
<pre><code>&gt;
    Complete.
![](https://velog.velcdn.com/images/scope_hyena/post/9329f381-9f70-4e8d-a09f-ea68c04ec2af/image.png)
    &lt;Figure size 640x480 with 0 Axes&gt;
    &lt;Figure size 640x480 with 0 Axes&gt;</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Q-Learning - TIL]]></title>
            <link>https://velog.io/@scope_hyena/Q-Learning-TIL</link>
            <guid>https://velog.io/@scope_hyena/Q-Learning-TIL</guid>
            <pubDate>Tue, 28 May 2024 07:19:48 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 다음을 참고로 하여 작성함
<a href="https://wikidocs.net/165849">위키독스 - Depp Learning Bible - 04. Q-Learning(Q-Table</a></p>
</blockquote>
<h1 id="overview">Overview</h1>
<h2 id="q-table">Q-Table</h2>
<ul>
<li>State-action 쌍에 대한 Q 값이 정리된 테이블</li>
<li>0으로 초기화하여 시작
<img src="https://velog.velcdn.com/images/scope_hyena/post/947b886e-814b-4374-adf2-1c2f94d5d28e/image.png" alt=""></li>
</ul>
<h2 id="q-table의-업데이트">Q-Table의 업데이트</h2>
<p>${\rarr\ }$ Model Free Algorithm 참조
<img src="https://velog.velcdn.com/images/scope_hyena/post/35c18c63-c97c-4f34-aa8f-3d665ac18b46/image.png" alt=""></p>
<ul>
<li><p>추정 Q값($Q_1$) 업데이트를 위해 next Q값 사용</p>
</li>
<li><p>가장 높은 Q값을 이용</p>
<ul>
<li>$Q_1$의 업데이트에만 사용할 target action으로 취급</li>
</ul>
</li>
<li><p>target Q 값을 식별했으므로,</p>
<blockquote>
<p>${Q_1 \ \larr \ Q_1 + \alpha(R_1 + \gamma Q_{max} - Q_1}$</p>
</blockquote>
<ul>
<li>current action
실제 실행하는 action</li>
<li>target action
실제 실행과는 무관하게 현재 Q를 업데이트하기 위한 next의 가장 큰 Q값을 선택</li>
</ul>
</li>
<li><p>선책은 $\epsilon$-greedy policy에 따름</p>
</li>
<li><p>off-policy 학습</p>
</li>
</ul>
<h1 id="q-table의-생성">Q-Table의 생성</h1>
<ol>
<li>initialization<ul>
<li>모두 0으로 초기화</li>
</ul>
</li>
<li>$\epsilon$-greedy에 따라 action 선택 후 Q 업데이트 (반복)</li>
<li>Optimal 값으로 수렴</li>
</ol>
<p>${\rarr \ }$ 모든 time step에서 실측 값으로 업데이트 되므로 Q값이 정확해짐</p>
<blockquote>
<p>${Q \larr Q + \alpha (R + \gamma\max{Q&#39;} - Q)}$</p>
</blockquote>
<pre><code>- R = 실측
- maxQ = 추정 -&gt; 실측
- Q - 추정 -&gt; 실측</code></pre><ul>
<li>episode를 더 많이 진행할 수록 Q-Table이 갱신되어 정확해짐</li>
</ul>
<h1 id="optimal-q-value">Optimal Q-Value</h1>
<ul>
<li>$\epsilon$-greedy는 가능한 한 많은 state와 action을 탐색하도록 권장함<ul>
<li>더 많은 반복과 경로 탐색을 수행할수록 더 나은 Q-Value를 찾기 위해 사용 가능한 모든 옵션을 시도한 것이 됨
${\therefore\ }$ optimal Q-Value를 찾을 수 있는 이유</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[model free 알고리즘 - TIL]]></title>
            <link>https://velog.io/@scope_hyena/model-free-algorithm-til</link>
            <guid>https://velog.io/@scope_hyena/model-free-algorithm-til</guid>
            <pubDate>Tue, 28 May 2024 07:18:11 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>다음 글에 기반하여 작성하였습니다.
<a href="https://wikidocs.net/165848">위키독스 - Deep Learning Bible - 한국어</a></p>
</blockquote>
<h1 id="policy-based와-value-based">Policy Based와 Value Based</h1>
<p>Value function을 비교하면 어떤 policy가 더 나은지 비교할 수 있음</p>
<blockquote>
<p>$V_Y &gt; V_X \ \Rarr {\text {Policy Y is better than X}}$</p>
</blockquote>
<p>Optimal policy = the best policy = the highest value function</p>
<p>어떤 policy가 optimal하면 해당  policy의</p>
<ul>
<li>state value</li>
<li>Q-value (state-action value)</li>
</ul>
<p>도 optimal하다.</p>
<h2 id="policy-based-algorithm의-기본-동작">Policy Based Algorithm의 기본 동작</h2>
<pre><code>Policy Based -&gt; Optimal Policy</code></pre><h2 id="value-based-algorithm-기본-동작">Value Based Algorithm 기본 동작</h2>
<pre><code>Value Based -&gt; Optimal Q-Value -&gt; Optimal Policy</code></pre><p>(후에 다룰 DQN을 위주로 볼 것이므로 Q-Value에 대해서만 정리)</p>
<h1 id="optimal-q-value-to-optimal-policy">Optimal Q-Value to Optimal Policy</h1>
<p>Optimal Q Value를 찾으면 highest Q-Value값을 가지는 action을 선택해 Optimal Policy를 알 수 있음</p>
<blockquote>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/45e7eee0-6365-4896-b439-c2e9fa1a0846/image.png" alt=""></p>
</blockquote>
<ul>
<li><p>위 그림에서 $Q_1(s, a_1),\ Q_2(s,a_2)$
${\pi = 1 \rarr \argmax(Q_1, Q_2)} \
\pi = 2 \rarr {\text {another }} action \
\therefore \ Q_1 &gt; Q_2, \  {\text {policy will choose }} a_1 \
\pi_1 = 1, \ \pi_2 = 0$</p>
</li>
<li><p>Optimal Policy는 최상의 action으로 이어지므로 deterministic하다.</p>
<ul>
<li>단, Q-Value가 동일하면 확률적으로 action을 선택한다.</li>
</ul>
</li>
</ul>
<h1 id="model-free-알고리즘의-분류">model free 알고리즘의 분류</h1>
<h2 id="look-up-table-vs-function-approximator">Look-up Table vs. Function approximator</h2>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">Look-up Table</th>
<th align="center">func</th>
</tr>
</thead>
<tbody><tr>
<td align="center">특징</td>
<td align="center">간단한 알고리즘에 주로 사용</td>
<td align="center">복잡한 알고리즘에 주로 사용 (RL)</td>
</tr>
<tr>
<td align="center">policy based</td>
<td align="center"></td>
<td align="center">policy gradient, actor-critic</td>
</tr>
<tr>
<td align="center">Q-value based</td>
<td align="center">Monte-Carlo Control, Sarsa, Sarsa-backward,  Q-Learning</td>
<td align="center">DQN</td>
</tr>
</tbody></table>
<h2 id="model-free-알고리즘의-기본-단계">model free 알고리즘의 기본 단계</h2>
<p>RL 문제는 대수적 접근이 불가하며, 반복 알고리즘을 사용해야 함</p>
<p>RL은 기본적으로는 아래와 같은 단계를 거친다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/5e7be958-03cf-4e9d-9d75-517e4405df60/image.png" alt=""></p>
<ol>
<li><p>초기화</p>
<ul>
<li>Table 생성 후 0으로 초기화<ul>
<li>Value-based: Estimated Optimal Q-Value Table 생성</li>
<li>Policy-based: Estimated Optimal Policy Table 생성</li>
</ul>
</li>
</ul>
</li>
<li><p>action 수행</p>
<ul>
<li>사용 가능한 모든 경로를 충분히 시도 후 최상의 옵션을 찾기 위해 탐색과 활용 사이에서 균형을 찾아야 함<ul>
<li><strong>Exploration (탐색)</strong>
무작위로 action 선택 후 reward를 관찰함</li>
<li><strong>Exploitation (활용)</strong>
가능한 모든 action이 탐색되면 최대 reward를 내는 최상의 action 선택</li>
</ul>
</li>
</ul>
<blockquote>
<ul>
<li>Policy Based</li>
</ul>
</blockquote>
<ul>
<li>Policy Table<blockquote>
</blockquote>
<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">$a_1$</th>
<th align="center">$a_2$</th>
<th align="center">$a_3$</th>
<th align="center">$a_4$</th>
</tr>
</thead>
<tbody><tr>
<td align="center">$s_1$</td>
<td align="center">$\pi_1$</td>
<td align="center">$\pi_2$</td>
<td align="center">$\pi_3$</td>
<td align="center">$\pi_4$</td>
</tr>
<tr>
<td align="center">policy table에는 주어진 state에 대한 모든 action의 원하는 확률을 알려주는 optimal policy에 대한 지속적 추정이 존재</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
<tr>
<td align="center">- 확률에 따라 action을 선택하며, action의 확률이 높을수록 선택될 가능성이 높아짐</td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
<td align="center"></td>
</tr>
</tbody></table>
<blockquote>
<ul>
<li>Value Based - $\epsilon$-greedy 전략
${\epsilon}$는 임의의 action을 선탯할 확률로, ${1-\epsilon}$의 확률로 최선의 행동을 선택함
<img src="https://velog.velcdn.com/images/scope_hyena/post/0eac98f8-3095-40d4-8c05-0a84ef0c8e7b/image.png" alt=""></li>
</ul>
</blockquote>
</li>
</ul>
</li>
<li><p>feedback
action 수행 후 env.에서 reward의 형태로 feedback</p>
</li>
<li><p>추정 개선
feedback을 이용해 reward 기반의 estimated optimal policy/value 개선</p>
<ul>
<li><p><strong>policy-based</strong>
postive reward이면 선택했던 action의 선택 확률을 높임</p>
</li>
<li><p><strong>value-based</strong>
reward 기반으로 Belman Equatrion을 통해 Value를 증가 또는 감소</p>
<ol>
<li><p>Q-value</p>
<blockquote>
<p>${\ Q(s,a) = {\mathbb E}[R + \gamma Q(s_{next}, a_{next})]}$</p>
</blockquote>
</li>
<li><p>오차</p>
<blockquote>
<p>${{\text{Error}} = (R_t + \gamma Q(s_{t+1}, a_{t+1}) - Q(s_t, a_t)}$</p>
</blockquote>
</li>
<li><p>업데이트</p>
<blockquote>
<p>${Q(s,a) = Q(s,a) + \alpha \times {\text {Error}}}$</p>
</blockquote>
</li>
</ol>
</li>
</ul>
</li>
</ol>
<h1 id="추정치-향상을-위한-다른-방법">추정치 향상을 위한 다른 방법</h1>
<p>다른 방법으로의 변형은 아래와 관련 있음</p>
<ul>
<li>Frequency
업데이트 전에 수행된 forward steps 수</li>
<li>Depth
업데이트를 전달하기 위한 backward step 수</li>
<li>Formula
업데이트된 추정치를 계산하는 데 사용됨</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[벨만 방정식 - TIL]]></title>
            <link>https://velog.io/@scope_hyena/Bellman-Equation</link>
            <guid>https://velog.io/@scope_hyena/Bellman-Equation</guid>
            <pubDate>Sun, 12 May 2024 20:44:52 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>이 글은 다음을 참고로 하여 작성함
<a href="https://wikidocs.net/165842">위키독스 -  Deep Learning Bible - 5. Reinforcement Learning - 한글/Solution Approaches</a></p>
</blockquote>
<h1 id="model-based-vs-model-free">Model Based vs. Model Free</h1>
<hr>
<h2 id="model-based">Model Based</h2>
<ul>
<li>Planning</li>
<li>Env.의 내부 작동이 알려진 경우 사용</li>
</ul>
<h2 id="model-free">Model Free</h2>
<ul>
<li>RL</li>
<li>Env.가 매우 복잡하고, 내부 작동 구조를 알 수 없을 때 사용<ul>
<li>Env.를 블랙박스로 취급</li>
</ul>
</li>
</ul>
<h1 id="prediction-vs-control">Prediction vs. Control</h1>
<hr>
<h2 id="prediction">Prediction</h2>
<blockquote>
<p><code>policy -&gt; [ evaluate ] -&gt; Value function</code></p>
</blockquote>
<ul>
<li>모든 policy일 수 있으며, 반드시 optimal하지는 않음</li>
</ul>
<h2 id="control">Control</h2>
<blockquote>
<p><code>[ Find the best policy] -&gt; optimal policy</code></p>
</blockquote>
<ul>
<li>input이 없으며, policy 공간을 탐색해 optimal policy를 찾는 것이 목표</li>
</ul>
<p>${\therefore\ }$ RL 문제에서는 Moel free-Control 문제를 주로 다룸</p>
<h1 id="model-free-approaches">Model-Free Approaches</h1>
<hr>
<ul>
<li>Env.와 실제로 상호작용해야만, Env.의 행동을 관찰할 수 있음<ul>
<li>시행착오를 통해 한번에 한 action씩 상호작용하여 어떻게 작용하는지 배움</li>
</ul>
</li>
<li>Agent: 알고리즘(action -&gt; next-state, reward를 확인, 반복)
${\rarr\ }$ pos/neg한 feedback을 얻음</li>
<li>각 단계를 수행한 trajectory 등이 알고리즘의 훈련 데이터가 됨</li>
</ul>
<h1 id="bellman-equation">Bellman Equation</h1>
<hr>
<ul>
<li><p>Return은 두 부분으로 분해할 수 있음</p>
<ol>
<li><p>Reward (for current State)</p>
</li>
<li><p>Next State로부터의 Discounted Return
${\rarr\ }$ 이 재귀 관계가 벨만 방정식</p>
<blockquote>
<p>${G_t = R_t + \gamma G_{t+1}}$
<code>Return = Reward + next-discounted Return</code></p>
</blockquote>
</li>
</ol>
</li>
<li><p>Value의 경우에도 Bellman 방정식은 동일하게 작용</p>
<ul>
<li>단, 이 경우에는 평균값(Expected Value)을 사용</li>
</ul>
</li>
</ul>
<h2 id="bellman-equation-for-state-value">Bellman Equation for State Value</h2>
<blockquote>
<p>${V(S_t) = \mathbb E[R_t + \gamma V(S_{t+1})]}$
<code>Value = Expected(reward + next-discounted Value)</code></p>
</blockquote>
<h2 id="bellman-equation-for-q-value">Bellman Equation for Q-Value</h2>
<blockquote>
<p>${Q(S_t, a_t) = \mathbb E [R_t + \gamma Q(S_{t+1}, a_{t+1})]}$
<code>Q-Val = Expected(reward + next-discounted Q-Val)</code></p>
</blockquote>
<h2 id="bellman-equation을-사용하는-이유">Bellman Equation을 사용하는 이유</h2>
<ol>
<li>Episode의 끝까지 갈 필요가 없음 (Episode가 너무 길 경우에도 사용 가능)</li>
<li>추정치를 사용할 수 있음<ul>
<li>State-Return</li>
<li>State-Reward + next-state Return</li>
</ul>
</li>
</ol>
<p>${\therefore\ }$ 실제 Return과 계산된 추정치를 비교하여 생긴 &#39;오차&#39;를 줄이는 방식으로 추정치를 개선함</p>
<p>RL에서 이용하는 알고리즘은 이 방식을 채택</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[강화학습 개요 - TIL]]></title>
            <link>https://velog.io/@scope_hyena/Overview-of-RL</link>
            <guid>https://velog.io/@scope_hyena/Overview-of-RL</guid>
            <pubDate>Fri, 10 May 2024 07:37:29 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="참고한-내용">참고한 내용</h3>
<p><a href="https://wikidocs.net/165845">위키독스 - Deep Learning Bible - 5. Reinforcement Learning - 한글</a></p>
</blockquote>
<h1 id="overview">Overview</h1>
<hr>
<h2 id="강화-학습rl">강화 학습(RL)</h2>
<ul>
<li>강화 학습은 환경과 상호작용하여 input을 수집하고 feedback을 취함<ul>
<li>해당 세계와 상호 작용하는 동안 취해야 하는 최상의 조치를 출력</li>
</ul>
</li>
</ul>
<h2 id="rl이-기존의-ml과-다른-점">RL이 기존의 ML과 다른 점</h2>
<ul>
<li>감독이 없음</li>
<li>훈련 데이터 세트가 없으며, 환경과 상호작용하여 데이터 세트를 수집함</li>
<li>반복적으로 결정을 내리고 input에 대한 추론을 한 번 실행해 예측 생성<ul>
<li>RL을 이용하면 추론을 반복적으롷 실행하고 실제 환경을 탐색하면서 이동함</li>
</ul>
</li>
</ul>
<h1 id="markov-결정-과정mdp">Markov 결정 과정(MDP)</h1>
<hr>
<p>RL 문제를 풀기 위해서는 문제를 MDP로 재구성할 필요가 있다.</p>
<h2 id="mdp의-구성-요소">MDP의 구성 요소</h2>
<ul>
<li>Agent<ul>
<li>RL 문제를 훈련시킬 모델</li>
</ul>
</li>
<li>Environment<ul>
<li>Agent가 상호 작용하는 환경</li>
</ul>
</li>
<li>State<ul>
<li>특정 시점에서 환경의 상태</li>
</ul>
</li>
<li>Action<ul>
<li>Agent가 취하는 행동</li>
</ul>
</li>
<li>Reward<ul>
<li>Action의 결과로 얻는 강화(보상)<ul>
<li>의도한 대로 행동했다면 Positive Reward</li>
<li>잘못된 행동을 했다면 Negative Reward<h2 id="mdp-구성-시-유의사항">MDP 구성 시 유의사항</h2>
</li>
</ul>
</li>
</ul>
</li>
<li>Agent / Env.<ul>
<li>Agent와 Env.의 역할과 범위를 정해야 한다</li>
</ul>
</li>
<li>State<ul>
<li>State에 포함된 데이터 요소와 표현 방식을 결정해야 한다.<ul>
<li>Agent가 추가 정보를 요구하지 않도록 필요한 모든 정보를 State 내에 정의해야 한다 (캡슐화)</li>
</ul>
</li>
</ul>
</li>
<li>Action</li>
<li>Reward<ul>
<li>Agent가 학습하고자 하는 behavior를 반영해야 함</li>
</ul>
</li>
</ul>
<h2 id="mdp의-작동-방식">MDP의 작동 방식</h2>
<p>ex) Tic-Tac-Toe 게임을 이용해 RL 문제를 풀 때,</p>
<ol>
<li>초기 상태<ul>
<li>State: 놓여 있는 모든 토큰의 위치</li>
</ul>
</li>
<li>Agent의 행동 결정</li>
<li>Action<ul>
<li>이미 배치된 토큰을 피해 토큰 배치</li>
</ul>
</li>
<li>Env.의 출력 + 다음 State와 reward</li>
<li>Feedback   <ul>
<li>State와 Reward를 Agent에게 입력</li>
</ul>
</li>
</ol>
<ul>
<li>하나의 time step 끝, 2부터 5까지 반복하여 Terminal State까지 도달</li>
</ul>
<p>Agent의 목표는 보상의 총량을 최대화하는 것</p>
<blockquote>
<p>보상의 총량 = 즉각적인 보상 + 누적 보상</p>
</blockquote>
<ul>
<li>전체적인 흐름
<img src="https://velog.velcdn.com/images/scope_hyena/post/5fccb4cf-c7dd-4018-9adb-1b38a7082046/image.png" alt=""></li>
</ul>
<h3 id="trajectory">Trajectory</h3>
<p>Trajectory(궤적)는 하나의 time step에서의 state, action, next time step에서 참고할 현재의 reward를 정의한다</p>
<blockquote>
<p>${\text {trajectory}} = (s_t, a_t, r_{t+1})$</p>
</blockquote>
<ul>
<li>여기서 $t$는 time step을 나타낸다.</li>
</ul>
<h3 id="episode">Episode</h3>
<ul>
<li>시작 상태부터 time step을 반복하여 도달한 끝 상태까지의 일련의 순서</li>
<li>episode가 끝나면 시작 상태(initial state)로 reset</li>
<li>각 episode는 서로 무관함</li>
</ul>
<h2 id="agent와-env의-state-action-전이transition-제어">Agent와 Env.의 state-action 전이(transition) 제어</h2>
<ul>
<li>현재 상태를 고려해 agent가 next action을 결정</li>
<li>현재 상태와 next action이 결정되면 next state의 전이와 reward는 env.에 의해 control됨</li>
</ul>
<h2 id="agent가-action을-선택하는-법">Agent가 action을 선택하는 법</h2>
<p>agent는 state에서 다음 3가지를 기반으로 action을 결정함</p>
<ul>
<li>Return</li>
<li>Policy</li>
<li>Value</li>
</ul>
<h1 id="return-policy-value">Return, Policy, Value</h1>
<hr>
<h2 id="return">Return</h2>
<ul>
<li><strong>누적 보상</strong>
action 기간 동안 누적된 총 보상<ul>
<li>discount factor를 적용해 즉각적인 보상에 신경쓰도록 하며, time step이 많아져도 reward가 무한히 커지지 않도록 함</li>
</ul>
</li>
</ul>
<blockquote>
<p>${\text {return}} = \displaystyle\sum_{n=0}^{t}\gamma^{n}r_n$</p>
</blockquote>
<ul>
<li><p>$\gamma$ = discount factor, $t$ = time step</p>
<ul>
<li>n제곱의 이유:</li>
<li>$\gamma &lt; 1$이므로 누적 보상 값이 수렴하도록 하기 위함</li>
<li>later reward를 더 많이 깎음으로써 즉각적인 reward에 더 가치를 두도록 하기 위함</li>
</ul>
</li>
<li><p>같은 보상이면 더 빨리 받도록 한다.</p>
</li>
<li><p>Agent는 Return이 높도록 action을 선택한다.</p>
</li>
</ul>
<h2 id="policy">Policy</h2>
<ul>
<li>action을 선택하도록 하는 전략<ul>
<li>state를 next action에 매핑</li>
</ul>
</li>
</ul>
<blockquote>
<p>${\text {policy}} : s \longmapsto a$</p>
</blockquote>
<ul>
<li><p>Deterministic policy (결정적 정책)
특정 상태  특정 행동으로 고정</p>
</li>
<li><p>Stochastic policy (확률적 정책)
action의 확률에 따라 state에 대한 action을 변경</p>
</li>
<li><p>Agent는 시작할 때 대개는 useful policy가 없음</p>
<ul>
<li>RL을 통해 helpful policy를 천천히 학습</li>
</ul>
</li>
</ul>
<p>$\therefore{\ }$ Agent의 목표는 Return을 최대화하는 Policy를 따르는 것임</p>
<h2 id="value">Value</h2>
<ul>
<li><p>policy를 따라 action을 선택했을 때, 얻을 수 있는 Return의 기댓값 (평균)</p>
</li>
<li><p>Value의 두가지 유형</p>
<ul>
<li><p>State Value</p>
<blockquote>
<p>$V_s = s \longmapsto {\text {value}}$</p>
</blockquote>
</li>
<li><p>State-action Value</p>
<blockquote>
<p>$V_{(s, a)} = (s, a) \longmapsto {\text {value}}$</p>
</blockquote>
<ul>
<li>Q-Value라고도 함</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="value와-return-reward의-관계">Value와 Return, Reward의 관계</h3>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><strong>Reward</strong></td>
<td align="center">단일 action $\rarr$ 즉시 reward</td>
</tr>
<tr>
<td align="center"><strong>Return</strong></td>
<td align="center">episode $\rarr$ 누적 discounted reward</td>
</tr>
<tr>
<td align="center"><strong>Value</strong></td>
<td align="center">다수의 episode에 대한 Return의 기댓값</td>
</tr>
</tbody></table>
<h3 id="value가-policy에-영향을-받는-이유">Value가 policy에 영향을 받는 이유</h3>
<p>Value는 reward에 의한 return에 따름
-&gt; Reward는 state와 그에 따른 action에 따라 다름
-&gt; <strong>action은 policy에 따라 다름</strong></p>
<h2 id="policy-비교를-위한-value-function">Policy 비교를 위한 Value function</h2>
<blockquote>
<p>$\pi \ : \ \text{state} \longmapsto \text{action}$</p>
</blockquote>
<ul>
<li><p>$\pi$ = policy</p>
</li>
<li><p>각 정책을 따라 수행 후, Return을 평가해 각 정책에 따른 $V_s$, $V_{(s,a)}$를 결정할 수 있다.</p>
</li>
<li><p>각 Value function이 있으면 Value function을 이용해 Policy를 비교할 수 있다</p>
</li>
<li><p>Value function이 더 높은 policy는 higher Return을 산출하므로, better policy라고 할 수 있음</p>
</li>
<li><p>policy 중 best policy, 즉 highest Return을 내는 policy는 optimal policy라고 함</p>
</li>
</ul>
<p><strong>$\therefore{\ }$ RL 문제의 목표는 Optimal policy를 찾는 것임</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Swift Playgrounds에 바라는 개선점들]]></title>
            <link>https://velog.io/@scope_hyena/Swift-Playgrounds-wishlists-2024</link>
            <guid>https://velog.io/@scope_hyena/Swift-Playgrounds-wishlists-2024</guid>
            <pubDate>Fri, 05 Apr 2024 03:57:21 GMT</pubDate>
            <description><![CDATA[<p>iPad에서 앱을 개발할 수 있는 길을 열어준 iPad용 Playgrounds.</p>
<p>평소에 iPad를 Mac보다 더 즐겨쓰는 필자도 이 앱 덕분에 간단하게 생각나는 아이디어를 iPad만을 이용해서 종종 옮겨보곤 한다.</p>
<p>다만, 여러 부분에서 좀 아쉬운 점이 있는데 iPad라 그러려니 싶어도 좀 이해가 안되는 것들이 몇 있다.</p>
<h2 id="앱-배포의-문제">앱 배포의 문제</h2>
<p>Playgrounds를 접하는 유저층들은 주로 코딩을 처음 접하거나, 혹은 간단하게 개인 프로젝트를 하거나, 간단한 앱을 만들어 지인과 공유하고 싶은 라이트 개발자들이 대부분일 것이다.</p>
<p>Apple에서 현재 제공하는 Playgrounds 결과물, 즉, 바이너리 앱의 배포 방식은 2가지이다.</p>
<ul>
<li>Apple Developers 유료 계정을 이용한 App Store Connect 배포</li>
<li>Mac의 Xcode로 옮겨 빌드하기</li>
</ul>
<p>각각의 장점과 단점을 살펴보자.</p>
<hr>
<h3 id="유료-계정을-이용한-app-store-connect-배포">유료 계정을 이용한 App Store Connect 배포</h3>
<p>iPad용 Playgrounds에서 만들어진 App을 직접 바이너리 형태로 공유하거나 배포하기 위해서는 App Store Connect에 올리는 방법밖에 없다.</p>
<p><a href="https://developer.apple.com/kr/help/app-store-connect/get-started/app-store-connect-workflow/">→ App Store Connect를 통한 앱 제출 알아보기</a></p>
<p>확실히, App Store Connect를 이용해서 공유하면 훨씬 안전한 배포가 가능하고, TestFlight을 통해 다수의 사람들과 공유하여 사용할 수 있다는 장점이 있다.</p>
<p>그러나, App Store Connect에 앱을 제출하기 위해서는 유료 버전의 Apple Developers 계정 가입이 필수적이며, 비용은 1년에 약 12만원(USD $99)이다. 당연하지만 1년마다 갱신하지 않으면 인증서 만료로 인해 해당 앱은 사용 중지가 된다.</p>
<p>App Store에 앱을 제출하기 위해서 Playgrounds App을 만드는 개발자들이라면 문제가 되지 않겠지만, 그저 라이트한 앱을 소수/혼자 사용하기 위해 개발하는 개발자가 앱 하나 배포하자고 저 비용을 감내해야 한다는 것은 문제가 있다고 본다. 그 앱을 통해서 수익이 나는 것도 아닌데 말이다.</p>
<hr>
<h3 id="mac의-xcode로-옮겨-빌드하기">Mac의 Xcode로 옮겨 빌드하기</h3>
<p>이 방법을 사용하면 유료 계정이 없더라도 빌드가 가능하다. macOS용으로 제공되는 Apple용 IDE인 Xcode를 사용하여 Playgrounds App 프로젝트를 불러와 빌드하는 것이다. 이 방법을 사용하면 사용 중인 Mac과 연결한 기기들에 대해 앱 배포를 진행할 수 있으며, 무료 계정이더라도 7일간은 갱신 없이 사용이 가능하다. (7일 이후에는 인증서 갱신을 위해 다시 빌드해야 한다)</p>
<p>단점은 Mac을 가지고 있어야 하며, 해당 프로젝트를 macOS로 옮겨야 하고, 혼자 쓸 때는 몰라도 두 명 이상이 쓰기에는 많은 애로사항이 있다는 점이다. (Mac과 연결된 기기만 배포가 가능하므로)</p>
<hr>
<p>그나마 iPad에서 다른 사람과 앱을 공유하는 방법은, Playground 자체를 통째로 공유하는 것이다. 당연히 여기에는 소스 코드가 포함이 되며, 공유받는 사람은 iPad와 iPad용 Swift Playgrounds 앱을 보유하고 있어야 한다. 소스코드를 까보는 게 싫거나, 괜히 건드려서 오류가 나게 될까봐 신경 쓰이는 개발자라면 지인이 제발 소스를 건드리지 않기를 바라는 수밖에 없다. 또한, iPhone으로는 절대적으로 공유가 불가능하다. iPhone용으로는 Playgrounds 앱이 존재하지 않기 때문이다.</p>
<h2 id="apple의-확장성">Apple의 확장성</h2>
<p>Apple은 최근 EU의 반독점법 관련 제재에 따라 EU 지역 Apple 사용자들에 대한 Sideloading을 허용했다. 서드 파티 Marketplace 사용과 Webkit 기반이 아닌 다른 웹브라우저 사용 허가 등이 주요 항목이다.</p>
<p>최근 미국 법무부에서 Apple에 대해 반독점법 위반으로 고소를 했다는 소식이 들려왔다.
Swift Playgrounds 앱에서 앱 개발이 가능하게 만든 건 좋지만 그 앱을 자유롭게 사용할 수 있는 방법이 극히 제한적이라는 점은 솔직히 너무 아쉽다.</p>
<p>실제 Swift Playgrounds에서 앱을 미리보기로 실행해 보면, Playgrounds 내에서 실행되는 Preview Canvas의 형태가 아닌, 별도의 앱을 자체적으로 빌드하여 <strong>임시로 iPad에 설치하여</strong> 실행하고, 실행이 중지되면 <strong>해당 App을 비활성화</strong>하는 식으로 테스트 과정이 이루어지는 걸 확인할 수 있다.</p>
<p>즉, 온디바이스에서 빌드가 가능하다면, 임시로 실행하다가 숨기는 식으로만 할 게 아니라, 최종 단계에서 빌드된 앱을 사용할 수 있게 해 주면 되는 일 아닌가 싶다.</p>
<p>혹자는, “그럼 Mac 및 App Store 수익에 대한 타격이 있지 않겠냐”고 할 테지만, Playgrounds 자체에서 이미 사용할 수 있는 Package가 제한되어 있다. 라이트한 개발에 있어서는 충분하지만, Full Feature를 사용하려면 결국 Mac의 Xcode를 사용해야 한다. 
또한, 그런 앱들은 애초에 App Store에 배포하려고 만드는 앱도 아닐 뿐더러 설령 그렇다 하더라도 App Store 심사에서 대부분 탈락하게 된다.
즉, 어느 쪽이든 수익에 타격은 없거나 극히 미비하다는 것이다.
반면, 사용자는 원하는 미니 앱을 심심풀이로 만들어보는 등 앱 개발의 진입장벽이 하나 없어지는 셈이니 Apple이 지향하는 More Creative 측면에서는 분명 더 많은 Plus가 될 것이라 생각한다.</p>
<hr>
<h2 id="apple에-바란다">Apple에 바란다</h2>
<p>Playgrounds App에 대한 나의 희망사항은 아래와 같다.</p>
<ol>
<li>로컬 배포 기능을 추가한다.</li>
<li>로컬 배포 시 발생할 수 있는 보안 문제에 대한 경고를 추가하여 사용자가 최종적으로 선택할 수 있게 한다. (이미 Shortcuts에서는 지원하는 기능이다)</li>
<li>AirDrop을 통한 iPhone, iPad로의 App 공유가 가능하게 한다.</li>
</ol>
<p>이번 WWDC24에서 Swift와 관련된 내용이 얼마나 나올지 궁금해진다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Pygame 개인플젝 정리] 1. 맵 그리기 및 화면 이동(1)]]></title>
            <link>https://velog.io/@scope_hyena/Pygame-proj-02</link>
            <guid>https://velog.io/@scope_hyena/Pygame-proj-02</guid>
            <pubDate>Tue, 05 Mar 2024 15:53:03 GMT</pubDate>
            <description><![CDATA[<p><a href="https://github.com/ScopeHyena/env-pygame.git">GitHub Repo</a></p>
<p>이제 본격적으로 Pygame 프로젝트를 시작해본다.</p>
<p>이 프로젝트는 내가 좋아하는 소닉 더 헤지혹 시리즈의 &#39;플랫포밍 RPG&#39;를 만들어보자는 생각으로 장기간에 걸쳐 만들려고 구상했다.</p>
<p>이번 포스트에서는 pygame의 작동 테스트도 해보고 pygame에 대해서 좀 알아보고자 <a href="https://youtu.be/YWN8GcmJ-jA?si=1FdNIwpveSUH70q4">YouTube - Clear Code의 영상</a>을 참고하여 코드를 작성하였고, 구현은 기본적인 플랫폼 모습 및 화면 스크롤 기능 일부를 구현하였다.</p>
<h1 id="화면-준비">화면 준비</h1>
<p><code>Main.py</code>의 코드를 살펴보자.</p>
<pre><code class="language-python">from Settings import *

pygame.init()
screen = pygame.display.set_mode((screenWidth, screenHeight))</code></pre>
<p>pygame의 게임창에 대한 속성을 정의하고 pygame 인스턴스를 시작하는 코드이다. <code>screenWidth</code>와 <code>screenHeight</code>는 뒤에서 작성할 <code>Settings.py</code>에 들어있다.</p>
<pre><code class="language-python">while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    screen.fill(&#39;black&#39;)

    pygame.display.update()
    clock.tick(60)</code></pre>
<p>게임이 실행될 동안 닫기 버튼이 눌렸는지 확인하여 눌렸으면 게임을 끌 수 있도록 한다.
또한, Pygame의 디스플레이를 검은색으로 채우고 60fps마다 업데이트하도록 한다.</p>
<h1 id="임시-맵-데이터-준비">(임시) 맵 데이터 준비</h1>
<p>게임에서 사용할 임시 맵 데이터를 구성한다. 여기서 사용하는 맵 데이터는 배열 기반으로 하드코딩되어 있으며, 추후 전용 스프라이트 기반 맵에디터를 이용해 맵을 제작하려고 한다.</p>
<p>우선 맵 데이터는 아래와 같이 <code>Settings.py</code>에 정리했다.</p>
<pre><code class="language-python"># example map data
levelMap = [
&#39;                            &#39;,
&#39;                            &#39;,
&#39;                            &#39;,
&#39; XX    XXX             XX   &#39;,
&#39; XX                         &#39;,
&#39; XXXX         XX          XX&#39;,
&#39; XXXX       XX              &#39;,
&#39; XX    X  XXXX    XX  XX    &#39;,
&#39;       X  XXXX    XX  XXX   &#39;,
&#39;    XXXX  XXXXXX  XX  XXXX  &#39;,
&#39;XXXXXXXX  XXXXXX  XX  XXXX  &#39;
]</code></pre>
<p>그런 다음 아해에 타일의 크기, 스크린 크기를 정의했다.</p>
<pre><code class="language-python">tileSize = 64
screenWidth = 1280
screenHeight = 720</code></pre>
<h1 id="타일-데이터와-레벨-데이터-만들기">타일 데이터와 레벨 데이터 만들기</h1>
<p>위의 맵 데이터에서 <code>X</code>표시된 부분을 진짜 타일로 만들기 위해 타일 클래스를 만들었다.
(<code>Tiles.py</code> 파일 중)</p>
<pre><code class="language-python">class Tile(pygame.sprite.Sprite):
    def __init__(self,pos, size):
        super().__init__()
        self.image = pygame.Surface((size,size))
        self.image.fill(&#39;grey&#39;)
        self.rect = self.image.get_rect(topleft = pos)</code></pre>
<p>아래는 <code>Levels.py</code> 파일 일부이다.</p>
<pre><code class="language-python">class Level:
    def __init__(self, levelData, surface):
        self.displaySurface = surface
        self.setupLevel(levelData)


    def setupLevel(self, layout):
        self.tiles = pygame.sprite.Group()
        for rowIndex, row in enumerate(layout):
            for colIndex, cell in enumerate(row):
                if cell == &#39;X&#39;:
                    x = colIndex * tileSize
                    y = rowIndex * tileSize
                    tile = Tile((x,y), tileSize)
                    self.tiles.add(tile)</code></pre>
<p>먼저 <code>Tile()</code> 클래스에서 타일의 색상과 크기를 정해주고, <code>Level()</code> 클래스에서 <code>X</code>를 읽어와 <code>Tile()</code>에서 정의한 타일 형태로 채우는 코드를 작성했다.</p>
<pre><code class="language-python">def run(self):
    self.tiles.draw(self.displaySurface)</code></pre>
<h1 id="결과">결과</h1>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/057ae48c-ec30-496b-85f8-640f36c6c5d5/image.png" alt=""></p>
<h1 id="화면-이동">화면 이동</h1>
<p>현재 스크린에서는 맵의 전체가 다 나오지 못하기 때문에 카메라의 위치를 오른쪽으로 옮겨줘야 한다.
그러나 pygame에는 Unity처럼 카메라 기능이 존재하지는 않기 때문에, 카메라가 아닌 맵이 왼쪽으로 이동하는 식으로 카메라 스크롤을 구현한다.</p>
<pre><code class="language-python"># Tiles.py
def update(self, xShift):
    self.rect.x += xShift 

# Levels.py, in run() method
self.tiles.update(-1)</code></pre>
<h1 id="결과-1">결과</h1>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/450c8a56-a2a4-464b-839e-ab5659506ac4/image.gif" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Pygame 개인플젝 정리] 0. 개발환경 셋팅]]></title>
            <link>https://velog.io/@scope_hyena/Pygame-proj-01</link>
            <guid>https://velog.io/@scope_hyena/Pygame-proj-01</guid>
            <pubDate>Sun, 03 Mar 2024 13:00:42 GMT</pubDate>
            <description><![CDATA[<p>컴퓨터 포맷 이후 One-PC 환경으로 개발 환경을 분리 구축하고자 다양한 방법을 시도해보았다.</p>
<h1 id="docker">Docker</h1>
<p>Pygame에 필요한 비디오 드라이버를 못잡기 때문에 포기했다.
macOS를 돌릴 수 있는 Docker 이미지도 있었지만, 기존에 사용하던 가상 머신 방식과 별 차이가 없어서 역시 걸렀다.</p>
<h1 id="conda">conda</h1>
<p>conda가 지금까지 썼던 가장 괜찮았던 환경 분리 방법이었으나, conda로 환경을 구축할 시, numpy, scipy등의 데이터 분석용 라이브러리가 같이 설치된다는 문제가 있어 버리기로 했다.</p>
<h1 id="pipenv">PIPenv</h1>
<p><strong>Python에서 공식으로 지원하는 가상환경 구축 방법이라기에</strong> 설치해서 구성하려고 했지만, 지원이 이전에 끊겨서 가상 환경 생성을 해도 pygame 모듈이 import되지 않는 문제가 있었다.</p>
<h1 id="pyenv--venv">Pyenv + venv</h1>
<p>그래서 마지막으로 시도한 방법은 pyenv와 venv를 이용하여 가상환경을 구축하는 것이었다. pyenv를 이용해 Python 버전을 3.10으로 고정하고, venv로 필요한 pip 패키지만을 설치하는 방식을 사용했다.</p>
<h1 id="pyenv-환경-구축">pyenv 환경 구축</h1>
<p>필자가 사용한 컴퓨터 환경은 macOS 14 Sonoma, M2, RAM 16GB였다.</p>
<p>먼저 Homebrew를 이용해 pyenv를 설치했다.</p>
<pre><code class="language-bash">brew install pyenv</code></pre>
<p>그런 후 아래의 명령어를 이용해 환경설정을 한다.</p>
<pre><code class="language-bash">echo -e &#39;if command -v pyenv 1&gt;/dev/null 2&gt;&amp;1; then\n  eval &quot;$(pyenv init -)&quot;\nfi&#39; &gt;&gt; ~/.zshrc</code></pre>
<pre><code class="language-bash">source ~/.zshrc</code></pre>
<p>이제 pyenv를 이용해 내가 사용할 python 3.10을 설치해준다.</p>
<pre><code class="language-bash">pyenv install 3.10</code></pre>
<p>설치가 완료되면 python 3.10을 global로 설정해준다.</p>
<pre><code class="language-bash">pyenv global 3.10</code></pre>
<h1 id="venv-환경-구축">venv 환경 구축</h1>
<p>이제 venv를 이용해서 python의 패키지를 분리할 수 있는 가상환경을 생성한다. 먼저 내가 프로젝트를 만들 폴더로 이동해 다음과 같이 입력한다.</p>
<pre><code class="language-bash">python3 -m venv env-pygame</code></pre>
<p>그러면 작업에 필요한 폴더와 가상환경 공간이 생성된다.</p>
<p>이 가상 환경을 사용하기 위해서는 아래와 같이 입력한다.</p>
<pre><code class="language-bash">source env-pygame/bin/activate</code></pre>
<p>그러면 프롬프트 앞에 (env-pygame)이 붙으며 가상환경이 시작된다.</p>
<p>이제 다음을 입력해 pygame을 설치한다.</p>
<pre><code class="language-bash">pip install pygame</code></pre>
<p>가상환경을 종료하고 싶다면 아래와 같이 입력한다.</p>
<pre><code class="language-bash">deactivate</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[단축어 개발 환경 미리 보기]]></title>
            <link>https://velog.io/@scope_hyena/shortcuts-preview-ide</link>
            <guid>https://velog.io/@scope_hyena/shortcuts-preview-ide</guid>
            <pubDate>Sun, 18 Feb 2024 12:59:25 GMT</pubDate>
            <description><![CDATA[<p>단축어는 메인, 자동화, 편집기의 세 가지 화면으로 나뉜다.</p>
<h1 id="메인">메인</h1>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/86b82a03-9af7-49c8-89ab-e6c5e1b7c164/image.png" alt=""></p>
<h1 id="자동화">자동화</h1>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/0be9dbbb-3b25-451f-953a-d4a70fd62b01/image.png" alt=""></p>
<h1 id="편집기">편집기</h1>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/e84732f0-bd51-4f5d-b560-c9d33b744fd9/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ns-3 혼공로그] #05 - 파이썬 ns-3 환경 설정하기]]></title>
            <link>https://velog.io/@scope_hyena/ns3studylog05</link>
            <guid>https://velog.io/@scope_hyena/ns3studylog05</guid>
            <pubDate>Wed, 03 Jan 2024 04:20:43 GMT</pubDate>
            <description><![CDATA[<p>지난 포스트에서 ns-3를 설치했다. 이번에는 ns-3에서 파이썬 사용을 위한 환경 설정을 해봤다.
ns-3는 기본적으로 C++ 개발을 전제로 하여 만들어진 툴이므로 C++로 코드를 작성했을 때 제일 효율이 좋지만, 파이썬을 이용하면 좀 더 이해하기 쉬운 코드 작성이 가능하다는 장점이 있다.</p>
<p>하지만, Pylance 등에서 ns-3에 대한 지원을 하지 않기 때문에 오히려 불편할 수 있다.</p>
<h1 id="사전-패키지-설치">사전 패키지 설치</h1>
<p>ns-3에서 Python 개발을 하기 위해서는 pip를 통한 cppyy 2.4.2와 bazaar 패키지가 필요하다.
우분투에서는 Python과 Python PIP를 따로 제공하기 때문에 pip도 설치한다.</p>
<p>정리</p>
<ul>
<li>pip - 파이썬 페키지를 설치하기 위해 필요</li>
<li>bzr - 파이썬 바인딩을 활성화시키기 위해 필요</li>
<li>cppyy==2.4.2 - ns-3에 있는 C++ 모듈 및 C++과의 호환성을 위해 필요</li>
</ul>
<p>먼저 apt를 이용해 pip와 bzr을 설치해 준다.</p>
<pre><code class="language-bash">$ sudo apt install python3-pip bzr</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/d428520a-e1c6-430a-b35f-fb5db52745c5/image.png" alt=""></p>
<p>그런 다음 pip를 이용해 cppyy 2.4.2를 설치한다. ns는 2.4.2 이하 버전을 설치할 것을 안내하고 있다.</p>
<pre><code class="language-bash">$ pip3 install --user cppyy==2.4.2</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/3065bd1d-8be3-4e1d-b9c5-99c9a1683e28/image.png" alt=""></p>
<h1 id="테스트">테스트</h1>
<p>이제 설치가 완료되었으니, ns-3에서 제공하는 파이썬 예제 파일(<code>first.py</code>)를 실행해본다.</p>
<pre><code class="language-bash">$ ./waf --pyrun examples/tutorial/first.py</code></pre>
<p>Waf에서 pyrun 옵션을 주면 된다.</p>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/7125f0e3-3d7b-4c3c-9f33-bdc593f62a99/image.png" alt=""></p>
<p>내친 김에 아래 코드도 작성하여 테스트해보았다.</p>
<pre><code class="language-python">import ns.core
import sys

def LOG_UNCOND(msg: str):
    while True:
        print(msg, file=sys.stderr)
        if False: continue
        break

LOG_UNCOND(&quot;Hello Simulator&quot;)</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/c163c9b8-b2d3-4b17-86c6-c081416d7282/image.png" alt=""></p>
<h1 id="마무리">마무리</h1>
<p>ns-3에서 파이썬을 사용하기 위한 환경 설정을 해보았다.
파이썬을 사용할 수 있는 건 좋은 일이지만, 포스트 첫머리에도 써둔 것처럼 Pylance 등의 인텔리센스가 작동하지 않는다는 문제가 있어, 오히려 C++을 쓰는 쪽이 더 편할 수도 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ns-3 혼공로그] #04 - ns-3 설치]]></title>
            <link>https://velog.io/@scope_hyena/ns3studylog04</link>
            <guid>https://velog.io/@scope_hyena/ns3studylog04</guid>
            <pubDate>Tue, 02 Jan 2024 03:08:18 GMT</pubDate>
            <description><![CDATA[<p>이제 본격적으로 ns-3를 설치해보았다. 장을 2개로 나누어 이번 포스트에서는 ns-3 설치를 다룰 것이고 다음 포스트에선 ns-3에서 Python을 사용하기 위한 설정을 다루려고 한다.</p>
<p>내가 사용하는 ns-3 버전은 3.30이다. 더 최신 것도 있지만 배운 걸 이걸로 배웠기에…</p>
<h1 id="사전-의존성-패키지-설치">사전 의존성 패키지 설치</h1>
<p>ns-3는 C++과 Python 기반으로 작동하는 도구이기 때문에 관련 패키지가 설치되어 있어야 한다.
아래의 명령어로 패키지들을 전부 설치해준다.</p>
<pre><code class="language-bash">$ sudo apt-get install g++ cmake python3 mercurial -y</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/269ea8fc-4557-401c-b8fc-f68e70af37e2/image.png" alt=""></p>
<h1 id="ns-3-설치">ns-3 설치</h1>
<p>ns-3를 설치하는 방법은 크게 두가지가 있다.</p>
<ol>
<li><a href="https://www.nsnam.org">ns-3 홈페이지</a>에서 다운로드하여 설치하는 방법</li>
<li>git clone을 통해 설치하는 방법</li>
</ol>
<p>나는 귀찮았기 때문에 그냥 홈페이지에서 받아서 설치하기로 했다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/4e3c6277-1d4f-43e7-95b9-3c3d788076fa/image.png" alt=""></p>
<p>사이트에서 Download를 누르면 버전을 선택할 수 있는 페이지가 나오는데, 나는 ns-3.30을 찾아서 내려받았다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/71a4521f-029e-4fe3-81fc-f3e44a08f764/image.png" alt=""></p>
<p>내려받은 ns-3 패키지를 파일 관리자를 이용해 압축을 풀어주었다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/2c886b8e-8ac4-45fc-8086-cb140d872e77/image.png" alt=""></p>
<p>이제 ns-3를 본격적으로 설치하기 위해 터미널을 열어주고 ns-3가 설치된 폴더까지 이동했다.</p>
<pre><code class="language-bash">$ cd ~/delveloper/ns-allinone-3.30.1/ns-3.30.1</code></pre>
<p>그런 다음 ns-3에 들어있는 예제들과 테스트 스크립트를 모두 활성화시킨다.</p>
<pre><code class="language-bash">$ ./waf configure --enable-examples --enable-tests</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/85afde0d-5cfe-4ce9-a65b-22b401066483/image.png" alt="">
그리고 이 스크립트를 모두 빌드한다.</p>
<pre><code class="language-bash">$ ./waf build</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/a6b4d737-cd43-4d0b-915b-4e78b33de1fa/image.png" alt="">
시간이 좀 걸린다. 다 끝나고 나면 아래와 같이 활성화된 모듈 목록이 나온다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/8a15fdb1-1f9e-4d0a-84a1-f34eb035f06f/image.png" alt="">
설치 끝</p>
<h1 id="ns-3-테스트">ns-3 테스트</h1>
<p>먼저 메뉴얼에 있는 대로 테스트를 해보기 위해 아래와 같이 진행했다.</p>
<pre><code class="language-bash">$ ./test.py -c core</code></pre>
<p>그럼 아래처럼 테스트하는 모듈이 표출되고 결과에 따라 PASS, SKIP, 오류등으로 나뉜다. 시간이 좀 걸린다.</p>
<p> <img src="https://velog.velcdn.com/images/scope_hyena/post/1606288f-0740-4cda-8912-5167ba63e37a/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/5a2cf9c4-49e4-4ee1-80ca-814f3033b729/image.png" alt=""></p>
<p>테스트가 끝났을 때 PASS, Skipped만 존재한다면 현재 ns-3는 정상적으로 잘 동작한다는 의미이다.</p>
<p>이제 아래와 같이 ns-3에서 제공하는 예제 파일을 실행시켜본다.</p>
<pre><code class="language-bash">$ ./waf --run hello-simulator</code></pre>
<p>그럼 ns-3가 알아서 예제 속에 있는 <code>hello-simulator.cc</code>를 찾아 실행시켜 준다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/53d24c54-683f-48d8-b766-fc5b0642d2b2/image.png" alt=""></p>
<h1 id="마무리">마무리</h1>
<p>일단 ns-3의 기본적인 설치는 모두 끝났다. 다음 포스트에서는 Python 사용을 위한 ns-3 설정을 해보도록 하겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ns-3 혼공로그] #03 - 우분투 스냅샷 설정 및 vscode 설치]]></title>
            <link>https://velog.io/@scope_hyena/ns3studylog03</link>
            <guid>https://velog.io/@scope_hyena/ns3studylog03</guid>
            <pubDate>Wed, 27 Dec 2023 06:13:22 GMT</pubDate>
            <description><![CDATA[<p>랩실컴(Windows+VirtualBox)은 VirtualBox 자체에 스냅샷 기능이 있어 스냅샷을 지정하기가 편리하지만, VirtualBuddy에는 스냅샷 기능이 없다.</p>
<p>우분투 자체에서 스냅샷을 생성해야 하는데, 스냅샷 설정을 위해서는 <a href="https://github.com/teejee2008/timeshift">TimeShift</a>라는 패키지를 이용하기로 했다.</p>
<h1 id="저장-장치-추가">저장 장치 추가</h1>
<p>이 패키지를 이용하기 위해서는 먼저 VirtualBuddy에 저장 장치를 추가해야 한다.
나는 50GB(실용량 54GB)짜리 외장 드라이브를 추가했다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/7ade4281-3e09-4078-984d-0184c493fe90/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/2725b276-b335-4a2b-be53-dd257f4e4db8/image.png" alt=""></p>
<p>(여기서 잠깐 BusyBox 부팅 오류가 나서 애먹었지만, 다행히 root 위치를 얄려주기에 fsck로 잘 넘어갔다)</p>
<p>그런 뒤, 우분투로 부팅하여, Disk 앱에서 해당 장치를 찾아 ext4로 포맷해주었다. <strong>TimeShift는 리눅스 저장장치만을 인식</strong>하기 때문에 이는 매우 중요하다.</p>
<h1 id="timeshift-설치">TimeShift 설치</h1>
<p>이제 터미널을 열어 다음 명령어를 통해 TimeShift를 설치했다.</p>
<pre><code class="language-bash">$ sudo apt install timeshift -y</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/6434e8c9-4965-40de-ab5e-a6551064a31b/image.png" alt=""></p>
<p>설치가 완료되면 TimeShift를 실행해 아래와 같이 설정 단계를 완료한다.</p>
<ol>
<li>rsync 옵션 - 우분투는 btrfs를 지원하지 않기 때문에 rsync 옵션으로 해야 한다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/20bb19fb-ece9-4667-8f7b-8003542cf984/image.png" alt=""></li>
<li>백업을 저장할 위치 선택
<img src="https://velog.velcdn.com/images/scope_hyena/post/24508787-a271-46cb-adaa-4bc12dc9a787/image.png" alt=""></li>
<li>자동 백업 스케줄 선택 - 나는 수동으로 스냅샷을 저장할 것이기 때문에 아무것도 선택하지 않고 넘어갔다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/cc96249a-4df2-402b-88ab-5ce7ef950c16/image.png" alt=""></li>
<li>User 홈 디렉토리 선택, 그냥 Next 누르면 된다.</li>
<li>설정 완료</li>
<li>앱 화면
<img src="https://velog.velcdn.com/images/scope_hyena/post/39c8b403-d539-46fe-bfbc-dff89859da78/image.png" alt=""></li>
</ol>
<p>화면 아래쪽에 스케줄 일정을 선택하지 않았다고 경고 문구를 띄우지만 수동으로 스냅샷을 생성하면 되므로 무시했다.</p>
<p>Create를 눌러 새 스냅샷을 만들어봤다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/0cf2c77b-22e1-4922-8044-c4741a2e6a7c/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/5e056158-c4c7-45ae-baa5-e453dfb8036f/image.png" alt=""></p>
<p>예상외로 빠르게 만들어져 꽤나 놀랐다.</p>
<h1 id="visual-studio-code-설치">Visual Studio Code 설치</h1>
<p>이제 VSCode를 설치할 차례다. 필자는 Swift를 제외하면 VSCode 편집기를 선호하는 편이다. </p>
<p>우선, ARM용 snap store에서는 vscode를 제공하지 않는다.
x64 환경이면 그냥</p>
<pre><code class="language-bash">$ sudo snap install --classic code</code></pre>
<p>하면 끝났을 일을…</p>
<p>일단 MS VSCode 웹사이트로 이동하여 ARM용 deb 파일을 받아준다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/088228fc-32ba-4df4-b079-4befccdc353b/image.png" alt=""></p>
<p>우분투는 Ubuntu Software Store가 있어, 파일 관리자에서 Install Package 기능을 손쉽게 제공하고 있다.</p>
<p><strong>단, x64만</strong>.
<img src="https://velog.velcdn.com/images/scope_hyena/post/3efdcfc6-0ca2-4531-a5ff-627e4cf43198/image.png" alt=""></p>
<p>arm용 우분투 서버에는 그런 거 없다.</p>
<p>하지만, 만능 터미널이 있다면 문제 없겠지…
터미널에서 Downloads 폴더로 이동해 다음과 같이 입력한다.</p>
<pre><code class="language-bash">$ sudo dpkg -i code_1.85.1-17blahblah</code></pre>
<p>(사실 <code>code</code>까지만 치고 Tab키 누르면 알아서 채워준다)
<img src="https://velog.velcdn.com/images/scope_hyena/post/066d706c-f49b-415d-99af-3b2fa6aa3a4b/image.png" alt=""></p>
<p>설치가 다 완료됐으니 VSCode 찾아서 켜 주고
<img src="https://velog.velcdn.com/images/scope_hyena/post/5e759119-807d-418a-b2c0-80d3f03b3561/image.png" alt=""></p>
<p>마무리로 TimeShift를 이용해 스냅샷을 다시 만들었다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/33ef8c8f-25d7-43b3-a449-7ab1b3ed551c/image.png" alt=""></p>
<h1 id="마무리">마무리</h1>
<p>이제 슬슬 ns-3를 설치할 시간이 왔다. arm 버전으로 하려니까 준비 과정이 많아서 좀 귀찮긴 하다.
(하지만 qemu로 x64 에뮬레이션하면 느릿느릿 굼벵이가 되는걸…)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[ns-3 혼공로그] #02 - 공유 폴더 설정]]></title>
            <link>https://velog.io/@scope_hyena/ns3studylog02</link>
            <guid>https://velog.io/@scope_hyena/ns3studylog02</guid>
            <pubDate>Wed, 27 Dec 2023 04:15:26 GMT</pubDate>
            <description><![CDATA[<p>작업 환경이 물리적, 네트워크적으로 분리된 랩실컴과 개인컴이다 보니, 매번 따로따로 진행하는 것이 번거로울 것 같아, git을 이용한 공유 폴더를 만들어 두고자 한다.</p>
<h1 id="깃허브에서-레포지토리-생성하기">깃허브에서 레포지토리 생성하기</h1>
<p>먼저 <a href="https://github.com">GitHub</a>에서 레포지토리를 아래와 같이 만들어주었다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/25218de1-2b60-4698-ada0-66dfc1408ab7/image.png" alt=""></p>
<p>이제 양쪽 컴퓨터에 이 레포지토리를 클론해오기만 하면 끝</p>
<pre><code class="language-bash">$ git clone https://github.com/ScopeHyena/ns3-workspace.git</code></pre>
<p>먼저 랩실컴:
<img src="https://velog.velcdn.com/images/scope_hyena/post/b739bb14-3c4a-4cbd-9036-32fe04a8c8d1/image.png" alt=""></p>
<p>그리고 개인컴은 <a href="https://remotedesktop.google.com/">크롬 원격 데스크탑</a>으로 원격 접속하여 VirtualBuddy를 켜고 똑같이 클론:
<img src="https://velog.velcdn.com/images/scope_hyena/post/1d547b47-8e06-471b-bfa5-11402d36a49f/image.png" alt=""></p>
<h1 id="테스트">테스트</h1>
<p>이제 잘 되는지 테스트해보기 위해서 간단한 예제 파일을 준비하고 깃헙에 푸시해보았다. (랩실컴으로 진행)</p>
<pre><code class="language-bash">$ touch helloworld.py
$ vi helloworld.py</code></pre>
<pre><code class="language-python">print(“Hello World!”)
`
```bash
$ git add .
$ git commit -m “test commit”
$ git push</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/54e2b1ba-7ad6-4176-926b-a7aa914394ee/image.png" alt=""></p>
<p>푸시 과정 중간에 github에 로그인하고 토큰 넣어야 하는 절차가 있어서 콘솔창이 지저분해졌지만 잘 올라갔다.</p>
<p>이제 개인컴 (iPad에서 Chrome RD로 진행)</p>
<pre><code class="language-bash">$ git fetch
$ git pull
$ python3 helloworld.py</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/74b1ab81-b8e3-4417-9270-bd01938445dc/image.png" alt=""></p>
<p>잘 공유됐음을 확인할 수 있었다.</p>
<h1 id="마무리">마무리</h1>
<p>포스트에서 구글 원격 데스크탑을 쓰고 있긴 하지만, 랩실컴에서 데스크탑 연결이 쉽지 않을 뿐만 아니라, Shift Key의 사용이 불가능해지는 등의 문제가 있어, 원격 접속만으로는 힘들었다.</p>
<p>어차피 파일 공유만 가능하면 되니, 이 방식에 만족하려고 한다.</p>
<p>랩실이나 집이나 매번 컴퓨터를 끼고 살기 때문에 컴퓨터를 쓰는 시간 내내 ns-3를 익힐 수 있으면 좋겠다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NS-3 혼공로그] #01 - Ubuntu 설치]]></title>
            <link>https://velog.io/@scope_hyena/ns3studylog01</link>
            <guid>https://velog.io/@scope_hyena/ns3studylog01</guid>
            <pubDate>Tue, 26 Dec 2023 16:04:47 GMT</pubDate>
            <description><![CDATA[<p>대학원에서 NS-3라는 툴을 알게 되었다.
안그래도 무선 통신 때문에 골치 아팠는 이참에 제대로 익혀서 써먹어보고 싶어졌다.</p>
<p>필자는 우선 두 가지 환경을 셋팅하려고 한다.</p>
<ol>
<li>대학원 랩실에 있는 x64 기반의 Windows 10 + VirtualBox (이미 셋팅 완료)</li>
<li>개인용 컴퓨터 - M2 Mac mini + VirtualBuddy</li>
</ol>
<p>2번 과정을 하면서 셋팅을 해보려고 한다.</p>
<h1 id="준비물">준비물</h1>
<p>먼저 <a href="https://github.com/insidegui/VirtualBuddy">VirtualBuddy</a>를 받아준다. 이 앱은 원래 개인컴에서 개인용과 업무용을 분리하려고 macOS 가상화 방법을 찾다가 발견했는데, 생각보다 성능이 매우 괜찮아서 계속 쓰고 있다. 복잡한 명령어 없이 보통의 가상머신을 사용하듯 사용 가능한 가상화 앱으로, Linux 가상화도 지원하고 있다.</p>
<p>또한, <a href="https://cdimage.ubuntu.com/releases/focal/release/">ubuntu 20.04 Server for ARM64 설치 이미지</a>도 받아 둔다. (NS-3.30을 돌리기 위해 일부러 낮은 버전을 설치했다)</p>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/21c61157-b78f-4efb-ab44-e014e18c22f9/image.png" alt=""></p>
<h1 id="설치">설치</h1>
<p>먼저 가상 머신을 생성해준다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/46643f19-c273-464f-8813-bd0e597c9f4e/image.png" alt=""></p>
<p>종류는 Linux, 이미 받아둔 설치 파일을 사용하기 위해 Open custom ISO 어쩌고를 선택하고, 다운로드 받은 설치 이미지 선택
<img src="https://velog.velcdn.com/images/scope_hyena/post/c002fff7-fa30-48de-9c1e-feebe6ff758a/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/a36269a5-8a63-408c-beb0-66ead97ee5d6/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/d6a7b2b6-3a33-4504-8bd3-3824dbbf8982/image.png" alt=""></p>
<p>그런 다음, 이름을 지정해주고, 세부 사양까지 조정해주면 완료
<img src="https://velog.velcdn.com/images/scope_hyena/post/03e860af-f638-42a9-b3cb-f669527316e3/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/d2bd961e-51df-4748-8137-fbba0c62253e/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/a6500804-7a37-4ee4-a695-80fd4bc94e11/image.png" alt=""></p>
<p>이제 부팅해주고 화면 따라서 Done만 계속 눌러주면 설치 끝
<img src="https://velog.velcdn.com/images/scope_hyena/post/93d052d3-27a6-4986-a501-16cbb6980669/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/d43bafc6-454b-4f6d-a8ef-e75f563076f8/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/088a99ed-2e0c-406e-ad91-11c5023a3756/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/5eb2390a-161e-410e-af44-f182d2fb8624/image.png" alt=""></p>
<p>재부팅해주고 로그인하면 검정 화면만이 반겨주고 있다.
<img src="https://velog.velcdn.com/images/scope_hyena/post/393bfa22-7c53-40f6-80e6-b496d27c79ca/image.png" alt=""></p>
<p>이렇게는 쓰기 불편하니, 우분투 데스크탑 설치</p>
<pre><code class="language-bash">$ sudo apt-get install ubuntu-desktop</code></pre>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/1443db3d-120f-43bc-be73-4567c57a7347/image.png" alt=""></p>
<p>설치에 시간이 좀 걸리니 그사이에 다른 일 하고 오다 보면 설치가 끝나 있고, 재부팅해주면,</p>
<p><img src="https://velog.velcdn.com/images/scope_hyena/post/c82fa665-7795-4d78-8adb-c8b39e221ab4/image.png" alt="">
<img src="https://velog.velcdn.com/images/scope_hyena/post/827aa5b4-e9fc-48ec-8e30-6413cea31c3c/image.png" alt=""></p>
<p>매우 익숙한 보라보라한 데스크탑 화면이 보인다.</p>
<h1 id="마무리">마무리</h1>
<p>여기까지가 우분투 설치에 관한 내용이었고, 이 다음은 ns-3 설치 및 설정이 되지 않을까 싶다.
그 전에, VirtualBuddy는 스냅샷 기능을 지원하지 않기 때문에, 리셋하는 게 싫다면 저장소 하나를 더 추가하여 Ubuntu 스냅샷 기능을 사용해야 할 듯 싶다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[단축어를 위한 환경 설정하기]]></title>
            <link>https://velog.io/@scope_hyena/shortcuts-set-env</link>
            <guid>https://velog.io/@scope_hyena/shortcuts-set-env</guid>
            <pubDate>Wed, 29 Nov 2023 03:19:47 GMT</pubDate>
            <description><![CDATA[<h1 id="시스템-요구-사항">시스템 요구 사항</h1>
<p>단축어 앱은 2017년 이래로 6년간 많은 명령이 추가되었고 지원하는 플랫폼별로 사용할 수 있는 명령도 조금씩 다르다. 현재 단축어를 이용하기 위한 최소 요구 사항은 아래와 같다.</p>
<ul>
<li>iOS 13</li>
<li>iPadOS 13</li>
<li>watchOS 7 (실행 전용)</li>
<li>macOS 12 Monterey</li>
</ul>
<p>위의 플랫폼은 모두 기본적으로 단축어 앱이 사전 설치되어 있다. 다만, iOS와 iPadOS에서 앱을 삭제한 경우에는 App Store에서 다운로드 받을 수 있다.</p>
<p><a href="https://apps.apple.com/app/%EB%8B%A8%EC%B6%95%EC%96%B4/id1462947752">App Store에서 단축어 다운로드</a></p>
<p><img src="https://i.esdrop.com/d/f/gU8QFuHdhU/uOpHf4ITco.png" alt="iOS">
<img src="https://i.esdrop.com/d/f/gU8QFuHdhU/HnJZThlrK4.png" alt="iPsdOS"></p>
<p>앱을 실행시키기 위해서는 아래의 아이콘을 찾아 실행시키면 된다.</p>
<p><img src="https://i.esdrop.com/d/f/gU8QFuHdhU/N9WNL7gFmP.png" alt="Icon"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[단축어 개발문서 - 목차]]></title>
            <link>https://velog.io/@scope_hyena/shortcuts-dev-doc-contents</link>
            <guid>https://velog.io/@scope_hyena/shortcuts-dev-doc-contents</guid>
            <pubDate>Tue, 28 Nov 2023 15:50:13 GMT</pubDate>
            <description><![CDATA[<p><a href="https://apps.apple.com/kr/app/%EB%8B%A8%EC%B6%95%EC%96%B4/id915249334">Siri 단축어</a>는 Apple이 <a href="https://www.linkedin.com/in/weinsteinari/">DeskConnect</a>에서 2015년에 개발한 Workflow를 2017년에 인수하여, 2018년 WWDC 2018에서 Siri 기능을 추가하여 내놓은 비주얼 스크립팅 언어이다.</p>
<p>Apple에서 비슷한 목적으로 제공하는 <a href="https://support.apple.com/ko-kr/guide/automator/welcome/mac">Automator</a>, <a href="https://support.apple.com/ko-kr/guide/terminal/trml1003/mac">AppleScript</a>와 호환되며 자체적으로 Safari에서 사용 가능한 Javascript의 실행도 제공하고 있다. 또한, iOS/iPadOS에서 사용할 수 있는 <a href="https://apps.apple.com/kr/app/pythonista-3/id1085978097">Pythonista</a> 앱과 같은 Python IDE와 연계하여 Python 코드를 실행시킬 수 있는 기능도 가지고 있다.</p>
<h1 id="단축어-시작하기">단축어 시작하기</h1>
<p><a href="https://velog.io/@scope_hyena/shortcuts-set-env">단축어를 위한 환경 설정하기</a>
<a href="https://velog.io/@scope_hyena/shortcuts-preview-ide">단축어 개발 환경 미리 보기</a>
단축어 갤러리 둘러보기</p>
<h1 id="문서">문서</h1>
<p>단축어에서 사용되는 블록에 대한 설명입니다.</p>
<p>내장 블록에 대한 문서
앱 API 블록에 대한 문서
단축어 자동화에 대한 문서</p>
<h1 id="고급">고급</h1>
<p>Automator와 연계하기
Python과 연계하기
Web API 이용하기</p>
<p>* 이 문서는 계속해서 채워져 나갈 예정입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Moving Blogs….]]></title>
            <link>https://velog.io/@scope_hyena/Moving-Blogs</link>
            <guid>https://velog.io/@scope_hyena/Moving-Blogs</guid>
            <pubDate>Sat, 25 Nov 2023 03:39:13 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@scope_hyena/Moving-Blogs-kr">한국어 포스트 보기</a></p>
<p>It was my first post on Velog, and the advantages and disadvantages of the blog platforms I&#39;ve been through have been so clear... I&#39;m going to talk about why I came to Velog to test it.</p>
<h1 id="blog-wandering">Blog Wandering</h1>
<h2 id="naver-blog">Naver blog</h2>
<p>It wasn&#39;t that long ago when I started posting through blogs in earnest.</p>
<p>It was quite convenient to blog at the time, and I created a blog through Naver, where there were many domestic users, and that would have been right before Smart Editor ONE came out.</p>
<p>The layout theme was supported in quite a variety, and it was easy to write.
The mobile app was also supported, so it was easy to write it on the go.</p>
<p>However, there were fatal shortcomings,</p>
<ul>
<li>Spam notes says sell my blog to them, that come every time you forget</li>
<li>Lack of support for <strong>code blocks</strong></li>
</ul>
<p>I don&#39;t know anything else, but it was too bad that I lacked support for code blocks.
By my standards, not only was it not that easy to put the code in, but the highlight support was poor.</p>
<p>So I decided to change my blog after 3 years.
With Notion, a document platform that was very popular at the time.</p>
<h1 id="notion-settler">Notion Settler</h1>
<h2 id="notion-and-markdown-and-oopy">Notion and Markdown, and oopy</h2>
<p>It didn&#39;t take long for me to write on Notion until I realised the amazing power of Notion. Markdown syntax and full-bodied shortcuts support, and embedding features for codeblocks that work pretty well. Notion was providing most of the features I needed.</p>
<p>In addition, it also provides a mobile Notion app, so it&#39;s very convenient, so I&#39;ve been writing on Notion for quite a long time.</p>
<p>One disappointment was the loading speed of the Notion page, which was easily resolved thanks to a hosting tool dedicated to the notation page called oopy.</p>
<p>While writing in Notion, a problem that didn&#39;t exist at first occurred now, and a problem that I didn&#39;t feel in the past exploded in an instant.</p>
<ul>
<li>It is not a free useable platform. Of course, Notion itself is free to use, but there is a limit on the number of blocks per workspace, so you need to upgrade your plan for around $10 per month to run as a blog.</li>
<li>As I said earlier, posting a webpage only with Notion is too slow to load. You can host it with oopy, but hosting also requires a subscription of about $4 per month.</li>
<li>Hosting with oopy has the disadvantage of not being able to take advantage of the latest features of Notion.</li>
<li>The Notion server often down.</li>
<li>It&#39;s hard to use the comment function.</li>
<li>When writing with an <strong>app on an iPad, the letters are too small</strong> to use Notion.</li>
</ul>
<p>The last drawback, in particular, was quite a fatal one for me, who mainly uses the iPad. Also, for me, who has a very big difference between writing and not writing, it was burdensome to pay about 20,000 won a month.</p>
<p>So I decided to find another platform.</p>
<h1 id="a-brief-wanderlust-and-settlement">A Brief Wanderlust And Settlement</h1>
<h2 id="github-pages">GitHub Pages</h2>
<p>At first, I thought about using GitHub Pages and actually tried setting it up.
However, errors, errors, <strong>ERRORS</strong>. For me, who has no experience in web development, it feels like I bit more than I could chew that I keep a blog while learning web development.</p>
<p>I think it&#39;s a platform that will be useful if I do well, but it doesn&#39;t <strong>even work on the iPad</strong> anyway, and it didn&#39;t seem like a great option for me right now.</p>
<p>After searching for the blog platform again, I finally decided to settle down here.</p>
<h2 id="velog">Velog</h2>
<p>The reason for coming to Velog was simple.
<strong>It&#39;s a markdown and it&#39;s free.</strong>
Additionally, Google&#39;s exposure was good, and code block support seemed good.
It&#39;s a shame that there&#39;s no dedicated authoring app, but if I need it, I can write it using another markdown author, so I don&#39;t pay much attention.</p>
<p>I hope I get used to Velog gradually in the future and become a nice writing bag.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[블로그를 이사하며...]]></title>
            <link>https://velog.io/@scope_hyena/Moving-Blogs-kr</link>
            <guid>https://velog.io/@scope_hyena/Moving-Blogs-kr</guid>
            <pubDate>Fri, 24 Nov 2023 04:32:07 GMT</pubDate>
            <description><![CDATA[<p><a href="https://velog.io/@scope_hyena/Moving-Blogs">View this post in English</a></p>
<p>Velog에 올리는 첫 글이기도 하고, 그동안 거쳐 온 블로그 플랫폼들의 장단점이 너무 명확했기에... 테스트도 할 겸 왜 Velog에 오게 되었는가 이야기해보려고 한다.</p>
<h1 id="블로그-방황기">블로그 방황기</h1>
<h2 id="네이버-블로그">네이버 블로그</h2>
<p>본격적으로 블로그라는 걸 통해 글을 올리기 시작한 때는 약 6년 전쯤으로 그리 오래되지는 않았다.</p>
<p>당시 블로그하면 꽤 편리하고 많은 국내 이용자가 있던 네이버를 통해 블로그를 만들었으며, 그때가 <a href="https://blog.naver.com/blogpeople/221412463319">스마트에디터 ONE</a>이 나오기 직전이었을 것이다.</p>
<p>테마도 꽤 다양하게 지원했고, 작성도 편했다. 
모바일 앱도 지원해주었기에 이동 중에 작성해두기도 편했다.</p>
<p>다만, 치명적인 단점들이 있었는데,</p>
<ul>
<li>잊을 만 하면 날아오는 블로그 팔라는 스팸 쪽지들</li>
<li><strong>코드 블록</strong>에 대한 지원 부족</li>
</ul>
<p>다른 건 몰라도 코드 블록에 대한 지원이 부족한 건 너무 아쉬웠다.
내 기준으로는 코드를 넣는게 그리 간편하지도 않았을 뿐더러, 하이라이트 지원도 빈약했다.</p>
<p>그래서 3년만에 블로그를 갈아타기로 결심했다.
당시 엄청나게 뜨고 있던 도큐먼트 플랫폼인 노션으로.</p>
<h1 id="노션-정착기">노션 정착기</h1>
<h3 id="노션과-마크다운-그리고-oopy">노션과 마크다운, 그리고 oopy</h3>
<p><a href="https://notion.so">노션</a>에 글을 작성하면서부터 노션의 놀라운 능력을 깨닫게 될 때까지는 얼마 걸리지 않았다. 마크다운 문법과 빵빵한 단축키 지원, 꽤 잘 동작하는 코드블록 기능에 임베딩 기능까지. 노션은 내가 필요했던 기능들의 대부분을 제공해주고 있었다.
게다가 모바일용 노션 앱도 제공해주고 있어서 매우 편리했기에 꽤 긴 시간동안 노션에서 글을 써왔다.</p>
<p>한가지 아쉬웠던 점은 노션 페이지의 로딩 속도였는데, 이는 <a href="https://oopy.io">oopy</a>라는 노션 페이지 전용 호스팅 툴 덕분에 어렵지 않게 해결할 수 있었다.</p>
<p>그렇게 노션에서 글을 쓰다가 처음에는 없었던 문제가 지금은 생겨나기도 했고, 그동안은 느껴지지 않았던 문제가 한순간에 터지기도 했다.</p>
<ul>
<li>무료로 쓸 수 있는 플랫폼이 아니다. 물론 노션 자체는 무료로도 사용할 수 있지만 워크스페이스당 블록 개수 제한이 있어 블로그로써 운영하려면 10달러 가량의 월정액을 내고 요금제를 업그레이드해야 한다.</li>
<li>앞서도 말했듯이 노션으로만 웹페이지를 게시하면 불러오는 속도가 너무 느리다. oopy를 이용해 호스팅할 수 있으나, 호스팅 역시 월 5천원 가량의 요금제 가입이 필요하다.</li>
<li>oopy를 이용해 호스트할 경우 노션의 최신 기능을 이용하지 못한다는 단점이 생긴다.</li>
<li>노션 서버가 종종 터진다.</li>
<li>댓글 기능을 이용하기가 어렵다.</li>
<li><strong>아이패드에서 앱으로 작성할 시, 글자가 너무 작아</strong> 노션을 이용하기가 어렵다.</li>
</ul>
<p>특히 마지막 단점은 아이패드를 주로 사용하는 나에게 있어 꽤 치명적인 단점이었다. 또한 글을 쓰는 시기와 안 쓰는 시기의 차가 매우 큰 나로써는 매월 2만원가량의 돈을 꼬박꼬박 내는것이 부담스럽기도 했다.</p>
<p>그래서 다른 플랫폼을 찾기로 결심했다.</p>
<h1 id="잠깐의-방황과-정착">잠깐의 방황과 정착</h1>
<h2 id="github-pages">GitHub Pages</h2>
<p>처음에는 GitHub Pages를 이용할까 했고 실제로 세팅도 시도했다.
하지만, 오류, 오류, <strong>오류</strong>. 웹 개발 경험이 전무한 나한테 있어서는 웹 개발까지 익혀가며 블로그를 하기에는 배보다 배꼽이 더 큰 것 같았다.</p>
<p>잘만 하면 유용할 플랫폼인 것 같지만 어쨌든 아이패드에서 작성도 안 될 뿐더러 당장의 나한테는 별로 좋은 선택지는 아닌 것 같았다.</p>
<p>그렇게 또다시 블로그 플랫폼을 찾아 헤매던 나는 최종적으로 이곳에 정착하기로 했다.</p>
<h2 id="velog">Velog</h2>
<p>Velog에 오게 된 계기는 단순했다.
<strong>마크다운 방식이며 무료이다.</strong>
추가적으로 구글 노출도 잘 되는데다, 코드블록 지원도 괜찮아 보였다.
전용 작성 앱이 없는 건 아쉽지만, 필요하다면 다른 마크다운 작성기를 이용해서 글을 작성해두면 되니 크게 신경쓰지는 않는다.</p>
<p>앞으로 Velog에 차차 익숙해져 어엿한 글주머니가 되었으면 좋겠다.</p>
]]></description>
        </item>
    </channel>
</rss>