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个样本(每个样本为列表中的一个元素)
1corpus = ['this is the first document',
2 'this document is the second document',
3 'and this is the third one',
4 'is this the first document']
同时,其对应的词表为
xxxxxxxxxx
11vocabulary = ['this', 'document', 'first', 'is', 'second', 'the','and', 'one']
(1)统计词频
首先,根据已知的样本和词表,可以得到如下所示的一个词频统计矩阵
xxxxxxxxxx
41[[1 1 1 1 0 1 0 0]
2 [1 2 0 1 1 1 0 0]
3 [1 0 0 1 0 1 1 1]
4 [1 1 1 1 0 1 0 0]]
其中矩阵中的每一行表示对应样本中,词表中各个词出现的次数。例如第1行中的前4个1表示词表中的前4个词均在样本“this is the first document”中出现;第5个0表示词表中的“second”并没有在第一个样本中出现;第6个1表示词表中的“the”出现在第1个样本中;最后两个0表示词表中“and”和“one”这两个词也没有出现在第一个样本中。词频矩阵中的其它3行也是同理。
(2)计算逆文档频率
由式可知,对于词表中的每一个词,根据其在整个样本中的出现情况都可以计算得到一个IDF值。因此,对于整个词表来说,可以计算得到如下所示的一个IDF向量
xxxxxxxxxx
11[1. 1.223 1.510 1. 1.916 1. 1.916 1.916]
例如对于单词“document”来说它出现在了3个样本中,因此其计算过程为
(3)计算TF-IDF
在计算得到样本中每一个词的词频,以及词表中每一个词的IDF值后,便可以根据计算得到样本中每一个词的TF-IDF值,最终得到如下所示的TF-IDF权重矩阵
xxxxxxxxxx
41[[1. 1.223 1.510 1. 0. 1. 0. 0. ]
2 [1. 2.446 0. 1. 1.916 1. 0. 0. ]
3 [1. 0. 0. 1. 0. 1. 1.916 1.916]
4 [1. 1.223 1.510 1. 0. 1. 0. 0. ]]
例如对于第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类来完成。
x1corpus = ['this is the first document',
2 'this document is the second document',
3 'and this is the third one',
4 'is this the first document']
5vocabulary = ['this', 'document', 'first', 'is', 'second', 'the',
6 'and', 'one']
7
8from sklearn.feature_extraction.text import TfidfTransformer
9from sklearn.feature_extraction.text import CountVectorizer
10
11count = CountVectorizer(vocabulary=vocabulary)
12count_matrix = count.fit_transform(corpus).toarray()
13tfidf_trans = TfidfTransformer(norm=None)
14tfidf_matrix = tfidf_trans.fit_transform(count_matrix)
15idf_vec = tfidf_trans.idf_
16
17print(tfidf_matrix.toarray())
在上述代码中,第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。同时,在上面示例中,设置norm
为None
只是为了复现2.2中TF-IDF的计算过程,方便读者理解。
最后,需要解释一下的地方是上述代码第12行和第14行后面的toarray()
方法。由于利用词袋模型来表示文本通常来说维度会比较高,当样本较多时词表中可能会有数万或者是数十万个词。因此,在这种情况下对于每个样本来说,其通过词袋模型转换后的特征向量中都会存在大量的0,从而使得最后得到的特征矩阵非常稀疏(Sparse)。所以,为了提高存储效率,在sklearn中这样的稀疏矩阵都会采用稀疏方式进行存储。例如2.4节中,tfidf_matrix
的第1行采用稀疏表示的结果为
xxxxxxxxxx
51(0, 5) 1.0
2(0, 3) 1.0
3(0, 2) 1.5108256237659907
4(0, 1) 1.2231435513142097
5(0, 0) 1.0
其中第1行的含义是原始矩阵中第0行第5列的值是1;同理第3行的含义是原始矩阵中第0行第2列的值是1.510。注意,这里的所索引都是从0开始。并且可以发现,对于原始矩阵中取值为0的位置在稀疏矩阵中并没有被体现出来,这也就极大的节省了变量的存储空间。所以,当有需要查看原始非稀疏矩阵的结果时,就可以通过toarray()
方法来转换得到。不过在sklearn中,不管样本特征采用的是稀疏表示方法还是非稀疏表示方法,都可以直接用来进行建模。
3 词云图
在介绍完文本的向量化表示方法后,这里再顺便介绍一个实用的对文本按权重(频率)进行可视化的Python包word cloud。根据word cloud,可以将词语以权重大小或者是词频高低来生成词云图,如图1所示。
图1所展示的词云图是根据宋词分词统计后所形成的结果,其中字体越大表示其出现的频率越高或者是TF-IDF权重越大。
3.1 生成词云图
在生成词云图之前,首先需要统计得到词频或者是TF-IDF权重。接下来,以一个宋词数据集为例进行介绍。
1) 载入原始文本
xxxxxxxxxx
191def load_data_and_cut(file_path='./data/QuanSongCi.txt'):
2 cut_words = ""
3 with open(file_path, encoding='utf-8') as f:
4 for line in f:
5 line = line.strip('\n')
6 if len(line) < 20:
7 continue
8 seg_list = jieba.cut(clean_str(line), cut_all=False)
9 cut_words += (" ".join(seg_list))
10 all_words = cut_words.split()
11 return all_words
12
13def clean_str(string, sep=" "):
14 """
15 该函数的作用是去掉一个字符串中的所有非中文字符
16 """
17 string = re.sub(r"[^\u4e00-\u9fff]", sep, string)
18 string = re.sub(r"\s{1,}", sep, string) # 若有空格,则最多只保留1个宽度
19 return string.strip()
2)统计词频
接下来,通过Counter
计数器来完成分词结果中词频的统计,代码如下:
xxxxxxxxxx
91def get_words_freq(all_words, top_k=500):
2 c = Counter()
3 for x in all_words:
4 if len(x) > 1 and x != '\r\n':
5 c[x] += 1
6 vocab = {
7 for (k, v) in c.most_common(top_k):
8 vocab[k] = v
9 return vocab
在上述代码中,第2行用来定义一个计数器;第5行用来对每个词进行计数;第6-8行用来查找出现频率最高的前top_k
个词,并将词和出现频率以字典的形式进行存储。
3)生成词云图
在得到词频字典后,便可以通过WordCloud
类来完成词云图的生成,代码如下:
xxxxxxxxxx
111def show_word_cloud(word_fre):
2 word_cloud = WordCloud(font_path='./data/simhei.ttf',
3 background_color='white', max_font_size=70)
4 word_cloud.fit_words(word_fre)
5 plt.imshow(word_cloud)
6 plt.show()
7
8if __name__ == '__main__':
9 all_words = load_data_and_cut()
10 words_freq = get_words_freq(all_words)
11 show_word_cloud(words_freq)
在上述代码中,第2行用来载入汉字字体,因为word cloud默认不支持汉字;第4行用来生成词云图;第5-6行用来展示最后生成的词云图。在运行完上述代码后,便可以得到如图1所示的词云图。可以发现,全词中出现频率最高的几个词便是“人家”、“东风”、“何处”、“风流”等。
3.2 自定义样式
通过word cloud除了能够生成类似7-3所示的矩形词云图以外,更多场景下我们希望能够生成自定义样式的词云图。例如一个人的形状、一个建筑的形状等。在word cloud中,只需要在实例化对象WordCloud
时传入一个掩码矩阵即可完成这一想法。
xxxxxxxxxx
171def show_word_cloud(word_fre):
2 from PIL import Image
3 img = Image.open('./data/dufu.png')
4 img_array = np.array(img)
5 word_cloud = WordCloud(font_path='./data/simhei.ttf',
6 background_color='white', max_font_size=70, mask=img_array)
7 word_cloud.fit_words(word_fre)
8 plt.imshow(word_cloud)
9 plt.xticks([]) # 去掉横坐标
10 plt.yticks([]) # 去掉纵坐标
11 plt.tight_layout()
12 plt.show()
13
14if __name__ == '__main__':
15 all_words = load_data_and_cut()
16 words_freq = get_words_freq(all_words)
17 show_word_cloud(words_freq)
在运行完上述代码后,便可以生成一个自定义形状的词云图,如图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.