基于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
因此,我们需要对数据集做一些预处理,统一格式后扔给机器训练模型。
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
这样机器就知道中共中央
、总书记
、、
是一个词了。
B
表示一个词的开头、I
表示一个词的中间、O
表示这个字不属于任何一块。中 共 中 央 总 书 记 、 国 家 主 席 江 泽 民
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。
中 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
国家
的国是这个词的开头,所以是B,因为是名词,所以是N,因此标识符就是B-N。模型框架
深度学习用到的框架有很多,这里就用transformers在tensorflow上搭建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文件。里面其实就是一个循环,用户输入句子,程序输出对应的词性。以下列出一些测试结果:
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
新闻类测试:
当我整理这份分享教程时,我随便拿当天新闻做测试,并不是拿人民日报的内容:
- 武汉动物园有序开放。
- ['武汉动物园/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']
- ['武汉动物园/B-NS', '有序/B-AD', '开放/B-V', '。/O-W']
- :['如今/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']
- ['本体/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']
- ['据说/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就稍微有些尴尬了,比如银火
是个名词,这里程序预判是银
名词、火
名词😅。不过像真属会
、属解
、凯罗弓
倒没预判错误😏。
银火
是个名词,这里程序预判是银
名词、火
名词😅。不过像真属会
、属解
、凯罗弓
倒没预判错误😏。