<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>새연님의 블로그</title>
    <link>https://idea79392.tistory.com/</link>
    <description>sybear02님의 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Wed, 5 Aug 2026 01:55:56 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>sybear02</managingEditor>
    <item>
      <title>[개발 일지] IT Trend Curator (v0.1)</title>
      <link>https://idea79392.tistory.com/18</link>
      <description>&lt;h1&gt;v0.1 — IT Trend Curator&lt;/h1&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;방식: &lt;strong&gt;바이브 코딩 (Vibe Coding)&lt;/strong&gt; — Cursor + AI 어시스턴트와 협업하여 설계·구현·문서화를 진행&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;1. 왜 이걸 구현했는가&lt;/h2&gt;
&lt;p&gt;요즘 AI·백엔드 관련 기술이 빠르게 바뀌어서, 글로만 읽기보다 &lt;strong&gt;직접 만들어 보며 빠르게 익히고 싶었다.&lt;/strong&gt;&lt;br&gt;그래서 해외 IT 유튜브·아티클 링크를 넣으면 핵심을 요약·번역해 마크다운 초안까지 만들어 주는 &lt;strong&gt;IT Trend Curator&lt;/strong&gt;를 제작했다.&lt;/p&gt;
&lt;p&gt;평소에도 관심 있는 영상을 모아두기만 하고 소화하지 못하는 경우가 많아서,&lt;br&gt;직접 쓰는 도구로 만들면 학습과 실사용을 같이 가져갈 수 있다고 생각했다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. 이번 버전에서 중점적으로 생각한 것&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;MVP 파이프라인&lt;/strong&gt;&lt;br&gt;URL → 추출 → 요약/번역/마크다운 → 파일 저장까지 한 흐름으로 동작하게 구현했다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;백엔드 Job 처리&lt;/strong&gt;&lt;br&gt;FastAPI로 Job을 받고, SQLite Job Store로 상태를 관리하며, Worker가 작업을 가져와 실패 시 재시도하도록 구성했다.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;품질 평가&lt;/strong&gt;&lt;br&gt;LLM-as-a-Judge로 faithfulness/coverage/clarity/usefulness를 1~5점 수치화했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;3. 기존과 다른 점&lt;/h2&gt;
&lt;p&gt;ChatGPT에 링크를 붙여 넣거나, 유튜브/브라우저 요약 확장처럼 &lt;strong&gt;한 번 요약만 해주는 도구&lt;/strong&gt;는 이미 많다.&lt;br&gt;IT Trend Curator는 “요약 결과”보다 &lt;strong&gt;링크부터 초안 저장까지를 파이프라인으로 돌리는 쪽&lt;/strong&gt;에 가깝다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;기존(챗봇·요약 확장 등)&lt;/th&gt;
&lt;th&gt;이 프로젝트&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;처리 방식&lt;/td&gt;
&lt;td&gt;대화창에서 한 번 요청&lt;/td&gt;
&lt;td&gt;Job 등록 → Worker가 비동기로 처리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;프롬프트&lt;/td&gt;
&lt;td&gt;보통 한 번에 요약&lt;/td&gt;
&lt;td&gt;요약 → 번역·정제 → 마크다운으로 단계 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;결과물&lt;/td&gt;
&lt;td&gt;채팅 답변으로 끝&lt;/td&gt;
&lt;td&gt;&lt;code&gt;output/posts/&lt;/code&gt; 마크다운 초안 파일로 저장&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;품질/비용&lt;/td&gt;
&lt;td&gt;눈으로만 확인하는 경우가 많음&lt;/td&gt;
&lt;td&gt;Judge 점수 + latency·토큰·추정 비용을 기록&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;실패 대응&lt;/td&gt;
&lt;td&gt;다시 물어보는 식&lt;/td&gt;
&lt;td&gt;Job 상태 관리 + 재시도&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;즉, 비슷한 “링크 요약” 기능이어도&lt;br&gt;&lt;strong&gt;비동기 Job · 단계별 AI 체이닝 · 품질/비용 계측 · 파일 산출&lt;/strong&gt;까지 묶여 있는 점이 다르다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. 기술 스택&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;구분&lt;/th&gt;
&lt;th&gt;선택&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;언어&lt;/td&gt;
&lt;td&gt;Python 3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API&lt;/td&gt;
&lt;td&gt;FastAPI + Uvicorn&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job/상태&lt;/td&gt;
&lt;td&gt;SQLite (pending → running → succeeded/failed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker&lt;/td&gt;
&lt;td&gt;별도 프로세스 폴링 + tenacity 재시도&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;추출&lt;/td&gt;
&lt;td&gt;&lt;code&gt;youtube-transcript-api&lt;/code&gt;, httpx + BeautifulSoup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLM&lt;/td&gt;
&lt;td&gt;OpenAI 호환 Chat Completions (&lt;code&gt;openai&lt;/code&gt; SDK)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;품질&lt;/td&gt;
&lt;td&gt;LLM-as-a-Judge (JSON 루브릭)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;산출물&lt;/td&gt;
&lt;td&gt;&lt;code&gt;output/posts/*.md&lt;/code&gt;, &lt;code&gt;output/metrics/*.json&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;5. 핵심 기능&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;POST /jobs&lt;/code&gt; : URL(및 optional fallback 텍스트) 등록 → &lt;code&gt;job_id&lt;/code&gt; 반환&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GET /jobs/{id}&lt;/code&gt; : 상태·에러·메트릭 조회&lt;/li&gt;
&lt;li&gt;Worker: pending Job claim → 파이프라인 실행 → 성공 저장 / 실패 시 재큐잉&lt;/li&gt;
&lt;li&gt;AI 체이닝 3단계: &lt;strong&gt;summarize → translate_refine → markdown_format&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Judge: 요약 품질 4축 점수 + 평균&lt;/li&gt;
&lt;li&gt;메트릭: 단계별 latency, 토큰 수, 추정 비용(USD)&lt;/li&gt;
&lt;li&gt;CLI: &lt;code&gt;submit&lt;/code&gt; / &lt;code&gt;status&lt;/code&gt; / &lt;code&gt;wait&lt;/code&gt; / &lt;code&gt;run&lt;/code&gt;(동기 디버그)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;6. 어떻게 개발했는가&lt;/h2&gt;
&lt;h3&gt;6.1 설계 선택&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Redis 대신 &lt;strong&gt;SQLite Job Store&lt;/strong&gt;를 택한 이유:&lt;br&gt;로컬에서 바로 돌릴 수 있고, 그래도 &lt;code&gt;claim_next&lt;/code&gt; / 상태머신 / 재시도 패턴은 동일하게 설명 가능.&lt;/li&gt;
&lt;li&gt;프롬프트를 한 방에 넣지 않고 &lt;strong&gt;3단계로 분리&lt;/strong&gt;:&lt;br&gt;실패 지점 추적·토큰 비용 분해·단계별 개선이 쉽도록.&lt;/li&gt;
&lt;li&gt;발행은 Tistory API에 의존하지 않고 &lt;strong&gt;마크다운 draft 저장&lt;/strong&gt;부터:&lt;br&gt;외부 플랫폼 정책 변경에 덜 흔들리게.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;6.2 구현 순서&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;설정/모델/&lt;code&gt;JobStore&lt;/code&gt; (상태 전이의 뼈대)&lt;/li&gt;
&lt;li&gt;추출 모듈 (YouTube → 웹 → fallback)&lt;/li&gt;
&lt;li&gt;LLM 클라이언트 + 사용량 로깅&lt;/li&gt;
&lt;li&gt;AI 체이닝 / Judge / publish&lt;/li&gt;
&lt;li&gt;pipeline 조립 → worker → FastAPI/CLI&lt;/li&gt;
&lt;li&gt;README + 본 개발일지&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;6.3 바이브 코딩에서 의식한 점&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;코드와 문서에 &lt;strong&gt;Vibe Coding&lt;/strong&gt;을 명시해, 학습/협업 방식까지 투명하게 남긴다.&lt;/li&gt;
&lt;li&gt;측정 가능한 것(지연, 토큰, Judge 점수)만 메트릭으로 남긴다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;7. 결과 / 수치&lt;/h2&gt;
&lt;p&gt;v0.1 시점에는 실제 API 측정값이 없었다.  &lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;8. 다음 버전에서 할 일&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;실제 샘플 5~10건으로 메트릭/Judge 점수 표 정리&lt;/li&gt;
&lt;li&gt;추출 실패 케이스별 fallback UX 개선 (에러 메시지 표준화)&lt;/li&gt;
&lt;li&gt;(여유 있으면) Job 목록 API / 간단 성공률 리포트&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;폴더 맵 (v0.1)&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;it-trend-curator/
  app/
    main.py        # FastAPI
    worker.py      # Job worker
    cli.py
    store.py       # SQLite job queue
    extract.py
    llm.py
    chain.py
    evaluate.py
    pipeline.py
    publish.py
  docs/
    DEVLOG_GUIDE.md
    devlog/v0.1_IT_Trend_Curator.md
  output/posts/
  output/metrics/&lt;/code&gt;&lt;/pre&gt;</description>
      <category>개발 일지</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/18</guid>
      <comments>https://idea79392.tistory.com/18#entry18comment</comments>
      <pubDate>Mon, 3 Aug 2026 23:26:32 +0900</pubDate>
    </item>
    <item>
      <title>[학습 노트] ForenShield 모델 원리</title>
      <link>https://idea79392.tistory.com/17</link>
      <description>&lt;h1&gt;딥페이크·위변조 모델이 보는 것&lt;/h1&gt;
&lt;p&gt;이 글은 부트캠프 최종 프로젝트 ForenShield에서 쓴 딥페이크·위변조 탐지 모듈이 각각 어떤 신호를 보는지 정리한 학습 노트다. Late Fusion으로 점수를 합치는 과정은 기술 회고로 따로 남겨 두었고, 여기서는 모듈·레인의 역할 구분에 초점을 둔다.&lt;/p&gt;
&lt;h2&gt;프로젝트와 동작 원리&lt;/h2&gt;
&lt;p&gt;ForenShield는 영상·이미지 위변조 분석과 증거 관리를 위한 딥페이크·디지털 포렌식 플랫폼이다. 나는 AI 딥페이크 탐지와 백엔드 연동을 중심으로 담당했고, 나온 점수를 Late Fusion에 넣어 서비스로 넘기는 쪽까지 작업했다. 학습·파인튜닝 파이프 전체를 혼자 돌렸다기보다, 각 모듈이 내는 점수의 의미를 맞춰 두는 일이 중요했다.&lt;/p&gt;
&lt;p&gt;점수를 합치는 이야기만 보면 “모델이 여러 개”로 끝나기 쉽다. 실제 구조는 보는 신호가 다른 레인을 나눠 둔 쪽에 가깝다. 딥페이크 레인은 사람·얼굴·신체가 AI로 합성·교체됐는지를 묻고, 위변조 레인은 국소 편집·잘라붙이기 같은 편집 흔적이 있는지를 묻는다. 신호가 다르면 오탐 패턴도 다르기 때문에, 화면과 점수 레인도 나눴다.&lt;/p&gt;
&lt;p&gt;딥페이크 레인은 다시 세 축으로 나뉜다. 얼굴 프레임의 공간 단서는 Xception, 클립의 시간 일관성은 TimeSformer, 프레임 쌍의 광학 흐름은 GMFlow와 그 위 점수 head다. forgery 쪽은 TruFor 등을 위변조 레인에 두는 흐름으로 이어졌다. soft-gate로 딥페이크 모듈 일부가 약해도, 위변조처럼 가능한 경로는 이어서 돌릴 수 있게 했다.&lt;/p&gt;
&lt;h2&gt;딥페이크 레인&lt;/h2&gt;
&lt;h3&gt;Xception — 얼굴 단위 공간 단서&lt;/h3&gt;
&lt;p&gt;한 장의(또는 크롭한) 얼굴 프레임에서 CNN이 공간 특징을 보고 fake/real 쪽 점수를 낸다. “이 얼굴 패치가 합성·교체 분포에 가까운가”에 가깝다. 얼굴이 잘 잡히는 구간에서는 가장 직관적인 단서라, Late Fusion에서도 얼굴 단위 CNN 점수는 핵심으로 쓴다.&lt;/p&gt;
&lt;p&gt;한계도 분명하다. 얼굴이 없거나 너무 작으면 이 모듈을 제대로 돌리기 어렵다. 그때를 soft-gate로 두고, 실패와 “일부만 돌았다”를 구분한다. 한 프레임만 보면 시간축 조작이나 움직임 이상은 놓치기 쉬워서 TimeSformer·GMFlow와 역할을 나눈다. 학습은 사전학습 가중치를 가져와 ff1k → celeb1k 순으로 파인튜닝하는 흐름이었고, Golden-200은 벤치용으로 학습에 넣지 않았다.&lt;/p&gt;
&lt;h3&gt;TimeSformer — 클립의 시간 단서&lt;/h3&gt;
&lt;p&gt;여러 프레임을 클립으로 묶어 transformer 계열로 시간축 일관성을 본다. 한 장의 텍스처보다 “이어지는 얼굴·장면이 자연스러운가”에 가깝다. 딥페이크는 프레임마다 조금씩 어색한 경우가 있어, 공간 CNN만으로는 약한 구간을 클립 단위 신호가 보완한다.&lt;/p&gt;
&lt;p&gt;클립을 만들 만큼 얼굴·구간이 확보돼야 한다. soft-gate나 모듈 스킵이 나면 Fusion에서는 TimeSformer 쪽 가중을 줄이거나 soft discount처럼 그 점수를 언제 덜 믿을지를 규칙으로 둔다. 모델이 항상 같다가 아니라, 이번 입력에서 이 모듈이 유효한가가 점수 해석에 들어간다. Xception과 같이 사전학습 후 ff1k → celeb1k 파인튜닝 흐름을 썼다.&lt;/p&gt;
&lt;h3&gt;GMFlow — 광학 흐름과 점수 head&lt;/h3&gt;
&lt;p&gt;GMFlow 본체는 “이 영상이 fake인가”를 바로 분류하는 네트워크가 아니다. 인접 프레임 쌍에서 픽셀이 어디로 움직였는지(optical flow)를 추정하는 사전학습 모델이다. 그 위에 평균·최대·표준편차, temporal jitter, 공간 불일치 같은 flow 통계를 쌓고, 그 숫자로 fake/real 쪽 점수를 붙인다.&lt;/p&gt;
&lt;p&gt;점수를 붙이는 방식은 두 갈래였다. 휴리스틱은 사람이 정한 규칙·기준선으로 motion_anomaly_score를 만든다. 빠른 baseline이지만 단독 판정용으로는 약했다. learned head는 같은 flow 통계를 feature로 두고, 라벨이 있는 영상으로 Random Forest 같은 얕은 분류기를 학습해 fake_score를 낸다. 여기서 learned는 GMFlow 신경망 전체를 다시 학습했다는 뜻이 아니라, flow 위에 얹은 head가 패턴을 배웠다는 뜻에 가깝다.&lt;/p&gt;
&lt;p&gt;운영에서는 휴리스틱보다 learned head 쪽이 나았고, Optical 대표로는 RAFT·PWC-Net 등과 비교한 뒤 GMFlow를 남겼다. 그래도 Xception·TimeSformer만큼 단독으로 쓰기엔 약해서, Fusion에서는 가중을 낮게 두고 보조 신호·veto/rescue 쪽에 가깝게 썼다. 같은 딥페이크 레인이어도 Xception·TimeSformer는 분류기에 가깝게 점수를 내고, GMFlow는 움직임 이상을 숫자로 뽑은 뒤 그 위에 점수를 얹는다는 점에서 입출력 의미가 다르다.&lt;/p&gt;
&lt;h2&gt;위변조 레인&lt;/h2&gt;
&lt;p&gt;위변조(forgery)는 “이 얼굴이 AI 합성인가”보다 국소·편집·조작 흔적이 어디에 있는지에 가깝다. ForenShield에서는 TruFor 등을 위변조 레인에 두는 흐름으로 이어졌고, 사전학습 위에 비디오 forgery 데이터로 파인튜닝하는 쪽이다.&lt;/p&gt;
&lt;p&gt;딥페이크 모듈은 신원·얼굴·신체 합성/교체에 관심이 크고 얼굴 게이트·크롭에 의존하는 편이 크다. 위변조는 잘라붙이기·국소 편집·조작 가능성 쪽에 가깝고, 얼굴이 약해도 편집 단서가 남을 수 있다. soft-gate로 딥페이크 쪽이 약해도 위변조 경로를 이어 돌리는 이유다. 점수를 하나로만 보여 주면 사용자가 “가짜”의 종류를 구분하기 어렵고, 레인을 나누면 soft-gate 이후에도 위변조 결과가 남는 이유를 설명하기 쉬워진다.&lt;/p&gt;
&lt;p&gt;TruFor 내부 아키텍처·손실·픽셀 단위 맵의 학술 디테일은 분석 엔진 담당 쪽이 더 깊다. 학습 노트로 남기는 것은 제품 관점의 역할이다. 위변조 레인은 딥페이크 fusion의 보조 버튼이 아니라, 다른 질문을 하는 탐지 경로다.&lt;/p&gt;
&lt;h2&gt;점수로 만날 때&lt;/h2&gt;
&lt;p&gt;모듈이 낸 점수는 나중에 Late Fusion·통합 riskScore 규칙으로 만난다. 모듈 점수는 그 신호가 이번 입력에서 얼마나 수상한가이고, Fusion·Veto·Rescue는 그 점수를 최종 판정에 얼마나·언제 반영할지다. 오버레이는 fusion으로 합친 히트맵이 아니라 모듈별 단서를 토글로 보여 주는 쪽에 가깝다.&lt;/p&gt;
&lt;p&gt;그래서 “GMFlow가 높다”와 “최종 riskScore가 높다”는 같은 문장이 아니다. 화면의 붉은 영역도 Grad-CAM처럼 전 모듈 XAI를 돌렸다는 뜻이라기보다, 탐지·로컬라이즈 결과를 시각화한 쪽에 가깝다. 가중·cap·현장 FP 튜닝의 세부 과정은 Late Fusion 기술 회고로 넘긴다.&lt;/p&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;ForenShield의 탐지 쪽을 “모델이 여러 개”로만 기억하면 설명이 금방 섞인다. 딥페이크는 얼굴·시간·움직임처럼 합성/교체를 나눠 보고, 위변조는 편집 흔적을 다른 레인에서 보며, Fusion은 그 점수들을 언제 믿을지 정하는 판정 정책이다.&lt;/p&gt;
&lt;p&gt;모델을 더 늘리는 이야기와, 지금 있는 신호가 무슨 질문의 답인지를 먼저 고정하는 이야기는 결이 다르다. 이 노트는 후자를 위한 정리였다.&lt;/p&gt;
&lt;p&gt;관련 코드: &lt;a href=&quot;https://github.com/owhat02/ai-forensic&quot;&gt;https://github.com/owhat02/ai-forensic&lt;/a&gt;&lt;/p&gt;</description>
      <category>학습 노트</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/17</guid>
      <comments>https://idea79392.tistory.com/17#entry17comment</comments>
      <pubDate>Sun, 2 Aug 2026 22:57:53 +0900</pubDate>
    </item>
    <item>
      <title>[학습 노트] ForenShield &amp;mdash; 백엔드에서 구현한 것</title>
      <link>https://idea79392.tistory.com/16</link>
      <description>&lt;h1&gt;ForenShield — 백엔드에서 구현한 것&lt;/h1&gt;
&lt;p&gt;ForenShield 백엔드(backend-forensic)에서 코드로 붙인 기능을 중심으로 정리한 노트다. Spring Boot 쪽이고, AI GPU Worker와는 RabbitMQ·S3·결과 JSON으로 연결된다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;1. AI 분석 연동&lt;/h2&gt;
&lt;p&gt;프론트가 AI를 오래 붙잡지 않도록, 백엔드가 접수 → 큐 → 결과 저장 → 조회를 맡는다.&lt;/p&gt;
&lt;h3&gt;접수와 큐&lt;/h3&gt;
&lt;p&gt;분석 요청이 오면 영상·메타를 S3에 두고 RabbitMQ로 GPU job을 보낸다. 추론을 동기적으로 끝까지 돌리는 게 아니라, 일을 넘기고 상태를 관리하는 쪽에 가깝다. 분석용 사본을 만들고 원본과 SHA-256을 맞춰 본 뒤 큐에 넣고, 끝나면 사본을 지우는 흐름도 custody 로그와 같이 간다.&lt;/p&gt;
&lt;h3&gt;진행률&lt;/h3&gt;
&lt;p&gt;AI가 IN_PROGRESS랑 progressPercent를 올리면 AnalysisWorkerService가 받아 둔다. 진행률은 0~100으로 정규화하고, 완료 직전까지는 99를 넘기지 않는다. 메시지가 순서가 어긋나도 중간에 값이 줄어들지 않게, 이미 저장된 값과 비교해 더 큰 쪽만 반영한다.&lt;/p&gt;
&lt;h3&gt;Soft COMPLETED&lt;/h3&gt;
&lt;p&gt;얼굴이 없거나 너무 작으면 FAILED로 끊지 않고 COMPLETED에 errorCode·메시지를 붙인다. NO_HUMAN_FACE, FACE_TOO_SMALL, NO_FACE, FACE_GATE 같은 코드가 soft face-gate다. 상태 API에서는 완료로 보이되, 딥페이크 점수는 빼고 위변조 쪽만으로 risk를 잡도록 IntegratedRiskCalculator가 맞춰 둔다.&lt;/p&gt;
&lt;h3&gt;오버레이 job&lt;/h3&gt;
&lt;p&gt;모듈별 오버레이 URL은 결과와 같이 저장한다. 필요할 때만 도는 on-demand overlay API를 두고 RabbitMQ로 워커에 연결했다. 지원 모듈은 cnn, temporal, optical, forgery_spatial, forgery_temporal이다.&lt;/p&gt;
&lt;p&gt;같은 분석 요청·같은 모듈에서 이미 끝난 job이 있으면 재사용한다. 재분석을 돌리면 예전 MP4를 물리지 않도록, 재사용 범위를 그 analysis에만 한정했다. forgery_spatial은 bbox 파이프라인이 바뀌면 예전 border 스타일 MP4를 쓰지 않고 다시 만들게 했다. overlay_jobs 테이블은 운영 RDS에서 ddl-auto=validate여도 없으면 스키마를 보강하는 initializer가 있다.&lt;/p&gt;
&lt;h3&gt;riskScore&lt;/h3&gt;
&lt;p&gt;증거 상세 API에는 TimeSformer·GMFlow 타임라인과 모듈 점수를 넣는다. Late Fusion 점수(F)와 위변조 레인 max(G)를 합친 동적 가중 riskScore는 저장할 때와 API로 읽을 때 다시 맞춘다.&lt;/p&gt;
&lt;p&gt;둘 다 있으면 &lt;code&gt;(F² + G²) / (F + G)&lt;/code&gt; 를 0~1로 구한 뒤 ×100이다. F만 있으면 딥페이크만, soft face-gate로 F를 못 쓰면 G만, 둘 다 없으면 LOW·0에 가깝게 둔다. AI 쪽 integrate_risk_score와 같은 식을 백엔드 util에 맞춰 두었다. 위험 구간은 대략 40 미만 LOW, 70 미만 MEDIUM, 그 위 HIGH로 나눈다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;2. 증거·사건 상세&lt;/h2&gt;
&lt;p&gt;결과가 사건·증거 화면에서 맞게 보이도록 손본 구간이다.&lt;/p&gt;
&lt;p&gt;증거 상세 로직을 다듬고 타입별 메타데이터·DB 연동을 붙였다. 영상 증거 파이프라인도 맞춰 두었다. 케이스·마이페이지 라우팅을 손봐서 증거 상세로 바로 들어가도 동작하게 했다. 업로드 API에 caseName을 받게 했고, 분석 상태·통계 쪽 null·예외 처리도 보강했다. CoC 로그랑 상세 쪽 충돌도 정리했다.&lt;/p&gt;
&lt;p&gt;대시보드 통계는 업로더별로 캐시하고, 분석이 끝나거나 요청이 바뀌면 invalidate해서 숫자가 오래 남지 않게 했다. S3에 둔 시각화 URL은 상세를 열 때 presigned로 다시 맞춰 주는 refresher가 있다.&lt;/p&gt;
&lt;h3&gt;Recovery Score&lt;/h3&gt;
&lt;p&gt;메타데이터 추출 상태를 바탕으로 0~100 복구 점수와 데이터 소실도를 낸다. 추출 실패면 30, 부분 성공이면 70, 성공이면 100에서 시작한다. width·height·duration·codec·fps·ffprobe JSON·추출 에러·오디오 스트림 유무처럼 빠진 항목마다 감점하고, CRITICAL·등급·factor 목록을 상세에 내려준다. CoC 체인 검증 결과와 같이 무결성·준비도 화면에 묶인다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;3. 무결성·보고서·비교&lt;/h2&gt;
&lt;h3&gt;CoC 로그&lt;/h3&gt;
&lt;p&gt;증거·분석·보고서처럼 중요한 사건이 날 때마다 custody_logs에 한 줄씩 남긴다. 누가(actor), 무엇에(targetType·targetId), 무슨 행동(actionType)인지, 당시 해시·저장 경로·사유·payload·IP를 같이 적는다.&lt;/p&gt;
&lt;p&gt;핵심은 해시 체인이다. 새 로그를 쓸 때 직전 로그의 currentLogHash를 previousLogHash로 가져오고, actor·action·해시·경로·payload·시각 등을 파이프로 이은 문자열을 SHA-256으로 돌려 currentLogHash에 넣는다. payload JSON은 DB가 공백을 바꿔도 해시가 안 깨지게 Jackson으로 정규화해서 넣었다. 시각도 밀리초 단위로 맞춰 둔다.&lt;/p&gt;
&lt;p&gt;분석 쪽은 AnalysisCustodyLogService에서 이벤트를 나눈다. 분석용 사본 생성·원본과 SHA-256 일치 검증·사본 삭제, 큐 publish 실패·복사 실패 같은 ERROR_OCCURRED, ANALYSIS_REQUESTED·STARTED·COMPLETED·FAILED, 품질 경고 확인 등을 여기서 record 호출로 남긴다. 업로드·PDF 생성·다운로드·step-up 재인증·연장 같은 다른 구간도 같은 CustodyLogService.record로 이어진다.&lt;/p&gt;
&lt;p&gt;검증은 해당 증거의 로그를 시간·logId 순으로 읽고, previousLogHash가 직전 행과 맞는지, currentLogHash를 다시 계산해 같은지 본다. 끊기거나 변조가 있으면 PREVIOUS_HASH_MISMATCH·HASH_MISMATCH 같은 이유로 실패한다. 이 결과를 CoC 체인 검증 API와 Recovery Score·상세 무결성 체크에서 같이 쓴다.&lt;/p&gt;
&lt;h3&gt;매니페스트·서명&lt;/h3&gt;
&lt;p&gt;증거 매니페스트를 JSON으로 만들고 PKCS#8 개인키·X.509 인증서로 서명한다. 예전에 쓰던 mock X.509 흐름을 실제 CA 연동 키·인증서 PEM(또는 Secrets Manager)으로 바꿨다. 서명 알고리즘·subject·인증서 SHA-256 지문·서명 시각을 저장하고, 보고서 무결성 스냅샷에서 VALID·INVALID·UNSIGNED를 다시 확인한다.&lt;/p&gt;
&lt;h3&gt;블록체인 앵커&lt;/h3&gt;
&lt;p&gt;보고서·매니페스트 해시를 앵커 테이블에 남기고, 모드에 따라 simulated 클라이언트나 HTTP 앵커 클라이언트로 보낸다. 하루 단위로 전날 merkle root를 올리는 스케줄러도 있다. 데모·운영에서 ledger에 찍힌 시각·tx 정보를 상세·PDF 쪽에 붙일 수 있게 해 둔 부분이다.&lt;/p&gt;
&lt;h3&gt;보고서·비교&lt;/h3&gt;
&lt;p&gt;비교 API로 두 증거를 묶어 검증 요약을 만들고, 비교·단일 증거용 PDF 리포트 생성·목록·다운로드를 연동했다. 게시 스냅샷 템플릿 버전을 두고, 리포트 custody 로그에도 compareId 등을 payload로 남긴다. 설정·알림 API와 스프린트 통합 때 BE–FE 맞춘 상세 시각화 API도 이 구간에 들어간다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;4. 보안·세션&lt;/h2&gt;
&lt;p&gt;인증·인가를 JWT·Redis·역할 가드로 나눈 구간이다. 로그인 실패 제한, 민감 조회용 step-up, 리프레시 세션, ORG_ADMIN과 일반 사용자 화면 분리가 핵심이다.&lt;/p&gt;
&lt;h3&gt;로그인 rate limit&lt;/h3&gt;
&lt;p&gt;LoginRateLimitService가 클라이언트 IP 기준으로 실패를 센다. 저장소는 프로세스 안 ConcurrentHashMap이다. IP가 비면 unknown으로 묶는다.&lt;/p&gt;
&lt;p&gt;단계마다 실패 5회가 한도다. 첫 단계에서 5회가 차면 stage 1로 올리고 약 3분(FIRST_LOCK) 동안 막는다. 차단이 풀린 뒤 다시 5회가 차면 stage 2로 올리고 약 24시간(SECOND_LOCK) 막는다. 막혀 있는 동안 로그인 시도는 LoginRateLimitException으로 거절하고, retry-after 초를 내려준다. 3분 차단과 24시간 차단은 메시지가 다르다.&lt;/p&gt;
&lt;p&gt;차단 시각이 지나면 blockedUntil을 지우고 failureCount를 0으로 돌린다. stage 2였으면 stage도 0으로 리셋한다. 로그인에 성공하면 그 IP 상태를 통째로 지워서, 정상 사용자에게 이전 실패가 남지 않게 한다.&lt;/p&gt;
&lt;h3&gt;Access JWT · Refresh&lt;/h3&gt;
&lt;p&gt;로그인이 성공하면 액세스 JWT와 리프레시 토큰을 발급한다. auth.refresh.enabled가 켜져 있으면 리프레시는 Redis에 유저별로 저장하고, HttpOnly 쿠키로 내려 XSS로 토큰을 읽기 어렵게 한다. Redis가 없으면 로컬·테스트용 인메모리 폴백이 있다. idle TTL은 마지막 refresh 이후 일정 분(설정값)이 지나면 세션이 끝나게 Access보다 길게 잡는 편이다.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/api/auth/refresh&lt;/code&gt;는 쿠키의 리프레시 JWT 검증 → Redis에 저장된 값과 일치 확인 → 새 액세스·리프레시 쌍 발급(rotation) 순서다. refresh가 꺼져 있으면 REFRESH_DISABLED로 거절하고 다시 로그인하라고 한다. 로그아웃 시 Redis에서 리프레시를 지운다.&lt;/p&gt;
&lt;p&gt;프론트에서는 NEXT_PUBLIC_AUTH_REFRESH_ENABLED로 새로고침·직접 접속 시 쿠키로 세션을 복구할지 끈다. false면 직접 URL로 들어와도 refresh로 자동 로그인되지 않는다. 유휴 타임아웃이 끝나면 refresh 재발급 없이 로그인 화면으로 보낸다. Access는 만료 직전에 선제 refresh를 시도하는 흐름이다.&lt;/p&gt;
&lt;h3&gt;Step-up (비밀번호 재인증)&lt;/h3&gt;
&lt;p&gt;증거 상세·HLS처럼 민감한 조회 전에 일반 로그인만으로는 부족하다고 보고, 비밀번호를 한 번 더 확인한다. &lt;code&gt;/api/auth/step-up/verify&lt;/code&gt;에서 PasswordEncoder로 맞추면 Redis에 step-up 토큰을 발급한다. 키 패턴은 STEPUP:{token} → userId이고, RefreshTokenRedisService와 같이 Redis + 인메모리 폴백이다.&lt;/p&gt;
&lt;p&gt;프론트·API는 &lt;code&gt;X-Step-Up-Token&lt;/code&gt; 헤더로 이 토큰을 실어 보낸다. requireValidStepUp은 토큰이 비었거나, Redis에 없거나, userId가 현재 사용자와 다르면 STEP_UP_REQUIRED(403)를 낸다.&lt;/p&gt;
&lt;p&gt;연장은 &lt;code&gt;/api/auth/step-up/extend&lt;/code&gt;다. 남은 TTL이 5분 이하일 때만 허용하고(STEP_UP_EXTEND_TOO_EARLY), 허용되면 설정상 step-up 만료 시간만큼 TTL을 다시 늘린다. 재인증 성공·연장은 custody에 STEP_UP_VERIFIED·STEP_UP_EXTENDED로 남긴다.&lt;/p&gt;
&lt;h3&gt;역할 가드 (ORG_ADMIN)&lt;/h3&gt;
&lt;p&gt;SecurityConfig에서 &lt;code&gt;/api/v1/admin/**&lt;/code&gt;는 ADMIN·ORG_ADMIN만 통과한다. 반대로 일반 사용자 화면용 API인 대시보드·통계·마이페이지·비교(&lt;code&gt;/api/v1/evidences/dashboard/**&lt;/code&gt;, stats, mypage, compare)는 INVESTIGATOR·USER·REVIEWER만 허용해서, ORG_ADMIN이 수사 화면 API를 쓰지 못하게 막았다. 그 외 evidences는 authenticated면 된다.&lt;/p&gt;
&lt;p&gt;프론트 미들웨어도 맞춘다. UI 세션 쿠키가 없으면 로그인으로 보내고, ORG_ADMIN·ADMIN이 /main·/mypage·/compare 같은 사용자 앱 경로로 오면 /admin으로 돌린다. 일반 역할이 /admin으로 오면 /main으로 보낸다. 백엔드 API 가드와 화면 라우팅을 같이 막아 역할이 섞이지 않게 한 부분이다.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;관련:&lt;br&gt;&lt;a href=&quot;https://github.com/owhat02/backend-forensic&quot;&gt;https://github.com/owhat02/backend-forensic&lt;/a&gt; · &lt;a href=&quot;https://github.com/owhat02/ai-forensic&quot;&gt;https://github.com/owhat02/ai-forensic&lt;/a&gt; · &lt;a href=&quot;https://github.com/owhat02/frontend-deepfake&quot;&gt;https://github.com/owhat02/frontend-deepfake&lt;/a&gt;&lt;/p&gt;</description>
      <category>학습 노트</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/16</guid>
      <comments>https://idea79392.tistory.com/16#entry16comment</comments>
      <pubDate>Wed, 29 Jul 2026 16:01:15 +0900</pubDate>
    </item>
    <item>
      <title>[학습 노트] 최종 발표 FAQ</title>
      <link>https://idea79392.tistory.com/15</link>
      <description>&lt;h1&gt;[학습 노트] 최종 발표 FAQ&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ForenShield는 영상&amp;middot;이미지 위변조 분석과 증거 관리를 위한 딥페이크&amp;middot;디지털 포렌식 플랫폼이다. SK쉴더스 Rookies 개발 5기 최종 프로젝트로, Late Fusion&amp;middot;분석 결과 연동&amp;middot;soft-gate&amp;middot;오버레이 시각화 쪽을 중심으로 작업했다. 발표 전에 나올 법한 질문을 모아 두고, 짧게 답만 적어 둔 노트다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 화면의 히트맵(붉은 영역)은 뭔가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초안에는 히트맵이랑 오버레이를 같이 쓰려고 했다. 그런데 히트맵은 대표 프레임 이미지만 나와서 영상 따라보기엔 불편했다. 그래서 히트맵은 빼고, 오버레이에 의심 구간이랑 영역을 붉게 표시하는 쪽으로 정리했다. 지금 화면의 붉은 부분은 그 오버레이다. Grad-CAM처럼 모델 내부 기여도 맵이라고 보진 않고, 탐지&amp;middot;로컬라이즈 결과를 시각화한 쪽에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Late Fusion 점수가 있는데, 오버레이는 어떤 모델 것인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오버레이는 fusion으로 하나로 합친 게 아니라 모델별로 토글한다. Late Fusion은 riskScore랑 Veto/Rescue 같은 판정 규칙 쪽이고, 오버레이는 모듈별 단서를 보여 주는 쪽이다. 종합 점수랑 화면에 찍히는 근거는 역할이 다르다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 오버레이 렌더 때문에 OOM&amp;middot;지연은 없었나?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오버레이 MP4 만들기가 부하가 커서 본분석이랑 분리했다. 필요할 때 on-demand overlay job으로 돌리고, 대표 구간&amp;middot;모듈별로만 만들어서 범위를 줄였다. 오버레이가 실패해도 점수나 메타 분석 결과는 남는다. soft-gate랑은 다른 이야기다. (아래 4번)&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. soft-gate는 뭔가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;얼굴이 없거나 너무 작아서 일부 딥페이크 모듈을 제대로 돌리기 어려울 때 쓴다. 그냥 하드 실패로 끊지 않고 COMPLETED에 errorCode랑 안내를 붙인다. 위변조처럼 가능한 경로는 이어서 돌리기도 한다. 점수가 나왔는지, 그 점수를 어떤 조건에서 읽어야 하는지를 같이 남기는 느낌이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 왜 프론트에서 AI를 바로 안 부르나? (RabbitMQ)&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;흐름은 FE &amp;rarr; BE &amp;rarr; S3 &amp;rarr; RabbitMQ &amp;rarr; GPU Worker &amp;rarr; 결과 저장 &amp;rarr; FE 조회다. 업로드랑 접수는 빨리 끝내고, 긴 추론은 Worker가 큐에서 가져가서 처리한다. 분석 중에는 progress도 발행한다. 웹이 추론에 오래 묶이지 않게 하려는 구성이다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. EKS랑 로컬 GPU를 같이 쓴 이유는?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹이랑 메타데이터는 클라우드에서 받고, 무거운 추론은 On-prem GPU에서 돌린다. 둘은 RabbitMQ로 이어져 있다. 본인 쪽은 job, 진행률, 결과, 오버레이 job 연동을 담당했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7. 블록체인(Fabric)은 왜? 원본도 올리나?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수사&amp;middot;감사 성격이라 허가된 접근이 맞다고 봤다. 원본은 S3에 두고(오프체인), 체인이나 앵커에는 해시&amp;middot;서명&amp;middot;시점 같은 검증 메타만 올린다. 로컬은 simulated고, 연동할 때는 HTTP 앵커 게이트웨이를 쓴다. 원본을 체인에 올리면 용량이랑 속도 부담이 크다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;8. Late Fusion에서 일정과 정확도가 싸웠을 때는?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 영상에는 화질 깨짐, 블러, 변환이 섞여 있다. 그래서 조건별로 모델을 많이 학습해서 fusion하자는 의견이 있었는데, 짧은 기간에 모델 수가 너무 커질 수 있었다. 기존 모듈 fusion은 유지하고, 분석 전에 화질/블러/깨짐 경고를 넣는 쪽으로 정했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;9. 딥페이크 레인이랑 위변조 레인을 왜 나누나?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥페이크는 사람&amp;middot;신체 AI 합성이나 교체 여부고, 위변조는 국소&amp;middot;시간 편집 흔적이다. 신호랑 오탐 특성이 달라서 레인이랑 화면도 나눴다. Fusion은 그 레인 안에서 모듈 점수를 안정화하는 쪽에 가깝다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;10. Explainable AI라고 적은 건 Grad-CAM을 했다는 뜻인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;학술 XAI를 전 모듈에 똑같이 적용했다는 뜻은 아니다. 점수만 주지 않고 모듈별 점수, 구간, 오버레이, fusion 규칙 같은 근거를 남긴다는 의미다. Grad-CAM은 기울기로 내부 기여도를 추정해서 히트맵으로 보여주는 기법이고, 본 제품은 탐지 결과를 오버레이로 시각화한 거라 Grad-CAM이랑은 별개다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;11. 본인 역할이랑 분석 엔진 담당은 어떻게 나뉘나?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석 엔진이랑 근거 시각화 생성은 그쪽이 중심이다. 본인은 Late Fusion 안정화랑, 결과가 큐&amp;middot;API&amp;middot;화면&amp;middot;soft-gate로 이어지게 연동하는 쪽을 담당했다. JSON 스키마, overlay URL, progress, soft COMPLETED 같은 맞닿는 구간은 계약하고 분업했다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;12. 보안은?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JWT, RBAC, Step-up, 해시&amp;middot;서명&amp;middot;CoC&amp;middot;앵커 같은 층이 여러 개 있다. 본인은 결과랑 상태가 권한&amp;middot;화면 규칙에 맞게 나가도록 연동이랑 가드를 맞췄다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;13. 모델은 어떻게 학습했나?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;딥페이크 레인은 Xception, TimeSformer, GMFlow 세 모듈을 쓰고, 학습 방식이 모듈마다 다르다. Xception이랑 TimeSformer는 사전학습 가중치를 가져와 ff1k &amp;rarr; celeb1k 순으로 파인튜닝했다. Golden-200은 벤치용으로 빼 두고 학습에 넣지 않았다. 위변조 쪽 TruFor도 사전학습 위에 비디오 forgery 데이터로 파인튜닝하는 흐름이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;GMFlow만 따로 보면, &amp;ldquo;딥페이크 분류기를 처음부터 학습했다&amp;rdquo;가 아니다. 본체는 광학 흐름을 추정하는 사전학습 모델(&lt;code&gt;gmflow_things&lt;/code&gt;)이고, 우리가 바꾼 건 그 flow로 fake/real 점수를 붙이는 &lt;b&gt;뒤단(head)&lt;/b&gt; 이다. 인접 프레임 쌍마다 GMFlow가 픽셀이 어디로 움직였는지(flow)를 계산하고, JSON에 평균&amp;middot;최대&amp;middot;표준편차, 시간 흔들림(temporal jitter), 공간 불일치 같은 &lt;b&gt;통계 숫자&lt;/b&gt;가 쌓인다. CNN처럼 한 번에 fake 확률을 내는 구조가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;휴리스틱&lt;/b&gt;은 이 숫자들을 사람이 정한 규칙으로 합쳐 &lt;code&gt;motion_anomaly_score&lt;/code&gt;를 만드는 방식이다. &amp;ldquo;real 영상 코호트에서 보통 이 정도다&amp;rdquo; 같은 기준선과 비교해서, jitter&amp;middot;flow 크기&amp;middot;방향 분산 등 몇 가지 신호를 가중합&amp;middot;z-score로 묶는다. 학습 데이터로 가중치를 최적화한 분류기가 아니라, &lt;b&gt;손으로 잡은 공식&lt;/b&gt;에 가깝다. 빠르게 baseline을 만들 수 있지만 Golden-200 기준으로 단독 성능이 약해서(대략 combined AUC 0.45, fake recall도 낮음) &amp;ldquo;이걸로만 판정하자&amp;rdquo;는 단계는 넘어갔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Random Forest learned head&lt;/b&gt;는 같은 flow 통계를 &lt;b&gt;feature 벡터&lt;/b&gt;로 뽑은 뒤, fake/real 라벨이 붙은 영상(ff1k+celeb1k manifest train/val)으로 &lt;b&gt;Random Forest&lt;/b&gt;를 학습해 &lt;code&gt;fake_score&lt;/code&gt;를 내는 방식이다. &amp;ldquo;learned head&amp;rdquo;는 GMFlow 신경망을 다시 학습한다는 뜻이 아니라, flow 위에 얹은 &lt;b&gt;얕은 분류기(head)&lt;/b&gt; 가 데이터에서 패턴을 배웠다는 뜻이다. 여러 프로필용 모델을 합친 &lt;b&gt;rf_pooled&lt;/b&gt;를 운영에 썼고, threshold는 val에서 맞춘 뒤 Golden-200으로 확인했다. 휴리스틱보다 나아졌지만 여전히 Xception&amp;middot;TimeSformer만큼 단독으로 쓰기엔 약해서, Fusion에서는 가중치를 낮게 두고 보조 신호&amp;middot;veto/rescue 쪽으로 쓴다. RAFT&amp;middot;PWC-Net도 같이 벤치해 봤지만 Optical 대표는 GMFlow로 남겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본인 쪽은 이 학습 파이프 전체를 혼자 돌렸다기보다, 나온 점수를 Late Fusion에 넣을 수 있게 안정화하고 서비스로 넘기는 쪽을 맡았다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련:&lt;br /&gt;&lt;a href=&quot;https://github.com/owhat02/ai-forensic&quot;&gt;https://github.com/owhat02/ai-forensic&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://github.com/owhat02/backend-forensic&quot;&gt;https://github.com/owhat02/backend-forensic&lt;/a&gt; &amp;middot; &lt;a href=&quot;https://github.com/owhat02/frontend-deepfake&quot;&gt;https://github.com/owhat02/frontend-deepfake&lt;/a&gt;&lt;/p&gt;</description>
      <category>학습 노트</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/15</guid>
      <comments>https://idea79392.tistory.com/15#entry15comment</comments>
      <pubDate>Tue, 28 Jul 2026 14:37:28 +0900</pubDate>
    </item>
    <item>
      <title>[기술 회고] 딥페이크 탐지 Late Fusion 설계와 점수 안정화</title>
      <link>https://idea79392.tistory.com/14</link>
      <description>&lt;h1&gt;딥페이크 탐지 Late Fusion 설계와 점수 안정화&lt;/h1&gt;
&lt;p&gt;이 글은 부트캠프 최종 프로젝트 ForenShield에서, 여러 딥페이크·위변조 탐지 모델의 점수를 late fusion으로 합치고 안정화한 과정을 정리한 기술 회고다.&lt;/p&gt;
&lt;h2&gt;프로젝트와 동작 원리&lt;/h2&gt;
&lt;p&gt;ForenShield는 영상·이미지 위변조 분석과 증거 관리를 위한 딥페이크·디지털 포렌식 플랫폼이다. 나는 AI 딥페이크 탐지와 백엔드 연동을 중심으로 담당했고, 분석 결과가 서비스에 남도록 연결하는 쪽까지 작업했다.&lt;/p&gt;
&lt;p&gt;단일 모델만 쓰면 특정 변조 유형에는 강하고 다른 유형에는 약해질 수 있다. 그래서 역할을 나눈 모델들을 쓰고, 각 모델의 점수를 나중에 합치는 late fusion 구조를 택했다. 얼굴 단위 CNN 탐지에는 Xception을, 시간축 정보는 TimeSformer를, 광학흐름 기반 정보는 GMFlow를 실험·평가에 포함했다. forgery 쪽은 TruFor 등을 함께 보는 흐름으로 이어졌다.&lt;/p&gt;
&lt;p&gt;late fusion의 기본 원리는 단순하다. 각 모델이 독립적으로 추론한 뒤, 최종 판정 직전에 점수를 규칙에 따라 합친다. 문제는 “어떻게 합치면 현장 오탐을 줄이면서도 fake 재현을 무너뜨리지 않는가”였다.&lt;/p&gt;
&lt;h2&gt;무엇이 문제였나&lt;/h2&gt;
&lt;p&gt;모델마다 잘 잡는 구간이 달랐다. 하나를 올리면 다른 쪽에서 흔들리기도 했다. 벤치마크에서만 맞추면 실제 입력 분포에서 FP가 늘거나, soft-gate 상황(얼굴이 없거나 너무 작음)에서 점수 해석이 애매해지기도 했다.&lt;/p&gt;
&lt;p&gt;점수를 그냥 평균내거나 최대값만 가져가면 단순해 보이지만, 모듈이 스킵되거나 실패한 경우까지 포함하면 최종 riskScore의 의미가 흔들렸다. 합치는 규칙 자체가 제품의 판정 정책이 됐다.&lt;/p&gt;
&lt;h2&gt;실제로 어떻게 개선했나&lt;/h2&gt;
&lt;h3&gt;모델별 역할 정리&lt;/h3&gt;
&lt;p&gt;Xception 기반 CNN으로 얼굴 단위 탐지를 구성하고, 벤치마크와 임계값 분석으로 성능을 다듬었다. TimeSformer와 GMFlow는 실험·평가를 거쳐 점수를 안정화한 뒤 fusion에 넣을 수 있는 상태로 맞췄다. 각 모델을 “다 쓰는 것”이 목표가 아니라, 최종 판정에 쓸 수 있을 만큼 점수를 믿을 수 있게 만드는 게 목표였다.&lt;/p&gt;
&lt;h3&gt;Late fusion과 통합 riskScore&lt;/h3&gt;
&lt;p&gt;세 모델 결과를 late fusion으로 합쳐 최종 판정에 쓰이도록 파이프라인을 만들었다. deepfake 점수와 forgery 쪽을 함께 보는 통합 riskScore 규칙도 다듬었다. 이후에는 late fusion·모듈 상태에 따라 가중을 동적으로 두는 쪽으로 발전시켰다. 단순 합이 아니라, 어떤 레인이 실제로 돌았는지까지  Reflex션에 반영해야 점수가 과하게 튀지 않았다.&lt;/p&gt;
&lt;p&gt;현장 FP를 줄이려는 방향으로 fusion 규칙을 튜닝하기도 했다. GMF 쪽 veto 강도, 중간 구간에서의 cap, TimeSformer soft discount처럼, 재현율을 한번에 포기하지 않으면서 오탐을 누르는 쪽의 조정이었다.&lt;/p&gt;
&lt;h3&gt;soft-gate와의 연결&lt;/h3&gt;
&lt;p&gt;얼굴이 없거나 너무 작은 경우를 무조건 실패로 두면, 분석 파이프와 점수 해석이 거칠어진다. soft-gate를 두어 일부 상황은 COMPLETED 형태의 advisory로 남기고, fusion· forgery 경로와의 관계를 정리했다. 점수 합치기만의 문제가 아니라, “이 점수를 언제 믿을 수 있는가”를 같이 정의하는 일이었다.&lt;/p&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;late fusion에서 한 일은 모델을 늘어놓는 것이 아니었다. 모델별 점수를 쓸 수 있는 상태까지 맞추고, 합산 규칙을 판정 정책으로 명시하며, 모듈 스킵·soft-gate·forgery 레인까지 포함한 riskScore로 안정화했다. 단일 모델 성능보다, 여러 신호가 모여도 흔들리지 않는 최종 판정 쪽이 더 중요했다.&lt;/p&gt;
&lt;p&gt;다음에 다시 한다면 fusion 규칙 변경 전후의 평가 세트를 더 고정해 두고, 모듈별 기여도를 설명 가능한 형태로 남기고 싶다.&lt;/p&gt;
&lt;p&gt;관련 코드: &lt;a href=&quot;https://github.com/owhat02/ai-forensic&quot;&gt;https://github.com/owhat02/ai-forensic&lt;/a&gt;&lt;/p&gt;</description>
      <category>기술 회고</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/14</guid>
      <comments>https://idea79392.tistory.com/14#entry14comment</comments>
      <pubDate>Mon, 27 Jul 2026 13:53:10 +0900</pubDate>
    </item>
    <item>
      <title>[기술 회고] 딥페이크 분석 결과의 서비스 연동과 Soft-gate 처리</title>
      <link>https://idea79392.tistory.com/13</link>
      <description>&lt;h1&gt;딥페이크 분석 결과의 서비스 연동과 Soft-gate 처리&lt;/h1&gt;
&lt;p&gt;이 글은 ForenShield에서 GPU 추론 결과를 큐·API·화면까지 연결하고, 얼굴 없음 같은 상황을 soft-gate로 처리한 과정을 정리한 기술 회고다.&lt;/p&gt;
&lt;h2&gt;프로젝트와 동작 원리&lt;/h2&gt;
&lt;p&gt;ForenShield는 분석 요청이 들어오면 AI 서버가 영상을 받아 추론하고, 결과를 백엔드에 남겨 프론트에서 보게 하는 구조다. 나는 AI 탐지뿐 아니라 RabbitMQ·S3를 통한 GPU 작업 전달, 분석 결과 API, Recovery Score·CoC 검증 연동, 화면에서 점수와 안내가 맞게 보이도록 하는 쪽까지 작업했다.&lt;/p&gt;
&lt;p&gt;원리는 단순해 보인다. 추론이 끝나면 결과를 저장하고 조회하면 된다. 실제로는 분석이 길어서 중간 진행률이 필요하고, overlay 같은 부가 산출물은 본분석과 타이밍이 다르며, 얼굴이 없거나 너무 작은 입력은 “실패”로 둘지 “완료+안내”로 둘지 정책이 필요하다. 모델 점수만 맞아도 서비스 경험이 깨질 수 있는 지점들이었다.&lt;/p&gt;
&lt;h2&gt;무엇이 문제였나&lt;/h2&gt;
&lt;p&gt;분석이 끝날 때까지 진행률이 거의 안 움직이면 사용자는 멈춘 줄 안다. overlay를 본분석에 전부 구워 넣으면 파이프가 무거워지고, 재분석 때 예전 overlay가 섞일 위험도 있다. 얼굴이 없는 입력을 그냥 에러로 처리하면, 실제로는 분석이 끝난 건지 실패한 건지 UI에서 구분이 어렵다.&lt;/p&gt;
&lt;p&gt;권한·세션 쪽도 서비스 신뢰와 연결됐다. 로그인 rate limit, step-up 세션 연장, 일반 사용자 API에서 관리자 접근 차단처럼, 결과 연동만 보면 안 보이는 문제들이 같이 있었다.&lt;/p&gt;
&lt;h2&gt;실제로 어떻게 개선했나&lt;/h2&gt;
&lt;h3&gt;추론 → 큐 → 저장&lt;/h3&gt;
&lt;p&gt;RabbitMQ와 S3로 GPU 분석 작업을 넘기는 구조를 사용했다. 본분석 중에는 mid-run progress를 발행해, 백엔드 상태 폴링이 실제 GPU 단계를 반영하게 맞췄다. 결과가 한 번에 점프하지 않고 진행이 보이게 만드는 쪽이 운영·UX 모두에 필요했다.&lt;/p&gt;
&lt;p&gt;증거 상세·분석 결과 API에는 module timeline, overlay URL, soft COMPLETED advisory 같은 정보를 남길 수 있게 정리했다. 모델이 낸 값을 저장만 하는 게 아니라, 화면이 해석할 수 있는 형태로 노출하는 일이었다.&lt;/p&gt;
&lt;h3&gt;On-demand overlay&lt;/h3&gt;
&lt;p&gt;본분석 때 overlay MP4를 전부 구워 두지 않고, 필요할 때 overlay job으로 생성하도록 맞춘 흐름이 있었다. job 상태 API와 RabbitMQ 연동으로 결과를 붙이고, 재분석 때 이전 요청의 overlay가 잘못 재사용되지 않게 가드를 뒀다. 무거운 부가 산출물을 본분석 경로에서 분리한 개선이었다.&lt;/p&gt;
&lt;h3&gt;Soft-gate&lt;/h3&gt;
&lt;p&gt;얼굴이 없거나(FACE 관련), 너무 작거나, temporal 모듈을 돌리기 어려운 상황을 무조건 실패로 두지 않았다. 일부는 soft COMPLETED와 함께 errorCode·안내 메시지를 남기고, UI에서는 실패 화면이 아니라 advisory로 보이게 했다. 탐지 파이프가 “아무것도 못 했다”와 “조건상 일부 모듈만 돌았다”를 구분할 수 있어야 점수와 종합 소견 해석이 가능하다.&lt;/p&gt;
&lt;h3&gt;화면·권한 연동&lt;/h3&gt;
&lt;p&gt;동적 가중 riskScore와 모듈별 임계값 표시, deepfake/forgery 레인 분리가 화면에 맞게 보이도록 맞췄다. 미로그인·권한에 따른 접근 가드도 함께 손봤다. 모델 결과가 맞아도 잘못된 사용자가 보거나, 점수가 화면 규칙과 어긋나면 서비스 신뢰가 깨진다.&lt;/p&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;서비스 연동에서 한 일은 추론 함수 하나를 호출한 것이 아니었다. 진행률, overlay job, soft-gate, 결과 API, 화면 해석, 권한까지 한 줄로 맞춰야 모델 결과가 제품에 남았다. soft-gate는 예외 처리를 대충 넘긴 게 아니라, 완료와 실패 사이에 있는 분석 상태를 명시적으로 남기기 위한 선택이었다.&lt;/p&gt;
&lt;p&gt;다음에 다시 한다면 soft-gate 코드별 사용자 문구와 QA 시나리오를 더 초기에 고정해 두고 싶다.&lt;/p&gt;
&lt;p&gt;관련 코드: &lt;a href=&quot;https://github.com/owhat02/ai-forensic&quot;&gt;https://github.com/owhat02/ai-forensic&lt;/a&gt; , &lt;a href=&quot;https://github.com/owhat02/backend-forensic&quot;&gt;https://github.com/owhat02/backend-forensic&lt;/a&gt; , &lt;a href=&quot;https://github.com/owhat02/frontend-deepfake&quot;&gt;https://github.com/owhat02/frontend-deepfake&lt;/a&gt;&lt;/p&gt;</description>
      <category>기술 회고</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/13</guid>
      <comments>https://idea79392.tistory.com/13#entry13comment</comments>
      <pubDate>Mon, 27 Jul 2026 13:52:18 +0900</pubDate>
    </item>
    <item>
      <title>[기술 회고] 실시간 경매 마감 처리와 동시성&amp;middot;트랜잭션 개선</title>
      <link>https://idea79392.tistory.com/12</link>
      <description>&lt;h1&gt;실시간 경매 마감 처리와 동시성·트랜잭션 개선&lt;/h1&gt;
&lt;p&gt;이 글은 부트캠프 3차 미니 프로젝트 MACTA 백엔드에서, 마감 직전 입찰 충돌과 종료 스케줄러·알림 경로를 어떻게 다듬었는지 정리한 기술 회고다. 팀장을 맡아 ERD·API 명세와 작업 기준을 맞추는 일도 함께 진행했다.&lt;/p&gt;
&lt;h2&gt;프로젝트와 동작 원리&lt;/h2&gt;
&lt;p&gt;MACTA는 트래픽과 보안 위협을 고려한 실시간 경매 플랫폼이다. 백엔드는 상품·입찰·결제·알림·스케줄러가 한 상태로 맞물린다. 입찰이 들어오면 최고가와 이력이 갱신되고, 마감 시각이 되면 스케줄러가 종료 처리를 하며, 그 과정에서 알림과 결제 검증이 이어진다.&lt;/p&gt;
&lt;p&gt;동시성의 핵심은 “한 건씩 순서대로만 오면 된다”가 성립하지 않는다는 점이다. 마감 직전에는 요청이 겹친다. 같은 순간에 두 입찰이 들어오거나, 종료 처리와 입찰이 겹치면 최고가나 상태가 어긋날 수 있다. 트랜잭션 격리와 스케줄러 설계가 기능 구현만큼 중요해지는 지점이다.&lt;/p&gt;
&lt;h2&gt;무엇이 문제였나&lt;/h2&gt;
&lt;p&gt;정상 시나리오만 보면 입찰·종료·알림이 동작하는 것처럼 보여도, 마감 직전 충돌을 가정하지 않으면 구멍이 남는다. 종료 스케줄러와 이벤트 리스너가 중복되거나, 알림 경로가 어긋나면 판매자·구매자 쪽 상태가 다르게 보인다. UTC와 KST 시간 차가 있으면 마감 시각 자체가 밀리거나 당겨진다.&lt;/p&gt;
&lt;p&gt;팀으로 만들 때는 기준이 흔들리기 쉬운 구간에서 의견이 갈렸다. 트랜잭션 격리, 알림 경로, 시간 동기화처럼 “맞게 보이지만 정의가 다른” 문제들이었다. 구현만 늘리면 충돌이 늘고, 문서와 테스트 기준이 필요했다.&lt;/p&gt;
&lt;h2&gt;실제로 어떻게 개선했나&lt;/h2&gt;
&lt;h3&gt;동시성과 종료 스케줄러&lt;/h3&gt;
&lt;p&gt;마감 직전 입찰이 겹치는 상황을 가정하고 동시성 테스트를 넣었다. 경매 종료 스케줄러에는 트랜잭션 격리를 적용하고, 장애에 대한 방어 로직과 트랜잭션 분리도 다듬었다. AuctionEventListener 중복을 정리하고 비동기 트랜잭션 쪽도 손봤다. “종료가 한 번만, 일관된 상태로” 끝나게 만드는 쪽이 목표였다.&lt;/p&gt;
&lt;h3&gt;결제·상태 검증&lt;/h3&gt;
&lt;p&gt;결제 전에는 금액·권한·상태 검증을 두었다. 입찰·결제·거래 API에서 하드코딩된 인증 가정을 줄이고 검증 로직을 맞추는 작업도 있었다. 실시간 도메인에서는 성공 응답 하나가 곧 돈과 신뢰로 이어지므로, 상태 머신을 건너뛴 처리가 위험했다.&lt;/p&gt;
&lt;h3&gt;시간·알림·보안&lt;/h3&gt;
&lt;p&gt;UTC/KST 차이로 마감이 어긋나는 문제를 잡고, 직렬화·스케줄러 쪽 시간 처리를 맞췄다. 입찰 시 판매자 알림 누락, 알림 경로 오류, 비동기 알림 처리도 이어서 수정했다. JWT·Security 하드닝과 예외 처리 강화도 진행했다. 기능 추가만큼 기본 보안·운영 이슈가 백엔드 신뢰에 직접 영향을 줬다.&lt;/p&gt;
&lt;h3&gt;API와 팀 기준&lt;/h3&gt;
&lt;p&gt;마이페이지 목록·입찰·관심 경매 조회와 페이지네이션, 메인 통계 API, 경매 상세 필드 보강, live bidding activity 반영 같은 API 작업도 이어졌다. 팀장으로서 ERD·API 명세와 작업 규칙을 먼저 정리하고, 인증·입찰·알림·스케줄러 PR을 리뷰·머지하며 일정을 맞췄다. 기준이 갈리는 구간은 이슈로 남겨 “무엇으로 판단할지”를 합의했다.&lt;/p&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;MACTA에서 오래 붙잡았던 건 화면용 CRUD가 아니라, 마감 직전에도 상태가 깨지지 않게 만드는 일이었다. 동시성 테스트, 스케줄러 트랜잭션 격리, 결제 전 검증, 시간 동기화, 알림 경로, 보안 하드닝을 같이 봐야 했다. 실시간 경매는 정상 경로보다 겹치는 경로를 먼저 적어야 한다는 감각이 남았다.&lt;/p&gt;
&lt;p&gt;다음에 다시 한다면 동시성 시나리오 목록을 명세 초기에 고정하고, 스케줄러·이벤트·알림의 순서도를 테스트와 한 세트로 두고 시작하고 싶다.&lt;/p&gt;
&lt;p&gt;관련 코드: &lt;a href=&quot;https://github.com/owhat02/backend-macta&quot;&gt;https://github.com/owhat02/backend-macta&lt;/a&gt;&lt;/p&gt;</description>
      <category>기술 회고</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/12</guid>
      <comments>https://idea79392.tistory.com/12#entry12comment</comments>
      <pubDate>Mon, 27 Jul 2026 13:43:22 +0900</pubDate>
    </item>
    <item>
      <title>[기술 회고] 데이터 부족 환경에서의 LSTM 진행 예측</title>
      <link>https://idea79392.tistory.com/11</link>
      <description>&lt;h1&gt;데이터 부족 환경에서의 LSTM 진행 예측&lt;/h1&gt;
&lt;p&gt;이 글은 현장실습에서 실측 시계열이 거의 없는 상태로 진행 예측을 붙이려 했을 때, LSTM을 어떻게 다뤘는지 정리한 기술 회고다. Segmentation·라벨링·규칙 엔진 본편은 별도 글로 분리해 두었고, 여기에서는 시계열·LSTM 쪽만 남긴다.&lt;/p&gt;
&lt;h2&gt;프로젝트와 동작 원리&lt;/h2&gt;
&lt;p&gt;파이프라인 후단에는 “지금 관측된 상태”뿐 아니라, 이후 어떻게 진행될지를 보조로 보는 구간이 있었다. 이미지·속성 쪽 모델이 fact를 뽑고 규칙 엔진이 해석한 뒤, 시계열이 있으면 LSTM으로 진행을 예측하는 흐름을 염두에 두었다.&lt;/p&gt;
&lt;p&gt;LSTM은 이전 시점의 상태를 기억해 다음 구간을 추정하는 시계열 모델이다. 다만 학습·검증이 되려면 시간 축이 있는 실측 시퀀스가 필요하다. 이번 구간의 병목도 모델 구조보다, 그 시퀀스 데이터가 거의 없다는 점이었다.&lt;/p&gt;
&lt;h2&gt;무엇이 문제였나&lt;/h2&gt;
&lt;p&gt;진행 예측을 LSTM으로 붙이려 해도, 쓸 만한 실측 시계열 데이터셋이 거의 없었다. 하이퍼파라미터를 만져도 진도가 나기 어려웠고, “모델이 안 된다”고만 보면 다음이 막혔다. 실제로 막혀 있던 지점은 학습·검증 가능한 형태의 시계열이 없다는 점이었다.&lt;/p&gt;
&lt;p&gt;정답이 부족한 구간을 사람이 보완할 수는 있었지만, 그것으로 곧바로 현업 성능을 주장하기는 어려웠다. 데이터가 없으면 실험이 안 되고, 시뮬레이션만으로 숫자를 올리면 검증의 의미가 흐려진다. 그래서 “파이프가 도는지”와 “현장 성능을 보장하는지”를 처음부터 분리해서 봐야 했다.&lt;/p&gt;
&lt;h2&gt;실제로 어떻게 우회·구성했나&lt;/h2&gt;
&lt;h3&gt;시뮬레이션으로 학습 루프부터 열기&lt;/h3&gt;
&lt;p&gt;실측이 부족한 상태에서는 진행 규칙을 가정한 시뮬레이션 데이터 생성 스크립트를 먼저 만들었다. 목적은 현업 정확도 확정이 아니라, LSTM 학습·테스트 코드가 입력부터 출력까지 한 바퀴 도는지 확인하는 것이었다. 시퀀스 길이, 배치, train/test 진입점을 고정해 두고, 데이터가 나중에 들어와도 같은 파이프에 꽂을 수 있게 준비하는 쪽에 가깝다.&lt;/p&gt;
&lt;h3&gt;LSTM 학습·테스트 코드&lt;/h3&gt;
&lt;p&gt;시뮬레이션 시퀀스를 입력으로 LSTM 기반 진행 예측의 학습/테스트 코드를 구성했다. 실측이 충분해지기 전에는 이 코드를 “성능 경쟁용”이 아니라 “시계열 모듈의 인터페이스와 실패 지점을 드러내는 장치”로 봤다. 입력이 비거나 길이가 짧을 때, 예측 결과를 후속 리포트에 어떻게 넘길지 같은 연결 이슈가 모델 자체보다 먼저 보였다.&lt;/p&gt;
&lt;h3&gt;한계를 지표처럼 다루기&lt;/h3&gt;
&lt;p&gt;시뮬레이션으로 맞춘 수치는 현업 성능을 대신하지 않는다. 그래서 정량 검증이 어렵다는 한계를 문서에 명시해 두고, 데모·통합 검증과 성능 주장을 구분했다. 데이터가 부족한 구간에서 할 수 있는 일은 “완벽한 예측기”를 만드는 것이 아니라, 나중에 실측이 쌓였을 때 바로 학습을 이어갈 수 있는 자리를 남겨 두는 일이었다.&lt;/p&gt;
&lt;p&gt;정답이 비는 구간은 사람이 진행 예상 영역을 표시해 보완하는 보조 도구와도 맞춰 두었다. 자동 예측을 대체한다기보다, 라벨이 없는 상태를 방치하지 않기 위한 우회였다.&lt;/p&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;데이터 부족 환경에서 LSTM으로 한 일의 핵심은 화려한 시계열 아키텍처가 아니었다. 실측이 없으니 시뮬레이션으로 학습 루프를 열고, LSTM 학습/테스트 코드를 파이프라인에 붙일 수 있게 준비했으며, “파이프 동작”과 “현업 성능 보장”을 섞지 않으려 했다. 모델이 안 된다고 멈추기보다, 학습 가능한 시퀀스가 없다는 문제 정의로 바꾼 경험이 남았다.&lt;/p&gt;
&lt;p&gt;다음에 다시 한다면 실측 시계열 스키마와 최소 샘플 수, 시뮬레이션 가정의 문서화를 더 먼저 두고, 실측이 들어오는 순간 train/valid 분할 기준을 바로 적용할 수 있게 체크리스트로 남겨 두고 싶다.&lt;/p&gt;
&lt;p&gt;관련 경험 시기: 모바일앱개발협동조합 현장실습 (2025.09~12)&lt;br&gt;같이 보면 좋은 글: 현장실습 다중 모델 파이프라인과 규칙 기반 후속 추론 / 현장실습 라벨링 도구 제작과 마스크 데이터 파이프라인&lt;/p&gt;</description>
      <category>기술 회고</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/11</guid>
      <comments>https://idea79392.tistory.com/11#entry11comment</comments>
      <pubDate>Mon, 27 Jul 2026 13:36:08 +0900</pubDate>
    </item>
    <item>
      <title>[기술 회고] Segmentation 모델 실험과 성능 개선</title>
      <link>https://idea79392.tistory.com/10</link>
      <description>&lt;h1&gt;Segmentation 모델 실험과 성능 개선&lt;/h1&gt;
&lt;p&gt;이 글은 현장실습에서 MobileNetV2 + FPN 기반 Segmentation 모델을 반복 실험하며 성능을 올린 과정을 정리한 기술 회고다. 협업 과제의 기밀을 위해 탐지 대상의 구체 명칭은 적지 않고, 아키텍처·손실함수·데이터·후처리 측면에서 무엇을 바꿨는지만 남긴다.&lt;/p&gt;
&lt;h2&gt;프로젝트와 동작 원리&lt;/h2&gt;
&lt;p&gt;목표는 이미지에서 관심 영역을 픽셀 단위로 나누는 Segmentation 모델을 만들고, 그 결과를 후속 분석 파이프라인에 넘기는 것이었다. Backbone은 MobileNetV2, 구조는 FPN을 사용했다. 경량 encoder로 학습·추론 부담을 줄이면서, FPN으로 다중 스케일 특징을 쓰는 조합이었다. &lt;code&gt;segmentation-models-pytorch&lt;/code&gt;로 ImageNet 사전학습 encoder를 붙여 시작했다.&lt;/p&gt;
&lt;p&gt;학습 환경은 Windows, Python 3.11, PyTorch(CUDA), VRAM 4GB급 GPU였다. 메모리가 넉넉하지 않아서 해상도·배치 크기 선택이 곧 학습 가능 여부를 가른다. Augmentation은 Albumentations로 Resize, Flip, Rotate, ColorJitter, Normalize를 구성했다.&lt;/p&gt;
&lt;p&gt;평가 지표는 mIoU를 중심으로 두고, 이후 precision·recall도 같이 보기 시작했다. 평균 mIoU만 보면 희소 클래스 실패가 가려질 수 있어서였다.&lt;/p&gt;
&lt;h2&gt;무엇이 문제였나&lt;/h2&gt;
&lt;p&gt;초기 사전학습 기반 모델은 mIoU가 어느 정도 나왔지만, 데이터 증강·클래스를 추가하거나 해상도를 키우면 오히려 수치가 떨어지는 실험이 반복됐다.&lt;/p&gt;
&lt;p&gt;클래스 불균형도 컸다. 배경 비율이 높고 관심 클래스는 픽셀이 적은 전형적인 분할 문제였다. 학습이 중간에 끊기면 처음부터 다시 해야 해서, 실험 속도도 문제였다. 그래서 모델 한 방을 노리기보다, 버전을 나눠 가설을 하나씩 검증하는 쪽으로 갔다.&lt;/p&gt;
&lt;h2&gt;실제로 어떻게 개선했나&lt;/h2&gt;
&lt;h3&gt;해상도와 입력 크기&lt;/h3&gt;
&lt;p&gt;256, 384, 512 등 입력 크기를 바꿔 가며 학습했다. 해상도를 올린다고 무조건 좋아지지 않았다. VRAM 제약과 세부 영역 학습이 맞물려, 큰 입력이 오히려 mIoU를 떨어뜨린 버전도 있었다. 해상도는 “선명도”가 아니라 학습 안정성과 클래스별 픽셀 분포까지 바꾸는 변수였다.&lt;/p&gt;
&lt;h3&gt;손실함수 실험&lt;/h3&gt;
&lt;p&gt;CE만으로 부족하다고 보고 Lovász-Softmax, Dice, Focal, 둘을 섞은 Hybrid Loss를 시험했다. 불균형이 큰 분할에서 region overlap을 직접 보는 Dice와, 어려운 픽셀에 가중을 주는 Focal을 조합하는 쪽이 방향성으로 맞았다. 비율을 잘못 잡으면 한쪽 지표만 올라가고 mIoU는 무너지는 경우도 있어서, loss 비중 자체를 실험 축으로 뒀다.&lt;/p&gt;
&lt;p&gt;클래스별 가중치도 함께 썼다. 배경은 낮게, 희소·중요 클래스는 높게 두는 방식이었다. 구체 클래스명은 생략하지만, “픽셀 비율이 아닌 업무 중요도·희소성”을 가중치에 반영하려 했다.&lt;/p&gt;
&lt;h3&gt;지표와 체크포인트&lt;/h3&gt;
&lt;p&gt;어느 버전부터는 precision·recall을 같이 로깅했다. mIoU만 보면 recall이 높은데 precision이 무너진 상태, 또는 그 반대를 구분하기 어려웠다. Best 모델 저장 기준도 단순 마지막 epoch가 아니라 validation mIoU가 갱신될 때 checkpoint를 덮어쓰도록 바꿨다. Early Stopping도 patience 기반으로 붙여, 지표가 정체되면 학습을 끊었다.&lt;/p&gt;
&lt;h3&gt;임계값과 후처리&lt;/h3&gt;
&lt;p&gt;추론 임계값을 올리거나 후처리를 넣었다 빼 보는 실험도 했다. 후처리를 넣으면 한쪽 지표는 나아 보여도 다른 쪽이 흔들릴 수 있어서, “예쁜 마스크”와 “지표상 이득”을 구분해 봐야 했다. 어떤 버전에서는 후처리를 제거하고 threshold만 조정하는 쪽이 나았다.&lt;/p&gt;
&lt;h3&gt;아키텍처 변경&lt;/h3&gt;
&lt;p&gt;초기 U-Net 계열 설정에서 FPN 쪽으로 구조를 정리·변경한 실험이 있었다. Encoder는 MobileNetV2를 유지한 채 neck/decoder 쪽을 바꿔, 경량 backbone + 다중 스케일 융합을 맞추려 했다.&lt;/p&gt;
&lt;h3&gt;데이터 쪽과 병행&lt;/h3&gt;
&lt;p&gt;모델만 돌리지 않았다. 중복 이미지 격리, 해상도 분석, 마스크 검증, 데이터셋 재분할(train/valid 등), 추가 클래스 병합 스크립트를 돌려 학습 분포를 바꿨다. 증강·클래스 추가가 한 번에 성능을 떨어뜨린 버전은, 데이터 합치는 방식과 정규화 가정이 깨졌을 가능성이 컸다. 그래서 이후에는 “데이터 변경 → 지표 확인 → loss/threshold 조정” 순서를 더 의식했다.&lt;/p&gt;
&lt;p&gt;버전을 十余 개 가까이 쌓아 가며, 실패 실험도 기록으로 남겼다. 예전에 잘 나왔던 설정으로 회귀했다가 threshold만 올리는 식으로, 좋은 축을 고정하고 변수 하나만 바꾸는 습관이 생겼다.&lt;/p&gt;
&lt;h3&gt;학습 결과의 쓰임&lt;/h3&gt;
&lt;p&gt;분할 모델이 어느 정도 안정된 뒤에는, 영역 유형 인식과 후속 분석을 같이 보는 멀티태스크·파이프라인 쪽으로 확장했다. Segmentation 출력을 입력으로 받아 규칙 기반 추론 엔진과 결합하고, FastAPI로 이미지 입력·결과 시각화·JSON 리포트를 반환하는 서버도 구성했다. 분할 성능 개선은 그 파이프라인의 첫 번째 게이트였다.&lt;/p&gt;
&lt;p&gt;수치로는 초기 대비 실험 과정에서 mIoU·precision·recall을 함께 끌어올린 버전을 확보했다. 절대 수치와 도메인 해석은 공개하지 않지만, “한 지표만 보는 실험”에서 “지표를 나눠 보고 loss·threshold·데이터를 되돌릴 수 있게 만든 실험”으로 바뀐 것이 핵심이다.&lt;/p&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;Segmentation 개선에서 한 일은 모델을 한 번 더 크게 만드는 것이 아니었다. MobileNetV2+FPN을 기준으로 해상도·Hybrid Loss·클래스 가중치·mIoU 기준 체크포인트·Early Stopping·threshold/후처리·데이터 정리를 버전 단위로 검증했다. VRAM이 작은 환경에서는 해상도 선택이 성능과 학습 가능 여부를 동시에 좌우한다는 점도 남았다.&lt;/p&gt;
&lt;p&gt;다음에 다시 한다면 실험 로그(데이터 해시, loss 비중, threshold, 해상도)를 표로 더 엄격히 남기고, 실패 버전을 재현 가능한 설정 파일로 고정해 두고 싶다.&lt;/p&gt;</description>
      <category>기술 회고</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/10</guid>
      <comments>https://idea79392.tistory.com/10#entry10comment</comments>
      <pubDate>Mon, 27 Jul 2026 13:22:18 +0900</pubDate>
    </item>
    <item>
      <title>[기술 회고] 라벨링 도구 제작과 마스크 데이터 파이프라인</title>
      <link>https://idea79392.tistory.com/9</link>
      <description>&lt;h1&gt;라벨링 도구 제작과 마스크 데이터 파이프라인&lt;/h1&gt;
&lt;p&gt;이 글은 현장실습에서 Segmentation·후속 모델 학습을 위해 라벨링 도구와 마스크 데이터 파이프라인을 만든 과정을 정리한 기술 회고다. 외부와 협업한 과제였기 때문에, 무엇을 탐지했는지 같은 도메인 상세는 적지 않고, 어떤 도구와 데이터 흐름을 만들었는지만 남긴다.&lt;/p&gt;
&lt;h2&gt;프로젝트와 필요성&lt;/h2&gt;
&lt;p&gt;실습에서는 이미지 분할(Segmentation)과, 분할 결과를 입력으로 받는 후속 분석 모델을 함께 다뤘다. 학습을 돌리려면 픽셀 단위 마스크와, 필요 시 세부 속성 라벨이 있어야 했다. 문제는 바로 쓸 수 있는 Golden Set가 충분하지 않았다는 점이었다.&lt;/p&gt;
&lt;p&gt;그래서 라벨을 “수동으로 어떻게든 찍는 일”이 아니라, 반복 가능한 도구와 변환 스크립트 쪽으로 정리하기 시작했다. 목표가 모델 학습이어도, 병목은 종종 라벨링 UX와 포맷 변환에 있었다.&lt;/p&gt;
&lt;h2&gt;무엇을 만들었나&lt;/h2&gt;
&lt;h3&gt;수동 라벨링 도구&lt;/h3&gt;
&lt;p&gt;세부 속성을 사람이 직접 선택·입력하고 CSV로 저장하는 라벨링 도구를 만들었다. 마스크만 있으면 끝나는 작업이 아니라, 이후 규칙 기반 추론이나 멀티라벨 분류에 쓸 속성까지 남겨야 해서, 단순 박스 찍기보다 속성 기록 흐름이 중요했다.&lt;/p&gt;
&lt;p&gt;진행 예상 영역을 사용자가 직접 표시할 수 있는 보조 도구도 만들었다. 시계열 예측 쪽 데이터를 준비할 때, 정답이 부족한 구간을 사람이 보완하는 용도였다.&lt;/p&gt;
&lt;h3&gt;박스 라벨 → 분할 마스크&lt;/h3&gt;
&lt;p&gt;바운딩 박스 형태로 남은 라벨을 Segmentation 학습용 마스크로 바꿔야 하는 경우가 있었다. SAM을 이용해 박스 영역을 더 정밀한 세그멘테이션 마스크로 변환하는 배치 스크립트를 만들었고, 변환 결과를 검증하는 과정도 붙였다. 처음부터 픽셀 마스크를 전부 새로 그리는 비용보다, 기존 박스 라벨을 재활용하는 쪽이 현실적이었다.&lt;/p&gt;
&lt;h3&gt;JSON·마스크 변환과 검증&lt;/h3&gt;
&lt;p&gt;JSON 라벨을 클래스 ID가 매핑된 PNG 마스크로 바꾸는 변환기, PIL 기반 마스크 생성, 마스크 클래스 정의 확인용 검증 스크립트를 정리했다. 라벨 포맷이 섞여 있으면 학습 직전에 깨지기 쉬워서, “변환 → 검증 → 학습” 순서를 고정하려고 했다.&lt;/p&gt;
&lt;h3&gt;자동 라벨링 보조&lt;/h3&gt;
&lt;p&gt;텍스트 설명이나 이미지 정보를 바탕으로 속성 키워드를 뽑는 자동 라벨링 보조도 실험했다. Gemini API를 호출해 속성 후보를 CSV로 남기는 흐름이었다. 완전 자동 정답으로 쓰지 않고, 사람이 검수할 초안을 만드는 쪽에 가깝게 봤다. 라벨 비용이 큰 구간에서 초안 생성용으로 의미가 있었다.&lt;/p&gt;
&lt;h3&gt;데이터 수집·정리 유틸&lt;/h3&gt;
&lt;p&gt;JSON에 포함된 이미지 파일을 원본 폴더에서 찾아 한곳으로 모으는 스크립트, 유사·중복 이미지를 격리하는 스크립트, 해상도 분포 분석, EXIF 확인, 회전 보정값 정리 같은 유틸도 같이 쌓였다. 라벨링 도구 하나보다, 학습 직전 데이터가 깨지지 않게 만드는 주변 스크립트가 더 많이 필요했다.&lt;/p&gt;
&lt;h2&gt;실제로 어떻게 개선에 쓰였나&lt;/h2&gt;
&lt;p&gt;라벨링 도구를 만든 이유는 문서용 산출물이 아니라, Golden Set를 만들고 Segmentation·속성 모델을 다시 학습하기 위해서였다. 마스크가 생기고 나서야 클래스 불균형, 중복, 해상도 문제를 수치로 볼 수 있었다.&lt;/p&gt;
&lt;p&gt;특히 중복·유사 이미지를 격리하고, 배경만 있는 샘플을 구분해 둔 뒤 학습 세트를 다시 짜는 과정이 모델 실험과 직접 연결됐다. 도구가 없으면 “데이터가 부족하다”에서 끝나고, 도구가 있으면 “어떤 라벨이 부족한지”로 질문이 바뀌었다.&lt;/p&gt;
&lt;h2&gt;정리&lt;/h2&gt;
&lt;p&gt;현장실습에서 라벨링 쪽으로 한 일은 특정 도메인 용어를 외운 것이 아니라, 수동 속성 라벨링 도구, 박스→마스크(SAM) 변환, JSON↔PNG 파이프라인, 자동 라벨 초안, 중복·해상도 정리 유틸을 만들어 학습 가능한 데이터 경로를 연 것이었다. 모델 개선 회고는 이 파이프라인이 생긴 뒤에야 본격적으로 이어졌다.&lt;/p&gt;
&lt;p&gt;다음에 다시 한다면 라벨 스키마와 검수 기준을 더 먼저 문서화하고, 도구 UI와 변환 스크립트의 입출력 계약을 테스트로 고정해 두고 싶다.&lt;/p&gt;</description>
      <category>기술 회고</category>
      <author>sybear02</author>
      <guid isPermaLink="true">https://idea79392.tistory.com/9</guid>
      <comments>https://idea79392.tistory.com/9#entry9comment</comments>
      <pubDate>Mon, 27 Jul 2026 13:09:44 +0900</pubDate>
    </item>
  </channel>
</rss>