在注释分类问题中使用文本和提取的数字特征





数据是什么样的?



首先,让我们看一下可用的测试和培训数据(来自kaggle.com平台上“毒性评论分类”挑战的数据)。在训练数据中,与测试数据相比,有用于分类的标签:





图1-训练数据头



从表中可以看到,在训练数据中,我们有6个标签列(“有毒”,“严重_有毒”,“淫秽”,“威胁” ,“ Insult”,“ identity_hate”),其中值“ 1”表示注释属于该类,还有一个包含注释的“ comment_text”列和一个“ id”(注释标识符)。



测试数据不包含类标签,因为它用于发送解决方案:





图2-测试数据头



特征提取



下一步是从评论中提取特征并进行探索性数据分析(EDA)。首先,让我们看一下训练数据集中评论类型的分布。为此,创建了一个新列“ toxic_type”,其中包含该注释所属的所有类:





图3-前十大类型的有毒注释



从表中您可以看到,主要的类型是缺少任何类标签,并且许多注释属于多个类。



让我们还看看如何为每个注释分配类型数:





图4-遇到的类型数



请注意,主要情况是评论仅以一种类型的毒性为特征,而评论通常以三种类型的毒性为特征,而评论却很少归因于所有类型。



现在让我们进入从文本提取特征的阶段,这通常称为特征提取。我提取了以下属性:



注释的长度。我想这些愤怒的评论可能很简短。

大写。在激进情绪的评论中,大写在文字中可能会更常见;

表情符号。在撰写有毒评论时,不太可能使用正面颜色的表情符号(:)等),我们还将考虑存在悲伤的表情符号(:(等);

标点。负面评论的作者可能不遵守标点符号规则,他们大多使用“!”;

第三方字符数。有些人在撰写令人反感的单词时经常使用@,$等符号。

功能添加如下:



train_data[‘total_length’] = train_data[‘comment_text’].apply(len)
train_data[‘uppercase’] = train_data[‘comment_text’].apply(lambda comment: sum(1 for c in comment if c.isupper()))
train_data[‘exclamation_punction’] = train_data[‘comment_text’].apply(lambda comment: comment.count(‘!’))
train_data[‘num_punctuation’] = train_data[‘comment_text’].apply(lambda comment: comment.count(w) for w in ‘.,;:?’))
train_data[‘num_symbols’] = train_data[‘comment_text’].apply(lambda comment: sum(comment.count(w) for w in ‘*&$%’))
train_data[‘num_words’] = train_data[‘comment_text’].apply(lambda comment: len(comment.split()))
train_data[‘num_happy_smilies’] = train_data[‘comment_text’].apply(lambda comment: sum(comment.count(w) for w in (‘:-)’, ‘:)’, ‘;)’, ‘;-)’)))
train_data[‘num_sad_smilies’] = train_data[‘comment_text’].apply(lambda comment: sum(comment.count(w) for w in (‘:-(’, ‘:(’, ‘;(’, ‘;-(’)))


探索性数据分析



现在,让我们使用刚刚获得的功能探索数据。首先,让我们看一下要素彼此之间的相关性,要素与类标签之间的相关性,类标签之间的相关性:





图5-相关



相关性表明要素之间存在线性关系。模数的相关值越接近1,则元素之间的线性相关性越明显。



例如,您可以看到单词的数量和文本的长度彼此密切相关(值0.99),这意味着可以从其中删除某些功能,而我删除了单词的数量。我们还可以得出更多结论:所选特征和类别标签之间实际上没有相关性,相关性最低的是字符数,文本的长度与标点字符数以及转换为大写字母的字符数相关。



接下来,我们将构建一些可视化对象,以更详细地了解功能对类标签的影响。首先,让我们看看注释长度是如何分布的:图6-注释长度的分布





(图形是交互式的,但这是屏幕截图)



不出所料,未分类的评论(即正常)的长度比标记的评论长得多。在负面评论中,最短的是威胁,最长的是有毒。



现在,让我们根据标点符号来检查注释。我们将构建平均值的图形表示形式,以使图更易于解释:





图7-平均标点值(图是交互式的,但这是屏幕截图)



从图中可以看到我们得到了三个聚类。



第一种-普通注释,其特征在于遵守标点符号规则(例如,放置标点符号,例如“:”)和少量的感叹号。



第二组包括威胁(威胁)和剧毒评论(剧毒),该组的特征是大量使用感叹号,而在中层使用其他标点符号。



第三类-有毒(有毒),淫秽(淫秽),侮辱(侮辱)和对某人的仇恨(身份仇恨)具有少量的标点符号和感叹号。



为了清楚起见,我们添加第三个轴-大写字母:





图8-三维图像(交互式,但这是屏幕截图)



在这里,我们看到了类似的情况-高亮显示了三个群集。还要注意,第二群集的元素之间的距离大于第三群集的元素之间的距离。这也可以在2D图形中看到:





图9-大写和标点符号(交互式,这是屏幕截图)



现在让我们看一下大写上下文中的注释类型/第三方字符数:





图10-大写和第三方字符数(交互式,这是屏幕截图)



如您所见,非常有毒的注释清晰地突出显示了-它们具有大量的大写字符和许多第三方字符。同样,第三方符号被讨厌某人的评论的作者积极使用。



因此,突出显示新功能并对其进行可视化可以更好地解释可用数据,并且以上可视化效果可以总结如下:



高毒性评论与其余评论分开;

普通评论也很突出。

就所考虑的特征而言,有毒,淫秽和令人反感的评论彼此非常接近。

使用DataFrameMapper组合文本和数字功能



现在,让我们看一下如何在Logistic回归中一起使用文本和数字功能。



首先,您需要选择一个模型以适合机器学习算法的形式表示文本。我使用了tf-idf模型,因为它能够突出显示特定单词并使常用单词的重要性降低(例如,介词):



tvec = TfidfVectorizer(
sublinear_tf=True,
strip_accents=’unicode’,
analyzer=’word’,
token_pattern=r’\w{1,}’,
stop_words=’english’,
ngram_range=(1, 1),
max_features=10000
)


因此,如果我们要使用Pandas库提供的数据框和Sklearn库的机器学习算法,可以使用Sklearn-pandas模块,该模块充当数据框和Sklearn方法之间的一种绑定。



mapper = DataFrameMapper([
			([‘uppercase’], StandardScaler()),
			([‘exclamation_punctuation’], StandardScaler()),
			([‘num_punctuation’], StandardScaler()),
			([‘num_symbols’], StandardScaler()),
			([‘num_happy_smilies’], StandardScaler()),
			([‘num_sad_smilies’], StandardScaler()),
			([‘total_length’], StandardScaler())
		], df_out=True)


首先,您需要创建一个如上所示的DataFrameMapper,它必须包含具有数字功能的列的名称。接下来,我们创建一个特征矩阵,然后将其转移到Logistic回归进行训练:



x_train = np.round(mapper.fit_transform(numeric_features_train.copy()), 2).values
x_train_features = sparse.hstack((csr_matrix(x_train), train_texts))


对测试数据集也执行类似的动作序列。



计算实验



为了进行多标签分类,我们将构建一个遍历所有类别的循环,并通过使用参数cv = 3和评分='roc_auc'的交叉验证来评估分类的质量:



scores = []
class_names = [‘toxic’, ‘severe_toxic’, ‘obscene’, ‘threat’, ‘identity_hate’]
for class_name in class_names:
	train_target = train_data[class_name]
	classifier = LogisticRegression(C=0.1, solver= ‘sag’)
	cv_score = np.mean(cross_val_score(classifier, x_train_features, train_target, cv=3, scoring= ‘auc_roc’))
	scores.append(cv_score)
	print(‘CV score for class {} is {}’.format(class_name, cv_score))
	classifier.fit(train_features, train_target)

print(‘Total CV score is {}’.format(np.mean(scores)))</source
<b> :</b>

<img src="https://habrastorage.org/webt/kt/a4/v6/kta4v6sqnr-tar_auhd6bxzo4dw.png" />
<i> 11 —  </i>

      ,       ,  , ,       ,   .  ,    ,       ,      .    - ,   “toxic”, ,  ,       (  3).         , ,          ,        .



All Articles