Python 使用scikit学习计算文本相似度时出错
我是向量空间模型(VSM)的初学者。我试过了我的密码 . 这是对VSM的一个很好的介绍,但我不知怎的从作者那里得到了不同的结果。这可能是因为一些兼容性问题,因为自介绍撰写以来似乎已经发生了很大的变化。也可能是我误解了解释。Python 使用scikit学习计算文本相似度时出错,python,machine-learning,nltk,cosine-similarity,Python,Machine Learning,Nltk,Cosine Similarity,我是向量空间模型(VSM)的初学者。我试过了我的密码 . 这是对VSM的一个很好的介绍,但我不知怎的从作者那里得到了不同的结果。这可能是因为一些兼容性问题,因为自介绍撰写以来似乎已经发生了很大的变化。也可能是我误解了解释。 我用下面的代码得到了错误的答案。有人能找出它的毛病吗?我把下面代码的结果和正确答案贴在下面 我已经做了手工计算,所以我知道网站的结果是好的。 还有一个网站使用相同的代码,但也没有得到与网站相同的结果。 import numpy, scipy, sklearn train_s
我用下面的代码得到了错误的答案。有人能找出它的毛病吗?我把下面代码的结果和正确答案贴在下面 我已经做了手工计算,所以我知道网站的结果是好的。 还有一个网站使用相同的代码,但也没有得到与网站相同的结果。
import numpy, scipy, sklearn
train_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 CountVectorizer
vectorizer = CountVectorizer(stop_words= 'english')
vectorizer.fit_transform(train_set)
smatrix = vectorizer.transform(test_set)
from sklearn.feature_extraction.text import TfidfTransformer
tfidf = 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\u idf\u矩阵
#[[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
使用以下公式():
其中,n|u samples
指文档总数(|D |
在教程中),而df
指出现术语的文档数量({D:t|u 1\在D}
在教程中)
为了处理零除法,默认情况下,它们使用平滑(在TfidfVectorizer
中的选项smooth\u idf=True
,请参见)来更改df
和n\u采样值,这样这些值至少为1:
df += 1
n_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\u extraction/text.py),在Windows中:我现在不使用Windows,但您可以搜索文件“text.py”)并直接编辑公式。您可能需要管理员/root访问权限,具体取决于您使用的平台
补充说明:
另外需要注意的是,词汇表中术语的顺序也不同(至少在我的机器中是这样),因此要得到完全相同的结果(如果公式相同),还需要传入完全相同的词汇表,如教程所示。因此,使用您的代码:
vocabulary = {'blue':0, 'sun':1, 'bright':2, 'sky':3}
vectorizer = CountVectorizer(vocabulary=vocabulary) # You don't need stop_words if you use vocabulary
vectorizer.fit_transform(train_set)
print 'Vocabulary:', vectorizer.vocabulary_
# Vocabulary: {'blue': 0, 'sun': 1, 'bright': 2, 'sky': 3}
打印语句的输出是什么?我发布了打印语句谢谢。似乎公式是正确的,代码不是正确的。不是吗?我是这个领域的初学者。如果函数不正确,我如何编辑它?但我认为正确的公式应该是idf=log(n_samples/(1+df))。你能确认一下吗?代码中的公式可能不是“正确的”,但在实践中,它可以很好地提取机器学习和余弦相似性的良好特征@DJJ您确定要更改scikit learn中的代码吗?难道它不会产生与博客文章相同的相似性排名吗?我们不会在Stackoverflow上发表评论来表示感谢。但你真的让我开心。我的主要观点是TF-IDF从根本上说只是一种对文本特征进行加权的黑客,使机器学习和余弦sim查询在这些特征上运行良好。没有“普遍最佳”的TF-IDF方法。TF-IDF的“正确”变体是通过特定机器学习任务(如多类分类)的特定度量(如F1分数)来衡量的最有效的变体。
vocabulary = {'blue':0, 'sun':1, 'bright':2, 'sky':3}
vectorizer = CountVectorizer(vocabulary=vocabulary) # You don't need stop_words if you use vocabulary
vectorizer.fit_transform(train_set)
print 'Vocabulary:', vectorizer.vocabulary_
# Vocabulary: {'blue': 0, 'sun': 1, 'bright': 2, 'sky': 3}