我是向量空间模型(VSM)的初学者。我尝试了这个网站上的代码。这是一个非常好的VSM介绍,但不知为何我的结果与作者的不同。这可能是因为scikit learn自从介绍编写以来已经发生了很多变化,可能是兼容性问题。也可能是我误解了解释。
我使用下面的代码得到了错误的答案。有人能找出代码的问题吗?我在下面发布了代码的结果和正确答案
我已经手动计算过,所以我知道网站的结果是正确的。还有另一个Stackoverflow问题使用了相同的代码,但结果与网站也不相同。
import numpy, scipy, sklearntrain_set = ("The sky is blue.","The sun is bright.")test_set = ("The sun is the sky is bright.", "We can see the shining sun, the bright sun.")from sklearn.feature_extraction.text import CountVectorizervectorizer = CountVectorizer(stop_words= 'english')vectorizer.fit_transform(train_set)smatrix = vectorizer.transform(test_set)from sklearn.feature_extraction.text import TfidfTransformertfidf = TfidfTransformer(norm='l2', sublinear_tf=True)tfidf.fit(smatrix)#print smatrix.todense()print tfidf.idf_tf_idf_matrix = tfidf.transform(smatrix)print tf_idf_matrix.todense()
tf-idf结果向量
#[ 2.09861229 1. 1.40546511 1. ]
tf-idf正确向量
#[0.69314718, -0.40546511, -0.40546511, 0]
tf_idf_matrix结果
#[[ 0. 0.50154891 0.70490949 0.50154891]
#[ 0. 0.50854232 0. 0.861037 ]]
正确答案
# [[ 0. -0.70710678 -0.70710678 0. ]
# [ 0. -0.89442719 -0.4472136 0. ]]
回答:
这不是你的错,这是因为当前sklearn
使用的公式与教程中使用的公式不同。
当前版本的sklearn
使用以下公式(源码):
idf = log ( n_samples / df ) + 1
其中n_samples
指的是文档总数(教程中的|D|
),df
指的是包含该词的文档数量(教程中的{d:t_1 \in D}
)。
为了处理零除问题,他们默认使用平滑处理(在TfidfVectorizer
中使用smooth_idf=True
选项,参见文档),这会改变df
和n_samples
的值,使这些值至少为1:
df += 1n_samples += 1
而教程中使用的公式是:
idf = log ( n_samples / (1+df) )
所以,除非你更改源代码中的公式,否则你无法得到与教程中完全相同的结果。
编辑:
严格来说,正确的公式是log(n_samples/df)
,但由于在实践中会导致零除问题,人们尝试修改公式以便在所有情况下都能使用。最常见的一种是如你所说:log(n_samples/(1+df))
,但使用log(n_samples/df)+1
的公式也不算错,前提是你已经提前进行了平滑处理。但阅读代码历史,似乎他们这样做是为了避免负的IDF值(如这个拉取请求中讨论的,并在这个修复中更新)。另一种消除负IDF值的方法是简单地将负值转换为0。我还没有找到哪种方法更常用。
他们确实同意他们这样做不是标准方法。所以你可以安全地说log(n_samples/(1+df))
是正确的方法。
要编辑公式,首先我必须警告你,这将影响使用该代码的每个用户,请确保你知道自己在做什么。
你可以直接进入源代码(在Unix系统中:位于/usr/local/lib/python2.7/dist-packages/sklearn/feature_extraction/text.py
,在Windows系统中:我现在不使用Windows,但你可以搜索“text.py”文件)并直接编辑公式。你可能需要管理员/根访问权限,具体取决于你使用的平台。
附加说明:
作为附加说明,词汇表中术语的顺序也不同(至少在我的机器上),所以要得到完全相同的结果(如果公式相同),你还需要传入与教程中显示的完全相同的词汇表。所以使用你的代码:
vocabulary = {'blue':0, 'sun':1, 'bright':2, 'sky':3}vectorizer = CountVectorizer(vocabulary=vocabulary) # 如果使用词汇表,你不需要stop_wordsvectorizer.fit_transform(train_set)print 'Vocabulary:', vectorizer.vocabulary_# Vocabulary: {'blue': 0, 'sun': 1, 'bright': 2, 'sky': 3}