토픽 모델링 (Topic Modeling)
- 토픽 (Topic) : 문서 집합 안에서 논의되는 주제나 개념을 의미
- 모델링 (Modeling) : 통계적인 방법으로 데이터의 패턴을 추출하는 과정
즉, 토픽 모델링이란
대규모 텍스트 데이터에 존재하는 다양한 주제를 자동으로 식별하고 분류하는 과정.
이를 통해 데이터 안에 숨겨진 주제 구조를 발견하고 이해하는 목적
=> 텍스트 마이닝, 자연어 처리, 정보 검색 등 다양한 분야에서 활용
토픽 모델링의 주제
: 일반적으로 단어의 집합으로 표현.
텍스트 내의 특정 패턴이나 빈도를 기반으로 선택
- 데이터 소스에 따라 주제의 범위가 결정됨
ex. 뉴스 기사, 소셜 미디어, 학술 논문 등
- 데이터의 트렌드와 특정 이벤트를 반영하는 경향
- 특정 주제와 비슷한 텍스트만 추려내는 등 활용 가능
토픽 모델링 사례
- 직원 복지를 위해 설문 결과를 분석 : Dell
-> 직원 만족도 설문 조사 분석 => 토픽 모델링을 통해 의견 파악 가능
- 검색어 트렌드 : Naver
: 인기 검색어로 사람들의 관심도 및 트렌드 확인 가능 => 사업 인사이트, 마케팅 아이디어 확인 가능
토픽 모델링 대표 모델, LDA
LDA
: 문서에 내재되어 있는 토픽을 찾아가는 알고리즘
LDA의 기본 가정
: 전체 문서는 여러 개별적인 문서들의 집합으로 구성되며,
하나의 개별 문서는 여러 개의 주제로 구성되고,
하나의 주제는 여러 단어들로 구성된다!
문서 1 : 우리 부모님은 건강을 위해 아침마다 수영을 하시고 저녁에는 산책을 합니다.
문서 2 : 나와 동생은 햄버거를 좋아합니다. 특히 치킨이 들어간 햄버거를 좋아하고, 어제는 피자를 먹었습니다.
문서 3 : 오늘은 나의 생일이라 햄버거를 먹었습니다. 그런데 살이 너무 많이 쪄서 산책과 수영을 시작했습니다.
-> 각 문서를 구성하는 토픽의 구성을 보면
문서 1 : 100% 토픽 A / 문서 2 : 100% 토픽 B / 문서 3 : 67% 토픽 A & 33% 토픽 B
-> 또한, 각 토픽을 구성하는 단어를 보면
토픽 A : 건강 (20%) / 수영 (40%) / 산책 (40%)
토픽 B : 햄버거 (60%) / 치킨 (20%) / 피자 (20%)
==> 이렇게 확률이나 비율의 집합을 분포로 표현한 것을 Dirichlet 분포라고 함
LDA는 문서 표면에 드러나지 않은 숨어있는 토픽의 확률 분포 (Latent Dirichlet)를 가정하고,
각 단어를 토픽에 할당(Allocation) 하는 분석 방법
* LDA : Latent Dirichlet Allocation
LDA 알고리즘
LDA의 중요한 두 확률값
: LDA 알고리즘을 결정짓는 중요한 두 확률값이 존재
1. 문서에 어떤 토픽이 들어있는가
=> P(토픽 t | 문서 d)
: 특정 문서 d에서 토픽 t가 차지하는 비율 (문서에서 각 토픽이 얼마나 중요한지를 나타냄)
2. 토픽에 어떤 단어가 들어있는가
=> P(단어 w | 토픽 t)
: 특정 토픽 t에서 단어 w가 차지하는 비율 (토픽에 특정 단어가 나타날 확률을 의미)
P(토픽 t | 문서 d, 단어 w)
: 특정 단어가 어떤 문서의 주제에 속할 확률
즉, 어떤 단어가 문서의 주제와 얼마나 잘 맞는지를 나타냄.
이 값이 크면 특정 단어가 그 문서의 주제와 매우 밀접한 관련이 있음을 의미함.
-> 이 값은 LDA에서 최종적으로 유추해야 하는 값이지만, 직접적으로 구하기 어려움
-> P(토픽 t | 문서 d) X P(단어 w | 토픽 t) 로 유추!
알고리즘 적용 과정
1. 토픽 개수 K 설정 (사용자의 가정)
2. 문서 내 모든 단어에 무작위로 K 토픽 중 하나를 할당
3. 단어 w의 토픽 할당을 결정하기 위해 나머지 단어들의 할당 결과를 활용
-> P(토픽 t | 문서 d) X P(단어 w | 토픽 t) 계산
-> 전체 문서의 모든 단어들을 대상으로 연산 진행
4. 토픽에 존재하는 단어들을 보고 토픽이 의미하는 주제를 사용자가 정의 (ex. 토픽1은 '먹거리' 구나!)
할당된 토픽을 기준으로 문서에 존재하는 토픽을 분석 (ex. 문서 1은 토픽 2, 6, 8이 있네)
토픽 모델링 실습
문제 : Yelp 데이터셋에 잠재하는 주제 찾기
입력 : 전체 텍스트 문서 집합, 토픽의 수
출력 : 문서별 토픽 분포, 토픽별 단어 분포
전처리
import re
import nltk
from nltk.corpus import stopwords
from nltk.stem import PorterStemmer
nltk.download('stopwords')
stop_words = set(stopwords.words('english'))
stemmer = PorterStemmer()
def preprocessing(text) :
# 소문자 변환
text = text.lower()
# 비 단어적 요소 제거
text = re.sub(r'\W', ' ', text)
# Tokeinize (띄어쓰기 단위로)
text = text.split()
# Stop words 제거
text = [t for t in text if t not in stop_words]
# 어간 추출 (PoterStemmer 사용)
text = [stemmer.stem(word) for word in text]
return text
LDA 실행
문서-단어 행렬(Document-Term Matrix, DTM)
: 전처리된 데이터에서 각 단어의 빈도를 나타내는 행렬 생성
from gensim import corpora
# Gensim의 Dictionary 객체를 생성
dictionary = corpora.Dictionary(preproc_data)
# [('love', 0), ('place', 1), ('wow', 2), ....
# 문서-단어 행렬을 생성
# 각 문장에 단어가 몇 번 나왔는지 나타냄 (단어 ID, 빈도수)
corpus = [dictionary.doc2bow(text) for text in preproc_data]
# [
# [(0, 1), (1, 1), (2, 1)], : 1번째 문장
# [...]
# ]
LDA 모델 생성 및 학습
from gensim.models import ldamodel
topicK = 3
num_trains = 10
lda_model = ldamodel.LdaModel(corpus,
num_topics=topicK, # 선정한 토픽 수
id2word=dictionary,# 단어 매핑 dictionary
passes=num_trains, # 학습 횟수
random_state=42)
LDA 모델 추론
# 토픽 별 단어 분포 확인
for k in range(topicK):
print(lda_model.show_topic(k, topn=20))
# 설정한 토픽과 단어를 보여줌. 숫자: 토픽을 구성하는데 단어가 얼마큼 쓰였는지 의미
# 즉, 총 3개의 토픽(topicK) 중 첫 번쨰 토픽은 service(0.01), good(0.01), disappoint(0.01)... 등 사용
# [('servic', 0.017086321), ('good', 0.014856899), ('disappoint', 0.010963259), ('like', 0.009364962), ('best', 0.009119847), ('great', 0.008761751), ('also', 0.00869444), ('realli', 0.008605385), ('place', 0.008238193), ('staff', 0.007839445), ('friendli', 0.007232641), ('order', 0.006750442), ('eat', 0.0066319024), ('nice', 0.0062933825), ('could', 0.0058348477), ('steak', 0.0052713323), ('amaz', 0.005116638), ('even', 0.0050651086), ('know', 0.004823529), ('burger', 0.004761508)]
# [('food', 0.042987805), ('back', 0.02138683), ('good', 0.019900084), ('servic', 0.017604794), ('place', 0.017388444), ('go', 0.017267667), ('time', 0.011744485), ('wait', 0.010304522), ('would', 0.008815033), ('ever', 0.0072248215), ('love', 0.0067911996), ('minut', 0.006650361), ('never', 0.006449497), ('come', 0.0062793614), ('pizza', 0.0056791217), ('like', 0.0056220596), ('vega', 0.005453013), ('want', 0.005266863), ('great', 0.0050176657), ('recommend', 0.0048496644)]
# [('place', 0.021426972), ('great', 0.017665138), ('restaur', 0.007841083), ('like', 0.0073311613), ('salad', 0.0071722586), ('star', 0.006963248), ('delici', 0.0064564915), ('time', 0.0063532447), ('get', 0.0053818994), ('pretti', 0.005359296), ('way', 0.0053427666), ('enough', 0.00500764), ('realli', 0.0050020404), ('experi', 0.0049468493), ('got', 0.0049142884), ('fri', 0.004801186), ('one', 0.0047682333), ('worth', 0.0047049844), ('much', 0.004480668), ('flavor', 0.0044741654)]
###############
# 토픽 별 단어 분포 확인
for k in range(topicK):
print(f'{k}번째 토픽을 구성하는 단어의 분포는...')
for word, prob in lda_model.show_topic(k, topn=5) :
print(f'{word} : {prob*100:.2f}%', end= ' ')
print()
print()
"""
0번째 토픽을 구성하는 단어의 분포는...
servic : 1.71% good : 1.49% disappoint : 1.10% like : 0.94% best : 0.91%
1번째 토픽을 구성하는 단어의 분포는...
food : 4.30% back : 2.14% good : 1.99% servic : 1.76% place : 1.74%
2번째 토픽을 구성하는 단어의 분포는...
place : 2.14% great : 1.77% restaur : 0.78% like : 0.73% salad : 0.72%
"""
# 문서 별 토픽 분포 확인
for document in corpus[:5]:
origin_doc = [dictionary[word_idx] for word_idx, word_num in document]
print(f'{origin_doc}에 속한 토픽의 분포는 아래와 같습니다.')
for topic_idx, topic_dist in lda_model[document]:
print(f'{topic_idx} 번째 토픽 : {topic_dist*100:.2f}% 확률')
"""
['love', 'place', 'wow']에 속한 토픽의 분포는 아래와 같습니다.
0 번째 토픽 : 8.79% 확률
1 번째 토픽 : 10.01% 확률
2 번째 토픽 : 81.20% 확률
['crust', 'good']에 속한 토픽의 분포는 아래와 같습니다.
0 번째 토픽 : 75.95% 확률
1 번째 토픽 : 12.74% 확률
2 번째 토픽 : 11.31% 확률
"""
결과 분석 -> 사람이 해야하는 일
=> LDA의 결과로 생성된 토픽의 의미는 사용자가 선정해야함.
==> 토픽을 구성하는 단어를 보고 토픽의 의미 선정
# 0번째 토픽을 구성하는 상위 10개 단어
for word, prob in lda_model.show_topic(0, topn=10):
print(f'{word}, {prob*100:.2f}%')
"""
servic, 1.71%
good, 1.49%
disappoint, 1.10%
like, 0.94%
best, 0.91%
great, 0.88%
also, 0.87%
realli, 0.86%
place, 0.82%
staff, 0.78%
"""
==> 0번째 토픽은 식당 서비스 품질과 고객 경험에 관한 것임
실제 예측
# 원문장과 토픽 분포를 보고 해석하기
target_idx = 10
print('원 문장 : ', data[target_idx])
print('전처리 문장 : ', preproc_data[target_idx])
print('문장 내 토픽 분포 : ')
for topic_idx, prob in lda_model[corpus[target_idx]]:
print(f' - {topic_idx}번 토픽 : {prob*100:.2f}%')
"""
원 문장 : Service was very prompt.
전처리 문장 : ['servic', 'prompt']
문장 내 토픽 분포 :
- 0번 토픽 : 76.57%
- 1번 토픽 : 12.26%
- 2번 토픽 : 11.16%
"""
=> 0번째 토픽과 가장 연관이 큼!
워드 클라우드
: 데이터를 시각적으로 표현한 것
문서나 데이터에서 가장 빈번하게 등장하는 단어들을 시각적으로 돋보이게 함
-> 빠르고 직관적인 이해
- 보고자 하는 목적에 맞춰 데이터 전처리 필요
ex. 뉴스, 소셜 미디어 등은 '명사' 기반
리뷰, 고객 피드백 등은 '형용사' 혹은 '동사' 활용
- 일반적으로 특수 문자와 숫자 제거
from wordcloud import WordCloud
import matplotlib.pyplot as plt
wordcloud = WordCloud(width=800, height=800,
background_color='white',
min_font_size=10, # 가장 작은 값의 크기
max_font_size=200).generate("텍스트") # 가장 큰 값의 크기
plt.figure(figsize=(8, 8), facecolor=None)
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.tight_layout(pad=0)
plt.show()
'STUDY > DevCourse' 카테고리의 다른 글
[데브코스][데이터 분석] 딥러닝 모델을 활용한 문장 분류 실습 (1) | 2024.05.15 |
---|---|
[데브코스][데이터 분석] 자연어 처리 & 문장 분류 (0) | 2024.05.15 |
[데브코스][데이터 분석] 감정 분석 (0) | 2024.05.14 |
[데브코스][데이터 분석] 단어 임베딩과 문장 임베딩 (0) | 2024.05.14 |
[데브코스][데이터 분석] 텍스트 마이닝 개요 (0) | 2024.05.13 |