🤖

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

⚠️

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

이미지 로딩 중...

TF-IDF 벡터화 완벽 가이드 - 슬라이드 1/8
A

AI Generated

2025. 12. 17. · 6 Views

TF-IDF 벡터화 완벽 가이드

텍스트 데이터를 숫자로 변환하는 TF-IDF 벡터화 기법을 처음부터 차근차근 배웁니다. Bag of Words부터 시작해서 CountVectorizer, TfidfVectorizer, 희소 행렬, n-gram까지 실무에 필요한 모든 것을 다룹니다.


목차

  1. Bag of Words 개념
  2. CountVectorizer 사용법
  3. TF-IDF란 무엇인가?
  4. TfidfVectorizer 사용법
  5. 희소 행렬 이해
  6. max_features 파라미터
  7. n-gram 활용

1. Bag of Words 개념

어느 날 김개발 씨는 회사에서 고객 리뷰 분석 프로젝트를 맡게 되었습니다. "텍스트를 어떻게 기계학습 모델에 넣을 수 있을까요?" 선배 박시니어 씨가 웃으며 대답했습니다.

"먼저 텍스트를 숫자로 바꿔야 해요."

Bag of Words는 텍스트를 숫자 벡터로 변환하는 가장 기본적인 방법입니다. 마치 장바구니에 물건을 담듯이, 문서에 등장하는 단어들을 세어서 표현합니다.

문장의 순서는 무시하고 단어의 출현 빈도만 기록하는 것이 핵심입니다.

다음 코드를 살펴봅시다.

# 간단한 Bag of Words 예제
documents = [
    "I love programming",
    "I love Python",
    "Python programming is fun"
]

# 모든 고유 단어 추출
vocabulary = set()
for doc in documents:
    words = doc.lower().split()
    vocabulary.update(words)

# 단어를 인덱스로 매핑
word_to_idx = {word: idx for idx, word in enumerate(sorted(vocabulary))}
print("단어 사전:", word_to_idx)

김개발 씨는 고객 리뷰 데이터를 받아들고 막막했습니다. 수천 개의 텍스트 리뷰를 어떻게 분석해야 할까요?

기계학습 모델은 숫자만 이해할 수 있는데 말이죠. 박시니어 씨가 화이트보드를 가리키며 설명을 시작했습니다.

"우선 가장 간단한 방법부터 배워봅시다. Bag of Words라는 개념이에요." Bag of Words는 정확히 무엇일까요?

쉽게 비유하자면, Bag of Words는 마치 장바구니에 물건을 담는 것과 같습니다. 슈퍼마켓에서 쇼핑을 할 때, 어떤 물건을 어떤 순서로 담았는지는 중요하지 않습니다.

중요한 건 장바구니에 사과가 3개, 우유가 1개, 빵이 2개 들어있다는 사실입니다. 텍스트도 마찬가지입니다.

문장의 순서는 무시하고, 어떤 단어가 몇 번 등장했는지만 기록하는 것입니다. 이 방법이 왜 필요했을까요?

컴퓨터는 "좋아요", "훌륭해요" 같은 단어를 그 자체로는 이해하지 못합니다. 컴퓨터가 이해할 수 있는 건 오직 숫자뿐입니다.

그렇다고 각 단어에 임의로 번호를 매기면 "사과=1", "바나나=2"처럼 의미 없는 관계가 생겨버립니다. 더 나은 방법이 필요했습니다.

바로 이런 고민에서 Bag of Words가 탄생했습니다. 이 방법을 사용하면 각 문서를 고정된 길이의 숫자 벡터로 표현할 수 있습니다.

모든 문서가 같은 어휘 사전을 공유하므로, 서로 비교하기도 쉬워집니다. 무엇보다 구현이 간단해서 초보자도 쉽게 시작할 수 있습니다.

위의 코드를 한 줄씩 살펴보겠습니다. 먼저 세 개의 문서를 리스트로 준비했습니다.

다음으로 모든 문서를 순회하면서 등장하는 고유 단어들을 set에 모았습니다. set을 사용한 이유는 중복을 자동으로 제거해주기 때문입니다.

마지막으로 이 단어들을 알파벳 순으로 정렬해서 각 단어에 고유한 인덱스를 부여했습니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 이메일 스팸 필터를 만든다고 가정해봅시다. 스팸 메일에는 "무료", "당첨", "클릭" 같은 단어가 자주 등장합니다.

정상 메일에는 "회의", "보고서", "일정" 같은 단어가 많습니다. Bag of Words로 각 이메일을 벡터로 만들면, 기계학습 모델이 이런 패턴을 학습할 수 있습니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수는 단어의 순서를 완전히 무시한다는 점을 간과하는 것입니다.

"개가 사람을 물었다"와 "사람이 개를 물었다"는 완전히 다른 의미지만, Bag of Words로 표현하면 똑같은 벡터가 됩니다. 따라서 문맥이 중요한 작업에는 적합하지 않을 수 있습니다.

김개발 씨는 화이트보드의 예제를 보며 이해가 되기 시작했습니다. "아, 그래서 단어를 세는 거군요!" Bag of Words는 텍스트 분석의 첫걸음입니다.

이 개념을 제대로 이해하면, 더 복잡한 TF-IDF 같은 기법도 쉽게 배울 수 있습니다.

실전 팁

💡 - 단어를 소문자로 변환해서 "Love"와 "love"를 같은 단어로 취급하세요

  • 불용어(the, a, an 등)는 제거하면 더 좋은 결과를 얻을 수 있습니다
  • 어휘 사전이 너무 크면 메모리 문제가 생길 수 있으니 빈도가 낮은 단어는 제외하세요

2. CountVectorizer 사용법

김개발 씨가 직접 Bag of Words를 구현해보니 코드가 복잡해졌습니다. "매번 이렇게 만들어야 하나요?" 박시니어 씨가 고개를 저었습니다.

"아니요, 사이킷런의 CountVectorizer를 쓰면 됩니다."

CountVectorizer는 사이킷런에서 제공하는 Bag of Words 구현체입니다. 복잡한 전처리와 벡터화 과정을 단 몇 줄로 처리할 수 있습니다.

자동으로 어휘 사전을 만들고, 문서를 벡터로 변환해주는 강력한 도구입니다.

다음 코드를 살펴봅시다.

from sklearn.feature_extraction.text import CountVectorizer

# 문서 데이터
documents = [
    "I love programming",
    "I love Python",
    "Python programming is fun"
]

# CountVectorizer 생성 및 학습
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(documents)

# 어휘 사전 확인
print("어휘 사전:", vectorizer.get_feature_names_out())
# 벡터 확인 (희소 행렬을 밀집 행렬로 변환)
print("문서 벡터:\n", X.toarray())

김개발 씨는 처음으로 Bag of Words를 직접 구현해봤습니다. 코드는 동작했지만, 뭔가 비효율적이라는 느낌이 들었습니다.

단어를 추출하고, 중복을 제거하고, 인덱스를 매기고... 이 모든 과정을 매번 반복해야 할까요?

"실무에서는 이미 만들어진 도구를 사용합니다." 박시니어 씨가 노트북을 열며 말했습니다. "CountVectorizer라는 게 있어요." CountVectorizer는 정확히 무엇일까요?

쉽게 비유하자면, CountVectorizer는 마치 전자동 세탁기와 같습니다. 손빨래로 옷을 빨 수도 있지만, 세탁기에 옷을 넣고 버튼만 누르면 모든 과정이 자동으로 진행됩니다.

물 온도 조절, 세제 투입, 헹굼, 탈수까지 알아서 해줍니다. CountVectorizer도 마찬가지입니다.

텍스트만 넣으면 전처리부터 벡터화까지 자동으로 처리해줍니다. 왜 이런 도구가 필요했을까요?

초기 데이터 과학자들은 텍스트 전처리에 많은 시간을 소비했습니다. 소문자 변환, 특수문자 제거, 토큰화, 어휘 사전 구축...

이런 과정을 프로젝트마다 반복해야 했습니다. 실수하기도 쉬웠고, 코드도 길어졌습니다.

표준화된 도구가 절실히 필요했습니다. 바로 이런 필요성에서 CountVectorizer가 탄생했습니다.

CountVectorizer를 사용하면 단 두 줄로 벡터화를 완료할 수 있습니다. 또한 다양한 옵션을 제공해서 불용어 제거, 최대/최소 빈도 설정, n-gram 생성 등을 쉽게 할 수 있습니다.

무엇보다 검증된 라이브러리라서 안정적이고 빠릅니다. 위의 코드를 한 줄씩 살펴보겠습니다.

먼저 CountVectorizer 객체를 생성합니다. 이때 다양한 파라미터를 설정할 수 있지만, 기본값도 충분히 좋습니다.

다음으로 fit_transform 메서드를 호출합니다. 이 메서드는 두 가지 일을 한 번에 처리합니다.

fit은 문서들로부터 어휘 사전을 학습하고, transform은 문서를 벡터로 변환합니다. 결과는 희소 행렬 형태로 반환됩니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 뉴스 기사 분류 시스템을 만든다고 가정해봅시다.

수천 개의 기사를 정치, 경제, 스포츠, 연예 카테고리로 분류해야 합니다. CountVectorizer로 각 기사를 벡터로 변환하면, 로지스틱 회귀나 나이브 베이즈 같은 분류 알고리즘을 바로 적용할 수 있습니다.

실제로 많은 뉴스 포털 사이트에서 이런 방식을 사용합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 fit_transformtransform의 차이를 이해하지 못하는 것입니다. 학습 데이터에는 fit_transform을 사용하지만, 테스트 데이터에는 transform만 사용해야 합니다.

테스트 데이터로 다시 fit을 하면 어휘 사전이 달라져서 올바른 평가를 할 수 없습니다. 김개발 씨는 코드를 실행해보고 감탄했습니다.

"와, 정말 간단하네요!" CountVectorizer는 텍스트 분석의 필수 도구입니다. 이것만 잘 사용해도 많은 프로젝트를 진행할 수 있습니다.

실전 팁

💡 - lowercase=True가 기본값이라 자동으로 소문자로 변환됩니다

  • max_features 파라미터로 어휘 사전 크기를 제한할 수 있습니다
  • stop_words='english'로 영어 불용어를 자동으로 제거할 수 있습니다

3. TF-IDF란 무엇인가?

CountVectorizer로 리뷰를 분석하던 김개발 씨가 문제를 발견했습니다. "is", "the" 같은 흔한 단어들이 너무 큰 비중을 차지했습니다.

"단순히 세는 것만으로는 부족해요." 박시니어 씨가 말했습니다. "TF-IDF를 써야 합니다."

TF-IDF는 단어의 중요도를 계산하는 통계적 기법입니다. 단순히 빈도만 세는 것이 아니라, 문서 전체에서 얼마나 흔한 단어인지도 고려합니다.

흔한 단어는 낮은 점수를, 특정 문서에만 나타나는 단어는 높은 점수를 부여합니다.

다음 코드를 살펴봅시다.

from sklearn.feature_extraction.text import TfidfVectorizer

# 문서 데이터
documents = [
    "the cat sat on the mat",
    "the dog sat on the log",
    "cats and dogs are enemies"
]

# TF-IDF 벡터화
vectorizer = TfidfVectorizer()
X = vectorizer.fit_transform(documents)

# 결과 확인
print("어휘 사전:", vectorizer.get_feature_names_out())
print("\nTF-IDF 행렬:\n", X.toarray().round(2))

김개발 씨는 CountVectorizer로 고객 리뷰를 분석했습니다. 그런데 이상한 점을 발견했습니다.

"the", "is", "a" 같은 단어들이 가장 높은 빈도를 보였습니다. 이런 단어들이 정말 중요할까요?

리뷰의 감정을 파악하는 데 도움이 될까요? 박시니어 씨가 화면을 보며 고개를 끄덕였습니다.

"바로 그 문제를 해결하기 위해 TF-IDF가 만들어졌어요." TF-IDF는 정확히 무엇일까요? 쉽게 비유하자면, TF-IDF는 마치 신문 기사에서 중요한 키워드를 찾는 것과 같습니다.

"그리고", "하지만", "것이" 같은 단어는 모든 기사에 나타나므로 중요하지 않습니다. 하지만 "반도체", "수출", "실적" 같은 단어가 경제 기사에만 자주 등장한다면, 그 기사의 핵심 주제를 나타냅니다.

TF-IDF는 바로 이런 중요도를 수치화합니다. TF-IDF는 두 가지 개념을 곱한 값입니다.

TF(Term Frequency)는 단어가 문서 내에서 얼마나 자주 등장하는지를 나타냅니다. 많이 등장할수록 높은 값을 가집니다.

IDF(Inverse Document Frequency)는 단어가 전체 문서에서 얼마나 드문지를 나타냅니다. 모든 문서에 등장하는 단어는 낮은 값을, 특정 문서에만 등장하는 단어는 높은 값을 가집니다.

왜 이런 복잡한 계산이 필요했을까요? 초기 정보 검색 시스템은 단순히 키워드 빈도만 사용했습니다.

그러다 보니 "the"나 "is" 같은 단어가 검색 결과를 왜곡시켰습니다. 사용자가 "인공지능 로봇"을 검색했는데, "the robot is intelligent"처럼 "the"와 "is"가 많은 문서가 상위에 노출되는 문제가 있었습니다.

더 똑똑한 방법이 필요했습니다. 바로 이런 문제를 해결하기 위해 TF-IDF가 개발되었습니다.

TF-IDF를 사용하면 문서의 핵심 단어를 자동으로 찾아낼 수 있습니다. 검색 엔진의 정확도가 크게 향상됩니다.

또한 문서 간 유사도를 계산할 때도 더 정확한 결과를 얻을 수 있습니다. 실제로 구글 초창기 검색 알고리즘도 TF-IDF를 기반으로 했습니다.

위의 코드를 한 줄씩 살펴보겠습니다. 세 개의 문서를 준비했습니다.

첫 두 문서는 "the", "sat", "on"이 공통으로 등장하고, 세 번째 문서는 다른 단어들을 포함합니다. TfidfVectorizer를 생성하고 fit_transform을 호출하면, 각 단어의 TF-IDF 값이 자동으로 계산됩니다.

결과를 보면 "the" 같은 흔한 단어는 낮은 값을, "enemies" 같은 희귀 단어는 높은 값을 가집니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 고객 문의 자동 분류 시스템을 만든다고 가정해봅시다. "결제", "환불", "배송"처럼 특정 카테고리를 나타내는 키워드들을 TF-IDF로 추출하면, 새로운 문의가 들어왔을 때 어느 부서로 전달해야 할지 자동으로 판단할 수 있습니다.

많은 고객센터에서 이런 시스템을 운영하고 있습니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 TF-IDF가 모든 상황에서 CountVectorizer보다 낫다고 생각하는 것입니다. 문서가 매우 짧거나, 모든 단어가 비슷한 빈도로 등장하는 경우에는 오히려 단순한 Count가 더 좋을 수 있습니다.

데이터의 특성을 파악하고 적절한 방법을 선택해야 합니다. 김개발 씨는 결과를 보며 놀랐습니다.

"정말로 중요한 단어들만 높은 점수를 받았네요!" TF-IDF는 텍스트 분석의 핵심 기법입니다. 이것을 이해하면 더 정교한 자연어 처리 시스템을 만들 수 있습니다.

실전 팁

💡 - IDF는 로그 스케일을 사용해서 극단적인 값을 방지합니다

  • 사이킷런의 TF-IDF는 자동으로 L2 정규화를 적용해서 벡터 길이를 1로 만듭니다
  • sublinear_tf=True를 사용하면 TF에도 로그 스케일을 적용할 수 있습니다

4. TfidfVectorizer 사용법

TF-IDF 개념을 이해한 김개발 씨는 이제 직접 구현해보고 싶었습니다. "TF-IDF 공식을 직접 코딩해야 하나요?" 박시니어 씨가 웃었습니다.

"아니요, TfidfVectorizer를 쓰면 됩니다."

TfidfVectorizer는 사이킷런에서 제공하는 TF-IDF 구현체입니다. CountVectorizer처럼 간단하게 사용할 수 있으면서도, 다양한 옵션으로 세밀하게 조정할 수 있습니다.

전처리부터 TF-IDF 계산까지 모든 과정을 자동화합니다.

다음 코드를 살펴봅시다.

from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np

# 실제 리뷰 데이터 예시
reviews = [
    "This product is amazing I love it",
    "Terrible quality waste of money",
    "Great value for the price",
    "Not worth it very disappointed"
]

# TfidfVectorizer 설정
vectorizer = TfidfVectorizer(
    max_features=10,      # 상위 10개 단어만 사용
    stop_words='english',  # 영어 불용어 제거
    lowercase=True         # 소문자로 변환
)

# 벡터화 수행
X = vectorizer.fit_transform(reviews)

# 각 리뷰에서 가장 중요한 단어 찾기
feature_names = vectorizer.get_feature_names_out()
for i, review in enumerate(reviews):
    tfidf_scores = X[i].toarray()[0]
    top_idx = np.argmax(tfidf_scores)
    print(f"리뷰 {i+1}의 핵심 단어: {feature_names[top_idx]} ({tfidf_scores[top_idx]:.3f})")

김개발 씨는 TF-IDF의 수학적 원리를 배웠습니다. TF에 IDF를 곱하고, 로그를 취하고, 정규화하고...

복잡한 공식이 머릿속을 가득 채웠습니다. "이걸 다 구현해야 하나요?" 박시니어 씨가 고개를 저었습니다.

"실무에서는 TfidfVectorizer를 씁니다. 모든 계산을 자동으로 해줘요." TfidfVectorizer는 정확히 무엇일까요?

쉽게 비유하자면, TfidfVectorizer는 마치 자동차의 내비게이션과 같습니다. 목적지만 입력하면 최적의 경로를 계산해줍니다.

교통 상황, 신호등, 도로 상태를 모두 고려해서 가장 빠른 길을 안내합니다. 직접 지도를 펴고 경로를 계산할 수도 있지만, 내비게이션이 훨씬 빠르고 정확합니다.

TfidfVectorizer도 마찬가지입니다. 텍스트만 입력하면 복잡한 TF-IDF 계산을 순식간에 처리합니다.

왜 이런 도구가 중요할까요? TF-IDF를 직접 구현하려면 많은 코드가 필요합니다.

단어 빈도 계산, 문서 빈도 계산, 로그 변환, 정규화... 각 단계마다 실수할 가능성이 있습니다.

또한 성능 최적화도 어렵습니다. 수만 개의 문서를 처리하려면 효율적인 자료구조와 알고리즘이 필요합니다.

개인이 처음부터 만들기에는 부담이 큽니다. 바로 이런 이유로 TfidfVectorizer가 널리 사용됩니다.

TfidfVectorizer를 사용하면 복잡한 구현 없이도 검증된 TF-IDF를 바로 적용할 수 있습니다. 다양한 파라미터로 세밀하게 조정할 수 있어서 데이터에 맞게 최적화할 수 있습니다.

무엇보다 CountVectorizer와 API가 동일해서 쉽게 바꿔가며 실험할 수 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

네 개의 리뷰 데이터를 준비했습니다. 긍정 리뷰와 부정 리뷰가 섞여 있습니다.

TfidfVectorizer를 생성할 때 세 가지 파라미터를 설정했습니다. max_features=10은 빈도가 높은 상위 10개 단어만 사용한다는 의미입니다.

stop_words='english'는 "the", "is" 같은 불용어를 자동으로 제거합니다. 마지막으로 각 리뷰의 TF-IDF 벡터에서 가장 높은 값을 가진 단어를 찾아서 출력합니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 쇼핑몰의 상품 추천 시스템을 만든다고 가정해봅시다.

각 상품의 설명을 TF-IDF로 벡터화하면, 코사인 유사도를 계산해서 비슷한 상품을 찾을 수 있습니다. 사용자가 "무선 블루투스 이어폰"을 보고 있다면, "블루투스", "무선", "이어폰" 같은 키워드의 TF-IDF가 높은 다른 상품들을 추천할 수 있습니다.

많은 이커머스 플랫폼에서 이런 기법을 사용합니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 너무 많은 파라미터를 한 번에 조정하려는 것입니다. 먼저 기본 설정으로 시작해서 결과를 확인하고, 하나씩 파라미터를 바꿔가며 성능 변화를 관찰해야 합니다.

또한 max_features를 너무 작게 설정하면 중요한 정보를 잃을 수 있으니 주의해야 합니다. 김개발 씨는 코드를 실행하고 결과를 보며 고개를 끄덕였습니다.

"정말로 각 리뷰의 핵심을 잘 찾아내네요!" TfidfVectorizer는 텍스트 분석의 스위스 아미 나이프입니다. 이것만 잘 활용해도 대부분의 텍스트 분석 작업을 처리할 수 있습니다.

실전 팁

💡 - norm='l2'가 기본값으로, 각 문서 벡터의 길이를 1로 정규화합니다

  • smooth_idf=True로 IDF 계산 시 0으로 나누는 문제를 방지합니다
  • use_idf=False로 설정하면 TF만 사용하는 CountVectorizer처럼 동작합니다

5. 희소 행렬 이해

김개발 씨가 TfidfVectorizer의 결과를 출력하려다가 당황했습니다. 화면에 이상한 숫자들만 출력되었습니다.

"이게 뭔가요?" 박시니어 씨가 설명했습니다. "희소 행렬이에요.

메모리를 절약하는 특별한 자료구조입니다."

희소 행렬은 대부분의 값이 0인 행렬을 효율적으로 저장하는 자료구조입니다. 텍스트 벡터화 결과는 대부분 0이므로, 0이 아닌 값만 저장해서 메모리를 크게 절약할 수 있습니다.

사이킷런은 자동으로 희소 행렬을 사용합니다.

다음 코드를 살펴봅시다.

from sklearn.feature_extraction.text import TfidfVectorizer
import numpy as np

# 문서 데이터
documents = [
    "machine learning is amazing",
    "deep learning is powerful",
    "python is great for data science"
]

vectorizer = TfidfVectorizer()
X_sparse = vectorizer.fit_transform(documents)

# 희소 행렬 정보 확인
print("희소 행렬 타입:", type(X_sparse))
print("희소 행렬 크기:", X_sparse.shape)
print("0이 아닌 원소 개수:", X_sparse.nnz)
print(f"희소도: {(1 - X_sparse.nnz / (X_sparse.shape[0] * X_sparse.shape[1])) * 100:.2f}%")

# 밀집 행렬로 변환
X_dense = X_sparse.toarray()
print("\n밀집 행렬:\n", X_dense.round(2))

김개발 씨는 리뷰 데이터 1만 건을 TF-IDF로 벡터화했습니다. 그런데 결과를 확인하려고 print(X)를 실행했을 때, 이상한 출력이 나왔습니다.

"(0, 147) 0.3521" 같은 형식의 숫자들이 화면을 가득 채웠습니다. 박시니어 씨가 화면을 보고 미소를 지었습니다.

"처음 보면 당황스럽죠. 이게 희소 행렬이에요." 희소 행렬은 정확히 무엇일까요?

쉽게 비유하자면, 희소 행렬은 마치 주소록과 같습니다. 모든 사람의 이름을 가나다 순으로 나열하고 전화번호를 적는 대신, 실제로 아는 사람의 이름과 번호만 기록합니다.

빈 칸을 모두 남겨두는 것은 종이 낭비입니다. 희소 행렬도 마찬가지입니다.

값이 0인 위치는 저장하지 않고, 0이 아닌 값과 그 위치만 기록합니다. 왜 이런 자료구조가 필요했을까요?

텍스트 데이터를 벡터화하면 어휘 사전이 수만 개가 됩니다. 각 문서는 그중 몇십 개의 단어만 포함합니다.

예를 들어 어휘 사전에 10,000개 단어가 있고 한 문서에 50개 단어만 등장한다면, 벡터의 99.5%가 0입니다. 이걸 모두 저장하면 엄청난 메모리 낭비입니다.

1만 개 문서를 처리하면 10,000 × 10,000 = 1억 개의 숫자를 저장해야 합니다. 바로 이런 문제를 해결하기 위해 희소 행렬이 사용됩니다.

희소 행렬을 사용하면 메모리 사용량이 수백 배 줄어듭니다. 0이 아닌 값만 저장하므로, 위 예시에서는 50개의 값만 저장하면 됩니다.

또한 계산 속도도 빨라집니다. 행렬 곱셈 같은 연산에서 0인 값은 건너뛸 수 있기 때문입니다.

수만 개의 문서도 일반 노트북에서 처리할 수 있습니다. 위의 코드를 한 줄씩 살펴보겠습니다.

세 개의 문서를 TF-IDF로 벡터화했습니다. 결과 X_sparse는 희소 행렬입니다.

type(X_sparse)를 출력하면 scipy.sparse.csr_matrix라는 타입이 나옵니다. nnz 속성은 "number of non-zeros"의 약자로, 0이 아닌 원소의 개수를 나타냅니다.

희소도를 계산하면 대부분이 0이라는 것을 확인할 수 있습니다. toarray() 메서드로 일반 NumPy 배열로 변환하면 익숙한 형태로 볼 수 있습니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 수십만 개의 뉴스 기사를 분석하는 시스템을 만든다고 가정해봅시다.

어휘 사전이 10만 개라면, 밀집 행렬로 저장하면 수십 GB의 메모리가 필요합니다. 하지만 희소 행렬을 사용하면 수백 MB로 충분합니다.

대규모 텍스트 분석이 일반 서버에서도 가능해집니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 불필요하게 toarray()를 호출하는 것입니다. 시각화나 디버깅을 위해서는 괜찮지만, 실제 분석에서는 희소 행렬 상태로 사용해야 합니다.

사이킷런의 대부분 알고리즘은 희소 행렬을 직접 지원합니다. 밀집 행렬로 변환하면 메모리가 부족해서 프로그램이 멈출 수 있습니다.

김개발 씨는 희소도를 확인하고 놀랐습니다. "95% 이상이 0이네요.

정말 공간을 많이 절약하겠어요!" 희소 행렬은 대규모 텍스트 분석의 핵심입니다. 이것을 이해하면 메모리 효율적인 시스템을 만들 수 있습니다.

실전 팁

💡 - csr_matrix는 행 기준 압축 방식으로, 행렬 연산에 효율적입니다

  • toarray()는 작은 데이터셋에서만 사용하세요
  • 희소 행렬끼리는 직접 연산이 가능하므로 변환이 필요 없습니다

6. max features 파라미터

김개발 씨의 모델이 너무 느리게 학습되었습니다. 어휘 사전이 5만 개나 되었기 때문입니다.

"어떻게 줄일 수 있나요?" 박시니어 씨가 답했습니다. "max_features 파라미터를 사용하세요."

max_features는 어휘 사전의 크기를 제한하는 파라미터입니다. 빈도가 높은 상위 N개 단어만 선택해서 벡터 차원을 줄입니다.

모델 학습 속도를 높이고 과적합을 방지하는 효과가 있습니다.

다음 코드를 살펴봅시다.

from sklearn.feature_extraction.text import TfidfVectorizer

# 샘플 문서
documents = [
    "machine learning and deep learning are subfields of artificial intelligence",
    "natural language processing uses machine learning techniques",
    "computer vision is another important area of artificial intelligence",
    "deep neural networks have revolutionized machine learning"
]

# max_features 비교
for n_features in [None, 10, 5]:
    vectorizer = TfidfVectorizer(max_features=n_features)
    X = vectorizer.fit_transform(documents)

    print(f"\nmax_features={n_features}")
    print(f"어휘 사전 크기: {len(vectorizer.get_feature_names_out())}")
    print(f"선택된 단어: {list(vectorizer.get_feature_names_out())}")
    print(f"행렬 크기: {X.shape}")

김개발 씨는 자랑스럽게 만든 리뷰 분류 모델을 테스트했습니다. 그런데 학습 시간이 30분이나 걸렸습니다.

메모리도 8GB나 사용했습니다. 원인을 찾아보니 어휘 사전에 5만 개가 넘는 단어가 들어있었습니다.

박시니어 씨가 코드를 보더니 한 가지를 지적했습니다. "max_features를 설정해보세요." max_features는 정확히 무엇일까요?

쉽게 비유하자면, max_features는 마치 옷장 정리와 같습니다. 옷이 200벌이 있지만 실제로 자주 입는 건 20벌뿐입니다.

나머지는 1년에 한두 번 입거나 아예 입지 않습니다. 옷장이 작다면 자주 입는 옷만 남기고 나머지는 정리하는 게 현명합니다.

단어도 마찬가지입니다. 전체 어휘 중 자주 등장하는 단어만 남기고 나머지는 제외하면 효율적입니다.

왜 어휘 사전을 제한해야 할까요? 어휘 사전이 크면 여러 문제가 생깁니다.

첫째, 벡터 차원이 커져서 계산량이 기하급수적으로 증가합니다. 둘째, 희귀 단어들이 노이즈가 되어 모델 성능을 떨어뜨립니다.

셋째, 과적합이 발생할 가능성이 높아집니다. 특히 학습 데이터가 적을 때는 차원의 저주 문제가 심각합니다.

바로 이런 문제를 해결하기 위해 max_features를 사용합니다. max_features를 설정하면 빈도가 높은 상위 N개 단어만 사용합니다.

벡터 차원이 줄어들어 학습 속도가 빨라집니다. 메모리 사용량도 감소합니다.

무엇보다 중요한 단어에만 집중해서 모델 성능이 향상되는 경우가 많습니다. 실험 결과 전체 어휘의 10-20%만 사용해도 성능이 비슷하거나 더 좋은 경우가 흔합니다.

위의 코드를 한 줄씩 살펴보겠습니다. 네 개의 AI 관련 문서를 준비했습니다.

먼저 max_features=None으로 모든 단어를 사용합니다. 그다음 10개, 5개로 제한해봅니다.

각 경우마다 어휘 사전 크기와 선택된 단어들을 출력합니다. 결과를 보면 빈도가 높은 "learning", "machine" 같은 단어들이 우선적으로 선택되는 것을 확인할 수 있습니다.

실제 현업에서는 어떻게 활용할까요? 예를 들어 스마트폰 앱의 리뷰 분석 시스템을 만든다고 가정해봅시다.

수십만 개의 리뷰에서 수만 개의 단어가 등장합니다. 하지만 실제로 감정 분석에 중요한 단어는 수천 개 정도입니다.

max_features=5000으로 설정하면 모델이 10배 빨라지면서도 정확도는 거의 동일합니다. 빠른 프로토타이핑과 실시간 서비스가 가능해집니다.

하지만 주의할 점도 있습니다. 초보 개발자들이 흔히 하는 실수는 max_features를 너무 작게 설정하는 것입니다.

너무 작으면 중요한 정보를 잃어버립니다. 반대로 너무 크면 효과가 없습니다.

일반적으로 1,000에서 10,000 사이에서 시작해서, 교차 검증으로 최적값을 찾는 게 좋습니다. 데이터셋 크기와 작업의 특성에 따라 적절한 값이 다릅니다.

김개발 씨는 max_features=3000으로 설정하고 다시 학습시켰습니다. "학습 시간이 30분에서 3분으로 줄었어요!" max_features는 실용적인 텍스트 분석의 핵심 도구입니다.

적절히 사용하면 속도와 성능을 모두 개선할 수 있습니다.

실전 팁

💡 - 데이터셋이 작으면 100-1000, 크면 5000-10000이 적당합니다

  • GridSearchCV로 최적의 max_features를 자동으로 찾을 수 있습니다
  • min_df, max_df 파라미터와 함께 사용하면 더 세밀한 조정이 가능합니다

7. n-gram 활용

김개발 씨의 감정 분석 모델이 "not good"을 긍정으로 잘못 분류했습니다. "good"이라는 단어 때문이었습니다.

"단어 순서를 고려할 수 있나요?" 박시니어 씨가 답했습니다. "n-gram을 사용하세요."

n-gram은 연속된 n개의 단어를 하나의 특성으로 취급하는 기법입니다. 단어의 순서와 조합을 고려할 수 있어서 문맥을 더 잘 파악할 수 있습니다.

bigram(2-gram), trigram(3-gram) 등이 자주 사용됩니다.

다음 코드를 살펴봅시다.

from sklearn.feature_extraction.text import TfidfVectorizer

# 감정이 포함된 리뷰
reviews = [
    "not good at all",
    "very good product",
    "not bad but not great",
    "absolutely amazing quality"
]

# 다양한 n-gram 설정 비교
configs = [
    (1, 1),  # unigram만
    (1, 2),  # unigram + bigram
    (2, 2),  # bigram만
]

for ngram_range in configs:
    vectorizer = TfidfVectorizer(ngram_range=ngram_range)
    X = vectorizer.fit_transform(reviews)

    print(f"\nngram_range={ngram_range}")
    print(f"특성 개수: {len(vectorizer.get_feature_names_out())}")
    features = vectorizer.get_feature_names_out()
    print(f"예시 특성: {list(features[:10])}")

김개발 씨는 고객 리뷰 감정 분석 모델을 완성했습니다. 테스트해보니 대부분 잘 작동했습니다.

그런데 "not good", "not recommended" 같은 리뷰를 긍정으로 잘못 분류했습니다. "good", "recommended" 같은 긍정 단어가 포함되었기 때문입니다.

박시니어 씨가 문제를 지적했습니다. "Bag of Words는 단어 순서를 무시합니다.

n-gram을 써야 해요." n-gram은 정확히 무엇일까요? 쉽게 비유하자면, n-gram은 마치 퍼즐 조각을 연결하는 것과 같습니다.

퍼즐 조각 하나만 보면 전체 그림을 알기 어렵습니다. 하지만 여러 조각을 연결하면 그림이 보이기 시작합니다.

"not"이라는 조각과 "good"이라는 조각을 따로 보면 의미가 명확하지 않지만, "not good"이라고 연결하면 부정적 의미가 분명해집니다. n-gram은 바로 이렇게 단어들을 연결해서 봅니다.

n-gram에는 여러 종류가 있습니다. unigram은 단어 하나만 봅니다.

"not", "good"처럼 개별 단어입니다. bigram은 연속된 두 단어를 봅니다.

"not good", "very bad"처럼 단어 쌍입니다. trigram은 세 단어를 연결합니다.

"not very good"처럼 더 긴 구문을 인식할 수 있습니다. 일반적으로 bigram까지만 사용하는 경우가 많습니다.

왜 n-gram이 필요했을까요? Bag of Words의 가장 큰 약점은 단어 순서를 무시한다는 것입니다.

"A는 B보다 좋다"와 "B는 A보다 좋다"가 같은 벡터가 됩니다. 부정어도 제대로 처리하지 못합니다.

"좋지 않다"에서 "좋다"만 인식해서 긍정으로 오판합니다. 실제 언어에서는 단어의 조합이 의미를 만들어냅니다.

바로 이런 한계를 극복하기 위해 n-gram이 도입되었습니다. n-gram을 사용하면 단어의 순서와 조합을 고려할 수 있습니다.

"not good"을 하나의 특성으로 인식해서 부정적으로 분류합니다. 관용구나 전문 용어도 잘 인식합니다.

"machine learning", "deep neural network" 같은 복합어를 하나의 개념으로 다룰 수 있습니다. 실제로 감정 분석, 텍스트 분류 등에서 성능이 크게 향상됩니다.

위의 코드를 한 줄씩 살펴보겠습니다. 네 개의 리뷰를 준비했습니다.

부정어가 포함된 문장들입니다. ngram_range=(1, 1)은 unigram만 사용합니다.

ngram_range=(1, 2)는 unigram과 bigram을 모두 사용합니다. ngram_range=(2, 2)는 bigram만 사용합니다.

각 설정의 특성 개수와 예시를 출력해서 차이를 확인합니다. 실제 현업에서는 어떻게 활용할까요?

예를 들어 의료 문서 분류 시스템을 만든다고 가정해봅시다. "당뇨병"이라는 단어만으로는 부족합니다.

"제2형 당뇨병", "당뇨병 합병증" 같은 bigram을 인식해야 정확한 분류가 가능합니다. 법률 문서 분석도 마찬가지입니다.

"지적 재산권", "손해 배상" 같은 법률 용어는 bigram으로 처리해야 제대로 인식됩니다. 하지만 주의할 점도 있습니다.

초보 개발자들이 흔히 하는 실수는 n-gram을 너무 크게 설정하는 것입니다. trigram이나 4-gram을 사용하면 특성 개수가 폭발적으로 증가합니다.

희귀한 조합들이 너무 많이 생겨서 과적합이 발생합니다. 일반적으로 ngram_range=(1, 2)가 가장 무난합니다.

데이터가 충분히 크고 전문 용어가 많은 도메인에서만 (1, 3)을 시도해보세요. 김개발 씨는 ngram_range=(1, 2)로 모델을 다시 학습시켰습니다.

"이제 'not good'을 제대로 부정으로 분류하네요!" n-gram은 텍스트 분석의 성능을 한 단계 높여주는 강력한 도구입니다. 적절히 활용하면 더 똑똑한 자연어 처리 시스템을 만들 수 있습니다.

실전 팁

💡 - 대부분의 경우 ngram_range=(1, 2)가 최적입니다

  • n-gram을 사용하면 특성이 많아지므로 max_features와 함께 사용하세요
  • min_df 파라미터로 희귀한 n-gram을 제거하면 과적합을 방지할 수 있습니다

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

#Python#TF-IDF#NLP#Vectorization#MachineLearning

댓글 (0)

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