前言
在许多NLP任务中,将文档转换成数学形式的“向量”是解决任务所必须的处理过程。其中词袋模型(Bag of Word)、TF-IDF是两种最基本的处理方式。
BOW原理
假设有 M M M篇需处理的文档,那么怎样使用向量的形式来表示每一篇文档呢?并且这个向量应该不能丢失掉原始文档的过多信息。
一种思路为, 可以设置 N N N个问题,然后对于每一篇文档,依次回答给定的所有问题,并仅记录问题的答案,再将答案按照一定的格式组合成向量,那么就可以认为该向量包含了原始文档的部分信息。
具体到词袋模型, N N N个问题对应 N N N个词汇,每个问题可以表述为“该篇文档中,第 N N N个词出现几次?”,将所有问题的答案按词典顺序组合成向量,则该向量就是文档在词袋模型中的向量表示。
虽然词袋模型比较直观,但其最大的缺陷就是“丢失了语序信息, 而语序在NLP中十分重要”。
gensim实现BOW
corpus = [['human', 'interface', 'computer'],
['survey', 'user', 'computer', 'system', 'response', 'time'],
['eps', 'user', 'interface', 'system'],
['system', 'human', 'system', 'eps'],
['user', 'response', 'time'],
['trees'],
['graph', 'trees'],
['graph', 'minors', 'trees'],
['graph', 'minors', 'survey']]
from gensim import corpora
# construct dictionary
dct = corpora.Dictionary(documents=corpus)
# call doc2bow method
corpus_bow = [dct.doc2bow(document) for document in corpus]
import pprint
pprint.pprint(corpus_bow)
# outcome
# sparse format: filter 0 count item
[[(0, 1), (1, 1), (2, 1)],
[(0, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)],
[(2, 1), (5, 1), (7, 1), (8, 1)],
[(1, 1), (5, 2), (8, 1)],
[(3, 1), (6, 1), (7, 1)],
[(9, 1)],
[(9, 1), (10, 1)],
[(9, 1), (10, 1), (11, 1)],
[(4, 1), (10, 1), (11, 1)]]
TF-IDF原理
TF-IDF(Term Frequency - Inverse Document Frequency)可以认为是对BOW的改进,在BOW中,文档向量的表示仅采用了词频信息,忽略了词本身的重要性信息,然后词本身的信息也十分重要。举个例子,“的”这个词在一篇文档出现很多次,但该词对一篇文档的信息表示意义却不是很大,而有些文档“主题词”可能只出现一两次,却能充分表示文档的意义。
TF-IDF的计算如下所示:
T
F
−
I
D
F
i
,
j
=
T
e
r
m
F
r
e
q
u
e
n
c
y
i
,
j
∗
l
o
g
2
(
D
D
o
c
u
m
e
n
t
F
r
e
q
u
e
n
c
y
i
)
TF-IDF_{i,j} = TermFrequency_{i, j} * log_2(\frac D {DocumentFrequency_i})
TF−IDFi,j=TermFrequencyi,j∗log2(DocumentFrequencyiD)
i
i
i表示某个词,
j
j
j表示某篇文档,
T
e
r
m
F
r
e
q
u
e
n
c
y
i
,
j
TermFrequency_{i, j}
TermFrequencyi,j表示第
i
i
i个词在第
j
j
j篇文档中的频数,
D
D
D表示文档库中文档的数量,
D
o
c
u
m
e
n
t
F
r
e
q
u
e
n
c
y
i
DocumentFrequency_i
DocumentFrequencyi表示文档库中包含第
i
i
i个词的文档的数量。从上述计算表达式可以看出,若某个词在大量文档中都出现,则其TF-IDF值将相对较低。
gensim 实现TF-IDF
from gensim import models
# 不对文档向量进行归一化
tf_idf = models.TfidfModel(bow_corpus, normalize=False)
print(tf_idf[bow_corpus[0]]
# outcome
# [(0, 2.1699250014423126), (1, 2.1699250014423126), (2, 2.1699250014423126)]
# import math
# 1 * math.log(9/2, 2) == 2.1699250014423126
# 对文档向量进行归一化
tf_idf = models.TfidfModel(bow_corpus, normalize=True)
print(tf_idf[bow_corpus[0]]
# outcome
# [(0, 0.5773502691896257), (1, 0.5773502691896257), (2, 0.5773502691896257)]
# math.pow(math.pow(0.5773502691896257, 2) * 3, 0.5) == 1.0