配置代码样式

Thursday, April 23, 2020

基于transformers、TensorFlow和Bert实现POS

基于transformers框架使用TensorFlow和Bert实现中文词性标注任务

上篇文章基于ALBERT实现中文NER讲述了如何使用https://github.com/google-research/albert/albert实现中文NER任务,这次使用一个awsome的框架(由huggingface开发的transformers)实现POS任务。
由于NER和POS都可以看作序列标注任务,所以实现思路上挺相似的。

声明

本文仅技术性分享,可以在google colab引入这份how_to_run.ipynb文件从零打造一款中文词性标注工具。里面包含文本数据的收集、数据预处理、模型训练到自定义句子测试等。对想熟悉深度学习的NLP新人应该是一份不错的入门实战经验。

数据搜集

本文使用1998人民日报数据集(仲多下载资源之一),请根据自身情况更换数据。使用其他领域的数据、甚至其他语言等数据都没问题的。

数据预处理

由于每个数据文本的格式各不相同,如该1998人民日报数据集每行开头都有一段明显不是人民日报原文的文字:
19980101-01-001-002/m  中共中央/nt  总书记/n  、/w  国家/n  主席/n  江/nr  泽民/nr  
19980101-01-001-003/m  (/w  一九九七年/t  十二月/t  三十一日/t  )/w  
因此,我们需要对数据集做一些预处理,统一格式后扔给机器训练模型。

词性符号

本人从网上整理了一些词性翻译表(不齐全),仅作为参考:
标签含义标签含义标签含义标签含义
n普通名词f方位名词s处所名词t时间
nr人名ns地名nt机构名nw作品名
nz其他专名v普通动词vd动副词vn名动词
a形容词ad副形词an名形词d副词
m数量词q量词r代词p介词
c连词u助词xc其他虚词w标点符号

统一数据集格式

该数据集虽然标识了词性,比如 中共中央/机构名, 总书记/名词,但机器自身并不知道一个句子,哪些字是一块的,所以人们定义了IOB标注模式(还有其他模式,如BMES等),标注句子中这个字是属于一个词的开头还是之中。
IOB,是inside,outside,begging缩写,其中B表示一个词的开头、I表示一个词的中间、O表示这个字不属于任何一块。
例如"中共中央总书记、国家主席江泽民",使用IOB标注后:
中 共 中 央 总 书 记 、 国 家 主 席 江 泽 民
B I I I B I I O B I B I O B I
这样机器就知道中共中央总书记是一个词了。

处理后的格式

我们最终的数据集格式是每行只包含一个字和对应的标识符(空格隔开),每个句子之间有一个空行区分:
中 O-NT
共 I-NT
中 I-NT
央 I-NT
总 B-N
书 I-N
记 I-N
、 O-W
国 B-N
家 I-N
主 B-N
席 I-N
江 O-NR
泽 B-NR
民 I-NR

( O-W
一 B-T
九 I-T
九 I-T
七 I-T
年 I-T
十 B-T
二 I-T
月 I-T
三 B-T
十 I-T
一 I-T
日 I-T
) O-W
这里每个字对应的标识符是BIO和词性的结合,如国家的国是这个词的开头,所以是B,因为是名词,所以是N,因此标识符就是B-N。

模型框架

深度学习用到的框架有很多,这里就用transformerstensorflow上搭建bert-pos模型吧。
代码已经上传到Github,欢迎各位吐槽。代码内容不多,熟悉TensorFlow的各位应该很快就知道运作原理了。不熟悉TensorFlow也没关系,官方已经提供足够的教程文档了,我就是从官方文档学习的。

测试结果

使用原模型参数训练1998人民日报语料库3个epochs,评测结果如下:
           precision    recall  f1-score   support

        N     0.9471    0.9542    0.9507     15824
        V     0.9287    0.9309    0.9298      9043
        A     0.9082    0.9228    0.9155      1684
       NR     0.9651    0.9677    0.9664      1486
        C     0.9002    0.9410    0.9202       441
       VN     0.8428    0.9038    0.8723      2735
        F     0.9423    0.9849    0.9631       398
        R     0.9821    0.9848    0.9834      1448
        B     0.8824    0.9071    0.8946       463
       NS     0.9296    0.9383    0.9339      2447
        J     0.7931    0.8125    0.8027       368
        L     0.4667    0.6609    0.5471       466
        T     0.9851    0.9874    0.9863      2145
       NT     0.9103    0.9511    0.9303      1003
        D     0.9246    0.9572    0.9406      1307
        M     0.9778    0.9857    0.9817      1609
        U     0.8108    1.0000    0.8955        30
        P     0.9031    0.8662    0.8843       269
        Q     0.9347    0.9841    0.9588       189
       NX     0.6757    0.9615    0.7937        26
       AD     0.9298    0.9636    0.9464       330
        Z     0.7532    0.8264    0.7881       144
       NZ     0.5942    0.6833    0.6357       180
        I     0.7780    0.8178    0.7974       450
       AN     0.8130    0.8288    0.8208       257
        S     0.8721    0.9091    0.8902       330
        W     0.8989    0.9302    0.9143        86
       VD     0.6471    0.6875    0.6667        32
        O     0.0000    0.0000    0.0000         7
        Y     0.4000    0.3333    0.3636         6

micro avg     0.9217    0.9395    0.9305     45203
macro avg     0.9243    0.9395    0.9315     45203
另外,我在github上提供了交互测试工具:terminal_predict.py注:运行该工具需要各位完成模型训练或email我获取相关pre-training model文件。里面其实就是一个循环,用户输入句子,程序输出对应的词性。以下列出一些测试结果:

新闻类测试:

当我整理这份分享教程时,我随便拿当天新闻做测试,并不是拿人民日报的内容:
  • 武汉动物园有序开放。
    • ['武汉动物园/B-NS', '有序/B-AD', '开放/B-V', '。/O-W']
  • 如今中国在建造隧道时遇到困难,三年时间只挖了4米,看到这里印度表示嘲笑,居然有中国完成不了的工程。
    • :['如今/B-T', '中国/B-NS', '在/O-P', '建造/B-V', '隧道/B-N', '时/O-NG', '遇到/B-V', '困难/B-AN', ',/O-W', '三/O-M', '年/O-Q', '时间/B-N', '只/O-D', '挖/O-V', '了/O-U', '4/O-M', '米/O-Q', ',/O-W', '看到/B-V', '这里/B-R', '印度/B-NS', '表示/B-V', '嘲笑/B-V', ',/O-W', '居然/B-D', '有/O-V', '中国/B-NS', '完成/B-V', '不/O-D', '了/O-V', '的/O-U', '工程/B-N', '。/O-W'] 
  • 最重要的是黄河这么多年来,水下的黄沙非常多,常年在水中,河床也非常松弛。
    • :['最/O-D', '重要/B-A', '的/O-U', '是/O-V', '黄河/B-NS', '这么/B-R', '多/O-M', '年/O-Q', '来/O-F', ',/O-W', '水下/B-S', '的/O-U', '黄沙/B-N', '非常/B-D', '多/O-A', ',/O-W', '常年/B-D', '在/O-P', '水中/B-S', ',/O-W', '河床/B-N', '也/O-D', '非常/B-D', '松弛/B-A', '。/O-W']

游戏类的评论测试:

随便从NGA进了一个讨论帖:
  • 本体没刷,今天刷了一天一把弓都炼不出来心态已经有点崩了,哪有那么大功夫在这抽卡,但是差距又这么明显,悲
    • ['本体/B-N', '没/O-D', '刷/O-V', ',/O-W', '今天/B-T', '刷/O-V', '了/O-U', '一/O-M', '天/O-Q', '一/O-M', '把/O-Q', '弓/O-N', '都/O-D', '炼/O-V', '不/O-D', '出来/B-V', '心态/B-N', '已经/B-D', '有点/B-D', '崩/O-V', '了/O-Y', ',/O-W', '哪/O-R', '有/O-V', '那么/B-R', '大/O-A', '功夫/B-N', '在/O-P', '这/O-R', '抽/O-V', '卡/O-N', ',/O-W', '但是/B-C', '差距/B-N', '又/O-D', '这么/B-R', '明显/B-A', ',/O-W', '悲/O-VG']
  • 我还做了一下真属会属解的凯罗弓,凯罗雷属解真属会也挺强的,就是配装很别扭
    • ['我/O-R', '还/O-D', '做/O-V', '了/O-U', '一下/B-M', '真属会/B-N', '属解/B-N', '的/O-U', '凯罗弓/B-N', ',/O-W', '凯/B-NR', '罗/I-N', '雷/I-NR', '属解/B-N', '真属会/B-N', '也/O-D', '挺/O-D', '强/O-A', '的/O-U', ',/O-W', '就是/B-D', '配装/B-V', '很/O-D', '别扭/B-A']
  • 据说刚射每一根差5点左右(帝王金各个属性有点差别),你等苍星应该也没用,苍星比现在银火多出的那些技能也拉不平那么多属性
    • ['据说/B-V', '刚/O-D', '射/O-V', '每/O-R', '一/O-M', '根/O-Q', '差/O-V', '5点/B-M', '左右/B-M', '(/O-W', '帝王金/B-N', '各个/B-R', '属性/B-N', '有/O-V', '点/O-Q', '差别/B-N', ')/O-W', ',/O-W', '你/O-R', '等/O-V', '苍星/B-N', '应该/B-V', '也/O-D', '没用/B-A', ',/O-W', '苍星/B-N', '比/O-P', '现在/B-T', '银/B-N', '火/O-N', '多/O-AD', '出/O-V', '的/O-U', '那些/B-R', '技能/B-N', '也/O-D', '拉/O-V', '不/O-D', '平/O-V', '那么/B-R', '多/O-A', '属性/B-N']

测试结果

本文重点是如何从零打造一个POS程序,从上面可以看出做对应领域的POS还是挺准的,但做其他领域的POS就稍微有些尴尬了,比如银火是个名词,这里程序预判是名词、名词😅。不过像真属会属解凯罗弓倒没预判错误😏。

参考资源

No comments:

Post a Comment