23-TF-IDF算法及其R实现
前文:20-余弦相似度及其R实现 https://www.jianshu.com/p/a894ebba4a1a
1、TF-IDF算法介绍
TF-IDF(term frequency–inverse document frequency,词频-逆文档频率)是一种用于信息检索(information retrieval)与文本挖掘(text mining)的常用加权技术。
如果某个词比较少见(在我们准备的文章库中的占比比较少),但是它在这篇文章中多次出现,那么它很可能反映了这篇文章的特性,正是我们所需要的关键词。在此,在词频TF的基础上又引出了逆向文档频率IDF的概念。一般来说,在一篇文章或一个句子中,每个词都有不同的重要性,这就是词的权重。在词频的基础上,赋予每一个词的权重,进一步体现该词的重要性。比如一篇报道中国农业养殖的新闻报道。最常见的词(“的”、“是”、“在”)给予最小的权重,较常见的词(“国内”、“中国”、“报道”)给予较小的权重,较少见的词(“养殖”)给予较大的权重。所以刻画能力强的词语,权重应该更高。
将TF和IDF进行相乘,就得到了一个词的TF-IDF值,某个词对文章重要性越高,该值越大,于是值排在前面的几个词,就是这篇文章的关键词。在实际应用中,还要考虑词的词性等多维度的特性,动词,名词,形容词的刻画能力也是有所差别的。
2、词频TF标准化的计算方法
- 词频=某个词在文章中出现的次数 / 文章的总词数
或者 - 词频=某个词在文章中出现的次数 / 该文出现次数最多的词出现的次数
词频标准化的目的是让所有的词频在同一维度上分析。词频标准化的两种计算方法,第一种情况得出词汇较小,不便于分析。一般情况下,第二种方法更适用,因为能够使词频的值相对大些,便于分析。
3、逆文档频率IDF的计算方法
- 逆文档频率=log(语料库的文章总数 / (包含该词的文档数+1))
- log函数是单调递增的,求log是为了归一化,保证逆文档频率不会过大。
- 分母+1是为了处理分母为0的情况。一般认为既然对这个词进行统计,这个词应该至少出现一次。这在训练模型的时候是正确的,但是在运用模型的时候,就不一定了,所以+1是为了防止分母为0的情况。
TF-IDF = 词频(TF) * 逆文档频率(IDF)
通过公式可以知道,TF-IDF与词频成正比,与包含该词的文档数成反比。
4、TF-IDF算法实践
还是使用微信朋友圈签名数据。
library(pacman)
p_load(tidyverse,textclean,jiebaR,tm)
# 载入数据
df <- read.csv("./data_set/friend.zl.csv",header = T,stringsAsFactors = F,row.names = 1)
# 数据脱敏、整理,并去掉非中文行
df.z <- df[,c("id","Signature")]
df.z$Signature <- df.z$Signature %>% replace_html() %>% replace_emoticon() %>%
replace_tag() %>% gsub("[a-zA-Z0-9\\.^_ㄟ|(|∞|﹏|.| ~|-。“”!!]","",.)
# 将繁体中文转换为简体
p_load(ropencc)
cc = converter(TW2S)
run_convert(cc, df.z$Signature)
# 去除空行
df.z <- df.z %>% filter(Signature != "")
# 中文分词,因为词量较少,就不去停用词了
wk <-worker()
# 对Signature的每一个元素,进行segment函数的处理,使用jieba默认分词引擎,最后得到一个新的列,命名为words,选取id和words列
corpus <- df.z %>% mutate(words = map(Signature,segment,jieba = wk)) %>% select(c("id","words"))
head(corpus)
## id words
## 1 V6 有恩于, 我, 的, 我, 砸锅卖铁, 心交, 你, 有, 仇于, 我, 的, 拆房, 卖地, 整垮, 你, 丬
## 2 V8 国庆节, 没, 地方, 去
## 3 V12 哈哈哈, 哈哈哈
## 4 V13 心想事成
## 5 V14 在, 人生, 的, 修行, 中, 历境, 炼心, 自在, 而行
## 6 V16 干净, 的, 灵魂, 需要, 慎独, 活着, 是, 种, 修行
# 计算词在文档中词频n
tf.table <- corpus %>% unnest(col = words) %>% count(id,words)
head(tf.table)
## # A tibble: 6 x 3
## id words n
## <chr> <chr> <int>
## 1 V102 兼济 1
## 2 V102 天下 1
## 3 V102 修身齐家 1
## 4 V103 纯 1
## 5 V103 护理 1
## 6 V103 面部 1
# tidytext包计算TF,IDF,TF-IDF,此处的TF是按第一种方法计算的
p_load(tidytext)
tf.idf.table <- tf.table %>% bind_tf_idf(term = words,document = id,n = n)
head(tf.idf.table)
## # A tibble: 6 x 6
## id words n tf idf tf_idf
## <chr> <chr> <int> <dbl> <dbl> <dbl>
## 1 V102 兼济 1 0.333 5.97 1.99
## 2 V102 天下 1 0.333 5.28 1.76
## 3 V102 修身齐家 1 0.333 5.97 1.99
## 4 V103 纯 1 0.125 5.97 0.746
## 5 V103 护理 1 0.125 5.97 0.746
## 6 V103 面部 1 0.125 5.97 0.746
# 提取每条签名最重要的三个关键词(自动摘要)
top3 <- tf.idf.table %>% group_by(id) %>% top_n(3,tf_idf) %>% ungroup();top3
## # A tibble: 1,423 x 6
## id words n tf idf tf_idf
## <chr> <chr> <int> <dbl> <dbl> <dbl>
## 1 V102 兼济 1 0.333 5.97 1.99
## 2 V102 天下 1 0.333 5.28 1.76
## 3 V102 修身齐家 1 0.333 5.97 1.99
## 4 V103 纯 1 0.125 5.97 0.746
## 5 V103 护理 1 0.125 5.97 0.746
## 6 V103 面部 1 0.125 5.97 0.746
## 7 V103 祛痘 1 0.125 5.97 0.746
## 8 V103 全额 1 0.125 5.97 0.746
## 9 V103 退款 1 0.125 5.97 0.746
## 10 V103 无效 1 0.125 5.97 0.746
## # … with 1,413 more rows
5、TF-IDF算法的不足
TF-IDF采用文本逆频率IDF对TF值加权取权值大的作为关键词,但IDF的简单结构并不能有效地反映单词的重要程度和特征词的分布情况,使其无法很好地完成对权值调整的功能,所以TF-IDF算法的精度并不是很高,尤其是当文本集已经分类的情况下。
在本质上IDF是一种试图抑制噪音的加权,并且单纯地认为文本频率小的单词就越重要,文本频率大的单词就越无用。这对于大部分文本信息,并不是完全正确的。IDF的简单结构并不能使提取的关键词十分有效地反映单词的重要程度和特征词的分布情况,使其无法很好地完成对权值调整的功能。尤其是在同类语料库中,这一方法有很大弊端,往往一些同类文本的关键词被掩盖。
总的来说,TF-IDF算法的不足如下:
(1)没有考虑特征词的位置因素对文本的区分度,词条出现在文档的不同位置时,对区分度的贡献大小是不一样的。
(2)按照传统TF-IDF,往往一些生僻词的IDF(逆文档频率)会比较高、因此这些生僻词常会被误认为是文档关键词。
(3)传统TF-IDF中的IDF部分只考虑了特征词与它出现的文本数之间的关系,而忽略了特征项在一个类别中不同的类别间的分布情况。
(4)对于文档中出现次数较少的重要人名、地名信息提取效果不佳。
6、利用TF-IDF计算文本相似度步骤
1)使用TF-IDF算法,找出两篇文章的关键词;
2)每篇文章各取出若干个关键词(比如20个),合并成一个集合,计算每篇文章对于这个集合中的词的词频(为了避免文章长度的差异,可以使用相对词频);
3)生成两篇文章各自的词频向量;
4)计算两个向量的余弦相似度,值越大(越接近于1)就表示越相似。