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



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

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

如果可以有效地将新文档添加到矩阵中,那么如果保存该计算的结果,初始计算是否花费十小时甚至几天都无关紧要。

当我在. . 矢量
  1. 化器上似乎有一个名为vectorizer.fixed_vocabulary_的方法。我在谷歌或SKlearn上找不到这种方法。无论如何,当我运行该方法时,它会返回False.有谁知道这是什么?我认为如果可能的话,修复词汇可能会很有用,否则将新文档添加到术语文档矩阵中可能会很麻烦,尽管我不确定如何做到这一点。

有人在这里问了一个类似的问题,这个问题被投票通过,但没有人回答。

他写道:

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

  1. 有没有人确切地理解他在这里的意思,或者有任何很好的链接来解释这个相当晦涩的话题?他说的对吗?我不知何故认为,如果他是对的,那么使用这个内部产品添加新文档的能力将取决于修复上述词汇。

作为我对另一个答案的评论的续集:是的,缺少词汇会导致问题,至少对我来说是这样。问题是在计算 tf-idf 值(或其他)时,不考虑不在词汇表中的单词。为了想象,当我们有一个句子"这是karamba",而词汇表中只有前两个词,那么他们得到的分数要高得多,beause"karamba"是一个未知的词,根本没有分数。因此,"this"和"is"在句子中比"karamba"在词汇表中要重要得多(请记住,karamba是我们真正想在这个句子中寻找的唯一单词)。

好吧,那么如果"karamba"根本不在语料库中,为什么会有问题呢?因为我们得到很多误报,因为"这个"和"是"真的很重要,即使它们有点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_tfidf_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_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])

2020 年的注意事项:如果您处于与 2018 年相比的相对未来并且您拥有更新版本的 scipy,则可能不需要此部分。

哦,凯,现在我们必须合并语料库矩阵。这是有问题的 因为矩阵的大小不再相同。我们必须调整大小 语料库矩阵,因为现在我们(可能)有更多的单词在那里和 我们不能在不使它们具有相同大小的情况下合并它们。 滑稽和 可悲的是,scipy.sparse支持调整大小 矩阵,但已发布的矩阵不支持调整 CSR 矩阵的大小scipy的版本 .因此,我安装了scipymaster分支 来自任意提交:pip install git+git://github.com/scipy/scipy.git@b8bf38c555223cca0bcc1e0407587c74ff4b3f2e#egg=scipy. 嗡!您需要安装cython才能在自己的机器中构建scipy(只是pip install cython)。

- 2018年的我

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

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])

砰,完成了。另一个答案仍然是正确的,我只是在详细说明该解决方案。

好吧,我解决了它,花了很多小时,围绕这个主题的另一篇文章让我对它描述线性代数的方式感到困惑,并且没有提到它的一个方面,这对写它的人来说可能是显而易见的。

因此,感谢您提供有关词汇的信息。

所以矢量化器是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

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

我们将拟合,然后将新消息添加到训练的模型中

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

最新更新