ํค์๋ / ์ฐ๊ด์ด ์ถ์ถ์ ์ํ ํ์ด์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์
๋๋ค. by Lovit (Hyunjoong) and Hunsik Shin
soykeyword ์์ ์ถ์ถํ๋ ํค์๋์ ์ฐ๊ด์ด๋ ๋ค์๊ณผ ๊ฐ์ด ์ ์๋ฉ๋๋ค. ํ ๋ฌธ์ ์งํฉ์ ํค์๋๋ ๋ค๋ฅธ ๋ฌธ์ ์งํฉ๊ณผ ํด๋น ๋ฌธ์ ์งํฉ์ ๊ตฌ๋ถํ ์ ์๋ ์ง ์ข์ ๋จ์ด์ด๋ฉฐ (๊ตฌ๋ถ๋ ฅ, discriminative power), ํด๋น ์งํฉ์ ์ ์ค๋ช
ํ ์ ์๋ (์ค๋ช
๋ ฅ, high coverage) ๋จ์ด์
๋๋ค. ๋น๋์๊ฐ ๋ฎ์ ๋จ์ด๋ ํ ์งํฉ์์๋ง ๋ฑ์ฅํ ๊ฐ๋ฅ์ฑ์ด ๋๊ธฐ ๋๋ฌธ์ ๊ตฌ๋ถ๋ ฅ์ ํฌ์ง๋ง ์ค๋ช
๋ ฅ์ด ์ฝํฉ๋๋ค. ์ ์๋ ๋ ๊ฐ์ง ์๊ณ ๋ฆฌ์ฆ์ ๋์ ์ค๋ช
๋ ฅ๊ณผ ๊ตฌ๋ถ๋ ฅ์ ๋์์ ์ง๋๋ ๋จ์ด๋ค์ ํค์๋๋ก ์ ํํฉ๋๋ค.
์ฐ๊ด์ด๋ ๊ธฐ์ค ๋จ์ด๊ฐ ํฌํจ๋ ๋ฌธ์ ์งํฉ๊ณผ ํฌํจ๋์ง ์์ ๋ฌธ์ ์งํฉ์ ๊ตฌ๋ถํ๋ ํค์๋๋ฅผ ์ฐ๊ด์ด๋ก ์ ์ํฉ๋๋ค. ์ด๋ co-occurrence ๊ฐ ๋์ ๋จ์ด๋ผ๋ ์๋ฏธ์ด๊ธฐ๋ ํฉ๋๋ค. co-occurrence ๊ฐ ๋์ผ๋ฉด์๋ ์ค๋ช
๋ ฅ์ด ์ข์ ๋จ์ด๋ฅผ ์ ํํฉ๋๋ค.
Setup
Requires
- Python >= 3.4 (not tested in Python 2)
- numpy >= 1.12.1
- scikit-learn >= 0.18
- psutil >=5.0.1
Usage
ํ์ต์ sparse matrix x ๋ฅผ extractor ์ ์
๋ ฅํฉ๋๋ค. index2word ๋ word idx ์ ๋ํ ๋จ์ด list ํ์์
๋๋ค. ์ด๋ฅผ train() ์ ์
๋ ฅํ์ง ์์ผ๋ฉด ํค์๋์ ์ฐ๊ด์ด๊ฐ ๋จ์ด๊ฐ ์๋ word idx ๋ก ์ถ๋ ฅ๋ฉ๋๋ค.
from soykeyword.lasso import LassoKeywordExtractor
lassobased_extractor = LassoKeywordExtractor(min_tf=20, min_df=10)
lassobased_extractor.train(x, index2word) # x: sparse matrix
ํค์๋๋ฅผ ์ถ์ถํ ๋ฌธ์ ์งํฉ documents ๋ฅผ extract_from_docs() ์ ์
๋ ฅํ๋ฉด, ํด๋น ๋ฌธ์ ์งํฉ๊ณผ ๊ทธ ์ธ์ ๋ฌธ์ ์งํฉ์ ๊ตฌ๋ถํ๋ keywords ๋ฅผ ์ถ์ถํฉ๋๋ค.
keywords = lassobased_extractor.extract_from_docs(
documents,
min_num_of_keywords=30
)
์ฐ๊ด์ด๋ extract_from_word ์ ๋จ์ด๋ฅผ ์
๋ ฅํ๋ฉด ๋ฉ๋๋ค.
lassobased_extractor.extract_from_word(
'์์ด์ค์์ด',
min_num_of_keywords=30
)
ํ๋ฃจ ๋ด์ค๋ฅผ ๊ธฐ์ค์ผ๋ก '์์ด์ค์์ด'์ ์ฐ๊ด์ด๋ฅผ ์ถ์ถํ ์์์
๋๋ค.
[KeywordScore(word='์์ด์ค์์ด', frequency=270, coefficient=17.850189941320671),
KeywordScore(word='์ ์นด์ดํธ๋ค์ด', frequency=221, coefficient=1.200759338786378),
KeywordScore(word='๋ฎค์ง', frequency=195, coefficient=1.081777863860977),
KeywordScore(word='์ผ์ฐ๋๊ตฌ', frequency=36, coefficient=0.98636875892070186),
KeywordScore(word='ํค๋ฏธ', frequency=297, coefficient=0.70877507721215616),
KeywordScore(word='์ฑํผ์ธ', frequency=105, coefficient=0.51940928356916138),
KeywordScore(word='๊ฐ๋ ฌ', frequency=352, coefficient=0.36972563098092176),
KeywordScore(word='์ปด๋ฐฑ', frequency=536, coefficient=0.30677481146665397),
KeywordScore(word='ํ๋ ค', frequency=518, coefficient=0.26764304959838653),
KeywordScore(word='์์ถ', frequency=735, coefficient=0.23882691530127598),
KeywordScore(word='๊ฑธ๊ทธ๋ฃน', frequency=1060, coefficient=0.20972098801573957),
KeywordScore(word='๋ฐฉ์', frequency=208, coefficient=0.19694219657704334),
KeywordScore(word='ํ๋ก๋์ค101', frequency=96, coefficient=0.17074232136595247),
...
์์ธํ ํํ ๋ฆฌ์ผ์ ๋งํฌ์ ์์ต๋๋ค.
Proportion based ํค์๋ / ์ฐ๊ด์ด ์ถ์ถ์ ๋ ์งํฉ์ ๋จ์ด ์ถ์ฐ ํ๋ฅ ์ ๋น์จ์ ๋ฐํ์ผ๋ก ํค์๋๋ฅผ ์ถ์ถํฉ๋๋ค. P(w|pos) ๋ ํค์๋๋ฅผ ์ถ์ถํ ๋ฌธ์ ์งํฉ์์์ ๋จ์ด w ์ ์ถ์ฐ ๋น์จ์ด๋ฉฐ, P(w|neg)๋ ๊ทธ ์ธ์ ๋ฌธ์ ์งํฉ์์์ ๋จ์ด w์ ์ถ์ฐ ๋น์จ ์
๋๋ค.
score(w) = P(w|pos) / { P(w|pos) + P(w|neg) }
ํ์ต ๋ฐ์ดํฐ์ ํํ๋ (sparse matrix, index2word) ํน์ ํ
์คํธ ๋ฐ์ดํฐ, ๋ ์ข
๋ฅ๋ฅผ ๋ชจ๋ ์ง์ํฉ๋๋ค.
ํ
์คํธ ๋ฐ์ดํฐ ํ์์ผ๋ก ํ์ต์ ํ ๊ฒฝ์ฐ์๋ min_tf, min_df, tokenize ๋ฅผ ์ค์ ํด์ค๋๋ค. ๋ค์์ ์์๋ default value ์
๋๋ค.
from soykeyword.proportion import CorpusbasedKeywordExtractor
corpusbased_extractor = CorpusbasedKeywordExtractor(
min_tf=20,
min_df=2,
tokenize=lambda x:x.strip().split(),
verbose=True
)
# docs: list of str like
corpusbased_extractor.train(docs)
ํค์๋๋ฅผ ์ถ์ถํ ๋ฌธ์ ์งํฉ documents ๋ฅผ ์
๋ ฅํฉ๋๋ค.
keywords = corpusbased_extractor.extract_from_docs(
documents,
min_score=0.8,
min_frequency=100
)
์ฐ๊ด์ด๋ฅผ ์ถ์ถํ ๋จ์ด word ๋ฅผ ์
๋ ฅํฉ๋๋ค.
keywords = corpusbased_extractor.extract_from_word(
'์์ด์ค์์ด',
min_score=0.8,
min_frequency=100
)
ํ๋ฃจ์ ๋ด์ค๋ฅผ ๋ฐํ์ผ๋ก ์ถ์ถํ ์์ด์ค์์ด์ ์ฐ๊ด์ด ์
๋๋ค.
keywords[:10]
[KeywordScore(word='์์ด์ค์์ด', frequency=270, score=1.0),
KeywordScore(word='์ ์นด์ดํธ๋ค์ด', frequency=221, score=0.997897148491129),
KeywordScore(word='ํํ๊ณค', frequency=104, score=0.9936420169665052),
KeywordScore(word='์ ๊น', frequency=162, score=0.9931809154109712),
KeywordScore(word='์ ๋ท', frequency=125, score=0.9910325251765126),
KeywordScore(word='๊ฑธํฌ๋ฌ์ฌ', frequency=111, score=0.9904705029926091),
KeywordScore(word='ํ์ดํ๊ณก', frequency=311, score=0.987384461584851),
KeywordScore(word='์ฝ๋', frequency=105, score=0.9871835929954923),
KeywordScore(word='๋ณธ๋ช
', frequency=105, score=0.9863934667369743),
KeywordScore(word='์์ค', frequency=101, score=0.9852544036088814)]
ํ์ต๋ฐ์ดํฐ์ ํํ๊ฐ (sparse matrix, index2word) ๋ผ๋ฉด MatrixbasedKeywordExtractor ๋ฅผ ์ด์ฉํฉ๋๋ค.
from soykeyword.proportion import MatrixbasedKeywordExtractor
matrixbased_extractor = MatrixbasedKeywordExtractor(
min_tf=20,
min_df=2,
verbose=True
)
matrixbased_extractor.train(x, index2word)
์์ธํ ํํ ๋ฆฌ์ผ์ ๋งํฌ์ ์์ต๋๋ค.
ํจ๊ป ์ด์ฉํ๋ฉด ์ข์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค
soynlp
ํ๊ตญ์ด ์์ฐ์ด์ฒ๋ฆฌ๋ฅผ ์ํ ๋ฏธ๋ฑ๋ก๋จ์ด ๋ฌธ์ ํด๊ฒฐ์ ์ํ ๋จ์ด ์ถ์ถ / ๋จ์ด ์ถ์ถ๊ธฐ์ ํ์ต ๊ฒฐ๊ณผ๋ฅผ ์ด์ฉํ๋ ํ ํฌ๋์ด์ / ํ์ฌ ํ๋ณ / ์ ๊ทํ ๋ฅผ ์ง์ํฉ๋๋ค.
KoNLPy
KoNLPy ๋ ํ๊ตญ์ด ์ ๋ณด์ฒ๋ฆฌ๋ฅผ ์ํ ํ์ด์ฌ ํจํค์ง์
๋๋ค. ํ๋๋, ๊ผฌ๊ผฌ๋ง, ์ฝ๋ชจ๋, MeCab-ko, ํธ์ํฐ ํ๊ตญ์ด ๋ถ์๊ธฐ๋ฅผ ํ์ด์ฌ ํ๊ฒฝ์์ ์ ๊ณตํฉ๋๋ค.
- http://konlpy.org
- KoNLPy ๋ Java๋ฅผ ์ด์ฉํ๊ธฐ ๋๋ฌธ์ Java ์ JPype ๊ฐ ํ์ํฉ๋๋ค. ํํ์ด์ง์ ์ค์น๋ฒ์ ๋ฐ๋์ ๋ณด์๊ธฐ ๋ฐ๋๋๋ค.
customized KoNLPy
KoNLPy ์ ๋ฑ๋ก๋์ง ์์ ๋จ์ด๋ฅผ ์์ฝ๊ฒ ์ฒ๋ฆฌํ๊ธฐ ์ํ์ฌ ํ
ํ๋ฆฟ๊ณผ ์ฌ์ ๊ธฐ๋ฐ string match ๋ฅผ KoNLPy ์ ํจ๊ป ์ด์ฉํ๋ wrapping ํ์ด์ฌ ํจํค์ง์
๋๋ค.
soyspacing
๋์ด์ฐ๊ธฐ ์ค๋ฅ๊ฐ ์์ ๊ฒฝ์ฐ ์ด๋ฅผ ์ ๊ฑฐํ๋ฉด ํ
์คํธ ๋ถ์์ด ์ฌ์์ง ์ ์์ต๋๋ค. ๋ถ์ํ๋ ค๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ด์ฐ๊ธฐ ์์ง์ ํ์ตํ๊ณ , ์ด๋ฅผ ์ด์ฉํ์ฌ ๋์ด์ฐ๊ธฐ ์ค๋ฅ๋ฅผ ๊ต์ ํฉ๋๋ค.