[Kaggle] TED-Talk 토픽모델링 (Topic modeling)

2020. 3. 28. 18:14노트/Python : 프로그래밍

토픽 모델링 기법은 여러 이야기들의 토픽들을 뽑는 데, 주로 사용할 수 있는 모델링 기법이다. 

지난 번 프로젝트 때 데이터 수집 및 전처리를 잘못해서 요상한 결과를 가지고 왔었는데, 

Kaggle에 잘 정제된 데이터와 분석 방법에 대한 노트북이 있어서 이를 참고하여 포스팅하고자 한다. 

 

 

 

 

데이터 다운로드 

 

아래 kaggle 링크 접속 후, 하단으로 스크롤하여 자막data와 tedtalk meta data를 다운로드

로그인 후, 다운로드 모양의 아이콘을 클릭하면 데이터를 다운로드 할 수 있다. 

 

https://www.kaggle.com/adelsondias/ted-talks-topic-models 

 

TED-Talks topic models

Explore and run machine learning code with Kaggle Notebooks | Using data from TED Talks

www.kaggle.com

 

라이브러리 호출 

import numpy as np 
import pandas as pd 
import matplotlib.pyplot as plt
import seaborn as sns
from time import time

 

데이터 불러오기

ted_main_df = pd.read_csv('ted_main.csv', encoding='utf-8')
transcripts_df = pd.read_csv('transcripts.csv', encoding='utf-8')

ted_main_df.head()
transcripts_df.head()

1. TFIDF로 텍스트 특징을 추출 

    : Text feature extraction with TFIDF 

 

 

첫번째로, 문서-빈도행렬(term-frequency(TF) matrix) 를 고려해야한다.

TF - matrix는 문서들의 리스트와 그 문서들의 단어 공간으로 부터 추출된 행렬이다.  

이 행렬은 모든 문서들의 요소 빈도들이 포함된 거대한 행렬이다. 

첫번째 문서의 단어1의 빈도는 3, 단어 3의 빈도는 2 ... N번째 문서의 단어1의 빈도는 1, 단어2의 빈도는 2 .... 로 해석할 수 있다. 

 

 

이후에는 이 빈도를 단어들의 중요성을 나타내는 척도로 변환 시키기 위해서  역 문서 행렬 (de idf)를 고려해야한다. 

예를들어 불용언 a, the, an, is 등은 대부분의 문서에 많은 빈도로 나타나지만 한 문서의 특징을 나타내는 단어들이 아니기 때문에, 이런 경우의 수를 배제 시키기 위하여 역 문서 행렬 을 곱해준다. 식은 다음과 같다. 

 

 

N은 전체 문서의 갯수이고,

df는 i 번째 단어가 포함되어 있는 문서의 빈도수 이다. 

그래서 전체 문서에 대부분 나타나있는 단어들은 가중치를 낮게, 

특정 문서에만 나타나는 단어들은 가중치를 높게 주어 단순 빈도로 나타낸 tf 를 tfidf 비율로 변환한다. 

우리 행렬에서 가장 중요한 단어들을 봐야한다! 

 

from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(stop_words="english",
                            use_idf=True,
                            ngram_range=(1,1), #오직 1-grams만 고려함
                            min_df = 0.05,     #5% 미만으로 문서에서 출현하는 단어들은 삭제
                            max_df = 0.3)      #30% 이상으로 문서에서 존재하는 단어들은 삭제(너무 흔함)

t0=time()

tfidf = vectorizer.fit_transform(transcripts_df['transcript'])

print("완료시간 : %0.3f초" % (time() - t0))
                             

* n-grams_range=(1,1) : 한 단어로만 판단 = 띄어쓰기 기준으로 단어를 판단.

 

# vectorizer 내에서 높은 순위로 고려되는 단어들을 불러오는 함수를 생성 
def rank_words(terms, feature_matrix):
    sums = feature_matrix.sum(axis=0) # 문서마다 단어의 tfidf 합산
    data = []
    for col, term in enumerate(terms): 
        data.append( (term, sums[0,col])) # 단어의 tfidf 값 매핑하여 리스트에 추가
    ranked = pd.DataFrame(data, columns=['term','rank']).sort_values('rank',ascending=False) 
    # tfidf 내림차순으로 정렬하여 데이터프레임 생성  
    return ranked 

ranked = rank_words(terms=vectorizer.get_feature_names(), feature_matrix=tfidf)

fix, ax = plt.subplots(figsize=(6,10), ncols=1, nrows=1)
sns.barplot(x='rank',y='term',data=ranked[:20], palette="Reds_r", ax=ax)

# 워드클라우드로 idf 역행렬로 얻어진 단어들의 빈도수를 시각화 
dic = {ranked.loc[i,'term'].upper() : ranked.loc[i,'rank']
      for i in range(0,len(ranked))}

from wordcloud import WordCloud
wordcloud = WordCloud(background_color="white",
                     max_words=100,
                     colormap="Reds").generate_from_frequencies(dic)

fig = plt.figure(1, figsize=(12,15))
plt.imshow(wordcloud, interpolation="bilinear")
plt.axis("off")
plt.show()

 

2. Topic modeling 

 

텍스트 데이터로 부터 topic들을 추출하는 것이 분해 기술로 최근에 되어서야 사용되고 있따. 

한 topic은 단어들의 mixture들이며, 만약 이 단어들이 그 문서들에 있다면 문서는 반드시 그 주제와 관련이 있어야한다.

예를 들어, 한 문서에 미적분, 삼각함수, 집합 등의 단어들이 많이 있다면 수학과 관련된 주제일 것이고, 

피카소, 모자이크, 입체파 등의 단어들이 많이 있다면 미술과 관련된 주제일 것이다. 

그래서 NMF나 LDA 알고리즘을 통해서 토픽들을 추출해볼 것이다. 

 

2-1. LDA 알고리즘을 통한 토픽 모델링 

from sklearn.decomposition import LatentDirichletAllocation

n_topics=10
lda = LatentDirichletAllocation(n_components=n_topics, random_state=0)

topics= lda.fit_transform(tfidf)

top_n_words = 5
t_words, word_strengths = {}, {}
for t_id, t in enumerate(lda.components_):
    t_words[t_id] =[vectorizer.get_feature_names()[i] 
                    for i in t.argsort()[:-top_n_words -1:-1]]
    word_strengths[t_id] = t[t.argsort()[:-top_n_words-1:-1]]
t_words    

fig, ax = plt.subplots(figsize=(7,15), ncols=2, nrows=5)
plt.subplots_adjust(wspace = 0.5, hspace=0.5)
c=0
for row in range(0,5):
    for col in range(0,2):
        sns.barplot(x=word_strengths[c], y=t_words[c],
                   color="red",ax=ax[row][col])
        c+=1
plt.show()

나쁘지 않지만, topics들이 적절하지 못하게 단어들이 분배된 것을 확인할 수 있다. 또한 주제들 사이에 단어들이 중복되었다. 그래서 우리가 주제를 해석하기에 적절하지 못한 단어들이 추출되었다고 판단할 수 있다. 이런 문제는 토픽모델링을 할 때, 실제로 발생하게 되는 공통적인 어려움이다. 또 다른 문제는 최적의 주제 수를 판단하는 것이다. 이를 검증하는데 몇가지 방식이 있긴하나..  그냥 10개의 topic들로 정하자! 

 

2-2. NMF 알고리즘을 통한 토픽 모델링 

 

from sklearn.decomposition import NMF 

n_topics = 10
nmf = NMF(n_components=n_topics,random_state=0)
topics = nmf.fit_transform(tfidf)
top_n_words = 5
t_words, word_strengths = {}, {}
for t_id, t in enumerate(nmf.components_):
    t_words[t_id] = [vectorizer.get_feature_names()[i]
                    for i in t.argsort()[:-top_n_words-1:-1]]
    word_strengths[t_id] = t[t.argsort()[:-top_n_words-1:-1]]

t_words

fig, ax = plt.subplots(figsize=(7,15), ncols=2, nrows=5)
plt.subplots_adjust(wspace =0.5, hspace=0.5)
c=0
for row in range(0,5):
    for col in range(0,2):
        sns.barplot(x=word_strengths[c], y=t_words[c],
                   color="red", ax=ax[row][col])
        c+=1
plt.show()

 

NMF 알고리즘을 이용한 토픽 모델링이 좀 더 적절한 것으로 보인다. 

이 결과로 문서들과 토픽들의 추출이 적절하게 되었는지 확인해 보고자 한다. 

 

# 문서를 삽입할 pipeline을 형성하고, 주제 적절성을 추출
from sklearn.pipeline import Pipeline
pipe = Pipeline([('tfidf', vectorizer),
                ('nmf',nmf)])

document_id =4 
t = pipe.transform([transcripts_df['transcript'].iloc[document_id]])
print('문서 #{}에 대한 토픽 배분: \n'.format(document_id),t)
print('문서 #{}에 대한 주제 관련성'.format(document_id),np.where(t>0.01)[1])
print('\n자막:\n',transcripts_df['transcript'].iloc[document_id][:500],"...")

talk = ted_main_df[ted_main_df['url']==transcripts_df['url'].iloc[document_id]]
print("\n Ted_main.csv 의 True tags : \n", talk['tags'])

결과를 해석해보면, 문서#4 의 자막은 국제개발에 교육하러 가서 아프리칸 사람들과 함께 공부하며, 의료기관에서 공부하고, 국제적인 건강문제 등에대해서 알게되었다는 내용인데, 5 번 토픽에 가장 높게 배분되었다. 

5번 토픽은 countries , africa , goverment, global, dollars, 이다.  얼추 비슷하다!! 

 

 

3. 추출된 토픽에 대한 통계 및 탐색적 분석 

 

t= pipe.transform(transcripts_df['transcript'])
t=pd.DataFrame(t, columns=[str(t_words[i]) for i in range(0,10)])
t.head()

new_t = t.melt()
fig, ax = plt.subplots(figsize=(12,6), ncols=1, nrows=1)
sns.violinplot(x="variable", y="value", data= new_t, 
                palette="Reds", ax=ax)
plt.setp(ax.xaxis.get_majorticklabels(), 
         rotation=-45, ha="left",rotation_mode="anchor")
plt.show()

몇가지 다음과 같은 결론을 얻을 수 있다. 

 

  1. Topic #0(['god', 'book', 'stories', 'oh', 'art']) 은 단어들의 의미를 보았을 때, 매우 그 자체로 일반적이다. 그리고 아마도 이것은 다른 것들과 비교하여 분배되었을 때에도 그렇다는 뜻이라고 해석할 수 있다. 

  2. Topic #2는 음악에 대한 이야기이고, 강한 아웃라이어를 가지는 것을 확인할 수 있다. 

  3. Topic #4 (about earth), #5(about government), #7(about data&information) 그리고 #9(about education)은 품질의 수준이 꽤 높다. 이는 TED Talks 이 이야기하는 가장 빈번한 주제들이라고 생각해볼 수 있다. 

 

거의 캐글 노트북을 번역한 수준으로 포스팅 하였다. 맨 아래 결론 부분이 잘 이해가 되진 않지만, 주어진 데이터로 TED talks에서 많이 이야기하고 있는 주제를 추출해볼 수 있었다. 알고리즘 원리에 대해서도 추후해 공부해봐야하고, 내가 직접 데이터를 파싱해서 csv 파일로 생성해본 다음 동향 분석을 진행해 볼 필요도 있다. NLP 처리에 관한 공부를 시작할 때 흥미롭게 진행해볼 수 있는 프로젝트 라고 생각한다!