주제

1. 임베딩(Embedding)
2. 단어 임베딩
3. 문장 임베딩
4.  감정 분석(Sentiment Analysis)
5 . 감정 분석 실습

 

1. 임베딩(Embedding)

  • 컴퓨터가 알아볼 수 있는 숫자의 형태로 변형하는 과정을 뜻함
  • 즉, 텍스트 데이터를 벡터로 변환하는 기술
  • 임베딩의 결과는 벡터이므로 벡터가 존재하는 벡터 공간에 표현이 가능
  • 좋은 임베딩은 비슷한 의미의 단어들이 비슷한 공간에 존재하게 됨

1.1 인코딩(Encoding) vs. 임베딩(Embedding)

  • 인코딩 : 데이터를 표준화한 형식으로 변환 목적. 본질적으로 의미가 변하지 않으며 변환하는데 중점을 둠
  • 임베딩 : 머신 러닝 모델이 처리하기 쉬운 형태로 변환하는 것이 목적. 

1.2 원핫 인코딩(One-Hot Encoding)

  • 특정 단어를 표현하는 위치만 1이고 나머지의 위치는 0으로 구성하는 임베딩 방법
  • 단어의 수만큼의 크기를 갖는 벡터가 생성됨
  • 직관적으로 쉽게 단어를 변환
  • 한계 : 
    • 차원의 저주 
      • 하나의 단어를 표현하는 벡터의 크기는 전체 단어 수와 같기 때문에 차원이 크면 효율성과 계산 복잡도가 증가함
    • 의미 부재
      • 의미적으로 비슷한 단어끼리 비슷한 공간에 존재하지 않을 수 있음
    • 정보의 희소성
      • 특정 단어의 위치만 1이므로 중요한 정보가 매우 희소함(0이 많음)

 

1.3 분산 표현(Distribution Representation)

  • 정수가 아닌 실수로 이루어진 벡터로 임베딩 진행
  • 실수로 단어를 변경하면서 데이터의 의미를 여러 특성에 걸쳐 분산시켜 표현함
  • 다양한 의미와 문맥적 특성을 풍부하게 포착 가능함
  • 임베딩이 잘 된다면 비슷한 의미를 갖는 단어들은 비슷한 분포를 가지게 됨
  • 벡터 공간에 표현하면 비슷한 공간에 표현
  • 수의 집합이므로 의미 차원에서 연산이 가능함

 

2. 단어 임베딩

  • 단어에 원핫 인코딩을 적용하면 문장을 단어의 형태로 분해한다(tokenization)
  • 고유한 단어 집합 생성하고, 고유 단어에 독립된 인덱스를 부여함
  • 나머지 자리는 0으로 채우고 벡터를 생성

 

2.1 학습 기반 임베딩

1. 분포 가설(Distribution Hypothesis)

  • 분포 표현의 이론적 기반이 되는 가설로, "단어의 의미는 그 단어가 나타나는 문맥에 의해서 결정된다"라는 아이디어가 중심
  • 최신 임베딩 기법은 이러한 가설을 기반으로 연구가 됨
  • 특정 단어의 의미를 숫자 벡터로 표현하기 위해 문맥과 주변 단어를 이용해 학습 진행

2. Word2Vec

  • 문장 위를 움직이는 슬라이딩 윈도우를 만든다
  • 해당 윈도우는 이동하면서 동일한 개수의 단어를 포함, 포함된 단어들 사이의 연산을 진행해 각 단어들을 임베딩함
  • 두 가지 방법으로 변환 :
    • CBOW : 이웃한 단어들로 가운데 단어가 무엇인지 예측하는 과정에서 임베딩을 진행
    • Skip-gram : 가운데 단어로 이웃한 단어들을 예측하는 과정으로 임베딩 진행

Word2Vec의 두가지 방법

 

3. GloVe(Global Vectors for Word Representation)

  • 전체 글에 단어 간 공동 출현 통계를 이용해 각 단어의 의미를 벡터로 표현하고 각 단어 쌍이 얼마나 자주 함께 나타나는지를 기록
  • 이것을 공동출현행렬이라고 함
  • 공동으로 자주 출현하는 단어들을 벡터 공간 내 비슷한 위치에 존재하도록 임베딩

 

4. 그 외 딥러닝을 활용한 학습 기반 단어 임베딩

  • BERT : 단어 별 중요도 기반의 모듈을 활용하여 문장 내적/외적 관계를 바탕으로 임베딩
  • CLIP : 이미지를 설명하는 글에서 이미지와 텍스트의 공동 의미를 임베딩에 활용

 

2.2 단어 임베딩 실습

  • 사용할 문장
sentence = "사과는 맛있다 바나나는 맛있다"
  • 문장을 단어로 분리 (띄어쓰기 단위를 사용)
# 문장을 단어로 분리
# 띄어쓰기 단위를 사용
words = sentence.split()
print(words)
  • 단어 배열 생성
# 단어 배열을 NumPy 배열로 변환
words_array = np.array(words).reshape(-1, 1)
print(words_array)
  • 원핫 인코딩 적용
# 원핫 인코딩 적용!
one_hot_encoded = encoder.fit_transform(words_array)
print(one_hot_encoded)
  • Word2Vec 적용
from gensim.models import KeyedVectors
from gensim.downloader import load

def use_word2vec(word, model):
    try:
        word_vector = model[word]
        return word_vector
    except KeyError:
        return "단어가 모델의 어휘에 없습니다."
  • 기본 Word2Vec 모델 불러오기(300단어)
# 제공하는 기본 Word2Vec 모델 불러오기
# 약 10~12분 정도 소요
word2vec_model = load('word2vec-google-news-300')
  • 코사인 유사도를 이용하여 단어의 유사도를 확인할 함수 생성
from scipy.spatial.distance import cosine

def word_similarity(word1, word2, model):
    try:
        vector1 = model[word1]
        vector2 = model[word2]

        similarity = 1 - cosine(vector1, vector2) # 코사인 유사도 계산
        return similarity
    except KeyError as e:
        return str(e)

def most_similar(word, model, topn=5):
    try:
        similar_words = model.most_similar(word, topn=topn) # 가장 유사한 단어 찾기
        return similar_words
    except KeyError as e:
        return str(e)
  • 두 단어 사이의 유사도 확인
# 두 단어 사이의 유사도 확인
print('football & basketball 유사도 : ' , word_similarity('football', 'basketball', word2vec_model))
print('football & airplane 유사도 : ' , word_similarity('football', 'airplane', word2vec_model))

유사도 확인

  • 특정 단어와 가장 유사한 단어 찾기
    • soccer, fooball, Football, basketball, footbal
# 특정 단어와 가장 유사한 단어 보여주기
print('football과 가장 유사한 단어 5개는 : ' , most_similar('football', word2vec_model, topn=5))
  • GloVe 적용
def use_glove(word, model):
    try:
        word_vector = model[word]
        return word_vector
    except KeyError:
        return "단어가 모델의 어휘에 없습니다."
        
        
# 제공하는 GloVe 모델 불러오기
# 약 5분정도 소요
glove_model = load('glove-wiki-gigaword-300')

# 두 단어 사이의 유사도 확인 (GloVe)
print('football & basketball 유사도 : ' , word_similarity('football', 'basketball', glove_model))
print('football & airplane 유사도 : ' , word_similarity('football', 'airplane', glove_model))

GloVe의 유사도 계산 결과

 

3. 문장 임베딩

3.1 단어 임베딩과 문장 임베딩 차이

  • 문장 임베딩 : 문장 자체를 숫자의 형태로 변환
    • 단어를 넘어 문장 자체가 갖고 있는 의미를 벡터로 표현함
  • 단어 임베딩과 문장 임베딩은 서로 다른 목적과 사용 사례를 기반으로 개발

 

3.2 문장에 원핫 인코딩 적용

  • 각 단어에 독립된 인덱싱 진행
  • 문장에 소속된 각 단어를 해당 단어의 인덱스 위치에 1을 부여하고 나머지를 0으로 채움
  • 예시) 사과는 = 0, 바나나는 = 1, 맛있다 = 2
    • 사과는 맛있다 → [1, 0, 1]
    • 바나나는 맛있다 → [0, 1, 1]

 

3.3 단어 임베딩을 활용한 문장 임베딩

  • 각 단어들의 임베딩을 이용해 문장의 임베딩을 생성
  • 각 단어의 임베딩의 평균을 활용
  • TF-IDF를 활용한 단어 가중치를 적용
    • TF-IDF : 문장 내 단어의 중요도를 나타내는 척도
    • 가중된 값들을 활용해 평균 값 활용
  • 문장 임베딩은 고차원적인 연산이 필요함 → 딥러닝 기법을 활용하는 과정에서 발전이 됨
  • 딥러닝 모델이 단어를 임베딩하는 과정에서 문장을 구성하는 각 단어에서 정보를 공급받아 전체 문장의 의미를 담는 벡터를 생성한다

 

3.4 문장 임베딩 실습

  • CountVectorizer 사용
    • 텍스트 데이터를 단어의 빈도로 변환하는 객체
    • binary = True이면 빈도가 2 이상이어도 1로 표현함
    • binary = False이면 BoW(Bag of Words)로 사용. 즉, 빈도만큼 표현함
from sklearn.feature_extraction.text import CountVectorizer

# 문장 리스트
sentences = ["사과는 맛있다",
             "바나나는 맛있다",
             "딸기는 맛있다",
             "김치는 맵다",
             "짜장면은 달다",
             "소고기는 좋다"]
             
vectorizer = CountVectorizer(binary=True)
vectorizer.fit(sentences)
# 단어-인덱스 매핑
word_to_index = vectorizer.vocabulary_
print("단어-인덱스 매핑:", word_to_index)

단어 매핑 결과

  • 문장을 벡터로 변환
# 문장을 벡터로 변환
input_sentence = ["소고기는 맛있다"]
# input_sentence = ["소고기는 소고기는 소고기는 맛있다"]
sentence_vectors = vectorizer.transform(input_sentence).toarray()
print(f"입력 문장 '{input_sentence[0]}'의 원핫 임베딩 벡터 :", sentence_vectors[0])

벡터 변환 결과

  • CountVectorizer의 binary 인자를 False로 두면 : 
# input_sentence = ["소고기는 맛있다"]
input_sentence = ["소고기는 소고기는 소고기는 맛있다"]
sentence_vectors = vectorizer.transform(input_sentence).toarray()
print(f"입력 문장 '{input_sentence[0]}'의 원핫 임베딩 벡터 :", sentence_vectors[0])

빈도수만큼 표현

 

4.  감정 분석(Sentiment Analysis)

  • 텍스트에서 작성자의 감정 상태나 태도를 파악하고 분류하는 과정
  • 텍스트 마이닝과 자연어 처리(NLP) 분야에 속함
  • 일반적인 기본 범주 : 
    • 긍정적(Positive)
    • 부정적(Negative)
    • 중립적(Neutral)

 

4.1 감정 분석의 응용

  • 소셜 미디어 감성 모니터링
    • 플랫폼 내 게시글 분석해 대중의 감정과 태도 파악
    • 특정 사건, 제품, 브랜드, 정치적 이슈 등에 대한 대중의 반응을 모니터링
    • 마케팅 효과를 분석
  • 고객 서비스 분석 및 소비자 인사이트
    • 고객 서비스 대화, 콜 센터 통화 내용, 이메일 등 분석으로 고객의 만족 파악
    • 고객의 불만을 해결하기 위한 인사이트 제공
  • 헬스 케어 및 의료
    • 환자의 감정 상태 분석
    • 우울증, 불안 장애 등 조기 징후 파악

 

4.2 텍스트 데이터 전처리

  • 전처리 과정 : Tokenize → Stop Words 제거 → Stemming
  • Tokenize
    • 원문 글을 분석에 사용할 기본 개념 단위로 분리하는 과정
    • 토큰의 정의는 문제에 따라 사용자가 정의하기 나름
    • 이 단위가 분석 과정의 가장 작은 의미 단위가 됨
  • Stop Words 제거
    • 개념 단위로 나뉜 개체에서 의미가 없는 개체를 제거하는 과정
    • 일반적으로 stop words는 미리 사전 정의하고 해당 단어가 나오면 제거하는 형태
    • 한글에서는 '그리고', '아', '내가' 등
    • 영어의 경우 'the', 'a', 'and' 등
  • Stemming
    • 단어 기반의 토큰을 사용하는 경우 특정 단어를 그것의 기본 형태(어간)으로 축소하는 과정
    • 접두사나 접미사를 제거하여 단어의 기본 줄기(stem)를 찾는 과정
      • Running → Run
      • Runner → Run
    • 문맥을 고려하지 않아 잘못된 결과를 반환할 수 있음
  • Lemmatization
    • 단어를 그 의미론적 기본 형태(Lemma)로 변환하는 과정
    • 특정 단어의 품사와 문맥을 고려해 정확한 기본 형태를 찾아냄
    • Stemming에 비해 복잡하고 시간이 오래 걸리지만 정확도가 좋음
  • 딥러닝 기반 전처리
    • 전통적인 방식에서 중요하게 생각했던 처리 과정을 일부 변형하여 사용

 

5 . 감정 분석 실습

  • 사용 데이터 : 케글의 Yelp 리뷰 데이터(데이터셋)
  • 레스토랑 리뷰 등록 플랫폼으로, 데이터 상 만족도가 1이면 만족 / 0이면 불만족
  • 분류 클래스의 비율은 1:1로 동일 (총 1000개의 데이터)
  • 문제 정의 : 사용자의 텍스트 리뷰를 바탕으로 그들의 감정 상태를 예측

전처리

  • 데이터 탐색

데이터의 생김새

  • 전처리 함수 생성 (모두 소문자로 변환, 표현식 제거, 띄어쓰기로 tokenization, stop words 제거)
# 최종 함수로 표현
import nltk
from nltk.corpus import stopwords

nltk.download('stopwords')
stop_words = set(stopwords.words('english'))

def preprocessing(text) :
    text = text.lower()
    text = re.sub(r'\W', ' ', text)
    text = text.split()
    text = [t for t in text if t not in stop_words]
    return text
  • 전체 리뷰 전처리
# 기존 데이터 프레임 업데이트
# apply 함수 사용
data['preprocessed'] = data['text'].apply(preprocessing)
data.head()

전처리된 모습까지 추가

 

문장의 임베딩 구하기

  • GloVe 적용
from gensim.downloader import load

# 너무 큰 차원은 시간이 오려걸려 100 차원으로 로드 진행
# 약 2분 소요
glove = load('glove-wiki-gigaword-100')

preprocessed_word = data['preprocessed'][0]

예시로 wow의 적용 결과만 확인

 

  • TF-IDF 적용
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer()

tfidf_matrix = vectorizer.fit_transform([' '.join(doc) for doc in data['preprocessed']]) # 모든 문장을 모아 하나의 큰 문서 덩어리를 생성
tfidf_feature_names = vectorizer.get_feature_names_out()
  • wow가 어느 위치에 있는지 확인
import numpy as np
np.where(tfidf_feature_names == 'wow')[0][0]

1889번째 위치에 존재

  • 각 토큰의 TF-IDF 확인
for word in preprocessed_word :
    doc_idx = 0 # 0 : 원래 문서의 0번째 문장이므로
    word_idx = np.where(tfidf_feature_names == word)[0][0] # 단어 집합으로부터 word의 위치를 찾아냄

    value = tfidf_matrix.toarray()[doc_idx][word_idx]
    # toarray() : tf-idf matrix가 0이 많은 sparse matrix여서 이를 원래 0과 숫자값으로 이루어진 형태로 변환

    print(f'{word}의 tf-idf 값 : {value:.4f}')

각 토큰의 중요도

  • 위 과정을 하나의 함수로 표현 :
import numpy as np

def sentence_embedding(tfidf_matrix, tfidf_feature_names, doc, doc_idx):
    embeddings = []
    for word in doc:
        # GloVe에서 학습한 데이터와 tf-idf에서 학습한 데이터만 취급
        # 그렇지 않은 데이터는 무시됨
        if word in glove and word in tfidf_feature_names:
            # 여기서는 transform을 쓰지 않고 이미 학습한 matrix에서 indexing으로 가져옴
            # 속도 효율성이 높음
            # 만약 처음 보는 문장에 대해서 TF-IDF를 한다면 transform이 필수!
            word_idx = np.where(tfidf_feature_names==word)[0][0]
            tfidf_weight = tfidf_matrix.toarray()[doc_idx, word_idx]

            embeddings.append(glove[word] * tfidf_weight)
    return np.mean(embeddings, axis=0) if embeddings else np.zeros(100)  # GloVe 차원에 맞춰 조정
  • 문장 임베딩 진행하여 임베딩 값을 새로운 열로 저장
# 문장 임베딩 값을 새로운 열로 저장
# 여러 입력을 넣어주기 위해 lambda 함수를 활용하고
# 특정 행을 의미하는 row를 이용해 행 번호를 넣어줌 (row.name)
data['sentence_emb'] = data.apply(lambda row: sentence_embedding(tfidf_matrix,
                                                                 tfidf_feature_names,
                                                                 row['preprocessed'],
                                                                 row.name), axis=1)
data.head()

임베딩값이 추가된 모습

 

로지스틱 회귀 모델 학습 및 평가

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

X = np.stack(data['sentence_emb'].values)
y = np.stack(data['sentiment'].values)

# 훈련 데이터와 테스트 데이터로 분할
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 로지스틱 회귀 모델 생성 및 학습
model = LogisticRegression()
model.fit(X_train, y_train)

# 학습 결과 확인을 위해 검증 데이터 추론 진행
predictions = model.predict(X_test)
  • 성능 확인

성능 평가

  • 정확도 등의 평가 지표 확인

성능 평가 지표

 

TF-IDF 란?

  • 각 단어마다 중요도를 나타내는 수치값
  • TF 파트와 IDF 파트의 곱으로 계산
  • TF(Term Frequency, 단어의 빈도) :
    • 특정 단어가 문서 내에 얼마나 자주 등장하는지를 나타내는 빈도

TF 계산식

  • IDF(Inverse Document Frequency, 역 문서 빈도) :
    • 특정 단어가 얼마나 여러 문서에서 등장하는지
    • 모든 문서에 자주 등장한 단어 : 중요도가 낮음
    • 특정 문서에서만 자주 등장한 단어 : 중요도가 높음

IDF 계산식

  • D는 전체 문서를 의미
  • TF-IDF 계산은 TF와 IDF값을 곱해서 산출
  • 이 값은 문서 d 안에서 단어 t가 갖는 상대적 중요도
  • 높은 TF-IDF 값을 갖는 단어는 해당 문서에서 더 많은 정보를 제공
  • 전체 문서 집합 D보다 의미 있는 특징을 갖고 있음

+ Recent posts