Fork me on GitHub

基于bert训练自己的分词系统

前言

在中文分词领域,已经有着很多优秀的工具,例如:

  • jieba分词
  • SnowNLP
  • 北京大学PKUse
  • 清华大学THULAC
  • HanLP
  • FoolNLTK
  • 哈工大LTP
  • 斯坦福分词器CoreNLP
  • BaiduLac

这里,我们不使用上述的工具,而是利用bert训练一个自己的分词器。

数据预处理

首先我们查看下初始的数据:data/sighan2005/raw_data/training.txt

1998年  ,  中国  人民  将  满怀信心  地  开创  新  的  业绩  。  尽管  我们  在  经济社会  发展  中  还  面临  不少  困难  ,  但  我们  有  *理论  的  指引  ,  有  改革  开放  近  20  年  来  取得  的  伟大  成就  和  积累  的  丰富  经验  ,  还有  其他  的  各种  有利  条件  ,  我们  一定  能够  克服  这些  困难  ,  继续  稳步前进  。  只要  我们  进一步  解放思想  ,  实事求是  ,  抓住  机遇  ,  开拓进取  ,  建设  有  中国  特色  社会主义  的  道路  就  会  越  走  越  宽广  。  
实现  祖国  的  完全  统一  ,  是  海内外  全体  中国  人  的  共同  心愿  。  通过  中  葡  双方  的  合作  和  努力  ,  按照  “  一国两制  ”  方针  和  澳门  《  基本法  》  ,  1999年  12月  澳门  的  回归  一定  能够  顺利  实现  。  
台湾  是  中国  领土  不可分割  的  一  部分  。  完成  祖国  统一  ,  是  大势所趋  ,  民心所向  。  任何  企图  制造  “  两  个  中国  ”  、  “  一中一台  ”  、  “  台湾  独立  ”  的  图谋  ,  都  注定  要  更  失败  。  希望  台湾  当局  以  民族  大义  为重  ,  拿  出  诚意  ,  采取  实际  的  行动  ,  推动  两岸  经济  文化  交流  和  人员  往来  ,  促进  两岸  直接  通邮  、  通航  、  通商  的  早日  实现  ,  并  尽早  回应  我们  发出  的  在  一个  中国  的  原则  下  两岸  进行  谈判  的  郑重  呼吁  。  
环顾  全球  ,  日益  密切  的  世界  经济  联系  ,  日新月异  的  科技  进步  ,  正在  为  各国  经济  的  发展  提供  历史  机遇  。  但是  ,  世界  还  不  安宁  。  南北  之间  的  贫富  差距  继续  扩大  ;  局部  冲突  时有发生  ;  不  公正  不  合理  的  旧  的  国际  政治经济  秩序  还  没有  根本  改变  ;  发展中国家  在  激烈  的  国际  经济  竞争  中  仍  处于  弱势  地位  ;  人类  的  生存  与  发展  还  面临  种种  威胁  和  挑战  。  和平  与  发展  的  前景  是  光明  的  ,  21  世纪  将  是  充满  希望  的  世纪  。  但  前进  的  道路  不  会  也  不  可能  一帆风顺  ,  关键  是  世界  各国  人民  要  进一步  团结  起来  ,  共同  推动  早日  建立  公正  合理  的  国际  政治经济  新  秩序  。  
中国  政府  将  继续  坚持  奉行  独立自主  的  和平  外交  政策  ,  在  和平共处  五  项  原则  的  基础  上  努力  发展  同  世界  各国  的  友好  关系  。  中国  愿意  加强  同  联合国  和  其他  国际  组织  的  协调  ,  促进  在  扩大  经贸  科技  交流  、  保护  环境  、  消除  贫困  、  打击  国际  犯罪  等  方面  的  国际  合作  。  中国  永远  是  维护  世界  和平  与  稳定  的  重要  力量  。  中国  人民  愿  与  世界  各国  人民  一道  ,  为  开创  持久  和平  、  共同  发展  的  新  世纪  而  不懈努力  !  
在  这  辞旧迎新  的  美好  时刻  ,  我  祝  大家  新年  快乐  ,  家庭  幸福  !  
谢谢  !  (  新华社  北京  12月  31日  电  )  
在  十五大  精神  指引  下  胜利  前进  ——  元旦  献辞  

这里面每一行是一段文本。一段文本中不同的词是以两个空格进行分隔的。接下来,我们要将数据处理成我们需要的样子。在data/sighan2005/raw_data/下新建一个process.py

import json
from pprint import pprint

max_seq_len= 512
# -1:索引为0-511,-2:去掉CLS和SEP,+1:词索引到下一位
max_seq_len = max_seq_len - 1 - 2 + 1

with open("training.txt", "r", encoding="utf-8") as fp:
    data = fp.readlines()

res = []
i = 0
for d in data:
    d = d.strip().split("  ")
    dtype = "word"
    start = 0
    tmp = []
    labels = []
    j = 0
    for word in d:
        start = len("".join(tmp))
        tmp.append(word)
        end = start + len(word)
        labels.append(["T{}".format(j), dtype, start, end, word])
        j += 1
        if end > max_seq_len:
            sub_tmp = tmp[:-1]
            sub_labels = labels[:-1]
            end = start + len("".join(sub_tmp))
            text = "".join(sub_tmp)
            res.append({
                "id": i,
                "text": text,
                "labels": sub_labels
            })

            start = 0
            tmp = [word]
            end = len("".join(tmp))
            labels = [["T{}".format(0), dtype, 0, end, word]]
            i += 1

    if tmp:
        text = "".join(tmp)
        res.append({
            "id": i,
            "text": text,
            "labels": labels
        })
        i += 1

with open("../mid_data/train.json", 'w', encoding="utf-8") as fp:
    json.dump(res, fp, ensure_ascii=False)

labels = ["word"]
with open("../mid_data/labels.json", 'w', encoding="utf-8") as fp:
    json.dump(labels, fp, ensure_ascii=False)

nor_ent2id = {"O":0, "B-word":1, "I-word":2, "E-word":3, "S-word":4}
with open("../mid_data/nor_ent2id.json", 'w', encoding="utf-8") as fp:
    json.dump(nor_ent2id, fp, ensure_ascii=False)

with open("test.txt", "r", encoding="utf-8") as fp:
    data = fp.readlines()

res = []
i = 0
for d in data:
    d = d.strip().split("  ")
    dtype = "word"
    start = 0
    tmp = []
    labels = []
    j = 0
    for word in d:
        start = len("".join(tmp))
        tmp.append(word)
        end = start + len(word)
        labels.append(["T{}".format(j), dtype, start, end, word])
        j += 1
        if end > max_seq_len:
            sub_tmp = tmp[:-1]
            sub_labels = labels[:-1]
            end = start + len("".join(sub_tmp))
            text = "".join(sub_tmp)
            res.append({
                "id": i,
                "text": text,
                "labels": sub_labels
            })

            start = 0
            tmp = [word]
            end = len("".join(tmp))
            labels = [["T{}".format(0), dtype, 0, end, word]]
            i += 1

    if tmp:
        text = "".join(tmp)
        res.append({
            "id": i,
            "text": text,
            "labels": labels
        })
        i += 1

with open("../mid_data/test.json", 'w', encoding="utf-8") as fp:
    json.dump(res, fp, ensure_ascii=False)

上述需要我们自己定义一个max_seq_len,然后会对句子进行切分,最后会在data/sighan2005/mid_data/下生成以下文件:train.json、test.json、labels.json、nor_ent2id.json。其中train.json部分数据如下:

[{"id": 0, "text": "迈向充满希望的新世纪——一九九八年新年讲话(附图片1张)", "labels": [["T0", "word", 0, 2, "迈向"], ["T1", "word", 2, 4, "充满"], ["T2", "word", 4, 6, "希望"], ["T3", "word", 6, 7, "的"], ["T4", "word", 7, 8, "新"], ["T5", "word", 8, 10, "世纪"], ["T6", "word", 10, 12, "——"], ["T7", "word", 12, 17, "一九九八年"], ["T8", "word", 17, 19, "新年"], ["T9", "word", 19, 21, "讲话"], ["T10", "word", 21, 22, "("], ["T11", "word", 22, 23, "附"], ["T12", "word", 23, 25, "图片"], ["T13", "word", 25, 26, "1"], ["T14", "word", 26, 27, "张"], ["T15", "word", 27, 28, ")"]]},]

labels.json是实体标签,这里就是一个["word"]。nor_ent2id是实体对应的BIOES标签,这里是:{"O": 0, "B-word": 1, "I-word": 2, "E-word": 3, "S-word": 4}。

有了这些数据之后,就可以生成bert所需要的数据了,具体代码在preprocess.py里面。这里面我们要定义以下参数:

dataset = "sighan2005"
args.max_seq_len = 512  # 和之前process.py里面保持一致

运行之后部分样例如下:

*** train_example-1 ***
[CLS] 迈 向 充 满 希 望 的 新 世 纪 [UNK] [UNK] 一 九 九 八 年 新 年 讲 话 ( 附 图 片 1 张 ) [SEP]
text: [CLS] 迈 向 充 满 希 望 的 新 世 纪 — — 一 九 九 八 年 新 年 讲 话 ( 附 图 片 1 张 ) [SEP]
token_ids: [101, 6815, 1403, 1041, 4007, 2361, 3307, 4638, 3173, 686, 5279, 100, 100, 671, 736, 736, 1061, 2399, 3173, 2399, 6382, 6413, 8020, 7353, 1745, 4275, 8029, 2476, 8021, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
attention_masks: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
token_type_ids: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
labels: [0, 1, 3, 1, 3, 1, 3, 4, 4, 1, 3, 1, 3, 1, 2, 2, 2, 3, 1, 3, 1, 3, 4, 4, 1, 3, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
length: 512

实际上我们会将每一个词转换为B(词的开头)I(词的中间)E(词的结尾)S(单独的一个字)标签,比如针对于"迈向",其对应的标签就是B-word,E-word,如果是单个的字,则是S-word。最后会在data/sighan2005/final_data下生成train.pkl和dev.pkl。

训练、验证、测试和预测

主代码在main.py里面,其中:

bertForNer = BertForNer(args, train_loader, dev_loader, dev_loader, id2query)
bertForNer.train()

model_path = './checkpoints/{}_{}/model.pt'.format(model_name, args.data_name)
bertForNer.test(model_path)
"""
{"id": 5, "text": "在1998年来临之际,我十分高兴地通过中央人民广播电台、中国国际广播电台和中央电视台,向全国各族人民,向香港特别行政区同胞、澳门和台湾同胞、海外侨胞,向世界各国的朋友们,致以诚挚的问候和良好的祝愿!", "labels": [["T0", "word", 0, 1, "在"], ["T1", "word", 1, 6, "1998年"], ["T2", "word", 6, 8, "来临"], ["T3", "word", 8, 10, "之际"], ["T4", "word", 10, 11, ","], ["T5", "word", 11, 12, "我"], ["T6", "word", 12, 14, "十分"], ["T7", "word", 14, 16, "高兴"], ["T8", "word", 16, 17, "地"], ["T9", "word", 17, 19, "通过"], ["T10", "word", 19, 21, "中央"], ["T11", "word", 21, 23, "人民"], ["T12", "word", 23, 25, "广播"], ["T13", "word", 25, 27, "电台"], ["T14", "word", 27, 28, "、"], ["T15", "word", 28, 30, "中国"], ["T16", "word", 30, 32, "国际"], ["T17", "word", 32, 34, "广播"], ["T18", "word", 34, 36, "电台"], ["T19", "word", 36, 37, "和"], ["T20", "word", 37, 39, "中央"], ["T21", "word", 39, 42, "电视台"], ["T22", "word", 42, 43, ","], ["T23", "word", 43, 44, "向"], ["T24", "word", 44, 46, "全国"], ["T25", "word", 46, 48, "各族"], ["T26", "word", 48, 50, "人民"], ["T27", "word", 50, 51, ","], ["T28", "word", 51, 52, "向"], ["T29", "word", 52, 54, "香港"], ["T30", "word", 54, 56, "特别"], ["T31", "word", 56, 59, "行政区"], ["T32", "word", 59, 61, "同胞"], ["T33", "word", 61, 62, "、"], ["T34", "word", 62, 64, "澳门"], ["T35", "word", 64, 65, "和"], ["T36", "word", 65, 67, "台湾"], ["T37", "word", 67, 69, "同胞"], ["T38", "word", 69, 70, "、"], ["T39", "word", 70, 72, "海外"], ["T40", "word", 72, 74, "侨胞"], ["T41", "word", 74, 75, ","], ["T42", "word", 75, 76, "向"], ["T43", "word", 76, 78, "世界"], ["T44", "word", 78, 80, "各国"], ["T45", "word", 80, 81, "的"], ["T46", "word", 81, 83, "朋友"], ["T47", "word", 83, 84, "们"], ["T48", "word", 84, 85, ","], ["T49", "word", 85, 87, "致以"], ["T50", "word", 87, 89, "诚挚"], ["T51", "word", 89, 90, "的"], ["T52", "word", 90, 92, "问候"], ["T53", "word", 92, 93, "和"], ["T54", "word", 93, 95, "良好"], ["T55", "word", 95, 96, "的"], ["T56", "word", 96, 98, "祝愿"], ["T57", "word", 98, 99, "!"]]}
"""
        
raw_text = "在1998年来临之际,我十分高兴地通过中央人民广播电台、中国国际广播电台和中央电视台,向全国各族人民,向香港特别行政区同胞、澳门和台湾同胞、海外侨胞,向世界各国的朋友们,致以诚挚的问候和良好的祝愿!"
logger.info(raw_text)
bertForNer.predict(raw_text, model_path)

分别是训练、测试和预测,如需只运行相关功能,注释掉其它代码即可。运行指令:

!python main.py \
--bert_dir="../model_hub/chinese-bert-wwm-ext/" \
--data_dir="./data/sighan2005/" \
--data_name='sighan2005' \
--log_dir="./logs/" \
--output_dir="./checkpoints/" \
--num_tags=5 \
--seed=123 \
--gpu_ids="0" \
--max_seq_len=512 \
--lr=3e-5 \
--crf_lr=3e-2 \
--other_lr=3e-4 \
--train_batch_size=16 \
--train_epochs=3 \
--eval_batch_size=16 \
--lstm_hidden=128 \
--num_layers=1 \
--use_lstm='False' \
--use_crf='True' \
--dropout_prob=0.3 \
--dropout=0.3 

最后我们来看看结果:

precision:0.9667 recall:0.9549 micro_f1:0.9608
          precision    recall  f1-score   support

    word       0.97      0.95      0.96    104371

micro-f1       0.97      0.95      0.96    104371

在1998年来临之际,我十分高兴地通过中央人民广播电台、中国国际广播电台和中央电视台,向全国各族人民,向香港特别行政区同胞、澳门和台湾同胞、海外侨胞,向世界各国的朋友们,致以诚挚的问候和良好的祝愿!
Load ckpt from ./checkpoints/bert_crf_sighan2005/model.pt
Use single gpu in: ['0']
{'word': [('在', 0), ('1998年', 1), ('来临', 6), ('之际', 8), (',', 10), ('我', 11), ('十分', 12), ('高兴', 14), ('地', 16), ('通过', 17), ('中央', 19), ('人民', 21), ('广播', 23), ('电台', 25), ('、', 27), ('中国', 28), ('国际', 30), ('广播', 32), ('电台', 34), ('和', 36), ('中央', 37), ('电视台', 39), (',', 42), ('向', 43), ('全国', 44), ('各族', 46), ('人民', 48), (',', 50), ('向', 51), ('香港', 52), ('特别', 54), ('行政区', 56), ('同胞', 59), ('、', 61), ('澳门', 62), ('和', 64), ('台湾', 65), ('同胞', 67), ('、', 69), ('海外', 70), ('侨胞', 72), (',', 74), ('向', 75), ('世界各国', 76), ('的', 80), ('朋友', 81), ('们', 83), (',', 84), ('致以', 85), ('诚挚', 87), ('的', 89), ('问候', 90), ('和', 92), ('良好', 93), ('的', 95), ('祝愿', 96), ('!', 98)]}

到此,我们的基于bert的分词就全部完成了。

补充

代码地址:https://github.com/taishan1994/pytorch_bert_bilstm_crf_ner

要先下载预训练模型和项目同级的model_hub下,这里使用的是hugging face上面的chinese-bert-wwm-ext。

posted @ 2022-08-17 10:13  西西嘛呦  阅读(165)  评论(0编辑  收藏  举报