
AI 기업들이 웹에서 LLM 학습 데이터를 구축하는 방법
요약
AI 기업들이 웹 크롤링 데이터를 LLM 학습용 코퍼스로 변환하는 데이터 파이프라인 과정을 설명합니다. 데이터 수집부터 정규화, 추출, 중복 제거, 필터링에 이르는 핵심 단계를 다룹니다.
핵심 포인트
- 가공되지 않은 HTML은 학습에 부적합하며 정제 과정이 필수적임
- 데이터 파이프라인의 6단계: Fetch, Normalize, Extract, Deduplicate, Filter, Record
- 차단 방지를 위한 프록시 및 API 활용 등 효율적인 수집 전략 필요
- 데이터 정제 후 실제 학습 가능한 데이터 양은 원본보다 크게 감소함
요약(TL;DR): AI 기업들은 가공되지 않은 HTML로 언어 모델을 학습시키지 않습니다. AI 연구소들이 웹 크롤링 데이터를 어떻게 가져오고(fetch), 추출(extract)하며, 중복을 제거(deduplicate)하고, 필터링(filter)하여 깨끗하고 재현 가능한 코퍼스(corpus)로 만드는지 알아보세요.
모델을 학습시키고 싶다면...
웹에서 가져온 가공되지 않은 데이터로 AI를 학습시킬 수 있을까요? 있는 그대로는 불가능합니다. 여러분이 실제로 원하는 데이터는 가져온 바이트(bytes)의 아주 작은 부분에 불과합니다. HTML 수프(HTML soup)를 제거한 후에도 중복된 페이지, 빈약한 콘텐츠, 그리고 한 가지 까다로운 문제에 직면하게 됩니다. 바로 변호사들이 문제를 제기했을 때, "이것이 제 모델이 학습한 정확한 데이터셋입니다"라고 단일 데이터셋을 지목하며 말할 수 없다는 점입니다.
그렇다면 AI/ML 팀은 실제로 어떻게 학습 데이터를 구축할까요? 이를 이해하기 위해, 저는 그들이 사용하는 것과 동일한 파이프라인 — 가져오기(fetch), 정규화(normalize), 추출(extract), 중복 제거(deduplicate), 필터링(filter), 기록(record) — 의 축소 버전을 모든 단계가 일어나는 것을 관찰할 수 있을 만큼 작은 규모로 구축했습니다. 원재료를 수집하기 위해, 저는 3단계 가져오기 폴백(fetch fallback) 방식을 사용하여 Arch Linux Wiki를 크롤링했습니다. 먼저 직접 HTTP를 시도하고, 서버가 차단하거나 봇 방어벽(bot wall)을 제공하면 Bright Data 주거용 프록시(residential proxy)를 사용하며, 마지막 수단으로 리비전(revision) URL을 위해 MediaWiki API를 사용했습니다.
저는 일반 문서, 토론 페이지(talk pages), 템플릿(templates), 도움말 및 목록 페이지, 중복된 URL, 그리고 과거 리비전들을 의도적으로 섞었습니다. 그 후 URL을 정규화하고, 텍스트를 추출하며, 콘텐츠의 중복을 제거하고, 남은 것들을 필터링했습니다.
가져온 데이터(Fetched): 500 페이지 33.8 MB HTML
추출 후(After extraction): 439 페이지 2.1 MB 텍스트
실제 학습 가능(Actually trainable): 286 페이지 1.3 MB 텍스트
이 포스트에서는 AI 팀이 수십억 개의 무질서한 웹페이지를 어떻게 학습 코퍼스(training corpus)로 전환하는지에 대한 작동하는 멘탈 모델(mental model)을 제공하겠습니다. 더 깊이 파고들고 싶다면 샘플 데이터가 포함된 관련 리포지토리(repo)를 확인하세요: https://github.com/sixthextinction/arch-corpus-funnel
💡 이 글은 가공되지 않은 웹 텍스트를 모델이 읽을 수 있는 깨끗한 문서로 변환하는 것에 관한 것입니다. SFT (Supervised Fine-Tuning) 데이터, RLHF (Reinforcement Learning from Human Feedback) 선호도 쌍, 또는 합성 증류 (Synthetic Distillation)에 관한 것이 아닙니다. 이 글은 그 모든 것이 가능해지기 전에 일어나는, 지루하지만 핵심적인 기반 작업에 관한 것입니다.
왜 AI 기업들은 스크래핑한 웹 데이터를 직접 사용하지 않을까요?
왜냐하면 AI 학습은 시간당 비용이 매우 비싼 클러스터(Cluster) 상에서, 수많은 에포크 (Epochs) 동안 동일한 데이터를 반복해서 읽어야 하기 때문입니다.
GPU가 배치 (Batch)를 요청할 때마다 모든 것을 다시 다운로드하는 것은 속도가 느리고, 막대한 비용이 들며, 무엇보다도 여러분의 실행 과정을 **재현 불가능 (Non-reproducible)**하게 만듭니다. 웹 페이지는 계속 변합니다. 실제로 수집한 데이터를 고정(Freeze)해두지 않으면, "당신의 모델이 실제로 무엇을 학습했는가?"라는 질문에 더 이상 답할 수 없게 됩니다. 참고로, 이는 수십억 달러 규모의 저작권 소송에서 정확히 제기되는 종류의 질문입니다.
따라서 가장 좋은 관행은 한 번 다운로드하고, 한 번 정제한 뒤, 그 결과를 저장하고, 이를 여러 번 읽는 것입니다.
그렇게 고정되고, 정제되어, 저장된 결과물이 바로 **코퍼스 (Corpus, 말뭉치)**입니다. 모든 워커 (Worker)가 동일한 바이트 (Bytes)를 읽게 되며, 이를 통해 학습 과정은 막연하게 기억하는 것이 아니라 6개월 후에도 감사 (Audit)할 수 있는 대상이 됩니다.
**코퍼스 (Corpus)**는 학습에 사용되는, 정제된 문서들의 고정되고 체크섬 (Checksummed) 처리된 집합입니다. 모든 워커가 동일한 바이트를 읽으므로, 학습 실행은 몇 달 후에도 재현 가능하고 감사 가능합니다.
크롤링 (Crawl)은 원재료를 제공하지만, 실제로 그것을 데이터셋 (Dataset)이라고 부를 수 있는 무언가로 바꾸는 것은 여러분의 학습 파이프라인 (Training Pipeline)입니다.
우리의 미니 학습 파이프라인
우리가 무엇을 할 것인지에 대한 핵심 내용을 알려드리겠습니다. 단계별로 안내해 드리겠습니다.
시각적으로, 모든 문서는 이 동일한 경로를 거칩니다.
- 페이지 가져오기 (Fetch the pages). URL 목록을 가져와 direct → proxy → API fallback 체인을 통해 크롤링하고, 가공되지 않은 HTML (raw HTML)을 확보합니다.
- URL 정규화 (Normalize the URLs). 각 리소스에 하나의 식별자를 부여하고, 중복된 URL을 제거합니다.
- 텍스트 추출 (Extract the text). 모든 내비게이션 바 (navbars), 배너 (banners), 그리고 마크업 수프 (markup soup) 아래에서 기사 내용을 뽑아냅니다.
- 정확한 중복 제거 (Remove exact duplicates). 텍스트를 해싱 (Hash)하여 동일한 복사본을 삭제합니다.
- 유사 중복 제거 (Remove near-duplicates). 정확한 해싱으로는 구조적으로 잡아낼 수 없는 이전 버전이나 미세하게 수정된 복사본들을 포착합니다.
- 콘텐츠 품질 확인 (Check content quality). 내용이 빈약하거나 (thin), 링크가 너무 많거나, 상용구 (boilerplate)가 많은 문서는 거부합니다.
- 언어 확인 (Check the language). 코퍼스 (corpus)가 영어로 구성되어야 한다면, 영어로 된 문서만 유지하고 그렇지 않은 것은 버립니다.
- 매니페스트 작성 (Write a manifest). 모든 개별 페이지에 어떤 일이 일어났는지, 그리고 그 이유가 무엇인지 기록합니다.
각 단계는 이전 단계가 이미 실행되었기 때문에 작동합니다. 다운스트림 (downstream)의 무언가를 망가뜨리지 않고서는 이 순서를 건너뛰거나 재정렬할 수 없습니다. 이 글의 나머지 부분은 단순히 이를 주장하는 대신, 실제 수치를 통해 여러분에게 이를 증명하는 과정입니다.
어느 단계에서든 조기 종료될 수 있습니다. 탈락된 행 (rows)은 drop_stage와 drop_reason과 함께 여전히 매니페스트에 기록됩니다. 살아남은 데이터는 trainable로 표시된 최종 행을 얻게 됩니다. 가져온 모든 URL은 어떤 방식으로든 매니페스트에 남게 됩니다.
👉 전체
run_pipeline단계는 여기에서 확인하세요: https://github.com/sixthextinction/arch-corpus-funnel/blob/main/funnel.py
AI 학습을 배우는 초보자에게 깨끗하고 예측 가능한 소스가 가장 좋은 이유
첫 번째 학습 파이프라인 (training pipeline)을 구축할 때는 한 번에 하나의 문제만 다루어야 합니다. 깨끗한 소스를 사용하면 스팸, 깨진 HTML, 다국어, 그리고 극도로 다른 페이지 레이아웃이 하나의 거대한 디버깅 세션으로 뒤섞이는 대신, 어떤 단계에서 무엇이 걸러졌는지 명확하게 알 수 있습니다.
Arch Linux Wiki는 이를 위해 이상적입니다. 훌륭한 영어 산문, 일관된 구조, 그리고 쓰레기 데이터가 거의 없기 때문입니다.
하지만 완벽한 문서로만 구성된 코퍼스 (Corpus)는 AI/ML 연구소에서 실제로 수행하는 작업을 실제로 반영하지 못할 것입니다. 그래서 저는 실제 환경의 지저분한 크롤링 (Crawl)을 더 잘 반영할 수 있는 페이지들을 의도적으로 섞었습니다.
- 일반 문서 (Regular articles) — 살아남아야 함.
- 토론 페이지 (Talk pages) — 내용이 빈약하거나 대부분 토론 위주임.
- 템플릿 페이지 (Template pages) — 짧고 재사용 가능한 코드 조각.
- 도움말 페이지 (Help pages) — 메타 문서 (Meta-documentation).
- 목록 페이지 (List pages) — 링크가 많은 인덱스.
- 빈약한 하위 페이지 (Thin subpages) — 최소한의 콘텐츠.
- 대체 URL 형태 (Alternate URL forms) — 동일한 페이지를 가리키는 서로 다른 URL.
- 이력 수정본 (Historical revisions) — 현재 문서와 거의 중복되는 이전 버전.
예시는 ArchWiki에서 가져왔지만, 이 패턴은 보편적입니다. 모든 크롤링에는 중복 URL, 저품질 페이지, 과거 복사본, 그리고 상용구 (Boilerplate)가 일정 비율로 섞여 있습니다.
500페이지 분량의 크롤링 데이터를 조립했으니, 첫 번째 단계인 페칭 (Fetching)부터 시작하겠습니다.
1단계: 페이지 페칭 (Fetching) — AI 팀이 학습을 위해 페이지를 안정적으로 크롤링하는 방법
1단계는 URL 리스트를 가져와 가공되지 않은 HTML을 다운로드하며, 서버가 요청을 거부할 경우를 대비한 폴백 체인 (Fallback chain)을 포함합니다.
AI 기업들은 한 번 다운로드한 후 결과를 저장합니다. 매 학습 에포크 (Epoch)마다 다시 페칭하는 것은 느리고, 비용이 많이 들며, 재현이 불가능하기 때문입니다. 하지만 대규모로 페칭하는 것은 단순히 curl을 사용하는 것만큼 간단하지 않습니다. 서버는 봇 (Bot)을 차단하고, 챌린지 페이지 (Challenge pages)를 띄우며, 위키의 오래된 수정 버전 URL은 종종 특별한 처리가 필요합니다.
우리의 URL 리스트는 seeds-arch.txt 파일로 제공되며, 탭으로 구분되어 있어 언제든 사용자의 URL로 교체할 수 있습니다. 1단계의 엔트리 스크립트는 fetch_from_seeds.py입니다 (시드 파일을 읽어 HTML을 data/ 디렉토리에 쓰고 fetch_log.jsonl에 기록합니다).
👉 크롤링 스크립트와 시드 리스트 확인: https://github.com/sixthextinction/arch-corpus-funnel/blob/main/fetch_from_seeds.py 및 https://github.com/sixthextinction/arch-corpus-funnel/blob/main/seeds-arch.txt
우리의 fetch (가져오기) 레이어는 차단되었음을 나타낼 수 있는 challenge-page (챌린지 페이지) 마커를 검색하며, 직접적인 HTTP, 그 다음 Bright Data 주거용 프록시 (residential proxy), 마지막으로 revision (수정) URL을 위한 MediaWiki API를 사용하는 waterfall (폭포수) 방식을 사용합니다.
👉
fetch_page,build_proxy, 그리고is_bot_page의 전체 코드는 여기에서 확인하세요: https://github.com/sixthextinction/arch-corpus-funnel/blob/main/lib/fetch.py
BOT_MARKERS = (b"not a bot", b"Making sure you", b"xess.min.css", b"within.website")
def is_bot_page(body: bytes) -> bool:
...
함께 따라오고 계신다면, .env.example을 .env로 복사하고 여기에서 가입한 후 대시보드에서 제공되는 BRIGHT_DATA_PROXY_RESIDENTIAL_USERNAME과 BRIGHT_DATA_PROXY_RESIDENTIAL_PASSWORD를 입력하세요 — 새로운 종량제 (pay-as-you-go) 계정은 신용카드 없이도 매달 5,000개의 무료 크레딧을 받을 수 있으며, 크레딧을 모두 사용하면 즉시 중단되므로 예상치 못한 청구서가 발생하지 않습니다. fetch_from_seeds.py는 .env를 자동으로 로드합니다.
우리의 Arch Wiki 실행 결과, 28개의 URL이 직접 HTTP 방식에서 봇 차단벽 (bot wall)에 부딪혔습니다. 28개 모두 주거용 프록시 (residential proxy) 폴백 (fallback)을 통해 복구되었습니다. 더 중요한 점은… 모든 fetch_method가 fetch_log.jsonl에 기록되어 각 페이지가 어떤 경로를 거쳤는지 감사 (audit)할 수 있다는 것입니다.
1단계 결과: 500개 페이지 가져오기 완료, 33.8 MB의 raw HTML (원시 HTML).
2단계: URL 정규화 (Normalize) — AI 기업들이 학습 전에 canonicalize (정규화)를 수행하는 이유.
디스크에 HTML이 저장되면, 2단계는 비용이 많이 드는 처리가 시작되기 전에 모든 문서에 정확히 하나의 식별자 (identity)를 부여합니다.
AI 기업들은 중복된 페이지가 추출, 해싱(hashing), 필터링되어 최종 학습 코퍼스(training corpus)에서 과도하게 비중을 차지하지 않도록 URL을 정규화(canonicalize)합니다. 중복으로 판단되어 곧 버려질 페이지에 컴퓨팅 자원을 소모할 이유가 없으므로, 이 작업은 가져오기(fetch) 직후에 실행되어야 합니다.
**정규 URL (canonical URL)**은 하나의 리소스를 나타내는 단일화된 정규 주소입니다. 프래그먼트(fragment)는 제거하고, 트래킹 파라미터(tracking parameters)는 버리며, 의미 있는 파라미터(예: 위키의
oldid)는 유지합니다.
웹은 다양한 주소를 통해 동일한 콘텐츠를 제공하는 것을 즐깁니다:
/title/Pacman
/title/Pacman#Usage
/title/Pacman?utm_source=example
학습 데이터셋에서 이는 무해한 반올림 오차가 아닙니다. 추가된 복사본 하나하나가 모델이 해당 페이지를 보는 횟수를 부풀리며, 해당 중복 페이지들이 담고 있는 내용 쪽으로 모델을 편향(biasing)시킵니다.
정규화(Canonicalization)는 모든 URL을 하나의 정규화된 형태로 축소합니다. 프래그먼트를 제거하고, 반환되는 내용을 바꾸지 않는 파라미터는 버리며, 내용을 바꾸는 파라미터는 유지합니다(위키의 oldid는 다른 버전을 선택하므로 유지됩니다). 이 과정을 통해 한 페이지에 대한 모든 주소가 동일한 키(key)에 매핑될 때까지 반복합니다.
👉 전체 정규화 코드 확인: https://github.com/sixthextinction/arch-corpus-funnel/blob/main/lib/canonicalize.py
def canonicalize(url: str) -> str:
parsed = urlparse(url)
if parsed.path.startswith("/title/"):
...
웹 규모(web scale)에서는 원본 문자열을 메모리에 유지하는 대신, 해당 정규화된 형태를 해싱(SHA256(canonical_url) 또는 여기서 파생된 UUID)하여 관리합니다.
2단계 결과: URL 중복 제거 후 440개의 고유 페이지 확보. 아직 이 중 어느 것도 깨끗한 텍스트 상태는 아닙니다. 다음 단계에서 이를 처리합니다.
3단계: 중복 제거 전 텍스트 추출
**3단계 (Step 3)**는 마크업 (markup)을 제거하고 기사 텍스트를 분리합니다. 가공되지 않은 웹 데이터는 메타데이터가 뒤섞인 난장판과 같기 때문에, AI 기업들은 다른 분석을 수행하기 전에 실제 콘텐츠를 추출합니다. 만약 탐색 메뉴 (nav menus), 푸터 (footers), 타임스탬프 (timestamps)가 여전히 붙어 있는 상태에서 해싱 (hashing)을 하거나 링크 밀도 (link density)를 측정한다면, 실제 중복 항목을 놓치고 품질을 잘못 판단하게 됩니다. 이것이 파이프라인에서 추출 (extraction) 단계가 중복 제거 (deduplication)나 품질 필터링 (quality filtering)보다 앞서 수행되는 이유입니다.
생각해 보십시오. 만약 단계를 건너뛰어 페이지 전체를 해싱함으로써 기사의 중복을 제거하려고 시도한다면, 탐색 배너 (navigation banner)나 타임스탬프의 아주 작은 변화만으로도 해시 값이 달라져 실제 중복 항목을 놓치게 될 것입니다.
게다가 모든 후속 프로세스 (downstream process)는 주변의 HTML이 아닌 순수 콘텐츠 (pure content)를 보고 있다는 가정하에 작동합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기