<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>haneun.log</title>
        <link>https://velog.io/</link>
        <description>천천히 꾸준히 취미처럼 냐미😋</description>
        <lastBuildDate>Fri, 05 Sep 2025 01:53:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>haneun.log</title>
            <url>https://velog.velcdn.com/images/syl_vana/profile/480b7af5-a036-4e23-9ffd-f51b75b35a62/image.jpeg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. haneun.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/syl_vana" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[YOLO 멀티 GPU 학습 시 좀비 메모리 삭제 방법 - CUDA Out of Memory 에러 ]]></title>
            <link>https://velog.io/@syl_vana/YOLO-%EB%A9%80%ED%8B%B0-GPU-%ED%95%99%EC%8A%B5-%EC%8B%9C-CUDA-Out-of-Memory-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0-%EA%B0%80%EC%9D%B4%EB%93%9C</link>
            <guid>https://velog.io/@syl_vana/YOLO-%EB%A9%80%ED%8B%B0-GPU-%ED%95%99%EC%8A%B5-%EC%8B%9C-CUDA-Out-of-Memory-%EC%97%90%EB%9F%AC-%ED%95%B4%EA%B2%B0-%EA%B0%80%EC%9D%B4%EB%93%9C</guid>
            <pubDate>Fri, 05 Sep 2025 01:53:00 GMT</pubDate>
            <description><![CDATA[<h2 id="문제-상황">문제 상황</h2>
<p>YOLO 모델을 멀티 GPU(4,5,6,7)로 학습시키려 했으나, 동일한 설정으로 GPU(0,1,2,3)에서는 정상 작동하는데 CUDA Out of Memory 에러가 발생했다.</p>
<pre><code class="language-python">torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 50.00 MiB. 
GPU 0 has a total capacty of 10.75 GiB of which 50.69 MiB is free.</code></pre>
<h2 id="원인-분석">원인 분석</h2>
<h3 id="1-gpu-메모리-상태-확인">1. GPU 메모리 상태 확인</h3>
<pre><code class="language-bash">nvidia-smi</code></pre>
<p>실행 결과, GPU 4,5,6,7에 프로세스는 보이지 않지만 메모리가 점유되어 있는 상태였다:</p>
<ul>
<li>GPU 4: 9170MB 사용 중</li>
<li>GPU 5: 8496MB 사용 중 (Utilization 100%)</li>
<li>GPU 6: 9064MB 사용 중 (Utilization 100%)</li>
<li>GPU 7: 10254MB 사용 중 (Utilization 100%)</li>
</ul>
<h3 id="2-좀비-프로세스-확인">2. 좀비 프로세스 확인</h3>
<pre><code class="language-bash">sudo lsof /dev/nvidia4 /dev/nvidia5 /dev/nvidia6 /dev/nvidia7</code></pre>
<p>Python 프로세스들이 GPU 디바이스 파일을 열어놓고 있어서 메모리가 해제되지 않는 상태였다. 
이는 이전 학습이 비정상 종료되어 발생한 좀비 프로세스 문제였다.</p>
<h2 id="해결-방법">해결 방법</h2>
<h3 id="step-1-문제-프로세스-찾기">Step 1: 문제 프로세스 찾기</h3>
<pre><code class="language-bash"># GPU 상태 상세 확인
nvidia-smi

# GPU를 사용하는 프로세스 확인
sudo lsof /dev/nvidia4 /dev/nvidia5 /dev/nvidia6 /dev/nvidia7

# Python 프로세스 확인
ps aux | grep python</code></pre>
<h3 id="step-2-좀비-프로세스-종료">Step 2: 좀비 프로세스 종료</h3>
<pre><code class="language-bash"># 특정 PID 종료 (lsof에서 확인한 PID)
kill -9 [PID1] [PID2] [PID3] [PID4]

# nvidia-smi 프로세스도 종료 (필요시)
sudo kill -9 [nvidia-smi_PID]

# DDP 관련 임시 파일 정리
rm -rf ~/.config/Ultralytics/DDP/_temp_*</code></pre>
<h3 id="step-3-gpu-리셋-시도">Step 3: GPU 리셋 시도</h3>
<pre><code class="language-bash"># 개별 GPU 리셋 (성공할 때까지 반복)
sudo nvidia-smi --gpu-reset -i 4
sudo nvidia-smi --gpu-reset -i 5
sudo nvidia-smi --gpu-reset -i 6
sudo nvidia-smi --gpu-reset -i 7</code></pre>
<p>만약 &quot;GPU is currently in use&quot; 에러가 계속 나타나면:</p>
<pre><code class="language-bash"># 숨겨진 프로세스 강제 종료
sudo fuser -k /dev/nvidia4
sudo fuser -k /dev/nvidia5
sudo fuser -k /dev/nvidia6
sudo fuser -k /dev/nvidia7</code></pre>
<h3 id="step-4-상태-확인">Step 4: 상태 확인</h3>
<pre><code class="language-bash">nvidia-smi</code></pre>
<p>모든 GPU가 6MB 정도만 사용하는 깨끗한 상태가 되면 성공!</p>
<h2 id="두-개-모델-동시-학습-설정">두 개 모델 동시 학습 설정</h2>
<h3 id="model-1-gpu-0123">Model 1 (GPU 0,1,2,3)</h3>
<pre><code class="language-python"># train_model1.py
import os
os.environ[&quot;CUDA_VISIBLE_DEVICES&quot;] = &quot;0,1,2,3&quot;

from ultralytics import YOLO
model = YOLO(&#39;yolov8s.pt&#39;)

results = model.train(
    data=&#39;dataset1/data.yaml&#39;,
    imgsz=1280,
    batch=16,
    epochs=1000,
    optimizer=&#39;AdamW&#39;,
    workers=2,
    cache=&#39;disk&#39;,
    mosaic=0,
    lr0=0.01,
    patience=300,
    plots=True,
    rect=False,
    project=&#39;runs/model1&#39;,  # 별도 폴더
    name=&#39;train&#39;
)</code></pre>
<h3 id="model-2-gpu-4567">Model 2 (GPU 4,5,6,7)</h3>
<pre><code class="language-python"># train_model2.py
import os
os.environ[&quot;CUDA_VISIBLE_DEVICES&quot;] = &quot;4,5,6,7&quot;

from ultralytics import YOLO
model = YOLO(&#39;yolov8s.pt&#39;)

results = model.train(
    data=&#39;dataset2/data.yaml&#39;,
    imgsz=1280,
    batch=16,
    epochs=1000,
    optimizer=&#39;AdamW&#39;,
    workers=2,
    cache=&#39;disk&#39;,
    mosaic=0,
    lr0=0.01,
    patience=300,
    plots=True,
    rect=False,
    project=&#39;runs/model2&#39;,  # 별도 폴더
    name=&#39;train&#39;
)</code></pre>
<h3 id="동시-실행">동시 실행</h3>
<pre><code class="language-bash"># Terminal 1
python train_model1.py

# Terminal 2 (또는 tmux/screen 사용)
python train_model2.py</code></pre>
<h2 id="예방-조치">예방 조치</h2>
<ul>
<li>정기적인 캐시 정리</li>
</ul>
<pre><code class="language-bash"># cron으로 매일 실행
0 3 * * * rm -rf ~/.config/Ultralytics/DDP/_temp_* &amp;&amp; rm -rf ~/.cache/torch/</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[임시]]></title>
            <link>https://velog.io/@syl_vana/%EC%9E%84%EC%8B%9C</link>
            <guid>https://velog.io/@syl_vana/%EC%9E%84%EC%8B%9C</guid>
            <pubDate>Mon, 26 May 2025 02:15:29 GMT</pubDate>
            <description><![CDATA[<p>: $x_0 \rightarrow x_1 \rightarrow \cdots \rightarrow x_T$
: 매 단계 $x_{t}$는 $x_{t-1}$만 보고 노이즈를 추가해 생성됨</p>
<p>: $x_T \rightarrow x_{T-1} \rightarrow \cdots \rightarrow x_0$
: 매 단계 $x_{t-1}$는 $x_{t}$만 보고 노이즈를 제거하며 복원</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[SDEdit]]></title>
            <link>https://velog.io/@syl_vana/SDEdit</link>
            <guid>https://velog.io/@syl_vana/SDEdit</guid>
            <pubDate>Sat, 12 Apr 2025 12:07:54 GMT</pubDate>
            <description><![CDATA[<pre><code>gpt4.5로 작성됨.</code></pre><h2 id="1-들어가며">1. 들어가며</h2>
<p>기존의 GAN(Generative Adversarial Network) 기반 방법들은 사용자의 입력과 사실적 이미지 간 균형을 유지하는 데 어려움이 있었고, 매번 새로운 데이터 수집 및 학습 과정이 필요했다. 이에 대한 대안으로 등장한 것이 바로 <strong>SDEdit(Stochastic Differential Editing)</strong>이다.</p>
<h2 id="2-sdedit란">2. SDEdit란?</h2>
<p>SDEdit는 확률적 미분 방정식(SDE, Stochastic Differential Equation)을 활용한 이미지 생성 및 편집 방법으로, 확산(diffusion) 모델의 원리를 기반으로 한다. 사용자의 입력(예: 간단한 색상 스트로크, 이미지 패치 등)을 받아 이 입력에 가우시안 잡음을 추가한 후, 이를 다시 제거하는 과정을 반복하여 현실적이고 사용자의 의도에 충실한 이미지를 생성한다.</p>
<h3 id="기존-방식과의-차별점">기존 방식과의 차별점:</h3>
<ul>
<li><strong>조건부 GAN(Conditional GAN)</strong>: 매번 새로운 작업에 맞춰 데이터 수집과 재학습이 필요했다.</li>
<li><strong>GAN Inversion</strong>: 복잡한 역추적 과정과 작업별 손실 함수 설계가 필요하며, 때로는 입력을 충실히 표현하지 못하는 문제점이 있었다.</li>
</ul>
<p>SDEdit는 이 두 방식의 문제를 해결하며, 별도의 재학습 없이 범용적인 이미지 편집 및 생성을 지원한다.</p>
<h2 id="3-sdedit의-핵심-개념">3. SDEdit의 핵심 개념</h2>
<p><img src="https://velog.velcdn.com/images/syl_vana/post/a7e1ac03-c6ab-47d1-86e0-f1e8f1a84f8c/image.png" alt=""></p>
<h3 id="sdestochastic-differential-equation의-이해">SDE(Stochastic Differential Equation)의 이해</h3>
<ul>
<li>SDE는 일반적인 미분 방정식에 무작위 노이즈를 더한 것이다. 이미지 생성에서 이 SDE는 원본 이미지를 잡음으로 서서히 변환한 뒤, 이 과정을 역으로 수행하여 잡음에서 사실적인 이미지를 얻는 방식이다.</li>
</ul>
<h3 id="ve-sdevariance-exploding-sde와-vp-sdevariance-preserving-sde">VE-SDE(Variance Exploding SDE)와 VP-SDE(Variance Preserving SDE)</h3>
<ul>
<li>VE-SDE: 시간이 지남에 따라 노이즈가 점점 커져 마지막에는 이미지가 순수한 잡음에 가까워진다.</li>
<li>VP-SDE: 데이터의 분산을 유지하면서 점점 잡음으로 변환한다.</li>
</ul>
<p>SDEdit는 특히 <strong>VE-SDE</strong>를 기반으로 설명된다.</p>
<h2 id="4-sdedit의-알고리즘">4. SDEdit의 알고리즘</h2>
<p><img src="https://velog.velcdn.com/images/syl_vana/post/0d7a129d-82f8-431f-a3ad-76306ce3d1d4/image.png" alt=""></p>
<p>사용자로부터 가이드 이미지(간단한 스트로크 등)를 입력받아 다음의 과정을 수행한다:</p>
<ol>
<li>가이드 이미지에 적절한 수준의 가우시안 노이즈를 추가한다.</li>
<li>이 노이즈가 추가된 이미지로부터 역방향 SDE를 반복적으로 적용하여 노이즈를 점점 제거한다.</li>
<li>최종적으로 사실적이며 입력에 충실한 이미지를 얻는다.</li>
</ol>
<p>이 과정에서 중요한 하이퍼파라미터는 <strong>t₀</strong>(노이즈 추가 정도)이며, 이 값이 클수록 사실성은 증가하지만 입력 충실도는 감소하는 trade-off가 있다.</p>
<h2 id="5-수식으로-이해하는-sdedit">5. 수식으로 이해하는 SDEdit</h2>
<p>SDEdit의 핵심은 확률적 미분 방정식(SDE)을 활용해 이미지를 점점 사실적인 방향으로 복원하는 것이다. 이 과정은 수식적으로 다음과 같이 표현된다:</p>
<h3 id="1-forward-sde-노이즈-추가-과정">1) Forward SDE (노이즈 추가 과정)</h3>
<p>SDE 모델은 원본 이미지 $\mathbf{x}(0)$에 시간에 따라 점점 강해지는 가우시안 노이즈를 더해 $\mathbf{x}(t)$를 만든다:</p>
<p>$\mathbf{x}(t) = \alpha(t)\mathbf{x}(0) + \sigma(t)\mathbf{z}, \quad \mathbf{z} \sim \mathcal{N}(0, \mathbf{I})$</p>
<ul>
<li>$\alpha(t)$: 원본 이미지 정보의 가중치</li>
<li>$\sigma(t)$: 노이즈의 세기를 조절하는 함수</li>
</ul>
<h3 id="2-reverse-sde-노이즈-제거-과정">2) Reverse SDE (노이즈 제거 과정)</h3>
<p>이제 $\mathbf{x}(t)$에서 다시 원본 이미지로 되돌아가는 과정은 다음과 같은 역방향 확률 미분 방정식으로 표현된다:</p>
<p>$d\mathbf{x}(t) = \left[ -\frac{d[\sigma^2(t)]}{dt} \nabla_{\mathbf{x}} \log p_t(\mathbf{x}) \right] dt + \sqrt{\frac{d[\sigma^2(t)]}{dt}} d\bar{w}$</p>
<table>
<thead>
<tr>
<th align="left">기호</th>
<th align="left">의미</th>
<th>해석</th>
</tr>
</thead>
<tbody><tr>
<td align="left">$\mathbf{x}(t)$</td>
<td align="left">시간 $t$일 때의 노이즈 이미지</td>
<td>시작은 노이즈 이미지</td>
</tr>
<tr>
<td align="left">$\frac{d\sigma^2(t)}{dt}$</td>
<td align="left">노이즈의 변화율</td>
<td>시간이 줄어들수록 노이즈를 얼마나 제거할지 결정</td>
</tr>
<tr>
<td align="left">$\nabla_{\mathbf{x}} \log p_t(\mathbf{x})$</td>
<td align="left">노이즈가 낀 데이터의 분포에 대한 score function</td>
<td>현재 이미지가 진짜일 확률의 기울기 방향</td>
</tr>
<tr>
<td align="left">$d\bar{w}$</td>
<td align="left">역방향 시간의 Wiener process (확률적 변화량)</td>
<td>Brownian motion (무작위성 보존)</td>
</tr>
<tr>
<td align="left">- $\frac{d\sigma^2(t)}{dt}$:</td>
<td align="left"></td>
<td></td>
</tr>
<tr>
<td align="left">- $\sigma^2(t)$:시간 t에 따른 노이즈의 분산 (시간이 지나면서 이미지에 점점 더 많은 노이즈를 섞어야 하기 때문)</td>
<td align="left"></td>
<td></td>
</tr>
<tr>
<td align="left">- 시간 t가 0에서 1로 갈수록 노이즈의 강도가 점점 커져야 함.</td>
<td align="left"></td>
<td></td>
</tr>
</tbody></table>
<p>  $d\mathbf{x}(t) = -\underbrace{\frac{d\sigma^2(t)}{dt}}<em>{\text{줄어드는 속도}} \cdot \underbrace{\nabla_x \log p_t(x)}</em>{\text{복원 방향}} \cdot dt + \underbrace{ \sqrt{ \frac{d\sigma^2(t)}{dt} } }_{\text{무작위성 크기}} \cdot d\bar{w}$</p>
<blockquote>
<h3 id="확률-미분이란">확률 미분이란?</h3>
<p>딥러닝에선 현실세계의 랜덤성을 부여하기 위해 확률 미분을 사용한다. 
$
dx(t) = a(t, x)dt + b(t, x)dW_t
$</p>
</blockquote>
<ul>
<li>$dx(t)$: 시간 $t$에서의 아주 작은 변화량. 여기에 무작위성이 포함.</li>
<li>$a(t, x)dt$: 일반적인 변화 (기울기)</li>
<li>$b(t, x)dW_t$: <strong>랜덤성 추가</strong></li>
</ul>
<h3 id="3-학습-score-matching-loss">3) 학습: Score Matching Loss</h3>
<p>모델은 score function을 다음과 같은 손실 함수를 통해 학습한다:</p>
<p>$
L_t = \mathbb{E}<em>{\mathbf{x}(0) \sim p</em>{\text{data}}, \ \mathbf{z} \sim \mathcal{N}(0, \mathbf{I})} \left[ \left| \sigma_t s_\theta(\mathbf{x}(t), t) - \mathbf{z} \right|^2 \right]$</p>
<table>
<thead>
<tr>
<th align="left">기호</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td align="left">$\mathbf{x}(0)$</td>
<td>실제 데이터 이미지 (예: 학습 이미지)</td>
</tr>
<tr>
<td align="left">$\mathbf{z} \sim \mathcal{N}(0, \mathbf{I})$</td>
<td>표준 정규분포에서 샘플링한 노이즈</td>
</tr>
<tr>
<td align="left">$\mathbf{x}(t) = \alpha(t)\mathbf{x}(0) + \sigma(t)\mathbf{z}$</td>
<td>Forward SDE로 노이즈를 섞은 중간 상태 이미지</td>
</tr>
<tr>
<td align="left">$s_\theta(\mathbf{x}(t), t)$</td>
<td>네트워크가 학습하려는 score function, 즉 노이즈의 방향을 추정함</td>
</tr>
<tr>
<td align="left">$\sigma_t$</td>
<td>시간 $t$에서의 노이즈 세기</td>
</tr>
<tr>
<td align="left">$\mathbb{E}[\cdot]$</td>
<td>전체 데이터와 노이즈에 대해 평균을 구함</td>
</tr>
<tr>
<td align="left">지금 이 이미지에는 어떤 노이즈가 섞여 있는지 맞춰봐! 이 이미지가 진짜 이미지라면, 어떤 방향으로 noise를 제거하면 될까?를 학습 한다.</td>
<td></td>
</tr>
</tbody></table>
<h3 id="4-샘플링-euler-maruyama로-구현">4) 샘플링: Euler-Maruyama로 구현</h3>
<p>실제 샘플링은 역방향 SDE를 유한 시간 간격으로 근사하여 아래와 같이 구현된다:</p>
<p>$
\mathbf{x}(t) = \mathbf{x}(t + \Delta t) + (\sigma^2(t) - \sigma^2(t + \Delta t)) s_\theta(\mathbf{x}(t), t) + \sqrt{\sigma^2(t) - \sigma^2(t + \Delta t)} \mathbf{z}
$</p>
<p>이를 반복함으로써 점차 현실적인 이미지를 얻는다.</p>
<h2 id="6-실험-및-결과">6. 실험 및 결과</h2>
<p>SDEdit는 다음과 같은 다양한 이미지 작업에서 탁월한 성능을 보였다:</p>
<ul>
<li><strong>스트로크 기반 이미지 생성</strong></li>
<li><strong>이미지 합성(Image compositing)</strong></li>
<li><strong>스트로크 기반 이미지 편집</strong></li>
</ul>
<p>특히, 인간의 평가에서 기존 GAN 기반 방법들보다 현실성에서는 최대 98.09%, 전체 만족도(현실성+입력 충실도)에서는 최대 91.72% 더 높은 평가를 받았다.</p>
<h2 id="7-sdedit의-장점-요약">7. SDEdit의 장점 요약</h2>
<ul>
<li><strong>범용성</strong>: 별도의 데이터 수집이나 모델 재학습 없이 다양한 이미지 작업을 지원한다.</li>
<li><strong>사실성과 충실도의 균형</strong>: 사용자의 의도를 정확히 반영하면서도 매우 현실적인 이미지를 생성할 수 있다.</li>
<li><strong>간단한 적용</strong>: 기존에 미리 학습된 SDE 기반 모델을 바로 활용할 수 있다.</li>
</ul>
<h2 id="8-의의">8. 의의</h2>
<ul>
<li><strong>확산 모델의 표현력 활용</strong>: SDEdit는 확산 모델이 가진 고해상도 이미지 생성 능력을 편집 작업에도 효과적으로 활용.</li>
<li><strong>GAN의 한계 극복</strong>: 기존 GAN 기반 방식이 가진 재학습, 최적화 불안정성, latent space 제약 등의 한계를 해결..</li>
<li><strong>재학습 없이 다양한 편집 가능</strong>: 한 번 학습된 확산 모델만으로 스트로크 기반 생성, 편집, 이미지 합성까지 수행할 수 있어 실용성이 높다.</li>
<li><strong>조건부 생성의 새로운 방향 제시</strong>: 확산 모델에서 사용자 가이드를 중간 노이즈 수준(t₀)에서 삽입하는 방식은 이후 다양한 diffusion 기반 편집 모델에 영향을 주었다.</li>
</ul>
<h3 id="참고">참고</h3>
<ul>
<li>Chenlin Meng et al., &quot;SDEdit: Guided Image Synthesis and Editing with Stochastic Differential Equations&quot;, Stanford University, 2022.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[LDM (Latent Diffusion Model)]]></title>
            <link>https://velog.io/@syl_vana/LDM-Latent-Diffusion-Model</link>
            <guid>https://velog.io/@syl_vana/LDM-Latent-Diffusion-Model</guid>
            <pubDate>Wed, 02 Apr 2025 11:03:56 GMT</pubDate>
            <description><![CDATA[<p>그래도 ldm은 ddpm보단 읽기 쉬운 듯. 차근차근 해보자~</p>
<h1 id="등장-배경">등장 배경</h1>
<p>기존의 Diffusion Model은 픽셀 단위에서 동작해서 고해상도 이미지 생성 시 연산량과 시간이 매우 많이 필요했다.</p>
<h1 id="개요">개요</h1>
<p><strong>LDM = Autoencoder + Diffusion Model</strong></p>
<h1 id="의의">의의</h1>
<ol>
<li><strong>계산 효율성 극대화</strong>
기존 픽셀 단위 Diffusion Model은 매우 높은 계산 비용을 요구했는데, LDM은 오토인코더를 통해 잠재공간에서 계산을 수행하므로 연산 비용과 메모리 사용량을 크게 줄일 수 있다.
개인 소규모 연구실에서도 고성능 이미지 생성 모델을 사용할 수 있게 되어, Diffusion Model의 활용 가능성을 확대했다.</li>
<li><strong>고해상도 이미지 생성 가능</strong>
기존 모델의 픽셀 기반 접근은 고해상도 이미지에서 매우 많은 연산량이 필요했지만, LDM은 잠재공간을 통해 높은 해상도의 이미지(예: 512×512, 1024×1024 픽셀 등)를 상대적으로 적은 연산량으로 생성 가능하게 만들었다.</li>
</ol>
<h1 id="구조">구조</h1>
<p>LDM은 AutoEncoder 구조를 활용해 다음과 같이 동작한다.</p>
<ol>
<li><p><strong>이미지 → [인코더] → 잠재표현</strong>  </p>
<ul>
<li>이미지 데이터를 인코더를 통해 낮은 차원의 잠재공간(latent space)으로 압축한다.</li>
</ul>
</li>
<li><p><strong>잠재표현 → [Latent Diffusion 모델] → 새로운 잠재표현 생성</strong>  </p>
<ul>
<li>Diffusion 모델은 이 압축된 잠재공간에서 데이터의 확률적 구조를 학습하고, 새로운 잠재표현을 생성한다.</li>
</ul>
</li>
<li><p><strong>생성된 잠재표현 → [디코더] → 최종 이미지 생성</strong>  </p>
<ul>
<li>디코더는 생성된 잠재표현을 고해상도 이미지로 복원한다.</li>
</ul>
</li>
</ol>
<p>즉, LDM은 AutoEncoder 구조를 통해 복잡한 고차원 이미지 공간을 더 간결하고 연산 효율적인 잠재 공간으로 변환한 뒤, 이 공간에서 이미지 생성을 수행한다.</p>
<h1 id="loss-function">loss function</h1>
<p>DM에서는 오토인코더를 훈련할 때, 두 가지 손실을 같이 사용한다:</p>
<ul>
<li><strong>Perceptual Loss</strong>: 사람이 보기 좋다고 느끼는 기준에 가까운지 평가.</li>
<li><strong>Patch-based GAN Loss</strong>: 생성된 이미지가 작은 조각 하나하나에서도 진짜처럼 보이도록 유도.</li>
</ul>
<h1 id="논문-리딩">논문 리딩</h1>
]]></description>
        </item>
        <item>
            <title><![CDATA[Autoencoder, VAE]]></title>
            <link>https://velog.io/@syl_vana/Autoencoder-VAE</link>
            <guid>https://velog.io/@syl_vana/Autoencoder-VAE</guid>
            <pubDate>Wed, 26 Mar 2025 11:37:56 GMT</pubDate>
            <description><![CDATA[<h2 id="개요">개요</h2>
<ul>
<li>압축(Encoding) → 복원(Decoding) 과정</li>
<li>이미지를 압축해서 중요한 특징만 남김. </li>
<li>중복된 정보나 노이즈는 자연스럽게 제거되어 연산이 가벼움.</li>
<li>비지도 학습<ul>
<li>출력과 입력을 비교하기 때문에 정답 라벨 필요x.</li>
</ul>
</li>
</ul>
<h2 id="구조">구조</h2>
<pre><code class="language-plaintext">입력 X ─▶ [Encoder] ─▶ 잠재 표현 Z ─▶ [Decoder] ─▶ 복원된 X&#39;</code></pre>
<p><strong>Latent Space (잠재 공간)</strong>: 핵심 특징만 담은 벡터 (차원이 작음)</p>
<pre><code class="language-plaintext">입력 X
  ↓
[Encoder]
  - Linear(784 → 256)
  - ReLU
  - Linear(256 → 64)
  ↓
Latent Vector (64차원)
  ↓
[Decoder]
  - Linear(64 → 256)
  - ReLU
  - Linear(256 → 784)
  - Sigmoid
  ↓
출력 X&#39; (복원된 입력)</code></pre>
<h2 id="loss-function">Loss function</h2>
<p>복원된 X&#39;와 원래 입력 X의 차이를 줄이는 게 목표.
대표적인 손실 함수: MSE (Mean Squared Error)</p>
<h2 id="python-예제">python 예제</h2>
<pre><code class="language-python">&quot;&quot;&quot;
[입력 이미지: 28x28]
→ 인코더: Conv + ReLU + MaxPool
→ 잠재 표현: 압축된 Feature Map
→ 디코더: ConvTranspose + ReLU
→ 출력 이미지: 28x28 복원
&quot;&quot;&quot;
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.datasets as datasets
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

# 장치 설정 (GPU 사용 가능 시 GPU 사용)
device = torch.device(&quot;cuda&quot; if torch.cuda.is_available() else &quot;cpu&quot;)

# 하이퍼파라미터
epochs = 5
batch_size = 128
learning_rate = 1e-3

# MNIST 데이터셋 불러오기 (흑백 이미지 28x28)
transform = transforms.ToTensor()
train_dataset = datasets.MNIST(root=&#39;./data&#39;, train=True, transform=transform, download=True)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

# CNN Autoencoder 클래스 정의
class CNNAutoencoder(nn.Module):
    def __init__(self):
        super(CNNAutoencoder, self).__init__()

        # 인코더: Conv → ReLU → MaxPool
        self.encoder = nn.Sequential(
            nn.Conv2d(1, 16, kernel_size=3, padding=1),   # 28x28 → 16x28x28
            nn.ReLU(),
            nn.MaxPool2d(2, 2),                           # → 16x14x14

            nn.Conv2d(16, 8, kernel_size=3, padding=1),   # → 8x14x14
            nn.ReLU(),
            nn.MaxPool2d(2, 2)                            # → 8x7x7
        )

        # 디코더: ConvTranspose → ReLU → 복원
        self.decoder = nn.Sequential(
            nn.ConvTranspose2d(8, 16, kernel_size=2, stride=2),  # → 16x14x14
            nn.ReLU(),

            nn.ConvTranspose2d(16, 1, kernel_size=2, stride=2),  # → 1x28x28
            nn.Sigmoid()  # 픽셀 값 0~1로 맞춤
        )

    def forward(self, x):
        x = self.encoder(x)
        x = self.decoder(x)
        return x

# 모델, 손실 함수, 옵티마이저 정의
model = CNNAutoencoder().to(device)
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=learning_rate)

# 학습 루프
for epoch in range(epochs):
    for data, _ in train_loader:
        data = data.to(device)

        # 순전파
        output = model(data)
        loss = criterion(output, data)

        # 역전파
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

    print(f&#39;Epoch [{epoch+1}/{epochs}], Loss: {loss.item():.4f}&#39;)

# 테스트용 이미지 시각화
with torch.no_grad():
    sample = next(iter(train_loader))[0][:6].to(device)  # 6장 샘플 이미지
    reconstructed = model(sample).cpu()

# 결과 시각화
plt.figure(figsize=(9,2))
for i in range(6):
    # 원본
    plt.subplot(2,6,i+1)
    plt.imshow(sample[i].cpu().squeeze(), cmap=&#39;gray&#39;)
    plt.title(&quot;Original&quot;)
    plt.axis(&#39;off&#39;)

    # 복원
    plt.subplot(2,6,i+7)
    plt.imshow(reconstructed[i].squeeze(), cmap=&#39;gray&#39;)
    plt.title(&quot;Reconstructed&quot;)
    plt.axis(&#39;off&#39;)

plt.tight_layout()
plt.show()
</code></pre>
<h1 id="vaevariational-autoencoder">VAE(Variational Autoencoder)</h1>
<ul>
<li>확률 기반의 생성 모델</li>
<li>Autoencoder 구조를 따르면서도 <strong>잠재공간(latent space)을 확률 분포로 모델링</strong></li>
</ul>
<h2 id="loss-function-1">Loss function</h2>
<p>$
\text{Loss} = \text{Reconstruction Loss} + \text{KL Divergence}
$</p>
<h3 id="1-reconstruction-loss-복원-손실">1. Reconstruction Loss (복원 손실)</h3>
<ul>
<li>일반 Autoencoder와 동일: 입력 x와 출력 x&#39;의 차이를 줄임</li>
<li>예: MSE, BCE 등</li>
</ul>
<h3 id="2-kl-divergence-kullback-leibler-divergence">2. KL Divergence (Kullback-Leibler Divergence)</h3>
<ul>
<li>z의 분포 q(z|x)가 <strong>표준 정규분포 N(0,1)</strong>에 가까워지도록 강제함</li>
<li>즉, <strong>잠재 공간을 더 부드럽고 연속적인 구조로 만들기 위한 정규화</strong></li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Denoising Diffusion Probabilistic Models(DDPM) - 비전공자(바로 나!)를 위한 정리]]></title>
            <link>https://velog.io/@syl_vana/Diffusion-Probabilistic-Models-DPM-%EC%A7%84%EC%A7%9C-%EC%A7%84%EC%A7%9C-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EB%B0%94%EB%A1%9C-%EB%82%98%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@syl_vana/Diffusion-Probabilistic-Models-DPM-%EC%A7%84%EC%A7%9C-%EC%A7%84%EC%A7%9C-%EB%B9%84%EC%A0%84%EA%B3%B5%EC%9E%90%EB%B0%94%EB%A1%9C-%EB%82%98%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sat, 08 Feb 2025 06:29:59 GMT</pubDate>
            <description><![CDATA[<h2 id="diffusion-model">Diffusion Model</h2>
<p>ddpm 논문을 읽기 전 필요한 배경 지식으로 diffusion model이 있다. </p>
<p>Ddpm의 의의는</p>
<ul>
<li>Diffusion Model을 딥러닝 기반 이미지 생성 모델로 발전시킴.</li>
<li>Neural Network를 활용한 Reverse Process 학습을 제안하여 실용화.
에 있다
ddpm 논문에서는 diffusion model을 <strong>diffusion Probabilistic Model</strong>이라고 지칭한다. &#39;확률적&#39; 관점을 더 강조한 느낌.</li>
</ul>
<p>이미지에 점진적으로 노이즈를 추가하고, 노이즈를 다시 없애면서 원본 이미지를 복원
목표:</p>
<ul>
<li>고품질 이미지 생성</li>
<li>이미지 복원 등</li>
</ul>
<h2 id="모델-개요">모델 개요</h2>
<p>  <img src="https://velog.velcdn.com/images/syl_vana/post/4cd7dae2-37c6-42b4-a610-1b618e280567/image.png" alt=""></p>
<h3 id="forward-process"><strong>Forward Process</strong></h3>
<ol>
<li>$q(x_{1:T} | x_0) := \prod_{t=1}^{T} q(x_t | x_{t-1})$<ul>
<li>노이즈를 점진적으로 추가하는 과정</li>
<li>전체 마르코프 과정의 결합 확률 분포</li>
<li>각 시간 단계에서 노이즈를 추가하는 과정은 바로 직전 단계에만 의존한다.</li>
<li>각 시간 단계에서 상태가 $x_t$ →$x_{t+1}$로 변할 확률을 모두 고려한 결합 확률 분포</li>
</ul>
</li>
</ol>
<blockquote>
<h3 id="마르코프-과정">마르코프 과정:</h3>
<p>현재 상태가 오로지 직전 상태에만 의존하는 확률 과정.
즉, 현재 $x_t$는 바로 이전 상태 $x_{t-1}$만 참조하고 그 이전의 데이터와는 직접적인 관련이 없음.</p>
<h3 id="그런데-왜-모든-시점의-확률을-곱할까">그런데 왜 모든 시점의 확률을 곱할까?</h3>
<p>수식은 <strong>마르코프 과정 전체에 대한 결합 확률 분포</strong>를 표현하기 때문임.각 단계의 확률을 개별적으로 정의x, <strong>과정 전체에서 발생할 확률을 계산</strong>하려는 거다.
   <strong>요소 설명</strong>
    - $q(x_{1:T} | x_0)$: 정방향 과정에서 원본 데이터 $x_0$가 주어졌을 때, $x_1$, $x_2$, ..., $x_T$까지 점진적으로 노이즈를 추가하는 전체 과정의 확률
    - $\prod_{t=1}^{T} q(x_t | x_{t-1})$: 각 단계에서 $x_{t-1}$에서$x_t$로 변할 확률을 전부 곱한 것</p>
</blockquote>
<ol start="2">
<li>$q(x_t | x_{t-1}) = \mathcal{N}(x_t; \sqrt{1 - \beta_t} x_{t-1}, \beta_t I)$</li>
</ol>
<ul>
<li>t-1 단계에서 t 단계로 넘어갈 때 노이즈를 점진적으로 추가. <strong>각 수식은 픽셀 하나의 값에 대해 적용된다.</strong> 즉, $x_t$와 $x_{t-1}$의 각 픽셀 값은 서로 같은 위치에서 매칭되고, 같은 위치의 $x_{t-1}$ 픽셀 값에 직접적인 영향(Markov Chain의 원리)을 받아 $x_t$의 값이 결정된다.</li>
</ul>
<h4 id="수식-설명">수식 설명</h4>
<ul>
<li><p>$q(x_t | x_{t-1})$: 이전 단계 $x_{t-1}$가 주어졌을 때, $x_t$가 어떻게 분포하는지를 나타내는 조건부 확률 분포</p>
</li>
<li><p>$N(x_t ; \sqrt{1 - \beta_t} x_{t-1}, \beta_t I)$:
$x_t$는 평균이 $\sqrt{1 - \beta_t} x_{t-1}$, 분산이 $\beta_t I$인 가우시안 분포에서 <strong>샘플링</strong>된 데이터
  = $x_t$의 모든 픽셀 값이 각 $x_{t-1}$를 중심으로 하는 가우시안 분포에서 샘플링된 값</p>
<ul>
<li>$x_t$: $t$ 시점의 노이즈가 추가된 데이터</li>
<li>$x_{t-1}$: $t-1$ 시점의 노이즈가 추가된 데이터 </li>
<li>$\beta_t$: 각 시점에서 노이즈 크기를 조절하는 파라미터 (Variance Schedule)</li>
<li>$\mathcal{N}(\mu, \sigma^2)$: 평균이 $\mu$, 분산이 $\sigma^2$인 <strong>가우시안 분포(정규분포)</strong>  </li>
<li>$I$: 단위 행렬 (각 데이터 차원에서 독립적으로 노이즈가 추가됨을 의미)</li>
</ul>
</li>
</ul>
<h3 id="reverse-process"><strong>Reverse Process</strong></h3>
<p>노이즈를 제거하면서 이미지를 복원
<strong>학습 목표</strong>: 역방향 확률 분포 모델링 (모델이 학습하는 부분)</p>
<h4 id="수식">수식</h4>
<ol>
<li>$p_\theta(x_{0:T}) := p(x_T) \prod_{t=1}^T p_\theta(x_{t-1} | x_t)$</li>
</ol>
<ul>
<li><p>전체 마르코프 과정의 결합 확률 분포</p>
</li>
<li><p>$x_T$에서 시작해 $x_0$으로 도달할 때의 확률을 계산하는 과정</p>
</li>
<li><p>이 노이즈에서 시작했을 때, 특정한 과정을 거쳐 원래 데이터로 복원될 확률은 얼마일까?</p>
<p>  <strong>요소 설명</strong></p>
<ul>
<li>$p(x_T)$: 초기 상태의 노이즈 확률 분포. 초기는 완전 노이즈이기 때문에 아마 가우시안 분포를 따른다고 가정.</li>
<li>$\prod_{t=1}^T p_\theta(x_{t-1} | x_t)$: 각 단계에서 $x_T$에서 $x_{T-1}$로 변할 확률을 모두 곱한 것. 즉, 모든 시간 단계에서의 조건부 확률을 곱해서 전체 확률을 계산함.</li>
</ul>
</li>
</ul>
<ol start="2">
<li>$p_\theta(x_{t-1} | x_t) = \mathcal{N}(x_{t-1}; \mu_\theta(x_t, t), \Sigma_\theta(x_t, t))$</li>
</ol>
<ul>
<li>$p_\theta(x_{t-1} | x_t)$: 현재 단계 $x_t$가 주어졌을 때, 이전 단계 $x_{t-1}$가 어떻게 분포하는지를 나타내는 조건부 확률 분포  </li>
<li>$\mathcal{N}(x_{t-1}; \mu_\theta(x_t, t), \Sigma_\theta(x_t, t))$: <strong>정규 분포</strong>로, $x_{t-1}$는 평균이 $\mu_\theta(x_t, t)$, 분산이 $\Sigma_\theta(x_t, t)$인 <strong>가우시안 분포에서 샘플링된 값</strong>  </li>
</ul>
<h4 id="수식-내-요소-설명">수식 내 요소 설명</h4>
<ul>
<li>$\theta$: 학습 가능한 파라미터. $\theta$가 붙은 기호는 <strong>예측하는 값</strong> 또는 <strong>학습되는 값</strong>이라고 생각하면 됨.</li>
<li>$\mu_\theta(x_t, t)$:  <ul>
<li>$x$와 $t$를 입력으로 받아 평균값 $\mu$를 반환하는 함수.  <ul>
<li>$x_t$: 데이터(변화하는 입력) 즉, t 시점에서의 x값</li>
<li>$t$: 시간 또는 상태를 조절하는 파라미터</li>
</ul>
</li>
<li><strong>평균값</strong>으로, 모델이 예측하는 값. 고정된 수식이 아니라, 학습 가능한 값!! 특정 규칙으로 고정된 평균값이 아니라, 학습 과정에서 동적으로 결정되는 값!</li>
<li>$x_t$가 주어졌을 때 가장 가능성이 높은 $x_{t-1}$의 값</li>
</ul>
</li>
<li>$\Sigma_\theta(x_t, t)$: $x_t$ 시점에서 $x_{t-1}$로 이동할 때, $x_{t-1}$의 분산을 예측한 값<ul>
<li>$\Sigma_\theta$: 가우시안 분포의 <strong>공분산</strong> 행렬<blockquote>
<p><strong>분산 vs 공분산 행렬</strong></p>
</blockquote>
</li>
<li><em>분산*</em>: 하나의 변수의 변동성</li>
<li><em>공분산 행렬*</em>: 다차원 데이터의 각 변수 간의 상관관계와 변동성을 동시에 나타냄. <em>너가 adp 공부할 때 배웠던 그 공분산 맞아~</em></li>
</ul>
</li>
</ul>
<blockquote>
<p>💡 <strong>표기법의 일반적 관습</strong></p>
</blockquote>
<ol>
<li>$p()$  <ul>
<li><strong>학습해야 할 확률 분포</strong> 또는 <strong>실제 데이터 분포</strong>를 나타냄.  </li>
<li>ex: $p(x)$: 데이터 $x$의 진짜 분포를 의미.</li>
</ul>
</li>
<li>$q()$  <ul>
<li><strong>근사 분포</strong>를 나타낼 때 사용.  </li>
<li>ex: $q(x | y)$: $y$ 조건 하에서 $x$를 근사하는 분포를 나타낸다.  </li>
<li><strong>Forward process</strong>에서 $q(x_t | x_{t-1})$는 <strong>고정된 근사 분포</strong>이기 때문에 $q$를 사용.</li>
</ul>
</li>
</ol>
<p>각 단계가 연결된 사건이므로 전체 확률을 구하려면 모든 단계의 확률을 곱해줘야함.</p>
<p>예를 들어, 특정한 경로를 따른다고 가정하면:</p>
<ul>
<li><strong>노이즈 $x_T$에서 $x_{T-1}$로 변할 확률:</strong> $P(x_{T-1} | x_T)$</li>
<li><strong>$x_{T-1}$에서 $x_{T-2}$로 변할 확률:</strong> $P(x_{T-2} | x_{T-1})$
…</li>
<li><strong>마지막으로$x_1$에서 ( x_0 )로 변할 확률:</strong> $P(x_0 | x_1)$</li>
</ul>
<p>그러면, 전체 확률은??
$P(x_0, x_1, ..., x_T) = P(x_T) \times P(x_{T-1} | x_T) \times P(x_{T-2} | x_{T-1}) \times \cdots \times P(x_0 | x_1)$</p>
<h2 id="ddpm의-손실-함수">DDPM의 손실 함수</h2>
<p><em>수식 (3)</em></p>
<p>$E[−\log p_{\theta}(x_0)] \leq E_q [-\log q(x_{1:T} | x_0)] = E_q [-\log p(x_T) - \sum_{t\geq1} \log \frac{p_{\theta}(x_{t-1} | x_t)}{q(x_t | x_{t-1})}] =: L$</p>
<ol>
<li><strong>목표</strong>: $p_{\theta}(x_0)$을 최대화하는 것.
$p_{\theta}(x_0)$: 노이즈에서 원본을 되찾을 확률.
근데 보통 딥러닝에서 최대화하는 것보단 최소화로 변환해서 사용한다. 왜냐면 계산하기 더 쉽기 때문에. -&gt;  <strong>Negative Log-Likelihood (NLL, 음의 로그 가능도)</strong> 를 사용
직접 최적화하기 어려우므로 -&gt; <strong>변분 추정</strong>을 이용하여 우상향 경계를 설정한다.</li>
</ol>
<p>$max(p_{\theta}(x_0)) -&gt; min(logp_{\theta}(x_0))$</p>
<p>log를 왜 이용할까?</p>
<blockquote>
<p><strong>변분추정(variational bound)</strong>: 확률을 계산할 때, 그 수식이 너무 복잡하므로 그 값을 근사하게 계산함.</p>
</blockquote>
<h3 id="손실함수-유도-차근히-뜯어보기">손실함수 유도 차근히 뜯어보기</h3>
<p><strong>변분추론을 적용한 전개</strong>
우리는 $p_\theta(x_0)$를 직접 계산하기 어려우니까, <strong>대신 전체 마르코프 체인 확률을 사용해서 근사</strong>할 수 있어.</p>
<p>$
p_\theta(x_0) = \int p_\theta(x_{0:T}) dx_{1:T}
$</p>
<p>이걸 로그를 취하면:</p>
<p>$
\log p_\theta(x_0) = \log \int p_\theta(x_{0:T}) dx_{1:T}
$</p>
<p>이 식을 다루기 어렵기 때문에 <strong>Jensen&#39;s Inequality (옌센 부등식)</strong> 을 사용하면:</p>
<p>$
\log p_\theta(x_0) \geq \mathbb{E}<em>q \left[ \log \frac{p_\theta(x</em>{0:T})}{q(x_{1:T} | x_0)} \right]
$</p>
<blockquote>
<p><strong>옌센(젠센) 부등식(Jensen&#39;s Inequality)</strong>란?
&quot;평균을 먼저 구하고 함수 적용&quot; 한 값은, &quot;먼저 함수 적용한 후 평균&quot; 을 낸 값보다 항상 작거나 같다!를 이용한 등식
$
f(\mathbb{E}[X]) \leq \mathbb{E}[f(X)]
$</p>
</blockquote>
<p>이제 <strong>좌변에 음수를 곱해서 최소화 문제로 바꾸면</strong>:</p>
<p>$
-\log p_\theta(x_0) \leq \mathbb{E}<em>q \left[ - \log \frac{p_\theta(x</em>{0:T})}{q(x_{1:T} | x_0)} \right]
$</p>
<p>기대값을 추가하면:</p>
<p>$
\mathbb{E}[- \log p_\theta(x_0)] \leq \mathbb{E}<em>q \left[ - \log \frac{p_\theta(x</em>{0:T})}{q(x_{1:T} | x_0)} \right]
$
짠. 논문의 (3)번 식이 된다.</p>
<p>다시 지피티한테 물어봄. 식 유도에 대해. 비전공자로서 이해가 된 답변을 가져옴.</p>
<p>우리가 원래 하고 싶은 건 <strong>$p_\theta(x_0)$를 직접 최적화하는 것</strong>이야.<br>즉, 우리가 원하는 최적화 목표는:</p>
<p>$
\max p_\theta(x_0)$</p>
<p>하지만 딥러닝에서 직접 확률을 다루는 대신 <strong>Negative Log-Likelihood (NLL)</strong> 를 최소화하는 방식으로 바꿔서 최적화하는 게 일반적이야:</p>
<p>$
\min -\log p_\theta(x_0)
$</p>
<p>즉, <strong>확률을 최대화하는 대신, 음의 로그 가능도를 최소화하는 문제로 바꾼 것!</strong><br>이렇게 하면 수학적으로 계산하기 편하고, 경사 하강법(Gradient Descent)으로 최적화하기 쉬워. -&gt; 여기까진 이해 o</p>
<hr>
<p><strong>그런데 왜 기대값이 등장하는가?</strong>
<strong>✅ 문제: $p_\theta(x_0)$ 를 직접 계산하기 어렵다!</strong>
$p_\theta(x_0)$를 계산하려면 <strong>전체 마르코프 체인에서 $x_0$가 나올 확률을 다 합쳐야 해.</strong><br>즉, $x_T$부터 시작해서 $x_0$까지 오는 <strong>모든 가능성</strong>을 고려해야 해.</p>
<p>이걸 수식으로 쓰면:
$
p_\theta(x_0) = \int p_\theta(x_{0:T}) dx_{1:T}
$</p>
<p>즉, $x_{1:T}$에 대해 적분(총합)을 취해야 하는데, <strong>이 적분을 직접 계산하는 건 너무 복잡해!</strong><br>그래서 <strong>적분을 기대값 형태로 변형</strong>해서 다루기 쉽게 바꾸는 거야.</p>
<blockquote>
<p>기억나니? 그래프의 아래 면적을 적분으로 구하는 거? 적분이 기대값과 완전 같은 개념은 아니지만 기대값을 확률 분포에서 구하려면 적분이 필요함.</p>
</blockquote>
<hr>
<p><strong>적분을 기대값(평균) 형태로 변형하는 과정</strong>
우리는 적분을 아래처럼 변형할 수 있어:</p>
<p>$
p_\theta(x_0) = \int p_\theta(x_{0:T}) dx_{1:T}
$</p>
<p><strong>이제 $q(x_{1:T} | x_0)$ (forward process) 를 곱하고 나누면?</strong></p>
<p>$
p_\theta(x_0) = \int \frac{p_\theta(x_{0:T})}{q(x_{1:T} | x_0)} q(x_{1:T} | x_0) dx_{1:T}
$</p>
<p>이제 <strong>기대값(Expectation) 형태</strong>로 바꾸면:</p>
<p>$
p_\theta(x_0) = \mathbb{E}<em>{q(x</em>{1:T} | x_0)} \left[ \frac{p_\theta(x_{0:T})}{q(x_{1:T} | x_0)} \right]
$</p>
<p>즉,<br><strong>&quot;적분을 직접 계산하는 대신, $q(x_{1:T} | x_0)$ 에 대해 평균을 취한 값으로 표현할 수 있다!&quot;</strong><br>이게 바로 <strong>기대값이 등장한 이유야.</strong></p>
<hr>
<p><strong>그런데 왜 옌센 부등식이 등장하는가?</strong>
우리는 이제 최적화 문제를 다음과 같이 바꿀  있다.</p>
<p>$
\min -\log p_\theta(x_0)
$</p>
<p>이제 <strong>여기서 옌센 부등식을 적용하려고 하는데, 왜 그럴까?</strong><br>왜냐하면, 우리가 위에서 유도한 <strong>기대값(평균)</strong> 은 로그 바깥에 있기 때문이야:</p>
<p>$
\log p_\theta(x_0) = \log \mathbb{E}<em>{q(x</em>{1:T} | x_0)} \left[ \frac{p_\theta(x_{0:T})}{q(x_{1:T} | x_0)} \right]
$</p>
<h3 id="여기서-핵심적인-문제"><strong>여기서 핵심적인 문제:</strong></h3>
<p>✅ <strong>로그 안에 기대값이 들어 있으면 직접 최적화하기 어렵다!</strong><br>✅ <strong>기대값(평균)을 로그 바깥으로 빼야 한다!</strong></p>
<p>옌센 부등식은 바로 <strong>이 로그를 바깥으로 빼주는 역할</strong>을 해.<br>즉, <strong>옌센 부등식의 성질을 이용하면, 기대값을 로그 바깥으로 뺄 수 있다!</strong>  </p>
<p>$
\log \mathbb{E}[X] \geq \mathbb{E}[\log X]
$</p>
<p>이걸 적용하면:</p>
<p>$
\log p_\theta(x_0) = \log \mathbb{E}<em>{q(x</em>{1:T} | x_0)} \left[ \frac{p_\theta(x_{0:T})}{q(x_{1:T} | x_0)} \right]
\geq \mathbb{E}<em>{q(x</em>{1:T} | x_0)} \left[ \log \frac{p_\theta(x_{0:T})}{q(x_{1:T} | x_0)} \right]
$</p>
<p>즉, <strong>우리는 로그 안의 기대값을 직접 최적화할 수 없으니까, 대신 우변을 최적화하는 방식으로 변환한 거야!</strong>  </p>
<h3 id="수식-4">수식 (4)</h3>
<p>Forward 과정에서 특정한 시점 t의 이미지를 바로 샘플링하는 수식</p>
<h3 id="수식-5---variational-bound-변분-경계를-이용하여-손실함수">수식 (5) - Variational Bound (변분 경계)를 이용하여 손실함수</h3>
<p><em>손실 함수(loss function)*</em> 를 정의</p>
<p>논문의 <strong>수식 (5)</strong> 에 대해서 차근차근 자세히 설명해줄게!  </p>
<p>이 수식은 DDPM에서 <strong>손실 함수(loss function)</strong> 를 정의하는 중요한 식이야.<br>즉, 모델이 어떻게 학습되는지를 결정하는 핵심적인 역할을 해.</p>
<hr>
<p><strong>수식 (5)의 기본 개념: Variational Bound (변분 경계)</strong></p>
<p>DDPM 모델은 원래 <strong>확률 모델</strong>이다.
목표: $ ( p_\theta(x_0) )$ 즉, <strong>모델이 생성한 데이터 분포</strong>가 <strong>실제 데이터 분포 ( q(x_0) )</strong> 와 최대한 비슷해지는 것.</p>
<p> <strong>음의 로그 우도(Negative Log Likelihood, NLL)</strong> 를 최소화하여 최적화 시킬 수 있음.</p>
<p>$
-\log p_\theta(x_0)
$</p>
<p>하지만 직접적으로 계산하기 어렵기 때문에, <strong>변분 경계(VB, Variational Bound)</strong> 라는 테크닉을 사용해서 대신 최적화할 수 있다.</p>
<p><strong>&lt;수식 (5)의 항 분석&gt;</strong>
$
L = \mathbb{E}<em>q \Big[ D</em>{KL}(q(x_T | x_0) || p(x_T)) + \sum_{t=1}^{T} D_{KL}(q(x_{t-1} | x_t, x_0) || p_\theta(x_{t-1} | x_t)) \Big]
$</p>
<p><strong>(1) 첫 번째 KL 발산 항:</strong>
$
D_{KL}(q(x_T | x_0) || p(x_T))
$</p>
<p>: Forward 과정의 마지막 단계에서 $x_T$ 가 노이즈 분포 $p(x_T)$ 와 얼마나 다른지를 측정하는 항</p>
<p>즉,</p>
<ul>
<li>Forward 과정에서 마지막 ( x_T ) 는 우리가 설계한 ( q(x_T | x_0) ) 로부터 나오는데,</li>
<li>우리가 모델에서 가정한 <strong>초기 노이즈 분포</strong> $p(x_T)$는 보통 <strong>정규분포 $\mathcal{N}(0, I)$</strong> 를 따르게 설정한다.</li>
<li>그러므로 <strong>이 KL 발산 항을 최소화하면, Forward 과정이 $x_T$에서 우리가 원하는 가우시안 분포로 잘 수렴하도록 도와줌</strong>.</li>
</ul>
<p><strong>-&gt; 학습 과정에서 상수값이 되기 때문에 무시.</strong></p>
<p><strong>(2) 두 번째 KL 발산 항: $D_{KL}(q(x_{t-1} | x_t, x_0) || p_\theta(x_{t-1} | x_t))$</strong>  </p>
<p><strong>&quot;모델 $p_\theta(x_{t-1} | x_t)$이 실제 역과정(Reverse process) 분포 $q(x_{t-1} | x_t, x_0)$를 얼마나 잘 따라가는지&quot;</strong> 를 측정.</p>
<ul>
<li><strong>$q(x_{t-1} | x_t, x_0)$</strong>: Forward 과정에서 우리가 정의한 실제 노이즈 제거 확률 분포 (Ground truth)  </li>
<li><strong>$p_\theta(x_{t-1} | x_t)$</strong>: 우리가 학습하려는 역과정(Reverse process)의 모델 예측 값  </li>
<li><strong>KL 발산 $D_{KL}$</strong>: 두 확률 분포의 차이를 나타냄  </li>
</ul>
<p>이 KL 발산 값을 최소화하면?<br>-&gt; 모델 $p_\theta(x_{t-1} | x_t)$ 가 실제 Forward 과정에서의 노이즈 제거 분포 $q(x_{t-1} | x_t, x_0)$와 최대한 비슷해지도록 학습됨을 의미.</p>
<hr>
<p><strong>최적화 방식 (손실 함수 L을 어떻게 학습하나?)</strong>
최적화해야 하는 손실 함수 $L$ 은 결국:</p>
<p>$
L = \sum_{t=1}^{T} D_{KL}(q(x_{t-1} | x_t, x_0) || p_\theta(x_{t-1} | x_t))
$
이다.</p>
<p>즉, Forward 과정에서 정의한 $q(x_{t-1} | x_t, x_0)$ 를 모델이 예측하는 $p_\theta(x_{t-1} | x_t)$ 가 최대한 비슷하게 만들도록 학습하는 것.</p>
<p>논문에서는 이 KL 발산을 직접 최소화하기보다는,<br>Forward 과정에서 한 번에 샘플링할 수 있는 <strong>( x_t = \sqrt{\bar{\alpha}_t} x_0 + \sqrt{1 - \bar{\alpha}_t} \epsilon )</strong><br>이 수식을 활용해서 <strong>모델이 노이즈 $\epsilon$을 예측하는 방식</strong>으로 학습한다.</p>
<p>즉, 최종적으로 <strong>손실 함수는 모델이 예측한 노이즈와 실제 노이즈 간의 차이를 최소화하는 방식</strong>으로 변형한다.</p>
<blockquote>
<p><strong>KL 발산(KL Divergence, Kullback-Leibler Divergence)이란?</strong></p>
</blockquote>
<ul>
<li><strong>두 확률 분포가 얼마나 다른지를 측정하는 방법</strong><ul>
<li>즉, 우리가 만든 모델의 분포가 실제 데이터의 분포와 얼마나 비슷한지를 평가.</li>
</ul>
</li>
<li>수식: 
$
D_{KL}(P || Q) = \sum_x P(x) \log \frac{P(x)}{Q(x)}
$<ul>
<li><strong>$P(x)$</strong>: 실제 따르고 싶은 &quot;참된&quot; 확률 분포 (Ground truth)</li>
<li><strong>$Q(x)$</strong>: 모델을 통해 학습하고 싶은 확률 분포 (모델의 예측)</li>
<li><strong>KL 발산 $D_{KL}(P || Q)$</strong>: 두 확률 분포가 얼마나 다른지 수치적으로 나타내는 값.   <blockquote>
</blockquote>
즉, KL 발산은 <strong>&quot;Q(x) 가 P(x) 와 얼마나 비슷한지를 측정하는 척도&quot;</strong><blockquote>
</blockquote>
</li>
</ul>
</li>
<li><em>직관적 이해*</em>
만약 $P(x)$와 $Q(x)$ 가 완전히 동일하면?  <ul>
<li>$\frac{P(x)}{Q(x)} = 1$이 되고,  </li>
<li>$\log 1 = 0$이므로 <strong>KL 발산 = 0</strong> </li>
<li>즉, 두 분포가 같으면 KL 발산 값은 0이다.<blockquote>
</blockquote>
만약 $Q(x)$가 $P(x)$와 다르다면?  </li>
<li>$\frac{P(x)}{Q(x)}$값이 커지거나 작아지면서 KL 발산 값이 증가함.  </li>
<li><strong>즉, KL 발산 값이 클수록 두 분포가 다르다는 의미</strong>야.<blockquote>
</blockquote>
</li>
</ul>
</li>
<li><em>DDPM에서 KL 발산은 어디에 쓰일까?*</em>
DDPM 논문에서는 KL 발산을 이용해서 <strong>Forward 과정과 Reverse 과정의 차이를 줄이는 것</strong>이 목적.<blockquote>
</blockquote>
</li>
<li><em>수식 (5)*</em> 에 나왔던 KL 발산:
$
D_{KL}(q(x_{t-1} | x_t, x_0) || p_\theta(x_{t-1} | x_t))
$
이 KL 발산을 최소화하면:
👉 모델이 $x_t$를 보고 $x_{t-1}$ 를 예측하는 방법이 실제 데이터 분포를 따르도록 학습된다는 의미.<blockquote>
</blockquote>
DDPM에서는 KL 발산을 사용해서:</li>
</ul>
<ol>
<li><strong>Forward 과정에서 설계한 노이즈 추가 과정</strong></li>
<li><strong>Reverse 과정에서 모델이 학습한 노이즈 제거 과정이 최대한 비슷하도록 만듦.</strong></li>
</ol>
<h3 id="수식-6-7">수식 (6), (7)</h3>
<p>Forward 과정에서 정의된 분포 $q(x_{t-1} | x_t, x_0)$를 구하는 과정에서 유도된 식.
Reverse 과정에서 원래 데이터로 복원하는 확률 분포를 정확하게 구하는 방법을 설명하는 핵심적인 식이다.</p>
<p><strong>1️⃣ 수식 (6):</strong>
$
q(x_{t-1} | x_t, x_0) = \mathcal{N}(x_{t-1}; \tilde{\mu}<em>t(x_t, x_0), \tilde{\beta}_t I)
$
**Forward 과정에서 $x_t$가 주어졌을 때, 한 단계 전인$x</em>{t-1}$의 분포를 구하는 공식**</p>
<ul>
<li><strong>평균(Mean): $\tilde{\mu}_t(x_t, x_0)$</strong>  <ul>
<li>$x_t$와 $x_0$의 가중합(weighted sum)으로 표현됨.</li>
</ul>
</li>
<li><strong>분산(Variance): $\tilde{\beta}_t$</strong>  <ul>
<li>Forward 과정에서 정의된 $\beta_t$값을 조정한 형태.</li>
</ul>
</li>
</ul>
<p><strong>2️⃣ 수식 (7):</strong></p>
<p><strong>평균$\tilde{\mu}_t(x_t, x_0)$:</strong>
$
\tilde{\mu}<em>t(x_t, x_0) := \frac{\sqrt{\bar{\alpha}</em>{t-1}} \beta_t}{1 - \bar{\alpha}<em>t} x_0 + \frac{\sqrt{\alpha_t} (1 - \bar{\alpha}</em>{t-1})}{1 - \bar{\alpha}_t} x_t
$</p>
<ol>
<li><strong>$x_t$와 $x_0$를 적절히 조합해서$x_{t-1}$의 평균을 구할 수 있다.</strong><ul>
<li>$x_0$에 가중치 $\frac{\sqrt{\bar{\alpha}_{t-1}} \beta_t}{1 - \bar{\alpha}_t}$를 곱한 항</li>
<li>$x_t$에 가중치 $\frac{\sqrt{\alpha_t} (1 - \bar{\alpha}_{t-1})}{1 - \bar{\alpha}_t}$를 곱한 항</li>
</ul>
</li>
<li><strong>Forward 과정에서 $x_t$가 $x_0$와 노이즈 $\epsilon$의 조합으로 되어 있기 때문에, 역방향으로 추정할 때도 이 두 항이 사용됨.</strong></li>
<li>즉, 이 평균 값은 Forward 과정에서 $x_t$가 어떻게 만들어졌는지를 반대로 계산해서 ( x_{t-1} ) 를 구하는 과정이다.</li>
</ol>
<p><strong>분산 $\tilde{\beta}_t$:</strong>
$
\tilde{\beta}<em>t := \frac{1 - \bar{\alpha}</em>{t-1}}{1 - \bar{\alpha}_t} \beta_t
$</p>
<p>Forward 과정에서 정의된 노이즈 $\beta_t$를 조정해서 역과정에 적절하게 맞추는 역할</p>
<ul>
<li>여기서 $\bar{\alpha}<em>t = \prod</em>{s=1}^{t} \alpha_s$ (누적된 노이즈 조절 계수)</li>
<li>Forward 과정에서 $x_t$ 가 점점 흐려지면서, 노이즈가 추가된 정도를 반영한 값.</li>
</ul>
<p>$x_t$ 를 보고 $x_{t-1}$를 샘플링할 때, 노이즈를 얼마나 추가할지 조절하는 값</p>
<p><strong>3️⃣ 수식의 의의</strong>
**Reverse 과정에서 정확하게 $x_{t-1}$를 샘플링할 수 있도록 도와줌.</p>
<ul>
<li>Forward 과정에서  $x_t$ 를 $x_{t-1}$로부터 노이즈를 추가해서 만들었었다.</li>
<li>그런데 역과정에서는 노이즈가 추가된 $x_t$를 보고 원래 $x_{t-1}$를 복원해야 한다.</li>
<li>이때, $q(x_{t-1} | x_t, x_0)$를 알면 Forward 과정에서 $x_t$를 만들었던 방법을 거꾸로 되돌리는 확률 분포 를 정확하게 구할 수 있다.</li>
</ul>
<h1 id="3-diffusion-models-and-denoising-autoencoders-디퓨전-모델과-잡음-auto-encoders">3. Diffusion models and denoising autoencoders (디퓨전 모델과 잡음 auto encoders)</h1>
<ul>
<li>확산 모델은 제한적인 것처럼 보이지만 구현 자유도가 크다.</li>
<li>Forward 과정의 βₜ, 역과정의 가우시안 분포 파라미터화, 모델 아키텍처를 결정해야 한다.</li>
<li>확산 모델과 Denoising Score Matching의 연결을 밝힘으로써, 손실 함수를 단순화할 수 있다.</li>
<li>모델 설계의 타당성은 단순함과 실험적 결과를 통해 증명된다.</li>
<li>논의는 수식 (5)의 항목들에 따라 진행될 것이다.</li>
</ul>
<h2 id="31-forward-process-and-l_t">3.1 Forward process and $L_T$</h2>
<ul>
<li><strong>Forward 과정의 분산 $\beta_t$를 학습할 수도 있지만, 이 논문에서는 상수로 고정</strong>
 -&gt; Forward 과정의 근사 사후분포 $q$에 학습할 파라미터가 없게 된다.**
 -&gt;$L_T$ (손실 함수의 일부 항)이 상수가 되므로, 학습 과정에서 무시됨**</li>
</ul>
<h2 id="32-reverse-process-and-l_1t-1">3.2 Reverse process and $L_{1:T-1}$</h2>
<ul>
<li>Reverse 과정의 분산 $\Sigma_\theta(x_t, t)$를 학습하지 않고 고정된 상수 $\sigma_t^2$로 설정.</li>
<li>실험적으로 $\sigma_t^2 = \beta_t$ 와 $\sigma_t^2 = \tilde{\beta}_t$ 를 사용했을 때 비슷한 결과가 나왔다.</li>
<li>Reverse 과정의 평균 $\mu_\theta(x_t, t)$ 를 예측하는 가장 간단한 방법은 Forward 과정의 사후분포 평균 $\tilde{\mu}_t$ 를 직접 예측하는 것이다.</li>
<li>수식 (4)와 수식 (7)을 활용하여 $x_t$ 를 $x_0$ 와 노이즈 $\epsilon$ 의 함수로 재구성할 수 있다.</li>
</ul>
<h3 id="수식-8---최종-손실-함수">수식 (8) - 최종 손실 함수</h3>
<p>논문에서 사용한 최종 손실 함수는 (8)이다. 
하지만 수식 (3)으로 부터 (5)를 유도했고, (5)로부터 (8)을 유도했다. </p>
<ul>
<li>수식 (3): 논문의 기본 목표를 정의하는 손실 함수지만, 너무 일반적이어서 바로 사용할 수 없음.</li>
<li>수식 (5): 는 Forward와 Reverse 과정 간 KL 발산을 최소화하는 방식으로 손실을 구체화한 것이지만, 여전히 KL 발산을 직접 계산해야 하는 문제가 있음.</li>
<li>수식 (8): KL 발산을 풀어서 L2 Loss 형태로 변형.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Multi-Scale Embedding을 활용한 Crowd Counting 모델]]></title>
            <link>https://velog.io/@syl_vana/Multi-Scale-Embedding%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-Crowd-Counting-%EB%AA%A8%EB%8D%B8</link>
            <guid>https://velog.io/@syl_vana/Multi-Scale-Embedding%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%9C-Crowd-Counting-%EB%AA%A8%EB%8D%B8</guid>
            <pubDate>Fri, 17 Jan 2025 07:03:39 GMT</pubDate>
            <description><![CDATA[<h3 id="1-csrnet-congested-scene-recognition-network">1. CSRNet (Congested Scene Recognition Network)</h3>
<p>CNN 백본(VGG-16)과 dilated convolution을 활용하여 넓은 수용영역을 확보하고, 군중 밀집 지역의 밀도를 예측하는 방식.</p>
<h3 id="2-mcnn-multi-column-cnn">2. MCNN (Multi-Column CNN)</h3>
<p>서로 다른 커널 크기를 가진 3개의 병렬 CNN을 사용하여 다양한 스케일의 정보를 학습하는 모델.</p>
<h3 id="3-scar-scale-aware-crowd-counting">3. SCAR (Scale-aware Crowd Counting)</h3>
<p>다중 스케일 특징을 동적으로 조절하여 군중 밀도에 맞게 적절한 스케일을 강조하는 모델.</p>
<h3 id="4-sanet-scale-aggregation-network">4. SANet (Scale Aggregation Network)</h3>
<p>다중 스케일 합성곱을 사용하여 서로 다른 크기의 특징을 효과적으로 조합하는 모델.</p>
<h3 id="5-transformer-기반-모델-cctrans-등">5. Transformer 기반 모델 (CCTrans 등)</h3>
<p>Self-Attention을 활용하여 이미지 내 여러 스케일에서 정보를 추출하고, 군중 계수를 예측하는 방식.</p>
<h2 id="2️⃣-multi-scale-crowd-counting-관련-논문--코드-분석">2️⃣ Multi-Scale Crowd Counting 관련 논문 &amp; 코드 분석</h2>
<p><strong>✅ 최신 연구를 통해, 실제 PCB 검사 적용 가능성을 평가</strong>  </p>
<h3 id="1-필수-논문-📄">(1) <strong>필수 논문 📄</strong></h3>
<p>💡 <strong>Multi-Scale 군중 계수 연구 트렌드</strong><br>📌 논문을 읽을 때, <strong>Multi-Scale Feature Extraction 방식</strong>에 집중해서 분석  </p>
<table>
<thead>
<tr>
<th>논문</th>
<th>주요 특징</th>
</tr>
</thead>
<tbody><tr>
<td><strong>MCNN (Multi-Column CNN, CVPR 2016)</strong></td>
<td>다른 크기의 커널을 사용하여 Multi-Scale Feature Extraction</td>
</tr>
<tr>
<td><strong>CSRNet (Congested Scene Recognition Network, CVPR 2018)</strong></td>
<td>Dilated CNN 활용 (Atrous Conv)</td>
</tr>
<tr>
<td><strong>SANet (Scale Aggregation Network, ECCV 2018)</strong></td>
<td>다중 스케일 피처를 효과적으로 결합</td>
</tr>
<tr>
<td><strong>SCAR (Scale-Aware Crowd Counting, 2019)</strong></td>
<td>Spatial &amp; Channel Attention 기반 Multi-Scale Feature Fusion</td>
</tr>
<tr>
<td><strong>CCTrans (Transformer 기반 Counting, 2021)</strong></td>
<td>CNN 대신 Transformer 사용하여 Global &amp; Local Feature 학습</td>
</tr>
</tbody></table>
<p>🔹 <strong>논문 읽기 추천 순서</strong><br>1️⃣ MCNN → 2️⃣ CSRNet → 3️⃣ SCAR/SANet → 4️⃣ CCTrans  </p>
<h3 id="2-코드-실습-🔧">(2) <strong>코드 실습 🔧</strong></h3>
<p>✅ 논문만 읽으면 이해하기 어려우니, 직접 코드를 실행하면서 구조를 익히기  </p>
<ul>
<li><strong>MCNN PyTorch 코드</strong>: <a href="https://github.com/ZhihengCV/Multi-Column-CNN-Crowd-Counting">https://github.com/ZhihengCV/Multi-Column-CNN-Crowd-Counting</a>  </li>
<li><strong>CSRNet PyTorch 코드</strong>: <a href="https://github.com/leeyeehoo/CSRNet-pytorch">https://github.com/leeyeehoo/CSRNet-pytorch</a>  </li>
<li><strong>SANet PyTorch 코드</strong>: <a href="https://github.com/VisDrone/SANet">https://github.com/VisDrone/SANet</a>  </li>
</ul>
<p>💡 <strong>분석 포인트</strong><br>1️⃣ Feature Extractor가 어떻게 Multi-Scale 정보를 처리하는지 확인<br>2️⃣ <strong>Dilated CNN / FPN / Transformer 기반인지 분석</strong><br>3️⃣ <strong>PCB 검사 모델과 비교</strong>: 기존 모델과의 차이점 찾기  </p>
<hr>
<h2 id="3️⃣-multi-scale-feature-적용-실습">3️⃣ Multi-Scale Feature 적용 실습</h2>
<p>✅ 실무 적용을 위해 PCB 데이터셋으로 Multi-Scale Feature를 실험해 보기  </p>
<h3 id="1-pcb-데이터-준비--전처리">(1) <strong>PCB 데이터 준비 &amp; 전처리</strong></h3>
<p>✔ 기존 PCB 이미지 데이터 → <strong>Crowd Counting 방식으로 변환 가능?</strong><br>✔ PCB 부품을 작은 객체(사람)처럼 보고 밀도 맵 생성 가능 여부 확인<br>✔ <strong>Data Augmentation</strong> 실험: <strong>Affine Transform, Color Jitter, Noise, Blur 등</strong>  </p>
<h3 id="2-multi-scale-feature-extraction-비교-실험">(2) <strong>Multi-Scale Feature Extraction 비교 실험</strong></h3>
<p>✅ <strong>PCB 검사 모델에서 어떤 방식이 가장 효과적인지 실험</strong><br>| 실험 모델 | 핵심 개념 |
|-----------|----------|
| <strong>Baseline CNN (ResNet, VGG)</strong> | 기존 모델 (비교 대상) |
| <strong>Dilated CNN (CSRNet 방식)</strong> | 큰 수용 영역 활용 |
| <strong>Multi-Column CNN (MCNN)</strong> | 다른 커널 크기로 Multi-Scale 정보 추출 |
| <strong>Feature Pyramid Networks (FPN)</strong> | 해상도별로 다른 Feature 학습 |
| <strong>Transformer 기반 (CCTrans)</strong> | Attention 기반 다중 스케일 학습 |</p>
<p>📌 <strong>결과 분석</strong>  </p>
<ul>
<li>어떤 모델이 PCB 검사에서 정확도가 더 높은지 실험  </li>
<li>Multi-Scale Feature가 <strong>작은 부품과 큰 부품을 동시에 잘 인식하는지 확인</strong>  </li>
<li>실무 적용 가능 여부 평가  </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[ASPP (Atrous Spatial Pyramid Pooling)]]></title>
            <link>https://velog.io/@syl_vana/ASPP-Atrous-Spatial-Pyramid-Pooling</link>
            <guid>https://velog.io/@syl_vana/ASPP-Atrous-Spatial-Pyramid-Pooling</guid>
            <pubDate>Fri, 17 Jan 2025 06:48:30 GMT</pubDate>
            <description><![CDATA[<p>요즘 multi-scaling 공부가 필요함</p>
<h2 id="1-atrous-convolution--dilated-convolution">1. Atrous convolution (= Dilated Convolution)</h2>
<p>dilation이 적용된 합성곱
dilation이란 아래 사진과 같이 픽셀 사이에 빈 공간을 사이사이에 넣는 것을 말한다. </p>
<p><img src="https://velog.velcdn.com/images/syl_vana/post/928e4708-6555-4154-b0f9-55e8d9c42829/image.png" alt="">
수용 영역(Receptive Field)가 확장되어 더 넓은 범위의 정보 학습이 가능.</p>
<h2 id="2-ssp-spatial-pyramid-pooling">2. SSP (Spatial Pyramid Pooling)</h2>
<p>입력 크기에 상관없이 고정된 크기의 특징 벡터를 만들 수 있는 풀링 기법. </p>
<blockquote>
<h3 id="ssp-필요한-이유">ssp 필요한 이유</h3>
<p>일반적인 CNN  구조에서 FC Layer를 사용하려면 고정된 크기의 입력이 필요하다. 하지만 입력 이미지 크기가 다르면 FC Layer에 입력할 수 없기 때문에 일반적으로 CNN에서는 고정된 크기로 Resizing을 해야 한다. 하지만 Resizing을 하려면 이미지 비율이 깨지거나 중요한 정보 손실될 수 있음. </p>
</blockquote>
<p>따라서 다양한 크기의 풀링 윈도우를 사용하여 다중 스케일 정보를 유지하면서 고정된 크기의 출력 벡터 생성함.</p>
<blockquote>
<p><strong>다양한 크기의 풀링을 수행하여, 다중 스케일 정보를 하나의 벡터로 병합한다.</strong>
<em>1x1 풀링 → 전체적인 정보 (전역 정보)
2x2 풀링 → 중간 크기의 정보
4x4 풀링 → 더 작은 영역의 정보
8x8 풀링 → 매우 작은 국소적(local) 정보</em></p>
</blockquote>
<h2 id="3-assp-atrous-spatial-pyramid-pooling">3. ASSP (Atrous Spatial Pyramid Pooling)</h2>
<p>다중 스케일 정보를 효과적으로 캡쳐하기 위해 설계된 합성곱 연산 기법.</p>
<p>SPP(Spatial Pyramid Pooling)의 atrous 버전.</p>
<p>ASSP는 Pooling 대신 Atrous Convolution을 사용 → 공간 해상도를 유지하면서도 다중 스케일 정보를 추출할 수 있다. </p>
<p><img src="https://velog.velcdn.com/images/syl_vana/post/0ee31715-361e-46a8-98da-c0037dd23cd5/image.png" alt=""></p>
<p>출처: <a href="https://blog.naver.com/h22hyeon/222247290992">https://blog.naver.com/h22hyeon/222247290992</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서버 동작 멈춤 문제 해결: 프로세스 관리]]></title>
            <link>https://velog.io/@syl_vana/%EC%84%9C%EB%B2%84-%EB%8F%99%EC%9E%91-%EB%A9%88%EC%B6%A4-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B4%80%EB%A6%AC</link>
            <guid>https://velog.io/@syl_vana/%EC%84%9C%EB%B2%84-%EB%8F%99%EC%9E%91-%EB%A9%88%EC%B6%A4-%EB%AC%B8%EC%A0%9C-%ED%95%B4%EA%B2%B0-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B4%80%EB%A6%AC</guid>
            <pubDate>Fri, 29 Nov 2024 08:45:43 GMT</pubDate>
            <description><![CDATA[<p>서버가 갑자기 멈추거나 비정상적인 동작을 보일 때, 특정 프로세스가 문제를 일으킬 가능성이 크다.</p>
<h2 id="1-문제-상황-진단">1. 문제 상황 진단</h2>
<p>서버에 SSH 또는 로컬로 접속</p>
<pre><code class="language-bash">ssh 사용자명@서버_IP</code></pre>
<h2 id="2-실행-중인-프로세스-확인">2. 실행 중인 프로세스 확인</h2>
<pre><code class="language-bash">ps -ef | grep 사용자명</code></pre>
<ul>
<li><code>ps -ef</code>: 현재 실행 중인 모든 프로세스를 상세히 출력.</li>
<li><code>| (파이프)</code>: ps -ef 명령의 출력을 grep 명령에 전달.</li>
<li><code>grep 사용자명</code>: 해당 사용자와 관련된 프로세스만 필터링하여 출력.</li>
</ul>
<p>출력 예시:</p>
<pre><code class="language-bash">사용자명   2954205       1  0 11:08 ?        00:00:00 /usr/libexec/gvfs-goa-volume-monitor
사용자명   2954219       1  0 11:08 ?        00:00:00 /usr/libexec/goa-daemon
사용자명   2954545  2954205  0 11:09 ?        00:00:00 /usr/bin/ibus-daemon --xim</code></pre>
<h2 id="3-문제-프로세스-종료">3. 문제 프로세스 종료</h2>
<p>문제가 있는 프로세스를 종료하려면 kill 명령을 사용한다.
강제 종료가 필요할 경우 -9 옵션을 추가.</p>
<pre><code class="language-bash">sudo kill -9 [프로세스 ID]
</code></pre>
<p>예시</p>
<pre><code class="language-bash">sudo kill -9 2954219</code></pre>
<ul>
<li><code>kill</code>: 지정된 프로세스 종료.</li>
<li><code>-9</code>: 강제로 종료(SIGNAL 9)를 보냄. 프로세스가 정상적으로 응답하지 않을 경우 사용.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[cnn 구조에 따른 data 구조 변화]]></title>
            <link>https://velog.io/@syl_vana/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88-%EC%A0%95%EA%B6%8C%EC%A7%80%EB%A5%B4%EA%B8%B0-1%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@syl_vana/%EB%94%A5%EB%9F%AC%EB%8B%9D-%EA%B8%B0%EC%B4%88-%EC%A0%95%EA%B6%8C%EC%A7%80%EB%A5%B4%EA%B8%B0-1%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Thu, 24 Oct 2024 05:45:30 GMT</pubDate>
            <description><![CDATA[<h1 id="cnn의-일반적인-구조">CNN의 일반적인 구조</h1>
<p>일반적인 CNN은 다음과 같은 순서로 배열된 레이어들로 구성됩니다:</p>
<ol>
<li><strong>입력 레이어</strong>: 원시 픽셀 데이터를 받아들이는 레이어.</li>
<li><strong>합성곱 레이어</strong>: 입력 데이터에 합성곱 연산을 적용하여 특징을 추출.</li>
<li><strong>활성화 함수</strong>: 비선형성을 도입하여 모델의 표현력을 높임 (예: ReLU).</li>
<li><strong>풀링 레이어</strong>: 공간적 차원(높이와 너비)을 줄여 계산량을 감소시키고 과적합을 방지.</li>
<li><strong>완전 연결 레이어</strong>: 추출된 특징을 기반으로 높은 수준의 추론 및 분류를 수행.</li>
<li><strong>출력 레이어</strong>: 최종 예측이나 분류 결과를 제공.
<img src="https://velog.velcdn.com/images/syl_vana/post/d8937820-612c-40d1-8dd6-f8c4dc84bde0/image.png" alt=""></li>
</ol>
<h2 id="예시로-각-레이어별-데이터-형태-변화-설명">예시로 각 레이어별 데이터 형태 변화 설명:</h2>
<p>예시: 32x32 픽셀의 그레이스케일 이미지</p>
<ol>
<li><p><strong>입력 레이어:</strong></p>
<ul>
<li><strong>입력 형태</strong>: <code>(높이, 너비, 채널 수)</code></li>
<li><strong>예시 형태</strong>: <code>(32, 32, 1)</code> (그레이스케일 이미지이므로 채널 수는 1)</li>
</ul>
</li>
<li><p><strong>첫 번째 합성곱 레이어:</strong></p>
<ul>
<li><strong>연산</strong>: 학습 가능한 필터(커널)를 입력에 적용.</li>
<li><strong>파라미터:</strong><ul>
<li>필터 수 (예: 16개)</li>
<li>커널 크기 (예: 3x3)</li>
<li>스트라이드 (예: 1)</li>
<li>패딩 (예: &#39;same&#39; 또는 &#39;valid&#39;)</li>
</ul>
</li>
<li><strong>출력 계산:</strong><ul>
<li>패딩이 &#39;same&#39;인 경우 출력 차원은 입력과 동일.</li>
<li><strong>출력 형태</strong>: <code>(32, 32, 16)</code> (필터 수 만큼 채널 증가)</li>
</ul>
</li>
</ul>
<p><strong>형태 변화 설명:</strong></p>
<ul>
<li>패딩을 사용하여 높이와 너비는 유지.</li>
<li>채널 수는 필터 수 만큼 증가.</li>
</ul>
</li>
<li><p><strong>활성화 함수(ReLU 등):</strong></p>
<ul>
<li><strong>연산</strong>: 각 요소에 비선형 함수를 적용.</li>
<li><strong>출력 형태</strong>: 입력과 동일.</li>
<li><strong>활성화 후 형태</strong>: <code>(32, 32, 16)</code></li>
</ul>
</li>
<li><p><strong>첫 번째 풀링 레이어:</strong></p>
<ul>
<li><strong>연산</strong>: 윈도우 내에서 최대값이나 평균값을 취해 다운샘플링.</li>
<li><strong>파라미터:</strong><ul>
<li>풀 크기 (예: 2x2)</li>
<li>스트라이드 (예: 2)</li>
</ul>
</li>
<li><strong>출력 계산:</strong><ul>
<li>새로운 높이 = (이전 높이 - 풀 높이) / 스트라이드 + 1</li>
<li>스트라이드가 풀 크기와 같고 &#39;valid&#39; 패딩인 경우 단순화됨.</li>
</ul>
</li>
<li><strong>출력 형태</strong>: <code>(16, 16, 16)</code></li>
</ul>
<p><strong>형태 변화 설명:</strong></p>
<ul>
<li>풀링을 통해 높이와 너비가 절반으로 감소.</li>
<li>채널 수는 그대로 유지.</li>
</ul>
</li>
<li><p><strong>두 번째 합성곱 레이어:</strong></p>
<ul>
<li><strong>파라미터:</strong><ul>
<li>필터 수 (예: 32개)</li>
<li>커널 크기 (예: 3x3)</li>
<li>스트라이드 (예: 1)</li>
<li>패딩 (예: &#39;same&#39;)</li>
</ul>
</li>
<li><strong>출력 형태</strong>: <code>(16, 16, 32)</code></li>
</ul>
</li>
<li><p><strong>활성화 함수:</strong></p>
<ul>
<li><strong>출력 형태</strong>: 동일.</li>
<li><strong>활성화 후 형태</strong>: <code>(16, 16, 32)</code></li>
</ul>
</li>
<li><p><strong>두 번째 풀링 레이어:</strong></p>
<ul>
<li><strong>파라미터:</strong><ul>
<li>풀 크기 (예: 2x2)</li>
<li>스트라이드 (예: 2)</li>
</ul>
</li>
<li><strong>출력 형태</strong>: <code>(8, 8, 32)</code></li>
</ul>
</li>
<li><p><strong>평탄화(Flatten) 레이어:</strong></p>
<ul>
<li><strong>연산</strong>: 3D 출력을 1D 벡터로 변환하여 완전 연결 레이어에 입력.</li>
<li><strong>계산:</strong><ul>
<li>총 유닛 수 = 높이 x 너비 x 채널 수</li>
<li><strong>총 유닛 수</strong>: <code>8 x 8 x 32 = 2048</code></li>
</ul>
</li>
<li><strong>출력 형태</strong>: <code>(2048,)</code></li>
</ul>
</li>
<li><p><strong>완전 연결(Dense) 레이어:</strong></p>
<ul>
<li><strong>파라미터:</strong><ul>
<li>유닛 수 (예: 128개 뉴런)</li>
</ul>
</li>
<li><strong>연산</strong>: 입력의 가중합을 계산하여 출력 생성.</li>
<li><strong>출력 형태</strong>: <code>(128,)</code></li>
</ul>
</li>
<li><p><strong>활성화 함수(ReLU 등):</strong></p>
<ul>
<li><strong>출력 형태</strong>: 동일.</li>
<li><strong>활성화 후 형태</strong>: <code>(128,)</code></li>
</ul>
</li>
<li><p><strong>출력 레이어:</strong></p>
<ul>
<li><strong>파라미터:</strong><ul>
<li>유닛 수 (예: 클래스 수 만큼, 숫자 0~9를 분류한다면 10)</li>
<li>활성화 함수 (예: 소프트맥스)</li>
</ul>
</li>
<li><strong>출력 형태</strong>: <code>(10,)</code></li>
</ul>
</li>
</ol>
<h3 id="각-레이어의-상세-설명">각 레이어의 상세 설명:</h3>
<ul>
<li><p><strong>합성곱 레이어:</strong></p>
<p>지역적인 패턴과 특징(에지, 텍스처, 형태 등)을 감지하는 역할을 합니다. 필터가 입력 데이터 위를 슬라이딩하며 요소별 곱셈과 합산(합성곱 연산)을 수행합니다.</p>
</li>
<li><p><strong>패딩:</strong></p>
<ul>
<li>&#39;Same&#39; 패딩: 출력이 입력과 동일한 공간적 차원을 가지도록 입력 주위에 0을 추가.</li>
<li>&#39;Valid&#39; 패딩: 패딩을 사용하지 않으며, 커널 크기에 따라 출력 크기가 감소.</li>
</ul>
</li>
<li><p><strong>스트라이드:</strong></p>
<p>필터 창이 입력 매트릭스 위를 이동하는 픽셀 수입니다. 스트라이드가 클수록 출력이 작아집니다.</p>
</li>
<li><p><strong>활성화 함수:</strong></p>
<p>비선형성을 도입하여 복잡한 패턴을 학습할 수 있게 합니다. ReLU(Rectified Linear Unit)는 기울기 소실 문제를 완화하여 널리 사용됩니다.</p>
</li>
<li><p><strong>풀링 레이어:</strong></p>
<p>공간적 차원을 줄여 계산량을 감소시키고 과적합을 방지합니다. 대표적인 종류로는 최대 풀링과 평균 풀링이 있습니다.</p>
</li>
<li><p><strong>평탄화 레이어:</strong></p>
<p>다차원 출력을 완전 연결 레이어에 입력하기 위해 1차원 배열로 변환합니다.</p>
</li>
<li><p><strong>완전 연결 레이어:</strong></p>
<p>이전 레이어의 모든 뉴런과 연결되어 있으며, 합성곱 레이어에서 추출된 특징을 기반으로 예측을 수행합니다.</p>
</li>
<li><p><strong>출력 레이어:</strong></p>
<p>최종 출력을 생성합니다. 분류 작업의 경우 소프트맥스 활성화 함수를 사용하여 각 클래스에 대한 확률을 출력합니다.</p>
</li>
</ul>
<h3 id="추가-고려-사항">추가 고려 사항:</h3>
<ul>
<li><p><strong>배치 크기:</strong></p>
<p>데이터는 일반적으로 배치 단위로 처리됩니다. 배치 크기가 <code>N</code>인 경우 입력과 출력 형태는 <code>(N, 높이, 너비, 채널 수)</code>가 됩니다.</p>
</li>
<li><p><strong>컬러 이미지:</strong></p>
<p>RGB 이미지를 사용하는 경우 초기 입력 형태는 <code>(높이, 너비, 3)</code>입니다.</p>
</li>
<li><p><strong>깊은 네트워크:</strong></p>
<p>VGG, ResNet, Inception과 같은 현대적인 CNN은 더 많은 레이어를 포함하며, 추가적인 합성곱 및 풀링 레이어, 배치 정규화 레이어, 스킵 연결 등을 포함합니다.</p>
</li>
<li><p><strong>정규화 기법:</strong></p>
<ul>
<li><strong>드롭아웃 레이어</strong>: 학습 시 무작위로 일부 입력 유닛을 0으로 설정하여 과적합을 방지.</li>
<li><strong>배치 정규화</strong>: 미니배치마다 입력을 정규화하여 학습 과정을 안정화.</li>
</ul>
</li>
<li><p><strong>RGB 이미지 예시:</strong></p>
<p>64x64 픽셀의 RGB 이미지를 사용한다고 가정합니다.</p>
<ul>
<li><strong>입력 형태</strong>: <code>(64, 64, 3)</code></li>
</ul>
<p>3x3 크기의 32개 필터를 가진 합성곱 레이어를 통과하면:</p>
<ul>
<li><strong>출력 형태</strong>: <code>(64, 64, 32)</code></li>
</ul>
<p>&#39;Valid&#39; 패딩을 사용하면 공간적 차원이 감소합니다:</p>
<ul>
<li><strong>출력 높이와 너비</strong>: <code>64 - 3 + 1 = 62</code></li>
<li><strong>출력 형태</strong>: <code>(62, 62, 32)</code></li>
</ul>
<p>풀 크기가 2x2이고 스트라이드가 2인 풀링을 적용하면:</p>
<ul>
<li><strong>출력 형태</strong>: <code>(31, 31, 32)</code></li>
</ul>
</li>
</ul>
<h3 id="출력-차원-계산을-위한-수학-공식">출력 차원 계산을 위한 수학 공식:</h3>
<ul>
<li><p><strong>합성곱 레이어 출력 차원:</strong></p>
<pre><code>출력 높이 = (입력 높이 - 커널 높이 + 2 x 패딩) / 스트라이드 + 1
출력 너비 = (입력 너비 - 커널 너비 + 2 x 패딩) / 스트라이드 + 1</code></pre></li>
<li><p><strong>풀링 레이어 출력 차원:</strong></p>
<pre><code>출력 높이 = (입력 높이 - 풀 높이) / 스트라이드 + 1
출력 너비 = (입력 너비 - 풀 너비) / 스트라이드 + 1</code></pre></li>
</ul>
<h3 id="실용적인-팁">실용적인 팁:</h3>
<ul>
<li><p><strong>커널 크기 선택:</strong></p>
<p>일반적인 커널 크기는 3x3이나 5x5입니다. 작은 커널은 세부 정보를, 큰 커널은 더 넓은 특징을 포착합니다.</p>
</li>
<li><p><strong>네트워크의 깊이:</strong></p>
<p>레이어 수를 증가시키면 복잡한 특징을 학습할 수 있지만, 과적합 위험과 계산 비용이 증가할 수 있습니다.</p>
</li>
<li><p><strong>필터 수:</strong></p>
<p>일반적으로 깊은 레이어에서는 더 복잡한 특징을 포착하기 위해 필터 수를 증가시킵니다.</p>
</li>
<li><p><strong>최적화:</strong></p>
<p>학습률 스케줄링, 모멘텀, Adam과 같은 적응형 옵티마이저를 사용하여 학습을 개선합니다.</p>
</li>
</ul>
<h3 id="fully-connected-layer의-유닛">Fully Connected layer의 유닛</h3>
<ul>
<li><strong>중간 특징 학습</strong>
완전 연결 레이어의 유닛 수는 <strong>CNN의 최종 특징을 압축</strong>하거나, <strong>학습을 통해 중요한 패턴을 더 잘 잡아내기 위해</strong> 설정된 중간 유닛 수입니다. 이 레이어의 유닛 수는 보통 임의로 설정되며, 특정한 특징이나 패턴을 좀 더 잘 학습할 수 있도록 모델을 조정하는 단계입니다.</li>
<li><strong>유닛 수 선택</strong>
보통 128, 256, 512 등의 유닛 수를 사용하는데, 이는 CNN이 학습한 복잡한 특징을 모아 최종 분류(또는 예측)를 위해 준비하는 역할을 합니다.</li>
</ul>
<blockquote>
<p>prompt: 일반적인 cnn의 구조를 알고싶어. 그리고 구조를 지날 때 input data shape이 어떻게 변화하는지 예시를 들어가며 알려줘. 최대한 길게 많은 내용을 (한국어로) 설명해</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 입문 5일차 - 포인터, 네임 스페이스]]></title>
            <link>https://velog.io/@syl_vana/C-%EC%B1%8C%EB%A6%B0%EC%A7%80-5%EC%9D%BC%EC%B0%A8-%ED%8F%AC%EC%9D%B8%ED%84%B0-%EC%9E%98-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@syl_vana/C-%EC%B1%8C%EB%A6%B0%EC%A7%80-5%EC%9D%BC%EC%B0%A8-%ED%8F%AC%EC%9D%B8%ED%84%B0-%EC%9E%98-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 22 Oct 2024 01:54:46 GMT</pubDate>
            <description><![CDATA[<p>팀장님이 틈새 C++ 과외해주셨는데 GPT보다 머리에 잘 들어오는 것이다? </p>
<p><code>*</code>: 포인터
<code>&amp;</code>: 참조자
<code>::</code>: 범위 지정 연산자
<code>std</code>: 네임 스페이스
<code>.</code>: 멤버 연산자
이렇게 5개의 개념을 중점으로 설명해주셨다.</p>
<h2 id="포인터-">포인터 (*)</h2>
<ul>
<li>메모리 주소를 저장하는 변수<h2 id="-참조자-주소-연산자">&amp;: 참조자, 주소 연산자</h2>
<code>&amp;</code>는 <strong>두가지 기능</strong>이 있다. 
문맥상 기능을 나누는 것이 아니라, 컴파일러 자체가 문법적으로 구분하여 각각 다르게 처리함.<h3 id="1-참조자-선언">1. 참조자 선언</h3>
</li>
<li>기존 변수를 가리키는 또 다른 이름 (like. 별명)</li>
<li>별도의 메모리 주소를 저장하지 않는다.<pre><code class="language-cpp">int num = 10;    // num이라는 변수를 선언하고 값 10을 저장
int &amp;ref = num;  // ref는 num의 참조자 (=별명)</code></pre>
<h3 id="2-주소-연산자">2. 주소 연산자</h3>
<pre><code class="language-cpp">int num = 10;
int *ptr = &amp;num; // 여기서 &amp;는 변수의 주소를 가져오는 역할</code></pre>
</li>
<li><code>ptr</code>은 <code>num</code>의 주소를 저장하는 포인터가 된다.</li>
<li>이 경우 <code>&amp;</code>는 표현식의 일부로 사용된다. 변수 <code>num</code>의 주소를 얻는 주소 연산자로 해석된다. </li>
</ul>
<h3 id="예시-코드로-이해하기---와-의-관계">예시 코드로 이해하기 - *와 &amp;의 관계</h3>
<pre><code class="language-cpp">int q=0; // 정수형 변수 `q`르 0으로 초기화
int &amp;a=q; //`q`라는 변수의 참조자인 a를 선언. a가 q를 참조한다. a를 수정하면 q가 바뀌고 q를 수정하면 a도 바뀐다.
int *b= &amp;a; // a의 주소를 포인터 b에 저장. b는 a와 q의 주소를 가리킨다.
int c= *b; // b가 가리키는 값을 역참조하여 c에 저장.</code></pre>
<p><code>b</code>는 <code>a</code>의 주소를 가리키고, <code>a</code>는 <code>q</code>를 참조하기 때문에 <code>c</code>는 <code>q</code>값을 가지게 된다. <strong>결과적으로 c = 0.</strong></p>
<pre><code class="language-cpp">int *z=0;</code></pre>
<p>정수형 포인트<code>z</code>를 <code>nullptr</code>로 초기화. <code>z</code>는 아무것도 가리키지 않음.</p>
<h3 id="다중-포인터">다중 포인터</h3>
<pre><code class="language-cpp">int **d= &amp;b;</code></pre>
<ul>
<li><code>b</code>의 주소를 저장하는 이중 포인터 <code>d</code>를 선언.</li>
<li><code>d</code>는 <code>b</code>라는 포인터를 가리키는 포인터<pre><code class="language-cpp">int ***e  = &amp;d; // e는 b라는 포인터를 가리키는 3중 포인터</code></pre>
<h3 id="다중-역참조">다중 역참조</h3>
<pre><code class="language-cpp">c= *(*d);  // d를 두 번 역참조하여 b가 가리키는 값을 가리킨다. (c=0)
c= *(*(*e)); // e를 세 번 역참조하여 b가 가리키는 값을 가리킨다. (c=0)</code></pre>
<h2 id="네임-스페이스">네임 스페이스</h2>
</li>
<li>이름(변수, 함수, 클래스 등)을 구분하기 위해 사용되는 &quot;공간&quot; 또는 &quot;범위</li>
<li>여러 코드나 라이브러리에서 같은 이름의 함수나 변수를 사용할 수 있는데, 그때 이름 충돌을 피하기 위해 필요하다.<pre><code class="language-cpp">namespace std {
  extern ostream cout;   // cout은 표준 출력 스트림(콘솔 출력)과 연결된 ostream 객체
}</code></pre>
</li>
</ul>
<h2 id="범위-지정-연산자-">범위 지정 연산자 (::)</h2>
<ul>
<li>특정 네임스페이스나 클래스 내에 정의된 함수 또는 변수를 명시적으로 지정할 때 사용</li>
<li><code>std::cout</code>: <code>::</code>는 <code>std</code>네임 스페이스에 속한 <code>cout</code> 객체를 명시</li>
</ul>
<h3 id="번외--연산자">번외 . 연산자</h3>
<p><code>.</code> : 객체의 멤버에 접근할 때 사용하는 연산자.</p>
<p>둘을 구분할 때에 </p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
namespace aaa{
class anda{
    public:
    void va()
    {

    }

    static va_s()
    {

    }
}
}

namespace std
{
    class anda
    {
        public:
        void va()
        {

        }

        static va_s()
        {

        }
    }

    bool getline(std::ifstream *f, std::string &amp; s)
    {
        int ** d= &amp;b;
        int **** e  = &amp;d;
        c= *(*d);
        c= *(*(*e));

        f-&gt;is_open();
        f[0]-&gt;is_open();
        *f.is_open();
    }
}

int main() {
    std::ifstream inFile(_T(&quot;example.txt&quot;));
    anda::va_s()
    anda a;
    bbb::test()

    a.va()
    if (inFile.is_open()) {
        std::string line;
        while (std::getline(&amp;inFile, line)) {
            std::cout &lt;&lt; line &lt;&lt; std::endl;
        }
        inFile(1);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 입문 4일차 - 파일 입출력]]></title>
            <link>https://velog.io/@syl_vana/C-%EC%B1%8C%EB%A6%B0%EC%A7%80-4%EC%9D%BC%EC%B0%A8-%ED%8C%8C%EC%9D%BC-%EC%9E%85%EC%B6%9C%EB%A0%A5</link>
            <guid>https://velog.io/@syl_vana/C-%EC%B1%8C%EB%A6%B0%EC%A7%80-4%EC%9D%BC%EC%B0%A8-%ED%8C%8C%EC%9D%BC-%EC%9E%85%EC%B6%9C%EB%A0%A5</guid>
            <pubDate>Mon, 21 Oct 2024 05:54:53 GMT</pubDate>
            <description><![CDATA[<h1 id="파일-입출력-기본">파일 입출력 기본</h1>
<p>프로그램이 외부 파일에서 데이터를 읽고, 파일에 데이터를 쓰는 데에 사용된다.</p>
<ul>
<li><code>&lt;fstream&gt;</code> 헤더를 사용<h2 id="기본-파일-입출력">기본 파일 입출력:</h2>
</li>
<li><strong><code>ifstream</code></strong>: 파일에서 데이터를 읽을 때 사용.</li>
<li><strong><code>ofstream</code></strong>: 파일에 데이터를 쓸 때 사용.</li>
<li><strong><code>fstream</code></strong>: 파일에서 읽고 쓰는 기능을 모두 사용할 때 사용.</li>
</ul>
<h2 id="파일에-데이터-쓰기-ofstream">파일에 데이터 쓰기 (ofstream)</h2>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
#include &lt;fstream&gt;  // 파일 입출력을 위한 헤더 파일

int main() {
    std::ofstream outFile(&quot;example.txt&quot;);  // 파일을 열거나 생성

    if (outFile.is_open()) {  // 파일이 정상적으로 열렸는지 확인
        outFile &lt;&lt; &quot;Hello, World!&quot; &lt;&lt; std::endl;  // 파일에 데이터 쓰기
        outFile &lt;&lt; &quot;This is C++ file handling.&quot; &lt;&lt; std::endl;
        outFile.close();  // 파일을 닫음
    } else {
        std::cout &lt;&lt; &quot;Unable to open file for writing&quot; &lt;&lt; std::endl;
    }

    return 0;
}</code></pre>
<ul>
<li><strong><code>outFile(&quot;example.txt&quot;)</code></strong>: 파일이 없으면 생성되고, 파일이 있으면 덮어쓰기가 .</li>
<li><strong><code>outFile &lt;&lt; ...</code></strong>: 파일에 데이터를 기록하는 방식은 <code>std::cout</code>과 유사하게 작동해.</li>
<li><strong><code>outFile.close()</code></strong>: 파일을 열었으면, 작업이 끝난 후 반드시 닫아줘야 해.</li>
</ul>
<h3 id="파일에서-데이터-읽기-ifstream">파일에서 데이터 읽기 (ifstream)</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;fstream&gt;

int main() {
    std::ifstream inFile(&quot;example.txt&quot;);  // 파일을 열어 읽기 모드로

    if (inFile.is_open()) {
        std::string line;
        while (std::getline(inFile, line)) {  // 파일의 각 줄을 읽음
            std::cout &lt;&lt; line &lt;&lt; std::endl;   // 읽은 내용을 출력
        }
        inFile.close();  // 파일을 닫음
    } else {
        std::cout &lt;&lt; &quot;Unable to open file for reading&quot; &lt;&lt; std::endl;
    }

    return 0;
}</code></pre>
<ul>
<li><strong><code>std::getline</code></strong>: 파일의 내용을 한 줄씩 읽어들이는 함수.</li>
<li><strong><code>inFile.close()</code></strong>: 파일 읽기가 끝나면 파일을 닫아야 함.</li>
</ul>
<h3 id="파일-입출력-동시에-하기-fstream">파일 입출력 동시에 하기 (fstream)</h3>
<ul>
<li>파일을 읽고 쓸 수 있는 양방향 스트림.</li>
<li>파일을 열고, 읽고, 쓸 수 있다.</li>
</ul>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;fstream&gt;

int main() {
    std::fstream file(&quot;example.txt&quot;, std::ios::in | std::ios::out);

    if (file.is_open()) {
        file &lt;&lt; &quot;Writing some new data.&quot; &lt;&lt; std::endl;  // 파일에 쓰기

        file.seekg(0);  // 파일 포인터를 다시 처음으로 이동
        std::string line;
        while (std::getline(file, line)) {
            std::cout &lt;&lt; line &lt;&lt; std::endl;  // 파일에서 읽기
        }

        file.close();  // 파일 닫기
    } else {
        std::cout &lt;&lt; &quot;Unable to open file&quot; &lt;&lt; std::endl;
    }

    return 0;
}</code></pre>
<ul>
<li><strong><code>std::ios::in | std::ios::out</code></strong>: 읽기와 쓰기를 동시에 하기 위한 모드.</li>
<li><strong><code>seekg(0)</code></strong>: 파일 포인터를 처음으로 돌려서 파일을 다시 읽기 시작.</li>
</ul>
<h3 id="연습-문제">연습 문제</h3>
<ol>
<li><code>student.txt</code> 파일을 생성하고, 학생의 이름과 나이를 저장하는 프로그램을 작성해보자.</li>
<li><code>student.txt</code> 파일을 읽어서 파일의 내용을 출력하는 프로그램을 만들어보자.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[notion to pptx 변환 5분컷]]></title>
            <link>https://velog.io/@syl_vana/notion-to-pptx-%EB%B3%80%ED%99%98-5%EB%B6%84%EC%BB%B7</link>
            <guid>https://velog.io/@syl_vana/notion-to-pptx-%EB%B3%80%ED%99%98-5%EB%B6%84%EC%BB%B7</guid>
            <pubDate>Sat, 19 Oct 2024 15:09:16 GMT</pubDate>
            <description><![CDATA[<p>나는 노션에 익숙한데, 자료정리를 무조건 pptx로 저장하라는 회사 이해x임. 여러번 노션이나 pdf를 권유했지만 팀장님의 마음을 돌릴 순 없었다 ㅠ gpt로 계속 만져보다가 실패를 거듭한 후 찾은 최선의 방법을 공유한다.</p>
<ol>
<li>변환하고 싶은 노션 페이지를 html로 내보내기한다.</li>
<li>파이썬으로 txt 변환한다.<pre><code class="language-python"># 입력 HTML 파일 경로
title = &#39;cuda upgrade&#39;
</code></pre>
</li>
</ol>
<p>from bs4 import BeautifulSoup
from bs4.element import NavigableString</p>
<p>def parse_html_to_text(node, depth=0, ol_counters=None, current_heading_level=0):
    if ol_counters is None:
        ol_counters = {}
    result = &#39;&#39;</p>
<pre><code># 현재 노드가 h1, h2, h3인지 확인하여 들여쓰기 수준 결정
if node.name in [&#39;h1&#39;, &#39;h2&#39;, &#39;h3&#39;]:
    indent_level = 0  # 헤딩은 들여쓰기 없음
else:
    indent_level = current_heading_level + 1  # 추가 들여쓰기 적용

indent = &#39;\t&#39; * indent_level

# 헤딩 처리
if node.name in [&#39;h1&#39;, &#39;h2&#39;, &#39;h3&#39;]:
    # 헤딩 텍스트 출력
    text = node.get_text(strip=True)
    if text:
        result += f&quot;{text}\n&quot;  # 헤딩은 들여쓰기 없음
    # 현재 헤딩 레벨 갱신
    current_heading_level = int(node.name[1])
# 문단 처리
elif node.name == &#39;p&#39;:
    text = node.get_text(strip=True)
    if text:
        result += f&quot;{indent}{text}\n&quot;
# 순서 있는 목록 처리
elif node.name == &#39;ol&#39;:
    # 현재 깊이의 번호를 초기화하거나 start 속성 사용
    start = node.get(&#39;start&#39;)
    if start:
        counter = int(start)
    else:
        counter = 1
    ol_counters[depth] = counter

    for li in node.find_all(&#39;li&#39;, recursive=False):
        result += parse_html_to_text(li, depth, ol_counters, current_heading_level)
    # 목록 종료 시 카운터 삭제
    del ol_counters[depth]
# 순서 없는 목록 처리
elif node.name == &#39;ul&#39;:
    for li in node.find_all(&#39;li&#39;, recursive=False):
        result += parse_html_to_text(li, depth, ol_counters, current_heading_level)
# 목록 아이템 처리
elif node.name == &#39;li&#39;:
    # 목록 아이템의 들여쓰기는 헤딩 레벨과 깊이에 따라 결정
    item_indent_level = indent_level + depth
    item_indent = &#39;\t&#39; * item_indent_level

    if node.parent.name == &#39;ol&#39;:
        number = ol_counters.get(depth, 1)
        ol_counters[depth] = number + 1
        bullet = f&quot;{number}. &quot;
    else:
        bullet = &quot;- &quot;

    # 아이템의 텍스트 내용 추출
    item_text = &#39;&#39;
    for child in node.contents:
        if isinstance(child, NavigableString):
            item_text += child.strip()
        elif child.name not in [&#39;ol&#39;, &#39;ul&#39;]:
            item_text += child.get_text(strip=True)

    result += f&quot;{item_indent}{bullet}{item_text}\n&quot;

    # 자식 요소 재귀 처리
    for child in node.contents:
        if child.name in [&#39;ul&#39;, &#39;ol&#39;]:
            result += parse_html_to_text(child, depth + 1, ol_counters, current_heading_level)
# 코드 블록 처리
elif node.name == &#39;pre&#39;:
    code_text = node.get_text()
    lines = code_text.split(&#39;\n&#39;)
    for line in lines:
        if line.strip():  # 빈 줄은 무시
            result += f&quot;{indent}{line}\n&quot;
        else:
            result += &quot;\n&quot;
# 불필요한 요소 필터링
elif node.name in [&#39;figure&#39;, &#39;img&#39;, &#39;style&#39;, &#39;script&#39;]:
    pass  # 해당 요소는 무시
# 텍스트 노드 처리
elif isinstance(node, NavigableString):
    text = node.strip()
    if text:
        result += f&quot;{indent}{text}\n&quot;
# 기타 요소 처리
else:
    for child in node.children:
        result += parse_html_to_text(child, depth, ol_counters, current_heading_level)

return result</code></pre><h1 id="입력-html-파일-경로">입력 HTML 파일 경로</h1>
<p>input_file = title + &#39;.html&#39;</p>
<h1 id="출력-텍스트-파일-경로">출력 텍스트 파일 경로</h1>
<p>output_file = title + &#39;.txt&#39;</p>
<h1 id="html-파일-읽기">HTML 파일 읽기</h1>
<p>with open(input_file, &#39;r&#39;, encoding=&#39;utf-8&#39;) as f:
    html_content = f.read()</p>
<h1 id="beautifulsoup으로-파싱">BeautifulSoup으로 파싱</h1>
<p>soup = BeautifulSoup(html_content, &#39;html.parser&#39;)</p>
<h1 id="body-태그-선택">body 태그 선택</h1>
<p>body = soup.find(&#39;body&#39;)</p>
<h1 id="텍스트-변환-실행">텍스트 변환 실행</h1>
<p>text_output = parse_html_to_text(body)</p>
<h1 id="결과를-텍스트-파일로-저장">결과를 텍스트 파일로 저장</h1>
<p>with open(output_file, &#39;w&#39;, encoding=&#39;utf-8&#39;) as f:
    f.write(text_output)</p>
<p>print(f&quot;변환이 완료되었습니다. 결과는 &#39;{output_file}&#39;에 저장되었습니다.&quot;)</p>
<pre><code>
3. python으로 txt를 pptx로 변환한다.
```python
from pptx import Presentation
from pptx.util import Inches, Pt
import re

# 입력 텍스트 파일 경로
input_txt_file = output_file

# 출력 PPTX 파일 경로
output_pptx_file = title + &#39;.pptx&#39;

# 프레젠테이션 객체 생성
prs = Presentation()

# 슬라이드 크기를 와이드스크린(16:9)으로 설정
prs.slide_width = Inches(13.33)
prs.slide_height = Inches(7.5)

# 텍스트 파일 읽기
with open(input_txt_file, &#39;r&#39;, encoding=&#39;utf-8&#39;) as f:
    lines = f.readlines()

# 1. 첫 장에 제목 슬라이드 추가 (slide_layouts[0] 사용)
title_slide_layout = prs.slide_layouts[0]  # 제목 슬라이드 레이아웃
title_slide = prs.slides.add_slide(title_slide_layout)

# 첫 번째 줄을 제목으로 사용
first_line = lines[0].strip()  # 첫 번째 줄에서 개행 문자 제거

# 제목과 부제목 설정
title = title_slide.shapes.title
title.text = first_line  # 첫 번째 줄을 제목으로 설정

# 이후 슬라이드 레이아웃 선택 (제목 및 내용)
slide_layout = prs.slide_layouts[1]

current_slide = None
bullet_levels = []

for i, line in enumerate(lines):  
    stripped_line = line.strip(&#39;\n&#39;)
    # 들여쓰기 수준 계산
    indent_level = len(re.match(r&#39;^(\t*)&#39;, stripped_line).group(1))
    content = stripped_line.lstrip(&#39;\t&#39;)

    if i == 0:
        # 첫 번째 슬라이드는 이미 제목으로 사용했으므로 건너뜀
        continue

    # 헤딩인지 확인 (h1, h2, h3는 들여쓰기 없음)
    if indent_level == 0:
        # 새로운 슬라이드 생성
        current_slide = prs.slides.add_slide(slide_layout)
        title_placeholder = current_slide.shapes.title
        body_placeholder = current_slide.placeholders[1]
        tf = body_placeholder.text_frame
        tf.clear()

        # 두 번째 슬라이드의 제목도 첫 번째 줄을 사용
        title_placeholder.text = first_line if i == 1 else content
        bullet_levels = []
    else:
        if current_slide is None:
            # 슬라이드가 없으면 새로 생성
            current_slide = prs.slides.add_slide(slide_layout)
            body_placeholder = current_slide.placeholders[1]
            tf = body_placeholder.text_frame
            tf.clear()
        else:
            body_placeholder = current_slide.placeholders[1]
            tf = body_placeholder.text_frame

        # 리스트 아이템인지 확인
        bullet_match = re.match(r&#39;^(- |\d+\. )(.*)&#39;, content)
        if bullet_match:
            bullet_text = bullet_match.group(2)
            p = tf.add_paragraph()
            p.text = bullet_text
            p.level = indent_level - 1  # 들여쓰기 수준에 따라 bullet level 설정
        else:
            # 일반 텍스트
            p = tf.add_paragraph()
            p.text = content
            p.level = indent_level - 1  # 들여쓰기 수준에 따라 bullet level 설정

# 프레젠테이션 저장
prs.save(output_pptx_file)

print(f&quot;텍스트 파일이 &#39;{output_pptx_file}&#39;로 변환되었습니다.&quot;)</code></pre><ol start="4">
<li>power point에서 pptx 열고 회사 템플릿을 불러온다.</li>
</ol>
<p>코드 더 수정해야지... 아직 태그에 종속적인 코드다.</p>
<h3 id="개선점">개선점</h3>
<ol>
<li>코드 부분이 삭제됨</li>
<li>이미지 넣기 안됨</li>
<li>프로그램 (exe) 형식으로 만들기</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 입문 3일차 - 클래스, 생성자, 소멸자, 상속, 다형성, 추상 클래스, 인터페이스]]></title>
            <link>https://velog.io/@syl_vana/C-%EC%9E%85%EB%AC%B8-3%EC%9D%BC%EC%B0%A8</link>
            <guid>https://velog.io/@syl_vana/C-%EC%9E%85%EB%AC%B8-3%EC%9D%BC%EC%B0%A8</guid>
            <pubDate>Fri, 18 Oct 2024 04:06:43 GMT</pubDate>
            <description><![CDATA[<h1 id="클래스-class">클래스 Class</h1>
<p><code>class</code>: 클래스를 정의할 때 사용하는 키워드.
<code>public</code>: 외부에서 접근 가능한 멤버 지정. 
    - 클래스 내부에서만 사용할 멤버는 private으로 설정할 수 있다.</p>
<p><strong>멤버 함수</strong>: 클래스 내부에 포함된 함수. 데이터를 처리하는 로직을 포함    </p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

class Person {
public:
    std::string name;
    int age;
    char gender;

    // 멤버 함수
    void introduce() {
        std::cout &lt;&lt; &quot;Hello, my name is &quot; &lt;&lt; name &lt;&lt; &quot; and I am &quot; &lt;&lt; age &lt;&lt; &quot; years old.&quot; &lt;&lt; std::endl;
    }
};

int main() {
    // 클래스 객체 선언
    Person p1;

    // 객체 멤버 변수에 값 대입
    p1.name = &quot;Alice&quot;;
    p1.age = 30;
    p1.gender = &#39;F&#39;;

    // 멤버 함수 호출
    p1.introduce();

    return 0;
}</code></pre>
<h3 id="연습문제">연습문제</h3>
<ol>
<li>구조체를 사용해 학생 정보를 저장하고 출력하는 프로그램을 만들어보자. 학생의 이름, 나이, 학년을 저장하도록 하자.</li>
<li>클래스를 사용해 간단한 Car 클래스를 만들어보자. Car 클래스는 모델명, 연식, 그리고 출력을 담당하는 멤버 함수를 가지도록 해보자.</li>
</ol>
<pre><code class="language-cpp">// 1.
#include &lt;iostream&gt;
#include &lt;string&gt;  // 문자열을 사용하기 위해 포함

struct Student {
    std::string name;  // str -&gt; std::string
    int age;
    int grade;
};

int main() {
    Student s1;
    s1.name = &quot;Alice&quot;;
    s1.age = 16;
    s1.grade = 10;

    std::cout &lt;&lt; &quot;Name: &quot; &lt;&lt; s1.name &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;Age: &quot; &lt;&lt; s1.age &lt;&lt; std::endl;
    std::cout &lt;&lt; &quot;Grade: &quot; &lt;&lt; s1.grade &lt;&lt; std::endl;

    return 0;
}

// 2.
#include &lt;iostream&gt;
#include &lt;string&gt;

class Car {
public:
    std::string name;  // 모델명
    int age;  // 연식

    // 멤버 함수
    void introduce() {
        std::cout &lt;&lt; &quot;This car&#39;s name is &quot; &lt;&lt; name &lt;&lt; &quot; and it is &quot; &lt;&lt; age &lt;&lt; &quot; years old.&quot; &lt;&lt; std::endl;
    }
};

int main() {
    Car car1;
    car1.name = &quot;Toyota&quot;;
    car1.age = 5;

    car1.introduce();

    return 0;
}</code></pre>
<h1 id="생성자-constructor">생성자 (Constructor)</h1>
<p>객체가 생성될 때 자동으로 호출되는 함수.
객체의 멤버 변수를 초기화하는 역할. 클래스 이름과 동일한 이름을 가지고 반환형이 없다.</p>
<ul>
<li><p>파이썬에서 <code>__init__</code>과 같은 역할</p>
<pre><code class="language-cpp">class Person {
public:
  std::string name;
  int age;

  // 생성자
  Person() {
      name = &quot;Unknown&quot;;
      age = 0;
  }

  void introduce() {
      std::cout &lt;&lt; &quot;Hello, my name is &quot; &lt;&lt; name &lt;&lt; &quot; and I am &quot; &lt;&lt; age &lt;&lt; &quot; years old.&quot; &lt;&lt; std::endl;
  }
};
</code></pre>
</li>
</ul>
<p>int main() {
    Person p1;  // 생성자가 자동으로 호출되어 name과 age가 초기화됨
    p1.introduce();
    return 0;
}</p>
<pre><code># 매개변수를 받는 생성자

매개변수: 객체의 멤버 변수를 초기화하기 위한 값.
```ccp
class Person {
public:
    std::string name;
    int age;

    // 매개변수 생성자
    Person(std::string n, int a) {
        name = n;
        age = a;
    }

    void introduce() {
        std::cout &lt;&lt; &quot;Hello, my name is &quot; &lt;&lt; name &lt;&lt; &quot; and I am &quot; &lt;&lt; age &lt;&lt; &quot; years old.&quot; &lt;&lt; std::endl;
    }
};</code></pre><p>같은 기능 파이썬에서 구현:</p>
<pre><code class="language-python">class Person:
    # 매개변수 생성자
    def __init__(self, name, age):
        self.name = name  # 매개변수 name을 멤버 변수 self.name에 저장
        self.age = age    # 매개변수 age를 멤버 변수 self.age에 저장

    def introduce(self):
        print(f&quot;Hello, my name is {self.name} and I am {self.age} years old.&quot;)</code></pre>
<h1 id="소멸자-destructor">소멸자 (Destructor)</h1>
<ul>
<li><p>객체가 삭제될 때 자동으로 호출되는 함수</p>
</li>
<li><p>동적으로 할당된 메모리를 해제할 때 사용</p>
</li>
<li><p>클래스 이름 앞에 <code>~</code>를 붙여서 정의</p>
</li>
<li><p>반환형x, 매개변수x</p>
<pre><code class="language-cpp">class Person {
public:
  std::string name;

  // 생성자
  Person(std::string n) {
      name = n;
      std::cout &lt;&lt; name &lt;&lt; &quot; is created.&quot; &lt;&lt; std::endl;
  }

  // 소멸자
  ~Person() {
      std::cout &lt;&lt; name &lt;&lt; &quot; is destroyed.&quot; &lt;&lt; std::endl;
  }
};
</code></pre>
</li>
</ul>
<p>int main() {
    Person p1(&quot;Alice&quot;);
    // 프로그램이 끝날 때 p1의 소멸자가 자동으로 호출됨
    return 0;
}</p>
<pre><code>
## 연습 문제
1. 매개변수 생성자를 사용해 Student 클래스를 만들어보자. Student는 이름과 학년을 가지며, 학생의 정보를 출력하는 멤버 함수를 포함해야 해.
2. 소멸자를 사용해 Car 클래스에 소멸자가 호출될 때 &quot;Car is being destroyed&quot;라는 메시지를 출력하도록 만들어보자.

```cpp
#include &lt;iostream&gt;

class Student {
public:
    std::string name;
    int grade;

    // 매개변수 생성자
    Student(std::string n, int g) {
        name = n;
        grade = g;
    }

    // 멤버 함수
    void introduce() {
        std::cout &lt;&lt; &quot;Name: &quot; &lt;&lt; name &lt;&lt; &quot;, Grade: &quot; &lt;&lt; grade &lt;&lt; std::endl;
    }

    // 소멸자
    ~Student() {
        std::cout &lt;&lt; name &lt;&lt; &quot; is being destroyed.&quot; &lt;&lt; std::endl;
    }
};

int main() {
    // 객체 생성
    Student s1(&quot;Alice&quot;, 26);

    // 객체의 멤버 함수 호출
    s1.introduce();

    return 0;
}</code></pre><h1 id="상속">상속</h1>
<p>부모 클래스의 기능을 자식 클래스가 물려받아 확장하거나 수정할 수 있는 기능</p>
<h3 id="상속의-기본-구조">상속의 기본 구조</h3>
<pre><code class="language-cpp">class Parent {
public:
    void speak() {
        std::cout &lt;&lt; &quot;I am the parent.&quot; &lt;&lt; std::endl;
    }
};

class Child : public Parent {
public:
    void introduce() {
        std::cout &lt;&lt; &quot;I am the child.&quot; &lt;&lt; std::endl;
    }
};

int main() {
    Child c;
    c.speak();  // 부모 클래스의 함수 호출
    c.introduce();  // 자식 클래스의 함수 호출
    return 0;
}</code></pre>
<h1 id="다형성polymorphism">다형성(Polymorphism)</h1>
<ul>
<li>같은 함수를 여러 가지 방식으로 구현하는 능력
부모 클래스에서 정의된 함수를 자식 클래스가 재정의(override)할 수 있다.</li>
</ul>
<h2 id="virtual-함수-재정의">Virtual: 함수 재정의</h2>
<ul>
<li>부모 클래스 앞에 <code>virtual</code> 키워드를 붙이면, 자식 클래스에서 그 함수를 재정의할 수 있다.</li>
<li>재정의된 함수는 부모 클래스의 포인터나 참조를 통해 호출되더라도 자식 클래스의 동작을 따르게 된다.</li>
</ul>
<pre><code class="language-cpp">class Parent {
public:  // 외부에서 클래스의 멤버 변수나 함수에 접근하고 싶을 때 사용
    virtual void speak() {  // 가상 함수
        std::cout &lt;&lt; &quot;I am the parent.&quot; &lt;&lt; std::endl;
    }
};

class Child : public Parent {
public:
    void speak() override {  // 함수 재정의
        std::cout &lt;&lt; &quot;I am the child.&quot; &lt;&lt; std::endl;
    }
};

int main() {
    Parent* p = new Child();  // 부모 클래스 포인터가 자식 클래스를 가리킴.
    p-&gt;speak();  // 자식 클래스의 speak 함수가 호출됨

    delete p;  // 동적 메모리 해제
    return 0;
}</code></pre>
<h3 id="virtual과-override">virtual과 override</h3>
<p>만약 Child 클래스에서 함수를 재정의할 때 virtual과 override가 없다면?
부모 클래스의 포인터나 참조로 자식 객체를 가리킬 때 부모 클래스의 함수가 호출된다. 즉, 부모 클래스의 speak() 함수가 호출되고, 자식 클래스의 speak() 함수는 무시된다.</p>
<h3 id="포인터-사용-이유">포인터 사용 이유</h3>
<p>다형성을 사용하려면 부모 클래스의 포인터 또는 참조를 통해 자식 클래스 객체에 접근해야 한다.
포인터를 사용하지 않으면 정적 바인딩이 이뤄진다.
즉, 컴파일 타임에 어느 함수가 호출될지 결정된다. = 해당 클래스의 함수만 사용할 수 있고 재정의된 함수는 호출되지 않는다.</p>
<h3 id="동적-메모리-해제">동적 메모리 해제</h3>
<ul>
<li><code>new</code>로 동적 할당한 메모리는 반드시 <code>delete</code>로 해제해야 함.</li>
</ul>
<p><code>delete p;</code>가 없으면: 동적으로 할당된 메모리가 해제되지 않아 <strong>메모리 누수(memory leak)</strong> 가 발생 -&gt; 프로그램의 메모리 사용량이 계속 증가하여 시스템 성능이 저하</p>
<h2 id="연습-문제">연습 문제</h2>
<ol>
<li>상속을 사용해 Animal 클래스를 만들고, Dog와 Cat 클래스를 상속받아 각각 다른 소리를 내는 speak 함수를 구현해보자.</li>
<li>다형성을 사용해 Animal 클래스의 speak 함수를 Dog와 Cat에서 재정의하고, 부모 클래스 포인터를 통해 각각의 speak 함수가 호출되는 프로그램을 만들어보자.</li>
</ol>
<pre><code class="language-cpp">#include &lt;iostream&gt;

class Animal {
public:
    virtual void sound() {  // 가상 함수
        std::cout &lt;&lt; &quot;sound sample&quot; &lt;&lt; std::endl;
    }
};

class Dog: public Animal {
public:
    void sound() override {  // 함수 재정의
        std::cout &lt;&lt; &quot;Bark&quot; &lt;&lt; std::endl;
    }
};

class Cat: public Animal {
public:
    void sound() override {  // 함수 재정의
        std::cout &lt;&lt; &quot;Meow&quot; &lt;&lt; std::endl;
    }
};

int main() {
    Animal* p1 = new Dog;
    Animal* p2 = new Cat;

    // 함수 호출
    p1-&gt;sound();  // Dog의 sound 함수 호출
    p2-&gt;sound();  // Cat의 sound 함수 호출

    // 동적 메모리 해제
    delete p1;
    delete p2;

    return 0;
}</code></pre>
<h1 id="추상-클래스-abstract-class">추상 클래스 (Abstract Class)</h1>
<ul>
<li>한 개 이상의 순수 가상 함수를 포함하는 클래스</li>
<li>여러 클래스를 그룹화 하기 위해 생성한다.<h3 id="순수-가상-함수">순수 가상 함수</h3>
</li>
<li>함수 자체에 구현이 없는 가상 함수</li>
</ul>
<pre><code class="language-cpp">class Animal {
public:
    virtual void sound() = 0;  // 순수 가상 함수
};

class Dog : public Animal {
public:
    void sound() override {
        std::cout &lt;&lt; &quot;Bark&quot; &lt;&lt; std::endl;
    }
};

class Cat : public Animal {
public:
    void sound() override {
        std::cout &lt;&lt; &quot;Meow&quot; &lt;&lt; std::endl;
    }
};

int main() {
    Animal* p1 = new Dog;
    Animal* p2 = new Cat;

    p1-&gt;sound();  // Dog의 sound 호출
    p2-&gt;sound();  // Cat의 sound 호출

    delete p1;
    delete p2;

    return 0;
}</code></pre>
<h1 id="인터페이스-interface">인터페이스 (Interface)</h1>
<h2 id="인터페이스추상-클래스란">인터페이스(추상 클래스)란?</h2>
<p>공통된 기능을 정의하지만, 그 기능의 구체적인 구현은 포함하지 않는 클래스</p>
<h4 id="파이썬에서-추상-클래스를-사용한-인터페이스-예시">파이썬에서 추상 클래스를 사용한 인터페이스 예시:</h4>
<pre><code class="language-python">class Shape:
    def area(self):
        pass  # 공통 인터페이스 역할만, 실제 구현 없음

# Circle 클래스 (Shape 상속)
class Circle(Shape):
    def __init__(self, r):
        self.r = r

    def area(self):
        return 3.14 * self.r * self.r

# Rectangle 클래스 (Shape 상속)
class Rectangle(Shape):
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def area(self):
        return self.w * self.h

# 메인 함수
def main():
    # Rectangle 객체 생성
    rec = Rectangle(3, 4)
    print(&quot;Area of rectangle:&quot;, rec.area())

    # Circle 객체 생성
    circ = Circle(5)
    print(&quot;Area of circle:&quot;, circ.area())

if __name__ == &quot;__main__&quot;:
    main()</code></pre>
<p><strong>추상 메서드란?</strong></p>
<ul>
<li>인터페이스에서 선언된 메서드</li>
<li>구현이 없이 선언만 되어 있는 메서드</li>
<li>파이썬에서는 pass를 사용</li>
</ul>
<blockquote>
<p>위 코드에서 Shape 클래스는 추상 클래스입니다. area 메소드는 구현되어 있지 않고, 추상 메소드로 선언되어 있습니다. 따라서, 이 클래스를 직접 인스턴스화 할 수 없으며, 이 클래스를 상속받아 구현된 클래스를 사용해야 합니다.
Circle 클래스와 Rectangle 클래스는 Shape 클래스를 상속받아서 만들어진 구체적인 도형 클래스입니다. 이들 클래스는 Shape 클래스에서 정의한 move 메소드를 사용할 수 있고, area 메소드를 구체적으로 구현하여 각 도형의 면적을 계산할 수 있습니다.
이러한 추상화를 통해, 각 도형의 공통된 속성과 기능을 효율적으로 관리할 수 있고, 필요에 따라 도형 클래스를 상속받아서 새로운 도형을 추가할 수 있습니다.
C++에서는 인터페이스라는 개념이 별도로 존재하지는 않지만, 이를 구현하는 방법은 순수 가상 함수만을 포함하는 추상 클래스를 정의하는 방식이다. 인터페이스는 오직 함수의 선언만 포함하고, 구현은 자식 클래스에서 이루어지게끔 만든다.</p>
</blockquote>
<p><strong>cpp:</strong></p>
<pre><code class="language-cpp">class IShape {
public:
    virtual void draw() = 0;
    virtual double area() = 0;
};

class Circle : public IShape {
public:
    void draw() override {
        std::cout &lt;&lt; &quot;Drawing Circle&quot; &lt;&lt; std::endl;
    }

    double area() override {
        return 3.14 * radius * radius;
    }

private:
    double radius = 5.0;
};

class Rectangle : public IShape {
public:
    void draw() override {
        std::cout &lt;&lt; &quot;Drawing Rectangle&quot; &lt;&lt; std::endl;
    }

    double area() override {
        return width * height;
    }

private:
    double width = 4.0;
    double height = 6.0;
};

int main() {
    IShape* shape1 = new Circle;
    IShape* shape2 = new Rectangle;

    shape1-&gt;draw();
    std::cout &lt;&lt; &quot;Area: &quot; &lt;&lt; shape1-&gt;area() &lt;&lt; std::endl;

    shape2-&gt;draw();
    std::cout &lt;&lt; &quot;Area: &quot; &lt;&lt; shape2-&gt;area() &lt;&lt; std::endl;

    delete shape1;
    delete shape2;

    return 0;
}</code></pre>
<h3 id="추상-클래스를-직접-객체로-생성하면-에러가-뜬다">추상 클래스를 직접 객체로 생성하면 에러가 뜬다.</h3>
<pre><code class="language-cpp">#include &lt;iostream&gt;
class IShape {
public:
    virtual void draw() = 0;  // 순수 가상 함수
};

int main() {
    IShape shape;  // 에러 발생: 추상 클래스의 객체는 생성 불가능
    return 0;
}</code></pre>
<pre><code>// 에러 메세지:
main.cpp: In function ‘int main()’:
main.cpp:16:12: error: cannot declare variable ‘shape’ to be of abstract type ‘IShape’
   16 |     IShape shape;  // 에러 발생: 추상 클래스의 객체는 생성 불가능
      |            ^~~~~
</code></pre><h3 id="연습-문제-1">연습 문제</h3>
<ol>
<li>추상 클래스를 사용해 Vehicle 클래스를 만들고, 이를 상속받는 Car와 Bike 클래스를 정의해보자. 각 클래스는 move() 함수를 구현해야 해.</li>
<li>인터페이스를 사용해 Shape 인터페이스를 만들고, 이를 구현하는 Square와 Triangle 클래스를 만들어 각각의 넓이를 구하는 함수를 구현해보자.</li>
</ol>
<pre><code class="language-cpp">// 1번
#include &lt;iostream&gt;

class Vehicle {
public:
    virtual void move() = 0;
};

class Bike : public Vehicle {
public:
    void move() override { 
        std::cout &lt;&lt; &quot;beep beep&quot; &lt;&lt; std::endl;
    }
};

class Car : public Vehicle {
public:
    void move() override { 
        std::cout &lt;&lt; &quot;ding-ding&quot; &lt;&lt; std::endl;
    }
};

int main() {
    Vehicle* veh1 = new Car;
    Vehicle* veh2 = new Bike;

    std::cout &lt;&lt; &quot;Car sound: &quot;;
    veh1-&gt;move();  // Car의 move 함수 호출

    std::cout &lt;&lt; &quot;Bike sound: &quot;;
    veh2-&gt;move();  // Bike의 move 함수 호출

    delete veh1;
    delete veh2;

    return 0;
}

// 2번
#include &lt;iostream&gt;

// 추상 클래스 Shape
class Shape {
public:
    virtual double area(double l) = 0;  // 순수 가상 함수
};

// Square 클래스, Shape 상속
class Square : public Shape {
public:
    double area(double l) override {  // 함수 재정의
        return l * l;
    }
};

// Triangle 클래스, Shape 상속
class Triangle : public Shape {
public:
    double area(double l) override {  // 함수 재정의
        return (l * l) / 2;
    }
};

int main() {
    Shape* shape1 = new Square;
    Shape* shape2 = new Triangle;

    double l = 4.0;

    // Square의 area 함수 호출
    std::cout &lt;&lt; &quot;Square area: &quot; &lt;&lt; shape1-&gt;area(l) &lt;&lt; std::endl;

    // Triangle의 area 함수 호출
    std::cout &lt;&lt; &quot;Triangle area: &quot; &lt;&lt; shape2-&gt;area(l) &lt;&lt; std::endl;

    delete shape1;
    delete shape2;

    return 0;
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[UNet 구조]]></title>
            <link>https://velog.io/@syl_vana/UNet</link>
            <guid>https://velog.io/@syl_vana/UNet</guid>
            <pubDate>Tue, 15 Oct 2024 01:49:03 GMT</pubDate>
            <description><![CDATA[<h1 id="unet이란">UNet이란?</h1>
<ul>
<li>의료 영상 분할(Semantic Segmentation)을 위한 딥러닝 모델로 2015년에 개발됨.</li>
<li>오토인코더(autoencoder)와 같은 인코더-디코더(encoder-decoder) 기반 모델<ul>
<li>근데 이제 스킵 연결이 추가된..
<img src="https://velog.velcdn.com/images/syl_vana/post/88bef640-e3af-4ba5-84e6-4a6e56a8ab50/image.png" alt=""></li>
</ul>
</li>
</ul>
<h1 id="unet의-구조">UNet의 구조</h1>
<h2 id="개요">개요</h2>
<ul>
<li>인코딩(압축)과 디코딩(복원) 과정을 거치는 U자형 구조.</li>
<li>인코딩 단계에서 이미지를 점점 작게 만들면서 중요한 특징을 추출하고, 디코딩 단계에서 다시 그 특징을 기반으로 원래 이미지와 비슷하게 복원한다.</li>
</ul>
<p>채널 수, 이미지 크기의 변화를 이해하기 위해 gpt에게 물어봤음.</p>
<h3 id="forward-과정에서의-변화">Forward 과정에서의 변화</h3>
<ol>
<li><strong>입력 (<code>x</code>)</strong>: <ul>
<li><strong>채널 수</strong>: 3 (RGB 이미지)</li>
<li><strong>차원 수</strong>: 2차원 이미지 (너비 × 높이) <code>MaxPool2d</code>와 <code>Conv2d</code> 함수로 2차원 데이터 임을 짐작할 수 있음.</li>
<li><strong>특징 수</strong>: x</li>
<li><strong>이미지 크기</strong>: 입력 이미지 크기 (예: 256×256)</li>
</ul>
</li>
</ol>
<h3 id="인코딩-downsampling">인코딩 (Downsampling)</h3>
<ol start="2">
<li><p><strong>Conv1 (첫 번째 Conv Block)</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 3 → 64 (64개의 필터 사용)</li>
<li><strong>차원 수</strong>: 그대로 (2D 이미지)</li>
<li><strong>특징 수</strong>: 64개의 특징 맵이 생성됨.</li>
<li><strong>이미지 크기</strong>: 256×256 (입력 크기 유지)</li>
</ul>
</li>
<li><p><strong>MaxPool</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 그대로 (64)</li>
<li><strong>차원 수</strong>: 그대로</li>
<li><strong>이미지 크기</strong>: 256×256 → 128×128 (풀링으로 크기 절반 감소)</li>
</ul>
</li>
<li><p><strong>Conv2 (두 번째 Conv Block)</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 64 → 128 (128개의 필터 사용)</li>
<li><strong>차원 수</strong>: 그대로</li>
<li><strong>특징 수</strong>: 128개의 특징 맵이 생성됨.</li>
<li><strong>이미지 크기</strong>: 128×128 유지</li>
</ul>
</li>
<li><p><strong>MaxPool</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 그대로 (128)</li>
<li><strong>차원 수</strong>: 그대로</li>
<li><strong>이미지 크기</strong>: 128×128 → 64×64</li>
</ul>
</li>
<li><p><strong>Conv3 (세 번째 Conv Block)</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 128 → 256</li>
<li><strong>특징 수</strong>: 256개의 특징 맵 생성.</li>
<li><strong>이미지 크기</strong>: 64×64 유지</li>
</ul>
</li>
<li><p><strong>MaxPool</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 그대로 (256)</li>
<li><strong>이미지 크기</strong>: 64×64 → 32×32</li>
</ul>
</li>
<li><p><strong>Conv4 (네 번째 Conv Block)</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 256 → 512</li>
<li><strong>이미지 크기</strong>: 32×32 유지</li>
</ul>
</li>
</ol>
<h3 id="디코딩-upsampling">디코딩 (Upsampling)</h3>
<ol start="9">
<li><p><strong>Up4</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 512 → 256</li>
<li><strong>이미지 크기</strong>: 32×32 → 64×64 (업샘플링으로 이미지 크기 복원)</li>
</ul>
</li>
<li><p><strong>Concatenation</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 256 + 256 → 512 (업샘플된 것과 Conv3 결과를 결합)</li>
<li><strong>이미지 크기</strong>: 64×64 유지</li>
</ul>
</li>
<li><p><strong>Up_conv4</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 512 → 256</li>
<li><strong>이미지 크기</strong>: 64×64 유지</li>
</ul>
</li>
<li><p><strong>Up3</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 256 → 128</li>
<li><strong>이미지 크기</strong>: 64×64 → 128×128</li>
</ul>
</li>
<li><p><strong>Concatenation</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 128 + 128 → 256 (Conv2 결과와 결합)</li>
<li><strong>이미지 크기</strong>: 128×128 유지</li>
</ul>
</li>
<li><p><strong>Up_conv3</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 256 → 128</li>
<li><strong>이미지 크기</strong>: 128×128 유지</li>
</ul>
</li>
<li><p><strong>Up2</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 128 → 64</li>
<li><strong>이미지 크기</strong>: 128×128 → 256×256</li>
</ul>
</li>
<li><p><strong>Concatenation</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 64 + 64 → 128 (Conv1 결과와 결합)</li>
<li><strong>이미지 크기</strong>: 256×256 유지</li>
</ul>
</li>
<li><p><strong>Up_conv2</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 128 → 64</li>
<li><strong>이미지 크기</strong>: 256×256 유지</li>
</ul>
</li>
<li><p><strong>Conv_1x1</strong>:</p>
<ul>
<li><strong>채널 수</strong>: 64 → 1 (출력 채널)</li>
<li><strong>이미지 크기</strong>: 256×256 유지</li>
</ul>
</li>
</ol>
<h1 id="unet-코드-flow">UNet 코드 flow</h1>
<pre><code class="language-python">class U_Net(nn.Module):
    def __init__(self, img_ch=3, output_ch=1):
        super(U_Net, self).__init__()

        self.MaxPool = nn.MaxPool2d(kernel_size=2, stride=2)
        self.Conv1 = conv_block(ch_in=img_ch, ch_out=64)
        self.Conv2 = conv_block(ch_in=64, ch_out=128)
        self.Conv3 = conv_block(ch_in=128, ch_out=256)
        self.Conv4 = conv_block(ch_in=256, ch_out=512)
        self.Up4 = up_conv(ch_in=512, ch_out=256)
        self.Up_conv4 = conv_block(ch_in=512, ch_out=256)
        self.Up3 = up_conv(ch_in=256, ch_out=128)
        self.Up_conv3 = conv_block(ch_in=256, ch_out=128)
        self.Up2 = up_conv(ch_in=128, ch_out=64)
        self.Up_conv2 = conv_block(ch_in=128, ch_out=64)
        self.Conv_1x1 = nn.Conv2d(64, output_ch, kernel_size=1, stride=1, padding=0)

    def forward(self, x):
        # encoding path
        x1 = self.Conv1(x)
        x2 = self.MaxPool(x1)

        x2 = self.Conv2(x2)
        x3 = self.MaxPool(x2)

        x3 = self.Conv3(x3)
        x4 = self.MaxPool(x3)

        x4 = self.Conv4(x4)
        d4 = self.Up4(x4)
        d4 = torch.cat((x3, d4), dim=1)

        d4 = self.Up_conv4(d4)
        d3 = self.Up3(d4)
        d3 = torch.cat((x2, d3), dim=1)

        d3 = self.Up_conv3(d3)
        d2 = self.Up2(d3)
        d2 = torch.cat((x1, d2), dim=1)

        d2 = self.Up_conv2(d2)
        net = self.Conv_1x1(d2)
        return net</code></pre>
<p><code>conv_block</code>: 여러 개의 Convolution layer를 사용하는 블록
<code>up_conv</code>: 디코딩 단계에서 이미지 크기를 복원(업샘플링)</p>
<h2 id="mermaid-다이어그램">Mermaid 다이어그램</h2>
<img src=" https://velog.velcdn.com/images/syl_vana/post/5b997b20-f75f-4dbd-8391-04193dd028e8/image.png" width="30%">

<h2 id="con_block">con_block</h2>
<pre><code class="language-python">class conv_block(nn.Module):
    def __init__(self, ch_in, ch_out):
        super(conv_block, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1, bias=True),
            nn.LeakyReLU(inplace=True),
            nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1, bias=True),
            nn.LeakyReLU(inplace=True)
        )

    def forward(self, x):
        x = self.conv(x)
        return x</code></pre>
<h3 id="구조">구조</h3>
<ul>
<li>(<code>3x3 convolution layer</code> + <code>LeakyReLU 활성화 함수</code> )패턴이 2중으로 구성됨.</li>
</ul>
<h2 id="up_conv">up_conv</h2>
<pre><code class="language-python">class up_conv(nn.Module):
    def __init__(self, ch_in, ch_out):
        super(up_conv, self).__init__()
        self.up = nn.Sequential(
            nn.Upsample(scale_factor=2),
            nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1, bias=True),
            # nn.LeakyReLU(inplace=True)
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        x = self.up(x)
        return x</code></pre>
<h3 id="구조-1">구조</h3>
<ol>
<li><p>업샘플링
<code>nn.Upsample(scale_factor=2)</code>: 이미지의 크기를 2배로 확대</p>
<ul>
<li>원래 이미지가 64×64라면, 업샘플링 후 128×128가 된다.</li>
</ul>
</li>
<li><p>3x3 convolution layer 1개
<code>nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=1, padding=1)</code></p>
</li>
<li><p>ReLU 활성화 함수
<code>nn.ReLU(inplace=True)</code></p>
</li>
</ol>
<p><a href="https://pasus.tistory.com/204">이미지 참고</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[순전파, 역전파, 인코딩, 디코딩]]></title>
            <link>https://velog.io/@syl_vana/%EC%88%9C%EC%A0%84%ED%8C%8C-%EC%97%AD%EC%A0%84%ED%8C%8C-%EC%9D%B8%EC%BD%94%EB%94%A9-%EB%94%94%EC%BD%94%EB%94%A9</link>
            <guid>https://velog.io/@syl_vana/%EC%88%9C%EC%A0%84%ED%8C%8C-%EC%97%AD%EC%A0%84%ED%8C%8C-%EC%9D%B8%EC%BD%94%EB%94%A9-%EB%94%94%EC%BD%94%EB%94%A9</guid>
            <pubDate>Tue, 15 Oct 2024 01:22:55 GMT</pubDate>
            <description><![CDATA[<p>이번 회사에 입사 후 모델 구조를 이해하는데 저 4개의 개념이 너무 어려웠다. 머리론 알겠지만 가슴 속으론 이해하지 못했다. 직접 하드코딩 해보면서 깨달아야지 이해가 되는 돌머리 ㅠ
지금은 시간이 없으니까 빠르게 개념만 정리해보자. (GPT canvas 이용)</p>
<h2 id="인코딩-encoding">인코딩 (Encoding)</h2>
<ul>
<li>인코딩은 입력 데이터를 압축해서 중요한 특징만 남기는 과정</li>
<li>이미지를 인코딩 시, 이미지 전체를 다 사용하지 않고 중요한 부분만 추출 주로 데이터를 더 작은 공간에 표현하려고 사용<h2 id="디코딩-decoding">디코딩 (Decoding)</h2>
</li>
<li>인코딩된 데이터를 다시 원래의 형식으로 복원하는 과정.</li>
<li>주로 이미지를 복원하거나 텍스트를 다시 재생성할 때 사용.<h2 id="순전파-forward-propagation">순전파 (Forward Propagation)</h2>
</li>
<li>입력 데이터가 인공 신경망을 통과하면서 계산되는 과정</li>
<li>입력이 첫 번째 층에서 시작해 마지막 층까지 이동하면서 각 층에서 계산이 일어남. 이 과정에서 예측 결과를 얻는다.</li>
<li>네트워크가 데이터를 &#39;어떻게&#39; 처리하는지를 알려주는 과정.<h2 id="역전파-backpropagation">역전파 (Backpropagation)</h2>
</li>
<li>모델의 예측이 실제 답과 얼마나 차이가 나는지 계산한 후, 그 차이를 줄이기 위해 가중치를 조정하는 과정</li>
<li>순전파로 나온 예측과 실제 값 사이의 오류를 바탕으로, 신경망의 가중치를 뒤로 되돌아가면서 조금씩 수정하고, 모델이 점점 더 정확한 예측을 함.</li>
</ul>
<h2 id="정리">정리</h2>
<h4 id="인코딩-encoding-1">인코딩 (Encoding)</h4>
<p>입력 데이터 → 압축, 중요한 특징 추출, 작은 공간 표현.</p>
<h4 id="디코딩-decoding-1">디코딩 (Decoding)</h4>
<p>압축된 데이터 → 원래 형식으로 복원.</p>
<h4 id="순전파-forward-propagation-1">순전파 (Forward Propagation)</h4>
<p>입력 데이터 → 신경망 통과, 예측 결과 계산.</p>
<h4 id="역전파-backpropagation-1">역전파 (Backpropagation)</h4>
<p>예측값과 실제 값 차이 → 가중치 조정, 오류 수정</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Mermaid란 / GPT로 mermaid 코드 추출 / notion에 옮기기]]></title>
            <link>https://velog.io/@syl_vana/Mermaid%EB%9E%80-GPT%EB%A1%9C-mermaid-%EC%BD%94%EB%93%9C-%EC%B6%94%EC%B6%9C-notion%EC%97%90-%EC%98%AE%EA%B8%B0%EA%B8%B0</link>
            <guid>https://velog.io/@syl_vana/Mermaid%EB%9E%80-GPT%EB%A1%9C-mermaid-%EC%BD%94%EB%93%9C-%EC%B6%94%EC%B6%9C-notion%EC%97%90-%EC%98%AE%EA%B8%B0%EA%B8%B0</guid>
            <pubDate>Tue, 08 Oct 2024 06:00:51 GMT</pubDate>
            <description><![CDATA[<h2 id="mermaid란">Mermaid란?</h2>
<p>   Mermaid는 코드만으로 복잡한 다이어그램을 쉽게 만들 수 있는 강력한 도구다. Markdown과 같은 텍스트 기반 도구들과 결합하면 더욱 효과적으로 기술 문서와 다이어그램을 작성할 수 있다.
<img src="https://velog.velcdn.com/images/syl_vana/post/5143b9b7-1de1-4d22-9e94-1df9be063dfc/image.png" alt="">
위 사진은 <a href="https://mermaid.live/">https://mermaid.live/</a> 에서 코드의 흐름을 다이어그램으로 뽑은 결과다. </p>
<h2 id="gpt로-mermaid-코드-생성하기">GPT로 Mermaid 코드 생성하기</h2>
<blockquote>
<p>~~
위 코드의 진행 흐름을 mermaid.live에서 사용가능한 flowchart 코드로 작성해줘</p>
</blockquote>
<p>이렇게 프롬프트를 구성하면 된다.</p>
<h2 id="markdown에서-사용하기">Markdown에서 사용하기</h2>
<p>GitHub, GitLab, Notion과 같은 플랫폼에서 직접 Mermaid 코드를 사용해 다이어그램을 그릴 수 있다. </p>
<blockquote>
<p>```mermaid
flowchart TD
    A[Start] --&gt; B[Step 1]
    B --&gt; C[Step 2]
    C --&gt; D[End]
```</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/syl_vana/post/07554d73-646a-4800-aa45-9ee381c83ded/image.png" alt="">
노션에 mermaid를 바로 그려주는 기능이 있다. 코드 이해하기에 정말 도움이 된다. 모두모두 이용하시길~</p>
<p>사용 예시:
<img src="https://velog.velcdn.com/images/syl_vana/post/b7204f35-ee3f-40b9-8dd9-baa5e0be59b6/image.png" alt=""></p>
<p>참고: 
<a href="https://dawonny.tistory.com/437">https://dawonny.tistory.com/437</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[vscode에서 ssh로 서버 접속하는 방법]]></title>
            <link>https://velog.io/@syl_vana/vscode%EC%97%90%EC%84%9C-%EC%A0%91%EC%86%8D%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@syl_vana/vscode%EC%97%90%EC%84%9C-%EC%A0%91%EC%86%8D%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Tue, 08 Oct 2024 02:06:40 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>vscode 켠다.
<img src="https://velog.velcdn.com/images/syl_vana/post/e8160178-262c-4fe3-a8f4-572760ebf1fa/image.png" alt=""></p>
</li>
<li><p><code>ctrl</code> + <code>shift</code> + <code>p</code> 단축키 눌러서 <code>Remote-SSH: Connect to Host</code>를 클릭.
<img src="https://velog.velcdn.com/images/syl_vana/post/0812565b-9b95-4e54-be34-fd41f504aeca/image.png" alt=""></p>
</li>
<li><p>포트 번호 입력하고 enter</p>
</li>
<li><p>비밀번호 입력하고 enter</p>
</li>
<li><p><code>Ctrl</code>+<code>O</code> 로 오픈할 폴더 위치를 입력할 창을 열고 파일 경로를 입력!</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/syl_vana/post/65d973ed-dce2-4e70-abe3-a424e3dcce1a/image.png" alt="지금 내가 할 프로젝트 경로: /pemtron/RAID/work/yjhe/IC/IC">
6. 한번 더 비밀번호 입력</p>
<p>비밀번호 없이 접속하는 법 pdf 보고 빨리 적용시키자~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이미지 증강]]></title>
            <link>https://velog.io/@syl_vana/%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A6%9D%EA%B0%95</link>
            <guid>https://velog.io/@syl_vana/%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%A6%9D%EA%B0%95</guid>
            <pubDate>Mon, 07 Oct 2024 08:37:02 GMT</pubDate>
            <description><![CDATA[<h2 id="데이터-증강이란data-augmentation">데이터 증강이란(Data Augmentation)</h2>
<p>모델의 성능을 향상시키기 위해 <strong>기존 데이터를 변형하여 새로운 데이터를 생성하는 기법</strong>.
주로 딥러닝에서 <strong>모델의 일반화 성능</strong>을 높이고 <strong>과적합(overfitting)</strong>을 방지하기 위해 사용된다.</p>
<h3 id="1-데이터-증강의-필요성">1. <strong>데이터 증강의 필요성</strong></h3>
<ul>
<li>딥러닝 모델은 <strong>많은 데이터</strong>를 필요로 하지만 데이터 수가 부족할 때, 모델이 <strong>과적합</strong>하는 문제가 발생할 수 있다. <blockquote>
<p>과적합: 모델이 훈련 데이터에 너무 특화되어 새로운 데이터에 대한 성능이 떨어지는 현상</p>
</blockquote>
</li>
<li>이 문제를 해결하기 위해, <strong>데이터 증강</strong>을 사용한다. 기존 데이터를 조금씩 변형해서 더 많은 데이터를 생성하고, 모델이 더 다양한 상황을 학습할 수 있다.</li>
</ul>
<h3 id="2-대표적인-데이터-증강-방법">2. <strong>대표적인 데이터 증강 방법</strong></h3>
<h4 id="1-회전-rotation">1) <strong>회전 (Rotation)</strong></h4>
<pre><code class="language-python">angle_rand = random.choice([0, 90, 180, 270])</code></pre>
<ul>
<li>이미지 전체를 <strong>0도, 90도, 180도, 270도</strong> 중 하나의 각도로 무작위 회전.</li>
<li>회전을 통해 모델이 <strong>다양한 방향</strong>에서 이미지를 학습할 수 있게 함.</li>
<li>같은 이미지라도 회전된 버전을 학습함으로써, 객체의 방향에 관계없이 모델이 인식할 수 있도록 한다.</li>
</ul>
<h4 id="2-평행-이동-translation">2) <strong>평행 이동 (Translation)</strong></h4>
<pre><code class="language-python">trans_rand = [random.uniform(0, 0.3), random.uniform(0, 0.3)]</code></pre>
<ul>
<li>이미지를 <strong>x축과 y축 방향</strong>으로 무작위로 이동.</li>
<li>평행 이동은 객체가 이미지의 중앙에만 위치하는 것이 아니라, <strong>다양한 위치에서</strong>도 모델이 인식할 수 있도록 도와준다. </li>
<li>객체가 이미지의 한쪽 구석에 있어도 모델이 이를 인식하도록 하는데 유용.</li>
</ul>
<h4 id="3-크기-조정-scaling">3) <strong>크기 조정 (Scaling)</strong></h4>
<pre><code class="language-python">scale_rand = random.uniform(0.8, 1.2)</code></pre>
<ul>
<li>이미지를 <strong>0.8배에서 1.2배</strong> 크기로 조정.</li>
<li>이는 객체의 <strong>크기가 다른</strong> 상황을 모델이 학습할 수 있도록 도와준다.
예를 들어, 같은 물체라도 멀리 있을 때는 작고 가까이 있을 때는 크게 보일 수 있다. 이 변형을 통해 모델이 다양한 크기의 객체를 학습할 수 있게 된다.</li>
</ul>
<h4 id="4-수평수직-반전-horizontalvertical-flip">4) <strong>수평/수직 반전 (Horizontal/Vertical Flip)</strong></h4>
<pre><code class="language-python">hori_rand = random.choice([True, False])
verti_rand = random.choice([True, False])</code></pre>
<ul>
<li>이미지를 <strong>좌우로 뒤집거나</strong>(수평 반전) <strong>위아래로 뒤집는</strong>(수직 반전) 방식.</li>
<li>이런 반전 작업을 통해, 같은 이미지를 다양한 방향으로 모델이 학습할 수 있다.
예를 들어, 사람의 얼굴이 좌우 반전되거나 상하 반전된 경우에도 모델이 이를 인식할 수 있다.</li>
</ul>
<h4 id="5-색상-조정-color-jitter">5) <strong>색상 조정 (Color Jitter)</strong></h4>
<pre><code class="language-python">transforms.ColorJitter(
    brightness=random.random(),
    contrast=random.random(),
    saturation=random.random(),
    hue=random.random() / 2
)</code></pre>
<ul>
<li>이미지의 <strong>밝기, 대비, 채도, 색조</strong>를 무작위로 변경.</li>
<li>이 변형을 통해 모델이 <strong>조명 조건이 다른</strong> 다양한 환경에서 이미지를 학습할 수 있도록 도와준다.</li>
</ul>
<h3 id="3-데이터-증강의-효과">3. <strong>데이터 증강의 효과</strong></h3>
<ul>
<li><strong>데이터 다양성 증가</strong>: 데이터 증강을 통해 기존 데이터에서 여러 변형된 버전을 생성하므로, <strong>데이터셋의 크기를 늘리는 효과</strong>를 얻을 수 있다.</li>
<li><strong>과적합 방지</strong>: 데이터 증강으로 다양한 변형된 데이터를 학습하게 하여, 특정 데이터 패턴에 의존하는 모델을 방지하고 <strong>일반화 성능</strong>을 높일 수 있다.</li>
<li><strong>훈련 데이터 부족 해결</strong>: 데이터 수가 부족할 때, 데이터 증강을 통해 더 많은 데이터를 학습할 수 있다. 이는 모델 성능 향상에 큰 도움이 된다.</li>
</ul>
<h3 id="4-데이터-증강의-구현-예시">4. <strong>데이터 증강의 구현 예시</strong></h3>
<p><code>transforms.Compose</code>를 사용해 <strong>변형을 차례대로 묶어</strong> 적용한다. </p>
<ul>
<li><strong><code>Affine()</code></strong>을 통해 회전, 평행 이동, 크기 조정을 적용하고,</li>
<li><strong><code>RandomHorizontalFlip()</code></strong>과 <strong><code>RandomVerticalFlip()</code></strong>으로 좌우, 상하 반전을 무작위로 적용한다.</li>
<li><strong><code>ColorJitter()</code></strong>를 사용해 밝기와 색상 변화를 주고, <strong><code>ToTensor()</code></strong>로 이미지를 텐서로 변환해 모델에 입력할 수 있게 만들어.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[C++ 입문 2일차: 포인터를 이용한 함수 호출, 동적 메모리 할당]]></title>
            <link>https://velog.io/@syl_vana/C-%EC%9E%85%EB%AC%B8-2%EC%9D%BC%EC%B0%A8-%ED%8F%AC%EC%9D%B8%ED%84%B0%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C-%EB%8F%99%EC%A0%81-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%95%A0%EB%8B%B9</link>
            <guid>https://velog.io/@syl_vana/C-%EC%9E%85%EB%AC%B8-2%EC%9D%BC%EC%B0%A8-%ED%8F%AC%EC%9D%B8%ED%84%B0%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C-%EB%8F%99%EC%A0%81-%EB%A9%94%EB%AA%A8%EB%A6%AC-%ED%95%A0%EB%8B%B9</guid>
            <pubDate>Fri, 04 Oct 2024 07:12:55 GMT</pubDate>
            <description><![CDATA[<h2 id="1-포인터를-이용한-함수-호출-call-by-reference">1. 포인터를 이용한 함수 호출 (Call by Reference)</h2>
<p>함수를 호출할 때, 파이썬에서는 값만 넘겨줬지만, C++에서는 포인터를 사용해서 변수의 주소를 넘겨줄 수 있다. 이렇게 하면 함수가 변수를 직접 수정할 수 있다.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

void changeValue(int* ptr) {
    *ptr = 20;  // 포인터를 이용해 변수의 값을 수정
}

int main() {
    int a = 10;
    std::cout &lt;&lt; &quot;Before: &quot; &lt;&lt; a &lt;&lt; std::endl;

    changeValue(&amp;a);  // a의 주소를 함수에 넘김
    std::cout &lt;&lt; &quot;After: &quot; &lt;&lt; a &lt;&lt; std::endl;

    return 0;
}</code></pre>
<p><code>changeValue(&amp;a)</code>: 변수 a의 주소를 함수에 넘겨줌.
<code>*ptr = 20;</code>: 함수 안에서 포인터를 통해 변수 a의 값을 변경.</p>
<p>파이썬에서는 </p>
<pre><code class="language-python">a=10
a=20</code></pre>
<p>이렇게 덮어쓸 수 있었는데 c++은 상당히 복잡하다.</p>
<h2 id="2-동적-메모리-할당">2. 동적 메모리 할당</h2>
<p>C++에서는 동적 메모리 할당을 통해 런타임에 메모리를 할당하고 사용해야 한다.
동적 메모리는 프로그램 실행 중에 메모리가 필요할 때 할당하고, 다 사용하면 해제하는 방식으로 사용한다.
이를 통해 배열의 크기를 실행 중에 동적으로 정할 수 있다.</p>
<p>동적 메모리 할당의 문법:
<code>new</code>: 메모리를 동적으로 할당.
<code>delete</code>: 동적으로 할당한 메모리를 해제.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

int main() {
    // 동적 메모리 할당
    int* ptr = new int;  // 정수형 변수를 위한 메모리 할당
    *ptr = 10;  // 동적 메모리 공간에 값 할당
    std::cout &lt;&lt; &quot;Value: &quot; &lt;&lt; *ptr &lt;&lt; std::endl;

    // 메모리 해제
    delete ptr;  // 동적으로 할당된 메모리 해제

    return 0;
}</code></pre>
<p><strong>동적 배열 할당 예시</strong></p>
<pre><code class="language-cpp">#include &lt;iostream&gt;

int main() {
    int size;
    std::cout &lt;&lt; &quot;Enter the size of the array: &quot;;
    std::cin &gt;&gt; size;

    // 동적으로 배열 할당
    int* arr = new int[size];

    // 배열에 값 입력 및 출력
    for (int i = 0; i &lt; size; i++) {
        arr[i] = i * 2;
        std::cout &lt;&lt; arr[i] &lt;&lt; &quot; &quot;;
    }
    std::cout &lt;&lt; std::endl;

    // 배열 메모리 해제
    delete[] arr;  // 동적으로 할당된 배열 메모리 해제

    return 0;
}</code></pre>
<h4 id="연습-문제">연습 문제</h4>
<p>함수에서 포인터를 사용해 두 변수의 값을 서로 바꾸는 프로그램을 만들어보자.</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;


void swap(int* ptr1, int* ptr2) {
    int temp = *ptr1;  //*ptr1: ptr1가 가리키는 값
    *ptr1 = *ptr2;
    *ptr2 = temp;
}

int main() {
    int a = 10;
    int b = 20;

    std::cout &lt;&lt; &quot;Before swap: a = &quot; &lt;&lt; a &lt;&lt; &quot;, b = &quot; &lt;&lt; b &lt;&lt; std::endl;

    // swap 함수 호출해서 x와 y의 값 교환
    swap(&amp;a, &amp;b);

    std::cout &lt;&lt; &quot;After swap: a = &quot; &lt;&lt; a &lt;&lt; &quot;, b = &quot; &lt;&lt; b &lt;&lt; std::endl;

    return 0;   
}</code></pre>
<blockquote>
<h3 id="new를-굳이-사용하는-이유-동적-메모리-할당">new를 굳이 사용하는 이유: 동적 메모리 할당</h3>
<p>C++에서는 <code>new</code> 없이도 변수를 선언하고 사용할 수 있지만, <code>new</code>를 사용하는 이유는 <strong>메모리를 다루는 방식</strong>에 큰 차이가 있기 때문.
<code>new</code>는 <strong>동적 메모리</strong>를 할당할 때 사용된다. <strong>동적 메모리</strong>를 사용하면, 변수의 <strong>크기와 수명</strong>을 <strong>프로그래머가 마음대로 조절</strong>할 수 있다.
<strong>프로그램이 실행 중일 때 필요한 만큼</strong> 메모리를 만들고, 필요 없어지면 <strong>해제</strong>할 수 있다.</p>
<h4 id="예시-동적-메모리-할당">예시: 동적 메모리 할당</h4>
</blockquote>
<pre><code class="language-cpp">#include &lt;iostream&gt;
&gt;
int main() {
    int* ptr = new int;  // 동적 메모리 할당 (정수형 변수 1개)
    *ptr = 20;           // 동적으로 할당된 메모리 공간에 값 저장
    std::cout &lt;&lt; *ptr &lt;&lt; std::endl;  // 값 출력
    delete ptr;          // 동적 메모리 해제
    return 0;
}</code></pre>
<p>여기서 <code>int* ptr = new int;</code>는 <strong>실행 중에(런타임) 메모리를 직접 만들어</strong>서 사용할 수 있게 한다. 그리고 <code>delete ptr;</code>을 사용해서 <strong>수동으로 메모리를 해제</strong>해줘야 한다.</p>
<h3 id="3-정적-메모리와-동적-메모리의-차이">3. <strong>정적 메모리와 동적 메모리의 차이</strong></h3>
<table>
<thead>
<tr>
<th><strong>정적 메모리(스택 메모리)</strong></th>
<th><strong>동적 메모리(힙 메모리)</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong><code>new</code> 없이 선언한 변수</strong> (<code>int a;</code> 등)</td>
<td><strong><code>new</code>를 사용해 메모리를 할당</strong> (<code>int* p = new int;</code>)</td>
</tr>
<tr>
<td><strong>크기와 수명이 고정</strong>되어 있음</td>
<td><strong>크기와 수명을 실행 중에 동적으로 결정</strong>할 수 있음</td>
</tr>
<tr>
<td><strong>함수가 끝나면 자동으로 메모리 해제</strong></td>
<td><strong>직접 <code>delete</code>로 메모리를 해제</strong>해야 함</td>
</tr>
<tr>
<td><strong>프로그램 시작 시 자동 할당</strong></td>
<td><strong>실행 도중에 필요할 때 메모리 할당</strong></td>
</tr>
<tr>
<td>### 4. <strong>그럼 언제 <code>new</code>를 써야 할까?</strong></td>
<td></td>
</tr>
<tr>
<td>- 프로그램이 실행될 때 <strong>얼마나 많은 메모리가 필요한지 모를 때</strong>!</td>
<td></td>
</tr>
<tr>
<td>- 배열의 크기나 객체의 수가 <strong>실행 중에 결정</strong>되어야 할 때.</td>
<td></td>
</tr>
<tr>
<td>- <strong>동적으로 할당된 메모리를 다른 함수나 클래스와 공유</strong>해야 할 때.</td>
<td></td>
</tr>
<tr>
<td>#### 예시: 동적 배열의 크기를 런타임에 결정하기</td>
<td></td>
</tr>
<tr>
<td>아래 코드에서 <code>new</code>를 사용한 이유가 바로 <strong>배열의 크기를 런타임에 결정할 때</strong>다:</td>
<td></td>
</tr>
<tr>
<td>```cpp</td>
<td></td>
</tr>
<tr>
<td>#include <iostream></td>
<td></td>
</tr>
<tr>
<td>int main() {</td>
<td></td>
</tr>
<tr>
<td>int size;</td>
<td></td>
</tr>
<tr>
<td>std::cout &lt;&lt; &quot;Enter the size of the array: &quot;;</td>
<td></td>
</tr>
<tr>
<td>std::cin &gt;&gt; size;</td>
<td></td>
</tr>
<tr>
<td>// 크기가 사용자가 입력한 값인 동적 배열 할당</td>
<td></td>
</tr>
<tr>
<td>int* arr = new int[size];</td>
<td></td>
</tr>
<tr>
<td>for (int i = 0; i &lt; size; i++) {</td>
<td></td>
</tr>
<tr>
<td>arr[i] = i * 2;  // 배열 초기화</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
<tr>
<td>delete[] arr;  // 메모리 해제</td>
<td></td>
</tr>
<tr>
<td>return 0;</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
<tr>
<td>```</td>
<td></td>
</tr>
<tr>
<td>- 이 코드에서는 <code>size</code>라는 값이 프로그램이 실행되는 <strong>도중</strong>에 결정된다. 만약 <code>new</code>를 사용하지 않고, 그냥 <code>int arr[size];</code>처럼 쓰면 <strong>컴파일 오류</strong>가 발생한다, 왜냐하면 <strong>정적 배열의 크기는 컴파일 타임에 고정</strong>되어야 하기 때문.</td>
<td></td>
</tr>
<tr>
<td>- 이처럼, <code>new</code>를 사용하면 프로그램이 실행 중일 때 <strong>배열의 크기</strong>를 동적으로 결정할 수 있다!</td>
<td></td>
</tr>
<tr>
<td>### 5. <strong><code>new</code> 없이 사용할 수 없는 경우: 동적 메모리의 필요성</strong></td>
<td></td>
</tr>
<tr>
<td>다음은 <code>new</code>를 사용해야만 원하는 동작을 할 수 있는 몇 가지 예시다:</td>
<td></td>
</tr>
<tr>
<td>#### 예시 1: 런타임에 배열의 크기를 결정하기</td>
<td></td>
</tr>
<tr>
<td>```cpp</td>
<td></td>
</tr>
<tr>
<td>#include <iostream></td>
<td></td>
</tr>
<tr>
<td>int main() {</td>
<td></td>
</tr>
<tr>
<td>int size;</td>
<td></td>
</tr>
<tr>
<td>std::cout &lt;&lt; &quot;Enter the size of the array: &quot;;</td>
<td></td>
</tr>
<tr>
<td>std::cin &gt;&gt; size;</td>
<td></td>
</tr>
<tr>
<td>// 사용자가 입력한 크기의 배열을 동적으로 할당</td>
<td></td>
</tr>
<tr>
<td>int* arr = new int[size];  // 크기 미리 모를 때 동적 할당</td>
<td></td>
</tr>
<tr>
<td>for (int i = 0; i &lt; size; i++) {</td>
<td></td>
</tr>
<tr>
<td>arr[i] = i + 1;</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
<tr>
<td>// 동적으로 할당한 배열을 해제</td>
<td></td>
</tr>
<tr>
<td>delete[] arr;</td>
<td></td>
</tr>
<tr>
<td>return 0;</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
<tr>
<td>```</td>
<td></td>
</tr>
<tr>
<td>#### 예시 2: 함수 내부에서 메모리를 할당하여 반환하기</td>
<td></td>
</tr>
<tr>
<td>```cpp</td>
<td></td>
</tr>
<tr>
<td>#include <iostream></td>
<td></td>
</tr>
<tr>
<td>int* createArray(int size) {</td>
<td></td>
</tr>
<tr>
<td>int* arr = new int[size];  // 함수 내부에서 배열 동적 할당</td>
<td></td>
</tr>
<tr>
<td>for (int i = 0; i &lt; size; i++) {</td>
<td></td>
</tr>
<tr>
<td>arr[i] = i + 1;</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
<tr>
<td>return arr;  // 동적 배열을 반환</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
<tr>
<td>int main() {</td>
<td></td>
</tr>
<tr>
<td>int size = 5;</td>
<td></td>
</tr>
<tr>
<td>int* myArray = createArray(size);</td>
<td></td>
</tr>
<tr>
<td>for (int i = 0; i &lt; size; i++) {</td>
<td></td>
</tr>
<tr>
<td>std::cout &lt;&lt; myArray[i] &lt;&lt; &quot; &quot;;  // 동적 배열의 요소 출력</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
<tr>
<td>delete[] myArray;  // 동적 배열 해제</td>
<td></td>
</tr>
<tr>
<td>return 0;</td>
<td></td>
</tr>
<tr>
<td>}</td>
<td></td>
</tr>
<tr>
<td>```</td>
<td></td>
</tr>
<tr>
<td>- 이 예시에서 <code>int* arr</code>은 함수 내부에서 <strong>동적 배열</strong>을 할당하고, 이 배열을 <strong>함수 밖으로 반환</strong>한다. 이렇게 <strong>동적 메모리</strong>를 사용하면, 함수가 끝난 후에도 메모리가 유지되기 때문에 <strong>반환 후에도 메모리를 사용할 수 있다</strong>.</td>
<td></td>
</tr>
<tr>
<td>- 만약 <code>new</code>를 사용하지 않고 함수 안에서 <code>int arr[10];</code>처럼 선언하면, 함수가 끝날 때 <strong>배열이 사라져</strong>버려서 제대로 값을 사용할 수 없게 된다.</td>
<td></td>
</tr>
</tbody></table>
]]></description>
        </item>
    </channel>
</rss>