
Security News
Deno 2.6 + Socket: Supply Chain Defense In Your CLI
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.
soynlp
Advanced tools
ํ๊ตญ์ด ๋ถ์์ ์ํ pure python code ์ ๋๋ค. ํ์ต๋ฐ์ดํฐ๋ฅผ ์ด์ฉํ์ง ์์ผ๋ฉด์ ๋ฐ์ดํฐ์ ์กด์ฌํ๋ ๋จ์ด๋ฅผ ์ฐพ๊ฑฐ๋, ๋ฌธ์ฅ์ ๋จ์ด์ด๋ก ๋ถํด, ํน์ ํ์ฌ ํ๋ณ์ ํ ์ ์๋ ๋น์ง๋ํ์ต ์ ๊ทผ๋ฒ์ ์งํฅํฉ๋๋ค.
soynlp ์์ ์ ๊ณตํ๋ WordExtractor ๋ NounExtractor ๋ ์ฌ๋ฌ ๊ฐ์ ๋ฌธ์๋ก๋ถํฐ ํ์ตํ ํต๊ณ ์ ๋ณด๋ฅผ ์ด์ฉํ์ฌ ์๋ํฉ๋๋ค. ๋น์ง๋ํ์ต ๊ธฐ๋ฐ ์ ๊ทผ๋ฒ๋ค์ ํต๊ณ์ ํจํด์ ์ด์ฉํ์ฌ ๋จ์ด๋ฅผ ์ถ์ถํ๊ธฐ ๋๋ฌธ์ ํ๋์ ๋ฌธ์ฅ ํน์ ๋ฌธ์์์ ๋ณด๋ค๋ ์ด๋ ์ ๋ ๊ท๋ชจ๊ฐ ์๋ ๋์ผํ ์ง๋จ์ ๋ฌธ์ (homogeneous documents) ์์ ์ ์๋ํฉ๋๋ค. ์ํ ๋๊ธ๋ค์ด๋ ํ๋ฃจ์ ๋ด์ค ๊ธฐ์ฌ์ฒ๋ผ ๊ฐ์ ๋จ์ด๋ฅผ ์ด์ฉํ๋ ์งํฉ์ ๋ฌธ์๋ง ๋ชจ์์ Extractors ๋ฅผ ํ์ตํ์๋ฉด ์ข์ต๋๋ค. ์ด์ง์ ์ธ ์ง๋จ์ ๋ฌธ์๋ค์ ํ๋๋ก ๋ชจ์ ํ์ตํ๋ฉด ๋จ์ด๊ฐ ์ ์ถ์ถ๋์ง ์์ต๋๋ค.
soynlp=0.0.46 ๊น์ง๋ min_score, minimum_score, l_len_min ์ฒ๋ผ ์ต์๊ฐ์ด๋ ์ต๋๊ฐ์ ์๊ตฌํ๋ parameters ์ ์ด๋ฆ๋ค์ ๊ท์น์ด ์์์ต๋๋ค. ์ง๊ธ๊น์ง ์์ ํ์ ์ฝ๋๋ค ์ค์์ ์ง์ parameters ๋ฅผ ์ค์ ํ์ ๋ถ๋ค์๊ฒ ํผ๋์ ๋๋ฆด ์ ์์ผ๋, ๋ ๋ฆ๊ธฐ์ ์ ์ดํ์ ๋ฐ์ํ ๋ถํธํจ์ ์ค์ด๊ธฐ ์ํ์ฌ ๋ณ์ ๋ช ์ ์์ ํ์์ต๋๋ค.
0.0.47 ์ดํ minimum, maximum ์ ์๋ฏธ๊ฐ ๋ค์ด๊ฐ๋ ๋ณ์๋ช ์ min, max ๋ก ์ค์ฌ ๊ธฐ์ ํฉ๋๋ค. ๊ทธ ๋ค์ ์ด๋ค ํญ๋ชฉ์ threshold parameter ์ธ์ง ์ด๋ฆ์ ๊ธฐ์ ํฉ๋๋ค. ๋ค์๊ณผ ๊ฐ์ ํจํด์ผ๋ก parameter ์ด๋ฆ์ ํต์ผํฉ๋๋ค. {min, max}_{noun, word}_{score, threshold} ๋ฑ์ผ๋ก ์ด๋ฆ์ ํต์ผํฉ๋๋ค. ํญ๋ชฉ์ด ์๋ช ํ ๊ฒฝ์ฐ์๋ ์ด๋ฅผ ์๋ตํ ์ ์์ต๋๋ค.
soynlp ์์๋ substring counting ์ ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. ๋น๋์์ ๊ด๋ จ๋ parameter ๋ count ๊ฐ ์๋ frequency ๋ก ํต์ผํฉ๋๋ค.
index ์ idx ๋ idx ๋ก ํต์ผํฉ๋๋ค.
์ซ์๋ฅผ ์๋ฏธํ๋ num ๊ณผ n ์ num ์ผ๋ก ํต์ผํฉ๋๋ค.
$ pip install soynlp
๋ช ์ฌ ์ถ์ถ์ ํ๊ธฐ ์ํด ์ฌ๋ฌ ์๋๋ฅผ ํ ๊ฒฐ๊ณผ, v1, news, v2 ์ธ ๊ฐ์ง ๋ฒ์ ์ด ๋ง๋ค์ด์ก์ต๋๋ค. ๊ฐ์ฅ ์ข์ ์ฑ๋ฅ์ ๋ณด์ด๋ ๊ฒ์ v2 ์ ๋๋ค.
WordExtractor ๋ ํต๊ณ๋ฅผ ์ด์ฉํ์ฌ ๋จ์ด์ ๊ฒฝ๊ณ ์ ์๋ฅผ ํ์ตํ๋ ๊ฒ์ผ ๋ฟ, ๊ฐ ๋จ์ด์ ํ์ฌ๋ฅผ ํ๋จํ์ง๋ ๋ชปํฉ๋๋ค. ๋๋ก๋ ๊ฐ ๋จ์ด์ ํ์ฌ๋ฅผ ์์์ผ ํ๋ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. ๋ํ ๋ค๋ฅธ ํ์ฌ๋ณด๋ค๋ ๋ช ์ฌ์์ ์๋ก์ด ๋จ์ด๊ฐ ๊ฐ์ฅ ๋ง์ด ๋ง๋ค์ด์ง๋๋ค. ๋ช ์ฌ์ ์ค๋ฅธ์ชฝ์๋ -์, -๋, -๋ผ๋, -ํ๋ ์ฒ๋ผ ํน์ ๊ธ์๋ค์ด ์์ฃผ ๋ฑ์ฅํฉ๋๋ค. ๋ฌธ์์ ์ด์ (๋์ด์ฐ๊ธฐ ๊ธฐ์ค ์ ๋)์์ ์ผ์ชฝ์ ์์นํ substring ์ ์ค๋ฅธ์ชฝ์ ์ด๋ค ๊ธ์๋ค์ด ๋ฑ์ฅํ๋์ง ๋ถํฌ๋ฅผ ์ดํด๋ณด๋ฉด ๋ช ์ฌ์ธ์ง ์๋์ง ํ๋จํ ์ ์์ต๋๋ค. soynlp ์์๋ ๋ ๊ฐ์ง ์ข ๋ฅ์ ๋ช ์ฌ ์ถ์ถ๊ธฐ๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋ ๋ชจ๋ ๊ฐ๋ฐ ๋จ๊ณ์ด๊ธฐ ๋๋ฌธ์ ์ด๋ค ๊ฒ์ด ๋ ์ฐ์ํ๋ค ๋งํ๊ธฐ๋ ์ด๋ ต์ต๋๋ค๋ง, NewsNounExtractor ๊ฐ ์ข ๋ ๋ง์ ๊ธฐ๋ฅ์ ํฌํจํ๊ณ ์์ต๋๋ค. ์ถํ, ๋ช ์ฌ ์ถ์ถ๊ธฐ๋ ํ๋์ ํด๋์ค๋ก ์ ๋ฆฌ๋ ์์ ์ ๋๋ค.
from soynlp.noun import LRNounExtractor
noun_extractor = LRNounExtractor()
nouns = noun_extractor.train_extract(sentences) # list of str like
from soynlp.noun import NewsNounExtractor
noun_extractor = NewsNounExtractor()
nouns = noun_extractor.train_extract(sentences) # list of str like
2016-10-20 ์ ๋ด์ค๋ก๋ถํฐ ํ์ตํ ๋ช ์ฌ์ ์์์ ๋๋ค.
๋ด๋งํฌ ์๋ ๋๋ฌด๋๋ฌด๋๋ฌด ๊ฐ๋ฝ๋ ๋งค๋ด์ผ ์ง๋๊ต์
์ ๋ง์น ๊ฐ๊ตฌ ์ธ๋๋ค ์ ์ฐ์
๊ธฐ๋ขฐ์ ๋
ธ์ค
ํ ๋ฆฌ์ฐ๋ ํ๋ผ์ ๋ถ๋ฒ์กฐ์
์์คํธ๋ฆฌํธ์ ๋ 2022๋
๋ถํ
๊ณ ์จ ์ดํ 1987๋
๋ถ์จ ์ ๊ธฐ ๋ ์ค
์คํ์ด ์ถฉ๋น๊ธ ๊ฑด์ถ๋ฌผ ๋ด์ง๋๋ ์ฌ๊ฐ ํ๋์ฉ
๊ทผ๋ ํฌ์์ฃผ์ฒด๋ณ 4์ ํ๊ถ ๋คํธ์์ค ๋ชจ๋ฐ์ผ๊ฒ์
์ฐ๋ ๋ฐ์นญ ๋ง์ฑ ์์ง ์ ์๋ฒ ํ์คํ
์คํด์ ์ฌ์ฌ์์๋ค ๋จ์ ๋ถ์ฅ์กฐ๋ฆฌ ์ฐจ๊ด๊ธ ๊ฒ์๋ฌผ
์ธํฐํฐ ์ํ ๋จ๊ธฐ๊ฐ ํธ๊ณก ๋ฌด์ฐ ์ธ๊ตญ์ธ๋ค
์ธ๋ฌด์กฐ์ฌ ์์ ํํ ์ํน ์ํผ์ค ์์ฅ ๊ณต๋ฒ
๋ ์์ธํ ์ค๋ช ์ ํํ ๋ฆฌ์ผ์ ์์ต๋๋ค.
soynlp=0.0.46+ ์์๋ ๋ช ์ฌ ์ถ์ถ๊ธฐ version 2 ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด์ ๋ฒ์ ์ ๋ช ์ฌ ์ถ์ถ์ ์ ํ์ฑ๊ณผ ํฉ์ฑ๋ช ์ฌ ์ธ์ ๋ฅ๋ ฅ, ์ถ๋ ฅ๋๋ ์ ๋ณด์ ์ค๋ฅ๋ฅผ ์์ ํ ๋ฒ์ ์ ๋๋ค. ์ฌ์ฉ๋ฒ์ version 1 ๊ณผ ๋น์ทํฉ๋๋ค.
from soynlp.utils import DoublespaceLineCorpus
from soynlp.noun import LRNounExtractor_v2
corpus_path = '2016-10-20-news'
sents = DoublespaceLineCorpus(corpus_path, iter_sent=True)
noun_extractor = LRNounExtractor_v2(verbose=True)
nouns = noun_extractor.train_extract(sents)
์ถ์ถ๋ nouns ๋ {str:namedtuple} ํ์์ ๋๋ค.
print(nouns['๋ด์ค']) # NounScore(frequency=4319, score=1.0)
_compounds_components ์๋ ๋ณตํฉ๋ช ์ฌ๋ฅผ ๊ตฌ์ฑํ๋ ๋จ์ผ๋ช ์ฌ๋ค์ ์ ๋ณด๊ฐ ์ ์ฅ๋์ด ์์ต๋๋ค. '๋ํ๋ฏผ๊ตญ', '๋ น์์ฑ์ฅ'๊ณผ ๊ฐ์ด ์ค์ ๋ก๋ ๋ณตํฉํํ์์ด์ง๋ง, ๋จ์ผ ๋ช ์ฌ๋ก ์ด์ฉ๋๋ ๊ฒฝ์ฐ๋ ๋จ์ผ ๋ช ์ฌ๋ก ์ธ์ํฉ๋๋ค.
list(noun_extractor._compounds_components.items())[:5]
# [('์ ์ํจ๋ฐ์ฌํ๋๋ฏธ์ฌ์ผ', ('์ ์ํจ', '๋ฐ์ฌ', 'ํ๋๋ฏธ์ฌ์ผ')),
# ('๋ฏธ์ฌ์ผ๋์๋ฅ๋ ฅ์์ํ', ('๋ฏธ์ฌ์ผ', '๋์', '๋ฅ๋ ฅ', '์์ํ')),
# ('๊ธ๋ก๋ฒ๋
น์์ฑ์ฅ์ฐ๊ตฌ์', ('๊ธ๋ก๋ฒ', '๋
น์์ฑ์ฅ', '์ฐ๊ตฌ์')),
# ('์์นด๊ณ ์ต์
๊ฑฐ๋์', ('์์นด๊ณ ', '์ต์
', '๊ฑฐ๋์')),
# ('๋ํ๋ฏผ๊ตญํน์์๋ฌด์ ๊ณต', ('๋ํ๋ฏผ๊ตญ', 'ํน์', '์๋ฌด', '์ ๊ณต')),
LRGraph ๋ ํ์ต๋ corpus ์ ๋ฑ์ฅํ ์ด์ ์ L-R ๊ตฌ์กฐ๋ฅผ ์ ์ฅํ๊ณ ์์ต๋๋ค. get_r ๊ณผ get_l ์ ์ด์ฉํ์ฌ ์ด๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
noun_extractor.lrgraph.get_r('์์ด์ค์์ด')
# [('', 123),
# ('์', 47),
# ('๋', 40),
# ('์', 18),
# ('๊ฐ', 18),
# ('์', 7),
# ('์๊ฒ', 6),
# ('๊น์ง', 2),
# ('๋', 2),
# ('๋ถํฐ', 1)]
๋ ์์ธํ ์ค๋ช ์ ํํ ๋ฆฌ์ผ 2์ ์์ต๋๋ค.
2016 ๋ 10์์ ์ฐ์๊ธฐ์ฌ ๋ด์ค์๋ 'ํธ์์ด์ค', '์์ด์ค์์ด' ์ ๊ฐ์ ๋จ์ด๊ฐ ์กด์ฌํฉ๋๋ค. ํ์ง๋ง ๋ง๋ญ์น๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ต๋ ํ์ฌ ํ๋ณ๊ธฐ / ํํ์ ๋ถ์๊ธฐ๋ ์ด๋ฐ ๋จ์ด๋ฅผ ๋ณธ ์ ์ด ์์ต๋๋ค. ๋ ์๋ก์ด ๋จ์ด๊ฐ ๋ง๋ค์ด์ง๊ธฐ ๋๋ฌธ์ ํ์ตํ์ง ๋ชปํ ๋จ์ด๋ฅผ ์ ๋๋ก ์ธ์ํ์ง ๋ชปํ๋ ๋ฏธ๋ฑ๋ก๋จ์ด ๋ฌธ์ (out of vocabulry, OOV) ๊ฐ ๋ฐ์ํฉ๋๋ค. ํ์ง๋ง ์ด ์๊ธฐ์ ์์ฑ๋ ์ฌ๋ฌ ๊ฐ์ ์ฐ์ ๋ด์ค ๊ธฐ์ฌ๋ฅผ ์ฝ๋ค๋ณด๋ฉด 'ํธ์์ด์ค', '์์ด์ค์์ด' ๊ฐ์ ๋จ์ด๊ฐ ๋ฑ์ฅํจ์ ์ ์ ์๊ณ , ์ฌ๋์ ์ด๋ฅผ ํ์ตํ ์ ์์ต๋๋ค. ๋ฌธ์์งํฉ์์ ์์ฃผ ๋ฑ์ฅํ๋ ์ฐ์๋ ๋จ์ด์ด์ ๋จ์ด๋ผ ์ ์ํ๋ค๋ฉด, ์ฐ๋ฆฌ๋ ํต๊ณ๋ฅผ ์ด์ฉํ์ฌ ์ด๋ฅผ ์ถ์ถํ ์ ์์ต๋๋ค. ํต๊ณ ๊ธฐ๋ฐ์ผ๋ก ๋จ์ด(์ ๊ฒฝ๊ณ)๋ฅผ ํ์ตํ๋ ๋ฐฉ๋ฒ์ ๋ค์ํฉ๋๋ค. soynlp๋ ๊ทธ ์ค, Cohesion score, Branching Entropy, Accessor Variety ๋ฅผ ์ ๊ณตํฉ๋๋ค.
from soynlp.word import WordExtractor
word_extractor = WordExtractor(min_frequency=100,
min_cohesion_forward=0.05,
min_right_branching_entropy=0.0
)
word_extractor.train(sentences) # list of str or like
words = word_extractor.extract()
words ๋ Scores ๋ผ๋ namedtuple ์ value ๋ก ์ง๋๋ dict ์ ๋๋ค.
words['์์ด์ค์์ด']
Scores(cohesion_forward=0.30063636035733476,
cohesion_backward=0,
left_branching_entropy=0,
right_branching_entropy=0,
left_accessor_variety=0,
right_accessor_variety=0,
leftside_frequency=270,
rightside_frequency=0
)
2016-10-26 ์ ๋ด์ค ๊ธฐ์ฌ๋ก๋ถํฐ ํ์ตํ ๋จ์ด ์ ์ (cohesion * branching entropy) ๊ธฐ์ค์ผ๋ก ์ ๋ ฌํ ์์์ ๋๋ค.
๋จ์ด (๋น๋์, cohesion, branching entropy)
์ดฌ์ (2222, 1.000, 1.823)
์์ธ (25507, 0.657, 2.241)
๋ค์ด (3906, 0.534, 2.262)
๋กฏ๋ฐ (1973, 0.999, 1.542)
ํ๊ตญ (9904, 0.286, 2.729)
๋ถํ (4954, 0.766, 1.729)
ํฌ์ (4549, 0.630, 1.889)
๋จ์ด (1453, 0.817, 1.515)
์งํ (8123, 0.516, 1.970)
์๊ธฐ (1157, 0.970, 1.328)
์ด์ (4537, 0.592, 1.768)
ํ๋ก๊ทธ๋จ (2738, 0.719, 1.527)
ํด๋ฆฐํด (2361, 0.751, 1.420)
๋ฐ์ด (927, 0.831, 1.298)
๋๋ผ๋ง (2375, 0.609, 1.606)
์ฐ๋ฆฌ (7458, 0.470, 1.827)
์ค๋น (1736, 0.639, 1.513)
๋ฃจ์ด (1284, 0.743, 1.354)
ํธ๋ผํ (3565, 0.712, 1.355)
์๊ฐ (3963, 0.335, 2.024)
ํฌ๋ค (999, 0.626, 1.341)
์ฐ์
(2203, 0.403, 1.769)
10 (18164, 0.256, 2.210)
ํ์ธ (3575, 0.306, 2.016)
ํ์ (3428, 0.635, 1.279)
๋ฌธ์ (4737, 0.364, 1.808)
ํ์ (2357, 0.962, 0.830)
ํ๊ฐ (2749, 0.362, 1.787)
20 (59317, 0.667, 1.171)
์คํฌ์ธ (3422, 0.428, 1.604)
์์ธํ ๋ด์ฉ์ word extraction tutorial ์ ์์ต๋๋ค. ํ์ฌ ๋ฒ์ ์์ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
WordExtractor ๋ก๋ถํฐ ๋จ์ด ์ ์๋ฅผ ํ์ตํ์๋ค๋ฉด, ์ด๋ฅผ ์ด์ฉํ์ฌ ๋จ์ด์ ๊ฒฝ๊ณ๋ฅผ ๋ฐ๋ผ ๋ฌธ์ฅ์ ๋จ์ด์ด๋ก ๋ถํดํ ์ ์์ต๋๋ค. soynlp ๋ ์ธ ๊ฐ์ง ํ ํฌ๋์ด์ ๋ฅผ ์ ๊ณตํฉ๋๋ค. ๋์ด์ฐ๊ธฐ๊ฐ ์ ๋์ด ์๋ค๋ฉด LTokenizer ๋ฅผ ์ด์ฉํ ์ ์์ต๋๋ค. ํ๊ตญ์ด ์ด์ ์ ๊ตฌ์กฐ๋ฅผ "๋ช ์ฌ + ์กฐ์ฌ" ์ฒ๋ผ "L + [R]" ๋ก ์๊ฐํฉ๋๋ค.
L parts ์๋ ๋ช ์ฌ/๋์ฌ/ํ์ฉ์ฌ/๋ถ์ฌ๊ฐ ์์นํ ์ ์์ต๋๋ค. ์ด์ ์์ L ๋ง ์ ์ธ์ํ๋ค๋ฉด ๋๋จธ์ง ๋ถ๋ถ์ด R parts ๊ฐ ๋ฉ๋๋ค. LTokenizer ์๋ L parts ์ ๋จ์ด ์ ์๋ฅผ ์ ๋ ฅํฉ๋๋ค.
from soynlp.tokenizer import LTokenizer
scores = {'๋ฐ์ด':0.5, '๋ฐ์ดํฐ':0.5, '๋ฐ์ดํฐ๋ง์ด๋':0.5, '๊ณต๋ถ':0.5, '๊ณต๋ถ์ค':0.45}
tokenizer = LTokenizer(scores=scores)
sent = '๋ฐ์ดํฐ๋ง์ด๋์ ๊ณต๋ถํ๋ค'
print(tokenizer.tokenize(sent, flatten=False))
#[['๋ฐ์ดํฐ๋ง์ด๋', '์'], ['๊ณต๋ถ', '์ค์ด๋ค']]
print(tokenizer.tokenize(sent))
# ['๋ฐ์ดํฐ๋ง์ด๋', '์', '๊ณต๋ถ', '์ค์ด๋ค']
๋ง์ฝ WordExtractor ๋ฅผ ์ด์ฉํ์ฌ ๋จ์ด ์ ์๋ฅผ ๊ณ์ฐํ์๋ค๋ฉด, ๋จ์ด ์ ์ ์ค ํ๋๋ฅผ ํํ์ฌ scores ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ์๋๋ Forward cohesion ์ ์ ์๋ง์ ์ด์ฉํ๋ ๊ฒฝ์ฐ์ ๋๋ค. ๊ทธ ์ธ์๋ ๋ค์ํ๊ฒ ๋จ์ด ์ ์๋ฅผ ์ ์ํ์ฌ ์ด์ฉํ ์ ์์ต๋๋ค.
from soynlp.word import WordExtractor
from soynlp.utils import DoublespaceLineCorpus
file_path = 'your file path'
corpus = DoublespaceLineCorpus(file_path, iter_sent=True)
word_extractor = WordExtractor(
min_frequency=100, # example
min_cohesion_forward=0.05,
min_right_branching_entropy=0.0
)
word_extractor.train(sentences)
words = word_extractor.extract()
cohesion_score = {word:score.cohesion_forward for word, score in words.items()}
tokenizer = LTokenizer(scores=cohesion_score)
๋ช ์ฌ ์ถ์ถ๊ธฐ์ ๋ช ์ฌ ์ ์์ Cohesion ์ ํจ๊ป ์ด์ฉํ ์๋ ์์ต๋๋ค. ํ ์๋ก, "Cohesion ์ ์ + ๋ช ์ฌ ์ ์"๋ฅผ ๋จ์ด ์ ์๋ก ์ด์ฉํ๋ ค๋ฉด ์๋์ฒ๋ผ ์์ ํ ์ ์์ต๋๋ค.
from soynlp.noun import LRNounExtractor_2
noun_extractor = LRNounExtractor_v2()
nouns = noun_extractor.train_extract(corpus) # list of str like
noun_scores = {noun:score.score for noun, score in nouns.items()}
combined_scores = {noun:score + cohesion_score.get(noun, 0)
for noun, score in noun_scores.items()}
combined_scores = combined_scores.update(
{subword:cohesion for subword, cohesion in cohesion_score.items()
if not (subword in combine_scores)}
)
tokenizer = LTokenizer(scores=combined_scores)
๋์ด์ฐ๊ธฐ๊ฐ ์ ๋๋ก ์ง์ผ์ง์ง ์์ ๋ฐ์ดํฐ๋ผ๋ฉด, ๋ฌธ์ฅ์ ๋์ด์ฐ๊ธฐ ๊ธฐ์ค์ผ๋ก ๋๋์ด์ง ๋จ์๊ฐ L + [R] ๊ตฌ์กฐ๋ผ ๊ฐ์ ํ ์ ์์ต๋๋ค. ํ์ง๋ง ์ฌ๋์ ๋์ด์ฐ๊ธฐ๊ฐ ์ง์ผ์ง์ง ์์ ๋ฌธ์ฅ์์ ์ต์ํ ๋จ์ด๋ถํฐ ๋์ ๋ค์ด์ต๋๋ค. ์ด ๊ณผ์ ์ ๋ชจ๋ธ๋ก ์ฎ๊ธด MaxScoreTokenizer ์ญ์ ๋จ์ด ์ ์๋ฅผ ์ด์ฉํฉ๋๋ค.
from soynlp.tokenizer import MaxScoreTokenizer
scores = {'ํ์ค': 0.3, 'ํ์คํ': 0.7, '์ข์์': 0.2, '์ข์':0.5}
tokenizer = MaxScoreTokenizer(scores=scores)
print(tokenizer.tokenize('๋ํ์คํ๊ฐ์ข์์'))
# ['๋', 'ํ์คํ', '๊ฐ', '์ข์', '์']
print(tokenizer.tokenize('๋ํ์คํ๊ฐ ์ข์์'), flatten=False)
# [[('๋', 0, 1, 0.0, 1), ('ํ์คํ', 1, 4, 0.7, 3), ('๊ฐ', 4, 5, 0.0, 1)],
# [('์ข์', 0, 2, 0.5, 2), ('์', 2, 3, 0.0, 1)]]
MaxScoreTokenizer ์ญ์ WordExtractor ์ ๊ฒฐ๊ณผ๋ฅผ ์ด์ฉํ์ค ๋์๋ ์์ ์์์ฒ๋ผ ์ ์ ํ scores ๋ฅผ ๋ง๋ค์ด ์ฌ์ฉํฉ๋๋ค. ์ด๋ฏธ ์๋ ค์ง ๋จ์ด ์ฌ์ ์ด ์๋ค๋ฉด ์ด ๋จ์ด๋ค์ ๋ค๋ฅธ ์ด๋ค ๋จ์ด๋ณด๋ค๋ ๋ ํฐ ์ ์๋ฅผ ๋ถ์ฌํ๋ฉด ๊ทธ ๋จ์ด๋ ํ ํฌ๋์ด์ ๊ฐ ํ๋์ ๋จ์ด๋ก ์๋ผ๋ ๋๋ค.
๊ท์น ๊ธฐ๋ฐ์ผ๋ก๋ ๋จ์ด์ด์ ๋ง๋ค ์ ์์ต๋๋ค. ์ธ์ด๊ฐ ๋ฐ๋๋ ๋ถ๋ถ์์ ์ฐ๋ฆฌ๋ ๋จ์ด์ ๊ฒฝ๊ณ๋ฅผ ์ธ์ํฉ๋๋ค. ์๋ฅผ ๋ค์ด "์์ด๊ณ ใ ใ ใ ใ ์ง์ง?" ๋ [์์ด๊ณ , ใ ใ , ใ ใ , ์ง์ง, ?]๋ก ์ฝ๊ฒ ๋จ์ด์ด์ ๋๋๋๋ค.
from soynlp.tokenizer import RegexTokenizer
tokenizer = RegexTokenizer()
print(tokenizer.tokenize('์ด๋ ๊ฒ์ฐ์๋๋ฌธ์ฅ์์๋ฆฌ์ง์์ต๋๋ค๋ง'))
# ['์ด๋ ๊ฒ์ฐ์๋๋ฌธ์ฅ์์๋ฆฌ์ง์์ต๋๋ค๋ง']
print(tokenizer.tokenize('์ซ์123์ด์์ดabc์์์ฌ์์ผ๋ฉดใ
ใ
์๋ฆฌ๊ฒ ์ฃ '))
# ['์ซ์', '123', '์ด์์ด', 'abc', '์์์ฌ์์ผ๋ฉด', 'ใ
ใ
', '์๋ฆฌ๊ฒ ์ฃ ']
๋จ์ด ์ฌ์ ์ด ์ ๊ตฌ์ถ๋์ด ์๋ค๋ฉด, ์ด๋ฅผ ์ด์ฉํ์ฌ ์ฌ์ ๊ธฐ๋ฐ ํ์ฌ ํ๋ณ๊ธฐ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. ๋จ, ํํ์๋ถ์์ ํ๋ ๊ฒ์ด ์๋๊ธฐ ๋๋ฌธ์ 'ํ๋', 'ํ๋ค', 'ํ๊ณ '๋ ๋ชจ๋ ๋์ฌ์ ํด๋นํฉ๋๋ค. Lemmatizer ๋ ํ์ฌ ๊ฐ๋ฐ/์ ๋ฆฌ ์ค์ ๋๋ค.
pos_dict = {
'Adverb': {'๋๋ฌด', '๋งค์ฐ'},
'Noun': {'๋๋ฌด๋๋ฌด๋๋ฌด', '์์ด์ค์์ด', '์์ด', '๋
ธ๋', '์ค', '์ด', '๊ณ ์'},
'Josa': {'๋', '์', '์ด๋ค', '์
๋๋ค', '์ด', '์ด๋', '๋ฅผ', '๋ผ', '๋ผ๋'},
'Verb': {'ํ๋', 'ํ๋ค', 'ํ๊ณ '},
'Adjective': {'์์', '์์๋ค'},
'Exclamation': {'์ฐ์'}
}
from soynlp.postagger import Dictionary
from soynlp.postagger import LRTemplateMatcher
from soynlp.postagger import LREvaluator
from soynlp.postagger import SimpleTagger
from soynlp.postagger import UnknowLRPostprocessor
dictionary = Dictionary(pos_dict)
generator = LRTemplateMatcher(dictionary)
evaluator = LREvaluator()
postprocessor = UnknowLRPostprocessor()
tagger = SimpleTagger(generator, evaluator, postprocessor)
sent = '๋๋ฌด๋๋ฌด๋๋ฌด๋์์ด์ค์์ด์๋
ธ๋์
๋๋ค!!'
print(tagger.tag(sent))
# [('๋๋ฌด๋๋ฌด๋๋ฌด', 'Noun'),
# ('๋', 'Josa'),
# ('์์ด์ค์์ด', 'Noun'),
# ('์', 'Josa'),
# ('๋
ธ๋', 'Noun'),
# ('์
๋๋ค', 'Josa'),
# ('!!', None)]
๋ ์์ธํ ์ฌ์ฉ๋ฒ์ ์ฌ์ฉ๋ฒ ํํ ๋ฆฌ์ผ ์ ๊ธฐ์ ๋์ด ์์ผ๋ฉฐ, ๊ฐ๋ฐ๊ณผ์ ๋ ธํธ๋ ์ฌ๊ธฐ์ ๊ธฐ์ ๋์ด ์์ต๋๋ค.
ํ ํฌ๋์ด์ ๋ฅผ ํ์ตํ๊ฑฐ๋, ํน์ ํ์ต๋ ํ ํฌ๋์ด์ ๋ฅผ ์ด์ฉํ์ฌ ๋ฌธ์๋ฅผ sparse matrix ๋ก ๋ง๋ญ๋๋ค. minimum / maximum of term frequency / document frequency ๋ฅผ ์กฐ์ ํ ์ ์์ต๋๋ค. Verbose mode ์์๋ ํ์ฌ์ ๋ฒกํฐ๋ผ์ด์ง ์ํฉ์ print ํฉ๋๋ค.
vectorizer = BaseVectorizer(
tokenizer=tokenizer,
min_tf=0,
max_tf=10000,
min_df=0,
max_df=1.0,
stopwords=None,
lowercase=True,
verbose=True
)
corpus.iter_sent = False
x = vectorizer.fit_transform(corpus)
๋ฌธ์์ ํฌ๊ธฐ๊ฐ ํฌ๊ฑฐ๋, ๊ณง๋ฐ๋ก sparse matrix ๋ฅผ ์ด์ฉํ ๊ฒ์ด ์๋๋ผ๋ฉด ์ด๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ฆฌ์ง ์๊ณ ๊ทธ๋๋ก ํ์ผ๋ก ์ ์ฅํ ์ ์์ต๋๋ค. fit_to_file() ํน์ to_file() ํจ์๋ ํ๋์ ๋ฌธ์์ ๋ํ term frequency vector ๋ฅผ ์ป๋๋๋ก ํ์ผ์ ๊ธฐ๋กํฉ๋๋ค. BaseVectorizer ์์ ์ด์ฉํ ์ ์๋ parameters ๋ ๋์ผํฉ๋๋ค.
vectorizer = BaseVectorizer(min_tf=1, tokenizer=tokenizer)
corpus.iter_sent = False
matrix_path = 'YOURS'
vectorizer.fit_to_file(corpus, matrix_path)
ํ๋์ ๋ฌธ์๋ฅผ sparse matrix ๊ฐ ์๋ list of int ๋ก ์ถ๋ ฅ์ด ๊ฐ๋ฅํฉ๋๋ค. ์ด ๋ vectorizer.vocabulary_ ์ ํ์ต๋์ง ์์ ๋จ์ด๋ encoding ์ด ๋์ง ์์ต๋๋ค.
vectorizer.encode_a_doc_to_bow('์ค๋ ๋ด์ค๋ ์ด๊ฒ์ด ์ ๋ถ๋ค')
# {3: 1, 258: 1, 428: 1, 1814: 1}
list of int ๋ list of str ๋ก decoding ์ด ๊ฐ๋ฅํฉ๋๋ค.
vectorizer.decode_from_bow({3: 1, 258: 1, 428: 1, 1814: 1})
# {'๋ด์ค': 1, '๋': 1, '์ค๋': 1, '์ด๊ฒ์ด': 1}
dict ํ์์ bag of words ๋ก๋ encoding ์ด ๊ฐ๋ฅํฉ๋๋ค.
vectorizer.encode_a_doc_to_list('์ค๋์ ๋ด์ค๋ ๋งค์ฐ ์ฌ๊ฐํฉ๋๋ค')
# [258, 4, 428, 3, 333]
dict ํ์์ bag of words ๋ decoding ์ด ๊ฐ๋ฅํฉ๋๋ค.
vectorizer.decode_from_list([258, 4, 428, 3, 333])
['์ค๋', '์', '๋ด์ค', '๋', '๋งค์ฐ']
๋ํ ๋ฐ์ดํฐ, ๋๊ธ ๋ฐ์ดํฐ์ ๋ฑ์ฅํ๋ ๋ฐ๋ณต๋๋ ์ด๋ชจํฐ์ฝ์ ์ ๋ฆฌ ๋ฐ ํ๊ธ, ํน์ ํ ์คํธ๋ง ๋จ๊ธฐ๊ธฐ ์ํ ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
from soynlp.normalizer import *
emoticon_normalize('ใ
ใ
ใ
ใ
ใ
ใ
ใ
ใ
ใ
ใ
ใ
ใ
ใ
์ฟ ใ
ใ
ใ
ใ
ใ
ใ
', num_repeats=3)
# 'ใ
ใ
ใ
ใ
ใ
ใ
'
repeat_normalize('์ํํํํํํํํํํซ', num_repeats=2)
# '์ํํํซ'
only_hangle('๊ฐ๋๋คใ
ใ
ใ
ใ
ใ
์ฟ ใ
ใ
ใ
abcd123!!์ํซ')
# '๊ฐ๋๋คใ
ใ
ใ
ใ
ใ
์ฟ ใ
ใ
ใ
์ํซ'
only_hangle_number('๊ฐ๋๋คใ
ใ
ใ
ใ
ใ
์ฟ ใ
ใ
ใ
abcd123!!์ํซ')
# '๊ฐ๋๋คใ
ใ
ใ
ใ
ใ
์ฟ ใ
ใ
ใ
123 ์ํซ'
only_text('๊ฐ๋๋คใ
ใ
ใ
ใ
ใ
์ฟ ใ
ใ
ใ
abcd123!!์ํซ')
# '๊ฐ๋๋คใ
ใ
ใ
ใ
ใ
์ฟ ใ
ใ
ใ
abcd123!!์ํซ'
๋ ์์ธํ ์ค๋ช ์ ํํ ๋ฆฌ์ผ์ ์์ต๋๋ค.
์ฐ๊ด์ด ๋ถ์์ ์ํ co-occurrence matrix ๊ณ์ฐ๊ณผ ์ด๋ฅผ ์ด์ฉํ Point-wise Mutual Information (PMI) ๊ณ์ฐ์ ์ํ ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
์๋ sent_to_word_contexts_matrix ํจ์๋ฅผ ์ด์ฉํ์ฌ (word, context words) matrix ๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค. x ๋ scipy.sparse.csr_matrix ์ด๋ฉฐ, (n_vocabs, n_vocabs) ํฌ๊ธฐ์ ๋๋ค. idx2vocab ์ x ์ ๊ฐ row, column ์ ํด๋นํ๋ ๋จ์ด๊ฐ ํฌํจ๋ list of str ์ ๋๋ค. ๋ฌธ์ฅ์ ์/๋ค windows ๋จ์ด๋ฅผ context ๋ก ์ธ์ํ๋ฉฐ, min_tf ์ด์์ ๋น๋์๋ก ๋ฑ์ฅํ ๋จ์ด์ ๋ํด์๋ง ๊ณ์ฐ์ ํฉ๋๋ค. dynamic_weight ๋ context ๊ธธ์ด์ ๋ฐ๋น๋กํ์ฌ weighting ์ ํฉ๋๋ค. windows ๊ฐ 3 ์ผ ๊ฒฝ์ฐ, 1, 2, 3 ์นธ ๋จ์ด์ง ๋จ์ด์ co-occurrence ๋ 1, 2/3, 1/3 ์ผ๋ก ๊ณ์ฐ๋ฉ๋๋ค.
from soynlp.vectorizer import sent_to_word_contexts_matrix
x, idx2vocab = sent_to_word_contexts_matrix(
corpus,
windows=3,
min_tf=10,
tokenizer=tokenizer, # (default) lambda x:x.split(),
dynamic_weight=False,
verbose=True
)
Co-occurrence matrix ์ธ x ๋ฅผ pmi ์ ์ ๋ ฅํ๋ฉด row ์ column ์ ๊ฐ ์ถ์ผ๋ก PMI ๊ฐ ๊ณ์ฐ๋ฉ๋๋ค. pmi_dok ์ scipy.sparse.dok_matrix ํ์์ ๋๋ค. min_pmi ์ด์์ ๊ฐ๋ง ์ ์ฅ๋๋ฉฐ, default ๋ min_pmi = 0 ์ด๊ธฐ ๋๋ฌธ์ Positive PMI (PPMI) ์ ๋๋ค. alpha ๋ PMI(x,y) = p(x,y) / ( p(x) * ( p(y) + alpha ) ) ์ ์ ๋ ฅ๋๋ smoothing parameter ์ ๋๋ค. ๊ณ์ฐ ๊ณผ์ ์ด ์ค๋ ๊ฑธ๋ฆฌ๊ธฐ ๋๋ฌธ์ verbose = True ๋ก ์ค์ ํ๋ฉด ํ์ฌ์ ์งํ ์ํฉ์ ์ถ๋ ฅํฉ๋๋ค.
from soynlp.word import pmi
pmi_dok = pmi(
x,
min_pmi=0,
alpha=0.0001,
verbose=True
)
๋ ์์ธํ ์ค๋ช ์ ํํ ๋ฆฌ์ผ์ ์์ต๋๋ค.
์์ฐ์ด์ฒ๋ฆฌ ๋ชจ๋ธ ํ์ต์ ์ํ์ฌ ์ธ์ข ๋ง๋ญ์น ๋ฐ์ดํฐ๋ฅผ ์ ์ ํ๊ธฐ ์ํ ํจ์๋ค์ ์ ๊ณตํฉ๋๋ค. ํํ์/ํ์ฌ ํํ๋ก ์ ์ ๋ ํ์ต์ฉ ๋ฐ์ดํฐ๋ฅผ ๋ง๋๋ ํจ์, ์ฉ์ธ์ ํ์ฉ ํํ๋ฅผ ์ ๋ฆฌํ์ฌ ํ ์ด๋ธ๋ก ๋ง๋๋ ํจ์, ์ธ์ข ๋ง๋ญ์น์ ํ์ฌ ์ฒด๊ณ๋ฅผ ๋จ์ํ ์ํค๋ ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
๋์ด์ฐ๊ธฐ ์ค๋ฅ๊ฐ ์์ ๊ฒฝ์ฐ ์ด๋ฅผ ์ ๊ฑฐํ๋ฉด ํ ์คํธ ๋ถ์์ด ์ฌ์์ง ์ ์์ต๋๋ค. ๋ถ์ํ๋ ค๋ ๋ฐ์ดํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋์ด์ฐ๊ธฐ ์์ง์ ํ์ตํ๊ณ , ์ด๋ฅผ ์ด์ฉํ์ฌ ๋์ด์ฐ๊ธฐ ์ค๋ฅ๋ฅผ ๊ต์ ํฉ๋๋ค.
ํ ํฌ๋์ด์ ๋ ๋จ์ด ์ถ์ถ๊ธฐ๋ฅผ ํ์ตํ ํ์์์ด, HITS algorithm ์ ์ด์ฉํ์ฌ substring graph ์์ ํค์๋๋ฅผ ์ถ์ถํฉ๋๋ค.
ํค์๋ ์ถ์ถ๊ธฐ์ ๋๋ค. Logistic Regression ์ ์ด์ฉํ๋ ๋ชจ๋ธ๊ณผ ํต๊ณ ๊ธฐ๋ฐ ๋ชจ๋ธ, ๋ ์ข ๋ฅ์ ํค์๋ ์ถ์ถ๊ธฐ๋ฅผ ์ ๊ณตํฉ๋๋ค. scipy.sparse ์ sparse matrix ํ์๊ณผ ํ ์คํธ ํ์ผ ํ์์ ์ง์ํฉ๋๋ค.
FAQs
Unsupervised Korean Natural Language Processing Toolkits
We found that soynlp demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.

Security News
New DoS and source code exposure bugs in React Server Components and Next.js: whatโs affected and how to update safely.

Security News
Socket CEO Feross Aboukhadijeh joins Software Engineering Daily to discuss modern software supply chain attacks and rising AI-driven security risks.