#!/usr/bin/env python # coding: utf-8 # # **Soynlp 의 Noun Extractor ver 2()** # > ! pip install soynlp # # 1. **Unsupervised tokenizers** in **[soynlp project](https://lovit.github.io/nlp/2018/04/09/three_tokenizers_soynlp/)** # 1. 비지도 학습방법을 활용한 **명사 Token 추출 방법을** 익힙니다. # ## **0 MeCab() 을 활용한 미등록 명사 추출** # - ! pip show soynlp # - ! cat ./data/petition.csv | head -n 2 # 내용 살펴보기 # - 개별 모듈의 사전의 내용과 성격에 따라 결과가 다른만큼 보정이 어려운 단점이 있습니다 # In[1]: # -*- encoding:utf8 -*- import konlpy from konlpy.tag import Mecab print('konlpy version = %s' % konlpy.__version__) sent = '트와이스와 아이오아이가 활동시절 박근혜 최순실의 국정농단 사태는 대통령탄핵으로 이어졌습니다' "MeCab() 명사추출 :", Mecab().nouns(sent)[:6] # ## **1 Noun Extractor ver 2() 학습모델 만들기** # 1. **2016년 까지 수집한 뉴스 텍스트를** 바탕으로 모델을 생성합니다. # 1. **어절간의 결합 엔트로피를** 계산합니다. **단어간 경계에 엔트로피가 크게 증가함을** 근거로 단어를 구분 합니다 # 1. **nouns** 는 **{ str : namedtuple }** 형식으로 추출 됩니다. # In[3]: get_ipython().run_cell_magic('time', '', "from soynlp.utils import DoublespaceLineCorpus\nfrom soynlp.noun import LRNounExtractor_v2\n\ncorpus_path = './data/news_2016-10-20.txt'\ncorpus = DoublespaceLineCorpus(corpus_path, iter_sent=True) # Soynlp Corpus 변환\nnoun_extractor = LRNounExtractor_v2(verbose=True) # Soynlp 모델의 정의\nnouns = noun_extractor.train_extract(corpus) # 모델의 훈련\n") # In[4]: list(noun_extractor._compounds_components.items())[:5] # In[5]: nouns['뉴스'] # NounScore(frequency=4319, score=1.0) # In[6]: nouns['부산과학기술기획평가'] # ## **2 학습모델로 새로운 명사 추출하기** # 1. **2016년 까지 수집한 뉴스 텍스트를** 바탕으로 모델을 생성합니다. # 1. **어절간의 결합 엔트로피를** 계산합니다. **단어간 경계에 엔트로피가 크게 증가함을** 근거로 단어를 구분 합니다 # 1. **nouns** 는 **{ str : namedtuple }** 형식으로 추출 됩니다. # In[7]: # nouns['두바이월드센터시카고옵션거래소'] noun_extractor.decompose_compound('두바이월드센터시카고옵션거래소를') # In[14]: # nouns['두바이월드센터시카고옵션거래소'] noun_extractor.decompose_compound('햄버거짬뽕') # In[7]: noun_extractor.decompose_compound('한국아이티비지니스진흥협회의') # In[8]: # 단 영어가 포함된 경우에는 엔트로피 측정이 잘 안됨에 주의 noun_extractor.decompose_compound('한국IT비지니스진흥협회에서') # In[9]: # 학습내용과 무관한 단어들은 구분능력이 떨어짐 (당연하지..) noun_extractor.decompose_compound('복만두를강냉이와') # In[10]: noun_extractor.lrgraph.get_r('아이오아이') # In[11]: # topk=10 으로 설정되어 있습니다. topk < 0 으로 설정하면 모든 R set 이 출력됩니다. noun_extractor.lrgraph.get_r('아이오아이', topk=-1) # In[12]: # L-R 구조의 L parts 도 확인할 수 있습니다. 이 역시 topk=10 으로 기본값이 설정되어 있습니다. noun_extractor.lrgraph.get_l('었다고') # ## **3 LRGraph 를 활용한 데이터의 L-R 구조 확인** # **soynlp.utils** 의 **LRGraph** 를 이용하면 **데이터의 L-R 구조를** 살펴볼 수 있습니다. # 1. 한국어의 어절은 **L(의미단어) + [R](문법단어)** 구조입니다. # 1. 문법은 사회적 약속이고 쉽게 바뀌지 않아 **closed class** 를 사용 합니다. # 1. 데이터에서 추출하는 **새로운 개념의 단어** 를 **L part** 라고 합니다. # # https://github.com/lovit/soynlp/blob/6632449319eac3e51b3f6ffbc1cf65dd7096678e/tutorials/wordextractor_lecture.ipynb # In[16]: get_ipython().run_cell_magic('time', '', "from soynlp.word import WordExtractor\n\nword_extractor = WordExtractor(\n min_frequency=100,\n min_cohesion_forward=0.05, \n min_right_branching_entropy=0.0\n)\n\nprint('num sentences = {:,}'.format(len(corpus)))\nword_extractor.train(corpus)\nwords = word_extractor.extract()\nlen(words)\n") # In[17]: import math def word_score(score): return (score.cohesion_forward * math.exp(score.right_branching_entropy)) print('단어 (빈도수, cohesion, branching entropy)\n') for word, score in sorted(words.items(), key = lambda x:word_score(x[1]), reverse = True)[:30]: print('{:}\t({:5,}, {:.3f}, {:.3f})'.format( word, score.leftside_frequency, score.cohesion_forward, score.right_branching_entropy)) # In[20]: get_ipython().run_cell_magic('time', '', "# Cohesion score, Branching Entropy, Accessor Variety 에 대하여 각각의 점수만 이용하고 싶은 경우에는 다음의 함수를 이용합니다\ncohesion_scores = word_extractor.all_cohesion_scores()\ncohesion_scores['아이오아이'] # (cohesion_forward, cohesion_backward)\n") # In[24]: get_ipython().run_cell_magic('time', '', "# Cohesion score, Branching Entropy, Accessor Variety 에 대하여 각각의 점수만 이용하고 싶은 경우에는 다음의 함수를 이용합니다\ncohesion_scores = word_extractor.all_cohesion_scores()\ncohesion_scores['아이오아이'] # (cohesion_forward, cohesion_backward)\n") # In[16]: get_ipython().run_cell_magic('time', '', "branching_entropy = word_extractor.all_branching_entropy()\nbranching_entropy['아이오아이'] # (left_branching_entropy, right_branching_entropy)\n") # In[17]: get_ipython().run_cell_magic('time', '', "accessor_variety = word_extractor.all_accessor_variety()\naccessor_variety['아이오아이'] # (left_accessor_variety, right_accessor_variety)\n") # ## **4 명사추출 구조화 방식에 따른 함수의 적용** # https://gist.github.com/lovit/b873e1fbc196e0e8f383414f54b1122e # 1. **NonuLMatchTokenizer** 는 어절의 **왼쪽부분에만 명사가 등장한다는** 가정을 한 모델 # 1. **NounMatchTokenizer** 는 어절의 **왼쪽이 아닌 부분에서 명사가 시작할 수 있다고** 가정한 모델입니다. # In[25]: get_ipython().run_cell_magic('time', '', 'from soynlp.tokenizer import NounLMatchTokenizer, NounMatchTokenizer\n\n# noun_scores[noun] = (score, frequency) : 학습을 위한 데이터로 변환 합니다.\nnoun_scores = noun_extractor.train_extract(corpus)\nlmatch_tokenizer = NounLMatchTokenizer(nouns = noun_scores)\n') # In[26]: lmatch_tokenizer.tokenize('오늘의 연합뉴스날씨입니다.') # In[28]: lmatch_tokenizer.tokenize('마파두부야채볶음') # In[20]: lmatch_tokenizer.tokenize('오늘의 연합뉴스날씨입니다.', compose_compound=False) # In[21]: lmatch_tokenizer.tokenize('오늘의 연합뉴스 날씨입니다.') # In[22]: lmatch_tokenizer.tokenize('오늘의 ㅋ연합뉴스날씨입니다.') # NounMatchTokenizer() 는 위 함수대로 작동하지 않습니다, # match_tokenizer = NounMatchTokenizer(noun_scores = noun_scores) # match_tokenizer.tokenize('오늘의 연합뉴스날씨입니다.') # match_tokenizer.tokenize('오늘의 ㅋ연합뉴스 날씨입니다.') # match_tokenizer.tokenize('오늘의 ㅋ연합뉴스날씨입니다.', compose_compound=False) #

# # **명사추출 및 WordCloud 실습** # https://lovit.github.io/nlp/2018/04/09/three_tokenizers_soynlp/ # # ## **1 데이터 불러오기** # 1. **[국민 청원 데이터](https://s3.ap-northeast-2.amazonaws.com/data10902/petition/petition.csv)** 를 활용한 분석 # 1. 특정 주제의 청원들로 **Filtering** 합니다 # In[24]: import re import pandas as pd df = pd.read_csv('./data/petition.csv', parse_dates=['start', 'end']) p = r'.*(대출|집값|부동산|LTV).*' estate = df[ df['title'].str.match(p) | df['content'].str.match( p, flags=re.MULTILINE) ].reset_index(drop=True) print(estate.shape) # 특정 인덱스 문서를 Sample 로 추출 합니다. sample_index = 15 # 샘플문서 인덱스 sample_title = estate['title'][sample_index] sample_content = estate['content'][sample_index] sample_title, sample_content[:100] # ## **2 Soynlp 를 활용한 Word Extractor** # 1. Word Extractor 에서 학습 알고리즘은 Cohesion score, Branching Entropy, Accessor Variety 있습니다. # 1. 이 포스트에서는 soynlp.word.WordExtractor 의 사용법에 대하여 이야기 합니다. # In[25]: # 토큰화 from soynlp.tokenizer import RegexTokenizer, LTokenizer, MaxScoreTokenizer tokenizer = RegexTokenizer() tokened_title = tokenizer.tokenize(sample_title) tokened_content = tokenizer.tokenize(sample_content) print(len(tokened_title), len(tokened_content)) tokened_title, tokened_content[:10] # In[26]: get_ipython().run_cell_magic('time', '', "# Preprocessing (개행문자 제거)\ndef preprocessing(text):\n text = re.sub('\\\\\\\\n', ' ', text)\n return text\n\nsentences = estate['content'].apply(preprocessing)\ntokens = sentences.apply(tokenizer.tokenize)\ntokens[:3], tokens[sample_index][:10]\n") # In[27]: get_ipython().run_cell_magic('time', '', 'from wordcloud import WordCloud\nimport matplotlib.pyplot as plt\n\ndef displayWordCloud(data = None, backgroundcolor = \'white\', width=800, height=300):\n wordcloud = WordCloud( font_path = fontpath,\n background_color = backgroundcolor, \n width = width, height = height).generate(data)\n plt.figure(figsize = (12 , 8))\n plt.imshow(wordcloud); plt.axis("off"); plt.show()\n\n%matplotlib inline\nimport matplotlib.font_manager as fm\nfontpath = "/home/markbaum/.local/share/fonts/D2Coding.ttf"\nfont = fm.FontProperties(fname=fontpath, size=9)\ndisplayWordCloud(\' \'.join(sentences))\n') # ## **3 Soynlp 를 활용하여 명사만 추출하기** # 1. Word Extractor 에서 학습 알고리즘은 Cohesion score, Branching Entropy, Accessor Variety 있습니다. # 1. 이 포스트에서는 soynlp.word.WordExtractor 의 사용법에 대하여 이야기 합니다. # In[28]: get_ipython().run_cell_magic('time', '', '# 추출된 명사를 찍어봅니다.\nfrom soynlp.noun import LRNounExtractor\nnoun_extractor = LRNounExtractor(verbose=True)\nnoun_extractor.train(sentences)\nnouns = noun_extractor.extract()\n\nprint("추출한 명사갯수 {:,}".format(len(nouns)))\ndisplayWordCloud(\' \'.join(nouns))\n')