-
[3월 2주차-3/11(3)]🏆 카운트 기반의 단어 표현 (Bag of Words)Why Not SW CAMP 5기/수업 기록 2025. 3. 11. 13:48
텍스트 데이터를 수치화하는 방법 중 가장 기본적인 기법인 Bag of Words(BoW)는 단어들의 순서를 고려하지 않고, 출현 빈도(Frequency)를 기반으로 문서를 표현하는 기법입니다. 주로 문서 분류나 문서 간 유사도 비교와 같은 작업에서 많이 사용됩니다.
📌 1. Bag of Words (BoW)란?
문서 내 단어의 등장 횟수를 기반으로 텍스트를 수치화하는 기법
✅ BoW는 단어 순서를 고려하지 않고, 각 단어가 문서에서 얼마나 등장했는지에 집중합니다.
✅ BoW는 텍스트 분류, 감성 분석, 문서 유사도 측정 등의 작업에서 활용됩니다.📌 활용 예시
- 뉴스 기사에서 어떤 단어가 많이 등장하는지를 분석하여 해당 기사의 주제를 분류
- 영화 리뷰에서 긍정적인 단어와 부정적인 단어의 빈도수를 활용한 감성 분석
📌 2. 파이썬으로 BoW 만들기
BoW를 구현하는 방법에는 직접 코드를 작성하는 방법과 사이킷런(Scikit-learn) 라이브러리를 활용하는 방법이 있습니다.
✅ 2.1 직접 BoW 구현하기
import nltk nltk.download('stopwords') from konlpy.tag import Okt okt = Okt() # BoW 생성 함수 def build_bag_of_words(doc): doc = doc.replace('.', '') # 문장에서 마침표 제거 tokenized_doc = okt.morphs(doc) word_to_index = {} bow = [] for word in tokenized_doc: if word not in word_to_index.keys(): word_to_index[word] = len(word_to_index) bow.insert(len(word_to_index)-1, 1) else: index = word_to_index.get(word) bow[index] += 1 return word_to_index, bow # 예제 문장 적용 doc1 = '정부가 발표하는 물가상승률과 소비자가 느끼는 물가상승률은 다르다' vocab, bow = build_bag_of_words(doc1) print(vocab) # {'정부': 0, '가': 1, '발표': 2, '하는': 3, '물가상승률': 4, '과': 5, '소비자': 6, '느끼는': 7, '은': 8, '다르다': 9} print(bow) # [1, 2, 1, 1, 2, 1, 1, 1, 1, 1]📌 설명
- word_to_index → 단어를 정수로 매핑하여 사전을 생성합니다.
- bow → 단어의 등장 횟수를 기록한 리스트입니다.
- BoW는 단어의 순서를 고려하지 않고, 단순히 등장 빈도만 반영한다는 특징이 있습니다.
✅ 2.2 CountVectorizer를 활용한 BoW 생성
사이킷런(Scikit-learn)의 CountVectorizer를 사용하면 BoW를 쉽게 구현할 수 있음
from sklearn.feature_extraction.text import CountVectorizer corpus = ['you know I want your love. because I love you.'] vectorizer = CountVectorizer() # BoW 벡터 생성 bow_matrix = vectorizer.fit_transform(corpus).toarray() print('Bag of Words Vector:', bow_matrix) # [[1, 1, 2, 1, 2, 1]] # 각 단어의 인덱스 확인 print('Vocabulary:', vectorizer.vocabulary_) # {'you': 4, 'know': 1, 'want': 3, 'your': 5, 'love': 2, 'because': 0}📌 설명
- fit_transform()을 사용하여 각 단어의 등장 횟수를 벡터 형태로 변환.
- vocabulary_ 속성을 통해 단어와 정수 매핑을 확인 가능.
- CountVectorizer는 기본적으로 길이가 2 이상인 단어만 추출하므로 "I"와 같은 단어는 제외됩니다.
📌 3. 불용어 제거 (Stopwords Filtering)
BoW에는 중요하지 않은 단어(불용어, Stopwords)까지 포함될 수 있습니다. 이를 방지하려면 불용어를 제거하는 과정이 필요합니다.
from sklearn.feature_extraction.text import CountVectorizer from nltk.corpus import stopwords text = ["Family is not an important thing. It's everything."] # 사용자가 직접 지정한 불용어 print('bag of words vector :',vect.fit_transform(text).toarray()) # [[1 1 1 1 1]] print('vocabulary :',vect.vocabulary_) # {'family': 1, 'important': 2, 'thing': 4, 'it': 3, 'everything': 0}📌 불용어 제거 후 변경점
- "is", "not" 등의 불용어가 제거됨.
- "family", "important", "thing" 등 의미 있는 단어만 남음.
사이킷런에서 기본 제공하는 영어 불용어를 사용할 수도 있습니다.
vect = CountVectorizer(stop_words="english") print('bag of words vector :',vect.fit_transform(text).toarray()) # [[1 1 1]] print('vocabulary :',vect.vocabulary_) # {'family': 0, 'important': 1, 'thing': 2}또는, NLTK에서 지원하는 불용어 리스트를 사용할 수도 있습니다.
stop_words = stopwords.words("english") vect = CountVectorizer(stop_words=stop_words) print('bag of words vector :',vect.fit_transform(text).toarray()) # [[1 1 1 1]] print('vocabulary:',vect.vocabulary_) # {'family': 1, 'important': 2, 'thing': 3, 'everything': 0}
📌 4. 문서 단어 행렬 (TF-IDF)
문서 내에서 중요한 단어를 강조하는 BoW의 확장 개념
✅ 개념
- TF-IDF (Term Frequency-Inverse Document Frequency)는 단순한 단어 빈도 기반의 BoW보다 더 정교한 가중치를 제공합니다.
- 특정 문서에서 자주 등장하지만, 전체 문서에서는 드물게 등장하는 단어의 가중치를 높이는 방식.
📊 TF-IDF 공식
- TF (Term Frequency, 단어 빈도): 특정 단어가 문서에서 등장한 횟수
- IDF (Inverse Document Frequency, 역문서 빈도): 해당 단어가 등장한 문서의 비율을 역수로 변환하여 계산
📌 6. 파이썬으로 TF-IDF 직접 구현하기
from math import log import pandas as pd docs = [ '먹고 싶은 사과', '먹고 싶은 바나나', '길고 노란 바나나 바나나', '저는 과일이 좋아요', ] vocab = list(set(w for doc in docs for w in doc.split())) vocab.sort() # ['과일이', '길고', '노란', '먹고', '바나나', '사과', '싶은', '저는', '좋아요'] # TF, IDF, TF-IDF 값 구하는 함수 # 총 문서의 수 N = len(docs) def tf(t,d): return d.count(t) def idf(t): df = 0 for doc in docs: df += t in doc return log(N/(df+1)) def tfidf(t,d): return tf(t,d) * idf(t) # DTM을 데이터프레임에 저장하여 출력 result = [] for i in range(N): result.append([]) d = docs[i] for j in range(len(vocab)): t = vocab[j] result[-1].append(tf(t,d)) tf_ = pd.DataFrame(result, columns=vocab) ''' 과일이 길고 노란 먹고 바나나 사과 싶은 저는 좋아요 0 0 0 0 1 0 1 1 0 0 1 0 0 0 1 1 0 1 0 0 2 0 1 1 0 2 0 0 0 0 3 1 0 0 0 0 0 0 1 1 ''' # 각 단어에 대한 IDF값 result = [] for j in range(len(vocab)): t = vocab[j] result.append(idf(t)) idf_ = pd.DataFrame(result, index = vocab, columns=["IDF"]) ''' IDF 과일이 0.693147 길고 0.693147 노란 0.693147 먹고 0.287682 바나나 0.287682 사과 0.693147 싶은 0.287682 저는 0.693147 좋아요 0.693147 ''' # TF-IDF 행렬 result = [] for i in range(N): result.append([]) d = docs[i] for j in range(len(vocab)): t = vocab[j] result[-1].append(tfidf(t,d)) tfidf_ = pd.DataFrame(result, columns=vocab) ''' 과일이 길고 노란 ... 싶은 저는 좋아요 0 0.000000 0.000000 0.000000 ... 0.287682 0.000000 0.000000 1 0.000000 0.000000 0.000000 ... 0.287682 0.000000 0.000000 2 0.000000 0.693147 0.693147 ... 0.000000 0.000000 0.000000 3 0.693147 0.000000 0.000000 ... 0.000000 0.693147 0.693147 '''📌 설명
- TF-IDF를 사용하면 흔한 단어보다 의미 있는 단어의 가중치를 높여 더 효과적인 문서 비교가 가능.
📌 7. 사이킷런을 이용한 DTM과 TF-IDF
사이킷런(Scikit-learn)을 활용하면 BoW와 TF-IDF를 더욱 쉽게 구현할 수 있습니다.
✅ CountVectorizer를 이용한 DTM (Document-Term Matrix)
from sklearn.feature_extraction.text import CountVectorizer corpus = [ 'you know I want your Love', 'I like you', 'What should I do' ] vector = CountVectorizer() X = vector.fit_transform(corpus).toarray() print(X)출력 결과:
array([[0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 0, 0, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 0]])📌 설명
- CountVectorizer를 사용하여 문서별 단어 출현 빈도를 행렬 형태로 변환합니다.
- X는 각 문서에서 단어가 몇 번 등장했는지를 나타내는 DTM 행렬입니다.
✅ TF-IDF 자동 계산 (TfidfVectorizer 활용)
사이킷런은 TF-IDF를 자동으로 계산해주는 TfidfVectorizer를 제공합니다.
from sklearn.feature_extraction.text import TfidfVectorizer corpus = [ 'you know I want your Love', 'I like you', 'What should I do' ] # TF-IDF 벡터 생성 tfidfv = TfidfVectorizer().fit(corpus) X_tfidf = tfidfv.transform(corpus).toarray() print(X_tfidf)출력 결과:
[[0., 0.46735098, 0., 0.46735098, 0., 0.46735098, 0., 0.35543247, 0.46735098], [0., 0., 0.79596054, 0., 0., 0., 0., 0.60534851, 0.], [0.57735027, 0., 0., 0., 0.57735027, 0., 0.57735027, 0., 0.]]📌 설명
- TfidfVectorizer()를 사용하면 BoW와 동일한 방식으로 벡터화하면서도 각 단어의 중요도를 고려한 가중치를 적용할 수 있습니다.
- 자주 등장하는 일반적인 단어의 가중치는 낮아지고, 특정 문서에서 중요한 단어의 가중치는 높아집니다.
🎯 최종 요약
✅ BoW는 단어의 등장 빈도를 기준으로 문서를 벡터화하는 기법.
✅ TF-IDF는 BoW의 확장 개념으로, 단어의 상대적 중요도를 반영하여 더욱 정교한 벡터를 생성.
✅ 사이킷런의 CountVectorizer와 TfidfVectorizer를 활용하면 쉽게 구현 가능.
✅ BoW와 TF-IDF는 텍스트 분류, 감성 분석, 문서 유사도 분석 등 다양한 NLP 작업에 활용 가능.📌 이제 BoW와 TF-IDF를 활용하여 텍스트 데이터를 효과적으로 분석할 수 있습니다! 🚀
'Why Not SW CAMP 5기 > 수업 기록' 카테고리의 다른 글
[3월 2주차-3/12(1)]코사인 유사도, 유클리드 거리, 자카드 유사도 - 차이점과 활용법 (0) 2025.03.12 [3월 2주차-3/11(4)]🏙️ 블로거들이 추천하는 서울 명소 분석하기! (WordCloud 시각화) (1) 2025.03.11 [3월 2주차-3/11(2)]🧠 언어 모델 (Language Model) (0) 2025.03.11 [3월 2주차-3/11(1)]🛠 한국어 전처리 패키지 (5) 2025.03.11 [3월 1주차-3/7(3)]텍스트 전처리4- 원-핫 인코딩, 데이터의 분리 (0) 2025.03.07