TF-IDF是NLP中常用的方法,也比较经典,基本思想是:如果一个词在文档中出现了很多次,但是这个词在其它文档中出现的次数很少,则这个词对这篇文档很重要。在一定程度上这个词可以表达这篇文档的关键信息,所以在网页搜索、关键词提取中常用到TF-IDF。
TF-IDF就是tf−idf(t,d)=tf(t,d)×idf(t)tf-idf(t,d)=tf(t,d) \times idf(t)tf−idf(t,d)=tf(t,d)×idf(t),公式中t代表词term,d代表文档document。其实就是TF与IDF相乘。
TF是词频term frequency ,idf是逆文档频率inverse document-frequency。频率与频数不同,这里的词频是指词的频数,也就是一个词在文档中出现的个数,比如“的”在一个文档中出现了128次数,则tf(t,d)=128tf(t,d)=128tf(t,d)=128,这个比较好理解。
逆文档频率:为什么要有IDF呢?如果只用TF会有什么问题呢?比如“的”在一个文档中出现128次,是整个文档中出现次数最多的词。但是这个词并没有什么意义。所以要求有意义的词在这篇文档中出现的次数多,但是在其它的文档中出现的少。我们理解的频率的概念是频数除以总数。“逆”就是倒过来的意思,为了防止分母为0,在分母上了个1,再做log处理。所以:
idf(t)=logn1+df(t)idf(t)=log\frac{n}{1+df(t)}idf(t)=log1+df(t)n
公式中nnn代表文档的总数,df(t)df(t)df(t)代表包含单词t的文档的个数。对于这个公式sklearn做了优化。
在sklearn中 TfidfTransformer
和 TfidfVectorizer
设置参数 smooth_idf=False
时,IDF的计算公式,如下:idf(t)=logndf(t)+1idf(t)=log\frac{n}{df(t)}+1idf(t)=logdf(t)n+1
把分子中的1拿到了外边。如果smooth_idf=True
时分子和分母同时加了1,做平滑处理。如下:
idf(t)=log1+n1+df(t)+1idf(t) = log\frac{1+n}{1+df(t)}+1idf(t)=log1+df(t)1+n+1
然后再l2l_2l2正则化:
vnorm=vv12+v22+...+vn2v_{norm}=\frac{v}{\sqrt{v_1^2+v_2^2+...+v_n^2}}vnorm=v12+v22+...+vn2v
这个向量其实是每个词的权重(weight),一开始是用于信息检索(information retrieval),后来发现这个词向量在文本分类与文本聚类中也很有效果。或许会问,为什么要做正则化?有没有发现正则化后,两个向量的点乘就是这两个向量的余弦相似度了。
举个例子:
>>> from sklearn.feature_extraction.text import TfidfTransformer
>>> transformer = TfidfTransformer(smooth_idf=False)
>>> transformer
# 未做smooth处理
TfidfTransformer(smooth_idf=False)
# 有6个文档,每个文档有三个特征词,一行是一个文档。
>>> counts = [[3, 0, 1],
... [2, 0, 0],
... [3, 0, 0],
... [4, 0, 0],
... [3, 2, 0],
... [3, 0, 2]]
...
# 使用sklearn的
>>> tfidf = transformer.fit_transform(counts)
>>> tfidf
<6x3 sparse matrix of type &#39;<... &#39;numpy.float64&#39;>&#39; with 9 stored elements in Compressed Sparse ... format>>>> tfidf.toarray()
array([[0.81940995, 0. , 0.57320793],[1. , 0. , 0. ],[1. , 0. , 0. ],[1. , 0. , 0. ],[0.47330339, 0.88089948, 0. ],# 做smooth处理的
>>> transformer &#61; TfidfTransformer()
>>> transformer.fit_transform(counts).toarray()
array([[0.85151335, 0. , 0.52433293],[1. , 0. , 0. ],[1. , 0. , 0. ],[1. , 0. , 0. ],[0.55422893, 0.83236428, 0. ],[0.63035731, 0. , 0.77630514]])
TfidfVectorizer
&#xff1a;
>>> from sklearn.feature_extraction.text import TfidfVectorizer
>>> vectorizer &#61; TfidfVectorizer()
# 直接把语料转换为if-idf的向量
>>> vectorizer.fit_transform(corpus)
<4x9 sparse matrix of type &#39;<... &#39;numpy.float64&#39;>&#39;with 19 stored elements in Compressed Sparse ... format>
这个例子中6篇文档3个特征的tf-idf的计算过程&#xff1a;
一共有6个文档&#xff0c;n&#61;6&#xff0c;特征词有三个&#xff0c;对于term1在所有的文档中都出现过&#xff0c;所以&#xff0c;df(t)term1&#61;6df(t)_{term1}&#61;6df(t)term1&#61;6&#xff0c;
对于doc1的第一个term的tf &#61; 3&#xff0c;所以tf−idfterm1&#61;tf∗idf(t)term1&#61;3×(log66&#43;1)&#61;3tf-idf_{term1}&#61; tf * idf(t)_{term1} &#61; 3 \times (log\frac{6}{6}&#43;1)&#61;3tf−idfterm1&#61;tf∗idf(t)term1&#61;3×(log66&#43;1)&#61;3
对于doc2的第二个term的tf &#61; 0&#xff0c;所以tf−idfterm1&#61;tf∗idf(t)term1&#61;0×(log61&#43;1)&#61;0tf-idf_{term1}&#61; tf * idf(t)_{term1} &#61; 0 \times (log\frac{6}{1}&#43;1)&#61;0tf−idfterm1&#61;tf∗idf(t)term1&#61;0×(log16&#43;1)&#61;0
对于doc1的第三个term的tf &#61; 1&#xff0c;所以tf−idfterm1&#61;tf∗idf(t)term1&#61;1×(log62&#43;1)&#61;2.0986tf-idf_{term1}&#61; tf * idf(t)_{term1} &#61; 1 \times (log\frac{6}{2}&#43;1)&#61;2.0986tf−idfterm1&#61;tf∗idf(t)term1&#61;1×(log26&#43;1)&#61;2.0986
原始的tf-idf向量&#xff1a;[3, 0, 2.0986]
正则化&#xff1a;
[3,0,2.0986]32&#43;02&#43;2.09862&#61;[0.819,0,0.573]\frac{[3,0,2.0986]}{\sqrt{3^2&#43;0^2&#43;2.0986^2}}&#61;[0.819,0,0.573]32&#43;02&#43;2.09862[3,0,2.0986]&#61;[0.819,0,0.573]
在sklearn中TfidfVecotrizer
是CountVectorizer
与TfidfTransformer
的结合&#xff0c;可以直接把文本的语料转化为以词为特征的向量。其实这个向量就是一个文本转化为向量后的结果&#xff0c;如果不限定的话&#xff0c;是所有文本组成的词典的长度&#xff0c;但是可以根据max_df
和min_df
来选择&#xff0c;超过max_df
或低于min_df
的词是不被选入词典的。max_df
的默认值是1.0&#xff0c;也就是最高文档频率是没有限制的&#xff0c;min_df
的默认值是1&#xff0c;也就是说单词要在所有的文档中有出现。特征是所有的转化为后向量可以用于文本分类或文本聚类。
使用的是sklearn中自带的文本样本20newsgroups数据集&#xff0c;一共有11314篇文档&#xff0c;20个类别。对样本向量化&#xff0c;再使用LightGBM对样本进行分类。对比了sklearn中两种tf-idf向量化的方式&#xff0c;向量化的结果是一样的。但是向量化后&#xff0c;数据比较稀疏&#xff0c;训练有点慢。不过仅仅使用这么稀疏的数据&#xff0c;而且使用的lgb的baseline模型&#xff0c;效果还是不错的了&#xff0c;最好的类别f1 score能到94%。
max_df
和min_df
来限制样本的tf-idf向量的维度&#xff1b;from jieba import analyse
sentence &#61; "人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;英文缩写为AI。它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。人工智能是计算机科学的一个分支&#xff0c;它企图了解智能的实质&#xff0c;并生产出一种新的能以人类智能相似的方式做出反应的智能机器&#xff0c;该领域的研究包括机器人、语言识别、图像识别、自然语言处理和专家系统等。人工智能从诞生以来&#xff0c;理论和技术日益成熟&#xff0c;应用领域也不断扩大&#xff0c;可以设想&#xff0c;未来人工智能带来的科技产品&#xff0c;将会是人类智慧的“容器”。人工智能可以对人的意识、思维的信息过程的模拟。人工智能不是人的智能&#xff0c;但能像人那样思考、也可能超过人的智能。人工智能是一门极富挑战性的科学&#xff0c;从事这项工作的人必须懂得计算机知识&#xff0c;心理学和哲学。人工智能是包括十分广泛的科学&#xff0c;它由不同的领域组成&#xff0c;如机器学习&#xff0c;计算机视觉等等&#xff0c;总的说来&#xff0c;人工智能研究的一个主要目标是使机器能够胜任一些通常需要人类智能才能完成的复杂工作。但不同的时代、不同的人对这种“复杂工作”的理解是不同的。2017年12月&#xff0c;人工智能入选“2017年度中国媒体十大流行语“。"
keywords &#61; analyse.extract_tags(sentence, topK&#61;10, withWeight&#61;False, allowPOS&#61;(), withFlag&#61;False)
print(keywords)
# [&#39;人工智能&#39;, &#39;智能&#39;, &#39;2017&#39;, &#39;机器&#39;, &#39;不同&#39;, &#39;人类&#39;, &#39;科学&#39;, &#39;模拟&#39;, &#39;一门&#39;, &#39;技术&#39;]
输入是一段字符串&#xff0c;输出关键词的list。
其中的参数&#xff1a;
使用起来确实简单&#xff0c;直接输入一段话就可以提取其中的关键词了。但是有一个问题是&#xff0c;idf的计算是需要看出现这个词的文档的个数&#xff0c;及总的文档数量的&#xff0c;jieba难道不需要么&#xff1f;
jieba中有一个自带的idf.txt&#xff0c;第一列单词&#xff0c;第二列是idf。这个idf是通过离线训练好的。
jieba的tf-idf计算比较简单&#xff0c;源码如下&#xff1a;
有三步&#xff1a;
self.idf_freq
。如果单词不在self.freq
中时&#xff0c;返回的是平均的idf值self.median_idf
。然后用第二步的tf值与得到idf相乘。至于为什么要除以一个total
&#xff0c;我觉得是为了把tf-idf值变的小一些&#xff0c;比较容易比较大小&#xff0c;毕竟所有的tf-idf都除以一个正数对大小的比较没有影响。参考&#xff1a;
声明&#xff1a;原创文章&#xff0c;转载请注明出处。