利用函数计算实现网络游戏或视频直播中的敏感词检测

一、概述

目前,网络游戏和视频直播都是很火爆的产业,然而,无论在多人在线网络游戏(MMORPG)中,还是在游戏视频直播或者其他直播中(弹幕),玩家或者用户的在线交流和互动都是它们的核心乐趣所在,但是玩家之间的交流和互动不可避免地会涉及到一些敏感词,对玩家或者用户交流中出现的敏感词进行过滤,提供一个健康和谐的网络环境是非常重要有意义的。

二、现有技术方案

现有技术中,通常采用两种方式进行敏感词的过滤:

第一种方式:客户端完成敏感词的过滤

客户端存储有敏感词库;由玩家输入的文本内容经过客户端敏感词过滤模块过滤,然后将过滤后的文本由客户端发送至服务器,再由服务器转发至接收方客户端显示,有时为了担心发送端的过滤模块被hacker绕过,需要在接收方在接收的时候,也经过下客户端敏感词过滤模块。

  • 优势:
    每个客户端单独执行敏感词过滤,效率高,且不占用服务器资源,服务器没有计算消耗。
  • 劣势:
    敏感词具有时效性,针对最新出现的敏感词,不能实时的增加到客户端的敏感词库,只能等下次客户端打新包或者patch才可以添加进去,这个时间成本有点大。
第二种方式:服务器完成敏感词的过滤

在服务器中存储敏感词库,并设置敏感词过滤模块,将服务器接收到的文本内容进行敏感词过滤,并将执行完敏感词过滤后的文本内容发送至客户端。

  • 优势:
    在于可实时增加敏感词,当出现突发事件后,不需要停服就可实现动态添加新的敏感词。
  • 劣势:由服务器执行敏感词过滤会导致增加大量额外计算消耗,影响服务器性能。有的特殊的输入可能会导致执行某些检测规则时间过长造成服务器进程卡顿。

三、函数计算可以提供的解决方案

将敏感词检测的模块放在函数计算实现,至于是由客户端和服务端发起函数调用看具体需求,在本示例中,我们把发起的敏感词检测过程放在客户端发起,如下图所示:

优势:不会增加服务器的计算消耗,同时只需要更新下函数,就可以达到实时更新敏感词目的。

具体步骤

1 . 函数计算作为 API 网关后端服务, 具体的教程可以参考官方教程和函数计算获取临时token
2 . 函数计算实现敏感词检测的功能,本文主要讲解这个过程, 以python作为编程语言,给出具体的示例代码。

在本教程中,选用正则式作为我们的敏感词检测,也就是python的re模块,但是做敏感词检查还存在一些性能问题需要我们去解决,问题是:当游戏或者视频直播上线的时候,敏感词正则库一般有几百个,在python中通过 re.compile 编译正则表达式是很耗时的,几百个正则编译完可能需要10几秒甚至20多秒,但是如果正则式如果是编译完的,那么直接进行匹配match或者search还是很快的,十几毫秒可以搞定。

初始方案:我们可以将正则表达式的编译结果 cPickle.dumps 序列化保存到文件中。待下次程序启动时直接从文件读取内容, cPickle.loads 反序列化成正则表达式对象。但是这个有坑,根本没有耗时改善,详情见cPickle正则表达式对象

最终方案:基于cPickle正则表达式对象的处理方案,对于最耗时的结果压缩再序列化,反之,反序列的时候记得再解压一下就行,这样的好处是,中间的序列化文件大小可以大大压缩,本教程测试一般显示能压缩5倍以上,但是对整个运行时间影响不是很大,如果对时间特别敏感的,可以不考虑压缩过程。

核心处理代码文件re_pickple.py

# -*- coding:utf-8 -*-
'''
_sre.SRE_Pattern 对象的序列化,就是编译的函数 re._compile 和输入的参数 pattern ,flags 给保存起来,
反序列化的时候 _compile(pattern, flags) ,这和直接 re.compile 没有有什么区别,赤裸裸的伪序列化
这里将re模块中的compile过程分成两个部分,dump最耗时间的code,然后load出code,再快速转换成 _sre.SRE_Pattern 对象
'''
import cPickle, re, sre_compile, sre_parse, _sre
import zlib,time

# 开启压缩的开关
zip_flag = False

# 目前函数计算的ca环境没有内置builtins
# 需要先在本地pip install future
if zip_flag:
  from builtins import int

'''
raw_compile, build_compiled是re模块中的compile拆分成两个部分
'''
# the first half of sre_compile.compile
def raw_compile(p, flags=0):
  # internal: convert pattern list to internal format

  if sre_compile.isstring(p):
      pattern = p
      p = sre_parse.parse(p, flags)
  else:
      pattern = None
  # 主要耗时在_code函数
  code = sre_compile._code(p, flags)

  if zip_flag:
      code = zlib.compress(str(code)) # code格式简单,元素都是整型的list

  return code

# the second half of sre_compile.compile
def build_compiled(p, flags, code):
  # print code
  # 重新计算p, parse函数耗时很少,如果将p dump,占用空间较大
  if sre_compile.isstring(p):
      pattern = p
      p = sre_parse.parse(p, flags)
  else:
      pattern = None

  # XXX: <fl> get rid of this limitation!
  if p.pattern.groups > 100:
      raise AssertionError(
          "sorry, but this version only supports 100 named groups"
          )

  # map in either direction
  groupindex = p.pattern.groupdict
  indexgroup = [None] * p.pattern.groups
  for k, i in groupindex.items():
      indexgroup[i] = k

  return _sre.compile(
      pattern, flags | p.pattern.flags, code,
      p.pattern.groups-1,
      groupindex, indexgroup
      )

def dump(regexes, o_file):
  picklable = []
  for r in regexes:
      code = raw_compile(r, 0)
      picklable.append((r,code))
  cPickle.dump(picklable,o_file)

def load(pkl):
  regexes = []
  for r, code in cPickle.load(pkl):
      if zip_flag:
          code= decode_str(code)
      regexes.append(build_compiled(r, 0, code))
  return regexes

def decode_str(code_str): #解压缩
  raw_code = zlib.decompress(code_str)
  raw_code.strip()
  raw_code = raw_code[1:-1]
  str_li = raw_code.split(',')
  code = [int(item.strip()) for item in str_li]
  return code

假设我们有一个检测昵称的正则字库,一个有150条正则规则,部分内容如下:

# -*- coding: utf-8 -*-
data = [
'(([工共龚供供拱恭]|([GgGg][OoOo][NnNn][GgGg]))[^一-龥]{0,6}?([產产厂]|([CcCc][HhHh][AaAa][NnNn]))[^一-龥]{0,6}?([党黨]|([DdDd][AaAa][NnNn][GgGg])))|胡.??[锦錦].??[濤涛]|毛[^一-龥]{0,6}?[澤泽][^一-龥]{0,6}?[东東]|中[^一-龥]{0,6}?[國国]|中[^一-龥]{0,6}?共|政[^一-龥]{0,6}?府|[習习刁][^一-龥]*?[近远尽進][^一-龥]*?[瓶评坪平]|李[^一-龥]*?克[^一-龥]*?[強强]|张[^一-龥]*?德[^一-龥]*?江|俞[^一-龥]*?正[^一-龥]*?声|刘[^一-龥]*?云[^一-龥]*?山|王[^一-龥]*?岐[^一-龥]*?山|张[^一-龥]*?高[^一-龥]*?丽|彭[^一-龥]*?丽[^一-龥]*?媛|汪[^一-龥]*?洋|([工共龚供供拱恭]|([GgGg][OoOo][NnNn][GgGg]))[^一-龥]*?匪',
'(?i)公会(强势)??入驻|招募.{0,3}?玩家|强势入驻|进群有??福利|体验号|交流群|群[^a-zA-Z一-龥0-9_丷灬一丨丿乀乄丶]*?号码|活跃工会|[qq][^a-zA-Z一-龥0-9_丷灬一丨丿乀乄丶]*?群|公会福利|福利[暴超]多|激活码|序列号|公会号|[百白]度搜|免费发放|发放免费|工会群|发放礼包|礼包发放|各种礼包|免费礼包|礼包免费|加入.{0,3}?公会',
'([\dⅠⅡⅢⅣⅤⅥⅧⅦⅨⅩⅪⅫ⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛㈠㈡㈢㈤㈣㈥㈦㈧㈨㈩①②③④⑤⑥⑦⑧⑨⑩⑴⑵⑷⑶⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇1234567890一二三四五六七八九十00零oOOo○〇0oOo0Oāóòáō零][^A-Za-z0-9_一-龥]{0,6}?){5,}?',
......
]

2.1 我们对其中间最耗时的结果进行序列化, 序列化结果保存在name_prog.pkl文件中:

  # -*- coding:utf-8 -*-
  import server_nick_name
  import re, time, re_pickle
  import cPickle as pickle

  with open(u'name_prog.pkl', 'wb') as f:
      nick_name_regx = []
      for pattern in server_nick_name.data:
          pattern = unicode(pattern,"utf8")
          nick_name_regx.append( pattern )

      re_pickle.dump(nick_name_regx, f)

  print "done"

2.2 敏感词检测demo

  # -*- coding:utf-8 -*-
import time, re_pickle

start = time.time()
with open(u'name_prog.pkl', 'r') as f:
    nick_name_prog = re_pickle.load(f)

end = time.time()
print "load time = ", end - start

def check_nick_name_valid(nick_name):
    nick_name = unicode(nick_name,"utf8")
    for prog in nick_name_prog:
        if prog.search(nick_name):
            return False
    return True

if __name__ == '__main__':
    inputlst = ["李四", "毛泽东", "习近平", "张三"]
    for input in inputlst:
        start = time.time()
        ret = check_nick_name_valid(input)
        end = time.time()
        print 'check ret = {} ; check_time = {}'.format(ret,end - start)

输出如下:

load time =  0.238202095032
check ret = True ; check_time = 0.000257015228271
check ret = False ; check_time = 8.10623168945e-06
check ret = False ; check_time = 4.05311584473e-06
check ret = True ; check_time = 0.000158071517944

因此,只要我们将name_prog.pkl和我们的检测函数、re_pickple.py一起,构建我们的函数,假设我们的函数如下,一次性检查4个名字,我们测试下调用100消耗时间的情况。

# -*- coding:utf-8 -*-
import time, re_pickle
begin = time.time()
with open(u'name_prog.pkl', 'r') as f:
    nick_name_prog = re_pickle.load(f)

end = time.time()
print "load time = ", end - begin

def check_nick_name_valid(nick_name):
    nick_name = unicode(nick_name,"utf8")
    for prog in nick_name_prog:
        if prog.search(nick_name):
            return False
    return True

def handler(event, context):
    inputlst = ["李四", "毛泽东", "习近平", "张三"]
    for input in inputlst:
        ret = check_nick_name_valid(input)
    return time.time() - begin

调用100次所消耗的时间情况如下:

avg: 0.000343136787415
min: 0.000230073928833
max: 0.000503063201904

总结

采用函数计算实现自定义的敏感词检测模块方案是一个很好的选择,自己预编译的的正则序列化中间结果除了第一次load大约是100来毫秒以外,其他的检测结果基本毫秒级就能给出结果,而且函数计算能做到自动按流量来scale,即使出现大量的文字交流,也能快速及时处理,很火的视频直播再也不怕海量弹幕了。同时,进一步扩展,针对网络游戏中出现的语音信息的翻译成文字,文字敏感性检测都可以通过函数计算来实现。

时间: 2024-10-21 20:55:46

利用函数计算实现网络游戏或视频直播中的敏感词检测的相关文章

不仅要“打假”,Facebook 还要治理视频直播中的裸露、暴力内容

据 VentureBeat 报道,12 月 1 日,Facebook 应用机器学习部门主管 Joaquin Candela 表示,Facebook 正在开发一项新的系统工具,可以在直播视频中对令人反感的违规内容进行自动标记,同时会继续利用人工智能技术来加强对视频直播内容的监控.目前,这一自动标记系统工具已经开始在 Facebook 视频直播产品 Facebook Live 上进行测试. 9 月初,Facebook 曾以不能显示裸体为由"封杀"了一张著名越南战争照片"战火中的女

如何利用函数计算获取临时token访问其他阿里云资源

对于一些用户来说,可能需要在网页,app等客户端可以操作阿里云资源,如上传文件到oss,但是用户又不想把自己的AK暴露出来,在本文中,实现用户不需要自己直接使用自己的AK来实现文件上传到oss的例子 主要步骤如下: 1,用户创建一个函数 get_sts,该函数如下: # -*- coding: utf-8 -*- def handler(event, context): response = { 'isBase64Encoded': False, 'statusCode': 200, 'head

【腾讯bugly干货分享】HTML 5 视频直播一站式扫盲

本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1277 视频直播这么火,再不学就 out 了. 为了紧跟潮流,本文将向大家介绍一下视频直播中的基本流程和主要的技术点,包括但不限于前端技术. 1. H5 到底能不能做视频直播? 当然可以, H5 火了这么久,涵盖了各个方面的技术. 对于视频录制,可以使用强大的 webRTC(Web Real-Time Communica

开展视频直播业务的最佳实践【Tech Insight演讲实录】

现场精彩干货: 直播,原先是广电系统的一个概念,十几年前我们印象中的直播还是电视台开一台直播车奔赴抗洪前线,而现在提到直播,我们想到的便成了网络直播. 刚刚过去的2016年可谓是"直播年".2016年,网络直播用户的规模已经达到了3.25亿,占网民总数的45.8%.直播市场规模(PC+移动)达到150 亿元,直播平台数量目前已经超过200家. 8月10日晚,"行走的表情包"傅园慧在某直播平台进行直播首秀,直播的内容就是和大家聊聊天,共有1054万人在线观看了直播,此

如何使用函数计算实现流式处理large文件

在某些场景中,用户需要对oss上的某个大文件抽取出某些信息, 本教程以oss上的一个大小为400M,内含105万条记录的csv文件为例,讲解如果利用函数计算流式处理, 查找出包含'Lilly'的记录. 一,单线程分块读取处理 # coding=utf-8 import re import json import oss2 import logging import time import csv CHUNK_SIZE = 1024*8 # 函数服务主函数 def handler(event, c

搜狐全免费打破僵局 门户网站的斗法视频直播

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 互联网带宽的不断增加,为网民在网上观看各种赛事提供了保障,视频直播市场由此催生而出.随着新赛季欧洲足球联赛大幕的拉开,旨在扩大用户数量的各大门户网站在视频直播业务上开始了新一轮斗法. 与目前业内新媒体直播欧洲赛事实行收费模式不同,搜狐在新赛季足球联赛的视频直播上依旧对网民免费.记者近日从搜狐获悉,继搜狐体育在06-07赛季对西甲进行了38轮的

为了不在直播中看到少儿不宜的景象,阿里做了这些

       直播作为近来新兴的互动形态和今年阿里巴巴双十一的一大亮点,其内容风险监控是一个全新的课题,技术的挑战非常大,管控难点主要包括业界缺乏成熟方案和标准.主播行为.直播内容不可控.峰值期间数千路高并发处理.对算法的高实时响应要求等等. 阿里巴巴集团安全部今年在直播管控中的特色在于大量采用人工智能和深度学习等技术,配合优化后的高性能多媒体计算集群,大幅度降低人工审核成本的同时,提升了对内容风险的防控能力.系统在峰值期间成功处理5400路直播视频,以及共计25万场粉丝连连看游戏,对违规内容进

【直播回顾】阿里云技术系列直播:异构计算、函数计算、自研数据库…

各位小伙伴,阿里云将在10月份杭州云栖大会发布系列新品,为了让大家更好地了解本次系列新品,我们组织了10场技术直播,分享各自技术实战. 下面是议程及嘉宾议题详情. 本次活动已结束,回顾正在生成中,敬请期待! 直播议程 时间 直播议题 14:00-14:40 云数据库POLARDB,时代从此划分 ECS最优性价比实例解析 14:40-15:20 移动云,一站式移动研发解决方案 阿里云异构计算产品家族 15:20-16:00 通过MaxCompute Studio实践大数据时代的DevOps 函数计

利用PHP函数计算中英文字符串长度的方法_php技巧

本文实例讲述了利用PHP函数计算中英文字符串长度的方法.分享给大家供大家参考.具体实现方法如下: 一般来说大家知道英文字符占一个字节,而中文字符gbk占两个字符,utf8占三个字符,很多人印象中php计算字符串长度就是strlen()函数,其实不然,它计算的是字节的长度而非字符的长度,那么如何获取一个字符串中字符的长度呢?还有有mb_strlen(). 具体代码如下: 复制代码 代码如下: echo $str = 'PHP点点通';  echo strlen($str); //3*1+3*3=1