Bag of Words (BoW)는 자연어 처리(NLP)에서 사용되는 기술입니다. 이는 문법과 단어의 순서를 고려하지 않고 텍스트 데이터를 기계가 읽을 수 있는 형식, 특히 수치 값으로 변환하는 데 널리 사용됩니다. BoW를 이해하는 것은 텍스트 데이터를 다루는 모든 사람에게 중요합니다. 파이썬은 Bag of Words를 효과적으로 구현할 수 있는 다양한 도구와 라이브러리를 제공합니다.
이 튜토리얼에서는 BoW에 대해 깊이 탐구하고, 그 개념을 소개하며, 용도와 파이썬에서의 상세 구현을 설명합니다. 이 튜토리얼이 끝나면, 여러분은 실제 문제에 Bag of Words 모델을 적용할 수 있게 될 것입니다. NLP에 새로이 접근하는 경우, 파이썬에서의 자연어 처리(NLP) 스킬 트랙을 확인해 보세요.
Bag of Words는 무엇인가요?
Bag of Words는 텍스트 데이터에서 기능을 추출하여 기계 학습 작업(텍스트 분류와 감성 분석 등)에 사용되는 기술입니다. 이것이 중요한 이유는 기계 학습 알고리즘이 텍스트 데이터를 처리할 수 없기 때문입니다. 텍스트를 숫자로 변환하는 과정은 기능 추출 또는 기능 인코딩으로 알려져 있습니다.
Bag of Words는 문서에서 단어의 발생 빈도에 기반을 둡니다. 이 과정은 텍스트에서 사전을 찾고 그 발생 빈도를 측정하는 것으로 시작됩니다. 이를 ‘백’이라고 부르는 이유는 단어의 순서와 구조가 고려되지 않고, 발생 빈도만 고려하기 때문입니다.
Bag of Words 모델은 주변 단어를 사용하여 대상 단어를 예측하여 단어 간의 의미적 관계를 포착하는 Continuous Bag of Words Model(CBOW)과 다릅니다. CBOW는 대규모 말뭉치에서 훈련이 필요하며, 낮은 차원의 벡터를 생성하여 단어의 문맥이 중요한 복잡한 NLP 응용에 유용합니다.
어스ぺクト |
BOW |
CBOW |
목적 |
각 단어의 발생 횟수 계산 |
문맥 기반으로 목표 단어 예측 |
출력 유형 |
고차원, 희소 벡터 |
저차원, 밀집 벡터 임베딩 |
문맥 고려 여부 |
아니요 (단어 순서 무시) |
네 (주변 단어 사용) |
表現 |
희소 빈도 벡터 |
의미를 포착한 밀집 벡터 |
복잡성 |
낮음(훈련 필요 없음) |
높음(대형 말뭉치에서의 훈련 필요) |
typische Anwendungen |
텍스트 분류, 감정 분석 |
단어 임베딩, 문맥이 필요한 NLP 작업 |
왜 Bag of Words를 사용해야 할까?
Bag of Words는 많은 NLP 작업 에서 유용하며, 그 사용 이유에는 다음과 같은 것들이 포함됩니다:
- 특성 추출: 비구조화된 텍스트 데이터를 구조화된 데이터로 변환하여 다양한 기계 학습 알고리즘의 입력으로 사용할 수 있습니다.
- 간단성과 효율성: BoW는 계산적으로 간단하게 구현할 수 있으며, 소형에서 중형 크기의 텍스트 말뭉치에 잘 작동합니다.
- 문서 유사성: 코사인 유사도와 같은 기술을 사용하여 텍스트 문서 간의 유사성을 계산할 수 있습니다.
- 텍스트 분류Naive Bayes와 같은 기술을 결합할 때 BoW는 스팸 분류와 같은 텍스트 분류 작업에 효과적이며, 감정 분석 등에도 사용됩니다.
하지만 의미, 단어 구조, 단어 배열을 고려하지 않는 등의 단점도 있습니다.
Python에서 Bag of Words를 구현하는 단계
bag-of-words 모델을 만들기 위해, 문헌에 있는 모든 단어를 가져와 각 단어로 컬럼을 만듭니다. 행은 문장을 나타냅니다. 특정 단어가 문장에 존재하면 1로 나타내고, 단어가 존재하지 않으면 0으로 나타냅니다. 컬럼에 있는 각 단어는 단일 특징을 나타냅니다.
결국, 스파스 행렬을 얻게 됩니다. 스파스 행렬은 많은 수가 0인 행렬입니다.
데이터 전처리
파이썬에서 bag-of-words 모델을 만들기 위해서는 몇 가지 전처리 단계를 거쳐야 합니다. 이 단계는 토큰화와停 사용 불가능 단어 제거를 포함합니다.
토큰화는 텍스트를 더 작은 단위로 나누는 과정으로, 일반적으로 단어로 나누는 것을 의미합니다. NLTK를 사용하여 토큰화를 수행할 수 있습니다.
Stop words는 영어에서 흔한 단어로 “the,” “that,” “a”와 같은 것들이 있으며, 문장의 정의에 기여하지 않습니다.
import nltk from nltk.corpus import stopwords from nltk.tokenize import word_tokenize # Stopwords와 토큰라이저를 아직 다운로드하지 않았다면 다운로드하세요 nltk.download("punkt") nltk.download("stopwords") # 예제 문장 sentence = "This is an example showing how to remove stop words from a sentence." # 문장을 단어로 토큰화하세요 words = word_tokenize(sentence) # 영어의 정지어 목록을 가져하세요 stop_words = set(stopwords.words("english")) # 문장에서 정지어를 제거하세요 filtered_sentence = [word for word in words if word.lower() not in stop_words] # 단어를 다시 문장으로 결합하세요 filtered_sentence = " ".join(filtered_sentence) print(filtered_sentence)
출력:
example showing remove stop words sentence.
어휘집 생성
어휘집은 텍스트 말뭉치에서 발견된 고유한 단어들의 모음입니다. 어휘집을 구성하는 것은 말뭉치에서 모든 고유한 단어를 모아 그들의 출현 횟수를 계산하는 것을 포함합니다. 이 어휘집은 언어 모델링, 단어 임베딩, 텍스트 분류와 같은 다양한 NLP 작업에 유용합니다.
아래 코드는 말뭉치에서 단어의 간단한 빈도 분포를 생성하며, 어휘집 구성이나 텍스트 내용 이해와 같은 기본 NLP 작업에 유용합니다:
- The corpus 변수는 몇 가지 예문을 포함하고 있습니다. 실제 응용 프로그램에서는 이 변수는 더 크고 다양한 텍스트 데이터를 포함할 것입니다.
- vocab =
defaultdict(int)
은 단어 빈도 계산을 간소화시키며, 새로운 단어를 자동으로 0으로 초기화하여 점검 없이 직접 증가할 수 있게 합니다. - 각 문장은 소문자로 변환하여 정규 표현식을 사용해 단어를 추출하여 토큰화됩니다. 패턴
\b\w+\b
는 문장 부호와 다른 기호를 무시하고 알phanumeric 문자만으로 구성된 단어를 식별합니다. - 각 단어의 수는 vocab 사전에서 업데이트됩니다.
- 어휘는 빈도수의 내림차순으로 정렬되어 가장 흔한 단어가 상단에 보이도록 하여 참조하기 쉽게 표시됩니다.
import re # 정규 표현 모듈을 导入하여 텍스트 처리를 도와줍니다 from collections import ( defaultdict, ) # defaultdict를 导入하여 단어 빈도 계산을 쉽게 처리합니다 # 텍스트 예시 말뭉치 - 분석하기 위한 작은 문장 데이터 셋 corpus = [ "Tokenization is the process of breaking text into words.", "Vocabulary is the collection of unique words.", "The process of tokenizing is essential in NLP.", ] # 단어 빈도를 저장하기 위해 defaultdict를 정수 값으로 초기화합니다 # defaultdict(int)는 각 새로운 키에 기본 정수 값 0을 초기화합니다 vocab = defaultdict(int) # 말뭉치의 각 문장을 루프를 돌려 토큰화하고 표준화합니다 for sentence in corpus: # 문장을 소문자로 변환하여 계수 일관성을 보장합니다 (예: 'Tokenization'과 'tokenization'은 같은 단어로 처리됩니다) # 정규 표현을 사용하여 알phanumeric 문자로 구성된 단어를 찾습니다 words = re.findall(r"\b\w+\b", sentence.lower()) # 발견된 각 단어에 대해 vocab 사전에서 그 빈도를 증가시킵니다 for word in words: vocab[word] += 1 # defaultdict vocab을 일반 사전으로 변환하여 처리와 정렬을 쉽게 합니다 # 사전을 단어 빈도로 내림차순 정렬하고 새로운 사전으로 변환합니다 sorted_vocab = dict(sorted(vocab.items(), key=lambda x: x[1], reverse=True)) # 각 단어와 그 빈도 수를 정렬된 형태로 표시합니다 print("Vocabulary with Frequencies:", sorted_vocab)
출력:
Vocabulary with Frequencies: {'is': 3, 'the': 3, 'of': 3, 'process': 2, 'words': 2, 'tokenization': 1, 'breaking': 1, 'text': 1, 'into': 1, 'vocabulary': 1, 'collection': 1, 'unique': 1, 'tokenizing': 1, 'essential': 1, 'in': 1, 'nlp': 1}
어휘를 수동으로 구축하는 것은 특히 대규모 말뭉치의 경우 시간이 많이 소요될 수 있습니다. Scikit-learn의 CountVectorizer는 이 과정을 자동화하고, 나중에 보게 될 것처럼 더 유연한 텍스트 처리를 가능하게 합니다.
파이썬을 사용한 Bag of Words 구현 (From Scratch)
Bag of Words의 기본적인 구현을 수동으로 시작해보겠습니다. 파이썬에서의 이 구현은 그것이 어떻게 동작하는 내부 기계를 이해하는 데 도움이 될 것입니다.
수동 구현
Step 1: 텍스트 데이터 전처리
우리는 텍스트를 처리하는 간단한 함수를 정의하는 것으로 시작할 것입니다. 여기에는 토큰화, 소문자 변환 및 구두점 제거가 포함됩니다.
from collections import defaultdict import string # 샘플 텍스트 데이터: 문장들 corpus = [ "Python is amazing and fun.", "Python is not just fun but also powerful.", "Learning Python is fun!", ] # 텍스트 전처리 함수 def preprocess(text): # 소문자로 변환 text = text.lower() # 구두점 제거 text = text.translate(str.maketrans("", "", string.punctuation)) # 토큰화: 텍스트를 단어로 분할 tokens = text.split() return tokens # 샘플 코퍼스에 전처리 적용 processed_corpus = [preprocess(sentence) for sentence in corpus] print(processed_corpus)
출력:
[['python', 'is', 'amazing', 'and', 'fun'], ['python', 'is', 'not', 'just', 'fun', 'but', 'also', 'powerful'], ['learning', 'python', 'is', 'fun']]
Step 2: 어휘 구축
이제 모든 문서를 검토하여 고유한 단어의 완전한 목록을 구축해야 합니다. 이것이 우리의 어휘입니다.
초기화된 빈 집합을 사전을 위한 초기화 vocabulary = set() 사전을 구축하기 for sentence in processed_corpus: vocabulary.update(sentence) 정렬된 목록으로 변환하기 vocabulary = sorted(list(vocabulary)) print("Vocabulary:", vocabulary)
Step 3: 단어 빈도 계산 및 벡터화
이제 처리된 말뭉치의 각 문서에서 사전에 포함된 각 단어의 빈도를 계산할 것입니다.
def create_bow_vector(sentence, vocab): vector = [0] * len(vocab) 초기화된 제로 벡터 생성 for word in sentence: if word in vocab: idx = vocab.index(word) 사전에서 단어의 인덱스를 찾기 vector[idx] += 1 해당 인덱스에서 카운트 증가 return vector
이제 말뭉치의 각 문서에 대해 Bag of Words 표현을 생성하였습니다.
처리된 말뭉치의 각 문장에 대해 BoW 벡터 생성 bow_vectors = [create_bow_vector(sentence, vocabulary) for sentence in processed_corpus] print("Bag of Words Vectors:") for vector in bow_vectors: print(vector)
출력:
Bag of Words Vectors: [0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1] [1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1] [0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1]
Scikit-learn의 CountVectorizer 사용
수동으로 Bag of Words 모델을 구축하는 것은 학습에 좋지만, 실무 애플리케이션에서는 효율적이고 최적화된 라이브러리인 Scikit-learn를 사용하고 싶을 것입니다.
우리가 토큰화에 사용하는 파이썬 함수는 CountVectorizer입니다. 이 함수는 sklearn.feature_extraction.text
에서 import됩니다. CountVectorizer
의 특징 중 하나는 max_features
로, 이는 바그 오브 워즈 모델에 사용하고자 하는 최대 단어 수를 나타냅니다. 이 경우 None을 사용하여 모든 기능을 사용하게 됩니다.
CountVectorizer
인스턴스를 생성한 후, .fit_transform(
) 메서드를 사용하여 바그 오브 워즈 모델을 생성합니다. 그 다음, .toarray()
를 사용하여 바그 오브 워즈 모델을 기계 학습 모델에 입력할 수 있는 numpy 배열로 변환합니다.
一旦装配完毕,CountVectorizer已经建立了一个特征索引字典。词汇中一个词的索引值与其在整个训练语料库中的频率相关联。
from sklearn.feature_extraction.text import CountVectorizer # 원본语料库 corpus = [ "Python is amazing and fun.", "Python is not just fun but also powerful.", "Learning Python is fun!", ] # CountVectorizer 객체 생성 vectorizer = CountVectorizer() # 语料库에 맞게 조정하고 변환 X = vectorizer.fit_transform(corpus) # 생성된 어휘 목록 출력 print("Vocabulary:", vectorizer.get_feature_names_out()) # Bag-of-Words 행렬 출력 print("BoW Representation:") print(X.toarray())
Output:
markdownVocabulary: ['also' 'amazing' 'and' 'but' 'fun' 'is' 'just' 'learning' 'not' 'powerful' 'python'] BoW Representation: [[0 1 1 0 1 1 0 0 0 0 1] [1 0 0 1 1 1 1 0 1 1 1] [0 0 0 0 1 1 0 1 0 0 1]]
예제: Bag of Words 적용
이제 작은 텍스트 语料库인 세 개의 영화 리뷰에 BoW 모델을 적용하여 전체 과정을 설명해 보겠습니다.
이 작은 텍스트 语料库에 BoW 모델을 적용하기 위해 Scikit-learn의 CountVectorizer를 사용하겠습니다.
다음은 우리가 따를 단계입니다:
CountVectorizer
는 텍스트를 토큰화하고, 문장 부호를 제거하며, 단어를 자동으로 소문자로 변환합니다..fit_transform(corpus)
는 문서를 문서-어휘 행렬로 변환하며 각 행은 문서를 나타내고 각 열은 어휘의 단어를 나타냅니다.X_dense
는 각 문서에서 각 단어의 빈도를 나타내는 밀집 행렬입니다.
from sklearn.feature_extraction.text import CountVectorizer # 영화 리뷰 샘플 문서 corpus = [ "I loved the movie, it was fantastic!", "The movie was okay, but not great.", "I hated the movie, it was terrible.", ] # CountVectorizer 초기화 vectorizer = CountVectorizer() # 문서를 문서-어휘 행렬로 변환 X = vectorizer.fit_transform(corpus) # 문서-어휘 행렬을 밀집 형식으로 변환(시각화를 위한 선택 사항) X_dense = X.toarray() # 어휘를 가져옵니다 (단어에서 인덱스 위치로의 매핑) vocab = vectorizer.get_feature_names_out() # 어휘와 문서-어휘 행렬 출력 print("Vocabulary:", vocab) print("Document-Term Matrix:\n", X_dense)
Output:
Vocabulary: ['but' 'fantastic' 'great' 'hated' 'it' 'loved' 'movie' 'not' 'okay' 'terrible' 'the' 'was'] Document-Term Matrix: [[0 1 0 0 1 1 1 0 0 0 1 1] 첫 번째 리뷰: "영화가 정말 좋았어요, fantastique!" [1 0 1 0 1 0 1 1 1 0 1 1] 두 번째 리뷰: "영화는 괜찮았지만 대단하지는 않았어요." [0 0 0 1 1 0 1 0 0 1 1 1]] 세 번째 리뷰: "영화를 싫어했어요, 끔찍했어요."
위의 출력을 어떻게 해석할 수 있는지 보겠습니다:
- corpus에 있는 각 고유한 단어는 인덱스를 할당받고, 단어는 알파벳 순으로 정렬됩니다. 예를 들어, “but”는 인덱스 0에 있고, “fantastic”은 인덱스 1에 있으며, “movie”는 인덱스 6에 있고 그렇게 합니다.
- 문서 행렬의 각 행은 영화 리뷰를 나타내고, 각 열은 사전에 있는 단어에 대응합니다. 행렬의 값들은 해당 문서에서 각 단어의 빈도를 나타냅니다.
- 첫 번째 리뷰: [0 1 0 0 1 1 1 0 0 0 1 1]는 다음을 의미합니다:
- “fantastic” 단어가 한 번 나타납니다 (인덱스 1에 1),
- “loved” 단어가 한 번 나타납니다 (인덱스 5에 1),
- “movie” 단어가 한 번 나타납니다 (인덱스 6에 1),
- “it” 단어가 한 번 나타납니다 (인덱스 4에 1),
- 그리고 그렇게 계속됩니다.
BoW 벡터는 다음과 같이 해석할 수 있습니다:
- 각 문서는 단어 수를 나타내는 숫자 벡터입니다. 벡터의 차원은 사전의 크기와 같습니다. 이 경우, 사전에는 12개의 단어가 있기 때문에 각 리뷰는 12차원 벡터로 변환됩니다.
- 각 행의 대부분의 단어는 제로이기 때문에 각 문서는 사전의 모든 단어를 포함하지 않습니다. 따라서 BoW 모델은 자주 희소하며, 많은 제로를 포함합니다.
Bag of Words의 장점과 한계
Bag of Words 모델의 장점과 한계에 대해 몇 가지 다루어보겠습니다.
장점
- imple implements and interprets: Bag of Words 모델은 가장 간단한 텍스트 표현 기법 중 하나로, 초보자에게 적합합니다. 그 간단함 덕분에 복잡한 전처리나 특별한 모델이 필요 없이 빠르게 구현할 수 있습니다.
- Easy to use for text classification tasks: Bag of Words는 텍스트 분류, 감성 분석, 스팸 탐지와 같은 기본적인 작업에 적합합니다. 이러한 작업은 복잡한 언어 모델이 필요하지 않아 BOW 표현이 충분하고 효율적입니다.
한계
- Vocabulary size affects sparsity of representations: 사전 크기가 클수록 표현이 더욱 희소하고 고차원이 됩니다. 이러한 희소성은 모델이 효과적으로 학습하기 어렵게 만들고, 과도한 계산 비용을 피하기 위해 사전 크기를 신경 쓰게 조절해야 합니다.
- Produces sparse matrices that are computationally expensive: 각 문서가 가능한 대로 큰 사전의 각 단어 빈도로 표현되므로, 결과로 나오는 행렬은 대부분 제로로 이루어져 있어 저장하고 처리하는 데 비효율적일 수 있습니다. 희소 행렬은 많은 메모리를 소비하고, 특히 큰 데이터셋에서는 효율적인 저장과 계산을 위한 특별한 도구와 라이브러리가 필요합니다.
- 의미와 문맥 상실: BOW는 단어의 순서와 문장 구조를 무시하여 문법 관계와 의미가 상실됩니다. 이 제한으로 인해 문맥, 미묘함 그리고 단어 순서가 중요한 번역이나 복잡한 문장에서의 감정 탐지와 같은 작업에 적합하지 않습니다.
Bag of Words의 사전 크기를 줄이기 위해 사용할 수 있는 다음 전략이 있습니다:
- 대소문자 무시.
- 문장 부호 제거.
- 일반 단어인 the와 a와 같은 불용어 제거.
- 모든 단어가 올바르게 철자되었는지 확인.
- 어간 추출 기술을 사용하여 단어를 뿌리 형태로 줄임.
다음 단계: Bag of Words를 넘어
Bag of Words 모델의 한계 중 하나는 모든 단어를 동일하게 다루는 점입니다. 불幸히도 이는 일부 단어가 빈도가 높아지기 때문에 더 큰 중요성을 가지게 될 수 있습니다.
TF-IDF(용어 빈도- inverses 문서 빈도)는 이 문제를 해결할 수 있는 방법으로, 모든 문서에서 단어가 나타나는 빈도에 따라 단어의 중량을 조정합니다.
TF-IDF: Bag of Words의 확장
용어 빈도(TF)는 문서에서 용어의 빈도를 나타냅니다. 역수 문서 빈도(IDF)는 여러 문서에서 자주 나타나는 단어의 영향을 줄입니다. TF-IDF 점수는 두 지표를 곱하여 계산됩니다.
文档中包含200个单词,其中单词”love”出现了5次。那么”love”的TF(词频)为(5 / 200) = 0.025。假设我们有100万个文档,而单词”love”在其中1000个文档中出现,那么逆文档频率(IDF)计算为log(1000000 / 1000) = 3。TF-IDF权重是这两个量的乘积:0.025 * 3 = 0.075.
在Scikit-learn中,使用TfidfVectorizer类来计算这个值相对简单。
from sklearn.feature_extraction.text import TfidfVectorizer # 示例语料库 corpus = [ "Python is amazing and fun.", "Python is not just fun but also powerful.", "Learning Python is fun!", ] # 创建Tf-idf向量化器 tfidf_vectorizer = TfidfVectorizer() # 对语料库进行拟合和转换 X_tfidf = tfidf_vectorizer.fit_transform(corpus) # 显示词汇表 print("Vocabulary:", tfidf_vectorizer.get_feature_names_out()) # 显示TF-IDF矩阵 print("TF-IDF Representation:") print(X_tfidf.toarray())
输出:
Vocabulary: ['also' 'amazing' 'and' 'but' 'fun' 'is' 'just' 'learning' 'not' 'powerful' 'python'] TF-IDF Representation: [[0. 0.57292883 0.57292883 0. 0.338381 0.338381 0. 0. 0. 0. 0.338381 ] [0.40667606 0. 0. 0.40667606 0.24018943 0.24018943 0.40667606 0. 0.40667606 0.40667606 0.24018943] [0. 0. 0. 0. 0.41285857 0.41285857 0. 0.69903033 0. 0. 0.41285857]]
上述实现的TF-IDF-IDF矩阵为您提供了加权度量,而不是原始频率。
Bag of Words 모델은 특히 더 크고 복잡한 데이터셋에서 제한이 있지만, 여전히 많은 NLP 응용 프로그램에서 필수적인 기본 블록입니다. 이를 이해하면 워드 임베딩과 트랜스포머와 같은 더 고级的 모델을 탐구할 때 도움이 될 것입니다.
이제 여기서 BoW를 프로젝트에 실험할 수 있으며, 스팸 검출, 감정 분석, 문서 클러스터링 등을 포함합니다.
Bag of Words를 넘어 더 나은 개선을 원한다면, 워드2벡터와 그로브와 같은 방법이나 BERT와 같은 딥러닝 모델을 탐구할 수 있습니다.
마지막 생각
Bag of Words 기법은 자연어 처리에서 사용되는 기본 기법입니다. 이는 구조화되지 않은 텍스트를 기계 학습 알고리즘에 사용할 수 있는 수치적 특성으로 변환하는 간단하면서도 효과적인 방법입니다. 이 튜토리얼에서는 다음을 다루었습니다:
- Bag of Words(BoW) 모델이 무엇인가?
- Bag of Word 모델이 기계 학습 모델을 구축하는 데带来的 이점.
- Python에서 Bag of Words 모델을 어떻게 구현하는지.
- Bag of Words의 장점과 제한.
- Bag of Words 모델의 이론과 동기.
- 傳統적인 Bag of Words 접근 방식을 개선한 TF-IDF를 소개.
자연어 처리에 깊이 다루는 우리의 Python으로 자연어 처리 기술 트랙를 확인해 보세요.
Source:
https://www.datacamp.com/tutorial/python-bag-of-words-model