低素质弹幕分类器的CNN实现

2017/2/6 14:48 下午 posted in  Deep Learning

整体架构

对于一条弹幕,首先进行分词,然后通过 word2vec 转换为词向量,再填充至固定长度,作为卷积神经网络的输入。

卷积神经网络的结构如下:

model = Sequential()
model.add(Convolution1D(100, 4, border_mode='valid', input_shape=(100, word_model.vector_size)))
model.add(Activation('relu'))
model.add(Convolution1D(100, 4, border_mode='valid', input_shape=(100, word_model.vector_size)))
model.add(Activation('relu'))
model.add(Flatten()) 
model.add(Dense(100, activation='relu'))
model.add(Dense(2, activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy']
             )

最终输出为2位的 categorical result,直接使用第一项,即骂人弹幕的概率作为输出。

然后通过代理,在弹幕服务器与播放器之间插入一层,实现弹幕的分类与屏蔽。最终实现了有效的骂人弹幕自动屏蔽,但是误伤的情况依然存在。

搭建过程

使用游戏区的所有弹幕来训练 word2vec model。这里我是用的是 word2vec 的 Python 实现 gensim

训练脚本来自这篇文章 中英文维基百科语料上的Word2Vec实验

# -*- coding: utf-8 -*-

"""
build a word2vec model by text file, each sentence for a line.
usage: [input file] [gensim model filename] [word2vec model filename]
example: output.txt life_damku.model life_damku.vector

"""

import logging
import os.path
import sys
import multiprocessing

from gensim.models import Word2Vec
from gensim.models.word2vec import LineSentence

if __name__ == '__main__':
    program = os.path.basename(sys.argv[0])
    logger = logging.getLogger(program)

    logging.basicConfig(format='%(asctime)s: %(levelname)s: %(message)s')
    logging.root.setLevel(level=logging.INFO)
    logger.info("running %s" % ' '.join(sys.argv))

    # check and process input arguments
    if len(sys.argv) < 4:
        print(globals()['__doc__'] % locals())
        sys.exit(1)
    inp, outp1, outp2 = sys.argv[1:4]

    model = Word2Vec(LineSentence(inp), size=400, window=5, min_count=5,
                     workers=multiprocessing.cpu_count())

    # trim unneeded model memory = use(much) less RAM
    # model.init_sims(replace=True)
    model.save(outp1)
    model.save_word2vec_format(outp2, binary=False)

然后,我从所有弹幕中随机抽取了5000条,进行人工标注分类,其中有63条骂人弹幕。由于骂人弹幕太少,我又通过关键词搜索加人工筛选的方式,增加了4000条骂人弹幕。

以这约9000条弹幕作为训练样本,80%的弹幕作为 train set, 其余的20%作为 validation set

对训练样本进行预处理:

  1. 分词
  2. 转换为词向量
  3. 填充至100位长,其中填充的位的词向量全部置零。

开始构建卷积神经网络,我选用的框架是使用 TensorFlow 后端的 keras。最终经过调试,得到这样一个结构

具体的训练过程可以看这里

model = Sequential()
model.add(Convolution1D(100, 4, border_mode='valid', input_shape=(100, word_model.vector_size)))
model.add(Activation('relu'))
model.add(Convolution1D(100, 4, border_mode='valid', input_shape=(100, word_model.vector_size)))
model.add(Activation('relu'))
model.add(Flatten()) 
model.add(Dense(100, activation='relu'))
model.add(Dense(2, activation='softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy']
             )

需要注意的是,model.fit 指定了 class weight

model.fit(X_train, Y_train, nb_epoch=1, batch_size=300, class_weight={
              0: len(positive_sample)/len(negative_sample),
              1: 1
          })

单次训练迭代时间约25秒,我总共迭代了20次。其中每次迭代我都将 model 保存到一个列表中,并使用当前 model 对验证集进行测试,输出测试结果。

然后根据测试结果,选用了第3次迭代的 model ,尝试使用新的,不在样本中的视频弹幕进行人工检验识别效果。

选用视频 av8365806 里面存在大量对骂的弹幕,适合进行测试

这里我花了小半天时间,用 tornado 搭了个服务器,模拟 comment.bilibili.com 的所有请求,其中对于弹幕的请求,插入一层封装好的神经网络模型进行分类,再返回给用户请求,其余请求直接转发。搭建好后通过 Surge 的 URL Rewrite 将请求转到本地服务器上。

屏幕快照 2017-02-06 13.24.12

然后此时访问弹幕都会先经过本地服务器进行处理了。查看 av8365806 的弹幕分类情况:

首先是比较欣慰的结果,大部分直接用脏字喷人的弹幕都被高确信地识别出来了
屏幕快照 2017-02-06 13.26.37

其次,存在一些短弹幕被误伤的情况,而且确信度还莫名的挺高
屏幕快照 2017-02-06 13.26.51

然后,依然存在一部分弹幕脏字不明显,以及一些反讽的语句没有识别出来
屏幕快照 2017-02-06 13.27.39

另外由于3000弹幕的识别时间在5秒左右,再加上网络延迟以及一些预处理后处理的时间,整体延迟约10秒,所以这个代理服务器可能还没能达到投入大规模使用的效率,比较可惜。

但是其识别效果,对于日常观感绝对是有所提升的。

脑海中对于屏蔽模式可以分为以下三挡:

  1. 【仁慈模式】被分类器识别为骂人的弹幕,弹幕内容前填充100个空格,颜色变为白色,字号变小,统一为滚动弹幕。其中,填充空格能够使得这条弹幕将会以极快的速度飘过视野。
  2. 【常规模式】被分类器识别为骂人的弹幕直接删除,该弹幕的发送者的其他弹幕使用仁慈模式进行修正。
  3. 【灭杀模式】被分类器识别为骂人的弹幕,以及其发送者发送的其他弹幕,统一删除。

原本对于仁慈模式是想进行高透明度处理的,但是由于B站弹幕不支持分开透明度所以就变成了现在的方案。不过由于可以直接修改弹幕类型,不知道高级弹幕是否支持透明度选项,以及高级弹幕在各个平台上的支持度如何。

结论

这次构建卷积神经网络,是学习深度学习方向以来第一次完全自己设计网络结构,并独立完成所有的过程,包括样本收集、样本处理、搭建网络、训练,并且实现了模型的真实可用。作为深度学习的阶段性成果,也是给了我很大的鼓励。这个假期接下来的时间,准备继续学习 RNN 以及 LSTM 等模型,以及其他优化技巧。