개발일지0813
TF-IDF를 통한 인물의 상세 설명에서 키워드 추출 본문
개념
TF-IDF
TF-IDF는 Term Frequency - Inverse Document Frequency 의 약자로써, 정보 검색과 텍스트 마이닝에서 이용하는 가중치이다. 여러 문서로 이루어진 문서들의 집합이 있을때, 어떤 단어가 특정 문서내에 얼머나 중요한지 추출해 내는 통계적 수치를 이용하여 문서의 추출하는데 이용된다. 또한 문서 들 간의 유사도를 판단하는 데 사용되기도 한다.
TF-IDF의 값은 TF와 IDF 값을 곱한 값이다.
TF
단어 빈도, term frequency, 는 특정 단어가 문서내에 얼마나 자주 등장하는지 나타내는 값이다. 값이 증가함에 따라 해당 단어의 중요도 또한 증가 한다고 해석 할 수 있다.
tf(t,d) 로 표현하며 가장 단순한 방식은 다음과 같다.
그 외에는 빈도 계산 법이 크게 세가지가 있다.
- 불린 빈도 : 단어 수가 그리 많지 않거나 등장 횟수가 적은 경우 자주 사용된다.
2. 로그 스케일 빈도
3. 증가 빈도: 문서의 길이가 상대적으로 길 경우 단어의 빈도값을 조절할 경우 사용된다.
DF 와 IDF
문서 빈도, document frequency, 는 단어 자체가 문서군 내에서 자주 사용 되는 경우 해당 단어가 흔하게 등장 하는 것을 의미한다. 즉, 특정 단어 t가 등장한 문서의 수를 가리킨다.
이때 우리가 필요한 것은 IDF 값이다. IDF는 DF값의 역수(반비례)로, Inverse document frequency의 약자다. IDF는 한 단어가 문서 집한 전체에서 얼마나 공통적으로 나타나는지 나타내는 수치이다.
IDF와 DF는 단순 역수 관계는 아니지만 역수비스무리 한 관계성을 나타낸다. 결국엔 한쪽이 증가함에 따라 다른 한쪽은 감소한다.
- |D|: 문서 집합의 크기
- |{d ∈ D : t ∈d}| :단어 t가포함된문서의수. 값이 0이 되는 것을 방지하기 위해 쓰임.
최종적으로는 TF-IDF 의 값을 다음과 같이 계산 할 수 있다.
특정 문서 내에 단어 빈도가 높을 수록 && 전체 문서들 중 그 단어를 포함한 문서가 적을수록 TF-IDF 값이 높게 나온다.
즉, 해당 단어가 그 특정 문서에서 키워드로 작용을 한다는 의미이다.
이를 이용하면 문서에서 공통적으로 나타나는 단어들은 미리 걸러낼 수 가 있다.
IDF의 로그 함수값은 상 1 이상이므로, IDF값과 TF-IDF값은 항상 0 이상이 된다.
특정 단어를 포함 하는 문서들이 많을 수록 로그 함수 안의 값이 1에 가까워지고, 해당 경우 IDF 와 TF-IDF의 값은 0에 가까워진다.
적용
실제로 적용하여 실험한 사항은 아니고 대략적인 구상이기 때문에 언제든지 변화 및 수정 사항이 존재 할 수 있음.
우리에게 주어진 각 인물에 대한 상세설명은 몇 문장짜리( 대략 한~두문단) 길이를 지닌다.
이때 상세 설명에서 TF-IDF를 응용하기 위해서는 '상세설명'을 두가지 형태로 처리 할 수 있다.
A. 문장마다 나누어진 리스트 형태의 타입
B. 한 인물의 상세설명 == 하나의 문서에 전부 대입.
1. Tokenize 이전에 명사들만 추출 해 내서 다시 합치는 재 처리 과정이 필요.
해당 과정에서 KoNLPy 파이썬 패키지 이용
kkma.nouns(string) 이용
hashingvectorization 등 다양한 경우 고려중.
설치는 이쪽으로 : https://konlpy-ko.readthedocs.io/ko/v0.5.1/install/
2. (1)을 기반으로 Tfidfvectorizor 이용
아래 블로그 참고 하여 코드 테스트 가능
3. 상세 설명은 키워드를 추출하는 것이 목표기 때문에 A or B 형태 두 가지 모두에 대해 테스트가 필요하다.
예를 들면 B형태 일경우 단순 TF만 계산 한다거나 A 형태 일 경우 TF-IDF를 활용하는 방안 구상.
전체적으로 각 경우에 대해 실험 필요
4. 실제로 위 블로그레 있는 코드를 참고하여 간단하게 실험을 해보았음. KoNLPy를 설치 못해서 명사 처리 하기 이전임. 해당 코드는 아래와 같음.
from sklearn.feature_extraction.text import TfidfVectorizer
from collections import defaultdict
import nltk
corpus = ['생졸년 미상. 부여의 왕.','서기전 1세기에 활동한 것으로 보이며, 그의 행적은 고구려 시조인 동명성왕의 신화를 전하고 있는 『삼국사기』‧『삼국유사』, 그리고 이규보(李奎報)의 「동명왕편(東明王篇)」에 전해지고 있다.',
'부여왕 해부루(解夫婁)는 늙도록 아들이 없어 산천에 후사를 구하러 다녔다.','그러던 중 곤연(鯤淵)이라는 연못가의 이상한 돌 밑에서 금빛나는 개구리(또는 달팽이) 모양의 아이를 발견하고 그를 하늘이 준 자식이라 생각하여 데리고 가서 키웠다. 금와라는 이름은 바로 금빛나는 개구리 모양을 한 데서 비롯된 것이다.',
'그뒤 태자로 책봉되어 해부루를 이어서 부여의 왕이 되었다. 그리고 태백산 남쪽의 우발수(優渤水)에서 하백(河伯)에게서 쫓겨난 하백의 딸 유화(柳花)를 발견하고 궁중으로 데려왔다.',
'그런데 유화가 이상하게도 알을 낳자 이를 버리게 하였다. 그러나 곧 알의 신비함을 인정하고 유화에게 돌려주었는데, 이 알에서 주몽이 탄생하였다. 그의 일곱 아들들이 주몽을 시기하여 그를 처치할 것을 건의하였지만 금와는 듣지 않고 주몽으로 하여금 말을 기르게 하여 그 뜻을 시험하고자 하였다.',
'그뒤 주몽이 달아나자 그를 추격하는 군대를 파견하였지만 잡지는 못하였다. 주몽이 고구려 건국을 위하여 남쪽으로 떠난 후에 유화가 24년에 죽자 태후(太后)의 예로서 장사를 치러주었다. 그의 사후 왕위는 아들인 대소(帶素)에 의하여 계승되었다.']
vectorizer = TfidfVectorizer()
sp_matrix = vectorizer.fit_transform(corpus)
word2id = defaultdict(lambda : 0)
for idx, feature in enumerate(vectorizer.get_feature_names()):
word2id[feature] = idx
for i, sent in enumerate(corpus):
print('====== document[%d] ======' % i)
print( [ (token, sp_matrix[i, word2id[token]]) for token in sent.split() ] )
결과
====== document[0] ======
[('생졸년', 0.6098192948782316), ('미상.', 0.0), ('부여의', 0.5062023856012858), ('왕.', 0.0)]
====== document[1] ======
[('서기전', 0.21883610113134444), ('1세기에', 0.21883610113134444), ('활동한', 0.21883610113134444), ('것으로', 0.21883610113134444), ('보이며,', 0.21883610113134444), ('그의', 0.15527075522291417), ('행적은', 0.21883610113134444), ('고구려', 0.18165275742954373), ('시조인', 0.21883610113134444), ('동명성왕의', 0.21883610113134444), ('신화를', 0.21883610113134444), ('전하고', 0.21883610113134444), ('있는', 0.21883610113134444), ('『삼국사기』‧『삼국유사』,', 0.21883610113134444), ('그리고', 0.18165275742954373), ('이규보(李奎報)의', 0.21883610113134444), ('「동명왕편(東明王篇)」에', 0.21883610113134444), ('전해지고', 0.21883610113134444), ('있다.', 0.21883610113134444)]
====== document[2] ======
[('부여왕', 0.31622776601683794), ('해부루(解夫婁)는', 0.0), ('늙도록', 0.31622776601683794), ('아들이', 0.31622776601683794), ('없어', 0.31622776601683794), ('산천에', 0.31622776601683794), ('후사를', 0.31622776601683794), ('구하러', 0.31622776601683794), ('다녔다.', 0.0)]
====== document[3] ======
[('그러던', 0.17357220477127927), ('중', 0.0), ('곤연(鯤淵)이라는', 0.0), ('연못가의', 0.17357220477127927), ('이상한', 0.17357220477127927), ('돌', 0.0), ('밑에서', 0.17357220477127927), ('금빛나는', 0.34714440954255854), ('개구리(또는', 0.0), ('달팽이)', 0.0), ('모양의', 0.17357220477127927), ('아이를', 0.17357220477127927), ('발견하고', 0.14407983621908985), ('그를', 0.1231546677226129), ('하늘이', 0.17357220477127927), ('준', 0.0), ('자식이라', 0.17357220477127927), ('생각하여', 0.17357220477127927), ('데리고', 0.17357220477127927), ('가서', 0.17357220477127927), ('키웠다.', 0.0), ('금와라는', 0.17357220477127927), ('이름은', 0.17357220477127927), ('바로', 0.17357220477127927), ('금빛나는', 0.34714440954255854), ('개구리', 0.34714440954255854), ('모양을', 0.17357220477127927), ('한', 0.0), ('데서', 0.17357220477127927), ('비롯된', 0.17357220477127927), ('것이다.', 0.0)]
====== document[4] ======
[('그뒤', 0.17400969218442333), ('태자로', 0.20962854148511464), ('책봉되어', 0.20962854148511464), ('해부루를', 0.20962854148511464), ('이어서', 0.20962854148511464), ('부여의', 0.17400969218442333), ('왕이', 0.20962854148511464), ('되었다.', 0.0), ('그리고', 0.17400969218442333), ('태백산', 0.20962854148511464), ('남쪽의', 0.20962854148511464), ('우발수(優渤水)에서', 0.0), ('하백(河伯)에게서', 0.0), ('쫓겨난', 0.20962854148511464), ('하백의', 0.20962854148511464), ('딸', 0.0), ('유화(柳花)를', 0.0), ('발견하고', 0.17400969218442333), ('궁중으로', 0.20962854148511464), ('데려왔다.', 0.0)]
====== document[5] ======
[('그런데', 0.16355039419021236), ('유화가', 0.13576087277075674), ('이상하게도', 0.16355039419021236), ('알을', 0.16355039419021236), ('낳자', 0.16355039419021236), ('이를', 0.16355039419021236), ('버리게', 0.16355039419021236), ('하였다.', 0.0), ('그러나', 0.16355039419021236), ('곧', 0.0), ('알의', 0.16355039419021236), ('신비함을', 0.16355039419021236), ('인정하고', 0.16355039419021236), ('유화에게', 0.16355039419021236), ('돌려주었는데,', 0.0), ('이', 0.0), ('알에서', 0.16355039419021236), ('주몽이', 0.13576087277075674), ('탄생하였다.', 0.0), ('그의', 0.11604389354239988), ('일곱', 0.16355039419021236), ('아들들이', 0.16355039419021236), ('주몽을', 0.16355039419021236), ('시기하여', 0.16355039419021236), ('그를', 0.11604389354239988), ('처치할', 0.16355039419021236), ('것을', 0.16355039419021236), ('건의하였지만', 0.16355039419021236), ('금와는', 0.16355039419021236), ('듣지', 0.16355039419021236), ('않고', 0.16355039419021236), ('주몽으로', 0.16355039419021236), ('하여금', 0.16355039419021236), ('말을', 0.16355039419021236), ('기르게', 0.16355039419021236), ('하여', 0.16355039419021236), ('그', 0.0), ('뜻을', 0.16355039419021236), ('시험하고자', 0.16355039419021236), ('하였다.', 0.0)]
====== document[6] ======
[('그뒤', 0.14949790734781357), ('주몽이', 0.29899581469562714), ('달아나자', 0.18009932595699582), ('그를', 0.12778585531322625), ('추격하는', 0.18009932595699582), ('군대를', 0.18009932595699582), ('파견하였지만', 0.18009932595699582), ('잡지는', 0.18009932595699582), ('못하였다.', 0.0), ('주몽이', 0.29899581469562714), ('고구려', 0.14949790734781357), ('건국을', 0.18009932595699582), ('위하여', 0.18009932595699582), ('남쪽으로', 0.18009932595699582), ('떠난', 0.18009932595699582), ('후에', 0.18009932595699582), ('유화가', 0.14949790734781357), ('24년에', 0.18009932595699582), ('죽자', 0.18009932595699582), ('태후(太后)의', 0.0), ('예로서', 0.18009932595699582), ('장사를', 0.18009932595699582), ('치러주었다.', 0.0), ('그의', 0.12778585531322625), ('사후', 0.18009932595699582), ('왕위는', 0.18009932595699582), ('아들인', 0.18009932595699582), ('대소(帶素)에', 0.0), ('의하여', 0.18009932595699582), ('계승되었다.', 0.0)]
좀 더 데이터에 대한 전처리를 하고 실험을 하면 좀 더 유의미한 결과가 나올 것 같음. 후에 인물 DB에 넣을 수 있는 형태로 키워드 추출하는 것이 최종 목표.
<REFERENCE>
[1] https://ko.wikipedia.org/wiki/Tf-idf
[2] https://konlpy-ko.readthedocs.io/ko/v0.4.3/#
[3]http://blog.naver.com/PostView.nhnblogId=vangarang&logNo=221072014624&categoryNo=35&parentCategoryNo=0
'개발일지0813' 카테고리의 다른 글
SOURCETREE 오류 - 원격저장소 로그인 정보 삭제 하기 (0) | 2019.10.13 |
---|---|
<GIT - 제대로 알자!> 버전 만들기(COMMIT) (0) | 2019.10.13 |
<GIT-제대로 알자!> 저장소(REPOSITORY) 만들기 (0) | 2019.10.12 |
<GIT - 제대로 알자!>GIT 과 SourceTree의 설치 (0) | 2019.10.12 |
20170126 백준 1260 DFS BFS (0) | 2017.01.26 |