医学电子病历命名实体识别的实现总结

医学电子病历命名实体识别的实现总结

一、整体概述

​ 本次实验数据集来自CCKS2018的电子病历命名实体识别的评测任务,原始数据样本包括:一般醒目,出院情况,病史情况,病史特点,诊疗经过,共提供600份标注好的电子病历文本,共需识别含治疗方式、身体部位、疾病症状、医学检查、疾病实体五类实体。

​ 实验采用==BERT+BiLSTM+CRF==构建模型(仅使用BiLSTM来训练NER模型的效果很差)。

二、实现过程
2.1. BIO标注

​ 对句子中的每个单词标记相应的标签,得到最终的命名实体识别结果。

​ O :非实体部分

​ TREATMENT:治疗方式

​ BODY:身体部位

​ SIGN:疾病症状

​ CHECK:医学检查

​ DISEASE:疾病实体

​ 数据转换脚本:

  def transfer(self):
    f = open(self.train_filepath, 'w+')
    count = 0
    for root,dirs,files in os.walk(self.origin_path):
        for file in files:
            filepath = os.path.join(root, file)
            if 'original' not in filepath:
                continue
            label_filepath = filepath.replace('.txtoriginal','')
            print(filepath, '\t\t', label_filepath)
            content = open(filepath).read().strip()
            res_dict = {}
            for line in open(label_filepath):
                res = line.strip().split('	')
                start = int(res[1])
                end = int(res[2])
                label = res[3]
                label_id = self.label_dict.get(label)
                for i in range(start, end+1):
                    if i == start:
                        label_cate = label_id + '-B'
                    else:
                        label_cate = label_id + '-I'
                    res_dict[i] = label_cate

            for indx, char in enumerate(content):
                char_label = res_dict.get(indx, 'O')
                print(char, char_label)
                f.write(char + '\t' + char_label + '\n')
    f.close()
    return
2.2 模型训练

​ 模型构建采用PyTorch实现,使用预训练字向量,作为embedding层输入,然后经过两个双向LSTM层进行编码,编码后加入dense层,最后送入CRF层进行序列标注。

​ 模型训练代码如下:

self.train_manager = DataManager(batch_size=self.batch_size, tags=self.tags)
            self.total_size = len(self.train_manager.batch_data)
            data = {
                "batch_size": self.train_manager.batch_size,
                "input_size": self.train_manager.input_size,
                "vocab": self.train_manager.vocab,
                "tag_map": self.train_manager.tag_map,
            }
            self.save_params(data)
            self.dev_manager = DataManager(batch_size=60, data_type="dev")
            # 验证集
            # self.dev_batch = self.dev_manager.iteration()

            self.model = BiLSTMCRF(
                tag_map=self.train_manager.tag_map,
                batch_size=self.batch_size,
                vocab_size=len(self.train_manager.vocab),
                dropout=self.dropout,
                embedding_dim=self.embedding_size,
                hidden_dim=self.hidden_size,
            )
            self.model = self.model.cuda()
            self.restore_model()
2.3 模型测试与评估

​ 与上述在同一文件,主要对模型的测试和评估,测试集来源于原数据集。

data_map = self.load_params()
            input_size = data_map.get("input_size")
            self.tag_map = data_map.get("tag_map")
            self.vocab = data_map.get("vocab")
            print('input_size',input_size)
            print('tag_map',self.tag_map)
            self.model = BiLSTMCRF(
                tag_map=self.tag_map,
                vocab_size=input_size,
                embedding_dim=self.embedding_size,
                hidden_dim=self.hidden_size
            )
            self.model = self.model.cuda()
            self.test_manager = DataManager(batch_size=60, data_type="dev")
            self.restore_model()
2.4 前端文本颜色标注与标签映射

​ 前端部分采用开源项目ner-app(文章结尾给出相关链接)实现,原项目构建时,解析后的文本已经赋值相应的颜色标注类型,但是我不想去修改模型输出结果(因为测试和训练在一个文件),因此就在前端定义了如下颜色标注类型。

// 定义一个对象来存储类型和颜色的映射  
const color = {  
  'BODY': 'white',  
  'TREATMENT': 'white',  
  'SIGNS': 'white',  
  'CHECK': 'white',
  'DISEASE': 'white',
}; 
const background = {  
  'BODY': 'deeppink',  
  'TREATMENT': 'orange',  
  'SIGNS': '#42b983',  
  'CHECK': 'blue',
  'DISEASE': 'red',
}; 
2.5 前端标注

实现效果如下,具体标注代码如下:

_dataNer(dataProps)
      .then((res) => {
        if (res.status === 200) {
          console.log("后端返回数据:", res.data);
          setmyJson(res.data)
          message.success("数据标识成功");
          setIsLoading(false);
          let data = res.data.data
          console.log("data",data)
          let str = "";
          let len = articleContent.length;
          let i, j, k;
          for (i = 0; i < len; i++) {
            for (j = 0; j < data.length; j++) {
              if (i === parseInt(data[j].start)) {
                str += `<Tooltip title="prompt text"><span onclick="alert('实体:${data[j].type}')" style="color:${color[data[j].type]};background:${background[data[j].type]}">`;
                for (
                  k = parseInt(data[j].start);
                  k <= parseInt(data[j].stop);
                  k++
                ) {
                  str += articleContent[k];
                }
                str += `</span ></Tooltip> `;
                i=k;
              }
            }
            str += articleContent[i];
          }
          setMarkdownContent(str);
        }
      })

在这里插入图片描述

2.6 后端实现

​ 后端使用Flask搭建,调用训练好的模型然后对数据进行训练,接收json格式前端数据,返回数据类型也是json。

​ 这里有个坑,后端返回的json不是array类型,因此在前端标注中遍历的时候会出错。

@app.route('/data', methods=['GET', 'POST'])
def data():
    data = request.json  # 获取前端传递的数据
    print(data)
    # 在这里执行数据标识的逻辑,将结果保存到 identified_data 变量中
    cn = ChineseNER("predict")
    identified_data = cn.predict(data['articleContent'])
    print("identified_data:",identified_data)
    # 返回标识结果给前端
    return jsonify({"status": 200, "data": identified_data})

if __name__ == '__main__':
    # print(data_ner())
    app.run(host="0.0.0.0",port=8000)
2.7 整体展示

​ 实验整体界面展示:
在这里插入图片描述

​ 数据标识展示:

在这里插入图片描述

​ 点击相应实体会有实体类型提示:

在这里插入图片描述

​ 侧边有相应数据统计展示:

在这里插入图片描述

三、 实验总结
3.1 LSTM相对于RNN的优势

​ RNN能够把以前的信息联系到现在,从而解决现在的问题。但是,随着预测信息和相关信息间的间隔增大, RNNs很难去把它们关联起来。如果序列过长,一方面会出现梯度消散或梯度爆炸的问题,另一方面,展开后的前馈神经网络会占用过大的内存。

​ LSTM非常适合用于对时序数据的建模,如文本数据。能将长时期的依赖关系联系起来。

3.2 为什么采用CRF

​ BiLSTM 可以预测出每一个字属于不同标签的概率,然后使用 Softmax 得到概率最大的标签,作为该位置的预测值。但是在预测的时候会忽略了标签之间的关联性,因此 BiLSTM+CRF 在 BiLSTM 的输出层加上一个 CRF,使得模型可以考虑类标之间的相关性

​ CRF层可以向最终的预测标签添加一些约束,以确保它们是有效的。这些约束可以由CRF层在训练过程中从训练数据集自动学习。示例如下:

句子中第一个单词的标签应该以“B-”或“O”开头,而不是“I-”

“B-label1 I-label2 I-label3 I-label4”,在这个模式中,label1、label2、label3、label4应该是相同的命名实体标签。例如,“B-BODY I-BODY I-BODY”是有效的,但是“B-BODY I-SIGNS”是无效的。

“O I-label”无效。一个命名实体的第一个标签应该以“B-”而不是“I-”开头,换句话说,有效的模式应该是“O B-label”。

………

​ 有了这些有用的约束,无效预测标签序列的数量将显著减少。

3.3 BERT的作用

​ BERT预训练模型通过对海量的文本语料进行自监督学习,可以为单词学习好的特征表示。借助Attention机制,BERT模型可以将任意位置的两个单词之间的距离转换为“1”,有效地解决长期依赖问题。

​ BERT 预训练模型可以充分挖掘到每个词的上下文信息,得到词的双向嵌入表示。 与BiLSTM + CRF相比,Bert + BiLSTM + CRF只是将embedding层(输入层)换成了BERT。

四、 参考资料

bert——Bert-BiLSTM-CRF命名实体识别演变-CSDN博客

Bert+BiLSTM+CRF实体抽取-CSDN博客

基于BiLSTM+CRF医学病例命名实体识别项目_基于bi-lstm+crf的医疗医疗命名实体识别-CSDN博客

Vue实现一个命名实体识别的可视化标注系统(前端核心代码)_命名实体识别前端怎么写-CSDN博客

医学命名实体识别: 中科院刘焕勇老师的命名实体识别项目 (gitee.com)

88679/article/details/100673242)

Vue实现一个命名实体识别的可视化标注系统(前端核心代码)_命名实体识别前端怎么写-CSDN博客

医学命名实体识别: 中科院刘焕勇老师的命名实体识别项目 (gitee.com)

ner-app: 命名实体识别前端展示,实现文本高亮标注 (gitee.com)

  • 25
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值