使用Tfidf和TruncatedSVD做文本主题分析

本文深入探讨了TF-IDF算法的基本原理及其在文本特征提取中的作用,同时介绍了如何使用TfidfVectorizer将文本转换为tf-idf特征矩阵。此外,文章还详细解析了TruncatedSVD的概念及其实现过程,展示了如何通过SVD进行数据降维,以及如何从降维后的数据中筛选出最具代表性的文档和主题关键词。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tfidf

基本概念

所谓Tfidf,可以分为TF(词频, Term Frequency)和IDF(反文档频率, Inverse Document Frequency),然后二者相乘即可得到Tfidf.通俗的说,TF就是某个关键字出现的频率,DF是一个词在整个文库字典中出现的频率,IDF由DF进行运算得来.

TF的计算

N i , j = n i , j ∑ k n i , k N_{i, j} = \frac{n_{i, j}}{\sum_{k}{n_{i,k}}} Ni,j=kni,kni,j
其中:

  • n i , j n_{i, j} ni,j表示词 j j j出现在文本 i i i中的次数
  • N i , j N_{i,j} Ni,j表示词 j j j在文本 i i i中的词频
    显然分子为句子中出现的某个词的个数,分母为词的总数。得到的矩阵为 N N N s h a p e shape shape ( n u m _ o f _ d o c m e n t s , v o c a b u l a r y ) (num\_of\_docments, vocabulary) (num_of_docments,vocabulary).如果我们只使用TF来作为文本的特征,势必会受到常用词的干扰,因此我们需要想方法惩罚常用词。

IDF的计算

I D F j = l o g ∣ D ∣ D j + 1 IDF_j = log{\frac{|D|}{D_j+1}} IDFj=logDj+1D
其中:

  • I D F j IDF_j IDFj为词 j j j的反文档频率
  • D j D_j Dj为包含词 j j j的文档个数
  • + 1 +1 +1是为了防止分母为 0 0 0
    可见 I D F IDF IDF s h a p e shape shape ( v o c a b u l a r y , ) (vocabulary,) (vocabulary,).对于常用词,即使 T F i , j TF_{i,j} TFi,j的值很大,和 I D F j IDF_j IDFj相乘后也会变得很小。

TfidfVectorizer的使用

使用TfidfVectorizer可以把文本转换为tf-idf的特征矩阵,之后可以利用这个特征矩阵区进行文本相似度,LSA,文本搜索排序等任务.

from sklearn.feature_extraction.text import TfidfVectorizer

# 文本格式要求,中文需要使用分词工具预处理
document = ['我们 走 在 热闹 的 大街 上', 
            '当 我们 走到 街 中心 时', 
            '哪里 真是 人山人海', 
            '热闹非凡']

tfidf_model = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b", sublinear_tf=True, use_idf=True).fit(document)
# 得到的是一个类似于以三元组方式存储的稀疏矩阵,可以使用'.todense()'还原出原矩阵
sparse_result = tfidf_model.transform(document)

# 这一步可以得到一个词汇列表,可以使用索引还原出对应的词
print(tfidf_model.vocabulary_)

# 该方法可以得到所有的特征的名称列表
terms = tfidf.get_feature_names()

重要的参数:

  • token_pattern: 默认为r"(?u)\b\w\w+\b",这是一个正则表达式,这里的两个w表示其需要匹配长度大于2的单词,因此中文中的单字单词会被忽略,可以通过去除第一个w来实现保留单个字的词语.对这个参数修改可以实现更多的要求,比如保留特殊符号等等.
  • max_df/min_df: 该参数可以为0到1之间的浮点数或者为正整数,默认值为1.0.如果为浮点数,就过滤掉超过max_df比例的或者低于max_df比例的句子中的词语.可以实现过滤掉一些无用的词.
  • stop_words: 接收list类型的数据,如["是", "的"],过滤掉指定的停用词
  • vocabulary: 接收dict类型的数据,只使用特定的词汇.可以设定感兴趣的词并且只考虑他们.如:{"我":0, "呀":1, "!":2}

TruncatedSVD

重要概念

TSVD将训练样本X转换为低维的近似等于 X : ( m , n ) X:(m, n) X:(m,n)矩阵的 X k X_k Xk.
X ≈ X k = U k Σ k V k T X \approx X_k = U_k \Sigma_k V_k^T XXk=UkΣkVkT
其中, U k : ( m , k ) U_k :(m, k) Uk:(m,k), Σ k : ( k , k ) \Sigma_k: (k, k) Σk:(k,k), V k : ( n , k ) V_k:(n, k) Vk:(n,k), X k : ( m , n ) X_k:(m, n) Xk:(m,n)
X V : ( m , k ) XV:(m, k) XV(m,k)就是具有k个特征值的转换后的数据集。这里需要注意的一点是m代表样本数,n代表特征数,同理对行压缩则可以使用左奇异矩阵 U T X : ( k , n ) U^TX:(k, n) UTX:(k,n)。还有一点需要指出的是对列压缩即去掉相关性比较高列。
同理测试集转换可以直接使用 X k t e s t = X t e s t V X_k^{test} = X^{test}V Xktest=XtestV.
然而在实际情况中样本形状多为 ( f e a t u r e s , s a m p l e s ) (features, samples) (features,samples),这就需要我们根据具体情况进行选择。
除此之外,TruncatedSVD 类似于PCA,不同的是TSVD直接处理样本矩阵 X X X,而不是 X X X的协方差矩阵 X T X X^TX XTX(数据标准化后的协方差矩阵)。注意到我们的SVD也可以得到协方差矩阵 X T X X^TX XTX最大的 d d d个特征向量张成的矩阵,但是SVD有个好处,有一些SVD的实现算法可以不先求出协方差矩阵 X T X X^TX XTX,也能求出我们的右奇异矩阵 V V V,如梯度下降法。也就是说,我们的PCA算法可以不用做特征分解( X T X X^TX XTX是方阵,可以直接做特征值分解),而是做SVD来完成。这个方法在样本量很大的时候很有效。实际上,scikit-learn的PCA算法的背后真正的实现就是用的SVD,而不是我们我们认为的暴力特征分解。
更多的关于SVD的内容可以查看奇异值分解(SVD)原理与在降维中的应用.

TSVD的使用

上面一步的得到的矩阵大小为 ( ∣ D ∣ , ∣ V ∣ ) (|D|, |V|) (D,V),这一步我们将对它进行降维得到 ( ∣ D ∣ , ∣ T ∣ ) (|D|, |T|) (D,T).其中 ∣ D ∣ |D| D为文档个数, ∣ V ∣ |V| V为单词个数, ∣ T ∣ |T| TSVD降维后的主题个数。

n_pick_topics = 3            # 设定主题数为3
svd = TruncatedSVD(n_pick_topics)               
X = svd.fit_transform(sparse_result)

对于一个特定行 X [ d , : ] X[d, :] X[d,:]来说,最大的值对应的主题 t m a x t_{max} tmax是这篇文档最有可能的主题;
对于一个特定列 X [ : , t ] X[:, t] X[:,t]来说,最大的值对应的文档 d m a x d_{max} dmax是最具备该主题的文档,也就是说文档 d m a x d_{max} dmax在该主题上最具有代表性。
我们可以通过如下方法筛选出文档 d m a x d_{max} dmax,该方法为每个主题筛选了两篇最具有代表性的文档。

n_pick_docs= 2
topic_docs_id = [X[:,t].argsort()[:-(n_pick_docs+1):-1] for t in range(n_pick_topics)]

除此之外,svd.components_是一个大小为 ( ∣ T ∣ , ∣ V ∣ ) (|T|, |V|) (T,V),每一行为主题在每个单词上的分布。我们可以通过这个矩阵得到哪些词对主题t贡献最大。下面这段代码为每个主题选取了4个关键字。

n_pick_keywords = 4
topic_keywords_id = [svd.components_[t].argsort()[:-(n_pick_keywords+1):-1] for t in range(n_pick_topics)]
topic_keywords_id
以下是使用Scikit-Learn库的K-Means算法对20个新闻组数据集进行文本聚类分析的代码,同时绘制了聚类结果的散点图: ```python import numpy as np import matplotlib.pyplot as plt from sklearn.datasets import fetch_20newsgroups from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import KMeans from sklearn.decomposition import TruncatedSVD from sklearn.pipeline import make_pipeline from sklearn.preprocessing import Normalizer # 加载数据集 dataset = fetch_20newsgroups(subset='all', shuffle=True, random_state=42) # 获取停用词 stopwords = set() with open('stopwords.txt', 'r') as f: for line in f: stopwords.add(line.strip()) # 进行TF-IDF向量化 tfidf_vectorizer = TfidfVectorizer(stop_words=stopwords, max_df=0.5, min_df=2) tfidf = tfidf_vectorizer.fit_transform(dataset.data) # 使用TruncatedSVD进行降维 svd = TruncatedSVD(n_components=50) normalizer = Normalizer(copy=False) lsa = make_pipeline(svd, normalizer) tfidf_lsa = lsa.fit_transform(tfidf) # 进行聚类 num_clusters = 20 km = KMeans(n_clusters=num_clusters, init='k-means++', max_iter=100, n_init=1) km.fit(tfidf_lsa) # 绘制聚类结果 plt.figure(figsize=(10, 6)) plt.scatter(tfidf_lsa[:, 0], tfidf_lsa[:, 1], c=km.predict(tfidf_lsa), cmap='Set2', s=5) plt.title('20 Newsgroups Clustering Results') plt.xlabel('Dimension 1') plt.ylabel('Dimension 2') plt.show() ``` 在这个示例中,我们使用了Scikit-Learn的fetch_20newsgroups数据集进行实验。首先,我们读取停用词文件,并使用TfidfVectorizer将文本数据转换为TF-IDF向量表示。然后,我们使用TruncatedSVD对向量化后的数据进行降维,以减少特征数量。接下来,我们使用K-Means算法对降维后的数据进行聚类,并绘制聚类结果的散点图。其中,每个点的颜色表示所属的簇。可以看到,K-Means算法能够将不同主题的新闻文章聚类到不同的簇中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值