简而言之:我编写了一个自定义模型,运行良好,但尝试在scikit-learn中重新实现时,效果却很差。我不确定我的代码(如下)是否有Bug,是否缺少了scikit-learn项目推荐的预处理步骤,还是我的模型选择有误。
项目的目标是:根据博客文章的标题,预测文章所写的具体产品。总共有大约2000种产品。
首先,我构建了一个自定义模型,使用了一些从教科书中采用的“语言模型”原则。
我遍历了标记数据,对于每个产品,我统计了其所有标题中使用的所有单词(例如,car:10,windshield:3,husband:2,tires:5)。然后,为了对未见过的标题进行新的预测,我对标题进行分词,并为每个产品构建一个分数:分数=(产品统计中单词1的百分比频率) * (产品统计中单词2的百分比频率) 等等。对于产品中缺失的单词,我设置了一个默认的低百分比频率(而不是使用0,因为这会干扰排名)。
然后,我按分数对产品进行排序,并返回前5名。我发现87%的情况下,正确的产品都在这些前5名中。
现在,我尝试使用scikit-learn中的朴素贝叶斯来实现它。目前作为评估指标,我使用的是默认的score()方法,这个方法更为严格,因为模型的顶级(唯一)预测必须是正确的。但我得到的准确率是44%,这让我感到惊讶。
- 值得注意的是,在训练数据上的评分时,我也得到了44%的准确率;我认为这应该更高,因为模型已经见过这些数据。
- 使用线性回归时,我也得到了低分:在已见的训练数据上评分为88%,但在未见的测试数据上为47%。
我的代码:
titles = []products = []with open('1pct_singlelabel.csv', 'r', encoding="utf8") as one_pct: reader = csv.reader(one_pct, delimiter=',', quotechar='"', lineterminator='\n') for i, row in enumerate(reader): if (i == 0): continue # skip header titles.append(row[2]) products.append(row[1])text_train, text_test, y_train, y_test = train_test_split(titles, products, random_state=0)vect = CountVectorizer(min_df=0)vect.fit(titles)X_train = vect.transform(text_train)X_test = vect.transform(text_test)le = preprocessing.LabelEncoder()le.fit(products)y_train = le.transform(y_train)y_test = le.transform(y_test)clf = LogisticRegression()clf.fit(X_train, y_train)print("Logistic Regression: ")print(clf.score(X_train, y_train))print(clf.score(X_test, y_test))
总之,我不明白为什么scikit-learn的表现比我的自定义代码差很多,我的自定义代码并不复杂,并且(据我所知)与朴素贝叶斯相似。我不确定我是否正确使用了scikit-learn。
回答:
我认为您误解了MultinomialNB
的score值。Sklearn没有提供内置的方法来评估top-k准确率。您看到的score结果是模型完全预测正确的样本百分比。我猜想,如果您计算scikit-learn中朴素贝叶斯模型的前5名,它将与您的自定义模型非常相似。
您可以使用predict_proba
方法获取分配给每个类的概率。从那里,您可以对输出进行排序,并获取前k个值,然后检查实际输出是否在其中。
predictions = clf.predict_proba(test)k = 5top_k = np.argsort(probs, axis=1)[-k:]top_k_preds = clf.classes_[top_k]
然后,您可以检查您的实际类是否在前5个预测中。