这个项目的本质是-----------医学文本的实体关系联合抽取问题,既要解决命名实体识别,又要解决关系分类问题。使用RoFormerV2模型作为预训练模型,GPLinker作为下游模型,在Embedding层添加FGM对抗训练增加模型性能。
CHIP-2020-2中文医学文本实体关系抽取数据集
数据集包含儿科训练语料和百种常见疾病训练语料,儿科训练语料来源于518种儿科疾病,百种常见疾病训练语料来源于109种常见疾病。
近7.5万三元组数据,2.8万疾病语句和53种定义好的schema。
数据集格式
{
"spo_list" : [
{
"Combined" : true,
"object" : {
"@value" : "外照射"
},
"object_type" : {
"@value" : "其他治疗"
},
"predicate" : "放射治疗",
"subject" : "慢性胰腺炎",
"subject_type" : "疾病"
},
{
"Combined" : true,
"object" : {
"@value" : "外照射"
},
"object_type" : {
"@value" : "其他治疗"
},
"predicate" : "放射治疗",
"subject" : "非肿瘤性疼痛",
"subject_type" : "疾病"
}
],
"text" : "慢性胰腺炎@### 低剂量放射 自1964年起,有几项病例系列报道称外照射 (5-50Gy) 可以有效改善慢性胰腺炎患者的疼痛症状。慢性胰腺炎@从概念上讲,外照射可以起到抗炎和止痛作用,并且已经开始被用于非肿瘤性疼痛的治疗。"
}
临床实践文本可能不提及主题疾病---每句之前增加主题疾病实体,并以@和原文分割
Combined-----三元组来源上下多个句子,拼接在一起
”text”----记录来源文本
SPO表示法。S-头实体,O-尾实体,P-Predicate,即「关系(Relation)」更专业的叫法。
{
"spo_list" : [
{
"Combined" : true,
"object" : "外照射",
"object_type" : "其他治疗",
"predicate" : "放射治疗",
"subject" : "慢性胰腺炎",
"subject_type" : "疾病"
},
{
"Combined" : true,
"object" : "外照射",
"object_type" : "其他治疗",
"predicate" : "放射治疗",
"subject" : "非肿瘤性疼痛",
"subject_type" : "疾病"
}
],
"text" : "慢性胰腺炎@### 低剂量放射 自1964年起,有几项病例系列报道称外照射 (5-50Gy) 可以有效改善慢性胰腺炎患者的疼痛症状。慢性胰腺炎@从概念上讲,外照射可以起到抗炎和止痛作用,并且已经开始被用于非肿瘤性疼痛的治疗。"
}
... ...
{"subject_type": "疾病", "predicate": "发病部位", "object_type": "部位"}
{"subject_type": "疾病", "predicate": "转移部位", "object_type": "部位"}
{"subject_type": "疾病", "predicate": "外侵部位", "object_type": "部位"}
... ...
{'text': text, 'spo_list': [(s, p, o)]}
schema中的predicate不能重复,如有重复要更改,加以区别,否则就算object_type和subject_type不一致,只要predicate一致,就认为是一个关系。数据里的predicate也要同步更改。
./
├── README.md
├── chinese_roformer-v2-char_L-12_H-768_A-12 roformer-v2 12层base版
│ ├── bert_config.json 是BERT在训练时可选调整参数
│ ├── bert_model.ckpt.data-00000-of-00001
│ ├── bert_model.ckpt.index 负责模型变量载入
│ ├── bert_model.ckpt.meta 负责模型变量载入
│ ├── checkpoint
│ └── vocab.txt
├── chinese_roformer-v2-char_L-6_H-384_A-6 roformer-v2 12层base版
│ ├── bert_config.json
│ ├── bert_model.ckpt.data-00000-of-00001
│ ├── bert_model.ckpt.index
│ ├── bert_model.ckpt.meta
│ ├── checkpoint
│ └── vocab.txt
├── config.py 部分超参数配置
├── data
│ ├── chip2020 数据集
│ │ ├── 53_schemas.json
│ │ ├── train_data.json
│ │ └── val_data.json
│ └── pred 最佳模型预测样本
│ ├── val_pred_ep38.json
│ └── val_pred_ep73.json
├── dataloader.py 数据编码器
├── evaluate.py 模型评估
├── images 数据绘图
│ ├── train_loss.png
│ ├── train_loss_base.png
│ ├── val_f1.png
│ └── val_f1_base.png
├── log 日志
│ ├── f1.out
│ ├── nohup.out
│ ├── nohup_base.out
│ ├── train_log.csv
│ └── train_log_base.csv
├── main.py
├── model.py 模型文件
├── path.py 项目路径
├── plot.py 画图工具
├── predict.py 预测文件
├── report 模型评估报告
│ ├── f1.csv
│ ├── predicate_f1.csv
│ └── predicate_f1_base.csv
├── schemaloader.py schema加载器
├── test.py token转可读字符
├── train.py 训练
├── utils bert4keras工具包,可pip下载
│ ├── __init__.py
│ ├── adversarial.py
│ ├── backend.py
│ ├── layers.py
│ ├── models.py
│ ├── optimizers.py
│ ├── snippets.py
│ └── tokenizers.py
└── weights 保存的权重
├── gplinker_roformer_best.h5
└── gplinker_roformer_v2_best.h5
10 directories, 51 files
模型中L表示的是transformer的层数,H表示输出的维度,A表示mutil-head attention的个数
# 构建模型
#extend_with_exponential_moving_average:返回新的优化器类,加入EMA(权重滑动平均)
AdamEMA = extend_with_exponential_moving_average(Adam, name = 'AdamEMA')
optimizer = AdamEMA(lr = 5e-5)
model.compile(loss = globalpointer_crossentropy, optimizer = optimizer)
model.summary()
adversarial_training(model, 'Embedding-Token', 0.5)
Adam:一阶梯度优化算法,提高计算效率,降低内存需求。
optimizer优化器
model.compile():模型编译的时候把优化器跟损失函数传进去,并返回一个张量作为输出结果。
adversarial_training(): 对抗训练
model.summary(): 输出模型各层的参数状况
def on_epoch_end(self, epoch, logs = None):
global count_model_did_not_improve #记录模型没有变好的次数
#确定模型参数保存位置
save_best_path = "{}/{}_{}_best.h5".format(weights_path, event_type, MODEL_TYPE)
#apply_ema_weights():备份原模型权重,然后将平均权重应用到模型上去
optimizer.apply_ema_weights()
f1, precision, recall = evaluate(valid_data, epoch)
f1_list.append(f1)
recall_list.append(recall)
precision_list.append(precision)
if f1 >= self.best_val_f1:
self.best_val_f1 = f1
count_model_did_not_improve = 0
model.save_weights(save_best_path)
else:
count_model_did_not_improve += 1
print("Early stop count " + str(count_model_did_not_improve) + "/" + str(self.patience))
#当验证集F1超过5轮没有上升时,停止训练。
if count_model_did_not_improve >= self.patience:
self.model.stop_training = True
print("Epoch %05d: early stopping THR" % epoch)
def get_model():
# 加载预训练模型
base = build_transformer_model(
config_path = config_path,
checkpoint_path = checkpoint_path,
model = MODEL_TYPE,
return_keras_model = False
)
# 预测结果
#SetLearningRate:层的一个包装,用来设置当前层的学习率
entity_output = SetLearningRate(GlobalPointer(heads = 2, head_size = 64, kernel_initializer = "he_normal"),
10, True)(base.model.output)
head_output = SetLearningRate(GlobalPointer(
heads = len(predicate2id), head_size = 64, RoPE = False, tril_mask = False, kernel_initializer = "he_normal"
), 10, True)(base.model.output)
tail_output = SetLearningRate(GlobalPointer(
heads = len(predicate2id), head_size = 64, RoPE = False, tril_mask = False, kernel_initializer = "he_normal"
), 10, True)(base.model.output)
outputs = [entity_output, head_output, tail_output]
#input输入层,output输出层
model = keras.models.Model(base.model.inputs, outputs)
return model
1.调用bert4keras中的build_transformer_model来选择使用的模型
checkpoint_path = checkpoint_path
checkpoint_path = BASE_CKPT_NAME
BASE_CKPT_NAME = proj_path + “/chinese_roformer-v2-char_L-12_H-768_A-12/bert_model.ckpt”
TensorFlow模型会保存在后缀为.ckpt的文件中。
保存后在save这个文件夹中实际会出现3个文件,因为TensorFlow会将计算图的结构和图上参数取值分开保存。
model.ckpt.meta文件保存了TensorFlow计算图的结构,可以理解为神经网络的网络结构model.ckpt文件保存了TensorFlow程序中每一个变量的取值
checkpoint文件保存了一个目录下所有的模型文件列表(也可以看作权重)
class SetLearningRate:
"""层的一个包装,用来设置当前层的学习率
"""
def __init__(self, layer, lamb, is_ada = False):
self.layer = layer
self.lamb = lamb # 学习率比例
self.is_ada = is_ada # 是否自适应学习率优化器
def __call__(self, inputs):
with K.name_scope(self.layer.name):
if not self.layer.built:
input_shape = K.int_shape(inputs)
self.layer.build(input_shape)
self.layer.built = True
if self.layer._initial_weights is not None:
self.layer.set_weights(self.layer._initial_weights)
for key in ['kernel', 'bias', 'embeddings', 'depthwise_kernel', 'pointwise_kernel', 'recurrent_kernel', 'gamma',
'beta']:
if hasattr(self.layer, key):
weight = getattr(self.layer, key)
if self.is_ada:
lamb = self.lamb # 自适应学习率优化器直接保持lamb比例
else:
lamb = self.lamb ** 0.5 # SGD(包括动量加速),lamb要开平方
K.set_value(weight, K.eval(weight) / lamb) # 更改初始化
setattr(self.layer, key, weight * lamb) # 按比例替换
return self.layer(inputs)
AttributeError: module ‘keras.engine.base_layer’ has no attribute ‘Node’
KeyError: ‘roformer’
解决:
因为bert4keras模块的版本不对应------pip install bert4keras==0.11.1
源代码中的utils文件夹其实就是bert4keras库,但是没有更新到最新的,所以导致找不到roformer
tensorflow.python.framework.errors_impl.NotFoundError: Key bert/embeddings/LayerNorm/beta not found in checkpoint
bert4keras版本出错
如何用gpu去跑数据
安装tensorflow-gpu并且注意与cuda的版本问题
tensorflow.python.framework.errors_impl.ResourceExhaustedError: 2 root error(s) found.
找到两个root错误
cuda+cuDNN+TensorFlow版本不一致导致
显存分配问题,更改为动态分配内存就可以解决。
ensorflow或者numpy版本引起-----tensorflow是1.14.0版本。应该使用pip uninstall numpy卸载所有的numpy,再安装1.16.4即可
(以上方法都没用)
后来发现漏了上一行报错tensorflow.python.framework.errors_impl.ResourceExhaustedError: OOM when allocating tensor with shape[32,256,55,55]”
ResourceExhaustedError:资源耗尽错误
减少批处理Batch 的大小32---->16
FileNotFoundError: [Errno 2] Unable to create file (unable to open file: name = ‘/root/autodl-tmp/chip2020_relation_extraction-gplinker/weights/gplinker_roformer_v2_best.h5’, errno = 2, error message = ‘No such file or directory’, flags = 13, o_flags = 242)
报错的中文解释是:没有这样的文件和目录
也就是说写的不应该是文件,而是文件的路径或者说是目录,否则系统根本找不到所指的东西。
问题解决
明确写好文件对应的位置
(其实后来发现是,给的源文件中少了weights的文件夹,添加后)
AttributeError: ‘str’ object has no attribute ‘decode’
解决:
一般是因为str的类型本身不是bytes,所以不能解码
问题原因:可能是h5py模块的版本过高,导致无法加载h5文件------------pip install h5py==2.10.0
有时候有环境配置里很复杂,
用pip install ‘h5py<3.0.0’ -i https://pypi.tuna.tsinghua.edu.cn/simple好一点
Linux实时将所有输出重定向到文件
nohup unbuffer command > file.out 2>&1 &
unbuffer命令需要额外安装expect-devel,用来实时刷新。
nohup命令用来忽略所有挂断(SIGHUP)信号,让你的程序即使在用户注销后依然继续运行。
command是任何一段你想要执行的shell命令。
> file.out 代表将command运行结果重定向到当前目录下的file.out文件中(如果要每次运行的结果追加到file.out后面,可以用>>而不是>)。
2 >&1表示将标准错误输出cerr的所有输出也都重定向到标准输出cout中,这样file.out中就会记录command命令运行过程中所有标准输出。
最后一个&表示后台运行该command。
10.File “/root/miniconda3/envs/simbert/lib/python3.7/site-packages/tensorflow/python/client/session.py”, line 1458, in call
run_metadata_ptr)
tensorflow.python.framework.errors_impl.InternalError: 2 root error(s) found.
(0) Internal: Blas xGEMMBatched launch failed : a.shape=[12,234,64], b.shape=[12,64,234], m=234, n=234, k=64, batch_size=12
[[{{node Transformer-0-MultiHeadSelfAttention_1/einsum/MatMul}}]]
[[global_pointer_5_1/truediv/_1251]]
(1) Internal: Blas xGEMMBatched launch failed : a.shape=[12,234,64], b.shape=[12,64,234], m=234, n=234, k=64, batch_size=12
[[{{node Transformer-0-MultiHeadSelfAttention_1/einsum/MatMul}}]]
解决:
很玄学,相同代码之前没问题,后来训练了一次mt5后出现问题,以为是内存爆炸,然后使用A500GPU还是不行,(A500不行怀疑是算力不匹配),然后换回最开始的代码在2080ti上跑行了