Flat-Lattice-Transformer模型源码测试
论文地址:
FLAT: Chinese NER Using Flat-Lattice Transformer
原github项目地址:
Flat-Lattice-Transformer
1. 下载embedding
Character and Bigram embeddings (gigaword_chn.all.a2b.{‘uni’ or ‘bi’}.ite50.vec) :
Word(Lattice) embeddings:yj, (ctb.50d.vec)
Word(Lattice) embeddings:ls, (sgns.merge.word.bz2)
在项目根目录下新建文件夹embeddings
,将所有embedding文件都放入embeddings/
文件夹下面。
2. 下载数据集
2.1. OntoNotes
需要注册申请,暂无法获取
2.2. MSRA
MSRA数据需要进行预处理,原始数据为bio标注,需要转成bmes。
2.2.1. 数据预处理
- 新建py脚本文件
util/prepro_data.py
,内容如下:
#!usr/bin/env python
# encoding: utf-8
from const.paths import msra_ner_cn_path
import os
import sys
def BIO2BMES(input_file, output_file):
print("Convert BIO -> BMES for file:", input_file)
with open(input_file,'r') as in_file:
fins = in_file.readlines()
fout = open(output_file,'w')
words = []
labels = []
for line in fins:
if len(line) < 3:
sent_len = len(words)
for idx in range(sent_len):
if "-" not in labels[idx]:
fout.write(words[idx]+" "+labels[idx]+"\n")
else:
label_type = labels[idx].split('-')[-1]
if "B-" in labels[idx]:
if (idx == sent_len - 1) or ("I-" not in labels[idx+1]):
fout.write(words[idx]+" S-"+label_type+"\n")
else:
fout.write(words[idx]+" B-"+label_type+"\n")
elif "I-" in labels[idx]:
if (idx == sent_len - 1) or ("I-" not in labels[idx+1]):
fout.write(words[idx]+" E-"+label_type+"\n")
else:
fout.write(words[idx]+" M-"+label_type+"\n")
fout.write('\n')
words = []
labels = []
else:
if line == '0\t\n':
words.append('0')
labels.append('O')
else:
pair = line.strip('\n').split()
words.append(pair[0])
labels.append(pair[-1].upper())
fout.close()
print("BMES file generated:", output_file)
def msra_bio2bmes(msrapath):
train_dev_path = os.path.join(msrapath, 'msra_train_bio.txt')
train_dev_path_out = os.path.join(msrapath, 'train_dev.char.bmes')
test_path = os.path.join(msrapath, 'msra_test_bio.txt')
test_path_out = os.path.join(msrapath, 'test.char.bmes')
BIO2BMES(train_dev_path, train_dev_path_out)
BIO2BMES(test_path, test_path_out)
if __name__ == '__main__':
msra_bio2bmes(msra_ner_cn_path)
print('- Done!')
按照配置paths.py
文件,配置好路径以后,直接运行脚本:
cd util
python prepro_data.py
如果要使用clip(限制句子长度,将长句子进行拆分)的MSRA数据训练模型还需要*_clip1
文件,可以在项目根目录运行下面命令生成:
python preprocess.py --clip_msra
2.3. Weibo
weibo数据需要进行预处理,将原始数据中的seg信息去掉。
2.3.1. 数据预处理
- 在py脚本文件
util/prepro_data.py
中添加如下内容:
#!usr/bin/env python
# encoding: utf-8
from const.paths import weibo_ner_path
import os
def deseg_weibo(weibopath):
train_path = os.path.join(weibopath, 'weiboNER_2nd_conll.train')
dev_path = os.path.join(weibopath, 'weiboNER_2nd_conll.dev')
test_path = os.path.join(weibopath, 'weiboNER_2nd_conll.test')
for data_file in [train_path, dev_path, test_path]:
output_file = data_file + "_deseg"
f_out = open(output_file, "w", encoding='utf8')
with open(data_file, "r", encoding='utf8') as f:
for line in f.readlines():
line = line.strip()
if line != "":
span_list = line.split('\t')
raw_char = ''.join(list(span_list[0])[:-1])
tag = span_list[-1]
f_out.write(' '.join([raw_char, tag]) + '\n')
else:
f_out.write('\n')
if __name__ == '__main__':
deseg_weibo(weibo_ner_path)
print('- Done!')
可以先直接运行脚本,生成数据文件,这样就没必要修改
load_data.py
文件了。
2.4. ResumeNER
ResumeNER数据可直接使用。
将所有数据集文件都放入data/
文件夹下面,文件夹结构如下:
-data
--Resume
---dev.char.bmes
---test.char.bmes
---train.char.bmes
--MSRA
---msra_test_bio.txt
---msra_train_bio.txt
---test.char.bmes
---train_dev.char.bmes
---test.char.bmes_clip1
---train_dev.char.bmes_clip1
--Weibo
---weiboNER_2nd_conll.dev
---weiboNER_2nd_conll.test
---weiboNER_2nd_conll.train
---weiboNER_2nd_conll.dev_deseg
---weiboNER_2nd_conll.test_deseg
---weiboNER_2nd_conll.train_deseg
3. 配置环境
conda create -n FLAT python=3.7.3
# gpu
conda install pytorch==1.2.0 torchvision==0.4.0 cudatoolkit=10.0 -c pytorch
# cpu
conda install pytorch==1.2.0 torchvision==0.4.0 cpuonly -c pytorch
pip install FastNLP==0.5.0 -i https://pypi.doubanio.com/simple
pip install Numpy==1.16.4 -i https://pypi.doubanio.com/simple
pip install fitlog -i https://pypi.doubanio.com/simple
pip install pytz -i https://pypi.doubanio.com/simple
其中FastNLP和fitlog都是复旦大学计算机科学技术学院自然语言处理与深度学习组的fastNLP团队开发的。
可直接运行
conda env create -f environment.yml
命令,从environment.yml
或者environment-gpu.yml
文件中创建名为FLAT
的conda环境。
4. 配置paths.py
文件
修改paths.py
文件为如下内容,然后只用配置PROJECT_DIR
(项目根目录)即可。
PROJECT_DIR = '/home/appendDisk/PycharmProjects/NER/Flat-Lattice-Transformer'
yangjie_rich_pretrain_unigram_path = PROJECT_DIR + '/embeddings/gigaword_chn.all.a2b.uni.ite50.vec'
yangjie_rich_pretrain_bigram_path = PROJECT_DIR + '/embeddings/gigaword_chn.all.a2b.bi.ite50.vec'
yangjie_rich_pretrain_word_path = PROJECT_DIR + '/embeddings/ctb.50d.vec'
yangjie_rich_pretrain_char_and_word_path = PROJECT_DIR + '/embeddings/yangjie_word_char_mix.txt'
# lk_word_path = '/remote-home/xnli/data/pretrain/chinese/sgns.merge.word'
lk_word_path_2 = PROJECT_DIR + '/embeddings/sgns.merge.word_2'
# ontonote4ner_cn_path = PROJECT_DIR + '/data/OntoNote4'
msra_ner_cn_path = PROJECT_DIR + '/data/MSRA'
resume_ner_path = PROJECT_DIR + '/data/Resume'
weibo_ner_path = PROJECT_DIR + '/data/Weibo'
5. 配置日志文件
5.1. 使用V0中的flat_main.py
fitlog init V0
cd V0
# fitlog log logs
修改V0/flat_main.py
文件:
import fitlog
# use_fitlog = False
use_fitlog = True
5.2. 使用V1中的flat_main.py
fitlog init V1
cd V1
# fitlog log logs
6. embedding文件预处理
运行下面脚本:
python preprocess.py
7. 训练模型
7.1. GPU设备
python flat_main.py --dataset resume
# 如果默认device报gpu OOM错误,可以通过nvidia-smi命令查看闲置的编号(如2),然后运行下面命令
python flat_main.py --dataset resume --device 2
目前可用的数据文件只有resume
MSRA和weibo数据好像都要做预处理,项目中没有给出详细的预处理信息
OntoNotes数据暂时无法获取
7.2. CPU设备
python flat_main.py --dataset resume --device cpu
还是会报找不到gpu未指定cpu的错误,参数未生效。
报错内容如下:
ValueError: There is no usable gpu. set `device` as `cpu` or `None`.
修改V0/flat_main.py
文件:
if args.device!='cpu':
if args.device == 'None':# 添加内容
device = None# 添加内容
else:# 添加内容
assert args.device.isdigit()
device = torch.device('cuda:{}'.format(args.device))
else:
device = torch.device('cpu')
refresh_data = True
运行下面命令即可:
python flat_main.py --dataset resume --device None
8. 查看模型结果
以下是在gpu服务器上运行的结果
仅输出了模型相关参数,没有输出训练好的模型。
8.1. MSRA
目前的只用了一个device,一直报CUDA out of memory
,目前跑不了了,看看后面能不能找到支持多GPU运行的办法。
github issue的类似问题里,作者说”吃的显存应该和训练数据中的最长句子的长度相关,对10g显存来说,一般支持长度200的句子“。
也有可能是数据预处理的问题,看看后面作者会不会公开预处理的脚本吧。
8.2. Weibo
8.2.1. V0结果摘要
时间信息:/V0/logs/log_20201202_110755/other.log
{"other": {"cost_time": "0h20m18s"}}
评价指标:/V0/logs/log_20201202_110755/metric.log
Step:6750 {"metric": {"SpanFPreRecMetric": {"f": 0.626109, "pre": 0.6175, "rec": 0.634961}, "label_acc": {"acc": 0.961509}}, "step": 6750, "epoch": 50}
Step:6750 {"metric": {"data_test": {"SpanFPreRecMetric": {"f": 0.592593, "pre": 0.591885, "rec": 0.593301}, "label_acc": {"acc": 0.956756}}}, "step": 6750, "epoch": 50}
其中最大的F1值为:0.635538
8.2.2. V1结果摘要
时间信息:/V1/logs/log_20201202_114132/other.log
{"other": {"cost_time": "0h40m55s"}}
评价指标:/V1/logs/log_20201202_114132/metric.log
Step:7695 {"metric": {"SpanFPreRecMetric": {"f": 0.685786, "pre": 0.66586, "rec": 0.706941}, "label_acc": {"acc": 0.964555}}, "step": 7695, "epoch": 57}
Step:7695 {"metric": {"data_test": {"SpanFPreRecMetric": {"f": 0.689017, "pre": 0.666667, "rec": 0.712919}, "label_acc": {"acc": 0.964987}}}, "step": 7695, "epoch": 57}
其中最大的F1值为:0.708763
8.3. ResumeNER
8.3.1. V0结果摘要
时间信息:/V0/logs/log_20201201_162830/other.log
{"other": {"cost_time": "0h35m50s"}}
评价指标:/V0/logs/log_20201201_162830/metric.log
Step:19150 {"metric": {"SpanFPreRecMetric": {"f": 0.949153, "pre": 0.944444, "rec": 0.953908}, "label_acc": {"acc": 0.975378}}, "step": 19150, "epoch": 50}
Step:19150 {"metric": {"data_test": {"SpanFPreRecMetric": {"f": 0.944887, "pre": 0.943154, "rec": 0.946626}, "label_acc": {"acc": 0.96543}}}, "step": 19150, "epoch": 50}
其中最大的F1值为:0.951855
8.3.2. V1结果摘要
时间信息:/V1/logs/log_20201201_173707/other.log
{"other": {"cost_time": "1h25m38s"}}
评价指标:/V1/logs/log_20201201_173707/metric.log
Step:19916 {"metric": {"SpanFPreRecMetric": {"f": 0.893802, "pre": 0.896505, "rec": 0.891116}, "label_acc": {"acc": 0.947228}}, "step": 19916, "epoch": 52}
Step:19916 {"metric": {"data_test": {"SpanFPreRecMetric": {"f": 0.894314, "pre": 0.900996, "rec": 0.88773}, "label_acc": {"acc": 0.94298}}}, "step": 19916, "epoch": 52}
其中最大的F1值为:0.951859
接下来打算做的事情:
跑通MSRA和weibo数据- 调用训练好的模型对输入字符串进行NER抽取任务
- 如何支持在多GPU运行