IDF weighting(Inverse Document Frequency)**
逆文档频率权重
Suppose a token t
IDF(t) = log(ND/NDt)
ND表示 the total number of documents;
NDt 表示出现t的文档数量 the number of documents that include t;
上述公式可知当ND和NDt相近似时,IDF(t)就越近似0;
并且上述公式不关心t在某个document中出现的次数。
Document有效长度
Suppose query consists only one token.
Suppose d1 (simlarity 100%),cause d1 consists only one token that just matching the query.
Intuitively, measure Sim(q,d) should take the length of document and query into consideration.
关于其它更具体的不在赘述,关于此内容远不止这些,讨论可以联系邮箱评论讨论
TF-IDF (Term Frequency-Inverse Document Frequency)
这个词汇在用Elastic-Search的时候听到过,但不去了解很难清楚这个东西是啥。
TF-IDF weight wtd of term t for document d is:
Wtd = Ftd*IDF(t);
IDF(t) = log(ND/NDt);
IDF(t)越大说明t只在少部分document中出现;
Ftd越大说明t在d中出现的次数越多。
Suppose t is just one term of query q;
把q当作一个document来处理,故 Wtq =ftq*IDF(t);
t在document q中的权值 等于 t在q中出现的次数*log(总次数/t出现的次数);
下文中实现了一个简单的TF-IDF算法
package com.lbh.tfidf;
import com.huaban.analysis.jieba.JiebaSegmenter;
import com.huaban.analysis.jieba.SegToken;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @Autohor liubh
* @Email lbhbinhao@163.com
* @Date 20200119
*/
public class TfIdf {
/**
* tf-idf算法
* @param token
* @param documents
* Wtd = Ftd*IDF(t);
* IDF(t) = log(ND/NDt);
* @return
*/
public static Map tf_idf(String token, List<List<String>> documents){
//返回结果value 为token对应document的tf_idf值, key 为该token对应的documents index
HashMap<String, Double> result = new HashMap<>();
Integer ND = documents.size();
Integer NDt = 0;
/**
* 每个document是由token组成的,和句子无关
*/
//记录包含该token的index
ArrayList<Integer> indexes = new ArrayList<>();
for(int i =0; i<documents.size(); i++){
for (String term:documents.get(i)){
if (term.equals(token)){
if (!indexes.contains(i)) {
NDt++;
indexes.add(i);
System.out.println("the document index of all documents is:"+i);
}
continue;
}
}
}
double log_ND_NDt = 0;
if (NDt!=0) {
//double计算
log_ND_NDt = Math.log(ND*1.0/NDt);
}
else{
//模拟NDt无穷小
log_ND_NDt = 0.0001*0.0001*0.0001;
}
System.out.println("the size of documents:"+ND);
System.out.println("the size of token occurs:"+NDt);
for (int i = 0; i<indexes.size(); i++){
System.out.println("the loop of :"+(i+1)+ " times");
List<String> documentConsistsToken = documents.get(indexes.get(i));
Integer numbersOfTokenOfDocument = 0;
for (String word:documentConsistsToken){
if (word.equals(token)){
numbersOfTokenOfDocument++;
}
}
double Wtd = numbersOfTokenOfDocument*log_ND_NDt;
result.put(String.valueOf(indexes.get(i)),Wtd);
}
return result;
}
public static void main(String[] args) {
// String text = "hello hello hello bye bye";
// Pattern hello = Pattern.compile("bye");
// Matcher matcher = hello.matcher(text);
// int i=0;
// while (matcher.find()){
// i++;
// }
// System.out.println(i);
String text1 = "两个孤独的灵魂撞到了一起,都有着相同的遭遇而生出共鸣,彼此就像卖火柴的小女孩在冬夜里手中的那小小的火焰,彼此温暖着,但要说这是爱情么我不觉得是。如果继续写下去,玛蒂达会有新的生活,新的故事,这一段只是她人生中的一段回忆罢了,不过这段故事却会刻在他的气质里。就像是里昂给玛蒂达讲的他曾经的那段故事,做成电影并不一定比这段差,这是里昂人生中的一个片段。";
String text2 = "因为有恶警的存在,让我一度认为女孩家里人很无辜。这个人是线人,都会被这样无情的杀戮,太残暴了,尤其是那个弟弟,太舍不得了,这也是小女孩对警察恨之入骨的原因吧。\n" +
"\n" +
"于是她发誓一定要报仇,于是她找他求学。\n" +
"\n" +
"和他一起练习的时候,是他们最开心最安宁的时光了。\n" +
"\n" +
"小女孩更多的是感恩感激以及依赖之情,而里昂是从最开始的同情发展到最后的深沉的爱,甚至到了可以为她付出生命的地步。\n" +
"\n" +
"与其说小女孩因为他有了依靠,不如说他们两个孤独的人,成为了彼此的依靠。她,让他即使是一个杀手,也不太冷。";
String text3 = "爱情很美好,但会害死人。里昂爱的深沉,结局早就注定,就算没有因为爱情死去,也会在这样的生存环境下亡命天涯。玛蒂达不是聪明的小孩,诚如不懂事的年少的自己,一味感情用事,亲手害死自己的爱人。弟弟特别聪明,镇定,如果活着的机会给他,也许里昂的命运也会因此改写,是不一样的结局,两个原生家庭都破碎不堪的人就不该走到认识,于彼此是负担,即使有那么一刻是开心,但悲伤多于快乐。里昂跟托尼全程封闭式沟通,就像一个很有能力的职场精英,脚踏实地,满身才华,善良勤劳,简单纯粹,却没勇气跟雇主提价,也不会明确说出自己的需求,我需要钱,我收手不干,我要过自己的生活,只是在自己的专业领域无比卓越,却不知道怎样去获得自己想要的,完整表达自己的需求,并用智慧或者更多的心机获得,也高估人性,不懂得贪婪的人只会更贪婪。里昂的职业精神跟30岁的我那么接近,相似,突然就很难过,这世间,没有永恒,生命由无数个片段组成,在这个片段精彩,下个片段谢幕。我想,也是时候改变了,内在,外在你都拥有这么多,从此不妨大胆些";
String text4 = "冷面杀手大叔与萝莉的故事。我想,在走廊的初次遇见,就已经有某些情愫产生了。里昂是一个杀手,可最后的结尾却感谢玛蒂达让他有了根。里昂给了玛蒂达生的希望,给了玛蒂达活下去的庇护,给了玛蒂达胜似家人的陪伴。给她关心,给她爱护。替她铺好以后的路。在没有人比里昂对玛蒂达好了。我想里昂一直一直都是爱着玛蒂达的。但是他是一个孤独了多年的杀手,他一个人喝牛奶,一个人坐在黑暗中,他是顶级的杀手。因为他没有顾忌,没有牵绊。可是这样孤单地,冰冷地过一辈子也不过是虚度。所以,在某些方面来说,玛蒂达的出现其实是里昂生命的延续与扩展。";
String text5 = "里昂开门的那一瞬间就注定了他的结局,不过他开门之后度过了一辈子也难以忘怀的一段幸福时光,作为杀手你要想活的久,除了专业技能过硬,就是要有少的羁绊,因为这些羁绊后来可能都成为你的弱点,成为杀死自己的武器,不过人这一辈子,如果没有体会过什么是爱,就算度过了一生也是遗憾满满,哪怕最后是用生命换来的幸福,也值得人回味无穷,他们可以算作不伦恋了,两个人年龄差距非常大,但是在爱情面前,一切看起来都是那么美好,幸福,希望在评论里你们可以相爱到老,心心";
JiebaSegmenter jiebaSegmenter = new JiebaSegmenter();
List<SegToken> process1 = jiebaSegmenter.process(text1, JiebaSegmenter.SegMode.SEARCH);
ArrayList<String> document1 = new ArrayList<>();
for (SegToken word:process1){
document1.add(word.word);
}
List<SegToken> process2 = jiebaSegmenter.process(text2, JiebaSegmenter.SegMode.SEARCH);
ArrayList<String> document2 = new ArrayList<>();
for (SegToken word:process2){
document2.add(word.word);
}
List<SegToken> process3 = jiebaSegmenter.process(text3, JiebaSegmenter.SegMode.SEARCH);
ArrayList<String> document3 = new ArrayList<>();
for (SegToken word:process3){
document3.add(word.word);
}
List<SegToken> process4 = jiebaSegmenter.process(text4, JiebaSegmenter.SegMode.SEARCH);
ArrayList<String> document4 = new ArrayList<>();
for (SegToken word:process4){
document4.add(word.word);
}
List<SegToken> process5 = jiebaSegmenter.process(text5, JiebaSegmenter.SegMode.SEARCH);
ArrayList<String> document5 = new ArrayList<>();
for (SegToken word:process5){
document5.add(word.word);
}
ArrayList<List<String>> lists = new ArrayList<>();
lists.add(document1);lists.add(document2);
lists.add(document3);lists.add(document4);
lists.add(document5);
Map result = tf_idf("杀手", lists);
System.err.println(result);
}
}
运行结果:
故上述例子中用“杀手”去查询所有的文档库,根据结果
{1=0.5108256237659907, 3=2.043302495063963, 4=0.5108256237659907}
可得出所有为3的文档值最高,排序最高
文档排序为3>1=4
而文档0和2与“杀手”关键词不匹配