在scikit-learn中,TfidfVectorizer
允许我们适应训练数据,然后使用相同的矢量化器来转换我们的测试数据。列车数据的变换输出是表示给定文档的每个单词的tf-idf分数的矩阵。
但是,拟合矢量化器如何计算新输入的分数?我猜到了:
我试过从scikit-learn的源代码code推断出这个操作,但还是没能弄明白。它是我之前提到的选项之一还是完全不同的选择?请协助。
它绝对是前者:每个单词的idf
(逆文档频率)仅基于训练文档计算。这是有道理的,因为这些值正是在向量化器上调用fit
时计算的值。如果您描述的第二个选项是真的,我们基本上每次都会重新设置一个矢量化器,并且我们也会导致information leak
,因为测试集中的idf将在模型评估期间使用。
除了这些纯粹的概念性解释,您还可以运行以下代码来说服自己:
from sklearn.feature_extraction.text import TfidfVectorizer
vect = TfidfVectorizer()
x_train = ["We love apples", "We really love bananas"]
vect.fit(x_train)
print(vect.get_feature_names())
>>> ['apples', 'bananas', 'love', 'really', 'we']
x_test = ["We really love pears"]
vectorized = vect.transform(x_test)
print(vectorized.toarray())
>>> array([[0. , 0. , 0.50154891, 0.70490949, 0.50154891]])
根据拟合方法的工作原理,您可以自己重新计算这些tfidf值:
“苹果”和“香蕉”的tfidf得分显然为0,因为它们没有出现在x_test
中。另一方面,“梨”在x_train
中不存在,因此甚至不会出现在矢量化中。因此,只有“爱”,“真的”和“我们”才会有tfidf得分。
Scikit-learn将tfidf实现为log((1 + n)/(1 + df)+ 1)* f其中n是训练集中的文档数(2为我们),df是单词中的文档数仅出现在训练集中,f表示测试集中单词的频率计数。因此:
tfidf_love = (np.log((1+2)/(1+2))+1)*1
tfidf_really = (np.log((1+2)/(1+1))+1)*1
tfidf_we = (np.log((1+2)/(1+2))+1)*1
然后,您需要根据文档的L2距离缩放这些tfidf分数:
tfidf_non_scaled = np.array([tfidf_love,tfidf_really,tfidf_we])
tfidf_list = tfidf_non_scaled/sum(tfidf_non_scaled**2)**0.5
print(tfidf_list)
>>> [0.50154891 0.70490949 0.50154891]
你可以看到,确实,我们得到了相同的值,这证实了scikit-learn
实现这种方法的方式。