Transformers Pipeline: 몇 줄의 코드로 NLP 모델 실행하기

1. 개요

Hugging Face Transformers는 pretrained NLP/vision/audio 모델을 tokenizer나 config를 로드하거나 tensor를 수동으로 처리할 필요 없이 단 몇 줄의 코드로 실행할 수 있는 pipeline() API를 제공합니다. 이 글은 pipeline()이 어떻게 작동하는지, 일반적인 작업(sentiment analysis, NER, summarization, zero-shot classification)에 어떻게 사용하는지, 그리고 더 자세한 제어가 필요할 때 언제 “더 낮은 수준으로” 내려가야 하는지를 이해하는 데 도움을 줍니다.

이 글을 읽은 후에는 Python 애플리케이션에서 Hugging Face 모델로 inference를 실행하고, output을 확인하는 테스트를 작성하며, production에 배포하기 전에 pipeline의 제한사항을 알게 될 것입니다.

2. 설치

transformers 라이브러리와 backend(PyTorch 또는 TensorFlow)가 필요합니다. 예를 들어 PyTorch를 사용할 경우:

pip install transformers torch

작성 시점 기준 권장 버전: transformers>=4.40, torch>=2.2.

주의: 처음 실행할 때, pipeline()은 Hugging Face Hub에서 모델을 다운로드하여 ~/.cache/huggingface에 캐시합니다. 모델은 수백 MB가 될 수 있으므로 CI 컨테이너에서 실행하는 경우, 이 단계에 필요한 시간과 대역폭을 고려하거나 cache를 이미지에 미리 포함시켜야 합니다.

3. 기본 Pipeline: Sentiment Analysis

from transformers import pipeline

classifier = pipeline("sentiment-analysis")
result = classifier("I really love this product, it works great!")
print(result)
  • 1번 줄: pipeline 함수를 import합니다.
  • 3번 줄: sentiment-analysis task에 대한 pipeline을 생성합니다. 모델을 지정하지 않으면 Hugging Face가 자동으로 기본 모델을 선택합니다(보통 distilbert-base-uncased-finetuned-sst-2-english).
  • 4번 줄: pipeline을 함수처럼 호출하고 텍스트를 전달합니다.

출력:

[{'label': 'POSITIVE', 'score': 0.9998}]

pipeline은 batch 처리를 지원하기 때문에 list를 반환합니다(여러 개의 문자열을 list로 전달해도 작동합니다).

4. 모델 및 Tokenizer 지정

Production 코드에서는 기본 모델에 의존하지 마세요. Hugging Face가 버전 간에 기본값을 변경할 수 있기 때문입니다. 명확하게 지정하세요:

from transformers import pipeline

classifier = pipeline(
    task="sentiment-analysis",
    model="distilbert-base-uncased-finetuned-sst-2-english",
    device=-1  # -1 = CPU, 0 = 첫 번째 GPU
)

results = classifier([
    "This movie was a complete waste of time.",
    "Absolutely fantastic service!"
])
for r in results:
    print(r)
  • model: checkpoint를 명확히 지정하여 변경될 수 있는 기본값에 의존하지 않습니다.
  • device: -1은 CPU에서 실행하고, 0은 GPU 인덱스 0에서 실행합니다. 머신에 CUDA가 없는데 device=0으로 설정하면 pipeline이 오류를 발생시킵니다.
  • classifier에 문장 list를 전달하여 batch 처리를 수행하면, 한 번에 한 문장씩 호출하는 것보다 효율적입니다.

출력:

{'label': 'NEGATIVE', 'score': 0.9995}
{'label': 'POSITIVE', 'score': 0.9997}

5. Named Entity Recognition (NER)

from transformers import pipeline

ner = pipeline("ner", model="dslim/bert-base-NER", aggregation_strategy="simple")
text = "Satya Nadella is the CEO of Microsoft, based in Redmond."
for entity in ner(text):
    print(entity["word"], "->", entity["entity_group"], f"{entity['score']:.2f}")
  • aggregation_strategy="simple": sub-token(예: Sat, ##ya)을 하나의 완전한 entity로 병합하여 token 조각을 따로 반환하지 않습니다.
  • entity_group: entity 타입 레이블(PER, ORG, LOC…).

출력:

Satya Nadella -> PER 0.99
Microsoft -> ORG 0.99
Redmond -> LOC 0.98

주의: aggregation_strategy를 생략하면 BIO-tagging(B-PER, I-PER…)이 있는 개별 token을 받게 되며, 수동으로 병합해야 합니다. 신중하게 처리하지 않으면 버그가 발생하기 쉽습니다.

6. Zero-Shot Classification

Zero-shot은 모델이 구체적으로 학습하지 않은 임의의 레이블로 텍스트를 분류할 수 있습니다.

from transformers import pipeline

classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")

result = classifier(
    "This new phone has an amazing camera and battery life.",
    candidate_labels=["technology", "sports", "politics", "finance"]
)
print(result["labels"][0], result["scores"][0])
  • candidate_labels: 직접 정의하는 레이블 목록으로, 모델을 fine-tune할 필요가 없습니다.
  • 모델은 Natural Language Inference 기법을 사용하여 텍스트가 각 레이블을 “entail”할 확률을 계산합니다.

출력:

technology 0.93

Zero-shot은 빠른 prototype에 매우 편리하지만, 작업에 직접 fine-tune된 모델보다 느리고 정확도가 낮습니다.

7. 텍스트 요약

from transformers import pipeline

summarizer = pipeline("summarization", model="facebook/bart-large-cnn")

article = """
The Transformers library by Hugging Face provides thousands of pretrained
models to perform tasks on texts such as classification, information
extraction, question answering, summarization, translation, and more.
It bridges research and production by exposing a simple pipeline API
on top of complex model architectures.
"""

summary = summarizer(article, max_length=40, min_length=10, do_sample=False)
print(summary[0]["summary_text"])
  • max_length/min_length: 요약의 token 길이를 제한하며, 단어 수가 아닙니다.
  • do_sample=False: random sampling 대신 greedy/beam decoding을 사용하여 실행 간에 안정적인 결과를 제공합니다.

출력 (축약):

Transformers library provides pretrained models for classification,
extraction, question answering, and summarization.

8. Pipeline 내부는 어떻게 되어 있나요?

pipeline()은 실제로 3가지 구성 요소를 결합합니다: tokenizer, model, 그리고 각 task에 대한 후처리 로직. 더 자세한 제어를 위해 직접 조립할 수 있습니다:

from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch

model_name = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(model_name)

inputs = tokenizer("Great product!", return_tensors="pt")
with torch.no_grad():
    logits = model(**inputs).logits

probs = torch.softmax(logits, dim=-1)
label_id = probs.argmax().item()
print(model.config.id2label[label_id], probs[0][label_id].item())
  • AutoTokenizer/AutoModelForSequenceClassification: 모델 이름에 따라 올바른 아키텍처를 자동으로 감지합니다.
  • tokenizer(..., return_tensors="pt"): 일반 숫자 list 대신 PyTorch tensor를 반환합니다.
  • torch.no_grad(): inference이므로 gradient 계산을 비활성화합니다. training이 아니므로 메모리를 절약하고 더 빠릅니다.
  • model.config.id2label: class 인덱스를 이름에 매핑합니다(모델에 따라 LABEL_0/LABEL_1 또는 POSITIVE/NEGATIVE).

이 수준을 이해하면 pipeline이 이상한 결과를 줄 때 디버깅하거나, custom logic이 필요할 때(예: argmax를 취하는 대신 custom threshold 적용) 도움이 됩니다.

9. Pipeline을 위한 테스트 작성

모델 output은 버전 간에 약간 변할 수 있으므로, 테스트는 정확한 decimal 일치가 아닌 “동작”을 확인해야 합니다.

import pytest
from transformers import pipeline

@pytest.fixture(scope="module")
def sentiment_classifier():
    return pipeline(
        "sentiment-analysis",
        model="distilbert-base-uncased-finetuned-sst-2-english",
    )

def test_classifies_positive_sentence(sentiment_classifier):
    result = sentiment_classifier("I absolutely love this!")[0]

    assert result["label"] == "POSITIVE"
    assert result["score"] > 0.9

def test_classifies_negative_sentence(sentiment_classifier):
    result = sentiment_classifier("This is the worst experience ever.")[0]

    assert result["label"] == "NEGATIVE"
    assert result["score"] > 0.9
  • scope="module": fixture는 전체 test 파일에 대해 모델을 한 번만 로드하여 각 test case마다 모델을 다시 로드하는 것을 피합니다. 모델 로드는 수 초에서 수십 초가 소요됩니다.
  • label과 score threshold에 기반하여 assert하고, library 버전 간에 약간 변할 수 있으므로 정확한 score 값을 hardcode하지 마세요.

주의: 이 유형의 테스트를 실행하려면 실제 모델을 로드해야 하므로 CI 루프의 빠른 단위 테스트에는 적합하지 않습니다. integration test로 분리하거나 주변 business logic을 테스트할 때 pipeline을 mock하세요.

10. Pipeline을 사용하면 안 되는 경우

Pipeline은 prototype과 표준 작업에는 매우 좋지만 제한이 있습니다:

  • 큰 배치, 높은 성능: pipeline은 batching, padding, dynamic batching을 수동으로 관리하는 것만큼 최적화되지 않습니다.
  • 복잡한 custom logic이 필요: 예를 들어 여러 조건의 후처리를 적용하거나, 여러 모델을 결합하거나, hidden states에 접근해야 합니다 — 섹션 8처럼 AutoModel + tokenizer를 직접 사용하세요.
  • 대규모 production 서빙: Text Generation Inference 또는 ONNX Runtime/TensorRT와 같은 전문 도구를 고려하여 latency를 최적화해야 합니다. request handler에서 pipeline()을 직접 호출하는 대신입니다.
  • CPU에 너무 큰 모델: bart-large-mnli 또는 수십억 개의 매개변수가 있는 LLM과 같은 모델은 CPU에서 매우 느리게 실행됩니다. GPU 또는 quantization이 필요합니다.

11. 결론

Hugging Face Transformers의 pipeline()은 pretrained NLP 모델로 inference를 실행하는 가장 빠른 방법입니다. Sentiment analysis, NER, summarization 또는 zero-shot classification에 단 몇 줄의 코드만 필요합니다. 성능이나 처리 로직을 더 자세히 제어해야 할 때, pipeline을 분리된 tokenizer와 model로 “분해”하여 직접 관리할 수 있습니다. 기본값을 사용하는 대신 항상 명시적으로 모델을 지정하고, library가 업데이트될 때 flaky test를 피하기 위해 절대값이 아닌 동작(label, score threshold)에 기반하여 테스트를 작성하세요.

huggingfacetransformersnlppythonmachine-learning