자연어(텍스트)를 계산 가능한 언어로 인코딩하기

자연어처리(NLP)를 위해서는 텍스트 데이터를 계산 가능한 언어로 바꿔야 한다. 즉, 숫자 벡터로 바꿔, 문서를 벡터 공간에 표현할 수 있다.

텍스트 데이터를 숫자 벡터로 바꾸는 '인코딩'에는 여러 가지 방법이 있다.

가장 간단한 방법이 문서 집합에서 단어 토큰을 생성하고 각 단어의 수를 세어 BOW(Bag of Words) 인코딩 벡터를 만드는 것이다. 사이킷런의 CountVectorizer 클래스를 사용하면 쉽게 할 수 있다.

이 방법에서 한 단계 나아간 방법이 TF-IDF를 이용해 단어 가중치까지 반영하는 방법이 있다.

TF-IDF는 Term Frequency - Inverse Document Frequency)로,

  • 단어 가중치 기법 중 가장 일반적으로 알려진 알고리즘이다.
  • 특정 문서 내에서 단어 빈도가 높을수록, 전체 문서들에는 그 단어를 포함한 문서가 적을수록 TF-IDF 값이 높아진다.
  • 이 값을 이용해 모든 문서에 나타나는 흔한 단어들을 걸러내며, 특정 단어가 가지는 중요도를 측정하는 데 사용된다.
from google.colab import drive
drive.mount('/content/drive')
Mounted at /content/drive
with open('/content/drive/MyDrive/data/test_text.txt', 'r') as f:
  corpus = f.read()
corpus
'Let me add some English sentences too!'
from sklearn.feature_extraction.text import TfidfVectorizer

text = ['토마스 만은 평의원이며 곡물 상인이었던 토마스 요한 하인리히 만과 율리아 다 실바 브룬스 부부 사이에서 두 번째 아들로 독일의 뤼베크에서 태어났다.', # Doc[0] 
        '어머니 율리아는 7살 때 독일로 망명한 부분적 독일계 브라질리안이다.', # Doc[1] 
        '토마스 만의 아버지가 1891년에 돌아가시면서 회사는 청산되었다.', # Doc[2]
        '1893년 뮌헨으로 이주하여 보험 회사의 견습 사원이 되었다.', # Doc[3]
        '이때 첫 작품 <호의>가 잡지에 실리면서 문단에 데뷔하였다.'] # Doc[4]


tfidf_vectorizer = TfidfVectorizer() # TF-IDF 객체 선언
tfidf_vectorizer.fit(text) # 단어를 학습시킴
tfidf_vectorizer.vocabulary_ # 단어사전을 출력
{'1891년에': 0,
 '1893년': 1,
 '7살': 2,
 '견습': 3,
 '곡물': 4,
 '데뷔하였다': 5,
 '독일계': 6,
 '독일로': 7,
 '독일의': 8,
 '돌아가시면서': 9,
 '되었다': 10,
 '뤼베크에서': 11,
 '만과': 12,
 '만은': 13,
 '만의': 14,
 '망명한': 15,
 '문단에': 16,
 '뮌헨으로': 17,
 '번째': 18,
 '보험': 19,
 '부부': 20,
 '부분적': 21,
 '브라질리안이다': 22,
 '브룬스': 23,
 '사원이': 24,
 '사이에서': 25,
 '상인이었던': 26,
 '실리면서': 27,
 '실바': 28,
 '아들로': 29,
 '아버지가': 30,
 '어머니': 31,
 '요한': 32,
 '율리아': 33,
 '율리아는': 34,
 '이때': 35,
 '이주하여': 36,
 '작품': 37,
 '잡지에': 38,
 '청산되었다': 39,
 '태어났다': 40,
 '토마스': 41,
 '평의원이며': 42,
 '하인리히': 43,
 '호의': 44,
 '회사는': 45,
 '회사의': 46}
sorted(tfidf_vectorizer.vocabulary_.items()) # 단어사전 정렬
[('1891년에', 0),
 ('1893년', 1),
 ('7살', 2),
 ('견습', 3),
 ('곡물', 4),
 ('데뷔하였다', 5),
 ('독일계', 6),
 ('독일로', 7),
 ('독일의', 8),
 ('돌아가시면서', 9),
 ('되었다', 10),
 ('뤼베크에서', 11),
 ('만과', 12),
 ('만은', 13),
 ('만의', 14),
 ('망명한', 15),
 ('문단에', 16),
 ('뮌헨으로', 17),
 ('번째', 18),
 ('보험', 19),
 ('부부', 20),
 ('부분적', 21),
 ('브라질리안이다', 22),
 ('브룬스', 23),
 ('사원이', 24),
 ('사이에서', 25),
 ('상인이었던', 26),
 ('실리면서', 27),
 ('실바', 28),
 ('아들로', 29),
 ('아버지가', 30),
 ('어머니', 31),
 ('요한', 32),
 ('율리아', 33),
 ('율리아는', 34),
 ('이때', 35),
 ('이주하여', 36),
 ('작품', 37),
 ('잡지에', 38),
 ('청산되었다', 39),
 ('태어났다', 40),
 ('토마스', 41),
 ('평의원이며', 42),
 ('하인리히', 43),
 ('호의', 44),
 ('회사는', 45),
 ('회사의', 46)]
tf_idf_array = tfidf_vectorizer.transform(text).toarray() # TF-IDF를 이용해 단어 가중치가 적용된 숫자 벡터를 어레이 형태로 보자 
tf_idf_array
array([[0.        , 0.        , 0.        , 0.        , 0.22585586,
        0.        , 0.        , 0.        , 0.22585586, 0.        ,
        0.        , 0.22585586, 0.22585586, 0.22585586, 0.        ,
        0.        , 0.        , 0.        , 0.22585586, 0.        ,
        0.22585586, 0.        , 0.        , 0.22585586, 0.        ,
        0.22585586, 0.22585586, 0.        , 0.22585586, 0.22585586,
        0.        , 0.        , 0.22585586, 0.22585586, 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.22585586, 0.36443818, 0.22585586, 0.22585586, 0.        ,
        0.        , 0.        ],
       [0.        , 0.        , 0.35355339, 0.        , 0.        ,
        0.        , 0.35355339, 0.35355339, 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.35355339, 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.35355339, 0.35355339, 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.35355339, 0.        , 0.        , 0.35355339,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        ],
       [0.38775666, 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.38775666,
        0.        , 0.        , 0.        , 0.        , 0.38775666,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.38775666, 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.38775666,
        0.        , 0.31283963, 0.        , 0.        , 0.        ,
        0.38775666, 0.        ],
       [0.        , 0.35355339, 0.        , 0.35355339, 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.35355339, 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.35355339, 0.        , 0.35355339,
        0.        , 0.        , 0.        , 0.        , 0.35355339,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.35355339, 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.35355339],
       [0.        , 0.        , 0.        , 0.        , 0.        ,
        0.37796447, 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.37796447, 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.        , 0.        , 0.37796447, 0.        , 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.        ,
        0.37796447, 0.        , 0.37796447, 0.37796447, 0.        ,
        0.        , 0.        , 0.        , 0.        , 0.37796447,
        0.        , 0.        ]])

TF-IDF 알고리즘을 이용해 숫자 벡터로 인코딩을 마친 위 데이터간 유사도를 살펴보자.

이를 위해, 코사인 유사도를 구해보자

from numpy import dot
from numpy.linalg import norm
import numpy as np

def cos_sim(A, B):
       return dot(A, B)/(norm(A)*norm(B))
doc1= tf_idf_array[0]
doc2= tf_idf_array[1]
doc3= tf_idf_array[2]
doc4 = tf_idf_array[3]
print(cos_sim(doc1, doc2)) #문서1과 문서2의 코사인 유사도
print(cos_sim(doc1, doc3)) #문서1과 문서3의 코사인 유사도
print(cos_sim(doc2, doc3)) #문서2과 문서3의 코사인 유사도
print(cos_sim(doc3, doc4)) #문서3과 문서4의 코사인 유사도
0.0
0.11401070555406811
0.0
0.0