#Set `y`
y = df._____
# Drop the `label` column
df.____("label", axis=1)
# Make training and test sets
X_train, X_test, y_train, y_test = ________________(df['text'], y, test_size=0.33, random_state=53)
# Initialize the `count_vectorizer`
count_vectorizer = _______________(stop_words='english')
# Fit and transform the training data
count_train = count_vectorizer._____________(X_train)
# Transform the test set
count_test = count_vectorizer._________(X_test)
# Initialize the `tfidf_vectorizer`
tfidf_vectorizer = _______________(stop_words='english', max_df=0.7)
# Fit and transform the training data
tfidf_train = tfidf_vectorizer._____________(X_train)
# Transform the test set
tfidf_test = tfidf_vectorizer._________(X_test)
# Get the feature names of `tfidf_vectorizer`
print(tfidf_vectorizer.___________()[-10:])
# Get the feature names of `count_vectorizer`
print(count_vectorizer.___________()[:10])
defplot_confusion_matrix(cm, classes, normalize=False, title='Confusion matrix', cmap=plt.cm.Blues):""" See full source and example: http://scikit-learn.org/stable/auto_examples/model_selection/plot_confusion_matrix.html This function prints and plots the confusion matrix. Normalization can be applied by setting `normalize=True`. """ plt.imshow(cm, interpolation='nearest', cmap=cmap) plt.title(title) plt.colorbar() tick_marks = np.arange(len(classes)) plt.xticks(tick_marks, classes, rotation=45) plt.yticks(tick_marks, classes)if normalize: cm = cm.astype('float')/ cm.sum(axis=1)[:, np.newaxis]print("Normalized confusion matrix")else:print('Confusion matrix, without normalization') thresh = cm.max()/2.for i, j in itertools.product(range(cm.shape[0]),range(cm.shape[1])): plt.text(j, i, cm[i, j], horizontalalignment="center", color="white"if cm[i, j]> thresh else"black") plt.tight_layout() plt.ylabel('True label') plt.xlabel('Predicted label')技术支持
侦测所谓的“假新闻”并不容易。首先,得先定义什么是假新闻——如今这甚至已成为一种政治表态。即便您找到了或达成了一定定义共识,也必须收集并正确标注真假新闻(最好是围绕相似主题,以便更清晰地区分)。完成收集后,还需要找到有用的特征来判别真假新闻。
想更深入了解这一问题空间,建议阅读 Miguel Martinez-Alvarez 的文章《How can Machine Learning and AI Help Solve the Fake News Problem》。
在读到 Miguel 的这篇洞见之作的同时,我还看到一篇开放数据科学的文章,探讨了如何使用贝叶斯模型构建成功的假新闻检测器。作者甚至创建了一个包含真假新闻标注样本的数据集仓库。我很好奇自己是否能轻松复现结果,并进一步弄清模型到底学到了什么。
在本教程中,您将跟随我最初的一些探索,一起看看能否构建一个有效的假新闻检测器!
提示:如果您想进一步学习自然语言处理(NLP)基础,建议参加Python 自然语言处理基础课程。
数据探索
首先,您应当快速浏览数据,感受其内容。为此,可以使用 Pandas DataFrame,查看其形状、前几行,并进行必要的转换。
提取训练数据
现在 DataFrame 已经更接近所需的样子,您需要分离标签并建立训练集与测试集。
在这个笔记本中,我决定聚焦使用较长的文章正文。因为我知道会用词袋(bag-of-words)与词频-逆文档频(TF-IDF)来抽取特征,所以这看起来是个不错的选择。使用更长的文本,有望为真假新闻数据带来更有区分度的词与特征。
构建向量化器分类器
现在您已经有了训练与测试数据,可以开始构建分类器了。为了了解文章中的词与标记是否对真假新闻的判断有显著影响,先从使用
CountVectorizer与TfidfVectorizer开始。您会看到示例中为 TF-IDF 向量化器
tfidf_vectorizer通过max_df参数设置了最大阈值.7。这会移除出现在超过 70% 文章中的词。此外,内置的stop_words参数会在向量化前移除英文停用词。还有更多可用参数,您可在 scikit-learn 文档中阅读相关说明:
TfidfVectorizer与CountVectorizer。现在已有向量,接着可以查看存储在
count_vectorizer与tfidf_vectorizer中的向量特征。有没有明显的问题?(有!)
很明显,当前数据集中包含注释、计量单位或其他无意义的词,还有多语种文章。通常,您应投入更多时间进行预处理并去除噪声,但由于本教程仅作为一个小型概念验证,我们将看看模型能否克服噪声,尽管存在这些问题仍能正确分类。
插曲:Count 与 TF-IDF 特征
我很好奇 count 与 TF-IDF 向量化器是否提取了不同的标记。为了查看并比较特征,您可以把向量信息还原回 DataFrame,以便进行简单的 Python 比较。
如您运行下列单元所见,两种向量化器提取了相同的标记,但显然权重不同。很可能通过调整 TF-IDF 向量化器的
max_df与min_df会改变结果,从而让二者的特征产生差异。5 rows × 56922 columns
5 rows × 56922 columns
比较模型
现在是训练与测试模型的时候了。
这里,我们从 NLP 常客
MultinomialNB开始。您可以用它来比较 TF-IDF 与词袋。我直觉认为词袋(即CountVectorizer)在该模型下表现更好。(想了解多项分布以及其为何更适用于整数的原因,请参阅这份宾大统计课程的简明解释)。我个人觉得混淆矩阵更易于比较与阅读,于是参考了 scikit-learn 文档构建了易读的混淆矩阵(感谢开源!)。混淆矩阵在主对角线(从左上到右下)显示预测正确的标签。其他单元格显示预测错误的标签,通常被称为假阳性或假阴性。根据您的问题场景,其中一项可能更重要。例如,对于假新闻问题,我们是否更在意不要把真实新闻误判为假新闻?如果是,最终可能需要对准确率进行加权,以更好地反映这一关注点。
除了混淆矩阵,scikit-learn 还提供了多种可视化和比较模型的方式。一种常见方法是使用 ROC 曲线。在 scikit-learn 的 metrics 模块中还有许多其他评估模型的方法。
果然,在完全未调参的情况下,词频向量化后的训练集
count_train明显优于 TF-IDF 向量!测试线性模型
有很多精彩的文章讲述线性模型与 TF-IDF 向量化器的良好配合(可参阅用于分类的 word2vec、scikit-learn 文本分析中的 SVM 参考等)。
那么,您就该用 SVM,对吗?
我最近看了Victor Lavrenko 关于文本分类的讲座,他将被动-进取(Passive Aggressive)分类器与线性 SVM 用于文本分类进行了比较。我们将用该数据集来测试这种方法(它具有显著的速度优势,但学习是一次性的也有劣势)。
哇!
令人印象深刻。混淆矩阵看起来有所不同,模型对假新闻的分类稍有提升。我们可以测试一下为
MultinomialNB调整alpha值能否得到可比结果。您还可以使用网格搜索进行参数调优,做更全面的探索。还差点意思……此时,或许可以对所有分类器进行参数调优,或看看其他scikit-learn 的贝叶斯分类器。您也可以用支持向量机(SVM)测试一下,看看是否优于被动-进取分类器。
不过我更好奇的是,被动-进取模型到底学到了什么。所以我们继续做内省。
模型内省
所以,假新闻问题解决了,对吗?在我的数据集上达到了 93% 的准确率,那就可以打烊回家了。
当然并非如此。考虑到我们在特征中看到的大量噪声,我对这些结果持保留态度。StackOverflow 上有一篇很棒的写作,提供了一个非常实用的函数,用来寻找对标签影响最大的向量。它只适用于二分类(两个类别的分类器),但对您来说正合适,因为只有 FAKE 与 REAL 两个标签。
使用当前表现最好的分类器与 TF-IDF 向量数据集(
tfidf_vectorizer)和被动-进取分类器(linear_clf),来查看假新闻与真新闻的前 30 个向量:您也可以用几行非常直接的 Python 代码来实现:将系数与特征打包到一起,然后查看列表的顶部与底部。
很明显,在“假”类的顶级特征中,有一些词语可能传达出政治立场与来源(比如 corporate 与 establishment)。
另外,真实新闻数据更常用 “to say” 的各种形式,这很可能是因为在报纸与大多数新闻出版物中,来源会被直接引用(“德国总理默克尔表示……”)。
若要从当前分类器中导出完整列表并查看每个标记(或在分类器之间轻松比较标记),可以像下面这样导出。
插曲:HashingVectorizer
文本分类中有时还会使用另一种向量化器:
HashingVectorizer。HashingVectorizer更省内存、速度更快(因为是稀疏的并使用哈希而非标记),但更难以做内省。如果您感兴趣,可在 scikit-learn 文档中了解使用HashingVectorizer的利弊。您可以尝试一下,并将其结果与其他向量化器比较。它的表现相当不错,使用
MultinomialNB时优于 TF-IDF 向量化器(这在一定程度上意料之中,原因与CountVectorizer表现更好的原因类似),但仍不及结合被动-进取线性算法的 TF-IDF 向量化器。结论
那么,您的假新闻分类器实验成功了吗?当然没有。
但您是否玩转了一个新数据集,测试了几种 NLP 分类模型,并对它们的效果进行了内省?是的。
正如一开始所预期的,试图用简单的词袋或 TF-IDF 向量来定义假新闻是过度简化的做法。尤其是在一个包含大量噪声标记的多语种数据集中。如果您没有看看模型到底学到了什么,可能会误以为它学到了某些有意义的东西。所以,记住:务必尽可能对模型进行内省!
如果您发现了我可能遗漏的其他数据趋势,我很感兴趣。我将会在我的博客跟进发布一篇文章,讨论不同分类器在重要特征方面的比较。如果您花时间做了研究并发现了有趣的内容,欢迎在评论区分享,也可以随时在 Twitter 上联系我(我在 @kjam)。
希望您在与我一起探索这个新的 NLP 数据集时玩得开心!
查看 DataCamp 的Python 机器学习:Scikit-Learn 教程。