🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

텍스트 분류 Fine-tuning 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 2. · 12 Views

텍스트 분류 Fine-tuning 완벽 가이드

사전 학습된 언어 모델을 텍스트 분류 작업에 맞게 Fine-tuning하는 방법을 배웁니다. Hugging Face Transformers 라이브러리를 활용하여 감성 분석 모델을 직접 구현해봅니다.


목차

  1. 분류_문제_정의
  2. 데이터셋_준비
  3. Trainer_API_활용
  4. 학습_하이퍼파라미터
  5. 모델_평가와_분석
  6. 실전_감성_분석_구현

1. 분류 문제 정의

김개발 씨는 스타트업에서 고객 피드백을 분석하는 업무를 맡게 되었습니다. 하루에 수천 건씩 쏟아지는 리뷰를 일일이 읽어가며 긍정인지 부정인지 분류하는 것은 불가능에 가까웠습니다.

선배 박시니어 씨가 말했습니다. "이럴 때 텍스트 분류 모델을 학습시키면 돼요."

텍스트 분류란 주어진 텍스트가 어떤 범주에 속하는지 자동으로 판별하는 작업입니다. 마치 우체국 직원이 편지를 주소별로 분류하듯, 모델이 텍스트를 미리 정의된 카테고리로 나눕니다.

스팸 필터링, 감성 분석, 뉴스 분류 등 실무에서 가장 많이 활용되는 NLP 태스크 중 하나입니다.

다음 코드를 살펴봅시다.

from transformers import AutoModelForSequenceClassification

# 분류할 레이블 정의
label2id = {"negative": 0, "positive": 1}
id2label = {0: "negative", 1: "positive"}

# 사전학습 모델을 분류 모델로 불러오기
model = AutoModelForSequenceClassification.from_pretrained(
    "bert-base-uncased",
    num_labels=2,  # 이진 분류
    label2id=label2id,
    id2label=id2label
)

# 모델 구조 확인
print(f"분류 레이블 수: {model.config.num_labels}")

김개발 씨는 입사 6개월 차 주니어 개발자입니다. 어느 날 팀장님이 급한 업무를 할당했습니다.

"우리 앱 스토어 리뷰가 너무 많아서 일일이 확인하기 어려워요. 긍정 리뷰와 부정 리뷰를 자동으로 분류해주는 시스템을 만들어 주세요." 김개발 씨는 당황했습니다.

텍스트를 어떻게 컴퓨터가 분류한다는 걸까요? 이때 박시니어 씨가 다가와 설명을 시작했습니다.

"텍스트 분류라는 게 있어요. NLP에서 가장 기본이 되는 작업이죠." 그렇다면 텍스트 분류란 정확히 무엇일까요?

쉽게 비유하자면, 텍스트 분류는 마치 서점 직원이 새로 들어온 책을 장르별 서가에 꽂는 것과 같습니다. 소설인지 과학서인지 요리책인지 판단해서 알맞은 위치에 배치하는 것처럼, 텍스트 분류 모델도 주어진 글을 읽고 어떤 범주에 속하는지 결정합니다.

이런 분류 작업을 프로그래밍으로 해결하려면 어떻게 해야 할까요? 예전에는 키워드 기반 규칙을 직접 만들었습니다.

"좋다", "최고", "만족"이 들어가면 긍정, "싫다", "별로", "불만"이 들어가면 부정으로 처리하는 식이었습니다. 하지만 이 방식은 한계가 명확했습니다.

"좋은 줄 알았는데 별로였어요"처럼 맥락에 따라 의미가 달라지는 경우를 처리할 수 없었습니다. 규칙이 수백 개로 늘어나도 정확도는 좀처럼 오르지 않았습니다.

바로 이런 문제를 해결하기 위해 딥러닝 기반 텍스트 분류가 등장했습니다. 특히 BERT와 같은 사전학습 모델을 활용한 Fine-tuning 방식이 혁신을 가져왔습니다.

위의 코드를 살펴보겠습니다. 먼저 label2idid2label 딕셔너리를 정의합니다.

이것은 레이블 이름과 숫자를 서로 변환하는 매핑입니다. 모델은 숫자로 학습하지만, 우리는 "positive", "negative" 같은 이름으로 결과를 확인하고 싶기 때문입니다.

다음으로 AutoModelForSequenceClassification을 사용해 사전학습된 BERT 모델을 불러옵니다. 여기서 중요한 것은 num_labels 파라미터입니다.

이진 분류이므로 2를 지정합니다. 만약 뉴스 카테고리처럼 여러 개의 클래스가 있다면 해당 숫자를 넣으면 됩니다.

실제 현업에서는 어떻게 활용할까요? 이커머스 회사에서는 상품 리뷰의 감성을 분석합니다.

고객센터에서는 문의 내용을 유형별로 자동 분류합니다. 뉴스 서비스에서는 기사를 정치, 경제, 스포츠 등으로 분류합니다.

하지만 주의할 점도 있습니다. 분류 문제를 정의할 때 레이블이 명확해야 합니다.

"애매한" 카테고리가 있으면 모델도 혼란스러워합니다. 또한 레이블 간의 경계가 분명해야 학습이 잘 됩니다.

다시 김개발 씨 이야기로 돌아가 봅시다. 박시니어 씨의 설명을 들은 김개발 씨는 고개를 끄덕였습니다.

"그러니까 우선 분류할 범주를 명확하게 정의하는 게 첫 번째 단계군요!"

실전 팁

💡 - 레이블 수는 너무 적거나 많지 않게, 실제 비즈니스 요구사항에 맞춰 정의하세요

  • 레이블 간 경계가 모호하면 다중 레이블 분류나 레이블 재설계를 고려하세요

2. 데이터셋 준비

분류 문제를 정의한 김개발 씨는 이제 데이터를 준비해야 했습니다. 박시니어 씨가 말했습니다.

"모델은 데이터를 먹고 자라요. 좋은 데이터 없이는 좋은 모델도 없습니다." 그런데 수천 건의 리뷰를 어떻게 학습용 데이터로 만들어야 할까요?

데이터셋 준비는 Fine-tuning의 성패를 좌우하는 가장 중요한 단계입니다. Hugging Face의 datasets 라이브러리를 사용하면 공개 데이터셋을 쉽게 불러오거나, 직접 만든 데이터를 학습에 적합한 형태로 변환할 수 있습니다.

텍스트를 토큰화하고 레이블을 숫자로 변환하는 전처리 과정이 필수입니다.

다음 코드를 살펴봅시다.

from datasets import load_dataset
from transformers import AutoTokenizer

# IMDB 영화 리뷰 데이터셋 불러오기
dataset = load_dataset("imdb")

# 토크나이저 준비
tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")

# 토큰화 함수 정의
def tokenize_function(examples):
    return tokenizer(
        examples["text"],
        padding="max_length",
        truncation=True,
        max_length=256  # 메모리 효율을 위해 길이 제한
    )

# 전체 데이터셋에 토큰화 적용
tokenized_dataset = dataset.map(tokenize_function, batched=True)

김개발 씨는 분류 문제를 정의하고 나서 다음 단계로 넘어갔습니다. 이제 모델을 학습시킬 데이터가 필요했습니다.

마침 회사에는 6개월치 앱 리뷰 데이터가 쌓여 있었습니다. 하지만 이 데이터를 그대로 모델에 넣을 수는 없었습니다.

박시니어 씨가 설명했습니다. "컴퓨터는 글자를 그대로 이해하지 못해요.

숫자로 변환해줘야 합니다. 이 과정을 토큰화라고 해요." 그렇다면 토큰화란 무엇일까요?

쉽게 비유하자면, 토큰화는 마치 통역사가 외국어를 한국어로 번역하는 것과 같습니다. 우리가 쓴 문장을 모델이 이해할 수 있는 숫자 언어로 번역해주는 것입니다.

"이 영화 정말 재미있었어요"라는 문장은 [101, 1142, 7298, 2087, ...]과 같은 숫자 시퀀스로 변환됩니다. 위의 코드에서 먼저 load_dataset("imdb")를 호출합니다.

이것은 Hugging Face Hub에서 IMDB 영화 리뷰 데이터셋을 자동으로 다운로드합니다. 이 데이터셋에는 5만 개의 영화 리뷰가 긍정과 부정으로 레이블링되어 있습니다.

다음으로 AutoTokenizer를 불러옵니다. 여기서 중요한 것은 모델과 같은 토크나이저를 사용해야 한다는 점입니다.

BERT 모델을 Fine-tuning한다면 BERT 토크나이저를 써야 합니다. 서로 다른 토크나이저를 사용하면 모델이 전혀 이해하지 못하는 입력을 받게 됩니다.

tokenize_function에서는 세 가지 중요한 옵션을 설정합니다. padding="max_length"는 모든 문장을 같은 길이로 맞춥니다.

배치 학습을 위해서는 입력 길이가 동일해야 하기 때문입니다. truncation=True는 너무 긴 문장을 자릅니다.

max_length=256은 최대 토큰 수를 제한합니다. 왜 길이를 제한할까요?

BERT의 기본 최대 길이는 512 토큰입니다. 하지만 긴 시퀀스는 메모리를 많이 사용합니다.

리뷰 텍스트의 핵심 내용은 대부분 앞부분에 있으므로, 256 토큰이면 충분한 경우가 많습니다. 마지막으로 dataset.map() 함수를 사용해 전체 데이터셋에 토큰화를 적용합니다.

batched=True 옵션은 한 번에 여러 샘플을 처리해서 속도를 높입니다. 수만 건의 데이터를 처리할 때 이 옵션 하나로 처리 시간이 크게 단축됩니다.

실무에서는 자체 데이터를 사용하는 경우가 많습니다. 이때는 CSV나 JSON 파일을 load_dataset("csv", data_files="data.csv") 형태로 불러올 수 있습니다.

중요한 것은 텍스트 컬럼과 레이블 컬럼이 명확하게 구분되어 있어야 한다는 점입니다. 주의할 점이 있습니다.

데이터 품질이 모델 성능을 결정합니다. 잘못 레이블링된 데이터가 많으면 모델도 혼란스러워합니다.

학습 전에 데이터를 샘플링해서 레이블이 정확한지 검토하는 과정이 필요합니다. 김개발 씨는 코드를 실행하며 물었습니다.

"그런데 학습 데이터와 테스트 데이터는 어떻게 나누나요?" 박시니어 씨가 답했습니다. "IMDB 데이터셋은 이미 train과 test로 나뉘어 있어요.

직접 만든 데이터라면 8:2 정도로 분할하면 됩니다."

실전 팁

💡 - 토크나이저는 반드시 모델과 동일한 것을 사용하세요

  • max_length는 데이터 특성과 GPU 메모리를 고려해 설정하세요
  • 자체 데이터 사용 시 레이블 분포가 균형 잡혀 있는지 확인하세요

3. Trainer API 활용

데이터 준비를 마친 김개발 씨는 이제 본격적으로 모델을 학습시킬 차례입니다. 하지만 학습 루프를 직접 작성하려니 막막했습니다.

옵티마이저 설정, 그래디언트 계산, 체크포인트 저장... 박시니어 씨가 웃으며 말했습니다.

"Trainer API가 다 해줍니다."

Trainer API는 Hugging Face가 제공하는 고수준 학습 인터페이스입니다. 복잡한 학습 루프를 직접 작성할 필요 없이, 모델과 데이터셋만 넘겨주면 학습, 평가, 저장까지 모두 처리합니다.

분산 학습, 혼합 정밀도 학습 같은 고급 기능도 설정 하나로 활성화할 수 있습니다.

다음 코드를 살펴봅시다.

from transformers import Trainer, TrainingArguments

# 학습 설정 정의
training_args = TrainingArguments(
    output_dir="./results",          # 체크포인트 저장 경로
    eval_strategy="epoch",           # 에폭마다 평가
    learning_rate=2e-5,              # Fine-tuning에 적합한 학습률
    per_device_train_batch_size=16,  # GPU당 배치 크기
    num_train_epochs=3,              # 학습 에폭 수
    weight_decay=0.01,               # 과적합 방지
    save_strategy="epoch",           # 에폭마다 저장
    load_best_model_at_end=True,     # 최고 성능 모델 로드
)

# Trainer 초기화
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset["train"],
    eval_dataset=tokenized_dataset["test"],
)

김개발 씨는 데이터를 준비하고 나서 학습 코드를 작성하려 했습니다. 인터넷에서 PyTorch 학습 코드를 찾아보니 수십 줄의 반복문과 복잡한 설정이 눈앞에 펼쳐졌습니다.

옵티마이저를 설정하고, 그래디언트를 계산하고, 손실을 역전파하고... 막막한 기분이 들었습니다.

박시니어 씨가 다가와 화면을 보더니 말했습니다. "직접 학습 루프를 짤 필요 없어요.

Trainer API를 쓰면 됩니다. Hugging Face에서 제공하는 학습 자동화 도구예요." Trainer API가 무엇인지 비유로 설명해 보겠습니다.

마치 자동차의 자동 변속기와 같습니다. 수동 변속기로 운전하려면 클러치를 밟고, 기어를 바꾸고, 타이밍을 맞춰야 합니다.

하지만 자동 변속기는 가속 페달만 밟으면 알아서 기어를 변경합니다. Trainer API도 마찬가지입니다.

복잡한 학습 과정을 알아서 처리해줍니다. 위의 코드에서 먼저 TrainingArguments를 정의합니다.

이것은 학습에 필요한 모든 설정을 담는 객체입니다. output_dir은 학습 중간 결과와 체크포인트가 저장될 경로입니다.

학습이 중단되어도 여기서 이어서 시작할 수 있습니다. eval_strategy="epoch"는 매 에폭이 끝날 때마다 검증 데이터로 모델을 평가하라는 의미입니다.

학습이 잘 되고 있는지 실시간으로 확인할 수 있습니다. learning_rate=2e-5는 Fine-tuning에서 흔히 사용하는 학습률입니다.

사전학습된 가중치를 너무 크게 바꾸지 않으면서 새로운 태스크에 적응하게 합니다. per_device_train_batch_size는 한 번에 처리할 샘플 수입니다.

16은 일반적인 GPU에서 무난하게 동작하는 값입니다. 메모리가 부족하면 8로 줄이고, 여유가 있으면 32로 늘릴 수 있습니다.

num_train_epochs=3은 전체 데이터를 3번 반복 학습한다는 의미입니다. weight_decay=0.01정규화 기법 중 하나입니다.

모델이 학습 데이터에만 과도하게 적응하는 과적합을 방지합니다. load_best_model_at_end=True는 학습이 끝나면 검증 성능이 가장 좋았던 체크포인트를 자동으로 불러옵니다.

Trainer 클래스를 초기화할 때는 모델, 학습 설정, 학습 데이터셋, 검증 데이터셋을 전달합니다. 이것이 전부입니다.

이제 trainer.train()을 호출하면 학습이 시작됩니다. 진행 상황이 실시간으로 출력되고, 에폭마다 검증 결과가 표시됩니다.

Trainer API의 장점은 확장성입니다. 다중 GPU 학습을 하고 싶다면 코드 변경 없이 실행 명령만 바꾸면 됩니다.

혼합 정밀도 학습으로 속도를 높이고 싶다면 fp16=True 옵션 하나만 추가하면 됩니다. 복잡한 기능이 설정 한 줄로 활성화됩니다.

주의할 점도 있습니다. Trainer API가 편리하긴 하지만, 내부 동작을 이해하지 못하면 문제가 생겼을 때 디버깅이 어렵습니다.

처음에는 Trainer를 사용하되, 시간이 되면 PyTorch 학습 루프도 직접 작성해보는 것을 권장합니다. 김개발 씨는 코드를 실행했습니다.

터미널에 학습 진행률이 표시되기 시작했습니다. "이렇게 간단하다니...

직접 짜려고 했으면 한참 걸렸겠네요." 박시니어 씨가 답했습니다. "그게 프레임워크의 힘이에요."

실전 팁

💡 - 학습률 2e-5는 Fine-tuning의 황금률입니다. 먼저 이 값으로 시작하세요

  • GPU 메모리 오류가 나면 배치 크기를 줄이거나 gradient_accumulation_steps를 늘리세요

4. 학습 하이퍼파라미터

학습을 시작한 김개발 씨는 결과가 기대에 못 미쳤습니다. 정확도가 80%를 넘지 못하는 것입니다.

박시니어 씨가 화면을 보더니 말했습니다. "하이퍼파라미터 튜닝을 해봐야겠네요.

요리할 때 불 세기를 조절하는 것처럼요."

하이퍼파라미터는 모델이 학습하는 방식을 제어하는 설정값입니다. 학습률, 배치 크기, 에폭 수, 웜업 스텝 등이 여기에 해당합니다.

같은 데이터와 모델이라도 하이퍼파라미터에 따라 성능이 크게 달라집니다. Fine-tuning에서는 특히 학습률 스케줄링이 중요합니다.

다음 코드를 살펴봅시다.

from transformers import TrainingArguments, get_scheduler

# 세밀한 하이퍼파라미터 설정
training_args = TrainingArguments(
    output_dir="./results",
    learning_rate=3e-5,              # 학습률 조정
    per_device_train_batch_size=32,  # 배치 크기 증가
    num_train_epochs=5,              # 에폭 수 증가
    warmup_ratio=0.1,                # 전체의 10%를 웜업에 사용
    lr_scheduler_type="cosine",      # 코사인 스케줄러
    logging_steps=100,               # 100스텝마다 로깅
    metric_for_best_model="accuracy",# 최적 모델 기준
    greater_is_better=True,          # 높을수록 좋음
)

# 학습률 스케줄 시각화 (개념 이해용)
# 웜업: 0 → 3e-5 (점진적 증가)
# 코사인: 3e-5 → 0 (부드럽게 감소)

김개발 씨는 첫 번째 학습 결과를 확인했습니다. 정확도 78%.

나쁘지 않지만, 실서비스에 적용하기에는 부족했습니다. 박시니어 씨가 말했습니다.

"모델 구조나 데이터는 그대로 두고, 하이퍼파라미터만 조절해도 성능이 크게 오를 수 있어요." 하이퍼파라미터란 무엇일까요? 비유하자면, 하이퍼파라미터는 요리의 불 세기와 조리 시간 같은 것입니다.

같은 재료로 같은 요리를 해도, 불이 너무 세면 타버리고, 너무 약하면 익지 않습니다. 적절한 온도에서 적절한 시간 동안 조리해야 맛있는 요리가 완성됩니다.

딥러닝도 마찬가지입니다. 가장 중요한 하이퍼파라미터는 **학습률(learning rate)**입니다.

학습률이 너무 높으면 모델이 최적점을 지나쳐버립니다. 마치 산 정상을 찾아 걸어가는데 보폭이 너무 커서 정상을 밟지 못하고 넘어가버리는 것과 같습니다.

반대로 학습률이 너무 낮으면 학습이 너무 느려집니다. Fine-tuning에서는 2e-5에서 5e-5 사이의 값이 일반적으로 잘 작동합니다.

**웜업(warmup)**은 학습 초반에 학습률을 점진적으로 높이는 기법입니다. 처음부터 높은 학습률로 시작하면 사전학습된 가중치가 크게 흔들릴 수 있습니다.

웜업은 이를 방지합니다. warmup_ratio=0.1은 전체 학습 스텝의 10%를 웜업에 사용한다는 의미입니다.

학습률 스케줄러는 학습이 진행됨에 따라 학습률을 조절하는 방법입니다. cosine 스케줄러는 코사인 함수 곡선처럼 학습률을 부드럽게 감소시킵니다.

학습 초반에는 빠르게 학습하고, 후반에는 세밀하게 조정하는 효과가 있습니다. 배치 크기도 중요합니다.

배치 크기가 크면 학습이 안정적이고 빠르지만, GPU 메모리를 많이 사용합니다. 배치 크기가 작으면 메모리는 절약되지만, 학습이 불안정해질 수 있습니다.

메모리가 허락하는 한 큰 배치 크기를 사용하는 것이 일반적입니다. logging_steps=100은 100스텝마다 손실값과 학습률을 출력합니다.

학습이 잘 되고 있는지 모니터링하기 위해 필요합니다. 손실값이 계속 감소하면 좋은 신호이고, 갑자기 튀면 문제가 있다는 신호입니다.

metric_for_best_modelgreater_is_better는 어떤 기준으로 최적 모델을 선택할지 결정합니다. 정확도를 기준으로 하고, 높을수록 좋다고 설정했습니다.

학습 중 가장 높은 검증 정확도를 기록한 체크포인트가 최종 모델이 됩니다. 하이퍼파라미터 튜닝에는 정해진 정답이 없습니다.

데이터와 태스크에 따라 최적값이 다릅니다. 일반적인 접근법은 먼저 권장값으로 시작하고, 결과를 보면서 조금씩 조절하는 것입니다.

학습률은 로그 스케일로 탐색하고, 배치 크기는 2배씩 늘려가며 실험합니다. 김개발 씨는 학습률을 3e-5로 올리고, 웜업과 코사인 스케줄러를 추가해서 다시 학습을 시작했습니다.

이번에는 정확도가 88%까지 올랐습니다. "같은 데이터인데 이렇게 차이가 나는군요!" 박시니어 씨가 답했습니다.

"하이퍼파라미터 튜닝은 모델 학습의 필수 과정이에요."

실전 팁

💡 - 학습률은 2e-5 ~ 5e-5 범위에서 시작하고, 로그 스케일로 탐색하세요

  • 웜업 비율 0.06 ~ 0.1은 대부분의 경우에 잘 작동합니다
  • Weights & Biases 같은 실험 추적 도구로 하이퍼파라미터 실험을 관리하세요

5. 모델 평가와 분석

학습을 마친 김개발 씨는 모델이 얼마나 잘 작동하는지 확인해야 했습니다. 단순히 정확도만 보면 될까요?

박시니어 씨가 말했습니다. "정확도는 빙산의 일각이에요.

진짜 중요한 건 모델이 어디서 실수하는지 파악하는 거예요."

모델 평가는 학습된 모델의 성능을 객관적으로 측정하는 과정입니다. 정확도 외에도 정밀도, 재현율, F1 스코어 등 다양한 지표를 확인해야 합니다.

특히 클래스 불균형이 있는 경우 정확도만으로는 실제 성능을 판단하기 어렵습니다. 오분류 분석을 통해 모델의 약점을 파악하고 개선 방향을 찾습니다.

다음 코드를 살펴봅시다.

import numpy as np
from sklearn.metrics import classification_report, confusion_matrix
import evaluate

# 평가 지표 로드
accuracy = evaluate.load("accuracy")
f1 = evaluate.load("f1")

# 평가 함수 정의
def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)

    # 여러 지표 계산
    acc = accuracy.compute(predictions=predictions, references=labels)
    f1_score = f1.compute(predictions=predictions, references=labels)

    return {"accuracy": acc["accuracy"], "f1": f1_score["f1"]}

# 학습 후 평가 실행
results = trainer.evaluate()
print(f"정확도: {results['eval_accuracy']:.4f}")
print(f"F1 Score: {results['eval_f1']:.4f}")

김개발 씨는 학습을 마치고 터미널에 출력된 숫자를 확인했습니다. "정확도 88%면 꽤 괜찮은 것 같은데요?" 하지만 박시니어 씨는 고개를 저었습니다.

"정확도만 보면 안 돼요. 더 깊이 들여다봐야 합니다." 모델 평가가 왜 중요한지 비유로 설명해 보겠습니다.

의사가 환자를 진단할 때 체온만 재지 않습니다. 혈압, 맥박, 혈액 검사 등 여러 지표를 종합적으로 살펴봅니다.

모델 평가도 마찬가지입니다. 정확도 하나만으로는 모델의 건강 상태를 완전히 파악할 수 없습니다.

특히 클래스 불균형이 있을 때 정확도는 함정이 됩니다. 예를 들어 스팸 메일이 전체의 5%라면, 아무것도 하지 않고 모든 메일을 "정상"으로 분류해도 정확도가 95%입니다.

하지만 이 모델은 쓸모가 없습니다. 스팸을 하나도 잡지 못하니까요.

이런 문제를 해결하기 위해 **정밀도(Precision)**와 **재현율(Recall)**을 함께 봅니다. 정밀도는 "긍정으로 예측한 것 중 실제로 긍정인 비율"입니다.

재현율은 "실제 긍정 중에서 긍정으로 예측한 비율"입니다. F1 스코어는 이 둘의 조화 평균으로, 불균형한 데이터에서도 모델 성능을 잘 반영합니다.

위의 코드에서 evaluate 라이브러리를 사용해 평가 지표를 계산합니다. compute_metrics 함수는 Trainer가 평가할 때 자동으로 호출됩니다.

모델의 예측값(predictions)과 실제 레이블(labels)을 받아서 지표를 계산합니다. np.argmax(predictions, axis=1)은 모델 출력을 클래스 레이블로 변환합니다.

모델은 각 클래스에 대한 점수(로짓)를 출력하는데, 가장 높은 점수의 인덱스가 예측 클래스입니다. **혼동 행렬(Confusion Matrix)**도 중요합니다.

실제 긍정을 긍정으로 맞춘 경우(True Positive), 부정을 부정으로 맞춘 경우(True Negative), 그리고 틀린 경우들(False Positive, False Negative)을 한눈에 볼 수 있습니다. 모델이 어떤 유형의 실수를 많이 하는지 파악할 수 있습니다.

오분류 분석은 한 단계 더 나아갑니다. 모델이 틀린 샘플들을 직접 확인해보는 것입니다.

"왜 이 리뷰를 부정으로 분류했을까?" 살펴보면 패턴이 보입니다. 반어법을 이해 못 하거나, 특정 도메인 용어에 약하거나 하는 식입니다.

이런 분석을 통해 데이터를 보강하거나 모델을 개선할 방향을 찾습니다. 실무에서는 A/B 테스트도 중요합니다.

오프라인 지표가 좋아도 실제 서비스에서 기대만큼 성능이 나오지 않을 수 있습니다. 일부 트래픽에 새 모델을 적용해보고, 실제 사용자 반응을 측정해야 합니다.

김개발 씨는 혼동 행렬을 출력해봤습니다. 부정 리뷰를 긍정으로 잘못 분류한 경우가 많았습니다.

해당 샘플들을 확인해보니, "기대했는데 별로였어요" 같은 반어적 표현이 많았습니다. "아, 이런 패턴의 데이터를 더 수집해야겠군요." 박시니어 씨가 끄덕였습니다.

"바로 그거예요. 평가는 끝이 아니라 개선의 시작입니다."

실전 팁

💡 - 클래스 불균형이 있으면 F1 스코어나 AUC-ROC를 주요 지표로 사용하세요

  • 오분류 샘플을 100개 정도 직접 확인해보면 개선 방향이 보입니다
  • 도메인 전문가의 피드백을 받으면 놓친 패턴을 발견할 수 있습니다

6. 실전 감성 분석 구현

이론과 실습을 마친 김개발 씨는 드디어 실제 서비스에 적용할 모델을 만들 차례입니다. 지금까지 배운 모든 것을 종합해서, 앱 스토어 리뷰를 분석하는 감성 분석 시스템을 완성합니다.

박시니어 씨가 말했습니다. "이제 진짜 시작이에요."

실전 구현에서는 지금까지 배운 내용을 하나의 완성된 파이프라인으로 통합합니다. 데이터 로딩부터 전처리, 학습, 평가, 그리고 새로운 텍스트에 대한 추론까지 전 과정을 다룹니다.

학습된 모델을 저장하고 불러와서 실제 서비스에서 사용하는 방법까지 익힙니다.

다음 코드를 살펴봅시다.

from transformers import pipeline

# 학습된 모델 저장
trainer.save_model("./sentiment-model")
tokenizer.save_pretrained("./sentiment-model")

# 추론 파이프라인 생성
classifier = pipeline(
    "text-classification",
    model="./sentiment-model",
    tokenizer="./sentiment-model"
)

# 새로운 리뷰 분석
reviews = [
    "이 앱 정말 최고예요! 매일 사용하고 있습니다.",
    "버그가 너무 많아서 짜증나요. 환불 원해요.",
    "그냥 그래요. 나쁘지도 좋지도 않아요."
]

for review in reviews:
    result = classifier(review)[0]
    print(f"리뷰: {review}")
    print(f"감성: {result['label']}, 확신도: {result['score']:.2f}\n")

김개발 씨의 모델 개발 여정이 마무리 단계에 접어들었습니다. 이제 지금까지 배운 모든 것을 하나로 통합해야 합니다.

박시니어 씨가 말했습니다. "개발자에게 중요한 건 이론이 아니라 실제로 돌아가는 코드예요.

처음부터 끝까지 완성해봅시다." 실전 구현의 첫 단계는 모델 저장입니다. 학습이 끝난 모델을 디스크에 저장해두면, 나중에 다시 불러와서 사용할 수 있습니다.

trainer.save_model()은 모델 가중치를 저장하고, tokenizer.save_pretrained()는 토크나이저 설정을 저장합니다. 둘 다 같은 경로에 저장하면 관리가 편합니다.

저장된 모델을 사용하는 가장 쉬운 방법은 pipeline입니다. pipeline은 Hugging Face가 제공하는 고수준 추론 인터페이스입니다.

토큰화, 모델 추론, 후처리를 한 번에 처리해줍니다. 텍스트를 넣으면 바로 결과가 나옵니다.

위의 코드에서 pipeline("text-classification", ...)을 호출하면 텍스트 분류용 파이프라인이 생성됩니다. modeltokenizer 인자에 저장한 경로를 지정합니다.

이제 classifier(text)를 호출하기만 하면 됩니다. 출력 결과에는 labelscore가 포함됩니다.

label은 예측된 클래스("positive" 또는 "negative")이고, score는 모델의 확신도입니다. 확신도가 높을수록 모델이 자신의 예측을 믿는다는 의미입니다.

하지만 주의할 점이 있습니다. 확신도가 높다고 해서 항상 정답은 아닙니다.

실서비스에 적용할 때는 몇 가지 고려사항이 있습니다. 먼저 배치 처리입니다.

리뷰를 하나씩 처리하면 느립니다. 여러 개를 한 번에 처리하면 훨씬 효율적입니다.

pipeline에 리스트를 넣으면 자동으로 배치 처리됩니다. 다음은 임계값 설정입니다.

확신도가 낮은 예측은 "판단 불가"로 처리하고 사람이 검토하게 할 수 있습니다. 예를 들어 확신도가 0.7 미만이면 자동 분류하지 않는 식입니다.

이렇게 하면 명확한 케이스만 자동화하고, 애매한 케이스는 품질을 유지할 수 있습니다. 모니터링도 중요합니다.

모델을 배포한 후에도 성능을 지속적으로 추적해야 합니다. 시간이 지나면 사용자의 언어 패턴이 변하거나 새로운 유형의 리뷰가 등장할 수 있습니다.

이런 데이터 드리프트를 감지하고 모델을 재학습해야 합니다. 에러 처리도 잊지 마세요.

빈 문자열이나 너무 긴 텍스트가 입력될 수 있습니다. 특수문자만 있는 입력도 있을 수 있습니다.

이런 예외 상황을 처리하는 코드가 필요합니다. 프로덕션 환경에서는 입력 검증이 필수입니다.

마지막으로 성능 최적화가 있습니다. GPU가 있으면 device=0으로 GPU를 사용합니다.

ONNX로 변환하면 추론 속도가 더 빨라집니다. 양자화를 적용하면 모델 크기가 줄어들고 속도도 빨라집니다.

서비스 요구사항에 맞게 최적화 방법을 선택합니다. 김개발 씨는 완성된 코드를 실행했습니다.

첫 번째 리뷰 "이 앱 정말 최고예요!"는 positive, 확신도 0.98로 분류되었습니다. 두 번째 리뷰는 negative, 0.95.

세 번째 "그냥 그래요"는 positive, 0.62로 확신도가 낮았습니다. 박시니어 씨가 말했습니다.

"세 번째처럼 확신도가 낮은 경우는 사람이 검토하게 하면 되겠죠?" 김개발 씨가 끄덕였습니다. "네, 임계값을 0.7로 설정하고, 그 미만은 따로 모아두겠습니다." 6개월 후, 김개발 씨의 감성 분석 시스템은 하루 수천 건의 리뷰를 자동으로 처리하고 있었습니다.

부정 리뷰가 급증하면 알림을 보내고, 주간 리포트도 자동으로 생성됩니다. 처음 막막했던 과제가 회사의 핵심 시스템이 된 것입니다.

실전 팁

💡 - 확신도 임계값을 설정해서 애매한 케이스는 사람이 검토하게 하세요

  • 배포 후에도 성능을 모니터링하고, 필요하면 재학습하세요
  • GPU가 없는 환경이라면 ONNX 변환이나 양자화로 속도를 개선하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#Python#Transformers#Fine-tuning#TextClassification#HuggingFace#Text Classification,Fine-tuning

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.