多标签文本分类简述
使用XGBoost和递归神经网络理解和实现多标签文本分类
事实上,自然语言处理中的几乎所有问题都可以简化为多标签问题,这是一个很好的研究和应用课题!
什么是多标签?
多标签是将标签分配给文档的过程,其中每个标签都是从独立的选项列表中选择的。多标签的关键在于允许为每个文档分配多个标签。您可以使用该框架来标识文本的主题,科目和文本特征,以便理解文本文档。
多标签Vs多分类
多分类是多标签的子集,其中标签的数量固定为1。多分类要求类是互斥的,并且无法处理独立标签。您可以使用此框架进行分类。
- 互斥标签指的是一个类的存在阻止任何其他类的存在的标签,即具有正面情绪的句子不能同时具有消极或中性情绪。使用多分类!
- 独立标签是指一个标签的存在不影响任何其他标签的情况,即电影可以是动作片,喜剧片,动画片或某些选项组合,包括动作喜剧或动画动作。使用多标签!
所以现在我们知道何时使用多标签和多分类,让我们看看它们之间的一些重要技术差异。
技术差异
主要区别在于模型输出格式。在多分类的情况下,输出将是在所有可能类上标准化的概率密度函数。这是一种奇特的数学方式,表示所有预测概率的总和必须为1。所以,如果属于A类的文档的概率为0.8,那么它属于所有剩余类的概率必须为0.2。概率最高的类获胜。所以,文档属于A类的可能性越大,它属于任何其他类的可能性就越小。考虑到我们对互斥标签的了解,这应该是有意义的。
多标签将每个标签视为其独特的二进制分类。对于每个标签,预测概率值必须在0和1之间,如果预测概率大于0.5,则选择标签。在这种情况下,分配给A类的概率对任何其他类的概率没有影响。
模型类型:
有些模型可以直接用于多标签,而其他模型则需要稍加修改。Sklearn提供了潜在的多标签模型的完整列表(https://scikit-learn.org/stable/modules/multiclass.html)。
总体架构:对于不直接支持多标签的算法,我们可以使用One-Vs-Rest ensemble包装器。包装器的目的是将多标签问题分解为一系列二进制问题,这些问题可以使用二进制分类来解决。假设我们有6个标签要分配,而不是训练一个模型识别所有6个标签,而是训练6个单独的分类器来识别每个标签。每个分类器只查找其特定的标签,因此能够对标签是否存在进行二进制预测。
神经网络分类器:任何神经网络分类器都可以用于多标签,只需将输出层从概率分布改为二进制分类即可。这可以通过使用具有sigmoid激活函数的密集输出层和使用二进制交叉熵作为损失函数来实现。通过使用sigmoid激活函数,我们告诉网络最后一个输出层应该是每个节点0到1之间的预测。输出层的尺寸应等于标签的数量。
编码示例:XGBoost ensemble和双向循环神经网络
我们将使用两种非常强大的算法来实现XGBoost和双向循环神经网络(bi-RNN)。选择这些算法是因为它们已被证明在各种文本分析问题中都有效,但是对每种算法的完整解释超出了本文的范围。
相反,我们将演示如何使用one vs rest ensemble包装器将任何模型转换为多标签模型,以及如何修改任何神经网络架构来支持多标签。
加载数据
我们将使用的数据集来自Kaggle上发布的。该数据集包含160k条评论,标有6种可能的类型,例子可能包括多种标签。
# Kaggle Toxic Detection Dataset import pandas as pd from sklearn.model_selection import train_test_split # Loading Data from Local path = '..//..//Datasets//Toxic comments Detection (Kaggle)//train.csv' train = pd.read_csv(path) text = train['comment_text'] labels = train[['toxic', 'severe_toxic', 'obscene', 'threat', 'insult', 'identity_hate']] # Creating training and testing sets x_train, x_test, y_train, y_test = train_test_split(text, labels, test_size = 0.15, random_state = 42)
加载数据并将其拆分为训练集和测试集XGBoost
创建TF-IDF向量以通过XGBoost将文本数据转换为机器可读数据。
from sklearn.feature_extraction.text import TfidfVectorizer tfidf = TfidfVectorizer(lowercase = False, stop_words = 'english', max_features = 10000) tfidf.fit(text) tfidf_train = tfidf.transform(x_train) tfidf_test = tfidf.transform(x_test)
将文本数据转换为机器可读的TF-IDF向量使用One-Vs-Rest分类器作为我们的整体包装器将梯度提升分类器转换为多标签。
from sklearn.multiclass import OneVsRestClassifier from sklearn.ensemble import GradientBoostingClassifier from sklearn.metrics import jaccard_similarity_score # Training XGboost Classifier using OvR ensemble wrapper ovr_xgboost = OneVsRestClassifier(GradientBoostingClassifier(n_estimators = 100, verbose = 1)) ovr_xgboost.fit(tfidf_train, y_train) Predictions = ovr_xgboost.predict(tfidf_test) Accuracy = jaccard_similarity_score(Predictions, y_test) print('The One Vs Rest similarity accuracy is', Accuracy)
没有任何优化或参数调整,我们可以达到0.925的精度。现在您可以用您喜欢的任何模型替换梯度提升分类器来创建一个支持多标签的分类器。
双向循环神经网络
处理RNN的数据有点复杂,这是因为必须一次一个地将单词传递给RNN,而不是作为大多数其他模型中常见的单个特征向量。
我们首先创建语料库中每个单词的词汇表并按频率对它们进行排序,所以最常见的单词的值为1,第二个最常用的单词的值为2......。接下来,将每个文档从单词序列转换为基于词汇表的数字序列。然后将序列缩放使其长度均为max_length。
# Using a Recurent Neural Network from keras.preprocessing import sequence from keras.preprocessing.text import Tokenizer max_vocabulary = 6000 max_length = 40 # Converting Text to word tokens token_model = Tokenizer(num_words = max_vocabulary) token_model.fit_on_texts(text) # Convert training text files to 40 word padded sequences train_sequences = token_model.texts_to_sequences(x_train) train_sequences_padded = sequence.pad_sequences(train_sequences, maxlen = max_length)
接下来,我们使用以下层来构建网络:
- 为每个单词创建n维嵌入的嵌入层。
- 双向RNN层,它使用正向和反向顺序存储单词嵌入序列中的重要信息。Dropout用于限制过度拟合。
- 密集输出层,将任何神经网络转换为多标签网络的层,它必须具有sigmoid激活函数和数量等于标签数量的输出节点。
最后,使用二进制交叉熵损失函数对模型进行了编译,并使用训练数据进行训练。只使用5个训练周期来节省运行时间,但通常建议使用更多的训练周期。
from keras.models import Sequential from keras.layers import Embedding from keras.layers import Bidirectional from keras.layers import SimpleRNN from keras.layers import Dense # Create the RNN model model_rnn = Sequential() model_rnn.add(Embedding(max_vocabulary, dim256, input_length = max_length)) model_rnn.add(Bidirectional(SimpleRNN(dim256, dropout = 0.5, recurrent_dropout = 0.5) ) ) model_rnn.add(Dense(6, activation='sigmoid')) # Compile model_rnn.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy']) model_rnn.fit(train_sequences_padded, y_train, batch_size = 64, epochs = 5)
最后,将测试数据转换为顺序数据,进行预测,并对结果进行评分。
import numpy as np from sklearn.metrics import jaccard_similarity_score # Converting test data to padded Sequences test_sequences = token_model.texts_to_sequences(x_test) test_sequences_padded = sequence.pad_sequences(test_sequences, maxlen = max_length) # Predicting test data Predictions = model_rnn.predict(test_sequences_padded) # Converting probablility score to binary Predictions = np.floor(Predictions + 0.5) Accuracy = jaccard_similarity_score(Predictions, y_test) print('The Bi-Directional Rnn Accuracy is', Accuracy)
精度得分为0.933,略高于XGBoost,对于没有任何超参数调整或修改的模型,效果非常好。
结论
- 解释多标签分类
- 比较不同的分类问题,以说明何时使用多标签与多分类
- 描述了如何使用One-Vs-Rest ensemble包装器将任何标准分类器转换为多标签分类器
- 描述了如何使用密集的sigmoid输出层将任何神经网络转换为多标签分类器
- 演示了一个使用梯度提升决策树的编码示例
- 演示了一个使用双向循环神经网络的代码示例