<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>b_shy.log</title>
        <link>https://velog.io/</link>
        <description>.</description>
        <lastBuildDate>Tue, 26 Aug 2025 00:15:15 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. b_shy.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/su_hwan" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Self Pre-training with Masked Autoencoders for Medical Image Classification and Segmentation]]></title>
            <link>https://velog.io/@su_hwan/Self-Pre-training-with-Masked-Autoencoders-for-Medical-Image-Classification-and-Segmentation</link>
            <guid>https://velog.io/@su_hwan/Self-Pre-training-with-Masked-Autoencoders-for-Medical-Image-Classification-and-Segmentation</guid>
            <pubDate>Tue, 26 Aug 2025 00:15:15 GMT</pubDate>
            <description><![CDATA[<h1 id="1-abstract">1. ABSTRACT</h1>
<ol>
<li><p><strong>MAE (Masked Autoencoder)</strong>는 ViT(Vision Transformer) 사전학습에 효과적임.</p>
</li>
<li><p>MAE는 부분적으로 마스킹된 이미지를 입력으로 받아 전체 이미지를 복원 → 문맥 집약 능력(context aggregation ability)이 생김.
의료 영상은 해부학적 구조들이 긴밀히 연결되어 있어 문맥 집약 능력이 특히 중요.</p>
</li>
<li><p>ImageNet 규모의 의료 영상 데이터셋 부재 → 제안: Self Pre-training (목표 데이터셋(target dataset)의 학습용 데이터 위에서 ViT를 사전 학습).</p>
</li>
</ol>
<p>결과: Self Pre-training으로 X-ray 분류, CT 다장기 분할, MRI 뇌종양 분할 모두 성능 향상.</p>
<hr>
<p>MAE (Masked Autoencoder)
<img src="https://velog.velcdn.com/images/su_hwan/post/ab95040d-9fe8-43dd-aa81-a3eefac8cfd3/image.png" alt=""></p>
<ol>
<li><p>입력 이미지 (Input)
원본 이미지를 <strong>작은 패치(patch)</strong>로 나눕니다.
이 패치 중 일부는 마스킹 처리됩니다.</p>
</li>
<li><p>인코더 (Encoder)
마스킹되지 않은 패치들만 Transformer 기반 인코더에 입력.
인코더는 이 패치들을 <strong>잠재 표현(latent representation)</strong>으로 변환합니다.</p>
</li>
<li><p>디코더 (Decoder)
디코더는 <strong>전체 패치(마스킹된 부분 포함)</strong>를 입력받아 원래 이미지를 복원하려고 시도.</p>
</li>
<li><p>출력 (Target)
디코더가 복원한 이미지를 원본 이미지와 비교하여 <strong>복원 손실(reconstruction loss)</strong>을 계산.
이 과정을 통해 모델은 이미지의 전역적 문맥을 학습합니다.</p>
</li>
</ol>
<p>ViT(Vision Transformer)</p>
<ul>
<li>이미지 처리에 Transformer 구조를 적용한 모델</li>
</ul>
<ol>
<li>ViT는 사전학습이 중요하다</li>
</ol>
<ul>
<li>ViT는 CNN과 달리 로컬 패턴(예: 엣지, 텍스처)을 자연스럽게 학습하지 못함.</li>
<li>따라서 <strong>대규모 데이터에서 사전학습(pretraining)</strong>을 해야 좋은 성능을 냅니다.</li>
<li>사전학습 후, 다운스트림 작업(분류, 탐지 등)에 <strong>파인튜닝(finetuning)</strong>을 합니다.</li>
</ul>
<ol start="2">
<li>MAE는 ViT 사전학습에 적합한 이유</li>
</ol>
<ul>
<li>MAE는 ViT 구조를 그대로 사용합니다. (패치 분할 → Transformer 인코더)</li>
<li>학습 목표는 마스킹된 이미지 복원 → 이미지의 전역적(global) 문맥을 이해해야 함.</li>
<li>이 과정에서 ViT는 풍부한 시각적 표현을 학습하게 됩니다.
라벨이 필요 없으므로 대규모 비라벨 데이터로 학습 가능 → 강력한 사전학습 효과.</li>
</ul>
<ol start="3">
<li>효과</li>
</ol>
<ul>
<li>MAE로 사전학습한 ViT는 ImageNet 분류, 객체 탐지, 세그멘테이션 등에서 성능이 크게 향상.</li>
<li>특히 라벨이 부족한 상황에서 매우 유리.</li>
</ul>
<h1 id="2-introduction">2. INTRODUCTION</h1>
<p>분할(segmentation)의 경우, 목표 대상의 고유한 특징뿐 아니라 주변 조직의 특징 역시 특정 구조를 구분하는 데 도움이 된다. </p>
<p>예를 들어, 뇌종양이 존재하면 주변 미세환경에서도 부종, 뇌 조직의 구조적 변화, 혈관화 증가 등의 추가적인 변화가 발생한다. </p>
<p>이 논문에서는 <strong>문맥 정보 학습에 대한 엄격한 요구 조건을 부여하는 것이 딥러닝 기반 의료 영상 분석의 성능을 향상시킬 수 있다</strong>고 가정
<img src="https://velog.velcdn.com/images/su_hwan/post/84d0cfbd-0da8-4372-81b4-89dcf8c57157/image.png" alt=""></p>
<h3 id="mae-self-pre-training">MAE Self Pre-training</h3>
<ol>
<li>입력 이미지 분할</li>
</ol>
<ul>
<li>CT/MRI 이미지를 여러 개의 작은 패치로 나눔.</li>
<li>일부 패치는 mask 처리(회색 블록) 되어 encoder에는 입력되지 않음.</li>
</ul>
<ol start="2">
<li>ViT Encoder</li>
</ol>
<ul>
<li>보이는 패치만 입력으로 받아 전체 문맥(Context)을 학습.</li>
<li>Encoder는 이미지의 전반적인 구조적 관계를 이해하는 표현(representation)을 학습.</li>
</ul>
<ol start="3">
<li>Transformer Decoder</li>
</ol>
<ul>
<li>Encoder 출력 + mask token을 받아 마스킹된 영역을 복원(reconstruction).</li>
<li>이 과정에서 Encoder가 “부분만 보고 전체를 이해하는 능력”을 얻게 됨.</li>
</ul>
<h3 id="unetr-segmentation-단계">UNETR Segmentation 단계</h3>
<ol>
<li>사전학습된 ViT Encoder 활용</li>
</ol>
<ul>
<li>앞서 MAE로 학습한 Encoder weight를 segmentation 모델의 backbone으로 가져옴.</li>
<li>즉, Encoder는 이미 “의료 영상의 문맥적 특징”을 잘 파악할 수 있는 상태임.</li>
</ul>
<ol start="2">
<li>UNETR Decoder 연결</li>
</ol>
<ul>
<li>ViT Encoder의 여러 단계 출력(z3, z6, z9, z12)을 U-Net처럼 skip connection 구조에 연결.</li>
<li>Decoder가 이를 이용해 segmentation 맵을 생성.</li>
</ul>
<ol start="3">
<li>Fine-tuning</li>
</ol>
<ul>
<li>전체 네트워크(Encoder + Decoder)를 segmentation 데이터셋에서 파인튜닝.</li>
<li>이렇게 하면 초기부터 좋은 표현으로 시작하기 때문에 학습 효율과 성능이 개선됨.</li>
</ul>
<p>요약:
이 그림은 <strong>1단계(마스크 복원 Self Pre-training) → 2단계(Segmentation Fine-tuning)</strong>의 전체 파이프라인을 보여줍니다.</p>
<ul>
<li>Pre-training: MAE로 “문맥 기반 표현” 학습.</li>
<li>Downstream task: 해당 Encoder를 가져와 segmentation 또는 classification 수행.</li>
</ul>
<hr>
<p>정리</p>
<ul>
<li><p>의료 영상 분석은 주변 구조와의 관계까지 고려해야 정확도 향상 가능.
예: 흉부 X-ray에서 폐 질환 분류 시 심장·종격동의 변화도 참고 필요.</p>
</li>
<li><p>종양 분할 시 주변 부종, 구조 변형, 혈관화 증가 등이 단서가 됨.</p>
</li>
<li><p>최근 Self-Supervised Learning (SSL) 발전 → Masked Image Modeling (MIM) 전략이 ViT 학습에 효과적임.
MAE는 단순하면서도 효과적인 MIM 방법.</p>
</li>
<li><p>본 연구 목표
MAE 기반 self pre-training 패러다임을 의료 영상 분석에 제안 -&gt; MAE 사전학습을 다운스트림 과제와 동일한 데이터셋의 학습(train) 세트에서 수행 = Self pre-training
Self pre-training은 적절한 사전학습용 데이터를 구하기 어려운 다양한 상황에서 유용할 수 있으며, 사전학습과 파인튜닝 간의 데이터 불일치(domain discrepancy) 문제를 피할 수 있다.</p>
</li>
</ul>
<p>세 과제에 적용</p>
<ol>
<li>흉부 X-ray 질환 분류 (CXR14 데이터셋)</li>
<li>CT 다장기 분할 (BTCV 데이터셋)</li>
<li>MRI 뇌종양 분할 (BraTS, Medical Segmentation Decathlon)</li>
</ol>
<p>결과</p>
<ol>
<li>MAE self pre-training이 무작위 초기화(random initialization)보다 의료 영상 분류와 분할 성능을 크게 향상</li>
<li>모든 데이터셋에서 ImageNet 기반 사전학습보다 더 뛰어난 성능을 달성<h1 id="3-methodology">3. METHODOLOGY</h1>
</li>
</ol>
<p>Vision Transformer (ViT)</p>
<p>입력 이미지를 패치 단위로 나누어 sequence로 변환.</p>
<p>포지션 임베딩 추가.</p>
<p>Transformer block(Attention + MLP)으로 학습.</p>
<p>사전학습 시엔 sine-cosine positional embedding 사용.</p>
<p>MAE Self Pre-training</p>
<p>입력 이미지를 랜덤하게 마스킹 (일부 패치만 encoder 입력).</p>
<p>Encoder: 보이는 패치만으로 representation 학습.</p>
<p>Decoder: mask token + encoder feature로 마스킹된 영역 복원.</p>
<p>Loss: MSE, 단 마스크된 영역만 복원.</p>
<p>Downstream Tasks</p>
<p>Classification: ViT + Linear Classifier (multi-label → BCE loss).</p>
<p>Segmentation: UNETR(Transformer 기반 U-Net 구조) decoder 추가.</p>
<p>사전학습된 encoder weight로 초기화 후 fine-tuning.</p>
<h2 id="21-preliminary-vision-transformer-vit">2.1 Preliminary: Vision Transformer (ViT)</h2>
<p>Backbone으로 ViT 사용 (pre-training, downstream 공통).</p>
<p>구성 요소:</p>
<ol>
<li><p>Patch Embedding: 3D 볼륨 이미지를 작은 패치 단위로 나눠 시퀀스로 변환.
입력: 
𝑥∈𝑅𝐻×𝑊×𝐷×𝐶x∈R
H×W×D×C
출력: Flatten된 패치 시퀀스 → 선형 프로젝션으로 임베딩.</p>
</li>
<li><p>Position Embedding: 위치 정보를 유지하기 위해 임베딩 추가.
일반적으로 learnable 1D embedding 사용.
그러나 실험적으로 학습 가능한 1D embedding은 MAE 복원에 방해 → 대신 sine-cosine embedding 사용.
다운스트림 작업에서는 sine-cosine으로 초기화 후 학습.</p>
</li>
<li><p>Transformer Block: Multi-Head Self-Attention(MSA)와 MLP 블록 반복.</p>
</li>
</ol>
<h2 id="22-self-pre-training-with-masked-autoencoders-mae">2.2 Self Pre-training with Masked Autoencoders (MAE)</h2>
<p>Encoder (ViT): 입력 이미지를 패치로 분할 → 일부는 마스킹, 일부만 encoder 입력.</p>
<p>Encoder는 보이는 패치들로부터 전체 문맥적 표현을 학습.</p>
<p>Decoder (Transformer): Encoder 출력 + mask token 입력 → 원래 마스크된 영역 복원.</p>
<p>Decoder는 사전학습에만 사용, downstream에서는 제거됨.</p>
<p>Loss Function:</p>
<p>Reconstruction loss (MSE).</p>
<p>전체 이미지를 복원하는 게 아니라 mask된 영역만 예측 → 성능 더 좋음.</p>
<p>Normalized pixel/voxel 값이 raw 값보다 더 나은 학습 목표.</p>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/0bc4ca8e-4879-41cc-95b5-e5e9dba2615b/image.png" alt="">
구성</p>
<p>첫 번째 줄 (First row): 원본 이미지 (Original image).</p>
<p>Chest X-ray, Abdominal CT, Brain MRI (왼쪽 → 오른쪽).</p>
<p>두 번째 줄 (Second row): 마스킹된 이미지 (Masked image).</p>
<p>이미지 일부 패치가 회색/검은색으로 가려져 있음.</p>
<p>Encoder는 이 보이는 일부 패치만 입력으로 받아 학습.</p>
<p>세 번째 줄 (Third row): 복원된 이미지 (Reconstructed image).</p>
<p>Encoder+Decoder가 마스킹된 영역을 추론해 채운 결과.</p>
<p>실제 원본과는 약간 다르지만, 전체적인 구조는 잘 복원됨.</p>
<p>의미</p>
<p>MAE는 이미지의 75% 이상을 가려도 나머지 정보로 전체를 추론할 수 있다는 걸 보여줌.</p>
<p>복원 품질이 완벽하진 않지만, 중요한 건 Decoder 품질이 아니라 Encoder가 문맥적 표현을 학습하는 것.</p>
<p>의료 영상에서 문맥 이해가 중요한 이유:</p>
<p>X-ray → 폐 질환은 주변 심장/종격동 구조까지 보고 추론 가능.</p>
<p>CT → 장기의 위치 관계, 형태적 연속성을 학습.</p>
<p>MRI → 종양뿐만 아니라 주변 뇌 조직의 변화를 함께 파악.</p>
<p>✅ 요약:
이 그림은 MAE가 일부 패치만 보고 전체 이미지를 재구성하는 과정을 시각적으로 보여주며, 이를 통해 <strong>Encoder가 풍부한 문맥 표현(contextual representation)</strong>을 학습함을 증명합니다.</p>
<h2 id="23-architectures-for-downstream-tasks">2.3 Architectures for Downstream Tasks</h2>
<p>Classification:</p>
<p>ViT encoder + Linear classifier head.</p>
<p>Chest X-ray는 multi-label 분류이므로 BCE loss 사용.</p>
<p>Segmentation (UNETR 기반):</p>
<p>MAE 사전학습된 ViT encoder + 랜덤 초기화된 UNETR decoder.</p>
<p>구조: U-Net처럼 encoder의 여러 resolution feature를 decoder에 skip-connection.</p>
<p>Encoder representation을 reshape → spatial dimension 복원 → upsampling &amp; concat으로 세부 segmentation 맵 생성.</p>
<p>📌 요약 정리:</p>
<p>ViT를 backbone으로 쓰되, MAE를 통해 mask 복원 pre-training → 문맥 이해 능력 강화.</p>
<p>Pre-training: Encoder+Decoder(MAE), Loss는 MSE(masked 부분만).</p>
<p>Downstream: Encoder weight를 가져와서 분류(Linear) 또는 분할(UNETR) head 연결.
<img src="https://velog.velcdn.com/images/su_hwan/post/424b10e5-fa2c-4d66-a0c0-7221bac686af/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/42f448e8-684c-4a7b-a218-11b884535f0a/image.png" alt=""></p>
<h1 id="4-experiments-and-results">4. EXPERIMENTS AND RESULTS</h1>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/b1303c44-45d1-4a83-9b90-5638f969c4cc/image.png" alt="">
1️⃣ BTCV (Abdomen CT, 첫 두 줄)</p>
<p>첫 번째 줄:</p>
<p>UNETR에서는 잘못된 false positive segmentation(주황색 화살표)이 존재.</p>
<p>MAE pre-training을 거친 UNETR은 이 오류가 사라짐 → 더 정확한 장기 분할.</p>
<p>두 번째 줄:</p>
<p>UNETR은 위(stomach) 영역 분할이 불완전 (빨간 별표).</p>
<p>UNETR+MAE는 훨씬 더 완전하게 위를 분할.</p>
<p>👉 결론: MAE pre-training 덕분에 오탐 감소, 장기 분할 정확도 향상.</p>
<p>2️⃣ BraTS (Brain Tumor MRI, 마지막 두 줄)</p>
<p>세 번째 줄:</p>
<p>UNETR은 종양 주변(peripheral) 세부적인 종양 핵(yellow necrotic core, 흰색 화살표)을 놓침.</p>
<p>UNETR+MAE는 이 작은 부분까지 포착 → 더 정밀한 segmentation.</p>
<p>네 번째 줄:</p>
<p>UNETR은 괴사 핵(necrotic core) 분할이 거의 없음.</p>
<p>UNETR+MAE는 해당 영역을 더 잘 탐지.</p>
<p>👉 결론: MAE pre-training은 종양 내부 구조(코어, 주변부)까지 더 정확히 분할.</p>
<p>✅ 요약</p>
<p>BTCV (CT 다장기 분할):</p>
<p>False positive 감소.</p>
<p>위(stomach) 같은 작은 장기 분할 성능 향상.</p>
<p>BraTS (MRI 뇌종양 분할):</p>
<p>종양 내부 구조(괴사 코어, 주변부)까지 더 잘 포착.</p>
<p>기존 UNETR보다 더 세밀하고 안정적인 분할 성능.</p>
<p>즉, MAE self pre-training → 단순 수치 향상뿐 아니라, 실제 시각적 분할 품질도 개선됨을 보여주는 그림입니다.</p>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/2d4812d2-5d3a-46da-a99e-88f5059854b4/image.png" alt=""></p>
<p>왼쪽 블록 (Method 비교)</p>
<p>UNETR (기본 모델)</p>
<p>UNETR + ImageNet Pre-training</p>
<p>UNETR + MAE Self Pre-training</p>
<p>👉 세 가지 방법의 뇌종양 분할 성능 비교 (DSC↑ = Dice Score, HD95↓ = Hausdorff Distance 95).</p>
<p>오른쪽 블록 (Ablation Study)</p>
<p>Mask ratio (마스크 비율) &amp; Pre-training Epochs 변화에 따른 평균 DSC 결과.</p>
<p>Mask ratio = 입력 이미지에서 가리는 비율.</p>
<p>📊 주요 결과
1️⃣ Method 비교 (왼쪽)</p>
<p>UNETR 기본: Avg DSC = 77.40, HD95 = 7.78</p>
<p>UNETR + ImageNet: 약간 향상 → Avg DSC = 77.78, HD95 = 7.38</p>
<p>UNETR + MAE: 가장 높음 → Avg DSC = 78.91, HD95 = 7.22</p>
<p>👉 결론: MAE self pre-training이 ImageNet 기반 pre-training보다 효과적.</p>
<p>2️⃣ 세부 클래스별 (WT, ET, TC)</p>
<p>WT (Whole Tumor): MAE = 90.84 (최고)</p>
<p>ET (Enhancing Tumor): MAE = 63.88 (최고)</p>
<p>TC (Tumor Core): MAE = 82.00 (최고)</p>
<p>👉 모든 세부 종양 영역에서 MAE가 우세.</p>
<p>3️⃣ Ablation Study (오른쪽)</p>
<p>Mask Ratio 효과:</p>
<p>87.5% → Avg DSC 77.14 (낮음)</p>
<p>75% → Avg DSC 78.14 ~ 78.43 (보통)</p>
<p>50% → Avg DSC 78.42 (500 epoch), 83.2 (10k epoch)</p>
<p>25% → Avg DSC 78.71 (500 epoch), 83.18 (10k epoch)</p>
<p>12.5% → Avg DSC 78.91 (500 epoch), 83.52 (10k epoch, 최고 성능)</p>
<p>👉 의료 영상에서는 낮은 mask ratio (12.5%~25%)가 최적, 자연 이미지처럼 75% 이상 마스킹은 오히려 성능 저하.</p>
<p>Epoch 효과:</p>
<p>Pre-training을 오래 할수록 성능이 향상되지만, 40k epoch에서는 오히려 성능 하락 (과적합) 발생.</p>
<p>✅ 요약</p>
<p>MAE self pre-training &gt; ImageNet pre-training &gt; Random init</p>
<p>모든 종양 영역(Whole, Enhancing, Core)에서 MAE가 최고 성능 달성</p>
<p>낮은 mask ratio (12.5%<del>25%) + 충분한 pre-training epoch (</del>10k) 조합이 최적.</p>
<p><strong>너무 많은 epoch (40k)</strong>는 오히려 성능 저하 (overfitting).</p>
<p>Datasets</p>
<p>ChestX-ray14: 112,120장의 X-ray (multi-class 분류).</p>
<p>BTCV: 30명 환자의 복부 CT, 13개 장기 annotation (8개 장기 평가).</p>
<p>BraTS (MSD): 484명의 멀티모달 MRI, 종양 분할.</p>
<p>Implementation</p>
<p>Backbone: ViT-B/16</p>
<p>Optimizer: AdamW</p>
<p>Pre-training epoch: CXR14 (800), BTCV (10k), BraTS (500)</p>
<p>Augmentation: crop, flip, normalize</p>
<p>Results</p>
<p>ChestX-ray 분류</p>
<p>ViT scratch: mAUC 74.4%</p>
<p>ImageNet pretrained: 80.7%</p>
<p>MAE self pre-training: 81.5%</p>
<p>BTCV 다장기 CT 분할</p>
<p>UNETR baseline: DSC 78.8%</p>
<p>ImageNet pretrained: 79.7%</p>
<p>MAE self pre-training: 83.5% (큰 향상)</p>
<p>BraTS 뇌종양 MRI 분할</p>
<p>UNETR baseline: DSC 77.4%</p>
<p>ImageNet pretrained: 77.8%</p>
<p>MAE self pre-training: 78.9%</p>
<p>Ablation Study</p>
<p>마스크 비율: 자연 영상(MAE 논문)에서는 75% 이상이 최적이지만, 의료 영상에서는 <strong>낮은 비율(12.5%)</strong>이 더 성능 좋음.</p>
<p>Pre-training epoch: 적당히 길수록 좋지만 너무 길면 과적합.</p>
<h2 id="31-datasets-and-implementation-details">3.1 Datasets and Implementation Details</h2>
<p>ChestX-ray14 (CXR14):</p>
<p>11만 개 이상의 흉부 X-ray (32,717 환자).</p>
<p>Train/val 80%, test 20%.</p>
<p>Metric: multi-class AUC.</p>
<p>BTCV (Abdomen CT, 다장기 분할):</p>
<p>30명 CT, 13개 장기 라벨링.</p>
<p>18개 학습, 12개 검증.</p>
<p>Metric: 평균 Dice Similarity Coefficient (DSC), 95% Hausdorff Distance (HD).</p>
<p>BraTS (MSD Brain Tumor Segmentation):</p>
<p>484개 multi-modal MRI (FLAIR, T1w, T1-Gd, T2w).</p>
<p>라벨: edema, enhancing tumor, necrotic/non-enhancing core.</p>
<p>Metric: DSC, 95% HD.</p>
<p>Train 80%, Validation 20%.</p>
<p>Implementation:</p>
<p>PyTorch + MONAI.</p>
<p>Backbone: ViT-B/16.</p>
<p>Optimizer: AdamW.</p>
<p>Patch size: 16×16 (2D), 16×16×16 (3D).</p>
<p>Data Preprocessing:</p>
<p>CXR14: 히스토그램 균등화, 랜덤 crop (224×224).</p>
<p>BTCV: intensity clipping, normalization, 랜덤 crop (96³).</p>
<p>BraTS: instance-wise normalization, 랜덤 crop (128³).</p>
<p>MAE Pre-training:</p>
<p>LR=1.5e-4, weight decay=0.05, cosine schedule.</p>
<p>Epochs: 800 (CXR), 10k (BTCV), 500 (BraTS).</p>
<p>Batch size: 256 (CXR), 6 (BTCV, BraTS).</p>
<p>Fine-tuning:</p>
<p>Layer-wise LR decay=0.75, DropPath=0.1.</p>
<p>LR: 1e-3 (CXR), 8e-4 (BTCV), 4e-4 (BraTS).</p>
<p>Batch size: 동일 (256, 6, 6).</p>
<h2 id="32-results">3.2 Results</h2>
<p>(1) MAE Reconstruction</p>
<p>Fig. 2: 마스크 비율 75%.</p>
<p>결과: 원본 → 마스크 → 복원 순서.</p>
<p>복원된 visible patch는 다소 blur하지만, masked 영역은 잘 복원.</p>
<p>목표는 reconstruction 품질 자체가 아니라 문맥적 표현 학습.</p>
<p>(2) Abdomen Multi-organ Segmentation (BTCV)</p>
<p>Baseline UNETR: 78.8% DSC.</p>
<p>UNETR+ImageNet: 79.7%.</p>
<p>UNETR+MAE: 83.5% (큰 향상).</p>
<p>적은 데이터(N=30)에서도 효과적 → 소규모 의료 데이터에서 MAE의 잠재력 입증.</p>
<p>목표는 SOTA가 아니라 MAE self pre-training의 효과성 증명.</p>
<p>(3) Brain Tumor Segmentation (BraTS, MSD)</p>
<p>Baseline UNETR: 77.4% DSC / 7.78mm HD95.</p>
<p>UNETR+ImageNet: 77.8% / 7.38mm.</p>
<p>UNETR+MAE (mask=12.5%) → 78.9% / 7.22mm.</p>
<p>개선폭은 크진 않지만 안정적 성능 향상.</p>
<p>(4) Ablation Study (Mask Ratio &amp; Epochs)</p>
<p>Pre-training Epochs: 길게 학습하면 성능이 좋아지지만, 너무 오래하면 오히려 과적합으로 성능 하락.</p>
<p>Mask Ratio: 자연 이미지에서는 높은 비율(75% 이상)이 효과적이지만,</p>
<p>의료 영상 segmentation은 <strong>낮은 mask 비율 (12.5% ~ 25%)</strong>이 가장 좋은 성능.</p>
<p>이유: 의료 영상은 구조적·문맥적 정보가 제한적이므로 지나치게 많이 가리면 복원이 어려움.</p>
<p>(5) Lung Disease Classification (CXR14)</p>
<p>ViT scratch (100 epoch): 74.4% mAUC.</p>
<p>ViT scratch (400 epoch): 74.9%.</p>
<p>ViT + ImageNet pretrain: 80.7%.</p>
<p>ViT + MAE self pretrain: 81.5%.</p>
<p>결과: ViT는 사전학습 없이 성능이 낮지만, MAE self pre-training은 ImageNet pre-training보다도 우수.</p>
<p>✅ 요약:</p>
<p>BTCV CT segmentation → 큰 성능 향상 (78.8% → 83.5%).</p>
<p>BraTS MRI segmentation → 소폭 향상 (77.4% → 78.9%).</p>
<p>ChestX-ray14 classification → ImageNet 사전학습보다도 우수 (80.7% → 81.5%).</p>
<p>Ablation: 의료 영상은 낮은 mask ratio가 효과적.</p>
<h1 id="5-conclusion">5. CONCLUSION</h1>
<ul>
<li>MAE Self Pre-training은 의료 영상 분석에서 무작위 초기화, ImageNet 사전학습 모두를 능가하는 성능을 보임.
특히 ImageNet 사전학습보다 소규모 데이터셋 환경에서 효과적임을 입증.</li>
<li>2D (X-ray)뿐 아니라 3D (CT, MRI) 영상에도 적용 가능.</li>
<li>향후 예후 예측, 임상 결과 분석 같은 고차원적 의료 AI 작업으로 확장 가능.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[문제 9]]></title>
            <link>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-9</link>
            <guid>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-9</guid>
            <pubDate>Mon, 18 Aug 2025 09:43:22 GMT</pubDate>
            <description><![CDATA[<p>10진수 decimal을 입력받아 2진수로 변환하고 이를 문자열로 반환하는 solution() 함수를 구현하세요.</p>
<hr>
<p>권장 시간: 30분
권장 시간 복잡도: O(logN)
출제: 저자 출제</p>
<hr>
<p>제약 조건</p>
<ul>
<li>decimal은 1,000만 이하의 양의 정수</li>
</ul>
<hr>
<h1 id="풀이">풀이</h1>
<ol>
<li>10진수 N을 2로 나눈 나머지를 저장하고 N은 2로 나눔</li>
<li>몫이 0이 아니라면 나머지를 버리고 다시 1을 수행</li>
<li>모든 과정이 끝나고 1에서 저장한 수를 뒤부터 순서대로 가져와 붙이기</li>
</ol>
<pre><code class="language-py">def solution(decimal):
    stack = []
    while decimal &gt; 0:
        remainder = decimal % 2
        stack.append(str(remainder))
        decimal //= 2
    binary = &quot;&quot;
    while stack:
        binary += stack.pop()

    return binary</code></pre>
<hr>
<h1 id="시간-복잡도">시간 복잡도</h1>
<ul>
<li>N은 이진수로 변환할 숫자</li>
<li>N을 이진수로 변환하는 과정은 N이 1이 될 때까지 2로 게속 나누므로 연산횟수는 O(logN)</li>
<li>문자열의 +=연산자는 수행할 때마다 객체를 새로 생성. 따라서 시간 복잡도는 O((logN)^2)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[문제 8]]></title>
            <link>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-8</link>
            <guid>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-8</guid>
            <pubDate>Mon, 18 Aug 2025 09:08:08 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p>소괄호 &#39;(&#39; , &#39;)&#39;로 구성된 문자열 s가 주어집니다. 이 때 괄호의 짝이 맞는지 판별하는 solution() 함수를 구현하세요. 열린 괄호는 자신과 가장 가까운 닫힌 괄호를 만나면 상쇄되며, 이때 반드시 열린 괄호가 닫힌 괄호보다 먼저 와야합니다.
이런 식으로 괄호를 없애는 과정을 반복했을 때, 남아있는 괄호가 없으면 짝이 맞다고 할 수 있습니다.</p>
<hr>
<p>권장 시간: 30분
권장 시간 복잡도: O(N)
출제: 저자 출제</p>
<hr>
<p>제약 조건</p>
<ul>
<li>s의 길이는 최대 10만이며, 빈 문자열인 경우는 없음</li>
<li>괄호의 짝이 맞으면 True, 짝이 맞지 않으면 False를 반환</li>
</ul>
<hr>
<h1 id="풀이">풀이</h1>
<p>닫힌 괄호가 임의 위치의 열린 괄호와 상쇄되는 것이 아니라 닫힌 괄호가 나오기 바로 전 즉, 가장 가까운 <strong>(최근)</strong> 열린 괄호와 상쇄
&#39;최근&#39; 이라는 키워드를 보고 스택을 떠올리는 감각을 키우자</p>
<ol>
<li>문자열을 앞에서 하나씩 보며 열린 괄호가 나오면 푸시</li>
<li>닫힌 괄호가 나오면 팝 연산을 통해 최근 등장했던 열린 괄호 중 아직 상쇄하지 않은 열린 괄호를 확인하고 상쇄</li>
</ol>
<pre><code class="language-py">def solution(s):
    stack = []
    for c in s:
        if c == &quot;(&quot;:
            stack.append(c)
        elif c == &quot;)&quot;:
            if not stack:
                return False
            else:
                stack.pop()

    if stack:
        return False
    else:
        return True</code></pre>
<hr>
<h1 id="시간-복잡도">시간 복잡도</h1>
<ul>
<li>N은 s의 길이</li>
<li>s를 순회하며 괄호의 쌍을 확인하므로 시간 복잡도는 O(N)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[문제 7]]></title>
            <link>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-7</link>
            <guid>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-7</guid>
            <pubDate>Mon, 18 Aug 2025 08:31:07 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://school.programmers.co.kr/learn/courses/30/lessons/49994">https://school.programmers.co.kr/learn/courses/30/lessons/49994</a>
게임 캐릭터를 4가지 명령어를 통해 움직이려 합니다. 명령어는 다음과 같습니다.</p>
<p>U: 위쪽으로 한 칸 가기
D: 아래쪽으로 한 칸 가기
R: 오른쪽으로 한 칸 가기
L: 왼쪽으로 한 칸 가기</p>
<p>캐릭터는 좌표평면의 (0, 0) 위치에서 시작합니다. 좌표평면의 경계는 왼쪽 위(-5, 5), 왼쪽 아래(-5, -5), 오른쪽 위(5, 5), 오른쪽 아래(5, -5)로 이루어져 있습니다.</p>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/8f913568-3e77-45bb-9cec-b7fb0f044b2c/image.png" alt=""></p>
<p>예를 들어, &quot;ULURRDLLU&quot;로 명령했다면</p>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/45037fdb-6fe5-4100-ac63-4a413f3185dd/image.png" alt=""></p>
<p>1번 명령어부터 7번 명령어까지 다음과 같이 움직입니다.
<img src="https://velog.velcdn.com/images/su_hwan/post/71ec8815-4d07-45f1-904d-6162b9cf0727/image.png" alt=""></p>
<p>8번 명령어부터 9번 명령어까지 다음과 같이 움직입니다.
<img src="https://velog.velcdn.com/images/su_hwan/post/ccd77902-146b-407d-a56e-3318927c0e1c/image.png" alt=""></p>
<p>이때, 우리는 게임 캐릭터가 지나간 길 중 캐릭터가 처음 걸어본 길의 길이를 구하려고 합니다. 예를 들어 위의 예시에서 게임 캐릭터가 움직인 길이는 9이지만, 캐릭터가 처음 걸어본 길의 길이는 7이 됩니다. (8, 9번 명령어에서 움직인 길은 2, 3번 명령어에서 이미 거쳐 간 길입니다)</p>
<p>단, 좌표평면의 경계를 넘어가는 명령어는 무시합니다.</p>
<p>예를 들어, &quot;LULLLLLLU&quot;로 명령했다면</p>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/e56059b5-1bd9-4ee6-a9cf-9b5ea509d90f/image.png" alt=""></p>
<p>1번 명령어부터 6번 명령어대로 움직인 후, 7, 8번 명령어는 무시합니다. 다시 9번 명령어대로 움직입니다.
<img src="https://velog.velcdn.com/images/su_hwan/post/cd6136a6-a2ac-4c57-9c82-eaf3981f102d/image.png" alt=""></p>
<p>이때 캐릭터가 처음 걸어본 길의 길이는 7이 됩니다.</p>
<p>명령어가 매개변수 dirs로 주어질 때, 게임 캐릭터가 처음 걸어본 길의 길이를 구하여 return 하는 solution 함수를 완성해 주세요.</p>
<hr>
<p>권장 시간: 40분
권장 시간 복잡도: O(N)
출제: Summer/Winter Coding(~2018)</p>
<hr>
<p>제약 조건</p>
<ul>
<li>dirs는 string형으로 주어지며, &#39;U&#39;, &#39;D&#39;, &#39;R&#39;, &#39;L&#39; 이외에 문자는 주어지지 않음</li>
<li>dirs의 길이는 500 이하의 자연수</li>
</ul>
<hr>
<h1 id="풀이">풀이</h1>
<p>주의</p>
<ol>
<li>중복 경로는 최종 길이에 포함하지 않음</li>
<li>음수 좌표를 포함</li>
</ol>
<p>문제의 핵심은 원점에서 출발해 최종 경로의 길이를 구하는 것이므로 원점을 변경해도 길이만 구할 수 있다면 상관없음
따라서 원점을 (0, 0)에서 (5, 5)로 바꿔 음수 좌표 문제를 해결</p>
<hr>
<pre><code class="language-py">def is_vaild_move(nx, ny):
    return 0 &lt;= nx &lt; 11 and 0 &lt;= ny &lt; 11

def update_location(x, y, dir):
    if dir == &#39;U&#39;:
        nx, ny = x, y + 1
    elif dir == &#39;D&#39;:
        nx, ny = x, y - 1
    elif dir == &#39;L&#39;:
        nx, ny = x - 1, y
    elif dir == &#39;R&#39;:
        nx, ny == x + 1, y
    return nx, ny

def solution(dirs):
    x, y = 5, 5
    ans = set() # 겹치는 좌표는 1개로 처리
    for dir in dirs:
        nx, ny = update_location(x, y, dir)
        if not is_vaild_move(nx, ny): # 벗어난 좌표는 인정 안함
            continue
        # A에서 B로 간 경우 B -&gt; A 추가
        ans.add((x, y, nx, ny))
        ans.add((nx, ny, x, y))
        x, y = nx, ny # 좌표를 이동했으므로 업데이트
    return len(ans)/2</code></pre>
<p>A -&gt; B, B -&gt; A는 같은 경로로 취급
따라서 둘 다 추가하고 최종 길이를 2로 나눔</p>
<hr>
<h1 id="시간-복잡도">시간 복잡도</h1>
<ul>
<li>N은 dirs의 길이</li>
<li>dirs의 길이만큼 순회하므로 시간 복잡도는 O(N)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[문제 6]]></title>
            <link>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-6</link>
            <guid>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-6</guid>
            <pubDate>Sun, 17 Aug 2025 06:09:55 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://programmers.co.kr/learn/courses/30/lessons/42889">https://programmers.co.kr/learn/courses/30/lessons/42889</a>
슈퍼 게임 개발자 오렐리는 큰 고민에 빠졌다. 그녀가 만든 프랜즈 오천성이 대성공을 거뒀지만, 요즘 신규 사용자의 수가 급감한 것이다. 원인은 신규 사용자와 기존 사용자 사이에 스테이지 차이가 너무 큰 것이 문제였다.</p>
<p>이 문제를 어떻게 할까 고민 한 그녀는 동적으로 게임 시간을 늘려서 난이도를 조절하기로 했다. 역시 슈퍼 개발자라 대부분의 로직은 쉽게 구현했지만, 실패율을 구하는 부분에서 위기에 빠지고 말았다. 오렐리를 위해 실패율을 구하는 코드를 완성하라.</p>
<ul>
<li>실패율은 다음과 같이 정의한다.<ul>
<li>스테이지에 도달했으나 아직 클리어하지 못한 플레이어의 수 / 스테이지에 도달한 플레이어 수</li>
</ul>
</li>
</ul>
<p>전체 스테이지의 개수 N, 게임을 이용하는 사용자가 현재 멈춰있는 스테이지의 번호가 담긴 배열 stages가 매개변수로 주어질 때, 실패율이 높은 스테이지부터 내림차순으로 스테이지의 번호가 담겨있는 배열을 return 하도록 solution 함수를 완성하라.</p>
<hr>
<p>권장 시간: 60분
권장 시간 복잡도: O(M+NlonN)
출제: 2019 KAKAO BLIND RECRUITMENT</p>
<hr>
<p>제약 조건</p>
<ul>
<li>스테이지 개수 N은 1 이상 500 이하 자연수</li>
<li>stages의 길이는 1 이상 200,000 이하</li>
<li>stages에는 1 이상 N+1 이하의 자연수가 있음<ul>
<li>각 자연수는 사용자가 현재 도전 중인 스테이지 번호를 나타냄</li>
<li>단, N+1은 마지막 스테이지, 즉, N까지 클리어한 사용자를 나타냄</li>
</ul>
</li>
<li>만약 실패율이 같은 스테이지가 있다면 작은 번호의 스테이지가 먼저 오면 됌</li>
<li>스테이지에 도달한 유저가 없는 경우 해당 스테이지의 실패율은 0으로 정의</li>
</ul>
<p>입출력 예
N    | stages | result
5  | [2, 1, 2, 6, 2, 4, 3, 3] | [3,4,2,1,5]
4 |    [4,4,4,4,4] | [4,1,2,3]</p>
<p>입출력 예 설명</p>
<p>입출력 예 #1
1번 스테이지에는 총 8명의 사용자가 도전했으며, 이 중 1명의 사용자가 아직 클리어하지 못했다. 따라서 1번 스테이지의 실패율은 다음과 같다.</p>
<ul>
<li>1 번 스테이지 실패율 : 1/8</li>
</ul>
<p>2번 스테이지에는 총 7명의 사용자가 도전했으며, 이 중 3명의 사용자가 아직 클리어하지 못했다. 따라서 2번 스테이지의 실패율은 다음과 같다.</p>
<ul>
<li>2 번 스테이지 실패율 : 3/7</li>
</ul>
<p>마찬가지로 나머지 스테이지의 실패율은 다음과 같다.</p>
<ul>
<li>3 번 스테이지 실패율 : 2/4</li>
<li>4번 스테이지 실패율 : 1/2</li>
<li>5번 스테이지 실패율 : 0/1</li>
</ul>
<p>각 스테이지의 번호를 실패율의 내림차순으로 정렬하면 다음과 같다.</p>
<p>[3,4,2,1,5]
입출력 예 #2</p>
<p>모든 사용자가 마지막 스테이지에 있으므로 4번 스테이지의 실패율은 1이며 나머지 스테이지의 실패율은 0이다.</p>
<p>[4,1,2,3]</p>
<hr>
<h1 id="풀이">풀이</h1>
<pre><code class="language-py">def solution(N, stages):
    # 스테이지별 도전자 수
    challenger = [0] * (N + 2)
    for stage in stages:
        challenger[stage] += 1

    # 스테이지별 실패한 사용자 수
    fails = {}
    total = len(stages)

    # 실패율 계산
    for i in range(1, N+1):
        # 도전한 사람이 없으면 0
        if challenger[i] == 0:
            fails[i] = 0
        else:
            fails[i] = challenger[i] / total # 실패율
            total -= challenger[i] # 다음 스테이지 실패율 계산을 위한 다음 스테이지 인원 뺴기

    # 실패율이 높은 스테이지부터 내림차순으로 정렬
    result = sorted(fails, key=lambda x : fails[x], reverse=True)
    return result</code></pre>
<ol>
<li>stages에는 플레이어가 어느 스테이지에 있는지에 대한 정보가 있음
따라서 반복문을 따라 순회하며 challengers[stage] 값을 증가시키면 결국 challenger에 각 stage에 있는 플레이어의 수를 저장 가능</li>
</ol>
<ul>
<li>리스트의 크기를 N + 2로 정한 이유<ul>
<li>N번째 스테이지를 클리어한 사용자는 stage가 N + 1</li>
<li>배열의 인덱스는 0부터 시작하므로 N + 1의 데이터를 저장하려면 N + 2크기의 배열이 필요</li>
<li>0번째 인덱스를 사용하지 않기에 낭비라 생각되지만 값 자체를 인덱스로 활용가능해 매우 편리해짐</li>
</ul>
</li>
</ul>
<ol start="2">
<li>fails - 실패율 저장
total - 총 사용자의 수</li>
<li><strong>실패율을 구하는 로직</strong>
위에서 구한 challengers값을 활용</li>
<li>해당 스테이지에 있는 사용자가 0이라면 문제 정의에 의해 실패율은 0</li>
<li>해당 스테이지에 사용자가 있다면 실패율 공식을 적용하여 실패율을 구함
fails는 리스트가 아닌 딕셔너리
fails[i] = challenger[i] / total에서 
키 - i 
challenger[i] / total - 값</li>
<li>N번째 스테이지에 도달한 사용자의 수를 구하려면 N-1번째 스테이지에 있는 사용자 수를 빼면 됌</li>
<li>fails는 딕셔너리
키는 각 스테이지를 가리키는 숫자값은 실패율을 의미
값을 기준으로 키를 정렬해서 반환</li>
</ol>
<hr>
<h1 id="시간-복잡도">시간 복잡도</h1>
<ul>
<li>N은 스테이지 개수, M은 stages의 길이</li>
<li>challenger 배열을 초기화하고 각 스테이지 도전자 수를 계산할 때 시간 복잡도는 O(N + M)</li>
<li>이후 스테이지 별로 실패율을 계산하는데 필요한 시간 복잡도 O(N)</li>
<li>실패율을 기준으로 스테이지를 정렬할 때의 시간 복잡도 O(NlogN)</li>
<li>O(2N + M + NlogN)이므로 최종 시간 복잡도는 O(M+NlogN)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[문제 5]]></title>
            <link>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-5</link>
            <guid>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-5</guid>
            <pubDate>Thu, 14 Aug 2025 04:58:58 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://programmers.co.kr/learn/courses/30/lessons/12949">https://programmers.co.kr/learn/courses/30/lessons/12949</a>
2차원 행렬 arr1과 arr2를 입력받아, arr1에 arr2를 곱한 결과를 반환하는 함수, solution을 완성하시오.</p>
<hr>
<p>권장 시간: 40분
권장 시간 복잡도: O(N^3)
출제: 연습문제</p>
<hr>
<p>제약 조건</p>
<ul>
<li>행렬 arr1, arr2의 행과 열의 길이는 2 이상 100 이하입니다.</li>
<li>행렬 arr1, arr2의 원소는 -10 이상 20 이하인 자연수입니다.</li>
<li>곱할 수 있는 배열만 주어집니다.</li>
</ul>
<hr>
<h1 id="풀이">풀이</h1>
<ol>
<li><p>arr1, arr2의 행, 열 정보를 변수에 기록하고 행렬 정의를 활용해 결과 행렬을 저장할 수 있는 크기의 새 행렬을 만들어 0으로 초기화</p>
</li>
<li><p>결과 행렬의 크기는 (r1, c2)이므로 해당 크기의 리스트를 미리 만들어 0으로 초기화</p>
</li>
<li><p>행렬의 곱을 위한 반복문</p>
</li>
<li><p>첫 번째 행렬의 i번째 행과 두 번째 행렬의 j번째 열을 곱함</p>
<pre><code class="language-py">def solution(arr1, arr2):
 # 행렬 arr1과 arr2의 행과 열 수
 r1, c1 = len(arr1), len(arr1[0])
 r2, c2 = len(arr2), len(arr2[0])

 # 결과를 저장할 2차원 리스트 초기화
 ret = [[0] * c2 for _ in range(r1)]

 # 첫 번째 행렬 arr1의 각 행과 두 번째 행렬 arr2의 각 열에 대해
 for i in range(r1):
     for j in range(c2):
         # 두 행렬의 데이터를 곱해 결과 리스트에 더해줌
         for k in range(c1):
             ret[i][j] += arr1[i][k] * arr2[k][j]
 return ret</code></pre>
</li>
</ol>
<hr>
<h1 id="시간-복잡도">시간 복잡도</h1>
<ul>
<li>N은 행 또는 열의 길이</li>
<li>행과 열의 길이는 같음
arr1의 행, 열을 r1, c1
arr2의 행, 열을 r2, c2라 했을 때
r1<em>c1</em>c2만큼 연산</li>
<li>최종 시간 복잡도는 O(N^3)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[문제 4]]></title>
            <link>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-4</link>
            <guid>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-4</guid>
            <pubDate>Thu, 14 Aug 2025 04:16:11 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://programmers.co.kr/learn/courses/30/lessons/42840">https://programmers.co.kr/learn/courses/30/lessons/42840</a></p>
<p>수포자는 1번 문제부터 마지막 문제까지 다음과 같이 찍습니다.</p>
<p>1번 수포자가 찍는 방식: 1, 2, 3, 4, 5, 1, 2, 3, 4, 5, ...
2번 수포자가 찍는 방식: 2, 1, 2, 3, 2, 4, 2, 5, 2, 1, 2, 3, 2, 4, 2, 5, ...
3번 수포자가 찍는 방식: 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, 3, 3, 1, 1, 2, 2, 4, 4, 5, 5, ...</p>
<p>1번 문제부터 마지막 문제까지의 정답이 순서대로 들은 배열 answers가 주어졌을 때, 가장 많은 문제를 맞힌 사람이 누구인지 배열에 담아 return 하도록 solution 함수를 작성하시오.</p>
<hr>
<p>권장 시간: 30분
권장 시간 복잡도: O(N)
출제: 완전 탐색</p>
<hr>
<p>제약 조건</p>
<ul>
<li>시험은 최대 10,000 문제</li>
<li>문제의 정답은 1, 2, 3, 4, 5 중 하나</li>
<li>가장 높은 점수를 받은 사람이 여럿이라면 반환하는 값을 오름차순으로 정렬</li>
</ul>
<hr>
<h1 id="풀이">풀이</h1>
<ol>
<li>패턴을 미리 리스트에 저장(특정 값이나 패턴이 주어지면 하드 코딩하는게 좋음)</li>
<li>패턴과 답안을 비교해서 일치하는 개수를 지정하는 리스트를 선언</li>
<li>각 정답지에 패턴을 매치하여 맞춘 정답 개수를 구해 score에 저장</li>
<li>반복문에서 가장 높은 점수 저장</li>
<li>가장 큰 점수를 갖는 수포자들을 찾아 리스트에 담아 반환</li>
</ol>
<pre><code class="language-py">def solution(answers):
    patterns = [
        [1,2,3,4,5],
        [2,1,2,3,2,4,2,5],
        [3,3,1,1,2,2,4,4,5,5]
    ]
    # 점수 저장 리스트
    scores = [0] * 3

    # 패턴, 정답 일치 확인
    for i, answers in enumerate(answers):
        for j, patterns in enumerate(patterns):
            # 정답 패턴 길이가 답안 길이보다 긴 경우 정답 처음 데이터와 다시 비교
            if answers == patterns[i % len(patterns)]:
                scores[j] += 1

    # 가장 높은 점수 저장
    max_score = max(scores)

    # 가장 높은 점수를 가진 수포자들의 번호를 찾아 리스트에 저장
    highest_scores = []
    for i, score in enumerate(scores):
        if score == max_score:
            highest_scores.append(i + 1)
    return highest_scores</code></pre>
<p><strong>enumerate() 함수</strong>
인자로 넘어온 목록을 기준으로 인덱스와 원소를 차례대로 접근하게 해주는 반복자(iterator) 객체를 반환해주는 함수</p>
<hr>
<h1 id="시간-복잡도">시간 복잡도</h1>
<ul>
<li>N은 answers의 길이</li>
<li>각 수포자들의 패턴과 정답을 비교하는 부분은 O(N)</li>
<li>이 후 scores를 순회하며 가장 높은 수포자를 추가하는 연산은 O(1)</li>
<li>따라서 최종 시간 복잡도는 O(N)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[문제 3]]></title>
            <link>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-3</link>
            <guid>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-3</guid>
            <pubDate>Wed, 13 Aug 2025 06:53:32 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<p><a href="https://programmers.co.kr/learn/courses/30/lessons/68644">https://programmers.co.kr/learn/courses/30/lessons/68644</a></p>
<p>정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 2개의 수를 뽑아 더해 만들 수 있는 모든 수를 배열에 오름차순으로 담아 반환하는 solution() 함수를 완성하시오</p>
<hr>
<p>정답률: 68%
권장 시간: 30분
권장 시간 복잡도: O(N^2log(N^2))
출제: 월간 코드 챌린지</p>
<hr>
<p>제약 조건</p>
<ul>
<li>numbers의 길이는 2 이상 100 이하입니다.</li>
<li>numbers의 모든 수는 0 이상 100 이하입니다.</li>
</ul>
<hr>
<h1 id="풀이">풀이</h1>
<ol>
<li>배열에서 두 수를 선택하고 이에 대한 합을 구함</li>
<li>1에서 구한 수를 새로운 배열에 저장하고 중복값 제거</li>
<li>배열을 오름차순으로 정렬하고 반환</li>
</ol>
<pre><code class="language-py">def solution(numbers):
    answer = []
    # 두 수를 선택하는 모든 경우의 수를 반복문으로 구함
    for i in range(len(numbers)):
        for j in range(i + 1, len(numbers)):
            # 두 수를 더한 결과를 새로운 배열에 추가
            answer.append(numbers[i] + numbers[j])
    # 중복된 값을 제거하고, 오름차순 정렬
    answer = sorted(set(ret))
    return answer

print(solution([2, 1, 3, 4, 1])) # 반환값 : [2, 3, 4, 5, 6, 7]
print(solution([5, 0, 2, 7])) # 반환값 : [2, 5, 7, 9, 12]</code></pre>
<hr>
<h1 id="시간-복잡도">시간 복잡도</h1>
<ul>
<li>N은 numbers의 길이</li>
<li>이중 반복문으로 모든 원소들에 대해 두 수의 합을 구하는 연산의 시간 복잡도는 O(N^2)</li>
<li>이를 set으로 만들 때 시간 복잡도 O(N^2)</li>
<li>N^2의 데이터 정렬에는 O(N^2log(N^2))의 시간 복잡도가 필요</li>
</ul>
<p>N = 100이므로 시간 복잡도를 이렇게 해도 문제를 푸는데 큰 영향 x</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[문제 2]]></title>
            <link>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-2</link>
            <guid>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-2</guid>
            <pubDate>Wed, 13 Aug 2025 05:53:59 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<h2 id="정수-배열을-하나-받아-배열의-중복값을-제거하고-배열-데이터를-내림차순으로-정렬해서-반환하는-solution함수를-구현하시오">정수 배열을 하나 받아 배열의 중복값을 제거하고 배열 데이터를 내림차순으로 정렬해서 반환하는 solution()함수를 구현하시오</h2>
<p>권장 시간: 10분
권장 시간 복잡도: O(NlogN)
출제: 저자 출제</p>
<hr>
<p>제약 조건</p>
<ul>
<li>배열 길이는 2 이상 100,000 이하입니다.</li>
<li>각 배열의 데이터 값은 -100,000 이상 100,000 이하입니다.</li>
</ul>
<hr>
<h1 id="풀이">풀이</h1>
<ol>
<li>중복값 제거</li>
</ol>
<p>set()</p>
<ul>
<li>집합을 생성하는 내장함수(집합은 중복값을 허용하지 않음)</li>
<li>배열의 중복값 제거</li>
</ul>
<ol start="2">
<li>내림차순 정렬</li>
</ol>
<p>sort()
-&gt; reverse=True(내림차순)</p>
<pre><code class="language-py">def solution(lst):
    nlist = list(set(lst))
    nlist.sort(reverse=True)
    return nlist

print(solution([4, 2, 2, 1, 3, 4])) # 반환값 : [4, 3, 2, 1]
print(solution([2, 1, 1, 3, 2, 5, 4])) # 반환값 : [5, 4, 3, 2, 1]</code></pre>
<hr>
<h1 id="시간-복잡도">시간 복잡도</h1>
<ul>
<li>N은 lst의 길이</li>
<li>lst의 중복 원소를 제거하는데 걸리는 시간 복잡도는 O(N)</li>
<li>다시 정렬하는데 걸리는 시간 복잡도는 O(NlogN)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[문제 1]]></title>
            <link>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-1</link>
            <guid>https://velog.io/@su_hwan/%EB%AC%B8%EC%A0%9C-1</guid>
            <pubDate>Wed, 13 Aug 2025 05:36:27 GMT</pubDate>
            <description><![CDATA[<h1 id="문제">문제</h1>
<h2 id="정수-배열을-오름차순-정렬해서-반환하는-solution-함수를-완성하시오">정수 배열을 오름차순 정렬해서 반환하는 solution() 함수를 완성하시오</h2>
<p>권장 시간: 10분
권장 시간 복잡도: O(NlogN)
출제: 저자 출제</p>
<hr>
<p>제약 조건</p>
<ul>
<li>정수 배열의 길이는 2 이상 10^5 이하입니다.</li>
<li>정수 배열의 각 데이터 값은 -100,000 이상 100,000 이하입니다.</li>
</ul>
<p>-&gt; 데이터 개수가 초대 10^5이기 때문에 제한시간이 3초라면 O(N^2)알고리즘은 사용이 불가능
단순히 내림, 오름차순으로 정렬하면 틀림</p>
<hr>
<h1 id="풀이">풀이</h1>
<pre><code class="language-py">def solution(arr):
    arr.sort()
    return arr

print(solution([1,-5,2,4,3])) # 반환값 : [-5, 1, 2, 3, 4]
print(solution([2,1,1,3,2,5,4])) # 반환값 : [1, 1, 2, 2, 3, 4, 5]
print(solution([1,6,7])) # 반환값 : [1, 6, 7]</code></pre>
<p>sort() 메서드</p>
<ul>
<li>리스트를 오름차순으로 정렬해서 반환</li>
<li><strong>리스트 원본 자체의 값을 바꿈</strong></li>
</ul>
<pre><code class="language-py"># 원본 리스트 그대로 두기
def solution(arr):
    sorted_list = sorted(arr)
    return sorted_list</code></pre>
<p>sorted() 메서드</p>
<ul>
<li>원본을 건드리지 않고, 정렬된 새로운 리스트를 만들어서 반환</li>
</ul>
<hr>
<h1 id="시간-복잡도">시간 복잡도</h1>
<ul>
<li>N은 arr의 길이이므로 O(NlogN)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[A Simple Framework for Contrastive Learning of Visual Representations]]></title>
            <link>https://velog.io/@su_hwan/A-Simple-Framework-for-Contrastive-Learning-of-Visual-Representations</link>
            <guid>https://velog.io/@su_hwan/A-Simple-Framework-for-Contrastive-Learning-of-Visual-Representations</guid>
            <pubDate>Thu, 31 Jul 2025 05:40:52 GMT</pubDate>
            <description><![CDATA[<h1 id="0-background">0. Background</h1>
<ul>
<li>기존의 Supervised Learning은 양질의 label 정보를 충분히 가지고 있는 데이터를 사용해야만 최적의 model을 만들 수 있었음.</li>
<li>양질의 데이터 및 label 정보를 얻는 것은 시간과 돈이 많이 필요함. </li>
<li>이러한 문제를 극복하기 위해 데이터에서 얻을 수 있는 정보를 사용해서 자체적인 label을 얻기 위해 고안된 방법이 Self-Supervised Learning</li>
</ul>
<hr>
<ul>
<li>Self-Supervised Learning 은 딥러닝 분야에서 최근 가장 핫한 토픽 중 하나</li>
<li>Self-Supervised Learning은 그 자체로 분류 및 예측 task의 정확도를 높이는 것이 목적이 아님. <strong>Backbone 모델</strong>에서 더 좋은 <strong>representation(feature)</strong>을 학습하고 추출할 수 있도록 하여 차후 <strong>task</strong>에서 성능을 올리는 것이 목적. </li>
<li>이를 위한 초기 연구들은 보통 Jigsaw Puzzle나 Rotation 등의 간단한 <strong>Pretext Task</strong>를 직접 선정하여 backbone 모델을 학습시키는 것이었음.</li>
<li>최근 Self-Supervised Learning 연구 동향은 Pretext Task 없이 <strong>Contrastive Learning</strong>을 통해 학습하는 것</li>
</ul>
<hr>
<p><strong>Backbone 모델</strong></p>
<ul>
<li>이미지의 feature를 추출하는 기본 신경망 구조</li>
<li>SimCLR에서는 ResNet-50이 backbone으로 사용</li>
</ul>
<p><strong>representation(feature)</strong></p>
<ul>
<li>backbone (예: ResNet)으로부터 나온 feature를 representation이라고도 부름</li>
<li>SimCLR에서는 실제로 feature를 z가 아닌 h로 사용</li>
</ul>
<p><strong>task</strong>
모델이 해결하려는 실제 문제</p>
<p><strong>pretext task</strong>
사전 학습용 인위적인 문제</p>
<p><strong>Contrastive Learning(대조 학습)</strong></p>
<ul>
<li><p>학습 데이터의 쌍(pair) 사이의 관계를 학습해서 feature space 상의 구조를 정렬하는 방식</p>
</li>
<li><p>입력 데이터 쌍 (x₁, x₂)을 만들어서
같은 클래스를 나타내는 쌍이면 → 가까워지게
다른 클래스를 나타내는 쌍이면 → 멀어지게</p>
</li>
</ul>
<h1 id="1-introduction">1. Introduction</h1>
<ul>
<li><p>Representation을 학습한다는 것은 쉽게 말해, 인간의 간섭 없이 모델이 스스로 task에 적합한 feature들을 알아낸다는 것</p>
</li>
<li><p>학습과정에서 data augmentation과의 차이를 학습하고 <strong>embedding</strong> layer를 깊게 쌓음으로써 유사도를 비교하는 공간인 feature space를 더 개선하여 image representation 학습 성능을 크게 향상 시키는 데에 기여</p>
</li>
<li><p>중요한 사항</p>
</li>
</ul>
<ol>
<li>간단한 몇 개의 Data augmentation 조합이 SimCLR 구조에서 중요한 역할을 함</li>
<li>Representation과 <strong>Contrastive loss</strong> 사이의 non-linear transformation이 representation 성능을 향상 -&gt; ResNet이 뽑은 feature (representation)를 바로 contrastive loss에 넣는 것보다,
한 번 비선형 함수(MLP 등)를 거쳐서 넣는 게 feature 성능이 더 좋다</li>
<li>Contrastive learning은 batch size와 training step(또는 epoch)이 클 때 효과적임</li>
</ol>
<hr>
<p><strong>embedding</strong>
원본 데이터를 수치화된 의미 공간으로 매핑하는 과정</p>
<p><strong>Contrastive loss</strong>
비슷한 쌍은 가깝게, 다른 쌍은 멀게” 학습하기 위한 손실 함수
SimCLR에서는 NT-Xent (Normalized Temperature-scaled Cross Entropy) Loss를 사용</p>
<h1 id="2-method">2. Method</h1>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/c99f463f-ddeb-421d-8dc8-108012dd3723/image.png" alt="">
SimCLR contrastive learning의 전체 framework를 나타내는 그림</p>
<p>T : Data augmentation 종류 (RandomResizeCrop, Random Color distortion, Gaussian Blur)
t,t′ : Random sampling된 Data augmentation 기법
xi,xj : Augmentation된 이미지
f(⋅) : Base Encoder (ResNet-50)
hi,hj : ResNet-50 output에서 Global Average Pooling(GAP) 된 vector</p>
<ul>
<li>feature map은 3차원 (7×7 공간, 2048 채널)이라서 classifier나 projection head에 넣기 어려움</li>
<li>GAP : 각 채널마다 공간 평균을 내서 1개의 숫자로 줄이는 연산
즉, [7×7×2048] → [1×1×2048] → 2048차원 벡터로 압축</li>
</ul>
<p>g(⋅) : non-linear representation을 적용하는 Projection head (Two-layer MLP)
zi,zj : Projection head 통과 후 생성된 vector
Contrastive loss function : zi,zj간의 NT-Xent Loss</p>
<h2 id="2-1-training">2-1. Training</h2>
<p>Training</p>
<ul>
<li>Batch size: 256 ~ 8192</li>
<li>LARS optimizer 사용 : 많은 Batch size 학습을 감당하기 위해</li>
<li>Aggregating BN mean and variance over all devices -&gt; 멀티 GPU 학습 중에 BatchNorm(BN)의 평균과 분산을 GPU 전체에서 모아서 계산</li>
</ul>
<p>Dataset</p>
<ul>
<li>ImageNet 2012</li>
</ul>
<p>Evaluation</p>
<ul>
<li>Linear Evaluation Protocol : representation까지의 parameter를 freeze하고 linear layer 하나만 추가하여 supervised learning, evaluation 진행</li>
<li><blockquote>
<p>self-supervised로 학습된 feature extractor(=Backbone)는 고정(freeze)시키고,
그 위에 Linear classifier 하나만 붙여서 supervised task (예: 분류)를 수행해보는 평가 방법</p>
</blockquote>
</li>
</ul>
<hr>
<p>Layer, classifier?</p>
<p>Layer</p>
<ul>
<li>딥러닝 모델을 구성하는 기본적인 구성요소
ex) Linear layer ,Convolutional layer, ReLU, BatchNorm, Dropout 등</li>
<li>모든 classifier는 layer</li>
</ul>
<p>classifier</p>
<ul>
<li>모델의 맨 끝에 위치해서 입력된 feature를 class로 구분하는 역할</li>
<li>보통은 Linear layer + softmax 조합으로 이루어짐</li>
<li>모든 layer가 classifier는 아님</li>
</ul>
<h1 id="3-data-augmentation-for-contrastive-representation-learning">3. Data Augmentation for Contrastive Representation Learning</h1>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/7c789231-4417-444f-a771-444395e51fcc/image.png" alt=""></p>
<p>간단한 몇 개로 구성된 Data Augmentation 기법 선택이 매우 중요했다고 함
<img src="https://velog.velcdn.com/images/su_hwan/post/51afbc73-5401-4526-9352-81ba1e6916e5/image.png" alt=""></p>
<p>Random cropping, Random color distortion, Gaussian blur 만을 사용하였을 때 가장 성능이 좋음
<img src="https://velog.velcdn.com/images/su_hwan/post/e07ef7c1-a133-4b76-9c15-ea1810d209cb/image.png" alt="">
색상 왜곡 강도를 높일수록 정확도가 증가 + Gaussian blur를 추가했을때 가장 높은 성능을 보였음 -&gt; Contrastive learning에서는 강한 색상 변형이 매우 중요</p>
<h1 id="4architectures-for-encoder-and-head">4.Architectures for Encoder and Head</h1>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/4f24da93-7e40-491d-ab72-2dfeac6b6293/image.png" alt="">
Non-linear projection head &gt; linear projection head &gt; None</p>
<h1 id="5-loss-function-and-batch-size">5. Loss Function and Batch Size</h1>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/e533c64d-427a-46b5-8417-421efec1bb9f/image.png" alt="">
Loss Function 중 NT-Xent (Normalized Temperature-scaled Cross Entropy)의 성능이 가장 높았음</p>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/d8ec5b76-77c5-4ce6-bfad-14fde424cc4d/image.png" alt="">
Batch Size를 매우 키우는 것이 더 좋은 성능을 보이고 있다. 이는 batch size를 매우 늘릴 경우 그 안에서 충분한 양의 negative sample을 뽑을 수 있기 때문이라고 추론해 볼 수 있다.</p>
<h1 id="6-comparison-with-state-of-the-art">6. Comparison with State-of-the-art</h1>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/540e4350-4ac9-4350-8847-24d6cf8cceb3/image.png" alt="">
여러 Self-Supervised model과의 Linear evaluation 결과를 비교하였을 때, Architecture 별로 ResNet-50을 사용한 결과와 더 깊은 구조를 사용했을 때의 결과 모두 SimCLR에서 가장 뛰어난 성능을 보인다.
Self-supervised 외에 transfer learning, semi-supervsied learning에 대한 실험에서도 우수한 성능을 보이고 있다.
<img src="https://velog.velcdn.com/images/su_hwan/post/8592dad9-3429-4c9c-b915-b6d3df6ceed1/image.png" alt="">
Semi-supervised learning에 대한 실험 결과
<img src="https://velog.velcdn.com/images/su_hwan/post/33c10f8a-8a4c-4182-b728-2deaeaf46168/image.png" alt="">
Transfer learning에 대한 실험 결과</p>
<h1 id="7-related-work">7. Related Work</h1>
<p>Handcrafted pretext tasks
Contrastive visual representation learning</p>
<h1 id="8-conclusion">8. Conclusion</h1>
<p>Self-supervised Learning Simple framework로 Self-supervised learning, Semi-supervised learning, Transfer learning의 성능을 크게 개선하였다.</p>
<p>Supervised learning과는 다른 augmentation과 non-linear projection head를 제안하였다.</p>
<p>Representation을 학습하는 것으로 Supervised learning 수준의 성능을 달성하였다.</p>
<p>기존의 Pretext task를 활용하지 않고 Memory Bank 또한 필요로 하지 않는다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이미지 필터링]]></title>
            <link>https://velog.io/@su_hwan/%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%95%84%ED%84%B0%EB%A7%81</link>
            <guid>https://velog.io/@su_hwan/%EC%9D%B4%EB%AF%B8%EC%A7%80-%ED%95%84%ED%84%B0%EB%A7%81</guid>
            <pubDate>Fri, 13 Jun 2025 15:47:44 GMT</pubDate>
            <description><![CDATA[<h1 id="이미지-필터링">이미지 필터링</h1>
<p>이미지에서 필요한 정보만 남기고 노이즈와 같은 불필요한 정보는 걸러내는 작업</p>
<ul>
<li>필터링한 이미지는 주로 영상에서 객체 인식과 같은 다른 작업을 위한 전처리 단계로 활용</li>
<li>주파수 공간에서 필터링(주로 사용), 이미지 공간에서 필터링</li>
<li>물체의 윤곽선이나 모서리 같은 부분이 고주파, 이웃한 픽셀들과 색상의 차이가 적은 부분이 저주파</li>
</ul>
<h2 id="91-평균-필터링">9.1 평균 필터링</h2>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/833eb3ef-d5ca-4602-8fd5-48b29ba26d59/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/d96ec8db-abd8-411d-b5ac-ee48c4d9bf6a/image.png" alt=""></p>
<h2 id="92-메디안-필터링">9.2 메디안 필터링</h2>
<p>cv2.medianBlur()는 가장자리 보존하면서 노이즈 제거가 필요한 경우에 적합합니다.</p>
<p>특히 점처럼 생기는 &quot;소금-후추 노이즈(Salt-and-Pepper Noise)&quot;에 매우 효과적입니다.</p>
<p>cv2.GaussianBlur()보다 경계 보존이 잘되는 특징이 있습니다.
<img src="https://velog.velcdn.com/images/su_hwan/post/4329336b-c50f-4c71-b2f1-7d9c686ba5ac/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/3cd4163b-138a-4bd8-b3ff-d104b147b64e/image.png" alt=""></p>
<h2 id="93-가우시안-필터링">9.3 가우시안 필터링</h2>
<p>cv2.imread()는 BGR 형식으로 이미지를 읽기 때문에, matplotlib.pyplot.imshow()로 표시할 때 <strong>cv2.cvtColor(..., cv2.COLOR_BGR2RGB)</strong>로 변환이 필요합니다.</p>
<p>plt.subplot(1, 2, n)은 가로 2개 그림 중 n번째 위치를 뜻합니다.</p>
<p>cv2.GaussianBlur()는 노이즈 제거와 함께 부드러운 흐림 효과를 제공합니다.
<img src="https://velog.velcdn.com/images/su_hwan/post/adb159c0-5123-4857-8d32-d1a60278af4f/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/a5c0b543-988e-4a57-9b8c-4bad2f174e77/image.png" alt=""></p>
<h2 id="94-고역통과-필터링">9.4 고역통과 필터링</h2>
<h3 id="1-에지-검출sobel-연산자">1. 에지 검출(Sobel 연산자)</h3>
<p>  Sobel 필터는 미분 연산을 기반으로 이미지의 경계를 검출합니다.</p>
<ul>
<li><p>dx=1, dy=0: 수평 방향 (세로 경계)</p>
</li>
<li><p>dx=0, dy=1: 수직 방향 (가로 경계)</p>
</li>
</ul>
<p>cv2.GaussianBlur()는 노이즈를 줄여 Sobel 결과의 품질을 높이는 역할을 합니다.</p>
<p>np.hstack()으로 원본, X 에지, Y 에지를 한 화면에 비교 가능하게 나열합니다.
  <img src="https://velog.velcdn.com/images/su_hwan/post/1ce0be1d-edde-4935-850d-124fbca32e3b/image.png" alt="">
  <img src="https://velog.velcdn.com/images/su_hwan/post/cc03f8eb-46c5-4899-acba-4ecb1c3b37fd/image.png" alt=""></p>
<h3 id="2-에지-검출laplacian-연산자">2. 에지 검출(Laplacian 연산자)</h3>
<p>  Laplacian 연산자는 2차 미분을 사용하여 밝기 변화가 급격한 경계(에지)를 검출합니다.</p>
<p>GaussianBlur는 에지 검출 전에 노이즈를 줄이는 사전 처리 역할을 합니다.</p>
<p>결과를 np.concatenate()로 나란히 출력해 비교가 쉽도록 합니다.
<img src="https://velog.velcdn.com/images/su_hwan/post/17bceeaa-9682-4c19-ac9a-d41103e9a3f9/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/e2de5ecd-ffd1-4fdc-8edf-cef75f13452b/image.png" alt=""></p>
<h3 id="3-에지-검출canny-알고리즘">3. 에지 검출(Canny 알고리즘)</h3>
<p>  cv2.createTrackbar(): 슬라이더를 만들고, 조작할 때마다 지정된 콜백 함수(onChange)가 실행됩니다.</p>
<p>cv2.Canny(): 주어진 최소/최대 임계값으로 엣지를 검출합니다.</p>
<p>minVal: 낮은 임계값 → 약한 에지</p>
<p>maxVal: 높은 임계값 → 강한 에지</p>
<p>이 코드는 실시간으로 임계값을 조정하면서 Canny 결과를 확인할 수 있는 인터랙티브 도구입니다.
  <img src="https://velog.velcdn.com/images/su_hwan/post/f68ddbe8-1b77-4105-81c4-603923aa6e1a/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/dfb1845d-e2ad-441c-bde2-9e7c973c37a1/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[색 공간과 이미지]]></title>
            <link>https://velog.io/@su_hwan/%EC%83%89-%EA%B3%B5%EA%B0%84%EA%B3%BC-%EC%9D%B4%EB%AF%B8%EC%A7%80</link>
            <guid>https://velog.io/@su_hwan/%EC%83%89-%EA%B3%B5%EA%B0%84%EA%B3%BC-%EC%9D%B4%EB%AF%B8%EC%A7%80</guid>
            <pubDate>Sat, 31 May 2025 15:15:39 GMT</pubDate>
            <description><![CDATA[<h1 id="7장-색-공간의-이해">7장 색 공간의 이해</h1>
<h2 id="71-rgb-색-공간">7.1 RGB 색 공간</h2>
<p><strong>RGB는 Red(빨강), Green(초록), Blue(파랑)</strong>의 약자</p>
<ul>
<li>컴퓨터는 이미지를 빨강, 초록, 파랑의 빛의 조합으로 표현</li>
<li>예를 들어, 흰색은 R, G, B가 모두 255 (최댓값)일 때 나옴</li>
<li>흑백 이미지는 RGB를 하나로 합쳐서 밝기만 표현</li>
</ul>
<p>🧠 기억 포인트:
이미지는 숫자로 구성된 격자이며, 색상은 RGB 값의 조합</p>
<h2 id="72-hsv-색-공간-cie-색-공간">7.2 HSV 색 공간, CIE 색 공간</h2>
<p>📌 HSV 색 공간
HSV는 Hue(색상), Saturation(채도), Value(명도) 로 구성된 색 표현 방법</p>
<p>색상(Hue): 색의 종류 (0~179)
채도(Saturation): 색의 선명함 (0은 회색, 255는 선명)
명도(Value): 밝기 (0은 어두움, 255는 밝음)</p>
<p>💡 왜 사용할까?
HSV는 사람이 인식하는 방식과 비슷해서 색상 추출, 필터링 등에 편리함</p>
<p>📌 CIE 색 공간
CIE 색 공간은 사람이 느끼는 색을 과학적으로 표현한 국제 표준 색 공간. 색의 물리적 특성과 인간의 시각을 기반으로 만들어짐</p>
<p>주로 색 정확도가 중요한 산업 (인쇄, 의료 등)에서 사용</p>
<p>BGR → HSV 색 공간 변환
<img src="https://velog.velcdn.com/images/su_hwan/post/6b50b559-e6ad-4468-900b-e611bffac116/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/3cc2e760-4627-4480-8132-f24a69cb5e06/image.png" alt=""></p>
<h2 id="73-동영상-처리">7.3 동영상 처리</h2>
<p>동영상은 <strong>많은 이미지(프레임)</strong>가 빠르게 연속해서 보여지는 것
각 프레임을 순차적으로 처리하여 사람, 물체, 움직임을 인식
프레임은 보통 1초에 30장 (FPS: Frames Per Second) 정도</p>
<h2 id="74-동영상-정보-획득">7.4 동영상 정보 획득</h2>
<p>OpenCV 같은 라이브러리를 사용하면 동영상 파일의 정보를 얻을 수 있음</p>
<p>프레임 수 (cv2.CAP_PROP_FRAME_COUNT)
프레임 속도 (FPS)
해상도 (가로x세로)
현재 프레임 위치</p>
<h2 id="75-동영상-캡쳐-및-저장">7.5 동영상 캡쳐 및 저장</h2>
<p>웹캠 영상이나 동영상 파일을 읽어서 저장</p>
<ol>
<li>cv2.VideoCapture()로 입력 장치 열기</li>
<li>프레임을 하나씩 읽고 (cap.read())</li>
<li>cv2.VideoWriter()로 저장<h2 id="76-저장된-동영상-플레이하기">7.6 저장된 동영상 플레이하기</h2>
OpenCV로 동영상 파일을 프레임 단위로 읽어 재생</li>
</ol>
<h1 id="8장-이미지-조절-및-향상">8장 이미지 조절 및 향상</h1>
<h2 id="81-이미지의-밝기-조절">8.1 이미지의 밝기 조절</h2>
<p>이미지 밝기를 조절한다는 것은 사진을 더 밝거나 어둡게 만든다는 뜻
밝기 조절
<img src="https://velog.velcdn.com/images/su_hwan/post/5393c302-8254-45a9-ade1-7553a0684283/image.png" alt="">
밝기 트랙바
<img src="https://velog.velcdn.com/images/su_hwan/post/c15a45be-f541-4476-b8af-7f84898d9e1c/image.png" alt=""></p>
<h3 id="1-포화현상이란-포화현상이-일어나는-경우">1) 포화현상이란? 포화현상이 일어나는 경우</h3>
<p>  포화현상(Saturation):</p>
<ul>
<li>이미지를 너무 밝게 하거나 너무 어둡게 해서 <strong>최댓값(255)이나 최솟값(0)</strong>으로 색이 꽉 차버리는 현상입니다.</li>
<li>너무 밝게 하면 → 흰색처럼 보이는 영역이 많아지고, 원래 정보(색)가 사라집니다.</li>
<li>너무 어둡게 하면 → 까맣게 변하면서 정보가 날아갑니다.
<img src="https://velog.velcdn.com/images/su_hwan/post/ad72fcb9-5163-4f3d-a963-67692b99299a/image.png" alt=""><h3 id="2-포화현상-해결">2) 포화현상 해결</h3>
<ol>
<li>OpenCV의 cv2.add() 함수는 자동으로 포화 현상을 막아줍니다.<ul>
<li>넘치는 값은 255로 고정</li>
<li>부족한 값은 0으로 고정</li>
</ul>
</li>
<li>np.clip()을 써서 직접 조절
<code>bright_img = np.clip(img + 100, 0, 255).astype(np.uint8)</code>
<img src="https://velog.velcdn.com/images/su_hwan/post/0a299fdc-d0d1-45be-8ad5-7839f55d1dd6/image.png" alt=""></li>
</ol>
</li>
</ul>
<h2 id="82-이미지의-명암대비-조절">8.2 이미지의 명암대비 조절</h2>
<p>명암 대비(Contrast):
밝은 부분은 더 밝게, 어두운 부분은 더 어둡게 만들어서 선명하게 보이도록 하는 것입니다.</p>
<ul>
<li>낮은 대비: 흐릿하고 뿌연 이미지</li>
<li>높은 대비: 선명하고 강한 이미지<hr>

</li>
</ul>
<p>원근 변환
<img src="https://velog.velcdn.com/images/su_hwan/post/cc89119e-6d9c-4af6-aba9-7993ca17ec61/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/036e2905-f2a4-41f8-afd6-6ab75d34f3b5/image.png" alt=""></p>
<h2 id="83-이미지의-히스토그램">8.3 이미지의 히스토그램</h2>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/00208c6b-83f1-40ae-82d9-c89c3fbcdcf7/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/0ee98e2b-6156-424e-8213-71edc54a504c/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/2f45227e-acd5-418d-889e-776b25b8ae1a/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/3b88c236-a07e-4242-977d-a0fe9154032c/image.png" alt=""></p>
<h3 id="831-히스토그램-평활화">8.3.1 히스토그램 평활화</h3>
<p>히스토그램 평활화는 이미지의 픽셀 분포를 재분배하여 대비(contrast)를 향상시키는 기법
어두운 이미지를 보다 명확하게 만들 때 자주 사용
<img src="https://velog.velcdn.com/images/su_hwan/post/56e741a5-95ed-4310-93c3-8434c49e08eb/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[이미지 변환]]></title>
            <link>https://velog.io/@su_hwan/%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B3%80%ED%99%98</link>
            <guid>https://velog.io/@su_hwan/%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%B3%80%ED%99%98</guid>
            <pubDate>Tue, 20 May 2025 13:40:17 GMT</pubDate>
            <description><![CDATA[<h1 id="이미지-변환">이미지 변환</h1>
<h2 id="기하학적-변환">기하학적 변환</h2>
<ul>
<li><p>영상을 구성하는 픽셀의 배치 구조를 변경함으로써 전체 영상의 모양을 바꾸는 작업</p>
</li>
<li><p>픽셀 값은 그대로 유지하면서 위치를 변경하는 작업</p>
<h3 id="어파인-변환">어파인 변환</h3>
</li>
<li><p>영상을 평행이동 시키거나 회전, 크기 변환 등을 통해 만들 수 있는 변환을 통칭</p>
</li>
<li><p>직선은 그대로 직선으로 나타나고, 직선 간의 길이 비율과 평행 관계는 그대로 유지</p>
</li>
<li><p>직사각형 형태의 영상은 어파인 변환에 의해 평행사변형에 해당하는 모양으로 변경</p>
<h4 id="1-이동translation">1) 이동(Translation)</h4>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/32bed087-5513-4eff-a2de-2984070c0087/image.png" alt=""></p>
<h4 id="2-크기-변환resizing">2) 크기 변환(Resizing)</h4>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/aaed1195-6bf1-4861-ad47-1c9dc4cb7003/image.png" alt=""></p>
</li>
<li><p><em>보간법(interpolation)*</em></p>
</li>
<li><p>cv2.INTER_NEAREST: 가장 가까운 픽셀값 사용 (계산 속도 빠름, 품질 낮음, 계단현상 발생 가능)</p>
</li>
<li><p>cv2.INTER_LINEAR: 선형 보간 (기본값, 보통의 확대/축소용)</p>
</li>
<li><p>cv2.INTER_CUBIC: 4x4 주변 픽셀 사용한 큐빅 보간 (품질 높음, 속도 느림)</p>
</li>
<li><p>cv2.INTER_LANCZOS4: Lanczos 알고리즘 사용 (가장 품질 좋음, 가장 느림)</p>
<h4 id="3-회전-변환rotation">3) 회전 변환(Rotation)</h4>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/c49389b8-dfb2-44da-ab1b-33a887d7b397/image.png" alt=""></p>
<h4 id="4-대칭-변환reflction">4) 대칭 변환(Reflction)</h4>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/9fc4e016-b9f7-4269-9213-e93bb799f2e0/image.png" alt=""></p>
</li>
</ul>
<table>
<thead>
<tr>
<th><code>flipCode</code> 값</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>1</code></td>
<td>좌우 반전 (Left ↔ Right)</td>
</tr>
<tr>
<td><code>0</code></td>
<td>상하 반전 (Top ↔ Bottom)</td>
</tr>
<tr>
<td><code>-1</code></td>
<td>상하 + 좌우 반전 (Diagonal)</td>
</tr>
</tbody></table>
<h4 id="5-전단-변환shear-밀림-변환">5) 전단 변환(Shear): 밀림 변환</h4>
<p>   <img src="https://velog.velcdn.com/images/su_hwan/post/6676c3b5-32b6-4a4c-9350-2c0b441c330e/image.png" alt="">
<strong>Shear 변환</strong></p>
<ul>
<li>기울이기(Shear)는 한 방향(수평 또는 수직)으로 픽셀을 밀어 이미지 형태를 기울게 만드는 변환</li>
<li>이 코드에서는 x 방향을 기준으로 y 방향으로 픽셀을 이동시키므로, 이미지가 수직으로 기울어짐</li>
</ul>
<h3 id="원근변환">원근변환</h3>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/f17cf830-73be-4346-bca3-664f6908b963/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[도형 그리기]]></title>
            <link>https://velog.io/@su_hwan/%EB%8F%84%ED%98%95-%EA%B7%B8%EB%A6%AC%EA%B8%B0</link>
            <guid>https://velog.io/@su_hwan/%EB%8F%84%ED%98%95-%EA%B7%B8%EB%A6%AC%EA%B8%B0</guid>
            <pubDate>Thu, 24 Apr 2025 15:08:02 GMT</pubDate>
            <description><![CDATA[<h2 id="트랙바-이벤트">트랙바 이벤트</h2>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/3691a09a-10df-4c97-acc8-f3c10bffe0f9/image.png" alt=""></p>
<h1 id="5장-도형그리기">5장 도형그리기</h1>
<h2 id="51-선그리기">5.1 선그리기</h2>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/470ef2c2-d237-437e-a246-772a38d5637c/image.png" alt=""></p>
<h2 id="52-사각형그리기">5.2 사각형그리기</h2>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/5c28e5ff-4b42-46bf-b14b-a2949bd95540/image.png" alt=""></p>
<h2 id="53-원그리기">5.3 원그리기</h2>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/473224a7-8c9f-4678-a390-c862d12f5451/image.png" alt=""></p>
<h2 id="54-타원-그리기">5.4 타원 그리기</h2>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/d123af65-eab1-426b-a2d7-8097946f03a6/image.png" alt=""></p>
<h2 id="55-다각형그리기">5.5 다각형그리기</h2>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/2b4d61d2-8fb5-4acb-9366-29b524235487/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Opencv]]></title>
            <link>https://velog.io/@su_hwan/Opencv</link>
            <guid>https://velog.io/@su_hwan/Opencv</guid>
            <pubDate>Sat, 12 Apr 2025 05:28:01 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/su_hwan/post/d96f2fb4-4a0a-4efa-837e-d100f0183241/image.png" alt=""></p>
<p>OpenCV의 주요 기능</p>
<ul>
<li>이미지 처리: 이미지의 기본적인 처리 작업(읽기, 쓰기, 변환)부터 복잡한 처리(필터링, 특징 추출, 이미지 복원 등)까지 지원</li>
<li>비디오 처리: 비디오 파일 읽기 및 쓰기, 비디오 스트림 처리, 객체 추적 등의 기능을 제공</li>
<li>컴퓨터 비전: 얼굴 인식, 객체 탐지, 광학 문자 인식(OCR), 패턴 인식 등 다양한 컴퓨터 비전 관련 작업을 수행 가능</li>
<li>기계 학습: OpenCV는 기본적인 기계 학습 알고리즘(예: k-최근접 이웃, SVM, 의사결정 트리 등)을 제공하며, 이미지 데이터와 함께 사용 가능</li>
<li>실시간 컴퓨터 비전: 카메라 스트림을 실시간으로 처리하고, 객체 탐지, 얼굴 인식, 동작 인식 등을 수행 가능</li>
</ul>
<h1 id="이미지-다루기">이미지 다루기</h1>
<ul>
<li>이미지 파일을 읽고, 보고, 저장하는 방법에 대해서 알아봅니다.</li>
<li>관련 함수인 <code>cv2.imread()</code>, <code>cv2.imshow()</code>, <code>cv2.imwrite()</code> 에 대해서 알아 봅니다.</li>
</ul>
<h2 id="이미지-읽기">이미지 읽기</h2>
<p>우선 openCV모듈을 import합니다.</p>
<blockquote>
<p><code>import cv2</code></p>
</blockquote>
<p><code>cv2.imread()</code> 함수를 이용하여 이미지 파일을 읽습니다. 이미지 파일의 경로는 절대/상대경로가 가능합니다.</p>
<blockquote>
<p><code>img = cv2.imread(&#39;lena.jpg&#39;, cv2.IMREAD_COLOR)</code></p>
</blockquote>
<p>py:function - cv2.imread(fileName, flag)</p>
<pre><code>이미지 파일을 flag값에 따라서 읽어들입니다.

:param fileName: 이미지파일의 경로
:type fileName: str
:param flag: 이미지 파일을 읽을 때의 Option.
:type flag: int
:return: image객체 행렬
:rtype: numpy.ndarray</code></pre><p>이미지 읽기의 flag는 3가지가 있습니다.</p>
<ul>
<li><code>cv2.IMREAD_COLOR</code> : 이미지 파일을 Color로 읽어들입니다. 투명한 부분은 무시되며, Default값입니다.</li>
<li><code>cv2.IMREAD_GRAYSCALE</code> : 이미지를 Grayscale로 읽어 들입니다. 실제 이미지 처리시 중간단계로 많이 사용합니다.</li>
<li><code>cv2.IMREAD_UNCHANGED</code> : 이미지파일을 alpha channel까지 포함하여 읽어 들입니다.</li>
</ul>
<p>3개의 flag대신에 1, 0, -1을 사용해도 됩니다.</p>
<p>img값은 numpy의 ndarray type입니다. numpy는 python에서 수학적 처리를 위한 모듈로 openCV에서도 많이 사용됩니다.
img가 어떤 형태의 행렬인지 확인을 해보려면 아래와 같이 입력합니다.</p>
<blockquote>
<p>img.shape
(206, 207, 3)</p>
</blockquote>
<p>이미지는 3차원 행렬로 return이 됩니다. 206은 행(Y축), 207은 열(X축), 3은 행과 열이 만나는 지점의 값이 몇개의
원소로 이루어져 있는지를 나타납니다. 위 값의 의미는 이미지의 사이즈는 207 X 206이라는 의미입니다.</p>
<p>그렇다면 3은 어떤 의미일까요. 바로 색을 표현하는 BGR값입니다. 일반적으로 RGB로 많이 나타내는데, openCV는
B(lue), G(reen), R(ed)로 표현을 합니다.</p>
<p>다음은 읽은 이미지를 보는 방법에 대해서 알아보겠습니다.</p>
<h2 id="이미지-보기">이미지 보기</h2>
<p><code>cv2.imshow()</code> 함수는 이미지를 사이즈에 맞게 보여줍니다.</p>
<blockquote>
<p><code>c22.imshow(&#39;image&#39;, img)</code>
<code>cv2.waitKey(0)</code>
<code>cv2.destroyAllWindows()</code></p>
</blockquote>
<p>py:function - cv2.imshow(title, image)</p>
<pre><code>읽어들인 이미지 파일을 윈도우창에 보여줍니다.

:param title: 윈도우 창의 Title
:type title: str
:param image: ``cv2.imread()`` 의 return값
:type image: numpy.ndarray</code></pre><p><code>cv2.waitKey()</code> 는 keyboard입력을 대기하는 함수로 0이면 key입력까지 무한대기이며 특정 시간동안 대기하려면 milisecond값을 넣어주면 됩니다.</p>
<p><code>cv2.destroyAllWindows()</code> 는 화면에 나타난 윈도우를 종료합니다. 일반적으로 위 3개는 같이 사용됩니다.</p>
<h2 id="이미지-저장하기">이미지 저장하기</h2>
<p><code>cv2.imwrite()</code> 함수를 이용하여 변환된 이미지나 동영상의 특정 프레임을 저장합니다.</p>
<blockquote>
<p>cv2.imwrite(&#39;lenagray.png&#39;, gray)</p>
</blockquote>
<p>py:function - cv2.imwrite(fileName, image)</p>
<pre><code>image파일을 저장합니다.

:param fileName: 저장될 파일명
:type fileName: str
:param image: 저장할 이미지</code></pre><h2 id="컬러-이미지의-r-g-b-채널">컬러 이미지의 R, G, B 채널</h2>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/ce3fe35d-19bb-479e-ad4b-b5119dc6dd89/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/2119d442-fb1d-4564-af3a-bbb034a0f94d/image.png" alt=""></p>
<p>컬러 이미지와 그레이스케일 이미지를 각각 불러와 비교하고 저장</p>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/7455d5b9-3faf-424d-9f81-fb622bff5d63/image.png" alt=""></p>
<p>컬러 이미지를 불러온 후 OpenCV 함수로 그레이스케일로 변환하여 출력
<img src="https://velog.velcdn.com/images/su_hwan/post/0adb27bd-384c-4b3a-b19f-927a27797956/image.png" alt="">
NumPy만 이용해서 컬러 이미지를 그레이스케일로 변환
<img src="https://velog.velcdn.com/images/su_hwan/post/1b02411a-cbaf-4795-8b80-531833346ffe/image.png" alt="">
수식으로 가중 평균을 사용해 그레이스케일 이미지로 변환
<img src="https://velog.velcdn.com/images/su_hwan/post/a86404a7-1391-467f-8014-a9c509b84727/image.png" alt="">
파란색, 초록색, 빨간색 채널의 밝기 변화를 시각적으로 확인할 수 있는 이미지를 생성
<img src="https://velog.velcdn.com/images/su_hwan/post/150c4990-b27f-47ae-835a-6b6d73261b02/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[AI 챗봇 관련 서비스]]></title>
            <link>https://velog.io/@su_hwan/AI-%EC%B1%97%EB%B4%87-%EA%B4%80%EB%A0%A8-%EC%84%9C%EB%B9%84%EC%8A%A4</link>
            <guid>https://velog.io/@su_hwan/AI-%EC%B1%97%EB%B4%87-%EA%B4%80%EB%A0%A8-%EC%84%9C%EB%B9%84%EC%8A%A4</guid>
            <pubDate>Mon, 07 Apr 2025 18:55:41 GMT</pubDate>
            <description><![CDATA[<h1 id="🍔-ai-챗봇--open-api-활용-프랜차이즈-사례-분석">🍔 AI 챗봇 &amp; Open API 활용 프랜차이즈 사례 분석</h1>
<h2 id="🚀-브랜드별-ai-챗봇-도입-사례">🚀 브랜드별 AI 챗봇 도입 사례</h2>
<h3 id="🍕-1-도미노피자-dominos-pizza---dom">🍕 1. 도미노피자 (Domino&#39;s Pizza) - &#39;Dom&#39;</h3>
<ul>
<li><strong>활용 플랫폼</strong>: Facebook Messenger, Google Assistant, 자체 앱, 웹사이트, 스마트 워치 등</li>
<li><strong>기술 스택</strong>: AI + NLP (자연어 처리), 음성 인식(STT/TTS), 자체 주문 시스템 API 연동</li>
<li><strong>주요 기능</strong>:<ul>
<li><strong>멀티 채널 자연어 주문</strong>: &quot;페퍼로니 피자 라지 사이즈 하나 주문해줘&quot; → 다양한 채널에서 텍스트/음성 주문 처리</li>
<li>최근 주문 간편 재주문</li>
<li>실시간 주문 추적 (Pizza Tracker 연동)</li>
<li>매장 찾기 (위치 기반)</li>
<li>Google Assistant 통한 음성 주문 (&quot;Hey Google, talk to Domino&#39;s&quot;)</li>
</ul>
</li>
<li><strong>📈 성과</strong>: 챗봇/음성 주문량 증가, 고객 주문 접근성 및 편의성 대폭 향상, 주문 처리 속도 개선</li>
<li><blockquote>
<p>✅ <strong>포인트</strong>: <strong>다양한 고객 접점(채널) + 음성 인식 기술</strong>을 적극 활용하여 주문 진입 장벽을 낮추고 <strong>속도와 접근성</strong> 극대화. 자체 주문/추적 시스템 API 연동이 핵심.</p>
</blockquote>
</li>
</ul>
<h3 id="🍔-2-버거킹-burger-king---messenger-bot--assistant-action">🍔 2. 버거킹 (Burger King) - Messenger Bot &amp; Assistant Action</h3>
<ul>
<li><strong>활용 플랫폼</strong>: Facebook Messenger, Google Assistant</li>
<li><strong>기술 스택</strong>: NLP 기반 챗봇 엔진, <strong>위치 정보 Open API (GPS)</strong>, <strong>메뉴 정보 API</strong> 연동</li>
<li><strong>주요 기능</strong>:<ul>
<li><strong>위치 기반 매장 안내</strong>: &quot;내 주변 버거킹 찾아줘&quot; → GPS API 연동으로 근처 매장 정보 제공</li>
<li>자연어 기반 메뉴 검색 및 주문</li>
<li>프로모션 및 쿠폰 정보 제공</li>
<li>영업시간 등 매장 정보 안내</li>
</ul>
</li>
<li><strong>📈 성과</strong>: 모바일 주문율 상승, 앱/챗봇 내 사용자 체류 시간 및 상호작용 증가, 위치 기반 서비스 통한 매장 방문 유도 효과.</li>
<li><blockquote>
<p>✅ <strong>포인트</strong>: <strong>외부 위치 정보 Open API</strong>와 <strong>내부 메뉴/프로모션 API</strong>를 연동하여 <strong>O2O(Online to Offline) 경험</strong> 강화 및 모바일 주문 편의성 증대.</p>
</blockquote>
</li>
</ul>
<h3 id="🌯-3-타코벨-taco-bell--tacobot">🌯 3. 타코벨 (Taco Bell) – ‘TacoBot’</h3>
<ul>
<li><strong>활용 플랫폼</strong>: Slack (초기 내부 테스트), 웹, 모바일 앱 연동 가능성</li>
<li><strong>기술 스택</strong>: NLP 기반 자체 AI 챗봇, <strong>내부 메뉴 시스템 API</strong> 연동</li>
<li><strong>주요 기능</strong>:<ul>
<li><strong>자유로운 대화형 주문</strong>: &quot;오늘 점심 뭐 먹지?&quot; → 메뉴 추천 및 주문 연계</li>
<li><strong>메뉴 커스터마이징</strong>: &quot;치즈 빼고 할라피뇨 추가해줘&quot; → 주문 시 옵션 변경 내용을 API 통해 시스템에 반영</li>
<li>과거 주문 내역 기반 재주문</li>
<li>유머러스하고 친근한 대화 톤 (브랜드 페르소나 반영)</li>
</ul>
</li>
<li><strong>📈 성과</strong>: Slack 기반 내부 파일럿 테스트에서 직원 만족도 및 사용성 검증, AI 학습 데이터 확보 및 고도화. (초기 단계)</li>
<li><blockquote>
<p>✅ <strong>포인트</strong>: <strong>메뉴 커스터마이징</strong>까지 자연어 대화로 처리하는 정교함. <strong>내부 메뉴 API</strong>를 활용하여 복잡한 주문도 처리 가능. 챗봇에 <strong>브랜드 캐릭터</strong>를 부여하여 사용자 경험 차별화.</p>
</blockquote>
</li>
</ul>
<h3 id="☕-4-스타벅스-starbucks--my-starbucks-barista--alexa-skill">☕ 4. 스타벅스 (Starbucks) – ‘My Starbucks Barista’ &amp; Alexa Skill</h3>
<ul>
<li><strong>활용 플랫폼</strong>: Starbucks 모바일 앱, Amazon Alexa</li>
<li><strong>기술 스택</strong>:<ul>
<li>고도화된 NLP + 음성 인식 (STT/TTS)</li>
<li><strong>자체 Open API 에코시스템 (메뉴, 재고, 결제, 리워드, 매장 정보 등 포괄적 연동)</strong></li>
<li>클라우드 기반 인프라 (e.g., AWS)</li>
</ul>
</li>
<li><strong>주요 기능</strong>:<ul>
<li><strong>음성/텍스트 기반 자연어 주문</strong>: &quot;아이스 아메리카노 톨 사이즈 하나, 내 계정으로 결제해줘&quot;</li>
<li>복잡한 커스텀 주문 처리 (샷 추가, 시럽 변경 등)</li>
<li><strong>Starbucks Rewards 연동</strong>: 별 적립, 잔액 확인, 리워드 사용, 자동 결제</li>
<li>최근 주문/선호 메뉴 기반 재주문</li>
<li>매장 위치 및 픽업 시간 지정</li>
</ul>
</li>
<li><strong>📈 성과</strong>: 충성 고객 대상 반복 주문 증가, 앱 활용도 및 체류 시간 증대, 개인화된 고객 경험 제공, 음성 인터페이스를 통한 주문 편의성 혁신.</li>
<li><blockquote>
<p>✅ <strong>포인트</strong>: <strong>자체 구축한 포괄적인 Open API 생태계</strong>를 기반으로 주문부터 <strong>결제, 리워드까지 끊김 없는(Seamless) 경험</strong> 제공. <strong>음성 인터페이스</strong>를 핵심 기능으로 내세워 고도화된 개인화 서비스 구현.</p>
</blockquote>
</li>
</ul>
<hr>
<h2 id="📊-사례-비교-분석">📊 사례 비교 분석</h2>
<h3 id="📌-기능별-비교-요약">📌 기능별 비교 요약</h3>
<table>
<thead>
<tr>
<th align="left">기능</th>
<th align="center">도미노피자</th>
<th align="center">버거킹</th>
<th align="center">타코벨</th>
<th align="center">스타벅스</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>자연어 주문</strong></td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">✅</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="left"><strong>음성 인터페이스</strong></td>
<td align="center">✅ (강점)</td>
<td align="center">✅</td>
<td align="center">❌ (초기)</td>
<td align="center">✅ (핵심 기능)</td>
</tr>
<tr>
<td align="left"><strong>메뉴 커스터마이징</strong></td>
<td align="center">△ (제한적)</td>
<td align="center">△ (제한적)</td>
<td align="center">✅ (강점)</td>
<td align="center">✅ (고도화)</td>
</tr>
<tr>
<td align="left"><strong>위치/매장 정보 제공</strong></td>
<td align="center">✅</td>
<td align="center">✅ (강점)</td>
<td align="center">❌ (초기)</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="left"><strong>Open API 활용</strong></td>
<td align="center">△ (내부)</td>
<td align="center">✅ (외부)</td>
<td align="center">✅ (내부)</td>
<td align="center">✅ (자체 생태계)</td>
</tr>
<tr>
<td align="left"><strong>결제/리워드 연동</strong></td>
<td align="center">△ (앱)</td>
<td align="center">❌</td>
<td align="center">❌ (초기)</td>
<td align="center">✅ (핵심 기능)</td>
</tr>
<tr>
<td align="left"><strong>다양한 채널 지원</strong></td>
<td align="center">✅ (강점)</td>
<td align="center">✅</td>
<td align="center">△ (Slack 시작)</td>
<td align="center">✅ (앱, 스피커)</td>
</tr>
</tbody></table>
<p><em>(△: 일부 지원 또는 다른 방식/수준으로 제공)</em></p>
<h3 id="📌-브랜드별-대표-전략-비교">📌 브랜드별 대표 전략 비교</h3>
<table>
<thead>
<tr>
<th align="left">브랜드</th>
<th align="left">대표 전략 1</th>
<th align="left">대표 전략 2</th>
<th align="left">API 활용 특징</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>도미노피자</strong></td>
<td align="left"><strong>멀티채널 + 음성</strong> 통한 주문 접근성 극대화</td>
<td align="left">빠른 주문 및 <strong>실시간 추적</strong> 연동</td>
<td align="left">자체 주문/추적 시스템 API 연동 중심</td>
</tr>
<tr>
<td align="left"><strong>버거킹</strong></td>
<td align="left"><strong>위치 기반 서비스 (O2O)</strong> 와 AI 연동 강화</td>
<td align="left">모바일 메신저 중심의 <strong>편의성</strong> 증대</td>
<td align="left">외부 위치 API + 내부 메뉴/프로모션 API</td>
</tr>
<tr>
<td align="left"><strong>타코벨</strong></td>
<td align="left"><strong>정교한 메뉴 커스터마이징</strong> 대화 처리</td>
<td align="left"><strong>친근한 브랜드 캐릭터</strong> 기반 UX 차별화</td>
<td align="left">내부 메뉴 시스템 API 연동 (복잡 주문 처리)</td>
</tr>
<tr>
<td align="left"><strong>스타벅스</strong></td>
<td align="left"><strong>자체 API 생태계</strong> 기반 통합 경험 제공</td>
<td align="left"><strong>음성 인터페이스</strong> 중심의 개인화된 주문/결제/리워드</td>
<td align="left">메뉴, 결제, 리워드 등 포괄적 자체 API</td>
</tr>
</tbody></table>
<h3 id="📌-공통점">📌 공통점</h3>
<ol>
<li><strong>AI 기반 자연어 이해 (NLP)는 기본</strong>: 모든 사례에서 사용자의 의도를 파악하고 자연스러운 대화를 가능하게 하는 NLP 기술이 핵심적으로 활용됩니다.</li>
<li><strong>대화형 UI/UX 도입</strong>: 챗봇 인터페이스를 통해 메뉴 탐색부터 주문 완료까지의 과정을 더 직관적이고 간편하게 만듭니다.</li>
<li><strong>주문 편의성 혁신</strong>: 재주문, 추천, 위치 안내 등 다양한 기능을 통해 고객의 주문 경험을 개선하고 시간을 절약해 줍니다.</li>
<li><strong>API 연동을 통한 기능 확장</strong>: 단순 대화를 넘어 실제 주문 처리, 위치 정보 확인, 결제, 멤버십 연동 등 복합적인 기능을 구현하기 위해 내부 시스템 또는 외부 서비스와의 API 연동이 필수적입니다. 특히 Open API 활용은 서비스 확장성과 유연성을 높이는 데 기여합니다.</li>
<li><strong>모바일 및 음성 플랫폼 적극 활용</strong>: 고객이 가장 많이 사용하는 스마트폰 앱, 메신저, AI 스피커 등을 통해 서비스를 제공하여 접근성을 높입니다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Pandas]]></title>
            <link>https://velog.io/@su_hwan/Pandas-ys00gaab</link>
            <guid>https://velog.io/@su_hwan/Pandas-ys00gaab</guid>
            <pubDate>Sun, 06 Apr 2025 14:17:26 GMT</pubDate>
            <description><![CDATA[<h1 id="판다스-설치">판다스 설치</h1>
<p>pip install pandas</p>
<hr>

<h1 id="판다스-데이터-객체">판다스 데이터 객체</h1>
<h2 id="dataframe-객체">DataFrame 객체</h2>
<p>DataFrame 객체는 행과 열로 이루어진 2차원 데이터를 다루기 위한 객체입니다. 열은 각각의 변수를 나타내고, 행은 각각의 관측치를 나타냅니다. DataFrame 객체는 여러 가지 방법으로 생성할 수 있습니다.</p>
<pre><code class="language-py"># 리스트를 사용하여 DataFrame 객체 생성하기
import pandas as pd

data = [[&#39;A&#39;, 1], [&#39;B&#39;, 2], [&#39;C&#39;, 3]]
df = pd.DataFrame(data, columns=[&#39;col1&#39;, &#39;col2&#39;])
print(df)
# 출력 결과
#   col1  col2
# 0    A     1
# 1    B     2
# 2    C     3

# 딕셔너리를 사용하여 DataFrame 객체 생성하기

data = {&#39;col1&#39;: [&#39;A&#39;, &#39;B&#39;, &#39;C&#39;], &#39;col2&#39;: [1, 2, 3]}
df = pd.DataFrame(data)
print(df)
# 출력 결과
#   col1  col2
# 0    A     1
# 1    B     2
# 2    C     3

# CSV 파일을 사용하여 DataFrame 객체 생성하기
df = pd.read_csv(&#39;data.csv&#39;)
print(df)</code></pre>
<h2 id="series-객체">Series 객체</h2>
<p>Series 객체는 인덱스와 값으로 이루어진 1차원 데이터를 다루기 위한 객체입니다. Series 객체는 DataFrame 객체에서 열을 선택하여 추출할 수 있습니다.</p>
<pre><code class="language-py"># 리스트를 사용하여 Series 객체 생성하기
import pandas as pd

data = [1, 2, 3]
s = pd.Series(data, index=[&#39;a&#39;, &#39;b&#39;, &#39;c&#39;])
print(s)
# 출력 결과
# a    1
# b    2
# c    3
# dtype: int64

# 딕셔너리를 사용하여 Series 객체 생성하기
data = {&#39;a&#39;: 1, &#39;b&#39;: 2, &#39;c&#39;: 3}
s = pd.Series(data)
print(s)
# 출력 결과
# a    1
# b    2
# c    3
# dtype: int64</code></pre>
<hr>

<h1 id="판다스-기본-사용법">판다스 기본 사용법</h1>
<h3 id="데이터-불러오기">데이터 불러오기</h3>
<p>판다스에서는 다양한 형태의 데이터를 불러올 수 있습니다. 대표적인 데이터 형식으로는 CSV, Excel, SQL 등이 있습니다.</p>
<p>CSV 파일 불러오기</p>
<ul>
<li>CSV(Comma-Separated Values) 파일은 쉼표로 구분된 데이터를 저장하는 파일 형식입니다. 판다스에서는 read_csv() 함수를 이용하여 CSV 파일을 불러올 수 있습니다.</li>
</ul>
<pre><code class="language-py">import pandas as pd

# CSV 파일 불러오기
df = pd.read_csv(&#39;data.csv&#39;)</code></pre>
<p>Excel 파일 불러오기</p>
<ul>
<li>Excel 파일은 스프레드시트 프로그램에서 생성한 데이터를 저장하는 파일 형식입니다. 판다스에서는 read_excel() 함수를 이용하여 Excel 파일을 불러올 수 있습니다.</li>
</ul>
<pre><code class="language-py">import pandas as pd

# Excel 파일 불러오기
df = pd.read_excel(&#39;data.xlsx&#39;)</code></pre>
<h3 id="데이터-살펴보기">데이터 살펴보기</h3>
<p>데이터를 불러온 후에는 데이터의 구조와 내용을 살펴볼 필요가 있습니다. 판다스에서는 다음과 같은 함수를 이용하여 데이터를 살펴볼 수 있습니다.</p>
<p>데이터프레임 정보 확인하기</p>
<ul>
<li><code>info()</code> 함수는 데이터프레임의 정보를 출력합니다. 데이터프레임의 크기, 데이터 타입, 결측치 등의 정보를 확인할 수 있습니다.</li>
</ul>
<pre><code class="language-py"># 데이터프레임 정보 확인하기
df.info()</code></pre>
<p>데이터프레임 일부 데이터 보기</p>
<ul>
<li><code>head()</code> 함수는 데이터프레임의 첫 n행을 출력합니다. n은 인자로 전달될 수 있습니다. 기본값은 5입니다.</li>
</ul>
<pre><code class="language-py"># 데이터프레임 일부 데이터 보기
df.head()</code></pre>
<p>데이터프레임 요약 통계량 보기</p>
<ul>
<li><code>describe()</code> 함수는 데이터프레임의 요약 통계량을 출력합니다. 각 열의 개수, 평균, 표준편차, 최소값, 25% 백분위수, 중앙값, 75% 백분위수, 최대값 등의 정보를 확인할 수 있습니다.</li>
</ul>
<pre><code class="language-py"># 데이터프레임 요약 통계량 보기
df.describe()</code></pre>
<h3 id="데이터-선택하기">데이터 선택하기</h3>
<p>데이터프레임에서는 특정한 열이나 행을 선택하여 데이터를 조회할 수 있습니다.</p>
<p><strong>열 선택하기</strong></p>
<ul>
<li>특정한 열을 선택할 때는 데이터프레임의 열 이름을 사용합니다.</li>
</ul>
<pre><code># 열 선택하기
df[&#39;열 이름&#39;]</code></pre><p>여러 개의 열을 선택할 때는 리스트 형태로 열 이름을 전달합니다.</p>
<pre><code># 여러 개의 열 선택하기
df[[&#39;열 이름 1&#39;, &#39;열 이름 2&#39;, ...]]</code></pre><p><strong>행 선택하기</strong></p>
<ul>
<li>특정한 행을 선택할 때는 <code>loc[]</code> 함수를 사용합니다. <code>loc[]</code> 함수는 행의 이름 또는 인덱스를 사용하여 행을 선택합니다.</li>
</ul>
<pre><code class="language-py"># 행 선택하기
df.loc[행 이름 또는 인덱스]</code></pre>
<p>여러 개의 행을 선택할 때는 리스트 형태로 행 이름 또는 인덱스를 전달합니다.</p>
<pre><code class="language-py"># 여러 개의 행 선택하기
df.loc[[행 이름 또는 인덱스 1, 행 이름 또는 인덱스 2, ...]]</code></pre>
<h3 id="데이터-필터링하기">데이터 필터링하기</h3>
<p>데이터프레임에서는 특정한 조건을 만족하는 데이터만 선택하여 조회할 수 있습니다.</p>
<p><strong>조건 필터링하기</strong></p>
<ul>
<li>조건 필터링은 [] 연산자와 조건식을 이용하여 수행합니다.</li>
</ul>
<pre><code class="language-py"># 조건 필터링하기
df[조건식]</code></pre>
<p>예를 들어, 2010년 이후의 데이터만 선택하고 싶을 때는 다음과 같이 작성할 수 있습니다.</p>
<pre><code class="language-py"># 2010년 이후의 데이터만 선택하기
df[df[&#39;Year&#39;] &gt;= 2010]</code></pre>
<p><strong>논리 연산자 이용하기</strong></p>
<ul>
<li>&amp;(and)와 |(or) 연산자를 이용하여 논리 연산을 수행할 수 있습니다.</li>
</ul>
<pre><code class="language-py"># 논리 연산자 이용하기
df[(조건식 1) &amp; (조건식 2)]
df[(조건식 1) | (조건식 2)]</code></pre>
<p>예를 들어, 2010년 이후이면서 금메달 이상을 획득한 데이터만 선택하고 싶을 때는 다음과 같이 작성할 수 있습니다.</p>
<pre><code class="language-py"># 2010년 이후이면서 금메달 이상을 획득한 데이터만 선택하기
df[(df[&#39;Year&#39;] &gt;= 2010) &amp; (df[&#39;Medal&#39;] == &#39;Gold&#39;)]</code></pre>
<p><strong>isin() 함수 이용하기</strong></p>
<ul>
<li>isin() 함수를 이용하여 특정한 값이 포함된 데이터만 선택할 수 있습니다.</li>
</ul>
<pre><code class="language-py"># isin() 함수 이용하기
df[df[&#39;열 이름&#39;].isin([값1, 값2, ...])]</code></pre>
<p>예를 들어, &#39;KOR&#39;, &#39;USA&#39;, &#39;JPN&#39; 국가의 데이터만 선택하고 싶을 때는 다음과 같이 작성할 수 있습니다.</p>
<pre><code class="language-py"># &#39;KOR&#39;, &#39;USA&#39;, &#39;JPN&#39; 국가의 데이터만 선택하기
df[df[&#39;NOC&#39;].isin([&#39;KOR&#39;, &#39;USA&#39;, &#39;JPN&#39;])]</code></pre>
<h3 id="데이터-그룹화하기">데이터 그룹화하기</h3>
<p>데이터프레임에서는 특정한 기준에 따라 데이터를 그룹화하여 처리할 수 있습니다.</p>
<p><strong>groupby() 함수 이용하기</strong></p>
<ul>
<li>groupby() 함수를 이용하여 특정한 열을 기준으로 데이터를 그룹화할 수 있습니다.</li>
</ul>
<pre><code># groupby() 함수 이용하기
df.groupby(&#39;열 이름&#39;)</code></pre><p>groupby() 함수로 그룹화한 데이터는 각 그룹에 대한 정보를 담고 있는 객체입니다. 이를 바탕으로 다양한 처리를 수행할 수 있습니다.</p>
<p><strong>집계 함수 이용하기</strong></p>
<ul>
<li>그룹화된 데이터에 대해 집계 함수를 이용하여 다양한 처리를 수행할 수 있습니다.</li>
</ul>
<pre><code># 집계 함수 이용하기
그룹화된 데이터.집계함수()</code></pre><p>그룹화된 데이터에 대해 적용할 수 있는 집계 함수는 다양합니다. 일반적으로 사용되는 집계 함수는 다음과 같습니다.
<img src="https://velog.velcdn.com/images/su_hwan/post/0e6828b4-ed1e-43a2-b235-2d61dea2eecd/image.png" alt=""></p>
<h3 id="데이터-정렬하기">데이터 정렬하기</h3>
<p>데이터프레임에서는 특정한 열을 기준으로 데이터를 정렬할 수 있습니다. 기본적으로 오름차순으로 정렬되며, 내림차순으로 정렬하려면 ascending=False 옵션을 추가합니다.</p>
<pre><code># 오름차순으로 정렬하기
df.sort_values(&#39;열 이름&#39;)

# 내림차순으로 정렬하기
df.sort_values(&#39;열 이름&#39;, ascending=False)</code></pre><h3 id="index-초기화하기">Index 초기화하기</h3>
<p>데이터프레임에서는 reset_index() 함수를 이용하여 인덱스를 초기화할 수 있습니다. 이때 기존 인덱스는 열로 추가됩니다.</p>
<pre><code># 인덱스 초기화하기
df.reset_index()</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Matplotlib]]></title>
            <link>https://velog.io/@su_hwan/Matplotlib-Pandas</link>
            <guid>https://velog.io/@su_hwan/Matplotlib-Pandas</guid>
            <pubDate>Sun, 06 Apr 2025 13:14:38 GMT</pubDate>
            <description><![CDATA[<h1 id="matplotlib">Matplotlib</h1>
<h2 id="matplotlib-설치">matplotlib 설치</h2>
<pre><code>pip install matplotlib</code></pre><ul>
<li>import는 관례적으로 다음과 같이 한다.</li>
</ul>
<pre><code class="language-py">from matplotlib import pyplot as plt</code></pre>
<pre><code class="language-py">import numpy as np
import pandas as pd</code></pre>
<hr>

<h2 id="font-설정">Font 설정</h2>
<p>아무 설정을 하지 않으면 한글이 깨지는 경우가 많다.</p>
<pre><code class="language-py">RuntimeWarning: Glyph 44256 missing from current font.
  font.set_text(s, 0.0, flags=flags)</code></pre>
<p>보통 다음과 같이 설정해주면 된다.</p>
<pre><code class="language-py">from matplotlib import rcParams
rcParams[&quot;font.family&quot;] = &quot;Malgun Gothic&quot;
rcParams[&quot;axes.unicode_minus&quot;] = False</code></pre>
<p>참고로 설정 가능한 폰트 목록을 확인하고 싶다면 다음 코드를 실행해 보자.</p>
<pre><code class="language-py">import matplotlib.font_manager
fpaths = matplotlib.font_manager.findSystemFonts()
font_names = []
for i in fpaths:
    f = matplotlib.font_manager.get_font(i)
    font_names.append(f.family_name)

print(font_names[:5])

for fn in font_names:
    if &#39;malgun&#39; in fn.lower():
        print(fn)</code></pre>
<blockquote>
<p>예제 데이터</p>
</blockquote>
<pre><code>data = pd.DataFrame(data={
    &#39;A&#39;: [1,4,3,6,5,8,7,9],
    &#39;B&#39;: [6,5,7,8,9,9,8,9],
    &#39;C&#39;: [8.8,7.7,6.6,5.5,4.4,3.3,2.2,1.1]
})</code></pre><hr>

<h2 id="그래프-종류">그래프 종류</h2>
<h3 id="산점도scatter">산점도(scatter)</h3>
<pre><code class="language-py">plt.scatter(x=[1,2,3], y=[4,5,6])</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/131e8745-e9a2-4c26-86b5-8e0c88025c3c/image.png" alt=""></p>
<h3 id="선-그래프plot">선 그래프(plot)</h3>
<pre><code class="language-py">plt.plot(data.index, data[&#39;B&#39;])
# 하나만 입력하면 기본 index로 그려진다.
plt.plot(data[&#39;C&#39;])</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/fed600d7-d1e7-4b2c-ac9c-cdb717e24e8c/image.png" alt=""></p>
<pre><code class="language-py"># 한 번의 plot() 호출로 여러 개를 그릴 수 있다. 순서는 x, y, fmt 순으로 입력할 수 있다.
t = np.arange(0., 5., 0.2)
plt.plot(t, t, &#39;r--&#39;, t, t**2, &#39;bs-&#39;, t, t**3, &#39;g^-.&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/b19c77b8-5ee1-4caa-bc26-f98505a08872/image.png" alt=""></p>
<p>plot()은 list나 DataFrame뿐 아니라 dictionary도 그래프로 나타낼 수 있다.</p>
<pre><code class="language-py">data_dict = {&#39;x&#39;: [1, 2, 3, 4, 5], &#39;y&#39;: [2, 3, 5, 7, 11]}
plt.plot(&#39;x&#39;, &#39;y&#39;, data=data_dict)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/9e011e0d-b4c5-4d3b-8bbf-54ed6564063b/image.png" alt=""></p>
<h3 id="막대-그래프bar">막대 그래프(bar)</h3>
<pre><code class="language-py">plt.bar(data.index, data[&#39;B&#39;])</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/07e8e0fd-687c-4113-b305-be738017a8ea/image.png" alt=""></p>
<p>2개 그룹 동시 표시
stack해서 사용하는 방법은 다음과 같이 bottom 옵션을 사용한다.</p>
<pre><code class="language-py">p1 = plt.bar(data.index, data[&#39;B&#39;], color=&#39;red&#39;, alpha=0.7)
p2 = plt.bar(data.index, data[&#39;C&#39;], color=&#39;blue&#39;, alpha=0.7, bottom=data[&#39;B&#39;])
# plot이 여러 개인 경우 plt.legend()는 그냥 넣으면 legend가 출력되지 않는다.</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/c25fb1f6-7330-48cf-baa7-638f2d9f2432/image.png" alt=""></p>
<p>나란히 놓는 방법은 bar의 width 옵션과 x좌표를 조정하면 된다.</p>
<pre><code>p1 = plt.bar(data.index-0.2, data[&#39;B&#39;], color=&#39;red&#39;, alpha=0.7, width=0.4)
p2 = plt.bar(data.index+0.2, data[&#39;C&#39;], color=&#39;blue&#39;, alpha=0.7, width=0.4)
plt.legend((p1, p2), (&#39;B&#39;, &#39;C&#39;), fontsize=12)</code></pre><p><img src="https://velog.velcdn.com/images/su_hwan/post/a505b97c-648c-4e52-9c56-678d39cb41f6/image.png" alt=""></p>
<h3 id="boxplot">boxplot</h3>
<pre><code class="language-py">x = np.random.normal(50, 5, 100)
plt.boxplot(x)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/1c67fcaa-8971-43f5-a372-ec938118257a/image.png" alt=""></p>
<p>여러 boxplot을 한 번에 그리려면 리스트에 담아서 전달한다.</p>
<pre><code class="language-py">x1 = np.random.normal(15, 5, 500)
x2 = np.random.normal(10, 10, 100)
plt.boxplot([x1, x2])
plt.xticks([1, 2], [&quot;x1&quot;, &quot;x2&quot;])</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/3f7bbaca-63d2-4b5f-a1a3-1dec2af2acd4/image.png" alt=""></p>
<h3 id="histogram">Histogram</h3>
<p>구간 개수는 bins으로 설정한다.</p>
<pre><code class="language-py">x = np.random.normal(10, 2, 100)
plt.hist(x, bins=10)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/837652bd-0299-4a60-b8ff-f7a8cacb83b2/image.png" alt=""></p>
<hr>

<h2 id="스타일-설정">스타일 설정</h2>
<ul>
<li>dashes - 직접 dash line을 조작할 수 있다.</li>
<li>markevery - 마커를 만들 샘플을 추출할 수 있다. 옵션값이 5(int)면 5개의 샘플마다, float이면 상대적 거리에 따라 추출한다.</li>
<li>visible - 선을 안 보이게 할 수 있다.</li>
<li>fillstyle - 마커를 채우는 방식을 설정할 수 있다. full, left, right, bottom, top, none 가능</li>
</ul>
<h3 id="format-stringfmt">format string(fmt)</h3>
<p>색상, 마커, 선 스타일을 쉽게 지정할 수 있다.</p>
<pre><code class="language-py">plt.plot(data[&#39;A&#39;], &#39;b.-&#39;, label=&#39;A&#39;)
plt.plot(data[&#39;B&#39;], &#39;cv--&#39;, label=&#39;B&#39;) 
plt.plot(data[&#39;C&#39;], &#39;m|:&#39;, label=&#39;C&#39;)
plt.legend()</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/ff670784-a570-4c20-9c58-409497d152ce/image.png" alt=""></p>
<h3 id="색상color">색상(color)</h3>
<pre><code class="language-py">plt.plot(data[&#39;B&#39;], label=&#39;B&#39;, color=&#39;red&#39;)
plt.plot(data[&#39;C&#39;], label=&#39;C&#39;, color=&#39;green&#39;)
plt.legend()</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/5fdb6f69-7d7d-4b50-adce-d8e5a40c12a9/image.png" alt=""></p>
<h3 id="선-스타일linestyle-두께linewidth">선 스타일(linestyle), 두께(linewidth)</h3>
<ul>
<li>선 스타일은 linestyle parameter를 전달하며 기본값인 solid와 dashed, dotted, dashdot이 있다.</li>
<li>선 두께는 linewidth로 설정하고 기본값은 1.5이다.<pre><code class="language-py">plt.plot(data[&#39;A&#39;], label=&#39;A&#39;, linestyle=&#39;dashed&#39;, linewidth=2)
plt.plot(data[&#39;B&#39;], label=&#39;B&#39;, linestyle=&#39;dotted&#39;, linewidth=3)
plt.plot(data[&#39;C&#39;], label=&#39;C&#39;, linestyle=&#39;dashdot&#39;, linewidth=4)
plt.legend()</code></pre>
<img src="https://velog.velcdn.com/images/su_hwan/post/466d9310-2f68-415b-9ff5-010a4e48fcb9/image.png" alt=""></li>
</ul>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/7c29fe7b-10ae-410d-b75a-1181fdd7790b/image.png" alt="">
수치로 직접 지정할 수 있다. 참고로 (0, (1, 1))은 dotted, (0, (5, 5))는 dashed, (0, (3, 5, 1, 5))는 dashdot과 같다.</p>
<pre><code class="language-py">plt.plot(data[&#39;A&#39;], label=&#39;A&#39;, linestyle=(0, (1,1)), linewidth=2)
plt.plot(data[&#39;B&#39;], label=&#39;B&#39;, linestyle=(0, (4,1)), linewidth=3)
plt.plot(data[&#39;C&#39;], label=&#39;C&#39;, linestyle=(0, (1,4)), linewidth=4)
plt.legend()</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/94dd1c79-eab6-4d81-8451-7ea8d6cb6acf/image.png" alt=""></p>
<h3 id="bar-스타일width-color-linewidth">bar 스타일(width, color, linewidth)</h3>
<p>bar의 두께를 설정할 수 있다. 각 데이터마다 다른 값을 주면 각각 다르게 설정할 수도 있다.</p>
<pre><code class="language-py">plt.bar(data.index, data[&#39;A&#39;], width=0.4, color=[&quot;red&quot;, &quot;blue&quot;, &quot;green&quot;, &quot;purple&quot;, &quot;red&quot;, &quot;blue&quot;, &quot;green&quot;, &quot;purple&quot;], linewidth=2.5)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/f1601705-4c06-417f-b2dc-16f42d7201cd/image.png" alt=""></p>
<h3 id="마커marker">마커(marker)</h3>
<p>각 data point마다 marker를 표시할 수 있다.</p>
<ul>
<li>점: <code>.,</code> 원: <code>o</code>, 사각형: <code>s</code>, 별: <code>*</code>, 다이아몬드:<code>D, d</code></li>
<li>marker의 사이즈도 <code>markersize</code> parameter로 지정할 수 있다.</li>
<li>산점도(scatter)의 마커 크기는 <code>s</code> parameter로 설정한다. 단 크기가 10배 차이난다.</li>
</ul>
<pre><code class="language-py">plt.plot(data[&#39;A&#39;], label=&#39;A&#39;, marker=&#39;o&#39;, markersize=4)
plt.plot(data[&#39;B&#39;], label=&#39;B&#39;, marker=&#39;s&#39;, markersize=8)
plt.scatter(data.index, data[&#39;C&#39;], label=&#39;C&#39;, marker=&#39;.&#39;, s=120, color=&#39;red&#39;)
plt.legend()</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/d66ed2eb-39ac-48f2-be55-33f1559330dd/image.png" alt="">
<img src="https://velog.velcdn.com/images/su_hwan/post/11ce40c8-8a54-48ce-b06a-d80cc07d5831/image.png" alt=""></p>
<h3 id="투명도alpha">투명도(alpha)</h3>
<p>데이터가 너무 많은 경우 투명도를 조절하면 좀 더 잘 보이게 할 수 있다.</p>
<pre><code class="language-py">import numpy as np
x = np.random.normal(0, 1, 16384)
y = np.random.normal(0, 1, 16384)
plt.scatter(x, y, alpha = 0.04, color=&#39;purple&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/3ec2e413-565d-4d59-8a3d-91d85658bafd/image.png" alt=""></p>
<hr>

<h2 id="그래프-전체-설정">그래프 전체 설정</h2>
<h3 id="그래프-크기-설정">그래프 크기 설정</h3>
<pre><code class="language-py">plt.figure(figsize=(6, 3))
plt.scatter(x=data.index, y=data[&#39;A&#39;])</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/5e571bd8-2d7e-40ed-bb39-1e7ca8b80a98/image.png" alt=""></p>
<h3 id="그래프-제목title">그래프 제목(title)</h3>
<pre><code class="language-py">plt.scatter(x=data.index, y=data[&#39;A&#39;])
plt.title(&quot;Gorio&quot;)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/1e774660-f163-4188-be2f-cf25651b9ec9/image.png" alt=""></p>
<h3 id="범례-설정legend">범례 설정(legend)</h3>
<p>기본적으로 <code>plot(label=...)</code> 등으로 label을 등록하면,<code>plt.legend()</code>로 등록된 label들을 표시해주는 개념이다.</p>
<pre><code class="language-py">plt.scatter(x=data.index, y=data[&#39;A&#39;], label=&#39;Gorio&#39;)
plt.legend()</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/204b3590-e146-4b5b-b80b-311b9d743968/image.png" alt=""></p>
<p>legend 위치는 그래프를 가리지 않는 위치에 임의로 생성된다. 위치를 지정하려면 <code>loc</code> parameter를 설정한다.</p>
<pre><code class="language-py">plt.scatter(x=data.index, y=data[&#39;A&#39;], label=&#39;Gorio&#39;)
plt.legend(loc=&#39;lower right&#39;)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/3149c3f9-817d-4121-9cbd-a77b57ebecf7/image.png" alt=""></p>
<p><strong>가능한 옵션</strong>
<code>left</code>, <code>center</code>, <code>right</code>, <code>upper left</code>, <code>upper center</code>, <code>upper right</code>, <code>lower left</code>, <code>lower center</code>, <code>lower right</code></p>
<p><code>loc=(0.5, 0.5)</code>와 같이 직접 수치를 지정할 수도 있다. 
<code>loc=(0.0, 0.0)</code>은 왼쪽 아래, <code>loc=(1.0, 1.0)</code>은 오른쪽 위이다.</p>
<p><code>legend()</code> 메서드에서도 label을 직접 등록하여 표시할 수 있다.</p>
<pre><code class="language-py">p1 = plt.bar(data.index-0.2, data[&#39;A&#39;], color=&#39;red&#39;, alpha=0.7, width=0.4)
p2 = plt.bar(data.index+0.2, data[&#39;C&#39;], color=&#39;blue&#39;, alpha=0.7, width=0.4)
plt.legend((p1, p2), (&#39;A&#39;, &#39;C&#39;), fontsize=12)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/9c4619ba-3574-4dff-925f-8bb6384e44ab/image.png" alt=""></p>
<p><code>ncol</code> 옵션으로 범례에 표시되는 텍스트의 열 개수를 지정할 수 있다.</p>
<pre><code class="language-py">p1 = plt.bar(data.index-0.2, data[&#39;A&#39;], color=&#39;red&#39;, alpha=0.7, width=0.4, label=&#39;A&#39;)
p2 = plt.bar(data.index+0.2, data[&#39;C&#39;], color=&#39;blue&#39;, alpha=0.7, width=0.4, label=&#39;C&#39;)
plt.legend(ncol=2)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/ad4fb18f-178b-4efc-a0db-7bcee428b163/image.png" alt=""></p>
<hr>

<h2 id="x-y축-설정">x, y축 설정</h2>
<h3 id="축-제목xlabel-ylabel">축 제목(xlabel, ylabel)</h3>
<pre><code class="language-py">plt.scatter(x=data.index, y=data[&#39;A&#39;])
plt.xlabel(&quot;x axis&quot;)
plt.ylabel(&quot;y axis&quot;)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/151c789b-0bee-4333-a912-8dc5eaeaaf93/image.png" alt=""></p>
<p>축 제목과 축 간 거리는 <code>labelpad</code>로 조정한다.</p>
<pre><code class="language-py">plt.plot(data.index, data[&#39;A&#39;])
plt.xlabel(&quot;x axis&quot;, labelpad=20)
plt.ylabel(&quot;y axis&quot;, labelpad=-1)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/12128e5c-5b2d-4de0-b275-e1d7f9404047/image.png" alt=""></p>
<h3 id="축-범위-설정xlim-ylim-axis">축 범위 설정(xlim, ylim, axis)</h3>
<p>각 함수를 호출하면 return value로 x축, y축, x 및 y축의 최솟값/최댓값을 얻을 수 있다.</p>
<pre><code class="language-py">plt.scatter(x=data.index, y=data[&#39;A&#39;])
xmin, xmax = plt.xlim(left=0, right=10)
ymin, ymax = plt.ylim(bottom=0, top=10)
# 아래 한 줄로도 쓸 수 있다.
# xmin, xmax, ymin, ymax = plt.axis([0,10,0,10])</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/276a7bf5-c65f-487a-8564-8b74aaa8f9e4/image.png" alt=""></p>
<p><code>left, right, bottom, top</code> 중 설정하지 않은 값은 데이터 최솟값/최댓값에 맞춰 자동으로 설정된다.</p>
<p>범위를 수치로 직접 설정하는 대신 그래프의 비율이나 scale을 조정할 수 있다.
<code>scaled</code>의 경우 다음과 같이 x축 간격과 y축 간격의 scale이 같아진다.</p>
<pre><code class="language-py">plt.scatter(x=data.index, y=data[&#39;A&#39;])
xmin, xmax, ymin, ymax = plt.axis(&#39;scaled&#39;)
# (-0.35000000000000003, 8.450000000000001, 0.6, 9.4)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/9f4d27ef-ef5f-42f1-b5ce-e504f16d14b4/image.png" alt=""></p>
<p> 옵션: <code>&#39;on&#39; | &#39;off&#39; | &#39;equal&#39; | &#39;scaled&#39; | &#39;tight&#39; | &#39;auto&#39; | &#39;normal&#39; | &#39;image&#39; | &#39;square&#39;</code></p>
<h3 id="눈금-값-설정xticks-yticks">눈금 값 설정(xticks, yticks)</h3>
<p><code>ticks</code>에 tick의 위치를 지정하고, <code>labels</code>에 원하는 tick 이름을 지정할 수 있다.</p>
<pre><code class="language-py">plt.scatter(x=data.index, y=data[&#39;A&#39;])
plt.xticks(ticks=[0,3,6], labels=[&#39;zero&#39;, &#39;three&#39;, &#39;six&#39;])
plt.ylim(bottom=0, top=10)</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/d947080b-42b0-4625-9840-48a26ba77fa4/image.png" alt=""></p>
<p>막대그래프는 기본적으로 막대의 중심 좌표를 기준으로 계산하지만 <code>align</code> parameter를 주면 왼쪽 위치로 계산할 수 있다.</p>
<pre><code class="language-py">plt.bar(data.index, data[&#39;B&#39;], align=&#39;edge&#39;)
plt.xticks([0,3,6], [&#39;zero&#39;, &#39;three&#39;, &#39;six&#39;])</code></pre>
<p><img src="https://velog.velcdn.com/images/su_hwan/post/14ff9881-f579-47fa-8a75-c9d6a32dac21/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[화면 구성]]></title>
            <link>https://velog.io/@su_hwan/%ED%99%94%EB%A9%B4-%EA%B5%AC%EC%84%B1</link>
            <guid>https://velog.io/@su_hwan/%ED%99%94%EB%A9%B4-%EA%B5%AC%EC%84%B1</guid>
            <pubDate>Wed, 02 Apr 2025 01:47:28 GMT</pubDate>
            <description><![CDATA[<h2 id="🏠웹사이트-흐름">🏠웹사이트 흐름</h2>
<h3 id="1-파일-구조">1. 파일 구조</h3>
<pre><code>frontend/src/app/
├── api/
│   └── property.ts  (API 연동)
├── components/
│   └── ChatBot.tsx    (챗봇 컴포넌트 - 추후 개발)
├── property/
│   └── [id]/
│       └── page.tsx   (매물 상세 페이지)
└── page.tsx           (메인 페이지)

backend/
├── app/
│   ├── main.py          # FastAPI 서버 실행 (진입점)
│   ├── routes.py        # 매물 조회 + 전세 사기 분석 API
│   ├── database.py      # 간단한 데이터 저장 (리스트 활용)
│   ├── scam_check.py    # 전세 사기 AI 분석 (랜덤 점수 부여)
│   ├── schemas.py       # API 요청/응답 데이터 구조
├── requirements.txt     # 필요한 Python 패키지 목록
├── .env                 # 환경 변수 (DB URL 등)
├── README.md</code></pre><h3 id="2-주요-페이지-흐름">2. 주요 페이지 흐름</h3>
<h4 id="21-홈-화면-pagetsx">2.1. 홈 화면 (page.tsx)</h4>
<ul>
<li><p><strong>구성 요소:</strong></p>
<ul>
<li><strong>매물 검색창:</strong><ul>
<li>지역, 가격, 면적 등 검색 조건 입력</li>
</ul>
</li>
<li><strong>매물 리스트:</strong><ul>
<li>검색 조건에 맞는 매물 목록 표시</li>
<li>각 매물은 간략한 정보 (이름, 가격, 사진) 표시</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>동작:</strong></p>
<ol>
<li><strong>사용자 접속:</strong> <code>page.tsx</code> 로드</li>
<li><strong>매물 검색:</strong> 사용자가 검색 조건 입력 후 검색</li>
<li><strong>매물 리스트 갱신:</strong> 검색 조건에 맞는 매물 리스트 표시</li>
<li><strong>매물 선택:</strong> 사용자가 특정 매물 클릭</li>
<li><strong>상세 페이지 이동:</strong> <code>property/[id]/page.tsx</code>로 이동 (해당 매물의 <code>id</code>를 파라미터로 전달)</li>
</ol>
</li>
</ul>
<h4 id="22-매물-상세-페이지-propertyidpagetsx">2.2. 매물 상세 페이지 (property/[id]/page.tsx)</h4>
<ul>
<li><p><strong>구성 요소:</strong></p>
<ul>
<li><strong>매물 기본 정보:</strong><ul>
<li>매물 이름, 가격, 면적, 층수, 준공 연도, 사진</li>
<li>매물 상세 설명</li>
</ul>
</li>
<li><strong>AI 기반 전세 사기 위험 분석 결과:</strong><ul>
<li><strong>전세 사기 위험 점수 (0~100):</strong> FastAPI에서 계산</li>
<li><strong>위험 레벨:</strong> 점수에 따라 &quot;안전&quot;, &quot;주의&quot;, &quot;위험&quot; 표시 (색상으로 구분)</li>
<li><strong>상세 분석 내용:</strong> (추후 개발)</li>
</ul>
</li>
<li><strong>관심 목록 추가 버튼:</strong><ul>
<li>매물을 관심 목록에 추가/제거 가능</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>AI 기반 전세 사기 위험 분석 (상세)</strong></p>
<table>
<thead>
<tr>
<th>위험 점수</th>
<th>레벨</th>
<th>색상</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td>0 ~ 30</td>
<td>안전</td>
<td>초록색</td>
<td>전세 사기 위험이 낮은 안전한 매물입니다.</td>
</tr>
<tr>
<td>31 ~ 70</td>
<td>주의</td>
<td>주황색</td>
<td>전세 사기 위험이 중간 정도인 매물입니다. 계약 시 주의가 필요합니다.</td>
</tr>
<tr>
<td>71 ~ 100</td>
<td>위험</td>
<td>빨간색</td>
<td>전세 사기 위험이 높은 매물입니다. 계약을 신중하게 고려하십시오.</td>
</tr>
</tbody></table>
</li>
<li><p><strong>동작:</strong></p>
<ol>
<li><strong>페이지 로드:</strong> <code>property/[id]/page.tsx</code> 로드 (매물 <code>id</code> 파라미터 전달)</li>
<li><strong>API 호출:</strong> 백엔드 API (<code>api/property.ts</code>)를 호출하여 해당 매물의 상세 정보 및 AI 분석 결과를 가져옴</li>
<li><strong>정보 표시:</strong> 매물 기본 정보 및 AI 분석 결과 화면에 표시</li>
<li><strong>관심 목록 추가/제거:</strong> 사용자가 &quot;관심 목록 추가&quot; 버튼 클릭 시, 로컬 스토리지 또는 API를 통해 관심 목록 관리</li>
</ol>
</li>
</ul>
<h3 id="3-데이터-흐름">3. 데이터 흐름</h3>
<ol>
<li><strong>매물 검색:</strong><ul>
<li>사용자가 <code>page.tsx</code>에서 검색 조건 입력</li>
<li>검색 조건과 함께 백엔드 API (<code>api/property.ts</code>) 호출</li>
<li>백엔드는 검색 조건에 맞는 매물 데이터 반환</li>
<li><code>page.tsx</code>는 반환된 데이터를 화면에 표시</li>
</ul>
</li>
<li><strong>매물 상세 정보 및 AI 분석 결과:</strong><ul>
<li>사용자가 특정 매물을 클릭 (<code>property/[id]/page.tsx</code> 로드)</li>
<li><code>property/[id]/page.tsx</code>는 해당 매물 <code>id</code>와 함께 백엔드 API (<code>api/property.ts</code>) 호출</li>
<li>백엔드는 매물 상세 정보 및 AI 분석 결과 반환</li>
<li><code>property/[id]/page.tsx</code>는 반환된 데이터를 화면에 표시<hr>

</li>
</ul>
</li>
</ol>
<p>홈
<img src="https://velog.velcdn.com/images/su_hwan/post/0e5851c4-629d-4c74-ac47-6d735ea22192/image.png" alt=""></p>
<p>상세 페이지 + AI 분석
<img src="https://velog.velcdn.com/images/su_hwan/post/bebf0356-ae28-4ec0-9545-7e8527036173/image.png" alt=""></p>
]]></description>
        </item>
    </channel>
</rss>