불용어 제거, 전처리 한 단계가 파이프라인 정확도를 가른다
NLTK·scikit-learn·spaCy의 불용어 처리 방식과 한국어 불용어 목록 구축, 고객 리뷰·뉴스 분류 파이프라인 적용 사례를 정리한다
2026-08-14 · 최초 발행 2025-05-23
텍스트를 벡터로 바꾸기 전, 관사와 조사처럼 문법적 기능만 수행하고 의미에는 별로 기여하지 않는 단어들을 걸러내는 작업이 있다. 불용어(Stopword) 제거는 이 작업을 가리키며, 처리 효율과 분석 품질을 동시에 좌우하는 전처리 단계다.
왜 불용어를 걸러내는가
불용어를 제거하면 데이터 크기가 줄어 처리 속도가 빨라지고 저장 공간이 절약된다. 의미 없는 단어가 빠지면 핵심 정보에 집중할 수 있어 텍스트 분류·군집화의 정확도가 개선되고, 특징(feature) 수가 줄어드는 만큼 모델 학습 속도가 빨라지고 과적합(overfitting) 위험도 낮아진다.
영어는 사전이 이미 있다
영어 불용어는 주로 내장된 stop_words 사전을 활용해 제거한다. NLTK는 다운로드한 불용어 사전으로 토큰을 걸러낸다.
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
# 불용어 사전 다운로드
nltk.download('stopwords')
nltk.download('punkt')
# 영어 불용어 목록 불러오기
stop_words = set(stopwords.words('english'))
# 예시 텍스트
text = "This is an example of stopword removal in natural language processing"
# 토큰화
word_tokens = word_tokenize(text)
# 불용어 제거
filtered_sentence = [word for word in word_tokens if word.lower() not in stop_words]
print("원본 텍스트 토큰:", word_tokens)
print("불용어 제거 후:", filtered_sentence)
scikit-learn은 벡터라이저 옵션 하나로 불용어 제거를 끝낸다.
from sklearn.feature_extraction.text import CountVectorizer
# 불용어 제거 옵션을 포함한 벡터라이저 생성
vectorizer = CountVectorizer(stop_words='english')
# 예시 텍스트
corpus = [
"This is the first document.",
"This document is the second document.",
"And this is the third one.",
"Is this the first document?"
]
# 벡터화 및 불용어 제거
X = vectorizer.fit_transform(corpus)
# 결과 확인
print("특징 이름:", vectorizer.get_feature_names_out())
print("벡터화 결과:", X.toarray())
spaCy는 토큰 객체의 is_stop 속성으로 판단한다.
import spacy
# 영어 모델 로드
nlp = spacy.load('en_core_web_sm')
# 예시 텍스트
text = "This is an example of stopword removal in natural language processing"
# 텍스트 처리
doc = nlp(text)
# 불용어 제거
filtered_tokens = [token.text for token in doc if not token.is_stop]
print("원본 텍스트 토큰:", [token.text for token in doc])
print("불용어 제거 후:", filtered_tokens)
한국어는 목록을 직접 만들어야 한다
한국어는 영어와 달리 표준화된 불용어 목록이 부족해 사용자가 직접 목록을 구축하고 관리하는 경우가 많다. txt 파일에 한 줄씩 작성하거나 CSV 파일로 체계적으로 관리하는 방식이 일반적이며, 조사('의', '가', '이', '은', '는', '을', '를', '에', '에서', '로'), 접속사('그리고', '또한', '그러나', '하지만'), 대명사('저', '나', '너', '우리', '그것'), 부사('매우', '너무', '아주', '잘', '더') 등이 흔히 목록에 오른다.
텍스트 파일에서 불러오는 방식은 이렇다.
# 불용어 목록 파일에서 불용어 로드
def load_stopwords(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
stopwords = [line.strip() for line in f]
return set(stopwords)
# 불용어 파일 경로
stopwords_file = 'korean_stopwords.txt'
# 불용어 로드
korean_stopwords = load_stopwords(stopwords_file)
# 예시 텍스트 (토큰화 가정)
tokens = ['저는', '오늘', '서울에', '갔습니다', '.']
# 불용어 제거
filtered_tokens = [token for token in tokens if token not in korean_stopwords]
print("원본 토큰:", tokens)
print("불용어 제거 후:", filtered_tokens)
CSV로 관리하면 컬럼 하나만 지정하면 된다.
import pandas as pd
# CSV 파일에서 불용어 목록 로드
def load_stopwords_from_csv(file_path, column_name):
df = pd.read_csv(file_path)
stopwords = df[column_name].tolist()
return set(stopwords)
# 불용어 파일 경로
stopwords_csv = 'korean_stopwords.csv'
# 불용어 로드 (CSV 파일의 'word' 컬럼에 불용어가 저장되어 있다고 가정)
korean_stopwords = load_stopwords_from_csv(stopwords_csv, 'word')
# 예시 텍스트 토큰
tokens = ['이것은', '한국어', '문장', '입니다', '.']
# 불용어 제거
filtered_tokens = [token for token in tokens if token not in korean_stopwords]
print("원본 토큰:", tokens)
print("불용어 제거 후:", filtered_tokens)
목록 하나로는 부족하다 — 운영 시 고려사항
도메인마다 불용어의 기준이 다르다. 고빈도이면서 정보량이 낮은 단어를 도메인 전문가가 검토해 목록에 반영하고, 성능이 개선되지 않으면 목록을 다시 수정하는 순환이 필요하다.
한국어는 형태소 분석과 결합하면 정확도가 더 올라간다.
from konlpy.tag import Okt
import pandas as pd
# 불용어 목록 로드
stopwords = pd.read_csv('korean_stopwords.csv')['word'].tolist()
# 형태소 분석기 초기화
okt = Okt()
# 예시 텍스트
text = "자연어 처리에서 불용어 제거는 중요한 전처리 과정입니다."
# 형태소 분석
morphs = okt.morphs(text)
# 불용어 제거
filtered_morphs = [word for word in morphs if word not in stopwords]
print("원본 형태소:", morphs)
print("불용어 제거 후:", filtered_morphs)
목록 관리는 상황별로 갈린다. 검색 시스템에서는 검색어에 자주 등장하지만 결과에 영향을 미치지 않는 단어를, 감성 분석에서는 감정 표현과 관련 없는 문법적 단어를, 주제 모델링에서는 주제 파악에 도움이 되지 않는 일반 단어를 걸러내야 한다.
고객 리뷰와 뉴스 분류 파이프라인에서
고객 리뷰 분석 시스템에서는 특수문자 제거와 형태소 분석, 불용어 제거를 하나의 함수로 묶어 전체 리뷰 데이터에 일괄 적용한다.
import pandas as pd
from konlpy.tag import Mecab
import re
# 데이터 로드 (예: 고객 리뷰 데이터)
reviews = pd.read_csv('customer_reviews.csv')
# 불용어 목록 로드
stopwords = pd.read_csv('korean_stopwords.csv')['word'].tolist()
# 형태소 분석기 초기화
mecab = Mecab()
# 전처리 함수
def preprocess_review(text):
# 특수문자 제거
text = re.sub(r'[^\w\s]', '', text)
# 형태소 분석
morphs = mecab.morphs(text)
# 불용어 제거
filtered_morphs = [word for word in morphs if word not in stopwords]
return filtered_morphs
# 모든 리뷰에 적용
reviews['processed_text'] = reviews['review_text'].apply(preprocess_review)
# 결과 확인
print(reviews[['review_text', 'processed_text']].head())
뉴스 기사 분류 시스템에서는 불용어 제거를 거친 텍스트를 TF-IDF로 벡터화하고 나이브 베이즈 분류기로 학습시킨다.
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from konlpy.tag import Okt
# 데이터 로드 (예: 뉴스 기사 데이터)
news_data = pd.read_csv('news_articles.csv')
# 불용어 목록 로드
with open('korean_stopwords.txt', 'r', encoding='utf-8') as f:
stopwords = [line.strip() for line in f]
# 형태소 분석기 초기화
okt = Okt()
# 텍스트 전처리 함수
def preprocess_text(text):
# 형태소 분석
morphs = okt.morphs(text)
# 불용어 제거
filtered_morphs = [word for word in morphs if word not in stopwords]
return ' '.join(filtered_morphs)
# 데이터 전처리
news_data['processed_text'] = news_data['content'].apply(preprocess_text)
# 학습/테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(
news_data['processed_text'],
news_data['category'],
test_size=0.2,
random_state=42
)
# 파이프라인 구성
pipeline = Pipeline([
('tfidf', TfidfVectorizer(min_df=2, max_df=0.9)),
('classifier', MultinomialNB())
])
# 모델 학습
pipeline.fit(X_train, y_train)
# 모델 평가
accuracy = pipeline.score(X_test, y_test)
print(f"Model accuracy: {accuracy:.4f}")
불용어 처리는 언어별로 접근이 갈린다. 영어는 NLTK·scikit-learn·spaCy가 제공하는 stop_words 사전으로 손쉽게 처리할 수 있지만, 한국어는 표준화된 목록이 없어 직접 구축하고 도메인 특성에 맞춰 관리해야 한다. 형태소 분석과 결합한 불용어 처리, 그리고 도메인 전문가 검토를 포함한 순환 개선 과정이 실무에서는 결과의 질을 가른다.