FLAT代码解读(1)-输入

论文 FLAT: Chinese NER Using Flat-Lattice Transformer(ACL 2020)

首先介绍一下命名实体识别任务数据集的常见标注格式:

  • BIO: B(Begin)-X 实体X的开头;I(Inside)-X 实体X的内部;O(Outside) 不属于实体
  • BIOES: B(Begin)-X 实体X的开头;I(Inside)-X 实体X的中间;O(Outside) 不属于实体;E(End)-X 实体X的结尾;S(Singleton)-the single word是实体
  • BMES:B(Begin)-X 实体X的开头;M(Middle)-X 实体X的中间;E(End)-X 实体X的结尾;S(Singleton)-the single word是实体

比如:

Michael Jeffrey Jordan was born in Brooklyn , New   York   .
B-PER   I-PER   E-PER   O   O   O  S-LOC    O B-LOC E-LOC  O

FALT整体框架图

词典信息加入模型被证明对中文NER任务很有效,但是结合词典的方法通常会使输入变成一个动态的结构,导致无法有效利用GPU的并行计算。FLAT模型通过采用一个特殊的位置编码表征输入结构,从而不需要在运行时动态改变结构来表征输入。这里推荐去看作者的讲解视频 结合词典的中文命名实体识别

FLAT

数据处理

  1. 读取数据集
from fastNLP.io.loader import ConllLoader

def get_bigrams(words):
    result = []
    for i, w in enumerate(words):
        if i != len(words)-1:
            result.append(words[i]+words[i+1])
        else:
            result.append(words[i]+'<end>')

    return result

train_path = os.path.join('/input/resume-ner', 'train.char.bmes')

loader = ConllLoader(['chars', 'target']) # header
train_bundle = loader.load(train_path)
datasets = dict()
datasets['train'] = train_bundle.datasets['train']
# 添加列 bigrams
datasets['train'].apply_field(get_bigrams, field_name='chars', new_field_name='bigrams')
# 添加列 seq_len
datasets['train'].add_seq_len('chars')

print(datasets['train'])

得到的输出格式为:

train examples

chars列为样本中的字符
target列为标签
bigrams列为两两相邻字符组成的双字
seq_len为chars列中字符的个数

  1. 提取vocab
from fastNLP import Vocabulary

char_vocab = Vocabulary()
bigram_vocab = Vocabulary()
label_vocab = Vocabulary()
# 根据列 field_name中的词构建词典
char_vocab.from_dataset(datasets['train'], field_name='chars') # <pad> id:0, <unk> id:1, 公 id:3 ...
bigram_vocab.from_dataset(datasets['train'], field_name='bigrams') # <pad> id:0, <unk> id:1, 公司 id:2 ...                
label_vocab.from_dataset(datasets['train'], field_name='target') # <pad> id:0, <unk> id:1, O id:2, M-ORG id:3 ...

根据数据集字段中的字符构建对应的词典vocab。

  1. 加载预训练embedding
embeddings = {}
    if char_embedding_path is not None:
        char_embedding = StaticEmbedding(char_vocab, char_embedding_path, word_dropout=0.01,
                                         min_freq=char_min_freq, only_train_min_freq=only_train_min_freq)
        embeddings['char'] = char_embedding

    if bigram_embedding_path is not None:
        bigram_embedding = StaticEmbedding(bigram_vocab, bigram_embedding_path, word_dropout=0.01,
                                           min_freq=bigram_min_freq, only_train_min_freq=only_train_min_freq)
        embeddings['bigram'] = bigram_embedding

    return datasets, vocabs, embeddings
  • char_embedding_path为预训练的单个字符的embedding向量。
  • bigram_embedding_path为预训练的双个字符的embedding向量。

给定预训练embedding的路径,StaticEmbdding函数根据vocab从embedding中抽取相应的数据(只会将出现在vocab中的词抽取出来,如果没有找到,则会随机初始化一个值(但如果该word是被标记为no_create_entry的话,则不会单独创建一个值,而是会被指向unk的index))。

  1. 融入词汇信息
w_list = load_yangjie_rich_pretrain_word_list(yangjie_rich_pretrain_word_path,
                                              _refresh=refresh_data,
                                              _cache_fp='cache/{}'.format(args.lexicon_name))

datasets,vocabs,embeddings = equip_chinese_ner_with_lexicon(datasets,vocabs,embeddings,
                                                            w_list,yangjie_rich_pretrain_word_path,
                                                            _refresh=refresh_data,_cache_fp=cache_name,
                                                            only_lexicon_in_train=args.only_lexicon_in_train,
                                                            word_char_mix_embedding_path=output_char_and_word_path,
                                                            number_normalized=args.number_normalized,
                                                            lattice_min_freq=args.lattice_min_freq,
                                                            only_train_min_freq=args.only_train_min_freq)

加载词典数据,得到w_list,词典中的词汇可包含两个,三个等多个字符。
equip_chinese_ner_with_lexicon函数用来将词汇信息lexicon写入样本中。

  • 得到词汇相关字段
    for k, v in datasets.items():
        # partial函数将一个函数的某些参数固定住,返回一个新函数
        v.apply_field(partial(get_skip_path, w_trie=w_trie), 'chars', 'lexicons')
        v.apply_field(copy.copy, 'chars', 'raw_chars')
        v.add_seq_len('lexicons', 'lex_num')
        v.apply_field(lambda x: list(map(lambda y: y[0], x)), 'lexicons', 'lex_s')
        v.apply_field(lambda x: list(map(lambda y: y[1], x)), 'lexicons', 'lex_e')

lexicons列为样本中匹配到的词汇信息,如[[0, 1, '中国'],[3, 5, '天安门']]
lex_num列为样本匹配到的词汇的个数
lex_s列为各个匹配词的起始索引列表,如[0, 3]
lex_e列为各个匹配词的终止索引列表,如[1, 5]

  1. 拼接原始字符串和词汇
    def concat(ins):
        chars = ins['chars']
        lexicons = ins['lexicons']
        result = chars + list(map(lambda x: x[2], lexicons))
        return result

    def get_pos_s(ins):
        lex_s = ins['lex_s']
        seq_len = ins['seq_len']
        pos_s = list(range(seq_len)) + lex_s
        return pos_s

    def get_pos_e(ins):
        lex_e = ins['lex_e']
        seq_len = ins['seq_len']
        pos_e = list(range(seq_len)) + lex_e
        return pos_e

    for k, v in datasets.items():
        v.apply(concat, new_field_name='lattice')
        v.set_input('lattice')
        v.apply(get_pos_s, new_field_name='pos_s')
        v.apply(get_pos_e, new_field_name='pos_e')
        v.set_input('pos_s', 'pos_e')

concat函数将词典lexicon中匹配到的词汇拼接到样本末尾,得到lattice
pos_s列记为论文中的Head
pos_e列即为论文中的Tail
这三列均被设置为Input

  1. 字符转成整数id
vocabs['char'].index_dataset(* (datasets.values()),
                                 field_name='chars', new_field_name='chars')
vocabs['bigram'].index_dataset(* (datasets.values()),
                                   field_name='bigrams', new_field_name='bigrams')
vocabs['label'].index_dataset(* (datasets.values()),
                                  field_name='target', new_field_name='target')
vocabs['lattice'].index_dataset(* (datasets.values()),
                                    field_name='lattice', new_field_name='lattice')

return datasets, vocabs, embeddings

最后通过各个vocab将datasets中的对应字符字段转成数字(vocab中的id)。Vocabulary类的主要作用就是实现字符和对应id之间的互相转换。

最终即得到了FLAT整体框架图中所需要的输入数据。

参考:
FLAT: Chinese NER Using Flat-Lattice Transformer (github.com)
论文阅读《FLAT:Chinese NER Using Flat-Lattice Transformer》
Flat-Lattice-Transformer模型源码测试

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容