介绍
你有没有去过那种运营良好的图书馆?我总是对图书馆馆员通过书名、内容或其他主题保持一切井井有条的方式印象深刻。但是如果你给他们数千本书,要求他们根据书的种类整理出来,他们很难在一天内完成这项任务,更不用说一小时!
但是,如果这些书以电子的形式出现,就难不倒你了,对吧?所有的整理会在几秒之间完成,并且不需要任何人工劳动。自然语言处理(NLP)万岁!
看看下面这段话:
你可以从高亮的词语中总结出,这段话有三个主题(或概念)——主题1、主题2和主题3。一个良好的主题模型可以识别出相似的词语并将它们放在一组或一个主题下。上述示例中最主要的主题是主题2,表明这段文字主要关于虚假视频。
是不是很好奇?太好了!在本文中,我们将学习一种叫做主题建模的文本挖掘方法。这是一种非常有用的提取主题的技术,在面对NLP挑战时你会经常使用到它。
注意:我强烈建议您通读这篇文章了解SVD和UMAP等定义。它们在本文中经常出现,因此对它们有基本的理解有助于巩固这些概念。
目录
1. 什么是主题模型?
2. 何时使用主题建模?
3. 潜在语义分析(LSA)概述
4. 在Python中实现LSA
数据读取和检查
数据预处理
文档-词项矩阵(Document-Term Matrix)
主题建模
主题可视化
5. LSA的优缺点
6. 其他主题建模技术
什么是主题模型?
主题模型可定义为一种在大量文档中发现其主题的无监督技术。这些主题本质上十分抽象,即彼此相关联的词语构成一个主题。同样,在单个文档中可以有多个主题。我们暂时将主题模型理解为一个如下图所示的黑盒子:
这个黑盒子(主题模型)将相似和相关的词语聚集成簇,称为主题。这些主题在文档中具有特定的分布,每个主题都根据它包含的不同单词的比例来定义。
何时使用主题建模?
回想一下刚才提到的将相似的书籍整理到一起的例子。现在假设你要对一些电子文档执行类似的任务。只要文档的数量不太多,你就可以手动完成。但是如果这些文档的数量非常多呢?
这就是NLP技术脱颖而出的地方。对于这个任务而言,主题建模非常适用。
主题建模有助于挖掘海量文本数据,查找词簇,文本之间的相似度以及发现抽象主题。如果这些理由还不够引人注目,主题建模也可用于搜索引擎,判断搜索字段与结果的匹配程度。越来越有趣了,是不是?那么,请继续阅读!
潜在语义分析(LSA)概述
所有语言都有自己的错综复杂和细微差别,比如一义多词和一词多义,这对机器而言很难捕捉(有时它们甚至也被人类误解了!)。
例如,如下两个句子:
1. I liked his last novel quite a lot.
2. We would like to go for a novel marketing campaign.
在第一个句子中,'novel' 指一本书,而在第二个句子中,它的含义是新奇的、新颖的。
我们能够轻松地区分这些单词,是因为我们可以理解这些词背后的语境。但是,机器并不能捕捉到这个概念,因为它不能理解单词的上下文。这就是潜在语义分析(LSA)发挥作用的地方,它可以利用单词所在的上下文来捕捉隐藏的概念,即主题。
因此,简单地将单词映射到文档并没有什么用。我们真正需要的是弄清楚单词背后的隐藏概念或主题。LSA是一种可以发现这些隐藏主题的技术。现在我们来深入探讨下LSA的内部工作机制。
LSA的实施步骤
假设我们有m篇文档,其中包含n个唯一词项(单词)。我们希望从所有文档的文本数据中提取出k个主题。主题数k,必须由用户给定。
生成一个m×n维的文档-词项矩阵(Document-Term Matrix),矩阵元素为TF-IDF分数
然后,我们使用奇异值分解(SVD)把上述矩阵的维度降到k(预期的主题数)维
SVD将一个矩阵分解为三个矩阵。假设我们利用SVD分解矩阵A,我们会得到矩阵U,矩阵S和矩阵VT(矩阵V的转置)
矩阵Uk(document-term matrix)的每个行向量代表相应的文档。这些向量的长度是k,是预期的主题数。代表数据中词项的向量可以在矩阵Vk(term-topic matrix)中找到。
因此,SVD为数据中的每篇文档和每个词项都提供了向量。每个向量的长度均为k。我们可以使用余弦相似度的方法通过这些向量找到相似的单词和文档。
在Python中实现LSA
是时候启动Python并了解如何在主题建模问题中应用LSA了。开启Python环境后,请按照如下步骤操作。
数据读取和检查
在开始之前,先加载需要的库。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
pd.set_option("display.max_colwidth", 200)
在本文中,我们使用sklearn中的"20 Newsgroup"数据集,可从这里下载,然后按照代码继续操作。
from sklearn.datasets import fetch_20newsgroups
dataset = fetch_20newsgroups(shuffle=True, random_state=1, remove=('header','footers',quotes'))
documents = dataset.data
len(documents)
Output: 11,314
Dataset.target_names
['alt.atheism',
'comp.graphics',
'comp.os.ms-windows.misc',
'comp.sys.ibm.pc.hardware',
'comp.sys.mac.hardware',
'misc.forsale',
'rec.autos',
'rec.motorcycles',
'rec.sport.baseball',
'rec.sport.hockey',
'sci.crypt',
'sci.electronics',
'sci.med',
'sci.space',
'soc.religion.christian',
'talk.politics.guns',
'talk.politics.mideast',
'talk.politics.misc',
'talk.religion.misc']
该数据集包含分布在20个不同新闻组中的11314篇文档。
数据预处理
首先,我们尝试尽可能地清理文本数据。我们的想法是,使用正则表达式replace("[^a-zA-Z#]", " ")一次性删除所有标点符号、数字和特殊字符,这个正则表达式可以替换除带空格的字母之外的所有内容。然后删除较短的单词,因为它们通常并不包含有用的信息。最后,将全部文本变为小写,使得大小写敏感失效。
news_df = pd.DataFrame({'document':documents})
# removing everything except alphabets
news_df['clean_doc'] = news_df['document'].str.replace("[^a-zA-Z#]", " ")
# removing short words
news_df['clean_doc'] = news_df['clean_doc'].apply(lambda x: ' '.join([w for w in x.split() if len(w)>3]))
# make all the lowercase
news_df['clean_doc'] = news_df['clean_doc'].apply(lambda x: x.lower())
最好将文本数据中的停止词删除,因为它们十分混乱,几乎不携带任何信息。停止词是指'it', 'they', 'am', 'been', 'about', 'because', 'while'之类的词汇。
要从文档中删除停止词,我们必须对文本进行标记,将文本中的字符串拆分为单个的标记或单词。删除完停止词后,我们将标记重新拼接到一起。
from nltk.corpus import stopwords
stop_words = stopwords.words('english')
# tokenization
tokenized_doc = news_df['clean_doc'].apply(lambda x: x.split())
# remove stop-words
tokenized_doc = tokenized_doc.apply(lambda x: [item for item in x ifitem not in stop_words])
# de-tokenization
detokenized_doc = []
for i in range(len(news_df)):
t = ' '.join(tokenized_doc[i])
detokenized_doc.append(t)
news_df['clean_doc'] = detokenized_doc
文档-词项矩阵(Document-Term Matrix)
这是主体建模的第一步。我们将使用sklearn的TfidfVectorizer来创建一个包含1000个词项的文档-词项矩阵。
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer(stop_words='english',
max_features =1000, # keep top 1000 terms
max_df = 0.5,
smooth_idf = True)
X = vectorizer.fit_transform(news_df['clean_doc'])
X.shape # check shape of the document-term matrix
(11314, 1000)
我们也可以使用全部词项来创建这个矩阵,但这回需要相当长的计算时间,并占用很多资源。因此,我们将特征的数量限制为1000。如果你有计算能力,建议尝试使用所有词项。
主题建模
下一步是将每个词项和文本表示为向量。我们将使用文本-词项矩阵,并将其分解为多个矩阵。我们将使用sklearn的TruncatedSVD来执行矩阵分解任务。
由于数据来自20个不同的新闻组,所以我们打算从文本数据中提取出20个主题。可以使用n_components参数来制定主题数量。
from sklearn.decomposition import TruncatedSVD
# SVD represent documents and terms in vectors
svd_model = TruncatedSVD(n_components=20, algorithm='randomized', n_iter=100, random_state=122)
svd_model.fit(X)
len(svd_model.components_)
20
svd_model的组成部分即是我们的主题,我们可以通过svd_model.components_来访问它们。最后,我们打印出20个主题中前几个最重要的单词,看看我们的模型都做了什么。
terms = vectorizer.get_feature_names()
for i, comp in enumerate(svd_model.components_):
terms_comp = zip(terms, comp)
sorted_terms = sorted(terms_comp, key=lambda x:x[1], reverse=True)[:7]
print("Topic "+str(i)+": ")
for t in sorted_terms:
print(t[0])
print(" ")
Topic 0: like know people think good time thanks
Topic 0: like know people think good time thanks
Topic 1: thanks windows card drive mail file advance
Topic 2: game team year games season players good
Topic 3: drive scsi disk hard card drives problem
Topic 4: windows file window files program using problem
Topic 5: government chip mail space information encryption data
Topic 6: like bike know chip sounds looks look
Topic 7: card sale video offer monitor price jesus
Topic 8: know card chip video government people clipper
Topic 9: good know time bike jesus problem work
Topic 10: think chip good thanks clipper need encryption
Topic 11: thanks right problem good bike time window
Topic 12: good people windows know file sale files
Topic 13: space think know nasa problem year israel
Topic 14: space good card people time nasa thanks
Topic 15: people problem window time game want bike
Topic 16: time bike right windows file need really
Topic 17: time problem file think israel long mail
Topic 18: file need card files problem right good
Topic 19: problem file thanks used space chip sale
主题可视化
为了找出主题之间的不同,我们将其可视化。当然,我们无法可视化维度大于3的数据,但有一些诸如PCA和t-SNE等技术可以帮助我们将高维数据可视化为较低维度。在这里,我们将使用一种名为UMAP(Uniform Manifold Approximation and Projection)的相对较新的技术。
import umap
X_topics = svd_model.fit_transform(X)
embedding = umap.UMAP(n_neighbors=150, min_dist=0.5, random_state=12).fit_transform(X_topics)
plt.figure(figsize=(7,5))
plt.scatter(embedding[:, 0], embedding[:, 1],
c = dataset.target,
s = 10, # size
edgecolor='none'
)
plt.show()
如上所示,结果非常漂亮。每个点代表一个文档,颜色代表20个新闻组。我们的LSA模型做得很好。可以任意改变UMAP的参数来观察其对图像的影响。
可在此找到本文的完整代码。
LSA的优缺点
如上所述,潜在语义分析非常有用,但是确实有其局限性。因此,对LSA的优缺点都有充分的认识十分重要,这样你就知道何时需要使用LSA,以及何时需要尝试其他方法。
优点:
LSA快速且易于实施。
它的结果相当好,比简单的向量模型强很多。
缺点:
因为它是线性模型,因此在具有非线性依赖性的数据集上可能效果不佳。
LSA假设文本中的词项服从正态分布,这可能不适用于所有问题。
LSA涉及到了SVD,它是计算密集型的,当新数据出现时难以更新。
其他主题建模技术
除了LSA,还有其他一些先进并有效的主题建模技术,如LDA(Latent Dirichlet Allocation)和Ida2Vec。我们有一篇关于LDA的精彩文章,你可以在这里查看。Ida2vec是一个基于word2vec单词嵌入的更先进的主题建模技术。如果你想对它有更多了解,可以在下方的评论中留言,我们很乐意回答你的问题。
尾记
本文意于与大家分享我的学习经验。主题建模是个非常有趣的话题,当你在处理文本数据集时会用到许多技巧和方法。因此,我敦促大家使用本文中的代码,并将其应用于不同的数据集。如果您对本文有任何疑问或反馈,请与我们联系。快乐地去挖掘文本吧!
原文标题:
Text Mining 101: A Stepwise Introduction to Topic Modeling using Latent Semantic Analysis (using Python)
原文链接:
https://www.analyticsvidhya.com/blog/2018/10/stepwise-guide-topic-modeling-latent-semantic-analysis/