본문 바로가기

머신러닝,딥러닝/자연어처리(NLP)

사이킷런 TFIDF 와 코사인유사도 로 문서 유사도 구하기

이번 시간에는 위키 데이터를 이용해서 문서 유사도를 구해보겠습니다. 

scikit-learn 의 TFIDF API 를 써서 구해보겠습니다. 

train 시키는 건 간단한데 이걸 저장하고 로드하는법도 알면 편합니다 :)

 

위키에서 문서를 크롤링 한뒤, 

정규표현식을 써서 한글,숫자,영어 이외에는 제거했습니다. 

일본어나 한자 같은게 들어가 있는 경우도 있는데 중요한 feature 라고 보기 힘들다고 생각했기 때문입니다.

 

 

 

 

그리고 mecab 을 활용해서 토큰나이저를 따로 만들었습니다. 

mecab 사전을 보면 나와있는 태그들을 보고 명사,동사,형용사 등의 말만 추출하도록 만들었고, 

한 음절로 되는 텍스트들은 정보가치가 떨어질 것으로 판단했습니다.

그리고 별도로 불용어 100개 사전을 참고했습니다. (출처는 맨 아래 있으니 참고하시면 됩니다)

 

 

def tokenizer(raw, pos=["NNG","NNP","VV","VA"], stopword=stopwords):
    
    from konlpy.tag import Mecab
    m = Mecab()
    # 길이가 1 이하인 토근은 제외, 위에서 지정한 (mecab 사전에 따른) 토큰들만 특징으로 삼기, 불용어 100개 제외  
    return [word for word, tag in m.pos(raw) if len(word) > 1 and tag in pos and word not in stopword ]

 

 

그리고 사이킷런의 tfidf vector 를 import 한 뒤에 vectorize 를 아래와 같이 설정했습니다. 

아래의 코드는 위 변수명에 맞춰서 바꾼거구요.

별도로 원래의 코드에서는 별도로 함수랑 클래스 만들어서 인자로 집어넣었습니다 :)

 

 

vectorize = TfidfVectorizer( stop_words= stopwords, tokenizer= tokenizer, 
    ngram_range=(1,3), min_df=2, sublinear_tf=True )

 

 

그리고 위에서 사용한 벡터를 인스턴스화 하여 살펴보면 

csr_matrix 형태인 것을 알 수 있습니다. 

csr_matrix 는 coo_matrix 에서 메모리 효율을 위해 개선된 희소행렬입니다. 

 

 

csr_matrix 관련해서는 

https://lovit.github.io/nlp/machine%20learning/2018/04/09/sparse_mtarix_handling/ 

 

Scipy sparse matrix handling

벡터는 행렬로 표현할 수 있습니다. Distributed representation 처럼 벡터의 대부분의 값이 0 이 아닐 경우에는 numpy.ndarray 와 같은 double[][] 형식으로 벡터를 저장합니다. Row 는 각 entity, column 은 벡터 공간에서의 각 차원에 해당합니다. 이와 반대로 sparse matrix 는 벡터의 많은 값들이 0 입니다. 대부분의 값이 일정하다면 그 값이 아닌 다른 값들만을 메모리에 저장하면 메모리를 효율적

lovit.github.io

를 참고하면 좋습니다 :) 

 

 

여기서 csr_matrix 객체를 저장하고 로드하는 게 중요합니다. 

 

from scipy.io import mmwrite, mmread

를 활용해서 해당 객체를 저장하고 로드할 수 있습니다.

pickle 을 써도...음...상관없을거 같긴한데...정확히는 모르겠네요. 

 

# csr_matrix 저장 coo, doc 도 저장 가능 
mtx_path = 'name.mtx'
# 새로 학습한 tfidf vector 를 csr_matrix 형태로 저장 
mmwrite(mtx_path, X)

 

 

저장을 했으니 다시 로드해보겠습니다. 

이 때, csr_matrix 로 저장했으나 로드하면 coo_matrix 입니다. 

그러니 tocsr() 메서드로 바꿔주시면 됩니다. 

이터러블 객체를 iter() 써서 이터레이터로 바꾸는 느낌이죠! 

 

# csr_matrix 객체 로드 
wiki_tdm = mmread(mtx_path)
wiki_tdm = wiki_tdm.tocsr()

 

이제 새로운 데이터를 하나 input 를 써서 넣고 변수로 할당해주세요. 

그리고 9천개의 위키문서로 학습한 tfidf의 기존 feature 를 추출하겠습니다. 

 

features = test_vector.vectorize.get_feature_names() 

 

새 데이터: ' 컴퓨터가 인간의 언어를 알아들을 수 있게 만드는 학문분야. 인공지능의 하위 분야로, 일반적인 인공지능을 만들려던 1960년대의 시도가 실패한 후[1], 인간의 언어를 분석하고 해석하여 처리하는 인공지능이 세분화되면서 생긴 학문 분야. 흔히 우리가 아는 말하는 컴퓨터 및 인간과 대화하는 컴퓨터 관련 기술이 이 쪽에 속한다. 언어공학, 컴퓨터과학, 인공지능, 전산언어학(Computational Linguistics)의 연구 분야이며, 자연어를 컴퓨터로 해석하고, 의미를 분석하여 이해하고, 자동으로 생성하는 것 등에 관련된 분야다. 이 분야의 하위 분류로 정보 추출, 자동 교정, 대화 시스템, 기계 번역 등이 있다. 자연 언어(또는 자연어, Natural Language)란 프로그래밍 언어와 같이 사람이 인공적으로 만든 언어가 아닌, 사람이 일상생활과 의사소통에 사용해 온, 한국어, 영어와 같이 오랜 세월에 걸쳐 자연적으로 만들어진 언어라는 의미로, 우리가 흔히 말하는 언어를 뜻한다. 굳이 자연 언어라고 언급하는 까닭은 컴퓨터공학에서 언어라고 하면 우선적으로 C언어나 Java 같은 프로그래밍 언어를 먼저 떠올리기 때문. 사실 C의 등장은 기존 패러다임에 비해서 그야말로 자연언어에 가까운 형식을 취한 혁신이었다. 변수와 함수, 패러미터의 조합은 어셈블리 수준의 코딩 체계에서 생판 남도 보고 해석 가능한 형식을 띄었기 때문. 하지만 어디까지나 코딩 분야에 한정된 부분이고, 그나마도 지금의 현대인이 궁극적으로 추구하는 자연 언어에 비하면 한참 수준이 낮은 것이므로 자연 언어 얘기가 나온 자리에 코딩을 끼워넣어 갑분싸 하게 하면 안 될 것이다. 자연 언어에 대한 연구는 오래전부터 이어져 오고 있음에도 2018년에 들어서도 아직 컴퓨터가 자연 언어를 사람처럼 이해하지는 못한다. 대신, 언어에 대한 깊은 이해없이 피상적인 확률 및 통계를 이용하여 대량의 정보를 처리하는 기술은 많이 발전한 상태. 대표적인 예를 들자면, 구글로 대표되는 검색 엔진들. 검색 엔진은 인간의 언어를 깊이 이해하지 않고, 단어간의 통계적 유사성에 바탕을 두고 문서를 검색해낸다. 소위 딥 러닝을 통한 '가끔 실수도 하고 그러는' 인간 같은 수준의 인공지능 개발이 선행되지 않는다면 완벽한 자연 언어 구사 또한 요원한 일이다. NLP 시스템을 테스트하는 방법으로 유명한 것 중 하나로 튜링 테스트가 있다. '

 

(사진이 잘 안보여서 첨부)

나무위키에서 자연어처리에 대한 문서 일부를 new data 로 넣었습니다. 

 

이를 가지고 기존 데이터의 특성을 가지고  토큰화 하겠습니다. 

컴프리헨션을 써서 아래처럼 만들면 됩니다. 

먼저 mecab 을 활용한 토큰화를 먼저하고 앞에서 학습한 tfidf 가 갖고 있는 30만개의 feature 를 가지고 들어있는지 유무를 통해 

토큰화를 완료합니다. 

 

srch = [ t for t in test_token.tokenizer(new_data) if t in features]

 

 

 

이제 코사인 유사도를 구할 수 있도록 벡터화 해보겠습니다. 

 

앞에서 썼던 vectorize 를 가져오고 train 된 tfidf 로 transform 해줍니다.

기존 train 한 tfidf 벡터의 feature 를 가지고 matrix 가 만들어졌습니다. 

이제 linear_kernel 을 통해서 코사인 유사도를 구해주고 argsort 로 정렬합니다. 

 

 

srch_vector = test_vector.vectorize.transform([new_data])

cosine_similar = linear_kernel(srch_vector, X).flatten()
sim_rank_idx = cosine_similar.argsort()[::-1]

 

 

자, 그러면 이제 새로 넣은 문서와 유사한 문서가 어떤게 있는지 보겠습니다. 

 

 

 

글이 잘 안보여서 좀 확대해보겠습니다. 

 

 

코사인 유사도는 둘 다 0.15 전후 인데 

생각보다 유사한 주제의 문서들이 나온 것을 확인할 수 있었습니다. 

 

만약, 0.6 이상의 유사도 값이 나온다면 좀 더 유사한 문서가 나올거 같습니다 :)

 

이상 사이킷런의 tfidf 와 코사인 유사도를 이용한 문서 유사도를 구해보았습니다. 

감사합니다 :) 

 

 

 

 

--참고-- 

 

https://www.kaggle.com/mattwills8/fit-transform-and-save-tfidfvectorizer

 

https://lovit.github.io/nlp/2018/03/26/from_text_to_matrix/ 

 

https://scikit-learn.org/stable/modules/generated/sklearn.feature_extraction.text.TfidfVectorizer.html 

 

https://chan-lab.tistory.com/27 

 

https://bab2min.tistory.com/544