<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>you_n_sunny.log</title>
        <link>https://velog.io/</link>
        <description>2026년 화이팅!!!</description>
        <lastBuildDate>Sun, 22 Feb 2026 14:52:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>you_n_sunny.log</title>
            <url>https://velog.velcdn.com/images/you_n_sunny/profile/948280de-36df-4f33-bcdc-9c3082556d67/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. you_n_sunny.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/you_n_sunny" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[기초] DataBase]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-DataBase</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-DataBase</guid>
            <pubDate>Sun, 22 Feb 2026 14:52:00 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/227e2781-da7e-4a97-ac5a-b7b542e249fe/image.png" alt=""></p>
<h3 id="11-database는-무엇인가">1.1 Database는 무엇인가</h3>
<p><strong>데이터베이스(Database, DB)</strong>는 여러 사람이 공유할 목적으로 체계화하여 통합, 관리하는 데이터의 집합입니다. 단순히 데이터를 저장하는 것을 넘어, 효율적으로 관리하고 필요할 때 신속하게 접근할 수 있도록 지원하는 정보 저장소입니다.  </p>
<h4 id="데이터베이스의-정의와-역할">데이터베이스의 정의와 역할</h4>
<p><strong>핵심 기능</strong></p>
<ul>
<li><strong>체계적 저장</strong>: 데이터를 구조화하여 중복 없이 저장</li>
<li><strong>효율적 검색</strong>: 필요한 데이터를 빠르게 찾아 제공</li>
<li><strong>데이터 수정</strong>: 삽입(Insert), 삭제(Delete), 갱신(Update) 지원</li>
<li><strong>동시 접근</strong>: 여러 사용자가 동시에 데이터 이용 가능</li>
</ul>
<p><strong>실생활 예시</strong></p>
<ul>
<li><strong>은행 시스템</strong>: 고객 정보, 거래 내역, 계좌 정보 관리</li>
<li><strong>전자상거래</strong>: 상품 정보, 주문 내역, 재고 관리</li>
<li><strong>소셜 미디어</strong>: 사용자 프로필, 게시물, 댓글 저장</li>
<li><strong>병원 시스템</strong>: 환자 기록, 진료 내역, 처방전 관리 </li>
</ul>
<h4 id="데이터베이스의-특징">데이터베이스의 특징</h4>
<p><strong>1. 실시간 접근성 (Real-Time Accessibility)</strong></p>
<ul>
<li>사용자의 요구에 실시간으로 응답</li>
<li>즉각적인 데이터 조회 및 처리 가능</li>
</ul>
<p><strong>2. 계속적인 변화 (Continuous Evolution)</strong></p>
<ul>
<li>새로운 데이터의 삽입(Insert)</li>
<li>불필요한 데이터의 삭제(Delete)</li>
<li>기존 데이터의 갱신(Update)</li>
<li>항상 최신 상태의 데이터 유지 </li>
</ul>
<p><strong>3. 동시 공용 (Concurrent Sharing)</strong></p>
<ul>
<li>다수의 사용자가 동시에 같은 데이터 이용</li>
<li>동시성 제어를 통한 데이터 일관성 유지</li>
</ul>
<p><strong>4. 내용에 의한 참조 (Content Reference)</strong></p>
<ul>
<li>데이터의 물리적 위치가 아닌 내용으로 검색</li>
<li>사용자 요구에 따른 데이터 내용으로 접근 </li>
</ul>
<h4 id="데이터베이스-관리-시스템-dbms">데이터베이스 관리 시스템 (DBMS)</h4>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/5c0ec27b-40ae-4a16-b459-1fbd0bce4a53/image.png" alt=""></p>
<p><strong>DBMS의 정의</strong></p>
<ul>
<li>Database Management System의 약자</li>
<li>데이터베이스를 관리하고 운영하는 소프트웨어</li>
<li>사용자와 데이터베이스 사이의 인터페이스 역할 </li>
</ul>
<p><strong>DBMS의 주요 기능</strong></p>
<ul>
<li><strong>데이터 정의</strong>: 데이터 구조 정의 및 스키마 관리</li>
<li><strong>데이터 조작</strong>: 검색, 삽입, 수정, 삭제 기능 제공</li>
<li><strong>데이터 제어</strong>: 접근 권한 관리 및 보안 유지</li>
<li><strong>데이터 공유</strong>: 여러 사용자의 동시 접근 지원</li>
<li><strong>데이터 무결성</strong>: 데이터의 정확성과 일관성 보장</li>
</ul>
<p><strong>DBMS의 장점</strong></p>
<ul>
<li>데이터 중복 최소화</li>
<li>데이터 일관성 유지</li>
<li>데이터 보안 강화</li>
<li>데이터 표준화</li>
<li>프로그램과 데이터의 독립성 </li>
</ul>
<h4 id="데이터베이스의-발전-역사">데이터베이스의 발전 역사</h4>
<p><strong>1세대: 계층형 데이터베이스 (Hierarchical DB)</strong></p>
<ul>
<li>1960년대 등장</li>
<li>트리(Tree) 구조로 데이터 구성</li>
<li>부모-자식 관계로 데이터 연결</li>
<li><strong>단점</strong>: 구조 변경 어려움, 데이터 검색 비효율적</li>
<li>현재는 거의 사용되지 않음 </li>
</ul>
<p><strong>2세대: 망형 데이터베이스 (Network DB)</strong></p>
<ul>
<li>계층형 DB의 문제점 개선</li>
<li>네트워크처럼 데이터 간 자유로운 연결</li>
<li><strong>단점</strong>: 복잡한 구조로 프로그래밍 어려움</li>
<li>현재는 거의 사용되지 않음 </li>
</ul>
<p><strong>3세대: 관계형 데이터베이스 (Relational DB)</strong></p>
<ul>
<li>1970년대 에드가 F. 커드(Edgar F. Codd)가 제안</li>
<li>테이블(Table) 형태로 데이터 저장</li>
<li>SQL을 통한 데이터 관리</li>
<li>현재 가장 널리 사용되는 형태 </li>
</ul>
<p><strong>4세대: NoSQL 데이터베이스</strong></p>
<ul>
<li>2000년대 이후 빅데이터 시대에 등장</li>
<li>비정형 데이터 처리에 적합</li>
<li>수평적 확장성 우수</li>
<li>다양한 데이터 모델 지원 </li>
</ul>
<h3 id="12-database-구성-요소">1.2 Database 구성 요소</h3>
<p>데이터베이스는 여러 구성 요소가 유기적으로 결합하여 데이터를 효과적으로 저장하고 관리합니다. 각 구성 요소의 역할과 관계를 이해하는 것이 중요합니다. </p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/382afba3-1716-469d-87dd-2be096f72475/image.png" alt=""></p>
<h4 id="테이블-table">테이블 (Table)</h4>
<p><strong>정의</strong>: 데이터베이스에서 데이터를 저장하는 기본 단위</p>
<p><strong>구조</strong></p>
<ul>
<li><strong>행(Row)</strong>: 개별 데이터 레코드를 나타냄 (튜플, Tuple)</li>
<li><strong>열(Column)</strong>: 데이터의 속성을 정의 (필드, Field)</li>
<li>행과 열이 교차하는 지점에 실제 데이터 값 저장</li>
</ul>
<p><strong>예시: 고객 정보 테이블</strong></p>
<pre><code>고객ID | 이름    | 전화번호      | 주소
-------|---------|--------------|------------------
001    | 홍길동  | 010-1234-5678| 서울시 강남구
002    | 김철수  | 010-9876-5432| 부산시 해운대구
003    | 이영희  | 010-5555-1234| 대구시 수성구</code></pre><h4 id="레코드-record">레코드 (Record)</h4>
<p><strong>정의</strong>: 테이블의 각 행(Row)을 의미하며, 하나의 완전한 데이터 단위</p>
<p><strong>특징</strong></p>
<ul>
<li>각 열에 해당하는 데이터 값을 포함</li>
<li>테이블의 스키마에 정의된 구조를 따름</li>
<li>고유 식별자(Primary Key)로 구분 가능</li>
</ul>
<p><strong>예시</strong></p>
<ul>
<li>고객 테이블의 한 레코드: (001, 홍길동, 010-1234-5678, 서울시 강남구)</li>
<li>각 필드가 모여 하나의 완전한 고객 정보 구성 </li>
</ul>
<h4 id="필드-field--속성-attribute">필드 (Field) / 속성 (Attribute)</h4>
<p><strong>정의</strong>: 테이블의 열(Column)을 의미하며, 데이터의 특정 속성을 나타냄</p>
<p><strong>필드의 구성 요소</strong></p>
<ul>
<li><strong>필드명</strong>: 속성의 이름 (예: 이름, 전화번호)</li>
<li><strong>데이터 타입</strong>: 저장할 데이터의 형식 (문자열, 숫자, 날짜 등)</li>
<li><strong>제약 조건</strong>: 데이터 입력 규칙 (NOT NULL, UNIQUE 등)</li>
</ul>
<p><strong>주요 데이터 타입</strong></p>
<ul>
<li><strong>문자형</strong>: VARCHAR, CHAR, TEXT</li>
<li><strong>숫자형</strong>: INT, BIGINT, DECIMAL, FLOAT</li>
<li><strong>날짜/시간</strong>: DATE, TIME, DATETIME, TIMESTAMP</li>
<li><strong>논리형</strong>: BOOLEAN</li>
</ul>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/942cd221-0d03-4700-a416-92470744929e/image.png" alt=""></p>
<h4 id="엔티티-entity">엔티티 (Entity)</h4>
<p><strong>정의</strong>: 독립적으로 존재하면서 고유하게 식별 가능한 실세계의 객체나 개념</p>
<p><strong>엔티티의 특징</strong></p>
<ul>
<li>사람, 장소, 사물, 사건 등을 나타냄</li>
<li>여러 속성(Attribute)으로 구성</li>
<li>데이터베이스에서 테이블로 구현됨</li>
</ul>
<p><strong>엔티티 예시</strong></p>
<ul>
<li><strong>고객 엔티티</strong>: 고객ID, 이름, 전화번호, 주소</li>
<li><strong>상품 엔티티</strong>: 상품ID, 상품명, 가격, 재고량</li>
<li><strong>주문 엔티티</strong>: 주문ID, 주문일자, 고객ID, 총액 </li>
</ul>
<h4 id="스키마-schema">스키마 (Schema)</h4>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/e53e4507-d592-4ccc-a29f-ce35d1900fec/image.png" alt=""></p>
<p><strong>정의</strong>: 데이터베이스의 구조와 제약 조건을 정의한 설계도</p>
<p><strong>스키마의 종류</strong></p>
<p><strong>1. 외부 스키마 (External Schema)</strong></p>
<ul>
<li>사용자 관점의 데이터베이스 구조</li>
<li>개별 사용자나 응용 프로그램이 보는 뷰(View)</li>
<li>여러 개 존재 가능</li>
</ul>
<p><strong>2. 개념 스키마 (Conceptual Schema)</strong></p>
<ul>
<li>조직 전체의 데이터베이스 구조</li>
<li>모든 사용자가 공유하는 논리적 구조</li>
<li>일반적으로 &#39;스키마&#39;라고 하면 개념 스키마를 의미</li>
</ul>
<p><strong>3. 내부 스키마 (Internal Schema)</strong></p>
<ul>
<li>물리적 저장 구조</li>
<li>실제 데이터가 저장되는 방식 정의</li>
<li>하나만 존재</li>
</ul>
<h4 id="키-key">키 (Key)</h4>
<p><strong>정의</strong>: 테이블에서 레코드를 고유하게 식별하거나 관계를 정의하는 속성</p>
<p><strong>주요 키의 종류</strong>
<img src="https://velog.velcdn.com/images/you_n_sunny/post/2d10d940-cf1d-470c-a642-826f078fbca4/image.png" alt=""></p>
<p><strong>1. 기본 키 (Primary Key, PK)</strong></p>
<ul>
<li>각 레코드를 고유하게 식별하는 속성</li>
<li>NULL 값 불가, 중복 불가</li>
<li>테이블당 하나만 존재</li>
<li>예: 고객ID, 주문번호</li>
</ul>
<p><strong>2. 외래 키 (Foreign Key, FK)</strong></p>
<ul>
<li>다른 테이블의 기본 키를 참조하는 속성</li>
<li>테이블 간 관계를 정의</li>
<li>참조 무결성 유지</li>
<li>예: 주문 테이블의 고객ID (고객 테이블 참조)</li>
</ul>
<p><strong>3. 후보 키 (Candidate Key)</strong></p>
<ul>
<li>기본 키가 될 수 있는 속성들</li>
<li>유일성과 최소성을 만족</li>
</ul>
<p><strong>4. 대체 키 (Alternate Key)</strong></p>
<ul>
<li>후보 키 중 기본 키로 선택되지 않은 키</li>
</ul>
<p><strong>5. 복합 키 (Composite Key)</strong></p>
<ul>
<li>두 개 이상의 속성을 조합한 키</li>
</ul>
<h4 id="관계-relationship">관계 (Relationship)</h4>
<p><strong>정의</strong>: 엔티티(테이블) 간의 연관성을 나타내는 개념</p>
<p><strong>관계의 유형</strong>
<img src="https://velog.velcdn.com/images/you_n_sunny/post/97adf6f2-d4d4-489b-8cb9-cc34349ac552/image.png" alt=""></p>
<p><strong>1. 일대일 (1:1) 관계</strong></p>
<ul>
<li>한 엔티티의 레코드가 다른 엔티티의 정확히 하나의 레코드와 연결</li>
<li>예: 사원 - 사원증 (한 사원은 하나의 사원증만 보유)</li>
</ul>
<p><strong>2. 일대다 (1:N) 관계</strong></p>
<ul>
<li>한 엔티티의 레코드가 다른 엔티티의 여러 레코드와 연결</li>
<li>가장 흔한 관계 유형</li>
<li>예: 고객 - 주문 (한 고객이 여러 주문 가능)</li>
</ul>
<p><strong>3. 다대다 (N:M) 관계</strong></p>
<ul>
<li>양쪽 엔티티의 레코드가 서로 여러 개와 연결</li>
<li>중간 테이블(연결 테이블)로 구현</li>
<li>예: 학생 - 과목 (한 학생이 여러 과목 수강, 한 과목을 여러 학생이 수강)</li>
</ul>
<h4 id="인덱스-index">인덱스 (Index)</h4>
<p><strong>정의</strong>: 데이터 검색 속도를 향상시키기 위한 데이터 구조</p>
<p><strong>인덱스의 원리</strong></p>
<ul>
<li>책의 색인과 유사한 개념</li>
<li>특정 열의 값과 해당 레코드의 위치 정보 저장</li>
<li>B-Tree, Hash 등의 자료구조 사용</li>
</ul>
<p><strong>인덱스의 장점</strong></p>
<ul>
<li>검색 속도 대폭 향상 (특히 대용량 데이터)</li>
<li>WHERE, ORDER BY, JOIN 연산 최적화</li>
<li>데이터 정렬 비용 감소</li>
</ul>
<p><strong>인덱스의 단점</strong></p>
<ul>
<li>추가 저장 공간 필요</li>
<li>INSERT, UPDATE, DELETE 시 성능 저하</li>
<li>인덱스 유지 관리 비용</li>
</ul>
<p><strong>인덱스 사용 전략</strong></p>
<ul>
<li>자주 검색되는 열에 생성</li>
<li>WHERE 절에 자주 사용되는 열</li>
<li>JOIN 조건으로 사용되는 열</li>
<li>카디널리티(고유값 비율)가 높은 열</li>
</ul>
<h3 id="13-db-생성-원칙">1.3 DB 생성 원칙</h3>
<p>효과적인 데이터베이스를 설계하기 위해서는 체계적인 원칙과 방법론을 따라야 합니다. 데이터 무결성, 정규화, 성능 최적화 등을 고려한 설계가 필요합니다.</p>
<h4 id="데이터-무결성-data-integrity">데이터 무결성 (Data Integrity)</h4>
<p><strong>정의</strong>: 데이터의 정확성, 일관성, 유효성이 유지되는 것</p>
<p><strong>무결성의 종류</strong></p>
<p><strong>1. 개체 무결성 (Entity Integrity)</strong></p>
<ul>
<li>기본 키는 NULL 값을 가질 수 없음</li>
<li>기본 키는 중복될 수 없음</li>
<li>각 레코드를 고유하게 식별 보장</li>
</ul>
<p><strong>2. 참조 무결성 (Referential Integrity)</strong></p>
<ul>
<li>외래 키 값은 참조 테이블의 기본 키 값이어야 함</li>
<li>또는 NULL 값 허용 (선택적 관계인 경우)</li>
<li>존재하지 않는 데이터 참조 방지</li>
</ul>
<p><strong>3. 도메인 무결성 (Domain Integrity)</strong></p>
<ul>
<li>각 속성 값은 정의된 도메인(범위) 내에 있어야 함</li>
<li>데이터 타입, 길이, 형식 제약 준수</li>
<li>CHECK 제약 조건으로 구현</li>
</ul>
<p><strong>4. 사용자 정의 무결성 (User-Defined Integrity)</strong></p>
<ul>
<li>비즈니스 규칙에 따른 제약 조건</li>
<li>예: 주문 수량은 0보다 커야 함 </li>
</ul>
<h4 id="정규화-normalization">정규화 (Normalization)</h4>
<p><strong>정의</strong>: 데이터 중복을 최소화하고 무결성을 향상시키기 위해 테이블을 분해하는 과정</p>
<p><strong>정규화의 목적</strong></p>
<ul>
<li>데이터 중복 제거</li>
<li>삽입/수정/삭제 이상(Anomaly) 방지</li>
<li>데이터 일관성 유지</li>
<li>저장 공간 효율화</li>
</ul>
<p><strong>제1정규형 (1NF)</strong></p>
<ul>
<li>모든 속성 값은 원자값(Atomic Value)이어야 함</li>
<li>반복 그룹 제거</li>
<li>각 열은 하나의 값만 포함</li>
</ul>
<p><strong>예시</strong></p>
<pre><code>비정규형:
학생ID | 이름   | 수강과목
001    | 홍길동 | 수학, 영어, 과학

1NF:
학생ID | 이름   | 수강과목
001    | 홍길동 | 수학
001    | 홍길동 | 영어
001    | 홍길동 | 과학</code></pre><p><strong>제2정규형 (2NF)</strong></p>
<ul>
<li>1NF를 만족</li>
<li>부분 함수 종속 제거</li>
<li>기본 키가 아닌 모든 속성이 기본 키에 완전 함수 종속</li>
</ul>
<p><strong>제3정규형 (3NF)</strong></p>
<ul>
<li>2NF를 만족</li>
<li>이행적 함수 종속 제거</li>
<li>기본 키가 아닌 속성 간의 종속 관계 제거</li>
</ul>
<h4 id="역정규화-denormalization">역정규화 (Denormalization)</h4>
<p><strong>정의</strong>: 성능 향상을 위해 의도적으로 중복을 허용하는 과정</p>
<p><strong>역정규화가 필요한 경우</strong></p>
<ul>
<li>조인 연산이 너무 많아 성능 저하</li>
<li>복잡한 쿼리로 인한 응답 시간 증가</li>
<li>읽기 작업이 압도적으로 많은 경우</li>
</ul>
<p><strong>역정규화 기법</strong></p>
<ul>
<li><strong>테이블 통합</strong>: 자주 조인되는 테이블 병합</li>
<li><strong>중복 열 추가</strong>: 계산 결과나 집계 값 저장</li>
<li><strong>파생 열 추가</strong>: 자주 계산되는 값 미리 저장</li>
<li><strong>요약 테이블</strong>: 집계 데이터를 별도 테이블로 관리</li>
</ul>
<p><strong>주의사항</strong></p>
<ul>
<li>데이터 일관성 유지 메커니즘 필요</li>
<li>업데이트 비용 증가 고려</li>
<li>저장 공간 증가 감수</li>
</ul>
<h4 id="명명-규칙-naming-convention">명명 규칙 (Naming Convention)</h4>
<p><strong>테이블 명명 규칙</strong></p>
<ul>
<li>명확하고 의미 있는 이름 사용</li>
<li>복수형 사용 권장 (예: users, orders)</li>
<li>소문자와 언더스코어 사용 (snake_case)</li>
<li>예약어 사용 금지</li>
</ul>
<p><strong>열 명명 규칙</strong></p>
<ul>
<li>설명적이고 간결한 이름</li>
<li>테이블명 접두사 불필요 (테이블 컨텍스트에서 명확)</li>
<li>일관된 명명 패턴 유지</li>
<li>예: user_id, created_at, email_address</li>
</ul>
<p><strong>제약 조건 명명</strong></p>
<ul>
<li>PK: pk_테이블명</li>
<li>FK: fk_테이블명_참조테이블명</li>
<li>INDEX: idx_테이블명_열명</li>
<li>UNIQUE: uq_테이블명_열명</li>
</ul>
<h4 id="데이터베이스-설계-프로세스">데이터베이스 설계 프로세스</h4>
<p><strong>1단계: 요구사항 분석</strong></p>
<ul>
<li>비즈니스 요구사항 파악</li>
<li>데이터 항목 식별</li>
<li>사용자 요구사항 수집</li>
</ul>
<p><strong>2단계: 개념적 설계</strong></p>
<ul>
<li>ER(Entity-Relationship) 다이어그램 작성</li>
<li>엔티티와 관계 정의</li>
<li>속성 식별</li>
</ul>
<p><strong>3단계: 논리적 설계</strong></p>
<ul>
<li>ER 다이어그램을 관계형 스키마로 변환</li>
<li>정규화 수행</li>
<li>키 정의 (기본 키, 외래 키)</li>
</ul>
<p><strong>4단계: 물리적 설계</strong></p>
<ul>
<li>DBMS 선택</li>
<li>인덱스 설계</li>
<li>파티셔닝 전략</li>
<li>저장 공간 할당</li>
</ul>
<p><strong>5단계: 구현 및 테스트</strong></p>
<ul>
<li>DDL(Data Definition Language)로 스키마 생성</li>
<li>샘플 데이터로 테스트</li>
<li>성능 튜닝</li>
</ul>
<h4 id="성능-최적화-원칙">성능 최적화 원칙</h4>
<p><strong>쿼리 최적화</strong></p>
<ul>
<li>SELECT 시 필요한 열만 조회 (SELECT * 지양)</li>
<li>WHERE 절에 인덱스 활용</li>
<li>서브쿼리보다 JOIN 사용 권장</li>
<li>LIMIT 절로 결과 제한</li>
</ul>
<p><strong>인덱스 전략</strong></p>
<ul>
<li>자주 검색되는 열에 인덱스 생성</li>
<li>복합 인덱스 활용 (여러 열 조합)</li>
<li>인덱스 개수 적정 유지 (과도한 인덱스는 역효과)</li>
</ul>
<p><strong>테이블 파티셔닝</strong></p>
<ul>
<li>대용량 테이블을 작은 단위로 분할</li>
<li>범위, 리스트, 해시 파티셔닝</li>
<li>쿼리 성능 향상 및 관리 용이성</li>
</ul>
<p><strong>캐싱 전략</strong></p>
<ul>
<li>자주 조회되는 데이터 캐싱</li>
<li>Redis, Memcached 등 활용</li>
<li>데이터베이스 부하 감소</li>
</ul>
<h3 id="14-rdbms와-nosql-비교">1.4 RDBMS와 NoSQL 비교</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/15046ea1-77e4-40be-afa1-9325f6eda5f5/image.png" alt=""></p>
<p>데이터베이스는 크게 <strong>관계형 데이터베이스(RDBMS)</strong>와 <strong>NoSQL 데이터베이스</strong>로 구분됩니다. 각각의 철학, 구조, 강점이 완전히 다르며, 프로젝트의 특성에 따라 적절한 선택이 필요합니다.  </p>
<h4 id="rdbms-관계형-데이터베이스">RDBMS (관계형 데이터베이스)</h4>
<p><strong>정의</strong>: 테이블 간의 관계를 기반으로 데이터를 구조화하여 관리하는 데이터베이스 시스템</p>
<p><strong>핵심 철학</strong></p>
<ul>
<li><strong>정확성과 일관성 중심</strong>: 한 글자, 한 숫자라도 틀리면 안 되는 데이터 관리</li>
<li><strong>ACID 트랜잭션</strong>: 데이터 무결성을 최우선으로 보장</li>
<li><strong>구조화된 데이터</strong>: 명확한 스키마로 데이터 구조 정의 </li>
</ul>
<p><strong>데이터 모델</strong></p>
<ul>
<li>테이블(Table) 형식의 관계형 데이터 모델</li>
<li>행(Row)과 열(Column)로 구성</li>
<li>테이블 간 관계(Relationship)로 연결</li>
<li>정형화된 데이터 저장  </li>
</ul>
<p><strong>스키마</strong></p>
<ul>
<li>정적 스키마 (Static Schema)</li>
<li>데이터 구조를 미리 정의 필수</li>
<li>데이터 타입과 제약 조건 명시</li>
<li>스키마 변경이 어렵고 신중해야 함  </li>
</ul>
<h5 id="rdbms의-장점">RDBMS의 장점</h5>
<p><strong>1. 데이터 무결성 보장</strong></p>
<ul>
<li>ACID 속성으로 트랜잭션 안정성 확보</li>
<li>참조 무결성으로 데이터 일관성 유지</li>
<li>제약 조건으로 잘못된 데이터 입력 방지 </li>
</ul>
<p><strong>2. 표준화된 쿼리 언어</strong></p>
<ul>
<li>SQL이라는 강력하고 표준화된 언어 제공</li>
<li>복잡한 쿼리와 조인 연산 지원</li>
<li>학습 곡선이 완만하고 자료 풍부 </li>
</ul>
<p><strong>3. 데이터 중복 최소화</strong></p>
<ul>
<li>정규화를 통한 중복 제거</li>
<li>저장 공간 효율적 사용</li>
<li>데이터 일관성 유지 용이</li>
</ul>
<p><strong>4. 성숙한 생태계</strong></p>
<ul>
<li>수십 년간 검증된 기술</li>
<li>풍부한 도구와 라이브러리</li>
<li>전문 인력 확보 용이</li>
</ul>
<h5 id="rdbms의-단점">RDBMS의 단점</h5>
<p><strong>1. 확장성 제한</strong></p>
<ul>
<li>수직 확장(Scale-up)에 의존</li>
<li>서버 성능 향상에 한계 존재</li>
<li>수평 확장(Scale-out) 어려움  </li>
</ul>
<p><strong>2. 스키마 유연성 부족</strong></p>
<ul>
<li>스키마 변경이 복잡하고 비용 높음</li>
<li>애자일 개발 환경에 부적합할 수 있음</li>
<li>비정형 데이터 처리 어려움 </li>
</ul>
<p><strong>3. 성능 이슈</strong></p>
<ul>
<li>대용량 데이터 처리 시 성능 저하</li>
<li>복잡한 조인 연산의 비용</li>
<li>특수 하드웨어 필요 가능성 </li>
</ul>
<p><strong>4. 고정된 구조</strong></p>
<ul>
<li>다양한 형태의 데이터 저장 제약</li>
<li>반정형/비정형 데이터 처리 한계</li>
</ul>
<h5 id="주요-rdbms-제품">주요 RDBMS 제품</h5>
<p><strong>MySQL</strong></p>
<ul>
<li>가장 널리 사용되는 오픈소스 RDBMS</li>
<li>LAMP 스택의 핵심 구성 요소</li>
<li>웹 애플리케이션에 최적화</li>
<li>커뮤니티 에디션 무료 제공  </li>
</ul>
<p><strong>PostgreSQL</strong></p>
<ul>
<li>고급 기능을 갖춘 오픈소스 RDBMS</li>
<li>ACID 준수 및 확장성 우수</li>
<li>복잡한 쿼리와 대용량 데이터 처리</li>
<li>JSON 지원으로 NoSQL 기능 일부 제공  </li>
</ul>
<p><strong>Oracle Database</strong></p>
<ul>
<li>대기업용 상용 데이터베이스</li>
<li>최고 수준의 성능과 안정성</li>
<li>엔터프라이즈 기능 풍부</li>
<li>높은 라이선스 비용  </li>
</ul>
<p><strong>Microsoft SQL Server</strong></p>
<ul>
<li>마이크로소프트의 상용 RDBMS</li>
<li>Windows 환경과 긴밀한 통합</li>
<li>.NET 개발과 호환성 우수</li>
<li>Azure 클라우드 지원  </li>
</ul>
<p><strong>MariaDB</strong></p>
<ul>
<li>MySQL의 포크 프로젝트</li>
<li>MySQL과 높은 호환성</li>
<li>오픈소스 정책 유지</li>
<li>성능 개선 및 추가 기능 </li>
</ul>
<p><strong>SQLite</strong></p>
<ul>
<li>경량 임베디드 데이터베이스</li>
<li>서버 없이 파일 기반 동작</li>
<li>모바일 앱, 소규모 프로젝트에 적합 </li>
</ul>
<h5 id="rdbms-적합한-사용-사례">RDBMS 적합한 사용 사례</h5>
<p><strong>금융 시스템</strong></p>
<ul>
<li>은행 거래, 결제 시스템</li>
<li>정확성과 일관성이 최우선</li>
<li>ACID 트랜잭션 필수</li>
<li>감사 추적(Audit Trail) 필요 </li>
</ul>
<p><strong>전자상거래</strong></p>
<ul>
<li>주문 관리, 재고 관리</li>
<li>복잡한 비즈니스 로직</li>
<li>데이터 무결성 중요</li>
<li>트랜잭션 처리 빈번 </li>
</ul>
<p><strong>ERP/CRM 시스템</strong></p>
<ul>
<li>기업 자원 관리</li>
<li>고객 관계 관리</li>
<li>정형화된 데이터 구조</li>
<li>복잡한 관계와 제약 조건</li>
</ul>
<p><strong>회계 시스템</strong></p>
<ul>
<li>재무 데이터 관리</li>
<li>정확한 계산과 기록</li>
<li>법적 요구사항 준수</li>
</ul>
<h4 id="nosql-비관계형-데이터베이스">NoSQL (비관계형 데이터베이스)</h4>
<p><strong>정의</strong>: &quot;Not Only SQL&quot;의 약자로, 관계형 모델을 따르지 않는 다양한 형태의 데이터베이스</p>
<p><strong>핵심 철학</strong></p>
<ul>
<li><strong>유연성과 확장성 중심</strong>: 빠르게 변화하는 데이터 구조 대응</li>
<li><strong>수평적 확장</strong>: 서버 추가로 성능 향상</li>
<li><strong>비정형 데이터 처리</strong>: 다양한 형태의 데이터 저장 </li>
</ul>
<p><strong>데이터 모델</strong></p>
<ul>
<li>다양한 데이터 모델 지원</li>
<li>키-값(Key-Value), 문서(Document), 열 지향(Column-family), 그래프(Graph)</li>
<li>비정형 또는 반정형 데이터 저장</li>
<li>스키마리스(Schema-less) 또는 동적 스키마  </li>
</ul>
<p><strong>스키마</strong></p>
<ul>
<li>동적 스키마 (Dynamic Schema)</li>
<li>데이터 구조를 미리 정의할 필요 없음</li>
<li>필드 추가/삭제가 자유로움</li>
<li>애자일 개발에 적합  </li>
</ul>
<h5 id="nosql의-장점">NoSQL의 장점</h5>
<p><strong>1. 수평적 확장성</strong></p>
<ul>
<li>서버 추가로 성능 향상 (Scale-out)</li>
<li>클러스터링을 통한 분산 처리</li>
<li>대용량 데이터 처리에 유리</li>
<li>비용 효율적인 확장  </li>
</ul>
<p><strong>2. 유연한 스키마</strong></p>
<ul>
<li>스키마 변경이 자유로움</li>
<li>다양한 형태의 데이터 저장 가능</li>
<li>빠른 개발 및 반복 가능</li>
<li>비정형 데이터 처리 우수 </li>
</ul>
<p><strong>3. 높은 성능</strong></p>
<ul>
<li>특정 작업에 최적화된 구조</li>
<li>빠른 읽기/쓰기 속도</li>
<li>상용 하드웨어 사용 가능</li>
<li>캐싱 메커니즘 내장 </li>
</ul>
<p><strong>4. 대용량 데이터 처리</strong></p>
<ul>
<li>빅데이터 환경에 적합</li>
<li>실시간 분석 지원</li>
<li>로그 데이터, 센서 데이터 처리 </li>
</ul>
<h5 id="nosql의-단점">NoSQL의 단점</h5>
<p><strong>1. 데이터 일관성 약화</strong></p>
<ul>
<li>ACID 대신 BASE 모델 (Basically Available, Soft state, Eventually consistent)</li>
<li>최종 일관성(Eventually Consistent) 보장</li>
<li>즉각적인 일관성 보장 어려움</li>
</ul>
<p><strong>2. 표준화 부족</strong></p>
<ul>
<li>각 NoSQL 데이터베이스마다 고유한 쿼리 언어</li>
<li>SQL처럼 표준화된 언어 없음</li>
<li>학습 곡선이 가파를 수 있음 </li>
</ul>
<p><strong>3. 복잡한 쿼리 제한</strong></p>
<ul>
<li>조인 연산 미지원 또는 제한적</li>
<li>복잡한 트랜잭션 처리 어려움</li>
<li>애플리케이션 레벨에서 처리 필요</li>
</ul>
<p><strong>4. 성숙도</strong></p>
<ul>
<li>RDBMS에 비해 역사가 짧음</li>
<li>도구와 생태계가 상대적으로 부족</li>
<li>전문 인력 확보 어려울 수 있음</li>
</ul>
<h5 id="nosql-데이터베이스-유형">NoSQL 데이터베이스 유형</h5>
<p><strong>1. 문서 데이터베이스 (Document Database)</strong></p>
<ul>
<li>JSON, BSON, XML 형식으로 데이터 저장</li>
<li>계층적 데이터 구조 지원</li>
<li>예: MongoDB, CouchDB, Elasticsearch</li>
<li>사용 사례: 콘텐츠 관리, 사용자 프로필 </li>
</ul>
<p><strong>2. 키-값 데이터베이스 (Key-Value Store)</strong></p>
<ul>
<li>단순한 키-값 쌍으로 데이터 저장</li>
<li>매우 빠른 읽기/쓰기 성능</li>
<li>예: Redis, Memcached, DynamoDB</li>
<li>사용 사례: 캐싱, 세션 관리, 실시간 분석</li>
</ul>
<p><strong>3. 열 지향 데이터베이스 (Column-family Store)</strong></p>
<ul>
<li>열 단위로 데이터 저장 및 압축</li>
<li>대용량 데이터 분석에 최적화</li>
<li>예: Cassandra, HBase, Google Bigtable</li>
<li>사용 사례: 시계열 데이터, 로그 분석 </li>
</ul>
<p><strong>4. 그래프 데이터베이스 (Graph Database)</strong></p>
<ul>
<li>노드와 엣지로 관계 표현</li>
<li>복잡한 관계 쿼리에 최적화</li>
<li>예: Neo4j, ArangoDB, Amazon Neptune</li>
<li>사용 사례: 소셜 네트워크, 추천 시스템, 지식 그래프</li>
</ul>
<h5 id="주요-nosql-제품">주요 NoSQL 제품</h5>
<p><strong>MongoDB</strong></p>
<ul>
<li>가장 인기 있는 문서 데이터베이스</li>
<li>JSON 형식의 BSON 문서 저장</li>
<li>풍부한 쿼리 기능과 인덱싱</li>
<li>수평적 확장 지원 (샤딩) </li>
</ul>
<p><strong>Redis</strong></p>
<ul>
<li>인메모리 키-값 저장소</li>
<li>초고속 읽기/쓰기 성능</li>
<li>다양한 데이터 구조 지원 (문자열, 리스트, 셋, 해시)</li>
<li>캐싱, 세션 관리에 널리 사용</li>
</ul>
<p><strong>Cassandra</strong></p>
<ul>
<li>분산 열 지향 데이터베이스</li>
<li>높은 가용성과 확장성</li>
<li>쓰기 성능 우수</li>
<li>대용량 데이터 처리 </li>
</ul>
<p><strong>Elasticsearch</strong></p>
<ul>
<li>검색 및 분석 엔진</li>
<li>전문 검색(Full-text Search) 기능</li>
<li>실시간 데이터 분석</li>
<li>로그 및 이벤트 데이터 처리 </li>
</ul>
<p><strong>Neo4j</strong></p>
<ul>
<li>그래프 데이터베이스</li>
<li>관계 중심 데이터 모델</li>
<li>Cypher 쿼리 언어</li>
<li>소셜 네트워크, 추천 시스템</li>
</ul>
<h5 id="nosql-적합한-사용-사례">NoSQL 적합한 사용 사례</h5>
<p><strong>소셜 미디어 플랫폼</strong></p>
<ul>
<li>사용자 피드, 게시물, 댓글</li>
<li>비정형 데이터 (이미지, 동영상 메타데이터)</li>
<li>대량의 읽기/쓰기 작업</li>
<li>실시간 업데이트 </li>
</ul>
<p><strong>IoT 및 센서 데이터</strong></p>
<ul>
<li>시계열 데이터 수집</li>
<li>초당 수만 건의 데이터 발생</li>
<li>실시간 분석 필요</li>
<li>스키마 변경 빈번 </li>
</ul>
<p><strong>콘텐츠 관리 시스템</strong></p>
<ul>
<li>다양한 형태의 콘텐츠</li>
<li>유연한 데이터 구조</li>
<li>빠른 검색 기능</li>
<li>확장 가능한 아키텍처</li>
</ul>
<p><strong>실시간 분석</strong></p>
<ul>
<li>로그 데이터 분석</li>
<li>사용자 행동 추적</li>
<li>대시보드 및 리포팅</li>
<li>빅데이터 처리 </li>
</ul>
<p><strong>캐싱 레이어</strong></p>
<ul>
<li>데이터베이스 부하 감소</li>
<li>응답 시간 단축</li>
<li>세션 관리</li>
<li>임시 데이터 저장</li>
</ul>
<h4 id="rdbms-vs-nosql-비교표">RDBMS vs NoSQL 비교표</h4>
<table>
<thead>
<tr>
<th><strong>특성</strong></th>
<th><strong>RDBMS</strong></th>
<th><strong>NoSQL</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>데이터 모델</strong></td>
<td>테이블 기반 관계형</td>
<td>문서, 키-값, 열, 그래프</td>
</tr>
<tr>
<td><strong>스키마</strong></td>
<td>정적, 사전 정의 필수</td>
<td>동적, 유연함</td>
</tr>
<tr>
<td><strong>확장성</strong></td>
<td>수직 확장 (Scale-up)</td>
<td>수평 확장 (Scale-out)</td>
</tr>
<tr>
<td><strong>트랜잭션</strong></td>
<td>ACID 보장</td>
<td>BASE (최종 일관성)</td>
</tr>
<tr>
<td><strong>쿼리 언어</strong></td>
<td>SQL (표준화)</td>
<td>제품마다 다름</td>
</tr>
<tr>
<td><strong>데이터 무결성</strong></td>
<td>강력함</td>
<td>상대적으로 약함</td>
</tr>
<tr>
<td><strong>성능</strong></td>
<td>복잡한 쿼리에 강함</td>
<td>단순 읽기/쓰기에 빠름</td>
</tr>
<tr>
<td><strong>사용 사례</strong></td>
<td>금융, ERP, 전자상거래</td>
<td>소셜미디어, IoT, 빅데이터</td>
</tr>
<tr>
<td><strong>데이터 중복</strong></td>
<td>최소화 (정규화)</td>
<td>허용 (역정규화)</td>
</tr>
<tr>
<td><strong>조인</strong></td>
<td>지원</td>
<td>제한적 또는 미지원</td>
</tr>
</tbody></table>
<h4 id="선택-가이드-rdbms-vs-nosql">선택 가이드: RDBMS vs NoSQL</h4>
<p><strong>RDBMS를 선택해야 하는 경우</strong></p>
<ul>
<li>데이터 무결성과 일관성이 최우선</li>
<li>복잡한 트랜잭션 처리 필요</li>
<li>정형화된 데이터 구조</li>
<li>복잡한 조인과 쿼리 빈번</li>
<li>ACID 속성 필수</li>
<li>예: 은행 시스템, 회계 시스템, ERP </li>
</ul>
<p><strong>NoSQL을 선택해야 하는 경우</strong></p>
<ul>
<li>대용량 데이터 처리</li>
<li>빠른 읽기/쓰기 성능 필요</li>
<li>스키마가 자주 변경됨</li>
<li>수평적 확장 필요</li>
<li>비정형/반정형 데이터</li>
<li>예: 소셜 미디어, IoT, 실시간 분석 </li>
</ul>
<p><strong>하이브리드 접근</strong></p>
<ul>
<li>많은 현대 애플리케이션은 두 가지를 함께 사용</li>
<li>RDBMS: 핵심 트랜잭션 데이터</li>
<li>NoSQL: 캐싱, 세션, 로그 데이터</li>
<li>각 데이터베이스의 강점 활용</li>
<li>Polyglot Persistence 패턴</li>
</ul>
<h3 id="25-sql-언어">2.5 SQL 언어</h3>
<p><strong>SQL(Structured Query Language)</strong>은 관계형 데이터베이스에서 데이터를 관리하기 위한 표준 프로그래밍 언어입니다. 1970년대부터 사용되어 온 SQL은 데이터 정의, 조작, 제어를 위한 강력하고 표준화된 도구입니다.  </p>
<h4 id="sql의-개요">SQL의 개요</h4>
<p><strong>정의</strong></p>
<ul>
<li>&quot;Structured Query Language&quot;의 약자</li>
<li>&quot;에스큐엘&quot; 또는 &quot;시퀄&quot;로 발음</li>
<li>관계형 데이터베이스의 표준 언어</li>
<li>ANSI/ISO 표준으로 제정 </li>
</ul>
<p><strong>SQL의 역할</strong></p>
<ul>
<li>데이터베이스 레코드 삽입, 검색, 업데이트, 삭제</li>
<li>데이터베이스 스키마 생성 및 수정</li>
<li>접근 권한 설정 및 관리</li>
<li>데이터베이스 최적화 및 유지 관리 </li>
</ul>
<p><strong>SQL의 특징</strong></p>
<ul>
<li>선언적 언어 (무엇을 할지 명시, 어떻게 할지는 DBMS가 결정)</li>
<li>집합 기반 언어 (한 번에 여러 행 처리)</li>
<li>대소문자 구분 없음 (관례적으로 키워드는 대문자)</li>
<li>세미콜론(;)으로 문장 종료</li>
</ul>
<h4 id="sql의-분류">SQL의 분류</h4>
<p><strong>1. DDL (Data Definition Language) - 데이터 정의어</strong></p>
<ul>
<li>데이터베이스 구조를 정의하고 수정</li>
<li>주요 명령어: CREATE, ALTER, DROP, TRUNCATE</li>
<li>스키마, 테이블, 인덱스 등 생성/수정/삭제</li>
</ul>
<p><strong>2. DML (Data Manipulation Language) - 데이터 조작어</strong></p>
<ul>
<li>데이터를 조회하고 변경</li>
<li>주요 명령어: SELECT, INSERT, UPDATE, DELETE</li>
<li>가장 빈번하게 사용되는 SQL 명령어</li>
</ul>
<p><strong>3. DCL (Data Control Language) - 데이터 제어어</strong></p>
<ul>
<li>데이터 접근 권한 관리</li>
<li>주요 명령어: GRANT, REVOKE</li>
<li>사용자 권한 부여 및 회수</li>
</ul>
<p><strong>4. TCL (Transaction Control Language) - 트랜잭션 제어어</strong></p>
<ul>
<li>트랜잭션 관리</li>
<li>주요 명령어: COMMIT, ROLLBACK, SAVEPOINT</li>
<li>데이터 변경 사항 확정 또는 취소</li>
</ul>
<h4 id="ddl-data-definition-language">DDL (Data Definition Language)</h4>
<p><strong>CREATE - 데이터베이스 객체 생성</strong></p>
<pre><code class="language-sql">-- 데이터베이스 생성
CREATE DATABASE company;

-- 테이블 생성
CREATE TABLE employees (
    employee_id INT PRIMARY KEY AUTO_INCREMENT,
    first_name VARCHAR(50) NOT NULL,
    last_name VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE,
    hire_date DATE,
    salary DECIMAL(10, 2),
    department_id INT,
    FOREIGN KEY (department_id) REFERENCES departments(department_id)
);

-- 인덱스 생성
CREATE INDEX idx_last_name ON employees(last_name);</code></pre>
<p><strong>ALTER - 데이터베이스 객체 수정</strong></p>
<pre><code class="language-sql">-- 열 추가
ALTER TABLE employees ADD COLUMN phone VARCHAR(20);

-- 열 수정
ALTER TABLE employees MODIFY COLUMN salary DECIMAL(12, 2);

-- 열 삭제
ALTER TABLE employees DROP COLUMN phone;

-- 제약 조건 추가
ALTER TABLE employees ADD CONSTRAINT chk_salary CHECK (salary &gt; 0);</code></pre>
<p><strong>DROP - 데이터베이스 객체 삭제</strong></p>
<pre><code class="language-sql">-- 테이블 삭제 (구조와 데이터 모두 삭제)
DROP TABLE employees;

-- 데이터베이스 삭제
DROP DATABASE company;

-- 인덱스 삭제
DROP INDEX idx_last_name ON employees;</code></pre>
<p><strong>TRUNCATE - 테이블 데이터 삭제</strong></p>
<pre><code class="language-sql">-- 테이블의 모든 데이터 삭제 (구조는 유지)
TRUNCATE TABLE employees;
-- DELETE보다 빠르지만 롤백 불가</code></pre>
<h4 id="dml-data-manipulation-language">DML (Data Manipulation Language)</h4>
<p><strong>SELECT - 데이터 조회</strong></p>
<pre><code class="language-sql">-- 기본 조회
SELECT * FROM employees;

-- 특정 열 조회
SELECT first_name, last_name, salary FROM employees;

-- 조건부 조회 (WHERE)
SELECT * FROM employees WHERE salary &gt; 50000;

-- 정렬 (ORDER BY)
SELECT * FROM employees ORDER BY salary DESC;

-- 제한 (LIMIT)
SELECT * FROM employees LIMIT 10;

-- 집계 함수
SELECT COUNT(*) FROM employees;
SELECT AVG(salary) FROM employees;
SELECT MAX(salary), MIN(salary) FROM employees;

-- 그룹화 (GROUP BY)
SELECT department_id, AVG(salary) 
FROM employees 
GROUP BY department_id;

-- HAVING (그룹 조건)
SELECT department_id, AVG(salary) 
FROM employees 
GROUP BY department_id 
HAVING AVG(salary) &gt; 60000;</code></pre>
<p><strong>INSERT - 데이터 삽입</strong></p>
<pre><code class="language-sql">-- 단일 행 삽입
INSERT INTO employees (first_name, last_name, email, hire_date, salary, department_id)
VALUES (&#39;John&#39;, &#39;Doe&#39;, &#39;john.doe@company.com&#39;, &#39;2024-01-15&#39;, 65000.00, 1);

-- 여러 행 삽입
INSERT INTO employees (first_name, last_name, email, hire_date, salary, department_id)
VALUES 
    (&#39;Jane&#39;, &#39;Smith&#39;, &#39;jane.smith@company.com&#39;, &#39;2024-01-20&#39;, 70000.00, 2),
    (&#39;Bob&#39;, &#39;Johnson&#39;, &#39;bob.johnson@company.com&#39;, &#39;2024-02-01&#39;, 55000.00, 1);

-- 다른 테이블에서 데이터 복사
INSERT INTO employees_backup
SELECT * FROM employees WHERE department_id = 1;</code></pre>
<p><strong>UPDATE - 데이터 수정</strong></p>
<pre><code class="language-sql">-- 특정 행 수정
UPDATE employees 
SET salary = 75000.00 
WHERE employee_id = 1;

-- 여러 열 수정
UPDATE employees 
SET salary = salary * 1.1, 
    email = &#39;new.email@company.com&#39; 
WHERE department_id = 2;

-- 조건부 수정
UPDATE employees 
SET salary = salary * 1.05 
WHERE hire_date &lt; &#39;2023-01-01&#39;;</code></pre>
<p><strong>DELETE - 데이터 삭제</strong></p>
<pre><code class="language-sql">-- 특정 행 삭제
DELETE FROM employees WHERE employee_id = 1;

-- 조건부 삭제
DELETE FROM employees WHERE department_id = 3;

-- 모든 행 삭제 (테이블 구조는 유지)
DELETE FROM employees;</code></pre>
<h4 id="join---테이블-결합">JOIN - 테이블 결합</h4>
<p><strong>INNER JOIN - 교집합</strong></p>
<pre><code class="language-sql">-- 두 테이블의 일치하는 행만 반환
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
INNER JOIN departments d ON e.department_id = d.department_id;</code></pre>
<p><strong>LEFT JOIN - 왼쪽 테이블 기준</strong></p>
<pre><code class="language-sql">-- 왼쪽 테이블의 모든 행 + 오른쪽 테이블의 일치하는 행
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id;</code></pre>
<p><strong>RIGHT JOIN - 오른쪽 테이블 기준</strong></p>
<pre><code class="language-sql">-- 오른쪽 테이블의 모든 행 + 왼쪽 테이블의 일치하는 행
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id;</code></pre>
<p><strong>FULL OUTER JOIN - 합집합</strong></p>
<pre><code class="language-sql">-- 양쪽 테이블의 모든 행 (MySQL은 미지원, UNION으로 구현)
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
LEFT JOIN departments d ON e.department_id = d.department_id
UNION
SELECT e.first_name, e.last_name, d.department_name
FROM employees e
RIGHT JOIN departments d ON e.department_id = d.department_id;</code></pre>
<p><strong>CROSS JOIN - 카티션 곱</strong></p>
<pre><code class="language-sql">-- 모든 가능한 조합
SELECT e.first_name, d.department_name
FROM employees e
CROSS JOIN departments d;</code></pre>
<p><strong>SELF JOIN - 자기 자신과 조인</strong></p>
<pre><code class="language-sql">-- 같은 테이블을 두 번 참조
SELECT e1.first_name AS employee, e2.first_name AS manager
FROM employees e1
INNER JOIN employees e2 ON e1.manager_id = e2.employee_id;</code></pre>
<h4 id="서브쿼리-subquery">서브쿼리 (Subquery)</h4>
<p><strong>단일 행 서브쿼리</strong></p>
<pre><code class="language-sql">-- 평균 급여보다 높은 직원 조회
SELECT first_name, last_name, salary
FROM employees
WHERE salary &gt; (SELECT AVG(salary) FROM employees);</code></pre>
<p><strong>다중 행 서브쿼리</strong></p>
<pre><code class="language-sql">-- IN 연산자 사용
SELECT first_name, last_name
FROM employees
WHERE department_id IN (
    SELECT department_id 
    FROM departments 
    WHERE location = &#39;Seoul&#39;
);

-- ANY/ALL 연산자
SELECT first_name, salary
FROM employees
WHERE salary &gt; ANY (
    SELECT salary 
    FROM employees 
    WHERE department_id = 1
);</code></pre>
<p><strong>상관 서브쿼리</strong></p>
<pre><code class="language-sql">-- 각 부서의 평균보다 높은 급여를 받는 직원
SELECT e1.first_name, e1.salary, e1.department_id
FROM employees e1
WHERE salary &gt; (
    SELECT AVG(salary)
    FROM employees e2
    WHERE e2.department_id = e1.department_id
);</code></pre>
<p><strong>FROM 절 서브쿼리 (인라인 뷰)</strong></p>
<pre><code class="language-sql">SELECT dept_avg.department_id, dept_avg.avg_salary
FROM (
    SELECT department_id, AVG(salary) as avg_salary
    FROM employees
    GROUP BY department_id
) AS dept_avg
WHERE dept_avg.avg_salary &gt; 60000;</code></pre>
<h4 id="트랜잭션-transaction">트랜잭션 (Transaction)</h4>
<p><strong>트랜잭션의 정의</strong></p>
<ul>
<li>하나의 논리적 작업 단위를 구성하는 일련의 연산들</li>
<li>모두 성공하거나 모두 실패해야 함 (All or Nothing)</li>
</ul>
<p><strong>ACID 속성</strong></p>
<ul>
<li><strong>Atomicity (원자성)</strong>: 트랜잭션의 모든 연산이 완전히 수행되거나 전혀 수행되지 않음</li>
<li><strong>Consistency (일관성)</strong>: 트랜잭션 실행 전후 데이터베이스가 일관된 상태 유지</li>
<li><strong>Isolation (격리성)</strong>: 동시 실행 트랜잭션들이 서로 영향을 주지 않음</li>
<li><strong>Durability (지속성)</strong>: 완료된 트랜잭션의 결과는 영구적으로 반영</li>
</ul>
<p><strong>트랜잭션 제어 명령어</strong></p>
<pre><code class="language-sql">-- 트랜잭션 시작 (암묵적으로 시작됨)
START TRANSACTION;
-- 또는
BEGIN;

-- 작업 수행
UPDATE accounts SET balance = balance - 1000 WHERE account_id = 1;
UPDATE accounts SET balance = balance + 1000 WHERE account_id = 2;

-- 트랜잭션 확정 (변경사항 영구 저장)
COMMIT;

-- 트랜잭션 취소 (변경사항 되돌림)
ROLLBACK;

-- 저장점 설정
SAVEPOINT savepoint1;
-- 특정 저장점으로 롤백
ROLLBACK TO savepoint1;</code></pre>
<p><strong>트랜잭션 예시</strong></p>
<pre><code class="language-sql">START TRANSACTION;

-- 계좌 이체 작업
UPDATE accounts 
SET balance = balance - 100000 
WHERE account_id = &#39;A001&#39;;

UPDATE accounts 
SET balance = balance + 100000 
WHERE account_id = &#39;B001&#39;;

-- 잔액 확인
SELECT balance FROM accounts WHERE account_id = &#39;A001&#39;;

-- 문제 없으면 확정
COMMIT;
-- 문제 있으면 취소
-- ROLLBACK;</code></pre>
<h4 id="주요-rdbms별-sql-특징">주요 RDBMS별 SQL 특징</h4>
<p><strong>MySQL</strong></p>
<ul>
<li>가장 널리 사용되는 오픈소스 RDBMS</li>
<li>AUTO_INCREMENT로 자동 증가 컬럼 생성</li>
<li>LIMIT 절로 결과 제한</li>
<li>다양한 스토리지 엔진 (InnoDB, MyISAM)</li>
</ul>
<pre><code class="language-sql">-- MySQL 특화 문법
SELECT * FROM employees LIMIT 10 OFFSET 20;
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100)
) ENGINE=InnoDB;</code></pre>
<p><strong>PostgreSQL</strong></p>
<ul>
<li>고급 기능을 갖춘 오픈소스 RDBMS</li>
<li>SERIAL 타입으로 자동 증가</li>
<li>강력한 JSON 지원</li>
<li>윈도우 함수, CTE 등 고급 기능</li>
</ul>
<pre><code class="language-sql">-- PostgreSQL 특화 문법
SELECT * FROM employees LIMIT 10 OFFSET 20;
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    metadata JSONB
);

-- CTE (Common Table Expression)
WITH dept_avg AS (
    SELECT department_id, AVG(salary) as avg_sal
    FROM employees
    GROUP BY department_id
)
SELECT * FROM dept_avg WHERE avg_sal &gt; 60000;</code></pre>
<p><strong>Oracle Database</strong></p>
<ul>
<li>엔터프라이즈급 상용 RDBMS</li>
<li>SEQUENCE로 자동 증가 구현</li>
<li>ROWNUM으로 결과 제한</li>
<li>PL/SQL 프로시저 언어</li>
</ul>
<pre><code class="language-sql">-- Oracle 특화 문법
SELECT * FROM employees WHERE ROWNUM &lt;= 10;

CREATE SEQUENCE emp_seq START WITH 1;
INSERT INTO employees (id, name) 
VALUES (emp_seq.NEXTVAL, &#39;John&#39;);</code></pre>
<p><strong>Microsoft SQL Server</strong></p>
<ul>
<li>마이크로소프트의 상용 RDBMS</li>
<li>IDENTITY로 자동 증가</li>
<li>TOP 절로 결과 제한</li>
<li>T-SQL 프로시저 언어</li>
</ul>
<pre><code class="language-sql">-- SQL Server 특화 문법
SELECT TOP 10 * FROM employees;

CREATE TABLE users (
    id INT IDENTITY(1,1) PRIMARY KEY,
    name VARCHAR(100)
);</code></pre>
<p><strong>MariaDB</strong></p>
<ul>
<li>MySQL의 포크 프로젝트</li>
<li>MySQL과 높은 호환성</li>
<li>추가 스토리지 엔진 (ColumnStore)</li>
<li>향상된 성능과 기능</li>
</ul>
<pre><code class="language-sql">-- MariaDB는 MySQL과 거의 동일한 문법
SELECT * FROM employees LIMIT 10;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] GitHub ]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-GitHub</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-GitHub</guid>
            <pubDate>Sun, 22 Feb 2026 14:16:32 GMT</pubDate>
            <description><![CDATA[<h3 id="11-형상-관리란-무엇인가">1.1 형상 관리란 무엇인가</h3>
<p><strong>형상 관리(Configuration Management)</strong> 또는 <strong>버전 관리(Version Control)</strong>는 소프트웨어 개발 과정에서 소스 코드와 관련 파일들의 변경 사항을 체계적으로 추적하고 관리하는 시스템입니다. </p>
<p>형상 관리 시스템은 다음과 같은 핵심 기능을 제공합니다:</p>
<ul>
<li><strong>변경 이력 추적</strong>: 누가, 언제, 무엇을, 왜 변경했는지 기록</li>
<li><strong>버전 관리</strong>: 코드의 특정 시점 상태를 저장하고 복원</li>
<li><strong>협업 지원</strong>: 여러 개발자가 동시에 작업 가능</li>
<li><strong>충돌 해결</strong>: 동일 파일의 동시 수정 시 병합 지원</li>
</ul>
<h4 id="형상-관리의-필요성">형상 관리의 필요성</h4>
<p><strong>개발 과정의 안정성 확보</strong></p>
<ul>
<li>실수로 코드를 삭제하거나 잘못 수정해도 이전 버전으로 복구 가능</li>
<li>새로운 기능 개발 중 문제 발생 시 안정적인 버전으로 롤백</li>
</ul>
<p><strong>효율적인 협업 환경 구축</strong></p>
<ul>
<li>여러 개발자가 동시에 다른 기능을 개발 가능</li>
<li>각자의 작업을 독립적으로 진행하고 나중에 통합</li>
<li>코드 리뷰와 품질 관리 프로세스 지원 </li>
</ul>
<h4 id="형상-관리-시스템의-유형">형상 관리 시스템의 유형</h4>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/05f986c7-e7ea-4476-96d1-7c24588878bb/image.png" alt=""></p>
<p><strong>중앙 집중식 버전 관리 (CVCS)</strong></p>
<ul>
<li>중앙 서버에 모든 버전 정보 저장</li>
<li>예: SVN (Subversion), CVS</li>
<li>단점: 서버 장애 시 작업 불가, 네트워크 필요</li>
</ul>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/9031f549-acaa-45d1-a1fe-70be4b3aee14/image.png" alt=""></p>
<p><strong>분산 버전 관리 (DVCS)</strong></p>
<ul>
<li>각 개발자가 전체 저장소의 복사본 보유</li>
<li>예: Git, Mercurial</li>
<li>장점: 오프라인 작업 가능, 빠른 속도, 유연한 워크플로우 </li>
</ul>
<h3 id="12-github란-무엇이고-왜-사용하는가">1.2 GitHub란 무엇이고 왜 사용하는가</h3>
<p><strong>GitHub</strong>는 Git을 기반으로 하는 클라우드 기반 소스 코드 호스팅 및 협업 플랫폼입니다. Microsoft가 소유한 이 웹 서비스는 전 세계 7,300만 명 이상의 개발자가 사용하는 세계 최대의 소프트웨어 개발 플랫폼입니다. </p>
<h4 id="git과-github의-관계">Git과 GitHub의 관계</h4>
<p><strong>Git (도구)</strong></p>
<ul>
<li>로컬 컴퓨터에서 실행되는 버전 관리 시스템</li>
<li>무료 오픈소스 소프트웨어</li>
<li>명령줄(CLI) 또는 GUI 도구로 사용</li>
<li>소스 코드 변경 사항을 추적하고 관리</li>
</ul>
<p><strong>GitHub (서비스)</strong></p>
<ul>
<li>Git 저장소를 클라우드에 호스팅하는 웹 서비스</li>
<li>Git의 기능에 협업 도구 추가 제공</li>
<li>웹 인터페이스를 통한 편리한 관리</li>
<li>소셜 코딩 기능 (팔로우, 스타, 포크 등) </li>
</ul>
<h4 id="github를-사용하는-이유">GitHub를 사용하는 이유</h4>
<p><strong>1. 코드 백업 및 접근성</strong></p>
<ul>
<li>클라우드에 코드 저장으로 데이터 손실 방지</li>
<li>어디서나 인터넷만 있으면 프로젝트 접근 가능</li>
<li>집 데스크톱에서 작업하다 노트북으로 이어서 작업 가능 </li>
</ul>
<p><strong>2. 협업 기능</strong></p>
<ul>
<li>Pull Request를 통한 코드 리뷰</li>
<li>Issue 트래킹으로 버그 및 기능 요청 관리</li>
<li>프로젝트 관리 도구 (Projects, Milestones)</li>
<li>팀원 간 코드 공유 및 토론</li>
</ul>
<p><strong>3. 오픈소스 생태계</strong></p>
<ul>
<li>전 세계 개발자들과 협업</li>
<li>오픈소스 프로젝트 기여 및 학습</li>
<li>포트폴리오 구축 및 경력 관리</li>
</ul>
<h4 id="github의-주요-활용-사례">GitHub의 주요 활용 사례</h4>
<p><strong>개인 프로젝트 관리</strong></p>
<ul>
<li>개인 학습 및 실험 프로젝트 저장</li>
<li>포트폴리오 웹사이트 호스팅 (GitHub Pages)</li>
<li>개발 이력 및 성장 과정 기록</li>
</ul>
<p><strong>팀 협업</strong></p>
<ul>
<li>기업 프로젝트의 소스 코드 관리</li>
<li>코드 리뷰 프로세스 구축</li>
<li>CI/CD 파이프라인 통합 (GitHub Actions)</li>
</ul>
<p><strong>오픈소스 기여</strong></p>
<ul>
<li>공개 프로젝트 참여 및 기여</li>
<li>커뮤니티와 지식 공유</li>
<li>기술 문서 및 튜토리얼 작성</li>
</ul>
<h3 id="13-github의-주요-개념">1.3 GitHub의 주요 개념</h3>
<p>GitHub를 효과적으로 사용하기 위해서는 핵심 개념들을 이해해야 합니다. 이러한 개념들은 Git의 기본 원리와 GitHub의 협업 기능을 포함합니다.</p>
<h4 id="repository-저장소">Repository (저장소)</h4>
<p><strong>정의</strong>: 프로젝트의 모든 파일과 각 파일의 수정 이력을 저장하는 공간</p>
<p><strong>로컬 저장소 (Local Repository)</strong></p>
<ul>
<li>개발자의 컴퓨터에 있는 저장소</li>
<li>오프라인에서도 작업 가능</li>
<li><code>git init</code> 명령으로 생성</li>
</ul>
<p><strong>원격 저장소 (Remote Repository)</strong></p>
<ul>
<li>GitHub 서버에 있는 저장소</li>
<li>팀원들과 공유하는 중앙 저장소</li>
<li>웹 인터페이스 또는 <code>git clone</code>으로 접근 </li>
</ul>
<h4 id="commit-커밋">Commit (커밋)</h4>
<p><strong>정의</strong>: 파일의 변경 사항을 저장소에 기록하는 행위</p>
<p><strong>커밋의 구성 요소</strong></p>
<ul>
<li>변경된 파일들의 스냅샷</li>
<li>커밋 메시지 (변경 내용 설명)</li>
<li>작성자 정보 (이름, 이메일)</li>
<li>타임스탬프</li>
<li>고유 식별자 (SHA-1 해시)</li>
</ul>
<p><strong>좋은 커밋 메시지 작성법</strong></p>
<ul>
<li>첫 줄: 50자 이내의 간결한 요약</li>
<li>본문: 변경 이유와 내용 상세 설명</li>
<li>명령형 현재 시제 사용 (&quot;Add feature&quot; not &quot;Added feature&quot;)</li>
</ul>
<h4 id="branch-브랜치">Branch (브랜치)</h4>
<p><strong>정의</strong>: 독립적인 작업 공간을 만들어 메인 코드에 영향 없이 개발할 수 있는 기능</p>
<p><strong>주요 브랜치 유형</strong></p>
<ul>
<li><strong>main/master</strong>: 배포 가능한 안정적인 코드</li>
<li><strong>develop</strong>: 개발 중인 최신 코드</li>
<li><strong>feature</strong>: 새로운 기능 개발용</li>
<li><strong>hotfix</strong>: 긴급 버그 수정용</li>
</ul>
<p><strong>브랜치 전략의 장점</strong></p>
<ul>
<li>여러 기능을 동시에 개발 가능</li>
<li>실험적인 코드 작성 시 안전성 확보</li>
<li>코드 리뷰 및 테스트 후 병합 가능</li>
</ul>
<h4 id="merge-병합">Merge (병합)</h4>
<p><strong>정의</strong>: 서로 다른 브랜치의 변경 사항을 하나로 합치는 작업</p>
<p><strong>병합 방식</strong></p>
<ul>
<li><strong>Fast-forward merge</strong>: 단순히 포인터만 이동</li>
<li><strong>3-way merge</strong>: 공통 조상을 기준으로 병합</li>
<li><strong>Squash merge</strong>: 여러 커밋을 하나로 압축</li>
</ul>
<p><strong>충돌 (Conflict) 해결</strong></p>
<ul>
<li>같은 파일의 같은 부분을 다르게 수정했을 때 발생</li>
<li>수동으로 충돌 부분을 확인하고 수정</li>
<li>충돌 해결 후 커밋하여 병합 완료</li>
</ul>
<h4 id="pull-request-pr">Pull Request (PR)</h4>
<p><strong>정의</strong>: 자신의 변경 사항을 다른 브랜치에 병합해달라고 요청하는 GitHub의 협업 기능</p>
<p><strong>Pull Request 프로세스</strong></p>
<ol>
<li>기능 브랜치에서 작업 완료</li>
<li>GitHub에 푸시</li>
<li>Pull Request 생성</li>
<li>팀원들의 코드 리뷰</li>
<li>피드백 반영 및 수정</li>
<li>승인 후 메인 브랜치에 병합</li>
</ol>
<p><strong>코드 리뷰의 이점</strong></p>
<ul>
<li>코드 품질 향상</li>
<li>지식 공유 및 학습</li>
<li>버그 조기 발견</li>
<li>팀 코딩 스타일 통일</li>
</ul>
<h4 id="fork와-clone">Fork와 Clone</h4>
<p><strong>Fork (포크)</strong></p>
<ul>
<li>다른 사람의 저장소를 자신의 GitHub 계정으로 복사</li>
<li>원본 저장소와 독립적으로 작업 가능</li>
<li>오픈소스 프로젝트 기여 시 주로 사용</li>
</ul>
<p><strong>Clone (클론)</strong></p>
<ul>
<li>원격 저장소를 로컬 컴퓨터로 복사</li>
<li><code>git clone &lt;repository-url&gt;</code> 명령 사용</li>
<li>로컬에서 작업 후 원격 저장소에 푸시</li>
</ul>
<p><strong>차이점</strong></p>
<ul>
<li>Fork: GitHub 서버 간 복사 (웹에서 수행)</li>
<li>Clone: GitHub → 로컬 컴퓨터 복사 (명령줄에서 수행)</li>
</ul>
<h4 id="issue-이슈">Issue (이슈)</h4>
<p><strong>정의</strong>: 버그 리포트, 기능 요청, 질문 등을 추적하고 관리하는 도구</p>
<p><strong>Issue 활용</strong></p>
<ul>
<li>버그 발견 시 상세 내용 기록</li>
<li>새로운 기능 제안 및 토론</li>
<li>작업 할당 및 진행 상황 추적</li>
<li>라벨, 마일스톤으로 분류 및 관리</li>
</ul>
<p><strong>Issue와 Pull Request 연결</strong></p>
<ul>
<li>커밋 메시지에 <code>#이슈번호</code> 포함</li>
<li>PR 설명에 <code>Closes #이슈번호</code> 작성</li>
<li>PR 병합 시 자동으로 이슈 종료</li>
</ul>
<h3 id="14-github-명령어-종류">1.4 GitHub 명령어 종류</h3>
<p>Git과 GitHub를 사용하기 위한 필수 명령어들을 카테고리별로 정리합니다. 명령줄 인터페이스(CLI)를 통해 효율적으로 버전 관리를 수행할 수 있습니다.</p>
<h4 id="저장소-초기화-및-복제">저장소 초기화 및 복제</h4>
<p><strong>새 저장소 생성</strong></p>
<pre><code class="language-bash">git init</code></pre>
<ul>
<li>현재 디렉토리를 Git 저장소로 초기화</li>
<li><code>.git</code> 폴더 생성 (버전 관리 정보 저장)</li>
</ul>
<p><strong>원격 저장소 복제</strong></p>
<pre><code class="language-bash">git clone &lt;repository-url&gt;</code></pre>
<ul>
<li>GitHub의 저장소를 로컬로 복사</li>
<li>자동으로 원격 저장소 연결 설정</li>
</ul>
<p><strong>예시</strong></p>
<pre><code class="language-bash">git clone https://github.com/username/repository.git</code></pre>
<h4 id="변경-사항-추적-및-커밋">변경 사항 추적 및 커밋</h4>
<p><strong>파일 상태 확인</strong></p>
<pre><code class="language-bash">git status</code></pre>
<ul>
<li>수정된 파일, 스테이징된 파일 확인</li>
<li>현재 브랜치 정보 표시</li>
</ul>
<p><strong>변경 사항 스테이징</strong></p>
<pre><code class="language-bash">git add &lt;파일명&gt;          # 특정 파일 추가
git add .                 # 모든 변경 파일 추가
git add *.js              # 특정 패턴 파일 추가</code></pre>
<p><strong>커밋 생성</strong></p>
<pre><code class="language-bash">git commit -m &quot;커밋 메시지&quot;
git commit -am &quot;메시지&quot;    # add + commit 동시 수행</code></pre>
<p><strong>커밋 이력 확인</strong></p>
<pre><code class="language-bash">git log                   # 전체 커밋 이력
git log --oneline         # 간략한 형식
git log --graph           # 그래프 형식</code></pre>
<h4 id="브랜치-관리">브랜치 관리</h4>
<p><strong>브랜치 생성 및 전환</strong></p>
<pre><code class="language-bash">git branch &lt;브랜치명&gt;           # 브랜치 생성
git checkout &lt;브랜치명&gt;         # 브랜치 전환
git checkout -b &lt;브랜치명&gt;      # 생성 + 전환
git switch &lt;브랜치명&gt;           # 브랜치 전환 (최신 방식)</code></pre>
<p><strong>브랜치 목록 확인</strong></p>
<pre><code class="language-bash">git branch                # 로컬 브랜치 목록
git branch -r             # 원격 브랜치 목록
git branch -a             # 모든 브랜치 목록</code></pre>
<p><strong>브랜치 삭제</strong></p>
<pre><code class="language-bash">git branch -d &lt;브랜치명&gt;        # 병합된 브랜치 삭제
git branch -D &lt;브랜치명&gt;        # 강제 삭제</code></pre>
<h4 id="병합-및-리베이스">병합 및 리베이스</h4>
<p><strong>브랜치 병합</strong></p>
<pre><code class="language-bash">git merge &lt;브랜치명&gt;</code></pre>
<ul>
<li>현재 브랜치에 다른 브랜치의 변경사항 병합</li>
<li>병합 커밋 생성</li>
</ul>
<p><strong>리베이스</strong></p>
<pre><code class="language-bash">git rebase &lt;브랜치명&gt;</code></pre>
<ul>
<li>커밋 이력을 선형으로 재정렬</li>
<li>깔끔한 히스토리 유지</li>
<li><strong>주의</strong>: 공개된 브랜치에서는 사용 지양</li>
</ul>
<p><strong>충돌 해결</strong></p>
<pre><code class="language-bash">git status                # 충돌 파일 확인
# 파일 수정 후
git add &lt;파일명&gt;
git commit                # 병합 완료</code></pre>
<h4 id="원격-저장소-작업">원격 저장소 작업</h4>
<p><strong>원격 저장소 연결</strong></p>
<pre><code class="language-bash">git remote add origin &lt;repository-url&gt;
git remote -v             # 원격 저장소 목록 확인</code></pre>
<p><strong>변경사항 업로드</strong></p>
<pre><code class="language-bash">git push origin &lt;브랜치명&gt;
git push -u origin main   # 기본 브랜치 설정
git push --all            # 모든 브랜치 푸시</code></pre>
<p><strong>변경사항 다운로드</strong></p>
<pre><code class="language-bash">git fetch                 # 원격 변경사항 가져오기 (병합 X)
git pull                  # 가져오기 + 병합
git pull origin main      # 특정 브랜치에서 가져오기</code></pre>
<p><strong>원격 브랜치 추적</strong></p>
<pre><code class="language-bash">git checkout -b &lt;로컬브랜치&gt; origin/&lt;원격브랜치&gt;</code></pre>
<h4 id="변경-사항-되돌리기">변경 사항 되돌리기</h4>
<p><strong>작업 디렉토리 변경 취소</strong></p>
<pre><code class="language-bash">git checkout -- &lt;파일명&gt;   # 파일 변경 취소
git restore &lt;파일명&gt;       # 파일 복원 (최신 방식)</code></pre>
<p><strong>스테이징 취소</strong></p>
<pre><code class="language-bash">git reset HEAD &lt;파일명&gt;
git restore --staged &lt;파일명&gt;  # 최신 방식</code></pre>
<p><strong>커밋 되돌리기</strong></p>
<pre><code class="language-bash">git reset --soft HEAD~1   # 커밋만 취소, 변경사항 유지
git reset --mixed HEAD~1  # 커밋 + 스테이징 취소
git reset --hard HEAD~1   # 모든 변경사항 삭제</code></pre>
<p><strong>커밋 수정</strong></p>
<pre><code class="language-bash">git commit --amend        # 마지막 커밋 수정
git commit --amend -m &quot;새 메시지&quot;  # 메시지만 수정</code></pre>
<p><strong>특정 커밋 되돌리기</strong></p>
<pre><code class="language-bash">git revert &lt;커밋해시&gt;      # 새로운 커밋으로 되돌림</code></pre>
<h4 id="임시-저장-stash">임시 저장 (Stash)</h4>
<p><strong>변경사항 임시 저장</strong></p>
<pre><code class="language-bash">git stash                 # 현재 변경사항 임시 저장
git stash save &quot;메시지&quot;    # 메시지와 함께 저장</code></pre>
<p><strong>임시 저장 목록 확인</strong></p>
<pre><code class="language-bash">git stash list</code></pre>
<p><strong>임시 저장 복원</strong></p>
<pre><code class="language-bash">git stash pop             # 가장 최근 stash 적용 + 삭제
git stash apply           # 적용만 하고 삭제 안함
git stash apply stash@{n} # 특정 stash 적용</code></pre>
<p><strong>임시 저장 삭제</strong></p>
<pre><code class="language-bash">git stash drop stash@{n}  # 특정 stash 삭제
git stash clear           # 모든 stash 삭제</code></pre>
<h4 id="태그-관리">태그 관리</h4>
<p><strong>태그 생성</strong></p>
<pre><code class="language-bash">git tag v1.0.0            # 경량 태그
git tag -a v1.0.0 -m &quot;버전 1.0.0 릴리스&quot;  # 주석 태그</code></pre>
<p><strong>태그 목록 확인</strong></p>
<pre><code class="language-bash">git tag                   # 모든 태그 목록
git tag -l &quot;v1.*&quot;         # 패턴 매칭</code></pre>
<p><strong>태그 푸시</strong></p>
<pre><code class="language-bash">git push origin v1.0.0    # 특정 태그 푸시
git push origin --tags    # 모든 태그 푸시</code></pre>
<p><strong>태그 삭제</strong></p>
<pre><code class="language-bash">git tag -d v1.0.0         # 로컬 태그 삭제
git push origin :refs/tags/v1.0.0  # 원격 태그 삭제</code></pre>
<h4 id="설정-및-도움말">설정 및 도움말</h4>
<p><strong>사용자 정보 설정</strong></p>
<pre><code class="language-bash">git config --global user.name &quot;이름&quot;
git config --global user.email &quot;이메일&quot;</code></pre>
<p><strong>설정 확인</strong></p>
<pre><code class="language-bash">git config --list         # 모든 설정 확인
git config user.name      # 특정 설정 확인</code></pre>
<p><strong>별칭(Alias) 설정</strong></p>
<pre><code class="language-bash">git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.st status</code></pre>
<p><strong>도움말</strong></p>
<pre><code class="language-bash">git help &lt;명령어&gt;
git &lt;명령어&gt; --help</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[논문 분석] 이상치 탐지 : RAE-MEPC]]></title>
            <link>https://velog.io/@you_n_sunny/%EB%85%BC%EB%AC%B8-%EB%B6%84%EC%84%9D-%EC%9D%B4%EC%83%81%EC%B9%98-%ED%83%90%EC%A7%80-RAE-MEPC</link>
            <guid>https://velog.io/@you_n_sunny/%EB%85%BC%EB%AC%B8-%EB%B6%84%EC%84%9D-%EC%9D%B4%EC%83%81%EC%B9%98-%ED%83%90%EC%A7%80-RAE-MEPC</guid>
            <pubDate>Tue, 17 Feb 2026 21:28:56 GMT</pubDate>
            <description><![CDATA[<p>논문 제목 : Recurrent Auto-Encoder with Multi-Resolution Ensemble and Predictive Coding for Multivariate Time-Series Anomaly Detection</p>
<h2 id="0-abstract">0. Abstract</h2>
<p>실세계 애플리케이션에서는 대규모 다변량 시계열 데이터가 광범위하게 활용되고 있으며, 이에 따라 다변량 시계열 이상 탐지는 다양한 산업 분야에서 핵심적인 역할을 수행하고 있다. 시계열 기반 이상 탐지는 설비 오작동을 사전에 감지하고 유지보수 비용을 절감함으로써 전반적인 시스템 생산성을 향상시키는 데 기여한다.</p>
<p>그러나 실제 시계열 데이터는 복잡한 비선형 시간적 종속성을 포함하고 있어, 정상 패턴을 정확히 모델링하는 것이 어렵다. 이러한 문제를 해결하기 위해서는 정상 동작의 시간적 동역학을 효과적으로 표현할 수 있는 풍부한 잠재 표현 학습이 필수적이다.</p>
<p>본 연구는 다중 해상도 앙상블(Multi-Resolution Ensemble)과 예측 코딩(Predictive Coding)을 결합한 비지도 다변량 시계열 이상 탐지 모델 RAE-MEPC를 제안한다. 제안 모델은 입력 시계열로부터 다양한 시간 스케일의 종속성을 포착하기 위해 다중 해상도 앙상블 인코딩 구조를 도입한다. 인코더는 서로 다른 인코딩 길이를 갖는 여러 서브 인코더로부터 추출된 시간적 특징을 계층적으로 집계하여, 정상 동작에 대한 다중 스케일 표현을 형성한다.</p>
<p>디코더는 이에 대응하는 다중 해상도 앙상블 디코딩 방식을 사용하여 입력 시계열을 재구성하며, 저해상도 표현이 고해상도 서브 디코더의 복원을 보조하는 구조를 갖는다. 또한 예측 코딩 모듈을 추가하여, 모델이 단순 재구성뿐 아니라 미래 시점 예측을 동시에 학습하도록 설계함으로써 시계열의 장기적 시간 의존성을 보다 효과적으로 반영한다.</p>
<h2 id="1-introduction">1. Introduction</h2>
<p>스마트 팩토리, 발전소, 사이버 보안 시스템과 같은 복잡한 산업 환경에서는 대규모 다변량 시계열 데이터가 지속적으로 축적되고 있다. 이러한 시스템의 안정적인 운영을 위해서는 작동 상태를 지속적으로 모니터링하고 잠재적인 위협을 조기에 식별하는 것이 중요하다. 특히 이상 징후에 대한 적절한 대응이 이루어지지 않을 경우 실제 사고로 이어질 수 있으며, 이는 상당한 경제적 손실과 운영 중단을 초래할 수 있다.</p>
<p>일반적으로 이상(anomaly)은 정상 관측 패턴에서 크게 벗어나는 데이터 포인트로 정의된다. 시계열 이상 탐지의 목적은 주어진 길이의 시계열 데이터에서 각 시점별 이상 여부를 판단하는 데 있다. 따라서 데이터를 지속적으로 생성하는 산업 시스템에서는 시스템 생산성을 향상시키기 위해 정확하고 신뢰할 수 있는 시계열 이상 탐지 모델이 요구된다.</p>
<p>효과적인 시계열 이상 탐지 방법은 정상 시계열 데이터에 내재된 비선형 시간적 정보를 충분히 포착하는 동시에, 사전에 관측되지 않은 이상 패턴까지 탐지할 수 있어야 한다. 그러나 이 문제는 여러 측면에서 어려움을 가진다.</p>
<p>첫째, 실제 환경에서는 이상 데이터의 수집이 매우 제한적이다. 이로 인해 대부분의 시계열 이상 탐지는 정상 데이터만을 이용한 비지도 학습 방식으로 수행된다.
둘째, 다변량 시계열 데이터는 복잡한 시간적 종속성과 확률적 변동성을 동시에 포함하고 있다. 기존 연구들은 이러한 문제를 해결하기 위해 정상 시계열의 장기적 종속성을 효과적으로 표현하는 잠재 표현 학습에 초점을 맞추어 왔다.</p>
<p>딥러닝 기반 시계열 이상 탐지 방법은 크게 예측 기반 방법과 재구성 기반 방법으로 구분된다.</p>
<p>예측 기반 방법은 과거 시계열을 입력으로 미래 값을 예측함으로써 정상 패턴을 학습한다. 이 접근은 정상 데이터로만 학습된 예측 모델이 비정상 패턴을 정확히 예측하지 못할 것이라는 가정에 기반한다. 추론 단계에서는 실제 관측값과 예측값 간의 오차가 사전에 정의된 임계값을 초과할 경우 해당 시점을 이상으로 판단한다. 이러한 방식은 시계열의 시간적 흐름을 직접적으로 모델링할 수 있다는 장점이 있다.</p>
<p>반면, 재구성 기반 방법은 정상 시계열 데이터를 잠재 공간으로 압축한 뒤 이를 다시 복원하는 과정을 통해 정상 패턴을 학습한다. 이 접근은 정상 데이터로만 학습된 모델이 이상 데이터를 제대로 재구성하지 못하며, 그 결과 재구성 오류가 크게 증가한다는 가정을 사용한다. 대부분의 재구성 기반 방법은 오토인코더 구조를 활용하며, 입력과 재구성 출력 간의 차이를 이상 점수로 사용한다.</p>
<p>일부 연구에서는 생성 모델을 결합하여 정상 데이터 분포를 보다 정밀하게 학습하려는 시도도 이루어지고 있다. 이러한 방법들은 생성자와 판별자 간의 적대적 학습을 통해 정상 분포를 모델링하고, 재구성 확률이 낮은 샘플을 이상치로 판단한다. 그러나 이 계열의 방법들은 학습 불안정성과 모드 붕괴 문제로 인해 실제 적용에서 제약이 존재한다.</p>
<p>본 연구에서는 예측 기반 접근과 재구성 기반 접근의 장점을 결합하여 보다 유익한 시계열 표현을 학습하기 위해, 다중 해상도 앙상블과 예측 코딩을 결합한 순환 오토인코더 모델 RAE-MEPC를 제안한다.</p>
<p>제안 모델은 인코딩 및 디코딩 단계 모두에서 서로 다른 시간 해상도를 활용하여 시계열의 다중 스케일 종속성을 학습한다. 인코더는 서로 다른 인코딩 길이를 갖는 여러 서브 인코더로 구성되며, 각 서브 인코더는 서로 다른 시간 스케일의 특징을 추출한다. 짧은 인코딩 길이는 전역 패턴을 포착하는 데 유리하며, 긴 인코딩 길이는 보다 세밀한 국소 패턴을 포착한다. 이후 이 특징들은 계층적으로 통합되어 최종 잠재 표현을 형성한다.</p>
<p>디코딩 단계에서는 다중 해상도 앙상블 구조를 통해 입력 시계열을 재구성하며, 가장 높은 해상도의 출력이 최종 재구성 결과로 사용된다. 또한 재구성 디코더와 함께 예측 디코더를 추가하여, 과거 잠재 특징으로부터 미래 시계열을 예측하도록 학습한다. 이를 통해 모델은 단순 재구성뿐 아니라 시간적 예측 관점에서도 정상 패턴을 학습하게 된다.</p>
<p>모델이 정상 데이터로 학습된 이후에는 입력 시계열과 재구성 결과 간의 차이를 기반으로 이상 여부를 판단한다.</p>
<p>본 연구의 주요 기여는 다음과 같다.</p>
<ul>
<li>다변량 시계열의 다중 스케일 종속성을 학습하기 위한 다중 해상도 앙상블 인코딩 구조를 제안한다.</li>
<li>재구성과 예측을 동시에 수행하는 예측 코딩 메커니즘을 도입하여 시간적 표현 품질을 향상시킨다.</li>
<li>단변량 및 다변량 시계열 데이터셋 전반에서 기존 벤치마크 모델 대비 우수한 이상 탐지 성능을 확인한다.</li>
</ul>
<h2 id="2-method">2. Method</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/2b64046b-431f-446d-ade6-c83e0b3fb5c6/image.png" alt=""></p>
<p>본 연구에서 제안된 방법은 인코딩된 특징들을 기반으로 입력 시계열을 재구성하고 미래 시계열을 예측하여 정상 패턴을 학습하는 것을 목표로 한다. 데이터는 다변량 시계열이며, 훈련 데이터의 대부분은 정상이라는 가정하에 기반하여 비지도 학습 방식으로 이상치를 탐지한다. 제안된 모델은 인코더와 두개의 디코더로 구성되며, 두 개의 디코더는 각각 입력 시계열을 재구성하고 미래 타임스텝을 예측하기 위해 사용된다.</p>
<h3 id="21-network-구조">2.1 Network 구조</h3>
<h3 id="211-multi-resolution-ensemble-encoding">2.1.1 Multi-Resolution Ensemble Encoding</h3>
<p>제안된 모델의 인코더의 목표는 여러 스케일에서 입력 시계열로부터 시간적 특징을 추출하는 것이다. 인코더는 서로 다른 길이를 가진 $$K^E$$ 개의 서브 인코더를 가진다. k번째 서브 인코더 $$E_k$$ 는 길이 $$T^{(E_k)}$$ 의 시계열에서 시간적 특징을 포착한다. 이 인코딩의 길이는 식 (1) 에 표시된 대로 정의된다.
<img src="https://velog.velcdn.com/images/you_n_sunny/post/6a339ca6-2601-4f50-80de-c58574b5942c/image.png" alt=""></p>
<p>여기서 $$\tau &gt; 1$$은 인코딩 길이를 결정하는 하이퍼파라미미터이며, 각 서브 인코더는 자신의 인코딩 길이와 일치하는 길이를 가진 시계열을 받는다. 원본 입력 시계열의 다중 스케일 종속성을 학습하기 위해 각 서브 인코더의 입력은 원본 입력과 유사한 형태를 가진다. 따라서 각 서브 인코더의 입력 시퀀스 $$x^{(E_k)}$$ 는 식 (2)에 표시된 대로 원본 시간 창 X를 길이 $$T^{E_k}$$의 부분 시퀀스로 다운 샘플링하여 얻는다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/81e3ba68-0486-421e-994b-cba38c7530e2/image.png" alt=""></p>
<p>제안된 모델의 모든 서브 인코더는 LSTM이ek.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/f01ea903-6a7d-4af0-ba7e-56f310fd7241/image.png" alt=""></p>
<p>짧은 인코딩 길이를 가진 저해상도 서브인코더는 거시적인 시간적 특성을 추출하고, 더 긴 인코딩 길이를 가진 고해상도 서브 인코더는 지역적인 시간적 패턴에 집중할 수 있다. 마지막으로 통합된 인코딩 표현은 낮은 해상도 ~ 높은 해상도 까지의 마지막 은닉 상태를 계층적으로 집계하여 얻어진다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/8b9caaf9-c495-4326-83b4-d3cb2a9db538/image.png" alt=""></p>
<p>여기서 $$\text{MLP}(E_{k} - E_{k+1})$$ 는 $$E_k$$ 와 $$E_{k+1}$$의 정보를 통합하는 fully connected Layer이다.
$$h^{(E_k)}<em>{T(E_k)}$$ 는 $$E_k$$ 의 마지막 은닉 상태이고 $$h^{(E_k)}$$는 $$E</em>{K(E)}$$부터 $$E_k$$까지 통합된 특징이다.
$$h^(E)$$ 는 모든 서브 인코더의 출력 앙상블로 부터 얻어진 최종 인코딩 representation이다. 이로써 RAE-MEPC의 인코더는 다중 해상도 앙상블 인코딩을 도입하여 general 하고 local 특징을 모두 가진 다중 스케일 종속성을 포착할 수 있다.</p>
<h3 id="212-multi-resolution-ensemble-decoding">2.1.2 Multi-Resolution Ensemble Decoding</h3>
<p>디코딩은 인코딩의 역순으로 입력 시계열을 구성한다. 서브 디코더의 해상도는 서브 인코더와 동일하며, 각 서브 디코더는 teacher forcing 없이 최종 인코딩된 표현을 기반으로 디코딩 길이의 입력 시계열을 재구성한다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/45703073-2ee0-4627-9250-2fa7c997f1c9/image.png" alt="">
<img src="https://velog.velcdn.com/images/you_n_sunny/post/40f9930b-d3d6-47e9-a174-118b9094155a/image.png" alt=""></p>
<p>추가로 각 서브 디코더에는 LSTM 입력에 작은양의 노이즈가 추가되는데, 입력 패턴의 부분적 손상에 대한 강건성을 향상시키기 위함이다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/43c36dbf-5c82-41a0-a1f3-902cebc64389/image.png" alt=""></p>
<p>여기서 $h(RD_k)<em>{t+1}$는 서브 디코더 $RD_k$의 이전 은닉 상태이며,
$h(RD</em>{k+1}){\left[t/\tau\right]}$는 더 거친 해상도를 갖는 인접 서브 디코더 $RD_{k+1}$의 해당 은닉 상태이다.</p>
<p>$\beta$는 더 거친 디코더의 정보를 반영하는 정도를 조절하는 하이퍼파라미터이다.
더 작은 $\beta$ 값을 사용함으로써 각 디코딩 단계에서 저해상도 정보가 보다 적극적으로 활용되도록 설정하였다.</p>
<p>이 과정을 통해 재구성 디코더는 상위 해상도의 출력을 생성할 때, 더 거친 해상도의 정보를 직접 참조함으로써 다중 해상도 정보를 효과적으로 통합할 수 있다.</p>
<p>다중 해상도 앙상블 디코딩 이후, 역순으로 재구성된 입력 시계열은 최고 해상도 디코더로부터 획득된다.
마지막으로, 식 (12)에 나타낸 바와 같이 재구성 디코더의 출력을 다시 역순으로 변환하여 최종 재구성 출력 $Y_{\text{recon}}$을 얻는다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/9ab176a5-23ff-49e5-a50c-3f840d38b178/image.png" alt=""></p>
<h3 id="213-predictive-coding">2.1.3 Predictive Coding</h3>
<p>본 연구에선, 입력 시계열의 장기 의존성을 추출하기 위해 predictive coding 을 도입한다. 예측 디코더의 목표는 인코더로부터 압축된 표현을 기반으로 T/2시점 이후의 미래 시계열을 예측하는 것이다. 제안된 모델에서  예측 디코더는 다음과 같이 예측 작업을 수행한다. </p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/ee3eabab-08e4-418f-a968-e8268c294902/image.png" alt=""></p>
<p>여기서 $h^{(PD)}_t$와 $y^{(PD)}_t$는 각각 예측 디코더의 $t$번째 시점에서의 은닉 상태와 예측된 출력이다.
$\text{MLP}^{(PD)}$는 출력 생성을 위한 완전 연결 계층(fully connected layer)이며, $\text{LSTM}^{(PD)}$는 시간적 의존성을 모델링하기 위해 예측 디코더 내부에 사용된 LSTM 모델이다.</p>
<p>마지막으로, 예측 디코더는 식 (16)에 나타낸 바와 같이 $T/2$ 시점 이후의 미래 시계열 $\vec{Y}_{pred}$를 예측한다.
예측 디코더는 예측 작업의 관점에서 시간적 의존성을 반영함으로써, 인코더가 정상 시계열로부터 보다 유익한 잠재 패턴을 학습하도록 유도한다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/2ded34c2-63ad-4cd8-95cf-ec7d8dc53eab/image.png" alt=""></p>
<h3 id="22-objective-function">2.2 Objective Function</h3>
<p>제안된 모델의 손실함수는 세가지 항으로 구성되는데, </p>
<ol>
<li>재구성 오류 $L_{\text{recon}}$</li>
<li>다중 Shape Constraint Loss $L_{\text{shape}}$</li>
<li>예측 오류 $L_{\text{pred}}$</li>
</ol>
<h4 id="reconstruction-loss">Reconstruction Loss</h4>
<p>재구성 손실 $L_{\text{recon}}$은 입력 시계열과 최고 해상도 서브 디코더 $RD_1$의 재구성 출력 간 평균 제곱 오차로 정의된다.</p>
<p>$$
L_{\text{recon}} = \sum_{t=1}^{T} | y^{(RD_1)}_t - x_t |_2^2
$$</p>
<p>이 손실은 재구성된 출력이 원본 입력 시계열과 최대한 유사하도록 유도한다.</p>
<h4 id="shape-constraint-loss">Shape Constraint Loss</h4>
<p>다중 해상도 형상 강제 손실 $L_{\text{shape}}$은 RAMED에서 제안된 방식으로, 서로 다른 해상도의 서브 디코더들이 입력 시계열과 일관된 시간적 추세를 학습하도록 유도하기 위해 도입되었다.</p>
<p>다중 해상도 앙상블 디코딩 구조에서는 각 서브 디코더가 서로 다른 시간 스케일의 출력을 생성하므로, 모든 해상도에서 입력과 유사한 패턴을 유지하는 것이 중요하다. 이미 $L_{\text{recon}}$이 최고 해상도 출력의 재구성을 보장하므로, $L_{\text{shape}}$은 이를 제외한 나머지 서브 디코더들에 대해서만 정의된다.</p>
<p>이 손실은 서로 다른 길이의 시계열 간 거리를 계산할 수 있는 DTW 기반으로 정의되며, DTW의 비미분 가능 문제를 해결하기 위해 smoothed DTW (sDTW)를 사용한다.</p>
<p>sDTW는 다음과 같이 정의된다.</p>
<p>$$
sDTW(X, \overleftarrow{Y}^{(RD_k)}) =
-\gamma \log \sum_{A \in \mathcal{A}} e^{-\langle AC\rangle/\gamma}
$$</p>
<p>여기서 $A$는 DTW 정렬 경로 행렬이며, $C$는 두 시계열 간 유클리드 거리로 구성된 정렬 비용 행렬이다. $\gamma$는 스무딩 강도를 제어하는 하이퍼파라미터이다.</p>
<p>이를 기반으로 $L_{\text{shape}}$는 다음과 같이 정의된다.</p>
<p>$$
L_{\text{shape}} =
\frac{1}{K(RD)-1}
\sum_{k=2}^{K(RD)} sDTW(X, \overleftarrow{Y}^{(RD_k)})
$$</p>
<p>이 손실은 서로 다른 해상도의 서브 디코더들이 입력과 유사한 시간적 구조를 유지하도록 강제한다.</p>
<h4 id="prediction-loss">Prediction Loss</h4>
<p>예측 손실 $L_{\text{pred}}$는 예측 디코더의 출력과 실제 미래 시계열 간 평균 제곱 오차로 정의된다.</p>
<p>$$
L_{\text{pred}} =
\sum_{t=1}^{T}
| y^{(PD)}<em>t - x</em>{t+\lceil T/2 \rceil} |_2^2
$$</p>
<p>이 손실은 예측 디코더가 $T/2$ 시점 이후의 미래 시계열을 정확히 예측하도록 유도하며, 동시에 인코더가 예측에 유용한 추가적인 시간 정보를 학습하도록 돕는다.</p>
<h4 id="total-loss">Total Loss</h4>
<p>위 세 가지 손실 항을 결합하여 RAE-MEPC의 총 손실은 다음과 같이 정의된다.</p>
<p>$$
L_{\text{total}} =
L_{\text{recon}} +
\lambda_{\text{shape}} L_{\text{shape}} +
\lambda_{\text{pred}} L_{\text{pred}}
$$</p>
<p>여기서 $\lambda_{\text{shape}}$와 $\lambda_{\text{pred}}$는 각각 형상 손실과 예측 손실의 중요도를 조절하는 하이퍼파라미터이다.</p>
<h2 id="3-현업-아이디어-적용">3. 현업 아이디어 적용</h2>
<p>방법1. 해상도 분할</p>
<ul>
<li>S를 기준으로 해상도를 분할하여, 각 서브 인코더가 입력받는 s를 해당 S의 중요 s만 한정하거나 Attention을 추가하여 학습하도록 함</li>
<li>만약, 도메인 지식으로 추가한다면, 입력에 중요도를 직접 곱하여 추가하며 손실함수에도 이를 반영 (중요도 높은 s에 더 큰 패널티 부여식으로..)</li>
</ul>
<p>방법2. S(t-1) -&gt; S(t) 를 예측하는 모델</p>
<ul>
<li>두 단계의 물리적 인과관계를 학습하여 S간의 관계를 학습하게 함</li>
</ul>
<p>+) 추가 방법 손실 함수에 R set값 추가</p>
<ul>
<li>단, rd 만 하는 값도 존재하기 때문에 이 부분은 고려 필요</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[논문 분석] 이상치 탐지 : MCA-VAE ]]></title>
            <link>https://velog.io/@you_n_sunny/Multi-Channel-Multi-Scale-Convolution-Attention-Variational-Autoencoder-MCA-VAE-An-Interpretable-Anomaly-Detection-Algorithm-Based-on-Variational-Autoencode</link>
            <guid>https://velog.io/@you_n_sunny/Multi-Channel-Multi-Scale-Convolution-Attention-Variational-Autoencoder-MCA-VAE-An-Interpretable-Anomaly-Detection-Algorithm-Based-on-Variational-Autoencode</guid>
            <pubDate>Tue, 17 Feb 2026 18:22:24 GMT</pubDate>
            <description><![CDATA[<p><strong>논문 제목</strong> : Multi-Channel Multi-Scale Convolution Attention Variational Autoencoder (MCA-VAE): An Interpretable Anomaly Detection Algorithm Based on Variational Autoencoder</p>
<h2 id="0-abstract">0. Abstract</h2>
<p>산업 자동화와 IoT 기술의 급속한 발전으로 공장 환경에서는 대규모 다차원 시계열 데이터가 지속적으로 생성되고 있다. 이러한 환경에서 이상 탐지는 설비 고장, 생산 중단, 비용 손실을 방지하기 위한 핵심 기술로 자리 잡았다. 따라서 이상 탐지 알고리즘은 높은 정확도뿐 아니라, 이상을 유발한 특정 센서 또는 장비를 신속히 식별할 수 있는 해석 가능성을 동시에 요구받는다.</p>
<p>그러나 기존 신경망 기반 이상 탐지 모델들은 다차원 시계열 데이터에 포함된 <strong>차원 내 시간적 특징</strong>과 <strong>차원 간 관계적 특징</strong>을 동시에 효과적으로 포착하지 못하는 한계를 가진다. 또한 두 특징을 모두 고려하는 일부 복잡한 모델은 이상 원인을 설명하지 못해 실제 산업 현장 적용에 어려움이 있다.</p>
<p>본 연구에서는 이러한 문제를 해결하기 위해, 변이형 오토인코더(VAE)를 기반으로 한 해석 가능한 이상 탐지 모델인 MCA-VAE(Multi-Channel Multi-Scale Convolution Attention Variational Autoencoder)를 제안한다. 제안 모델은 다중 스케일 가중치 공유 컨볼루션을 통해 각 차원의 시간적 특징을 추출하고, 멀티 헤드 어텐션 메커니즘을 활용하여 센서 간 관계적 특징을 모델링한다. 이후 추출된 특징을 VAE 잠재 공간에 매핑하고 재구성 오차를 기반으로 이상을 탐지한다.</p>
<p>또한 VAE의 확률 분포 특성을 활용하여 각 센서별 조건부 확률을 계산함으로써, 이상 발생 원인을 센서 단위로 해석할 수 있는 메커니즘을 제공한다. 실험 결과, 제안 모델은 산업 제어 데이터셋에서 AUC 0.982, F1 점수 0.905를 기록하며 기존 베이스라인 대비 우수한 성능을 보였고, 동시에 정확한 이상 원인 분석 능력을 확인하였다.</p>
<hr>
<h2 id="1-introduction">1. Introduction</h2>
<p>최근 산업 제어 시스템은 IoT 기술과 네트워크 인프라의 발전으로 인해 점점 더 많은 센서와 장비가 연결되는 구조로 변화하고 있다. 이러한 변화는 생산 공정의 자동화와 정밀 모니터링을 가능하게 하지만, 동시에 시스템 전반의 위험도를 증가시키는 요인이 된다.</p>
<p>현대 제조 공장에서는 다수의 센서와 액추에이터가 동시에 동작하며 고차원 다변량 시계열 데이터를 생성한다. 이 데이터는 설비 상태를 실시간으로 반영하지만, 이상 상황이 발생할 경우 빠르게 전체 시스템으로 전파될 수 있다. 예를 들어 특정 센서의 비정상 값이 즉시 탐지되지 않을 경우, 국소적 고장이 전체 생산 라인으로 확산되어 심각한 가동 중단과 경제적 손실로 이어질 수 있다.</p>
<p>이러한 배경에서 이상 탐지 알고리즘은 단순한 이벤트 감지를 넘어 다음과 같은 요구 조건을 충족해야 한다.</p>
<ul>
<li>다차원 시계열에서 높은 탐지 정확도를 유지할 것</li>
<li>이상 발생 시점을 명확히 식별할 것</li>
<li>이상을 유발한 센서 또는 장비를 함께 제공할 것</li>
</ul>
<p>그러나 기존 방법들은 다음과 같은 구조적 한계를 가진다.</p>
<p>첫째, 많은 모델이 <strong>차원 내 시간적 특징</strong>과 <strong>차원 간 관계적 특징</strong> 중 하나에만 집중한다. 일부 모델은 개별 센서의 시간 패턴을 잘 학습하지만 센서 간 상호작용을 충분히 반영하지 못하며, 반대로 관계 구조에 집중한 모델은 각 센서의 세밀한 시간 변화를 놓치는 경우가 많다.</p>
<p>둘째, 두 특징을 모두 고려하는 모델이라 하더라도 해석 가능성이 부족한 경우가 대부분이다. 이러한 모델은 특정 시점에서 이상이 발생했음을 알려줄 수는 있지만, 실제로 어떤 센서가 문제의 원인인지 설명하지 못한다. 이는 산업 현장에서 유지보수 엔지니어가 즉각적인 조치를 취하는 데 큰 제약이 된다.</p>
<p>결과적으로, 기존 다변량 시계열 이상 탐지 기법은 다음 두 가지 측면에서 개선이 필요하다.</p>
<ol>
<li>시간적 특징과 관계적 특징을 동시에 효과적으로 모델링할 수 있는 구조</li>
<li>이상 발생 원인을 센서 단위로 제공할 수 있는 해석 가능성</li>
</ol>
<p>본 연구는 이러한 문제를 해결하기 위해 MCA-VAE 모델을 제안한다. 제안된 방법은 다중 스케일 컨볼루션을 통해 각 센서의 시간 패턴을 추출하고, 멀티 헤드 어텐션을 활용하여 센서 간 관계를 학습한다. 이후 VAE 기반 확률 모델을 통해 이상을 탐지하며, 재구성 확률 분해를 통해 센서별 이상 점수를 산출한다.</p>
<p>이를 통해 기존 방법이 가지는 정확도와 해석 가능성의 트레이드오프를 완화하고, 실제 산업 제어 환경에서 활용 가능한 이상 탐지 프레임워크를 제시하는 것을 목표로 한다.</p>
<h2 id="2-methods">2. Methods</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/307faea1-5586-4601-b938-415e3e25e862/image.png" alt=""></p>
<p>시계열 이상치 탐지는 대상에 따라 단변량과 다변량으로 나눌 수 있다.</p>
<p>단변량 시계열 이상치 유형은 점 이상치와 패턴 이상치라는 두 가지 주요 범주로 나눌 수 있다. 연속된 점들의 집합은 패턴 이상치를 유발하며, 이는 주기 이상치와 모양 이상치, 추세 이상치로 더 세분화 될 수 있다.</p>
<p>다변량 시계열의 이상치 유형은 세 가지 범주로 나눌 수 있다. 여기엔  단변량의 가장 일반적인 이상치 유형도 속하, 상관 이상치와 상관-시간 이상치라는 두 가지 고유한 이상치 유형이 있다. 상관 이상치는 여러 지표 간의 관계가 갑작스러운 변화를 겪었거나 과거 관계를 위반 했음을 나타낸다. 상관-시간 이상치는 상관 이상치와 시간 이상치를 모두 포함하며 탐지하기 가장 쉬운 이상치 유형이다.</p>
<p>현재 이상치 탐지 모델들은 시계열 데이터의 각 차원을 모델링하는 데 집중하여 시계열 데이터 내의 차원 간 상관 관계를 무시하거나, 차원 간 상관관계를 추출하기 위해 복잡한 구조를 사용하지만, 각 차원 데이터의 시간 종속성에 대한 상세한 모델링 과정이 부족하다는 것을 이 논문에서는 문제점으로 지적한다. 따라서 더 나은 이상치 탐지 정확도를 달성하기 위해 <strong>차원 내 시간 종속성과 차원 간 상관관계를 동시에 모델링 하는 것을 제안</strong>한다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/b383135c-ae72-474a-afda-ec22c01cdabc/image.png" alt=""></p>
<ul>
<li>$$X = {X_1, \dots, X_N }$$: 데이터 셋</li>
<li>$$X \in \mathbb{R}^{M \times N}$$ : 연속적인 관측치를 등간격 샘플링 한 것</li>
<li>M : 차원수</li>
<li>N : 데이터 길이</li>
<li>$$X_i$$ : 하나의 차원</li>
<li>$$X_t$$ : 하나의 관측치</li>
</ul>
<p>여기서 Multi-Variate Time Series 길이에 문맥 정보를 통합하고, 이상치 결과를 계산하기 위해 $$W$$ 길이의 슬라이딩 윈도우를 적용한다.
이상치 탐지는 관측된 결과이상이 발생한 원인을 (어떤 센서가 이상인지) 찾아내는 것을 목표로 한다.</p>
<h3 id="21-network-구조">2.1 Network 구조</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/42cedd76-c71d-4026-8180-d8dea22168df/image.png" alt=""></p>
<p>모델은 위의 그림에서 나타낸 바와 같이 세 부분으로 구성된다.</p>
<ol>
<li>다중 스케일 가중치 공유 컨볼루션 네트워크(Multi-Scale Weight Sharing Convolutional Network)<ul>
<li>이는 서로 다른 시간 스케일에서 특징을 효과적으로 포착할 수 있다.</li>
<li>작은 Conv 커널은 단기 시계열 특징을 포착할 수 있으며, 큰 Conv 커널은 장기 시계열 특징을 포착할 수 있다.</li>
<li>또한, 다중 채널 지역 가중치 공유 방법은 정확도를 보장하면서 매개변수 수를 최대한 줄일 수 있다.</li>
</ul>
</li>
</ol>
<ol start="2">
<li>다중 헤드 어텐션 메커니즘(Multi-Head Attention Mechanism)<ul>
<li>서로 다른 데이터 차원 간의 상호 영향을 유연하게 파악하여, 차원 간의 관계 특징을 모델이 포착하도록 돕는다.</li>
</ul>
</li>
</ol>
<p>3.
개선된 변이형 오토인코더(Improved Variational Autoencoder, VAE)</p>
<ul>
<li>제안된 KL Divergence 최적화 방법은 VAE 모델 학습이 더 나은 특징 추출 결과를 달성하도록 한다.</li>
<li>또한 VAE 모델의 확률 분포는 이상치 해석에 사용된다.</li>
</ul>
<h4 id="211-multi-scale-weight-sharing-convolutional-network">2.1.1 Multi-Scale Weight Sharing Convolutional Network</h4>
<p>기존의 1D CNN 모델은 단일 채널 시계열 데이터에만 적합하며 , 여러 상관된 채널의 정보를 완전히 활용하지 못한다. 다중 채널 1D CNN 모델은 여러 개의 독립적인 Conv 커널을 사용하여 각 채널에 대해 Conv 연산을 수행한 후, 각 채널의 Conv 결과를 병합한다. 이 접근 방식은 여러 차원에서 시간적 특징을 동시에 추출하고 병합 연산을 통해 다중 채널 정보를 완전히 활용하여 모델의 성능과 정확도를 향상시킬 수 있다.</p>
<p>이를 적용하기 위해 각 입력 데이터 채널을 독립적으로 처리하기 위하여 다중 채널 커널 K를 생성해야 하는데, 이 수식은 아래와 같다. </p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/77f66e1b-067f-4716-b15d-b975dd789336/image.png" alt=""></p>
<ul>
<li>$$n_c$$ : 데이터의 채널 수</li>
<li>$$X_c$$ : 시점 t-i+1에서의 h번째 채널 측정 값</li>
</ul>
<p>단일 채널에 대한 Conv 과 다른 점은 모든 채널에 대한 추가적인 합산이 다르다는 점이다. 다중 채널 Conv에서는 각 채널에 대해 Conv 커널이 설정되며, 각 Conv 커널의 가중치 수가 더 많아져 모델 파라미터가 커지고 학습 속도가 느려지며 과적합 위험이 증가한다. </p>
<p>때문에 이 논문에서는 개선된 다중 스케일 가중치 공유 Conv 방법을 제안하는데, 많은 파라미터와 특징 추출 효과 사이의 균형과 다른 시간 스케일에 적응하지 못하는 문제를 해결하기 위해 가중치 공유 방법을 제안한다. 
(다른 시간 스케일에 적응하지 못하는 문제란, 하나의 고정된 크기의 커널은 모든 시간 스케일의 특징을 동시에 잘 포착하기 어렵다는 말이다.)</p>
<p>가중치 공유는 서로 다른 위치에 있는 Conv 커널에 동일한 파라미터를 사용하는 것을 의미한다. 이 방법은 각 Conv 커널의 파라미터 수를 단일 Conv 커널과 동일한 수로 압축할 수 있어, 신경망의 파라미터 수를 크게 줄일 수 있다.</p>
<p>구체적으로, 컨볼루션 레이어에 k개의 컨볼루션 커널이 포함되어 있고, 각 컨볼루션 커널의 크기가 $$h \times w$$ 이며, 입력 채널 수가 $$c_{in}$$ 이고, 출력 채널 수가 $$c_{out}$$이라고 가정하면,</p>
<p>가중치 공유 방법을 사용하지 않으면 이 레이어의 파라미터 수는 $$k \times h \times w \times c_{in} \times c_{out}$$이다. 하지만 가중치 공유 방법을 사용하면 이 레이어의 파라미터 수는 $$k \times h \times w$$ 에 불과하다. 따라서 가중치 공유는 파라미터 수를 크게 줄여 모델의 복잡성을 감소시키고 모델의 학습 속도 및 일반화 성능을 향상시킬 수 있다.</p>
<p>하지만 global 가중치 공유 방법은 모든 conv 커널이 동일한 가중치 행렬을 사용하기 때문에 학습이 불충분하다는 문제가 있다.</p>
<p>이런 문제를 해결하기 위해 이 논문에선 추가로 local 가중치 공유 방법을 제안한다. local 가중치 공유는 가중치 행렬을 여러개의 지역 가중치 행렬로 분해하며, 지역 가중치 행렬은 독립적으로 조정될 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/05b9616d-5755-41fa-963f-6964e1660f9c/image.png" alt=""></p>
<p>수식 (2)는 global 가중치 공유 식이며, (3)은 local 가중치 공유 식이다. 여기서 $$W^{(i,j)}_{m,n,l,k}$$는 좌표 (i, j)에서의 지역 가중치 행렬 내 가중치를 나타낸다. 논문 저자들은 데이터 간의 상관관계를 분석하여 상관관계가 강한 다차원 데이터에 대해 먼저 동일한 지역 가중치 행렬을 사용한다고 한다.
(→ 내가 지금 적용하고자 하는 아이디어에는 센서들을 그룹화 해서 step 별로 사용하면 될 것 같다.)</p>
<h4 id="212-implementation-of-the-multi-scale-convolutional-network">2.1.2 Implementation of the Multi-Scale Convolutional Network</h4>
<p>고정된 Conv 커널이 특정 스케일의 특징만 포착할 수 있다는 문제를 해결하기 위해 데이터셋에서 다양한 시간 스케일의 고도로 표현력 있는 잠재 특징을 추출하기 위한 다중 스케일 Conv 커널을 이 논문에선 추가로 제안한다. 여기선 세 개의 병렬 경롤르 가진 Conv 레이어를 구성하며, 각 경로는 서로 다른 커널 크기를 가지고, 각 Conv 레이어는 다중 Conv 방법을 사용한다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/e592b379-bdcf-4542-8941-5f89754b75af/image.png" alt=""></p>
<p>논문에선 공장 데이터는 단기적인 규칙성(단일 센서 스케일에서 제품이 공정에 들어가거나 나가는 순간을 고려), 중기적인 패턴(단일 공정 내에서 발생하는 생산 공정을 고려), 그리고 장기적인 변화(운송, 생산, 검사를 포함하여 한 제품에서 다음 제품까지의 전체 주기를 고려)를 포함하여 이 세가지 상황을 포괄하기 위해 주기를 짧은주기, 중간 주기, 긴주기로 크기를 선택했다고 한다.
(→ 이 부분을 내 현업 도메인 지식에 맞춰 설명 가능하고 해석 가능한 주기로 변경하면 좋을 것 같다.)</p>
<p>이후 Conv 및 풀링 후 얻어진 특징 벡터들은 연결되어 새로운 전역 특징 벡터 행렬을 형성하는데, 이후 어텐션 메커니즘은 이런 계층적 특징으로 부터 유횽한 정보를 추출한다. 다양한 스케일의 Conv 레이어를 사용하여 특징을 추출한 후, 데이터 공간 차원을 줄이고 가장 중요한 특징을 유지하기 위해 특징 선택 및 다운 샘플링을 수행한다. 여기선 맥스 풀링을 사용하여 특징 맵에 대한 다운 샘플링 연산을 수행한다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/52b0d53b-abea-4d15-8b9b-66a1a2d67dc9/image.png" alt=""></p>
<p>여기서 $$c_k$$ 는 k의 커널을 사용하여 추출된 특징 맵을 나타내고, , $$\hat{c}_k$$는 샘플링 후의 특징 맵을 나타낸다. 
![]
(<a href="https://velog.velcdn.com/images/you_n_sunny/post/29a75a97-183e-4a38-aa5d-aaf1cb545675/image.png">https://velog.velcdn.com/images/you_n_sunny/post/29a75a97-183e-4a38-aa5d-aaf1cb545675/image.png</a>)</p>
<p>이후 전역 특징 벡터 행렬 T는 추가 처리를 위해 멀티헤드 어텐션 메커니즘 입력으로 사용된다.</p>
<h4 id="213-multi-head-attention-mechanism">2.1.3 Multi-Head Attention Mechanism</h4>
<p>차원 간의 상관 관계 특징을 포착하기 위해, 이전 단계에서 얻은 컨볼루션 네트워크로부터 유용한 정보를 추가로 추출하기 위해 멀티 헤드 어텐션 메커니즘을 사용한다. 단계는 다음과 같다</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/1fbba719-7c2c-4b9a-9780-4a978dcf9226/image.png" alt=""></p>
<ol>
<li>먼저 이전 Conv 네트워크에서 얻은 전역 특징 벡터 행렬 T로 부터 행렬 Q, K, V를 초기화한다.</li>
<li>Scaled Dot-product Attention(SDA)를 이용하여 Q와 K의 내적을 계산하여 유사도를 구한 다음, 내적의 결과가 너무 커지는 것을 방지하기 위해 $$d_k$$ 에 루트를 씌운 값으로 나눈다. (여기서 $$d_k$$는 행렬 $$k$$의 차원))</li>
<li>이후 softmax 함수를 이용하여 정규화 한다.</li>
<li>행렬 V와 곱하여 어텐션 표현을 얻는다.</li>
</ol>
<p>이를 이용한 아이디어로 서로 다른 파라미터 $$W_{Q_i}, W_{K_i}, W_{V_i}$$를 사용하여 행렬 Q, K, V 에 선형 변환을 수행하고, 그 결과를 SDA에 입력한다. 헤드 i부터 h까지의 결과들을 하나의 행렬로 연결하고 파라미터 W를 곱하여 최종 선형 변환을 완료하여 식(8)에 나타난 멀티 헤드 어텐션 메커니즘의 최종 출력을 얻는다.
<img src="https://velog.velcdn.com/images/you_n_sunny/post/032a8e11-9f90-4b53-a9cc-4305f493f415/image.png" alt=""></p>
<h4 id="214-improved-variational-autoencoder">2.1.4 Improved Variational Autoencoder</h4>
<p>이 논문에선 전역적인 관점에서 다중 변수의 정보를 모델링하고 다른 차원 간의 종속성을 더 잘 포착하기 위해, 어텐션 메커니즘의 출력을 VAE 모델의 잠재 공간으로 매핑한다. 그러나 일반적인 VAE가 확률적 경사 하강법(SGD)과 같은 최적화 방법을 사용하여 VAE를 훈련할 때, 인코더는 각 입력을 잠재 분포의 고정된 점으로 매핑하는 경향이 있기 때문에, 이로 인해 VAE의 KL divergence 가 상수화되어 잠재 변수와 더 이상 관련이 없게 되므로 효과적으로 최적화될 수 없다. 이 문제를 해결하기 위해 이 논문에선 수정된 Evidence Lower Bound (ELBO) 표현과 재매개변수화 최적화를 갖춘 개선된 VAE 방법을 제안한다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/a9300f30-2e01-4214-99ea-1819399be912/image.png" alt=""></p>
<p>위의 식은 기존 VAE의 손실함수 ELBO이며, 여기서 $$E_{q_\phi(z|X)}$$는 입력 X가 주어졌을 때 z의 기댓값을 나타낸다. $$KL({q_\phi(z|X)}  |  p(z))$$ 는 사후 분포 $$q_\phi(z|X)$$와 사전 분포 $$p(z)$$간의 KL divergence를 나타낸다. 그러나 실제에서는 KL divergence 의 가중치가 너무 크거나 데이터셋의 분포와 사전 분포간의 많은 차이가 있는 경우, 모델은 샘플 간의 다양성을 무시하고 입력 샘플을 잠재 공간의 작은 영역으로 매핑하는 경향이 있다. 이 문제를 해결하기 위해 논문에서는 ELBO의 첫번째 항인 재구성 오류를 최적화 하고 더 많은 가중치를 부여한다. 수정된 식은 아래와 같은데
<img src="https://velog.velcdn.com/images/you_n_sunny/post/eeb2edd5-309d-4d40-b64e-d2c27efd7f93/image.png" alt=""></p>
<p>여기서 베타는 재구성 오류와 KL divergence 오류의 상대적 중요도를 제어하는데 사용한다. 만약 베타가 작으면, 모델은 재구축에 더 집중하여 입력 데이터를 더 정확하게 복원하는데 능숙해진다. 하지만 잠재 공간이 너무 압축되어 잠재 변수의 다양성이 줄어들고 새로운 데이터를 생성하는 능력이 떨어질 수 있다.
반대로 베타가 크다면  잠재 공간의 여러 지점에서 샘플링 했을 때 생성되는 데이터가 다양성을 가지게 되기 때문에 정상 데이터를 완벽하게 복원하는 능력이 떨어질 수 있다.</p>
<h4 id="215-anomaly-explanation-based-on-reconstruction-probability">2.1.5 Anomaly Explanation Based on Reconstruction Probability</h4>
<p>이 논문에선 이상확률에 대해 계산하여 비정상 스코어를 계산할 수 있도록 하는 방법또한 제안하였는데, 알고리즘이 이상 설명 기능을 제공하여 엔지니어가 비정상 장치를 신속하게 탐지할 수 있도록, VAE의 확률론적 이론을 결합하고 이상 설명을 위해 재구성 확률을 사용한다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/84ad3225-5727-43a0-af81-7944d1d8c50b/image.png" alt=""></p>
<p>여기서 $$f_\theta$$는 VAE의 디코더를 나타내고, $$\theta$$는 디코더의 매개변수를 나타냅니다. 그런 다음 각 차원의 재구성 확률 $$P_\theta(x_i^t | Z_{t-T:t})$$를 계산한다. 
(잠재 변수 조건 $$Z_{t-T:t}$$가 주어졌을 때 i번째 차원의 $$x_t$$에 대한 확률)</p>
<p>이후, 가우시안 분포를 통해 $$\hat{x}_i^t$$의 확률 밀도 함수 값을 계산한다.
<img src="https://velog.velcdn.com/images/you_n_sunny/post/8895d1a6-3fd8-421d-a94a-535b4acd5b96/image.png" alt=""></p>
<p>이상 점수가 이상을 직관적으로 표시하기 위해 
<img src="https://velog.velcdn.com/images/you_n_sunny/post/9f3a9ef4-18d3-4cc3-945e-10361a9ce8dc/image.png" alt=""></p>
<p>재구성 확률에 음수 로그 변환을 적용한다.
여기서 $$s_i^t$$ 는 i번째 차원에서 입력샘플 $$x_t$$의 이상 점수를 나타내며, 이상 점수가 높을 수록 해당 샘플은 더 비정상임을 나타낸다.</p>
<h2 id="3-현업-아이디어-적용">3. 현업 아이디어 적용</h2>
<p>이 논문에서 제안된 방법을 응용하여 현업 문제에 적용을 해야한다. 크게 3가지 방식으로 적용할 수 있는데, 구체화는 팀원들과 논의한 후에 ..!</p>
<p>방법 1. S 별 Attention Masking </p>
<ul>
<li>Attention Mask 를 적용하여 S에 따라 동적으로 생성되게 한다.</li>
<li>중요 센서간(i,j)의 관계에서만 학습이 되도록 변환</li>
<li>이 방법은 논문에서 제안한 구조에서 크게 바뀌지 않는다.</li>
</ul>
<p>방법 2. S별로 Multi Scale Convolution Network를 만들어 학습</p>
<ul>
<li>모든 센서 공통 CNN 와, 특정 S에만 활성화되는 S 전용 필터를 병렬로 두어 S의 데이터가 들어오면, S번에 할당된 컨볼루션 필터들만 활성화되어 특징을 추출</li>
<li>입력데이터와 S 임베딩을 결합하여 게이트를 만드는 방식으로, S에서 중요한 s의 신호는 증폭, 나머지는 억제한 상태로 Conv 에 전달</li>
<li>Attention Head 자체를 S에 할당하여 최종 특징 벡터 생성</li>
<li>이후 특징 벡터를 VAE의 잠재공간 z로 보낼 때 S를 다시 결합하여 z에서 S별로 Cluster 가 다르게 형성되도록 함</li>
</ul>
<p>방법 3. VAE의 Latent 를 S 별로 분리 (조건부 입력)</p>
<ul>
<li>S별로 다른 Latent 분포 학습</li>
<li>$$\text{ELBO}(S) = E_{q_\phi(z|x, S)}[\log p_\theta(x|z, S)] - \beta KL(q_\phi(z|x, S) | p(z|S))$$</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Python] 메모리/속도 최적화 방법]]></title>
            <link>https://velog.io/@you_n_sunny/Python-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%86%8D%EB%8F%84-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@you_n_sunny/Python-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%86%8D%EB%8F%84-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 11 Feb 2026 14:50:12 GMT</pubDate>
            <description><![CDATA[<p>데이터 사이언스 작업에서 메모리와 속도 최적화는 대용량 데이터 처리의 핵심입니다. Python의 Pandas와 NumPy를 중심으로 한 데이터 처리 라이브러리들은 편의성이 뛰어나지만, 최적화 없이 사용하면 메모리 부족이나 처리 속도 저하 문제가 발생할 수 있습니다.  </p>
<p>+) 이 글로 사내에서 세미나를 했는데 아주 Hot했습니다..^^</p>
<h2 id="1-메모리-최적화-기법">1. 메모리 최적화 기법</h2>
<p>메모리 최적화는 대용량 데이터 처리의 첫 번째 관문입니다. Pandas는 기본적으로 가장 큰 데이터 타입을 사용하기 때문에 메모리가 낭비되며, 적절한 최적화 없이는 메모리 부족 오류가 발생할 수 있습니다.  </p>
<h3 id="11-데이터-타입-최적화">1.1 데이터 타입 최적화</h3>
<p>Pandas에서 메모리 사용량을 줄이는 가장 효과적인 방법은 데이터 타입을 최적화하는 것입니다. 기본 int64를 int32로, float64를 float32로 변환하면 메모리 사용량을 절반으로 줄일 수 있습니다. </p>
<pre><code class="language-python"># 데이터 타입 확인
print(df.dtypes)

# 정수형 컬럼 최적화
df[&#39;id&#39;] = df[&#39;id&#39;].astype(&#39;int32&#39;)  # int64 → int32

# 범주형 데이터 변환
df[&#39;category&#39;] = df[&#39;category&#39;].astype(&#39;category&#39;)

# 메모리 사용량 확인
print(df.memory_usage(deep=True))</code></pre>
<h3 id="왜-int64-→-int32-float64-→-float32-로-바꾸면-메모리가-절반이-되는가">왜 int64 → int32, float64 → float32 로 바꾸면 메모리가 절반이 되는가?</h3>
<p>이 현상은 Pandas의 특수한 동작 때문이 아니라, <strong>컴퓨터가 숫자를 저장하는 방식</strong>에서 비롯됩니다.</p>
<p>컴퓨터에서 모든 숫자는 이진수(bit) 형태로 저장되며, 각 데이터 타입은 고정된 크기를 가집니다.</p>
<ul>
<li>1 byte = 8 bits</li>
</ul>
<p>대표적인 타입의 메모리 크기는 다음과 같습니다.</p>
<table>
<thead>
<tr>
<th>타입</th>
<th>비트</th>
<th>바이트</th>
</tr>
</thead>
<tbody><tr>
<td>int32</td>
<td>32 bits</td>
<td>4 bytes</td>
</tr>
<tr>
<td>int64</td>
<td>64 bits</td>
<td>8 bytes</td>
</tr>
<tr>
<td>float32</td>
<td>32 bits</td>
<td>4 bytes</td>
</tr>
<tr>
<td>float64</td>
<td>64 bits</td>
<td>8 bytes</td>
</tr>
</tbody></table>
<p>즉, int64와 float64는 각각 8바이트,<br>int32와 float32는 4바이트를 사용합니다.</p>
<p>따라서 동일한 개수의 값이 있을 때,<br>64비트 타입을 32비트 타입으로 변경하면 각 원소가 차지하는 메모리가 정확히 절반이 됩니다.</p>
<hr>
<p>Pandas의 Series와 DataFrame 컬럼은 내부적으로 NumPy 배열(ndarray) 위에 저장됩니다.<br>이는 곧, 컬럼 하나가 <strong>연속된 메모리 블록</strong>으로 구성되며 모든 값이 동일한 dtype을 사용한다는 의미입니다.</p>
<p>이 때문에 컬럼의 메모리 사용량은 다음과 같이 계산됩니다.</p>
<p><strong>전체 메모리 = row 수 × dtype 크기</strong></p>
<p>예를 들어 1,000,000개의 값이 있을 경우:</p>
<ul>
<li>int64: 약 8MB  </li>
<li>int32: 약 4MB  </li>
</ul>
<p>차이는 정확히 2배입니다.</p>
<h3 id="그렇다면-pandas는-왜-기본적으로-int64와-float64를-사용할까요">그렇다면 Pandas는 왜 기본적으로 int64와 float64를 사용할까요?</h3>
<p>첫 번째 이유는 표현 가능한 값의 범위입니다.</p>
<p>int32는 약 ±21억까지만 표현할 수 있지만,<br>int64는 훨씬 큰 값을 다룰 수 있습니다. 실무 데이터에서는 ID, timestamp, 누적 카운트 등이 쉽게 int32 범위를 초과할 수 있기 때문에, Pandas는 안전한 기본값으로 int64를 선택합니다.</p>
<p>두 번째 이유는 float64가 과학 계산의 표준이기 때문입니다.<br>float64는 약 15자리 십진 정밀도를 제공하지만, float32는 약 7자리 수준에 불과합니다. 머신러닝이나 통계 계산에서는 오차 누적을 방지하기 위해 float64가 기본으로 사용됩니다.</p>
<p>실제 환경에서는 이 차이가 더욱 크게 체감됩니다.</p>
<p>수백만 row와 수십 개 이상의 컬럼을 가진 데이터셋에서는,<br>float64 대신 float32를 사용하는 것만으로도 수백 MB에서 수 GB 단위의 메모리를 절약할 수 있습니다.</p>
<p>이 차이는 단순한 저장 공간뿐 아니라:</p>
<ul>
<li>데이터 로딩 속도</li>
<li>CPU 캐시 효율</li>
<li>연산 성능</li>
<li>메모리 부족(OOM) 발생 여부</li>
</ul>
<p>에도 직접적인 영향을 줍니다.</p>
<p>정리하면, int64 → int32, float64 → float32 변환 시 메모리가 절반으로 줄어드는 이유는 각 값이 차지하는 비트 수가 64에서 32로 감소하기 때문입니다.</p>
<p>Pandas는 고정 폭 dtype을 사용하는 연속 메모리 구조를 가지므로, 이러한 차이가 전체 메모리 사용량에 그대로 반영됩니다.</p>
<p>데이터 타입 최적화는 단순한 미세 조정이 아니라, 대용량 데이터 처리에서 가장 먼저 고려해야 할 구조적 최적화입니다.</p>
<hr>
<h3 id="12-다운캐스팅-downcasting">1.2 다운캐스팅 (Downcasting)</h3>
<p>앞서 수동으로 <code>astype()</code>을 사용해 데이터 타입을 변경할 수도 있지만,<br>Pandas는 보다 안전하게 타입을 축소할 수 있도록 <code>pd.to_numeric()</code>의 <code>downcast</code> 옵션을 제공합니다.</p>
<p>다운캐스팅은 <strong>현재 값들을 표현할 수 있는 가장 작은 숫자 타입으로 자동 변환하는 과정</strong>입니다.</p>
<pre><code class="language-python">df[&#39;value&#39;] = pd.to_numeric(df[&#39;value&#39;], downcast=&#39;integer&#39;)
df[&#39;price&#39;] = pd.to_numeric(df[&#39;price&#39;], downcast=&#39;float&#39;)</code></pre>
<h4 id="downcast는-무엇을-하는가">downcast는 무엇을 하는가?</h4>
<p><code>downcast</code> 옵션은 단순히 int64를 무조건 int32로 바꾸는 기능이 아닙니다.</p>
<p>Pandas는 다음과 같은 순서로 동작합니다.</p>
<ol>
<li>컬럼 전체 값을 스캔</li>
<li>최소값과 최대값 계산</li>
<li>해당 범위를 표현할 수 있는 가장 작은 dtype 선택</li>
<li>안전할 경우에만 변환 수행</li>
</ol>
<p>예를 들어 <code>downcast=&#39;integer&#39;</code>를 사용하면 내부적으로 다음 후보들을 검사합니다.</p>
<ul>
<li>int8</li>
<li>int16</li>
<li>int32</li>
<li>int64</li>
</ul>
<p>그리고 실제 값의 범위가 int8에 들어가면 int8,
int16에 들어가면 int16,
그보다 크면 int32 … 이런 식으로 자동 선택합니다.</p>
<p>float 역시 동일하게:</p>
<ul>
<li>float16</li>
<li>float32</li>
<li>float64</li>
</ul>
<p>중에서 가능한 가장 작은 타입을 선택합니다.</p>
<p>즉, downcast는 <strong>“값을 잃지 않는 범위 내에서 최소 메모리 타입을 찾는 과정”</strong>입니다.</p>
<h4 id="astype과-downcast의-차이">astype과 downcast의 차이</h4>
<p><code>astype()</code>은 개발자가 dtype을 직접 지정합니다.</p>
<pre><code class="language-python">df[&#39;id&#39;] = df[&#39;id&#39;].astype(&#39;int32&#39;)</code></pre>
<p>이 방식은 빠르고 단순하지만,
값 범위를 벗어나면 overflow가 발생하거나 에러가 날 수 있습니다.</p>
<p>반면 <code>downcast</code>는 자동 검사 후 변환하기 때문에 상대적으로 안전합니다.</p>
<p>즉:</p>
<ul>
<li>astype → 수동, 빠름, 위험 가능</li>
<li>downcast → 자동, 안전, 약간 느림</li>
</ul>
<p>대량 데이터에서는 downcast가 실수 가능성을 크게 줄여줍니다.</p>
<h4 id="왜-downcast가-실무에서-중요한가">왜 downcast가 실무에서 중요한가?</h4>
<p>실제 데이터에서는 다음과 같은 경우가 많습니다.</p>
<ul>
<li>0~100 범위의 카운트 값</li>
<li>작은 정수 범위의 코드 값</li>
<li>소수점 두 자리 가격 정보</li>
</ul>
<p>이런 컬럼들이 기본적으로 int64 / float64로 로딩되면 필요 이상의 메모리를 차지합니다.</p>
<p>downcast를 적용하면:</p>
<ul>
<li>int64 → int8 / int16</li>
<li>float64 → float32</li>
</ul>
<p>까지 자동으로 축소되는 경우가 많아,
수십 퍼센트 이상의 메모리 절감 효과를 얻을 수 있습니다.</p>
<h4 id="주의할-점">주의할 점</h4>
<p>float downcast는 정밀도 손실 가능성이 있습니다.</p>
<p>float32는 약 7자리 십진 정밀도만 유지되므로,
아주 작은 차이가 중요한 금융 계산이나 통계 분석에서는 주의가 필요합니다.</p>
<p>또한 <code>pd.to_numeric()</code>은 문자열 컬럼도 숫자로 변환하려 시도하므로,
혼합 타입 컬럼에서는 의도치 않은 NaN이 생길 수 있습니다.</p>
<p>정리하면, downcasting은 컬럼의 실제 값 범위를 기반으로 가장 작은 dtype을 자동 선택하는 메커니즘이며,
대용량 데이터에서 메모리 최적화를 위한 매우 효과적인 첫 단계입니다.</p>
<p>특히 여러 컬럼을 동시에 처리해야 할 경우, 수동 astype보다 downcast 방식이 훨씬 안정적입니다.</p>
<hr>
<h3 id="13-범주형category-데이터-활용">1.3 범주형(Category) 데이터 활용</h3>
<p>pandas에서 문자열 컬럼은 기본적으로 <code>object</code> 타입으로 저장됩니다. 이 경우 각 행마다 개별 Python 문자열 객체가 생성되며, 동일한 문자열이라도 반복해서 메모리에 저장됩니다. 이 구조는 대량 데이터에서 불필요한 메모리 사용을 유발합니다.</p>
<p><code>category</code> 타입은 이러한 문자열 컬럼을 <strong>고유값 테이블(categories)</strong> 과 <strong>정수 인덱스 배열(codes)</strong> 로 분리하여 저장합니다. 고유 문자열은 한 번만 저장되고, 실제 데이터는 작은 정수로 참조됩니다.</p>
<p>즉 내부 구조는 다음과 같습니다.</p>
<ul>
<li>categories: 컬럼의 고유 문자열 목록</li>
<li>codes: 각 행이 참조하는 정수 배열</li>
</ul>
<p>이 방식은 반복되는 문자열을 직접 저장하지 않기 때문에, 고유값 수가 적은 컬럼에서 메모리 절감 효과가 큽니다.</p>
<pre><code class="language-python"># 범주형 변환 전후 메모리 비교
print(df[&#39;status&#39;].memory_usage(deep=True))  # 변환 전
df[&#39;status&#39;] = df[&#39;status&#39;].astype(&#39;category&#39;)
print(df[&#39;status&#39;].memory_usage(deep=True))  # 변환 후</code></pre>
<p>카디널리티가 낮을수록(categories의 크기가 작을수록) 효과가 크며, 고유값이 많은 컬럼에서는 오히려 이점이 줄어들 수 있습니다. 따라서 상태값, 타입 코드, 플래그와 같은 반복 패턴이 있는 컬럼에 주로 적용하는 것이 적절합니다.</p>
<h4 id="범주형-변환-시-내부-구조-변화">범주형 변환 시 내부 구조 변화</h4>
<p>문자열 컬럼은 기본적으로 <code>object</code> 타입으로 저장됩니다. 이 경우 각 행마다 독립적인 Python 문자열 객체가 생성되며, 동일한 문자열이라도 반복해서 메모리에 저장됩니다.</p>
<p>예를 들어 다음과 같은 컬럼이 있을 때:</p>
<pre><code>status
------
OK
FAIL
OK
OK
FAIL</code></pre><p>pandas 내부에서는 다음과 같이 처리됩니다.</p>
<pre><code class="language-text">[&quot;OK&quot;, &quot;FAIL&quot;, &quot;OK&quot;, &quot;OK&quot;, &quot;FAIL&quot;]</code></pre>
<p>각 원소는 다음 구성 요소를 가집니다.</p>
<ul>
<li>Python object pointer (약 8 bytes)</li>
<li>Python string 객체</li>
<li>문자열 길이만큼의 문자 버퍼</li>
</ul>
<p>즉 전체 메모리는 대략 다음 형태가 됩니다.</p>
<pre><code>행 수 N × (포인터 + 문자열 객체 + 실제 문자 데이터)</code></pre><p>같은 <code>&quot;OK&quot;</code> 값이라도:</p>
<ul>
<li>매 행마다 새로운 문자열 객체가 생성되고</li>
<li>매번 별도의 메모리가 할당됩니다.</li>
</ul>
<p>이 구조 때문에 데이터가 커질수록 메모리 사용량이 빠르게 증가하며, <code>memory_usage(deep=True)</code> 값도 크게 나타납니다.</p>
<h4 id="category-dtype-적용-후-구조">category dtype 적용 후 구조</h4>
<pre><code class="language-python">df[&#39;status&#39;] = df[&#39;status&#39;].astype(&#39;category&#39;)</code></pre>
<p>이렇게 변환하면 pandas는 컬럼을 두 부분으로 분리합니다.</p>
<h4 id="1-categories-고유값-테이블">(1) Categories (고유값 테이블)</h4>
<pre><code class="language-text">Index([&#39;FAIL&#39;, &#39;OK&#39;])</code></pre>
<p>컬럼의 고유 문자열만 별도로 저장하며, 각 문자열은 단 한 번만 메모리에 올라갑니다.</p>
<h4 id="2-codes-정수-인덱스-배열">(2) Codes (정수 인덱스 배열)</h4>
<p>원본 데이터:</p>
<pre><code>OK, FAIL, OK, OK, FAIL</code></pre><p>은 다음과 같이 정수로 변환됩니다.</p>
<pre><code>[1, 0, 1, 1, 0]</code></pre><p>이 배열은 numpy 기반의 정수 배열이며,</p>
<ul>
<li>int8 / int16 / int32 중 자동 선택</li>
<li>연속 메모리 구조</li>
<li>Python object 없음</li>
</ul>
<p>이라는 특징을 가집니다.</p>
<h4 id="최종-메모리-구조">최종 메모리 구조</h4>
<p>category 컬럼은 내부적으로 다음과 같이 구성됩니다.</p>
<ul>
<li><p>categories
→ <code>[&#39;FAIL&#39;, &#39;OK&#39;]</code> (고유 문자열만 저장)</p>
</li>
<li><p>codes
→ <code>[1, 0, 1, 1, 0]</code> (순수 정수 배열)</p>
</li>
</ul>
<p>정리하면:</p>
<pre><code>변환 전:
N × string 객체

변환 후:
K × string  +  N × 작은 정수</code></pre><p>여기서:</p>
<ul>
<li>N: 전체 행 수</li>
<li>K: 고유값 개수 (cardinality)</li>
</ul>
<p>입니다.</p>
<h4 id="카디널리티가-낮을수록-효과가-큰-이유">카디널리티가 낮을수록 효과가 큰 이유</h4>
<p>예를 들어 1,000,000 rows가 있을 때,</p>
<p>고유값이 3개라면:</p>
<ul>
<li>categories: 문자열 3개</li>
<li>codes: 정수 1,000,000개</li>
</ul>
<p>으로 매우 큰 메모리 절감 효과를 얻습니다.</p>
<p>반면 고유값이 900,000개라면:</p>
<ul>
<li>categories: 문자열 900,000개</li>
<li>codes: 정수 1,000,000개</li>
</ul>
<p>가 되어 절감 효과가 거의 없으며, 오히려 성능이 나빠질 수도 있습니다.</p>
<p>따라서 category 타입은 상태값, 타입 코드, 플래그처럼 <strong>반복 패턴이 뚜렷한 low-cardinality 컬럼에 사용하는 것이 일반적입니다.</strong></p>
<h3 id="memory_usagedeeptrue가-줄어드는-이유">memory_usage(deep=True)가 줄어드는 이유</h3>
<p><code>deep=True</code> 옵션은 object 내부 문자열 메모리까지 모두 포함해 계산합니다.</p>
<p>category로 변환하면:</p>
<ul>
<li>행 단위 문자열 객체가 제거되고</li>
<li>대신 numpy 정수 배열이 사용됩니다.</li>
</ul>
<p>이 때문에 메모리 사용량이 눈에 띄게 감소하게 됩니다.</p>
<hr>
<h3 id="14-청크chunk-단위-처리">1.4 청크(Chunk) 단위 처리</h3>
<p>대용량 CSV 파일을 <code>pd.read_csv()</code>로 한 번에 로드하면, 파일 전체가 메모리에 올라가게 됩니다. 이때 DataFrame은 단순히 파일 크기만큼의 메모리를 사용하는 것이 아니라, 파싱 과정에서 생성되는 Python 객체와 내부 버퍼까지 포함하여 더 많은 메모리를 요구합니다.</p>
<p>즉 다음과 같은 호출은:</p>
<pre><code class="language-python">df = pd.read_csv(&quot;large_file.csv&quot;)</code></pre>
<p>내부적으로:</p>
<ul>
<li>전체 파일을 메모리로 읽고</li>
<li>각 컬럼을 파싱하여 numpy 배열 또는 object 배열로 변환하며</li>
<li>DataFrame 구조를 구성하기 위한 추가 메모리를 할당합니다.</li>
</ul>
<p>파일 크기가 수 GB 이상이거나, 시스템 메모리가 제한적인 환경에서는 이 과정에서 쉽게 메모리 부족(OOM)이 발생할 수 있습니다.</p>
<p><code>chunksize</code> 옵션을 사용하면 pandas는 파일을 한 번에 읽지 않고, 지정한 행 수 단위로 나누어 순차적으로 로드합니다.</p>
<pre><code class="language-python">chunk_size = 10000
for chunk in pd.read_csv(&#39;large_file.csv&#39;, chunksize=chunk_size):
    process_chunk(chunk)</code></pre>
<p>이 방식에서는:</p>
<ul>
<li>전체 파일을 메모리에 올리지 않고</li>
<li>일정 크기의 DataFrame만 반복적으로 생성하며</li>
<li>이전 청크는 처리 후 garbage collection 대상이 됩니다.</li>
</ul>
<p>결과적으로 동시에 메모리에 존재하는 데이터의 크기가 <code>chunk_size</code>에 의해 제한됩니다.</p>
<p>내부적으로는 다음과 같은 흐름으로 동작합니다.</p>
<ol>
<li>CSV 파일 스트림을 열고</li>
<li>지정된 행 수만큼 읽어서 DataFrame으로 변환</li>
<li>해당 청크를 iterator 형태로 반환</li>
<li>다음 블록을 다시 읽는 과정을 반복</li>
</ol>
<p>즉:</p>
<pre><code class="language-text">파일 → 작은 DataFrame → 처리 → 버림 → 다음 DataFrame</code></pre>
<p>이라는 패턴으로 동작합니다.</p>
<p>이 방식의 핵심 장점은 메모리 사용량이 데이터 전체 크기가 아니라, 청크 크기에 의해 결정된다는 점입니다.</p>
<p>예를 들어:</p>
<ul>
<li>전체 파일: 10GB</li>
<li>chunksize: 10,000 rows</li>
</ul>
<p>라면, 실제 메모리에는 항상 10,000행 정도만 존재하게 됩니다.</p>
<p>따라서 로그 데이터, 센서 데이터, 대규모 이벤트 로그처럼 파일 크기가 메모리를 초과하는 경우에도 안정적으로 처리가 가능합니다.</p>
<p>청크 처리는 특히 다음과 같은 상황에서 유용합니다.</p>
<ul>
<li>전체 데이터를 한 번에 올릴 필요가 없는 집계 작업</li>
<li>feature engineering을 단계적으로 수행하는 경우</li>
<li>모델 학습 전 전처리를 스트리밍 방식으로 처리할 때</li>
<li>메모리 제한이 있는 서버나 컨테이너 환경</li>
</ul>
<p>정리하면, <code>chunksize</code>는 pandas가 제공하는 스트리밍 처리 방식이며, 대용량 데이터를 “로드”하는 대신 “순차 소비”하도록 구조를 바꿔주는 기능입니다. 메모리 제약 환경에서는 사실상 필수적인 접근 방식입니다.</p>
<hr>
<h3 id="15-불필요한-복사-최소화">1.5 불필요한 복사 최소화</h3>
<p>pandas의 많은 연산은 기본적으로 <strong>새로운 DataFrame 또는 Series 객체를 생성</strong>합니다. 이는 함수형 스타일을 유지하기 위한 설계이지만, 대용량 데이터에서는 불필요한 메모리 사용으로 이어질 수 있습니다.</p>
<p>예를 들어 다음과 같은 코드에서:</p>
<pre><code class="language-python">subset = df[df[&#39;value&#39;] &gt; 100]</code></pre>
<p>pandas는 조건에 맞는 행을 골라 <strong>완전히 새로운 DataFrame을 생성</strong>합니다. 이때 선택된 데이터는 원본과 메모리를 공유하지 않으며, 실제 값이 모두 복사됩니다.</p>
<p>즉 내부적으로는:</p>
<pre><code class="language-text">원본 DataFrame → 조건 필터 → 새 DataFrame 생성 → 데이터 복사</code></pre>
<p>라는 흐름을 따릅니다.</p>
<p>행 수가 많을수록 이 복사 비용은 빠르게 증가합니다.</p>
<p>마찬가지로 다음과 같은 연산들도 대부분 새로운 객체를 반환합니다.</p>
<ul>
<li><code>groupby()</code></li>
<li><code>merge()</code></li>
<li><code>concat()</code></li>
<li><code>assign()</code></li>
<li><code>drop()</code> (기본값)</li>
</ul>
<p>이들은 모두 원본을 수정하지 않고 결과를 새로 만들어 돌려줍니다.</p>
<h4 id="inplace-옵션">inplace 옵션</h4>
<p>일부 메서드는 <code>inplace=True</code> 옵션을 제공합니다.</p>
<pre><code class="language-python">df.drop(columns=[&#39;unnecessary_col&#39;], inplace=True)</code></pre>
<p>이 경우 pandas는 기존 DataFrame 객체를 직접 수정하며, 별도의 결과 객체를 생성하지 않습니다. 따라서 불필요한 DataFrame 복사를 줄일 수 있습니다.</p>
<p>다만 내부 구현상 항상 완전한 zero-copy는 아니며, 컬럼 구조 변경이나 블록 재정렬이 필요한 경우에는 일부 메모리 재할당이 발생할 수 있습니다. 그럼에도 불구하고, 결과 객체를 하나 더 만드는 것보다는 메모리 사용이 훨씬 적습니다.</p>
<h4 id="view와-copy의-차이">View와 Copy의 차이</h4>
<p>numpy와 pandas는 개념적으로 <strong>view(참조)</strong> 와 <strong>copy(복사)</strong> 를 구분합니다.</p>
<ul>
<li>view: 원본 메모리를 그대로 참조</li>
<li>copy: 새로운 메모리를 할당</li>
</ul>
<p>열 단위 접근은 종종 view를 반환합니다.</p>
<pre><code class="language-python">col = df[&#39;value&#39;]</code></pre>
<p>이 경우 대부분 원본 데이터 블록을 공유합니다.</p>
<p>반면 조건 필터링은 항상 copy입니다.</p>
<pre><code class="language-python">subset = df.loc[df[&#39;value&#39;] &gt; 100]</code></pre>
<p>여기서는 boolean mask가 적용되며, 결과 DataFrame은 새 메모리를 할당합니다.</p>
<p>즉 “필요할 때만 복사한다”는 의미는, 가능하면 컬럼 단위 참조를 활용하고, 대규모 행 필터링을 반복적으로 수행하지 않는 구조를 만드는 것을 의미합니다.</p>
<h4 id="실무-관점에서의-정리">실무 관점에서의 정리</h4>
<p>대용량 DataFrame을 다룰 때는 다음 원칙이 도움이 됩니다.</p>
<ul>
<li>불필요한 중간 DataFrame 생성을 피한다</li>
<li>가능한 경우 inplace 연산을 사용한다</li>
<li>컬럼 단위 접근을 우선한다</li>
<li>반복적인 조건 슬라이싱을 최소화한다</li>
<li>파이프라인 중간 결과를 무분별하게 변수로 저장하지 않는다</li>
</ul>
<p>정리하면, pandas의 대부분 연산은 안전성을 위해 “복사 기반”으로 동작합니다. 작은 데이터에서는 문제가 되지 않지만, 대용량 환경에서는 이 복사 비용이 곧 메모리 병목이 됩니다. 따라서 연산 흐름을 설계할 때 객체 생성 횟수 자체를 줄이는 것이 중요합니다.</p>
<hr>
<h3 id="16-python-객체의-메모리-오버헤드-이해">1.6 Python 객체의 메모리 오버헤드 이해</h3>
<p>pandas 메모리 사용의 근본 원인은 Python 객체 자체의 구조에 있습니다. CPython에서 모든 객체는 단순한 값이 아니라, 메타데이터를 포함한 복합 구조로 관리됩니다.</p>
<p>예를 들어 하나의 Python 객체는 최소한 다음 정보를 포함합니다.</p>
<ul>
<li>참조 카운트 (reference count)</li>
<li>타입 포인터 (type pointer)</li>
<li>객체 헤더</li>
</ul>
<p>이 때문에 “값 하나”를 저장하더라도 상당한 고정 비용이 발생합니다.</p>
<p>실제로 일반적인 64bit CPython 환경에서의 대략적인 크기는 다음과 같습니다.</p>
<ul>
<li>빈 문자열: 약 41 bytes</li>
<li>정수(int): 약 28 bytes</li>
<li>빈 리스트: 약 56 bytes</li>
<li>빈 딕셔너리: 약 64 bytes</li>
</ul>
<p>여기에 실제 데이터가 추가되면 크기는 더 증가합니다.</p>
<p>즉 Python에서 다음과 같은 코드가 있다고 할 때:</p>
<pre><code class="language-python">values = [1, 2, 3, 4]</code></pre>
<p>메모리에는 단순히 <code>4 × int</code> 만 존재하는 것이 아니라,</p>
<ul>
<li>리스트 객체</li>
<li>포인터 배열</li>
<li>각 정수 객체</li>
</ul>
<p>가 모두 별도로 생성됩니다.</p>
<p>구조적으로는 다음과 같습니다.</p>
<pre><code class="language-text">list object
 ├─ pointer → int object
 ├─ pointer → int object
 ├─ pointer → int object
 └─ pointer → int object</code></pre>
<p>즉:</p>
<ul>
<li>리스트는 포인터 배열을 들고 있고</li>
<li>각 숫자는 독립적인 Python 객체입니다.</li>
</ul>
<p>이 방식은 유연성과 안전성 측면에서는 장점이 있지만, 대량 데이터 처리에서는 매우 비효율적입니다.</p>
<h4 id="numpy-배열이-메모리를-적게-사용하는-이유">NumPy 배열이 메모리를 적게 사용하는 이유</h4>
<p>반면 NumPy 배열은 Python 객체가 아니라, 연속된 C 메모리 블록 위에 순수 값만 저장합니다.</p>
<p>예를 들어 <code>int64</code> 배열의 경우:</p>
<ul>
<li>각 원소는 정확히 8 bytes</li>
<li>추가 객체 헤더 없음</li>
<li>포인터 없음</li>
<li>연속 메모리 구조</li>
</ul>
<p>입니다.</p>
<p>즉 다음과 같은 차이가 발생합니다.</p>
<pre><code class="language-text">Python list: N × (포인터 + Python int 객체)
NumPy array: N × 8 bytes</code></pre>
<p>이 차이가 수백만 단위 데이터에서는 수십~수백 MB 이상의 격차로 이어집니다.</p>
<p>pandas에서 object 컬럼보다 numeric 컬럼이 훨씬 메모리 효율적인 이유도 동일한 원리입니다.</p>
<h4 id="slots를-통한-객체-경량화"><strong>slots</strong>를 통한 객체 경량화</h4>
<p>클래스 기반 구조에서도 기본 Python 객체는 <code>__dict__</code> 를 사용하여 속성을 저장합니다.</p>
<pre><code class="language-python">class A:
    pass</code></pre>
<p>이 경우 각 인스턴스는 내부에 딕셔너리를 하나씩 가지게 됩니다.</p>
<p><code>__slots__</code> 를 사용하면 이 딕셔너리를 제거하고, 고정 슬롯 기반 구조로 바꿀 수 있습니다.</p>
<pre><code class="language-python">class A:
    __slots__ = (&quot;x&quot;, &quot;y&quot;)</code></pre>
<p>이 방식은:</p>
<ul>
<li>객체당 메모리 사용량 감소</li>
<li>속성 접근 속도 개선</li>
</ul>
<p>효과가 있으며, 대량 객체를 생성하는 경우 유의미한 차이를 만듭니다.</p>
<p>pandas에서 메모리 문제가 발생하는 이유는 단순히 DataFrame이 커서가 아니라, 내부에 수많은 Python 객체가 생성되기 때문입니다.</p>
<p>따라서 메모리 최적화의 핵심은 다음과 같습니다.</p>
<ul>
<li>object dtype을 피하고 numeric / category 타입을 사용한다</li>
<li>가능하면 NumPy 기반 구조를 활용한다</li>
<li>불필요한 DataFrame 복사를 줄인다</li>
<li>대량 객체 생성이 필요한 경우 <code>__slots__</code> 등을 고려한다</li>
</ul>
<p>결국 pandas 메모리 사용량은 “데이터 크기”보다 “Python 객체 개수”에 더 크게 좌우됩니다.</p>
<hr>
<h2 id="2-속도-최적화-기법">2. 속도 최적화 기법</h2>
<p>처리 속도 최적화는 데이터 사이언스 워크플로우의 효율성을 결정합니다. Python은 편의성이 뛰어나지만 실행 속도가 느리다는 단점이 있으며, 특히 반복문 사용 시 성능 저하가 심각합니다. </p>
<h3 id="21-벡터화vectorization-연산-활용">2.1 벡터화(Vectorization) 연산 활용</h3>
<p>Python에서 가장 큰 성능 병목은 반복문 자체가 아니라, <strong>Python 인터프리터가 개입하는 횟수</strong>입니다. <code>for</code> 루프를 사용한 순회 방식은 매 반복마다 Python 레벨의 연산과 타입 확인이 발생하며, 이 비용이 누적되면 전체 성능을 크게 저하시킵니다.</p>
<p>예를 들어 다음 코드를 보면:</p>
<pre><code class="language-python">result = []
for i in range(len(df)):
    result.append(df.iloc[i][&#39;value&#39;] * 2)</code></pre>
<p>이 한 줄의 연산 안에는 다음 과정이 반복됩니다.</p>
<ul>
<li>Python <code>for</code> 루프 제어</li>
<li><code>df.iloc[i]</code> 인덱싱</li>
<li>Series 생성</li>
<li><code>&#39;value&#39;</code> 키 접근</li>
<li>Python 정수 연산</li>
<li>리스트 append</li>
</ul>
<p>즉 단순한 곱셈 연산 하나를 위해, 여러 단계의 Python 객체 접근과 함수 호출이 매번 발생합니다.</p>
<p>반면 벡터화 연산은 완전히 다른 실행 경로를 가집니다.</p>
<pre><code class="language-python">result = df[&#39;value&#39;].values * 2</code></pre>
<p>이 코드에서 핵심은 <code>.values</code> 를 통해 <strong>NumPy ndarray</strong> 를 사용하는 것입니다.</p>
<p>NumPy 배열은 다음과 같은 특징을 가집니다.</p>
<ul>
<li>동일한 dtype의 값만 저장</li>
<li>연속된 메모리 구조</li>
<li>Python 객체 없음</li>
</ul>
<p>이 상태에서 수행되는 <code>* 2</code> 연산은 Python 반복문이 아니라, <strong>C로 구현된 내부 루프</strong>에서 실행됩니다.</p>
<p>즉 실행 흐름은 다음과 같습니다.</p>
<pre><code class="language-text">Python 호출 1회 → C 레벨 루프 → 전체 배열 연산</code></pre>
<p>반복문이 Python 레벨에서 실행되지 않기 때문에, 인터프리터 오버헤드가 거의 발생하지 않습니다.</p>
<h4 id="python-for-loop와-벡터화의-본질적인-차이">Python for loop와 벡터화의 본질적인 차이</h4>
<p>두 방식의 차이는 연산 횟수가 아니라, <strong>어디서 반복이 실행되느냐</strong>에 있습니다.</p>
<pre><code class="language-text">for loop:
Python → Python → Python → Python (N번)

Vectorization:
Python → C → C → C (N번)</code></pre>
<p>Python은 동적 타입 언어이기 때문에, 각 연산마다 타입 확인과 참조 관리가 필요합니다. 반면 C 레벨에서는 타입이 고정되어 있으며, 연속 메모리 접근이 가능해 CPU 캐시 효율도 높습니다.</p>
<p>이 때문에 벡터화 연산은 일반적으로 Python 반복문 대비 수십 배 이상의 성능 차이를 보입니다.</p>
<h4 id="pandas에서도-동일하게-적용되는-이유">pandas에서도 동일하게 적용되는 이유</h4>
<p>pandas의 많은 연산은 내부적으로 NumPy를 기반으로 구현되어 있습니다. 따라서 다음과 같은 연산들은 대부분 벡터화되어 처리됩니다.</p>
<ul>
<li>산술 연산 (<code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>)</li>
<li>비교 연산</li>
<li>boolean mask</li>
<li>통계 연산 (<code>mean</code>, <code>sum</code>, <code>std</code> 등)</li>
</ul>
<p>가능한 경우 pandas Series나 DataFrame 전체에 대해 연산을 적용하는 것이, 행 단위 접근보다 훨씬 효율적입니다.</p>
<p>벡터화 연산이 빠른 이유는 “연산을 한 번에 처리해서”가 아니라, <strong>반복을 Python이 아닌 C 레벨로 내려보내기 때문</strong>입니다.</p>
<p>따라서 성능이 중요한 코드에서는:</p>
<ul>
<li>Python for loop 기반 순회를 피하고</li>
<li>NumPy ndarray 또는 pandas의 벡터화 연산을 사용하며</li>
<li>행 단위 처리보다는 컬럼 단위 연산을 우선하는 것이</li>
</ul>
<p>기본적인 최적화 전략이 됩니다.</p>
<h3 id="22-pandas-내장-함수-우선-사용">2.2 Pandas 내장 함수 우선 사용</h3>
<p>pandas에서 제공하는 집계 함수(<code>sum</code>, <code>mean</code>, <code>min</code>, <code>max</code> 등)는 단순한 편의 기능이 아니라, NumPy 기반의 저수준(low-level) 루틴 위에 구현된 고성능 연산입니다.</p>
<p>예를 들어 다음 두 코드는 기능적으로 동일합니다.</p>
<pre><code class="language-python"># Python 반복문
total = 0
for value in df[&#39;column&#39;]:
    total += value</code></pre>
<pre><code class="language-python"># Pandas 내장 함수
total = df[&#39;column&#39;].sum()</code></pre>
<p>하지만 내부 실행 방식은 완전히 다릅니다.</p>
<h4 id="python-반복문의-내부-동작">Python 반복문의 내부 동작</h4>
<p>첫 번째 방식에서는 각 반복마다 다음 작업이 발생합니다.</p>
<ul>
<li>Python iterator 제어</li>
<li>pandas Series에서 값 추출</li>
<li>Python 객체로 변환</li>
<li>Python 정수 연산</li>
<li>결과 저장</li>
</ul>
<p>즉 매 원소마다 Python 인터프리터가 개입합니다.</p>
<p>구조적으로는 다음과 같습니다.</p>
<pre><code class="language-text">Python → Python → Python → Python (N번 반복)</code></pre>
<p>데이터가 커질수록 이 인터프리터 오버헤드가 누적되어 성능이 급격히 저하됩니다.</p>
<h4 id="pandas-내장-함수의-실행-경로">Pandas 내장 함수의 실행 경로</h4>
<p>반면 <code>df[&#39;column&#39;].sum()</code> 은 내부적으로 NumPy 배열을 직접 참조하여 C 레벨 루프에서 연산을 수행합니다.</p>
<p>실행 흐름은 다음과 같습니다.</p>
<ol>
<li>Series 내부의 ndarray 추출</li>
<li>NumPy reduce 함수 호출</li>
<li>C 코드에서 연속 메모리 순회</li>
<li>결과 반환</li>
</ol>
<p>즉:</p>
<pre><code class="language-text">Python 호출 1회 → C 레벨 루프 → 결과 반환</code></pre>
<p>Python 인터프리터는 시작과 끝에만 관여하고, 실제 반복은 C에서 처리됩니다.</p>
<h4 id="추가적인-최적화-요소">추가적인 최적화 요소</h4>
<p>pandas/NumPy 내장 함수는 단순 반복 외에도 다음과 같은 최적화를 포함합니다.</p>
<ul>
<li>연속 메모리 접근으로 CPU cache 효율 극대화</li>
<li>SIMD(Vectorized instruction) 활용 가능</li>
<li>dtype 기반 분기 제거</li>
<li>불필요한 Python 객체 생성 없음</li>
</ul>
<p>이 때문에 같은 연산이라도 직접 작성한 Python 루프보다 수십 배 이상 빠른 경우가 일반적입니다.</p>
<p>pandas에서 성능을 확보하려면 기본 원칙은 단순합니다.</p>
<ul>
<li>직접 반복문을 작성하지 않는다</li>
<li>내장 집계 함수와 벡터화 연산을 우선 사용한다</li>
<li>row 단위 접근보다 column 단위 연산을 사용한다</li>
</ul>
<p>정리하면, pandas 내장 함수가 빠른 이유는 “잘 만들어져 있어서”가 아니라, <strong>연산이 Python이 아닌 C 레벨에서 수행되기 때문</strong>입니다. 따라서 동일한 작업이라면 항상 pandas/NumPy가 제공하는 연산을 먼저 고려하는 것이 바람직합니다.</p>
<h3 id="23-apply-대신-벡터화-연산-사용">2.3 apply() 대신 벡터화 연산 사용</h3>
<p><code>apply()</code> 는 pandas에서 제공하는 함수이지만, 내부적으로는 각 원소 또는 각 행에 대해 Python 함수를 반복 호출하는 구조입니다. 즉 벡터화 연산이 아니라, Python 레벨 반복문에 가깝게 동작합니다.</p>
<p>다음 예제를 보면:</p>
<pre><code class="language-python">df[&#39;result&#39;] = df[&#39;value&#39;].apply(lambda x: x * 2 + 1)</code></pre>
<p>겉보기에는 pandas 연산처럼 보이지만, 실제로는 다음 과정이 반복됩니다.</p>
<ul>
<li>Series에서 값 하나 추출</li>
<li>Python 객체로 변환</li>
<li>lambda 함수 호출</li>
<li>Python 산술 연산</li>
<li>결과 저장</li>
</ul>
<p>이를 구조적으로 표현하면:</p>
<pre><code class="language-text">Python → lambda → Python → lambda → Python (N번)</code></pre>
<p>즉 각 원소마다 Python 함수 호출이 발생하며, 인터프리터 오버헤드가 누적됩니다.</p>
<p>반면 벡터화 방식은 완전히 다른 경로를 사용합니다.</p>
<pre><code class="language-python">df[&#39;result&#39;] = df[&#39;value&#39;] * 2 + 1</code></pre>
<p>이 코드는 내부적으로 Series가 보유한 NumPy ndarray를 직접 참조하여 C 레벨 루프에서 연산을 수행합니다.</p>
<p>실행 흐름은 다음과 같습니다.</p>
<pre><code class="language-text">Python 호출 1회 → NumPy C 루프 → 전체 배열 연산</code></pre>
<p>Python은 연산 시작과 종료에만 관여하고, 실제 반복은 모두 C 코드에서 처리됩니다.</p>
<h4 id="apply가-특히-느려지는-이유">apply()가 특히 느려지는 이유</h4>
<p><code>apply()</code> 는 단순 반복 외에도 다음과 같은 추가 비용을 가집니다.</p>
<ul>
<li>각 호출마다 Python 함수 객체 실행</li>
<li>dtype 추론</li>
<li>결과를 다시 Series로 재구성</li>
</ul>
<p>특히 lambda 함수나 사용자 정의 함수(UDF)를 사용할 경우, JIT 컴파일이나 벡터화 최적화가 적용되지 않기 때문에 성능 차이가 더 커집니다.</p>
<p>가능하면 다음 우선순위를 따르는 것이 일반적입니다.</p>
<ol>
<li>pandas / NumPy 내장 벡터화 연산</li>
<li>pandas 집계 함수</li>
<li>NumPy 함수</li>
<li>최후의 수단으로 apply()</li>
</ol>
<p>즉 <code>apply()</code> 는 편의 기능이지, 성능을 위한 도구는 아닙니다.</p>
<p>정리하면, <code>apply()</code> 가 느린 이유는 pandas 함수라서가 아니라, <strong>Python 함수가 원소 단위로 반복 호출되기 때문</strong>입니다. 동일한 연산이 벡터화로 표현 가능하다면, 항상 벡터화 방식을 우선 사용하는 것이 바람직합니다.</p>
<h3 id="24-numpy와-pandas의-메모리-공유-활용">2.4 NumPy와 Pandas의 메모리 공유 활용</h3>
<p>pandas는 독립적인 데이터 구조처럼 보이지만, 실제로는 대부분의 numeric 컬럼이 NumPy ndarray 위에 직접 구성되어 있습니다. 즉 Series와 DataFrame은 내부적으로 NumPy 배열을 감싸는 thin wrapper에 가깝습니다.</p>
<p>따라서 다음과 같은 변환은 기본적으로 데이터를 복사하지 않습니다.</p>
<pre><code class="language-python">array = df[&#39;column&#39;].values</code></pre>
<p>이 경우 <code>array</code> 는 새로운 배열이 아니라, 기존 Series가 사용 중이던 NumPy 메모리를 그대로 참조합니다.</p>
<p>이를 직접 확인하면:</p>
<pre><code class="language-python">import numpy as np
np.shares_memory(df[&#39;column&#39;], df[&#39;column&#39;].values)  # True</code></pre>
<p>True가 반환되며, 이는 pandas와 NumPy가 동일한 메모리 블록을 공유하고 있음을 의미합니다.</p>
<h4 id="내부-구조-관점에서의-설명">내부 구조 관점에서의 설명</h4>
<p>numeric Series는 대략 다음 구조를 가집니다.</p>
<pre><code class="language-text">Series
 └─ NumPy ndarray (실제 데이터)</code></pre>
<p><code>.values</code> 또는 <code>.to_numpy()</code> 를 호출하면, 이 ndarray에 대한 참조만 반환됩니다.</p>
<p>즉:</p>
<pre><code class="language-text">복사 발생 ❌
포인터 전달 ✅</code></pre>
<p>이기 때문에 변환 비용이 거의 없습니다.</p>
<h4 id="numpy-연산을-직접-사용하는-이유">NumPy 연산을 직접 사용하는 이유</h4>
<p>NumPy 함수들은 연속 메모리 위에서 C 레벨 루프를 사용하여 동작합니다.</p>
<pre><code class="language-python">result = np.sqrt(array)</code></pre>
<p>이 연산은:</p>
<ul>
<li>Python 반복문 없음</li>
<li>pandas 오버헤드 없음</li>
<li>dtype 고정</li>
<li>SIMD 최적화 가능</li>
</ul>
<p>이라는 특징을 가지며, pandas Series 메서드보다도 빠른 경우가 많습니다.</p>
<p>특히 복잡한 수치 계산이나 커스텀 연산이 필요한 경우, ndarray로 변환한 뒤 NumPy 연산을 적용하는 방식이 가장 효율적인 경로가 됩니다.</p>
<h4 id="주의할-점-1">주의할 점</h4>
<p>메모리 공유는 numeric dtype에서만 안전하게 적용됩니다.</p>
<p>object dtype 컬럼의 경우 <code>.values</code> 는 object 배열을 반환하며, 이 경우 각 원소는 여전히 Python 객체입니다. 따라서 기대하는 성능 향상이 발생하지 않을 수 있습니다.</p>
<p>또한 <code>.to_numpy(copy=True)</code> 를 명시하면 실제 복사가 발생하므로, 메모리 절약이 목적이라면 기본 옵션을 유지하는 것이 좋습니다.</p>
<p>pandas와 NumPy는 동일한 메모리를 공유하므로, <code>.values</code> 또는 <code>.to_numpy()</code> 를 사용하면 복사 없이 ndarray를 얻을 수 있습니다. 이를 활용하면 pandas 구조를 거치지 않고 NumPy의 고성능 연산을 직접 사용할 수 있으며, 대용량 데이터 처리 시 성능과 메모리 효율을 동시에 개선할 수 있습니다.</p>
<hr>
<h3 id="25-캐싱caching-활용">2.5 캐싱(Caching) 활용</h3>
<p>연산이 느린 이유가 반복 계산 때문이라면, 가장 효과적인 최적화는 동일한 계산을 다시 수행하지 않는 것입니다. <code>functools.lru_cache</code> 는 함수 호출 결과를 메모리에 저장해 두었다가, 동일한 입력이 들어오면 즉시 반환하는 방식의 캐싱 메커니즘을 제공합니다.</p>
<pre><code class="language-python">from functools import lru_cache

@lru_cache(maxsize=128)
def expensive_computation(n):
    # 복잡한 계산
    return result</code></pre>
<p>이후 동일한 인자로 함수를 호출하면:</p>
<pre><code class="language-python">result = expensive_computation(100)  # 첫 호출: 실제 계산 수행
result = expensive_computation(100)  # 두 번째 호출: 캐시에서 즉시 반환</code></pre>
<p>두 번째 호출부터는 함수 내부 코드가 실행되지 않습니다.</p>
<h4 id="lru_cache의-내부-동작">lru_cache의 내부 동작</h4>
<p><code>lru_cache</code> 는 함수 입력을 key로, 반환값을 value로 하는 딕셔너리 기반 캐시를 유지합니다.</p>
<p>구조적으로는 다음과 같습니다.</p>
<pre><code class="language-text">(arguments) → result</code></pre>
<p>이미 계산된 입력이 다시 들어오면:</p>
<ul>
<li>함수 본문 실행 ❌</li>
<li>캐시 lookup ✅</li>
<li>저장된 결과 반환</li>
</ul>
<p>이라는 흐름을 따릅니다.</p>
<p><code>maxsize</code> 는 캐시에 저장할 최대 항목 수를 의미하며, 이를 초과하면 가장 오래 사용되지 않은 항목(LRU: Least Recently Used)부터 제거됩니다.</p>
<h4 id="언제-효과적인가">언제 효과적인가</h4>
<p>캐싱은 다음 조건을 만족할 때 가장 효과적입니다.</p>
<ul>
<li>동일한 입력으로 함수가 반복 호출되는 경우</li>
<li>계산 비용이 lookup 비용보다 훨씬 큰 경우</li>
<li>함수가 순수 함수(pure function)에 가까운 경우
(같은 입력 → 항상 같은 출력)</li>
</ul>
<p>예를 들어:</p>
<ul>
<li>feature engineering 중 반복되는 변환</li>
<li>복잡한 규칙 기반 계산</li>
<li>동일 파라미터로 여러 번 호출되는 통계 함수</li>
</ul>
<p>같은 상황에서 유의미한 성능 개선을 얻을 수 있습니다.</p>
<h4 id="주의할-점-2">주의할 점</h4>
<p>캐싱은 메모리를 사용합니다. 따라서:</p>
<ul>
<li>입력 종류가 매우 많은 경우</li>
<li>결과 객체가 큰 경우</li>
<li>한 번만 호출되는 함수인 경우</li>
</ul>
<p>에는 오히려 메모리 낭비가 될 수 있습니다.</p>
<p>또한 pandas DataFrame이나 NumPy 배열처럼 mutable 객체는 <code>lru_cache</code>의 key로 사용할 수 없으므로, 스칼라 값이나 immutable 타입 위주로 사용하는 것이 일반적입니다.</p>
<p><code>lru_cache</code> 는 계산을 빠르게 만드는 도구라기보다, <strong>계산 자체를 생략하게 만드는 도구</strong>입니다. 반복 호출되는 고비용 함수가 있다면, 벡터화나 C 레벨 최적화보다 캐싱이 더 큰 효과를 주는 경우도 많습니다.</p>
<h2 id="3-데이터-구조별-접근-패턴-최적화">3. 데이터 구조별 접근 패턴 최적화</h2>
<p>Pandas와 NumPy는 서로 다른 메모리 레이아웃을 가지며, 이를 이해하고 활용하면 성능을 크게 향상시킬 수 있습니다.</p>
<h3 id="31-pandas-column-major-접근">3.1 Pandas: Column-major 접근</h3>
<p>Pandas DataFrame은 컬럼 우선(column-major) 방식으로 데이터를 저장합니다. 따라서 컬럼 단위 연산이 행 단위 연산보다 훨씬 빠릅니다.</p>
<pre><code class="language-python"># 빠른 방법: 컬럼 단위 연산
df[&#39;new_col&#39;] = df[&#39;col1&#39;] + df[&#39;col2&#39;]

# 느린 방법: 행 단위 순회
for idx, row in df.iterrows():
    df.at[idx, &#39;new_col&#39;] = row[&#39;col1&#39;] + row[&#39;col2&#39;]</code></pre>
<p>컬럼 단위 연산은 메모리 지역성(locality)이 좋아 캐시 효율이 높습니다.</p>
<h3 id="32-numpy-row-major-접근-c-order">3.2 NumPy: Row-major 접근 (C-order)</h3>
<p>NumPy 배열은 기본적으로 행 우선(row-major, C-order) 방식으로 저장됩니다. 행 단위로 접근할 때 메모리 지역성이 좋습니다.</p>
<pre><code class="language-python">import numpy as np

# C-order (row-major): 행 단위 접근이 빠름
array_c = np.array([[1, 2, 3], [4, 5, 6]], order=&#39;C&#39;)

# F-order (column-major): 컬럼 단위 접근이 빠름
array_f = np.array([[1, 2, 3], [4, 5, 6]], order=&#39;F&#39;)

# 행 단위 연산 (C-order에서 빠름)
for row in array_c:
    process(row)</code></pre>
<h3 id="왜-pandas는-컬럼-단위가-빠르고-numpy는-행-단위가-빠른가">왜 Pandas는 컬럼 단위가 빠르고, NumPy는 행 단위가 빠른가?</h3>
<p>이 차이는 단순히 라이브러리 구현 방식 때문이 아니라,
<strong>메모리에 데이터가 배치되는 방식과 CPU 캐시 동작 원리</strong>에서 비롯됩니다.</p>
<p>핵심은 다음 두 가지입니다.</p>
<ul>
<li>메모리 레이아웃 (Memory Layout)</li>
<li>메모리 지역성 (Memory Locality)</li>
</ul>
<p>이를 Pandas와 NumPy 관점에서 각각 살펴보겠습니다.</p>
<h4 id="pandas-series-기반-column-major-구조">Pandas: Series 기반 Column-major 구조</h4>
<p>Pandas DataFrame은 내부적으로 여러 개의 <code>Series</code>로 구성되어 있습니다.
각 컬럼은 독립적인 NumPy 배열로 존재하며, 메모리 상에서 연속적으로 저장됩니다.</p>
<p>즉 구조적으로 보면 다음과 같습니다.</p>
<pre><code>DataFrame
 ├── Series(col1)
 ├── Series(col2)
 ├── Series(col3)</code></pre><p>따라서 다음과 같은 연산은</p>
<pre><code class="language-python">df[&#39;col1&#39;] + df[&#39;col2&#39;]</code></pre>
<p>실제로는 다음 과정을 거칩니다.</p>
<ul>
<li><code>col1</code> 전체 배열을 한 번에 읽고</li>
<li><code>col2</code> 전체 배열을 한 번에 읽은 뒤</li>
<li>벡터화 연산으로 처리합니다.</li>
</ul>
<p>이 과정에서는</p>
<ul>
<li>연속 메모리 접근이 이루어지고</li>
<li>CPU cache line에 자연스럽게 적재되며</li>
<li>Python loop 없이 C 레벨에서 계산됩니다.</li>
</ul>
<p>결과적으로</p>
<ul>
<li>분기(branch)가 없고</li>
<li>cache miss가 최소화되며</li>
<li>SIMD 벡터 연산이 활용됩니다.</li>
</ul>
<p>매우 이상적인 실행 경로입니다.</p>
<p>반면 다음과 같은 코드에서는</p>
<pre><code class="language-python">for idx, row in df.iterrows():</code></pre>
<p>완전히 다른 접근 패턴이 발생합니다.</p>
<p>이 경우 각 row는 새로운 Series 객체로 생성되며, 컬럼마다 서로 다른 메모리 위치를 참조하게 됩니다. 또한 Python 레벨 loop와 attribute access가 반복됩니다.</p>
<p>즉 다음과 같은 비용이 동시에 발생합니다.</p>
<ul>
<li>객체 생성 비용</li>
<li>포인터 점프 증가</li>
<li>cache locality 붕괴</li>
</ul>
<p>따라서 <code>iterrows</code> 기반 접근은 구조적으로 느릴 수밖에 없습니다.</p>
<p>정리하면 Pandas에서는 다음과 같습니다.</p>
<ul>
<li>컬럼 단위 연산: 연속 메모리 + 벡터화</li>
<li>행 단위 순회: 분산 메모리 + Python loop</li>
</ul>
<p>이 차이가 곧 성능 차이로 이어집니다.</p>
<h4 id="numpy-기본-row-major-c-order-메모리-배치">NumPy: 기본 Row-major (C-order) 메모리 배치</h4>
<p>NumPy 배열은 기본적으로 C-order, 즉 row-major 방식으로 저장됩니다.</p>
<p>예를 들어 다음 배열은</p>
<pre><code class="language-python">[[1,2,3],
 [4,5,6]]</code></pre>
<p>메모리에 다음과 같이 배치됩니다.</p>
<pre><code>1 2 3 4 5 6</code></pre><p>첫 번째 행이 끝까지 연속적으로 저장됩니다.</p>
<p>따라서 다음과 같은 행 단위 접근은</p>
<pre><code class="language-python">for row in array:</code></pre>
<p>내부적으로 연속 메모리를 순차적으로 읽게 됩니다.
이때 CPU prefetch가 제대로 작동하고, cache hit 비율도 높아집니다.</p>
<p>반면 컬럼 단위 접근은 다음과 같은 패턴이 됩니다.</p>
<pre><code>1 → 4 → 2 → 5 → 3 → 6</code></pre><p>이처럼 stride jump가 발생하면</p>
<ul>
<li>cache line 활용도가 떨어지고</li>
<li>TLB miss가 증가하며</li>
<li>prefetch가 실패합니다.</li>
</ul>
<p>이 때문에 NumPy에서 컬럼 접근은 상대적으로 느립니다.</p>
<p>물론 <code>order=&#39;F&#39;</code> 옵션으로 column-major 배열을 만들 수 있지만,
대부분의 NumPy 연산과 C-extension은 C-order 기준으로 최적화되어 있습니다.</p>
<p>중요한 점은 이 모든 차이가 연산 복잡도가 아니라
<strong>메모리를 어떻게 읽느냐</strong>에서 발생한다는 사실입니다.</p>
<p>CPU에서는 계산보다 메모리 접근이 훨씬 느립니다.</p>
<p>따라서 성능의 본질은 다음에 달려 있습니다.</p>
<ul>
<li>얼마나 연속적으로 읽을 수 있는가</li>
<li>cache line을 얼마나 효율적으로 사용하는가</li>
</ul>
<p>이를 정리하면 다음과 같습니다.</p>
<ul>
<li>Pandas는 컬럼이 연속 메모리이므로 컬럼 단위 연산이 빠릅니다.</li>
<li>NumPy는 행이 연속 메모리이므로 행 단위 접근이 빠릅니다.</li>
</ul>
<p>이 구조를 거스르는 순간 성능은 급격히 저하됩니다.</p>
<p>때문에</p>
<ul>
<li>Pandas에서는 항상 컬럼 벡터 연산을 사용합니다.</li>
<li>NumPy에서는 메모리 order에 맞는 접근 방향을 유지합니다.</li>
<li>Python loop는 최후의 수단으로 사용합니다.</li>
</ul>
<p>이는 단순한 코딩 스타일 문제가 아니라,
CPU 아키텍처 레벨에서 결정되는 물리적인 제약에 가깝습니다.</p>
<h3 id="33-데이터-구조-선택-가이드">3.3 데이터 구조 선택 가이드</h3>
<p>NumPy와 Pandas는 각각 다른 강점을 가지므로 작업 특성에 맞게 선택해야 합니다. </p>
<p><strong>NumPy 사용이 적합한 경우:</strong></p>
<ul>
<li>수치 연산 및 시뮬레이션</li>
<li>선형 대수, 푸리에 변환</li>
<li>동일한 타입의 대량 데이터 처리</li>
</ul>
<p><strong>Pandas 사용이 적합한 경우:</strong></p>
<ul>
<li>테이블 형식 데이터 분석</li>
<li>데이터 로딩, 재구성, 피벗, 병합</li>
<li>누락된 데이터 처리</li>
<li>다양한 타입의 컬럼 관리</li>
</ul>
<hr>
<h2 id="4-최적화-전략">4. 최적화 전략</h2>
<p>이론적 지식을 실제 프로젝트에 적용하기 위한 단계별 전략입니다. </p>
<h3 id="41-최적화-우선순위-결정">4.1 최적화 우선순위 결정</h3>
<p><strong>최적화 우선순위:</strong></p>
<ol>
<li>프로파일링으로 병목 지점 식별</li>
<li>가장 많은 시간/메모리를 소비하는 부분부터 최적화</li>
<li>측정 → 최적화 → 재측정 사이클 반복</li>
<li>가독성과 성능의 균형 유지</li>
</ol>
<h3 id="42-단계별-최적화-접근법">4.2 단계별 최적화 접근법</h3>
<p>복잡한 최적화 기법보다는 간단하고 효과적인 방법부터 적용합니다.</p>
<p><strong>1단계: 저비용 최적화</strong></p>
<ul>
<li>데이터 타입 최적화</li>
<li>범주형 데이터 변환</li>
<li>불필요한 컬럼 제거</li>
</ul>
<p><strong>2단계: 알고리즘 개선</strong></p>
<ul>
<li>반복문을 벡터화 연산으로 변경</li>
<li>Pandas 내장 함수 활용</li>
<li>NumPy 연산 활용</li>
</ul>
<p><strong>3단계: 구조적 변경</strong></p>
<ul>
<li>청크 단위 처리 도입</li>
<li>병렬 처리 적용</li>
<li>데이터 파이프라인 재설계</li>
</ul>
<hr>
<h2 id="5-고급-최적화-기법">5. 고급 최적화 기법</h2>
<p>기본 최적화로 충분하지 않을 때 적용할 수 있는 고급 기법들입니다.</p>
<h3 id="51-__slots__-활용">5.1 <code>__slots__</code> 활용</h3>
<p>Python 클래스에서 <code>__slots__</code>를 사용하면 인스턴스의 메모리 사용량을 크게 줄일 수 있습니다. </p>
<pre><code class="language-python"># 일반 클래스 (딕셔너리 기반)
class RegularClass:
    def __init__(self, x, y):
        self.x = x
        self.y = y

# __slots__ 사용 클래스
class SlottedClass:
    __slots__ = [&#39;x&#39;, &#39;y&#39;]
    def __init__(self, x, y):
        self.x = x
        self.y = y

# 메모리 사용량 비교
import sys
regular = RegularClass(1, 2)
slotted = SlottedClass(1, 2)
print(f&quot;Regular: {sys.getsizeof(regular.__dict__)} bytes&quot;)
print(f&quot;Slotted: {sys.getsizeof(slotted)} bytes&quot;)</code></pre>
<h3 id="52-딕셔너리와-집합의-효율적-사용">5.2 딕셔너리와 집합의 효율적 사용</h3>
<p>딕셔너리와 집합은 해시 테이블 기반으로 O(1) 조회 성능을 제공합니다. </p>
<pre><code class="language-python"># 느린 방법: 리스트에서 검색 (O(n))
if item in my_list:  # 선형 탐색
    process(item)

# 빠른 방법: 집합에서 검색 (O(1))
my_set = set(my_list)
if item in my_set:  # 해시 테이블 조회
    process(item)</code></pre>
<p>대량의 조회 작업이 필요한 경우 리스트를 집합이나 딕셔너리로 변환하는 것이 효과적입니다.</p>
<details open><summary>출처</summary>

<p><a href="https://blog.choonzang.com/it/python/2670/">Pandas 고급 기능과 성능 최적화: 대용량 데이터 처리의 비밀</a></p>
<p><a href="https://nnnn.choiiee.com/entry/%ED%8C%90%EB%8B%A4%EC%8A%A4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC-%EA%B3%BC%EC%A0%95%EC%97%90%EC%84%9C-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%82%AC%EC%9A%A9%EC%9C%A8%EC%9D%B4-%EB%86%92%EC%95%84%EC%A7%80%EB%8A%94-%EA%B2%BD%EC%9A%B0%EC%99%80-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95">판다스 데이터 처리 과정에서 메모리 사용율이 높아지는 경우와 해결 방법</a></p>
<p><a href="https://lsjsj92.tistory.com/604">꿈 많은 사람의 이야기</a></p>
<p><a href="https://yahwang.github.io/posts/85">Pandas를 Numpy로! 최적화 시리즈(1) - ndarray 활용 - YA-Hwang 기술 블로그</a></p>
<p><a href="https://jeremyko.github.io/2022/08/22/pandas_speed.html">(python) pandas 처리 속도 개선에 대하여</a></p>
<p><a href="https://docs.kanaries.net/ko/topics/NumPy/numpy-vs-pandas">NumPy 대 Pandas: 일반적인 용어로 차이점 설명</a></p>
<p><a href="https://hyperconnect.github.io/2023/05/30/Python-Performance-Tips.html">고성능 ML 백엔드를 위한 10가지 Python 성능 최적화 팁</a></p>
<p><a href="https://devhwi.tistory.com/44">44</a></p>
<p><a href="https://news.hada.io/topic?id=25516">파이썬 프로그래머가 알아야 할 주요 성능 수치</a></p>
<p><a href="https://github.com/gzupark/optimize_cpu_for_python">AI 개발자를 위한 단계별 Python 최적화 가이드라인 세션의 코드를 실행하여 최적화 가능성을 확인합니다. 딥러닝 모델 학습의 데이터로더 CPU 병목을 줄이고 모델 추론 과정에서 전처리 속도를 개선하는데 도움이 됩니다</a></p>
</details>]]></description>
        </item>
        <item>
            <title><![CDATA[@LRU_CACHE]]></title>
            <link>https://velog.io/@you_n_sunny/LRUCACHE</link>
            <guid>https://velog.io/@you_n_sunny/LRUCACHE</guid>
            <pubDate>Sun, 01 Feb 2026 14:06:11 GMT</pubDate>
            <description><![CDATA[<p><code>functools.lru_cache</code>는 Python에서 한 줄의 데코레이터만으로 반복 계산을 제거할 수 있기 때문에, 성능 개선을 목적으로 자주 선택되는 비교적 쉽게 적용할 수 있는 최적화 수단이다. </p>
<p>하지만 실제 실무에서는 단순히 &quot;빠르게 만든다&quot;는 이유만으로 적용했다가, 메모리 사용량 증가나 예상치 못한 정합성 문제를 겪는 경우도 적지 않다고 한다. 때문에 lru_cache 의 판단 기준에 대해서 학습한 내용을 이 글에 다루고자 한다.</p>
<hr>
<h2 id="1-lru_cache의-역할">1. <code>lru_cache</code>의 역할</h2>
<p><code>lru_cache</code>는 Python 표준 라이브러리 <code>functools</code>에 포함된 데코레이터로, <strong>함수 호출 결과를 메모리에 저장해 두었다가 동일한 입력이 다시 들어오면 계산을 생략하는 구조</strong>를 제공한다.</p>
<p>중요한 점은, 이것이 알고리즘을 바꾸는 최적화가 아니라는 점이다. 계산 자체는 동일하며, 단지 <strong>이미 계산된 결과를 재사용할 수 있을 때만 실행 경로를 단축</strong>한다.</p>
<p>따라서 <code>lru_cache</code>는 다음과 같은 선택을 코드에 명시적으로 추가하는 것과 같다.</p>
<blockquote>
<p>같은 입력이 다시 들어올 가능성이 높고, 그때 계산을 다시 하는 비용보다 메모리에 결과를 보관하는 비용이 더 저렴하다.</p>
</blockquote>
<p>이 전제가 성립하지 않으면, <code>lru_cache</code>는 기대한 효과를 내지 못한다.</p>
<hr>
<h2 id="2-내부-동작을-기준으로-이해하기">2. 내부 동작을 기준으로 이해하기</h2>
<h3 id="21-cache-key는-어떻게-만들어지는가">2.1 Cache key는 어떻게 만들어지는가</h3>
<p><code>lru_cache</code>는 함수가 호출될 때, 전달된 <strong>모든 positional argument와 keyword argument를 묶어 하나의 key</strong>로 사용한다. 이 key는 내부적으로 dictionary의 key로 사용되므로, 다음 조건을 만족해야 한다.</p>
<ul>
<li>모든 인자는 hashable해야 한다</li>
<li>값이 같으면 항상 같은 hash를 가져야 한다</li>
</ul>
<p>이 때문에 list, dict, DataFrame과 같은 mutable 객체는 그대로 인자로 사용할 수 없다.</p>
<p>또한 인자의 값이 조금이라도 다르면, 논리적으로 같은 의미라 하더라도 <strong>완전히 다른 cache entry</strong>로 취급된다.</p>
<hr>
<h3 id="22-cache에-저장되는-것은-무엇인가">2.2 Cache에 저장되는 것은 무엇인가</h3>
<p><code>lru_cache</code>는 함수의 반환값을 <strong>그대로</strong> 저장한다. 복사본을 만들거나, 약한 참조를 사용하는 것이 아니다.</p>
<p>즉, 반환 객체가 크다면:</p>
<ul>
<li>해당 객체는 eviction 될 때까지 메모리에 유지되고</li>
<li>그 객체가 참조하는 모든 내부 구조 역시 메모리를 점유한다</li>
</ul>
<p>DataFrame, 대형 numpy array, 복잡한 dict를 반환하는 함수에 <code>lru_cache</code>를 적용할 경우, 이는 곧 <strong>메모리 상주 객체 수를 늘리는 결정</strong>이 된다.</p>
<hr>
<h3 id="23-lru-이-실제로-의미하는-것">2.3 LRU 이 실제로 의미하는 것</h3>
<p><code>maxsize</code>는 단순히 &quot;최대 몇 개&quot;를 의미하지 않는다. 이는 다음을 의미한다.</p>
<ul>
<li>최근에 사용된 항목은 남는다</li>
<li>한동안 호출되지 않은 항목은 제거된다</li>
</ul>
<p>즉, 호출 패턴이 국소적(locality)을 가질수록 효과가 커지고, 호출 패턴이 넓게 퍼져 있을수록 eviction이 잦아진다.</p>
<p>호출이 다음과 같은 형태라면:</p>
<pre><code>A → B → C → A → B → C</code></pre><p>LRU는 매우 효과적으로 동작한다. 반면,</p>
<pre><code>A → B → C → D → E → F → ...</code></pre><p>와 같은 패턴에서는 cache는 거의 도움이 되지 않는다.</p>
<hr>
<h2 id="3-언제-lru_cache를-써야-하는--가">3. 언제 <code>lru_cache</code>를 써야 하는  가</h2>
<h3 id="31-반복-호출이-구조적으로-보장되는-경우">3.1 반복 호출이 구조적으로 보장되는 경우</h3>
<p>단순히 &quot;여러 번 호출될 수도 있다&quot;는 추측이 아니라, 코드 구조상 반복 호출이 발생할 수밖에 없는 경우가 있다.</p>
<p>예를 들어:</p>
<ul>
<li>동일한 설정 값을 여러 컴포넌트에서 참조</li>
<li>동일한 경로의 데이터를 여러 후처리 단계에서 재사용</li>
<li>파라미터 조합이 제한적인 전처리 함수</li>
</ul>
<p>이러한 경우에는 cache hit 비율이 높게 유지될 가능성이 크다.</p>
<hr>
<h3 id="32-계산-비용이-명확히-큰-경우">3.2 계산 비용이 명확히 큰 경우</h3>
<p><code>lru_cache</code>는 cache miss 시에는 아무런 이득이 없다. 따라서 <strong>miss가 발생했을 때의 비용</strong>이 충분히 커야 한다.</p>
<p>대표적인 예는 다음과 같다.</p>
<ul>
<li>디스크 I/O</li>
<li>네트워크 호출</li>
<li>복잡한 데이터 전처리 파이프라인</li>
</ul>
<p>이 경우 hit 한 번이 가져오는 이득이 매우 크기 때문에, 비교적 적은 hit 비율도 의미를 가질 수 있다.</p>
<hr>
<h3 id="33-결과의-최신성이-절대적이지-않은-경우">3.3 결과의 최신성이 절대적이지 않은 경우</h3>
<p><code>lru_cache</code>는 외부 상태 변화를 감지하지 않는다. 따라서 다음과 같은 전제가 필요하다.</p>
<ul>
<li>한 run 동안 결과가 변하지 않아도 무방하다</li>
<li>약간 오래된 결과가 시스템 전체에 영향을 주지 않는다</li>
</ul>
<p>이 조건이 충족되지 않는다면, cache 무효화 전략이 없는 <code>lru_cache</code>는 적절한 도구가 아니다.</p>
<hr>
<h2 id="4-피해야-할-사용-패턴">4. 피해야 할 사용 패턴</h2>
<h3 id="41-외부-상태에-의존하는-함수">4.1 외부 상태에 의존하는 함수</h3>
<p>파일, DB, 환경 변수 등 외부 상태에 의존하는 함수는 인자만으로 결과가 결정되지 않는다.</p>
<p>이 경우 <code>lru_cache</code>는 실제 상태와 불일치하는 값을 반환할 수 있으며, 이는 디버깅이 매우 어려운 버그로 이어진다.</p>
<hr>
<h3 id="42-반환-객체가-크고-hit-비율이-낮은-경우">4.2 반환 객체가 크고 hit 비율이 낮은 경우</h3>
<p>DataFrame을 반환하는 함수에 cache를 적용할 때 가장 흔히 발생하는 문제다.</p>
<ul>
<li>hit이 거의 없음</li>
<li>eviction 이전까지 메모리에 상주</li>
<li>전체 프로세스 메모리 사용량 증가</li>
</ul>
<p>이 경우 cache는 최적화가 아니라 메모리 사용에 <strong>부담</strong>이 된다.</p>
<hr>
<h3 id="43-인스턴스-메서드에-대한-무비판적-적용">4.3 인스턴스 메서드에 대한 무비판적 적용</h3>
<p>인스턴스 메서드에 <code>lru_cache</code>를 적용하면 <code>self</code>가 key에 포함된다. 이는 다음을 의미한다.</p>
<ul>
<li>인스턴스마다 별도의 cache</li>
<li>인스턴스 수가 많을 경우 cache가 선형적으로 증가</li>
</ul>
<p>이 동작을 명확히 이해하지 않은 상태에서 적용하면, 메모리 사용량을 예측하기 어렵다.</p>
<hr>
<h2 id="5-그럼-현재-내가-하고-있는-실무에서-사용하기-적절한가">5. 그럼 현재 내가 하고 있는 실무에서.. 사용하기 적절한가?</h2>
<ul>
<li>DataLoader는 run당 하나만 생성된다</li>
<li>동일 path가 여러 번 사용될 가능성이 있다</li>
<li>로드 및 가공 비용이 크다</li>
<li>결과의 최신성 요구가 낮다</li>
</ul>
<p>이 조건들은 <code>lru_cache</code>가 기대한 대로 동작하기에 충분한 근거를 제공한다. 이론적으론 맞을 지 몰라도 실제로 cache hit이 발생하는지  확인하지 않으면 원래의 의도와 달리 메모리만 소비할 수 있다.때문에 hit 비율을 확인해야 한다.</p>
<pre><code>hits / (hits + misses)</code></pre><p>이 값이 충분히 높다면, 해당 cache는 구조적으로 의미가 있다.
월요일에 출근 하자 마자 .. 다시 log 찍어보는걸로!_!</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] CNN-LSTM, LSTM-AE]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-CNN-LSTM-LSTM-AE</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-CNN-LSTM-LSTM-AE</guid>
            <pubDate>Wed, 21 Jan 2026 15:13:40 GMT</pubDate>
            <description><![CDATA[<h1 id="1-lstm-autoencoder">1. LSTM AutoEncoder</h1>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/68bf65c8-83ff-4537-8e87-4ed2f38faeea/image.png" alt=""></p>
<hr>
<h1 id="lstm-autoencoder와-시퀀스-입력출력-구조">LSTM Autoencoder와 시퀀스 입력–출력 구조</h1>
<h2 id="1-시퀀스-모델에서의-입력출력-구조">1. 시퀀스 모델에서의 입력–출력 구조</h2>
<p>순환 신경망(RNN) 및 LSTM 계열 모델은 입력과 출력이 시간 축을 따라 어떻게 대응되는가에 따라 여러 구조로 구분된다.
이러한 구분은 모델이 시계열 정보를 <strong>어떤 방식으로 요약하고, 언제 출력으로 전개하는지</strong>를 이해하는 데 중요한 기준이 된다.</p>
<p>아래 그림은 대표적인 다섯 가지 시퀀스 입력–출력 구조를 도식화한 것이다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/7f2b8d5b-1968-4e40-9cb3-da43ec6d457d/image.png" alt=""></p>
<h3 id="11-one-to-one-구조">1.1 One-to-One 구조</h3>
<p>하나의 입력이 하나의 출력으로 대응되는 구조이다.
시간적 의존성이 명시적으로 활용되지 않으며, 구조적으로는 feedforward 신경망과 동일하다.
시계열 문제에서는 제한적인 경우에만 사용된다.</p>
<p><strong>대표적인 예시</strong></p>
<ul>
<li>특정 시점의 센서 벡터 → 해당 시점의 상태 값 예측</li>
<li>고정 길이 특징 벡터 → 하나의 회귀값 또는 클래스</li>
<li>시계열 데이터에서 시간 축을 제거하고, 통계 요약값만을 입력으로 사용하는 경우</li>
</ul>
<h3 id="12-one-to-many-구조">1.2 One-to-Many 구조</h3>
<p>하나의 입력으로부터 여러 시점의 출력을 생성하는 구조이다.
입력은 고정된 컨텍스트로 작용하며, 출력은 시간 축을 따라 순차적으로 생성된다.</p>
<p><strong>대표적인 예시</strong></p>
<ul>
<li>이미지 하나 → 설명 문장 생성 (Image Captioning)</li>
<li>초기 상태 벡터 → 미래 시점의 시계열 시뮬레이션</li>
<li>단일 조건 벡터 → 제어 시퀀스 생성</li>
</ul>
<h3 id="13-many-to-one-구조">1.3 Many-to-One 구조</h3>
<p>여러 시점의 입력 시퀀스를 하나의 출력으로 요약하는 구조이다.
시계열 분류나 상태 판별과 같이, <strong>시퀀스 전체에 대한 하나의 판단</strong>이 필요한 문제에 주로 사용된다.</p>
<p><strong>대표적인 예시</strong></p>
<ul>
<li>일정 시간 구간의 센서 시계열 → 정상 / 이상 분류</li>
<li>문장 전체 → 감정 분류 결과</li>
<li>공정 로그 시퀀스 → 공정 상태 라벨</li>
</ul>
<h3 id="14-many-to-many-구조-동기식">1.4 Many-to-Many 구조 (동기식)</h3>
<p>입력 시퀀스와 출력 시퀀스가 동일한 길이를 가지며,
각 시점의 입력과 출력이 직접 대응되는 구조이다.</p>
<p><strong>대표적인 예시</strong></p>
<ul>
<li>과거 시계열 → 같은 길이의 미래 시계열 예측</li>
<li>시계열 데이터 → 시점별 이상 점수 산출</li>
<li>문장 입력 → 각 단어에 대한 품사 태깅</li>
</ul>
<h3 id="15-many-to-many-구조-비동기식-encoderdecoder">1.5 Many-to-Many 구조 (비동기식, Encoder–Decoder)</h3>
<p>입력 시퀀스를 모두 처리한 뒤, 별도의 출력 시퀀스를 생성하는 구조이다.
입력과 출력 시퀀스의 길이는 서로 다를 수 있으며,
일반적으로 <strong>Encoder–Decoder 구조</strong> 또는 <strong>sequence-to-sequence 구조</strong>라고 불린다.</p>
<p><strong>대표적인 예시</strong></p>
<ul>
<li>문장 → 다른 언어의 문장 (기계 번역)</li>
<li>음성 신호 시퀀스 → 텍스트 시퀀스</li>
<li>시계열 전체 → 요약된 이벤트 시퀀스</li>
<li>시계열 → 동일한 시계열의 재구성 (<strong>LSTM Autoencoder</strong>)</li>
</ul>
<p>LSTM Autoencoder는 이 구조를 사용하되,
출력 시퀀스를 새로운 목표 시퀀스가 아니라 <strong>입력 시퀀스 자체의 재구성</strong>으로 설정한다.</p>
<hr>
<h2 id="2-lstm-autoencoder의-기본-개념">2. LSTM Autoencoder의 기본 개념</h2>
<p>LSTM Autoencoder는 시계열 데이터를 입력으로 받아,
<strong>동일한 시계열을 다시 복원하도록 학습되는 비지도 학습 모델</strong>이다.</p>
<p>이 모델의 핵심 목적은 다음과 같이 정리할 수 있다.</p>
<blockquote>
<p>시계열 전체를 고정 차원의 잠재 표현으로 압축한 뒤,
해당 표현만을 이용해 원래 시계열의 시간 구조를 재구성한다.</p>
</blockquote>
<p>이를 위해 LSTM Autoencoder는 <strong>Encoder LSTM</strong>과 <strong>Decoder LSTM</strong>으로 구성된다.</p>
<hr>
<h2 id="3-encoderdecoder-구조">3. Encoder–Decoder 구조</h2>
<p>이 글에선 입력 시퀀스와 출력 시퀀스의 길이가 동일한 재구성(autoencoding) 설정을 가정한다.</p>
<h3 id="31-encoder-lstm">3.1 Encoder LSTM</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/42d766f6-0ccc-401e-84ec-83604e29daf5/image.png" alt=""></p>
<p>입력 시계열을 다음과 같이 정의한다.</p>
<p>$$
X = (x_1, x_2, \dots, x_T)
$$</p>
<p>Encoder LSTM은 입력 시퀀스를 시간 순서대로 처리하며 hidden state와 cell state를 갱신한다.</p>
<ul>
<li>Cell state : 시계열 전반에 걸쳐 유지되는 long-term memory를 담당<ul>
<li>여러 시점을 관통하는 추세</li>
<li>장기간 유지되어야 할 상태 정보</li>
<li>급격한 변동에 쉽게 흔들리지 않는 정보</li>
</ul>
</li>
<li>Hidden state : 현재 lstm cell 시점에서의 output<ul>
<li>현재 시점의 입력, 장기 기억(cell state), 직전 hidden state의 요약 정보</li>
</ul>
</li>
</ul>
<p>$$
(h_t, c_t) = \mathrm{LSTM}<em>{enc}(x_t, h</em>{t-1}, c_{t-1})
$$</p>
<p>마지막 시점의 hidden state는 시계열 전체를 요약한 <strong>잠재 벡터</strong>로 사용된다.</p>
<p>$$
z = h_T
$$</p>
<p>Cell state (c_T)를 잠재 표현으로 사용하는 변형도 존재하지만,
일반적으로는 hidden state (h_T)가 더 널리 사용된다.</p>
<hr>
<h3 id="32-decoder-lstm">3.2 Decoder LSTM</h3>
<p>Decoder LSTM은 Encoder가 생성한 잠재 벡터 (z)를 초기 상태로 받아,
시간 순서에 따라 입력 시퀀스를 재구성하는 역할을 한다.</p>
<p>일반적으로 잠재 벡터 (z)는 Decoder LSTM의 초기 hidden state와 cell state로 주어진다.</p>
<p>$$
(\hat{h}_0, \hat{c}_0) = g(z)
$$</p>
<p>이후 시점 (t)에서 Decoder LSTM의 상태는 다음과 같이 갱신된다.</p>
<p>$$
(\hat{h}<em>t, \hat{c}_t)
= \mathrm{LSTM}</em>{dec}(\hat{x}<em>{t-1}, \hat{h}</em>{t-1}, \hat{c}_{t-1})
$$</p>
<p>각 시점의 출력은 hidden state로부터 계산된다.</p>
<p>$$
\hat{x}_t = f(\hat{h}_t)
$$</p>
<p>여기서 (f(\cdot))는 선형 변환 혹은 출력 차원에 맞춘 비선형 함수이다.</p>
<hr>
<h2 id="4-학습-목표-함수">4. 학습 목표 함수</h2>
<p>LSTM Autoencoder는 입력 시계열과 출력 시계열 간의 차이를 최소화하도록 학습된다.
대표적인 손실 함수는 평균 제곱 오차(MSE)이다.</p>
<p>$$
\mathcal{L} = \sum_{t=1}^{T} | x_t - \hat{x}_t |^2
$$</p>
<p>이 손실 함수는 단순한 복사 문제처럼 보일 수 있으나,
다음과 같은 구조적 제약이 존재한다.</p>
<ul>
<li>Encoder는 전체 시계열을 하나의 고정 차원 벡터로 압축해야 한다.</li>
<li>Decoder는 해당 벡터를 기반으로 시간적 구조를 복원해야 한다.</li>
</ul>
<p>따라서 잠재 벡터 (z)에는 시계열의 전반적인 동역학 정보가 내재될 수밖에 없다.</p>
<hr>
<h2 id="5-many-to-many-구조와-lstm-autoencoder의-해석">5. Many-to-Many 구조와 LSTM Autoencoder의 해석</h2>
<p>그림 X의 비동기식 many-to-many 구조는 LSTM Autoencoder를 이해하는 핵심 틀을 제공한다.</p>
<ul>
<li>입력 시퀀스 전체 → Encoder</li>
<li>잠재 벡터 (z) → Decoder</li>
<li>출력 시퀀스 전체</li>
</ul>
<p>LSTM Autoencoder는 이 구조를 그대로 사용하되,
출력 시퀀스를 새로운 목표 시퀀스가 아니라 <strong>입력 시퀀스 자체의 재구성</strong>으로 설정한다.</p>
<p>즉,</p>
<blockquote>
<p>LSTM Autoencoder는
sequence-to-sequence 형태의 many-to-many 구조를 이용한
시계열 재구성 모델이다.</p>
</blockquote>
<hr>
<h2 id="6-이상-탐지-관점에서의-활용">6. 이상 탐지 관점에서의 활용</h2>
<p>이상 탐지 목적으로 사용되는 경우,
LSTM Autoencoder는 일반적으로 <strong>정상 시계열 데이터만을 사용해 학습</strong>된다.</p>
<p>정상 데이터에 대해 학습된 모델은 정상적인 시간 패턴에 대해서는 낮은 재구성 오차를 보이는 반면,
비정상적인 패턴에 대해서는 높은 재구성 오차를 보인다.</p>
<p>시점별 재구성 오차는 다음과 같이 정의할 수 있다.</p>
<p>$$
e_t = | x_t - \hat{x}_t |
$$</p>
<p>또는 시퀀스 단위의 재구성 오차:</p>
<p>$$
E = \sum_{t=1}^{T} e_t
$$</p>
<p>이 오차를 기준으로 시계열의 정상 여부를 판단할 수 있다.</p>
<h2 id="7-코드">7. 코드</h2>
<pre><code class="language-py">import torch.nn as nn


# Encoder
class Encoder(nn.Module):
    def __init__(self, input_dim, hidden_dim, dropout, seq_len):
        super().__init__()
        self.input_dim = input_dim
        self.hidden_dim = hidden_dim
        self.seq_len = seq_len

        self.encoder_lstm = nn.LSTM(
            input_size=input_dim,
            hidden_size=hidden_dim,
            dropout=dropout,
            batch_first=True
        )

    def forward(self, input_seq):
        encoder_outputs, (h_n, c_n) = self.encoder_lstm(input_seq)

        # last hidden state -&gt; latent representation
        latent = h_n.squeeze(0)                       # (B, H)
        latent_seq = latent.unsqueeze(1).repeat(1, input_seq.size(1), 1)

        return latent_seq, encoder_outputs


# Decoder
class Decoder(nn.Module):
    def __init__(self, output_dim, hidden_dim, dropout, seq_len, use_output_activation):
        super().__init__()
        self.output_dim = output_dim
        self.hidden_dim = hidden_dim
        self.seq_len = seq_len
        self.use_output_activation = use_output_activation

        self.decoder_lstm = nn.LSTM(
            input_size=hidden_dim,
            hidden_size=hidden_dim,
            dropout=dropout,
            batch_first=True
        )

        self.output_layer = nn.Linear(hidden_dim, output_dim)
        self.activation = nn.Sigmoid()

    def forward(self, latent_seq):
        decoder_outputs, (h_n, c_n) = self.decoder_lstm(latent_seq)
        reconstructed_seq = self.output_layer(decoder_outputs)

        if self.use_output_activation:
            reconstructed_seq = self.activation(reconstructed_seq)

        return reconstructed_seq, h_n


# LSTM AutoEncoder
class LSTMAutoEncoder(nn.Module):
    def __init__(self, input_dim, hidden_dim, dropout, seq_len, use_output_activation=True):
        super().__init__()
        self.input_dim = input_dim
        self.hidden_dim = hidden_dim
        self.seq_len = seq_len

        self.encoder = Encoder(
            input_dim=input_dim,
            hidden_dim=hidden_dim,
            dropout=dropout,
            seq_len=seq_len
        )

        self.decoder = Decoder(
            output_dim=input_dim,
            hidden_dim=hidden_dim,
            dropout=dropout,
            seq_len=seq_len,
            use_output_activation=use_output_activation
        )

    def forward(self, input_seq, return_latent=False, return_encoder_outputs=False):
        latent_seq, encoder_outputs = self.encoder(input_seq)
        reconstructed_seq, last_hidden = self.decoder(latent_seq)

        if return_latent:
            return reconstructed_seq, last_hidden
        elif return_encoder_outputs:
            return reconstructed_seq, encoder_outputs

        return reconstructed_seq</code></pre>
<h1 id="2-cnnlstm-모델">2. CNN–LSTM 모델</h1>
<h2 id="1-cnnlstm이-필요한-배경">1. CNN–LSTM이 필요한 배경</h2>
<p>시계열 데이터 $x_1, x_2, \dots, x_T$는 다음 두 가지 구조를 동시에 가진다.</p>
<ol>
<li><p><strong>국소 패턴(Local pattern)</strong>
짧은 시간 구간에서 반복적으로 나타나는 파형, 주기, 급격한 변화
→ 필터 기반의 국소 특징 추출에 유리</p>
</li>
<li><p><strong>장기 의존성(Temporal dependency)</strong>
과거의 상태가 현재의 해석에 영향을 미침
→ 상태(state)를 누적하는 순환 구조가 필요</p>
</li>
</ol>
<p>CNN–LSTM은 이 두 구조를 <strong>분리된 역할</strong>로 처리한다.</p>
<table>
<thead>
<tr>
<th>구성 요소</th>
<th>역할</th>
</tr>
</thead>
<tbody><tr>
<td>CNN</td>
<td>짧은 시간 구간의 <strong>형태적 패턴</strong> 추출</td>
</tr>
<tr>
<td>LSTM</td>
<td>패턴들의 <strong>시간적 연결 관계</strong> 모델링</td>
</tr>
</tbody></table>
<p>즉,</p>
<blockquote>
<p><strong>CNN은 “무엇이 일어났는지”를 요약하고,
LSTM은 “그 일이 언제, 어떤 순서로 일어났는지”를 학습한다.</strong></p>
</blockquote>
<hr>
<h2 id="2-전체-구조-개요">2. 전체 구조 개요</h2>
<p>입력 시계열을 다음과 같이 표현한다.</p>
<p>$$
\mathbf{X} \in \mathbb{R}^{T \times D}
$$</p>
<ul>
<li>$T$: 시계열 길이</li>
<li>$D$: 채널 수 (센서 수, 변수 수)</li>
</ul>
<p>CNN–LSTM의 처리 흐름은 다음과 같다.
<img src="https://velog.velcdn.com/images/you_n_sunny/post/cd5aa396-5ac0-4a68-b906-512dbdde894c/image.png" alt=""></p>
<p>$$
\mathbf{X}
;\xrightarrow{\text{CNN}};
\mathbf{F}
;\xrightarrow{\text{LSTM}};
\mathbf{H}
;\xrightarrow{\text{Output Layer}};
\hat{\mathbf{y}}
$$</p>
<hr>
<h2 id="3-cnn-단계-국소-패턴-추출">3. CNN 단계: 국소 패턴 추출</h2>
<h3 id="31-1d-convolution의-적용">3.1 1D Convolution의 적용</h3>
<p>시계열에서는 <strong>시간 축을 따라 1D Convolution</strong>을 적용한다.</p>
<p>$$
\mathbf{F}<em>t^{(k)}
= \sum</em>{i=0}^{K-1} \mathbf{W}<em>i^{(k)} \cdot \mathbf{X}</em>{t+i} + b^{(k)}
$$</p>
<ul>
<li>$K$: 커널 크기 (time window)</li>
<li>$k$: 필터 인덱스</li>
<li>$\mathbf{F}^{(k)}$: $k$번째 필터의 feature map</li>
</ul>
<h3 id="32-의미-해석">3.2 의미 해석</h3>
<p>이 연산은 다음을 의미한다.</p>
<ul>
<li><strong>짧은 시간 구간에서의 패턴을 감지</strong></li>
<li>위치가 달라도 동일한 패턴을 같은 특징으로 인식</li>
<li>노이즈에 비교적 강인</li>
</ul>
<p>CNN의 출력은 다음과 같은 형태가 된다.</p>
<p>$$
\mathbf{F} \in \mathbb{R}^{T&#39; \times C}
$$</p>
<ul>
<li>$C$: CNN 필터 개수</li>
<li>$T&#39;$: padding/stride에 따라 줄어든 시간 길이</li>
</ul>
<h3 id="33-pooling의-역할-선택적">3.3 Pooling의 역할 (선택적)</h3>
<p>Pooling이 사용될 경우,</p>
<ul>
<li>미세한 시간 정렬 오차에 둔감</li>
<li>계산량 감소</li>
<li>그러나 <strong>시간 해상도 손실</strong></li>
</ul>
<p>→ LSTM과 결합 시에는 <strong>과도한 pooling은 피하는 경우가 많다</strong></p>
<hr>
<h2 id="4-lstm-단계-시간적-의존성-학습">4. LSTM 단계: 시간적 의존성 학습</h2>
<p>CNN 출력 $\mathbf{F}$를 LSTM의 입력 시퀀스로 사용한다.</p>
<p>$$
\mathbf{f}<em>1, \mathbf{f}_2, \dots, \mathbf{f}</em>{T&#39;}
$$</p>
<h3 id="41-lstm-내부-연산">4.1 LSTM 내부 연산</h3>
<p>각 시점 $t$에서 LSTM은 다음을 계산한다.</p>
<p>$$
\begin{aligned}
\mathbf{i}<em>t &amp;= \sigma(W_i \mathbf{f}_t + U_i \mathbf{h}</em>{t-1} + b_i)
\end{aligned}
$$
$$\begin{aligned}
\mathbf{f}<em>t &amp;= \sigma(W_f \mathbf{f}_t + U_f \mathbf{h}</em>{t-1} + b_f)
\end{aligned}
$$
$$\begin{aligned}
\mathbf{o}<em>t &amp;= \sigma(W_o \mathbf{f}_t + U_o \mathbf{h}</em>{t-1} + b_o)
\end{aligned}
$$
$$\begin{aligned}
\tilde{\mathbf{c}}<em>t &amp;= \tanh(W_c \mathbf{f}_t + U_c 
\mathbf{h}</em>{t-1} + b_c)
\end{aligned}
$$</p>
<p>$$
\begin{aligned}
\mathbf{c}<em>t &amp;= \mathbf{f}_t \odot \mathbf{c}</em>{t-1}
  \end{aligned}
  $$
$$
\begin{aligned}
\mathbf{i}_t \odot \tilde{\mathbf{c}}_t <br>  \mathbf{h}_t &amp;= \mathbf{o}_t \odot \tanh(\mathbf{c}_t)
  \end{aligned}
  $$</p>
<h3 id="42-cnn과-결합되었을-때의-의미">4.2 CNN과 결합되었을 때의 의미</h3>
<ul>
<li>LSTM은 <strong>원시 신호</strong>가 아니라
<strong>CNN이 요약한 고수준 특징 시퀀스</strong>를 처리</li>
<li>불필요한 단기 변동은 CNN에서 제거됨</li>
<li>LSTM은 <strong>장기 흐름에 집중 가능</strong></li>
</ul>
<hr>
<h2 id="5-출력-구조">5. 출력 구조</h2>
<p>문제 유형에 따라 출력 방식이 달라진다.</p>
<h3 id="51-시계열-분류">5.1 시계열 분류</h3>
<ul>
<li>마지막 hidden state 사용</li>
</ul>
<p>$$
\hat{y} = \mathrm{softmax}(W \mathbf{h}_{T&#39;} + b)
$$</p>
<h3 id="52-시계열-회귀--예측">5.2 시계열 회귀 / 예측</h3>
<ul>
<li>마지막 또는 모든 시점의 hidden state 사용</li>
</ul>
<p>$$
\hat{y}_t = W \mathbf{h}_t + b
$$</p>
<h3 id="53-이상-탐지-autoencoder-계열">5.3 이상 탐지 (Autoencoder 계열)</h3>
<ul>
<li>LSTM Decoder를 추가하여 재구성 오차 사용</li>
</ul>
<hr>
<h2 id="6-cnnlstm의-설계-의도-정리">6. CNN–LSTM의 설계 의도 정리</h2>
<table>
<thead>
<tr>
<th>단계</th>
<th>설계 목적</th>
</tr>
</thead>
<tbody><tr>
<td>CNN</td>
<td>국소 패턴 자동 추출, 노이즈 완화</td>
</tr>
<tr>
<td>LSTM</td>
<td>시간 순서 및 장기 의존성 모델링</td>
</tr>
<tr>
<td>결합</td>
<td>역할 분리로 학습 안정성 향상</td>
</tr>
</tbody></table>
<p>CNN–LSTM은 다음 가정을 내포한다.</p>
<blockquote>
<p>“의미 있는 단기 패턴이 먼저 존재하고,
그 패턴들의 시간적 조합이 전체 의미를 결정한다.”</p>
</blockquote>
<hr>
<h2 id="7-장점과-한계">7. 장점과 한계</h2>
<h3 id="71-장점">7.1 장점</h3>
<ul>
<li>원시 시계열에서 <strong>수작업 feature engineering 감소</strong></li>
<li>LSTM 단독 사용 대비 학습 안정성 증가</li>
<li>다변량 센서 데이터에 적합</li>
</ul>
<h3 id="72-한계">7.2 한계</h3>
<ul>
<li>구조가 비교적 무거움 (CNN + LSTM)</li>
<li>매우 긴 시계열에서는 여전히 비효율</li>
<li>전역 의존성 모델링에는 Transformer 대비 한계</li>
</ul>
<hr>
<h2 id="8-언제-cnnlstm을-사용하는가">8. 언제 CNN–LSTM을 사용하는가</h2>
<p>다음 조건일 때 특히 효과적이다.</p>
<ul>
<li>짧은 주기의 반복 패턴이 중요한 경우</li>
<li>센서 신호, 진동, 로그 시계열</li>
<li>이상 탐지, 상태 분류, 이벤트 인식</li>
</ul>
<p>반대로,</p>
<ul>
<li>장기 의존성이 매우 길거나</li>
<li>전역 패턴이 중요한 경우</li>
</ul>
<p>→ TCN, Transformer 계열이 더 적합할 수 있다.</p>
<h2 id="9-코드">9. 코드</h2>
<pre><code class="language-py">class CNNLSTM(nn.Module):
    def __init__(
        self,
        input_dim,        # D
        cnn_channels,     # C
        kernel_size,      # K
        lstm_hidden_dim,  # H
        lstm_layers,
        num_classes,
        dropout=0.0
    ):
        super().__init__()

        # -------------------------
        # 1. CNN (1D Convolution)
        # -------------------------
        self.conv1d = nn.Conv1d(
            in_channels=input_dim,
            out_channels=cnn_channels,
            kernel_size=kernel_size,
            padding=kernel_size // 2  # time length 유지
        )

        self.relu = nn.ReLU()

        # -------------------------
        # 2. LSTM
        # -------------------------
        self.lstm = nn.LSTM(
            input_size=cnn_channels,
            hidden_size=lstm_hidden_dim,
            num_layers=lstm_layers,
            batch_first=True,
            dropout=dropout if lstm_layers &gt; 1 else 0.0
        )

        # -------------------------
        # 3. Output layer
        # -------------------------
        self.fc = nn.Linear(lstm_hidden_dim, num_classes)

    def forward(self, x):
        &quot;&quot;&quot;
        x: (B, T, D)
        &quot;&quot;&quot;

        # -------------------------
        # CNN expects (B, D, T)
        # -------------------------
        x = x.permute(0, 2, 1)

        # (B, C, T)
        x = self.conv1d(x)
        x = self.relu(x)

        # -------------------------
        # LSTM expects (B, T, C)
        # -------------------------
        x = x.permute(0, 2, 1)

        # x: (B, T, C)
        lstm_out, (h_n, c_n) = self.lstm(x)

        # -------------------------
        # 마지막 시점 hidden state
        # -------------------------
        h_last = lstm_out[:, -1, :]   # (B, H)

        # -------------------------
        # Output
        # -------------------------
        out = self.fc(h_last)         # (B, num_classes)

        return out</code></pre>
<ul>
<li>pytorch conv1d 입력 형식 : (batch, channels, length)</li>
<li>시계열 데이터 : (batch, time, features)
→ 시간 축을 length로 보기 위해 permute가 필수</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 딥러닝 최적화 보충]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B3%B4%EC%B6%A9</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%B5%9C%EC%A0%81%ED%99%94-%EB%B3%B4%EC%B6%A9</guid>
            <pubDate>Mon, 19 Jan 2026 14:03:48 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/e8bfc01d-8f44-4e9e-9bde-66a22711c154/image.jpg" alt="딥러닝 최적화 보충1">
<img src="https://velog.velcdn.com/images/you_n_sunny/post/ea0be882-b540-4510-903f-23c7cfeaf581/image.jpg" alt="딥러닝 최적화 보충2">
<img src="https://velog.velcdn.com/images/you_n_sunny/post/e2d86ead-c738-4112-b78b-beabf181e7d8/image.jpg" alt="딥러닝 최적화 보충3"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 딥러닝 학습]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%94%A5%EB%9F%AC%EB%8B%9D-%ED%95%99%EC%8A%B5</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%94%A5%EB%9F%AC%EB%8B%9D-%ED%95%99%EC%8A%B5</guid>
            <pubDate>Mon, 19 Jan 2026 09:17:48 GMT</pubDate>
            <description><![CDATA[<h2 id="1장-오버피팅overfitting">1장. 오버피팅(Overfitting)</h2>
<h3 id="1-오버피팅이란-무엇인가">1. 오버피팅이란 무엇인가</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/cf9ee9c0-98d5-4fec-b92a-3128ef0c0d30/image.png" alt=""></p>
<p>오버피팅(overfitting)이란, 학습된 모델이 <strong>훈련 데이터(training data)</strong> 에는 매우 높은 성능을 보이지만, 학습에 사용되지 않은 <strong>시험 데이터(test data)</strong> 나 실제 환경의 새로운 데이터에 대해서는 성능이 급격히 저하되는 현상을 의미한다. 이는 모델이 데이터에 존재하는 <strong>일반적인 패턴</strong>을 학습한 것이 아니라, 훈련 데이터에만 존재하는 <strong>우연적 노이즈나 특이한 패턴</strong>까지 과도하게 학습했기 때문에 발생한다.</p>
<p>기계학습과 딥러닝의 궁극적인 목표는 훈련 데이터 자체를 잘 맞히는 것이 아니라, <strong>보지 못한 데이터에 대해서도 일관되게 좋은 예측을 수행하는 범용 성능(generalization performance)</strong> 을 확보하는 것이다. 따라서 오버피팅은 실무 관점에서도 반드시 해결해야 할 핵심 문제이다.</p>
<h3 id="2-오버피팅이-발생하는-주요-원인">2. 오버피팅이 발생하는 주요 원인</h3>
<p>오버피팅은 다음과 같은 조건에서 특히 빈번하게 발생한다.</p>
<ol>
<li><p><strong>훈련 데이터의 양이 부족한 경우</strong>
데이터가 충분하지 않으면, 모델은 데이터의 통계적 특성을 안정적으로 학습하기 어렵다. 특히 딥러닝 모델처럼 파라미터 수가 많은 경우, 소량의 데이터는 오버피팅을 거의 필연적으로 유발한다.</p>
</li>
<li><p><strong>모델의 복잡도가 데이터에 비해 지나치게 높은 경우</strong>
층의 수가 많거나, 각 층의 뉴런 수가 매우 큰 모델은 높은 표현력을 갖는다. 표현력이 높다는 것은 복잡한 함수 형태를 근사할 수 있다는 장점이 있지만, 동시에 훈련 데이터에 과도하게 맞춰질 위험이 커진다.</p>
</li>
<li><p><strong>학습을 지나치게 오래 수행한 경우</strong>
초기에는 훈련 데이터와 검증 데이터 모두 성능이 향상되지만, 일정 시점 이후에는 검증 성능이 오히려 하락하는 경우가 많다. 이는 모델이 훈련 데이터에 과적합되기 시작했음을 의미한다.</p>
</li>
</ol>
<h3 id="3-오버피팅의-전형적인-관찰-패턴">3. 오버피팅의 전형적인 관찰 패턴</h3>
<p>오버피팅이 발생하면 다음과 같은 특징적인 현상이 나타난다.</p>
<ul>
<li>훈련 정확도는 에폭(epoch)이 증가함에 따라 지속적으로 상승</li>
<li>검증(validation)/시험(test) 정확도는 어느 시점 이후 정체되거나 하락</li>
<li>훈련 손실(loss)은 감소하지만, 검증 손실은 증가</li>
</ul>
<p>이러한 패턴을 통해 오버피팅 여부를 시각적으로 판단할 수 있다.</p>
<hr>
<h2 id="2장-가중치-감쇠weight-decay">2장. 가중치 감쇠(Weight Decay)</h2>
<h3 id="1-가중치-감쇠의-직관적-이해">1. 가중치 감쇠의 직관적 이해</h3>
<p>딥러닝 모델에서 가중치(weight)가 커질수록 모델은 입력 데이터의 작은 변화에도 매우 민감하게 반응하게 된다. 이는 모델의 표현력을 증가시키는 효과가 있지만, 동시에 데이터의 노이즈까지 학습하게 만들어 오버피팅을 유발한다.</p>
<p><strong>가중치 감쇠(weight decay)</strong> 는 이러한 문제를 완화하기 위해, 학습 과정에서 <strong>가중치의 크기가 커지는 것을 억제</strong>하는 정규화(regularization) 기법이다.</p>
<h3 id="2-수식적-정의">2. 수식적 정의</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/cfbe4369-63b9-4518-a4a1-fbf2ca8ee8cf/image.png" alt=""></p>
<p>가중치 감쇠는 손실 함수(loss function)에 가중치의 L2 노름(norm) 제곱을 패널티 항으로 추가하는 방식으로 구현된다.</p>
<ul>
<li><p>기존 손실 함수: $$L$$</p>
</li>
<li><p>가중치 감쇠가 적용된 손실 함수: $$L&#39; = L + λ \sum_i w_i^2$$</p>
</li>
</ul>
<p>여기서 λ(람다)는 <strong>정규화 계수(regularization strength)</strong> 로, 가중치 크기에 대한 패널티의 강도를 조절한다.</p>
<h4 id="1-가중치가-커질수록-더-강한-패널티">(1) 가중치가 커질수록 더 강한 패널티</h4>
<p>과적합의 핵심 원인은 보통 </p>
<blockquote>
<p><strong>일부 가중치가 과도하게 커지면서
특정 샘플에만 맞춘 결정 경계를 만드는 것</strong></p>
</blockquote>
<p>** 결정 경계란 아주 미세한 입력 차이까지 구분하려는 경계이다.
** 일부 가중치가 과도하게 커진다는 것은, 입력 공간의 특정 방향에 대해 모델의 출력이 지나치게 민감해졌음을 의미한다. 이러한 경우 결정 경계는 소수의 샘플, 특히 노이즈나 이상치가 포함된 샘플을 정확히 분류하기 위해 국소적으로 급격하게 변형된다. 그 결과 모델은 데이터 분포 전반에 공통적으로 나타나는 구조를 학습하기보다는, 개별 학습 샘플의 우연한 특성에 맞춰진 결정 규칙을 형성하게 된다.
** 하여, 전체 데이터 분포에는 중요하지 않은 방향에 매우 큰 가중치가 할당될 수 있다.</p>
<p>그래서 정규화 항은 다음 성질을 가져야 한다.</p>
<ul>
<li>(w = 0) 근처에서는 영향이 작고</li>
<li>(|w|)가 커질수록 <strong>패널티가 빠르게 증가</strong>
 ** 특정 가중치를 과도하게 키우는 것을 방지</li>
</ul>
<p>즉,</p>
<ul>
<li>작은 가중치: 거의 벌점 없음</li>
<li>큰 가중치: 제곱으로 급격히 증가</li>
</ul>
<h4 id="2-미분-가능하고-최적화가-쉬워야-한다">(2) 미분 가능하고, 최적화가 쉬워야 한다</h4>
<p>신경망은 <strong>경사 하강법</strong>으로 학습한다.
따라서 패널티 항은 반드시:</p>
<ul>
<li>모든 점에서 미분 가능</li>
<li>gradient가 안정적이어야 한다</li>
</ul>
<p>L2 제곱의 미분은:</p>
<p>$$\frac{\partial}{\partial w_i} \left( \frac{\lambda}{2} w_i^2 \right)
= \lambda w_i$$</p>
<p>이 결과가 중요한 이유는:</p>
<ul>
<li>gradient가 <strong>선형</strong></li>
<li>갑작스럽게 튀지 않음</li>
<li>학습이 안정적</li>
</ul>
<p>(반면 L1은 (w=0)에서 미분 불연속 → 다른 성질을 만든다)</p>
<h3 id="3-정규화가-포함된-손실-함수">3. 정규화가 포함된 손실 함수</h3>
<p>원래 손실 함수 (L(w))에 L2 패널티를 추가하면:</p>
<p>$$\tilde{L}(w) = L(w) + \frac{\lambda}{2} |w|_2^2$$</p>
<p>이걸 그대로 미분하면</p>
<p>$$\frac{\partial \tilde{L}}{\partial w} = \frac{\partial L}{\partial w}* \lambda w$$</p>
<p>업데이트 식은 다음과 같다.</p>
<p>$$w \leftarrow w - \eta \left( \frac{\partial L}{\partial w} + \lambda w \right)$$</p>
<p>이를 분해하면:</p>
<p>$$w \leftarrow (1 - \eta \lambda) w ;-; \eta \frac{\partial L}{\partial w}$$</p>
<ul>
<li><p>$$- \eta \frac{\partial L}{\partial w}$$
→ <strong>데이터에 맞추려는 힘</strong></p>
</li>
<li><p>$$(1 - \eta \lambda) w$$
→ <strong>가중치를 원점(0)으로 끌어당기는 힘</strong></p>
</li>
</ul>
<p>즉, L2 정규화는매 step마다 모든 가중치를 <strong>조금씩 0 방향으로 수축(shrink)</strong> 시킨다</p>
<p>그래서 이름이 <strong>Weight Decay(가중치 감쇠)</strong> 다.</p>
<ul>
<li>(w)가 크면 → 과적합 가능성 큼 → 강하게 줄여야 함</li>
<li>(w)가 작으면 → 이미 단순한 모델 → 거의 건드리지 않음</li>
</ul>
<h3 id="4-학습-과정에서의-영향">4. 학습 과정에서의 영향</h3>
<p>경사 하강법(gradient descent)을 적용하면, 각 가중치는 다음과 같이 업데이트된다.</p>
<ul>
<li><p>기존 업데이트:
$$w ← w − η (∂L/∂w)$$</p>
</li>
<li><p>가중치 감쇠 적용 후:
$$w ← w − η (∂L/∂w + λw)$$</p>
</li>
</ul>
<p>즉, 가중치는 손실 함수의 기울기뿐 아니라 자신의 크기에 비례하여 추가적으로 감소하는 방향으로 업데이트된다.</p>
<h3 id="5-효과와-한계">5. 효과와 한계</h3>
<ul>
<li><p>장점:</p>
<ul>
<li>모델이 지나치게 복잡해지는 것을 방지</li>
<li>구현이 단순하고 계산 비용이 낮음</li>
</ul>
</li>
<li><p>한계:</p>
<ul>
<li>λ 값이 너무 크면 과소적합(underfitting) 발생</li>
<li>데이터가 매우 복잡한 경우 단독으로는 충분하지 않을 수 있음</li>
</ul>
</li>
</ul>
<hr>
<h2 id="3장-드롭아웃dropout">3장. 드롭아웃(Dropout)</h2>
<h3 id="1-드롭아웃의-기본-아이디어">1. 드롭아웃의 기본 아이디어</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/cf134a13-1f35-4c3f-ae48-5003c3cf63bc/image.png" alt=""></p>
<p>드롭아웃(dropout)은 학습 단계에서 <strong>신경망의 일부 뉴런을 무작위로 비활성화</strong>하는 기법이다. 각 미니배치(mini-batch)가 입력될 때마다, 뉴런은 확률 p로만 활성화되고 나머지는 임시로 제거된다.</p>
<p>이는 다음과 같은 효과를 갖는다.</p>
<ul>
<li>매 학습 단계마다 서로 다른 구조의 신경망을 학습하는 효과</li>
<li>특정 뉴런이나 경로에 대한 과도한 의존 방지</li>
<li>앙상블(ensemble) 학습과 유사한 정규화 효과</li>
</ul>
<h3 id="2-training-과-testing-단계의-차이">2. training 과 testing 단계의 차이</h3>
<ul>
<li><strong>학습 단계(training)</strong>
일부 뉴런만 활성화된 축소된 네트워크로 학습 수행
forward pass에서 일부 뉴런의 출력이 0이 됨
뉴런들의 결과를 평균내기 때문에, 출력 크기 자체가 줄어듬
수식적으로는, 뉴런 수는 그대로이며, 각 뉴런의 출력의 기댓값이 줄어든 것임</li>
</ul>
<ul>
<li><strong>시험 단계(testing)</strong>
모든 뉴런을 사용하되, 출력값에 활성화 확률 p를 곱하여 스케일을 보정</li>
</ul>
<p>이는 학습 시 평균적으로 사용된 활성화 크기와 시험 시 활성화 크기를 일치시키기 위함이다.</p>
<h3 id="3-직관적-해석">3. 직관적 해석</h3>
<p>드롭아웃은 각 뉴런이 &quot;언제든지 제거될 수 있다&quot;는 가정하에 학습되도록 강제한다. 그 결과, 모델은 특정 뉴런에 의존하지 않고 보다 <strong>분산된 표현(distributed representation)</strong> 을 학습하게 된다.
즉, 어떤 뉴런도 항상 켜져있다(0이 아니다)고 가정할 수 없고, 다음 step에서도 반드시 사용될 것이라고 기대할 수 없다는 것을 의미한다. 이로 인하여, drop out을 적용한 최적화는 &quot;일부 뉴런이 없어져도, 전체 출력이 크게 변하지 않는 방향&quot;으로 유도된다. 이는 모델이 자연스럽게 하나의 의미(특징)를 여러 뉴런이 나눠서 표현하게 하여, 단일 뉴런 하나에만 강하게 인코딩 되지 않고 여러 뉴런의 활성화 조합으로 표현되어 의미 정보가 여러 뉴런에 분산되어 저장된다.</p>
<h3 id="4-장단점">4. 장단점</h3>
<ul>
<li><p>장점:</p>
<ul>
<li>강력한 오버피팅 억제 효과</li>
<li>다양한 문제에서 안정적인 성능 향상</li>
</ul>
</li>
<li><p>단점:</p>
<ul>
<li>학습 속도 저하
이는 매번 구조가 조금씩 바뀌는 모델을 학습하기 때문에,
gradient의 분산이 커지고 손실 곡선이 덜 매끄러워져, 수렴이 느려진다.</li>
<li>배치 정규화와 함께 사용할 경우 조합에 주의 필요
배치 정규화는 다음을 가정한다.
&quot;현재 레이어의 활성화 분포는, 미니 배치 기준으로 비교적 안정적인 추정이 가능&quot;
배치 정규화는 학습 중에, 배치의 평균과 분산을 계산하여 이 통계 값을 이동 평균으로 누적하고 테스트시에는 이 값을 사용한다.
하지만, 배치 정규화 앞에 드롭아웃을 두면, 활성화가 랜덤으로 0이되어, 배치 평균과 분산이 매 step마다 크게 흔들리고, 드롭아웃 노이즈가 섞인 분포를 학습하여 테스트 시 분포와 실제 분포가 달라지게 되는 문제점이 있다
만약 배치 정규화 뒤에 드롭아웃을 두면 배치 정규화의 작업을 드롭아웃이 다시 랜덤하게 꺠뜨리기때문에, 배치 정규화가 만든 안정성이 일부 상쇄된다. 이 경우 배치 정규화의 효고와 드롭아웃의 효과가 중복되거나 충돌될 수 있다.</li>
</ul>
</li>
</ul>
<hr>
<h2 id="4장-검증-데이터와-하이퍼파라미터-최적화">4장. 검증 데이터와 하이퍼파라미터 최적화</h2>
<h3 id="1-하이퍼파라미터란-무엇인가">1. 하이퍼파라미터란 무엇인가</h3>
<p>하이퍼파라미터(hyper-parameter)는 오차 역전파로 학습되지 않고, 사용자가 사전에 설정해야 하는 모든 파라미터를 의미한다.</p>
<p>대표적인 예시는 다음과 같다.</p>
<ul>
<li>학습률(learning rate)</li>
<li>배치 크기(batch size)</li>
<li>은닉층의 수와 각 층의 뉴런 수</li>
<li>가중치 감쇠 계수 λ</li>
<li>드롭아웃 비율</li>
<li>학습 epoch 수</li>
</ul>
<h3 id="2-데이터-분할의-원칙">2. 데이터 분할의 원칙</h3>
<ul>
<li>훈련 데이터: 모델 파라미터 학습</li>
<li>검증 데이터: 하이퍼파라미터 선택</li>
<li>시험 데이터: 최종 성능 평가</li>
</ul>
<p>시험 데이터를 하이퍼파라미터 조정에 사용하면, 모델이 시험 데이터에 오버피팅되므로 절대 사용해서는 안 된다.</p>
<h3 id="3-하이퍼파라미터-탐색-절차">3. 하이퍼파라미터 탐색 절차</h3>
<ol>
<li>탐색할 하이퍼파라미터 범위 설정</li>
<li>범위 내에서 무작위 또는 체계적으로 샘플링</li>
<li>각 설정에 대해 짧은 학습 수행</li>
<li>검증 성능을 기준으로 범위 축소 및 반복</li>
</ol>
<hr>
<h2 id="5장-early-stopping">5장. Early Stopping</h2>
<h3 id="1-early-stopping의-개념">1. Early Stopping의 개념</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/116637e4-54c5-4174-8500-45c2de163863/image.png" alt=""></p>
<p>Early stopping은 학습을 충분히 오래 수행하지 않고, <strong>검증 성능이 더 이상 개선되지 않는 시점에서 학습을 중단</strong>하는 기법이다.</p>
<p>이는 학습 자체를 정규화로 활용하는 방법이라고 볼 수 있다.</p>
<h3 id="2-장점">2. 장점</h3>
<ul>
<li>추가적인 계산 비용 없이 오버피팅 억제</li>
<li>매우 깊은 모델에서도 실용적인 적용 가능</li>
</ul>
<hr>
<h2 id="6장-배치-정규화batch-normalization">6장. 배치 정규화(Batch Normalization)</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/f94909b3-c0f8-492a-b3df-7e3632f58780/image.png" alt=""></p>
<h3 id="1-깊은-신경망-학습의-어려움">1. 깊은 신경망 학습의 어려움</h3>
<p>깊은 신경망에서는 이전 계층의 가중치가 업데이트될 때마다, 다음 계층으로 전달되는 입력 분포가 계속 변한다. 이를 <strong>내부 공변량 변화(internal covariate shift)</strong> 라고 하며, 학습을 불안정하고 느리게 만드는 주요 원인이다.</p>
<h3 id="2-배치-정규화의-핵심-아이디어">2. 배치 정규화의 핵심 아이디어</h3>
<p>배치 정규화는 각 계층의 입력을 미니배치 단위로 정규화하여 평균 0, 분산 1에 가깝게 만드는 기법이다.</p>
<p>절차는 다음과 같다.</p>
<ol>
<li>미니배치 평균 계산</li>
<li>미니배치 분산 계산</li>
<li>정규화 수행</li>
<li>학습 가능한 파라미터 γ, β로 스케일 및 이동</li>
</ol>
<p>이를 통해 표현력 감소 문제를 해결한다.</p>
<h3 id="3-training-과-testing-단계의-처리-방식">3. training 과 testing 단계의 처리 방식</h3>
<ul>
<li>학습 단계: 미니배치 통계량 사용</li>
<li>시험 단계: 학습 중 누적한 이동 평균(mean, variance) 사용</li>
</ul>
<p>이를 통해 입력이 하나씩 들어오는 상황에서도 안정적인 정규화를 수행할 수 있다.</p>
<h3 id="4-배치-정규화의-효과">4. 배치 정규화의 효과</h3>
<ul>
<li>학습 속도 대폭 향상</li>
<li>초기 가중치에 대한 민감도 감소</li>
<li>오버피팅 완화 효과</li>
</ul>
<h3 id="5-한계와-확장-기법">5. 한계와 확장 기법</h3>
<p>배치 크기가 매우 작을 경우, 통계량이 불안정해진다. 이를 보완하기 위해 다음과 같은 기법들이 제안되었다.</p>
<ul>
<li>Layer Normalization</li>
<li>Group Normalization</li>
<li>Instance Normalization</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 딥러닝 최적화와 초기화]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%B5%9C%EC%A0%81%ED%99%94%EC%99%80-%EC%B4%88%EA%B8%B0%ED%99%94</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%94%A5%EB%9F%AC%EB%8B%9D-%EC%B5%9C%EC%A0%81%ED%99%94%EC%99%80-%EC%B4%88%EA%B8%B0%ED%99%94</guid>
            <pubDate>Tue, 13 Jan 2026 15:13:48 GMT</pubDate>
            <description><![CDATA[<h2 id="1-딥러닝-학습">1. 딥러닝 학습</h2>
<h3 id="11-학습의-수학적-정의">1.1 학습의 수학적 정의</h3>
<p>딥러닝 모델은 다음과 같은 최적화 문제로 정의된다.</p>
<p>$$
\theta^* = \arg\min_\theta ; \mathbb{E}_{(x,y)\sim \mathcal{D}} \left[ L(f(x;\theta), y) \right]
$$</p>
<p>여기서:</p>
<ul>
<li>( $$\theta$$ ): 모델의 모든 파라미터 집합 (가중치, 바이어스)</li>
<li>( $$f(x;\theta)$$ ): 신경망이 정의하는 비선형 함수</li>
<li>( $$L(\cdot)$$ ): 손실 함수</li>
<li>( $$\mathcal{D}$$ ): 데이터 분포</li>
</ul>
<p>Convex(볼록) 함수는 하나의 완전한 최소점만 있지만, 딥러닝 손실 함수는 구불구불한 산과 계곡이 많아 여러 최소점이 존재합니다. 그래서 최적화가 훨씬 어렵다.
실제 학습은 <strong>비볼록(non-convex)</strong> 손실 공간에서의 수치적 최적화 과정이며, 이때 초기화와 최적화 알고리즘은 다음을 결정한다.</p>
<ul>
<li>어떤 영역에서 탐색을 시작하는가 (initial point)</li>
<li>탐색 경로가 얼마나 안정적인가 (conditioning)</li>
<li>국소 최소/안장점(saddle point)을 얼마나 잘 탈출하는가</li>
</ul>
<hr>
<h3 id="12-초기화와-최적화의-역할-분리">1.2 초기화와 최적화의 역할 분리</h3>
<table>
<thead>
<tr>
<th>요소</th>
<th>담당 역할</th>
</tr>
</thead>
<tbody><tr>
<td>가중치 초기화</td>
<td>신호 분산의 스케일 결정, 학습 가능성 확보</td>
</tr>
<tr>
<td>최적화 기법</td>
<td>손실 지형에서의 탐색 효율 및 수렴 속도</td>
</tr>
</tbody></table>
<p>초기화는 <strong>학습이 가능한 상태를 만들고</strong>,
최적화는 <strong>그 상태에서 얼마나 잘 학습할 수 있는지를 결정</strong>한다.</p>
<hr>
<h2 id="2-가중치-초기화-weight-initialization">2. 가중치 초기화 (Weight Initialization)</h2>
<h3 id="21-초기화-실패가-야기하는-구조적-문제">2.1 초기화 실패가 야기하는 구조적 문제</h3>
<h4 id="1-symmetry-breaking-실패">(1) Symmetry Breaking 실패</h4>
<p>만약 각 뉴런의 가중치가 전부 같으면, 입력이 아무리 다르더라도 뉴런의 출력이 같아지고 결국 학습이 분쇄되지 않는다. 모든 가중치를 0으로 초기화할 경우, 같은 층의 모든 뉴런은 다음을 공유한다.</p>
<ul>
<li>동일한 입력</li>
<li>동일한 출력</li>
<li>동일한 gradient</li>
</ul>
<p>결과적으로:
$$
\frac{\partial L}{\partial W_1}
=
\frac{\partial L}{\partial W_2}
=
\cdots
$$</p>
<p>→ 학습이 진행되어도 뉴런 간 역할 분화가 발생하지 않는다.
이는 <strong>표현력 붕괴(representational collapse)</strong>로 이어진다.</p>
<h4 id="2-gradient-vanishing--exploding의-근본-원인">(2) Gradient Vanishing / Exploding의 근본 원인</h4>
<p>은닉층 ( l )의 선형 변환을 다음과 같이 두자.</p>
<p>$$
z^{(l)} = W^{(l)} a^{(l-1)}
$$</p>
<p>만약:</p>
<ul>
<li>( $$\text{Var}(W^{(l)}) \gg 1$$ ) → 분산 폭주</li>
<li>( $$\text{Var}(W^{(l)}) \ll 1$$ ) → 분산 수축</li>
</ul>
<p>이면, 깊이에 따라 다음 현상이 반복적으로 누적된다.</p>
<p>$$
\text{Var}(z^{(l)}) = n \cdot \text{Var}(W^{(l)}) \cdot \text{Var}(a^{(l-1)})
$$</p>
<p>즉, 초기 분산 선택은 <strong>깊이 방향으로의 분산 전파를 제어하는 문제</strong>다.</p>
<p>활성화 함수의 출력이 특정 범위에 집중되면, 역전파될 때 그 기울기가 점점 작아져 신호가 사라지기도(=vanishing), 너무 커지기도(=exploding) 한다.</p>
<h3 id="22-분산-보존-관점에서의-초기화-원리">2.2 분산 보존 관점에서의 초기화 원리</h3>
<p>이상적인 초기화 조건은 다음 두 가지를 동시에 만족해야 한다.</p>
<ol>
<li><p><strong>Forward pass</strong>
$$
\text{Var}(a^{(l)}) \approx \text{Var}(a^{(l-1)})
$$</p>
</li>
<li><p><strong>Backward pass</strong>
$$
\text{Var}\left(\frac{\partial L}{\partial a^{(l)}}\right)
\approx
\text{Var}\left(\frac{\partial L}{\partial a^{(l-1)}}\right)
$$</p>
</li>
</ol>
<p>이는 초기화가 단순한 “시작값 설정”이 아니라,
<strong>네트워크 전체를 관통하는 신호 전달 시스템의 스케일링 문제</strong>임을 의미한다.</p>
<h3 id="23-xavier-initialization-glorot-initialization">2.3 Xavier Initialization (Glorot Initialization)</h3>
<h4 id="1-이론적-가정">(1) 이론적 가정</h4>
<ul>
<li>입력 ( $$x_i$$ )는 평균 0, 분산 ( $$\sigma^2$$ )</li>
<li>가중치 ( $$W_i$$ )는 독립이고 평균 0</li>
<li>활성화 함수는 선형 또는 약한 비선형 (sigmoid, tanh)</li>
</ul>
<p>이때,
$$
\text{Var}(z) = n \cdot \text{Var}(W) \cdot \text{Var}(x)
$$</p>
<p>이를 입력 분산과 동일하게 유지하려면:
$$
\text{Var}(W) = \frac{1}{n}
$$</p>
<h4 id="2-수식적-정의">(2) 수식적 정의</h4>
<ul>
<li><p>정규분포:
$$
W \sim \mathcal{N}\left(0, \frac{1}{n}\right)
$$</p>
</li>
<li><p>균등분포:
$$
W \sim U\left(-\sqrt{\frac{6}{n}}, \sqrt{\frac{6}{n}}\right)
$$</p>
</li>
</ul>
<h4 id="3-한계">(3) 한계</h4>
<ul>
<li>Sigmoid/Tanh의 포화 영역 문제는 완화하지만 제거하지는 못함</li>
<li>ReLU 계열에서는 활성화 분산이 절반으로 감소</li>
</ul>
<p>→ ReLU 기반 네트워크에서는 분산 손실 누적 발생</p>
<h3 id="24-he-initialization">2.4 He Initialization</h3>
<h4 id="1-relu-활성화의-통계적-특성">(1) ReLU 활성화의 통계적 특성</h4>
<p>ReLU는 다음과 같이 정의된다.
$$
\text{ReLU}(x) = \max(0, x)
$$</p>
<p>입력이 대칭 분포일 경우:</p>
<ul>
<li>출력의 약 50%가 0</li>
<li>분산이 약 절반으로 감소</li>
</ul>
<p>이를 보정하기 위해:
$$
\text{Var}(W) = \frac{2}{n}
$$</p>
<h4 id="2-정의">(2) 정의</h4>
<p>$$
W \sim \mathcal{N}\left(0, \frac{2}{n}\right)
$$</p>
<hr>
<h4 id="3-효과">(3) 효과</h4>
<ul>
<li>깊은 ReLU 네트워크에서 학습 안정성 확보</li>
<li>BatchNorm 없이도 초기 학습 가능</li>
</ul>
<h3 id="25-초기화-방법의-선택-기준">2.5 초기화 방법의 선택 기준</h3>
<table>
<thead>
<tr>
<th>조건</th>
<th>선택</th>
</tr>
</thead>
<tbody><tr>
<td>얕은 네트워크 + Sigmoid</td>
<td>Xavier</td>
</tr>
<tr>
<td>깊은 네트워크 + ReLU</td>
<td>He</td>
</tr>
<tr>
<td>BatchNorm 사용</td>
<td>초기화 민감도 감소</td>
</tr>
<tr>
<td>Transformer 계열</td>
<td>Xavier + LayerNorm</td>
</tr>
</tbody></table>
<hr>
<h2 id="3-최적화-optimization">3. 최적화 (Optimization)</h2>
<h3 id="31-경사하강법의-기하학적-해석">3.1 경사하강법의 기하학적 해석</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/7d79730e-5f1c-4b8a-ab00-e9eeb1dd1543/image.png" alt=""></p>
<p>경사하강법은 다음을 반복한다.</p>
<p>$$
W_{t+1} = W_t - \eta \nabla L(W_t)
$$</p>
<p>이는:</p>
<ul>
<li>손실 함수의 1차 테일러 근사에서</li>
<li>가장 빠르게 감소하는 방향(negative gradient)을 따라 이동</li>
</ul>
<p>한다는 의미다.</p>
<h3 id="32-학습률의-역할">3.2 학습률의 역할</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/b164e256-ef90-426e-b6df-4904873ea26e/image.png" alt=""></p>
<p>학습률(learning rate, ($$\eta$$))은 경사 하강법에서 <strong>파라미터가 한 번의 업데이트에서 얼마나 이동할지를 결정하는 스케일 계수</strong>이다.
기본적인 파라미터 업데이트 식은 다음과 같다.</p>
<p>$$\theta_{t+1} = \theta_t - \eta \nabla_\theta L(\theta_t)$$</p>
<p>여기서 gradient ($$\nabla_\theta L$$)는 손실을 가장 빠르게 감소시키는 <strong>방향</strong>을 제공하고,
학습률은 이 방향을 <strong>실제 파라미터 변화량으로 변환하는 크기</strong>를 결정한다.</p>
<h4 id="손실-지형-관점에서의-학습률">손실 지형 관점에서의 학습률</h4>
<p>손실 함수는 파라미터 공간에서 곡률(curvature)이 서로 다른 지형을 가진다.
학습률은 이러한 지형에 대해 다음과 같은 가정을 암묵적으로 포함한다.</p>
<blockquote>
<p>현재 위치에서의 손실 지형은 이 정도 이동을 허용할 만큼 완만하다.</p>
</blockquote>
<p>학습률이 이 가정과 맞지 않을 경우, 최적화는 불안정해진다.</p>
<h4 id="학습률이-너무-큰-경우">학습률이 너무 큰 경우</h4>
<p>학습률이 과도하게 크면 한 번의 업데이트가 최소점을 넘어가게 된다.
이로 인해 손실이 진동하거나 증가하며, 학습이 발산할 수 있다.
이는 손실 지형의 곡률에 비해 이동량이 지나치게 큰 경우에 발생한다.</p>
<h4 id="학습률이-너무-작은-경우">학습률이 너무 작은 경우</h4>
<p>학습률이 지나치게 작으면 파라미터가 매우 느리게 변화한다.
손실은 감소하지만, 평탄한 영역에서는 실질적인 학습이 거의 진행되지 않는다.
이 경우 이론적으로는 수렴 가능하더라도 실용적인 학습이 어렵다.</p>
<h4 id="초기화-및-최적화와의-관계">초기화 및 최적화와의 관계</h4>
<p>학습률은 모델의 초기화 및 손실 지형의 conditioning과 밀접한 관련이 있다.
적절한 초기화는 파라미터 전반에 걸쳐 gradient의 스케일을 균형 있게 만들어,
하나의 학습률로 안정적인 학습이 가능하도록 한다.</p>
<p>반대로 초기화가 부적절하면,
일부 파라미터는 과도하게 업데이트되고 다른 일부는 거의 업데이트되지 않아
학습률 선택이 매우 어려워진다.</p>
<h4 id="local-minima-국소-최소점">Local Minima (국소 최소점)</h4>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/57b9acc7-f7f1-4554-a7d9-8eb75c556986/image.png" alt=""></p>
<p>손실 함수 $$L(θ)$$를 파라미터 
𝜃 에 대해 최적화할 때, <strong>local minima(국소 최소점)</strong>란 다음 조건을 만족하는 지점을 의미한다.</p>
<p>어떤 작은 이웃 범위 내에서는 손실이 최소이지만,
전체 파라미터 공간에서의 최소값(global minimum)는 아닌 지점</p>
<p>즉, 주변으로는 더 낮은 방향이 없지만,
멀리 떨어진 곳에는 더 낮은 손실값이 존재할 수 있다.</p>
<h4 id="local-minima와-global-minima의-차이">Local minima와 Global minima의 차이</h4>
<p>Global minimum: 손실 함수 전체에서 가장 작은 값
Local minimum: 국소 영역에서만 가장 작은 값</p>
<p>딥러닝에서의 최적화는 일반적으로
global minimum을 보장하지 않으며,
실제로는 local minimum 또는 그 근방에서 수렴하는 경우가 많다.</p>
<h4 id="딥러닝에서-local-minima의-실제-영향">딥러닝에서 local minima의 실제 영향</h4>
<p>고전적인 비선형 최적화 문제와 달리, 고차원 딥러닝 모델에서는 다음과 같은 특성이 있다.</p>
<ul>
<li>파라미터 차원이 매우 큼</li>
<li>동일한 성능을 갖는 최소점이 다수 존재</li>
<li>성능이 크게 나쁘지 않은 local minimum이 많음</li>
</ul>
<p>이로 인해 딥러닝에서는 local minimum 자체가 치명적인 문제인 경우는 드물다
실제 학습 성능을 크게 저해하는 요인은 local minimum보다 saddle point(안장점) 인 경우가 많다.
<img src="https://velog.velcdn.com/images/you_n_sunny/post/51efe06f-ef13-4273-ac7f-ea0190fcc1ee/image.png" alt=""></p>
<h4 id="학습률-및-초기화와의-관계">학습률 및 초기화와의 관계</h4>
<p>학습률과 초기화는
어떤 local minimum으로 수렴하는지를 결정하는 중요한 요소이다.</p>
<p>초기화: 최적화가 시작되는 위치를 결정
학습률: 국소 지형을 탈출할 수 있는 이동 크기를 결정
학습률이 너무 작으면: 얕은 local minimum이나 평탄한 영역에 쉽게 머무름
학습률이 적절하면: 얕은 local minimum을 지나쳐 더 낮은 손실 영역으로 이동 가능</p>
<hr>
<h2 id="4-확률적-경사하강법-stochastic-gradient-descent-sgd">4. 확률적 경사하강법 (Stochastic Gradient Descent, SGD)</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/ed8afb68-5d17-4221-8d9b-094fcbd67038/image.png" alt=""></p>
<p>딥러닝 최적화 기법의 역사는 모두 SGD로부터 시작된다.
SGD는 가장 단순한 형태의 최적화 방법이지만, 이후 등장하는 Momentum, AdaGrad, RMSProp, Adam 등 모든 기법은 <strong>SGD가 가진 구조적 한계를 어떻게 극복할 것인가</strong>라는 문제의식에서 출발했다.</p>
<p>따라서 SGD는 단순한 “기본 방법”이 아니라, <strong>최적화 기법 진화의 기준점(reference)</strong> 이다.</p>
<h3 id="41-경사하강법의-기본-원리">4.1 경사하강법의 기본 원리</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/51cf4585-af99-47f5-9e9a-f5309835a860/image.png" alt=""></p>
<p>최적화의 목적은 손실 함수 ( L(W) )를 최소화하는 파라미터 ( W )를 찾는 것이다.
경사하강법은 현재 위치에서의 기울기(gradient)를 이용해 손실이 가장 빠르게 감소하는 방향으로 파라미터를 갱신한다.</p>
<p>$$W_{t+1} = W_t - \eta \nabla L(W_t)
$$
여기서</p>
<ul>
<li>( $$\eta$$ ) : 학습률(learning rate)</li>
<li>( $$\nabla L(W_t)$$ ) : 손실 함수의 기울기</li>
</ul>
<p>이 업데이트 식은 손실 함수를 현재 지점에서 <strong>1차 테일러 근사</strong>로 해석했을 때, 손실을 가장 빠르게 감소시키는 방향이 gradient의 반대 방향이라는 사실에 기반한다.</p>
<h3 id="42-batch-stochastic-mini-batch-gradient-descent">4.2 Batch, Stochastic, Mini-batch Gradient Descent</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/7dd677c9-0645-44e6-a4e3-25541cc71b3c/image.png" alt=""></p>
<p>이론적으로는 전체 데이터에 대한 정확한 gradient를 계산하는 것이 이상적이다. 그러나 딥러닝에서는 데이터 수가 매우 크기 때문에, 다음과 같은 근사 방식이 사용된다.</p>
<ul>
<li><strong>Batch Gradient Descent</strong></li>
</ul>
<p>$$\nabla L(W) = \frac{1}{N} \sum_{i=1}^{N} \nabla L_i(W)
$$
전체 데이터 (N)개를 사용하므로 안정적이지만, 계산 비용이 매우 크다.</p>
<ul>
<li><strong>Stochastic Gradient Descent (SGD)</strong></li>
</ul>
<p>$$\nabla L(W) \approx \nabla L_i(W)
$$
데이터 한 개로 gradient를 근사한다. 계산은 빠르지만, 업데이트의 분산이 크다.</p>
<ul>
<li><strong>Mini-batch Gradient Descent</strong></li>
</ul>
<p>$$\nabla L(W) \approx \frac{1}{M} \sum_{i=1}^{M} \nabla L_i(W)
$$
 계산 효율과 안정성 사이의 균형을 제공한다.</p>
<h3 id="43-딥러닝-손실-함수의-기하적-특성">4.3 딥러닝 손실 함수의 기하적 특성</h3>
<p>SGD의 한계를 이해하려면 딥러닝 손실 함수의 구조를 이해해야 한다.</p>
<p>딥러닝 손실 함수는 일반적으로 다음과 같은 특성을 가진다.</p>
<ul>
<li>고차원 파라미터 공간</li>
<li>Saddle point가 지역 최소점보다 훨씬 많음</li>
<li>방향마다 기울기 크기가 크게 다른 <strong>비등방성(non-isotropic)</strong> 구조</li>
</ul>
<p>이러한 손실 지형은 SGD에 매우 불리하다.</p>
<h3 id="44-sgd의-구조적-한계">4.4 SGD의 구조적 한계</h3>
<h4 id="1-지그재그-현상">(1) 지그재그 현상</h4>
<p>손실 함수가 좁고 긴 계곡 형태일 경우, 한 방향에서는 gradient가 크고 다른 방향에서는 매우 작다.
SGD는 큰 gradient 방향으로 과도하게 이동하고, 결과적으로 다음과 같은 비효율적인 경로를 따른다.</p>
<ul>
<li>진동(oscillation)</li>
<li>불필요한 이동 거리 증가</li>
</ul>
<h4 id="2-느린-수렴">(2) 느린 수렴</h4>
<p>완만한 방향의 gradient는 매우 작기 때문에, 해당 방향으로의 이동은 극도로 느려진다.
이는 학습 초반뿐 아니라, 최소점 근처에서도 수렴 속도를 크게 저하시킨다.</p>
<h4 id="3-전역-학습률의-한계">(3) 전역 학습률의 한계</h4>
<p>SGD는 모든 파라미터에 동일한 학습률 ( $$\eta$$ )를 적용한다. 그러나 실제로는:</p>
<ul>
<li>어떤 파라미터는 큰 업데이트가 필요</li>
<li>어떤 파라미터는 매우 작은 업데이트가 필요</li>
</ul>
<p>이로 인해 다음과 같은 딜레마가 발생한다.</p>
<ul>
<li>( $$\eta$$ )가 크면: 발산 위험</li>
<li>( $$\eta$$ )가 작으면: 학습 속도 저하</li>
</ul>
<p>SGD의 한계는 요약하면 다음 두 가지다.</p>
<ol>
<li><strong>방향 정보가 현재 gradient에만 의존</strong></li>
<li><strong>파라미터별 스케일 차이를 전혀 반영하지 못함</strong></li>
</ol>
<p>모던 최적화 기법들은 SGD가 가진 진동, 느린 수렴 문제를 보완하기 위해 등장했다.
이 문제를 해결하기 위한 첫 번째 시도가 바로 <strong>Momentum</strong>, 즉 <em>과거 gradient의 누적</em>이다.</p>
<hr>
<h2 id="5-momentum">5. Momentum</h2>
<p>SGD의 가장 큰 문제는 <strong>현재 순간의 gradient에만 의존한다</strong>는 점이다.
Momentum은 이 한계를 인식하고, 최적화 과정에 <strong>시간적 누적 정보(time history)</strong> 를 처음으로 도입한 방법이다.</p>
<p>즉, Momentum은 단순한 “속도 트릭”이 아니라,
<strong>gradient의 1차 모멘트(평균 방향)를 추정하려는 시도</strong>로 이해할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/46763ca4-c081-457f-b44f-4fb49db8e64b/image.png" alt=""></p>
<h3 id="51-왜-momentum이-필요한가">5.1 왜 Momentum이 필요한가</h3>
<p>SGD는 다음과 같은 구조적 문제를 가진다.</p>
<ol>
<li>비등방 손실 지형에서 지그재그 이동</li>
<li>완만한 방향으로의 매우 느린 수렴</li>
<li>gradient 노이즈에 민감한 업데이트</li>
</ol>
<p>이 문제들은 모두 <strong>gradient의 방향성이 불안정하다</strong>는 점에서 비롯된다.
Momentum은 이 문제를 “과거의 gradient를 기억하자”는 발상으로 해결하고자 한다.</p>
<h3 id="52-momentum의-핵심-아이디어">5.2 Momentum의 핵심 아이디어</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/85197f1d-9e08-4793-abb4-e96701eb1493/image.png" alt=""></p>
<p>Momentum은 현재 gradient뿐만 아니라, <strong>과거 gradient의 누적 방향</strong>을 함께 고려한다.</p>
<p>이를 위해 새로운 변수인 <strong>속도(velocity)</strong> 를 도입한다.</p>
<p>$$\begin{aligned}
v_t &amp;= \alpha v_{t-1} - \eta \nabla L(W_t) <br>\end{aligned}
$$
$$\begin{aligned}
W_{t+1} &amp;= W_t + v_t
\end{aligned}
$$
여기서</p>
<ul>
<li>( $$v_t$$ ): 누적된 이동 방향 (velocity)</li>
<li>( $$\alpha \in [0,1)$$ ): 관성 계수 (보통 0.9)</li>
</ul>
<h3 id="53-수식적-해석">5.3 수식적 해석</h3>
<p>Momentum 업데이트를 전개해 보면 다음과 같다.</p>
<p>$$v_t = -\eta \sum_{k=0}^{t} \alpha^{t-k} \nabla L(W_k)
$$
즉, Momentum은 <strong>과거 gradient들의 지수 이동 평균(EMA)</strong> 을 사용한다.</p>
<ul>
<li>최근 gradient일수록 더 큰 가중치</li>
<li>오래된 gradient는 점점 영향 감소</li>
</ul>
<p>이 관점에서 보면 Momentum은 다음을 수행한다.</p>
<blockquote>
<p>“현재 gradient의 잡음을 줄이고, 일관된 하강 방향을 추정한다.”</p>
</blockquote>
<h3 id="54-기하학적-관점에서의-효과">5.4 기하학적 관점에서의 효과</h3>
<p>Momentum은 손실 지형에서 다음과 같은 효과를 만든다.</p>
<ul>
<li>진동이 심한 방향 → 상쇄됨</li>
<li>일관된 방향 → 가속됨</li>
</ul>
<p>즉,</p>
<ul>
<li>좁고 긴 계곡에서는 <strong>중심 방향으로 빠르게 이동</strong></li>
<li>지그재그 경로가 완화됨</li>
</ul>
<p>이는 SGD가 갖던 <strong>비효율적인 경로 문제</strong>를 크게 개선한다.</p>
<h3 id="55-momentum의-한계">5.5 Momentum의 한계</h3>
<p>Momentum은 분명한 진전이지만, 여전히 중요한 한계를 가진다.</p>
<h4 id="1-현재-위치에서의-gradient만-사용">(1) 현재 위치에서의 gradient만 사용</h4>
<p>Momentum은 이동 방향을 누적하지만, <strong>gradient 계산 자체는 항상 현재 위치에서 수행</strong>된다.
이로 인해 다음 문제가 발생한다.</p>
<ul>
<li>관성으로 인해 최소점을 지나쳐 버림 (overshooting)</li>
<li>최적점 근처에서 불안정한 진동</li>
</ul>
<h4 id="2-학습률-문제는-여전히-해결되지-않음">(2) 학습률 문제는 여전히 해결되지 않음</h4>
<ul>
<li>모든 파라미터에 동일한 ( $$\eta$$ ) 적용</li>
<li>파라미터별 스케일 차이 미반영</li>
</ul>
<p>즉, Momentum은 <strong>방향성 문제는 완화</strong>했지만,
<strong>“지금 어디로 가고 있는가”를 미리 고려하지는 못한다.</strong></p>
<p>Momentum의 한계는 다음 질문으로 이어진다.</p>
<blockquote>
<p>“관성으로 이동할 것을 알고 있다면,
<strong>그 위치에서의 gradient를 미리 계산할 수는 없을까?</strong>”</p>
</blockquote>
<p>이 질문에 대한 해답이 바로 <strong>Nesterov Accelerated Gradient (NAG)</strong> 이다.</p>
<hr>
<h2 id="6-nesterov-accelerated-gradient-nag">6. Nesterov Accelerated Gradient (NAG)</h2>
<p>Momentum은 SGD의 지그재그 문제를 효과적으로 완화했지만, 새로운 한계를 드러냈다.
바로 <strong>관성으로 인해 최소점을 지나쳐 버리는 현상(overshooting)</strong> 이다.</p>
<p>Nesterov Accelerated Gradient(NAG)는 이 문제를 다음과 같은 질문으로 재정의한다.</p>
<blockquote>
<p>“어차피 관성으로 이동할 예정이라면,
<strong>그 이동한 위치에서의 gradient를 미리 계산하면 어떨까?</strong>”</p>
</blockquote>
<p>이 질문이 Nesterov의 출발점이다.</p>
<h3 id="61-momentum의-근본적-한계-재해석">6.1 Momentum의 근본적 한계 재해석</h3>
<p>Momentum 업데이트는 다음과 같다.</p>
<p>$$\begin{aligned}
v_t &amp;= \alpha v_{t-1} - \eta \nabla L(W_t) <br>\end{aligned}
$$</p>
<p>$$\begin{aligned}
W_{t+1} &amp;= W_t + v_t
\end{aligned}
$$
이 식의 문제는 명확하다.</p>
<ul>
<li>실제로는 ( $$W_t$$ )에서 gradient를 계산하지만</li>
<li>파라미터는 ( $$W_t + \alpha v_{t-1}$$ ) 방향으로 이동하게 됨</li>
</ul>
<p>즉, <strong>gradient를 계산한 위치와 실제 이동 방향이 불일치</strong>한다.</p>
<p>이 불일치는 다음 현상을 유발한다.</p>
<ul>
<li>최소점 근처에서 과도한 이동</li>
<li>불필요한 진동</li>
<li>수렴 지연</li>
</ul>
<h3 id="62-nesterov의-핵심-아이디어-look-ahead-gradient">6.2 Nesterov의 핵심 아이디어: Look-ahead Gradient</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/1a0ec434-9fe4-42da-a8d1-6aafdfb025bf/image.png" alt=""></p>
<p>Nesterov는 gradient를 계산하기 전에 <strong>한 발 앞서 이동</strong>한다.</p>
<p>$$W&#39;<em>t = W_t + \alpha v</em>{t-1}
$$
그리고 이 미래 위치에서 gradient를 계산한다.</p>
<p>$$\begin{aligned}
v_t &amp;= \alpha v_{t-1} - \eta \nabla L(W&#39;<em>t) <br>\end{aligned}
$$
$$\begin{aligned}
W</em>{t+1} &amp;= W_t + v_t
\end{aligned}
$$
즉,</p>
<blockquote>
<p>“이동한 다음에 어떤 경사가 기다리고 있는지를 먼저 본다.”</p>
</blockquote>
<p>이것이 Nesterov의 본질이다.</p>
<h3 id="63-수식적-관점-왜-overshooting이-줄어드는가">6.3 수식적 관점: 왜 overshooting이 줄어드는가</h3>
<p>Nesterov는 관성으로 이동한 위치에서 gradient를 계산하므로 다음 효과가 발생한다.</p>
<ul>
<li><p>만약 최소점에 가까워졌다면
→ gradient 크기가 작아짐
→ 업데이트가 자동으로 완화됨</p>
</li>
<li><p>만약 잘못된 방향으로 가고 있다면
→ 미래 위치에서 gradient가 즉시 이를 감지
→ 보정 방향으로 업데이트</p>
</li>
</ul>
<p>즉, Nesterov는 <strong>사전 보정(pre-correction)</strong> 을 수행한다.</p>
<h3 id="64-momentum-vs-nesterov-구조적-비교">6.4 Momentum vs Nesterov: 구조적 비교</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>Momentum</th>
<th>Nesterov</th>
</tr>
</thead>
<tbody><tr>
<td>Gradient 계산 위치</td>
<td>현재 위치 (W_t)</td>
<td>미래 위치 (W_t + \alpha v_{t-1})</td>
</tr>
<tr>
<td>Overshooting</td>
<td>발생 가능</td>
<td>크게 감소</td>
</tr>
<tr>
<td>수렴 안정성</td>
<td>중간</td>
<td>높음</td>
</tr>
<tr>
<td>계산 비용</td>
<td>낮음</td>
<td>거의 동일</td>
</tr>
</tbody></table>
<p>중요한 점은, <strong>계산 비용은 거의 증가하지 않으면서</strong> 수렴 특성이 개선된다는 것이다.</p>
<h3 id="65-이론적-관점에서의-의의">6.5 이론적 관점에서의 의의</h3>
<p>Nesterov Accelerated Gradient는 단순한 휴리스틱이 아니다.</p>
<ul>
<li>볼록 최적화(convex optimization) 이론에서</li>
<li><strong>최적의 수렴률(optimal convergence rate)</strong> 을 가지는 방법으로 증명됨</li>
</ul>
<p>이 때문에 “Accelerated”라는 이름이 붙었다.</p>
<h3 id="66-여전히-남아-있는-문제">6.6 여전히 남아 있는 문제</h3>
<p>Nesterov는 Momentum의 방향성 문제를 상당 부분 해결했지만, 여전히 근본적인 한계를 가진다.</p>
<h4 id="1-학습률은-여전히-전역적이다">(1) 학습률은 여전히 전역적이다</h4>
<ul>
<li>모든 파라미터에 동일한 ( $$\eta$$ )</li>
<li>파라미터별 스케일 차이 미반영</li>
</ul>
<h4 id="2-gradient-크기-정보는-직접적으로-사용하지-않는다">(2) gradient 크기 정보는 직접적으로 사용하지 않는다</h4>
<ul>
<li>방향은 개선되었으나</li>
<li>“얼마나 크게 가야 하는가”는 여전히 고정 규칙</li>
</ul>
<p>이제 질문은 다음으로 넘어간다.</p>
<blockquote>
<p>“방향만이 아니라,
<strong>파라미터마다 다른 학습률을 쓸 수는 없을까?</strong>”</p>
</blockquote>
<hr>
<h2 id="7-adagrad">7. AdaGrad</h2>
<p>Momentum과 Nesterov는 SGD의 <strong>방향성 문제</strong>를 상당 부분 해결했다.
그러나 여전히 하나의 근본적인 가정이 남아 있었다.</p>
<blockquote>
<p>“모든 파라미터는 같은 크기의 step으로 업데이트되어도 괜찮다.”</p>
</blockquote>
<p>AdaGrad는 이 가정을 처음으로 명시적으로 부정한 방법이다.
즉, AdaGrad는 <strong>‘방향’이 아니라 ‘스케일’ 문제를 해결하려는 시도</strong>다.</p>
<h3 id="71-문제-인식-왜-전역-학습률은-부적절한가">7.1 문제 인식: 왜 전역 학습률은 부적절한가</h3>
<p>딥러닝 모델의 파라미터들은 동일한 성격을 가지지 않는다.</p>
<ul>
<li>어떤 파라미터는 자주 업데이트됨</li>
<li>어떤 파라미터는 매우 드물게 업데이트됨 (희소 특징)</li>
</ul>
<p>예를 들어:</p>
<ul>
<li>NLP에서의 희소 단어 벡터</li>
<li>추천 시스템에서의 희소 사용자/아이템 특성</li>
</ul>
<p>이 경우 전역 학습률 ( $$\eta$$ )는 다음과 같은 문제를 만든다.</p>
<ul>
<li>자주 등장하는 파라미터 → 과도한 업데이트</li>
<li>드물게 등장하는 파라미터 → 학습이 거의 되지 않음</li>
</ul>
<p>AdaGrad는 다음 질문에서 출발한다.</p>
<blockquote>
<p>“<strong>파라미터마다 다른 학습률을 쓸 수는 없을까?</strong>”</p>
</blockquote>
<h3 id="72-adagrad의-핵심-아이디어">7.2 AdaGrad의 핵심 아이디어</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/46f78f3c-cb6c-4ef0-b354-51732a97833c/image.png" alt=""></p>
<p>AdaGrad는 각 파라미터에 대해 <strong>과거 gradient의 크기 정보를 누적</strong>한다.</p>
<p>각 파라미터 ( i )에 대해:</p>
<p>$$\begin{aligned}
h_{t,i} &amp;= h_{t-1,i} + g_{t,i}^2 <br>w_{t+1,i} &amp;= w_{t,i} - \frac{\eta}{\sqrt{h_{t,i}} + \epsilon} g_{t,i}
\end{aligned}
$$
여기서</p>
<ul>
<li>( $$g_{t,i} = \frac{\partial L}{\partial w_{t,i}}$$ )</li>
<li>( $$h_{t,i}$$): 해당 파라미터의 gradient 제곱 누적합</li>
</ul>
<h3 id="73-수식적-의미-자동-학습률-감소">7.3 수식적 의미: 자동 학습률 감소</h3>
<p>AdaGrad 업데이트를 해석하면 다음과 같다.</p>
<ul>
<li>( $$h_{t,i}$$ )가 커질수록
→ 분모가 커짐
→ 해당 파라미터의 유효 학습률 감소</li>
</ul>
<p>즉,</p>
<p>$$\text{effective learning rate}<em>i = \frac{\eta}{\sqrt{h</em>{t,i}}}
$$
이 구조는 다음 효과를 만든다.</p>
<ul>
<li>자주 업데이트되는 파라미터 → 학습률 자동 감소</li>
<li>드물게 업데이트되는 파라미터 → 상대적으로 큰 학습률 유지</li>
</ul>
<p>이는 희소 특징 학습에 매우 유리하다.</p>
<h3 id="74-기하학적-관점-대각선-hessian-근사">7.4 기하학적 관점: 대각선 Hessian 근사</h3>
<p>AdaGrad는 암묵적으로 다음을 수행한다.</p>
<ul>
<li>손실 함수의 곡률(curvature)을 파라미터별로 추정</li>
<li>Hessian의 <strong>대각 성분만 근사적으로 반영</strong></li>
</ul>
<p>즉, AdaGrad는 다음과 같이 해석할 수 있다.</p>
<blockquote>
<p>“기울기가 자주 크던 방향은 조심스럽게,
거의 변하지 않던 방향은 과감하게 움직이자.”</p>
</blockquote>
<p>이는 SGD + Momentum 계열과 명확히 다른 관점이다.</p>
<h3 id="75-adagrad의-결정적-한계">7.5 AdaGrad의 결정적 한계</h3>
<p>AdaGrad는 개념적으로 매우 강력했지만, 실무에서는 치명적인 문제가 드러났다.</p>
<h4 id="1-누적합의-비가역성">(1) 누적합의 비가역성</h4>
<p>$$h_{t,i} = \sum_{k=1}^{t} g_{k,i}^2
$$</p>
<ul>
<li>한 번 커진 ( $$h_{t,i}$$ )는 절대 줄어들지 않음</li>
<li>학습이 진행될수록 분모가 계속 증가</li>
</ul>
<p>결과적으로:</p>
<ul>
<li>학습률이 점점 0에 수렴</li>
<li>아직 수렴하지 않은 지점에서도 업데이트가 사실상 멈춤</li>
</ul>
<h4 id="2-비정상non-stationary-환경에-부적합">(2) 비정상(non-stationary) 환경에 부적합</h4>
<p>딥러닝 학습에서는:</p>
<ul>
<li>초반과 후반의 gradient 특성이 크게 다름</li>
</ul>
<p>AdaGrad는 <strong>아주 오래된 gradient까지 동일하게 기억</strong>하므로,
현재 상황을 제대로 반영하지 못한다.</p>
<p>AdaGrad의 실패는 다음 질문으로 이어진다.</p>
<blockquote>
<p>“과거 gradient를 모두 기억할 필요가 있을까?”
“<strong>최근 gradient에 더 집중할 수는 없을까?</strong>”</p>
</blockquote>
<p>이 질문에 대한 해답이 바로 <strong>RMSProp</strong>이다.</p>
<ul>
<li>누적합 → 지수 이동 평균</li>
<li>학습률이 0으로 붕괴되는 문제 해결</li>
</ul>
<hr>
<h2 id="8-rmsprop">8. RMSProp</h2>
<p>AdaGrad는 파라미터별 학습률이라는 중요한 돌파구를 열었지만,
<strong>“과거 gradient를 모두 기억한다”</strong>는 설계 때문에 학습이 조기에 멈추는 치명적인 한계를 드러냈다.</p>
<p>RMSProp은 이 문제를 다음과 같이 재정의한다.</p>
<blockquote>
<p>“과거 gradient가 중요하긴 한데,
<strong>모두 동일하게 중요할 필요는 있지 않다.</strong>”</p>
</blockquote>
<p>즉, RMSProp은 <strong>비정상(non-stationary) 환경에서의 통계적 추정 문제</strong>로 최적화를 다시 바라본다.</p>
<h3 id="81-adagrad-실패의-본질적-원인">8.1 AdaGrad 실패의 본질적 원인</h3>
<p>AdaGrad의 누적 변수는 다음과 같다.</p>
<p>$$h_{t,i} = \sum_{k=1}^{t} g_{k,i}^2
$$
이 식은 다음 특성을 가진다.</p>
<ul>
<li>시간 (t)가 증가할수록 단조 증가</li>
<li>초반의 gradient와 후반의 gradient를 동일하게 취급</li>
<li>현재 손실 지형의 변화 반영 불가</li>
</ul>
<p>즉, AdaGrad는 <strong>시간에 따라 변하는 gradient 분포</strong>를 전혀 고려하지 않는다.</p>
<h3 id="82-rmsprop의-핵심-아이디어">8.2 RMSProp의 핵심 아이디어</h3>
<p>RMSProp은 AdaGrad의 누적합을 <strong>지수 이동 평균(Exponential Moving Average, EMA)</strong> 으로 대체한다.</p>
<p>$$\begin{aligned}
h_{t,i} &amp;= \rho h_{t-1,i} + (1 - \rho) g_{t,i}^2 <br>\end{aligned}
$$</p>
<p>$$\begin{aligned}
w_{t+1,i} &amp;= w_{t,i} - \frac{\eta}{\sqrt{h_{t,i}} + \epsilon} g_{t,i}
\end{aligned}
$$
여기서</p>
<ul>
<li>( $$\rho$$ ): decay rate (보통 0.9 또는 0.99)</li>
</ul>
<h3 id="83-수식적-의미-ema로서의-분산-추정">8.3 수식적 의미: EMA로서의 분산 추정</h3>
<p>RMSProp의 ( h_{t,i} )는 다음과 같이 해석할 수 있다.</p>
<p>$$h_{t,i} \approx \mathbb{E}[g_i^2]$$
즉, RMSProp은 각 파라미터에 대해</p>
<ul>
<li>gradient 제곱의 <strong>이동 평균</strong></li>
<li>gradient 분산(scale)에 대한 추정치</li>
</ul>
<p>를 유지한다.</p>
<p>이로 인해 다음 효과가 발생한다.</p>
<ul>
<li>과거의 오래된 gradient 영향 감소</li>
<li>최근 손실 지형에 빠르게 적응</li>
</ul>
<h3 id="84-adagrad-대비-개선점">8.4 AdaGrad 대비 개선점</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>AdaGrad</th>
<th>RMSProp</th>
</tr>
</thead>
<tbody><tr>
<td>과거 gradient 처리</td>
<td>전부 누적</td>
<td>지수적으로 감쇠</td>
</tr>
<tr>
<td>학습률 붕괴</td>
<td>발생</td>
<td>방지</td>
</tr>
<tr>
<td>비정상 환경 적응</td>
<td>불가</td>
<td>가능</td>
</tr>
<tr>
<td>실무 안정성</td>
<td>낮음</td>
<td>높음</td>
</tr>
</tbody></table>
<p>즉, RMSProp은 <strong>“너무 많은 기억”이 문제였음을 정확히 짚고 보완</strong>한 방법이다.</p>
<h3 id="85-rmsprop의-한계">8.5 RMSProp의 한계</h3>
<p>RMSProp은 스케일 문제를 상당히 잘 해결했지만, 여전히 중요한 정보가 빠져 있다.</p>
<h4 id="1-방향성-정보의-부재">(1) 방향성 정보의 부재</h4>
<ul>
<li>RMSProp은 gradient 크기(scale)만 고려</li>
<li>Momentum처럼 방향의 일관성은 반영하지 않음</li>
</ul>
<p>즉, RMSProp은 다음 질문에 답하지 못한다.</p>
<blockquote>
<p>“어디로 가야 하는지는 알겠는데,
<strong>그 방향이 일관된 방향인가?</strong>”</p>
</blockquote>
<h4 id="2-노이즈에-대한-취약성">(2) 노이즈에 대한 취약성</h4>
<ul>
<li>분산은 조절하지만</li>
<li>방향 자체는 여전히 noisy한 gradient에 의존</li>
</ul>
<ul>
<li>Momentum / Nesterov → <strong>방향 문제 해결</strong></li>
<li>AdaGrad / RMSProp → <strong>스케일 문제 해결</strong></li>
</ul>
<blockquote>
<p>“방향과 스케일을 <strong>동시에</strong> 다룰 수는 없을까?”</p>
</blockquote>
<hr>
<h2 id="9-adam-adaptive-moment-estimation">9. Adam (Adaptive Moment Estimation)</h2>
<p>Adam은 하나의 새로운 아이디어라기보다, 지금까지 등장한 최적화 기법들이 해결하려 했던 문제들을 하나의 수식 체계로 통합한 결과물이다. SGD가 가진 느린 수렴과 불안정성, Momentum 계열이 해결한 방향성 문제, AdaGrad와 RMSProp이 다룬 파라미터별 스케일 문제는 모두 Adam이라는 프레임 안에서 동시에 고려된다.</p>
<p>즉, Adam은 다음과 같은 질문에 대한 종합적인 해답이다.</p>
<blockquote>
<p>“경사는 어느 방향으로 가야 하는가?”
“그 방향으로 얼마나 크게 이동해야 하는가?”</p>
</blockquote>
<p>Adam은 이 두 질문을 <strong>gradient 자체가 아니라, gradient의 통계량을 추정하는 문제</strong>로 재정의한다.</p>
<h3 id="91-모멘트-추정">9.1 모멘트 추정</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/0599238b-6b1a-4a32-89f4-e909313dc055/image.png" alt=""></p>
<p>Adam의 핵심은 각 파라미터에 대해 두 가지 통계량을 유지하는 것이다.</p>
<p>첫째는 <strong>1차 모멘트</strong>, 즉 gradient의 평균이다. 이는 Momentum이 하던 역할과 동일하며, 업데이트 방향의 일관성을 확보하는 역할을 한다.</p>
<p>둘째는 <strong>2차 모멘트</strong>, 즉 gradient 제곱의 평균이다. 이는 RMSProp에서 도입된 개념으로, 파라미터별 gradient 크기(스케일)를 추정한다.</p>
<p>이 두 모멘트는 모두 지수 이동 평균(Exponential Moving Average, EMA)으로 계산된다. 시간 ( t )에서 gradient를 ( $$g_t = \nabla L(W_t)$$ )라 하면,</p>
<p>$$
m_t = \beta_1 m_{t-1} + (1 - \beta_1) g_t
$$</p>
<p>$$
v_t = \beta_2 v_{t-1} + (1 - \beta_2) g_t^2
$$</p>
<p>여기서 ( $$\beta_1$$ )은 보통 0.9, ( $$\beta_2$$ )는 0.999로 설정된다. 이 선택은 최근 gradient의 방향 정보는 비교적 빠르게 반영하고, 분산 정보는 더 안정적으로 추정하겠다는 의미를 담고 있다.</p>
<h3 id="92-바이어스-보정이-필요한-이유">9.2 바이어스 보정이 필요한 이유</h3>
<p>Adam의 모멘트 변수 ( $$m_t, v_t$$ )는 0으로 초기화된다. 이로 인해 학습 초반에는 EMA 특성상 추정치가 실제보다 작게 계산되는 <strong>초기 바이어스(bias)</strong> 가 발생한다.</p>
<p>이를 보정하지 않으면 초반 학습 단계에서 업데이트가 과도하게 작아지고, Adam의 장점이 충분히 발휘되지 않는다.</p>
<p>이를 해결하기 위해 Adam은 다음과 같은 바이어스 보정식을 적용한다.</p>
<p>$$
\hat{m}_t = \frac{m_t}{1 - \beta_1^t}, \quad
\hat{v}_t = \frac{v_t}{1 - \beta_2^t}
$$</p>
<p>이 보정은 단순한 트릭이 아니라, Adam이 <strong>통계적으로 의미 있는 모멘트 추정기</strong>임을 보여주는 중요한 요소다.</p>
<h3 id="93-adam의-최종-업데이트-식">9.3 Adam의 최종 업데이트 식</h3>
<p>바이어스가 보정된 모멘트를 이용한 Adam의 파라미터 업데이트 식은 다음과 같다.</p>
<p>$$
W_{t+1} = W_t - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}
$$</p>
<p>이 수식 하나에는 지금까지의 최적화 기법이 해결해 온 핵심 문제가 모두 녹아 있다.</p>
<ul>
<li>Momentum을 통한 방향 안정성</li>
<li>RMSProp을 통한 파라미터별 스케일 조절</li>
<li>비정상적인 손실 지형에 대한 적응</li>
<li>학습 초반의 불안정성 완화</li>
</ul>
<p>이 때문에 Adam은 실무에서 “기본값(default optimizer)”으로 널리 사용된다.</p>
<h3 id="94-adam의-구조적-한계-weight-decay-문제">9.4 Adam의 구조적 한계: Weight Decay 문제</h3>
<p>Adam의 학습 속도와 안정성에도 불구하고, 실무에서는 한 가지 중요한 문제가 관찰된다.
바로 <strong>정규화(regularization)가 의도한 방식으로 동작하지 않을 수 있다</strong>는 점이다.</p>
<p>일반적으로 L2 정규화는 손실 함수에 다음 항을 추가하는 방식으로 구현된다.</p>
<p>$$
L&#39;(W) = L(W) + \lambda |W|^2
$$</p>
<p>이 경우 gradient는</p>
<p>$$
g_t = \nabla L(W_t) + \lambda W_t
$$</p>
<p>가 된다.</p>
<p>SGD에서는 이 항이 모든 파라미터를 동일한 비율로 줄이는 <strong>균등한 수축(shrinkage)</strong> 으로 작동한다. 그러나 Adam에서는 gradient가 모멘트와 분산으로 변환되기 때문에, 이 정규화 항마저도 파라미터별로 서로 다른 스케일을 갖게 된다.</p>
<p>그 결과, Adam에서의 weight decay는
“모든 가중치를 일정 비율로 줄인다”는 원래의 의미를 잃고,
<strong>gradient 통계에 따라 왜곡된 형태로 적용</strong>된다.</p>
<p>이는 Adam이 훈련 데이터에서는 빠르게 수렴하지만, 테스트 성능에서는 불안정해지는 현상의 주요 원인 중 하나로 지목된다.</p>
<h3 id="95-adamw-최적화와-정규화의-분리">9.5 AdamW: 최적화와 정규화의 분리</h3>
<p>AdamW는 이 문제를 매우 단순한 질문으로 환원한다.</p>
<blockquote>
<p>“Weight decay는 최적화 문제가 아니라,
파라미터 크기를 제어하는 규칙 아닌가?”</p>
</blockquote>
<p>이에 따라 AdamW는 <strong>gradient 기반 업데이트와 weight decay를 완전히 분리</strong>한다.</p>
<p>먼저 Adam 방식으로 파라미터를 업데이트하고,</p>
<p>$$
W_{t+\frac{1}{2}} = W_t - \eta \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon}
$$</p>
<p>그 다음, gradient와 무관하게 weight decay를 적용한다.</p>
<p>$$W_{t+1} = W_{t+\frac{1}{2}} - \eta \lambda W_t$$</p>
<p>이렇게 함으로써 AdamW는
Adam의 빠른 수렴 특성과 SGD에서 의도한 정규화 효과를 동시에 유지한다.</p>
<hr>
<h2 id="10-초기화와-최적화의-상호작용">10. 초기화와 최적화의 상호작용</h2>
<p>딥러닝 학습은 흔히 “좋은 모델 구조 + 좋은 optimizer”의 문제로 단순화되곤 한다. 그러나 실제로 학습의 성패를 좌우하는 것은 <strong>초기화와 최적화가 함께 만들어내는 학습 동역학(dynamics)</strong> 이다.</p>
<p>이 챕터에서는 지금까지 다룬 내용을 바탕으로, 초기화와 최적화가 어떻게 상호작용하는지, 그리고 실무에서 어떤 기준으로 최적화 기법을 선택해야 하는지를 정리한다.</p>
<h3 id="101-초기화와-최적화는-독립적인-문제가-아니다">10.1 초기화와 최적화는 독립적인 문제가 아니다</h3>
<p>가중치 초기화는 단순히 “시작점”을 정하는 문제가 아니다.
초기화는 <strong>학습 초반의 손실 지형과 gradient 분포를 결정</strong>하고, 이는 곧 optimizer가 어떤 정보를 보게 되는지를 결정한다.</p>
<p>예를 들어,</p>
<ul>
<li><p>잘못된 초기화
→ 활성화 값 포화
→ gradient vanishing
→ 어떤 optimizer를 써도 학습 불가</p>
</li>
<li><p>Xavier / He 초기화
→ 층별 분산 안정
→ gradient 신호 유지
→ optimizer가 제 역할을 수행할 수 있는 환경 제공</p>
</li>
</ul>
<p>즉, 초기화는 optimizer의 성능을 <strong>발휘 가능하게 만드는 전제 조건</strong>이다.</p>
<h3 id="102-sgd-계열은-언제-사용하면-좋은가">10.2 SGD 계열은 언제 사용하면 좋은가</h3>
<p>SGD와 그 변형(Momentum, Nesterov)은 구조적으로 단순하다.</p>
<p>$$
W_{t+1} = W_t - \eta \nabla L(W_t)
$$</p>
<p>또는 방향성을 보완한 형태일 뿐이다. 이 단순함은 다음과 같은 상황에서 강점으로 작용한다.</p>
<h4 id="sgd-계열이-유리한-경우">SGD 계열이 유리한 경우</h4>
<ul>
<li>데이터가 충분히 많고</li>
<li>학습 시간이 상대적으로 길어도 괜찮으며</li>
<li>일반화 성능이 중요한 경우</li>
</ul>
<p>SGD는 gradient의 노이즈를 그대로 유지한다. 이 노이즈는 단점처럼 보이지만, 실제로는 <strong>날카로운(local sharp) 최소점에 빠지는 것을 방지</strong>하고, 더 평평한(flat) 최소점으로 이동하게 만든다.</p>
<p>이 때문에 다음과 같은 경험적 관찰이 자주 성립한다.</p>
<blockquote>
<p>“SGD는 느리게 학습되지만, 테스트 성능은 더 좋다.”</p>
</blockquote>
<h3 id="103-adam-계열은-언제-사용하면-좋은가">10.3 Adam 계열은 언제 사용하면 좋은가</h3>
<p>Adam과 AdamW는 다음과 같은 환경에서 특히 강력하다.</p>
<ul>
<li>학습 초반이 불안정한 경우</li>
<li>gradient 스케일이 파라미터마다 크게 다른 경우</li>
<li>비정상(non-stationary) 손실 지형</li>
<li>빠른 수렴이 중요한 경우</li>
</ul>
<p>Adam 계열은 파라미터별로 학습률을 자동 조절하고, 방향성과 스케일을 동시에 고려한다. 그 결과, 하이퍼파라미터 튜닝 부담이 낮고, <strong>“일단 잘 돌아가는 모델”을 빠르게 얻을 수 있다</strong>.</p>
<p>그래서 실무에서는 흔히 다음 전략을 취한다.</p>
<ul>
<li>초기 실험 → Adam / AdamW</li>
<li>모델 검증 후 → SGD 계열로 전환 여부 검토</li>
</ul>
<h3 id="104-왜-adamw가-기본값이-되었는가">10.4 왜 AdamW가 기본값이 되었는가</h3>
<p>앞 장에서 보았듯이, AdamW는 Adam의 구조적 문제였던 weight decay 왜곡을 해결한다. 이는 단순한 성능 개선이 아니라, <strong>최적화와 정규화의 역할을 명확히 분리</strong>했다는 점에서 의미가 크다.</p>
<p>현재의 딥러닝 학습 환경은 다음과 같은 특성을 가진다.</p>
<ul>
<li>매우 깊은 모델</li>
<li>LayerNorm, Residual Connection 등으로 gradient 흐름이 복잡</li>
<li>대규모 데이터와 배치</li>
</ul>
<p>이 환경에서는 AdamW의 다음 특성이 특히 중요하다.</p>
<ul>
<li>안정적인 초기 학습</li>
<li>명확한 정규화 해석</li>
<li>큰 모델에서도 일관된 동작</li>
</ul>
<p>이로 인해 AdamW는 현재 사실상의 표준(default optimizer)로 자리 잡았다.</p>
<h3 id="105-선택-가이드">10.5 선택 가이드</h3>
<table>
<thead>
<tr>
<th>상황</th>
<th>권장 선택</th>
</tr>
</thead>
<tbody><tr>
<td>빠른 프로토타이핑</td>
<td>Adam / AdamW</td>
</tr>
<tr>
<td>대규모 모델, Transformer</td>
<td>AdamW</td>
</tr>
<tr>
<td>일반화 성능 최우선</td>
<td>SGD + Momentum</td>
</tr>
<tr>
<td>충분한 데이터, 긴 학습</td>
<td>SGD 계열</td>
</tr>
<tr>
<td>하이퍼파라미터 튜닝 최소화</td>
<td>Adam 계열</td>
</tr>
</tbody></table>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/3176ab89-802b-4a77-9bb8-e08e24af0759/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 퍼셉트론과 다층 퍼셉트론 + 순전파 역전파]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%ED%8D%BC%EC%85%89%ED%8A%B8%EB%A1%A0%EA%B3%BC-%EB%8B%A4%EC%B8%B5-%ED%8D%BC%EC%85%89%ED%8A%B8%EB%A1%A0</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%ED%8D%BC%EC%85%89%ED%8A%B8%EB%A1%A0%EA%B3%BC-%EB%8B%A4%EC%B8%B5-%ED%8D%BC%EC%85%89%ED%8A%B8%EB%A1%A0</guid>
            <pubDate>Mon, 12 Jan 2026 14:19:07 GMT</pubDate>
            <description><![CDATA[<h2 id="1-인공-신경망의-기본-아이디어">1. 인공 신경망의 기본 아이디어</h2>
<p>인공지능 연구의 핵심 목표 중 하나는 인간의 사고 과정을 수학적·계산적으로 모사하는 것이다. 이러한 시도의 출발점은 <strong>뉴런(neuron)</strong> 이라는 생물학적 개념에 있다. 인간의 뇌는 수많은 뉴런이 서로 연결되어 정보를 전달하고 처리함으로써 복잡한 판단을 수행한다. 인공 신경망(Artificial Neural Network)은 이 구조를 단순화하여 컴퓨터에서 구현한 모델이다.</p>
<p>이 장에서는 인공 신경망의 가장 기본적인 구성 요소인 <strong>퍼셉트론(perceptron)</strong> 과, 그 한계를 극복하기 위해 제안된 <strong>다층 퍼셉트론(multi-layer perceptron)</strong> 을 살펴본다.</p>
<hr>
<h2 id="2-퍼셉트론-가장-단순한-인공-뉴런">2. 퍼셉트론: 가장 단순한 인공 뉴런</h2>
<p>퍼셉트론은 입력값을 활성화함수(activation function) 에 통과시켜 일정한수준(threshold) 을 넘으면 True 아니면 False를 내보내는 간단한 회로이다. 이는 인공 신경망을 구성하는 가장 기본적인 계산 단위이며, 입력 값과 활성화 함수를 사용해 출력 값을 넘기는 가장 작은 신경망 단위이다.</p>
<p>구조는 매우 단순하다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/997fcca3-253d-489b-b2bc-b292c6c79ed1/image.png" alt=""></p>
<p>퍼셉트론은 여러 개의 입력값을 받아 각 입력에 대응하는 가중치를 곱한 뒤, 이들의 합에 바이어스를 더한다. 이렇게 계산된 가중합은 활성화 함수를 통과하여 최종 출력값을 결정한다.</p>
<p>이를 수식으로 표현하면 다음과 같다.</p>
<p>$$y = \tau\left(\sum_{i=1}^{d} w_i x_i + b\right)$$</p>
<p>여기서</p>
<ul>
<li>(x_i)는 입력값</li>
<li>(w_i)는 가중치</li>
<li>(b)는 바이어스</li>
<li>($$\tau(\cdot)$$)는 활성화 함수이다.</li>
</ul>
<p>활성화 함수란, 전통적으론 가중 합의 결과로부터 0또는 1을 출력하는 함수다.
(현재의 활성화 함수는 가중합에 비선형을 더해주는 임의의 함수다.)</p>
<p>초기의 퍼셉트론에서 활성화 함수는 임계값 함수(threshold function)를 사용하였다. 가중합이 0보다 크면 1을 출력하고, 그렇지 않으면 0을 출력한다. 따라서 퍼셉트론은 본질적으로 <strong>이진 분류기(binary classifier)</strong> 로 볼 수 있다.</p>
<hr>
<h2 id="3-퍼셉트론과-선형-분리">3. 퍼셉트론과 선형 분리</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/26e642c1-d051-4387-96e0-8eabf64e0c28/image.png" alt=""></p>
<p>퍼셉트론이 해결할 수 있는 문제에는 중요한 제약 조건이 있다. 퍼셉트론은 입력 공간에서 <strong>하나의 직선(또는 고차원에서는 초평면)</strong> 으로 클래스가 구분 가능한 경우에만 정상적으로 작동한다. 이를 <strong>선형 분리 가능(linearly separable)</strong> 하다고 한다.</p>
<p>예를 들어 AND, OR 논리 연산은 입력 공간에서 하나의 직선으로 True와 False를 구분할 수 있기 때문에 퍼셉트론으로 구현이 가능하다. 반면 XOR 논리 연산은 동일한 방식으로는 해결할 수 없다.</p>
<hr>
<h2 id="4-xor-문제와-퍼셉트론의-한계">4. XOR 문제와 퍼셉트론의 한계</h2>
<p>XOR 문제는 퍼셉트론의 구조적 한계를 가장 명확하게 보여주는 예제이다. XOR 연산은 두 입력이 서로 다를 때만 True를 출력한다. 하지만 이 네 개의 입력 조합은 어떤 하나의 직선으로도 분리할 수 없다.</p>
<p>이 사실은 1969년 Marvin Minsky가 『Perceptrons』에서 지적하였으며, 이는 당시 인공지능 연구에 큰 영향을 미쳤다. 퍼셉트론을 단순히 확장하는 방식만으로는 일반적인 지능을 구현하기 어렵다는 인식이 확산되었고, 인공지능 연구는 한동안 침체기를 겪게 된다.</p>
<hr>
<h2 id="5-다층-퍼셉트론의-등장">5. 다층 퍼셉트론의 등장</h2>
<p>퍼셉트론의 한계를 극복하기 위한 핵심 아이디어는 다음과 같다.</p>
<blockquote>
<p>하나의 직선으로 분리할 수 없다면, 여러 번 나누면 되지 않을까?</p>
</blockquote>
<p>이 생각에서 출발한 모델이 <strong>다층 퍼셉트론(Multi-layer Perceptron, MLP)</strong> 이다. 다층 퍼셉트론은 퍼셉트론을 여러 층으로 쌓아 구성한다. 일반적으로 입력층과 출력층 사이에 하나 이상의 <strong>은닉층(hidden layer)</strong> 을 둔다.</p>
<p>은닉층의 퍼셉트론들은 단순한 분류기가 아니라, 입력 데이터를 새로운 표현 공간으로 변환하는 역할을 한다. 이를 통해 선형 분리가 불가능했던 문제를 선형 분리가 가능한 형태로 바꿀 수 있다.</p>
<p>이 구조를 사용하면 XOR 문제 역시 간단히 해결할 수 있다.</p>
<hr>
<h2 id="6-은닉층의-역할-공간의-변환">6. 은닉층의 역할: 공간의 변환</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/d26cc169-3a0b-48a0-8936-4ed1ea657eed/image.png" alt=""></p>
<p>은닉층의 본질적인 역할은 <strong>좌표 공간의 변환</strong>이다. 입력 데이터를 다른 공간으로 사상(mapping)함으로써, 기존 공간에서는 복잡하게 얽혀 있던 데이터가 새로운 공간에서는 단순한 경계로 분리될 수 있도록 만든다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/e5611483-e6f7-4b52-91e5-5fec993a9d8f/image.png" alt=""></p>
<p>이 관점에서 보면,</p>
<ul>
<li>퍼셉트론은 분류기이며</li>
<li>은닉층은 특징 추출기(feature extractor)라고 할 수 있다.</li>
</ul>
<p>때문에, XOR 문제를 해결하기 위해서는 좌표 평면 자체에 변환을 주어야 하며, 이를 가능하게 하려면 숨어있는 층인 은닉층을 만들어야 한다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/c492c426-9483-4b1c-8a31-39083eef9190/image.png" alt=""></p>
<p>은닉층은 좌표 평면의 왜곡이며, 다른 공간으로의 변환 (차원 확대 또는 축소)라고 볼 수 있다.
이 아이디어는 이후 딥러닝 전반에서 매우 중요한 개념으로 확장된다.</p>
<p>다시 XOR 문제로 넘어가면,
<img src="https://velog.velcdn.com/images/you_n_sunny/post/968f45f8-3f1f-49cb-aaf8-e9c13a840d96/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/2be61bc9-ecd9-454d-abb0-df81aceea4e1/image.png" alt=""></p>
<hr>
<h2 id="7-다층-퍼셉트론의-학습과-기울기-소실-문제">7. 다층 퍼셉트론의 학습과 기울기 소실 문제</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/402f4c46-a070-4207-a96d-59f78366179e/image.png" alt=""></p>
<p>다층 퍼셉트론은 구조적으로는 강력하지만, 학습 과정에서 새로운 문제를 야기한다. 신경망은 일반적으로 <strong>경사하강법(gradient descent)</strong> 과 <strong>역전파(backpropagation)</strong> 알고리즘을 사용해 학습한다.</p>
<h3 id="경사하강법과-역전파-알고리즘">경사하강법과 역전파 알고리즘</h3>
<p>다층 퍼셉트론은 구조적으로는 강력하지만, 학습 과정에서 새로운 문제를 야기한다. 신경망은 일반적으로 <strong>경사하강법(gradient descent)</strong> 과 <strong>역전파(backpropagation)</strong> 알고리즘을 사용해 학습한다. 이 절에서는 이 두 개념이 무엇이며, 왜 함께 사용되는지를 설명한다.</p>
<h4 id="경사하강법gradient-descent">경사하강법(Gradient Descent)</h4>
<p>경사하강법은 신경망 학습에서 <strong>손실 함수(loss function)를 최소화하기 위한 최적화 기법</strong>이다. 신경망의 목표는 입력에 대한 예측값과 실제 정답 사이의 차이를 줄이는 것이며, 이 차이를 수치적으로 표현한 것이 손실 함수이다.</p>
<p>신경망의 모든 가중치와 바이어스를 변수로 생각하면, 손실 함수는 이 변수들에 의해 결정되는 하나의 함수가 된다. 학습이란 이 함수가 최소가 되는 지점을 찾는 과정이라고 볼 수 있다.</p>
<p>경사하강법의 핵심 아이디어는 다음과 같다.</p>
<blockquote>
<p>현재 위치에서 손실 함수의 값이 가장 빠르게 감소하는 방향은
그 지점에서의 기울기(gradient)의 반대 방향이다.</p>
</blockquote>
<p>이를 바탕으로 경사하강법은 각 가중치를 다음과 같은 규칙으로 업데이트한다.</p>
<p>$$w \leftarrow w - \eta \frac{\partial L}{\partial w}$$</p>
<p>여기서</p>
<ul>
<li>( $$\frac{\partial L}{\partial w}$$ )는 가중치 (w)에 대한 손실 함수의 기울기</li>
<li>( $$\eta$$ )는 학습률(learning rate)로, 한 번의 업데이트에서 이동하는 크기를 의미한다.</li>
</ul>
<p>즉, 경사하강법은 손실을 증가시키는 방향의 반대로 가중치를 조금씩 조정함으로써 모델의 성능을 점진적으로 개선한다.</p>
<h4 id="역전파backpropagation">역전파(Backpropagation)</h4>
<p>경사하강법을 적용하기 위해서는 각 가중치에 대해 손실 함수의 기울기를 계산해야 한다. 그러나 다층 퍼셉트론에서는 출력이 여러 층의 연속적인 연산을 거쳐 계산되기 때문에, 이 미분을 직접 계산하는 것은 매우 비효율적이다.</p>
<p>이 문제를 해결하기 위해 사용되는 알고리즘이 <strong>역전파(backpropagation)</strong> 이다. 역전파는 출력층에서 계산된 오차를 입력층 방향으로 전달하면서, 각 층의 가중치가 손실에 얼마나 기여했는지를 계산한다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/2791a5ca-2f2a-4bdc-8aea-80aa5ed833cb/image.png" alt=""></p>
<p>역전파의 핵심은 <strong>미분의 연쇄 법칙(chain rule)</strong> 이다. 신경망의 출력은 여러 함수가 중첩된 형태이므로, 전체 미분은 각 단계의 미분을 곱한 형태로 표현된다.</p>
<p>역전파 과정은 다음과 같은 순서로 이루어진다.</p>
<ol>
<li><strong>순전파(forward propagation)</strong>
입력층에서 시작해 은닉층을 거쳐 출력층까지 값을 계산한다.</li>
<li><strong>손실 계산</strong>
출력값과 실제 정답을 비교하여 손실 함수를 계산한다.</li>
<li><strong>역전파(backward propagation)</strong>
출력층부터 입력층 방향으로 기울기를 전달하며 각 가중치에 대한 미분값을 계산한다.</li>
<li><strong>가중치 업데이트</strong>
계산된 기울기를 사용해 경사하강법으로 가중치를 수정한다.</li>
</ol>
<p>즉, 역전파는 <strong>경사하강법에 필요한 기울기를 효율적으로 계산하는 알고리즘</strong>이라고 할 수 있다.</p>
<hr>
<h4 id="기울기-소실-문제와의-연결">기울기 소실 문제와의 연결</h4>
<p>역전파 과정에서 각 층의 기울기는 여러 미분값의 곱으로 표현된다. 만약 각 층에서의 미분값이 1보다 작다면, 이 곱은 층이 깊어질수록 급격히 작아진다. 그 결과 입력층에 가까운 가중치들은 거의 업데이트되지 않게 된다.</p>
<p>이 현상을 <strong>기울기 소실(vanishing gradient) 문제</strong>라고 하며, 이는 전통적인 다층 퍼셉트론에서 학습이 어려웠던 주요 원인 중 하나이다. 특히 시그모이드 함수와 같이 미분값이 작은 활성화 함수를 사용할 경우, 이 문제가 더욱 심각하게 나타난다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/9f248996-eeba-4556-a02d-f5451ee55cd8/image.png" alt=""></p>
<hr>
<h4 id="정리">정리</h4>
<p>경사하강법과 역전파는 역할이 명확히 구분된다.</p>
<ul>
<li>경사하강법은 <strong>가중치를 어느 방향으로, 얼마나 변경할지</strong>를 결정하는 방법이며</li>
<li>역전파는 <strong>그 방향을 계산하기 위해 필요한 기울기를 계산하는 방법</strong>이다.</li>
</ul>
<p>따라서 다층 퍼셉트론의 학습에서 두 알고리즘은 항상 함께 사용되며, 이후 등장하는 딥러닝 모델에서도 동일한 원리가 적용된다.</p>
<hr>
<h2 id="8-활성화-함수와-기울기-소실">8. 활성화 함수와 기울기 소실</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/14a80150-600c-4f2e-81fd-0b700564ea52/image.png" alt=""></p>
<p>기울기 소실 문제의 주요 원인 중 하나는 전통적으로 활성화 함수로 사용되던 <strong>시그모이드(sigmoid) 함수</strong>의 특성이다. 시그모이드 함수는 출력값이 0과 1 사이로 제한되며, 미분값의 최대도 매우 작다. 따라서 여러 층을 거치면 기울기가 거의 0에 수렴하게 된다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/ed9fa8aa-2b55-46f2-8c0c-807b2f903626/image.png" alt=""></p>
<p>이 문제를 해결하기 위해 최근의 신경망에서는 은닉층의 활성화 함수로 <strong>ReLU(Rectified Linear Unit)</strong> 가 주로 사용된다. ReLU는 양수 영역에서 기울기가 1이므로, 깊은 신경망에서도 기울기가 비교적 안정적으로 전달된다.</p>
<h3 id="왜-초기-다층-퍼셉트론에서는-시그모이드-함수를-사용했는가">왜 초기 다층 퍼셉트론에서는 시그모이드 함수를 사용했는가</h3>
<p>오늘날의 신경망에서는 은닉층의 활성화 함수로 ReLU가 표준처럼 사용되지만, 초기 다층 퍼셉트론에서는 대부분 <strong>시그모이드(sigmoid) 함수</strong>가 사용되었다. 이는 단순한 관성이나 우연이 아니라, 당시의 <strong>이론적 배경과 기술적 제약</strong>에 따른 합리적인 선택이었다.</p>
<h4 id="생물학적-뉴런-모델과의-유사성">생물학적 뉴런 모델과의 유사성</h4>
<p>초기 인공 신경망 연구는 생물학적 뉴런을 모사하는 데서 출발했다. 시그모이드 함수는 입력이 증가함에 따라 출력이 점진적으로 증가하다가 포화되는 형태를 갖는데, 이는 뉴런의 발화(firing) 특성과 유사하다고 여겨졌다.</p>
<p>특히 시그모이드 함수는 다음과 같은 특징을 가진다.</p>
<ul>
<li>입력이 작을 때는 거의 0</li>
<li>특정 임계 구간에서는 민감하게 반응</li>
<li>입력이 충분히 크면 출력이 1에 가까워짐</li>
</ul>
<p>이러한 형태는 “뉴런이 자극의 강도에 따라 발화 확률이 증가한다”는 직관적 해석과 잘 맞아떨어졌기 때문에, 시그모이드는 생물학적으로 타당한 활성화 함수로 받아들여졌다.</p>
<h4 id="미분-가능성과-수학적-안정성">미분 가능성과 수학적 안정성</h4>
<p>퍼셉트론의 학습에는 미분이 필수적이다. 역전파 알고리즘을 적용하려면 활성화 함수가 <strong>연속적이고 미분 가능</strong>해야 한다.</p>
<p>시그모이드 함수는 다음과 같은 장점을 갖는다.</p>
<ul>
<li>모든 구간에서 미분 가능</li>
<li>함수 형태가 부드럽고 연속적</li>
<li>미분식이 간단함</li>
</ul>
<p>$$\sigma&#39;(x) = \sigma(x)(1 - \sigma(x))$$</p>
<p>이 단순한 미분 형태는 초기 신경망 연구에서 이론 분석과 구현을 매우 용이하게 했다. 반면, ReLU와 같은 함수는 0에서 미분이 정의되지 않기 때문에 초기에는 수학적으로 다루기 어렵다고 여겨졌다.</p>
<h4 id="확률적-해석이-가능함">확률적 해석이 가능함</h4>
<p>시그모이드 함수의 출력은 항상 0과 1 사이에 위치한다. 이 특성은 출력을 <strong>확률로 해석</strong>하는 데 매우 유리하다.</p>
<p>특히 이진 분류 문제에서 시그모이드 출력은</p>
<p>$$P(y = 1 \mid x)$$</p>
<p>로 해석될 수 있다. 이 관점은 이후 로지스틱 회귀(logistic regression)와 신경망을 연결하는 중요한 이론적 기반이 되었다.</p>
<p>초기 연구자들은 은닉층에서도 이러한 “확률적 활성화”가 유의미할 것이라 기대했고, 이는 시그모이드를 은닉층까지 확장해 사용하는 계기가 되었다.</p>
<h4 id="당시에는-깊은-신경망이-현실적이지-않았음">당시에는 ‘깊은 신경망’이 현실적이지 않았음</h4>
<p>기울기 소실 문제는 <strong>신경망이 깊어질 때</strong> 본격적으로 문제가 된다. 그러나 초기 인공 신경망 연구에서는 다음과 같은 현실적 제약이 있었다.</p>
<ul>
<li>은닉층 수가 매우 적음 (1~2층 수준)</li>
<li>데이터 규모가 작음</li>
<li>계산 자원이 극히 제한적임</li>
</ul>
<p>이러한 환경에서는 시그모이드의 단점이 심각하게 드러나지 않았다. 오히려 부드럽고 안정적인 시그모이드가 학습을 더 잘 수렴시키는 경우도 많았다.</p>
<p>즉, 당시의 문제 설정에서는 시그모이드는 <strong>충분히 잘 작동하는 함수</strong>였다.</p>
<p>문제는 ‘시그모이드’ 자체가 아니라, 깊이가 깊어졌을 때였다</p>
<p>중요한 점은 다음이다.</p>
<blockquote>
<p>시그모이드 함수는 잘못된 선택이 아니었다.
문제는 시그모이드를 사용한 채로 신경망을 깊게 쌓으려 했다는 데 있다.</p>
</blockquote>
<p>층이 깊어질수록 시그모이드의 미분값이 0에 가까워지면서 기울기 소실 문제가 발생했고, 이는 깊은 신경망의 학습을 어렵게 만들었다. 이 한계를 인식하고 극복하는 과정에서 ReLU, 가중치 초기화 기법, 정규화 기법 등이 등장하며 딥러닝이 본격적으로 발전하게 된다.</p>
<h2 id="9-계산-그래프를-이용한-오차-역전파">9. 계산 그래프를 이용한 오차 역전파</h2>
<p>앞 절에서 살펴본 역전파 알고리즘은 미분의 연쇄 법칙(chain rule)에 기반한다. 그러나 실제 신경망은 수많은 연산이 중첩된 매우 복잡한 함수 형태를 가지므로, 이를 수식으로 직접 미분하는 것은 비현실적이다. 이 문제를 체계적으로 해결하기 위해 도입된 개념이 <strong>계산 그래프(computational graph)</strong> 이다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/9ce1f466-c02c-4548-b0aa-12fb88ae00a0/image.png" alt=""></p>
<h3 id="계산-그래프의-개념">계산 그래프의 개념</h3>
<p>계산 그래프는 하나의 함수를 <strong>연산 단위로 분해하여 그래프 형태로 표현한 것</strong>이다.</p>
<ul>
<li><strong>노드(node)</strong> 는 연산(덧셈, 곱셈, 활성화 함수 등)을 나타내고</li>
<li><strong>간선(edge)</strong> 은 연산 사이를 오가는 데이터(값 또는 미분값)를 나타낸다.</li>
</ul>
<p>이러한 표현 방식의 핵심 장점은, 복잡한 함수라도 <strong>국소적인 계산(local computation)</strong> 들의 조합으로 이해할 수 있다는 점이다.</p>
<hr>
<h3 id="순전파와-역전파">순전파와 역전파</h3>
<p>계산 그래프에서는 모든 계산이 두 단계로 나뉜다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/3fc4664b-e889-4051-9bbf-066d9ec4f208/image.png" alt=""></p>
<h4 id="순전파-forward-propagation">순전파 (Forward Propagation)</h4>
<p>순전파는 입력으로부터 출력을 계산하는 과정이다. 계산 그래프의 왼쪽에서 오른쪽으로 연산을 수행하며, 각 노드에서 중간 결과를 저장한다. 이 단계에서 신경망은 주어진 입력에 대한 예측값을 계산한다.</p>
<h4 id="역전파-backward-propagation">역전파 (Backward Propagation)</h4>
<p>역전파는 출력에서 발생한 오차가 각 연산과 가중치에 어떻게 영향을 미쳤는지를 계산하는 과정이다. 계산 그래프의 오른쪽에서 왼쪽으로 진행하며, 각 노드에서 <strong>출력에 대한 입력의 미분값</strong>을 계산한다.</p>
<p>이때 핵심 원리는 다음과 같다.</p>
<blockquote>
<p>전체 미분은 각 연산의 국소 미분을 곱하여 전달할 수 있다.</p>
</blockquote>
<p>이는 미분의 연쇄 법칙을 계산 그래프 형태로 구현한 것이다.</p>
<hr>
<h3 id="연쇄-법칙과-계산-그래프">연쇄 법칙과 계산 그래프</h3>
<p>함수가 다음과 같이 여러 함수의 합성으로 이루어져 있다고 하자.</p>
<p>$$a = h(g(f(x)))$$</p>
<p>이때 (a)를 (x)에 대해 미분하면,</p>
<p>$$
\frac{da}{dx}=</p>
<p>\frac{da}{dz}
\cdot
\frac{dz}{dy}
\cdot
\frac{dy}{dx}
$$</p>
<p>와 같이 표현된다. 계산 그래프는 이 과정을 <strong>시각적이고 기계적으로 처리할 수 있게 해준다</strong>.</p>
<p>각 노드는 자신이 수행한 연산에 대해서만 미분을 계산하고, 그 결과를 이전 노드로 전달한다. 이 방식 덕분에 복잡한 함수도 전체 형태를 다시 보지 않고 미분할 수 있다.</p>
<hr>
<h3 id="기본-연산-노드의-역전파">기본 연산 노드의 역전파</h3>
<p>계산 그래프에서 자주 등장하는 연산들은 다음과 같은 단순한 역전파 규칙을 갖는다.</p>
<ul>
<li><p><strong>덧셈 노드</strong>
<img src="https://velog.velcdn.com/images/you_n_sunny/post/de153b4c-ebf2-420e-88a4-888225d4803b/image.png" alt=""></p>
<p>출력의 기울기가 그대로 각 입력으로 전달된다.</p>
</li>
<li><p><strong>곱셈 노드</strong>
<img src="https://velog.velcdn.com/images/you_n_sunny/post/495c09f3-62d4-4b88-b280-bb9b504dd442/image.png" alt=""></p>
<p>한 입력에 대한 기울기는 다른 입력값을 곱한 형태로 전달된다.</p>
</li>
</ul>
<h3 id="relu의-역전파">RELU의 역전파</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/d4fa91d7-d54d-4b96-91ff-f4e45e2c6b0e/image.png" alt="">
ReLU(Rectified Linear Unit)는 은닉층에서 가장 널리 사용되는 활성화 함수로, 다음과 같이 정의된다.
$$y = \mathrm{ReLU}(x) = \max(0, x)$$
역전파에서 입력 (x)에 대한 손실 함수 (L)의 기울기는 다음과 같이 계산된다. 
$$ \frac{\partial L}{\partial x}
=
\frac{\partial L}{\partial y}
\cdot
\frac{dy}{dx}
$$
따라서 ReLU 계층의 역전파는 ($$\frac{dy}{dx}$$), 즉 ReLU 함수의 미분값에 의해 결정된다.</p>
<p>  <strong>입력이 양수인 경우 ((x &gt; 0))</strong></p>
<ul>
<li><p><strong>순전파</strong>
입력 (x &gt; 0) 이므로 출력은 (y = x) 이다.</p>
</li>
<li><p><strong>미분값</strong>
이 구간에서 ReLU는 항등 함수(identity function)이므로,
$$
\frac{dy}{dx} = 1
$$</p>
</li>
<li><p><strong>역전파</strong>
$$</p>
<h1 id="fracpartial-lpartial-x">\frac{\partial L}{\partial x}</h1>
<p>\frac{\partial L}{\partial y}
$$
즉, 출력 쪽에서 전달된 기울기가 <strong>감소나 변화 없이 그대로 입력 쪽으로 전달된다</strong>. 그림에서 빨간 화살표가 끊기지 않고 그대로 이어지는 이유가 바로 이 때문이다.</p>
</li>
</ul>
<p>** 입력이 0 이하인 경우 (($$x \le 0$$)) **</p>
<ul>
<li><strong>순전파</strong>
입력 ($$x \le 0$$) 이므로 출력은 (y = 0) 이다.</li>
<li><strong>미분값</strong>
이 구간에서 ReLU는 상수 함수이므로,
$$\frac{dy}{dx} = 0$$</li>
<li><strong>역전파</strong>
$$\frac{\partial L}{\partial x}=0$$
즉, 출력 쪽에서 전달된 기울기가 입력 쪽으로 <strong>전혀 전달되지 않고 차단된다</strong>. 그림에서 입력 방향으로의 기울기가 0으로 표시되는 이유가 여기에 있다.</li>
</ul>
<p>위와같은 이유로 RELU는 입력이 양수인 영역에서는 기울기가 1로 유지되어  <strong>기울기 소실 문제를 완화</strong>한다. 또한, 계산이 단순하여 학습 속도가 빠르고 구현이 용이하다.</p>
<p>다만, 입력이 항상 0 이하로 유지되는 뉴런은 기울기가 계속 0이 되어 더 이상 학습되지 않는 문제가 발생할 수 있으며, 이를 <strong>Dead ReLU 문제</strong>라고 한다. 이 문제를 완화하기 위해 Leaky ReLU, PReLU 등의 변형 함수가 제안되었다.</p>
<h3 id="sigmoid의-역전파">sigmoid의 역전파</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/023783d9-1cdf-4416-8140-6efd2a1aaea2/image.png" alt=""></p>
<p><strong>Sigmoid 계층의 계산 그래프와 오차 역전파</strong></p>
<p>Sigmoid 함수는 다음과 같이 정의된다.</p>
<p>$$y = \sigma(x) = \frac{1}{1 + \exp(-x)}$$</p>
<p>Sigmoid는 하나의 함수처럼 보이지만, 계산 그래프 관점에서는 여러 개의 기본 연산으로 분해할 수 있다. 이러한 분해는 오차 역전파를 이해하는 데 매우 중요하다.</p>
<p><strong>Sigmoid 함수의 계산 그래프 분해</strong></p>
<p>Sigmoid 함수는 다음과 같은 연산들의 조합으로 표현된다.</p>
<ol>
<li>입력 (x)에 (-1)을 곱해 (-x)를 계산</li>
<li>($$\exp(-x)$$) 계산</li>
<li>($$1 + \exp(-x)$$) 계산</li>
<li>역수 연산을 통해
$$y = \frac{1}{1 + \exp(-x)}$$
계산</li>
</ol>
<p><strong>순전파 (Forward Propagation)</strong></p>
<p>순전파에서는 계산 그래프의 왼쪽에서 오른쪽으로 연산이 진행된다.</p>
<ul>
<li>입력 (x) → (-x)</li>
<li>($$-x$$) → ($$\exp(-x)$$)</li>
<li>($$\exp(-x)$$) → ($$1 + \exp(-x)$$)</li>
<li>최종 출력 (y)</li>
</ul>
<p>이 과정에서 각 노드의 출력값은 <strong>역전파를 위해 저장</strong>된다.</p>
<p><strong>역전파 (Backward Propagation)의 목표</strong></p>
<p>역전파의 목적은 다음 값을 계산하는 것이다.</p>
<p>$$\frac{\partial L}{\partial x}$$</p>
<p>즉, 출력 (y)에 대한 손실 함수의 기울기 ($$\frac{\partial L}{\partial y}$$)가 주어졌을 때, 이 오차가 입력 (x)에 어떻게 전달되는지를 계산한다.</p>
<p><strong>단계별 오차 역전파 설명</strong></p>
<p>이제 그림의 <strong>아래쪽(빨간 화살표)</strong> 을 따라, 오른쪽에서 왼쪽으로 역전파를 진행한다.</p>
<p><strong>1) 1단계: 나눗셈(역수) 노드</strong></p>
<p>$$y = \frac{1}{u}, \quad u = 1 + \exp(-x)$$</p>
<p>미분하면,</p>
<p>$$\frac{dy}{du} = -\frac{1}{u^2} = -y^2$$</p>
<p>따라서 역전파 기울기는</p>
<p>$$\frac{\partial L}{\partial u}
=\frac{\partial L}{\partial y}
\cdot (-y^2)$$</p>
<p>그림에서 ($$-\frac{\partial L}{\partial y} y^2$$) 가 표시된 이유가 바로 이 때문이다.</p>
<p><strong>2) 2단계: 덧셈 노드</strong>
$$u = 1 + \exp(-x)$$</p>
<p>덧셈 연산의 미분값은 항상 1이므로,</p>
<p>$$\frac{\partial L}{\partial \exp(-x)}=\frac{\partial L}{\partial u}$$</p>
<p>즉, 기울기는 <strong>그대로 전달</strong>된다.</p>
<p><strong>3) 3단계: 지수(exp) 노드</strong></p>
<p>$$v = \exp(-x)$$</p>
<p>미분하면,</p>
<p>$$\frac{dv}{d(-x)} = \exp(-x)$$</p>
<p>따라서,</p>
<p>$$\frac{\partial L}{\partial (-x)}
=\frac{\partial L}{\partial v}
\cdot \exp(-x)$$</p>
<p><strong>4) 4단계: 곱셈 노드 ($$(-1 \times x)$$)</strong></p>
<p>$$* x = (-1) \cdot x
  $$</p>
<p>이 경우,
$$
\frac{d(-x)}{dx} = -1
$$</p>
<p>따라서 최종적으로,</p>
<p>$$
\frac{\partial L}{\partial x}
=* \frac{\partial L}{\partial (-x)}
$$</p>
<p>위의 모든 단계를 곱하면 다음과 같은 결과를 얻는다.</p>
<p>$$
\frac{\partial L}{\partial x}
=
\frac{\partial L}{\partial y}
\cdot y (1 - y)
$$</p>
<p>이는 Sigmoid 함수의 잘 알려진 미분 결과이다.</p>
<p>이 과정에서 중요한 점은 다음이다.</p>
<ul>
<li>Sigmoid의 복잡한 미분 공식을 <strong>외울 필요가 없다</strong></li>
<li>각 노드는 자신의 연산에 대한 국소 미분만 계산한다</li>
<li>전체 미분은 국소 미분들의 곱으로 자동 완성된다</li>
</ul>
<p>즉, 계산 그래프는 <strong>복잡한 함수의 미분을 기계적으로 수행할 수 있게 해주는 도구</strong>이다.</p>
<p><strong>Sigmoid 역전파와 기울기 소실 문제</strong></p>
<p>Sigmoid의 미분값은 $$y(1 - y)$$ 이며, 이 값은 항상 0과 0.25 사이에 존재한다. 따라서 Sigmoid 계층이 여러 층 반복되면 기울기는 계속 작아지며, 깊은 신경망에서는 <strong>기울기 소실 문제</strong>가 발생하게 된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[EXPLAINABLE ANOMALY DETECTION FOR INDUSTRIAL]]></title>
            <link>https://velog.io/@you_n_sunny/EXPLAINABLE-ANOMALY-DETECTION-FOR-INDUSTRIAL</link>
            <guid>https://velog.io/@you_n_sunny/EXPLAINABLE-ANOMALY-DETECTION-FOR-INDUSTRIAL</guid>
            <pubDate>Sun, 11 Jan 2026 14:44:47 GMT</pubDate>
            <description><![CDATA[<h2 id="1-abstract">1. Abstract</h2>
<p>본 연구의 목적은 <strong>산업 제어 시스템(ICS)의 사이버 보안 강화를 위해 높은 탐지 성능과 해석 가능성을 동시에 갖춘 이상 탐지(Anomaly Detection, AD) 방법론을 제안하는 것</strong>이다.</p>
<p>이를 위해 <strong>LSTM 오토인코더(Autoencoder)</strong> 와 <strong>단일 클래스 서포트 벡터 머신(OCSVM)</strong> 을 결합한 하이브리드 이상 탐지 모델을 구성하고, 여기에 <strong>Gradient SHAP 기반의 설명 가능한 인공지능(XAI) 모듈</strong>을 통합하였다.</p>
<p>제안한 방법은 <strong>SCADA(Secure Water Treatment) 계열 데이터셋 중 Gas Pipeline 데이터셋</strong>을 활용하여 평가되었으며, 기존 방법론 대비 <strong>Recall 96.28%, F1-Score 90.12%</strong>로 우수한 탐지 성능을 보였다.</p>
<p>또한 XAI 모듈을 통해 이상 징후 판단에 가장 크게 기여한 특징(feature)을 시각적으로 해석함으로써, 엔지니어가 해당 이상이 <strong>실제 사이버 공격인지, 혹은 시스템/센서 오작동인지</strong>를 보다 신속하게 판단하고 대응할 수 있도록 지원한다.</p>
<hr>
<h2 id="2-핵심-용어-및-개념-정리">2. 핵심 용어 및 개념 정리</h2>
<h3 id="ics-industrial-control-systems">ICS (Industrial Control Systems)</h3>
<p>전력, 가스, 물 공급 등 <strong>국가 기반 시설의 운영을 제어·관리하는 산업 제어 시스템</strong>을 의미한다. 최근 IoT, 클라우드, AI 기술과 결합되면서 보안 위협이 증가하고 있다.</p>
<h3 id="ad-anomaly-detection">AD (Anomaly Detection)</h3>
<p>정상적인 데이터 패턴에서 벗어난 <strong>비정상 행위 또는 이벤트를 탐지하는 기술</strong>로, ICS 환경에서는 사이버 침입 및 시스템 공격 방지에 핵심적인 역할을 한다.</p>
<h3 id="lstm-autoencoder">LSTM Autoencoder</h3>
<p>시계열 데이터의 압축 표현 학습을 위해 <strong>인코더와 디코더 모두에 LSTM 셀을 사용하는 오토인코더 구조</strong>이다. 시간 의존성이 강한 데이터에서 정상 패턴을 효과적으로 학습할 수 있다.</p>
<h3 id="ocsvm-one-class-support-vector-machine">OCSVM (One-Class Support Vector Machine)</h3>
<p>정상 데이터만을 학습하여, 새로운 입력 데이터가 정상 범주에 속하는지 여부를 판별하는 <strong>단일 클래스 분류 기반 이상 탐지 알고리즘</strong>이다.</p>
<h3 id="xai-explainable-artificial-intelligence">XAI (Explainable Artificial Intelligence)</h3>
<p>AI 모델의 예측 결과와 내부 작동 과정을 <strong>사람이 이해할 수 있도록 설명하는 기술</strong>로, 산업 현장에서 모델 신뢰성을 확보하는 데 중요하다.</p>
<h3 id="shap-shapley-additive-explanations">SHAP (SHapley Additive exPlanations)</h3>
<p>게임 이론의 Shapley 값을 기반으로, <strong>각 특징(feature)이 예측 결과에 기여한 정도를 정량적으로 추정</strong>하는 XAI 기법이다.</p>
<h3 id="gradient-shap">Gradient SHAP</h3>
<p>딥러닝 모델에 적합한 SHAP 근사 기법으로, 기준선(baseline) 분포에서 무작위 샘플링을 수행하고 <strong>기울기(gradient)의 기댓값을 계산하여 SHAP 값을 추정</strong>한다. Deep SHAP 대비 계산 비용이 낮다.</p>
<hr>
<h2 id="3-method">3. Method</h2>
<h3 id="31-하이브리드-이상-탐지-모델">3.1 하이브리드 이상 탐지 모델</h3>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/e4e7e1ad-17ae-4f6f-a6d9-fec5030b7297/image.png" alt=""></p>
<p><em>(LSTM Autoencoder – OCSVM)</em>
그림 1은 본 연구에서 제안한 LSTM Autoencoder–OCSVM 기반 이상 탐지 모델과 Gradient SHAP 기반 XAI 모듈의 전체 처리 흐름을 나타낸다.</p>
<p>입력 시계열 데이터는 길이 𝑙의 슬라이딩 윈도우(sliding window) 를 통해 겹치는 시퀀스로 분할된다. 각 시퀀스는 LSTM Autoencoder의 인코더를 통해 시간 의존성을 반영한 잠재 표현(latent representation)으로 압축되며, 디코더를 통해 원래 시퀀스로 재구성된다.</p>
<p>이후 입력 시퀀스 𝑋𝑖와 재구성된 시퀀스 𝑋^𝑖 간의 재구성 오차(reconstruction error) 가 계산되며, 해당 오차 정보는 OCSVM(One-Class Support Vector Machine) 모델의 입력으로 전달된다. OCSVM은 정상 데이터 분포로부터 학습된 결정 경계를 기반으로, 해당 시퀀스가 정상(normal) 범주에 속하는지 또는 이상(anomalous)인지 여부를 판별한다.</p>
<p>이상으로 탐지된 시퀀스에 대해서는 설명 가능성 확보를 위한 XAI 모듈이 추가적으로 적용된다. Gradient SHAP을 사용하기 위해, 사전 학습된 LSTM Autoencoder의 가중치를 전이한 설명자(Explainer) 모델을 구성하며, 이때 모델 상단에 Flatten 레이어를 추가하여 SHAP 값 계산이 가능하도록 구조를 수정한다. 설명자 모델은 학습 데이터로부터 추출된 background 데이터와 이상 시퀀스를 입력으로 받아, 각 특징(feature)이 이상 예측에 기여한 정도를 Shapley 값 형태로 시각화한다.</p>
<p>이를 통해 제안된 파이프라인은
(1) LSTM Autoencoder를 통한 시계열 표현 학습,
(2) OCSVM을 통한 안정적인 이상 판별,
(3) Gradient SHAP을 통한 이상 원인 해석
을 단계적으로 수행하며, 높은 탐지 성능과 모델 투명성을 동시에 만족하는 ICS 이상 탐지 구조를 제공한다.</p>
<h4 id="데이터-전처리">데이터 전처리</h4>
<p>입력 데이터
$X = {x_1, x_2, \dots, x_n}$
를 길이 (l)의 <strong>슬라이딩 윈도우(sliding window)</strong> 를 사용해 겹치는 시퀀스로 분할한다.</p>
<h4 id="lstm-autoencoder의-역할">LSTM Autoencoder의 역할</h4>
<ul>
<li>입력 시퀀스를 인코딩하여 저차원 압축 표현(latent representation) 학습</li>
<li>디코더를 통해 원래 시퀀스로 재구성</li>
<li>입력과 출력 간의 <strong>재구성 손실(reconstruction loss)</strong> 계산</li>
</ul>
<p>LSTM Autoencoder는 <strong>정상 시계열의 구조적 패턴 학습</strong>에만 집중한다.</p>
<h4 id="ocsvm의-역할">OCSVM의 역할</h4>
<ul>
<li>LSTM Autoencoder에서 계산된 재구성 손실 정보를 입력으로 사용</li>
<li>새로운 시퀀스가 정상 범주에 속하는지 여부를 최종 판별</li>
</ul>
<p>이를 통해 임계값 설정의 불안정성을 완화하고, 이상 판별을 보다 명확하게 수행한다.</p>
<h4 id="성능-최적화-기준">성능 최적화 기준</h4>
<p>ICS 환경 특성상 실제 이상을 놓치는 비용이 크므로,</p>
<ul>
<li><strong>Precision보다 Recall과 F1-Score를 우선적으로 고려</strong></li>
<li>약간의 오탐(False Positive)을 감수하더라도 이상 탐지율 극대화 목표</li>
</ul>
<hr>
<h3 id="32-설명-가능성-모듈-xai-module">3.2 설명 가능성 모듈 (XAI Module)</h3>
<h4 id="shap-값-계산">SHAP 값 계산</h4>
<p>각 슬라이딩 윈도우 단위 샘플에 대해 <strong>Shapley 값</strong>을 계산하여,</p>
<ul>
<li>어떤 특징이</li>
<li>어느 정도로
이상 예측에 기여했는지 분석한다.</li>
</ul>
<h4 id="gradient-shap-적용-이유">Gradient SHAP 적용 이유</h4>
<p>LSTM Autoencoder의 출력은 벡터 형태를 가지므로,</p>
<ul>
<li>계산 비용이 높은 Deep SHAP 대신</li>
<li>딥러닝 모델에 적합한 <strong>Gradient SHAP</strong>을 사용</li>
</ul>
<h4 id="모델-구조-수정">모델 구조 수정</h4>
<p>Gradient SHAP 계산을 위해,</p>
<ul>
<li>사전 학습된 LSTM Autoencoder의 가중치를 전이(transfer)</li>
<li>상단에 <strong>Flatten 레이어</strong>를 추가한 별도의 설명자(Explainer) 모델 구성</li>
</ul>
<h4 id="해석-방식">해석 방식</h4>
<p>계산된 SHAP 값을 시각화하여,</p>
<ul>
<li>예측된 이상 징후에 대해</li>
<li>특징(feature)별 기여 비율을 직관적으로 확인한다.</li>
</ul>
<hr>
<h2 id="4-results">4. Results</h2>
<h3 id="41-성능-평가">4.1 성능 평가</h3>
<p><em>(Gas Pipeline SCADA Dataset)</em></p>
<p>실험은 <strong>Gas Pipeline SCADA 데이터셋</strong>을 사용하여 수행되었다.</p>
<ul>
<li>학습 데이터: 정상 샘플 40,192개</li>
<li>테스트 데이터: 정상 + 비정상 샘플 10,048개
(이상 비율 약 20%)</li>
</ul>
<p>평가 지표는 <strong>Precision, Recall, F1-Score</strong>를 사용하였다.</p>
<p>제안한 <strong>LSTM Autoencoder–OCSVM 모델</strong>은 다음과 같은 성능을 보였다.</p>
<ul>
<li><strong>Recall: 96.28%</strong></li>
<li><strong>F1-Score: 90.12%</strong></li>
</ul>
<p>이는 K-means–CAE, SVM, 단독 LSTM 모델 등 기존 방법론 대비 우수한 결과로,
실제 ICS 사이버 보안 환경에서의 활용 가능성을 보여준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 머신러닝 모델 평가 지표]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-%ED%8F%89%EA%B0%80-%EC%A7%80%ED%91%9C</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D-%EB%AA%A8%EB%8D%B8-%ED%8F%89%EA%B0%80-%EC%A7%80%ED%91%9C</guid>
            <pubDate>Sun, 11 Jan 2026 13:52:57 GMT</pubDate>
            <description><![CDATA[<p>모델 평가는 단순히 하나의 수치를 계산하는 과정이 아니다.
문제의 성격, 데이터의 분포, 그리고 잘못된 예측이 초래하는 비용(cost)에 따라
적절한 평가 지표는 완전히 달라진다.</p>
<p>이 장에서는 회귀와 분류 문제를 중심으로,
대표적인 평가 지표들의 정의, 해석, 사용 시점과 주의점을 체계적으로 정리한다.</p>
<hr>
<h2 id="1-회귀regression-모델-평가">1. 회귀(Regression) 모델 평가</h2>
<h3 id="핵심-질문">핵심 질문</h3>
<p>회귀 문제에서의 핵심 질문은 다음과 같다.</p>
<blockquote>
<p>예측값은 실제값과 얼마나 가까운가?
즉, 오차(error)의 크기를 어떻게 정의하고, 어떻게 평균낼 것인가?</p>
</blockquote>
<p>회귀 모델은 연속적인 실수값(real-valued output)을 예측한다.
따라서 분류 문제처럼 “맞다 / 틀리다”로 평가할 수 없으며,</p>
<ul>
<li>오차의 크기</li>
<li>오차의 분포</li>
<li>큰 오차를 얼마나 강하게 벌줄 것인지</li>
</ul>
<p>를 명확히 정의해야 한다.</p>
<hr>
<h3 id="11-mae-mean-absolute-error-평균-절대-오차">1.1 MAE (Mean Absolute Error, 평균 절대 오차)</h3>
<p>MAE는 가장 직관적인 회귀 오차 지표다.
각 데이터 포인트에서 발생한 오차의 절대값을 취한 뒤, 이를 평균낸다.</p>
<p>$$
MAE = \frac{1}{n} \sum_{i=1}^{n} |y_i - \hat{y}_i|
$$</p>
<p>절대값을 사용하는 이유는 평가 과정에서
과대 예측과 과소 예측의 방향보다는 <strong>오차의 크기 자체</strong>가 중요하기 때문이다.</p>
<h4 id="특성-및-해석">특성 및 해석</h4>
<ul>
<li>모든 오차를 선형적으로 취급한다.</li>
<li>오차 10은 오차 5의 정확히 두 배로 반영된다.</li>
<li>일부 이상치(outlier)가 존재하더라도 지표가 급격히 커지지 않는다.</li>
<li>데이터 단위와 동일하므로 해석이 용이하다.</li>
</ul>
<p>예를 들어, 집값 예측에서 MAE가 100이라면
모델은 평균적으로 약 100만 원 정도의 오차를 가진다고 해석할 수 있다.</p>
<h4 id="사용이-적합한-경우">사용이 적합한 경우</h4>
<ul>
<li>“평균적으로 얼마나 틀리는지”를 파악하고자 할 때</li>
<li>예측 오차의 단위 자체가 해석의 대상이 되는 경우</li>
<li>일부 이상치는 존재하지만, 이를 과도하게 강조할 필요가 없는 문제</li>
</ul>
<h4 id="사용이-부적절한-경우">사용이 부적절한 경우</h4>
<ul>
<li>큰 오차가 발생했을 때의 비용이 매우 큰 문제</li>
<li>극단적인 예측 실패를 반드시 억제해야 하는 경우</li>
</ul>
<p>이러한 상황에서는 MAE가 큰 오차의 위험성을 충분히 반영하지 못할 수 있다.</p>
<hr>
<h3 id="12-mse--rmse-mean-squared-error">1.2 MSE / RMSE (Mean Squared Error)</h3>
<p>MSE는 오차를 제곱한 뒤 평균낸 값이며,
RMSE는 MSE에 제곱근을 취해 원래 데이터 단위로 복원한 지표다.</p>
<p>$$
MSE = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2
$$</p>
<p>$$
RMSE = \sqrt{MSE}
$$</p>
<p>오차를 제곱함으로써, 큰 오차는 작은 오차보다 훨씬 큰 패널티를 받는다.</p>
<h4 id="왜-제곱을-사용하는가">왜 제곱을 사용하는가?</h4>
<p>오차 제곱은 다음과 같은 효과를 만든다.</p>
<ul>
<li>작은 오차는 상대적으로 영향이 작다.</li>
<li>큰 오차는 제곱으로 인해 급격히 커진다.</li>
</ul>
<p>즉, MSE/RMSE는
“큰 실수는 반드시 강하게 벌주겠다”는 명확한 의도를 가진 지표다.</p>
<h4 id="mae와의-근본적인-차이">MAE와의 근본적인 차이</h4>
<table>
<thead>
<tr>
<th>관점</th>
<th>MAE</th>
<th>RMSE</th>
</tr>
</thead>
<tbody><tr>
<td>오차 처리</td>
<td>선형</td>
<td>제곱</td>
</tr>
<tr>
<td>큰 오차</td>
<td>비교적 관대</td>
<td>매우 민감</td>
</tr>
<tr>
<td>이상치 영향</td>
<td>작음</td>
<td>큼</td>
</tr>
</tbody></table>
<p>실무에서는 해석의 편의성 때문에 RMSE가 MSE보다 더 자주 사용된다.</p>
<h4 id="사용이-적합한-경우-1">사용이 적합한 경우</h4>
<ul>
<li>큰 예측 오차가 실제 비용 또는 위험으로 직결되는 경우</li>
<li>일부 시점의 실패가 시스템 전체에 영향을 미치는 문제
(전력 수요 예측, 트래픽 예측, 설비 부하 예측 등)</li>
</ul>
<h4 id="사용이-부적절한-경우-1">사용이 부적절한 경우</h4>
<ul>
<li>데이터에 노이즈나 이상치가 많이 포함된 경우</li>
<li>소수의 비정상 샘플이 전체 성능 평가를 왜곡해서는 안 되는 경우</li>
</ul>
<hr>
<h3 id="13-mape-mean-absolute-percentage-error">1.3 MAPE (Mean Absolute Percentage Error)</h3>
<p>MAPE는 오차를 실제값 대비 비율로 표현하는 지표다.</p>
<p>$$
MAPE = \frac{100%}{n} \sum_{i=1}^{n}
\left| \frac{y_i - \hat{y}_i}{y_i} \right|
$$</p>
<p>절대 오차는 데이터의 스케일에 크게 의존한다.
MAPE는 이를 보완하기 위해 비율 기반 평가를 사용한다.</p>
<h4 id="특성-및-장점">특성 및 장점</h4>
<ul>
<li>서로 다른 스케일의 데이터 간 성능 비교가 가능하다.</li>
<li>비즈니스 보고에 직관적이다.</li>
<li>“평균적으로 8% 정도의 오차가 발생한다”와 같은 설명이 가능하다.</li>
</ul>
<h4 id="사용이-적합한-경우-2">사용이 적합한 경우</h4>
<ul>
<li>국가·지점·제품군 등 규모가 다른 대상 간 성능 비교</li>
<li>실제값이 0이 되지 않는 매출, 수요, 트래픽 데이터</li>
<li>비즈니스 의사결정 보고용 지표가 필요한 경우</li>
</ul>
<h4 id="사용이-부적절한-경우-2">사용이 부적절한 경우</h4>
<ul>
<li>실제값이 0이 될 수 있는 데이터</li>
<li>실제값이 매우 작은 구간이 자주 등장하는 경우</li>
</ul>
<p>이 경우 MAPE는 정의되지 않거나 비현실적으로 큰 값을 가질 수 있다.</p>
<hr>
<h3 id="14-r2-score-및-adjusted-r2">1.4 (R^2) Score 및 Adjusted (R^2)</h3>
<p>(R^2)는 오차 기반 지표가 아니라,
모델이 종속 변수의 전체 분산을 얼마나 설명하는지를 측정한다.</p>
<p>$$R^2 = 1 - \frac{SSE}{SST}$$</p>
<ul>
<li>SSE: 잔차 제곱합</li>
<li>SST: 전체 제곱합</li>
</ul>
<h4 id="해석">해석</h4>
<ul>
<li>(R^2 = 1): 완벽한 예측</li>
<li>(R^2 = 0): 평균 예측과 동일</li>
<li>음수 가능: 평균보다 못한 모델</li>
</ul>
<h4 id="치명적인-한계">치명적인 한계</h4>
<p>독립변수(X)를 추가하면,
그 변수가 의미가 없더라도 SSE는 감소할 가능성이 크다.
따라서 (R^2)는 거의 항상 증가한다.</p>
<p>즉, 모델의 복잡도를 전혀 고려하지 않는다.</p>
<h4 id="adjusted-r2">Adjusted (R^2)</h4>
<p>이를 보완하기 위해 변수 개수와 샘플 수를 고려한 지표가 Adjusted (R^2)다.</p>
<p>$$R^2_{adj} = 1 - (1 - R^2)\frac{n-1}{n-k-1}$$</p>
<ul>
<li>(n): 샘플 수</li>
<li>(k): 독립변수 개수</li>
</ul>
<p>불필요한 변수가 추가되면 패널티가 부여된다.</p>
<h4 id="사용이-적합한-경우-3">사용이 적합한 경우</h4>
<ul>
<li>선형 회귀 및 다중 회귀 모델 비교</li>
<li>설명력과 해석 가능성이 중요한 분석 과제</li>
</ul>
<h4 id="사용이-부적절한-경우-3">사용이 부적절한 경우</h4>
<ul>
<li>비선형 머신러닝 모델의 단독 성능 평가</li>
<li>예측 정확도가 핵심 목표인 문제</li>
</ul>
<hr>
<h2 id="2-분류classification-모델-평가">2. 분류(Classification) 모델 평가</h2>
<h3 id="핵심-질문-1">핵심 질문</h3>
<p>분류 문제에서는 다음 두 질문이 중요하다.</p>
<ul>
<li>얼마나 정확히 구분하는가?</li>
<li>어떤 종류의 실수를 하는가?</li>
</ul>
<p>특히 클래스 불균형이 존재하는 경우,
Accuracy 하나만으로는 모델 성능을 평가할 수 없다.</p>
<hr>
<h3 id="21-혼동-행렬-confusion-matrix">2.1 혼동 행렬 (Confusion Matrix)</h3>
<p>모든 분류 지표는 혼동 행렬에서 출발한다.</p>
<table>
<thead>
<tr>
<th>실제 \ 예측</th>
<th>Positive</th>
<th>Negative</th>
</tr>
</thead>
<tbody><tr>
<td>Positive</td>
<td>TP</td>
<td>FN</td>
</tr>
<tr>
<td>Negative</td>
<td>FP</td>
<td>TN</td>
</tr>
</tbody></table>
<ul>
<li>FP: 1종 오류 (오진)</li>
<li>FN: 2종 오류 (놓침)</li>
</ul>
<hr>
<h3 id="22-precision과-recall">2.2 Precision과 Recall</h3>
<h4 id="precision-정밀도">Precision (정밀도)</h4>
<p>$$
Precision = \frac{TP}{TP + FP}
$$</p>
<p>양성이라고 예측한 것 중, 실제로 맞은 비율이다.
FP를 줄이는 것이 핵심이다.</p>
<p>적합한 사례:</p>
<ul>
<li>스팸 필터링</li>
<li>추천 시스템</li>
<li>자동 차단 시스템</li>
</ul>
<h4 id="recall-재현율">Recall (재현율)</h4>
<p>$$Recall = \frac{TP}{TP + FN}$$</p>
<p>실제 양성 중에서 모델이 찾아낸 비율이다.
FN을 줄이는 것이 핵심이다.</p>
<p>적합한 사례:</p>
<ul>
<li>암 진단</li>
<li>사기 탐지</li>
<li>결함 검출</li>
</ul>
<hr>
<h3 id="23-f1-score">2.3 F1-score</h3>
<p>Precision과 Recall의 조화 평균이다.</p>
<p>$$F1 = 2 \cdot \frac{Precision \cdot Recall}{Precision + Recall}$$</p>
<p>두 지표 중 하나라도 낮으면 F1-score도 낮아진다.
불균형 데이터에서 Accuracy를 대체하는 대표적인 지표다.</p>
<hr>
<h3 id="24-log-loss-cross-entropy-loss">2.4 Log Loss (Cross-Entropy Loss)</h3>
<p>Log Loss는 정답 여부가 아니라,
모델이 예측한 <strong>확률의 품질</strong>을 평가한다.</p>
<p>$$
-\frac{1}{N} \sum (y \log(p) + (1-y)\log(1-p))
$$</p>
<p>틀렸을 뿐 아니라,
틀린 예측에 강한 확신을 가질수록 큰 패널티를 받는다.</p>
<p>확률 기반 의사결정 시스템에서 핵심적인 지표다.</p>
<hr>
<h3 id="25-roc-auc">2.5 ROC-AUC</h3>
<p>ROC-AUC는 임계값에 독립적인 성능 지표다.
순위 기반으로 모델의 분류 능력을 평가한다.</p>
<p>“임의의 양성 샘플이 음성 샘플보다 높은 점수를 받을 확률”로 해석할 수 있다.</p>
<h2 id="한눈에-보는-요약-cheat-sheet">한눈에 보는 요약 (Cheat Sheet)</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>지표</th>
<th>특징 및 사용 시점</th>
</tr>
</thead>
<tbody><tr>
<td>회귀</td>
<td>MAE</td>
<td>이상치에 덜 민감, 직관적 해석</td>
</tr>
<tr>
<td></td>
<td>MSE / RMSE</td>
<td>큰 오차를 강하게 줄이고 싶을 때</td>
</tr>
<tr>
<td></td>
<td>MAPE</td>
<td>비율(%) 기반 비교, 비즈니스 보고</td>
</tr>
<tr>
<td></td>
<td>Adjusted (R^2)</td>
<td>변수가 많은 회귀 모델 평가</td>
</tr>
<tr>
<td>분류</td>
<td>Accuracy</td>
<td>클래스 균형일 때만 사용</td>
</tr>
<tr>
<td></td>
<td>Precision</td>
<td>FP를 줄이는 것이 중요한 경우</td>
</tr>
<tr>
<td></td>
<td>Recall</td>
<td>FN을 줄이는 것이 중요한 경우</td>
</tr>
<tr>
<td></td>
<td>F1-score</td>
<td>불균형 데이터에서 종합 평가</td>
</tr>
<tr>
<td></td>
<td>Log Loss</td>
<td>확률 예측의 품질까지 평가</td>
</tr>
</tbody></table>
<hr>
<h2 id="quiz">Quiz</h2>
<h3 id="📌-퀴즈-1-집값-예측-모델">📌 퀴즈 1. 집값 예측 모델</h3>
<p>어떤 모델이 <strong>집값(단위: 만 원)</strong>을 예측하고 있다.
데이터에는 고가 주택이 일부 섞여 있어 <strong>아주 큰 오차가 간혹 발생</strong>한다.
비즈니스 담당자는</p>
<blockquote>
<p>“평균적으로 얼마나 틀리는지만 알면 된다”
고 말한다.</p>
</blockquote>
<p><strong>Q. 이 상황에서 가장 적절한 평가 지표는 무엇인가?</strong></p>
<hr>
<h3 id="📌-퀴즈-2-전력-수요-예측">📌 퀴즈 2. 전력 수요 예측</h3>
<p>전력 사용량을 예측하는 모델을 만들었다.
갑작스러운 폭염·한파 시 <strong>예측이 크게 빗나가는 경우</strong>가 있으며,
운영팀은 다음과 같이 요구한다.</p>
<blockquote>
<p>“큰 예측 실패는 절대 용납할 수 없다.”</p>
</blockquote>
<p><strong>Q. 이 요구사항을 가장 잘 반영하는 회귀 평가 지표는 무엇인가?</strong></p>
<hr>
<h3 id="📌-퀴즈-3-글로벌-매출-예측-비교">📌 퀴즈 3. 글로벌 매출 예측 비교</h3>
<p>A 국가의 매출은 <strong>수백만 원 단위</strong>,
B 국가는 <strong>수천억 원 단위</strong>다.
두 나라의 예측 모델 성능을 <strong>같은 기준으로 비교</strong>해야 한다.</p>
<p><strong>Q. 이 경우 가장 적절한 회귀 평가 지표는 무엇인가?</strong></p>
<hr>
<h3 id="📌-퀴즈-4-스팸-메일-필터링">📌 퀴즈 4. 스팸 메일 필터링</h3>
<p>스팸 메일 분류 모델을 운영 중이다.
스팸 메일 몇 개를 놓치는 것보다
<strong>정상 메일을 스팸으로 분류하는 것이 훨씬 치명적</strong>이다.</p>
<p><strong>Q. 이 상황에서 가장 우선적으로 봐야 할 분류 평가 지표는 무엇인가?</strong></p>
<hr>
<h3 id="📌-퀴즈-5-암-진단-모델">📌 퀴즈 5. 암 진단 모델</h3>
<p>암 진단 보조 모델을 개발 중이다.
의사는 다음과 같이 말한다.</p>
<blockquote>
<p>“암 환자를 정상으로 분류하는 일만은 절대 없어야 한다.”</p>
</blockquote>
<p>데이터는 <strong>정상 환자가 훨씬 많은 불균형 데이터</strong>다.</p>
<p><strong>Q. 이 상황에서 가장 중요한 평가 지표는 무엇인가?
(정확도는 적절한 지표일까?)</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 시계열 예측 모델 - RNN 이후의 시계열 모델들]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EC%98%88%EC%B8%A1-%EB%AA%A8%EB%8D%B8-RNN-%EC%9D%B4%ED%9B%84%EC%9D%98-%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%AA%A8%EB%8D%B8%EB%93%A4</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EC%98%88%EC%B8%A1-%EB%AA%A8%EB%8D%B8-RNN-%EC%9D%B4%ED%9B%84%EC%9D%98-%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%AA%A8%EB%8D%B8%EB%93%A4</guid>
            <pubDate>Tue, 06 Jan 2026 14:45:59 GMT</pubDate>
            <description><![CDATA[<p>RNN / LSTM / GRU는<strong>순서를 순차적으로 처리한다</strong>는 명확한 장점을 가졌지만, 그만큼 명확한 한계도 있었다.</p>
<ul>
<li>병렬화가 어렵다</li>
<li>시퀀스가 길어질수록 비효율적이다</li>
</ul>
<p>이 문제를 해결하려는 방향에서
다음과 같은 시계열 모델들이 등장한다.</p>
<br>

<h2 id="1-1d-cnn-convolutional-neural-network-for-time-series">1. 1D CNN (Convolutional Neural Network for Time Series)</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/e61b6a2c-00c7-4454-b8d3-ece5d2fd81d0/image.png" alt=""></p>
<br>

<h3 id="핵심-개념">핵심 개념</h3>
<blockquote>
<p><strong>시간 축 위에서 패턴을 “국소적으로” 훑는다</strong></p>
</blockquote>
<ul>
<li>이미지의 가로·세로 대신</li>
<li><strong>시간 축 하나만 놓고 컨볼루션</strong></li>
</ul>
<p>짧은 구간의 패턴을 반복적으로 감지한다.</p>
<br>


<h3 id="시계열을-어떻게-보나">시계열을 어떻게 보나?</h3>
<ul>
<li>시계열 = 1차원 신호</li>
<li>커널 = 짧은 시간 패턴 탐지기</li>
</ul>
<p>예:</p>
<ul>
<li>급격한 상승</li>
<li>특정 파형</li>
<li>반복되는 로컬 패턴</li>
</ul>
<br>


<h3 id="언제-쓰는가">언제 쓰는가?</h3>
<ul>
<li><strong>국소 패턴이 중요할 때</strong></li>
<li>전체 순서보다는 <strong>형태(shape)</strong> 가 중요할 때</li>
<li>시계열 길이가 길고 빠른 학습이 필요할 때</li>
</ul>
<p>예:</p>
<ul>
<li>센서 신호</li>
<li>진동 데이터</li>
<li>음성 파형</li>
<li>설비 이상 감지</li>
</ul>
<br>

<h2 id="2-tcn-temporal-convolutional-network">2. TCN (Temporal Convolutional Network)</h2>
<p>TCN은 <strong>시계열 전용으로 설계된 CNN</strong>이라고 보면 된다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/805a09dd-c9f8-48f8-8bbd-cb0d53a463ae/image.png" alt=""></p>
<br>


<h3 id="핵심-개념-1">핵심 개념</h3>
<blockquote>
<p><strong>CNN으로 RNN의 역할을 대체하자</strong></p>
</blockquote>
<p>TCN은 다음 두 가지를 결합한다.</p>
<ul>
<li><strong>Causal Convolution</strong>
→ 미래를 보지 않음</li>
<li><strong>Dilated Convolution</strong>
→ 적은 층으로 긴 과거를 커버</li>
</ul>
<br>


<h3 id="시계열을-어떻게-보나-1">시계열을 어떻게 보나?</h3>
<ul>
<li>순서를 지키면서</li>
<li>점점 더 넓은 과거를 컨볼루션으로 본다</li>
</ul>
<p>즉,</p>
<blockquote>
<p><strong>RNN처럼 과거를 누적하지만
순차 계산은 하지 않는다</strong></p>
</blockquote>
<br>


<h3 id="언제-쓰는가-1">언제 쓰는가?</h3>
<ul>
<li><strong>중·장기 의존성이 필요한 시계열</strong></li>
<li>RNN의 순차 계산이 느릴 때</li>
<li>병렬 학습이 중요한 환경</li>
</ul>
<p>예:</p>
<ul>
<li>시계열 예측</li>
<li>로그 분석</li>
<li>제어 시스템</li>
<li>산업 데이터</li>
</ul>
<br>

<h2 id="3-transformer">3. Transformer</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/a60a2508-9b50-4b41-b636-5ed0e958190e/image.png" alt=""></p>
<p>Transformer는 시계열뿐 아니라 자연어 처리 전반의 패러다임을 바꾼 모델이다.</p>
<br>


<h3 id="핵심-개념-2">핵심 개념</h3>
<blockquote>
<p><strong>모든 시점을 동시에 보고, 중요한 것만 선택한다</strong></p>
</blockquote>
<ul>
<li>순차 처리 없음</li>
<li>모든 시점 간의 관계를 한 번에 계산</li>
<li>중요도는 <strong>Attention</strong> 으로 결정</li>
</ul>
<br>


<h3 id="시계열을-어떻게-보나-2">시계열을 어떻게 보나?</h3>
<ul>
<li>시계열 = 토큰 시퀀스</li>
<li>각 시점은 서로를 직접 참조 가능</li>
<li>거리와 상관없이 관계를 학습</li>
</ul>
<p>즉,</p>
<blockquote>
<p><strong>“얼마나 오래됐는지”보다
“얼마나 중요한지”가 더 중요하다</strong></p>
</blockquote>
<br>


<h3 id="언제-쓰는가-2">언제 쓰는가?</h3>
<ul>
<li><strong>아주 긴 시계열</strong></li>
<li>복잡한 전역 패턴</li>
<li>다변량 시계열</li>
<li>충분한 데이터와 연산 자원 존재</li>
</ul>
<p>예:</p>
<ul>
<li>장기 예측</li>
<li>멀티 센서 시계열</li>
<li>복잡한 상호작용이 있는 데이터</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 시계열 예측 모델 - RNN 계열]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EC%98%88%EC%B8%A1-%EB%AA%A8%EB%8D%B8-RNN-%EA%B3%84%EC%97%B4</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EC%98%88%EC%B8%A1-%EB%AA%A8%EB%8D%B8-RNN-%EA%B3%84%EC%97%B4</guid>
            <pubDate>Tue, 06 Jan 2026 14:44:33 GMT</pubDate>
            <description><![CDATA[<p>머신러닝 기반 시계열 접근은 공통된 특징을 가진다.</p>
<ul>
<li>시계열 → 특징(feature) 추출</li>
<li>고정 길이 벡터 → 회귀 문제</li>
<li>시간 의존성은 <strong>간접적으로만</strong> 존재</li>
</ul>
<p>RNN 계열 모델은 이 접근에 대한 명확한 문제의식에서 출발한다.</p>
<blockquote>
<p><strong>“왜 시간 정보를 사람이 요약해야 하는가?”</strong>
<strong>“순서 자체를 모델이 직접 다루게 할 수는 없는가?”</strong></p>
</blockquote>
<p>그 결과가 RNN, 그리고 그 한계를 보완한 LSTM과 GRU다.</p>
<br>

<h2 id="1-rnn-recurrent-neural-network">1. RNN (Recurrent Neural Network)</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/31aae0d4-2ef6-4062-95c2-6e8050135396/image.png" alt=""></p>
<br>

<h3 id="기본-아이디어">기본 아이디어</h3>
<p>RNN은 이전 시점의 상태를 다음 시점으로 전달한다.</p>
<p>$$[
h_t = \phi(W_x x_t + W_h h_{t-1} + b)
]$$
$$[
y_t = W_y h_t
]$$</p>
<ul>
<li>(x_t): 시점 (t)의 입력</li>
<li>(h_t): 은닉 상태(hidden state)</li>
<li>(h_{t-1}): 과거 정보를 압축한 벡터</li>
</ul>
<blockquote>
<p><strong>과거 정보가 하나의 상태 벡터로 누적된다</strong></p>
</blockquote>
<br>

<h3 id="rnn이-시계열을-다루는-방식">RNN이 시계열을 다루는 방식</h3>
<ul>
<li>시계열 전체를 한 스텝씩 순차 처리</li>
<li>과거 → 은닉 상태에 압축</li>
<li>피처 추출을 사람이 아닌 <strong>모델이 수행</strong></li>
</ul>
<p>이는 슬라이딩 윈도우 + 회귀와 근본적으로 다르다.</p>
<br>

<h3 id="한계-gradient-vanishing--exploding">한계: Gradient Vanishing / Exploding</h3>
<p>RNN은 학습 시 <strong>역전파(BPTT)</strong> 를 사용한다.
이때 다음 문제가 발생한다.</p>
<ul>
<li>긴 시퀀스에서 기울기 소실</li>
<li>먼 과거 정보가 학습되지 않음</li>
</ul>
<p>결과적으로,</p>
<blockquote>
<p><strong>RNN은 이론적으로는 시계열을 기억하지만
실제로는 짧은 기억만 가능하다</strong></p>
</blockquote>
<br>
<br>

<h2 id="2-lstm-long-short-term-memory">2. LSTM (Long Short-Term Memory)</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/74a763b4-0f3e-4488-b7f2-5de0fa8d5c12/image.png" alt=""></p>
<p>RNN의 한계를 해결하기 위해 등장한 구조다.
핵심은 <strong>기억을 선택적으로 저장하고 삭제하는 메커니즘</strong>이다.</p>
<br>

<h3 id="lstm의-핵심-구성-요소">LSTM의 핵심 구성 요소</h3>
<p>LSTM은 두 개의 상태를 가진다.</p>
<ul>
<li><strong>Cell State ((c_t))</strong>: 장기 기억</li>
<li><strong>Hidden State ((h_t))</strong>: 단기 출력</li>
</ul>
<p>그리고 이를 제어하는 <strong>게이트(gate)</strong> 가 있다.</p>
<br>

<h3 id="게이트-구조">게이트 구조</h3>
<h4 id="forget-gate">Forget Gate</h4>
<p>$$[
f_t = \sigma(W_f [h_{t-1}, x_t])
]$$</p>
<ul>
<li>무엇을 잊을지 결정</li>
</ul>
<h4 id="input-gate">Input Gate</h4>
<p>$$[
i_t = \sigma(W_i [h_{t-1}, x_t])
]$$
$$[
\tilde{c}<em>t = \tanh(W_c [h</em>{t-1}, x_t])
]$$</p>
<ul>
<li>무엇을 새로 기억할지 결정</li>
</ul>
<h4 id="cell-update">Cell Update</h4>
<p>$$[
c_t = f_t \odot c_{t-1} + i_t \odot \tilde{c}_t
]$$</p>
<h4 id="output-gate">Output Gate</h4>
<p>$$[
o_t = \sigma(W_o [h_{t-1}, x_t])
]$$
$$[
h_t = o_t \odot \tanh(c_t)
]$$</p>
<br>

<h3 id="lstm-정리">LSTM 정리</h3>
<ul>
<li>기울기 소실 문제 완화</li>
<li>장기 의존성 학습 가능</li>
<li>불규칙한 시계열에서도 안정적</li>
<li>단, 구조가 복잡하고 연산량이 크다.</li>
</ul>
<br>

<h2 id="3-gru-gated-recurrent-unit">3 GRU (Gated Recurrent Unit)</h2>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/9bf0ae91-9764-4cbb-a2d4-11ec8972a783/image.png" alt=""></p>
<p>GRU는 LSTM의 단순화 버전이다.</p>
<h3 id="gru-구조">GRU 구조</h3>
<p>GRU는 하나의 상태만 유지한다.</p>
<p>$$[
z_t = \sigma(W_z [h_{t-1}, x_t]) \quad \text{(update gate)}
]$$
$$[
r_t = \sigma(W_r [h_{t-1}, x_t]) \quad \text{(reset gate)}
]$$</p>
<p>$$[
\tilde{h}<em>t = \tanh(W [r_t \odot h</em>{t-1}, x_t])
]$$</p>
<p>$$[
h_t = (1 - z_t) \odot h_{t-1} + z_t \odot \tilde{h}_t
]$$</p>
<br>

<h3 id="gru의-특징">GRU의 특징</h3>
<ul>
<li>LSTM보다 파라미터 적음</li>
<li>학습 속도 빠름</li>
<li>성능은 많은 경우 LSTM과 유사</li>
</ul>
<br>

<h2 id="4-rnn--lstm--gru-비교-도표">4. RNN / LSTM / GRU 비교 도표</h2>
<table>
<thead>
<tr>
<th>구분</th>
<th>RNN</th>
<th>LSTM</th>
<th>GRU</th>
</tr>
</thead>
<tbody><tr>
<td>장기 의존성</td>
<td>❌ 매우 약함</td>
<td>⭕ 강함</td>
<td>⭕ 비교적 강함</td>
</tr>
<tr>
<td>기울기 소실</td>
<td>❌ 심각</td>
<td>⭕ 완화</td>
<td>⭕ 완화</td>
</tr>
<tr>
<td>상태 구조</td>
<td>Hidden State 1개</td>
<td>Hidden + Cell</td>
<td>Hidden State 1개</td>
</tr>
<tr>
<td>게이트 수</td>
<td>없음</td>
<td>3개</td>
<td>2개</td>
</tr>
<tr>
<td>파라미터 수</td>
<td>적음</td>
<td>많음</td>
<td>중간</td>
</tr>
<tr>
<td>학습 속도</td>
<td>빠름</td>
<td>느림</td>
<td>중간</td>
</tr>
<tr>
<td>구현 복잡도</td>
<td>낮음</td>
<td>높음</td>
<td>중간</td>
</tr>
<tr>
<td>실무 사용 빈도</td>
<td>거의 없음</td>
<td>있음</td>
<td>매우 높음</td>
</tr>
</tbody></table>
<br>

<h2 id="5-언제-어떤-모델을-사용하는가">5. 언제 어떤 모델을 사용하는가</h2>
<h3 id="rnn을-쓰는-경우">RNN을 쓰는 경우</h3>
<ul>
<li>시퀀스 길이가 매우 짧을 때</li>
</ul>
<h3 id="lstm을-쓰는-경우">LSTM을 쓰는 경우</h3>
<ul>
<li>장기 패턴이 명확한 시계열</li>
<li>불규칙한 간격, 누락 데이터 존재</li>
<li>시계열 길이가 길고 복잡한 경우</li>
</ul>
<p>예:</p>
<ul>
<li>설비 로그</li>
<li>사용자 행동 시퀀스</li>
<li>금융 시계열</li>
</ul>
<h3 id="gru를-쓰는-경우">GRU를 쓰는 경우</h3>
<ul>
<li>LSTM이 과도하게 무거운 경우</li>
<li>데이터가 상대적으로 적을 때</li>
<li>빠른 실험이 필요한 경우</li>
</ul>
<br>

<hr>
<br>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 시계열 예측 모델 - 머신러닝]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EC%98%88%EC%B8%A1-%EB%AA%A8%EB%8D%B8-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EC%98%88%EC%B8%A1-%EB%AA%A8%EB%8D%B8-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D</guid>
            <pubDate>Tue, 06 Jan 2026 14:40:11 GMT</pubDate>
            <description><![CDATA[<p>머신러닝 모델은 기본적으로 다음 가정을 전제로 한다.</p>
<ul>
<li>입력 샘플들은 서로 독립(i.i.d.)</li>
<li>각 샘플은 고정 길이의 벡터</li>
<li>순서 정보는 구조적으로 존재하지 않음</li>
</ul>
<p>이 가정은 시계열 데이터와 정면으로 충돌한다.
따라서 머신러닝에서 시계열을 사용하려면,
<strong>시계열의 “시간적 구조”를 직접 모델에 맡기지 않는다.</strong></p>
<p>대신,</p>
<blockquote>
<p><strong>시계열의 성질을 사람이 해석 가능한 특징으로 추출한 뒤
이를 일반 회귀 문제로 변환한다.</strong></p>
</blockquote>
<br>

<h2 id="시계열-→-고정-길이-벡터로의-변환">시계열 → 고정 길이 벡터로의 변환</h2>
<p>시계열 ( {y_t} ) 는 길이가 가변적이고 순서 의존적이다.
머신러닝에 입력하려면 다음 조건을 만족해야 한다.</p>
<ul>
<li>길이가 고정된 벡터</li>
<li>각 시점이 하나의 샘플</li>
</ul>
<p>이를 위해 <strong>윈도우 기반 특징 추출(window-based feature extraction)</strong> 을 사용한다.</p>
<p>$$[
X_t = g(y_{t-k}, \dots, y_{t-1})
]$$</p>
<ul>
<li>$$(g(\cdot))$$: 특징 추출 함수</li>
<li>출력은 고정 차원의 벡터</li>
</ul>
<br>

<h2 id="통계적-특징">통계적 특징</h2>
<ul>
<li><p>이동 평균
$$[
\mu_t = \frac{1}{k}\sum_{i=1}^{k} y_{t-i}
]$$</p>
</li>
<li><p>이동 분산 / 표준편차</p>
</li>
<li><p>최소값, 최대값</p>
</li>
<li><p>분위수(quantile)</p>
</li>
</ul>
<br>

<h2 id="변화량-기반-특징">변화량 기반 특징</h2>
<h4 id="차분-기반-특징">차분 기반 특징</h4>
<p>$$[
\Delta y_{t-1} = y_{t-1} - y_{t-2}
]$$</p>
<h4 id="비율--증가율">비율 / 증가율</h4>
<p>$$[
\frac{y_{t-1} - y_{t-2}}{y_{t-2}}
]$$</p>
<p>이는 추세 방향성과 속도를 표현한다.</p>
<ul>
<li>상승 / 하강 국면</li>
<li>급격한 변화 감지</li>
</ul>
<br>

<h2 id="회귀-모델에서의-시계열">회귀 모델에서의 시계열</h2>
<p>최종적으로 위에 설명한 내용을 수식으로 쓰면 아래와 같이 나타낼 수 있다.</p>
<p>$$y_t = f(X_t)$$</p>
<ul>
<li><p>$$X_t$$: 시계열에서 추출된 특징 벡터</p>
</li>
<li><p>$$f$$: 일반 회귀 모델</p>
<ul>
<li>Linear / Ridge / Lasso</li>
<li>Random Forest</li>
<li>XGBoost / LightGBM</li>
<li>MLP</li>
</ul>
</li>
</ul>
<p><strong>장점</strong></p>
<ul>
<li>모델 선택이 자유롭다</li>
<li>해석 가능한 피처 설계 가능</li>
<li>데이터가 적어도 적용 가능</li>
<li>학습이 안정적</li>
</ul>
<p><strong>한계</strong></p>
<ul>
<li>장기 의존성은 직접 표현 불가<ul>
<li>윈도우 크기에 의존</li>
<li>멀리 있는 과거는 요약되거나 삭제됨</li>
</ul>
</li>
<li>피처 설계에 과도하게 의존<ul>
<li>사람이 무엇을 추출하느냐가 성능을 좌우</li>
<li>도메인 지식 필요</li>
</ul>
</li>
<li>“순서”는 간접적으로만 존재<ul>
<li>모델은 시간을 이해하지 않음</li>
<li>시간은 통계량으로 요약됨</li>
</ul>
</li>
</ul>
<br>]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] Feature Selection]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-Feature-Selection</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-Feature-Selection</guid>
            <pubDate>Sun, 04 Jan 2026 06:32:57 GMT</pubDate>
            <description><![CDATA[<h1 id="feature-selection">Feature Selection</h1>
<h2 id="part-1-feature-selection의-본질">Part 1. Feature Selection의 본질</h2>
<h3 id="11-정의">1.1 정의</h3>
<p>Feature Selection은 주어진 입력 변수 집합 $X = {x_1, x_2, \dots, x_p}$ 중에서 예측 목표 $y$에 유의미한 일부 $S \subseteq X$를 선택하는 과정이다.</p>
<p>이는 새로운 변수를 생성하는 Feature Engineering과 구분된다.</p>
<ul>
<li>Feature Selection: $X \rightarrow S \subseteq X$</li>
<li>Feature Engineering: $X \rightarrow X&#39;$ (차원, 의미 변경)</li>
</ul>
<h3 id="12-왜-필요한가">1.2 왜 필요한가</h3>
<ol>
<li><p><strong>차원의 저주 (Curse of Dimensionality)</strong>
차원 $p$가 증가할수록 샘플 공간의 부피는 지수적으로 증가한다.
모델은 제한된 샘플로 고차원 공간을 일반화해야 하므로 분산이 급격히 증가한다.</p>
</li>
<li><p><strong>분산 증가 (Variance Inflation)</strong>
선형 회귀에서 추정 분산은 다음과 같이 표현된다.</p>
</li>
</ol>
<p>$$
\mathrm{Var}(\hat{\beta}) = \sigma^2 (X^TX)^{-1}
$$</p>
<p>불필요하거나 상관된 feature가 많아질수록 $X^TX$는 ill-conditioned가 되며, 추정치 분산이 커진다.</p>
<ol start="3">
<li><strong>해석 가능성</strong>
모델이 사용하는 변수의 수가 줄어들수록 각 변수의 역할을 설명할 수 있다.</li>
</ol>
<hr>
<h2 id="part-2-filter-method">Part 2. Filter Method</h2>
<h3 id="21-개념">2.1 개념</h3>
<p>Filter Method는 <strong>모델과 독립적으로</strong> feature와 target 사이의 통계적 관계만을 이용해 변수를 선택한다.</p>
<p>핵심 가정:</p>
<blockquote>
<p>좋은 feature는 target과 개별적으로도 강한 연관성을 가진다.</p>
</blockquote>
<h3 id="22-주요-기법">2.2 주요 기법</h3>
<h4 id="221-분산-기반-선택-variance-threshold">2.2.1 분산 기반 선택 (Variance Threshold)</h4>
<p>분산이 0 또는 매우 작은 feature는 모든 샘플에서 거의 동일한 값을 가지므로 정보량이 적다.</p>
<p>$$
\mathrm{Var}(x_j) = \frac{1}{n}\sum_{i=1}^n (x_{ij}-\bar{x}_j)^2
$$</p>
<p>임계값 $\tau$보다 작은 feature는 제거한다.</p>
<p>한계:</p>
<ul>
<li>target과의 관계를 전혀 고려하지 않는다.</li>
</ul>
<h4 id="222-상관계수-기반-선택">2.2.2 상관계수 기반 선택</h4>
<p>연속형 변수에서 Pearson 상관계수:</p>
<p>$$
\rho(x_j, y) = \frac{\mathrm{Cov}(x_j,y)}{\sigma_{x_j}\sigma_y}
$$</p>
<p>$|\rho|$가 큰 feature를 선택한다.</p>
<p>주의점:</p>
<ul>
<li>비선형 관계를 포착하지 못함</li>
<li>다중공선성 문제 미해결</li>
</ul>
<h4 id="223-통계적-가설-검정">2.2.3 통계적 가설 검정</h4>
<ul>
<li>회귀: t-test, F-test</li>
<li>분류: Chi-square test</li>
</ul>
<p>귀무가설 $H_0$: feature와 target은 독립이다.</p>
<p>p-value가 작을수록 해당 feature는 target과 유의미한 관계를 가진다.</p>
<hr>
<h2 id="part-3-wrapper-method">Part 3. Wrapper Method</h2>
<h3 id="31-개념">3.1 개념</h3>
<p>Wrapper Method는 feature subset을 직접 모델에 적용하여 성능을 기준으로 선택한다.</p>
<p>$$
S^* = \arg\max_{S \subseteq X} \mathrm{Score}(f_S)
$$</p>
<p>여기서 $f_S$는 subset $S$로 학습한 모델이다.</p>
<h3 id="32-알고리즘">3.2 알고리즘</h3>
<h4 id="321-forward-selection">3.2.1 Forward Selection</h4>
<ol>
<li>$S = \emptyset$</li>
<li>모든 $x_j \notin S$에 대해 $S \cup {x_j}$ 평가</li>
<li>성능 최대 feature 추가</li>
<li>개선이 없을 때 종료</li>
</ol>
<p>복잡도:</p>
<ul>
<li>$O(p^2)$ 수준</li>
</ul>
<h4 id="322-backward-elimination">3.2.2 Backward Elimination</h4>
<ol>
<li>$S = X$</li>
<li>하나씩 제거하며 성능 평가</li>
<li>성능 저하 최소 feature 제거</li>
</ol>
<p>전제:</p>
<ul>
<li>초기 모델 학습 가능해야 함</li>
</ul>
<h4 id="323-rfe-recursive-feature-elimination">3.2.3 RFE (Recursive Feature Elimination)</h4>
<p>모델의 feature 중요도를 이용해 반복적으로 제거한다.</p>
<p>선형 모델 기준:</p>
<p>$$
\text{importance}(x_j) = |\hat{\beta}_j|
$$</p>
<hr>
<h2 id="part-4-embedded-method">Part 4. Embedded Method</h2>
<h3 id="41-개념">4.1 개념</h3>
<p>Embedded Method는 모델 학습 과정 내부에 feature 선택이 포함된다.</p>
<h3 id="42-l1-정규화-lasso">4.2 L1 정규화 (Lasso)</h3>
<p>목적 함수:</p>
<p>$$
\min_{\beta} |y - X\beta|_2^2 + \lambda |\beta|_1
$$</p>
<p>$L_1$ penalty는 계수를 정확히 0으로 만들 수 있다.</p>
<p>기하학적 해석 없이 보면:</p>
<ul>
<li>절댓값 패널티 → 작은 계수는 비용 대비 이득이 없어 제거됨</li>
</ul>
<h3 id="43-tree-기반-모델">4.3 Tree 기반 모델</h3>
<p>Decision Tree, Random Forest, Gradient Boosting은 분할 과정에서 feature를 선택한다.</p>
<p>불순도 감소 기준:</p>
<p>$$
\Delta I = I(parent) - \sum_k \frac{n_k}{n} I(child_k)
$$</p>
<p>중요한 feature는 더 자주, 더 큰 불순도 감소를 만든다.</p>
<p>주의:</p>
<ul>
<li>제거가 아닌 &quot;사용 빈도&quot; 기반</li>
<li>고유값 많은 feature 편향</li>
</ul>
<hr>
<h2 id="part-5-실무-적용-전략">Part 5. 실무 적용 전략</h2>
<ol>
<li>Variance / Correlation Filter로 1차 제거</li>
<li>L1 또는 Tree 기반 Embedded로 압축</li>
<li>필요 시 Wrapper로 미세 조정</li>
</ol>
<blockquote>
<p>Feature Selection은 정확도를 올리는 기법이 아니라, <strong>일반화와 해석을 통제하는 기법</strong>이다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 시계열 데이터 스케일링]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%8A%A4%EC%BC%80%EC%9D%BC%EB%A7%81</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%8A%A4%EC%BC%80%EC%9D%BC%EB%A7%81</guid>
            <pubDate>Sun, 04 Jan 2026 06:21:19 GMT</pubDate>
            <description><![CDATA[<h2 id="시계열-데이터에서의-스케일링">시계열 데이터에서의 스케일링</h2>
<h3 id="-일반-스케일링과-무엇이-다른가">— 일반 스케일링과 무엇이 다른가</h3>
<p>시계열 데이터에서 스케일링은
단순히 값의 크기를 맞추는 문제가 아니다.</p>
<blockquote>
<p><strong>시간 흐름을 보존하면서
미래 정보를 사용하지 않는 것</strong>이 핵심이다.</p>
</blockquote>
<hr>
<h2 id="1-시계열-스케일링의-핵심-제약-조건">1. 시계열 스케일링의 핵심 제약 조건</h2>
<h3 id="11-데이터-누수data-leakage-방지">1.1 데이터 누수(Data Leakage) 방지</h3>
<p>시계열 데이터는 다음이 절대 금지다.</p>
<pre><code class="language-text">❌ 전체 기간 평균 / 표준편차로 스케일링</code></pre>
<p>이 경우,</p>
<ul>
<li>미래 시점의 통계량이 과거 학습에 사용된다</li>
</ul>
<p>이는 모델 성능을 <strong>비현실적으로 부풀린다.</strong></p>
<hr>
<h3 id="12-올바른-기준">1.2 올바른 기준</h3>
<p>시계열 스케일링은 반드시 다음을 따른다.</p>
<ol>
<li>Train 구간에서만 통계량 계산</li>
<li>Validation / Test에 동일 적용</li>
<li>시간 순서 유지</li>
</ol>
<hr>
<h2 id="2-시계열-데이터에-적합한-스케일링-방법">2. 시계열 데이터에 적합한 스케일링 방법</h2>
<h3 id="21-standard-scaling-가장-일반적인-선택">2.1 Standard Scaling (가장 일반적인 선택)</h3>
<h4 id="언제-좋은가">언제 좋은가</h4>
<ul>
<li>값의 분포가 비교적 안정적</li>
<li>급격한 스케일 변화 없음</li>
<li>딥러닝 모델 사용 (LSTM, GRU, Transformer)</li>
</ul>
<h4 id="장점">장점</h4>
<ul>
<li>학습 안정성 우수</li>
<li>경사하강 수렴 속도 개선</li>
<li>대부분의 시계열 NN과 궁합이 좋음</li>
</ul>
<h4 id="주의점">주의점</h4>
<ul>
<li>이상치에 민감</li>
<li>Drift가 심한 경우 성능 저하 가능</li>
</ul>
<h3 id="22-min-max-scaling-범위-제한이-필요한-경우">2.2 Min-Max Scaling (범위 제한이 필요한 경우)</h3>
<h4 id="언제-좋은가-1">언제 좋은가</h4>
<ul>
<li>입력 범위가 명확히 제한된 모델</li>
<li>activation 함수가 민감한 경우</li>
<li>CNN 기반 시계열 모델</li>
</ul>
<h4 id="주의점-1">주의점</h4>
<ul>
<li>이상치에 매우 취약</li>
<li>미래에 새로운 max/min 등장 가능</li>
<li>Test 구간에서 범위 초과 발생 가능</li>
</ul>
<h3 id="23-robust-scaling-이상치가-많은-시계열">2.3 Robust Scaling (이상치가 많은 시계열)</h3>
<h4 id="언제-좋은가-2">언제 좋은가</h4>
<ul>
<li>센서 데이터</li>
<li>산업 장비 로그</li>
<li>간헐적인 스파이크 존재</li>
</ul>
<h4 id="장점-1">장점</h4>
<ul>
<li>중앙값 기준</li>
<li>IQR 기반 → 극단값 영향 최소화</li>
<li>이상치 제거가 어려운 경우 대안</li>
</ul>
<h3 id="24-log-변환--scaling-강력한-조합">2.4 Log 변환 + Scaling (강력한 조합)</h3>
<p>시계열 데이터는
분포가 심하게 치우친 경우가 많다.</p>
<p>이 경우 추천되는 흐름은 다음이다.</p>
<pre><code class="language-text">Log Transform → Standard / Robust Scaling</code></pre>
<h4 id="효과">효과</h4>
<ul>
<li>분산 안정화</li>
<li>급격한 스케일 변화 완화</li>
<li>학습 안정성 증가</li>
</ul>
<hr>
<h2 id="3-시계열-모델별-스케일링-권장-사항">3. 시계열 모델별 스케일링 권장 사항</h2>
<table>
<thead>
<tr>
<th>모델</th>
<th>스케일링 필요</th>
<th>추천</th>
</tr>
</thead>
<tbody><tr>
<td>ARIMA / SARIMA</td>
<td>△</td>
<td>로그 / 차분</td>
</tr>
<tr>
<td>Prophet</td>
<td>❌</td>
<td>내부 처리</td>
</tr>
<tr>
<td>Linear Regression</td>
<td>✔</td>
<td>Standard</td>
</tr>
<tr>
<td>XGBoost</td>
<td>❌</td>
<td>선택</td>
</tr>
<tr>
<td>LSTM / GRU</td>
<td>✔</td>
<td>Standard</td>
</tr>
<tr>
<td>Transformer</td>
<td>✔</td>
<td>Standard / Robust</td>
</tr>
</tbody></table>
<p>※ 트리 계열은 필수는 아니지만
입력 분산 안정 목적이라면 적용 가능</p>
<hr>
<h2 id="4-시계열-전용-스케일링-전략-고급">4. 시계열 전용 스케일링 전략 (고급)</h2>
<h3 id="41-window-based-scaling">4.1 Window-based Scaling</h3>
<ul>
<li>슬라이딩 윈도우마다 스케일 재계산</li>
<li>온라인 예측</li>
<li>Drift 대응 가능</li>
</ul>
<pre><code class="language-text">[t-n, t-1] 구간 기준 scaling → t 예측</code></pre>
<p>단점:</p>
<ul>
<li>구현 난이도 높음</li>
<li>재현성 관리 어려움</li>
</ul>
<h3 id="42-feature별-스케일링-분리">4.2 Feature별 스케일링 분리</h3>
<ul>
<li>원본 값</li>
<li>Lag 값</li>
<li>Rolling 값</li>
</ul>
<p>각각 다른 스케일링 적용 가능</p>
<hr>
<h2 id="5-시계열-스케일링에서-자주-발생하는-실수">5. 시계열 스케일링에서 자주 발생하는 실수</h2>
<ol>
<li>전체 기간 기준 scaling</li>
<li>Test 데이터로 scaler 재학습</li>
<li>Lag 생성 후 scaling 순서 오류</li>
<li>이상치 고려 없이 Min-Max 적용</li>
<li>변수별 스케일링 불일치</li>
</ol>
<hr>
<h2 id="6-권장-전처리-순서-시계열">6. 권장 전처리 순서 (시계열)</h2>
<pre><code class="language-text">Resampling
→ 결측치 처리
→ 이상치 처리
→ 분포 변환 (선택)
→ Lag / Rolling Feature 생성
→ Scaling (Train 기준)</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 비시계열 데이터 전처리]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%B9%84%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EB%B9%84%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 04 Jan 2026 06:16:28 GMT</pubDate>
            <description><![CDATA[<h2 id="1-비시계열-데이터">1. 비시계열 데이터</h2>
<h3 id="11-비시계열-데이터란-무엇인가">1.1 비시계열 데이터란 무엇인가</h3>
<p>비시계열 데이터는
<strong>각 관측값(행)이 시간 순서에 의존하지 않는 데이터</strong>를 의미한다.</p>
<p>대표적인 예시는 다음과 같다.</p>
<ul>
<li>고객 정보 테이블</li>
<li>실험 결과 데이터</li>
<li>설문조사 데이터</li>
<li>정적인 특성 기반 데이터셋</li>
</ul>
<p>이러한 데이터는 일반적으로
<strong>행 단위 분석</strong>을 전제로 한다.</p>
<h3 id="12-핵심-가정-iid">1.2 핵심 가정: i.i.d.</h3>
<p>비시계열 데이터 분석은
다음 가정을 암묵적으로 포함한다.</p>
<ul>
<li>Independent: 각 샘플은 서로 독립</li>
<li>Identically Distributed: 동일한 분포에서 생성됨</li>
</ul>
<p>이 가정이 성립하기 때문에 다음이 가능해진다.</p>
<ul>
<li>행 순서 무시</li>
<li>랜덤 셔플</li>
<li>통계량의 전역적 계산</li>
</ul>
<p>이 점이 시계열 데이터와의 가장 큰 차이다.</p>
<hr>
<h2 id="2-비시계열-데이터-전처리의-목적">2. 비시계열 데이터 전처리의 목적</h2>
<h3 id="21-목적의-재정의">2.1 목적의 재정의</h3>
<p>비시계열 데이터 전처리의 목적은 다음과 같다.</p>
<blockquote>
<p>데이터의 통계적 특성과 표현 방식을
모델의 가정과 계산 구조에 맞게 변환하는 것</p>
</blockquote>
<p>즉,</p>
<ul>
<li>정보를 유지하면서</li>
<li>불필요한 변동을 줄이고</li>
<li>모델이 학습하기 쉬운 형태로 바꾼다</li>
</ul>
<h3 id="22-전처리의-주요-대상">2.2 전처리의 주요 대상</h3>
<ol>
<li>결측치 (Missing Values)</li>
<li>이상치 (Outliers)</li>
<li>스케일 (Scale)</li>
<li>분포 (Distribution)</li>
<li>범주형 변수 (Categorical Variables)</li>
<li>특성 간 관계 (Feature Relationships)</li>
</ol>
<hr>
<h2 id="3-결측치-처리-missing-value-handling">3. 결측치 처리 (Missing Value Handling)</h2>
<h3 id="31-결측치의-유형">3.1 결측치의 유형</h3>
<p>비시계열 데이터에서 결측치는 다음 세 가지로 구분된다.</p>
<ol>
<li>MCAR (Missing Completely At Random)</li>
<li>MAR (Missing At Random)</li>
<li>MNAR (Missing Not At Random)</li>
</ol>
<p>이 구분은 <strong>처리 전략 선택의 근거</strong>가 된다.</p>
<h3 id="32-단순-삭제-listwise-deletion">3.2 단순 삭제 (Listwise Deletion)</h3>
<pre><code class="language-python">df = df.dropna()</code></pre>
<h4 id="언제-가능한가">언제 가능한가</h4>
<ul>
<li>MCAR 가정이 성립</li>
<li>결측 비율이 매우 낮음</li>
<li>데이터가 충분히 많음</li>
</ul>
<h4 id="문제점">문제점</h4>
<ul>
<li>데이터 손실</li>
<li>분포 왜곡 가능성</li>
</ul>
<h3 id="33-단순-대체-imputation">3.3 단순 대체 (Imputation)</h3>
<h4 id="평균--중앙값-대체">평균 / 중앙값 대체</h4>
<pre><code class="language-python">df[&#39;age&#39;] = df[&#39;age&#39;].fillna(df[&#39;age&#39;].median())</code></pre>
<ul>
<li>평균: 정규 분포에 적합</li>
<li>중앙값: 이상치에 강건</li>
</ul>
<p>이 방법은 <strong>분산을 인위적으로 줄인다</strong>는 한계가 있다.</p>
<h3 id="34-조건부-대체">3.4 조건부 대체</h3>
<ul>
<li>그룹별 평균</li>
<li>카테고리별 중앙값</li>
</ul>
<pre><code class="language-python">df[&#39;income&#39;] = df.groupby(&#39;job&#39;)[&#39;income&#39;].transform(
    lambda x: x.fillna(x.median())
)</code></pre>
<p>결측이 <strong>다른 변수와 관련</strong>되어 있을 때 유리하다.</p>
<h3 id="35-모델-기반-대체">3.5 모델 기반 대체</h3>
<ul>
<li>KNN Imputation</li>
<li>Regression Imputation</li>
</ul>
<p>이 방법은 정보 손실을 줄이지만,</p>
<ul>
<li>계산 비용 증가</li>
<li>과적합 위험</li>
</ul>
<p>이 존재한다.</p>
<hr>
<h2 id="4-이상치-처리-outlier-handling">4. 이상치 처리 (Outlier Handling)</h2>
<h3 id="41-비시계열-이상치의-정의">4.1 비시계열 이상치의 정의</h3>
<p>비시계열 데이터에서 이상치는
<strong>전체 분포 기준에서 극단적인 값</strong>이다.</p>
<p>시간 맥락은 고려하지 않는다.</p>
<h3 id="42-통계적-탐지-방법">4.2 통계적 탐지 방법</h3>
<h4 id="z-score">Z-score</h4>
<pre><code class="language-python">z = (df[&#39;x&#39;] - df[&#39;x&#39;].mean()) / df[&#39;x&#39;].std()
df = df[abs(z) &lt; 3]</code></pre>
<p>정규 분포 가정이 암묵적으로 포함된다.</p>
<h4 id="iqr">IQR</h4>
<pre><code class="language-python">Q1 = df[&#39;x&#39;].quantile(0.25)
Q3 = df[&#39;x&#39;].quantile(0.75)</code></pre>
<ul>
<li>분포 가정이 약함</li>
<li>실무에서 가장 많이 사용</li>
</ul>
<h3 id="43-이상치-처리-전략">4.3 이상치 처리 전략</h3>
<ul>
<li>제거</li>
<li>클리핑</li>
<li>로그 변환</li>
<li>별도 플래그 변수 생성</li>
</ul>
<p>모델과 목적에 따라 선택해야 한다.</p>
<hr>
<h2 id="5-스케일링-feature-scaling">5. 스케일링 (Feature Scaling)</h2>
<h3 id="51-왜-스케일링이-필요한가">5.1 왜 스케일링이 필요한가</h3>
<p>많은 모델은
<strong>변수의 절대 크기에 민감</strong>하다.</p>
<ul>
<li>거리 기반 모델 (KNN, K-Means)</li>
<li>경사하강 기반 모델 (Linear/Logistic Regression, NN)</li>
</ul>
<hr>
<h3 id="52-대표적인-스케일링-방법">5.2 대표적인 스케일링 방법</h3>
<h4 id="standardization">Standardization</h4>
<pre><code class="language-python">(x - mean) / std</code></pre>
<ul>
<li>평균 0, 분산 1</li>
<li>정규 분포 가정</li>
</ul>
<h4 id="min-max-scaling">Min-Max Scaling</h4>
<pre><code class="language-python">(x - min) / (max - min)</code></pre>
<ul>
<li>[0, 1] 범위</li>
<li>이상치에 민감</li>
</ul>
<h4 id="robust-scaling">Robust Scaling</h4>
<ul>
<li>중앙값 기준</li>
<li>IQR 사용</li>
<li>이상치에 강건</li>
</ul>
<hr>
<h2 id="6-분포-변환-distribution-transformation">6. 분포 변환 (Distribution Transformation)</h2>
<h3 id="61-왜-분포가-중요한가">6.1 왜 분포가 중요한가</h3>
<p>많은 통계 모델은
입력 변수의 분포에 대한 가정을 포함한다.</p>
<ul>
<li>선형 회귀: 잔차 정규성</li>
<li>통계 검정: 정규성 가정</li>
</ul>
<h3 id="62-로그-변환">6.2 로그 변환</h3>
<pre><code class="language-python">df[&#39;x_log&#39;] = np.log1p(df[&#39;x&#39;])</code></pre>
<ul>
<li>양의 치우친 분포 완화</li>
<li>분산 안정화</li>
</ul>
<h3 id="63-box-cox--yeo-johnson">6.3 Box-Cox / Yeo-Johnson</h3>
<ul>
<li>자동 분포 정규화</li>
<li>모델 기반 전처리에 유리</li>
</ul>
<hr>
<h2 id="7-범주형-변수-처리-categorical-encoding">7. 범주형 변수 처리 (Categorical Encoding)</h2>
<h3 id="71-범주형-변수의-문제점">7.1 범주형 변수의 문제점</h3>
<p>대부분의 모델은 문자형 데이터를 직접 처리하지 못한다.</p>
<h3 id="72-one-hot-encoding">7.2 One-Hot Encoding</h3>
<pre><code class="language-python">pd.get_dummies(df[&#39;category&#39;])</code></pre>
<ul>
<li>단순</li>
<li>차원 폭발 위험</li>
</ul>
<h3 id="73-ordinal-encoding">7.3 Ordinal Encoding</h3>
<ul>
<li>순서가 있는 범주에만 사용</li>
<li>잘못 쓰면 심각한 왜곡 발생</li>
</ul>
<h3 id="74-target-encoding">7.4 Target Encoding</h3>
<ul>
<li>평균 타깃값으로 치환</li>
<li>데이터 누수 위험</li>
<li>반드시 Train 기준 적용</li>
</ul>
<hr>
<h2 id="8-특성-선택과-차원-축소">8. 특성 선택과 차원 축소</h2>
<h3 id="81-불필요한-특성의-문제">8.1 불필요한 특성의 문제</h3>
<ul>
<li>노이즈 증가</li>
<li>과적합</li>
<li>해석력 저하</li>
</ul>
<hr>
<h3 id="82-filter--wrapper--embedded-방법">8.2 Filter / Wrapper / Embedded 방법</h3>
<ul>
<li>통계 기반</li>
<li>모델 기반</li>
<li>정규화 기반</li>
</ul>
<hr>
<h3 id="83-pca">8.3 PCA</h3>
<ul>
<li>상관된 변수 압축</li>
<li>해석력 감소</li>
<li>거리 기반 모델에서 효과적</li>
</ul>
<h2 id="9-비시계열-데이터-전처리-흐름-요약">9. 비시계열 데이터 전처리 흐름 요약</h2>
<ol>
<li>데이터 이해 및 분포 확인</li>
<li>결측치 유형 판단</li>
<li>이상치 탐지 및 처리</li>
<li>스케일 및 분포 변환</li>
<li>범주형 인코딩</li>
<li>특성 선택 / 차원 축소</li>
<li>데이터 분리 및 누수 방지</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[[기초] 시계열 데이터 전처리]]></title>
            <link>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC1</link>
            <guid>https://velog.io/@you_n_sunny/%EA%B8%B0%EC%B4%88-%EC%8B%9C%EA%B3%84%EC%97%B4-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%84%EC%B2%98%EB%A6%AC1</guid>
            <pubDate>Sun, 04 Jan 2026 06:14:09 GMT</pubDate>
            <description><![CDATA[<p>시계열 데이터 전처리와 통계적 진단</p>
<h2 id="1-시계열-데이터의-정의와-특성">1. 시계열 데이터의 정의와 특성</h2>
<h3 id="11-시계열-데이터란-무엇인가">1.1 시계열 데이터란 무엇인가</h3>
<p>시계열 데이터(Time Series Data)는
<strong>시간의 흐름에 따라 순차적으로 관측된 데이터</strong>를 의미한다.</p>
<p>수학적으로는 시간 ( t )에 대해 정의된 확률 변수 ( X_t )의 집합으로 표현된다.</p>
<p><img src="https://velog.velcdn.com/images/you_n_sunny/post/41340efe-710d-4db4-adbd-f85d2fe2dfa5/image.png" alt=""></p>
<p>이 정의에서 중요한 점은 다음 두 가지다.</p>
<ol>
<li>각 관측값은 확률 변수이다</li>
<li>관측값들은 서로 독립적이지 않다</li>
</ol>
<hr>
<h3 id="12-일반-데이터와의-차이점">1.2 일반 데이터와의 차이점</h3>
<p>일반적인 머신러닝 데이터는 다음 가정을 암묵적으로 가진다.</p>
<ul>
<li>각 샘플은 독립적이다 (i.i.d.)</li>
<li>행의 순서는 의미가 없다</li>
</ul>
<p>시계열 데이터는 이 가정을 모두 위반한다.</p>
<ul>
<li>시간 순서가 의미를 가진다</li>
<li>과거 값이 현재 값에 영향을 준다</li>
<li>데이터 분포가 시간에 따라 변할 수 있다</li>
</ul>
<p>따라서 시계열 데이터는
<strong>별도의 전처리와 분석 체계</strong>가 필요하다.</p>
<hr>
<h2 id="2-시계열-전처리의-목적">2. 시계열 전처리의 목적</h2>
<h3 id="21-전처리의-재정의">2.1 전처리의 재정의</h3>
<p>시계열 전처리는 단순히 데이터를 깨끗하게 만드는 작업이 아니다.</p>
<p>시계열 전처리의 목적은 다음과 같다.</p>
<blockquote>
<p>시간 구조를 유지한 채
데이터 생성 과정을 단순한 통계적 구조로 변환하여
모델이 학습 가능한 형태로 만드는 것</p>
</blockquote>
<hr>
<h3 id="22-시계열-전처리의-핵심-원칙">2.2 시계열 전처리의 핵심 원칙</h3>
<ol>
<li>시간 순서를 절대 훼손하지 않는다</li>
<li>결측과 이상을 삭제가 아닌 해석의 대상으로 본다</li>
<li>통계적 성질을 확인한 뒤 피처를 설계한다</li>
</ol>
<hr>
<h2 id="3-시간-인덱스와-샘플링-구조">3. 시간 인덱스와 샘플링 구조</h2>
<h3 id="31-시간-인덱스의-역할">3.1 시간 인덱스의 역할</h3>
<p>시계열 데이터에서 시간 인덱스는
단순한 컬럼이 아니라 <strong>좌표계</strong>에 해당한다.</p>
<pre><code class="language-python">df = df.sort_index()</code></pre>
<p>이 작업은 모든 시계열 분석의 전제 조건이다.</p>
<h3 id="32-불규칙-샘플링-문제">3.2 불규칙 샘플링 문제</h3>
<p>현실 데이터에서는 다음과 같은 상황이 빈번하다.</p>
<ul>
<li>기록 누락</li>
<li>이벤트 기반 수집</li>
<li>센서 지연</li>
</ul>
<p>이 경우 모델은 실제 현상이 아니라
<strong>샘플링 간격의 불규칙성</strong>을 학습하게 된다.</p>
<p>이를 해결하기 위해 시간 단위를 명시적으로 통일해야 한다.</p>
<hr>
<h2 id="4-resampling-리샘플링">4. Resampling (리샘플링)</h2>
<h3 id="41-resampling의-정의">4.1 Resampling의 정의</h3>
<p>Resampling은
<strong>시간 단위를 기준으로 데이터를 재집계하는 과정</strong>이다.</p>
<pre><code class="language-python">df = df.resample(&quot;1H&quot;).mean()</code></pre>
<h3 id="42-다운샘플링과-업샘플링">4.2 다운샘플링과 업샘플링</h3>
<h4 id="다운샘플링">다운샘플링</h4>
<ul>
<li>짧은 간격 → 긴 간격</li>
<li>노이즈 감소</li>
<li>장기 추세 강조</li>
</ul>
<h4 id="업샘플링">업샘플링</h4>
<ul>
<li>긴 간격 → 짧은 간격</li>
<li>결측치 증가</li>
<li>보간 필수</li>
</ul>
<p>Resampling은 항상
<strong>결측치 처리 전략과 함께 설계</strong>되어야 한다.</p>
<hr>
<h2 id="5-시계열-결측치-처리">5. 시계열 결측치 처리</h2>
<h3 id="51-시계열에서-결측의-의미">5.1 시계열에서 결측의 의미</h3>
<p>시계열에서 결측은
“값이 0”이 아니라
<strong>해당 시점에 관측이 이루어지지 않았음</strong>을 의미한다.</p>
<p>따라서 단순 삭제는
시간 구조를 붕괴시킨다.</p>
<hr>
<h3 id="52-선형-보간-linear-interpolation">5.2 선형 보간 (Linear Interpolation)</h3>
<pre><code class="language-python">df[&#39;value&#39;] = df[&#39;value&#39;].interpolate(method=&#39;linear&#39;)</code></pre>
<ul>
<li>연속적인 변화 가정</li>
<li>짧은 결측 구간에 적합</li>
<li>물리량 데이터에 자주 사용</li>
</ul>
<h3 id="53-이전-값-채우기-forward-fill">5.3 이전 값 채우기 (Forward Fill)</h3>
<pre><code class="language-python">df[&#39;value&#39;] = df[&#39;value&#39;].ffill()</code></pre>
<ul>
<li>상태 유지 가정</li>
<li>설정 값, 장비 모드에 적합</li>
<li>장기 결측 시 왜곡 가능</li>
</ul>
<hr>
<h2 id="6-이상치-처리">6. 이상치 처리</h2>
<h3 id="61-시계열-이상치의-특성">6.1 시계열 이상치의 특성</h3>
<p>시계열 이상치는 다음 중 하나일 수 있다.</p>
<ul>
<li>측정 오류</li>
<li>시스템 상태 변화</li>
<li>실제 이벤트 발생</li>
</ul>
<p>따라서 이상치는
무조건 제거 대상이 아니다.</p>
<h3 id="62-통계적-방법의-한계">6.2 통계적 방법의 한계</h3>
<p>IQR, z-score는
시간 순서를 고려하지 않는다.</p>
<p>실무에서는 이상치를 제거하기보다
<strong>이상치 여부를 피처로 활용</strong>하는 경우가 많다.</p>
<hr>
<h2 id="7-정상성-stationarity">7. 정상성 (Stationarity)</h2>
<h3 id="71-정상성의-정의">7.1 정상성의 정의</h3>
<p>정상성이란
시계열의 통계적 성질이 시간에 따라 변하지 않는 상태를 의미한다.</p>
<ul>
<li>평균 일정</li>
<li>분산 일정</li>
<li>자기상관 구조 일정</li>
</ul>
<h3 id="72-정상성의-중요성">7.2 정상성의 중요성</h3>
<ul>
<li>대부분의 시계열 모델은 정상성 가정</li>
<li>비정상 시계열은 예측 불안정</li>
<li>과거 데이터가 미래를 설명하지 못함</li>
</ul>
<h3 id="73-정상성-검정-adf-test">7.3 정상성 검정 (ADF Test)</h3>
<pre><code class="language-python">from statsmodels.tsa.stattools import adfuller
adfuller(df[&#39;value&#39;])[1]</code></pre>
<ul>
<li>p-value &lt; 0.05 → 정상</li>
<li>p-value ≥ 0.05 → 비정상</li>
</ul>
<hr>
<h2 id="8-차분-differencing">8. 차분 (Differencing)</h2>
<h3 id="81-차분의-의미">8.1 차분의 의미</h3>
<p>차분은
시계열을 값의 문제에서
<strong>변화량의 문제로 변환</strong>한다.</p>
<pre><code class="language-python">df[&#39;diff&#39;] = df[&#39;value&#39;].diff()</code></pre>
<h3 id="82-차분의-효과">8.2 차분의 효과</h3>
<ul>
<li>추세 제거</li>
<li>정상성 확보</li>
<li>자기상관 구조 단순화</li>
</ul>
<hr>
<h2 id="9-추세와-계절성-분해">9. 추세와 계절성 분해</h2>
<h3 id="91-시계열-구성-요소">9.1 시계열 구성 요소</h3>
<p>시계열은 일반적으로 다음으로 구성된다.</p>
<ul>
<li>추세 (Trend)</li>
<li>계절성 (Seasonality)</li>
<li>불규칙 성분 (Residual)</li>
</ul>
<h3 id="92-시계열-분해">9.2 시계열 분해</h3>
<pre><code class="language-python">from statsmodels.tsa.seasonal import seasonal_decompose
seasonal_decompose(df[&#39;value&#39;], period=24)</code></pre>
<p>이를 통해
시계열 구조를 시각적으로 확인할 수 있다.</p>
<hr>
<h2 id="10-자기상관-분석">10. 자기상관 분석</h2>
<h3 id="101-자기상관의-개념">10.1 자기상관의 개념</h3>
<p>자기상관은 현재 값과 과거 값 사이의 상관 관계다.</p>
<h3 id="102-acf--pacf">10.2 ACF / PACF</h3>
<ul>
<li>ACF: 전체 자기상관 구조</li>
<li>PACF: 순수한 lag 영향</li>
</ul>
<p>이는 Lag Feature 설계의 기준이 된다.</p>
<hr>
<h2 id="11-lag-및-rolling-feature">11. Lag 및 Rolling Feature</h2>
<h3 id="111-lag-feature">11.1 Lag Feature</h3>
<pre><code class="language-python">df[&#39;lag_1&#39;] = df[&#39;value&#39;].shift(1)</code></pre>
<p>과거 정보를
명시적 입력 변수로 변환한다.</p>
<h3 id="112-rolling-feature">11.2 Rolling Feature</h3>
<pre><code class="language-python">df[&#39;rolling_mean&#39;] = df[&#39;value&#39;].rolling(7).mean()</code></pre>
<p>최근 구간의 상태를 요약한다.</p>
<hr>
<h2 id="12-정리">12. 정리</h2>
<p>이 장에서 다룬 시계열 전처리는
다음 흐름으로 정리된다.</p>
<ol>
<li>시간 인덱스 정렬</li>
<li>Resampling</li>
<li>결측치 처리</li>
<li>이상치 해석</li>
<li>정상성 진단</li>
<li>차분 및 변환</li>
<li>자기상관 분석</li>
<li>피처 엔지니어링</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>