ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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의 확장 개념으로, 단어의 상대적 중요도를 반영하여 더욱 정교한 벡터를 생성.
    사이킷런의 CountVectorizerTfidfVectorizer를 활용하면 쉽게 구현 가능.
    BoW와 TF-IDF는 텍스트 분류, 감성 분석, 문서 유사도 분석 등 다양한 NLP 작업에 활용 가능.

    📌 이제 BoW와 TF-IDF를 활용하여 텍스트 데이터를 효과적으로 분석할 수 있습니다! 🚀

     

Designed by Tistory.