from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
您好,我有以下评论列表:
comments = ['I am very agry','this is not interesting','I am very happy']
这些是相应的标签:
sents = ['angry','indiferent','happy']
我使用tfidf来向量化这些评论,如下所示:
tfidf_vectorizer = TfidfVectorizer(analyzer='word')tfidf = tfidf_vectorizer.fit_transform(comments)from sklearn import preprocessing
我使用标签编码器来向量化标签:
le = preprocessing.LabelEncoder()le.fit(sents)labels = le.transform(sents)print(labels.shape)from sklearn.linear_model import PassiveAggressiveClassifierfrom sklearn.model_selection import train_test_splitwith open('tfidf.pickle','wb') as idxf: pickle.dump(tfidf, idxf, pickle.HIGHEST_PROTOCOL)with open('tfidf_vectorizer.pickle','wb') as idxf: pickle.dump(tfidf_vectorizer, idxf, pickle.HIGHEST_PROTOCOL)
在这里,我使用被动攻击算法来拟合模型:
clf2 = PassiveAggressiveClassifier()with open('passive.pickle','wb') as idxf: pickle.dump(clf2, idxf, pickle.HIGHEST_PROTOCOL)with open('passive.pickle', 'rb') as infile: clf2 = pickle.load(infile)with open('tfidf_vectorizer.pickle', 'rb') as infile: tfidf_vectorizer = pickle.load(infile)with open('tfidf.pickle', 'rb') as infile: tfidf = pickle.load(infile)
在这里,我尝试测试部分拟合的使用,如下所示,使用三个新的评论及其相应的标签:
new_comments = ['I love the life','I hate you','this is not important']new_labels = [1,0,2]vec_new_comments = tfidf_vectorizer.transform(new_comments)print(clf2.predict(vec_new_comments))clf2.partial_fit(vec_new_comments, new_labels)
问题是我在部分拟合后没有得到正确的结果,如下所示:
print('AFTER THIS UPDATE THE RESULT SHOULD BE 1,0,2??')print(clf2.predict(vec_new_comments))
然而,我得到的输出是:
[2 2 2]
因此,我非常希望得到支持来找出为什么模型在使用相同的例子进行测试时没有更新,如果它已经用于训练所需的输出应该是:
[1,0,2]
我希望得到支持来调整超参数,以看到所需的输出。
这是完整的代码,以展示部分拟合:
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizerimport sysfrom sklearn.metrics.pairwise import cosine_similarityimport randomcomments = ['I am very agry','this is not interesting','I am very happy']sents = ['angry','indiferent','happy']tfidf_vectorizer = TfidfVectorizer(analyzer='word')tfidf = tfidf_vectorizer.fit_transform(comments)#print(tfidf.shape)from sklearn import preprocessingle = preprocessing.LabelEncoder()le.fit(sents)labels = le.transform(sents)from sklearn.linear_model import PassiveAggressiveClassifierfrom sklearn.model_selection import train_test_splitwith open('tfidf.pickle','wb') as idxf: pickle.dump(tfidf, idxf, pickle.HIGHEST_PROTOCOL)with open('tfidf_vectorizer.pickle','wb') as idxf: pickle.dump(tfidf_vectorizer, idxf, pickle.HIGHEST_PROTOCOL)clf2 = PassiveAggressiveClassifier()clf2.fit(tfidf, labels)with open('passive.pickle','wb') as idxf: pickle.dump(clf2, idxf, pickle.HIGHEST_PROTOCOL)with open('passive.pickle', 'rb') as infile: clf2 = pickle.load(infile)with open('tfidf_vectorizer.pickle', 'rb') as infile: tfidf_vectorizer = pickle.load(infile)with open('tfidf.pickle', 'rb') as infile: tfidf = pickle.load(infile)new_comments = ['I love the life','I hate you','this is not important']new_labels = [1,0,2]vec_new_comments = tfidf_vectorizer.transform(new_comments)clf2.partial_fit(vec_new_comments, new_labels)print('AFTER THIS UPDATE THE RESULT SHOULD BE 1,0,2??')print(clf2.predict(vec_new_comments))
然而,我得到的是:
AFTER THIS UPDATE THE RESULT SHOULD BE 1,0,2??[2 2 2]
回答:
你的代码中有多个问题。我将从明显的问题开始,逐步到更复杂的问题:
- 你在
clf2
学习任何东西之前就对其进行了序列化(即,你在它被定义后立即序列化,它没有任何用处)。如果只是测试,那还可以。否则,它们应该在fit()
或等效调用之后进行序列化。 -
你在调用
clf2.partial_fit()
之前调用了clf2.fit()
。这完全违背了partial_fit()
的目的。当你调用fit()
时,你实际上是固定了模型将要学习的类(标签)。在你的情况下这是可以接受的,因为在你后续调用partial_fit()
时,你给出了相同的标签。但这仍然不是一个好做法。在
partial_fit()
场景中,永远不要调用fit()
。始终使用你的起始数据和新数据调用partial_fit()
。但请确保在第一次调用partial_fit()
时,在参数classes
中提供你希望模型学习的所有标签。 -
现在关于你的
tfidf_vectorizer
的最后部分。你对tfidf_vectorizer
使用fit_transform()
(这实际上是fit()
和transform()
的组合)来处理comments
数组。这意味着在后续对transform()
的调用中(如你在transform(new_comments)
中所做的那样),它不会从new_comments
中学习新词,而只会使用在调用fit()
时看到的词(即comments
中存在的词)。LabelEncoder
和sents
也是如此。这在在线学习场景中再次不是首选。你应该一次性拟合所有可用数据。但由于你试图使用
partial_fit()
,我们假设你有一个非常大的数据集,可能无法一次性装入内存。因此,你可能希望对TfidfVectorizer
也应用某种形式的partial_fit
。但TfidfVectorizer
不支持partial_fit()
。事实上,它不是为大数据设计的。因此,你需要改变你的方法。查看以下问题以获取更多详情:
抛开一切不谈,如果你只是更改tfidf部分来一次性拟合所有数据(comments
和new_comments
),你将得到你想要的结果。
查看下面的代码更改(我可能对其进行了一些组织并将vec_new_comments
重命名为new_tfidf
,请仔细阅读):
comments = ['I am very agry','this is not interesting','I am very happy']sents = ['angry','indiferent','happy']new_comments = ['I love the life','I hate you','this is not important']new_sents = ['happy','angry','indiferent']tfidf_vectorizer = TfidfVectorizer(analyzer='word')le = preprocessing.LabelEncoder()# 以下几行很重要# 我已经将所有数据提供给tfidf_vectorizer来拟合tfidf_vectorizer.fit(comments + new_comments)# 对于`sents`也是如此,但由于标签没有变化,所以使用哪个都无所谓,因为结果是一样的# le.fit(sents)le.fit(sents + new_sents)
以下是非首选代码(你正在使用的代码,我在第2点中提到的),但只要你进行上述更改,结果会很好。
tfidf = tfidf_vectorizer.transform(comments)labels = le.transform(sents)clf2.fit(tfidf, labels)print(clf2.predict(tfidf))# [0 2 1]new_tfidf = tfidf_vectorizer.transform(new_comments)new_labels = le.transform(new_sents)clf2.partial_fit(new_tfidf, new_labels)print(clf2.predict(new_tfidf))# [1 0 2] 正如你想要的
正确的方法,或者partial_fit()
的预期使用方式:
# 声明你希望模型学习的所有标签# 使用labelEncoder学习的类来做这件事# 在任何对`partial_fit()`的调用中,所有标签都应来自此数组all_classes = le.transform(le.classes_)# 注意这里的参数classes# 它需要在第一次出现clf2.partial_fit(tfidf, labels, classes=all_classes)print(clf2.predict(tfidf))# [0 2 1]# 这里没有classesclf2.partial_fit(new_tfidf, new_labels)print(clf2.predict(new_tfidf))# [1 0 2]