【IT168 技术】今年,我们已经听到了多家内容平台整改的消息,对于标题党、蹭热点等行为,用户早已经见怪不怪。同样是知识分享平台,知乎倒是躲过了每一次整改,肯定有人会说是幸运,也肯定有人会从理性的角度对知乎的内容推荐和讨论方式进行分析,就好像每一条知乎问答下的用户一样理性。
同样是AI算法,同样是内容推荐,知乎更侧重于将关注点放在内容生产本身,对违规内容的处理速度也是极其快速。本文详细介绍了知乎算法是如何通过识别垃圾广告导流信息、识别低质量回答并处理违规信息的,想必对业内不少工程师会有借鉴意义。
算法在社区氛围的应用(一):识别垃圾广告导流信息
近期,我们发现社区内出现了垃圾广告的导流内容,影响用户体验,破坏认真、专业和友善的社区氛围。为了解决这种情况,我们进行了大量努力和探索。最开始在识别导流信息上采用的是干扰转换+正则匹配+匹配项回溯的方式进行异常导流信息的识别与控制,取得了很好的效果。
但是我们发现,随着我们处理这些内容的同时,他们正在逐步增加导流信息的各种变体,常见的有以下几种方式:第一种变体是导流前缀的变化,如 QQ 导流前缀变化成企鹅,「腾顺」等等;第二变体是不使用前缀,如退款 123377281;第三种变体是导流中随机插入非特殊字符,如 319xxxx053xxxx7178。我们对这些变体进行了收集整理和分析。
通过对典型导流样本的分析,我们发现尽管导流信息变体在不断演化,但是它们所在的上下文变化并不明显。因此,我们尝试通过序列标注的方式来识别导流内容,提高算法的识别准确度。
模型
常用的序列标注算法,有 HMM、CRF、RNN、BILSTM-CRF 等。BILSTM-CRF 在多个自然语言序列标注问题(NER、POS)上都表现优秀,同时,通过实验,我们也发现 BILSTM-CRF 表现优于其他模型。
网络结构
BILSTM-CRF 模型结构如下图所示:
第一层为 Embedding 层,将输入文本转换为词向量表示。
第二层为双向 LSTM 层。LSTM 具有强大的序列建模能力,能够捕捉长远的上下文信息,同时还拥有神经网络拟合非线性的能力。相比单向的 LSTM,双向 LSTM 不仅能够利用以前的上下文信息,还能利用未来的上下文信息。
第三层为一个全连接层。作用是将上一层的输出,映射为 [T,C] 的向量,T为输入序列长度,C为标签数量。输出的也就是每个 timestep 对应的状态 score。
最后一层为 linear-chain CRF 层。CRF 计算的是一种联合概率,优化的是整个序列(最终目标),而不是将每个时刻的最优拼接起来。在 CRF 层,使用 viterbi 解码算法从状态 score 和转移矩阵中解码得到输出状态序列。
BILSTM-CRF 模型同时结合了 LSTM 和 CRF 的优点,使得其在序列标注任务上具有极强的优势。
CRF
从上述网络结构,可知要优化的目标函数由最后一层决定。
通常给定一个线性链条件随机场 ,当观测序列为
时,
标签序列为
的概率可写为
其中,Z(x) 为归一化函数,对所有可能的标签序列求和。
是特征函数,通常考虑转移特征和状态特征两方面。状态特征描述标签之间的相似程度,
是上一层网络的输出。
转移特征考虑状态之间的变化趋势
在模型的概率给出之后,可以使用最大似然估计优化参数,即最小化负对数似然 -logP(y|x),从而得到整个网络的 loss function。
是否包含前后缀?
训练模型之前,我们需要标记训练数据。
标记训练数据的一个问题是,是否要包含导流信息的前后缀。如「加V:xxxxxx」,是否需要包含「加V」。通常情况下,答案应该是不包含,因为我们的实体是微信号,「加 V」不属于我们要识别的实体。
但是,我们在实验过程中发现,如果不加前后缀,模型会把大量的英文单词,或者字母数字组合,标记为导流内容。原因是,作为中文社区,英文单词出现频率很低,同时大部分导流信息都是字母数字组合,从而使得模型出现错误。
针对这种情况,我们在处理数据时,将导流信息的前后缀也作为实体的一部分,有效的降低了上述问题出现的概率。
实体编码
序列标注模型另一个需要注意的问题是,实体编码的格式。常用的序列实体编码方式有IO、BIO、BMEWO三种。
IO 编码是最简单的编码,它将属于类型X的实体的序列元素标记为 I_X,不属于任何实体的序列元素标记为 O。这种编码存在缺陷,因为它不能代表彼此相邻的两个实体,因为没有边界标签。
BIO 编码是当前实体编码的行业标准。它将表示实体的 I_X 标签细分为实体开始标签 B_X和实体延续标签 I_X。
BMEWO 编码进一步区分实体结束元素 E_X 和实体中间元素令 M_X,并为单元素实体添加一个全新的标签 W_X。
上述三种编码的示例,如下所示:
综合考虑,我们选择 BIO 编码,一是满足我们对于导流信息的区分,二是其标记方式相对通用。
效果
在实验阶段,分别将 HMM、BILSTM、BIGRU、BILSTM-CRF 做了一系列的对比,将表现比较好的 BILSTM-CRF 放在线上与原本的 Base 模型进行 AB 实验。从结果上来看,宽深度学习模型在线下/线上都有比较好的效果。线上实验结论如下:
后续的改进
为了提高模型的效果,我们需要使用更多的训练数据,构造更复杂的网络结构,使用更多的超参数设置训练模型。然而,不断增加的模型尺寸和超参数极大地延长了训练时间。很明显,计算能力已经成为了模型优化的主要瓶颈。相比 CNN 和 Attention 等操作,LSTM 仍然不太适应多线程 /GPU 计算,训练速度偏慢,不能充分利用GPU的并行计算优势。因此,我们还在尝试 SRU 等RNN 加速方案,希望在模型效果损失不大的情况下,提高模型的训练速度。
我们当前采用的是Char-based model,Char-based model在一个优势在于利用词元(lemmas)和形态学信息(morphological information ),能更好的处理导流内容内部结构,如手机号的组成。另一方面,Word-based model 更多的利用词语信息,词语比字具有更高的抽象等级,通常正确率会更高。我们希望通过训练一个新的分词模型的方式,使得在保持处理导流内容内部结构的情况下,构建 Word-based model。
算法在社区氛围的应用(二):深度学习在不友善文本识别中的应用
此前,我们常常收到知友们的反馈说「好烦哦,TA 又不友善了」、「我要举报 XX,TA 在评论区又开始杠上了」、「这种辱骂他人的人,你们都不处理吗?」等等。今年年初,我们开始尝试用深度学习算法辅助审核人员处理不友善问题,经过近三个月的探索和尝试,目前该算法第一版已经上线,并且取得相对不错的效果。
知乎不友善文本识别应用的场景和策略
目前,瓦力识别不友善的算法已经应用在知友们的举报和社区实时产生的内容中。针对这两种内容的不同场景和特点,我们采用了不同的处理策略:
举报内容的处理策略
瓦力有效地提升了我们的举报处理效率和响应速度。目前,我们每天约收到知友们近 25,000 条举报。在这些举报中,大约有 7,000 条是关于不友善的内容。模型训练阶段,我们利用经过人工标注的举报内容进行模型训练。线上预测阶段,如果模型预测某条内容属于不友善的概率x 大于阈值 p_abuse,瓦力会在 0.3 秒间完成判断并直接删除,内容被处理后,知友们也会收到相应的私信通知;如果模型预测该内容属于非不友善类型的概率 x 大于阈值 p_friend,则认为该内容属于非不友善内容,那么该举报会被忽略;不满足以上条件的内容,我们会进行多次人工审核判断,人工审核后的判断标准也会用于下一轮模型的迭代和升级。
我们重视每一个举报,并根据举报内容增强瓦力可识别的范围和准确度,还会每日人工复核知友们的举报,针对可能存在不同处理意见的举报,会根据规范与实际的应用场景多次复审。在这里,我们也非常感谢知友们的每一次举报,感谢大家与我们一起并肩维护社区氛围,我们也正是在知友们的举报中逐渐形成统一的判断标准。
全量内容的处理策略
我们会对每天新产生的内容进行全量审核,每天可以实时拦截处理 3,000 条内容。在实际的操作过程中,我们发现全量内容有如下两个特点使其不能跟举报内容共用模型和策略:
不友善样本和非不友善样本分布非常不均衡;
词语分布和举报内容有区别。比如,举报内容中包含「SB、NC」之类的脏词的基本属于不友善类型;但是在全量内容中脏字、脏词可能出现在影视作品讨论、陈述自己的经历等场景等非不友善内容中,例如:坑到你头皮发麻。———《鲁班智商二百五》
由于数据不均衡、数据排查标注成本较高和上述数据的分布特点,全量内容模型要做到准确率 98% 以上非常困难,因此我们根据人工审核量,选择一个适宜的阈值,在保证每天召回量的基础上,维持召回内容的处理准确率到 80% 以上,并将召回的内容进行人工审核。
知乎社区不友善文本识别系统基本框架
目前,不友善内容处理系统架构如下图所示(以知友举报内容识别识别系统为例,全量内容识别系统与其类似)
▲不友善内容处理系统框图
我们在选择模型时在小批量数据集上对比了 lstm 模型、svm 和朴素贝叶斯模型,lstm 模型表现最好,因此我们的模型优化工作主要集中在深度学习模型上。
词向量
将词用「词向量」表示是深度学习模型处理 NLP 问题的关键一步,我们的系统中使用 Google 提出的 word2vec 词向量模型,训练数据采用来自知乎社区 300 多万条真实的提问、评论、回答数据,内容涉及娱乐、政治、新闻、科学等各个领域,词向量维度采用 128 维,训练模型窗口大小 5。
Word2vec 的原理和使用方法这里就不做过多介绍了,有兴趣的可以阅读文献[1],Python 版本实现的可以参考 gensim官方文档。
text-cnn
TextCNN 是利用卷积神经网络对文本进行分类的算法, 年由 Yoon Kim 提出(见参考[3])。 TextCNN 的结构比较简单,其模型的结构如下图:
▲ext-cnn 网络结构
Embedding Layer——该层将输入的自然语言编码成 distributed representation,我们的模型该层使用 word2vec 预先训练好的词向量,同时该层设置为 trainable。
Convolution Layer——这一层主要是通过卷积,提取文本的 n-gram 特征,输入文本通过 embedding layer 后,会转变成一个二维矩阵,假设文本的长度为 |T|,词向量的大小为 |d|,则该二维矩阵的大小为 |T|x|d|,接下的卷积工作就是对这一个 |T|x|d| 的二维矩阵进行的。卷积核的大小一般设为 n x |d|,n 是卷积核的长度,|d| 是卷积核的宽度(LP 中通常取词向量的维度)。我们的模型中 n 取 [2,3,4,5]4 个值,每个值固定取 128 个 filter。
Max Pooling Layer——最大池化层,对卷积后得到的若干个一维向量取最大值,然后拼接在一块,作为本层的输出值。如果卷积核的 size=2,3,4,5 每个 size 有 128 个 filter,则经过卷积层后会得到 4×128 个一维的向量,再经过 max-pooling 之后,会得到 4×128 个 scalar 值,拼接在一块,最终得到一个 512×1 的向量。max-pooling 层的意义在于对卷积提取的 n-gram 特征,提取激活程度最大的特征。
Fully-connected Layer——将 max-pooling layer 后再拼接一层,作为输出结果。实际中为了提高网络的学习能力,可以拼接多个全连接层。
Softmax——根据类别数目设定节点数,我们不友善文本识别项目是二分类问题,设置一个节点,激活函数选择 sigmoid。
Bi-LSTM
CNN 最大问题是固定 filter_size 的视野,无法建模更长的序列信息,自然语言处理中更常用的是 RNN,因为 RNN 能够更好的表达上下文信息。在文本分类任务中,由于语句过长,会出现梯度消失或梯度爆炸的问题,基本的RNN网络难于处理语言中的长程依赖问题,为了解决这个问题,人们提出了 LSTM 模型。我们在对比 LSTM 和 Bi-LSTM 效果后选择了 Bi-LSTM,Bi-LSTM 从某种意义上可以理解为可以捕获变长且双向的「n-gram」信息。图 3 是 Bi-LSTM 用于分类问题的网络结构,黄色的节点分别是前向和后向RNN的输出,示例中的是利用最后一个词的结果直接接全连接层 softmax 输出了。我们的实际使用的网络结构在全联接层之后加了一层 dropout 层,dropout 输出接 sigmoid 二元分类层。RNN、LSTM、BI-LSTM 可以参考文献[4]。
▲用于文本分类的双向 LSTM 网络结构
我们参考论文 A C-LSTM Neural Network for Text Classification 来实现 CNN-LSTM 文本分类模型,模型的网络结构如下所示(该图直接来自论文)
▲CNN-ISTM 网络结构
效果
举报的内容
知友举报内容不管是直接删除还是直接忽略,都要求算法有较高的准确率(本文准确率没有特别说明的情况下指 precision),经过对比不同模型的评测效果,目前线上选用模型和准确率、召回率如下:
全量内容
经过模型调优和对比不同模型的评测效果,目前线上使用的模型和准确率、召回率如下:
后续工作
我们会利用更多数据模型,尝试更复杂定神经网络并进行语义分析。通过分析实际操作中的 bad case 可以发现,有些文本带有脏字、脏词但并不属于不友善类别,同时也存在一些很隐晦的不友善内容,主要有以下三类:
影视、社会热点事件讨论中,评价影视作品中的人物或者热点事件当事人,例如:这个视频难道不是货车司机的错,有时候又想去延安路龙翔桥附近斑马线前停一天车,MDZZ 规定。
陈述自己等经历,例如:我当时竟然信了,觉得当时的自己就是 FJ 。
诙谐词语或反语,例如:你真聪明,连这都不会;有哪些人工 ZZ 发明。
我们会继续尝试用深度学习做语义分析,对文本进行语义角色标注(见参考文献[6]),抽取句子的施事者(Agent)、受事者(Patient)、客体及其对应的描述词;通过语义相似度构建语义词典;将语义信息融合到我们的不友善文本识别模型中。
算法在社区氛围的应用(三): 机器学习在答非所问识别上的运用
跳绳的好处有哪些?可以锻炼哪些肌肉?
A:心肺功能比之前有提高。
B:有助于提高身体的乳酸阈值。
C:有助于提高身体的协调性。
D:谢谢,我去买了跳绳。
请问,以上哪个答案是答非所问?
现在,瓦力可直接识别并处理该题中的答非所问内容。
我们鼓励认真、专业的分享,期待每一次讨论都能碰撞出更多有价值的信息,并希望每一个用心的回答都能够得到好的展示,为他人带来更多帮助。但是,我们也发现在社区中出现了答非所问类的内容,影响知友们获取有价值内容的效率。
为了更好地识别答非所问类内容,我们采用了多种模型,包括传统的机器学习模型和比较新的深度学习模型。通过前期对语料的分析,我们发现语言用词、作者历史行为、知友对内容的反馈信息等都具有比较明显的区分度,因而我们尝试使用特征工程和传统机器学习方法实现了瓦力识别答非所问的第一版模型,并达到了一个相对不错的效果。
Random Forest
随机森林 (Random Forest) 是树模型里两个常用模型之一(另一个是 Gradient Boosting Decision Tree)。顾名思义,就是用随机的机制建立一个森林,森林由多棵分类树构成。当新样本进入时,我们需要将样本输入到每棵树中进行分类。打个形象的比喻,知乎森林召开议会,讨论@刘看山
到底是狗还是北极狐(看山,我知道你是北极狐的,手动捂脸逃…),森林中的每棵树都独立发表了自己对这个问题的观点,做出了自己的判断。最终刘看山是狗还是北极狐,要依据投票情况来确定,获得票数最多的类别就是这片森林对其的分类结果。如同图一所示意境。
▲森林会议
样本
通过训练语料和业务数据,进行特征工程,提取出了以下三类特征:
回答和问题的文本特征:如二者的词向量、词向量相似度、关键词相似度、话题相似度等;
回答的统计特征:如回答的赞同、反对、评论、举报等是用户对其的交互特征;
回答作者的统计特征:正向行为,如关注、回答、提问、评论、举报等,负向行为,如回答被赞同、被反对、被感谢、被举报等。
同时,通过历史积累、用户标注、策略生成产生出了训练样本集,然后用以上特征类别表示出每条样本。
分类树
使用随机有放回抽样选取每棵树的训练样本,随机选取 m 个特征 (m < 总特征数) 进行无限分裂生长,成长为能独立决策的树。
投票决策
通过建好的多棵分类树,对新的样本进行决策投票,获得最终的分类结果。
对于 Random Forest 的实现,有很多优秀开源的实现,在实际中我们封装了 Spark 中的 Random Forest 完成了模型的迭代。最终取得了 Precision 97%,Recall 58% 左右的不错结果。
细心的知友可能注意到了,我们的特征里有一类特征是与时间和回答的暴光有关的,即回答和作者的统计特征。为此我们在现有模型的基础上分析了这类特征的时间累积效果,如图二所示。从图中可以看到,经过一天的统计特征累积,Precision 达到了 90%,但 Recall 只有 40%,可以说这一天时间对于 40% 的答非所问有了比较充分的特征积累以支撑对其的准确判断。而随时间的增加,基本上 Precision 和 Recall 都有提升。但并不是时间越长,提升越多。
最终我们结合产品应用层面和算法阈值,分别选出两个时间点,一方面牺牲 Recall 快速识别处理一部分答非所问的回答,另一方面允许一定的处理延时,保证了大量的 Recall,大大净化了回答区域的无关内容。
▲统计特征累积周期(天)对 Precision 和 Recall 的影响
传统机器学习的一个核心内容就是特征工程,包括特征提取、特征选择等。
·特征提取:从原始数据出发构造出特征,通常包括业务和对语料的统计分析。
·特征选择:从提取出的候选特征中挑选出有用的特征。
但特征工程总是会耗费比较多的时间,而且在答非所问的识别中一些时间相关的特征,还延长了处理周期,不利于快速处理。而广为流传的深度学习,能自动对输入的低阶特征进行组合、变换,映射到高阶的特征,这也促使我们转向深度学习进行答非所问的识别。
深度学习兴起于图像识别,其过程可以引用图三[1] 大致描绘,输入特征,经隐藏层逐层抽象、组合,最后经输出层得出识别结果。
▲深度学习示意
相较于图片天然的像素表征,可以直接输入到深度神经网络里,文本需要进行向量化后方可作为网络的输入。关于「词向量化」的精彩描述可以参考[2]。此处我们抽取了知乎社区 1000 多万真实的文本信息,包括问题、回答、文章、评论等数据,利用 Facebook 开源的 FastText 训练了 256 维的词向量和字向量。对于 FastText 的原理和用法此处不作详细阐述,感兴趣的朋友可以参考[3]。
▲CNN 网络 (Convolutional Neural Network)
我们模型的网络结构基本上采用了 Severyn[4] 提出的网络结构,但在一些细节上做了些改动,比如图四中的 CNN-answer/question, 我们结合了 Wide & Deep[5] 的思想,以提取更为丰富的语义信息。
▲模型结构
Embedding Layer——该层利用预训练好的 FastText 词向量将原始词序列表达成词向量序列
Convolutional Layer——此层主要通过卷积操作,同时捕获类似于 N-Gram 特征(但同 N-Gram 还是有差异的)。我们的模型选取了 [2, 3, 4, 5, 6] 5 个卷积核宽度,为每个卷积核配置了 64 个 filter
Pooling Layer——池化层,对经过卷积操作提取的特征进行 Sampling。此处我们采用了 K-Max-Average pooling,对卷积层提取的特征,选择出激活程度 top-K 的特征值的平均值作为 pooling 的结果
Feature Join & Fully connected Layer——将前述几层获得的特征,以及额外信息进行融合,作为最终的特征输出,以便于最后的决策判断。实际上,我们在 Fully Connection 后面加了 3 层 Dense Layer,以提高网络的表达能力。
Softmax——将最后的特征转换成二分类决策概率。
最终训练好该模型,在验证集上达到了 Precision 78%, Recall 80% 的效果。Recall 虽有比较大的提升,但 Precision 并没有前文描述的 Random Forest 的方法好。
效果
目前,答非所问几个模型都上线到了知乎产品的诸多场景下,如反对、举报、专项清理等。每天清理约 5000 条新产生的「答非所问」内容,以及此前现存的 115 万条「答非所问」内容。