728x90

개요

대화체 문장간의 유사도를 구하여 추천 알고리즘으로 만들기 위한 목적으로 스터디 하고 있습니다.

임베딩는 크게 단어임베딩 VS 문장임베딩으로 나눌수가 있습니다.

단어임베딩은 각 단어에 대해서 vector 진행 후 유사도를 구하는데 이에 단점은 문맥을 고려 하지 않는다는 점이다. 문맥을 포함한 임베딩을 만들기 위해서 문장임베딩이 사용이 되는데, 여기에는 Doc2vec 혹은 Transformer과 같은 딥러닝의 임베딩을 예로 들수가 있다.

 

필자는 Word2vec(단어임베딩)를 사용하여 문장 전체 임베딩을 구하는 sentence2vec을 사용하여 실험해 보았습니다.

자세한 코드 내용은 https://github.com/stanleyfok/sentence2vec 에서 볼수 있습니다.

사용된 데이터는 유튜브 댓글임을 알려드립니다.

 

코드

import re
import numpy as np
from numpy import dot
from numpy.linalg import norm
from gensim.models import Word2Vec
from nltk import word_tokenize


class Sentence2Vec:
    def __init__(self, model_file):
        self.load(model_file)

    def load(self, model_file):
        self.model = Word2Vec.load(model_file)

    def get_vector(self, sentence):
        # convert to lowercase, ignore all special characters - keep only
        # alpha-numericals and spaces
        sentence = re.sub('[^ ㄱ-ㅣ가-힣]+', '', str(sentence))

        vectors = [self.model.wv[w] for w in word_tokenize(sentence)
                   if w in self.model.wv]

        v = np.zeros(self.model.vector_size)

        if (len(vectors) > 0):
            v = (np.array([sum(x) for x in zip(*vectors)])) / v.size

        return v

    def similarity(self, x, y): #코사인 유사도 
        xv = self.get_vector(x)
        yv = self.get_vector(y)

        score = 0

        if xv.size > 0 and yv.size > 0:
            score = dot(xv, yv) / (norm(xv) * norm(yv))

        return score

문장 벡터를 구하기 위한 Class를 선언 합니다. def similarity 함수에서 문장간의 유사도를 구할수 있습니다.

 

 

import pandas as pd
import re
import logging
import nltk
from nltk import word_tokenize
from nltk.corpus import stopwords
from gensim.models import Word2Vec
nltk.download('stopwords')
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s',
                    level=logging.INFO)

def cleanData(documents):
    # for i,document in enumerate(documents):
        
        
        #(), [] 패턴 삭제
        # document = re.sub('\[[a-zA-Z0-9가-힣-=+’:\'./!★\s]{0,60}\]|\([a-zA-Z0-9가-힣-=.\'_/‘’:眞&?★·!,\s]{0,60}\)',"",document)
        
        # #한글 및 띄어쓰기만 남기고 제거
    document = re.sub('[^ ㄱ-ㅣ가-힣]+',"",str(documents))
        #특수문자 제거
        # document = re.sub('[★◆●▶♥●◆!@#$-=+,#/\?:^$.@*\"※~&%ㆍ!』\\‘|\(\)\[\]\<\>`\'…》;:?↑→‘’]',"",document)  
        
    return document

df = pd.read_csv('./testing_csv_raw.csv')

# drop duplicate rows
df = df.drop_duplicates(subset='string')

# clean data
df['string'] = df['string'].map(lambda x: cleanData(x))
# print('strings',df['string'] )

# get array of strings
strings = df['string'].values.tolist()

# tokenize the each string
tok_strings = [word_tokenize(string) for string in strings]

# refer to here for all parameters:
# https://radimrehurek.com/gensim/models/word2vec.html
model = Word2Vec(tok_strings, sg=1, vector_size=100, window=5, min_count=5, workers=4,
                 epochs=100)

# save model to file
model.save('./model/utube_strings.model')

문장 정제 후 word_tokenize로 문장내 단어간의 분리를 한뒤 각 Word2Vec, 즉 각 단어 별로 벡터화를 진행해줍니다.

각 단어별로 단어를 구하면 아래 그림처럼 평균으로 만들어서 문장벡터로 변환할수 있습니다.

 

 

해당 코드 결과, "잘 어울려요"와 비슷한 문장을 내림차순으로 정렬한 모습입니다.

제 개인적인 생각으로는 "잘" 이라는 요소 때문에 어울린다는 말과는 별개로 추천되고 있다는 느낌을 받았습니다.

아마도 단어 기반의 벡터로 유사도를 구했기 때문임을 추측해 볼수 있습니다. 결국 댓글과 같은 문맥이 중요한 텍스트는 문장 임베디을 사용하는 것이 유리하다고 판단 됩니다. 

따라서 문장 임베딩 기법 중 Doc2vec에 대하여 스터디 진행해 볼 계획입니다. 

 

728x90

+ Recent posts