文本分类:互联网内容王国的锦衣卫

2020 年 2 月 26 日 AINLP

一、文本分类与内容审核

内容是互联网公司线上业务流转的血液,重视内容生产也是各家互联网公司的常态,但是在追求内容量的同时,控制内容的质也是不可或缺的,因为内容是直接广播给亿万普通用户的,所以内容审核也是所有互联网公司要面临的难题。面对海量的内容,要做到自动高效地审核,文本分类就是一项关键技术,堪称互联网内容王国的锦衣卫。

文本分类作为NLP领域最经典的使用场景之一,已经积淀了许多方法,大致可以分为三类。

第一种是最早的专家规则(Pattern)方法,这种方法可以短平快的解决top问题,但覆盖的范围和准确率都非常有限。

第二种就是统计学习方法。在线文本数量增长和机器学习的兴起,一套‘人工特征工程+浅层分类模型’的方式逐渐形成,用来解决大规模文本分类问题。这一阶段主要的的特征表示方法为one-hot,连续词袋模型BOW,tfidf等等,主要的分类模型包括SVM,贝叶斯,决策树等等。

第三种就是深度学习方法,目的为了解决文本表示高纬度、高稀疏的特征表达能力弱,文本上下文依赖问题。google的Mikolov提出的word2vec词向量及工具包的开源,推动了文本语义的研究,与此同时,提出了Hierarchical Softmax 和 Negative Sample两个方法,很好的解决了计算效率问题。脱胎于word2vec,诞生了一个fasttext模型。强有力的词向量的表示方法,使得文本可以转化为类图片的稠密数据,从而诞生了基于cnn的模型,例如textcnn;为了解决文本上下文依赖问题,循环神经网络RNN,LSTM,BiLSTM等模型被应用。

基于此,本文将浅探SVM,fastText, GDBT三个常见文本分类模型在产业互联网内容审核业务中的实践效果。

二、相关技术

2.1.svm

支持向量机(svm)通过寻找一个超平面来对样本进行分割,分割的原则是间隔最大化,最终转化为一个凸二次规划问题来求解。通常有三种情况:

一、样本线性可分时,通过硬间隔最大化;

二、为了解决一些outliers,引入松弛变量,增强容错率,采用软间距;

三、为了解决线性不可分的样本,引入核函数,将原始样本映射到一个高维空间,从而使其线性可分。

这三种情况的描述如下:

为了更容易地求解支持向量的优化问题,和自然地引入核函数,应用拉格朗日对偶性将原始问题转化为对偶问题进行求解,线性可分svm原始问题与对偶问题对应关系如下:

在内容审核中,首先提取房评的tfidf特征,然后进行简单的svm分类,相关代码:

  
  
    
  1. from sklearn.svm import SVC

  2. def train(self, x, label):

  3. self.svm = SVC(

  4. kernel='rbf',

  5. class_weight='balanced',

  6. C=3,

  7. gamma=1,

  8. probability=True)

  9. self.svm.fit(x, label)

2.2.fasttext

脱胎于word2vec的fastText主要包含三部分,模型架构层次SoftMax和N-gram特征。原理是把句子中所有的词向量进行平均,然后直接接softmax层。与此同时,加入了一些n-gram特征来捕获局部序列信息。相关原理图如下:

在内容审核中,先将内容信息分词,然后用调用fasttext,命令如下:

  
  
    
  1. fastText/build/fasttext supervised \

  2. -input data/raw/fasttext_train_1.txt \

  3. -output data/fasttext_model/fst_1 \

  4. -lr 1 -epoch 100


  5. fastText/build/fasttext predict-prob data/fasttext_model/fst_1.bin \

  6. data/raw/fasttext_test_1.txt > data/raw/fasttext_res_1.prob

2.3 GBDT

梯度提升决策树(GDBT)采用加法模型,不断减小训练过程产生的残差来达到将数据分类或者回归的算法。GBDT通过多轮迭代,每轮迭代产生一个弱分类器,弱分类器一般为CART TREE(分类回归树),每个分类器在上一轮分类器的残差基础上进行训练。最终的总分类器 是将每轮训练得到的弱分类器加权求和得到的。模型最终可以描述为:

在内容审核中,对于不同的文本进行不同的处理,对于基础信息的类型字段做onehot特征处理,标签字段counter2vector特征处理,以及城市字段做onehot特征处理,对于内容详情分字段处理特征,关键词特征,从人工审核人员标红的内容中提取tfidf特征等,具体如下图所示:


特征工程相关代码:

  
  
    
  1. from sklearn.feature_extraction.text import CountVectorizer

  2. from sklearn.preprocessing import OneHotEncoder,LabelEncoder

  3. from sklearn.feature_extraction.text import TfidfVectorizer

  4. def get_feature(self, input_path,all_data_path):

  5. data = pd.read_csv(input_path)

  6. data_all = pd.read_csv(all_data_path).fillna('')

  7. #获取标红字段语料

  8. data_all['SEG']=data_all['biaohong'].apply(seg_comment)

  9. data['SEG'] = data['comment'].apply(filter_comment).apply(seg_comment)

  10. data['comment_dict'] = data['comment'].apply(json_to_dict)

  11. data['base_dict'] = data['base'].apply(json_to_dict)

  12. features = list(data_all['SEG'])

  13. tfidf_vec1 = TfidfVectorizer(token_pattern=r"(?u)\b\w+\b",

  14. stop_words=self.stopword)

  15. tfidf_vec1.fit(features)

  16. #关键词

  17. tfidf_vec2 = TfidfVectorizer(

  18. token_pattern=r"(?u)\b\w+\b",

  19. stop_words=self.stopword,

  20. vocabulary=set(self.keywords))

  21. tfidf_vec2.fit(features)

  22. # 划分数据集

  23. np.random.seed(0)

  24. test_index = np.random.choice(len(data), size=int(0.2 * len(data)))

  25. train_index = list(set(data.index.tolist()).difference(set(test_index)))

  26. test_data = data.loc[test_index, :]

  27. test_x = tfidf_vec1.transform(list(test_data['SEG']))

  28. test_y = list(test_data[self.class_used])

  29. train_data = data.loc[train_index, :]

  30. train_x = tfidf_vec1.transform(list(train_data['SEG']))

  31. train_y = list(train_data[self.class_used])


  32. arr_test = tfidf_vec2.transform(list(test_data['SEG']))

  33. arr_train = tfidf_vec2.transform(list(train_data['SEG']))

  34. test_x = sparse.hstack((test_x, arr_test))

  35. train_x = sparse.hstack((train_x, arr_train))


  36. cont_vec = CountVectorizer(

  37. token_pattern=r"(?u)\b\w+\b",

  38. max_features=50,

  39. stop_words=self.stopword)


  40. # comment分字段特征提取

  41. for field in self.comment_key:

  42. data[field] = data.apply(

  43. lambda raw: sub_feild_text(raw['comment_dict'], field), axis=1)

  44. data[field] = data[field].apply(seg_comment)

  45. cont_vec.fit(data[field])

  46. arr_train = cont_vec.transform(data.loc[train_index][field])

  47. arr_test = cont_vec.transform(data.loc[test_index][field])

  48. train_x = sparse.hstack((train_x, arr_train))

  49. test_x = sparse.hstack((test_x, arr_test))


  50. #基础信息特征

  51. onehot_vec = OneHotEncoder()

  52. counter_vec = CountVectorizer(stop_words=self.stopword)

  53. for field in self.base_key:

  54. if field in self.onehot_field:

  55. data[field] = data.apply(lambda raw: sub_feild_text(raw['base_dict'], field), axis=1)

  56. data[field] = LabelEncoder().fit_transform(data[field])

  57. onehot_vec.fit(data[field].values.reshape(-1, 1))

  58. arr_train = onehot_vec.transform(data.loc[train_index][field].values.reshape(-1, 1))

  59. arr_test = onehot_vec.transform(data.loc[test_index][field].values.reshape(-1, 1))

  60. train_x = sparse.hstack((train_x, arr_train))

  61. test_x = sparse.hstack((test_x, arr_test))

  62. if field in self.counter_field:

  63. data[field] = data.apply(lambda raw: sub_feild_text(raw['base_dict'], field), axis=1)

  64. counter_vec.fit(data[field])

  65. arr_train = counter_vec.transform(data.loc[train_index][field])

  66. arr_test = counter_vec.transform(data.loc[test_index][field])

  67. train_x = sparse.hstack((train_x, arr_train))

  68. test_x = sparse.hstack((test_x, arr_test))


  69. # 加入城市字段特征

  70. counter_vec.fit(data['city_code'].apply(str))

  71. arr_train = counter_vec.transform(

  72. data.loc[train_index]['city_code'].apply(str))

  73. arr_test = counter_vec.transform(

  74. data.loc[test_index]['city_code'].apply(str))

  75. train_x = sparse.hstack((train_x, arr_train))

  76. test_x = sparse.hstack((test_x, arr_test))

  77. # 归一化

  78. scaler = MaxAbsScaler()

  79. scaler_path = self.vectorizer_path + 'nor_vec' + '.pickle'

  80. scaler.fit(train_x)

  81. train_x = scaler.transform(train_x)

  82. test_x = scaler.transform(test_x)

  83. self.save_model(scaler, scaler_path)

  84. return train_x, train_y, test_x, test_y

模型代码:

  
  
    
  1. from sklearn.ensemble import GradientBoostingClassifier

  2. def train(self, x, label):

  3. self.gdbt = GradientBoostingClassifier(

  4. learning_rate=0.06,

  5. n_estimators=1000,

  6. random_state=0,

  7. max_depth=9,

  8. subsample=0.8,

  9. min_samples_leaf=20)

  10. self.gdbt.fit(x, label)

三、实验对比

首先,拉取数据库中的全量数据,统计各审核类型的占比。

其次,根据处理人字段,筛选出优秀审核人员的数据。

然后,对全量数据进行清洗,将明显的错误样本数据过滤掉,主要通过做了两部处理;一、关键词过滤明显判错的样本,二、采用simhash去掉两个字段完全相同但判错的样本。

再者,根据全量数据分布随机抽取各类数据;最后,构建了与线上分布一致的数据集。数据处理的流程如下:

随机拉取了1000条数据用于验证。审核类型有11种,对于不同类别的要求,审核方给了详细的文档,根据文档说明,总结了各类的相关规则;整个项目流程是先规则审核,规则处理不了的,通过模型处理。

以上三种模型在此1000条数据上的效果如下:

模型 准确率 召回率 F1
SVM 0.9 0.81 0.85
fastText 0.9 0.8 0.85
GDBT 0.9 0.84 0.87

从上表可以得出如下结论:在内容审核项目中,由于树模型与该问题决策过程比较吻合,且提取了大量可描述错误信息的特征,故性能表现较好。对于需要审核的内容,各字段描述的信息具有一致性,通过样本与不通过样本差异不明显,无明显语义区别,故而fasttext性能不佳。SVM是基于距离来优化模型的,对于离散型的特征,即使归一化了,计算距离时,影响较大,会导致效果不佳。

四、踩过的坑

  1. 尽管一般增加样本有防止过拟合、提高模型的识别能力等等优点,当训练样本大于2w条时,SVM会很吃力;当训练数据量比较大时,离群点变多时,也会导致SVM性能不佳。

  2. 数据分布很重要,但采样的数据分布差异较大时,评估的结果会存在较大的差异,数据集划分很重要。

五、参考文献

[1]李航统计方法学(第七章SVM)

[2]https://arxiv.org/pdf/1301.3781.pdf (word2vec) 

[3]https://arxiv.org/pdf/1411.2738.pdf (word2vec)

[4]https://arxiv.org/pdf/1607.01759v2.pdf (fasttext) 

[5]https://projecteuclid.org/download/pdf_1/euclid.aos/1013203451(gdbt)

作者介绍

江霜艳,2019年1月毕业于哈尔滨工业大学智能计算研究中心,毕业后加入贝壳语言智能与搜索部,主要从事意图理解、内容审核等工作。

推荐阅读

AINLP年度阅读收藏清单

ALBERT:用于语言表征自监督学习的轻量级 BERT

中文NER任务实验小结报告——深入模型实现细节

大幅减少GPU显存占用:可逆残差网络(The Reversible Residual Network)

鼠年春节,用 GPT-2 自动写对联和对对联

transformer-XL与XLNet笔记

AINLP-DBC GPU 云服务器租用平台建立,价格足够便宜

征稿启示 | 稿费+GPU算力+星球嘉宾一个都不少

关于AINLP

AINLP 是一个有趣有AI的自然语言处理社区,专注于 AI、NLP、机器学习、深度学习、推荐算法等相关技术的分享,主题包括文本摘要、智能问答、聊天机器人、机器翻译、自动生成、知识图谱、预训练模型、推荐系统、计算广告、招聘信息、求职经验分享等,欢迎关注!加技术交流群请添加AINLP君微信(id:AINLP2),备注工作/研究方向+加群目的。


登录查看更多
0

相关内容

在机器学习中,支持向量机(SVM,也称为支持向量网络)是带有相关学习算法的监督学习模型,该算法分析用于分类和回归分析的数据。支持向量机(SVM)算法是一种流行的机器学习工具,可为分类和回归问题提供解决方案。给定一组训练示例,每个训练示例都标记为属于两个类别中的一个或另一个,则SVM训练算法会构建一个模型,该模型将新示例分配给一个类别或另一个类别,使其成为非概率二进制线性分类器(尽管方法存在诸如Platt缩放的问题,以便在概率分类设置中使用SVM)。SVM模型是将示例表示为空间中的点,并进行了映射,以使各个类别的示例被尽可能宽的明显间隙分开。然后,将新示例映射到相同的空间,并根据它们落入的间隙的侧面来预测属于一个类别。

知识荟萃

精品入门和进阶教程、论文和代码整理等

更多

查看相关VIP内容、论文、资讯等
【ACL2020】基于图神经网络的文本分类新方法
专知会员服务
69+阅读 · 2020年7月12日
基于多头注意力胶囊网络的文本分类模型
专知会员服务
78+阅读 · 2020年5月24日
【哈工大】基于抽取的高考作文生成
专知会员服务
37+阅读 · 2020年3月10日
Transformer文本分类代码
专知会员服务
117+阅读 · 2020年2月3日
NLP基础任务:文本分类近年发展汇总,68页超详细解析
专知会员服务
58+阅读 · 2020年1月3日
神经网络与深度学习,复旦大学邱锡鹏老师
专知会员服务
119+阅读 · 2019年9月24日
图卷积神经网络(GCN)文本分类详述
专知
279+阅读 · 2019年4月5日
撩一发深度文本分类之RNN via Attention
AINLP
7+阅读 · 2019年1月27日
基于内容的小说文本分类
人工智能头条
7+阅读 · 2018年12月14日
专栏 | NLP概述和文本自动分类算法详解
机器之心
12+阅读 · 2018年7月24日
【长篇干货】深度学习在文本分类中的应用
七月在线实验室
27+阅读 · 2018年4月4日
基于深度学习的文本分类?
数萃大数据
9+阅读 · 2018年3月4日
机器学习自动文本分类
AI前线
23+阅读 · 2018年2月4日
深度学习在文本分类中的应用
AI研习社
13+阅读 · 2018年1月7日
基于 word2vec 和 CNN 的文本分类 :综述 & 实践
Sparse Sequence-to-Sequence Models
Arxiv
5+阅读 · 2019年5月14日
Music Transformer
Arxiv
5+阅读 · 2018年12月12日
Arxiv
7+阅读 · 2018年5月23日
VIP会员
相关VIP内容
【ACL2020】基于图神经网络的文本分类新方法
专知会员服务
69+阅读 · 2020年7月12日
基于多头注意力胶囊网络的文本分类模型
专知会员服务
78+阅读 · 2020年5月24日
【哈工大】基于抽取的高考作文生成
专知会员服务
37+阅读 · 2020年3月10日
Transformer文本分类代码
专知会员服务
117+阅读 · 2020年2月3日
NLP基础任务:文本分类近年发展汇总,68页超详细解析
专知会员服务
58+阅读 · 2020年1月3日
神经网络与深度学习,复旦大学邱锡鹏老师
专知会员服务
119+阅读 · 2019年9月24日
相关资讯
图卷积神经网络(GCN)文本分类详述
专知
279+阅读 · 2019年4月5日
撩一发深度文本分类之RNN via Attention
AINLP
7+阅读 · 2019年1月27日
基于内容的小说文本分类
人工智能头条
7+阅读 · 2018年12月14日
专栏 | NLP概述和文本自动分类算法详解
机器之心
12+阅读 · 2018年7月24日
【长篇干货】深度学习在文本分类中的应用
七月在线实验室
27+阅读 · 2018年4月4日
基于深度学习的文本分类?
数萃大数据
9+阅读 · 2018年3月4日
机器学习自动文本分类
AI前线
23+阅读 · 2018年2月4日
深度学习在文本分类中的应用
AI研习社
13+阅读 · 2018年1月7日
基于 word2vec 和 CNN 的文本分类 :综述 & 实践
Top
微信扫码咨询专知VIP会员