CMeKG代码解读(以项目为导向从零开始学习知识图谱)(四)

书接上文:CMeKG代码解读(以项目为导向从零开始学习知识图谱)(三)_chen_nnn的博客-CSDN博客作者从零开始学习和知识图谱有关技术和内容,而本文的核心内容是对CMeKG的python代码进行学习和解读,供大家讨论参考共同进步。CMeKG(Chinese Medical Knowledge Graph)是利用自然语言处理与文本挖掘技术,基于大规模医学文本数据,以人机结合的方式研发的中文医学知识图谱。https://blog.csdn.net/chen_nnn/article/details/122842069?spm=1001.2014.3001.5501

开始新的一个文件的解读,加油加油!

目录

medical_cws.py

medical_seg类:

from_input():

from_txt():

recover_to_text():

predict_sentense():

predict_file():

结语


medical_cws.py

 该项目文件将主要用于医学文本分词,而之前的medical_re.py文件则是用于医学关系抽取。在文件的开始还是常规的引入各种模块,但是后面的utils、cws_constant、model_cws都是自己书写的函数。os.environ[‘环境变量名称’]=‘环境变量值’ ,其中key和value均为string类型,而文中这个语句的作用是设置程序可见的显卡ID。如果"CUDA_VISIBLE_DEVICES" 书写错误,也不报错,自动选用第一个显卡。

import codecs
import torch
from torch.autograd import Variable
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader

from utils import load_vocab
from cws_constant import *

from model_cws import BERT_LSTM_CRF
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "2"

medical_seg类:

class medical_seg(object):
    def __init__(self):
        self.NEWPATH = '/Users/yangyf/workplace/model/medical_cws/pytorch_model.pkl'
        if torch.cuda.is_available():
            self.device = torch.device("cuda", 0)
            self.use_cuda = True
        else:
            self.device = torch.device("cpu")
            self.use_cuda = False

        self.vocab = load_vocab('/Users/yangyf/workplace/model/medical_cws/vocab.txt')
        self.vocab_reverse = {v: k for k, v in self.vocab.items()}

        self.model = BERT_LSTM_CRF('/Users/yangyf/workplace/model/medical_cws', tagset_size, 768, 200, 2,
                              dropout_ratio=0.5, dropout1=0.5, use_cuda=use_cuda)

        if use_cuda:
            self.model.cuda()

该init中主要是将所需要的设备和数据引入,首先定义好路径,然后询问设备cuda(GPU)是否就绪,如果可以的话就将设备设置为0号cuda,并将使用cuda(use_cuda)的参数设置为真,否则将在cpu上运行并将use_cuda设置为假。然后从文件夹中加载词汇文本并将其以字典的形式存储在vocab_reverse中,并且加载分词模型,并设定好参数,最后判断如果use_cuda为真,则在cuda上开始分词。

from_input():

def from_input(self, input_str):
    # 单行的输入
    raw_text = []
    textid = []
    textmask = []
    textlength = []
    text = ['[CLS]'] + [x for x in input_str] + ['[SEP]']
    raw_text.append(text)
    cur_len = len(text)
    # raw_textid = [self.vocab[x] for x in text] + [0] * (max_length - cur_len)
    raw_textid = [self.vocab[x] for x in text if self.vocab.__contains__(x)] + [0] * (max_length - cur_len)
    textid.append(raw_textid)
    raw_textmask = [1] * cur_len + [0] * (max_length - cur_len)
    textmask.append(raw_textmask)
    textlength.append([cur_len])
    textid = torch.LongTensor(textid)
    textmask = torch.LongTensor(textmask)
    textlength = torch.LongTensor(textlength)
    return raw_text, textid, textmask, textlength

首先根据单行输入的字符串列表,在函数内部定好text内容,在前面加上文本开始符,最后加上文本结束符。然后将其附在原始文本之后。首先判断如果在text文本中的词出现在vocab字典中则将其保存下来,如果不足max_length则用0补齐,并将这些出现在字典中的id附在textid中记录下来,同时定义一个掩码,掩码的长度就是文本的长度,不足最大长度则用0补齐,然后附在textmask之后,同时也将长度存储在textlength中,最后将textid、textmask、textlength转换成longtensor型并返回。

from_txt():

    def from_txt(self, input_path):
        # 多行输入
        raw_text = []
        textid = []
        textmask = []
        textlength = []
        with open(input_path, 'r', encoding='utf-8') as f:
            for line in f.readlines():
                if len(line) > 148:
                    line = line[:148]
                temptext = ['[CLS]'] + [x for x in line[:-1]] + ['[SEP]']
                cur_len = len(temptext)
                raw_text.append(temptext)

                tempid = [self.vocab[x] for x in temptext[:cur_len]] + [0] * (max_length - cur_len)
                textid.append(tempid)
                textmask.append([1] * cur_len + [0] * (max_length - cur_len))
                textlength.append([cur_len])

        textid = torch.LongTensor(textid)
        textmask = torch.LongTensor(textmask)
        textlength = torch.LongTensor(textlength)
        return raw_text, textid, textmask, textlength

 上一个函数用于单行输入,该函数用于处理多行输入时的文本切分,前面是同样的预定义,然后以'utf-8'的编码形式编译文件中的内容,如果文件中的某一行超过了148字长,就截取前148个词,之后的处理就和处理单行输入时的文本内容一致了,不再赘述。

recover_to_text():

    def recover_to_text(self, pred, raw_text):
        # 输入[标签list]和[原文list],batch为1
        pred = [i2l_dic[t.item()] for t in pred[0]]
        pred = pred[:len(raw_text)]
        pred = pred[1:-1]
        raw_text = raw_text[1:-1]
        raw = ""
        res = ""
        for tag, char in zip(pred, raw_text):
            res += char
            if tag in ["S", 'E']:
                res += ' '
            raw += char
        return raw, res

 我认为该函数功能比较简单,就是根据模型预测好之后的pred按照预测结果将词从原文中取出来,并返回原文和分词之后的结果。

predict_sentense():

    def predict_sentence(self, sentence):
        if sentence == '':
            print("输入为空!请重新输入")
            return
        if len(sentence) > 148:
            print("输入句子过长,请输入小于148的长度字符!")
            sentence = sentence[:148]
        raw_text, test_ids, test_masks, test_lengths = self.from_input(sentence)

        test_dataset = TensorDataset(test_ids, test_masks, test_lengths)
        test_loader = DataLoader(test_dataset, shuffle=False, batch_size=1)
        # self.model.load_state_dict(torch.load(self.NEWPATH, map_location={'cuda:0': str(self.device)}))
        self.model.load_state_dict(torch.load(self.NEWPATH,map_location=self.device))
        self.model.eval()

        for i, dev_batch in enumerate(test_loader):
            sentence, masks, lengths = dev_batch
            batch_raw_text = raw_text[i]
            sentence, masks, lengths = Variable(sentence), Variable(masks), Variable(lengths)
            if use_cuda:
                sentence = sentence.cuda()
                masks = masks.cuda()

            predict_tags = self.model(sentence, masks)
            predict_tags.tolist()

            raw, res = self.recover_to_text(predict_tags, batch_raw_text)
            #print("输入:", raw)
            #print("结果:", res)
        return res

 首先对输入的句子做最基本的判断是否为空,是否超过最大字长。然后将输入得到的sentence输入from_input函数中处理,返回原文、id、mask和length。然后是TensorDataset可以用来对 tensor 进行打包,就好像 python 中的 zip 功能。该类通过每一个 tensor 的第一个维度进行索引。因此,该类中的 tensor 第一维度必须相等。而DataLoader就是用来包装所使用的数据,每次抛出一批数据,从数据库中每次抽出batch_size个样本。至于load_state_dict()则是对模型进行完成度匹配,然后对模型的参数进行评价。 然后根据enumerate生成的编号进行循环,取出原文,掩码和长度等信息,然后询问是否能在cuda上运行,如果可以的话则将对句子和掩码放到GPU上运行。然后进行模型的训练得到预测的标签,并将该标签和原文作为输入参数放到recover_to_text中进行匹配输出。

predict_file():

    def predict_file(self, input_file, output_file):
        # raw_text, test_ids, test_masks, test_lengths = self.from_txt("./data/raw_text.txt")
        raw_text, test_ids, test_masks, test_lengths = self.from_txt(input_file)

        test_dataset = TensorDataset(test_ids, test_masks, test_lengths)
        test_loader = DataLoader(test_dataset, shuffle=False, batch_size=1)
        self.model.load_state_dict(torch.load(self.NEWPATH, map_location={'cuda:0': str(self.device)}))
        self.model.eval()

        op_file = codecs.open(output_file, 'w', 'utf-8')
        for i, dev_batch in enumerate(test_loader):
            sentence, masks, lengths = dev_batch
            batch_raw_text = raw_text[i]
            sentence, masks, lengths = Variable(sentence), Variable(masks), Variable(lengths)
            if use_cuda:
                sentence = sentence.cuda()
                masks = masks.cuda()

            predict_tags = self.model(sentence, masks)
            predict_tags.tolist()

            raw, res = self.recover_to_text(predict_tags, batch_raw_text)
            op_file.write(res + '\n')

        op_file.close()
        print('处理完成!')
        print("results have been stored in {}".format(output_file))

此时是处理多行输入的情况,根据输入的文本路径,打开文件,同样经过TensorDataset和DataLoader,还有模型的匹配和评价步骤。对于文本中内容的处理与处理单行输入是一致,只不过是添加了部分文档操作的内容。

结语

医疗文本分词的源码文件也已经分析完了,相比起来前面的关系抽取难度降低了不少,也比较好懂,但是目前会遇到一些原理上的问题,还需学习部分机器学习的内容也许可以帮的上忙。如果您看到这里的话,希望您能留下一个免费的赞赞,这对我的帮助很大,对我本人同样也有着激励作用。

你可能感兴趣的:(笔记,知识图谱,python)