Skip to content

feat(news): GDELT 전 구간 수집 러너 + 기기 간 이어받기 — combined 모드 불가, 감사파일 28.8GB 문제 해결 - #69

Open
choigod1023 wants to merge 1 commit into
devfrom
feat/news-collection-runner
Open

feat(news): GDELT 전 구간 수집 러너 + 기기 간 이어받기 — combined 모드 불가, 감사파일 28.8GB 문제 해결#69
choigod1023 wants to merge 1 commit into
devfrom
feat/news-collection-runner

Conversation

@choigod1023

Copy link
Copy Markdown
Contributor

Description

뉴스 신호를 학습·검증 구간 전체로 채우려다 세 곳에서 막혔고, 그걸 풀기 위한 러너입니다. 다른 기기에서 이어받을 수 있게 수집 결과도 함께 올립니다.

#67 로 원자재 매핑이 열리면서 뉴스가 붙을 자리(출고량 기준 97.64%)는 생겼는데, 정작 뉴스를 다 못 받아서 지금 피처테이블은 2024-01 한 달치(2.35%)만 채워져 있습니다.

막힌 세 곳

1. GDELT 는 IP 단위로 강하게 제한합니다

실측한 통과 패턴입니다.

17:07  17:08  17:09  17:10   ← 4개 통과
17:11~ 429 지속 (백오프 60→120→240→480→900초)

4~5요청 통과 후 긴 벽이 반복됩니다. 시간당 7~8개꼴이라 72요청이면 8시간 이상입니다. 스코어러를 단발로 호출하면 중간에 죽고 처음부터입니다.

2. GDELT_QUERY_MODE=combined 은 항상 실패합니다

요청 수를 72 → 24 로 줄이려고 켰는데 첫 요청부터 거부됐습니다. rate limit 이 아닙니다.

Your query was too short or too long.
combined  risk_candidate        261자  ← 거부
split     raw_material          172자  ← 정상
          medical_supply        130자
          infectious_disease     66자

상한이 172~261자 사이라 combined 분기는 도달은 하지만 항상 실패하는 경로입니다. 이 PR 은 러너에서 split 만 쓰도록 했고, combined 자체 수정은 손대지 않았습니다 — 어떤 키워드를 버릴지가 도메인 판단이라 #22 에 3안을 올려두고 의견을 기다리는 중입니다.

3. 수집 결과가 기기에 갇힙니다

data/external/ 은 gitignore 대상이라, 기기를 옮기면 8시간을 다시 씁니다. 같은 입력으로 같은 WAPE 가 재현되는지 확인하려면 기사 원본이 고정돼야 한다는 점에서도 곤란합니다.

추가한 것

pipelines/news_collection/collect_full_window.py

429 를 견디며 전 구간이 찰 때까지 재시도합니다. 체크포인트가 (월 x 카테고리) 단위로 즉시 저장되고 다음 호출에서 건너뛰므로, 죽어도 진행이 누적됩니다.

기간은 NEWS_START_DATE / NEWS_END_DATE 로 좁힐 수 있습니다. 모델 평가만 목적이라면 검증(2025 Q1/Q2)·홀드아웃(2025-10~12)이 전부 2025년이라 36요청으로 끝납니다.

pipelines/news_collection/sync_checkpoints.py

체크포인트 ↔ data/handoff/news_gdelt_articles.parquet 왕복입니다. export / restore / status.

restore멱등이고(이미 있는 파일은 안 건드림), 체크포인트를 지웠다 되살려 내용이 바이트 단위로 같은 것을 확인했습니다.

data/handoff/news_gdelt_articles.parquet

현재까지 받은 9/72 (2024-01~03, 2,248건, 0.20MB) 입니다.

data/handoff/ 를 고른 이유는 .gitignore 에 적힌 기준 그대로입니다 — "재생성 비용이 크고 정의 고정이 필요한 것만". 8시간짜리 재수집과 WAPE 재현성이 정확히 그 경우라고 봤습니다.

담기는 건 기사 메타데이터(date title summary source country url + 출처 월/카테고리)뿐입니다. 공개 레포라 커밋 전에 확인했고 기관코드·품목코드는 0건입니다.

NEWS_ARTICLE_SCORE_FORMAT 환경변수

기사별 감사 산출물이 기사 수에 정비례합니다(기사 1건당 감사행 약 3천 = 기사 × 매칭 품목).

기간 기사 CSV parquet+zstd
1개월 745건 1,220 MB 8.2 MB
24개월(추정) ~18,000건 ~28.8 GB ~0.19 GB

148배 차이입니다. 24개월 CSV 는 웬만한 작업기기에서 디스크가 먼저 터집니다(실제로 오늘 두 번 No space left on device 를 맞았습니다).

기본값은 csv 로 뒀습니다. 문서에 정의된 산출물이라 팀이 결정하기 전엔 모양을 바꾸지 않는 게 맞다고 봤습니다.

테스트

tests/test_news_risk_scorer.py 에 4건 추가했습니다.

  • 기본값이 csv 인지(기존 산출물 정의 불변)
  • parquet 왕복이 무손실인지
  • parquet 선택 시 stale csv 를 남기지 않는지
  • 잘못된 값은 조용히 넘어가지 않고 실패하는지
Ran 24 tests in 0.313s
OK

To Reviewer

  1. data/handoff/ 에 뉴스 기사를 두는 게 맞는지 — 기준에는 부합한다고 봤는데, 기존 handoff 는 전부 모델 산출물이라 성격이 다릅니다. 부적절하면 어디로 옮길지 알려주세요.
  2. NEWS_ARTICLE_SCORE_FORMAT 기본값을 parquet 으로 뒤집을지 — 이 PR 은 csv 를 유지했습니다. 소비하는 코드는 없고 문서 3곳에서 감사 산출물로만 언급됩니다. 뒤집는 게 낫다면 문서까지 같이 고치겠습니다.
  3. 수집 기간 — 2024~25 전체(72요청, 8시간+)로 갈지, 평가에 필요한 2025년만(36요청)으로 갈지. 저는 후자를 먼저 채우고 2024년을 나중에 이어받는 쪽이 낫다고 봅니다.

Type

  • 새로운 기능
  • 버그 수정
  • 리팩토링
  • 문서
  • 의존성 추가/수정

PR Checklist

  • Commit Message Convention을 준수했습니다.
  • Code Convention을 준수했습니다.
  • 변경한 기능이 잘 동작하는지 테스트했습니다. — 단위 테스트 24건 통과, export/restore 왕복 무손실 실측, parquet 압축비 실측

뉴스 신호를 학습·검증 구간 전체로 채우려다 세 곳에서 막혀 이를 해결한다.

1. GDELT 는 IP 단위로 요청을 강하게 제한한다. 실측하면 4~5요청 통과 후 긴
   429 벽이 오고, 24개월 x 3분할 = 72요청을 다 받는 데 8시간 이상 걸린다.
   스코어러 단발 호출로는 중간에 죽는다.

2. GDELT_QUERY_MODE=combined 는 항상 실패한다. 질의가 261자라 GDELT 가
   "Your query was too short or too long." 로 거부한다(split 은 최대 172자).
   요청 수를 줄이려고 만든 선택지인데 쓸 수 없다. 상세는 이슈 #22.

3. 수집 결과가 gitignore 된 data/external/ 에만 남아 기기를 옮기면
   8시간을 다시 써야 한다.

추가한 것:

- pipelines/news_collection/collect_full_window.py
  429 를 견디며 전 구간이 찰 때까지 재시도하는 드라이버. 체크포인트가
  (월 x 카테고리) 단위로 저장되므로 죽어도 이어받는다. 기간은
  NEWS_START_DATE / NEWS_END_DATE 로 좁힐 수 있다.

- pipelines/news_collection/sync_checkpoints.py
  체크포인트 <-> data/handoff/news_gdelt_articles.parquet 왕복.
  export/restore/status. restore 는 멱등이고 왕복 무손실을 확인했다.

- data/handoff/news_gdelt_articles.parquet
  현재까지 받은 9/72 (2024-01~03, 2,248건, 0.20MB). handoff 는 "재생성
  비용이 크고 정의 고정이 필요한" 산출물 자리라는 기준에 맞다. 기사
  메타데이터(날짜·제목·출처·국가·URL)만 담기며 원장 데이터는 없다.

- NEWS_ARTICLE_SCORE_FORMAT 환경변수
  기사별 감사 산출물이 기사 수에 정비례해 커진다. 1개월(745건)에 CSV
  1,220MB 였고 24개월이면 약 28.8GB 다. parquet+zstd 로 쓰면 8.2MB
  (148배)이고 24개월 추정 0.19GB 다. 기본값은 csv 라 기존 산출물 정의는
  바뀌지 않는다.

테스트 4건 추가(tests/test_news_risk_scorer.py) — 기본값이 csv 인지,
parquet 왕복이 무손실인지, stale csv 를 남기지 않는지, 잘못된 값은
조용히 넘어가지 않고 실패하는지. 전체 24건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sehyeon03

Copy link
Copy Markdown
Contributor

2026-08-10 신규 PR 검토

결론: 재시작 가능한 수집기와 parquet 감사 출력은 필요한 개선이지만, 커밋된 뉴스의 기간·완결성 품질이 모델 입력 기준을 통과하지 못해 현재는 병합 보류가 적절합니다. 관련 테스트 24개와 통합 브랜치 전체 244개 테스트는 통과했습니다.

Blocking 1: 커밋된 “월 체크포인트”가 요청 월을 대표하지 않습니다

커밋 SHA의 parquet(sha256=c7561eb2...78b4)를 직접 검사한 결과입니다.

항목 결과
전체 행 / unique URL 2,248 / 2,240
source_month와 실제 date의 월이 다른 행 1,546 (68.8%)
relevance filter 통과 123
통과분 중 월 불일치 88 (71.5%)
체크포인트별 행 수 9개 모두 249~250

infectious/raw-material 분할은 각 월마다 대부분 단 하루, 월말 다음 날의 250건만 담습니다. sort=datedesc와 250건 상한에 포화되어 월 전체가 아니라 끝부분 표본입니다. 시간창을 모두 만들었다는 뜻의 9/72와 기사 수집 완결성은 다른 지표입니다. 이 상태로 월별 뉴스 피처나 WAPE를 계산하면 기간 편향이 큽니다.

필수 조치:

  1. 응답 date를 요청 window로 검증하고 범위 밖 행은 reject/quarantine
  2. 250건 포화 시 일/주 단위로 window를 재분할하거나 지원되는 pagination/집계 방식 사용
  3. part별 requested range, returned min/max date, unique dates, raw/filtered count, saturation을 manifest에 기록
  4. 위 gate를 통과하기 전 handoff를 model_ready로 사용하지 않기

Blocking 2: 진행률 계산이 파일 개수만 봅니다

done_parts()는 요청 기간의 정확한 (month, category) 집합이 아니라 디렉터리의 matching 파일 개수만 셉니다. 2025-01 한 달을 요청한 상태에서 2024-01 파일 3개만 두어도 done=3, expected=3으로 잘못 완료됩니다. sync_checkpoints.py도 기간 24개월, 72 parts, max_records=250을 하드코딩해 runner의 환경변수와 계약이 다릅니다.

정확한 expected key set 차집합으로 진행률을 계산하고, query text/hash·mode·기간·max_records를 checkpoint identity와 manifest에 포함해 주세요. restore는 기존 파일을 무조건 skip하지 말고 hash/content가 다르면 실패해야 하며, checkpoint 쓰기도 temp→atomic rename이 필요합니다.

Blocking 3: stale CSV 테스트가 실제 전환을 검증하지 않습니다

기존 CSV를 만든 뒤 parquet 모드로 전환해 보니 parquet은 생성되지만 기존 CSV가 그대로 남았습니다. 현재 테스트는 빈 디렉터리만 사용해 이름과 달리 stale 파일을 검증하지 않습니다. 반대 확장자 삭제 또는 active artifact manifest를 구현하고 실제 stale 파일을 미리 만든 테스트가 필요합니다.

추가 권장:

  • 새 runner/sync 스크립트의 custom range, corrupt/empty part, partial export/restore 테스트 추가
  • 28.8GB 위험을 고려해 감사 출력 기본값을 parquet으로 바꾸고 CSV를 명시적 호환 모드로 전환
  • IP rate limit을 다른 기기로 우회하라는 문구는 제거하고 API 정책에 맞춘 속도·공식 대체 소스 사용
  • 외부 기사 metadata는 query/source/license/provenance manifest와 partial_not_model_ready 상태를 함께 관리

현재 파일은 수집기 개발용 부분 cache로는 쓸 수 있지만, 학습·평가용 고정 뉴스 데이터로 승인하면 안 됩니다.

choigod1023 added a commit that referenced this pull request Aug 11, 2026
supply_news_risk 가 비영 0% 였다. 원인은 키워드도 수집량도 아니었다.

수집 기사 6,844건을 분류기에 통과시키면 공급 사건이 6,127건(89.5%)이다.

  port_or_logistics_disruption   5,033
  export_restriction_or_sanction   842
  factory_shutdown                 208
  war_or_armed_conflict             44

그런데 산출물에 남은 고유 기사는 36건이고 공급 사건은 0건이었다. 이 사건들은
분류기가 material="general_material" 을 부여하는데, 그 이름의 원자재 매핑이
없어 _match_stock_mapping 이 빈 결과를 돌려주고 전량 버려졌다.

항만 파업·전쟁·수출통제·물류 차질은 **특정 원자재에 귀속시킬 수 없는** 사건이다.
매핑을 알루미늄·라텍스까지 넓혀도 이 경로는 열리지 않는다. 정의상 전 품목에
적용해야 한다.

- SUPPLY_WIDE_EVENT_TYPES 를 전 품목 경로로 보낸다. factory_shutdown 은
  라텍스처럼 원자재가 특정되면 그쪽으로 먼저 매칭되고, 특정되지 않은 경우에만
  내려온다.
- 감사행은 사건당 1행만 남긴다. 품목별로 남기면 사건 1건이 6만 행이 되고
  6,127건이면 3.7억 행(약 190GB)이 되어 디스크가 먼저 터진다(PR #69 에서
  No space left on device 를 두 번 겪었다). 집계 단계에서 전 품목으로 펼친다.
- 기존 품목별 supply_news_risk 와는 **최댓값**으로 합친다. 합산하면 같은 사건이
  두 경로로 들어와 이중 계상된다.

실측 효과
  supply_news_risk        비영   0.0% → 98.1%  (중앙 0.1697 / 최대 0.2667)
  total_news_risk         최대 0.0860 → 0.3216
  module_c_supply_risk    최대 0.0326 → 0.1249  (3.8배)
  감사행                   3.7억 예상 → 831행

이로써 supply_signal 가중치의 45%(supply_news)가 죽어 있던 상태가 해소된다.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants