文章目录
1. 写在最前面
真的是心力交瘁的一个月,最近在做「敏感词过滤」的功能:
- 需要识别敏感的英语单词
- 需要识别敏感的英语词组
虽然笔者在需求评审会议上极力说明,这个需求的复杂程度比较高,自己实现有种种风险,但是无奈人家人多,被产品和负责人怼回来了。
行吧,做就做呗。不过这让我想起来一句话,「人有多大胆,地有多大产」。
2. 算法调研
2.1 google 大法好
本着不懂就查的思路,笔者从 google 处得到了一丝丝思路:
2.2 github 一下
接着这个思路,笔者从一众开源算法中,挑选到了
注:其实还尝试了 godlp,但这个更倾向于一个根据一系列规则对敏感数据的识别和处置方案,不大适用本场景。
3. 上手
3.1 不如试试
本着 「talk is cheap show me the code」的原则,笔者就这两个库,是否能够满足,产品的需求做了一个简单的 POC 尝试,代码如下:
package main
import (
"bytes"
"fmt"
"io/ioutil"
filter "github.com/antlinker/go-dirtyfilter"
"github.com/importcjj/sensitive"
)
func FilterDirtyFilter(path, content string, isReplace bool) ([]string, error) {
fd, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
df := filter.NewNodeReaderFilter(bytes.NewReader(fd), '\n')
var (
result = make([]string, 0)
rs string
)
if isReplace {
rs, err = df.Replace(content, '*')
result = append(result, rs)
} else {
result, err = df.Filter(content)
}
if err != nil {
return nil, err
}
return result, nil
}
func FilterSensitive(path, content string, isReplace bool) ([]string, error) {
filter := sensitive.New()
err := filter.LoadWordDict(path)
if err != nil {
return nil, err
}
if isReplace {
s := filter.Replace(content, '*')
return []string{s}, nil
}
s := filter.FindAll(content)
return s, nil
}
func TestCNFilter() {
c := "小明抓到了一只王八"
fmt.Printf("原始词 %s - 脏词 %s\n", c, "王八")
sr, err := FilterSensitive("./filter-cn.txt", c, true)
if err != nil {
panic(err)
}
dr, err := FilterDirtyFilter("./filter-cn.txt", c, true)
if err != nil {
panic(err)
}
fmt.Println("sensitive", sr)
fmt.Println("dirty", dr)
}
func TestENFilter() {
c := "fucked"
fmt.Printf("原始词 %s - 脏词 %s\n", c, "fuck")
sr, err := FilterSensitive("./filter-en.txt", c, true)
if err != nil {
panic(err)
}
dr, err := FilterDirtyFilter("./filter-en.txt", c, true)
if err != nil {
panic(err)
}
fmt.Println("sensitive", sr)
fmt.Println("dirty", dr)
}
func main() {
TestCNFilter()
fmt.Println()
TestENFilter()
}
注:./filter-en.txt 为与 mian.go 同级的一个文件,包含「fuck」 字符
./filter-cn.txt 为与 main.go 同级的一个文件,包含 「王八」 字符
3.2 求甚解
上述两个库都是 DFA 算法的一种实现,实现的时候使用 Trie Tree 这种数据结构。这篇文章主要介绍选项的思路,后续有空会整理一篇 DFA 跟 Trie Tree 相关的文章。
注:先记在这里,求不打自己的脸。
4. 遇到的问题
「出来混,总是会遇到各种各样的问题」
4.1 英文词组可拆为字母
英文单词由单词组成,假设敏感词的词库里有 fuck 这个单词,所有带有 fuck 的词组,下图两个 golang 库测试例子:
4.2 误识别的问题
开源版本的算法,都是识别部分,不支持对上下文及情绪进行判断,会产生较高的误识别率,下图两个 golang 库测试例子:
注:做人啊,不能要求太高,连情绪啥的都开源了,人家做这个的公司不得黄了
5. 碎碎念
以上就是这个月糟心的开始,毕竟猜测没准产品他们后面就想着要不自己实现一个好了……,但是做人还是要乐观、坚持、努力吖:
- 相逢的意义在于照亮彼此,不然的话 一个人喝茶也很浪漫,一个人吹风也能清醒。
- 为什么要蜷缩在黑暗里,仅有一次的人生当然要活的炽热。
- 在你的成长过程中,你会渐渐发现世界是以这样一种方式在运转着,人们会告诉你世事险恶,你要遵规守纪,不要做一些太出格的事。这是非常局限的生活。当你发现这样一个简单的事实,生命会无限扩展——你身边一切被你称之为生活的事物,都是一些不如你聪慧的人创造的。而你可以影响它们,你可以改变这一切,你可以建造出给别人带来福祉的事物,上面有着只属于你的印记。一旦你学会这一点,你将卓越不凡。