将新文档添加到术语文档矩阵以进行相似度计算

时间:2017-06-09 15:47:15

标签: scikit-learn nlp cosine-similarity spacy term-document-matrix

所以我知道有几种方法可以在文档语料库中找到最相似或最三个类似的文档。我知道可能存在扩展问题,现在我有大约一万个文档,并且已经在大约三十个子集上运行测试。这就是我现在所得到的,但正在考虑研究弹性搜索或doc2vec,如果这被证明是不可能或效率低的。

到目前为止,脚本运行得非常好,它们使用spaCy来标记文本,并使用Sklearn TfidfVectorizer来适应所有文档,并找到非常相似的文档。我注意到我的NumPy对象的形状出现在管道中(33,104354),这可能暗示了104354个词汇,不包括所有33个文档中的停用词。这一步需要20分钟才能运行,但下一步是计算所有余弦相似度的矩阵乘法非常快,但我知道它可能会因为矩阵变成数千而不是30行而变慢。

如果您可以有效地将新文档添加到矩阵中,如果保存该计算的结果,初始计算需要十小时甚至几天,这并不重要。

  1. 当我按下标签后。似乎在矢量化器上有一个名为vectorizer.fixed_vocabulary_的方法。我无法在Google或SKlearn上找到此方法。无论如何,当我运行该方法时,它返回False。有谁知道这是什么?我认为如果可能的话,修复词汇表可能会有用,否则将新文档添加到术语文档矩阵可能会很麻烦,尽管我不知道该怎么做。
  2. 有人问similar question这里投了票,但没有人回答过。

    他写道:

      

    对于新文件   当我得到一份新文件doc(k)时该怎么办?好吧,我必须计算这个文档与之前所有文档的相似性,这些文档并不需要构建整个矩阵。我可以把doc(k)dot doc(j)的内积用于所有以前的j,结果是S(k,j),这很棒。

    1. 有没有人完全理解他在这里的含义,或者在解释这个相当模糊的主题时有什么好的联系?他是对的吗?我不知何故认为,如果他是正确的话,使用这种内部产品添加新文档的能力将取决于如上所述修复词汇表。

3 个答案:

答案 0 :(得分:1)

好吧,我解决了它,花了好几个小时,围绕这个主题的另一个帖子让我对它描述线性代数的方式感到困惑,并没有提到它的一个方面,这对于编写它的人来说可能是显而易见的。 / p>

非常感谢有关词汇的信息..

因此,矢量化器是sklearn.feature_extraction.text.vectorizer的一个实例。我使用vocabulary_方法来提取现有33种文本的词汇:

v = vectorizer.vocabulary_
print (type(v))
>> dict
print (len(v))
>> 104354

将这个字典腌制以备将来使用,只是为了测试它是否有效,在包含TfidfVectorizer的管道对象上重新fit_transform,其参数为vocabulary=v

通过找到原始的成对相似性矩阵 pairwise_similarity = (p * p.T).A其中p是拟合的管道对象,也是术语文档矩阵。

添加了一个小型新文档:

new_document= """

Remove the lamb from the fridge 1 hour before you want to cook it, to let it come up to room temperature. Preheat the oven to 200ºC/400ºC/gas 6 and place a roasting dish for the potatoes on the bottom. Break the garlic bulb up into cloves, then peel 3, leaving the rest whole.
"""

将管道安装到一个文档中,其中包含现在固定的词汇表:

p_new = pipe.fit_transform([new_document]) 
print (p_new.shape)
> (1, 104354)

然后把它们放在一起:

from scipy.sparse import vstack as vstack_sparse_matrices
p_combined = vstack_sparse_matrices([p, p_new])
print (p_combined.shape)
>> (34, 104354)

并重新配对成对相似度方程式:

pairwise_similarity = (p_combined * p_combined.T).A

对代码或理论并不完全有信心,但我相信这是正确的并且有效 - 布丁的证据就在吃,我后来的代码发现最相似的文件也与烹饪相关。将原始文档更改为其他几个主题并重新考虑所有主题,这些相似之处完全符合您的预期。

答案 1 :(得分:1)

作为我对其他答案的评论的续集:是的,缺少的词汇会导致问题,至少它对我有用。问题在于,在计算tf-idf值(或其他)时,不考虑词汇表中没有的词。想象一下,当我们有一个句子"这是karamba。"并且只有前两个单词在词汇表中,然后他们得到了更高的分数,因为" karamba"是一个未知的单词,并没有得分。因此"这个"和"是"在句子中比在" karamba"中更重要。在词汇表中(并记住karamba是我们在这句话中实际想要看的唯一词)。

好吧,那么为什么如果" karamba"根本不在语料库中?因为我们在“#34;这个"基础上得到了很多误报。和"是"非常重要,即使他们有点 meh 我是怎么解决的?不方便,但可行。

首先我创建我的语料库'另一个答案中建议的词汇。

import copy
from sklearn.feature_extraction.text import TfidfVectorizer
from collections import defaultdict

corpus = []

# Populate the corpus with some data that I have
for d in sorted(os.listdir('d'), key=lambda x: int(x.split('.')[0])):
    with open(os.path.join('d', d)) as f:
        corpus.append(f.read())

corpus_tfidf_vectorizer = TfidfVectorizer()
corpus_tfidf_matrix = corpus_tfidf_vectorizer.fit_transform(corpus)

corpus_vocabulary = defaultdict(None, copy.deepcopy(corpus_count_vectorizer.vocabulary_))
corpus_vocabulary.default_factory = corpus_vocabulary.__len__

为什么defaultdict?这是我在TfidfVectorizer内部实施词汇创作时偷走的一个巧妙技巧。如果您想查找,请检查sklearn.feature_extraction.text.CountVectorizer._count_vocab。从本质上讲,它只是一种在词汇表中添加单词的方式,而不必过多担心正确的权限。

Anywhoo,现在我们开始查询我们要添加到语料库中的查询。

 # Let's say I got a query value from somewhere
 query = f.read()

 query_vocabulary_vectorizer = TfidfVectorizer()
 query_vocabulary = query_vocabulary_vectorizer.fit_transform([query])
 for word in query_vocabulary_vectorizer.vocabulary_.keys():
     # Added with proper index if not in vocabulary
     corpus_vocabulary[word]

 # Nice, everything in the vocabulary now!

 query_tfidf_matrix = TfidfVectorizer(vocabulary=corpus_vocabulary).fit_transform([query])

哦 - 凯,现在我们必须合并语料库矩阵。这是有问题的,因为矩阵不再具有相同的大小。我们必须调整语料库矩阵的大小,因为现在我们(可能)在那里有更多的单词,我们无法将它们合并而不会使它们具有相同的大小。
有趣的是,scipy.sparse支持调整矩阵大小,但scipy的已发布版本不支持调整CSR矩阵的大小。因此,我从任意提交中安装了master scipy分支:pip install git+git://github.com/scipy/scipy.git@b8bf38c555223cca0bcc1e0407587c74ff4b3f2e#egg=scipy。 PS!您需要安装cython才能在自己的计算机中构建scipy(仅pip install cython)。

所以这很麻烦,但现在我们可以高兴地宣布:

from scipy import sparse as sp

corpus_tfidf_matrix.resize((corpus_tfidf_matrix.shape[0], query_tfidf_matrix.shape[1]))
# And voilà, we can merge now!
tfidf_matrix = sp.vstack([corpus_tfidf_matrix, query_tfidf_matrix])
巴姆,完成了。另一个答案仍然是正确的,我只是详细阐述了解决方案。

答案 2 :(得分:0)

我们将适应,然后将新消息添加到经过训练的模型中

tf_one = TfidfVectorizer(analyzer='word', stop_words = "english", lowercase = True)
X_train_one = tf_one.fit_transform(X_train)
nb_one = MultinomialNB()
nb_one.fit(X_train_one , Y_train)

# When you receive a new document
X = tf_one.transform([mymessage_X])
prediction = nb_one.predict(X)
print(prediction)


 # New document 
mymessage_X ="This message will be added to the existing model"
label_Y=" your label"



 tf_two = TfidfVectorizer(analyzer='word', stop_words = "english", lowercase = True ,vocabulary = tf_one.vocabulary_)

X_train_two = tf_two.fit_transform([mymessage_X])
nb = MultinomialNB()
nb.fit(X_train_two, [label_Y])

#print the length of the tf_two vocabulary
len(tf_two.vocabulary_)    

from scipy.sparse import vstack as vstack_sparse_matrices
p_combined = vstack_sparse_matrices([X_train_one, X_train_two])
print (p_combined.shape) 

pairwise_similarity = (p_combined * p_combined.T).A
pairwise_similarity