튜토리얼 2: PDF 문서 → SFT 훈련 데이터
이 튜토리얼에서는 로컬 PDF 문서를 eulerweave 파이프라인으로 처리하여 LLM 미세 조정(SFT)용 질문-답변 훈련 데이터를 생성하는 전체 과정을 안내합니다.
이 튜토리얼에서 배우는 내용:
- PDF 추출기로 문서에서 텍스트 추출
build_langextract_qna블록으로 QnA 쌍 자동 생성- LLM 없이 테스트할 수 있는 FakeProvider 모드
- Ollama를 사용한 실제 QnA 생성
사전 요구 사항: eulerweave가 설치되어 있어야 합니다. PDF 지원을 위해
pip install -e ".[pdf]"가 필요합니다. 튜토리얼 0: 설치를 참고하세요.
시나리오
당신에게는 기술 문서, 논문, 또는 교재가 PDF 파일로 있습니다. 이 문서를 SFT 훈련 데이터로 변환하고 싶습니다:
data/
paper.pdf ← 입력: 연구 논문 PDF
manual.pdf ← 입력: 기술 매뉴얼 PDF
out/
training_data.jsonl ← 출력: SFT 훈련용 QnA 데이터
단계 1: PDF 추출기 확인
PDF 추출기가 설치되었는지 확인합니다:
eulerweave plugins list
출력에 pdf 추출기가 표시되어야 합니다:
Extractors:
...
pdf eulerweave.core.io.pdf_extractor:PdfExtractor [.pdf]
...
보이지 않는다면:
pip install -e ".[pdf]"
단계 2: 매니페스트 작성
방법 A: FakeProvider로 테스트 (LLM 불필요)
먼저 LLM 없이 파이프라인이 올바르게 동작하는지 테스트합니다.
build_langextract_qna는 기본적으로 결정론적 FakeProvider를 사용하므로,
Ollama 없이도 전체 파이프라인을 실행할 수 있습니다.
manifest_pdf_test.yaml:
version: 1
track: sft
inputs:
- type: pdf
uri: data/paper.pdf
options:
strategy: auto # text → OCR 자동 폴백
page_range: "1-5" # 처음 5페이지만 (테스트용)
pipeline:
- id: norm1
type: normalize_text
slot: normalize
input_type: TextDocument
output_type: TextDocument
- id: filter1
type: heuristic_filter
slot: filter
input_type: TextDocument
output_type: TextDocument
params:
min_length: 50 # 50자 미만 페이지 건너뛰기
- id: qna1
type: build_langextract_qna
slot: build_task
input_type: TextDocument
output_type: SFTQnA
- id: exp1
type: export_jsonl
slot: export
input_type: SFTQnA
output_type: ExportedDataset
exports:
- type: jsonl
path: out/training_data.jsonl
profile:
cpu_only: true
allow_external_llm: false
핵심 포인트:
type: pdf— PDF 추출기를 사용하여 페이지별로 텍스트를 추출합니다.strategy: auto— 텍스트 추출을 먼저 시도하고, 실패하면 OCR로 폴백합니다.page_range: "1-5"— 특정 페이지만 처리합니다 (테스트에 유용).build_langextract_qna— 텍스트에서 QnA 쌍을 생성합니다.allow_external_llm: false— FakeProvider를 사용합니다 (LLM 불필요).
단계 3: 검증 및 실행 계획 확인
eulerweave validate manifest_pdf_test.yaml
[eulerweave] Validating manifest: manifest_pdf_test.yaml
[eulerweave] Parsing YAML ... OK
[eulerweave] Checking inputs ... OK (1 input, pdf)
[eulerweave] Checking pipeline blocks ... OK (4 blocks)
[eulerweave] Checking type chain ... OK
[eulerweave] Checking slot ordering ... OK
✓ Manifest is valid.
실행 계획 확인:
eulerweave plan manifest_pdf_test.yaml
Execution Plan
+---------+-----------------------+-----------+---------------+----------------+
| Step | Block | Slot | Input Type | Output Type |
+---------+-----------------------+-----------+---------------+----------------+
| 1 | norm1 | normalize | TextDocument | TextDocument |
| 2 | filter1 | filter | TextDocument | TextDocument |
| 3 | qna1 | build_task| TextDocument | SFTQnA |
| 4 | exp1 | export | SFTQnA | ExportedDataset|
+---------+-----------------------+-----------+---------------+----------------+
단계 4: FakeProvider로 실행
eulerweave run manifest_pdf_test.yaml \
--input data/paper.pdf \
--artifacts ./artifacts
[eulerweave] Loading manifest: manifest_pdf_test.yaml
[eulerweave] Input: data/paper.pdf (5 pages → 5 records)
[eulerweave] [1/4] norm1 (normalize_text) ...
[eulerweave] Processed 5 documents
[eulerweave] [2/4] filter1 (heuristic_filter) ...
[eulerweave] Processed 5 documents, 1 filtered (min_length=50)
[eulerweave] [3/4] qna1 (build_langextract_qna) ...
[eulerweave] Generated 4 QnA pairs
[eulerweave] [4/4] exp1 (export_jsonl) ...
[eulerweave] Exported 4 records
[eulerweave] Pipeline completed successfully.
출력 확인:
head -1 out/training_data.jsonl | python -m json.tool
{
"messages": [
{"role": "user", "content": "What is the main topic of: 'The paper presents ...'?"},
{"role": "assistant", "content": "The text discusses: The paper presents ... [fake-a1b2c3]"}
]
}
FakeProvider는 MD5 해시 기반의 결정론적 QnA를 생성합니다. 파이프라인이 올바르게 동작하는지 확인하는 데 충분합니다.
단계 5: Ollama로 실제 QnA 생성
실제 LLM을 사용하여 고품질 QnA를 생성하려면 Ollama를 설정합니다.
5-1. Ollama 설치 및 시작
# Ollama 설치 (아직 안 했다면)
curl -fsSL https://ollama.com/install.sh | sh
# Ollama 서버 시작
ollama serve
# 모델 다운로드
ollama pull gpt-oss:20b
5-2. Ollama용 매니페스트 작성
manifest_pdf_ollama.yaml:
version: 1
track: sft
inputs:
- type: pdf
uri: data/paper.pdf
options:
strategy: auto
pipeline:
- id: norm1
type: normalize_text
slot: normalize
input_type: TextDocument
output_type: TextDocument
- id: filter1
type: heuristic_filter
slot: filter
input_type: TextDocument
output_type: TextDocument
params:
min_length: 100
max_length: 50000
- id: qna1
type: build_langextract_qna
slot: build_task
input_type: TextDocument
output_type: SFTQnA
params:
model: "gpt-oss:20b"
base_url: "http://localhost:11434"
- id: exp1
type: export_jsonl
slot: export
input_type: SFTQnA
output_type: ExportedDataset
exports:
- type: jsonl
path: out/training_data.jsonl
profile:
cpu_only: false
allow_external_llm: true # ← LLM 사용 허용 필수
핵심 변경점:
params.model— 사용할 Ollama 모델params.base_url— Ollama 서버 주소 (기본:http://localhost:11434)allow_external_llm: true— LLM 호출을 허용
5-3. 실행
eulerweave run manifest_pdf_ollama.yaml \
--input data/paper.pdf \
--artifacts ./artifacts
5-4. Ollama 출력 확인
head -1 out/training_data.jsonl | python -m json.tool
{
"messages": [
{
"role": "user",
"content": "What is the main contribution of this paper?"
},
{
"role": "assistant",
"content": "The paper proposes a novel approach to transformer-based language models that reduces computational complexity while maintaining performance. The key contribution is a sparse attention mechanism that achieves O(n log n) complexity compared to the standard O(n²) self-attention."
}
]
}
PDF 추출기 옵션 상세
strategy
| 값 | 동작 | 의존성 |
|---|---|---|
auto (기본) |
텍스트 추출 시도 후, 실패 시 OCR 폴백 | pdfminer.six + (선택) ocrmypdf |
text |
pdfminer.six로 텍스트만 추출 | pdfminer.six |
ocr |
OCRmyPDF로 이미지 기반 추출 | pdfminer.six + ocrmypdf |
page_range
페이지 범위를 문자열로 지정합니다:
options:
page_range: "1-10" # 1~10 페이지
page_range: "5" # 5페이지만
생략하면 전체 페이지를 추출합니다.
PDF → 레코드 매핑
PDF 추출기는 페이지당 하나의 레코드를 생성합니다:
paper.pdf (30 pages) → 30개의 CanonicalRecord
├── record[0]: page 1 텍스트 + {page: 1, source: "paper.pdf"}
├── record[1]: page 2 텍스트 + {page: 2, source: "paper.pdf"}
└── ...
각 레코드에는 출처 메타데이터가 포함됩니다:
source_uri: 원본 파일 경로source_type:"pdf"page: 페이지 번호
실전 파이프라인: 여러 PDF + 메트릭
여러 PDF를 처리하고 품질 메트릭도 수집하는 파이프라인:
version: 1
track: sft
inputs:
- type: pdf
uri: data/paper1.pdf
options:
strategy: text
- type: pdf
uri: data/paper2.pdf
options:
strategy: auto
page_range: "1-20"
pipeline:
- id: norm1
type: normalize_text
slot: normalize
input_type: TextDocument
output_type: TextDocument
- id: filter1
type: heuristic_filter
slot: filter
input_type: TextDocument
output_type: TextDocument
params:
min_length: 100
- id: dedup1
type: dedup_exact
slot: dedup
input_type: TextDocument
output_type: TextDocument
- id: m1
type: metrics_text_basic
slot: metrics
input_type: TextDocument
output_type: TextDocument
- id: qna1
type: build_langextract_qna
slot: build_task
input_type: TextDocument
output_type: SFTQnA
params:
model: "gpt-oss:20b"
- id: exp1
type: export_jsonl
slot: export
input_type: SFTQnA
output_type: ExportedDataset
exports:
- type: jsonl
path: out/training_data.jsonl
profile:
cpu_only: false
allow_external_llm: true
이 파이프라인은:
- 두 PDF에서 텍스트를 추출합니다 (총 페이지 수만큼 레코드 생성).
- 텍스트를 정규화하고 짧은 페이지를 필터링합니다.
- 정확히 동일한 페이지를 중복 제거합니다.
- 텍스트 기본 통계를 수집합니다 (메트릭).
- 각 페이지에서 QnA 쌍을 생성합니다.
- 최종 결과를 JSONL로 내보냅니다.
완성된 훈련 데이터 포맷
최종 출력 out/training_data.jsonl의 각 줄은 다음과 같습니다:
{
"messages": [
{"role": "user", "content": "What does the paper discuss about attention mechanisms?"},
{"role": "assistant", "content": "The paper discusses a novel sparse attention mechanism..."}
]
}
이 형식은 OpenAI, Ollama, 그리고 대부분의 SFT 훈련 프레임워크와 직접 호환됩니다.
전체 과정 요약
# 1. PDF 지원 설치
pip install -e ".[pdf,llm]"
# 2. Ollama 시작 (실제 QnA 생성 시)
ollama serve && ollama pull gpt-oss:20b
# 3. 매니페스트 작성
cat > manifest.yaml << 'EOF'
version: 1
track: sft
inputs:
- type: pdf
uri: data/paper.pdf
options:
strategy: auto
pipeline:
- id: norm1
type: normalize_text
slot: normalize
input_type: TextDocument
output_type: TextDocument
- id: filter1
type: heuristic_filter
slot: filter
input_type: TextDocument
output_type: TextDocument
params:
min_length: 100
- id: qna1
type: build_langextract_qna
slot: build_task
input_type: TextDocument
output_type: SFTQnA
params:
model: "gpt-oss:20b"
- id: exp1
type: export_jsonl
slot: export
input_type: SFTQnA
output_type: ExportedDataset
exports:
- type: jsonl
path: out/training_data.jsonl
profile:
cpu_only: false
allow_external_llm: true
EOF
# 4. 검증
eulerweave validate manifest.yaml
# 5. 실행
eulerweave run manifest.yaml --input data/paper.pdf --artifacts ./artifacts
# 6. 결과 확인
wc -l out/training_data.jsonl
head -1 out/training_data.jsonl | python -m json.tool
트러블슈팅
ModuleNotFoundError: No module named 'pdfminer'
PDF 의존성을 설치합니다:
pip install -e ".[pdf]"
strategy: ocr에서 ImportError: ocrmypdf
OCR 의존성을 설치합니다:
pip install -e ".[pdf_ocr]"
PDF에서 텍스트가 추출되지 않음 (빈 레코드)
스캔된 PDF일 가능성이 높습니다. strategy: ocr를 사용하세요:
options:
strategy: ocr
Ollama 연결 오류
Ollama가 실행 중인지 확인합니다:
curl http://localhost:11434/api/tags
다음 단계
- 튜토리얼 3: HuggingFace → 훈련 데이터 — HF에서 데이터셋을 다운로드하여 처리
- 튜토리얼 4: SFT 트랙 심화 — 세 가지 SFT 빌더 블록 비교
- 튜토리얼 8: 메트릭 — 파이프라인에 품질 통계 추가