Posts Keybert와 kiwi형태소분석기를 사용하여 키워드추출 하기
Post
Cancel

Keybert와 kiwi형태소분석기를 사용하여 키워드추출 하기

1
2
# !pip install keybert
# !pip install kiwipiepy

0. 들어가며

  • KeyBert라는 키워드 추출 Bert 있어 간략히 소개 하려고 한다.
  • KeyBert에 대한 자세한 내용은 https://maartengr.github.io/KeyBERT/ 참조

1. 데이터 소개

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from keybert import KeyBERT
from kiwipiepy import Kiwi
from transformers import BertModel

text="""
김영하가 『살인자의 기억법』 이후 9 년 만에 내놓는 장편소설 『작별인사』는 그리 멀지 않은 미래를 배경으로, 별안간 삶이 송두리째 뒤흔들린 한 소년의 여정을 좇는다. 
유명한 IT 기업의 연구원인 아버지와 쾌적하고 평화롭게 살아가던 철이는 어느날 갑자기 수용소로 끌려가 난생처음 날것의 감정으로 가득한 혼돈의 세계에 맞닥뜨리게 되면서 정신적, 신체적 위기에 직면한다. 
동시에 자신처럼 사회에서 배제된 자들을 만나 처음으로 생생한 소속감을 느끼고 따뜻한 우정도 싹틔운다. 철이는 그들과 함께 수용소를 탈출하여 집으로 돌아가기 위해 길을 떠나지만 그 여정에는 피할 수 없는 질문이 기다리고 있다.
세계보건기구 WHO가 팬데믹을 선언한 지 2년이 지나서야 작가는 『작별인사』의 개작을 마쳤다. 420매 분량이던 원고는 약 800매로 늘었고, 주제도 완전히 달라졌다. 
‘인간을 인간으로 만드는 것은 무엇인가?’, ‘인간과 인간이 아닌 존재들을 가르는 경계는 어디인가’를 묻던 소설은 ‘삶이란 과연 계속될 가치가 있는 것인가?’, 
‘세상에 만연한 고통을 어떻게 하면 줄일 수 있을 것인가’, ‘어쩔 수 없이 태어났다면 어떻게 살고 어떻게 죽어야 할 것인가’와 같은 질문을 던지는 이야기로 바뀌었다.
팬데믹이 개작에 영향을 주었을 수도 있고, 원래 『작별인사』의 구상에 담긴 어떤 맹아가 오랜 개작을 거치며 발아했는지도 모른다. 그것에 대해 작가는 이렇게 말하고 있다.
마치 제목이 어떤 마력이 있어서 나로 하여금 자기에게 어울리는 이야기로 다시 쓰도록 한 것 같은 느낌이다. 탈고를 하고 얼마 지나지 않아 원고를 다시 읽어보았다. 
이제야 비로소 애초에 내가 쓰려고 했던 어떤 것이 제대로, 남김 없이 다 흘러나왔다는 생각이 들었다. _’작가의 말’에서
전면적인 수정을 통해 2022년의 『작별인사』는 2020년의 『작별인사』를 마치 시놉시스나 초고처럼 보이게 할 정도로 확연하게 달라졌다. 그리고 김영하의 이전 문학 세계와의 연결점들이 분명해졌다.
"""

  • 최근 출간한 김영하 작가의 장편소설 작별인사의 소개문이다.
  • 위의 소개문을 가지고 키워드 추출을 해볼것이다.

2. 키워드 추출

1
2
3
4
model = BertModel.from_pretrained('skt/kobert-base-v1')
kw_model = KeyBERT(model)
keywords = kw_model.extract_keywords(text, keyphrase_ngram_range=(1, 1), stop_words=None, top_n=10)
keywords
1
2
3
4
5
6
7
8
9
10
[('장편소설', 0.531),
 ('소설은', 0.5247),
 ('위기에', 0.5216),
 ('삶이란', 0.5162),
 ('삶이', 0.5157),
 ('작가는', 0.5111),
 ('정신적', 0.5075),
 ('생생한', 0.5043),
 ('문학', 0.5007),
 ('연구원인', 0.4899)]
  • pre train 된 skt/kobert-base-v1을 기본 모델로 만들고 Keybert 객체로 만들어준다.
  • extract_keywords를 사용하여 키워드를 추출하면 (키워드 : 가중치) 형태의 결과가 나온다.
    • keyphrase_ngram_range : 몇개의 ngram으로 사용할것인가.
    • top_n : 몇개의 키워드를 뽑을것인가
    • stop_words : 불용어 처리를 할것인가
  • 결과를 보면 장편소설 처럼 좋은 키워드도 있지만 삶이란, 삶이와 같은 애매하거나 중복되는 키워드도 존재한다.
  • 그래서 kiwi 형태소 분석기를 사용하여 명사만 골라 다시 키워드를 추출한다.

3. Kiwi 형태소 분석기를 사용한 명사 추출

1
2
kiwi = Kiwi()
kiwi.analyze(text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[([Token(form='김영하', tag='NNP', start=1, len=3),
   Token(form='가', tag='JKS', start=4, len=1),
   Token(form='『', tag='SS', start=6, len=1),
   Token(form='살인자', tag='NNG', start=7, len=3),
   Token(form='의', tag='JKG', start=10, len=1),
   Token(form='기억', tag='NNG', start=12, len=2),
   Token(form='법', tag='NNG', start=14, len=1),
   Token(form='』', tag='SS', start=15, len=1),
   ...
   Token(form='지', tag='VX', start=976, len=1),
   Token(form='었', tag='EP', start=976, len=1),
   Token(form='다', tag='EF', start=977, len=1),
   Token(form='.', tag='SF', start=978, len=1)],
  -2288.81298828125)]
  • Kiwi를 사용하여 형태소 분석을 진행하였다.
  • 이중에 키워드만 사용할 tag가 N으로 시작하는 명사만 남김
1
2
3
4
5
6
7
8
# 명사 추출 함수
def noun_extractor(text):
    results = []
    result = kiwi.analyze(text)
    for token, pos, _, _ in result[0][0]:
        if len(token) != 1 and pos.startswith('N') or pos.startswith('SL'):
            results.append(token)
    return results
1
2
nouns = noun_extractor(text)
nouns
1
['김영하','살인자','기억','이후','장편','소설','작별','인사','미래','배경','소년','여정','유명','IT','기업','연구원', '아버지','평화','철이','수용소','날것','감정','혼돈','세계','정신','신체','위기','직면','동시','자신','사회','배제','처음','소속감','우정','철이','수용소','탈출','여정','질문','세계보건기구','WHO','팬데믹','선언','작가','작별','인사','개작','분량','원고','주제','인간','인간','무엇','인간','인간','존재','경계','어디','소설','계속','가치','세상','만연','고통','질문','이야기','팬데믹','개작','영향','원래','작별','인사','구상','맹아','개작','발아','그것','작가','제목','마력','자기','이야기','느낌','탈고','얼마','원고','애초','생각','작가','전면','수정','작별','인사','작별','인사','시놉시스','초고','정도','김영하','이전','문학','세계','연결']
  • 명사가 잘 추출된것을 볼 수 있다.
1
2
text = ' '.join(nouns)
text
1
'김영하 살인자 기억 이후 장편 소설 작별 인사 미래 배경 소년 여정 유명 IT 기업 연구원 아버지 평화 철이 수용소 날것 감정 혼돈 세계 정신 신체 위기 직면 동시 자신 사회 배제 처음 소속감 우정 철이 수용소 탈출 여정 질문 세계보건기구 WHO 팬데믹 선언 작가 작별 인사 개작 분량 원고 주제 인간 인간 무엇 인간 인간 존재 경계 어디 소설 계속 가치 세상 만연 고통 질문 이야기 팬데믹 개작 영향 원래 작별 인사 구상 맹아 개작 발아 그것 작가 제목 마력 자기 이야기 느낌 탈고 얼마 원고 애초 생각 작가 전면 수정 작별 인사 작별 인사 시놉시스 초고 정도 김영하 이전 문학 세계 연결'
  • 리스트형태가 아닌 문자열 형태로 바꾸기 위해 join을 사용하여 연결해준다.
1
2
keywords = kw_model.extract_keywords(text, keyphrase_ngram_range=(1, 1), stop_words=None, top_n=20)
keywords
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[('원래', 0.6794),
 ('세상', 0.6729),
 ('이전', 0.6568),
 ('어디', 0.6205),
 ('원고', 0.6179),
 ('애초', 0.6105),
 ('김영하', 0.6049),
 ('팬데믹', 0.6007),
 ('전면', 0.6001),
 ('존재', 0.5995),
 ('인간', 0.5976),
 ('자기', 0.5787),
 ('초고', 0.5772),
 ('탈고', 0.5747),
 ('이야기', 0.5726),
 ('정도', 0.5673),
 ('정신', 0.5624),
 ('처음', 0.5576),
 ('세계', 0.5512),
 ('시놉시스', 0.5475)]
  • 다시 keybert 모델을 사용하여 키워드를 추출하면 처음보다는 나은 결과를 보여준다.
  • 불용어 처리와 같은 전처리를 진행한다면 조금더 나은 결과를 얻을 수 있을것 같다.
This post is licensed under CC BY 4.0 by the author.

아나콘다 가상환경 주피터랩에서 쉽게 쓰기

2022-07-12 TIL