作为文本处理比较经典的方法,本文通过手动构造 tf_idf 矩阵和 R 的 tm包当中提供的 DocumentTermMatrix 函数方法的构造进行对比,来一种比较直观的解读.
1.构造原始文本数据
doc_1 <- "见鬼了"
doc_2 <- "我见鬼了"
doc_3 <- "我真的见鬼了"
text_data <- c(doc_1, doc_2, doc_3)
2. 数据分词构建文档-词项矩阵
library(tm)
library(Rwordseg)
text_data <- segmentCN(text_data)
corpus <- Corpus(VectorSource(text_data))
dtm <- DocumentTermMatrix(corpus)
inspect(weightTfIdf(dtm))
<<DocumentTermMatrix (documents: 3, terms: 6)>>
Non-/sparse entries: 4/14
Sparsity : 78%
Maximal term length: 1
Weighting : term frequency - inverse document frequency (normalized) (tf-idf)
Sample :
Terms
Docs 了 我 的 真 见 鬼
1 0 0.00000000 0.0000000 0.0000000 0 0
2 0 0.14624063 0.0000000 0.0000000 0 0
3 0 0.09749375 0.2641604 0.2641604 0 0
如果不清楚 tf_idf 具体怎么计算的, 可以通过上述代码步骤就无脑得到一个矩阵, 但是知其然而不知其所以然, 怎么可以?下面根据 tf_idf 公式定义, 手动制造一个看看结果与上述是否相符.
tf_idf,其实是两个部分, 一个是tf, 一个是idf.把它们的乘积定义为tf_idf.
1. tf顾名思义就是文档中的词频公式如下:
TF_{ij} = \frac{n_{i,j}}\sum_k n_{k, j}
2. idf逆向文件频率, 用于剔除掉文档之间共现的词, 以此保证单个文档中词的具有良好的区分能力.计算公式如下:
IDF_{i} = log\frac{|D|}{\left \{ j:t_{i} \epsilon d_{j} \right \}}
tf <- matrix(c(1/3, 0, 0, 0, 1/3, 1/3, 1/4, 1/4, 0, 0, 1/4, 1/4, 1/6, 1/6, 1/6, 1/6, 1/6, 1/6), 3, 6, byrow = TRUE)
idf <- c(log2(3/3), log2(3/2), log2(3/1), log2(3/1), log2(3/3), log2(3/3))
t(t(tf) * idf)
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 0 0.00000000 0.0000000 0.0000000 0 0
[2,] 0 0.14624063 0.0000000 0.0000000 0 0
[3,] 0 0.09749375 0.2641604 0.2641604 0 0
代码中的 tf 矩阵根据对文本one-hot 后除以其文本长度, 也就是对tf公式的实现. idf 中的log 使用以2为底数, 对数中分子表示文档的总数目, 分子表示词袋中词在所有文档中出现的总频率. 这里因为 R 的向量是列向量.所以直接对 tf矩阵乘以idf会按照tf的每一列去广播idf向量.所以采用两次转置.
最终结果是一样的.同时发现结果中会把文档之间共存的词权重数值都归了零, 留下了可以对该文档进行表示的词.其实这就是idf的作用, 而tf本身的作用只是对这些数据做了归一化.