1 什么是TF-IDF?

词频逆词频模型(TF-IDF)的出现主要是为了解决BOW仅考虑了词频而忽略了词的重要性的问题。TF-IDF是基于统计来评估文本中词对于语料库中的一份文本的重要程度的方法。
TF-IDF使得文本内的高频率词语及其在整个文件集合中的低频率文件可以得到高权重的TF-IDF。在TF-IDF中,词语的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降,这从侧面反映TF-IDF倾向于保留重要的词语,过滤掉常见的词语。
举个栗子:
想象你是新手房产中介,摆在面前的有100个楼盘的文档资料,当客户来咨询楼盘的时候,你必须快速定位到某一篇文档,你会怎么做?我们当然会想要使用某种方法将其归类,什么方法合适呢? 你会不会去找每篇文档中的关键词?那些在某篇文档中出现频率很高的词,比如每篇文档中基本都会有谈论租房或新房或二手房这样的字眼,这些高频的字眼其实就代表着这篇文章的属性, 我们大概也能通过这些字眼判断这是不是客户关心的问题。
但是还有一个问题,很多语气词,没有代表意义的词在一篇文档中同样频率很高,比如我,中介,楼盘,和这种词,几乎每篇文档中都会存在,而且提及很多次。 它们很明显,虽然词频高,但是不具有区分力,用上面的方法,这些词也会被误认为很重要。所以学者很聪明,他们知道光看局部信息(某篇文档中的词频TF)会带来统计偏差, 它们就引入了一个全局参数(IDF),来判断这个词在所有文档中,是不是垃圾信息。很明显,我,中介,和这种词在全量文档中就是这样的垃圾信息, 而租房或新房或二手房是在全局下有区分力的词。所以如果我们把局部(TF)和全局(IDF)的信息都整合起来一起看的时候,我们就能快速定位到具体的文档啦。

2 TF-IDF的数学表达形式

其实它就是一个庞大的矩阵,用词语的数字向量来代表一篇文档,当比较文档时,就是在比较这些向量的相似性。
如果把向量展示在图上,就会有类似下面图案的样子,加上一个计算向量相似度的方式,比如cosine距离,我们就能判段哪些文档在这个三维空间上比较相似了。 图中两个蓝色向量离得越近,就代表他们越像。

3 实现

最简单的一个搜索引擎,就是计算好所有的文档向量,然后每次来一个搜索问题,就将这个问题转换成同样的向量,找到那个和问题向量最相近的文档。 这就是很多搜索引擎的本质了。
首先我用一句话代替一篇文档,总共15句话代表了15篇文档,当然文档很长,为了方便举例,用一句话代表一篇文档。如下代码所示:

下面展示一些 内联代码片

import numpy as np
from collections import Counter
import itertools
docs = ["it is a good day, I like to stay here","I am happy to be here","I am bob","it is sunny today","I have a party today","it is a dog and that is a cat","there are dog and cat on the tree","I study hard this morning","today is a good day","tomorrow will be a good day","I like coffee, I like book and I like apple","I do not like it","I am kitty, I like bob","I do not care who like bob, but I like kitty","It is coffee time, bring your cup",
]

将文档的单词转换成ID形式,这样便于后续通过ID进行统计。

docs_words = [d.replace(",", "").split(" ") for d in docs]
vocab = set(itertools.chain(*docs_words))
v2i = {v: i for i, v in enumerate(vocab)}
i2v = {i: v for v, i in v2i.items()}

词w 的IDF本质计算 IDF=log(所有文档数/所有文档中 词w 数), 当然还有很多种变异的计算方式。代码如下:

idf_methods = {"log": lambda x: 1 + np.log(len(docs) / (x+1)),"prob": lambda x: np.maximum(0, np.log((len(docs) - x) / (x+1))),"len_norm": lambda x: x / (np.sum(np.square(x))+1),}def get_idf(method="log"):# inverse document frequency: low idf for a word appears in more docs, mean less importantdf = np.zeros((len(i2v), 1))for i in range(len(i2v)):d_count = 0for d in docs_words:d_count += 1 if i2v[i] in d else 0df[i, 0] = d_countidf_fn = idf_methods.get(method, None)if idf_fn is None:raise ValueErrorreturn idf_fn(df)        # [n_vocab, 1]

词w 在 文档d 的TF本质计算 TF=文档d 中 词w 总数。

tf_methods = {"log": lambda x: np.log(1+x),"augmented": lambda x: 0.5 + 0.5 * x / np.max(x, axis=1, keepdims=True),"boolean": lambda x: np.minimum(x, 1),"log_avg": lambda x: (1 + safe_log(x)) / (1 + safe_log(np.mean(x, axis=1, keepdims=True))),}
def get_tf(method="log"):# term frequency: how frequent a word appears in a doc_tf = np.zeros((len(vocab), len(docs)), dtype=np.float64)    # [n_vocab, n_doc]for i, d in enumerate(docs_words):counter = Counter(d)for v in counter.keys():_tf[v2i[v], i] = counter[v] / counter.most_common(1)[0][1]weighted_tf = tf_methods.get(method, None)if weighted_tf is None:raise ValueErrorreturn weighted_tf(_tf)     # [n_vocab, n_doc]

TF 是用所有词组成的每篇文档向量表示,shape=[n_vocab, n_doc], 通过TF-IDF的乘积,就能得到每篇文档的TF-IDF向量表示了。

tf = get_tf()           # [n_vocab, n_doc]
idf = get_idf()         # [n_vocab, 1]
tf_idf = tf * idf       # [n_vocab, n_doc]

最终计算出来的TF-IDF实际是一个词语和文章的矩阵,代表着用词语向量表示的文章。

接着,有了这些向量表示,当我们进行搜索时,只需要将搜索的问题向量化,把搜索向量在文档向量上进行距离计算,就能算出来哪些文档更贴近搜索向量了。 我们尝试搜索 I get a coffee cup, 并返回15篇文档当中最像这句搜索的前3篇文档。

def cosine_similarity(q, _tf_idf):unit_q = q / np.sqrt(np.sum(np.square(q), axis=0, keepdims=True))unit_ds = _tf_idf / np.sqrt(np.sum(np.square(_tf_idf), axis=0, keepdims=True))similarity = unit_ds.T.dot(unit_q).ravel()return similaritydef docs_score(q, len_norm=False):q_words = q.replace(",", "").split(" ")# add unknown wordsunknown_v = 0for v in set(q_words):if v not in v2i:v2i[v] = len(v2i)i2v[len(v2i)-1] = vunknown_v += 1if unknown_v > 0:_idf = np.concatenate((idf, np.zeros((unknown_v, 1), dtype=np.float)), axis=0)_tf_idf = np.concatenate((tf_idf, np.zeros((unknown_v, tf_idf.shape[1]), dtype=np.float)), axis=0)else:_idf, _tf_idf = idf, tf_idfcounter = Counter(q_words)q_tf = np.zeros((len(_idf), 1), dtype=np.float)     # [n_vocab, 1]for v in counter.keys():q_tf[v2i[v], 0] = counter[v]q_vec = q_tf * _idf            # [n_vocab, 1]q_scores = cosine_similarity(q_vec, _tf_idf)if len_norm:len_docs = [len(d) for d in docs_words]q_scores = q_scores / np.array(len_docs)return q_scoresdef get_keywords(n=2):for c in range(3):col = tf_idf[:, c]idx = np.argsort(col)[-n:]print("doc{}, top{} keywords {}".format(c, n, [i2v[i] for i in idx]))tf = get_tf()           # [n_vocab, n_doc]
idf = get_idf()         # [n_vocab, 1]
tf_idf = tf * idf       # [n_vocab, n_doc]
print("tf shape(vecb in each docs): ", tf.shape)
print("\ntf samples:\n", tf[:2])
print("\nidf shape(vecb in all docs): ", idf.shape)
print("\nidf samples:\n", idf[:2])
print("\ntf_idf shape: ", tf_idf.shape)
print("\ntf_idf sample:\n", tf_idf[:2])

完整的代码如下所示:

import numpy as np
from collections import Counter
import itertools
from visual import show_tfidf
docs = ["it is a good day, I like to stay here","I am happy to be here","I am bob","it is sunny today","I have a party today","it is a dog and that is a cat","there are dog and cat on the tree","I study hard this morning","today is a good day","tomorrow will be a good day","I like coffee, I like book and I like apple","I do not like it","I am kitty, I like bob","I do not care who like bob, but I like kitty","It is coffee time, bring your cup",
]docs_words = [d.replace(",", "").split(" ") for d in docs]
vocab = set(itertools.chain(*docs_words))
v2i = {v: i for i, v in enumerate(vocab)}
i2v = {i: v for v, i in v2i.items()}def safe_log(x):mask = x != 0x[mask] = np.log(x[mask])return xtf_methods = {"log": lambda x: np.log(1+x),"augmented": lambda x: 0.5 + 0.5 * x / np.max(x, axis=1, keepdims=True),"boolean": lambda x: np.minimum(x, 1),"log_avg": lambda x: (1 + safe_log(x)) / (1 + safe_log(np.mean(x, axis=1, keepdims=True))),}
idf_methods = {"log": lambda x: 1 + np.log(len(docs) / (x+1)),"prob": lambda x: np.maximum(0, np.log((len(docs) - x) / (x+1))),"len_norm": lambda x: x / (np.sum(np.square(x))+1),}def get_tf(method="log"):# term frequency: how frequent a word appears in a doc_tf = np.zeros((len(vocab), len(docs)), dtype=np.float64)    # [n_vocab, n_doc]for i, d in enumerate(docs_words):counter = Counter(d)for v in counter.keys():_tf[v2i[v], i] = counter[v] / counter.most_common(1)[0][1]weighted_tf = tf_methods.get(method, None)if weighted_tf is None:raise ValueErrorreturn weighted_tf(_tf)def get_idf(method="log"):# inverse document frequency: low idf for a word appears in more docs, mean less importantdf = np.zeros((len(i2v), 1))for i in range(len(i2v)):d_count = 0for d in docs_words:d_count += 1 if i2v[i] in d else 0df[i, 0] = d_countidf_fn = idf_methods.get(method, None)if idf_fn is None:raise ValueErrorreturn idf_fn(df)def cosine_similarity(q, _tf_idf):unit_q = q / np.sqrt(np.sum(np.square(q), axis=0, keepdims=True))unit_ds = _tf_idf / np.sqrt(np.sum(np.square(_tf_idf), axis=0, keepdims=True))similarity = unit_ds.T.dot(unit_q).ravel()return similaritydef docs_score(q, len_norm=False):q_words = q.replace(",", "").split(" ")# add unknown wordsunknown_v = 0for v in set(q_words):if v not in v2i:v2i[v] = len(v2i)i2v[len(v2i)-1] = vunknown_v += 1if unknown_v > 0:_idf = np.concatenate((idf, np.zeros((unknown_v, 1), dtype=np.float)), axis=0)_tf_idf = np.concatenate((tf_idf, np.zeros((unknown_v, tf_idf.shape[1]), dtype=np.float)), axis=0)else:_idf, _tf_idf = idf, tf_idfcounter = Counter(q_words)q_tf = np.zeros((len(_idf), 1), dtype=np.float)     # [n_vocab, 1]for v in counter.keys():q_tf[v2i[v], 0] = counter[v]q_vec = q_tf * _idf            # [n_vocab, 1]q_scores = cosine_similarity(q_vec, _tf_idf)if len_norm:len_docs = [len(d) for d in docs_words]q_scores = q_scores / np.array(len_docs)return q_scoresdef get_keywords(n=2):for c in range(3):col = tf_idf[:, c]idx = np.argsort(col)[-n:]print("doc{}, top{} keywords {}".format(c, n, [i2v[i] for i in idx]))tf = get_tf()           # [n_vocab, n_doc]
idf = get_idf()         # [n_vocab, 1]
tf_idf = tf * idf       # [n_vocab, n_doc]
print("tf shape(vecb in each docs): ", tf.shape)
print("\ntf samples:\n", tf[:2])
print("\nidf shape(vecb in all docs): ", idf.shape)
print("\nidf samples:\n", idf[:2])
print("\ntf_idf shape: ", tf_idf.shape)
print("\ntf_idf sample:\n", tf_idf[:2])# test
get_keywords()
q = "I get a coffee cup"
scores = docs_score(q)
d_ids = scores.argsort()[-3:][::-1]
print("\ntop 3 docs for '{}':\n{}".format(q, [docs[i] for i in d_ids]))show_tfidf(tf_idf.T, [i2v[i] for i in range(tf_idf.shape[0])], "tfidf_matrix")

基于TF-IDF的简单搜索引擎的实现相关推荐

  1. 搜索引擎:文本分类——TF/IDF算法

    原理 TFIDF的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类.TFIDF实际上是:TF * IDF,TF ...

  2. DL之RNN:基于TF利用RNN实现简单的序列数据类型(DIY序列数据集)的二分类(线性序列随机序列)

    DL之RNN:基于TF利用RNN实现简单的序列数据类型(DIY序列数据集)的二分类(线性序列&随机序列) 目录 序列数据类型&输出结果 设计思路 序列数据类型&输出结果 1.t ...

  3. scitkit-learn:计算机科学论文的TF / IDF和余弦相似度

    几个月前,我下载了数千篇计算机科学论文的元数据,这样我就可以尝试编写一个迷你推荐引擎来告诉我接下来应该读什么论文. 由于我没有任何人可以阅读每篇论文的数据,因此排除了协作过滤方法,所以我认为我可以尝试 ...

  4. tf-idf词向量和bow_使用词袋Bow和TF IDF进行多标签分类

    tf-idf词向量和bow 1.加载数据 (1. Load the data) For this study, we are using Kaggle data for Toxic Comment C ...

  5. DL之GRU:GRU算法相关论文、建立过程(基于TF)、相关思路配图集合、TF代码实现

    DL之GRU:GRU算法相关论文.建立过程(基于TF).相关思路配图集合.TF代码实现 目录 GRU算法相关论文 GRU算法建立过程(基于TF) GRU算法的TF代码实现 GRU算法相关论文 GRU是 ...

  6. 关键词提取算法—TF/IDF算法

    关键词提取算法一般可分为有监督学习和无监督学习两类. 有监督的关键词提取方法可以通过分类的方式进行,通过构建一个较为完善的词表,然后判断每个文档与词表中的每个词的匹配程度,以类似打标签的方式,达到关键 ...

  7. DL之RNN:人工智能为你写周董歌词——基于TF利用RNN算法实现【机器为你作词】、训练测试过程全记录

    DL之RNN:人工智能为你写周董歌词--基于TF利用RNN算法实现~机器为你作词~.训练&测试过程全记录 目录 输出结果 模型监控 训练.测试过程全记录 训练的数据集 输出结果 1.test0 ...

  8. 【java毕业设计】基于java+Lucene+Tomcat的搜索引擎设计与实现(毕业论文+程序源码)——搜索引擎

    基于java+Lucene+Tomcat的搜索引擎设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+Lucene+Tomcat的搜索引擎设计与实现,文章末尾附有本毕业设计的论文和源 ...

  9. 数据挖掘_基于balance-scale数据集的简单分类任务验证性实验

    目录 一,题目描述 二,实验准备 1,数据集下载 1.1 常用数据集链接 1.2 UCI数据集 1.3 Kaggle 2,所需基础知识 2.1 分类的方法 2.2 去重 2.3 扩增数据集 2.4 特 ...

最新文章

  1. 这10个著名的思想实验,竟然是物理学家完成的
  2. python中用def实现自动排序_用 python 实现各种排序算法
  3. Matlab百度中Cell(单元数据)的用法
  4. Java培训教程之使用Lock取代synchronized
  5. linux 文件处理命令
  6. fastapi 传输文件存文件_python3 FastAPI框架入门 基本使用, 模版渲染, 数据交互,cookie使用, 上传文件, 静态文件配置...
  7. DataGridView的单元格内容即时更新方法
  8. JVM&NIO&HashMap简单问
  9. Hello工作室制作《无人深空》更新档
  10. SQL中的Having与Where的区别(面试常问)
  11. Abaqus帮助文档翻译——Abaqus/CAE主窗口介绍
  12. 有些CAD通过Arcgis程序读取后,发现面积不对
  13. 数据库新用户登录报错user test lacks create session privilege logon denied
  14. Vista Ultimate X64 绝对正宗的激活工具
  15. Android应用优化之冷启动优化
  16. AMD推土机架构桌面CPU品牌各代情况
  17. robotframework调用python类方法_【RF基础】RF调用Python函数基础
  18. 基于评论的跨境电商产品满意度分析_kaic
  19. chapter 2 古典密码技术
  20. Linux内核与编程

热门文章

  1. “usermod:UID‘0‘already exists”
  2. [Kudu基础]--Kudu+Impala介绍 | 微店数据科学团队博客
  3. 机器学习——线性模型之Softmax回归
  4. 怎么在Windows10中找回Windows7的照片查看器(Windows 照片查看器)win10新的照片查看器太难用了
  5. javascript中使用split对多个分割符进行分割
  6. 连物联网数据都理不清楚还怎么搞大数据分析?
  7. ACM数论 裴蜀定理(贝祖定理)
  8. 3D角色遮挡住UI的实现,关于Stencil Buffer,描边,以及其他
  9. Python Flask框架-开发简单博客-项目布局、应用设置
  10. HDU_多重背包系列