本文通過杰倫小公舉的歌詞來捋一捋LDA主題分析的流程,并對LDA主題進(jìn)行可視化,順便對比一下周杰倫作詞的歌詞主題與方文山作詞的歌詞主題有什么不同。
之前也用周杰倫的歌詞數(shù)據(jù)做了一個關(guān)鍵詞抽取和可視化,請戳那些年我們家杰倫的歌都在唱些什么
一、LDA是什么?
簡單來說,LDA( Latent Dirichelet Allocation)是一個文檔主題生成模型,包含詞、主題、文檔三層結(jié)構(gòu)。模型的思想是假設(shè)一篇文章的每個詞都是通過“以一定的概率選擇了某個主題,并在該主題下以一定的概率選擇詞語”這樣一個過程來得到。通過LDA主題分析,我們可以得到每一個文檔的主題分布。
二、怎么做LDA?
01 加載相關(guān)包
# 加載相關(guān)包
import numpy as np
import pandas as pd
import xlrd #讀取excel數(shù)據(jù)用
import openpyxl #輸出excel數(shù)據(jù)用
import re
import jieba
import jieba.analyse
#jieba.enable_parallel() #并行分詞開啟
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import LatentDirichletAllocation
# 文本主題可視化
import pyLDAvis
import pyLDAvis.sklearn
pyLDAvis.enable_notebook()
import warnings
warnings.filterwarnings("ignore")
02 加載數(shù)據(jù),并進(jìn)行預(yù)處理
def stopwordslist():
stopwords = [line.strip() for line in open('./data/stopwords.txt',encoding='UTF-8').readlines()]
return stopwords
def data_preprocessing():
songs = pd.read_excel("data/周杰倫歌詞.xlsx", encoding='utf-8')
# 歌詞只保留中文
songs['歌詞'] = songs['歌詞'].apply(lambda x: re.sub(r'[^\u4e00-\u9fa5]+', ' ',str(x), flags=re.U))
# 分詞,去停用詞
stopwords = stopwordslist()
songs['分詞'] = songs['歌詞'].apply(lambda x: ' '.join([w for w in jieba.lcut(x.strip()) if w not in stopwords and w != ' ']))
all_train = list(songs.分詞.values)
return all_train, songs
corpus, songs = data_preprocessing()
03 生成tfidf矩陣
tfidf_vectorizer = TfidfVectorizer(min_df = 5)
tfidf_mat = tfidf_vectorizer.fit_transform(corpus)
print('字典長度:', len(tfidf_vectorizer.vocabulary_))
Output: 字典長度: 306
04 LDA主題聚類
n_topics = 3 # 自定義主題個數(shù)
lda_model = LatentDirichletAllocation(n_components = n_topics, batch_size=8, random_state = 0)
# 使用TF-IDF矩陣擬合LDA模型
lda_model.fit(tfidf_mat)

我們來可視化一下主題分類效果,可以看到,在多維度量尺下,各個主題隔得很遠(yuǎn),主題2和主題3內(nèi)部比較緊湊,這個效果看起來還可以。
data = pyLDAvis.sklearn.prepare(lda_model, tfidf_mat, tfidf_vectorizer)
pyLDAvis.show(data)

我們打印各個主題權(quán)重最大的前15個詞來看看,每個主題是什么意思。
# 主題詞打印函數(shù)
def print_top_words(model, feature_names, n_top_words):
for topic_idx, topic in enumerate(model.components_):
print("Topic #%d:"%(topic_idx+1))
print(" ".join([feature_names[i] for i in topic.argsort()[-n_top_words-1:-1]]))
n_top_words = 15
tf_feature_names = tfidf_vectorizer.get_feature_names()
print_top_words(lda_model, tf_feature_names, n_top_words)

我試(hu)圖(luan)解釋一下各個主題在傳達(dá)什么:
主題1:關(guān)于快樂
主題2:關(guān)于后悔
主題3:關(guān)于愛情
我們來看看《七里香》專輯中每首歌的主題分布:
doc_topic_matrix = lda_model.transform(tfidf_mat)
doc_topic_df = pd.DataFrame(doc_topic_matrix)
songs['最大概率主題'] = np.argmax(doc_topic_matrix, axis=1) + 1
songs['主題1比例'], songs['主題2比例'], songs['主題3比例'] = doc_topic_df.iloc[:,0], doc_topic_df.iloc[:,1], doc_topic_df.iloc[:,2]
songs[songs['專輯'] == '七里香'].drop(columns=['專輯','年份','分詞'])

嗯,七里香這首歌簡直就是初戀的味道了,主題3的占比為85%,這很合理,其它的就。。。。。。我們還是來對比一下周杰倫的作詞主題與方文山的有什么不同吧。
三、方文山歌詞主題分布 vs 周杰倫歌詞主題分布
fang = songs[songs['填詞'] == '方文山']
jay = songs[songs['填詞'] == '周杰倫']
topics = ['About happiness','About regret','About Love']
fang_topic = fang.iloc[:,-3:].mean()
plt.pie(fang_topic,
labels=topics,
startangle=90,
shadow= True,
explode=(0,0,0.1), # explode the second one eating to draw attention
autopct='%1.1f%%') # adds percentage
plt.title("Topic Distribution of Fang Wenshan's songs")

jay_topic = jay.iloc[:,-3:].mean()
plt.pie(jay_topic,
labels=topics,
startangle=90,
shadow= True,
explode=(0,0,0.1), # explode the second one eating to draw attention
autopct='%1.1f%%') # adds percentage
plt.title("Topic Distribution of Jay's songs")

總體而言,方文山的主題集中在愛情故事,而周杰倫的歌詞主題分布則顯得相對分散。
四、參考資料
【1】An NLP Approach to Mining Online Reviews using Topic Modeling(with Python codes)
【2】鬼吹燈文本挖掘4:LDA模型提取文檔主題 sklearn LatentDirichletAllocation和gensim LdaModel
【3】網(wǎng)易云課堂AI工程師(自然語言處理)— 主題模型:文本主題抽取與表示
Enjoy reading :)
不點(diǎn)個小心心嗎 ?