| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | |||||
| 3 | 4 | 5 | 6 | 7 | 8 | 9 |
| 10 | 11 | 12 | 13 | 14 | 15 | 16 |
| 17 | 18 | 19 | 20 | 21 | 22 | 23 |
| 24 | 25 | 26 | 27 | 28 | 29 | 30 |
| 31 |
- anaconda3
- 아나콘다3
- 데이터싸이언티스트
- 가상환경만들기
- 데이터사이언티스트 #데이터 #모두의연구소 #데이터분석부트캠프
- 데이터사시언티스트 #데이터 #모두의연구소
- GCP
- 커널
- jupyter
- 아이펠톤
- 아나콘다설치
- 데이터사이언티스트
- python
- 모두의연구소
- 데이터분석환경
- nosql
- MongoDB
- 데이터
- 가상환경설정
- 데이터분석부트캠프
- 데이터전처리
- 데이터분석
- Today
- Total
EH_dream
딥러닝 : LSA와 LDA 본문
토픽 모델링을 통한 문서 이해
이번 주 학습의 마지막 파트는 문서에서 토픽을 추출해내는 대표적인 두 가지 방식인 LSA(Latent Semantic Analysis)와 LDA(Latent Dirichlet Allocation)에 대해 다뤘다. 텍스트를 수치화한 DTM이나 TF-IDF 행렬은 결국 단어의 의미를 고려하지 못하는 단점이 있는데, 이를 보완하는 것이 바로 차원 축소와 확률 기반의 토픽 모델링이었다.
LSA는 기본적으로 특잇값 분해(SVD: Singular Value Decomposition)를 통해 이루어진다. DTM 또는 TF-IDF 행렬에 대해 SVD를 수행하면, 이 행렬을 U, Σ, V^T로 분해할 수 있고, 여기서 상위 k개의 특잇값만 남기고 나머지를 제거하는 Truncated SVD를 수행하면 노이즈는 줄이면서 주요한 의미만 남긴 근사 행렬이 된다. 이 방식의 핵심은 단어들 간의 의미적 유사성을 저차원 공간에서 찾을 수 있다는 것이다. TruncatedSVD를 사용해 문서-단어 행렬을 압축하고 나서 각 축을 주제(topic)라고 가정해 그에 해당하는 핵심 단어들을 출력하는 방식으로 LSA를 실습했다.
실제로 뉴스 헤드라인 데이터셋을 받아 불용어를 제거하고 토큰화, 표제어 추출, 단어 길이 제한 등을 통해 전처리를 수행한 후 CountVectorizer를 통해 DTM을 만들었다. TruncatedSVD를 적용하고 각 축별로 가중치가 높은 단어들을 확인해보니, 특정 주제와 관련된 단어들이 잘 뭉쳐져 있었고, 이 벡터의 각 차원이 어떤 주제를 대변하고 있음을 유추할 수 있었다. 토픽 수는 10개로 지정했고, 각 토픽에 대해 가중치가 높은 단어 5개씩을 출력해 확인할 수 있었다.
반면, LDA는 확률 기반 모델이다. 문서가 토픽의 혼합이고, 토픽이 단어의 혼합이라는 가정 하에 문서-단어 분포에서 역으로 문서 내 토픽 분포와 각 토픽의 단어 분포를 추정하는 방식이다. 이론적으로 LDA는 단어가 특정 토픽에 속할 확률과 문서가 특정 토픽에 속할 확률을 곱해서, 토픽 구조를 도출해낸다. 사전에 정의된 토픽 수만큼 모델이 자동으로 단어와 문서를 분류하는데, 이를 통해 각 문서의 주제를 파악할 수 있다.
실습에서는 TF-IDF 행렬을 생성해 LDA 모델의 입력값으로 사용했다. Scikit-learn의 LatentDirichletAllocation 클래스를 사용하여 학습을 진행했고, 학습 결과로 도출된 10개의 토픽별 단어 분포를 확인했다. 각 토픽별로 가장 높은 확률을 가지는 단어들을 나열해보면, 서로 다른 주제를 분리해내는 것이 확연하게 드러났고, LSA보다 더 뚜렷하게 주제가 나뉘는 경향을 보였다.
# 상위 5,000개의 단어만 사용
tfidf_vectorizer = TfidfVectorizer(stop_words='english', max_features=5000)
tf_idf_matrix = tfidf_vectorizer.fit_transform(train_data)
# TF-IDF 행렬의 크기를 확인해봅시다.
print('행렬의 크기 :', tf_idf_matrix.shape)
결과
행렬의 크기 : (1054983, 5000)
from sklearn.decomposition import LatentDirichletAllocation
lda_model = LatentDirichletAllocation(n_components=10, learning_method='online', random_state=777, max_iter=1)
lda_model.fit_transform(tf_idf_matrix)
결과
array([[0.0335099 , 0.0335099 , 0.0335099 , ..., 0.1702395 , 0.0335099 ,
0.03351907],
[0.03365628, 0.03365628, 0.03365628, ..., 0.03365628, 0.03365628,
0.03365628],
[0.25184095, 0.0366096 , 0.0366096 , ..., 0.45528225, 0.0366096 ,
0.0366096 ],
...,
[0.26688724, 0.02914502, 0.14077174, ..., 0.02914502, 0.02914502,
0.02914502],
[0.02637829, 0.0263834 , 0.11651895, ..., 0.39092402, 0.02637829,
0.02637829],
[0.03376055, 0.03376055, 0.03376055, ..., 0.40866263, 0.21169867,
0.03376966]])
# LDA를 통해 얻은 결과 행렬의 크기를 확인
print(lda_model.components_.shape)
(10, 5000)
# 전체 코퍼스로부터 얻은 10개의 토픽과 각 토픽에서의 단어의 비중
terms = tfidf_vectorizer.get_feature_names_out() # 단어 집합. 5,000개의 단어가 저장됨.
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(5)) for i in topic.argsort()[:-n-1:-1]])
get_topics(lda_model.components_, terms)
결과
Topic 1: [('home', 4048.78778), ('hit', 3572.60152), ('market', 3141.55263), ('ban', 2995.63559), ('rise', 2909.02651)]
Topic 2: [('australia', 6721.04185), ('perth', 4551.37242), ('kill', 3977.09633), ('year', 3925.53959), ('say', 3395.39353)]
Topic 3: [('say', 7349.92516), ('court', 5250.21134), ('open', 3770.65617), ('state', 3656.60868), ('face', 3611.67807)]
Topic 4: [('man', 6521.51056), ('police', 6333.33358), ('charge', 5948.07591), ('queensland', 5552.61778), ('murder', 4677.1475)]
Topic 5: [('melbourne', 5298.43132), ('school', 3966.56666), ('report', 3792.92855), ('rural', 3521.89517), ('warn', 3379.34835)]
Topic 6: [('australian', 7674.39181), ('world', 4536.14226), ('country', 4168.83782), ('day', 3852.20674), ('crash', 3794.11334)]
Topic 7: [('election', 5416.23599), ('adelaide', 4868.15748), ('south', 4852.30258), ('house', 4481.58381), ('make', 3773.26989)]
Topic 8: [('canberra', 4323.53542), ('2016', 3962.69218), ('win', 3874.8274), ('qld', 3229.28135), ('price', 2762.91947)]
Topic 9: [('attack', 4787.33128), ('plan', 3166.71407), ('break', 2696.77267), ('fight', 2654.17809), ('need', 2567.57217)]
Topic 10: [('trump', 8189.6415), ('government', 6344.14927), ('die', 4026.92255), ('change', 3323.438), ('hour', 3124.37672)]
학습을 마치고 정리한 결과를 통해 두 방식의 차이도 명확히 인식할 수 있었다. LSA는 DTM이나 TF-IDF 행렬에 대해 차원 축소를 수행하여 축소된 공간에서 단어 간 유사성을 찾는 방식이라면, LDA는 확률 기반으로 단어와 문서가 토픽에 속할 확률을 추정하여 분류하는 방식이다. 즉, 하나는 선형대수 기반, 다른 하나는 베이지안 추론 기반이라는 방식의 차이가 있다.
이번 학습은 문서나 텍스트가 단순히 단어의 집합이 아니라, 그 안에 숨겨진 주제를 수치적으로 드러낼 수 있는 가능성을 보여주었다. 단어 간 의미 관계를 학습하고 이를 통해 문서의 주제를 분류하는 기술은 정보 검색, 뉴스 분석, 고객의 소리 분석 등 다양한 분야에서 활용될 수 있을 것이다. 특히 직접 데이터 전처리부터 토픽 추출까지 전 과정을 실습해보면서 각 기술의 구현 구조를 자연스럽게 익힐 수 있어 매우 유익한 시간이었다.
'딥러닝' 카테고리의 다른 글
| 딥러닝 프로젝트 : Vocabulary Size에 따른 ML/DL 성능 비교_25.06.19 (0) | 2025.06.19 |
|---|---|
| 딥러닝 학습 : 로이터 뉴스 분류 실습 _ 25.06.18 (0) | 2025.06.18 |
| 딥러닝 : 비지도학습 토크나이저 _ 25.06.16 (1) | 2025.06.18 |
| 딥러닝학습 : 단어빈도와 벡터화 25.06.16 (0) | 2025.06.18 |
| 딥러닝실습정리: Boston, Reuters, CIFAR_25.06.11 (1) | 2025.06.11 |