1 引言

在前面的文章中,笔者介绍了两种基本的用于文本表示的词袋模型表示方法,两者之间的唯一区别就是一个考虑的词频而另外一个没有考虑。下面我们再介绍另外一种应用更为常见和广泛的词袋模型表示方式——TFIDF表示方法。

2 TF-IDF文本表示法

2.1 理解TF-IDF模型

之所以陆续的会出现不同的向量化表示形式,其最终目的都只有一个,那就是即尽可能准确的对原始文本进行表示。TF-IDF为词频-逆文档频率(Term Frequence – Inverse Document Frequence)的简称。

首先需要明白的是TF-IDF实际上是TF与IDF两者的乘积。同时,出现TF-IDF的原因在于,通常来说在一个样本中一次词出现的频率越高,其重要性应该对应越高,即考虑到词频对文本向量的影响;但是如果仅仅只是考虑到这一个因素则同样会带来一个新的弊端,即有的词不只是在某个样本中出现的频率高,其实它在整个数据集中的出现频率都很高,而这样的词往往也是没有意义的。因此,TF-IDF的做法是通过词的逆文档频率来加以修正调整。

2.2 TF-IDF计算原理

TF-IDF的计算过程总体上可以分为两步,先统计词频,然后计算逆文档频率,最后将两者相乘得到TF-IDF值[1]。

(1) 统计词频

(2)计算逆文档频率

其中表示取自然对数。

根据式可以发现,如果一个词越是常见,那么对应的分母就越大,逆文档频率就越小。分母之所以要加1,是为了避免分母为0时(当使用自定义词表时)的平滑处理。这就是最原始的IDF计算方式。不过这种做法的一个瑕疵就是,当所有样本中都含有某个词的时候,计算出来的IDF就为负数。因此,sklearn在实现IDF计算时采用了另外一种平滑处理的方式

这样就同时避免了上面所出现的两种情况。在后面的计算示例中,笔者也将采用式中的公式来计算IDF值。

(3)计算TF-IDF

最后,根据计算得到的TF和IDF值便可以根据式来计算TF-IDF值。同时,对于数据集中的每一个词都能计算得到对应的TF-IDF值,最后将所有的值组合成一个矩阵便得到了文本的向量化表示。

注意:对于样本中的每一个词,如果其没有出现在词表中,那么对应的TF-IDF值为0。

2.3 TF-IDF计算示例

现在假设有如下4个样本(每个样本为列表中的一个元素)

同时,其对应的词表为

(1)统计词频

首先,根据已知的样本和词表,可以得到如下所示的一个词频统计矩阵

其中矩阵中的每一行表示对应样本中,词表中各个词出现的次数。例如第1行中的前4个1表示词表中的前4个词均在样本“this is the first document”中出现;第5个0表示词表中的“second”并没有在第一个样本中出现;第6个1表示词表中的“the”出现在第1个样本中;最后两个0表示词表中“and”和“one”这两个词也没有出现在第一个样本中。词频矩阵中的其它3行也是同理。

(2)计算逆文档频率

由式可知,对于词表中的每一个词,根据其在整个样本中的出现情况都可以计算得到一个IDF值。因此,对于整个词表来说,可以计算得到如下所示的一个IDF向量

例如对于单词“document”来说它出现在了3个样本中,因此其计算过程为

(3)计算TF-IDF

在计算得到样本中每一个词的词频,以及词表中每一个词的IDF值后,便可以根据计算得到样本中每一个词的TF-IDF值,最终得到如下所示的TF-IDF权重矩阵

例如对于第2个样本来说:

词表中的第1个词“this”在该样本中的出现的次数为1,所以其TF-IDF值为

词表中的第2个词“document” 在该样本中的出现的次数为2,所以其TF-IDF值为

词表中的第3个词“first” 在该样本中的出现的次数为0,所以其TF-IDF值为

同样,对于其它样本TF-IDF值的计算也可以按照上述过程进行,读者们可以自行进行验算。这样,我们就将原始的文本表示转换成了TF-IDF形式的数值表示。

2.4 TF-IDF示例代码

对于上述整个计算过程,可以使用sklearn中的CountVectorizer类和TfidfTransformer类来完成。

在上述代码中,第11行用来实例化类CountVectorizer,并同时传入词表;第12行用来对原始数据进行词频统计;第13-14行代码用来计算整个TF-IDF矩阵。同时,count_martrix是词频统计矩阵;tfidf_matrix是TF-IDF权重矩阵,也就是2.3节计算TF-IDF中的结果;idf_vec是IDF向量。

但在默认情况下,第13行代码中的参数norm的值为’l2’,也就是说此时TfidfTransformer会对TF-IDF权重矩阵的每一行进行标准化,即标准化后每一行的模为1。同时,在上面示例中,设置normNone只是为了复现2.2中TF-IDF的计算过程,方便读者理解。

最后,需要解释一下的地方是上述代码第12行和第14行后面的toarray()方法。由于利用词袋模型来表示文本通常来说维度会比较高,当样本较多时词表中可能会有数万或者是数十万个词。因此,在这种情况下对于每个样本来说,其通过词袋模型转换后的特征向量中都会存在大量的0,从而使得最后得到的特征矩阵非常稀疏(Sparse)。所以,为了提高存储效率,在sklearn中这样的稀疏矩阵都会采用稀疏方式进行存储。例如2.4节中,tfidf_matrix的第1行采用稀疏表示的结果为

其中第1行的含义是原始矩阵中第0行第5列的值是1;同理第3行的含义是原始矩阵中第0行第2列的值是1.510。注意,这里的所索引都是从0开始。并且可以发现,对于原始矩阵中取值为0的位置在稀疏矩阵中并没有被体现出来,这也就极大的节省了变量的存储空间。所以,当有需要查看原始非稀疏矩阵的结果时,就可以通过toarray()方法来转换得到。不过在sklearn中,不管样本特征采用的是稀疏表示方法还是非稀疏表示方法,都可以直接用来进行建模。

3 词云图

在介绍完文本的向量化表示方法后,这里再顺便介绍一个实用的对文本按权重(频率)进行可视化的Python包word cloud。根据word cloud,可以将词语以权重大小或者是词频高低来生成词云图,如图1所示。

图 1. 词云图

图1所展示的词云图是根据宋词分词统计后所形成的结果,其中字体越大表示其出现的频率越高或者是TF-IDF权重越大。

3.1 生成词云图

在生成词云图之前,首先需要统计得到词频或者是TF-IDF权重。接下来,以一个宋词数据集为例进行介绍。

1) 载入原始文本

2)统计词频

接下来,通过Counter计数器来完成分词结果中词频的统计,代码如下:

在上述代码中,第2行用来定义一个计数器;第5行用来对每个词进行计数;第6-8行用来查找出现频率最高的前top_k个词,并将词和出现频率以字典的形式进行存储。

3)生成词云图

在得到词频字典后,便可以通过WordCloud类来完成词云图的生成,代码如下:

在上述代码中,第2行用来载入汉字字体,因为word cloud默认不支持汉字;第4行用来生成词云图;第5-6行用来展示最后生成的词云图。在运行完上述代码后,便可以得到如图1所示的词云图。可以发现,全词中出现频率最高的几个词便是“人家”、“东风”、“何处”、“风流”等。

3.2 自定义样式

通过word cloud除了能够生成类似7-3所示的矩形词云图以外,更多场景下我们希望能够生成自定义样式的词云图。例如一个人的形状、一个建筑的形状等。在word cloud中,只需要在实例化对象WordCloud时传入一个掩码矩阵即可完成这一想法。

在运行完上述代码后,便可以生成一个自定义形状的词云图,如图2右所示。

图 2. 自定义词云图样式

在上述代码中,第3-4行用来打开一张图片,并同时转换为一个矩阵;第6行在实例化类WordCloud时需要将这个矩阵赋值到mask参数。这样便能够生成自定义样式的词云图。这里需要注意的一个地方就是,选择的这张图片的背景一定要是纯白色的,因为WordCloud的填充原理就是在图片的非白色区域进行填充。如果使用的是一张非白色背景的图片,那么最后生成的词云图依旧是一个矩形。

4 总结

在这篇文章中,笔者首先详细介绍了TF-IDF的原理以及其计算过程,包括sklearn中的代码实现过程等;接着介绍了如何使用word cloud来根据词频生成对应的词云图。

本次内容就到此结束,感谢您的阅读!如果你觉得上述内容对你有所帮助,欢迎分享至一位你的朋友!若有任何疑问与建议,请添加笔者微信'nulls8'或加群进行交流。青山不改,绿水长流,我们月来客栈见!

引用

[1] Scikit-learn: Machine Learning in Python, Pedregosa et al., JMLR 12, pp. 2825-2830, 2011.