ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [자연어처리] Word2Vec 의 모든 것
    자연어처리 공부 2023. 12. 26. 16:19

    ▶︎ Word2Vec 이란? 

    → Neural Network를 활용하여 단어를 벡터화하는 구글에서 개발한 Word Embedding 방법

    → 대규모의 텍스트 corpus에서, 비슷한 의미를 가진 단어들은 비슷한 벡터로 매핑되도록 학습됩니다. 문맥 정보가 중요할 때 사용됩니다.

     

    ▶︎ Word2Vec 의 핵심 아이디어 

    → 비슷한 문맥에서 나타나는 단어들은 비슷한 벡터로 표현되어야 한다

    → 이를 통해 모델이 단어 간의 의미적 유사성을 캡처하고, 단어 간의 유사성을 계산할 수 있게 된다. 임베딩 된 간어의 내적이 코사인 유사도가 되도록 합니다.

     

    ▶︎ "Word2Vec 를 학습한다"는 의미는?

    Word2Vec은 말씀드렸듯이 비슷한 의미를 지닌 단어는 비슷한 공간에 모이도록 만든 상대적인 위치 값입니다. 상대적인 위치값이기 때문에 벡터의 숫자는 특정한 의미를 지니지 않습니다. 같은 단어라도 어떤 문단에서 쓰이는지에 따라 의미가 달라지는 것처럼 기준이 바뀌면 숫자도 바뀝니다. 즉, Word2Vec은 비슷한 의미를 지닌 단어는 비슷한 공간에 모이도록 만든 상대적인 위치 값이고, 이를 학습한다는 것은 각 단어들이 상대적 위치값을 잘 찾아가도록 업데이트하는 것이라고 생각하면 됩니다. 

     

    ▶︎ Word2Vec 의 방법

    CBOW : 문맥 내 주변 단어들을 이용하여 특정(중심) 단어를 예측하는 방식

    Skip-gram : 특정(중심) 단어가 주어졌을 때 주변 단어를 예측하는 방식

    DBOW도 Word2Vec의 일종이지만, 위와는 다르게 주로 문맥 정보를 무시하고 각 단어를 독립적으로 처리하는 방식입니다. 문맥정보가 중요하지 않을 때 사용하면 모델이 복잡하지 않아서 빠르게 훈련이 가능하지만 보통 문맥이 중요한 자연어처리에서는 CBOW나 Skip-gram 를 사용합니다.

    # dbow      : model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=4, dm=0)
    # cbow      : model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=4, sg=0)
    # skip-gram : model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=4, sg=1)

     

     

    AI hub 에서 감성 대화 말뭉치를 사용하였습니다. (https://aihub.or.kr/aihubdata/data/view.do?currMenu=115&topMenu=100&dataSetSn=86)

     

    1. 필요한 라이브러리 불러오기

    from gensim.models import Word2Vec
    from nltk.tokenize import word_tokenize
    import nltk
    import pandas as pd
    import numpy as np
    import os
    import json
    import re
    import multiprocessing
    import os
    import gensim
    from bs4 import BeautifulSoup
    from konlpy.tag import Okt
    from gensim.models import word2vec
    import matplotlib.pyplot as plt
    from matplotlib import font_manager, rc
    from konlpy.tag import Kkma, Komoran, Okt, Hannanum 
    from IPython.display import Image

     

    2. 데이터 가져오기 및 준비

    # 데이터 가져오기
    df_train = pd.read_excel("./018.감성대화/Training_221115_add/원천데이터/감성대화말뭉치(최종데이터)_Training.xlsx")
    df_test = pd.read_excel("./018.감성대화/Validation_221115_add/원천데이터/감성대화말뭉치(최종데이터)_Validation.xlsx")
    df = pd.concat([df_train, df_test], axis=0, ignore_index=True)
    
    del df['Unnamed: 0']
    
    print(df.isna().sum())
    
    df.fillna('', inplace=True)
    df['사람문장'] = df['사람문장1'] + ' ' + df['사람문장2'] + ' ' + df['사람문장3']

     

    3. 한국어 토큰화 및 불용어 제거

    okt = Okt()
    kkma = Kkma()
    komoran = Komoran()
    hannanum = Hannanum()
    
    def morphs_cleaning(tokenizer, column):
        '''
        1. 함수 설명 : input으로 넣은 열을 토큰화하고 불용어 제거, 조사/이모지/문장부호 제거, 한 단어 제거 하는 클렌징 작업을 마친 새로운 데이터프레임과 사용된 형태수의 종류와 갯수를 확인하는 함수
        2. Args : 
            tokenizer (Str)   : 사용하고 싶은 토크나이저 (okt, kkma, komoran, hannanum 중 하나)
            column   (Series) : 형태소 분류하고 싶은 열 이름
        3. Return : 
            cleaned_list (List) : 클렌징 작업을 마친 리스트 형태의 데이터
            tags_counts (Dict)  : 토크나이징된 형태소의 종류와 갯수를 확인하는 딕셔너리
        '''
        
        try:
            cleaned_list = [] # 클렌징된 본문 내용
            tags_counts = {} # 클렌징된 형태소별 사용 빈도
            # 감정분석이므로 중립적인 단어만 빼자
            stopwords = ['하다','있다','같다','너무','되다','그렇다','돼다','나다','때문','스럽다','지금','우리','되어다','이렇다'
                         ,'하나','이다','대해','위해','자기','그렇게','수가','두다','진짜','정도','이렇게','내다'
                         ,'내','들','것','나','수','게','거','다','때','이','적','그','응','난','걸','건','곳','후','님','데','중']
            for idx in range(len(column)):
                #- 토큰화, 품사 태깅
                # stem은 아름다운-> 아름답다
                # norm ~하게 -> 하다
                raw_pos_tagged = tokenizer.pos(column[idx], norm=True, stem=True)
                
                # 불용어 제거
                word_cleaned = []
                for word in raw_pos_tagged: #  ('서울', 'Noun'), ('인천', 'Noun')
                    # Foreign == ”, “ 와 같이 제외되어야할 항목들 
                    # & 한 글자로 이뤄진 단어들을 제외 & 원치 않는 단어들을 제외
                    # 특수 기호에 해당하지 않을 경우
                    
                    if word[1] not in ["Josa", "Eomi", "Punctuation"] and  (word[0] not in stopwords) and re.match('^[a-zA-Z가-힣]+', word[0]): 
                        word_cleaned.append(word[0]) 
                        
                        if word[0] in tags_counts:
                            tags_counts[word[0]] += 1
                        else:
                            tags_counts[word[0]] = 1
    
                word_cleaned = ' '.join(word_cleaned)
                cleaned_list.append(word_cleaned)
                
            
            return cleaned_list, tags_counts
        
        except:
            print('형태소 분류기 또는 열을 다시 입력해주세요. (okt, kkma, komoran, hannanum)')
            
    # 전처리된 열과 사용빈도를 확인하기
    cleaned_list, tags_counts = morphs_cleaning(okt, df['사람문장'])

     

    4. 값을 기준으로 딕셔너리 정렬, 훑어보면서 불용어 사전에 추가하기

    # 값을 기준으로 딕셔너리 정렬, 훑어보면서 불용어 사전에 추가하기
    sorted_dict = dict(sorted(tags_counts.items(), key=lambda item: item[1], reverse=True))
    sorted_dict

     

    5. 전처리한 결과 텍스트 파일로 저장. (꼭 저장할 필요는 없지만, 자연어처리에 쓰이는 데이터는 용량이 크기 때문에 혹시 몰라서 저장!)

    # 전처리한 결과를 텍스트 파일로 저장
    result_file = 'preprocessing_word2vecTest.txt'       # 텍스트 파일임!
    with open(result_file, 'wt', encoding='utf-8') as myfile:
         myfile.write('\n'.join(cleaned_list))           # 라인으로 결합
    
    print(result_file + ' 파일 저장됨!')

     

    6. 모델 만들고 저장

    # 모델 만들기 (skip-gram)
    data = word2vec.LineSentence(source=result_file)
    model = word2vec.Word2Vec(data, vector_size=100, window=5, min_count=1, workers=4, sg=1)
    
    # .model 파일은 이진 파일임
    model_filename = 'word2vec.model'
    model.save(model_filename)
    print(model_filename + ' 모델 저장됨!')
    
    print('-finished-')
    
    # 모델 불러오기
    # model_filename = 'word2vec.model'
    # model = word2vec.Word2Vec.load(model_filename)

     

    7. 유사도 활용해보기

    • similarity(단어1, 단어2) 함수 : 두 단어의 유사도 계산
    print(model.wv.similarity('친구','벗'))
    print(model.wv.similarity('할머니','따돌림'))
    • most_similar(지정단어) 함수 : 지정 단어와 가장 유사한 단어를 출력
    # 모델 사용 예시
    similar_words = model.wv.most_similar('친구', topn=5)
    print(similar_words)
    
    similar_words = model.wv.most_similar('공부', topn=5)
    print(similar_words)
    
    similar_words = model.wv.most_similar('퇴사', topn=5)
    print(similar_words)
    • 유사도를 활용한 시각화
    # model 파일의 유사도를 이용한 시각화
    # 바 그래프 그리기
    def showGraph(bargraph, top_n):
        rc('font', family='AppleGothic')
        plt.rcParams['axes.unicode_minus'] = False
        xtick = [item[0] for item in bargraph]
        ytick = [item[1] for item in bargraph]
        plt.figure()
        plt.bar(xtick, ytick)
        
    # 유사도 구하기
    # 단어와 유사도가 높은 단어 10개를 리스트로 반환
    # - most_similar 함수
    # 학습 데이터에서 top-N 만큼 유사한 단어들을 찾아 줍니다.
    # 코사인 유사도(cosine similarity)를 이용하여 계산합니다.
    # 원소가 tuples인 list 자료 구조를 반환합니다.
    
    bargraph = model.wv.most_similar(positive=['휴가'],topn=10)
    showGraph(bargraph, 10)
    
    plt.show()
    # 학습된 단어 벡터 확인
    embedding_matrix = model.wv.vectors
    
    # 결과 출력
    print("Shape of Embedding Matrix:", embedding_matrix.shape)
    print("Embedding Matrix:\n", embedding_matrix)
    
    out_v = open('vects.tsv', 'w', encoding='utf-8')
    out_m = open('meta.tsv', 'w', encoding='utf-8')
    
    for i in range(len(tags_counts)):
        word = list(tags_counts.keys())[i] 
        embeddings = model.wv[word]
        out_m.write(word + '\n')
        out_v.write('\t'.join([str(x) for x in embeddings]) + '\n')
        
    out_v.close()
    out_m.close()
    
    # Embedding projector https://projector.tensorflow.org/ 를 이용하여 word embedding 시각화
    # embedding layer 의 weight 를 disk 에 write. 
    # Embedding projector 사용을 위해 embedding vector file 과 단어가 들어 있는 meta data file 로 구분하여 upload.

     

    - "친구", "미래" 등의 단어를 검색했을 때, 비슷한 의미를 가진 단어들이 비슷한 벡터 공간에 있는 것을 확인할 수 있다. 엄청 신기하다!

Designed by Tistory.