作者从零开始学习和知识图谱有关技术和内容,而本文的核心内容是对CMeKG的python代码进行学习和解读,供大家讨论参考共同进步。

CMeKG(Chinese Medical Knowledge Graph)是利用自然语言处理与文本挖掘技术,基于大规模医学文本数据,以人机结合的方式研发的中文医学知识图谱。

目录

项目来源:

项目成果展示:

项目源代码获取:

medical_re.py

config 类:

IterableDataset 类:

search():

process_data():

get_stream():

Model4s类

__init__():

forward():


项目来源:

中文医学知识图谱CMeKG2.0版发布-自然语言处理实验室北京大学计算语言学研究所、郑州大学自然语言处理实验室与鹏城实验室人工智能研究中心智慧医疗课题组联合发布中文医学知识图谱CMeKG2.0版http://cmekg.pcl.ac.cn/ 欢迎大家试用并提出宝贵意见!  CMeKG(Chinese Medical Knowledge Graph)是利用自然语言处理与文本挖掘技术,基于大规模医学文本数据,以人机结合的方式研发的中文医学知识图谱。CMeKG的构建参考了ICD、ATC、SNOMED、MeSH等权威的国际医学标准以及规模庞大、多源...http://www5.zzu.edu.cn/nlp/info/1018/1785.htm

项目成果展示:

CMeKG中文医学知识图谱http://cmekg.pcl.ac.cn/

项目源代码获取:

https://github.com/king-yyf/CMeKG_toolshttps://github.com/king-yyf/CMeKG_tools

medical_re.py

config 类:

class config:batch_size = 32max_seq_len = 256num_p = 23learning_rate = 1e-5EPOCH = 2PATH_SCHEMA = "/Users/yangyf/workplace/model/medical_re/predicate.json"PATH_TRAIN = '/Users/yangyf/workplace/model/medical_re/train_data.json'PATH_BERT = "/Users/yangyf/workplace/model/medical_re/"PATH_MODEL = "/Users/yangyf/workplace/model/medical_re/model_re.pkl"PATH_SAVE = '/content/model_re.pkl'tokenizer = BertTokenizer.from_pretrained("/Users/yangyf/workplace/model/medical_re/" + 'vocab.txt')id2predicate = {}predicate2id = {}

此类中定义了部分基础变量,例如一个批的数据大小是32,最大字长是256等等。同时还定义了部分文件路径,在使用是需要根据自己电脑中文件的位置进行修改。

tokenizer = BerTokenizer.from_pretrained 是 transformers 当中的一个方法,该方法的工作流程是它会先判断 from_pretrained 函数的参数,如果是 PRETRAINED_MODEL_ARCHIVE_MAP 已有的,就会去cache里找;如果不是,就会判断它是不是一个路径,会在这个路径下找需要的文件,一个config文件和一个bin文件。

 PRETRAINED_MODEL_ARCHIVE_MAP = {'bert-base-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-uncased.tar.gz",'bert-large-uncased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-large-uncased.tar.gz",'bert-base-cased': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-cased.tar.gz",'bert-base-multilingual': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-multilingual.tar.gz",'bert-base-chinese': "https://s3.amazonaws.com/models.huggingface.co/bert/bert-base-chinese.tar.gz",}
"""Params:pretrained_model_name: either:- a str with the name of a pre-trained model to load selected in the list of:. `bert-base-uncased`. `bert-large-uncased`. `bert-base-cased`. `bert-base-multilingual`. `bert-base-chinese`- a path or url to a pretrained model archive containing:. `bert_config.json` a configuration file for the model. `pytorch_model.bin` a PyTorch dump of a BertForPreTraining instance*inputs, **kwargs: additional input for the specific Bert class(ex: num_labels for BertForSequenceClassification)
"""

IterableDataset 类:

class IterableDataset(torch.utils.data.IterableDataset):def __init__(self, data, random):super(IterableDataset).__init__()self.data = dataself.random = randomself.tokenizer = config.tokenizerdef __len__(self):return len(self.data)def search(self, sequence, pattern):n = len(pattern)for i in range(len(sequence)):if sequence[i:i + n] == pattern:return ireturn -1def process_data(self):idxs = list(range(len(self.data)))if self.random:np.random.shuffle(idxs)batch_size = config.batch_sizemax_seq_len = config.max_seq_lennum_p = config.num_pbatch_token_ids = np.zeros((batch_size, max_seq_len), dtype=np.int)         batch_mask_ids = np.zeros((batch_size, max_seq_len), dtype=np.int)          batch_segment_ids = np.zeros((batch_size, max_seq_len), dtype=np.int)batch_subject_ids = np.zeros((batch_size, 2), dtype=np.int)batch_subject_labels = np.zeros((batch_size, max_seq_len, 2), dtype=np.int)batch_object_labels = np.zeros((batch_size, max_seq_len, num_p, 2), dtype=np.int)batch_i = 0for i in idxs:text = self.data[i]['text']batch_token_ids[batch_i, :] = self.tokenizer.encode(text, max_length=max_seq_len, pad_to_max_length=True,add_special_tokens=True)batch_mask_ids[batch_i, :len(text) + 2] = 1spo_list = self.data[i]['spo_list']idx = np.random.randint(0, len(spo_list), size=1)[0]s_rand = self.tokenizer.encode(spo_list[idx][0])[1:-1]s_rand_idx = self.search(list(batch_token_ids[batch_i, :]), s_rand)batch_subject_ids[batch_i, :] = [s_rand_idx, s_rand_idx + len(s_rand) - 1]for i in range(len(spo_list)):spo = spo_list[i]s = self.tokenizer.encode(spo[0])[1:-1]p = config.prediction2id[spo[1]]o = self.tokenizer.encode(spo[2])[1:-1]s_idx = self.search(list(batch_token_ids[batch_i]), s)o_idx = self.search(list(batch_token_ids[batch_i]), o)if s_idx != -1 and o_idx != -1:#如果主体和客体都存在batch_subject_labels[batch_i, s_idx, 0] = 1batch_subject_labels[batch_i, s_idx + len(s) - 1, 1] = 1if s_idx == s_rand_idx:batch_object_labels[batch_i, o_idx, p, 0] = 1batch_object_labels[batch_i, o_idx + len(o) - 1, p, 1] = 1batch_i += 1if batch_i == batch_size or i == idxs[-1]:yield batch_token_ids, batch_mask_ids, batch_segment_ids, batch_subject_labels, batch_subject_ids, batch_object_labelsbatch_token_ids[:, :] = 0batch_mask_ids[:, :] = 0batch_subject_ids[:, :] = 0batch_subject_labels[:, :, :] = 0batch_object_labels[:, :, :, :] = 0batch_i = 0

search():

用来进行模式匹配,该函数中有两个输入参数,一个返回参数,在一给定的 sequence 序列中找到所希望的 pattern ,如果能找到则返回 pattern 开始的位置,找不到则返回-1。

process_data():

对数据进行处理和分类,便于以后使用。如果类中 random 参数为真,则会使用 shuffle 方法将idxs中的数据进行打乱打散。之后创建所需的标志和掩码数组。以前两条创建语句为例:生成一个batch_size,max_seq_len大小的零数组,数据的存储方式是numpy的整型形式,数组名称是token;生成和token同样大小的零数组,用于掩码,mask。

在 for 循环中首先从字典中取出所需处理的文本,然后使用 tokenizer.encode() 中进行解析,返回的是切分之后的编码情况。此处对 tokenizer.encode() 进行详细阐述。

一个简单的例子来展示 tokenizer.encode() 的作用,此部分来自天才小呵呵的博文:

Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode1 引言Hugging Face公司出的transformer包,能够超级方便的引入预训练模型,BERT、ALBERT、GPT2… tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') model = BertForTokenClassification.from_pretrained('bert-bas...https://blog.csdn.net/qq_33293040/article/details/105439750

sentence = "Hello, my sun is cuting."
input_ids_method1 = torch.tensor(tokenizer.encode(sentence, add_special_tokens=True))  # Batch size 1# tensor([ 101, 7592, 1010, 2026, 2365, 2003, 3013, 2075, 1012,  102])

代码运行后的输出包括101和102表示开头和结尾的输出[cls]、[sep]。附上 tokenizer.encode() 的源码解析各参数的作用。

    def encode(self,text: str,  # 需要转化的句子text_pair: Optional[str] = None,   add_special_tokens: bool = True, max_length: Optional[int] = None,  stride: int = 0,truncation_strategy: str = "longest_first",pad_to_max_length: bool = False,return_tensors: Optional[str] = None,**kwargs):"""Converts a string in a sequence of ids (integer), using the tokenizer and vocabulary.Same as doing ``self.convert_tokens_to_ids(self.tokenize(text))``.Args:text (:obj:`str` or :obj:`List[str]`):The first sequence to be encoded. This can be a string, a list of strings (tokenized string usingthe `tokenize` method) or a list of integers (tokenized string ids using the `convert_tokens_to_ids`method)text_pair (:obj:`str` or :obj:`List[str]`, `optional`, defaults to :obj:`None`):Optional second sequence to be encoded. This can be a string, a list of strings (tokenizedstring using the `tokenize` method) or a list of integers (tokenized string ids using the`convert_tokens_to_ids` method)add_special_tokens (:obj:`bool`, `optional`, defaults to :obj:`True`):If set to ``True``, the sequences will be encoded with the special tokens relativeto their model.max_length (:obj:`int`, `optional`, defaults to :obj:`None`):If set to a number, will limit the total sequence returned so that it has a maximum length.If there are overflowing tokens, those will be added to the returned dictionarystride (:obj:`int`, `optional`, defaults to ``0``):If set to a number along with max_length, the overflowing tokens returned will contain some tokensfrom the main sequence returned. The value of this argument defines the number of additional tokens.truncation_strategy (:obj:`str`, `optional`, defaults to `longest_first`):String selected in the following options:- 'longest_first' (default) Iteratively reduce the inputs sequence until the input is under max_lengthstarting from the longest one at each token (when there is a pair of input sequences)- 'only_first': Only truncate the first sequence- 'only_second': Only truncate the second sequence- 'do_not_truncate': Does not truncate (raise an error if the input sequence is longer than max_length)pad_to_max_length (:obj:`bool`, `optional`, defaults to :obj:`False`):If set to True, the returned sequences will be padded according to the model's padding side andpadding index, up to their max length. If no max length is specified, the padding is done up to themodel's max length. The tokenizer padding sides are handled by the class attribute `padding_side`which can be set to the following strings:- 'left': pads on the left of the sequences- 'right': pads on the right of the sequencesDefaults to False: no padding.return_tensors (:obj:`str`, `optional`, defaults to :obj:`None`):Can be set to 'tf' or 'pt' to return respectively TensorFlow :obj:`tf.constant`or PyTorch :obj:`torch.Tensor` instead of a list of python integers.**kwargs: passed to the `self.tokenize()` method"""encoded_inputs = self.encode_plus(text,text_pair=text_pair,max_length=max_length,add_special_tokens=add_special_tokens,stride=stride,truncation_strategy=truncation_strategy,pad_to_max_length=pad_to_max_length,return_tensors=return_tensors,**kwargs,)return encoded_inputs["input_ids"]
  • add_special_tokens: bool = True 将句子转化成对应模型的输入形式,默认开启

  •  max_length 设置最大长度,如果不设置的话原模型设置的最大长度是512,此时,如果句子长度超过512会报错:

  •  pad_to_max_length: bool = False 是否按照最长长度补齐,默认关闭,此处可以通过tokenizer.padding_side = 'left' 设置补齐的位置在左边插入。

  • truncation_strategy: str = "longest_first" 截断机制,有四种方式来读取句子内容:

    • ‘longest_first’(默认):一直迭代,读到不能再读,读满为止
    • ‘only_first’: 只读入第一个序列
    • ‘only_second’: 只读入第二个序列
    • ‘do_not_truncate’: 不做截取,长了就报错
  • return_tensors: Optional[str] = None 返回的数据类型,默认是None,可以选择tensorflow版本 ('tf') 和pytorch版本('pt')。

然后源码在mask列表里面选择第 batch_i 行,将里面文本长度加2的值置1表明文本长度,预留出开头和结尾的输出。spo是关系抽取中的三元组,s是主体,o是客体,p是关系。找到一个随机主体,然后在文本中进行匹配,找到该主体位置并记录。然后对spo_list进行遍历,如果主体、客体都在文本中存在,则记录下主体位置和关系,同时如果该主体与随机主体相同,则记录下客体的位置。

小循环的次数为spo_list的长度,大循环的结束条件有两个一是批数达到上限二是数据达到上限。

这里附上对于 yield 方法的解析:

一个带有 yield 的函数就是一个 generator,它和普通函数不同,生成一个 generator 看起来像函数调用,但不会执行任何函数代码,直到对其调用 next()(在 for 循环中会自动调用 next())才开始执行。虽然执行流程仍按函数的流程执行,但每执行到一个 yield 语句就会中断,并返回一个迭代值,下次执行时从 yield 的下一个语句继续执行。看起来就好像一个函数在正常执行的过程中被 yield 中断了数次,每次中断都会通过 yield 返回当前的迭代值。yield 的好处是显而易见的,把一个函数改写为一个 generator 就获得了迭代能力,比起用类的实例保存状态来计算下一个 next() 的值,不仅代码简洁,而且执行流程异常清晰。

get_stream():

def get_stream(self):return cycle(self.process_data())

在此类中接下来两个函数,用来对上文中的process_data()函数进行循环无尽的生成,对于cycle函数的解释如下。

itertools. cycle ( iterable )
Make an iterator returning elements from the iterable and saving a copy of each. When the iterable is exhausted, return elements from the saved copy. Repeats indefinitely. Roughly equivalent to:
def cycle(iterable):
    # cycle('ABCD') --> A B C D A B C D A B C D ...
    saved = []
    for element in iterable:
        yield element
        saved.append(element)
    while saved:
        for element in saved:
              yield element

Model4s类

class Model4s(nn.Module):def __init__(self, hidden_size=768):super(Model4s, self).__init__()self.bert = BertModel.from_pretrained(config.PATH_BERT)for param in self.bert.parameters():param.requires_grad = Trueself.dropout = nn.Dropout(p=0.2)self.linear = nn.Linear(in_features=hidden_size, out_features=2, bias=True)self.sigmoid = nn.Sigmoid()def forward(self, input_ids, input_mask, segment_ids, hidden_size=768):hidden_states = self.bert(input_ids,attention_mask=input_mask,token_type_ids=segment_ids)[0]  # (batch_size, sequence_length, hidden_size)output = self.sigmoid(self.linear(self.dropout(hidden_states))).pow(2)return output, hidden_states

先从名字理解该类,此类用于主体数据模型的训练。

__init__():

在这里定义并初始化了一些以后会使用的函数。

super().__init__(),就是继承父类的init方法,关于super().__init__()的详细解释可以查看此篇博文,个人认为介绍的十分详细。python中super().__init__()_java/python知识分享-CSDN博客_super().__init__()super().__init__() 1、子类构造函数调用super().__init__()1.1、第一个直接继承父类,可以调用name1.2、第二个继承自父类,覆盖初始化化def init,增加属性age,不能调用name属性1.3、第三个继承自父类,覆盖初始化化def init,并继承初始化属性name,可以调用2、继承顺序3、python2和3的区别1、子类构造函数调用super().i...https://blog.csdn.net/a__int__/article/details/104600972

然后加载一个初始化的训练模型,路径在前面已经定义过了。 之后由于是最终的训练模型所以将require_grad设置为真。

根据PyTorch的自动求导机制,如果一个tensor设置require_grad为True的情况下,才会对这个tensor以及由这个tensor计算出来的其他tensor求导,并将导数值存在tensor的grad属性中,便于优化器来更新参数。

所以一般情况下,只有对训练过程的tensor设置require_grad为True,便于求导和更新参数。而验证过程只是检查当前模型的泛化能力,只需要求出loss,所以不需要根据val set的loss对参数进行更新,因此require_grad为False。

作者:zhFang1999
链接:https://www.zhihu.com/question/436410778/answer/1645941506
来源:知乎

对于nn.Dropout的解释可以参见这篇博文:深度学习中Dropout原理解析_Microstrong-CSDN博客_dropout1. Dropout简介1.1 Dropout出现的原因在机器学习的模型中,如果模型的参数太多,而训练样本又太少,训练出来的模型很容易产生过拟合的现象。在训练神经网络的时候经常会遇到过拟合的问题,过拟合具体表现在:模型在训练数据上损失函数较小,预测准确率较高;但是在测试数据上损失函数比较大,预测准确率较低。过拟合是很多机器学习的通病。如果模型过拟合,那么得到的模型几乎不能用。为了解决过拟合问题,一...https://blog.csdn.net/program_developer/article/details/80737724

对于nn.Linear的解释可以参照这两篇博文:其中第二篇博客最清晰的解释了in_features和out_features,第一篇则较为全面的介绍了整个nn.linear的使用方式并给出公式说明。

Pytorch.nn.Linear 解析(数学角度)_Medlen-CSDN博客_nn.linearpytorch.nn.Linear 是一个类,下面是它的一些初始化参数in_features : 输入样本的张量大小out_features : 输出样本的张量大小bias : 偏置它主要是对输入数据做一个线性变换。y=xAT+by=xA^T+by=xAT+b这里A是权重矩阵,b是偏置。他们都是根据 in_features 生成测试代码:m = torch.nn.Linea...https://blog.csdn.net/weixin_38481963/article/details/105258389PyTorch的nn.Linear()详解_风雪夜归人o的博客-CSDN博客_nn.linear  PyTorch的nn.Linear()是用于设置网络中的全连接层的,需要注意的是全连接层的输入与输出都是二维张量,一般形状为[batch_size, size],不同于卷积层要求输入输出是四维张量。其用法与形参说明如下:  in_features指的是输入的二维张量的大小,即输入的[batch_size, size]中的size。  out_features指的是输出的二维张量的大小,即...https://blog.csdn.net/qq_42079689/article/details/102873766?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

该函数中的最后一句引用了nn中的Sigmoid函数,而该函数形式为:

forward():

这个函数说实话还并没有看明白,在这个python文件中并没有使用,应结合在实际py文件中的使用,在此推测一下应该是使用初始化好的模型对主体进行相关计算,计算出隐藏层数,然后使用sigmoid计算出一个数字(经过平方),以供日后使用

CMeKG代码解读(以项目为导向从零开始学习知识图谱)(一)相关推荐

  1. 如何从零开始搭建知识图谱?

    导读: 从一开始的 Google 搜索,到现在的聊天机器人.大数据风控.证券投资.智能医疗.自适应教育.推荐系统,无一不跟知识图谱相关.它在技术领域的热度也在逐年上升. 本文以通俗易懂的方式来讲解知识 ...

  2. 【项目实践】从零开始学习Deep SORT+YOLO V3进行多目标跟踪(附注释项目代码)...

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达本文转自|集智书童 1.跟踪基础知识简介 首先要说明一点,现在多目标 ...

  3. 项目实战:如何构建知识图谱

    实践了下怎么建一个简单的知识图谱,两个版本,一个从 0 开始(start from scratch),一个在 CN-DBpedia 基础上补充,把 MySQL,PostgreSQL,Neo4j 数据库 ...

  4. 机器学习项目(六)医疗知识图谱构建(四)

    Neo4j Neo4j是一个高性能的NOSQL图形数据库,它将机构化数据存储在网络上而不是表中 Node节点 获得图数据库中所有实体节点 relationship 增加所有增删改查的边关系节点 Nod ...

  5. 电网知识图谱项目总结(1)python代码实现RDF三元组自动化标注

    电网知识图谱项目总结(1)python代码实现RDF三元组自动化标注 文章目录 电网知识图谱项目总结(1)python代码实现RDF三元组自动化标注 简介 文档内容 RDF规范 标注思路 代码结构 详 ...

  6. VINS-Mono代码解读——状态估计器流程 estimator 写在初始化和非线性优化前

    前言 本文主要介绍VINS的状态估计器模块(estimator),主要在代码中/vins_estimator节点的相关部分实现. 这个模块可以说是VINS的最核心模块,从论文的内容上来说,里面的内容包 ...

  7. 从零开始学习springmvc(5)——Spring国际化和全局异常处理

    [项目地址] 从零开始学习springmvc 如果觉得有用可以关注一下公众号:码字不易,求赞求关注 五.Spring国际化和全局异常处理 五.Spring国际化和全局异常处理 5.1 国际化介绍 5. ...

  8. 【原创】强化学习笔记|从零开始学习PPO算法编程(pytorch版本)

    从零开始学习PPO算法编程(pytorch版本)_melody_cjw的博客-CSDN博客_ppo算法 pytorch 从零开始学习PPO算法编程(pytorch版本)(二)_melody_cjw的博 ...

  9. NLP精选10个实现项目推荐-涉及预训练Bert、知识图谱、智能问答、机器翻译、对话等...

    自然语言处理技术近几年发展非常快,像BERT.GPT-3.图神经网络.知识图谱等技术被大量应用于项目实践中. 今年大厂的NLP面试中对项目方面的考察深度也随之提升了很多,经常会被面试官揪着细节一步一步 ...

最新文章

  1. dedecms调用自定义会员模型会员信息的方法
  2. 8953n的user获取权限以及remount
  3. 计算机突然蓝屏无法启动_为什么计算机无法立即启动?
  4. Linux性能优化2.1 CPU性能统计信息
  5. C++ struct结构体 实现搜索二叉树(BST)
  6. 有哪些适合大学生浏览的网站?
  7. 定时器中断实验 编写程序使定时器0或者定时器1工作在方式2,自动重装载模式,定时500ms使两位数码管从00、01、02……98、99每间隔500ms加1显示。
  8. 蓝牙耳机声音一顿一顿的_这次让世界听听我们的声音——声阔SoundcoreLiberty2Pro蓝牙耳机...
  9. List集合和set集合
  10. 支付宝认错,回应央行 18 万行政罚单!
  11. python睡眠_Python时间睡眠()
  12. 五种提高 SQL 性能的方法
  13. shred如果说它Linux系统是文件粉碎机它就更有名了
  14. Insecure Randomness引发对随机数生成器抵挡加密攻击的方法
  15. ELEMENTARY: Is Even
  16. 2020 第十一届蓝桥杯大赛软件类省赛第二场 C/C++ 大学 B 组 完整题面和题解
  17. 新闻主题分类任务NLP
  18. AACWallet 上线 小白也能一键发币啦
  19. 软件开发需求分析究竟应该由谁来做
  20. 山东自然人电子税务局(扣缴端)_企业财务,电子税务局、自然人电子税务局扣缴客户端操作热点来啦...

热门文章

  1. 西工大-网安学院-2021复试-面试题目
  2. 中山大学计算机在职研究生分数线,中山大学在职研究生复试分数线详情
  3. 中山大学计算机在职研究生分数线,2018年中山大学在职研究生分数线高吗?
  4. Hibernate QBE 复合查询问题
  5. 盘一盘!实时自动驾驶车辆定位技术都有哪些?(视觉/Lidar/多传感器数据融合)...
  6. html在线表单生成,一种基于html5的在线表单设计系统的制作方法
  7. 成本控制探讨:BPM扭转粗放费用管控模式
  8. Web前端面试指导(一):写好简历是面试成功的第一步
  9. android 手机充电慢,导致手机充电太慢的四大原因及解决方法【图文教程】
  10. 关于hive数据的导入