🤖

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

⚠️

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

이미지 로딩 중...

실전 NLP 프로젝트 완벽 가이드 - 슬라이드 1/7
A

AI Generated

2025. 12. 2. · 11 Views

실전 NLP 프로젝트 완벽 가이드

자연어 처리 프로젝트를 처음부터 끝까지 진행하는 방법을 다룹니다. 주제 선정부터 데이터 수집, 모델 Fine-tuning, RAG 시스템 구축, 그리고 Streamlit 데모 배포까지 실무에서 바로 적용할 수 있는 내용을 담았습니다.


목차

  1. 프로젝트_주제_선정
  2. 데이터_수집_및_정제
  3. 모델_선택_및_Fine-tuning
  4. RAG_시스템_구축
  5. Streamlit_데모_개발
  6. 성능_평가_및_개선

1. 프로젝트 주제 선정

신입 개발자 김개발 씨가 팀장님에게 불려갔습니다. "이번에 NLP 프로젝트 하나 맡아볼래요?

주제 선정부터 배포까지 전부요." 김개발 씨는 설레면서도 막막했습니다. 도대체 어디서부터 시작해야 할까요?

NLP 프로젝트의 주제 선정은 마치 여행 목적지를 정하는 것과 같습니다. 목적지가 명확해야 어떤 교통수단을 이용하고, 무엇을 준비할지 결정할 수 있습니다.

좋은 주제는 비즈니스 가치, 기술적 실현 가능성, 데이터 확보 가능성 세 가지를 모두 만족해야 합니다.

다음 코드를 살펴봅시다.

# NLP 프로젝트 주제 평가 체크리스트
project_evaluation = {
    "project_name": "고객 리뷰 감성 분석 시스템",
    "business_value": {
        "problem": "수작업 리뷰 분석에 월 40시간 소요",
        "expected_roi": "분석 시간 90% 단축",
        "stakeholders": ["마케팅팀", "CS팀", "상품기획팀"]
    },
    "technical_feasibility": {
        "model_type": "BERT 기반 분류 모델",
        "complexity": "중간",
        "team_expertise": True
    },
    "data_availability": {
        "data_source": "자사 쇼핑몰 리뷰 데이터",
        "volume": "월 5만 건",
        "labeling_cost": "내부 라벨링 가능"
    }
}

김개발 씨는 팀장님의 말씀을 듣고 자리로 돌아왔습니다. NLP 프로젝트라니, 요즘 핫하다는 그 자연어 처리 분야를 직접 해볼 수 있다니 가슴이 두근거렸습니다.

하지만 막상 노트북을 펴고 나니 막막했습니다. 챗봇을 만들까, 번역기를 만들까, 아니면 요약 시스템을 만들까?

선배 개발자 박시니어 씨가 커피를 건네며 다가왔습니다. "뭘 그렇게 고민해요?" 김개발 씨가 상황을 설명하자, 박시니어 씨가 웃으며 말했습니다.

"주제 선정이 프로젝트의 절반이에요. 잘 선택하면 순항이고, 잘못 선택하면 난파선이 되는 거죠." 그렇다면 좋은 NLP 프로젝트 주제란 무엇일까요?

박시니어 씨는 세 가지 기준을 꼽았습니다. 첫 번째는 비즈니스 가치입니다.

아무리 기술적으로 훌륭해도 회사에 도움이 되지 않으면 의미가 없습니다. "이 프로젝트가 완성되면 누가 기뻐할까?"라는 질문에 명확한 답이 있어야 합니다.

고객 리뷰 분석 시스템이라면 마케팅팀이 고객 반응을 빠르게 파악할 수 있고, CS팀은 불만 사항을 선제적으로 대응할 수 있습니다. 두 번째는 기술적 실현 가능성입니다.

마치 등산을 계획할 때 자신의 체력을 고려하는 것과 같습니다. 에베레스트 등반은 멋지지만, 등산 초보자가 도전하기엔 무리가 있습니다.

팀의 역량과 주어진 시간을 냉정하게 평가해야 합니다. 세 번째는 데이터 확보 가능성입니다.

데이터는 NLP 프로젝트의 연료입니다. 아무리 좋은 엔진을 가진 자동차도 연료가 없으면 움직일 수 없습니다.

필요한 데이터를 어디서, 얼마나, 어떤 비용으로 확보할 수 있는지 미리 파악해야 합니다. 김개발 씨는 회사의 상황을 떠올렸습니다.

마침 쇼핑몰을 운영하고 있었고, 매달 5만 건의 고객 리뷰가 쌓이고 있었습니다. 현재 이 리뷰들은 담당자가 수작업으로 읽고 분류하고 있었는데, 이 작업에만 월 40시간이 소요되고 있었습니다.

박시니어 씨가 고개를 끄덕였습니다. "좋은 주제네요.

데이터도 있고, 해결할 문제도 명확하고, 수혜자도 분명해요." 김개발 씨는 고객 리뷰 감성 분석 시스템을 첫 NLP 프로젝트 주제로 확정했습니다. 주의할 점도 있습니다.

초보자들이 흔히 하는 실수는 너무 거창한 주제를 잡는 것입니다. "모든 언어를 완벽하게 번역하는 시스템"보다는 "영한 기술 문서 번역 보조 도구"처럼 범위를 좁히는 것이 현명합니다.

김개발 씨는 주제가 정해지자 마음이 한결 가벼워졌습니다. 이제 다음 단계로 넘어갈 준비가 되었습니다.

실전 팁

💡 - 처음에는 범위를 최대한 좁게 잡고 시작하세요

  • 사내 이해관계자와 미리 인터뷰하여 실제 니즈를 파악하세요
  • MVP(최소 기능 제품) 관점에서 핵심 기능만 먼저 정의하세요

2. 데이터 수집 및 정제

주제가 정해진 김개발 씨는 본격적으로 데이터를 모으기 시작했습니다. 그런데 막상 데이터베이스에서 리뷰를 추출하고 보니 예상과 달랐습니다.

빈 값도 있고, 이상한 특수문자도 섞여 있었습니다. "이걸 그대로 모델에 넣어도 되나요?"

데이터 정제는 마치 요리 전 재료 손질과 같습니다. 아무리 좋은 재료도 흙이 묻어 있거나 상한 부분이 있으면 맛있는 요리가 될 수 없습니다.

텍스트 정규화, 중복 제거, 라벨링은 NLP 프로젝트에서 가장 중요한 전처리 과정입니다.

다음 코드를 살펴봅시다.

import pandas as pd
import re
from sklearn.model_selection import train_test_split

# 원본 데이터 로드
df = pd.read_csv("reviews.csv")

# 텍스트 정제 함수
def clean_text(text):
    if pd.isna(text):
        return ""
    text = re.sub(r'[^\w\s가-힣]', '', text)  # 특수문자 제거
    text = re.sub(r'\s+', ' ', text)  # 연속 공백 제거
    return text.strip()

# 데이터 정제 적용
df['cleaned_review'] = df['review'].apply(clean_text)
df = df[df['cleaned_review'].str.len() > 10]  # 너무 짧은 리뷰 제거
df = df.drop_duplicates(subset=['cleaned_review'])  # 중복 제거

# 학습/검증/테스트 분리 (8:1:1)
train, temp = train_test_split(df, test_size=0.2, random_state=42)
valid, test = train_test_split(temp, test_size=0.5, random_state=42)

김개발 씨는 데이터베이스 담당자에게 연락하여 지난 1년간의 고객 리뷰 데이터를 요청했습니다. CSV 파일로 받은 데이터를 열어본 순간, 김개발 씨는 당황했습니다.

데이터가 생각보다 지저분했기 때문입니다. "ㅋㅋㅋ 최고!!!" 같은 리뷰가 있는가 하면, 특수문자로만 이루어진 리뷰도 있었습니다.

심지어 같은 내용이 여러 번 중복된 경우도 많았습니다. 박시니어 씨가 김개발 씨의 화면을 보며 말했습니다.

"실제 데이터는 항상 이래요. 교과서에 나오는 깔끔한 데이터는 현실에 없어요." 데이터 정제가 왜 중요할까요?

쉽게 비유하자면, 머신러닝 모델은 학생과 같습니다. 학생에게 잘못된 교재를 주면 잘못된 것을 배웁니다.

쓰레기가 들어가면 쓰레기가 나온다는 말처럼, 더러운 데이터로 학습한 모델은 좋은 성능을 낼 수 없습니다. 첫 번째 단계는 텍스트 정규화입니다.

특수문자를 제거하고, 연속된 공백을 하나로 줄이고, 앞뒤 공백을 제거합니다. 정규표현식을 활용하면 이런 작업을 효율적으로 처리할 수 있습니다.

두 번째 단계는 이상치 처리입니다. 너무 짧은 리뷰는 의미 있는 정보를 담고 있지 않습니다.

"좋아요"라는 두 글자만으로는 모델이 충분한 맥락을 학습할 수 없습니다. 반대로 너무 긴 리뷰도 문제가 될 수 있습니다.

대부분의 모델은 입력 길이에 제한이 있기 때문입니다. 세 번째 단계는 중복 제거입니다.

같은 리뷰가 여러 번 포함되면 모델이 그 데이터에 과적합될 수 있습니다. 마치 시험에 나올 문제만 반복해서 공부한 학생이 다른 문제를 풀지 못하는 것과 같습니다.

네 번째 단계는 데이터 분리입니다. 전체 데이터를 학습용, 검증용, 테스트용으로 나눕니다.

일반적으로 8:1:1 비율을 많이 사용합니다. 학습 데이터로 모델을 훈련하고, 검증 데이터로 하이퍼파라미터를 조정하며, 테스트 데이터로 최종 성능을 평가합니다.

김개발 씨는 한 가지 더 고민이 있었습니다. 감성 분석을 하려면 각 리뷰에 긍정인지 부정인지 라벨을 붙여야 했습니다.

다행히 회사의 리뷰 데이터에는 별점이 함께 저장되어 있었습니다. 4점 이상은 긍정, 2점 이하는 부정으로 자동 라벨링할 수 있었습니다.

박시니어 씨가 조언했습니다. "자동 라벨링은 편리하지만, 샘플링해서 직접 검수하는 것도 중요해요.

별점 3점인데 내용은 부정적인 경우도 많거든요." 김개발 씨는 100개 정도를 직접 확인해보기로 했습니다. 데이터 정제 과정에서 원본 데이터의 30%가 제거되었습니다.

처음에는 아까운 마음이 들었지만, 남은 데이터의 품질이 확실히 좋아졌습니다. 좋은 재료가 준비되었으니, 이제 요리를 시작할 차례입니다.

실전 팁

💡 - 원본 데이터는 절대 수정하지 말고, 별도 컬럼이나 파일로 정제 결과를 저장하세요

  • 데이터 분포(긍정/부정 비율)를 확인하고 불균형이 심하면 오버샘플링을 고려하세요
  • 자동 라벨링 후 반드시 일부를 수작업으로 검수하세요

3. 모델 선택 및 Fine-tuning

깨끗한 데이터가 준비된 김개발 씨는 이제 모델을 선택할 차례입니다. 구글에 "NLP 모델"을 검색하니 BERT, GPT, RoBERTa, KoBERT 등 수많은 이름이 쏟아져 나왔습니다.

"도대체 뭘 써야 하죠?" 김개발 씨의 질문에 박시니어 씨가 웃으며 답했습니다.

모델 선택은 마치 자동차를 고르는 것과 같습니다. 스포츠카, SUV, 경차 중 무엇이 좋을지는 용도에 따라 다릅니다.

한국어 텍스트 분류에는 KoBERTKoELECTRA 같은 한국어 특화 모델이 효과적이며, Fine-tuning을 통해 우리 데이터에 맞게 조정할 수 있습니다.

다음 코드를 살펴봅시다.

from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import TrainingArguments, Trainer
import torch

# 한국어 BERT 모델 로드
model_name = "monologg/koelectra-base-v3-discriminator"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(
    model_name, num_labels=2  # 긍정/부정 2개 클래스
)

# 토크나이징 함수
def tokenize_function(examples):
    return tokenizer(
        examples["text"], padding="max_length",
        truncation=True, max_length=128
    )

# 학습 설정
training_args = TrainingArguments(
    output_dir="./results",
    num_train_epochs=3,
    per_device_train_batch_size=16,
    learning_rate=2e-5,
    evaluation_strategy="epoch",
    save_strategy="epoch",
    load_best_model_at_end=True
)

김개발 씨는 모델 세계의 복잡함에 압도당할 뻔했습니다. BERT, GPT, T5, ELECTRA...

이름만 들어도 머리가 복잡해졌습니다. 하지만 박시니어 씨의 설명을 듣고 나니 생각보다 간단했습니다.

핵심은 사전학습 모델이라는 개념입니다. 쉽게 비유하자면, 사전학습 모델은 이미 대학교까지 졸업한 사람과 같습니다.

기본적인 지식은 다 갖추고 있어서, 특정 직무 교육만 받으면 바로 일할 수 있습니다. 이 직무 교육에 해당하는 것이 바로 Fine-tuning입니다.

한국어 텍스트를 다루는 경우, 영어로 학습된 모델보다는 한국어로 학습된 모델을 선택하는 것이 좋습니다. KoBERT는 SKT에서 공개한 한국어 BERT 모델이고, KoELECTRA는 한국어 ELECTRA 모델입니다.

둘 다 HuggingFace에서 쉽게 불러올 수 있습니다. 김개발 씨는 KoELECTRA를 선택했습니다.

비슷한 성능에 더 효율적으로 학습할 수 있다는 장점이 있기 때문입니다. HuggingFace의 transformers 라이브러리를 사용하면 몇 줄의 코드로 모델을 불러올 수 있습니다.

Fine-tuning 과정에서 중요한 것은 하이퍼파라미터 설정입니다. 학습률(learning rate)은 너무 크면 학습이 불안정해지고, 너무 작으면 시간이 오래 걸립니다.

일반적으로 2e-5에서 5e-5 사이의 값을 사용합니다. 에폭(epoch) 수는 보통 3에서 5 정도면 충분합니다.

배치 크기(batch size)도 중요합니다. GPU 메모리가 허용하는 한 크게 잡는 것이 좋지만, 너무 크면 일반화 성능이 떨어질 수 있습니다.

16이나 32가 무난한 선택입니다. 김개발 씨는 회사의 GPU 서버에서 학습을 시작했습니다.

3 에폭 학습에 약 2시간이 걸렸습니다. 학습이 진행되는 동안 검증 데이터에 대한 정확도가 점점 올라가는 것을 보며 김개발 씨는 뿌듯했습니다.

주의할 점이 있습니다. 검증 데이터에서의 성능만 좋아지고 학습 데이터와의 격차가 커지면 과적합을 의심해야 합니다.

이럴 때는 드롭아웃을 추가하거나 학습을 일찍 멈추는 Early Stopping을 적용합니다. 박시니어 씨가 조언했습니다.

"요즘은 LoRA 같은 기법으로 더 효율적으로 Fine-tuning할 수도 있어요. 다음 프로젝트에서 한번 시도해봐요." 김개발 씨는 메모해두었습니다.

Fine-tuning이 완료된 모델은 저장해두어야 합니다. HuggingFace의 save_pretrained 메서드를 사용하면 모델과 토크나이저를 함께 저장할 수 있습니다.

이제 이 모델을 실제 서비스에 적용할 차례입니다.

실전 팁

💡 - GPU 메모리가 부족하면 gradient accumulation을 활용하세요

  • 학습 과정을 시각화하여 과적합 여부를 모니터링하세요
  • 모델 저장 시 config와 tokenizer도 함께 저장하는 것을 잊지 마세요

4. RAG 시스템 구축

Fine-tuning된 감성 분석 모델이 잘 작동했습니다. 그런데 팀장님이 추가 요청을 했습니다.

"리뷰 분석뿐 아니라, 관련 FAQ도 자동으로 찾아주면 좋겠는데요." 김개발 씨는 고민에 빠졌습니다. 모델이 모든 FAQ를 외울 수는 없는 노릇이었습니다.

RAG(Retrieval-Augmented Generation)는 마치 오픈북 시험과 같습니다. 모든 것을 외우는 대신, 필요할 때 책을 찾아보면서 답하는 방식입니다.

벡터 데이터베이스에 문서를 저장하고, 질문과 유사한 문서를 검색한 뒤, 이를 바탕으로 LLM이 답변을 생성합니다.

다음 코드를 살펴봅시다.

from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA

# 임베딩 모델 설정
embeddings = HuggingFaceEmbeddings(
    model_name="jhgan/ko-sroberta-multitask"
)

# 문서 분할
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500, chunk_overlap=50
)
documents = text_splitter.split_documents(raw_docs)

# 벡터 DB 생성
vectordb = Chroma.from_documents(
    documents, embeddings, persist_directory="./chroma_db"
)

# RAG 체인 구성
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
qa_chain = RetrievalQA.from_chain_type(
    llm=llm, retriever=vectordb.as_retriever(search_kwargs={"k": 3})
)

김개발 씨는 팀장님의 요청을 듣고 난감했습니다. FAQ가 수백 개나 되는데, 이걸 모델이 다 기억할 수 있을까요?

더구나 FAQ는 수시로 업데이트됩니다. 새로운 FAQ가 추가될 때마다 모델을 다시 학습시킬 수는 없는 노릇이었습니다.

박시니어 씨가 해결책을 제시했습니다. "RAG를 써보는 건 어때요?" RAG는 Retrieval-Augmented Generation의 약자로, 검색 증강 생성이라고 번역합니다.

쉽게 말해 검색 + 생성을 결합한 방식입니다. 비유하자면 RAG는 오픈북 시험과 같습니다.

학생이 모든 내용을 암기하는 대신, 시험 중에 책을 찾아볼 수 있습니다. 마찬가지로 LLM도 모든 정보를 기억하는 대신, 필요한 정보를 데이터베이스에서 검색해서 답변에 활용합니다.

RAG 시스템의 핵심 구성 요소는 세 가지입니다. 첫째, 임베딩 모델은 텍스트를 숫자 벡터로 변환합니다.

의미가 비슷한 문장은 비슷한 벡터로 변환되어, 나중에 유사도 검색이 가능해집니다. 한국어에는 ko-sroberta 같은 모델을 사용합니다.

둘째, 벡터 데이터베이스는 이 벡터들을 저장하고 빠르게 검색할 수 있게 해줍니다. Chroma, Pinecone, Weaviate 등 다양한 선택지가 있습니다.

로컬에서 테스트할 때는 Chroma가 간편합니다. 셋째, LLM은 검색된 문서를 바탕으로 답변을 생성합니다.

GPT-3.5나 GPT-4를 많이 사용하지만, 비용이 부담된다면 오픈소스 모델을 사용할 수도 있습니다. 김개발 씨는 LangChain 라이브러리를 사용하기로 했습니다.

LangChain은 이런 RAG 파이프라인을 쉽게 구축할 수 있게 도와주는 프레임워크입니다. 마치 레고 블록처럼 필요한 컴포넌트를 조립하면 됩니다.

문서를 준비할 때 중요한 것은 **청킹(Chunking)**입니다. 긴 문서를 적절한 크기로 나누어야 합니다.

너무 작으면 맥락이 끊어지고, 너무 크면 검색 정확도가 떨어집니다. 보통 500자에서 1000자 사이가 적당합니다.

청크 간에 약간의 중복(overlap)을 두면 문맥이 자연스럽게 이어집니다. 검색 시에는 상위 몇 개의 문서를 가져올지 결정해야 합니다.

너무 적으면 필요한 정보가 누락될 수 있고, 너무 많으면 LLM에 전달되는 컨텍스트가 길어져 비용이 증가합니다. 보통 3개에서 5개 정도가 적당합니다.

김개발 씨는 FAQ 문서 200개를 벡터 데이터베이스에 저장했습니다. 테스트 질문을 넣어보니, 관련 FAQ를 잘 찾아서 답변을 생성했습니다.

팀장님도 만족스러워했습니다. "새로운 FAQ가 추가되면 그냥 DB에 넣기만 하면 되는 거죠?" 맞습니다.

모델 재학습 없이 지식을 업데이트할 수 있다는 것이 RAG의 큰 장점입니다.

실전 팁

💡 - 청크 크기는 문서 특성에 따라 조정하세요. 구조화된 FAQ는 작게, 설명 문서는 크게

  • 검색 성능이 중요하다면 하이브리드 검색(키워드 + 벡터)을 고려하세요
  • 답변 품질을 높이려면 프롬프트 엔지니어링에 시간을 투자하세요

5. Streamlit 데모 개발

RAG 시스템까지 완성되자 팀장님이 말했습니다. "이거 다른 팀에도 보여주고 싶은데, 데모 페이지 하나 만들 수 있어요?" 김개발 씨는 웹 개발 경험이 많지 않아 걱정되었습니다.

그런데 박시니어 씨가 말했습니다. "Streamlit 써봤어요?

파이썬만 알면 돼요."

Streamlit은 마치 파워포인트처럼 쉽게 웹 앱을 만들 수 있는 프레임워크입니다. HTML, CSS, JavaScript를 몰라도 파이썬 코드만으로 인터랙티브한 데모 페이지를 만들 수 있습니다.

데이터 과학자와 ML 엔지니어가 모델을 빠르게 시연할 때 많이 사용합니다.

다음 코드를 살펴봅시다.

import streamlit as st
from model import SentimentAnalyzer, RAGSystem

# 페이지 설정
st.set_page_config(page_title="NLP 데모", layout="wide")
st.title("고객 리뷰 분석 시스템")

# 탭 구성
tab1, tab2 = st.tabs(["감성 분석", "FAQ 검색"])

with tab1:
    st.header("리뷰 감성 분석")
    review_input = st.text_area("분석할 리뷰를 입력하세요", height=100)

    if st.button("분석하기"):
        with st.spinner("분석 중..."):
            analyzer = SentimentAnalyzer()
            result = analyzer.predict(review_input)
            st.success(f"감성: {result['label']} (확신도: {result['score']:.2%})")

with tab2:
    st.header("FAQ 검색")
    question = st.text_input("질문을 입력하세요")

    if question:
        rag = RAGSystem()
        answer = rag.query(question)
        st.write(answer)

김개발 씨는 웹 개발이라는 말에 긴장했습니다. React?

Vue? 뭔가 새로 배워야 할 것 같은 압박감이 밀려왔습니다.

하지만 Streamlit을 접하고 나서 생각이 완전히 바뀌었습니다. Streamlit은 마치 마법과 같습니다.

파이썬 코드를 작성하면 자동으로 웹 페이지가 만들어집니다. HTML이나 CSS를 전혀 몰라도 됩니다.

st.write()로 텍스트를 출력하고, st.text_input()으로 입력창을 만들고, st.button()으로 버튼을 만드는 것이 전부입니다. 김개발 씨는 pip install streamlit 한 줄로 설치를 마쳤습니다.

그리고 app.py 파일을 만들어 코드를 작성하기 시작했습니다. 가장 먼저 한 일은 페이지 제목을 설정하는 것이었습니다.

st.set_page_config()로 페이지 타이틀과 레이아웃을 지정할 수 있습니다. Streamlit의 강점은 상태 관리가 자동이라는 점입니다.

버튼을 클릭하거나 입력값이 바뀌면 자동으로 페이지가 다시 렌더링됩니다. 복잡한 콜백 함수나 이벤트 핸들러를 작성할 필요가 없습니다.

김개발 씨는 탭 기능을 활용해 감성 분석과 FAQ 검색을 나눴습니다. st.tabs()를 사용하면 깔끔하게 여러 기능을 구분할 수 있습니다.

각 탭에서는 입력을 받고, 모델을 호출하고, 결과를 표시하는 간단한 흐름입니다. 로딩 중에는 사용자에게 피드백을 주는 것이 중요합니다.

st.spinner()를 사용하면 처리 중에 "분석 중..."이라는 메시지와 함께 스피너가 표시됩니다. 처리가 끝나면 st.success()나 st.error()로 결과를 보여줍니다.

박시니어 씨가 팁을 알려줬습니다. "캐싱을 잘 활용하면 속도가 훨씬 빨라져요." Streamlit의 @st.cache_resource 데코레이터를 사용하면 모델 로딩 같은 무거운 작업을 한 번만 수행하고 결과를 재사용할 수 있습니다.

로컬에서 테스트하려면 터미널에서 streamlit run app.py를 실행하면 됩니다. 브라우저가 자동으로 열리며 앱이 표시됩니다.

코드를 수정하고 저장하면 자동으로 새로고침되어 변경사항을 바로 확인할 수 있습니다. 배포도 간단합니다.

Streamlit Cloud를 사용하면 GitHub 저장소만 연결하면 무료로 배포할 수 있습니다. 회사 내부용이라면 서버에서 직접 실행하거나 Docker로 컨테이너화할 수도 있습니다.

김개발 씨는 하루 만에 데모 페이지를 완성했습니다. 다른 팀 사람들이 직접 리뷰를 입력하고 결과를 확인할 수 있게 되었습니다.

"와, 이게 ML 모델이에요?" 마케팅팀 직원의 감탄에 김개발 씨는 뿌듯했습니다.

실전 팁

💡 - @st.cache_resource로 모델 로딩을 캐싱하면 재방문 시 속도가 빨라집니다

  • st.sidebar를 활용하면 설정이나 필터를 깔끔하게 배치할 수 있습니다
  • requirements.txt를 꼭 작성해두세요. 배포 시 필수입니다

6. 성능 평가 및 개선

데모가 잘 작동하자 팀장님이 물었습니다. "성능은 어느 정도야?

정확도 같은 거 수치로 보여줄 수 있어?" 김개발 씨는 잠시 멈칫했습니다. 테스트 데이터로 돌려보긴 했는데, 체계적으로 평가하지는 않았습니다.

이제 마지막 관문, 성능 평가의 시간입니다.

모델 성능 평가는 마치 건강검진과 같습니다. 겉보기엔 멀쩡해 보여도 숫자로 확인해야 진짜 상태를 알 수 있습니다.

분류 모델에서는 정확도, 정밀도, 재현율, F1 점수를 확인하고, RAG 시스템에서는 검색 정확도와 답변 품질을 평가합니다.

다음 코드를 살펴봅시다.

from sklearn.metrics import classification_report, confusion_matrix
import seaborn as sns
import matplotlib.pyplot as plt

# 테스트 데이터로 예측
predictions = model.predict(test_texts)
true_labels = test_labels

# 분류 성능 리포트
report = classification_report(
    true_labels, predictions,
    target_names=['부정', '긍정'],
    output_dict=True
)
print(f"정확도: {report['accuracy']:.2%}")
print(f"F1 점수: {report['weighted avg']['f1-score']:.2%}")

# 혼동 행렬 시각화
cm = confusion_matrix(true_labels, predictions)
plt.figure(figsize=(8, 6))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues',
            xticklabels=['부정', '긍정'],
            yticklabels=['부정', '긍정'])
plt.ylabel('실제')
plt.xlabel('예측')
plt.title('혼동 행렬')
plt.savefig('confusion_matrix.png')

김개발 씨는 성능 평가라는 말에 긴장했습니다. 솔직히 "잘 되는 것 같다"는 느낌은 있었지만, 숫자로 증명하라고 하니 막막했습니다.

박시니어 씨가 다가와 설명을 시작했습니다. 분류 모델을 평가할 때는 여러 지표를 함께 봐야 합니다.

가장 기본적인 것은 **정확도(Accuracy)**입니다. 전체 예측 중 맞힌 비율입니다.

하지만 정확도만 보면 함정에 빠질 수 있습니다. 박시니어 씨가 예를 들었습니다.

"만약 리뷰의 95%가 긍정이라면, 모든 것을 긍정으로 예측해도 정확도가 95%예요. 하지만 이 모델은 쓸모가 없죠." 그래서 **정밀도(Precision)**와 **재현율(Recall)**도 함께 확인해야 합니다.

정밀도는 모델이 긍정이라고 예측한 것 중 실제로 긍정인 비율입니다. 재현율은 실제 긍정 중에서 모델이 긍정이라고 예측한 비율입니다.

두 지표는 트레이드오프 관계에 있어서, 둘을 조화롭게 균형 잡은 F1 점수를 많이 사용합니다. 김개발 씨는 테스트 데이터로 모델을 평가했습니다.

정확도 91%, F1 점수 89%가 나왔습니다. 괜찮은 수치였지만, 더 자세히 살펴볼 필요가 있었습니다.

**혼동 행렬(Confusion Matrix)**을 그려보니, 부정 리뷰를 긍정으로 잘못 분류하는 경우가 많았습니다. 왜 이런 오류가 발생했을까요?

오분류된 샘플들을 직접 확인해보니 패턴이 보였습니다. "기대했는데 별로였어요"처럼 긍정적인 단어가 포함되어 있지만 결론이 부정인 리뷰들이 문제였습니다.

이런 반전 표현을 모델이 어려워했습니다. 개선 방법은 여러 가지가 있습니다.

첫째, 이런 반전 표현이 포함된 데이터를 더 많이 수집하여 학습시킬 수 있습니다. 둘째, 더 큰 모델을 사용하거나 앙상블 기법을 적용할 수 있습니다.

셋째, 특별히 어려운 케이스에 대해 규칙 기반 후처리를 추가할 수 있습니다. RAG 시스템의 평가는 조금 다릅니다.

검색이 제대로 되는지, 생성된 답변이 정확한지를 확인해야 합니다. 김개발 씨는 테스트 질문 50개를 준비하고, 각각에 대해 검색된 문서가 적절한지, 답변이 정확한지를 직접 평가했습니다.

평가 결과를 문서화하는 것도 중요합니다. 나중에 모델을 개선했을 때 이전 버전과 비교할 수 있어야 합니다.

김개발 씨는 평가 결과를 엑셀 파일로 정리하고, 개선이 필요한 부분을 목록으로 만들었습니다. 팀장님에게 보고할 때는 숫자만 나열하지 않고, 비즈니스 관점에서 해석했습니다.

"현재 모델은 10개 중 9개를 정확히 분류합니다. 오분류되는 1개도 대부분 애매한 케이스입니다.

실제 업무에서는 담당자가 빠르게 확인만 하면 됩니다." 박시니어 씨가 마지막 조언을 했습니다. "성능 평가는 일회성이 아니에요.

모델을 배포한 후에도 지속적으로 모니터링해야 해요. 데이터 분포가 바뀌면 성능도 떨어질 수 있거든요." 김개발 씨는 고개를 끄덕이며 메모했습니다.

첫 NLP 프로젝트가 드디어 완성되었습니다.

실전 팁

💡 - 테스트 셋은 학습에 절대 사용하지 말고, 최종 평가용으로만 보관하세요

  • 오분류된 샘플을 직접 확인하면 모델의 약점을 파악할 수 있습니다
  • 배포 후에도 주기적으로 샘플링하여 성능을 모니터링하세요

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

#NLP#LLM#RAG#Fine-tuning#Streamlit#HuggingFace#Project

댓글 (0)

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