[ NLP ] Topic Modeling : LDA , ์ ์ฌ ๋๋ฆฌํด๋ ํ ๋น
๋ณธ ํฌ์คํธ์์๋ NLP ํ๋ก์ ํธ๋ฅผ ์งํํ๋ฉด์ ๊ณต๋ถํ ๋ด์ฉ์ ์ ๋ฆฌํ์๋ค. Topic Modeling์ ๋ํ ์๊ณ ๋ฆฌ์ฆ LDA์ ๋ํด ์์๋ณด์.
LSA Latent Semantic Analysis
Topic Modeling ๋ถ์ผ์ ์์ด๋์ด๋ฅผ ์ ๊ณตํ ๊ฑด LSA ์๊ณ ๋ฆฌ์ฆ์ด๋ค. LDA๋ฅผ ์์๋ณด๊ธฐ ์ ์ ๋จผ์ LSA ์ ๋ํด ์ ๋ฆฌํ๊ณ ๋์ด๊ฐ์.
LSA๋ Topic Modeling ์ ์ํด ์ต์ ํ๋ ์๊ณ ๋ฆฌ์ฆ์ ์๋๋ค.
๊ทธ๋ฌ๋ ๊ธฐ์กด์ BoW Bag of Words ์ ๊ธฐ๋ฐํ DTM์ด๋ TF-IDF ๋ฐฉ๋ฒ์ ๋จ์ด์ ๋น๋์๋ง ์ด์ฉํ๊ณ ์๋ฏธ๋ฅผ ๊ณ ๋ คํ์ง ๋ชปํ๋ค๋ ํ๊ณ์ ์ ๋ณด์ํ ๋ฐฉ๋ฒ์ผ๋ก DTM์ ์ ์ฌ๋ (Latent) ์๋ฏธ๋ฅผ ๋ถ์ํ๋ค๊ณ ํด์ LSA ๋ผ๋ ์๊ณ ๋ฆฌ์ฆ์ด ์ ์๋์๋ค. ๋ค๋ฅธ ๋ง๋ก LSI Latent Semantic Indexing ๋ผ๊ณ ๋ถ๋ฅด๊ธฐ๋ ํ๋ค๊ณ ํ๋ค.
LSA ๋ ๋จผ์ DTM ์ด๋ TF-IDF ํ๋ ฌ์ ์ ๋จ๋ SVD (truncated SVD) ๋ฅผ ์ฌ์ฉํด์ ์ฐจ์์ ์ถ์์ํค๊ณ , ๋จ์ด๋ค์ ์ ์ฌ ์๋ฏธ๋ฅผ ๋์ด๋ธ๋ค.
Truncated SVD ๋ฅผ ์ด์ฉํ์ฌ ํ๋ ฌ์ ํน์ด๊ฐ ์ค ์์ t ๊ฐ๋ง ๋จ๊ธฐ๊ณ ๋๋จธ์ง๋ ๋ชจ๋ ์ ๊ฑฐํ์ฌ ์ฐจ์์ ์ถ์ํ๋ค. ์ด๋ t ๋ ํ ํฝ์ ๊ฐ์๋ฅผ ์๋ฏธํ๋ค. ์ด๋ฐ์์ผ๋ก ๋์จ ๋ฌธ์ ๋ฒกํฐ๋ค๊ณผ ๋จ์ด ๋ฒกํฐ๋ค์ ํตํด ๋ค๋ฅธ ๋ฌธ์์ ์ ์ฌ๋, ๋ค๋ฅธ ๋จ์ด์ ์ ์ฌ๋, ๋จ์ด๋ก๋ถํฐ ๋ฌธ์์ ์ ์ฌ๋๋ฅผ ๊ตฌํ ์ ์๋ค.
LSA ๋ฅผ ์ด์ฉํ๋ฉด ์ฝ๊ณ ๋น ๋ฅด๊ฒ ๊ตฌํ์ด ๊ฐ๋ฅํ๋ฉฐ, ๋จ์ด์ ์ ์ฌ ์๋ฏธ๋ฅผ ์ด๋์ด๋ผ ์ ์์ด์ ์ข์ ์ฑ๋ฅ์ ๋ณด์ฌ์ค ์ ์๋ค. ๊ทธ๋ฌ๋ SVD์ ํน์ฑ์ ์ด๋ฏธ ๊ณ์ฐ๋ LSA ์ ์๋ก์ด ๋ฐ์ดํฐ๊ฐ ๋ค์ด์ค๋ฉด ์ผ๋ฐ์ ์ผ๋ก ์ฒ์๋ถํฐ ๋ค์ ๊ณ์ฐํด์ผํ๊ธฐ ๋๋ฌธ์ LSA ๋์ Word2Vec ๋ฑ ๋จ์ด์ ์๋ฏธ๋ฅผ ๋ฒกํฐํํ์ฌ ์ฌ์ฉํ๋ ์ธ๊ณต ์ ๊ฒฝ๋ง ๊ธฐ๋ฐ์ ๋ฐฉ๋ฒ๋ก ์ด ์ฃผ๋ชฉ๋ฐ๊ณ ์๋ค.
LSA ๋ฅผ ์ด์ฉํ์ฌ ํ ํฝ ๋ชจ๋ธ๋ง ์ค์ต๋ ํด๋ณด์. ์ค์ต ์ฝ๋๋ ์๋์ ์ ์ด๋์๋ค.
LDA Latent Dirichlet Allocation
LDA ๋ ์ฃผ์ด์ง ๋ฌธ์์ ๋ํ์ฌ ๊ฐ ๋ฌธ์์ ์ด๋ค ์ฃผ์ ๋ค์ด ์กด์ฌํ๋์ง์ ๋ํ ํ๋ฅ ๋ชจํ์ผ๋ก, ํ ํฝ ๋ชจ๋ธ๋ง์ ๋ํ์ ์ธ ์๊ณ ๋ฆฌ์ฆ์ด๋ค. ๋๋ต์ ์ธ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ๋ค.
LDA ๋ ๋ค์์ ์ํฉ์ ๊ฐ์ ํ๋ค.
- ๋ฌธ์๋ค์ ํ ํฝ๋ค์ ํผํฉ์ผ๋ก ๊ตฌ์ฑ
- ํ ํฝ๋ค์ ํ๋ฅ ๋ถํฌ์ ๊ธฐ๋ฐํ์ฌ ๋จ์ด๋ฅผ ์์ฑ
LDA ๋ ํน์ ํ ํฝ์ ํน์ ๋จ์ด๊ฐ ๋ํ๋ ํ๋ฅ ์ ๊ณ์ฐํด์ค๋ค. ์์ ๊ทธ๋ฆผ์ ์์๋ก ๋ค์ด๋ณด์๋ฉด, ๋ ธ๋์ ํ ํฝ์ gene, dna, genetic ์ด๋ผ๋ ๋จ์ด๊ฐ ๋์ฌ ํ๋ฅ ์ด ๋์ ๊ฑธ๋ก ๋ณด์ ์ ์ ์ ๊ด๋ จ ์ฃผ์ ์ผ ๊ฒ์ด๋ค. ํํธ, ๋ฌธ์๋ฅผ ๋ณด๋ฉด ๋นจ๊ฐ์, ํ๋์ ํ ํฝ์ ํด๋นํ๋ ๋จ์ด๋ณด๋ค ๋ ธ๋์ ํ ํฝ์ ํด๋นํ๋ ๋จ์ด๊ฐ ๋ ๋ง์ ๊ฑธ๋ก ๋ณด์ ๋ ธ๋์ ํ ํฝ์ผ ๊ฐ๋ฅ์ฑ์ด ๋์ ๊ฒ์ด๋ค. ์ด๋ฐ์์ผ๋ก LDA๋ฅผ ์ด์ฉํด ๋ฌธ์์ ํ ํฝ์ ์ถ์ถํด๋ธ๋ค.
โ๏ธ LDA ์ํ ๊ณผ์
1๏ธโฃ ์ฌ์ฉ์๊ฐ ์๊ณ ๋ฆฌ์ฆ์๊ฒ ํ ํฝ์ ๊ฐ์ k ๋ฅผ ์ง์ ํด์ค๋ค.
2๏ธโฃ ๋ชจ๋ ๋จ์ด๋ฅผ k ๊ฐ ์ค ํ๋์ ํ ํฝ์ ํ ๋นํ๋ค.
3๏ธโฃ ๋ชจ๋ ๋ฌธ์์ ๋ชจ๋ ๋จ์ด์ ๋ํ์ฌ ๋ค์ ๊ณผ์ ์ ๋ฐ๋ณตํ๋ค.
์ด๋ค ๋ฌธ์์์ ๊ฐ ๋จ์ด w ๊ฐ ์๋ชป๋ ํ ํฝ์ ํ ๋น, ๋๋จธ์ง ๋จ์ด๋ ๋ชจ๋ ์ฌ๋ฐ๋ฅธ ํ ํฝ์ ํ ๋น๋์ด์๋ค๊ณ ๊ฐ์ ํ์ฌ ๋ค์์ 2๊ฐ์ง ๊ธฐ์ค์ ๋ฐ๋ผ ์ฌํ ๋น๋๋ค.
p(topic t | document d)
: ๋ฌธ์ d์ ๋จ์ด๋ค ์ค ํ ํฝ t์ ํด๋นํ๋ ๋จ์ด๋ค์ ๋น์จp(word w | topic t)
: ๊ฐ ํ ํฝ๋ค t์์ ํด๋น ๋จ์ด w์ ๋ถํฌ
LDA ๋ฅผ ์ด์ฉํ์ฌ ํ ํฝ ๋ชจ๋ธ๋ง ์ค์ต๋ ํด๋ณด์. ๋ง์ฐฌ๊ฐ์ง๋ก, ์ค์ต ์ฝ๋๋ ์๋์ ์ ์ด๋์๋ค.
๐ค LSA์ LDA์ ์ฐจ์ด?
- LSA๋ DTM์ ์ฐจ์ ์ถ์ํ๊ณ , ์ถ์๋ ์ฐจ์์์ ๊ทผ์ ๋จ์ด๋ค์ ํ ํฝ์ผ๋ก ๋ฌถ๋๋ค.
- LDA๋ ๋จ์ด๊ฐ ํน์ ํ ํฝ์ ์กด์ฌํ ํ๋ฅ ๊ณผ ๋ฌธ์์ ํน์ ํ ํฝ์ด ์กด์ฌํ ํ๋ฅ ์ ๊ฒฐํฉํ๋ฅ ๋ก ์ถ์ ํ์ฌ ํ ํฝ์ ์ถ์ถํ๋ค.
๐ป ์ฝ๋ ์ค์ต
- LSA
scikit-learn์ Twenty Newsgroups ๋ฐ์ดํฐ๋ฅผ ์ด์ฉํด LSA ์ค์ต์ ์งํํด๋ณด์.
ํด๋น ๋ฐ์ดํฐ์ ์ 20๊ฐ์ ๋ค๋ฅธ ์ฃผ์ ๋ฅผ ๊ฐ์ง ๋ด์ค๊ทธ๋ฃน ๋ฐ์ดํฐ๋ฅผ ํฌํจํ๊ณ ์๊ณ , ์ด๋ฅผ ์ด์ฉํด ๋ฌธ์๋ฅผ ์ํ๋ ํ ํฝ์ ์๋ก ์์ถํ์ฌ ๊ฐ ํ ํฝ ๋น ๊ฐ์ฅ ์ค์ํ ๋จ์ด 5๊ฐ๋ฅผ ์ถ์ถํ ๊ฒ์ด๋ค.
์ฐธ๊ณ : Wikidocs : ์ ์ฌ ์๋ฏธ ๋ถ์
import pandas as pd
from sklearn.datasets import fetch_20newsgroups
import nltk
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import TruncatedSVD
dataset = fetch_20newsgroups(shuffle=True, random_state=42, remove=('headers', 'footers', 'quotes'))
# ๋ด์ค ๊ทธ๋ฃน ๋ฐ์ดํฐ
documents = dataset.data
# ์นดํ
๊ณ ๋ฆฌ
dataset.target_names
news_df = pd.DataFrame({'document':documents})
# ํน์ ๋ฌธ์ ์ ๊ฑฐ
news_df['clean_doc'] = news_df['document'].str.replace("[^a-zA-Z]", " ")
# ๊ธธ์ด๊ฐ 3์ดํ์ธ ๋จ์ด๋ ์ ๊ฑฐ (๊ธธ์ด๊ฐ ์งง์ ๋จ์ด ์ ๊ฑฐ)
news_df['clean_doc'] = news_df['clean_doc'].apply(lambda x: ' '.join([w for w in x.split() if len(w)>3]))
# ์ ์ฒด ๋จ์ด์ ๋ํ ์๋ฌธ์ ๋ณํ
news_df['clean_doc'] = news_df['clean_doc'].apply(lambda x: x.lower())
# NLTK๋ก๋ถํฐ ๋ถ์ฉ์ด๋ฅผ ๋ฐ์์ค๊ธฐ
stop_words = stopwords.words('english')
# ํ ํฐํ
tokenized_doc = news_df['clean_doc'].apply(lambda x: x.split())
tokenized_doc = tokenized_doc.apply(lambda x: [item for item in x if item not in stop_words])
# ์ญํ ํฐํ
detokenized_doc = []
for i in range(len(news_df)):
t = ' '.join(tokenized_doc[i])
detokenized_doc.append(t)
news_df['clean_doc'] = detokenized_doc
# TF-IDF
vectorizer = TfidfVectorizer(stop_words='english', max_features= 1000, # ์์ 1,000๊ฐ์ ๋จ์ด๋ฅผ ๋ณด์กด
max_df = 0.5, smooth_idf=True)
X = vectorizer.fit_transform(news_df['clean_doc'])
# Topic Modeling
svd_model = TruncatedSVD(n_components=20, algorithm='randomized', n_iter=100, random_state=122)
svd_model.fit(X)
# topic ๊ฐ์
len(svd_model.components_)
- LDA
์ด๋ฒ์ ์ฝ 15๋ ๊ฐ ๋ฐํ๋ ์์ด ๋ด์ค ๊ธฐ์ฌ ์ ๋ชฉ์ ๋ชจ์๋์ ๋ฐ์ดํฐ์ ์ ์ด์ฉํ์ฌ scikit learn์ LDA ์ค์ต์ ํด๋ณด๊ฒ ๋ค.
์ฐธ๊ณ : Wikidocs : ์ฌ์ดํท๋ฐ์ ์ ์ฌ ๋๋ฆฌํด๋ ํ ๋น ํ์ต
import pandas as pd
import urllib.request
import nltk
from nltk.corpus import stopwords
from nltk.stem import WordNetLemmatizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import LatentDirichletAllocation
urllib.request.urlretrieve("https://raw.githubusercontent.com/ukairia777/tensorflow-nlp-tutorial/main/19.%20Topic%20Modeling/dataset/abcnews-date-text.csv", filename="abcnews-date-text.csv")
data = pd.read_csv('abcnews-date-text.csv', error_bad_lines=False)
# ๋ด์ค ์ ๋ชฉ ๋ฐ์ดํฐ๋ง ์ ์ฅ
text = data[['headline_text']]
# ๋ถ์ฉ์ด ์ ๊ฑฐ
text['headline_text'] = text.apply(lambda row: nltk.word_tokenize(row['headline_text']), axis=1)
stop_words = stopwords.words('english')
text['headline_text'] = text['headline_text'].apply(lambda x: [word for word in x if word not in (stop_words)])
# 3์ธ์นญ ๋จ์ -> 1์ธ์นญ / ๊ณผ๊ฑฐ ํ์ฌํ -> ํ์ฌ
text['headline_text'] = text['headline_text'].apply(lambda x: [WordNetLemmatizer().lemmatize(word, pos='v') for word in x])
# ๊ธธ์ด๊ฐ 3 ์ดํ์ธ ๋จ์ด๋ ์ ๊ฑฐ
tokenized_doc = text['headline_text'].apply(lambda x: [word for word in x if len(word) > 3])
# ์ญํ ํฐํ (ํ ํฐํ ์์
์ ๋๋๋ฆผ)
detokenized_doc = []
for i in range(len(text)):
t = ' '.join(tokenized_doc[i])
detokenized_doc.append(t)
# ๋ค์ text['headline_text']์ ์ฌ์ ์ฅ
text['headline_text'] = detokenized_doc
# ์์ 1,000๊ฐ์ ๋จ์ด๋ฅผ ๋ณด์กด
vectorizer = TfidfVectorizer(stop_words='english', max_features= 1000)
# TF-IDF ํ๋ ฌ ๋ง๋ค๊ธฐ
X = vectorizer.fit_transform(text['headline_text'])
# ํ ํฝ ๋ชจ๋ธ๋ง
lda_model = LatentDirichletAllocation(n_components=10,learning_method='online',random_state=777,max_iter=1)
lda_top = lda_model.fit_transform(X)
# ๋จ์ด ์งํฉ. 1,000๊ฐ์ ๋จ์ด๊ฐ ์ ์ฅ๋จ.
terms = vectorizer.get_feature_names()
def get_topics(components, feature_names, n=5):
for idx, topic in enumerate(components):
print("Topic %d:" % (idx+1), [(feature_names[i], topic[i].round(2)) for i in topic.argsort()[:-n - 1:-1]])
get_topics(lda_model.components_,terms)
# LDA ์๊ฐํ
# pip install pyLDAvis
import pyLDAvis.gensim_models
pyLDAvis.enable_notebook()
vis = pyLDAvis.gensim_models.prepare(ldamodel, corpus, dictionary)
pyLDAvis.display(vis)
๊ฐ ์๊ณผ์ ๊ฑฐ๋ฆฌ๋ ๊ฐ ํ ํฝ๋ค์ด ์๋ก ์ผ๋ง๋ ๋ค๋ฅธ์ง๋ฅผ ๋ณด์ฌ์ค๋ค. ์ฃผ์ํด์ผํ ์ ์ LDA ๋ชจ๋ธ์์ ์ถ๋ ฅ์ ํ๋ฉด ํ ํฝ ๋ฒํธ๊ฐ 0๋ถํฐ ๋ถ์ฌ๋์ง๋ง, ์์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ์ฌ ์๊ฐํ๋ฅผ ํ๋ฉด ํ ํฝ ๋ฒํธ๊ฐ 1๋ถํฐ ์์๋๋ค๋ ์ ์ด๋ค.
# ๋ฌธ์ ๋ณ ํ ํฝ ๋ถํฌ ๋ณด๊ธฐ
for i, topic_list in enumerate(ldamodel[corpus]):
if i==5:
break
print(i,'๋ฒ์งธ ๋ฌธ์์ topic ๋น์จ์',topic_list)
# ๋ฌธ์ ๋ณ ํ ํฝ ๋ถํฌ ๋ฐ์ดํฐ ํ๋ ์์ผ๋ก ๋ณด๊ธฐ
def make_topictable_per_doc(ldamodel, corpus):
topic_table = pd.DataFrame()
for i, topic_list in enumerate(ldamodel[corpus]):
doc = topic_list[0] if ldamodel.per_word_topics else topic_list
doc = sorted(doc, key=lambda x: (x[1]), reverse=True)
# ๋ชจ๋ ๋ฌธ์์ ๋ํด์ ๊ฐ๊ฐ ์๋๋ฅผ ์ํ
for j, (topic_num, prop_topic) in enumerate(doc):
if j == 0: # ๊ฐ์ฅ ๋น์ค์ด ๋์ ํ ํฝ
topic_table = topic_table.append(pd.Series([int(topic_num), round(prop_topic,4), topic_list]), ignore_index=True)
else:
break
return(topic_table)
topictable = make_topictable_per_doc(ldamodel, corpus)
topictable = topictable.reset_index() # ๋ฌธ์ ๋ฒํธ์ ์๋ฏธํ๋ ์ด(column)๋ก ์ฌ์ฉํ๊ธฐ ์ํด์ ์ธ๋ฑ์ค ์ด์ ํ๋ ๋ ๋ง๋ ๋ค.
topictable.columns = ['๋ฌธ์ ๋ฒํธ', '๊ฐ์ฅ ๋น์ค์ด ๋์ ํ ํฝ', '๊ฐ์ฅ ๋์ ํ ํฝ์ ๋น์ค', '๊ฐ ํ ํฝ์ ๋น์ค']
topictable[:10]
์ฐธ๊ณ
Wikidocs : ์ ์ฌ ์๋ฏธ ๋ถ์
Wikidocs : ์ฌ์ดํท๋ฐ์ ์ ์ฌ ๋๋ฆฌํด๋ ํ ๋น ํ์ต
ratsgoโs blog for textmining
Latent Semantic Analysis โ Deduce the hidden topic from the document
Related Posts
Summary | AI-OCR๋? | |
Summary | CNN Architectures | |
TIL | LeakGAN์ด๋? - NLP Text Generation Model |
๐ You need to log in to GitHub to write comments. ๐
If you can't see comments, please refresh page(F5).