이전 글
(1) 크롤링 + 빈도 분석 : https://nthree.tistory.com/24
(2) word cloud : https://nthree.tistory.com/43
(3) 장소 어휘 : https://nthree.tistory.com/44
대중가요 가사 분석 프로젝트의 마지막 토픽모델링이다.
이전 포스팅과 비교해서 상대적으로 내용이 길다고 생각하지만 요약해서 해보겠다!
구글링의 비율이 압도적이기도 하고 시간이 좀 지나서 기억이 잘 안나는 부분도 있다 ㅎㅎ
1
2
3
4
5
|
import konlpy
import re
from konlpy.tag import Okt
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
|
cs |
이전에 했던 것처럼
필요한 패키지를 설치해준 다음
데이터를 불러오고 간단한 전처리를 진행해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
def tokenize_korean_text(text):
text = re.sub(r'[^,.?!\w\s]','', text) ## ,.?!와 문자+숫자+_(\w)와 공백(\s)만 남김 # 앞에 r을 붙여주면 deprecation warning이 안뜸 (raw string으로 declare)
okt = konlpy.tag.Okt()
Okt_morphs = okt.pos(text, stem = True) # stem=True로 설정하면 동사원형으로 바꿔서 return
words = []
for word, pos in Okt_morphs:
if pos == 'Adjective' or pos == 'Verb' or pos == 'Noun': # 이 경우에는 형용사, 동사, 명사만 남김
words.append(word)
words_str = ' '.join(words)
return words_str
# 가사를 하나씩 tokenize해서 list로 저장
tokenized_list = []
for text in df2010['lyrics']:
tokenized_list.append(tokenize_korean_text(text))
print(len(tokenized_list))
print(tokenized_list[0])
|
cs |
200
앞 한마디 하다 뒤 내 얘길 안 좋다 해 차다 어이 없다 나 같다 이다 처음 보다 것 같다 왜 나르다 판단 하다 내 혹시 두렵다 겉 속 나르다 자다 알다 하다 내 겉모습 보다 한심하다 여자 보다 너 시선 난 웃기다 춤추다 땐 사랑 춤추다 내 모습 볼 때 넋 놓다 보고서 끝나다 손가락질 하다 그 위선 난 웃기다 이렇다 옷 이렇다 머리 모양 이렇다 춤 추다 여자 뻔하다 네 더 뻔하다 자신 없다 저 뒤 뒤 뒤 물러서다 되다 왜 자꾸 떠들다 네 속이다 훤히 보이다 건 아니다 겉 속 나르다 자다 알다 하다 내 겉모습 보다 한심하다 여자 보다 너 시선 난 웃기다 춤추다 땐 사랑 춤추다 내 모습 볼 때 넋 놓다 보고서 끝나다 손가락질 하다 그 위선 난 웃기다 날 감당 하다 수 있다 남다 찾다 진짜 남자 찾다 말로 남자 다운 척 하다 남자 날 불안하다 하다 않다 남다 없다 자신감 넘치다 내 나일 수 있다 자유롭다 두다 멀리 바라보다 겉 속 나르다 자다 알다 하다 내 겉모습 보다 한심하다 여자 보다 너 시선 난 웃기다 춤추다 땐 사랑 춤추다 내 모습 볼 때 넋 놓다 보고서 끝나다 손가락질 하다 그 위선 난 웃기다
빈도기반 countvectorizer와 lda를 비롯한 다양한 패키지를 설치해준다.
tokenization을 위한 함수를 정의해주었다.
2010년 가사를 리스트로 저장해주었고
그 안에 토크나이징한 텍스트를 넣어준다.
1
2
3
4
5
6
7
8
9
10
11
12
|
drop_corpus = []
for index in range(len(tokenized_list)):
corpus = tokenized_list[index]
if len(set(corpus.split())) < 3: # 같은 단어 1-2개만 반복되는 corpus도 지우기 위해 set()을 사용
df2010.drop(index, axis='index', inplace=True)
drop_corpus.append(corpus)
for corpus in drop_corpus:
tokenized_list.remove(corpus)
df2010.reset_index(drop=True, inplace=True)
|
cs |
중복된 corpus를 지우는 작업을 시행해준다.
1
2
3
4
5
6
|
count_vectorizer = CountVectorizer(max_df=0.1, max_features=1000, min_df=2, ngram_range=(1,2))
# 2개의 문서 미만으로 등장하는 단어는 제외, 전체의 10% 이상으로 자주 등장하는 단어는 제외
# bigram도 포함
feat_vect = count_vectorizer.fit_transform(tokenized_list)
print('CountVectorizer Shape:', feat_vect.shape)
|
cs |
count_vectorizer 학습을 시켜준다.
1
2
3
4
5
6
7
8
9
10
11
|
#토픽수 설정
lda = LatentDirichletAllocation(n_components=14)
lda.fit(feat_vect)
LatentDirichletAllocation(batch_size=128, doc_topic_prior=None,
evaluate_every=-1, learning_decay=0.7,
learning_method='batch', learning_offset=10.0,
max_doc_update_iter=100, max_iter=10, mean_change_tol=0.001,
n_components=14, n_jobs=None, perp_tol=0.1,
random_state=None, topic_word_prior=None,
total_samples=1000000.0, verbose=0)
|
cs |
lda 하이퍼파라미터를 설정하고 lda 모델을 학습시켜준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
def display_topics(model, feature_names, num_top_words):
for topic_index, topic in enumerate(model.components_):
print('Topic #', topic_index)
# components_ array에서 가장 값이 큰 순으로 정렬했을 때, 그 값의 array index를 반환.
topic_word_indexes = topic.argsort()[::-1]
top_indexes=topic_word_indexes[:num_top_words]
# top_indexes대상인 index별로 feature_names에 해당하는 word feature 추출 후 join으로 concat
feature_concat = ' '.join([feature_names[i] for i in top_indexes])
print(feature_concat)
# CountVectorizer객체내의 전체 word들의 명칭을 get_features_names( )를 통해 추출
feature_names = count_vectorizer.get_feature_names()
# Topic별 가장 연관도가 높은 word를 10개만 추출
display_topics(lda, feature_names, 10)
|
cs |
Topic # 0
아래 흔들다 흔들다 흔들다 울다 후회 때문 파란 후회 하다 입술 준비
Topic # 1
반하다 빛나다 고백 가다 모르다 사이 자꾸만 바로 몰래 밝다 어어
Topic # 2
그녀 그만하다 이름 좋다 하다 좋아하다 울다 노랗다 이야기 끌리다 죽다
Topic # 3
빨다 하다 우리 운명 그대로 소리 거울 놀다 아름답다 안녕 외톨이
Topic # 4
나가다 싶다 보고 오빠 제일 건지다 겨울 변하다 머물다 보다 되다 만나다 되다
Topic # 5
잠들다 내리다 벚꽃 새다 별빛 요즘 아름답다 가장 당신 따뜻하다
Topic # 6
나나 그땐 나나 나나 흔들다 그땐 그땐 빠지다 시작 하다 시작 비우다 그치다
Topic # 7
그녀 목소리 알다 사람 돌아오다 위해 수도 수도 없다 영원하다 향기 서로
Topic # 8
어쩌면 흘러가다 이번 라며 사랑 이번 마지막 라며 이번 마지막 가르치다 울다 일지
Topic # 9
좋아하다 문제 하다 않다 항상 미안하다 정신 설레다 듯이 미치다 듯이 그게
Topic # 10
없다 없다 잊어버리다 돌다 쉬다 꽂히다 나나 뛰다 알다 알다 보다 보다 되어다
Topic # 11
바다 춤추다 쉬다 추다 차라리 걷다 뻔하다 걷다 싶다 좋아하다 웃기다
Topic # 12
달라 뜨겁다 그대 그대 지나가다 돌아가다 원하다 돌아오다 먹다 피고 모든 순간
Topic # 13
치다 입술 누가 쉬다 흔들다 사이 어서 훔치다 몰래 티비
display topic 함수를 정의하고 토픽별로 연관도가 높은 단어들을 추출해보았다.
1
2
3
4
5
|
import pyLDAvis.sklearn # sklearn의 ldamodel에 최적화된 라이브러리
pyLDAvis.enable_notebook()
vis = pyLDAvis.sklearn.prepare(lda, feat_vect, count_vectorizer)
pyLDAvis.display(vis)
|
cs |
(직접 캡쳐한 영상)
topic별 단어 빈도를 효과적이게 시각화 해준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 노래별 가장 확률이 높은 topic으로 할당해줌
doc_topic = lda.transform(feat_vect)
doc_per_topic_list = []
for n in range(doc_topic.shape[0]):
topic_most_pr = doc_topic[n].argmax()
topic_pr = doc_topic[n].max()
doc_per_topic_list.append([n, topic_most_pr, topic_pr])
doc_topic_df = pd.DataFrame(doc_per_topic_list, columns=['Doc_Num', 'Topic', 'Percentage'])
doc_topic_df = doc_topic_df.join(df2010)
doc_topic_df.head()
|
cs |

노래별로 가장 확률이 높은 topic으로 할당해주었다.
위에서도 그럤듯이 topic name이 숫자로 되어있지만
실제로 토픽은 단어와 가사를 보고 적당한 주제로 임의 설정해주었다.
1
2
3
4
5
6
7
|
#토픽별로, 가장 높은 확률로 할당된 노래 가사 top 3 확인
for topic in range(len(doc_topic_df['Topic'].unique())):
print('Topic #', topic, '-----------------------------')
top_pr_topics = doc_topic_df[doc_topic_df['Topic'] == topic].sort_values(by='Percentage', ascending=False)
print(top_pr_topics['lyrics'].iloc[0])
print(top_pr_topics['lyrics'].iloc[1])
print(top_pr_topics['lyrics'].iloc[2], '\n')
|
cs |
마지막으로 토픽별 가장 확률이 높은, 말 그대로 주제를 가장 잘 반영하고 있는 노래 가사 top 3를 확인해 보았다.
결과물이 너무 사이즈가 커서 따로 업로드 하지는 않았다.
(이런식으로 분석할 수 있다는 것을 보여주려고)
1
|
doc_topic_df.groupby('Topic')[['Doc_Num']].count()
|
cs |
포스팅은 이렇게 원론적으로 진행했지만
실제 프로젝트 할 때는 앞선 모든 코드를 custom해주어 분석에 맞게 진행하였다.
마지막으로 토픽별로 count를 하여 가장 많이 사용된 topic들을 위주로 직접 topic을 정의해 준 후 분류하여 분석하였다.
실제 프로젝트에서 각 시기별로 자주 사용되었던 topic들은 다음과 같다.
이렇게 topic 모델링을 진행해보았다.
길고 긴 대중가요 가사 업로드를 마쳤는데
아직 깃허브에 정리를 하지 못하여 빨리 정리하고
원서 작성 후 잠시 쉬고싶다 ㅎㅎ
사실 바쁜척 하지만 여유로운데 너무 뒹굴뒹굴하느라 여유롭지 못하고 바쁜느낌?
뭐라는건지 모르겠지만 끝!
'자연어처리 > 실습' 카테고리의 다른 글
NLP 감정 분류 실습 (feat. 데이콘) (0) | 2022.11.29 |
---|---|
한국 대중가요 가사 분석 프로젝트 (3) 장소 어휘 (0) | 2022.10.24 |
한국 대중가요 가사 분석 프로젝트 (2) word cloud (0) | 2022.10.23 |
MRC(기계독해) 실습 1 : JSON 데이터셋 불러오기 (Groom Competition) (0) | 2022.10.06 |
구름 AI 자연어처리 team project bug search (0) | 2022.09.28 |
댓글