> EulerWeave > 튜토리얼 > PDF → SFT 훈련 데이터

튜토리얼 2: PDF 문서 → SFT 훈련 데이터

이 튜토리얼에서는 로컬 PDF 문서를 eulerweave 파이프라인으로 처리하여 LLM 미세 조정(SFT)용 질문-답변 훈련 데이터를 생성하는 전체 과정을 안내합니다.

이 튜토리얼에서 배우는 내용:

사전 요구 사항: 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

핵심 포인트:


단계 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 사용 허용 필수

핵심 변경점:

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"}
  └── ...

각 레코드에는 출처 메타데이터가 포함됩니다:


실전 파이프라인: 여러 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

이 파이프라인은:

  1. 두 PDF에서 텍스트를 추출합니다 (총 페이지 수만큼 레코드 생성).
  2. 텍스트를 정규화하고 짧은 페이지를 필터링합니다.
  3. 정확히 동일한 페이지를 중복 제거합니다.
  4. 텍스트 기본 통계를 수집합니다 (메트릭).
  5. 각 페이지에서 QnA 쌍을 생성합니다.
  6. 최종 결과를 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

다음 단계

← 이전: 빠른 시작 목록으로 다음: HuggingFace → SFT 훈련 데이터 →