파이프라인(Pipeline)

킹용맨 ㅣ 2026. 2. 5. 10:00

코딩 한 줄의 비밀: 허깅페이스 파이프라인 완벽 해부

허깅페이스(Hugging Face)를 다루는 개발자라면 pipeline() 함수가 얼마나 강력한지 잘 알고 계실 겁니다. 단 한 줄의 코드로 감성 분석, 번역, 요약 등 복잡한 인공지능 작업을 뚝딱 처리해 주기 때문입니다. 하지만 이 마법 같은 함수가 내부적으로 어떻게 작동하는지 정확히 이해하는 것은, 나중에 모델을 커스터마이징하거나 문제를 해결할 때 필수적입니다. 오늘은 파이프라인이라는 블랙박스를 열어, 그 안에서 벌어지는 '전처리 → 모델 추론 → 후처리'의 3단계 과정을 실제 파이썬 코드 예시와 함께 낱낱이 파헤쳐 보겠습니다.




전체 흐름 미리보기: 원재료가 요리가 되기까지

파이프라인의 작동 원리는 요리 과정과 매우 비슷합니다. 우리가 pipeline(text)를 실행하면 내부적으로는 다음과 같은 세 가지 핵심 단계가 순차적으로 일어납니다.

단계 명칭 역할 데이터의 변화 (예시)
1단계 전처리 (Preprocessing) 재료 손질 "I love AI" (텍스트) → [101, 1045, 2293...] (숫자)
2단계 모델 추론 (Model Inference) 조리 (연산) [101, 1045...] (숫자) → [-1.5, 4.2] (로짓/점수)
3단계 후처리 (Post-processing) 플레이팅 (해석) [-1.5, 4.2] (점수) → "긍정 (99%)" (최종 결과)

이제 각 단계를 코드로 직접 구현해 보며 자세히 알아보겠습니다.




1단계: 전처리 (Preprocessing) - 컴퓨터가 이해하는 숫자로 변환

컴퓨터와 AI 모델은 우리가 사용하는 영어나 한국어를 직접 이해하지 못합니다. 그들은 오직 '숫자'만 처리할 수 있습니다. 그래서 첫 번째 단계에서는 토크나이저(Tokenizer)가 등장하여 문장을 모델이 알아들을 수 있는 포맷으로 변환합니다.

  • 문장을 작은 단위(토큰)로 쪼갭니다.
  • 각 토큰을 사전(Vocabulary)에 정의된 고유한 숫자(Input IDs)로 바꿉니다.
  • 연산을 위해 텐서(Tensor) 형태로 변환합니다.
from transformers import AutoTokenizer

# 모델 체크포인트 지정
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

# 분석할 문장
raw_inputs = ["I love using Hugging Face transformers!"]

# 전처리 실행 (텍스트 -> 텐서)
inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")

print(inputs)
# 결과: {'input_ids': tensor([[101, 1045, 2293, ...]]), 'attention_mask': ...}



2단계: 모델 추론 (Model Inference) - 고차원 연산과 로짓(Logits)

숫자로 변환된 데이터(input_ids)는 이제 모델의 본체로 전달됩니다. 여기서 트랜스포머(Transformer) 신경망을 통과하며 복잡한 연산이 수행됩니다. 이 과정을 추론이라고 합니다.

중요한 점은 모델이 내놓는 결과값이 최종 정답(확률)이 아니라는 것입니다. 모델은 '로짓(Logits)'이라고 불리는 날것의 점수(Raw Score)를 출력합니다. 이 점수는 정규화되지 않아 -1.54.2 같은 실수 형태로 나옵니다.

from transformers import AutoModelForSequenceClassification

# 모델 로드
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)

# 모델에 데이터 입력 (추론)
outputs = model(**inputs)

# 모델이 뱉어낸 날것의 점수 (Logits) 확인
print(outputs.logits)
# 결과: tensor([[-1.5607,  1.6123]], grad_fn=<AddmmBackward0>)

위 결과를 보면 두 개의 숫자가 나왔습니다. 이는 모델이 학습한 클래스(부정, 긍정) 각각에 대한 점수입니다.




3단계: 후처리 (Post-processing) - 확률 변환 및 결과 출력

마지막으로, 모델이 뱉어낸 알쏭달쏭한 로짓 값을 사람이 이해하기 쉬운 확률(Probability)라벨(Label)로 바꿔주는 과정이 필요합니다. 이때 소프트맥스(Softmax) 함수가 사용됩니다.

  • 소프트맥스: 모든 로짓 값을 0과 1 사이의 확률값으로 변환하고, 그 합이 1이 되도록 만듭니다.
  • 라벨링: 가장 높은 확률을 가진 인덱스를 찾아 실제 이름(예: POSITIVE)으로 매핑합니다.
import torch

# 로짓을 확률로 변환 (Softmax 함수 사용)
predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)

print(predictions)
# 결과: tensor([[0.0402, 0.9598]]) -> 부정 4%, 긍정 96%

# 최종 라벨 확인
print(model.config.id2label)
# 결과: {0: 'NEGATIVE', 1: 'POSITIVE'}

결국 파이프라인은 [-1.56, 1.61]이라는 난해한 숫자를 POSITIVE (96%)라는 명확한 결과로 통역해 주는 역할을 합니다.




요약: 파이프라인의 진정한 가치

우리가 pipeline() 함수를 호출할 때, 내부에서는 위에서 본 토크나이징(번역) → 모델 연산(계산) → 후처리(통역)의 과정이 눈 깜짝할 사이에 자동으로 일어납니다.

이 내부 구조를 이해하고 있다면, 단순히 라이브러리를 사용하는 사용자를 넘어 상황에 맞게 입출력을 조절하고 모델을 미세 조정(Fine-tuning) 할 수 있는 개발자로 한 단계 성장할 수 있습니다. 이제 파이프라인이 마냥 신기한 마법 상자가 아니라, 정교하게 설계된 공학의 산물이라는 것이 느껴지시나요?