반응형

안녕하세요.

저번 sklearn LDA 토픽 모델링 게시물에 이어서 이번에는 선정한 토픽에 대해

기간별로 어떻게 변화하는지를 확인해 보도록 하겠습니다.

 

데이터 준비

데이터는 저번 게시물에서 사용한 데이터를 사용하겠습니다.

- 데이터: 공공데이터 포탈의 '공정거래위원회_소비자 민원학습데이터 처리기관별 소비자 상담내역' 

링크 들어가셔서

'공정거래위원회_소비자 민원학습데이터 처리기관별 소비자 상담내역_20211227.csv' 

파일을 다운받아 주세요.

import pandas as pd
df = pd.read_csv('공정거래위원회_소비자 민원학습데이터 처리기관별 소비자 상담내역_20211227.csv',encoding='cp949')
print(df.head())
print(df.shape)

약 5만개의 행으로 이루어져 있습니다.

이 데이터에서 날짜 데이터도 있고 기타 데이터도 많지만,

토픽 모델링은 사건 제목을 가지고 해보도록 하겠습니다.

#토픽 토크나이저를 위해 필요한 부분 (사건제목) 추출
contents= df['사건제목(ACCIDENT_TITLE)']
contents

텍스트 토크나이징 및 카운트 벡터화

이제 이 텍스트를 가지고 sklearn을 사용하여 텍스트 토큰화 및 카운트 벡터화를 하겠습니다.

카운트 벡터는 sklearn.feature_extraction.text.CountVectorizer를 사용하여 생성할 수 있습니다.

저번 게시글과 마찬가지로, max_df=0.5로, min_df를 5로 지정하고 max_features 최대 2000개까지만 카운트벡터를 생성해 보겠습니다.

from konlpy.tag import Okt
from sklearn.feature_extraction.text import CountVectorizer
###형태소 분석###
okt = Okt()
def tokenizer(text):
    #명사 추출, 2글자 이상 단어 추출
    return [ word for word in okt.nouns(text) if len(word)>1]
Count_vector = CountVectorizer(tokenizer=tokenizer,
                              max_df = 0.5,
                              min_df = 5,
                              max_features = 2000)

%time minwon_cv = Count_vector.fit_transform(contents) 
print(minwon_cv[:5])
print(minwon_cv.shape)

이렇게 minwon_cv라는 민원 제목과 관련된 카운트벡터를 만들었습니다.

최적 토픽 수 선정하기

이번에는 토픽 수를 선정해 보겠습니다.

sklearn를 사용하는 경우, 혼란도만 제공하고 있는데, tmtoolkit을 사용하면 응집도도 구할 수 있습니다.

토픽에 따른 혼란도, 응집도 및 토픽 내용을 print해주는 함수를 하나 만들어 실행해 보도록 하겠습니다.토픽 수는 3에서 10까지 늘려 값을 확인해 보겠습니다.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import LatentDirichletAllocation
from tmtoolkit.topicmod.evaluate import metric_coherence_gensim

# 토픽별 단어를 보여주는 함수
def print_top_words(model, feature_names, n_top_words):
    for topic_idx, topic in enumerate(model.components_):
        print("토픽 #%d: " % topic_idx, end='')
        print(", ".join([feature_names[i]
                        for i in topic.argsort()[:-n_top_words -1:-1]]))
                        
# 혼란도 및 응집도 구하는 함수
def calc_pv_coherence(countVector,vocab, start=5, end=30, max_iter=10,topic_wp=0.1, doc_tp=1.0):
    num = []
    per_value = []
    cor_value = []
    for i in range(start,end+1):
        lda = LatentDirichletAllocation(n_components=i,max_iter=max_iter,
                                       topic_word_prior=topic_wp,
                                       doc_topic_prior=doc_tp,
                                       learning_method='batch', n_jobs=-1,
                                       random_state=0)
        lda.fit(countVector)
        num.append(i)
        pv = lda.perplexity(countVector)
        per_value.append(pv)
        coherence_score = metric_coherence_gensim(measure='u_mass',
                                                  top_n=10,
                                                  topic_word_distrib=lda.components_,
                                                 dtm=countVector,
                                                 vocab=vocab,
                                                 texts=None)
        cor_value.append(np.mean(coherence_score))
        
        print(f'토픽 수: {i}, 혼란도: {pv:0.4f}, 응집도:{np.mean(coherence_score):0.4f}')
        print_top_words(lda, vocab,10)
    
    plt.plot(num,per_value,'g-')
    plt.xlabel("topic_num")
    plt.ylabel("perplexity")
    plt.show()
    
    plt.plot(num,cor_value,'r--')
    plt.xlabel("topic_num")
    plt.ylabel("coherence_score")
    plt.show()
    return per_value,cor_value
per_values, cor_values = calc_pv_coherence(minwon_cv,names,start=3,end=10)

이렇게 토픽 갯수별 토픽 단어들이 나오고,

토픽별 혼란도, 응집도를 확인했을 때 최적의 토픽 수는 4 로 보입니다.

선정된 토픽 갯수로 LDA 토픽 모델링 하기

sklearn LDA를 사용해서 4개의 토픽을 가지고 있는 모델을 만들어 보겠습니다.

from sklearn.decomposition import LatentDirichletAllocation
lda = LatentDirichletAllocation(n_components=4,
                               n_jobs= -1,
                               random_state=0,
                               max_iter=10,
                                topic_word_prior=0.1,
                               doc_topic_prior=1.0,
                               learning_method='batch',
                               )
minwon_topics = lda.fit_transform(minwon_cv)

토픽 모델링 한 결과는 아래와 같습니다. 각 토픽별로 상위 20개 단어를 보도록 하겠습니다.

print_top_words(lda, Count_vector.get_feature_names_out(),20)

 

트렌드 확인하기

이제 이 민원 토픽들을 가지고 시간별로 어떤 종류의 토픽이 많이 나왔는지 확인하기 위한 트렌드 분석을 진행해 보겠습니다.

날짜 데이터는 '등록일자(RCPT_YMD)' 컬럼에 있습니다. 

행이 5만개가 되기 때문에 날짜를 월단위까지만 자른 데이터를 토픽 분포 데이터와 합치도록 하겠습니다.

from sklearn.feature_extraction.text import CountVectorizer
trend_data = pd.DataFrame(minwon_topics,columns=['토픽'+str(i) for i in range(1,5)])
trend_data = pd.concat([trend_data,df['등록일자(RCPT_YMD)'].map(lambda x:x[:7])], axis=1)
trend_data.head()

이후 groupby를 사용하여 월 단위로 토픽의 평균을 구해보겠습니다.

#월별 평균 구하기
trend = trend_data.groupby(['등록일자(RCPT_YMD)']).mean()
trend.head()

이제 월별로 토픽 비중이 어떻게 변했는지 확인하기 위해 matplotlib을 사용하여 그래프를 그려 보도록 하겠습니다.

matplotlib의 AutoDateLocator를 사용하여 x축은 6개월 단위로 눈금 및 값을 표시해서 보여주도록 하겠습니다.

import matplotlib
import matplotlib.pyplot as plt
from matplotlib import font_manager,rc
from matplotlib.dates import AutoDateLocator,MonthLocator
# 한글 폰트 설정
font_location = 'C:/Windows/Fonts/MALGUNSL.TTF' #맑은고딕
font_name = font_manager.FontProperties(fname=font_location).get_name()
rc('font',family=font_name)
#날짜설정
locator = AutoDateLocator()
locator.intervald['MONTHLY'] = [6]

print_top_words(lda, Count_vector.get_feature_names_out(),20)

fig,axes = plt.subplots(2,2,figsize=(10,10))
for col,ax in zip(trend.columns.tolist(),axes.ravel()):
    ax.set_title(col)
    ax.xaxis.set_major_locator(locator)
    ax.axes.xaxis.set_visible(True)
    
    ax.plot(trend[col])

plt.show()

4개의 토픽에서 토픽별로 시간에 따라 분포가 어떻게 변화했는지 확인할 수 있습니다.

각 토픽 주제는 대략적으로

토픽 1: 피해 상담 문의

토픽2: 환불 및 반품 문의

토픽3: 수리 및 보상 문의

토픽4: 계약 해지 문의 

이렇게 볼 수 있을 것 같습니다.

토픽1 은 최근에 급격히 비중이 늘어났고, 토픽 3번은 시간에 따라 비중이 줄어들고 있고, 토픽 4번은 후반부에 비중이 늘어남을 확인할 수 있습니다.

 

 참고자료

1. 도서 - 파이썬 텍스트 마이닝 완벽 가이드(위키북스)

2.  https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.LatentDirichletAllocation.html

 

sklearn.decomposition.LatentDirichletAllocation

Examples using sklearn.decomposition.LatentDirichletAllocation: Topic extraction with Non-negative Matrix Factorization and Latent Dirichlet Allocation Topic extraction with Non-negative Matrix Fac...

scikit-learn.org

3. https://matplotlib.org/stable/api/dates_api.html

 

matplotlib.dates — Matplotlib 3.6.3 documentation

matplotlib.dates Matplotlib provides sophisticated date plotting capabilities, standing on the shoulders of python datetime and the add-on module dateutil. By default, Matplotlib uses the units machinery described in units to convert datetime.datetime, and

matplotlib.org

 

반응형
반응형

안녕하세요. 저번시간에는 토픽 모델링을 할 때 gensim으로 하는 방법을 알아보았는데,

이번에는 sklearn을 활용하여 LDA를 하는 방법을 알아보도록 하겠습니다.

 

​데이터 준비

공공데이터 포탈의 '공정거래위원회_소비자 민원학습데이터 처리기관별 소비자 상담내역' 을 활용하여

LDA를 해보도록 하겠습니다.

아래의 링크에서 csv파일을 다운받아 주세요.

https://www.data.go.kr/data/15098351/fileData.do#tab-layer-file

 

공정거래위원회_소비자 민원학습데이터 처리기관별 소비자 상담내역_20211227

공정거래위원회의 소비자 민원학습 데이터로, 소비자들의 민원상담을 처리한 기관별 상담데이터를 보여주는 데이터입니다. 이 데이터는 기관명 상위기관을 포함하고 있습니다.

www.data.go.kr

import pandas as pd
df = pd.read_csv('공정거래위원회_소비자 민원학습데이터 처리기관별 소비자 상담내역_20211227.csv',encoding='cp949')
print(df.head())
print(df.shape)

약 5만개의 행으로 이루어져 있습니다.

여기서 사용할 컬럼은 사건 제목을 사용하여 토픽 모델링을 진행해 보도록 하겠습니다.

#토픽 토크나이저를 위해 필요한 부분 (사건제목) 추출
contents= df['사건제목(ACCIDENT_TITLE)']
contents

행별로 처리결과라는 카테고리가 있는데 그것이 몇개 있는지 확인해보도록 하겠습니다.

#카테고리 확인
df.groupby('처리결과명(PRCS_RESULT_NAME)').count()
print(len(df.groupby('처리결과명(PRCS_RESULT_NAME)').count()))

27

총 27개의 카테고리가 있습니다.

 

텍스트 토크나이징 및 카운트 벡터화

이제 이 텍스트를 가지고 sklearn을 사용하여 텍스트 토큰화 및 카운트 벡터화를 하겠습니다.

카운트 벡터는 sklearn.feature_extraction.text.CountVectorizer를 사용하여 생성할 수 있습니다.

tokenizer 함수를 하나 만들어 주고,

CountVectorizer의 tokenizer에 해당 토큰화함수를 입력하고,

max_df는 gensim에서 사용했던 no_above처럼 일정 %이상 빈도가 너무 자주 등장하는 것을 제외하고,

min_df는 no_below처럼 몇 회 미만으로 나온 단어를 제외합니다.

이번에는 max_df=0.5로, min_df를 5로 지정하고 max_features 최대 2000개까지만 카운트벡터를 생성해 보겠습니다.

from konlpy.tag import Okt
from sklearn.feature_extraction.text import CountVectorizer
###형태소 분석###
okt = Okt()
def tokenizer(text):
    #명사 추출, 2글자 이상 단어 추출
    return [ word for word in okt.nouns(text) if len(word)>1]
Count_vector = CountVectorizer(tokenizer=tokenizer,
                              max_df = 0.5,
                              min_df = 5,
                              max_features = 2000)

%time minwon_cv = Count_vector.fit_transform(contents) 
print(minwon_cv[:5])
print(minwon_cv.shape)

이렇게 minwon_cv라는 민원 제목과 관련된 카운트벡터를 만들었습니다.

 

최적 토픽 수 선정을 위해 혼란도, 응집도 구하기

이번에는 토픽 수를 선정해 보겠습니다.

sklearn의 경우, 혼란도만 제공하고 있는데, tmtoolkit을 사용하면 응집도도 구할 수 있습니다.

tmtoolkit 설치는 아래의 명령어를 입력하여 터미널에서 설치해 주시면 됩니다.

pip install --user tmtoolkit

sklearn에서 LDA는 sklearn.decomposition.LatentDirichletAllocation 을 사용하여 구할 수 있습니다.

sklearn LDA의 n_components는 추출할 토픽의 수를 의미하고,

max_iter는 알고리즘의 최대 반복 횟수(gensim의 passes라고 생각하면 됩니다.),

topic_word_prior은 베타값(토픽의 사전 단어분포),

doc_topic_prior은 알파값(문서의 사전 토픽분포),

learning_method는 학습방법으로 online과 batch가 있습니다.

n_jobs는 모델 사용시 사용하는 프로세서의 수를 의미하며 -1로 설정하면 모든 프로세서를 사용할 수 있습니다.

이번에는 max_iter를 10으로 설정하고,

topic_word_prior와 doc_topic_prior은 아래의 논문

https://www.ncbi.nlm.nih.gov/pmc/articles/PMC387300/

 

Colloquium Paper: Mapping Knowledge Domains: Finding scientific topics

A first step in identifying the content of a document is determining which topics that document addresses. We describe a generative model for documents, introduced by Blei, Ng, and Jordan [Blei, D. M., Ng, A. Y. & Jordan, M. I. (2003) J. Machine ...

www.ncbi.nlm.nih.gov

을 참고하여 각각 0.1, 1.0으로 설정해 보도록 하겠습니다.

random_state는 0으로 설정해준 뒤,

n_components는 5부터 30까지의 값을 넣어 실행한 뒤,

혼란도, 응집도 값을 각각 구해 matplotlib으로 시각화 해보도록 하겠습니다.

혼란도는 sklearn LDA 모형의 .perplexity로 값을 구하고,

응집도는 tmtoolkit.topicmod.evaluate.metric.coherence_gensim을 사용하여 구하도록 하겠습니다.

coherence score에서 measure은 'u_mass'를 사용하였습니다.

import numpy as np
import matplotlib.pyplot as plt
from sklearn.decomposition import LatentDirichletAllocation
from tmtoolkit.topicmod.evaluate import metric_coherence_gensim
def calc_pv_coherence(countVector,vocab, start=5, end=30, max_iter=10,topic_wp=0.1, doc_tp=1.0):
    num = []
    per_value = []
    cor_value = []
    for i in range(start,end+1):
        lda = LatentDirichletAllocation(n_components=i,max_iter=max_iter,
                                       topic_word_prior=topic_wp,
                                       doc_topic_prior=doc_tp,
                                       learning_method='batch', n_jobs=-1,
                                       random_state=0)
        lda.fit(countVector)
        num.append(i)
        pv = lda.perplexity(countVector)
        per_value.append(pv)
        coherence_score = metric_coherence_gensim(measure='u_mass',
                                                  top_n=10,
                                                  topic_word_distrib=lda.components_,
                                                 dtm=countVector,
                                                 vocab=vocab,
                                                 texts=None)
        cor_value.append(np.mean(coherence_score))
        
        print(f'토픽 수: {i}, 혼란도: {pv:0.4f}, 응집도:{np.mean(coherence_score):0.4f}')
    
    plt.plot(num,per_value,'g-')
    plt.xlabel("토픽 수:")
    plt.ylabel("혼란도: ")
    plt.show()
    
    plt.plot(num,cor_value,'r--')
    plt.xlabel("토픽 수:")
    plt.ylabel("응집도: ")
    plt.show()
    return per_value,cor_value

함수를 이렇게 만들고 아래와 같이 호출해 줍니다.

per_values, cor_values = calc_pv_coherence(minwon_cv,Count_vector.get_feature_names_out(),start=5,end=30)

시작 토픽은 5, 끝나는 토픽 수는 30으로 설정한 뒤 실행해 줍니다.

여기서 시간이 2~3시간정도 걸릴 수 있습니다.

 

이렇게 각 토픽에 대한 혼란도, 응집도가 나오고,

위의 그래프는 토픽 수에 따른 혼란도를,

이 그래프는 토픽 수에 따른 응집도(u_mass)를 보여 줍니다.

여기서 적절한 토픽 수 찾기가 좀 어려웠는데요,

혼란도는 적을수록 좋은데 이 그래프에서는 토픽수가 늘어날수록 혼란도도 늘어나고,

응집도는 0에 가까울수롤 좋은데 응집도 그래프에서는 토픽이 29개일 때 응집도가 가장 0에 가깝기 때문입니다.

저는 여기서 응집도를 기준으로 가장 높았던 토픽 수 29로 설정해 보았습니다.

만약 다른 갯수로 설정하고 싶다면 n_components를 해당 토픽 갯수로 바꿔서 진행하면 됩니다.

 

결정된 토픽 수로 토픽 모델링 하기
lda = LatentDirichletAllocation(n_components=29,
                               n_jobs= -1,
                               random_state=0,
                               max_iter=10,
                                topic_word_prior=0.1,
                               doc_topic_prior=1.0,
                               learning_method='batch')
%time minwon_topics = lda.fit_transform(minwon_cv)

토픽 수 29로 LDA모델을 만들고 fit한 후 토픽을 뽑아보면,

for topic_idx, topic in enumerate(lda.components_):
    print("토픽 #%d: " % topic_idx, end='')
    print(", ".join([Count_vector.get_feature_names_out()[i] for i in topic.argsort()[:10]]))

이렇게 결과가 나옵니다. 하지만 이는 비중이 작은 순서라서 다시 가장 비중이 큰 10개만 다시 뽑아보면,

for topic_idx, topic in enumerate(lda.components_):
    print("토픽 #%d: " % topic_idx, end='')
    print(", ".join([Count_vector.get_feature_names_out()[i] for i in topic.argsort()[:-11:-1]]))

비슷한 단어가 많이 겹치는 것을 확인할 수 있습니다.

단순히 응집도 점수만 봤을 때 이런 결과가 나오니 다른 토픽 수도 확인하여 결정하는 것이 좋을 것 같습니다.

 

참고자료

1. 도서 - 파이썬 텍스트 마이닝 완벽 가이드(위키북스)

2. https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.LatentDirichletAllocation.html

 

sklearn.decomposition.LatentDirichletAllocation

Examples using sklearn.decomposition.LatentDirichletAllocation: Topic extraction with Non-negative Matrix Factorization and Latent Dirichlet Allocation Topic extraction with Non-negative Matrix Fac...

scikit-learn.org

3. https://alvinntnu.github.io/NTNU_ENC2045_LECTURES/nlp/topic-modeling-naive.html#additional-notes

 

2. Topic Modeling: A Naive Example — ENC2045 Computational Linguistics

Simple Text Pre-processing Depending on the nature of the raw corpus data, we may need to implement more specific steps in text preprocessing. In our current naive example, we consider: removing symbols and punctuations normalizing the letter case strippin

alvinntnu.github.io

 

반응형
반응형

안녕하세요.

이번에는 한국어 데이터셋을 가지고 gensim을 사용한 LDA 토픽 모델링 실습을 해보도록 하겠습니다.

 

데이터 준비

테이터셋으로 '청와대 국민청원' 에 만료된 청원 데이터를 사용하도록 하겠습니다.

아래 사이트 접속 후 

https://github.com/akngs/petitions

 

GitHub - akngs/petitions: 청와대 국민청원 데이터

청와대 국민청원 데이터. Contribute to akngs/petitions development by creating an account on GitHub.

github.com

petition_sampled.csv 를 클릭하여 다운받아 주세요.

전체 데이터는 데이터 용량이 커서 샘플 데이터를 사용하겠습니다.

 

파일 불러오기

다운받은 파일중 5개만 예시로 출력해보겠습니다.

import pandas as pd
df = pd.read_csv('petition_sampled.csv')
df.head()

데이터는 위와 같이 구성되어 있는 것을 확인할 수 있습니다.

여기서 실습에서는 청원 내용을 가지고 토픽 모델링을 해보도록 하겠습니다.

때문에 content컬럼만 따로 뽑아줍니다.

contents = df['content']
contents

또한 원본 데이터에 카테고리 항목이 있는데 

데이터가 몇 개의 카테고리가 있는지도 한번 확인해 보겠습니다.

groupby를 사용하여 카테고리가 몇 개 있는지 확인해 보겠습니다.

len(df.groupby('category').votes.sum())

이렇게 17개의 카테고리가 있는 것을 확인할 수 있습니다.

참고로 가장 많은 votes 수의 카테고리는 '인권/성평등' 카테고리임을 확인할 수있습니다.

 

 토픽 모델링을 위한 텍스트 토큰화(형태소분석)하기

여기서는 Gensim을 사용하여 LDA 토픽 모델링을 해 볼 건데요,

gensim을 사용하면 혼란도와 토픽 응집도를 간단하게 구할 수 있어 많이 사용됩니다.

pip install gensim

을 터미널에 입력하여 설치할 수 있습니다.

gensim은 입력값으로 텍스트를 그대로 입력하는 것이 아닌 토큰화된 결과를 입력값으로 사용하므로

청원 내용을 형태소 분석을 통해 토큰화 해보도록 하겠습니다.

형태소 분석으로는 konlpy의 Okt를 사용하여 분석해 보겠습니다. 

단어 중 '명사' 및 2글자 이상의 단어만 추출해 보도록 하겠습니다.

from konlpy.tag import Okt
###형태소 분석###
okt = Okt()
def analysis_pos(text):
    morphs = okt.pos(text,stem=True)

    words = []
    #명사 추출, 2글자 이상 단어 추출
    for word, pos in morphs:
        if pos == 'Noun':
            if len(word) > 1:
                words.append(word)
    return words

texts = [analysis_pos(news) for news in contents]

이렇게 함수를 만들어 texts 변수 안에 토큰화한 텍스트를 저장하였습니다.

 

토큰화 된 텍스트로 gensim Dictionary 만들기

gensim LDA에서 id2word에 사용하기 위해 토큰화된 텍스트로 사전을 만들어 보겠습니다.

from gensim.corpora.dictionary import Dictionary
# 형태소 분석으로 토큰화 후 dictionary 생성
dictionary = Dictionary(texts)
print("#문서에 있는 단어 수: %d개"%len(dictionary))

문서에 있는 단어 수가 3만개가 넘는데요,

이를 filter_extremes를 사용해 단어를 2000개까지 설정하고 5개 미만으로 나온 단어는 제외, 전체 50% 이상으로 많이 나오는 단어들도 제외를 해보도록 하겠습니다.

keep_n 으로 단어 갯수를 설정하고, no_below로 일정 횟수 미만으로 나온 단어는 제외, no_above로 일정 비율 이상으로 나오는 단어들도 제외하도록 설정할 수 있습니다.

물론 다른 비율 또는 횟수, 갯수로 바꿔서 필터링 할 수 도 있습니다.

# 너무 많은 단어나 너무 적은 단어를 제외하고, 단어 빈도순으로 정리
dictionary.filter_extremes(keep_n=2000, no_below=5, no_above=0.5)
print("#너무 많은 단어 및 적은 단어를 제외하고 문서에 남아 있는 단어 수: %d개"%len(dictionary))

카운트벡터로 변환하기

gensim LDA 에 사용하기 위한 corpus를 생성하려면 doc2bow를 사용하여 카운트 벡터로 변환하여야 합니다.

gensim에서는 토큰화 된 결과를 texts라 하고 이것을 카운트 벡터로 변환한 것을 corpus라고 합니다.

# 카운트 벡터로 변환
corpus = [dictionary.doc2bow(text) for text in texts]
print("#최종적으로 문서에 있는 단어 수: %d개"%len(dictionary))
print("#카운트 벡터 수: %d개"%len(corpus))

Gensim LDA 모델 생성

gensim의 LdaModel을 사용해서 LDA 모델링을 수행할 수 있습니다.

num_topics은 토픽의 수를, passes는 말뭉치 전체를 학습하는 횟수, corpus는 토큰화한 값들을 카운트 벡터화 한것, id2word는 위에서 만든 dictionary를 입력하면 됩니다.

우선 원본 데이터가 총 17개의 카테고리를 가지고 있으므로,

num_topics를 17로 설정하여 모델링을 수행해 보도록 하겠습니다.

from gensim.models import LdaModel
num_topics = 17
passes = 5
model = LdaModel(corpus=corpus,id2word=dictionary,\
    passes=passes, num_topics=num_topics,\
        random_state=7)

그 후 모델링 한 결과를 각 토픽당 10개의 단어씩 뽑아서 보도록 하겠습니다.

model.print_topics(num_words=10)

pyLDAvis를 사용한 시각화

위의 모델링 결과를 한눈에 보기 쉽게 pyLDAvis를 사용하여 시각화할 수 있습니다.

설치는 pip install pyLDAvis 로 설치 가능합니다.

import pyLDAvis
import pyLDAvis.gensim_models as gensimvis
pyLDAvis.enable_notebook()

#LDA모형 시각화
lda_visual= gensimvis.prepare(model,corpus,dictionary)
pyLDAvis.display(lda_visual)

최적의 토픽 수 선정하기

원본이 17개 카테고리여서 num_topics를 17로 설정했지만 실제 분석시에는 카테고리조차 없는 데이터들도 있을 것입니다. 

또한 위의 결과도 최적의 토픽 개수를 설정한 것인지 알 수 없으므로 혼란도와 토픽응집도를 구해서 살펴보도록 하겠습니다.

gensim에서 .log_perplexity를 사용하면 혼란도를, CoherenceModel로 토픽응집도를 간편히 구할 수 있습니다.

여기서는 혼란도와 토픽 응집도를 구하는 함수를 만들고

for문을 사용하여 토픽 갯수당 각각의 혼란도, 응집도를 구해 그래프로 시각화 후 최적화를 진행해보겠습니다.

import matplotlib.pyplot as plt
from gensim.models import CoherenceModel
def coherences(corpus, dictionary, start=6,end=15):
    iter_num=[]
    per_value=[]
    coh_value=[]
    for i in range(start, end+1):
        model = LdaModel(corpus=corpus, id2word=dictionary,
                        chunksize=1000, num_topics=i,
                        random_state=7)
        iter_num.append(i)
        pv = model.log_perplexity(corpus)
        per_value.append(pv)
        CM = CoherenceModel(model=model, corpus=corpus, coherence='u_mass')
        coherence_value = CM.get_coherence()
        coh_value.append(coherence_value)
        print(f'토픽 수: {i}, 혼란도:{pv:0.3f}, 응집도: {coherence_value:0.3f}')
        
    plt.plot(iter_num,per_value,'g-')
    plt.xlabel("num_topics")
    plt.ylabel("perplexity")
    plt.show()
        
    plt.plot(iter_num, coh_value,'r--')
    plt.xlabel("num_topics")
    plt.ylabel("coherence")
    plt.show()

start는 토픽의 시작 갯수를, end는 마지막으로 구할 토픽 갯수입니다.

간단히 말해 for문의 시작과 끝을 설정한다고 볼 수 있습니다.

여기서는 토픽 5개~20개까지로 설정했을 때 각각의 혼란도, 토픽 응집도를 구해 보겠습니다.

coherences(corpus, dictionary, start=5,end=20)

혼란도는 낮을 수록 좋고 토픽 응집도는 0에 가까울수록 좋습니다.

위의 그래프를 보면 혼란도가 가장 낮게 나타난 곳은 17개이고, 토픽 응집도가 가장 0에 가까운 곳은 6개로

차이가 있음을 알 수 있습니다.

최적화가 쉽지 않아보이지만 둘 중 더 토픽을 잘 나타내는 것으로 최종 선택을 할 수 있겠습니다.

혼란도가 가장 낮은 토픽 17개로는 이미 전에 모델링을 실행해 보았으니,

이번에는 응집도가 가장 높은 토픽 6개로 모델링을 해보도록 하겠습니다.

num_topics = 6
passes = 5
model2 = LdaModel(corpus=corpus,id2word=dictionary,\
    passes=passes, num_topics=num_topics,\
        random_state=7)
        
model2.print_topics(num_words=10)

#LDA모형 시각화
lda_visual2= gensimvis.prepare(model2,corpus,dictionary)
pyLDAvis.display(lda_visual2)

이 둘 중에 또는 모델을 조정하여 최종적으로 토픽 수를 정할 수 있겠습니다.

 

코드 파일

kor_LDA.ipynb
0.40MB

참고자료

1. 책- 파이썬 텍스트 마이닝 완벽 가이드(위키북스)

2. https://radimrehurek.com/gensim/models/coherencemodel.html

 

Gensim: topic modelling for humans

Efficient topic modelling in Python

radimrehurek.com

 

반응형
반응형

안녕하세요.

오늘은 LDA(Latent Dirichlet Allocation)에 대해 알아보도록 하겠습니다.

LDA는 자연어처리에서 토픽 모델링을 할 때 많이 활용되는 방법입니다.

 

토픽 모델링이란?

토픽 모델링은 어떠한 문서가 있다면 그 문서의 주제가 무엇일지에 대한 분석을 가능하게 하는 방법입니다.

보통 단어의 집합으로 토픽을 표현하고 이에 대한 토픽이 무엇일지를 파악할 수 있는데,

가령, '눈' 이라는 단어가 있을 때,

'눈', '얼굴' , '시력' 등의 단어가 같이 있으면 신체 부위인 '눈'을 말하는 것인지 알 수 있고

'눈', '겨울', '흰색' 등의 단어 집합이 있으면 하늘에서 내리는 '눈'이라는 것을 알 수 있습니다.

또한 이렇게 단어들로 토픽을 뽑아 보면 각 토픽의 단어 집합을 통해 해당 문서의 주제가 무엇일 지 유추할 수 있습니다.

 

LDA(Latent Dirichlet Allocation): 잠재 디리클레 할당

처음에 언급했듯이 LDA는 토픽 모델링에서 많이 사용됩니다.

LDA는 문서 작성 시 몇 개의 토픽을 미리 정하고 문서가 작성될 때 이 토픽과 관련된 단어들을 사용한다고 가정합니다.

문서의 구성

LDA 모형의 구조는 아래와 같습니다.

출처: https://ko.wikipedia.org/wiki/잠재디리클레할당

1. α: 디리클레 분포의 매개변수(LDA 하이퍼 파라미터)

2. M: 문서의 수

3. θ: 문서의 토픽 분포(디리클레 분포를 따름)

※ 디리클레 분포: 연속확률분포 중 하나.

k 차원의 실수 벡터 중 벡터의 요소가 양수이며 모든 요소를 더한 값이 1인 경우 실수 벡터에서 각 벡터 값이 양수이고 모든 값을 더하면 1이 되는 경우에 대해 확률값이 정의되는 분포(출처:위키피디아-디리클레 분포)

예시: 문서에 토픽 3개 선정시 토픽의 비중을 [0.4, 0.4, 0.2]로 할당한다. 이 값을 다 더하면 1이 된다.

4. β: 각 토픽의 단어 분포에 관여하는 매개변수( LDA 하이퍼 파라미터)

5. Z: 문서에 있는 토픽

6. W: Z에 있는 단어들

7. N: 문서에 나타난 단어들의 빈도

 

이에 대해 간단히 설명하자면

LDA는 주어진 문서에 사용된 단어(W)들의 빈도(N)를 측정하고, 이로부터 문서의 토픽분포(θ)와 각 토픽의 단어분포를 추정합니다.

또한 LDA를 실행할 때, 토픽의 개수 k와 위에서 언급한 α, β 를 사용자가 설정해주어야 합니다.

위의 설정값들을 어떻게 설정하느냐에 따라 LDA 토픽 모델링의 성능을 높일 수 있습니다.

이때, 적절한 값을 판단하기 위해 혼란도(perplexity)와 토픽 응집도(topic coherence)를 이용합니다.

 

LDA 모형 평가

- 혼란도(perplexity): 사용자가 추정한 LDA모형이 실제 문서 집합과 비교했을 때 얼마나 유사한지 알아보는 척도입니다.

혼란도는 값이 작을수록 문서집합을 잘 반영하고 있다고 해석할 수 있습니다.

- 토픽 응집도(topic coherence): 각 토픽에 속한 단어들 중 높은 비중을 가진 단어들이 의미적으로 서로 유사한지를 나타냅니다.

토픽 응집도는 혼란도와는 반대로, 값이 클수록 좋습니다.(어떤 응집도 방법을 선택하느냐에 따라 차이는 있을 수 있습니다.)

 

성능 평가시 주의할 점

사용자가 모델링한 LDA 모형을 평가 시 가장 중요한 것은

혼란도 또는 토픽 응집도 수치에만 너무 집착하지 않는 것입니다.

LDA를 실행 후 사람의 눈으로 봤을 때 토픽과 그 단어의 구성이 자연스러운 것이 가장 좋은 모델입니다.

때문에 최종적으로 토픽 수 결정 시에는 척도 값만 보고 토픽 수를 결정하기보다,

최적값 근처의 값들을 추가로 선택 후 

그 값들에 대해 모델링을 해보고 결과들을 사용자가 본인의 눈으로 직접 비교하여 최종 값을 선택하는 것이 좋습니다.

 

참고자료

1. 파이썬 텍스트 마이닝 완벽 가이드(책)

2. 위키피디아 - 잠재 디리클레 할당

반응형
반응형

안녕하세요. 

오늘 포스팅은 전에 알아보았던 BERT의 사전학습된 모형을 사용하는 간단한 방법을 알아보도록 하겠습니다.

BERT 사전학습 모델은 허깅페이스(Hugging Face)에서 제공해서 사이트를 이용해보도록 하겠습니다.

 

1. 라이브러리 설치

anaconda prompt 또는 기타 터미널 창을 열어 transformers 라이브러리를 설치해 주세요.

CPU환경에서 사용할 것이므로 아래의 명령어를 입력해 줍니다.

pip install transformers

만약 다운로드 중 권한 오류가 있다면 관리자 권한으로 실행하시길 바랍니다.

가상환경으로 실행하고 싶은 분들은 사용할 가상환경 activate 후 설치하시면 됩니다.

 

2. transformers 파이프라인(pipeline)

transformers 라이브러리에서는 pipeline(파이프라인) 클래스를 제공하는데요,

이 클래스를 사용하면 알아서 입력값을 토크나이저를 이용해 BERT에 맞는 입력으로 변환하여 주고,

BERT 사전학습 모델에 전달하여 예측값을 반환합니다.

때문에 코드 몇 줄 만으로 간단히 사전학습 모델을 사용할 수 있습니다.

pipeline에서 지원하는 사전학습 모델 종류는 아래와 같습니다.

1. Sentiment-analysis : 감정분석
2. Text-generation : 텍스트 생성
3. NER(Named entity recognition): 개체명 인식
4. Question-answering : 기계독해
5. Fill-mask : 빈칸 예측하기
6. Summarization : 문서 요약하기
7. Translation : 번역하기

이번에 입력하는 텍스트는 영어로 입력하여야 합니다.

이제 위의 7가지 사전학습 모델 간단 사용법을 각각 알아보도록 하겠습니다.

참고로, 각 모델을 사용하며 입력한 문장 몇개들은 위키피디아에서 가져왔습니다.

2.1 감정분석

감정분석은 텍스트에 대해 긍정적인 감정을 담고 있는지 부정적인 감정을 담고 있는지 예측하는 모델입니다.

예시)
1. 이 영화는 재미있다 -> positive(긍정)
2. 이 음식은 맛없다. -> negative(부정)
사용방법: pipeline('sentiment-analysis')
from transformers import pipeline

# text classification : 감정분석
classifier= pipeline('sentiment-analysis')
sentence = "I can't wait for watching this movie!" #감성분석하고 싶은 문장 입력
result = classifier(sentence)[0]
print("입력문장: ",sentence)
print('감성분석결과: %s, 감성스코어: %0.2f'%(result['label'],result['score']))

여기서는 I can't wait for watching this movie!(이 영화를 빨리 보고 싶어!) 라는 문장을 예시로 넣어 보았습니다.

그 후 분석결과와 감성 스코어를 소숫점 2자리수까지 출력하도록 설정하였습니다.

그 결과 이 문장은 POSITIVE(긍정적)인 문장이며 점수 역시 1.00이 나왔습니다.

점수는 0~1 사이로, 점수가 1에 가까워질수록 긍정적이라고 할 수 있습니다.

 

2.2 텍스트 생성

텍스트 생성은 어느 한 텍스트를 입력 받아 그 뒤의 문장을 만들어 줍니다.

예시)
입력 문장: 나는 사과를 먹었다.
텍스트 생성: 나는 사과를 먹었다. 그래도 아직 배가 고파서 바나나를 먹었다.
사용방법: pipeline('text-generation')
# text generation : 문장 생성
text_generator = pipeline('text-generation')
sentence = "I was sitting on my bed."
result = text_generator(sentence)
print("생성된 문장: ",result[0]['generated_text'])

입력 문장으로 

"I was sitting on my bed."(나는 내 침대 위에 앉아 있었다.)를 입력 후

텍스트 생성을 하니,

I was sitting on my bed. I could see the moon and it was beautiful. I was sitting on my pillow. It was gorgeous. I looked at my bed. I was wearing my jeans at the side and the top. It was so nice

(나는 침대 위에 앉아 있었다. 나는 달을 볼 수 있었고 그것은 아름다웠다. 나는 베개 위에 앉아있었다. 정말 황홀했다. 나는 침대를 바라봤다. 나는 청바지를 입고 있었다. 너무 좋았다.)

입력된 문장에 이어서 위와 같은 문장을 출력하였습니다.

출력 결과는 실행할 때마다 매번 달라집니다.

 

2.3 개체명 인식

개체명 인식(NER: Named entity recognition)이란 입력된 텍스트 속에 사람 이름, 단체 이름 등과 같이 고유한 의미가 있는 단어를 찾아 분류하는 기술입니다.

예시)
입력 -> 영희는 이번에 애플에서 새로 출시한 아이폰을 샀다.
출력 -> 영희: 사람 이름, 애플: 회사 이름, 아이폰: 물건 이름

transformers에서 인식하는 개체는 아래와 같습니다.

[토큰 설명]
O : Outside of a named entity (NER에서 인식범위를 벗어난 개체)

B-MIS : Beginning of a miscellaneous entity right after another miscellaneous entity
(기타 개체 바로 뒤에 오는 기타 개체의 시작)

I-MIS : Miscellaneous entity(기타 개체)

B-PER : Beginning of a person’s name right after another person’s name
(사람 이름 바로 뒤에 오는 사람 이름의 시작)

I-PER :  Person’s name(사람 이름)

B-ORG : Beginning of an organisation right after another organisation
(단체 이름 바로뒤에오는 단체 이름의 시작)

I-ORG : Organisation (단체)

B-LOC : Beginning of a location right after another location(지리적 위치 바로 뒤에 오는 위치의 시작)
I-LOC : Location (지리적 위치)
사용방법: pipeline('ner')
ner = pipeline('ner')
sentence = 'BTS is a South Korean boy band formed in 2010 and debuting in 2013 under Big Hit Entertainment.'
result = ner(sentence)
print(sentence)
for i in range(len(result)):
    print("-단어: ",result[i]['word'],", 구분: ",result[i]['entity'],end=' ')

입력 문장으로

'BTS is a South Korean boy band formed in 2010 and debuting in 2013 under Big Hit Entertainment.'

(BTS는 2010년에 결성된 아이돌이며 빅히트 엔터테인트먼트에서 2013년도에 데뷔하였다.)

라는 문장을 입력하여 아래와 같은 개체명 인식 결과가 나왔습니다.

-단어:  BT , 구분:  I-ORG 

-단어:  ##S , 구분:  I-ORG 

-단어:  South , 구분:  I-MISC 

-단어:  Korean , 구분:  I-MISC 

-단어:  Big , 구분:  I-ORG 

-단어:  Hit ,  구분:  I-ORG 

-단어:  Entertainment , 구분:  I-ORG

BTS, Big Hit Entertaintment를 단체라고 구분하였습니다. South Korean은 기타로 분류하였네요.

 

2.4 기계독해

기계독해는 우리가 국어 지문을 읽고 문제를 푸는 것처럼

어떠한 문서가 있으면 그것을 입력 값으로 주고 지문과 관련한 질문을 해서 맞추는 것을 말합니다.

예시)
입력: "레몬은 신맛이 나는 과일이다."
질문: "신맛이 나는 과일은?"
답: "레몬"
사용방법: pipeline('question-answering')
# Question answering : 기계독해
question_answering = pipeline("question-answering")
context = """
Shake Shack is an American fast casual restaurant chain based in New York City. It started out as a hot dog cart inside Madison Square Park in 2001, and its popularity steadily grew.
In 2004, it received a permit to open a permanent kiosk within the park, expanding its menu from New York–style hot dogs to one with hamburgers, hot dogs, fries and its namesake milkshakes.
Since its founding, it has been one of the fastest-growing food chains, eventually becoming a public company filing for an initial public offering of stock in late 2014. The offering priced on January 29, 2015; the initial price of its shares was at $21, immediately rising by 123% to $47 on their first day of trading.
Shake Shack Inc. owns and operates over 400 locations globally.
"""
question = "How many locations Shake Shack Inc. operates?"
result = question_answering(question=question,context=context)
print("지문: ", context)
print("문제: ",question)
print("답: ", result['answer'])

지문으로 셰이크쉑에 대한 위키피디아 지문을 넣었습니다.

그 후 질문으로 "쉐이크쉑은 얼마나 많은 가게를 운영하고 있나?" 라는 질문을 했고

그 결과 "over 400(400개 이상)"이라는 답을 받았습니다.

 

2.5 빈칸 예측하기

빈칸 예측하기(fill-mask)는 문장에 마스킹 처리를 해 놓고 그 안에 들어갈 단어가 무엇일지 예측하는 모델입니다.

예시)
문장: 나는 자러 [        ]에 간다.
예측 : 나는 자러 [침대]에 간다.
사용방법: pipeline('fill-mask')

transformers에서는 해당 모델 사용 시

마스킹 하는 부분을 fill_mask객체.tokenizer.mask_token 로 설정하여 사용합니다.

# Fill-mask : 빈칸 예측하기
from pprint import pprint
fill_mask = pipeline("fill-mask")
sentence =  f"AlphaGo is a computer {fill_mask.tokenizer.mask_token} that plays the board game Go."
result = fill_mask(sentence)
print("문장: ",sentence)
pprint(result)

문제로 위키피디아에서 설명하는 알파고에 대한 문장 중 하나를 가져왔습니다.

"알파고는 보드게임 Go 를 하는 컴퓨터 <mask>이다."

라고 입력값을 주었고, 원래 문장은 "알파고는 보드게임 Go 를 하는 컴퓨터 프로그램(program)이다." 였습니다.

그 결과값으로는 빈칸에 들어갈 수 있는 여러 단어가 출력이 되었습니다.

위에서 부터 차례대로 점수가 높은 순이며,

program, game, emulator, simulator, simulation 등의 단어가 들어갈 수 있다고 예측하였습니다.

 

2.6 문서 요약하기

문서 요약(summarization)은 말 그대로 긴 문서를 몇 문장으로 요약하는 것을 말합니다.

예시)
문서: 전국이 대체로 흐린 가운데 오전 중부지방을 시작으로 눈이 내렸다. 오후서울 등 수도권과 강원 일부 지방은 최대 10㎝ 이상의 많은 눈이 예상돼 대설특보가 발효됐다. 기상청은 이날 "오늘 오전부터 강원중·남부동해안을 제외한 중부지방부터 비 또는 눈이 시작돼 오후부터 전북, 경북북부내륙, 경북남서내륙, 경남서부내륙, 밤부터 전남권북부에 비 또는 눈이 오는 곳이 있겠다"며 "수도권과 충남북부는 늦은 오후에 대부분 그치겠고, 그 밖의 지역은 밤에 대부분 그치겠다"고 밝혔다. (출처: https://n.news.naver.com/mnews/article/003/0011593636?sid=103)

요약: 새벽부터 내린 비나 눈이 점차 확대되면서 경기동부와 강원영서에는 최고 10cm 이상의 많은 눈이 쌓이겠다. 낮 동안 소강상태를 보이는 곳이 많겠으나 저녁부터 다시 강해져 모레 월요일 출근길이 빙판길로 변할 가능성이 높다. 이번 눈이나 비는 짧은 시간 강하게 내리다가 그치는 형태라 강수량보다는 적설량이 많을 것으로 보인다.
사용방법: pipeline('summarization')

min/max_length으로 최소/최대 길이를 조정할 수 있습니다.

# Summarization : 문서 요약하기
summerizer = pipeline('summarization')
article = """An apple is an edible fruit produced by an apple tree (Malus domestica). Apple trees are cultivated worldwide and are the most widely grown species in the genus Malus. The tree originated in Central Asia, where its wild ancestor, Malus sieversii, is still found today. Apples have been grown for thousands of years in Asia and Europe and were brought to North America by European colonists. Apples have religious and mythological significance in many cultures, including Norse, Greek, and European Christian tradition.
Apples grown from seed tend to be very different from those of their parents, and the resultant fruit frequently lacks desired characteristics. Generally, apple cultivars are propagated by clonal grafting onto rootstocks. Apple trees grown without rootstocks tend to be larger and much slower to fruit after planting. Rootstocks are used to control the speed of growth and the size of the resulting tree, allowing for easier harvesting.
There are more than 7,500 known cultivars of apples. Different cultivars are bred for various tastes and uses, including cooking, eating raw, and cider production. Trees and fruit are prone to a number of fungal, bacterial, and pest problems, which can be controlled by a number of organic and non-organic means. In 2010, the fruit's genome was sequenced as part of research on disease control and selective breeding in apple production.
Worldwide production of apples in 2018 was 86 million tonnes, with China accounting for nearly half of the total."""
result = summerizer(article,max_length=150,min_length=50)
print("문서: ",article)
print("요약: ",result[0]['summary_text'])

입력 문서로 위키피디아에 '사과' 문서를 사용하였습니다.

요약으로 긴 문장을 3문장 정도로 축약한 것을 확인할 수 있습니다.

 

2.7 번역

번역(Translation)은 어떠한 나라의 언어에서 다른 나라의 언어로 통역해주는 모델을 말합니다.

예시)
한국어: 나는 사과를 좋아한다.
                      ↓
영어: I like apples.
사용방법: pipeline('translation_언어_to_언어')

transformers에서 제공하는 번역은 영어-독일어(en_to_de), 영어-루마니아어(en_to_ro) 입니다.

여기서는 영어-독일어 번역(translation_en_to_de)을 사용해 보도록 하겠습니다.

# Translation : 번역하기
translator = pipeline('translation_en_to_de')
sentences = "Bitcoin is a decentralized digital currency that can be transferred on the peer-to-peer bitcoin network."
result = translator(sentences, max_length=50)
print("영어: ",sentences)
print("독어: ",result[0]['translation_text'])

영어 입력문장은 위키피디아 비트코인 문서에서 가져온 문장을 사용하였습니다.

출력 값으로 독일어 번역이 된 문장을 반환합니다.

 

참고자료

https://huggingface.co/docs/transformers/task_summary#translation

 

Summary of the tasks

>>> from transformers import pipeline >>> text_generator = pipeline("text-generation") >>> print(text_generator("As far as I am concerned, I will", max_length=50, do_sample=False)) [{'generated_text': 'As far as I am concerned, I will be the first to admit

huggingface.co

마무리

이번에 간단하게 사전학습된 BERT모형 사용법을 알아보았습니다.

기본 제공이 영어라 한국어 적용도 해보고 싶어서

다음 포스팅에서는 한국어로 사용할 수 있는 방법을 알려드리도록 하겠습니다.

반응형
반응형
사전학습모델이란?

사전학습모델(pre-training model)은 어떠한 새로운 문제를 해결하려 모델을 만들어 학습시킬 때,

 기존의 다른 문제를 해결하는 데 사용한 모델의 가중치들을  활용하여 구성하는 모델을 말합니다.

예를 들어,

전에 감성 분석 모델을 미리 만들어 놓았었고 이번에는 텍스트 유사도 예측 모델을 만든다고 할 때,

미리 만들어 놓은 감정 분석 문제 모델의 가중치를 텍스트 유사도 예측 모델의 가중치로 활용하는 것이라 할 수 있습니다.

즉, 감성 분석 문제를 학습하면서 얻은 정보를 유사도 문제를 학습하는 데 활용하는 방식이라고 할 수 있습니다.

이때, 감정 분석 문제는 사전 학습 문제(pre-train-ask)가 되고,

텍스트 유사도 문제는 하위 문제(downstream task)가 됩니다. 

자주 사용하는 사전학습 문제: 언어 모델

위에서 사전학습 모델과 그 예를 알아보았는데

실제 연구에서는 보통 사전 학습 문제로 '언어 모델(language model)'을 사용합니다.

언어 모델(language model)이란?

언어모델은 문장 또는 단어 나열에 확률을 부여하는 모델입니다.

간단히 말해 특정 단어가 있을 때 그 다음 단어 또는 다른 위치에 있는 단어가 어떤 단어일지를  예측하는 모델입니다.

예를 들어,
"나는 추워서 롱패딩을 입었다." 이 문장이 있을 때, 마지막 부분인 "입었다."를 모델이 예측하며 학습합니다.

또한 "나는 너무 졸려서 잔다." 와 "나는 너무 졸려서 일어난다." 의 두 문장이 있을 때,

언어 모델은 더 자연스러운 문장인 "나는 너무 졸려서 잔다." 라는 문장에 더 높은 확률을 할당합니다.

여기서 자연스럽다는 의미는 일반적으로 더 많이 사용된다는 의미로,

이렇듯 언어 모델은 언어에 대한 이해를 높여주는 모델이라고 할 수 있습니다.

또한 정답 라벨이 필요 없는 비지도 학습이므로 지도 학습에 비해 전처리 시간이 줄어듭니다.

 

BERT란?

BERT(Bidirectional Encoder Representation from Transformers)는

2018년도에 나온

<<BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding>>

라는 논문에서 제안된 사전학습모델입니다.

BERT(버트)에서 활용하는 사전 학습 문제는 위에서 언급한 언어 모델(language model)이며,

트랜스포머에서 인코더 부분만 사용한 모형입니다.

인코더의 특징으로 양방향(bidirectional) 셀프 어텐션을 구현하고 있다는 것이 있습니다.

이는 다른 사전학습 모델(GPT, ELMo)등과 비교했을 때 확연히 다른 차이점입니다.

출처:&nbsp;https://arxiv.org/pdf/1810.04805.pdf

BERT의 학습

BERT 의 학습으로,

1. 사전학습

2. 미세조정학습

이렇게 두 단계가 있습니다.

여기서 사전학습은 비지도학습이고, 미세조정학습은 해결해야 하는 문제에 대한 지도학습입니다.

 

BERT의 사전학습

BERT는 두 문제를 사전 학습합니다.

두 문제는 아래와 같습니다.

1.1 마스크 언어 모델

- 마스크 언어 모델: 주어진 문장에서 일부 단어를 마스킹(가림) 처리하여 가려진 단어가 무엇인지 예측하는 학습
여기서, BERT는 마스킹된 앞 뒤 단어 (양 방향)를 모두 사용해서 가려진 단어를 예측한다.

성능 향상을 위해 마스킹 할 단어의 80% 는 [MASK]라는 토큰을 사용하고,
10%는 다른 단어로 바꾸고, 마지막 10%는 단어 그대로 놔둔다.
예) 원래 문장: 분석은 전처리 과정이 매우 중요하다.
      마스킹 후 문장: 분석은 [MASK] 과정이 오늘 중요하다.

1.2 다음 문장 예측(next sentence prediction)

- 다음 문장 예측: 두 개의 주어진 문장에서 이 두 문장이 이어진 문장인지 아닌지를 분류하는 학습
이를 통해 문장 간의 관계를 학습할 수 있다.
데이터 셋 구성 시 50%는 이어진 문장, 나머지 50%는 이어지지 않은 문장으로 구성한다.
앞 문장에는 [CLS]라는 토큰을 넣고, 두번째 문장 앞과 맨 끝에는 [SEP] 토큰을 넣어 문장을 구분할 수 있도록 처리한다.
예) [CLS] 분석은 [MASK] 과정이 [MASK] 중요하다. [SEP] 그래서 [MASK] 시간이 많이 걸린다.[SEP] 
위의 예시에서 모델은 가려진 단어들과 두 문장이 이어졌는지 아닌지를 예측한다.

 

BERT의 미세조정학습

미세조정학습은 사전학습으로 BERT의 언어에 대한 이해가 학습되고,

이를 통해  최종적으로 해결하여야 하는 하위문제(예: 문서 분류, 유사도 측정 등)를 풀기 위한 학습을 말합니다.

여러 문제와 관련한 미세조정-출처: https://arxiv.org/pdf/1810.04805.pdf

미세조정 학습을 하고 나면,

사전학습으로 만들어진 가중치들이 하위 문제에 맞게 세밀하게 조정되어 최종적인 예측 값을 반환합니다.

 

참고자료

1. 텐서플로2와 머신러닝으로 시작하는 자연어처리(위키북스)

2. 파이썬 텍스트 마이닝 완벽 가이드(위키북스)

3. BERT논문 - https://arxiv.org/pdf/1810.04805.pdf

마무리

이번에는 간단하게 사전학습 모델이 무엇인지, BERT가 무엇인지 알아보았습니다.

BERT를 개인이 직접 사전학습하려면 많은 비용과 시간이 들어갈 것입니다.

그래도 개인이 BERT를 사용할 수 있는 방법이 있습니다.

바로 사전학습된 모형을 무료로 제공하는 웹사이트들을 활용하는 것입니다.

다음 포스팅에서는 허깅페이스(Hugging Face)에서

미리 사전학습된 BERT를 사용하는 방법을 같이 알아보도록 하겠습니다.  

반응형
반응형

안녕하세요! 포스팅을 오랜만에 하네요.

오늘은 감성분석이 무엇인지, 또 어떤 방법으로 감성분석을 할 수 있는지 같이 알아보도록 하겠습니다.

 

감성분석(Sentiment Analysis)이란?

감성분석(Sentiment Analysis)은 여러 종류의 글(텍스트) 안에 있는 감성을 분석하는 것입니다.

그렇다면 감성(Sentiment)이란 무엇일까요?

감성(Sentiment)은 감정(Emotion)이라고도 할 수 있고, 감정은 주관적입니다.

예를 들어, 아래 나비사진을 봅시다.

출처: Pixabay

같은 나비를 보더라도 어떤 사람은 아름답다고 생각하는 긍정적인 반응을 보이는 한편,

곤충을 싫어하는 사람에게는 나비의 무늬가 징그럽다고 생각하는 부정적인 반응을 보일 수 도 있습니다.

또는 아무 생각 없이 그냥 나비구나 하는 사람도 있을 것입니다. 이것을 중립적으로 본다고 하겠습니다.

 

또한 감성은 텍스트에서도 나타납니다.

"해리포터 책은 마치 마법사가 실존하는 것처럼 생생하게 묘사한다." 라는 문장이 있을 때, 

어떤 사람은 이 문장을 긍정적으로 볼 것이며, 어떤  사람은 중립적으로 볼 수도 있을 것입니다.

반면, "이 영화는 재미가 없다."라는 문장은 부정적으로 볼 가능성이 클 것입니다.

 

감성 분류

텍스트를 감성 분석할 때,  감성을  3가지로 분류할 수 있습니다.

1. 이 텍스트가 긍정(Positive)인지

2. 중립(Neutral)인지

3. 아니면 부정(Negative) 인지

때로는 긍정, 부정 2가지만으로 분류하여 분석할 때도 있습니다.

이런 감성의 정도를 극성(Polarity)라고 합니다.

긍정, 중립, 부정 을 그림으로 나타내면 아래와 같이 표현할 수 있습니다.

0은 중립, +(양수)는 긍정, -(음수)는 부정을 나타냅니다.

 

감성 분석 방법

감성 분석을 하는 방법은 여러 가지가 있지만 크게 2가지로 나눈다면

1. 감성 사전을 활용한 감성분석

2. 기계학습(Machine Learning)을 사용하는 감성분석

으로 나눌 수 있습니다.

 

1. 감성사전을 활용한 감성분석

 

감성 사전은 명사, 형용사, 동사의 모든 단어들을 긍정 또는 부정으로 라벨링 하여 구축할 수 있습니다.

감성 사전은 직접 구축할 수도 있고(시간이 많이 걸립니다.), 기존에 만들어진 사전을 사용할 수도 있습니다.

감성사전을 기반으로 문장의 감성을 파악할 때는 아래와 같이 분류할 수 있습니다.

어떤 문장이 있다면 그 문장을 명사, 동사, 형용사 기준으로 형태소 분석을 하여

사전에 구축된 감성 사전에서 해당 단어와 매칭 되는 점수를 찾아

전부 더해서 최종적으로 나온 점수를 기준으로 문장의 감성을 판단할 수 있습니다.

그렇다면 아래의 문장은 어떻게 판단될까요?

"해리포터 영화의 영상미가 좋다고 해서 영화가 좋은 것은 아니다."

위에서 단순히 사전에서 매칭 되는 단어를 찾아 그 점수를 각각 더한다면 

위와 같이 최종적으로 1점이 되어 긍정 문장으로 판단이 될 것입니다.

하지만 문장 전체를 보면 과연 긍정적이라고 판단할 수 있을까요?

이러한 오류를 방지하고자 문장을 구로 묶어서 단계적으로 감성을 판단하는 방법을 사용할 수도 있습니다.

이를 청킹(chunking)이라고 합니다.

아래는 스탠포드에서 제공하는 트리 감성분석 입니다.

파란색일수록 긍정적이며, 붉을수록 부정적임을 나타냅니다.

https://nlp.stanford.edu/sentiment/treebank.html?w=this%2Cmovie&na=3&nb=10 

 

Recursive Deep Models for Semantic Compositionality Over a Sentiment Treebank

Displaying N sentences that satisfy the following criteria.

nlp.stanford.edu

 

출처: nlp.stanford.edu

 

2. 기계학습(Machine Learning)을 사용하는 감성분석

기계학습을 이용하여 감성분석을 할 때에는 학습 셋이 필요합니다.

머신러닝의 지도학습으로, 각 텍스트 데이터에는 긍정인지 부정인지를 구분하는 라벨링을 해주어야 합니다.

각 테스트 데이터는 문제, 라벨 데이터는 답이라고 할 수 있습니다.

그 후 train, test 데이터로 나누어 train 데이터를 가지고 학습을 하고, test 데이터로 학습이 잘 되었는지 검증합니다.

또한 train데이터를 가지고 여러 알고리즘을 사용하여 최적의 학습이 된 모델로

새로운 문장이 긍정인지 부정인지 예측할 수도 있습니다.

기계학습 기반의 감성분석은 학습데이터가 매우 중요합니다.

만약 영화 리뷰를 가지고 학습을 한 모델을 가지고 영화 리뷰와 관련된 문장을 예측하는 것은 예측 정확도가 높을 수 있습니다.

하지만 그 모델을 그대로 의료 서비스 리뷰에 적용을 한다면 이때의 문장 감성분석 정확도는 장담할 수 없습니다.

이럴 때는 주제에 맞게 모델을 수정하여 예측하여야 합니다.

 

참고자료

위키북스 - 파이썬 텍스트 마이닝 완벽 가이드: 자연어처리 기초부터 딥러닝 기반 BERT 모델까지

마무리

이렇게 간단하게 감성분석에 대해 알아보았습니다.

다음 포스팅에는 python 감성 분석 실습과 관련하여 글을 업로드하도록 하겠습니다.

 

반응형
반응형

안녕하세요.

형태소 분석기 중 하나인 MeCab을 Linux 환경에서만 사용 가능한 줄 알았는데

Windows에서도 사용할 수 있는 방법이 있어 설치 방법을 공유하고자 합니다.

python, R 각각의 설치 방법을 알려 드리도록 하겠습니다.

Python에 MeCab 설치하기

1.  "C:\"에 mecab 폴더 만들어 주기

C:\mecab 안에 다운로드 파일들을 집어넣을 수 있도록 mecab 폴더를 생성해 줍니다.

2.  mecab-ko-msvc 설치 및 mecab 폴더에 압축해제

https://github.com/Pusnow/mecab-ko-msvc/releases/tag/release-0.9.2-msvc-3

 

Release release-0.9.2-msvc-3 · Pusnow/mecab-ko-msvc

Fix: -r 옵션을 지정해 주지 않았을 때 레지스트리에서 이상한 값을 가져와 오류가 나는 문제 해결

github.com

위 링크에서 컴퓨터 버전에 맞는 zip파일을 다운받아 주세요.

저는 mecab-ko-msvc-x64.zip 을 다운받았습니다.

그 후 1번에서 만든 mecab 폴더에 zip 파일을 옮긴 후 압축해제해 주세요.

압축해제시 여기에 풀기를 눌러 압축해제해 주세요.

3. mecab-ko-dic-msv 설치 및 mecab 폴더에 압축해제

https://github.com/Pusnow/mecab-ko-dic-msvc/releases/tag/mecab-ko-dic-2.1.1-20180720-msvc-2

 

Release mecab-ko-dic-2.1.1-20180720-msvc-2 · Pusnow/mecab-ko-dic-msvc

코스트 반영을 위한 compile-win.ps1 추가

github.com

위의 링크에서 mecab-ko-dic-msvc.zip을 다운받아 주세요.

그 후 2번과 같이 전에 만든 mecab 폴더에 zip 파일을 옮긴 후 압축해제해 주세요.

압축해제시 여기에 풀기를 눌러 압축해제해 주세요.

4. anaconda로 가상환경 만들고 파이썬 3.8버전으로 설치하기

지금 제가 사용하는 python은 3.9버전이어서 3.8버전으로 파이썬을 설치하기 위해 가상환경을 만들어 주도록 하겠습니다.

anaconda prompt를 열어 아래의 명령어를 입력해 주세요.

conda create -n mecab python=3.8.0

mecab이라는 가상환경을 만들어 python 3.8.0버전을 설치해 줍니다.

중간에 proceed? y/n 라고 커맨드 창에 뜨는데 y를 입력 후 엔터를 눌러주면 완료됩니다.

5. python wheel 다운받고 설치하기

https://github.com/Pusnow/mecab-python-msvc/releases/tag/mecab_python-0.996_ko_0.9.2_msvc-3

 

Release mecab_python-0.996_ko_0.9.2_msvc-3 · Pusnow/mecab-python-msvc

Add Python 3.8 support

github.com

위의 링크에서 컴퓨터와 파이썬 버전에 맞는 파일을 다운받아 주세요.

파이썬 가장 최신버전이 3.8이어서 저는 3.8버전이고 64bit인

'mecab_python-0.996_ko_0.9.2_msvc-cp38-cp38-win_amd64.whl' 을 다운받았습니다.

다운받은 'mecab_python-0.996_ko_0.9.2_msvc-cp38-cp38-win_amd64.whl' 파일을 mecab 가상환경 site-packages에 위치시켜 주세요.

정확한 경로는 아래와 같습니다.

C:\Users\I사용자\anaconda3\envs\mecab\Lib\site-packages

그 후 Anaconda prompt를 열고

 conda activate mecab

을 입력하여 mecab 가상환경을 활성화 해 줍니다.

그 후 아래의 명령어를 입력하여 whl파일이 있는 곳으로 터미널을 위치시켜 줍니다.

cd C:\Users\사용자\anaconda3\envs\mecab\Lib\site-packages

다음으로 whl파일을 설치해 줍니다.

pip install mecab_python-0.996_ko_0.9.2_msvc-cp38-cp38-win_amd64.whl

파일이름이 너무 길면 pip install mecab을 친 후 tab키를 누르면 자동으로 나머지 부분이 입력이 됩니다.

이렇게 python에서 mecab을 설치가 모두 끝났습니다.

6. python에서 mecab 가상환경의 interpreter 설정해 주기

python에서 mecab을 사용하기 전에 interpreter를 가상환경의 interpreter로 설정해 주어야 합니다.

python 프로젝트를 위한 폴더를 만든 후 파이참에서 폴더를 open해 주세요.

저는 mecab_ko라는 폴더를 생성하여 open해 주었습니다.

그 후 파이참 우측 상단의 톱니바퀴를 눌러 Settings로 들어가 줍니다.

나타난 settings창에서 python interpreter를 누르고

Virtualenv Environment > Existing environment >

C:\Users\사용자\anaconda3\envs\mecab\python.exe > OK 를 눌러 설정을 완료해 줍니다.

그러면 아래와 같이 mecab-python이 설치되어 있는 것을 확인할 수 있습니다.

7. python에서 mecab 사용해 보기

파이썬 파일을 하나 만들고 그 위에다 아래와 같이 예제 코드를 입력하여 실행이 잘 되는지 확인해 봅시다.

import MeCab
mecab = MeCab.Tagger()
out = mecab.parse("오늘은 맑은 날씨이다.")
print(out)

Cntl+Shift+F10을 눌러 코드를 실행 시켜서

위와 같이 결과가 잘 출력되면 설치가 완료되었습니다. 이후 필요한 형태소 분석에 mecab을 사용하면 됩니다.

가상 환경을 종료하고 싶을 때는 명령창에 conda deactivate를 입력하여 종료해 주면 됩니다.

 

R에서 MeCab 설치하기

이번에는 R에서 MeCab을 설치해 보도록 하겠습니다.

RStudio를 열어 준 후 r스크립트를 하나 만들어 줍니다.

그 후 아래의 명령어를 입력 후 Ctrl + Enter를 눌러 실행해 줍니다.

#mecab설치
install.packages('remotes')
library(remotes)
install_github("junhewk/RcppMeCab")

설치가 완료 되었으면 문장 하나를 임의로 입력하여 분석을 해 줍니다.

여기서 주의할 점은 text를 utf8로 변경해 주어야 출력시 한글이 깨지지 않고 출력됩니다.

library(RcppMeCab)
#text를 utf8로 변경 후 분석
text = enc2utf8('오늘은 날씨가 맑습니다.')
pos(sentence = text)

위의 사진처럼 출력이 잘 되면 R에서도 mecab을 사용하여 분석이 가능합니다.

그럼 모두들 즐거운 형태소 분석 하시길 바랍니다 ㅎㅎ

반응형

+ Recent posts